Files
knowai/decorators/common.ts
tobegold574 382e3aff21 feat(reset): 以构造器模式重构
- 加了大文件传输自定义分片协议

BREAKING CHANGES: 0.1.0(latest)
2025-11-30 20:27:53 +08:00

129 lines
3.9 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { setMeta, getMeta, type ApiMethodMeta } from './meta';
import { AxiosHttpClient, type BaseError } from '../client';
// ApiError错误接口
export interface ApiError {
status: number; // 状态码来自response的status
statusText: string; // 状态文本来自response的msg
code: string; // 错误码(字符串)
message: string; // 报错信息(字符串)
// 没有别的细节了,错误码直接对照常量映射
}
// 客户端请求参数接口
export interface ClientRequestParams {
method: string;
url: string;
query?: Record<string, string | number>;
body?: any;
}
// 创建 AxiosHttpClient 单例实例
const apiClientInstance = new AxiosHttpClient();
/**
* 创建 HTTP 方法装饰器的通用函数
* @param method HTTP 方法类型
* @returns 装饰器函数
*/
export function createHttpMethodDecorator(method: ApiMethodMeta['method']) {
return function(path: string) {
return function(_target: Object, context: ClassMethodDecoratorContext) {
// 确保只应用于方法
if (context.kind !== 'method') {
throw new Error(`HTTP method decorator (${method}) can only be applied to methods`);
}
// 获取原始方法
const originalMethod = _target[context.name as keyof Object] as Function;
// 设置元数据
setMeta(originalMethod, 'api', {
method,
path
} as ApiMethodMeta);
};
};
}
/**
* 为 API 客户端类绑定 HTTP 客户端实例的装饰器
* @description 该装饰器对当前类注入 AxiosHttpClient 实例
*/
export function ApiClientBound<T extends { new (...args: any[]): { client: AxiosHttpClient } }>(target: T, _context: ClassDecoratorContext<T>) {
// 定义一个新类,继承自目标类
return class extends target {
// 在构造函数中初始化客户端实例
constructor(...args: any[]) {
super(...args);
// 添加客户端实例
this.client = apiClientInstance;
}
}
}
/**
* 请求执行装饰器,自动处理 API 调用逻辑
*/
export function Request(target: Object, context: ClassMethodDecoratorContext) {
const methodName = String(context.name);
// 确保只应用于方法
if (context.kind !== 'method') {
throw new Error('Request decorator can only be applied to methods');
}
// 保存原始方法
const originalMethod = target[context.name as keyof Object] as Function;
// 定义新的方法实现
async function replacementMethod(this: { client: { request: (params: ClientRequestParams) => Promise<any> } }, ...args: any[]) {
// 获取 API 元数据
const meta = getMeta(originalMethod, 'api') as ApiMethodMeta | undefined;
if (!meta) {
throw new Error(`Missing API metadata on ${methodName}. Please use an HTTP method decorator (GET, POST, etc.)`);
}
const { method, path } = meta;
// 处理请求参数
const params = args[0] ?? {};
const body = args[1];
// 替换路径中的动态参数段
let finalPath = path.replace(/:([\w]+)/g, (_, key) => {
const paramValue = params[key];
if (paramValue === undefined) {
throw new Error(`Missing required path parameter '${key}' for API endpoint '${path}'`);
}
return String(paramValue);
});
// 执行请求并处理错误
try {
return await this.client.request({
method,
url: finalPath,
query: params,
body,
});
} catch (error: BaseError | any) {
// 确保错误对象符合 ApiError 接口
const apiError: ApiError = {
status: error.response?.status || 500,
statusText: error.response?.msg || 'Internal Server Error',
code: error?.code || 'UNKNOWN_ERROR',
message: error?.message || 'An unexpected error occurred',
};
throw apiError;
}
}
// TS5+直接返回新方法定义
return replacementMethod;
}