nsgm-cli 2.1.39 → 2.1.40

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.
@@ -62,6 +62,25 @@ export const formatDate = (date: Date | string, locale = "zh-CN") => {
62
62
  }).format(dateObj);
63
63
  };
64
64
 
65
+ // Format date and time as YYYY-MM-DD HH:mm:ss
66
+ export const formatDateTime = (date: Date | string) => {
67
+ const dateObj = typeof date === "string" ? new Date(date) : date;
68
+
69
+ // Check if date is valid
70
+ if (isNaN(dateObj.getTime())) {
71
+ return "";
72
+ }
73
+
74
+ const year = dateObj.getFullYear();
75
+ const month = String(dateObj.getMonth() + 1).padStart(2, "0");
76
+ const day = String(dateObj.getDate()).padStart(2, "0");
77
+ const hours = String(dateObj.getHours()).padStart(2, "0");
78
+ const minutes = String(dateObj.getMinutes()).padStart(2, "0");
79
+ const seconds = String(dateObj.getSeconds()).padStart(2, "0");
80
+
81
+ return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
82
+ };
83
+
65
84
  // Format number based on locale
66
85
  export const formatNumber = (number: number, locale = "zh-CN") => {
67
86
  return new Intl.NumberFormat(locale).format(number);
@@ -218,8 +218,8 @@ const generateDynamicFiles = (controller, action, paths, fields, dictionary) =>
218
218
  fs_1.default.writeFileSync(paths.destServerModulesResolver, resolverGenerator.generate());
219
219
  fs_1.default.writeFileSync(paths.destClientAction, serviceGenerator.generate());
220
220
  fs_1.default.writeFileSync(paths.destPagesAction, pageGenerator.generate());
221
- // 生成 DataLoader 文件
222
- const dataLoaderPath = (0, path_1.resolve)(`${projectPath}/server/dataloaders/${controller}-dataloader.ts`);
221
+ // 生成 DataLoader 文件 (JavaScript)
222
+ const dataLoaderPath = (0, path_1.resolve)(`${projectPath}/server/dataloaders/${controller}-dataloader.js`);
223
223
  (0, utils_1.mkdirSync)(path_1.default.dirname(dataLoaderPath));
224
224
  fs_1.default.writeFileSync(dataLoaderPath, dataLoaderGenerator.generate());
225
225
  console.log(`🚀 已生成 DataLoader 文件: ${dataLoaderPath}`);
@@ -1,7 +1,7 @@
1
1
  import { BaseGenerator } from "./base-generator";
2
2
  /**
3
3
  * DataLoader生成器
4
- * 自动生成对应的 DataLoader 文件
4
+ * 自动生成对应的 DataLoader JavaScript 文件
5
5
  */
6
6
  export declare class DataLoaderGenerator extends BaseGenerator {
7
7
  generate(): string;
@@ -4,34 +4,24 @@ exports.DataLoaderGenerator = void 0;
4
4
  const base_generator_1 = require("./base-generator");
5
5
  /**
6
6
  * DataLoader生成器
7
- * 自动生成对应的 DataLoader 文件
7
+ * 自动生成对应的 DataLoader JavaScript 文件
8
8
  */
9
9
  class DataLoaderGenerator extends base_generator_1.BaseGenerator {
10
10
  generate() {
11
11
  const capitalizedController = this.getCapitalizedController();
12
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';
13
+ return `const DataLoader = require('dataloader');
14
+ const { executeQuery } = require('../utils/common');
16
15
 
17
16
  /**
18
17
  * ${capitalizedController} DataLoader
19
18
  * 针对 ${this.controller} 表的批量数据加载器,解决 N+1 查询问题
20
19
  */
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
-
20
+ class ${capitalizedController}DataLoader {
31
21
  constructor() {
32
22
  // 按 ID 批量加载
33
23
  this.byId = new DataLoader(
34
- async (ids: readonly number[]) => {
24
+ async (ids) => {
35
25
  try {
36
26
  console.log(\`🔍 DataLoader: 批量加载 \${ids.length} 个 ${this.controller} by ID\`);
37
27
 
@@ -42,7 +32,7 @@ export class ${capitalizedController}DataLoader {
42
32
 
43
33
  // 确保返回顺序与输入 keys 一致,未找到的返回 null
44
34
  return ids.map(id =>
45
- results.find((row: any) => row.id === id) || null
35
+ results.find((row) => row.id === id) || null
46
36
  );
47
37
  } catch (error) {
48
38
  console.error('DataLoader byId 批量加载失败:', error);
@@ -58,7 +48,7 @@ export class ${capitalizedController}DataLoader {
58
48
 
59
49
  // 按名称批量加载
60
50
  this.byName = new DataLoader(
61
- async (names: readonly string[]) => {
51
+ async (names) => {
62
52
  try {
63
53
  console.log(\`🔍 DataLoader: 批量加载 \${names.length} 个 ${this.controller} by name\`);
64
54
 
@@ -69,7 +59,7 @@ export class ${capitalizedController}DataLoader {
69
59
 
70
60
  // 确保返回顺序与输入 keys 一致
71
61
  return names.map(name =>
72
- results.find((row: any) => row.name === name) || null
62
+ results.find((row) => row.name === name) || null
73
63
  );
74
64
  } catch (error) {
75
65
  console.error('DataLoader byName 批量加载失败:', error);
@@ -85,7 +75,7 @@ export class ${capitalizedController}DataLoader {
85
75
 
86
76
  // 按名称模糊搜索(返回数组)
87
77
  this.searchByName = new DataLoader(
88
- async (searchTerms: readonly string[]) => {
78
+ async (searchTerms) => {
89
79
  try {
90
80
  console.log(\`🔍 DataLoader: 批量搜索 \${searchTerms.length} 个关键词\`);
91
81
 
@@ -116,7 +106,7 @@ export class ${capitalizedController}DataLoader {
116
106
  /**
117
107
  * 清除所有缓存
118
108
  */
119
- clearAll(): void {
109
+ clearAll() {
120
110
  this.byId.clearAll();
121
111
  this.byName.clearAll();
122
112
  this.searchByName.clearAll();
@@ -126,21 +116,21 @@ export class ${capitalizedController}DataLoader {
126
116
  /**
127
117
  * 清除特定 ID 的缓存
128
118
  */
129
- clearById(id: number): void {
119
+ clearById(id) {
130
120
  this.byId.clear(id);
131
121
  }
132
122
 
133
123
  /**
134
124
  * 清除特定名称的缓存
135
125
  */
136
- clearByName(name: string): void {
126
+ clearByName(name) {
137
127
  this.byName.clear(name);
138
128
  }
139
129
 
140
130
  /**
141
131
  * 预加载数据到缓存
142
132
  */
143
- prime(id: number, data: any): void {
133
+ prime(id, data) {
144
134
  this.byId.prime(id, data);
145
135
  if (data && data.name) {
146
136
  this.byName.prime(data.name, data);
@@ -171,9 +161,11 @@ export class ${capitalizedController}DataLoader {
171
161
  /**
172
162
  * 创建 ${capitalizedController} DataLoader 实例
173
163
  */
174
- export function create${capitalizedController}DataLoader(): ${capitalizedController}DataLoader {
164
+ function create${capitalizedController}DataLoader() {
175
165
  return new ${capitalizedController}DataLoader();
176
- }`;
166
+ }
167
+
168
+ module.exports = { ${capitalizedController}DataLoader, create${capitalizedController}DataLoader };`;
177
169
  }
178
170
  /**
179
171
  * 生成外键 DataLoader
@@ -190,7 +182,7 @@ export function create${capitalizedController}DataLoader(): ${capitalizedControl
190
182
  return `
191
183
  // 按 ${fk.name} 批量加载相关的 ${this.controller}
192
184
  this.by${capitalizedRelated}Id = new DataLoader(
193
- async (${fk.name}s: readonly number[]) => {
185
+ async (${fk.name}s) => {
194
186
  try {
195
187
  console.log(\`🔍 DataLoader: 批量加载 \${${fk.name}s.length} 个 ${this.controller} by ${fk.name}\`);
196
188
 
@@ -201,7 +193,7 @@ export function create${capitalizedController}DataLoader(): ${capitalizedControl
201
193
 
202
194
  // 按外键分组
203
195
  return ${fk.name}s.map(${fk.name} =>
204
- results.filter((row: any) => row.${fk.name} === ${fk.name})
196
+ results.filter((row) => row.${fk.name} === ${fk.name})
205
197
  );
206
198
  } catch (error) {
207
199
  console.error('DataLoader by${capitalizedRelated}Id 批量加载失败:', error);
@@ -48,7 +48,7 @@ import { get${capitalizedController}Service } from '@/service/${this.controller}
48
48
  import { RootState, AppDispatch } from '@/redux/store'
49
49
  import _ from 'lodash'
50
50
  import { useTranslation } from 'next-i18next'
51
- import { getAntdLocale } from '@/utils/i18n'
51
+ import { getAntdLocale, formatDateTime } from '@/utils/i18n'
52
52
  import { useRouter } from 'next/router'
53
53
  import { handleXSS, checkModalObj } from '@/utils/common'
54
54
  import { UploadOutlined } from '@ant-design/icons'
@@ -501,7 +501,7 @@ export default Page`;
501
501
  }
502
502
  // 根据字段类型设置特定属性
503
503
  if (field.type === "timestamp" || field.type === "date" || field.type === "datetime") {
504
- column += `,\n render: (text: string) => text ? new Date(text).toLocaleString() : '-'`;
504
+ column += `,\n render: (text: string) => text ? formatDateTime(text) : '-'`;
505
505
  }
506
506
  else if (field.type === "integer" || field.type === "decimal") {
507
507
  column += `,\n align: 'center' as const`;
@@ -1,11 +1,8 @@
1
- import { TemplateDataLoader } from "./template-dataloader";
2
1
  /**
3
2
  * DataLoader 上下文接口
4
3
  */
5
4
  export interface DataLoaderContext extends Record<string, unknown> {
6
- dataloaders: {
7
- template: TemplateDataLoader;
8
- };
5
+ dataloaders: Record<string, any>;
9
6
  }
10
7
  /**
11
8
  * 创建 DataLoader 上下文
@@ -15,23 +12,7 @@ export declare function createDataLoaderContext(): DataLoaderContext;
15
12
  /**
16
13
  * DataLoader 统计信息
17
14
  */
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
- };
15
+ export declare function getDataLoaderStats(context: DataLoaderContext): Record<string, any>;
35
16
  /**
36
17
  * 清除所有 DataLoader 缓存
37
18
  */
@@ -1,16 +1,79 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.createDataLoaderContext = createDataLoaderContext;
4
7
  exports.getDataLoaderStats = getDataLoaderStats;
5
8
  exports.clearAllDataLoaderCache = clearAllDataLoaderCache;
9
+ const fs_1 = __importDefault(require("fs"));
10
+ const path_1 = require("path");
6
11
  const template_dataloader_1 = require("./template-dataloader");
12
+ /**
13
+ * 加载项目中的自定义 DataLoader
14
+ * 只加载编译后的 JavaScript 文件 (*.js)
15
+ */
16
+ function loadProjectDataLoaders() {
17
+ const projectDataLoadersPath = (0, path_1.join)(process.cwd(), "server", "dataloaders");
18
+ const dataLoaders = {};
19
+ if (!fs_1.default.existsSync(projectDataLoadersPath)) {
20
+ return dataLoaders;
21
+ }
22
+ try {
23
+ const files = fs_1.default.readdirSync(projectDataLoadersPath);
24
+ let loadedCount = 0;
25
+ files.forEach((file) => {
26
+ // 只加载 JavaScript dataloader 文件:*-dataloader.js
27
+ const match = file.match(/^([a-z_]+)-?_dataloader\.js$/i);
28
+ if (match) {
29
+ const moduleName = match[1]; // 提取模块名,如 'product'
30
+ const filePath = (0, path_1.join)(projectDataLoadersPath, file);
31
+ try {
32
+ // 清除 require 缓存,确保每次都能重新加载
33
+ delete require.cache[require.resolve(filePath)];
34
+ // 加载 JavaScript DataLoader 文件
35
+ const loaderModule = require(filePath);
36
+ // 获取 DataLoader 类的构造函数或工厂函数
37
+ const DataLoaderClass = loaderModule.default || loaderModule;
38
+ if (typeof DataLoaderClass === "function") {
39
+ const instance = new DataLoaderClass();
40
+ dataLoaders[moduleName] = instance;
41
+ loadedCount++;
42
+ }
43
+ else {
44
+ // 尝试工厂函数
45
+ const factoryName = `create${moduleName.charAt(0).toUpperCase() + moduleName.slice(1)}DataLoader`;
46
+ const createFunc = loaderModule[factoryName] || loaderModule.createProductDataLoader;
47
+ if (typeof createFunc === "function") {
48
+ dataLoaders[moduleName] = createFunc();
49
+ loadedCount++;
50
+ }
51
+ }
52
+ }
53
+ catch (error) {
54
+ console.warn(`⚠️ 加载 DataLoader 失败: ${file}`, error.message);
55
+ }
56
+ }
57
+ });
58
+ if (loadedCount > 0) {
59
+ console.log(`📦 共加载 ${loadedCount} 个自定义 DataLoader: ${Object.keys(dataLoaders).join(", ")}`);
60
+ }
61
+ return dataLoaders;
62
+ }
63
+ catch (error) {
64
+ console.warn("⚠️ 加载项目 DataLoader 时出错:", error);
65
+ return dataLoaders;
66
+ }
67
+ }
7
68
  /**
8
69
  * 创建 DataLoader 上下文
9
70
  * 每个 GraphQL 请求都会创建新的 DataLoader 实例,确保请求隔离
10
71
  */
11
72
  function createDataLoaderContext() {
73
+ const projectDataLoaders = loadProjectDataLoaders();
12
74
  return {
13
75
  dataloaders: {
76
+ ...projectDataLoaders,
14
77
  template: (0, template_dataloader_1.createTemplateDataLoader)(),
15
78
  },
16
79
  };
@@ -19,15 +82,33 @@ function createDataLoaderContext() {
19
82
  * DataLoader 统计信息
20
83
  */
21
84
  function getDataLoaderStats(context) {
22
- return {
23
- template: context.dataloaders.template.getStats(),
85
+ const stats = {
24
86
  timestamp: new Date().toISOString(),
25
87
  };
88
+ if (context.dataloaders) {
89
+ Object.keys(context.dataloaders).forEach((key) => {
90
+ const loader = context.dataloaders[key];
91
+ if (loader && typeof loader.getStats === "function") {
92
+ stats[key] = loader.getStats();
93
+ }
94
+ else {
95
+ stats[key] = { name: key, status: "no stats" };
96
+ }
97
+ });
98
+ }
99
+ return stats;
26
100
  }
27
101
  /**
28
102
  * 清除所有 DataLoader 缓存
29
103
  */
30
104
  function clearAllDataLoaderCache(context) {
31
- context.dataloaders.template.clearAll();
105
+ if (context.dataloaders) {
106
+ Object.keys(context.dataloaders).forEach((key) => {
107
+ const loader = context.dataloaders[key];
108
+ if (loader && typeof loader.clearAll === "function") {
109
+ loader.clearAll();
110
+ }
111
+ });
112
+ }
32
113
  console.log("🧹 所有 DataLoader 缓存已清空");
33
114
  }
@@ -19,23 +19,7 @@ export declare const dataLoaderStatsResolver: {
19
19
  };
20
20
  loaders: any[];
21
21
  recommendations: string[];
22
- contextStats: {
23
- template: {
24
- byId: {
25
- cacheMap: any;
26
- name: string;
27
- };
28
- byName: {
29
- cacheMap: any;
30
- name: string;
31
- };
32
- searchByName: {
33
- cacheMap: any;
34
- name: string;
35
- };
36
- };
37
- timestamp: string;
38
- } | null;
22
+ contextStats: Record<string, any> | null;
39
23
  timestamp: string;
40
24
  }>;
41
25
  resetDataLoaderStats: () => Promise<{
@@ -67,21 +67,5 @@ export declare function getDataLoaderHealth(context?: DataLoaderContext): {
67
67
  };
68
68
  loaders: any[];
69
69
  recommendations: string[];
70
- contextStats: {
71
- template: {
72
- byId: {
73
- cacheMap: any;
74
- name: string;
75
- };
76
- byName: {
77
- cacheMap: any;
78
- name: string;
79
- };
80
- searchByName: {
81
- cacheMap: any;
82
- name: string;
83
- };
84
- };
85
- timestamp: string;
86
- } | null;
70
+ contextStats: Record<string, any> | null;
87
71
  };