chore: 清理本地配置并重组文档结构

This commit is contained in:
Cheng Zhou
2026-03-30 17:32:34 +08:00
parent 244c424126
commit 3a1415d1e1
23 changed files with 215 additions and 541 deletions
+35
View File
@@ -0,0 +1,35 @@
# Auth Session Acceptance Checklist
Status: `active`
Scope: `auth-session`
Last Updated: `2026-03-30`
1) Active user 60 minutes without logout
- 登录后持续操作 60 分钟,观察 token 多次 /auth/extend 且不会跳登录
2) Auto-extend near expiry
- 人工缩短 token 有效期或等待 exp < 2min
- 保持操作,确认 /auth/extend 被调用且业务不中断
3) Single-flight extend
- 同时触发 10 个 API 请求
- 仅出现 1 次 /auth/extend,其余请求自动重放成功
4) Idle lock at 30 minutes
- 30 分钟无鼠标/键盘/触摸/滚动且无任何请求
- 出现锁屏,背景虚化,原页面状态保留
5) Unlock success
- 锁屏输入正确密码,解锁成功并继续原页面
6) Unlock failure threshold
- 连续错误密码 >= 5 次
- 强制登出并跳登录页
7) Extend failure to lock
- 人为让 token 彻底过期,/auth/extend 返回 401
- 不跳登录,进入锁屏并要求密码解锁
8) 403 permission vs disabled
- 权限不足接口返回 403 时不跳登录
- /auth/extend 或 /auth/unlock 返回 403 时强制登出跳登录
@@ -0,0 +1,18 @@
# Enterprise UI Acceptance Checklist
Status: `pass`
Scope: `enterprise-ui`
Reviewed On: `2026-03-04`
Reviewer: `Codex`
- [x] Overview and milestones visual consistency
- [x] Risk pages visual consistency
- [x] Finance pages visual consistency
- [x] File/admin detail visual consistency
- [x] Desktop and iPad width checks
- [x] Management demo path continuity
Notes:
- Verified unified contract classes and shared shell usage on all target pages.
- Verified enterprise state components and KPI contract alignment.
- Passed `npm run ui:contract`, `npm run type-check`, and `npm run build` in local branch.
+78
View File
@@ -0,0 +1,78 @@
# 立项配置代码审计报告
状态: `archived`
适用范围: `setup-config`
审计时间: `2026-02`
## 范围
- 前端:`frontend/src/views/admin/ProjectDetail.vue``frontend/src/api/studies.ts``frontend/src/types/setupConfig.ts``frontend/src/utils/setupFieldLocator.ts`
- 后端:`backend/app/api/v1/studies.py``backend/app/crud/study_setup_config.py``backend/app/schemas/study_setup_config.py``backend/app/services/setup_config_excel.py`
- 文档:`docs/guides/setup-config-api.md``docs/setup-config-curl-smoke.sh``docs/postman/setup-config.postman_collection.json``backend/scripts/smoke_setup_config.py`
## 发现清单
### 严重(会导致错误或明显行为偏差)
- 无新增严重问题。
### 中等(会导致交互不一致或维护风险)
1. `ProjectDetail.vue` 中存在大量 `v-if="false"` 历史分支(Step4-7),增加阅读成本并容易引发误改。
2. 步骤表格从“输入态表格”转“展示态 + 抽屉编辑”后,存在未清理的旧方法与旧样式残留,导致逻辑路径复杂。
3. “发布预览”语义已切换为“当前草稿只读预览”,但文档未明确,容易与后端 `published_data` 概念混淆。
### 低(代码异味/重复/可读性)
1. `ProjectDetail.vue` 文件过长(约 3800+ 行),后续改动冲突概率高。
2. 多个步骤编辑抽屉逻辑重复,适合后续抽象为通用 `RowEditorDrawer`
3. 表格列模板重复,适合后续改为列配置驱动。
## 本轮已处理
1. 删除 Step4-7 全部 `v-if="false"` 模板分支。
2. 删除上述分支遗留的无用方法:
- `handleSiteMilestoneSiteChange`
- `handleSiteEnrollmentSiteChange`
- `handleCenterConfirmSiteChange`
- `touchStrategy`
- `applyStrategy`
3. 清理无用样式:
- `.section-toolbar`
- `.setup-content.is-published-preview .section-toolbar` 相关规则
4. 保持现有交互行为不变并完成构建验证。
5. 修复步骤标题行按钮保护不一致问题:
- 第2/4/5/6/7步标题行功能键增加 `:disabled="!canEditSetup"`,避免锁定项目或无权限时误触发。
- 对应新增/删除/抽屉打开与保存方法统一加守卫,防止外部调用绕过前端可见状态。
6. 收敛重复的草稿可编辑判定,新增 `canMutateDraft()` 供步骤行编辑方法复用(不改变界面行为)。
7. 抽取步骤行编辑通用控制器(`IndexedEditorController`)与通用函数:
- `openIndexedEditor`
- `getEditingIndex`
- `closeIndexedEditor`
复用到 Step2/4/5/6/7 的单行抽屉编辑逻辑,减少重复代码。
8. 合并重复 `onMounted` 注册逻辑,统一在单一入口完成 `loadProject``beforeunload` 监听初始化。
9. 新增 `frontend/src/composables/useSetupConfig.ts`
- 将 setup-config 相关 API 编排从页面中收口到 composable(获取/保存/发布/版本列表/回滚/版本删除/Excel 导入导出)。
- `ProjectDetail.vue` 改为依赖 composable 暴露的方法,降低页面对 API 层的直接耦合。
10. 新增 `frontend/src/utils/setupDiffRows.ts`
- 将“步骤差异明细”计算与字段路径可读化逻辑从 `ProjectDetail.vue` 抽离为工具函数。
- 页面保留项目主字段差异拼装,步骤差异统一由工具输出,降低页面认知负担。
11. 将步骤标题行按钮显隐规则收敛为 `stepActionMode` 计算属性:
- 去除模板中多段 `activeStep === n && !isPublishedView` 分支。
- 保持按钮行为与权限控制不变,降低后续步骤扩展的修改面。
## 验证结果
1. `frontend npm run build` 通过。
2. 后端 setup-config 核心文件通过 AST 语法解析检查。
3. 当前环境 `npx tsc --noEmit` 失败主要来自项目既有依赖类型版本不匹配(`vue`/`element-plus`/`csstype`),非本轮立项配置改动引入。
## 一致性核对结果
1. 前后端 setup-config 路由一致:
- `GET/PUT /setup-config`
- `POST /setup-config/publish`
- `GET /setup-config/versions`
- `POST /setup-config/rollback`
- `DELETE /setup-config/versions/{target_version}`
- `GET /setup-config/export-excel`
- `POST /setup-config/import-excel`
2. 前端 `setupConfig` 类型与后端 schema 字段一致(`published_data` 仍保留用于发布快照与差异比较)。
## 下一步建议(不改行为)
1. 拆分 `ProjectDetail.vue` 为步骤子组件(先拆 Step4-7)。
2. 抽象通用行编辑抽屉组件,统一校验和保存流程。
3. 使用列配置对象复用 `el-table-column` 渲染,降低重复模板。
+60
View File
@@ -0,0 +1,60 @@
# 存储落库核查报告
状态: `snapshot`
适用范围: `storage-persistence`
最后更新: `2026-02-27`
- 扫描目录: `/Users/zcc/MyCTMS/ctms-project/frontend/src`
- 总发现数: `46`
- 高风险: `4` / 中风险: `0` / 低风险: `42`
## 明细
| 风险 | 分类 | 文件 | 行号 | 调用 | key表达式 | 说明 |
| --- | --- | --- | --- | --- | --- | --- |
| high | business-draft | `frontend/src/views/admin/ProjectDetail.vue` | 2849 | `localStorage.getItem` | `storageKey.value` | 立项配置草稿本地兜底,需显式提示未落库 |
| high | business-draft | `frontend/src/views/admin/ProjectDetail.vue` | 2908 | `localStorage.getItem` | `projectDraftStorageKey.value` | 立项配置草稿本地兜底,需显式提示未落库 |
| high | business-draft | `frontend/src/views/admin/ProjectDetail.vue` | 2976 | `localStorage.removeItem` | `projectDraftStorageKey.value` | 立项配置草稿本地兜底,需显式提示未落库 |
| high | business-draft | `frontend/src/views/admin/ProjectDetail.vue` | 2984 | `localStorage.removeItem` | `storageKey.value` | 立项配置草稿本地兜底,需显式提示未落库 |
| low | ui-preference | `frontend/src/components/Layout.vue` | 227 | `localStorage.getItem` | `"ctms_sidebar_collapsed"` | UI偏好/上下文缓存 |
| low | ui-preference | `frontend/src/components/Layout.vue` | 390 | `localStorage.setItem` | `"ctms_sidebar_collapsed"` | UI偏好/上下文缓存 |
| low | auth-session | `frontend/src/components/LockScreenModal.vue` | 92 | `localStorage.getItem` | `"ctms_last_login_email"` | 认证/会话数据(非业务主数据) |
| low | auth-session | `frontend/src/components/ThreadList.vue` | 72 | `localStorage.getItem` | `"ctms_token"` | 认证/会话数据(非业务主数据) |
| low | auth-session | `frontend/src/components/attachments/AttachmentList.vue` | 132 | `localStorage.getItem` | `"ctms_token"` | 认证/会话数据(非业务主数据) |
| low | auth-session | `frontend/src/components/attachments/AttachmentList.vue` | 137 | `localStorage.getItem` | `"ctms_token"` | 认证/会话数据(非业务主数据) |
| low | auth-session | `frontend/src/components/attachments/AttachmentList.vue` | 168 | `localStorage.getItem` | `"ctms_token"` | 认证/会话数据(非业务主数据) |
| low | auth-session | `frontend/src/components/fees/FeeAttachmentPanel.vue` | 169 | `localStorage.getItem` | `"ctms_token"` | 认证/会话数据(非业务主数据) |
| low | auth-session | `frontend/src/components/fees/FeeAttachmentPanel.vue` | 174 | `localStorage.getItem` | `"ctms_token"` | 认证/会话数据(非业务主数据) |
| low | auth-session | `frontend/src/components/fees/FeeAttachmentPanel.vue` | 223 | `localStorage.getItem` | `"ctms_token"` | 认证/会话数据(非业务主数据) |
| low | auth-session | `frontend/src/session/sessionManager.ts` | 33 | `localStorage.setItem` | `"ctms_auth_broadcast"` | 认证/会话数据(非业务主数据) |
| low | auth-session | `frontend/src/session/sessionManager.ts` | 181 | `sessionStorage.removeItem` | `LOGOUT_REASON_STORAGE_KEY` | 认证/会话数据(非业务主数据) |
| low | auth-session | `frontend/src/session/sessionManager.ts` | 184 | `sessionStorage.setItem` | `LOGOUT_REASON_STORAGE_KEY` | 认证/会话数据(非业务主数据) |
| low | auth-session | `frontend/src/session/sessionManager.ts` | 192 | `sessionStorage.getItem` | `LOGOUT_REASON_STORAGE_KEY` | 认证/会话数据(非业务主数据) |
| low | auth-session | `frontend/src/session/sessionManager.ts` | 193 | `sessionStorage.removeItem` | `LOGOUT_REASON_STORAGE_KEY` | 认证/会话数据(非业务主数据) |
| low | auth-session | `frontend/src/store/auth.ts` | 24 | `localStorage.setItem` | `LAST_LOGIN_EMAIL_KEY` | 认证/会话数据(非业务主数据) |
| low | auth-session | `frontend/src/store/auth.ts` | 39 | `localStorage.setItem` | `LAST_LOGIN_EMAIL_KEY` | 认证/会话数据(非业务主数据) |
| low | auth-session | `frontend/src/store/auth.ts` | 46 | `localStorage.getItem` | `LAST_LOGIN_EMAIL_KEY` | 认证/会话数据(非业务主数据) |
| low | ui-preference | `frontend/src/store/study.ts` | 20 | `localStorage.getItem` | `LAST_STUDY_BY_USER_KEY` | UI偏好/上下文缓存 |
| low | ui-preference | `frontend/src/store/study.ts` | 30 | `localStorage.setItem` | `LAST_STUDY_BY_USER_KEY` | UI偏好/上下文缓存 |
| low | ui-preference | `frontend/src/store/study.ts` | 44 | `localStorage.removeItem` | `SITE_KEY` | UI偏好/上下文缓存 |
| low | ui-preference | `frontend/src/store/study.ts` | 48 | `localStorage.setItem` | `STUDY_KEY` | UI偏好/上下文缓存 |
| low | ui-preference | `frontend/src/store/study.ts` | 50 | `localStorage.setItem` | `STUDY_ROLE_KEY` | UI偏好/上下文缓存 |
| low | auth-session | `frontend/src/store/study.ts` | 52 | `localStorage.getItem` | `"ctms_last_login_email"` | 认证/会话数据(非业务主数据) |
| low | ui-preference | `frontend/src/store/study.ts` | 59 | `localStorage.getItem` | `STUDY_KEY` | UI偏好/上下文缓存 |
| low | ui-preference | `frontend/src/store/study.ts` | 67 | `localStorage.getItem` | `STUDY_ROLE_KEY` | UI偏好/上下文缓存 |
| low | ui-preference | `frontend/src/store/study.ts` | 70 | `localStorage.getItem` | `SITE_KEY` | UI偏好/上下文缓存 |
| low | ui-preference | `frontend/src/store/study.ts` | 116 | `localStorage.removeItem` | `STUDY_KEY` | UI偏好/上下文缓存 |
| low | ui-preference | `frontend/src/store/study.ts` | 117 | `localStorage.removeItem` | `STUDY_ROLE_KEY` | UI偏好/上下文缓存 |
| low | ui-preference | `frontend/src/store/study.ts` | 118 | `localStorage.removeItem` | `SITE_KEY` | UI偏好/上下文缓存 |
| low | ui-preference | `frontend/src/store/study.ts` | 165 | `localStorage.setItem` | `STUDY_ROLE_KEY` | UI偏好/上下文缓存 |
| low | ui-preference | `frontend/src/store/study.ts` | 167 | `localStorage.removeItem` | `STUDY_ROLE_KEY` | UI偏好/上下文缓存 |
| low | ui-preference | `frontend/src/store/study.ts` | 174 | `localStorage.setItem` | `SITE_KEY` | UI偏好/上下文缓存 |
| low | ui-preference | `frontend/src/store/study.ts` | 176 | `localStorage.removeItem` | `SITE_KEY` | UI偏好/上下文缓存 |
| low | auth-session | `frontend/src/utils/auth.ts` | 4 | `localStorage.getItem` | `TOKEN_KEY` | 认证/会话数据(非业务主数据) |
| low | auth-session | `frontend/src/utils/auth.ts` | 7 | `localStorage.setItem` | `TOKEN_KEY` | 认证/会话数据(非业务主数据) |
| low | auth-session | `frontend/src/utils/auth.ts` | 11 | `localStorage.removeItem` | `TOKEN_KEY` | 认证/会话数据(非业务主数据) |
| low | auth-session | `frontend/src/utils/auth.ts` | 17 | `localStorage.setItem` | `CREDENTIAL_KEY` | 认证/会话数据(非业务主数据) |
| low | auth-session | `frontend/src/utils/auth.ts` | 24 | `localStorage.getItem` | `CREDENTIAL_KEY` | 认证/会话数据(非业务主数据) |
| low | auth-session | `frontend/src/utils/auth.ts` | 38 | `localStorage.removeItem` | `CREDENTIAL_KEY` | 认证/会话数据(非业务主数据) |
| low | feature-cache | `frontend/src/views/workbench/components/PersonalTodo.vue` | 79 | `localStorage.getItem` | `props.storageKey` | 工作台本地待办缓存(非核心主数据) |
| low | feature-cache | `frontend/src/views/workbench/components/PersonalTodo.vue` | 91 | `localStorage.setItem` | `props.storageKey` | 工作台本地待办缓存(非核心主数据) |
@@ -0,0 +1,56 @@
# 全项目重要数据落库治理基线
状态: `active`
适用范围: `storage-persistence`
最后更新: `2026-02-27`
## 1. 口径
- 纳入范围:业务主数据 + 审计数据
- 排除范围:纯 UI 偏好、登录态会话、跨标签广播等非业务主数据
## 2. 自动核查入口
```bash
backend/scripts/storage_persistence_audit.py --format markdown --output docs/audits/storage-persistence-audit.md
backend/scripts/storage_persistence_audit.py --fail-on-banned
```
- `docs/audits/storage-persistence-audit.md`:当前扫描快照
- `--fail-on-banned`:用于阻断禁止项(当前默认禁止 `audit_local_*`
## 3. 当前结论(基于 2026-02-27 扫描)
- 高风险:4 条(均为 `ProjectDetail` 本地草稿兜底读写)
- 中风险:0 条
- 低风险:42 条(会话、偏好、工作台本地待办等)
## 4. 已完成整改
### 4.1 审计链路
- 关键管理动作改为后端落库(站点、项目成员)
- 审计页改为服务端唯一数据源
- 前端本地审计日志通道已下线(不再写 `audit_local_*`
- 审计导出事件改为后端写入审计表
### 4.2 业务链路
- 设备管理已接入后端并落库
- 项目里程碑编辑保存已改为调用后端接口并落库
- 立项配置草稿保留本地兜底,但已增加:
- `SYNCED / LOCAL_ONLY / DIRTY_UNSAVED` 状态识别
- 本地草稿恢复确认(含过期/冲突提示)
- 发布前 `LOCAL_ONLY` 阻断
- 保存失败重试与成功后自动清理本地草稿
- 发布比对签名已改为后端返回的 `published_project_snapshot`,不再依赖 localStorage
### 4.3 门禁与流程
- 已新增 CI 工作流:`.github/workflows/storage-persistence-guard.yml`
- PR / push 到主干时会执行 `storage_persistence_audit.py --fail-on-banned`
## 5. 剩余优化项(可选)
1. 将“高风险清单”阈值从固定 key 规则升级为白名单机制(白名单外一律告警)。
2. 评估是否把 `ProjectDetail` 本地草稿从 localStorage 迁移为后端草稿(可选能力,非当前口径强制项)。