nsgm-cli 2.1.13 → 2.1.15
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 +394 -156
- package/client/components/Button.tsx +3 -5
- package/client/components/ClientProviders.tsx +29 -0
- package/client/components/LanguageSwitcher.tsx +59 -0
- package/client/components/SSRSafeAntdProvider.tsx +24 -0
- package/client/components/SuppressHydrationWarnings.tsx +55 -0
- package/client/components/__tests__/Button.test.tsx +10 -10
- package/client/layout/index.tsx +153 -185
- package/client/redux/reducers.ts +1 -1
- package/client/redux/store.ts +2 -1
- package/client/redux/template/manage/actions.ts +77 -88
- package/client/redux/template/manage/reducers.ts +25 -37
- package/client/redux/template/manage/types.ts +1 -1
- package/client/service/template/manage.ts +20 -21
- package/client/styled/common.ts +12 -14
- package/client/styled/layout/index.ts +234 -120
- package/client/styled/template/manage.ts +102 -14
- package/client/utils/common.ts +23 -21
- package/client/utils/cookie.ts +18 -19
- package/client/utils/fetch.ts +64 -100
- package/client/utils/i18n.ts +68 -0
- package/client/utils/menu.tsx +42 -23
- package/client/utils/navigation.ts +58 -0
- package/client/utils/sso.ts +74 -84
- package/client/utils/suppressWarnings.ts +32 -0
- package/eslint.config.js +53 -19
- package/generation/README.md +25 -6
- package/generation/__tests__/example.test.js +41 -0
- package/generation/client/redux/reducers.ts +1 -1
- package/generation/client/utils/menu.tsx +36 -23
- package/generation/env +3 -0
- package/generation/env.example +3 -0
- package/generation/eslint.config.js +112 -0
- package/generation/gitignore +6 -1
- package/generation/jest.config.js +40 -0
- package/generation/next.config.js +7 -3
- package/generation/package.json +28 -4
- package/generation/tsconfig.json +6 -19
- package/jest.config.js +23 -6
- package/lib/args.js +9 -1
- package/lib/cli/app.d.ts +28 -0
- package/lib/cli/app.js +99 -0
- package/lib/cli/commands/build.d.ts +2 -0
- package/lib/cli/commands/build.js +29 -0
- package/lib/cli/commands/create.d.ts +2 -0
- package/lib/cli/commands/create.js +113 -0
- package/lib/cli/commands/delete.d.ts +3 -0
- package/lib/cli/commands/delete.js +151 -0
- package/lib/cli/commands/export.d.ts +2 -0
- package/lib/cli/commands/export.js +42 -0
- package/lib/cli/commands/help.d.ts +2 -0
- package/lib/cli/commands/help.js +42 -0
- package/lib/cli/commands/init.d.ts +2 -0
- package/lib/cli/commands/init.js +115 -0
- package/lib/cli/commands/server.d.ts +3 -0
- package/lib/cli/commands/server.js +26 -0
- package/lib/cli/commands/upgrade.d.ts +2 -0
- package/lib/cli/commands/upgrade.js +38 -0
- package/lib/cli/commands/version.d.ts +2 -0
- package/lib/cli/commands/version.js +24 -0
- package/lib/cli/index.d.ts +16 -0
- package/lib/cli/index.js +33 -0
- package/lib/cli/parser.d.ts +22 -0
- package/lib/cli/parser.js +115 -0
- package/lib/cli/registry.d.ts +33 -0
- package/lib/cli/registry.js +81 -0
- package/lib/cli/types/project.d.ts +10 -0
- package/lib/cli/types/project.js +2 -0
- package/lib/cli/types.d.ts +31 -0
- package/lib/cli/types.js +20 -0
- package/lib/cli/utils/console.d.ts +62 -0
- package/lib/cli/utils/console.js +148 -0
- package/lib/cli/utils/index.d.ts +2 -0
- package/lib/cli/utils/index.js +7 -0
- package/lib/cli/utils/prompt.d.ts +83 -0
- package/lib/cli/utils/prompt.js +327 -0
- package/lib/constants.d.ts +65 -0
- package/lib/constants.js +177 -0
- package/lib/generate.d.ts +25 -3
- package/lib/generate.js +98 -621
- package/lib/generate_create.d.ts +9 -0
- package/lib/generate_create.js +329 -0
- package/lib/generate_delete.d.ts +8 -0
- package/lib/generate_delete.js +233 -0
- package/lib/generate_init.d.ts +56 -0
- package/lib/generate_init.js +612 -0
- package/lib/generators/base-generator.d.ts +47 -0
- package/lib/generators/base-generator.js +92 -0
- package/lib/generators/file-generator.d.ts +48 -0
- package/lib/generators/file-generator.js +455 -0
- package/lib/generators/generator-factory.d.ts +20 -0
- package/lib/generators/generator-factory.js +25 -0
- package/lib/generators/i18n-generator.d.ts +51 -0
- package/lib/generators/i18n-generator.js +320 -0
- package/lib/generators/page-generator.d.ts +45 -0
- package/lib/generators/page-generator.js +578 -0
- package/lib/generators/resolver-generator.d.ts +14 -0
- package/lib/generators/resolver-generator.js +342 -0
- package/lib/generators/schema-generator.d.ts +7 -0
- package/lib/generators/schema-generator.js +57 -0
- package/lib/generators/service-generator.d.ts +11 -0
- package/lib/generators/service-generator.js +233 -0
- package/lib/generators/sql-generator.d.ts +8 -0
- package/lib/generators/sql-generator.js +52 -0
- package/lib/index.d.ts +1 -1
- package/lib/index.js +14 -173
- package/lib/server/csrf.js +9 -16
- package/lib/server/db.js +6 -7
- package/lib/server/graphql.js +5 -6
- package/lib/server/plugins/date.js +1 -1
- package/lib/server/utils/graphql-cache.js +3 -3
- package/lib/tsconfig.build.tsbuildinfo +1 -1
- package/lib/utils/project-config.d.ts +5 -0
- package/lib/utils/project-config.js +145 -0
- package/lib/utils.js +1 -1
- package/next-i18next.config.js +18 -0
- package/next.config.js +61 -18
- package/package.json +16 -8
- package/pages/_app.tsx +77 -33
- package/pages/_document.tsx +78 -21
- package/pages/_error.tsx +66 -0
- package/pages/index.tsx +109 -47
- package/pages/login.tsx +66 -41
- package/pages/template/manage.tsx +162 -151
- package/public/fonts/font-awesome.min.css +4 -0
- package/public/fonts/fontawesome-webfont.woff +0 -0
- package/public/fonts/fontawesome-webfont.woff2 +0 -0
- package/public/locales/en-US/common.json +48 -0
- package/public/locales/en-US/home.json +57 -0
- package/public/locales/en-US/layout.json +22 -0
- package/public/locales/en-US/login.json +13 -0
- package/public/locales/en-US/template.json +42 -0
- package/public/locales/ja-JP/common.json +48 -0
- package/public/locales/ja-JP/home.json +57 -0
- package/public/locales/ja-JP/layout.json +22 -0
- package/public/locales/ja-JP/login.json +13 -0
- package/public/locales/ja-JP/template.json +42 -0
- package/public/locales/zh-CN/common.json +48 -0
- package/public/locales/zh-CN/home.json +57 -0
- package/public/locales/zh-CN/layout.json +22 -0
- package/public/locales/zh-CN/login.json +13 -0
- package/public/locales/zh-CN/template.json +42 -0
- package/public/slbhealthcheck.html +1 -1
- package/server/apis/template.js +0 -2
- package/server/utils/validation.js +163 -0
- package/types/i18next.d.ts +10 -0
- package/generation/eslintrc.js +0 -16
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ResolverGenerator = void 0;
|
|
4
|
+
const base_generator_1 = require("./base-generator");
|
|
5
|
+
/**
|
|
6
|
+
* Resolver生成器
|
|
7
|
+
*/
|
|
8
|
+
class ResolverGenerator extends base_generator_1.BaseGenerator {
|
|
9
|
+
generate() {
|
|
10
|
+
const selectFields = this.fields.map((f) => f.name).join(', ');
|
|
11
|
+
const insertFields = this.getFormFields();
|
|
12
|
+
const searchableFields = this.getSearchableFields();
|
|
13
|
+
const insertFieldNames = insertFields.map((f) => f.name).join(', ');
|
|
14
|
+
const insertPlaceholders = insertFields.map(() => '?').join(', ');
|
|
15
|
+
const insertValues = insertFields
|
|
16
|
+
.map((f) => {
|
|
17
|
+
if (f.type === 'integer') {
|
|
18
|
+
return `valid${f.name.charAt(0).toUpperCase() + f.name.slice(1)}`;
|
|
19
|
+
}
|
|
20
|
+
return `data.${f.name}`;
|
|
21
|
+
})
|
|
22
|
+
.join(', ');
|
|
23
|
+
const searchConditions = this.generateSearchConditions(searchableFields);
|
|
24
|
+
const updateFields = insertFields.map((f) => `${f.name} = ?`).join(', ');
|
|
25
|
+
return `const { executeQuery, executePaginatedQuery } = require('../../utils/common')
|
|
26
|
+
const { validateInteger, validatePagination, validateId } = require('../../utils/validation')
|
|
27
|
+
|
|
28
|
+
module.exports = {
|
|
29
|
+
// 获取${this.controller}列表(分页)
|
|
30
|
+
${this.controller}: async ({ page = 0, pageSize = 10 }) => {
|
|
31
|
+
try {
|
|
32
|
+
const { page: validPage, pageSize: validPageSize } = validatePagination(page, pageSize);
|
|
33
|
+
|
|
34
|
+
const sql = 'SELECT ${selectFields} FROM ${this.controller} LIMIT ? OFFSET ?';
|
|
35
|
+
const countSql = 'SELECT COUNT(*) as counts FROM ${this.controller}';
|
|
36
|
+
const values = [validPageSize, validPage * validPageSize];
|
|
37
|
+
|
|
38
|
+
console.log('执行分页查询:', { sql, values, countSql });
|
|
39
|
+
|
|
40
|
+
return await executePaginatedQuery(sql, countSql, values);
|
|
41
|
+
} catch (error) {
|
|
42
|
+
console.error('获取${this.controller}列表失败:', error.message);
|
|
43
|
+
throw error;
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
// 根据ID获取${this.controller}
|
|
48
|
+
${this.controller}Get: async ({ id }) => {
|
|
49
|
+
try {
|
|
50
|
+
const validId = validateId(id);
|
|
51
|
+
|
|
52
|
+
const sql = 'SELECT ${selectFields} FROM ${this.controller} WHERE id = ?';
|
|
53
|
+
const values = [validId];
|
|
54
|
+
|
|
55
|
+
console.log('根据ID查询${this.controller}:', { sql, values });
|
|
56
|
+
|
|
57
|
+
const results = await executeQuery(sql, values);
|
|
58
|
+
|
|
59
|
+
if (results.length === 0) {
|
|
60
|
+
throw new Error(\`ID为 \${validId} 的${this.controller}不存在\`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return results[0];
|
|
64
|
+
} catch (error) {
|
|
65
|
+
console.error('获取${this.controller}失败:', error.message);
|
|
66
|
+
throw error;
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
// 搜索${this.controller}(分页)
|
|
71
|
+
${this.controller}Search: async ({ page = 0, pageSize = 10, data = {} }) => {
|
|
72
|
+
try {
|
|
73
|
+
const { page: validPage, pageSize: validPageSize } = validatePagination(page, pageSize);
|
|
74
|
+
|
|
75
|
+
const values = [];
|
|
76
|
+
const countValues = [];
|
|
77
|
+
|
|
78
|
+
let whereSql = '';
|
|
79
|
+
${searchConditions}
|
|
80
|
+
|
|
81
|
+
const sql = \`SELECT ${selectFields} FROM ${this.controller} WHERE 1=1\${whereSql} LIMIT ? OFFSET ?\`;
|
|
82
|
+
const countSql = \`SELECT COUNT(*) as counts FROM ${this.controller} WHERE 1=1\${whereSql}\`;
|
|
83
|
+
|
|
84
|
+
values.push(validPageSize, validPage * validPageSize);
|
|
85
|
+
|
|
86
|
+
console.log('搜索${this.controller}:', { sql, values, countSql, countValues });
|
|
87
|
+
|
|
88
|
+
return await executePaginatedQuery(sql, countSql, values, countValues);
|
|
89
|
+
} catch (error) {
|
|
90
|
+
console.error('搜索${this.controller}失败:', error.message);
|
|
91
|
+
throw error;
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
// 添加${this.controller}
|
|
96
|
+
${this.controller}Add: async ({ data }) => {
|
|
97
|
+
try {
|
|
98
|
+
${this.generateNewValidationCalls(insertFields)}
|
|
99
|
+
|
|
100
|
+
const sql = 'INSERT INTO ${this.controller} (${insertFieldNames}) VALUES (${insertPlaceholders})';
|
|
101
|
+
const values = [${insertValues}];
|
|
102
|
+
|
|
103
|
+
console.log('添加${this.controller}:', { sql, values });
|
|
104
|
+
|
|
105
|
+
const results = await executeQuery(sql, values);
|
|
106
|
+
return results.insertId;
|
|
107
|
+
} catch (error) {
|
|
108
|
+
console.error('添加${this.controller}失败:', error.message);
|
|
109
|
+
throw error;
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
|
|
113
|
+
// 批量添加${this.controller}
|
|
114
|
+
${this.controller}BatchAdd: async ({ datas }) => {
|
|
115
|
+
try {
|
|
116
|
+
if (!Array.isArray(datas) || datas.length === 0) {
|
|
117
|
+
throw new Error('批量添加数据不能为空');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// 验证所有数据并转换
|
|
121
|
+
const validatedDatas = datas.map((data, index) => {
|
|
122
|
+
try {
|
|
123
|
+
${this.generateBatchValidation(insertFields)}
|
|
124
|
+
return { ${this.generateBatchReturnObject(insertFields)} };
|
|
125
|
+
} catch (error) {
|
|
126
|
+
throw new Error(\`第 \${index + 1} 条数据验证失败: \${error.message}\`);
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
const placeholders = validatedDatas.map(() => '(${insertPlaceholders})').join(',');
|
|
131
|
+
const sql = \`INSERT INTO ${this.controller} (${insertFieldNames}) VALUES \${placeholders}\`;
|
|
132
|
+
const values = validatedDatas.flatMap(data => [${this.generateBatchInsertValues(insertFields)}]);
|
|
133
|
+
|
|
134
|
+
console.log('批量添加${this.controller}:', { sql, values });
|
|
135
|
+
|
|
136
|
+
const results = await executeQuery(sql, values);
|
|
137
|
+
return results.insertId;
|
|
138
|
+
} catch (error) {
|
|
139
|
+
console.error('批量添加${this.controller}失败:', error.message);
|
|
140
|
+
throw error;
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
|
|
144
|
+
// 更新${this.controller}
|
|
145
|
+
${this.controller}Update: async ({ id, data }) => {
|
|
146
|
+
try {
|
|
147
|
+
const validId = validateId(id);
|
|
148
|
+
|
|
149
|
+
if (!data) {
|
|
150
|
+
throw new Error('更新数据不能为空');
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
${this.generateUpdateValidation(insertFields)}
|
|
154
|
+
|
|
155
|
+
const sql = 'UPDATE ${this.controller} SET ${updateFields} WHERE id = ?';
|
|
156
|
+
const values = [${this.generateUpdateValues(insertFields)}, validId];
|
|
157
|
+
|
|
158
|
+
console.log('更新${this.controller}:', { sql, values });
|
|
159
|
+
|
|
160
|
+
const results = await executeQuery(sql, values);
|
|
161
|
+
|
|
162
|
+
if (results.affectedRows === 0) {
|
|
163
|
+
throw new Error(\`ID为 \${validId} 的${this.controller}不存在\`);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return true;
|
|
167
|
+
} catch (error) {
|
|
168
|
+
console.error('更新${this.controller}失败:', error.message);
|
|
169
|
+
throw error;
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
|
|
173
|
+
// 删除${this.controller}
|
|
174
|
+
${this.controller}Delete: async ({ id }) => {
|
|
175
|
+
try {
|
|
176
|
+
const validId = validateId(id);
|
|
177
|
+
|
|
178
|
+
const sql = 'DELETE FROM ${this.controller} WHERE id = ?';
|
|
179
|
+
const values = [validId];
|
|
180
|
+
|
|
181
|
+
console.log('删除${this.controller}:', { sql, values });
|
|
182
|
+
|
|
183
|
+
const results = await executeQuery(sql, values);
|
|
184
|
+
|
|
185
|
+
if (results.affectedRows === 0) {
|
|
186
|
+
throw new Error(\`ID为 \${validId} 的${this.controller}不存在\`);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return true;
|
|
190
|
+
} catch (error) {
|
|
191
|
+
console.error('删除${this.controller}失败:', error.message);
|
|
192
|
+
throw error;
|
|
193
|
+
}
|
|
194
|
+
},
|
|
195
|
+
|
|
196
|
+
// 批量删除${this.controller}
|
|
197
|
+
${this.controller}BatchDelete: async ({ ids }) => {
|
|
198
|
+
try {
|
|
199
|
+
if (!Array.isArray(ids) || ids.length === 0) {
|
|
200
|
+
throw new Error('批量删除的ID列表不能为空');
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// 验证所有ID
|
|
204
|
+
const validIds = ids.map((id, index) => {
|
|
205
|
+
try {
|
|
206
|
+
return validateId(id, \`第\${index + 1}个ID\`);
|
|
207
|
+
} catch (error) {
|
|
208
|
+
throw new Error(\`第 \${index + 1} 个ID验证失败: \${error.message}\`);
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
const placeholders = validIds.map(() => '?').join(',');
|
|
213
|
+
const sql = \`DELETE FROM ${this.controller} WHERE id IN (\${placeholders})\`;
|
|
214
|
+
|
|
215
|
+
console.log('批量删除${this.controller}:', { sql, values: validIds });
|
|
216
|
+
|
|
217
|
+
const results = await executeQuery(sql, validIds);
|
|
218
|
+
|
|
219
|
+
if (results.affectedRows === 0) {
|
|
220
|
+
throw new Error('没有找到要删除的${this.controller}');
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return true;
|
|
224
|
+
} catch (error) {
|
|
225
|
+
console.error('批量删除${this.controller}失败:', error.message);
|
|
226
|
+
throw error;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}`;
|
|
230
|
+
}
|
|
231
|
+
generateSearchConditions(searchableFields) {
|
|
232
|
+
if (searchableFields.length === 0)
|
|
233
|
+
return '';
|
|
234
|
+
const conditions = searchableFields.map((field) => {
|
|
235
|
+
if (field.type === 'varchar' || field.type === 'text') {
|
|
236
|
+
return ` if (data.${field.name} && data.${field.name}.trim() !== '') {
|
|
237
|
+
whereSql += ' AND ${field.name} LIKE ?';
|
|
238
|
+
const ${field.name}Pattern = \`%\${data.${field.name}.trim()}%\`;
|
|
239
|
+
values.push(${field.name}Pattern);
|
|
240
|
+
countValues.push(${field.name}Pattern);
|
|
241
|
+
}`;
|
|
242
|
+
}
|
|
243
|
+
else if (field.type === 'integer') {
|
|
244
|
+
return ` if (data.${field.name} !== undefined && data.${field.name} !== null && data.${field.name} !== '') {
|
|
245
|
+
// 使用通用验证工具验证 ${field.name}
|
|
246
|
+
const valid${field.name.charAt(0).toUpperCase() + field.name.slice(1)} = validateInteger(data.${field.name}, '${field.name}', { min: 0, max: 150 });
|
|
247
|
+
if (valid${field.name.charAt(0).toUpperCase() + field.name.slice(1)} !== undefined) {
|
|
248
|
+
whereSql += ' AND ${field.name} = ?';
|
|
249
|
+
values.push(valid${field.name.charAt(0).toUpperCase() + field.name.slice(1)});
|
|
250
|
+
countValues.push(valid${field.name.charAt(0).toUpperCase() + field.name.slice(1)});
|
|
251
|
+
}
|
|
252
|
+
}`;
|
|
253
|
+
}
|
|
254
|
+
else {
|
|
255
|
+
return ` if (data.${field.name} !== undefined && data.${field.name} !== null) {
|
|
256
|
+
whereSql += ' AND ${field.name} = ?';
|
|
257
|
+
values.push(data.${field.name});
|
|
258
|
+
countValues.push(data.${field.name});
|
|
259
|
+
}`;
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
return conditions.join('\n\n');
|
|
263
|
+
}
|
|
264
|
+
generateNewValidationCalls(insertFields) {
|
|
265
|
+
return insertFields
|
|
266
|
+
.map((f) => {
|
|
267
|
+
if (f.type === 'integer') {
|
|
268
|
+
const validationOptions = f.name === 'age' ? '{ min: 0, max: 150, required: true }' : '{ required: true }';
|
|
269
|
+
return ` const valid${f.name.charAt(0).toUpperCase() + f.name.slice(1)} = validateInteger(data.${f.name}, '${f.name}', ${validationOptions});`;
|
|
270
|
+
}
|
|
271
|
+
else if (f.required) {
|
|
272
|
+
return ` if (!data.${f.name}) {
|
|
273
|
+
throw new Error('${f.comment || f.name}是必填字段');
|
|
274
|
+
}`;
|
|
275
|
+
}
|
|
276
|
+
return '';
|
|
277
|
+
})
|
|
278
|
+
.filter((call) => call.length > 0)
|
|
279
|
+
.join('\n');
|
|
280
|
+
}
|
|
281
|
+
generateBatchValidation(insertFields) {
|
|
282
|
+
return insertFields
|
|
283
|
+
.map((f) => {
|
|
284
|
+
if (f.type === 'integer') {
|
|
285
|
+
const validationOptions = f.name === 'age' ? '{ min: 0, max: 150, required: true }' : '{ required: true }';
|
|
286
|
+
return ` const valid${f.name.charAt(0).toUpperCase() + f.name.slice(1)} = validateInteger(data.${f.name}, \`第\${index + 1}条数据的${f.name}\`, ${validationOptions});`;
|
|
287
|
+
}
|
|
288
|
+
else if (f.required) {
|
|
289
|
+
return ` if (!data.${f.name}) {
|
|
290
|
+
throw new Error('${f.comment || f.name}是必填字段');
|
|
291
|
+
}`;
|
|
292
|
+
}
|
|
293
|
+
return '';
|
|
294
|
+
})
|
|
295
|
+
.filter((call) => call.length > 0)
|
|
296
|
+
.join('\n');
|
|
297
|
+
}
|
|
298
|
+
generateUpdateValidation(insertFields) {
|
|
299
|
+
return insertFields
|
|
300
|
+
.map((f) => {
|
|
301
|
+
if (f.type === 'integer') {
|
|
302
|
+
const validationOptions = f.name === 'age' ? '{ min: 0, max: 150, required: true }' : '{ required: true }';
|
|
303
|
+
return ` let valid${f.name.charAt(0).toUpperCase() + f.name.slice(1)} = data.${f.name};
|
|
304
|
+
if (data.${f.name} !== undefined) {
|
|
305
|
+
valid${f.name.charAt(0).toUpperCase() + f.name.slice(1)} = validateInteger(data.${f.name}, '${f.name}', ${validationOptions});
|
|
306
|
+
}`;
|
|
307
|
+
}
|
|
308
|
+
else if (f.required) {
|
|
309
|
+
return ` if (data.${f.name} !== undefined && !data.${f.name}) {
|
|
310
|
+
throw new Error('${f.comment || f.name}是必填字段');
|
|
311
|
+
}`;
|
|
312
|
+
}
|
|
313
|
+
return '';
|
|
314
|
+
})
|
|
315
|
+
.filter((call) => call.length > 0)
|
|
316
|
+
.join('\n');
|
|
317
|
+
}
|
|
318
|
+
generateUpdateValues(insertFields) {
|
|
319
|
+
return insertFields
|
|
320
|
+
.map((f) => {
|
|
321
|
+
if (f.type === 'integer') {
|
|
322
|
+
return `valid${f.name.charAt(0).toUpperCase() + f.name.slice(1)}`;
|
|
323
|
+
}
|
|
324
|
+
return `data.${f.name}`;
|
|
325
|
+
})
|
|
326
|
+
.join(', ');
|
|
327
|
+
}
|
|
328
|
+
generateBatchReturnObject(insertFields) {
|
|
329
|
+
return insertFields
|
|
330
|
+
.map((f) => {
|
|
331
|
+
if (f.type === 'integer') {
|
|
332
|
+
return `${f.name}: valid${f.name.charAt(0).toUpperCase() + f.name.slice(1)}`;
|
|
333
|
+
}
|
|
334
|
+
return `${f.name}: data.${f.name}`;
|
|
335
|
+
})
|
|
336
|
+
.join(', ');
|
|
337
|
+
}
|
|
338
|
+
generateBatchInsertValues(insertFields) {
|
|
339
|
+
return insertFields.map((f) => `data.${f.name}`).join(', ');
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
exports.ResolverGenerator = ResolverGenerator;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SchemaGenerator = void 0;
|
|
4
|
+
const base_generator_1 = require("./base-generator");
|
|
5
|
+
/**
|
|
6
|
+
* GraphQL Schema生成器
|
|
7
|
+
*/
|
|
8
|
+
class SchemaGenerator extends base_generator_1.BaseGenerator {
|
|
9
|
+
generate() {
|
|
10
|
+
const capitalizedTypeName = this.getCapitalizedController();
|
|
11
|
+
const pluralTypeName = `${capitalizedTypeName}s`;
|
|
12
|
+
const typeFields = this.getNonSystemFields()
|
|
13
|
+
.map((field) => ` ${field.name}: ${this.getGraphQLType(field.type)}`)
|
|
14
|
+
.join('\n');
|
|
15
|
+
const inputFields = this.getFormFields()
|
|
16
|
+
.map((field) => ` ${field.name}: ${this.getGraphQLType(field.type)}`)
|
|
17
|
+
.join('\n');
|
|
18
|
+
const searchableFields = this.getSearchableFields();
|
|
19
|
+
const searchFields = searchableFields.length > 0
|
|
20
|
+
? searchableFields.map((field) => ` ${field.name}: ${this.getGraphQLType(field.type)}`).join('\n')
|
|
21
|
+
: ' name: String';
|
|
22
|
+
return `module.exports = {
|
|
23
|
+
query: \`
|
|
24
|
+
${this.controller}(page: Int, pageSize: Int): ${pluralTypeName}
|
|
25
|
+
${this.controller}Get(id: Int): ${capitalizedTypeName}
|
|
26
|
+
${this.controller}Search(page: Int, pageSize: Int, data: ${capitalizedTypeName}SearchInput): ${pluralTypeName}
|
|
27
|
+
\`,
|
|
28
|
+
mutation: \`
|
|
29
|
+
${this.controller}Add(data: ${capitalizedTypeName}AddInput): Int
|
|
30
|
+
${this.controller}BatchAdd(datas: [${capitalizedTypeName}AddInput]): Int
|
|
31
|
+
${this.controller}Update(id: Int, data: ${capitalizedTypeName}AddInput): Boolean
|
|
32
|
+
${this.controller}Delete(id: Int): Boolean
|
|
33
|
+
${this.controller}BatchDelete(ids: [Int]): Boolean
|
|
34
|
+
\`,
|
|
35
|
+
subscription: \`\`,
|
|
36
|
+
type: \`
|
|
37
|
+
type ${capitalizedTypeName} {
|
|
38
|
+
${typeFields}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
type ${pluralTypeName} {
|
|
42
|
+
totalCounts: Int
|
|
43
|
+
items: [${capitalizedTypeName}]
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
input ${capitalizedTypeName}AddInput {
|
|
47
|
+
${inputFields}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
input ${capitalizedTypeName}SearchInput {
|
|
51
|
+
${searchFields}
|
|
52
|
+
}
|
|
53
|
+
\`
|
|
54
|
+
}`;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
exports.SchemaGenerator = SchemaGenerator;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { BaseGenerator } from './base-generator';
|
|
2
|
+
/**
|
|
3
|
+
* 客户端服务生成器
|
|
4
|
+
*/
|
|
5
|
+
export declare class ServiceGenerator extends BaseGenerator {
|
|
6
|
+
generate(): string;
|
|
7
|
+
private generateValidationFunctions;
|
|
8
|
+
private generateValidationCallsForService;
|
|
9
|
+
private generateDataObjectWithValidation;
|
|
10
|
+
private generateBatchValidationCalls;
|
|
11
|
+
}
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ServiceGenerator = void 0;
|
|
4
|
+
const base_generator_1 = require("./base-generator");
|
|
5
|
+
/**
|
|
6
|
+
* 客户端服务生成器
|
|
7
|
+
*/
|
|
8
|
+
class ServiceGenerator extends base_generator_1.BaseGenerator {
|
|
9
|
+
generate() {
|
|
10
|
+
const capitalizedTypeName = this.getCapitalizedController();
|
|
11
|
+
// 排除系统字段 create_date 和 update_date
|
|
12
|
+
const selectFields = this.fields
|
|
13
|
+
.filter((f) => !['create_date', 'update_date'].includes(f.name))
|
|
14
|
+
.map((f) => f.name)
|
|
15
|
+
.join(' ');
|
|
16
|
+
const inputFields = this.getFormFields();
|
|
17
|
+
const searchFields = this.getSearchableFields();
|
|
18
|
+
// 检查是否有integer类型字段需要验证
|
|
19
|
+
const hasIntegerFields = inputFields.some((field) => field.type === 'integer');
|
|
20
|
+
const validationFunctions = hasIntegerFields ? this.generateValidationFunctions(inputFields) : '';
|
|
21
|
+
return `import { getLocalGraphql } from '@/utils/fetch'
|
|
22
|
+
import _ from 'lodash'
|
|
23
|
+
|
|
24
|
+
${validationFunctions}
|
|
25
|
+
|
|
26
|
+
export const get${capitalizedTypeName}Service = (page = 0, pageSize = 10) => {
|
|
27
|
+
const get${capitalizedTypeName}Query = \`query ($page: Int, $pageSize: Int) { ${this.controller}(page: $page, pageSize: $pageSize) {
|
|
28
|
+
totalCounts items {
|
|
29
|
+
${selectFields}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}\`
|
|
33
|
+
|
|
34
|
+
return getLocalGraphql(get${capitalizedTypeName}Query, {
|
|
35
|
+
page,
|
|
36
|
+
pageSize
|
|
37
|
+
})
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export const search${capitalizedTypeName}ByIdService = (id: number) => {
|
|
41
|
+
const search${capitalizedTypeName}ByIdQuery = \`query ($id: Int) { ${this.controller}Get(id: $id){
|
|
42
|
+
${selectFields}
|
|
43
|
+
}
|
|
44
|
+
}\`
|
|
45
|
+
|
|
46
|
+
return getLocalGraphql(search${capitalizedTypeName}ByIdQuery, {
|
|
47
|
+
id
|
|
48
|
+
})
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export const search${capitalizedTypeName}Service = (page = 0, pageSize = 10, data: any) => {
|
|
52
|
+
const { ${searchFields.map((f) => f.name).join(', ')} } = data
|
|
53
|
+
|
|
54
|
+
${this.generateValidationCallsForService(searchFields, ' ', false)}
|
|
55
|
+
|
|
56
|
+
const search${capitalizedTypeName}Query = \`query ($page: Int, $pageSize: Int, $data: ${capitalizedTypeName}SearchInput) {
|
|
57
|
+
${this.controller}Search(page: $page, pageSize: $pageSize, data: $data) {
|
|
58
|
+
totalCounts items {
|
|
59
|
+
${selectFields}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}\`
|
|
63
|
+
|
|
64
|
+
return getLocalGraphql(search${capitalizedTypeName}Query, {
|
|
65
|
+
page,
|
|
66
|
+
pageSize,
|
|
67
|
+
data: {
|
|
68
|
+
${this.generateDataObjectWithValidation(searchFields)}
|
|
69
|
+
}
|
|
70
|
+
})
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export const add${capitalizedTypeName}Service = (data: any) => {
|
|
74
|
+
const { ${inputFields.map((f) => f.name).join(', ')} } = data
|
|
75
|
+
|
|
76
|
+
${this.generateValidationCallsForService(inputFields, ' ', true)}
|
|
77
|
+
|
|
78
|
+
const add${capitalizedTypeName}Query = \`mutation ($data: ${capitalizedTypeName}AddInput) { ${this.controller}Add(data: $data) }\`
|
|
79
|
+
|
|
80
|
+
return getLocalGraphql(add${capitalizedTypeName}Query, {
|
|
81
|
+
data: {
|
|
82
|
+
${this.generateDataObjectWithValidation(inputFields)}
|
|
83
|
+
}
|
|
84
|
+
})
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export const update${capitalizedTypeName}Service = (id: number, data: any) => {
|
|
88
|
+
const { ${inputFields.map((f) => f.name).join(', ')} } = data
|
|
89
|
+
|
|
90
|
+
${this.generateValidationCallsForService(inputFields, ' ', true)}
|
|
91
|
+
|
|
92
|
+
const update${capitalizedTypeName}Query = \`mutation ($id: Int, $data: ${capitalizedTypeName}AddInput) { ${this.controller}Update(id: $id, data: $data) }\`
|
|
93
|
+
|
|
94
|
+
return getLocalGraphql(update${capitalizedTypeName}Query, {
|
|
95
|
+
id,
|
|
96
|
+
data: {
|
|
97
|
+
${this.generateDataObjectWithValidation(inputFields)}
|
|
98
|
+
}
|
|
99
|
+
})
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export const delete${capitalizedTypeName}Service = (id: number) => {
|
|
103
|
+
const delete${capitalizedTypeName}Query = \`mutation ($id: Int) { ${this.controller}Delete(id: $id) }\`
|
|
104
|
+
|
|
105
|
+
return getLocalGraphql(delete${capitalizedTypeName}Query, {
|
|
106
|
+
id
|
|
107
|
+
})
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export const batchAdd${capitalizedTypeName}Service = (datas: any) => {
|
|
111
|
+
${this.generateBatchValidationCalls(inputFields, ' ')}
|
|
112
|
+
|
|
113
|
+
const batchAdd${capitalizedTypeName}Query = \`mutation ($datas: [${capitalizedTypeName}AddInput]) { ${this.controller}BatchAdd(datas: $datas) }\`
|
|
114
|
+
|
|
115
|
+
return getLocalGraphql(batchAdd${capitalizedTypeName}Query, {
|
|
116
|
+
datas: validatedDatas
|
|
117
|
+
})
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export const batchDelete${capitalizedTypeName}Service = (ids: any) => {
|
|
121
|
+
const batchDelete${capitalizedTypeName}Query = \`mutation ($ids: [Int]) { ${this.controller}BatchDelete(ids: $ids) }\`
|
|
122
|
+
|
|
123
|
+
return getLocalGraphql(batchDelete${capitalizedTypeName}Query, {
|
|
124
|
+
ids
|
|
125
|
+
})
|
|
126
|
+
}
|
|
127
|
+
`;
|
|
128
|
+
}
|
|
129
|
+
generateValidationFunctions(inputFields) {
|
|
130
|
+
const integerFields = inputFields.filter((field) => field.type === 'integer');
|
|
131
|
+
if (integerFields.length === 0) {
|
|
132
|
+
return '';
|
|
133
|
+
}
|
|
134
|
+
const validationFunctions = integerFields.map((field) => {
|
|
135
|
+
const fieldName = field.name;
|
|
136
|
+
const capitalizedName = fieldName.charAt(0).toUpperCase() + fieldName.slice(1);
|
|
137
|
+
const upperFieldName = fieldName.toUpperCase();
|
|
138
|
+
return `// 简化的${fieldName}验证函数
|
|
139
|
+
const validate${capitalizedName} = (${fieldName}: any, required = false) => {
|
|
140
|
+
if (${fieldName} === undefined || ${fieldName} === null || ${fieldName} === '') {
|
|
141
|
+
if (required) {
|
|
142
|
+
return { valid: false, error: '${fieldName}是必填字段', code: 'REQUIRED_${upperFieldName}_MISSING' }
|
|
143
|
+
}
|
|
144
|
+
return { valid: true, value: undefined }
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const parsed${capitalizedName} = parseInt(${fieldName}, 10)
|
|
148
|
+
if (isNaN(parsed${capitalizedName})) {
|
|
149
|
+
return {
|
|
150
|
+
valid: false,
|
|
151
|
+
error: \`${fieldName}必须是数字,收到的值: "\${${fieldName}}"\`,
|
|
152
|
+
code: 'INVALID_${upperFieldName}_FORMAT'
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return { valid: true, value: parsed${capitalizedName} }
|
|
157
|
+
}`;
|
|
158
|
+
});
|
|
159
|
+
return `${validationFunctions.join('\n\n')}\n\n`;
|
|
160
|
+
}
|
|
161
|
+
generateValidationCallsForService(inputFields, indent, required) {
|
|
162
|
+
const integerFields = inputFields.filter((field) => field.type === 'integer');
|
|
163
|
+
if (integerFields.length === 0) {
|
|
164
|
+
return '';
|
|
165
|
+
}
|
|
166
|
+
const validationCalls = integerFields.map((field) => {
|
|
167
|
+
const fieldName = field.name;
|
|
168
|
+
const capitalizedName = fieldName.charAt(0).toUpperCase() + fieldName.slice(1);
|
|
169
|
+
const requiredStr = required ? 'true' : 'false';
|
|
170
|
+
return `${indent}// 验证${required ? '必填' : ''}${fieldName}
|
|
171
|
+
${indent}const ${fieldName}Validation = validate${capitalizedName}(${fieldName}, ${requiredStr})
|
|
172
|
+
${indent}if (!${fieldName}Validation.valid) {
|
|
173
|
+
${indent} return Promise.reject({
|
|
174
|
+
${indent} error: true,
|
|
175
|
+
${indent} message: ${fieldName}Validation.error,
|
|
176
|
+
${indent} code: ${fieldName}Validation.code
|
|
177
|
+
${indent} })
|
|
178
|
+
${indent}}`;
|
|
179
|
+
});
|
|
180
|
+
return validationCalls.join('\n\n') + (validationCalls.length > 0 ? '\n\n' : '');
|
|
181
|
+
}
|
|
182
|
+
generateDataObjectWithValidation(inputFields) {
|
|
183
|
+
return inputFields
|
|
184
|
+
.map((field) => {
|
|
185
|
+
if (field.type === 'integer') {
|
|
186
|
+
return ` ${field.name}: ${field.name}Validation.value`;
|
|
187
|
+
}
|
|
188
|
+
return ` ${field.name}`;
|
|
189
|
+
})
|
|
190
|
+
.join(',\n');
|
|
191
|
+
}
|
|
192
|
+
generateBatchValidationCalls(inputFields, indent) {
|
|
193
|
+
const integerFields = inputFields.filter((field) => field.type === 'integer');
|
|
194
|
+
if (integerFields.length === 0) {
|
|
195
|
+
return `${indent}// 验证批量数据
|
|
196
|
+
${indent}const validatedDatas: Array<{ ${inputFields.map((f) => `${f.name}: any`).join('; ')} }> = []
|
|
197
|
+
|
|
198
|
+
${indent}for (let i = 0; i < datas.length; i++) {
|
|
199
|
+
${indent} const { ${inputFields.map((f) => f.name).join(', ')} } = datas[i]
|
|
200
|
+
|
|
201
|
+
${indent} validatedDatas.push({
|
|
202
|
+
${indent} ${inputFields.map((f) => f.name).join(',\n ')}
|
|
203
|
+
${indent} })
|
|
204
|
+
${indent}}`;
|
|
205
|
+
}
|
|
206
|
+
return `${indent}// 验证批量数据
|
|
207
|
+
${indent}const validatedDatas: Array<{ ${inputFields.map((f) => `${f.name}: ${f.type === 'integer' ? 'number' : 'any'}`).join('; ')} }> = []
|
|
208
|
+
|
|
209
|
+
${indent}for (let i = 0; i < datas.length; i++) {
|
|
210
|
+
${indent} const { ${inputFields.map((f) => f.name).join(', ')} } = datas[i]
|
|
211
|
+
|
|
212
|
+
${integerFields
|
|
213
|
+
.map((field) => {
|
|
214
|
+
const fieldName = field.name;
|
|
215
|
+
const capitalizedName = fieldName.charAt(0).toUpperCase() + fieldName.slice(1);
|
|
216
|
+
return `${indent} const ${fieldName}Validation = validate${capitalizedName}(${fieldName}, true)
|
|
217
|
+
${indent} if (!${fieldName}Validation.valid) {
|
|
218
|
+
${indent} return Promise.reject({
|
|
219
|
+
${indent} error: true,
|
|
220
|
+
${indent} message: \`第 \${i + 1} 条数据: \${${fieldName}Validation.error}\`,
|
|
221
|
+
${indent} code: ${fieldName}Validation.code
|
|
222
|
+
${indent} })
|
|
223
|
+
${indent} }`;
|
|
224
|
+
})
|
|
225
|
+
.join('\n\n')}
|
|
226
|
+
|
|
227
|
+
${indent} validatedDatas.push({
|
|
228
|
+
${indent} ${inputFields.map((field) => (field.type === 'integer' ? `${field.name}: ${field.name}Validation.value!` : field.name)).join(',\n ')}
|
|
229
|
+
${indent} })
|
|
230
|
+
${indent}}`;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
exports.ServiceGenerator = ServiceGenerator;
|