Files
john 4e21ca937a
Deploy Documentation / deploy (push) Has been cancelled
Canary / Prepare Version (push) Has been cancelled
Canary / build-cli (push) Has been cancelled
Canary / Upload Install Script (push) Has been cancelled
Canary / bundle-desktop (push) Has been cancelled
Canary / bundle-desktop-intel (push) Has been cancelled
Canary / bundle-desktop-linux (push) Has been cancelled
Canary / bundle-desktop-windows (push) Has been cancelled
Canary / bundle-desktop-windows-cuda (push) Has been cancelled
Canary / Release (push) Has been cancelled
Unused Dependencies / machete (push) Has been cancelled
CI / changes (push) Has been cancelled
CI / Check Rust Code Format (push) Has been cancelled
CI / Build and Test Rust Project (push) Has been cancelled
CI / Build Rust Project on Windows (push) Has been cancelled
CI / Check MSRV (push) Has been cancelled
CI / Lint Rust Code (push) Has been cancelled
CI / Check Generated Schemas are Up-to-Date (push) Has been cancelled
CI / Test and Lint Electron Desktop App (push) Has been cancelled
CI / H5 Plaza Tests and Build (push) Has been cancelled
Live Provider Tests / check-fork (push) Has been cancelled
Live Provider Tests / changes (push) Has been cancelled
Live Provider Tests / Build Binary (push) Has been cancelled
Live Provider Tests / Smoke Tests (push) Has been cancelled
Live Provider Tests / Smoke Tests (Code Execution) (push) Has been cancelled
Live Provider Tests / Compaction Tests (push) Has been cancelled
Live Provider Tests / goose server HTTP integration tests (push) Has been cancelled
Publish Ask AI Bot Docker Image / docker (push) Has been cancelled
Publish Docker Image / docker (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
Add TKMind platform extensions, H5/MindSpace stack, and deployment tooling.
Fork goose with custom MCP widgets, platform extensions (aider, git, web, search),
MindSpace H5 backend/frontend, Plaza/Ops UIs, and deploy scripts for tkmind.cn.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-14 21:30:20 +08:00

355 lines
13 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.
---
sidebar_position: 3
title: 数据模型
sidebar_label: 数据模型
description: Plaza 所有数据库表的完整字段定义、索引策略、约束规则和迁移说明
---
# 数据模型
## 总览
Plaza 新增 **10 张业务表**`plaza_*` 前缀 + `ops_audit_log`),复用现有 `h5_users``h5_publish_records`,不创建新的用户体系。
**逻辑名与物理表映射**(与 MindSpace 文档一致):
| 文档逻辑名 | 物理表 |
| --- | --- |
| users | `h5_users` |
| publications | `h5_publish_records` |
**时间字段约定**:Plaza 新表与现有 H5 库一致,使用 `BIGINT` 毫秒 UTC`created_at` / `updated_at` / `published_at` 等),不使用 `DATETIME`
```text
h5_users (现有)
├──< plaza_posts 广场帖子(每条对应一个已发布的 publication
│ │
│ ├──< plaza_reactions 点赞 / 收藏 / 分享
│ ├──< plaza_comments 评论(支持二级回复)
│ └──< plaza_comment_reactions 评论点赞
└──< plaza_follows 关注关系(创作者 ↔ 粉丝)
plaza_categories 广场分类(独立配置表)
└──< plaza_posts
plaza_algorithm_config 热度公式权重(键值表)
plaza_reports 用户举报
plaza_featured 精选位配置
ops_audit_log 运营操作审计(追加-only)
```
---
## plaza_posts(广场帖子)
```sql
CREATE TABLE plaza_posts (
id CHAR(36) NOT NULL,
publication_id CHAR(36) NOT NULL, -- → h5_publish_records.id
user_id CHAR(36) NOT NULL, -- → h5_users.id
-- 快照字段(冗余,避免跨表 JOIN)
title VARCHAR(200) NOT NULL,
summary VARCHAR(500) NOT NULL DEFAULT '',
cover_url VARCHAR(500) NOT NULL DEFAULT '',
user_slug VARCHAR(100) NOT NULL, -- 快照,发帖时写入
user_display_name VARCHAR(100) NOT NULL,
user_avatar_url VARCHAR(500) NOT NULL DEFAULT '',
-- 分类和标签
category_id CHAR(36) NOT NULL, -- → plaza_categories.id
tags JSON NOT NULL DEFAULT (JSON_ARRAY()), -- 最多 5 个
-- 状态
status ENUM(
'pending_review', -- 待审核(默认)
'published', -- 审核通过,公开展示
'hidden', -- 被运营隐藏或 publication 下线
'rejected' -- 审核拒绝
) NOT NULL DEFAULT 'pending_review',
-- 互动计数(热计数用 Redis,定时同步到这里)
view_count INT UNSIGNED NOT NULL DEFAULT 0,
like_count INT UNSIGNED NOT NULL DEFAULT 0,
collect_count INT UNSIGNED NOT NULL DEFAULT 0,
comment_count INT UNSIGNED NOT NULL DEFAULT 0,
share_count INT UNSIGNED NOT NULL DEFAULT 0,
-- 热度
hot_score DOUBLE NOT NULL DEFAULT 0,
hot_updated_at BIGINT NULL,
-- 设置
allow_comment TINYINT(1) NOT NULL DEFAULT 1,
-- 时间(毫秒 UTC
published_at BIGINT NOT NULL,
created_at BIGINT NOT NULL,
updated_at BIGINT NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY uq_publication (publication_id), -- 每个 publication 只能发一次
KEY idx_user_status (user_id, status),
KEY idx_category_hot (category_id, status, hot_score DESC),
KEY idx_published_at (published_at DESC),
KEY idx_status_hot (status, hot_score DESC)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
```
**字段说明:**
- `publication_id` 唯一约束:同一个发布记录只能出现在广场一次,防止重复发布。
- 快照字段(`user_slug` 等)在发帖时写入,此后不随用户改名而变。如果用户改名,需要额外的更新任务,但不影响已有帖子的显示。
- `status = hidden` 覆盖两种场景:运营主动隐藏、publication 被原作者下线。
- `hot_score` 由后台定时任务计算写入,不在每次请求时实时计算。
---
## plaza_reactions(点赞 / 收藏 / 分享)
```sql
CREATE TABLE plaza_reactions (
id CHAR(36) NOT NULL,
post_id CHAR(36) NOT NULL, -- → plaza_posts.id
user_id CHAR(36) NOT NULL, -- → h5_users.id
type ENUM(
'like',
'collect',
'share'
) NOT NULL,
created_at BIGINT NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY uq_user_post_type (post_id, user_id, type), -- 同一用户同一帖子同类型只能有一条
KEY idx_post_type (post_id, type),
KEY idx_user_type (user_id, type)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
```
**触发器逻辑(应用层实现,不用数据库触发器):**
- 插入 reaction → Redis INCR `plaza:post:{id}:like_count`
- 删除 reaction(取消点赞)→ Redis DECR
- 定时任务每 5 分钟将 Redis 计数同步到 `plaza_posts` 对应字段
---
## plaza_comments(评论)
```sql
CREATE TABLE plaza_comments (
id CHAR(36) NOT NULL,
post_id CHAR(36) NOT NULL, -- → plaza_posts.id
user_id CHAR(36) NOT NULL, -- → h5_users.id
parent_id CHAR(36) NULL, -- 二级回复时指向父评论,NULL 表示一级评论
-- 内容
content VARCHAR(500) NOT NULL,
-- 状态
status ENUM(
'visible',
'deleted', -- 用户自己删除(内容替换为空,记录保留)
'flagged', -- 被举报,等待审核
'hidden' -- 运营隐藏
) NOT NULL DEFAULT 'visible',
-- 计数
like_count INT UNSIGNED NOT NULL DEFAULT 0,
reply_count INT UNSIGNED NOT NULL DEFAULT 0, -- 仅一级评论有效
-- 删除人标记
deleted_by ENUM('user', 'ops') NULL,
created_at BIGINT NOT NULL,
updated_at BIGINT NOT NULL,
PRIMARY KEY (id),
KEY idx_post_visible (post_id, status, created_at DESC),
KEY idx_parent (parent_id),
KEY idx_user (user_id, created_at DESC)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
```
**规则:**
- 只支持两级:一级评论(`parent_id IS NULL`)和二级回复(`parent_id` 指向一级)。
- 二级回复不再允许继续嵌套,前端也不展示更深的层级。
- 删除评论时不物理删除,将 `content` 设为空字符串,`status` 设为 `deleted`,保留 `reply_count` 用于展示"该评论已删除,有 N 条回复"。
- `post_id``comment_count` 只计算 `status = visible` 的一级评论数量。
---
## plaza_follows(关注关系)
```sql
CREATE TABLE plaza_follows (
follower_id CHAR(36) NOT NULL, -- 关注者 → h5_users.id
followee_id CHAR(36) NOT NULL, -- 被关注者 → h5_users.id
created_at BIGINT NOT NULL,
PRIMARY KEY (follower_id, followee_id),
KEY idx_followee (followee_id, created_at DESC)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
```
**说明:**
- 不允许自己关注自己(应用层校验:`follower_id != followee_id`)。
- `h5_users` 表新增冗余计数字段 `plaza_following_count``plaza_follower_count`,关注/取关时事务内更新。
- 取关是物理删除行。
---
## plaza_categories(广场分类)
```sql
CREATE TABLE plaza_categories (
id CHAR(36) NOT NULL,
name VARCHAR(50) NOT NULL,
slug VARCHAR(50) NOT NULL, -- URL 友好,如 work-report
icon VARCHAR(10) NOT NULL DEFAULT '', -- emoji 或图标代码
description VARCHAR(200) NOT NULL DEFAULT '',
sort_order INT NOT NULL DEFAULT 0,
is_active TINYINT(1) NOT NULL DEFAULT 1,
created_at BIGINT NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY uq_slug (slug),
KEY idx_sort (is_active, sort_order)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
```
**初始分类数据:**
| name | slug | icon | sort_order |
| --- | --- | --- | --- |
| 职场报告 | work-report | 📊 | 1 |
| 学习笔记 | study-notes | 📚 | 2 |
| 创意作品 | creative | 🎨 | 3 |
| 门店展示 | business | 🏪 | 4 |
| 旅行攻略 | travel | ✈️ | 5 |
| 数据分析 | data-analysis | 📈 | 6 |
| 生活记录 | lifestyle | 🌿 | 7 |
| 其他 | other | 💡 | 99 |
---
## plaza_comment_reactions(评论点赞)
```sql
CREATE TABLE plaza_comment_reactions (
id CHAR(36) NOT NULL,
comment_id CHAR(36) NOT NULL, -- → plaza_comments.id
user_id CHAR(36) NOT NULL, -- → h5_users.id
created_at BIGINT NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY uq_user_comment (comment_id, user_id),
KEY idx_comment (comment_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
```
- 点赞/取消点赞时同步更新 `plaza_comments.like_count`(事务内,不走 Redis)。
- 不提供独立 Redis 键;评论点赞量低于帖子级互动,直接写 MySQL 即可。
---
## plaza_algorithm_config(热度权重)
见 [05-feed-algorithm](./05-feed-algorithm) 中的建表 SQL。Sprint 3 引入。
---
## plaza_reports(举报)
见 [06-ops-platform](./06-ops-platform) 中的建表 SQL。Sprint 5 引入。
---
## plaza_featured(精选位)
见 [06-ops-platform](./06-ops-platform) 中的建表 SQL。Sprint 5 引入。
---
## ops_audit_log(运营审计)
见 [06-ops-platform](./06-ops-platform) 中的建表 SQL。Sprint 5 引入。
---
## h5_users / h5_publish_records 扩展字段
在现有表上通过 `db.mjs` 增量迁移添加:
```sql
ALTER TABLE h5_users
ADD COLUMN plaza_post_count INT UNSIGNED NOT NULL DEFAULT 0,
ADD COLUMN plaza_follower_count INT UNSIGNED NOT NULL DEFAULT 0,
ADD COLUMN plaza_following_count INT UNSIGNED NOT NULL DEFAULT 0,
ADD COLUMN plaza_verified TINYINT(1) NOT NULL DEFAULT 0,
ADD COLUMN plaza_post_banned TINYINT(1) NOT NULL DEFAULT 0,
ADD COLUMN plaza_comment_banned TINYINT(1) NOT NULL DEFAULT 0,
ADD COLUMN ops_role ENUM('none','reviewer','editor','ops_admin') NOT NULL DEFAULT 'none';
ALTER TABLE h5_publish_records
ADD COLUMN plaza_view_count BIGINT NOT NULL DEFAULT 0,
ADD COLUMN plaza_like_count BIGINT NOT NULL DEFAULT 0;
```
**`total_likes`(用户主页 API)**:不单独落库,查询时 `SUM(plaza_posts.like_count) WHERE user_id = ? AND status = 'published'`,或维护 `h5_users.plaza_total_likes` 冗余字段(Sprint 2 可选优化)。
---
## Redis 数据结构
```text
# 实时计数(定时同步到 MySQL)
plaza:post:{post_id}:view_count STRING INCR
plaza:post:{post_id}:like_count STRING INCR/DECR
# Feed 缓存(TTL 5 分钟)
plaza:feed:hot:{category_id}:page:{n} STRING JSON 数组,热门帖子 ID 列表
plaza:feed:new:{category_id}:page:{n} STRING JSON 数组,最新帖子 ID 列表
# 用户行为缓存(判断当前用户是否已点赞)
plaza:user:{user_id}:liked SET post_id 集合,TTL 30 分钟
```
---
## 索引策略
| 查询场景 | 使用索引 |
| --- | --- |
| 广场首页热门 feed | `idx_status_hot (status, hot_score DESC)` |
| 分类热门 feed | `idx_category_hot (category_id, status, hot_score DESC)` |
| 用户主页帖子列表 | `idx_user_status (user_id, status)` |
| 最新帖子列表 | `idx_published_at (published_at DESC)` |
| 帖子评论列表 | `idx_post_visible (post_id, status, created_at DESC)` |
| 用户关注列表 | `idx_followee (followee_id, created_at DESC)` |
---
## 迁移规范
- 每次 schema 变更:更新 `ui/h5/schema.sql`,并在 `db.mjs``migrateSchema()` 中做幂等 `ALTER`(与现有 MindSpace 迁移方式一致)。
- 可选归档文件命名:`ui/h5/migrations/V{n}__plaza_{description}.sql`(仅作审计,执行入口仍是 `initSchema`)。
- 新增字段必须有 DEFAULT 值,不允许直接在非空表上添加 NOT NULL 无默认值的列。
- `plaza_posts` 的快照字段(`user_slug` 等)在写入后不自动同步更新,需要独立的后台对账任务。
- 删除字段前先废弃读写代码,确认无流量后再执行 DDL。
---
## 不变量
- 每个 `publication_id``plaza_posts` 中最多出现一次(更新元数据走 `PATCH`,不换 publication)。
- `plaza_posts.status = hidden` 必须在 `h5_publish_records.status != 'online'` 时下线联动触发(同一事务)。
- `comment_count` 只统计 `status = visible` 的一级评论。
- 所有计数字段不允许出现负数(应用层保证 DECR 前先检查当前值)。
- `plaza_follows` 不允许 `follower_id = followee_id`