31 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 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 五个模块。每个模块:
- 创建 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 成功(含所有测试)