fix: evaluation report P0/P1/P2 fixes, remove Docker, add upload UI
Backend: - Add NotFoundException + BusinessException, return correct HTTP status (404/400) - Add @Index on reports.project_id and reports.upload_time - Add fileSize column to reports, populate on upload, return in DTO - Cascade delete: deleting project now removes all reports (DB + files + PDFs) - Delete report: also clean up pre-rendered PDF - File upload MIME validation (extension + Content-Type) - Remove duplicate @ExceptionHandler from ReportController - Switch from System.err to SLF4J logger - Handle MethodArgumentNotValid, MissingServletRequestPart, etc. Frontend: - Remove all Docker files (project uses 宝塔 panel deployment) - Upgrade axios 1.6.8 -> 1.7.7 (CVE-2024-39338) - Remove unused @vue-office/pptx + vue-demi (see CHANGELOG for rationale) - Fix vite proxy port 37821 -> 30081 - Remove mock data fallback in production - Add upload report UI (button + modal in ProjectDetail) - Add create project UI (button + modal in ProjectList) - Add filename search box in ProjectDetail - New useApi methods: createProject, uploadReport, deleteProject, deleteReport - FilePreview/ReportCard: show fileSize (was undefined before) Docs: - Add README.md (overview, quick start, structure) - Add CHANGELOG.md (full change log + pptx removal rationale) - Include EVALUATION_REPORT.md and blog-vibe-coding.md Tests: - All 73 backend tests pass - All 43 frontend tests pass - Updated test fixtures for new API contract
@@ -0,0 +1,26 @@
|
||||
from playwright.sync_api import sync_playwright
|
||||
|
||||
url = "https://www.1415243231.top/publish_dishboard"
|
||||
|
||||
with sync_playwright() as p:
|
||||
browser = p.chromium.launch(headless=True)
|
||||
page = browser.new_page(viewport={"width": 1280, "height": 720})
|
||||
|
||||
api_calls = []
|
||||
def on_response(r):
|
||||
if '/api/' in r.url:
|
||||
api_calls.append({'status': r.status, 'url': r.url})
|
||||
|
||||
page.on('response', on_response)
|
||||
page.goto(url, timeout=20000, wait_until='networkidle')
|
||||
page.wait_for_timeout(5000)
|
||||
|
||||
print("API calls made by frontend:")
|
||||
for r in api_calls:
|
||||
print(f" {r['status']} {r['url']}")
|
||||
|
||||
if not api_calls:
|
||||
print(" (no API calls made)")
|
||||
print("\nBody text:", page.inner_text('body')[:200])
|
||||
|
||||
browser.close()
|
||||
@@ -0,0 +1,18 @@
|
||||
import urllib.request, json
|
||||
|
||||
base = 'https://www.1415243231.top/api'
|
||||
|
||||
# Get all projects
|
||||
req = urllib.request.urlopen(base + '/projects')
|
||||
projects = json.loads(req.read())
|
||||
print('Found:', [(p['id'], p['name']) for p in projects])
|
||||
|
||||
# Delete each
|
||||
for p in projects:
|
||||
try:
|
||||
req = urllib.request.urlopen(
|
||||
urllib.request.Request(base + '/projects/' + str(p['id']), method='DELETE')
|
||||
)
|
||||
print('Deleted %s: %s (%s)' % (p['id'], p['name'], req.status))
|
||||
except Exception as e:
|
||||
print('Error deleting %s: %s' % (p['id'], e))
|
||||
@@ -0,0 +1,11 @@
|
||||
from playwright.sync_api import sync_playwright
|
||||
|
||||
url = "https://www.1415243231.top/publish_dishboard"
|
||||
with sync_playwright() as p:
|
||||
browser = p.chromium.launch(headless=True)
|
||||
page = browser.new_page(viewport={"width": 1280, "height": 720})
|
||||
page.goto(url, timeout=20000, wait_until='networkidle')
|
||||
page.wait_for_timeout(3000)
|
||||
page.screenshot(path="D:/Idea Project/publish/agent_test/screenshots/final_check.png", full_page=False)
|
||||
print("Text:", page.inner_text('body')[:300])
|
||||
browser.close()
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"width": 1280,
|
||||
"errors": [
|
||||
"Failed to load resource: the server responded with a status of 403 ()"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"width": 375,
|
||||
"errors": [
|
||||
"Failed to load resource: the server responded with a status of 403 ()"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"width": 768,
|
||||
"errors": [
|
||||
"Failed to load resource: the server responded with a status of 403 ()"
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 431 KiB |
|
After Width: | Height: | Size: 343 KiB |
|
After Width: | Height: | Size: 343 KiB |
@@ -1,75 +0,0 @@
|
||||
============================================================
|
||||
移动端响应式布局E2E测试
|
||||
测试目标: 验证手机端列表/预览切换功能
|
||||
Viewport: 375x667 (iPhone SE)
|
||||
============================================================
|
||||
============================================================
|
||||
启动浏览器 (iPhone SE viewport: 375x667)
|
||||
|
||||
============================================================
|
||||
Step 1: 导航到首页
|
||||
============================================================
|
||||
[Console] Failed to load resource: the server responded with a status of 500 (Internal Server Error)
|
||||
[Console] API not available, using mock data
|
||||
截图保存: D:\Idea Project\publish\agent_test\screenshots\01_home.png
|
||||
等待项目列表加载...
|
||||
|
||||
============================================================
|
||||
Step 2: 进入项目详情页
|
||||
============================================================
|
||||
尝试选择器: div.cursor-pointer (找到 3 个元素)
|
||||
[Console] Failed to load resource: the server responded with a status of 500 (Internal Server Error)
|
||||
[Console] API not available, using mock data
|
||||
[Console] Failed to load resource: the server responded with a status of 500 (Internal Server Error)
|
||||
[Console] API not available, using mock data
|
||||
成功点击元素 0: 项目一主要产品线15 份报告 未知时间...
|
||||
截图保存: D:\Idea Project\publish\agent_test\screenshots\02_project_page.png
|
||||
|
||||
============================================================
|
||||
Step 3: 验证报告列表
|
||||
============================================================
|
||||
列表选择器 '.glass-light': 1 个元素, 可见=True
|
||||
[OK] 报告列表已显示
|
||||
|
||||
============================================================
|
||||
Step 4: 点击报告进入预览模式
|
||||
============================================================
|
||||
页面文本长度: 148
|
||||
包含报告关键词的元素: 0
|
||||
尝试报告选择器: div.cursor-pointer.rounded-xl (找到 4 个)
|
||||
[Console] Failed to load resource: the server responded with a status of 500 (Internal Server Error)
|
||||
[Console] API not available, using mock data
|
||||
[OK] 点击了报告卡片: 2026-05-22 日报.htmlHTML15KB 2026-05-22...
|
||||
等待预览内容加载...
|
||||
截图保存: D:\Idea Project\publish\agent_test\screenshots\03_preview_mode.png
|
||||
|
||||
============================================================
|
||||
Step 5: 验证预览内容
|
||||
============================================================
|
||||
[OK] 发现可见的 iframe 元素 (第1个)
|
||||
预览页面内容长度: 182
|
||||
列表面板是否隐藏: True
|
||||
[OK] 移动端:列表面板已隐藏,预览模式激活
|
||||
|
||||
============================================================
|
||||
Step 6: 验证返回按钮
|
||||
============================================================
|
||||
查找移动端返回列表按钮...
|
||||
[OK] 找到移动端返回按钮: '返回列表'
|
||||
点击返回列表按钮
|
||||
截图保存: D:\Idea Project\publish\agent_test\screenshots\04_back_to_list.png
|
||||
|
||||
============================================================
|
||||
Step 7: 验证返回列表视图
|
||||
============================================================
|
||||
返回后页面内容长度: 148
|
||||
[OK] 回到了报告列表视图
|
||||
|
||||
============================================================
|
||||
Step 8: 截图对比分析
|
||||
============================================================
|
||||
图片对比: 03_preview_mode.png vs 04_back_to_list.png
|
||||
- 哈希值: 21adf0b2... vs 417cd742...
|
||||
- 相同: False
|
||||
[OK] 预览视图与列表视图存在明显差异(切换正常)
|
||||
最终截图保存: D:\Idea Project\publish\agent_test\screenshots\mobile-responsive-test.png
|
||||
|
After Width: | Height: | Size: 8.0 KiB |
|
After Width: | Height: | Size: 5.8 KiB |
|
After Width: | Height: | Size: 7.3 KiB |
|
After Width: | Height: | Size: 343 KiB |
|
After Width: | Height: | Size: 343 KiB |
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"url": "https://www.1415243231.top/publish_dishboard",
|
||||
"screenshot": "D:/Idea Project/publish/agent_test/screenshots/viewport_publish_final.png",
|
||||
"console_errors": [],
|
||||
"http_errors": [],
|
||||
"success": true
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"url": "https://www.1415243231.top",
|
||||
"screenshots": [
|
||||
{
|
||||
"width": 1280,
|
||||
"path": "D:\\Idea Project\\publish\\agent_test\\screenshots\\viewport_1280px.png",
|
||||
"size_bytes": 8146
|
||||
},
|
||||
{
|
||||
"width": 1280,
|
||||
"path": "D:\\Idea Project\\publish\\agent_test\\screenshots\\console_1280px.json",
|
||||
"type": "console"
|
||||
},
|
||||
{
|
||||
"width": 768,
|
||||
"path": "D:\\Idea Project\\publish\\agent_test\\screenshots\\viewport_768px.png",
|
||||
"size_bytes": 7464
|
||||
},
|
||||
{
|
||||
"width": 768,
|
||||
"path": "D:\\Idea Project\\publish\\agent_test\\screenshots\\console_768px.json",
|
||||
"type": "console"
|
||||
},
|
||||
{
|
||||
"width": 375,
|
||||
"path": "D:\\Idea Project\\publish\\agent_test\\screenshots\\viewport_375px.png",
|
||||
"size_bytes": 5907
|
||||
},
|
||||
{
|
||||
"width": 375,
|
||||
"path": "D:\\Idea Project\\publish\\agent_test\\screenshots\\console_375px.json",
|
||||
"type": "console"
|
||||
}
|
||||
],
|
||||
"errors": [
|
||||
"[1280px] Failed to load resource: the server responded with a status of 403 ()",
|
||||
"[768px] Failed to load resource: the server responded with a status of 403 ()",
|
||||
"[375px] Failed to load resource: the server responded with a status of 403 ()"
|
||||
],
|
||||
"success": false
|
||||
}
|
||||