fix(qa): 第五轮系统性质量保障缺陷修复 + 单元/集成测试

安全/鉴权:登出清服务端 httpOnly 令牌、登录 IP 限流前置 bcrypt、停用账号
令牌在 6 个内联 role handler 失效、admin/items 存在性 oracle 认证前置、
同意偏好 sanitize fail-closed、api-crypto 按 HTTP 方法判体。

CMS:workflow 状态更新+审计收进交互式 $transaction(失败即回滚)、编辑器
richtext 走 textarea 防换行损毁、array/object 子字段按作用域读写防污染、
about/contact/erp-upgrade 补 ISR revalidate 与路由映射。

UI/a11y:AnimatedCounter 入视口前显起点、CLS 累计上报、MetricCard 动画入
effect 并卸载取消 rAF、后台状态四态映射/搜索防抖+序号守卫/媒体单 input/
吞错改横幅/表单 label-aria 关联。

新增对应判别性单测与 pagination/rate-limit/client-ip/sanitize/validate-content-data
等模块及 tests-integration 真库集成层。
This commit is contained in:
2026-09-28 10:48:07 +08:00
parent 040951c0a3
commit a366bd1400
224 changed files with 8938 additions and 5579 deletions
+18 -18
View File
@@ -112,7 +112,7 @@ const FALLBACK_FOUNDER_QUOTE = {
// 排版约束:3 行均势(17 / 18 / 13 字),避开"量。"孤儿字。
// 配合 blockquote 的 whitespace-pre-line 渲染换行。
text: '我们相信,数字化不是一次性项目,\n而是持续迭代的能力建设。\n每一份投入,都应当经得起衡量。',
name: 'Novalon 创始团队',
name: '睿新致远创始团队',
role: '技术咨询 · 数字化转型同行者',
};
@@ -255,7 +255,7 @@ function HeroSection({ heroData }: {
className={`group inline-flex items-center gap-2 text-sm font-medium transition-colors duration-200 py-4 ${dark ? 'text-white/70 hover:text-white' : 'text-text-secondary hover:text-ink'}`}
>
{secondaryCtaLabel}
<ArrowRight className="w-4 h-4 transition-transform duration-300 group-hover:translate-x-1" />
<ArrowRight className="w-4 h-4 transition-transform duration-normal group-hover:translate-x-1" />
</a>
)}
</motion.div>
@@ -394,7 +394,7 @@ function NarrativeSection({ pageCopy }: { pageCopy?: HomePageCopy | null }) {
<div className="grid md:grid-cols-3 gap-px bg-border-primary">
{NARRATIVE_ACTS_CMS.map((act) => (
<StaggerReveal key={act.number} staggerDelay={0.05}>
<div className="group relative h-full bg-bg-primary p-8 sm:p-10 md:p-12 transition-colors duration-300 hover:bg-bg-secondary">
<div className="group relative h-full bg-bg-primary p-8 sm:p-10 md:p-12 transition-colors duration-normal hover:bg-bg-secondary">
<div className="flex items-start justify-between mb-8">
<span
aria-hidden="true"
@@ -403,7 +403,7 @@ function NarrativeSection({ pageCopy }: { pageCopy?: HomePageCopy | null }) {
{act.number}
</span>
</div>
<h3 className="text-xl lg:text-2xl font-bold text-ink mb-4 group-hover:text-brand-ink transition-colors duration-300">
<h3 className="text-xl lg:text-2xl font-bold text-ink mb-4 group-hover:text-brand-ink transition-colors duration-normal">
{act.title}
</h3>
<p className="text-text-secondary leading-relaxed mb-8 text-sm">
@@ -415,7 +415,7 @@ function NarrativeSection({ pageCopy }: { pageCopy?: HomePageCopy | null }) {
className="group/link inline-flex items-center min-h-6 gap-2 text-sm font-semibold text-text-secondary group-hover:text-brand-ink transition-colors"
>
<span>{act.cta}</span>
<ArrowRight className="w-4 h-4 transition-transform duration-300 group-hover/link:translate-x-1" />
<ArrowRight className="w-4 h-4 transition-transform duration-normal group-hover/link:translate-x-1" />
</a>
</div>
</div>
@@ -464,7 +464,7 @@ function InsightsSection({ pageCopy }: { pageCopy?: HomePageCopy | null }) {
className="group inline-flex items-center min-h-6 gap-3 text-ink font-semibold text-sm"
>
查看全部洞察
<ArrowRight className="w-4 h-5 transition-transform duration-300 group-hover:translate-x-2 text-text-secondary group-hover:text-brand-ink" />
<ArrowRight className="w-4 h-5 transition-transform duration-normal group-hover:translate-x-2 text-text-secondary group-hover:text-brand-ink" />
</StaticLink>
</ScrollReveal>
</div>
@@ -474,7 +474,7 @@ function InsightsSection({ pageCopy }: { pageCopy?: HomePageCopy | null }) {
<StaggerReveal key={i} staggerDelay={0.05}>
<a
href={insight.href}
className="group flex flex-col h-full bg-bg-primary p-8 sm:p-10 md:p-12 transition-colors duration-300 hover:bg-bg-secondary"
className="group flex flex-col h-full bg-bg-primary p-8 sm:p-10 md:p-12 transition-colors duration-normal hover:bg-bg-secondary"
>
<div className="flex items-center justify-between mb-8">
<span className="text-[13px] tracking-[0.12em] font-semibold text-brand-ink">
@@ -484,7 +484,7 @@ function InsightsSection({ pageCopy }: { pageCopy?: HomePageCopy | null }) {
<span className="text-xs text-text-muted tabular-nums">{insight.date}</span>
)}
</div>
<h3 className="text-xl lg:text-2xl font-bold text-ink mb-4 leading-[1.25] group-hover:text-brand-ink transition-colors duration-300">
<h3 className="text-xl lg:text-2xl font-bold text-ink mb-4 leading-[1.25] group-hover:text-brand-ink transition-colors duration-normal">
{insight.title}
</h3>
<p className="text-text-secondary leading-relaxed mb-8 text-sm flex-1">
@@ -493,7 +493,7 @@ function InsightsSection({ pageCopy }: { pageCopy?: HomePageCopy | null }) {
<div className="pt-6 border-t border-border-primary">
<span className="inline-flex items-center gap-2 text-sm font-semibold text-text-secondary group-hover:text-brand-ink transition-colors">
了解详情
<ArrowRight className="w-4 h-4 transition-transform duration-300 group-hover:translate-x-1" />
<ArrowRight className="w-4 h-4 transition-transform duration-normal group-hover:translate-x-1" />
</span>
</div>
</a>
@@ -543,7 +543,7 @@ function ServicesSection({ services, pageCopy }: { services?: Record<string, any
</div>
<p className="text-text-secondary leading-relaxed mb-8 text-sm">{service.desc}</p>
<div className="flex flex-wrap gap-2 mb-8">
{service.highlights.map((h: string, j: number) => (
{(Array.isArray(service.highlights) ? service.highlights : []).map((h: string, j: number) => (
<span key={j} className="px-3 py-1.5 text-xs text-text-muted border border-border-primary font-medium">
{h}
</span>
@@ -565,7 +565,7 @@ function ServicesSection({ services, pageCopy }: { services?: Record<string, any
<div className="mt-auto pt-2">
<div className="inline-flex items-center gap-2 text-sm font-semibold text-text-secondary group-hover:text-brand-ink transition-colors">
<span>了解详情</span>
<ArrowRight className="w-4 h-4 transition-transform duration-300 group-hover:translate-x-1" />
<ArrowRight className="w-4 h-4 transition-transform duration-normal group-hover:translate-x-1" />
</div>
</div>
</div>
@@ -728,7 +728,7 @@ function CasesSection({ cases, pageCopy }: { cases?: Record<string, any>[]; page
className="group inline-flex items-center min-h-6 gap-3 text-ink font-semibold text-sm"
>
查看全部案例
<ArrowRight className="w-4 h-5 transition-transform duration-300 group-hover:translate-x-2 text-text-secondary group-hover:text-brand-ink" />
<ArrowRight className="w-4 h-5 transition-transform duration-normal group-hover:translate-x-2 text-text-secondary group-hover:text-brand-ink" />
</StaticLink>
</ScrollReveal>
</div>
@@ -780,7 +780,7 @@ function CasesSection({ cases, pageCopy }: { cases?: Record<string, any>[]; page
<div className="mt-auto pt-6 border-t border-white/10 flex items-center justify-between gap-6 flex-wrap">
<span className="inline-flex items-center gap-2 text-sm font-semibold text-white group-hover:text-brand-light transition-colors">
查看完整案例
<ArrowRight className="w-4 h-4 transition-transform duration-300 group-hover:translate-x-1" />
<ArrowRight className="w-4 h-4 transition-transform duration-normal group-hover:translate-x-1" />
</span>
</div>
</div>
@@ -825,7 +825,7 @@ function CasesSection({ cases, pageCopy }: { cases?: Record<string, any>[]; page
</h3>
<div className="grid grid-cols-3 gap-4 mb-8">
{caseItem.metrics.slice(0, 3).map((metric: Record<string, any>, j: number) => (
{(Array.isArray(caseItem.metrics) ? caseItem.metrics : []).slice(0, 3).map((metric: Record<string, any>, j: number) => (
<div key={j}>
<div className={cn(
'text-xl sm:text-2xl font-black leading-none mb-2',
@@ -843,7 +843,7 @@ function CasesSection({ cases, pageCopy }: { cases?: Record<string, any>[]; page
<div className="mt-auto pt-4 border-t border-border-primary">
<div className="inline-flex items-center gap-2 text-sm font-semibold text-text-secondary group-hover:text-brand-ink transition-colors">
<span>了解详情</span>
<ArrowRight className="w-4 h-4 transition-transform duration-300 group-hover:translate-x-1" />
<ArrowRight className="w-4 h-4 transition-transform duration-normal group-hover:translate-x-1" />
</div>
</div>
</a>
@@ -884,7 +884,7 @@ function NewsSection({ news, pageCopy }: { news?: NewsItem[]; pageCopy?: HomePag
className="group inline-flex items-center min-h-6 gap-3 text-ink font-semibold text-sm"
>
{ctaLabel}
<ArrowRight className="w-4 h-5 transition-transform duration-300 group-hover:translate-x-2 text-text-secondary group-hover:text-brand-ink" />
<ArrowRight className="w-4 h-5 transition-transform duration-normal group-hover:translate-x-2 text-text-secondary group-hover:text-brand-ink" />
</StaticLink>
</ScrollReveal>
</div>
@@ -905,7 +905,7 @@ function NewsSection({ news, pageCopy }: { news?: NewsItem[]; pageCopy?: HomePag
{item.date}
</span>
</div>
<h3 className="text-lg lg:text-xl font-bold text-ink mb-4 leading-[1.3] line-clamp-2 group-hover:text-brand-ink transition-colors duration-300">
<h3 className="text-lg lg:text-xl font-bold text-ink mb-4 leading-[1.3] line-clamp-2 group-hover:text-brand-ink transition-colors duration-normal">
{item.title}
</h3>
<p className="text-sm text-text-secondary leading-relaxed mb-6 line-clamp-2 flex-1">
@@ -914,7 +914,7 @@ function NewsSection({ news, pageCopy }: { news?: NewsItem[]; pageCopy?: HomePag
<div className="pt-4 border-t border-border-primary">
<span className="inline-flex items-center gap-2 text-sm font-semibold text-text-secondary group-hover:text-brand-ink transition-colors">
阅读全文
<ArrowRight className="w-4 h-4 transition-transform duration-300 group-hover:translate-x-1" />
<ArrowRight className="w-4 h-4 transition-transform duration-normal group-hover:translate-x-1" />
</span>
</div>
</StaticLink>