docs(architecture): v0.3 为多人、多登录方式预留表结构(AR11、AR12)

团队版、企业版、飞书 / 企业微信登录都不在早期版本,但 MVP 的表结构先按
多人、多登录方式建,以后接入不用重构:
- 身份拆成人 / 登录方式 / 成员身份三层,一人一企业在服务层限制
- 项目成员表、统一的权限检查点和可见范围过滤先建好
- 记录「谁」的字段指向人,人不做物理删除;订阅带席位数

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
YANG JIANKUAN
2026-11-03 17:19:21 +08:00
parent 819e66519a
commit 47242ba9d7
4 changed files with 72 additions and 10 deletions

View File

@@ -1,7 +1,7 @@
# 技术架构设计 v0.2
# 技术架构设计 v0.3
> 依据:PRD v0.9.1 第 7 章(非功能需求)、`docs/analysis/`(功能需求、领域模型、样本结论)、`docs/decisions.md`(T1 及各阶段决策)、参考项目分析 `ref-bid-engine-analysis.md`。
> 状态:v0.2,选型已全部确认。
> 状态:v0.3。v0.2 的选型已全部确认;v0.3 新增 §3.14:MVP 的表结构先按多人、多登录方式建,给以后的团队版、企业版、飞书 / 企业微信登录留结构(AR11、AR12,已确认)。
> - 已确认(2026-11-03):T2–T5;AR1、AR3、AR5–AR9 按建议;AR2 改为 Vue 3 + Vite。
> - 已确认:AR4 修订后的建议(引入 Valkey,只存可以丢的数据,§6.1);AR10 不引入带样式的 UI 框架(§4.3)。至此选型全部确认。
> - 确认后写入 `docs/decisions.md`;构建、运行、测试命令等工程骨架搭好后回填到 AGENTS.md。
@@ -147,7 +147,7 @@ flowchart LR
| 模块 | 职责 | 对应需求 |
|---|---|---|
| `identity` | 账号、企业主体(租户)、会话、短信验证码、登录风控 | FR-M6-04 |
| `identity` | 人、登录方式、企业主体(租户)、成员身份、会话、短信验证码、登录风控、权限点(§3.14) | FR-M6-04 |
| `billing` | 套餐、订阅、额度账户、额度流水 | FR-M6-03、05 |
| `project` | 项目、分包、状态流、就绪标记 | FR-M6-01、02 |
| `document` | 文件上传、格式识别、转换、文字层、OCR、出处定位 | FR-M2-01、06 |
@@ -307,7 +307,7 @@ flowchart LR
- 放在 PostgreSQL 而不是 Redis,理由见 §6.1:能持久化、能按用户列出所有会话(用来实现「同一账号异地多设备登录时提示并限制」,FR-M6-04),删除租户时也能级联清理。
- 网页端用 HttpOnly + Secure cookie,桌面端用 Bearer 令牌并存进系统钥匙串。
- **验证码和登录限流**:短信验证码、发送频率、登录失败次数都放在 Valkey 里,带过期时间。这些数据丢了,最多是用户重新获取一次验证码。
- **身份来源可插拔**:SaaS 用手机号 + 短信验证码;私有化用账号密码,并预留 OIDC、LDAP 接入(P2 的 SSO)。
- **身份来源可插拔**:SaaS 用手机号 + 短信验证码;私有化用账号密码,并预留 OIDC、LDAP 接入(P2 的 SSO);飞书、企业微信登录按同一接口扩展(§3.14)。
- **密钥**:只从环境变量或密钥管理服务注入,代码和镜像里一律不放。启动时如果检测到默认密钥或弱密钥,在生产环境下拒绝启动。
- **调试开关**:「跳过登录」之类的开关只在测试配置里存在;生产环境检测到这类开关会直接退出。
- **加密**:
@@ -409,6 +409,51 @@ flowchart LR
- **报价**:模板里的报价位置只放空白填充位,由用户自己填写(红线 6)。
- **OFD 格式**:标擎没有处理国家标准的 OFD 版式文件(GB/T 33190)。部分政府采购平台和电子公文会用到 OFD,需要在种子用户访谈时确认是否需要支持;需要的话再评估开源的 OFD 解析和转换库。
### 3.14 账号、企业与权限:给以后的版本留结构
以后会上团队版和企业版:一个企业多个席位,按角色和项目分权限;也会支持飞书、企业微信登录,以及私有化部署的 SSO。**这些功能早期版本都不做**。这一节只定一件事:MVP 的表结构和代码分层先按多人、多登录方式来建,以后上这些功能时只加功能,不用重构。
#### 原设计的问题
原设计的租户隔离(§3.3)和建在企业上的额度池(§3.9)不用改。会导致以后要重构的有三处:
1. **用户表直接带所属企业**(04 §2)。以后一个人要能有多种登录方式、要能离职、要能加入别的企业,就得拆表、迁移数据。
2. **权限只有租户这一层**。以后加项目级权限,要回头给每个接口补检查,还要改所有列表查询。
3. **记录「谁」的字段没约定指向哪张表**。如果指向会被删除的账号,人离职以后,留痕就断了。
#### MVP 就按这个结构建
| 内容 | MVP 的样子 | 以后接入时 |
|---|---|---|
| `user_account`:一个自然人,全局表,不带 `tenant_id` | 注册时建一条 | 不变 |
| `login_identity`:登录方式,字段有来源、外部组织编号、外部用户编号;唯一键是「来源 + 外部组织 + 外部用户」 | 只有「手机号」一种来源 | 飞书、企业微信、账号密码、OIDC、LDAP 都只是新的来源 |
| `membership`:人在企业里的成员身份,字段有企业角色、状态 | 每个企业一条,角色是「所有者」;「一人只属于一个企业」(FR-M6-04)在服务层检查,不写进表结构 | 加邀请、停用,放开一人多企业 |
| `user_session` 绑定 `membership_id` | 当前租户从成员身份上取 | 切换企业就是换一个会话 |
| `project_member`:项目、用户、项目角色 | 建项目的人自动成为负责人 | 加项目成员管理、可见范围 |
| 订阅带「席位数」 | 写 1 | 按席位计费 |
| 权限点枚举 + service 层统一调用 `authz.require(ctx, 权限点, 对象)` | 所有者拥有全部权限点,检查总是通过 | 加角色、改角色和权限点的对应关系;接口代码不用改 |
| 列表查询走 repo 层统一的可见范围过滤 | 过滤条件只有租户 | 加「只有项目成员可见」的条件 |
| `IdentityProvider` 接口:授权地址 → 回调换取用户信息 → 统一的外部身份 | 只有手机号验证码一个实现 | 每种新登录方式加一个实现 |
约定:
- 业务表里记录「谁」的字段(创建人、确认人、处理人)都指向 `user_account`。人不做物理删除,离职只停用成员身份。
- 额度流水和审计事件都记操作人;审计再存一份操作人姓名快照,人离职后,报告和留痕上仍显示当时确认的人。
- `user_account` 和 `login_identity` 是 §3.3 唯一的例外:全局表,不受行级安全策略约束,只能由 `identity` 模块读写,在 import-linter 规则里写明。
- 行级安全策略只管租户这一层,项目级权限在应用层判断。
这些在 MVP 里多出来的工作只是两三张表和一层检查调用,界面上看不出任何区别。
#### 接入时再定的事
下面这些不影响表结构,到对应版本立项时再设计:
- 团队版、企业版的具体角色和权限点划分(PRD 写的是管理员 / 编标 / 只读);
- 项目可见范围的默认值、分工指派;
- 飞书、企业微信的应用形态,外部组织怎么绑定到企业,新成员是否需要批准,账号怎么合并;
- 桌面端的第三方登录回调方式;
- 个人注销和企业注销的拆分(MVP 只有一个成员,两者是同一个操作)。
## 4. 前端
### 4.1 Vue 3 单页应用(AR2,已确认)
@@ -739,7 +784,8 @@ docs/ # 现有文档
物理建模在下一步细化,这里先定原则:
- 主键用 **UUIDv7**:按时间有序、不可猜测;私有化环境和 SaaS 之间迁移数据时也不会冲突。
- 所有业务表都带 `tenant_id`、`created_at`、`updated_at`;时间一律用 `timestamptz`。
- 所有业务表都带 `tenant_id`、`created_at`、`updated_at`;时间一律用 `timestamptz`。例外只有 `user_account` 和 `login_identity` 两张全局表(§3.14)。
- 业务表里记录「谁」的字段(创建人、确认人、处理人)都指向 `user_account`,不指向成员身份;人不做物理删除。
- 出处(Citation)存:文档编号、规范化 PDF 的哈希、页码、句子编号、字符范围、坐标框数组(`quads`)。
- AI 值和人工值分开存(§3.8);状态字段用枚举,不用自由文本。
- 文字层的逐字数据量大,存在对象存储里;数据库里只存句子和块的索引,以及校验要用的文本。
@@ -783,10 +829,12 @@ S1–S4 风险最高,建议先做;S8(桌面外壳 PoC)等桌面版立项
| AR8 | 会话机制 | 服务端会话(可吊销),令牌只存哈希;网页端用 cookie,桌面端用 Bearer 令牌 | 已确认 |
| AR9 | 仓库组织 | 单一仓库(monorepo) | 已确认 |
| AR10 | UI 组件 | **不引入带样式的 UI 框架**:视觉自己写,复杂交互用 Reka UI(无样式)打底;表格、表单也用无样式方案(§4.3)。运营后台另议 | 已确认 |
| AR11 | 身份模型 | 拆成人(`user_account`)、登录方式(`login_identity`)、成员身份(`membership`)三层,MVP 就按这个结构建;「一人只属于一个企业」在服务层限制(§3.14) | 已确认 |
| AR12 | 权限检查点 | MVP 就建 `project_member` 表,service 层统一调用 `authz.require`,列表查询统一按可见范围过滤;具体角色划分到团队版再定(§3.14) | 已确认 |
## 12. 下一步
1. 做技术验证 S1–S4,结论写进本文件,并据此确定 FR-M2-05、FR-M2-06 的目标值。
2. 物理数据建模:在 04 领域模型的基础上细化表结构,重点是清单项、出处、问题、额度流水。
2. 物理数据建模:在 04 领域模型的基础上细化表结构,重点是清单项、出处、问题、额度流水,以及 §3.14 的人 / 登录方式 / 成员身份、项目成员。
3. 定接口契约:先写核心接口的 OpenAPI 定义,前后端据此并行开发。
4. 搭工程骨架:仓库结构、CI、Compose 一键启动、一个贯通「上传 → 解析 → 显示清单」的最小链路。