Merge develop into test - sync latest code

This commit is contained in:
2026-04-10 12:31:19 +08:00
1255 changed files with 107256 additions and 24904 deletions

153
zentao_bug_fetch.js Normal file
View File

@@ -0,0 +1,153 @@
const { chromium } = require('playwright');
const fs = require('fs');
async function fetchBug(bugId) {
const browser = await chromium.launch({
headless: true,
args: ['--no-sandbox', '--disable-setuid-sandbox']
});
const context = await browser.newContext({
viewport: { width: 1920, height: 2000 }
});
const page = await context.newPage();
try {
const zentaoUrl = 'https://zentao.gentronhealth.com';
// Step 1: 访问登录页面
console.log('[Step 1] 访问登录页面...');
await page.goto(zentaoUrl + '/user-login.html', {
waitUntil: 'networkidle',
timeout: 60000
});
// Step 2: 登录
console.log('[Step 2] 登录...');
await page.waitForSelector('#account', { timeout: 10000 });
await page.fill('#account', 'admin');
await page.fill('input[name="password"]', 'Jchl1528');
await Promise.all([
page.click('#submit'),
page.waitForNavigation({ waitUntil: 'networkidle', timeout: 60000 })
]);
console.log('[Step 3] 登录成功');
await page.waitForTimeout(2000);
// Step 4: 访问BUG详情页面
console.log(`[Step 4] 访问 BUG #${bugId} ...`);
const bugUrl = zentaoUrl + '/index.php?m=bug&f=view&bugID=' + bugId;
await page.goto(bugUrl, {
waitUntil: 'networkidle',
timeout: 60000
});
await page.waitForTimeout(3000);
// Step 5: 关键使用frameLocator获取iframe内容
console.log('[Step 5] 获取iframe内容...');
const frame = page.frameLocator('iframe[name="app-qa"]');
const bodyElement = frame.locator('body');
// 等待iframe内容加载
await bodyElement.waitFor({ state: 'visible', timeout: 10000 }).catch(() => {});
// Step 6: 提取BUG信息
const bugData = await bodyElement.evaluate(() => {
const info = {
title: '',
description: '',
steps: '',
expected: '',
actual: '',
priority: '',
status: '',
module: '',
assignedTo: '',
openedBy: '',
bugType: '',
severity: '',
allText: document.body.innerText
};
// 获取标题
const titleEl = document.querySelector('.page-title, h1, h2, .title');
if (titleEl) {
info.title = titleEl.textContent.replace(/^\d+\s*/, '').trim();
}
// 获取表格数据
const rows = document.querySelectorAll('table.table-form tr');
rows.forEach(row => {
const th = row.querySelector('th');
const td = row.querySelector('td');
if (th && td) {
const label = th.textContent.trim();
const value = td.textContent.trim();
if (label.includes('所属模块')) info.module = value;
else if (label.includes('优先级')) info.priority = value;
else if (label.includes('Bug状态') || label.includes('状态')) info.status = value;
else if (label.includes('指派给')) info.assignedTo = value;
else if (label.includes('创建')) info.openedBy = value;
else if (label.includes('Bug类型')) info.bugType = value;
else if (label.includes('严重程度')) info.severity = value;
}
});
return info;
});
// 从完整文本提取描述和步骤
const fullText = bugData.allText;
const descMatch = fullText.match(/\[步骤\]([\s\S]*?)\[结果\]/);
if (descMatch) bugData.steps = descMatch[1].trim();
const actualMatch = fullText.match(/\[结果\]([\s\S]*?)\[期望\]/);
if (actualMatch) bugData.actual = actualMatch[1].trim();
const expectedMatch = fullText.match(/\[期望\]([\s\S]*?)(?=历史记录|$)/);
if (expectedMatch) bugData.expected = expectedMatch[1].trim();
// 输出结果
console.log('\n==== BUG #' + bugId + ' 详细信息 ====');
console.log(JSON.stringify(bugData, null, 2));
// 保存到文件
fs.writeFileSync(`zentao_bug_${bugId}_parsed.json`, JSON.stringify(bugData, null, 2));
// 保存截图
await page.screenshot({ path: `zentao_bug_${bugId}.png`, fullPage: true });
return { success: true, data: bugData };
} catch (error) {
console.error('[Error]', error.message);
await page.screenshot({ path: `zentao_bug_${bugId}_error.png`, fullPage: true });
return { success: false, error: error.message };
} finally {
await browser.close();
}
}
// 主函数
async function main() {
const bugId = 320;
console.log(`开始获取禅道 BUG #${bugId}...`);
const result = await fetchBug(bugId);
if (!result.success) {
console.log('\n❌ 获取失败!');
process.exit(1);
}
console.log('\n✅ 获取成功!');
}
main();