refactor: 清理 Apifox API 依赖并优化工具链
- 删除 7 个不再工作的 Apifox API 相关脚本 - 删除 .env.apifox 配置文件 - 简化 package.json 命令(移除 api:sync 和 api:test) - 修复 API 生成工具:新增模块接口信息显示为 undefined - 新增文档模块接口(初稿):weekHotAPI、fileListAPI - 创建 API_GUIDE.md 使用指南 - 更新 API 联调日志(v2.3)和 CHANGELOG Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Showing
15 changed files
with
943 additions
and
1083 deletions
| ... | @@ -5,6 +5,73 @@ | ... | @@ -5,6 +5,73 @@ |
| 5 | 5 | ||
| 6 | --- | 6 | --- |
| 7 | 7 | ||
| 8 | +## [2026-02-04] - 清理 Apifox 依赖并优化工具链 | ||
| 9 | + | ||
| 10 | +### 移除 | ||
| 11 | +- Apifox API 相关脚本(不再工作) | ||
| 12 | + - 删除 `scripts/apifox-to-openapi.js` - 无法从 API 获取数据 | ||
| 13 | + - 删除 `scripts/apifox-sync.js` - API 同步脚本 | ||
| 14 | + - 删除 `scripts/debug-apifox-response.js` - 调试脚本 | ||
| 15 | + - 删除 `scripts/test-apifox-connection.js` - 测试脚本 | ||
| 16 | + - 删除 `scripts/test-apifox-endpoints.js` - 测试脚本 | ||
| 17 | + - 删除 `scripts/test-apifox-export.js` - 测试脚本 | ||
| 18 | + - 删除 `.env.apifox` 配置文件 | ||
| 19 | +- package.json 命令清理 | ||
| 20 | + - 移除 `api:sync` 命令(依赖 Apifox API) | ||
| 21 | + - 移除 `api:test` 命令(依赖已删除的脚本) | ||
| 22 | + - 保留 `api:generate` 命令(从 OpenAPI 文档生成代码) | ||
| 23 | + | ||
| 24 | +### 新增 | ||
| 25 | +- API 文档维护指南(`scripts/API_GUIDE.md`) | ||
| 26 | + - 详细说明手动维护 OpenAPI 文档的工作流程 | ||
| 27 | + - 提供完整的文档格式示例和最佳实践 | ||
| 28 | + - 包含故障排除指南 | ||
| 29 | +- 新增文档模块接口(初稿) | ||
| 30 | + - `weekHotAPI` - 本周热门资料 | ||
| 31 | + - `fileListAPI` - 文档列表 | ||
| 32 | + - 标记为初稿状态,后端字段未最终确定 | ||
| 33 | + | ||
| 34 | +### 优化 | ||
| 35 | +- API 生成脚本(`scripts/generateApiFromOpenAPI.js`) | ||
| 36 | + - 修复新增模块接口信息显示为 undefined 的问题 | ||
| 37 | + - 使用 `extractAPIInfo` 提取完整的接口信息(方法、路径、描述) | ||
| 38 | + - 改进接口信息显示逻辑 | ||
| 39 | + | ||
| 40 | +### 文档 | ||
| 41 | +- 更新 API 接口联调日志(`docs/api-integration-log.md`) | ||
| 42 | + - 新增文档模块:2个初稿接口(字段未确定) | ||
| 43 | + - 更新总体进度:26个接口(12已完成,9待联调,2初稿,3已废弃) | ||
| 44 | + - 文档版本升级到 v2.3 | ||
| 45 | + - 添加移除 Apifox 依赖的变更记录 | ||
| 46 | + - 更新快速索引,添加文档模块链接 | ||
| 47 | + | ||
| 48 | +--- | ||
| 49 | + | ||
| 50 | +**详细信息**: | ||
| 51 | +- **影响文件**: | ||
| 52 | + - `scripts/apifox-to-openapi.js` (已删除) | ||
| 53 | + - `scripts/apifox-sync.js` (已删除) | ||
| 54 | + - `scripts/debug-apifox-response.js` (已删除) | ||
| 55 | + - `scripts/test-apifox-connection.js` (已删除) | ||
| 56 | + - `scripts/test-apifox-endpoints.js` (已删除) | ||
| 57 | + - `scripts/test-apifox-export.js` (已删除) | ||
| 58 | + - `.env.apifox` (已删除) | ||
| 59 | + - `scripts/API_GUIDE.md` (新增) | ||
| 60 | + - `scripts/generateApiFromOpenAPI.js` (修复) | ||
| 61 | + - `package.json` (命令清理) | ||
| 62 | + - `docs/api-integration-log.md` (更新) | ||
| 63 | + - `docs/api-specs/file/week_hot.md` (新增) | ||
| 64 | + - `docs/api-specs/get_file_list/file_list.md` (新增) | ||
| 65 | +- **技术栈**: Node.js, fs 模块, js-yaml | ||
| 66 | +- **测试状态**: ✅ 已测试通过 | ||
| 67 | +- **备注**: | ||
| 68 | + - 项目现在完全采用手动维护 OpenAPI 文档的方式 | ||
| 69 | + - 工作流:从 Apifox 导出 → 放入 `docs/api-specs/` → 运行 `pnpm api:generate` | ||
| 70 | + - 不再依赖 Apifox API,更加稳定可控 | ||
| 71 | + - 文档模块接口标记为初稿,待后端确认字段后更新 | ||
| 72 | + | ||
| 73 | +--- | ||
| 74 | + | ||
| 8 | ## [2026-02-04] - 优化 OpenAPI 文档生成工具 | 75 | ## [2026-02-04] - 优化 OpenAPI 文档生成工具 |
| 9 | 76 | ||
| 10 | ### 优化 | 77 | ### 优化 | ... | ... |
| ... | @@ -4,11 +4,12 @@ | ... | @@ -4,11 +4,12 @@ |
| 4 | 4 | ||
| 5 | ## 📊 总体进度 | 5 | ## 📊 总体进度 |
| 6 | 6 | ||
| 7 | -- **总接口数**: 24 | 7 | +- **总接口数**: 26 |
| 8 | -- **已完成**: 12 (50.0%) | 8 | +- **已完成**: 12 (46.2%) |
| 9 | - **联调中**: 0 (0%) | 9 | - **联调中**: 0 (0%) |
| 10 | -- **已废弃**: 3 (12.5%) | 10 | +- **已废弃**: 3 (11.5%) |
| 11 | -- **待联调**: 9 (37.5%) | 11 | +- **待联调**: 9 (34.6%) |
| 12 | +- **初稿(字段未确定)**: 2 (7.7%) | ||
| 12 | - **有阻塞**: 0 | 13 | - **有阻塞**: 0 |
| 13 | 14 | ||
| 14 | --- | 15 | --- |
| ... | @@ -18,6 +19,15 @@ | ... | @@ -18,6 +19,15 @@ |
| 18 | - 添加 CLAUDE.md 文件过滤,避免将文档文件当作 API 文档处理 | 19 | - 添加 CLAUDE.md 文件过滤,避免将文档文件当作 API 文档处理 |
| 19 | - 改进新增模块检测逻辑,显示新增模块包含的所有接口 | 20 | - 改进新增模块检测逻辑,显示新增模块包含的所有接口 |
| 20 | - 优化变更检测报告,提升可读性 | 21 | - 优化变更检测报告,提升可读性 |
| 22 | + - 修复接口信息显示为 undefined 的问题 | ||
| 23 | +- 🗑️ **清理 Apifox API 依赖**: | ||
| 24 | + - 删除不再工作的 Apifox API 相关脚本(apifox-to-openapi.js、apifox-sync.js 等) | ||
| 25 | + - 删除 .env.apifox 配置文件 | ||
| 26 | + - 更新 package.json,移除 api:sync 和 api:test 命令 | ||
| 27 | + - 创建 API_GUIDE.md 使用指南,明确手动维护 OpenAPI 文档的工作流 | ||
| 28 | +- 📝 **新增文档模块接口(初稿)**: | ||
| 29 | + - weekHotAPI(本周热门资料):字段未最终确定,待联调确认 | ||
| 30 | + - fileListAPI(文档列表):字段未最终确定,待联调确认 | ||
| 21 | - ✅ **收藏模块后端完成**:3 个收藏接口(addAPI、delAPI、listAPI)后端已开发完成,前端待联调 | 31 | - ✅ **收藏模块后端完成**:3 个收藏接口(addAPI、delAPI、listAPI)后端已开发完成,前端待联调 |
| 22 | - ✅ **埋点接口后端完成**:埋点接口(addAPI)后端已开发完成,前端待联调 | 32 | - ✅ **埋点接口后端完成**:埋点接口(addAPI)后端已开发完成,前端待联调 |
| 23 | - ✅ **产品模块联调完成**:产品列表接口(listAPI)联调成功 | 33 | - ✅ **产品模块联调完成**:产品列表接口(listAPI)联调成功 |
| ... | @@ -818,6 +828,121 @@ | ... | @@ -818,6 +828,121 @@ |
| 818 | 828 | ||
| 819 | --- | 829 | --- |
| 820 | 830 | ||
| 831 | +### 文档模块 | ||
| 832 | + | ||
| 833 | +#### 接口 1: 本周热门资料(初稿) | ||
| 834 | + | ||
| 835 | +**接口信息** | ||
| 836 | +- **接口名称**: `weekHotAPI` | ||
| 837 | +- **接口路径**: `/srv/?a=file&t=week_hot` | ||
| 838 | +- **请求方法**: GET | ||
| 839 | +- **负责页面**: 待确认 | ||
| 840 | +- **负责人**: 后端团队 | ||
| 841 | + | ||
| 842 | +**接口文档更新记录** | ||
| 843 | + | ||
| 844 | +| 日期 | 版本 | 变更内容 | 变更原因 | 文档链接 | | ||
| 845 | +|------|------|---------|---------|---------| | ||
| 846 | +| 2026-02-04 | v0.1 | 初稿版本 | 后端字段未确定,待联调确认 | [查看](docs/api-specs/file/week_hot.md) | | ||
| 847 | + | ||
| 848 | +**页面调试情况** | ||
| 849 | + | ||
| 850 | +| 日期 | 调试页面 | 问题记录 | 解决方案 | 状态 | | ||
| 851 | +|------|---------|---------|---------|------| | ||
| 852 | +| 2026-02-04 | - | 后端字段未确定 | 待后端确认后更新 | ⏳ 待联调 | | ||
| 853 | + | ||
| 854 | +**接口状态**: ⏳ 初稿 - 后端字段未确定 | ||
| 855 | + | ||
| 856 | +**备注**: | ||
| 857 | +- ⚠️ **初稿状态**:当前接口文档为初稿,后端字段可能还有调整 | ||
| 858 | +- **参数**: | ||
| 859 | + - `page`: 页码,从 0 开始 | ||
| 860 | + - `limit`: 每页数量 | ||
| 861 | +- **预期返回数据结构**: | ||
| 862 | + ```javascript | ||
| 863 | + { | ||
| 864 | + code: 1, | ||
| 865 | + data: { | ||
| 866 | + list: [ | ||
| 867 | + { | ||
| 868 | + meta_id: 123, // 文件ID | ||
| 869 | + name: "文件名称", // 文件名称 | ||
| 870 | + src: "https://...", // 文件URL | ||
| 871 | + size: "1.2MB", // 文件大小 | ||
| 872 | + read_people_count: 100, // 学习人数 | ||
| 873 | + read_people_percent: 85.5 // 学习人数比例 | ||
| 874 | + } | ||
| 875 | + ] | ||
| 876 | + } | ||
| 877 | + } | ||
| 878 | + ``` | ||
| 879 | +- **字段确认状态**:待后端确认最终字段结构 | ||
| 880 | +- 实现位置:`src/api/file.js:weekHotAPI` | ||
| 881 | + | ||
| 882 | +--- | ||
| 883 | + | ||
| 884 | +#### 接口 2: 文档列表(初稿) | ||
| 885 | + | ||
| 886 | +**接口信息** | ||
| 887 | +- **接口名称**: `fileListAPI` | ||
| 888 | +- **接口路径**: `/srv/?a=get_file_list&t=file_list` | ||
| 889 | +- **请求方法**: GET | ||
| 890 | +- **负责页面**: 待确认 | ||
| 891 | +- **负责人**: 后端团队 | ||
| 892 | + | ||
| 893 | +**接口文档更新记录** | ||
| 894 | + | ||
| 895 | +| 日期 | 版本 | 变更内容 | 变更原因 | 文档链接 | | ||
| 896 | +|------|------|---------|---------|---------| | ||
| 897 | +| 2026-02-04 | v0.1 | 初稿版本 | 后端字段未确定,待联调确认 | [查看](docs/api-specs/get_file_list/file_list.md) | | ||
| 898 | + | ||
| 899 | +**页面调试情况** | ||
| 900 | + | ||
| 901 | +| 日期 | 调试页面 | 问题记录 | 解决方案 | 状态 | | ||
| 902 | +|------|---------|---------|---------|------| | ||
| 903 | +| 2026-02-04 | - | 后端字段未确定 | 待后端确认后更新 | ⏳ 待联调 | | ||
| 904 | + | ||
| 905 | +**接口状态**: ⏳ 初稿 - 后端字段未确定 | ||
| 906 | + | ||
| 907 | +**备注**: | ||
| 908 | +- ⚠️ **初稿状态**:当前接口文档为初稿,后端字段可能还有调整 | ||
| 909 | +- **参数**: | ||
| 910 | + - `client_id`: 主体 ID(可选) | ||
| 911 | + - `cid`: 分类 ID(可选) | ||
| 912 | + - `page`: 页码(从 0 开始) | ||
| 913 | + - `limit`: 每页数量 | ||
| 914 | +- **预期返回数据结构**: | ||
| 915 | + ```javascript | ||
| 916 | + { | ||
| 917 | + code: 1, | ||
| 918 | + data: { | ||
| 919 | + categories: [ | ||
| 920 | + { id: 2769851, name: "分类名称" } | ||
| 921 | + ], | ||
| 922 | + list: [ | ||
| 923 | + { | ||
| 924 | + id: 2769856, // 产品id | ||
| 925 | + product_name: "产品名称", // 产品名 | ||
| 926 | + recommend: "hot", // 推荐位: normal-普通, hot-热卖 | ||
| 927 | + form_sn: "customize_jsj_pnzuky", // 产品编号 | ||
| 928 | + created_time: "2026-02-03 10:36:29", | ||
| 929 | + categories: [ // 产品所属分类 | ||
| 930 | + { id: "2769851", name: "分类名称" } | ||
| 931 | + ], | ||
| 932 | + tags: [ // 产品标签 | ||
| 933 | + { id: "2769847", name: "标签名", bg_color: "#3e5160", text_color: "#ffffff" } | ||
| 934 | + ] | ||
| 935 | + } | ||
| 936 | + ], | ||
| 937 | + total: 3 // 产品总数 | ||
| 938 | + } | ||
| 939 | + } | ||
| 940 | + ``` | ||
| 941 | +- **字段确认状态**:待后端确认最终字段结构 | ||
| 942 | +- 实现位置:`src/api/get_file_list.js:fileListAPI` | ||
| 943 | + | ||
| 944 | +--- | ||
| 945 | + | ||
| 821 | ### 埋点模块 | 946 | ### 埋点模块 |
| 822 | 947 | ||
| 823 | #### 接口 1: 添加埋点 | 948 | #### 接口 1: 添加埋点 |
| ... | @@ -938,6 +1063,7 @@ | ... | @@ -938,6 +1063,7 @@ |
| 938 | - [❌ 已废弃](#通用模块) - 3个接口 | 1063 | - [❌ 已废弃](#通用模块) - 3个接口 |
| 939 | - [⏳ 待联调](#收藏模块) - 3个接口 | 1064 | - [⏳ 待联调](#收藏模块) - 3个接口 |
| 940 | - [⏳ 待联调](#埋点模块) - 1个接口 | 1065 | - [⏳ 待联调](#埋点模块) - 1个接口 |
| 1066 | +- [⏳ 初稿 - 字段未确定](#文档模块) - 2个接口 | ||
| 941 | - [⏳ 后端开发中](#消息模块) - 2个接口 | 1067 | - [⏳ 后端开发中](#消息模块) - 2个接口 |
| 942 | 1068 | ||
| 943 | ### 按模块查看 | 1069 | ### 按模块查看 |
| ... | @@ -947,6 +1073,7 @@ | ... | @@ -947,6 +1073,7 @@ |
| 947 | - [产品](#产品模块) - ✅ 1个已完成 | 1073 | - [产品](#产品模块) - ✅ 1个已完成 |
| 948 | - [收藏](#收藏模块) - ⏳ 3个待联调 | 1074 | - [收藏](#收藏模块) - ⏳ 3个待联调 |
| 949 | - [埋点](#埋点模块) - ⏳ 1个待联调 | 1075 | - [埋点](#埋点模块) - ⏳ 1个待联调 |
| 1076 | +- [文档](#文档模块) - ⏳ 2个初稿 | ||
| 950 | - [消息](#消息模块) - ⏳ 2个后端开发中 | 1077 | - [消息](#消息模块) - ⏳ 2个后端开发中 |
| 951 | - [知识库](#知识库模块) - ⏳ 未开始 | 1078 | - [知识库](#知识库模块) - ⏳ 未开始 |
| 952 | - [家办](#家办模块) - ⏳ 未开始 | 1079 | - [家办](#家办模块) - ⏳ 未开始 |
| ... | @@ -984,20 +1111,20 @@ | ... | @@ -984,20 +1111,20 @@ |
| 984 | 1111 | ||
| 985 | --- | 1112 | --- |
| 986 | 1113 | ||
| 987 | -**最后更新时间**: 2026-02-04 13:00 | 1114 | +**最后更新时间**: 2026-02-04 15:30 |
| 988 | -**文档版本**: v2.2 | 1115 | +**文档版本**: v2.3 |
| 989 | **更新内容**: | 1116 | **更新内容**: |
| 990 | -- 收藏模块:3个接口后端已完成,前端待联调 | 1117 | +- 📝 **新增文档模块接口(初稿)**: |
| 991 | - - addAPI(添加收藏) | 1118 | + - weekHotAPI(本周热门资料):字段未最终确定 |
| 992 | - - delAPI(取消收藏) | 1119 | + - fileListAPI(文档列表):字段未最终确定 |
| 993 | - - listAPI(收藏列表) | 1120 | +- 🗑️ **移除 Apifox API 依赖**: |
| 994 | -- 埋点模块:1个接口后端已完成,前端待联调 | 1121 | + - 删除 apifox-to-openapi.js 等不再工作的脚本 |
| 995 | - - addAPI(添加埋点) | 1122 | + - 更新 package.json,简化为 api:generate 单一命令 |
| 996 | -- OpenAPI 文档生成工具优化: | 1123 | + - 明确手动维护 OpenAPI 文档的工作流 |
| 997 | - - 添加 CLAUDE.md 文件过滤 | 1124 | +- 🔧 **API 生成工具优化**: |
| 998 | - - 改进新增模块检测逻辑 | 1125 | + - 修复新增模块接口信息显示为 undefined 的问题 |
| 999 | - - 优化变更检测报告 | 1126 | + - 改进接口信息提取逻辑 |
| 1000 | -- 更新总体进度:24个接口(12个已完成,9个待联调,3个已废弃) | 1127 | +- 更新总体进度:26个接口(12个已完成,9个待联调,2个初稿,3个已废弃) |
| 1001 | 1128 | ||
| 1002 | **历史版本**: | 1129 | **历史版本**: |
| 1003 | - v2.1 (2026-02-03 21:00): 产品模块联调完成 | 1130 | - v2.1 (2026-02-03 21:00): 产品模块联调完成 | ... | ... |
docs/api-specs/file/week_hot.md
0 → 100644
| 1 | +# 本周热门资料 | ||
| 2 | + | ||
| 3 | +## OpenAPI Specification | ||
| 4 | + | ||
| 5 | +```yaml | ||
| 6 | +openapi: 3.0.1 | ||
| 7 | +info: | ||
| 8 | + title: '' | ||
| 9 | + version: 1.0.0 | ||
| 10 | +paths: | ||
| 11 | + /srv/: | ||
| 12 | + get: | ||
| 13 | + summary: 本周热门资料 | ||
| 14 | + deprecated: false | ||
| 15 | + description: '' | ||
| 16 | + tags: | ||
| 17 | + - 文档 | ||
| 18 | + parameters: | ||
| 19 | + - name: f | ||
| 20 | + in: query | ||
| 21 | + description: '' | ||
| 22 | + required: true | ||
| 23 | + example: manulife | ||
| 24 | + schema: | ||
| 25 | + type: string | ||
| 26 | + - name: a | ||
| 27 | + in: query | ||
| 28 | + description: '' | ||
| 29 | + required: true | ||
| 30 | + example: file | ||
| 31 | + schema: | ||
| 32 | + type: string | ||
| 33 | + - name: t | ||
| 34 | + in: query | ||
| 35 | + description: '' | ||
| 36 | + required: true | ||
| 37 | + example: week_hot | ||
| 38 | + schema: | ||
| 39 | + type: string | ||
| 40 | + - name: page | ||
| 41 | + in: query | ||
| 42 | + description: 页码,从0开始 | ||
| 43 | + required: true | ||
| 44 | + example: '0' | ||
| 45 | + schema: | ||
| 46 | + type: string | ||
| 47 | + - name: limit | ||
| 48 | + in: query | ||
| 49 | + description: 每页数量 | ||
| 50 | + required: true | ||
| 51 | + example: '10' | ||
| 52 | + schema: | ||
| 53 | + type: string | ||
| 54 | + responses: | ||
| 55 | + '200': | ||
| 56 | + description: '' | ||
| 57 | + content: | ||
| 58 | + application/json: | ||
| 59 | + schema: | ||
| 60 | + type: object | ||
| 61 | + properties: | ||
| 62 | + code: | ||
| 63 | + type: integer | ||
| 64 | + msg: | ||
| 65 | + type: string | ||
| 66 | + data: | ||
| 67 | + type: object | ||
| 68 | + properties: | ||
| 69 | + list: | ||
| 70 | + type: array | ||
| 71 | + items: | ||
| 72 | + type: object | ||
| 73 | + properties: | ||
| 74 | + meta_id: | ||
| 75 | + type: integer | ||
| 76 | + title: 文件ID | ||
| 77 | + name: | ||
| 78 | + type: string | ||
| 79 | + title: 文件名称 | ||
| 80 | + src: | ||
| 81 | + type: string | ||
| 82 | + title: 文件URL | ||
| 83 | + size: | ||
| 84 | + type: string | ||
| 85 | + title: 文件大小 | ||
| 86 | + read_people_count: | ||
| 87 | + type: integer | ||
| 88 | + title: 学习人数 | ||
| 89 | + read_people_percent: | ||
| 90 | + type: number | ||
| 91 | + title: 学习人数比例 | ||
| 92 | + x-apifox-orders: | ||
| 93 | + - meta_id | ||
| 94 | + - name | ||
| 95 | + - src | ||
| 96 | + - size | ||
| 97 | + - read_people_count | ||
| 98 | + - read_people_percent | ||
| 99 | + required: | ||
| 100 | + - size | ||
| 101 | + - read_people_count | ||
| 102 | + - read_people_percent | ||
| 103 | + required: | ||
| 104 | + - list | ||
| 105 | + x-apifox-orders: | ||
| 106 | + - list | ||
| 107 | + required: | ||
| 108 | + - code | ||
| 109 | + - msg | ||
| 110 | + - data | ||
| 111 | + x-apifox-orders: | ||
| 112 | + - code | ||
| 113 | + - msg | ||
| 114 | + - data | ||
| 115 | + headers: {} | ||
| 116 | + x-apifox-name: 成功 | ||
| 117 | + x-apifox-ordering: 0 | ||
| 118 | + security: [] | ||
| 119 | + x-apifox-folder: 文档 | ||
| 120 | + x-apifox-status: developing | ||
| 121 | + x-run-in-apifox: https://app.apifox.com/web/project/7792797/apis/api-415055277-run | ||
| 122 | +components: | ||
| 123 | + schemas: {} | ||
| 124 | + responses: {} | ||
| 125 | + securitySchemes: {} | ||
| 126 | +servers: [] | ||
| 127 | +security: [] | ||
| 128 | + | ||
| 129 | +``` |
docs/api-specs/get_file_list/file_list.md
0 → 100644
| 1 | +# 文档列表 | ||
| 2 | + | ||
| 3 | +## OpenAPI Specification | ||
| 4 | + | ||
| 5 | +```yaml | ||
| 6 | +openapi: 3.0.1 | ||
| 7 | +info: | ||
| 8 | + title: '' | ||
| 9 | + version: 1.0.0 | ||
| 10 | +paths: | ||
| 11 | + /srv/: | ||
| 12 | + get: | ||
| 13 | + summary: 文档列表 | ||
| 14 | + deprecated: false | ||
| 15 | + description: '' | ||
| 16 | + tags: | ||
| 17 | + - 文档 | ||
| 18 | + parameters: | ||
| 19 | + - name: f | ||
| 20 | + in: query | ||
| 21 | + description: '' | ||
| 22 | + required: true | ||
| 23 | + example: manulife | ||
| 24 | + schema: | ||
| 25 | + type: string | ||
| 26 | + - name: a | ||
| 27 | + in: query | ||
| 28 | + description: '' | ||
| 29 | + required: true | ||
| 30 | + example: get_file_list | ||
| 31 | + schema: | ||
| 32 | + type: string | ||
| 33 | + - name: t | ||
| 34 | + in: query | ||
| 35 | + description: '' | ||
| 36 | + required: false | ||
| 37 | + example: file_list | ||
| 38 | + schema: | ||
| 39 | + type: string | ||
| 40 | + - name: client_id | ||
| 41 | + in: query | ||
| 42 | + description: 主体id | ||
| 43 | + required: false | ||
| 44 | + example: '30901' | ||
| 45 | + schema: | ||
| 46 | + type: string | ||
| 47 | + - name: limit | ||
| 48 | + in: query | ||
| 49 | + description: '' | ||
| 50 | + required: false | ||
| 51 | + example: '10' | ||
| 52 | + schema: | ||
| 53 | + type: string | ||
| 54 | + - name: page | ||
| 55 | + in: query | ||
| 56 | + description: '' | ||
| 57 | + required: false | ||
| 58 | + example: '0' | ||
| 59 | + schema: | ||
| 60 | + type: string | ||
| 61 | + - name: cid | ||
| 62 | + in: query | ||
| 63 | + description: 分类id | ||
| 64 | + required: false | ||
| 65 | + schema: | ||
| 66 | + type: string | ||
| 67 | + responses: | ||
| 68 | + '200': | ||
| 69 | + description: '' | ||
| 70 | + content: | ||
| 71 | + application/json: | ||
| 72 | + schema: | ||
| 73 | + type: object | ||
| 74 | + properties: | ||
| 75 | + code: | ||
| 76 | + type: integer | ||
| 77 | + msg: | ||
| 78 | + type: integer | ||
| 79 | + data: | ||
| 80 | + type: object | ||
| 81 | + properties: | ||
| 82 | + categories: | ||
| 83 | + type: array | ||
| 84 | + items: | ||
| 85 | + type: object | ||
| 86 | + properties: | ||
| 87 | + id: | ||
| 88 | + type: integer | ||
| 89 | + title: 分类id | ||
| 90 | + name: | ||
| 91 | + type: string | ||
| 92 | + title: 分类名 | ||
| 93 | + x-apifox-orders: | ||
| 94 | + - id | ||
| 95 | + - name | ||
| 96 | + title: 分类列表 | ||
| 97 | + list: | ||
| 98 | + type: array | ||
| 99 | + items: | ||
| 100 | + type: object | ||
| 101 | + properties: | ||
| 102 | + id: | ||
| 103 | + type: integer | ||
| 104 | + title: 产品id | ||
| 105 | + product_name: | ||
| 106 | + type: string | ||
| 107 | + title: 产品名 | ||
| 108 | + recommend: | ||
| 109 | + type: string | ||
| 110 | + title: '推荐位: normal-普通, hot-热卖' | ||
| 111 | + form_sn: | ||
| 112 | + type: string | ||
| 113 | + created_time: | ||
| 114 | + type: string | ||
| 115 | + title: 创建时间 | ||
| 116 | + categories: | ||
| 117 | + type: array | ||
| 118 | + items: | ||
| 119 | + type: object | ||
| 120 | + properties: | ||
| 121 | + id: | ||
| 122 | + type: string | ||
| 123 | + title: 分类id | ||
| 124 | + name: | ||
| 125 | + type: string | ||
| 126 | + title: 分类名 | ||
| 127 | + required: | ||
| 128 | + - id | ||
| 129 | + - name | ||
| 130 | + x-apifox-orders: | ||
| 131 | + - id | ||
| 132 | + - name | ||
| 133 | + title: 产品所属分类 | ||
| 134 | + tags: | ||
| 135 | + type: array | ||
| 136 | + items: | ||
| 137 | + type: object | ||
| 138 | + properties: | ||
| 139 | + id: | ||
| 140 | + type: string | ||
| 141 | + title: 标签id | ||
| 142 | + name: | ||
| 143 | + type: string | ||
| 144 | + title: 标签名 | ||
| 145 | + bg_color: | ||
| 146 | + type: string | ||
| 147 | + title: 标签背景色 | ||
| 148 | + text_color: | ||
| 149 | + type: string | ||
| 150 | + title: 标签文字色 | ||
| 151 | + required: | ||
| 152 | + - id | ||
| 153 | + - name | ||
| 154 | + - bg_color | ||
| 155 | + - text_color | ||
| 156 | + x-apifox-orders: | ||
| 157 | + - id | ||
| 158 | + - name | ||
| 159 | + - bg_color | ||
| 160 | + - text_color | ||
| 161 | + title: 产品标签 | ||
| 162 | + required: | ||
| 163 | + - id | ||
| 164 | + - product_name | ||
| 165 | + - recommend | ||
| 166 | + - form_sn | ||
| 167 | + - created_time | ||
| 168 | + - categories | ||
| 169 | + - tags | ||
| 170 | + x-apifox-orders: | ||
| 171 | + - id | ||
| 172 | + - product_name | ||
| 173 | + - recommend | ||
| 174 | + - form_sn | ||
| 175 | + - created_time | ||
| 176 | + - categories | ||
| 177 | + - tags | ||
| 178 | + title: 产品列表 | ||
| 179 | + total: | ||
| 180 | + type: integer | ||
| 181 | + title: 产品总数 | ||
| 182 | + required: | ||
| 183 | + - categories | ||
| 184 | + - list | ||
| 185 | + - total | ||
| 186 | + x-apifox-orders: | ||
| 187 | + - categories | ||
| 188 | + - list | ||
| 189 | + - total | ||
| 190 | + required: | ||
| 191 | + - code | ||
| 192 | + - msg | ||
| 193 | + - data | ||
| 194 | + x-apifox-orders: | ||
| 195 | + - code | ||
| 196 | + - msg | ||
| 197 | + - data | ||
| 198 | + example: | ||
| 199 | + code: 1 | ||
| 200 | + msg: 0 | ||
| 201 | + data: | ||
| 202 | + categories: | ||
| 203 | + - id: 2769851 | ||
| 204 | + name: '11' | ||
| 205 | + list: | ||
| 206 | + - id: 2769856 | ||
| 207 | + product_name: '22' | ||
| 208 | + recommend: hot | ||
| 209 | + form_sn: customize_jsj_pnzuky | ||
| 210 | + created_time: '2026-02-03 10:36:29' | ||
| 211 | + categories: | ||
| 212 | + - id: '2769851' | ||
| 213 | + name: '11' | ||
| 214 | + tags: | ||
| 215 | + - id: '2769847' | ||
| 216 | + name: '111' | ||
| 217 | + bg_color: '#3e5160' | ||
| 218 | + text_color: '#ffffff' | ||
| 219 | + - id: 2769848 | ||
| 220 | + product_name: '1111' | ||
| 221 | + recommend: normal | ||
| 222 | + form_sn: customize_jsj_ivleuz | ||
| 223 | + created_time: '2026-02-02 16:33:01' | ||
| 224 | + categories: | ||
| 225 | + - id: '2769851' | ||
| 226 | + name: '11' | ||
| 227 | + tags: | ||
| 228 | + - id: '2769846' | ||
| 229 | + name: 测试1 | ||
| 230 | + bg_color: '#1e9fff' | ||
| 231 | + text_color: '#ffffff' | ||
| 232 | + - id: '2769847' | ||
| 233 | + name: '111' | ||
| 234 | + bg_color: '#3e5160' | ||
| 235 | + text_color: '#ffffff' | ||
| 236 | + - id: 2769845 | ||
| 237 | + product_name: '1' | ||
| 238 | + recommend: normal | ||
| 239 | + form_sn: customize_jsj_pnzuky | ||
| 240 | + created_time: '2026-02-02 16:22:26' | ||
| 241 | + categories: [] | ||
| 242 | + tags: [] | ||
| 243 | + total: 3 | ||
| 244 | + headers: {} | ||
| 245 | + x-apifox-name: 成功 | ||
| 246 | + x-apifox-ordering: 0 | ||
| 247 | + security: [] | ||
| 248 | + x-apifox-folder: 文档 | ||
| 249 | + x-apifox-status: developing | ||
| 250 | + x-run-in-apifox: https://app.apifox.com/web/project/7792797/apis/api-414702244-run | ||
| 251 | +components: | ||
| 252 | + schemas: {} | ||
| 253 | + responses: {} | ||
| 254 | + securitySchemes: {} | ||
| 255 | +servers: [] | ||
| 256 | +security: [] | ||
| 257 | + | ||
| 258 | +``` |
| ... | @@ -29,8 +29,6 @@ | ... | @@ -29,8 +29,6 @@ |
| 29 | "postinstall": "weapp-tw patch", | 29 | "postinstall": "weapp-tw patch", |
| 30 | "lint": "eslint --ext .js,.vue src", | 30 | "lint": "eslint --ext .js,.vue src", |
| 31 | "api:generate": "node scripts/generateApiFromOpenAPI.js", | 31 | "api:generate": "node scripts/generateApiFromOpenAPI.js", |
| 32 | - "api:sync": "node scripts/apifox-to-openapi.js", | ||
| 33 | - "api:test": "node scripts/test-apifox-connection.js", | ||
| 34 | "changelog:check": "bash scripts/check-changelog.sh 7", | 32 | "changelog:check": "bash scripts/check-changelog.sh 7", |
| 35 | "changelog:check:30": "bash scripts/check-changelog.sh 30", | 33 | "changelog:check:30": "bash scripts/check-changelog.sh 30", |
| 36 | "changelog:check:all": "bash scripts/check-changelog.sh 0" | 34 | "changelog:check:all": "bash scripts/check-changelog.sh 0" | ... | ... |
scripts/API_GUIDE.md
0 → 100644
| 1 | +# API 文档生成指南 | ||
| 2 | + | ||
| 3 | +本项目的 API 文档采用手动维护的方式。 | ||
| 4 | + | ||
| 5 | +## 📝 工作流程 | ||
| 6 | + | ||
| 7 | +### 1. 维护 OpenAPI 文档 | ||
| 8 | + | ||
| 9 | +在 `docs/api-specs/` 目录中维护 OpenAPI 文档。 | ||
| 10 | + | ||
| 11 | +#### 目录结构 | ||
| 12 | + | ||
| 13 | +``` | ||
| 14 | +docs/api-specs/ | ||
| 15 | +├── user/ # 用户模块 | ||
| 16 | +│ ├── login.md | ||
| 17 | +│ ├── login_status.md | ||
| 18 | +│ └── ... | ||
| 19 | +├── favorite/ # 收藏模块 | ||
| 20 | +│ ├── add.md | ||
| 21 | +│ ├── del.md | ||
| 22 | +│ └── list.md | ||
| 23 | +└── ... | ||
| 24 | +``` | ||
| 25 | + | ||
| 26 | +#### 文档格式 | ||
| 27 | + | ||
| 28 | +每个 `.md` 文件包含: | ||
| 29 | +- 接口描述(Markdown 格式) | ||
| 30 | +- OpenAPI 3.0.1 规范(YAML 格式) | ||
| 31 | + | ||
| 32 | +**示例**(`docs/api-specs/user/login.md`): | ||
| 33 | + | ||
| 34 | +\`\`\`markdown | ||
| 35 | +# 登录并绑定 OpenID | ||
| 36 | + | ||
| 37 | +## 接口信息 | ||
| 38 | + | ||
| 39 | +- **方法**: POST | ||
| 40 | +- **路径**: /srv/?a=user&t=login | ||
| 41 | +- **标签**: user | ||
| 42 | + | ||
| 43 | +## OpenAPI 规范 | ||
| 44 | + | ||
| 45 | +\`\`\`yaml | ||
| 46 | +openapi: 3.0.0 | ||
| 47 | +info: | ||
| 48 | + title: 登录并绑定 OpenID | ||
| 49 | + description: 使用手机号和验证码登录,并绑定到 OpenID | ||
| 50 | + version: 1.0.0 | ||
| 51 | +paths: | ||
| 52 | + /srv/?: | ||
| 53 | + post: | ||
| 54 | + summary: 登录并绑定 OpenID | ||
| 55 | + description: 使用手机号和验证码登录,并绑定到 OpenID | ||
| 56 | + requestBody: | ||
| 57 | + content: | ||
| 58 | + application/x-www-form-urlencoded: | ||
| 59 | + schema: | ||
| 60 | + type: object | ||
| 61 | + required: | ||
| 62 | + - f | ||
| 63 | + - a | ||
| 64 | + - t | ||
| 65 | + properties: | ||
| 66 | + f: | ||
| 67 | + type: string | ||
| 68 | + description: 业务模块标识 | ||
| 69 | + example: manulife | ||
| 70 | + a: | ||
| 71 | + type: string | ||
| 72 | + description: 模块名(user) | ||
| 73 | + example: user | ||
| 74 | + t: | ||
| 75 | + type: string | ||
| 76 | + description: 接口类型(login) | ||
| 77 | + example: login | ||
| 78 | + phone: | ||
| 79 | + type: string | ||
| 80 | + description: 手机号 | ||
| 81 | + example: '13800138000' | ||
| 82 | + code: | ||
| 83 | + type: string | ||
| 84 | + description: 验证码 | ||
| 85 | + example: '123456' | ||
| 86 | + openid: | ||
| 87 | + type: string | ||
| 88 | + description: 微信 OpenID | ||
| 89 | + example: 'oXXXX-XXXXXXXXXXXXXXXXXXX' | ||
| 90 | + responses: | ||
| 91 | + '200': | ||
| 92 | + description: 成功 | ||
| 93 | + content: | ||
| 94 | + application/json: | ||
| 95 | + schema: | ||
| 96 | + type: object | ||
| 97 | + properties: | ||
| 98 | + code: | ||
| 99 | + type: number | ||
| 100 | + description: 状态码(0=失败,1=成功) | ||
| 101 | + msg: | ||
| 102 | + type: string | ||
| 103 | + description: 消息 | ||
| 104 | + data: | ||
| 105 | + type: object | ||
| 106 | + description: 用户信息 | ||
| 107 | + properties: | ||
| 108 | + id: | ||
| 109 | + type: number | ||
| 110 | + description: 用户 ID | ||
| 111 | + avatar: | ||
| 112 | + type: string | ||
| 113 | + description: 头像 URL | ||
| 114 | + name: | ||
| 115 | + type: string | ||
| 116 | + description: 姓名 | ||
| 117 | +\`\`\` | ||
| 118 | +\`\`\` | ||
| 119 | + | ||
| 120 | +### 2. 生成 API 代码 | ||
| 121 | + | ||
| 122 | +运行生成脚本: | ||
| 123 | + | ||
| 124 | +```bash | ||
| 125 | +node scripts/generateApiFromOpenAPI.js | ||
| 126 | +``` | ||
| 127 | + | ||
| 128 | +#### 生成内容 | ||
| 129 | + | ||
| 130 | +脚本会: | ||
| 131 | +1. 扫描 `docs/api-specs/` 目录 | ||
| 132 | +2. 解析每个 `.md` 文件中的 OpenAPI 规范 | ||
| 133 | +3. 生成对应的 JavaScript API 文件到 `src/api/` 目录 | ||
| 134 | + | ||
| 135 | +#### 输出示例 | ||
| 136 | + | ||
| 137 | +\`\`\` | ||
| 138 | +=== OpenAPI 转 API 文档生成器 === | ||
| 139 | + | ||
| 140 | +输入目录: /Users/huyirui/program/itomix/git/manulife-weapp/docs/api-specs | ||
| 141 | +输出目录: /Users/huyirui/program/itomix/git/manulife-weapp/src/api | ||
| 142 | + | ||
| 143 | +💾 备份当前 OpenAPI 文档... | ||
| 144 | + | ||
| 145 | +找到 9 个模块: event, favorite, feedback, file, get_file_list, get_product, news, user, wechat | ||
| 146 | + | ||
| 147 | +处理模块: user | ||
| 148 | +找到 5 个 API 文档 | ||
| 149 | + ✓ get_profile: 获取个人信息 | ||
| 150 | + ✓ login: 登录并绑定openid | ||
| 151 | + ✓ login_status: 查询登录状态 | ||
| 152 | + ✓ logout: 退出登录并解绑openid | ||
| 153 | + ✓ update_profile: 更新个人资料 | ||
| 154 | + 📝 生成文件: /Users/huyirui/program/itomix/git/manulife-weapp/src/api/user.js | ||
| 155 | + | ||
| 156 | +✅ API 文档生成完成! | ||
| 157 | +\`\`\` | ||
| 158 | + | ||
| 159 | +### 3. 使用生成的 API | ||
| 160 | + | ||
| 161 | +在组件中导入并使用: | ||
| 162 | + | ||
| 163 | +\`\`\`javascript | ||
| 164 | +import { loginAPI, getUserProfileAPI } from '@/api/user'; | ||
| 165 | + | ||
| 166 | +// 登录 | ||
| 167 | +const result = await loginAPI({ | ||
| 168 | + phone: '13800138000', | ||
| 169 | + code: '123456', | ||
| 170 | + openid: 'oXXXX-XXXXXXXXXXXXXXXXXXX' | ||
| 171 | +}); | ||
| 172 | + | ||
| 173 | +if (result.code === 1) { | ||
| 174 | + console.log('登录成功', result.data); | ||
| 175 | +} | ||
| 176 | +\`\`\` | ||
| 177 | + | ||
| 178 | +## 🔧 高级功能 | ||
| 179 | + | ||
| 180 | +### API 变更检测 | ||
| 181 | + | ||
| 182 | +脚本会自动检测 API 变更: | ||
| 183 | + | ||
| 184 | +- ✅ **新增接口** - 检测到新的 API 文档 | ||
| 185 | +- ⚠️ **修改接口** - 检测到 API 规范变更 | ||
| 186 | +- ❌ **删除接口** - 检测到删除的 API 文档 | ||
| 187 | + | ||
| 188 | +#### 变更报告示例 | ||
| 189 | + | ||
| 190 | +\`\`\` | ||
| 191 | +🔍 开始检测 API 变更... | ||
| 192 | + | ||
| 193 | +📦 新增模块: user | ||
| 194 | + 包含 2 个新增接口: | ||
| 195 | + • POST /srv/?a=user&t=login - 登录并绑定 OpenID | ||
| 196 | + • GET /srv/?a=user&t=get_profile - 获取个人信息 | ||
| 197 | + | ||
| 198 | +📦 对比范围: 9 个旧接口 → 11 个新接口 | ||
| 199 | +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ | ||
| 200 | + | ||
| 201 | +✅ 新增接口 (2): | ||
| 202 | + + login - 登录并绑定 OpenID | ||
| 203 | + + get_profile - 获取个人信息 | ||
| 204 | + | ||
| 205 | +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ | ||
| 206 | +总计: 2 新增, 0 修改, 0 删除 | ||
| 207 | +✅ 未检测到破坏性变更 | ||
| 208 | +\`\`\` | ||
| 209 | + | ||
| 210 | +## 📚 最佳实践 | ||
| 211 | + | ||
| 212 | +### 1. 文档命名 | ||
| 213 | + | ||
| 214 | +- 使用语义化文件名(如 `login.md`, `get_list.md`) | ||
| 215 | +- 避免使用通用名称(如 `api.md`, `endpoint.md`) | ||
| 216 | + | ||
| 217 | +### 2. 参数定义 | ||
| 218 | + | ||
| 219 | +- **必填参数**:在 `required` 数组中列出 | ||
| 220 | +- **可选参数**:不在 `required` 中 | ||
| 221 | +- **描述**:为每个参数添加清晰的 `description` | ||
| 222 | +- **示例**:为每个参数添加 `example` | ||
| 223 | + | ||
| 224 | +### 3. 响应结构 | ||
| 225 | + | ||
| 226 | +- 统一使用 `{ code, msg, data }` 格式 | ||
| 227 | +- `code`: 状态码(0=失败,1=成功) | ||
| 228 | +- `msg`: 消息说明 | ||
| 229 | +- `data`: 数据内容 | ||
| 230 | + | ||
| 231 | +### 4. 文档分组 | ||
| 232 | + | ||
| 233 | +- 按业务模块分组(user, favorite, product 等) | ||
| 234 | +- 每个模块一个目录 | ||
| 235 | +- 相关接口放在同一目录 | ||
| 236 | + | ||
| 237 | +## 🛠️ 故障排除 | ||
| 238 | + | ||
| 239 | +### 问题:YAML 解析失败 | ||
| 240 | + | ||
| 241 | +**错误信息**: | ||
| 242 | +\`\`\` | ||
| 243 | +✗ login.md: 解析失败 - YAML 代码块格式错误 | ||
| 244 | +\`\`\` | ||
| 245 | + | ||
| 246 | +**解决方案**: | ||
| 247 | +- 检查 YAML 代码块是否正确包裹在 `\`\`\`yaml` 和 `\`\`\`` 之间 | ||
| 248 | +- 检查 YAML 缩进是否正确(使用空格,不要使用 Tab) | ||
| 249 | +- 使用在线 YAML 验证器验证格式 | ||
| 250 | + | ||
| 251 | +### 问题:未找到 YAML 代码块 | ||
| 252 | + | ||
| 253 | +**错误信息**: | ||
| 254 | +\`\`\` | ||
| 255 | +⚠️ login.md: 未找到 YAML 代码块 | ||
| 256 | +\`\`\` | ||
| 257 | + | ||
| 258 | +**解决方案**: | ||
| 259 | +- 确保文档中包含 `\`\`\`yaml` 代码块 | ||
| 260 | +- 检查代码块格式是否正确 | ||
| 261 | + | ||
| 262 | +### 问题:生成的 API 代码为空 | ||
| 263 | + | ||
| 264 | +**可能原因**: | ||
| 265 | +1. OpenAPI 文档格式不正确 | ||
| 266 | +2. `paths` 或 `requestBody` 定义缺失 | ||
| 267 | + | ||
| 268 | +**解决方案**: | ||
| 269 | +- 检查 OpenAPI 文档结构是否完整 | ||
| 270 | +- 参考本文档中的示例格式 | ||
| 271 | + | ||
| 272 | +## 📖 参考资料 | ||
| 273 | + | ||
| 274 | +- [OpenAPI 3.0 规范](https://swagger.io/specification/) | ||
| 275 | +- [YAML 语法指南](https://yaml.org/spec/1.2/spec.html) | ||
| 276 | +- [项目 API 文档目录](../docs/api-specs/) |
scripts/apifox-sync.js
deleted
100644 → 0
| 1 | -#!/usr/bin/env node | ||
| 2 | - | ||
| 3 | -/** | ||
| 4 | - * Apifox API 同步工具 | ||
| 5 | - * | ||
| 6 | - * 功能: | ||
| 7 | - * 1. 从 Apifox 获取所有接口数据 | ||
| 8 | - * 2. 生成 TypeScript/JavaScript API 接口代码 | ||
| 9 | - * 3. 生成 Mock 数据 | ||
| 10 | - * 4. 自动更新 src/api/ 目录 | ||
| 11 | - * | ||
| 12 | - * 使用: | ||
| 13 | - * node scripts/apifox-sync.js | ||
| 14 | - */ | ||
| 15 | - | ||
| 16 | -const fs = require('fs'); | ||
| 17 | -const path = require('path'); | ||
| 18 | -const https = require('https'); | ||
| 19 | - | ||
| 20 | -// 配置 | ||
| 21 | -const CONFIG = { | ||
| 22 | - token: process.env.VITE_APIFOX_TOKEN, | ||
| 23 | - projectId: process.env.VITE_APIFOX_PROJECT_ID, | ||
| 24 | - baseUrl: 'api.apifox.com', | ||
| 25 | - outputDir: path.join(__dirname, '../src/api'), | ||
| 26 | - mockDir: path.join(__dirname, '../src/mocks') | ||
| 27 | -}; | ||
| 28 | - | ||
| 29 | -// 颜色输出 | ||
| 30 | -const colors = { | ||
| 31 | - reset: '\x1b[0m', | ||
| 32 | - bright: '\x1b[1m', | ||
| 33 | - green: '\x1b[32m', | ||
| 34 | - yellow: '\x1b[33m', | ||
| 35 | - red: '\x1b[31m', | ||
| 36 | - blue: '\x1b[34m' | ||
| 37 | -}; | ||
| 38 | - | ||
| 39 | -function log(message, color = 'reset') { | ||
| 40 | - console.log(`${colors[color]}${message}${colors.reset}`); | ||
| 41 | -} | ||
| 42 | - | ||
| 43 | -// 读取 .env.apifox 文件 | ||
| 44 | -function loadEnv() { | ||
| 45 | - const envPath = path.join(__dirname, '../.env.apifox'); | ||
| 46 | - | ||
| 47 | - if (!fs.existsSync(envPath)) { | ||
| 48 | - log('❌ 未找到 .env.apifox 文件', 'red'); | ||
| 49 | - log('📝 请先创建 .env.apifox 文件并填写 Apifox 凭证:', 'yellow'); | ||
| 50 | - log(''); | ||
| 51 | - log('VITE_APIFOX_TOKEN=your_api_token_here', 'blue'); | ||
| 52 | - log('VITE_APIFOX_PROJECT_ID=your_project_id_here', 'blue'); | ||
| 53 | - process.exit(1); | ||
| 54 | - } | ||
| 55 | - | ||
| 56 | - const env = fs.readFileSync(envPath, 'utf-8'); | ||
| 57 | - env.split('\n').forEach(line => { | ||
| 58 | - const [key, ...valueParts] = line.split('='); | ||
| 59 | - const value = valueParts.join('='); | ||
| 60 | - if (key && !key.startsWith('#') && value) { | ||
| 61 | - process.env[key.trim()] = value.trim(); | ||
| 62 | - } | ||
| 63 | - }); | ||
| 64 | - | ||
| 65 | - if (!process.env.VITE_APIFOX_TOKEN || !process.env.VITE_APIFOX_PROJECT_ID) { | ||
| 66 | - log('❌ .env.apifox 文件中缺少必要的配置', 'red'); | ||
| 67 | - log('请确保填写了 VITE_APIFOX_TOKEN 和 VITE_APIFOX_PROJECT_ID', 'yellow'); | ||
| 68 | - process.exit(1); | ||
| 69 | - } | ||
| 70 | - | ||
| 71 | - CONFIG.token = process.env.VITE_APIFOX_TOKEN; | ||
| 72 | - CONFIG.projectId = process.env.VITE_APIFOX_PROJECT_ID; | ||
| 73 | - | ||
| 74 | - log(`✅ 已加载配置,项目 ID: ${CONFIG.projectId}`, 'green'); | ||
| 75 | -} | ||
| 76 | - | ||
| 77 | -// 发送 HTTPS 请求 | ||
| 78 | -function httpsRequest(options) { | ||
| 79 | - return new Promise((resolve, reject) => { | ||
| 80 | - const req = https.request(options, (res) => { | ||
| 81 | - const chunks = []; | ||
| 82 | - | ||
| 83 | - res.on('data', chunk => { | ||
| 84 | - chunks.push(chunk); | ||
| 85 | - }); | ||
| 86 | - | ||
| 87 | - res.on('end', () => { | ||
| 88 | - const raw = Buffer.concat(chunks).toString('utf8').trim(); | ||
| 89 | - | ||
| 90 | - if (!raw) { | ||
| 91 | - if (res.statusCode >= 200 && res.statusCode < 300) { | ||
| 92 | - resolve({ data: null, total: 0, __empty: true, statusCode: res.statusCode }); | ||
| 93 | - return; | ||
| 94 | - } | ||
| 95 | - | ||
| 96 | - reject(new Error(`HTTP ${res.statusCode}: Empty response`)); | ||
| 97 | - return; | ||
| 98 | - } | ||
| 99 | - | ||
| 100 | - try { | ||
| 101 | - const json = JSON.parse(raw.replace(/^\uFEFF/, '')); | ||
| 102 | - if (res.statusCode === 200) { | ||
| 103 | - resolve(json); | ||
| 104 | - } else { | ||
| 105 | - reject(new Error(`HTTP ${res.statusCode}: ${json.message || raw}`)); | ||
| 106 | - } | ||
| 107 | - } catch (err) { | ||
| 108 | - reject(new Error(`解析响应失败: ${err.message}`)); | ||
| 109 | - } | ||
| 110 | - }); | ||
| 111 | - }); | ||
| 112 | - | ||
| 113 | - req.on('error', reject); | ||
| 114 | - req.end(); | ||
| 115 | - }); | ||
| 116 | -} | ||
| 117 | - | ||
| 118 | -// 获取 Apifox 项目所有接口 | ||
| 119 | -async function fetchApis() { | ||
| 120 | - log('\n📡 正在从 Apifox 获取接口数据...', 'blue'); | ||
| 121 | - | ||
| 122 | - const options = { | ||
| 123 | - hostname: CONFIG.baseUrl, | ||
| 124 | - path: `/api/v1/projects/${CONFIG.projectId}/apis?pageSize=1000`, | ||
| 125 | - method: 'GET', | ||
| 126 | - headers: { | ||
| 127 | - 'Authorization': `Bearer ${CONFIG.token}`, | ||
| 128 | - 'Content-Type': 'application/json' | ||
| 129 | - } | ||
| 130 | - }; | ||
| 131 | - | ||
| 132 | - try { | ||
| 133 | - const response = await httpsRequest(options); | ||
| 134 | - const apis = Array.isArray(response.data) ? response.data : []; | ||
| 135 | - | ||
| 136 | - log(`✅ 成功获取 ${apis.length} 个接口`, 'green'); | ||
| 137 | - return apis; | ||
| 138 | - } catch (err) { | ||
| 139 | - log(`❌ 获取接口失败: ${err.message}`, 'red'); | ||
| 140 | - throw err; | ||
| 141 | - } | ||
| 142 | -} | ||
| 143 | - | ||
| 144 | -// 获取接口详情 | ||
| 145 | -async function fetchApiDetail(apiId) { | ||
| 146 | - const options = { | ||
| 147 | - hostname: CONFIG.baseUrl, | ||
| 148 | - path: `/api/v1/projects/${CONFIG.projectId}/apis/${apiId}`, | ||
| 149 | - method: 'GET', | ||
| 150 | - headers: { | ||
| 151 | - 'Authorization': `Bearer ${CONFIG.token}`, | ||
| 152 | - 'Content-Type': 'application/json' | ||
| 153 | - } | ||
| 154 | - }; | ||
| 155 | - | ||
| 156 | - try { | ||
| 157 | - const response = await httpsRequest(options); | ||
| 158 | - return response.data; | ||
| 159 | - } catch (err) { | ||
| 160 | - log(`⚠️ 获取接口详情失败 (${apiId}): ${err.message}`, 'yellow'); | ||
| 161 | - return null; | ||
| 162 | - } | ||
| 163 | -} | ||
| 164 | - | ||
| 165 | -// 生成 API 接口代码 | ||
| 166 | -function generateApiCode(apis) { | ||
| 167 | - log('\n📝 正在生成 API 接口代码...', 'blue'); | ||
| 168 | - | ||
| 169 | - let code = `/** | ||
| 170 | - * @description API 接口定义(从 Apifox 自动生成) | ||
| 171 | - * @generated by scripts/apifox-sync.js | ||
| 172 | - * @lastUpdated ${new Date().toISOString()} | ||
| 173 | - */ | ||
| 174 | - | ||
| 175 | -import { fn } from '@/api/fn'; | ||
| 176 | - | ||
| 177 | -`; | ||
| 178 | - | ||
| 179 | - // 按标签分组 | ||
| 180 | - const groupedApis = {}; | ||
| 181 | - apis.forEach(api => { | ||
| 182 | - const tags = api.attributes?.tags || ['default']; | ||
| 183 | - const tag = tags[0]; | ||
| 184 | - if (!groupedApis[tag]) { | ||
| 185 | - groupedApis[tag] = []; | ||
| 186 | - } | ||
| 187 | - groupedApis[tag].push(api); | ||
| 188 | - }); | ||
| 189 | - | ||
| 190 | - // 生成每个分组的接口 | ||
| 191 | - Object.entries(groupedApis).forEach(([tag, items]) => { | ||
| 192 | - code += `\n// ==================== ${tag} ====================\n\n`; | ||
| 193 | - | ||
| 194 | - items.forEach(api => { | ||
| 195 | - const apiId = api.id; | ||
| 196 | - const name = api.attributes?.name || apiId; | ||
| 197 | - const method = (api.attributes?.method || 'GET').toLowerCase(); | ||
| 198 | - const path = api.attributes?.path || ''; | ||
| 199 | - | ||
| 200 | - // 生成接口函数名(将路径转换为驼峰命名) | ||
| 201 | - const functionName = pathToFunctionName(method, path); | ||
| 202 | - | ||
| 203 | - // 生成注释 | ||
| 204 | - code += `/**\n`; | ||
| 205 | - code += ` * @description ${name}\n`; | ||
| 206 | - code += ` * @path ${method.toUpperCase()} ${path}\n`; | ||
| 207 | - if (api.attributes?.description) { | ||
| 208 | - code += ` * @remark ${api.attributes.description}\n`; | ||
| 209 | - } | ||
| 210 | - code += ` */\n`; | ||
| 211 | - | ||
| 212 | - // 生成接口函数 | ||
| 213 | - code += `export const ${functionName} = (params = {}) => {\n`; | ||
| 214 | - code += ` return fn({\n`; | ||
| 215 | - code += ` method: '${method}',\n`; | ||
| 216 | - code += ` url: '${path}',\n`; | ||
| 217 | - code += ` data: params\n`; | ||
| 218 | - code += ` })\n`; | ||
| 219 | - code += `}\n\n`; | ||
| 220 | - }); | ||
| 221 | - }); | ||
| 222 | - | ||
| 223 | - // 写入文件 | ||
| 224 | - const outputPath = path.join(CONFIG.outputDir, 'generated.js'); | ||
| 225 | - fs.writeFileSync(outputPath, code, 'utf-8'); | ||
| 226 | - | ||
| 227 | - log(`✅ 已生成 API 接口代码: ${outputPath}`, 'green'); | ||
| 228 | - log(` 共 ${apis.length} 个接口,${Object.keys(groupedApis).length} 个分组`, 'green'); | ||
| 229 | - | ||
| 230 | - return outputPath; | ||
| 231 | -} | ||
| 232 | - | ||
| 233 | -// 生成 Mock 数据 | ||
| 234 | -function generateMockData(apis) { | ||
| 235 | - log('\n🎭 正在生成 Mock 数据...', 'blue'); | ||
| 236 | - | ||
| 237 | - // 确保 mocks 目录存在 | ||
| 238 | - if (!fs.existsSync(CONFIG.mockDir)) { | ||
| 239 | - fs.mkdirSync(CONFIG.mockDir, { recursive: true }); | ||
| 240 | - } | ||
| 241 | - | ||
| 242 | - let mockIndex = `/** | ||
| 243 | - * @description Mock 数据(从 Apifox 自动生成) | ||
| 244 | - * @generated by scripts/apifox-sync.js | ||
| 245 | - */ | ||
| 246 | - | ||
| 247 | -`; | ||
| 248 | - | ||
| 249 | - // 为每个接口生成 Mock 数据 | ||
| 250 | - apis.forEach(api => { | ||
| 251 | - const path = api.attributes?.path || ''; | ||
| 252 | - const method = (api.attributes?.method || 'GET').toLowerCase(); | ||
| 253 | - const functionName = pathToFunctionName(method, path); | ||
| 254 | - const mockFileName = `${functionName}.mock.js`; | ||
| 255 | - | ||
| 256 | - // 从响应示例中提取 Mock 数据 | ||
| 257 | - const responses = api.attributes?.responses || []; | ||
| 258 | - const successResponse = responses.find(r => r.code === 200) || responses[0]; | ||
| 259 | - | ||
| 260 | - if (successResponse) { | ||
| 261 | - // 生成 Mock 文件 | ||
| 262 | - const mockCode = `/** | ||
| 263 | - * @description Mock data for ${functionName} | ||
| 264 | - * @path ${method.toUpperCase()} ${path} | ||
| 265 | - */ | ||
| 266 | - | ||
| 267 | -export const mock${functionName.charAt(0).toUpperCase() + functionName.slice(1)}Data = ${JSON.stringify(successResponse, null, 2)}; | ||
| 268 | - | ||
| 269 | -export default mock${functionName.charAt(0).toUpperCase() + functionName.slice(1)}Data; | ||
| 270 | -`; | ||
| 271 | - | ||
| 272 | - const mockPath = path.join(CONFIG.mockDir, mockFileName); | ||
| 273 | - fs.writeFileSync(mockPath, mockCode, 'utf-8'); | ||
| 274 | - | ||
| 275 | - mockIndex += `export { default as mock${functionName.charAt(0).toUpperCase() + functionName.slice(1)}Data } from './${mockFileName}';\n`; | ||
| 276 | - } | ||
| 277 | - }); | ||
| 278 | - | ||
| 279 | - // 写入索引文件 | ||
| 280 | - const indexPath = path.join(CONFIG.mockDir, 'index.js'); | ||
| 281 | - fs.writeFileSync(indexPath, mockIndex, 'utf-8'); | ||
| 282 | - | ||
| 283 | - log(`✅ 已生成 Mock 数据: ${indexPath}`, 'green'); | ||
| 284 | - | ||
| 285 | - return indexPath; | ||
| 286 | -} | ||
| 287 | - | ||
| 288 | -// 将 URL 路径转换为驼峰命名的函数名 | ||
| 289 | -function pathToFunctionName(method, path) { | ||
| 290 | - // 移除路径参数和查询参数 | ||
| 291 | - let cleanPath = path | ||
| 292 | - .replace(/:\w+/g, '') | ||
| 293 | - .replace(/\?.*$/, '') | ||
| 294 | - .replace(/^\//, '') | ||
| 295 | - .replace(/\/$/, ''); | ||
| 296 | - | ||
| 297 | - // 转换为驼峰命名 | ||
| 298 | - const parts = cleanPath.split('/').filter(Boolean); | ||
| 299 | - let functionName = method; | ||
| 300 | - | ||
| 301 | - parts.forEach(part => { | ||
| 302 | - // 首字母大写 | ||
| 303 | - const capitalized = part.charAt(0).toUpperCase() + part.slice(1); | ||
| 304 | - functionName += capitalized; | ||
| 305 | - }); | ||
| 306 | - | ||
| 307 | - // 移除特殊字符 | ||
| 308 | - functionName = functionName.replace(/[^a-zA-Z0-9]/g, ''); | ||
| 309 | - | ||
| 310 | - return functionName; | ||
| 311 | -} | ||
| 312 | - | ||
| 313 | -// 主函数 | ||
| 314 | -async function main() { | ||
| 315 | - try { | ||
| 316 | - log('\n🚀 Apifox API 同步工具', 'bright'); | ||
| 317 | - log('=' .repeat(50), 'bright'); | ||
| 318 | - | ||
| 319 | - // 1. 加载配置 | ||
| 320 | - loadEnv(); | ||
| 321 | - | ||
| 322 | - // 2. 获取接口列表 | ||
| 323 | - const apis = await fetchApis(); | ||
| 324 | - | ||
| 325 | - // 3. 生成 API 接口代码 | ||
| 326 | - generateApiCode(apis); | ||
| 327 | - | ||
| 328 | - // 4. 生成 Mock 数据 | ||
| 329 | - generateMockData(apis); | ||
| 330 | - | ||
| 331 | - // 完成 | ||
| 332 | - log('\n✅ 同步完成!', 'green'); | ||
| 333 | - log('📦 生成的文件:', 'blue'); | ||
| 334 | - log(` - src/api/generated.js (API 接口)`, 'blue'); | ||
| 335 | - log(` - src/mocks/ (Mock 数据)`, 'blue'); | ||
| 336 | - log('\n💡 提示: 将 src/api/generated.js 中的接口导入到 src/api/index.js 中使用', 'yellow'); | ||
| 337 | - | ||
| 338 | - } catch (err) { | ||
| 339 | - log(`\n❌ 同步失败: ${err.message}`, 'red'); | ||
| 340 | - process.exit(1); | ||
| 341 | - } | ||
| 342 | -} | ||
| 343 | - | ||
| 344 | -// 运行 | ||
| 345 | -main(); |
scripts/apifox-to-openapi.js
deleted
100644 → 0
This diff is collapsed. Click to expand it.
scripts/debug-apifox-response.js
deleted
100644 → 0
| 1 | -#!/usr/bin/env node | ||
| 2 | - | ||
| 3 | -/** | ||
| 4 | - * Apifox API 响应调试工具 | ||
| 5 | - * | ||
| 6 | - * 功能:输出完整的 API 响应,帮助诊断问题 | ||
| 7 | - */ | ||
| 8 | - | ||
| 9 | -const fs = require('fs'); | ||
| 10 | -const path = require('path'); | ||
| 11 | -const https = require('https'); | ||
| 12 | - | ||
| 13 | -// 加载配置 | ||
| 14 | -function loadConfig() { | ||
| 15 | - const envPath = path.join(__dirname, '../.env.apifox'); | ||
| 16 | - const env = fs.readFileSync(envPath, 'utf-8'); | ||
| 17 | - const config = {}; | ||
| 18 | - | ||
| 19 | - env.split('\n').forEach(line => { | ||
| 20 | - const [key, ...valueParts] = line.split('='); | ||
| 21 | - const value = valueParts.join('='); | ||
| 22 | - if (key && !key.startsWith('#') && value) { | ||
| 23 | - config[key.trim()] = value.trim(); | ||
| 24 | - } | ||
| 25 | - }); | ||
| 26 | - | ||
| 27 | - return config; | ||
| 28 | -} | ||
| 29 | - | ||
| 30 | -// 发送 HTTPS 请求 | ||
| 31 | -function httpsRequest(options) { | ||
| 32 | - return new Promise((resolve, reject) => { | ||
| 33 | - console.log(`📡 请求 URL: https://${options.hostname}${options.path}`); | ||
| 34 | - console.log(`📋 请求头:`, JSON.stringify(options.headers, null, 2)); | ||
| 35 | - | ||
| 36 | - const req = https.request(options, (res) => { | ||
| 37 | - console.log(`\n📊 响应状态码: ${res.statusCode}`); | ||
| 38 | - console.log(`📋 响应头:`, JSON.stringify(res.headers, null, 2)); | ||
| 39 | - | ||
| 40 | - const chunks = []; | ||
| 41 | - | ||
| 42 | - res.on('data', chunk => { | ||
| 43 | - chunks.push(chunk); | ||
| 44 | - }); | ||
| 45 | - | ||
| 46 | - res.on('end', () => { | ||
| 47 | - const raw = Buffer.concat(chunks).toString('utf8').trim(); | ||
| 48 | - | ||
| 49 | - if (!raw) { | ||
| 50 | - console.log('⚠️ 响应体为空'); | ||
| 51 | - resolve({ raw: null, statusCode: res.statusCode }); | ||
| 52 | - return; | ||
| 53 | - } | ||
| 54 | - | ||
| 55 | - console.log(`\n📦 响应体长度: ${raw.length} 字符`); | ||
| 56 | - console.log(`📄 响应体内容:`); | ||
| 57 | - console.log('---开始---'); | ||
| 58 | - console.log(raw); | ||
| 59 | - console.log('---结束---\n'); | ||
| 60 | - | ||
| 61 | - try { | ||
| 62 | - const json = JSON.parse(raw.replace(/^\uFEFF/, '')); | ||
| 63 | - resolve(json); | ||
| 64 | - } catch (err) { | ||
| 65 | - console.error(`❌ JSON 解析失败: ${err.message}`); | ||
| 66 | - resolve({ raw, parseError: err.message }); | ||
| 67 | - } | ||
| 68 | - }); | ||
| 69 | - }); | ||
| 70 | - | ||
| 71 | - req.on('error', (err) => { | ||
| 72 | - console.error(`❌ 请求失败: ${err.message}`); | ||
| 73 | - reject(err); | ||
| 74 | - }); | ||
| 75 | - | ||
| 76 | - req.end(); | ||
| 77 | - }); | ||
| 78 | -} | ||
| 79 | - | ||
| 80 | -// 主函数 | ||
| 81 | -async function main() { | ||
| 82 | - console.log('🔍 Apifox API 响应调试工具\n'); | ||
| 83 | - | ||
| 84 | - const config = loadConfig(); | ||
| 85 | - | ||
| 86 | - // 测试获取接口列表 | ||
| 87 | - console.log('========================================'); | ||
| 88 | - console.log('测试: 获取接口列表'); | ||
| 89 | - console.log('========================================\n'); | ||
| 90 | - | ||
| 91 | - const options = { | ||
| 92 | - hostname: 'api.apifox.com', | ||
| 93 | - path: `/api/v1/projects/${config.VITE_APIFOX_PROJECT_ID}/apis?pageSize=10`, | ||
| 94 | - method: 'GET', | ||
| 95 | - headers: { | ||
| 96 | - 'Authorization': `Bearer ${config.VITE_APIFOX_TOKEN}`, | ||
| 97 | - 'Content-Type': 'application/json' | ||
| 98 | - } | ||
| 99 | - }; | ||
| 100 | - | ||
| 101 | - try { | ||
| 102 | - const response = await httpsRequest(options); | ||
| 103 | - | ||
| 104 | - console.log('========================================'); | ||
| 105 | - console.log('解析结果'); | ||
| 106 | - console.log('========================================\n'); | ||
| 107 | - | ||
| 108 | - if (response.parseError) { | ||
| 109 | - console.log(`❌ 无法解析 JSON`); | ||
| 110 | - console.log(`原始响应:`, response.raw); | ||
| 111 | - } else { | ||
| 112 | - console.log(`✅ JSON 解析成功`); | ||
| 113 | - console.log(`响应类型: ${typeof response}`); | ||
| 114 | - console.log(`响应键:`, Object.keys(response)); | ||
| 115 | - console.log(`\ndata 类型: ${typeof response.data}`); | ||
| 116 | - console.log(`data 是数组: ${Array.isArray(response.data)}`); | ||
| 117 | - console.log(`data 长度: ${response.data?.length || 0}`); | ||
| 118 | - console.log(`total: ${response.total}`); | ||
| 119 | - | ||
| 120 | - if (response.data && response.data.length > 0) { | ||
| 121 | - console.log(`\n第一个接口示例:`); | ||
| 122 | - console.log(JSON.stringify(response.data[0], null, 2)); | ||
| 123 | - } | ||
| 124 | - } | ||
| 125 | - | ||
| 126 | - } catch (err) { | ||
| 127 | - console.error(`\n❌ 请求失败: ${err.message}`); | ||
| 128 | - } | ||
| 129 | -} | ||
| 130 | - | ||
| 131 | -main(); |
| ... | @@ -709,9 +709,11 @@ function compareAPIChanges(openAPIDir) { | ... | @@ -709,9 +709,11 @@ function compareAPIChanges(openAPIDir) { |
| 709 | try { | 709 | try { |
| 710 | const newDocs = parseOpenAPIPath(moduleDir); | 710 | const newDocs = parseOpenAPIPath(moduleDir); |
| 711 | if (newDocs && newDocs.length > 0) { | 711 | if (newDocs && newDocs.length > 0) { |
| 712 | - console.log(` 包含 ${newDocs.length} 个新增接口:`); | 712 | + // 使用 extractAPIInfo 提取 API 信息 |
| 713 | - newDocs.forEach(doc => { | 713 | + const apiInfos = newDocs.map(doc => extractAPIInfo(doc)); |
| 714 | - console.log(` • ${doc.method} ${doc.path} - ${doc.summary}`); | 714 | + console.log(` 包含 ${apiInfos.length} 个新增接口:`); |
| 715 | + apiInfos.forEach(api => { | ||
| 716 | + console.log(` • ${api.method} ${api.path} - ${api.summary || api.name}`); | ||
| 715 | }); | 717 | }); |
| 716 | } | 718 | } |
| 717 | } catch (error) { | 719 | } catch (error) { | ... | ... |
scripts/test-apifox-connection.js
deleted
100644 → 0
| 1 | -#!/usr/bin/env node | ||
| 2 | - | ||
| 3 | -/** | ||
| 4 | - * Apifox 连接测试工具 | ||
| 5 | - * | ||
| 6 | - * 功能: | ||
| 7 | - * 1. 验证 .env.apifox 配置是否正确 | ||
| 8 | - * 2. 测试 Apifox API 连接 | ||
| 9 | - * 3. 显示项目基本信息 | ||
| 10 | - * 4. 列出所有接口数量 | ||
| 11 | - * | ||
| 12 | - * 使用: | ||
| 13 | - * node scripts/test-apifox-connection.js | ||
| 14 | - */ | ||
| 15 | - | ||
| 16 | -const fs = require('fs'); | ||
| 17 | -const path = require('path'); | ||
| 18 | -const https = require('https'); | ||
| 19 | - | ||
| 20 | -// 颜色输出 | ||
| 21 | -const colors = { | ||
| 22 | - reset: '\x1b[0m', | ||
| 23 | - bright: '\x1b[1m', | ||
| 24 | - green: '\x1b[32m', | ||
| 25 | - yellow: '\x1b[33m', | ||
| 26 | - red: '\x1b[31m', | ||
| 27 | - blue: '\x1b[34m', | ||
| 28 | - cyan: '\x1b[36m' | ||
| 29 | -}; | ||
| 30 | - | ||
| 31 | -function log(message, color = 'reset') { | ||
| 32 | - console.log(`${colors[color]}${message}${colors.reset}`); | ||
| 33 | -} | ||
| 34 | - | ||
| 35 | -// 加载配置 | ||
| 36 | -function loadConfig() { | ||
| 37 | - const envPath = path.join(__dirname, '../.env.apifox'); | ||
| 38 | - | ||
| 39 | - if (!fs.existsSync(envPath)) { | ||
| 40 | - log('❌ 未找到 .env.apifox 文件', 'red'); | ||
| 41 | - log('\n📝 请先创建配置文件:', 'yellow'); | ||
| 42 | - log(' cp .env.apifox.example .env.apifox', 'blue'); | ||
| 43 | - log(' 然后编辑 .env.apifox 填写凭证信息', 'blue'); | ||
| 44 | - return null; | ||
| 45 | - } | ||
| 46 | - | ||
| 47 | - const env = fs.readFileSync(envPath, 'utf-8'); | ||
| 48 | - const config = {}; | ||
| 49 | - | ||
| 50 | - env.split('\n').forEach(line => { | ||
| 51 | - const [key, ...valueParts] = line.split('='); | ||
| 52 | - const value = valueParts.join('='); | ||
| 53 | - if (key && !key.startsWith('#') && value) { | ||
| 54 | - config[key.trim()] = value.trim(); | ||
| 55 | - } | ||
| 56 | - }); | ||
| 57 | - | ||
| 58 | - if (!config.VITE_APIFOX_TOKEN || !config.VITE_APIFOX_PROJECT_ID) { | ||
| 59 | - log('❌ .env.apifox 文件中缺少必要的配置', 'red'); | ||
| 60 | - log('\n请确保填写了以下配置:', 'yellow'); | ||
| 61 | - log(' VITE_APIFOX_TOKEN=aps-your_token_here', 'blue'); | ||
| 62 | - log(' VITE_APIFOX_PROJECT_ID=your_project_id_here', 'blue'); | ||
| 63 | - return null; | ||
| 64 | - } | ||
| 65 | - | ||
| 66 | - return config; | ||
| 67 | -} | ||
| 68 | - | ||
| 69 | -// 验证 Token 格式 | ||
| 70 | -function validateToken(token) { | ||
| 71 | - // Apifox Token 格式:APS-xxxxxxxx (大写) | ||
| 72 | - if (!token.toUpperCase().startsWith('APS-')) { | ||
| 73 | - log('⚠️ Token 格式不正确', 'yellow'); | ||
| 74 | - log(' 正确格式: APS-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', 'cyan'); | ||
| 75 | - return false; | ||
| 76 | - } | ||
| 77 | - | ||
| 78 | - if (token.length < 10) { | ||
| 79 | - log('⚠️ Token 长度似乎不正确', 'yellow'); | ||
| 80 | - return false; | ||
| 81 | - } | ||
| 82 | - | ||
| 83 | - return true; | ||
| 84 | -} | ||
| 85 | - | ||
| 86 | -// 验证项目 ID 格式 | ||
| 87 | -function validateProjectId(projectId) { | ||
| 88 | - if (!/^\d+$/.test(projectId)) { | ||
| 89 | - log('⚠️ 项目 ID 格式不正确', 'yellow'); | ||
| 90 | - log(' 正确格式: 纯数字(如: 12345678)', 'cyan'); | ||
| 91 | - return false; | ||
| 92 | - } | ||
| 93 | - | ||
| 94 | - return true; | ||
| 95 | -} | ||
| 96 | - | ||
| 97 | -// 发送 HTTPS 请求 | ||
| 98 | -function httpsRequest(options) { | ||
| 99 | - return new Promise((resolve, reject) => { | ||
| 100 | - log(`📡 发送请求到: https://${options.hostname}${options.path}`, 'blue'); | ||
| 101 | - | ||
| 102 | - const req = https.request(options, (res) => { | ||
| 103 | - const chunks = []; | ||
| 104 | - | ||
| 105 | - res.on('data', chunk => { | ||
| 106 | - chunks.push(chunk); | ||
| 107 | - }); | ||
| 108 | - | ||
| 109 | - res.on('end', () => { | ||
| 110 | - const raw = Buffer.concat(chunks).toString('utf8').trim(); | ||
| 111 | - | ||
| 112 | - if (!raw) { | ||
| 113 | - if (res.statusCode >= 200 && res.statusCode < 300) { | ||
| 114 | - resolve({ data: null, total: 0, __empty: true, statusCode: res.statusCode }); | ||
| 115 | - return; | ||
| 116 | - } | ||
| 117 | - | ||
| 118 | - reject(new Error(`HTTP ${res.statusCode}: Empty response`)); | ||
| 119 | - return; | ||
| 120 | - } | ||
| 121 | - | ||
| 122 | - try { | ||
| 123 | - const json = JSON.parse(raw.replace(/^\uFEFF/, '')); | ||
| 124 | - if (res.statusCode === 200) { | ||
| 125 | - resolve(json); | ||
| 126 | - } else { | ||
| 127 | - reject(new Error(`HTTP ${res.statusCode}: ${json.message || raw}`)); | ||
| 128 | - } | ||
| 129 | - } catch (err) { | ||
| 130 | - reject(new Error(`解析响应失败: ${err.message}`)); | ||
| 131 | - } | ||
| 132 | - }); | ||
| 133 | - }); | ||
| 134 | - | ||
| 135 | - req.on('error', reject); | ||
| 136 | - req.end(); | ||
| 137 | - }); | ||
| 138 | -} | ||
| 139 | - | ||
| 140 | -// 测试获取项目信息 | ||
| 141 | -async function testProjectInfo(config) { | ||
| 142 | - log('\n📊 获取项目信息...', 'cyan'); | ||
| 143 | - | ||
| 144 | - const options = { | ||
| 145 | - hostname: 'api.apifox.com', | ||
| 146 | - path: `/api/v1/projects/${config.VITE_APIFOX_PROJECT_ID}`, | ||
| 147 | - method: 'GET', | ||
| 148 | - headers: { | ||
| 149 | - 'Authorization': `Bearer ${config.VITE_APIFOX_TOKEN}`, | ||
| 150 | - 'Content-Type': 'application/json' | ||
| 151 | - } | ||
| 152 | - }; | ||
| 153 | - | ||
| 154 | - try { | ||
| 155 | - const response = await httpsRequest(options); | ||
| 156 | - if (!response || !response.data) { | ||
| 157 | - throw new Error('响应为空或缺少 data 字段'); | ||
| 158 | - } | ||
| 159 | - | ||
| 160 | - const project = response.data; | ||
| 161 | - | ||
| 162 | - log('✅ 项目信息获取成功', 'green'); | ||
| 163 | - log(''); | ||
| 164 | - log('项目详情:', 'bright'); | ||
| 165 | - log(` 名称: ${project.name || '未设置'}`, 'reset'); | ||
| 166 | - log(` ID: ${project.id}`, 'reset'); | ||
| 167 | - log(` 描述: ${project.description || '无'}`, 'reset'); | ||
| 168 | - log(` 创建时间: ${project.createdTime || '未知'}`, 'reset'); | ||
| 169 | - | ||
| 170 | - return true; | ||
| 171 | - } catch (err) { | ||
| 172 | - log(`❌ 获取项目信息失败: ${err.message}`, 'red'); | ||
| 173 | - | ||
| 174 | - if (err.message.includes('401')) { | ||
| 175 | - log('\n可能的原因:', 'yellow'); | ||
| 176 | - log(' 1. API Token 错误或已过期', 'yellow'); | ||
| 177 | - log(' 2. 请检查 .env.apifox 中的 VITE_APIFOX_TOKEN', 'yellow'); | ||
| 178 | - } else if (err.message.includes('403') || err.message.includes('404')) { | ||
| 179 | - log('\n可能的原因:', 'yellow'); | ||
| 180 | - log(' 1. 项目 ID 错误', 'yellow'); | ||
| 181 | - log(' 2. 账号无该项目的访问权限', 'yellow'); | ||
| 182 | - log(' 3. 请检查 .env.apifox 中的 VITE_APIFOX_PROJECT_ID', 'yellow'); | ||
| 183 | - } | ||
| 184 | - | ||
| 185 | - return false; | ||
| 186 | - } | ||
| 187 | -} | ||
| 188 | - | ||
| 189 | -// 测试获取接口列表 | ||
| 190 | -async function testApiList(config) { | ||
| 191 | - log('\n📋 获取接口列表...', 'cyan'); | ||
| 192 | - | ||
| 193 | - const options = { | ||
| 194 | - hostname: 'api.apifox.com', | ||
| 195 | - path: `/api/v1/projects/${config.VITE_APIFOX_PROJECT_ID}/apis?pageSize=10`, | ||
| 196 | - method: 'GET', | ||
| 197 | - headers: { | ||
| 198 | - 'Authorization': `Bearer ${config.VITE_APIFOX_TOKEN}`, | ||
| 199 | - 'Content-Type': 'application/json' | ||
| 200 | - } | ||
| 201 | - }; | ||
| 202 | - | ||
| 203 | - try { | ||
| 204 | - const response = await httpsRequest(options); | ||
| 205 | - const apis = Array.isArray(response.data) ? response.data : []; | ||
| 206 | - | ||
| 207 | - log('✅ 接口列表获取成功', 'green'); | ||
| 208 | - log(` 共找到 ${response.total || apis.length} 个接口`, 'blue'); | ||
| 209 | - | ||
| 210 | - if (apis.length > 0) { | ||
| 211 | - log('\n前 10 个接口:', 'bright'); | ||
| 212 | - apis.forEach((api, index) => { | ||
| 213 | - const method = (api.attributes?.method || 'GET').toUpperCase(); | ||
| 214 | - const path = api.attributes?.path || '/'; | ||
| 215 | - const name = api.attributes?.name || api.id; | ||
| 216 | - | ||
| 217 | - log(` ${index + 1}. ${method} ${path}`, 'cyan'); | ||
| 218 | - log(` ${name}`, 'reset'); | ||
| 219 | - }); | ||
| 220 | - | ||
| 221 | - if (response.total > 10) { | ||
| 222 | - log(`\n ... 还有 ${response.total - 10} 个接口`, 'yellow'); | ||
| 223 | - } | ||
| 224 | - } else if (response.__empty) { | ||
| 225 | - log('⚠️ 接口列表响应为空,已按空列表处理', 'yellow'); | ||
| 226 | - } | ||
| 227 | - | ||
| 228 | - return true; | ||
| 229 | - } catch (err) { | ||
| 230 | - log(`❌ 获取接口列表失败: ${err.message}`, 'red'); | ||
| 231 | - return false; | ||
| 232 | - } | ||
| 233 | -} | ||
| 234 | - | ||
| 235 | -// 主函数 | ||
| 236 | -async function main() { | ||
| 237 | - log('\n🧪 Apifox 连接测试工具', 'bright'); | ||
| 238 | - log('=' .repeat(50), 'bright'); | ||
| 239 | - | ||
| 240 | - // 1. 加载配置 | ||
| 241 | - log('\n📝 步骤 1/4: 加载配置文件', 'cyan'); | ||
| 242 | - const config = loadConfig(); | ||
| 243 | - if (!config) { | ||
| 244 | - process.exit(1); | ||
| 245 | - } | ||
| 246 | - | ||
| 247 | - log('✅ 配置文件加载成功', 'green'); | ||
| 248 | - log(` Token: ${config.VITE_APIFOX_TOKEN.substring(0, 15)}...`, 'blue'); | ||
| 249 | - log(` 项目 ID: ${config.VITE_APIFOX_PROJECT_ID}`, 'blue'); | ||
| 250 | - | ||
| 251 | - // 2. 验证格式 | ||
| 252 | - log('\n🔍 步骤 2/4: 验证配置格式', 'cyan'); | ||
| 253 | - | ||
| 254 | - let valid = true; | ||
| 255 | - | ||
| 256 | - if (!validateToken(config.VITE_APIFOX_TOKEN)) { | ||
| 257 | - valid = false; | ||
| 258 | - } | ||
| 259 | - | ||
| 260 | - if (!validateProjectId(config.VITE_APIFOX_PROJECT_ID)) { | ||
| 261 | - valid = false; | ||
| 262 | - } | ||
| 263 | - | ||
| 264 | - if (!valid) { | ||
| 265 | - log('\n❌ 配置验证失败,请修正后重试', 'red'); | ||
| 266 | - process.exit(1); | ||
| 267 | - } | ||
| 268 | - | ||
| 269 | - log('✅ 配置格式验证通过', 'green'); | ||
| 270 | - | ||
| 271 | - // 3. 测试项目信息 | ||
| 272 | - log('\n🌐 步骤 3/4: 测试 API 连接', 'cyan'); | ||
| 273 | - const projectSuccess = await testProjectInfo(config); | ||
| 274 | - | ||
| 275 | - if (!projectSuccess) { | ||
| 276 | - log('\n❌ API 连接测试失败', 'red'); | ||
| 277 | - log('\n💡 建议:', 'yellow'); | ||
| 278 | - log(' 1. 检查网络连接', 'yellow'); | ||
| 279 | - log(' 2. 验证 Token 和项目 ID 是否正确', 'yellow'); | ||
| 280 | - log(' 3. 确认账号有该项目的访问权限', 'yellow'); | ||
| 281 | - process.exit(1); | ||
| 282 | - } | ||
| 283 | - | ||
| 284 | - // 4. 测试接口列表 | ||
| 285 | - const apiSuccess = await testApiList(config); | ||
| 286 | - | ||
| 287 | - if (!apiSuccess) { | ||
| 288 | - log('\n⚠️ 接口列表获取失败,但项目连接正常', 'yellow'); | ||
| 289 | - process.exit(1); | ||
| 290 | - } | ||
| 291 | - | ||
| 292 | - // 完成 | ||
| 293 | - log('\n' + '='.repeat(50), 'bright'); | ||
| 294 | - log('✅ 所有测试通过!', 'green'); | ||
| 295 | - log('\n🎉 配置正确,可以开始同步 API 了', 'green'); | ||
| 296 | - log('\n运行以下命令同步 API:', 'blue'); | ||
| 297 | - log(' pnpm api:sync', 'bright'); | ||
| 298 | - log(''); | ||
| 299 | -} | ||
| 300 | - | ||
| 301 | -// 运行 | ||
| 302 | -main().catch(err => { | ||
| 303 | - log(`\n❌ 测试失败: ${err.message}`, 'red'); | ||
| 304 | - console.error(err); | ||
| 305 | - process.exit(1); | ||
| 306 | -}); |
scripts/test-apifox-endpoints.js
deleted
100644 → 0
| 1 | -#!/usr/bin/env node | ||
| 2 | - | ||
| 3 | -/** | ||
| 4 | - * 测试不同的 Apifox API 端点 | ||
| 5 | - */ | ||
| 6 | - | ||
| 7 | -const fs = require('fs'); | ||
| 8 | -const path = require('path'); | ||
| 9 | -const https = require('https'); | ||
| 10 | - | ||
| 11 | -// 加载配置 | ||
| 12 | -function loadConfig() { | ||
| 13 | - const envPath = path.join(__dirname, '../.env.apifox'); | ||
| 14 | - const env = fs.readFileSync(envPath, 'utf-8'); | ||
| 15 | - const config = {}; | ||
| 16 | - | ||
| 17 | - env.split('\n').forEach(line => { | ||
| 18 | - const [key, ...valueParts] = line.split('='); | ||
| 19 | - const value = valueParts.join('='); | ||
| 20 | - if (key && !key.startsWith('#') && value) { | ||
| 21 | - config[key.trim()] = value.trim(); | ||
| 22 | - } | ||
| 23 | - }); | ||
| 24 | - | ||
| 25 | - return config; | ||
| 26 | -} | ||
| 27 | - | ||
| 28 | -// 发送 HTTPS 请求 | ||
| 29 | -function httpsRequest(options) { | ||
| 30 | - return new Promise((resolve, reject) => { | ||
| 31 | - const req = https.request(options, (res) => { | ||
| 32 | - const chunks = []; | ||
| 33 | - | ||
| 34 | - res.on('data', chunk => { | ||
| 35 | - chunks.push(chunk); | ||
| 36 | - }); | ||
| 37 | - | ||
| 38 | - res.on('end', () => { | ||
| 39 | - const raw = Buffer.concat(chunks).toString('utf8').trim(); | ||
| 40 | - | ||
| 41 | - if (!raw) { | ||
| 42 | - resolve({ statusCode: res.statusCode, data: null, headers: res.headers }); | ||
| 43 | - return; | ||
| 44 | - } | ||
| 45 | - | ||
| 46 | - try { | ||
| 47 | - const json = JSON.parse(raw.replace(/^\uFEFF/, '')); | ||
| 48 | - resolve({ statusCode: res.statusCode, data: json, headers: res.headers }); | ||
| 49 | - } catch (err) { | ||
| 50 | - resolve({ statusCode: res.statusCode, raw, parseError: err.message, headers: res.headers }); | ||
| 51 | - } | ||
| 52 | - }); | ||
| 53 | - }); | ||
| 54 | - | ||
| 55 | - req.on('error', reject); | ||
| 56 | - req.end(); | ||
| 57 | - }); | ||
| 58 | -} | ||
| 59 | - | ||
| 60 | -// 测试端点 | ||
| 61 | -async function testEndpoint(config, endpointPath, description) { | ||
| 62 | - console.log(`\n测试: ${description}`); | ||
| 63 | - console.log(`端点: ${endpointPath}`); | ||
| 64 | - | ||
| 65 | - const options = { | ||
| 66 | - hostname: 'api.apifox.com', | ||
| 67 | - path: endpointPath, | ||
| 68 | - method: 'GET', | ||
| 69 | - headers: { | ||
| 70 | - 'Authorization': `Bearer ${config.VITE_APIFOX_TOKEN}`, | ||
| 71 | - 'Content-Type': 'application/json' | ||
| 72 | - } | ||
| 73 | - }; | ||
| 74 | - | ||
| 75 | - try { | ||
| 76 | - const response = await httpsRequest(options); | ||
| 77 | - | ||
| 78 | - console.log(`状态码: ${response.statusCode}`); | ||
| 79 | - console.log(`Content-Type: ${response.headers['content-type']}`); | ||
| 80 | - console.log(`Content-Length: ${response.headers['content-length']}`); | ||
| 81 | - | ||
| 82 | - if (response.parseError) { | ||
| 83 | - console.log(`❌ JSON 解析失败: ${response.parseError}`); | ||
| 84 | - console.log(`原始响应 (前 200 字符): ${response.raw?.substring(0, 200)}`); | ||
| 85 | - } else if (response.data) { | ||
| 86 | - console.log(`✅ 成功`); | ||
| 87 | - | ||
| 88 | - // 显示响应结构 | ||
| 89 | - if (Array.isArray(response.data)) { | ||
| 90 | - console.log(` - data 是数组,长度: ${response.data.length}`); | ||
| 91 | - } else if (typeof response.data === 'object') { | ||
| 92 | - console.log(` - data 是对象,键: ${Object.keys(response.data).join(', ')}`); | ||
| 93 | - } | ||
| 94 | - | ||
| 95 | - // 如果有接口数据,显示第一个 | ||
| 96 | - if (Array.isArray(response.data) && response.data.length > 0) { | ||
| 97 | - console.log(` - 第一个接口:`, JSON.stringify(response.data[0]).substring(0, 150)); | ||
| 98 | - } | ||
| 99 | - } else { | ||
| 100 | - console.log(`⚠️ 响应为空`); | ||
| 101 | - } | ||
| 102 | - | ||
| 103 | - return response.statusCode === 200 && response.data && (Array.isArray(response.data) ? response.data.length > 0 : true); | ||
| 104 | - } catch (err) { | ||
| 105 | - console.log(`❌ 请求失败: ${err.message}`); | ||
| 106 | - return false; | ||
| 107 | - } | ||
| 108 | -} | ||
| 109 | - | ||
| 110 | -// 主函数 | ||
| 111 | -async function main() { | ||
| 112 | - const config = loadConfig(); | ||
| 113 | - const projectId = config.VITE_APIFOX_PROJECT_ID; | ||
| 114 | - | ||
| 115 | - console.log('='.repeat(60)); | ||
| 116 | - console.log('测试不同的 Apifox API 端点'); | ||
| 117 | - console.log('='.repeat(60)); | ||
| 118 | - | ||
| 119 | - // 尝试不同的端点 | ||
| 120 | - const endpoints = [ | ||
| 121 | - { path: `/api/v1/projects/${projectId}/apis?pageSize=10`, desc: '当前使用的端点 (/apis)' }, | ||
| 122 | - { path: `/api/v1/projects/${projectId}/interfaces?pageSize=10`, desc: '尝试 /interfaces' }, | ||
| 123 | - { path: `/v1/projects/${projectId}/apis?pageSize=10`, desc: '不带 /api 前缀' }, | ||
| 124 | - { path: `/api/v1/projects/${projectId}/api-lists?pageSize=10`, desc: '尝试 /api-lists' }, | ||
| 125 | - { path: `/api/v1/projects/${projectId}/endpoints?pageSize=10`, desc: '尝试 /endpoints' }, | ||
| 126 | - ]; | ||
| 127 | - | ||
| 128 | - let successCount = 0; | ||
| 129 | - | ||
| 130 | - for (const endpoint of endpoints) { | ||
| 131 | - const success = await testEndpoint(config, endpoint.path, endpoint.desc); | ||
| 132 | - if (success) successCount++; | ||
| 133 | - } | ||
| 134 | - | ||
| 135 | - console.log('\n' + '='.repeat(60)); | ||
| 136 | - console.log(`测试完成!成功: ${successCount}/${endpoints.length}`); | ||
| 137 | - console.log('='.repeat(60)); | ||
| 138 | -} | ||
| 139 | - | ||
| 140 | -main(); |
scripts/test-apifox-export.js
deleted
100644 → 0
| 1 | -#!/usr/bin/env node | ||
| 2 | - | ||
| 3 | -/** | ||
| 4 | - * 测试 Apifox 导出 API | ||
| 5 | - */ | ||
| 6 | - | ||
| 7 | -const fs = require('fs'); | ||
| 8 | -const path = require('path'); | ||
| 9 | -const https = require('https'); | ||
| 10 | - | ||
| 11 | -// 加载配置 | ||
| 12 | -function loadConfig() { | ||
| 13 | - const envPath = path.join(__dirname, '../.env.apifox'); | ||
| 14 | - const env = fs.readFileSync(envPath, 'utf-8'); | ||
| 15 | - const config = {}; | ||
| 16 | - | ||
| 17 | - env.split('\n').forEach(line => { | ||
| 18 | - const [key, ...valueParts] = line.split('='); | ||
| 19 | - const value = valueParts.join('='); | ||
| 20 | - if (key && !key.startsWith('#') && value) { | ||
| 21 | - config[key.trim()] = value.trim(); | ||
| 22 | - } | ||
| 23 | - }); | ||
| 24 | - | ||
| 25 | - return config; | ||
| 26 | -} | ||
| 27 | - | ||
| 28 | -// 发送 HTTPS 请求 | ||
| 29 | -function httpsRequest(options) { | ||
| 30 | - return new Promise((resolve, reject) => { | ||
| 31 | - const req = https.request(options, (res) => { | ||
| 32 | - const chunks = []; | ||
| 33 | - | ||
| 34 | - res.on('data', chunk => { | ||
| 35 | - chunks.push(chunk); | ||
| 36 | - }); | ||
| 37 | - | ||
| 38 | - res.on('end', () => { | ||
| 39 | - const raw = Buffer.concat(chunks).toString('utf8').trim(); | ||
| 40 | - | ||
| 41 | - if (!raw) { | ||
| 42 | - resolve({ statusCode: res.statusCode, data: null, headers: res.headers }); | ||
| 43 | - return; | ||
| 44 | - } | ||
| 45 | - | ||
| 46 | - try { | ||
| 47 | - const json = JSON.parse(raw.replace(/^\uFEFF/, '')); | ||
| 48 | - resolve({ statusCode: res.statusCode, data: json, headers: res.headers }); | ||
| 49 | - } catch (err) { | ||
| 50 | - resolve({ statusCode: res.statusCode, raw, headers: res.headers }); | ||
| 51 | - } | ||
| 52 | - }); | ||
| 53 | - }); | ||
| 54 | - | ||
| 55 | - req.on('error', reject); | ||
| 56 | - req.end(); | ||
| 57 | - }); | ||
| 58 | -} | ||
| 59 | - | ||
| 60 | -// 主函数 | ||
| 61 | -async function main() { | ||
| 62 | - const config = loadConfig(); | ||
| 63 | - const projectId = config.VITE_APIFOX_PROJECT_ID; | ||
| 64 | - | ||
| 65 | - console.log('='.repeat(60)); | ||
| 66 | - console.log('测试 Apifox 导出 API'); | ||
| 67 | - console.log('='.repeat(60)); | ||
| 68 | - | ||
| 69 | - // 尝试导出 OpenAPI 格式 | ||
| 70 | - const exportEndpoints = [ | ||
| 71 | - { path: `/api/v1/projects/${projectId}/export-openapi`, desc: '导出 OpenAPI' }, | ||
| 72 | - { path: `/api/v1/projects/${projectId}/export-openapi/3.0.0`, desc: '导出 OpenAPI 3.0' }, | ||
| 73 | - { path: `/api/v1/projects/${projectId}/export-openapi/3.0.0/json`, desc: '导出 OpenAPI 3.0 JSON' }, | ||
| 74 | - ]; | ||
| 75 | - | ||
| 76 | - for (const endpoint of exportEndpoints) { | ||
| 77 | - console.log(`\n测试: ${endpoint.desc}`); | ||
| 78 | - console.log(`端点: ${endpoint.path}`); | ||
| 79 | - | ||
| 80 | - const options = { | ||
| 81 | - hostname: 'api.apifox.com', | ||
| 82 | - path: endpoint.path, | ||
| 83 | - method: 'GET', | ||
| 84 | - headers: { | ||
| 85 | - 'Authorization': `Bearer ${config.VITE_APIFOX_TOKEN}`, | ||
| 86 | - 'Content-Type': 'application/json', | ||
| 87 | - 'X-Apifox-Version': '2024-06-14' // 尝试添加版本头 | ||
| 88 | - } | ||
| 89 | - }; | ||
| 90 | - | ||
| 91 | - try { | ||
| 92 | - const response = await httpsRequest(options); | ||
| 93 | - | ||
| 94 | - console.log(`状态码: ${response.statusCode}`); | ||
| 95 | - console.log(`Content-Type: ${response.headers['content-type']}`); | ||
| 96 | - console.log(`Content-Length: ${response.headers['content-length']}`); | ||
| 97 | - | ||
| 98 | - if (response.statusCode === 200 && response.raw) { | ||
| 99 | - console.log(`✅ 成功获取数据 (${response.raw.length} 字符)`); | ||
| 100 | - | ||
| 101 | - // 尝试解析为 JSON | ||
| 102 | - try { | ||
| 103 | - const json = JSON.parse(response.raw); | ||
| 104 | - console.log(` - JSON 解析成功`); | ||
| 105 | - console.log(` - 顶层键: ${Object.keys(json).join(', ')}`); | ||
| 106 | - | ||
| 107 | - // 检查是否有接口数据 | ||
| 108 | - if (json.paths) { | ||
| 109 | - const pathCount = Object.keys(json.paths).length; | ||
| 110 | - console.log(` - OpenAPI paths 数量: ${pathCount}`); | ||
| 111 | - | ||
| 112 | - if (pathCount > 0) { | ||
| 113 | - console.log(` ✓ 找到接口数据!`); | ||
| 114 | - // 保存到文件 | ||
| 115 | - const outputPath = path.join(__dirname, `../test-openapi-${Date.now()}.json`); | ||
| 116 | - fs.writeFileSync(outputPath, JSON.stringify(json, null, 2), 'utf-8'); | ||
| 117 | - console.log(` - 已保存到: ${outputPath}`); | ||
| 118 | - return; // 找到数据就退出 | ||
| 119 | - } | ||
| 120 | - } | ||
| 121 | - } catch (err) { | ||
| 122 | - console.log(` - JSON 解析失败,可能是其他格式`); | ||
| 123 | - } | ||
| 124 | - } else if (response.data) { | ||
| 125 | - console.log(`✅ 响应包含 data 字段`); | ||
| 126 | - console.log(` - data 键: ${Object.keys(response.data).join(', ')}`); | ||
| 127 | - } else { | ||
| 128 | - console.log(`⚠️ 响应为空`); | ||
| 129 | - } | ||
| 130 | - | ||
| 131 | - } catch (err) { | ||
| 132 | - console.log(`❌ 请求失败: ${err.message}`); | ||
| 133 | - } | ||
| 134 | - } | ||
| 135 | - | ||
| 136 | - console.log('\n' + '='.repeat(60)); | ||
| 137 | -} | ||
| 138 | - | ||
| 139 | -main(); |
src/api/file.js
0 → 100644
| 1 | +import { fn, fetch } from '@/api/fn'; | ||
| 2 | + | ||
| 3 | +const Api = { | ||
| 4 | + WeekHot: '/srv/?a=file&t=week_hot', | ||
| 5 | +} | ||
| 6 | + | ||
| 7 | +/** | ||
| 8 | + * @description 本周热门资料 | ||
| 9 | + * @remark | ||
| 10 | + * @param {Object} params 请求参数 | ||
| 11 | + * @param {string} params.page 页码,从0开始 | ||
| 12 | + * @param {string} params.limit 每页数量 | ||
| 13 | + * @returns {Promise<{ | ||
| 14 | + * code: number; // 状态码 | ||
| 15 | + * msg: string; // 消息 | ||
| 16 | + * data: { | ||
| 17 | + * list: Array<{ | ||
| 18 | + * meta_id: integer; // 文件ID | ||
| 19 | + * name: string; // 文件名称 | ||
| 20 | + * src: string; // 文件URL | ||
| 21 | + * size: string; // 文件大小 | ||
| 22 | + * read_people_count: integer; // 学习人数 | ||
| 23 | + * read_people_percent: number; // 学习人数比例 | ||
| 24 | + * }>; | ||
| 25 | + * }; | ||
| 26 | + * }>} | ||
| 27 | + */ | ||
| 28 | +export const weekHotAPI = (params) => fn(fetch.get(Api.WeekHot, params)); |
src/api/get_file_list.js
0 → 100644
| 1 | +import { fn, fetch } from '@/api/fn'; | ||
| 2 | + | ||
| 3 | +const Api = { | ||
| 4 | + FileList: '/srv/?a=get_file_list&t=file_list', | ||
| 5 | +} | ||
| 6 | + | ||
| 7 | +/** | ||
| 8 | + * @description 文档列表 | ||
| 9 | + * @remark | ||
| 10 | + * @param {Object} params 请求参数 | ||
| 11 | + * @param {string} params.client_id (可选) 主体id | ||
| 12 | + * @param {string} params.limit (可选) | ||
| 13 | + * @param {string} params.page (可选) | ||
| 14 | + * @param {string} params.cid (可选) 分类id | ||
| 15 | + * @returns {Promise<{ | ||
| 16 | + * code: number; // 状态码 | ||
| 17 | + * msg: string; // 消息 | ||
| 18 | + * data: { | ||
| 19 | + * categories: Array<{ | ||
| 20 | + * id: integer; // 分类id | ||
| 21 | + * name: string; // 分类名 | ||
| 22 | + * }>; | ||
| 23 | + * list: Array<{ | ||
| 24 | + * id: integer; // 产品id | ||
| 25 | + * product_name: string; // 产品名 | ||
| 26 | + * recommend: string; // 推荐位: normal-普通, hot-热卖 | ||
| 27 | + * form_sn: string; // | ||
| 28 | + * created_time: string; // 创建时间 | ||
| 29 | + * categories: array; // 产品所属分类 | ||
| 30 | + * tags: array; // 产品标签 | ||
| 31 | + * }>; | ||
| 32 | + * total: integer; // 产品总数 | ||
| 33 | + * }; | ||
| 34 | + * }>} | ||
| 35 | + */ | ||
| 36 | +export const fileListAPI = (params) => fn(fetch.get(Api.FileList, params)); |
-
Please register or login to post a comment