无人机使用率+任务执行平均周期

This commit is contained in:
温文静WWW 2025-07-17 18:55:49 +08:00
parent e0e23dbe48
commit 761e923f89
14 changed files with 128 additions and 47 deletions

View File

@ -21,7 +21,7 @@ import java.lang.annotation.*;
* @author jacky * @author jacky
* 用于标记匿名访问方法 * 用于标记匿名访问方法
*/ */
@Inherited //@Inherited
@Documented @Documented
@Target({ElementType.METHOD,ElementType.ANNOTATION_TYPE}) @Target({ElementType.METHOD,ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)

View File

@ -9,7 +9,6 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
import java.util.List; import java.util.List;
import java.util.Map;
/** /**
* <p> * <p>
@ -21,11 +20,11 @@ import java.util.Map;
*/ */
public interface AircraftDeviceMapper extends BaseMapper<AircraftDevice> { public interface AircraftDeviceMapper extends BaseMapper<AircraftDevice> {
Map<Long, AircraftDevice> getAircraftDeviceById(List<Long> deviceIds); List<AircraftDevice> getAircraftDeviceById(List<Long> deviceIds);
/** /**
* 分页查询 飞行器设备 * 分页查询 飞行器设备
*/ */
IPage<AircraftDevicePageVO> page(@Param("search")AircraftDevicePageDTO search, Page page); IPage<AircraftDevicePageVO> page(@Param("search") AircraftDevicePageDTO search, Page page);
} }

View File

@ -107,8 +107,8 @@ public class CpArticleController {
errorResponse.put("message", "不存在该文章"); errorResponse.put("message", "不存在该文章");
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(errorResponse); return ResponseEntity.status(HttpStatus.NOT_FOUND).body(errorResponse);
} }
cpArticleMapper.updateViewCountById(id); // cpArticleMapper.updateViewCountById(id);
entity.setViewCount(entity.getViewCount()+1); // entity.setViewCount(entity.getViewCount()+1);
return ResponseEntity.ok(entity); return ResponseEntity.ok(entity);
} catch (Exception e) { } catch (Exception e) {
LOG.error("查询单个文章失败", e); LOG.error("查询单个文章失败", e);

View File

@ -63,15 +63,12 @@ public class CpMaterialController {
@RequestParam(required = false) String keyword) { @RequestParam(required = false) String keyword) {
try { try {
QueryWrapper<CpMaterial> wrapper = new QueryWrapper<>(); QueryWrapper<CpMaterial> wrapper = new QueryWrapper<>();
// 仅对素材名称进行模糊查询 // 仅对素材名称进行模糊查询
if (keyword != null && !keyword.isEmpty()) { if (keyword != null && !keyword.isEmpty()) {
wrapper.like("name", keyword); wrapper.like("name", keyword);
} }
wrapper.eq("del_flag", 0); // 只查询未删除的素材
// 只查询未删除的素材 wrapper.orderByDesc("create_time");//按时间倒序排序
wrapper.eq("del_flag", 0);
IPage<CpMaterial> records = materialService.page(page, wrapper); IPage<CpMaterial> records = materialService.page(page, wrapper);
return new ResponseEntity<>(records, HttpStatus.OK); return new ResponseEntity<>(records, HttpStatus.OK);
} catch (Exception e) { } catch (Exception e) {
@ -102,7 +99,7 @@ public class CpMaterialController {
@PostMapping @PostMapping
public ResponseEntity<CpMaterial> add(@Valid @RequestBody CpMaterialDTO material) { public ResponseEntity<CpMaterial> add(@Valid @RequestBody CpMaterialDTO material) {
try { try {
CpMaterial cpMaterial=new CpMaterial(); CpMaterial cpMaterial = new CpMaterial();
BeanUtils.copyProperties(material, cpMaterial); BeanUtils.copyProperties(material, cpMaterial);
// 填充默认值 // 填充默认值
cpMaterial.setCreateTime(LocalDateTime.now()); cpMaterial.setCreateTime(LocalDateTime.now());
@ -152,6 +149,7 @@ public class CpMaterialController {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
} }
} }
@ApiOperation(value = "删除素材", notes = "逻辑删除将cp_material表中的del_flag设为1") @ApiOperation(value = "删除素材", notes = "逻辑删除将cp_material表中的del_flag设为1")
@ApiImplicitParam(name = "id", value = "素材ID", required = true, paramType = "path", dataType = "int") @ApiImplicitParam(name = "id", value = "素材ID", required = true, paramType = "path", dataType = "int")
@DeleteMapping("/{id}") @DeleteMapping("/{id}")
@ -165,7 +163,7 @@ public class CpMaterialController {
// 逻辑删除更新del_flag=1 // 逻辑删除更新del_flag=1
material.setDelFlag(1); material.setDelFlag(1);
materialService.updateDelFlagById(material.getId(),material.getDelFlag()); materialService.updateDelFlagById(material.getId(), material.getDelFlag());
// 3. 删除成功返回成功信息 // 3. 删除成功返回成功信息
Map<String, Object> success = new HashMap<>(); Map<String, Object> success = new HashMap<>();
success.put("code", 200); success.put("code", 200);

View File

@ -1,7 +1,11 @@
package com.aircraft.modules.article.controller; package com.aircraft.modules.article.controller;
import com.aircraft.annotation.rest.AnonymousAccess;
import com.aircraft.annotation.rest.AnonymousGetMapping;
import com.aircraft.modules.article.domain.CpArticle; import com.aircraft.modules.article.domain.CpArticle;
import com.aircraft.modules.article.domain.CpText; import com.aircraft.modules.article.domain.CpText;
import com.aircraft.modules.article.mapper.CpLabelMapper; import com.aircraft.modules.article.mapper.CpLabelMapper;
import com.aircraft.modules.article.mapper.CpTextMapper;
import com.aircraft.modules.article.service.CpArticleService; import com.aircraft.modules.article.service.CpArticleService;
import com.aircraft.modules.article.service.CpTextService; import com.aircraft.modules.article.service.CpTextService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
@ -44,13 +48,12 @@ public class CpTextController {
@Autowired @Autowired
private CpTextService cpTextService; private CpTextService cpTextService;
@Autowired @Autowired
private CpArticleService cpArticleService; private CpArticleService cpArticleService;
@Autowired @Autowired
private CpLabelMapper cpLabelMapper; private CpTextMapper cpTextMapper;
// @AnonymousAccess
@ApiOperation(value = "分页查询文本内容", notes = "分页查询文本内容") @ApiOperation(value = "分页查询文本内容", notes = "分页查询文本内容")
@RequestMapping(method = RequestMethod.GET) @RequestMapping(method = RequestMethod.GET)
@ApiImplicitParams({ @ApiImplicitParams({
@ -101,6 +104,8 @@ public class CpTextController {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
} }
} }
// @AnonymousAccess
@ApiOperation(value = "查询单个文本内容") @ApiOperation(value = "查询单个文本内容")
@RequestMapping(value = "{id}", method = {RequestMethod.GET}) @RequestMapping(value = "{id}", method = {RequestMethod.GET})
@ApiImplicitParam(name = "id", value = "文本内容ID", required = true, paramType = "path") @ApiImplicitParam(name = "id", value = "文本内容ID", required = true, paramType = "path")
@ -110,7 +115,8 @@ public class CpTextController {
if (entity == null) { if (entity == null) {
return ResponseEntity.notFound().build(); return ResponseEntity.notFound().build();
} }
cpTextMapper.updateViewCountById(id);
entity.setViewCount(entity.getViewCount() + 1);
return ResponseEntity.ok(entity); return ResponseEntity.ok(entity);
} catch (Exception e) { } catch (Exception e) {
LOG.error("查询单个文本内容失败", e); LOG.error("查询单个文本内容失败", e);

View File

@ -30,6 +30,9 @@ public class CpText {
@ApiModelProperty(value = "文章正文") @ApiModelProperty(value = "文章正文")
private String text; private String text;
@ApiModelProperty(value = "浏览量")
private Long viewCount;
@ApiModelProperty(value = "逻辑删除1删除0正常") @ApiModelProperty(value = "逻辑删除1删除0正常")
private Integer del_flag; private Integer del_flag;
} }

View File

@ -18,4 +18,6 @@ import org.apache.ibatis.annotations.Mapper;
public interface CpTextMapper extends BaseMapper<CpText> { public interface CpTextMapper extends BaseMapper<CpText> {
IPage<CpText> findByPage(Page<CpText> page, String keyWord); IPage<CpText> findByPage(Page<CpText> page, String keyWord);
void updateViewCountById(Integer id);
} }

View File

@ -18,6 +18,9 @@ public class OrderDetailAnalysisResult<T> {
private BigDecimal completionRate; // 任务完成率 private BigDecimal completionRate; // 任务完成率
private BigDecimal failedRate; // 任务失败率 private BigDecimal failedRate; // 任务失败率
private Double averageTaskCycle; // 平均任务周期
private BigDecimal droneUsageRate; // 无人机使用率
private List<RouteStat> routeDistribution; // 景区路线统计分布 private List<RouteStat> routeDistribution; // 景区路线统计分布
private PageInfo pageInfo; // 分页信息 private PageInfo pageInfo; // 分页信息
private List<?> orderDetailList; // 子单列表分页 private List<?> orderDetailList; // 子单列表分页

View File

@ -341,7 +341,13 @@ public class OrderAnalysisServiceImpl implements OrderAnalysisService {
.collect(Collectors.toList()); .collect(Collectors.toList());
// 获取飞行设备 ID 对应的 AircraftDevice 对象的映射 // 获取飞行设备 ID 对应的 AircraftDevice 对象的映射
Map<Long, AircraftDevice> deviceMap = aircraftDeviceMapper.getAircraftDeviceById(deviceIds); List<AircraftDevice> deviceList = aircraftDeviceMapper.getAircraftDeviceById(deviceIds);
Map<Long, AircraftDevice> deviceMap = deviceList.stream()
.collect(Collectors.toMap(
AircraftDevice::getId, // 提取 ID 作为 key
device -> device, // 设备对象作为 value
(existing, replacement) -> existing // 处理 ID 重复保留第一个
));
// 按型号分组统计数量处理设备不存在的情况 // 按型号分组统计数量处理设备不存在的情况
Map<String, Long> modelCountMap = orderDetails.stream() Map<String, Long> modelCountMap = orderDetails.stream()

View File

@ -1,9 +1,9 @@
package com.aircraft.modules.order.service.impl; package com.aircraft.modules.order.service.impl;
import com.aircraft.modules.aircraft.domain.AircraftDevice; import com.aircraft.modules.aircraft.domain.AircraftDevice;
import com.aircraft.modules.aircraft.service.AircraftDeviceService;
import com.aircraft.modules.order.controller.OrderAnalysisController; import com.aircraft.modules.order.controller.OrderAnalysisController;
import com.aircraft.modules.order.domain.OrderDetail; import com.aircraft.modules.order.domain.OrderDetail;
import com.aircraft.modules.order.domain.OrderMain;
import com.aircraft.modules.order.domain.dto.*; import com.aircraft.modules.order.domain.dto.*;
import com.aircraft.modules.order.mapper.OrderDetailMapper; import com.aircraft.modules.order.mapper.OrderDetailMapper;
import com.aircraft.modules.order.service.OrderDetailAnalysisService; import com.aircraft.modules.order.service.OrderDetailAnalysisService;
@ -11,20 +11,22 @@ import com.aircraft.modules.route.domain.CpRoute;
import com.aircraft.modules.route.mapper.CpRouteMapper; import com.aircraft.modules.route.mapper.CpRouteMapper;
import com.aircraft.modules.system.mapper.EmScenicMapper; import com.aircraft.modules.system.mapper.EmScenicMapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.apache.poi.ss.formula.functions.T; import org.apache.poi.ss.formula.functions.T;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Duration;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.YearMonth; import java.time.YearMonth;
import java.time.ZoneId; import java.time.ZoneId;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.time.temporal.Temporal;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.IntStream; import java.util.stream.IntStream;
@ -51,6 +53,8 @@ public class OrderDetailAnalysisServiceImpl implements OrderDetailAnalysisServic
private CpRouteMapper cpRouteMapper; private CpRouteMapper cpRouteMapper;
@Autowired @Autowired
private EmScenicMapper emScenicMapper; private EmScenicMapper emScenicMapper;
@Autowired
private AircraftDeviceService aircraftDeviceService;
@Override @Override
public OrderDetailAnalysisResult<T> analyzeAllOrderDetails(List<OrderDetail> allDetails) { public OrderDetailAnalysisResult<T> analyzeAllOrderDetails(List<OrderDetail> allDetails) {
@ -260,6 +264,14 @@ public class OrderDetailAnalysisServiceImpl implements OrderDetailAnalysisServic
result.setCompletionRate(calculateRate(completedCount, total)); result.setCompletionRate(calculateRate(completedCount, total));
result.setFailedRate(calculateRate(failedCount, total)); result.setFailedRate(calculateRate(failedCount, total));
// 新增 计算平均任务周期单位分钟
double averageTaskCycle = calculateAverageTaskCycle(orderDetails);
result.setAverageTaskCycle(averageTaskCycle);
// 新增 计算无人机使用率
BigDecimal droneUsageRate = calculateDroneUsageRate(orderDetails);
result.setDroneUsageRate(droneUsageRate);
// 5. 景区路线订单分布 // 5. 景区路线订单分布
result.setRouteDistribution(calculateRouteDistribution(orderDetails)); result.setRouteDistribution(calculateRouteDistribution(orderDetails));
@ -269,11 +281,53 @@ public class OrderDetailAnalysisServiceImpl implements OrderDetailAnalysisServic
result.setOrderDetailList(getPagedTasks(orderDetails, pageInfo)); result.setOrderDetailList(getPagedTasks(orderDetails, pageInfo));
// 7. 时间序列数据在调用此方法的上层设置 // 7. 时间序列数据在调用此方法的上层设置
result.setTimeSeriesData(null); // 由调用者设置具体时间序列数据 result.setTimeSeriesData(null);
return result; return result;
} }
//计算平均任务周期
private double calculateAverageTaskCycle(List<OrderDetail> orderDetails) {
//获取任务数
List<OrderDetail> completedTasks = orderDetails.stream()
.filter(task -> task.getOrderItemStatus() == STATUS_COMPLETED)
.collect(Collectors.toList());
int completedTaskCount = completedTasks.size();
if (completedTasks.isEmpty()) {
return 0.0;
}
//获取时间范围内任务的执行总时间更新时间-创建时间
long totalSeconds = completedTasks.stream()
.mapToLong(task ->
Duration.between(task.getCreateTime().toInstant(), task.getUpdateTime().toInstant()).getSeconds()
)
.sum();
//平均执行任务时间=总执行任务时间÷总任务数
return (double) totalSeconds / (60.0 * completedTaskCount);
}
//计算无人机使用率
private BigDecimal calculateDroneUsageRate(List<OrderDetail> orderDetails) {
// 1. 获取时间范围内订单中出现的无人机数量去重
Set<Long> usedDroneIds = orderDetails.stream()
.filter(task -> task.getDeviceId() != null) // 过滤使用了无人机的任务
.map(OrderDetail::getDeviceId)
.collect(Collectors.toSet());
int usedDroneCount = usedDroneIds.size();
// 2. 获取无人机总量
QueryWrapper<AircraftDevice> queryWrapper = new QueryWrapper<>();
int totalDroneCount = aircraftDeviceService.count(queryWrapper);
// 3. 计算使用率保留两位小数
if (totalDroneCount == 0) {
return BigDecimal.ZERO;
}
return BigDecimal.valueOf((double) usedDroneCount / totalDroneCount)
.setScale(2, RoundingMode.HALF_UP);
}
/** /**
* 按状态统计任务数量 * 按状态统计任务数量
*/ */

View File

@ -66,6 +66,14 @@ public class SpringSecurityConfig {
protected SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception { protected SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
// 获取匿名标记 // 获取匿名标记
Map<String, Set<String>> anonymousUrls = AnonTagUtils.getAnonymousUrl(applicationContext); Map<String, Set<String>> anonymousUrls = AnonTagUtils.getAnonymousUrl(applicationContext);
// 手动添加需要匿名访问的接口路径核心修改
// 1. 分页查询接口GET /cpText
anonymousUrls.computeIfAbsent(RequestMethodEnum.GET.getType(), k -> new HashSet<>())
.add("/cpText");
// 2. 通过 ID 查询接口GET /cpText/{id}支持任意 ID
anonymousUrls.computeIfAbsent(RequestMethodEnum.GET.getType(), k -> new HashSet<>())
.add("/cpText/**");
return httpSecurity return httpSecurity
// 禁用 CSRF // 禁用 CSRF
.csrf().disable() .csrf().disable()
@ -88,6 +96,10 @@ public class SpringSecurityConfig {
// 静态资源等等 // 静态资源等等
.antMatchers( .antMatchers(
HttpMethod.GET, HttpMethod.GET,
// "/swagger-ui.html",
// "/swagger-resources/**",
// "/*/api-docs", // Swagger API 文档接口
// "/doc.html" , // Knife4j 文档页面
"/*.html", "/*.html",
"/**/*.html", "/**/*.html",
"/**/*.css", "/**/*.css",

View File

@ -3,22 +3,21 @@
<mapper namespace="com.aircraft.modules.aircraft.mapper.AircraftDeviceMapper"> <mapper namespace="com.aircraft.modules.aircraft.mapper.AircraftDeviceMapper">
<!-- 飞行器设备基础映射 --> <!-- 飞行器设备基础映射 -->
<resultMap id="BaseResultMap" type="com.aircraft.modules.aircraft.domain.AircraftDevice"> <resultMap id="BaseResultMap" type="com.aircraft.modules.aircraft.domain.AircraftDevice">
<id column="id" property="id" jdbcType="BIGINT" /> <id column="id" property="id" jdbcType="BIGINT"/>
<result column="name" property="name" jdbcType="VARCHAR" /> <result column="name" property="name" jdbcType="VARCHAR"/>
<result column="model" property="model" jdbcType="VARCHAR" /> <result column="model" property="model" jdbcType="VARCHAR"/>
<result column="brand" property="brand" jdbcType="VARCHAR" /> <result column="brand" property="brand" jdbcType="VARCHAR"/>
<result column="use_type" property="useType" jdbcType="INTEGER" /> <result column="use_type" property="useType" jdbcType="INTEGER"/>
<result column="area_id" property="areaId" jdbcType="BIGINT" /> <result column="area_id" property="areaId" jdbcType="BIGINT"/>
<result column="scenic_id" property="scenicId" jdbcType="BIGINT" /> <result column="scenic_id" property="scenicId" jdbcType="BIGINT"/>
<result column="employees_id" property="employeesId" jdbcType="BIGINT" /> <result column="employees_id" property="employeesId" jdbcType="BIGINT"/>
<result column="remark" property="remark" jdbcType="VARCHAR" /> <result column="remark" property="remark" jdbcType="VARCHAR"/>
<!-- 继承BaseEntity的公共字段映射 --> <!-- 继承BaseEntity的公共字段映射 -->
<result column="create_by" property="createBy" jdbcType="VARCHAR" /> <result column="create_by" property="createBy" jdbcType="VARCHAR"/>
<result column="create_time" property="createTime" jdbcType="TIMESTAMP" /> <result column="create_time" property="createTime" jdbcType="TIMESTAMP"/>
<result column="update_by" property="updateBy" jdbcType="VARCHAR" /> <result column="update_by" property="updateBy" jdbcType="VARCHAR"/>
<result column="update_time" property="updateTime" jdbcType="TIMESTAMP" /> <result column="update_time" property="updateTime" jdbcType="TIMESTAMP"/>
</resultMap> </resultMap>
<!-- 通过设备ID列表查询飞行器设备 --> <!-- 通过设备ID列表查询飞行器设备 -->
<select id="getAircraftDeviceById" resultMap="BaseResultMap"> <select id="getAircraftDeviceById" resultMap="BaseResultMap">
SELECT SELECT
@ -41,15 +40,6 @@
#{id} #{id}
</foreach> </foreach>
</select> </select>
<!-- 结果处理器将List转换为Map<Long, AircraftDevice> -->
<resultMap id="DeviceMapResultMap" type="java.util.HashMap">
<result column="id" property="key" javaType="java.lang.Long" />
<association property="value" resultMap="BaseResultMap" />
</resultMap>
<select id="page" resultType="com.aircraft.modules.aircraft.domain.vo.AircraftDevicePageVO"> <select id="page" resultType="com.aircraft.modules.aircraft.domain.vo.AircraftDevicePageVO">
select d.id, select d.id,
d.name, d.name,

View File

@ -30,6 +30,7 @@
<if test="titleKeyword != null and titleKeyword != ''"> <if test="titleKeyword != null and titleKeyword != ''">
AND a.title LIKE CONCAT('%', #{titleKeyword}, '%') AND a.title LIKE CONCAT('%', #{titleKeyword}, '%')
</if> </if>
ORDER BY create_time DESC
</select> </select>
<!--通过文章id查询文章--> <!--通过文章id查询文章-->

View File

@ -10,4 +10,11 @@
</if> </if>
ORDER BY id DESC ORDER BY id DESC
</select> </select>
<!-- 浏览量+1-->
<update id="updateViewCountById">
UPDATE cp_text
SET view_count = view_count + 1 -- 自增1
WHERE id = #{id}
AND del_flag = 0 -- 确保只更新未删除的文章
</update>
</mapper> </mapper>