- 将wwjcloud目录重命名为wwjcloud-nest-v1作为项目根目录 - 将原nestjs目录重命名为wwjcloud作为NestJS后端目录 - 实现真正的前后端分离架构 - 恢复工作区中丢失的目录结构 - 更新相关配置文件路径引用 - 清理重复和嵌套目录问题 目录结构: wwjcloud-nest-v1/ ├── wwjcloud/ # NestJS 后端 ├── admin/ # 管理端前端 ├── web/ # PC端前端 ├── uni-app-x/ # 移动端前端 ├── wwjcloud-web/ # 部署根目录 ├── docker/ # Docker 配置 ├── docs/ # 文档 └── tools/ # 工具集
596 lines
15 KiB
JavaScript
596 lines
15 KiB
JavaScript
/**
|
|
* 质量保证系统
|
|
* 为AI自动生成打下基石
|
|
*/
|
|
|
|
class QualityAssurance {
|
|
constructor() {
|
|
this.validators = {
|
|
syntax: this.validateSyntax.bind(this),
|
|
types: this.validateTypes.bind(this),
|
|
imports: this.validateImports.bind(this),
|
|
business: this.validateBusinessLogic.bind(this),
|
|
performance: this.validatePerformance.bind(this),
|
|
security: this.validateSecurity.bind(this)
|
|
};
|
|
|
|
this.fixers = {
|
|
syntax: this.fixSyntaxErrors.bind(this),
|
|
types: this.fixTypeErrors.bind(this),
|
|
imports: this.fixImportErrors.bind(this),
|
|
business: this.fixBusinessLogicErrors.bind(this)
|
|
};
|
|
|
|
this.metrics = {
|
|
complexity: this.calculateComplexity.bind(this),
|
|
maintainability: this.calculateMaintainability.bind(this),
|
|
testability: this.calculateTestability.bind(this),
|
|
performance: this.calculatePerformance.bind(this)
|
|
};
|
|
}
|
|
|
|
/**
|
|
* 执行完整的质量检查
|
|
*/
|
|
async performQualityCheck(code, context = {}) {
|
|
const results = {
|
|
overall: 'pass',
|
|
validations: {},
|
|
fixes: {},
|
|
metrics: {},
|
|
recommendations: [],
|
|
errors: [],
|
|
warnings: []
|
|
};
|
|
|
|
console.log('🛡️ 开始质量检查...');
|
|
|
|
// 执行所有验证
|
|
for (const [type, validator] of Object.entries(this.validators)) {
|
|
try {
|
|
console.log(` 🔍 执行${type}验证...`);
|
|
const validation = await validator(code, context);
|
|
results.validations[type] = validation;
|
|
|
|
if (validation.errors.length > 0) {
|
|
results.errors.push(...validation.errors);
|
|
results.overall = 'fail';
|
|
}
|
|
|
|
if (validation.warnings.length > 0) {
|
|
results.warnings.push(...validation.warnings);
|
|
}
|
|
|
|
console.log(` ✅ ${type}验证完成: ${validation.errors.length}个错误, ${validation.warnings.length}个警告`);
|
|
} catch (error) {
|
|
console.error(` ❌ ${type}验证失败:`, error.message);
|
|
results.errors.push({
|
|
type,
|
|
message: error.message,
|
|
stack: error.stack
|
|
});
|
|
results.overall = 'fail';
|
|
}
|
|
}
|
|
|
|
// 计算质量指标
|
|
for (const [type, calculator] of Object.entries(this.metrics)) {
|
|
try {
|
|
results.metrics[type] = calculator(code, context);
|
|
} catch (error) {
|
|
console.error(` ❌ ${type}指标计算失败:`, error.message);
|
|
}
|
|
}
|
|
|
|
// 生成建议
|
|
results.recommendations = this.generateRecommendations(results);
|
|
|
|
console.log(`🎯 质量检查完成: ${results.overall.toUpperCase()}`);
|
|
return results;
|
|
}
|
|
|
|
/**
|
|
* 自动修复代码问题
|
|
*/
|
|
async autoFix(code, qualityResults) {
|
|
let fixedCode = code;
|
|
const fixes = [];
|
|
|
|
console.log('🔧 开始自动修复...');
|
|
|
|
// 修复语法错误
|
|
if (qualityResults.validations.syntax?.errors.length > 0) {
|
|
const syntaxFixes = await this.fixers.syntax(fixedCode, qualityResults.validations.syntax);
|
|
fixedCode = syntaxFixes.code;
|
|
fixes.push(...syntaxFixes.fixes);
|
|
}
|
|
|
|
// 修复类型错误
|
|
if (qualityResults.validations.types?.errors.length > 0) {
|
|
const typeFixes = await this.fixers.types(fixedCode, qualityResults.validations.types);
|
|
fixedCode = typeFixes.code;
|
|
fixes.push(...typeFixes.fixes);
|
|
}
|
|
|
|
// 修复导入错误
|
|
if (qualityResults.validations.imports?.errors.length > 0) {
|
|
const importFixes = await this.fixers.imports(fixedCode, qualityResults.validations.imports);
|
|
fixedCode = importFixes.code;
|
|
fixes.push(...importFixes.fixes);
|
|
}
|
|
|
|
// 修复业务逻辑错误
|
|
if (qualityResults.validations.business?.errors.length > 0) {
|
|
const businessFixes = await this.fixers.business(fixedCode, qualityResults.validations.business);
|
|
fixedCode = businessFixes.code;
|
|
fixes.push(...businessFixes.fixes);
|
|
}
|
|
|
|
console.log(`✅ 自动修复完成: ${fixes.length}个修复`);
|
|
|
|
return {
|
|
code: fixedCode,
|
|
fixes,
|
|
summary: {
|
|
totalFixes: fixes.length,
|
|
fixedTypes: [...new Set(fixes.map(f => f.type))]
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* 验证语法
|
|
*/
|
|
async validateSyntax(code, context) {
|
|
const errors = [];
|
|
const warnings = [];
|
|
|
|
// 检查方括号错误
|
|
const bracketErrors = this.findBracketErrors(code);
|
|
errors.push(...bracketErrors);
|
|
|
|
// 检查重复前缀
|
|
const prefixErrors = this.findPrefixErrors(code);
|
|
errors.push(...prefixErrors);
|
|
|
|
// 检查语法错误
|
|
const syntaxErrors = this.findSyntaxErrors(code);
|
|
errors.push(...syntaxErrors);
|
|
|
|
// 检查代码风格
|
|
const styleWarnings = this.findStyleWarnings(code);
|
|
warnings.push(...styleWarnings);
|
|
|
|
return { errors, warnings };
|
|
}
|
|
|
|
/**
|
|
* 验证类型
|
|
*/
|
|
async validateTypes(code, context) {
|
|
const errors = [];
|
|
const warnings = [];
|
|
|
|
// 检查类型声明
|
|
const typeErrors = this.findTypeErrors(code);
|
|
errors.push(...typeErrors);
|
|
|
|
// 检查类型使用
|
|
const usageWarnings = this.findTypeUsageWarnings(code);
|
|
warnings.push(...usageWarnings);
|
|
|
|
return { errors, warnings };
|
|
}
|
|
|
|
/**
|
|
* 验证导入
|
|
*/
|
|
async validateImports(code, context) {
|
|
const errors = [];
|
|
const warnings = [];
|
|
|
|
// 检查缺失的导入
|
|
const missingImports = this.findMissingImports(code);
|
|
errors.push(...missingImports);
|
|
|
|
// 检查未使用的导入
|
|
const unusedImports = this.findUnusedImports(code);
|
|
warnings.push(...unusedImports);
|
|
|
|
return { errors, warnings };
|
|
}
|
|
|
|
/**
|
|
* 验证业务逻辑
|
|
*/
|
|
async validateBusinessLogic(code, context) {
|
|
const errors = [];
|
|
const warnings = [];
|
|
|
|
// 检查业务逻辑完整性
|
|
const businessErrors = this.findBusinessLogicErrors(code);
|
|
errors.push(...businessErrors);
|
|
|
|
// 检查业务规则
|
|
const ruleWarnings = this.findBusinessRuleWarnings(code);
|
|
warnings.push(...ruleWarnings);
|
|
|
|
return { errors, warnings };
|
|
}
|
|
|
|
/**
|
|
* 验证性能
|
|
*/
|
|
async validatePerformance(code, context) {
|
|
const errors = [];
|
|
const warnings = [];
|
|
|
|
// 检查性能问题
|
|
const performanceIssues = this.findPerformanceIssues(code);
|
|
warnings.push(...performanceIssues);
|
|
|
|
return { errors, warnings };
|
|
}
|
|
|
|
/**
|
|
* 验证安全性
|
|
*/
|
|
async validateSecurity(code, context) {
|
|
const errors = [];
|
|
const warnings = [];
|
|
|
|
// 检查安全问题
|
|
const securityIssues = this.findSecurityIssues(code);
|
|
errors.push(...securityIssues);
|
|
|
|
return { errors, warnings };
|
|
}
|
|
|
|
// 错误检测方法
|
|
findBracketErrors(code) {
|
|
const errors = [];
|
|
const lines = code.split('\n');
|
|
|
|
lines.forEach((line, index) => {
|
|
if (line.includes(']') && !line.includes('[')) {
|
|
// 检查是否是函数调用中的方括号错误
|
|
if (line.match(/\w+\]/)) {
|
|
errors.push({
|
|
type: 'syntax',
|
|
message: '方括号错误: 应该是圆括号',
|
|
line: index + 1,
|
|
code: line.trim(),
|
|
severity: 'error'
|
|
});
|
|
}
|
|
}
|
|
});
|
|
|
|
return errors;
|
|
}
|
|
|
|
findPrefixErrors(code) {
|
|
const errors = [];
|
|
const lines = code.split('\n');
|
|
|
|
lines.forEach((line, index) => {
|
|
if (line.includes('BusinessBusinessException')) {
|
|
errors.push({
|
|
type: 'syntax',
|
|
message: '重复的Business前缀',
|
|
line: index + 1,
|
|
code: line.trim(),
|
|
severity: 'error'
|
|
});
|
|
}
|
|
});
|
|
|
|
return errors;
|
|
}
|
|
|
|
findSyntaxErrors(code) {
|
|
const errors = [];
|
|
const lines = code.split('\n');
|
|
|
|
lines.forEach((line, index) => {
|
|
// 检查等号错误
|
|
if (line.includes('====')) {
|
|
errors.push({
|
|
type: 'syntax',
|
|
message: '重复的等号',
|
|
line: index + 1,
|
|
code: line.trim(),
|
|
severity: 'error'
|
|
});
|
|
}
|
|
|
|
// 检查括号不匹配
|
|
const openParens = (line.match(/\(/g) || []).length;
|
|
const closeParens = (line.match(/\)/g) || []).length;
|
|
const openBrackets = (line.match(/\[/g) || []).length;
|
|
const closeBrackets = (line.match(/\]/g) || []).length;
|
|
|
|
if (openParens !== closeParens) {
|
|
errors.push({
|
|
type: 'syntax',
|
|
message: '括号不匹配',
|
|
line: index + 1,
|
|
code: line.trim(),
|
|
severity: 'error'
|
|
});
|
|
}
|
|
|
|
if (openBrackets !== closeBrackets) {
|
|
errors.push({
|
|
type: 'syntax',
|
|
message: '方括号不匹配',
|
|
line: index + 1,
|
|
code: line.trim(),
|
|
severity: 'error'
|
|
});
|
|
}
|
|
});
|
|
|
|
return errors;
|
|
}
|
|
|
|
findStyleWarnings(code) {
|
|
const warnings = [];
|
|
const lines = code.split('\n');
|
|
|
|
lines.forEach((line, index) => {
|
|
// 检查行长度
|
|
if (line.length > 120) {
|
|
warnings.push({
|
|
type: 'style',
|
|
message: '行长度超过120字符',
|
|
line: index + 1,
|
|
code: line.trim(),
|
|
severity: 'warning'
|
|
});
|
|
}
|
|
|
|
// 检查尾随空格
|
|
if (line.endsWith(' ')) {
|
|
warnings.push({
|
|
type: 'style',
|
|
message: '尾随空格',
|
|
line: index + 1,
|
|
code: line.trim(),
|
|
severity: 'warning'
|
|
});
|
|
}
|
|
});
|
|
|
|
return warnings;
|
|
}
|
|
|
|
findTypeErrors(code) {
|
|
const errors = [];
|
|
// 类型错误检测逻辑
|
|
return errors;
|
|
}
|
|
|
|
findTypeUsageWarnings(code) {
|
|
const warnings = [];
|
|
// 类型使用警告检测逻辑
|
|
return warnings;
|
|
}
|
|
|
|
findMissingImports(code) {
|
|
const errors = [];
|
|
const lines = code.split('\n');
|
|
|
|
// 检查使用的类是否已导入
|
|
const usedClasses = this.extractUsedClasses(code);
|
|
const importedClasses = this.extractImportedClasses(code);
|
|
|
|
usedClasses.forEach(className => {
|
|
if (!importedClasses.includes(className)) {
|
|
errors.push({
|
|
type: 'import',
|
|
message: `缺失导入: ${className}`,
|
|
line: -1,
|
|
code: '',
|
|
severity: 'error'
|
|
});
|
|
}
|
|
});
|
|
|
|
return errors;
|
|
}
|
|
|
|
findUnusedImports(code) {
|
|
const warnings = [];
|
|
// 未使用导入检测逻辑
|
|
return warnings;
|
|
}
|
|
|
|
findBusinessLogicErrors(code) {
|
|
const errors = [];
|
|
// 业务逻辑错误检测逻辑
|
|
return errors;
|
|
}
|
|
|
|
findBusinessRuleWarnings(code) {
|
|
const warnings = [];
|
|
// 业务规则警告检测逻辑
|
|
return warnings;
|
|
}
|
|
|
|
findPerformanceIssues(code) {
|
|
const warnings = [];
|
|
// 性能问题检测逻辑
|
|
return warnings;
|
|
}
|
|
|
|
findSecurityIssues(code) {
|
|
const errors = [];
|
|
// 安全问题检测逻辑
|
|
return errors;
|
|
}
|
|
|
|
// 修复方法
|
|
async fixSyntaxErrors(code, validation) {
|
|
let fixedCode = code;
|
|
const fixes = [];
|
|
|
|
validation.errors.forEach(error => {
|
|
if (error.message.includes('方括号错误')) {
|
|
fixedCode = fixedCode.replace(/(\w+)\]/g, '$1)');
|
|
fixes.push({
|
|
type: 'syntax',
|
|
description: '修复方括号错误',
|
|
line: error.line
|
|
});
|
|
}
|
|
|
|
if (error.message.includes('重复的Business前缀')) {
|
|
fixedCode = fixedCode.replace(/BusinessBusinessException/g, 'BusinessException');
|
|
fixes.push({
|
|
type: 'syntax',
|
|
description: '修复重复的Business前缀',
|
|
line: error.line
|
|
});
|
|
}
|
|
|
|
if (error.message.includes('重复的等号')) {
|
|
fixedCode = fixedCode.replace(/====/g, '===');
|
|
fixes.push({
|
|
type: 'syntax',
|
|
description: '修复重复的等号',
|
|
line: error.line
|
|
});
|
|
}
|
|
});
|
|
|
|
return { code: fixedCode, fixes };
|
|
}
|
|
|
|
async fixTypeErrors(code, validation) {
|
|
let fixedCode = code;
|
|
const fixes = [];
|
|
// 类型错误修复逻辑
|
|
return { code: fixedCode, fixes };
|
|
}
|
|
|
|
async fixImportErrors(code, validation) {
|
|
let fixedCode = code;
|
|
const fixes = [];
|
|
// 导入错误修复逻辑
|
|
return { code: fixedCode, fixes };
|
|
}
|
|
|
|
async fixBusinessLogicErrors(code, validation) {
|
|
let fixedCode = code;
|
|
const fixes = [];
|
|
// 业务逻辑错误修复逻辑
|
|
return { code: fixedCode, fixes };
|
|
}
|
|
|
|
// 指标计算方法
|
|
calculateComplexity(code, context) {
|
|
const lines = code.split('\n');
|
|
const methods = (code.match(/function\s+\w+/g) || []).length;
|
|
const conditions = (code.match(/if\s*\(|else\s*if\s*\(|switch\s*\(/g) || []).length;
|
|
const loops = (code.match(/for\s*\(|while\s*\(|foreach\s*\(/g) || []).length;
|
|
|
|
return {
|
|
lines: lines.length,
|
|
methods,
|
|
conditions,
|
|
loops,
|
|
cyclomatic: methods + conditions + loops + 1
|
|
};
|
|
}
|
|
|
|
calculateMaintainability(code, context) {
|
|
const complexity = this.calculateComplexity(code, context);
|
|
const maintainabilityIndex = Math.max(0, 171 - 5.2 * Math.log(complexity.lines) - 0.23 * complexity.cyclomatic);
|
|
|
|
return {
|
|
index: maintainabilityIndex,
|
|
rating: maintainabilityIndex > 80 ? 'A' : maintainabilityIndex > 60 ? 'B' : maintainabilityIndex > 40 ? 'C' : 'D'
|
|
};
|
|
}
|
|
|
|
calculateTestability(code, context) {
|
|
const methods = (code.match(/function\s+\w+/g) || []).length;
|
|
const dependencies = (code.match(/this\.\w+Service/g) || []).length;
|
|
|
|
return {
|
|
methods,
|
|
dependencies,
|
|
testabilityScore: Math.max(0, 100 - dependencies * 10)
|
|
};
|
|
}
|
|
|
|
calculatePerformance(code, context) {
|
|
const loops = (code.match(/for\s*\(|while\s*\(|foreach\s*\(/g) || []).length;
|
|
const asyncCalls = (code.match(/await\s+/g) || []).length;
|
|
|
|
return {
|
|
loops,
|
|
asyncCalls,
|
|
performanceScore: Math.max(0, 100 - loops * 5 - asyncCalls * 2)
|
|
};
|
|
}
|
|
|
|
// 辅助方法
|
|
extractUsedClasses(code) {
|
|
const classes = [];
|
|
const matches = code.match(/([A-Z][a-zA-Z0-9_]*)/g);
|
|
if (matches) {
|
|
classes.push(...matches);
|
|
}
|
|
return [...new Set(classes)];
|
|
}
|
|
|
|
extractImportedClasses(code) {
|
|
const imports = [];
|
|
const matches = code.match(/import\s*\{\s*([^}]+)\s*\}\s*from/g);
|
|
if (matches) {
|
|
matches.forEach(match => {
|
|
const importMatch = match.match(/import\s*\{\s*([^}]+)\s*\}\s*from/);
|
|
if (importMatch) {
|
|
const classNames = importMatch[1].split(',').map(name => name.trim());
|
|
imports.push(...classNames);
|
|
}
|
|
});
|
|
}
|
|
return imports;
|
|
}
|
|
|
|
generateRecommendations(results) {
|
|
const recommendations = [];
|
|
|
|
if (results.errors.length > 0) {
|
|
recommendations.push({
|
|
type: 'error',
|
|
message: '修复所有语法错误以提高代码质量',
|
|
priority: 'high'
|
|
});
|
|
}
|
|
|
|
if (results.warnings.length > 10) {
|
|
recommendations.push({
|
|
type: 'warning',
|
|
message: '减少警告数量以提高代码质量',
|
|
priority: 'medium'
|
|
});
|
|
}
|
|
|
|
if (results.metrics.complexity?.cyclomatic > 10) {
|
|
recommendations.push({
|
|
type: 'complexity',
|
|
message: '降低代码复杂度以提高可维护性',
|
|
priority: 'medium'
|
|
});
|
|
}
|
|
|
|
return recommendations;
|
|
}
|
|
}
|
|
|
|
module.exports = QualityAssurance;
|