Files
claude_dev 918bbed0fc fix: 修复登录500错误和移除明文密码提示
- 修复 deps.py 中 get_vn_service 的引用错误 (vn_service.vn_service -> vn_service)
- 移除登录页面上的明文密码提示
- 改进前端错误处理,避免数据加载失败导致登录显示错误
2026-07-02 12:23:55 +08:00

392 lines
11 KiB
JavaScript

/**
* Comment 解析器
*
* 解析 Issue Comments 中的标记,提取关键信息
*/
/**
* 标记类型枚举
*/
export const MarkerType = {
EXECUTE_DONE: 'EXECUTE_DONE',
REVIEW_DONE: 'REVIEW_DONE',
TEST_DONE: 'TEST_DONE',
VERIFICATION_PASSED: 'VERIFICATION_PASSED',
VERIFICATION_REJECTED: 'VERIFICATION_REJECTED',
PROGRESS_UPDATE: 'PROGRESS_UPDATE',
CONSISTENCY_ISSUE: 'CONSISTENCY_ISSUE'
}
/**
* 解析完成标记
*
* @param {string} commentBody - Comment 内容
* @returns {Object|null} 解析结果
*/
export function parseCompletionMarker(commentBody) {
// 检测 EXECUTE_DONE
if (commentBody.includes('EXECUTE_DONE')) {
return {
type: MarkerType.EXECUTE_DONE,
status: 'completed',
data: extractExecuteData(commentBody)
}
}
// 检测 REVIEW_DONE
if (commentBody.includes('REVIEW_DONE')) {
return {
type: MarkerType.REVIEW_DONE,
status: 'completed',
data: extractReviewData(commentBody)
}
}
// 检测 TEST_DONE
if (commentBody.includes('TEST_DONE')) {
return {
type: MarkerType.TEST_DONE,
status: 'completed',
data: extractTestData(commentBody)
}
}
// 检测 VERIFICATION_PASSED
if (commentBody.includes('VERIFICATION_PASSED')) {
return {
type: MarkerType.VERIFICATION_PASSED,
status: 'completed',
data: extractVerificationData(commentBody)
}
}
// 检测 VERIFICATION_REJECTED
if (commentBody.includes('VERIFICATION_REJECTED')) {
return {
type: MarkerType.VERIFICATION_REJECTED,
status: 'failed',
data: extractVerificationData(commentBody)
}
}
// 检测 PROGRESS_UPDATE
if (commentBody.includes('进度更新')) {
return {
type: MarkerType.PROGRESS_UPDATE,
status: 'in_progress',
data: extractProgressData(commentBody)
}
}
// 检测 CONSISTENCY_ISSUE
if (commentBody.includes('CONSISTENCY_ISSUE')) {
return {
type: MarkerType.CONSISTENCY_ISSUE,
status: 'failed',
data: extractDeviationData(commentBody)
}
}
return null
}
/**
* 提取 EXECUTE_DONE 数据
*/
function extractExecuteData(commentBody) {
const deliverables = []
const deliverableMatch = commentBody.match(/### 交付物\n([\s\S]*?)\n\n/)
if (deliverableMatch) {
const lines = deliverableMatch[1].split('\n')
lines.forEach(line => {
const match = line.match(/\d+\.\s*`([^`]+)`/)
if (match) {
deliverables.push(match[1])
}
})
}
const testLocalMatch = commentBody.match(/本地运行:\s*([✅❌])\s*(\w+)/)
const coverageMatch = commentBody.match(/覆盖率:\s*([\d%]+)/)
const executorMatch = commentBody.match(/\*\*执行者\*\*:\s*([^\n]+)/)
const completedAtMatch = commentBody.match(/\*\*完成时间\*\*:\s*([^\n]+)/)
return {
deliverables,
testStatus: {
local: testLocalMatch ? `${testLocalMatch[1]} ${testLocalMatch[2]}` : 'UNKNOWN',
coverage: coverageMatch ? coverageMatch[1] : '0%'
},
executor: executorMatch ? executorMatch[1].trim() : 'Execute Sub Agent',
completedAt: completedAtMatch ? completedAtMatch[1].trim() : null
}
}
/**
* 提取 REVIEW_DONE 数据
*/
function extractReviewData(commentBody) {
const verdictMatch = commentBody.match(/verdict=(\w+)/)
const verdict = verdictMatch ? verdictMatch[1].toLowerCase() : 'unknown'
const findings = []
const findingsMatch = commentBody.match(/### 发现的问题\n([\s\S]*?)\n\n/)
if (findingsMatch) {
const lines = findingsMatch[1].split('\n')
lines.forEach(line => {
const match = line.match(/\d+\.\s*(.+)/)
if (match) {
findings.push(match[1].trim())
}
})
}
const suggestions = []
const suggestionsMatch = commentBody.match(/### 改进建议\n([\s\S]*?)\n\n/)
if (suggestionsMatch) {
const lines = suggestionsMatch[1].split('\n')
lines.forEach(line => {
const match = line.match(/\d+\.\s*(.+)/)
if (match) {
suggestions.push(match[1].trim())
}
})
}
const reviewerMatch = commentBody.match(/\*\*审查者\*\*:\s*([^\n]+)/)
const completedAtMatch = commentBody.match(/\*\*完成时间\*\*:\s*([^\n]+)/)
return {
verdict: verdict === 'approved' ? 'approved' : 'rejected',
findings,
suggestions,
reviewer: reviewerMatch ? reviewerMatch[1].trim() : 'Review Sub Agent',
completedAt: completedAtMatch ? completedAtMatch[1].trim() : null
}
}
/**
* 提取 TEST_DONE 数据
*/
function extractTestData(commentBody) {
const resultMatch = commentBody.match(/result=(\w+)/)
const coverageMatch = commentBody.match(/coverage=([\d%]+)/)
const summaryMatch = commentBody.match(/### 测试摘要\n([\s\S]*?)\n\n/)
const failures = []
const failuresMatch = commentBody.match(/### 失败的测试\n([\s\S]*?)\n\n/)
if (failuresMatch) {
const lines = failuresMatch[1].split('\n')
lines.forEach(line => {
const match = line.match(/\d+\.\s*(.+)/)
if (match) {
failures.push(match[1].trim())
}
})
}
const testerMatch = commentBody.match(/\*\*测试者\*\*:\s*([^\n]+)/)
const completedAtMatch = commentBody.match(/\*\*完成时间\*\*:\s*([^\n]+)/)
return {
result: resultMatch ? resultMatch[1].toLowerCase() : 'unknown',
coverage: coverageMatch ? coverageMatch[1] : '0%',
summary: summaryMatch ? summaryMatch[1].trim() : '',
failures,
tester: testerMatch ? testerMatch[1].trim() : 'Test Sub Agent',
completedAt: completedAtMatch ? completedAtMatch[1].trim() : null
}
}
/**
* 提取 VERIFICATION 数据
*/
function extractVerificationData(commentBody) {
// 提取三个检查结果
const requirementDesignPassed = commentBody.includes('需求→设计: 通过')
const designCodePassed = commentBody.includes('设计→编码: 通过')
const requirementCodePassed = commentBody.includes('需求→编码: 通过')
const allPassed = requirementDesignPassed && designCodePassed && requirementCodePassed
const completedAtMatch = commentBody.match(/\*\*完成时间\*\*:\s*([^\n]+)/)
return {
requirementDesign: { passed: requirementDesignPassed },
designCode: { passed: designCodePassed },
requirementCode: { passed: requirementCodePassed },
allPassed,
verdict: allPassed ? 'approved' : 'rejected',
completedAt: completedAtMatch ? completedAtMatch[1].trim() : null
}
}
/**
* 提取 PROGRESS_UPDATE 数据
*/
function extractProgressData(commentBody) {
const progressMatch = commentBody.match(/\*\*进度\*\*:\s*(\d+)%/)
const stageMatch = commentBody.match(/进度更新:\s*(\w+)\s*阶段/)
const completed = []
const completedMatch = commentBody.match(/## 已完成\n([\s\S]*?)\n\n/)
if (completedMatch) {
const lines = completedMatch[1].split('\n')
lines.forEach(line => {
const match = line.match/- (.+)/)
if (match) {
completed.push(match[1].trim())
}
})
}
const inProgress = []
const inProgressMatch = commentBody.match(/## 进行中\n([\s\S]*?)\n\n/)
if (inProgressMatch) {
const lines = inProgressMatch[1].split('\n')
lines.forEach(line => {
const match = line.match/- 🔄 (.+)/)
if (match) {
inProgress.push(match[1].trim())
}
})
}
return {
stage: stageMatch ? stageMatch[1] : 'Unknown',
progress: progressMatch ? parseInt(progressMatch[1], 10) : 0,
completed,
inProgress
}
}
/**
* 提取 CONSISTENCY_ISSUE 数据
*/
function extractDeviationData(commentBody) {
const typeMatch = commentBody.match(/问题:\s*(.+)\s*偏差/)
const issueMatch = commentBody.match(/\*\*问题描述\*\*:\s*([^\n]+)/)
const expectedMatch = commentBody.match(/\*\*预期\*\*:\s*([^\n]+)/)
const actualMatch = commentBody.match(/\*\*实际\*\*:\s*([^\n]+)/)
const actionRequired = []
const actionMatch = commentBody.match(/### 处理要求\n([\s\S]*?)\n\n/)
if (actionMatch) {
const lines = actionMatch[1].split('\n')
lines.forEach(line => {
const match = line.match(/\d+\.\s*(.+)/)
if (match) {
actionRequired.push(match[1].trim())
}
})
}
return {
type: typeMatch ? typeMatch[1].trim() : 'unknown',
issue: issueMatch ? issueMatch[1].trim() : '',
expected: expectedMatch ? expectedMatch[1].trim() : '',
actual: actualMatch ? actualMatch[1].trim() : '',
actionRequired
}
}
/**
* 查找最新的指定类型标记
*
* @param {Array<Object>} comments - Comments 数组
* @param {string} markerType - 标记类型
* @returns {Object|null} 最新的标记数据
*/
export function findLatestMarker(comments, markerType) {
if (!comments || comments.length === 0) {
return null
}
// 从后往前遍历(最新的在前)
for (let i = comments.length - 1; i >= 0; i--) {
const comment = comments[i]
const parsed = parseCompletionMarker(comment.body || comment.content || '')
if (parsed && parsed.type === markerType) {
return {
...parsed,
commentId: comment.id,
createdAt: comment.created_at || comment.timestamp,
author: comment.user?.login || comment.author || 'Unknown'
}
}
}
return null
}
/**
* 检查指定标记是否存在
*
* @param {Array<Object>} comments - Comments 数组
* @param {string} markerType - 标记类型
* @returns {boolean} 是否存在
*/
export function hasMarker(comments, markerType) {
return findLatestMarker(comments, markerType) !== null
}
/**
* 获取所有标记的摘要
*
* @param {Array<Object>} comments - Comments 数组
* @returns {Object} 标记摘要
*/
export function getMarkersSummary(comments) {
const summary = {
executeDone: findLatestMarker(comments, MarkerType.EXECUTE_DONE),
reviewDone: findLatestMarker(comments, MarkerType.REVIEW_DONE),
testDone: findLatestMarker(comments, MarkerType.TEST_DONE),
verificationPassed: findLatestMarker(comments, MarkerType.VERIFICATION_PASSED),
verificationRejected: findLatestMarker(comments, MarkerType.VERIFICATION_REJECTED),
progressUpdate: findLatestMarker(comments, MarkerType.PROGRESS_UPDATE),
consistencyIssue: findLatestMarker(comments, MarkerType.CONSISTENCY_ISSUE)
}
// 计算当前阶段状态
summary.currentPhase = determineCurrentPhase(summary)
return summary
}
/**
* 确定当前阶段
*/
function determineCurrentPhase(summary) {
if (summary.verificationPassed || summary.verificationRejected) {
return 'completed'
}
if (summary.reviewDone && summary.testDone) {
return 'verification'
}
if (summary.executeDone) {
return 'review_and_test'
}
if (summary.progressUpdate) {
return 'executing'
}
return 'pending'
}
/**
* 导出所有解析函数
*/
export default {
MarkerType,
parseCompletionMarker,
extractMarkerData: parseCompletionMarker,
findLatestMarker,
hasMarker,
getMarkersSummary,
determineCurrentPhase
}