# 变更日志 — 2026-09-14 ## 请求 通知模块新增「通知历史记录」表格界面:有分页、有查询,记录每次通知的渠道、类型、分组、详细内容等。 前端项目 `E:\Project\admin.xpcool.com` 一起实现并联调。 ## 变更 ### 后端 —— 通知历史记录能力扩展 - `api/notice/notice.go` — 通知历史记录契约扩展: - `NoticeLogItem` 由 11 字段扩到 27 字段(新增 `batchId`/`ruleName`/`eventName`/`noticeType`/`typeName`/ `group`/`groupName`/`channelName`/`userName`/`status`/`statusName`/`retryCount`/`durationMs`/`source`/`remark`)。 - `NoticeLogListReq` 新增 `keyword`/`noticeType`/`group`/`userId`/`batchId`/`status`/`orderBy`/`orderDir`; `NoticeLogListRes` 新增 `stats` 统计概览。 - 新增端点:`POST /notice/log/detail`(详情)、`/notice/log/delete`(批量删除)、 `/notice/log/clear`(按保留天数/时间范围清空)、`/notice/log/options`(字典选项)。 - `internal/model/dto/notice_meta.go`(**新增**)— 通知字典:渠道(bark/pushplus/webhook/email/internal)、 事件(7 种)、类型(job/security/recruit/test/manual/system)、分组(auto_job/server/recruitment/system) 的编码常量 + 中文名映射 + 选项下发 + 事件/类型/分组三级推导函数。 - `internal/model/dto/notice.go` — `NoticeLogVO`/`NoticeLogFilter` 同步扩展,并新增 `Normalize()` 归一化分页/排序(`Status` 与兼容字段 `Result` 的优先级合并)。 - `internal/model/entity/notice.go`、`internal/model/do/notice.go` — `NoticeLog` 新增 `batch_id`/`status`/`retry_count`/`duration_ms`/`source`/`remark` 六个字段。 - `internal/service/notice/notice.go` — - `LogList`:重写为多维筛选(关键字/类型/分组/事件/渠道/接收人/批次/状态/时间范围/排序)+ 统计概览, 列表与统计共用同一套 `logQuery` 条件保证口径一致;批量补齐规则名与接收人(避免 N+1)。 - 新增 `LogDetail`/`LogDelete`/`LogClear`。 - `Send` 引入 `batchId`(同一次业务触发的多条投递共用);`deliver`/`Test` 记录耗时、来源、状态、备注。 - 写库统一改用 `do.NoticeLog` / `do.NoticeRule` / `do.NoticeChannel`(原先用 `map[string]interface{}`, 不符合 AGENTS.md「数据库操作必须用 DO 对象」)。 - `internal/controller/notice/notice.go` — 新增 `LogDetail`/`LogDelete`/`LogClear`/`MetaOptions`, 并抽出 `toLogItem`/`toMetaItems` 做 dto → 契约转换。 - `manifest/sql/017_notice_log_enhance.sql`(**新增**)— `notice_log` 加 6 列 + 3 个索引; 按旧字段 `result` 回填 `status`;菜单 982 改名「通知历史」;新增 5 条 type=2 接口权限 (`notice:log:list/detail/delete/clear/options`);超管角色绑定。 ### 前端 —— `E:\Project\admin.xpcool.com` - `apps/web-tdesign/src/api/notice.ts` — 通知历史记录 API 客户端:类型与查询参数扩展, 新增 `getNoticeLogDetail` / `deleteNoticeLog` / `clearNoticeLog` / `getNoticeMetaOptions`, 以及 `NOTICE_STATUS_OPTIONS` / `noticeStatusTheme` 等展示辅助。 - `apps/web-tdesign/src/views/system/notice/log/index.vue` — **重写**为完整「通知历史」页面: - 顶部 4 张统计卡(总数/成功/失败/成功率),随筛选条件同步刷新; - 筛选区 9 个条件(时间范围、关键字、分组、类型、事件、渠道、接收人、状态、排序); - vxe 表格:多选列 + 通知时间/分组/类型/事件/渠道/接收人/标题/详细内容/结果/耗时/错误 + 行操作; - 详情抽屉(基本信息 + 标题/内容/目标/错误分区展示,均支持一键复制); - 批量删除、清空历史(可选「保留最近 N 天」);按权限码控制按钮显隐。 ### 联调 - 本地库执行 `017_notice_log_enhance.sql`:`notice_log` 现 17 列、3 个新索引; 菜单 `982 / 9821-9825` 就位;14 条历史失败记录 `status` 正确回填为 2。 - 后端 5 个新接口 + 原有 list 全部通过 RBAC 权限映射,返回 `code=0`。 - 真实投递一条测试通知,验证 `batchId` / `durationMs` / `source` / `userName` / `target` / `remark` 六项新字段正确落库与回显;统计概览 `1/0/1` 与筛选条件联动正确。 - 经前端 Vite 代理(20100 → 10100)完整跑通 登录 → 字典选项 → 分页列表 → 详情。 - 前端 `vue-tsc` 类型检查、`oxfmt` 格式检查、`oxlint` 全部 0 错误。 - **联调中修复 1 个缺陷**:`LogDetail` 对不存在的 ID 会把 `sql.ErrNoRows` 包装上抛, 被 gf 归为内部错误 `code=50` 并回显 SQL 细节;改为与项目既有写法一致 (忽略 Scan 错误、以 `Id==0` 判定),现返回 `code=10001`「通知记录不存在」。 ## 决策与理由 - **类型/分组不落库,由 `event_type` 派生**:二者与事件是一对多的确定映射,落库会引入双写不一致; 改为只存事件编码,展示与筛选在服务层推导(`NoticeEventToType` / `NoticeTypeToGroup`)。 - **`status` 与旧的 `result` 并存**:`result` 是已有数据在用的字段(1成功/0失败),直接改语义会破坏历史数据。 新增 `status`(0待发送/1成功/2失败)表达三态,查询时用 `(status=1 OR (status=0 AND result=1))` 兼容历史行。 - **字典选项由后端 `/notice/log/options` 统一下发**:前端原先把事件类型硬编码在页面里 (`NOTICE_EVENT_OPTIONS` 只有 4 项,且与 `notice_rule.event_type` 种子语义有偏差), 新增事件就要改两处;改为后端单一事实源,前端只做渲染。 - **清空提供「保留最近 N 天」而非纯全清**:历史记录是排查问题的依据,全清不可逆; 默认保留 30 天,`keepDays=0` 才真正全清,属于对误操作的兜底。 - **列表与统计共用 `logQuery`**:避免"列表按分组筛选、统计按全量"这类口径不一致, 前端统计卡与表格永远对同一批数据。 - **迁移脚本中 `status` 默认值取 0 而非 1**:`ALTER TABLE ADD COLUMN ... DEFAULT 1` 会把**所有历史行** 一并置为 1(含失败记录),造成"历史失败记录显示成功"。改为默认 0 后按 `result` 回填,语义正确且可重跑。 - **提示文案与枚举中文名收口到 `dto` 层**:中文名映射只在 `notice_meta.go` 定义一次, API、服务、前端展示三处复用,避免同义词漂移。 ## 待办与风险 - ⚠️ **`internal/service/recruitment/noise_test.go` 是 0 字节空文件**(未跟踪),会让 `go build ./...` 与 `go test ./...` 直接报 `expected 'package', found 'EOF'`。 本次联调为编译通过曾临时移走,**已原样还原**;需删除该文件或补上包声明。 - ⚠️ 工作区另有他人未提交改动:`internal/service/recruitment/bark.go` 修改、 `live_diag_test.go` 与 `testdata/rszk.html` 删除;前端 `api/wallpaper.ts`、 `views/wallpaper/*` 处于暂存状态。均非本次范围。 - 手动「测试发送」在目标凭据为空时会直接返回参数错误、**不写历史记录**(沿用原有行为)。 若希望失败尝试也可追溯,需在 `Test` 的提前返回分支补记日志。 - 历史记录只增不减,长期需要定期清理策略(目前靠页面「清空」手动处理), 后续可考虑挂到 `auto_job` 定时任务上。 - 本地联调环境:Node 22.22.2 + corepack `pnpm@11.16.0`;后端 10100、前端 dev 20100, 前端 `vite.config.ts` 已把 `/api/service` 代理到 `http://localhost:10100`。