diff --git a/docs/architecture/详细设计/供应商信息维护模块.md b/docs/architecture/详细设计/供应商信息维护模块.md
new file mode 100644
index 0000000..b17ff73
--- /dev/null
+++ b/docs/architecture/详细设计/供应商信息维护模块.md
@@ -0,0 +1,1157 @@
+# 供应商信息维护模块
+
+| 文档名称 | 供应商信息维护模块详细设计 |
+| -------- | ------------------------------------------------ |
+| **文档版本** | V1.0 |
+| **编制日期** | 2026-07-01 |
+| **关联文档** | [[供应商注册模块]]《[[登录模块]]》《[[SRM 与 STP 数据交换接口方案说明书]]》 |
+
+---
+
+## 1. 模块概述
+
+### 1.1 建设目标
+供应商成功注册并登录后,需具备**自主维护企业信息**的能力。本模块覆盖**密码修改、联系人信息变更、公司证明文件(营业执照)更新**三大高频维护场景,确保账户安全、业务通知可达、企业资质动态更新,所有变更实时同步至SRM并全量审计。
+
+### 1.2 设计原则
+| 原则 | 说明 |
+| :------------ | :------------------------------------- |
+| **安全优先** | 修改密码需验证旧密码;修改核心联系方式需双因子验证;敏感操作记录哈希链审计。 |
+| **平滑同步** | 信息变更后实时同步SRM,确保两平台数据一致(最终一致性)。 |
+| **版本可溯** | 营业执照等文件更新时保留历史版本,支持追溯。 |
+| **自助为主,人工兜底** | 90%场景供应商自助完成,旧手机不可用时提供线下人工变更通道。 |
+
+### 1.3 适用角色与场景
+| 角色 | 场景 |
+| :--- | :--- |
+| **已登录供应商** | 修改密码、更新联系人、重新上传营业执照。 |
+| **供应商管理专员** | 查看变更记录、处理异常变更申请(旧联系方式不可用时)。 |
+| **系统管理员** | 配置安全策略(密码复杂度、会话有效期)。 |
+
+
+## 2. 页面UI详细设计(工作台端)
+
+### 2.1 “企业信息”总览页
+
+#### 2.1.1 “基本资料”Tab
+
+#### (一)页面 UI 设计
+
+```svg
+
+```
+
+### (二)“变更联系人”完整处理流程
+
+> **业务语义**:此操作表示企业联系人已更换(如人员离职、调岗),需要将姓名、手机、邮箱**作为一个整体**替换为新联系人的信息,而非逐项零散修改。
+
+#### 1. 整体流程泳道图
+
+```mermaid
+flowchart TD
+ subgraph 供应商端
+ A[点击“变更联系人”] --> B[弹出变更向导(Step 1/3)]
+ B --> C[Step 1:验证当前联系人]
+ C --> D[选择验证方式:手机或邮箱]
+ D --> E[发送验证码至选定方式]
+ E --> F[输入验证码]
+ F --> G{验证通过?}
+ G -->|失败| H[提示错误,可重试]
+ G -->|成功| I[进入 Step 2]
+
+ I --> J[Step 2:填写新联系人信息]
+ J --> K[填写新姓名、新手机、新邮箱]
+ K --> L[点击“下一步”]
+ L --> M[后端校验:新手机/邮箱全局唯一]
+ M -->|已被占用| N[提示具体冲突项]
+ M -->|可用| O[进入 Step 3]
+
+ O --> P[Step 3:验证新联系方式]
+ P --> Q[分别发送验证码至新手机和新邮箱]
+ Q --> R[输入手机验证码 + 邮箱验证码]
+ R --> S[点击“确认变更”]
+ S --> T[提交全部变更]
+ end
+
+ subgraph STP后端
+ STP1[接收变更请求] --> STP2[校验旧联系人验证Token]
+ STP2 --> STP3[校验新手机验证Token]
+ STP3 --> STP4[校验新邮箱验证Token]
+ STP4 --> STP5[原子性更新三个字段]
+ STP5 --> STP6[更新 login_name(若登录名为手机号)]
+ STP6 --> STP7[调用SRM批量更新联系人接口]
+ STP7 --> STP8[记录审计日志(含新旧完整数据)]
+ STP8 --> STP9[发送通知至新旧联系人]
+ STP9 --> STP10[返回成功]
+ end
+
+ subgraph 外部系统
+ SRM[SRM系统]
+ NOTIFY[通知服务]
+ end
+
+ STP7 --> SRM
+ STP9 --> NOTIFY
+```
+
+
+
+
+#### 2. 详细交互时序图
+
+```mermaid
+sequenceDiagram
+ participant U as 供应商
+ participant FE as 前端
+ participant Svc as 供应商服务
+ participant Cache as Redis
+ participant SMS as 短信网关
+ participant Email as 邮件网关
+ participant DB as 数据库
+ participant SRM as SRM系统
+
+ Note over U,SRM: === Step 1: 验证旧联系人 ===
+ U->>FE: 点击“变更联系人”
+ FE-->>U: 弹出第一步验证窗
+ U->>FE: 选择验证方式(手机/邮箱)
+ FE->>Svc: POST /api/v1/auth/verify/send { type:PHONE/EMAIL }
+ Svc->>SMS/Email: 发送验证码
+ Svc->>Cache: SET verify:old:{userId} TTL=300
+ U->>FE: 输入验证码
+ FE->>Svc: POST /api/v1/auth/verify/check { code }
+ Svc->>Cache: 比对
+ Svc-->>FE: { oldOtpToken: "temp_token_old" }
+
+ Note over U,SRM: === Step 2: 填写新联系人 ===
+ U->>FE: 填写新姓名、新手机、新邮箱
+ FE->>Svc: 前端校验格式
+ U->>FE: 点击“下一步”
+ FE->>Svc: POST /api/v1/supplier/contact/check-unique { newPhone, newEmail }
+ Svc->>DB: 查询新手机/邮箱是否已被占用
+ alt 已被占用
+ Svc-->>FE: 409 冲突,提示具体占用项
+ else 可用
+ Svc-->>FE: 200 可用,进入Step 3
+ end
+
+ Note over U,SRM: === Step 3: 验证新联系方式 ===
+ FE->>Svc: POST /api/v1/auth/verify/send { type:PHONE, target:新手机, purpose:NEW }
+ Svc->>SMS: 发送验证码
+ Svc->>Cache: SET verify:new_phone:{userId} TTL=300
+
+ FE->>Svc: POST /api/v1/auth/verify/send { type:EMAIL, target:新邮箱, purpose:NEW }
+ Svc->>Email: 发送验证码
+ Svc->>Cache: SET verify:new_email:{userId} TTL=300
+
+ U->>FE: 输入两个验证码
+ FE->>Svc: POST /api/v1/auth/verify/check { type:PHONE, code }
+ FE->>Svc: POST /api/v1/auth/verify/check { type:EMAIL, code }
+ Svc-->>FE: { newPhoneToken, newEmailToken }
+
+ Note over U,SRM: === 最终提交 ===
+ U->>FE: 点击“确认变更”
+ FE->>Svc: PUT /api/v1/supplier/contact/change-all
+
+ Svc->>Svc: 校验三个Token有效性(old/newPhone/newEmail)
+ Svc->>DB: 开启事务
+ Svc->>DB: UPDATE contact_name, contact_phone, contact_email
+ Svc->>DB: UPDATE login_name(若登录名=手机号)
+ Svc->>DB: 提交事务
+
+ Svc->>SRM: POST /api/v1/supplier/contact/batch-update { contactName, phone, email }
+ SRM-->>Svc: 200 OK
+
+ Svc->>Cache: DEL 所有验证码缓存
+ Svc->>DB: INSERT 审计日志(含完整新旧数据)
+ Svc->>SMS: 发送通知至旧手机(告知已变更)
+ Svc->>Email: 发送通知至旧邮箱
+ Svc->>SMS: 发送通知至新手机(告知已绑定)
+ Svc->>Email: 发送通知至新邮箱
+
+ Svc-->>FE: 200 成功
+ FE-->>U: 变更成功,页面刷新
+```
+
+
+#### 3. 关键业务规则
+
+|规则项|说明|
+|---|---|
+|**原子性**|姓名、手机、邮箱必须在同一个数据库事务中同时更新,不允许部分成功。|
+|**唯一性校验**|新手机号和新邮箱必须全局唯一(未被其他ACTIVE供应商占用),与注册规则一致。|
+|**登录名联动**|若系统使用手机号作为登录名(`login_name`),更换手机号后`login_name`同步更新,旧手机号不可再用于登录。|
+|**双因子验证**|必须同时验证三个Token(旧联系人、新手机、新邮箱),任一缺失或失效则拒绝变更。|
+|**Token有效期**|所有验证Token有效期为5分钟,超时需重新获取。|
+|**操作频率限制**|同一供应商30天内最多变更联系人**3次**。|
+|**SRM同步**|调用SRM的**批量更新接口**一次性传递三个字段,确保SRM侧原子更新。|
+|**会话处理**|变更手机号后,不强制踢出当前登录会话(旧会话仍有效),但密码找回等使用手机号的功能将指向新号。|
+|**审计粒度**|审计日志需记录变更前的完整联系人信息和变更后的完整信息。|
+
+#### 4. 异常场景处理
+
+|异常场景|用户侧表现|后端处理|
+|---|---|---|
+|**新手机号已被占用**|提示“该手机号已被其他供应商绑定”|返回冲突字段,阻塞提交|
+|**新邮箱已被占用**|提示“该邮箱已被其他供应商绑定”|返回冲突字段,阻塞提交|
+|**旧联系人验证超时(>5min)**|提示“验证已过期,请重新获取”|清除旧Token,要求重新验证|
+|**新手机验证超时**|提示“手机验证码已过期”|清除该Token,要求重发|
+|**新邮箱验证超时**|提示“邮箱验证码已过期”|清除该Token,要求重发|
+|**SRM同步失败**|用户无感知(STP侧先成功)|异步重试5次,仍失败告警管理员|
+|**30天内变更超限**|提示“30天内仅允许变更联系人3次”|返回限流错误码|
+
+#### 5. 接口规范
+
+**最终提交接口**
+
+|项目|内容|
+|---|---|
+|**路径**|`/api/v1/supplier/contact/change-all`|
+|**方法**|`PUT`|
+|**认证**|需要登录态(JWT)|
+
+**请求体**
+
+```json
+{
+ "newContactName": "王芳",
+ "newPhone": "13900139000",
+ "newEmail": "wangfang@example.com",
+ "oldOtpToken": "eyJ...",
+ "newPhoneOtpToken": "eyJ...",
+ "newEmailOtpToken": "eyJ..."
+}
+
+```
+
+**成功响应**
+
+```json
+{
+ "code": "0",
+ "message": "联系人变更成功",
+ "data": {
+ "contactName": "王芳",
+ "contactPhone": "13900139000",
+ "contactEmail": "wangfang@example.com"
+ }
+}
+```
+
+---
+
+### 2.1.2 Tab 2:安全设置
+
+#### (一)页面 UI 设计(切换到“安全设置”Tab 时的展示)
+
+```svg
+
+```
+
+#### (二)登录日志查看详细功能设计
+
+##### 1. 日志数据结构
+
+|字段|类型|说明|
+|---|---|---|
+|`log_id`|VARCHAR(32)|日志ID|
+|`user_id`|VARCHAR(32)|供应商用户ID|
+|`login_time`|DATETIME|登录时间|
+|`logout_time`|DATETIME|登出/Token过期时间(可选)|
+|`ip_address`|VARCHAR(64)|登录IP|
+|`location`|VARCHAR(128)|地理位置(根据IP库解析)|
+|`user_agent`|VARCHAR(256)|设备/浏览器信息|
+|`device_fingerprint`|VARCHAR(128)|设备指纹(Canvas指纹,用于同一设备识别)|
+|`login_type`|VARCHAR(20)|REGISTER(注册自动登录)/ PASSWORD(密码)/ SMS(短信验证码)/ TOKEN(RefreshToken)|
+|`status`|VARCHAR(20)|SUCCESS / FAIL / LOCKED|
+|`fail_reason`|VARCHAR(64)|登录失败原因(如“密码错误”)|
+|`is_current_session`|BOOLEAN|是否当前活跃会话|
+|`hash_chain`|VARCHAR(256)|审计哈希链指针|
+
+> 日志数据来源于 `t_user_login_log` 和 `t_user_session` 表,保留期限 ≥ 90 天(对外展示),后台归档 7 年。
+
+##### 2. 登录日志查看流程
+
+```mermaid
+flowchart TD
+ A[供应商进入安全设置Tab] --> B[页面自动加载最近20条登录日志]
+ B --> C[按登录时间倒序排列]
+ C --> D[高亮显示异常登录记录]
+
+ D --> E{用户操作}
+ E -->|点击“刷新日志”| F[重新请求最新日志]
+ E -->|点击“查看全部”| G[跳转至完整的日志列表页
(支持分页/筛选/导出)]
+ E -->|点击“异常”标记| H[弹出详细告警提示]
+
+ F --> B
+ G --> I[完整日志页:支持按时间范围筛选]
+ I --> J[支持按登录状态筛选
(全部/正常/异常/失败)]
+ J --> K[支持按设备筛选]
+ K --> L[支持导出为CSV(脱敏处理)]
+
+ H --> M[展示:异常时间/IP/位置/设备]
+ M --> N[提示建议:立即修改密码 / 检查账户绑定]
+ N --> O[提供快捷跳转至“修改密码”]
+
+ style D fill:#ffcdd2,stroke:#c62828
+ style O fill:#e3f2fd,stroke:#1565c0
+```
+
+##### 3. 异常登录判定规则(后台自动标注)
+
+| 判定规则 | 说明 | 告警级别 |
+| ---------- | ----------------------------------- | ------- |
+| **地理位置突变** | 本次登录IP归属地与前5次登录归属地跨省/跨境,且时间间隔小于12小时 | ⚠️ 高风险 |
+| **设备指纹突变** | 本次登录设备指纹与历史记录完全不匹配 | ⚠️ 中风险 |
+| **非常规时间段** | 登录时间为北京时间 00:00-06:00 | ⚠️ 中风险 |
+| **登录失败连续** | 同一账户在5分钟内连续失败3次以上,随后成功登录 | ⚠️ 中风险 |
+| **IP黑名单** | 登录IP落入已知威胁IP库(由安全团队维护) | 🔴 严重风险 |
+| **暴力破解特征** | 同一IP尝试多个供应商账号登录 | 🔴 严重风险 |
+
+##### 4. 登录日志查询接口
+
+|项目|内容|
+|---|---|
+|**路径**|`/api/v1/supplier/login-logs`|
+|**方法**|`GET`|
+|**认证**|需要登录态|
+
+**请求参数(分页 + 筛选)**
+
+| 参数名 | 类型 | 必填 | 说明 |
+| ----------- | ------- | --- | ---------------------------------- |
+| `pageNo` | Integer | 否 | 页码,默认1 |
+| `pageSize` | Integer | 否 | 每页条数,默认20,最大100 |
+| `startDate` | String | 否 | 开始时间(ISO 8601) |
+| `endDate` | String | 否 | 结束时间 |
+| `status` | String | 否 | 筛选:ALL / SUCCESS / FAIL / ABNORMAL |
+| `device` | String | 否 | 设备筛选关键词 |
+**成功响应**
+
+```json
+{
+ "code": "0",
+ "data": {
+ "total": 156,
+ "pageNo": 1,
+ "pageSize": 20,
+ "abnormalCount": 2,
+ "list": [
+ {
+ "logId": "LOG_20260701_001",
+ "loginTime": "2026-07-01 09:30:25",
+ "ipAddress": "192.168.1.100",
+ "location": "山东·滨州",
+ "device": "Chrome/Windows 10",
+ "loginType": "PASSWORD",
+ "status": "SUCCESS",
+ "isCurrentSession": true,
+ "isAbnormal": false
+ },
+ {
+ "logId": "LOG_20260629_003",
+ "loginTime": "2026-06-29 03:45:12",
+ "ipAddress": "203.0.113.45",
+ "location": "境外·未知",
+ "device": "Firefox/Linux",
+ "loginType": "PASSWORD",
+ "status": "SUCCESS",
+ "isCurrentSession": false,
+ "isAbnormal": true,
+ "abnormalReason": "地理位置突变"
+ }
+ ]
+ }
+}
+```
+
+#### (三)“安全设置”Tab 完整操作流程汇总
+
+| 功能项 | 验证强度 | 是否同步SRM | 频率限制 | 审计记录 |
+| ---------- | ----------- | ----------- | --------- | -------- |
+| **修改密码** | 高(验证旧密码) | 否(SRM侧密码独立) | 每天≤5次失败锁定 | ✅ 完整哈希链 |
+| **更换手机号** | 最高(旧+新双因子) | ✅ 是 | 30天≤3次 | ✅ 完整哈希链 |
+| **更换邮箱** | 最高(旧+新双因子) | ✅ 是 | 30天≤3次 | ✅ 完整哈希链 |
+| **踢出所有设备** | 中(登录态+二次确认) | 否 | 无限制 | ✅ 完整哈希链 |
+| **查看登录日志** | 低(仅登录态) | 否 | 无限制 | ✅ 仅查询审计 |
+| **异常告警反馈** | 低 | 否 | 无限制 | ✅ 生成反馈工单 |
+#### (四)安全设置各子项处理流程
+
+##### 1. 修改密码流程(双因子验证旧密码)
+
+```mermaid
+flowchart TD
+ A[点击“修改密码”] --> B[弹窗:输入旧密码 + 新密码 + 确认]
+ B --> C[点击“确认修改”]
+ C --> D[前端校验:非空/强度/新旧一致/新旧不同]
+ D -->|校验失败| E[提示具体错误,表单高亮]
+ D -->|校验通过| F[提交 PUT /api/v1/supplier/password]
+ F --> G[后端:查询DB获取当前密码哈希]
+ G --> H[Argon2id 比对旧密码]
+ H -->|失败| I[失败计数 Redis + 1]
+ I --> J{失败次数 ≥ 5?}
+ J -->|是| K[锁定账号 30 分钟 + 告警]
+ J -->|否| L[返回 401 剩余尝试次数]
+ K --> M[提示“账户已锁定”]
+ H -->|成功| N[重置失败计数]
+ N --> O[新密码哈希后 UPDATE DB]
+ O --> P[清除 Redis 中所有该用户的会话 Token]
+ P --> Q[记录审计日志(哈希链)]
+ Q --> R[发送通知至旧联系人(仅告知)]
+ R --> S[返回成功,要求重新登录]
+ S --> T[前端清除本地 Token,跳转登录页]
+```
+
+##### 2. 更换手机号流程(双因子验证旧+新)
+
+```mermaid
+flowchart TD
+ A[点击“更换手机号”] --> B[弹窗:Step1 验证旧手机]
+ B --> C[发送验证码至旧手机]
+ C --> D[用户输入验证码]
+ D --> E[后端比对 Redis 验证码]
+ E -->|失败| F[提示错误,可重试 5 次]
+ E -->|成功| G[生成 oldOtpToken(有效期 5 分钟)]
+ G --> H[Step2:输入新手机号]
+ H --> I[点击“发送验证码”]
+ I --> J[后端校验新手机号唯一性(未绑定其他账户)]
+ J -->|已被占用| K[提示“该手机号已被其他供应商绑定”]
+ J -->|可用| L[发送验证码至新手机]
+ L --> M[用户输入新手机验证码]
+ M --> N[后端比对 Redis 验证码]
+ N -->|失败| O[提示错误]
+ N -->|成功| P[生成 newOtpToken(有效期 5 分钟)]
+ P --> Q[Step3:点击“确认变更”]
+ Q --> R[提交 PUT /supplier/contact 带两个 Token]
+ R --> S[后端校验两个 Token 有效性]
+ S --> T[UPDATE DB:contact_phone 字段]
+ T --> U[UPDATE DB:login_name(若手机号为登录名)]
+ U --> V[调用 SRM 更新接口]
+ V --> W[记录审计日志(含新旧值)]
+ W --> X[清除所有验证码缓存]
+ X --> Y[发送通知至新旧手机号]
+ Y --> Z[返回成功,保持登录态(旧 Token 仍有效)]
+```
+
+##### 3. 更换邮箱流程(流程与手机号完全对称)
+
+> 验证介质从“短信验证码”替换为“邮箱验证码”,调用 `/api/v1/auth/email/send` 接口,其余逻辑与更换手机号完全一致。
+
+##### 4. 踢出所有设备流程
+
+```mermaid
+flowchart TD
+ A[点击“踢出所有设备”] --> B[二次确认弹窗]
+ B -->|取消| C[关闭弹窗]
+ B -->|确认| D[POST /api/v1/supplier/session/revoke-all]
+ D --> E[后端:删除 Redis 中该用户所有 session_key]
+ E --> F[记录审计日志]
+ F --> G[返回成功]
+ G --> H[前端清除本地 Token 跳转登录页]
+```
+
+---
+
+### 2.1.3 Tab 3:资质文件
+
+#### (一)页面 UI 设计
+
+```svg
+
+```
+
+#### (二)完整数据模型
+
+##### 资质文件表 `t_supplier_qualification`
+
+|字段名|类型|约束|说明|
+|---|---|---|---|
+|`id`|BIGINT(20)|PK, AUTO_INCREMENT|自增主键|
+|`file_id`|VARCHAR(32)|UNIQUE, NOT NULL|文件业务ID(Q_YYYYMMDD_XXXX)|
+|`supplier_id`|VARCHAR(32)|FK, NOT NULL|供应商ID|
+|`category`|VARCHAR(32)|NOT NULL|**分类**:LICENSE / OTHER_QUALIFICATION / PERFORMANCE|
+|`file_name`|VARCHAR(256)|NOT NULL|文件显示名称(可修改)|
+|`file_original_name`|VARCHAR(256)|NOT NULL|原始文件名(上传时的名称,不可修改)|
+|`file_url`|VARCHAR(512)|NOT NULL|OSS存储URL|
+|`file_size`|BIGINT||文件大小(字节)|
+|`file_type`|VARCHAR(16)||MIME类型(application/pdf, image/jpeg等)|
+|`upload_time`|DATETIME|NOT NULL|上传时间|
+|`last_modified_time`|DATETIME||最后修改时间(重命名时更新)|
+|`version`|INT|DEFAULT 1|版本号(仅营业执照使用,其他文件固定为1)|
+|`status`|VARCHAR(20)|NOT NULL|ACTIVE / DELETED(软删除)|
+|`deleted_time`|DATETIME||删除时间|
+|`created_at`|DATETIME||创建时间|
+|`updated_at`|DATETIME||更新时间|
+
+##### 营业执照单独管理(专用字段,与上表冗余关联)
+
+> 营业执照固定存在且唯一,使用`t_supplier_profile`中的字段存储主版本,历史版本通过`license_history` JSON字段保存,方便追溯。
+
+| 供应商主表字段 | 说明 |
+| -------------------- | ------------------------------------------- |
+| `license_file_url` | 当前版本营业执照文件URL |
+| `license_version` | 当前版本号 |
+| `license_ocr_result` | OCR识别结果JSON |
+| `license_history` | 历史版本数组:`[{version, url, ocr, upload_time}]` |
+
+#### (三)详细处理流程
+
+##### 1. 上传资质/业绩文件
+
+```mermaid
+flowchart TD
+ subgraph 供应商端
+ A[点击“上传资质文件”或“上传业绩文件”] --> B[弹出文件选择窗口]
+ B --> C[选择本地文件]
+ C --> D[前端校验:格式/大小]
+ D -->|不合格| E[提示错误:格式不支持或文件过大]
+ D -->|合格| F[点击“确认上传”]
+ F --> G[显示上传进度条]
+ G --> H[调用 POST /api/v1/supplier/qualification/upload]
+ H --> I[等待后端响应]
+ I --> J{响应结果}
+ J -->|成功| K[列表新增一行,显示新文件]
+ J -->|失败| L[提示具体错误原因]
+ K --> M[完成]
+ end
+ subgraph STP后端
+ STP1[接收上传请求] --> STP2[校验供应商权限]
+ STP2 --> STP3[校验文件数量是否超限]
+ STP3 -->|超限| STP4[返回错误:文件数超50]
+ STP3 -->|未超限| STP5[流式上传至OSS]
+ STP5 --> STP6[获取文件URL]
+ STP6 --> STP7[INSERT t_supplier_qualification]
+ STP7 --> STP8[记录审计日志]
+ STP8 --> STP9[返回成功 + 文件元数据]
+ end
+ subgraph 外部系统
+ OSS[OSS对象存储]
+ end
+ STP5 --> OSS
+
+```
+
+**关键校验规则(上传)**
+
+| 校验项 | 规则 |
+| ----- | ----------------------------- |
+| 文件格式 | JPG、PNG、PDF(MIME类型校验) |
+| 文件大小 | ≤ 20MB |
+| 总数量限制 | 每个分类(资质/业绩)各 ≤ 30 个,总计 ≤ 50 个 |
+| 文件名称 | 自动去除特殊字符,防止路径遍历攻击 |
+| 文件去重 | 校验MD5,避免同一文件重复上传(提示“该文件已存在”) |
+
+**接口定义**
+
+|项目|内容|
+|---|---|
+|**路径**|`/api/v1/supplier/qualification/upload`|
+|**方法**|`POST`|
+|**认证**|需要登录态|
+|**请求**|`multipart/form-data`|
+
+|表单参数|类型|必填|说明|
+|---|---|---|---|
+|`file`|File|是|文件内容|
+|`category`|String|是|`OTHER_QUALIFICATION` / `PERFORMANCE`|
+|`fileName`|String|否|自定义显示名称(不传则使用原始文件名)|
+
+**成功响应**
+
+```json
+{
+ "code": "0",
+ "data": {
+ "fileId": "Q_20260701_001",
+ "fileName": "ISO9001_质量管理体系认证.pdf",
+ "fileUrl": "https://oss.stp.com/...",
+ "fileSize": 2411724,
+ "uploadTime": "2026-07-01 10:30:00"
+ }
+}
+```
+
+##### 2. 重命名文件
+
+```mermaid
+flowchart TD
+ A[点击文件行的“重命名”] --> B[文件名变为可编辑输入框]
+ B --> C[供应商输入新名称]
+ C --> D[点击“保存”或按回车]
+ D --> E[前端校验:非空、长度≤100、不含特殊字符]
+ E -->|校验失败| F[提示具体错误]
+ E -->|校验通过| G[调用 PUT /api/v1/supplier/qualification/rename]
+ G --> H[后端校验权限:文件归属当前供应商]
+ H --> I[更新 file_name 字段]
+ I --> J[更新 last_modified_time]
+ J --> K[记录审计日志]
+ K --> L[返回成功]
+ L --> M[页面刷新显示新名称]
+```
+
+**接口定义**
+
+|项目|内容|
+|---|---|
+|**路径**|`/api/v1/supplier/qualification/rename`|
+|**方法**|`PUT`|
+|**认证**|需要登录态|
+
+**请求体**
+
+```json
+
+{
+ "fileId": "Q_20260701_001",
+ "newName": "ISO9001_质量管理体系认证证书_2026版.pdf"
+}
+```
+
+
+##### 3. 删除文件
+
+```mermaid
+flowchart TD
+ A[点击文件行的“删除”] --> B[弹出二次确认弹窗]
+ B -->|取消| C[关闭弹窗]
+ B -->|确认| D[调用 DELETE /api/v1/supplier/qualification/delete]
+ D --> E[后端校验权限]
+ E --> F[检查文件是否被正在进行的投标报名引用]
+ F -->|被引用| G[返回错误:文件正在使用中,不可删除]
+ F -->|未被引用| H[软删除:status='DELETED' + deleted_time]
+ H --> I[记录审计日志(含文件名和删除人)]
+ I --> J[返回成功]
+ J --> K[前端移除该行]
+```
+
+> **特别规则**:
+> - 软删除仅标记状态,OSS文件暂不物理删除(保留期≥90天,便于审计追溯)。
+> - 若文件已被**进行中的投标报名**引用,则不允许删除,提示“该文件已被报名项目引用,无法删除”。
+
+**接口定义**
+
+| 项目 | 内容 |
+| :--- | :--- |
+| **路径** | `/api/v1/supplier/qualification/delete` |
+| **方法** | `DELETE` |
+| **认证** | 需要登录态 |
+
+**请求体**
+```json
+{
+ "fileId": "Q_20260701_001"
+}
+```
+
+##### 4. 营业执照更新
+
+> 营业执照更新逻辑与资质文件独立,使用专门接口。
+
+```mermaid
+flowchart TD
+ subgraph 供应商端
+ A[点击“更新营业执照”] --> B[上传新营业执照文件]
+ B --> C[显示“上传中...AI审查中...”]
+ C --> D{等待后端最终结果}
+ D -->|审查通过| E[显示成功:营业执照已更新]
+ D -->|审查不通过| F[显示具体驳回原因
+ 重新上传按钮]
+ E --> G[页面自动刷新,显示新信息]
+ F --> H[供应商修改后重新上传]
+ end
+
+ subgraph STP后端处理链路
+ STP1[接收上传请求] --> STP2[文件病毒扫描 + OSS上传]
+ STP2 --> STP3[调用 OCR 服务提取文字字段]
+ STP3 --> STP4[AI 真实性审查(见下方子流程)]
+ STP4 --> STP5{真实性审查通过?}
+ STP5 -->|否| STP6[驳回:营业执照疑似伪造或篡改]
+ STP5 -->|是| STP7[AI 一致性审查(与数据库/工商库比对)]
+ STP7 --> STP8{一致性审查通过?}
+ STP8 -->|否| STP9[驳回:信息不一致,需人工复核]
+ STP8 -->|是| STP10[生成审查报告 + 数字指纹]
+ STP10 --> STP11[更新供应商主表企业字段]
+ STP11 --> STP12[保存历史版本至 license_history]
+ STP12 --> STP13[调用 SRM 批量更新接口]
+ STP13 --> STP14[记录审计日志(含审查报告哈希)]
+ STP14 --> STP15[返回成功]
+
+ STP6 --> STP16[记录审计日志(含失败原因)]
+ STP9 --> STP16
+ STP16 --> STP17[返回驳回 + 原因]
+ end
+
+ subgraph AI审查引擎
+ AI1[真实性审查] --> AI1a[图像真伪检测
(PS/拼接/翻拍识别)]
+ AI1 --> AI1b[印章/水印检测]
+ AI1 --> AI1c[版面一致性检测]
+ AI1a --> AI2[输出:真实/可疑/伪造]
+ AI1b --> AI2
+ AI1c --> AI2
+
+ AI3[一致性审查] --> AI3a[OCR字段 vs 数据库现有字段]
+ AI3 --> AI3b[OCR字段 vs 工商API返回数据]
+ AI3a --> AI4[输出:一致/部分不一致/严重不一致]
+ AI3b --> AI4
+ end
+
+ subgraph 外部系统
+ EXT1[OSS对象存储]
+ EXT2[工商信息API]
+ EXT3[SRM系统]
+ end
+
+ STP2 --> EXT1
+ STP3 --> STP4
+ AI3b --> EXT2
+ STP13 --> EXT3
+```
+
+##### 5. 营业执照历史版本查看
+
+点击“查看历史版本”弹出时间线,展示所有历史版本:
+
+```
+V3 (当前) - 2026-06-30 14:30 滨化新材料有限公司_2026.pdf ✅ 当前使用
+V2 - 2025-12-01 09:20 滨化新材料有限公司_2025.pdf
+V1 - 2024-06-15 11:00 滨化新材料有限公司_2024.pdf
+```
+
+每个版本可预览/下载。
+
+#### (四)业务规则汇总
+
+| 规则ID | 规则 | 适用范围 |
+| :--- | :------------------------------------------------- | :---- |
+| R01 | 营业执照全局唯一,供应商仅可维护一份,版本递增。 | 营业执照 |
+| R02 | 营业执照信用代码识别结果与系统不一致时,系统不自动更新,提示人工复核。 | 营业执照 |
+| R03 | 其他资质文件分类:自动归类为`OTHER_QUALIFICATION`或`PERFORMANCE`。 | 资质/业绩 |
+| R04 | 同一分类文件数量上限为30个,总上限50个。 | 资质/业绩 |
+| R05 | 文件格式仅支持PDF/JPG/PNG,大小≤20MB。 | 资质/业绩 |
+| R06 | 文件重命名后需同步更新`last_modified_time`。 | 资质/业绩 |
+| R07 | 删除为软删除,数据永久保留(不物理删除OSS文件,保留≥90天)。 | 资质/业绩 |
+| R08 | 被进行中的投标报名引用的文件不可删除。 | 资质/业绩 |
+| R09 | 所有上传/删除/重命名操作记录审计日志(哈希链)。 | 全场景 |
+| R10 | 资质/业绩文件不自动同步至SRM(由供应商自主管理,报名时按需引用)。 | 资质/业绩 |
+| R11 | 营业执照更新后自动同步至SRM,保持企业信息一致。 | 营业执照 |
+#### (五)异常场景处理矩阵
+
+| 异常场景 | 用户侧表现 | 后端处理 |
+| :----------------- | :---------------------------- | :--------------- |
+| **上传文件格式不支持** | 前端提示“仅支持PDF/JPG/PNG格式” | 返回400 |
+| **上传文件超过20MB** | 前端提示“文件不能超过20MB” | 返回400 |
+| **文件数量达到上限(50个)** | 提示“文件数量已满(50个),请删除旧文件后再上传” | 返回429 |
+| **重命名时名称重复** | 提示“该名称已存在,请修改” | 返回409 |
+| **重命名时含非法字符** | 提示“文件名不能包含特殊字符” | 返回400 |
+| **删除时文件被报名引用** | 提示“该文件已被项目XXX引用,无法删除” | 返回403 |
+| **营业执照OCR识别失败** | 提示“识别失败,请确保图片清晰” | 返回错误,允许用户重试或手动修改 |
+| **营业执照信用代码不一致** | 页面高亮告警“识别结果与系统记录不一致,请人工确认” | 记录告警日志 |
+| **OSS上传超时(>10s)** | 提示“上传超时,请重试” | 记录WARN日志 |
+| **翻拍检测报警** | “请上传营业执照原件扫描件,而非照片/复印件” | 驳回 |
+| **信用代码与工商API不匹配** | “统一社会信用代码与工商登记信息不一致,请核实后重新上传” | 驳回,记录差异 |
+| **OCR识别结果与数据库冲突** | “识别结果与系统记录不一致,进入人工复核流程” | 冻结变更,生成待办 |
+| **AI审查服务超时(>20s)** | “审查服务繁忙,请稍后重试” | 返回503,不更新任何数据 |
+| **工商API不可用** | “企业信息核验服务暂不可用,请稍后重试” | 阻断更新(不降级),记录告警 |
+
+---
+## 3. SRM同步与异常重试
+
+### 8.1 同步触发条件
+
+| 变更类型 | 调用SRM接口 | 同步内容 |
+| :------ | :---------------------- | :-------------------------------------- |
+| 修改密码 | 不调用SRM | 无(SRM侧密码独立管理) |
+| 修改联系人姓名 | `POST /contact/update` | contactName |
+| 修改手机号 | `POST /contact/update` | contactPhone |
+| 修改邮箱 | `POST /contact/update` | contactEmail |
+| 更新营业执照 | `POST /supplier/update` | companyName, address, legalPerson等(若变更) |
+
+### 8.2 重试机制
+
+```mermaid
+stateDiagram-v2
+ [*] --> SYNC_PENDING: 变更提交
+ SYNC_PENDING --> SYNCING: 立即调用SRM
+ SYNCING --> SYNC_SUCCESS: 成功
+ SYNCING --> SYNC_FAILED: 失败
+ SYNC_FAILED --> SYNCING: 异步重试(间隔5/15/30/60/120分钟)
+ SYNC_FAILED --> ALERT: 5次均失败
+ ALERT --> [*]: 管理员人工介入
+```
+
+
+## 9. 异常场景处理矩阵
+
+| 异常场景 | 用户侧表现 | 后端处理 |
+| :--- | :--- | :--- |
+| **修改密码时旧密码错误** | 提示“当前密码错误,剩余X次机会” | 失败计数+1,写入审计日志 |
+| **密码错误达5次** | 提示“账户已锁定30分钟” | 锁定并告警 |
+| **修改手机号时新号已被占用** | 提示“该手机号已被其他供应商绑定” | 返回409冲突 |
+| **验证码发送频率过高** | 提示“发送过于频繁,请60秒后重试” | 返回429限流 |
+| **双因子验证Token超时(5分钟)** | 提示“验证已过期,请重新获取” | 清除旧Token,要求重发 |
+| **营业执照OCR识别失败** | 提示“识别失败,请检查图片清晰度” | 允许手动填写关键字段并标记 |
+| **SRM同步超时(>5s)** | 用户无感知(显示成功) | 异步重试,后台告警 |
+| **上传文件格式/大小不符** | 前端拦截,提示具体原因 | 后端二次校验 |
+
+
+## 10. 通知策略
+
+| 触发事件 | 通知方式 | 接收方 | 文案 |
+| :--- | :--- | :--- | :--- |
+| **密码修改成功** | 短信+邮件 | 当前联系人与旧联系人(若变更) | “您的STP账户密码已于XX时间修改,若非本人操作请立即联系管理员。” |
+| **手机号修改成功** | 短信+邮件 | 新旧手机号 | “您的STP账户绑定手机号已变更为XXX,请确认。” |
+| **邮箱修改成功** | 邮件 | 新旧邮箱 | “您的STP账户绑定邮箱已变更为XXX。” |
+| **营业执照更新成功** | 站内信 | 供应商联系人 | “企业营业执照已更新,请留意后续通知。” |
+| **SRM同步失败告警** | 邮件+飞书 | 系统管理员 | “供应商ID {id} 信息变更同步SRM失败,请人工介入。” |