chore: 准备第三期迁移脚手架

This commit is contained in:
sunlei 2026-06-15 01:16:08 +08:00
parent 9db2e4bc5f
commit 3c3dc3a69f
13 changed files with 1153 additions and 38 deletions

View File

@ -0,0 +1,12 @@
# API/Admin Contract Matrix
| Batch | API Contract | Admin Surface | Smoke Evidence |
| --- | --- | --- | --- |
| 1 | `GET /health/runtime` plain JSON, runtime adapter internals | Runtime status remains available | `curl http://localhost:<port>/health/runtime` |
| 2 | `/auth/*`, `/admin/user/*`, `/admin/menu/*`, `/admin/role/*`, `/admin/dept/*`, `/admin/dict/*`, `/admin/notice/*` | Login, menu, system pages | Login request, menu load, route render |
| 3 | `/blog/*`, `/wordpress/*`, `/asset/*` | Blog, WordPress, Asset pages | Public blog request, Admin list request, asset upload smoke |
| 4 | `/qqbot/account/*`, `/qqbot/command/*`, `/qqbot/rule/*`, `/qqbot/message/*`, `/qqbot/send/*` | QQBot core pages | `/qqbot/command/test` local request |
| 5 | `/qqbot/plugin-platform/*` | Plugin upload/install/enable/config/health pages | local test plugin install and enable |
| 6 | plugin operations exposed through QQBot command/event routing | Existing plugin pages and operation views | BangDream, FF14, FFLogs, Repeater smoke |
| 7 | `/qqbot/napcat/*`, login SSE events | NapCat device/login progress pages | simulated captcha and new-device session |
| 8 | public deployed URLs | deployed Admin | online smoke bundle |

View File

@ -0,0 +1,20 @@
# Refactor V3 Breaking Changes
## Approved
| Area | Change | Reason | First Batch |
| --- | --- | --- | --- |
| Database | Rebuild all API-owned tables from new full schema | Current data is not important and old schema blocks clean module ownership | Batch 0 |
| SQL init | Replace historical patch scripts with `sql/refactor-v3/*` | Avoid accumulated `ALTER TABLE` history | Batch 0 |
| Admin QQBot API types | Replace old QQBot caller types with new contract | New QQBot Core, Plugin Platform, and NapCat state model | Batch 4 |
## Protected Behavior
- Admin login succeeds.
- Admin menu loads.
- Vben success/error wrappers remain stable for Admin APIs.
- `/health/runtime` remains plain JSON.
- Blog public list/detail remain available.
- QQBot command test remains available.
- QQBot status keeps OneBot, container, WebUI, and QQ login state separate.
- NapCat cleanup failure blocks success.

View File

@ -0,0 +1,31 @@
# Refactor V3 Rebuild Runbook
## Local Dry Run
1. Create an empty local database dedicated to refactor V3.
2. Apply `sql/refactor-v3/00-full-schema.sql`.
3. Apply `sql/refactor-v3/01-seed-core.sql`.
4. Run `sql/refactor-v3/99-verify.sql`.
5. Start API against the dry-run database.
6. Run `scripts/refactor-v3/local-smoke.ps1`.
## Online Backup
1. Confirm the exact API image tag and current database name.
2. Run `scripts/refactor-v3/db-backup-online.ps1`.
3. Record backup path, timestamp, source database, and restore command.
## Online Rebuild
1. Stop or limit API write traffic.
2. Apply full schema and seed scripts.
3. Run verify SQL.
4. Deploy API/Admin image versions bound to this schema.
5. Run online smoke bundle.
## Rollback
1. Stop write traffic.
2. Restore the recorded backup or previous schema bundle.
3. Roll back API/Admin images.
4. Re-run smoke for the restored version.

View File

@ -0,0 +1,25 @@
# Refactor V3 Schema Map
## Global Rules
- Primary keys are Snowflake `BIGINT`; API/Admin boundary treats IDs as strings.
- Table names use lower snake case.
- Queryable values are structured columns, not JSON-only fields.
- Event/log tables are append-only and have retention strategy.
- New schema is full initialization SQL, not historical `ALTER TABLE` patches.
## Domains
| Domain | Tables | Owner Batch | Notes |
| --- | --- | --- | --- |
| Admin Identity | `admin_user`, `admin_role`, `admin_permission`, `admin_menu`, `admin_department`, `admin_user_role`, `admin_role_permission`, `admin_role_menu` | Batch 2 | Login, menu, role, permission, department. |
| Platform Config | `platform_dict_group`, `platform_dict_item`, `platform_component_template`, `platform_setting` | Batch 2 | Dict and component template split from legacy Admin misc. |
| Blog Content | `blog_post`, `blog_taxonomy`, `blog_term`, `blog_post_term`, `blog_theme_profile`, `blog_import_job` | Batch 3 | Categories and tags use relation table. |
| WordPress Mirror | `wordpress_site`, `wordpress_auth_session`, `wordpress_remote_post`, `wordpress_remote_term`, `wordpress_sync_job`, `wordpress_sync_mapping` | Batch 3 | Remote state separate from local Blog content. |
| Asset | `asset_bucket`, `asset_object`, `asset_reference`, `asset_access_grant` | Batch 3 | MinIO object ownership and access grant. |
| System Event | `system_notice`, `system_event`, `system_event_dedupe`, `system_event_delivery` | Batch 2 | MySQL stores actionable events; Loki remains log query source. |
| Runtime Evidence | `runtime_evidence_index` | Batch 1 | Safe index only, no large logs or secrets. |
| QQBot Core | `qqbot_account`, `qqbot_connection_session`, `qqbot_capability_binding`, `qqbot_permission_policy`, `qqbot_command`, `qqbot_command_alias`, `qqbot_rule`, `qqbot_conversation`, `qqbot_message`, `qqbot_send_task`, `qqbot_send_log`, `qqbot_dedupe_event` | Batch 4 | Account, connection, permission, command, message, send queue. |
| NapCat Runtime | `napcat_container`, `napcat_device_identity`, `napcat_account_binding`, `napcat_login_session`, `napcat_login_challenge`, `napcat_runtime_cleanup` | Batch 7 | Device identity and login challenge state. |
| QQBot Plugin Platform | `qqbot_plugin`, `qqbot_plugin_version`, `qqbot_plugin_installation`, `qqbot_plugin_operation`, `qqbot_plugin_event_handler`, `qqbot_plugin_account_binding`, `qqbot_plugin_config`, `qqbot_plugin_asset`, `qqbot_plugin_runtime_event` | Batch 5 | Manifest, install lifecycle, runtime health, bindings. |
| Plugin-Owned Data | plugin namespace tables | Batch 6 | Table names start with registered plugin namespace. |

View File

@ -18,8 +18,8 @@ Every batch must leave the product in a locally verifiable state. Batch 8 is the
## Current State Evidence
- API repo: `D:\MyFiles\KT\Node\kt-template-online-api`, branch `main`, clean at plan start, package manager `pnpm@9.15.9`, no `.node-version`, no `engines`.
- Admin repo: `D:\MyFiles\KT\Vue\kt-template-admin`, branch `main`, package manager `pnpm@10.28.2`, `.node-version=22.22.0`, `engines.node >=20.19.0`, `engines.pnpm >=10.0.0`.
- API repo: `D:\MyFiles\KT\Node\kt-template-online-api`, implementation branch `dev-api-full-refactor-v3`, package manager `pnpm@9.15.9`, no `.node-version`, no `engines`.
- Admin repo: `D:\MyFiles\KT\Vue\kt-template-admin`, implementation branch `dev-admin-full-refactor-v3`, package manager `pnpm@10.28.2`, `.node-version=22.22.0`, `engines.node >=20.19.0`, `engines.pnpm >=10.0.0`.
- Admin repo has three old dirty files authorized by the user to discard during implementation preparation:
- `README.md`
- `apps/web-antdv-next/src/api/qqbot/index.ts`
@ -106,7 +106,7 @@ Every batch must leave the product in a locally verifiable state. Batch 8 is the
**Files:**
- No file edits in this task.
- [ ] **Step 1: Verify API worktree is clean**
- [x] **Step 1: Verify API worktree is clean**
Run:
@ -120,21 +120,21 @@ Expected:
## main...origin/main [ahead 3]
```
- [ ] **Step 2: Create API implementation branch**
- [x] **Step 2: Create API implementation branch**
Run:
```powershell
git -C D:\MyFiles\KT\Node\kt-template-online-api switch -c dev/api-full-refactor-v3
git -C D:\MyFiles\KT\Node\kt-template-online-api switch -c dev-api-full-refactor-v3
```
Expected:
```text
Switched to a new branch 'dev/api-full-refactor-v3'
Switched to a new branch 'dev-api-full-refactor-v3'
```
- [ ] **Step 3: Confirm branch**
- [x] **Step 3: Confirm branch**
Run:
@ -145,9 +145,11 @@ git -C D:\MyFiles\KT\Node\kt-template-online-api branch --show-current
Expected:
```text
dev/api-full-refactor-v3
dev-api-full-refactor-v3
```
Actual note: `dev/api-full-refactor-v3` could not be created because the API repository already has a local `dev` branch, so Git cannot create a nested ref under `refs/heads/dev`. The implementation branch is `dev-api-full-refactor-v3` to preserve the existing `dev` branch.
### Task 0.2: Clean authorized Admin old artifacts and prepare branch
**Files:**
@ -156,7 +158,7 @@ dev/api-full-refactor-v3
- `D:\MyFiles\KT\Vue\kt-template-admin\apps\web-antdv-next\src\api\qqbot\index.ts`
- `D:\MyFiles\KT\Vue\kt-template-admin\apps\web-antdv-next\src\views\qqbot\account\list.tsx`
- [ ] **Step 1: Verify Admin dirty set is exactly authorized**
- [x] **Step 1: Verify Admin dirty set is exactly authorized**
Run:
@ -172,11 +174,11 @@ Expected:
M apps/web-antdv-next/src/views/qqbot/account/list.tsx
```
- [ ] **Step 2: Stop if any extra path appears**
- [x] **Step 2: Stop if any extra path appears**
If the output contains another path, stop and ask the user before cleanup.
- [ ] **Step 3: Restore authorized old files**
- [x] **Step 3: Restore authorized old files**
Run:
@ -186,7 +188,7 @@ git -C D:\MyFiles\KT\Vue\kt-template-admin restore -- README.md apps/web-antdv-n
Expected: command exits `0`.
- [ ] **Step 4: Confirm Admin worktree is clean**
- [x] **Step 4: Confirm Admin worktree is clean**
Run:
@ -200,18 +202,18 @@ Expected:
## main...origin/main
```
- [ ] **Step 5: Create Admin implementation branch**
- [x] **Step 5: Create Admin implementation branch**
Run:
```powershell
git -C D:\MyFiles\KT\Vue\kt-template-admin switch -c dev/admin-full-refactor-v3
git -C D:\MyFiles\KT\Vue\kt-template-admin switch -c dev-admin-full-refactor-v3
```
Expected:
```text
Switched to a new branch 'dev/admin-full-refactor-v3'
Switched to a new branch 'dev-admin-full-refactor-v3'
```
### Task 0.3: Create migration control documents
@ -222,7 +224,7 @@ Switched to a new branch 'dev/admin-full-refactor-v3'
- Create: `D:\MyFiles\KT\Node\kt-template-online-api\docs\refactor-v3\breaking-changes.md`
- Create: `D:\MyFiles\KT\Node\kt-template-online-api\docs\refactor-v3\rebuild-runbook.md`
- [ ] **Step 1: Add `schema-map.md`**
- [x] **Step 1: Add `schema-map.md`**
Use this content:
@ -254,7 +256,7 @@ Use this content:
| Plugin-Owned Data | plugin namespace tables | Batch 6 | Table names start with registered plugin namespace. |
```
- [ ] **Step 2: Add `api-admin-contract-matrix.md`**
- [x] **Step 2: Add `api-admin-contract-matrix.md`**
Use this content:
@ -273,7 +275,7 @@ Use this content:
| 8 | public deployed URLs | deployed Admin | online smoke bundle |
```
- [ ] **Step 3: Add `breaking-changes.md`**
- [x] **Step 3: Add `breaking-changes.md`**
Use this content:
@ -300,7 +302,7 @@ Use this content:
- NapCat cleanup failure blocks success.
```
- [ ] **Step 4: Add `rebuild-runbook.md`**
- [x] **Step 4: Add `rebuild-runbook.md`**
Use this content:
@ -338,7 +340,7 @@ Use this content:
4. Re-run smoke for the restored version.
```
- [ ] **Step 5: Verify docs exist**
- [x] **Step 5: Verify docs exist**
Run:
@ -366,7 +368,7 @@ True
- Create: `D:\MyFiles\KT\Node\kt-template-online-api\sql\refactor-v3\99-verify.sql`
- Create: `D:\MyFiles\KT\Node\kt-template-online-api\test\refactor-v3\schema-map.spec.ts`
- [ ] **Step 1: Write failing schema test**
- [x] **Step 1: Write failing schema test**
Create `test/refactor-v3/schema-map.spec.ts`:
@ -425,15 +427,17 @@ describe('refactor v3 schema skeleton', () => {
);
expect(seed).toContain('INSERT INTO admin_user');
expect(seed).toContain('INSERT INTO qqbot_command');
expect(seed).toContain('INSERT INTO qqbot_plugin');
expect(verify).toContain('admin_user');
expect(verify).toContain('qqbot_command');
expect(verify).toContain('qqbot_plugin');
expect(verify).toContain('napcat_device_identity');
});
});
```
- [ ] **Step 2: Run RED check**
- [x] **Step 2: Run RED check**
Run:
@ -443,11 +447,11 @@ pnpm --dir D:\MyFiles\KT\Node\kt-template-online-api exec jest --runInBand --run
Expected: FAIL because `sql/refactor-v3/00-full-schema.sql` does not exist.
- [ ] **Step 3: Add minimal SQL skeleton**
- [x] **Step 3: Add minimal SQL skeleton**
Create the three SQL files with table declarations, seed markers, and verify markers. Use full table definitions before ending Batch 0; the initial minimal version only makes the RED check meaningful.
- [ ] **Step 4: Run GREEN check**
- [x] **Step 4: Run GREEN check**
Run:
@ -465,7 +469,7 @@ Expected: PASS.
- Create: `D:\MyFiles\KT\Node\kt-template-online-api\scripts\refactor-v3\db-restore-online.ps1`
- Create: `D:\MyFiles\KT\Node\kt-template-online-api\scripts\refactor-v3\local-smoke.ps1`
- [ ] **Step 1: Add dry-run script**
- [x] **Step 1: Add dry-run script**
Create `db-dry-run.ps1`:
@ -478,18 +482,31 @@ param(
)
$ErrorActionPreference = "Stop"
if ($Database -notmatch '^[A-Za-z0-9_]+$') {
throw "Database must match ^[A-Za-z0-9_]+$"
}
$root = Resolve-Path (Join-Path $PSScriptRoot "..\..")
$schema = Join-Path $root "sql\refactor-v3\00-full-schema.sql"
$seed = Join-Path $root "sql\refactor-v3\01-seed-core.sql"
$verify = Join-Path $root "sql\refactor-v3\99-verify.sql"
function Invoke-MysqlSource {
param(
[Parameter(Mandatory = $true)][string]$Path
)
$sourcePath = (Resolve-Path -LiteralPath $Path).Path.Replace("\", "/")
mysql -h $HostName -P $Port -u $User $Database --execute="source $sourcePath"
}
mysql -h $HostName -P $Port -u $User -e "DROP DATABASE IF EXISTS ``$Database``; CREATE DATABASE ``$Database`` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
mysql -h $HostName -P $Port -u $User $Database < $schema
mysql -h $HostName -P $Port -u $User $Database < $seed
mysql -h $HostName -P $Port -u $User $Database < $verify
Invoke-MysqlSource -Path $schema
Invoke-MysqlSource -Path $seed
Invoke-MysqlSource -Path $verify
```
- [ ] **Step 2: Add online backup script**
- [x] **Step 2: Add online backup script**
Create `db-backup-online.ps1`:
@ -500,13 +517,17 @@ param(
)
$ErrorActionPreference = "Stop"
if ($Database -notmatch '^[A-Za-z0-9_]+$') {
throw "Database must match ^[A-Za-z0-9_]+$"
}
$stamp = Get-Date -Format "yyyyMMdd-HHmmss"
$target = Join-Path $OutputDirectory "$Database-refactor-v3-$stamp.sql"
mysqldump --set-gtid-purged=OFF --single-transaction --routines --triggers $Database | Out-File -Encoding utf8 $target
mysqldump --set-gtid-purged=OFF --single-transaction --routines --triggers --default-character-set=utf8mb4 "--result-file=$target" $Database
Write-Output $target
```
- [ ] **Step 3: Add restore script**
- [x] **Step 3: Add restore script**
Create `db-restore-online.ps1`:
@ -517,11 +538,19 @@ param(
)
$ErrorActionPreference = "Stop"
if ($Database -notmatch '^[A-Za-z0-9_]+$') {
throw "Database must match ^[A-Za-z0-9_]+$"
}
if (-not (Test-Path -LiteralPath $BackupFile -PathType Leaf)) {
throw "BackupFile does not exist"
}
$sourcePath = (Resolve-Path -LiteralPath $BackupFile).Path.Replace("\", "/")
mysql -e "DROP DATABASE IF EXISTS ``$Database``; CREATE DATABASE ``$Database`` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
mysql $Database < $BackupFile
mysql --default-character-set=utf8mb4 $Database --execute="source $sourcePath"
```
- [ ] **Step 4: Add local smoke script**
- [x] **Step 4: Add local smoke script**
Create `local-smoke.ps1`:
@ -538,13 +567,15 @@ if (-not $runtime.service) {
Write-Output "runtime.service=$($runtime.service)"
```
- [ ] **Step 5: Validate scripts parse**
- [x] **Step 5: Validate scripts parse**
Run:
```powershell
powershell -NoProfile -Command "$null = [scriptblock]::Create((Get-Content -Raw D:\MyFiles\KT\Node\kt-template-online-api\scripts\refactor-v3\db-dry-run.ps1)); 'ok'"
powershell -NoProfile -Command "$null = [scriptblock]::Create((Get-Content -Raw D:\MyFiles\KT\Node\kt-template-online-api\scripts\refactor-v3\local-smoke.ps1)); 'ok'"
powershell -NoProfile -Command '$null = [scriptblock]::Create((Get-Content -Raw -LiteralPath "D:\MyFiles\KT\Node\kt-template-online-api\scripts\refactor-v3\db-dry-run.ps1")); "ok"'
powershell -NoProfile -Command '$null = [scriptblock]::Create((Get-Content -Raw -LiteralPath "D:\MyFiles\KT\Node\kt-template-online-api\scripts\refactor-v3\local-smoke.ps1")); "ok"'
powershell -NoProfile -Command '$null = [scriptblock]::Create((Get-Content -Raw -LiteralPath "D:\MyFiles\KT\Node\kt-template-online-api\scripts\refactor-v3\db-backup-online.ps1")); "ok"'
powershell -NoProfile -Command '$null = [scriptblock]::Create((Get-Content -Raw -LiteralPath "D:\MyFiles\KT\Node\kt-template-online-api\scripts\refactor-v3\db-restore-online.ps1")); "ok"'
```
Expected:
@ -552,6 +583,8 @@ Expected:
```text
ok
ok
ok
ok
```
### Task 0.6: Batch 0 verification and commits
@ -1373,8 +1406,8 @@ After confirmation, push both repos.
Run:
```powershell
git -C D:\MyFiles\KT\Node\kt-template-online-api push -u origin dev/api-full-refactor-v3
git -C D:\MyFiles\KT\Vue\kt-template-admin push -u origin dev/admin-full-refactor-v3
git -C D:\MyFiles\KT\Node\kt-template-online-api push -u origin dev-api-full-refactor-v3
git -C D:\MyFiles\KT\Vue\kt-template-admin push -u origin dev-admin-full-refactor-v3
```
Then collect Jenkins/K8s evidence:

View File

@ -0,0 +1,14 @@
param(
[Parameter(Mandatory = $true)][string]$Database,
[Parameter(Mandatory = $true)][string]$OutputDirectory
)
$ErrorActionPreference = "Stop"
if ($Database -notmatch '^[A-Za-z0-9_]+$') {
throw "Database must match ^[A-Za-z0-9_]+$"
}
$stamp = Get-Date -Format "yyyyMMdd-HHmmss"
$target = Join-Path $OutputDirectory "$Database-refactor-v3-$stamp.sql"
mysqldump --set-gtid-purged=OFF --single-transaction --routines --triggers --default-character-set=utf8mb4 "--result-file=$target" $Database
Write-Output $target

View File

@ -0,0 +1,30 @@
param(
[Parameter(Mandatory = $true)][string]$Database,
[string]$HostName = "127.0.0.1",
[int]$Port = 3306,
[string]$User = "root"
)
$ErrorActionPreference = "Stop"
if ($Database -notmatch '^[A-Za-z0-9_]+$') {
throw "Database must match ^[A-Za-z0-9_]+$"
}
$root = Resolve-Path (Join-Path $PSScriptRoot "..\..")
$schema = Join-Path $root "sql\refactor-v3\00-full-schema.sql"
$seed = Join-Path $root "sql\refactor-v3\01-seed-core.sql"
$verify = Join-Path $root "sql\refactor-v3\99-verify.sql"
function Invoke-MysqlSource {
param(
[Parameter(Mandatory = $true)][string]$Path
)
$sourcePath = (Resolve-Path -LiteralPath $Path).Path.Replace("\", "/")
mysql -h $HostName -P $Port -u $User $Database --execute="source $sourcePath"
}
mysql -h $HostName -P $Port -u $User -e "DROP DATABASE IF EXISTS ``$Database``; CREATE DATABASE ``$Database`` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
Invoke-MysqlSource -Path $schema
Invoke-MysqlSource -Path $seed
Invoke-MysqlSource -Path $verify

View File

@ -0,0 +1,16 @@
param(
[Parameter(Mandatory = $true)][string]$Database,
[Parameter(Mandatory = $true)][string]$BackupFile
)
$ErrorActionPreference = "Stop"
if ($Database -notmatch '^[A-Za-z0-9_]+$') {
throw "Database must match ^[A-Za-z0-9_]+$"
}
if (-not (Test-Path -LiteralPath $BackupFile -PathType Leaf)) {
throw "BackupFile does not exist"
}
$sourcePath = (Resolve-Path -LiteralPath $BackupFile).Path.Replace("\", "/")
mysql -e "DROP DATABASE IF EXISTS ``$Database``; CREATE DATABASE ``$Database`` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
mysql --default-character-set=utf8mb4 $Database --execute="source $sourcePath"

View File

@ -0,0 +1,10 @@
param(
[string]$BaseUrl = "http://127.0.0.1:5320"
)
$ErrorActionPreference = "Stop"
$runtime = Invoke-RestMethod -Method Get -Uri "$BaseUrl/health/runtime" -TimeoutSec 10
if (-not $runtime.service) {
throw "Runtime health response did not include service"
}
Write-Output "runtime.service=$($runtime.service)"

View File

@ -0,0 +1,677 @@
CREATE TABLE IF NOT EXISTS admin_user (
id BIGINT NOT NULL PRIMARY KEY,
username VARCHAR(64) NOT NULL,
password_hash VARCHAR(255) NOT NULL,
nickname VARCHAR(64) NOT NULL,
status VARCHAR(32) NOT NULL,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_admin_user_username (username)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS admin_role (
id BIGINT NOT NULL PRIMARY KEY,
role_key VARCHAR(64) NOT NULL,
role_name VARCHAR(64) NOT NULL,
status VARCHAR(32) NOT NULL,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_admin_role_key (role_key)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS admin_permission (
id BIGINT NOT NULL PRIMARY KEY,
permission_key VARCHAR(128) NOT NULL,
permission_name VARCHAR(128) NOT NULL,
module_key VARCHAR(64) NOT NULL,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_admin_permission_key (permission_key)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS admin_menu (
id BIGINT NOT NULL PRIMARY KEY,
parent_id BIGINT NULL,
menu_key VARCHAR(128) NOT NULL,
title VARCHAR(128) NOT NULL,
path VARCHAR(255) NOT NULL,
component VARCHAR(255) NULL,
sort_no INT NOT NULL DEFAULT 0,
status VARCHAR(32) NOT NULL,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_admin_menu_key (menu_key),
KEY idx_admin_menu_parent (parent_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS admin_department (
id BIGINT NOT NULL PRIMARY KEY,
parent_id BIGINT NULL,
dept_name VARCHAR(128) NOT NULL,
sort_no INT NOT NULL DEFAULT 0,
status VARCHAR(32) NOT NULL,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
KEY idx_admin_department_parent (parent_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS admin_user_role (
id BIGINT NOT NULL PRIMARY KEY,
user_id BIGINT NOT NULL,
role_id BIGINT NOT NULL,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY uk_admin_user_role (user_id, role_id),
KEY idx_admin_user_role_role (role_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS admin_role_permission (
id BIGINT NOT NULL PRIMARY KEY,
role_id BIGINT NOT NULL,
permission_id BIGINT NOT NULL,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY uk_admin_role_permission (role_id, permission_id),
KEY idx_admin_role_permission_permission (permission_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS admin_role_menu (
id BIGINT NOT NULL PRIMARY KEY,
role_id BIGINT NOT NULL,
menu_id BIGINT NOT NULL,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY uk_admin_role_menu (role_id, menu_id),
KEY idx_admin_role_menu_menu (menu_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS platform_dict_group (
id BIGINT NOT NULL PRIMARY KEY,
group_key VARCHAR(128) NOT NULL,
group_name VARCHAR(128) NOT NULL,
status VARCHAR(32) NOT NULL,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_platform_dict_group_key (group_key)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS platform_dict_item (
id BIGINT NOT NULL PRIMARY KEY,
group_id BIGINT NOT NULL,
item_key VARCHAR(128) NOT NULL,
item_label VARCHAR(128) NOT NULL,
item_value VARCHAR(255) NOT NULL,
sort_no INT NOT NULL DEFAULT 0,
status VARCHAR(32) NOT NULL,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_platform_dict_item (group_id, item_key),
KEY idx_platform_dict_item_group (group_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS platform_component_template (
id BIGINT NOT NULL PRIMARY KEY,
template_key VARCHAR(128) NOT NULL,
template_name VARCHAR(128) NOT NULL,
schema_json JSON NULL,
status VARCHAR(32) NOT NULL,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_platform_component_template_key (template_key)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS platform_setting (
id BIGINT NOT NULL PRIMARY KEY,
setting_key VARCHAR(128) NOT NULL,
setting_value TEXT NULL,
value_type VARCHAR(32) NOT NULL,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_platform_setting_key (setting_key)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS blog_post (
id BIGINT NOT NULL PRIMARY KEY,
slug VARCHAR(255) NOT NULL,
title VARCHAR(255) NOT NULL,
summary TEXT NULL,
content_markdown LONGTEXT NULL,
content_html LONGTEXT NULL,
status VARCHAR(32) NOT NULL,
publish_time DATETIME NULL,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_blog_post_slug (slug),
KEY idx_blog_post_status (status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS blog_taxonomy (
id BIGINT NOT NULL PRIMARY KEY,
taxonomy_key VARCHAR(64) NOT NULL,
taxonomy_name VARCHAR(128) NOT NULL,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_blog_taxonomy_key (taxonomy_key)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS blog_term (
id BIGINT NOT NULL PRIMARY KEY,
taxonomy_id BIGINT NOT NULL,
slug VARCHAR(255) NOT NULL,
term_name VARCHAR(128) NOT NULL,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_blog_term_slug (taxonomy_id, slug),
KEY idx_blog_term_taxonomy (taxonomy_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS blog_post_term (
id BIGINT NOT NULL PRIMARY KEY,
post_id BIGINT NOT NULL,
term_id BIGINT NOT NULL,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY uk_blog_post_term (post_id, term_id),
KEY idx_blog_post_term_term (term_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS blog_theme_profile (
id BIGINT NOT NULL PRIMARY KEY,
profile_key VARCHAR(128) NOT NULL,
profile_name VARCHAR(128) NOT NULL,
config_json JSON NOT NULL,
enabled TINYINT NOT NULL DEFAULT 0,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_blog_theme_profile_key (profile_key)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS blog_import_job (
id BIGINT NOT NULL PRIMARY KEY,
source_key VARCHAR(64) NOT NULL,
status VARCHAR(32) NOT NULL,
started_at DATETIME NULL,
finished_at DATETIME NULL,
summary_json JSON NULL,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
KEY idx_blog_import_job_status (status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS wordpress_site (
id BIGINT NOT NULL PRIMARY KEY,
site_key VARCHAR(128) NOT NULL,
base_url VARCHAR(512) NOT NULL,
status VARCHAR(32) NOT NULL,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_wordpress_site_key (site_key)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS wordpress_auth_session (
id BIGINT NOT NULL PRIMARY KEY,
site_id BIGINT NOT NULL,
status VARCHAR(32) NOT NULL,
expires_at DATETIME NULL,
safe_summary JSON NULL,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
KEY idx_wordpress_auth_session_site (site_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS wordpress_remote_post (
id BIGINT NOT NULL PRIMARY KEY,
site_id BIGINT NOT NULL,
remote_id VARCHAR(64) NOT NULL,
slug VARCHAR(255) NOT NULL,
status VARCHAR(32) NOT NULL,
raw_payload JSON NULL,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_wordpress_remote_post (site_id, remote_id),
KEY idx_wordpress_remote_post_slug (slug)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS wordpress_remote_term (
id BIGINT NOT NULL PRIMARY KEY,
site_id BIGINT NOT NULL,
remote_id VARCHAR(64) NOT NULL,
taxonomy_key VARCHAR(64) NOT NULL,
slug VARCHAR(255) NOT NULL,
raw_payload JSON NULL,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_wordpress_remote_term (site_id, taxonomy_key, remote_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS wordpress_sync_job (
id BIGINT NOT NULL PRIMARY KEY,
site_id BIGINT NOT NULL,
job_type VARCHAR(64) NOT NULL,
status VARCHAR(32) NOT NULL,
started_at DATETIME NULL,
finished_at DATETIME NULL,
summary_json JSON NULL,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
KEY idx_wordpress_sync_job_status (status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS wordpress_sync_mapping (
id BIGINT NOT NULL PRIMARY KEY,
site_id BIGINT NOT NULL,
remote_type VARCHAR(64) NOT NULL,
remote_id VARCHAR(64) NOT NULL,
local_type VARCHAR(64) NOT NULL,
local_id BIGINT NOT NULL,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_wordpress_sync_mapping (site_id, remote_type, remote_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS asset_bucket (
id BIGINT NOT NULL PRIMARY KEY,
bucket_key VARCHAR(128) NOT NULL,
bucket_name VARCHAR(128) NOT NULL,
provider VARCHAR(64) NOT NULL,
status VARCHAR(32) NOT NULL,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_asset_bucket_key (bucket_key)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS asset_object (
id BIGINT NOT NULL PRIMARY KEY,
bucket_id BIGINT NOT NULL,
object_key VARCHAR(512) NOT NULL,
source_module VARCHAR(64) NOT NULL,
mime_type VARCHAR(128) NULL,
size_bytes BIGINT NULL,
status VARCHAR(32) NOT NULL,
metadata_json JSON NULL,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_asset_object_key (bucket_id, object_key),
KEY idx_asset_object_source (source_module)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS asset_reference (
id BIGINT NOT NULL PRIMARY KEY,
object_id BIGINT NOT NULL,
owner_module VARCHAR(64) NOT NULL,
owner_type VARCHAR(64) NOT NULL,
owner_id BIGINT NOT NULL,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY uk_asset_reference (object_id, owner_module, owner_type, owner_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS asset_access_grant (
id BIGINT NOT NULL PRIMARY KEY,
object_id BIGINT NOT NULL,
grant_token VARCHAR(128) NOT NULL,
expires_at DATETIME NOT NULL,
status VARCHAR(32) NOT NULL,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY uk_asset_access_grant_token (grant_token),
KEY idx_asset_access_grant_object (object_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS system_notice (
id BIGINT NOT NULL PRIMARY KEY,
notice_key VARCHAR(128) NOT NULL,
title VARCHAR(255) NOT NULL,
content TEXT NOT NULL,
level VARCHAR(32) NOT NULL,
status VARCHAR(32) NOT NULL,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
KEY idx_system_notice_status (status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS system_event (
id BIGINT NOT NULL PRIMARY KEY,
event_key VARCHAR(128) NOT NULL,
source_module VARCHAR(64) NOT NULL,
event_type VARCHAR(64) NOT NULL,
payload_json JSON NULL,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
KEY idx_system_event_key (event_key)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS system_event_dedupe (
id BIGINT NOT NULL PRIMARY KEY,
dedupe_key VARCHAR(255) NOT NULL,
first_seen_at DATETIME NOT NULL,
last_seen_at DATETIME NOT NULL,
hit_count INT NOT NULL DEFAULT 1,
UNIQUE KEY uk_system_event_dedupe_key (dedupe_key)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS system_event_delivery (
id BIGINT NOT NULL PRIMARY KEY,
event_id BIGINT NOT NULL,
delivery_target VARCHAR(128) NOT NULL,
status VARCHAR(32) NOT NULL,
attempt_count INT NOT NULL DEFAULT 0,
last_error TEXT NULL,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
KEY idx_system_event_delivery_event (event_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS runtime_evidence_index (
id BIGINT NOT NULL PRIMARY KEY,
evidence_key VARCHAR(128) NOT NULL,
evidence_type VARCHAR(64) NOT NULL,
artifact_path VARCHAR(512) NOT NULL,
safe_summary JSON NULL,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY uk_runtime_evidence_key (evidence_key)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS qqbot_account (
id BIGINT NOT NULL PRIMARY KEY,
self_id VARCHAR(64) NOT NULL,
display_name VARCHAR(128) NULL,
onebot_status VARCHAR(32) NOT NULL,
container_status VARCHAR(32) NOT NULL,
webui_status VARCHAR(32) NOT NULL,
qq_login_status VARCHAR(32) NOT NULL,
last_error TEXT NULL,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_qqbot_account_self_id (self_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS qqbot_connection_session (
id BIGINT NOT NULL PRIMARY KEY,
account_id BIGINT NOT NULL,
session_key VARCHAR(128) NOT NULL,
status VARCHAR(32) NOT NULL,
connected_at DATETIME NULL,
disconnected_at DATETIME NULL,
close_reason TEXT NULL,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY uk_qqbot_connection_session_key (session_key),
KEY idx_qqbot_connection_session_account (account_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS qqbot_capability_binding (
id BIGINT NOT NULL PRIMARY KEY,
account_id BIGINT NOT NULL,
capability_key VARCHAR(128) NOT NULL,
enabled TINYINT NOT NULL DEFAULT 1,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY uk_qqbot_capability_binding (account_id, capability_key)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS qqbot_permission_policy (
id BIGINT NOT NULL PRIMARY KEY,
policy_key VARCHAR(128) NOT NULL,
scope_type VARCHAR(64) NOT NULL,
scope_value VARCHAR(128) NOT NULL,
effect VARCHAR(32) NOT NULL,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_qqbot_permission_policy (policy_key, scope_type, scope_value)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS qqbot_command (
id BIGINT NOT NULL PRIMARY KEY,
operation_key VARCHAR(128) NOT NULL,
command_key VARCHAR(128) NOT NULL,
plugin_key VARCHAR(128) NULL,
enabled TINYINT NOT NULL DEFAULT 1,
cooldown_seconds INT NOT NULL DEFAULT 0,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_qqbot_command_key (command_key),
KEY idx_qqbot_command_operation (operation_key)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS qqbot_command_alias (
id BIGINT NOT NULL PRIMARY KEY,
command_id BIGINT NOT NULL,
alias_text VARCHAR(128) NOT NULL,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY uk_qqbot_command_alias (command_id, alias_text)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS qqbot_rule (
id BIGINT NOT NULL PRIMARY KEY,
rule_key VARCHAR(128) NOT NULL,
account_id BIGINT NULL,
command_id BIGINT NULL,
matcher_json JSON NOT NULL,
action_json JSON NOT NULL,
enabled TINYINT NOT NULL DEFAULT 1,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_qqbot_rule_key (rule_key)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS qqbot_conversation (
id BIGINT NOT NULL PRIMARY KEY,
account_id BIGINT NOT NULL,
conversation_type VARCHAR(64) NOT NULL,
conversation_key VARCHAR(128) NOT NULL,
display_name VARCHAR(255) NULL,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_qqbot_conversation (account_id, conversation_type, conversation_key)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS qqbot_message (
id BIGINT NOT NULL PRIMARY KEY,
account_id BIGINT NOT NULL,
conversation_id BIGINT NULL,
message_id VARCHAR(128) NOT NULL,
direction VARCHAR(32) NOT NULL,
message_type VARCHAR(64) NOT NULL,
summary TEXT NULL,
raw_payload JSON NULL,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY uk_qqbot_message (account_id, message_id),
KEY idx_qqbot_message_conversation (conversation_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS qqbot_send_task (
id BIGINT NOT NULL PRIMARY KEY,
account_id BIGINT NOT NULL,
conversation_id BIGINT NULL,
task_key VARCHAR(128) NOT NULL,
status VARCHAR(32) NOT NULL,
payload_json JSON NOT NULL,
reserved_at DATETIME NULL,
sent_at DATETIME NULL,
last_error TEXT NULL,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_qqbot_send_task_key (task_key),
KEY idx_qqbot_send_task_status (status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS qqbot_send_log (
id BIGINT NOT NULL PRIMARY KEY,
task_id BIGINT NULL,
account_id BIGINT NOT NULL,
status VARCHAR(32) NOT NULL,
safe_summary JSON NULL,
error_message TEXT NULL,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
KEY idx_qqbot_send_log_task (task_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS qqbot_dedupe_event (
id BIGINT NOT NULL PRIMARY KEY,
dedupe_key VARCHAR(255) NOT NULL,
account_id BIGINT NULL,
expires_at DATETIME NOT NULL,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY uk_qqbot_dedupe_event_key (dedupe_key)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS qqbot_plugin (
id BIGINT NOT NULL PRIMARY KEY,
plugin_key VARCHAR(128) NOT NULL,
plugin_name VARCHAR(128) NOT NULL,
description TEXT NULL,
status VARCHAR(32) NOT NULL,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_qqbot_plugin_key (plugin_key)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS qqbot_plugin_version (
id BIGINT NOT NULL PRIMARY KEY,
plugin_id BIGINT NOT NULL,
version VARCHAR(64) NOT NULL,
package_hash VARCHAR(128) NOT NULL,
manifest_json JSON NOT NULL,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY uk_qqbot_plugin_version (plugin_id, version)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS qqbot_plugin_installation (
id BIGINT NOT NULL PRIMARY KEY,
plugin_id BIGINT NOT NULL,
version_id BIGINT NOT NULL,
status VARCHAR(32) NOT NULL,
runtime_status VARCHAR(32) NOT NULL,
installed_path VARCHAR(512) NOT NULL,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
KEY idx_qqbot_plugin_installation_status (status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS qqbot_plugin_operation (
id BIGINT NOT NULL PRIMARY KEY,
plugin_id BIGINT NOT NULL,
operation_key VARCHAR(128) NOT NULL,
operation_name VARCHAR(128) NOT NULL,
handler_name VARCHAR(128) NOT NULL,
enabled TINYINT NOT NULL DEFAULT 1,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY uk_qqbot_plugin_operation (plugin_id, operation_key)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS qqbot_plugin_event_handler (
id BIGINT NOT NULL PRIMARY KEY,
plugin_id BIGINT NOT NULL,
event_key VARCHAR(128) NOT NULL,
handler_name VARCHAR(128) NOT NULL,
enabled TINYINT NOT NULL DEFAULT 1,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY uk_qqbot_plugin_event_handler (plugin_id, event_key)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS qqbot_plugin_account_binding (
id BIGINT NOT NULL PRIMARY KEY,
plugin_id BIGINT NOT NULL,
account_id BIGINT NOT NULL,
enabled TINYINT NOT NULL DEFAULT 1,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY uk_qqbot_plugin_account_binding (plugin_id, account_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS qqbot_plugin_config (
id BIGINT NOT NULL PRIMARY KEY,
plugin_id BIGINT NOT NULL,
config_key VARCHAR(128) NOT NULL,
config_value JSON NULL,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_qqbot_plugin_config (plugin_id, config_key)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS qqbot_plugin_asset (
id BIGINT NOT NULL PRIMARY KEY,
plugin_id BIGINT NOT NULL,
asset_key VARCHAR(255) NOT NULL,
asset_path VARCHAR(512) NOT NULL,
content_hash VARCHAR(128) NOT NULL,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY uk_qqbot_plugin_asset (plugin_id, asset_key)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS qqbot_plugin_runtime_event (
id BIGINT NOT NULL PRIMARY KEY,
plugin_id BIGINT NOT NULL,
installation_id BIGINT NULL,
event_type VARCHAR(64) NOT NULL,
level VARCHAR(32) NOT NULL,
safe_summary JSON NULL,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
KEY idx_qqbot_plugin_runtime_event_plugin (plugin_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS napcat_container (
id BIGINT NOT NULL PRIMARY KEY,
account_id BIGINT NOT NULL,
container_name VARCHAR(128) NOT NULL,
image_name VARCHAR(255) NOT NULL,
status VARCHAR(32) NOT NULL,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_napcat_container_name (container_name)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS napcat_device_identity (
id BIGINT NOT NULL PRIMARY KEY,
account_id BIGINT NOT NULL,
container_id BIGINT NULL,
data_dir VARCHAR(512) NOT NULL,
hostname VARCHAR(128) NOT NULL,
machine_id_path VARCHAR(512) NOT NULL,
mac_address VARCHAR(64) NOT NULL,
verification_status VARCHAR(32) NOT NULL,
last_login_evidence JSON NULL,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_napcat_device_identity_account (account_id),
KEY idx_napcat_device_identity_container (container_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS napcat_account_binding (
id BIGINT NOT NULL PRIMARY KEY,
account_id BIGINT NOT NULL,
container_id BIGINT NOT NULL,
device_identity_id BIGINT NOT NULL,
status VARCHAR(32) NOT NULL,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_napcat_account_binding_account (account_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS napcat_login_session (
id BIGINT NOT NULL PRIMARY KEY,
account_id BIGINT NOT NULL,
session_key VARCHAR(128) NOT NULL,
login_stage VARCHAR(64) NOT NULL,
status VARCHAR(32) NOT NULL,
progress_message VARCHAR(255) NOT NULL,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_napcat_login_session_key (session_key),
KEY idx_napcat_login_session_account (account_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS napcat_login_challenge (
id BIGINT NOT NULL PRIMARY KEY,
session_id BIGINT NOT NULL,
challenge_type VARCHAR(64) NOT NULL,
status VARCHAR(32) NOT NULL,
challenge_url TEXT NULL,
challenge_payload JSON NULL,
resolved_at DATETIME NULL,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
KEY idx_napcat_login_challenge_session (session_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS napcat_runtime_cleanup (
id BIGINT NOT NULL PRIMARY KEY,
session_id BIGINT NOT NULL,
cleanup_type VARCHAR(64) NOT NULL,
status VARCHAR(32) NOT NULL,
error_message TEXT NULL,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
KEY idx_napcat_runtime_cleanup_session (session_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

View File

@ -0,0 +1,138 @@
INSERT INTO admin_user (
id,
username,
password_hash,
nickname,
status
) VALUES (
1000000000000000001,
'admin',
'!disabled-refactor-v3-password-hash!',
'Administrator',
'password_unset'
) ON DUPLICATE KEY UPDATE
nickname = VALUES(nickname),
status = VALUES(status);
INSERT INTO admin_role (
id,
role_key,
role_name,
status
) VALUES (
1000000000000000002,
'super_admin',
'Super Administrator',
'enabled'
) ON DUPLICATE KEY UPDATE
role_name = VALUES(role_name),
status = VALUES(status);
INSERT INTO admin_user_role (
id,
user_id,
role_id
) VALUES (
1000000000000000003,
1000000000000000001,
1000000000000000002
) ON DUPLICATE KEY UPDATE
role_id = VALUES(role_id);
INSERT INTO admin_menu (
id,
parent_id,
menu_key,
title,
path,
component,
sort_no,
status
) VALUES (
1000000000000000004,
NULL,
'dashboard',
'Dashboard',
'/dashboard',
'dashboard/workspace/index',
10,
'enabled'
) ON DUPLICATE KEY UPDATE
title = VALUES(title),
path = VALUES(path),
component = VALUES(component),
status = VALUES(status);
INSERT INTO platform_setting (
id,
setting_key,
setting_value,
value_type
) VALUES (
1000000000000000005,
'schema.version',
'refactor-v3',
'string'
) ON DUPLICATE KEY UPDATE
setting_value = VALUES(setting_value),
value_type = VALUES(value_type);
INSERT INTO qqbot_plugin (
id,
plugin_key,
plugin_name,
description,
status
) VALUES
(
1000000000000000101,
'bangdream',
'BangDream',
'Built-in BangDream command plugin metadata.',
'installed'
),
(
1000000000000000102,
'ff14-market',
'FF14 Market',
'Built-in FF14 market command plugin metadata.',
'installed'
),
(
1000000000000000103,
'fflogs',
'FFLogs',
'Built-in FFLogs command plugin metadata.',
'installed'
),
(
1000000000000000104,
'repeater',
'Repeater',
'Built-in repeater event plugin metadata.',
'installed'
)
ON DUPLICATE KEY UPDATE
plugin_name = VALUES(plugin_name),
description = VALUES(description),
status = VALUES(status);
INSERT INTO qqbot_command (
id,
operation_key,
command_key,
plugin_key,
enabled,
cooldown_seconds
) VALUES (
1000000000000000201,
'bangdream.song.search',
'bangdream_song',
'bangdream',
1,
2
) ON DUPLICATE KEY UPDATE
operation_key = VALUES(operation_key),
plugin_key = VALUES(plugin_key),
enabled = VALUES(enabled),
cooldown_seconds = VALUES(cooldown_seconds);

View File

@ -0,0 +1,53 @@
SELECT 'admin_user' AS table_name, COUNT(*) AS row_count FROM admin_user;
SELECT 'admin_role' AS table_name, COUNT(*) AS row_count FROM admin_role;
SELECT 'admin_menu' AS table_name, COUNT(*) AS row_count FROM admin_menu;
SELECT 'platform_setting' AS table_name, COUNT(*) AS row_count FROM platform_setting;
SELECT 'qqbot_command' AS table_name, COUNT(*) AS row_count FROM qqbot_command;
SELECT 'qqbot_plugin' AS table_name, COUNT(*) AS row_count FROM qqbot_plugin;
SELECT 'napcat_device_identity' AS table_name, COUNT(*) AS row_count FROM napcat_device_identity;
SELECT 'seed_admin_user' AS check_name, COUNT(*) AS matched_rows
FROM admin_user
WHERE username = 'admin'
AND status = 'password_unset';
SELECT 'seed_platform_schema_version' AS check_name, COUNT(*) AS matched_rows
FROM platform_setting
WHERE setting_key = 'schema.version'
AND setting_value = 'refactor-v3';
SELECT 'seed_qqbot_plugin_bangdream' AS check_name, COUNT(*) AS matched_rows
FROM qqbot_plugin
WHERE plugin_key = 'bangdream'
AND status = 'installed';
SELECT 'seed_qqbot_command_bangdream_song' AS check_name, COUNT(*) AS matched_rows
FROM qqbot_command
WHERE command_key = 'bangdream_song'
AND operation_key = 'bangdream.song.search'
AND plugin_key = 'bangdream'
AND enabled = 1;
SELECT 'index_admin_user_username' AS check_name, COUNT(*) AS matched_rows
FROM information_schema.statistics
WHERE table_schema = DATABASE()
AND table_name = 'admin_user'
AND index_name = 'uk_admin_user_username';
SELECT 'index_platform_setting_key' AS check_name, COUNT(*) AS matched_rows
FROM information_schema.statistics
WHERE table_schema = DATABASE()
AND table_name = 'platform_setting'
AND index_name = 'uk_platform_setting_key';
SELECT 'index_qqbot_command_key' AS check_name, COUNT(*) AS matched_rows
FROM information_schema.statistics
WHERE table_schema = DATABASE()
AND table_name = 'qqbot_command'
AND index_name = 'uk_qqbot_command_key';
SELECT 'index_napcat_device_identity_account' AS check_name, COUNT(*) AS matched_rows
FROM information_schema.statistics
WHERE table_schema = DATABASE()
AND table_name = 'napcat_device_identity'
AND index_name = 'uk_napcat_device_identity_account';

View File

@ -0,0 +1,56 @@
import { readFileSync } from 'fs';
import { join } from 'path';
const root = join(__dirname, '..', '..');
const extractSchemaMapTables = () => {
const schemaMap = readFileSync(
join(root, 'docs/refactor-v3/schema-map.md'),
'utf8',
);
const matches = schemaMap.match(/`[a-z][a-z0-9_]+`/g) || [];
return Array.from(
new Set(
matches
.map((match) => match.slice(1, -1))
.filter((tableName) => !tableName.endsWith('_*')),
),
).sort();
};
describe('refactor v3 schema skeleton', () => {
it('declares every table listed in the schema map in the full schema file', () => {
const sql = readFileSync(
join(root, 'sql/refactor-v3/00-full-schema.sql'),
'utf8',
);
const requiredTables = extractSchemaMapTables();
expect(requiredTables.length).toBeGreaterThan(50);
for (const table of requiredTables) {
expect(sql).toContain(`CREATE TABLE IF NOT EXISTS ${table}`);
}
});
it('declares core seed and verification scripts', () => {
const seed = readFileSync(
join(root, 'sql/refactor-v3/01-seed-core.sql'),
'utf8',
);
const verify = readFileSync(
join(root, 'sql/refactor-v3/99-verify.sql'),
'utf8',
);
expect(seed).toContain('INSERT INTO admin_user');
expect(seed).toContain('INSERT INTO qqbot_command');
expect(seed).toContain('INSERT INTO qqbot_plugin');
expect(verify).toContain('admin_user');
expect(verify).toContain('qqbot_command');
expect(verify).toContain('qqbot_plugin');
expect(verify).toContain('napcat_device_identity');
});
});