Files
new-api-key-usage/docs/design/members表字段设计.md
skycurtain e293a8e5a9 docs: R16 成员档案修改与空壳删除裁决入档
成员操作面扩为可改 name/remark、key_count=0 时硬删空壳;软删除仍是账本实体默认语义。
2026-10-11 22:41:43 +08:00

79 lines
4.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# members 表字段设计
> 数据模型新增表之一:成员实体——「成员」维度的锚点。key 归属(经 `api_keys.member_id` 指向本表)、成员视图的身份、按成员统计的分组键。裁决背景见 `docs/design/需求清单.md` §9「访问主体模型」。
| 项目 | 内容 |
|------|------|
| 状态 | 定稿(2026-10-01;R16 修订 2026-10-10) |
| 关联 | `docs/design/api_keys表字段设计.md` 3.9(归属列指向本表)· `docs/design/需求清单.md` §9(访问主体模型)· R16(档案可改、空壳删除) |
| 范围 | 列结构 + 语义 + 写入时机;令牌生成细节归技术稿;方言映射归数据访问层设计 |
| 字段数 | 7 = 1 主键 + 6 业务字段 |
---
## 1. 表定位
- **一行 = 一个分摊成员**
- 三个角色:`api_keys.member_id` 的归属目标;成员视图的访问主体(令牌校验命中本表行 + `status = active`);按成员统计的分组键
- 数据规模:个位数行
## 2. 字段清单
| 列 | 类型 | 值来源 | 说明 |
|----|------|--------|------|
| `id` | bigserial PK | 自增 | 不可改 |
| `name` | text UNIQUE | 管理面录入(可改) | 显示名——账目与视图中的署名;开户后可改(R16) |
| `remark` | text | 管理面录入(可改) | 备注(联系方式、是谁等;可空);开户后可改(R16) |
| `status` | text | 管理面启停 | `active` / `disabled`——停用即成员视图访问立即失效(令牌校验含状态判断);**不并入档案编辑** |
| `token_hash` | text | 管理面生成令牌时 | 成员视图的访问凭证(哈希存储,明文只在生成时展示一次);仅随重发更新 |
| `token_issued_at` | timestamptz | 同上 | 当前令牌的签发时间(审计线索);仅随重发更新 |
| `created_at` | timestamptz | 开户时 | 不可改 |
## 3. 决策记录(含理由)
### 3.1 令牌哈希存储
拖库不泄令牌(与 `api_keys.key_hash` 同思路);副作用是明文不可找回——丢了只能重发(`token_hash` + `token_issued_at` 一起更新),这正是「凭证事务经作者」的裁决语义。
### 3.2 令牌不过期(无 `token_expires_at`),且与 `sessions` 无关
- 令牌是**长期访问凭证**(access token,近 GitHub PAT 形态),不是「登录令牌」:没有会话语义——不过期、无登出、无「验证持久身份 → 换发短时会话」的过程,吊销与重发是唯一的生命周期操作,均经作者(管理面)
- **与 `sessions` 表零关系**:sessions 服务于管理面(作者)的登录体系(密码验证 → 短时会话);本表令牌是成员链路唯一的凭证,成员请求直接携带、服务端直查本表(`token_hash` + `status`)——两条链路各自独立、物理隔离
- 成员低频只读,过期只会制造无意义的重发摩擦
### 3.3 `name` UNIQUE
账目署名不可歧义。
### 3.4 软删除(`status`)为主,空壳例外硬删(R16)
成员名下的 key 与历史用量是账本的一部分——「退拼」是停用(保留行、停访问),不是抹掉事实。**默认无物理删除**。
**空壳例外(R16)**:当前 `key_count = 0`(无任何 `api_keys.member_id` 指向本行)时允许硬删——该行不承载账本事实,删了只清掉误建/未使用的开户壳,并释放 UNIQUE 名称。名下有 key 一律拒绝删除(只能停用)。硬删与软删除不冲突:账本实体的默认语义仍是软删除,空壳删是「无账本可保」时的清理通道。
### 3.5 档案字段可改(`name` / `remark`)(R16)
- **动机**:开户后挂 key 前发现名称不对(误建、占位名)——已发出令牌时「改名」优于「删了重建」(重建会作废旧令牌)
- **可改字段收窄**:仅 `name` / `remark`;`status` / 令牌 / `id` / `created_at` 各走各的生命周期,不并入档案编辑
- **`name` 仍 UNIQUE**(3.3 不变);冲突 `409 member_name_taken`
- **展示口径 = 当前名**:日志 / 总览 / 成员视图联查实时取 `members.name`(与 `api_keys.token_name` 的「当前名为准」同则)——历史署名随改名漂移,可接受
## 4. 写入时机
| 时机 | 动作 |
|------|------|
| 开户(管理面) | INSERT(`name` / `remark`;生成令牌 → `token_hash` + `token_issued_at`) |
| 档案修改(R16) | UPDATE `name` 和/或 `remark` |
| 停用 / 启用 | UPDATE `status` |
| 重发令牌 | UPDATE `token_hash` + `token_issued_at`(旧令牌即失效) |
| 空壳删除(R16) | DELETE(仅 `key_count = 0`;名下有 key 拒绝) |
以上全部为管理面写操作,均落**审计日志**(审计为独立需求,表结构归技术稿)。
## 5. 待定事项
| 事项 | 归属 |
|------|------|
| 令牌生成的随机源与长度(256bit 起步) | 技术稿 |
| 审计日志表结构 | 技术稿 |