nsgm-cli 2.1.22 → 2.1.23

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.
@@ -25,12 +25,16 @@ export const getLocalApiPrefix = () => {
25
25
  protocol = protocol.split(":")[0];
26
26
  }
27
27
  host = location.hostname;
28
- port = location.port || (protocol.indexOf("https") !== -1 ? "443" : "80");
28
+ port = location.port;
29
29
  }
30
30
  // 服务器端:直接使用配置中的值,无需额外处理
31
31
  }
32
32
 
33
- localApiPrefix = `${protocol}://${host}:${port}${prefix}`;
33
+ // 只在非标准端口时才添加端口号
34
+ const isStandardPort = (protocol === "https" && port === "443") || (protocol === "http" && port === "80") || !port;
35
+ const portStr = isStandardPort ? "" : `:${port}`;
36
+
37
+ localApiPrefix = `${protocol}://${host}${portStr}${prefix}`;
34
38
  return localApiPrefix;
35
39
  };
36
40
 
@@ -188,7 +188,7 @@ export const getLocalGraphql = async (query: string, variables: any = {}) => {
188
188
  };
189
189
 
190
190
  const retryResponse = await axios.post(
191
- `${getLocalApiPrefix()}/graphql`,
191
+ `/api/graphql`,
192
192
  { query, variables },
193
193
  { headers: retryHeaders, withCredentials: true }
194
194
  );
@@ -210,22 +210,32 @@ export const directLogin = (userName: string, userPassword: string, callback: an
210
210
  // 使用 encodeURIComponent 处理可能的特殊字符,然后再进行 Base64 编码
211
211
  const safeStr = handleXSS(`${userName},${userPassword}`);
212
212
  const encodedName = btoa(encodeURIComponent(safeStr));
213
- const url = `${getLocalApiPrefix()}/rest/sso/ticketCheck?ticket=XXX&name=${encodedName}`;
213
+ const apiPrefix = getLocalApiPrefix();
214
+ const url = `${apiPrefix}/rest/sso/ticketCheck?ticket=XXX&name=${encodedName}`;
215
+
216
+ console.warn("[Login] Login URL:", url);
217
+ console.warn("[Login] Username:", userName);
214
218
 
215
219
  return fetch(url)
216
- .then((response) => response.json())
220
+ .then((response) => {
221
+ console.warn("[Login] Response status:", response.status);
222
+ return response.json();
223
+ })
217
224
  .then((data) => {
225
+ console.warn("[Login] Response data:", data);
218
226
  if (data && data.returnCode === 0) {
219
227
  // 登录成功,设置cookie
220
228
  if (typeof window !== "undefined") {
229
+ console.warn("[Login] Login successful");
221
230
  storeLogin(data.cookieValue, data.cookieExpire, data.userAttr, callback);
222
231
  return { success: true };
223
232
  }
224
233
  }
234
+ console.warn("[Login] Login failed, returnCode:", data?.returnCode, "message:", data?.message);
225
235
  return { success: false, message: "用户名或密码错误" };
226
236
  })
227
237
  .catch((error) => {
228
- console.error("登录请求失败:", error);
238
+ console.warn("[Login] Login request failed:", error);
229
239
  return { success: false, message: "登录请求失败,请稍后重试" };
230
240
  });
231
241
  };
@@ -48,6 +48,7 @@ const resolver_generator_1 = require("./generators/resolver-generator");
48
48
  const service_generator_1 = require("./generators/service-generator");
49
49
  const page_generator_1 = require("./generators/page-generator");
50
50
  const file_generator_1 = require("./generators/file-generator");
51
+ const dataloader_generator_1 = require("./generators/dataloader-generator");
51
52
  // 常量定义
52
53
  const TEMPLATE_FILES = {
53
54
  reduxActions: "redux/template/manage/actions.ts",
@@ -207,6 +208,7 @@ const generateDynamicFiles = (controller, action, paths, fields, dictionary) =>
207
208
  const resolverGenerator = new resolver_generator_1.ResolverGenerator(controller, action, fields);
208
209
  const serviceGenerator = new service_generator_1.ServiceGenerator(controller, action, fields);
209
210
  const pageGenerator = new page_generator_1.PageGenerator(controller, action, fields);
211
+ const dataLoaderGenerator = new dataloader_generator_1.DataLoaderGenerator(controller, action, fields);
210
212
  // 根据 dictionary 确定文件生成器的项目路径
211
213
  const projectPath = !dictionary || dictionary === "." ? "." : path_1.default.join(constants_1.destFolder, dictionary);
212
214
  const fileGenerator = new file_generator_1.FileGenerator(projectPath);
@@ -216,6 +218,11 @@ const generateDynamicFiles = (controller, action, paths, fields, dictionary) =>
216
218
  fs_1.default.writeFileSync(paths.destServerModulesResolver, resolverGenerator.generate());
217
219
  fs_1.default.writeFileSync(paths.destClientAction, serviceGenerator.generate());
218
220
  fs_1.default.writeFileSync(paths.destPagesAction, pageGenerator.generate());
221
+ // 生成 DataLoader 文件
222
+ const dataLoaderPath = (0, path_1.resolve)(`${projectPath}/server/dataloaders/${controller}-dataloader.ts`);
223
+ (0, utils_1.mkdirSync)(path_1.default.dirname(dataLoaderPath));
224
+ fs_1.default.writeFileSync(dataLoaderPath, dataLoaderGenerator.generate());
225
+ console.log(`🚀 已生成 DataLoader 文件: ${dataLoaderPath}`);
219
226
  // 生成多语言文件
220
227
  fileGenerator.generateI18nFiles(controller, action, fields);
221
228
  };
@@ -302,6 +309,8 @@ const createFiles = (controller, action, dictionary, fields) => {
302
309
  paths.destClientServiceController,
303
310
  paths.destClientStyledController,
304
311
  paths.destServerModulesController,
312
+ // 添加 DataLoader 目录
313
+ (0, path_1.resolve)(`${getDestPath(constants_1.destServerPath)}/dataloaders`),
305
314
  ];
306
315
  createDirectoryStructure(basePaths);
307
316
  console.log("Directory structure created");
@@ -0,0 +1,12 @@
1
+ import { BaseGenerator } from "./base-generator";
2
+ /**
3
+ * DataLoader生成器
4
+ * 自动生成对应的 DataLoader 文件
5
+ */
6
+ export declare class DataLoaderGenerator extends BaseGenerator {
7
+ generate(): string;
8
+ /**
9
+ * 生成外键 DataLoader
10
+ */
11
+ private generateForeignKeyLoaders;
12
+ }
@@ -0,0 +1,221 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DataLoaderGenerator = void 0;
4
+ const base_generator_1 = require("./base-generator");
5
+ /**
6
+ * DataLoader生成器
7
+ * 自动生成对应的 DataLoader 文件
8
+ */
9
+ class DataLoaderGenerator extends base_generator_1.BaseGenerator {
10
+ generate() {
11
+ const capitalizedController = this.getCapitalizedController();
12
+ const selectFields = this.fields.map((f) => f.name).join(", ");
13
+ // const searchableFields = this.getSearchableFields(); // 暂时注释掉未使用的变量
14
+ return `import DataLoader from 'dataloader';
15
+ import { executeQuery } from '../utils/common';
16
+
17
+ /**
18
+ * ${capitalizedController} DataLoader
19
+ * 针对 ${this.controller} 表的批量数据加载器,解决 N+1 查询问题
20
+ */
21
+ export class ${capitalizedController}DataLoader {
22
+ // 按 ID 批量加载 ${this.controller}
23
+ public readonly byId: DataLoader<number, any>;
24
+
25
+ // 按名称批量加载 ${this.controller}
26
+ public readonly byName: DataLoader<string, any>;
27
+
28
+ // 按名称模糊搜索 ${this.controller}
29
+ public readonly searchByName: DataLoader<string, any[]>;
30
+
31
+ constructor() {
32
+ // 按 ID 批量加载
33
+ this.byId = new DataLoader(
34
+ async (ids: readonly number[]) => {
35
+ try {
36
+ console.log(\`🔍 DataLoader: 批量加载 \${ids.length} 个 ${this.controller} by ID\`);
37
+
38
+ const placeholders = ids.map(() => '?').join(',');
39
+ const sql = \`SELECT ${selectFields} FROM ${this.controller} WHERE id IN (\${placeholders})\`;
40
+
41
+ const results = await executeQuery(sql, [...ids]);
42
+
43
+ // 确保返回顺序与输入 keys 一致,未找到的返回 null
44
+ return ids.map(id =>
45
+ results.find((row: any) => row.id === id) || null
46
+ );
47
+ } catch (error) {
48
+ console.error('DataLoader byId 批量加载失败:', error);
49
+ throw error;
50
+ }
51
+ },
52
+ {
53
+ cache: true,
54
+ maxBatchSize: 100,
55
+ batchScheduleFn: callback => setTimeout(callback, 10), // 10ms 内的请求合并
56
+ }
57
+ );
58
+
59
+ // 按名称批量加载
60
+ this.byName = new DataLoader(
61
+ async (names: readonly string[]) => {
62
+ try {
63
+ console.log(\`🔍 DataLoader: 批量加载 \${names.length} 个 ${this.controller} by name\`);
64
+
65
+ const placeholders = names.map(() => '?').join(',');
66
+ const sql = \`SELECT ${selectFields} FROM ${this.controller} WHERE name IN (\${placeholders})\`;
67
+
68
+ const results = await executeQuery(sql, [...names]);
69
+
70
+ // 确保返回顺序与输入 keys 一致
71
+ return names.map(name =>
72
+ results.find((row: any) => row.name === name) || null
73
+ );
74
+ } catch (error) {
75
+ console.error('DataLoader byName 批量加载失败:', error);
76
+ throw error;
77
+ }
78
+ },
79
+ {
80
+ cache: true,
81
+ maxBatchSize: 50,
82
+ batchScheduleFn: callback => setTimeout(callback, 10),
83
+ }
84
+ );
85
+
86
+ // 按名称模糊搜索(返回数组)
87
+ this.searchByName = new DataLoader(
88
+ async (searchTerms: readonly string[]) => {
89
+ try {
90
+ console.log(\`🔍 DataLoader: 批量搜索 \${searchTerms.length} 个关键词\`);
91
+
92
+ // 对于搜索,我们需要为每个搜索词执行独立的查询
93
+ const results = await Promise.all(
94
+ searchTerms.map(async (term) => {
95
+ const sql = 'SELECT ${selectFields} FROM ${this.controller} WHERE name LIKE ?';
96
+ return executeQuery(sql, [\`%\${term}%\`]);
97
+ })
98
+ );
99
+
100
+ return results;
101
+ } catch (error) {
102
+ console.error('DataLoader searchByName 批量搜索失败:', error);
103
+ throw error;
104
+ }
105
+ },
106
+ {
107
+ cache: true,
108
+ maxBatchSize: 20, // 搜索请求较少,降低批量大小
109
+ batchScheduleFn: callback => setTimeout(callback, 20), // 稍长的等待时间
110
+ }
111
+ );
112
+
113
+ ${this.generateForeignKeyLoaders()}
114
+ }
115
+
116
+ /**
117
+ * 清除所有缓存
118
+ */
119
+ clearAll(): void {
120
+ this.byId.clearAll();
121
+ this.byName.clearAll();
122
+ this.searchByName.clearAll();
123
+ console.log('🧹 ${capitalizedController} DataLoader 缓存已清空');
124
+ }
125
+
126
+ /**
127
+ * 清除特定 ID 的缓存
128
+ */
129
+ clearById(id: number): void {
130
+ this.byId.clear(id);
131
+ }
132
+
133
+ /**
134
+ * 清除特定名称的缓存
135
+ */
136
+ clearByName(name: string): void {
137
+ this.byName.clear(name);
138
+ }
139
+
140
+ /**
141
+ * 预加载数据到缓存
142
+ */
143
+ prime(id: number, data: any): void {
144
+ this.byId.prime(id, data);
145
+ if (data && data.name) {
146
+ this.byName.prime(data.name, data);
147
+ }
148
+ }
149
+
150
+ /**
151
+ * 获取缓存统计信息
152
+ */
153
+ getStats() {
154
+ return {
155
+ byId: {
156
+ cacheMap: this.byId.cacheMap?.size || 0,
157
+ name: '${capitalizedController}.byId'
158
+ },
159
+ byName: {
160
+ cacheMap: this.byName.cacheMap?.size || 0,
161
+ name: '${capitalizedController}.byName'
162
+ },
163
+ searchByName: {
164
+ cacheMap: this.searchByName.cacheMap?.size || 0,
165
+ name: '${capitalizedController}.searchByName'
166
+ }
167
+ };
168
+ }
169
+ }
170
+
171
+ /**
172
+ * 创建 ${capitalizedController} DataLoader 实例
173
+ */
174
+ export function create${capitalizedController}DataLoader(): ${capitalizedController}DataLoader {
175
+ return new ${capitalizedController}DataLoader();
176
+ }`;
177
+ }
178
+ /**
179
+ * 生成外键 DataLoader
180
+ */
181
+ generateForeignKeyLoaders() {
182
+ const foreignKeys = this.fields.filter((f) => f.name.endsWith("_id") && f.name !== "id");
183
+ if (foreignKeys.length === 0) {
184
+ return "";
185
+ }
186
+ return foreignKeys
187
+ .map((fk) => {
188
+ const relatedTable = fk.name.replace("_id", "");
189
+ const capitalizedRelated = relatedTable.charAt(0).toUpperCase() + relatedTable.slice(1);
190
+ return `
191
+ // 按 ${fk.name} 批量加载相关的 ${this.controller}
192
+ this.by${capitalizedRelated}Id = new DataLoader(
193
+ async (${fk.name}s: readonly number[]) => {
194
+ try {
195
+ console.log(\`🔍 DataLoader: 批量加载 \${${fk.name}s.length} 个 ${this.controller} by ${fk.name}\`);
196
+
197
+ const placeholders = ${fk.name}s.map(() => '?').join(',');
198
+ const sql = \`SELECT ${this.fields.map((f) => f.name).join(", ")} FROM ${this.controller} WHERE ${fk.name} IN (\${placeholders})\`;
199
+
200
+ const results = await executeQuery(sql, [...${fk.name}s]);
201
+
202
+ // 按外键分组
203
+ return ${fk.name}s.map(${fk.name} =>
204
+ results.filter((row: any) => row.${fk.name} === ${fk.name})
205
+ );
206
+ } catch (error) {
207
+ console.error('DataLoader by${capitalizedRelated}Id 批量加载失败:', error);
208
+ throw error;
209
+ }
210
+ },
211
+ {
212
+ cache: true,
213
+ maxBatchSize: 50,
214
+ batchScheduleFn: callback => setTimeout(callback, 10),
215
+ }
216
+ );`;
217
+ })
218
+ .join("\n");
219
+ }
220
+ }
221
+ exports.DataLoaderGenerator = DataLoaderGenerator;
@@ -10,5 +10,6 @@ export declare class ResolverGenerator extends BaseGenerator {
10
10
  private generateUpdateValidation;
11
11
  private generateUpdateValues;
12
12
  private generateBatchReturnObject;
13
- private generateBatchInsertValues;
13
+ private generateDataLoaderSearchLogic;
14
+ private generateNewRecordObject;
14
15
  }
@@ -44,34 +44,58 @@ module.exports = {
44
44
  }
45
45
  },
46
46
 
47
- // 根据ID获取${this.controller}
48
- ${this.controller}Get: async ({ id }) => {
47
+ // 根据ID获取${this.controller} - 使用 DataLoader 优化
48
+ ${this.controller}Get: async ({ id }, context) => {
49
49
  try {
50
50
  const validId = validateId(id);
51
51
 
52
- const sql = 'SELECT ${selectFields} FROM ${this.controller} WHERE id = ?';
53
- const values = [validId];
54
-
55
- console.log('根据ID查询${this.controller}:', { sql, values });
52
+ console.log('🚀 使用 DataLoader 根据ID查询${this.controller}:', { id: validId });
56
53
 
57
- const results = await executeQuery(sql, values);
54
+ // 使用 DataLoader 批量加载,自动去重和缓存
55
+ const result = await context.dataloaders.${this.controller}.byId.load(validId);
58
56
 
59
- if (results.length === 0) {
57
+ if (!result) {
60
58
  throw new Error(\`ID为 \${validId} 的${this.controller}不存在\`);
61
59
  }
62
60
 
63
- return results[0];
61
+ return result;
64
62
  } catch (error) {
65
63
  console.error('获取${this.controller}失败:', error.message);
66
64
  throw error;
67
65
  }
68
66
  },
69
67
 
70
- // 搜索${this.controller}(分页)
71
- ${this.controller}Search: async ({ page = 0, pageSize = 10, data = {} }) => {
68
+ // 批量获取${this.controller} - 新增方法,展示 DataLoader 批量能力
69
+ ${this.controller}BatchGet: async ({ ids }, context) => {
70
+ try {
71
+ if (!Array.isArray(ids) || ids.length === 0) {
72
+ throw new Error('ID列表不能为空');
73
+ }
74
+
75
+ // 验证所有ID
76
+ const validIds = ids.map(id => validateId(id));
77
+
78
+ console.log('🚀 使用 DataLoader 批量查询${this.controller}:', { ids: validIds });
79
+
80
+ // DataLoader 自动批量处理,一次查询获取所有数据
81
+ const results = await context.dataloaders.${this.controller}.byId.loadMany(validIds);
82
+
83
+ // 过滤掉 null 结果(未找到的记录)
84
+ return results.filter(result => result !== null && !(result instanceof Error));
85
+ } catch (error) {
86
+ console.error('批量获取${this.controller}失败:', error.message);
87
+ throw error;
88
+ }
89
+ },
90
+
91
+ // 搜索${this.controller}(分页)- 使用 DataLoader 优化搜索
92
+ ${this.controller}Search: async ({ page = 0, pageSize = 10, data = {} }, context) => {
72
93
  try {
73
94
  const { page: validPage, pageSize: validPageSize } = validatePagination(page, pageSize);
74
95
 
96
+ ${this.generateDataLoaderSearchLogic(searchableFields)}
97
+
98
+ // 原始查询方式(作为备用)
75
99
  const values = [];
76
100
  const countValues = [];
77
101
 
@@ -83,7 +107,7 @@ ${searchConditions}
83
107
 
84
108
  values.push(validPageSize, validPage * validPageSize);
85
109
 
86
- console.log('搜索${this.controller}:', { sql, values, countSql, countValues });
110
+ console.log('搜索${this.controller}(备用查询):', { sql, values, countSql, countValues });
87
111
 
88
112
  return await executePaginatedQuery(sql, countSql, values, countValues);
89
113
  } catch (error) {
@@ -92,8 +116,8 @@ ${searchConditions}
92
116
  }
93
117
  },
94
118
 
95
- // 添加${this.controller}
96
- ${this.controller}Add: async ({ data }) => {
119
+ // 添加${this.controller} - 添加 DataLoader 缓存预加载
120
+ ${this.controller}Add: async ({ data }, context) => {
97
121
  try {
98
122
  ${this.generateNewValidationCalls(insertFields)}
99
123
 
@@ -103,7 +127,16 @@ ${this.generateNewValidationCalls(insertFields)}
103
127
  console.log('添加${this.controller}:', { sql, values });
104
128
 
105
129
  const results = await executeQuery(sql, values);
106
- return results.insertId;
130
+ const insertId = results.insertId;
131
+
132
+ // 预加载新数据到 DataLoader 缓存
133
+ if (insertId && context?.dataloaders?.${this.controller}) {
134
+ const newRecord = { id: insertId, ${this.generateNewRecordObject(insertFields)} };
135
+ context.dataloaders.${this.controller}.prime(insertId, newRecord);
136
+ console.log('🚀 新${this.controller}已预加载到 DataLoader 缓存:', newRecord);
137
+ }
138
+
139
+ return insertId;
107
140
  } catch (error) {
108
141
  console.error('添加${this.controller}失败:', error.message);
109
142
  throw error;
@@ -129,7 +162,7 @@ ${this.generateBatchValidation(insertFields)}
129
162
 
130
163
  const placeholders = validatedDatas.map(() => '(${insertPlaceholders})').join(',');
131
164
  const sql = \`INSERT INTO ${this.controller} (${insertFieldNames}) VALUES \${placeholders}\`;
132
- const values = validatedDatas.flatMap(data => [${this.generateBatchInsertValues(insertFields)}]);
165
+ const values = validatedDatas.flatMap(data => [${insertFields.map((f) => `data.${f.name}`).join(", ")}]);
133
166
 
134
167
  console.log('批量添加${this.controller}:', { sql, values });
135
168
 
@@ -141,8 +174,8 @@ ${this.generateBatchValidation(insertFields)}
141
174
  }
142
175
  },
143
176
 
144
- // 更新${this.controller}
145
- ${this.controller}Update: async ({ id, data }) => {
177
+ // 更新${this.controller} - 添加 DataLoader 缓存清理
178
+ ${this.controller}Update: async ({ id, data }, context) => {
146
179
  try {
147
180
  const validId = validateId(id);
148
181
 
@@ -163,6 +196,12 @@ ${this.generateUpdateValidation(insertFields)}
163
196
  throw new Error(\`ID为 \${validId} 的${this.controller}不存在\`);
164
197
  }
165
198
 
199
+ // 清除 DataLoader 缓存,确保下次查询获取最新数据
200
+ if (context?.dataloaders?.${this.controller}) {
201
+ context.dataloaders.${this.controller}.clearById(validId);
202
+ console.log('🧹 已清除 DataLoader 缓存:', { id: validId });
203
+ }
204
+
166
205
  return true;
167
206
  } catch (error) {
168
207
  console.error('更新${this.controller}失败:', error.message);
@@ -170,8 +209,8 @@ ${this.generateUpdateValidation(insertFields)}
170
209
  }
171
210
  },
172
211
 
173
- // 删除${this.controller}
174
- ${this.controller}Delete: async ({ id }) => {
212
+ // 删除${this.controller} - 添加 DataLoader 缓存清理
213
+ ${this.controller}Delete: async ({ id }, context) => {
175
214
  try {
176
215
  const validId = validateId(id);
177
216
 
@@ -186,6 +225,12 @@ ${this.generateUpdateValidation(insertFields)}
186
225
  throw new Error(\`ID为 \${validId} 的${this.controller}不存在\`);
187
226
  }
188
227
 
228
+ // 清除 DataLoader 缓存
229
+ if (context?.dataloaders?.${this.controller}) {
230
+ context.dataloaders.${this.controller}.clearById(validId);
231
+ console.log('🧹 已清除 DataLoader 缓存:', { id: validId });
232
+ }
233
+
189
234
  return true;
190
235
  } catch (error) {
191
236
  console.error('删除${this.controller}失败:', error.message);
@@ -193,8 +238,8 @@ ${this.generateUpdateValidation(insertFields)}
193
238
  }
194
239
  },
195
240
 
196
- // 批量删除${this.controller}
197
- ${this.controller}BatchDelete: async ({ ids }) => {
241
+ // 批量删除${this.controller} - 添加 DataLoader 缓存清理
242
+ ${this.controller}BatchDelete: async ({ ids }, context) => {
198
243
  try {
199
244
  if (!Array.isArray(ids) || ids.length === 0) {
200
245
  throw new Error('批量删除的ID列表不能为空');
@@ -220,6 +265,14 @@ ${this.generateUpdateValidation(insertFields)}
220
265
  throw new Error('没有找到要删除的${this.controller}');
221
266
  }
222
267
 
268
+ // 批量清除 DataLoader 缓存
269
+ if (context?.dataloaders?.${this.controller}) {
270
+ validIds.forEach(id => {
271
+ context.dataloaders.${this.controller}.clearById(id);
272
+ });
273
+ console.log('🧹 已批量清除 DataLoader 缓存:', { ids: validIds });
274
+ }
275
+
223
276
  return true;
224
277
  } catch (error) {
225
278
  console.error('批量删除${this.controller}失败:', error.message);
@@ -335,8 +388,48 @@ ${this.generateUpdateValidation(insertFields)}
335
388
  })
336
389
  .join(", ");
337
390
  }
338
- generateBatchInsertValues(insertFields) {
339
- return insertFields.map((f) => `data.${f.name}`).join(", ");
391
+ // private generateBatchInsertValues(insertFields: any[]): string {
392
+ // return insertFields.map((f) => `data.${f.name}`).join(", ");
393
+ // }
394
+ generateDataLoaderSearchLogic(searchableFields) {
395
+ if (searchableFields.length === 0)
396
+ return "";
397
+ const nameField = searchableFields.find((f) => f.name === "name");
398
+ if (!nameField)
399
+ return "";
400
+ return `// 如果有名称搜索,尝试使用 DataLoader 搜索缓存
401
+ if (data.name && data.name.trim() !== '') {
402
+ console.log('🚀 使用 DataLoader 搜索${this.controller}:', { searchTerm: data.name.trim() });
403
+
404
+ try {
405
+ // 使用 DataLoader 进行搜索(这里会缓存搜索结果)
406
+ const searchResults = await context.dataloaders.${this.controller}.searchByName.load(data.name.trim());
407
+
408
+ // 手动分页处理
409
+ const totalCounts = searchResults.length;
410
+ const startIndex = validPage * validPageSize;
411
+ const endIndex = startIndex + validPageSize;
412
+ const items = searchResults.slice(startIndex, endIndex);
413
+
414
+ return {
415
+ totalCounts,
416
+ items
417
+ };
418
+ } catch (dataLoaderError) {
419
+ console.warn('DataLoader 搜索失败,回退到直接查询:', dataLoaderError.message);
420
+ // 如果 DataLoader 失败,回退到原始查询方式
421
+ }
422
+ }`;
423
+ }
424
+ generateNewRecordObject(insertFields) {
425
+ return insertFields
426
+ .map((f) => {
427
+ if (f.type === "integer") {
428
+ return `${f.name}: valid${f.name.charAt(0).toUpperCase() + f.name.slice(1)}`;
429
+ }
430
+ return `${f.name}: data.${f.name}`;
431
+ })
432
+ .join(", ");
340
433
  }
341
434
  }
342
435
  exports.ResolverGenerator = ResolverGenerator;
@@ -23,6 +23,7 @@ class SchemaGenerator extends base_generator_1.BaseGenerator {
23
23
  query: \`
24
24
  ${this.controller}(page: Int, pageSize: Int): ${pluralTypeName}
25
25
  ${this.controller}Get(id: Int): ${capitalizedTypeName}
26
+ ${this.controller}BatchGet(ids: [Int]): [${capitalizedTypeName}]
26
27
  ${this.controller}Search(page: Int, pageSize: Int, data: ${capitalizedTypeName}SearchInput): ${pluralTypeName}
27
28
  \`,
28
29
  mutation: \`
@@ -0,0 +1,38 @@
1
+ import { TemplateDataLoader } from "./template-dataloader";
2
+ /**
3
+ * DataLoader 上下文接口
4
+ */
5
+ export interface DataLoaderContext extends Record<string, unknown> {
6
+ dataloaders: {
7
+ template: TemplateDataLoader;
8
+ };
9
+ }
10
+ /**
11
+ * 创建 DataLoader 上下文
12
+ * 每个 GraphQL 请求都会创建新的 DataLoader 实例,确保请求隔离
13
+ */
14
+ export declare function createDataLoaderContext(): DataLoaderContext;
15
+ /**
16
+ * DataLoader 统计信息
17
+ */
18
+ export declare function getDataLoaderStats(context: DataLoaderContext): {
19
+ template: {
20
+ byId: {
21
+ cacheMap: any;
22
+ name: string;
23
+ };
24
+ byName: {
25
+ cacheMap: any;
26
+ name: string;
27
+ };
28
+ searchByName: {
29
+ cacheMap: any;
30
+ name: string;
31
+ };
32
+ };
33
+ timestamp: string;
34
+ };
35
+ /**
36
+ * 清除所有 DataLoader 缓存
37
+ */
38
+ export declare function clearAllDataLoaderCache(context: DataLoaderContext): void;
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createDataLoaderContext = createDataLoaderContext;
4
+ exports.getDataLoaderStats = getDataLoaderStats;
5
+ exports.clearAllDataLoaderCache = clearAllDataLoaderCache;
6
+ const template_dataloader_1 = require("./template-dataloader");
7
+ /**
8
+ * 创建 DataLoader 上下文
9
+ * 每个 GraphQL 请求都会创建新的 DataLoader 实例,确保请求隔离
10
+ */
11
+ function createDataLoaderContext() {
12
+ return {
13
+ dataloaders: {
14
+ template: (0, template_dataloader_1.createTemplateDataLoader)(),
15
+ },
16
+ };
17
+ }
18
+ /**
19
+ * DataLoader 统计信息
20
+ */
21
+ function getDataLoaderStats(context) {
22
+ return {
23
+ template: context.dataloaders.template.getStats(),
24
+ timestamp: new Date().toISOString(),
25
+ };
26
+ }
27
+ /**
28
+ * 清除所有 DataLoader 缓存
29
+ */
30
+ function clearAllDataLoaderCache(context) {
31
+ context.dataloaders.template.clearAll();
32
+ console.log("🧹 所有 DataLoader 缓存已清空");
33
+ }