diff --git a/CLAUDE.md b/CLAUDE.md index 6b5f037..9384f29 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -143,3 +143,35 @@ gz-oarms-web/docs/prd-llm/ - F. Harness Engineering(全流程、模块开发进度总表、核心实体跨域依赖、待开发任务建议顺序) 前后端联调指南见 `docs/前后端联调指南.md`,包含 10 个模块的接口清单、DTO 字段、数据库表映射及高频错误模式。 + +## Coding Guidelines (Karpathy) + +编码行为准则,偏向谨慎而非速度。简单任务可灵活判断。 + +### 1. 先思考再编码 + +- 不要假设。不确定时先问。 +- 存在多种理解时,列出选项而非静默选择。 +- 有更简单的方案时说出来。必要时反驳。 +- 不清楚就停,指出困惑点。 + +### 2. 简洁优先 + +- 只实现被要求的功能,不加额外特性。 +- 单次使用的代码不做抽象。 +- 没有要求的"灵活性"或"可配置性"不加。 +- 不可能发生的场景不加错误处理。 +- 200 行能缩减到 50 行就重写。 + +### 3. 精准修改 + +- 只改必须改的。不"改善"相邻代码、注释或格式。 +- 能用的代码不重构。匹配已有风格。 +- 发现无关死代码时提及但不删除。 +- 自己的改动产生的废弃 import/变量/函数要清理。 + +### 4. 目标驱动 + +- 将任务转化为可验证的目标。 +- 多步骤任务先列出简短计划和验证点。 +- 成功标准要明确,避免模糊目标(如"让它能用")。 diff --git a/docs/db/sql/V10.0.1__CW_clue_transfer_init_data.sql b/docs/db/sql/V10.0.1__CW_clue_transfer_init_data.sql new file mode 100644 index 0000000..cb0a267 --- /dev/null +++ b/docs/db/sql/V10.0.1__CW_clue_transfer_init_data.sql @@ -0,0 +1,119 @@ +-- ============================================================================ +-- OARMS - CW-4 线索转办管理 初始数据 +-- Database: DM8 (达梦) +-- Schema: OARMS +-- Version: V10.0.1 +-- Date: 2026-05-27 +-- Description: 基于 CW-MC-001 线索的转办记录 + 操作日志 +-- Note: +-- 1. CW-MC-001 当前线索状态为 1(待转办),创建一条已转办记录 +-- 2. 同时将线索状态更新为 2(已转办) +-- 3. 转办目标使用行政区划中的区域编码 +-- ============================================================================ + +-- 1. CW-MC-001(白云万达虚假宣传) → 转办到白云区市场监督管理局 +INSERT INTO OARMS.CW_CLUE_TRANSFER_RECORD ( + ID, CLUE_ID, TRANSFER_TARGET_DISTRICT, TRANSFER_TARGET_DEPARTMENT, + TRANSFER_TARGET_PERSON, TRANSFER_DESCRIPTION, TRANSFER_PERSON, + TRANSFERRED_AT, TRANSFER_STATUS, EXTERNAL_CLUE_ID, + CREATE_BY, CREATE_TIME, CREATE_NAME, UPDATE_BY, UPDATE_TIME, UPDATE_NAME +) VALUES ( + 'CW-CTR-001', 'CW-MC-001', '440111', '白云区市场监督管理局', + '张志明', '该线索涉及白云万达广场LED大屏虚假宣传"包治百病",建议转白云区市监局处理', + '系统管理员', + TO_TIMESTAMP('2026-05-25 10:00:00', 'YYYY-MM-DD HH24:MI:SS'), 'transferred', 'EXT-CLUE-20260525-001', + 'system', TO_TIMESTAMP('2026-05-25 10:00:00', 'YYYY-MM-DD HH24:MI:SS'), '系统', + 'system', TO_TIMESTAMP('2026-05-25 10:00:00', 'YYYY-MM-DD HH24:MI:SS'), '系统' +); + +-- 2. CW-MC-001 → 转办记录2(天河区,已办结状态) +INSERT INTO OARMS.CW_CLUE_TRANSFER_RECORD ( + ID, CLUE_ID, TRANSFER_TARGET_DISTRICT, TRANSFER_TARGET_DEPARTMENT, + TRANSFER_TARGET_PERSON, TRANSFER_DESCRIPTION, TRANSFER_PERSON, + TRANSFERRED_AT, TRANSFER_STATUS, EXTERNAL_CLUE_ID, + DISPOSAL_RESULT, DISPOSAL_COMPLETED_AT, + CREATE_BY, CREATE_TIME, CREATE_NAME, UPDATE_BY, UPDATE_TIME, UPDATE_NAME +) VALUES ( + 'CW-CTR-002', 'CW-MC-001', '440106', '天河区市场监督管理局', + '李伟强', '同时转天河区市监局协助调查广告主信息', + '系统管理员', + TO_TIMESTAMP('2026-05-26 09:30:00', 'YYYY-MM-DD HH24:MI:SS'), 'completed', 'EXT-CLUE-20260526-002', + '已联系广告主,责令拆除违规广告内容,罚款5000元', + TO_TIMESTAMP('2026-05-26 16:00:00', 'YYYY-MM-DD HH24:MI:SS'), + 'system', TO_TIMESTAMP('2026-05-26 09:30:00', 'YYYY-MM-DD HH24:MI:SS'), '系统', + 'system', TO_TIMESTAMP('2026-05-26 16:00:00', 'YYYY-MM-DD HH24:MI:SS'), '系统' +); + +-- ===================== 转办操作日志 ===================== + +-- CW-CTR-001: 提交 + 推送成功 +INSERT INTO OARMS.CW_TRANSFER_OPERATION_LOG ( + ID, CLUE_TRANSFER_RECORD_ID, OPERATION_TYPE, OPERATION_DETAIL, + FROM_STATUS, TO_STATUS, OPERATOR, OPERATED_AT +) VALUES ( + 'CW-TOL-001', 'CW-CTR-001', 'submit', + '提交线索转办,目标:白云区市场监督管理局', + NULL, 'transferred', '系统管理员', + TO_TIMESTAMP('2026-05-25 10:00:00', 'YYYY-MM-DD HH24:MI:SS') +); + +INSERT INTO OARMS.CW_TRANSFER_OPERATION_LOG ( + ID, CLUE_TRANSFER_RECORD_ID, OPERATION_TYPE, OPERATION_DETAIL, + FROM_STATUS, TO_STATUS, OPERATOR, OPERATED_AT +) VALUES ( + 'CW-TOL-002', 'CW-CTR-001', 'push_success', + '线索推送成功,外部线索ID: EXT-CLUE-20260525-001', + 'transferred', 'transferred', '系统管理员', + TO_TIMESTAMP('2026-05-25 10:00:05', 'YYYY-MM-DD HH24:MI:SS') +); + +-- CW-CTR-002: 提交 + 推送成功 + 状态同步 + 结果反馈 +INSERT INTO OARMS.CW_TRANSFER_OPERATION_LOG ( + ID, CLUE_TRANSFER_RECORD_ID, OPERATION_TYPE, OPERATION_DETAIL, + FROM_STATUS, TO_STATUS, OPERATOR, OPERATED_AT +) VALUES ( + 'CW-TOL-003', 'CW-CTR-002', 'submit', + '提交线索转办,目标:天河区市场监督管理局', + NULL, 'transferred', '系统管理员', + TO_TIMESTAMP('2026-05-26 09:30:00', 'YYYY-MM-DD HH24:MI:SS') +); + +INSERT INTO OARMS.CW_TRANSFER_OPERATION_LOG ( + ID, CLUE_TRANSFER_RECORD_ID, OPERATION_TYPE, OPERATION_DETAIL, + FROM_STATUS, TO_STATUS, OPERATOR, OPERATED_AT +) VALUES ( + 'CW-TOL-004', 'CW-CTR-002', 'push_success', + '线索推送成功,外部线索ID: EXT-CLUE-20260526-002', + 'transferred', 'transferred', '系统管理员', + TO_TIMESTAMP('2026-05-26 09:30:05', 'YYYY-MM-DD HH24:MI:SS') +); + +INSERT INTO OARMS.CW_TRANSFER_OPERATION_LOG ( + ID, CLUE_TRANSFER_RECORD_ID, OPERATION_TYPE, OPERATION_DETAIL, + FROM_STATUS, TO_STATUS, OPERATOR, OPERATED_AT +) VALUES ( + 'CW-TOL-005', 'CW-CTR-002', 'status_update', + '状态从 transferred 变更为 processing', + 'transferred', 'processing', '外部系统回调', + TO_TIMESTAMP('2026-05-26 14:00:00', 'YYYY-MM-DD HH24:MI:SS') +); + +INSERT INTO OARMS.CW_TRANSFER_OPERATION_LOG ( + ID, CLUE_TRANSFER_RECORD_ID, OPERATION_TYPE, OPERATION_DETAIL, + FROM_STATUS, TO_STATUS, OPERATOR, OPERATED_AT +) VALUES ( + 'CW-TOL-006', 'CW-CTR-002', 'status_update', + '状态从 processing 变更为 completed', + 'processing', 'completed', '外部系统回调', + TO_TIMESTAMP('2026-05-26 16:00:00', 'YYYY-MM-DD HH24:MI:SS') +); + +-- ===================== 更新线索状态 ===================== +-- CW-MC-001 从待转办(1)更新为已转办(2) +UPDATE OARMS.CW_MONITORING_CLUE +SET CLUE_STATUS = 2, + TRANSFERRED_AT = TO_TIMESTAMP('2026-05-25 10:00:00', 'YYYY-MM-DD HH24:MI:SS'), + UPDATE_BY = 'system', + UPDATE_TIME = TO_TIMESTAMP('2026-05-26 16:00:00', 'YYYY-MM-DD HH24:MI:SS'), + UPDATE_NAME = '系统' +WHERE ID = 'CW-MC-001'; diff --git a/src/main/java/com/chinaweal/youfool/prj/modules/evidence/record/entity/vo/EvidenceRecordDetailVO.java b/src/main/java/com/chinaweal/youfool/prj/modules/evidence/record/entity/vo/EvidenceRecordDetailVO.java index dac4a93..6a4769b 100644 --- a/src/main/java/com/chinaweal/youfool/prj/modules/evidence/record/entity/vo/EvidenceRecordDetailVO.java +++ b/src/main/java/com/chinaweal/youfool/prj/modules/evidence/record/entity/vo/EvidenceRecordDetailVO.java @@ -112,4 +112,9 @@ public class EvidenceRecordDetailVO { * 状态变更历史列表 */ private List> statusHistory; + + /** + * 关联规则数量 + */ + private Integer relatedRuleCount; } diff --git a/src/main/java/com/chinaweal/youfool/prj/modules/evidence/record/service/impl/EvidenceRecordServiceImpl.java b/src/main/java/com/chinaweal/youfool/prj/modules/evidence/record/service/impl/EvidenceRecordServiceImpl.java index 10ddde7..6d3c4ba 100644 --- a/src/main/java/com/chinaweal/youfool/prj/modules/evidence/record/service/impl/EvidenceRecordServiceImpl.java +++ b/src/main/java/com/chinaweal/youfool/prj/modules/evidence/record/service/impl/EvidenceRecordServiceImpl.java @@ -20,6 +20,8 @@ import com.chinaweal.youfool.prj.modules.screen.entity.ScreenEntity; import com.chinaweal.youfool.prj.modules.system.entity.vo.LoginUserVO; import com.chinaweal.youfool.prj.modules.system.util.DataScopeHelper; import com.chinaweal.youfool.prj.modules.screen.mapper.ScreenMapper; +import com.chinaweal.youfool.prj.modules.evidence.relation.entity.EvidenceRuleRelationEntity; +import com.chinaweal.youfool.prj.modules.evidence.relation.mapper.EvidenceRuleRelationMapper; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeanUtils; @@ -30,6 +32,7 @@ import java.time.format.DateTimeFormatter; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; /** @@ -45,6 +48,7 @@ public class EvidenceRecordServiceImpl extends ServiceImpl voPage = new Page<>(entityPage.getCurrent(), entityPage.getSize(), entityPage.getTotal()); + + // 批量查询关联规则数量 + Set evidenceIds = entityPage.getRecords().stream() + .map(EvidenceRecordEntity::getId).collect(Collectors.toSet()); + Map ruleCountMap = Map.of(); + if (!evidenceIds.isEmpty()) { + LambdaQueryWrapper ruleWrapper = new LambdaQueryWrapper<>(); + ruleWrapper.in(EvidenceRuleRelationEntity::getEvidenceId, evidenceIds); + List allRelations = evidenceRuleRelationMapper.selectList(ruleWrapper); + ruleCountMap = allRelations.stream() + .collect(Collectors.groupingBy(EvidenceRuleRelationEntity::getEvidenceId, Collectors.counting())); + } + + Map finalRuleCountMap = ruleCountMap; List voList = entityPage.getRecords().stream().map(entity -> { EvidenceRecordDetailVO vo = new EvidenceRecordDetailVO(); BeanUtils.copyProperties(entity, vo); + vo.setRelatedRuleCount(finalRuleCountMap.getOrDefault(entity.getId(), 0L).intValue()); return vo; }).collect(Collectors.toList()); voPage.setRecords(voList); diff --git a/src/main/java/com/chinaweal/youfool/prj/modules/rule/controller/MonitoringRuleController.java b/src/main/java/com/chinaweal/youfool/prj/modules/rule/controller/MonitoringRuleController.java index 9e4ce2b..5474298 100644 --- a/src/main/java/com/chinaweal/youfool/prj/modules/rule/controller/MonitoringRuleController.java +++ b/src/main/java/com/chinaweal/youfool/prj/modules/rule/controller/MonitoringRuleController.java @@ -192,4 +192,17 @@ public class MonitoringRuleController { log.info("[OK] 导出监测规则数据"); return monitoringRuleService.exportData(query); } + + /** + * 查询规则操作历史 + * + * @param id 规则ID + * @return 操作历史列表 + */ + @GetMapping("histories") + @Operation(summary = "查询规则操作历史") + public RestResult>> getHistories(String id) { + log.info("[OK] 查询规则操作历史: id={}", id); + return monitoringRuleService.getHistories(id); + } } diff --git a/src/main/java/com/chinaweal/youfool/prj/modules/rule/entity/vo/MonitoringRuleDetailVO.java b/src/main/java/com/chinaweal/youfool/prj/modules/rule/entity/vo/MonitoringRuleDetailVO.java index f5021ae..71e264a 100644 --- a/src/main/java/com/chinaweal/youfool/prj/modules/rule/entity/vo/MonitoringRuleDetailVO.java +++ b/src/main/java/com/chinaweal/youfool/prj/modules/rule/entity/vo/MonitoringRuleDetailVO.java @@ -5,7 +5,7 @@ import com.fasterxml.jackson.annotation.JsonFormat; import lombok.Data; import lombok.experimental.Accessors; -import java.util.Date; +import java.time.LocalDateTime; import java.util.List; import java.util.Map; @@ -52,10 +52,10 @@ public class MonitoringRuleDetailVO { private Integer enableStatus; @JsonFormat(pattern = DateUtil.DATETIME_DEFAULT_FORMAT, timezone = "GMT+8") - private Date createTime; + private LocalDateTime createTime; @JsonFormat(pattern = DateUtil.DATETIME_DEFAULT_FORMAT, timezone = "GMT+8") - private Date updateTime; + private LocalDateTime updateTime; private String createName; diff --git a/src/main/java/com/chinaweal/youfool/prj/modules/rule/service/IMonitoringRuleService.java b/src/main/java/com/chinaweal/youfool/prj/modules/rule/service/IMonitoringRuleService.java index 45e16b1..d489223 100644 --- a/src/main/java/com/chinaweal/youfool/prj/modules/rule/service/IMonitoringRuleService.java +++ b/src/main/java/com/chinaweal/youfool/prj/modules/rule/service/IMonitoringRuleService.java @@ -89,4 +89,12 @@ public interface IMonitoringRuleService { * @return 导出数据 */ RestResult>> exportData(MonitoringRuleQuery query); + + /** + * 查询规则操作历史 + * + * @param id 规则ID + * @return 操作历史列表 + */ + RestResult>> getHistories(String id); } diff --git a/src/main/java/com/chinaweal/youfool/prj/modules/rule/service/impl/MonitoringRuleServiceImpl.java b/src/main/java/com/chinaweal/youfool/prj/modules/rule/service/impl/MonitoringRuleServiceImpl.java index ac3c540..ef02777 100644 --- a/src/main/java/com/chinaweal/youfool/prj/modules/rule/service/impl/MonitoringRuleServiceImpl.java +++ b/src/main/java/com/chinaweal/youfool/prj/modules/rule/service/impl/MonitoringRuleServiceImpl.java @@ -18,6 +18,8 @@ import com.chinaweal.youfool.prj.modules.rule.mapper.MonitoringRuleMapper; import com.chinaweal.youfool.prj.modules.rule.mapper.RuleLawClauseRelMapper; import com.chinaweal.youfool.prj.modules.rule.mapper.RuleOperationHistoryMapper; import com.chinaweal.youfool.prj.modules.rule.service.IMonitoringRuleService; +import com.chinaweal.youfool.prj.modules.law.entity.LawClauseEntity; +import com.chinaweal.youfool.prj.modules.law.mapper.LawClauseMapper; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeanUtils; @@ -46,6 +48,7 @@ public class MonitoringRuleServiceImpl extends ServiceImpl clauseRels = ruleLawClauseRelMapper.selectList(clauseWrapper); List> lawClauses = clauseRels.stream().map(rel -> { - Map map = new HashMap<>(); + Map map = new LinkedHashMap<>(); map.put("id", rel.getId()); map.put("clauseId", rel.getClauseId()); + LawClauseEntity clause = lawClauseMapper.selectById(rel.getClauseId()); + if (clause == null) { + LambdaQueryWrapper codeWrapper = new LambdaQueryWrapper<>(); + codeWrapper.eq(LawClauseEntity::getClauseCode, rel.getClauseId()); + clause = lawClauseMapper.selectOne(codeWrapper); + } + if (clause != null) { + map.put("lawName", clause.getLawName()); + map.put("clauseCode", clause.getClauseCode()); + map.put("clauseContent", clause.getClauseContent()); + } return map; }).collect(Collectors.toList()); vo.setLawClauses(lawClauses); @@ -301,6 +315,25 @@ public class MonitoringRuleServiceImpl extends ServiceImpl>> getHistories(String id) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(RuleOperationHistoryEntity::getRuleId, id); + wrapper.orderByDesc(RuleOperationHistoryEntity::getOperatedAt); + List histories = ruleOperationHistoryMapper.selectList(wrapper); + List> list = histories.stream().map(h -> { + Map map = new LinkedHashMap<>(); + map.put("id", h.getId()); + map.put("operator", h.getOperator()); + map.put("operationType", h.getOperationType()); + map.put("beforeChange", h.getBeforeChange()); + map.put("afterChange", h.getAfterChange()); + map.put("operatedAt", h.getOperatedAt()); + return map; + }).collect(Collectors.toList()); + return RestResult.ok(list); + } + // ========================================================================= // 私有方法 // =========================================================================