diff --git a/src/main/java/com/chinaweal/youfool/prj/modules/evidence/transfer/controller/ClueTransferController.java b/src/main/java/com/chinaweal/youfool/prj/modules/evidence/transfer/controller/ClueTransferController.java index 16f356b..3346fee 100644 --- a/src/main/java/com/chinaweal/youfool/prj/modules/evidence/transfer/controller/ClueTransferController.java +++ b/src/main/java/com/chinaweal/youfool/prj/modules/evidence/transfer/controller/ClueTransferController.java @@ -3,8 +3,15 @@ package com.chinaweal.youfool.prj.modules.evidence.transfer.controller; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.chinaweal.youfool.framework.springboot.rest.RestResult; import com.chinaweal.youfool.prj.modules.evidence.transfer.entity.query.ClueTransferQuery; +import com.chinaweal.youfool.prj.modules.evidence.transfer.entity.query.PendingClueQuery; import com.chinaweal.youfool.prj.modules.evidence.transfer.entity.req.ClueTransferReq; +import com.chinaweal.youfool.prj.modules.evidence.transfer.entity.req.TransferStatusUpdateReq; +import com.chinaweal.youfool.prj.modules.evidence.transfer.entity.req.TransferUrgeReq; +import com.chinaweal.youfool.prj.modules.evidence.transfer.entity.req.TransferWithdrawReq; import com.chinaweal.youfool.prj.modules.evidence.transfer.entity.vo.ClueTransferDetailVO; +import com.chinaweal.youfool.prj.modules.evidence.transfer.entity.vo.OperationLogVO; +import com.chinaweal.youfool.prj.modules.evidence.transfer.entity.vo.PendingClueDetailVO; +import com.chinaweal.youfool.prj.modules.evidence.transfer.entity.vo.PendingClueVO; import com.chinaweal.youfool.prj.modules.evidence.transfer.service.IClueTransferService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; @@ -92,4 +99,40 @@ public class ClueTransferController { public RestResult> disposalFeedback(String clueId) { return clueTransferService.disposalFeedback(clueId); } + + @PostMapping("pending-clues/query") + @Operation(summary = "分页查询待转办线索列表") + public RestResult> queryPendingClues(PendingClueQuery query) { + return clueTransferService.queryPendingClues(query); + } + + @GetMapping("pending-clues/detail") + @Operation(summary = "获取待转办线索详情") + public RestResult getPendingClueDetail(String clueId) { + return clueTransferService.getPendingClueDetail(clueId); + } + + @GetMapping("operation-logs") + @Operation(summary = "获取转办操作日志") + public RestResult> getOperationLogs(String transferRecordId) { + return clueTransferService.getOperationLogs(transferRecordId); + } + + @PostMapping("status-update") + @Operation(summary = "更新转办状态(外部系统回调)") + public RestResult updateTransferStatus(TransferStatusUpdateReq req) { + return clueTransferService.updateTransferStatus(req); + } + + @PostMapping("urge") + @Operation(summary = "转办催办") + public RestResult urgeTransfer(TransferUrgeReq req) { + return clueTransferService.urgeTransfer(req); + } + + @PostMapping("withdraw") + @Operation(summary = "转办撤回") + public RestResult withdrawTransfer(TransferWithdrawReq req) { + return clueTransferService.withdrawTransfer(req); + } } diff --git a/src/main/java/com/chinaweal/youfool/prj/modules/evidence/transfer/entity/query/PendingClueQuery.java b/src/main/java/com/chinaweal/youfool/prj/modules/evidence/transfer/entity/query/PendingClueQuery.java new file mode 100644 index 0000000..4400248 --- /dev/null +++ b/src/main/java/com/chinaweal/youfool/prj/modules/evidence/transfer/entity/query/PendingClueQuery.java @@ -0,0 +1,25 @@ +package com.chinaweal.youfool.prj.modules.evidence.transfer.entity.query; + +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * 待转办线索查询条件 + * + * @author chenjy + * @since 2026/05/18 + */ +@Data +@Accessors(chain = true) +public class PendingClueQuery { + + private Integer pageNum = 1; + + private Integer pageSize = 10; + + private String clueCode; + + private String district; + + private String keyword; +} diff --git a/src/main/java/com/chinaweal/youfool/prj/modules/evidence/transfer/entity/req/TransferStatusUpdateReq.java b/src/main/java/com/chinaweal/youfool/prj/modules/evidence/transfer/entity/req/TransferStatusUpdateReq.java new file mode 100644 index 0000000..0f3c35e --- /dev/null +++ b/src/main/java/com/chinaweal/youfool/prj/modules/evidence/transfer/entity/req/TransferStatusUpdateReq.java @@ -0,0 +1,25 @@ +package com.chinaweal.youfool.prj.modules.evidence.transfer.entity.req; + +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * 转办状态更新请求 + * + * @author chenjy + * @since 2026/05/18 + */ +@Data +@Accessors(chain = true) +public class TransferStatusUpdateReq { + + private String transferRecordId; + + private String newStatus; + + private String externalClueId; + + private String disposalResult; + + private String remark; +} diff --git a/src/main/java/com/chinaweal/youfool/prj/modules/evidence/transfer/entity/req/TransferUrgeReq.java b/src/main/java/com/chinaweal/youfool/prj/modules/evidence/transfer/entity/req/TransferUrgeReq.java new file mode 100644 index 0000000..91cbcbe --- /dev/null +++ b/src/main/java/com/chinaweal/youfool/prj/modules/evidence/transfer/entity/req/TransferUrgeReq.java @@ -0,0 +1,19 @@ +package com.chinaweal.youfool.prj.modules.evidence.transfer.entity.req; + +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * 转办催办请求 + * + * @author chenjy + * @since 2026/05/18 + */ +@Data +@Accessors(chain = true) +public class TransferUrgeReq { + + private String transferRecordId; + + private String urgeMessage; +} diff --git a/src/main/java/com/chinaweal/youfool/prj/modules/evidence/transfer/entity/req/TransferWithdrawReq.java b/src/main/java/com/chinaweal/youfool/prj/modules/evidence/transfer/entity/req/TransferWithdrawReq.java new file mode 100644 index 0000000..0c64291 --- /dev/null +++ b/src/main/java/com/chinaweal/youfool/prj/modules/evidence/transfer/entity/req/TransferWithdrawReq.java @@ -0,0 +1,19 @@ +package com.chinaweal.youfool.prj.modules.evidence.transfer.entity.req; + +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * 转办撤回请求 + * + * @author chenjy + * @since 2026/05/18 + */ +@Data +@Accessors(chain = true) +public class TransferWithdrawReq { + + private String transferRecordId; + + private String withdrawReason; +} diff --git a/src/main/java/com/chinaweal/youfool/prj/modules/evidence/transfer/entity/vo/OperationLogVO.java b/src/main/java/com/chinaweal/youfool/prj/modules/evidence/transfer/entity/vo/OperationLogVO.java new file mode 100644 index 0000000..56c0622 --- /dev/null +++ b/src/main/java/com/chinaweal/youfool/prj/modules/evidence/transfer/entity/vo/OperationLogVO.java @@ -0,0 +1,32 @@ +package com.chinaweal.youfool.prj.modules.evidence.transfer.entity.vo; + +import com.chinaweal.youfool.framework.springboot.common.util.DateUtil; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.time.LocalDateTime; + +/** + * 转办操作日志 VO + * + * @author chenjy + * @since 2026/05/18 + */ +@Data +public class OperationLogVO { + + private String id; + + private String operationType; + + private String operationDetail; + + private String fromStatus; + + private String toStatus; + + private String operator; + + @JsonFormat(pattern = DateUtil.DATETIME_DEFAULT_FORMAT, timezone = "GMT+8") + private LocalDateTime operatedAt; +} diff --git a/src/main/java/com/chinaweal/youfool/prj/modules/evidence/transfer/entity/vo/PendingClueDetailVO.java b/src/main/java/com/chinaweal/youfool/prj/modules/evidence/transfer/entity/vo/PendingClueDetailVO.java new file mode 100644 index 0000000..39d232a --- /dev/null +++ b/src/main/java/com/chinaweal/youfool/prj/modules/evidence/transfer/entity/vo/PendingClueDetailVO.java @@ -0,0 +1,37 @@ +package com.chinaweal.youfool.prj.modules.evidence.transfer.entity.vo; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 待转办线索详情 VO + * + * @author chenjy + * @since 2026/05/18 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class PendingClueDetailVO extends PendingClueVO { + + private String evidenceId; + + private String videoEvidencePath; + + private String clipStartTime; + + private String clipEndTime; + + private Integer clipDuration; + + private String operatorUnit; + + private String operatorContact; + + private String ownerContact; + + private String generatedBy; + + private Boolean hasTransferRecord; + + private String existingTransferId; +} diff --git a/src/main/java/com/chinaweal/youfool/prj/modules/evidence/transfer/entity/vo/PendingClueVO.java b/src/main/java/com/chinaweal/youfool/prj/modules/evidence/transfer/entity/vo/PendingClueVO.java new file mode 100644 index 0000000..c4b8156 --- /dev/null +++ b/src/main/java/com/chinaweal/youfool/prj/modules/evidence/transfer/entity/vo/PendingClueVO.java @@ -0,0 +1,42 @@ +package com.chinaweal.youfool.prj.modules.evidence.transfer.entity.vo; + +import com.chinaweal.youfool.framework.springboot.common.util.DateUtil; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.time.LocalDateTime; + +/** + * 待转办线索列表项 VO + * + * @author chenjy + * @since 2026/05/18 + */ +@Data +public class PendingClueVO { + + private String id; + + private String clueCode; + + private String screenId; + + private String screenName; + + private String screenAddress; + + private String district; + + private String ownerUnit; + + private String advertiser; + + private String relatedRules; + + private String relatedLawClauses; + + private Integer clueStatus; + + @JsonFormat(pattern = DateUtil.DATETIME_DEFAULT_FORMAT, timezone = "GMT+8") + private LocalDateTime generatedAt; +} diff --git a/src/main/java/com/chinaweal/youfool/prj/modules/evidence/transfer/service/IClueTransferService.java b/src/main/java/com/chinaweal/youfool/prj/modules/evidence/transfer/service/IClueTransferService.java index 738000f..ddd3200 100644 --- a/src/main/java/com/chinaweal/youfool/prj/modules/evidence/transfer/service/IClueTransferService.java +++ b/src/main/java/com/chinaweal/youfool/prj/modules/evidence/transfer/service/IClueTransferService.java @@ -3,8 +3,15 @@ package com.chinaweal.youfool.prj.modules.evidence.transfer.service; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.chinaweal.youfool.framework.springboot.rest.RestResult; import com.chinaweal.youfool.prj.modules.evidence.transfer.entity.query.ClueTransferQuery; +import com.chinaweal.youfool.prj.modules.evidence.transfer.entity.query.PendingClueQuery; import com.chinaweal.youfool.prj.modules.evidence.transfer.entity.req.ClueTransferReq; +import com.chinaweal.youfool.prj.modules.evidence.transfer.entity.req.TransferStatusUpdateReq; +import com.chinaweal.youfool.prj.modules.evidence.transfer.entity.req.TransferUrgeReq; +import com.chinaweal.youfool.prj.modules.evidence.transfer.entity.req.TransferWithdrawReq; import com.chinaweal.youfool.prj.modules.evidence.transfer.entity.vo.ClueTransferDetailVO; +import com.chinaweal.youfool.prj.modules.evidence.transfer.entity.vo.OperationLogVO; +import com.chinaweal.youfool.prj.modules.evidence.transfer.entity.vo.PendingClueDetailVO; +import com.chinaweal.youfool.prj.modules.evidence.transfer.entity.vo.PendingClueVO; import java.util.List; import java.util.Map; @@ -72,4 +79,16 @@ public interface IClueTransferService { * @return 处置反馈 */ RestResult> disposalFeedback(String clueId); + + RestResult> queryPendingClues(PendingClueQuery query); + + RestResult getPendingClueDetail(String clueId); + + RestResult> getOperationLogs(String transferRecordId); + + RestResult updateTransferStatus(TransferStatusUpdateReq req); + + RestResult urgeTransfer(TransferUrgeReq req); + + RestResult withdrawTransfer(TransferWithdrawReq req); } diff --git a/src/main/java/com/chinaweal/youfool/prj/modules/evidence/transfer/service/impl/ClueTransferServiceImpl.java b/src/main/java/com/chinaweal/youfool/prj/modules/evidence/transfer/service/impl/ClueTransferServiceImpl.java index 9136ac6..bc735a8 100644 --- a/src/main/java/com/chinaweal/youfool/prj/modules/evidence/transfer/service/impl/ClueTransferServiceImpl.java +++ b/src/main/java/com/chinaweal/youfool/prj/modules/evidence/transfer/service/impl/ClueTransferServiceImpl.java @@ -13,8 +13,15 @@ import com.chinaweal.youfool.prj.modules.evidence.clue.mapper.MonitoringClueMapp import com.chinaweal.youfool.prj.modules.evidence.transfer.entity.ClueTransferRecordEntity; import com.chinaweal.youfool.prj.modules.evidence.transfer.entity.TransferOperationLogEntity; import com.chinaweal.youfool.prj.modules.evidence.transfer.entity.query.ClueTransferQuery; +import com.chinaweal.youfool.prj.modules.evidence.transfer.entity.query.PendingClueQuery; import com.chinaweal.youfool.prj.modules.evidence.transfer.entity.req.ClueTransferReq; +import com.chinaweal.youfool.prj.modules.evidence.transfer.entity.req.TransferStatusUpdateReq; +import com.chinaweal.youfool.prj.modules.evidence.transfer.entity.req.TransferUrgeReq; +import com.chinaweal.youfool.prj.modules.evidence.transfer.entity.req.TransferWithdrawReq; import com.chinaweal.youfool.prj.modules.evidence.transfer.entity.vo.ClueTransferDetailVO; +import com.chinaweal.youfool.prj.modules.evidence.transfer.entity.vo.OperationLogVO; +import com.chinaweal.youfool.prj.modules.evidence.transfer.entity.vo.PendingClueDetailVO; +import com.chinaweal.youfool.prj.modules.evidence.transfer.entity.vo.PendingClueVO; import com.chinaweal.youfool.prj.modules.evidence.transfer.mapper.ClueTransferRecordMapper; import com.chinaweal.youfool.prj.modules.evidence.transfer.mapper.TransferOperationLogMapper; import com.chinaweal.youfool.prj.modules.evidence.transfer.service.IClueTransferService; @@ -247,6 +254,166 @@ public class ClueTransferServiceImpl extends ServiceImpl> queryPendingClues(PendingClueQuery query) { + Page page = new Page<>(query.getPageNum(), query.getPageSize()); + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(MonitoringClueEntity::getClueStatus, 1); + if (StringUtils.isNotBlank(query.getClueCode())) { + wrapper.like(MonitoringClueEntity::getClueCode, query.getClueCode()); + } + if (StringUtils.isNotBlank(query.getDistrict())) { + wrapper.eq(MonitoringClueEntity::getDistrict, query.getDistrict()); + } + if (StringUtils.isNotBlank(query.getKeyword())) { + wrapper.and(w -> w + .like(MonitoringClueEntity::getScreenName, query.getKeyword()) + .or() + .like(MonitoringClueEntity::getScreenAddress, query.getKeyword()) + ); + } + wrapper.orderByDesc(MonitoringClueEntity::getGeneratedAt); + Page entityPage = monitoringClueMapper.selectPage(page, wrapper); + + Page voPage = new Page<>(entityPage.getCurrent(), + entityPage.getSize(), entityPage.getTotal()); + List voList = entityPage.getRecords().stream().map(entity -> { + PendingClueVO vo = new PendingClueVO(); + BeanUtils.copyProperties(entity, vo); + return vo; + }).collect(Collectors.toList()); + voPage.setRecords(voList); + return RestResult.ok(voPage); + } + + @Override + public RestResult getPendingClueDetail(String clueId) { + AssertUtils.isNotBlank(clueId); + MonitoringClueEntity entity = monitoringClueMapper.selectById(clueId); + AssertUtils.isNotNull(entity, "线索不存在"); + AssertUtils.isTrue(entity.getClueStatus() == 1, + BaseResultCode.PARAM_IS_INVALID, "当前线索状态非待转办"); + + PendingClueDetailVO vo = new PendingClueDetailVO(); + BeanUtils.copyProperties(entity, vo); + + // 检查是否已有转办记录 + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(ClueTransferRecordEntity::getClueId, clueId); + wrapper.orderByDesc(ClueTransferRecordEntity::getTransferredAt); + wrapper.last("LIMIT 1"); + List existing = this.list(wrapper); + vo.setHasTransferRecord(!existing.isEmpty()); + if (!existing.isEmpty()) { + vo.setExistingTransferId(existing.get(0).getId()); + } + return RestResult.ok(vo); + } + + @Override + public RestResult> getOperationLogs(String transferRecordId) { + AssertUtils.isNotBlank(transferRecordId); + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(TransferOperationLogEntity::getClueTransferRecordId, transferRecordId); + wrapper.orderByAsc(TransferOperationLogEntity::getOperatedAt); + List logs = transferOperationLogMapper.selectList(wrapper); + List voList = logs.stream().map(logEntity -> { + OperationLogVO vo = new OperationLogVO(); + BeanUtils.copyProperties(logEntity, vo); + return vo; + }).collect(Collectors.toList()); + return RestResult.ok(voList); + } + + @Override + @DSTransactional + public RestResult updateTransferStatus(TransferStatusUpdateReq req) { + AssertUtils.isNotBlank(req.getTransferRecordId()); + AssertUtils.isNotBlank(req.getNewStatus()); + + ClueTransferRecordEntity record = this.getById(req.getTransferRecordId()); + AssertUtils.isNotNull(record, "转办记录不存在"); + + String oldStatus = record.getTransferStatus(); + validateStatusTransition(oldStatus, req.getNewStatus()); + + // 更新转办记录 + record.setTransferStatus(req.getNewStatus()); + if (StringUtils.isNotBlank(req.getExternalClueId())) { + record.setExternalClueId(req.getExternalClueId()); + } + if ("completed".equals(req.getNewStatus())) { + AssertUtils.isTrue(StringUtils.isNotBlank(req.getDisposalResult()), + BaseResultCode.PARAM_IS_INVALID, "处置结果不能为空"); + record.setDisposalResult(req.getDisposalResult()); + record.setDisposalCompletedAt(LocalDateTime.now()); + } + this.updateById(record); + + // 同步线索状态 + MonitoringClueEntity clue = monitoringClueMapper.selectById(record.getClueId()); + if (clue != null) { + if ("processing".equals(req.getNewStatus())) { + clue.setClueStatus(3); + } else if ("completed".equals(req.getNewStatus())) { + clue.setClueStatus(4); + } else if ("failed".equals(req.getNewStatus())) { + clue.setClueStatus(1); + } + monitoringClueMapper.updateById(clue); + } + + // 记录操作日志 + saveOperationLog(record.getId(), "status_update", + "状态从 " + oldStatus + " 变更为 " + req.getNewStatus(), + oldStatus, req.getNewStatus(), "外部系统回调"); + return RestResult.ok(); + } + + @Override + @DSTransactional + public RestResult urgeTransfer(TransferUrgeReq req) { + AssertUtils.isNotBlank(req.getTransferRecordId()); + ClueTransferRecordEntity record = this.getById(req.getTransferRecordId()); + AssertUtils.isNotNull(record, "转办记录不存在"); + AssertUtils.isTrue("transferred".equals(record.getTransferStatus()) + || "processing".equals(record.getTransferStatus()), + BaseResultCode.PARAM_IS_INVALID, "当前状态不允许催办"); + + // TODO: 对接消息系统发送催办通知 + saveOperationLog(record.getId(), "urge", + "催办:" + (req.getUrgeMessage() != null ? req.getUrgeMessage() : "请尽快处理"), + record.getTransferStatus(), record.getTransferStatus(), "系统管理员"); + return RestResult.ok(); + } + + @Override + @DSTransactional + public RestResult withdrawTransfer(TransferWithdrawReq req) { + AssertUtils.isNotBlank(req.getTransferRecordId()); + ClueTransferRecordEntity record = this.getById(req.getTransferRecordId()); + AssertUtils.isNotNull(record, "转办记录不存在"); + AssertUtils.isTrue("transferred".equals(record.getTransferStatus()), + BaseResultCode.PARAM_IS_INVALID, "只有已转办状态才能撤回"); + + String oldStatus = record.getTransferStatus(); + record.setTransferStatus("failed"); + this.updateById(record); + + // 回退线索状态到待转办 + MonitoringClueEntity clue = monitoringClueMapper.selectById(record.getClueId()); + if (clue != null) { + clue.setClueStatus(1); + clue.setTransferredAt(null); + monitoringClueMapper.updateById(clue); + } + + saveOperationLog(record.getId(), "withdraw", + "撤回转办:" + (req.getWithdrawReason() != null ? req.getWithdrawReason() : ""), + oldStatus, "failed", "系统管理员"); + return RestResult.ok(); + } + // ========================================================================= // 私有方法 // ========================================================================= @@ -267,4 +434,11 @@ public class ClueTransferServiceImpl extends ServiceImpl " + to); + } }