feat: 分级拦截日志 + 两类日志文件;成绩录入防呆/解析/宽度修复
## 日志(用户要求:分级 + 每个拦截都要有指出代码行的调试日志 + 两类文件)
- 新增 internal/logging:拦截点捕获(file:line + 函数名,相对路径)、两类文件轮转、
按级别/类别分流。httpapi 与 middleware 各自薄封装,无循环依赖。
- 两类目录(数据目录下):
operations/ 放行类(INFO「操作已放行」+ 常规运行日志,按 log.level)
interceptions/ 拦截类(WARN 中文原因 + 拦截点 + method/path/actor/role/requestId、
DEBUG 上下文、ERROR 服务端异常)—— **固定 debug 采集**,
否则默认 info 下"每次拦截的调试日志"根本看不见
- 命名 operations-YYYYMMDD[.N].log,按天+按大小轮转,每类保留 keepFiles 份;写盘失败
降级只写控制台且只 Warn 一次。
- 所有业务错误统一走 newAPIError 捕获拦截点(11 处内联 &apiError{} 已收进构造函数,
防止绕过);中间件层拦截(401/403/CSRF/未改初始密码/429)也留痕,响应体与状态码一字未变。
- config.yaml 的 log 段新增 dir / maxSizeMB / keepFiles(留空即默认)。
- 服务端包数 12 → 13(新增 internal/logging)。
## 成绩录入(用户实机踩坑)
- 真因一:成绩格 placeholder 是光秃秃的 `1:06.33`,而全仓库没有任何 ::placeholder 样式,
示例文本与真实输入长得一样 → 用户把灰字当成已填内容,那 13 格其实是空的。
- 真因二:2 条报错是秒位 ≥ 60(1:70)或全角冒号(1:06);1:06 与 1:06.00 在解析器里
本来就等价(都 = 66),格式不是问题。
- 真因三:成绩格没有宽度定义(旁边名次 72px、状态 110px 都有),被挤到第 4 位数字看不见。
修复:parseSecondsDetail 区分 empty/seconds-overflow/unrecognized;新增 entryGuards.ts
纯函数(录入页与审核页共用),报错**打出那一格的实际内容**并说明原因;解析器新认全角
冒号/全角数字/1分06秒 等写法;成绩格 min-width 112px(按 fontTools 实测文本宽度定);
示例文本改 `例 1:06.33` 等并给录入表格内的 ::placeholder 加淡色斜体;新增
onBeforeRouteLeave 离开守卫(原来切菜单会静默丢未保存成绩)。
测试:服务端 13 包全绿(372 个测试函数);前端 vue-tsc 通过、205 例全过;Console 18 文件。
docs/ 与 devdocs/ 同步(FAQ 到 Q76,部署指南新增第 10 节「服务端日志与拦截复盘」)。
This commit is contained in:
1 parent
8e707e1e9b
commit
e5e0944d26
32 files changed
+2017
-138
No files matched your search
+18
-3
@@ -76,11 +76,19 @@
|
||||
|
||||
| 列 | 操作 |
|
||||
| --- | --- |
|
||||
| 成绩输入 | 直接输入成绩。时间类项目按 `1:06.33` 格式;工具栏会提示当前单位(秒 / 米 / 次 / 分) |
|
||||
| 成绩输入 | 直接输入成绩;工具栏会提示当前单位(秒 / 米 / 次 / 分),成绩格比过去更宽,四位数也看得全 |
|
||||
| 名次 | 自动排名项目由系统自动计算,不可编辑;手动名次项目(录入方式为「手动名次」或比较方式为「不比较」)可直接填名次 |
|
||||
| 状态 | 下拉选择:**有效**、**DNS 弃赛**、**DQ 取消**、**DNF 未完赛**、**退赛** |
|
||||
| 积分(自动) | 随成绩实时计算,仅供参考 |
|
||||
| 备注 | 自由填写 |
|
||||
| 备注 | 自由填写,示例文本是 `可留空` |
|
||||
|
||||
**成绩写法**:能接受的写法比过去多,中文输入法下的全角冒号 `1:06`、全角数字也认。
|
||||
|
||||
- 时间类(单位「秒」):`66`、`66.33`、`1:06`、`1:06.33`、`1:6`、`1分06秒`、`1分6秒`、`1分06`、`1小时2分3秒`;
|
||||
- 距离 / 计数 / 得分类:按工具栏提示的单位填数字,如米 `12.34`、次 `12`、分 `96.5`;
|
||||
- 秒位 ≥ 60 仍然拒绝(`1:70` 不是 70 秒),但报错会写明原因(见 4.4)。
|
||||
|
||||
**示例文本不是已填内容**:空格子里显示的 `例 1:06.33`(距离 `例 12.34`、计数 `例 12`、得分 `例 96.5`)是**更淡的斜体灰字**,只是告诉你大概怎么写,**不代表这一格已经有成绩**。灰字不会被提交;点发布时如果那一格还是灰字,就按「成绩为空」拦下。过去示例文本长得和真实输入一样,容易看成「我已经填了」。
|
||||
|
||||
右侧「实时积分预览」显示:若现在发布,各单位积分的变化、最大分差、受影响单位数。
|
||||
|
||||
@@ -104,7 +112,11 @@
|
||||
|
||||
页面下方「防呆校验」会实时列出问题:
|
||||
|
||||
- **错误(阻止发布)**:缺少成绩、成绩格式无法解析、手动名次项目缺少名次;
|
||||
- **错误(阻止发布)**:成绩格有三种原因,文案都会**把那一格的实际内容原样打出来**,并对症给出改法,不会只说一句「缺少成绩」让你去改本来就对的格式:
|
||||
- 空格子:`第 3 行 张三:成绩为空(当前「(空)」),请填写成绩`;
|
||||
- 秒位 ≥ 60:`第 3 行 张三:秒位不能 ≥ 60(当前「1:70」),请写成 1:06 或 66 这类`;
|
||||
- 写法无法识别:`第 3 行 张三:成绩写法无法识别(当前「约1分06秒」),可用 66、66.33、1:06、1:06.33、1分06秒`——非时间单位会换成对应的数字提示(不会拿时间格式去教距离项目);
|
||||
- 另外还有:手动名次项目缺少名次。
|
||||
- **提醒(不阻止发布)**:成绩 ≤ 0、名次重复(请确认是否并列)、成绩与赛会纪录差距过大。
|
||||
|
||||
有错误时点「发布」会被拦下并提示第一处问题。
|
||||
@@ -112,6 +124,7 @@
|
||||
### 4.5 存草稿与发布
|
||||
|
||||
1. **存草稿**:保存当前组别的成绩,草稿**不会上屏**,也不计入积分榜;有未保存修改时页面会显示「有未保存修改」徽标。
|
||||
- **未保存的成绩不会被静默丢掉**:有未保存修改时,点左侧菜单切到别的页面(以及浏览器前进 / 后退)会先弹出确认框,确认丢弃才离开;关闭或刷新浏览器时浏览器也会原生提示;切换项目本来就会先确认。选「取消」留在本页,改动都还在。
|
||||
2. **发布**:点击后弹出确认框,显示本次影响的单位数与最大分差;确认后成绩立即上屏并重算积分榜。
|
||||
- 发布前系统会自动把当前内容整组保存为草稿再发布,无需手动先存;
|
||||
- 示例数据模式下「发布」按钮不可用,避免把未落库的改动当成发布成功。
|
||||
@@ -530,6 +543,8 @@
|
||||
4. 筛选与分页作用于最近 200 条记录;
|
||||
5. 该页面仅管理员可查看。
|
||||
|
||||
> 本页是**业务审计**(谁在什么时候改了什么),只记**放行**的操作。想看「某次操作为什么被系统拦下」——比如保存被拒、导入报错、切页提示离线——那要看服务端写在数据目录 `logs\` 下的**两类日志文件**(放行类 / 拦截类):我们主动拦下的报错会写明中文原因、拦截点 `文件:行 函数名`、操作人与 requestId。位置、轮转与复盘步骤见[部署指南 · 服务端日志与拦截复盘](./部署指南.md#10-服务端日志与拦截复盘)。
|
||||
|
||||
---
|
||||
|
||||
## 14. 系统设置
|
||||
|
||||
Reference in new issue
Block a user