feat: 完成分类、worksheet 接口开发,资源迁移到七牛

This commit is contained in:
R524809
2026-10-16 17:32:23 +08:00
parent e6ded00a4c
commit 8827110f2f
107 changed files with 12775 additions and 125 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
# 公共接口契约
仅包含可公开的基础响应和健康检查类型。服务端通过 `workspace:*` 引用,使用 `import type` 避免引入无必要的运行时逻辑。
包含可公开的基础响应、健康检查、微信登录请求/结果、本人摘要、分类查询/创建/更新、题型查询/公开 DTO/分页及有限错误原因类型。题型计数为含 seed 的十进制展示字符串,previewImg 可空。服务端通过 `workspace:*` 引用,使用 `import type` 避免引入无必要的运行时逻辑。
后续可以加入公开请求/响应类型及枚举,但禁止包含 Prisma Client、数据库模型、密钥、管理权限实现或私有生成器。
+162
View File
@@ -10,3 +10,165 @@ export interface HealthStatus {
status: 'ok';
service: 'doodle-store-server';
}
/** 安全、有限的失败原因;HTTP code 仍承担错误大类,不暴露内部异常。 */
export type ApiErrorReason =
| 'AUTH_REQUIRED'
| 'AUTH_INVALID'
| 'AUTH_EXPIRED'
| 'AUTH_REVOKED'
| 'ACCOUNT_NOT_FOUND'
| 'ACCOUNT_DISABLED'
| 'WECHAT_CODE_INVALID'
| 'WECHAT_LOGIN_BLOCKED'
| 'RATE_LIMITED'
| 'AUTH_UPSTREAM_UNAVAILABLE'
| 'DATABASE_UNAVAILABLE';
export interface ApiError {
code: number;
message: string;
requestId?: string;
reason?: ApiErrorReason;
}
export type WechatLoginMode = 'start' | 'resume';
export interface WechatLoginRequest {
code: string;
mode: WechatLoginMode;
}
/** 显式白名单摘要,不含微信身份或凭证版本。头像签名由后续资产模块补齐。 */
export interface UserSummary {
id: string;
nickName: string | null;
avatarUrl: string | null;
avatarUrlExpiresAt: string | null;
createdAt: string;
}
export interface WechatLoginResult {
accessToken: string;
tokenType: 'Bearer';
expiresIn: number;
expiresAt: string;
user: UserSummary;
}
export interface CurrentUserProfile extends UserSummary {
role: 'user' | 'admin';
}
export type ContentStatus = 'draft' | 'active' | 'hidden';
/** 分类 ID 沿用云数据库原值;列表为扁平结构,parentId=null 表示根分类。 */
export interface Category {
status: ContentStatus;
id: string;
name: string;
icon: string;
color: string;
sortOrder: number;
parentId: string | null;
createdAt: string;
updatedAt: string;
}
export interface CreateCategoryRequest {
status?: ContentStatus;
id: string;
name: string;
icon: string;
color: string;
sortOrder?: number;
parentId?: string | null;
}
/** PATCH 不允许修改 ID;省略父级保持原值,null 或空串移到根级。 */
export type UpdateCategoryRequest = Partial<Omit<CreateCategoryRequest, 'id'>>;
export type WorksheetAgeBand = '3-4' | '4-5' | '5-6' | '6-7' | '7-8';
export type WorksheetSort = 'default' | 'latest' | 'hot';
/** 筛选条件可组合;grade 与 grades、age 与 ageBand 分别互斥。 */
export interface WorksheetQuery {
page?: number;
pageSize?: number;
categoryId?: string;
grade?: number;
grades?: number[];
ageBand?: WorksheetAgeBand;
age?: number;
tag?: string;
sort?: WorksheetSort;
isHot?: boolean;
isNew?: boolean;
}
/** 公开题型白名单;64 位展示计数使用十进制字符串,避免 JavaScript 精度丢失。 */
export interface PublicWorksheet {
id: string;
title: string;
subtitle: string;
category: string;
subcategory: string | null;
path: string;
ageMin: number;
ageMax: number;
grades: number[];
difficulty: number;
previewImg: string | null;
tags: string[];
isNew: boolean;
isHot: boolean;
sortOrder: number;
downloads: string;
likes: string;
createdAt: string;
updatedAt: string;
contentUpdatedAt: string | null;
firstPublishedAt: string | null;
}
export interface WorksheetPage {
items: PublicWorksheet[];
total: number;
page: number;
pageSize: number;
hasNext: boolean;
}
/** 管理预览沿用公开字段白名单,额外标明自身状态和整条所属关系是否可见。 */
export interface AdminWorksheet extends PublicWorksheet {
status: ContentStatus;
visible: boolean;
}
export interface AdminWorksheetPage extends Omit<WorksheetPage, 'items'> {
items: AdminWorksheet[];
}
export interface UpdateContentStatusRequest {
status: ContentStatus;
}
export interface ContentPublication {
id: string;
status: ContentStatus;
firstPublishedAt: string | null;
updatedAt: string;
}
/** 资源管理预览不返回配置覆盖、源文件地址、私有对象或签名 URL。 */
export interface AdminWorksheetResource extends ContentPublication {
worksheetId: string;
title: string;
previewImg: string | null;
sortOrder: number;
downloads: string;
createdAt: string;
visible: boolean;
}
export interface AdminResourcePage {
items: AdminWorksheetResource[];
total: number;
page: number;
pageSize: number;
hasNext: boolean;
}