58 KiB
P1: muse-cloud 后端搭建 — 执行计划
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task.
目标: 在 Yudao Cloud fork 基础上搭建 6 个 Muse 业务模块,实现全量 API(229 接口),提供可调用的后端服务。
架构: Yudao Cloud 微服务 + PostgreSQL 16 + Flyway 迁移。6 个业务模块按依赖顺序实现:content+account → meta → ai+knowledge → market。Java 21 + Spring Boot 3 + JUnit 5 + Testcontainers。
技术栈: Java 21, Spring Boot 3, PostgreSQL 16, Flyway, Maven, Docker Compose, JUnit 5, Testcontainers, ArchUnit
Step 1: 基础环境搭建
Task 1.1: 确认 Yudao Cloud Fork 状态
仓库路径: muse-cloud/
- Step 1: 检查当前模块结构
ls muse-cloud/muse-module-*/pom.xml
预期输出:
muse-cloud/muse-module-ai/muse-module-ai-api/pom.xml
muse-cloud/muse-module-ai/muse-module-ai-server/pom.xml
muse-cloud/muse-module-system/muse-module-system-api/pom.xml
muse-cloud/muse-module-system/muse-module-system-server/pom.xml
muse-cloud/muse-module-infra/.../pom.xml
muse-cloud/muse-module-bpm/.../pom.xml
muse-cloud/muse-module-member/.../pom.xml
muse-cloud/muse-module-mp/.../pom.xml
muse-cloud/muse-module-pay/.../pom.xml
muse-cloud/muse-module-report/.../pom.xml
- Step 2: 确认包名是否已改为 com.muse
find muse-cloud -name "*.java" -path "*/com/muse/*" | head -10
若仍为 cn.iocoder.yudao,需全局替换。
- Step 3: 确认 pom.xml 中 Java 版本
grep -r "java.version\|maven.compiler" muse-cloud/pom.xml | head -5
确保为 Java 21。
- Step 4: 验证可编译
cd muse-cloud && mvn compile -q 2>&1 | tail -5
Task 1.2: Docker Compose 开发环境
文件:
-
创建:
muse-cloud/docker-compose.yml -
Step 1: 编写 docker-compose.yml
version: '3.8'
services:
postgres:
image: postgres:16-alpine
environment:
POSTGRES_USER: muse
POSTGRES_PASSWORD: muse_dev
POSTGRES_DB: muse
ports:
- "5432:5432"
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U muse"]
interval: 5s
timeout: 5s
retries: 5
redis:
image: redis:8-alpine
ports:
- "6379:6379"
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 5s
retries: 5
volumes:
pgdata:
- Step 2: 启动并验证
cd muse-cloud && docker compose up -d
docker compose ps
预期输出: postgres 和 redis 均为 running 状态。
- Step 3: 提交
git add docker-compose.yml
git commit -m "feat(infra): 添加 Docker Compose 开发环境(PostgreSQL 16 + Redis 8)"
Task 1.3: 调整 Yudao 配置适配 PostgreSQL
文件:
-
修改:
muse-cloud/muse-server/src/main/resources/application-local.yaml -
Step 1: 确认 PostgreSQL 数据源配置
grep -A10 "datasource" muse-cloud/muse-server/src/main/resources/application-local.yaml
确保 url 为 jdbc:postgresql://localhost:5432/muse,driver 为 org.postgresql.Driver。
- Step 2: 提交
git add muse-cloud/muse-server/src/main/resources/application-local.yaml
git commit -m "chore(config): 确认 PostgreSQL 数据源配置"
Task 1.4: 配置 .idea 团队共享
文件:
-
创建:
muse-cloud/.idea/下多个配置文件 -
Step 1: 创建 .idea/.gitignore
# 团队共享 IDE 配置
# 个人配置不入库
/shelf/
/workspace.xml
/httpRequests/
/dataSources/
/dataSources.local.xml
- Step 2: 创建代码风格配置
mkdir -p muse-cloud/.idea/codeStyles
cat > muse-cloud/.idea/codeStyles/Project.xml << 'XML'
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<JavaCodeStyleSettings>
<option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99" />
<option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99" />
<option name="PACKAGES_TO_USE_IMPORT_ON_DEMAND">
<value />
</option>
</JavaCodeStyleSettings>
</code_scheme>
</component>
XML
- Step 3: 提交
git add muse-cloud/.idea/
git commit -m "chore(ide): 添加团队共享 .idea 配置"
Step 2: 数据库 Migration
Task 2.1: 编写全量 DDL
文件:
-
创建:
muse-cloud/sql/muse/V1__init_content_schema.sql -
创建:
muse-cloud/sql/muse/V2__init_account_schema.sql -
创建:
muse-cloud/sql/muse/V3__init_meta_schema.sql -
创建:
muse-cloud/sql/muse/V4__init_ai_schema.sql -
创建:
muse-cloud/sql/muse/V5__init_knowledge_schema.sql -
创建:
muse-cloud/sql/muse/V6__init_market_schema.sql -
参考:
design-docs/后端-04-统一数据库Schema-v1.md -
参考:
design-docs/后端-04a-完整建表SQL.sql -
Step 1: 创建 Migration 目录
mkdir -p muse-cloud/sql/muse
- Step 1b: 创建 updated_at 触发器函数(PostgreSQL 通用)
所有业务表通过触发器自动更新 update_time,无需 ON UPDATE CURRENT_TIMESTAMP。
-- 触发器函数:自动更新 update_time 列
CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
NEW.update_time = CURRENT_TIMESTAMP;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
- Step 2: 编写 Content 模块 DDL (V1)
从 design-docs/后端-04a-完整建表SQL.sql 提取 Content 相关表(Work, Chapter, Block, BlockSourceAttribution 等),调整表名为 Yudao 约定(加 muse_content_ 前缀),添加必备审计字段。
-- V1__init_content_schema.sql
-- Muse Content 模块初始 Schema(PostgreSQL 16)
-- 对应设计文档: design-docs/后端-04-统一数据库Schema-v1.md
-- ============================================================
-- 作品表
-- ============================================================
CREATE TABLE muse_content_work (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
owner_user_id BIGINT NOT NULL,
title VARCHAR(200) NOT NULL,
genre VARCHAR(50),
summary TEXT,
status VARCHAR(20) NOT NULL DEFAULT 'writing',
work_schema_id BIGINT,
import_status VARCHAR(20),
parse_status VARCHAR(20),
word_count INT NOT NULL DEFAULT 0,
chapter_count INT NOT NULL DEFAULT 0,
creator VARCHAR(64) NOT NULL DEFAULT '',
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updater VARCHAR(64) NOT NULL DEFAULT '',
update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted BOOLEAN NOT NULL DEFAULT FALSE,
tenant_id BIGINT NOT NULL DEFAULT 0
);
CREATE INDEX idx_muse_content_work_owner ON muse_content_work(owner_user_id, update_time);
CREATE INDEX idx_muse_content_work_status ON muse_content_work(status);
CREATE TRIGGER trg_muse_content_work_updated_at
BEFORE UPDATE ON muse_content_work
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
-- ============================================================
-- 章节表
-- ============================================================
CREATE TABLE muse_content_chapter (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
work_id BIGINT NOT NULL,
title VARCHAR(200) NOT NULL,
order_no INT NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'draft',
goal_snapshot JSON,
outline_snapshot JSON,
parse_review_status VARCHAR(20),
creator VARCHAR(64) NOT NULL DEFAULT '',
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updater VARCHAR(64) NOT NULL DEFAULT '',
update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted BOOLEAN NOT NULL DEFAULT FALSE,
tenant_id BIGINT NOT NULL DEFAULT 0,
CONSTRAINT uk_muse_content_chapter_work_order UNIQUE (work_id, order_no)
);
CREATE INDEX idx_muse_content_chapter_work ON muse_content_chapter(work_id);
CREATE TRIGGER trg_muse_content_chapter_updated_at
BEFORE UPDATE ON muse_content_chapter
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
-- ============================================================
-- Block 正文块表
-- ============================================================
CREATE TABLE muse_content_block (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
work_id BIGINT NOT NULL,
chapter_id BIGINT NOT NULL,
order_no INT NOT NULL,
content_doc JSON,
revision INT NOT NULL DEFAULT 1,
word_count INT NOT NULL DEFAULT 0,
creator VARCHAR(64) NOT NULL DEFAULT '',
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updater VARCHAR(64) NOT NULL DEFAULT '',
update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted BOOLEAN NOT NULL DEFAULT FALSE,
tenant_id BIGINT NOT NULL DEFAULT 0,
CONSTRAINT uk_muse_content_block_chapter_order UNIQUE (chapter_id, order_no)
);
CREATE INDEX idx_muse_content_block_work ON muse_content_block(work_id);
CREATE INDEX idx_muse_content_block_chapter ON muse_content_block(chapter_id);
CREATE TRIGGER trg_muse_content_block_updated_at
BEFORE UPDATE ON muse_content_block
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
-- ============================================================
-- Block 来源归因表
-- ============================================================
CREATE TABLE muse_content_block_source_attribution (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
work_id BIGINT NOT NULL,
block_id BIGINT NOT NULL,
revision INT NOT NULL,
source_type VARCHAR(50) NOT NULL,
source_object_id VARCHAR(100),
source_version INT,
lineage_payload JSON,
authorization_snapshot_id BIGINT,
source_status VARCHAR(30),
license_restriction_snapshot JSON,
creator VARCHAR(64) NOT NULL DEFAULT '',
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT uk_muse_content_block_src_revision UNIQUE (block_id, revision)
);
CREATE INDEX idx_muse_content_block_src_block ON muse_content_block_source_attribution(block_id);
CREATE TRIGGER trg_muse_content_block_src_updated_at
BEFORE UPDATE ON muse_content_block_source_attribution
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
- Step 3: 编写 Account 模块 DDL (V2)
-- V2__init_account_schema.sql
-- Muse Account 模块初始 Schema(PostgreSQL 16)
-- 物理承载: muse-module-member, 表前缀 muse_member_
-- 用户权益表(可变表 + 审计日志)
CREATE TABLE muse_member_entitlement (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
account_user_id BIGINT NOT NULL,
plan_type VARCHAR(50) NOT NULL DEFAULT 'free',
plan_started_at TIMESTAMP,
plan_expires_at TIMESTAMP,
ai_call_quota INT NOT NULL DEFAULT 0,
ai_call_used INT NOT NULL DEFAULT 0,
storage_quota_mb BIGINT NOT NULL DEFAULT 0,
storage_used_mb BIGINT NOT NULL DEFAULT 0,
kb_count_quota INT NOT NULL DEFAULT 0,
work_count_quota INT NOT NULL DEFAULT 0,
export_quota INT NOT NULL DEFAULT 0,
features_json JSON,
creator VARCHAR(64) NOT NULL DEFAULT '',
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updater VARCHAR(64) NOT NULL DEFAULT '',
update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted BOOLEAN NOT NULL DEFAULT FALSE,
tenant_id BIGINT NOT NULL DEFAULT 0,
CONSTRAINT uk_muse_member_entitlement_user UNIQUE (account_user_id)
);
CREATE INDEX idx_muse_member_entitlement_user ON muse_member_entitlement(account_user_id);
CREATE TRIGGER trg_muse_member_entitlement_updated_at
BEFORE UPDATE ON muse_member_entitlement
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
-- 权益审计日志表(append-only)
CREATE TABLE muse_member_entitlement_audit_log (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
account_user_id BIGINT NOT NULL,
change_type VARCHAR(30) NOT NULL,
source_owner VARCHAR(50),
source_id BIGINT,
idempotency_key VARCHAR(200) NOT NULL,
before_value_snapshot JSON,
delta_value_snapshot JSON,
after_value_snapshot JSON,
reason_code VARCHAR(50),
reason_message VARCHAR(500),
operator_user_id BIGINT,
audit_event_id BIGINT,
creator VARCHAR(64) NOT NULL DEFAULT '',
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT uk_muse_member_ent_audit_idem UNIQUE (idempotency_key)
);
CREATE INDEX idx_muse_member_ent_audit_user ON muse_member_entitlement_audit_log(account_user_id, create_time);
-- 用户配额表
CREATE TABLE muse_member_quota (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
account_user_id BIGINT NOT NULL,
quota_type VARCHAR(50) NOT NULL,
total_amount BIGINT NOT NULL DEFAULT 0,
used_amount BIGINT NOT NULL DEFAULT 0,
reset_cycle VARCHAR(20),
last_reset_at TIMESTAMP,
next_reset_at TIMESTAMP,
creator VARCHAR(64) NOT NULL DEFAULT '',
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updater VARCHAR(64) NOT NULL DEFAULT '',
update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted BOOLEAN NOT NULL DEFAULT FALSE,
tenant_id BIGINT NOT NULL DEFAULT 0,
CONSTRAINT uk_muse_member_quota_user_type UNIQUE (account_user_id, quota_type)
);
CREATE INDEX idx_muse_member_quota_user ON muse_member_quota(account_user_id);
CREATE TRIGGER trg_muse_member_quota_updated_at
BEFORE UPDATE ON muse_member_quota
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
-- 用量记录表
CREATE TABLE muse_member_usage_record (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
account_user_id BIGINT NOT NULL,
usage_type VARCHAR(50) NOT NULL,
work_id BIGINT,
task_id BIGINT,
token_input INT DEFAULT 0,
token_output INT DEFAULT 0,
cost_reference VARCHAR(100),
gateway_request_id VARCHAR(200),
correlation_id VARCHAR(200),
status VARCHAR(20) NOT NULL DEFAULT 'completed',
creator VARCHAR(64) NOT NULL DEFAULT '',
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updater VARCHAR(64) NOT NULL DEFAULT '',
update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted BOOLEAN NOT NULL DEFAULT FALSE,
tenant_id BIGINT NOT NULL DEFAULT 0
);
CREATE INDEX idx_muse_member_usage_user_time ON muse_member_usage_record(account_user_id, create_time);
CREATE INDEX idx_muse_member_usage_correlation ON muse_member_usage_record(correlation_id);
CREATE TRIGGER trg_muse_member_usage_record_updated_at
BEFORE UPDATE ON muse_member_usage_record
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
- Step 4: 编写 Meta 模块 DDL (V3)
-- V3__init_meta_schema.sql
-- MetaSchema 模块初始 Schema(PostgreSQL 16)
CREATE TABLE muse_meta_schema (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
schema_key VARCHAR(100) NOT NULL,
domain VARCHAR(20) NOT NULL,
scope VARCHAR(20) NOT NULL,
target_type VARCHAR(50),
display_name VARCHAR(200) NOT NULL,
active_version_id BIGINT,
effective_scope JSON,
projection_version INT NOT NULL DEFAULT 1,
status VARCHAR(20) NOT NULL DEFAULT 'active',
creator VARCHAR(64) NOT NULL DEFAULT '',
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updater VARCHAR(64) NOT NULL DEFAULT '',
update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted BOOLEAN NOT NULL DEFAULT FALSE,
CONSTRAINT uk_muse_meta_schema_key UNIQUE (domain, scope, target_type, schema_key)
);
CREATE TRIGGER trg_muse_meta_schema_updated_at
BEFORE UPDATE ON muse_meta_schema
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
CREATE TABLE muse_meta_schema_version (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
schema_id BIGINT NOT NULL,
schema_key VARCHAR(100) NOT NULL,
version_no INT NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'draft',
active_flag BOOLEAN NOT NULL DEFAULT FALSE,
effective_scope JSON,
field_contract_snapshot JSON,
impact_preview_snapshot JSON,
rollback_from_version_id BIGINT,
rollback_to_version_id BIGINT,
published_by VARCHAR(64),
published_at TIMESTAMP,
activated_by VARCHAR(64),
activated_at TIMESTAMP,
change_note VARCHAR(500),
creator VARCHAR(64) NOT NULL DEFAULT '',
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT uk_muse_meta_schema_version_key UNIQUE (schema_key, version_no)
);
CREATE INDEX idx_muse_meta_schema_ver_sid ON muse_meta_schema_version(schema_id);
CREATE TRIGGER trg_muse_meta_schema_version_updated_at
BEFORE UPDATE ON muse_meta_schema_version
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
CREATE TABLE muse_meta_field (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
schema_version_id BIGINT NOT NULL,
field_key VARCHAR(100) NOT NULL,
display_name VARCHAR(200) NOT NULL,
field_type VARCHAR(20) NOT NULL,
is_required BOOLEAN NOT NULL DEFAULT FALSE,
default_value JSON,
enum_values JSON,
validation_rules JSON,
sort_order INT NOT NULL DEFAULT 0,
creator VARCHAR(64) NOT NULL DEFAULT '',
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_muse_meta_field_ver ON muse_meta_field(schema_version_id);
CREATE TABLE muse_meta_visibility_policy (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
schema_version_id BIGINT NOT NULL,
ui_visible BOOLEAN NOT NULL DEFAULT TRUE,
ai_context BOOLEAN NOT NULL DEFAULT FALSE,
user_editable BOOLEAN NOT NULL DEFAULT TRUE,
user_searchable BOOLEAN NOT NULL DEFAULT FALSE,
exportable BOOLEAN NOT NULL DEFAULT FALSE,
creator VARCHAR(64) NOT NULL DEFAULT '',
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_muse_meta_vis_policy_ver ON muse_meta_visibility_policy(schema_version_id);
- Step 5: 编写 AI 模块 DDL (V4)
-- V4__init_ai_schema.sql
-- Muse AI 模块初始 Schema(PostgreSQL 16)
-- AI 生成任务表
CREATE TABLE muse_ai_generation (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
work_id BIGINT,
chapter_id BIGINT,
block_id BIGINT,
agent_id BIGINT,
agent_version_id BIGINT,
task_type VARCHAR(50) NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'queued',
idempotency_key VARCHAR(200) NOT NULL,
input_snapshot JSON,
output_summary JSON,
candidate_id BIGINT,
error_code VARCHAR(50),
error_message VARCHAR(500),
retry_count INT NOT NULL DEFAULT 0,
started_at TIMESTAMP,
finished_at TIMESTAMP,
creator VARCHAR(64) NOT NULL DEFAULT '',
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updater VARCHAR(64) NOT NULL DEFAULT '',
update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted BOOLEAN NOT NULL DEFAULT FALSE,
CONSTRAINT uk_muse_ai_generation_idem UNIQUE (idempotency_key)
);
CREATE INDEX idx_muse_ai_gen_work_status ON muse_ai_generation(work_id, status, create_time);
CREATE INDEX idx_muse_ai_gen_block ON muse_ai_generation(block_id);
CREATE TRIGGER trg_muse_ai_generation_updated_at
BEFORE UPDATE ON muse_ai_generation
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
-- AI 候选/Suggestion 表
CREATE TABLE muse_ai_suggestion (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
work_id BIGINT NOT NULL,
chapter_id BIGINT,
block_id BIGINT,
generation_id BIGINT NOT NULL,
candidate_type VARCHAR(50) NOT NULL DEFAULT 'suggestion',
content_snapshot JSON,
agent_id BIGINT,
agent_name VARCHAR(200),
agent_version VARCHAR(50),
status VARCHAR(20) NOT NULL DEFAULT 'pending',
source_revision INT,
source_hash VARCHAR(100),
diff_summary JSON,
quality_scores JSON,
quality_result_id BIGINT,
source_snapshot_id BIGINT,
authorization_snapshot_id BIGINT,
source_status VARCHAR(30),
source_action_policy VARCHAR(30),
decision_archive_id BIGINT,
creator VARCHAR(64) NOT NULL DEFAULT '',
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updater VARCHAR(64) NOT NULL DEFAULT '',
update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted BOOLEAN NOT NULL DEFAULT FALSE
);
CREATE INDEX idx_muse_ai_suggestion_work_status ON muse_ai_suggestion(work_id, status, create_time);
CREATE INDEX idx_muse_ai_suggestion_gen ON muse_ai_suggestion(generation_id);
CREATE TRIGGER trg_muse_ai_suggestion_updated_at
BEFORE UPDATE ON muse_ai_suggestion
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
-- 智能体定义表
CREATE TABLE muse_agent (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
agent_key VARCHAR(100) NOT NULL,
name VARCHAR(200) NOT NULL,
description VARCHAR(500),
agent_type VARCHAR(20) NOT NULL DEFAULT 'system',
owner_user_id BIGINT,
prompt_key VARCHAR(100),
status VARCHAR(20) NOT NULL DEFAULT 'active',
current_version_id BIGINT,
slot_bindings JSON,
category VARCHAR(50),
tags JSON,
creator VARCHAR(64) NOT NULL DEFAULT '',
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updater VARCHAR(64) NOT NULL DEFAULT '',
update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted BOOLEAN NOT NULL DEFAULT FALSE,
CONSTRAINT uk_muse_agent_key UNIQUE (agent_key)
);
CREATE INDEX idx_muse_agent_type_status ON muse_agent(agent_type, status);
CREATE TRIGGER trg_muse_agent_updated_at
BEFORE UPDATE ON muse_agent
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
-- 智能体版本历史表
CREATE TABLE muse_agent_version (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
agent_id BIGINT NOT NULL,
version VARCHAR(50) NOT NULL,
config JSON,
change_note VARCHAR(500),
status VARCHAR(20) NOT NULL DEFAULT 'draft',
creator VARCHAR(64) NOT NULL DEFAULT '',
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT uk_muse_agent_version_agent_ver UNIQUE (agent_id, version)
);
CREATE INDEX idx_muse_agent_version_agent ON muse_agent_version(agent_id);
-- 作品级槽位绑定表
CREATE TABLE muse_agent_slot_binding (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
work_id BIGINT NOT NULL,
slot_key VARCHAR(100) NOT NULL,
agent_id BIGINT NOT NULL,
agent_version VARCHAR(50) NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'active',
revision INT NOT NULL DEFAULT 1,
authorization_snapshot_id BIGINT,
source_snapshot_id BIGINT,
fallback_agent_id BIGINT,
binding_source VARCHAR(50),
creator VARCHAR(64) NOT NULL DEFAULT '',
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updater VARCHAR(64) NOT NULL DEFAULT '',
update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted BOOLEAN NOT NULL DEFAULT FALSE
);
CREATE INDEX idx_muse_agent_slot_binding_work ON muse_agent_slot_binding(work_id, slot_key, status);
CREATE TRIGGER trg_muse_agent_slot_binding_updated_at
BEFORE UPDATE ON muse_agent_slot_binding
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
-- Prompt 模板表
CREATE TABLE muse_prompt (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
prompt_key VARCHAR(100) NOT NULL,
name VARCHAR(200) NOT NULL,
description VARCHAR(500),
content TEXT,
active_version VARCHAR(50),
status VARCHAR(20) NOT NULL DEFAULT 'active',
category VARCHAR(50),
creator VARCHAR(64) NOT NULL DEFAULT '',
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updater VARCHAR(64) NOT NULL DEFAULT '',
update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted BOOLEAN NOT NULL DEFAULT FALSE,
CONSTRAINT uk_muse_prompt_key UNIQUE (prompt_key)
);
CREATE TRIGGER trg_muse_prompt_updated_at
BEFORE UPDATE ON muse_prompt
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
-- Prompt 版本历史表
CREATE TABLE muse_prompt_version (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
prompt_id BIGINT NOT NULL,
prompt_key VARCHAR(100) NOT NULL,
version VARCHAR(50) NOT NULL,
content TEXT,
variables JSON,
change_note VARCHAR(500),
status VARCHAR(20) NOT NULL DEFAULT 'draft',
creator VARCHAR(64) NOT NULL DEFAULT '',
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT uk_muse_prompt_version_key_ver UNIQUE (prompt_key, version)
);
CREATE INDEX idx_muse_prompt_version_prompt ON muse_prompt_version(prompt_id);
-- 质量策略表
CREATE TABLE muse_quality_policy (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
policy_key VARCHAR(100) NOT NULL,
name VARCHAR(200) NOT NULL,
description VARCHAR(500),
active_version VARCHAR(50),
task_types JSON,
creator VARCHAR(64) NOT NULL DEFAULT '',
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updater VARCHAR(64) NOT NULL DEFAULT '',
update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted BOOLEAN NOT NULL DEFAULT FALSE,
CONSTRAINT uk_muse_quality_policy_key UNIQUE (policy_key)
);
CREATE TRIGGER trg_muse_quality_policy_updated_at
BEFORE UPDATE ON muse_quality_policy
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
-- 质量策略版本表
CREATE TABLE muse_quality_policy_version (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
policy_id BIGINT NOT NULL,
policy_key VARCHAR(100) NOT NULL,
version_no INT NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'draft',
active_flag BOOLEAN NOT NULL DEFAULT FALSE,
effective_scope JSON,
metric_contract_snapshot JSON,
threshold_snapshot JSON,
rewrite_policy_snapshot JSON,
rollback_from_version_id BIGINT,
rollback_to_version_id BIGINT,
creator VARCHAR(64) NOT NULL DEFAULT '',
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT uk_muse_quality_policy_ver UNIQUE (policy_key, version_no)
);
CREATE INDEX idx_muse_quality_policy_ver_pid ON muse_quality_policy_version(policy_id);
-- 工具授权表
CREATE TABLE muse_tool_grant (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
grant_key VARCHAR(100) NOT NULL,
agent_id BIGINT NOT NULL,
tool_name VARCHAR(100) NOT NULL,
action VARCHAR(100) NOT NULL,
purpose VARCHAR(200),
authority_owner VARCHAR(50) NOT NULL DEFAULT 'governance',
scope JSON,
budget JSON,
input_source_policy JSON,
output_target_policy JSON,
egress_policy JSON,
outbound_policy JSON,
audit_requirement JSON,
approval_status VARCHAR(20) NOT NULL DEFAULT 'pending',
approved_by VARCHAR(64),
status VARCHAR(20) NOT NULL DEFAULT 'active',
creator VARCHAR(64) NOT NULL DEFAULT '',
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updater VARCHAR(64) NOT NULL DEFAULT '',
update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted BOOLEAN NOT NULL DEFAULT FALSE,
CONSTRAINT uk_muse_tool_grant_key UNIQUE (grant_key)
);
CREATE INDEX idx_muse_tool_grant_agent ON muse_tool_grant(agent_id);
CREATE TRIGGER trg_muse_tool_grant_updated_at
BEFORE UPDATE ON muse_tool_grant
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
- Step 6: 编写 Knowledge 模块 DDL (V5)
-- V5__init_knowledge_schema.sql
-- Muse Knowledge 模块初始 Schema(PostgreSQL 16)
-- 知识库根表
CREATE TABLE muse_knowledge_base (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
name VARCHAR(200) NOT NULL,
description VARCHAR(500),
kb_type VARCHAR(20) NOT NULL DEFAULT 'user',
owner_user_id BIGINT,
source_market_asset_id BIGINT,
status VARCHAR(20) NOT NULL DEFAULT 'active',
active_version INT NOT NULL DEFAULT 1,
visibility_policy JSON,
license_snapshot_id BIGINT,
creator VARCHAR(64) NOT NULL DEFAULT '',
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updater VARCHAR(64) NOT NULL DEFAULT '',
update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted BOOLEAN NOT NULL DEFAULT FALSE,
tenant_id BIGINT NOT NULL DEFAULT 0
);
CREATE INDEX idx_muse_kb_owner_status ON muse_knowledge_base(owner_user_id, status);
CREATE INDEX idx_muse_kb_type ON muse_knowledge_base(kb_type);
CREATE TRIGGER trg_muse_knowledge_base_updated_at
BEFORE UPDATE ON muse_knowledge_base
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
-- 知识库资料表
CREATE TABLE muse_knowledge_document (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
kb_id BIGINT NOT NULL,
title VARCHAR(200) NOT NULL,
file_name VARCHAR(500),
file_size BIGINT,
mime_type VARCHAR(100),
file_hash VARCHAR(100),
storage_ref VARCHAR(500),
scan_status VARCHAR(20) NOT NULL DEFAULT 'pending',
parse_status VARCHAR(20),
index_status VARCHAR(20),
retention_policy JSON,
author VARCHAR(200),
source_url VARCHAR(1000),
tags JSON,
creator VARCHAR(64) NOT NULL DEFAULT '',
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updater VARCHAR(64) NOT NULL DEFAULT '',
update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted BOOLEAN NOT NULL DEFAULT FALSE
);
CREATE INDEX idx_muse_kb_doc_kb ON muse_knowledge_document(kb_id, status);
CREATE INDEX idx_muse_kb_doc_hash ON muse_knowledge_document(file_hash);
CREATE TRIGGER trg_muse_knowledge_document_updated_at
BEFORE UPDATE ON muse_knowledge_document
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
-- 资料版本表
CREATE TABLE muse_knowledge_document_version (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
document_id BIGINT NOT NULL,
version INT NOT NULL,
file_hash VARCHAR(100) NOT NULL,
storage_ref VARCHAR(500),
content_hash VARCHAR(100),
source_snapshot_id BIGINT,
scan_status VARCHAR(20) NOT NULL DEFAULT 'pending',
parse_status VARCHAR(20) NOT NULL DEFAULT 'pending',
processing_status VARCHAR(20) NOT NULL DEFAULT 'pending',
previous_version_id BIGINT,
change_summary JSON,
change_reason VARCHAR(500),
creator VARCHAR(64) NOT NULL DEFAULT '',
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT uk_muse_kb_doc_ver UNIQUE (document_id, version)
);
CREATE INDEX idx_muse_kb_doc_ver_doc ON muse_knowledge_document_version(document_id);
-- 知识实体表(Local KB Canonical)
CREATE TABLE muse_knowledge_entity (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
work_id BIGINT NOT NULL,
kb_id BIGINT,
entity_type VARCHAR(50) NOT NULL,
normalized_name VARCHAR(200) NOT NULL,
scope VARCHAR(50) NOT NULL DEFAULT 'global',
description TEXT,
attributes JSON,
source_snapshot_id BIGINT,
authorization_snapshot_id BIGINT,
source_status VARCHAR(30) NOT NULL DEFAULT 'active',
source_action_policy VARCHAR(30) NOT NULL DEFAULT 'allowed',
source_event_version INT,
lineage_payload JSON,
status VARCHAR(20) NOT NULL DEFAULT 'active',
confidence DOUBLE PRECISION DEFAULT 1.0,
source_type VARCHAR(50),
source_id BIGINT,
creator VARCHAR(64) NOT NULL DEFAULT '',
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updater VARCHAR(64) NOT NULL DEFAULT '',
update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted BOOLEAN NOT NULL DEFAULT FALSE,
CONSTRAINT uk_muse_knowledge_entity UNIQUE (work_id, entity_type, normalized_name, scope)
);
CREATE INDEX idx_muse_knowledge_entity_work_type ON muse_knowledge_entity(work_id, entity_type);
CREATE INDEX idx_muse_knowledge_entity_kb ON muse_knowledge_entity(kb_id);
CREATE TRIGGER trg_muse_knowledge_entity_updated_at
BEFORE UPDATE ON muse_knowledge_entity
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
-- 知识关系表
CREATE TABLE muse_knowledge_relation (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
work_id BIGINT NOT NULL,
source_entity_id BIGINT NOT NULL,
target_entity_id BIGINT NOT NULL,
relation_type VARCHAR(50) NOT NULL,
description VARCHAR(500),
attributes JSON,
source_status VARCHAR(30) NOT NULL DEFAULT 'active',
source_action_policy VARCHAR(30) NOT NULL DEFAULT 'allowed',
creator VARCHAR(64) NOT NULL DEFAULT '',
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updater VARCHAR(64) NOT NULL DEFAULT '',
update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted BOOLEAN NOT NULL DEFAULT FALSE
);
CREATE INDEX idx_muse_knowledge_rel_work ON muse_knowledge_relation(work_id);
CREATE INDEX idx_muse_knowledge_rel_src ON muse_knowledge_relation(source_entity_id);
CREATE INDEX idx_muse_knowledge_rel_tgt ON muse_knowledge_relation(target_entity_id);
CREATE TRIGGER trg_muse_knowledge_relation_updated_at
BEFORE UPDATE ON muse_knowledge_relation
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
-- 知识草稿表
CREATE TABLE muse_knowledge_draft (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
work_id BIGINT NOT NULL,
entity_id BIGINT,
draft_type VARCHAR(30) NOT NULL DEFAULT 'entity',
target_object_id BIGINT,
proposed_changes JSON,
draft_payload JSON,
current_canonical_snapshot JSON,
source_snapshot_id BIGINT,
authorization_snapshot_id BIGINT,
risk_marker_snapshot JSON,
source_status VARCHAR(30),
source_action_policy VARCHAR(30),
status VARCHAR(20) NOT NULL DEFAULT 'pending',
confidence DOUBLE PRECISION,
source_type VARCHAR(50),
source_id BIGINT,
creator VARCHAR(64) NOT NULL DEFAULT '',
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updater VARCHAR(64) NOT NULL DEFAULT '',
update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted BOOLEAN NOT NULL DEFAULT FALSE
);
CREATE INDEX idx_muse_knowledge_draft_work_status ON muse_knowledge_draft(work_id, status, create_time);
CREATE INDEX idx_muse_knowledge_draft_entity ON muse_knowledge_draft(entity_id);
CREATE TRIGGER trg_muse_knowledge_draft_updated_at
BEFORE UPDATE ON muse_knowledge_draft
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
-- 作品-知识库绑定表
CREATE TABLE muse_knowledge_binding (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
work_id BIGINT NOT NULL,
kb_id BIGINT NOT NULL,
binding_type VARCHAR(50) NOT NULL DEFAULT 'read',
binding_scope VARCHAR(50) NOT NULL DEFAULT 'read',
binding_status VARCHAR(30) NOT NULL DEFAULT 'active',
source_snapshot_id BIGINT,
authorization_snapshot_id BIGINT,
source_version INT,
target_version INT,
creator VARCHAR(64) NOT NULL DEFAULT '',
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updater VARCHAR(64) NOT NULL DEFAULT '',
update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted BOOLEAN NOT NULL DEFAULT FALSE,
CONSTRAINT uk_muse_knowledge_binding_work_kb UNIQUE (work_id, kb_id)
);
CREATE INDEX idx_muse_knowledge_binding_work ON muse_knowledge_binding(work_id);
CREATE INDEX idx_muse_knowledge_binding_kb ON muse_knowledge_binding(kb_id);
CREATE TRIGGER trg_muse_knowledge_binding_updated_at
BEFORE UPDATE ON muse_knowledge_binding
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
- Step 7: 编写 Market 模块 DDL (V6)
-- V6__init_market_schema.sql
-- Muse Market 模块初始 Schema(PostgreSQL 16)
-- 市场资产表
CREATE TABLE muse_market_asset (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
name VARCHAR(200) NOT NULL,
description VARCHAR(1000),
asset_type VARCHAR(50) NOT NULL,
category VARCHAR(50),
source_id BIGINT,
publisher_id BIGINT NOT NULL,
listing_status VARCHAR(20) NOT NULL DEFAULT 'draft',
license_type VARCHAR(50) NOT NULL DEFAULT 'standard',
status VARCHAR(20) NOT NULL DEFAULT 'draft',
current_version_id BIGINT,
rating DOUBLE PRECISION DEFAULT 0,
install_count BIGINT NOT NULL DEFAULT 0,
tags JSON,
creator VARCHAR(64) NOT NULL DEFAULT '',
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updater VARCHAR(64) NOT NULL DEFAULT '',
update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted BOOLEAN NOT NULL DEFAULT FALSE
);
CREATE INDEX idx_muse_market_asset_type_status ON muse_market_asset(asset_type, listing_status);
CREATE INDEX idx_muse_market_asset_publisher ON muse_market_asset(publisher_id);
CREATE TRIGGER trg_muse_market_asset_updated_at
BEFORE UPDATE ON muse_market_asset
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
-- 资产版本表
CREATE TABLE muse_market_asset_version (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
asset_id BIGINT NOT NULL,
version VARCHAR(50) NOT NULL,
change_note VARCHAR(500),
version_snapshot JSON,
publish_check_snapshot_id BIGINT,
status VARCHAR(20) NOT NULL DEFAULT 'draft',
creator VARCHAR(64) NOT NULL DEFAULT '',
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT uk_muse_market_asset_ver UNIQUE (asset_id, version)
);
CREATE INDEX idx_muse_market_asset_ver_asset ON muse_market_asset_version(asset_id);
-- 用户安装表
CREATE TABLE muse_market_installation (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
asset_id BIGINT NOT NULL,
asset_version_id BIGINT NOT NULL,
user_id BIGINT NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'installed',
installed_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
uninstalled_at TIMESTAMP,
authorization_snapshot_id BIGINT,
source_snapshot_id BIGINT,
creator VARCHAR(64) NOT NULL DEFAULT '',
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT uk_muse_market_install UNIQUE (user_id, asset_id, asset_version_id)
);
CREATE INDEX idx_muse_market_install_user ON muse_market_installation(user_id);
CREATE INDEX idx_muse_market_install_asset ON muse_market_installation(asset_id);
-- 发布请求表
CREATE TABLE muse_market_publish_request (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
asset_id BIGINT NOT NULL,
asset_version_id BIGINT,
publisher_id BIGINT NOT NULL,
draft_version INT NOT NULL DEFAULT 1,
status VARCHAR(20) NOT NULL DEFAULT 'pending_review',
review_note VARCHAR(1000),
reviewer_id BIGINT,
reviewed_at TIMESTAMP,
submitted_at TIMESTAMP,
creator VARCHAR(64) NOT NULL DEFAULT '',
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updater VARCHAR(64) NOT NULL DEFAULT '',
update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted BOOLEAN NOT NULL DEFAULT FALSE
);
CREATE INDEX idx_muse_market_pub_req_asset ON muse_market_publish_request(asset_id, status);
CREATE INDEX idx_muse_market_pub_req_reviewer ON muse_market_publish_request(reviewer_id);
CREATE TRIGGER trg_muse_market_publish_request_updated_at
BEFORE UPDATE ON muse_market_publish_request
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
-- 申诉表
CREATE TABLE muse_market_appeal (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
asset_id BIGINT NOT NULL,
user_id BIGINT NOT NULL,
appeal_type VARCHAR(50) NOT NULL,
reason TEXT,
status VARCHAR(20) NOT NULL DEFAULT 'pending',
resolution VARCHAR(1000),
resolver_id BIGINT,
resolved_at TIMESTAMP,
creator VARCHAR(64) NOT NULL DEFAULT '',
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updater VARCHAR(64) NOT NULL DEFAULT '',
update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted BOOLEAN NOT NULL DEFAULT FALSE
);
CREATE INDEX idx_muse_market_appeal_asset ON muse_market_appeal(asset_id, status);
CREATE INDEX idx_muse_market_appeal_user ON muse_market_appeal(user_id);
CREATE TRIGGER trg_muse_market_appeal_updated_at
BEFORE UPDATE ON muse_market_appeal
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
-- Handoff 会话表
CREATE TABLE muse_market_handoff (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
token VARCHAR(200) NOT NULL,
source_asset_id BIGINT NOT NULL,
asset_version_id BIGINT,
target_owner_hint VARCHAR(100),
target_endpoint VARCHAR(500),
handoff_token_hash VARCHAR(200) NOT NULL,
license_summary JSON,
governance_summary JSON,
authorization_snapshot_id BIGINT,
source_snapshot_id BIGINT,
status VARCHAR(20) NOT NULL DEFAULT 'active',
consumed_at TIMESTAMP,
canceled_at TIMESTAMP,
jump_audit_id BIGINT,
expires_at TIMESTAMP NOT NULL,
creator VARCHAR(64) NOT NULL DEFAULT '',
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT uk_muse_market_handoff_token UNIQUE (token)
);
CREATE INDEX idx_muse_market_handoff_asset ON muse_market_handoff(source_asset_id);
CREATE TRIGGER trg_muse_market_handoff_updated_at
BEFORE UPDATE ON muse_market_handoff
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
- Step zero: 更新 Flyway migration 顺序引用
确保 update_updated_at_column() 函数在第一个 migration 文件中创建,后续所有 migration 文件可直接引用。触发器创建语句包含在每个表的 DDL 文件中。
- Step 8: 配置 Flyway
修改 muse-cloud/muse-server/src/main/resources/application.yaml,确保 Flyway 启用并指向 sql/muse 目录。
- Step 9: 运行 Migration 并验证
cd muse-cloud && mvn flyway:migrate -pl muse-server
验证: 所有表创建成功,索引正确。
- Step 10: 提交
git add muse-cloud/sql/
git commit -m "feat(db): 添加 6 个模块全量 DDL + Flyway 迁移配置"
Step 3: 业务模块骨架
Task 3.1: 创建 Content 模块
文件:
-
创建:
muse-cloud/muse-module-content/pom.xml -
创建:
muse-cloud/muse-module-content/muse-module-content-api/pom.xml -
创建:
muse-cloud/muse-module-content/muse-module-content-server/pom.xml -
创建: API 接口包
com.muse.module.content.api -
创建: Server 包结构
com.muse.module.content.controller/service/dal -
Step 1: 创建父 pom.xml
<!-- muse-cloud/muse-module-content/pom.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.muse</groupId>
<artifactId>muse</artifactId>
<version>${revision}</version>
</parent>
<artifactId>muse-module-content</artifactId>
<packaging>pom</packaging>
<name>Muse Module Content</name>
<description>作品、章节、Block、导出模块</description>
<modules>
<module>muse-module-content-api</module>
<module>muse-module-content-server</module>
</modules>
</project>
- Step 2: 创建 api 子模块 pom.xml
<!-- muse-cloud/muse-module-content/muse-module-content-api/pom.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.muse</groupId>
<artifactId>muse-module-content</artifactId>
<version>${revision}</version>
</parent>
<artifactId>muse-module-content-api</artifactId>
<packaging>jar</packaging>
<name>Muse Module Content API</name>
<description>Content 模块 API 接口定义(Feign 接口 + DTO)</description>
<dependencies>
<dependency>
<groupId>com.muse</groupId>
<artifactId>muse-common</artifactId>
</dependency>
</dependencies>
</project>
- Step 3: 创建 server 子模块 pom.xml
<!-- muse-cloud/muse-module-content/muse-module-content-server/pom.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.muse</groupId>
<artifactId>muse-module-content</artifactId>
<version>${revision}</version>
</parent>
<artifactId>muse-module-content-server</artifactId>
<packaging>jar</packaging>
<name>Muse Module Content Server</name>
<description>Content 模块服务实现</description>
<dependencies>
<dependency>
<groupId>com.muse</groupId>
<artifactId>muse-module-content-api</artifactId>
</dependency>
<dependency>
<groupId>com.muse</groupId>
<artifactId>muse-module-system-api</artifactId>
</dependency>
<!-- Spring Boot Starter Web, MyBatis, PostgreSQL, Testcontainers -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>postgresql</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
- Step 4: 创建 API 接口类 (Feign)
// muse-module-content/muse-module-content-api/src/main/java/com/muse/module/content/api/ContentApi.java
package com.muse.module.content.api;
import com.muse.module.content.api.dto.*;
import com.muse.common.result.CommonResult;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* Content 模块 Feign API 接口
* 对应 OpenAPI: content/openapi.yaml
*/
@FeignClient(name = "muse-module-content")
public interface ContentApi {
// ===== Work =====
@GetMapping("/app-api/muse/works")
CommonResult<PageResult<WorkSummaryDTO>> listWorks(
@RequestParam("pageNo") Integer pageNo,
@RequestParam("pageSize") Integer pageSize,
@RequestParam(value = "status", required = false) String status
);
@PostMapping("/app-api/muse/works")
CommonResult<WorkDTO> createWork(@RequestBody CreateWorkRequest request);
@GetMapping("/app-api/muse/works/{workId}")
CommonResult<WorkDTO> getWork(@PathVariable("workId") Long workId);
@PutMapping("/app-api/muse/works/{workId}")
CommonResult<Void> updateWork(@PathVariable("workId") Long workId, @RequestBody UpdateWorkRequest request);
@DeleteMapping("/app-api/muse/works/{workId}")
CommonResult<Void> deleteWork(@PathVariable("workId") Long workId);
// ===== Chapter =====
@GetMapping("/app-api/muse/works/{workId}/chapters")
CommonResult<List<ChapterDTO>> listChapters(@PathVariable("workId") Long workId);
@PostMapping("/app-api/muse/works/{workId}/chapters")
CommonResult<ChapterDTO> createChapter(@PathVariable("workId") Long workId, @RequestBody CreateChapterRequest request);
@GetMapping("/app-api/muse/chapters/{chapterId}")
CommonResult<ChapterDetailDTO> getChapter(@PathVariable("chapterId") Long chapterId);
@PutMapping("/app-api/muse/chapters/{chapterId}")
CommonResult<Void> updateChapter(@PathVariable("chapterId") Long chapterId, @RequestBody UpdateChapterRequest request);
@DeleteMapping("/app-api/muse/chapters/{chapterId}")
CommonResult<Void> deleteChapter(@PathVariable("chapterId") Long chapterId);
// ===== Block =====
@GetMapping("/app-api/muse/chapters/{chapterId}/blocks")
CommonResult<List<BlockDTO>> listBlocks(@PathVariable("chapterId") Long chapterId);
@PostMapping("/app-api/muse/chapters/{chapterId}/blocks")
CommonResult<BlockDTO> createBlock(@PathVariable("chapterId") Long chapterId, @RequestBody CreateBlockRequest request);
@PutMapping("/app-api/muse/blocks/{blockId}")
CommonResult<SaveBlockResultDTO> saveBlock(@PathVariable("blockId") Long blockId, @RequestBody SaveBlockRequest request);
@DeleteMapping("/app-api/muse/blocks/{blockId}")
CommonResult<Void> deleteBlock(@PathVariable("blockId") Long blockId);
}
- Step 5: 创建 DTO 类
从 docs/api-contracts/generated/java/com/muse/dto/ 复制 Content 相关 DTO 到 muse-module-content-api/src/main/java/com/muse/module/content/api/dto/。
- Step 6: 创建 Server 包结构
mkdir -p muse-cloud/muse-module-content/muse-module-content-server/src/main/java/com/muse/module/content/{controller,service,dal/{mysql,convert}}
mkdir -p muse-cloud/muse-module-content/muse-module-content-server/src/test/java/com/muse/module/content
- Step 7: 在父 pom.xml 注册模块
# 在 muse-cloud/pom.xml 的 <modules> 中添加
# <module>muse-module-content</module>
- Step 8: 编译验证
cd muse-cloud && mvn compile -pl muse-module-content -am
- Step 9: 提交
git add muse-cloud/muse-module-content/ muse-cloud/pom.xml
git commit -m "feat(content): 创建 Content 模块骨架(api+server)"
Task 3.2-3.6: 创建其余 5 个业务模块骨架
按相同模式创建 Account, Meta, AI, Knowledge, Market 五个模块。每个模块:
- 创建 api/server 子模块 pom.xml
- 创建 Feign API 接口类(对齐 OpenAPI 定义)
- 从
docs/api-contracts/generated/java/com/muse/dto/复制 DTO - 创建 server 包结构(controller/service/dal)
- 在父 pom.xml 注册
- 编译验证
每个模块单独提交。
Step 4: API 实现(按依赖顺序)
Task 4.1: Content API 实现
依赖: system(用户认证)
- Step 1: 编写 WorkService
// muse-module-content-server/src/main/java/com/muse/module/content/service/WorkService.java
package com.muse.module.content.service;
import com.muse.module.content.api.dto.*;
import org.springframework.data.domain.Page;
/**
* 作品服务接口
*/
public interface WorkService {
Page<WorkSummaryDTO> listWorks(Long userId, Integer pageNo, Integer pageSize, String status);
WorkDTO createWork(Long userId, CreateWorkRequest request);
WorkDTO getWork(Long workId);
void updateWork(Long workId, UpdateWorkRequest request);
void deleteWork(Long workId);
}
- Step 2: 编写 WorkService 实现
// muse-module-content-server/src/main/java/com/muse/module/content/service/impl/WorkServiceImpl.java
package com.muse.module.content.service.impl;
import com.muse.module.content.dal.mysql.WorkMapper;
import com.muse.module.content.dal.dataobject.WorkDO;
import com.muse.module.content.service.WorkService;
import com.muse.module.content.api.dto.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class WorkServiceImpl implements WorkService {
private final WorkMapper workMapper;
public WorkServiceImpl(WorkMapper workMapper) {
this.workMapper = workMapper;
}
@Override
public Page<WorkSummaryDTO> listWorks(Long userId, Integer pageNo, Integer pageSize, String status) {
// 分页查询作品列表
}
@Override
@Transactional
public WorkDTO createWork(Long userId, CreateWorkRequest request) {
WorkDO work = new WorkDO();
work.setTitle(request.getTitle());
work.setDescription(request.getDescription());
work.setGenre(request.getGenre());
work.setCreator(String.valueOf(userId));
workMapper.insert(work);
return convertToDTO(work);
}
// ... 其余方法
}
- Step 3: 编写 WorkMapper (MyBatis)
// muse-module-content-server/src/main/java/com/muse/module/content/dal/mysql/WorkMapper.java
package com.muse.module.content.dal.mysql;
import com.muse.framework.mybatis.core.mapper.BaseMapperX;
import com.muse.module.content.dal.dataobject.WorkDO;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface WorkMapper extends BaseMapperX<WorkDO> {
}
- Step 4: 编写 WorkController
// muse-module-content-server/src/main/java/com/muse/module/content/controller/WorkController.java
package com.muse.module.content.controller;
import com.muse.module.content.service.WorkService;
import com.muse.module.content.api.dto.*;
import com.muse.common.result.CommonResult;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/app-api/muse/works")
public class WorkController {
private final WorkService workService;
public WorkController(WorkService workService) {
this.workService = workService;
}
@GetMapping
public CommonResult<PageResult<WorkSummaryDTO>> listWorks(
@RequestParam(defaultValue = "1") Integer pageNo,
@RequestParam(defaultValue = "20") Integer pageSize,
@RequestParam(required = false) String status
) {
Long userId = SecurityUtils.getLoginUserId();
return CommonResult.success(workService.listWorks(userId, pageNo, pageSize, status));
}
@PostMapping
public CommonResult<WorkDTO> createWork(@RequestBody CreateWorkRequest request) {
Long userId = SecurityUtils.getLoginUserId();
return CommonResult.success(workService.createWork(userId, request));
}
// ... 其余端点
}
- Step 5: 编写集成测试
// muse-module-content-server/src/test/java/com/muse/module/content/WorkControllerTest.java
package com.muse.module.content;
import com.muse.module.content.api.dto.*;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Testcontainers
class WorkControllerTest {
@Container
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:16-alpine")
.withDatabaseName("muse_test")
.withUsername("test")
.withPassword("test");
@Autowired
private TestRestTemplate restTemplate;
@Test
void shouldCreateAndGetWork() {
// 创建作品
CreateWorkRequest request = new CreateWorkRequest();
request.setTitle("测试作品");
ResponseEntity<CommonResult> createResp = restTemplate.postForEntity(
"/app-api/muse/works", request, CommonResult.class
);
assertEquals(HttpStatus.OK, createResp.getStatusCode());
assertNotNull(createResp.getBody().getData());
}
}
- Step 6: 运行测试
cd muse-cloud && mvn test -pl muse-module-content/muse-module-content-server
预期: 集成测试通过。
- Step 7: 提交
git add muse-cloud/muse-module-content/
git commit -m "feat(content): Content API 实现(Work CRUD + 集成测试)"
Task 4.2: Chapter/Block API 实现
按相同 TDD 模式实现章节和 Block CRUD API:
- 编写测试(验证 CRUD + 乐观锁冲突 + 排序逻辑)
- 编写 Service + Controller + Mapper
- 测试通过
- 提交
Task 4.3: Block 保存(乐观锁 + 版本管理)
核心:PUT /app-api/muse/blocks/{blockId} 实现乐观锁更新。
@Transactional
public SaveBlockResultDTO saveBlock(Long userId, Long blockId, SaveBlockRequest request) {
// 1. 查询当前 Block
BlockDO block = blockMapper.selectById(blockId);
if (block == null) throw new ResourceNotFoundException("Block not found");
// 2. 乐观锁校验:expectedRevision 必须匹配
if (!request.getExpectedRevision().equals(block.getRevision())) {
throw new RevisionConflictException(block.getRevision());
}
// 3. 更新 Block 内容和版本号
block.setContent(request.getContent());
block.setRevision(block.getRevision() + 1);
block.setUpdater(String.valueOf(userId));
blockMapper.updateById(block);
// 4. 写入来源归因
BlockSourceAttributionDO attribution = new BlockSourceAttributionDO();
attribution.setBlockId(blockId);
attribution.setRevision(block.getRevision());
attribution.setSourceType(request.getSourceType());
sourceAttributionMapper.insert(attribution);
// 5. 返回新版本号
SaveBlockResultDTO result = new SaveBlockResultDTO();
result.setNewRevision(block.getRevision());
return result;
}
Task 4.4-4.11: 其余 Content 接口实现
按 OpenAPI 顺序逐步实现: 4.4: Source Attribution 接口 4.5: Meta Projections 接口 4.6: Planning 接口(规划查询/保存/候选创建/确认/丢弃) 4.7: Style Check 接口 4.8: Import 接口(上传/解析/确认/批量确认) 4.9: Export 接口(创建任务/查询/下载) 4.10: Admin Content 接口(列表/详情/风险治理) 4.11: apply-suggestion 接口(接受 AI 候选后写入 Canonical)
每个子任务:TDD → 实现 → 测试通过 → 提交。
Task 4.12: Account API 实现
依赖: content 模块不相关,可独立开始。
实现权益查询、配额查询、用量摘要、安全事件、个人中心等接口。 每个子任务按 TDD 模式:测试 → Service → Controller → Mapper → 验证 → 提交。
Task 4.13: Meta API 实现
依赖: content + account 已稳定。
实现 MetaSchema CRUD、版本管理、草稿/校验/影响预览/发布/激活/回滚/废弃/灰度、保护节点查询、功能链路管理。
Task 4.14: AI API 实现
依赖: content + meta 已稳定。
实现 AI 任务提交、SSE 流式输出、候选管理(列表/详情/接受/拒绝)、智能体管理(创建/版本/试用)、槽位绑定、Admin Prompt/Agent/质量策略管理、来源状态查询/重验。
Task 4.15: Knowledge API 实现
依赖: content + meta 已稳定。
实现知识实体/关系、自动确认逻辑、全局KB管理、用户KB管理、文档上传/解析、发布流程、知识草稿确认/忽略、知识绑定/解绑。
Task 4.16: Market API 实现
依赖: account + ai + knowledge 已稳定。
实现市场资产浏览/推荐、收藏、购买/安装、Handoff 创建/消费、发布草稿/检查/提交、管理端审核/下架/召回、申诉处理。
完成标准
- 6 个模块全部 API 自测通过
- Domain 层单元测试覆盖率 ≥90%(JUnit 5 + Mockito)
- Application 层集成测试覆盖率 ≥80%(Testcontainers + PostgreSQL)
- ArchUnit 规则通过(AI grant/runtime 包隔离检查)
- Checkstyle / SpotBugs lint 通过
- Maven build 成功(含所有测试)