feat(ops): refactor-order-operations
This commit is contained in:
@@ -58,10 +58,10 @@ public interface OrderLifecycleManager {
|
||||
OrderTransitionResult enqueue(OrderTransitionRequest request);
|
||||
|
||||
/**
|
||||
* 工单出队并派单:QUEUED → DISPATCHED
|
||||
* 工单出队并派单:PENDING/QUEUED → DISPATCHED
|
||||
* <p>
|
||||
* 状态转换:
|
||||
* - 工单状态:QUEUED → DISPATCHED
|
||||
* - 工单状态:PENDING/QUEUED → DISPATCHED
|
||||
* - 队列状态:WAITING → PROCESSING
|
||||
*
|
||||
* @param request 状态转换请求
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
package com.viewsh.module.ops.infrastructure.code;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 工单编号生成器
|
||||
* <p>
|
||||
* 格式:{业务前缀}-{日期}-{序号}
|
||||
* 例如:CLEAN-20250119-0001, SECURITY-20250119-0001
|
||||
* <p>
|
||||
* 特性:
|
||||
* - 使用 Redis 保证序号唯一性
|
||||
* - 序号每日自动重置(按日期分 key)
|
||||
* - 不同业务类型独立计数
|
||||
*
|
||||
* @author lzh
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class OrderCodeGenerator {
|
||||
|
||||
private static final String KEY_PREFIX = "ops:order:code:";
|
||||
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd");
|
||||
private static final long DEFAULT_EXPIRE_DAYS = 7; // key 默认保留7天
|
||||
|
||||
@Resource
|
||||
private StringRedisTemplate stringRedisTemplate;
|
||||
|
||||
/**
|
||||
* 生成工单编号
|
||||
*
|
||||
* @param businessType 业务类型(如:CLEAN、SECURITY、FACILITIES)
|
||||
* @return 工单编号,格式:{业务类型}-{日期}-{4位序号}
|
||||
*/
|
||||
public String generate(String businessType) {
|
||||
if (businessType == null || businessType.isEmpty()) {
|
||||
throw new IllegalArgumentException("Business type cannot be null or empty");
|
||||
}
|
||||
|
||||
String dateStr = LocalDate.now().format(DATE_FORMATTER);
|
||||
String key = KEY_PREFIX + businessType + ":" + dateStr;
|
||||
|
||||
// Redis 自增并获取新值
|
||||
Long seq = stringRedisTemplate.opsForValue().increment(key);
|
||||
|
||||
// 首次创建时设置过期时间
|
||||
if (seq != null && seq == 1) {
|
||||
stringRedisTemplate.expire(key, DEFAULT_EXPIRE_DAYS, TimeUnit.DAYS);
|
||||
}
|
||||
|
||||
if (seq == null) {
|
||||
throw new RuntimeException("Failed to generate order code for business type: " + businessType);
|
||||
}
|
||||
|
||||
String orderCode = String.format("%s-%s-%04d", businessType, dateStr, seq);
|
||||
|
||||
log.debug("生成工单编号: businessType={}, orderCode={}", businessType, orderCode);
|
||||
|
||||
return orderCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定业务类型当天的当前序号
|
||||
*
|
||||
* @param businessType 业务类型
|
||||
* @return 当前序号,如果不存在返回0
|
||||
*/
|
||||
public long getCurrentSeq(String businessType) {
|
||||
String dateStr = LocalDate.now().format(DATE_FORMATTER);
|
||||
String key = KEY_PREFIX + businessType + ":" + dateStr;
|
||||
String value = stringRedisTemplate.opsForValue().get(key);
|
||||
return value == null ? 0 : Long.parseLong(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置指定业务类型当天的序号(仅供测试或特殊场景使用)
|
||||
*
|
||||
* @param businessType 业务类型
|
||||
*/
|
||||
public void reset(String businessType) {
|
||||
String dateStr = LocalDate.now().format(DATE_FORMATTER);
|
||||
String key = KEY_PREFIX + businessType + ":" + dateStr;
|
||||
stringRedisTemplate.delete(key);
|
||||
log.warn("重置工单编号序号: businessType={}, date={}", businessType, dateStr);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.viewsh.module.ops.infrastructure.id;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 工单ID生成器
|
||||
* <p>
|
||||
* 使用雪花算法,保证分布式环境下的全局唯一性。
|
||||
* 适用于工单主键生成。
|
||||
*
|
||||
* @author lzh
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class OrderIdGenerator {
|
||||
|
||||
private final OrderIdProperties orderIdProperties;
|
||||
|
||||
private SnowflakeIdGenerator snowflake;
|
||||
|
||||
/**
|
||||
* 构造函数,注入配置
|
||||
*/
|
||||
public OrderIdGenerator(OrderIdProperties orderIdProperties) {
|
||||
this.orderIdProperties = orderIdProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化雪花算法生成器
|
||||
*/
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
int datacenterId = orderIdProperties.getDatacenterId();
|
||||
int machineId = orderIdProperties.getMachineId();
|
||||
|
||||
this.snowflake = new SnowflakeIdGenerator(datacenterId, machineId);
|
||||
|
||||
log.info("OrderIdGenerator初始化完成: datacenterId={}, machineId={}",
|
||||
datacenterId, machineId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成工单ID
|
||||
*
|
||||
* @return 唯一的Long类型ID
|
||||
*/
|
||||
public Long generate() {
|
||||
if (snowflake == null) {
|
||||
throw new IllegalStateException("OrderIdGenerator 尚未初始化");
|
||||
}
|
||||
return snowflake.nextId();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.viewsh.module.ops.infrastructure.id;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 工单ID生成器<E68890><E599A8>置
|
||||
*
|
||||
* @author lzh
|
||||
*/
|
||||
@Data
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "ops.order")
|
||||
public class OrderIdProperties {
|
||||
|
||||
/**
|
||||
* 数据中心ID(0-31)
|
||||
* 默认值:1
|
||||
*/
|
||||
private int datacenterId = 1;
|
||||
|
||||
/**
|
||||
* 机器ID(0-31)
|
||||
* 默认值:1
|
||||
*/
|
||||
private int machineId = 1;
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
package com.viewsh.module.ops.infrastructure.id;
|
||||
|
||||
/**
|
||||
* 雪花算法ID生成器
|
||||
* <p>
|
||||
* ID<49><44><EFBFBD>构(64位Long):
|
||||
* - 1位符号位(永远为0)
|
||||
* - 41位时间戳(毫秒级,可用69年)
|
||||
* - 5位数据中心ID(0-31)
|
||||
* - 5位机器ID(0-31)
|
||||
* - 12位序列号(毫秒内计数,0-4095)
|
||||
* <p>
|
||||
* 特性:
|
||||
* - 分布式环境全局唯一
|
||||
* - 时间有序
|
||||
* - 高性能(单机每毫秒可生成4096个ID)
|
||||
*
|
||||
* @author lzh
|
||||
*/
|
||||
public class SnowflakeIdGenerator {
|
||||
|
||||
/**
|
||||
* 起始时间戳(2024-01-01 00:00:00)
|
||||
* 可根据项目实际情况调整
|
||||
*/
|
||||
private static final long EPOCH = 1704067200000L;
|
||||
|
||||
/**
|
||||
* 各部分位数
|
||||
*/
|
||||
private static final long DATACENTER_ID_BITS = 5L;
|
||||
private static final long MACHINE_ID_BITS = 5L;
|
||||
private static final long SEQUENCE_BITS = 12L;
|
||||
|
||||
/**
|
||||
* 各部分最大值
|
||||
*/
|
||||
private static final long MAX_DATACENTER_ID = ~(-1L << DATACENTER_ID_BITS); // 31
|
||||
private static final long MAX_MACHINE_ID = ~(-1L << MACHINE_ID_BITS); // 31
|
||||
private static final long MAX_SEQUENCE = ~(-1L << SEQUENCE_BITS); // 4095
|
||||
|
||||
/**
|
||||
* 各部分位移
|
||||
*/
|
||||
private static final long MACHINE_ID_SHIFT = SEQUENCE_BITS;
|
||||
private static final long DATACENTER_ID_SHIFT = SEQUENCE_BITS + MACHINE_ID_BITS;
|
||||
private static final long TIMESTAMP_SHIFT = SEQUENCE_BITS + MACHINE_ID_BITS + DATACENTER_ID_BITS;
|
||||
|
||||
/**
|
||||
* 数据中心ID
|
||||
*/
|
||||
private final long datacenterId;
|
||||
|
||||
/**
|
||||
* 机器ID
|
||||
*/
|
||||
private final long machineId;
|
||||
|
||||
/**
|
||||
* 序列号
|
||||
*/
|
||||
private long sequence = 0L;
|
||||
|
||||
/**
|
||||
* 上次生成ID的时间戳
|
||||
*/
|
||||
private long lastTimestamp = -1L;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*
|
||||
* @param datacenterId 数据中心ID(0-31)
|
||||
* @param machineId 机器ID(0-31)
|
||||
* @throws IllegalArgumentException 如果ID超出范围
|
||||
*/
|
||||
public SnowflakeIdGenerator(long datacenterId, long machineId) {
|
||||
if (datacenterId < 0 || datacenterId > MAX_DATACENTER_ID) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format("Datacenter ID must be between 0 and %d", MAX_DATACENTER_ID));
|
||||
}
|
||||
if (machineId < 0 || machineId > MAX_MACHINE_ID) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format("Machine ID must be between 0 and %d", MAX_MACHINE_ID));
|
||||
}
|
||||
this.datacenterId = datacenterId;
|
||||
this.machineId = machineId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成下一个ID(线程安全)
|
||||
*
|
||||
* @return 唯一的Long类型ID
|
||||
*/
|
||||
public synchronized long nextId() {
|
||||
long timestamp = getCurrentTimestamp();
|
||||
|
||||
// 时钟回拨检查
|
||||
if (timestamp < lastTimestamp) {
|
||||
long offset = lastTimestamp - timestamp;
|
||||
if (offset <= 5) {
|
||||
// 少量时钟回拨,等待
|
||||
try {
|
||||
Thread.sleep(offset << 1);
|
||||
timestamp = getCurrentTimestamp();
|
||||
if (timestamp < lastTimestamp) {
|
||||
throw new RuntimeException("Clock moved backwards. Refusing to generate ID");
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException("Clock moved backwards. Waiting interrupted", e);
|
||||
}
|
||||
} else {
|
||||
throw new RuntimeException("Clock moved backwards. Refusing to generate ID");
|
||||
}
|
||||
}
|
||||
|
||||
// 同一毫秒内,序列号自增
|
||||
if (timestamp == lastTimestamp) {
|
||||
sequence = (sequence + 1) & MAX_SEQUENCE;
|
||||
// 序列号溢出,等待下一毫秒
|
||||
if (sequence == 0) {
|
||||
timestamp = tilNextMillis(lastTimestamp);
|
||||
}
|
||||
} else {
|
||||
// 新的毫秒,序列号重置
|
||||
sequence = 0L;
|
||||
}
|
||||
|
||||
lastTimestamp = timestamp;
|
||||
|
||||
// 组装ID
|
||||
return ((timestamp - EPOCH) << TIMESTAMP_SHIFT)
|
||||
| (datacenterId << DATACENTER_ID_SHIFT)
|
||||
| (machineId << MACHINE_ID_SHIFT)
|
||||
| sequence;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前时间戳
|
||||
*/
|
||||
private long getCurrentTimestamp() {
|
||||
return System.currentTimeMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
* 等待下一毫秒
|
||||
*/
|
||||
private long tilNextMillis(long lastTimestamp) {
|
||||
long timestamp = getCurrentTimestamp();
|
||||
while (timestamp <= lastTimestamp) {
|
||||
timestamp = getCurrentTimestamp();
|
||||
}
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析ID,获取时间戳信息
|
||||
*
|
||||
* @param id 雪花算法生成的ID
|
||||
* @return 原始时间戳(毫秒)
|
||||
*/
|
||||
public static long parseTimestamp(long id) {
|
||||
return ((id >> TIMESTAMP_SHIFT) & ~(-1L << 41L)) + EPOCH;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析ID,获取数据中心ID
|
||||
*
|
||||
* @param id 雪花算法生成的ID
|
||||
* @return 数据中心ID
|
||||
*/
|
||||
public static long parseDatacenterId(long id) {
|
||||
return (id >> DATACENTER_ID_SHIFT) & MAX_DATACENTER_ID;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析ID,获取机器ID
|
||||
*
|
||||
* @param id 雪花算法生成的ID
|
||||
* @return 机器ID
|
||||
*/
|
||||
public static long parseMachineId(long id) {
|
||||
return (id >> MACHINE_ID_SHIFT) & MAX_MACHINE_ID;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
package com.viewsh.module.ops.service;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 工单详情 VO(基类)
|
||||
* <p>
|
||||
* 支持多态,具体业务类型可继承此类添加扩展字段
|
||||
*
|
||||
* @author lzh
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class OrderDetailVO {
|
||||
|
||||
/**
|
||||
* 工单ID
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 工单编号
|
||||
*/
|
||||
private String orderCode;
|
||||
|
||||
/**
|
||||
* 工单类型
|
||||
*/
|
||||
private String orderType;
|
||||
|
||||
/**
|
||||
* 来源类型
|
||||
*/
|
||||
private String sourceType;
|
||||
|
||||
/**
|
||||
* 工单标题
|
||||
*/
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 工单描述
|
||||
*/
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 优先级
|
||||
*/
|
||||
private Integer priority;
|
||||
|
||||
/**
|
||||
* 工单状态
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 区域ID
|
||||
*/
|
||||
private Long areaId;
|
||||
|
||||
/**
|
||||
* 具体位置描述
|
||||
*/
|
||||
private String location;
|
||||
|
||||
/**
|
||||
* 加急原因
|
||||
*/
|
||||
private String urgentReason;
|
||||
|
||||
/**
|
||||
* 当前执行人ID
|
||||
*/
|
||||
private Long assigneeId;
|
||||
|
||||
/**
|
||||
* 当前执行人姓名
|
||||
*/
|
||||
private String assigneeName;
|
||||
|
||||
/**
|
||||
* 巡检员ID
|
||||
*/
|
||||
private Long inspectorId;
|
||||
|
||||
/**
|
||||
* 巡检员姓名
|
||||
*/
|
||||
private String inspectorName;
|
||||
|
||||
/**
|
||||
* 工单开始时间
|
||||
*/
|
||||
private LocalDateTime startTime;
|
||||
|
||||
/**
|
||||
* 工单结束时间
|
||||
*/
|
||||
private LocalDateTime endTime;
|
||||
|
||||
/**
|
||||
* 验收评分
|
||||
*/
|
||||
private Integer qualityScore;
|
||||
|
||||
/**
|
||||
* 验收评语
|
||||
*/
|
||||
private String qualityComment;
|
||||
|
||||
/**
|
||||
* 响应耗时(秒)
|
||||
*/
|
||||
private Integer responseSeconds;
|
||||
|
||||
/**
|
||||
* 完成耗时(秒)
|
||||
*/
|
||||
private Integer completionSeconds;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
/**
|
||||
* 触发来源
|
||||
*/
|
||||
private String triggerSource;
|
||||
|
||||
/**
|
||||
* 触发规则ID
|
||||
*/
|
||||
private Long triggerRuleId;
|
||||
|
||||
/**
|
||||
* 触发设备ID
|
||||
*/
|
||||
private Long triggerDeviceId;
|
||||
|
||||
/**
|
||||
* 触发设备Key
|
||||
*/
|
||||
private String triggerDeviceKey;
|
||||
|
||||
/**
|
||||
* 扩展信息(Map形式,通用字段)
|
||||
*/
|
||||
@Builder.Default
|
||||
private Map<String, Object> extInfo = new java.util.HashMap<>();
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.viewsh.module.ops.service;
|
||||
|
||||
import com.viewsh.module.ops.dal.dataobject.workorder.OpsOrderDO;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 工单扩展表查询处理器接口
|
||||
* <p>
|
||||
* 各业务模块实现此接口,提供对应业务类型的扩展信息加载能力。
|
||||
* 例如:
|
||||
* - environment-biz 实现 CleanOrderExtQueryHandler
|
||||
* - security-biz 实现 SecurityOrderExtQueryHandler
|
||||
*
|
||||
* @author lzh
|
||||
*/
|
||||
public interface OrderExtQueryHandler {
|
||||
|
||||
/**
|
||||
* 是否支持此业务类型
|
||||
*
|
||||
* @param orderType 工单类型(如:CLEAN、SECURITY、FACILITIES)
|
||||
* @return true-支持,false-不支持
|
||||
*/
|
||||
boolean supports(String orderType);
|
||||
|
||||
/**
|
||||
* 为汇总VO填充扩展信息
|
||||
* <p>
|
||||
* 用于分页查询场景,将扩展信息填充到 extInfo Map 中
|
||||
*
|
||||
* @param vo 汇总VO
|
||||
* @param orderId 工单ID
|
||||
*/
|
||||
void enrichWithExtInfo(OrderSummaryVO vo, Long orderId);
|
||||
|
||||
/**
|
||||
* 构建详情VO
|
||||
* <p>
|
||||
* 用于详情查询场景,返回包含完整扩展信息的详情VO
|
||||
*
|
||||
* @param order 工单主表数据
|
||||
* @return 详情VO
|
||||
*/
|
||||
OrderDetailVO buildDetailVO(OpsOrderDO order);
|
||||
}
|
||||
@@ -0,0 +1,220 @@
|
||||
package com.viewsh.module.ops.service;
|
||||
|
||||
import com.viewsh.framework.common.pojo.PageResult;
|
||||
import com.viewsh.module.ops.dal.dataobject.workorder.OpsOrderDO;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 工单中心查询服务接口
|
||||
* <p>
|
||||
* 职责:
|
||||
* 1. 综合分页查询(支持所有业务类型)
|
||||
* 2. 工单详情查询(含完整扩展信息)
|
||||
* 3. 工单统计信息
|
||||
*
|
||||
* @author lzh
|
||||
*/
|
||||
public interface OrderQueryService {
|
||||
|
||||
/**
|
||||
* 分页查询工单(支持所有业务类型)
|
||||
* <p>
|
||||
* 查询主表数据后,自动调用对应的扩展查询处理器填充扩展信息
|
||||
*
|
||||
* @param query 查询条件
|
||||
* @return 分页结果
|
||||
*/
|
||||
PageResult<OrderSummaryVO> queryPage(OrderQuery query);
|
||||
|
||||
/**
|
||||
* 查询工单详情(含完整扩展信息)
|
||||
* <p>
|
||||
* 根据工单的业务类型,调用对应的扩展查询处理器加载完整扩展信息
|
||||
*
|
||||
* @param orderId 工单ID
|
||||
* @return 详情VO
|
||||
*/
|
||||
OrderDetailVO getDetail(Long orderId);
|
||||
|
||||
/**
|
||||
* 获取工单统计信息
|
||||
* <p>
|
||||
* 按业务类型分组返回工单数量统计
|
||||
*
|
||||
* @param groupBy 分组维度(status:按状态统计,null:只按类型统计)
|
||||
* @return 统计结果
|
||||
*/
|
||||
Map<String, Object> getStats(String groupBy);
|
||||
|
||||
/**
|
||||
* 工单查询条件
|
||||
*/
|
||||
class OrderQuery {
|
||||
/**
|
||||
* 页码(从1开始)
|
||||
*/
|
||||
private Integer page = 1;
|
||||
|
||||
/**
|
||||
* 每页大小
|
||||
*/
|
||||
private Integer size = 20;
|
||||
|
||||
/**
|
||||
* 工单类型(可选)
|
||||
*/
|
||||
private String orderType;
|
||||
|
||||
/**
|
||||
* 工单状态(可选)
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 优先级(可选)
|
||||
*/
|
||||
private Integer priority;
|
||||
|
||||
/**
|
||||
* 区域ID(可选)
|
||||
*/
|
||||
private Long areaId;
|
||||
|
||||
/**
|
||||
* 执行人ID(可选)
|
||||
*/
|
||||
private Long assigneeId;
|
||||
|
||||
/**
|
||||
* 工单编号模糊查询(可选)
|
||||
*/
|
||||
private String orderCode;
|
||||
|
||||
/**
|
||||
* 标题模糊查询(可选)
|
||||
*/
|
||||
private String title;
|
||||
|
||||
public Integer getPage() {
|
||||
return page;
|
||||
}
|
||||
|
||||
public void setPage(Integer page) {
|
||||
this.page = page;
|
||||
}
|
||||
|
||||
public Integer getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public void setSize(Integer size) {
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
public String getOrderType() {
|
||||
return orderType;
|
||||
}
|
||||
|
||||
public void setOrderType(String orderType) {
|
||||
this.orderType = orderType;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public Integer getPriority() {
|
||||
return priority;
|
||||
}
|
||||
|
||||
public void setPriority(Integer priority) {
|
||||
this.priority = priority;
|
||||
}
|
||||
|
||||
public Long getAreaId() {
|
||||
return areaId;
|
||||
}
|
||||
|
||||
public void setAreaId(Long areaId) {
|
||||
this.areaId = areaId;
|
||||
}
|
||||
|
||||
public Long getAssigneeId() {
|
||||
return assigneeId;
|
||||
}
|
||||
|
||||
public void setAssigneeId(Long assigneeId) {
|
||||
this.assigneeId = assigneeId;
|
||||
}
|
||||
|
||||
public String getOrderCode() {
|
||||
return orderCode;
|
||||
}
|
||||
|
||||
public void setOrderCode(String orderCode) {
|
||||
this.orderCode = orderCode;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public static OrderQuery builder() {
|
||||
return new OrderQuery();
|
||||
}
|
||||
|
||||
public OrderQuery withPage(Integer page) {
|
||||
this.page = page;
|
||||
return this;
|
||||
}
|
||||
|
||||
public OrderQuery withSize(Integer size) {
|
||||
this.size = size;
|
||||
return this;
|
||||
}
|
||||
|
||||
public OrderQuery withOrderType(String orderType) {
|
||||
this.orderType = orderType;
|
||||
return this;
|
||||
}
|
||||
|
||||
public OrderQuery withStatus(String status) {
|
||||
this.status = status;
|
||||
return this;
|
||||
}
|
||||
|
||||
public OrderQuery withPriority(Integer priority) {
|
||||
this.priority = priority;
|
||||
return this;
|
||||
}
|
||||
|
||||
public OrderQuery withAreaId(Long areaId) {
|
||||
this.areaId = areaId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public OrderQuery withAssigneeId(Long assigneeId) {
|
||||
this.assigneeId = assigneeId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public OrderQuery withOrderCode(String orderCode) {
|
||||
this.orderCode = orderCode;
|
||||
return this;
|
||||
}
|
||||
|
||||
public OrderQuery withTitle(String title) {
|
||||
this.title = title;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
package com.viewsh.module.ops.service;
|
||||
|
||||
import com.viewsh.framework.common.pojo.PageResult;
|
||||
import com.viewsh.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import com.viewsh.module.ops.dal.dataobject.workorder.OpsOrderDO;
|
||||
import com.viewsh.module.ops.dal.mysql.workorder.OpsOrderMapper;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 工单中心查询服务实现
|
||||
*
|
||||
* @author lzh
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class OrderQueryServiceImpl implements OrderQueryService {
|
||||
|
||||
@Resource
|
||||
private OpsOrderMapper opsOrderMapper;
|
||||
|
||||
/**
|
||||
* 扩展查询处理器列表(由各业务模块注入)
|
||||
*/
|
||||
private final List<OrderExtQueryHandler> extQueryHandlers;
|
||||
|
||||
public OrderQueryServiceImpl(List<OrderExtQueryHandler> extQueryHandlers) {
|
||||
this.extQueryHandlers = extQueryHandlers;
|
||||
log.info("OrderQueryService 初始化,注册 {} 个扩展查询处理器", extQueryHandlers.size());
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<OrderSummaryVO> queryPage(OrderQuery query) {
|
||||
// 1. 构建查询条件
|
||||
LambdaQueryWrapperX<OpsOrderDO> wrapper = new LambdaQueryWrapperX<OpsOrderDO>()
|
||||
.eqIfPresent(OpsOrderDO::getOrderType, query.getOrderType())
|
||||
.eqIfPresent(OpsOrderDO::getStatus, query.getStatus())
|
||||
.eqIfPresent(OpsOrderDO::getPriority, query.getPriority())
|
||||
.eqIfPresent(OpsOrderDO::getAreaId, query.getAreaId())
|
||||
.eqIfPresent(OpsOrderDO::getAssigneeId, query.getAssigneeId())
|
||||
.likeIfPresent(OpsOrderDO::getOrderCode, query.getOrderCode())
|
||||
.likeIfPresent(OpsOrderDO::getTitle, query.getTitle())
|
||||
.orderByDesc(OpsOrderDO::getCreateTime);
|
||||
|
||||
// 2. 分页查询主表
|
||||
PageResult<OpsOrderDO> pageResult = opsOrderMapper.selectPage(query.getPage(), query.getSize(), wrapper);
|
||||
|
||||
// 3. <20><><EFBFBD>换为 VO 并填充扩展信息
|
||||
List<OrderSummaryVO> voList = pageResult.getList().stream()
|
||||
.map(this::convertToSummaryVO)
|
||||
.toList();
|
||||
|
||||
return new PageResult<>(voList, pageResult.getTotal());
|
||||
}
|
||||
|
||||
@Override
|
||||
public OrderDetailVO getDetail(Long orderId) {
|
||||
// 1. 查询主表
|
||||
OpsOrderDO order = opsOrderMapper.selectById(orderId);
|
||||
if (order == null) {
|
||||
throw new RuntimeException("工单不存在: " + orderId);
|
||||
}
|
||||
|
||||
// 2. 找到对应的扩展查询处理器
|
||||
OrderExtQueryHandler handler = findExtQueryHandler(order.getOrderType());
|
||||
|
||||
// 3. 构建详情VO
|
||||
if (handler != null) {
|
||||
return handler.buildDetailVO(order);
|
||||
} else {
|
||||
// 没有对应的扩展查询处理器,返回基础VO
|
||||
return convertToDetailVO(order);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getStats(String groupBy) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
|
||||
if ("status".equals(groupBy)) {
|
||||
// 按类型和状态统计
|
||||
List<OpsOrderDO> allOrders = opsOrderMapper.selectList();
|
||||
Map<String, Map<String, Long>> typeStatusMap = new HashMap<>();
|
||||
|
||||
for (OpsOrderDO order : allOrders) {
|
||||
String type = order.getOrderType();
|
||||
String status = order.getStatus();
|
||||
|
||||
typeStatusMap.computeIfAbsent(type, k -> new HashMap<>())
|
||||
.merge(status, 1L, Long::sum);
|
||||
}
|
||||
|
||||
result.putAll(typeStatusMap);
|
||||
} else {
|
||||
// 只按类型统计
|
||||
List<OpsOrderDO> allOrders = opsOrderMapper.selectList();
|
||||
Map<String, Long> typeCountMap = new HashMap<>();
|
||||
|
||||
for (OpsOrderDO order : allOrders) {
|
||||
String type = order.getOrderType();
|
||||
typeCountMap.merge(type, 1L, Long::sum);
|
||||
}
|
||||
|
||||
result.putAll(typeCountMap);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为汇总VO并填充扩展信息
|
||||
*/
|
||||
private OrderSummaryVO convertToSummaryVO(OpsOrderDO order) {
|
||||
OrderSummaryVO vo = OrderSummaryVO.builder()
|
||||
.id(order.getId())
|
||||
.orderCode(order.getOrderCode())
|
||||
.orderType(order.getOrderType())
|
||||
.title(order.getTitle())
|
||||
.description(order.getDescription())
|
||||
.priority(order.getPriority())
|
||||
.status(order.getStatus())
|
||||
.areaId(order.getAreaId())
|
||||
.location(order.getLocation())
|
||||
.assigneeId(order.getAssigneeId())
|
||||
.startTime(order.getStartTime())
|
||||
.endTime(order.getEndTime())
|
||||
.createTime(order.getCreateTime())
|
||||
.build();
|
||||
|
||||
// 填充扩展信息
|
||||
OrderExtQueryHandler handler = findExtQueryHandler(order.getOrderType());
|
||||
if (handler != null) {
|
||||
handler.enrichWithExtInfo(vo, order.getId());
|
||||
}
|
||||
|
||||
return vo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为详情VO(基础版本,不含扩展信息)
|
||||
*/
|
||||
private OrderDetailVO convertToDetailVO(OpsOrderDO order) {
|
||||
return OrderDetailVO.builder()
|
||||
.id(order.getId())
|
||||
.orderCode(order.getOrderCode())
|
||||
.orderType(order.getOrderType())
|
||||
.sourceType(order.getSourceType())
|
||||
.title(order.getTitle())
|
||||
.description(order.getDescription())
|
||||
.priority(order.getPriority())
|
||||
.status(order.getStatus())
|
||||
.areaId(order.getAreaId())
|
||||
.location(order.getLocation())
|
||||
.urgentReason(order.getUrgentReason())
|
||||
.assigneeId(order.getAssigneeId())
|
||||
.inspectorId(order.getInspectorId())
|
||||
.startTime(order.getStartTime())
|
||||
.endTime(order.getEndTime())
|
||||
.qualityScore(order.getQualityScore())
|
||||
.qualityComment(order.getQualityComment())
|
||||
.responseSeconds(order.getResponseSeconds())
|
||||
.completionSeconds(order.getCompletionSeconds())
|
||||
.createTime(order.getCreateTime())
|
||||
.updateTime(order.getUpdateTime())
|
||||
.triggerSource(order.getTriggerSource())
|
||||
.triggerRuleId(order.getTriggerRuleId())
|
||||
.triggerDeviceId(order.getTriggerDeviceId())
|
||||
.triggerDeviceKey(order.getTriggerDeviceKey())
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找对应的扩展查询处理器
|
||||
*/
|
||||
private OrderExtQueryHandler findExtQueryHandler(String orderType) {
|
||||
return extQueryHandlers.stream()
|
||||
.filter(handler -> handler.supports(orderType))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
package com.viewsh.module.ops.service;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 工单汇总信息 VO
|
||||
* <p>
|
||||
* 用于工单中心分页查询,包含主表信息和扩展信息(Map形式)
|
||||
*
|
||||
* @author lzh
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class OrderSummaryVO {
|
||||
|
||||
/**
|
||||
* 工单ID
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 工单编号
|
||||
*/
|
||||
private String orderCode;
|
||||
|
||||
/**
|
||||
* 工单类型(CLEAN/SECURITY/FACILITIES/SERVICE)
|
||||
*/
|
||||
private String orderType;
|
||||
|
||||
/**
|
||||
* 工单标题
|
||||
*/
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 工单描述
|
||||
*/
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 优先级(0=P0/1=P1/2=P2)
|
||||
*/
|
||||
private Integer priority;
|
||||
|
||||
/**
|
||||
* 工单状态
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 区域ID
|
||||
*/
|
||||
private Long areaId;
|
||||
|
||||
/**
|
||||
* 具体位置描述
|
||||
*/
|
||||
private String location;
|
||||
|
||||
/**
|
||||
* 当前执行人ID
|
||||
*/
|
||||
private Long assigneeId;
|
||||
|
||||
/**
|
||||
* 当前执行人姓名(冗余)
|
||||
*/
|
||||
private String assigneeName;
|
||||
|
||||
/**
|
||||
* 工单开始时间
|
||||
*/
|
||||
private LocalDateTime startTime;
|
||||
|
||||
/**
|
||||
* 工单结束时间
|
||||
*/
|
||||
private LocalDateTime endTime;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 扩展信息(Map形式)
|
||||
* <p>
|
||||
* 根据工单类型不同,包含不同的扩展字段:
|
||||
* - CLEAN: expectedDuration, cleaningType, difficultyLevel
|
||||
* - SECURITY: route, checkpoint, patrolTime
|
||||
*/
|
||||
@Builder.Default
|
||||
private Map<String, Object> extInfo = new java.util.HashMap<>();
|
||||
}
|
||||
Reference in New Issue
Block a user