修复计划接受流程的前后端状态不一致
This commit is contained in:
@@ -119,7 +119,12 @@
|
|||||||
- 改为服务端基于认证态校验,或强制 query/body 校验成员身份;
|
- 改为服务端基于认证态校验,或强制 query/body 校验成员身份;
|
||||||
- 前端补齐身份参数并确保不可伪造(推荐 token/cookie 方案)。
|
- 前端补齐身份参数并确保不可伪造(推荐 token/cookie 方案)。
|
||||||
|
|
||||||
### P2-2 “接受计划”前端逻辑乐观更新过早,且未检查 `res.ok`
|
### P2-2 “接受计划”前端逻辑乐观更新过早,且未检查 `res.ok`【已完成】
|
||||||
|
- 修复状态:✅ 已完成(2026-03-03)
|
||||||
|
- 修复内容:
|
||||||
|
- `handleAcceptPlan` 改为“后端成功后再更新 `planAccepted/activeContract`”;
|
||||||
|
- 增加 `res.ok` 检查与失败错误提示;
|
||||||
|
- 防止后端失败时前端误显示“已接受”。
|
||||||
- 证据:
|
- 证据:
|
||||||
- `src/hooks/useBlindboxPlan.ts:227-240`(先 `setPlanAccepted(true)`,请求后不判断 `res.ok`)
|
- `src/hooks/useBlindboxPlan.ts:227-240`(先 `setPlanAccepted(true)`,请求后不判断 `res.ok`)
|
||||||
- 影响:
|
- 影响:
|
||||||
|
|||||||
@@ -224,20 +224,30 @@ export function useBlindboxPlan(
|
|||||||
}, [profile, planDays, handlePlanDaysChange, toast]);
|
}, [profile, planDays, handlePlanDaysChange, toast]);
|
||||||
|
|
||||||
const handleAcceptPlan = useCallback(async () => {
|
const handleAcceptPlan = useCallback(async () => {
|
||||||
setPlanAccepted(true);
|
if (!planId || !profile) {
|
||||||
fireConfetti();
|
toast.show("计划信息不完整,请重新生成后再试");
|
||||||
if (planId && profile) {
|
return;
|
||||||
try {
|
}
|
||||||
const res = await fetch("/api/blindbox/plan", {
|
|
||||||
method: "PATCH",
|
try {
|
||||||
headers: { "Content-Type": "application/json" },
|
const res = await fetch("/api/blindbox/plan", {
|
||||||
body: JSON.stringify({ planId, userId: profile.id, action: "accept" }),
|
method: "PATCH",
|
||||||
});
|
headers: { "Content-Type": "application/json" },
|
||||||
const data = await res.json();
|
body: JSON.stringify({ planId, userId: profile.id, action: "accept" }),
|
||||||
setActiveContract({ id: planId, days: planDays, endTime: data.endTime ?? null });
|
});
|
||||||
} catch (e) { console.error("acceptPlan failed:", e); }
|
const data = await res.json().catch(() => ({}));
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new Error(data.error || "接受契约失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
setPlanAccepted(true);
|
||||||
|
setActiveContract({ id: planId, days: planDays, endTime: data.endTime ?? null });
|
||||||
|
fireConfetti();
|
||||||
|
toast.show("契约已接受!");
|
||||||
|
} catch (e) {
|
||||||
|
console.error("acceptPlan failed:", e);
|
||||||
|
toast.show(e instanceof Error ? e.message : "接受契约失败");
|
||||||
}
|
}
|
||||||
toast.show("契约已接受!");
|
|
||||||
}, [planId, profile, planDays, fireConfetti, toast]);
|
}, [planId, profile, planDays, fireConfetti, toast]);
|
||||||
|
|
||||||
const handleRegenerate = useCallback(() => {
|
const handleRegenerate = useCallback(() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user