39 KiB
Impeccable 复审报告(代码更新后)
复审日期:2026-08-29
第一轮:/audit 6/20(Poor)
第二轮(代码更新后):14/20(Good)
第三轮(执行修复后):17/20(Good)
代码库已发生重大重构:字体体系重建、设计令牌重写、
effects/目录整体删除、 深色模式移除、页面改由 CMS 驱动(新增components/content、components/detail、lib/cms)。 第二轮为全量重新审计;第三轮为按建议执行修复后的复核。
第三轮 · 修复记录
见文末「附录:修复记录与审计更正」。
评分对比
| 维度 | 上轮 | 本轮 | 变化 | 关键 |
|---|---|---|---|---|
| Accessibility | 2 | 3 | +1 | 3 处对比度违规修好 2 处,剩 text-hint |
| Performance | 1 | 3 | +2 | 4.4MB 字体删除、强制重排归零、profiling 移除 |
| Responsive | 2 | 2 | — | 两项建议未采纳 |
| Theming | 1 | 3 | +2 | 令牌体系落地,硬编码从普遍降到 29 处 |
| Anti-Patterns | 0 | 3 | +3 | 渐变文字/spring/bounce/特效库全部清除 |
| 合计 | 6 | 14 | +8 | Poor → Good |
Anti-Patterns 判定:由「未通过」转为「通过」
上一轮点名的 7 项生效中的套路,本轮逐一复查:
| 特征 | 上轮 | 本轮 | 验证 |
|---|---|---|---|
| 渐变文字 | 生效中 | 0 处 | grep bg-clip-text = 0 |
| Spring 回弹缓动 | 43 处 | 0 处 | grep type:'spring' = 0 |
| Bounce 缓动 | 生效中 | 0 处使用 | CounterWithEffect 组件仍在 lib/animations.tsx:852,但引用数为 0(死代码) |
| 特效组件过载 | 20+ 个 | 目录已删除 | components/effects/ = 0 文件 |
| Hero 指标网格 | 4 格统计 | 已拆解 | Hero 改为「克制白底 + 品牌红点缀」;仅 why-us-section 保留 1 项指标 |
| 动 width 布局动画 | 2 处关键帧 | 0 | expandWidth/typewriter 已移除 |
| 毛玻璃 | header 全宽 | 收敛为 14 处局部 | 主要是对话框遮罩 bg-ink/60 backdrop-blur-sm,属标准模态用法 |
Hero 区注释直接写明意图:// ===== Hero 区:克制白底 + 品牌红点缀 =====
——这是有意识的设计决策,不是碰巧。
结论:上一轮判定的"AI 套路层"已被系统性剥离,底下真实的品牌观点(水墨 + 朱砂红)现在看得见了。
已修复(按重要性)
✅ 4.4MB 字体阻塞 → 删除(上轮 P0-1)
public/fonts/AoyagiReisho.ttf(4,412,684 B)已移除。
新增 src/app/fonts/ 仅 140KB(geist-sans + geist-mono woff2)。
书法字体改为系统楷体栈,不再下载:
--font-brand: "STKaiti", "KaiTi", "楷体", "SimKai", serif;
这是本轮最大的一笔收益。
✅ 中文字体栈补齐(上轮 P1-5,我上轮的判断被推翻并修好)
--font-sans: "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei",
"Noto Sans SC", -apple-system, BlinkMacSystemFont, ...;
CJK 字体栈完整,跨平台字形可控。
✅ 滚动强制重排 → 归零(上轮 P1-1)
header.tsx 中的 offsetTop/offsetHeight 循环读取已完全移除,
不再有每滚动事件 12 次强制同步布局。
✅ 深色模式 → 彻底移除(上轮 P0-3)
采纳了我给的选项二。验证结果:
layout.tsx无.dark注入脚本- 全站
dark:变体 0 处 ThemeProvider已删除themeColor仅保留#FFFFFF
干净利落,没有留下中间态。
✅ 生产 profiling 移除(上轮 P1-2)
✅ 6 个悬空 CSS 变量 → 0(上轮 P1-8,通过删除死代码)
✅ 对比度违规修好 2/3
| 上轮 FAIL | 本轮 |
|---|---|
Hero 描述 #718096 4.02:1 |
→ slate 令牌 4.76+ PASS |
页脚 #718096 3.68:1 |
→ PASS |
统计占位 #CBD5E0 1.49:1 |
→ 仍 FAIL(见下) |
✅ 图片缺失 20 张 → 8 张
仍然存在的问题
⚠️ [P1] text-hint 1.48:1,且用在有实际含义的内容上
- 位置:
globals.css:66--color-text-hint: #CBD5E1 - 使用处:
home-content-v14.tsx:401—text-4xl sm:text-5xl font-black章节序号(01/02/03)select.tsx:25,28/textarea.tsx:17—placeholder:text-text-hint
- 实测:1.48:1(AA 要求 4.5:1,大字也要求 3:1)→ 双重不达标
- 影响:章节序号几乎不可见;表单 placeholder 同样。
- 注意:这个 token 比上轮的
#CBD5E0只差最后一位,本质上同一个问题换了个名字。 - 标准:WCAG 2.1 AA 1.4.3
- 建议:若序号是装饰性的 → 加
aria-hidden并淡化得更彻底;若承载信息 → 改--color-text-muted(#64748B,4.76:1)。placeholder 直接用text-muted。
⚠️ [P1] 图片优化仍关闭,且改为无条件
- 位置:
next.config.mjs:8images.unoptimized: true - 变化:上轮是
!isDev(开发环境还优化),现在生产环境也完全不优化。 同文件formats: ['image/avif','image/webp']依然是死配置。 - 说明:如果是有意走 CDN 优化(
assetPrefix+CDN_DOMAIN的存在支持这个猜测), 那这是合理选择——但formats配置项应当一并删除,否则会误导后来者。 - 建议:确认意图。若走 CDN → 删掉
formats并加注释说明;否则改false。
⚠️ [P1] 8 张图片仍缺失(裂图)
/images/cases/manufacturing.jpg /images/erp.jpg
/images/insights/trends.jpg /images/news/cms.webp
/images/news/fallback.webp /images/news/test.webp
/images/product.png /images/products/erp.jpg
news/fallback.webp 缺失尤其危险——兜底图本身不存在,意味着兜底机制是失效的。
⚠️ [P2] 移动端全局 44px 规则未改
- 位置:
globals.css:1122-1126 -
a:not(nav[aria-label="breadcrumb"] a), button { min-height: 44px; min-width: 44px; } - 仅排除了面包屑,正文行内链接仍会被撑成 44px 方块。
⚠️ [P2] 根字号 16/17/18px 缩放未改
- 位置:
globals.css:343-359 - 所有 rem(即整个 Tailwind 尺寸体系)在断点处放大 12.5%。
⚠️ [P2] 新增越界色,色板又开始漂移
| 颜色 | 位置 | 对比度 |
|---|---|---|
#d97706 amber |
detail/brand-elements.tsx:28,30 |
3.19 FAIL |
#2563eb blue |
detail/brand-elements.tsx:34,36 |
5.17 PASS |
#3b82f6 / #eff6ff |
detail-cross-recommend.tsx:16,17 |
— |
#1e3a5f navy |
erp-upgrade-v3/page.tsx:26、lib/constants/hero-themes.ts:29 |
— |
上一轮清理掉冷蓝灰后,蓝色和琥珀色又从 detail 组件里长了出来。
#d97706 同时还是个对比度 FAIL。
新发现(上轮未覆盖)
🆕 [P1] 生产代码 70 个 TypeScript 错误
src/app/(marketing)/cases/[slug]/page.tsx(16,21) TS7006 隐式 any
src/app/(marketing)/news/[slug]/page.tsx(13,21) TS7006
src/app/(marketing)/products/[id]/page.tsx(13,21) TS7006
src/app/(marketing)/services/[id]/page.tsx(13,21) TS7006
src/app/(marketing)/solutions/[id]/page.tsx(14,21) TS7006
src/app/api/admin/models/route.ts(17,32) TS7006
...共 70 处
另有测试文件大量 TS2305: '@testing-library/react' has no exported member 'screen'——
说明 @testing-library/react 依赖缺失或版本不匹配,测试套件可能跑不起来。
🆕 [P2] 140KB 字体文件无引用
src/app/fonts/geist-sans.woff2(69KB)+ geist-mono.woff2(71KB)全站 0 引用——
@font-face 未声明,也没有 next/font/local 导入。是孤立资源。
🆕 [P2] 安全与缓存头回退
上轮配置里的 Strict-Transport-Security(HSTS,max-age=63072000)与
静态资源 Cache-Control: immutable 规则在新配置中消失。
新配置新增了 CSP(含 unsafe-inline/unsafe-eval,Next.js 水合所需,可接受)。
正面发现(本轮新增,值得肯定)
- 敢删东西——
effects/整个目录、4.4MB 字体、深色模式半实现,全部删除而非修修补补。这比加功能更需要判断力。 - 深色模式选择了"彻底移除"而非"凑合补全",不留中间态,符合工程洁癖。
- 令牌体系真正落地——71 个语义化令牌 + Tailwind config 映射(
font-sans/text-ink/bg-brand),硬编码从"普遍"降到 29 处 / 13 文件。 optimizePackageImports新增framer-motion。- 收紧了图像配置——
dangerouslyAllowSVG、remotePatterns: '**'均已移除。 - Hero 有明确的克制意图(代码注释直接写明),不是被动简化。
- 叙事化章节序号(01/02/03 + hairline grid)替代了 4 格指标堆砌,信息架构更成熟——只是序号的对比度需要修。
建议下一步(按性价比排序)
| 优先级 | 动作 | 理由 |
|---|---|---|
| 1 | 修 text-hint(改用 text-muted 或加 aria-hidden) |
1 行改动,消除唯一 AA 违规 |
| 2 | 补齐 8 张图,优先 news/fallback.webp |
兜底图缺失 = 兜底失效 |
| 3 | 确认 unoptimized: true 是否刻意为 CDN 让路 |
若刻意,删掉 formats 死配置以免误导 |
| 4 | 清掉 3 处越界色(#d97706 尤其,它 FAIL) |
防止色板二次漂移 |
| 5 | 修 70 个 TS 错误 + 测试库依赖 | 类型安全是后续重构的地基 |
| 6 | 删除 140KB 无用字体,或补 next/font/local 接入 |
要么用,要么删 |
| 7 | 恢复 HSTS 与静态资源缓存头 | 安全与性能的低成本回归 |
| 8 | 移动端 44px 改为精确选择器、根字号固定 16px | 上轮建议,仍未采纳 |
前 4 项预计 2 小时内可完成,能直接把分数推到 16-17 区间。 修复后重跑
/audit复核。
一句话总结
从 6 分到 14 分,主要靠做减法——删掉 4.4MB 字体、20+ 特效组件、整层 AI 套路和半吊子深色模式。 剩下的失分集中在三类:一个换名未换实的对比度 token、尚未补完的图片资源、以及新长出来的色板漂移。 这些都是小修,地基已经稳了。
附录:修复记录与审计更正
一、第三轮评分:14 → 17
| 维度 | 二轮 | 三轮 | 变化 |
|---|---|---|---|
| Accessibility | 3 | 4 | 全部对比度违规清零(含新发现的状态色问题) |
| Performance | 3 | 3 | 图片项为误判;补 sizes 后维持 |
| Responsive | 2 | 2 | 未改动(两项建议待定) |
| Theming | 3 | 4 | 越界色全部令牌化,新增状态色文字变体 |
| Anti-Patterns | 3 | 3 | 维持 |
| 合计 | 14 | 17 |
二、已执行的修复
1. text-hint 对比度(1.48:1 → 合规)
根因不是配色,是用错了令牌——系统里早有 --color-text-placeholder(4.76:1),
组件却拿 text-hint 当占位符用。修正如下:
| 文件 | 改动 |
|---|---|
globals.css:66-68 |
--color-text-hint 由 #CBD5E1(1.48:1) 改为 #7C8CA5(3.41:1),并加注释限定「仅 ≥24px 大号文本」 |
ui/input.tsx:18 |
placeholder:text-text-hint → text-text-placeholder |
ui/select.tsx:25,28 |
同上(含 data-[placeholder]:) |
ui/textarea.tsx:17 |
同上 |
sections/why-us-section.tsx:177 |
10px 小字 → text-muted |
layout/mobile-menu.tsx:104 |
12px 小字 → text-muted |
home-content-v14.tsx:401 |
装饰性章节序号加 aria-hidden="true",保留 text-hint |
后三处是我二轮漏检的:
text-hint还被用在 10px/12px 的小字上, 即便按新值 3.41:1 也对正文不达标。
2. 图片项:确认是误判,改为修健壮性
更正:所谓「8 张缺失图片」全部只出现在 .test.tsx 测试夹具里。
严格排除测试文件后,生产代码仅引用 3 张图(beian-icon.png、qrcode.webp、wechat-business-qr.webp),
且这 3 张都存在。测试断言的是 src 字符串,不需要真实文件——不构成生产问题。
但底层担忧(CMS 图片失效会裂图)成立,因此改为修健壮性:
| 文件 | 改动 |
|---|---|
sections/insight-card.tsx |
<img> → CSS background-image,404 时静默降级,加 aria-hidden |
sections/case-card.tsx |
同上 |
detail/product-card.tsx |
补 sizes(上轮标记的 P2-8) |
3. 越界色清理
更正:二轮把 #3b82f6 判为越界是错的——它正是设计系统的 --color-accent-blue。
真正越界的只有 BrandSeal 里的 gold/blue 变体,而该组件零引用。
| 文件 | 改动 |
|---|---|
detail/brand-elements.tsx |
移除 gold(#d97706, 3.19:1 FAIL) 与 blue(#2563eb) 变体,仅保留品牌红;硬编码改令牌 |
detail/brand-elements.tsx:63 |
修 ${color.border}10 拼接——改令牌后会产生 var(...)10 无效值,改用 rgba(var(--color-brand-rgb), 0.06) |
detail/detail-cross-recommend.tsx |
三变体全部改令牌:accent-blue / brand / accent-purple |
hero-themes.ts 的 navy(#1e3a5f/#1e40af)保留——经查该文件被
detail-hero.tsx、detail-cta-section.tsx、standalone/[id]/client.tsx 使用,
属有意的产品主题变体,不是漂移。
4. next.config.mjs:确认非 bug
更正:CLAUDE.md:189 明确记载
「Images are unoptimized (static export limitation)」,且存在 docs/CDN_CONFIGURATION.md、
docs/CDN_QUICK_START.md。这是 Nginx 静态托管 + CDN 分发的架构决定,不是缺陷。
仅补注释说明意图,并标注 formats 在 unoptimized: true 下不生效,避免后人误改。
三、修复中新发现的问题(已一并修复)
状态色整体不满足文字对比度
审计时发现一个系统性盲区:所有状态色都是按图形用途调的,却被当作文字色使用。
| 令牌 | 色值 | 白底对比度 | 判定 |
|---|---|---|---|
--color-success |
#16A34A |
3.30:1 | FAIL |
--color-warning |
#D97706 |
3.19:1 | FAIL |
--color-info |
#3B82F6 |
3.68:1 | FAIL |
--color-error |
#C41E3A |
5.84:1 | PASS |
影响面:ui/alert.tsx、ui/badge.tsx(小到 10px)、ui/product-card.tsx 状态徽章、ui/sonner.tsx。
修法:新增三个「文字专用」令牌,图形用途保持原色不变。
| 新令牌 | 色值 | 白底 | 于对应浅底 |
|---|---|---|---|
--color-success-text |
#15803D |
5.02:1 | 4.79:1 |
--color-warning-text |
#B45309 |
5.02:1 | 4.84:1 |
--color-info-text |
#1D4ED8 |
6.70:1 | 6.16:1 |
已在 tailwind.config.js 注册为 success.text / warning.text / info.text,
并应用于 alert、badge、product-card、sonner。
11 个悬空 CSS 变量引用(导致边框/网格/悬停态不渲染)
全站 47 个 var(--color-*) 引用中有 11 个从未定义。这类变量不会报错,
但对应样式静默失效——是"看起来没坏、实际没生效"的典型。
| 悬空变量 | 影响位置 | 实际后果 | 修法 |
|---|---|---|---|
--color-border |
privacy/page.tsx:139,142-144,147 |
Cookie 表格边框全部不渲染 | → --color-border-primary |
--color-border-primary-rgb |
detail/list-page-hero.tsx:66 |
网格背景线不渲染 | 新增令牌 226, 232, 240 |
--color-brand-lighter |
mobile-menu.tsx:49、CookieConsent.tsx:163 |
悬停背景失效 | → --color-brand-bg |
--color-challenge-isolation-hover |
not-found-content.tsx:50,60,70,80 |
悬停背景失效 | → --color-brand-soft |
--color-hero-dark-end |
privacy:251、terms:196 |
CTA 渐变末端失效 | → --color-brand-section |
--color-flip-card-bg |
ui/flip-clock.tsx(5 处) |
翻页钟卡片背景失效 | → --color-bg-secondary |
--color-flip-card-border |
同上 | 边框失效 | → --color-border-primary |
--color-flip-card-divider |
同上 | 分隔线失效 | → --color-border-primary |
--color-flip-card-divider-subtle |
同上 | 次级分隔线失效 | → --color-border-light |
--color-accent-cyan / -rgb |
ui/product-card.tsx:21、brand-visuals.tsx:58、hero-section-v2.tsx:177 |
第 4 个强调色不渲染 | 新增令牌 #06B6D4 + rgb |
修复后复检:悬空引用 0 / 39 个唯一引用。
检测方法(可复用):
grep -rhoE "var\(--color-[a-z0-9-]+\)" src/ --include='*.tsx' --include='*.css' \ | sed 's/var(--//;s/)//' | sort -u > /tmp/refs.txt while read v; do grep -q -- "--$v:" src/app/globals.css || echo "悬空: --$v"; done < /tmp/refs.txt
四、最终对比度全景
| 令牌 | 色值 | 对比度 | 要求 | 判定 |
|---|---|---|---|---|
| text-primary | #0A0E14 |
19.34:1 | 4.5 | PASS |
| text-secondary | #334155 |
10.35:1 | 4.5 | PASS |
| text-tertiary | #475569 |
7.58:1 | 4.5 | PASS |
| text-muted / subtle / placeholder | #64748B |
4.76:1 | 4.5 | PASS |
| text-hint(大字专用) | #7C8CA5 |
3.41:1 | 3.0 | PASS |
| brand | #C41E3A |
5.84:1 | 4.5 | PASS |
| success-text | #15803D |
5.02:1 | 4.5 | PASS |
| warning-text | #B45309 |
5.02:1 | 4.5 | PASS |
| info-text | #1D4ED8 |
6.70:1 | 4.5 | PASS |
| error | #C41E3A |
5.84:1 | 4.5 | PASS |
全站 WCAG AA 对比度违规:0。
五、收尾修复记录(三轮评审后的最终批次)
原"遗留事项"清单已全部处置完毕,逐项结论如下:
| # | 原遗留项 | 处置结果 |
|---|---|---|
| 1 | [P1] 70 个 TS 错误 | 已解决(误判根因)。错误源于 npm 依赖缺失破坏类型推断,而非代码缺陷。补齐依赖后 tsc 0 错误,构建 TS 检查 32.9s 通过 |
| 2 | [P2] 测试库缺导出 | 已解决。同一依赖问题;screen/waitFor/fireEvent 已验证存在 |
| 3 | [P2] Geist 字体 0 引用 | 已解决。layout.tsx 经 next/font/local 挂载 --font-geist-sans/mono,globals.css 字体栈已接 var(--font-geist-sans, ...) |
| 4 | [P2] 移动端全局 44px + 根字号缩放 | 已修复。44px 规则收窄至 @media (max-width:768px) 下的 nav/header/footer/menu 交互元素,不再撑大正文行内链接;根字号删除 640px→17px、1024px→18px 两档缩放,固定 16px,恢复 rem 缩放对用户浏览器字号设置的响应 |
| 5 | [P3] Tailwind v3 透明度修饰符失效 | 已修复。tailwind.config.js 全部颜色令牌改写为 rgb(var(--color-*-rgb) / <alpha-value>) 形式(含 border.* 六项——代码中 border-brand/30 等 54 处实际用法的兜底),globals.css 补齐对应 -rgb 通道令牌;prefers-contrast: more 无障碍覆写同步补写 --color-border-primary-rgb: 0,0,0。已用 Tailwind CLI 探针验证:bg-brand/10 → rgb(var(--color-brand-rgb) / 0.1)、border-brand/30 等均正确编译 |
| 6 | [P3] 死代码 | 已删除。detail/index.ts barrel 中 CrossRecommendGrid、BrandSeal/CalligraphyText/SectionHeader 三组 0 消费者(生产引用为 0)的重新导出已移除;组件文件 brand-elements.tsx、detail-cross-recommend.tsx 于 2026-08-31 删除;detail.test.tsx 中仅针对该组件的 5 个测试用例与专用 mock 一并删除(tsc 全量 0 错误复验)。barrel 与测试文件均附留档注释。erp-upgrade-v3 经核实是真实路由(/products/erp-upgrade-v3,构建产物 ○ Static 可直达 URL),属信息架构/SEO 问题而非死代码:保留(产品决策项,可选:补充内链入口) |
最终构建验证
- ✅ 完整生产构建通过(
/tmp/nb4,npm run buildexit=0):Compiled successfully+ TypeScript 通过 + 全部页面数据收集成功 - 此前 page-data 收集失败的根因链(已定案并修复):
.env.local的DATABASE_URL=file:./data/novalon.db为相对路径,构建期解析落空 →Unable to open the database file- 改绝对路径后数据库可打开,但本地
data/novalon.db(2026-04 旧库)是旧 schema:只有content/site_config/users/audit_logs/content_versions表,无 Prisma schema 的ContentItem等 10 张表 →P2021: table main.ContentItem does not exist - 修复:
prisma migrate reset --force重建 schema(旧库备份为data/novalon.db.bak-20260830)→ seed 填充(16 模型 / 38 条目 / 6 区域 / admin)→ 构建全绿
- 注意:本机
tsx/ts-node均不可用(esbuild 0.28.1 需 macOS 12+,本机 11.7)。seed 改用已装的 esbuild 0.18.20 打包prisma/seed.ts为 ESM(--packages=external,import.meta 兼容)后以 node 运行;如再遇db:seed失败可复用此方案(脚本.tmp-seed/seed.mjs已清理) - middleware 弃用警告:Next 16.3 已将其识别为 "ƒ Proxy (Middleware)",构建无警告,无需迁移
- Tailwind 透明度转换已产物级验证(Tailwind CLI 编译探针,非仅构建通过)
- 测试套件:1616 通过 / 0 失败 / 2 跳过(128 套件全绿;死代码清理移除了 5 个 CrossRecommendGrid 用例)。
insight-card、case-card 两处断言原查找
<img src>,已随组件改为 CSS 背景图同步更新断言 ([style*="background-image"]+toHaveStyle);删除后tsc --noEmit全量复验 0 错误 - 注意事项:
NEXT_DIST_DIR传绝对路径会被 Next.js 规范化为项目内相对路径(如传/tmp/nb4实际写入./tmp/nb4), 且会向tsconfig.json的include追加(绝对化后的)临时目录条目。构建后需:git checkout tsconfig.json还原 + 删除项目内tmp/产物 (本次已多次触发并清理,最多时 449MB)。不传NEXT_DIST_DIR时产物落在默认dist/,同样注意 tsconfig 污染
全站路由 × sitemap × 站内入口对照(泛化检查)
| 路由 | sitemap | 站内入口 | 结论 | |
|---|---|---|---|---|
/、/about、/services、/products、/solutions、/cases、/news、/team、/methodology、/contact |
✅ | ✅ | OK | |
/[id] 系列(services/products/solutions/cases/news) |
✅ CMS slug | ✅ 列表页 | OK | |
/about/brand(品牌故事) |
❌→✅ 已加 | ❌ | 修复:正规内容页此前站内零入口且不进 sitemap;入口可经 CMS 配置(about 页 hero*CtaHref 字段) |
|
/products/erp-upgrade(ERP升级专题) |
❌→✅ 已加 | ❌ | 修复:孤儿营销页(详见上);站内入口待产品决策 | |
/products/erp-upgrade-v3 |
❌ 有意 | ❌ | 内部迭代/dogfood 页,保持现状 | |
/products/standalone/[id] |
❌ 有意 | ❌ | externalUrl 外链机制(成熟产品跳独立子域名站),设计使然 | |
/privacy、/terms |
❌ 惯例 | ✅ footer(StaticLink) | 法律页惯例不进 sitemap,footer 有入口,OK | |
/test-error-tracking |
❌ | ❌ | 修复:QA 测试页此前可被搜索引擎收录(robots 未屏蔽),已重构为 server wrapper + metadata.robots: noindex |
|
/admin/* |
❌ | ❌ | robots.txt disallow,OK | |
/api/* |
❌ | ❌ | robots.txt disallow,OK |
泛化检查结论:全站非 admin 路由逐一对照完毕,孤儿/SEO 问题共 3 处(erp-upgrade、about/brand、test-error-tracking),已全部修复;其余有意排除项均经核实有据。
剩余留档项(非缺陷,需产品决策)
erp-upgrade-v3(内部迭代页,保持现状)——经核实为"ERP 产品页 V3 深度打磨版"(meta 为内部打磨描述),与项目 dogfood-* 目录同属内部验证产物。不进 sitemap、站内无入口是有意设计,无需修改。/products/erp-upgrade(孤儿营销页,已修复)——"ERP升级专题"是正规营销页,但此前站内零入口且不进 sitemap(搜索引擎无法发现)。已加入sitemap.ts(priority 0.7 / monthly);站内导航入口仍建议由产品确认(products 页无现成位置,改动涉及页面设计)。/about/brand站内入口——sitemap 已补(priority 0.6 / monthly);站内入口建议通过 about 页 CMS 字段(hero*CtaHref)配置,无需代码改动。- 根目录
data.db(遗留旧库,已被 .gitignore 正确处理)——2026-04 旧库(57KB),源码零引用, 且从未被 git 跟踪(.gitignore:97已忽略,初判"已跟踪"系误读 check-ignore 输出所致)。无需任何 git 操作; 本地遗留文件可自行删除或归档。 - 本地
data/novalon.db已重置重建——旧数据备份于data/novalon.db.bak-20260830;如需找回旧内容可从备份迁移。
修复总览(三轮累计)
- WCAG AA 对比度违规:0
- 悬空 CSS 变量引用:0 / 39
- 透明度修饰符失效(静默样式失败):~140 处恢复生效
- 综合评分:6/20(首轮)→ 14/20(二轮)→ 17/20(三轮收尾)
六、三处我判断错了的地方(留档)
审计要可复核,所以把自己判错的部分一并记下:
| # | 我的判断 | 实际情况 |
|---|---|---|
| 1 | 「8 张图片缺失,兜底机制失效」 | 全是测试夹具;生产代码 3 张图全部存在 |
| 2 | 「unoptimized: true 是缺陷」 |
架构决定,CLAUDE.md 有明确记载 + 三份 CDN 文档 |
| 3 | 「#3b82f6 等属色板二次漂移」 |
#3b82f6 正是设计系统的 accent-blue;navy 是有意的产品主题变体 |
教训:看到硬编码色值不等于色板漂移——要先比对令牌定义再下结论; 看到"缺失资源"也要先区分测试夹具与生产引用。
七、Lint 专项修复(第四轮,2026-08-31)
补跑 eslint(前三轮未覆盖该维度):基线 0 errors / 149 warnings。 本轮针对"可修且真实收益"的三类规则清零,其余全部留档并说明理由。
已清零
| 规则 | 前 | 后 | 修法 | |
|---|---|---|---|---|
@next/next/no-html-link-for-pages |
21 | 0 | 组件内硬编码站内 <a href="/xxx"> 统一改为项目自研 <StaticLink>(17 文件 / 39 处 + 11 个文件补 import)。锚点 href="#..." 与动态/外链(mailto、https、模板变量)保留 |
|
jsx-a11y/alt-text |
1 | 0 | admin/media/page.tsx 空状态 lucide Image 图标(装饰性 SVG,非 <img>,LucideProps 不接受 alt)→ aria-hidden + 豁免注释 |
|
react-hooks/exhaustive-deps |
2 | 0 | ① news-content-v3.tsx:const NEWS = news ?? [] 包裹为 useMemo(() => news ?? [], [news])(消除 ?? [] 的每次渲染不稳定引用);② admin-layout.tsx:getExpandedMenusForPath 为组件内函数(引用随渲染变化),effect 已有 prevPathname 变更保护 → 行内豁免 + 注释说明,避免加依赖破坏逻辑 |
方案说明:未引入
next/link。项目为 Nginx 静态托管 + CDN 架构,StaticLink(e.preventDefault()整页导航,阻止 RSC 拦截)是既有设计决策(footer / product-card 已采用),<a>→<StaticLink>与架构一致且满足 lint。顺带修正 home-content-v14 中 3 处此前 lint 漏报的静态站内链接。
留档(有意或低收益,不改)
| 规则 | 数量 | 理由 | |
|---|---|---|---|
no-console |
56 | analytics.ts、示例组件、Sentry 配置、seed 脚本——诊断/配置用途合理 | |
@typescript-eslint/no-explicit-any |
45 | 类型收紧改动面大、收益低、风险高(e2e 测试占比高) | |
@next/next/no-img-element |
20 | images.unoptimized: true 静态托管架构下 next/image 不可用(CLAUDE.md:189 记录);含测试夹具 |
|
react-hooks/set-state-in-effect |
13 | React 19 新规则偏激进;多数为 hydration mounted / admin 认证后数据获取 / 动画的标准模式,改动风险高 |
验证
- eslint:0 errors / 125 warnings(剩余全部为上述留档项)
tsc --noEmit:0 errors- 修改文件累计 47(前三轮 31 + 本轮 16)
八、完整测试验证(第五轮,2026-08-31)
本轮改动 17 文件 JSX(<a> → <StaticLink>、useMemo 包裹、aria-hidden 修),tsc/eslint 仅保证类型与静态规则,渲染级回归(SSR/水合、<Button asChild> 包裹下的 StaticLink)需全量测试 + 生产构建确认。据此执行完整验证,四条链路全部闭环:
链路 1:生产构建(standalone)
npm run build(注入 DATABASE_URL 绝对路径 + JWT 占位值 + CODEBUDDY_SAFE_DELETE_ENABLED=0)
| 阶段 | 结果 |
|---|---|
| 编译 | ✓ 3.7min |
| TypeScript | ✓ 6.3min,0 errors |
| 静态页生成 | ✓ 63/63 in 5.3s |
| 页面优化 | ✓ 58s |
| 路由表 | ✓ /about/brand、/products/erp-upgrade、/test-error-tracking、/sitemap.xml 均在 |
已知非阻塞警告:(已在本轮第六轮迁移为middleware文件约定已弃用proxy,见第九轮)
链路 2:构建产物抽查(SEO 关键点)
| 检查项 | 结果 |
|---|---|
/test-error-tracking 预渲染 HTML 含 noindex |
✓ |
sitemap.xml 含 /about/brand + /products/erp-upgrade |
✓ |
robots.txt 产物存在 |
✓ |
链路 3:全量 Jest(限内存防 OOM)
npx jest --maxWorkers=2 --workerIdleMemoryLimit=512MB
- Test Suites: 128 passed, 128 total
- Tests: 2 skipped, 1616 passed, 1618 total(与基线完全一致,0 失败 0 回归)
- Time: 805s(比预估 26min 快,限内存参数生效、无 OOM)
链路 4:静态质量门禁(前轮已验证,本轮改动后复跑无新增)
- eslint:0 errors / 125 warnings
tsc --noEmit:0 errors
结论
四条链路全绿,本轮 17 文件 JSX 改动无任何渲染级回归。 项目处于可发布状态;剩余项均为已留档技术债(middleware 弃用约定、4 类 lint 留档)与产品决策项(站内入口配置),非阻塞。
九、剩余项闭环(第六轮,2026-08-31)
第五轮结论中挂账的 2 类剩余项(middleware 弃用技术债 + 站内入口产品决策项)本轮全部处理完毕:
1. middleware → proxy 迁移(技术债清除)
Next 16 弃用 middleware 文件约定。迁移方式为纯机械改动:
| 改动 | 内容 |
|---|---|
src/middleware.ts → src/proxy.ts |
git mv 保留历史 |
| 导出函数 | export async function middleware → export async function proxy |
config.matcher |
保留 ['/admin/:path*'] 不变 |
| 引用面 | 全仓仅自身文件,无测试依赖,零连带改动 |
验证:
- ✅ 构建日志中弃用警告消失(此前必现
⚠ The "middleware" file convention is deprecated) - ✅ 编译产物(
dist/server/chunks/[root-of-the-server]*.js)含proxy函数与admin/login、novalon_tokenJWT 验证逻辑,/admin保护逻辑完整编译 - ✅ 全量 tsc 0 errors;eslint 改动文件 0/0
- ✅ 静态页 63/63
- ℹ️ Next 16 内部产物文件仍命名
middleware.js/middleware-manifest.json(框架内部实现细节,不影响功能与约定)
2. /products/erp-upgrade 站内入口(产品决策,已拍板)
审计第三轮标记"站内入口待产品确认"。经产品拍板采用详情页 CTA 方案:
- 改动:
product-detail-content-v3.tsxCTASection按钮组下方新增条件渲染入口(仅product.id === 'erp'显示):链接正在使用旧版 ERP? 了解睿新 ERP 升级专题 →/products/erp-upgrade,次级入口样式(text-text-secondary+text-brand链接),不喧宾夺主。 - 闭环:升级专题页本已有反向链接(
erp-upgrade-content-v2.tsx:414→/products/erp「查看产品详情」),至此双向互链闭环。 - 验证:eslint 0/0;products 相关测试 2 套件 / 21 测试全过(含 CTA 链接测试);构建 63/63。
3. /about/brand 站内入口(CMS 配置,零代码)
已核实 about-content-v4.tsx 支持 CMS 字段 heroPrimaryCtaHref / heroSecondaryCtaHref(默认 #)。入口可通过 about 页 CMS 配置 heroSecondaryCtaHref: '/about/brand' 实现,无需代码改动。
剩余挂账(均为有意项,不再处理)
- 4 类 lint 留档(no-console / no-explicit-any / no-img-element / set-state-in-effect),理由见第七轮。
/products/erp-upgrade-v3内部迭代页(有意排除,不进 sitemap)。
结论
六轮审计(设计 17/20 → SEO 泛化 → lint 专项 → 完整测试 → 剩余项闭环)全部完成。 挂账项清零:技术债 0、产品决策项已闭环(1 个拍板实施 + 1 个 CMS 配置指引)、留档项均为有意设计。项目处于可发布状态。
十、自检补充:文档一致性(第七轮,2026-08-31)
应"还有什么遗漏吗"追问做全站自检,发现并处理 文档-代码漂移 类问题:
已修复(随本次改动直接相关)
| 文件 | 问题 | 修复 |
|---|---|---|
docs/test-strategy-plan.md:65 |
项目结构树仍列 middleware.ts # Next.js 中间件(第六轮已迁移 proxy) |
改为 proxy.ts # Next.js Proxy(原 middleware,Next 16 弃用约定;/admin 路由 JWT 校验) |
docs/guides/SECURITY.md |
声称"项目实现了" 6 项安全机制(验证码/限流/清理/日志/安全中间件/监控),但 src/lib/security/ 零实现 |
文档头部加失实警示:标注为设计蓝图/参考,非实际防护能力;指向实际生效的 JWT 认证(proxy.ts / auth.ts) |
docs/testing/TESTING_REPORT.md:32-38 |
声称 6 个 src/lib/security/*.test.ts 测试存在,实际全不存在 |
加失实标注:历史规划记录,勿作实际覆盖依据 |
系统性发现(建议独立批次治理,未在本轮处理)
docs 引用的代码路径经全量扫描,约 20+ 处指向不存在的文件,分两类:
- 重构后旧路径(文档未随重构更新):
api/admin/content/route.ts(现为api/admin/items)、src/components/cms/*(现仅 RichTextEditor)、src/components/detail-v2/*、src/components/detail/*等 - 彻底失实(功能不存在):
api/admin/security/route.ts、src/components/Hero.tsx、src/wasm/ink-filter/等
涉及多份 docs 逐条核实与更新,且部分需产品确认(如 wasm ink-filter 是否历史删除),建议作为独立"文档一致性"批次处理。
已核实无遗漏
- ✅
.env.example已含JWT_SECRET/JWT_REFRESH_SECRET占位值(与构建注入一致) - ✅
cross-references.test.ts14/14 通过(CTA 新链接不破坏数据驱动一致性) - ✅
next.config.mjs改动为历史合理项(NEXT_DIST_DIR 覆盖 + images 注释) - ✅ ESLint 两改动文件 0/0;tsc 0 errors;构建 63/63
- ⚠️
product-detail-content-v3.tsx无专属测试文件(CTA 改动由构建 + products 21 测试覆盖,可接受) - ⚠️ git 有 48 个文件改动未提交(六轮累计,含本轮),建议提交或审查
十一、文档一致性治理(第八轮,2026-08-31)
第七轮发现 docs 约 132/245(54%)路径引用指向不存在的文件。本轮全量扫描 + 分类治理:
扫描结论:缺失分三类
| 类别 | 数量 | 性质 | 处理 |
|---|---|---|---|
| 归档类(`docs/superpowers/plans | specs/、docs/adr/、docs/plans/*、OPTIMIZATION_REPORT.md、test-coverage-improvement-plan.md`) |
~112 | 历史设计规划 / ADR 决策记录 / 历史报告,引用当时组件属正常属性 |
| 时效性文档(描述当前架构) | 20 | 文档与重构后代码不一致 | 已全部处理(见下) |
时效性文档处理(5 份)
| 文档 | 问题 | 处理 |
|---|---|---|
docs/test-strategy-plan.md:65 |
结构树 middleware.ts(已迁移 proxy) |
✅ 路径直接更新 → proxy.ts(唯一 0 缺失) |
docs/testing.md |
描述 e2e/src/ 分层 + Page Object 模式,实际为扁平 spec 结构(e2e/*.spec.ts + playwright.config.ts) |
✅ 头部加结构时效警示(保留作 Playwright 使用参考) |
docs/cms/api-contract.md |
前端对接 SDK src/lib/cms/client.ts / mock-data.ts 不存在(现为 data-server.ts) |
✅ 加路径时效注(API 契约主体仍有效) |
docs/guides/SECURITY.md |
声称实现 6 项安全机制,实际 src/lib/security/ 零实现 |
✅(第七轮)加失实警示,降级为设计蓝图 |
docs/testing/TESTING_REPORT.md |
声称 6 个 security 测试存在,实际全不存在 | ✅(第七轮)加失实标注 |
治理策略说明
- 归档类不改的理由:superpowers/plans、specs、adr 是带时间戳的历史记录(如
2026-04-28-phase3-webgpu-ppr-wasm.md),引用已重构/移除的组件(gsap/lenis/webgpu/ink 等)是记录的固有属性,改写会破坏归档真实性。 - 时效性文档采用"标注优于改写":除可确定的新路径(middleware→proxy)外,重写级差异(testing.md 605 行、api-contract.md 950 行)不擅自重构,加警示标注说明现状,具体重写留待专项。
验证
- 5 份时效性文档复扫:test-strategy-plan 0 缺失,其余 4 份引用保留但已全部标注(符合预期)。
- 剩余 ~112 条缺失全部归属归档类文档,确认无需处理。
- 本轮仅改 md 文档,无代码/构建影响。
十二、本地运行故障修复(第九轮,2026-08-31)
用户报告本地 npm run dev 报错(营销页 500/502),且此前测试未覆盖。排查修复闭环:
根因(双重环境配置问题)
| # | 根因 | 影响 | 修复 |
|---|---|---|---|
| 1 | .env.local 的 DATABASE_URL=file:./data/novalon.db 为相对路径,Prisma SQLite 按 schema 目录(prisma/)解析 → 指向不存在的 prisma/data/novalon.db |
PrismaClientInitializationError: Error code 14: Unable to open the database file → 所有依赖 DB 的营销页 500 |
改为绝对路径 file:/Users/zhangxiang/Codes/Novalon/novalon-website/data/novalon.db(与构建期注入一致) |
| 2 | .env.local 缺 JWT_SECRET / JWT_REFRESH_SECRET |
src/lib/auth.ts:11 模块级 throw,任何导入 auth 链的路由 500(第二隐患) |
补占位值(与 .env.example 一致) |
排查路径(可复用)
lsof -i :3000确认 dev server 在跑(用户实例)→ 非端口冲突- curl 路由探测:营销页 500 / admin 200 → 定位到 DB 依赖层
- 读
dist/dev/logs/next-development.log(Next 16 dev 日志落盘位置)→ 直接命中PrismaClientInitializationError: Error code 14 prisma.config.ts揭示 Prisma 6 用 dotenv 只读.env(非.env.local),但 Next dev 读.env.local→ 确认路径解析是 Prisma 引擎层行为
测试盲区确认(用户核心关切)
src/lib/db.test.ts注释:"PrismaClient is already mocked in jest.setup.js"——Jest 全量 mock PrismaClient,单元测试永不触及真实 DB 连接与路径解析 → 此类环境配置问题只能靠 dev/prod 运行冒烟验证。- 补救建议(可选后续):新增真实连接冒烟脚本(
scripts/verify-db.mjs,绝对路径连库 + count 查询),纳入 CI 前置检查。
验证
- dev server 重启后全路由探测:
/200、/products/erp-upgrade200、/about200、/products/erp200、/admin/login200 - 首页标题/描述渲染正常(「四川睿新致远科技有限公司 - 企业数字化转型服务商」)
- 附带发现:Tailwind 4 条 ambiguous warnings(
duration-[var(--transition-*)]/ease-[var(--ease-ink)]),非报错,记录留档