feat: 分类接口替换为api 接口
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
import { apiRequest } from './api-request';
|
||||
|
||||
/** 小程序使用的公开接口字段,不复用旧云集合的 _id 和管理配置。 */
|
||||
export interface ApiCategory {
|
||||
id: string;
|
||||
name: string;
|
||||
icon: string;
|
||||
parentId: string | null;
|
||||
status: 'active';
|
||||
}
|
||||
|
||||
export interface ApiWorksheet {
|
||||
id: string;
|
||||
title: string;
|
||||
subtitle: string;
|
||||
category: string;
|
||||
path: string;
|
||||
ageMin: number;
|
||||
ageMax: number;
|
||||
difficulty: 1 | 2 | 3 | 4;
|
||||
previewImg: string | null;
|
||||
likes: string;
|
||||
downloads: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
contentUpdatedAt: string | null;
|
||||
}
|
||||
|
||||
interface WorksheetPage {
|
||||
items: ApiWorksheet[];
|
||||
total: number;
|
||||
page: number;
|
||||
pageSize: number;
|
||||
hasNext: boolean;
|
||||
}
|
||||
|
||||
/** 分类直接由服务端发布状态决定,不用本地目录补齐或恢复下架分类。 */
|
||||
export async function listCategories(): Promise<ApiCategory[]> {
|
||||
const rows = await apiRequest<ApiCategory[]>('/categories');
|
||||
if (
|
||||
!Array.isArray(rows) ||
|
||||
rows.some(
|
||||
(row) =>
|
||||
!row ||
|
||||
typeof row.id !== 'string' ||
|
||||
!row.id ||
|
||||
typeof row.name !== 'string' ||
|
||||
typeof row.icon !== 'string' ||
|
||||
row.status !== 'active',
|
||||
)
|
||||
) {
|
||||
throw new Error('分类数据异常,请稍后重试');
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
/** 展示计数保持十进制字符串,不转换 Number 或重复叠加展示种子。 */
|
||||
function validateWorksheet(row: ApiWorksheet): void {
|
||||
if (
|
||||
!row ||
|
||||
!row.id ||
|
||||
typeof row.id !== 'string' ||
|
||||
typeof row.title !== 'string' ||
|
||||
typeof row.subtitle !== 'string' ||
|
||||
typeof row.category !== 'string' ||
|
||||
typeof row.path !== 'string' ||
|
||||
!Number.isInteger(row.ageMin) ||
|
||||
!Number.isInteger(row.ageMax) ||
|
||||
row.ageMin < 0 ||
|
||||
row.ageMax < row.ageMin ||
|
||||
![1, 2, 3, 4].includes(row.difficulty) ||
|
||||
typeof row.downloads !== 'string' ||
|
||||
!/^\d+$/.test(row.downloads) ||
|
||||
typeof row.likes !== 'string' ||
|
||||
!/^\d+$/.test(row.likes) ||
|
||||
(row.previewImg !== null && typeof row.previewImg !== 'string')
|
||||
) {
|
||||
throw new Error('题型数据异常,请稍后重试');
|
||||
}
|
||||
}
|
||||
|
||||
/** 取完整分页后才能在客户端计算收藏榜和混合排序;分页变化或缺页时整次重试。 */
|
||||
export async function listAllWorksheets(): Promise<ApiWorksheet[]> {
|
||||
const pageSize = 100;
|
||||
const rows: ApiWorksheet[] = [];
|
||||
const ids = new Set<string>();
|
||||
let expectedTotal: number | undefined;
|
||||
for (let page = 1; ; page++) {
|
||||
const result = await apiRequest<WorksheetPage>('/worksheets', {
|
||||
data: { page, pageSize },
|
||||
});
|
||||
if (
|
||||
!result ||
|
||||
!Array.isArray(result.items) ||
|
||||
!Number.isSafeInteger(result.total) ||
|
||||
result.total < 0 ||
|
||||
result.page !== page ||
|
||||
result.pageSize !== pageSize ||
|
||||
result.hasNext !== page * pageSize < result.total ||
|
||||
result.items.length !==
|
||||
Math.max(
|
||||
0,
|
||||
Math.min(pageSize, result.total - (page - 1) * pageSize),
|
||||
) ||
|
||||
(expectedTotal !== undefined && result.total !== expectedTotal)
|
||||
) {
|
||||
throw new Error('题型列表已变化,请重试');
|
||||
}
|
||||
expectedTotal = result.total;
|
||||
for (const row of result.items) {
|
||||
validateWorksheet(row);
|
||||
if (ids.has(row.id)) throw new Error('题型列表已变化,请重试');
|
||||
ids.add(row.id);
|
||||
rows.push(row);
|
||||
}
|
||||
if (!result.hasNext) return rows;
|
||||
}
|
||||
}
|
||||
|
||||
/** 打开题型前重新检查公开详情,避免已下架内容从旧列表继续进入。 */
|
||||
export async function getWorksheet(id: string): Promise<ApiWorksheet> {
|
||||
const row = await apiRequest<ApiWorksheet>(
|
||||
`/worksheets/${encodeURIComponent(id)}`,
|
||||
);
|
||||
validateWorksheet(row);
|
||||
if (row.id !== id) throw new Error('题型数据异常,请稍后重试');
|
||||
return row;
|
||||
}
|
||||
Reference in New Issue
Block a user