feat: 完成 NestJS 后端核心底座开发 (M1-M6) 和 Ant Design Vue 前端迁移

主要更新:
1. 后端核心底座完成 (M1-M6):
   - 健康检查、指标监控、分布式锁
   - 事件总线、队列系统、事务管理
   - 安全守卫、多租户隔离、存储适配器
   - 审计日志、配置管理、多语言支持

2. 前端迁移到 Ant Design Vue:
   - 从 Element Plus 迁移到 Ant Design Vue
   - 完善 system 模块 (role/menu/dept)
   - 修复依赖和配置问题

3. 文档完善:
   - AI 开发工作流文档
   - 架构约束和开发规范
   - 项目进度跟踪

4. 其他改进:
   - 修复编译错误和类型问题
   - 完善测试用例
   - 优化项目结构
This commit is contained in:
万物街
2025-08-27 11:24:22 +08:00
parent be07b9ffec
commit 1cd5d3bdef
696 changed files with 36708 additions and 16868 deletions

View File

@@ -1,7 +1,10 @@
import { Body, Controller, Get, Put, UseGuards } from '@nestjs/common';
import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger';
import { EmailSettingsService } from './email-settings.service';
import { UpdateEmailSettingsDto, type EmailSettingsVo } from './email-settings.dto';
import {
UpdateEmailSettingsDto,
type EmailSettingsVo,
} from './email-settings.dto';
import { JwtAuthGuard } from '../../auth/guards/JwtAuthGuard';
import { Roles } from '../../auth/decorators/RolesDecorator';
import { RolesGuard } from '../../auth/guards/RolesGuard';
@@ -23,8 +26,10 @@ export class EmailSettingsController {
@Put()
@ApiOperation({ summary: '更新邮件设置' })
async update(@Body() dto: UpdateEmailSettingsDto): Promise<{ code: number; data: EmailSettingsVo }> {
async update(
@Body() dto: UpdateEmailSettingsDto,
): Promise<{ code: number; data: EmailSettingsVo }> {
const data = await this.service.updateSettings(dto);
return { code: 0, data };
}
}
}

View File

@@ -1,4 +1,12 @@
import { IsBoolean, IsEmail, IsInt, IsOptional, IsString, Max, Min } from 'class-validator';
import {
IsBoolean,
IsEmail,
IsInt,
IsOptional,
IsString,
Max,
Min,
} from 'class-validator';
export class UpdateEmailSettingsDto {
@IsBoolean()
@@ -33,4 +41,4 @@ export interface EmailSettingsVo {
user: string;
pass: string;
from: string;
}
}

View File

@@ -28,15 +28,21 @@ export class EmailSettingsService {
}
}
async updateSettings(patch: Partial<EmailSettingsVo>): Promise<EmailSettingsVo> {
async updateSettings(
patch: Partial<EmailSettingsVo>,
): Promise<EmailSettingsVo> {
const current = await this.getSettings();
const next: EmailSettingsVo = { ...current, ...patch };
await fs.promises.mkdir(SETTINGS_DIR, { recursive: true });
await fs.promises.writeFile(SETTINGS_FILE, JSON.stringify(next, null, 2), 'utf8');
await fs.promises.writeFile(
SETTINGS_FILE,
JSON.stringify(next, null, 2),
'utf8',
);
return next;
}
static getSettingsPath() {
return SETTINGS_FILE;
}
}
}

View File

@@ -0,0 +1,63 @@
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
import { BaseEntity } from '@wwjCore/base/BaseEntity';
/**
* 系统配置实体
* 对应数据库表: sys_config
*/
@Entity('sys_config')
export class SysConfig extends BaseEntity {
@PrimaryGeneratedColumn({ name: 'id' })
id: number;
@Column({ name: 'config_key', type: 'varchar', length: 255, default: '' })
config_key: string;
@Column({ name: 'value', type: 'text', nullable: true })
value: string;
@Column({ name: 'status', type: 'tinyint', default: 1 })
status: number;
@Column({ name: 'addon', type: 'varchar', length: 255, default: '' })
addon: string;
/**
* 获取配置值JSON解析
*/
getValueAsJson<T = any>(): T | null {
try {
return this.value ? JSON.parse(this.value) : null;
} catch (error) {
return null;
}
}
/**
* 设置配置值JSON序列化
*/
setValueFromJson(data: any): void {
this.value = JSON.stringify(data);
}
/**
* 检查配置是否启用
*/
isEnabled(): boolean {
return this.status === 1;
}
/**
* 启用配置
*/
enable(): void {
this.status = 1;
}
/**
* 禁用配置
*/
disable(): void {
this.status = 0;
}
}

View File

@@ -1,6 +1,9 @@
import { Body, Controller, Get, Put, UseGuards } from '@nestjs/common';
import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger';
import { UpdateLoginSettingsDto, type LoginSettingsVo } from './login-settings.dto';
import {
UpdateLoginSettingsDto,
type LoginSettingsVo,
} from './login-settings.dto';
import { LoginSettingsService } from './login-settings.service';
import { JwtAuthGuard } from '../../auth/guards/JwtAuthGuard';
import { Roles } from '../../auth/decorators/RolesDecorator';
@@ -23,8 +26,10 @@ export class LoginSettingsController {
@Put()
@ApiOperation({ summary: '更新登录设置' })
async update(@Body() dto: UpdateLoginSettingsDto): Promise<{ code: number; data: LoginSettingsVo }> {
async update(
@Body() dto: UpdateLoginSettingsDto,
): Promise<{ code: number; data: LoginSettingsVo }> {
const data = await this.service.updateSettings(dto);
return { code: 0, data };
}
}
}

View File

@@ -21,4 +21,4 @@ export interface LoginSettingsVo {
bg?: string;
isSiteCaptcha: boolean;
siteBg?: string;
}
}

View File

@@ -25,15 +25,21 @@ export class LoginSettingsService {
}
}
async updateSettings(patch: Partial<LoginSettingsVo>): Promise<LoginSettingsVo> {
async updateSettings(
patch: Partial<LoginSettingsVo>,
): Promise<LoginSettingsVo> {
const current = await this.getSettings();
const next: LoginSettingsVo = { ...current, ...patch };
await fs.promises.mkdir(SETTINGS_DIR, { recursive: true });
await fs.promises.writeFile(SETTINGS_FILE, JSON.stringify(next, null, 2), 'utf8');
await fs.promises.writeFile(
SETTINGS_FILE,
JSON.stringify(next, null, 2),
'utf8',
);
return next;
}
static getSettingsPath() {
return SETTINGS_FILE;
}
}
}

View File

@@ -7,4 +7,4 @@ import { LoginSettingsController } from './login-settings.controller';
controllers: [LoginSettingsController],
exports: [LoginSettingsService],
})
export class LoginModule {}
export class LoginModule {}

View File

@@ -1,7 +1,10 @@
import { Body, Controller, Get, Put, UseGuards } from '@nestjs/common';
import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger';
import { PaymentSettingsService } from './payment-settings.service';
import { UpdatePaymentSettingsDto, type PaymentSettingsVo } from './payment-settings.dto';
import {
UpdatePaymentSettingsDto,
type PaymentSettingsVo,
} from './payment-settings.dto';
import { JwtAuthGuard } from '../../auth/guards/JwtAuthGuard';
import { Roles } from '../../auth/decorators/RolesDecorator';
import { RolesGuard } from '../../auth/guards/RolesGuard';
@@ -29,4 +32,4 @@ export class PaymentSettingsController {
const data = await this.service.updateSettings(dto);
return { code: 0, data };
}
}
}

View File

@@ -17,4 +17,4 @@ export interface PaymentSettingsVo {
enabled: boolean;
alipay?: Record<string, string>;
wechatpay?: Record<string, string>;
}
}

View File

@@ -24,15 +24,21 @@ export class PaymentSettingsService {
}
}
async updateSettings(patch: Partial<PaymentSettingsVo>): Promise<PaymentSettingsVo> {
async updateSettings(
patch: Partial<PaymentSettingsVo>,
): Promise<PaymentSettingsVo> {
const current = await this.getSettings();
const next: PaymentSettingsVo = { ...current, ...patch };
await fs.promises.mkdir(SETTINGS_DIR, { recursive: true });
await fs.promises.writeFile(SETTINGS_FILE, JSON.stringify(next, null, 2), 'utf8');
await fs.promises.writeFile(
SETTINGS_FILE,
JSON.stringify(next, null, 2),
'utf8',
);
return next;
}
static getSettingsPath() {
return SETTINGS_FILE;
}
}
}

View File

@@ -1,4 +1,5 @@
import { Module } from '@nestjs/common';
import { Module, forwardRef } from '@nestjs/common';
import { AuthModule } from '../auth/auth.module';
import { StorageModule } from './storage/storage.module';
import { PaymentModule } from './payment/payment.module';
import { EmailModule } from './email/email.module';
@@ -9,6 +10,7 @@ import { SiteModule } from './site/site.module';
@Module({
imports: [
forwardRef(() => AuthModule),
StorageModule,
PaymentModule,
EmailModule,

View File

@@ -8,7 +8,12 @@ import {
HttpCode,
HttpStatus,
} from '@nestjs/common';
import { ApiTags, ApiOperation, ApiResponse, ApiBearerAuth } from '@nestjs/swagger';
import {
ApiTags,
ApiOperation,
ApiResponse,
ApiBearerAuth,
} from '@nestjs/swagger';
import { JwtAuthGuard } from '../../auth/guards/JwtAuthGuard';
import { RolesGuard } from '../../auth/guards/RolesGuard';
import { Roles } from '../../auth/decorators/RolesDecorator';
@@ -35,7 +40,9 @@ export class SiteSettingsController {
@ApiOperation({ summary: '更新站点设置' })
@ApiResponse({ status: 200, description: '站点设置更新成功' })
@Roles('super', 'admin')
async updateSiteSettings(@Body() updateSiteSettingsDto: UpdateSiteSettingsDto) {
async updateSiteSettings(
@Body() updateSiteSettingsDto: UpdateSiteSettingsDto,
) {
return this.siteSettingsService.updateSiteSettings(updateSiteSettingsDto);
}
@@ -47,4 +54,4 @@ export class SiteSettingsController {
async resetSiteSettings() {
return this.siteSettingsService.resetSiteSettings();
}
}
}

View File

@@ -88,4 +88,4 @@ export class SiteSettingsDto {
@ApiProperty({ description: '关闭原因' })
close_reason: string;
}
}

View File

@@ -3,7 +3,10 @@ import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Site } from './site.entity';
import { UpdateSiteSettingsDto } from './site-settings.dto';
import { DEFAULT_SITE_CONFIG, SYSTEM_CONSTANTS } from '../../../config/common/constants';
import {
DEFAULT_SITE_CONFIG,
SYSTEM_CONSTANTS,
} from '@wwjConfig/common/constants';
@Injectable()
export class SiteSettingsService {
@@ -30,7 +33,8 @@ export class SiteSettingsService {
site_name: site.site_name || DEFAULT_SITE_CONFIG.site_name,
site_title: site.site_title || DEFAULT_SITE_CONFIG.site_title,
site_keywords: site.site_keywords || DEFAULT_SITE_CONFIG.site_keywords,
site_description: site.site_description || DEFAULT_SITE_CONFIG.site_description,
site_description:
site.site_description || DEFAULT_SITE_CONFIG.site_description,
site_logo: site.site_logo || DEFAULT_SITE_CONFIG.site_logo,
site_favicon: site.site_favicon || DEFAULT_SITE_CONFIG.site_favicon,
icp_number: site.icp_number || DEFAULT_SITE_CONFIG.icp_number,
@@ -69,7 +73,8 @@ export class SiteSettingsService {
site_name: site_name || DEFAULT_SITE_CONFIG.site_name,
site_title: site_title || DEFAULT_SITE_CONFIG.site_title,
site_keywords: site_keywords || DEFAULT_SITE_CONFIG.site_keywords,
site_description: site_description || DEFAULT_SITE_CONFIG.site_description,
site_description:
site_description || DEFAULT_SITE_CONFIG.site_description,
site_logo: site_logo || DEFAULT_SITE_CONFIG.site_logo,
site_favicon: site_favicon || DEFAULT_SITE_CONFIG.site_favicon,
icp_number: icp_number || DEFAULT_SITE_CONFIG.icp_number,
@@ -82,7 +87,8 @@ export class SiteSettingsService {
if (site_name !== undefined) site.site_name = site_name;
if (site_title !== undefined) site.site_title = site_title;
if (site_keywords !== undefined) site.site_keywords = site_keywords;
if (site_description !== undefined) site.site_description = site_description;
if (site_description !== undefined)
site.site_description = site_description;
if (site_logo !== undefined) site.site_logo = site_logo;
if (site_favicon !== undefined) site.site_favicon = site_favicon;
if (icp_number !== undefined) site.icp_number = icp_number;
@@ -111,4 +117,4 @@ export class SiteSettingsService {
await this.siteRepository.save(defaultSite);
return { message: '站点设置重置成功' };
}
}
}

View File

@@ -10,7 +10,7 @@ export class Site {
site_id: number;
// 添加缺失的字段以匹配 PHP 项目
@PrimaryGeneratedColumn({ name: 'id' })
@Column({ name: 'id', type: 'int', default: 0 })
id: number;
@Column({ name: 'site_title', type: 'varchar', length: 255, default: '' })
@@ -129,4 +129,4 @@ export class Site {
@Column({ name: 'meta_keyword', type: 'varchar', length: 255, default: '' })
meta_keyword: string;
}
}

View File

@@ -10,4 +10,4 @@ import { Site } from './site.entity';
providers: [SiteSettingsService],
exports: [SiteSettingsService],
})
export class SiteModule {}
export class SiteModule {}

View File

@@ -23,8 +23,10 @@ export class SmsSettingsController {
@Put()
@ApiOperation({ summary: '更新短信设置' })
async update(@Body() dto: UpdateSmsSettingsDto): Promise<{ code: number; data: SmsSettingsVo }> {
async update(
@Body() dto: UpdateSmsSettingsDto,
): Promise<{ code: number; data: SmsSettingsVo }> {
const data = await this.service.updateSettings(dto);
return { code: 0, data };
}
}
}

View File

@@ -1,4 +1,10 @@
import { IsBoolean, IsIn, IsObject, IsOptional, IsString } from 'class-validator';
import {
IsBoolean,
IsIn,
IsObject,
IsOptional,
IsString,
} from 'class-validator';
export class UpdateSmsSettingsDto {
@IsBoolean()
@@ -36,4 +42,4 @@ export interface SmsSettingsVo {
accessKeySecret?: string;
region?: string;
templates?: Record<string, string>;
}
}

View File

@@ -32,11 +32,15 @@ export class SmsSettingsService {
const current = await this.getSettings();
const next: SmsSettingsVo = { ...current, ...patch };
await fs.promises.mkdir(SETTINGS_DIR, { recursive: true });
await fs.promises.writeFile(SETTINGS_FILE, JSON.stringify(next, null, 2), 'utf8');
await fs.promises.writeFile(
SETTINGS_FILE,
JSON.stringify(next, null, 2),
'utf8',
);
return next;
}
static getSettingsPath() {
return SETTINGS_FILE;
}
}
}

View File

@@ -1,7 +1,10 @@
import { Body, Controller, Get, Put, UseGuards } from '@nestjs/common';
import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger';
import { StorageSettingsService } from './storage-settings.service';
import { UpdateStorageSettingsDto, type StorageSettingsVo } from './storage-settings.dto';
import {
UpdateStorageSettingsDto,
type StorageSettingsVo,
} from './storage-settings.dto';
import { JwtAuthGuard } from '../../auth/guards/JwtAuthGuard';
import { Roles } from '../../auth/decorators/RolesDecorator';
import { RolesGuard } from '../../auth/guards/RolesGuard';
@@ -29,4 +32,4 @@ export class StorageSettingsController {
const data = await this.service.updateSettings(dto);
return { code: 0, data };
}
}
}

View File

@@ -51,4 +51,4 @@ export interface StorageSettingsVo {
domain?: string;
folder?: string;
isPrivate?: boolean;
}
}

View File

@@ -48,4 +48,4 @@ export class StorageSettingsService {
static getSettingsPath() {
return SETTINGS_FILE;
}
}
}