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
This commit is contained in:
2026-06-01 21:35:13 +08:00
parent 7000c186e2
commit afcd18c54f
77 changed files with 1498 additions and 2886 deletions
+26
View File
@@ -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()
+18
View File
@@ -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))
+11
View File
@@ -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 ()"
]
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 431 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 343 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 343 KiB

-75
View File
@@ -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
Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 343 KiB

Binary file not shown.

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
}
+41
View File
@@ -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
}