feat(admin): 添加用户管理相关文件

添加用户管理视图、API和状态管理文件
This commit is contained in:
张翔
2026-03-28 14:37:29 +08:00
commit 08ea5fbe98
1643 changed files with 255646 additions and 0 deletions
@@ -0,0 +1,285 @@
import { exec, spawn, ChildProcess } from 'child_process';
import { promisify } from 'util';
import * as fs from 'fs';
import * as path from 'path';
import { logger } from '../utils/logger';
import { TestEnvironment } from '../models/test-result';
const execAsync = promisify(exec);
interface ServiceConfig {
name: string;
command: string;
cwd?: string;
healthCheckUrl?: string;
healthCheckCommand?: string;
port?: number;
startupTimeout: number;
env?: Record<string, string>;
}
export class TestEnvironmentManager {
private processes: Map<string, ChildProcess> = new Map();
private projectRoot: string;
private testRoot: string;
private environment: TestEnvironment | null = null;
constructor() {
this.testRoot = process.cwd();
this.projectRoot = path.dirname(this.testRoot);
}
async prepare(): Promise<TestEnvironment> {
logger.section('环境准备');
logger.info('检查系统环境...');
await this.checkSystemRequirements();
logger.info('检查并安装依赖...');
await this.installDependencies();
logger.info('收集环境信息...');
this.environment = await this.collectEnvironmentInfo();
logger.info('环境准备完成', { environment: this.environment });
return this.environment;
}
private async checkSystemRequirements(): Promise<void> {
const requirements = [
{ name: 'Node.js', command: 'node --version', minVersion: '18.0.0' },
{ name: 'npm', command: 'npm --version', minVersion: '9.0.0' }
];
for (const req of requirements) {
try {
const { stdout } = await execAsync(req.command);
const version = stdout.trim();
logger.debug(`${req.name} 版本: ${version}`);
} catch (error) {
throw new Error(`${req.name} 未安装或版本过低,最低要求: ${req.minVersion}`);
}
}
}
private async installDependencies(): Promise<void> {
const nodeModulesPath = path.join(this.testRoot, 'node_modules');
if (!fs.existsSync(nodeModulesPath)) {
logger.info('安装 npm 依赖...');
await execAsync('npm install', { cwd: this.testRoot });
}
const playwrightPath = path.join(nodeModulesPath, '@playwright');
if (!fs.existsSync(playwrightPath)) {
logger.info('安装 Playwright 浏览器...');
await execAsync('npx playwright install', { cwd: this.testRoot });
}
}
private async collectEnvironmentInfo(): Promise<TestEnvironment> {
const { stdout: nodeVersion } = await execAsync('node --version');
let browserVersions: Record<string, string> = {};
try {
const { stdout } = await execAsync('npx playwright --version', { cwd: this.testRoot });
browserVersions['playwright'] = stdout.trim();
} catch {
browserVersions['playwright'] = 'unknown';
}
return {
nodeVersion: nodeVersion.trim(),
os: process.platform,
browserVersions,
apiBaseUrl: process.env.API_BASE_URL || 'http://localhost:8080',
adminBaseUrl: process.env.ADMIN_BASE_URL || 'http://localhost:5174',
uniappBaseUrl: process.env.UNIAPP_BASE_URL || 'http://localhost:8081',
databaseType: process.env.DB_TYPE || 'h2',
timestamp: new Date()
};
}
async startAPIService(): Promise<void> {
logger.section('启动 API 服务');
const apiProjectPath = path.join(this.projectRoot, 'everything-is-suitable-api');
if (!fs.existsSync(apiProjectPath)) {
logger.warn('API 项目目录不存在,跳过 API 服务启动');
return;
}
const serviceConfig: ServiceConfig = {
name: 'api',
command: 'mvn',
cwd: apiProjectPath,
port: 8080,
startupTimeout: 120000,
env: {
SPRING_PROFILES_ACTIVE: 'test',
SPRING_DATASOURCE_URL: 'jdbc:h2:mem:testdb',
SPRING_DATASOURCE_DRIVER_CLASS_NAME: 'org.h2.Driver',
SPRING_JPA_DATABASE_PLATFORM: 'org.hibernate.dialect.H2Dialect'
}
};
await this.startService(serviceConfig, ['spring-boot:run', '-Dspring-boot.run.profiles=test']);
}
async startAdminService(): Promise<void> {
logger.section('启动 Admin 服务');
const adminProjectPath = path.join(this.projectRoot, 'everything-is-suitable-admin');
if (!fs.existsSync(adminProjectPath)) {
logger.warn('Admin 项目目录不存在,跳过 Admin 服务启动');
return;
}
const serviceConfig: ServiceConfig = {
name: 'admin',
command: 'npm',
cwd: adminProjectPath,
port: 5174,
startupTimeout: 60000,
env: {}
};
if (!fs.existsSync(path.join(adminProjectPath, 'node_modules'))) {
logger.info('安装 Admin 项目依赖...');
await execAsync('npm install', { cwd: adminProjectPath });
}
await this.startService(serviceConfig, ['run', 'dev']);
}
async startUniappService(): Promise<void> {
logger.section('启动 Uniapp 服务');
const uniappProjectPath = path.join(this.projectRoot, 'everything-is-suitable-uniapp');
if (!fs.existsSync(uniappProjectPath)) {
logger.warn('Uniapp 项目目录不存在,跳过 Uniapp 服务启动');
return;
}
const serviceConfig: ServiceConfig = {
name: 'uniapp',
command: 'npm',
cwd: uniappProjectPath,
port: 8081,
startupTimeout: 60000,
env: {}
};
if (!fs.existsSync(path.join(uniappProjectPath, 'node_modules'))) {
logger.info('安装 Uniapp 项目依赖...');
await execAsync('npm install', { cwd: uniappProjectPath });
}
await this.startService(serviceConfig, ['run', 'dev:h5']);
}
private async startService(config: ServiceConfig, args: string[]): Promise<void> {
logger.info(`启动 ${config.name} 服务...`);
const env = { ...process.env, ...config.env };
const childProcess = spawn(config.command, args, {
cwd: config.cwd,
env,
stdio: ['ignore', 'pipe', 'pipe'],
detached: false
});
this.processes.set(config.name, childProcess);
childProcess.stdout?.on('data', (data) => {
const output = data.toString();
logger.debug(`[${config.name}] ${output.trim()}`);
});
childProcess.stderr?.on('data', (data) => {
const output = data.toString();
logger.debug(`[${config.name}] ERROR: ${output.trim()}`);
});
childProcess.on('error', (error) => {
logger.error(`${config.name} 服务启动失败`, error);
});
await this.waitForServiceReady(config);
logger.info(`${config.name} 服务已启动`);
}
private async waitForServiceReady(config: ServiceConfig): Promise<void> {
const startTime = Date.now();
const timeout = config.startupTimeout;
while (Date.now() - startTime < timeout) {
try {
if (config.port) {
const isReady = await this.checkPort(config.port);
if (isReady) {
return;
}
}
await this.sleep(1000);
} catch (error) {
logger.debug(`等待 ${config.name} 服务就绪...`);
}
}
throw new Error(`${config.name} 服务启动超时 (${timeout}ms)`);
}
private async checkPort(port: number): Promise<boolean> {
try {
const { stdout } = await execAsync(`lsof -i :${port} -t`);
return stdout.trim().length > 0;
} catch {
return false;
}
}
async checkServiceHealth(name: string, url: string): Promise<boolean> {
try {
const { stdout } = await execAsync(`curl -s -o /dev/null -w "%{http_code}" ${url}`);
const statusCode = parseInt(stdout.trim(), 10);
return statusCode >= 200 && statusCode < 300;
} catch {
return false;
}
}
async stopAllServices(): Promise<void> {
logger.section('停止所有服务');
const entries = Array.from(this.processes.entries());
for (const [name, process] of entries) {
logger.info(`停止 ${name} 服务...`);
try {
process.kill('SIGTERM');
logger.info(`${name} 服务已停止`);
} catch (error) {
logger.warn(`停止 ${name} 服务时出错`, { error });
}
}
this.processes.clear();
}
async cleanup(): Promise<void> {
logger.section('清理环境');
await this.stopAllServices();
logger.info('环境清理完成');
}
private sleep(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}
getEnvironment(): TestEnvironment | null {
return this.environment;
}
}