Initial commit: Telegram Management System
Some checks failed
Deploy / deploy (push) Has been cancelled

Full-stack web application for Telegram management
- Frontend: Vue 3 + Vben Admin
- Backend: NestJS
- Features: User management, group broadcast, statistics

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
你的用户名
2025-11-04 15:37:50 +08:00
commit 237c7802e5
3674 changed files with 525172 additions and 0 deletions

View File

@@ -0,0 +1,384 @@
import { test, expect, type Page } from '@playwright/test';
interface MenuStats {
totalCount: number;
menuItems: string[];
categories: string[];
timestamp: string;
}
interface MenuComparison {
backend: MenuStats;
mock: MenuStats;
differences: {
missingInMock: string[];
onlyInMock: string[];
totalDifference: number;
};
}
/**
* 菜单对比测试
* 测试前后端菜单显示差异,找出不一致的地方
*/
test.describe('菜单显示对比测试', () => {
let comparison: MenuComparison;
test.beforeAll(async () => {
console.log('🚀 开始菜单对比测试');
});
test('1⃣ 后端启动状态下的菜单测试', async ({ page }) => {
console.log('📊 测试后端启动状态下的菜单');
// 访问应用
await page.goto('http://localhost:5174');
await page.waitForTimeout(2000);
// 检查是否在登录页面
const isLoginPage = await page
.locator('input[placeholder*="用户名"], input[placeholder*="admin"]')
.isVisible();
if (isLoginPage) {
console.log('🔐 检测到登录页面,开始登录');
// 填写登录信息
await page.fill(
'input[placeholder*="用户名"], input[placeholder*="admin"]',
'admin',
);
await page.fill(
'input[placeholder*="密码"], input[type="password"]',
'111111',
);
// 点击登录按钮
await page.click('button[type="submit"], button:has-text("登录")');
await page.waitForTimeout(3000);
}
// 等待菜单加载
await page.waitForSelector(
'aside[class*="sidebar"], nav[class*="menu"], .ant-menu',
{ timeout: 10000 },
);
await page.waitForTimeout(2000);
// 获取所有菜单项
const menuStats = await extractMenuItems(page);
// 截图保存
await page.screenshot({
path: 'test-results/screenshots/backend-menu-full.png',
fullPage: true,
});
console.log(`✅ 后端模式菜单统计:`);
console.log(` - 总菜单项: ${menuStats.totalCount}`);
console.log(` - 主要分类: ${menuStats.categories.length}`);
console.log(
` - 菜单列表: ${menuStats.menuItems.slice(0, 10).join(', ')}${menuStats.menuItems.length > 10 ? '...' : ''}`,
);
comparison = {
backend: menuStats,
mock: { totalCount: 0, menuItems: [], categories: [], timestamp: '' },
differences: { missingInMock: [], onlyInMock: [], totalDifference: 0 },
};
});
test('2⃣ Mock模式下的菜单测试', async ({ page, browser }) => {
console.log('🎭 测试Mock模式下的菜单');
// 创建新的浏览器上下文来模拟无后端状态
const context = await browser.newContext();
const mockPage = await context.newPage();
// 拦截所有API请求模拟后端不可用
await mockPage.route('**/api/**', (route) => {
route.abort('failed');
});
await mockPage.route('**/auth/**', (route) => {
route.abort('failed');
});
await mockPage.route('**/system/**', (route) => {
route.abort('failed');
});
// 访问应用
await mockPage.goto('http://localhost:5174');
await mockPage.waitForTimeout(3000);
// 尝试登录使用Mock数据
try {
const isLoginPage = await mockPage
.locator('input[placeholder*="用户名"], input[placeholder*="admin"]')
.isVisible();
if (isLoginPage) {
console.log('🔐 Mock模式登录测试');
// 使用Mock用户登录
await mockPage.fill(
'input[placeholder*="用户名"], input[placeholder*="admin"]',
'admin',
);
await mockPage.fill(
'input[placeholder*="密码"], input[type="password"]',
'123456',
);
await mockPage.click('button[type="submit"], button:has-text("登录")');
await mockPage.waitForTimeout(3000);
}
} catch (error) {
console.log('⚠️ Mock模式登录失败尝试直接访问主页');
await mockPage.goto('http://localhost:5174/dashboard');
await mockPage.waitForTimeout(3000);
}
// 等待菜单加载或超时
try {
await mockPage.waitForSelector(
'aside[class*="sidebar"], nav[class*="menu"], .ant-menu',
{ timeout: 8000 },
);
await mockPage.waitForTimeout(2000);
} catch (error) {
console.log('⚠️ Mock模式菜单加载超时');
}
// 获取Mock模式菜单项
const mockMenuStats = await extractMenuItems(mockPage);
// 截图保存
await mockPage.screenshot({
path: 'test-results/screenshots/mock-menu-full.png',
fullPage: true,
});
console.log(`🎭 Mock模式菜单统计:`);
console.log(` - 总菜单项: ${mockMenuStats.totalCount}`);
console.log(` - 主要分类: ${mockMenuStats.categories.length}`);
console.log(
` - 菜单列表: ${mockMenuStats.menuItems.slice(0, 10).join(', ')}${mockMenuStats.menuItems.length > 10 ? '...' : ''}`,
);
// 更新对比数据
comparison.mock = mockMenuStats;
await context.close();
});
test('3⃣ 生成菜单对比报告', async () => {
console.log('📋 生成详细对比报告');
// 计算差异
const backendMenus = new Set(comparison.backend.menuItems);
const mockMenus = new Set(comparison.mock.menuItems);
const missingInMock = Array.from(backendMenus).filter(
(item) => !mockMenus.has(item),
);
const onlyInMock = Array.from(mockMenus).filter(
(item) => !backendMenus.has(item),
);
comparison.differences = {
missingInMock,
onlyInMock,
totalDifference: Math.abs(
comparison.backend.totalCount - comparison.mock.totalCount,
),
};
// 生成报告
const report = generateComparisonReport(comparison);
// 保存报告到文件
const fs = await import('fs');
await fs.promises.writeFile(
'test-results/menu-comparison-report.json',
JSON.stringify(comparison, null, 2),
);
// 输出控制台报告
console.log('\n' + '='.repeat(80));
console.log('🔍 菜单对比分析报告');
console.log('='.repeat(80));
console.log(report);
console.log('='.repeat(80));
// 断言检查
expect(comparison.backend.totalCount).toBeGreaterThan(0);
// 注意Mock模式可能完全没有菜单这是正常的测试结果
console.log(
`⚠️ 发现重大差异: Mock模式缺失 ${comparison.differences.totalDifference} 个菜单项`,
);
});
});
/**
* 提取页面菜单项信息
*/
async function extractMenuItems(page: Page): Promise<MenuStats> {
// 等待菜单容器加载
await page.waitForTimeout(2000);
try {
// 尝试多种菜单选择器
const menuSelectors = [
'.ant-menu-item',
'.ant-menu-submenu-title',
'[role="menuitem"]',
'li[class*="menu"]',
'a[class*="menu"]',
'.sidebar-menu-item',
'.nav-item',
];
let allMenuItems: string[] = [];
let categories: string[] = [];
for (const selector of menuSelectors) {
try {
const elements = await page.locator(selector).all();
for (const element of elements) {
const text = await element.textContent();
if (text && text.trim()) {
const cleanText = text.trim();
if (!allMenuItems.includes(cleanText)) {
allMenuItems.push(cleanText);
}
}
}
} catch (error) {
// 忽略选择器错误,继续下一个
}
}
// 尝试获取主要分类
try {
const categorySelectors = [
'.ant-menu-submenu-title',
'.menu-group-title',
'[class*="category"]',
'.sidebar-title',
];
for (const selector of categorySelectors) {
try {
const elements = await page.locator(selector).all();
for (const element of elements) {
const text = await element.textContent();
if (text && text.trim()) {
const cleanText = text.trim();
if (!categories.includes(cleanText)) {
categories.push(cleanText);
}
}
}
} catch (error) {
// 忽略选择器错误
}
}
} catch (error) {
console.log('⚠️ 获取分类失败:', error.message);
}
// 如果没有找到菜单项,尝试其他方法
if (allMenuItems.length === 0) {
console.log('⚠️ 使用备用方法获取菜单');
const fallbackSelectors = [
'span:has-text("仪表板")',
'span:has-text("账号管理")',
'span:has-text("群组管理")',
'span:has-text("私信群发")',
'span:has-text("系统管理")',
'a[href*="/dashboard"]',
'a[href*="/account"]',
'a[href*="/system"]',
];
for (const selector of fallbackSelectors) {
try {
const elements = await page.locator(selector).all();
for (const element of elements) {
const text = await element.textContent();
if (text && text.trim()) {
const cleanText = text.trim();
if (!allMenuItems.includes(cleanText)) {
allMenuItems.push(cleanText);
}
}
}
} catch (error) {
// 忽略错误
}
}
}
return {
totalCount: allMenuItems.length,
menuItems: allMenuItems,
categories: categories,
timestamp: new Date().toISOString(),
};
} catch (error) {
console.log('❌ 菜单提取失败:', error.message);
return {
totalCount: 0,
menuItems: [],
categories: [],
timestamp: new Date().toISOString(),
};
}
}
/**
* 生成对比报告
*/
function generateComparisonReport(comparison: MenuComparison): string {
const { backend, mock, differences } = comparison;
let report = '';
report += `📊 菜单统计对比:\n`;
report += ` 后端模式: ${backend.totalCount} 个菜单项\n`;
report += ` Mock模式: ${mock.totalCount} 个菜单项\n`;
report += ` 差异数量: ${differences.totalDifference}\n\n`;
if (differences.missingInMock.length > 0) {
report += `❌ Mock模式中缺失的菜单项 (${differences.missingInMock.length}个):\n`;
differences.missingInMock.forEach((item) => {
report += ` - ${item}\n`;
});
report += '\n';
}
if (differences.onlyInMock.length > 0) {
report += ` 仅在Mock模式中存在的菜单项 (${differences.onlyInMock.length}个):\n`;
differences.onlyInMock.forEach((item) => {
report += ` - ${item}\n`;
});
report += '\n';
}
report += `🔧 建议修复方案:\n`;
if (differences.missingInMock.length > 0) {
report += ` 1. 在Mock数据中添加缺失的 ${differences.missingInMock.length} 个菜单项\n`;
report += ` 2. 检查菜单权限配置是否一致\n`;
report += ` 3. 验证路由配置完整性\n`;
}
if (differences.totalDifference === 0) {
report += `✅ 前后端菜单完全一致,无需修复\n`;
}
return report;
}