docs: 新增详细设计文档(供应商报名/注册/外部专家/新用户/登录),更新需求分析说明书

This commit is contained in:
2026-06-30 18:47:03 +08:00
parent 01528c9434
commit 7477566b9d
6 changed files with 2728 additions and 0 deletions
@@ -0,0 +1,232 @@
# 供应商招标报名模块
## 总体流程概述
供应商从获知招标公告到完成报名并获取投标资格,涉及**公告浏览 → 身份认证 → 报名申请 → 资质审核 → 费用缴纳 → 文件获取 → 投标文件上传**七大环节。本设计覆盖全流程的**页面交互**、**系统跳转**与**内部处理逻辑**,并充分复用现有 SRM 供应商库与统一认证体系。
## 页面流转与跳转设计
```mermaid
graph TD
A[招标公告详情页] -->|点击“立即报名”| B{登录检查}
B -->|未登录| C[登录/注册选择页]
C -->|选择“STP账号登录”| D[STP登录表单]
C -->|选择“SRM账号登录”| E[SRM登录表单(无感验证)]
D --> F[验证通过 → 跳转报名页]
E -->|调用SRM验证接口| F
C -->|选择“新用户注册”| G[注册表单]
G -->|提交注册| H[注册成功 → 自动登录 → 跳转报名页]
F --> I[报名信息填写页]
I -->|填写基本信息+上传资质| J[预览确认页]
J -->|提交| K[报名提交成功页(等待审核)]
K -->|审核通过(邮件通知)| L[缴费通知页]
L -->|选择支付方式| M[在线支付/上传凭证]
M -->|支付完成| N[缴费确认中]
N -->|财务确认到账| O[招标文件下载页]
O -->|下载文件| P[投标文件上传页]
P -->|加密上传| Q[上传成功页]
Q -->|等待开标| R[我的项目列表(状态已报名)]
style A fill:#fce4ec,stroke:#c2185b,stroke-width:2px
style K fill:#e8f5e9,stroke:#388e3c
style Q fill:#e8f5e9,stroke:#388e3c
style R fill:#e3f2fd,stroke:#1565c0
```
## 系统交互时序图
```mermaid
sequenceDiagram
participant U as 供应商浏览器
participant STP as STP平台
participant SRM as SRM系统
participant OA as OA审批
participant Pay as 支付网关
participant AI as AI审核服务
U->>STP: 访问招标公告页面
STP-->>U: 展示公告内容
U->>STP: 点击“报名”
STP-->>U: 检测未登录,跳转登录页
U->>STP: 选择“使用SRM账号登录”
STP->>SRM: POST /api/v1/auth/login/for-stp {username, password}
SRM-->>SRM: 验证账号密码
SRM-->>STP: 200 { srmId, name, taxId, ... }
STP-->>STP: 建立本地会话,关联SRM ID
STP-->>U: 自动跳转至报名页
U->>STP: 填写报名表单 + 上传资质文件
STP->>AI: 启动AI初审(资质/风险/关联)
AI-->>STP: 初审意见(通过/警告/不通过)
alt 需要人工评审
STP->>STP: 生成待办任务
STP-->>U: 进入人工评审阶段
STP->>OA: 通知评审人员(站内/邮件)
OA-->>STP: 评审完成回调
else AI直接通过
STP-->>STP: 报名状态更新为“审核通过”
end
STP-->>U: 发送审核结果通知(邮件+站内信)
U->>STP: 查看缴费通知
STP-->>U: 展示缴费金额及支付方式
U->>Pay: 在线支付 / 上传银行凭证
Pay-->>STP: 异步通知支付结果(或凭证待确认)
STP->>OA: 发起财务确认审批(含凭证)
OA-->>STP: 审批通过(确认到账)
STP-->>U: 解锁招标文件下载权限
U->>STP: 下载招标文件
STP-->>U: 返回文件(加水印/加密)
U->>STP: 加密上传投标文件
STP->>STP: 文件哈希存证(区块链/哈希链)
STP->>SRM: 记录供应商投标状态(可选)
STP->>Archive: 归档报名材料
STP-->>U: 投标成功
```
## 页面原型设计
下面以**报名信息填写页**为例,展示核心表单与操作区。
```svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 720" font-family="'Microsoft YaHei', sans-serif">
<!-- 背景 -->
<rect width="1024" height="720" fill="#f5f7fa"/>
<!-- 顶部导航栏 -->
<rect width="1024" height="64" fill="#ffffff" stroke="#e0e0e0" stroke-width="1"/>
<text x="24" y="40" font-size="20" font-weight="bold" fill="#c2185b">智慧招标平台</text>
<text x="750" y="40" font-size="14" fill="#666">欢迎,XX科技公司</text>
<rect x="880" y="20" width="100" height="30" rx="4" fill="#f44336" />
<text x="920" y="40" font-size="13" fill="#fff" text-anchor="middle">退出</text>
<!-- 步骤条 -->
<rect x="120" y="80" width="784" height="40" rx="6" fill="#fff" stroke="#e0e0e0"/>
<text x="180" y="105" font-size="14" fill="#c2185b" font-weight="bold">① 阅读公告</text>
<text x="330" y="105" font-size="14" fill="#c2185b" font-weight="bold">② 报名信息</text>
<text x="480" y="105" font-size="14" fill="#999">③ 资质审核</text>
<text x="630" y="105" font-size="14" fill="#999">④ 缴纳费用</text>
<text x="780" y="105" font-size="14" fill="#999">⑤ 投标文件</text>
<!-- 连线 -->
<line x1="240" y1="95" x2="320" y2="95" stroke="#c2185b" stroke-width="2"/>
<line x1="400" y1="95" x2="470" y2="95" stroke="#ccc" stroke-width="2"/>
<line x1="550" y1="95" x2="620" y2="95" stroke="#ccc" stroke-width="2"/>
<line x1="700" y1="95" x2="770" y2="95" stroke="#ccc" stroke-width="2"/>
<!-- 主表单卡片 -->
<rect x="120" y="140" width="784" height="520" rx="8" fill="#ffffff" filter="url(#shadow)"/>
<text x="150" y="180" font-size="18" font-weight="bold" fill="#333">报名信息填写</text>
<text x="150" y="205" font-size="14" fill="#888">项目:2026年生产用原材料采购(标段A)</text>
<line x1="150" y1="220" x2="874" y2="220" stroke="#eee" stroke-width="1"/>
<!-- 表单字段 -->
<text x="150" y="260" font-size="14" fill="#333">联系人姓名 <tspan fill="red">*</tspan></text>
<rect x="150" y="270" width="200" height="32" rx="4" fill="#fafafa" stroke="#ccc"/>
<text x="160" y="292" font-size="13" fill="#999">请输入联系人</text>
<text x="420" y="260" font-size="14" fill="#333">手机号 <tspan fill="red">*</tspan></text>
<rect x="420" y="270" width="200" height="32" rx="4" fill="#fafafa" stroke="#ccc"/>
<text x="430" y="292" font-size="13" fill="#999">请输入手机号</text>
<text x="150" y="330" font-size="14" fill="#333">投标标段(可多选)</text>
<rect x="150" y="340" width="300" height="32" rx="4" fill="#fafafa" stroke="#ccc"/>
<text x="160" y="362" font-size="13" fill="#999">☑ 标段A ☐ 标段B ☐ 标段C</text>
<text x="150" y="400" font-size="14" fill="#333">上传营业执照 <tspan fill="red">*</tspan></text>
<rect x="150" y="410" width="400" height="80" rx="4" stroke-dasharray="5,5" fill="#fafafa" stroke="#ccc"/>
<text x="280" y="445" font-size="13" fill="#999" text-anchor="middle">点击或拖拽上传(支持 PDF/JPG,≤10MB</text>
<text x="280" y="470" font-size="12" fill="#aaa" text-anchor="middle">已上传:0个文件</text>
<text x="150" y="520" font-size="14" fill="#333">业绩证明材料(可选)</text>
<rect x="150" y="530" width="400" height="60" rx="4" stroke-dasharray="5,5" fill="#fafafa" stroke="#ccc"/>
<text x="280" y="565" font-size="13" fill="#999" text-anchor="middle">点击上传合同/业绩报告</text>
<!-- 按钮区 -->
<rect x="150" y="610" width="120" height="36" rx="4" fill="#e0e0e0"/>
<text x="210" y="634" font-size="14" fill="#666" text-anchor="middle">返回</text>
<rect x="600" y="610" width="160" height="36" rx="4" fill="#c2185b"/>
<text x="680" y="634" font-size="14" fill="#fff" text-anchor="middle">提交报名</text>
<text x="630" y="655" font-size="12" fill="#888">* 提交后不可修改,请仔细核对</text>
<!-- 阴影定义 -->
<defs>
<filter id="shadow" x="-5%" y="-5%" width="110%" height="110%">
<feDropShadow dx="0" dy="4" stdDeviation="6" flood-opacity="0.06"/>
</filter>
</defs>
</svg>
```
## 状态机与内部处理逻辑
| 状态码 | 状态名称 | 描述 | 触发动作 |
| -------------------- | ------ | ------------------- | --------------------- |
| `DRAFT` | 草稿 | 供应商正在填写报名表单,尚未提交 | 保存草稿,允许修改 |
| `PENDING_REVIEW` | 待审核 | 提交报名后,等待AI初审或人工评审 | 触发AI审核;若需人工,则生成评审任务 |
| `AI_PASS` | AI自动通过 | 资质齐全,无风险,直接通过 | 发送审核通过通知,生成缴费单 |
| `MANUAL_REVIEW` | 人工评审中 | 需采购部门/供应商管理专员人工确认 | 推送待办至相关人员,超时催办 |
| `REJECTED` | 审核不通过 | 资质不符或存在风险,被驳回 | 发送拒绝原因,允许重新提交(在报名截止前) |
| `PENDING_PAYMENT` | 待缴费 | 审核通过,等待缴纳标书费/保证金 | 生成缴费单,发送缴费通知 |
| `PAYMENT_CONFIRMING` | 缴费确认中 | 供应商已上传凭证,等待财务确认 | 发起OA审批,财务确认到账 |
| `PAYMENT_FAILED` | 缴费失败 | 超时未缴或财务确认为未到账 | 取消报名资格,释放名额 |
| `PAID` | 已缴费 | 缴费成功,招标文件已解锁 | 开放文件下载权限,记录缴费信息 |
| `BID_SUBMITTED` | 已投标 | 投标文件加密上传完成 | 存证哈希,报名流程结束 |
| `CANCELLED` | 已取消 | 供应商主动取消报名,或项目废标导致失效 | 退回保证金(如有),归档取消记录 |
## 关键业务规则与异常处理
| 场景 | 处理方式 |
| ------------------------ | ------------------------------------------------------------------ |
| **标书费/保证金支付超时** | 系统在截止时间前24h/12h/1h 三级催办;超时自动取消报名资格并释放名额。 |
| **同一供应商重复报名** | 系统检测到同一税号或统一信用代码的供应商已报名,提示“已报名,不能重复提交”。 |
| **资质文件大小/格式不符** | 前端限制 + 后端二次校验,不符合时返回明确错误提示。 |
| **报名截止时间已过** | 按钮置灰,提示“报名已截止”,不允许新提交。 |
| **==联合体投标(待确定,留扩展接口)==** | 允许在报名时添加联合体成员(需分别提供资质),系统自动校验成员关联关系。 |
| **无感登录票据过期** | 跳转时若票据超时,提示“登录状态已过期,请重新登录”,跳转至登录页。 |
| **财务确认超时** | 系统自动催办财务审批人;若超时,支持招标专员手动确认(需备注理由)。 |
| **==投标文件加密方式(待确定)==** | 沿用系统统一加密方案(平台端加解密),同时为供应商提供“个人加密”选项(开关),若选个人加密则生成独立密钥,开标时需供应商在线解密。 |
## 通知与提醒策略
| 触发事件 | 通知方式 | 接收方 |
| --------------- | ----------------- | ----- |
| 审核通过/不通过 | 站内信 + 邮件 | 供应商 |
| 缴费提醒(含截止时间) | 站内信 + 邮件 + 短信(可选) | 供应商 |
| 财务确认到账 | 站内信 | 供应商 |
| 招标文件可下载 | 站内信 + 邮件 | 供应商 |
| 投标截止前24h/1h | 站内信 + 邮件 | 供应商 |
| 人工评审任务待办 | 站内信 + 邮件 | 评审人员 |
| 财务审批待办 | 站内信 + 邮件 | 财务人员 |
| 系统异常告警(支付回调失败等) | 飞书/短信 | 系统管理员 |
## 性能与安全要求
- **并发报名**:支持单项目100+供应商同时报名,响应时间≤3秒。
- **文件上传**:支持断点续传,单个文件≤500MB,总大小≤2GB。
- **数据加密**:报名表单中的联系人手机号、身份证号采用字段级加密存储
- **防机器人**:报名提交时增加图形验证码或行为验证,防止恶意刷单。
- **日志审计**:所有报名操作(登录、提交、审核、缴费)均记录审计日志,保留≥7年。
## 附录:报名表单字段定义
| 字段名 | 类型 | 必填 | 说明 |
| --------------------- | ------ | --- | --------------------------------- |
| `projectId` | String | 是 | 招标项目ID,自动带入 |
| `bidSection` | Array | 是 | 投标标段(多选) |
| `contactName` | String | 是 | 联系人姓名 |
| `contactPhone` | String | 是 | 手机号,需短信验证(可选) |
| `contactEmail` | String | 是 | 邮箱 |
| `qualificationFiles` | Array | 是 | 资质文件列表(营业执照、资质证书、授权书等),每个文件需有类型标签 |
| `performanceFiles` | Array | 否 | 业绩证明文件 |
| `jointVentureMembers` | Array | 否 | 联合体成员列表(供应商ID+名称+税号) |
| `remarks` | String | 否 | 备注 |
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,527 @@
# 外部专家注册模块
| 文档名称 | 智慧招标平台(STP)外部专家注册模块详细设计 |
| :--- | :--- |
| **文档版本** | V1.1 |
| **编制日期** | 2026-06-29 |
| **关联文档** | 《需求分析说明书 V1.4》《评委管理规定》《SRM 与 STP 数据交换接口方案说明书》 |
---
## 1. 模块概述
### 1.1 建设目标
建立面向**社会外部评标专家**的在线注册、准入审核与档案管理体系,实现专家信息的结构化采集、AI辅助资质核验,并与后续的**专家抽取、智能匹配、在线评标、劳务报酬结算**全流程贯通。
### 1.2 设计原则
| 原则 | 说明 |
| :------- | :--------------------------------------- |
| **准入严控** | 严格遵循《评委管理规定》4.1/4.2要求,设置多级审核(AI初筛+人工复核)。 |
| **信息完备** | 采集字段覆盖专业能力、执业资格、收款账户等全维度。 |
| **隐私保护** | 身份证号、手机号、银行账户等敏感信息采用**字段级加密**存储(内存级密钥)。 |
| **智能提效** | AI自动识别证件、校验真伪、关联风险图谱,大幅降低人工审核成本。 |
| **统一身份** | 注册完成后自动生成STP平台账号,支持手机号/邮箱登录,无需二次注册。 |
### 1.3 适用角色与场景
| 角色 | 场景 |
| :--- | :--- |
| **外部专家申请人** | 通过招标门户“专家注册”入口提交申请,查询审核进度。 |
| **专家管理员(招标专员)** | 在后台审核专家资料、激活/驳回/冻结专家账户。 |
| **合规审计人员** | 查阅专家注册审计轨迹、关联关系图谱。 |
| **AI 审核服务** | 自动执行OCR识别、证件验真、利益冲突初筛(仅作后台参考,不要求专家主动声明)。 |
---
## 2. 页面布局与 UI 设计
### 2.1 整体注册流程步骤条(SVG 线框图 - 顶部引导)
```svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 120" font-family="'Microsoft YaHei', sans-serif">
<rect width="1000" height="120" fill="#f8f9fc" rx="8"/>
<!-- 步骤条背景 -->
<rect x="50" y="40" width="900" height="40" rx="20" fill="#fff" stroke="#e0e4ec"/>
<!-- 步骤1:已激活 -->
<circle cx="200" cy="60" r="18" fill="#c2185b"/>
<text x="200" y="66" text-anchor="middle" font-size="14" fill="#fff" font-weight="bold">1</text>
<text x="200" y="100" text-anchor="middle" font-size="13" fill="#c2185b" font-weight="bold">基本信息</text>
<!-- 连接线1 -->
<line x1="218" y1="60" x2="292" y2="60" stroke="#c2185b" stroke-width="3"/>
<!-- 步骤2:进行中 -->
<circle cx="310" cy="60" r="18" fill="#c2185b"/>
<text x="310" y="66" text-anchor="middle" font-size="14" fill="#fff" font-weight="bold">2</text>
<text x="310" y="100" text-anchor="middle" font-size="13" fill="#c2185b" font-weight="bold">专业资质</text>
<!-- 连接线2 -->
<line x1="328" y1="60" x2="402" y2="60" stroke="#e0e4ec" stroke-width="3"/>
<!-- 步骤3:未开始 -->
<circle cx="420" cy="60" r="18" fill="#e0e4ec"/>
<text x="420" y="66" text-anchor="middle" font-size="14" fill="#8895aa">3</text>
<text x="420" y="100" text-anchor="middle" font-size="13" fill="#8895aa">收款账户</text>
<!-- 连接线3 -->
<line x1="438" y1="60" x2="512" y2="60" stroke="#e0e4ec" stroke-width="3"/>
<!-- 步骤4 -->
<circle cx="530" cy="60" r="18" fill="#e0e4ec"/>
<text x="530" y="66" text-anchor="middle" font-size="14" fill="#8895aa">4</text>
<text x="530" y="100" text-anchor="middle" font-size="13" fill="#8895aa">提交审核</text>
<!-- 提示文字 -->
<text x="900" y="35" text-anchor="end" font-size="12" fill="#8895aa">* 带红色星号均为必填项</text>
</svg>
```
### 2.2 注册主表单页面(SVG 线框图 - 以“基本信息”页为例)
```svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1100 900" font-family="'Microsoft YaHei', 'PingFang SC', sans-serif">
<defs>
<filter id="shadowLight" x="-2%" y="-2%" width="104%" height="104%">
<feDropShadow dx="0" dy="4" stdDeviation="8" flood-color="#000" flood-opacity="0.04"/>
</filter>
<linearGradient id="brandGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#c2185b"/>
<stop offset="100%" stop-color="#e91e63"/>
</linearGradient>
</defs>
<!-- 背景 -->
<rect width="1100" height="900" fill="#f5f7fa"/>
<!-- 主卡片 -->
<rect x="80" y="30" width="940" height="840" rx="16" fill="#fff" filter="url(#shadowLight)"/>
<!-- 标题区 -->
<text x="120" y="80" font-size="22" font-weight="bold" fill="#1a2332">外部专家注册</text>
<text x="120" y="108" font-size="14" fill="#8895aa">请完整填写以下信息,所有数据将受到严格加密保护</text>
<line x1="120" y1="125" x2="980" y2="125" stroke="#f0f2f7" stroke-width="2"/>
<!-- ===== 第一行:姓名 + 性别 ===== -->
<text x="120" y="170" font-size="14" fill="#4a5568">真实姓名 <tspan fill="#e91e63">*</tspan></text>
<rect x="120" y="180" width="260" height="40" rx="6" fill="#fafbfc" stroke="#e0e4ec"/>
<text x="135" y="206" font-size="14" fill="#b0bbcc">请输入中文姓名</text>
<text x="430" y="170" font-size="14" fill="#4a5568">性别 <tspan fill="#e91e63">*</tspan></text>
<rect x="430" y="180" width="260" height="40" rx="6" fill="#fafbfc" stroke="#e0e4ec"/>
<text x="445" y="206" font-size="14" fill="#b0bbcc">请选择</text>
<path d="M 670 200 L 678 208 L 686 200" stroke="#b0bbcc" stroke-width="2" fill="none"/>
<!-- ===== 第二行:身份证号(脱敏提示) ===== -->
<text x="120" y="250" font-size="14" fill="#4a5568">身份证号码 <tspan fill="#e91e63">*</tspan></text>
<rect x="120" y="260" width="420" height="40" rx="6" fill="#fafbfc" stroke="#e0e4ec"/>
<text x="135" y="286" font-size="14" fill="#b0bbcc">请输入18位身份证号</text>
<text x="560" y="286" font-size="12" fill="#8895aa">🔒 将使用AES-256加密存储</text>
<!-- ===== 第三行:出生日期(自动计算) ===== -->
<text x="120" y="330" font-size="14" fill="#4a5568">出生日期</text>
<rect x="120" y="340" width="260" height="40" rx="6" fill="#f5f6fa" stroke="#e0e4ec"/>
<text x="135" y="366" font-size="14" fill="#b0bbcc">由身份证自动识别</text>
<text x="430" y="330" font-size="14" fill="#4a5568">年龄</text>
<rect x="430" y="340" width="260" height="40" rx="6" fill="#f5f6fa" stroke="#e0e4ec"/>
<text x="445" y="366" font-size="14" fill="#b0bbcc">自动计算</text>
<!-- ===== 第四行:手机号 + 邮箱 ===== -->
<text x="120" y="410" font-size="14" fill="#4a5568">手机号码 <tspan fill="#e91e63">*</tspan></text>
<rect x="120" y="420" width="260" height="40" rx="6" fill="#fafbfc" stroke="#e0e4ec"/>
<text x="135" y="446" font-size="14" fill="#b0bbcc">请输入手机号</text>
<text x="430" y="410" font-size="14" fill="#4a5568">电子邮箱 <tspan fill="#e91e63">*</tspan></text>
<rect x="430" y="420" width="260" height="40" rx="6" fill="#fafbfc" stroke="#e0e4ec"/>
<text x="445" y="446" font-size="14" fill="#b0bbcc">请输入常用邮箱</text>
<!-- ===== 第五行:工作单位 + 职务 ===== -->
<text x="120" y="490" font-size="14" fill="#4a5568">工作单位</text>
<rect x="120" y="500" width="260" height="40" rx="6" fill="#fafbfc" stroke="#e0e4ec"/>
<text x="135" y="526" font-size="14" fill="#b0bbcc">请输入单位全称</text>
<text x="430" y="490" font-size="14" fill="#4a5568">现从事专业 <tspan fill="#e91e63">*</tspan></text>
<rect x="430" y="500" width="260" height="40" rx="6" fill="#fafbfc" stroke="#e0e4ec"/>
<text x="445" y="526" font-size="14" fill="#b0bbcc">如:化工工艺、设备管理</text>
<!-- ===== 第六行:职称 ===== -->
<text x="120" y="570" font-size="14" fill="#4a5568">技术职称 <tspan fill="#e91e63">*</tspan></text>
<rect x="120" y="580" width="260" height="40" rx="6" fill="#fafbfc" stroke="#e0e4ec"/>
<text x="135" y="606" font-size="14" fill="#b0bbcc">高级/副高级/中级/初级</text>
<rect x="440" y="580" width="260" height="40" rx="6" fill="#fafbfc" stroke="#e0e4ec"/>
<text x="455" y="606" font-size="14" fill="#b0bbcc">职称专业(如:化工工程)</text>
<!-- ===== 第七行:文件上传(资质证书) ===== -->
<text x="120" y="660" font-size="14" fill="#4a5568">职称/资质证书扫描件 <tspan fill="#e91e63">*</tspan></text>
<rect x="120" y="670" width="560" height="70" rx="6" stroke-dasharray="6,4" fill="#fafbfc" stroke="#c2185b"/>
<text x="400" y="700" text-anchor="middle" font-size="14" fill="#c2185b">📄 点击上传或拖拽文件(支持 PDF/JPG/PNG,≤20MB</text>
<text x="400" y="725" text-anchor="middle" font-size="12" fill="#8895aa">已上传:职称证.pdf (2.3MB) <tspan fill="#4caf50">✅ 识别完成</tspan></text>
<!-- ===== 底部按钮 ===== -->
<line x1="120" y1="770" x2="980" y2="770" stroke="#f0f2f7" stroke-width="2"/>
<rect x="740" y="790" width="120" height="40" rx="6" fill="#e0e4ec"/>
<text x="800" y="816" text-anchor="middle" font-size="14" fill="#8895aa">上一步</text>
<rect x="880" y="790" width="140" height="40" rx="6" fill="url(#brandGrad)"/>
<text x="950" y="816" text-anchor="middle" font-size="14" font-weight="bold" fill="#fff">下一步 →</text>
<!-- 右侧信息浮窗 -->
<rect x="720" y="140" width="260" height="180" rx="8" fill="#fff3e0" stroke="#ffcc80"/>
<text x="740" y="168" font-size="14" font-weight="bold" fill="#e65100">📌 注册须知</text>
<text x="740" y="195" font-size="12" fill="#bf360c">1. 所有信息须与身份证及职称证书一致</text>
<text x="740" y="218" font-size="12" fill="#bf360c">2. 集团公司将对信息进行真实性核验</text>
<text x="740" y="241" font-size="12" fill="#bf360c">3. 入库专家须遵守《评委管理规定》</text>
<text x="740" y="264" font-size="12" fill="#bf360c">4. 个人信息变更需及时在线更新</text>
</svg>
```
---
## 3. 详细业务流程
```mermaid
%%{init: {'theme':'base', 'themeVariables': { 'primaryColor':'#c2185b','primaryTextColor':'#fff','primaryBorderColor':'#880e4f','lineColor':'#555','secondaryColor':'#fce4ec'}}}%%
flowchart TD
subgraph 专家申请人端
A[访问招标门户] --> B[点击“专家注册”]
B --> C[填写基本信息<br>(姓名/身份证/手机/邮箱等)]
C --> D[填写专业资质<br>(职称/执业资格/工作经历)]
D --> E[签署评标纪律承诺书]
E --> F[填写收款账户信息]
F --> G[提交注册申请]
G --> H[查看审核状态]
H -- 驳回 --> I[根据驳回原因修改信息]
I --> G
H -- 通过 --> J[收到激活通知邮件/短信]
J --> K[首次登录设置密码]
K --> L[进入专家工作台]
end
subgraph STP平台后端
STP1[接收注册申请] --> STP2[触发AI初审]
STP2 --> STP3{AI检查项}
STP3 -->|OCR识别证件| STP4[提取姓名/身份证号/有效期]
STP3 -->|真伪核验| STP5[对接公安部/学信网/天眼查]
STP3 -->|利益冲突图谱(参考)| STP6[扫描与黑名单/关联企业关系]
STP4 --> STP7[生成AI审核报告]
STP5 --> STP7
STP6 --> STP7
STP7 --> STP8{AI评估结果}
STP8 -->|高风险/不通过| STP9[自动驳回 + 发送原因]
STP8 -->|中风险/待人工| STP10[推送至专家管理员待办]
STP8 -->|低风险/通过| STP11[自动进入人工抽检池]
STP10 --> STP12[人工复核资料]
STP11 --> STP12
STP12 --> STP13{复核决定}
STP13 -->|驳回| STP14[记录驳回原因 + 通知专家]
STP13 -->|需补充材料| STP15[发起补充通知]
STP13 -->|通过| STP16[生成专家档案编号]
STP16 --> STP17[写入专家库主数据]
STP17 --> STP18[自动创建STP登录账号]
STP18 --> STP19[发送激活通知]
STP19 --> STP20[记录全流程审计日志]
end
subgraph 外部系统
EXT1[公安身份认证库]
EXT2[学信网/职称系统]
EXT3[天眼查/企查查]
EXT4[邮件短信网关]
end
STP5 -.-> EXT1
STP5 -.-> EXT2
STP6 -.-> EXT3
STP19 -.-> EXT4
H -- 补充材料 --> STP15
STP14 --> H
```
---
## 4. 核心流程时序图
```mermaid
sequenceDiagram
participant U as 专家申请人
participant FE as STP前端
participant GW as API网关
participant RegSvc as 注册服务
participant AISvc as AI审核服务
participant IDSvc as 身份认证服务(外部)
participant DB as 专家库数据库
participant Admin as 专家管理员
participant Notify as 通知服务
U->>FE: 填写注册表单 + 上传证件
FE->>FE: 前端校验(非空/手机格式/身份证校验位)
FE->>GW: POST /api/v1/expert/register
GW->>RegSvc: 转发请求
RegSvc->>DB: 校验身份证号是否已注册(防重)
alt 已存在
DB-->>RegSvc: 记录已存在
RegSvc-->>FE: 409 Conflict { message: "该身份证已注册" }
else 未注册
RegSvc->>AISvc: 异步启动AI审核任务(OCR+验真+图谱)
RegSvc-->>FE: 202 Accepted { applicationId: "EXP_2024001" }
FE-->>U: 显示“提交成功,审核中...”
AISvc->>IDSvc: 调用公安接口校验身份证号与姓名
IDSvc-->>AISvc: 实名验证通过/失败
AISvc->>AISvc: OCR识别职称证书关键字段
AISvc->>AISvc: 扫描企业关联图谱(作为利益冲突参考,不要求专家声明)
AISvc-->>RegSvc: 返回AI审核结论(Pass/Review/Reject
alt AI高风险或敏感
RegSvc->>DB: 状态更新为 PENDING_MANUAL
RegSvc->>Admin: 推送待办任务(站内+邮件)
Admin->>RegSvc: POST /api/v1/expert/audit { action: approve/reject }
else AI低风险
RegSvc->>DB: 状态更新为 APPROVED(自动)
end
RegSvc->>DB: 生成专家档案(EXP_XXXXX
RegSvc->>RegSvc: 创建STP登录账号(默认手机号+随机密码)
RegSvc->>Notify: 发送激活通知(含账号及首次登录链接)
Notify-->>U: 短信+邮件
U->>FE: 点击激活链接
FE->>RegSvc: POST /api/v1/expert/activate { password }
RegSvc->>DB: 更新密码哈希(Argon2id
RegSvc-->>FE: 激活成功,跳转至专家工作台
end
```
---
## 5. 专家激活流程
以下是专家激活设置密码的详细流程图,清晰展示了从审核通过到账号激活的全过程:
```mermaid
flowchart TD
A[人工/AI审核通过] --> B[系统自动创建STP账号]
B --> C[生成随机临时密码(仅系统内部占位)]
C --> D[发送激活通知邮件/短信<br>(内含激活链接,不含密码)]
D --> E[专家收到通知,点击激活链接]
E --> F[跳转至激活页面]
F --> G[专家填写新密码及确认密码]
G --> H{前端校验}
H -->|格式不符| I[提示密码不满足安全策略<br>(长度≥8,含大小写、数字、特殊字符)]
I --> G
H -->|通过| J[提交激活请求<br>POST /api/v1/expert/activate]
J --> K[后端校验链接有效性及过期时间]
K -->|无效/过期| L[提示链接失效,重新发送]
L --> E
K -->|有效| M[对密码进行Argon2id哈希加密]
M --> N[更新数据库:<br>密码哈希 + 状态ACTIVATED + 激活时间]
N --> O[记录审计日志]
O --> P[返回激活成功]
P --> Q[专家跳转至专家工作台]
Q --> R[流程结束]
style A fill:#e8f5e9,stroke:#388e3c
style R fill:#e0e0e0,stroke:#616161
style C fill:#fff3e0,stroke:#ff9800
style M fill:#fce4ec,stroke:#c2185b
```
**流程要点说明:**
- **随机密码未告知专家**:系统生成后仅用于初始化账户,专家通过激活链接自行设置密码,全程无需接触该临时密码。
- **密码安全策略**:前端校验密码复杂度(长度、字符类型),后端再次校验并哈希存储(Argon2id)。
- **链接安全性**:激活链接包含唯一token,后端校验其有效性与过期时间(通常24小时内有效),防止重放攻击。
- **审计追踪**:所有操作(创建账号、发送通知、激活成功)均有日志记录。
## 6. 数据模型设计
### 6.1 外部专家主表 `t_expert_profile`
| 字段名 | 类型 | 约束 | 说明 |
| :--------------------- | :----------- | :--------------- | :----------------------------------------------------------------- |
| `expert_id` | VARCHAR(32) | PK | 专家档案编号(EXP_YYYYMMDD_XXX |
| `user_id` | VARCHAR(32) | FK | 关联STP用户账号表(登录凭证) |
| `real_name` | VARCHAR(64) | NOT NULL | 真实姓名 |
| `id_card_no` | VARCHAR(256) | NOT NULL, UNIQUE | 身份证号(**字段级加密存储**) |
| `gender` | TINYINT | | 1-男,2-女 |
| `birth_date` | DATE | | 出生日期(由身份证提取) |
| `phone` | VARCHAR(256) | NOT NULL | 手机号(字段级加密) |
| `email` | VARCHAR(128) | NOT NULL | 电子邮箱 |
| `company` | VARCHAR(128) | | 工作单位 |
| `profession` | VARCHAR(64) | NOT NULL | 从事专业 |
| `title` | VARCHAR(32) | NOT NULL | 职称等级(高级/副高/中级) |
| `title_major` | VARCHAR(64) | | 职称专业 |
| `qualification_files` | JSON | | 资质证书文件URL列表(含OCR元数据) |
| `bank_name` | VARCHAR(64) | | 开户银行 |
| `bank_account` | VARCHAR(256) | | 银行账号(字段级加密) |
| `expertise_tags` | JSON | | 擅长领域标签(用于智能匹配) |
| `status` | VARCHAR(20) | NOT NULL | DRAFT/PENDING_AI/PENDING_MANUAL/REJECTED/APPROVED/ACTIVATED/LOCKED |
| `ai_audit_report` | JSON | | AI审核报告(含评分、风险点) |
| `manual_audit_comment` | TEXT | | 人工审核备注 |
| `discipline_signed` | BOOLEAN | DEFAULT 0 | 是否签署评标纪律承诺书 |
| `created_at` | DATETIME | | 注册时间 |
| `updated_at` | DATETIME | | 更新时间 |
| `activated_at` | DATETIME | | 首次激活时间 |
### 6.3 审核记录表 `t_expert_audit_log`
| 字段名 | 类型 | 约束 | 说明 |
| :------------ | :---------- | :-- | :-------------------------- |
| `audit_id` | VARCHAR(32) | PK | |
| `expert_id` | VARCHAR(32) | FK | |
| `auditor` | VARCHAR(64) | | 审核人(AI/人工) |
| `action` | VARCHAR(20) | | APPROVE/REJECT/REQUEST_INFO |
| `reason` | TEXT | | 原因 |
| `attachments` | JSON | | 附件URL |
| `created_at` | DATETIME | | |
---
## 7. 接口详细定义
### 7.1 提交注册申请
| 项目 | 内容 |
| :--- | :--- |
| **路径** | `/api/v1/expert/register` |
| **方法** | `POST` |
| **认证** | 否(公开接口,需图形验证码) |
**请求体(部分字段)**
```json
{
"realName": "王建国",
"idCardNo": "110101199003074477",
"gender": 1,
"phone": "13800138000",
"email": "wangjianguo@example.com",
"company": "清华大学化工系",
"profession": "化学工程",
"title": "SENIOR", // SENIOR/VICE_SENIOR/MIDDLE/JUNIOR
"titleMajor": "化学工程",
"expertiseTags": ["石油化工", "反应工程", "分离技术"],
"bankName": "中国银行北京分行",
"bankAccount": "6217850000000000",
"qualificationFiles": [
{
"fileName": "professor_cert.pdf",
"fileUrl": "https://oss.stp.com/...",
"ocrResult": { "title": "教授", "issueDate": "2018-06" }
}
],
"disciplineAccepted": true, // 是否同意评标纪律
"captchaToken": "a1b2c3d4"
}
```
**成功响应(异步)**
```json
{
"code": "0",
"data": {
"applicationId": "EXP_20260628_001",
"status": "PENDING_AI",
"estimatedTime": "预计24小时内完成初审"
}
}
```
### 7.2 查询审核进度
| 项目 | 内容 |
| :----- | :---------------------------------- |
| **路径** | `/api/v1/expert/application/status` |
| **方法** | `GET` |
| **参数** | `applicationId` |
### 7.3 专家激活
| 项目 | 内容 |
| :----- | :------------------------ |
| **路径** | `/api/v1/expert/activate` |
| **方法** | `POST` |
**请求体**
```json
{
"applicationId": "EXP_20260628_001",
"password": "Abcd@2026#",
"confirmPassword": "Abcd@2026#"
}
```
### 7.4 后台专家审核(管理员)
| 项目 | 内容 |
| :--- | :--- |
| **路径** | `/api/v1/admin/expert/audit` |
| **方法** | `POST` |
| **认证** | 需 ADMIN/EXPERT_MANAGER 角色 |
**请求体**
```json
{
"applicationId": "EXP_20260628_001",
"action": "APPROVE", // APPROVE / REJECT / REQUEST_INFO
"reason": "职称证书已验证,资质符合要求",
"notifyExpert": true
}
```
---
## 8. AI 赋能详细设计
| AI 能力 | 实现方式 | 输出 | 优先级 |
| :------------- | :---------------------------------------- | :----- | :-- |
| **身份证 OCR 解析** | 调用OCR服务(如百度/阿里)提取姓名、号码、有效期,自动回填表单。 | 结构化字段 | P0 |
| **实名认证** | ==对接公安部CTID接口核验身份证号与姓名一致性。== | 通过/不通过 | P0 |
| **职称证书验真** | ==对接人社部/学信网接口(或基于图像防伪特征识别)验证证书真伪。== | 可信度评分 | P1 |
| **专业标签智能推荐** | 根据专家填写的从事专业与历史项目品类,自动匹配并推荐擅长的采购品类标签。 | 标签列表 | P1 |
| **AI初审报告生成** | 汇总各项检查结果,生成带评分(0-100)的建议结论(自动通过/人工复核/拒绝)。 | 审核报告 | P0 |
---
## 9. 业务规则与异常处理
| 场景 | 规则/处理方式 |
| :------------ | :------------------------------------------------------- |
| **身份证号唯一性** | 系统全局唯一,已注册或审核中的身份证不得重复提交。 |
| **手机号/邮箱唯一性** | 同一手机号或邮箱仅能注册一次(支持找回账号)。 |
| **职称等级门槛** | 至少具备**中级及以上**职称方可入库(《评委管理规定》4.2.1)。 |
| **驳回后重提** | 允许在 30 天内修改后重新提交,保留历史审核记录供查。 |
| **虚假信息处理** | 一经发现虚假注册,永久拉入黑名单,并通报集团内各单位。 |
| **评标纪律签署** | 注册时须同意《评标纪律承诺书》,否则无法提交。 |
| **敏感信息脱敏展示** | 后台管理中,身份证号显示为 `110101********4477`,银行账号显示为 `******0000`。 |
| **数据导出** | 支持按专家状态、专业分类、注册时间段导出加密Excel(需二次审批)。 |
---
## 10. 安全与合规设计
| 安全维度 | 落地措施 |
| :--------- | :------------------------------------------------------------ |
| **传输安全** | 全链路 HTTPS + TLS 1.3,文件上传使用预签名URL直传OSS。 |
| **敏感字段加密** | 身份证、手机号、银行账号使用 **AES-256-GCM** 应用层字段级加密,DEK内存漂移(见《需求说明书》4.2)。 |
| **防机器人** | 注册提交需通过图形验证码 + 滑动验证(极验/人机识别)。 |
| **密码策略** | 首次激活强制设置密码(长度≥8,含大写、小写、数字、特殊字符)。 |
| **审计日志** | 记录每次提交、审核、激活操作的IP、设备指纹、操作人,写入哈希链防篡改(见《需求说明书》4.8.1)。 |
| **个人信息保护** | 遵循《个人信息保护法》,注册前弹出《隐私政策》与《用户协议》,须勾选同意。 |
---
## 11. 通知策略矩阵
| 触发事件 | 通知方式 | 通知对象 | 文案要点 |
| :--- | :--- | :--- | :--- |
| 提交注册成功 | 站内信 | 申请人 | 已收到申请,预计24小时反馈 |
| AI 审核不通过 | 邮件+短信 | 申请人 | 详细说明不通过原因及修改指引 |
| 转入人工审核 | 站内信+邮件 | 专家管理员 | 新专家待审核,附AI报告摘要 |
| 人工审核通过 | 邮件+短信 | 申请人 | 恭喜通过审核,请点击链接激活账号 |
| 账号激活成功 | 站内信 | 申请人 | 欢迎成为STP评标专家,可开始接受评标任务 |
| 补充材料通知 | 邮件+短信 | 申请人 | 请于X月X日前补充如下材料... |
@@ -0,0 +1,483 @@
# 新用户注册模块
新用户注册包含了[[供应商注册模块]]和[[外部专家注册模块]] 。
### 供应商注册页面要素:
- 企业全称
- 统一社会信用代码
- 注册资本(万元)
- 法定代表人
- 行业
- 地址
- 经营范围
- 主要产品
- 联系人姓名
- 联系电话(手机号)
- 电子邮箱
- 营业执照
## 供应商注册模块
## 外部专家注册模块
## 1. 模块概述
### 1.1 建设目标
建立面向**社会外部评标专家**的在线注册、准入审核与档案管理体系,实现专家信息的结构化采集、AI辅助资质核验、回避关系预审,并与后续的**专家抽取、智能匹配、在线评标、劳务报酬结算**全流程贯通。
### 1.2 设计原则
| 原则 | 说明 |
| :------- | :-------------------------------------- |
| **信息完备** | 采集字段覆盖专业能力、执业资格、回避关系、收款账户等全维度。 |
| **隐私保护** | 身份证号、手机号、银行账户等敏感信息采用**字段级加密**存储(内存级密钥)。 |
| **统一身份** | 注册完成后自动生成STP平台账号,支持手机号/邮箱登录,无需二次注册。 |
### 1.3 适用角色与场景
| 角色 | 场景 |
| :-------------- | :----------------------------- |
| **外部专家申请人** | 通过招标门户“专家注册”入口提交申请,查询审核进度。 |
| **专家管理员(招标专员)** | 在后台审核专家资料、处理回避申报、激活/驳回/冻结专家账户。 |
| **合规审计人员** | 查阅专家注册审计轨迹、关联关系图谱。 |
| **AI 审核服务** | 自动执行OCR识别、证件验真。 |
---
## 2. 页面布局与 UI 设计
### 2.1 整体注册流程步骤条
```svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 120" font-family="'Microsoft YaHei', sans-serif">
<rect width="1000" height="120" fill="#f8f9fc" rx="8"/>
<!-- 步骤条背景 -->
<rect x="50" y="40" width="900" height="40" rx="20" fill="#fff" stroke="#e0e4ec"/>
<!-- 步骤1:已激活 -->
<circle cx="140" cy="60" r="18" fill="#c2185b"/>
<text x="140" y="66" text-anchor="middle" font-size="14" fill="#fff" font-weight="bold">1</text>
<text x="140" y="100" text-anchor="middle" font-size="13" fill="#c2185b" font-weight="bold">基本信息</text>
<!-- 连接线1 -->
<line x1="158" y1="60" x2="232" y2="60" stroke="#c2185b" stroke-width="3"/>
<!-- 步骤2:进行中 -->
<circle cx="250" cy="60" r="18" fill="#c2185b"/>
<text x="250" y="66" text-anchor="middle" font-size="14" fill="#fff" font-weight="bold">2</text>
<text x="250" y="100" text-anchor="middle" font-size="13" fill="#c2185b" font-weight="bold">专业资质</text>
<!-- 连接线2 -->
<line x1="268" y1="60" x2="562" y2="60" stroke="#e0e4ec" stroke-width="3"/>
<!-- 步骤5 -->
<circle cx="580" cy="60" r="18" fill="#e0e4ec"/>
<text x="580" y="66" text-anchor="middle" font-size="14" fill="#8895aa">5</text>
<text x="580" y="100" text-anchor="middle" font-size="13" fill="#8895aa">提交审核</text>
<!-- 提示文字 -->
<text x="900" y="35" text-anchor="end" font-size="12" fill="#8895aa">* 带红色星号均为必填项</text>
</svg>
```
### 2.2 注册主表单页面
```svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1100 900" font-family="'Microsoft YaHei', 'PingFang SC', sans-serif">
<defs>
<filter id="shadowLight" x="-2%" y="-2%" width="104%" height="104%">
<feDropShadow dx="0" dy="4" stdDeviation="8" flood-color="#000" flood-opacity="0.04"/>
</filter>
<linearGradient id="brandGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#c2185b"/>
<stop offset="100%" stop-color="#e91e63"/>
</linearGradient>
</defs>
<!-- 背景 -->
<rect width="1100" height="900" fill="#f5f7fa"/>
<!-- 主卡片 -->
<rect x="80" y="30" width="940" height="840" rx="16" fill="#fff" filter="url(#shadowLight)"/>
<!-- 标题区 -->
<text x="120" y="80" font-size="22" font-weight="bold" fill="#1a2332">外部专家注册</text>
<text x="120" y="108" font-size="14" fill="#8895aa">请完整填写以下信息,所有数据将受到严格加密保护</text>
<line x1="120" y1="125" x2="980" y2="125" stroke="#f0f2f7" stroke-width="2"/>
<!-- ===== 第一行:姓名 + 性别 ===== -->
<text x="120" y="170" font-size="14" fill="#4a5568">真实姓名 <tspan fill="#e91e63">*</tspan></text>
<rect x="120" y="180" width="260" height="40" rx="6" fill="#fafbfc" stroke="#e0e4ec"/>
<text x="135" y="206" font-size="14" fill="#b0bbcc">请输入中文姓名</text>
<text x="430" y="170" font-size="14" fill="#4a5568">性别 <tspan fill="#e91e63">*</tspan></text>
<rect x="430" y="180" width="260" height="40" rx="6" fill="#fafbfc" stroke="#e0e4ec"/>
<text x="445" y="206" font-size="14" fill="#b0bbcc">请选择</text>
<path d="M 670 200 L 678 208 L 686 200" stroke="#b0bbcc" stroke-width="2" fill="none"/>
<!-- ===== 第二行:身份证号(脱敏提示) ===== -->
<text x="120" y="250" font-size="14" fill="#4a5568">身份证号码 <tspan fill="#e91e63">*</tspan></text>
<rect x="120" y="260" width="420" height="40" rx="6" fill="#fafbfc" stroke="#e0e4ec"/>
<text x="135" y="286" font-size="14" fill="#b0bbcc">请输入18位身份证号</text>
<text x="560" y="286" font-size="12" fill="#8895aa">🔒 将使用AES-256加密存储</text>
<!-- ===== 第三行:出生日期(自动计算) ===== -->
<text x="120" y="330" font-size="14" fill="#4a5568">出生日期</text>
<rect x="120" y="340" width="260" height="40" rx="6" fill="#f5f6fa" stroke="#e0e4ec"/>
<text x="135" y="366" font-size="14" fill="#b0bbcc">由身份证自动识别</text>
<text x="430" y="330" font-size="14" fill="#4a5568">年龄</text>
<rect x="430" y="340" width="260" height="40" rx="6" fill="#f5f6fa" stroke="#e0e4ec"/>
<text x="445" y="366" font-size="14" fill="#b0bbcc">自动计算</text>
<!-- ===== 第四行:手机号 + 邮箱 ===== -->
<text x="120" y="410" font-size="14" fill="#4a5568">手机号码 <tspan fill="#e91e63">*</tspan></text>
<rect x="120" y="420" width="260" height="40" rx="6" fill="#fafbfc" stroke="#e0e4ec"/>
<text x="135" y="446" font-size="14" fill="#b0bbcc">请输入手机号</text>
<text x="430" y="410" font-size="14" fill="#4a5568">电子邮箱 <tspan fill="#e91e63">*</tspan></text>
<rect x="430" y="420" width="260" height="40" rx="6" fill="#fafbfc" stroke="#e0e4ec"/>
<text x="445" y="446" font-size="14" fill="#b0bbcc">请输入常用邮箱</text>
<!-- ===== 第五行:工作单位 + 职务 ===== -->
<text x="120" y="490" font-size="14" fill="#4a5568">工作单位</text>
<rect x="120" y="500" width="260" height="40" rx="6" fill="#fafbfc" stroke="#e0e4ec"/>
<text x="135" y="526" font-size="14" fill="#b0bbcc">请输入单位全称</text>
<text x="430" y="490" font-size="14" fill="#4a5568">现从事专业 <tspan fill="#e91e63">*</tspan></text>
<rect x="430" y="500" width="260" height="40" rx="6" fill="#fafbfc" stroke="#e0e4ec"/>
<text x="445" y="526" font-size="14" fill="#b0bbcc">如:化工工艺、设备管理</text>
<!-- ===== 第六行:职称 ===== -->
<text x="120" y="570" font-size="14" fill="#4a5568">技术职称 <tspan fill="#e91e63">*</tspan></text>
<rect x="120" y="580" width="260" height="40" rx="6" fill="#fafbfc" stroke="#e0e4ec"/>
<text x="135" y="606" font-size="14" fill="#b0bbcc">高级/副高级/中级/初级</text>
<rect x="440" y="580" width="260" height="40" rx="6" fill="#fafbfc" stroke="#e0e4ec"/>
<text x="455" y="606" font-size="14" fill="#b0bbcc">职称专业(如:化工工程)</text>
<!-- ===== 第七行:文件上传(资质证书) ===== -->
<text x="120" y="660" font-size="14" fill="#4a5568">职称/资质证书扫描件 <tspan fill="#e91e63">*</tspan></text>
<rect x="120" y="670" width="560" height="70" rx="6" stroke-dasharray="6,4" fill="#fafbfc" stroke="#c2185b"/>
<text x="400" y="700" text-anchor="middle" font-size="14" fill="#c2185b">📄 点击上传或拖拽文件(支持 PDF/JPG/PNG,≤20MB</text>
<text x="400" y="725" text-anchor="middle" font-size="12" fill="#8895aa">已上传:职称证.pdf (2.3MB) <tspan fill="#4caf50">✅ 识别完成</tspan></text>
<!-- ===== 底部按钮 ===== -->
<line x1="120" y1="770" x2="980" y2="770" stroke="#f0f2f7" stroke-width="2"/>
<rect x="740" y="790" width="120" height="40" rx="6" fill="#e0e4ec"/>
<text x="800" y="816" text-anchor="middle" font-size="14" fill="#8895aa">上一步</text>
<rect x="880" y="790" width="140" height="40" rx="6" fill="url(#brandGrad)"/>
<text x="950" y="816" text-anchor="middle" font-size="14" font-weight="bold" fill="#fff">下一步 →</text>
<!-- 右侧信息浮窗 -->
<rect x="720" y="140" width="260" height="180" rx="8" fill="#fff3e0" stroke="#ffcc80"/>
<text x="740" y="168" font-size="14" font-weight="bold" fill="#e65100">📌 注册须知</text>
<text x="740" y="195" font-size="12" fill="#bf360c">1. 所有信息须与身份证及职称证书一致</text>
<text x="740" y="218" font-size="12" fill="#bf360c">2. 集团公司将对信息进行真实性核验</text>
<text x="740" y="241" font-size="12" fill="#bf360c">3. 入库专家须遵守《评委管理规定》</text>
<text x="740" y="264" font-size="12" fill="#bf360c">4. 个人信息变更需及时在线更新</text>
<text x="740" y="287" font-size="12" fill="#bf360c">5. 评审回避要求详见本章程4.7条</text>
</svg>
```
---
## 4. 核心流程时序图
```mermaid
sequenceDiagram
participant U as 专家申请人
participant FE as STP前端
participant GW as API网关
participant RegSvc as 注册服务
participant AISvc as AI审核服务
participant IDSvc as 身份认证服务(外部)
participant DB as 专家库数据库
participant Admin as 专家管理员
participant Notify as 通知服务
U->>FE: 填写注册表单 + 上传证件
FE->>FE: 前端校验(非空/手机格式/身份证校验位)
FE->>GW: POST /api/v1/expert/register
GW->>RegSvc: 转发请求
RegSvc->>DB: 校验身份证号是否已注册(防重)
alt 已存在
DB-->>RegSvc: 记录已存在
RegSvc-->>FE: 409 Conflict { message: "该身份证已注册" }
else 未注册
RegSvc->>AISvc: 异步启动AI审核任务(OCR+验真+图谱)
RegSvc-->>FE: 202 Accepted { applicationId: "EXP_2024001" }
FE-->>U: 显示“提交成功,审核中...”
AISvc->>IDSvc: 调用公安接口校验身份证号与姓名
IDSvc-->>AISvc: 实名验证通过/失败
AISvc->>AISvc: OCR识别职称证书关键字段
AISvc->>AISvc: 扫描企业关联图谱(回避关系)
AISvc-->>RegSvc: 返回AI审核结论(Pass/Review/Reject
alt AI高风险或敏感
RegSvc->>DB: 状态更新为 PENDING_MANUAL
RegSvc->>Admin: 推送待办任务(站内+邮件)
Admin->>RegSvc: POST /api/v1/expert/audit { action: approve/reject }
else AI低风险
RegSvc->>DB: 状态更新为 APPROVED(自动)
end
RegSvc->>DB: 生成专家档案(EXP_XXXXX
RegSvc->>RegSvc: 创建STP登录账号(默认手机号+随机密码)
RegSvc->>Notify: 发送激活通知(含账号及首次登录链接)
Notify-->>U: 短信+邮件
U->>FE: 点击激活链接
FE->>RegSvc: POST /api/v1/expert/activate { password }
RegSvc->>DB: 更新密码哈希(Argon2id
RegSvc-->>FE: 激活成功,跳转至专家工作台
end
```
---
## 5. 数据模型设计
### 5.1 外部专家主表 `t_expert_profile`
| 字段名 | 类型 | 约束 | 说明 |
| :--- | :--- | :--- | :--- |
| `expert_id` | VARCHAR(32) | PK | 专家档案编号(EXP_YYYYMMDD_XXX |
| `user_id` | VARCHAR(32) | FK | 关联STP用户账号表(登录凭证) |
| `real_name` | VARCHAR(64) | NOT NULL | 真实姓名 |
| `id_card_no` | VARCHAR(256) | NOT NULL, UNIQUE | 身份证号(**字段级加密存储**) |
| `gender` | TINYINT | | 1-男,2-女 |
| `birth_date` | DATE | | 出生日期(由身份证提取) |
| `phone` | VARCHAR(256) | NOT NULL | 手机号(字段级加密) |
| `email` | VARCHAR(128) | NOT NULL | 电子邮箱 |
| `company` | VARCHAR(128) | | 工作单位 |
| `profession` | VARCHAR(64) | NOT NULL | 从事专业 |
| `title` | VARCHAR(32) | NOT NULL | 职称等级(高级/副高/中级) |
| `title_major` | VARCHAR(64) | | 职称专业 |
| `qualification_files` | JSON | | 资质证书文件URL列表(含OCR元数据) |
| `bank_name` | VARCHAR(64) | | 开户银行 |
| `bank_account` | VARCHAR(256) | | 银行账号(字段级加密) |
| `expertise_tags` | JSON | | 擅长领域标签(用于智能匹配) |
| `status` | VARCHAR(20) | NOT NULL | DRAFT/PENDING_AI/PENDING_MANUAL/REJECTED/APPROVED/ACTIVATED/LOCKED |
| `ai_audit_report` | JSON | | AI审核报告(含评分、风险点) |
| `manual_audit_comment` | TEXT | | 人工审核备注 |
| `declaration_signed` | BOOLEAN | DEFAULT 0 | 是否签署回避声明 |
| `created_at` | DATETIME | | 注册时间 |
| `updated_at` | DATETIME | | 更新时间 |
| `activated_at` | DATETIME | | 首次激活时间 |
### 5.2 回避声明表 `t_expert_declaration`
| 字段名 | 类型 | 约束 | 说明 |
| :--- | :--- | :--- | :--- |
| `declaration_id` | VARCHAR(32) | PK | 声明编号 |
| `expert_id` | VARCHAR(32) | FK | 专家ID |
| `declaration_type` | VARCHAR(32) | | 定期声明/项目专项声明 |
| `content` | JSON | | 逐项勾选记录 |
| `signed_at` | DATETIME | | 签署时间 |
| `ip_address` | VARCHAR(64) | | 签署IP |
| `valid_until` | DATETIME | | 有效期(通常1年) |
### 5.3 审核记录表 `t_expert_audit_log`
| 字段名 | 类型 | 约束 | 说明 |
| :--- | :--- | :--- | :--- |
| `audit_id` | VARCHAR(32) | PK | |
| `expert_id` | VARCHAR(32) | FK | |
| `auditor` | VARCHAR(64) | | 审核人(AI/人工) |
| `action` | VARCHAR(20) | | APPROVE/REJECT/REQUEST_INFO |
| `reason` | TEXT | | 原因 |
| `attachments` | JSON | | 附件URL |
| `created_at` | DATETIME | | |
---
## 6. 接口详细定义
### 6.1 提交注册申请
| 项目 | 内容 |
| :--- | :--- |
| **路径** | `/api/v1/expert/register` |
| **方法** | `POST` |
| **认证** | 否(公开接口,需图形验证码) |
**请求体(部分字段)**
```json
{
"realName": "王建国",
"idCardNo": "110101199003074477",
"gender": 1,
"phone": "13800138000",
"email": "wangjianguo@example.com",
"company": "清华大学化工系",
"profession": "化学工程",
"title": "SENIOR", // SENIOR/VICE_SENIOR/MIDDLE/JUNIOR
"titleMajor": "化学工程",
"expertiseTags": ["石油化工", "反应工程", "分离技术"],
"bankName": "中国银行北京分行",
"bankAccount": "6217850000000000",
"qualificationFiles": [
{
"fileName": "professor_cert.pdf",
"fileUrl": "https://oss.stp.com/...",
"ocrResult": { "title": "教授", "issueDate": "2018-06" }
}
],
"declarations": {
"noAffiliation": true,
"noInvestment": true,
"noConsulting": true,
"acceptDiscipline": true
},
"captchaToken": "a1b2c3d4"
}
```
**成功响应(异步)**
```json
{
"code": "0",
"data": {
"applicationId": "EXP_20260628_001",
"status": "PENDING_AI",
"estimatedTime": "预计24小时内完成初审"
}
}
```
### 6.2 查询审核进度
| 项目 | 内容 |
| :--- | :--- |
| **路径** | `/api/v1/expert/application/status` |
| **方法** | `GET` |
| **参数** | `applicationId` / `idCardNo`(二选一) |
### 6.3 专家激活(首次设置密码)
| 项目 | 内容 |
| :--- | :--- |
| **路径** | `/api/v1/expert/activate` |
| **方法** | `POST` |
**请求体**
```json
{
"applicationId": "EXP_20260628_001",
"password": "Abcd@2026#",
"confirmPassword": "Abcd@2026#"
}
```
### 6.4 后台专家审核(管理员)
| 项目 | 内容 |
| :--- | :--- |
| **路径** | `/api/v1/admin/expert/audit` |
| **方法** | `POST` |
| **认证** | 需 ADMIN/EXPERT_MANAGER 角色 |
**请求体**
```json
{
"applicationId": "EXP_20260628_001",
"action": "APPROVE", // APPROVE / REJECT / REQUEST_INFO
"reason": "职称证书已验证,资质符合要求",
"notifyExpert": true
}
```
---
## 7. AI 赋能详细设计
| AI 能力 | 实现方式 | 输出 | 优先级 |
| :--- | :--- | :--- | :--- |
| **身份证 OCR 解析** | 调用OCR服务(如百度/阿里)提取姓名、号码、有效期,自动回填表单。 | 结构化字段 | P0 |
| **实名认证** | 对接公安部CTID接口核验身份证号与姓名一致性。 | 通过/不通过 | P0 |
| **职称证书验真** | 对接人社部/学信网接口(或基于图像防伪特征识别)验证证书真伪。 | 可信度评分 | P1 |
| **利益冲突图谱** | 利用知识图谱匹配专家任职企业是否与集团供应商库/黑名单关联,输出风险等级(绿/黄/红)。 | 风险报告 | P0 |
| **专业标签智能推荐** | 根据专家填写的从事专业与历史项目品类,自动匹配并推荐擅长的采购品类标签。 | 标签列表 | P1 |
| **AI初审报告生成** | 汇总各项检查结果,生成带评分(0-100)的建议结论(自动通过/人工复核/拒绝)。 | 审核报告 | P0 |
---
## 8. 业务规则与异常处理
| 场景 | 规则/处理方式 |
| :--- | :--- |
| **身份证号唯一性** | 系统全局唯一,已注册或审核中的身份证不得重复提交。 |
| **手机号/邮箱唯一性** | 同一手机号或邮箱仅能注册一次(支持找回账号)。 |
| **职称等级门槛** | 至少具备**中级及以上**职称方可入库(《评委管理规定》4.2.1)。 |
| **AI自动通过阈值** | AI评分 ≥ 90 分且无高风险项 → 自动通过;60~89分 → 人工复核;<60分 → 自动驳回。 |
| **驳回后重提** | 允许在 30 天内修改后重新提交,保留历史审核记录供查。 |
| **虚假信息处理** | 一经发现虚假注册,永久拉入黑名单,并通报集团内各单位。 |
| **回避声明有效期** | 每年需重新签署一次,到期前30天系统自动催办。 |
| **敏感信息脱敏展示** | 后台管理中,身份证号显示为 `110101********4477`,银行账号显示为 `******0000`。 |
| **数据导出** | 支持按专家状态、专业分类、注册时间段导出加密Excel(需二次审批)。 |
---
## 9. 安全与合规设计
| 安全维度 | 落地措施 |
| :--- | :--- |
| **传输安全** | 全链路 HTTPS + TLS 1.3,文件上传使用预签名URL直传OSS。 |
| **敏感字段加密** | 身份证、手机号、银行账号使用 **AES-256-GCM** 应用层字段级加密,DEK内存漂移(见《需求说明书》4.2)。 |
| **防机器人** | 注册提交需通过图形验证码 + 滑动验证(极验/人机识别)。 |
| **密码策略** | 首次激活强制设置密码(长度≥8,含大写、小写、数字、特殊字符)。 |
| **审计日志** | 记录每次提交、审核、激活操作的IP、设备指纹、操作人,写入哈希链防篡改(见《需求说明书》4.8.1)。 |
| **个人信息保护** | 遵循《个人信息保护法》,注册前弹出《隐私政策》与《用户协议》,须勾选同意。 |
---
## 10. 通知策略矩阵
| 触发事件 | 通知方式 | 通知对象 | 文案要点 |
| :--- | :--- | :--- | :--- |
| 提交注册成功 | 站内信 | 申请人 | 已收到申请,预计24小时反馈 |
| AI 审核不通过 | 邮件+短信 | 申请人 | 详细说明不通过原因及修改指引 |
| 转入人工审核 | 站内信+邮件 | 专家管理员 | 新专家待审核,附AI报告摘要 |
| 人工审核通过 | 邮件+短信 | 申请人 | 恭喜通过审核,请点击链接激活账号 |
| 账号激活成功 | 站内信 | 申请人 | 欢迎成为STP评标专家,可开始接受评标任务 |
| 回避声明临期 | 邮件 | 专家 | 请及时重新签署回避声明,否则无法参与评标 |
| 补充材料通知 | 邮件+短信 | 申请人 | 请于X月X日前补充如下材料... |
---
## 11. 与整体架构的集成衔接
```mermaid
graph LR
subgraph 专家生命周期
A[注册申请] --> B[AI/人工审核]
B --> C[生成账号]
C --> D[激活登录]
D --> E[专家抽取匹配]
E --> F[在线评标]
F --> G[劳务报酬结算]
end
subgraph 外围系统
H[(专家库)]
I[IAM/认证中心]
J[支付系统]
K[电子档案]
end
C --> I
G --> J
B --> K
E --> H
F --> H
style A fill:#fce4ec,stroke:#c2185b
style D fill:#e8f5e9,stroke:#388e3c
```
---
本详细设计已完整覆盖外部专家从注册入口、信息填报、AI+人工双轨审核、账号激活到后续业务衔接的全链路,严格遵循《评委管理规定》的准入标准,并深度集成平台现有安全体系与AI能力。如需调整字段或审核流层级,可进一步展开讨论。
@@ -0,0 +1,411 @@
# 登录模块
## 1. 模块概述
### 1.1 设计原则
| 原则 | 说明 |
| -------- | ----------------------------- |
| **安全优先** | 传输全程加密,防暴力破解。 |
| **角色分流** | 通过“内部员工登录”开关明确区分认证路由,杜绝权限越界。 |
| **存量兼容** | 无缝兼容 SRM 存量供应商账号,降低用户迁移成本。 |
| **体验极简** | 支持账号/手机双因子登录,并提供“记住我”与智能异常提示。 |
### 1.2 角色与认证路由映射
| 用户类型 | 开关状态 | 认证目标 | 说明 |
| ---------- | --------- | -------------------- | ------------------------------------------------- |
| **集团内部员工** | 开启(员工模式) | IAM(集团统一身份认证) | 跳转至 IAM 登录页或调用 OAuth2/OIDC 接口,不校验平台本地密码。 |
| **外部供应商** | 关闭(供应商模式) | STP 本地用户表 / SRM 回退认证 | 优先校验 STP 本地账号;若不存在,自动路由至 SRM 验证(满足 SRM 存量用户无感登录)。 |
## 2. 页面布局与 UI 设计
### 2.1 整体布局
```svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 800" font-family="'Microsoft YaHei', 'PingFang SC', sans-serif">
<defs>
<linearGradient id="bgGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#f8f9fc"/>
<stop offset="100%" stop-color="#e9ecef"/>
</linearGradient>
<linearGradient id="brandGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#c2185b"/>
<stop offset="100%" stop-color="#e91e63"/>
</linearGradient>
<filter id="cardShadow" x="-5%" y="-5%" width="110%" height="110%">
<feDropShadow dx="0" dy="10" stdDeviation="20" flood-color="#000" flood-opacity="0.08"/>
</filter>
<filter id="inputShadow" x="-2%" y="-2%" width="104%" height="104%">
<feDropShadow dx="0" dy="2" stdDeviation="4" flood-color="#c2185b" flood-opacity="0.1"/>
</filter>
</defs>
<!-- 背景 -->
<rect width="1200" height="800" fill="url(#bgGrad)"/>
<!-- 左侧品牌区域 -->
<rect x="0" y="0" width="600" height="800" fill="url(#brandGrad)"/>
<circle cx="300" cy="350" r="160" fill="rgba(255,255,255,0.08)"/>
<circle cx="300" cy="350" r="100" fill="rgba(255,255,255,0.12)"/>
<text x="300" y="240" text-anchor="middle" font-size="48" font-weight="bold" fill="#fff" letter-spacing="4">智慧招标平台</text>
<text x="300" y="290" text-anchor="middle" font-size="16" fill="rgba(255,255,255,0.8)" letter-spacing="2">Smart Tendering Platform</text>
<!-- 左侧装饰性图标(简化招投标场景) -->
<g transform="translate(300, 420)" stroke="rgba(255,255,255,0.3)" stroke-width="2" fill="none">
<rect x="-80" y="-60" width="160" height="120" rx="8"/>
<line x1="-40" y1="-30" x2="40" y2="-30"/>
<line x1="-40" y1="-10" x2="30" y2="-10"/>
<line x1="-40" y1="10" x2="20" y2="10"/>
<rect x="-30" y="30" width="60" height="20" rx="3" fill="rgba(255,255,255,0.2)"/>
</g>
<text x="300" y="540" text-anchor="middle" font-size="14" fill="rgba(255,255,255,0.7)">AI 原生 · 全流程线上闭环 · 主动式智能</text>
<text x="300" y="570" text-anchor="middle" font-size="12" fill="rgba(255,255,255,0.5)">覆盖公开招标 · 邀请招标 · 竞争性谈判 · 单一来源</text>
<!-- 右侧登录卡片 -->
<rect x="660" y="120" width="440" height="560" rx="16" fill="#ffffff" filter="url(#cardShadow)"/>
<!-- 卡片顶部:欢迎语 -->
<text x="880" y="180" text-anchor="middle" font-size="24" font-weight="bold" fill="#1a2332">欢迎登录</text>
<text x="880" y="210" text-anchor="middle" font-size="14" fill="#8895aa">登录您的账户,开始智能招采之旅</text>
<!-- 角色切换开关 -->
<rect x="700" y="235" width="360" height="40" rx="20" fill="#f0f2f7" stroke="#e0e4ec"/>
<rect x="702" y="237" width="176" height="36" rx="18" fill="#c2185b" id="toggleSlider"/>
<text x="790" y="261" text-anchor="middle" font-size="14" font-weight="bold" fill="#fff">👤 内部员工</text>
<text x="970" y="261" text-anchor="middle" font-size="14" fill="#8895aa">🏢 外部账号</text>
<text x="880" y="290" text-anchor="middle" font-size="12" fill="#c2185b">当前:内部员工模式(走 IAM 认证)</text>
<!-- Tab 切换:账号登录 / 手机登录 -->
<rect x="700" y="315" width="360" height="2" fill="#e0e4ec"/>
<rect x="700" y="315" width="100" height="2" fill="#c2185b"/>
<text x="730" y="340" font-size="16" font-weight="bold" fill="#1a2332">账号登录</text>
<text x="880" y="340" font-size="16" fill="#8895aa" text-anchor="middle">手机验证码</text>
<!-- 账号输入 -->
<text x="700" y="385" font-size="14" fill="#4a5568">账号 / 手机号</text>
<rect x="700" y="395" width="360" height="44" rx="8" fill="#f7f8fc" stroke="#e0e4ec"/>
<text x="715" y="423" font-size="14" fill="#b0bbcc">请输入您的账号</text>
<circle cx="1020" cy="417" r="8" fill="#c2185b" opacity="0.6"/>
<text x="1025" y="422" font-size="12" fill="#fff">!</text>
<!-- 密码输入 -->
<text x="700" y="465" font-size="14" fill="#4a5568">密码</text>
<rect x="700" y="475" width="360" height="44" rx="8" fill="#f7f8fc" stroke="#e0e4ec"/>
<text x="715" y="503" font-size="14" fill="#b0bbcc">请输入您的密码</text>
<text x="1015" y="503" font-size="14" fill="#8895aa">👁️</text>
<!-- 额外选项:记住我 + 忘记密码 -->
<rect x="700" y="535" width="16" height="16" rx="3" fill="#c2185b"/>
<path d="M704 543 L708 547 L716 537" stroke="#fff" stroke-width="2" fill="none"/>
<text x="724" y="549" font-size="14" fill="#4a5568">记住我</text>
<text x="950" y="549" font-size="14" fill="#c2185b" text-anchor="end" text-decoration="underline">忘记密码?</text>
<!-- 登录按钮 -->
<rect x="700" y="570" width="360" height="48" rx="10" fill="url(#brandGrad)" filter="url(#inputShadow)"/>
<text x="880" y="600" text-anchor="middle" font-size="18" font-weight="bold" fill="#fff">登 录</text>
<!-- 立即注册 & SRM 提示 -->
<text x="700" y="645" font-size="14" fill="#4a5568">还没有账号?</text>
<text x="820" y="645" font-size="14" font-weight="bold" fill="#c2185b" text-decoration="underline">立即注册</text>
<rect x="700" y="660" width="360" height="2" stroke-dasharray="4,4" stroke="#e0e4ec"/>
<rect x="700" y="660" width="100" height="2" fill="#e0e4ec"/>
<text x="880" y="672" text-anchor="middle" font-size="12" fill="#8895aa">温馨提示</text>
<rect x="700" y="685" width="360" height="36" rx="6" fill="#fff3e0" stroke="#ffcc80"/>
<text x="880" y="709" text-anchor="middle" font-size="12" fill="#e65100">💡 原 SRM 注册用户,可直接使用 SRM 账号密码登录,无需重复注册。</text>
<!-- 底部版权 -->
<text x="880" y="780" text-anchor="middle" font-size="11" fill="#c0c8d8">© 2026 滨化集团 | 智慧招标平台 V1.0</text>
</svg>
```
### 2.2 账号登录页面
关键元素:
* 账号输入框
* 密码输入框
* `记住我`选择框。
* `忘记密码`链接
* `登录`按钮
* `内部员工登录`选择框,选择后将走集团 IAM 路由进行身份认证,否则走平台账号路由进行身认证。
* `立即注册`链接
* 登录说明:原 SRM 注册用户,可直接使用 SRM账号密码登录,无需重复注册。
### 2.3 手机验证码登录页面
关键元素:
* 手机号输出框
* 验证码输出框
* 发送验证码按钮
* 登录按钮
### 2.4 页面状态与元素交互说明
| UI 元素 | 交互行为 | 状态变化 |
| ------------------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------ |
| **内部员工 / 供应商 切换滑块** | 点击滑块或文字,滑块滑动至对应位置,下方提示文字切换,表单保持不变,但认证逻辑改变。 | 员工模式:下方显示`当前:内部员工模式(IAM 认证)`,且“忘记密码”链接跳转至 IAM 密码找回页;“立即注册”置灰或隐藏(员工由 HR 系统同步)。供应商模式:恢复常规提示。 |
| **账号登录 / 手机登录 Tab** | 点击切换 Tab,下方表单内容切换。 | 账号登录:显示账号+密码框。手机登录:显示手机号+验证码框+发送验证码按钮。 |
| **记住我** | 勾选后,登录成功时将 `refresh_token` 存入浏览器本地(Secure Storage),有效期为 7 天。 | 复选框状态持久化(基于本地 Cookie 记忆偏好)。 |
| **忘记密码** | 供应商模式:跳转至“重置密码”页(需短信/邮箱验证)。员工模式:跳转至 IAM 统一认证的密码重置页面。 | 页面跳转。 |
| **立即注册** | 供应商模式:跳转至供应商注册页(含 SRM 存量账号绑定引导)。员工模式:不可用(灰色)。 | 页面跳转 / 按钮置灰。 |
## .详细交互流程
### 3.1 登录主流程
```mermaid
graph TD
A[供应商访问STP门户] --> B{检测登录态?}
B -->|有有效Token| C[无感跳转至工作台]
B -->|无Token| D[展示登录页面]
D --> E[输入账号/密码]
E --> F{账号来源检测}
F -->|STP本地账号| G[STP身份认证]
F -->|SRM存量账号| H[调用Vendor SSO鉴权]
G --> I{认证成功?}
H --> I
I -->|失败| J[显示错误信息<br>记录失败日志]
J --> K{失败次数≥5?}
K -->|是| L[锁定账号30分钟<br>触发安全告警]
K -->|否| E
I -->|成功| M{是否MFA启用?}
M -->|是| N[发送短信验证码]
N --> O[验证码校验]
O -->|失败| N
O -->|成功| P[生成JWT令牌]
M -->|否| P
P --> Q[更新最后登录信息]
Q --> R[记录审计日志]
R --> S[跳转至供应商工作台]
```
### 3.2 时序图:多认证源详细交互
```mermaid
sequenceDiagram
participant U as 用户/浏览器
participant FE as STP 前端
participant GW as API 网关
participant Auth as 认证服务
participant IAM as 集团 IAM
participant LocalDB as STP 用户库
participant SRM as SRM 系统
U->>FE: 输入账号/密码,点击登录
FE->>FE: 前端基础校验(非空/格式)
FE->>GW: POST /api/v1/auth/login { loginType, account, password, mode }
alt 员工模式 (mode=employee)
GW->>Auth: 转发至 IAM 认证处理器
Auth->>IAM: OAuth2/OIDC 密码模式 / LDAP 验证
IAM-->>Auth: 返回用户信息 (employeeId, dept, roles)
Auth-->>Auth: 若成功,生成 STP 内部 Session
Auth-->>FE: 200 OK { token, redirectUrl }
else 供应商模式 (mode=vendor)
GW->>Auth: 转发至本地认证处理器
Auth->>LocalDB: 查询 account
alt 本地用户存在
LocalDB-->>Auth: 返回用户实体(含 salt, hash
Auth->>Auth: 校验密码(BCrypt/Argon2
else 本地用户不存在
Auth->>SRM: 调用 /api/v1/auth/login/for-stp { username, password }
SRM-->>Auth: 200 { srmId, name, taxId, ... }
Auth->>LocalDB: 创建影子用户(绑定 srmId)
LocalDB-->>Auth: 返回新用户实体
end
Auth-->>FE: 200 OK { token, vendorId, srmId, redirectUrl }
end
FE->>FE: 存储 Token 至 LocalStorage/Session
FE-->>U: 302 重定向至工作台
```
### 3.3 手机验证码登录详细流程
```mermaid
sequenceDiagram
participant U as 用户
participant FE as 前端
participant Auth as 认证服务
participant SMS as 短信网关
participant Cache as Redis 缓存
U->>FE: 输入手机号,点击“发送验证码”
FE->>FE: 校验手机号格式(11位/国际格式)
FE->>Auth: POST /api/v1/auth/sms/send { phone, captchaToken }
Auth->>Auth: 校验图形验证码(防机器人)
Auth->>Cache: 查询该手机号发送频率(60s内是否已发)
alt 频率过高
Cache-->>Auth: 已发送,拒绝
Auth-->>FE: 429 Too Many Requests { remainSeconds: 45 }
else 频率正常
Auth->>Auth: 生成6位随机验证码(如 882345)
Auth->>Cache: 存储验证码至 RedisKey: sms:login:{phone}, TTL: 5min
Auth->>SMS: 调用短信发送接口
SMS-->>Auth: 发送成功
Auth-->>FE: 200 OK { expireSeconds: 300 }
FE-->>U: 开始倒计时(60s
end
U->>FE: 输入验证码,点击“登录”
FE->>Auth: POST /api/v1/auth/login/mobile { phone, code }
Auth->>Cache: 读取验证码并比对
alt 验证码匹配
Cache-->>Auth: 匹配成功
Auth->>Auth: 查询或创建用户(若不存在则自动注册/绑定SRM)
Auth-->>FE: 200 { token, isNewUser }
else 验证码错误或过期
Auth-->>FE: 401 { code: "ERR_SMS_001", message: "验证码错误或已过期" }
end
```
## 4. 接口详细定义
### 4.1 账号密码登录接口
|项目|内容|
|---|---|
|**路径**|`/api/v1/auth/login`|
|**方法**|`POST`|
|**是否认证**|否(公开接口)|
**请求体**
```json
{
"account": "zhangli@binhai.com", // 账号/邮箱/手机号
"password": "abcsdfg",
"mode": "vendor", // "employee" 或 "vendor"
"back_url":"/index" //返回的链接,可省略
"rememberMe": true // 是否记住我
}
```
**成功响应(200**
```json
{
"code": "0",
"data": {
"accessToken": "eyJhbGciOiJSUzI1NiIs...",
"refreshToken": "dGhpcyBpcyBhIHJlZnJlc2ggdG9rZW4...",
"expiresIn": 1800,
"userInfo": {
"userId": "U_20240001",
"userType": "VENDOR", // EMPLOYEE / VENDOR
"name": "张立",
"srmId": "SRM_12345", // 若存在关联
"roles": ["SUPPLIER"],
"redirectUrl": "/dashboard/vendor"
}
}
}
```
**失败响应(401**
```json
{
"code": "ERR_AUTH_001",
"message": "账号或密码错误",
"remainingAttempts": 4
}
```
### 4.2 手机验证码发送接口
|项目|内容|
|---|---|
|**路径**|`/api/v1/auth/sms/send`|
|**方法**|`POST`|
**请求体**
```json
{
"phone": "13800138000",
"captchaToken": "a1b2c3d4" // 图形验证码 token
"mode": "vendor", // "employee" 或 "vendor"
"back_url":"/index" //返回的链接,可省略
"rememberMe": true // 是否记住我
}
```
**成功响应**
```json
{
"code": "0",
"data": {
"expireSeconds": 300,
"resendAfterSeconds": 60
}
}
```
## 5. 安全设计细项
| 维度 | 具体策略 |
| -------------- | ----------------------------------------------------------------------------------------------------- |
| **传输加密** | 强制 HTTPS/TLS 1.3,禁用弱加密套件。 |
| **密码存储** | 使用 **Argon2id** 或 **BCryptcost≥10** 加盐哈希存储。 |
| **防暴力破解** | 单账号 5 次失败后锁定 30 分钟(IP 维度 20 次失败后全局验证码强制)。 |
| **JWT 安全** | 算法使用 RS256(私钥签名,公钥验签);Access Token 有效期 30 分钟;Refresh Token 有效期 7 天,存储于 HTTPOnly Secure Cookie(防 XSS)。 |
| **验证码安全** | 手机验证码 6 位数字,有效期 5 分钟;发送间隔限制 60 秒;单日单号码上限 10 条。 |
| **登录审计** | 记录:用户 ID、登录时间、IP、User-Agent、设备指纹(Canvas Fingerprint)、认证源(IAM/Local/SRM)。审计日志写入哈希链(见《需求说明书》4.8.1)。 |
| **多因素认证(MFA** | 预留扩展接口。敏感操作(修改密码、更换手机)强制二次验证。 |
| **会话并发** | 默认允许同一账号多设备登录,==但可在管理后台启用“单点登录互踢”策略(保留配置项)==。 |
## 6. 异常场景与错误码矩阵
| 异常场景 | 错误码 | HTTP 状态码 | 用户提示 | 后端处理 |
| --------------- | --------------- | -------- | ----------------- | ---------- |
| 账号为空 | `ERR_PARAM_001` | 400 | 请输入账号 | 无 |
| 密码为空 | `ERR_PARAM_002` | 400 | 请输入密码 | 无 |
| 账号/密码不匹配(本地) | `ERR_AUTH_001` | 401 | 账号或密码错误 | 失败计数 + 1 |
| 账号不存在且 SRM 验证失败 | `ERR_AUTH_002` | 401 | 账号不存在,请检查或注册 | 记录未注册尝试 |
| 账号已锁定 | `ERR_AUTH_003` | 403 | 账号已被锁定,请 30 分钟后重试 | 告警(若为频繁攻击) |
| 手机号格式错误 | `ERR_PARAM_003` | 400 | 请输入正确的手机号 | 无 |
| 验证码发送频率过高 | `ERR_SMS_001` | 429 | 发送过于频繁,请 60 秒后重试 | 返回剩余秒数 |
| 验证码错误/过期 | `ERR_SMS_002` | 401 | 验证码错误或已过期 | 清除已用验证码 |
| IAM 认证超时 | `ERR_IAM_001` | 504 | 集团认证服务繁忙,请稍后重试 | 熔断降级,记录日志 |
## 7. 登录成功后的路由与权限门控
登录成功后,前端根据 `userType` 和 `roles` 决定跳转目标,同时后端在后续请求中通过 **JWT** 持续校验权限。
| 用户类型 | 默认跳转页面 | 可访问范围 |
| ------------------ | --------------------- | ------------------------------------- |
| **供应商(VENDOR** | `/portal/dashboard` | 招标公告列表、报名管理、我的投标、中标通知、保证金管理。 |
| **评审专家(EXPERT** | `/expert/dashboard` | 评标任务列表、回避申报、承诺书签署、历史评审记录。 |
| **内部员工(EMPLOYEE** | `/employee/dashboard` | 根据子角色(需求人员/招标专员/审批人/管理员)展示对应工作台。 |
| **系统管理员(ADMIN** | `/admin/console` | 用户管理、流程配置、审计日志、系统监控。<br>(管理员不在普通页面登录) |
```mermaid
graph LR
A[登录成功] --> B{UserType}
B -->|VENDOR| C[供应商工作台]
B -->|EXPERT| D[专家工作台]
B -->|EMPLOYEE| E{子角色}
E -->|需求人员| F[需求工作台]
E -->|招标专员| G[招标管理工作台]
E -->|审批人| H[审批待办中心]
E -->|管理员| I[系统管理后台]
style A fill:#e8f5e9,stroke:#388e3c
style C fill:#e3f2fd,stroke:#1565c0
style D fill:#fff3e0,stroke:#e65100
style I fill:#fce4ec,stroke:#c2185b
```
## 8. 与 SRM 无感登录的集成衔接
登录页下方的 **“原 SRM 注册用户,可直接使用 SRM账号密码登录”** 提示对应后端自动回退逻辑(详见 3.2 时序图)。此外,登录成功后,前端页面内嵌的指向 SRM 的所有链接,将自动携带 **跳转票据(Ticket** 实现无感跳转(技术方案详见《SRM 与 STP 数据交换接口方案说明书》3.4.2 节)。
@@ -760,4 +760,5 @@ Agent 在调用任何业务数据前,必须通过统一数据网关验证请
* **标书费配置**
1. 收费节点定于标书发放前,付费后解锁招标文件查看权限;
2. 招标方案制定时可选择是否收取标书费;
* **是否需要英文版本**