fix(ops): XML 手写 SQL 添加 tenant_id 预编译参数过滤
yudao 官方明确 MyBatis Plus 拦截器不处理 XML 手写 SQL,需手动过滤。
将 ${} OGNL 表达式改为 #{tenantId} 预编译参数,避免 NPE 和
@TenantIgnore 不兼容问题。
- OpsOrderMapper: 8 条统计 SQL 添加 AND tenant_id = #{tenantId}
- OpsTrafficStatisticsMapper: deleteByStatHourBefore 补上 tenant_id
- OpsStatisticsServiceImpl: 10 处调用传入 tenantId 参数
- TrafficStatisticsCleanupJob: executeIgnore → @TenantJob + 显式传参
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -31,6 +31,7 @@ public interface OpsTrafficStatisticsMapper extends BaseMapperX<OpsTrafficStatis
|
||||
* @param beforeTime 截止时间
|
||||
* @return 删除的记录数
|
||||
*/
|
||||
int deleteByStatHourBefore(@Param("beforeTime") LocalDateTime beforeTime);
|
||||
int deleteByStatHourBefore(@Param("beforeTime") LocalDateTime beforeTime,
|
||||
@Param("tenantId") Long tenantId);
|
||||
|
||||
}
|
||||
|
||||
@@ -99,33 +99,38 @@ public interface OpsOrderMapper extends BaseMapperX<OpsOrderDO> {
|
||||
*/
|
||||
List<DateCountRespVO> selectCreatedCountGroupByDate(@Param("orderType") String orderType,
|
||||
@Param("startTime") LocalDateTime startTime,
|
||||
@Param("endTime") LocalDateTime endTime);
|
||||
@Param("endTime") LocalDateTime endTime,
|
||||
@Param("tenantId") Long tenantId);
|
||||
|
||||
/**
|
||||
* 按日期分组统计工单完成数
|
||||
*/
|
||||
List<DateCountRespVO> selectCompletedCountGroupByDate(@Param("orderType") String orderType,
|
||||
@Param("startTime") LocalDateTime startTime,
|
||||
@Param("endTime") LocalDateTime endTime);
|
||||
@Param("endTime") LocalDateTime endTime,
|
||||
@Param("tenantId") Long tenantId);
|
||||
|
||||
/**
|
||||
* 按日期分组统计平均响应时长和完成时长(秒)
|
||||
*/
|
||||
List<AvgTimeRespVO> selectAvgTimeGroupByDate(@Param("orderType") String orderType,
|
||||
@Param("startTime") LocalDateTime startTime,
|
||||
@Param("endTime") LocalDateTime endTime);
|
||||
@Param("endTime") LocalDateTime endTime,
|
||||
@Param("tenantId") Long tenantId);
|
||||
|
||||
/**
|
||||
* 按区域分组统计工单数和完成数(非取消的工单)
|
||||
*/
|
||||
List<AreaCountRespVO> selectCountGroupByAreaId(@Param("orderType") String orderType);
|
||||
List<AreaCountRespVO> selectCountGroupByAreaId(@Param("orderType") String orderType,
|
||||
@Param("tenantId") Long tenantId);
|
||||
|
||||
/**
|
||||
* 按小时分组统计工单创建数
|
||||
*/
|
||||
List<HourCountRespVO> selectCountGroupByHour(@Param("orderType") String orderType,
|
||||
@Param("startTime") LocalDateTime startTime,
|
||||
@Param("endTime") LocalDateTime endTime);
|
||||
@Param("endTime") LocalDateTime endTime,
|
||||
@Param("tenantId") Long tenantId);
|
||||
|
||||
/**
|
||||
* 按星期和小时分组统计工单创建数(用于热力图)
|
||||
@@ -133,18 +138,21 @@ public interface OpsOrderMapper extends BaseMapperX<OpsOrderDO> {
|
||||
*/
|
||||
List<HeatmapRespVO> selectCountGroupByDayOfWeekAndHour(@Param("orderType") String orderType,
|
||||
@Param("startTime") LocalDateTime startTime,
|
||||
@Param("endTime") LocalDateTime endTime);
|
||||
@Param("endTime") LocalDateTime endTime,
|
||||
@Param("tenantId") Long tenantId);
|
||||
|
||||
/**
|
||||
* 按保洁类型分组统计平均作业时长(分钟)
|
||||
* JOIN ops_order_clean_ext,使用 arrived_time 和 completed_time 计算
|
||||
*/
|
||||
List<CleaningTypeAvgDurationRespVO> selectAvgDurationGroupByCleaningType(@Param("orderType") String orderType);
|
||||
List<CleaningTypeAvgDurationRespVO> selectAvgDurationGroupByCleaningType(@Param("orderType") String orderType,
|
||||
@Param("tenantId") Long tenantId);
|
||||
|
||||
/**
|
||||
* 按功能类型分组统计工单数和完成数(非取消的工单)
|
||||
*/
|
||||
List<FunctionTypeCountRespVO> selectCountGroupByFunctionType(@Param("orderType") String orderType);
|
||||
List<FunctionTypeCountRespVO> selectCountGroupByFunctionType(@Param("orderType") String orderType,
|
||||
@Param("tenantId") Long tenantId);
|
||||
|
||||
// 注意:分页查询方法需要在Service层实现,这里只提供基础查询方法
|
||||
// 具体分页查询请参考Service实现
|
||||
|
||||
@@ -1,53 +1,55 @@
|
||||
package com.viewsh.module.ops.service.job;
|
||||
|
||||
import com.viewsh.framework.tenant.core.util.TenantUtils;
|
||||
import com.viewsh.module.ops.dal.mysql.statistics.OpsTrafficStatisticsMapper;
|
||||
import com.xxl.job.core.handler.annotation.XxlJob;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 客流统计清理任务
|
||||
* <p>
|
||||
* 每月 1 日凌晨 2 点执行,删除 30 天前的客流统计记录
|
||||
*
|
||||
* @author AI
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class TrafficStatisticsCleanupJob {
|
||||
|
||||
@Resource
|
||||
private OpsTrafficStatisticsMapper trafficStatisticsMapper;
|
||||
|
||||
/**
|
||||
* 清理过期的客流统计记录
|
||||
* <p>
|
||||
* XxlJob 配置:
|
||||
* - Cron: 0 0 2 1 * ? (每月 1 日凌晨 2 点)
|
||||
*
|
||||
* @return 执行结果
|
||||
*/
|
||||
@XxlJob("trafficStatisticsCleanupJob")
|
||||
public String execute() {
|
||||
log.info("[TrafficStatisticsCleanupJob] 开始执行客流统计清理任务");
|
||||
|
||||
try {
|
||||
LocalDateTime beforeTime = LocalDateTime.now().minusDays(30);
|
||||
// 使用 executeIgnore 忽略租户过滤,清理所有租户的过期数据
|
||||
int deletedCount = TenantUtils.executeIgnore(
|
||||
() -> trafficStatisticsMapper.deleteByStatHourBefore(beforeTime));
|
||||
|
||||
log.info("[TrafficStatisticsCleanupJob] 客流统计清理完成:删除 {} 条记录(截止时间={})",
|
||||
deletedCount, beforeTime);
|
||||
return "清理完成:删除 " + deletedCount + " 条记录";
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("[TrafficStatisticsCleanupJob] 客流统计清理失败", e);
|
||||
return "清理失败: " + e.getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
package com.viewsh.module.ops.service.job;
|
||||
|
||||
import com.viewsh.framework.tenant.core.context.TenantContextHolder;
|
||||
import com.viewsh.framework.tenant.core.job.TenantJob;
|
||||
import com.viewsh.module.ops.dal.mysql.statistics.OpsTrafficStatisticsMapper;
|
||||
import com.xxl.job.core.handler.annotation.XxlJob;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 客流统计清理任务
|
||||
* <p>
|
||||
* 每月 1 日凌晨 2 点执行,删除 30 天前的客流统计记录。
|
||||
* 使用 @TenantJob 遍历每个租户逐个清理,确保租户隔离。
|
||||
*
|
||||
* @author AI
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class TrafficStatisticsCleanupJob {
|
||||
|
||||
@Resource
|
||||
private OpsTrafficStatisticsMapper trafficStatisticsMapper;
|
||||
|
||||
/**
|
||||
* 清理过期的客流统计记录
|
||||
* <p>
|
||||
* XxlJob 配置:
|
||||
* - Cron: 0 0 2 1 * ? (每月 1 日凌晨 2 点)
|
||||
*
|
||||
* @return 执行结果
|
||||
*/
|
||||
@XxlJob("trafficStatisticsCleanupJob")
|
||||
@TenantJob
|
||||
public String execute() {
|
||||
Long tenantId = TenantContextHolder.getRequiredTenantId();
|
||||
log.info("[TrafficStatisticsCleanupJob] 开始执行客流统计清理任务, tenantId={}", tenantId);
|
||||
|
||||
try {
|
||||
LocalDateTime beforeTime = LocalDateTime.now().minusDays(30);
|
||||
int deletedCount = trafficStatisticsMapper.deleteByStatHourBefore(beforeTime, tenantId);
|
||||
|
||||
log.info("[TrafficStatisticsCleanupJob] 客流统计清理完成:tenantId={}, 删除 {} 条记录(截止时间={})",
|
||||
tenantId, deletedCount, beforeTime);
|
||||
return "清理完成:删除 " + deletedCount + " 条记录";
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("[TrafficStatisticsCleanupJob] 客流统计清理失败, tenantId={}", tenantId, e);
|
||||
return "清理失败: " + e.getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.viewsh.module.ops.service.statistics;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.viewsh.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import com.viewsh.framework.tenant.core.context.TenantContextHolder;
|
||||
import com.viewsh.module.ops.controller.admin.workorder.vo.statistics.*;
|
||||
import com.viewsh.module.ops.controller.admin.workorder.vo.statistics.DashboardStatsRespVO.*;
|
||||
import com.viewsh.module.ops.controller.admin.workorder.vo.statistics.DeviceTrafficRealtimeRespVO;
|
||||
@@ -621,8 +622,9 @@ public class OpsStatisticsServiceImpl implements OpsStatisticsService {
|
||||
LocalDateTime endDateTime = endDate.plusDays(1).atStartOfDay();
|
||||
|
||||
// 一次查询获取所有天的创建数
|
||||
Long tenantId = TenantContextHolder.getRequiredTenantId();
|
||||
List<DateCountRespVO> createdRows = opsOrderMapper.selectCreatedCountGroupByDate(
|
||||
orderType, startDateTime, endDateTime);
|
||||
orderType, startDateTime, endDateTime, tenantId);
|
||||
Map<String, Integer> createdMap = createdRows.stream()
|
||||
.collect(Collectors.toMap(
|
||||
row -> row.getStatDate().toString(),
|
||||
@@ -632,7 +634,7 @@ public class OpsStatisticsServiceImpl implements OpsStatisticsService {
|
||||
|
||||
// 一次查询获取所有天的完成数
|
||||
List<DateCountRespVO> completedRows = opsOrderMapper.selectCompletedCountGroupByDate(
|
||||
orderType, startDateTime, endDateTime);
|
||||
orderType, startDateTime, endDateTime, tenantId);
|
||||
Map<String, Integer> completedMap = completedRows.stream()
|
||||
.collect(Collectors.toMap(
|
||||
row -> row.getStatDate().toString(),
|
||||
@@ -666,7 +668,7 @@ public class OpsStatisticsServiceImpl implements OpsStatisticsService {
|
||||
LocalDateTime startDateTime,
|
||||
LocalDateTime endDateTime) {
|
||||
List<HourCountRespVO> rows = opsOrderMapper.selectCountGroupByHour(
|
||||
orderType, startDateTime, endDateTime);
|
||||
orderType, startDateTime, endDateTime, TenantContextHolder.getRequiredTenantId());
|
||||
|
||||
// 构建小时 -> 计数映射
|
||||
int[] hourBuckets = new int[24];
|
||||
@@ -696,7 +698,7 @@ public class OpsStatisticsServiceImpl implements OpsStatisticsService {
|
||||
LocalDateTime endDateTime = endDate.plusDays(1).atStartOfDay();
|
||||
|
||||
List<AvgTimeRespVO> rows = opsOrderMapper.selectAvgTimeGroupByDate(
|
||||
orderType, startDateTime, endDateTime);
|
||||
orderType, startDateTime, endDateTime, TenantContextHolder.getRequiredTenantId());
|
||||
|
||||
// 构建日期 -> 响应/完成时长映射
|
||||
Map<String, Double> responseMap = new HashMap<>();
|
||||
@@ -779,7 +781,7 @@ public class OpsStatisticsServiceImpl implements OpsStatisticsService {
|
||||
LocalDateTime startDateTime,
|
||||
LocalDateTime endDateTime) {
|
||||
List<HeatmapRespVO> rows = opsOrderMapper.selectCountGroupByDayOfWeekAndHour(
|
||||
orderType, startDateTime, endDateTime);
|
||||
orderType, startDateTime, endDateTime, TenantContextHolder.getRequiredTenantId());
|
||||
|
||||
// 生成近7天的日期列表(格式:MM-dd)
|
||||
LocalDate startDate = startDateTime.toLocalDate();
|
||||
@@ -843,7 +845,8 @@ public class OpsStatisticsServiceImpl implements OpsStatisticsService {
|
||||
* 构建功能类型排行(将枚举值转换为中文)
|
||||
*/
|
||||
private List<FunctionTypeRankingItem> buildFunctionTypeRanking(String orderType) {
|
||||
List<FunctionTypeCountRespVO> rows = opsOrderMapper.selectCountGroupByFunctionType(orderType);
|
||||
List<FunctionTypeCountRespVO> rows = opsOrderMapper.selectCountGroupByFunctionType(
|
||||
orderType, TenantContextHolder.getRequiredTenantId());
|
||||
|
||||
return rows.stream()
|
||||
.map(row -> {
|
||||
@@ -959,7 +962,7 @@ public class OpsStatisticsServiceImpl implements OpsStatisticsService {
|
||||
LocalDateTime todayStart,
|
||||
LocalDateTime todayEnd) {
|
||||
List<HourCountRespVO> rows = opsOrderMapper.selectCountGroupByHour(
|
||||
orderType, todayStart, todayEnd);
|
||||
orderType, todayStart, todayEnd, TenantContextHolder.getRequiredTenantId());
|
||||
|
||||
int[] hourBuckets = new int[24];
|
||||
for (HourCountRespVO row : rows) {
|
||||
@@ -1079,7 +1082,7 @@ public class OpsStatisticsServiceImpl implements OpsStatisticsService {
|
||||
LocalDateTime endDateTime = endDate.plusDays(1).atStartOfDay();
|
||||
|
||||
List<DateCountRespVO> rows = opsOrderMapper.selectCreatedCountGroupByDate(
|
||||
orderType, startDateTime, endDateTime);
|
||||
orderType, startDateTime, endDateTime, TenantContextHolder.getRequiredTenantId());
|
||||
Map<String, Integer> countMap = rows.stream()
|
||||
.collect(Collectors.toMap(
|
||||
row -> row.getStatDate().toString(),
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
<delete id="deleteByStatHourBefore">
|
||||
DELETE FROM ops_traffic_statistics
|
||||
WHERE stat_hour < #{beforeTime}
|
||||
AND tenant_id = #{tenantId}
|
||||
AND deleted = 0
|
||||
</delete>
|
||||
|
||||
|
||||
@@ -1,119 +1,129 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.viewsh.module.ops.dal.mysql.workorder.OpsOrderMapper">
|
||||
|
||||
<!-- 按日期分组统计工单创建数 -->
|
||||
<select id="selectCreatedCountGroupByDate" resultType="com.viewsh.module.ops.controller.admin.workorder.vo.statistics.DateCountRespVO">
|
||||
SELECT DATE(create_time) AS statDate, COUNT(*) AS cnt
|
||||
FROM ops_order
|
||||
WHERE order_type = #{orderType}
|
||||
AND deleted = 0
|
||||
AND create_time >= #{startTime}
|
||||
AND create_time < #{endTime}
|
||||
GROUP BY DATE(create_time)
|
||||
ORDER BY statDate
|
||||
</select>
|
||||
|
||||
<!-- 按日期分组统计工单完成数 -->
|
||||
<select id="selectCompletedCountGroupByDate" resultType="com.viewsh.module.ops.controller.admin.workorder.vo.statistics.DateCountRespVO">
|
||||
SELECT DATE(end_time) AS statDate, COUNT(*) AS cnt
|
||||
FROM ops_order
|
||||
WHERE order_type = #{orderType}
|
||||
AND status = 'COMPLETED'
|
||||
AND deleted = 0
|
||||
AND end_time >= #{startTime}
|
||||
AND end_time < #{endTime}
|
||||
GROUP BY DATE(end_time)
|
||||
ORDER BY statDate
|
||||
</select>
|
||||
|
||||
<!-- 按日期分组统计平均响应时长和完成时长 -->
|
||||
<select id="selectAvgTimeGroupByDate" resultType="com.viewsh.module.ops.controller.admin.workorder.vo.statistics.AvgTimeRespVO">
|
||||
SELECT DATE(end_time) AS statDate,
|
||||
AVG(CASE WHEN response_seconds > 0 THEN response_seconds ELSE NULL END) AS avgResponse,
|
||||
AVG(CASE WHEN completion_seconds > 0 THEN completion_seconds ELSE NULL END) AS avgCompletion
|
||||
FROM ops_order
|
||||
WHERE order_type = #{orderType}
|
||||
AND status = 'COMPLETED'
|
||||
AND deleted = 0
|
||||
AND end_time >= #{startTime}
|
||||
AND end_time < #{endTime}
|
||||
GROUP BY DATE(end_time)
|
||||
ORDER BY statDate
|
||||
</select>
|
||||
|
||||
<!-- 按区域分组统计工单数和完成数 -->
|
||||
<select id="selectCountGroupByAreaId" resultType="com.viewsh.module.ops.controller.admin.workorder.vo.statistics.AreaCountRespVO">
|
||||
SELECT area_id AS areaId,
|
||||
COUNT(*) AS totalCount,
|
||||
SUM(CASE WHEN status = 'COMPLETED' THEN 1 ELSE 0 END) AS completedCount
|
||||
FROM ops_order
|
||||
WHERE order_type = #{orderType}
|
||||
AND status != 'CANCELLED'
|
||||
AND deleted = 0
|
||||
AND area_id IS NOT NULL
|
||||
GROUP BY area_id
|
||||
ORDER BY totalCount DESC
|
||||
LIMIT 10
|
||||
</select>
|
||||
|
||||
<!-- 按小时分组统计工单创建数 -->
|
||||
<select id="selectCountGroupByHour" resultType="com.viewsh.module.ops.controller.admin.workorder.vo.statistics.HourCountRespVO">
|
||||
SELECT HOUR(create_time) AS hourVal, COUNT(*) AS cnt
|
||||
FROM ops_order
|
||||
WHERE order_type = #{orderType}
|
||||
AND deleted = 0
|
||||
AND create_time >= #{startTime}
|
||||
AND create_time < #{endTime}
|
||||
GROUP BY HOUR(create_time)
|
||||
ORDER BY hourVal
|
||||
</select>
|
||||
|
||||
<!-- 按星期和小时分组统计(热力图) -->
|
||||
<select id="selectCountGroupByDayOfWeekAndHour" resultType="com.viewsh.module.ops.controller.admin.workorder.vo.statistics.HeatmapRespVO">
|
||||
SELECT DAYOFWEEK(create_time) AS dayOfWeek,
|
||||
HOUR(create_time) AS hourVal,
|
||||
COUNT(*) AS cnt
|
||||
FROM ops_order
|
||||
WHERE order_type = #{orderType}
|
||||
AND deleted = 0
|
||||
AND create_time >= #{startTime}
|
||||
AND create_time < #{endTime}
|
||||
GROUP BY DAYOFWEEK(create_time), HOUR(create_time)
|
||||
ORDER BY dayOfWeek, hourVal
|
||||
</select>
|
||||
|
||||
<!-- 按保洁类型分组统计平均作业时长(JOIN clean_ext) -->
|
||||
<select id="selectAvgDurationGroupByCleaningType" resultType="com.viewsh.module.ops.controller.admin.workorder.vo.statistics.CleaningTypeAvgDurationRespVO">
|
||||
SELECT ext.cleaning_type AS cleaningType,
|
||||
ROUND(AVG(TIMESTAMPDIFF(MINUTE, ext.arrived_time, ext.completed_time))) AS avgDuration
|
||||
FROM ops_order_clean_ext ext
|
||||
INNER JOIN ops_order o ON ext.ops_order_id = o.id
|
||||
WHERE o.status = 'COMPLETED'
|
||||
AND o.order_type = #{orderType}
|
||||
AND o.deleted = 0
|
||||
AND ext.deleted = 0
|
||||
AND ext.completed_time IS NOT NULL
|
||||
AND ext.arrived_time IS NOT NULL
|
||||
AND ext.cleaning_type IS NOT NULL
|
||||
GROUP BY ext.cleaning_type
|
||||
</select>
|
||||
|
||||
<!-- 按功能类型分组统计工单数和完成数 -->
|
||||
<select id="selectCountGroupByFunctionType" resultType="com.viewsh.module.ops.controller.admin.workorder.vo.statistics.FunctionTypeCountRespVO">
|
||||
SELECT a.function_type AS functionType,
|
||||
COUNT(o.id) AS totalCount,
|
||||
SUM(CASE WHEN o.status = 'COMPLETED' THEN 1 ELSE 0 END) AS completedCount
|
||||
FROM ops_order o
|
||||
INNER JOIN ops_bus_area a ON o.area_id = a.id
|
||||
WHERE o.order_type = #{orderType}
|
||||
AND o.status != 'CANCELLED'
|
||||
AND o.deleted = 0
|
||||
AND a.deleted = 0
|
||||
AND a.function_type IS NOT NULL
|
||||
GROUP BY a.function_type
|
||||
ORDER BY totalCount DESC
|
||||
LIMIT 10
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.viewsh.module.ops.dal.mysql.workorder.OpsOrderMapper">
|
||||
|
||||
<!-- 按日期分组统计工单创建数 -->
|
||||
<select id="selectCreatedCountGroupByDate" resultType="com.viewsh.module.ops.controller.admin.workorder.vo.statistics.DateCountRespVO">
|
||||
SELECT DATE(create_time) AS statDate, COUNT(*) AS cnt
|
||||
FROM ops_order
|
||||
WHERE order_type = #{orderType}
|
||||
AND tenant_id = #{tenantId}
|
||||
AND deleted = 0
|
||||
AND create_time >= #{startTime}
|
||||
AND create_time < #{endTime}
|
||||
GROUP BY DATE(create_time)
|
||||
ORDER BY statDate
|
||||
</select>
|
||||
|
||||
<!-- 按日期分组统计工单完成数 -->
|
||||
<select id="selectCompletedCountGroupByDate" resultType="com.viewsh.module.ops.controller.admin.workorder.vo.statistics.DateCountRespVO">
|
||||
SELECT DATE(end_time) AS statDate, COUNT(*) AS cnt
|
||||
FROM ops_order
|
||||
WHERE order_type = #{orderType}
|
||||
AND tenant_id = #{tenantId}
|
||||
AND status = 'COMPLETED'
|
||||
AND deleted = 0
|
||||
AND end_time >= #{startTime}
|
||||
AND end_time < #{endTime}
|
||||
GROUP BY DATE(end_time)
|
||||
ORDER BY statDate
|
||||
</select>
|
||||
|
||||
<!-- 按日期分组统计平均响应时长和完成时长 -->
|
||||
<select id="selectAvgTimeGroupByDate" resultType="com.viewsh.module.ops.controller.admin.workorder.vo.statistics.AvgTimeRespVO">
|
||||
SELECT DATE(end_time) AS statDate,
|
||||
AVG(CASE WHEN response_seconds > 0 THEN response_seconds ELSE NULL END) AS avgResponse,
|
||||
AVG(CASE WHEN completion_seconds > 0 THEN completion_seconds ELSE NULL END) AS avgCompletion
|
||||
FROM ops_order
|
||||
WHERE order_type = #{orderType}
|
||||
AND tenant_id = #{tenantId}
|
||||
AND status = 'COMPLETED'
|
||||
AND deleted = 0
|
||||
AND end_time >= #{startTime}
|
||||
AND end_time < #{endTime}
|
||||
GROUP BY DATE(end_time)
|
||||
ORDER BY statDate
|
||||
</select>
|
||||
|
||||
<!-- 按区域分组统计工单数和完成数 -->
|
||||
<select id="selectCountGroupByAreaId" resultType="com.viewsh.module.ops.controller.admin.workorder.vo.statistics.AreaCountRespVO">
|
||||
SELECT area_id AS areaId,
|
||||
COUNT(*) AS totalCount,
|
||||
SUM(CASE WHEN status = 'COMPLETED' THEN 1 ELSE 0 END) AS completedCount
|
||||
FROM ops_order
|
||||
WHERE order_type = #{orderType}
|
||||
AND tenant_id = #{tenantId}
|
||||
AND status != 'CANCELLED'
|
||||
AND deleted = 0
|
||||
AND area_id IS NOT NULL
|
||||
GROUP BY area_id
|
||||
ORDER BY totalCount DESC
|
||||
LIMIT 10
|
||||
</select>
|
||||
|
||||
<!-- 按小时分组统计工单创建数 -->
|
||||
<select id="selectCountGroupByHour" resultType="com.viewsh.module.ops.controller.admin.workorder.vo.statistics.HourCountRespVO">
|
||||
SELECT HOUR(create_time) AS hourVal, COUNT(*) AS cnt
|
||||
FROM ops_order
|
||||
WHERE order_type = #{orderType}
|
||||
AND tenant_id = #{tenantId}
|
||||
AND deleted = 0
|
||||
AND create_time >= #{startTime}
|
||||
AND create_time < #{endTime}
|
||||
GROUP BY HOUR(create_time)
|
||||
ORDER BY hourVal
|
||||
</select>
|
||||
|
||||
<!-- 按星期和小时分组统计(热力图) -->
|
||||
<select id="selectCountGroupByDayOfWeekAndHour" resultType="com.viewsh.module.ops.controller.admin.workorder.vo.statistics.HeatmapRespVO">
|
||||
SELECT DAYOFWEEK(create_time) AS dayOfWeek,
|
||||
HOUR(create_time) AS hourVal,
|
||||
COUNT(*) AS cnt
|
||||
FROM ops_order
|
||||
WHERE order_type = #{orderType}
|
||||
AND tenant_id = #{tenantId}
|
||||
AND deleted = 0
|
||||
AND create_time >= #{startTime}
|
||||
AND create_time < #{endTime}
|
||||
GROUP BY DAYOFWEEK(create_time), HOUR(create_time)
|
||||
ORDER BY dayOfWeek, hourVal
|
||||
</select>
|
||||
|
||||
<!-- 按保洁类型分组统计平均作业时长(JOIN clean_ext) -->
|
||||
<select id="selectAvgDurationGroupByCleaningType" resultType="com.viewsh.module.ops.controller.admin.workorder.vo.statistics.CleaningTypeAvgDurationRespVO">
|
||||
SELECT ext.cleaning_type AS cleaningType,
|
||||
ROUND(AVG(TIMESTAMPDIFF(MINUTE, ext.arrived_time, ext.completed_time))) AS avgDuration
|
||||
FROM ops_order_clean_ext ext
|
||||
INNER JOIN ops_order o ON ext.ops_order_id = o.id
|
||||
WHERE o.status = 'COMPLETED'
|
||||
AND o.tenant_id = #{tenantId}
|
||||
AND o.order_type = #{orderType}
|
||||
AND o.deleted = 0
|
||||
AND ext.tenant_id = #{tenantId}
|
||||
AND ext.deleted = 0
|
||||
AND ext.completed_time IS NOT NULL
|
||||
AND ext.arrived_time IS NOT NULL
|
||||
AND ext.cleaning_type IS NOT NULL
|
||||
GROUP BY ext.cleaning_type
|
||||
</select>
|
||||
|
||||
<!-- 按功能类型分组统计工单数和完成数 -->
|
||||
<select id="selectCountGroupByFunctionType" resultType="com.viewsh.module.ops.controller.admin.workorder.vo.statistics.FunctionTypeCountRespVO">
|
||||
SELECT a.function_type AS functionType,
|
||||
COUNT(o.id) AS totalCount,
|
||||
SUM(CASE WHEN o.status = 'COMPLETED' THEN 1 ELSE 0 END) AS completedCount
|
||||
FROM ops_order o
|
||||
INNER JOIN ops_bus_area a ON o.area_id = a.id
|
||||
WHERE o.order_type = #{orderType}
|
||||
AND o.tenant_id = #{tenantId}
|
||||
AND o.status != 'CANCELLED'
|
||||
AND o.deleted = 0
|
||||
AND a.tenant_id = #{tenantId}
|
||||
AND a.deleted = 0
|
||||
AND a.function_type IS NOT NULL
|
||||
GROUP BY a.function_type
|
||||
ORDER BY totalCount DESC
|
||||
LIMIT 10
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
|
||||
Reference in New Issue
Block a user