# 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: 检查当前模块结构** ```bash 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** ```bash find muse-cloud -name "*.java" -path "*/com/muse/*" | head -10 ``` 若仍为 `cn.iocoder.yudao`,需全局替换。 - [ ] **Step 3: 确认 pom.xml 中 Java 版本** ```bash grep -r "java.version\|maven.compiler" muse-cloud/pom.xml | head -5 ``` 确保为 Java 21。 - [ ] **Step 4: 验证可编译** ```bash 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** ```yaml 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: 启动并验证** ```bash cd muse-cloud && docker compose up -d docker compose ps ``` 预期输出: postgres 和 redis 均为 `running` 状态。 - [ ] **Step 3: 提交** ```bash 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 数据源配置** ```bash 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: 提交** ```bash 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** ```gitignore # 团队共享 IDE 配置 # 个人配置不入库 /shelf/ /workspace.xml /httpRequests/ /dataSources/ /dataSources.local.xml ``` - [ ] **Step 2: 创建代码风格配置** ```bash mkdir -p muse-cloud/.idea/codeStyles cat > muse-cloud/.idea/codeStyles/Project.xml << 'XML' XML ``` - [ ] **Step 3: 提交** ```bash 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 目录** ```bash mkdir -p muse-cloud/sql/muse ``` - [ ] **Step 1b: 创建 updated_at 触发器函数(PostgreSQL 通用)** 所有业务表通过触发器自动更新 `update_time`,无需 `ON UPDATE CURRENT_TIMESTAMP`。 ```sql -- 触发器函数:自动更新 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_` 前缀),添加必备审计字段。 ```sql -- 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)** ```sql -- 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)** ```sql -- 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)** ```sql -- 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)** ```sql -- 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)** ```sql -- 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 并验证** ```bash cd muse-cloud && mvn flyway:migrate -pl muse-server ``` 验证: 所有表创建成功,索引正确。 - [ ] **Step 10: 提交** ```bash 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** ```xml 4.0.0 com.muse muse ${revision} muse-module-content pom Muse Module Content 作品、章节、Block、导出模块 muse-module-content-api muse-module-content-server ``` - [ ] **Step 2: 创建 api 子模块 pom.xml** ```xml 4.0.0 com.muse muse-module-content ${revision} muse-module-content-api jar Muse Module Content API Content 模块 API 接口定义(Feign 接口 + DTO) com.muse muse-common ``` - [ ] **Step 3: 创建 server 子模块 pom.xml** ```xml 4.0.0 com.muse muse-module-content ${revision} muse-module-content-server jar Muse Module Content Server Content 模块服务实现 com.muse muse-module-content-api com.muse muse-module-system-api org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-test test org.testcontainers postgresql test ``` - [ ] **Step 4: 创建 API 接口类 (Feign)** ```java // 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> listWorks( @RequestParam("pageNo") Integer pageNo, @RequestParam("pageSize") Integer pageSize, @RequestParam(value = "status", required = false) String status ); @PostMapping("/app-api/muse/works") CommonResult createWork(@RequestBody CreateWorkRequest request); @GetMapping("/app-api/muse/works/{workId}") CommonResult getWork(@PathVariable("workId") Long workId); @PutMapping("/app-api/muse/works/{workId}") CommonResult updateWork(@PathVariable("workId") Long workId, @RequestBody UpdateWorkRequest request); @DeleteMapping("/app-api/muse/works/{workId}") CommonResult deleteWork(@PathVariable("workId") Long workId); // ===== Chapter ===== @GetMapping("/app-api/muse/works/{workId}/chapters") CommonResult> listChapters(@PathVariable("workId") Long workId); @PostMapping("/app-api/muse/works/{workId}/chapters") CommonResult createChapter(@PathVariable("workId") Long workId, @RequestBody CreateChapterRequest request); @GetMapping("/app-api/muse/chapters/{chapterId}") CommonResult getChapter(@PathVariable("chapterId") Long chapterId); @PutMapping("/app-api/muse/chapters/{chapterId}") CommonResult updateChapter(@PathVariable("chapterId") Long chapterId, @RequestBody UpdateChapterRequest request); @DeleteMapping("/app-api/muse/chapters/{chapterId}") CommonResult deleteChapter(@PathVariable("chapterId") Long chapterId); // ===== Block ===== @GetMapping("/app-api/muse/chapters/{chapterId}/blocks") CommonResult> listBlocks(@PathVariable("chapterId") Long chapterId); @PostMapping("/app-api/muse/chapters/{chapterId}/blocks") CommonResult createBlock(@PathVariable("chapterId") Long chapterId, @RequestBody CreateBlockRequest request); @PutMapping("/app-api/muse/blocks/{blockId}") CommonResult saveBlock(@PathVariable("blockId") Long blockId, @RequestBody SaveBlockRequest request); @DeleteMapping("/app-api/muse/blocks/{blockId}") CommonResult 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 包结构** ```bash 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 注册模块** ```bash # 在 muse-cloud/pom.xml 的 中添加 # muse-module-content ``` - [ ] **Step 8: 编译验证** ```bash cd muse-cloud && mvn compile -pl muse-module-content -am ``` - [ ] **Step 9: 提交** ```bash 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 五个模块。每个模块: 1. 创建 api/server 子模块 pom.xml 2. 创建 Feign API 接口类(对齐 OpenAPI 定义) 3. 从 `docs/api-contracts/generated/java/com/muse/dto/` 复制 DTO 4. 创建 server 包结构(controller/service/dal) 5. 在父 pom.xml 注册 6. 编译验证 每个模块单独提交。 --- ## Step 4: API 实现(按依赖顺序) ### Task 4.1: Content API 实现 **依赖:** system(用户认证) - [ ] **Step 1: 编写 WorkService** ```java // 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 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 实现** ```java // 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 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)** ```java // 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 { } ``` - [ ] **Step 4: 编写 WorkController** ```java // 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> 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 createWork(@RequestBody CreateWorkRequest request) { Long userId = SecurityUtils.getLoginUserId(); return CommonResult.success(workService.createWork(userId, request)); } // ... 其余端点 } ``` - [ ] **Step 5: 编写集成测试** ```java // 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 createResp = restTemplate.postForEntity( "/app-api/muse/works", request, CommonResult.class ); assertEquals(HttpStatus.OK, createResp.getStatusCode()); assertNotNull(createResp.getBody().getData()); } } ``` - [ ] **Step 6: 运行测试** ```bash cd muse-cloud && mvn test -pl muse-module-content/muse-module-content-server ``` 预期: 集成测试通过。 - [ ] **Step 7: 提交** ```bash 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: 1. 编写测试(验证 CRUD + 乐观锁冲突 + 排序逻辑) 2. 编写 Service + Controller + Mapper 3. 测试通过 4. 提交 ### Task 4.3: Block 保存(乐观锁 + 版本管理) 核心:`PUT /app-api/muse/blocks/{blockId}` 实现乐观锁更新。 ```java @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 成功(含所有测试)