oh-my-muse/docs/superpowers/plans/2026-05-24-P1-muse-cloud.md

31 KiB
Raw Blame History

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 业务模块,实现全量 API229 接口),提供可调用的后端服务。

架构: 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

确保 urljdbc:postgresql://localhost:5432/musedriver 为 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 2: 编写 Content 模块 DDL (V1)

design-docs/后端-04a-完整建表SQL.sql 提取 Content 相关表Work, Chapter, Block, BlockSourceAttribution 等),调整表名为 Yudao 约定(加 muse_ 前缀),添加必备审计字段。

-- V1__init_content_schema.sql
-- Muse Content 模块初始 Schema
-- 对应设计文档: design-docs/后端-04-统一数据库Schema-v1.md

-- ============================================================
-- 作品表
-- ============================================================
CREATE TABLE muse_work (
    id BIGINT NOT NULL AUTO_INCREMENT,
    title VARCHAR(200) NOT NULL,
    description VARCHAR(2000),
    genre VARCHAR(100),
    cover_image_url VARCHAR(500),
    status VARCHAR(20) NOT NULL DEFAULT 'draft',
    word_count INT NOT NULL DEFAULT 0,
    chapter_count INT NOT NULL DEFAULT 0,
    creator VARCHAR(64) NOT NULL DEFAULT '',
    create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updater VARCHAR(64) NOT NULL DEFAULT '',
    update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    deleted BIT NOT NULL DEFAULT FALSE,
    tenant_id BIGINT NOT NULL DEFAULT 0,
    PRIMARY KEY (id)
);

CREATE INDEX idx_muse_work_creator ON muse_work(creator);
CREATE INDEX idx_muse_work_status ON muse_work(status);

-- ============================================================
-- 章节表
-- ============================================================
CREATE TABLE muse_chapter (
    id BIGINT NOT NULL AUTO_INCREMENT,
    work_id BIGINT NOT NULL,
    title VARCHAR(500) NOT NULL,
    sort_order INT NOT NULL DEFAULT 0,
    status VARCHAR(20) NOT NULL DEFAULT 'draft',
    word_count INT NOT NULL DEFAULT 0,
    block_count INT NOT NULL DEFAULT 0,
    creator VARCHAR(64) NOT NULL DEFAULT '',
    create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updater VARCHAR(64) NOT NULL DEFAULT '',
    update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    deleted BIT NOT NULL DEFAULT FALSE,
    tenant_id BIGINT NOT NULL DEFAULT 0,
    PRIMARY KEY (id)
);

CREATE INDEX idx_muse_chapter_work ON muse_chapter(work_id);

-- ============================================================
-- Block 表
-- ============================================================
CREATE TABLE muse_block (
    id BIGINT NOT NULL AUTO_INCREMENT,
    chapter_id BIGINT NOT NULL,
    block_type VARCHAR(20) NOT NULL,
    title VARCHAR(500),
    content MEDIUMTEXT,
    sort_order INT NOT NULL DEFAULT 0,
    revision INT NOT NULL DEFAULT 1,
    word_count INT NOT NULL DEFAULT 0,
    creator VARCHAR(64) NOT NULL DEFAULT '',
    create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updater VARCHAR(64) NOT NULL DEFAULT '',
    update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    deleted BIT NOT NULL DEFAULT FALSE,
    tenant_id BIGINT NOT NULL DEFAULT 0,
    PRIMARY KEY (id)
);

CREATE INDEX idx_muse_block_chapter ON muse_block(chapter_id);

-- ============================================================
-- Block 来源归因表
-- ============================================================
CREATE TABLE muse_block_source_attribution (
    id BIGINT NOT NULL AUTO_INCREMENT,
    block_id BIGINT NOT NULL,
    revision INT NOT NULL,
    source_type VARCHAR(50) NOT NULL,
    source_id VARCHAR(100),
    source_version INT,
    authorization_snapshot_id VARCHAR(100),
    lineage JSON,
    license_restrictions JSON,
    creator VARCHAR(64) NOT NULL DEFAULT '',
    create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (id)
);

CREATE INDEX idx_muse_block_src_block ON muse_block_source_attribution(block_id);
CREATE INDEX idx_muse_block_src_revision ON muse_block_source_attribution(block_id, revision);
  • Step 3: 编写 Account 模块 DDL (V2)
-- V2__init_account_schema.sql
-- Muse Account 模块初始 Schema

-- 权益表(可变表 + 审计日志)
CREATE TABLE muse_entitlement (
    id BIGINT NOT NULL AUTO_INCREMENT,
    user_id BIGINT NOT NULL,
    resource_type VARCHAR(100) NOT NULL,
    limit_value BIGINT NOT NULL,
    used_value BIGINT NOT NULL DEFAULT 0,
    expires_at DATETIME,
    creator VARCHAR(64) NOT NULL DEFAULT '',
    create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updater VARCHAR(64) NOT NULL DEFAULT '',
    update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    deleted BIT NOT NULL DEFAULT FALSE,
    tenant_id BIGINT NOT NULL DEFAULT 0,
    PRIMARY KEY (id)
);

CREATE INDEX idx_muse_entitlement_user ON muse_entitlement(user_id);

-- 权益审计日志表
CREATE TABLE muse_entitlement_audit_log (
    id BIGINT NOT NULL AUTO_INCREMENT,
    user_id BIGINT NOT NULL,
    resource_type VARCHAR(100) NOT NULL,
    operation_type VARCHAR(20) NOT NULL,
    before_snapshot JSON,
    after_snapshot JSON,
    reason VARCHAR(500),
    command_id VARCHAR(100) NOT NULL,
    correlation_id VARCHAR(100),
    operator VARCHAR(64) NOT NULL DEFAULT '',
    create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (id)
);

CREATE INDEX idx_muse_ent_audit_user ON muse_entitlement_audit_log(user_id);
CREATE INDEX idx_muse_ent_audit_cmd ON muse_entitlement_audit_log(command_id);

-- 配额表
CREATE TABLE muse_quota (
    id BIGINT NOT NULL AUTO_INCREMENT,
    user_id BIGINT NOT NULL,
    resource_type VARCHAR(100) NOT NULL,
    total INT NOT NULL DEFAULT 0,
    remaining INT NOT NULL DEFAULT 0,
    reset_at DATETIME NOT NULL,
    creator VARCHAR(64) NOT NULL DEFAULT '',
    create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updater VARCHAR(64) NOT NULL DEFAULT '',
    update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    deleted BIT NOT NULL DEFAULT FALSE,
    tenant_id BIGINT NOT NULL DEFAULT 0,
    PRIMARY KEY (id)
);

CREATE INDEX idx_muse_quota_user ON muse_quota(user_id);
  • Step 4: 编写 Meta 模块 DDL (V3)
-- V3__init_meta_schema.sql
-- MetaSchema 模块初始 Schema

CREATE TABLE muse_meta_schema (
    id BIGINT NOT NULL AUTO_INCREMENT,
    schema_key VARCHAR(100) NOT NULL,
    display_name VARCHAR(200) NOT NULL,
    field_type VARCHAR(20) NOT NULL,
    scope VARCHAR(20) NOT NULL,
    active_version INT NOT NULL DEFAULT 1,
    status VARCHAR(20) NOT NULL DEFAULT 'active',
    creator VARCHAR(64) NOT NULL DEFAULT '',
    create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updater VARCHAR(64) NOT NULL DEFAULT '',
    update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    deleted BIT NOT NULL DEFAULT FALSE,
    PRIMARY KEY (id),
    UNIQUE KEY uk_schema_key (schema_key)
);

CREATE TABLE muse_meta_schema_version (
    id BIGINT NOT NULL AUTO_INCREMENT,
    schema_id BIGINT NOT NULL,
    version INT NOT NULL,
    default_value JSON,
    is_required BIT NOT NULL DEFAULT FALSE,
    enum_values JSON,
    validation_rules JSON,
    ui_visible BIT NOT NULL DEFAULT TRUE,
    ai_context BIT NOT NULL DEFAULT FALSE,
    user_editable BIT NOT NULL DEFAULT TRUE,
    user_searchable BIT NOT NULL DEFAULT FALSE,
    exportable BIT NOT NULL DEFAULT FALSE,
    status VARCHAR(20) NOT NULL DEFAULT 'draft',
    change_note VARCHAR(500),
    creator VARCHAR(64) NOT NULL DEFAULT '',
    create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (id),
    UNIQUE KEY uk_schema_version (schema_id, version)
);

CREATE INDEX idx_muse_meta_schema_ver_sid ON muse_meta_schema_version(schema_id);

CREATE TABLE muse_protection_node (
    id BIGINT NOT NULL AUTO_INCREMENT,
    node_key VARCHAR(100) NOT NULL,
    display_name VARCHAR(200) NOT NULL,
    required_permissions JSON,
    audit_required BIT NOT NULL DEFAULT TRUE,
    irremovable_reason VARCHAR(500),
    shadow_canonical_boundary VARCHAR(500),
    creator VARCHAR(64) NOT NULL DEFAULT '',
    create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (id),
    UNIQUE KEY uk_protection_node_key (node_key)
);
  • Step 5-7: 编写 AI, Knowledge, Market 模块 DDL (V4-V6)

design-docs/后端-04a-完整建表SQL.sql 提取对应表结构,按 Yudao 约定(muse_ 前缀、审计字段、租户字段)调整。

关键表:

  • AI: muse_ai_generation, muse_ai_suggestion, muse_agent, muse_agent_slot_binding, muse_prompt, muse_quality_policy

  • Knowledge: muse_knowledge_base, muse_knowledge_document, muse_knowledge_entity, muse_knowledge_relation, muse_knowledge_draft, muse_knowledge_binding

  • Market: muse_market_asset, muse_market_installation, muse_market_publish_request, muse_market_appeal

  • 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 五个模块。每个模块:

  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
// 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

  1. 编写测试(验证 CRUD + 乐观锁冲突 + 排序逻辑)
  2. 编写 Service + Controller + Mapper
  3. 测试通过
  4. 提交

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 成功(含所有测试)