@@ -35,12 +35,28 @@ async function generateApiTypes() {
3535 httpClientType : 'axios' ,
3636 typePrefix : 'I' ,
3737 generateClient : true ,
38+ generateResponses : true ,
39+ extractRequestParams : true ,
40+ extractResponseBody : true ,
41+ singleHttpClient : true ,
42+ unwrapResponseData : true ,
3843 hooks : {
3944 onPrepareConfig : ( currentConfiguration ) => {
4045 const config = currentConfiguration . config ;
41- config . fileNames . httpClient = 'HttpClient' ; // 使用大驼峰命名
46+ config . fileNames . httpClient = 'HttpClient' ;
4247 return { ...currentConfiguration , config } ;
4348 } ,
49+ onParseSchema : ( originalSchema , parsedSchema ) => {
50+ // 确保所有属性都有类型
51+ if ( parsedSchema . properties ) {
52+ Object . keys ( parsedSchema . properties ) . forEach ( prop => {
53+ if ( ! parsedSchema . properties [ prop ] . type ) {
54+ parsedSchema . properties [ prop ] . type = 'any' ;
55+ }
56+ } ) ;
57+ }
58+ return parsedSchema ;
59+ } ,
4460 } ,
4561 } ) ;
4662
@@ -86,13 +102,14 @@ async function organizeGeneratedFiles() {
86102 // API 文件:首字母大写的文件或特定的客户端文件
87103 fs . moveSync ( filePath , path . join ( apiDir , file ) , { overwrite : true } ) ;
88104 console . log ( `📄 移动 API 文件: ${ file } -> api/` ) ;
89- } else if ( file . includes ( 'contract' ) || file === 'types.ts' ) {
105+ } else if ( file . includes ( 'contract' ) || file === 'types.ts' || file . includes ( 'response' ) ) {
90106 // 类型定义文件
91- fs . moveSync ( filePath , path . join ( typesDir , file === 'types.ts' ? 'index.ts' : file ) , { overwrite : true } ) ;
92- console . log ( `📄 移动类型文件: {file} -> types/` ) ;
107+ const targetName = file === 'types.ts' ? 'index.ts' : file ;
108+ fs . moveSync ( filePath , path . join ( typesDir , targetName ) , { overwrite : true } ) ;
109+ console . log ( `📄 移动类型文件: ${ file } -> types/` ) ;
93110 } else {
94111 // 其他文件留在根目录
95- console . log ( `📄 保留文件: {file}` ) ;
112+ console . log ( `📄 保留文件: $ {file } ` ) ;
96113 }
97114 }
98115 } ) ;
@@ -103,9 +120,15 @@ async function organizeGeneratedFiles() {
103120 // 修复 API 文件中的导入路径
104121 await fixApiImports ( ) ;
105122
123+ // 修复缺失的类型导入
124+ await fixMissingImports ( ) ;
125+
106126 // 重命名 API 方法,使其更专业
107127 await renameApiMethods ( ) ;
108128
129+ // 修复请求和响应类型
130+ await fixRequestResponseTypes ( ) ;
131+
109132 // 创建统一的类型索引
110133 await createTypeIndex ( ) ;
111134
@@ -171,6 +194,12 @@ async function fixApiImports() {
171194 "from '../types/data-contracts'"
172195 ) ;
173196
197+ // 修复响应类型导入路径
198+ content = content . replace (
199+ / f r o m ' \. \/ ( .* r e s p o n s e s .* ) ' / g,
200+ "from '../types/$1'"
201+ ) ;
202+
174203 // 修复 HttpClient 导入路径
175204 content = content . replace (
176205 / f r o m ' \. \/ h t t p C l i e n t ' / g,
@@ -182,7 +211,7 @@ async function fixApiImports() {
182211 / f r o m ' \. \/ ( [ ^ ' ] + ) ' / g,
183212 ( match , importPath ) => {
184213 // 如果导入路径是类型文件,则重定向到 types 目录
185- if ( importPath . includes ( 'contract' ) || importPath === 'types' ) {
214+ if ( importPath . includes ( 'contract' ) || importPath . includes ( 'response' ) || importPath === 'types' ) {
186215 return `from '../types/${ importPath } '` ;
187216 }
188217 return match ;
@@ -197,6 +226,63 @@ async function fixApiImports() {
197226 console . log ( '✅ 所有 API 文件的导入路径已修复' ) ;
198227}
199228
229+ // 修复缺失的类型导入
230+ async function fixMissingImports ( ) {
231+ console . log ( '🔧 修复缺失的类型导入...' ) ;
232+
233+ const apiDir = path . join ( CONFIG . output , 'api' ) ;
234+ const typesDir = path . join ( CONFIG . output , 'types' ) ;
235+
236+ // 获取所有 API 文件
237+ const apiFiles = fs . readdirSync ( apiDir ) . filter ( file => file . endsWith ( '.ts' ) && file !== 'index.ts' ) ;
238+
239+ // 读取数据契约文件,获取所有可用的类型
240+ const dataContractsPath = path . join ( typesDir , 'data-contracts.ts' ) ;
241+ let availableTypes = [ ] ;
242+
243+ if ( fs . existsSync ( dataContractsPath ) ) {
244+ const dataContractsContent = fs . readFileSync ( dataContractsPath , 'utf8' ) ;
245+ // 提取所有接口和类型定义
246+ const typeRegex = / ( e x p o r t i n t e r f a c e | e x p o r t t y p e ) ( I [ A - Z ] [ a - z A - Z ] * ) / g;
247+ let match ;
248+ while ( ( match = typeRegex . exec ( dataContractsContent ) ) !== null ) {
249+ availableTypes . push ( match [ 2 ] ) ;
250+ }
251+ }
252+
253+ for ( const file of apiFiles ) {
254+ const filePath = path . join ( apiDir , file ) ;
255+ let content = fs . readFileSync ( filePath , 'utf8' ) ;
256+
257+ // 检查当前导入语句
258+ const importMatch = content . match ( / i m p o r t \s * { ( [ ^ } ] + ) } \s * f r o m \s * [ ' " ] \. \. \/ t y p e s \/ d a t a - c o n t r a c t s [ ' " ] / ) ;
259+
260+ if ( importMatch ) {
261+ const importedTypes = importMatch [ 1 ] . split ( ',' ) . map ( t => t . trim ( ) ) ;
262+
263+ // 查找文件中使用的类型
264+ const usedTypes = [ ] ;
265+ for ( const type of availableTypes ) {
266+ if ( content . includes ( type ) && ! importedTypes . includes ( type ) ) {
267+ usedTypes . push ( type ) ;
268+ }
269+ }
270+
271+ // 如果有缺失的类型,添加到导入语句中
272+ if ( usedTypes . length > 0 ) {
273+ const newImport = `import { ${ importedTypes . join ( ', ' ) } , ${ usedTypes . join ( ', ' ) } } from '../types/data-contracts';` ;
274+ content = content . replace ( / i m p o r t \s * { ( [ ^ } ] + ) } \s * f r o m \s * [ ' " ] \. \. \/ t y p e s \/ d a t a - c o n t r a c t s [ ' " ] / , newImport ) ;
275+
276+ // 写入修复后的内容
277+ fs . writeFileSync ( filePath , content ) ;
278+ console . log ( `✅ 为 ${ file } 添加缺失的类型导入: ${ usedTypes . join ( ', ' ) } ` ) ;
279+ }
280+ }
281+ }
282+
283+ console . log ( '✅ 缺失的类型导入已修复' ) ;
284+ }
285+
200286// 重命名 API 方法,使其更专业
201287async function renameApiMethods ( ) {
202288 console . log ( '🔧 重命名 API 方法...' ) ;
@@ -215,24 +301,113 @@ async function renameApiMethods() {
215301 const filePath = path . join ( apiDir , file ) ;
216302 let content = fs . readFileSync ( filePath , 'utf8' ) ;
217303
218- // 使用更精确的正则表达式匹配并重命名方法
219- // 匹配模式: 方法名以 Controller 开头,后面跟着大写字母
220- // 例如: articleControllerFindById -> findById
304+ // 提取控制器名称 (从文件名)
305+ const controllerName = file . replace ( '.ts' , '' ) ;
306+
307+ // 构建正则表达式来匹配方法定义
308+ const methodRegex = new RegExp ( `${ controllerName . toLowerCase ( ) } Controller([A-Z]\\w+)\\s*=` , 'g' ) ;
309+
310+ // 替换方法名
311+ content = content . replace ( methodRegex , ( match , methodPart ) => {
312+ // 将方法名的首字母小写
313+ const newMethodName = methodPart . charAt ( 0 ) . toLowerCase ( ) + methodPart . slice ( 1 ) ;
314+ return `${ newMethodName } =` ;
315+ } ) ;
316+
317+ // 写入修复后的内容
318+ fs . writeFileSync ( filePath , content ) ;
319+ console . log ( `✅ 重命名 ${ file } 中的方法` ) ;
320+ }
321+
322+ console . log ( '✅ 所有 API 方法已重命名' ) ;
323+ }
324+
325+ // 修复请求和响应类型
326+ async function fixRequestResponseTypes ( ) {
327+ console . log ( '🔧 修复请求和响应类型...' ) ;
328+
329+ const apiDir = path . join ( CONFIG . output , 'api' ) ;
330+ const typesDir = path . join ( CONFIG . output , 'types' ) ;
331+
332+ // 获取所有 API 文件
333+ const apiFiles = fs . readdirSync ( apiDir ) . filter ( file =>
334+ file . endsWith ( '.ts' ) &&
335+ file !== 'index.ts' &&
336+ file !== 'HttpClient.ts' &&
337+ / [ A - Z ] / . test ( file [ 0 ] ) // 首字母大写的文件
338+ ) ;
339+
340+ // 读取数据契约类型
341+ let dataContractsContent = '' ;
342+ const dataContractsPath = path . join ( typesDir , 'data-contracts.ts' ) ;
343+ if ( fs . existsSync ( dataContractsPath ) ) {
344+ dataContractsContent = fs . readFileSync ( dataContractsPath , 'utf8' ) ;
345+ }
346+
347+ for ( const file of apiFiles ) {
348+ const filePath = path . join ( apiDir , file ) ;
349+ let content = fs . readFileSync ( filePath , 'utf8' ) ;
350+
351+ // 修复 GET 请求的错误数据参数
352+ content = content . replace (
353+ / ( G E T | D E L E T E | H E A D | O P T I O N S ) .* \n .* \( d a t a : .* p a r a m s : R e q u e s t P a r a m s = { } \) / g,
354+ ( match ) => {
355+ // 对于 GET/DELETE/HEAD/OPTIONS 请求,不应该有 data 参数
356+ return match . replace ( 'data: any, ' , '' ) . replace ( 'data: I\\w+, ' , '' ) ;
357+ }
358+ ) ;
359+
360+ // 修复请求和响应类型
361+ content = content . replace (
362+ / t h i s \. h t t p \. r e q u e s t < ( [ ^ , ] + ) , \s * a n y > \( { / g,
363+ ( match , responseType ) => {
364+ // 如果响应类型是 void 或 any,尝试推断正确的类型
365+ if ( responseType === 'void' || responseType === 'any' ) {
366+ // 根据方法名和类名推断类型
367+ const className = file . replace ( '.ts' , '' ) ;
368+ const methodMatch = content . match ( / ( \w + ) \s * = \s * \( [ ^ ) ] * \) \s * = > / ) ;
369+ if ( methodMatch ) {
370+ const methodName = methodMatch [ 1 ] ;
371+
372+ // 根据方法名推断响应类型
373+ let inferredType = 'any' ;
374+ if ( methodName . includes ( 'find' ) || methodName . includes ( 'get' ) ) {
375+ inferredType = `I${ className } []` ;
376+ } else if ( methodName . includes ( 'create' ) || methodName . includes ( 'update' ) ) {
377+ inferredType = `I${ className } ` ;
378+ }
379+
380+ return match . replace ( responseType , inferredType ) ;
381+ }
382+ }
383+ return match ;
384+ }
385+ ) ;
386+
387+ // 修复请求参数类型
221388 content = content . replace (
222- / ( \w + ) C o n t r o l l e r ( [ A - Z ] \w + ) / g,
223- ( match , className , methodName ) => {
224- // 将方法名的首字母小写
225- const newMethodName = methodName . charAt ( 0 ) . toLowerCase ( ) + methodName . slice ( 1 ) ;
226- return newMethodName ;
389+ / \( p a r a m s : \s * R e q u e s t P a r a m s \s * = \s * { } \) \s * = > / g,
390+ ( match ) => {
391+ // 根据方法名推断请求体类型
392+ const methodMatch = content . match ( / ( \w + ) \s * = \s * \( [ ^ ) ] * \) \s * = > / ) ;
393+ if ( methodMatch ) {
394+ const methodName = methodMatch [ 1 ] ;
395+ const className = file . replace ( '.ts' , '' ) ;
396+
397+ if ( methodName . includes ( 'create' ) || methodName . includes ( 'update' ) ) {
398+ return `(data: I${ className } , params: RequestParams = {}) =>` ;
399+ }
400+ }
401+ return match ;
227402 }
228403 ) ;
229404
230405 // 写入修复后的内容
231406 fs . writeFileSync ( filePath , content ) ;
232- console . log ( `✅ 重命名 ${ file } 中的方法 ` ) ;
407+ console . log ( `✅ 修复 ${ file } 的请求和响应类型 ` ) ;
233408 }
234409
235- console . log ( '✅ 所有 API 方法已重命名 ' ) ;
410+ console . log ( '✅ 所有 API 的请求和响应类型已修复 ' ) ;
236411}
237412
238413// 创建统一的类型索引
@@ -398,7 +573,7 @@ async function generateUtils() {
398573 const utilsDir = path . join ( CONFIG . output , 'utils' ) ;
399574 fs . ensureDirSync ( utilsDir ) ;
400575
401- // HTTP 客户端配置 - 修复类型错误
576+ // HTTP 客户端配置
402577 const httpUtilsContent = `import axios, { AxiosRequestConfig, AxiosResponse, InternalAxiosRequestConfig } from 'axios';
403578
404579// 创建自定义 axios 实例
@@ -411,7 +586,7 @@ export const createHttpClient = (baseURL?: string) => {
411586 },
412587 });
413588
414- // 请求拦截器 - 修复类型错误
589+ // 请求拦截器
415590 instance.interceptors.request.use(
416591 (config: InternalAxiosRequestConfig) => {
417592 // 添加认证令牌
0 commit comments