将前端端口号修改为9345，host也做适当修改，防止只能本IP使用。

---

我想做以下改动：
1. 目前，所有页面的header和下方背景之间是明确的分隔。我希望，header和主体背景之间，加入一条高度约20px左右的渐变，使得分割线不那么突兀；
2. header颜色变为天蓝色，比现在再青翠一些；
3. 页面主体，覆盖一层透明度为80%的图片，图片素材为src/styles/background-mask.jpeg；图片不影响所有按钮和文字的交互。

---

去掉叠加的背景图吧

---

针对序号，没有下拉三角的项，也与带下拉三角的项左对齐吧

---

方案管理页面，不再用树状结构来区分主方案和子方案。在下方再规划一块区域，分两个Tab页，一个Tab页是子方案，一个Tab页是配套资料，点击主方案时，下方子方案Tab页中显示相关联的子方案，配套资料Tab页中显示相关联的配套资料（如政策法规）。可切换Tab页。

---

方案管理页面，在右侧方案列表上方增加一个简单的检索框。

---

后台管理页面，同样，不再用树状结构来区分主方案和子方案。在下方再规划一块区域，分两个Tab页，一个Tab页是子方案，一个Tab页是配套资料，点击主方案时，下方子方案Tab页中管理相关联的子方案，配套资料Tab页中管理相关联的配套资料（如政策法规）。可切换Tab页。

---

子方案和配套资料Tab页，增加上传文档和批量导入的相关按钮

---

各界面的左侧“组织机构”，修改为“方案计划分类”，内容为按层级展开的各类场景、样式、情况、版本。你来自己设计层级嵌套方式。
另外，在管理页面，设计针对分类的添加、添加子分类、编辑、删除等按钮。
不要涉及敏感场景，具体内容均使用XXXX场景、XXXX样式字样替代。

---

权限管理处，还使用原先的组织机构进行设置，而非当前的分类。

---

我的项目里还会有个 springboot 后端。请写一个 .gitignore。

---

我还会有python相关的模块，补充到 .gitingore。

----------------------------
你是本项目的“会话2：前端设计与实现工程师”。
项目名称：方案计划文档管理系统。
你的长期职责是负责前端设计实现、前端工程结构、API调用、状态管理、前端测试，以及与后端接口联调。
一、工作边界
你只允许修改 frontend/**。
不修改 backend/**。
不修改MySQL表结构、文件存储逻辑和后端权限规则。
不自行修改以下统领文档：FUNCTION_AND_API_SPECIFICATION.md
CODEX_BACKEND_PERSISTENCE_GUIDE.md

如果需求或接口存在问题，只提交“变更建议”，等待统领会话批准。
不修改现有AI、Milvus或向量化相关内容。
不一次实现全部功能。每次只完成统领会话指定的一个阶段。
不自动提交、合并或推送Git，除非我明确要求。
保留用户已有修改，不覆盖无关文件。
二、必须先阅读
FUNCTION_AND_API_SPECIFICATION.md
CODEX_BACKEND_PERSISTENCE_GUIDE.md
frontend/src/app/App.tsx
frontend/package.json
当前Git状态和现有前端目录结构
三、已确定的核心业务规则
主案可以包含多个子方案。
子方案只能属于一个主案。
共享附件独立存在，不属于某一个主案。
同一个共享附件可以挂载到多个主案。
解除挂载不能删除附件文件。
共享附件对所有状态正常的已登录用户可见、可下载。
共享附件不配置组织和人员权限。
已挂载附件不能直接删除，后端将返回 ATTACHMENT_IN_USE。
前端JSON字段使用 lowerCamelCase。
前端不得根据数据库结构自行设计接口字段。
四、长期实施阶段
F0：前端现状和接口映射分析
F1：API客户端、DTO、错误处理和Mock切换骨架
F2：登录和当前用户接入
F3：组织与分类接入
F4：主案、子方案、共享附件只读接口接入
F5：上传、预览和下载接入
F6：共享附件挂载与解除挂载接入
F7：文档权限接入
F8：日志与统计接入
F9：前端回归和模拟数据清理
在当前轮次只执行F0，不修改任何代码。
五、本轮任务：F0
请只读分析当前前端，并输出：
当前页面、组件和状态结构。
当前所有按钮与FUNCTION_AND_API_SPECIFICATION.md接口的映射表。
哪些按钮已经有模拟交互，哪些是空函数，哪些仍使用模拟数据。
建议的前端目录拆分方案。
建议的API客户端、DTO和状态管理边界。
前端需要后端提供的接口清单，按照开发优先级排序。
当前前端实现与功能规格之间的冲突或疑问。
F1阶段建议修改的具体文件，但不要实际修改。
输出格式固定为：
【阶段】
【现状分析】
【按钮—接口映射】
【拟新增前端模块】
【接口依赖】
【冲突与待确认】
【本轮未执行】
【下一阶段建议】
完成F0后立即停止，不要继续F1。
------------------------------------------
【会话身份】

你是“会话2：前端设计与实现工程师”。

你负责本项目的前端设计、前端实现、前端测试以及后续与后端接口联调。

本轮执行阶段：

F1：前端接口基础设施建设

F0现状分析已经通过统领会话审核。本轮允许修改前端代码，但不得进入具体业务页面的接口联调。

--------------------------------------------------
一、强制加载的契约
--------------------------------------------------

执行任何操作前，必须完整读取以下文件：

1. DMS_FUNCTION_CONTRACT.md
2. DMS_API_CONTRACT.md

文件位置均为当前项目根目录。

这两份文件是本阶段的强制执行契约：

- DMS_FUNCTION_CONTRACT.md 是业务功能、权限、页面行为和范围边界的最高执行依据。
- DMS_API_CONTRACT.md 是接口路径、字段、DTO、枚举、错误码和请求响应格式的最高执行依据。

以下文件只作为背景和补充参考：

3. FUNCTION_AND_API_SPECIFICATION.md
4. CODEX_BACKEND_PERSISTENCE_GUIDE.md

约束优先级为：

1. 用户或统领会话最新明确确认的要求
2. DMS_FUNCTION_CONTRACT.md
3. DMS_API_CONTRACT.md
4. FUNCTION_AND_API_SPECIFICATION.md
5. CODEX_BACKEND_PERSISTENCE_GUIDE.md
6. 当前代码、界面模拟数据和历史实现

如果低优先级内容与新契约冲突，必须以新契约为准。

不得自行修改两份契约。

不得自行增加接口、字段、枚举或兼容废止路径。

如果发现两份新契约之间存在冲突，或者契约内容无法实现，应停止相关部分，记录问题并提交统领会话裁决，不得自行猜测。

--------------------------------------------------
二、本轮目标
--------------------------------------------------

建立一套最小化、统一、可测试的前端接口基础设施，为后续登录、分类、文档、附件、权限和审计页面逐步接入真实后端做好准备。

本轮只实现基础设施，不调用具体业务接口，不替换当前模拟数据。

--------------------------------------------------
三、本轮允许修改范围
--------------------------------------------------

允许修改：

- frontend/src/shared/**
- frontend/package.json
- frontend/package-lock.json
- frontend/tsconfig*.json
- 前端测试配置文件
- 前端环境变量示例文件
- 为接口基础设施所必需的少量前端配置文件

如果当前工程没有shared目录，可以建立符合现有工程结构的等价目录，但必须说明原因。

建议目录：

frontend/src/shared/api/
- config.ts
- types.ts
- enums.ts
- errors.ts
- query.ts
- requestId.ts
- httpClient.ts

frontend/src/shared/auth/
- tokenStore.ts
- authEvents.ts

frontend/src/shared/api/__tests__/
- httpClient.test.ts
- query.test.ts
- tokenStore.test.ts

目录可适当调整，但不得把接口基础设施继续堆入App.tsx。

--------------------------------------------------
四、本轮禁止事项
--------------------------------------------------

本轮禁止：

1. 不修改后端代码。
2. 不修改DMS_FUNCTION_CONTRACT.md。
3. 不修改DMS_API_CONTRACT.md。
4. 不修改FUNCTION_AND_API_SPECIFICATION.md。
5. 不修改CODEX_BACKEND_PERSISTENCE_GUIDE.md。
6. 不替换当前页面模拟数据。
7. 不把登录页接到真实接口。
8. 不把分类树接到真实接口。
9. 不把文档、附件、权限、审计页面接到真实接口。
10. 不创建documentsApi、attachmentsApi、authApi等具体业务接口模块。
11. 不修改现有页面布局、颜色、字号和视觉样式。
12. 不进行App.tsx业务重构。
13. 不引入Redux、MobX、TanStack Query、MSW等大型框架。
14. 不兼容契约中明确废止的接口路径。
15. 不修改现有AI相关功能。
16. 不进行Git提交、推送、合并、重置、清理或覆盖用户改动。
17. 不自行进入F2。

--------------------------------------------------
五、必须实现的接口基础能力
--------------------------------------------------

1. API基础地址

从环境变量读取：

VITE_API_BASE_URL

未配置时默认：

/api/v1

不得在具体组件中硬编码服务器主机名或接口前缀。

2. 统一HTTP客户端

基于原生fetch封装统一客户端，至少支持：

- GET
- POST
- PUT
- DELETE
- JSON请求
- FormData请求
- Blob响应
- 查询参数序列化
- AbortSignal
- 自定义请求头
- Bearer Token
- X-Request-Id

3. 查询参数

必须：

- 自动忽略undefined
- 自动忽略null
- 保留false、0和空字符串的明确语义
- 支持字符串、数字、布尔值和数组
- 数组序列化策略必须固定并有测试
- 不能通过字符串拼接产生未编码参数

4. JSON请求

发送JSON时自动添加：

Content-Type: application/json

FormData请求不得手工添加multipart Content-Type，必须由浏览器生成boundary。

5. Token注入

存在Token时自动添加：

Authorization: Bearer <accessToken>

Token不存在时不得发送空Bearer头。

6. Request ID

每次请求：

- 如果调用方提供X-Request-Id，继续使用；
- 如果没有提供，前端生成UUID；
- 保存请求使用的requestId；
- 优先读取响应头X-Request-Id；
- JSON响应中的requestId需要能够用于错误展示和日志定位。

7. 统一成功响应

严格按照DMS_API_CONTRACT.md处理：

{
  "code": "OK",
  "message": "success",
  "data": {},
  "requestId": "uuid"
}

HTTP状态成功但code不是OK时，不能当作业务成功。

8. 统一错误响应

严格处理：

{
  "code": "ERROR_CODE",
  "message": "错误说明",
  "details": null,
  "requestId": "uuid"
}

建立统一前端异常，例如HttpError，至少保存：

- httpStatus
- code
- message
- details
- requestId
- cause（如果适用）

9. 非JSON错误

后端、代理服务器或网络错误可能返回：

- text/plain
- text/html
- 空响应
- 无法解析的JSON

这些情况必须转换为统一HttpError，不能让页面直接接触JSON解析异常。

10. Blob响应

文件预览和下载接口成功时返回Blob，不使用JSON包装。

Blob处理必须满足：

- 成功时返回Blob以及必要响应头信息；
- 失败响应即使接口期望Blob，也要尝试解析后端JSON错误；
- 能读取Content-Type；
- 能读取Content-Disposition；
- 能取得requestId；
- 不把JSON错误内容当作文件下载。

11. 401处理

收到401时：

- 清理sessionStorage和localStorage中的DMS Token；
- 发布统一“登录失效”事件；
- HTTP客户端不得直接操作具体页面或React组件；
- 后续页面层订阅该事件并跳转登录页；
- 避免多个并发401重复发布大量事件。

12. Token存储

建立统一tokenStore，支持：

- sessionStorage
- localStorage
- 读取当前Token
- 保存Token
- 清理Token
- 区分“保持登录”和“当前会话”
- 禁止保存用户名密码
- 禁止保存明文密码

13. ID类型

接口中所有数据库ID统一定义为字符串，例如：

type Id = string

不得将接口ID定义为number，不得对ID执行数值计算。

14. 分页类型

至少定义：

- PageResult<T>
- page
- pageSize
- total
- totalPages
- items

15. 固定枚举

严格根据DMS_API_CONTRACT.md定义，不得更名或增加兼容值。

至少包括：

- RoleCode
- AllowedModule
- DocumentType
- DocumentStatus
- SecurityLevel
- VisibilityType
- AttachmentType
- SubjectType
- AllowedAction
- CategoryType
- EnabledStatus
- AuditOperationResult
- AuditActionType
- AuditTargetType

前端可以为枚举建立中文显示映射，但提交给后端的值必须使用契约代码。

16. 配置

允许增加前端环境变量示例文件，例如：

VITE_API_BASE_URL=/api/v1

不得填写真实服务器地址、账号、Token或密钥。

--------------------------------------------------
六、测试要求
--------------------------------------------------

建立最小自动化测试，测试不得依赖真实后端。

至少覆盖：

1. 查询参数忽略undefined和null。
2. 查询参数正确编码。
3. 数组参数序列化。
4. JSON成功响应解析。
5. HTTP成功但业务code非OK。
6. 标准JSON错误解析。
7. 非JSON错误解析。
8. 网络错误转换。
9. Bearer Token自动注入。
10. 无Token时不发送Authorization。
11. X-Request-Id生成和透传。
12. 401清理Token并发布登录失效事件。
13. 并发401不会产生失控的重复通知。
14. FormData不手工设置Content-Type。
15. Blob成功响应。
16. Blob接口返回JSON错误。
17. Token在sessionStorage和localStorage中的存取、切换和清理。
18. ID类型保持字符串。

如果当前项目没有测试框架，可以增加最小必要测试依赖，但不得引入与F1无关的框架。

--------------------------------------------------
七、构建与验证
--------------------------------------------------

必须执行并真实报告：

1. npm run build
2. npm run typecheck
3. 前端自动化测试命令
4. 必要的静态检查

如果项目原来没有typecheck命令，应增加：

tsc --noEmit

如果因为当前工程已有问题导致验证失败：

- 不得隐瞒；
- 区分“本轮引入问题”和“已有问题”；
- 只允许修复与F1直接相关的问题；
- 与F1无关的问题记录后停止扩展。

--------------------------------------------------
八、验收标准
--------------------------------------------------

F1完成必须满足：

1. 当前页面视觉和模拟业务行为不变。
2. 接口基础设施没有继续写入App.tsx。
3. 支持JSON、FormData和Blob。
4. Token、401和requestId形成统一机制。
5. 所有接口ID使用字符串。
6. 枚举与DMS_API_CONTRACT.md完全一致。
7. 没有具体业务接口调用。
8. 没有使用废止路径。
9. 自动化测试覆盖关键基础行为。
10. build、typecheck和测试结果真实可复查。

--------------------------------------------------
九、完成报告格式
--------------------------------------------------

完成后必须返回：

1. 阶段结论
   - F1完成、部分完成或阻塞

2. 契约加载情况
   - 确认完整读取了哪些契约文件

3. 修改文件清单
   - 每个文件说明用途

4. 目录结构
   - 展示新增接口基础设施目录

5. 实现说明
   - HTTP客户端
   - Token
   - 401
   - requestId
   - Blob
   - 查询参数
   - 错误处理
   - 枚举和ID类型

6. 验证结果
   - 命令
   - 退出码
   - 通过数量
   - 失败内容

7. 契约符合性检查
   - 对应DMS_FUNCTION_CONTRACT.md哪些章节
   - 对应DMS_API_CONTRACT.md哪些章节
   - 是否存在任何偏差
   - 是否使用了未定义字段或路径
   - 是否误用了废止接口

8. 未实现内容
   - 明确说明仍属于F2及以后阶段的内容

9. 风险和待统领裁决事项

10. 下一阶段建议
   - 只提出F2建议，不得自行执行

本轮到F1完成后立即停止，等待统领会话验收。
------------------------------------------------------------
【阶段】F2：前端认证闭环和导航权限接入

你是会话2：前端设计与实现工程师。

F1已经通过统领会话验收。B2后端应已经提供以下真实接口：

POST /api/v1/auth/login
POST /api/v1/auth/logout
GET  /api/v1/users/me

本轮只接入登录、当前用户恢复、退出和顶部导航模块权限。

分类、文档、附件、挂载、文档权限和审计数据仍保持当前模拟状态，不得提前接入。

一、强制契约

执行前完整读取：

1. DMS_FUNCTION_CONTRACT.md
2. DMS_API_CONTRACT.md

补充参考：

3. FUNCTION_AND_API_SPECIFICATION.md
4. CODEX_BACKEND_PERSISTENCE_GUIDE.md

不得修改两份DMS契约。

不得兼容废止接口。

发现后端实际响应与契约不同，应记录差异并停止相关联调，不得在前端静默兼容错误响应。

二、本轮目标

形成真实认证闭环：

1. 登录页调用真实登录接口。
2. 登录成功保存Token。
3. 根据keepSignedIn选择存储位置。
4. 调用/users/me恢复当前用户。
5. 使用allowedModules控制顶部入口。
6. 订阅统一401登录失效事件。
7. 退出时调用真实logout。
8. 清理前端认证状态并返回登录页。
9. 页面刷新后可以恢复有效会话。
10. 不保存用户名和密码。

三、API模块

允许新增：

frontend/src/features/auth/
- authTypes.ts
- authApi.ts
- authSession.ts
- **tests**/

或符合现有工程结构的等价目录。

实现：

login(request)
logout()
getCurrentUser()

严格使用：

POST /auth/login
POST /auth/logout
GET /users/me

基础地址由F1 HttpClient统一处理，业务模块不得重复拼接/api/v1。

四、DTO

LoginRequest：

{
  "username": "admin",
  "password": "******",
  "keepSignedIn": true
}

LoginResponseData：

{
  "accessToken": "jwt",
  "tokenType": "Bearer",
  "expiresIn": 7200,
  "user": UserSummary
}

UserSummary必须包含：

- id：string
- username
- realName
- organizationId：string或null
- organizationName
- roleCode
- securityLevel
- status
- allowedModules

不得使用number表示ID。

不得增加接口未定义的兼容字段。

五、登录页改造

在保持当前整体视觉风格的前提下：

1. 将“记住密码”语义调整为“保持登录”。
2. 不保存用户名或密码。
3. 用户名、密码为空时前端提示。
4. 点击登录时显示提交状态。
5. 提交期间防止重复登录。
6. 登录失败展示后端message。
7. 能展示requestId或提供可查看的错误编号。
8. 不根据错误判断用户名是否存在。
9. 登录成功后保存Token和当前用户。
10. 根据allowedModules进入第一个允许页面。
11. 不使用硬编码admin账号绕过后端。

六、应用启动恢复

应用初始化时：

1. 检查tokenStore。
2. 无Token直接显示登录页。
3. 有Token时调用GET /users/me。
4. 恢复期间显示与当前风格一致的轻量加载状态。
5. 恢复成功后进入允许模块。
6. 401时由统一机制清理Token并返回登录页。
7. 网络错误和500不得错误清理仍可能有效的Token。
8. 网络错误应提供重试或返回登录页的明确操作。
9. 不把UserSummary长期写入localStorage作为权限事实来源。
10. 页面刷新必须重新调用/users/me。

七、导航权限

顶部入口只能根据后端allowedModules显示：

DOCUMENT_BROWSER：
- 文档浏览

BACKEND_MANAGEMENT：
- 后台管理

AUDIT_LOG：
- 日志审计

要求：

1. 不根据roleCode自行补全模块。
2. 当前页面不在allowedModules中时，切换到第一个允许模块。
3. 没有任何allowedModules时展示无可用模块状态，不得默认进入后台。
4. USER不能看到后台管理和日志审计。
5. ADMIN不能因为前端推断看到未返回模块。
6. AUDITOR只展示后端实际返回的模块。

八、401处理

使用F1的subscribeAuthExpired：

1. 应用层只注册一次。
2. 收到事件后清理当前用户状态。
3. 返回登录页。
4. 显示“登录状态已失效，请重新登录”。
5. 多个并发401只显示一次提示。
6. 不在各页面重复编写401逻辑。

九、退出

点击退出：

1. 调用POST /auth/logout。
2. 成功后清理Token和当前用户。
3. 返回登录页。
4. 请求失败时仍允许清理本地状态并退出前端。
5. 失败时提示“服务端退出未确认”，但不得保留本地Token。
6. 防止重复点击。
7. 不使用直接刷新页面代替状态清理。

十、本地联调

后端本地地址按实际B2运行地址配置。

推荐通过未提交的本地环境变量：

VITE_API_BASE_URL=http://localhost:5000/api/v1

不得把机器专用地址写入生产代码。

至少验证本地开发账号：

- admin
- auditor
- user

密码以B2实际初始化结果为准。

验证：

1. admin登录及导航。
2. auditor登录及导航。
3. user登录及导航。
4. 错误密码。
5. 刷新恢复。
6. 退出。
7. 退出后旧Token失效。
8. 手工构造无效Token。
9. 后端停止时的恢复错误。
10. allowedModules控制。

如果B2接口尚不可用，应只完成代码和模拟Fetch单元测试，并把真实联调标记为阻塞；不得修改后端。

十一、测试

至少覆盖：

1. login请求路径和请求体。
2. logout请求。
3. getCurrentUser请求。
4. 登录成功Token保存。
5. keepSignedIn=false使用sessionStorage。
6. keepSignedIn=true使用localStorage。
7. 登录失败不保存Token。
8. /users/me恢复。
9. 401登录失效。
10. 非401网络错误不错误清除Token。
11. allowedModules过滤导航。
12. 没有模块时的行为。
13. 退出成功。
14. 退出失败仍清理本地状态。
15. ID保持字符串。
16. 不保存用户名和密码。
17. 当前App其他模拟数据未被替换。

可以增加最小必要的React测试依赖，但不得引入大型状态管理框架。

十二、构建验证

必须执行：

- npm run typecheck
- npm run test
- npm run build

如果Vite/esbuild在沙箱中出现spawn EPERM，应按允许的方式在沙箱外重新验证，并如实报告。

十三、禁止事项

- 不修改后端。
- 不修改DMS契约。
- 不接入分类接口。
- 不接入组织和人员接口。
- 不接入文档和附件接口。
- 不接入权限、挂载、文件、审计和统计接口。
- 不重构视觉风格。
- 不引入Redux、MobX或TanStack Query。
- 不升级React Router和Vite，安全升级另行安排。
- 不删除现有模拟业务数据。
- 不提交或推送Git。
- 不进入F3。

十四、完成报告

必须返回：

1. 阶段结论。
2. 契约加载情况。
3. 修改文件清单。
4. 认证状态流转。
5. Token存储行为。
6. /users/me恢复行为。
7. allowedModules导航行为。
8. 401处理。
9. 退出行为。
10. 单元测试结果。
11. 构建结果。
12. 真实后端联调结果。
13. 三类角色验证结果。
14. 契约符合性检查。
15. 是否存在偏差。
16. 未实现内容。
17. F3建议，但不得执行。

本轮完成F2后停止，等待统领会话验收。
-----------------------------------------------
【阶段】F2-V：真实后端认证界面验证

你是会话2：前端设计与实现工程师。

本轮只做F2真实联调验证，原则上不修改业务代码，不进入F3。

一、当前环境

后端已经运行：

http://localhost:8754

健康检查：

GET http://localhost:8754/api/v1/health

前端本地配置已经修正：

VITE_API_BASE_URL=http://localhost:8754/api/v1

本地开发账号：

admin / Admin@123456
auditor / Auditor@123456
user / User@123456

这些仅是本地开发账号。

二、强制读取

完整读取：

1. DMS_FUNCTION_CONTRACT.md
2. DMS_API_CONTRACT.md

不得修改契约。

三、验证内容

启动前端：

npm run dev

验证：

1. health接口可访问。
2. admin能够登录。
3. admin只显示：
   - 文档浏览
   - 后台管理
4. admin不显示日志审计。
5. auditor能够登录。
6. auditor只显示日志审计。
7. user能够登录。
8. user只显示文档浏览。
9. 错误密码显示INVALID_CREDENTIALS对应提示。
10. 登录失败不会误触发“原会话失效”。
11. keepSignedIn=false写入sessionStorage。
12. keepSignedIn=true写入localStorage。
13. 页面刷新后通过/users/me恢复。
14. 退出后返回登录页。
15. 退出后旧Token不能继续调用/users/me。
16. 无效Token触发统一登录失效。
17. 后端停止或网络失败时显示恢复失败，而不是错误清除Token。
18. 其他模拟业务数据保持不变。

四、自动化复验

执行：

npm run typecheck
npm run test
npm run build

预期：

- TypeScript检查通过；
- 6个测试文件、39项测试通过；
- 构建通过；
- 允许保留现有包体积警告。

五、禁止事项

- 不修改后端。
- 不修改契约。
- 不接入分类、组织、人员业务页面。
- 不接入文档、附件、权限和审计接口。
- 不修改视觉风格。
- 不升级依赖。
- 不进入F3。
- 不提交或推送Git。

六、完成报告

返回：

1. 实际前端访问地址。
2. 三类账号验证结果。
3. 导航模块验证结果。
4. Token存储和刷新恢复结果。
5. 错误密码和无效Token结果。
6. 退出结果。
7. 自动化测试和构建结果。
8. 是否存在前后端契约偏差。
9. 是否修改任何文件。

完成F2-V后停止。
--------------------------------------
【阶段】F2-V：真实后端认证联调收尾与最终验收

你是“会话2：前端设计与实现工程师”。

本轮只完成F2认证功能的真实后端联调、页面验证和最终报告。

不得进入F3，不得接入分类、组织、人员、文档、附件、权限、挂载、审计或统计页面接口。

--------------------------------------------------
一、立即终止当前阻塞等待
--------------------------------------------------

不要继续等待当前 npm run dev 命令。

npm run dev 是常驻开发服务器，正常情况下不会自动退出。当前持续等待没有意义。

请立即终止当前阻塞的命令调用。

终止等待不等于必须关闭已经成功启动的Vite服务：

1. 先检查Vite是否已经监听端口。
2. 优先检查5173、5174和5175。
3. 如果已经监听，继续使用该服务。
4. 如果没有监听，再通过独立后台进程或单独终端启动。
5. 不得再次在Codex命令调用中无限等待常驻服务。

--------------------------------------------------
二、强制契约
--------------------------------------------------

开始验证前，必须完整读取：

1. DMS_FUNCTION_CONTRACT.md
2. DMS_API_CONTRACT.md

补充参考：

3. FUNCTION_AND_API_SPECIFICATION.md
4. CODEX_BACKEND_PERSISTENCE_GUIDE.md

约束优先级：

用户或统领会话最新明确要求
→ DMS_FUNCTION_CONTRACT.md
→ DMS_API_CONTRACT.md
→ 两份旧参考文档
→ 当前代码和模拟数据

不得修改两份DMS契约。

如果实际接口与契约不一致：

- 不得在前端增加静默兼容；
- 记录请求、响应、HTTP状态和requestId；
- 在报告中列为契约偏差。

--------------------------------------------------
三、当前环境
--------------------------------------------------

后端服务地址：

http://localhost:8754

健康检查：

GET http://localhost:8754/api/v1/health

前端本地配置应为：

VITE_API_BASE_URL=http://localhost:8754/api/v1

前端开发服务通常为：

http://127.0.0.1:5173

如果5173被占用，Vite可能自动使用5174、5175或其他端口。必须以实际监听端口或Vite启动输出为准。

本地开发账号：

管理员：

admin
Admin@123456

审计员：

auditor
Auditor@123456

普通用户：

user
User@123456

这些账号仅用于本地开发验证，不得写入业务代码、前端默认值或Git跟踪配置。

--------------------------------------------------
四、本轮原则
--------------------------------------------------

本轮原则上只做验证，不修改业务代码。

允许：

- 检查端口和进程；
- 启动或停止本项目的前端开发服务；
- 访问本地前端和后端；
- 使用现有三类开发账号；
- 读取sessionStorage和localStorage；
- 查看浏览器控制台；
- 执行现有测试、类型检查和构建；
- 生成临时验证记录。

禁止：

1. 不修改后端业务代码。
2. 不修改前端业务代码。
3. 不修改两份DMS契约。
4. 不修改模拟业务数据。
5. 不安装新依赖。
6. 不升级Vite、React Router或其他依赖。
7. 不接入F3及以后接口。
8. 不提交、推送、合并或重置Git。
9. 不无限等待常驻服务命令。
10. 不进入F3。

如果发现必须修改代码才能继续：

- 不得自行修改；
- 记录具体问题；
- 输出阻塞原因；
- 等待统领会话裁决。

--------------------------------------------------
五、服务检查
--------------------------------------------------

1. 检查后端健康接口。

预期：

- HTTP 200；
- code=OK；
- data.service=dms；
- data.status=UP；
- 响应包含requestId；
- 响应头包含X-Request-Id。

2. 检查Vite监听端口。

如果已经监听：

- 记录实际端口；
- 不要重复启动第二个Vite实例。

如果没有监听：

- 在独立后台进程或单独终端启动：

npm run dev -- --host 127.0.0.1

启动后只需确认端口可以访问，不要等待该命令结束。

3. 访问前端首页。

记录：

- 实际前端地址；
- HTTP状态；
- 是否出现登录页；
- 是否白屏；
- 是否存在启动错误。

--------------------------------------------------
六、管理员验证
--------------------------------------------------

使用：

admin / Admin@123456

验证：

1. 登录成功。
2. 登录请求调用：
   POST /api/v1/auth/login
3. 登录响应ID为字符串。
4. roleCode=ADMIN。
5. allowedModules严格为：
   - DOCUMENT_BROWSER
   - BACKEND_MANAGEMENT
6. 顶部显示：
   - 文档浏览
   - 后台管理
7. 顶部不显示：
   - 日志审计
8. 登录后进入第一个允许模块。
9. 不根据ADMIN角色自行补出AUDIT_LOG。
10. 现有模拟文档和后台管理内容仍能正常显示。
11. 不出现未处理控制台错误。

--------------------------------------------------
七、审计员验证
--------------------------------------------------

退出管理员账号后，使用：

auditor / Auditor@123456

验证：

1. 登录成功。
2. roleCode=AUDITOR。
3. allowedModules严格为：
   - AUDIT_LOG
4. 顶部只显示日志审计。
5. 不显示文档浏览。
6. 不显示后台管理。
7. 登录后进入日志审计。
8. 当前模拟日志页面仍能正常显示。
9. 不出现未处理控制台错误。

--------------------------------------------------
八、普通用户验证
--------------------------------------------------

退出审计员账号后，使用：

user / User@123456

验证：

1. 登录成功。
2. roleCode=USER。
3. allowedModules严格为：
   - DOCUMENT_BROWSER
4. 顶部只显示文档浏览。
5. 不显示后台管理。
6. 不显示日志审计。
7. 登录后进入文档浏览。
8. 当前模拟文档浏览页面仍能显示。
9. 不出现未处理控制台错误。

--------------------------------------------------
九、错误密码
--------------------------------------------------

使用：

用户名：admin
密码：任意错误密码

验证：

1. 后端返回HTTP 401。
2. 错误码为INVALID_CREDENTIALS。
3. 页面展示明确登录失败提示。
4. 不泄露用户是否存在。
5. 不保存Token。
6. 不保存用户名和密码。
7. 登录接口401不得触发“已有会话失效”提示。
8. 不清理其他可能存在的有效登录会话，除非用户主动切换登录状态。

--------------------------------------------------
十、保持登录和Token存储
--------------------------------------------------

分别验证：

场景A：不勾选“保持登录”

预期：

- Token写入sessionStorage；
- localStorage中没有DMS Token；
- 不保存密码；
- 不持久化UserSummary。

场景B：勾选“保持登录”

预期：

- Token写入localStorage；
- sessionStorage中没有DMS Token；
- 不保存密码；
- 不持久化UserSummary。

切换存储方式时：

- 保存新Token前清理旧位置；
- 两处不能同时存在DMS Token。

--------------------------------------------------
十一、刷新恢复
--------------------------------------------------

登录成功后刷新页面。

验证：

1. 前端读取现有Token。
2. 调用：
   GET /api/v1/users/me
3. 恢复期间显示加载状态。
4. 恢复成功后显示正确用户和正确模块。
5. 不依赖持久化UserSummary恢复权限。
6. 不根据roleCode补全模块。
7. 用户ID仍为字符串。
8. 页面没有明显白屏或错误闪烁。

--------------------------------------------------
十二、退出和旧Token失效
--------------------------------------------------

登录成功后：

1. 在退出前记录当前Token，仅用于本次验证，不得写入日志或报告正文。
2. 点击退出。
3. 验证调用：
   POST /api/v1/auth/logout
4. 退出后回到登录页。
5. sessionStorage和localStorage中的DMS Token均被清理。
6. 当前用户内存状态被清理。
7. 使用退出前旧Token调用：
   GET /api/v1/users/me
8. 预期返回HTTP 401。
9. 记录实际错误码。
10. 错误码优先预期为AUTH_VERSION_MISMATCH。
11. 如果HTTP状态为401但错误码读取失败，应记录原始响应解析情况，不要继续长时间排查。
12. 不得在报告中输出完整Token。

--------------------------------------------------
十三、无效Token
--------------------------------------------------

手工写入一个明显无效的Token，然后刷新页面。

验证：

1. /users/me返回401。
2. 统一401机制清理Token。
3. 返回登录页。
4. 显示：
   登录状态已失效，请重新登录
5. 如有requestId，页面可以展示或定位。
6. 多个并发401不会重复弹出大量提示。

--------------------------------------------------
十四、服务端暂时不可用
--------------------------------------------------

如果能够安全模拟后端不可用，可执行一次。

不得为了验证而修改后端代码。

验证：

1. 已有Token时，/users/me发生网络错误。
2. 前端不能把网络错误误判为401。
3. 不应错误删除仍可能有效的Token。
4. 显示恢复失败页面。
5. 提供重试。
6. 提供返回登录页。
7. 恢复后端后可以重试。

如果停止后端会影响其他会话，跳过此项，并在报告中说明未执行原因。

--------------------------------------------------
十五、页面完整性
--------------------------------------------------

确认认证改造没有破坏原有页面：

1. 登录页布局正常。
2. “保持登录”文案正确。
3. 登录按钮状态正常。
4. 错误提示不遮挡主要控件。
5. 顶部导航没有错位。
6. 文档浏览模拟内容仍存在。
7. 后台管理模拟内容仍存在。
8. 日志审计模拟内容仍存在。
9. 页面无白屏。
10. 页面无明显运行时异常。
11. 浏览器控制台无未处理错误。
12. 不存在调用废止接口的请求。

--------------------------------------------------
十六、自动化复验
--------------------------------------------------

执行：

npm run typecheck
npm run test
npm run build

预期：

- typecheck退出码0；
- 6个测试文件通过；
- 39项测试通过；
- build退出码0；
- 允许保留现有500kB包体积警告；
- 不允许新增测试失败。

如果Vite或Vitest在普通沙箱中出现：

spawn EPERM

应按已允许方式在沙箱外执行一次。

不得因为常驻服务未退出而无限等待。

--------------------------------------------------
十七、浏览器能力不足时的处理
--------------------------------------------------

如果当前Codex环境不具备浏览器交互、Computer Use或等价能力：

1. 不要继续等待。
2. 不要安装新工具。
3. 完成健康接口、真实账号、Token、logout和自动化测试等可以执行的验证。
4. 明确列出无法执行的浏览器交互项目。
5. 在结论中写明：

“真实接口联调通过，自动化测试和构建通过；浏览器人工交互待用户确认。”

6. 随后输出最终报告并停止。

--------------------------------------------------
十八、最终报告格式
--------------------------------------------------

必须按以下结构返回：

1. 阶段结论
   - F2-V通过、部分通过或阻塞

2. 服务状态
   - 后端地址和健康状态
   - 前端实际地址和端口

3. 三类账号结果
   - admin
   - auditor
   - user

4. 导航模块结果
   - 每个角色实际allowedModules
   - 每个角色实际显示菜单

5. 错误密码结果
   - HTTP状态
   - 错误码
   - 页面提示
   - 是否错误触发会话失效

6. Token存储
   - sessionStorage
   - localStorage
   - 是否保存账号密码
   - 是否持久化UserSummary

7. 页面刷新恢复
   - /users/me
   - 加载状态
   - 用户与模块恢复

8. 退出结果
   - logout响应
   - 本地状态清理
   - 旧TokenHTTP状态和错误码

9. 无效Token结果

10. 后端不可用结果
    - 已执行或跳过及原因

11. 页面完整性
    - 登录页
    - 顶部导航
    - 三个业务页面
    - 控制台错误

12. 自动化验证
    - typecheck
    - test
    - build
    - 退出码和通过数量

13. 契约符合性
    - 是否使用废止路径
    - 是否出现未知字段或枚举
    - 是否存在前后端偏差

14. 修改情况
    - 原则上应为未修改业务代码
    - 如发生任何修改必须逐项说明

15. 未验证内容
    - 说明受浏览器能力限制的项目

16. 最终建议
    - 是否可以结束F2
    - 不得进入F3

完成F2-V最终报告后立即停止，不要进入F3。
-----------------------------------------
【阶段任务】F4：接入主案、子方案、共享附件及挂载关系的只读接口

你是本项目的前端设计与实现工程师。本轮只执行 F4，不得进入 F5，不得擅自扩大功能范围。

一、开始前必须加载的约束文档

开始任何分析或修改前，必须完整读取：

1. F:\Project\wsj\document-management-system\DMS_FUNCTION_CONTRACT.md
2. F:\Project\wsj\document-management-system\DMS_API_CONTRACT.md

以下文档只能作为背景参考；如果与上述两份 DMS 契约冲突，必须以上述两份 DMS 契约为准：

3. F:\Project\wsj\document-management-system\FUNCTION_AND_API_SPECIFICATION.md
4. F:\Project\wsj\document-management-system\CODEX_BACKEND_PERSISTENCE_GUIDE.md

还应检查：

5. 后端 OpenAPI：
   F:\Project\wsj\document-management-system\backend\openapi
6. 当前前端 F2、F3 已实现的认证、分类、组织和人员模块。
7. B4 已实现的后端接口和 DTO，不得根据旧模拟数据自行猜测字段。

不得修改上述契约文档。

二、B4 验收状态

B4 已通过统领会话验收。

后端地址：

http://127.0.0.1:8754

健康接口：

GET /api/v1/health

当前已实现并验收通过的 B4 只读接口只有：

1. GET /api/v1/documents
2. GET /api/v1/documents/{id}
3. GET /api/v1/main-plans/{id}/sub-plans
4. GET /api/v1/attachments
5. GET /api/v1/attachments/{id}
6. GET /api/v1/attachments/{id}/main-plans
7. GET /api/v1/main-plans/{id}/attachments

不得调用不存在的文档写接口。

三、本轮目标

将前端现有的主案、子方案、共享附件和挂载关系展示，从模拟数据切换到 B4 的真实只读接口。

本轮只打通“查询和展示闭环”，不得实现上传、编辑、删除、权限保存、挂载、解除挂载、预览或下载文件流。

本轮完成后应达到：

1. 文档浏览页使用真实主案和子方案数据。
2. 后台管理页使用真实主案、子方案和共享附件数据。
3. 选中主案后，可查看直属子方案和已挂载共享附件。
4. 共享附件作为独立、全员共享的数据资源展示。
5. 可查询某个共享附件挂载到了哪些主案。
6. 列表、详情、分页、搜索、筛选和排序严格遵守接口契约。
7. 不再使用模拟主案、模拟子方案、模拟附件或模拟挂载关系作为业务数据源。

四、必须遵守的业务规则

1. 文档类型：

   - MAIN：主案
   - SUB_PLAN：子方案
   - ATTACHMENT：共享附件

2. 方案列表只能展示 MAIN 和 SUB_PLAN，不得混入 ATTACHMENT。

3. 共享附件列表只展示 ATTACHMENT。

4. 子方案的可见范围和 ACL 由后端动态继承根主案。

   前端不得：

   - 本地复制主案权限；
   - 本地推导子方案权限；
   - 将子方案当作独立权限对象；
   - 根据角色自行补全后端未返回的权限。

5. 共享附件对所有有效登录用户共享，不使用方案 ACL。

6. 前端只能根据后端返回的 `allowedActions` 控制操作入口。

   不得仅根据 `roleCode` 猜测查看、下载、编辑、删除、权限或挂载能力。

7. `allowedActions` 在 B4 只表达授权能力，不代表对应写接口已经实现。

   因此：

   - 可以根据 `allowedActions`决定按钮是否可见或禁用；
   - 但不得调用尚未实现的写接口；
   - 对尚未实现的功能，应明确提示“该功能将在后续阶段接入”；
   - 不得弹出虚假的“操作成功”。

8. 所有业务 ID 必须按字符串处理。

   禁止：

   - 转成 number；
   - 使用 `parseInt`；
   - 使用数值运算；
   - 使用会丢失精度的类型定义。

9. 时间使用后端返回的 UTC `Z` 格式解析并展示，不得修改接口时间语义。

10. `tags` 必须按数组处理，不得兼容逗号字符串等非契约格式。

11. 前端不得访问或展示：

   - `fileRelativePath`
   - 服务器绝对路径
   - 内部 ACL 查询细节
   - 密码、Token 或其他敏感字段

五、文档浏览页改造要求

（一）列表标签

保留现有三个标签：

1. 主案列表
2. 全部方案
3. 最近更新

分别使用真实接口实现：

1. 主案列表：

   - 调用 `GET /api/v1/documents`
   - 仅查询 MAIN
   - 不展示子方案和附件

2. 全部方案：

   - 调用 `GET /api/v1/documents`
   - 展示 MAIN 和 SUB_PLAN
   - 不展示 ATTACHMENT
   - 如果接口需要分别查询后合并，必须先确认契约是否允许；
   - 禁止绕开契约自行进行不稳定分页合并。

3. 最近更新：

   - 使用契约规定的更新时间排序参数；
   - 展示 MAIN 和 SUB_PLAN；
   - 不展示 ATTACHMENT；
   - 不得使用前端模拟排序替代后端分页排序。

具体查询参数、排序字段和枚举值必须以 DMS API 契约及 OpenAPI 为准，不得凭经验命名。

（二）分类联动

F3 已接入真实分类树。

点击分类后：

- 将选中的分类字符串 ID 按契约传给文档查询接口；
- 展示对应分类下的真实方案；
- 分类切换时重置文档分页；
- 不得继续只更新本地选中状态而不查询文档；
- 不得在前端根据分类名称过滤模拟数组。

如果后端接口对主案和子方案的分类筛选规则不同，严格按契约处理。

（三）搜索

使用后端文档查询接口实现真实搜索：

- 搜索字段和 `keyword` 语义以契约为准；
- 保留合理防抖，建议复用 F3 的 300ms 机制；
- 使用 AbortController 取消旧请求；
- 增加请求版本保护，旧响应不得覆盖新响应；
- 搜索条件变化后回到第一页；
- 清空搜索后恢复正常列表；
- 不得在当前页数据上做伪全量搜索。

关键词高亮仅用于展示，不得改变原始 DTO。

（四）分页、排序与筛选

必须接入后端分页：

- page
- pageSize
- total

具体字段以契约为准。

要求：

- 不得把后端分页数据误当作全量数据；
- pageSize 不超过后端上限 100；
- 排序值只能使用契约白名单；
- 切换标签、分类、关键词或筛选条件时重置页码；
- 请求失败不得保留成“加载成功”状态。

（五）文档详情

点击“查看”或“查看详情”时：

- 调用 `GET /api/v1/documents/{id}`；
- 展示真实详情字段；
- 正确展示名称、概述、类型、状态、密级、分类、标签、创建人、更新时间、计数等契约允许字段；
- 详情接口成功会增加后端查看次数，前端不得重复请求详情；
- React StrictMode、状态更新或弹框重复渲染不得导致无意的重复详情请求；
- 关闭再重新打开时是否重新请求，应采用明确、可测试的策略。

B4 尚未实现真实文件预览，因此：

- 不得把详情接口伪装成 Word 预览接口；
- 不得生成假文档内容；
- 可以保留统一详情弹框；
- 预览区域应明确提示“文件预览将在后续阶段接入”。

六、后台管理页改造要求

（一）主案管理列表

后台管理页主案列表改为真实接口：

- 仅加载 MAIN；
- 支持真实分页、搜索、分类筛选和排序；
- 选中行状态使用字符串 ID；
- 列表展示字段以契约 DTO 为准；
- 不得继续使用模拟主案数组。

本轮后端没有文档新增、编辑、删除和权限持久化接口，因此相关按钮：

- 不得调用虚假或不存在接口；
- 不得提示“保存成功”“删除成功”；
- 可以保留入口并明确标记“后续阶段接入”；
- 或按 `allowedActions` 和当前阶段能力合理禁用。

不要删除现有页面结构，避免为后续阶段重复重建。

（二）选中主案后的子方案区域

用户选中主案后，调用：

GET /api/v1/main-plans/{id}/sub-plans

要求：

- 只显示当前主案的直属子方案；
- 不得从全部文档列表前端筛选替代该接口；
- 主案切换时取消旧请求；
- 旧主案响应不得覆盖当前选择；
- 正确处理加载、空数据、失败和重试；
- 未选中主案时显示明确空状态；
- 选中子方案时，不加载主案关联管理区。

点击子方案详情时，可调用：

GET /api/v1/documents/{id}

但必须避免重复请求导致查看次数被重复增加。

（三）选中主案后的配套资料区域

现有“配套资料”应调整为“当前主案已挂载的共享附件”，调用：

GET /api/v1/main-plans/{id}/attachments

要求：

- 显示当前主案有效挂载的共享附件；
- 使用后端返回的 `bindingId`、`bindingSortNo` 和附件 DTO；
- `bindingId` 必须为字符串；
- 按后端返回顺序展示；
- 不得复制附件文件或附件元数据到主案本地状态；
- 不得把附件当作主案子文档；
- 主案切换时重新加载；
- 正确处理加载、空数据、失败和重试。

本轮没有挂载写接口，因此“挂载资料”和“解除挂载”不得执行真实写操作，也不得提示成功。

七、共享附件库要求

后台管理页应提供独立的共享附件库视图或区域，调用：

GET /api/v1/attachments

需要支持契约允许的：

- 关键词搜索；
- 附件类型筛选；
- 文件扩展名筛选；
- 更新时间筛选；
- 分页；
- 排序。

具体参数必须以契约和 OpenAPI 为准。

附件列表至少应合理展示：

- 附件名称；
- 附件类型；
- 文件扩展名；
- 文件大小；
- 概述；
- 标签；
- 更新时间；
- 已挂载主案数量；
- 契约允许的操作。

点击附件详情时调用：

GET /api/v1/attachments/{id}

详情成功会增加查看次数，因此必须防止重复请求。

查看附件挂载位置时调用：

GET /api/v1/attachments/{id}/main-plans

要求：

- 展示该附件当前挂载到的有效主案；
- 正确使用字符串 `bindingId` 和主案 ID；
- 不得从全部主案前端反向推导挂载关系；
- 无挂载时显示明确空状态；
- 不得实现解除挂载。

八、角色验证要求

必须使用真实接口验证三类账号：

1. ADMIN

   - 可以进入后台管理；
   - 可以浏览方案与共享附件；
   - 页面操作入口以 `allowedActions` 为准；
   - 尚未实现的写操作不得伪成功。

2. USER

   - 只显示后端授权可见且符合状态、密级和 ACL 的方案；
   - 可以查看共享附件；
   - 不得因为前端本地过滤错误而看到无权方案。

3. AUDITOR

   - 默认不能浏览方案；
   - 后端返回 403 `DOCUMENT_VIEW_FORBIDDEN` 时，前端应正确处理；
   - 不得把 403 错误渲染为“空列表”；
   - 不得通过前端角色判断绕过后端验证。

本地开发账号只能用于人工联调，禁止写入前端源码、测试快照、配置文件或提交记录。

九、DTO和前端结构要求

沿用 F2、F3 已建立的模块化结构。

建议至少拆分：

- 文档 DTO 和类型；
- 文档 API；
- 文档运行时校验；
- 文档查询状态；
- 附件 DTO 和类型；
- 附件 API；
- 附件运行时校验；
- 挂载关系 DTO；
- 页面组合逻辑。

要求：

1. HTTP 调用、DTO 校验、异步状态和页面组件分离。
2. 不引入大型状态管理框架。
3. 不在 `App.tsx` 内堆积所有接口和 DTO 解析逻辑。
4. 严格校验：

   - 字符串 ID；
   - 文档类型；
   - 文档状态；
   - 密级；
   - 可见范围；
   - `allowedActions`；
   - `tags` 数组；
   - UTC 时间；
   - 分页字段；
   - 挂载关系字段。

5. 拒绝：

   - number ID；
   - snake_case 字段替代 camelCase；
   - 未知枚举；
   - 缺少必要字段；
   - 用默认值静默掩盖后端契约错误。

6. DTO 契约错误应进入明确错误状态，并保留 `requestId`。

十、统一错误处理

必须覆盖：

- 400：查询参数错误；
- 401：统一登录失效处理；
- 403：无文档访问权限；
- 404：文档或关系不存在；
- 409：如后端只读查询中存在契约规定的冲突；
- 500：服务器错误；
- 网络错误；
- DTO 契约错误。

要求：

- 错误尽可能显示或保留 `requestId`；
- 401 继续复用 F2 的统一认证失效机制；
- 403 不得误判为未登录；
- 网络错误不得清理有效 Token；
- 请求失败不得进入成功状态；
- 空数据和请求失败必须是两种不同状态；
- 不得静默回退到模拟数据。

十一、本轮明确禁止事项

不得：

1. 修改 backend/**。
2. 修改两份 DMS 契约。
3. 实现上传文档。
4. 实现批量导入。
5. 实现文档编辑。
6. 实现文档删除。
7. 实现文档恢复。
8. 实现任何 `/documents/{id}/restore` 调用。
9. 实现真实预览。
10. 实现真实下载文件流。
11. 实现挂载或解除挂载。
12. 实现权限保存。
13. 实现审计查询或统计接口。
14. 修改 AI 模块。
15. 创建新的后端测试数据库。
16. 安装或升级无关依赖。
17. 提交、推送、合并或重置 Git。
18. 进入 F5。

特别注意：

第一阶段明确不提供文档恢复接口和恢复页面。即使旧文档、旧代码或 B5 建议中出现恢复，也不得实现。

十二、测试要求

必须新增或调整前端自动化测试，至少覆盖：

1. 7 个 B4 API 的路径和请求参数。
2. API 模块不重复拼接 `/api/v1`。
3. 文档和附件 DTO 正常解析。
4. number ID 被拒绝。
5. snake_case 替代字段被拒绝。
6. 未知枚举被拒绝。
7. `tags` 非数组被拒绝。
8. 主案列表仅展示 MAIN。
9. 全部方案不展示 ATTACHMENT。
10. 分类、关键词、标签切换后分页重置。
11. 旧请求响应不能覆盖新请求。
12. 选中主案后加载直属子方案。
13. 选中主案后加载已挂载附件。
14. 附件反向查询挂载主案。
15. 空数据、失败和重试。
16. 403 不被渲染为空列表。
17. 401 复用统一认证失效处理。
18. 详情请求不会因重复渲染而重复触发。
19. `allowedActions` 不由前端根据角色自行补全。
20. 不调用上传、编辑、删除、权限写、挂载写、预览、下载和恢复接口。
21. F2 认证测试继续通过。
22. F3 分类、组织和人员测试继续通过。

十三、实际验证要求

在自动化测试之外，使用真实后端完成联调验证：

1. 后端健康检查正常。
2. ADMIN：

   - 主案列表；
   - 全部方案；
   - 最近更新；
   - 分类筛选；
   - 关键词搜索；
   - 分页和排序；
   - 主案详情；
   - 直属子方案；
   - 已挂载附件；
   - 共享附件列表；
   - 附件详情；
   - 附件挂载主案列表。

3. USER：

   - 只能看到有权访问的方案；
   - 能查看共享附件。

4. AUDITOR：

   - 方案查询的 403 被正确展示；
   - 不被错误显示为空列表。

5. 浏览器刷新后认证恢复正常。
6. 页面无白屏。
7. 浏览器无未处理运行时异常。
8. Network 中不出现废止接口或 F5 写接口。
9. 确认详情弹框没有因重复请求导致查看计数异常增长。

如果当前环境没有浏览器交互能力：

- 完成能够执行的接口、测试和构建验证；
- 明确记录“真实接口联调通过，浏览器人工交互待确认”；
- 不得无限等待常驻 Vite 命令。

Vite 必须在后台或独立终端启动，不得把 `npm run dev` 当作会自动退出的命令持续等待。

十四、完成前必须执行

至少执行：

1. npm run typecheck
2. npm run test
3. npm run build

若构建因沙箱 `spawn EPERM` 失败，应在获得授权后于沙箱外复验；不得把环境权限问题误判为业务代码失败。

不得为了通过测试而削弱 DTO 校验、跳过核心测试或恢复模拟数据回退。

十五、阻塞处理规则

出现以下情况时必须立即停止修改并提交统领裁决：

1. 契约与 OpenAPI 字段不一致。
2. 前端设计需要后端提供第 8 个新接口。
3. 现有 B4 接口无法完成规定展示。
4. 需要改变字符串 ID、枚举或 `allowedActions` 语义。
5. 需要调用上传、编辑、删除、权限、挂载写、预览、下载或恢复接口。
6. 需要修改后端代码。
7. 需要修改两份契约。
8. 权限规则无法按后端响应表达。
9. 真实接口与自动化测试结果存在无法解释的偏差。

不得自行兼容、猜测或扩展接口。

十六、最终报告格式

完成后必须输出：

1. 阶段结论：F4 是否完成，是否停止在 F4。
2. 契约加载情况。
3. 修改文件清单。
4. 前端模块结构。
5. 7 个只读接口的接入情况。
6. 文档 DTO 和运行时校验结果。
7. 文档浏览页接入结果。
8. 分类、搜索、分页、筛选和排序结果。
9. 主案详情结果及重复请求控制。
10. 后台主案管理列表结果。
11. 直属子方案加载结果。
12. 当前主案已挂载附件结果。
13. 独立共享附件库结果。
14. 附件反向挂载查询结果。
15. `allowedActions` 处理方式。
16. ADMIN、USER、AUDITOR 角色验证结果。
17. 错误处理结果。
18. 自动化测试结果。
19. typecheck 和 build 结果。
20. 真实后端联调结果。
21. 是否调用任何 F5 或废止接口。
22. 契约符合性检查。
23. 是否存在偏差。
24. 未实现内容。
25. F5 建议，但不得执行。

再次强调：

- 本轮只执行 F4。
- 不修改后端。
- 不实现写操作。
- 不实现预览和下载文件流。
- 不实现恢复功能。
- 不进入 F5。
- 不提交或推送 Git。
----------------------------------------------------------
【F4-C1 统领裁决】解除 F4 契约阻塞，继续执行 F4

统领会话已审查你提交的两个阻塞问题。

裁决结果：采用“调整 F4 验收要求”方案。

不修改：

- DMS_FUNCTION_CONTRACT.md
- DMS_API_CONTRACT.md
- backend/**
- OpenAPI
- B4 接口和 DTO

本裁决用于修正上一版 F4 阶段任务中超出正式契约的要求，不属于接口契约变更。

一、附件列表 fileSize 裁决

`GET /api/v1/attachments` 返回 `DocumentSummary`，正式契约没有要求该 DTO 包含 `fileSize`。

因此调整为：

1. 共享附件列表不要求展示文件大小。
2. 列表严格展示 `DocumentSummary` 实际提供的字段。
3. `fileSize` 只在用户主动打开附件详情，并成功调用：

   GET /api/v1/attachments/{id}

   后根据 `DocumentDetail` 展示。

4. 禁止为补齐列表文件大小逐项调用附件详情。
5. 禁止形成 N+1 请求。
6. 禁止在附件列表中伪造、估算或缓存推导 `fileSize`。
7. 禁止因为列表缺少 `fileSize` 而修改后端或扩展 DTO。
8. 附件详情仍需防止重复渲染、React StrictMode或状态变化造成重复请求和查看计数异常增加。

共享附件列表应根据 `DocumentSummary` 和正式契约合理展示已有字段，例如：

- 附件名称；
- 附件类型；
- 文件扩展名；
- 概述；
- 标签；
- 更新时间；
- 已挂载主案数量；
- allowedActions允许表达的操作。

仅展示接口实际返回且契约定义的字段，不得自行补字段。

二、附件反向挂载查询 bindingId 裁决

`GET /api/v1/attachments/{id}/main-plans` 返回 `MainPlanBrief`。

正式契约和 OpenAPI 没有要求 `MainPlanBrief` 返回 `bindingId`，因此调整为：

1. 附件反向挂载列表只需要使用主案字符串 `id`。
2. 不要求该接口返回或使用 `bindingId`。
3. 不得根据主案ID和附件ID拼接、推导或伪造 `bindingId`。
4. 不得通过额外请求反向获取 `bindingId`。
5. 本轮反向列表仅用于展示“当前附件挂载到了哪些主案”。
6. 由于 F4 不实现解除挂载，该页面不需要依赖 `bindingId`执行写操作。
7. 可以使用主案字符串 `id`作为：

   - React列表key；
   - 主案详情或选中状态标识；
   - 页面导航或展示标识。

8. `MainPlanBrief` 中的以下字段按契约使用：

   - id
   - documentName
   - categoryName
   - securityLevel

三、正向挂载关系保持原要求

对于：

GET /api/v1/main-plans/{id}/attachments

仍按正式契约处理：

- 使用后端返回的字符串 `bindingId`；
- 使用后端返回的 `bindingSortNo`；
- 使用附件字符串 ID；
- 按后端顺序展示当前主案已挂载附件；
- 不得复制附件文件；
- 不得伪造挂载关系；
- 不得实现解除挂载。

也就是说：

- 主案 → 已挂载附件：存在 `bindingId`、`bindingSortNo`；
- 附件 → 已挂载主案：只返回 `MainPlanBrief`，不要求 `bindingId`。

这是两个接口不同的正式响应模型，前端应分别建模，不得强行统一DTO。

四、测试要求同步调整

继续 F4 时，自动化测试应按以下规则编写：

1. 验证附件列表 DTO 不强制要求 `fileSize`。
2. 验证附件详情 DTO 正确解析 `fileSize`。
3. 验证附件列表不会为了补齐 `fileSize`逐项调用详情接口。
4. 验证打开附件详情时只发起一次必要的详情请求。
5. 验证反向挂载 `MainPlanBrief` 不要求 `bindingId`。
6. 验证反向挂载列表使用字符串主案 ID。
7. 验证反向挂载列表不会伪造或推导 `bindingId`。
8. 验证正向挂载关系列表严格解析字符串 `bindingId` 和 `bindingSortNo`。
9. 正向挂载 DTO 与反向挂载 DTO 必须分开定义或严格区分。
10. 不得为了通过测试放宽其他 DTO 的严格校验。

五、继续执行要求

现在解除 F4 阻塞，可以继续原 F4 任务。

继续执行前：

1. 记录已收到 `F4-C1` 统领裁决。
2. 按本裁决修正对上一版 F4 提示词的理解。
3. 继续实施：

   - F4 DTO/API模块；
   - 文档浏览页真实数据接入；
   - 后台主案列表真实接入；
   - 主案直属子方案接入；
   - 主案已挂载共享附件接入；
   - 独立共享附件库接入；
   - 附件反向挂载主案查询；
   - F4自动化测试；
   - typecheck、test、build；
   - 真实后端联调。

六、阶段边界保持不变

仍然不得：

- 修改后端；
- 修改契约；
- 修改OpenAPI；
- 实现上传；
- 实现编辑；
- 实现删除；
- 实现恢复；
- 实现权限保存；
- 实现挂载或解除挂载；
- 实现真实文件预览；
- 实现真实文件下载；
- 调用任何F5接口；
- 进入F5；
- 提交或推送Git。

如果继续执行过程中发现新的正式契约与真实响应不一致，应再次停止并提交统领裁决。

七、最终报告补充要求

F4最终报告中增加“F4-C1裁决执行情况”，明确说明：

1. 附件列表未展示 `fileSize`。
2. `fileSize`只在附件详情展示。
3. 未产生补齐文件大小的N+1请求。
4. 反向挂载列表只使用主案字符串ID。
5. 反向挂载列表未要求、推导或伪造 `bindingId`。
6. 正向挂载列表正常使用后端返回的 `bindingId`和`bindingSortNo`。
7. 该处理依据统领会话F4-C1裁决，不计为契约偏差。

现在继续执行F4，完成后停止，不得进入F5。
-------------------------------------------------------
【阶段任务】F5：接入文档与共享附件的上传、批量导入、编辑、逻辑删除、预览和下载

你是本项目的前端设计与实现工程师。本轮只执行 F5，不得进入 F6，不得修改后端代码。

一、开始前必须加载的约束

开始分析或修改前，必须完整读取：

1. F:\Project\wsj\document-management-system\DMS_FUNCTION_CONTRACT.md
2. F:\Project\wsj\document-management-system\DMS_API_CONTRACT.md

以下文档只能作为背景参考，如有冲突，以上述两份DMS契约为准：

3. F:\Project\wsj\document-management-system\FUNCTION_AND_API_SPECIFICATION.md
4. F:\Project\wsj\document-management-system\CODEX_BACKEND_PERSISTENCE_GUIDE.md

同时检查：

5. backend/openapi/**
6. 当前F2—F4前端模块
7. 当前认证、分类、组织、人员、文档和附件API封装
8. 当前统一HTTP客户端、Token存储和401处理
9. B5后端真实接口和响应
10. 当前页面已有上传、批量导入、编辑、删除、查看、下载等交互入口

不得修改两份DMS契约。

二、B5验收状态

B5后端已通过统领验收。

当前业务后端：

http://127.0.0.1:8754

健康接口：

GET /api/v1/health

B5新增并验收通过的10个接口：

方案文档：

1. POST /api/v1/documents
2. POST /api/v1/documents/batch-import
3. PUT /api/v1/documents/{id}
4. DELETE /api/v1/documents/{id}

共享附件：

5. POST /api/v1/attachments
6. POST /api/v1/attachments/batch-import
7. PUT /api/v1/attachments/{id}
8. DELETE /api/v1/attachments/{id}

统一文件读取：

9. GET /api/v1/documents/{id}/preview
10. GET /api/v1/documents/{id}/download

F4原有7个只读接口继续使用，不得废弃或重新设计路径。

三、本轮目标

完成以下前端真实业务闭环：

1. 单个上传主案。
2. 单个上传子方案。
3. 多文件批量导入方案。
4. 编辑主案和子方案元数据。
5. 逻辑删除主案和子方案。
6. 单个上传共享附件。
7. 多文件批量导入共享附件。
8. 编辑共享附件元数据。
9. 逻辑删除未挂载共享附件。
10. PDF在线预览。
11. Office文件明确提示不支持在线预览，并提供下载入口。
12. 主案、子方案和共享附件真实文件下载。
13. 所有成功操作后重新加载真实列表和详情。
14. 所有失败操作显示后端错误和requestId。
15. 所有按钮继续受后端 `allowedActions`约束。

四、本轮明确不实现

不得实现：

1. 附件挂载。
2. 解除附件挂载。
3. 权限读取。
4. 权限保存。
5. 审计日志查询。
6. 统计接口。
7. 文档恢复。
8. 恢复按钮或恢复页面。
9. 文件替换。
10. 文档历史版本。
11. 强制删除已挂载附件。
12. Office转PDF。
13. Office伪在线预览。
14. Milvus或向量化。
15. AI模块改造。
16. backend/**修改。
17. OpenAPI修改。
18. 契约修改。
19. 新建数据库。
20. 进入F6。
21. Git提交、推送、合并或重置。

特别禁止调用：

- `/documents/{id}/restore`
- `/attachments/{id}/preview`
- `/attachments/{id}/download`
- 任何B6挂载写或权限写接口

附件预览和下载必须继续使用统一路径：

- `/documents/{id}/preview`
- `/documents/{id}/download`

五、API基础设施改造

在现有统一HTTP客户端基础上，增加或完善：

1. multipart/form-data请求能力；
2. Blob响应能力；
3. Content-Disposition文件名解析；
4. X-Request-Id响应头读取；
5. 413和415等非JSON/JSON错误处理；
6. 请求取消；
7. 重复提交保护。

要求：

- FormData请求不得手工设置 `Content-Type`；
- 浏览器必须自动生成multipart boundary；
- 继续由统一客户端添加Bearer Token；
- 业务模块不得重复拼接 `/api/v1`；
- Blob成功响应与JSON错误响应必须正确区分；
- Blob接口返回JSON错误时仍须解析统一错误结构；
- 401继续复用F2统一认证失效机制；
- 网络错误不得清除仍有效的Token；
- 不得把文件正文输出到控制台。

六、上传文件的前端规则

允许用户选择：

- .doc
- .docx
- .pdf
- .xls
- .xlsx

要求：

1. `accept`仅用于文件选择提示，不能代替后端安全校验。
2. 前端可以检查扩展名和空文件，但不得声称已完成文件安全认证。
3. 最终文件类型、容器结构和大小以服务端校验为准。
4. 不硬编码可能与后端配置不一致的文件大小限制。
5. 服务端返回413时展示明确的文件过大提示。
6. 服务端返回 `UNSUPPORTED_FILE_TYPE`时展示文件类型或内容不符合要求。
7. 文件名只用于展示。
8. 不在localStorage或sessionStorage保存文件内容、metadata草稿或路径。
9. 关闭弹框后清理File对象和临时页面状态。
10. 上传过程中禁用重复提交。
11. 不伪造上传进度；如果没有真实进度信息，只显示“正在上传/处理中”。

七、单个上传主案和子方案

接入：

POST /api/v1/documents

请求：

- file
- metadata：JSON字符串

主案表单应包含：

- documentName
- documentType=MAIN
- summary
- categoryId
- securityLevel
- visibilityType
- status
- tags

子方案表单应包含：

- documentName
- documentType=SUB_PLAN
- parentDocumentId
- summary
- categoryId
- securityLevel
- visibilityType
- status
- tags

交互要求：

1. 支持明确选择“主案”或“子方案”。
2. 上传主案时不提交parentDocumentId。
3. 上传子方案时必须选择有效主案。
4. 主案选择数据来自真实接口，不得使用模拟数组。
5. 分类选择使用F3真实分类树。
6. ID按字符串提交，不得转成number。
7. tags按字符串数组提交。
8. 不提交：

   - fileRelativePath
   - fileHash
   - fileSize
   - mimeType
   - categoryName
   - categoryPath
   - rootDocumentId
   - rowVersion
   - 操作人字段

9. 成功必须以服务端返回的 `DocumentDetail`为准。
10. 成功后：

   - 关闭或重置表单；
   - 刷新相关文档列表；
   - 新增子方案时刷新主案直属子方案；
   - 刷新相关计数；
   - 不自行给本地计数加一替代后端结果。

11. 失败时保留用户已填metadata，便于修正后重试。
12. 文件失败时不得提示业务记录已创建。

八、方案批量导入

接入：

POST /api/v1/documents/batch-import

管理页面中的“批量导入”必须支持：

- 点击选择多个文件；
- 拖入多个文件；
- 删除尚未提交的某个文件；
- 为每个文件填写或调整metadata；
- 保持文件顺序和items顺序一致。

请求必须使用：

- 重复的 `files`字段；
- `items` JSON数组字符串；
- `files[n]`与`items[n]`严格一一对应。

要求：

1. 每个文件有独立metadata。
2. 主案和子方案字段规则与单文件上传一致。
3. 子方案必须选择父主案。
4. 前端提交前检查files和items数量一致。
5. 不得根据文件名自动猜测密级、分类或父主案。
6. 可以用文件名去除扩展名作为名称初始建议，但必须允许用户修改。
7. 后端整体返回HTTP 200不代表每个文件都成功。
8. 必须逐项展示：

   - 原始文件名；
   - 成功或失败；
   - 成功文档名称；
   - errorCode；
   - errorMessage。

9. 展示：

   - total；
   - successCount；
   - failureCount。

10. 部分失败时不得把整个批次提示为“全部成功”。
11. 支持仅保留失败项，供用户修正后重新提交。
12. 重试失败项时必须生成新的files/items对应关系。
13. 不得自动重复提交已成功项。
14. 成功项完成后刷新真实文档列表。

九、编辑主案和子方案

接入：

PUT /api/v1/documents/{id}

只允许提交：

- documentName
- summary
- categoryId
- securityLevel
- visibilityType
- status
- tags
- rowVersion

不得提交或修改：

- documentType
- parentDocumentId
- rootDocumentId
- originalFileName
- fileRelativePath
- fileExtension
- mimeType
- fileSize
- fileHash
- 文件正文

要求：

1. 编辑表单必须使用最新详情数据及rowVersion。
2. 文件名称、扩展名和哈希等文件身份只读展示。
3. 不提供“替换文件”入口。
4. 成功后使用服务端返回的最新 `DocumentDetail`更新页面。
5. 刷新列表、详情、分类路径及相关区域。
6. `DATA_VERSION_CONFLICT`时：

   - 不覆盖服务端数据；
   - 明确提示数据已被其他操作修改；
   - 展示requestId；
   - 重新加载最新详情和列表；
   - 用户确认后重新编辑。

7. 不得在冲突后使用旧rowVersion自动重试写入。
8. 编辑不得伪造成功。

十、删除主案和子方案

接入：

DELETE /api/v1/documents/{id}?rowVersion=<integer>

删除确认必须明确说明：

- 这是逻辑删除；
- 原始文件不会立即物理删除；
- 本期不提供恢复功能。

要求：

1. rowVersion来自最新详情或列表。
2. 仅在 `allowedActions`包含DELETE时显示可执行入口。
3. 删除主案前不得在前端假定无子方案，必须以服务端结果为准。
4. `MAIN_PLAN_HAS_CHILDREN`时：

   - 明确提示存在有效子方案；
   - 不提示删除成功；
   - 不自动递归删除；
   - 不提供“强制删除”。

5. `DATA_VERSION_CONFLICT`时重新加载最新数据。
6. 成功后：

   - 从当前选中状态移除；
   - 关闭详情弹框；
   - 清理该文档详情缓存；
   - 刷新文档列表；
   - 刷新主案直属子方案；
   - 刷新相关计数和分类展示。

7. 不调用恢复接口。
8. 不在前端保留“回收站”或“撤销删除”假功能。

十一、单个上传共享附件

接入：

POST /api/v1/attachments

请求：

- file
- metadata：JSON字符串

表单字段：

- documentName
- attachmentType
- summary
- tags

不得提交：

- documentType
- securityLevel
- visibilityType
- categoryId
- parentDocumentId
- rootDocumentId
- ACL
- 文件路径
- 文件哈希
- 操作人字段

页面应明确说明：

- 共享附件面向所有有效登录用户共享；
- 不配置组织或人员权限；
- 上传后可在后续阶段挂载到多个主案。

成功后：

- 使用服务端 `DocumentDetail`；
- 刷新共享附件库；
- 重置上传表单；
- 不伪造挂载关系。

十二、共享附件批量导入

接入：

POST /api/v1/attachments/batch-import

必须支持：

- 拖入多个文件；
- 选择多个文件；
- 删除待上传项；
- 为每个文件填写附件名称、附件类型、概述和标签；
- 保持files和items顺序一致；
- 显示逐文件成功/失败结果；
- 支持只重试失败项。

不得为附件填写：

- 分类；
- 密级；
- 可见范围；
- 主案权限；
- 父方案；
- 挂载主案。

批量成功后刷新真实共享附件列表。

十三、编辑共享附件

接入：

PUT /api/v1/attachments/{id}

只允许提交：

- documentName
- attachmentType
- summary
- tags
- rowVersion

固定只读信息：

- documentType=ATTACHMENT
- securityLevel=PUBLIC
- visibilityType=ALL_AUTHENTICATED
- 文件身份字段

要求：

1. 不允许修改文件。
2. 不显示密级和可见范围为可编辑项。
3. 使用最新rowVersion。
4. 冲突处理与文档编辑一致。
5. 成功后刷新：

   - 附件详情；
   - 共享附件列表；
   - 当前主案已挂载附件区域；
   - 附件反向挂载主案弹框中的附件标题。

十四、删除共享附件

接入：

DELETE /api/v1/attachments/{id}?rowVersion=<integer>

要求：

1. 仅在 `allowedActions`包含DELETE时允许操作。
2. 删除确认明确说明逻辑删除且本期不支持恢复。
3. 服务端返回 `ATTACHMENT_IN_USE`时：

   - 展示 `mountedPlanCount`；
   - 展示后端返回的mainPlans；
   - 明确提示必须先解除挂载；
   - 不提供强制删除；
   - 不自动调用解除挂载；
   - 不提示删除成功。

4. 未挂载附件删除成功后：

   - 关闭详情；
   - 清理附件详情缓存；
   - 刷新共享附件列表；
   - 刷新当前主案已挂载附件；
   - 清理当前选中状态。

5. `DATA_VERSION_CONFLICT`时重新加载最新数据。
6. 不实现恢复按钮。

十五、PDF预览

接入：

GET /api/v1/documents/{id}/preview

统一用于：

- MAIN
- SUB_PLAN
- ATTACHMENT

要求：

1. 只有 `allowedActions`包含VIEW时显示预览入口。
2. PDF成功响应使用Blob展示。
3. 可使用浏览器内嵌PDF区域或统一预览弹框。
4. 创建Object URL后必须在以下场景释放：

   - 关闭弹框；
   - 切换文档；
   - 组件卸载；
   - 新预览替换旧预览；
   - 请求失败。

5. 不得重复请求同一预览造成资源泄漏。
6. 可以缓存当前打开预览，但必须有明确生命周期。
7. 不得将Blob或Object URL保存到localStorage/sessionStorage。
8. 文件缺失 `FILE_NOT_FOUND`时显示明确错误。
9. 403显示无权限，不得伪装为空内容。
10. Blob成功响应不应尝试解析为JSON。

十六、Office预览处理

DOC、DOCX、XLS、XLSX调用预览接口时，后端返回：

HTTP 415
code=PREVIEW_UNAVAILABLE

前端必须：

1. 明确提示“当前文件类型暂不支持在线预览”。
2. 根据 `allowedActions`决定是否提供下载按钮。
3. 不把Office文件伪装成PDF、HTML或Word浏览器内容。
4. 不接入第三方在线Office服务。
5. 不上传文件到任何外部服务。
6. 不把415显示为系统崩溃。
7. 保留requestId便于定位。

十七、真实文件下载

接入：

GET /api/v1/documents/{id}/download

统一用于三类文档。

要求：

1. 只有 `allowedActions`包含DOWNLOAD时显示下载入口。
2. 使用Bearer Token请求Blob。
3. 从Content-Disposition安全解析下载文件名。
4. 优先支持 `filename*`，兼容标准 `filename`。
5. 防止响应头文件名注入。
6. 下载文件名解析失败时使用安全后备名称。
7. 创建临时Object URL触发浏览器下载。
8. 触发后及时释放Object URL并移除临时DOM节点。
9. 下载过程中禁用重复点击，避免一次操作增加多次downloadCount。
10. 请求失败不得生成空文件。
11. JSON错误响应必须进入统一错误处理。
12. 文件缺失显示 `FILE_NOT_FOUND`。
13. 403显示无下载权限。
14. 401进入统一登录失效流程。
15. 下载成功后可刷新当前详情的downloadCount。
16. 不在前端手工增加downloadCount替代后端结果。
17. 不使用 `<a href="/api/...">`绕过Bearer认证。

十八、allowedActions规则

按钮必须由后端返回的 `allowedActions`控制：

- VIEW
- DOWNLOAD
- EDIT
- DELETE
- MANAGE_PERMISSION
- BIND_ATTACHMENT

具体枚举以正式契约为准。

本轮只执行：

- VIEW
- DOWNLOAD
- EDIT
- DELETE

要求：

1. 不根据roleCode自行补全allowedActions。
2. ADMIN身份不代表所有文档都可操作。
3. ADMIN仍受密级规则限制。
4. USER只执行后端明确授权的查看和下载。
5. AUDITOR不因角色自动获得方案查看或下载能力。
6. MANAGE_PERMISSION和BIND_ATTACHMENT即使存在，也不得在F5调用B6接口。
7. 页面隐藏按钮不能替代后端鉴权。
8. 后端返回403时必须正确处理。

模块级“上传”和“批量导入”入口可在ADMIN的后台管理模块显示，但后端仍必须完成最终鉴权。

十九、页面交互和一致性

保持现有界面风格，不进行视觉重构。

所有新增弹框应统一：

- 标题区域；
- 表单布局；
- 必填标识；
- 错误展示；
- requestId展示；
- 确认和取消按钮；
- 提交中状态；
- 重复提交保护；
- 关闭行为。

不得：

- 使用浏览器原生alert代替既有业务提示体系；
- 成功前提前关闭弹框；
- 失败后清空全部表单；
- 在请求未结束时重复发送；
- 把空数据、失败和权限不足显示成同一状态。

二十、前端模块结构

沿用F2—F4模块化设计，不引入大型状态管理框架。

建议增加或完善：

- documentMutationApi.ts
- documentMutationTypes.ts
- documentMutationState.ts
- attachmentMutationApi.ts
- attachmentMutationTypes.ts
- attachmentMutationState.ts
- fileApi.ts
- fileTypes.ts
- fileState.ts
- batchImportTypes.ts
- batchImportState.ts
- filename/content-disposition工具
- Object URL生命周期工具

具体文件名可结合现有结构调整，但必须做到：

1. API调用、DTO校验、状态管理和页面组合分离。
2. 不把所有逻辑继续堆入App.tsx。
3. 上传和批量导入使用明确类型。
4. JSON响应进行运行时校验。
5. Blob响应检查Content-Type和响应状态。
6. 所有业务ID继续按字符串处理。
7. 不兼容number ID。
8. 不兼容snake_case替代camelCase。
9. 不静默兼容未知枚举。
10. 不持久化未知响应字段。

二十一、错误处理

至少覆盖：

- INVALID_REQUEST
- INVALID_ID
- UNAUTHORIZED
- FORBIDDEN
- RESOURCE_NOT_FOUND
- DOCUMENT_VIEW_FORBIDDEN
- DOCUMENT_DOWNLOAD_FORBIDDEN
- UNSUPPORTED_FILE_TYPE
- PREVIEW_UNAVAILABLE
- PAYLOAD_TOO_LARGE
- BATCH_MANIFEST_MISMATCH
- DATA_VERSION_CONFLICT
- MAIN_PLAN_HAS_CHILDREN
- ATTACHMENT_IN_USE
- FILE_NOT_FOUND
- INTERNAL_ERROR

要求：

1. 显示用户可理解的中文提示。
2. 保留或展示requestId。
3. 401复用统一认证失效机制。
4. 403不误判为未登录。
5. 409不提示操作成功。
6. 413明确提示文件或请求过大。
7. 415用于Office预览不支持提示。
8. 网络错误不清理有效Token。
9. JSON契约错误进入明确错误状态。
10. Blob接口错误也必须正确解析JSON错误体。

二十二、缓存和并发要求

F4已有详情请求缓存和并发合并机制，F5不得破坏。

要求：

1. 上传、编辑、删除成功后使相关缓存失效。
2. 编辑成功后缓存最新详情。
3. 删除成功后移除对应缓存。
4. 文档和附件缓存继续隔离。
5. 旧列表响应不能覆盖新条件结果。
6. 关闭弹框时取消不再需要的请求。
7. 删除和编辑不能同时对同一对象重复提交。
8. 下载按钮连续点击只能保留一个进行中请求。
9. 预览同一文档的并发请求应合理合并或阻止。
10. Object URL必须回收。

二十三、自动化测试要求

必须新增F5测试，至少覆盖：

（一）API层

1. 10个B5路径和HTTP方法正确。
2. 不重复拼接 `/api/v1`。
3. FormData不手工设置Content-Type。
4. file和metadata字段正确。
5. 批量files重复字段正确。
6. files与items顺序一致。
7. PUT请求字段严格。
8. DELETE携带rowVersion。
9. preview/download统一使用documents路径。
10. 不调用附件专用文件路径。
11. 不调用恢复、挂载或权限接口。

（二）上传

12. 主案metadata正确。
13. 子方案parentDocumentId为字符串。
14. 附件metadata不包含固定后端字段。
15. number ID拒绝。
16. tags始终为数组。
17. 未知枚举拒绝。
18. 重复提交保护。
19. 失败后保留metadata。
20. 成功后刷新真实列表。

（三）批量导入

21. 拖入多个文件。
22. 删除待上传项。
23. 文件和items稳定对应。
24. 全部成功结果。
25. 部分成功结果。
26. 全部失败结果。
27. 失败项单独重试。
28. 不重复上传已成功项。
29. 不把HTTP 200误认为全部成功。

（四）编辑和删除

30. 编辑仅提交允许字段。
31. 禁止提交文件身份字段。
32. rowVersion冲突处理。
33. MAIN_PLAN_HAS_CHILDREN处理。
34. ATTACHMENT_IN_USE展示挂载数量和主案。
35. 删除成功清理缓存和选择。
36. 不存在恢复入口或恢复请求。
37. 不提供强制删除。

（五）预览和下载

38. PDF Blob预览。
39. Object URL创建和释放。
40. Office 415提示。
41. Office有DOWNLOAD权限时显示下载入口。
42. 文件缺失处理。
43. 下载Blob成功。
44. Content-Disposition filename*解析。
45. filename后备解析。
46. JSON错误响应不生成空文件。
47. 下载重复点击保护。
48. 401统一处理。
49. 403正确提示。
50. 不使用无Token的普通链接下载。

（六）回归

51. F2认证测试继续通过。
52. F3分类、组织和人员测试继续通过。
53. F4文档和附件只读测试继续通过。
54. 原83项测试不得回退。
55. 无F6接口调用。
56. 无废止路径。
57. ID保持字符串。
58. DTO保持严格校验。

二十四、真实联调环境

业务主库为 `dms`，不建议为了F5界面验证向主库写入测试文档。

写操作联调应优先使用：

- 数据库：dms_test
- 独立后端测试端口，例如8755
- 独立测试存储目录
- 独立前端联调端口，例如9346

要求：

1. 不创建新数据库。
2. 不使用 `dms_f5_test`等阶段库。
3. 测试后端明确指向dms_test。
4. 测试存储目录不得与业务主库存储混用。
5. 不修改提交用 `.env.local`来永久切换测试环境。
6. 可以通过进程环境变量临时覆盖API地址。
7. 验证结束后停止测试服务。
8. 只清理明确的测试临时存储目录。
9. 不清理或downgrade主库。
10. 不删除历史测试数据库。

如果无法安全建立独立写联调环境：

- 完成自动化测试、构建和只读联调；
- 明确报告写操作浏览器联调待确认；
- 不得直接污染dms主库。

二十五、真实联调要求

使用真实后端验证：

1. ADMIN上传主案。
2. ADMIN上传子方案。
3. ADMIN上传共享附件。
4. 方案批量导入部分成功。
5. 附件批量导入部分成功。
6. 编辑三类文档。
7. rowVersion冲突。
8. 主案有子方案时删除冲突。
9. 已挂载附件删除冲突。
10. 未挂载附件逻辑删除。
11. 子方案逻辑删除。
12. PDF预览。
13. Office预览415。
14. 主案、子方案、附件下载。
15. USER只能执行allowedActions允许的操作。
16. AUDITOR不自动取得方案下载能力。
17. 下载计数真实增加一次。
18. 浏览器无未处理运行时异常。
19. Network中没有恢复、挂载写、权限写、审计或统计请求。
20. 上传、编辑、删除后列表与详情真实刷新。

浏览器联调产生的验证数据必须在 `dms_test`中，不得污染 `dms`主库。

二十六、必须执行的验证

至少执行：

1. npm run typecheck
2. npm run test
3. npm run build

如果沙箱内出现esbuild `spawn EPERM`：

- 在获得授权后于沙箱外复验；
- 不得把环境权限问题误判为业务失败。

Vite是常驻服务：

- 必须在后台进程或独立终端启动；
- 不得在Codex工具调用中无限等待；
- 完成验证后输出明确结果。

二十七、阻塞规则

出现以下情况必须停止并提交统领裁决：

1. 后端B5真实响应与契约不一致。
2. 需要第11个B5接口。
3. 需要修改后端。
4. 需要修改契约或OpenAPI。
5. 需要实现挂载或权限接口。
6. 需要实现恢复。
7. Blob错误无法按统一结构处理。
8. 批量返回无法对应原始文件。
9. allowedActions无法表达页面操作。
10. 必须污染dms主库才能验证。
11. 必须创建新测试数据库。
12. 需要改变字符串ID或枚举语义。
13. 需要接入外部Office预览服务。
14. 需要安装无关依赖。

不得自行兼容、猜测或扩大范围。

二十八、最终报告格式

完成后必须输出：

1. 阶段结论：F5是否完成，是否停止在F5。
2. 契约加载情况。
3. 修改文件清单。
4. 前端模块结构。
5. 10个B5接口接入情况。
6. multipart和Blob基础设施。
7. 主案、子方案单文件上传结果。
8. 方案批量导入结果。
9. 共享附件单文件上传结果。
10. 附件批量导入结果。
11. 三类文档编辑结果。
12. 主案和子方案删除结果。
13. 附件删除冲突结果。
14. PDF预览结果。
15. Office 415处理结果。
16. 下载及文件名处理结果。
17. Object URL清理结果。
18. allowedActions处理结果。
19. rowVersion冲突处理。
20. 统一错误和requestId处理。
21. 缓存失效与重复请求控制。
22. ADMIN、USER、AUDITOR验证结果。
23. dms_test联调环境说明。
24. 是否创建新测试数据库。
25. 是否污染dms主库。
26. 自动化测试结果。
27. typecheck和build结果。
28. 浏览器真实联调结果。
29. 是否调用F6或废止接口。
30. F5-C类统领裁决执行情况，如无则写无。
31. 契约符合性检查。
32. 是否存在偏差。
33. 未实现内容。
34. F6建议，但不得执行。

再次强调：

- 本轮只执行F5。
- 不修改后端。
- 不修改契约或OpenAPI。
- 不实现挂载和权限。
- 不实现审计与统计。
- 不实现任何恢复功能。
- 不使用附件专用预览或下载路径。
- 不创建新测试数据库。
- 不污染dms主库。
- 不进入F6。
- 不提交或推送Git。
----------------------------------------------------------
【F5-R1】解除联调环境阻塞，继续完成F5真实写接口验收

统领会话已核查你提交的F5报告。

结论：

1. F5代码、Mock测试、typecheck和build部分暂时认可。
2. F5尚未最终验收，必须补充真实写接口和文件流联调。
3. 当前阻塞已经解除。
4. 不得进入F6。

一、环境澄清

`dms_test`并非不存在。

你之前检查到缺少：

- DMS_TEST_DATABASE_URL
- DMS_TEST_JWT_SECRET
- DMS_TEST_USER_PASSWORD

这些变量主要用于后端pytest测试夹具，不是前端浏览器联调必须具备的变量。

统领会话已确认：

- 数据库 `dms_test`存在；
- 迁移为 `0001_initial_schema`；
- 未创建任何新测试数据库；
- 已完成独立联调数据初始化。

二、当前可直接使用的隔离环境

测试后端：

http://127.0.0.1:8755

API基础地址：

http://127.0.0.1:8755/api/v1

健康接口：

http://127.0.0.1:8755/api/v1/health

当前健康状态：

UP

测试后端进程PID：

15288

数据库：

dms_test

独立文件存储：

C:\Users\w8429\AppData\Local\Temp\dms-f5-integration-storage

当前初始化数据：

- 3个组织；
- 3个用户；
- 5个有效分类；
- 3个主案；
- 3个子方案；
- 3个共享附件；
- 3条附件挂载关系；
- 9个真实DOCX文件。

测试账号：

1. 管理员

   用户名：admin
   密码：Admin@123456

2. 审计员

   用户名：auditor
   密码：Auditor@123456

3. 普通用户

   用户名：user
   密码：User@123456

这些凭据仅用于本地隔离联调，不得写入前端源码、配置文件、测试快照或提交文件。

三、前端联调启动方式

不要修改 `.env.local`永久切换后端。

启动F5联调前端时，通过当前进程环境临时覆盖：

```powershell
$env:VITE_API_BASE_URL='http://127.0.0.1:8755/api/v1'
npm run dev -- --host 127.0.0.1 --port 9346