Files
novalon-website/IMPECCABLE_AUDIT.md

39 KiB
Raw Permalink Blame History

Impeccable 复审报告(代码更新后)

复审日期:2026-08-29 第一轮:/audit 6/20(Poor) 第二轮(代码更新后):14/20Good 第三轮(执行修复后):17/20Good

代码库已发生重大重构:字体体系重建、设计令牌重写、effects/ 目录整体删除、 深色模式移除、页面改由 CMS 驱动(新增 components/contentcomponents/detaillib/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.ttf4,412,684 B)已移除。 新增 src/app/fonts/ 仅 140KBgeist-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:401text-4xl sm:text-5xl font-black 章节序号(01/02/03
    • select.tsx:25,28 / textarea.tsx:17placeholder: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#64748B4.76:1)。placeholder 直接用 text-muted

⚠️ [P1] 图片优化仍关闭,且改为无条件

  • 位置next.config.mjs:8 images.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:26lib/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.woff269KB+ geist-mono.woff271KB全站 0 引用—— @font-face 未声明,也没有 next/font/local 导入。是孤立资源。

🆕 [P2] 安全与缓存头回退

上轮配置里的 Strict-Transport-SecurityHSTSmax-age=63072000)与 静态资源 Cache-Control: immutable 规则在新配置中消失。 新配置新增了 CSP(含 unsafe-inline/unsafe-evalNext.js 水合所需,可接受)。


正面发现(本轮新增,值得肯定)

  1. 敢删东西——effects/ 整个目录、4.4MB 字体、深色模式半实现,全部删除而非修修补补。这比加功能更需要判断力。
  2. 深色模式选择了"彻底移除"而非"凑合补全",不留中间态,符合工程洁癖。
  3. 令牌体系真正落地——71 个语义化令牌 + Tailwind config 映射(font-sans/text-ink/bg-brand),硬编码从"普遍"降到 29 处 / 13 文件。
  4. optimizePackageImports 新增 framer-motion
  5. 收紧了图像配置——dangerouslyAllowSVGremotePatterns: '**' 均已移除。
  6. Hero 有明确的克制意图(代码注释直接写明),不是被动简化。
  7. 叙事化章节序号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-placeholder4.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-hinttext-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.pngqrcode.webpwechat-business-qr.webp), 且这 3 张都存在。测试断言的是 src 字符串,不需要真实文件——不构成生产问题。

但底层担忧(CMS 图片失效会裂图)成立,因此改为修健壮性:

文件 改动
sections/insight-card.tsx <img> → CSS background-image404 时静默降级,加 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.tsxdetail-cta-section.tsxstandalone/[id]/client.tsx 使用, 属有意的产品主题变体,不是漂移。

4. next.config.mjs:确认非 bug

更正CLAUDE.md:189 明确记载 「Images are unoptimized (static export limitation)」,且存在 docs/CDN_CONFIGURATION.mddocs/CDN_QUICK_START.md。这是 Nginx 静态托管 + CDN 分发的架构决定,不是缺陷

仅补注释说明意图,并标注 formatsunoptimized: 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.tsxui/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:49CookieConsent.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:251terms:196 CTA 渐变末端失效 --color-brand-section
--color-flip-card-bg ui/flip-clock.tsx5 处) 翻页钟卡片背景失效 --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:21brand-visuals.tsx:58hero-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.tsxnext/font/local 挂载 --font-geist-sans/monoglobals.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 中 CrossRecommendGridBrandSeal/CalligraphyText/SectionHeader 三组 0 消费者(生产引用为 0)的重新导出已移除;组件文件 brand-elements.tsxdetail-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/nb4npm run build exit=0Compiled successfully + TypeScript 通过 + 全部页面数据收集成功
  • 此前 page-data 收集失败的根因链(已定案并修复):
    1. .env.localDATABASE_URL=file:./data/novalon.db 为相对路径,构建期解析落空 → Unable to open the database file
    2. 改绝对路径后数据库可打开,但本地 data/novalon.db2026-04 旧库)是旧 schema:只有 content/site_config/users/audit_logs/content_versions 表,无 Prisma schema 的 ContentItem 等 10 张表 → P2021: table main.ContentItem does not exist
    3. 修复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=externalimport.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.jsoninclude 追加(绝对化后的)临时目录条目。构建后需: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-upgradeERP升级专题) 已加 修复:孤儿营销页(详见上);站内入口待产品决策
/products/erp-upgrade-v3 有意 内部迭代/dogfood 页,保持现状
/products/standalone/[id] 有意 externalUrl 外链机制(成熟产品跳独立子域名站),设计使然
/privacy/terms 惯例 footerStaticLink 法律页惯例不进 sitemapfooter 有入口,OK
/test-error-tracking 修复:QA 测试页此前可被搜索引擎收录(robots 未屏蔽),已重构为 server wrapper + metadata.robots: noindex
/admin/* robots.txt disallowOK
/api/* robots.txt disallowOK

泛化检查结论:全站非 admin 路由逐一对照完毕,孤儿/SEO 问题共 3 处(erp-upgrade、about/brand、test-error-tracking),已全部修复;其余有意排除项均经核实有据。

剩余留档项(非缺陷,需产品决策)

  1. erp-upgrade-v3(内部迭代页,保持现状)——经核实为"ERP 产品页 V3 深度打磨版"(meta 为内部打磨描述),与项目 dogfood-* 目录同属内部验证产物。不进 sitemap、站内无入口是有意设计,无需修改。
  2. /products/erp-upgrade(孤儿营销页,已修复)——"ERP升级专题"是正规营销页,但此前站内零入口且不进 sitemap(搜索引擎无法发现)。已加入 sitemap.tspriority 0.7 / monthly);站内导航入口仍建议由产品确认(products 页无现成位置,改动涉及页面设计)。
  3. /about/brand 站内入口——sitemap 已补(priority 0.6 / monthly);站内入口建议通过 about 页 CMS 字段(hero*CtaHref)配置,无需代码改动。
  4. 根目录 data.db(遗留旧库,已被 .gitignore 正确处理)——2026-04 旧库(57KB),源码零引用, 且从未被 git 跟踪.gitignore:97 已忽略,初判"已跟踪"系误读 check-ignore 输出所致)。无需任何 git 操作; 本地遗留文件可自行删除或归档。
  5. 本地 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-bluenavy 是有意的产品主题变体

教训:看到硬编码色值不等于色板漂移——要先比对令牌定义再下结论; 看到"缺失资源"也要先区分测试夹具与生产引用


七、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.tsxconst NEWS = news ?? [] 包裹为 useMemo(() => news ?? [], [news])(消除 ?? [] 的每次渲染不稳定引用);② admin-layout.tsxgetExpandedMenusForPath 为组件内函数(引用随渲染变化),effect 已有 prevPathname 变更保护 → 行内豁免 + 注释说明,避免加依赖破坏逻辑

方案说明:未引入 next/link。项目为 Nginx 静态托管 + CDN 架构,StaticLinke.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 认证后数据获取 / 动画的标准模式,改动风险高

验证

  • eslint0 errors / 125 warnings(剩余全部为上述留档项)
  • tsc --noEmit0 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.3min0 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:静态质量门禁(前轮已验证,本轮改动后复跑无新增)

  • eslint0 errors / 125 warnings
  • tsc --noEmit0 errors

结论

四条链路全绿,本轮 17 文件 JSX 改动无任何渲染级回归。 项目处于可发布状态;剩余项均为已留档技术债(middleware 弃用约定、4 类 lint 留档)与产品决策项(站内入口配置),非阻塞。


九、剩余项闭环(第六轮,2026-08-31)

第五轮结论中挂账的 2 类剩余项(middleware 弃用技术债 + 站内入口产品决策项)本轮全部处理完毕:

1. middleware → proxy 迁移(技术债清除)

Next 16 弃用 middleware 文件约定。迁移方式为纯机械改动:

改动 内容
src/middleware.tssrc/proxy.ts git mv 保留历史
导出函数 export async function middlewareexport async function proxy
config.matcher 保留 ['/admin/:path*'] 不变
引用面 全仓仅自身文件,无测试依赖,零连带改动

验证

  • 构建日志中弃用警告消失(此前必现 ⚠ The "middleware" file convention is deprecated
  • 编译产物(dist/server/chunks/[root-of-the-server]*.js)含 proxy 函数与 admin/loginnovalon_token JWT 验证逻辑,/admin 保护逻辑完整编译
  • 全量 tsc 0 errorseslint 改动文件 0/0
  • 静态页 63/63
  • Next 16 内部产物文件仍命名 middleware.js/middleware-manifest.json(框架内部实现细节,不影响功能与约定)

2. /products/erp-upgrade 站内入口(产品决策,已拍板)

审计第三轮标记"站内入口待产品确认"。经产品拍板采用详情页 CTA 方案

  • 改动product-detail-content-v3.tsx CTASection 按钮组下方新增条件渲染入口(仅 product.id === 'erp' 显示):
    正在使用旧版 ERP? 了解睿新 ERP 升级专题 →
    
    链接 /products/erp-upgrade,次级入口样式(text-text-secondary + text-brand 链接),不喧宾夺主。
  • 闭环:升级专题页本已有反向链接(erp-upgrade-content-v2.tsx:414/products/erp「查看产品详情」),至此双向互链闭环
  • 验证eslint 0/0products 相关测试 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(原 middlewareNext 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.tssrc/components/Hero.tsxsrc/wasm/ink-filter/

涉及多份 docs 逐条核实与更新,且部分需产品确认(如 wasm ink-filter 是否历史删除),建议作为独立"文档一致性"批次处理。

已核实无遗漏

  • .env.example 已含 JWT_SECRET / JWT_REFRESH_SECRET 占位值(与构建注入一致)
  • cross-references.test.ts 14/14 通过(CTA 新链接不破坏数据驱动一致性)
  • next.config.mjs 改动为历史合理项(NEXT_DIST_DIR 覆盖 + images 注释)
  • ESLint 两改动文件 0/0tsc 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.mdtest-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.localDATABASE_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.localJWT_SECRET / JWT_REFRESH_SECRET src/lib/auth.ts:11 模块级 throw,任何导入 auth 链的路由 500(第二隐患) 补占位值(与 .env.example 一致)

排查路径(可复用)

  1. lsof -i :3000 确认 dev server 在跑(用户实例)→ 非端口冲突
  2. curl 路由探测:营销页 500 / admin 200 → 定位到 DB 依赖层
  3. dist/dev/logs/next-development.logNext 16 dev 日志落盘位置)→ 直接命中 PrismaClientInitializationError: Error code 14
  4. 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-upgrade 200、/about 200、/products/erp 200、/admin/login 200
  • 首页标题/描述渲染正常(「四川睿新致远科技有限公司 - 企业数字化转型服务商」)
  • 附带发现:Tailwind 4 条 ambiguous warningsduration-[var(--transition-*)] / ease-[var(--ease-ink)]),非报错,记录留档