吉安感知网项目-后端
linwei
5 days ago 8edf0004e4bd8b758b0fc5940a8a83f0f7948f74
feat(workorder): 添加事件处置记录和状态流转功能

- 新增事件状态枚举类,定义状态流转矩阵和校验逻辑
- 创建事件处置记录实体、参数、视图对象及相关映射文件
- 实现处置记录的新增、查询及状态同步更新功能
- 添加事件详情完整接口,包含处置记录和状态流转时间线
- 扩展事件统计功能,支持按状态分类统计
- 更新事件状态描述和数据库查询逻辑以匹配新状态定义
7 files modified
16 files added
1652 ■■■■■ changed files
drone-service/drone-gd/src/main/java/org/sxkj/gd/workorder/controller/GdClueEventController.java 75 ●●●●● patch | view | raw | blame | history
drone-service/drone-gd/src/main/java/org/sxkj/gd/workorder/entity/GdClueDisposeRecordEntity.java 101 ●●●●● patch | view | raw | blame | history
drone-service/drone-gd/src/main/java/org/sxkj/gd/workorder/entity/GdClueEventEntity.java 4 ●●●● patch | view | raw | blame | history
drone-service/drone-gd/src/main/java/org/sxkj/gd/workorder/entity/GdClueEventStatusFlowEntity.java 89 ●●●●● patch | view | raw | blame | history
drone-service/drone-gd/src/main/java/org/sxkj/gd/workorder/enums/EventStatusEnum.java 163 ●●●●● patch | view | raw | blame | history
drone-service/drone-gd/src/main/java/org/sxkj/gd/workorder/mapper/GdClueDisposeRecordMapper.java 45 ●●●●● patch | view | raw | blame | history
drone-service/drone-gd/src/main/java/org/sxkj/gd/workorder/mapper/GdClueDisposeRecordMapper.xml 64 ●●●●● patch | view | raw | blame | history
drone-service/drone-gd/src/main/java/org/sxkj/gd/workorder/mapper/GdClueEventMapper.xml 10 ●●●● patch | view | raw | blame | history
drone-service/drone-gd/src/main/java/org/sxkj/gd/workorder/mapper/GdClueEventStatusFlowMapper.java 45 ●●●●● patch | view | raw | blame | history
drone-service/drone-gd/src/main/java/org/sxkj/gd/workorder/mapper/GdClueEventStatusFlowMapper.xml 61 ●●●●● patch | view | raw | blame | history
drone-service/drone-gd/src/main/java/org/sxkj/gd/workorder/param/GdClueDisposeRecordAddParam.java 96 ●●●●● patch | view | raw | blame | history
drone-service/drone-gd/src/main/java/org/sxkj/gd/workorder/param/GdClueEventStatusUpdateParam.java 74 ●●●●● patch | view | raw | blame | history
drone-service/drone-gd/src/main/java/org/sxkj/gd/workorder/service/IGdClueDisposeRecordService.java 56 ●●●●● patch | view | raw | blame | history
drone-service/drone-gd/src/main/java/org/sxkj/gd/workorder/service/IGdClueEventService.java 12 ●●●●● patch | view | raw | blame | history
drone-service/drone-gd/src/main/java/org/sxkj/gd/workorder/service/IGdClueEventStatusFlowService.java 59 ●●●●● patch | view | raw | blame | history
drone-service/drone-gd/src/main/java/org/sxkj/gd/workorder/service/impl/GdClueDisposeRecordServiceImpl.java 179 ●●●●● patch | view | raw | blame | history
drone-service/drone-gd/src/main/java/org/sxkj/gd/workorder/service/impl/GdClueEventServiceImpl.java 79 ●●●●● patch | view | raw | blame | history
drone-service/drone-gd/src/main/java/org/sxkj/gd/workorder/service/impl/GdClueEventStatusFlowServiceImpl.java 126 ●●●●● patch | view | raw | blame | history
drone-service/drone-gd/src/main/java/org/sxkj/gd/workorder/vo/GdClueDisposeRecordVO.java 126 ●●●●● patch | view | raw | blame | history
drone-service/drone-gd/src/main/java/org/sxkj/gd/workorder/vo/GdClueEventCountVO.java 9 ●●●●● patch | view | raw | blame | history
drone-service/drone-gd/src/main/java/org/sxkj/gd/workorder/vo/GdClueEventDetailVO.java 56 ●●●●● patch | view | raw | blame | history
drone-service/drone-gd/src/main/java/org/sxkj/gd/workorder/vo/GdClueEventStatusFlowVO.java 119 ●●●●● patch | view | raw | blame | history
drone-service/drone-gd/src/main/java/org/sxkj/gd/workorder/vo/GdClueEventVO.java 4 ●●●● patch | view | raw | blame | history
drone-service/drone-gd/src/main/java/org/sxkj/gd/workorder/controller/GdClueEventController.java
@@ -33,12 +33,14 @@
import org.springframework.web.bind.annotation.*;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.sxkj.gd.workorder.entity.GdClueEventEntity;
import org.sxkj.gd.workorder.param.GdClueDisposeRecordAddParam;
import org.sxkj.gd.workorder.param.GdClueEventRejectParam;
import org.sxkj.gd.workorder.vo.GdClueEventCountVO;
import org.sxkj.gd.workorder.vo.GdClueEventListVO;
import org.sxkj.gd.workorder.vo.GdClueEventVO;
import org.sxkj.gd.workorder.param.GdClueEventStatusUpdateParam;
import org.sxkj.gd.workorder.vo.*;
import org.sxkj.gd.workorder.excel.GdClueEventExcel;
import org.sxkj.gd.workorder.service.IGdClueDisposeRecordService;
import org.sxkj.gd.workorder.service.IGdClueEventService;
import org.sxkj.gd.workorder.service.IGdClueEventStatusFlowService;
import org.springblade.core.boot.ctrl.BladeController;
import org.springblade.core.tool.utils.DateUtil;
import org.springblade.core.excel.util.ExcelUtil;
@@ -62,6 +64,8 @@
public class GdClueEventController extends BladeController {
    private final IGdClueEventService gdClueEventService;
    private final IGdClueDisposeRecordService gdClueDisposeRecordService;
    private final IGdClueEventStatusFlowService gdClueEventStatusFlowService;
    /**
     * 事件表(线索事件) 详情
@@ -170,6 +174,71 @@
        return R.status(gdClueEventService.handleClueEvent(rejectParam));
    }
    /**
     * 事件详情(包含处置记录和状态流转)
     */
    @GetMapping("/detailFull")
    @ApiOperationSupport(order = 9)
    @ApiOperation(value = "事件详情(包含处置记录和状态流转)", notes = "传入事件ID")
    public R<GdClueEventDetailVO> detailFull(@ApiParam(value = "事件ID", required = true) @RequestParam Long id) {
        // 1. 查询事件基本信息
        GdClueEventVO eventInfo = gdClueEventService.getGdClueEventDetail(id);
        // 2. 查询处置记录时间线
        List<GdClueDisposeRecordVO> disposeRecords = gdClueDisposeRecordService.getDisposeRecordsByEventId(id);
        // 3. 查询状态流转时间线
        List<GdClueEventStatusFlowVO> statusFlows = gdClueEventStatusFlowService.getStatusFlowsByEventId(id);
        // 4. 组装返回数据
        GdClueEventDetailVO detailVO = new GdClueEventDetailVO();
        detailVO.setEventInfo(eventInfo);
        detailVO.setDisposeRecords(disposeRecords);
        detailVO.setStatusFlows(statusFlows);
        return R.data(detailVO);
    }
    /**
     * 查询处置记录列表
     */
    @GetMapping("/disposeRecords")
    @ApiOperationSupport(order = 10)
    @ApiOperation(value = "查询处置记录列表", notes = "传入事件ID")
    public R<List<GdClueDisposeRecordVO>> getDisposeRecords(@ApiParam(value = "事件ID", required = true) @RequestParam Long eventId) {
        return R.data(gdClueDisposeRecordService.getDisposeRecordsByEventId(eventId));
    }
    /**
     * 新增处置记录
     */
    @PostMapping("/addDisposeRecord")
    @ApiOperationSupport(order = 11)
    @ApiOperation(value = "新增处置记录", notes = "传入处置记录参数,可同步更新事件状态")
    public R addDisposeRecord(@Valid @RequestBody GdClueDisposeRecordAddParam addParam) {
        return R.status(gdClueDisposeRecordService.addDisposeRecord(addParam));
    }
    /**
     * 查询状态流转记录列表
     */
    @GetMapping("/statusFlows")
    @ApiOperationSupport(order = 12)
    @ApiOperation(value = "查询状态流转记录列表", notes = "传入事件ID")
    public R<List<GdClueEventStatusFlowVO>> getStatusFlows(@ApiParam(value = "事件ID", required = true) @RequestParam Long eventId) {
        return R.data(gdClueEventStatusFlowService.getStatusFlowsByEventId(eventId));
    }
    /**
     * 更新事件状态
     */
    @PostMapping("/updateStatus")
    @ApiOperationSupport(order = 13)
    @ApiOperation(value = "更新事件状态", notes = "传入事件ID和目标状态,必须符合状态流转矩阵")
    public R updateStatus(@Valid @RequestBody GdClueEventStatusUpdateParam updateParam) {
        return R.status(gdClueEventService.updateEventStatus(updateParam));
    }
//    /**
//     * 导出数据
//     */
drone-service/drone-gd/src/main/java/org/sxkj/gd/workorder/entity/GdClueDisposeRecordEntity.java
New file
@@ -0,0 +1,101 @@
/*
 *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice,
 *  this list of conditions and the following disclaimer.
 *  Redistributions in binary form must reproduce the above copyright
 *  notice, this list of conditions and the following disclaimer in the
 *  documentation and/or other materials provided with the distribution.
 *  Neither the name of the dreamlu.net developer nor the names of its
 *  contributors may be used to endorse or promote products derived from
 *  this software without specific prior written permission.
 *  Author: Chill 庄骞 (smallchill@163.com)
 */
package org.sxkj.gd.workorder.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springblade.core.mp.base.BaseEntity;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
import java.util.List;
/**
 * 事件处置流水记录表 实体类
 * <p>
 * 记录事件的处置过程信息,包括核查记录和处置记录
 * </p>
 *
 * @author system
 * @since 2026-07-29
 */
@Data
@TableName(value = "ja_gd_clue_dispose_record", autoResultMap = true)
@ApiModel(value = "GdClueDisposeRecord对象", description = "事件处置流水记录表")
@EqualsAndHashCode(callSuper = true)
public class GdClueDisposeRecordEntity extends BaseEntity {
    /**
     * 关联事件ID(ja_gd_clue_event.id)
     */
    @ApiModelProperty(value = "关联事件ID")
    private Long eventId;
    /**
     * 记录类型:0核查记录、1处置记录
     */
    @ApiModelProperty(value = "记录类型:0核查记录、1处置记录")
    private Integer recordType;
    /**
     * 处置操作人
     */
    @ApiModelProperty(value = "处置操作人")
    private Long disposeUser;
    /**
     * 处置操作部门
     */
    @ApiModelProperty(value = "处置操作部门")
    private Long disposeDept;
    /**
     * 处置状态,与事件表event_status保持一致:0待确认、1处置中、2已退回、3已处置、4已完成
     */
    @ApiModelProperty(value = "处置状态:0待确认、1处置中、2已退回、3已处置、4已完成")
    private Integer disposeStatus;
    /**
     * 处置文字说明
     */
    @ApiModelProperty(value = "处置文字说明")
    private String disposeContent;
    /**
     * 现场附件图片URL数组,使用数组存储,不使用逗号拼接字符串
     * <p>
     * 数据库字段类型为text[],MyBatis-Plus使用TypeHandler进行转换
     * </p>
     */
    @ApiModelProperty(value = "现场附件图片URL数组")
    @com.baomidou.mybatisplus.annotation.TableField(typeHandler = JacksonTypeHandler.class)
    private List<String> attachUrl;
    /**
     * 实际操作时间(界面展示时间)
     */
    @ApiModelProperty(value = "实际操作时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date operateTime;
}
drone-service/drone-gd/src/main/java/org/sxkj/gd/workorder/entity/GdClueEventEntity.java
@@ -70,9 +70,9 @@
    @ApiModelProperty(value = "纬度")
    private Double latitude;
    /**
     * 状态:0未分发、1已分发、2已驳回、3已确认
     * 状态:0待确认、1处置中、2已退回、3已处置、4已完成
     */
    @ApiModelProperty(value = "状态:0未分发、1已分发、2已驳回、3已确认")
    @ApiModelProperty(value = "状态:0待确认、1处置中、2已退回、3已处置、4已完成")
    private Integer eventStatus;
    /**
     * 区域编码
drone-service/drone-gd/src/main/java/org/sxkj/gd/workorder/entity/GdClueEventStatusFlowEntity.java
New file
@@ -0,0 +1,89 @@
/*
 *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice,
 *  this list of conditions and the following disclaimer.
 *  Redistributions in binary form must reproduce the above copyright
 *  notice, this list of conditions and the following disclaimer in the
 *  documentation and/or other materials provided with the distribution.
 *  Neither the name of the dreamlu.net developer nor the names of its
 *  contributors may be used to endorse or promote products derived from
 *  this software without specific prior written permission.
 *  Author: Chill 庄骞 (smallchill@163.com)
 */
package org.sxkj.gd.workorder.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springblade.core.mp.base.BaseEntity;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
/**
 * 事件状态流转履历表 实体类
 * <p>
 * 记录事件状态的变更历史,仅用于审计,不允许删除和修改
 * </p>
 *
 * @author system
 * @since 2026-07-29
 */
@Data
@TableName("ja_gd_clue_event_status_flow")
@ApiModel(value = "GdClueEventStatusFlow对象", description = "事件状态流转履历表")
@EqualsAndHashCode(callSuper = true)
public class GdClueEventStatusFlowEntity extends BaseEntity {
    /**
     * 关联事件ID(ja_gd_clue_event.id)
     */
    @ApiModelProperty(value = "关联事件ID")
    private Long eventId;
    /**
     * 变更前状态:0待确认、1处置中、2已退回、3已处置、4已完成
     */
    @ApiModelProperty(value = "变更前状态:0待确认、1处置中、2已退回、3已处置、4已完成")
    private Integer oldStatus;
    /**
     * 变更后状态:0待确认、1处置中、2已退回、3已处置、4已完成
     */
    @ApiModelProperty(value = "变更后状态:0待确认、1处置中、2已退回、3已处置、4已完成")
    private Integer newStatus;
    /**
     * 状态变更操作人
     */
    @ApiModelProperty(value = "状态变更操作人")
    private Long operateUser;
    /**
     * 状态变更操作部门
     */
    @ApiModelProperty(value = "状态变更操作部门")
    private Long operateDept;
    /**
     * 状态变更备注说明(分发、驳回、启动处置、完成处置等)
     */
    @ApiModelProperty(value = "状态变更备注说明")
    private String operateContent;
    /**
     * 状态变更时间(界面展示时间)
     */
    @ApiModelProperty(value = "状态变更时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date operateTime;
}
drone-service/drone-gd/src/main/java/org/sxkj/gd/workorder/enums/EventStatusEnum.java
New file
@@ -0,0 +1,163 @@
/*
 *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice,
 *  this list of conditions and the following disclaimer.
 *  Redistributions in binary form must reproduce the above copyright
 *  notice, this list of conditions and the following disclaimer in the
 *  documentation and/or other materials provided with the distribution.
 *  Neither the name of the dreamlu.net developer nor the names of its
 *  contributors may be used to endorse or promote products derived from
 *  this software without specific prior written permission.
 *  Author: Chill 庄骞 (smallchill@163.com)
 */
package org.sxkj.gd.workorder.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
/**
 * 事件状态枚举类
 * <p>
 * 定义事件状态的编码和名称,以及合法的状态流转矩阵
 * </p>
 *
 * @author system
 * @since 2026-07-29
 */
@Getter
@AllArgsConstructor
public enum EventStatusEnum {
    /**
     * 待确认
     */
    PENDING_CONFIRM(0, "待确认"),
    /**
     * 处置中
     */
    DISPOSING(1, "处置中"),
    /**
     * 已退回
     */
    RETURNED(2, "已退回"),
    /**
     * 已处置
     */
    DISPOSED(3, "已处置"),
    /**
     * 已完成
     */
    COMPLETED(4, "已完成");
    /**
     * 状态编码
     */
    private final Integer code;
    /**
     * 状态名称
     */
    private final String name;
    /**
     * 合法状态流转矩阵
     * Key: 当前状态编码
     * Value: 允许流转到的目标状态编码列表
     */
    private static final Map<Integer, List<Integer>> STATUS_FLOW_MATRIX = new HashMap<>();
    static {
        // 初始化状态流转矩阵
        // 0-待确认 -> 1-处置中, 2-已退回
        STATUS_FLOW_MATRIX.put(0, Arrays.asList(1, 2));
        // 1-处置中 -> 2-已退回, 3-已处置
        STATUS_FLOW_MATRIX.put(1, Arrays.asList(2, 3));
        // 2-已退回 -> 0-待确认, 1-处置中
        STATUS_FLOW_MATRIX.put(2, Arrays.asList(0, 1));
        // 3-已处置 -> 2-已退回, 4-已完成
        STATUS_FLOW_MATRIX.put(3, Arrays.asList(2, 4));
        // 4-已完成 -> 终态,不允许流转
        STATUS_FLOW_MATRIX.put(4, Arrays.asList());
    }
    /**
     * 根据状态编码获取枚举对象
     *
     * @param code 状态编码
     * @return 枚举对象,如果不存在则返回null
     */
    public static EventStatusEnum getByCode(Integer code) {
        if (code == null) {
            return null;
        }
        for (EventStatusEnum status : values()) {
            if (status.getCode().equals(code)) {
                return status;
            }
        }
        return null;
    }
    /**
     * 根据状态编码获取状态名称
     *
     * @param code 状态编码
     * @return 状态名称,如果不存在则返回"未知状态"
     */
    public static String getNameByCode(Integer code) {
        EventStatusEnum status = getByCode(code);
        return status != null ? status.getName() : "未知状态";
    }
    /**
     * 校验状态流转是否合法
     *
     * @param currentStatus 当前状态编码
     * @param targetStatus  目标状态编码
     * @return true-合法,false-非法
     */
    public static boolean isValidTransition(Integer currentStatus, Integer targetStatus) {
        if (currentStatus == null || targetStatus == null) {
            return false;
        }
        List<Integer> allowedStatuses = STATUS_FLOW_MATRIX.get(currentStatus);
        return allowedStatuses != null && allowedStatuses.contains(targetStatus);
    }
    /**
     * 获取指定状态允许流转的所有目标状态编码列表
     *
     * @param currentStatus 当前状态编码
     * @return 允许流转的目标状态编码列表
     */
    public static List<Integer> getAllowedTransitions(Integer currentStatus) {
        if (currentStatus == null) {
            return Arrays.asList();
        }
        List<Integer> allowed = STATUS_FLOW_MATRIX.get(currentStatus);
        return allowed != null ? allowed : Arrays.asList();
    }
    /**
     * 校验状态编码是否有效
     *
     * @param code 状态编码
     * @return true-有效,false-无效
     */
    public static boolean isValidCode(Integer code) {
        return getByCode(code) != null;
    }
}
drone-service/drone-gd/src/main/java/org/sxkj/gd/workorder/mapper/GdClueDisposeRecordMapper.java
New file
@@ -0,0 +1,45 @@
/*
 *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice,
 *  this list of conditions and the following disclaimer.
 *  Redistributions in binary form must reproduce the above copyright
 *  notice, this list of conditions and the following disclaimer in the
 *  documentation and/or other materials provided with the distribution.
 *  Neither the name of the dreamlu.net developer nor the names of its
 *  contributors may be used to endorse or promote products derived from
 *  this software without specific prior written permission.
 *  Author: Chill 庄骞 (smallchill@163.com)
 */
package org.sxkj.gd.workorder.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
import org.sxkj.gd.workorder.entity.GdClueDisposeRecordEntity;
import org.sxkj.gd.workorder.vo.GdClueDisposeRecordVO;
import java.util.List;
/**
 * 事件处置流水记录表 Mapper 接口
 *
 * @author system
 * @since 2026-07-29
 */
public interface GdClueDisposeRecordMapper extends BaseMapper<GdClueDisposeRecordEntity> {
    /**
     * 根据事件ID查询处置记录列表
     * <p>
     * 按操作时间升序排列
     * </p>
     *
     * @param eventId 事件ID
     * @return 处置记录列表
     */
    List<GdClueDisposeRecordVO> selectDisposeRecordsByEventId(@Param("eventId") Long eventId);
}
drone-service/drone-gd/src/main/java/org/sxkj/gd/workorder/mapper/GdClueDisposeRecordMapper.xml
New file
@@ -0,0 +1,64 @@
<?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="org.sxkj.gd.workorder.mapper.GdClueDisposeRecordMapper">
    <!-- 通用查询映射结果 -->
    <resultMap id="disposeRecordResultMap" type="org.sxkj.gd.workorder.entity.GdClueDisposeRecordEntity">
        <result column="id" property="id"/>
        <result column="event_id" property="eventId"/>
        <result column="record_type" property="recordType"/>
        <result column="dispose_user" property="disposeUser"/>
        <result column="dispose_dept" property="disposeDept"/>
        <result column="dispose_status" property="disposeStatus"/>
        <result column="dispose_content" property="disposeContent"/>
        <result column="attach_url" property="attachUrl" typeHandler="com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler"/>
        <result column="operate_time" property="operateTime"/>
        <result column="create_user" property="createUser"/>
        <result column="create_dept" property="createDept"/>
        <result column="create_time" property="createTime"/>
        <result column="update_user" property="updateUser"/>
        <result column="update_time" property="updateTime"/>
        <result column="status" property="status"/>
        <result column="is_deleted" property="isDeleted"/>
    </resultMap>
    <!-- 处置记录VO结果映射 -->
    <resultMap id="disposeRecordVoResultMap" type="org.sxkj.gd.workorder.vo.GdClueDisposeRecordVO">
        <result column="id" property="id"/>
        <result column="event_id" property="eventId"/>
        <result column="record_type" property="recordType"/>
        <result column="dispose_user" property="disposeUser"/>
        <result column="dispose_user_name" property="disposeUserName"/>
        <result column="dispose_dept" property="disposeDept"/>
        <result column="dispose_dept_name" property="disposeDeptName"/>
        <result column="dispose_status" property="disposeStatus"/>
        <result column="dispose_content" property="disposeContent"/>
        <result column="attach_url" property="attachUrl" typeHandler="com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler"/>
        <result column="operate_time" property="operateTime"/>
        <result column="create_time" property="createTime"/>
    </resultMap>
    <!-- 根据事件ID查询处置记录列表 -->
    <select id="selectDisposeRecordsByEventId" resultMap="disposeRecordVoResultMap">
        SELECT
            r.id,
            r.event_id,
            r.record_type,
            r.dispose_user,
            r.dispose_dept,
            r.dispose_status,
            r.dispose_content,
            r.attach_url,
            r.operate_time,
            r.create_time,
            u.real_name AS dispose_user_name,
            d.full_name AS dispose_dept_name
        FROM jagzw_yw.ja_gd_clue_dispose_record r
        LEFT JOIN blade_user u ON r.dispose_user = u.id AND u.is_deleted = 0
        LEFT JOIN blade_dept d ON r.dispose_dept = d.id AND d.is_deleted = 0
        WHERE r.event_id = #{eventId}
          AND r.is_deleted = 0
        ORDER BY r.operate_time ASC
    </select>
</mapper>
drone-service/drone-gd/src/main/java/org/sxkj/gd/workorder/mapper/GdClueEventMapper.xml
@@ -65,6 +65,9 @@
    <resultMap id="gdClueEventCountVoResultMap" type="org.sxkj.gd.workorder.vo.GdClueEventCountVO">
        <result column="total_count" property="totalCount"/>
        <result column="my_count" property="myCount"/>
        <result column="returned_count" property="returnedCount"/>
        <result column="disposed_count" property="disposedCount"/>
        <result column="completed_count" property="completedCount"/>
    </resultMap>
    <sql id="gdClueEventDeptWhere">
@@ -164,10 +167,13 @@
    <select id="selectGdClueEventCount" resultMap="gdClueEventCountVoResultMap">
        select count(1) as total_count,
        COALESCE(sum(case when ce.dispose_user = #{userId} then 1 else 0 end), 0) as my_count
        COALESCE(sum(case when ce.dispose_user = #{userId} then 1 else 0 end), 0) as my_count,
        COALESCE(sum(case when ce.event_status = 2 then 1 else 0 end), 0) as returned_count,
        COALESCE(sum(case when ce.event_status = 3 then 1 else 0 end), 0) as disposed_count,
        COALESCE(sum(case when ce.event_status = 4 then 1 else 0 end), 0) as completed_count
        from ja_gd_clue_event ce
        <include refid="gdClueEventDeptWhere"/>
        and ce.event_status in (1, 3)
        and ce.event_status in (0, 1, 2, 3, 4)
        <if test="keyword != null and keyword != ''">
            and ce.event_num like concat('%', #{keyword}, '%')
        </if>
drone-service/drone-gd/src/main/java/org/sxkj/gd/workorder/mapper/GdClueEventStatusFlowMapper.java
New file
@@ -0,0 +1,45 @@
/*
 *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice,
 *  this list of conditions and the following disclaimer.
 *  Redistributions in binary form must reproduce the above copyright
 *  notice, this list of conditions and the following disclaimer in the
 *  documentation and/or other materials provided with the distribution.
 *  Neither the name of the dreamlu.net developer nor the names of its
 *  contributors may be used to endorse or promote products derived from
 *  this software without specific prior written permission.
 *  Author: Chill 庄骞 (smallchill@163.com)
 */
package org.sxkj.gd.workorder.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
import org.sxkj.gd.workorder.entity.GdClueEventStatusFlowEntity;
import org.sxkj.gd.workorder.vo.GdClueEventStatusFlowVO;
import java.util.List;
/**
 * 事件状态流转履历表 Mapper 接口
 *
 * @author system
 * @since 2026-07-29
 */
public interface GdClueEventStatusFlowMapper extends BaseMapper<GdClueEventStatusFlowEntity> {
    /**
     * 根据事件ID查询状态流转记录列表
     * <p>
     * 按操作时间升序排列,用于时间线展示
     * </p>
     *
     * @param eventId 事件ID
     * @return 状态流转记录列表
     */
    List<GdClueEventStatusFlowVO> selectStatusFlowsByEventId(@Param("eventId") Long eventId);
}
drone-service/drone-gd/src/main/java/org/sxkj/gd/workorder/mapper/GdClueEventStatusFlowMapper.xml
New file
@@ -0,0 +1,61 @@
<?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="org.sxkj.gd.workorder.mapper.GdClueEventStatusFlowMapper">
    <!-- 通用查询映射结果 -->
    <resultMap id="statusFlowResultMap" type="org.sxkj.gd.workorder.entity.GdClueEventStatusFlowEntity">
        <result column="id" property="id"/>
        <result column="event_id" property="eventId"/>
        <result column="old_status" property="oldStatus"/>
        <result column="new_status" property="newStatus"/>
        <result column="operate_user" property="operateUser"/>
        <result column="operate_dept" property="operateDept"/>
        <result column="operate_content" property="operateContent"/>
        <result column="operate_time" property="operateTime"/>
        <result column="create_user" property="createUser"/>
        <result column="create_dept" property="createDept"/>
        <result column="create_time" property="createTime"/>
        <result column="update_user" property="updateUser"/>
        <result column="update_time" property="updateTime"/>
        <result column="status" property="status"/>
        <result column="is_deleted" property="isDeleted"/>
    </resultMap>
    <!-- 状态流转VO结果映射 -->
    <resultMap id="statusFlowVoResultMap" type="org.sxkj.gd.workorder.vo.GdClueEventStatusFlowVO">
        <result column="id" property="id"/>
        <result column="event_id" property="eventId"/>
        <result column="old_status" property="oldStatus"/>
        <result column="new_status" property="newStatus"/>
        <result column="operate_user" property="operateUser"/>
        <result column="operate_user_name" property="operateUserName"/>
        <result column="operate_dept" property="operateDept"/>
        <result column="operate_dept_name" property="operateDeptName"/>
        <result column="operate_content" property="operateContent"/>
        <result column="operate_time" property="operateTime"/>
        <result column="create_time" property="createTime"/>
    </resultMap>
    <!-- 根据事件ID查询状态流转记录列表 -->
    <select id="selectStatusFlowsByEventId" resultMap="statusFlowVoResultMap">
        SELECT
            f.id,
            f.event_id,
            f.old_status,
            f.new_status,
            f.operate_user,
            f.operate_dept,
            f.operate_content,
            f.operate_time,
            f.create_time,
            u.real_name AS operate_user_name,
            d.full_name AS operate_dept_name
        FROM jagzw_yw.ja_gd_clue_event_status_flow f
        LEFT JOIN blade_user u ON f.operate_user = u.id AND u.is_deleted = 0
        LEFT JOIN blade_dept d ON f.operate_dept = d.id AND d.is_deleted = 0
        WHERE f.event_id = #{eventId}
          AND f.is_deleted = 0
        ORDER BY f.operate_time ASC
    </select>
</mapper>
drone-service/drone-gd/src/main/java/org/sxkj/gd/workorder/param/GdClueDisposeRecordAddParam.java
New file
@@ -0,0 +1,96 @@
/*
 *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice,
 *  this list of conditions and the following disclaimer.
 *  Redistributions in binary form must reproduce the above copyright
 *  notice, this list of conditions and the following disclaimer in the
 *  documentation and/or other materials provided with the distribution.
 *  Neither the name of the dreamlu.net developer nor the names of its
 *  contributors may be used to endorse or promote products derived from
 *  this software without specific prior written permission.
 *  Author: Chill 庄骞 (smallchill@163.com)
 */
package org.sxkj.gd.workorder.param;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotNull;
import java.util.Date;
import java.util.List;
/**
 * 事件处置记录新增参数
 * <p>
 * 用于新增处置记录,可同步触发事件状态变更
 * </p>
 *
 * @author system
 * @since 2026-07-29
 */
@Data
@ApiModel(value = "GdClueDisposeRecordAddParam对象", description = "事件处置记录新增参数")
public class GdClueDisposeRecordAddParam {
    /**
     * 关联事件ID(必填)
     */
    @NotNull(message = "事件ID不能为空")
    @ApiModelProperty(value = "关联事件ID", required = true)
    private Long eventId;
    /**
     * 记录类型:0核查记录、1处置记录(必填)
     */
    @NotNull(message = "记录类型不能为空")
    @ApiModelProperty(value = "记录类型:0核查记录、1处置记录", required = true)
    private Integer recordType;
    /**
     * 处置文字说明
     */
    @ApiModelProperty(value = "处置文字说明")
    private String disposeContent;
    /**
     * 现场附件图片URL数组
     */
    @ApiModelProperty(value = "现场附件图片URL数组")
    private List<String> attachUrl;
    /**
     * 实际操作时间(必填)
     */
    @NotNull(message = "操作时间不能为空")
    @ApiModelProperty(value = "实际操作时间", required = true)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date operateTime;
    /**
     * 是否同步更新事件状态
     * <p>
     * true: 提交处置内容时,自动将事件状态更新为处置中、已处置或已完成
     * false: 仅新增处置记录,不更新事件状态
     * </p>
     */
    @ApiModelProperty(value = "是否同步更新事件状态")
    private Boolean updateEventStatus;
    /**
     * 目标事件状态(当updateEventStatus=true时必填)
     * <p>
     * 1: 处置中
     * 3: 已处置
     * 4: 已完成
     * </p>
     */
    @ApiModelProperty(value = "目标事件状态(当updateEventStatus=true时必填)")
    private Integer targetEventStatus;
}
drone-service/drone-gd/src/main/java/org/sxkj/gd/workorder/param/GdClueEventStatusUpdateParam.java
New file
@@ -0,0 +1,74 @@
/*
 *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice,
 *  this list of conditions and the following disclaimer.
 *  Redistributions in binary form must reproduce the above copyright
 *  notice, this list of conditions and the following disclaimer in the
 *  documentation and/or other materials provided with the distribution.
 *  Neither the name of the dreamlu.net developer nor the names of its
 *  contributors may be used to endorse or promote products derived from
 *  this software without specific prior written permission.
 *  Author: Chill 庄骞 (smallchill@163.com)
 */
package org.sxkj.gd.workorder.param;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotNull;
import java.util.Date;
/**
 * 事件状态变更参数
 * <p>
 * 用于更新事件状态,必须符合状态流转矩阵规则
 * </p>
 *
 * @author system
 * @since 2026-07-29
 */
@Data
@ApiModel(value = "GdClueEventStatusUpdateParam对象", description = "事件状态变更参数")
public class GdClueEventStatusUpdateParam {
    /**
     * 事件ID(必填)
     */
    @NotNull(message = "事件ID不能为空")
    @ApiModelProperty(value = "事件ID", required = true)
    private Long eventId;
    /**
     * 目标状态(必填)
     * <p>
     * 0: 待确认
     * 1: 处置中
     * 2: 已退回
     * 3: 已处置
     * 4: 已完成
     * </p>
     */
    @NotNull(message = "目标状态不能为空")
    @ApiModelProperty(value = "目标状态:0待确认、1处置中、2已退回、3已处置、4已完成", required = true)
    private Integer targetStatus;
    /**
     * 状态变更备注说明
     */
    @ApiModelProperty(value = "状态变更备注说明")
    private String operateContent;
    /**
     * 操作时间(可选,不填则使用当前时间)
     */
    @ApiModelProperty(value = "操作时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date operateTime;
}
drone-service/drone-gd/src/main/java/org/sxkj/gd/workorder/service/IGdClueDisposeRecordService.java
New file
@@ -0,0 +1,56 @@
/*
 *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice,
 *  this list of conditions and the following disclaimer.
 *  Redistributions in binary form must reproduce the above copyright
 *  notice, this list of conditions and the following disclaimer in the
 *  documentation and/or other materials provided with the distribution.
 *  Neither the name of the dreamlu.net developer nor the names of its
 *  contributors may be used to endorse or promote products derived from
 *  this software without specific prior written permission.
 *  Author: Chill 庄骞 (smallchill@163.com)
 */
package org.sxkj.gd.workorder.service;
import org.sxkj.gd.workorder.entity.GdClueDisposeRecordEntity;
import org.sxkj.gd.workorder.param.GdClueDisposeRecordAddParam;
import org.sxkj.gd.workorder.vo.GdClueDisposeRecordVO;
import org.springblade.core.mp.base.BaseService;
import java.util.List;
/**
 * 事件处置流水记录表 服务类
 *
 * @author system
 * @since 2026-07-29
 */
public interface IGdClueDisposeRecordService extends BaseService<GdClueDisposeRecordEntity> {
    /**
     * 根据事件ID查询处置记录列表
     * <p>
     * 按操作时间升序排列
     * </p>
     *
     * @param eventId 事件ID
     * @return 处置记录列表
     */
    List<GdClueDisposeRecordVO> getDisposeRecordsByEventId(Long eventId);
    /**
     * 新增处置记录
     * <p>
     * 支持同步更新事件状态
     * </p>
     *
     * @param addParam 新增参数
     * @return 是否成功
     */
    boolean addDisposeRecord(GdClueDisposeRecordAddParam addParam);
}
drone-service/drone-gd/src/main/java/org/sxkj/gd/workorder/service/IGdClueEventService.java
@@ -20,6 +20,7 @@
import org.sxkj.gd.workorder.entity.GdClueEventEntity;
import org.sxkj.gd.workorder.param.GdClueEventDistributeParam;
import org.sxkj.gd.workorder.param.GdClueEventRejectParam;
import org.sxkj.gd.workorder.param.GdClueEventStatusUpdateParam;
import org.sxkj.gd.workorder.vo.GdClueEventCountVO;
import org.sxkj.gd.workorder.vo.GdClueEventListVO;
import org.sxkj.gd.workorder.vo.GdClueEventVO;
@@ -111,4 +112,15 @@
     */
    boolean saveOrUpdateExternal(GdClueEventEntity gdClueEvent);
    /**
     * 更新事件状态
     * <p>
     * 必须符合状态流转矩阵规则,并插入状态流转记录
     * </p>
     *
     * @param updateParam 状态更新参数
     * @return 是否操作成功
     */
    boolean updateEventStatus(GdClueEventStatusUpdateParam updateParam);
}
drone-service/drone-gd/src/main/java/org/sxkj/gd/workorder/service/IGdClueEventStatusFlowService.java
New file
@@ -0,0 +1,59 @@
/*
 *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice,
 *  this list of conditions and the following disclaimer.
 *  Redistributions in binary form must reproduce the above copyright
 *  notice, this list of conditions and the following disclaimer in the
 *  documentation and/or other materials provided with the distribution.
 *  Neither the name of the dreamlu.net developer nor the names of its
 *  contributors may be used to endorse or promote products derived from
 *  this software without specific prior written permission.
 *  Author: Chill 庄骞 (smallchill@163.com)
 */
package org.sxkj.gd.workorder.service;
import org.sxkj.gd.workorder.entity.GdClueEventStatusFlowEntity;
import org.sxkj.gd.workorder.vo.GdClueEventStatusFlowVO;
import org.springblade.core.mp.base.BaseService;
import java.util.List;
/**
 * 事件状态流转履历表 服务类
 *
 * @author system
 * @since 2026-07-29
 */
public interface IGdClueEventStatusFlowService extends BaseService<GdClueEventStatusFlowEntity> {
    /**
     * 根据事件ID查询状态流转记录列表
     * <p>
     * 按操作时间升序排列,用于时间线展示
     * </p>
     *
     * @param eventId 事件ID
     * @return 状态流转记录列表
     */
    List<GdClueEventStatusFlowVO> getStatusFlowsByEventId(Long eventId);
    /**
     * 插入状态流转记录
     * <p>
     * 仅用于内部调用,不允许外部修改
     * </p>
     *
     * @param eventId       事件ID
     * @param oldStatus     变更前状态
     * @param newStatus     变更后状态
     * @param operateContent 操作备注
     * @param operateTime   操作时间
     * @return 是否成功
     */
    boolean insertStatusFlow(Long eventId, Integer oldStatus, Integer newStatus, String operateContent, java.util.Date operateTime);
}
drone-service/drone-gd/src/main/java/org/sxkj/gd/workorder/service/impl/GdClueDisposeRecordServiceImpl.java
New file
@@ -0,0 +1,179 @@
/*
 *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice,
 *  this list of conditions and the following disclaimer.
 *  Redistributions in binary form must reproduce the above copyright
 *  notice, this list of conditions and the following disclaimer in the
 *  documentation and/or other materials provided with the distribution.
 *  Neither the name of the dreamlu.net developer nor the names of its
 *  contributors may be used to endorse or promote products derived from
 *  this software without specific prior written permission.
 *  Author: Chill 庄骞 (smallchill@163.com)
 */
package org.sxkj.gd.workorder.service.impl;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.secure.utils.AuthUtil;
import org.springblade.core.tool.utils.Func;
import org.sxkj.gd.workorder.entity.GdClueDisposeRecordEntity;
import org.sxkj.gd.workorder.entity.GdClueEventEntity;
import org.sxkj.gd.workorder.enums.EventStatusEnum;
import org.sxkj.gd.workorder.mapper.GdClueDisposeRecordMapper;
import org.sxkj.gd.workorder.param.GdClueDisposeRecordAddParam;
import org.sxkj.gd.workorder.service.IGdClueDisposeRecordService;
import org.sxkj.gd.workorder.service.IGdClueEventService;
import org.sxkj.gd.workorder.service.IGdClueEventStatusFlowService;
import org.sxkj.gd.workorder.vo.GdClueDisposeRecordVO;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springblade.core.mp.base.BaseServiceImpl;
import java.util.Date;
import java.util.List;
/**
 * 事件处置流水记录表 服务实现类
 *
 * @author system
 * @since 2026-07-29
 */
@Slf4j
@Service
@AllArgsConstructor
public class GdClueDisposeRecordServiceImpl extends BaseServiceImpl<GdClueDisposeRecordMapper, GdClueDisposeRecordEntity> implements IGdClueDisposeRecordService {
    private final IGdClueEventService gdClueEventService;
    private final IGdClueEventStatusFlowService gdClueEventStatusFlowService;
    /**
     * 根据事件ID查询处置记录列表
     * <p>
     * 步骤:
     * 1. 参数校验
     * 2. 查询处置记录列表
     * 3. 补充状态名称
     * </p>
     *
     * @param eventId 事件ID
     * @return 处置记录列表
     */
    @Override
    public List<GdClueDisposeRecordVO> getDisposeRecordsByEventId(Long eventId) {
        // 1. 参数校验
        if (eventId == null) {
            throw new RuntimeException("事件ID不能为空");
        }
        // 2. 查询处置记录列表
        List<GdClueDisposeRecordVO> records = baseMapper.selectDisposeRecordsByEventId(eventId);
        // 3. 补充状态名称和类型名称
        if (Func.isNotEmpty(records)) {
            records.forEach(record -> {
                // 补充记录类型名称
                if (record.getRecordType() != null) {
                    record.setRecordTypeName(record.getRecordType() == 0 ? "核查记录" : "处置记录");
                }
                // 补充处置状态名称
                if (record.getDisposeStatus() != null) {
                    record.setDisposeStatusName(EventStatusEnum.getNameByCode(record.getDisposeStatus()));
                }
            });
        }
        return records;
    }
    /**
     * 新增处置记录
     * <p>
     * 步骤:
     * 1. 参数校验
     * 2. 查询事件信息
     * 3. 如果需要同步更新事件状态,则更新事件状态
     * 4. 构建处置记录实体
     * 5. 保存处置记录
     * </p>
     *
     * @param addParam 新增参数
     * @return 是否成功
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean addDisposeRecord(GdClueDisposeRecordAddParam addParam) {
        // 1. 参数校验
        if (addParam == null || addParam.getEventId() == null) {
            throw new RuntimeException("参数不能为空");
        }
        if (addParam.getRecordType() == null) {
            throw new RuntimeException("记录类型不能为空");
        }
        if (addParam.getOperateTime() == null) {
            throw new RuntimeException("操作时间不能为空");
        }
        // 2. 查询事件信息
        GdClueEventEntity eventEntity = gdClueEventService.getById(addParam.getEventId());
        if (eventEntity == null || (eventEntity.getIsDeleted() != null && eventEntity.getIsDeleted() != 0)) {
            throw new RuntimeException("事件不存在");
        }
        Integer oldStatus = eventEntity.getEventStatus();
        // 3. 如果需要同步更新事件状态,则更新事件状态
        if (addParam.getUpdateEventStatus() != null && addParam.getUpdateEventStatus()
                && addParam.getTargetEventStatus() != null) {
            // 校验目标状态合法性
            if (!EventStatusEnum.isValidCode(addParam.getTargetEventStatus())) {
                throw new RuntimeException("目标状态编码无效");
            }
            // 校验状态流转合法性
            if (!EventStatusEnum.isValidTransition(oldStatus, addParam.getTargetEventStatus())) {
                String oldStatusName = EventStatusEnum.getNameByCode(oldStatus);
                String newStatusName = EventStatusEnum.getNameByCode(addParam.getTargetEventStatus());
                throw new RuntimeException(String.format("状态流转非法:不允许从[%s]流转到[%s]", oldStatusName, newStatusName));
            }
            // 更新事件状态
            eventEntity.setEventStatus(addParam.getTargetEventStatus());
            eventEntity.setUpdateUser(AuthUtil.getUserId());
            eventEntity.setUpdateTime(new Date());
            if (!gdClueEventService.updateById(eventEntity)) {
                throw new RuntimeException("更新事件状态失败");
            }
            // 插入状态流转记录
            gdClueEventStatusFlowService.insertStatusFlow(
                    addParam.getEventId(),
                    oldStatus,
                    addParam.getTargetEventStatus(),
                    addParam.getDisposeContent(),
                    addParam.getOperateTime()
            );
        }
        // 4. 构建处置记录实体
        GdClueDisposeRecordEntity recordEntity = new GdClueDisposeRecordEntity();
        recordEntity.setEventId(addParam.getEventId());
        recordEntity.setRecordType(addParam.getRecordType());
        recordEntity.setDisposeContent(addParam.getDisposeContent());
        recordEntity.setAttachUrl(addParam.getAttachUrl());
        recordEntity.setOperateTime(addParam.getOperateTime());
        recordEntity.setDisposeStatus(eventEntity.getEventStatus());
        recordEntity.setDisposeUser(AuthUtil.getUserId());
        recordEntity.setDisposeDept(Long.valueOf(AuthUtil.getDeptId()));
        recordEntity.setCreateUser(AuthUtil.getUserId());
        recordEntity.setCreateDept(Long.valueOf(AuthUtil.getDeptId()));
        recordEntity.setCreateTime(new Date());
        // 5. 保存处置记录
        return save(recordEntity);
    }
}
drone-service/drone-gd/src/main/java/org/sxkj/gd/workorder/service/impl/GdClueEventServiceImpl.java
@@ -26,11 +26,14 @@
import org.sxkj.common.utils.OrderNumUtils;
import org.sxkj.gd.workorder.entity.GdClueEventEntity;
import org.sxkj.gd.workorder.entity.GdTaskResultEntity;
import org.sxkj.gd.workorder.enums.EventStatusEnum;
import org.sxkj.gd.workorder.excel.GdClueEventExcel;
import org.sxkj.gd.workorder.mapper.GdClueEventMapper;
import org.sxkj.gd.workorder.param.GdClueEventDistributeParam;
import org.sxkj.gd.workorder.param.GdClueEventRejectParam;
import org.sxkj.gd.workorder.param.GdClueEventStatusUpdateParam;
import org.sxkj.gd.workorder.service.IGdClueEventService;
import org.sxkj.gd.workorder.service.IGdClueEventStatusFlowService;
import org.sxkj.gd.workorder.service.IGdTaskResultService;
import org.sxkj.gd.workorder.vo.GdClueEventCountVO;
import org.sxkj.gd.workorder.vo.GdClueEventListVO;
@@ -55,6 +58,7 @@
public class GdClueEventServiceImpl extends BaseServiceImpl<GdClueEventMapper, GdClueEventEntity> implements IGdClueEventService {
    private final IGdTaskResultService gdTaskResultService;
    private final IGdClueEventStatusFlowService gdClueEventStatusFlowService;
    @Override
    public IPage<GdClueEventVO> selectGdClueEventPage(IPage<GdClueEventVO> page, GdClueEventVO gdClueEvent) {
@@ -94,15 +98,25 @@
    @Override
    public GdClueEventCountVO getGdClueEventCount(String keyword) {
        // 1. 获取当前用户部门ID和用户ID
        Long deptId = Long.valueOf(AuthUtil.getDeptId());
        Long userId = AuthUtil.getUserId();
        // 2. 查询统计数据
        GdClueEventCountVO countVO = baseMapper.selectGdClueEventCount(deptId, userId, keyword);
        // 3. 处理空结果,返回默认值
        if (countVO == null) {
            GdClueEventCountVO empty = new GdClueEventCountVO();
            empty.setTotalCount(0L);
            empty.setMyCount(0L);
            empty.setReturnedCount(0L);
            empty.setDisposedCount(0L);
            empty.setCompletedCount(0L);
            return empty;
        }
        // 4. 返回统计结果
        return countVO;
    }
@@ -217,4 +231,69 @@
        return saveOrUpdate(gdClueEvent);
    }
    /**
     * 更新事件状态
     * <p>
     * 步骤:
     * 1. 参数校验
     * 2. 查询事件信息
     * 3. 校验状态流转合法性
     * 4. 更新事件状态
     * 5. 插入状态流转记录
     * </p>
     *
     * @param updateParam 状态更新参数
     * @return 是否操作成功
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateEventStatus(GdClueEventStatusUpdateParam updateParam) {
        // 1. 参数校验
        if (updateParam == null || updateParam.getEventId() == null) {
            throw new RuntimeException("参数不能为空");
        }
        if (updateParam.getTargetStatus() == null) {
            throw new RuntimeException("目标状态不能为空");
        }
        // 2. 查询事件信息
        GdClueEventEntity eventEntity = getById(updateParam.getEventId());
        if (eventEntity == null || (eventEntity.getIsDeleted() != null && eventEntity.getIsDeleted() != 0)) {
            throw new RuntimeException("事件不存在");
        }
        Integer oldStatus = eventEntity.getEventStatus();
        Integer targetStatus = updateParam.getTargetStatus();
        // 3. 校验状态流转合法性
        if (!EventStatusEnum.isValidCode(targetStatus)) {
            throw new RuntimeException("目标状态编码无效");
        }
        if (!EventStatusEnum.isValidTransition(oldStatus, targetStatus)) {
            String oldStatusName = EventStatusEnum.getNameByCode(oldStatus);
            String newStatusName = EventStatusEnum.getNameByCode(targetStatus);
            throw new RuntimeException(String.format("状态流转非法:不允许从[%s]流转到[%s]", oldStatusName, newStatusName));
        }
        // 4. 更新事件状态
        eventEntity.setEventStatus(targetStatus);
        eventEntity.setUpdateUser(AuthUtil.getUserId());
        eventEntity.setUpdateTime(new Date());
        if (!updateById(eventEntity)) {
            throw new RuntimeException("更新事件状态失败");
        }
        // 5. 插入状态流转记录
        gdClueEventStatusFlowService.insertStatusFlow(
                updateParam.getEventId(),
                oldStatus,
                targetStatus,
                updateParam.getOperateContent(),
                updateParam.getOperateTime() != null ? updateParam.getOperateTime() : new Date()
        );
        return true;
    }
}
drone-service/drone-gd/src/main/java/org/sxkj/gd/workorder/service/impl/GdClueEventStatusFlowServiceImpl.java
New file
@@ -0,0 +1,126 @@
/*
 *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice,
 *  this list of conditions and the following disclaimer.
 *  Redistributions in binary form must reproduce the above copyright
 *  notice, this list of conditions and the following disclaimer in the
 *  documentation and/or other materials provided with the distribution.
 *  Neither the name of the dreamlu.net developer nor the names of its
 *  contributors may be used to endorse or promote products derived from
 *  this software without specific prior written permission.
 *  Author: Chill 庄骞 (smallchill@163.com)
 */
package org.sxkj.gd.workorder.service.impl;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.secure.utils.AuthUtil;
import org.sxkj.gd.workorder.entity.GdClueEventStatusFlowEntity;
import org.sxkj.gd.workorder.enums.EventStatusEnum;
import org.sxkj.gd.workorder.mapper.GdClueEventStatusFlowMapper;
import org.sxkj.gd.workorder.service.IGdClueEventStatusFlowService;
import org.sxkj.gd.workorder.vo.GdClueEventStatusFlowVO;
import org.springframework.stereotype.Service;
import org.springblade.core.mp.base.BaseServiceImpl;
import java.util.Date;
import java.util.List;
/**
 * 事件状态流转履历表 服务实现类
 *
 * @author system
 * @since 2026-07-29
 */
@Slf4j
@Service
@AllArgsConstructor
public class GdClueEventStatusFlowServiceImpl extends BaseServiceImpl<GdClueEventStatusFlowMapper, GdClueEventStatusFlowEntity> implements IGdClueEventStatusFlowService {
    /**
     * 根据事件ID查询状态流转记录列表
     * <p>
     * 步骤:
     * 1. 参数校验
     * 2. 查询状态流转记录列表
     * 3. 补充状态名称
     * </p>
     *
     * @param eventId 事件ID
     * @return 状态流转记录列表
     */
    @Override
    public List<GdClueEventStatusFlowVO> getStatusFlowsByEventId(Long eventId) {
        // 1. 参数校验
        if (eventId == null) {
            throw new RuntimeException("事件ID不能为空");
        }
        // 2. 查询状态流转记录列表
        List<GdClueEventStatusFlowVO> flows = baseMapper.selectStatusFlowsByEventId(eventId);
        // 3. 补充状态名称
        if (flows != null && !flows.isEmpty()) {
            flows.forEach(flow -> {
                // 补充变更前状态名称
                if (flow.getOldStatus() != null) {
                    flow.setOldStatusName(EventStatusEnum.getNameByCode(flow.getOldStatus()));
                }
                // 补充变更后状态名称
                if (flow.getNewStatus() != null) {
                    flow.setNewStatusName(EventStatusEnum.getNameByCode(flow.getNewStatus()));
                }
            });
        }
        return flows;
    }
    /**
     * 插入状态流转记录
     * <p>
     * 步骤:
     * 1. 参数校验
     * 2. 构建状态流转记录实体
     * 3. 保存状态流转记录
     * </p>
     *
     * @param eventId        事件ID
     * @param oldStatus      变更前状态
     * @param newStatus      变更后状态
     * @param operateContent 操作备注
     * @param operateTime    操作时间
     * @return 是否成功
     */
    @Override
    public boolean insertStatusFlow(Long eventId, Integer oldStatus, Integer newStatus, String operateContent, Date operateTime) {
        // 1. 参数校验
        if (eventId == null) {
            throw new RuntimeException("事件ID不能为空");
        }
        if (newStatus == null) {
            throw new RuntimeException("目标状态不能为空");
        }
        // 2. 构建状态流转记录实体
        GdClueEventStatusFlowEntity flowEntity = new GdClueEventStatusFlowEntity();
        flowEntity.setEventId(eventId);
        flowEntity.setOldStatus(oldStatus);
        flowEntity.setNewStatus(newStatus);
        flowEntity.setOperateContent(operateContent);
        flowEntity.setOperateTime(operateTime != null ? operateTime : new Date());
        flowEntity.setOperateUser(AuthUtil.getUserId());
        flowEntity.setOperateDept(Long.valueOf(AuthUtil.getDeptId()));
        flowEntity.setCreateUser(AuthUtil.getUserId());
        flowEntity.setCreateDept(Long.valueOf(AuthUtil.getDeptId()));
        flowEntity.setCreateTime(new Date());
        // 3. 保存状态流转记录
        return save(flowEntity);
    }
}
drone-service/drone-gd/src/main/java/org/sxkj/gd/workorder/vo/GdClueDisposeRecordVO.java
New file
@@ -0,0 +1,126 @@
/*
 *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice,
 *  this list of conditions and the following disclaimer.
 *  Redistributions in binary form must reproduce the above copyright
 *  notice, this list of conditions and the following disclaimer in the
 *  documentation and/or other materials provided with the distribution.
 *  Neither the name of the dreamlu.net developer nor the names of its
 *  contributors may be used to endorse or promote products derived from
 *  this software without specific prior written permission.
 *  Author: Chill 庄骞 (smallchill@163.com)
 */
package org.sxkj.gd.workorder.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
import java.util.List;
/**
 * 事件处置流水记录表 视图实体类
 * <p>
 * 用于展示事件处置记录信息,包括核查记录和处置记录
 * </p>
 *
 * @author system
 * @since 2026-07-29
 */
@Data
@ApiModel(value = "GdClueDisposeRecordVO对象", description = "事件处置流水记录表")
public class GdClueDisposeRecordVO {
    /**
     * 记录ID(主键)
     */
    @ApiModelProperty(value = "记录ID")
    private Long id;
    /**
     * 关联事件ID
     */
    @ApiModelProperty(value = "关联事件ID")
    private Long eventId;
    /**
     * 记录类型:0核查记录、1处置记录
     */
    @ApiModelProperty(value = "记录类型:0核查记录、1处置记录")
    private Integer recordType;
    /**
     * 记录类型名称
     */
    @ApiModelProperty(value = "记录类型名称")
    private String recordTypeName;
    /**
     * 处置操作人
     */
    @ApiModelProperty(value = "处置操作人")
    private Long disposeUser;
    /**
     * 处置操作人姓名
     */
    @ApiModelProperty(value = "处置操作人姓名")
    private String disposeUserName;
    /**
     * 处置操作部门
     */
    @ApiModelProperty(value = "处置操作部门")
    private Long disposeDept;
    /**
     * 处置操作部门名称
     */
    @ApiModelProperty(value = "处置操作部门名称")
    private String disposeDeptName;
    /**
     * 处置状态:0待确认、1处置中、2已退回、3已处置、4已完成
     */
    @ApiModelProperty(value = "处置状态")
    private Integer disposeStatus;
    /**
     * 处置状态名称
     */
    @ApiModelProperty(value = "处置状态名称")
    private String disposeStatusName;
    /**
     * 处置文字说明
     */
    @ApiModelProperty(value = "处置文字说明")
    private String disposeContent;
    /**
     * 现场附件图片URL数组
     */
    @ApiModelProperty(value = "现场附件图片URL数组")
    private List<String> attachUrl;
    /**
     * 实际操作时间
     */
    @ApiModelProperty(value = "实际操作时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date operateTime;
    /**
     * 记录创建时间
     */
    @ApiModelProperty(value = "记录创建时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date createTime;
}
drone-service/drone-gd/src/main/java/org/sxkj/gd/workorder/vo/GdClueEventCountVO.java
@@ -36,4 +36,13 @@
    @ApiModelProperty(value = "我的工单数量")
    private Long myCount;
    @ApiModelProperty(value = "已退回工单数量(状态2)")
    private Long returnedCount;
    @ApiModelProperty(value = "已处置工单数量(状态3)")
    private Long disposedCount;
    @ApiModelProperty(value = "已完成工单数量(状态4)")
    private Long completedCount;
}
drone-service/drone-gd/src/main/java/org/sxkj/gd/workorder/vo/GdClueEventDetailVO.java
New file
@@ -0,0 +1,56 @@
/*
 *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice,
 *  this list of conditions and the following disclaimer.
 *  Redistributions in binary form must reproduce the above copyright
 *  notice, this list of conditions and the following disclaimer in the
 *  documentation and/or other materials provided with the distribution.
 *  Neither the name of the dreamlu.net developer nor the names of its
 *  contributors may be used to endorse or promote products derived from
 *  this software without specific prior written permission.
 *  Author: Chill 庄骞 (smallchill@163.com)
 */
package org.sxkj.gd.workorder.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
/**
 * 事件详情VO
 * <p>
 * 包含事件基本信息、处置记录时间线和状态流转时间线
 * </p>
 *
 * @author system
 * @since 2026-07-29
 */
@Data
@ApiModel(value = "GdClueEventDetailVO对象", description = "事件详情VO")
public class GdClueEventDetailVO {
    /**
     * 事件基本信息
     */
    @ApiModelProperty(value = "事件基本信息")
    private GdClueEventVO eventInfo;
    /**
     * 处置记录时间线(按操作时间升序)
     */
    @ApiModelProperty(value = "处置记录时间线")
    private List<GdClueDisposeRecordVO> disposeRecords;
    /**
     * 状态流转时间线(按操作时间升序)
     */
    @ApiModelProperty(value = "状态流转时间线")
    private List<GdClueEventStatusFlowVO> statusFlows;
}
drone-service/drone-gd/src/main/java/org/sxkj/gd/workorder/vo/GdClueEventStatusFlowVO.java
New file
@@ -0,0 +1,119 @@
/*
 *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice,
 *  this list of conditions and the following disclaimer.
 *  Redistributions in binary form must reproduce the above copyright
 *  notice, this list of conditions and the following disclaimer in the
 *  documentation and/or other materials provided with the distribution.
 *  Neither the name of the dreamlu.net developer nor the names of its
 *  contributors may be used to endorse or promote products derived from
 *  this software without specific prior written permission.
 *  Author: Chill 庄骞 (smallchill@163.com)
 */
package org.sxkj.gd.workorder.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
/**
 * 事件状态流转履历表 视图实体类
 * <p>
 * 用于展示事件状态变更历史,仅用于审计
 * </p>
 *
 * @author system
 * @since 2026-07-29
 */
@Data
@ApiModel(value = "GdClueEventStatusFlowVO对象", description = "事件状态流转履历表")
public class GdClueEventStatusFlowVO {
    /**
     * 流转记录ID(主键)
     */
    @ApiModelProperty(value = "流转记录ID")
    private Long id;
    /**
     * 关联事件ID
     */
    @ApiModelProperty(value = "关联事件ID")
    private Long eventId;
    /**
     * 变更前状态
     */
    @ApiModelProperty(value = "变更前状态")
    private Integer oldStatus;
    /**
     * 变更前状态名称
     */
    @ApiModelProperty(value = "变更前状态名称")
    private String oldStatusName;
    /**
     * 变更后状态
     */
    @ApiModelProperty(value = "变更后状态")
    private Integer newStatus;
    /**
     * 变更后状态名称
     */
    @ApiModelProperty(value = "变更后状态名称")
    private String newStatusName;
    /**
     * 状态变更操作人
     */
    @ApiModelProperty(value = "状态变更操作人")
    private Long operateUser;
    /**
     * 状态变更操作人姓名
     */
    @ApiModelProperty(value = "状态变更操作人姓名")
    private String operateUserName;
    /**
     * 状态变更操作部门
     */
    @ApiModelProperty(value = "状态变更操作部门")
    private Long operateDept;
    /**
     * 状态变更操作部门名称
     */
    @ApiModelProperty(value = "状态变更操作部门名称")
    private String operateDeptName;
    /**
     * 状态变更备注说明
     */
    @ApiModelProperty(value = "状态变更备注说明")
    private String operateContent;
    /**
     * 状态变更时间
     */
    @ApiModelProperty(value = "状态变更时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date operateTime;
    /**
     * 记录创建时间
     */
    @ApiModelProperty(value = "记录创建时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date createTime;
}
drone-service/drone-gd/src/main/java/org/sxkj/gd/workorder/vo/GdClueEventVO.java
@@ -68,9 +68,9 @@
    @ApiModelProperty(value = "纬度")
    private Double latitude;
    /**
     * 状态:0未分发、1已分发、2已驳回、3已确认
     * 状态:0待确认、1处置中、2已退回、3已处置、4已完成
     */
    @ApiModelProperty(value = "状态:0未分发、1已分发、2已驳回、3已确认")
    @ApiModelProperty(value = "状态:0待确认、1处置中、2已退回、3已处置、4已完成")
    private Integer eventStatus;
    /**
     * 区域编码