@nest-omni/core 4.1.3-27 → 4.1.3-29
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/audit/interceptors/audit-action.interceptor.d.ts +1 -0
- package/audit/interceptors/audit-action.interceptor.js +5 -3
- package/audit/services/audit-action.service.d.ts +1 -0
- package/audit/services/audit-action.service.js +5 -3
- package/audit/services/operation-description.service.d.ts +1 -0
- package/audit/services/operation-description.service.js +5 -3
- package/audit/services/transaction-audit.service.d.ts +1 -0
- package/audit/services/transaction-audit.service.js +6 -4
- package/common/helpers/validation-metadata-helper.d.ts +57 -0
- package/common/helpers/validation-metadata-helper.js +109 -5
- package/decorators/examples/field-i18n.example.d.ts +294 -0
- package/decorators/examples/field-i18n.example.js +478 -0
- package/decorators/field.decorators.d.ts +23 -0
- package/decorators/field.decorators.js +7 -2
- package/decorators/translate.decorator.d.ts +26 -0
- package/decorators/translate.decorator.js +26 -1
- package/http-client/decorators/http-client.decorators.d.ts +1 -0
- package/http-client/decorators/http-client.decorators.js +47 -30
- package/http-client/http-client.module.d.ts +8 -0
- package/http-client/http-client.module.js +24 -24
- package/http-client/services/http-client.service.js +56 -11
- package/http-client/utils/context-extractor.util.d.ts +2 -0
- package/http-client/utils/context-extractor.util.js +15 -3
- package/interceptors/index.d.ts +0 -1
- package/interceptors/index.js +0 -1
- package/interceptors/translation-interceptor.service.d.ts +7 -0
- package/interceptors/translation-interceptor.service.js +40 -8
- package/package.json +1 -1
- package/setup/bootstrap.setup.js +1 -1
- package/shared/services/api-config.service.js +18 -3
- package/interceptors/http-logging-interceptor.service.d.ts +0 -38
- package/interceptors/http-logging-interceptor.service.js +0 -167
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 字段装饰器 i18n 翻译使用示例
|
|
3
|
+
*
|
|
4
|
+
* 本示例演示如何使用 fieldI18n 选项通过 i18n key 进行多语言转换
|
|
5
|
+
*
|
|
6
|
+
* 注意:此示例文件仅供参考,展示如何使用 fieldI18n 选项。
|
|
7
|
+
* 如果使用 TypeScript 5+ 的新装饰器语法,需要确保 tsconfig.json 中
|
|
8
|
+
* "experimentalDecorators" 设置为 true。
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* 在应用启动时(如 app.module.ts 或 main.ts)配置 i18n 翻译函数
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* // app.module.ts
|
|
15
|
+
* import { setI18nTranslate } from '@your-lib/core';
|
|
16
|
+
*
|
|
17
|
+
*@Module({
|
|
18
|
+
* imports: [
|
|
19
|
+
* ConfigModule.forRoot(),
|
|
20
|
+
* I18nModule.forRoot({
|
|
21
|
+
* fallbackLanguage: 'en',
|
|
22
|
+
* loader: TranslationsLoader({
|
|
23
|
+
* config: {
|
|
24
|
+
* defaultLanguage: 'zh',
|
|
25
|
+
* languages: ['zh', 'en'],
|
|
26
|
+
* types: ['validation', 'field'],
|
|
27
|
+
* },
|
|
28
|
+
* }),
|
|
29
|
+
* }),
|
|
30
|
+
* ],
|
|
31
|
+
* providers: [AppService],
|
|
32
|
+
* })
|
|
33
|
+
* export class AppModule implements OnModuleInit {
|
|
34
|
+
* constructor(private readonly i18n: I18nService) {}
|
|
35
|
+
*
|
|
36
|
+
* async onModuleInit() {
|
|
37
|
+
* // 设置全局 i18n 翻译函数
|
|
38
|
+
* setI18nTranslate((key, options) => this.i18n.translate(key, options));
|
|
39
|
+
* }
|
|
40
|
+
* }
|
|
41
|
+
*/
|
|
42
|
+
/**
|
|
43
|
+
* 翻译文件结构示例 (src/i18n/zh.json):
|
|
44
|
+
*
|
|
45
|
+
* {
|
|
46
|
+
* "field": {
|
|
47
|
+
* "user": {
|
|
48
|
+
* "username": "用户名",
|
|
49
|
+
* "email": "邮箱",
|
|
50
|
+
* "phone": "手机号",
|
|
51
|
+
* "role": "角色",
|
|
52
|
+
* "status": "状态",
|
|
53
|
+
* "description": "个人简介"
|
|
54
|
+
* },
|
|
55
|
+
* "product": {
|
|
56
|
+
* "name": "产品名称",
|
|
57
|
+
* "price": "价格",
|
|
58
|
+
* "stock": "库存",
|
|
59
|
+
* "status": "产品状态"
|
|
60
|
+
* }
|
|
61
|
+
* },
|
|
62
|
+
* "field.value": {
|
|
63
|
+
* "user.role": {
|
|
64
|
+
* "user": "普通用户",
|
|
65
|
+
* "admin": "管理员",
|
|
66
|
+
* "vip": "VIP用户"
|
|
67
|
+
* },
|
|
68
|
+
* "user.status": {
|
|
69
|
+
* "active": "正常",
|
|
70
|
+
* "inactive": "未激活",
|
|
71
|
+
* "suspended": "已暂停"
|
|
72
|
+
* }
|
|
73
|
+
* }
|
|
74
|
+
* }
|
|
75
|
+
*
|
|
76
|
+
* 翻译文件结构示例 (src/i18n/en.json):
|
|
77
|
+
*
|
|
78
|
+
* {
|
|
79
|
+
* "field": {
|
|
80
|
+
* "user": {
|
|
81
|
+
* "username": "Username",
|
|
82
|
+
* "email": "Email",
|
|
83
|
+
* "phone": "Phone Number",
|
|
84
|
+
* "role": "Role",
|
|
85
|
+
* "status": "Status",
|
|
86
|
+
* "description": "Bio"
|
|
87
|
+
* },
|
|
88
|
+
* "product": {
|
|
89
|
+
* "name": "Product Name",
|
|
90
|
+
* "price": "Price",
|
|
91
|
+
* "stock": "Stock",
|
|
92
|
+
* "status": "Product Status"
|
|
93
|
+
* }
|
|
94
|
+
* },
|
|
95
|
+
* "field.value": {
|
|
96
|
+
* "user.role": {
|
|
97
|
+
* "user": "User",
|
|
98
|
+
* "admin": "Administrator",
|
|
99
|
+
* "vip": "VIP User"
|
|
100
|
+
* },
|
|
101
|
+
* "user.status": {
|
|
102
|
+
* "active": "Active",
|
|
103
|
+
* "inactive": "Inactive",
|
|
104
|
+
* "suspended": "Suspended"
|
|
105
|
+
* }
|
|
106
|
+
* }
|
|
107
|
+
* }
|
|
108
|
+
*/
|
|
109
|
+
/**
|
|
110
|
+
* 用户 DTO - 使用 i18n key
|
|
111
|
+
*/
|
|
112
|
+
export declare class CreateUserDto {
|
|
113
|
+
username: string;
|
|
114
|
+
email: string;
|
|
115
|
+
phone?: string;
|
|
116
|
+
description?: string;
|
|
117
|
+
emailVerified?: boolean;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* 产品 DTO - 使用 i18n key
|
|
121
|
+
*/
|
|
122
|
+
export declare class CreateProductDto {
|
|
123
|
+
name: string;
|
|
124
|
+
price: number;
|
|
125
|
+
stock: number;
|
|
126
|
+
}
|
|
127
|
+
declare enum UserRole {
|
|
128
|
+
USER = "user",
|
|
129
|
+
ADMIN = "admin",
|
|
130
|
+
VIP = "vip"
|
|
131
|
+
}
|
|
132
|
+
declare enum UserStatus {
|
|
133
|
+
ACTIVE = "active",
|
|
134
|
+
INACTIVE = "inactive",
|
|
135
|
+
SUSPENDED = "suspended"
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* 用户更新 DTO - 枚举字段使用 i18n
|
|
139
|
+
*/
|
|
140
|
+
export declare class UpdateUserDto {
|
|
141
|
+
role: UserRole;
|
|
142
|
+
status: UserStatus;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* 验证错误过滤器示例
|
|
146
|
+
*
|
|
147
|
+
* @example
|
|
148
|
+
* import { getValidationMetadata, getFieldLabelForValidation } from '@your-lib/core';
|
|
149
|
+
*
|
|
150
|
+
* @Catch(ValidationError)
|
|
151
|
+
* export class ValidationErrorFilter implements ExceptionFilter {
|
|
152
|
+
* catch(exception: ValidationError, host: ArgumentsHost) {
|
|
153
|
+
* const ctx = host.switchToHttp();
|
|
154
|
+
* const response = ctx.getResponse<Response>();
|
|
155
|
+
* const request = ctx.getRequest<Request>();
|
|
156
|
+
* const language = request.headers['accept-language'] || 'zh';
|
|
157
|
+
*
|
|
158
|
+
* const errors = exception.errors.map(error => {
|
|
159
|
+
* const metadata = getValidationMetadata(error.target.constructor, error.property);
|
|
160
|
+
* const fieldLabel = getFieldLabelForValidation(metadata, language);
|
|
161
|
+
*
|
|
162
|
+
* // 如果 fieldLabel 是 i18n key,需要翻译
|
|
163
|
+
* const displayLabel = fieldLabel.includes('.')
|
|
164
|
+
* ? this.i18n.translate(fieldLabel, { lang: language })
|
|
165
|
+
* : fieldLabel;
|
|
166
|
+
*
|
|
167
|
+
* return {
|
|
168
|
+
* field: error.property,
|
|
169
|
+
* fieldLabel: displayLabel,
|
|
170
|
+
* constraints: error.constraints,
|
|
171
|
+
* };
|
|
172
|
+
* });
|
|
173
|
+
*
|
|
174
|
+
* response.status(400).json({
|
|
175
|
+
* statusCode: 400,
|
|
176
|
+
* message: 'Validation failed',
|
|
177
|
+
* errors,
|
|
178
|
+
* });
|
|
179
|
+
* }
|
|
180
|
+
* }
|
|
181
|
+
*/
|
|
182
|
+
/**
|
|
183
|
+
* 审计日志服务示例
|
|
184
|
+
*
|
|
185
|
+
* @example
|
|
186
|
+
* import { getAuditMetadata } from '@your-lib/core';
|
|
187
|
+
*
|
|
188
|
+
* @Injectable()
|
|
189
|
+
* export class AuditService {
|
|
190
|
+
* constructor(private readonly i18n: I18nService) {}
|
|
191
|
+
*
|
|
192
|
+
* async logFieldChange(
|
|
193
|
+
* entity: any,
|
|
194
|
+
* field: string,
|
|
195
|
+
* oldValue: any,
|
|
196
|
+
* newValue: any,
|
|
197
|
+
* language: string = 'zh',
|
|
198
|
+
* ) {
|
|
199
|
+
* const metadata = getAuditMetadata(entity.constructor, field);
|
|
200
|
+
*
|
|
201
|
+
* // 翻译字段标签
|
|
202
|
+
* let fieldLabel = metadata?.label || field;
|
|
203
|
+
* if (typeof fieldLabel === 'string' && fieldLabel.includes('.')) {
|
|
204
|
+
* fieldLabel = await this.i18n.translate(fieldLabel, { lang: language });
|
|
205
|
+
* } else if (typeof fieldLabel === 'object') {
|
|
206
|
+
* fieldLabel = fieldLabel[language] || fieldLabel.zh || fieldLabel.en;
|
|
207
|
+
* }
|
|
208
|
+
*
|
|
209
|
+
* // 翻译枚举值
|
|
210
|
+
* const valueLabels = metadata?.valueLabels;
|
|
211
|
+
* const displayOldValue = valueLabels?.[oldValue]
|
|
212
|
+
* ? await this.translateValueLabel(valueLabels[oldValue], language)
|
|
213
|
+
* : oldValue;
|
|
214
|
+
* const displayNewValue = valueLabels?.[newValue]
|
|
215
|
+
* ? await this.translateValueLabel(valueLabels[newValue], language)
|
|
216
|
+
* : newValue;
|
|
217
|
+
*
|
|
218
|
+
* return {
|
|
219
|
+
* fieldLabel,
|
|
220
|
+
* oldValue,
|
|
221
|
+
* newValue,
|
|
222
|
+
* displayOldValue,
|
|
223
|
+
* displayNewValue,
|
|
224
|
+
* };
|
|
225
|
+
* }
|
|
226
|
+
*
|
|
227
|
+
* private async translateValueLabel(
|
|
228
|
+
* label: string | { [lang: string]: string },
|
|
229
|
+
* language: string,
|
|
230
|
+
* ): Promise<string> {
|
|
231
|
+
* if (typeof label === 'string' && label.includes('.')) {
|
|
232
|
+
* return await this.i18n.translate(label, { lang: language });
|
|
233
|
+
* }
|
|
234
|
+
* if (typeof label === 'object') {
|
|
235
|
+
* return label[language] || label.zh || label.en || '';
|
|
236
|
+
* }
|
|
237
|
+
* return label;
|
|
238
|
+
* }
|
|
239
|
+
* }
|
|
240
|
+
*/
|
|
241
|
+
/**
|
|
242
|
+
* 场景 1: 纯 i18n key 方式(推荐)
|
|
243
|
+
*
|
|
244
|
+
* 优点:
|
|
245
|
+
* - 翻译集中管理,易于维护
|
|
246
|
+
* - 支持动态切换语言
|
|
247
|
+
* - 翻译可以复用
|
|
248
|
+
* - 适合大型项目和国际化需求
|
|
249
|
+
*
|
|
250
|
+
* 缺点:
|
|
251
|
+
* - 需要额外的翻译文件配置
|
|
252
|
+
* - 增加了一定的复杂度
|
|
253
|
+
*/
|
|
254
|
+
export declare class PureI18nDto {
|
|
255
|
+
username: string;
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* 场景 2: 纯多语言对象方式
|
|
259
|
+
*
|
|
260
|
+
* 优点:
|
|
261
|
+
* - 简单直接,无需额外配置
|
|
262
|
+
* - 适合小型项目或固定语言场景
|
|
263
|
+
* - 翻译就在代码中,查看方便
|
|
264
|
+
*
|
|
265
|
+
* 缺点:
|
|
266
|
+
* - 翻译分散在各个 DTO 中,难以统一管理
|
|
267
|
+
* - 不易于动态切换语言
|
|
268
|
+
* - 翻译无法复用
|
|
269
|
+
*/
|
|
270
|
+
export declare class PureLabelDto {
|
|
271
|
+
username: string;
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* 场景 3: 混合方式(fieldLabel 优先)
|
|
275
|
+
*
|
|
276
|
+
* 优点:
|
|
277
|
+
* - 灵活性最高
|
|
278
|
+
* - 可以为特定字段覆盖 i18n 翻译
|
|
279
|
+
*
|
|
280
|
+
* 缺点:
|
|
281
|
+
* - 可能造成混淆
|
|
282
|
+
* - 建议统一使用一种方式
|
|
283
|
+
*/
|
|
284
|
+
export declare class MixedDto {
|
|
285
|
+
username: string;
|
|
286
|
+
}
|
|
287
|
+
export declare class RegisterUserDto {
|
|
288
|
+
username: string;
|
|
289
|
+
email: string;
|
|
290
|
+
password: string;
|
|
291
|
+
phone?: string;
|
|
292
|
+
agreeTerms?: boolean;
|
|
293
|
+
}
|
|
294
|
+
export {};
|