918bbed0fc
- 修复 deps.py 中 get_vn_service 的引用错误 (vn_service.vn_service -> vn_service) - 移除登录页面上的明文密码提示 - 改进前端错误处理,避免数据加载失败导致登录显示错误
377 lines
9.9 KiB
JavaScript
377 lines
9.9 KiB
JavaScript
/**
|
||
* 一致性检查模块
|
||
*
|
||
* 实现需求-设计-编码三向一致性检查逻辑
|
||
*/
|
||
|
||
/**
|
||
* 需求→设计一致性检查
|
||
*
|
||
* @param {Object} requirements - 需求列表
|
||
* @param {Object} design - 设计文档
|
||
* @returns {Object} 检查结果
|
||
*/
|
||
export function checkRequirementDesign(requirements, design) {
|
||
const items = []
|
||
let allPassed = true
|
||
|
||
// 如果 requirements 是数组,转换为对象
|
||
const reqMap = Array.isArray(requirements)
|
||
? requirements.reduce((acc, r, i) => {
|
||
acc[`R${i + 1}`] = r
|
||
return acc
|
||
}, {})
|
||
: requirements
|
||
|
||
// 遍历每个需求,检查设计是否覆盖
|
||
for (const [reqId, requirement] of Object.entries(reqMap)) {
|
||
const reqText = typeof requirement === 'string' ? requirement : requirement.description || requirement.text || JSON.stringify(requirement)
|
||
const isCovered = checkDesignCoverage(reqText, design)
|
||
|
||
items.push({
|
||
check: `需求 ${reqId}`,
|
||
expected: reqText,
|
||
actual: isCovered ? '已覆盖' : '未覆盖',
|
||
passed: isCovered
|
||
})
|
||
|
||
if (!isCovered) {
|
||
allPassed = false
|
||
}
|
||
}
|
||
|
||
return {
|
||
type: 'requirement-design',
|
||
passed: allPassed,
|
||
items,
|
||
summary: allPassed ? '需求完全被设计覆盖' : '部分需求未被设计覆盖'
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 设计→编码一致性检查
|
||
*
|
||
* @param {Object} design - 设计文档
|
||
* @param {Object} codeChanges - 代码变更
|
||
* @returns {Object} 检查结果
|
||
*/
|
||
export function checkDesignCode(design, codeChanges) {
|
||
const items = []
|
||
let allPassed = true
|
||
|
||
// 提取设计中的关键组件/模块
|
||
const designComponents = extractDesignComponents(design)
|
||
|
||
// 提取代码变更中的文件/模块
|
||
const codeFiles = extractCodeFiles(codeChanges)
|
||
|
||
// 检查每个设计组件是否都有对应代码
|
||
for (const component of designComponents) {
|
||
const isImplemented = codeFiles.some(file =>
|
||
file.name.includes(component.name) ||
|
||
file.path.includes(component.path || component.name)
|
||
)
|
||
|
||
items.push({
|
||
check: `组件: ${component.name}`,
|
||
expected: `设计: ${component.description || component.type || '组件'}`,
|
||
actual: isImplemented ? `已实现: ${component.name}` : '未实现',
|
||
passed: isImplemented
|
||
})
|
||
|
||
if (!isImplemented) {
|
||
allPassed = false
|
||
}
|
||
}
|
||
|
||
return {
|
||
type: 'design-code',
|
||
passed: allPassed,
|
||
items,
|
||
summary: allPassed ? '设计完全被编码实现' : '部分设计未在编码中实现'
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 需求→编码一致性检查
|
||
*
|
||
* @param {Object} requirements - 需求列表
|
||
* @param {Object} codeChanges - 代码变更
|
||
* @returns {Object} 检查结果
|
||
*/
|
||
export function checkRequirementCode(requirements, codeChanges) {
|
||
const items = []
|
||
let allPassed = true
|
||
|
||
// 如果 requirements 是数组,转换为对象
|
||
const reqMap = Array.isArray(requirements)
|
||
? requirements.reduce((acc, r, i) => {
|
||
acc[`R${i + 1}`] = r
|
||
return acc
|
||
}, {})
|
||
: requirements
|
||
|
||
// 提取代码实现的功能点
|
||
const codeFeatures = extractCodeFeatures(codeChanges)
|
||
|
||
// 检查每个需求是否在代码中实现
|
||
for (const [reqId, requirement] of Object.entries(reqMap)) {
|
||
const reqText = typeof requirement === 'string' ? requirement : requirement.description || requirement.text || JSON.stringify(requirement)
|
||
const isImplemented = checkCodeImplementation(reqText, codeFeatures)
|
||
|
||
items.push({
|
||
check: `需求 ${reqId}`,
|
||
expected: reqText,
|
||
actual: isImplemented ? '已实现' : '未实现',
|
||
passed: isImplemented
|
||
})
|
||
|
||
if (!isImplemented) {
|
||
allPassed = false
|
||
}
|
||
}
|
||
|
||
return {
|
||
type: 'requirement-code',
|
||
passed: allPassed,
|
||
items,
|
||
summary: allPassed ? '需求完全在编码中实现' : '部分需求未在编码中实现'
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 完整的三向一致性检查
|
||
*
|
||
* @param {Object} requirements - 需求列表
|
||
* @param {Object} design - 设计文档
|
||
* @param {Object} codeChanges - 代码变更
|
||
* @returns {Object} 完整检查结果
|
||
*/
|
||
export function runThreeWayCheck(requirements, design, codeChanges) {
|
||
const requirementDesign = checkRequirementDesign(requirements, design)
|
||
const designCode = checkDesignCode(design, codeChanges)
|
||
const requirementCode = checkRequirementCode(requirements, codeChanges)
|
||
|
||
const allPassed = requirementDesign.passed && designCode.passed && requirementCode.passed
|
||
|
||
return {
|
||
allPassed,
|
||
requirementDesign,
|
||
designCode,
|
||
requirementCode,
|
||
summary: allPassed ? '三向一致性检查全部通过' : '三向一致性检查未通过',
|
||
deviations: findDeviations(requirementDesign, designCode, requirementCode)
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 查找偏差
|
||
*/
|
||
function findDeviations(requirementDesign, designCode, requirementCode) {
|
||
const deviations = []
|
||
|
||
// 需求→设计偏差
|
||
if (!requirementDesign.passed) {
|
||
deviations.push({
|
||
type: 'requirement-design',
|
||
issues: requirementDesign.items.filter(i => !i.passed)
|
||
})
|
||
}
|
||
|
||
// 设计→编码偏差
|
||
if (!designCode.passed) {
|
||
deviations.push({
|
||
type: 'design-code',
|
||
issues: designCode.items.filter(i => !i.passed)
|
||
})
|
||
}
|
||
|
||
// 需求→编码偏差
|
||
if (!requirementCode.passed) {
|
||
deviations.push({
|
||
type: 'requirement-code',
|
||
issues: requirementCode.items.filter(i => !i.passed)
|
||
})
|
||
}
|
||
|
||
return deviations
|
||
}
|
||
|
||
/**
|
||
* 辅助函数:检查设计是否覆盖需求
|
||
*/
|
||
function checkDesignCoverage(requirementText, design) {
|
||
const designText = typeof design === 'string' ? design : JSON.stringify(design)
|
||
const reqKeywords = extractKeywords(requirementText)
|
||
|
||
// 至少有一个关键词在设计中被提及
|
||
return reqKeywords.some(keyword =>
|
||
designText.toLowerCase().includes(keyword.toLowerCase())
|
||
)
|
||
}
|
||
|
||
/**
|
||
* 辅助函数:检查代码是否实现需求
|
||
*/
|
||
function checkCodeImplementation(requirementText, codeFeatures) {
|
||
const reqKeywords = extractKeywords(requirementText)
|
||
|
||
// 至少有一个关键词在代码特性中被实现
|
||
return reqKeywords.some(keyword =>
|
||
codeFeatures.some(feature =>
|
||
feature.toLowerCase().includes(keyword.toLowerCase())
|
||
)
|
||
)
|
||
}
|
||
|
||
/**
|
||
* 辅助函数:提取关键词
|
||
*/
|
||
function extractKeywords(text) {
|
||
// 简单的关键词提取:过滤常见停用词
|
||
const stopWords = new Set(['的', '了', '是', '在', '和', '与', '或', '但', '如果', '那么', '这样', '那样', 'the', 'a', 'an', 'is', 'are', 'and', 'or', 'but', 'if', 'then', 'this', 'that'])
|
||
|
||
return text
|
||
.toLowerCase()
|
||
.split(/[\s,,。.!!??;;::]/)
|
||
.filter(word => word.length > 1 && !stopWords.has(word))
|
||
.slice(0, 5) // 取前5个关键词
|
||
}
|
||
|
||
/**
|
||
* 辅助函数:从设计中提取组件
|
||
*/
|
||
function extractDesignComponents(design) {
|
||
const components = []
|
||
|
||
// 如果 design 是字符串,尝试解析
|
||
if (typeof design === 'string') {
|
||
// 简单的提取逻辑:查找常见的组件模式
|
||
const patterns = [
|
||
/表:\s*(\w+)/g,
|
||
/接口:\s*(\w+)/g,
|
||
/模块:\s*(\w+)/g,
|
||
/组件:\s*(\w+)/g,
|
||
/文件:\s*([^\s]+)/g
|
||
]
|
||
|
||
for (const pattern of patterns) {
|
||
let match
|
||
while ((match = pattern.exec(design)) !== null) {
|
||
components.push({
|
||
name: match[1],
|
||
type: 'unknown',
|
||
description: match[0]
|
||
})
|
||
}
|
||
}
|
||
} else if (typeof design === 'object' && design !== null) {
|
||
// 如果 design 是对象,直接提取
|
||
for (const [key, value] of Object.entries(design)) {
|
||
if (value && typeof value === 'object' && value.name) {
|
||
components.push(value)
|
||
} else if (typeof value === 'string') {
|
||
components.push({
|
||
name: key,
|
||
type: 'unknown',
|
||
description: value
|
||
})
|
||
}
|
||
}
|
||
}
|
||
|
||
return components
|
||
}
|
||
|
||
/**
|
||
* 辅助函数:从代码变更中提取文件
|
||
*/
|
||
function extractCodeFiles(codeChanges) {
|
||
const files = []
|
||
|
||
if (typeof codeChanges === 'string') {
|
||
// 简单的文件路径提取
|
||
const lines = codeChanges.split('\n')
|
||
for (const line of lines) {
|
||
const match = line.match(/([a-zA-Z0-9_\/]+\.[a-zA-Z0-9]+)/)
|
||
if (match) {
|
||
files.push({
|
||
name: match[1].split('/').pop(),
|
||
path: match[1]
|
||
})
|
||
}
|
||
}
|
||
} else if (Array.isArray(codeChanges)) {
|
||
// 如果是数组,假设每个元素都是文件信息
|
||
for (const change of codeChanges) {
|
||
if (typeof change === 'string') {
|
||
files.push({
|
||
name: change.split('/').pop(),
|
||
path: change
|
||
})
|
||
} else if (change.file || change.path || change.name) {
|
||
files.push({
|
||
name: change.file || change.name || change.path?.split('/').pop(),
|
||
path: change.path || change.file
|
||
})
|
||
}
|
||
}
|
||
} else if (typeof codeChanges === 'object' && codeChanges !== null) {
|
||
// 如果是对象,提取文件信息
|
||
if (codeChanges.files) {
|
||
return extractCodeFiles(codeChanges.files)
|
||
}
|
||
if (codeChanges.changes) {
|
||
return extractCodeFiles(codeChanges.changes)
|
||
}
|
||
}
|
||
|
||
return files
|
||
}
|
||
|
||
/**
|
||
* 辅助函数:从代码中提取功能点
|
||
*/
|
||
function extractCodeFeatures(codeChanges) {
|
||
const features = []
|
||
|
||
// 提取函数名、类名、变量名等作为功能特征
|
||
const files = extractCodeFiles(codeChanges)
|
||
|
||
for (const file of files) {
|
||
const name = file.name || file.path || ''
|
||
// 从文件名推断功能
|
||
if (name.includes('.')) {
|
||
const baseName = name.substring(0, name.lastIndexOf('.'))
|
||
features.push(baseName)
|
||
}
|
||
features.push(name)
|
||
}
|
||
|
||
// 如果 codeChanges 包含 description 或 summary
|
||
if (typeof codeChanges === 'object' && codeChanges !== null) {
|
||
if (codeChanges.description) {
|
||
features.push(codeChanges.description)
|
||
}
|
||
if (codeChanges.summary) {
|
||
features.push(codeChanges.summary)
|
||
}
|
||
if (codeChanges.changes) {
|
||
features.push(codeChanges.changes)
|
||
}
|
||
}
|
||
|
||
return features
|
||
}
|
||
|
||
/**
|
||
* 导出所有检查函数
|
||
*/
|
||
export default {
|
||
checkRequirementDesign,
|
||
checkDesignCode,
|
||
checkRequirementCode,
|
||
runThreeWayCheck,
|
||
findDeviations
|
||
}
|