按当前设计整理。核心 11 表 + 可选 2 表 + 注释 1 表。归属内联(
parent_id/owner_id),取消独立归属关系表;通讯录 3 表无主表,依托resources;created_at全删(Id自带时间序)。
一、ID 体系回顾
1.1 编码布局
text
i64(64 位):
bit 63 : 0(符号位,恒 0)
bit 62 : 0(时间压缩标记,恒 0;为 1 时启用压缩,暂不实现)
bit 61 ~ 48 : kind(14 位,16384 种)
bit 47 ~ 0 : time(48 位毫秒,约 8920 年)
1.2 IdKind
| kind | 值 | 含义 | 区分方式 |
|---|---|---|---|
User |
0x0000 |
PANGU + 用户 | time == 0 → PANGU;time > 0 → 普通用户 |
Terminal |
0x0001 |
终端 | --- |
Resource |
0x0002 |
资源实例 | --- |
ResourceKind |
0x0003 |
资源类型定义 | --- |
Group |
0x3FFF |
组 | --- |
1.3 PANGU 双重身份
text
PANGU 的 ID = 0(kind = User, time = 0)
硬编码部分:
- 用户名 / 密码常量
- 应急代码路径
- 不受数据库删除影响
登录部分:
- users 表 id = 0 的记录
- 可走正常登录流程
- 登录后是普通用户视角
区别只在 time == 0,登录能力 / 权限校验 / 审计与普通用户一致。
1.4 时间语义
next():max(now, last+1),接近真实时间add1():纯+1,只保证顺序,与真实时间漂移
创建时间在 ID 的 48 位 time 里,不需要独立 created_at。
二、主体层
2.1 users(用户)
sql
CREATE TABLE users (
id INTEGER PRIMARY KEY, -- Id { kind: User, time }
id_card TEXT NOT NULL UNIQUE, -- 身份证压缩码(属性,非主键)
name TEXT NOT NULL,
phone TEXT,
email TEXT,
status TEXT NOT NULL DEFAULT 'active' -- active / left / ...
);
说明:
- 用户 ID 是时间戳(注册时刻),身份证降为唯一属性
- 公共表,所有登录者
id = 0(PANGU)也在本表,走正常登录流程created_at删(ID 自带时间序)
2.2 groups(组)
sql
CREATE TABLE groups (
id INTEGER PRIMARY KEY, -- Id { kind: Group, time }
name TEXT NOT NULL,
owner_id INTEGER NOT NULL -- 组的事实拥有者(用户)
);
说明:
owner_id是组的事实拥有者(User类型)- 对人:组负责人
- 对资源:组内资源的事实 owner
- 组换 owner → 组内资源自动跟随
2.3 terminals(终端 + 资产)
sql
CREATE TABLE terminals (
id INTEGER PRIMARY KEY, -- Id { kind: Terminal, time }
owner_id INTEGER NOT NULL, -- 终端拥有人(用户,唯一)
fingerprint TEXT NOT NULL, -- 终端指纹(综合哈希)
name TEXT,
-- 资产信息(后续扩展,约 30 条)
mac_address TEXT, -- MAC 地址(指纹原料)
disk_serial TEXT, -- 硬盘序列号(指纹原料)
cpu_model TEXT, -- CPU 型号(指纹原料)
os_version TEXT, -- 操作系统版本
-- ... 其它资产字段按需补充
FOREIGN KEY (owner_id) REFERENCES users(id)
);
CREATE INDEX idx_terminals_fingerprint ON terminals(fingerprint);
CREATE INDEX idx_terminals_owner ON terminals(owner_id);
说明:
- 归属并入本表(
owner_id),取消独立terminal_owner表 - 一台终端一个 owner,不会出现第二个
- 指纹与资产核实统一:指纹匹配 = 关键资产未变
mac_address/disk_serial/cpu_model是指纹原料,也是资产配置
三、资源层
3.1 resource_kind_def(资源类型定义,初版)
sql
CREATE TABLE resource_kind_def (
id INTEGER PRIMARY KEY, -- Id { kind: ResourceKind, time }
name TEXT NOT NULL, -- "通讯录" / "图纸" / "流程"
decoder TEXT NOT NULL, -- 反序列化器标识(如 "contact_book_v1")
storage_mode TEXT NOT NULL, -- 'table' | 'bytes' | 'external'
schema TEXT -- 结构定义(JSON,可选)
);
说明:
- 资源类型是数据,新增插记录,不改代码
decoder是反序列化依据:客户端 / 服务器按此选择解码器- 未定稿,先按此实行,有问题调整
反序列化流程:
text
resources.resource_kind_id
→ resource_kind_def.decoder
→ 按 decoder 选择反序列化器
→ 例:"contact_book_v1" → ContactBook::decode
3.2 resources(资源实例元信息)
sql
CREATE TABLE resources (
id INTEGER PRIMARY KEY, -- Id { kind: Resource, time }
resource_kind_id INTEGER NOT NULL, -- 指向 resource_kind_def
name TEXT NOT NULL,
parent_id INTEGER NOT NULL, -- 唯一拥有者:组 ID 或用户 ID
content_path TEXT, -- 内容文件路径(为空时数据库内部保存)
version INTEGER NOT NULL, -- 最后修改版本(48 位 time)
created_by INTEGER NOT NULL, -- 创建者(用户)
FOREIGN KEY (resource_kind_id) REFERENCES resource_kind_def(id)
);
CREATE INDEX idx_resources_kind ON resources(resource_kind_id);
CREATE INDEX idx_resources_parent ON resources(parent_id);
说明:
parent_id是唯一拥有者,kind 决定是组还是用户:parent_id.kind == Group→ 资源归组parent_id.kind == User→ 资源直接归用户
- 取消
resource_edges表,归属直接放parent_id content_path为空 → 内容存数据库内部(拆表 / bytes);非空 → 存外部文件version保留(最后修改时刻),created_by保留(创建者)created_at删(ID 自带时间序)
3.3 values(通用字节存储,暂时注释)
sql
-- 暂时无需求,注释
-- CREATE TABLE values (
-- resource_id INTEGER PRIMARY KEY,
-- bytes BLOB NOT NULL,
-- FOREIGN KEY (resource_id) REFERENCES resources(id)
-- );
说明:
- 流程、审批、配置:可能用专表,可能外部文件
- 大文件:用
resources.content_path指向外部 - 通用字节:才用
values - 资源统一由
resources管理,存储方式由resource_kind_def.storage_mode决定
3.4 通讯录 3 表(无主表)
sql
-- 通讯录用户(关联公共 users)
CREATE TABLE contact_users (
resource_id INTEGER NOT NULL, -- 通讯录资源
user_id INTEGER NOT NULL, -- 引用公共 users.id
dept_id INTEGER, -- 在该通讯录里的部门(资源内 ID)
title TEXT, -- 在该通讯录里的岗位
sort_order INTEGER,
PRIMARY KEY (resource_id, user_id),
FOREIGN KEY (resource_id) REFERENCES resources(id),
FOREIGN KEY (user_id) REFERENCES users(id)
);
CREATE INDEX idx_contact_users_dept ON contact_users(resource_id, dept_id);
CREATE INDEX idx_contact_users_user ON contact_users(user_id);
-- 通讯录部门(资源内唯一)
CREATE TABLE contact_depts (
resource_id INTEGER NOT NULL,
dept_id INTEGER NOT NULL, -- 资源内唯一
name TEXT NOT NULL,
parent_id INTEGER, -- 资源内父部门
sort_order INTEGER,
PRIMARY KEY (resource_id, dept_id),
FOREIGN KEY (resource_id) REFERENCES resources(id)
);
CREATE INDEX idx_contact_depts_parent ON contact_depts(resource_id, parent_id);
-- 通讯录关系(资源内 ID)
CREATE TABLE contact_relations (
resource_id INTEGER NOT NULL,
from_id INTEGER NOT NULL, -- 资源内 ID(用户或部门)
to_id INTEGER NOT NULL,
relation_kind TEXT NOT NULL, -- 'member' | 'report' | ...
PRIMARY KEY (resource_id, from_id, to_id, relation_kind),
FOREIGN KEY (resource_id) REFERENCES resources(id)
);
CREATE INDEX idx_contact_relations_to ON contact_relations(resource_id, to_id);
说明:
- 无独立
contact_books主表 ,通讯录元信息在resources - 用户公共 :
contact_users.user_id外键users.id,不复制用户信息 - 部门 / 关系按资源隔离 :一张表 +
resource_id,不做物理分表 - 主键都以
resource_id打头 - "加入通讯录" = 在
contact_users插关联,前提是users已有该用户
四、关系层
4.1 org_edges(组织结构)
sql
CREATE TABLE org_edges (
from_id INTEGER NOT NULL, -- 用户或组
to_id INTEGER NOT NULL, -- 组
PRIMARY KEY (from_id, to_id)
);
CREATE INDEX idx_org_edges_to ON org_edges(to_id);
说明:
edge_kind去掉,靠from_id.kind推断:from_id.kind == User→ 用户属于组(membership)from_id.kind == Group→ 子组属于父组(hierarchy)
created_at删(边的顺序可由两端 ID 推断)- 未来需要"用户对用户"或"组对用户"的边,再考虑加
edge_kind
4.2 terminal_login(终端登录)
sql
CREATE TABLE terminal_login (
terminal_id INTEGER NOT NULL,
user_id INTEGER NOT NULL,
last_online_at INTEGER NOT NULL, -- 最后在线时刻
status TEXT NOT NULL DEFAULT 'offline', -- 'online' | 'offline'
PRIMARY KEY (terminal_id, user_id)
);
CREATE INDEX idx_terminal_login_status ON terminal_login(status, last_online_at);
说明:
- 用户 → 终端,登录状态 + 最后在线时间
- 保留,不并入
terminals:terminals:终端归属(一台一个 owner)terminal_login:登录记录(一台终端多人可登录)
last_online_at保留(动态时间,非创建时间)- 心跳 64×1024 ms ≈ 65.5 秒,窗口 1024×1024 ms ≈ 17.5 分钟
五、权限层
5.1 grants(组级权限)
sql
CREATE TABLE grants (
group_id INTEGER NOT NULL,
pc_id INTEGER NOT NULL, -- 终端 ID
can_read BOOLEAN NOT NULL DEFAULT 0,
can_write BOOLEAN NOT NULL DEFAULT 0,
PRIMARY KEY (group_id, pc_id)
);
CREATE INDEX idx_grants_pc ON grants(pc_id);
说明:
- 授权单位是组,组内资源继承组权限
granted_at删(授权时刻由audit_log承载)- 客户端权限表是此表的按
pc_id过滤子集
六、辅助层
6.1 audit_log(审计)
sql
CREATE TABLE audit_log (
id INTEGER PRIMARY KEY AUTOINCREMENT,
at INTEGER NOT NULL, -- 操作时刻(必须保留)
actor INTEGER NOT NULL, -- 操作人
op TEXT NOT NULL, -- insert / update / delete / transfer
target INTEGER NOT NULL, -- 目标资源
detail TEXT, -- 差异 / 说明
source TEXT -- 来源(流程单号等,可选)
);
CREATE INDEX idx_audit_at ON audit_log(at);
CREATE INDEX idx_audit_target ON audit_log(target);
说明:
- append-only,不可改
at必须保留(操作时刻,非创建时刻)- ID 是自增,不是
Id
6.2 transfer_chain(接替链)
sql
CREATE TABLE transfer_chain (
resource_id INTEGER NOT NULL,
seq INTEGER NOT NULL,
from_id INTEGER NOT NULL,
to_id INTEGER NOT NULL,
at INTEGER NOT NULL, -- 移交时刻(必须保留)
reason TEXT,
PRIMARY KEY (resource_id, seq)
);
说明:
- 资源所有权移交历史,链首是 PANGU
at必须保留(业务事件时间)
6.3 blocks(块,可选)
sql
-- 增量同步用,全量同步时可省略
CREATE TABLE blocks (
id INTEGER PRIMARY KEY, -- 块起点
end_time INTEGER NOT NULL, -- 块终点
resource_id INTEGER NOT NULL, -- 所属资源
changes BLOB NOT NULL, -- 净变化(JSON / bincode)
FOREIGN KEY (resource_id) REFERENCES resources(id)
);
CREATE INDEX idx_blocks_resource ON blocks(resource_id, end_time);
6.4 history_resources(历史,可选)
sql
-- 全量上传时旧数据入历史
CREATE TABLE history_resources (
resource_id INTEGER NOT NULL,
version INTEGER NOT NULL, -- 旧版本
bytes BLOB NOT NULL, -- 旧数据
archived_at INTEGER NOT NULL, -- 归档时刻(必须保留)
PRIMARY KEY (resource_id, version)
);
七、表清单汇总
| 层 | 表 | 状态 | 说明 |
|---|---|---|---|
| 主体 | users |
✅ | 用户(含 PANGU),身份证是属性 |
| 主体 | groups |
✅ | 组,owner_id 是事实拥有者 |
| 主体 | terminals |
✅ | 终端 + 资产 + owner_id |
| 资源 | resource_kind_def |
✅ | 类型定义,decoder 是反序列化依据 |
| 资源 | resources |
✅ | 实例元信息,parent_id 唯一拥有者 |
| 资源 | values |
⏸️ 注释 | 暂无需求 |
| 资源 | contact_users |
✅ | 通讯录用户(关联公共 users) |
| 资源 | contact_depts |
✅ | 通讯录部门(资源内唯一) |
| 资源 | contact_relations |
✅ | 通讯录关系(资源内 ID) |
| 关系 | org_edges |
✅ | 组织结构,edge_kind 由 from_id.kind 推断 |
| 关系 | terminal_login |
✅ | 终端登录 |
| 权限 | grants |
✅ | 组级权限 |
| 辅助 | audit_log |
✅ | 审计 |
| 辅助 | transfer_chain |
✅ | 接替链 |
| 辅助 | blocks |
⚠️ 可选 | 增量同步 |
| 辅助 | history_resources |
⚠️ 可选 | 历史版本 |
核心 11 表 + 可选 2 表 + 注释 1 表。
八、关键设计决策
8.1 归属内联
text
resources.parent_id → 组或用户
terminals.owner_id → 用户
groups.owner_id → 用户
取消 resource_edges / terminal_owner 表。
唯一拥有者用字段表达,不需要关系表。
8.2 通讯录无主表
text
resources 承载通讯录元信息(id / name / kind)
contact_users / contact_depts / contact_relations 存具体数据
3 表通过 resource_id 关联 resources
8.3 时间字段取舍
| 类型 | 处理 |
|---|---|
| 创建时间 | 删(ID 自带) |
事件时间(audit_log.at / transfer_chain.at) |
保留 |
动态时间(terminal_login.last_online_at) |
保留 |
最后修改版本(resources.version) |
保留 |
创建者(resources.created_by) |
保留 |
归档时刻(history_resources.archived_at) |
保留 |
8.4 关系表精简
text
org_edges:去掉 edge_kind,靠 from_id.kind 推断
terminal_login:保留(登录是多人多终端的关系,与归属不同)
8.5 用户公共
text
users:公共表,所有登录者(含 PANGU)
contact_users:关联表,不复制用户信息
展示时 JOIN
8.6 部门/关系按资源隔离
text
contact_depts / contact_relations:一张表 + resource_id
不做物理分表(表数量爆炸、DDL 复杂、跨资源查询难)
主键以 resource_id 打头
8.7 PANGU 双重身份
text
硬编码:用户名 / 密码常量、应急代码路径、不受数据库删除影响
登录:users 表 id = 0,可走正常登录流程
区别:只在 time == 0
九、数据流示例
9.1 读通讯录
text
pc 请求资源 R:
1. resources[R] → parent_id = G
2. G.kind == Group → groups[G].owner_id = A
3. grants[G][pc] → can_read?
4. can_read → 从 contact_* 表组装
5. 返回
9.2 写通讯录
text
pc 提交全量通讯录:
1. resources[R] → parent_id = G
2. grants[G][pc] → can_write?
3. 解析 JSON → 3 个 Vec
4. 清空 + 重插 contact_*
5. 更新 resources.version
9.3 终端归属解析
text
资源 R 的事实 owner:
1. resources[R].parent_id = P
2. P.kind == Group → groups[P].owner_id = A
3. P.kind == User → A = P
4. A 是用户 → 终点
pc 是 R 的 owner 终端?
1. terminals[pc].owner_id = A
2. A == R 的事实 owner → 是
9.4 终端指纹校验
text
pc 登录 / 心跳:
1. terminals[pc].fingerprint == 客户端上报的指纹?
2. 匹配 → 允许
3. 不匹配 → 拒绝(终端可能被替换)
9.5 PANGU 登录
text
PANGU 登录:
1. 客户端提交 admin / admin11(或改后的密码)
2. 走正常登录流程 → users[id = 0]
3. 登录成功,获得普通用户 token
4. 硬编码部分(应急代码)另行触发,与登录独立
十、一句话
服务器 11 张核心表:主体 3(users/groups/terminals)、资源 5(resource_kind_def/resources/contact_*)、关系 2(org_edges/terminal_login)、权限 1(grants)+ 辅助(audit_log/transfer_chain)+ 可选(blocks/history_resources)。归属内联(
parent_id/owner_id),通讯录无主表,created_at全删,edge_kind去掉。Human/Device统一改为User/Terminal,PANGU 可登录。归属内联,关系精简。用户公共,部门/关系按资源隔离。时间在 ID 里,事件在字段里。PANGU 能登录,
time=0是唯一区别。