1075 lines
56 KiB
Markdown
1075 lines
56 KiB
Markdown
# 智慧招标平台(STP)供应商注册模块详细设计说明书
|
||
|
||
| 文档名称 | 供应商注册模块详细设计说明书 |
|
||
| :------- | :--------------------------------- |
|
||
| **文档版本** | V1.0 |
|
||
| **编制日期** | 2026-06-30 |
|
||
| **关联文档** | 《需求分析说明书 V1.4》《供应商报名详细设计》《SRM接口方案》 |
|
||
|
||
|
||
## 目录
|
||
1. [模块概述](#1-模块概述)
|
||
2. [用户角色与权限](#2-用户角色与权限)
|
||
3. [页面UI详细设计](#3-页面ui详细设计)
|
||
4. [字段级校验规则](#4-字段级校验规则)
|
||
5. [AI自动审核详细设计](#5-ai自动审核详细设计)
|
||
6. [完整业务流程(含状态机)](#6-完整业务流程含状态机)
|
||
7. [时序图(含外部系统交互)](#7-时序图含外部系统交互)
|
||
8. [数据模型完整定义](#8-数据模型完整定义)
|
||
9. [API接口详细规范](#9-api接口详细规范)
|
||
10. [业务规则与约束](#10-业务规则与约束)
|
||
11. [异常处理矩阵](#11-异常处理矩阵)
|
||
12. [通知策略](#12-通知策略)
|
||
13. [SRM双向同步机制](#13-srm双向同步机制)
|
||
14. [供应商联系人变更(注册后维护)](#14-供应商联系人变更注册后维护)
|
||
15. [安全与合规](#15-安全与合规)
|
||
16. [附录](#16-附录)
|
||
|
||
---
|
||
|
||
## 1. 模块概述
|
||
|
||
### 1.1 建设目标
|
||
建立面向潜在投标供应商的**轻量化、全自动、即时生效**的注册机制。注册阶段仅做**主体身份认证**(营业执照有效性核验),不进行人工资格审核。通过即开通平台登录权限和投标报名资格,**深度资质审核后移至投标报名环节按项目要求执行**。
|
||
|
||
### 1.2 核心设计理念:注册与审核分离
|
||
|
||
| 阶段 | 审核内容 | 审核方式 | 通过标准 |
|
||
| :--- | :--- | :--- | :--- |
|
||
| **注册阶段** | 营业执照有效性、企业存续状态、基础信息一致性、严重违法筛查 | **全自动AI**(无人工) | 所有一票否决项通过 |
|
||
| **投标报名阶段** | 项目特定资质、业绩、财务能力、技术能力 | AI初审 + 人工复核 | 满足具体项目资格要求 |
|
||
|
||
### 1.3 设计原则
|
||
- **极简快速**:提交即审核,5秒内出结果,无等待队列。
|
||
- **AI全自动**:OCR + 工商API + 风险图谱全链路自动化。
|
||
- **一次注册,双平台通用**:自动同步至SRM,建立统一供应商身份(`srm_id`双向绑定)。
|
||
- **审核后置**:深度资质审核下沉至报名环节,降低准入门槛。
|
||
- **安全合规**:敏感字段(信用代码、手机号)AES-256-GCM加密存储,全量审计。
|
||
|
||
### 1.4 适用范围
|
||
- **适用对象**:所有拟参与滨化集团招标项目的潜在供应商(含工程、货物、服务类)。
|
||
- **与SRM关系**:新供应商自动在SRM创建档案(状态:待认证);已存续SRM供应商通过“无感登录”直接绑定,无需重复注册。
|
||
|
||
---
|
||
|
||
## 2. 用户角色与权限
|
||
|
||
| 角色 | 权限范围 |
|
||
| :--- | :--- |
|
||
| **供应商申请人(未注册)** | 访问注册页面、提交申请、查看审核结果。 |
|
||
| **已注册供应商(ACTIVE)** | 登录平台、浏览公告、投标报名、维护企业信息(含联系人变更)。 |
|
||
| **供应商管理专员** | 查看注册统计、处理注册异常申诉、执行黑名单管理(冻结/解封)。 |
|
||
| **系统管理员** | 配置AI审核阈值、管理工商API密钥、查看系统日志。 |
|
||
| **合规/审计** | 查阅注册审计日志(只读)。 |
|
||
|
||
---
|
||
|
||
## 3. 页面UI详细设计
|
||
|
||
### 3.1 注册步骤导航条
|
||
|
||
```svg
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 100" font-family="'Microsoft YaHei', sans-serif">
|
||
<rect width="1000" height="100" fill="#f8f9fc"/>
|
||
<rect x="50" y="30" width="900" height="40" rx="20" fill="#fff" stroke="#e0e4ec"/>
|
||
|
||
<circle cx="160" cy="50" r="18" fill="#c2185b"/>
|
||
<text x="160" y="56" text-anchor="middle" font-size="14" fill="#fff" font-weight="bold">1</text>
|
||
<text x="160" y="85" text-anchor="middle" font-size="12" fill="#c2185b" font-weight="bold">企业信息</text>
|
||
<line x1="178" y1="50" x2="252" y2="50" stroke="#c2185b" stroke-width="3"/>
|
||
|
||
<circle cx="270" cy="50" r="18" fill="#e0e4ec"/>
|
||
<text x="270" y="56" text-anchor="middle" font-size="14" fill="#8895aa">2</text>
|
||
<text x="270" y="85" text-anchor="middle" font-size="12" fill="#8895aa">联系人</text>
|
||
<line x1="288" y1="50" x2="362" y2="50" stroke="#e0e4ec" stroke-width="3"/>
|
||
|
||
<circle cx="380" cy="50" r="18" fill="#e0e4ec"/>
|
||
<text x="380" y="56" text-anchor="middle" font-size="14" fill="#8895aa">3</text>
|
||
<text x="380" y="85" text-anchor="middle" font-size="12" fill="#8895aa">提交/自动审核</text>
|
||
|
||
<rect x="730" y="32" width="190" height="28" rx="14" fill="#e8f5e9" stroke="#4caf50"/>
|
||
<text x="825" y="51" text-anchor="middle" font-size="12" fill="#2e7d32">⚡ AI即时审核 · 秒级通过</text>
|
||
</svg>
|
||
```
|
||
|
||
### 3.2 步骤一:企业信息
|
||
|
||
```svg
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1100 920" 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="920" fill="#f5f7fa"/>
|
||
|
||
<!-- 主卡片 -->
|
||
<rect x="80" y="30" width="940" height="860" 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"/>
|
||
|
||
<!-- 营业执照上传区(突出显示) -->
|
||
<rect x="120" y="145" width="820" height="80" rx="8" stroke-dasharray="8,4" fill="#fafbfc" stroke="#c2185b" stroke-width="2"/>
|
||
<text x="530" y="180" text-anchor="middle" font-size="16" fill="#c2185b">📄 上传营业执照(JPG/PNG/PDF,≤10MB)</text>
|
||
<text x="530" y="208" text-anchor="middle" font-size="13" fill="#8895aa">支持拖拽或点击上传,AI 将自动识别企业信息并填充表单</text>
|
||
|
||
<!-- 识别状态条 -->
|
||
<rect x="120" y="240" width="820" height="30" rx="4" fill="#e8f5e9" stroke="#a5d6a7"/>
|
||
<text x="140" y="260" font-size="13" fill="#2e7d32">✅ 已识别:营业执照信息提取成功(统一信用代码:91370100XXXXXXXXXX)</text>
|
||
|
||
<!-- ===== 表单字段 ===== -->
|
||
<!-- 第一行:企业全称 + 统一社会信用代码 -->
|
||
<text x="120" y="300" font-size="14" fill="#4a5568">企业全称 <tspan fill="#e91e63">*</tspan></text>
|
||
<rect x="120" y="310" width="380" height="40" rx="6" fill="#fafbfc" stroke="#e0e4ec"/>
|
||
<text x="135" y="336" font-size="14" fill="#b0bbcc">系统自动识别或手动输入</text>
|
||
|
||
<text x="540" y="300" font-size="14" fill="#4a5568">统一社会信用代码 <tspan fill="#e91e63">*</tspan></text>
|
||
<rect x="540" y="310" width="380" height="40" rx="6" fill="#fafbfc" stroke="#e0e4ec"/>
|
||
<text x="555" y="336" font-size="14" fill="#b0bbcc">18位代码</text>
|
||
|
||
<!-- 第二行:注册资本 + 法定代表人 -->
|
||
<text x="120" y="380" font-size="14" fill="#4a5568">注册资本(万元) <tspan fill="#e91e63">*</tspan></text>
|
||
<rect x="120" y="390" width="180" height="40" rx="6" fill="#fafbfc" stroke="#e0e4ec"/>
|
||
<text x="135" y="416" font-size="14" fill="#b0bbcc">数字</text>
|
||
|
||
<text x="340" y="380" font-size="14" fill="#4a5568">法定代表人 <tspan fill="#e91e63">*</tspan></text>
|
||
<rect x="340" y="390" width="180" height="40" rx="6" fill="#fafbfc" stroke="#e0e4ec"/>
|
||
<text x="355" y="416" font-size="14" fill="#b0bbcc">姓名</text>
|
||
|
||
<text x="560" y="380" font-size="14" fill="#4a5568">行业分类 <tspan fill="#e91e63">*</tspan></text>
|
||
<rect x="560" y="390" width="260" height="40" rx="6" fill="#fafbfc" stroke="#e0e4ec"/>
|
||
<text x="575" y="416" font-size="14" fill="#b0bbcc">请选择/输入行业</text>
|
||
|
||
<!-- 第三行:地址 -->
|
||
<text x="120" y="460" font-size="14" fill="#4a5568">注册地址 <tspan fill="#e91e63">*</tspan></text>
|
||
<rect x="120" y="470" width="560" height="40" rx="6" fill="#fafbfc" stroke="#e0e4ec"/>
|
||
<text x="135" y="496" font-size="14" fill="#b0bbcc">请填写详细地址</text>
|
||
|
||
<!-- 第四行:经营范围 -->
|
||
<text x="120" y="540" font-size="14" fill="#4a5568">经营范围 <tspan fill="#e91e63">*</tspan></text>
|
||
<rect x="120" y="550" width="560" height="60" rx="6" fill="#fafbfc" stroke="#e0e4ec"/>
|
||
<text x="135" y="575" font-size="14" fill="#b0bbcc">系统识别或手动输入,建议不超过200字</text>
|
||
|
||
<!-- 第五行:主要产品(可添加多个) -->
|
||
<text x="120" y="640" font-size="14" fill="#4a5568">主要产品 <tspan fill="#e91e63">*</tspan></text>
|
||
<rect x="120" y="650" width="300" height="40" rx="6" fill="#fafbfc" stroke="#e0e4ec"/>
|
||
<text x="135" y="676" font-size="14" fill="#b0bbcc">如:工业阀门、管道</text>
|
||
<rect x="440" y="650" width="100" height="40" rx="6" fill="#e3f2fd" stroke="#1565c0"/>
|
||
<text x="490" y="676" text-anchor="middle" font-size="13" fill="#1565c0">+ 添加</text>
|
||
<!-- 已添加的标签 -->
|
||
<rect x="560" y="650" width="120" height="40" rx="20" fill="#fce4ec" stroke="#c2185b"/>
|
||
<text x="620" y="676" text-anchor="middle" font-size="13" fill="#c2185b">工业阀门 ✕</text>
|
||
<rect x="690" y="650" width="120" height="40" rx="20" fill="#fce4ec" stroke="#c2185b"/>
|
||
<text x="750" y="676" text-anchor="middle" font-size="13" fill="#c2185b">管道配件 ✕</text>
|
||
|
||
<!-- ===== 底部按钮 ===== -->
|
||
<line x1="120" y1="730" x2="980" y2="730" stroke="#f0f2f7" stroke-width="2"/>
|
||
|
||
<rect x="740" y="750" width="120" height="40" rx="6" fill="#e0e4ec"/>
|
||
<text x="800" y="776" text-anchor="middle" font-size="14" fill="#8895aa">上一步</text>
|
||
|
||
<rect x="880" y="750" width="140" height="40" rx="6" fill="url(#brandGrad)"/>
|
||
<text x="950" y="776" 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. 已有SRM账号请直接登录</text>
|
||
</svg>
|
||
```
|
||
|
||
**布局说明**:采用左右结构,左侧为表单(占比70%),右侧为“注册须知”浮窗(占比30%)。
|
||
|
||
**表单字段明细**(按顺序):
|
||
|
||
| 字段 | 组件类型 | 占位提示 | 备注 |
|
||
| :----------- | :------- | :------------------------------- | :-------------------- |
|
||
| **营业执照上传** | 拖拽/点击上传区 | “点击或拖拽上传营业执照(JPG/PNG/PDF,≤10MB)” | **AI识别触发点**,上传即自动OCR |
|
||
| **企业全称** | 文本输入框 | “**系统自动识别**或手动输入” | 最大长度128字符 |
|
||
| **统一社会信用代码** | 文本输入框 | “18位统一社会信用代码”自动识别 | 自动转大写,格式校验 |
|
||
| **注册资本(万元)** | 数字输入框 | “请输入数字” | 保留2位小数,≥0 |
|
||
| **法定代表人** | 文本输入框 | “请输入法定代表人姓名” | 最大长度64字符 |
|
||
| **行业分类** | 下拉框+搜索 | “请选择或搜索行业” | 基于GB/T 4754-2017,三级分类 |
|
||
| **注册地址** | 文本输入框 | “请填写详细地址” | 最大长度256字符 |
|
||
| **经营范围** | 多行文本域 | “系统自动识别或手动输入,不超过500字” | 最大500字符 |
|
||
| **主要产品** | 标签输入组件 | “输入产品名称,回车添加” | 最多添加20个,每个≤30字符 |
|
||
|
||
**OCR识别状态条**(实时反馈):
|
||
- 识别中:`⏳ 正在识别营业执照...`
|
||
- 成功:`✅ 识别完成,已自动回填以下字段:[企业全称]、[信用代码]、[法人]、[地址]、[经营范围]`
|
||
- 失败:`❌ 识别失败,请手动填写(或检查图片是否清晰)`
|
||
|
||
### 3.3 步骤二:联系人信息
|
||
|
||
```svg
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 1000" width="100%" height="100%">
|
||
<defs>
|
||
<style>
|
||
.label { font-family: system-ui, -apple-system, sans-serif; font-size: 14px; font-weight: 600; fill: #374151; }
|
||
.input-bg { fill: #FFFFFF; stroke: #D1D5DB; stroke-width: 1.5px; rx: 6px; transition: all 0.2s ease; }
|
||
.input-bg:focus-within { stroke: #6366F1; stroke-width: 2px; }
|
||
.input-text { font-family: system-ui, -apple-system, sans-serif; font-size: 14px; fill: #111827; }
|
||
.placeholder { font-family: system-ui, -apple-system, sans-serif; font-size: 14px; fill: #9CA3AF; }
|
||
.helper-text { font-family: system-ui, -apple-system, sans-serif; font-size: 12px; fill: #6B7280; }
|
||
.required { font-family: system-ui, -apple-system, sans-serif; font-size: 16px; fill: #EF4444; }
|
||
.btn-primary { fill: url(#brandGrad); rx: 6px; cursor: pointer; transition: all 0.2s ease; }
|
||
.btn-primary:hover { fill: #4338CA; }
|
||
.btn-secondary { fill: url(#brandGrad); rx: 6px; cursor: pointer; transition: all 0.2s ease; }
|
||
.btn-secondary:hover { fill: url(#brandGrad); }
|
||
.btn-text { font-family: system-ui, -apple-system, sans-serif; font-size: 14px; font-weight: 600; fill: #FFFFFF; text-anchor: middle; dominant-baseline: central; pointer-events: none; }
|
||
.rule-pass { font-family: system-ui, -apple-system, sans-serif; font-size: 14px; fill: #10B981; }
|
||
.rule-fail { font-family: system-ui, -apple-system, sans-serif; font-size: 14px; fill: #EF4444; }
|
||
.rule-text { font-family: system-ui, -apple-system, sans-serif; font-size: 13px; fill: #374151; }
|
||
.strength-meter-bg { fill: #E5E7EB; rx: 4px; }
|
||
.strength-meter-fill { fill: #F59E0B; rx: 4px; }
|
||
.strength-label { font-family: system-ui, -apple-system, sans-serif; font-size: 12px; fill: #9CA3AF; }
|
||
.strength-label-active { font-family: system-ui, -apple-system, sans-serif; font-size: 12px; font-weight: bold; fill: #F59E0B; }
|
||
.dot-inactive { fill: #FFFFFF; stroke: #D1D5DB; stroke-width: 1.5px; }
|
||
.dot-active { fill: #F59E0B; stroke: #F59E0B; stroke-width: 1.5px; }
|
||
.eye-icon { stroke: #9CA3AF; stroke-width: 2px; fill: none; stroke-linecap: round; cursor: pointer; }
|
||
.eye-icon:hover { stroke: #4B5563; }
|
||
</style>
|
||
|
||
<!-- 卡片投影 -->
|
||
<filter id="cardShadow" x="-5%" y="-5%" width="110%" height="110%">
|
||
<feDropShadow dx="0" dy="8" stdDeviation="24" flood-color="#000000" flood-opacity="0.06"/>
|
||
<feDropShadow dx="0" dy="2" stdDeviation="4" flood-color="#000000" flood-opacity="0.03"/>
|
||
</filter>
|
||
|
||
<!-- 输入框聚焦阴影 -->
|
||
<filter id="inputFocus" x="-5%" y="-5%" width="110%" height="110%">
|
||
<feDropShadow dx="0" dy="0" stdDeviation="6" flood-color="#6366F1" flood-opacity="0.15"/>
|
||
</filter>
|
||
</defs>
|
||
|
||
<!-- 背景 -->
|
||
<rect width="1200" height="1000" fill="#F3F4F6"/>
|
||
|
||
<!-- 卡片容器 -->
|
||
<rect x="100" y="40" width="1000" height="920" rx="16" fill="#FFFFFF" filter="url(#cardShadow)"/>
|
||
|
||
<!-- 页面标题 -->
|
||
<text x="140" y="90" font-family="system-ui, -apple-system, sans-serif" font-size="24" font-weight="700" fill="#1F2937" letter-spacing="0.5">联系人信息</text>
|
||
|
||
<!-- ==================== 表单项 1:联系人姓名 ==================== -->
|
||
<text x="280" y="167" class="label" text-anchor="end">联系人姓名</text>
|
||
<text x="290" y="167" class="required">*</text>
|
||
<rect x="300" y="145" width="720" height="44" class="input-bg"/>
|
||
<text x="315" y="172" class="placeholder">请输入联系人姓名</text>
|
||
<text x="315" y="208" class="helper-text">必填,最大64字符</text>
|
||
|
||
<!-- ==================== 表单项 2:联系电话 ==================== -->
|
||
<text x="280" y="257" class="label" text-anchor="end">联系电话</text>
|
||
<text x="290" y="257" class="required">*</text>
|
||
<!-- 聚焦状态演示 -->
|
||
<rect x="300" y="235" width="720" height="44" class="input-bg" stroke="#6366F1" stroke-width="2" filter="url(#inputFocus)"/>
|
||
<text x="315" y="262" class="input-text">13800138000</text>
|
||
<text x="315" y="298" class="helper-text">必填,格式校验,全局唯一</text>
|
||
|
||
<!-- ==================== 表单项 3:电子邮箱 ==================== -->
|
||
<text x="280" y="347" class="label" text-anchor="end">电子邮箱</text>
|
||
<text x="290" y="347" class="required">*</text>
|
||
<rect x="300" y="325" width="720" height="44" class="input-bg"/>
|
||
<text x="315" y="352" class="placeholder">请输入常用邮箱</text>
|
||
<text x="315" y="388" class="helper-text">必填,格式校验,全局唯一</text>
|
||
|
||
<!-- ==================== 表单项 4:短信验证码 ==================== -->
|
||
<text x="280" y="437" class="label" text-anchor="end">短信验证码</text>
|
||
<text x="290" y="437" class="required">*</text>
|
||
<rect x="300" y="415" width="480" height="44" class="input-bg"/>
|
||
<text x="315" y="442" class="placeholder">点击发送验证码</text>
|
||
<rect x="800" y="415" width="120" height="44" class="btn-secondary"/>
|
||
<text x="860" y="437" class="btn-text">发送验证码</text>
|
||
<text x="315" y="478" class="helper-text">用于验证手机号真实性(注册必验)</text>
|
||
|
||
<!-- ==================== 表单项 5:输入密码 ==================== -->
|
||
<text x="280" y="527" class="label" text-anchor="end">输入密码</text>
|
||
<rect x="300" y="505" width="720" height="44" class="input-bg"/>
|
||
<!-- 密码占位圆点 -->
|
||
<circle cx="315" cy="527" r="4" fill="#9CA3AF"/>
|
||
<circle cx="335" cy="527" r="4" fill="#9CA3AF"/>
|
||
<circle cx="355" cy="527" r="4" fill="#9CA3AF"/>
|
||
<circle cx="375" cy="527" r="4" fill="#9CA3AF"/>
|
||
<circle cx="395" cy="527" r="4" fill="#9CA3AF"/>
|
||
<circle cx="415" cy="527" r="4" fill="#9CA3AF"/>
|
||
<circle cx="435" cy="527" r="4" fill="#9CA3AF"/>
|
||
<circle cx="455" cy="527" r="4" fill="#9CA3AF"/>
|
||
<!-- 显示/隐藏按钮(眼睛图标) -->
|
||
<path d="M 970 517 C 970 517, 980 527, 970 537 M 980 517 C 980 517, 990 527, 980 537" class="eye-icon"/>
|
||
|
||
<!-- ==================== 表单项 6:确认密码 ==================== -->
|
||
<text x="280" y="617" class="label" text-anchor="end">确认密码</text>
|
||
<rect x="300" y="595" width="720" height="44" class="input-bg"/>
|
||
<!-- 密码占位圆点 -->
|
||
<circle cx="315" cy="617" r="4" fill="#9CA3AF"/>
|
||
<circle cx="335" cy="617" r="4" fill="#9CA3AF"/>
|
||
<circle cx="355" cy="617" r="4" fill="#9CA3AF"/>
|
||
<circle cx="375" cy="617" r="4" fill="#9CA3AF"/>
|
||
<circle cx="395" cy="617" r="4" fill="#9CA3AF"/>
|
||
<circle cx="415" cy="617" r="4" fill="#9CA3AF"/>
|
||
<circle cx="435" cy="617" r="4" fill="#9CA3AF"/>
|
||
<circle cx="455" cy="617" r="4" fill="#9CA3AF"/>
|
||
<!-- 显示/隐藏按钮(眼睛图标) -->
|
||
<path d="M 970 607 C 970 607, 980 617, 970 627 M 980 607 C 980 607, 990 617, 980 627" class="eye-icon"/>
|
||
|
||
<!-- ==================== 密码复杂度要求 ==================== -->
|
||
<rect x="300" y="660" width="720" height="150" rx="8" fill="#F8FAFC" stroke="#E2E8F0" stroke-width="1"/>
|
||
<text x="315" y="685" font-family="system-ui, -apple-system, sans-serif" font-size="14" font-weight="600" fill="#374151">密码复杂度要求</text>
|
||
|
||
<!-- 规则 1:满足 -->
|
||
<text x="315" y="715" class="rule-pass">✔</text>
|
||
<text x="335" y="715" class="rule-text">长度 8~20 位</text>
|
||
|
||
<!-- 规则 2:满足 -->
|
||
<text x="315" y="740" class="rule-pass">✔</text>
|
||
<text x="335" y="740" class="rule-text">至少包含 1 个大写字母(A-Z)和 1 个小写字母(a-z)</text>
|
||
|
||
<!-- 规则 3:满足 -->
|
||
<text x="315" y="765" class="rule-pass">✔</text>
|
||
<text x="335" y="765" class="rule-text">至少包含 1 个数字(0-9)和 1 个特殊字符(!@#$%^&*()_+-=)</text>
|
||
|
||
<!-- 规则 4:满足 -->
|
||
<text x="315" y="790" class="rule-pass">✔</text>
|
||
<text x="335" y="790" class="rule-text">禁止连续/重复字符(如 12345678, aaaaaaaa)</text>
|
||
|
||
<!-- 规则 5:不满足 -->
|
||
<text x="315" y="815" class="rule-fail">✘</text>
|
||
<text x="335" y="815" class="rule-text">禁止常见弱口令(内置弱密码字典)</text>
|
||
|
||
<!-- ==================== 密码强度实时指示器 ==================== -->
|
||
<text x="280" y="855" class="label" text-anchor="end">密码强度</text>
|
||
|
||
<!-- 进度条背景 -->
|
||
<rect x="300" y="845" width="300" height="8" class="strength-meter-bg"/>
|
||
<!-- 进度条填充(演示中等级别,满足 4/5 项) -->
|
||
<rect x="300" y="845" width="180" height="8" class="strength-meter-fill"/>
|
||
|
||
<!-- 弱标签 -->
|
||
<circle cx="320" cy="871" r="6" class="dot-inactive"/>
|
||
<text x="335" y="875" class="strength-label">弱</text>
|
||
|
||
<!-- 中标签(当前激活) -->
|
||
<circle cx="390" cy="871" r="6" class="dot-active"/>
|
||
<text x="405" y="875" class="strength-label-active">中</text>
|
||
|
||
<!-- 强标签 -->
|
||
<circle cx="460" cy="871" r="6" class="dot-inactive"/>
|
||
<text x="475" y="875" class="strength-label">强</text>
|
||
|
||
<!-- ==================== 提交按钮 ==================== -->
|
||
<rect x="300" y="910" width="160" height="44" class="btn-primary"/>
|
||
<text x="380" y="932" class="btn-text">提交</text>
|
||
|
||
</svg>
|
||
```
|
||
|
||
| 字段 | 组件类型 | 占位提示 | 备注 |
|
||
| :------------ | :------- | :---------- | :--------------- |
|
||
| **联系人姓名** | 文本输入框 | “请输入联系人姓名” | 必填,最大64字符 |
|
||
| **联系电话(手机号)** | 文本输入框 | “请输入11位手机号” | 必填,格式校验,**全局唯一** |
|
||
| **电子邮箱** | 文本输入框 | “请输入常用邮箱” | 必填,格式校验,**全局唯一** |
|
||
| **短信验证码** | 输入框+发送按钮 | “点击发送验证码” | 用于验证手机号真实性(注册必验) |
|
||
| 输入密码 | 密码输入框 | | |
|
||
| 确认密码 | 密码输入框 | | |
|
||
|
||
**密码复杂度要求**:
|
||
- 长度 8~20 位
|
||
- 至少包含 1 个大写字母(A-Z)
|
||
- 至少包含 1 个小写字母(a-z)
|
||
- 至少包含 1 个数字(0-9)
|
||
- 至少包含 1 个特殊字符(!@#$%^&*()_+-=)
|
||
- 禁止使用连续/重复字符(如 `12345678`、`aaaaaaaa`)
|
||
- 禁止使用常见弱口令(内置弱密码字典,如 `password`、`admin123`)
|
||
|
||
|
||
**密码强度实时指示器**:
|
||
|
||
- 弱(红色):< 3 项满足
|
||
- 中(橙色):≥ 4 项满足
|
||
- 强(绿色):全部满足 + 长度≥12
|
||
|
||
### 3.4 步骤三:SRM存量引导弹窗
|
||
|
||
**当检测到SRM存量时,弹出如下引导窗(模态框):**
|
||
|
||
```svg
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 600 380" font-family="'Microsoft YaHei', sans-serif">
|
||
<defs><filter id="shadow"/><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="600" height="380" fill="rgba(0,0,0,0.4)"/>
|
||
<rect x="40" y="40" width="520" height="300" rx="16" fill="#fff" filter="url(#shadow)"/>
|
||
|
||
<text x="300" y="85" text-anchor="middle" font-size="22" font-weight="bold" fill="#1a2332">🔍 检测到SRM存量供应商</text>
|
||
<text x="300" y="120" text-anchor="middle" font-size="15" fill="#4a5568">该企业已在SRM系统中注册,您可以选择:</text>
|
||
|
||
<rect x="80" y="150" width="200" height="44" rx="8" fill="#e3f2fd" stroke="#1565c0"/>
|
||
<text x="180" y="177" text-anchor="middle" font-size="15" fill="#1565c0" font-weight="bold">使用SRM账号登录</text>
|
||
|
||
<rect x="320" y="150" width="200" height="44" rx="8" fill="url(#brandGrad)"/>
|
||
<text x="420" y="177" text-anchor="middle" font-size="15" fill="#fff" font-weight="bold">继续注册</text>
|
||
|
||
<text x="300" y="225" text-anchor="middle" font-size="13" fill="#8895aa">选择“继续注册”将跳过资质审核,直接绑定SRM账号</text>
|
||
<text x="300" y="250" text-anchor="middle" font-size="13" fill="#8895aa">选择“登录”将跳转至登录页,使用SRM账号密码登录</text>
|
||
|
||
<rect x="220" y="280" width="160" height="30" rx="6" fill="#f5f5f5" stroke="#ccc"/>
|
||
<text x="300" y="300" text-anchor="middle" font-size="13" fill="#666">取消</text>
|
||
</svg>
|
||
```
|
||
|
||
**选择“继续注册”后的处理**:
|
||
|
||
- 前端重新提交注册请求,携带参数 `confirmSrmBinding: true`。
|
||
- 后端跳过AI审核,直接创建STP账号并绑定SRM ID,返回成功(自动登录)。
|
||
|
||
**选择“使用SRM账号登录”**:
|
||
|
||
- 前端跳转至登录页,并自动填充企业名称/信用代码(可选),引导用户使用SRM账号密码登录(利用统一登录接口)。
|
||
|
||
### 3.5 步骤四:提交 & 自动审核
|
||
|
||
**审核中(等待 3~8 秒)** :
|
||
|
||
```svg
|
||
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 600 300" font-family="'Microsoft YaHei', sans-serif">
|
||
<rect width="600" height="300" fill="#f8f9fc"/>
|
||
<rect x="60" y="40" width="480" height="220" rx="16" fill="#fff" filter="url(#shadow)"/>
|
||
|
||
<!-- 旋转加载器 -->
|
||
<circle cx="300" cy="120" r="40" fill="none" stroke="#e0e4ec" stroke-width="6"/>
|
||
<path d="M 300 80 A 40 40 0 0 1 335 110" fill="none" stroke="#c2185b" stroke-width="6" stroke-linecap="round">
|
||
<animateTransform attributeName="transform" type="rotate" from="0 300 120" to="360 300 120" dur="1s" repeatCount="indefinite"/>
|
||
</path>
|
||
|
||
<text x="300" y="185" text-anchor="middle" font-size="18" font-weight="bold" fill="#1a2332">⏳ AI 正在审核企业信息...</text>
|
||
<text x="300" y="215" text-anchor="middle" font-size="14" fill="#8895aa">预计 3~8 秒,请勿关闭页面</text>
|
||
<text x="300" y="240" text-anchor="middle" font-size="13" fill="#b0bbcc">✓ 工商信息核验 ✓ 风险扫描 ✓ 唯一性检查</text>
|
||
</svg>
|
||
```
|
||
|
||
**审核通过(自动登录跳转)** :
|
||
|
||
```svg
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 600 420" font-family="'Microsoft YaHei', sans-serif">
|
||
<defs>
|
||
<filter id="shadow"/>
|
||
<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="600" height="420" fill="#f8f9fc"/>
|
||
<rect x="60" y="30" width="480" height="360" rx="16" fill="#fff" filter="url(#shadow)"/>
|
||
|
||
<!-- 成功图标 -->
|
||
<circle cx="300" cy="100" r="50" fill="#e8f5e9"/>
|
||
<path d="M270 100 L290 120 L330 75" stroke="#4caf50" stroke-width="6" fill="none" stroke-linecap="round"/>
|
||
|
||
<text x="300" y="185" text-anchor="middle" font-size="26" font-weight="bold" fill="#1a2332">🎉 注册成功!</text>
|
||
<text x="300" y="218" text-anchor="middle" font-size="15" fill="#4a5568">您的供应商账号已激活,正在自动登录...</text>
|
||
|
||
<line x1="120" y1="245" x2="480" y2="245" stroke="#f0f2f7" stroke-width="2"/>
|
||
<!-- 供应商信息摘要 -->
|
||
<text x="300" y="278" text-anchor="middle" font-size="14" fill="#8895aa">供应商编号:<tspan fill="#1a2332" font-weight="bold">SUP_20260630_0158</tspan></text>
|
||
<text x="300" y="308" text-anchor="middle" font-size="14" fill="#8895aa">登录账号:<tspan fill="#1a2332" font-weight="bold">138****8001</tspan></text>
|
||
|
||
<!-- 温馨提示 -->
|
||
<rect x="120" y="330" width="360" height="45" rx="6" fill="#fff3e0" stroke="#ffcc80"/>
|
||
<text x="300" y="350" text-anchor="middle" font-size="12" fill="#e65100">📌 温馨提示:参与具体项目投标时,需按招标公告要求提交资质审核材料</text>
|
||
</svg>
|
||
```
|
||
|
||
**审核不通过**:
|
||
|
||
```svg
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 600 420" font-family="'Microsoft YaHei', sans-serif">
|
||
<!-- 背景和卡片 -->
|
||
<rect width="600" height="420" fill="#f8f9fc"/>
|
||
<rect x="60" y="30" width="480" height="370" rx="16" fill="#fff" filter="url(#shadow)"/>
|
||
|
||
<!-- 失败图标 -->
|
||
<circle cx="300" cy="90" r="50" fill="#ffcdd2"/>
|
||
<text x="300" y="105" text-anchor="middle" font-size="48" fill="#c62828">✕</text>
|
||
|
||
<text x="300" y="175" text-anchor="middle" font-size="24" font-weight="bold" fill="#c62828">审核未通过</text>
|
||
|
||
<!-- 原因列表 -->
|
||
<rect x="100" y="200" width="400" height="100" rx="8" fill="#fafafa" stroke="#e0e4ec"/>
|
||
<text x="120" y="225" font-size="13" fill="#c62828">• 统一社会信用代码与工商登记信息不匹配</text>
|
||
<text x="120" y="250" font-size="13" fill="#c62828">• 企业状态为“注销”,不符合注册条件</text>
|
||
<text x="120" y="275" font-size="13" fill="#666">💡 请核对信息后重新提交,如有疑问请联系客服</text>
|
||
|
||
<!-- 按钮 -->
|
||
<rect x="160" y="330" width="280" height="42" rx="8" fill="#c2185b"/>
|
||
<text x="300" y="357" text-anchor="middle" font-size="16" font-weight="bold" fill="#fff">修改后重新提交</text>
|
||
</svg>
|
||
```
|
||
|
||
---
|
||
|
||
## 4. 字段级校验规则(前后端双重校验)
|
||
|字段|前端校验|后端校验|错误码|
|
||
|---|---|---|---|
|
||
|**营业执照文件**|格式(JPEG/PNG/PDF)、大小≤10MB|文件病毒扫描、图片清晰度检测|`ERR_FILE_001`|
|
||
|**企业全称**|非空,长度≤128|与OCR结果一致性比对|`ERR_PARAM_001`|
|
||
|**统一社会信用代码**|非空,格式正则`^[0-9A-HJ-NPQRTUWXY]{2}\d{6}[0-9A-HJ-NPQRTUWXY]{10}$`,自动转大写|唯一性校验;与工商API比对|`ERR_PARAM_002` / `ERR_REG_002`|
|
||
|**注册资本**|数字,≥0,最多2位小数|数据类型校验|`ERR_PARAM_003`|
|
||
|**法定代表人**|非空,长度≤64|与OCR结果一致性比对|`ERR_PARAM_004`|
|
||
|**行业分类**|非空,必须为GB/T 4754叶子节点|枚举校验|`ERR_PARAM_005`|
|
||
|**注册地址**|非空,长度≤256|-|`ERR_PARAM_006`|
|
||
|**经营范围**|非空,长度≤500|-|`ERR_PARAM_007`|
|
||
|**主要产品**|数组非空,每个≤30字符,最多20个|-|`ERR_PARAM_008`|
|
||
|**联系人姓名**|非空,长度≤64|-|`ERR_PARAM_009`|
|
||
|**联系电话**|非空,格式`^1[3-9]\d{9}$`|唯一性校验(`contact_phone`不可重复)|`ERR_PARAM_010` / `ERR_REG_003`|
|
||
|**电子邮箱**|非空,邮箱格式正则|唯一性校验|`ERR_PARAM_011` / `ERR_REG_003`|
|
||
|**短信验证码**|非空,6位数字|匹配Redis缓存,有效期5分钟|`ERR_AUTH_001`|
|
||
|**登录密码**|非空,长度8-20|强度校验(大小写+数字+特殊字符+弱口令字典)|`ERR_PARAM_012`|
|
||
|**确认密码**|非空,与密码一致|与密码字段比对|`ERR_PARAM_013`|
|
||
|
||
---
|
||
|
||
## 5. AI自动审核详细设计
|
||
|
||
### 5.1 审核前置:SRM存量检查
|
||
|
||
```mermaid
|
||
graph TD
|
||
A[接收注册请求] --> B{请求中是否包含<br>confirmSrmBinding=true?}
|
||
B -->|是| C[跳过所有AI检查,直接执行SRM绑定流程]
|
||
B -->|否| D[调用SRM查询接口<br>根据信用代码查询供应商]
|
||
D --> E{SRM中存在?}
|
||
E -->|是| F[返回特殊错误码ERR_SRM_EXISTS<br>附带SRM供应商基本信息]
|
||
E -->|否| G[进入AI审核流程(原有7项检查)]
|
||
C --> H[创建STP供应商记录status=ACTIVE<br>绑定srm_id]
|
||
G --> I{综合仲裁}
|
||
I -->|通过| H
|
||
I -->|不通过| J[驳回]
|
||
```
|
||
|
||
|
||
### 5.2 各项检查详细规则
|
||
|
||
| 检查项 | 调用源 | 通过条件 | 硬性拒绝条件 | 超时处理 |
|
||
| :------------- | :----------- | :----------------------- | :------------------------ | :--------------------- |
|
||
| **OCR完整性** | 内部OCR服务 | 成功提取「企业全称+信用代码+法人」 | 三项任一缺失 | 超时3s → 降级允许手动输入,但不自动通过 |
|
||
| **工商验真** | 天眼查/企查查API | 输入名称与信用代码匹配,且置信度≥95% | 不匹配 | 超时5s → 返回“服务繁忙,请稍后重试” |
|
||
| **企业存续状态** | 天眼查API | 状态为“存续(在营)” | 注销/吊销/清算 | 使用缓存数据(缓存≤24h) |
|
||
| **严重违法** | 国家企业信用信息公示系统 | 不在“严重违法失信名单” | 在名单中 | 若API不可用,默认通过(记录告警) |
|
||
| **经营异常** | 天眼查API | 不在“经营异常名录” | **不硬性拒绝**,仅标记 `risk_tags` | 若API不可用,默认无异常 |
|
||
| **信用代码唯一性** | 本地DB | 未注册(含ACTIVE/REJECTED/锁定) | 已存在(不论状态) | N/A |
|
||
| **手机号唯一性** | 本地DB | 未被其他ACTIVE供应商绑定 | 已绑定 | N/A |
|
||
| **SRM 存量供商应商** | | | | |
|
||
| | | | | |
|
||
### 5.3 SRM存量绑定跳过项
|
||
|
||
当 `confirmSrmBinding=true` 时,跳过以下检查:
|
||
|
||
- OCR完整性
|
||
- 工商验真
|
||
- 企业状态
|
||
- 严重违法
|
||
- 经营异常
|
||
|
||
仍保留的检查:
|
||
- 信用代码唯一性(STP内)
|
||
- 手机号/邮箱唯一性
|
||
|
||
|
||
### 5.3 综合仲裁逻辑(伪代码)
|
||
|
||
```python
|
||
def audit_register(request):
|
||
results = parallel_run([
|
||
ocr_check(request.file),
|
||
business_verify(request.credit_code, request.company_name),
|
||
status_check(request.credit_code),
|
||
fraud_check(request.credit_code),
|
||
exception_check(request.credit_code),
|
||
uniqueness_check(request.credit_code, request.phone, request.email)
|
||
])
|
||
|
||
# 一票否决项
|
||
fatal_checks = ['ocr', 'verify', 'status', 'fraud', 'uniqueness']
|
||
for check in fatal_checks:
|
||
if not results[check].passed:
|
||
return reject(results[check].reason)
|
||
|
||
# 经营异常仅标记,不阻止
|
||
if results['exception'].has_exception:
|
||
mark_risk_tag(request.supplier_id, '经营异常')
|
||
|
||
return approve()
|
||
```
|
||
|
||
---
|
||
|
||
## 6. 完整业务流程(含状态机)
|
||
|
||
### 6.1 状态定义(精简)
|
||
|
||
| 状态码 | 名称 | 说明 | 可执行操作 |
|
||
| :--- | :--- | :--- | :--- |
|
||
| `DRAFT` | 草稿 | 用户正在填写,尚未提交 | 编辑、提交 |
|
||
| `SUBMITTED` | 提交审核中 | AI正在执行检查(瞬时状态,<5s) | 无(轮询等待) |
|
||
| `ACTIVE` | 已激活/正常 | AI全部通过,可登录报名 | 登录、报名、修改信息、变更联系人 |
|
||
| `REJECTED` | 审核驳回 | 至少一项一票否决未通过 | 查看原因、修改后重新提交(30天内) |
|
||
| `LOCKED` | 已锁定 | 违规操作/被管理员冻结 | 联系管理员解封 |
|
||
| `ARCHIVED` | 已归档 | 长期未使用或主动注销 | 只读(不可登录) |
|
||
|
||
### 6.2 状态流转图
|
||
|
||
```mermaid
|
||
stateDiagram-v2
|
||
[*] --> DRAFT: 开始注册
|
||
DRAFT --> SUBMITTED: 提交(不含SRM绑定确认)
|
||
SUBMITTED --> SRM_EXISTS: 检测到SRM存量(返回ERR_SRM_EXISTS)
|
||
SRM_EXISTS --> DRAFT: 用户取消/关闭弹窗
|
||
SRM_EXISTS --> ACTIVE: 用户选择“继续注册”(带confirmSrmBinding=true)
|
||
SRM_EXISTS --> [*]: 用户选择“使用SRM账号登录”(跳转登录)
|
||
SUBMITTED --> ACTIVE: AI全部通过(新供应商)
|
||
SUBMITTED --> REJECTED: AI不通过
|
||
REJECTED --> DRAFT: 修改重提
|
||
ACTIVE --> LOCKED: 管理员冻结
|
||
LOCKED --> ACTIVE: 解封
|
||
```
|
||
|
||
> **注意**:由于审核是实时的,`SUBMITTED`状态在数据库层面几乎不持久化(前端展示加载态即可)。
|
||
|
||
### 6.3 完整业务流程
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
subgraph 供应商端
|
||
A[访问注册页] --> B[填写完整信息并提交]
|
||
B --> C[等待审核结果(3-8秒)]
|
||
C --> D{前端收到响应}
|
||
D -->|成功(新供应商通过)| E[显示成功页+自动登录]
|
||
D -->|错误码ERR_SRM_EXISTS| F[弹出SRM存量引导窗]
|
||
F --> G{用户选择}
|
||
G -->|“继续注册”| H[重新提交请求带confirmSrmBinding=true]
|
||
G -->|“使用SRM账号登录”| I[跳转登录页]
|
||
G -->|取消| J[返回注册页修改]
|
||
D -->|其他驳回| K[显示驳回原因+修改]
|
||
H --> L[立即成功+自动登录]
|
||
end
|
||
```
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
subgraph STP后端
|
||
STP1[接收注册请求] --> STP2{是否有confirmSrmBinding=true?}
|
||
STP2 -->|是| STP3[跳过AI审核,查询SRM获取srmId]
|
||
STP2 -->|否| STP4[调用SRM查询接口检测存量]
|
||
STP4 --> STP5{SRM存在?}
|
||
STP5 -->|存在| STP6[返回ERR_SRM_EXISTS(含srmId+名称)]
|
||
STP5 -->|不存在| STP7[执行AI审核(7项检查)]
|
||
STP7 --> STP8{AI通过?}
|
||
STP8 -->|通过| STP9[创建供应商status=ACTIVE]
|
||
STP8 -->|不通过| STP10[创建供应商status=REJECTED]
|
||
STP3 --> STP9
|
||
STP9 --> STP11[创建用户账号+密码哈希]
|
||
STP11 --> STP12[生成JWT Token]
|
||
STP12 --> STP13[同步SRM(若已有srmId则更新绑定)]
|
||
STP13 --> STP14[发送成功通知]
|
||
STP10 --> STP15[发送驳回通知]
|
||
end
|
||
subgraph 外部系统
|
||
EXT1[SRM查询接口]
|
||
EXT2[SRM注册/绑定接口]
|
||
EXT3[通知网关]
|
||
end
|
||
STP4 -.-> EXT1
|
||
STP3 -.-> EXT2
|
||
STP14 -.-> EXT3
|
||
```
|
||
|
||
---
|
||
|
||
## 7. 时序图
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant U as 供应商
|
||
participant FE as 前端
|
||
participant GW as API网关
|
||
participant RegSvc as 注册服务
|
||
participant AISvc as AI审核服务
|
||
participant BizAPI as 天眼查API
|
||
participant SRM as SRM系统
|
||
participant DB as STP数据库
|
||
participant Notify as 通知服务
|
||
|
||
U->>FE: 填写完整信息(含密码)并提交
|
||
FE->>GW: POST /register(无confirmSrmBinding)
|
||
GW->>RegSvc: 转发
|
||
|
||
RegSvc->>DB: 校验信用代码是否已在STP注册
|
||
alt 已注册
|
||
DB-->>RegSvc: 已存在
|
||
RegSvc-->>FE: 409 ERR_REG_002(已注册)
|
||
else 未注册
|
||
RegSvc->>SRM: GET /query/by-tax-id(查询存量)
|
||
SRM-->>RegSvc: 返回结果(存在或不存在)
|
||
alt SRM存在
|
||
RegSvc-->>FE: 200 但 code=ERR_SRM_EXISTS,附带srmId和supplierName
|
||
FE-->>U: 弹出引导窗
|
||
U->>FE: 点击“继续注册”
|
||
FE->>GW: POST /register + confirmSrmBinding=true
|
||
GW->>RegSvc: 转发(携带srmId)
|
||
RegSvc->>DB: 创建供应商 status=ACTIVE,绑定srmId
|
||
RegSvc->>RegSvc: 创建用户账号,密码哈希
|
||
RegSvc->>RegSvc: 生成JWT Token
|
||
RegSvc->>Notify: 发送成功通知(无需激活)
|
||
RegSvc-->>FE: 200 成功 + accessToken
|
||
FE->>FE: 存储Token
|
||
FE-->>U: 自动跳转工作台
|
||
else SRM不存在(新供应商)
|
||
RegSvc->>AISvc: 执行AI审核(7项并行)
|
||
AISvc->>BizAPI: 工商验真/状态/违法等
|
||
BizAPI-->>AISvc: 结果
|
||
AISvc-->>RegSvc: 审核结论
|
||
alt 通过
|
||
RegSvc->>DB: 创建供应商 status=ACTIVE
|
||
RegSvc->>RegSvc: 创建用户账号、生成Token
|
||
RegSvc->>SRM: POST /register(同步注册至SRM)
|
||
SRM-->>RegSvc: srmId
|
||
RegSvc->>DB: 更新srmId
|
||
RegSvc->>Notify: 发送成功通知
|
||
RegSvc-->>FE: 200 成功 + Token
|
||
FE-->>U: 自动登录
|
||
else 不通过
|
||
RegSvc->>DB: 创建供应商 status=REJECTED
|
||
RegSvc->>Notify: 发送驳回通知
|
||
RegSvc-->>FE: 400 失败(含原因)
|
||
FE-->>U: 显示驳回页
|
||
end
|
||
end
|
||
end
|
||
```
|
||
|
||
---
|
||
|
||
## 8. 数据模型完整定义
|
||
|
||
### 8.1 供应商主表 `t_supplier_profile`
|
||
|
||
| 字段名 | 类型 | 约束 | 说明 |
|
||
| ---------------------- | ------------- | ---------------- | -------------------------------------- |
|
||
| `id` | BIGINT | PK | 自增主键 |
|
||
| `supplier_id` | VARCHAR(32) | UNIQUE, NOT NULL | 业务编号 |
|
||
| `user_id` | VARCHAR(32) | INDEX | 关联用户账号 |
|
||
| `srm_id` | VARCHAR(32) | INDEX | SRM系统ID(存量绑定或新注册同步后回填) |
|
||
| `company_name` | VARCHAR(128) | NOT NULL | 企业全称 |
|
||
| `credit_code` | VARCHAR(128) | NOT NULL, UNIQUE | 信用代码(加密) |
|
||
| `credit_code_md5` | CHAR(32) | UNIQUE | MD5盲索引 |
|
||
| `registered_capital` | DECIMAL(15,2) | | 注册资本 |
|
||
| `legal_representative` | VARCHAR(64) | | 法定代表人 |
|
||
| `industry_code` | VARCHAR(16) | | 行业代码 |
|
||
| `industry_name` | VARCHAR(64) | | 行业名称 |
|
||
| `address` | VARCHAR(256) | | 注册地址 |
|
||
| `business_scope` | TEXT | | 经营范围 |
|
||
| `main_products` | JSON | | 主要产品 |
|
||
| `contact_name` | VARCHAR(64) | NOT NULL | 联系人姓名 |
|
||
| `contact_phone` | VARCHAR(128) | NOT NULL, UNIQUE | 手机号(加密) |
|
||
| `contact_phone_md5` | CHAR(32) | UNIQUE | MD5 |
|
||
| `contact_email` | VARCHAR(128) | NOT NULL | 邮箱 |
|
||
| `license_file_url` | VARCHAR(512) | | 营业执照URL |
|
||
| `license_ocr_result` | JSON | | OCR结果 |
|
||
| `audit_result` | JSON | | AI审核报告(新供应商) |
|
||
| **`source_type`** | VARCHAR(20) | NOT NULL | **NEW / SRM_BINDING**(标识注册来源) |
|
||
| **`srm_bind_time`** | DATETIME | | **SRM绑定时间(存量绑定)** |
|
||
| `status` | VARCHAR(20) | NOT NULL | DRAFT/SUBMITTED/ACTIVE/REJECTED/LOCKED |
|
||
| `risk_tags` | JSON | | 风险标签 |
|
||
| `reject_reasons` | JSON | | 驳回原因 |
|
||
| `activated_at` | DATETIME | | 激活时间(即注册成功时间) |
|
||
| `created_at` | DATETIME(3) | | 创建时间 |
|
||
| `updated_at` | DATETIME(3) | | 更新时间 |
|
||
|
||
### 8.2 用户账号表 `t_user`(新增字段)
|
||
|
||
| 字段名 | 类型 | 说明 |
|
||
| --------------- | ------------ | ----------- |
|
||
| `user_id` | VARCHAR(32) | PK |
|
||
| `supplier_id` | VARCHAR(32) | FK |
|
||
| `login_name` | VARCHAR(64) | UNIQUE(手机号) |
|
||
| `password_hash` | VARCHAR(128) | Argon2id哈希 |
|
||
| `user_type` | VARCHAR(20) | 'SUPPLIER' |
|
||
| `status` | VARCHAR(20) | 'ACTIVE' |
|
||
| `last_login_at` | DATETIME | |
|
||
|
||
---
|
||
|
||
## 9. API接口详细规范
|
||
|
||
### 9.1 提交供应商注册(支持SRM存量检测)
|
||
|
||
|项目|内容|
|
||
|---|---|
|
||
|**路径**|`/api/v1/supplier/register`|
|
||
|**方法**|`POST`|
|
||
|
||
**请求体(第一次提交,无confirmSrmBinding)**
|
||
|
||
```json
|
||
{
|
||
"companyName": "滨化新材料有限公司",
|
||
"creditCode": "91370100MA3XXXXXXX",
|
||
"registeredCapital": 5000.00,
|
||
"legalRepresentative": "张伟",
|
||
"industryCode": "C2614",
|
||
"address": "山东省滨州市黄河五路888号",
|
||
"businessScope": "化工产品、新材料技术研发...",
|
||
"mainProducts": ["聚丙烯", "环氧丙烷"],
|
||
"contactName": "李强",
|
||
"contactPhone": "13800138001",
|
||
"contactEmail": "liqiang@example.com",
|
||
"licenseFileUrl": "https://oss.stp.com/xxx.pdf",
|
||
"smsCode": "123456",
|
||
"password": "Abcd@2026#",
|
||
"confirmPassword": "Abcd@2026#",
|
||
"agreeTerms": true
|
||
}
|
||
```
|
||
|
||
**特殊响应(检测到SRM存量)**
|
||
|
||
```json
|
||
{
|
||
"code": "ERR_SRM_EXISTS",
|
||
"message": "检测到该供应商已在SRM系统中注册",
|
||
"data": {
|
||
"srmId": "SRM_12345",
|
||
"companyName": "滨化新材料有限公司",
|
||
"creditCode": "91370100MA3XXXXXXX",
|
||
"hint": "您可以选择继续注册(跳过资质审核)或使用SRM账号登录"
|
||
}
|
||
}
|
||
```
|
||
|
||
**第二次提交(用户选择继续注册)**
|
||
|
||
```json
|
||
{
|
||
...(同第一次请求),
|
||
"confirmSrmBinding": true,
|
||
"srmId": "SRM_12345" // 可选,后端也可再次查询
|
||
}
|
||
```
|
||
|
||
**成功响应(无论新注册还是存量绑定,均一致)**
|
||
|
||
```json
|
||
{
|
||
"code": "0",
|
||
"data": {
|
||
"supplierId": "SUP_20260630_0158",
|
||
"srmId": "SRM_12345",
|
||
"userId": "U_...",
|
||
"status": "ACTIVE",
|
||
"accessToken": "eyJ...",
|
||
"tokenType": "Bearer",
|
||
"expiresIn": 1800,
|
||
"sourceType": "NEW" 或 "SRM_BINDING",
|
||
"message": "注册成功,账号已激活"
|
||
}
|
||
}
|
||
```
|
||
|
||
### 9.2 营业执照OCR(预填)
|
||
|
||
| 项目 | 内容 |
|
||
| :--------- | :------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||
| **路径** | `/api/v1/ocr/business-license` |
|
||
| **方法** | `POST` |
|
||
| **请求** | `multipart/form-data`,字段 `file` |
|
||
| **响应(成功)** | `{"code":"0","data":{"companyName":"...","creditCode":"...","legalPerson":"...","registeredCapital":"...","address":"...","businessScope":"..."}}` |
|
||
| **响应(失败)** | `{"code":"ERR_OCR_001","message":"图片识别失败,请确认图片清晰"}` |
|
||
|
||
### 9.3 发送短信验证码
|
||
|
||
| 项目 | 内容 |
|
||
| :--- | :--- |
|
||
| **路径** | `/api/v1/auth/sms/send` |
|
||
| **方法** | `POST` |
|
||
| **请求** | `{"phone":"13800138000","captchaToken":"图形验证码"}` |
|
||
| **响应** | `{"code":"0","data":{"expireSeconds":300,"resendAfterSeconds":60}}` |
|
||
|
||
**其他错误码**
|
||
- `ERR_REG_002`:信用代码已注册(409)
|
||
- `ERR_REG_003`:手机号已被占用(409)
|
||
- `ERR_REG_004`:邮箱已被占用(409)
|
||
- `ERR_REG_005`:短信验证码错误或过期(401)
|
||
- `ERR_REG_006`:工商API服务繁忙,请稍后重试(503)
|
||
|
||
### 9.4 首次登录激活(设置密码)
|
||
|
||
| 项目 | 内容 |
|
||
| :--- | :--- |
|
||
| **路径** | `/api/v1/supplier/activate` |
|
||
| **方法** | `POST` |
|
||
| **请求** | `{"supplierId":"SUP_...","password":"Abcd@1234","confirmPassword":"Abcd@1234"}` |
|
||
| **成功** | `{"code":"0","message":"激活成功"}` |
|
||
|
||
---
|
||
|
||
## 10. 业务规则与约束
|
||
|
||
| ID | 规则 | 说明 |
|
||
| --- | ---------------- | ------------------------------------------------------- |
|
||
| R01 | **信用代码唯一性** | 无论来源,STP内信用代码唯一。 |
|
||
| R02 | **SRM存量检测优先** | 每次注册请求首先查询SRM,除非已带`confirmSrmBinding`参数。 |
|
||
| R03 | **存量绑定跳过AI** | 当`confirmSrmBinding=true`时,跳过OCR、工商验真、状态、违法检查,仅保留唯一性校验。 |
|
||
| R04 | **存量绑定的SRM账号状态** | 不要求SRM账号必须激活,只要存在记录即可绑定。 |
|
||
| R05 | **密码设置要求** | 存量绑定同样需要设置密码(与正常注册相同)。 |
|
||
| R06 | **存量绑定后SRM同步** | 若srmId已存在,则无需再次调用SRM注册接口,只需在STP记录绑定关系。若srmId为空(异常),需补录。 |
|
||
| R07 | **引导弹窗后取消** | 用户取消则回到注册页,可修改信息后再次提交(会再次触发SRM检测)。 |
|
||
| R08 | **重复检测** | 若STP已存在同信用代码,直接返回已注册,不查询SRM。 |
|
||
|
||
---
|
||
|
||
## 11. 异常处理矩阵
|
||
|
||
|异常场景|处理方式|用户侧表现|后端动作|
|
||
|---|---|---|---|
|
||
|**SRM查询接口超时**|降级:视为新供应商,进入AI审核|正常AI审核流程(可能延迟)|记录告警,超时5s后转AI|
|
||
|**SRM查询返回错误**|降级:视为新供应商|正常AI审核|记录错误日志|
|
||
|**存量绑定后SRM绑定失败**|异步重试,STP仍激活|用户无感知|标记同步失败,告警管理员|
|
||
|**用户选择“继续注册”但信用代码已在STP注册**|返回已注册错误|提示“该企业已注册”|返回ERR_REG_002|
|
||
|
||
---
|
||
|
||
## 12. 通知策略
|
||
|
||
|触发事件|通知方式|接收方|文案|
|
||
|---|---|---|---|
|
||
|**新供应商注册成功**|邮件+短信|联系人|“恭喜X公司注册成功!已自动激活,请直接登录。”|
|
||
|**SRM存量绑定成功**|邮件+短信|联系人|“恭喜X公司完成STP账号绑定,您已是滨化供应商,可直接登录参与投标。”|
|
||
|**注册驳回**|邮件+短信|联系人|“注册未通过,原因:...”|
|
||
|
||
---
|
||
|
||
## 13. SRM双向同步机制
|
||
|
||
### 13.1 注册时正向同步(STP → SRM)
|
||
|
||
- **触发时机**:AI审核通过,`INSERT`成功后立即执行。
|
||
- **接口**:`POST /api/v1/supplier/register`(见接口方案3.2)
|
||
- **映射关系**:
|
||
|
||
| STP字段 | SRM字段 |
|
||
| :--- | :--- |
|
||
| `company_name` | `supplierName` |
|
||
| `credit_code` | `taxId` |
|
||
| `contact_name` | `contactInfo.name` |
|
||
| `contact_phone` | `contactInfo.phone` |
|
||
| `contact_email` | `contactInfo.email` |
|
||
| `address` | `address` |
|
||
|
||
- **状态映射**:STP `ACTIVE` → SRM `PENDING_CERTIFICATION`(需后续补充资质文件完成认证)。
|
||
|
||
### 13.2 SRM存量供应商绑定(无需同步,仅获取srmId)
|
||
|
||
- 调用SRM查询接口获取srmId,存入本地,不调用注册接口。
|
||
- 如果SRM供应商状态为“未激活”等,不影响STP绑定。
|
||
|
||
### 13.3 反向同步(SRM → STP)
|
||
若SRM侧修改供应商信息,通过定时增量同步接口更新STP(联系人等),与本注册模块无关。
|
||
|
||
---
|
||
|
||
## 14. 供应商联系人变更(注册后维护)
|
||
|
||
> 本模块为注册流程的**必要延伸**,确保企业联系人信息变更时账户安全与业务通知可达。
|
||
|
||
### 14.1 自助变更(旧手机号可用)
|
||
|
||
```mermaid
|
||
flowchart LR
|
||
A[登录工作台] --> B[进入企业信息页]
|
||
B --> C[点击修改联系人]
|
||
C --> D[验证旧手机号/邮箱]
|
||
D --> E[填写新联系人信息]
|
||
E --> F[验证新手机号/邮箱]
|
||
F --> G[提交变更]
|
||
G --> H[即时生效 + 同步SRM]
|
||
```
|
||
|
||
### 14.2 人工辅助变更(旧手机号不可用)
|
||
|
||
```mermaid
|
||
flowchart LR
|
||
A[点击“旧手机无法接收验证码?”] --> B[下载《变更申请函》模板]
|
||
B --> C[填写+加盖公章]
|
||
C --> D[上传盖章扫描件]
|
||
D --> E[提交人工申请]
|
||
E --> F[管理员审核(4h内)]
|
||
F --> G[后台执行变更]
|
||
```
|
||
|
||
### 14.3 接口与数据(简略)
|
||
|
||
- **变更接口**:`PUT /api/v1/supplier/contact`
|
||
- **审计日志**:写入`t_supplier_contact_change_log`(含旧值/新值/变更人/IP/哈希链)。
|
||
- **SRM同步**:调用`POST /api/v1/supplier/contact/update`。
|
||
|
||
> 详细设计参考《联系人变更补充设计》,此处仅作概要嵌入,确保完整性。
|
||
|
||
---
|
||
|
||
## 15. 安全与合规
|
||
|
||
| 安全维度 | 具体措施 |
|
||
| :--------- | :----------------------------------------------------------------------------------- |
|
||
| **传输安全** | 强制 HTTPS + TLS 1.3,HSTS 头部强制。 |
|
||
| **敏感字段加密** | `credit_code`、`contact_phone` 使用 **AES-256-GCM** 应用层加密,密钥由KMS托管,内存动态漂移(参见全局安全方案4.2)。 |
|
||
| **可搜索加密** | 唯一性校验使用字段的 **MD5/SHA-256** 盲索引(不可逆),不直接对密文进行`SELECT`。 |
|
||
| **数据脱敏展示** | 后台展示信用代码为 `913701**********`,手机号为 `138****8001`。 |
|
||
| **审计防篡改** | 所有审计日志采用 **哈希链+数字签名**(参见需求说明书4.8.1),保留≥7年。 |
|
||
| **防机器人** | 注册提交需验证图形验证码(极验),手机验证码发送每日限5次。 |
|
||
| **密码安全** | 首次激活密码强度≥8位,含大小写+数字+特殊字符,不得为弱口令(密码字典检测)。 |
|
||
| **合规性** | 满足《个人信息保护法》第13条(告知同意)、第17条(隐私政策),注册前勾选同意《用户协议》、《隐私政策》及《反商业贿赂承诺书》。 |
|
||
|
||
---
|
||
|
||
## 16. 附录
|
||
|
||
### 16.1 统一社会信用代码正则(前端+后端)
|
||
|
||
```regex
|
||
^[0-9A-HJ-NPQRTUWXY]{2}\d{6}[0-9A-HJ-NPQRTUWXY]{10}$
|
||
```
|
||
|
||
### 16.2 行业分类示例(GB/T 4754-2017 三级)
|
||
|
||
| 一级 | 二级 | 三级代码 | 三级名称 |
|
||
| :--- | :--- | :--- | :--- |
|
||
| C 制造业 | 26 化学原料和化学制品制造业 | 2614 | 有机化学原料制造 |
|
||
| C 制造业 | 29 橡胶和塑料制品业 | 2926 | 塑料包装箱及容器制造 |
|
||
| E 建筑业 | 48 土木工程建筑业 | 4813 | 市政道路工程建筑 |
|
||
|
||
### 16.3 错误码速查表
|
||
|
||
| 错误码 | HTTP状态 | 含义 |
|
||
| :--- | :--- | :--- |
|
||
| `0` | 200 | 成功 |
|
||
| `ERR_PARAM_001~011` | 400 | 参数校验失败(具体见第4章) |
|
||
| `ERR_REG_001` | 400 | 注册审核不通过 |
|
||
| `ERR_REG_002` | 409 | 信用代码已注册 |
|
||
| `ERR_REG_003` | 409 | 手机号/邮箱已被占用 |
|
||
| `ERR_REG_004` | 503 | 工商API服务繁忙 |
|
||
| `ERR_AUTH_001` | 401 | 验证码错误/过期 |
|
||
| `ERR_FILE_001` | 400 | 文件格式/大小不符 |
|
||
| `ERR_OCR_001` | 400 | OCR识别失败 |
|
||
| `ERR_SYS_001` | 500 | 系统内部异常 |
|