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:
@@ -1,6 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect, useState, useCallback } from 'react';
|
||||
import { useEffect, useState, useCallback, useRef } from 'react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useAuth } from '@/components/admin/auth-context';
|
||||
import { adminApi } from '@/lib/admin-api';
|
||||
@@ -63,8 +63,11 @@ export default function NotificationsPage() {
|
||||
const [unreadOnly, setUnreadOnly] = useState(false);
|
||||
const [unreadCount, setUnreadCount] = useState(0);
|
||||
const pageSize = 20;
|
||||
// 单调递增的请求序号:只有最新一次请求的响应可以写进 state
|
||||
const fetchSeqRef = useRef(0);
|
||||
|
||||
const fetchNotifications = useCallback(async () => {
|
||||
const seq = ++fetchSeqRef.current;
|
||||
setLoading(true);
|
||||
setError('');
|
||||
try {
|
||||
@@ -80,12 +83,14 @@ export default function NotificationsPage() {
|
||||
pageSize: number;
|
||||
totalPages: number;
|
||||
}>(`/api/admin/notifications?${query.toString()}`);
|
||||
if (seq !== fetchSeqRef.current) return;
|
||||
setNotifications(data.items || []);
|
||||
setTotal(data.total || 0);
|
||||
} catch (err) {
|
||||
if (seq !== fetchSeqRef.current) return;
|
||||
setError(err instanceof Error ? err.message : '加载失败');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
if (seq === fetchSeqRef.current) setLoading(false);
|
||||
}
|
||||
}, [page, unreadOnly]);
|
||||
|
||||
@@ -164,7 +169,7 @@ export default function NotificationsPage() {
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
onClick={() => { setUnreadOnly(!unreadOnly); setPage(1); }}
|
||||
onClick={() => { setUnreadOnly((prev) => !prev); setPage(1); }}
|
||||
className={`flex items-center gap-2 px-3 py-2 rounded-lg text-sm font-medium border transition-colors ${
|
||||
unreadOnly
|
||||
? 'bg-dark-bg text-white border-border-dark'
|
||||
|
||||
Reference in New Issue
Block a user