feat(content): D1-3 work schema 选项端点(前端选 schema)+ 单测[P1]

D1 落地链第三环:content app-api 暴露 GET /works/schema-options(经 MetaSchemaQueryApi 拉 active schema,不跨 BC 查 meta.dal),供 studio work 创建/编辑选 schema。WorkSchemaOptionRespVO(id/schemaKey/displayName/targetType);端点置 /works/{workId} 之前(字面量段优先)。

验证:ContentAppServiceTest 18/18(+1 listWorkSchemaOptions)。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
lili 2026-06-21 00:52:07 -07:00
parent cd85d9d649
commit 0fd7a60f66
5 changed files with 70 additions and 0 deletions

View File

@ -17,6 +17,11 @@ public interface ContentAppService {
*/
PageResult<WorkSummaryRespVO> listWorks(Long userId, Integer pageNo, Integer pageSize, String status);
/**
* 列出作品可绑定的 active Meta Schema 选项(targetType 为 null 则全部 active)。
*/
List<WorkSchemaOptionRespVO> listWorkSchemaOptions(String targetType);
/**
* 创建作品。
*/

View File

@ -9,6 +9,7 @@ import cn.iocoder.muse.module.content.convert.ContentConvert;
import cn.iocoder.muse.module.content.dal.dataobject.*;
import cn.iocoder.muse.module.content.dal.mysql.*;
import cn.iocoder.muse.module.content.domain.ContentRevisionGuard;
import cn.iocoder.muse.module.meta.api.schema.MetaSchemaBriefDTO;
import cn.iocoder.muse.module.meta.api.schema.MetaSchemaQueryApi;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
@ -60,6 +61,23 @@ public class ContentAppServiceImpl implements ContentAppService {
return new PageResult<>(page.getList().stream().map(ContentConvert::toWorkSummary).toList(), page.getTotal());
}
@Override
public List<WorkSchemaOptionRespVO> listWorkSchemaOptions(String targetType) {
// 经 meta-api 端口拉取 active schema(不跨 BC 查 meta.dal),映射为前端 work 创建选项。
return metaSchemaQueryApi.listActiveSchemasByTargetType(targetType).stream()
.map(this::toWorkSchemaOption)
.toList();
}
private WorkSchemaOptionRespVO toWorkSchemaOption(MetaSchemaBriefDTO brief) {
WorkSchemaOptionRespVO option = new WorkSchemaOptionRespVO();
option.setId(brief.id());
option.setSchemaKey(brief.schemaKey());
option.setDisplayName(brief.displayName());
option.setTargetType(brief.targetType());
return option;
}
@Override
@Transactional(rollbackFor = Exception.class)
public WorkRespVO createWork(Long userId, WorkCreateReqVO reqVO) {

View File

@ -41,6 +41,15 @@ public class AppContentController {
return success(contentAppService.listWorks(getLoginUserId(), pageNo, pageSize, status));
}
@GetMapping("/works/schema-options")
@Operation(summary = "查询作品可绑定的 Meta Schema 选项")
public CommonResult<List<WorkSchemaOptionRespVO>> listWorkSchemaOptions(
@RequestHeader(value = "X-API-Version", required = false) String apiVersion,
@RequestParam(required = false) String targetType) {
ContentApiVersionGuard.requireVersion(apiVersion);
return success(contentAppService.listWorkSchemaOptions(targetType));
}
@PostMapping("/works")
@Operation(summary = "创建作品")
public CommonResult<WorkRespVO> createWork(@RequestHeader(value = "X-API-Version", required = false) String apiVersion,

View File

@ -0,0 +1,25 @@
package cn.iocoder.muse.module.content.controller.app.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* 作品可绑定的 Meta Schema 选项(work 创建/编辑时供用户选择)。
*/
@Schema(description = "作品可绑定的 Meta Schema 选项")
@Data
public class WorkSchemaOptionRespVO {
@Schema(description = "Schema id(绑定时回传给 createWork/updateWork 的 schemaId)")
private Long id;
@Schema(description = "Schema 业务键")
private String schemaKey;
@Schema(description = "展示名")
private String displayName;
@Schema(description = "目标类型")
private String targetType;
}

View File

@ -188,6 +188,19 @@ class ContentAppServiceTest extends BaseMockitoUnitTest {
verify(workMapper).updateById(argThat((WorkDO w) -> Long.valueOf(7L).equals(w.getWorkSchemaId())));
}
@Test
void should_listWorkSchemaOptions_fromMetaPort() {
// 经 meta 端口拉取 active schema 选项,映射为前端可选项(work 创建/编辑用)。
when(metaSchemaQueryApi.listActiveSchemasByTargetType("work")).thenReturn(List.of(
new MetaSchemaBriefDTO(5L, "work_core", "作品核心", "work")));
List<WorkSchemaOptionRespVO> options = contentAppService.listWorkSchemaOptions("work");
assertEquals(1, options.size());
assertEquals(5L, options.getFirst().getId());
assertEquals("work_core", options.getFirst().getSchemaKey());
}
@Test
void should_replayFirstSaveBlockResult_when_blockChangedAfterFirstExecution() {
BlockSaveReqVO reqVO = new BlockSaveReqVO();