fix: 统一日志格式并修复字体列表接口参数验证

refactor(字体服务): 重构日志格式添加上下文信息
fix(API-fonts/list): 添加分页参数验证和限制
style: 调整代码格式和注释
This commit is contained in:
bo.yu 2025-08-22 17:45:41 +08:00
parent 6eb838d9f0
commit a6847e28de
13 changed files with 85 additions and 39 deletions

2
.gitignore vendored
View file

@ -56,7 +56,7 @@ web_modules/
# Optional eslint cache
.eslintcache

.vscode/settings.json
.vscode/*
# Optional prettier cache
.prettiercache


View file

@ -218,7 +218,7 @@ const useFontList = (filters: {
const res: ApiResponse = await requestPost('/api/fonts/list', {
pageData: { page, size: PAGE_SIZE },
tags: filters.tags || undefined,
keywords: debouncedKeywords || undefined,
keyword: debouncedKeywords || undefined,
languages: filters.languages || undefined,
category: filters.category || undefined,
});

View file

@ -194,7 +194,7 @@ export async function GET(request: NextRequest): Promise<NextResponse> {
},
});
} catch (error: any) {
Logger.error('CSS生成页面 - CSS生成接口错误:', error);
Logger.error('[API-css] CSS生成接口错误', error);
return new NextResponse('/* 内部服务器错误 */', {
status: 500,
headers: {
@ -296,8 +296,8 @@ async function generateCSSResponseOriginal(
const staticBaseRaw = APP_CONFIG.fontStaticUrl;
if (!staticBaseRaw) {
Logger.error(
'CSS生成页面 - 缺少字体静态资源基础 URL。请在环境变量中设置 NEXT_PUBLIC_FONT_STATIC_URL 或 OSS_ENDPOINT。'
);
'[API-css] 缺少字体静态资源基础 URL。请在环境变量中设置 NEXT_PUBLIC_FONT_STATIC_URL 或 OSS_ENDPOINT。'
);
throw new Error('缺少字体静态资源基础 URL');
}
const staticBase = staticBaseRaw.replace(/\/+$/g, '');
@ -342,7 +342,7 @@ async function generateCSSResponseOriginal(

return cssContent;
} catch (error: any) {
Logger.error('CSS生成页面 - 生成CSS响应时出错:', error);
Logger.error('[API-css] 生成CSS响应时出错', error);
throw error;
}
}

View file

@ -32,7 +32,7 @@ export async function GET(
message: 'success',
});
} catch (error: any) {
Logger.error('字体管理页面 - 自动更新字体接口错误:', error);
Logger.error('[API-fonts/auto_update] 自动更新字体接口错误', error);
return NextResponse.json(
{
code: 500,

View file

@ -61,7 +61,7 @@ export async function POST(
message: 'success',
});
} catch (error: any) {
Logger.error('字体管理页面 - 创建字体接口错误:', error);
Logger.error('[API-fonts/create] 创建字体接口错误', error);
return NextResponse.json(
{
code: 500,

View file

@ -32,7 +32,7 @@ export async function POST(
message: 'success',
});
} catch (error: any) {
Logger.error('字体管理页面 - 删除字体接口错误:', error);
Logger.error('[API-fonts/delete] 删除字体接口错误', error);
return NextResponse.json(
{
code: 500,

View file

@ -39,7 +39,7 @@ export async function GET(
message: 'success',
});
} catch (error: any) {
Logger.error('字体详情页面 - 获取字体详情接口错误:', error);
Logger.error('[API-fonts/detail] 获取字体详情接口错误', error);
return NextResponse.json(
{
code: 500,
@ -94,7 +94,7 @@ export async function POST(
message: 'success',
});
} catch (error: any) {
Logger.error('字体详情页面 - 根据名称获取字体详情接口错误:', error);
Logger.error('[API-fonts/detail] 根据名称获取字体详情接口错误', error);
return NextResponse.json(
{
code: 500,

View file

@ -14,6 +14,43 @@ export async function POST(
const typefaceService = new TypefaceService();

const body: ListTypefaceDto = await request.json();

// 验证必需的参数
if (!body.pageData || typeof body.pageData !== 'object') {
return NextResponse.json(
{
code: 400,
message: 'pageData 参数是必需的',
status: 'fail',
},
{ status: 200 }
);
}

const { page, size } = body.pageData;
if (typeof page !== 'number' || typeof size !== 'number' || page < 1 || size < 1) {
return NextResponse.json(
{
code: 400,
message: 'pageData.page 和 pageData.size 必须是大于0的数字',
status: 'fail',
},
{ status: 200 }
);
}

// 限制每页最大数量,防止性能问题
if (size > 100) {
return NextResponse.json(
{
code: 400,
message: '每页最多只能查询100条数据',
status: 'fail',
},
{ status: 200 }
);
}

const result = await typefaceService.findAll(body);

// Convert Paging instance to IPageData format
@ -35,7 +72,7 @@ export async function POST(
message: 'success',
});
} catch (error: any) {
Logger.error('字体列表页面 - 获取字体列表接口错误:', error);
Logger.error('[API-fonts/list] 获取字体列表接口错误', error);
return NextResponse.json(
{
code: 500,
@ -60,7 +97,7 @@ export async function GET(): Promise<NextResponse<IResponseData<any>>> {
message: 'success',
});
} catch (error: any) {
Logger.error('字体列表页面 - 获取所有字体接口错误:', error);
Logger.error('[API-fonts/list] 获取字体列表接口错误', error);
return NextResponse.json(
{
code: 500,

View file

@ -33,7 +33,7 @@ export async function POST(
message: 'success',
});
} catch (error: any) {
Logger.error('字体管理页面 - 更新字体接口错误:', error);
Logger.error('[API-fonts/update] 更新字体接口错误', error);
return NextResponse.json(
{
code: 500,

View file

@ -29,7 +29,7 @@ async function fetchFonts(): Promise<TypefaceOutput[]> {
Logger.info('获取字体列表成功,字体列表长度:', result.dataList.length);
return [];
} catch (error) {
Logger.error('字体展示页面 - 获取字体列表失败:', error);
Logger.error('[页面-fonts] 获取字体列表失败', error);
return [];
}
}

View file

@ -57,7 +57,7 @@ export async function testConnection() {
Logger.info('Database connection established successfully');
return true;
} catch (error) {
Logger.error('数据库服务 - 数据库连接失败:', error);
Logger.error('[数据库服务] 数据库连接失败', error);
throw error;
}
}
@ -68,7 +68,7 @@ export async function closeDatabase() {
await client.end();
Logger.info('Database connection closed');
} catch (error) {
Logger.error('数据库服务 - 关闭数据库连接错误:', error);
Logger.error('[数据库服务] 关闭数据库连接错误', error);
throw error;
}
}

View file

@ -255,9 +255,17 @@ export class TypefaceService {
conditions.push(like(typefaces.languages, `%${languages}%`));
}

const query = db.select().from(typefaces).where(and(...conditions));
// 构建查询条件
const whereCondition = conditions.length > 0 ? and(...conditions) : undefined;

const data = await query.limit(size).offset(offset);
// 查询数据
const baseQuery = db.select().from(typefaces);
const query = whereCondition ? baseQuery.where(whereCondition) : baseQuery;

const data = await query
.orderBy(desc(typefaces.createdAt))
.limit(size)
.offset(offset);

// 转换字段名以匹配前端期望的格式
const transformedData = data.map(font => ({
@ -266,9 +274,10 @@ export class TypefaceService {
updated_at: font.updatedAt,
}));

const [{ count }] = await db
.select({ count: sql<number>`count(*)` })
.from(typefaces);
// 获取符合条件的总数
const countBaseQuery = db.select({ count: sql<number>`count(*)` }).from(typefaces);
const countQuery = whereCondition ? countBaseQuery.where(whereCondition) : countBaseQuery;
const [{ count }] = await countQuery;

return new Paging(transformedData, Number(count), page, size);
} catch (error) {
@ -432,7 +441,7 @@ export class TypefaceService {

const processedVariants = [];
let finalMetaInfo = null;

console.log(fontSubfamilies);
// 遍历每个fontSubfamily变体
for (const subfamily of fontSubfamilies) {
try {
@ -441,7 +450,7 @@ export class TypefaceService {
const fontBuffer = await this.ossClient.get(ossFilePath);

if (!fontBuffer || !fontBuffer.content) {
Logger.warn(`Font file not found in OSS for variant: ${subfamily}`);
Logger.warn(`[字体服务-autoUpdate] OSS文件不存在 - 字体变体: ${subfamily}, 路径: ${ossFilePath}`);
// eslint-disable-next-line no-continue
continue;
}
@ -500,7 +509,7 @@ export class TypefaceService {
if (licenseFileBuffer) break;
}
} catch (licenseError) {
Logger.warn(`字体服务 - 获取许可证文件失败 ${subfamily}:`, licenseError);
Logger.warn(`[字体服务-autoUpdate] 获取许可证文件失败 - 字体变体: ${subfamily}`, licenseError);
}

// 上传处理后的文件到 OSS
@ -513,9 +522,9 @@ export class TypefaceService {
licenseFileBuffer,
licenseFileName,
});
Logger.info(`字体服务 - 成功上传字体变体到OSS: ${subfamily}`);
Logger.info(`[字体服务-autoUpdate] OSS上传成功 - 字体变体: ${subfamily}`);
} catch (uploadError) {
Logger.error(`字体服务 - OSS上传字体变体失败: ${subfamily}`, uploadError);
Logger.error(`[字体服务-autoUpdate] OSS上传失败 - 字体变体: ${subfamily}`, uploadError);
// 继续处理,不因为上传失败而中断整个流程
}

@ -526,17 +535,17 @@ export class TypefaceService {
finalMetaInfo = metaInfo;
}

Logger.info(`字体服务 - 成功处理字体变体: ${subfamily}`);
Logger.info(`[字体服务-autoUpdate] 字体变体处理成功: ${subfamily}`);

// 清理临时文件目录
try {
await rm(fontPackageDir, { recursive: true, force: true });
Logger.info(`字体服务 - 清理临时目录: ${fontPackageDir}`);
Logger.info(`[字体服务-autoUpdate] 临时目录清理成功: ${fontPackageDir}`);
} catch (cleanupError) {
Logger.warn(`字体服务 - 清理临时目录失败: ${fontPackageDir}`, cleanupError);
Logger.warn(`[字体服务-autoUpdate] 临时目录清理失败: ${fontPackageDir}`, cleanupError);
}
} catch (variantError) {
Logger.error(`字体服务 - 处理字体变体失败: ${subfamily}`, variantError);
Logger.error(`[字体服务-autoUpdate] 字体变体处理失败: ${subfamily}`, variantError);
// 继续处理下一个变体,不中断整个流程
}
}
@ -577,7 +586,7 @@ export class TypefaceService {
metaInfo: finalMetaInfo,
};
} catch (error) {
Logger.error('字体服务 - 自动更新失败:', error);
Logger.error(`[字体服务-autoUpdate] 自动更新失败 - 字体ID: ${id}`, error);
throw error;
}
});
@ -723,9 +732,9 @@ export class TypefaceService {
if (fontPackageDir) {
try {
await rm(fontPackageDir, { recursive: true, force: true });
Logger.info(`字体服务 - 清理临时目录: ${fontPackageDir}`);
Logger.info(`[字体服务-create] 临时目录清理成功: ${fontPackageDir}`);
} catch (cleanupError) {
Logger.warn(`字体服务 - 清理临时目录失败: ${fontPackageDir}`, cleanupError);
Logger.warn(`[字体服务-create] 临时目录清理失败: ${fontPackageDir}`, cleanupError);
}
}

@ -735,7 +744,7 @@ export class TypefaceService {

return result;
} catch (error) {
Logger.error('字体服务 - 创建字体失败:', error);
Logger.error('[字体服务-create] 创建字体失败', error);
throw error;
}
});
@ -853,7 +862,7 @@ export class TypefaceService {
);

if (failedFiles.length > 0) {
Logger.warn('以下文件上传失败:', failedFiles.map(f => `${f.fileName}: ${f.error}`));
Logger.warn(`[字体服务-uploadToOSS] 部分文件上传失败 - 字体: ${fontFamily}-${fontSubfamily}`, failedFiles.map(f => `${f.fileName}: ${f.error}`));
// 如果关键文件上传失败,抛出错误
const criticalFiles = [`${fontFamily}-${fontSubfamily}.ttf`, `${OSS_CONFIG.subFolderWeb}.zip`];
const failedCriticalFiles = failedFiles.filter(f =>
@ -865,7 +874,7 @@ export class TypefaceService {
}
}
} catch (error: any) {
Logger.error('字体服务 - OSS上传失败:', error);
Logger.error(`[字体服务-uploadToOSS] OSS上传失败 - 字体: ${fontFamily}-${fontSubfamily}`, error);
throw new Error(`OSS upload failed: ${error.message}`);
}
}
@ -979,7 +988,7 @@ export class TypefaceService {
errors: undefined,
};
} catch (error) {
Logger.error('字体服务 - 批量更新事务失败:', error);
Logger.error('[字体服务-batchUpdate] 批量更新事务失败', error);
return {
success: false,
results,

View file

@ -29,7 +29,7 @@ export class ConcurrentUploader {
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
this.results.push({ success: false, fileName, error: errorMessage });
Logger.error(`字体服务 - 文件上传失败 ${fileName}:`, error);
Logger.error(`[字体服务-concurrent-uploader] 文件上传失败 - 文件: ${fileName}`, error);
resolve(); // 不要 reject让其他文件继续上传
}
});