gorig-cli 1.0.7 → 1.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,65 +1,65 @@
1
1
  # Gorig CLI
2
2
 
3
- Gorig CLI 是一个基于 Node.js 的脚手架工具,用于快速创建基于 Gorig 框架的 Go 项目结构和模块。
3
+ Gorig CLI is a scaffolding tool based on Node.js, used for quickly creating the project structure and modules based on the Gorig framework for Go.
4
4
 
5
- ## 安装
5
+ ## Installation
6
6
 
7
- 使用 npm 全局安装:
7
+ Install globally using npm:
8
8
 
9
9
  ```sh
10
10
  npm install -g gorig-cli
11
11
  ```
12
12
 
13
- 或者使用 npx 直接运行:
13
+ Or run directly using npx:
14
14
 
15
15
  ```sh
16
16
  npx gorig-cli@latest <command>
17
17
  ```
18
18
 
19
- ## 快速开始
19
+ ## Quick Start
20
20
 
21
- ### 初始化新项目
21
+ ### Initialize a New Project
22
22
 
23
- 使用 `init` 命令创建一个新项目:
23
+ Use the `init` command to create a new project:
24
24
 
25
25
  ```sh
26
26
  gorig-cli init my-new-project
27
27
  ```
28
28
 
29
- 或者使用 npx
29
+ Or use npx:
30
30
 
31
31
  ```sh
32
32
  npx gorig-cli@latest init my-new-project
33
33
  ```
34
34
 
35
- 这将在当前目录下创建一个新项目,包含 `_cmd/main.go`、`domain/init.go`、`cron/cron.go` 等基本文件和目录。
35
+ This will create a new project in the current directory, including basic files and directories like `_cmd/main.go`, `domain/init.go`, `cron/cron.go`, etc.
36
36
 
37
- ### 创建新模块
37
+ ### Create a New Module
38
38
 
39
- 在项目根目录下使用 `create` 命令创建一个新模块:
39
+ Use the `create` command in the project root directory to create a new module:
40
40
 
41
41
  ```sh
42
42
  gorig-cli create user
43
43
  ```
44
44
 
45
- 或者使用 npx
45
+ Or use npx:
46
46
 
47
47
  ```sh
48
48
  npx gorig-cli@latest create user
49
49
  ```
50
50
 
51
- 这将在项目中创建一个名为 `user` 的模块,包含 `api/`、`internal/`、`model/` 等文件夹和必要的代码。
51
+ This will create a module named `user` in the project, including folders like `api/`, `internal/`, `model/`, and necessary code.
52
52
 
53
- ### 运行项目
53
+ ### Run the Project
54
54
 
55
- 进入项目目录后,可以使用以下命令运行项目:
55
+ After entering the project directory, you can run the project using the following commands:
56
56
 
57
57
  ```sh
58
58
  cd my-new-project
59
59
  go run _cmd/main.go
60
60
  ```
61
61
 
62
- 或者编译后运行:
62
+ Or run it after building:
63
63
 
64
64
  ```sh
65
65
  go build -o my-new-project _cmd/main.go && ./my-new-project
package/bin/cli.js CHANGED
@@ -39,6 +39,14 @@ switch (command) {
39
39
  console.error(chalk.red('无法加载 init 命令模块:', error.message));
40
40
  });
41
41
  break;
42
+ case 'doc':
43
+ import(path.join(__dirname, '../commands/doc.js')).then(module => {
44
+ const docModule = module.default;
45
+ docModule(); // 执行 doc 命令
46
+ }).catch(error => {
47
+ console.error(chalk.red('无法加载 doc 命令模块:', error.message));
48
+ });
49
+ break;
42
50
 
43
51
  default:
44
52
  console.error(chalk.red(`未知的命令: ${command}`));
@@ -4,11 +4,11 @@ import ejs from 'ejs';
4
4
  import chalk from 'chalk';
5
5
  import { fileURLToPath } from 'url';
6
6
 
7
- // 定义 __dirname 变量
7
+ // Define __dirname variable
8
8
  const __filename = fileURLToPath(import.meta.url);
9
9
  const __dirname = path.dirname(__filename);
10
10
 
11
- // 获取 go.mod 文件中的模块名称
11
+ // Get the module name from the go.mod file
12
12
  const getGoModModuleName = async () => {
13
13
  try {
14
14
  const goModPath = path.join(process.cwd(), 'go.mod');
@@ -17,35 +17,35 @@ const getGoModModuleName = async () => {
17
17
  if (moduleLine) {
18
18
  return moduleLine.split(' ')[1].trim();
19
19
  } else {
20
- throw new Error('未找到模块名称');
20
+ throw new Error('Module name not found');
21
21
  }
22
22
  } catch (error) {
23
- console.error(chalk.red('无法读取 go.mod 文件。请确认您是否在 Go 项目的根目录下运行该命令。'));
23
+ console.error(chalk.red('Unable to read go.mod file. Please confirm that you are running this command in the root directory of the Go project.'));
24
24
  process.exit(1);
25
25
  }
26
26
  };
27
27
 
28
- // 创建模块的主函数
28
+ // Create module main function
29
29
  const createModule = async (args) => {
30
30
  if (args.length < 1) {
31
- console.error(chalk.yellow('请使用正确的命令格式: npx <你的包名> create <模块名>'));
31
+ console.error(chalk.yellow('Please use the correct command format: npx <your package name> create <module name>'));
32
32
  process.exit(1);
33
33
  }
34
34
 
35
35
  const moduleName = args[0];
36
36
  const ModuleName = moduleName.charAt(0).toUpperCase() + moduleName.slice(1);
37
37
 
38
- // 获取当前工作目录,即执行命令的目录
38
+ // Get the current working directory, which is the directory where the command is executed
39
39
  const currentDir = process.cwd();
40
40
 
41
- // 构建 domain 目录和模块目录的路径
41
+ // Build the paths for the domain directory and module directory
42
42
  const domainDir = path.join(currentDir, 'domain');
43
43
  const moduleDir = path.join(domainDir, moduleName);
44
44
 
45
- // 定义需要创建的子目录
45
+ // Define the subdirectories to be created
46
46
  const subDirs = ['api', 'model', 'internal', 'api/req'];
47
47
 
48
- console.log(chalk.blue(`\n开始创建模块: ${chalk.bold(moduleName)}`));
48
+ console.log(chalk.blue(`\nStarting to create module: ${chalk.bold(moduleName)}`));
49
49
 
50
50
  try {
51
51
  const projectName = await getGoModModuleName();
@@ -53,106 +53,107 @@ const createModule = async (args) => {
53
53
  await fs.ensureDir(domainDir);
54
54
  await fs.ensureDir(moduleDir);
55
55
 
56
- // 创建子目录
56
+ // Create subdirectories
57
57
  const createSubDirs = subDirs.map((subDir) => {
58
58
  const subDirPath = path.join(moduleDir, subDir);
59
59
  return fs.ensureDir(subDirPath);
60
60
  });
61
61
  await Promise.all(createSubDirs);
62
62
 
63
- // 创建并写入 model.go 文件
63
+ // Create and write model.go file
64
64
  const modelDir = path.join(moduleDir, 'model');
65
65
  const modelFilePath = path.join(modelDir, `${moduleName}.go`);
66
66
  const modelTemplatePath = path.join(__dirname, '../templates/model.go.ejs');
67
67
  const modelContent = await ejs.renderFile(modelTemplatePath, { moduleName, ModuleName, projectName });
68
68
  await fs.writeFile(modelFilePath, modelContent);
69
- console.log(chalk.green(`成功创建 ${chalk.bold('model.go')} 文件`));
69
+ console.log(chalk.green(`Successfully created ${chalk.bold('model.go')} file`));
70
70
 
71
- // 创建并写入 req.go 文件
71
+ // Create and write req.go file
72
72
  const apiDir = path.join(moduleDir, 'api');
73
73
  const reqDir = path.join(apiDir, 'req');
74
74
  const reqFilePath = path.join(reqDir, `req.go`);
75
75
  const reqTemplatePath = path.join(__dirname, '../templates/req.go.ejs');
76
76
  const reqContent = await ejs.renderFile(reqTemplatePath, { moduleName, ModuleName, projectName });
77
77
  await fs.writeFile(reqFilePath, reqContent);
78
- console.log(chalk.green(`成功创建 ${chalk.bold('req.go')} 文件`));
78
+ console.log(chalk.green(`Successfully created ${chalk.bold('req.go')} file`));
79
79
 
80
- // 创建并写入 resp.go 文件
80
+ // Create and write resp.go file
81
81
  const respFilePath = path.join(reqDir, `resp.go`);
82
82
  const respTemplatePath = path.join(__dirname, '../templates/resp.go.ejs');
83
83
  const respContent = await ejs.renderFile(respTemplatePath, { moduleName, ModuleName, projectName });
84
84
  await fs.writeFile(respFilePath, respContent);
85
- console.log(chalk.green(`成功创建 ${chalk.bold('resp.go')} 文件`));
85
+ console.log(chalk.green(`Successfully created ${chalk.bold('resp.go')} file`));
86
86
 
87
- // 创建并写入 internal.go 文件
87
+ // Create and write internal.go file
88
88
  const internalDir = path.join(moduleDir, 'internal');
89
89
  const internalFilePath = path.join(internalDir, `${moduleName}.go`);
90
90
  const internalTemplatePath = path.join(__dirname, '../templates/internal.go.ejs');
91
91
  const internalContent = await ejs.renderFile(internalTemplatePath, { moduleName, ModuleName, projectName });
92
92
  await fs.writeFile(internalFilePath, internalContent);
93
- console.log(chalk.green(`成功创建 ${chalk.bold('internal.go')} 文件`));
93
+ console.log(chalk.green(`Successfully created ${chalk.bold('internal.go')} file`));
94
94
 
95
- // 创建并写入 service.pub.go 文件
95
+ // Create and write service.pub.go file
96
96
  const serviceFilePath = path.join(apiDir, `service.pub.go`);
97
97
  const serviceTemplatePath = path.join(__dirname, '../templates/service.pub.go.ejs');
98
98
  const serviceContent = await ejs.renderFile(serviceTemplatePath, { moduleName, ModuleName, projectName });
99
99
  await fs.writeFile(serviceFilePath, serviceContent);
100
- console.log(chalk.green(`成功创建 ${chalk.bold('service.pub.go')} 文件`));
100
+ console.log(chalk.green(`Successfully created ${chalk.bold('service.pub.go')} file`));
101
101
 
102
- // 创建并写入 service.go 文件
102
+ // Create and write service.go file
103
103
  const serviceGoFilePath = path.join(apiDir, `service.go`);
104
104
  const serviceGoTemplatePath = path.join(__dirname, '../templates/service.go.ejs');
105
105
  const serviceGoContent = await ejs.renderFile(serviceGoTemplatePath, { moduleName, ModuleName, projectName });
106
106
  await fs.writeFile(serviceGoFilePath, serviceGoContent);
107
- console.log(chalk.green(`成功创建 ${chalk.bold('service.go')} 文件`));
107
+ console.log(chalk.green(`Successfully created ${chalk.bold('service.go')} file`));
108
108
 
109
- // 创建并写入 controller.go 文件
109
+ // Create and write controller.go file
110
110
  const controllerGoFilePath = path.join(apiDir, `controller.go`);
111
111
  const controllerGoTemplatePath = path.join(__dirname, '../templates/controller.go.ejs');
112
112
  const controllerGoContent = await ejs.renderFile(controllerGoTemplatePath, { moduleName, ModuleName, projectName });
113
113
  await fs.writeFile(controllerGoFilePath, controllerGoContent);
114
- console.log(chalk.green(`成功创建 ${chalk.bold('controller.go')} 文件`));
114
+ console.log(chalk.green(`Successfully created ${chalk.bold('controller.go')} file`));
115
115
 
116
- // 创建并写入 router.go 文件
116
+ // Create and write router.go file
117
117
  const routerGoFilePath = path.join(apiDir, `router.go`);
118
118
  const routerGoTemplatePath = path.join(__dirname, '../templates/router.go.ejs');
119
119
  const routerGoContent = await ejs.renderFile(routerGoTemplatePath, { moduleName, ModuleName, projectName });
120
120
  await fs.writeFile(routerGoFilePath, routerGoContent);
121
- console.log(chalk.green(`成功创建 ${chalk.bold('router.go')} 文件`));
121
+ console.log(chalk.green(`Successfully created ${chalk.bold('router.go')} file`));
122
122
 
123
- // 检查并更新 domain/init.go 文件
123
+ // Check and update domain/init.go file
124
124
  const initFilePath = path.join(domainDir, 'init.go');
125
125
  if (await fs.pathExists(initFilePath)) {
126
126
  let initFileContent = await fs.readFile(initFilePath, 'utf-8');
127
-
127
+
128
128
  const importStatement = `import _ "${projectName}/domain/${moduleName}/api"`;
129
129
 
130
130
  if (!initFileContent.includes(importStatement)) {
131
131
  const initFuncIndex = initFileContent.indexOf('func init()');
132
-
132
+
133
133
  if (initFuncIndex !== -1) {
134
- // 找到 init 函数,确保在 init 函数上方添加 import 语句
134
+ // Find init function, ensure import statement is added above init function
135
135
  const insertPosition = initFuncIndex;
136
- initFileContent =
137
- initFileContent.slice(0, insertPosition) +
138
- `${importStatement}\n\n` +
136
+ const headCode = initFileContent.slice(0, insertPosition).trimEnd();
137
+ initFileContent =
138
+ `${headCode}\n` +
139
+ `${importStatement}\n\n` +
139
140
  initFileContent.slice(insertPosition);
140
141
 
141
142
  await fs.writeFile(initFilePath, initFileContent);
142
- console.log(chalk.green(`成功更新 ${chalk.bold('init.go')} 文件,添加 import 语句`));
143
+ console.log(chalk.green(`Successfully updated ${chalk.bold('init.go')} file, added import statement`));
143
144
  } else {
144
- console.log(chalk.yellow(`未找到 init() 函数,跳过 import 语句添加`));
145
+ console.log(chalk.yellow(`init() function not found, skipping import statement addition`));
145
146
  }
146
147
  } else {
147
- console.log(chalk.yellow(`init.go 文件中已存在相应的 import 语句,无需重复添加`));
148
+ console.log(chalk.yellow(`The corresponding import statement already exists in init.go, no need to add it again`));
148
149
  }
149
150
  } else {
150
- console.log(chalk.yellow(`未找到 ${chalk.bold('init.go')} 文件,跳过 import 语句添加`));
151
+ console.log(chalk.yellow(`Could not find ${chalk.bold('init.go')} file, skipping import statement addition`));
151
152
  }
152
153
 
153
- console.log(chalk.blue(`\n模块 ${chalk.bold(moduleName)} 已成功创建在 ${chalk.bold(`domain/${moduleName}`)} 目录下`));
154
+ console.log(chalk.blue(`\nModule ${chalk.bold(moduleName)} has been successfully created in ${chalk.bold(`domain/${moduleName}`)} directory`));
154
155
  } catch (error) {
155
- console.error(chalk.red('创建模块时出错:'), chalk.redBright(error.message));
156
+ console.error(chalk.red('Error creating module:'), chalk.redBright(error.message));
156
157
  }
157
158
  };
158
159
 
@@ -0,0 +1,682 @@
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+ import chalk from 'chalk';
4
+ import ejs from 'ejs';
5
+ import { spawn } from 'child_process'; // 从 exec 改为 spawn
6
+ import { fileURLToPath } from 'url';
7
+ import readline from 'readline';
8
+ import semver from 'semver'; // 用于处理版本号
9
+ import { Command } from 'commander'; // 正确导入 Command
10
+ import yaml from 'js-yaml'; // 导入 js-yaml 用于解析 YAML
11
+
12
+ // 获取当前文件的目录
13
+ const __filename = fileURLToPath(import.meta.url);
14
+ const __dirname = path.dirname(__filename);
15
+
16
+ /**
17
+ * 格式化日期为 "YYYY-MM-DD HH:MM:SS"
18
+ * @param {Date} date - 要格式化的日期对象
19
+ * @returns {string} - 格式化后的日期字符串
20
+ */
21
+ const formatDate = (date) => {
22
+ const pad = (n) => n < 10 ? '0' + n : n;
23
+ return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ` +
24
+ `${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;
25
+ };
26
+
27
+ /**
28
+ * 提取路径参数,例如 "/user/get/:id" -> ["id"]
29
+ */
30
+ const extractPathParams = (pathStr) => {
31
+ const regex = /:([\w]+)/g;
32
+ const params = [];
33
+ let match;
34
+ while ((match = regex.exec(pathStr)) !== null) {
35
+ params.push(match[1]);
36
+ }
37
+ return params;
38
+ };
39
+
40
+ /**
41
+ * 解析 router.go 获取路由信息
42
+ */
43
+ const scanRouterFile = async (routerFilePath) => {
44
+ try {
45
+ const routerContent = await fs.readFile(routerFilePath, 'utf-8');
46
+ const routeRegex = /(\w+)\.(GET|POST|PUT|DELETE)\("([^"]+)",\s*(\w+)\)/g;
47
+ const routes = [];
48
+ let match;
49
+ while ((match = routeRegex.exec(routerContent)) !== null) {
50
+ const method = match[2];
51
+ const path = match[3];
52
+ const handler = match[4];
53
+ const pathParams = extractPathParams(path);
54
+ routes.push({
55
+ method,
56
+ path,
57
+ handler,
58
+ pathParams
59
+ });
60
+ }
61
+ return routes;
62
+ } catch (error) {
63
+ console.error(chalk.red(`Failed to read router file: ${routerFilePath}`), error);
64
+ return [];
65
+ }
66
+ };
67
+
68
+ /**
69
+ * 解析结构体文件,提取带有 `form` 或 `json` 标签的字段
70
+ * 支持递归解析嵌套的结构体
71
+ * @param {string} structFilePath - 结构体文件路径
72
+ * @param {string} structName - 结构体名称
73
+ * @param {string} modelDir - 模型文件目录
74
+ * @returns {Array} - 字段数组,包含 name, type
75
+ */
76
+ const parseStructFields = async (structFilePath, structName, modelDir) => {
77
+ try {
78
+ const structContent = await fs.readFile(structFilePath, 'utf-8');
79
+ // 修正正则表达式,确保能够匹配结构体内容
80
+ const structRegex = new RegExp(`type\\s+${structName}\\s+struct\\s*{([\\s\\S]*?)}\\s*`, 'm');
81
+ const match = structRegex.exec(structContent);
82
+ if (!match) {
83
+ console.warn(chalk.yellow(`Struct ${structName} definition not found`));
84
+ return [];
85
+ }
86
+ const fieldsContent = match[1];
87
+ const fields = [];
88
+ const fieldRegex = /\s*(\w+)\s+([\w\.\*\[\]]+)\s+`[^`]*?(?:json|form):"([^"]+)"[^`]*`/g;
89
+ let fieldMatch;
90
+ while ((fieldMatch = fieldRegex.exec(fieldsContent)) !== null) {
91
+ const fieldName = fieldMatch[1];
92
+ const fieldType = fieldMatch[2];
93
+ const tagName = fieldMatch[3];
94
+ fields.push({
95
+ name: tagName,
96
+ type: fieldType
97
+ });
98
+
99
+ // 检查是否为嵌入的结构体,例如 model.UserD 或 req.UserReq
100
+ if (fieldType.startsWith('model.') || fieldType.startsWith('req.')) {
101
+ const embeddedStructName = fieldType.split('.')[1].replace('*', '');
102
+ let embeddedStructFile = '';
103
+
104
+ if (fieldType.startsWith('model.')) {
105
+ embeddedStructFile = path.join(modelDir, `${embeddedStructName.toLowerCase()}.go`);
106
+ } else if (fieldType.startsWith('req.')) {
107
+ embeddedStructFile = structFilePath; // 如果是 req 包,使用同一个文件
108
+ }
109
+
110
+ // 解析嵌入的结构体
111
+ if (await fs.pathExists(embeddedStructFile)) {
112
+ const embeddedFields = await parseStructFields(embeddedStructFile, embeddedStructName, modelDir);
113
+ fields.push(...embeddedFields);
114
+ } else {
115
+ console.warn(chalk.yellow(`Embedded struct file not found: ${embeddedStructFile}`));
116
+ }
117
+ }
118
+ }
119
+ return fields;
120
+ } catch (error) {
121
+ console.error(chalk.red(`Failed to parse struct file: ${structFilePath}`), error);
122
+ return [];
123
+ }
124
+ };
125
+
126
+ /**
127
+ * 解析 controller.go 获取方法中的参数
128
+ */
129
+ const extractParamsFromController = async (controllerContent, reqDir, modelDir) => {
130
+ const functionParams = {};
131
+ // 正则表达式匹配处理函数
132
+ const functionRegex = /func\s+(\w+)\s*\(ctx\s+\*gin\.Context\)\s*{([\s\S]*?)^}/gm;
133
+ let funcMatch;
134
+ while ((funcMatch = functionRegex.exec(controllerContent)) !== null) {
135
+ const functionName = funcMatch[1];
136
+ const functionBody = funcMatch[2];
137
+
138
+ const params = [];
139
+ let usesGetPageReq = false;
140
+
141
+ // 建立变量名到类型的映射
142
+ const varTypeMap = {};
143
+ const varRegex = /(?:var\s+)?(\w+)\s*(?:[:=])=\s*(?:&)?([\w\.]+)\s*(?:\{\})?/g;
144
+ let varMatch;
145
+ while ((varMatch = varRegex.exec(functionBody)) !== null) {
146
+ const varName = varMatch[1];
147
+ const varType = varMatch[2];
148
+ varTypeMap[varName] = varType;
149
+ }
150
+
151
+ // 检查是否有 apix.GetPageReq 调用
152
+ if (/apix\.GetPageReq\(/.test(functionBody)) {
153
+ usesGetPageReq = true;
154
+ }
155
+
156
+ // 正则表达式匹配 apix.GetParamX 调用
157
+ const paramRegex = /(\w+),\s*e\s*:=\s*apix\.GetParam(\w+)\s*\(\s*ctx\s*,\s*"([^"]+)"(?:\s*,\s*(apix\.\w+|true|false))?\s*\)/g;
158
+ let paramMatch;
159
+ while ((paramMatch = paramRegex.exec(functionBody)) !== null) {
160
+ const paramName = paramMatch[1]; // 变量名,例如: id
161
+ const paramTypeFunc = paramMatch[2]; // 类型函数,例如: Int64, Str
162
+ const paramKey = paramMatch[3]; // 参数键,例如: "id"
163
+ const paramForce = paramMatch[4]; // 可选的强制参数,例如: apix.Force, true
164
+
165
+ // 确定是否必填
166
+ let required = false;
167
+ if (paramForce) {
168
+ if (paramForce === 'apix.Force' || paramForce === 'true') {
169
+ required = true;
170
+ }
171
+ }
172
+
173
+ // 映射 Go 类型到 OpenAPI 类型
174
+ let schemaType = 'string';
175
+ switch (paramTypeFunc.toLowerCase()) {
176
+ case 'int':
177
+ case 'int32':
178
+ case 'int64':
179
+ schemaType = 'integer';
180
+ break;
181
+ case 'bool':
182
+ schemaType = 'boolean';
183
+ break;
184
+ case 'float32':
185
+ case 'float64':
186
+ schemaType = 'number';
187
+ break;
188
+ default:
189
+ schemaType = 'string';
190
+ }
191
+
192
+ const paramObject = {
193
+ name: paramKey,
194
+ in: 'query', // 默认设置为 query
195
+ required: required,
196
+ schema: { type: schemaType }
197
+ };
198
+
199
+ params.push(paramObject);
200
+ }
201
+
202
+ // 处理通过 apix.BindParams 绑定的参数
203
+ const bindParamsRegex = /e\s*:=\s*apix\.BindParams\s*\(\s*ctx\s*,\s*&?(\w+)\s*\)/g;
204
+ let bindMatch;
205
+ while ((bindMatch = bindParamsRegex.exec(functionBody)) !== null) {
206
+ const varName = bindMatch[1];
207
+ const fullType = varTypeMap[varName] || varName; // 获取变量的类型,若未声明则使用变量名
208
+
209
+ let bindStructName = fullType;
210
+
211
+ // 如果类型包含包名,例如 req.UserReq
212
+ if (fullType.includes('.')) {
213
+ bindStructName = fullType.split('.')[1];
214
+ }
215
+
216
+ params.push({
217
+ bindStructName: bindStructName
218
+ });
219
+ }
220
+
221
+ // 将是否使用分页的标记添加到参数中
222
+ if (usesGetPageReq) {
223
+ params.push({ usesGetPageReq: true });
224
+ }
225
+
226
+ functionParams[functionName] = params;
227
+ }
228
+ return functionParams;
229
+ };
230
+
231
+ /**
232
+ * 解析模型定义
233
+ */
234
+ const extractModelFields = async (modelFilePath) => {
235
+ try {
236
+ const modelContent = await fs.readFile(modelFilePath, 'utf-8');
237
+ const fields = [];
238
+ const modelRegex = /type\s+(\w+)\s+struct\s*{([\s\S]*?)^}/gm;
239
+ let match;
240
+ while ((match = modelRegex.exec(modelContent)) !== null) {
241
+ const modelName = match[1];
242
+ const modelFields = [];
243
+ const fieldsContent = match[2];
244
+ const fieldRegex = /\s*(\w+)\s+([\w\.\*\[\]]+)\s+`[^`]*?(?:json|form):"([^"]+)"[^`]*`/g;
245
+ let fieldMatch;
246
+ while ((fieldMatch = fieldRegex.exec(fieldsContent)) !== null) {
247
+ const fieldName = fieldMatch[1];
248
+ const fieldType = fieldMatch[2];
249
+ const tagName = fieldMatch[3];
250
+ modelFields.push({
251
+ field: fieldName,
252
+ type: fieldType,
253
+ json: tagName
254
+ });
255
+ }
256
+ fields.push({ modelName, modelFields });
257
+ }
258
+ return fields;
259
+ } catch (error) {
260
+ console.error(chalk.red(`Failed to parse model file: ${modelFilePath}`), error);
261
+ return [];
262
+ }
263
+ };
264
+
265
+ /**
266
+ * 获取版本号并递增
267
+ */
268
+ const getNextVersion = (existingVersion) => {
269
+ if (!existingVersion) return "1.0.0"; // 默认版本号
270
+
271
+ const nextVersion = semver.inc(existingVersion, 'patch'); // 按照补丁版本递增
272
+ return nextVersion || "1.0.0";
273
+ };
274
+
275
+ /**
276
+ * 生成或更新 redoc.html
277
+ * @param {string} docDir - 目标项目的 doc 目录路径
278
+ * @param {string} templatePath - redoc_template.html 的路径
279
+ */
280
+ const generateRedocHtml = async (docDir, templatePath) => {
281
+ try {
282
+ // 读取模板文件
283
+ const redocTemplate = await fs.readFile(templatePath, 'utf-8');
284
+
285
+ // 获取 doc 文件夹下所有 JSON 文件(不包�� redoc.html)
286
+ const jsonFiles = await fs.readdir(docDir);
287
+ const openApiJsonFiles = jsonFiles.filter(file => file.endsWith('.json'));
288
+
289
+ // 生成 API_OPTIONS
290
+ const apiOptions = openApiJsonFiles.map(file => {
291
+ const apiName = path.basename(file, '.json');
292
+ return `<option value="./${file}">${apiName}</option>`;
293
+ }).join('\n');
294
+
295
+ // 替换模板中的 {{API_OPTIONS}} 占位符
296
+ const redocHtmlContent = redocTemplate.replace('{{API_OPTIONS}}', apiOptions);
297
+
298
+ // 写入 redoc.html 到 doc 文件夹
299
+ const redocHtmlPath = path.join(docDir, 'redoc.html');
300
+ await fs.writeFile(redocHtmlPath, redocHtmlContent, 'utf-8');
301
+ console.log(chalk.green(`ReDoc page generated or updated: doc/redoc.html`));
302
+ } catch (error) {
303
+ console.error(chalk.red('Error generating ReDoc page:'), chalk.redBright(error.message));
304
+ }
305
+ };
306
+
307
+ /**
308
+ * 启动 http-server 以预览文档
309
+ * 仅显示 'Available on:' 和 'Hit CTRL-C to stop the server' 部分
310
+ * 将控制台提示改为英文
311
+ * @param {string} docDir - 目标项目的 doc 目录路径
312
+ */
313
+ const startHttpServer = (docDir) => { // 添加 docDir 参数
314
+ const server = spawn('http-server', ['-p', '8080', docDir], { shell: true });
315
+
316
+ // 监听 stdout 数据
317
+ server.stdout.on('data', (data) => {
318
+ const output = data.toString();
319
+ const lines = output.split('\n');
320
+ lines.forEach(line => {
321
+ const trimmedLine = line.trim();
322
+ if (trimmedLine === 'Available on:' || trimmedLine === 'Hit CTRL-C to stop the server') {
323
+ console.log(line);
324
+ return;
325
+ }
326
+ if (trimmedLine.startsWith('http://') || trimmedLine.startsWith('https://')) {
327
+ console.log(` ${trimmedLine}/redoc.html`); // 指向 redoc.html
328
+ }
329
+ });
330
+ });
331
+
332
+ // 监听 stderr 数据并过滤掉特定的 Deprecation Warning
333
+ server.stderr.on('data', (data) => {
334
+ const errorOutput = data.toString();
335
+ if (!errorOutput.includes('DEP0066')) {
336
+ console.error(chalk.red('http-server error:'), chalk.redBright(errorOutput));
337
+ }
338
+ });
339
+
340
+ // 监听错误事件
341
+ server.on('error', (err) => {
342
+ if (err.code === 'EADDRINUSE') {
343
+ console.error(chalk.red(`Port 8080 is already in use. Please use another port.`));
344
+ } else {
345
+ console.error(chalk.red('Error starting http-server:'), chalk.redBright(err.message));
346
+ }
347
+ });
348
+
349
+ // 监听关闭事件
350
+ server.on('close', (code) => {
351
+ if (code !== 0) {
352
+ console.error(chalk.red(`http-server process exited with code ${code}`));
353
+ }
354
+ });
355
+ };
356
+
357
+ /**
358
+ * 生成 OpenAPI 文档
359
+ * @param {string|null} moduleName - 指定的模块名
360
+ */
361
+ const generateOpenAPIDocs = async (moduleName = null) => {
362
+ try {
363
+ const cliDir = path.resolve(__dirname, '..'); // 指向 CLI 项目的根目录
364
+ const templatesDir = path.join(cliDir, 'templates');
365
+ const redocTemplatePath = path.join(templatesDir, 'redoc_template.html');
366
+
367
+ // 确保模板文件存在
368
+ if (!(await fs.pathExists(redocTemplatePath))) {
369
+ console.error(chalk.red(`Template file missing: ${redocTemplatePath}`));
370
+ return;
371
+ }
372
+
373
+ const domainDir = path.join(process.cwd(), 'domain');
374
+ const docDir = path.join(process.cwd(), 'doc');
375
+
376
+ // 确保 doc 目录存在
377
+ await fs.ensureDir(docDir);
378
+
379
+ // 读取并解析 _bin/local.yaml 获取 API 服务器地址
380
+ let serverUrl = 'http://localhost:8080'; // 默认值
381
+ const binDir = path.join(process.cwd(), '_bin');
382
+ const localYamlPath = path.join(binDir, 'local.yaml');
383
+
384
+ if (await fs.pathExists(localYamlPath)) {
385
+ try {
386
+ const yamlContent = await fs.readFile(localYamlPath, 'utf-8');
387
+ const config = yaml.load(yamlContent);
388
+ const addr = config?.api?.rest?.addr;
389
+ if (addr) {
390
+ // addr 例如:9527
391
+ let host = 'localhost';
392
+ let port = '8080';
393
+ const addrMatch = addr.match(/(.*):(\d+)/);
394
+ if (addrMatch) {
395
+ if (addrMatch[1]) {
396
+ host = addrMatch[1];
397
+ }
398
+ port = addrMatch[2];
399
+ }
400
+ serverUrl = `http://${host}:${port}`;
401
+ }
402
+ } catch (error) {
403
+ console.error(chalk.red(`Failed to parse YAML file: ${localYamlPath}`), error);
404
+ console.warn(chalk.yellow('Using default server URL: http://localhost:8080'));
405
+ }
406
+ } else {
407
+ console.warn(chalk.yellow(`YAML configuration file not found: ${localYamlPath}`));
408
+ console.warn(chalk.yellow('Using default server URL: http://localhost:8080'));
409
+ }
410
+
411
+ // 扫描所有模块
412
+ const moduleDirs = moduleName ? [moduleName] : await fs.readdir(domainDir);
413
+
414
+ // 过滤出目录
415
+ const modulesToProcess = [];
416
+ for (const module of moduleDirs) {
417
+ const modulePath = path.join(domainDir, module);
418
+ if (await fs.pathExists(modulePath) && (await fs.lstat(modulePath)).isDirectory()) {
419
+ modulesToProcess.push(module);
420
+ }
421
+ }
422
+
423
+ for (const module of modulesToProcess) {
424
+ const modulePath = path.join(domainDir, module);
425
+ const routerFilePath = path.join(modulePath, 'api/router.go');
426
+ const controllerFilePath = path.join(modulePath, 'api/controller.go');
427
+ const modelFilePath = path.join(modulePath, 'model', `${module}.go`);
428
+ const reqFilePath = path.join(modulePath, 'api', 'req', 'req.go');
429
+
430
+ // 检查文件是否存在
431
+ if (!(await fs.pathExists(routerFilePath)) || !(await fs.pathExists(controllerFilePath)) || !(await fs.pathExists(modelFilePath)) || !(await fs.pathExists(reqFilePath))) {
432
+ console.warn(chalk.yellow(`Module "${module}" is missing required files, skipping...`));
433
+ continue;
434
+ }
435
+
436
+ // 扫描 router.go 获取路由信息
437
+ const routes = await scanRouterFile(routerFilePath);
438
+ // console.log(`Scanned routes for module "${module}":`, routes);
439
+
440
+ // 扫描 controller.go 获取方法参数
441
+ const controllerContent = await fs.readFile(controllerFilePath, 'utf-8');
442
+ const controllerParams = await extractParamsFromController(controllerContent, path.dirname(reqFilePath), path.dirname(modelFilePath));
443
+ // console.log(`Extracted controller parameters for module "${module}":`, controllerParams);
444
+
445
+ // 扫描 model.go 和 req.go 获取字段
446
+ const modelFields = await extractModelFields(modelFilePath);
447
+ const reqFields = await extractModelFields(reqFilePath);
448
+ const allFields = [...modelFields, ...reqFields];
449
+
450
+ // 生成 OpenAPI 格式文档
451
+ const openAPIDoc = {
452
+ openapi: "3.0.0",
453
+ info: {
454
+ title: `${module} API`,
455
+ version: "1.0.0", // 默认版本号,稍后会递增
456
+ description: `${module} api doc, generated by gorig-cli
457
+ \n last updated: ${formatDate(new Date())}`, // 添加描述信息
458
+ },
459
+ servers: [
460
+ {
461
+ url: serverUrl, // 使用从 YAML 获取的 serverUrl
462
+ description: "API Server"
463
+ }
464
+ ],
465
+ paths: {},
466
+ components: {
467
+ schemas: {},
468
+ },
469
+ };
470
+
471
+ // 检查是否存在现有文档文件,读取并递增版本号
472
+ const existingDocPath = path.join(docDir, `${module}.json`);
473
+ if (await fs.pathExists(existingDocPath)) {
474
+ const existingDoc = await fs.readJson(existingDocPath);
475
+ openAPIDoc.info.version = getNextVersion(existingDoc.info.version);
476
+ // 复用 schemas
477
+ if (existingDoc.components && existingDoc.components.schemas) {
478
+ openAPIDoc.components.schemas = existingDoc.components.schemas;
479
+ }
480
+ }
481
+
482
+ // 添加所有结构体定义到 components.schemas
483
+ allFields.forEach(model => {
484
+ const properties = {};
485
+ model.modelFields.forEach(field => {
486
+ let fieldType = 'string'; // 默认类型
487
+ const lowerType = field.type.toLowerCase();
488
+ if (lowerType.includes('int')) {
489
+ fieldType = 'integer';
490
+ } else if (lowerType.includes('bool')) {
491
+ fieldType = 'boolean';
492
+ } else if (lowerType.includes('float')) {
493
+ fieldType = 'number';
494
+ } else if (lowerType.startsWith('[]')) {
495
+ fieldType = 'array';
496
+ }
497
+
498
+ if (fieldType === 'array') {
499
+ // 假设数组元素类型为 string,实际应根据具体类型调整
500
+ properties[field.json] = {
501
+ type: 'array',
502
+ items: { type: 'string' }
503
+ };
504
+ } else {
505
+ properties[field.json] = { type: fieldType };
506
+ }
507
+ });
508
+ openAPIDoc.components.schemas[model.modelName] = {
509
+ type: 'object',
510
+ properties,
511
+ };
512
+ });
513
+
514
+ // 手动合并嵌套结构体字段到 UserReq
515
+ if (openAPIDoc.components.schemas['UserReq'] && openAPIDoc.components.schemas['UserD']) {
516
+ const userReqProperties = openAPIDoc.components.schemas['UserReq'].properties;
517
+ const userDProperties = openAPIDoc.components.schemas['UserD'].properties;
518
+
519
+ // 合并 UserD 的字段到 UserReq
520
+ openAPIDoc.components.schemas['UserReq'].properties = {
521
+ ...userReqProperties,
522
+ ...userDProperties
523
+ };
524
+
525
+ // 如果不需要独立定义 UserD,可以删除它
526
+ // delete openAPIDoc.components.schemas['UserD'];
527
+ }
528
+
529
+ // 替换 'filter' schema reference with 'UserReq'
530
+ for (const [handler, params] of Object.entries(controllerParams)) {
531
+ for (const param of params) {
532
+ if (param.bindStructName === 'filter') {
533
+ param.bindStructName = 'UserReq';
534
+ }
535
+ }
536
+ }
537
+
538
+ // 生成完整路径并修复嵌套结构问题
539
+ for (const route of routes) {
540
+ const basePath = `/${module}`; // 从 groupRouter.Group("user") 获取
541
+ const fullPath = `${basePath}/${route.path}`.replace(/\/+/g, '/'); // 确保路径格式正确
542
+ const pathParamsList = route.pathParams; // 从路由中获取 path 参数列表
543
+
544
+ // 获取对应处理函数的参数
545
+ const funcParams = controllerParams[route.handler] || [];
546
+
547
+ let parameters = [];
548
+ let requestBody = undefined;
549
+ let usesGetPageReq = false;
550
+
551
+ // 分离参数和 requestBody
552
+ for (const param of funcParams) {
553
+ if (param.usesGetPageReq) {
554
+ usesGetPageReq = true;
555
+ } else if (param.bindStructName) {
556
+ // 处理绑定的结构体,作为 requestBody 或 query 参数
557
+ if (['POST', 'PUT'].includes(route.method.toUpperCase())) {
558
+ // POST 和 PUT 请求使用 requestBody
559
+ requestBody = {
560
+ required: true,
561
+ content: {
562
+ 'application/json': {
563
+ schema: {
564
+ $ref: `#/components/schemas/${param.bindStructName}`,
565
+ },
566
+ },
567
+ },
568
+ };
569
+ } else if (['GET', 'DELETE'].includes(route.method.toUpperCase())) {
570
+ // GET 和 DELETE 请求使用 query 参数
571
+ const schema = openAPIDoc.components.schemas[param.bindStructName];
572
+ if (schema && schema.properties) {
573
+ for (const [fieldName, fieldSchema] of Object.entries(schema.properties)) {
574
+ parameters.push({
575
+ name: fieldName,
576
+ in: 'query',
577
+ required: false, // 根据实际情况调整
578
+ schema: fieldSchema
579
+ });
580
+ }
581
+ } else {
582
+ console.warn(chalk.yellow(`Function "${route.handler}" has undefined bindStructName "${param.bindStructName}"`));
583
+ }
584
+ }
585
+ } else {
586
+ // 确定参数位置
587
+ if (pathParamsList.includes(param.name)) {
588
+ param.in = 'path';
589
+ } else {
590
+ param.in = 'query';
591
+ }
592
+ parameters.push(param);
593
+ }
594
+ }
595
+
596
+ // 如果函数中使用了 apix.GetPageReq,则添加分页参数
597
+ if (usesGetPageReq) {
598
+ parameters = parameters.concat([
599
+ { name: "page", in: "query", required: false, schema: { type: "integer" } },
600
+ { name: "size", in: "query", required: false, schema: { type: "integer" } },
601
+ { name: "lastID", in: "query", required: false, schema: { type: "integer" } },
602
+ ]);
603
+ }
604
+
605
+ // 去重参数
606
+ const uniqueParameters = [];
607
+ const paramNames = new Set();
608
+ parameters.forEach(param => {
609
+ if (!paramNames.has(param.name)) {
610
+ paramNames.add(param.name);
611
+ uniqueParameters.push(param);
612
+ }
613
+ });
614
+
615
+ // 定义响应
616
+ const responses = {
617
+ '200': {
618
+ description: 'Success',
619
+ content: {
620
+ 'application/json': {
621
+ schema: {
622
+ type: 'object',
623
+ properties: {} // 可以根据需要进一步完善响应体
624
+ },
625
+ },
626
+ },
627
+ },
628
+ };
629
+
630
+ // 如果有 requestBody,添加到 operation
631
+ const operation = {
632
+ summary: route.handler,
633
+ parameters: uniqueParameters.length > 0 ? uniqueParameters : undefined,
634
+ responses,
635
+ };
636
+
637
+ if (requestBody) {
638
+ operation.requestBody = requestBody;
639
+ }
640
+
641
+ // 初始化路径对象
642
+ if (!openAPIDoc.paths[fullPath]) {
643
+ openAPIDoc.paths[fullPath] = {};
644
+ }
645
+
646
+ openAPIDoc.paths[fullPath][route.method.toLowerCase()] = operation;
647
+ }
648
+
649
+ // 写入 OpenAPI JSON 文件
650
+ await fs.writeJson(existingDocPath, openAPIDoc, { spaces: 2 });
651
+ console.log(chalk.green(`OpenAPI documentation generated: doc/${path.basename(existingDocPath)}`));
652
+ }
653
+
654
+ // 生成或更新 redoc.html 一次
655
+ await generateRedocHtml(docDir, redocTemplatePath);
656
+
657
+ // 启动 http-server 以预览文档
658
+ startHttpServer(docDir);
659
+ } catch (error) {
660
+ console.error(chalk.red('Error generating API documentation:'), chalk.redBright(error.message));
661
+ }
662
+ };
663
+
664
+ /**
665
+ * 处理命令行输入
666
+ */
667
+ const docCommand = async () => {
668
+ const program = new Command();
669
+
670
+ program
671
+ .command('doc')
672
+ .description('Generate API documentation')
673
+ .argument('[moduleName]', 'Module name. If specified, only generate documentation for that module.')
674
+ .action(async (moduleName) => {
675
+ await generateOpenAPIDocs(moduleName);
676
+ });
677
+
678
+ program.parse(process.argv);
679
+ };
680
+
681
+ // 默认导出一个函数
682
+ export default docCommand;
package/commands/init.js CHANGED
@@ -15,7 +15,7 @@ const checkGoEnvironment = () => {
15
15
  return new Promise((resolve, reject) => {
16
16
  exec('go version', (error, stdout, stderr) => {
17
17
  if (error) {
18
- reject(new Error('未检测到 Go 环境,请先安装 Go 语言环境,访问 https://golang.org/dl/ 进行安装。'));
18
+ reject(new Error('Go environment not detected, please install Go language environment first, visit https://golang.org/dl/ for installation.'));
19
19
  } else {
20
20
  resolve(stdout);
21
21
  }
@@ -28,7 +28,7 @@ const addGorigDependency = (projectDir) => {
28
28
  return new Promise((resolve, reject) => {
29
29
  exec('go get github.com/jom-io/gorig@latest', { cwd: projectDir }, (error, stdout, stderr) => {
30
30
  if (error) {
31
- reject(new Error(`无法添加 gorig 依赖: ${stderr}`));
31
+ reject(new Error(`Failed to add gorig dependency: ${stderr}`));
32
32
  } else {
33
33
  resolve(stdout);
34
34
  }
@@ -41,7 +41,7 @@ const runGoModTidy = (projectDir) => {
41
41
  return new Promise((resolve, reject) => {
42
42
  exec('go mod tidy', { cwd: projectDir }, (error, stdout, stderr) => {
43
43
  if (error) {
44
- reject(new Error(`运行 go mod tidy 失败: ${stderr}`));
44
+ reject(new Error(`Failed to run go mod tidy: ${stderr}`));
45
45
  } else {
46
46
  resolve(stdout);
47
47
  }
@@ -57,7 +57,7 @@ const askOverwriteConfirmation = (projectDir) => {
57
57
  output: process.stdout
58
58
  });
59
59
  rl.question(
60
- chalk.yellow(`目录 ${chalk.bold(projectDir)} 已经存在,是否覆盖?(y/N): `),
60
+ chalk.yellow(`Directory ${chalk.bold(projectDir)} already exists, do you want to overwrite? (y/N): `),
61
61
  (answer) => {
62
62
  rl.close();
63
63
  if (answer.toLowerCase() === 'y') {
@@ -73,7 +73,7 @@ const askOverwriteConfirmation = (projectDir) => {
73
73
  // 初始化项目的主函数
74
74
  const initModule = async (args) => {
75
75
  if (args.length < 1) {
76
- console.error(chalk.yellow('请使用正确的命令格式: npx <你的包名> init <项目名>'));
76
+ console.error(chalk.yellow('Please use the correct command format: npx <your package name> init <project name>'));
77
77
  process.exit(1);
78
78
  }
79
79
 
@@ -85,22 +85,22 @@ const initModule = async (args) => {
85
85
  // 定义需要创建的子目录
86
86
  const subDirs = ['_bin', '_cmd', 'domain', 'global', 'cron'];
87
87
 
88
- console.log(chalk.blue(`\n开始初始化项目: ${chalk.bold(projectName)}`));
88
+ console.log(chalk.blue(`\nStarting project initialization: ${chalk.bold(projectName)}`));
89
89
 
90
90
  try {
91
91
  // 检查本地是否存在 Go 环境
92
92
  await checkGoEnvironment();
93
- console.log(chalk.green('检测到 Go 环境,继续进行项目初始化...'));
93
+ console.log(chalk.green('Go environment detected, continuing project initialization...'));
94
94
 
95
95
  // 检查项目目录是否已存在
96
96
  if (await fs.pathExists(projectDir)) {
97
97
  const shouldOverwrite = await askOverwriteConfirmation(projectDir);
98
98
  if (!shouldOverwrite) {
99
- console.log(chalk.red('项目初始化已取消。'));
99
+ console.log(chalk.red('Project initialization has been canceled.'));
100
100
  process.exit(0);
101
101
  } else {
102
102
  await fs.remove(projectDir); // 删除已有的目录
103
- console.log(chalk.yellow(`已删除现有目录: ${chalk.bold(projectDir)}`));
103
+ console.log(chalk.yellow(`Deleted existing directory: ${chalk.bold(projectDir)}`));
104
104
  }
105
105
  }
106
106
 
@@ -118,12 +118,12 @@ const initModule = async (args) => {
118
118
  const goModPath = path.join(projectDir, 'go.mod');
119
119
  const goModContent = `module ${projectName}\n\ngo 1.20\n`;
120
120
  await fs.writeFile(goModPath, goModContent);
121
- console.log(chalk.green(`成功创建 ${chalk.bold('go.mod')} 文件`));
121
+ console.log(chalk.green(`Successfully created ${chalk.bold('go.mod')} file`));
122
122
 
123
123
  // 添加 gorig 依赖
124
- console.log(chalk.blue('添加 gorig 依赖中,请稍候...'));
124
+ console.log(chalk.blue('Adding gorig dependency, please wait...'));
125
125
  await addGorigDependency(projectDir);
126
- console.log(chalk.green(`成功添加 github.com/jom-io/gorig 最新版本的依赖`));
126
+ console.log(chalk.green(`Successfully added the latest version of github.com/jom-io/gorig dependency`));
127
127
 
128
128
  // 创建 _bin 目录下的配置文件
129
129
  const binDir = path.join(projectDir, '_bin');
@@ -138,7 +138,7 @@ const initModule = async (args) => {
138
138
  mode: env,
139
139
  });
140
140
  await fs.writeFile(configFilePath, configContent);
141
- console.log(chalk.green(`成功创建 ${chalk.bold(`${env}.yaml`)} 文件`));
141
+ console.log(chalk.green(`Successfully created ${chalk.bold(`${env}.yaml`)} file`));
142
142
  }
143
143
 
144
144
  // 创建 domain/init.go 文件
@@ -147,7 +147,7 @@ const initModule = async (args) => {
147
147
  const initGoTemplatePath = path.join(__dirname, '../templates/init.go.ejs');
148
148
  const initGoContent = await ejs.renderFile(initGoTemplatePath, { projectNameUpper });
149
149
  await fs.writeFile(initGoPath, initGoContent);
150
- console.log(chalk.green(`成功创建 ${chalk.bold('init.go')} 文件`));
150
+ console.log(chalk.green(`Successfully created ${chalk.bold('init.go')} file`));
151
151
 
152
152
  // 创建 _cmd/main.go 文件
153
153
  const cmdDir = path.join(projectDir, '_cmd');
@@ -155,7 +155,7 @@ const initModule = async (args) => {
155
155
  const mainGoTemplatePath = path.join(__dirname, '../templates/main.go.ejs');
156
156
  const mainGoContent = await ejs.renderFile(mainGoTemplatePath, { projectName });
157
157
  await fs.writeFile(mainGoPath, mainGoContent);
158
- console.log(chalk.green(`成功创建 ${chalk.bold('main.go')} 文件`));
158
+ console.log(chalk.green(`Successfully created ${chalk.bold('main.go')} file`));
159
159
 
160
160
  // 创建 global/config.go 文件
161
161
  const globalDir = path.join(projectDir, 'global');
@@ -163,7 +163,7 @@ const initModule = async (args) => {
163
163
  const configGoTemplatePath = path.join(__dirname, '../templates/config.go.ejs');
164
164
  const configGoContent = await ejs.renderFile(configGoTemplatePath, { projectNameUpper });
165
165
  await fs.writeFile(configGoPath, configGoContent);
166
- console.log(chalk.green(`成功创建 ${chalk.bold('config.go')} 文件`));
166
+ console.log(chalk.green(`Successfully created ${chalk.bold('config.go')} file`));
167
167
 
168
168
  // 创建 cron/cron.go 文件
169
169
  const cronDir = path.join(projectDir, 'cron');
@@ -171,23 +171,23 @@ const initModule = async (args) => {
171
171
  const cronGoTemplatePath = path.join(__dirname, '../templates/cron.go.ejs');
172
172
  const cronGoContent = await ejs.renderFile(cronGoTemplatePath, {});
173
173
  await fs.writeFile(cronGoPath, cronGoContent);
174
- console.log(chalk.green(`成功创建 ${chalk.bold('cron.go')} 文件`));
174
+ console.log(chalk.green(`Successfully created ${chalk.bold('cron.go')} file`));
175
175
 
176
176
  // 运行 go mod tidy
177
- console.log(chalk.blue('整理 Go 模块依赖中 (go mod tidy),请稍候...'));
177
+ console.log(chalk.blue('Organizing Go module dependencies (go mod tidy), please wait...'));
178
178
  await runGoModTidy(projectDir);
179
- console.log(chalk.green('成功整理 Go 模块依赖'));
179
+ console.log(chalk.green('Successfully organized Go module dependencies'));
180
180
 
181
181
  // 提示用户如何运行项目
182
- console.log(chalk.blue(`\n项目 ${chalk.bold(projectName)} 已成功创建在 ${chalk.bold(projectDir)} 目录下`));
183
- console.log(chalk.yellow('\n运行项目的方法:'));
184
- console.log(chalk.green(`1. 进入项目目录: cd ${projectName}`));
185
- console.log(chalk.green(`2. 使用 Go 命令运行项目:`));
182
+ console.log(chalk.blue(`\nProject ${chalk.bold(projectName)} has been successfully created in ${chalk.bold(projectDir)} directory`));
183
+ console.log(chalk.yellow('\nHow to run the project:'));
184
+ console.log(chalk.green(`1. Enter the project directory: cd ${projectName}`));
185
+ console.log(chalk.green(`2. Use Go command to run the project:`));
186
186
  console.log(chalk.cyan(` go run _cmd/main.go`));
187
- console.log(chalk.green(`\n或者直接编译并运行:`));
187
+ console.log(chalk.green(`\nOr compile and run directly:`));
188
188
  console.log(chalk.cyan(` go build -o ${projectName} _cmd/main.go && ./${projectName}`));
189
189
  } catch (error) {
190
- console.error(chalk.red('初始化项目时出错:'), chalk.redBright(error.message));
190
+ console.error(chalk.red('Error during project initialization:'), chalk.redBright(error.message));
191
191
  }
192
192
  };
193
193
 
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "gorig-cli",
3
- "version": "1.0.7",
3
+ "version": "1.0.8",
4
4
  "type": "module",
5
- "description": "gorig构建工具",
5
+ "description": "gorig build tool",
6
6
  "main": "bin/cli.js",
7
7
  "bin": {
8
8
  "gorig-cli": "./bin/cli.js"
@@ -21,6 +21,9 @@
21
21
  "chalk": "^5.3.0",
22
22
  "commander": "^12.1.0",
23
23
  "ejs": "^3.1.10",
24
- "fs-extra": "^11.2.0"
24
+ "fs-extra": "^11.2.0",
25
+ "http-server": "^14.1.1",
26
+ "js-yaml": "^4.1.0",
27
+ "semver": "^7.6.3"
25
28
  }
26
29
  }
@@ -32,12 +32,12 @@ func List<%= ModuleName %>(ctx *gin.Context) {
32
32
 
33
33
  func Save<%= ModuleName %>(ctx *gin.Context) {
34
34
  defer apix.HandlePanic(ctx)
35
- <%= ModuleName %>Req := &req.<%= ModuleName %>Req{}
36
- e := apix.BindParams(ctx, <%= ModuleName %>Req)
35
+ <%= moduleName %>Req := &req.<%= ModuleName %>Req{}
36
+ e := apix.BindParams(ctx, <%= moduleName %>Req)
37
37
  if e != nil {
38
38
  return
39
39
  }
40
- err := gService.Save<%= ModuleName %>(ctx, <%= ModuleName %>Req)
40
+ err := gService.Save<%= ModuleName %>(ctx, <%= moduleName %>Req)
41
41
  apix.HandleData(ctx, consts.CurdUpdateFailCode, nil, err)
42
42
  }
43
43
 
@@ -1,7 +1,7 @@
1
1
  package model
2
2
 
3
3
  type <%= moduleName.charAt(0).toUpperCase() + moduleName.slice(1) %>D struct {
4
- UserID int64 `gorm:"column:user_id;type:bigint(20);comment:user_id" json:"userID"`
5
- FiledStr string `gorm:"column:fieldStr;type:varchar(255);comment:fieldStr" json:"fieldStr"`
6
- FieldInt int64 `gorm:"column:fieldInt;type:bigint(20);comment:fieldInt" json:"fieldInt"`
4
+ UserID int64 `gorm:"column:user_id;type:bigint(20);comment:user_id" json:"userID" form:"userID"`
5
+ FiledStr string `gorm:"column:fieldStr;type:varchar(255);comment:fieldStr" json:"fieldStr" form:"fieldStr"`
6
+ FieldInt int64 `gorm:"column:fieldInt;type:bigint(20);comment:fieldInt" json:"fieldInt" form:"fieldInt"`
7
7
  }
@@ -0,0 +1,102 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>API Documentation</title>
5
+ <!-- Include ReDoc CDN -->
6
+ <script src="https://cdn.redoc.ly/redoc/latest/bundles/redoc.standalone.js"></script>
7
+ <style>
8
+ body {
9
+ margin: 0;
10
+ padding: 0;
11
+ font-family: Arial, sans-serif;
12
+ background-color: #f9f9f9;
13
+ color: #333;
14
+ }
15
+ #header {
16
+ padding: 15px;
17
+ background-color: #007bff;
18
+ position: fixed;
19
+ top: 0;
20
+ width: 100%;
21
+ z-index: 1000;
22
+ box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
23
+ }
24
+ #header label {
25
+ color: white;
26
+ font-weight: bold;
27
+ }
28
+ #spec-selector {
29
+ padding: 8px;
30
+ font-size: 16px;
31
+ border: none;
32
+ border-radius: 4px;
33
+ margin-left: 10px;
34
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
35
+ }
36
+ #view-source, #go-home {
37
+ margin-left: 10px;
38
+ cursor: pointer;
39
+ color: white;
40
+ text-decoration: underline;
41
+ font-weight: bold;
42
+ }
43
+ #redoc-container {
44
+ height: calc(100vh - 70px);
45
+ margin-top: 70px;
46
+ padding: 20px;
47
+ border-radius: 8px;
48
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
49
+ }
50
+ </style>
51
+ </head>
52
+ <body>
53
+ <div id="header">
54
+ <label for="spec-selector">Select API Documentation: </label>
55
+ <select id="spec-selector">
56
+ <option value="">-- Select API --</option>
57
+ {{API_OPTIONS}}
58
+ </select>
59
+ <span id="view-source">View Source Data</span>
60
+ <span id="go-home" class="view-source">All List</span>
61
+ </div>
62
+ <div id="redoc-container">
63
+ <!-- ReDoc will render here -->
64
+ </div>
65
+ <script>
66
+ const selector = document.getElementById('spec-selector');
67
+ const container = document.getElementById('redoc-container');
68
+ const viewSource = document.getElementById('view-source');
69
+ const goHome = document.getElementById('go-home');
70
+
71
+ // Default select the first document
72
+ selector.selectedIndex = 1;
73
+ const defaultSpecUrl = selector.value;
74
+ if (defaultSpecUrl) {
75
+ // Render the default API documentation using ReDoc
76
+ Redoc.init(defaultSpecUrl, { scrollYOffset: 50 }, container);
77
+ }
78
+
79
+ selector.addEventListener('change', function() {
80
+ const specUrl = this.value;
81
+ if (specUrl) {
82
+ // Clear existing ReDoc rendering
83
+ container.innerHTML = '';
84
+ // Render the new API documentation using ReDoc
85
+ Redoc.init(specUrl, { scrollYOffset: 50 }, container);
86
+ }
87
+ });
88
+
89
+ viewSource.addEventListener('click', function() {
90
+ const specUrl = selector.value;
91
+ if (specUrl) {
92
+ // Redirect to the corresponding API source data
93
+ window.location.href = specUrl.replace('.json', '.json'); // Assuming source data is in JSON format
94
+ }
95
+ });
96
+
97
+ goHome.addEventListener('click', function() {
98
+ window.location.href = '/';
99
+ });
100
+ </script>
101
+ </body>
102
+ </html>
@@ -7,12 +7,12 @@ import (
7
7
 
8
8
  func init() {
9
9
  httpx.RegisterRouter(func(groupRouter *gin.RouterGroup) {
10
- <%= ModuleName %> := groupRouter.Group("<%= moduleName %>")
11
- //<%= ModuleName %>.Use(mid.SignMP())
12
- <%= ModuleName %>.GET("page", Page<%= ModuleName %>)
13
- <%= ModuleName %>.GET("list", List<%= ModuleName %>)
14
- <%= ModuleName %>.POST("save", Save<%= ModuleName %>)
15
- <%= ModuleName %>.GET("info", Get<%= ModuleName %>)
16
- <%= ModuleName %>.DELETE("delete", Delete<%= ModuleName %>)
10
+ <%= moduleName %> := groupRouter.Group("<%= moduleName %>")
11
+ //<%= moduleName %>.Use(mid.SignMP())
12
+ <%= moduleName %>.GET("page", Page<%= ModuleName %>)
13
+ <%= moduleName %>.GET("list", List<%= ModuleName %>)
14
+ <%= moduleName %>.POST("save", Save<%= ModuleName %>)
15
+ <%= moduleName %>.GET("info", Get<%= ModuleName %>)
16
+ <%= moduleName %>.DELETE("delete", Delete<%= ModuleName %>)
17
17
  })
18
18
  }