Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 64 additions & 7 deletions packages/api/generated/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -602,7 +602,7 @@
"get": {
"operationId": "getBoard",
"summary": "批量获取指定版面",
"description": "根据一个或多个版面 ID 批量返回版面信息,常用于补齐收藏和主题记录中的版面资料。",
"description": "批量查询一个或多个版面,返回值字段与 /board/{boardId} 相同。适合已有多个版面 ID 时一次补齐收藏、主题记录等列表中的版面资料,避免逐个请求。不存在的 ID 会被忽略,全部 ID 均不存在时返回 404;调用方应按 id 关联结果,不应依赖响应顺序。",
"tags": ["Board"],
"security": [],
"x-cc98-risk": "read-only",
Expand All @@ -617,15 +617,15 @@
"items": {
"type": "number"
},
"description": "要查询的版面 ID,可重复传入多个 id 参数。"
"description": "要查询的版面 ID,可重复传入多个 id 参数;重复的 ID 只返回一次。"
},
"required": true,
"description": "要查询的版面 ID,可重复传入多个 id 参数。"
"description": "要查询的版面 ID,可重复传入多个 id 参数;重复的 ID 只返回一次。"
}
],
"responses": {
"200": {
"description": "成功获取指定版面列表",
"description": "成功获取至少一个匹配的版面详情列表",
"content": {
"application/json": {
"schema": {
Expand Down Expand Up @@ -680,7 +680,7 @@
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Board"
"$ref": "#/components/schemas/BoardSummary"
}
}
}
Expand All @@ -703,7 +703,7 @@
"get": {
"operationId": "getBoardBoardId",
"summary": "获取版面详情",
"description": "返回版面公告、简介、统计、访问限制和当前访问者能力登录后还会包含是否已关注等用户相关状态。",
"description": "查询单个版面并直接返回一个 Board 对象;需要同时查询多个版面时应使用 /board/ 批量接口,两者返回相同的版面字段。本接口包含版面公告、简介、统计、访问限制和当前访问者能力登录后还会包含是否已关注等用户相关状态。",
"tags": ["Board"],
"security": [],
"x-cc98-risk": "read-only",
Expand Down Expand Up @@ -8643,13 +8643,70 @@
"description": "分组内的版面概要列表。",
"type": "array",
"items": {
"$ref": "#/components/schemas/Board"
"$ref": "#/components/schemas/BoardSummary"
}
}
},
"additionalProperties": {},
"description": "论坛版面导航中的一级分组。"
},
"BoardSummary": {
"type": "object",
"properties": {
"id": {
"description": "版面 ID。",
"type": "number"
},
"name": {
"description": "版面名称。",
"type": "string"
},
"description": {
"description": "版面简介;未配置时可能为 null。",
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"topicCount": {
"description": "版面主题总数。",
"type": "number"
},
"postCount": {
"description": "版面回复总数。",
"type": "number"
},
"todayCount": {
"description": "版面今日回复数。",
"type": "number"
},
"boardMasters": {
"description": "版主用户名列表。",
"type": "array",
"items": {
"type": "string"
}
},
"anonymousState": {
"description": "匿名发帖模式:0 为不可匿名,1 为只能匿名,2、3 均允许在发主题时选择是否匿名;2 与 3 在回复场景中的具体差异尚未完全确认。",
"type": "number"
},
"showShareTip": {
"description": "是否展示版面分享提示,具体触发场景尚未确认。",
"type": "boolean"
},
"canVote": {
"description": "当前访问者是否可以在版面发布投票主题。",
"type": "boolean"
}
},
"additionalProperties": {},
"description": "版面导航中的版面概要,仅包含 /board/all 实际返回的字段。"
},
"Board": {
"type": "object",
"properties": {
Expand Down
12 changes: 7 additions & 5 deletions packages/api/src/operations/board.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
boardGroupSchema,
boardMutedUserSchema,
boardSchema,
boardSummarySchema,
boardTagDataSchema,
createTopicRequestSchema,
errorCodeSchema,
Expand Down Expand Up @@ -51,13 +52,13 @@ export const boardOperations = defineOperations([
in: "query",
required: true,
schema: z.array(z.number()),
description: "要查询的版面 ID,可重复传入多个 id 参数。",
description: "要查询的版面 ID,可重复传入多个 id 参数;重复的 ID 只返回一次。",
probeValue: 758,
},
],
responses: {
"200": {
description: "成功获取指定版面列表",
description: "成功获取至少一个匹配的版面详情列表",
contentType: "application/json",
schema: z.array(boardSchema),
},
Expand All @@ -71,7 +72,8 @@ export const boardOperations = defineOperations([
risk: "read-only",
verificationStatus: "verified-anonymous",
sources: ["legacy-openapi", "live-probe"],
description: "根据一个或多个版面 ID 批量返回版面信息,常用于补齐收藏和主题记录中的版面资料。",
description:
"批量查询一个或多个版面,返回值字段与 /board/{boardId} 相同。适合已有多个版面 ID 时一次补齐收藏、主题记录等列表中的版面资料,避免逐个请求。不存在的 ID 会被忽略,全部 ID 均不存在时返回 404;调用方应按 id 关联结果,不应依赖响应顺序。",
},
{
method: "GET",
Expand All @@ -93,7 +95,7 @@ export const boardOperations = defineOperations([
"200": {
description: "成功获取名称或简介匹配关键词的版面",
contentType: "application/json",
schema: z.array(boardSchema),
schema: z.array(boardSummarySchema),
},
default: {
description: "API 错误码",
Expand Down Expand Up @@ -140,7 +142,7 @@ export const boardOperations = defineOperations([
verificationStatus: "verified-anonymous",
sources: ["legacy-openapi", "live-probe"],
description:
"返回版面公告、简介、统计、访问限制和当前访问者能力登录后还会包含是否已关注等用户相关状态。",
"查询单个版面并直接返回一个 Board 对象;需要同时查询多个版面时应使用 /board/ 批量接口,两者返回相同的版面字段。本接口包含版面公告、简介、统计、访问限制和当前访问者能力登录后还会包含是否已关注等用户相关状态。",
},
{
method: "GET",
Expand Down
21 changes: 20 additions & 1 deletion packages/api/src/schemas/board.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,25 @@ export const boardSchema = z
});
export type Board = z.infer<typeof boardSchema>;

export const boardSummarySchema = boardSchema
.pick({
id: true,
name: true,
description: true,
topicCount: true,
postCount: true,
todayCount: true,
boardMasters: true,
anonymousState: true,
showShareTip: true,
canVote: true,
})
.meta({
id: "BoardSummary",
description: "版面导航中的版面概要,仅包含 /board/all 实际返回的字段。",
});
export type BoardSummary = z.infer<typeof boardSummarySchema>;

export const tagSchema = z
.looseObject({
id: z.number().optional().meta({ description: "全局标签 ID。" }),
Expand Down Expand Up @@ -132,7 +151,7 @@ export const boardGroupSchema = z
name: z.string().optional().meta({ description: "版面分组名称。" }),
order: z.number().optional().meta({ description: "版面分组的展示顺序。" }),
masters: z.array(z.string()).optional().meta({ description: "分区主管用户名列表。" }),
boards: z.array(boardSchema).optional().meta({ description: "分组内的版面概要列表。" }),
boards: z.array(boardSummarySchema).optional().meta({ description: "分组内的版面概要列表。" }),
})
.meta({ id: "BoardGroup", description: "论坛版面导航中的一级分组。" });
export type BoardGroup = z.infer<typeof boardGroupSchema>;
Expand Down
32 changes: 32 additions & 0 deletions packages/api/tests/contract.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import openIdOpenapi from "../generated/openid.openapi.json" with { type: "json"
import {
endpointCatalog,
boardEventPageSchema,
boardSummarySchema,
boardSchema,
createPostRequestSchema,
favoriteTopicGroupSchema,
Expand Down Expand Up @@ -108,6 +109,37 @@ describe("API 契约基线", () => {
});
});

it("版面概要不混入详情字段", async () => {
const boardAllFixture = JSON.parse(
await readFile(
resolve(import.meta.dirname, "../fixtures/anonymous/getBoardAll.json"),
"utf8",
),
);
const boardSearchFixture = JSON.parse(
await readFile(
resolve(import.meta.dirname, "../fixtures/anonymous/getBoardSearch.json"),
"utf8",
),
);
const summary = boardSummarySchema.parse(boardAllFixture[0].boards[0]);
const summaryProperties = openapi.components.schemas.BoardSummary.properties;

expect(summary.showShareTip).toBeTypeOf("boolean");
expect(boardSummarySchema.array().safeParse(boardSearchFixture).success).toBe(true);
expect(summaryProperties).toHaveProperty("showShareTip");
expect(summaryProperties).not.toHaveProperty("logoUri");
expect(openapi.components.schemas.BoardGroup.properties.boards.items).toEqual({
$ref: "#/components/schemas/BoardSummary",
});
expect(
openapi.paths["/board/search"].get.responses["200"].content["application/json"].schema,
).toEqual({
type: "array",
items: { $ref: "#/components/schemas/BoardSummary" },
});
});

it("版务记录分页契约保留总数与事件列表", async () => {
const fixture = JSON.parse(
await readFile(
Expand Down