Add TKMind platform extensions, H5/MindSpace stack, and deployment tooling.
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

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>
This commit is contained in:
john
2026-06-14 21:30:20 +08:00
parent e5fd568e01
commit 4e21ca937a
359 changed files with 70658 additions and 56 deletions
@@ -0,0 +1,354 @@
---
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`