添加模块
This commit is contained in:
parent
c3257afd4a
commit
a0b821dd85
|
|
@ -0,0 +1,40 @@
|
|||
package com.tuoheng.airport.airline.application.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 航线创建请求 DTO
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class AirlineCreateRequest {
|
||||
|
||||
/**
|
||||
* 航线编码
|
||||
*/
|
||||
private String airlineCode;
|
||||
|
||||
/**
|
||||
* 航线名称
|
||||
*/
|
||||
private String airlineName;
|
||||
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 航点列表
|
||||
*/
|
||||
private List<WaypointRequest> waypoints;
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
package com.tuoheng.airport.airline.application.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 航线查询请求 DTO
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class AirlineQueryRequest {
|
||||
|
||||
/**
|
||||
* 航线名称(模糊查询)
|
||||
*/
|
||||
private String airlineName;
|
||||
|
||||
/**
|
||||
* 航线状态
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 创建人ID
|
||||
*/
|
||||
private Long creatorId;
|
||||
|
||||
/**
|
||||
* 租户ID
|
||||
*/
|
||||
private Long tenantId;
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
package com.tuoheng.airport.airline.application.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 航线响应 DTO
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class AirlineResponse {
|
||||
|
||||
private Long id;
|
||||
private String airlineCode;
|
||||
private String airlineName;
|
||||
private String description;
|
||||
private String status;
|
||||
private String fileUrl;
|
||||
private Double totalDistance;
|
||||
private Long estimatedDuration;
|
||||
private Long tenantId;
|
||||
private Long creatorId;
|
||||
private Long reviewerId;
|
||||
private String reviewComment;
|
||||
private LocalDateTime reviewTime;
|
||||
private LocalDateTime createTime;
|
||||
private LocalDateTime updateTime;
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tuoheng.airport.airline.application.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class AirlineStatisticsResponse {
|
||||
// TODO: 添加字段
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
package com.tuoheng.airport.airline.application.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 航线更新请求 DTO
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class AirlineUpdateRequest {
|
||||
|
||||
/**
|
||||
* 航线ID
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 航线名称
|
||||
*/
|
||||
private String airlineName;
|
||||
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
private String description;
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
package com.tuoheng.airport.airline.application.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
/**
|
||||
* 航线文件上传请求 DTO
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class AirlineUploadRequest {
|
||||
|
||||
/**
|
||||
* 航线文件(KML/KMZ)
|
||||
*/
|
||||
private MultipartFile file;
|
||||
|
||||
/**
|
||||
* 航线名称
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
private String description;
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
package com.tuoheng.airport.airline.application.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 航线验证结果响应 DTO
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class AirlineValidationResultResponse {
|
||||
|
||||
/**
|
||||
* 是否验证通过
|
||||
*/
|
||||
private Boolean valid;
|
||||
|
||||
/**
|
||||
* 错误信息列表
|
||||
*/
|
||||
private List<String> errors;
|
||||
|
||||
/**
|
||||
* 警告信息列表
|
||||
*/
|
||||
private List<String> warnings;
|
||||
|
||||
/**
|
||||
* 是否穿越禁飞区
|
||||
*/
|
||||
private Boolean crossesNoFlyZone;
|
||||
|
||||
/**
|
||||
* 是否有空域冲突
|
||||
*/
|
||||
private Boolean hasAirspaceConflict;
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
package com.tuoheng.airport.airline.application.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 空域冲突检查响应 DTO
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class AirspaceConflictCheckResponse {
|
||||
|
||||
/**
|
||||
* 是否有冲突
|
||||
*/
|
||||
private Boolean hasConflict;
|
||||
|
||||
/**
|
||||
* 冲突的飞行记录ID列表
|
||||
*/
|
||||
private List<Long> conflictFlightRecordIds;
|
||||
|
||||
/**
|
||||
* 冲突描述
|
||||
*/
|
||||
private String conflictDescription;
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
package com.tuoheng.airport.airline.application.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 紧急航线生成请求 DTO
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class EmergencyAirlineRequest {
|
||||
|
||||
/**
|
||||
* 起点纬度
|
||||
*/
|
||||
private Double startLatitude;
|
||||
|
||||
/**
|
||||
* 起点经度
|
||||
*/
|
||||
private Double startLongitude;
|
||||
|
||||
/**
|
||||
* 终点纬度
|
||||
*/
|
||||
private Double endLatitude;
|
||||
|
||||
/**
|
||||
* 终点经度
|
||||
*/
|
||||
private Double endLongitude;
|
||||
|
||||
/**
|
||||
* 飞行高度(米)
|
||||
*/
|
||||
private Double altitude;
|
||||
|
||||
/**
|
||||
* 飞行速度(m/s)
|
||||
*/
|
||||
private Double speed;
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
package com.tuoheng.airport.airline.application.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 航点请求 DTO
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class WaypointRequest {
|
||||
|
||||
/**
|
||||
* 航点索引
|
||||
*/
|
||||
private Integer waypointIndex;
|
||||
|
||||
/**
|
||||
* 纬度
|
||||
*/
|
||||
private Double latitude;
|
||||
|
||||
/**
|
||||
* 经度
|
||||
*/
|
||||
private Double longitude;
|
||||
|
||||
/**
|
||||
* 高度(米)
|
||||
*/
|
||||
private Double altitude;
|
||||
|
||||
/**
|
||||
* 速度(m/s)
|
||||
*/
|
||||
private Double speed;
|
||||
|
||||
/**
|
||||
* 动作
|
||||
*/
|
||||
private String action;
|
||||
|
||||
/**
|
||||
* 动作参数
|
||||
*/
|
||||
private String actionParam;
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package com.tuoheng.airport.airline.application.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 航点响应 DTO
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class WaypointResponse {
|
||||
|
||||
private Long id;
|
||||
private Long airlineId;
|
||||
private Integer waypointIndex;
|
||||
private Double latitude;
|
||||
private Double longitude;
|
||||
private Double altitude;
|
||||
private Double speed;
|
||||
private String action;
|
||||
private String actionParam;
|
||||
}
|
||||
|
|
@ -0,0 +1,226 @@
|
|||
package com.tuoheng.airport.airline.application.service;
|
||||
|
||||
import com.tuoheng.airport.airline.application.dto.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 航线应用服务接口(Application层)
|
||||
* 定义航线相关的用例(Use Cases)
|
||||
* 协调领域模型完成航线管理操作
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
public interface AirlineApplicationService {
|
||||
|
||||
/**
|
||||
* 创建航线
|
||||
* 业务逻辑:
|
||||
* 1. 验证航线名称唯一性
|
||||
* 2. 验证航点数据合法性
|
||||
* 3. 创建航线记录
|
||||
* 4. 保存航点信息
|
||||
*
|
||||
* @param request 创建请求
|
||||
* @return 航线响应
|
||||
*/
|
||||
AirlineResponse createAirline(AirlineCreateRequest request);
|
||||
|
||||
/**
|
||||
* 上传航线文件
|
||||
* 业务逻辑:
|
||||
* 1. 验证文件格式(KML/KMZ)
|
||||
* 2. 解析航线文件
|
||||
* 3. 提取航点信息
|
||||
* 4. 创建航线记录
|
||||
* 5. 上传文件到存储服务
|
||||
*
|
||||
* @param request 上传请求
|
||||
* @return 航线响应
|
||||
*/
|
||||
AirlineResponse uploadAirlineFile(AirlineUploadRequest request);
|
||||
|
||||
/**
|
||||
* 更新航线信息
|
||||
* 业务逻辑:
|
||||
* 1. 验证航线存在
|
||||
* 2. 检查航线状态(已审核的航线不能修改)
|
||||
* 3. 更新航线基本信息
|
||||
*
|
||||
* @param request 更新请求
|
||||
* @return 航线响应
|
||||
*/
|
||||
AirlineResponse updateAirline(AirlineUpdateRequest request);
|
||||
|
||||
/**
|
||||
* 根据ID查询航线
|
||||
*
|
||||
* @param id 航线ID
|
||||
* @return 航线响应
|
||||
*/
|
||||
AirlineResponse getAirlineById(Long id);
|
||||
|
||||
/**
|
||||
* 根据条件查询航线列表
|
||||
*
|
||||
* @param request 查询请求
|
||||
* @return 航线列表
|
||||
*/
|
||||
List<AirlineResponse> queryAirlines(AirlineQueryRequest request);
|
||||
|
||||
/**
|
||||
* 删除航线
|
||||
* 业务逻辑:
|
||||
* 1. 验证航线存在
|
||||
* 2. 检查航线是否被任务使用
|
||||
* 3. 逻辑删除航线
|
||||
* 4. 删除关联的航点
|
||||
*
|
||||
* @param id 航线ID
|
||||
*/
|
||||
void deleteAirline(Long id);
|
||||
|
||||
/**
|
||||
* 验证航线
|
||||
* 业务逻辑:
|
||||
* 1. 验证航点数量(至少2个)
|
||||
* 2. 验证航点坐标合法性
|
||||
* 3. 验证飞行高度范围
|
||||
* 4. 验证飞行速度范围
|
||||
* 5. 检查空域冲突
|
||||
* 6. 检查禁飞区
|
||||
*
|
||||
* @param id 航线ID
|
||||
* @return 验证结果
|
||||
*/
|
||||
AirlineValidationResultResponse validateAirline(Long id);
|
||||
|
||||
/**
|
||||
* 提交航线审核
|
||||
* 业务逻辑:
|
||||
* 1. 验证航线合法性
|
||||
* 2. 更新航线状态为待审核
|
||||
* 3. 创建审核记录
|
||||
* 4. 发送审核通知
|
||||
*
|
||||
* @param id 航线ID
|
||||
* @return 航线响应
|
||||
*/
|
||||
AirlineResponse submitAirlineForReview(Long id);
|
||||
|
||||
/**
|
||||
* 审核通过航线
|
||||
* 业务逻辑:
|
||||
* 1. 验证航线状态为待审核
|
||||
* 2. 更新航线状态为已审核
|
||||
* 3. 记录审核意见
|
||||
* 4. 发送审核结果通知
|
||||
*
|
||||
* @param id 航线ID
|
||||
* @param comment 审核意见
|
||||
* @return 航线响应
|
||||
*/
|
||||
AirlineResponse approveAirline(Long id, String comment);
|
||||
|
||||
/**
|
||||
* 审核拒绝航线
|
||||
* 业务逻辑:
|
||||
* 1. 验证航线状态为待审核
|
||||
* 2. 更新航线状态为审核拒绝
|
||||
* 3. 记录拒绝原因
|
||||
* 4. 发送审核结果通知
|
||||
*
|
||||
* @param id 航线ID
|
||||
* @param reason 拒绝原因
|
||||
* @return 航线响应
|
||||
*/
|
||||
AirlineResponse rejectAirline(Long id, String reason);
|
||||
|
||||
/**
|
||||
* 复制航线
|
||||
* 业务逻辑:
|
||||
* 1. 验证源航线存在
|
||||
* 2. 复制航线基本信息
|
||||
* 3. 复制所有航点
|
||||
* 4. 新航线状态为草稿
|
||||
*
|
||||
* @param id 源航线ID
|
||||
* @param newName 新航线名称
|
||||
* @return 新航线响应
|
||||
*/
|
||||
AirlineResponse copyAirline(Long id, String newName);
|
||||
|
||||
/**
|
||||
* 查询航线的航点列表
|
||||
*
|
||||
* @param airlineId 航线ID
|
||||
* @return 航点列表
|
||||
*/
|
||||
List<WaypointResponse> getAirlineWaypoints(Long airlineId);
|
||||
|
||||
/**
|
||||
* 计算航线飞行时长
|
||||
* 业务逻辑:
|
||||
* 1. 获取航线的所有航点
|
||||
* 2. 计算航点间的距离
|
||||
* 3. 根据飞行速度计算时长
|
||||
* 4. 加上起降时间
|
||||
*
|
||||
* @param airlineId 航线ID
|
||||
* @param speed 飞行速度(m/s)
|
||||
* @return 飞行时长(秒)
|
||||
*/
|
||||
Long calculateFlightDuration(Long airlineId, Double speed);
|
||||
|
||||
/**
|
||||
* 生成紧急航线
|
||||
* 业务逻辑:
|
||||
* 1. 验证起点和终点坐标
|
||||
* 2. 使用最短路径算法生成航线
|
||||
* 3. 避开禁飞区和障碍物
|
||||
* 4. 创建临时航线记录
|
||||
*
|
||||
* @param request 紧急航线请求
|
||||
* @return 航线响应
|
||||
*/
|
||||
AirlineResponse generateEmergencyAirline(EmergencyAirlineRequest request);
|
||||
|
||||
/**
|
||||
* 下载航线文件
|
||||
* 业务逻辑:
|
||||
* 1. 验证航线存在
|
||||
* 2. 生成KML/KMZ文件
|
||||
* 3. 返回文件流
|
||||
*
|
||||
* @param id 航线ID
|
||||
* @param format 文件格式(kml/kmz)
|
||||
*/
|
||||
void downloadAirlineFile(Long id, String format);
|
||||
|
||||
/**
|
||||
* 优化航线
|
||||
* 业务逻辑:
|
||||
* 1. 分析航线路径
|
||||
* 2. 优化航点顺序
|
||||
* 3. 减少不必要的航点
|
||||
* 4. 平滑飞行路径
|
||||
*
|
||||
* @param id 航线ID
|
||||
* @return 优化后的航线
|
||||
*/
|
||||
AirlineResponse optimizeAirline(Long id);
|
||||
|
||||
/**
|
||||
* 检查航线空域冲突
|
||||
* 业务逻辑:
|
||||
* 1. 查询指定时间段内的其他飞行计划
|
||||
* 2. 检查航线是否有重叠
|
||||
* 3. 返回冲突信息
|
||||
*
|
||||
* @param airlineId 航线ID
|
||||
* @param startTime 开始时间
|
||||
* @param endTime 结束时间
|
||||
* @return 冲突检查结果
|
||||
*/
|
||||
AirspaceConflictCheckResponse checkAirspaceConflict(Long airlineId, String startTime, String endTime);
|
||||
}
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
package com.tuoheng.airport.airline.domain.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 航线领域模型
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class Airline {
|
||||
|
||||
private Long id;
|
||||
private String airlineCode;
|
||||
private String airlineName;
|
||||
private String description;
|
||||
private AirlineStatus status;
|
||||
private String fileUrl;
|
||||
private Double totalDistance;
|
||||
private Long estimatedDuration;
|
||||
private Long tenantId;
|
||||
private Long creatorId;
|
||||
private Long reviewerId;
|
||||
private String reviewComment;
|
||||
private LocalDateTime reviewTime;
|
||||
private LocalDateTime createTime;
|
||||
private LocalDateTime updateTime;
|
||||
private Boolean deleted;
|
||||
|
||||
// ==================== 业务方法 ====================
|
||||
|
||||
public void submitForReview() {
|
||||
if (this.status != AirlineStatus.DRAFT) {
|
||||
throw new IllegalStateException("只有草稿状态的航线可以提交审核");
|
||||
}
|
||||
this.status = AirlineStatus.PENDING_REVIEW;
|
||||
this.updateTime = LocalDateTime.now();
|
||||
}
|
||||
|
||||
public void approve(Long reviewerId, String comment) {
|
||||
if (this.status != AirlineStatus.PENDING_REVIEW) {
|
||||
throw new IllegalStateException("只有待审核状态的航线可以审核");
|
||||
}
|
||||
this.status = AirlineStatus.APPROVED;
|
||||
this.reviewerId = reviewerId;
|
||||
this.reviewComment = comment;
|
||||
this.reviewTime = LocalDateTime.now();
|
||||
this.updateTime = LocalDateTime.now();
|
||||
}
|
||||
|
||||
public void reject(Long reviewerId, String reason) {
|
||||
if (this.status != AirlineStatus.PENDING_REVIEW) {
|
||||
throw new IllegalStateException("只有待审核状态的航线可以拒绝");
|
||||
}
|
||||
this.status = AirlineStatus.REJECTED;
|
||||
this.reviewerId = reviewerId;
|
||||
this.reviewComment = reason;
|
||||
this.reviewTime = LocalDateTime.now();
|
||||
this.updateTime = LocalDateTime.now();
|
||||
}
|
||||
|
||||
public boolean isApproved() {
|
||||
return this.status == AirlineStatus.APPROVED;
|
||||
}
|
||||
|
||||
public boolean canModify() {
|
||||
return this.status == AirlineStatus.DRAFT || this.status == AirlineStatus.REJECTED;
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
this.deleted = true;
|
||||
this.updateTime = LocalDateTime.now();
|
||||
}
|
||||
|
||||
public static Airline create(String airlineCode, String airlineName, Long tenantId, Long creatorId) {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
return Airline.builder()
|
||||
.airlineCode(airlineCode)
|
||||
.airlineName(airlineName)
|
||||
.status(AirlineStatus.DRAFT)
|
||||
.tenantId(tenantId)
|
||||
.creatorId(creatorId)
|
||||
.createTime(now)
|
||||
.updateTime(now)
|
||||
.deleted(false)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package com.tuoheng.airport.airline.domain.model;
|
||||
|
||||
/**
|
||||
* 航线状态枚举
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
public enum AirlineStatus {
|
||||
|
||||
DRAFT("草稿"),
|
||||
PENDING_REVIEW("待审核"),
|
||||
APPROVED("已审核"),
|
||||
REJECTED("已拒绝");
|
||||
|
||||
private final String description;
|
||||
|
||||
AirlineStatus(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
package com.tuoheng.airport.airline.domain.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 航线验证结果领域模型
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class AirlineValidationResult {
|
||||
|
||||
/**
|
||||
* 是否验证通过
|
||||
*/
|
||||
private Boolean valid;
|
||||
|
||||
/**
|
||||
* 错误信息列表
|
||||
*/
|
||||
private List<String> errors;
|
||||
|
||||
/**
|
||||
* 警告信息列表
|
||||
*/
|
||||
private List<String> warnings;
|
||||
|
||||
/**
|
||||
* 是否穿越禁飞区
|
||||
*/
|
||||
private Boolean crossesNoFlyZone;
|
||||
|
||||
/**
|
||||
* 是否有空域冲突
|
||||
*/
|
||||
private Boolean hasAirspaceConflict;
|
||||
|
||||
public boolean hasErrors() {
|
||||
return errors != null && !errors.isEmpty();
|
||||
}
|
||||
|
||||
public boolean hasWarnings() {
|
||||
return warnings != null && !warnings.isEmpty();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
package com.tuoheng.airport.airline.domain.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 航点领域模型
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class Waypoint {
|
||||
|
||||
private Long id;
|
||||
private Long airlineId;
|
||||
private Integer waypointIndex;
|
||||
private Double latitude;
|
||||
private Double longitude;
|
||||
private Double altitude;
|
||||
private Double speed;
|
||||
private String action;
|
||||
private String actionParam;
|
||||
|
||||
// ==================== 业务方法 ====================
|
||||
|
||||
public void validateCoordinates() {
|
||||
if (latitude == null || latitude < -90 || latitude > 90) {
|
||||
throw new IllegalArgumentException("纬度必须在-90到90之间");
|
||||
}
|
||||
if (longitude == null || longitude < -180 || longitude > 180) {
|
||||
throw new IllegalArgumentException("经度必须在-180到180之间");
|
||||
}
|
||||
if (altitude == null || altitude < 0 || altitude > 500) {
|
||||
throw new IllegalArgumentException("高度必须在0到500米之间");
|
||||
}
|
||||
}
|
||||
|
||||
public static Waypoint create(Long airlineId, Integer waypointIndex, Double latitude,
|
||||
Double longitude, Double altitude, Double speed) {
|
||||
Waypoint waypoint = Waypoint.builder()
|
||||
.airlineId(airlineId)
|
||||
.waypointIndex(waypointIndex)
|
||||
.latitude(latitude)
|
||||
.longitude(longitude)
|
||||
.altitude(altitude)
|
||||
.speed(speed)
|
||||
.build();
|
||||
waypoint.validateCoordinates();
|
||||
return waypoint;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
package com.tuoheng.airport.airline.domain.repository;
|
||||
|
||||
import com.tuoheng.airport.airline.domain.model.Airline;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 航线仓储接口
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
public interface AirlineRepository {
|
||||
|
||||
Airline save(Airline airline);
|
||||
|
||||
Optional<Airline> findById(Long id);
|
||||
|
||||
Optional<Airline> findByAirlineCode(String airlineCode);
|
||||
|
||||
List<Airline> findByStatus(String status);
|
||||
|
||||
List<Airline> findByTenantId(Long tenantId);
|
||||
|
||||
List<Airline> findByCreatorId(Long creatorId);
|
||||
|
||||
void delete(Long id);
|
||||
|
||||
boolean existsByAirlineName(String airlineName, Long tenantId);
|
||||
|
||||
long countByStatus(String status);
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package com.tuoheng.airport.airline.domain.repository;
|
||||
|
||||
import com.tuoheng.airport.airline.domain.model.Waypoint;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 航点仓储接口
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
public interface WaypointRepository {
|
||||
|
||||
Waypoint save(Waypoint waypoint);
|
||||
|
||||
Optional<Waypoint> findById(Long id);
|
||||
|
||||
List<Waypoint> findByAirlineId(Long airlineId);
|
||||
|
||||
void delete(Long id);
|
||||
|
||||
void deleteByAirlineId(Long airlineId);
|
||||
|
||||
long countByAirlineId(Long airlineId);
|
||||
}
|
||||
|
|
@ -0,0 +1,305 @@
|
|||
package com.tuoheng.airport.airline.domain.service;
|
||||
|
||||
import com.tuoheng.airport.airline.domain.model.Airline;
|
||||
import com.tuoheng.airport.airline.domain.model.Waypoint;
|
||||
import com.tuoheng.airport.airline.domain.model.AirlineValidationResult;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 航线领域服务接口(Domain层)
|
||||
* 封装航线相关的业务逻辑和业务规则
|
||||
*
|
||||
* Domain Service 的职责:
|
||||
* 1. 封装航线管理的核心业务规则
|
||||
* 2. 协调航线和航点的关系
|
||||
* 3. 保证航线数据的一致性
|
||||
* 4. 不包含事务管理(事务由 Application 层管理)
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
public interface AirlineDomainService {
|
||||
|
||||
/**
|
||||
* 创建航线
|
||||
* 业务规则:
|
||||
* 1. 航线名称不能重复(同一租户下)
|
||||
* 2. 航点数量至少2个
|
||||
* 3. 航点顺序必须连续
|
||||
* 4. 新航线默认为草稿状态
|
||||
*
|
||||
* @param airline 航线领域模型
|
||||
* @return 创建后的航线
|
||||
*/
|
||||
Airline createAirline(Airline airline);
|
||||
|
||||
/**
|
||||
* 更新航线信息
|
||||
* 业务规则:
|
||||
* 1. 已审核的航线不能修改
|
||||
* 2. 不能修改航线编码
|
||||
* 3. 修改后需要重新审核
|
||||
*
|
||||
* @param airline 航线领域模型
|
||||
* @return 更新后的航线
|
||||
*/
|
||||
Airline updateAirline(Airline airline);
|
||||
|
||||
/**
|
||||
* 删除航线
|
||||
* 业务规则:
|
||||
* 1. 检查航线是否被任务使用
|
||||
* 2. 被使用的航线不能删除
|
||||
* 3. 逻辑删除航线
|
||||
* 4. 删除关联的航点
|
||||
*
|
||||
* @param airlineId 航线ID
|
||||
*/
|
||||
void deleteAirline(Long airlineId);
|
||||
|
||||
/**
|
||||
* 验证航线合法性
|
||||
* 业务规则:
|
||||
* 1. 航点数量至少2个
|
||||
* 2. 航点坐标必须合法(经纬度范围)
|
||||
* 3. 飞行高度在允许范围内(0-500米)
|
||||
* 4. 飞行速度在允许范围内(1-20m/s)
|
||||
* 5. 不能穿越禁飞区
|
||||
* 6. 检查空域冲突
|
||||
*
|
||||
* @param airlineId 航线ID
|
||||
* @return 验证结果
|
||||
*/
|
||||
AirlineValidationResult validateAirline(Long airlineId);
|
||||
|
||||
/**
|
||||
* 提交航线审核
|
||||
* 业务规则:
|
||||
* 1. 航线必须通过验证
|
||||
* 2. 更新航线状态为待审核
|
||||
* 3. 记录提交时间和提交人
|
||||
*
|
||||
* @param airlineId 航线ID
|
||||
* @return 更新后的航线
|
||||
*/
|
||||
Airline submitForReview(Long airlineId);
|
||||
|
||||
/**
|
||||
* 审核通过航线
|
||||
* 业务规则:
|
||||
* 1. 航线状态必须为待审核
|
||||
* 2. 更新航线状态为已审核
|
||||
* 3. 记录审核时间和审核人
|
||||
*
|
||||
* @param airlineId 航线ID
|
||||
* @param reviewerId 审核人ID
|
||||
* @param comment 审核意见
|
||||
* @return 更新后的航线
|
||||
*/
|
||||
Airline approveAirline(Long airlineId, Long reviewerId, String comment);
|
||||
|
||||
/**
|
||||
* 审核拒绝航线
|
||||
* 业务规则:
|
||||
* 1. 航线状态必须为待审核
|
||||
* 2. 更新航线状态为审核拒绝
|
||||
* 3. 记录拒绝原因
|
||||
*
|
||||
* @param airlineId 航线ID
|
||||
* @param reviewerId 审核人ID
|
||||
* @param reason 拒绝原因
|
||||
* @return 更新后的航线
|
||||
*/
|
||||
Airline rejectAirline(Long airlineId, Long reviewerId, String reason);
|
||||
|
||||
/**
|
||||
* 复制航线
|
||||
* 业务规则:
|
||||
* 1. 复制航线基本信息
|
||||
* 2. 复制所有航点
|
||||
* 3. 新航线状态为草稿
|
||||
* 4. 新航线名称不能重复
|
||||
*
|
||||
* @param sourceAirlineId 源航线ID
|
||||
* @param newName 新航线名称
|
||||
* @return 新航线
|
||||
*/
|
||||
Airline copyAirline(Long sourceAirlineId, String newName);
|
||||
|
||||
/**
|
||||
* 查询航线详情
|
||||
*
|
||||
* @param airlineId 航线ID
|
||||
* @return 航线领域模型
|
||||
*/
|
||||
Airline getAirlineById(Long airlineId);
|
||||
|
||||
/**
|
||||
* 查询航线的航点列表
|
||||
*
|
||||
* @param airlineId 航线ID
|
||||
* @return 航点列表(按顺序排序)
|
||||
*/
|
||||
List<Waypoint> getAirlineWaypoints(Long airlineId);
|
||||
|
||||
/**
|
||||
* 添加航点到航线
|
||||
* 业务规则:
|
||||
* 1. 航线必须是草稿状态
|
||||
* 2. 航点顺序自动递增
|
||||
* 3. 验证航点坐标合法性
|
||||
*
|
||||
* @param airlineId 航线ID
|
||||
* @param waypoint 航点
|
||||
* @return 添加后的航点
|
||||
*/
|
||||
Waypoint addWaypoint(Long airlineId, Waypoint waypoint);
|
||||
|
||||
/**
|
||||
* 更新航点信息
|
||||
* 业务规则:
|
||||
* 1. 航线必须是草稿状态
|
||||
* 2. 验证航点坐标合法性
|
||||
*
|
||||
* @param waypoint 航点
|
||||
* @return 更新后的航点
|
||||
*/
|
||||
Waypoint updateWaypoint(Waypoint waypoint);
|
||||
|
||||
/**
|
||||
* 删除航点
|
||||
* 业务规则:
|
||||
* 1. 航线必须是草稿状态
|
||||
* 2. 删除后重新排序剩余航点
|
||||
* 3. 航点数量不能少于2个
|
||||
*
|
||||
* @param waypointId 航点ID
|
||||
*/
|
||||
void deleteWaypoint(Long waypointId);
|
||||
|
||||
/**
|
||||
* 计算航线总距离
|
||||
* 根据航点坐标计算航线的总飞行距离
|
||||
*
|
||||
* @param airlineId 航线ID
|
||||
* @return 总距离(米)
|
||||
*/
|
||||
Double calculateTotalDistance(Long airlineId);
|
||||
|
||||
/**
|
||||
* 计算航线飞行时长
|
||||
* 根据航线距离和飞行速度计算预计飞行时长
|
||||
*
|
||||
* @param airlineId 航线ID
|
||||
* @param speed 飞行速度(m/s)
|
||||
* @return 飞行时长(秒)
|
||||
*/
|
||||
Long calculateFlightDuration(Long airlineId, Double speed);
|
||||
|
||||
/**
|
||||
* 生成紧急航线
|
||||
* 业务规则:
|
||||
* 1. 使用Dijkstra最短路径算法
|
||||
* 2. 避开禁飞区
|
||||
* 3. 避开障碍物
|
||||
* 4. 生成临时航线(不保存到数据库)
|
||||
*
|
||||
* @param startLat 起点纬度
|
||||
* @param startLng 起点经度
|
||||
* @param endLat 终点纬度
|
||||
* @param endLng 终点经度
|
||||
* @param altitude 飞行高度
|
||||
* @return 紧急航线
|
||||
*/
|
||||
Airline generateEmergencyAirline(Double startLat, Double startLng, Double endLat, Double endLng, Double altitude);
|
||||
|
||||
/**
|
||||
* 优化航线
|
||||
* 业务规则:
|
||||
* 1. 移除冗余航点
|
||||
* 2. 平滑飞行路径
|
||||
* 3. 优化转弯角度
|
||||
* 4. 保持航线总体方向不变
|
||||
*
|
||||
* @param airlineId 航线ID
|
||||
* @return 优化后的航线
|
||||
*/
|
||||
Airline optimizeAirline(Long airlineId);
|
||||
|
||||
/**
|
||||
* 检查航线是否穿越禁飞区
|
||||
* 业务规则:
|
||||
* 1. 查询所有禁飞区
|
||||
* 2. 检查航线路径是否与禁飞区相交
|
||||
*
|
||||
* @param airlineId 航线ID
|
||||
* @return 是否穿越禁飞区
|
||||
*/
|
||||
boolean crossesNoFlyZone(Long airlineId);
|
||||
|
||||
/**
|
||||
* 检查航线空域冲突
|
||||
* 业务规则:
|
||||
* 1. 查询指定时间段内的其他飞行计划
|
||||
* 2. 检查航线是否有重叠
|
||||
* 3. 检查飞行高度是否有冲突
|
||||
*
|
||||
* @param airlineId 航线ID
|
||||
* @param startTime 开始时间
|
||||
* @param endTime 结束时间
|
||||
* @return 是否有冲突
|
||||
*/
|
||||
boolean hasAirspaceConflict(Long airlineId, String startTime, String endTime);
|
||||
|
||||
/**
|
||||
* 检查航线名称是否存在
|
||||
*
|
||||
* @param name 航线名称
|
||||
* @param tenantId 租户ID
|
||||
* @return 是否存在
|
||||
*/
|
||||
boolean isAirlineNameExists(String name, Long tenantId);
|
||||
|
||||
/**
|
||||
* 检查航线是否被使用
|
||||
* 业务规则:
|
||||
* 1. 检查是否有任务使用该航线
|
||||
* 2. 被使用的航线不能删除
|
||||
*
|
||||
* @param airlineId 航线ID
|
||||
* @return 是否被使用
|
||||
*/
|
||||
boolean isAirlineInUse(Long airlineId);
|
||||
|
||||
/**
|
||||
* 解析航线文件
|
||||
* 从KML/KMZ文件中解析航点信息
|
||||
*
|
||||
* @param fileContent 文件内容
|
||||
* @param fileType 文件类型(kml/kmz)
|
||||
* @return 航点列表
|
||||
*/
|
||||
List<Waypoint> parseAirlineFile(String fileContent, String fileType);
|
||||
|
||||
/**
|
||||
* 生成航线文件
|
||||
* 将航线和航点信息导出为KML/KMZ文件
|
||||
*
|
||||
* @param airlineId 航线ID
|
||||
* @param fileType 文件类型(kml/kmz)
|
||||
* @return 文件内容
|
||||
*/
|
||||
String generateAirlineFile(Long airlineId, String fileType);
|
||||
|
||||
/**
|
||||
* 计算两点之间的距离
|
||||
* 使用Haversine公式计算地球表面两点间的距离
|
||||
*
|
||||
* @param lat1 点1纬度
|
||||
* @param lng1 点1经度
|
||||
* @param lat2 点2纬度
|
||||
* @param lng2 点2经度
|
||||
* @return 距离(米)
|
||||
*/
|
||||
Double calculateDistance(Double lat1, Double lng1, Double lat2, Double lng2);
|
||||
}
|
||||
|
|
@ -0,0 +1,265 @@
|
|||
package com.tuoheng.airport.airline.presentation.controller;
|
||||
|
||||
import com.tuoheng.airport.airline.application.dto.*;
|
||||
import com.tuoheng.airport.airline.application.service.AirlineApplicationService;
|
||||
import com.tuoheng.airport.airline.presentation.converter.AirlineVoConverter;
|
||||
import com.tuoheng.airport.airline.presentation.vo.*;
|
||||
import com.tuoheng.common.response.Result;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 航线管理控制器(Presentation层)
|
||||
* 提供航线管理的 REST API 接口
|
||||
* 负责航线、航点、空域的管理
|
||||
*
|
||||
* 职责:
|
||||
* 1. 接收前端的 VO 对象
|
||||
* 2. 将 VO 转换为 DTO 传递给 Application 层
|
||||
* 3. 将 Application 层返回的 DTO 转换为 VO 返回给前端
|
||||
* 4. 不包含任何业务逻辑
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/airlines")
|
||||
@RequiredArgsConstructor
|
||||
@Validated
|
||||
@Tag(name = "航线管理", description = "航线管理相关接口")
|
||||
public class AirlineController {
|
||||
|
||||
private final AirlineApplicationService airlineApplicationService;
|
||||
|
||||
/**
|
||||
* 创建航线
|
||||
* POST /api/v1/airlines
|
||||
*/
|
||||
@PostMapping
|
||||
@Operation(summary = "创建航线", description = "创建新的飞行航线")
|
||||
public Result<AirlineVO> createAirline(@Valid @RequestBody AirlineCreateVO vo) {
|
||||
log.info("接收到创建航线请求: {}", vo);
|
||||
AirlineCreateRequest request = AirlineVoConverter.toCreateRequest(vo);
|
||||
AirlineResponse response = airlineApplicationService.createAirline(request);
|
||||
AirlineVO result = AirlineVoConverter.toVO(response);
|
||||
return Result.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传航线文件
|
||||
* POST /api/v1/airlines/upload
|
||||
*/
|
||||
@PostMapping("/upload")
|
||||
@Operation(summary = "上传航线文件", description = "上传KML/KMZ格式的航线文件")
|
||||
public Result<AirlineVO> uploadAirlineFile(
|
||||
@Parameter(description = "航线文件") @RequestParam("file") MultipartFile file,
|
||||
@Parameter(description = "航线名称") @RequestParam String name,
|
||||
@Parameter(description = "航线描述") @RequestParam(required = false) String description) {
|
||||
log.info("接收到上传航线文件请求,文件名: {}, 航线名称: {}", file.getOriginalFilename(), name);
|
||||
|
||||
AirlineUploadRequest request = AirlineUploadRequest.builder()
|
||||
.file(file)
|
||||
.name(name)
|
||||
.description(description)
|
||||
.build();
|
||||
|
||||
AirlineResponse response = airlineApplicationService.uploadAirlineFile(request);
|
||||
AirlineVO result = AirlineVoConverter.toVO(response);
|
||||
return Result.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新航线信息
|
||||
* PUT /api/v1/airlines/{id}
|
||||
*/
|
||||
@PutMapping("/{id}")
|
||||
@Operation(summary = "更新航线信息", description = "更新指定航线的基本信息")
|
||||
public Result<AirlineVO> updateAirline(
|
||||
@Parameter(description = "航线ID") @PathVariable Long id,
|
||||
@Valid @RequestBody AirlineUpdateVO vo) {
|
||||
log.info("接收到更新航线请求,航线ID: {}, 请求参数: {}", id, vo);
|
||||
vo.setId(id);
|
||||
AirlineUpdateRequest request = AirlineVoConverter.toUpdateRequest(vo);
|
||||
AirlineResponse response = airlineApplicationService.updateAirline(request);
|
||||
AirlineVO result = AirlineVoConverter.toVO(response);
|
||||
return Result.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询航线详情
|
||||
* GET /api/v1/airlines/{id}
|
||||
*/
|
||||
@GetMapping("/{id}")
|
||||
@Operation(summary = "查询航线详情", description = "根据航线ID查询航线详细信息")
|
||||
public Result<AirlineVO> getAirlineById(
|
||||
@Parameter(description = "航线ID") @PathVariable Long id) {
|
||||
log.info("接收到查询航线请求,航线ID: {}", id);
|
||||
AirlineResponse response = airlineApplicationService.getAirlineById(id);
|
||||
AirlineVO result = AirlineVoConverter.toVO(response);
|
||||
return Result.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询航线列表
|
||||
* GET /api/v1/airlines
|
||||
*/
|
||||
@GetMapping
|
||||
@Operation(summary = "查询航线列表", description = "根据条件查询航线列表")
|
||||
public Result<List<AirlineVO>> queryAirlines(AirlineQueryVO vo) {
|
||||
log.info("接收到查询航线列表请求,查询条件: {}", vo);
|
||||
AirlineQueryRequest request = AirlineVoConverter.toQueryRequest(vo);
|
||||
List<AirlineResponse> responses = airlineApplicationService.queryAirlines(request);
|
||||
List<AirlineVO> results = AirlineVoConverter.toVOList(responses);
|
||||
return Result.success(results);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除航线
|
||||
* DELETE /api/v1/airlines/{id}
|
||||
*/
|
||||
@DeleteMapping("/{id}")
|
||||
@Operation(summary = "删除航线", description = "删除指定的航线(逻辑删除)")
|
||||
public Result<Void> deleteAirline(
|
||||
@Parameter(description = "航线ID") @PathVariable Long id) {
|
||||
log.info("接收到删除航线请求,航线ID: {}", id);
|
||||
airlineApplicationService.deleteAirline(id);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证航线
|
||||
* POST /api/v1/airlines/{id}/validate
|
||||
*/
|
||||
@PostMapping("/{id}/validate")
|
||||
@Operation(summary = "验证航线", description = "验证航线的合法性和安全性")
|
||||
public Result<AirlineValidationResultVO> validateAirline(
|
||||
@Parameter(description = "航线ID") @PathVariable Long id) {
|
||||
log.info("接收到验证航线请求,航线ID: {}", id);
|
||||
AirlineValidationResultResponse response = airlineApplicationService.validateAirline(id);
|
||||
AirlineValidationResultVO result = AirlineVoConverter.toValidationResultVO(response);
|
||||
return Result.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交航线审核
|
||||
* POST /api/v1/airlines/{id}/submit-review
|
||||
*/
|
||||
@PostMapping("/{id}/submit-review")
|
||||
@Operation(summary = "提交航线审核", description = "提交航线进行审核")
|
||||
public Result<AirlineVO> submitAirlineForReview(
|
||||
@Parameter(description = "航线ID") @PathVariable Long id) {
|
||||
log.info("接收到提交航线审核请求,航线ID: {}", id);
|
||||
AirlineResponse response = airlineApplicationService.submitAirlineForReview(id);
|
||||
AirlineVO result = AirlineVoConverter.toVO(response);
|
||||
return Result.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 审核通过航线
|
||||
* POST /api/v1/airlines/{id}/approve
|
||||
*/
|
||||
@PostMapping("/{id}/approve")
|
||||
@Operation(summary = "审核通过航线", description = "审核通过指定的航线")
|
||||
public Result<AirlineVO> approveAirline(
|
||||
@Parameter(description = "航线ID") @PathVariable Long id,
|
||||
@RequestParam(required = false) String comment) {
|
||||
log.info("接收到审核通过航线请求,航线ID: {}, 审核意见: {}", id, comment);
|
||||
AirlineResponse response = airlineApplicationService.approveAirline(id, comment);
|
||||
AirlineVO result = AirlineVoConverter.toVO(response);
|
||||
return Result.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 审核拒绝航线
|
||||
* POST /api/v1/airlines/{id}/reject
|
||||
*/
|
||||
@PostMapping("/{id}/reject")
|
||||
@Operation(summary = "审核拒绝航线", description = "审核拒绝指定的航线")
|
||||
public Result<AirlineVO> rejectAirline(
|
||||
@Parameter(description = "航线ID") @PathVariable Long id,
|
||||
@RequestParam String reason) {
|
||||
log.info("接收到审核拒绝航线请求,航线ID: {}, 拒绝原因: {}", id, reason);
|
||||
AirlineResponse response = airlineApplicationService.rejectAirline(id, reason);
|
||||
AirlineVO result = AirlineVoConverter.toVO(response);
|
||||
return Result.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 复制航线
|
||||
* POST /api/v1/airlines/{id}/copy
|
||||
*/
|
||||
@PostMapping("/{id}/copy")
|
||||
@Operation(summary = "复制航线", description = "复制现有航线创建新航线")
|
||||
public Result<AirlineVO> copyAirline(
|
||||
@Parameter(description = "航线ID") @PathVariable Long id,
|
||||
@RequestParam String newName) {
|
||||
log.info("接收到复制航线请求,航线ID: {}, 新航线名称: {}", id, newName);
|
||||
AirlineResponse response = airlineApplicationService.copyAirline(id, newName);
|
||||
AirlineVO result = AirlineVoConverter.toVO(response);
|
||||
return Result.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询航线的航点列表
|
||||
* GET /api/v1/airlines/{id}/waypoints
|
||||
*/
|
||||
@GetMapping("/{id}/waypoints")
|
||||
@Operation(summary = "查询航线航点", description = "查询指定航线的所有航点")
|
||||
public Result<List<WaypointVO>> getAirlineWaypoints(
|
||||
@Parameter(description = "航线ID") @PathVariable Long id) {
|
||||
log.info("接收到查询航线航点请求,航线ID: {}", id);
|
||||
List<WaypointResponse> responses = airlineApplicationService.getAirlineWaypoints(id);
|
||||
List<WaypointVO> results = AirlineVoConverter.toWaypointVOList(responses);
|
||||
return Result.success(results);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算航线飞行时长
|
||||
* GET /api/v1/airlines/{id}/duration
|
||||
*/
|
||||
@GetMapping("/{id}/duration")
|
||||
@Operation(summary = "计算飞行时长", description = "计算航线的预计飞行时长")
|
||||
public Result<Long> calculateFlightDuration(
|
||||
@Parameter(description = "航线ID") @PathVariable Long id,
|
||||
@Parameter(description = "飞行速度(m/s)") @RequestParam(defaultValue = "10") Double speed) {
|
||||
log.info("接收到计算飞行时长请求,航线ID: {}, 飞行速度: {}m/s", id, speed);
|
||||
Long duration = airlineApplicationService.calculateFlightDuration(id, speed);
|
||||
return Result.success(duration);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成紧急航线
|
||||
* POST /api/v1/airlines/emergency
|
||||
*/
|
||||
@PostMapping("/emergency")
|
||||
@Operation(summary = "生成紧急航线", description = "根据起点和终点动态生成紧急航线")
|
||||
public Result<AirlineVO> generateEmergencyAirline(@Valid @RequestBody EmergencyAirlineRequestVO vo) {
|
||||
log.info("接收到生成紧急航线请求: {}", vo);
|
||||
EmergencyAirlineRequest request = AirlineVoConverter.toEmergencyRequest(vo);
|
||||
AirlineResponse response = airlineApplicationService.generateEmergencyAirline(request);
|
||||
AirlineVO result = AirlineVoConverter.toVO(response);
|
||||
return Result.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载航线文件
|
||||
* GET /api/v1/airlines/{id}/download
|
||||
*/
|
||||
@GetMapping("/{id}/download")
|
||||
@Operation(summary = "下载航线文件", description = "下载航线的KML/KMZ文件")
|
||||
public void downloadAirlineFile(
|
||||
@Parameter(description = "航线ID") @PathVariable Long id,
|
||||
@Parameter(description = "文件格式") @RequestParam(defaultValue = "kml") String format) {
|
||||
log.info("接收到下载航线文件请求,航线ID: {}, 格式: {}", id, format);
|
||||
airlineApplicationService.downloadAirlineFile(id, format);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
package com.tuoheng.airport.airline.presentation.converter;
|
||||
|
||||
import com.tuoheng.airport.airline.application.dto.*;
|
||||
import com.tuoheng.airport.airline.presentation.vo.*;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class AirlineVoConverter {
|
||||
|
||||
public static AirlineCreateRequest toCreateRequest(AirlineCreateVO vo) {
|
||||
// TODO: 实现转换逻辑
|
||||
return new AirlineCreateRequest();
|
||||
}
|
||||
|
||||
public static AirlineUpdateRequest toUpdateRequest(AirlineUpdateVO vo) {
|
||||
// TODO: 实现转换逻辑
|
||||
return new AirlineUpdateRequest();
|
||||
}
|
||||
|
||||
public static AirlineQueryRequest toQueryRequest(AirlineQueryVO vo) {
|
||||
// TODO: 实现转换逻辑
|
||||
return new AirlineQueryRequest();
|
||||
}
|
||||
|
||||
public static AirlineVO toVO(AirlineResponse response) {
|
||||
// TODO: 实现转换逻辑
|
||||
return new AirlineVO();
|
||||
}
|
||||
|
||||
public static List<AirlineVO> toVOList(List<AirlineResponse> responses) {
|
||||
return responses.stream().map(AirlineVoConverter::toVO).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static AirlineStatisticsVO toStatisticsVO(AirlineStatisticsResponse response) {
|
||||
// TODO: 实现转换逻辑
|
||||
return new AirlineStatisticsVO();
|
||||
}
|
||||
|
||||
public static WaypointVO toWaypointVO(WaypointResponse response) {
|
||||
// TODO: 实现转换逻辑
|
||||
return new WaypointVO();
|
||||
}
|
||||
|
||||
public static List<WaypointVO> toWaypointVOList(List<WaypointResponse> responses) {
|
||||
return responses.stream().map(AirlineVoConverter::toWaypointVO).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static EmergencyAirlineRequest toEmergencyRequest(EmergencyAirlineRequestVO vo) {
|
||||
// TODO: 实现转换逻辑
|
||||
return new EmergencyAirlineRequest();
|
||||
}
|
||||
|
||||
public static AirlineValidationResultVO toValidationResultVO(AirlineValidationResultResponse response) {
|
||||
// TODO: 实现转换逻辑
|
||||
return new AirlineValidationResultVO();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
package com.tuoheng.airport.airline.presentation.vo;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 航线创建 VO
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class AirlineCreateVO {
|
||||
|
||||
/**
|
||||
* 航线编码
|
||||
*/
|
||||
@NotBlank(message = "航线编码不能为空")
|
||||
private String airlineCode;
|
||||
|
||||
/**
|
||||
* 航线名称
|
||||
*/
|
||||
@NotBlank(message = "航线名称不能为空")
|
||||
private String airlineName;
|
||||
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 航点列表
|
||||
*/
|
||||
@NotNull(message = "航点列表不能为空")
|
||||
private List<WaypointVO> waypoints;
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
package com.tuoheng.airport.airline.presentation.vo;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 航线查询 VO
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class AirlineQueryVO {
|
||||
|
||||
/**
|
||||
* 航线名称(模糊查询)
|
||||
*/
|
||||
private String airlineName;
|
||||
|
||||
/**
|
||||
* 航线状态
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 创建人ID
|
||||
*/
|
||||
private Long creatorId;
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tuoheng.airport.airline.presentation.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class AirlineStatisticsVO {
|
||||
// TODO: 添加字段
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
package com.tuoheng.airport.airline.presentation.vo;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
* 航线更新 VO
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class AirlineUpdateVO {
|
||||
|
||||
/**
|
||||
* 航线ID
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 航线名称
|
||||
*/
|
||||
@NotBlank(message = "航线名称不能为空")
|
||||
private String airlineName;
|
||||
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
private String description;
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
package com.tuoheng.airport.airline.presentation.vo;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 航线展示 VO
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class AirlineVO {
|
||||
|
||||
private Long id;
|
||||
private String airlineCode;
|
||||
private String airlineName;
|
||||
private String description;
|
||||
private String status;
|
||||
private String fileUrl;
|
||||
private Double totalDistance;
|
||||
private Long estimatedDuration;
|
||||
private Long tenantId;
|
||||
private Long creatorId;
|
||||
private Long reviewerId;
|
||||
private String reviewComment;
|
||||
private LocalDateTime reviewTime;
|
||||
private LocalDateTime createTime;
|
||||
private LocalDateTime updateTime;
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
package com.tuoheng.airport.airline.presentation.vo;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 航线验证结果 VO
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class AirlineValidationResultVO {
|
||||
|
||||
/**
|
||||
* 是否验证通过
|
||||
*/
|
||||
private Boolean valid;
|
||||
|
||||
/**
|
||||
* 错误信息列表
|
||||
*/
|
||||
private List<String> errors;
|
||||
|
||||
/**
|
||||
* 警告信息列表
|
||||
*/
|
||||
private List<String> warnings;
|
||||
|
||||
/**
|
||||
* 是否穿越禁飞区
|
||||
*/
|
||||
private Boolean crossesNoFlyZone;
|
||||
|
||||
/**
|
||||
* 是否有空域冲突
|
||||
*/
|
||||
private Boolean hasAirspaceConflict;
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
package com.tuoheng.airport.airline.presentation.vo;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* 紧急航线生成请求 VO
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class EmergencyAirlineRequestVO {
|
||||
|
||||
/**
|
||||
* 起点纬度
|
||||
*/
|
||||
@NotNull(message = "起点纬度不能为空")
|
||||
private Double startLatitude;
|
||||
|
||||
/**
|
||||
* 起点经度
|
||||
*/
|
||||
@NotNull(message = "起点经度不能为空")
|
||||
private Double startLongitude;
|
||||
|
||||
/**
|
||||
* 终点纬度
|
||||
*/
|
||||
@NotNull(message = "终点纬度不能为空")
|
||||
private Double endLatitude;
|
||||
|
||||
/**
|
||||
* 终点经度
|
||||
*/
|
||||
@NotNull(message = "终点经度不能为空")
|
||||
private Double endLongitude;
|
||||
|
||||
/**
|
||||
* 飞行高度(米)
|
||||
*/
|
||||
@NotNull(message = "飞行高度不能为空")
|
||||
private Double altitude;
|
||||
|
||||
/**
|
||||
* 飞行速度(m/s)
|
||||
*/
|
||||
private Double speed;
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
package com.tuoheng.airport.airline.presentation.vo;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* 航点 VO
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class WaypointVO {
|
||||
|
||||
private Long id;
|
||||
private Long airlineId;
|
||||
|
||||
/**
|
||||
* 航点索引
|
||||
*/
|
||||
@NotNull(message = "航点索引不能为空")
|
||||
private Integer waypointIndex;
|
||||
|
||||
/**
|
||||
* 纬度
|
||||
*/
|
||||
@NotNull(message = "纬度不能为空")
|
||||
private Double latitude;
|
||||
|
||||
/**
|
||||
* 经度
|
||||
*/
|
||||
@NotNull(message = "经度不能为空")
|
||||
private Double longitude;
|
||||
|
||||
/**
|
||||
* 高度(米)
|
||||
*/
|
||||
@NotNull(message = "高度不能为空")
|
||||
private Double altitude;
|
||||
|
||||
/**
|
||||
* 速度(m/s)
|
||||
*/
|
||||
private Double speed;
|
||||
|
||||
/**
|
||||
* 动作
|
||||
*/
|
||||
private String action;
|
||||
|
||||
/**
|
||||
* 动作参数
|
||||
*/
|
||||
private String actionParam;
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tuoheng.airport.approval.application.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ApprovalCreateRequest {
|
||||
// TODO: 添加字段
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tuoheng.airport.approval.application.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ApprovalDecisionRequest {
|
||||
private String comment;
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tuoheng.airport.approval.application.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ApprovalHistoryResponse {
|
||||
// TODO: 添加字段
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tuoheng.airport.approval.application.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ApprovalQueryRequest {
|
||||
// TODO: 添加字段
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tuoheng.airport.approval.application.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ApprovalResponse {
|
||||
// TODO: 添加字段
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tuoheng.airport.approval.application.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ApprovalStatisticsResponse {
|
||||
// TODO: 添加字段
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package com.tuoheng.airport.approval.application.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class BatchApprovalRequest {
|
||||
private List<Long> approvalIds;
|
||||
private String comment;
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package com.tuoheng.airport.approval.application.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class BatchApprovalResultResponse {
|
||||
private Integer successCount;
|
||||
private Integer failureCount;
|
||||
}
|
||||
|
|
@ -0,0 +1,226 @@
|
|||
package com.tuoheng.airport.approval.application.service;
|
||||
|
||||
import com.tuoheng.airport.approval.application.dto.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 审批应用服务接口(Application层)
|
||||
* 定义审批流程相关的用例(Use Cases)
|
||||
* 协调领域模型完成审批流程管理操作
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
public interface ApprovalApplicationService {
|
||||
|
||||
/**
|
||||
* 创建审批申请
|
||||
* 业务逻辑:
|
||||
* 1. 验证申请数据完整性
|
||||
* 2. 创建审批记录
|
||||
* 3. 初始化审批状态为草稿
|
||||
* 4. 关联业务对象(航线、设备等)
|
||||
*
|
||||
* @param request 创建请求
|
||||
* @return 审批响应
|
||||
*/
|
||||
ApprovalResponse createApproval(ApprovalCreateRequest request);
|
||||
|
||||
/**
|
||||
* 提交审批申请
|
||||
* 业务逻辑:
|
||||
* 1. 验证审批状态为草稿
|
||||
* 2. 验证申请数据完整性
|
||||
* 3. 确定审批人(根据审批类型和规则)
|
||||
* 4. 更新审批状态为待审批
|
||||
* 5. 发送审批通知
|
||||
*
|
||||
* @param id 审批ID
|
||||
* @return 审批响应
|
||||
*/
|
||||
ApprovalResponse submitApproval(Long id);
|
||||
|
||||
/**
|
||||
* 审批通过
|
||||
* 业务逻辑:
|
||||
* 1. 验证审批状态为待审批
|
||||
* 2. 验证当前用户是审批人
|
||||
* 3. 记录审批意见
|
||||
* 4. 更新审批状态为已通过
|
||||
* 5. 执行业务回调(如航线审核通过)
|
||||
* 6. 发送审批结果通知
|
||||
*
|
||||
* @param id 审批ID
|
||||
* @param request 审批决策请求
|
||||
* @return 审批响应
|
||||
*/
|
||||
ApprovalResponse approveApproval(Long id, ApprovalDecisionRequest request);
|
||||
|
||||
/**
|
||||
* 审批拒绝
|
||||
* 业务逻辑:
|
||||
* 1. 验证审批状态为待审批
|
||||
* 2. 验证当前用户是审批人
|
||||
* 3. 记录拒绝原因
|
||||
* 4. 更新审批状态为已拒绝
|
||||
* 5. 执行业务回调(如航线审核拒绝)
|
||||
* 6. 发送审批结果通知
|
||||
*
|
||||
* @param id 审批ID
|
||||
* @param request 审批决策请求
|
||||
* @return 审批响应
|
||||
*/
|
||||
ApprovalResponse rejectApproval(Long id, ApprovalDecisionRequest request);
|
||||
|
||||
/**
|
||||
* 撤回审批申请
|
||||
* 业务逻辑:
|
||||
* 1. 验证审批状态为待审批
|
||||
* 2. 验证当前用户是申请人
|
||||
* 3. 更新审批状态为已撤回
|
||||
* 4. 记录撤回原因
|
||||
* 5. 发送撤回通知
|
||||
*
|
||||
* @param id 审批ID
|
||||
* @param reason 撤回原因
|
||||
* @return 审批响应
|
||||
*/
|
||||
ApprovalResponse withdrawApproval(Long id, String reason);
|
||||
|
||||
/**
|
||||
* 转审
|
||||
* 业务逻辑:
|
||||
* 1. 验证审批状态为待审批
|
||||
* 2. 验证当前用户是审批人
|
||||
* 3. 验证目标审批人有效
|
||||
* 4. 更新审批人
|
||||
* 5. 记录转审原因
|
||||
* 6. 发送转审通知
|
||||
*
|
||||
* @param id 审批ID
|
||||
* @param targetApproverId 目标审批人ID
|
||||
* @param reason 转审原因
|
||||
* @return 审批响应
|
||||
*/
|
||||
ApprovalResponse transferApproval(Long id, Long targetApproverId, String reason);
|
||||
|
||||
/**
|
||||
* 查询审批详情
|
||||
*
|
||||
* @param id 审批ID
|
||||
* @return 审批响应
|
||||
*/
|
||||
ApprovalResponse getApprovalById(Long id);
|
||||
|
||||
/**
|
||||
* 根据条件查询审批列表
|
||||
*
|
||||
* @param request 查询请求
|
||||
* @return 审批列表
|
||||
*/
|
||||
List<ApprovalResponse> queryApprovals(ApprovalQueryRequest request);
|
||||
|
||||
/**
|
||||
* 查询我的待审批列表
|
||||
* 业务逻辑:
|
||||
* 1. 获取当前用户ID
|
||||
* 2. 查询审批人为当前用户且状态为待审批的记录
|
||||
* 3. 按提交时间倒序排序
|
||||
*
|
||||
* @return 审批列表
|
||||
*/
|
||||
List<ApprovalResponse> getMyPendingApprovals();
|
||||
|
||||
/**
|
||||
* 查询我的申请列表
|
||||
* 业务逻辑:
|
||||
* 1. 获取当前用户ID
|
||||
* 2. 查询申请人为当前用户的记录
|
||||
* 3. 可按状态筛选
|
||||
* 4. 按提交时间倒序排序
|
||||
*
|
||||
* @param status 审批状态(可选)
|
||||
* @return 审批列表
|
||||
*/
|
||||
List<ApprovalResponse> getMyApplications(String status);
|
||||
|
||||
/**
|
||||
* 查询审批历史
|
||||
* 业务逻辑:
|
||||
* 1. 查询审批的所有流转记录
|
||||
* 2. 包括提交、审批、转审、撤回等操作
|
||||
* 3. 按时间顺序排序
|
||||
*
|
||||
* @param id 审批ID
|
||||
* @return 审批历史列表
|
||||
*/
|
||||
List<ApprovalHistoryResponse> getApprovalHistory(Long id);
|
||||
|
||||
/**
|
||||
* 查询审批统计信息
|
||||
* 业务逻辑:
|
||||
* 1. 统计审批总数
|
||||
* 2. 按状态分组统计(待审批、已通过、已拒绝)
|
||||
* 3. 按审批类型分组统计
|
||||
* 4. 计算平均审批时长
|
||||
* 5. 按日期分组统计
|
||||
*
|
||||
* @param approvalType 审批类型(可选)
|
||||
* @param startDate 开始日期(可选)
|
||||
* @param endDate 结束日期(可选)
|
||||
* @return 审批统计信息
|
||||
*/
|
||||
ApprovalStatisticsResponse getApprovalStatistics(String approvalType, String startDate, String endDate);
|
||||
|
||||
/**
|
||||
* 删除审批申请
|
||||
* 业务逻辑:
|
||||
* 1. 验证审批状态为草稿
|
||||
* 2. 验证当前用户是申请人
|
||||
* 3. 逻辑删除审批记录
|
||||
*
|
||||
* @param id 审批ID
|
||||
*/
|
||||
void deleteApproval(Long id);
|
||||
|
||||
/**
|
||||
* 批量审批
|
||||
* 业务逻辑:
|
||||
* 1. 验证所有审批的状态和权限
|
||||
* 2. 批量更新审批状态
|
||||
* 3. 批量执行业务回调
|
||||
* 4. 批量发送通知
|
||||
* 5. 返回成功和失败的统计
|
||||
*
|
||||
* @param request 批量审批请求
|
||||
* @return 批量审批结果
|
||||
*/
|
||||
BatchApprovalResultResponse batchApprove(BatchApprovalRequest request);
|
||||
|
||||
/**
|
||||
* 催办审批
|
||||
* 业务逻辑:
|
||||
* 1. 验证审批状态为待审批
|
||||
* 2. 验证当前用户是申请人
|
||||
* 3. 检查距离上次催办时间(至少间隔1小时)
|
||||
* 4. 发送催办通知给审批人
|
||||
*
|
||||
* @param id 审批ID
|
||||
*/
|
||||
void urgeApproval(Long id);
|
||||
|
||||
/**
|
||||
* 加签(增加审批人)
|
||||
* 业务逻辑:
|
||||
* 1. 验证审批状态为待审批
|
||||
* 2. 验证当前用户是审批人
|
||||
* 3. 添加新的审批人
|
||||
* 4. 发送加签通知
|
||||
*
|
||||
* @param id 审批ID
|
||||
* @param additionalApproverId 新增审批人ID
|
||||
* @param reason 加签原因
|
||||
* @return 审批响应
|
||||
*/
|
||||
ApprovalResponse addApprover(Long id, Long additionalApproverId, String reason);
|
||||
}
|
||||
|
|
@ -0,0 +1,131 @@
|
|||
package com.tuoheng.airport.approval.domain.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 审批领域模型
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class Approval {
|
||||
|
||||
private Long id;
|
||||
private String approvalCode;
|
||||
private String approvalType;
|
||||
private String title;
|
||||
private String content;
|
||||
private ApprovalStatus status;
|
||||
private Long applicantId;
|
||||
private Long approverId;
|
||||
private String approvalComment;
|
||||
private Long bizId;
|
||||
private String bizType;
|
||||
private Long tenantId;
|
||||
private LocalDateTime submitTime;
|
||||
private LocalDateTime approvalTime;
|
||||
private LocalDateTime createTime;
|
||||
private LocalDateTime updateTime;
|
||||
private Boolean deleted;
|
||||
|
||||
// ==================== 业务方法 ====================
|
||||
|
||||
public void submit(Long approverId) {
|
||||
if (this.status != ApprovalStatus.DRAFT) {
|
||||
throw new IllegalStateException("只有草稿状态的审批可以提交");
|
||||
}
|
||||
this.status = ApprovalStatus.PENDING;
|
||||
this.approverId = approverId;
|
||||
this.submitTime = LocalDateTime.now();
|
||||
this.updateTime = LocalDateTime.now();
|
||||
}
|
||||
|
||||
public void approve(Long approverId, String comment) {
|
||||
if (this.status != ApprovalStatus.PENDING) {
|
||||
throw new IllegalStateException("只有待审批状态的审批可以通过");
|
||||
}
|
||||
if (!this.approverId.equals(approverId)) {
|
||||
throw new IllegalStateException("只有审批人可以审批");
|
||||
}
|
||||
this.status = ApprovalStatus.APPROVED;
|
||||
this.approvalComment = comment;
|
||||
this.approvalTime = LocalDateTime.now();
|
||||
this.updateTime = LocalDateTime.now();
|
||||
}
|
||||
|
||||
public void reject(Long approverId, String reason) {
|
||||
if (this.status != ApprovalStatus.PENDING) {
|
||||
throw new IllegalStateException("只有待审批状态的审批可以拒绝");
|
||||
}
|
||||
if (!this.approverId.equals(approverId)) {
|
||||
throw new IllegalStateException("只有审批人可以审批");
|
||||
}
|
||||
this.status = ApprovalStatus.REJECTED;
|
||||
this.approvalComment = reason;
|
||||
this.approvalTime = LocalDateTime.now();
|
||||
this.updateTime = LocalDateTime.now();
|
||||
}
|
||||
|
||||
public void withdraw(Long applicantId, String reason) {
|
||||
if (this.status != ApprovalStatus.PENDING) {
|
||||
throw new IllegalStateException("只有待审批状态的审批可以撤回");
|
||||
}
|
||||
if (!this.applicantId.equals(applicantId)) {
|
||||
throw new IllegalStateException("只有申请人可以撤回");
|
||||
}
|
||||
this.status = ApprovalStatus.WITHDRAWN;
|
||||
this.approvalComment = reason;
|
||||
this.updateTime = LocalDateTime.now();
|
||||
}
|
||||
|
||||
public void transfer(Long currentApproverId, Long targetApproverId, String reason) {
|
||||
if (this.status != ApprovalStatus.PENDING) {
|
||||
throw new IllegalStateException("只有待审批状态的审批可以转审");
|
||||
}
|
||||
if (!this.approverId.equals(currentApproverId)) {
|
||||
throw new IllegalStateException("只有当前审批人可以转审");
|
||||
}
|
||||
this.approverId = targetApproverId;
|
||||
this.approvalComment = reason;
|
||||
this.updateTime = LocalDateTime.now();
|
||||
}
|
||||
|
||||
public boolean isPending() {
|
||||
return this.status == ApprovalStatus.PENDING;
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
if (this.status != ApprovalStatus.DRAFT) {
|
||||
throw new IllegalStateException("只有草稿状态的审批可以删除");
|
||||
}
|
||||
this.deleted = true;
|
||||
this.updateTime = LocalDateTime.now();
|
||||
}
|
||||
|
||||
public static Approval create(String approvalCode, String approvalType, String title,
|
||||
String content, Long applicantId, String bizType, Long bizId, Long tenantId) {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
return Approval.builder()
|
||||
.approvalCode(approvalCode)
|
||||
.approvalType(approvalType)
|
||||
.title(title)
|
||||
.content(content)
|
||||
.status(ApprovalStatus.DRAFT)
|
||||
.applicantId(applicantId)
|
||||
.bizType(bizType)
|
||||
.bizId(bizId)
|
||||
.tenantId(tenantId)
|
||||
.createTime(now)
|
||||
.updateTime(now)
|
||||
.deleted(false)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
package com.tuoheng.airport.approval.domain.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 审批历史领域模型
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class ApprovalHistory {
|
||||
|
||||
private Long id;
|
||||
private Long approvalId;
|
||||
private String action;
|
||||
private Long operatorId;
|
||||
private String operatorName;
|
||||
private String comment;
|
||||
private LocalDateTime operateTime;
|
||||
|
||||
public static ApprovalHistory create(Long approvalId, String action, Long operatorId,
|
||||
String operatorName, String comment) {
|
||||
return ApprovalHistory.builder()
|
||||
.approvalId(approvalId)
|
||||
.action(action)
|
||||
.operatorId(operatorId)
|
||||
.operatorName(operatorName)
|
||||
.comment(comment)
|
||||
.operateTime(LocalDateTime.now())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
package com.tuoheng.airport.approval.domain.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 审批统计领域模型
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class ApprovalStatistics {
|
||||
|
||||
/**
|
||||
* 审批总数
|
||||
*/
|
||||
private Long totalCount;
|
||||
|
||||
/**
|
||||
* 待审批数量
|
||||
*/
|
||||
private Long pendingCount;
|
||||
|
||||
/**
|
||||
* 已通过数量
|
||||
*/
|
||||
private Long approvedCount;
|
||||
|
||||
/**
|
||||
* 已拒绝数量
|
||||
*/
|
||||
private Long rejectedCount;
|
||||
|
||||
/**
|
||||
* 已撤回数量
|
||||
*/
|
||||
private Long withdrawnCount;
|
||||
|
||||
/**
|
||||
* 平均审批时长(小时)
|
||||
*/
|
||||
private Double averageApprovalDuration;
|
||||
|
||||
/**
|
||||
* 通过率
|
||||
*/
|
||||
public double getApprovalRate() {
|
||||
if (totalCount == null || totalCount == 0) {
|
||||
return 0.0;
|
||||
}
|
||||
return (double) approvedCount / totalCount * 100;
|
||||
}
|
||||
|
||||
/**
|
||||
* 拒绝率
|
||||
*/
|
||||
public double getRejectionRate() {
|
||||
if (totalCount == null || totalCount == 0) {
|
||||
return 0.0;
|
||||
}
|
||||
return (double) rejectedCount / totalCount * 100;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
package com.tuoheng.airport.approval.domain.model;
|
||||
|
||||
/**
|
||||
* 审批状态枚举
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
public enum ApprovalStatus {
|
||||
|
||||
DRAFT("草稿"),
|
||||
PENDING("待审批"),
|
||||
APPROVED("已通过"),
|
||||
REJECTED("已拒绝"),
|
||||
WITHDRAWN("已撤回");
|
||||
|
||||
private final String description;
|
||||
|
||||
ApprovalStatus(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package com.tuoheng.airport.approval.domain.repository;
|
||||
|
||||
import com.tuoheng.airport.approval.domain.model.ApprovalHistory;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 审批历史仓储接口
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
public interface ApprovalHistoryRepository {
|
||||
|
||||
ApprovalHistory save(ApprovalHistory approvalHistory);
|
||||
|
||||
List<ApprovalHistory> findByApprovalId(Long approvalId);
|
||||
|
||||
void deleteByApprovalId(Long approvalId);
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
package com.tuoheng.airport.approval.domain.repository;
|
||||
|
||||
import com.tuoheng.airport.approval.domain.model.Approval;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 审批仓储接口
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
public interface ApprovalRepository {
|
||||
|
||||
Approval save(Approval approval);
|
||||
|
||||
Optional<Approval> findById(Long id);
|
||||
|
||||
Optional<Approval> findByApprovalCode(String approvalCode);
|
||||
|
||||
List<Approval> findByApplicantId(Long applicantId);
|
||||
|
||||
List<Approval> findByApproverId(Long approverId);
|
||||
|
||||
List<Approval> findByStatus(String status);
|
||||
|
||||
List<Approval> findByApprovalType(String approvalType);
|
||||
|
||||
List<Approval> findByBizTypeAndBizId(String bizType, Long bizId);
|
||||
|
||||
void delete(Long id);
|
||||
|
||||
long countByStatus(String status);
|
||||
|
||||
long countByApprovalType(String approvalType);
|
||||
}
|
||||
|
|
@ -0,0 +1,296 @@
|
|||
package com.tuoheng.airport.approval.domain.service;
|
||||
|
||||
import com.tuoheng.airport.approval.domain.model.Approval;
|
||||
import com.tuoheng.airport.approval.domain.model.ApprovalHistory;
|
||||
import com.tuoheng.airport.approval.domain.model.ApprovalStatistics;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 审批领域服务接口(Domain层)
|
||||
* 封装审批流程相关的业务逻辑和业务规则
|
||||
*
|
||||
* Domain Service 的职责:
|
||||
* 1. 封装审批流程的核心业务规则
|
||||
* 2. 协调审批记录和审批历史
|
||||
* 3. 保证审批数据的一致性
|
||||
* 4. 不包含事务管理(事务由 Application 层管理)
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
public interface ApprovalDomainService {
|
||||
|
||||
/**
|
||||
* 创建审批申请
|
||||
* 业务规则:
|
||||
* 1. 生成唯一的审批编号
|
||||
* 2. 初始化审批状态为草稿
|
||||
* 3. 记录申请人和申请时间
|
||||
* 4. 关联业务对象
|
||||
*
|
||||
* @param approval 审批领域模型
|
||||
* @return 创建后的审批
|
||||
*/
|
||||
Approval createApproval(Approval approval);
|
||||
|
||||
/**
|
||||
* 提交审批申请
|
||||
* 业务规则:
|
||||
* 1. 验证审批状态为草稿
|
||||
* 2. 验证申请数据完整性
|
||||
* 3. 确定审批人(根据审批类型和规则)
|
||||
* 4. 更新审批状态为待审批
|
||||
* 5. 记录提交时间
|
||||
* 6. 创建审批历史记录
|
||||
*
|
||||
* @param approvalId 审批ID
|
||||
* @return 更新后的审批
|
||||
*/
|
||||
Approval submitApproval(Long approvalId);
|
||||
|
||||
/**
|
||||
* 审批通过
|
||||
* 业务规则:
|
||||
* 1. 验证审批状态为待审批
|
||||
* 2. 验证审批人权限
|
||||
* 3. 更新审批状态为已通过
|
||||
* 4. 记录审批意见和审批时间
|
||||
* 5. 创建审批历史记录
|
||||
*
|
||||
* @param approvalId 审批ID
|
||||
* @param approverId 审批人ID
|
||||
* @param comment 审批意见
|
||||
* @return 更新后的审批
|
||||
*/
|
||||
Approval approveApproval(Long approvalId, Long approverId, String comment);
|
||||
|
||||
/**
|
||||
* 审批拒绝
|
||||
* 业务规则:
|
||||
* 1. 验证审批状态为待审批
|
||||
* 2. 验证审批人权限
|
||||
* 3. 更新审批状态为已拒绝
|
||||
* 4. 记录拒绝原因和审批时间
|
||||
* 5. 创建审批历史记录
|
||||
*
|
||||
* @param approvalId 审批ID
|
||||
* @param approverId 审批人ID
|
||||
* @param reason 拒绝原因
|
||||
* @return 更新后的审批
|
||||
*/
|
||||
Approval rejectApproval(Long approvalId, Long approverId, String reason);
|
||||
|
||||
/**
|
||||
* 撤回审批申请
|
||||
* 业务规则:
|
||||
* 1. 验证审批状态为待审批
|
||||
* 2. 验证申请人权限
|
||||
* 3. 更新审批状态为已撤回
|
||||
* 4. 记录撤回原因和时间
|
||||
* 5. 创建审批历史记录
|
||||
*
|
||||
* @param approvalId 审批ID
|
||||
* @param applicantId 申请人ID
|
||||
* @param reason 撤回原因
|
||||
* @return 更新后的审批
|
||||
*/
|
||||
Approval withdrawApproval(Long approvalId, Long applicantId, String reason);
|
||||
|
||||
/**
|
||||
* 转审
|
||||
* 业务规则:
|
||||
* 1. 验证审批状态为待审批
|
||||
* 2. 验证当前审批人权限
|
||||
* 3. 验证目标审批人有效
|
||||
* 4. 更新审批人
|
||||
* 5. 记录转审原因和时间
|
||||
* 6. 创建审批历史记录
|
||||
*
|
||||
* @param approvalId 审批ID
|
||||
* @param currentApproverId 当前审批人ID
|
||||
* @param targetApproverId 目标审批人ID
|
||||
* @param reason 转审原因
|
||||
* @return 更新后的审批
|
||||
*/
|
||||
Approval transferApproval(Long approvalId, Long currentApproverId, Long targetApproverId, String reason);
|
||||
|
||||
/**
|
||||
* 加签(增加审批人)
|
||||
* 业务规则:
|
||||
* 1. 验证审批状态为待审批
|
||||
* 2. 验证当前审批人权限
|
||||
* 3. 添加新的审批人
|
||||
* 4. 记录加签原因和时间
|
||||
* 5. 创建审批历史记录
|
||||
*
|
||||
* @param approvalId 审批ID
|
||||
* @param currentApproverId 当前审批人ID
|
||||
* @param additionalApproverId 新增审批人ID
|
||||
* @param reason 加签原因
|
||||
* @return 更新后的审批
|
||||
*/
|
||||
Approval addApprover(Long approvalId, Long currentApproverId, Long additionalApproverId, String reason);
|
||||
|
||||
/**
|
||||
* 删除审批申请
|
||||
* 业务规则:
|
||||
* 1. 只能删除草稿状态的审批
|
||||
* 2. 验证申请人权限
|
||||
* 3. 逻辑删除审批记录
|
||||
*
|
||||
* @param approvalId 审批ID
|
||||
* @param applicantId 申请人ID
|
||||
*/
|
||||
void deleteApproval(Long approvalId, Long applicantId);
|
||||
|
||||
/**
|
||||
* 查询审批详情
|
||||
*
|
||||
* @param approvalId 审批ID
|
||||
* @return 审批领域模型
|
||||
*/
|
||||
Approval getApprovalById(Long approvalId);
|
||||
|
||||
/**
|
||||
* 查询审批历史
|
||||
*
|
||||
* @param approvalId 审批ID
|
||||
* @return 审批历史列表
|
||||
*/
|
||||
List<ApprovalHistory> getApprovalHistory(Long approvalId);
|
||||
|
||||
/**
|
||||
* 确定审批人
|
||||
* 业务规则:
|
||||
* 1. 根据审批类型确定审批人
|
||||
* 2. 航线审批:航线管理员
|
||||
* 3. 设备审批:设备管理员
|
||||
* 4. 任务审批:任务管理员
|
||||
* 5. 支持多级审批
|
||||
*
|
||||
* @param approvalType 审批类型
|
||||
* @param bizId 业务ID
|
||||
* @return 审批人ID列表
|
||||
*/
|
||||
List<Long> determineApprovers(String approvalType, Long bizId);
|
||||
|
||||
/**
|
||||
* 验证审批人权限
|
||||
* 业务规则:
|
||||
* 1. 检查用户是否是当前审批人
|
||||
* 2. 检查用户是否有审批权限
|
||||
*
|
||||
* @param approvalId 审批ID
|
||||
* @param userId 用户ID
|
||||
* @return 是否有权限
|
||||
*/
|
||||
boolean hasApprovalPermission(Long approvalId, Long userId);
|
||||
|
||||
/**
|
||||
* 验证申请人权限
|
||||
* 业务规则:
|
||||
* 1. 检查用户是否是申请人
|
||||
*
|
||||
* @param approvalId 审批ID
|
||||
* @param userId 用户ID
|
||||
* @return 是否是申请人
|
||||
*/
|
||||
boolean isApplicant(Long approvalId, Long userId);
|
||||
|
||||
/**
|
||||
* 检查审批是否超时
|
||||
* 业务规则:
|
||||
* 1. 待审批状态超过3天视为超时
|
||||
* 2. 发送超时提醒
|
||||
*
|
||||
* @param approvalId 审批ID
|
||||
* @return 是否超时
|
||||
*/
|
||||
boolean isApprovalTimeout(Long approvalId);
|
||||
|
||||
/**
|
||||
* 计算审批时长
|
||||
* 从提交到审批完成的时长(小时)
|
||||
*
|
||||
* @param approvalId 审批ID
|
||||
* @return 审批时长(小时)
|
||||
*/
|
||||
Long calculateApprovalDuration(Long approvalId);
|
||||
|
||||
/**
|
||||
* 计算审批统计信息
|
||||
* 业务规则:
|
||||
* 1. 统计审批总数
|
||||
* 2. 按状态分组统计
|
||||
* 3. 按审批类型分组统计
|
||||
* 4. 计算平均审批时长
|
||||
* 5. 按日期分组统计
|
||||
*
|
||||
* @param approvalType 审批类型(可选)
|
||||
* @param startDate 开始日期(可选)
|
||||
* @param endDate 结束日期(可选)
|
||||
* @return 审批统计信息
|
||||
*/
|
||||
ApprovalStatistics calculateStatistics(String approvalType, String startDate, String endDate);
|
||||
|
||||
/**
|
||||
* 创建审批历史记录
|
||||
*
|
||||
* @param approvalId 审批ID
|
||||
* @param action 操作类型(提交、审批、拒绝、撤回、转审)
|
||||
* @param operatorId 操作人ID
|
||||
* @param comment 操作说明
|
||||
*/
|
||||
void createApprovalHistory(Long approvalId, String action, Long operatorId, String comment);
|
||||
|
||||
/**
|
||||
* 检查是否可以催办
|
||||
* 业务规则:
|
||||
* 1. 审批状态为待审批
|
||||
* 2. 距离上次催办至少1小时
|
||||
*
|
||||
* @param approvalId 审批ID
|
||||
* @return 是否可以催办
|
||||
*/
|
||||
boolean canUrge(Long approvalId);
|
||||
|
||||
/**
|
||||
* 记录催办
|
||||
*
|
||||
* @param approvalId 审批ID
|
||||
* @param applicantId 申请人ID
|
||||
*/
|
||||
void recordUrge(Long approvalId, Long applicantId);
|
||||
|
||||
/**
|
||||
* 执行业务回调
|
||||
* 审批通过或拒绝后,执行相应的业务逻辑
|
||||
* 例如:航线审批通过后,更新航线状态为已审核
|
||||
*
|
||||
* @param approvalId 审批ID
|
||||
* @param approved 是否通过
|
||||
*/
|
||||
void executeBusinessCallback(Long approvalId, boolean approved);
|
||||
|
||||
/**
|
||||
* 检查审批是否可以删除
|
||||
* 业务规则:
|
||||
* 1. 只能删除草稿状态的审批
|
||||
*
|
||||
* @param approvalId 审批ID
|
||||
* @return 是否可以删除
|
||||
*/
|
||||
boolean canDelete(Long approvalId);
|
||||
|
||||
/**
|
||||
* 检查审批是否可以撤回
|
||||
* 业务规则:
|
||||
* 1. 只能撤回待审批状态的审批
|
||||
* 2. 只有申请人可以撤回
|
||||
*
|
||||
* @param approvalId 审批ID
|
||||
* @param userId 用户ID
|
||||
* @return 是否可以撤回
|
||||
*/
|
||||
boolean canWithdraw(Long approvalId, Long userId);
|
||||
}
|
||||
|
|
@ -0,0 +1,244 @@
|
|||
package com.tuoheng.airport.approval.presentation.controller;
|
||||
|
||||
import com.tuoheng.airport.approval.application.dto.*;
|
||||
import com.tuoheng.airport.approval.application.service.ApprovalApplicationService;
|
||||
import com.tuoheng.airport.approval.presentation.converter.ApprovalVoConverter;
|
||||
import com.tuoheng.airport.approval.presentation.vo.*;
|
||||
import com.tuoheng.common.response.Result;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 审批管理控制器(Presentation层)
|
||||
* 提供审批流程管理的 REST API 接口
|
||||
* 负责审批申请、审核、流程管理
|
||||
*
|
||||
* 职责:
|
||||
* 1. 接收前端的 VO 对象
|
||||
* 2. 将 VO 转换为 DTO 传递给 Application 层
|
||||
* 3. 将 Application 层返回的 DTO 转换为 VO 返回给前端
|
||||
* 4. 不包含任何业务逻辑
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/approvals")
|
||||
@RequiredArgsConstructor
|
||||
@Validated
|
||||
@Tag(name = "审批管理", description = "审批流程管理相关接口")
|
||||
public class ApprovalController {
|
||||
|
||||
private final ApprovalApplicationService approvalApplicationService;
|
||||
|
||||
/**
|
||||
* 创建审批申请
|
||||
* POST /api/v1/approvals
|
||||
*/
|
||||
@PostMapping
|
||||
@Operation(summary = "创建审批申请", description = "创建新的审批申请")
|
||||
public Result<ApprovalVO> createApproval(@Valid @RequestBody ApprovalCreateVO vo) {
|
||||
log.info("接收到创建审批申请请求: {}", vo);
|
||||
ApprovalCreateRequest request = ApprovalVoConverter.toCreateRequest(vo);
|
||||
ApprovalResponse response = approvalApplicationService.createApproval(request);
|
||||
ApprovalVO result = ApprovalVoConverter.toVO(response);
|
||||
return Result.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交审批申请
|
||||
* POST /api/v1/approvals/{id}/submit
|
||||
*/
|
||||
@PostMapping("/{id}/submit")
|
||||
@Operation(summary = "提交审批申请", description = "提交审批申请进入审批流程")
|
||||
public Result<ApprovalVO> submitApproval(
|
||||
@Parameter(description = "审批ID") @PathVariable Long id) {
|
||||
log.info("接收到提交审批申请请求,审批ID: {}", id);
|
||||
ApprovalResponse response = approvalApplicationService.submitApproval(id);
|
||||
ApprovalVO result = ApprovalVoConverter.toVO(response);
|
||||
return Result.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 审批通过
|
||||
* POST /api/v1/approvals/{id}/approve
|
||||
*/
|
||||
@PostMapping("/{id}/approve")
|
||||
@Operation(summary = "审批通过", description = "审批通过指定的申请")
|
||||
public Result<ApprovalVO> approveApproval(
|
||||
@Parameter(description = "审批ID") @PathVariable Long id,
|
||||
@Valid @RequestBody ApprovalDecisionVO vo) {
|
||||
log.info("接收到审批通过请求,审批ID: {}, 审批意见: {}", id, vo);
|
||||
ApprovalDecisionRequest request = ApprovalVoConverter.toDecisionRequest(vo);
|
||||
ApprovalResponse response = approvalApplicationService.approveApproval(id, request);
|
||||
ApprovalVO result = ApprovalVoConverter.toVO(response);
|
||||
return Result.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 审批拒绝
|
||||
* POST /api/v1/approvals/{id}/reject
|
||||
*/
|
||||
@PostMapping("/{id}/reject")
|
||||
@Operation(summary = "审批拒绝", description = "审批拒绝指定的申请")
|
||||
public Result<ApprovalVO> rejectApproval(
|
||||
@Parameter(description = "审批ID") @PathVariable Long id,
|
||||
@Valid @RequestBody ApprovalDecisionVO vo) {
|
||||
log.info("接收到审批拒绝请求,审批ID: {}, 拒绝原因: {}", id, vo);
|
||||
ApprovalDecisionRequest request = ApprovalVoConverter.toDecisionRequest(vo);
|
||||
ApprovalResponse response = approvalApplicationService.rejectApproval(id, request);
|
||||
ApprovalVO result = ApprovalVoConverter.toVO(response);
|
||||
return Result.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 撤回审批申请
|
||||
* POST /api/v1/approvals/{id}/withdraw
|
||||
*/
|
||||
@PostMapping("/{id}/withdraw")
|
||||
@Operation(summary = "撤回审批申请", description = "撤回已提交的审批申请")
|
||||
public Result<ApprovalVO> withdrawApproval(
|
||||
@Parameter(description = "审批ID") @PathVariable Long id,
|
||||
@RequestParam(required = false) String reason) {
|
||||
log.info("接收到撤回审批申请请求,审批ID: {}, 撤回原因: {}", id, reason);
|
||||
ApprovalResponse response = approvalApplicationService.withdrawApproval(id, reason);
|
||||
ApprovalVO result = ApprovalVoConverter.toVO(response);
|
||||
return Result.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转审
|
||||
* POST /api/v1/approvals/{id}/transfer
|
||||
*/
|
||||
@PostMapping("/{id}/transfer")
|
||||
@Operation(summary = "转审", description = "将审批转交给其他审批人")
|
||||
public Result<ApprovalVO> transferApproval(
|
||||
@Parameter(description = "审批ID") @PathVariable Long id,
|
||||
@Parameter(description = "目标审批人ID") @RequestParam Long targetApproverId,
|
||||
@RequestParam(required = false) String reason) {
|
||||
log.info("接收到转审请求,审批ID: {}, 目标审批人ID: {}, 转审原因: {}", id, targetApproverId, reason);
|
||||
ApprovalResponse response = approvalApplicationService.transferApproval(id, targetApproverId, reason);
|
||||
ApprovalVO result = ApprovalVoConverter.toVO(response);
|
||||
return Result.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询审批详情
|
||||
* GET /api/v1/approvals/{id}
|
||||
*/
|
||||
@GetMapping("/{id}")
|
||||
@Operation(summary = "查询审批详情", description = "根据审批ID查询审批详细信息")
|
||||
public Result<ApprovalVO> getApprovalById(
|
||||
@Parameter(description = "审批ID") @PathVariable Long id) {
|
||||
log.info("接收到查询审批详情请求,审批ID: {}", id);
|
||||
ApprovalResponse response = approvalApplicationService.getApprovalById(id);
|
||||
ApprovalVO result = ApprovalVoConverter.toVO(response);
|
||||
return Result.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询审批列表
|
||||
* GET /api/v1/approvals
|
||||
*/
|
||||
@GetMapping
|
||||
@Operation(summary = "查询审批列表", description = "根据条件查询审批列表")
|
||||
public Result<List<ApprovalVO>> queryApprovals(ApprovalQueryVO vo) {
|
||||
log.info("接收到查询审批列表请求,查询条件: {}", vo);
|
||||
ApprovalQueryRequest request = ApprovalVoConverter.toQueryRequest(vo);
|
||||
List<ApprovalResponse> responses = approvalApplicationService.queryApprovals(request);
|
||||
List<ApprovalVO> results = ApprovalVoConverter.toVOList(responses);
|
||||
return Result.success(results);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询我的待审批列表
|
||||
* GET /api/v1/approvals/pending
|
||||
*/
|
||||
@GetMapping("/pending")
|
||||
@Operation(summary = "查询待审批列表", description = "查询当前用户的待审批列表")
|
||||
public Result<List<ApprovalVO>> getMyPendingApprovals() {
|
||||
log.info("接收到查询待审批列表请求");
|
||||
List<ApprovalResponse> responses = approvalApplicationService.getMyPendingApprovals();
|
||||
List<ApprovalVO> results = ApprovalVoConverter.toVOList(responses);
|
||||
return Result.success(results);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询我的申请列表
|
||||
* GET /api/v1/approvals/my-applications
|
||||
*/
|
||||
@GetMapping("/my-applications")
|
||||
@Operation(summary = "查询我的申请列表", description = "查询当前用户提交的所有审批申请")
|
||||
public Result<List<ApprovalVO>> getMyApplications(
|
||||
@Parameter(description = "审批状态") @RequestParam(required = false) String status) {
|
||||
log.info("接收到查询我的申请列表请求,状态: {}", status);
|
||||
List<ApprovalResponse> responses = approvalApplicationService.getMyApplications(status);
|
||||
List<ApprovalVO> results = ApprovalVoConverter.toVOList(responses);
|
||||
return Result.success(results);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询审批历史
|
||||
* GET /api/v1/approvals/{id}/history
|
||||
*/
|
||||
@GetMapping("/{id}/history")
|
||||
@Operation(summary = "查询审批历史", description = "查询审批的流转历史记录")
|
||||
public Result<List<ApprovalHistoryVO>> getApprovalHistory(
|
||||
@Parameter(description = "审批ID") @PathVariable Long id) {
|
||||
log.info("接收到查询审批历史请求,审批ID: {}", id);
|
||||
List<ApprovalHistoryResponse> responses = approvalApplicationService.getApprovalHistory(id);
|
||||
List<ApprovalHistoryVO> results = ApprovalVoConverter.toHistoryVOList(responses);
|
||||
return Result.success(results);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询审批统计信息
|
||||
* GET /api/v1/approvals/statistics
|
||||
*/
|
||||
@GetMapping("/statistics")
|
||||
@Operation(summary = "查询审批统计", description = "查询审批统计信息")
|
||||
public Result<ApprovalStatisticsVO> getApprovalStatistics(
|
||||
@Parameter(description = "审批类型") @RequestParam(required = false) String approvalType,
|
||||
@Parameter(description = "开始日期") @RequestParam(required = false) String startDate,
|
||||
@Parameter(description = "结束日期") @RequestParam(required = false) String endDate) {
|
||||
log.info("接收到查询审批统计请求,审批类型: {}, 开始日期: {}, 结束日期: {}", approvalType, startDate, endDate);
|
||||
ApprovalStatisticsResponse response = approvalApplicationService.getApprovalStatistics(approvalType, startDate, endDate);
|
||||
ApprovalStatisticsVO result = ApprovalVoConverter.toStatisticsVO(response);
|
||||
return Result.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除审批申请
|
||||
* DELETE /api/v1/approvals/{id}
|
||||
*/
|
||||
@DeleteMapping("/{id}")
|
||||
@Operation(summary = "删除审批申请", description = "删除指定的审批申请(仅草稿状态可删除)")
|
||||
public Result<Void> deleteApproval(
|
||||
@Parameter(description = "审批ID") @PathVariable Long id) {
|
||||
log.info("接收到删除审批申请请求,审批ID: {}", id);
|
||||
approvalApplicationService.deleteApproval(id);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量审批
|
||||
* POST /api/v1/approvals/batch-approve
|
||||
*/
|
||||
@PostMapping("/batch-approve")
|
||||
@Operation(summary = "批量审批", description = "批量审批通过多个申请")
|
||||
public Result<BatchApprovalResultVO> batchApprove(@Valid @RequestBody BatchApprovalVO vo) {
|
||||
log.info("接收到批量审批请求,审批数量: {}", vo.getApprovalIds().size());
|
||||
BatchApprovalRequest request = ApprovalVoConverter.toBatchRequest(vo);
|
||||
BatchApprovalResultResponse response = approvalApplicationService.batchApprove(request);
|
||||
BatchApprovalResultVO result = ApprovalVoConverter.toBatchResultVO(response);
|
||||
return Result.success(result);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
package com.tuoheng.airport.approval.presentation.converter;
|
||||
|
||||
import com.tuoheng.airport.approval.application.dto.*;
|
||||
import com.tuoheng.airport.approval.presentation.vo.*;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class ApprovalVoConverter {
|
||||
|
||||
public static ApprovalCreateRequest toCreateRequest(ApprovalCreateVO vo) {
|
||||
// TODO: 实现转换逻辑
|
||||
return new ApprovalCreateRequest();
|
||||
}
|
||||
|
||||
public static ApprovalDecisionRequest toDecisionRequest(ApprovalDecisionVO vo) {
|
||||
// TODO: 实现转换逻辑
|
||||
return new ApprovalDecisionRequest();
|
||||
}
|
||||
|
||||
public static ApprovalQueryRequest toQueryRequest(ApprovalQueryVO vo) {
|
||||
// TODO: 实现转换逻辑
|
||||
return new ApprovalQueryRequest();
|
||||
}
|
||||
|
||||
public static ApprovalVO toVO(ApprovalResponse response) {
|
||||
// TODO: 实现转换逻辑
|
||||
return new ApprovalVO();
|
||||
}
|
||||
|
||||
public static List<ApprovalVO> toVOList(List<ApprovalResponse> responses) {
|
||||
return responses.stream().map(ApprovalVoConverter::toVO).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static ApprovalHistoryVO toHistoryVO(ApprovalHistoryResponse response) {
|
||||
// TODO: 实现转换逻辑
|
||||
return new ApprovalHistoryVO();
|
||||
}
|
||||
|
||||
public static List<ApprovalHistoryVO> toHistoryVOList(List<ApprovalHistoryResponse> responses) {
|
||||
return responses.stream().map(ApprovalVoConverter::toHistoryVO).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static ApprovalStatisticsVO toStatisticsVO(ApprovalStatisticsResponse response) {
|
||||
// TODO: 实现转换逻辑
|
||||
return new ApprovalStatisticsVO();
|
||||
}
|
||||
|
||||
public static BatchApprovalRequest toBatchRequest(BatchApprovalVO vo) {
|
||||
// TODO: 实现转换逻辑
|
||||
return new BatchApprovalRequest();
|
||||
}
|
||||
|
||||
public static BatchApprovalResultVO toBatchResultVO(BatchApprovalResultResponse response) {
|
||||
// TODO: 实现转换逻辑
|
||||
return new BatchApprovalResultVO();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tuoheng.airport.approval.presentation.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ApprovalCreateVO {
|
||||
// TODO: 添加字段
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tuoheng.airport.approval.presentation.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ApprovalDecisionVO {
|
||||
private String comment;
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tuoheng.airport.approval.presentation.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ApprovalHistoryVO {
|
||||
// TODO: 添加字段
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tuoheng.airport.approval.presentation.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ApprovalQueryVO {
|
||||
// TODO: 添加字段
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tuoheng.airport.approval.presentation.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ApprovalStatisticsVO {
|
||||
// TODO: 添加字段
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tuoheng.airport.approval.presentation.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ApprovalVO {
|
||||
// TODO: 添加字段
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package com.tuoheng.airport.approval.presentation.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class BatchApprovalResultVO {
|
||||
private Integer successCount;
|
||||
private Integer failureCount;
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package com.tuoheng.airport.approval.presentation.vo;
|
||||
|
||||
import lombok.Data;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class BatchApprovalVO {
|
||||
private List<Long> approvalIds;
|
||||
private String comment;
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
package com.tuoheng.airport.fms.application.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 紧急飞行请求 DTO
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class EmergencyFlightRequest {
|
||||
|
||||
/**
|
||||
* 起点纬度
|
||||
*/
|
||||
private Double startLatitude;
|
||||
|
||||
/**
|
||||
* 起点经度
|
||||
*/
|
||||
private Double startLongitude;
|
||||
|
||||
/**
|
||||
* 终点纬度
|
||||
*/
|
||||
private Double endLatitude;
|
||||
|
||||
/**
|
||||
* 终点经度
|
||||
*/
|
||||
private Double endLongitude;
|
||||
|
||||
/**
|
||||
* 飞行高度(米)
|
||||
*/
|
||||
private Double altitude;
|
||||
|
||||
/**
|
||||
* 设备ID(可选,不指定则自动选择最近的可用设备)
|
||||
*/
|
||||
private Long deviceId;
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
package com.tuoheng.airport.fms.application.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 飞行执行请求 DTO
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class FlightExecuteRequest {
|
||||
|
||||
/**
|
||||
* 任务ID(可选)
|
||||
*/
|
||||
private Long taskId;
|
||||
|
||||
/**
|
||||
* 航线ID
|
||||
*/
|
||||
private Long airlineId;
|
||||
|
||||
/**
|
||||
* 设备ID
|
||||
*/
|
||||
private Long deviceId;
|
||||
|
||||
/**
|
||||
* 计划开始时间
|
||||
*/
|
||||
private String planStartTime;
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
package com.tuoheng.airport.fms.application.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 飞行记录查询请求 DTO
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class FlightRecordQueryRequest {
|
||||
|
||||
/**
|
||||
* 飞行编号
|
||||
*/
|
||||
private String flightCode;
|
||||
|
||||
/**
|
||||
* 任务ID
|
||||
*/
|
||||
private Long taskId;
|
||||
|
||||
/**
|
||||
* 设备ID
|
||||
*/
|
||||
private Long deviceId;
|
||||
|
||||
/**
|
||||
* 飞行状态
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 开始日期
|
||||
*/
|
||||
private String startDate;
|
||||
|
||||
/**
|
||||
* 结束日期
|
||||
*/
|
||||
private String endDate;
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tuoheng.airport.fms.application.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class FlightRecordResponse {
|
||||
// TODO: 添加字段
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
package com.tuoheng.airport.fms.application.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 飞行统计响应 DTO
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class FlightStatisticsResponse {
|
||||
|
||||
/**
|
||||
* 飞行总次数
|
||||
*/
|
||||
private Long totalFlights;
|
||||
|
||||
/**
|
||||
* 成功次数
|
||||
*/
|
||||
private Long successCount;
|
||||
|
||||
/**
|
||||
* 失败次数
|
||||
*/
|
||||
private Long failureCount;
|
||||
|
||||
/**
|
||||
* 取消次数
|
||||
*/
|
||||
private Long cancelledCount;
|
||||
|
||||
/**
|
||||
* 总飞行时长(秒)
|
||||
*/
|
||||
private Long totalDuration;
|
||||
|
||||
/**
|
||||
* 总飞行距离(米)
|
||||
*/
|
||||
private Double totalDistance;
|
||||
|
||||
/**
|
||||
* 平均飞行时长(秒)
|
||||
*/
|
||||
private Long averageDuration;
|
||||
|
||||
/**
|
||||
* 成功率(%)
|
||||
*/
|
||||
private Double successRate;
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
package com.tuoheng.airport.fms.application.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 飞行状态响应 DTO
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class FlightStatusResponse {
|
||||
|
||||
/**
|
||||
* 飞行记录ID
|
||||
*/
|
||||
private Long flightRecordId;
|
||||
|
||||
/**
|
||||
* 飞行状态
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 当前纬度
|
||||
*/
|
||||
private Double latitude;
|
||||
|
||||
/**
|
||||
* 当前经度
|
||||
*/
|
||||
private Double longitude;
|
||||
|
||||
/**
|
||||
* 当前高度(米)
|
||||
*/
|
||||
private Double altitude;
|
||||
|
||||
/**
|
||||
* 当前速度(m/s)
|
||||
*/
|
||||
private Double speed;
|
||||
|
||||
/**
|
||||
* 电池电量(%)
|
||||
*/
|
||||
private Integer batteryLevel;
|
||||
|
||||
/**
|
||||
* 当前航点索引
|
||||
*/
|
||||
private Integer currentWaypointIndex;
|
||||
|
||||
/**
|
||||
* 已飞行距离(米)
|
||||
*/
|
||||
private Double flownDistance;
|
||||
|
||||
/**
|
||||
* 已飞行时长(秒)
|
||||
*/
|
||||
private Long flownDuration;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
private LocalDateTime updateTime;
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
package com.tuoheng.airport.fms.application.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 飞行状态更新请求 DTO(由设备上报触发)
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class FlightStatusUpdateRequest {
|
||||
|
||||
/**
|
||||
* 飞行记录ID
|
||||
*/
|
||||
private Long flightRecordId;
|
||||
|
||||
/**
|
||||
* 当前纬度
|
||||
*/
|
||||
private Double latitude;
|
||||
|
||||
/**
|
||||
* 当前经度
|
||||
*/
|
||||
private Double longitude;
|
||||
|
||||
/**
|
||||
* 当前高度(米)
|
||||
*/
|
||||
private Double altitude;
|
||||
|
||||
/**
|
||||
* 当前速度(m/s)
|
||||
*/
|
||||
private Double speed;
|
||||
|
||||
/**
|
||||
* 电池电量(%)
|
||||
*/
|
||||
private Integer batteryLevel;
|
||||
|
||||
/**
|
||||
* 当前航点索引
|
||||
*/
|
||||
private Integer currentWaypointIndex;
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
package com.tuoheng.airport.fms.application.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 飞行轨迹点响应 DTO
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class TrajectoryPointResponse {
|
||||
|
||||
private Long id;
|
||||
private Long flightRecordId;
|
||||
private Double latitude;
|
||||
private Double longitude;
|
||||
private Double altitude;
|
||||
private Double speed;
|
||||
private Integer batteryLevel;
|
||||
private LocalDateTime recordTime;
|
||||
}
|
||||
|
|
@ -0,0 +1,263 @@
|
|||
package com.tuoheng.airport.fms.application.service;
|
||||
|
||||
import com.tuoheng.airport.fms.application.dto.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 飞行应用服务接口(Application层)
|
||||
* 定义单次飞行相关的用例(Use Cases)
|
||||
* 协调领域模型完成飞行执行、控制、监控操作
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
public interface FlightApplicationService {
|
||||
|
||||
/**
|
||||
* 执行单次飞行
|
||||
* 业务逻辑:
|
||||
* 1. 验证航线有效性(已审核)
|
||||
* 2. 检查设备可用性(在线、激活)
|
||||
* 3. 检查空域冲突
|
||||
* 4. 分配设备资源(锁定设备)
|
||||
* 5. 下发飞行指令到设备
|
||||
* 6. 创建飞行记录
|
||||
* 7. 启动实时监控
|
||||
*
|
||||
* @param request 飞行执行请求
|
||||
* @return 飞行记录响应
|
||||
*/
|
||||
FlightRecordResponse executeFlight(FlightExecuteRequest request);
|
||||
|
||||
/**
|
||||
* 紧急飞行
|
||||
* 业务逻辑:
|
||||
* 1. 动态生成紧急航线(最短路径)
|
||||
* 2. 选择最近的可用设备
|
||||
* 3. 优先级最高,可中断其他飞行
|
||||
* 4. 立即执行飞行
|
||||
*
|
||||
* @param request 紧急飞行请求
|
||||
* @return 飞行记录响应
|
||||
*/
|
||||
FlightRecordResponse emergencyFlight(EmergencyFlightRequest request);
|
||||
|
||||
/**
|
||||
* 取消飞行
|
||||
* 业务逻辑:
|
||||
* 1. 验证飞行状态(准备中或执行中)
|
||||
* 2. 发送取消指令到设备
|
||||
* 3. 释放设备资源
|
||||
* 4. 更新飞行记录状态为已取消
|
||||
* 5. 记录取消原因
|
||||
*
|
||||
* @param recordId 飞行记录ID
|
||||
* @param reason 取消原因
|
||||
*/
|
||||
void cancelFlight(Long recordId, String reason);
|
||||
|
||||
/**
|
||||
* 暂停飞行
|
||||
* 业务逻辑:
|
||||
* 1. 验证飞行状态为执行中
|
||||
* 2. 发送暂停指令到设备
|
||||
* 3. 更新飞行状态为已暂停
|
||||
*
|
||||
* @param recordId 飞行记录ID
|
||||
*/
|
||||
void pauseFlight(Long recordId);
|
||||
|
||||
/**
|
||||
* 恢复飞行
|
||||
* 业务逻辑:
|
||||
* 1. 验证飞行状态为已暂停
|
||||
* 2. 发送恢复指令到设备
|
||||
* 3. 更新飞行状态为执行中
|
||||
*
|
||||
* @param recordId 飞行记录ID
|
||||
*/
|
||||
void resumeFlight(Long recordId);
|
||||
|
||||
/**
|
||||
* 返航
|
||||
* 业务逻辑:
|
||||
* 1. 验证飞行状态为执行中
|
||||
* 2. 发送返航指令到设备
|
||||
* 3. 记录返航时间
|
||||
*
|
||||
* @param recordId 飞行记录ID
|
||||
*/
|
||||
void returnHome(Long recordId);
|
||||
|
||||
/**
|
||||
* 无人机起飞
|
||||
* 业务逻辑:
|
||||
* 1. 验证飞行状态为准备中
|
||||
* 2. 发送起飞指令到设备
|
||||
* 3. 更新飞行状态为执行中
|
||||
* 4. 记录起飞时间
|
||||
*
|
||||
* @param recordId 飞行记录ID
|
||||
*/
|
||||
void takeoff(Long recordId);
|
||||
|
||||
/**
|
||||
* 无人机降落
|
||||
* 业务逻辑:
|
||||
* 1. 验证飞行状态为执行中
|
||||
* 2. 发送降落指令到设备
|
||||
* 3. 记录降落时间
|
||||
*
|
||||
* @param recordId 飞行记录ID
|
||||
*/
|
||||
void land(Long recordId);
|
||||
|
||||
/**
|
||||
* 调整飞行高度
|
||||
* 业务逻辑:
|
||||
* 1. 验证飞行状态为执行中
|
||||
* 2. 验证目标高度合法性(0-500米)
|
||||
* 3. 发送调整高度指令到设备
|
||||
*
|
||||
* @param recordId 飞行记录ID
|
||||
* @param altitude 目标高度(米)
|
||||
*/
|
||||
void adjustAltitude(Long recordId, Integer altitude);
|
||||
|
||||
/**
|
||||
* 调整飞行速度
|
||||
* 业务逻辑:
|
||||
* 1. 验证飞行状态为执行中
|
||||
* 2. 验证目标速度合法性(1-20m/s)
|
||||
* 3. 发送调整速度指令到设备
|
||||
*
|
||||
* @param recordId 飞行记录ID
|
||||
* @param speed 目标速度(m/s)
|
||||
*/
|
||||
void adjustSpeed(Long recordId, Double speed);
|
||||
|
||||
/**
|
||||
* 跳转到指定航点
|
||||
* 业务逻辑:
|
||||
* 1. 验证飞行状态为执行中
|
||||
* 2. 验证航点索引合法性
|
||||
* 3. 发送跳转航点指令到设备
|
||||
*
|
||||
* @param recordId 飞行记录ID
|
||||
* @param waypointIndex 航点索引
|
||||
*/
|
||||
void gotoWaypoint(Long recordId, Integer waypointIndex);
|
||||
|
||||
/**
|
||||
* 查询飞行记录详情
|
||||
*
|
||||
* @param recordId 飞行记录ID
|
||||
* @return 飞行记录响应
|
||||
*/
|
||||
FlightRecordResponse getFlightRecord(Long recordId);
|
||||
|
||||
/**
|
||||
* 查询飞行实时状态
|
||||
* 业务逻辑:
|
||||
* 1. 查询飞行记录
|
||||
* 2. 从缓存或设备获取实时状态
|
||||
* 3. 返回位置、高度、速度、电量等信息
|
||||
*
|
||||
* @param recordId 飞行记录ID
|
||||
* @return 飞行状态响应
|
||||
*/
|
||||
FlightStatusResponse getFlightStatus(Long recordId);
|
||||
|
||||
/**
|
||||
* 查询飞行轨迹
|
||||
* 业务逻辑:
|
||||
* 1. 查询飞行记录
|
||||
* 2. 查询所有轨迹点
|
||||
* 3. 按时间排序返回
|
||||
*
|
||||
* @param recordId 飞行记录ID
|
||||
* @return 轨迹点列表
|
||||
*/
|
||||
List<TrajectoryPointResponse> getFlightTrajectory(Long recordId);
|
||||
|
||||
/**
|
||||
* 根据条件查询飞行记录列表
|
||||
*
|
||||
* @param request 查询请求
|
||||
* @return 飞行记录列表
|
||||
*/
|
||||
List<FlightRecordResponse> queryFlightRecords(FlightRecordQueryRequest request);
|
||||
|
||||
/**
|
||||
* 根据设备ID查询飞行记录列表
|
||||
*
|
||||
* @param deviceId 设备ID
|
||||
* @return 飞行记录列表
|
||||
*/
|
||||
List<FlightRecordResponse> getFlightRecordsByDevice(Long deviceId);
|
||||
|
||||
/**
|
||||
* 查询正在执行的飞行列表
|
||||
* 业务逻辑:
|
||||
* 1. 查询状态为执行中的飞行记录
|
||||
* 2. 返回实时状态信息
|
||||
*
|
||||
* @return 飞行记录列表
|
||||
*/
|
||||
List<FlightRecordResponse> getActiveFlights();
|
||||
|
||||
/**
|
||||
* 查询飞行统计信息
|
||||
* 业务逻辑:
|
||||
* 1. 统计飞行总次数
|
||||
* 2. 统计成功/失败次数
|
||||
* 3. 统计总飞行时长
|
||||
* 4. 统计总飞行距离
|
||||
* 5. 按日期分组统计
|
||||
*
|
||||
* @param deviceId 设备ID(可选)
|
||||
* @param startDate 开始日期(可选)
|
||||
* @param endDate 结束日期(可选)
|
||||
* @return 飞行统计信息
|
||||
*/
|
||||
FlightStatisticsResponse getFlightStatistics(Long deviceId, String startDate, String endDate);
|
||||
|
||||
/**
|
||||
* 更新飞行实时状态(由设备上报触发)
|
||||
* 业务逻辑:
|
||||
* 1. 接收设备上报的实时数据
|
||||
* 2. 更新飞行状态
|
||||
* 3. 记录轨迹点
|
||||
* 4. 检查异常情况(低电量、失联等)
|
||||
*
|
||||
* @param request 状态更新请求
|
||||
*/
|
||||
void updateFlightStatus(FlightStatusUpdateRequest request);
|
||||
|
||||
/**
|
||||
* 完成飞行(由设备上报触发)
|
||||
* 业务逻辑:
|
||||
* 1. 验证飞行状态
|
||||
* 2. 记录降落时间
|
||||
* 3. 计算飞行时长和距离
|
||||
* 4. 释放设备资源
|
||||
* 5. 更新飞行状态为已完成
|
||||
* 6. 触发媒体文件上传
|
||||
*
|
||||
* @param recordId 飞行记录ID
|
||||
*/
|
||||
void completeFlight(Long recordId);
|
||||
|
||||
/**
|
||||
* 飞行失败(由设备上报或系统检测触发)
|
||||
* 业务逻辑:
|
||||
* 1. 记录失败原因
|
||||
* 2. 释放设备资源
|
||||
* 3. 更新飞行状态为失败
|
||||
* 4. 发送告警通知
|
||||
*
|
||||
* @param recordId 飞行记录ID
|
||||
* @param reason 失败原因
|
||||
*/
|
||||
void failFlight(Long recordId, String reason);
|
||||
}
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
package com.tuoheng.airport.fms.domain.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 飞行记录领域模型
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class FlightRecord {
|
||||
|
||||
private Long id;
|
||||
private String flightCode;
|
||||
private Long taskId;
|
||||
private Long airlineId;
|
||||
private Long deviceId;
|
||||
private FlightStatus status;
|
||||
private LocalDateTime planStartTime;
|
||||
private LocalDateTime actualStartTime;
|
||||
private LocalDateTime actualEndTime;
|
||||
private Long actualDuration;
|
||||
private Double actualDistance;
|
||||
private String failureReason;
|
||||
private Long tenantId;
|
||||
private LocalDateTime createTime;
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
// ==================== 业务方法 ====================
|
||||
|
||||
public void start() {
|
||||
if (this.status != FlightStatus.PREPARING) {
|
||||
throw new IllegalStateException("只有准备中的飞行可以开始");
|
||||
}
|
||||
this.status = FlightStatus.EXECUTING;
|
||||
this.actualStartTime = LocalDateTime.now();
|
||||
this.updateTime = LocalDateTime.now();
|
||||
}
|
||||
|
||||
public void complete() {
|
||||
if (this.status != FlightStatus.EXECUTING) {
|
||||
throw new IllegalStateException("只有执行中的飞行可以完成");
|
||||
}
|
||||
this.status = FlightStatus.COMPLETED;
|
||||
this.actualEndTime = LocalDateTime.now();
|
||||
if (this.actualStartTime != null) {
|
||||
this.actualDuration = java.time.Duration.between(this.actualStartTime, this.actualEndTime).getSeconds();
|
||||
}
|
||||
this.updateTime = LocalDateTime.now();
|
||||
}
|
||||
|
||||
public void cancel(String reason) {
|
||||
if (this.status == FlightStatus.COMPLETED || this.status == FlightStatus.FAILED) {
|
||||
throw new IllegalStateException("已完成或已失败的飞行不能取消");
|
||||
}
|
||||
this.status = FlightStatus.CANCELLED;
|
||||
this.failureReason = reason;
|
||||
this.updateTime = LocalDateTime.now();
|
||||
}
|
||||
|
||||
public void fail(String reason) {
|
||||
this.status = FlightStatus.FAILED;
|
||||
this.failureReason = reason;
|
||||
this.actualEndTime = LocalDateTime.now();
|
||||
this.updateTime = LocalDateTime.now();
|
||||
}
|
||||
|
||||
public void pause() {
|
||||
if (this.status != FlightStatus.EXECUTING) {
|
||||
throw new IllegalStateException("只有执行中的飞行可以暂停");
|
||||
}
|
||||
this.status = FlightStatus.PAUSED;
|
||||
this.updateTime = LocalDateTime.now();
|
||||
}
|
||||
|
||||
public void resume() {
|
||||
if (this.status != FlightStatus.PAUSED) {
|
||||
throw new IllegalStateException("只有已暂停的飞行可以恢复");
|
||||
}
|
||||
this.status = FlightStatus.EXECUTING;
|
||||
this.updateTime = LocalDateTime.now();
|
||||
}
|
||||
|
||||
public boolean isActive() {
|
||||
return this.status == FlightStatus.EXECUTING || this.status == FlightStatus.PAUSED;
|
||||
}
|
||||
|
||||
public static FlightRecord create(String flightCode, Long taskId, Long airlineId, Long deviceId, Long tenantId) {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
return FlightRecord.builder()
|
||||
.flightCode(flightCode)
|
||||
.taskId(taskId)
|
||||
.airlineId(airlineId)
|
||||
.deviceId(deviceId)
|
||||
.status(FlightStatus.PREPARING)
|
||||
.planStartTime(now)
|
||||
.tenantId(tenantId)
|
||||
.createTime(now)
|
||||
.updateTime(now)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
package com.tuoheng.airport.fms.domain.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 飞行统计领域模型
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class FlightStatistics {
|
||||
|
||||
/**
|
||||
* 飞行总次数
|
||||
*/
|
||||
private Long totalFlights;
|
||||
|
||||
/**
|
||||
* 成功次数
|
||||
*/
|
||||
private Long successCount;
|
||||
|
||||
/**
|
||||
* 失败次数
|
||||
*/
|
||||
private Long failureCount;
|
||||
|
||||
/**
|
||||
* 取消次数
|
||||
*/
|
||||
private Long cancelledCount;
|
||||
|
||||
/**
|
||||
* 总飞行时长(秒)
|
||||
*/
|
||||
private Long totalDuration;
|
||||
|
||||
/**
|
||||
* 总飞行距离(米)
|
||||
*/
|
||||
private Double totalDistance;
|
||||
|
||||
/**
|
||||
* 平均飞行时长(秒)
|
||||
*/
|
||||
public Long getAverageDuration() {
|
||||
if (successCount == null || successCount == 0) {
|
||||
return 0L;
|
||||
}
|
||||
return totalDuration / successCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* 成功率
|
||||
*/
|
||||
public double getSuccessRate() {
|
||||
if (totalFlights == null || totalFlights == 0) {
|
||||
return 0.0;
|
||||
}
|
||||
return (double) successCount / totalFlights * 100;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package com.tuoheng.airport.fms.domain.model;
|
||||
|
||||
/**
|
||||
* 飞行状态枚举
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
public enum FlightStatus {
|
||||
|
||||
PREPARING("准备中"),
|
||||
EXECUTING("执行中"),
|
||||
PAUSED("已暂停"),
|
||||
COMPLETED("已完成"),
|
||||
CANCELLED("已取消"),
|
||||
FAILED("失败");
|
||||
|
||||
private final String description;
|
||||
|
||||
FlightStatus(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
package com.tuoheng.airport.fms.domain.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 飞行轨迹点领域模型
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class TrajectoryPoint {
|
||||
|
||||
private Long id;
|
||||
private Long flightRecordId;
|
||||
private Double latitude;
|
||||
private Double longitude;
|
||||
private Double altitude;
|
||||
private Double speed;
|
||||
private Integer batteryLevel;
|
||||
private LocalDateTime recordTime;
|
||||
|
||||
public static TrajectoryPoint create(Long flightRecordId, Double latitude, Double longitude,
|
||||
Double altitude, Double speed, Integer batteryLevel) {
|
||||
return TrajectoryPoint.builder()
|
||||
.flightRecordId(flightRecordId)
|
||||
.latitude(latitude)
|
||||
.longitude(longitude)
|
||||
.altitude(altitude)
|
||||
.speed(speed)
|
||||
.batteryLevel(batteryLevel)
|
||||
.recordTime(LocalDateTime.now())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
package com.tuoheng.airport.fms.domain.repository;
|
||||
|
||||
import com.tuoheng.airport.fms.domain.model.FlightRecord;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 飞行记录仓储接口
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
public interface FlightRecordRepository {
|
||||
|
||||
FlightRecord save(FlightRecord flightRecord);
|
||||
|
||||
Optional<FlightRecord> findById(Long id);
|
||||
|
||||
Optional<FlightRecord> findByFlightCode(String flightCode);
|
||||
|
||||
List<FlightRecord> findByTaskId(Long taskId);
|
||||
|
||||
List<FlightRecord> findByDeviceId(Long deviceId);
|
||||
|
||||
List<FlightRecord> findByStatus(String status);
|
||||
|
||||
List<FlightRecord> findActiveFlights();
|
||||
|
||||
void delete(Long id);
|
||||
|
||||
long countByTaskId(Long taskId);
|
||||
|
||||
long countByDeviceId(Long deviceId);
|
||||
|
||||
long countByStatus(String status);
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
package com.tuoheng.airport.fms.domain.repository;
|
||||
|
||||
import com.tuoheng.airport.fms.domain.model.TrajectoryPoint;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 飞行轨迹点仓储接口
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
public interface TrajectoryPointRepository {
|
||||
|
||||
TrajectoryPoint save(TrajectoryPoint trajectoryPoint);
|
||||
|
||||
List<TrajectoryPoint> findByFlightRecordId(Long flightRecordId);
|
||||
|
||||
void deleteByFlightRecordId(Long flightRecordId);
|
||||
|
||||
long countByFlightRecordId(Long flightRecordId);
|
||||
}
|
||||
|
|
@ -0,0 +1,359 @@
|
|||
package com.tuoheng.airport.fms.domain.service;
|
||||
|
||||
import com.tuoheng.airport.fms.domain.model.FlightRecord;
|
||||
import com.tuoheng.airport.fms.domain.model.FlightStatus;
|
||||
import com.tuoheng.airport.fms.domain.model.TrajectoryPoint;
|
||||
import com.tuoheng.airport.fms.domain.model.FlightStatistics;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 飞行领域服务接口(Domain层)
|
||||
* 封装飞行执行相关的业务逻辑和业务规则
|
||||
*
|
||||
* Domain Service 的职责:
|
||||
* 1. 封装飞行执行的核心业务规则
|
||||
* 2. 协调飞行记录、设备、航线的关系
|
||||
* 3. 保证飞行数据的一致性
|
||||
* 4. 不包含事务管理(事务由 Application 层管理)
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
public interface FlightDomainService {
|
||||
|
||||
/**
|
||||
* 验证飞行可以执行
|
||||
* 业务规则:
|
||||
* 1. 航线必须已审核通过
|
||||
* 2. 设备必须在线且可用(激活状态)
|
||||
* 3. 设备必须已分配到机场
|
||||
* 4. 天气条件满足飞行要求(风速、降雨等)
|
||||
* 5. 空域没有冲突
|
||||
* 6. 设备电量充足(>20%)
|
||||
*
|
||||
* @param airlineId 航线ID
|
||||
* @param deviceId 设备ID
|
||||
* @return 是否可以执行
|
||||
*/
|
||||
boolean canExecuteFlight(Long airlineId, Long deviceId);
|
||||
|
||||
/**
|
||||
* 创建飞行记录
|
||||
* 业务规则:
|
||||
* 1. 生成唯一的飞行编号
|
||||
* 2. 记录飞行计划信息(航线、设备、预计时长)
|
||||
* 3. 初始化飞行状态为准备中
|
||||
* 4. 锁定设备资源
|
||||
*
|
||||
* @param flightRecord 飞行记录领域模型
|
||||
* @return 创建后的飞行记录
|
||||
*/
|
||||
FlightRecord createFlightRecord(FlightRecord flightRecord);
|
||||
|
||||
/**
|
||||
* 开始飞行
|
||||
* 业务规则:
|
||||
* 1. 验证飞行状态为准备中
|
||||
* 2. 下发飞行指令到设备
|
||||
* 3. 更新飞行状态为执行中
|
||||
* 4. 记录起飞时间
|
||||
* 5. 启动实时监控
|
||||
*
|
||||
* @param recordId 飞行记录ID
|
||||
* @return 更新后的飞行记录
|
||||
*/
|
||||
FlightRecord startFlight(Long recordId);
|
||||
|
||||
/**
|
||||
* 取消飞行
|
||||
* 业务规则:
|
||||
* 1. 只有准备中或执行中的飞行可以取消
|
||||
* 2. 发送取消指令到设备
|
||||
* 3. 释放设备资源
|
||||
* 4. 更新飞行状态为已取消
|
||||
* 5. 记录取消原因和时间
|
||||
*
|
||||
* @param recordId 飞行记录ID
|
||||
* @param reason 取消原因
|
||||
* @return 更新后的飞行记录
|
||||
*/
|
||||
FlightRecord cancelFlight(Long recordId, String reason);
|
||||
|
||||
/**
|
||||
* 暂停飞行
|
||||
* 业务规则:
|
||||
* 1. 只有执行中的飞行可以暂停
|
||||
* 2. 发送暂停指令到设备(悬停)
|
||||
* 3. 更新飞行状态为已暂停
|
||||
* 4. 记录暂停时间
|
||||
*
|
||||
* @param recordId 飞行记录ID
|
||||
* @return 更新后的飞行记录
|
||||
*/
|
||||
FlightRecord pauseFlight(Long recordId);
|
||||
|
||||
/**
|
||||
* 恢复飞行
|
||||
* 业务规则:
|
||||
* 1. 只有已暂停的飞行可以恢复
|
||||
* 2. 发送恢复指令到设备
|
||||
* 3. 更新飞行状态为执行中
|
||||
* 4. 记录恢复时间
|
||||
*
|
||||
* @param recordId 飞行记录ID
|
||||
* @return 更新后的飞行记录
|
||||
*/
|
||||
FlightRecord resumeFlight(Long recordId);
|
||||
|
||||
/**
|
||||
* 完成飞行
|
||||
* 业务规则:
|
||||
* 1. 记录降落时间
|
||||
* 2. 计算实际飞行时长
|
||||
* 3. 计算实际飞行距离
|
||||
* 4. 释放设备资源
|
||||
* 5. 更新飞行状态为已完成
|
||||
* 6. 触发媒体文件上传任务
|
||||
*
|
||||
* @param recordId 飞行记录ID
|
||||
* @return 更新后的飞行记录
|
||||
*/
|
||||
FlightRecord completeFlight(Long recordId);
|
||||
|
||||
/**
|
||||
* 飞行失败
|
||||
* 业务规则:
|
||||
* 1. 记录失败原因和时间
|
||||
* 2. 释放设备资源
|
||||
* 3. 更新飞行状态为失败
|
||||
* 4. 发送告警通知
|
||||
* 5. 记录异常日志
|
||||
*
|
||||
* @param recordId 飞行记录ID
|
||||
* @param reason 失败原因
|
||||
* @return 更新后的飞行记录
|
||||
*/
|
||||
FlightRecord failFlight(Long recordId, String reason);
|
||||
|
||||
/**
|
||||
* 更新飞行实时状态
|
||||
* 接收设备上报的实时数据(位置、高度、速度、电量等)
|
||||
* 业务规则:
|
||||
* 1. 验证飞行状态为执行中
|
||||
* 2. 更新实时状态信息
|
||||
* 3. 记录轨迹点
|
||||
* 4. 检查异常情况(低电量、失联、偏航等)
|
||||
*
|
||||
* @param recordId 飞行记录ID
|
||||
* @param status 飞行状态
|
||||
* @return 更新后的飞行记录
|
||||
*/
|
||||
FlightRecord updateFlightStatus(Long recordId, FlightStatus status);
|
||||
|
||||
/**
|
||||
* 记录飞行轨迹点
|
||||
* 业务规则:
|
||||
* 1. 验证飞行状态为执行中
|
||||
* 2. 记录轨迹点(位置、高度、速度、时间)
|
||||
* 3. 轨迹点按时间排序
|
||||
*
|
||||
* @param recordId 飞行记录ID
|
||||
* @param point 轨迹点
|
||||
*/
|
||||
void recordTrajectoryPoint(Long recordId, TrajectoryPoint point);
|
||||
|
||||
/**
|
||||
* 查询飞行记录详情
|
||||
*
|
||||
* @param recordId 飞行记录ID
|
||||
* @return 飞行记录领域模型
|
||||
*/
|
||||
FlightRecord getFlightRecordById(Long recordId);
|
||||
|
||||
/**
|
||||
* 查询飞行实时状态
|
||||
*
|
||||
* @param recordId 飞行记录ID
|
||||
* @return 飞行状态
|
||||
*/
|
||||
FlightStatus getFlightStatus(Long recordId);
|
||||
|
||||
/**
|
||||
* 查询飞行轨迹
|
||||
*
|
||||
* @param recordId 飞行记录ID
|
||||
* @return 轨迹点列表
|
||||
*/
|
||||
List<TrajectoryPoint> getFlightTrajectory(Long recordId);
|
||||
|
||||
/**
|
||||
* 查询正在执行的飞行列表
|
||||
*
|
||||
* @return 飞行记录列表
|
||||
*/
|
||||
List<FlightRecord> getActiveFlights();
|
||||
|
||||
/**
|
||||
* 检查飞行是否超时
|
||||
* 业务规则:
|
||||
* 1. 飞行时长超过预计时长的 150%
|
||||
* 2. 自动标记为异常
|
||||
* 3. 发送告警通知
|
||||
*
|
||||
* @param recordId 飞行记录ID
|
||||
* @return 是否超时
|
||||
*/
|
||||
boolean isFlightTimeout(Long recordId);
|
||||
|
||||
/**
|
||||
* 检查设备是否失联
|
||||
* 业务规则:
|
||||
* 1. 超过30秒未收到设备心跳
|
||||
* 2. 标记为失联状态
|
||||
* 3. 发送告警通知
|
||||
*
|
||||
* @param recordId 飞行记录ID
|
||||
* @return 是否失联
|
||||
*/
|
||||
boolean isDeviceDisconnected(Long recordId);
|
||||
|
||||
/**
|
||||
* 检查设备电量是否充足
|
||||
* 业务规则:
|
||||
* 1. 电量低于20%时发送低电量告警
|
||||
* 2. 电量低于10%时强制返航
|
||||
*
|
||||
* @param recordId 飞行记录ID
|
||||
* @return 电量是否充足
|
||||
*/
|
||||
boolean isBatteryLevelSufficient(Long recordId);
|
||||
|
||||
/**
|
||||
* 检查飞行是否偏航
|
||||
* 业务规则:
|
||||
* 1. 实际位置与计划航线偏离超过50米
|
||||
* 2. 标记为偏航状态
|
||||
* 3. 发送告警通知
|
||||
*
|
||||
* @param recordId 飞行记录ID
|
||||
* @return 是否偏航
|
||||
*/
|
||||
boolean isFlightOffCourse(Long recordId);
|
||||
|
||||
/**
|
||||
* 检查空域冲突
|
||||
* 业务规则:
|
||||
* 1. 检查同一时间段内是否有其他飞行
|
||||
* 2. 检查航线是否有重叠
|
||||
* 3. 检查飞行高度是否有冲突
|
||||
*
|
||||
* @param airlineId 航线ID
|
||||
* @param startTime 开始时间
|
||||
* @return 是否有冲突
|
||||
*/
|
||||
boolean hasAirspaceConflict(Long airlineId, String startTime);
|
||||
|
||||
/**
|
||||
* 计算飞行统计信息
|
||||
* 业务规则:
|
||||
* 1. 统计飞行总次数
|
||||
* 2. 统计成功/失败次数
|
||||
* 3. 统计总飞行时长
|
||||
* 4. 统计总飞行距离
|
||||
* 5. 计算平均飞行时长
|
||||
* 6. 按日期分组统计
|
||||
*
|
||||
* @param deviceId 设备ID(可选)
|
||||
* @param startDate 开始日期(可选)
|
||||
* @param endDate 结束日期(可选)
|
||||
* @return 飞行统计信息
|
||||
*/
|
||||
FlightStatistics calculateStatistics(Long deviceId, String startDate, String endDate);
|
||||
|
||||
/**
|
||||
* 发送飞行控制指令
|
||||
* 业务规则:
|
||||
* 1. 验证飞行状态
|
||||
* 2. 构建控制指令
|
||||
* 3. 通过MQTT发送到设备
|
||||
* 4. 记录指令日志
|
||||
*
|
||||
* @param recordId 飞行记录ID
|
||||
* @param command 控制指令
|
||||
* @param params 指令参数
|
||||
*/
|
||||
void sendFlightCommand(Long recordId, String command, Object params);
|
||||
|
||||
/**
|
||||
* 返航
|
||||
* 业务规则:
|
||||
* 1. 验证飞行状态为执行中
|
||||
* 2. 发送返航指令到设备
|
||||
* 3. 记录返航时间
|
||||
*
|
||||
* @param recordId 飞行记录ID
|
||||
* @return 更新后的飞行记录
|
||||
*/
|
||||
FlightRecord returnHome(Long recordId);
|
||||
|
||||
/**
|
||||
* 起飞
|
||||
* 业务规则:
|
||||
* 1. 验证飞行状态为准备中
|
||||
* 2. 发送起飞指令到设备
|
||||
* 3. 更新飞行状态为执行中
|
||||
* 4. 记录起飞时间
|
||||
*
|
||||
* @param recordId 飞行记录ID
|
||||
* @return 更新后的飞行记录
|
||||
*/
|
||||
FlightRecord takeoff(Long recordId);
|
||||
|
||||
/**
|
||||
* 降落
|
||||
* 业务规则:
|
||||
* 1. 验证飞行状态为执行中
|
||||
* 2. 发送降落指令到设备
|
||||
* 3. 记录降落时间
|
||||
*
|
||||
* @param recordId 飞行记录ID
|
||||
* @return 更新后的飞行记录
|
||||
*/
|
||||
FlightRecord land(Long recordId);
|
||||
|
||||
/**
|
||||
* 调整飞行高度
|
||||
* 业务规则:
|
||||
* 1. 验证飞行状态为执行中
|
||||
* 2. 验证目标高度合法性(0-500米)
|
||||
* 3. 发送调整高度指令到设备
|
||||
*
|
||||
* @param recordId 飞行记录ID
|
||||
* @param altitude 目标高度(米)
|
||||
*/
|
||||
void adjustAltitude(Long recordId, Integer altitude);
|
||||
|
||||
/**
|
||||
* 调整飞行速度
|
||||
* 业务规则:
|
||||
* 1. 验证飞行状态为执行中
|
||||
* 2. 验证目标速度合法性(1-20m/s)
|
||||
* 3. 发送调整速度指令到设备
|
||||
*
|
||||
* @param recordId 飞行记录ID
|
||||
* @param speed 目标速度(m/s)
|
||||
*/
|
||||
void adjustSpeed(Long recordId, Double speed);
|
||||
|
||||
/**
|
||||
* 跳转到指定航点
|
||||
* 业务规则:
|
||||
* 1. 验证飞行状态为执行中
|
||||
* 2. 验证航点索引合法性
|
||||
* 3. 发送跳转航点指令到设备
|
||||
*
|
||||
* @param recordId 飞行记录ID
|
||||
* @param waypointIndex 航点索引
|
||||
*/
|
||||
void gotoWaypoint(Long recordId, Integer waypointIndex);
|
||||
}
|
||||
|
|
@ -0,0 +1,289 @@
|
|||
package com.tuoheng.airport.fms.presentation.controller;
|
||||
|
||||
import com.tuoheng.airport.fms.application.dto.*;
|
||||
import com.tuoheng.airport.fms.application.service.FlightApplicationService;
|
||||
import com.tuoheng.airport.fms.presentation.converter.FlightVoConverter;
|
||||
import com.tuoheng.airport.fms.presentation.vo.*;
|
||||
import com.tuoheng.common.response.Result;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 飞行管理控制器(Presentation层)
|
||||
* 提供单次飞行管理的 REST API 接口
|
||||
* 负责飞行执行、控制、监控
|
||||
*
|
||||
* 职责:
|
||||
* 1. 接收前端的 VO 对象
|
||||
* 2. 将 VO 转换为 DTO 传递给 Application 层
|
||||
* 3. 将 Application 层返回的 DTO 转换为 VO 返回给前端
|
||||
* 4. 不包含任何业务逻辑
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/flights")
|
||||
@RequiredArgsConstructor
|
||||
@Validated
|
||||
@Tag(name = "飞行管理", description = "单次飞行管理相关接口")
|
||||
public class FlightController {
|
||||
|
||||
private final FlightApplicationService flightApplicationService;
|
||||
|
||||
/**
|
||||
* 执行单次飞行
|
||||
* POST /api/v1/flights/execute
|
||||
*/
|
||||
@PostMapping("/execute")
|
||||
@Operation(summary = "执行单次飞行", description = "执行一次飞行任务")
|
||||
public Result<FlightRecordVO> executeFlight(@Valid @RequestBody FlightExecuteVO vo) {
|
||||
log.info("接收到执行飞行请求: {}", vo);
|
||||
FlightExecuteRequest request = FlightVoConverter.toExecuteRequest(vo);
|
||||
FlightRecordResponse response = flightApplicationService.executeFlight(request);
|
||||
FlightRecordVO result = FlightVoConverter.toVO(response);
|
||||
return Result.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 紧急飞行(一键起飞)
|
||||
* POST /api/v1/flights/emergency
|
||||
*/
|
||||
@PostMapping("/emergency")
|
||||
@Operation(summary = "紧急飞行", description = "执行紧急飞行任务,动态生成航线")
|
||||
public Result<FlightRecordVO> emergencyFlight(@Valid @RequestBody EmergencyFlightVO vo) {
|
||||
log.info("接收到紧急飞行请求: {}", vo);
|
||||
EmergencyFlightRequest request = FlightVoConverter.toEmergencyRequest(vo);
|
||||
FlightRecordResponse response = flightApplicationService.emergencyFlight(request);
|
||||
FlightRecordVO result = FlightVoConverter.toVO(response);
|
||||
return Result.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消飞行
|
||||
* POST /api/v1/flights/{recordId}/cancel
|
||||
*/
|
||||
@PostMapping("/{recordId}/cancel")
|
||||
@Operation(summary = "取消飞行", description = "取消正在执行或准备中的飞行")
|
||||
public Result<Void> cancelFlight(
|
||||
@Parameter(description = "飞行记录ID") @PathVariable Long recordId,
|
||||
@RequestParam(required = false) String reason) {
|
||||
log.info("接收到取消飞行请求,飞行记录ID: {}, 取消原因: {}", recordId, reason);
|
||||
flightApplicationService.cancelFlight(recordId, reason);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 暂停飞行
|
||||
* POST /api/v1/flights/{recordId}/pause
|
||||
*/
|
||||
@PostMapping("/{recordId}/pause")
|
||||
@Operation(summary = "暂停飞行", description = "暂停正在执行的飞行")
|
||||
public Result<Void> pauseFlight(
|
||||
@Parameter(description = "飞行记录ID") @PathVariable Long recordId) {
|
||||
log.info("接收到暂停飞行请求,飞行记录ID: {}", recordId);
|
||||
flightApplicationService.pauseFlight(recordId);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 恢复飞行
|
||||
* POST /api/v1/flights/{recordId}/resume
|
||||
*/
|
||||
@PostMapping("/{recordId}/resume")
|
||||
@Operation(summary = "恢复飞行", description = "恢复已暂停的飞行")
|
||||
public Result<Void> resumeFlight(
|
||||
@Parameter(description = "飞行记录ID") @PathVariable Long recordId) {
|
||||
log.info("接收到恢复飞行请求,飞行记录ID: {}", recordId);
|
||||
flightApplicationService.resumeFlight(recordId);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返航
|
||||
* POST /api/v1/flights/{recordId}/return-home
|
||||
*/
|
||||
@PostMapping("/{recordId}/return-home")
|
||||
@Operation(summary = "返航", description = "命令无人机返回起飞点")
|
||||
public Result<Void> returnHome(
|
||||
@Parameter(description = "飞行记录ID") @PathVariable Long recordId) {
|
||||
log.info("接收到返航请求,飞行记录ID: {}", recordId);
|
||||
flightApplicationService.returnHome(recordId);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 无人机起飞
|
||||
* POST /api/v1/flights/{recordId}/takeoff
|
||||
*/
|
||||
@PostMapping("/{recordId}/takeoff")
|
||||
@Operation(summary = "无人机起飞", description = "命令无人机起飞")
|
||||
public Result<Void> takeoff(
|
||||
@Parameter(description = "飞行记录ID") @PathVariable Long recordId) {
|
||||
log.info("接收到起飞请求,飞行记录ID: {}", recordId);
|
||||
flightApplicationService.takeoff(recordId);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 无人机降落
|
||||
* POST /api/v1/flights/{recordId}/land
|
||||
*/
|
||||
@PostMapping("/{recordId}/land")
|
||||
@Operation(summary = "无人机降落", description = "命令无人机降落")
|
||||
public Result<Void> land(
|
||||
@Parameter(description = "飞行记录ID") @PathVariable Long recordId) {
|
||||
log.info("接收到降落请求,飞行记录ID: {}", recordId);
|
||||
flightApplicationService.land(recordId);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 调整飞行高度
|
||||
* POST /api/v1/flights/{recordId}/altitude
|
||||
*/
|
||||
@PostMapping("/{recordId}/altitude")
|
||||
@Operation(summary = "调整飞行高度", description = "调整无人机的飞行高度")
|
||||
public Result<Void> adjustAltitude(
|
||||
@Parameter(description = "飞行记录ID") @PathVariable Long recordId,
|
||||
@Parameter(description = "目标高度(米)") @RequestParam Integer altitude) {
|
||||
log.info("接收到调整飞行高度请求,飞行记录ID: {}, 目标高度: {}米", recordId, altitude);
|
||||
flightApplicationService.adjustAltitude(recordId, altitude);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 调整飞行速度
|
||||
* POST /api/v1/flights/{recordId}/speed
|
||||
*/
|
||||
@PostMapping("/{recordId}/speed")
|
||||
@Operation(summary = "调整飞行速度", description = "调整无人机的飞行速度")
|
||||
public Result<Void> adjustSpeed(
|
||||
@Parameter(description = "飞行记录ID") @PathVariable Long recordId,
|
||||
@Parameter(description = "目标速度(m/s)") @RequestParam Double speed) {
|
||||
log.info("接收到调整飞行速度请求,飞行记录ID: {}, 目标速度: {}m/s", recordId, speed);
|
||||
flightApplicationService.adjustSpeed(recordId, speed);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转到指定航点
|
||||
* POST /api/v1/flights/{recordId}/goto-waypoint
|
||||
*/
|
||||
@PostMapping("/{recordId}/goto-waypoint")
|
||||
@Operation(summary = "跳转到指定航点", description = "命令无人机跳转到指定航点")
|
||||
public Result<Void> gotoWaypoint(
|
||||
@Parameter(description = "飞行记录ID") @PathVariable Long recordId,
|
||||
@Parameter(description = "航点索引") @RequestParam Integer waypointIndex) {
|
||||
log.info("接收到跳转航点请求,飞行记录ID: {}, 航点索引: {}", recordId, waypointIndex);
|
||||
flightApplicationService.gotoWaypoint(recordId, waypointIndex);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询飞行记录详情
|
||||
* GET /api/v1/flights/{recordId}
|
||||
*/
|
||||
@GetMapping("/{recordId}")
|
||||
@Operation(summary = "查询飞行记录详情", description = "根据飞行记录ID查询详细信息")
|
||||
public Result<FlightRecordVO> getFlightRecord(
|
||||
@Parameter(description = "飞行记录ID") @PathVariable Long recordId) {
|
||||
log.info("接收到查询飞行记录请求,飞行记录ID: {}", recordId);
|
||||
FlightRecordResponse response = flightApplicationService.getFlightRecord(recordId);
|
||||
FlightRecordVO result = FlightVoConverter.toVO(response);
|
||||
return Result.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询飞行实时状态
|
||||
* GET /api/v1/flights/{recordId}/status
|
||||
*/
|
||||
@GetMapping("/{recordId}/status")
|
||||
@Operation(summary = "查询飞行实时状态", description = "查询飞行的实时状态信息")
|
||||
public Result<FlightStatusVO> getFlightStatus(
|
||||
@Parameter(description = "飞行记录ID") @PathVariable Long recordId) {
|
||||
log.info("接收到查询飞行状态请求,飞行记录ID: {}", recordId);
|
||||
FlightStatusResponse response = flightApplicationService.getFlightStatus(recordId);
|
||||
FlightStatusVO result = FlightVoConverter.toStatusVO(response);
|
||||
return Result.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询飞行轨迹
|
||||
* GET /api/v1/flights/{recordId}/trajectory
|
||||
*/
|
||||
@GetMapping("/{recordId}/trajectory")
|
||||
@Operation(summary = "查询飞行轨迹", description = "查询飞行的历史轨迹点")
|
||||
public Result<List<TrajectoryPointVO>> getFlightTrajectory(
|
||||
@Parameter(description = "飞行记录ID") @PathVariable Long recordId) {
|
||||
log.info("接收到查询飞行轨迹请求,飞行记录ID: {}", recordId);
|
||||
List<TrajectoryPointResponse> responses = flightApplicationService.getFlightTrajectory(recordId);
|
||||
List<TrajectoryPointVO> results = FlightVoConverter.toTrajectoryVOList(responses);
|
||||
return Result.success(results);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询飞行记录列表
|
||||
* GET /api/v1/flights
|
||||
*/
|
||||
@GetMapping
|
||||
@Operation(summary = "查询飞行记录列表", description = "根据条件查询飞行记录列表")
|
||||
public Result<List<FlightRecordVO>> queryFlightRecords(FlightRecordQueryVO vo) {
|
||||
log.info("接收到查询飞行记录列表请求,查询条件: {}", vo);
|
||||
FlightRecordQueryRequest request = FlightVoConverter.toQueryRequest(vo);
|
||||
List<FlightRecordResponse> responses = flightApplicationService.queryFlightRecords(request);
|
||||
List<FlightRecordVO> results = FlightVoConverter.toVOList(responses);
|
||||
return Result.success(results);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询设备的飞行记录
|
||||
* GET /api/v1/flights/device/{deviceId}
|
||||
*/
|
||||
@GetMapping("/device/{deviceId}")
|
||||
@Operation(summary = "查询设备飞行记录", description = "查询指定设备的所有飞行记录")
|
||||
public Result<List<FlightRecordVO>> getFlightRecordsByDevice(
|
||||
@Parameter(description = "设备ID") @PathVariable Long deviceId) {
|
||||
log.info("接收到查询设备飞行记录请求,设备ID: {}", deviceId);
|
||||
List<FlightRecordResponse> responses = flightApplicationService.getFlightRecordsByDevice(deviceId);
|
||||
List<FlightRecordVO> results = FlightVoConverter.toVOList(responses);
|
||||
return Result.success(results);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询正在执行的飞行列表
|
||||
* GET /api/v1/flights/active
|
||||
*/
|
||||
@GetMapping("/active")
|
||||
@Operation(summary = "查询正在执行的飞行", description = "查询所有正在执行的飞行任务")
|
||||
public Result<List<FlightRecordVO>> getActiveFlights() {
|
||||
log.info("接收到查询正在执行的飞行请求");
|
||||
List<FlightRecordResponse> responses = flightApplicationService.getActiveFlights();
|
||||
List<FlightRecordVO> results = FlightVoConverter.toVOList(responses);
|
||||
return Result.success(results);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询飞行统计信息
|
||||
* GET /api/v1/flights/statistics
|
||||
*/
|
||||
@GetMapping("/statistics")
|
||||
@Operation(summary = "查询飞行统计", description = "查询飞行统计信息")
|
||||
public Result<FlightStatisticsVO> getFlightStatistics(
|
||||
@Parameter(description = "设备ID") @RequestParam(required = false) Long deviceId,
|
||||
@Parameter(description = "开始日期") @RequestParam(required = false) String startDate,
|
||||
@Parameter(description = "结束日期") @RequestParam(required = false) String endDate) {
|
||||
log.info("接收到查询飞行统计请求,设备ID: {}, 开始日期: {}, 结束日期: {}", deviceId, startDate, endDate);
|
||||
FlightStatisticsResponse response = flightApplicationService.getFlightStatistics(deviceId, startDate, endDate);
|
||||
FlightStatisticsVO result = FlightVoConverter.toStatisticsVO(response);
|
||||
return Result.success(result);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
package com.tuoheng.airport.fms.presentation.converter;
|
||||
|
||||
import com.tuoheng.airport.fms.application.dto.*;
|
||||
import com.tuoheng.airport.fms.presentation.vo.*;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class FlightVoConverter {
|
||||
|
||||
public static FlightExecuteRequest toExecuteRequest(FlightExecuteVO vo) {
|
||||
// TODO: 实现转换逻辑
|
||||
return new FlightExecuteRequest();
|
||||
}
|
||||
|
||||
public static EmergencyFlightRequest toEmergencyRequest(EmergencyFlightVO vo) {
|
||||
// TODO: 实现转换逻辑
|
||||
return new EmergencyFlightRequest();
|
||||
}
|
||||
|
||||
public static FlightRecordQueryRequest toQueryRequest(FlightRecordQueryVO vo) {
|
||||
// TODO: 实现转换逻辑
|
||||
return new FlightRecordQueryRequest();
|
||||
}
|
||||
|
||||
public static FlightRecordVO toVO(FlightRecordResponse response) {
|
||||
// TODO: 实现转换逻辑
|
||||
return new FlightRecordVO();
|
||||
}
|
||||
|
||||
public static List<FlightRecordVO> toVOList(List<FlightRecordResponse> responses) {
|
||||
return responses.stream().map(FlightVoConverter::toVO).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static FlightStatusVO toStatusVO(FlightStatusResponse response) {
|
||||
// TODO: 实现转换逻辑
|
||||
return new FlightStatusVO();
|
||||
}
|
||||
|
||||
public static TrajectoryPointVO toTrajectoryVO(TrajectoryPointResponse response) {
|
||||
// TODO: 实现转换逻辑
|
||||
return new TrajectoryPointVO();
|
||||
}
|
||||
|
||||
public static List<TrajectoryPointVO> toTrajectoryVOList(List<TrajectoryPointResponse> responses) {
|
||||
return responses.stream().map(FlightVoConverter::toTrajectoryVO).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static FlightStatisticsVO toStatisticsVO(FlightStatisticsResponse response) {
|
||||
// TODO: 实现转换逻辑
|
||||
return new FlightStatisticsVO();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
package com.tuoheng.airport.fms.presentation.vo;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* 紧急飞行 VO
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class EmergencyFlightVO {
|
||||
|
||||
/**
|
||||
* 起点纬度
|
||||
*/
|
||||
@NotNull(message = "起点纬度不能为空")
|
||||
private Double startLatitude;
|
||||
|
||||
/**
|
||||
* 起点经度
|
||||
*/
|
||||
@NotNull(message = "起点经度不能为空")
|
||||
private Double startLongitude;
|
||||
|
||||
/**
|
||||
* 终点纬度
|
||||
*/
|
||||
@NotNull(message = "终点纬度不能为空")
|
||||
private Double endLatitude;
|
||||
|
||||
/**
|
||||
* 终点经度
|
||||
*/
|
||||
@NotNull(message = "终点经度不能为空")
|
||||
private Double endLongitude;
|
||||
|
||||
/**
|
||||
* 飞行高度(米)
|
||||
*/
|
||||
@NotNull(message = "飞行高度不能为空")
|
||||
private Double altitude;
|
||||
|
||||
/**
|
||||
* 设备ID(可选)
|
||||
*/
|
||||
private Long deviceId;
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
package com.tuoheng.airport.fms.presentation.vo;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* 飞行执行 VO
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class FlightExecuteVO {
|
||||
|
||||
/**
|
||||
* 任务ID(可选)
|
||||
*/
|
||||
private Long taskId;
|
||||
|
||||
/**
|
||||
* 航线ID
|
||||
*/
|
||||
@NotNull(message = "航线ID不能为空")
|
||||
private Long airlineId;
|
||||
|
||||
/**
|
||||
* 设备ID
|
||||
*/
|
||||
@NotNull(message = "设备ID不能为空")
|
||||
private Long deviceId;
|
||||
|
||||
/**
|
||||
* 计划开始时间
|
||||
*/
|
||||
private String planStartTime;
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
package com.tuoheng.airport.fms.presentation.vo;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 飞行记录查询 VO
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class FlightRecordQueryVO {
|
||||
|
||||
/**
|
||||
* 飞行编号
|
||||
*/
|
||||
private String flightCode;
|
||||
|
||||
/**
|
||||
* 任务ID
|
||||
*/
|
||||
private Long taskId;
|
||||
|
||||
/**
|
||||
* 设备ID
|
||||
*/
|
||||
private Long deviceId;
|
||||
|
||||
/**
|
||||
* 飞行状态
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 开始日期
|
||||
*/
|
||||
private String startDate;
|
||||
|
||||
/**
|
||||
* 结束日期
|
||||
*/
|
||||
private String endDate;
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tuoheng.airport.fms.presentation.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class FlightRecordVO {
|
||||
// TODO: 添加字段
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
package com.tuoheng.airport.fms.presentation.vo;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 飞行统计 VO
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class FlightStatisticsVO {
|
||||
|
||||
private Long totalFlights;
|
||||
private Long successCount;
|
||||
private Long failureCount;
|
||||
private Long cancelledCount;
|
||||
private Long totalDuration;
|
||||
private Double totalDistance;
|
||||
private Long averageDuration;
|
||||
private Double successRate;
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
package com.tuoheng.airport.fms.presentation.vo;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 飞行状态 VO
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class FlightStatusVO {
|
||||
|
||||
private Long flightRecordId;
|
||||
private String status;
|
||||
private Double latitude;
|
||||
private Double longitude;
|
||||
private Double altitude;
|
||||
private Double speed;
|
||||
private Integer batteryLevel;
|
||||
private Integer currentWaypointIndex;
|
||||
private Double flownDistance;
|
||||
private Long flownDuration;
|
||||
private LocalDateTime updateTime;
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
package com.tuoheng.airport.fms.presentation.vo;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 飞行轨迹点 VO
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class TrajectoryPointVO {
|
||||
|
||||
private Long id;
|
||||
private Long flightRecordId;
|
||||
private Double latitude;
|
||||
private Double longitude;
|
||||
private Double altitude;
|
||||
private Double speed;
|
||||
private Integer batteryLevel;
|
||||
private LocalDateTime recordTime;
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tuoheng.airport.media.application.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class MediaAnalysisResultRequest {
|
||||
// TODO: 添加字段
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tuoheng.airport.media.application.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class MediaCreateFromDeviceRequest {
|
||||
// TODO: 添加字段
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tuoheng.airport.media.application.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class MediaInfoResponse {
|
||||
// TODO: 添加字段
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tuoheng.airport.media.application.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class MediaQueryRequest {
|
||||
// TODO: 添加字段
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tuoheng.airport.media.application.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class MediaStatisticsResponse {
|
||||
// TODO: 添加字段
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package com.tuoheng.airport.media.application.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class MediaUpdateRequest {
|
||||
private Long id;
|
||||
// TODO: 添加字段
|
||||
}
|
||||
|
|
@ -0,0 +1,196 @@
|
|||
package com.tuoheng.airport.media.application.service;
|
||||
|
||||
import com.tuoheng.airport.media.application.dto.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 媒体资源应用服务接口(Application层)
|
||||
* 定义媒体资源相关的用例(Use Cases)
|
||||
* 协调领域模型完成媒体资源管理操作
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
public interface MediaApplicationService {
|
||||
|
||||
/**
|
||||
* 查询媒体资源详情
|
||||
*
|
||||
* @param id 媒体资源ID
|
||||
* @return 媒体资源响应
|
||||
*/
|
||||
MediaInfoResponse getMediaById(Long id);
|
||||
|
||||
/**
|
||||
* 根据条件查询媒体资源列表
|
||||
*
|
||||
* @param request 查询请求
|
||||
* @return 媒体资源列表
|
||||
*/
|
||||
List<MediaInfoResponse> queryMedia(MediaQueryRequest request);
|
||||
|
||||
/**
|
||||
* 根据飞行记录ID查询媒体资源列表
|
||||
* 业务逻辑:
|
||||
* 1. 验证飞行记录存在
|
||||
* 2. 查询该飞行记录的所有媒体资源
|
||||
* 3. 按拍摄时间排序
|
||||
*
|
||||
* @param flightRecordId 飞行记录ID
|
||||
* @return 媒体资源列表
|
||||
*/
|
||||
List<MediaInfoResponse> getMediaByFlightRecordId(Long flightRecordId);
|
||||
|
||||
/**
|
||||
* 根据设备ID查询媒体资源列表
|
||||
*
|
||||
* @param deviceId 设备ID
|
||||
* @return 媒体资源列表
|
||||
*/
|
||||
List<MediaInfoResponse> getMediaByDeviceId(Long deviceId);
|
||||
|
||||
/**
|
||||
* 更新媒体资源信息
|
||||
* 业务逻辑:
|
||||
* 1. 验证媒体资源存在
|
||||
* 2. 更新元数据信息(标题、描述、标签等)
|
||||
* 3. 不允许修改文件本身
|
||||
*
|
||||
* @param request 更新请求
|
||||
* @return 媒体资源响应
|
||||
*/
|
||||
MediaInfoResponse updateMedia(MediaUpdateRequest request);
|
||||
|
||||
/**
|
||||
* 删除媒体资源
|
||||
* 业务逻辑:
|
||||
* 1. 验证媒体资源存在
|
||||
* 2. 检查是否被引用(分析结果等)
|
||||
* 3. 从存储服务删除文件
|
||||
* 4. 删除媒体资源元数据(逻辑删除)
|
||||
*
|
||||
* @param id 媒体资源ID
|
||||
*/
|
||||
void deleteMedia(Long id);
|
||||
|
||||
/**
|
||||
* 批量删除媒体资源
|
||||
* 业务逻辑:
|
||||
* 1. 验证所有媒体资源
|
||||
* 2. 批量从存储服务删除
|
||||
* 3. 批量删除元数据
|
||||
*
|
||||
* @param mediaIds 媒体资源ID列表
|
||||
*/
|
||||
void batchDeleteMedia(List<Long> mediaIds);
|
||||
|
||||
/**
|
||||
* 下载媒体资源
|
||||
* 业务逻辑:
|
||||
* 1. 验证媒体资源存在
|
||||
* 2. 检查访问权限
|
||||
* 3. 从存储服务获取文件
|
||||
* 4. 记录下载日志
|
||||
*
|
||||
* @param id 媒体资源ID
|
||||
*/
|
||||
void downloadMedia(Long id);
|
||||
|
||||
/**
|
||||
* 批量下载媒体资源(ZIP压缩包)
|
||||
* 业务逻辑:
|
||||
* 1. 验证所有媒体资源
|
||||
* 2. 从存储服务获取所有文件
|
||||
* 3. 打包为ZIP文件
|
||||
* 4. 返回ZIP文件流
|
||||
*
|
||||
* @param mediaIds 媒体资源ID列表
|
||||
*/
|
||||
void batchDownloadMedia(List<Long> mediaIds);
|
||||
|
||||
/**
|
||||
* 获取媒体资源访问URL
|
||||
* 业务逻辑:
|
||||
* 1. 验证媒体资源存在
|
||||
* 2. 调用存储服务生成临时访问URL
|
||||
* 3. 设置URL有效期
|
||||
*
|
||||
* @param id 媒体资源ID
|
||||
* @param expireSeconds URL有效期(秒)
|
||||
* @return 媒体资源访问URL
|
||||
*/
|
||||
String getMediaUrl(Long id, Long expireSeconds);
|
||||
|
||||
/**
|
||||
* 标记媒体资源为已分析
|
||||
* 业务逻辑:
|
||||
* 1. 验证媒体资源存在
|
||||
* 2. 保存AI分析结果
|
||||
* 3. 更新媒体资源状态为已分析
|
||||
* 4. 触发后续处理(告警、统计等)
|
||||
*
|
||||
* @param id 媒体资源ID
|
||||
* @param analysisResult 分析结果
|
||||
* @return 媒体资源响应
|
||||
*/
|
||||
MediaInfoResponse markMediaAsAnalyzed(Long id, MediaAnalysisResultRequest analysisResult);
|
||||
|
||||
/**
|
||||
* 查询媒体资源统计信息
|
||||
* 业务逻辑:
|
||||
* 1. 统计媒体资源总数
|
||||
* 2. 按类型分组统计(图片、视频)
|
||||
* 3. 统计存储空间使用量
|
||||
* 4. 统计分析完成率
|
||||
*
|
||||
* @param flightRecordId 飞行记录ID(可选)
|
||||
* @param deviceId 设备ID(可选)
|
||||
* @return 媒体统计信息
|
||||
*/
|
||||
MediaStatisticsResponse getMediaStatistics(Long flightRecordId, Long deviceId);
|
||||
|
||||
/**
|
||||
* 同步媒体资源上传状态
|
||||
* 业务逻辑:
|
||||
* 1. 查询存储服务中的文件状态
|
||||
* 2. 更新媒体资源的上传状态
|
||||
* 3. 处理上传失败的情况
|
||||
*
|
||||
* @param id 媒体资源ID
|
||||
* @return 媒体资源响应
|
||||
*/
|
||||
MediaInfoResponse syncMediaUploadStatus(Long id);
|
||||
|
||||
/**
|
||||
* 创建媒体资源记录(由设备上报触发)
|
||||
* 业务逻辑:
|
||||
* 1. 接收设备上报的媒体信息
|
||||
* 2. 创建媒体资源记录
|
||||
* 3. 关联飞行记录
|
||||
* 4. 触发文件上传任务
|
||||
*
|
||||
* @param request 创建请求
|
||||
* @return 媒体资源响应
|
||||
*/
|
||||
MediaInfoResponse createMediaFromDevice(MediaCreateFromDeviceRequest request);
|
||||
|
||||
/**
|
||||
* 批量创建媒体资源记录
|
||||
*
|
||||
* @param requests 创建请求列表
|
||||
* @return 媒体资源列表
|
||||
*/
|
||||
List<MediaInfoResponse> batchCreateMediaFromDevice(List<MediaCreateFromDeviceRequest> requests);
|
||||
|
||||
/**
|
||||
* 重新上传媒体资源
|
||||
* 业务逻辑:
|
||||
* 1. 验证媒体资源存在
|
||||
* 2. 检查上传状态为失败
|
||||
* 3. 重新触发上传任务
|
||||
*
|
||||
* @param id 媒体资源ID
|
||||
* @return 媒体资源响应
|
||||
*/
|
||||
MediaInfoResponse retryUploadMedia(Long id);
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
package com.tuoheng.airport.media.domain.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 媒体分析结果领域模型
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class MediaAnalysisResult {
|
||||
|
||||
/**
|
||||
* 是否发现异常
|
||||
*/
|
||||
private Boolean hasAbnormality;
|
||||
|
||||
/**
|
||||
* 异常类型列表
|
||||
*/
|
||||
private List<String> abnormalityTypes;
|
||||
|
||||
/**
|
||||
* 异常描述
|
||||
*/
|
||||
private String abnormalityDescription;
|
||||
|
||||
/**
|
||||
* 置信度(0-1)
|
||||
*/
|
||||
private Double confidence;
|
||||
|
||||
/**
|
||||
* AI模型版本
|
||||
*/
|
||||
private String modelVersion;
|
||||
|
||||
/**
|
||||
* 分析耗时(毫秒)
|
||||
*/
|
||||
private Long analysisTime;
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package com.tuoheng.airport.media.domain.model;
|
||||
|
||||
/**
|
||||
* 媒体分析状态枚举
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
public enum MediaAnalysisStatus {
|
||||
|
||||
PENDING("待分析"),
|
||||
ANALYZING("分析中"),
|
||||
COMPLETED("分析完成"),
|
||||
FAILED("分析失败");
|
||||
|
||||
private final String description;
|
||||
|
||||
MediaAnalysisStatus(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
package com.tuoheng.airport.media.domain.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 媒体资源领域模型
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class MediaInfo {
|
||||
|
||||
private Long id;
|
||||
private String mediaName;
|
||||
private MediaType mediaType;
|
||||
private String fileUrl;
|
||||
private Long fileSize;
|
||||
private MediaUploadStatus uploadStatus;
|
||||
private Long flightRecordId;
|
||||
private Long deviceId;
|
||||
private String shootingLocation;
|
||||
private LocalDateTime shootingTime;
|
||||
private MediaAnalysisStatus analysisStatus;
|
||||
private String analysisResult;
|
||||
private Long tenantId;
|
||||
private LocalDateTime createTime;
|
||||
private LocalDateTime updateTime;
|
||||
private Boolean deleted;
|
||||
|
||||
// ==================== 业务方法 ====================
|
||||
|
||||
public void markAsUploading() {
|
||||
this.uploadStatus = MediaUploadStatus.UPLOADING;
|
||||
this.updateTime = LocalDateTime.now();
|
||||
}
|
||||
|
||||
public void markAsUploaded(String fileUrl) {
|
||||
this.uploadStatus = MediaUploadStatus.COMPLETED;
|
||||
this.fileUrl = fileUrl;
|
||||
this.updateTime = LocalDateTime.now();
|
||||
}
|
||||
|
||||
public void markAsUploadFailed() {
|
||||
this.uploadStatus = MediaUploadStatus.FAILED;
|
||||
this.updateTime = LocalDateTime.now();
|
||||
}
|
||||
|
||||
public void markAsAnalyzed(String analysisResult) {
|
||||
this.analysisStatus = MediaAnalysisStatus.COMPLETED;
|
||||
this.analysisResult = analysisResult;
|
||||
this.updateTime = LocalDateTime.now();
|
||||
}
|
||||
|
||||
public boolean isUploaded() {
|
||||
return this.uploadStatus == MediaUploadStatus.COMPLETED;
|
||||
}
|
||||
|
||||
public boolean isAnalyzed() {
|
||||
return this.analysisStatus == MediaAnalysisStatus.COMPLETED;
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
this.deleted = true;
|
||||
this.updateTime = LocalDateTime.now();
|
||||
}
|
||||
|
||||
public static MediaInfo create(String mediaName, MediaType mediaType, Long flightRecordId, Long deviceId, Long tenantId) {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
return MediaInfo.builder()
|
||||
.mediaName(mediaName)
|
||||
.mediaType(mediaType)
|
||||
.uploadStatus(MediaUploadStatus.PENDING)
|
||||
.analysisStatus(MediaAnalysisStatus.PENDING)
|
||||
.flightRecordId(flightRecordId)
|
||||
.deviceId(deviceId)
|
||||
.tenantId(tenantId)
|
||||
.createTime(now)
|
||||
.updateTime(now)
|
||||
.deleted(false)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
package com.tuoheng.airport.media.domain.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 媒体统计领域模型
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class MediaStatistics {
|
||||
|
||||
/**
|
||||
* 媒体总数
|
||||
*/
|
||||
private Long totalCount;
|
||||
|
||||
/**
|
||||
* 图片数量
|
||||
*/
|
||||
private Long imageCount;
|
||||
|
||||
/**
|
||||
* 视频数量
|
||||
*/
|
||||
private Long videoCount;
|
||||
|
||||
/**
|
||||
* 已上传数量
|
||||
*/
|
||||
private Long uploadedCount;
|
||||
|
||||
/**
|
||||
* 已分析数量
|
||||
*/
|
||||
private Long analyzedCount;
|
||||
|
||||
/**
|
||||
* 发现异常数量
|
||||
*/
|
||||
private Long abnormalityCount;
|
||||
|
||||
/**
|
||||
* 总存储空间(字节)
|
||||
*/
|
||||
private Long totalStorageSize;
|
||||
|
||||
/**
|
||||
* 分析完成率
|
||||
*/
|
||||
public double getAnalysisCompletionRate() {
|
||||
if (totalCount == null || totalCount == 0) {
|
||||
return 0.0;
|
||||
}
|
||||
return (double) analyzedCount / totalCount * 100;
|
||||
}
|
||||
|
||||
/**
|
||||
* 异常率
|
||||
*/
|
||||
public double getAbnormalityRate() {
|
||||
if (analyzedCount == null || analyzedCount == 0) {
|
||||
return 0.0;
|
||||
}
|
||||
return (double) abnormalityCount / analyzedCount * 100;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package com.tuoheng.airport.media.domain.model;
|
||||
|
||||
/**
|
||||
* 媒体类型枚举
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
public enum MediaType {
|
||||
|
||||
IMAGE("图片"),
|
||||
VIDEO("视频");
|
||||
|
||||
private final String description;
|
||||
|
||||
MediaType(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package com.tuoheng.airport.media.domain.model;
|
||||
|
||||
/**
|
||||
* 媒体上传状态枚举
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
public enum MediaUploadStatus {
|
||||
|
||||
PENDING("待上传"),
|
||||
UPLOADING("上传中"),
|
||||
COMPLETED("上传完成"),
|
||||
FAILED("上传失败");
|
||||
|
||||
private final String description;
|
||||
|
||||
MediaUploadStatus(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
package com.tuoheng.airport.media.domain.repository;
|
||||
|
||||
import com.tuoheng.airport.media.domain.model.MediaInfo;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 媒体资源仓储接口
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
public interface MediaInfoRepository {
|
||||
|
||||
MediaInfo save(MediaInfo mediaInfo);
|
||||
|
||||
Optional<MediaInfo> findById(Long id);
|
||||
|
||||
List<MediaInfo> findByFlightRecordId(Long flightRecordId);
|
||||
|
||||
List<MediaInfo> findByDeviceId(Long deviceId);
|
||||
|
||||
List<MediaInfo> findByUploadStatus(String uploadStatus);
|
||||
|
||||
List<MediaInfo> findByAnalysisStatus(String analysisStatus);
|
||||
|
||||
void delete(Long id);
|
||||
|
||||
void batchDelete(List<Long> ids);
|
||||
|
||||
long countByFlightRecordId(Long flightRecordId);
|
||||
|
||||
long countByDeviceId(Long deviceId);
|
||||
|
||||
long sumFileSizeByTenantId(Long tenantId);
|
||||
}
|
||||
|
|
@ -0,0 +1,227 @@
|
|||
package com.tuoheng.airport.media.domain.service;
|
||||
|
||||
import com.tuoheng.airport.media.domain.model.MediaInfo;
|
||||
import com.tuoheng.airport.media.domain.model.MediaAnalysisResult;
|
||||
import com.tuoheng.airport.media.domain.model.MediaStatistics;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 媒体资源领域服务接口(Domain层)
|
||||
* 封装媒体资源相关的业务逻辑和业务规则
|
||||
*
|
||||
* Domain Service 的职责:
|
||||
* 1. 封装媒体资源管理的核心业务规则
|
||||
* 2. 协调媒体资源和存储服务
|
||||
* 3. 保证媒体资源数据的一致性
|
||||
* 4. 不包含事务管理(事务由 Application 层管理)
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
public interface MediaDomainService {
|
||||
|
||||
/**
|
||||
* 创建媒体资源记录
|
||||
* 业务规则:
|
||||
* 1. 生成唯一的媒体资源ID
|
||||
* 2. 验证文件类型(图片/视频)
|
||||
* 3. 关联飞行记录和设备
|
||||
* 4. 初始化上传状态为待上传
|
||||
*
|
||||
* @param mediaInfo 媒体资源领域模型
|
||||
* @return 创建后的媒体资源
|
||||
*/
|
||||
MediaInfo createMediaInfo(MediaInfo mediaInfo);
|
||||
|
||||
/**
|
||||
* 批量创建媒体资源记录
|
||||
* 业务规则:
|
||||
* 1. 验证所有媒体资源
|
||||
* 2. 批量创建记录
|
||||
* 3. 关联同一飞行记录
|
||||
*
|
||||
* @param mediaInfos 媒体资源列表
|
||||
* @return 创建后的媒体资源列表
|
||||
*/
|
||||
List<MediaInfo> batchCreateMediaInfo(List<MediaInfo> mediaInfos);
|
||||
|
||||
/**
|
||||
* 更新媒体资源上传状态
|
||||
* 业务规则:
|
||||
* 1. 验证状态转换合法性(待上传->上传中->完成/失败)
|
||||
* 2. 记录上传时间
|
||||
* 3. 上传完成后触发分析任务
|
||||
*
|
||||
* @param mediaId 媒体资源ID
|
||||
* @param uploadStatus 上传状态
|
||||
* @param fileUrl 文件URL(上传完成时)
|
||||
* @return 更新后的媒体资源
|
||||
*/
|
||||
MediaInfo updateUploadStatus(Long mediaId, String uploadStatus, String fileUrl);
|
||||
|
||||
/**
|
||||
* 标记媒体资源为已分析
|
||||
* 业务规则:
|
||||
* 1. 验证媒体资源已上传完成
|
||||
* 2. 保存AI分析结果
|
||||
* 3. 更新分析状态
|
||||
* 4. 如果发现异常,触发告警
|
||||
*
|
||||
* @param mediaId 媒体资源ID
|
||||
* @param analysisResult 分析结果
|
||||
* @return 更新后的媒体资源
|
||||
*/
|
||||
MediaInfo markAsAnalyzed(Long mediaId, MediaAnalysisResult analysisResult);
|
||||
|
||||
/**
|
||||
* 删除媒体资源
|
||||
* 业务规则:
|
||||
* 1. 检查媒体资源是否被引用
|
||||
* 2. 从存储服务删除文件
|
||||
* 3. 逻辑删除媒体资源记录
|
||||
* 4. 更新统计信息
|
||||
*
|
||||
* @param mediaId 媒体资源ID
|
||||
*/
|
||||
void deleteMediaInfo(Long mediaId);
|
||||
|
||||
/**
|
||||
* 批量删除媒体资源
|
||||
* 业务规则:
|
||||
* 1. 验证所有媒体资源可以删除
|
||||
* 2. 批量从存储服务删除
|
||||
* 3. 批量删除记录
|
||||
*
|
||||
* @param mediaIds 媒体资源ID列表
|
||||
*/
|
||||
void batchDeleteMediaInfo(List<Long> mediaIds);
|
||||
|
||||
/**
|
||||
* 查询媒体资源详情
|
||||
*
|
||||
* @param mediaId 媒体资源ID
|
||||
* @return 媒体资源领域模型
|
||||
*/
|
||||
MediaInfo getMediaInfoById(Long mediaId);
|
||||
|
||||
/**
|
||||
* 根据飞行记录ID查询媒体资源列表
|
||||
*
|
||||
* @param flightRecordId 飞行记录ID
|
||||
* @return 媒体资源列表
|
||||
*/
|
||||
List<MediaInfo> getMediaInfoByFlightRecordId(Long flightRecordId);
|
||||
|
||||
/**
|
||||
* 根据设备ID查询媒体资源列表
|
||||
*
|
||||
* @param deviceId 设备ID
|
||||
* @return 媒体资源列表
|
||||
*/
|
||||
List<MediaInfo> getMediaInfoByDeviceId(Long deviceId);
|
||||
|
||||
/**
|
||||
* 生成媒体资源访问URL
|
||||
* 业务规则:
|
||||
* 1. 验证媒体资源已上传完成
|
||||
* 2. 调用存储服务生成临时URL
|
||||
* 3. 设置URL有效期
|
||||
*
|
||||
* @param mediaId 媒体资源ID
|
||||
* @param expireSeconds URL有效期(秒)
|
||||
* @return 媒体资源访问URL
|
||||
*/
|
||||
String generateMediaUrl(Long mediaId, Long expireSeconds);
|
||||
|
||||
/**
|
||||
* 检查媒体资源是否被引用
|
||||
* 业务规则:
|
||||
* 1. 检查是否有分析结果引用
|
||||
* 2. 检查是否有告警引用
|
||||
* 3. 被引用的媒体资源不能删除
|
||||
*
|
||||
* @param mediaId 媒体资源ID
|
||||
* @return 是否被引用
|
||||
*/
|
||||
boolean isMediaReferenced(Long mediaId);
|
||||
|
||||
/**
|
||||
* 计算媒体资源统计信息
|
||||
* 业务规则:
|
||||
* 1. 统计媒体资源总数
|
||||
* 2. 按类型分组统计(图片、视频)
|
||||
* 3. 统计存储空间使用量
|
||||
* 4. 统计分析完成率
|
||||
*
|
||||
* @param flightRecordId 飞行记录ID(可选)
|
||||
* @param deviceId 设备ID(可选)
|
||||
* @return 媒体统计信息
|
||||
*/
|
||||
MediaStatistics calculateStatistics(Long flightRecordId, Long deviceId);
|
||||
|
||||
/**
|
||||
* 同步媒体资源上传状态
|
||||
* 业务规则:
|
||||
* 1. 查询存储服务中的文件状态
|
||||
* 2. 更新媒体资源的上传状态
|
||||
* 3. 处理上传超时的情况
|
||||
*
|
||||
* @param mediaId 媒体资源ID
|
||||
* @return 更新后的媒体资源
|
||||
*/
|
||||
MediaInfo syncUploadStatus(Long mediaId);
|
||||
|
||||
/**
|
||||
* 重新上传媒体资源
|
||||
* 业务规则:
|
||||
* 1. 验证上传状态为失败
|
||||
* 2. 重置上传状态为待上传
|
||||
* 3. 触发上传任务
|
||||
*
|
||||
* @param mediaId 媒体资源ID
|
||||
* @return 更新后的媒体资源
|
||||
*/
|
||||
MediaInfo retryUpload(Long mediaId);
|
||||
|
||||
/**
|
||||
* 验证媒体文件类型
|
||||
* 业务规则:
|
||||
* 1. 检查文件扩展名
|
||||
* 2. 支持的图片格式:jpg, jpeg, png, bmp
|
||||
* 3. 支持的视频格式:mp4, avi, mov
|
||||
*
|
||||
* @param fileName 文件名
|
||||
* @return 是否为有效的媒体文件类型
|
||||
*/
|
||||
boolean isValidMediaType(String fileName);
|
||||
|
||||
/**
|
||||
* 获取媒体文件类型
|
||||
* 根据文件扩展名判断是图片还是视频
|
||||
*
|
||||
* @param fileName 文件名
|
||||
* @return 媒体类型(IMAGE/VIDEO)
|
||||
*/
|
||||
String getMediaType(String fileName);
|
||||
|
||||
/**
|
||||
* 清理过期的未上传媒体资源
|
||||
* 业务规则:
|
||||
* 1. 查找创建超过24小时但未上传的媒体资源
|
||||
* 2. 批量删除这些记录
|
||||
*
|
||||
* @return 清理的媒体资源数量
|
||||
*/
|
||||
int cleanExpiredUnuploadedMedia();
|
||||
|
||||
/**
|
||||
* 检查媒体资源上传是否超时
|
||||
* 业务规则:
|
||||
* 1. 上传中状态超过30分钟视为超时
|
||||
* 2. 超时后标记为上传失败
|
||||
*
|
||||
* @param mediaId 媒体资源ID
|
||||
* @return 是否超时
|
||||
*/
|
||||
boolean isUploadTimeout(Long mediaId);
|
||||
}
|
||||
|
|
@ -0,0 +1,221 @@
|
|||
package com.tuoheng.airport.media.presentation.controller;
|
||||
|
||||
import com.tuoheng.airport.media.application.dto.*;
|
||||
import com.tuoheng.airport.media.application.service.MediaApplicationService;
|
||||
import com.tuoheng.airport.media.presentation.converter.MediaVoConverter;
|
||||
import com.tuoheng.airport.media.presentation.vo.*;
|
||||
import com.tuoheng.common.response.Result;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 媒体资源管理控制器(Presentation层)
|
||||
* 提供媒体资源管理的 REST API 接口
|
||||
* 负责图片、视频等媒体文件的管理
|
||||
*
|
||||
* 职责:
|
||||
* 1. 接收前端的 VO 对象
|
||||
* 2. 将 VO 转换为 DTO 传递给 Application 层
|
||||
* 3. 将 Application 层返回的 DTO 转换为 VO 返回给前端
|
||||
* 4. 不包含任何业务逻辑
|
||||
*
|
||||
* @author tuoheng
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/media")
|
||||
@RequiredArgsConstructor
|
||||
@Validated
|
||||
@Tag(name = "媒体资源管理", description = "媒体资源管理相关接口")
|
||||
public class MediaController {
|
||||
|
||||
private final MediaApplicationService mediaApplicationService;
|
||||
|
||||
/**
|
||||
* 查询媒体资源详情
|
||||
* GET /api/v1/media/{id}
|
||||
*/
|
||||
@GetMapping("/{id}")
|
||||
@Operation(summary = "查询媒体资源详情", description = "根据媒体资源ID查询详细信息")
|
||||
public Result<MediaInfoVO> getMediaById(
|
||||
@Parameter(description = "媒体资源ID") @PathVariable Long id) {
|
||||
log.info("接收到查询媒体资源请求,媒体资源ID: {}", id);
|
||||
MediaInfoResponse response = mediaApplicationService.getMediaById(id);
|
||||
MediaInfoVO result = MediaVoConverter.toVO(response);
|
||||
return Result.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询媒体资源列表
|
||||
* GET /api/v1/media
|
||||
*/
|
||||
@GetMapping
|
||||
@Operation(summary = "查询媒体资源列表", description = "根据条件查询媒体资源列表")
|
||||
public Result<List<MediaInfoVO>> queryMedia(MediaQueryVO vo) {
|
||||
log.info("接收到查询媒体资源列表请求,查询条件: {}", vo);
|
||||
MediaQueryRequest request = MediaVoConverter.toQueryRequest(vo);
|
||||
List<MediaInfoResponse> responses = mediaApplicationService.queryMedia(request);
|
||||
List<MediaInfoVO> results = MediaVoConverter.toVOList(responses);
|
||||
return Result.success(results);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据飞行记录ID查询媒体资源列表
|
||||
* GET /api/v1/media/flight/{flightRecordId}
|
||||
*/
|
||||
@GetMapping("/flight/{flightRecordId}")
|
||||
@Operation(summary = "查询飞行媒体资源", description = "查询指定飞行记录的所有媒体资源")
|
||||
public Result<List<MediaInfoVO>> getMediaByFlightRecordId(
|
||||
@Parameter(description = "飞行记录ID") @PathVariable Long flightRecordId) {
|
||||
log.info("接收到查询飞行媒体资源请求,飞行记录ID: {}", flightRecordId);
|
||||
List<MediaInfoResponse> responses = mediaApplicationService.getMediaByFlightRecordId(flightRecordId);
|
||||
List<MediaInfoVO> results = MediaVoConverter.toVOList(responses);
|
||||
return Result.success(results);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据设备ID查询媒体资源列表
|
||||
* GET /api/v1/media/device/{deviceId}
|
||||
*/
|
||||
@GetMapping("/device/{deviceId}")
|
||||
@Operation(summary = "查询设备媒体资源", description = "查询指定设备的所有媒体资源")
|
||||
public Result<List<MediaInfoVO>> getMediaByDeviceId(
|
||||
@Parameter(description = "设备ID") @PathVariable Long deviceId) {
|
||||
log.info("接收到查询设备媒体资源请求,设备ID: {}", deviceId);
|
||||
List<MediaInfoResponse> responses = mediaApplicationService.getMediaByDeviceId(deviceId);
|
||||
List<MediaInfoVO> results = MediaVoConverter.toVOList(responses);
|
||||
return Result.success(results);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新媒体资源信息
|
||||
* PUT /api/v1/media/{id}
|
||||
*/
|
||||
@PutMapping("/{id}")
|
||||
@Operation(summary = "更新媒体资源信息", description = "更新媒体资源的元数据信息")
|
||||
public Result<MediaInfoVO> updateMedia(
|
||||
@Parameter(description = "媒体资源ID") @PathVariable Long id,
|
||||
@Valid @RequestBody MediaUpdateVO vo) {
|
||||
log.info("接收到更新媒体资源请求,媒体资源ID: {}, 请求参数: {}", id, vo);
|
||||
vo.setId(id);
|
||||
MediaUpdateRequest request = MediaVoConverter.toUpdateRequest(vo);
|
||||
MediaInfoResponse response = mediaApplicationService.updateMedia(request);
|
||||
MediaInfoVO result = MediaVoConverter.toVO(response);
|
||||
return Result.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除媒体资源
|
||||
* DELETE /api/v1/media/{id}
|
||||
*/
|
||||
@DeleteMapping("/{id}")
|
||||
@Operation(summary = "删除媒体资源", description = "删除指定的媒体资源")
|
||||
public Result<Void> deleteMedia(
|
||||
@Parameter(description = "媒体资源ID") @PathVariable Long id) {
|
||||
log.info("接收到删除媒体资源请求,媒体资源ID: {}", id);
|
||||
mediaApplicationService.deleteMedia(id);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除媒体资源
|
||||
* POST /api/v1/media/batch-delete
|
||||
*/
|
||||
@PostMapping("/batch-delete")
|
||||
@Operation(summary = "批量删除媒体资源", description = "批量删除多个媒体资源")
|
||||
public Result<Void> batchDeleteMedia(@RequestBody List<Long> mediaIds) {
|
||||
log.info("接收到批量删除媒体资源请求,媒体资源数量: {}", mediaIds.size());
|
||||
mediaApplicationService.batchDeleteMedia(mediaIds);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载媒体资源
|
||||
* GET /api/v1/media/{id}/download
|
||||
*/
|
||||
@GetMapping("/{id}/download")
|
||||
@Operation(summary = "下载媒体资源", description = "下载指定的媒体资源文件")
|
||||
public void downloadMedia(
|
||||
@Parameter(description = "媒体资源ID") @PathVariable Long id) {
|
||||
log.info("接收到下载媒体资源请求,媒体资源ID: {}", id);
|
||||
mediaApplicationService.downloadMedia(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量下载媒体资源(ZIP压缩包)
|
||||
* POST /api/v1/media/batch-download
|
||||
*/
|
||||
@PostMapping("/batch-download")
|
||||
@Operation(summary = "批量下载媒体资源", description = "批量下载多个媒体资源,打包为ZIP文件")
|
||||
public void batchDownloadMedia(@RequestBody List<Long> mediaIds) {
|
||||
log.info("接收到批量下载媒体资源请求,媒体资源数量: {}", mediaIds.size());
|
||||
mediaApplicationService.batchDownloadMedia(mediaIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取媒体资源访问URL
|
||||
* GET /api/v1/media/{id}/url
|
||||
*/
|
||||
@GetMapping("/{id}/url")
|
||||
@Operation(summary = "获取媒体资源URL", description = "获取媒体资源的临时访问URL")
|
||||
public Result<String> getMediaUrl(
|
||||
@Parameter(description = "媒体资源ID") @PathVariable Long id,
|
||||
@Parameter(description = "URL有效期(秒)") @RequestParam(defaultValue = "3600") Long expireSeconds) {
|
||||
log.info("接收到获取媒体资源URL请求,媒体资源ID: {}, 有效期: {}秒", id, expireSeconds);
|
||||
String url = mediaApplicationService.getMediaUrl(id, expireSeconds);
|
||||
return Result.success(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* 标记媒体资源为已分析
|
||||
* POST /api/v1/media/{id}/analyzed
|
||||
*/
|
||||
@PostMapping("/{id}/analyzed")
|
||||
@Operation(summary = "标记媒体已分析", description = "标记媒体资源已完成AI分析")
|
||||
public Result<MediaInfoVO> markMediaAsAnalyzed(
|
||||
@Parameter(description = "媒体资源ID") @PathVariable Long id,
|
||||
@Valid @RequestBody MediaAnalysisResultVO analysisResult) {
|
||||
log.info("接收到标记媒体已分析请求,媒体资源ID: {}", id);
|
||||
MediaAnalysisResultRequest request = MediaVoConverter.toAnalysisResultRequest(analysisResult);
|
||||
MediaInfoResponse response = mediaApplicationService.markMediaAsAnalyzed(id, request);
|
||||
MediaInfoVO result = MediaVoConverter.toVO(response);
|
||||
return Result.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询媒体资源统计信息
|
||||
* GET /api/v1/media/statistics
|
||||
*/
|
||||
@GetMapping("/statistics")
|
||||
@Operation(summary = "查询媒体统计", description = "查询媒体资源统计信息")
|
||||
public Result<MediaStatisticsVO> getMediaStatistics(
|
||||
@Parameter(description = "飞行记录ID") @RequestParam(required = false) Long flightRecordId,
|
||||
@Parameter(description = "设备ID") @RequestParam(required = false) Long deviceId) {
|
||||
log.info("接收到查询媒体统计请求,飞行记录ID: {}, 设备ID: {}", flightRecordId, deviceId);
|
||||
MediaStatisticsResponse response = mediaApplicationService.getMediaStatistics(flightRecordId, deviceId);
|
||||
MediaStatisticsVO result = MediaVoConverter.toStatisticsVO(response);
|
||||
return Result.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步媒体资源上传状态
|
||||
* POST /api/v1/media/{id}/sync-status
|
||||
*/
|
||||
@PostMapping("/{id}/sync-status")
|
||||
@Operation(summary = "同步上传状态", description = "同步媒体资源的上传状态")
|
||||
public Result<MediaInfoVO> syncMediaUploadStatus(
|
||||
@Parameter(description = "媒体资源ID") @PathVariable Long id) {
|
||||
log.info("接收到同步媒体上传状态请求,媒体资源ID: {}", id);
|
||||
MediaInfoResponse response = mediaApplicationService.syncMediaUploadStatus(id);
|
||||
MediaInfoVO result = MediaVoConverter.toVO(response);
|
||||
return Result.success(result);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
package com.tuoheng.airport.media.presentation.converter;
|
||||
|
||||
import com.tuoheng.airport.media.application.dto.*;
|
||||
import com.tuoheng.airport.media.presentation.vo.*;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class MediaVoConverter {
|
||||
|
||||
public static MediaQueryRequest toQueryRequest(MediaQueryVO vo) {
|
||||
// TODO: 实现转换逻辑
|
||||
return new MediaQueryRequest();
|
||||
}
|
||||
|
||||
public static MediaUpdateRequest toUpdateRequest(MediaUpdateVO vo) {
|
||||
// TODO: 实现转换逻辑
|
||||
return new MediaUpdateRequest();
|
||||
}
|
||||
|
||||
public static MediaInfoVO toVO(MediaInfoResponse response) {
|
||||
// TODO: 实现转换逻辑
|
||||
return new MediaInfoVO();
|
||||
}
|
||||
|
||||
public static List<MediaInfoVO> toVOList(List<MediaInfoResponse> responses) {
|
||||
return responses.stream().map(MediaVoConverter::toVO).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static MediaAnalysisResultRequest toAnalysisRequest(MediaAnalysisResultVO vo) {
|
||||
// TODO: 实现转换逻辑
|
||||
return new MediaAnalysisResultRequest();
|
||||
}
|
||||
|
||||
public static MediaAnalysisResultRequest toAnalysisResultRequest(MediaAnalysisResultVO vo) {
|
||||
// TODO: 实现转换逻辑
|
||||
return new MediaAnalysisResultRequest();
|
||||
}
|
||||
|
||||
public static MediaStatisticsVO toStatisticsVO(MediaStatisticsResponse response) {
|
||||
// TODO: 实现转换逻辑
|
||||
return new MediaStatisticsVO();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tuoheng.airport.media.presentation.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class MediaAnalysisResultVO {
|
||||
// TODO: 添加字段
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tuoheng.airport.media.presentation.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class MediaInfoVO {
|
||||
// TODO: 添加字段
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tuoheng.airport.media.presentation.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class MediaQueryVO {
|
||||
// TODO: 添加字段
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue