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 +16 -16
- package/bin/cli.js +8 -0
- package/commands/create.js +41 -40
- package/commands/doc.js +682 -0
- package/commands/init.js +25 -25
- package/package.json +6 -3
- package/templates/controller.go.ejs +3 -3
- package/templates/model.go.ejs +3 -3
- package/templates/redoc_template.html +102 -0
- package/templates/router.go.ejs +7 -7
package/README.md
CHANGED
|
@@ -1,65 +1,65 @@
|
|
|
1
1
|
# Gorig CLI
|
|
2
2
|
|
|
3
|
-
Gorig CLI
|
|
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
|
-
|
|
7
|
+
Install globally using npm:
|
|
8
8
|
|
|
9
9
|
```sh
|
|
10
10
|
npm install -g gorig-cli
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
|
|
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
|
-
|
|
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
|
-
|
|
29
|
+
Or use npx:
|
|
30
30
|
|
|
31
31
|
```sh
|
|
32
32
|
npx gorig-cli@latest init my-new-project
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
-
|
|
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
|
-
|
|
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
|
-
|
|
45
|
+
Or use npx:
|
|
46
46
|
|
|
47
47
|
```sh
|
|
48
48
|
npx gorig-cli@latest create user
|
|
49
49
|
```
|
|
50
50
|
|
|
51
|
-
|
|
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}`));
|
package/commands/create.js
CHANGED
|
@@ -4,11 +4,11 @@ import ejs from 'ejs';
|
|
|
4
4
|
import chalk from 'chalk';
|
|
5
5
|
import { fileURLToPath } from 'url';
|
|
6
6
|
|
|
7
|
-
//
|
|
7
|
+
// Define __dirname variable
|
|
8
8
|
const __filename = fileURLToPath(import.meta.url);
|
|
9
9
|
const __dirname = path.dirname(__filename);
|
|
10
10
|
|
|
11
|
-
//
|
|
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('
|
|
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('
|
|
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
|
-
//
|
|
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(`\
|
|
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
|
-
//
|
|
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(
|
|
69
|
+
console.log(chalk.green(`Successfully created ${chalk.bold('model.go')} file`));
|
|
70
70
|
|
|
71
|
-
//
|
|
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(
|
|
78
|
+
console.log(chalk.green(`Successfully created ${chalk.bold('req.go')} file`));
|
|
79
79
|
|
|
80
|
-
//
|
|
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(
|
|
85
|
+
console.log(chalk.green(`Successfully created ${chalk.bold('resp.go')} file`));
|
|
86
86
|
|
|
87
|
-
//
|
|
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(
|
|
93
|
+
console.log(chalk.green(`Successfully created ${chalk.bold('internal.go')} file`));
|
|
94
94
|
|
|
95
|
-
//
|
|
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(
|
|
100
|
+
console.log(chalk.green(`Successfully created ${chalk.bold('service.pub.go')} file`));
|
|
101
101
|
|
|
102
|
-
//
|
|
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(
|
|
107
|
+
console.log(chalk.green(`Successfully created ${chalk.bold('service.go')} file`));
|
|
108
108
|
|
|
109
|
-
//
|
|
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(
|
|
114
|
+
console.log(chalk.green(`Successfully created ${chalk.bold('controller.go')} file`));
|
|
115
115
|
|
|
116
|
-
//
|
|
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(
|
|
121
|
+
console.log(chalk.green(`Successfully created ${chalk.bold('router.go')} file`));
|
|
122
122
|
|
|
123
|
-
//
|
|
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
|
-
//
|
|
134
|
+
// Find init function, ensure import statement is added above init function
|
|
135
135
|
const insertPosition = initFuncIndex;
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
`${
|
|
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(
|
|
143
|
+
console.log(chalk.green(`Successfully updated ${chalk.bold('init.go')} file, added import statement`));
|
|
143
144
|
} else {
|
|
144
|
-
console.log(chalk.yellow(
|
|
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
|
|
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(
|
|
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(`\
|
|
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('
|
|
156
|
+
console.error(chalk.red('Error creating module:'), chalk.redBright(error.message));
|
|
156
157
|
}
|
|
157
158
|
};
|
|
158
159
|
|
package/commands/doc.js
ADDED
|
@@ -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('
|
|
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(
|
|
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(
|
|
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(
|
|
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('
|
|
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(`\
|
|
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('
|
|
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(
|
|
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(
|
|
121
|
+
console.log(chalk.green(`Successfully created ${chalk.bold('go.mod')} file`));
|
|
122
122
|
|
|
123
123
|
// 添加 gorig 依赖
|
|
124
|
-
console.log(chalk.blue('
|
|
124
|
+
console.log(chalk.blue('Adding gorig dependency, please wait...'));
|
|
125
125
|
await addGorigDependency(projectDir);
|
|
126
|
-
console.log(chalk.green(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
174
|
+
console.log(chalk.green(`Successfully created ${chalk.bold('cron.go')} file`));
|
|
175
175
|
|
|
176
176
|
// 运行 go mod tidy
|
|
177
|
-
console.log(chalk.blue('
|
|
177
|
+
console.log(chalk.blue('Organizing Go module dependencies (go mod tidy), please wait...'));
|
|
178
178
|
await runGoModTidy(projectDir);
|
|
179
|
-
console.log(chalk.green('
|
|
179
|
+
console.log(chalk.green('Successfully organized Go module dependencies'));
|
|
180
180
|
|
|
181
181
|
// 提示用户如何运行项目
|
|
182
|
-
console.log(chalk.blue(`\
|
|
183
|
-
console.log(chalk.yellow('\
|
|
184
|
-
console.log(chalk.green(`1.
|
|
185
|
-
console.log(chalk.green(`2.
|
|
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(`\
|
|
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('
|
|
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.
|
|
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
|
-
<%=
|
|
36
|
-
e := apix.BindParams(ctx, <%=
|
|
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, <%=
|
|
40
|
+
err := gService.Save<%= ModuleName %>(ctx, <%= moduleName %>Req)
|
|
41
41
|
apix.HandleData(ctx, consts.CurdUpdateFailCode, nil, err)
|
|
42
42
|
}
|
|
43
43
|
|
package/templates/model.go.ejs
CHANGED
|
@@ -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>
|
package/templates/router.go.ejs
CHANGED
|
@@ -7,12 +7,12 @@ import (
|
|
|
7
7
|
|
|
8
8
|
func init() {
|
|
9
9
|
httpx.RegisterRouter(func(groupRouter *gin.RouterGroup) {
|
|
10
|
-
<%=
|
|
11
|
-
//<%=
|
|
12
|
-
<%=
|
|
13
|
-
<%=
|
|
14
|
-
<%=
|
|
15
|
-
<%=
|
|
16
|
-
<%=
|
|
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
|
}
|