feat: WWJCloud 企业级全栈框架 v0.3.5 完整更新

🚀 核心更新:
-  完善 NestJS 企业级架构设计
-  优化配置中心和基础设施层
-  增强第三方服务集成能力
-  完善多租户架构支持
- 🎯 对标 Java Spring Boot 和 PHP ThinkPHP

📦 新增文件:
- wwjcloud-nest 完整框架结构
- Docker 容器化配置
- 管理后台界面
- 数据库迁移脚本

🔑 Key: ebb38b43ec39f355f071294fd1cf9c42
This commit is contained in:
wanwu
2025-10-13 01:27:37 +08:00
parent 10bcd7f624
commit 1ed0085d15
1697 changed files with 260750 additions and 127 deletions

View File

@@ -0,0 +1,385 @@
import { Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { promises as fs } from 'fs';
import * as path from 'path';
import { ILanguageService } from './language.interface';
/**
* 语言服务
* 基于 NestJS 实现模块化多语言支持
* 符合NestJS规范的服务层实现
* 对应PHP: think\Lang 类
* 对应Java: MessageSource 实现
*/
@Injectable()
export class LanguageService implements ILanguageService {
private readonly logger = new Logger(LanguageService.name);
private readonly languageCache = new Map<string, Map<string, any>>();
private readonly moduleCache = new Map<string, Set<string>>(); // 记录已加载的模块
constructor(private readonly configService: ConfigService) {
this.initializeLanguagePacks();
}
/**
* 获取消息
* @param key 消息键
* @param args 参数
* @param module 模块名
* @param type 类型 (api|dict|validate)
* @param language 语言
* @returns 消息内容
*/
async getMessage(key: string, args?: any, module: string = 'common', type: string = 'api', language?: string): Promise<string> {
try {
const currentLanguage = language || this.getCurrentLanguage();
const cacheKey = `${type}.${module}.${key}`;
// 确保模块已加载
await this.ensureModuleLoaded(module, type, currentLanguage);
const message = this.getMessageFromCache(cacheKey, currentLanguage);
if (message && message !== key) {
// 支持参数替换
if (args && typeof args === 'object') {
return this.replaceMessageParams(message, args);
}
return message;
}
this.logger.warn(`未找到消息: ${key} (模块: ${module}, 类型: ${type}, 语言: ${currentLanguage})`);
return key;
} catch (error) {
this.logger.warn(`获取消息失败: ${key}`, error);
return key;
}
}
/**
* 获取消息(实例方法)
* @param key 消息键
* @param args 参数
* @param module 模块名
* @param type 类型
* @param language 语言
* @returns 消息内容
*/
async get(key: string, args?: any, module: string = 'common', type: string = 'api', language?: string): Promise<string> {
return await this.getMessage(key, args, module, type, language);
}
/**
* 初始化语言包
* 只加载通用语言包,其他模块按需加载
*/
private async initializeLanguagePacks(): Promise<void> {
try {
const supportedLanguages = this.getSupportedLanguages();
for (const language of supportedLanguages) {
// 只加载通用模块
await this.loadModuleLanguagePack('common', 'api', language);
await this.loadModuleLanguagePack('common', 'dict', language);
await this.loadModuleLanguagePack('common', 'validate', language);
}
this.logger.log('通用语言包初始化完成');
} catch (error) {
this.logger.error('语言包初始化失败', error);
}
}
/**
* 确保模块已加载
* @param module 模块名
* @param type 类型
* @param language 语言
*/
private async ensureModuleLoaded(module: string, type: string, language: string): Promise<void> {
const moduleKey = `${module}.${type}`;
const languageKey = `${language}.${moduleKey}`;
if (!this.moduleCache.has(language) || !this.moduleCache.get(language)!.has(moduleKey)) {
await this.loadModuleLanguagePack(module, type, language);
}
}
/**
* 加载模块语言包
* @param module 模块名
* @param type 类型 (api|dict|validate)
* @param language 语言
*/
private async loadModuleLanguagePack(module: string, type: string, language: string): Promise<void> {
try {
const langDir = path.join(process.cwd(), 'src', 'lang', language);
const languagePack = new Map<string, any>();
// 1. 加载通用语言包
if (module !== 'common') {
await this.loadCommonLanguagePack(langDir, type, languagePack);
}
// 2. 加载模块语言包
await this.loadModuleSpecificPack(langDir, module, type, languagePack);
// 3. 加载插件语言包
await this.loadAddonLanguagePacks(langDir, type, languagePack);
// 4. 缓存语言包
if (!this.languageCache.has(language)) {
this.languageCache.set(language, new Map());
}
const languageCache = this.languageCache.get(language)!;
for (const [key, value] of languagePack) {
languageCache.set(key, value);
}
// 5. 记录已加载的模块
if (!this.moduleCache.has(language)) {
this.moduleCache.set(language, new Set());
}
this.moduleCache.get(language)!.add(`${module}.${type}`);
this.logger.log(`模块语言包加载完成: ${module}.${type} (${language})`);
} catch (error) {
this.logger.error(`加载模块语言包失败: ${module}.${type} (${language})`, error);
}
}
/**
* 加载通用语言包
*/
private async loadCommonLanguagePack(langDir: string, type: string, languagePack: Map<string, any>): Promise<void> {
const commonDir = path.join(langDir, 'common');
const filePath = path.join(commonDir, `${type}.json`);
try {
const content = await fs.readFile(filePath, 'utf8');
const data = JSON.parse(content);
// 合并到语言包,添加前缀
for (const [key, value] of Object.entries(data)) {
languagePack.set(`${type}.common.${key}`, value);
}
} catch (error) {
this.logger.warn(`加载通用语言包失败: ${type}`, error);
}
}
/**
* 加载模块特定语言包
*/
private async loadModuleSpecificPack(langDir: string, module: string, type: string, languagePack: Map<string, any>): Promise<void> {
const moduleDir = path.join(langDir, module);
const filePath = path.join(moduleDir, `${type}.json`);
try {
const content = await fs.readFile(filePath, 'utf8');
const data = JSON.parse(content);
// 合并到语言包,添加前缀
for (const [key, value] of Object.entries(data)) {
languagePack.set(`${type}.${module}.${key}`, value);
}
} catch (error) {
this.logger.warn(`加载模块语言包失败: ${module}.${type}`, error);
}
}
/**
* 加载插件语言包
*/
private async loadAddonLanguagePacks(langDir: string, type: string, languagePack: Map<string, any>): Promise<void> {
const addonsDir = path.join(langDir, 'addons');
try {
const addonDirs = await fs.readdir(addonsDir);
for (const addonDir of addonDirs) {
const addonPath = path.join(addonsDir, addonDir);
const stat = await fs.stat(addonPath);
if (stat.isDirectory()) {
const filePath = path.join(addonPath, `${type}.json`);
try {
const content = await fs.readFile(filePath, 'utf8');
const data = JSON.parse(content);
// 合并到语言包,添加前缀
for (const [key, value] of Object.entries(data)) {
languagePack.set(`${type}.addon.${addonDir}.${key}`, value);
}
} catch (error) {
this.logger.warn(`加载插件语言包失败: ${addonDir}.${type}`, error);
}
}
}
} catch (error) {
this.logger.warn(`扫描插件语言包失败: ${type}`, error);
}
}
/**
* 合并语言数据
*/
private mergeLanguageData(target: Map<string, any>, source: Record<string, any>): void {
for (const [key, value] of Object.entries(source)) {
target.set(key, value);
}
}
/**
* 从缓存获取消息
*/
private getMessageFromCache(key: string, language: string): string {
const languagePack = this.languageCache.get(language);
if (languagePack && languagePack.has(key)) {
return languagePack.get(key);
}
return key; // 未找到返回key作为fallback
}
/**
* 替换消息参数
* 对应 Java: MessageFormat.format()
*/
private replaceMessageParams(message: string, args: Record<string, any>): string {
let result = message;
for (const [key, value] of Object.entries(args)) {
const placeholder = `{${key}}`;
result = result.replace(new RegExp(placeholder, 'g'), String(value));
}
return result;
}
/**
* 获取当前语言
* @returns 当前语言
*/
getCurrentLanguage(): string {
return this.getDefaultLanguage();
}
/**
* 设置语言
* @param language 语言代码
*/
setLanguage(language: string): void {
this.logger.log(`设置语言: ${language}`);
}
/**
* 获取支持的语言列表
* @returns 支持的语言列表
*/
getSupportedLanguages(): string[] {
return this.configService.get('app.supportedLocales', ['zh_CN', 'en_US']);
}
/**
* 获取默认语言
* @returns 默认语言
*/
getDefaultLanguage(): string {
return this.configService.get('app.defaultLanguage', 'zh_CN');
}
/**
* 检查语言是否支持
* @param language 语言代码
* @returns 是否支持
*/
isLanguageSupported(language: string): boolean {
const supportedLanguages = this.getSupportedLanguages();
return supportedLanguages.includes(language);
}
/**
* 重新加载语言包
*/
async reloadLanguagePack(language: string): Promise<void> {
try {
// 清除该语言的所有缓存
this.languageCache.delete(language);
this.moduleCache.delete(language);
// 重新加载通用语言包
await this.loadModuleLanguagePack('common', 'api', language);
await this.loadModuleLanguagePack('common', 'dict', language);
await this.loadModuleLanguagePack('common', 'validate', language);
this.logger.log(`重新加载语言包完成: ${language}`);
} catch (error) {
this.logger.error(`重新加载语言包失败: ${language}`, error);
}
}
/**
* 重新加载所有语言包
*/
async reloadAllLanguagePacks(): Promise<void> {
try {
this.languageCache.clear();
this.moduleCache.clear();
await this.initializeLanguagePacks();
this.logger.log('所有语言包重新加载完成');
} catch (error) {
this.logger.error('重新加载所有语言包失败', error);
}
}
/**
* 获取API消息
* @param key 消息键
* @param args 参数
* @param module 模块名
* @param language 语言
*/
async getApiMessage(key: string, args?: any, module: string = 'common', language?: string): Promise<string> {
const currentLanguage = language || this.getCurrentLanguage();
return this.getMessage(key, args, module, 'api', currentLanguage);
}
/**
* 获取字典数据
* @param key 字典键
* @param args 参数
* @param module 模块名
* @param language 语言
*/
async getDictData(key: string, args?: any, module: string = 'common', language?: string): Promise<string> {
const currentLanguage = language || this.getCurrentLanguage();
return this.getMessage(key, args, module, 'dict', currentLanguage);
}
/**
* 获取验证器消息
* @param key 验证器键
* @param args 参数
* @param module 模块名
* @param language 语言
*/
async getValidateMessage(key: string, args?: any, module: string = 'common', language?: string): Promise<string> {
const currentLanguage = language || this.getCurrentLanguage();
return this.getMessage(key, args, module, 'validate', currentLanguage);
}
/**
* 批量获取消息
* @param keys 消息键数组
* @param module 模块名
* @param type 类型
* @param language 语言
*/
async getBatchMessages(keys: string[], module: string = 'common', type: string = 'api', language?: string): Promise<Record<string, string>> {
const results: Record<string, string> = {};
const currentLanguage = language || this.getCurrentLanguage();
for (const key of keys) {
results[key] = await this.getMessage(key, undefined, module, type, currentLanguage);
}
return results;
}
}