CREATE TABLE user_verifications ( id BIGINT PRIMARY KEY, user_id BIGINT NOT NULL REFERENCES users (id), role VARCHAR(20) NOT NULL, status VARCHAR(20) NOT NULL DEFAULT 'pending', materials JSONB NOT NULL, reject_reason TEXT, reviewed_by BIGINT, reviewed_at TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), CONSTRAINT chk_verification_status CHECK (status IN ('pending', 'approved', 'rejected')), UNIQUE (user_id, role) -- 每个角色只有一条最新的认证记录 ); -- Note:Auto update verification_status of users table CREATE OR REPLACE FUNCTION sync_user_verification_status() RETURNS TRIGGER AS $$ DECLARE status_json JSONB; BEGIN SELECT jsonb_object_agg(role, status) INTO status_json FROM user_verifications WHERE user_id = NEW.user_id; UPDATE users SET verification_status = status_json WHERE id = NEW.user_id; RETURN NEW; END; $$ LANGUAGE plpgsql; CREATE TRIGGER trigger_sync_verifications AFTER INSERT OR UPDATE ON user_verifications FOR EACH ROW EXECUTE FUNCTION sync_user_verification_status(); -- | 字段名 | 类型 | 技术含义 | 业务目的与设计思考 | -- | :--- | :--- | :--- | :--- | -- | **`id`** | BIGINT | **主键 (Primary Key)** | **这张认证申请单的唯一编号**。
后端代码在处理“审核通过”这个动作时,操作的是这个 ID(例如:`Approve(verificationId)`)。 | -- | **`user_id`** | BIGINT | **外键 (Foreign Key)** | **谁在申请?**
关联到 `users` 表。通过这个字段,我们可以查到申请人的昵称、手机号等信息。 | -- | **`role`** | VARCHAR | 普通字段 | **申请什么身份?**
枚举值:`player` (打手), `owner` (店长)。
因为一个用户可以同时是打手和店长,所以需要区分这条记录是申请哪个身份的。 | -- | **`status`** | VARCHAR | 状态字段 | **当前进度如何?**
枚举值:`pending` (待审核), `approved` (已通过), `rejected` (已驳回)。 | -- | **`materials`** | **JSONB** | **非结构化数据** | **提交了什么证明材料?**
**精华设计**:这里不使用多张表或多个字段,而是用 JSON 存。因为打手需要传“身份证+段位图”,店长需要传“营业执照”。不同角色的材料结构不同,用 JSONB 最灵活,以后改规则不需要改表结构。 | -- | **`reject_reason`** | TEXT | 文本 | **驳回理由**。
只有当 `status` = 'rejected' 时才有值,告诉用户哪里填错了。 | -- | **`reviewed_by`** | BIGINT | 审计字段 | **谁审核的?**
记录是哪个管理员(Admin ID)点击了通过或拒绝。用于内部追责和审计。 | -- | **`reviewed_at`** | TIMESTAMPTZ | 时间字段 | **什么时候审核的?**
用于统计管理员的工作效率,或者展示给用户“审核耗时”。 | -- | **`created_at`** | TIMESTAMPTZ | 时间字段 | **申请提交时间**。 | -- | **`updated_at`** | TIMESTAMPTZ | 时间字段 | **最后更新时间**。
比如用户重新上传了图片,这个时间会变。 |