fix(dispute): enforce participant checks and phase constraints

This commit is contained in:
zetaloop
2026-02-22 15:21:32 +08:00
parent ca95165e1b
commit 1f2dc1434b
2 changed files with 164 additions and 25 deletions
+39 -6
View File
@@ -17,6 +17,7 @@ import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { DISPUTE_TO_RESOLVED_MS } from "@/lib/config/demo-timers"
import { notifyInfo } from "@/lib/toast"
import { Label } from "@/components/ui/label"
import { Separator } from "@/components/ui/separator"
import { Textarea } from "@/components/ui/textarea"
@@ -108,13 +109,17 @@ export default function DisputePage({ params }: { params: Promise<{ id: string }
const handleSubmit = () => {
if (!userId || !userName || !reason.trim()) return
submitDispute({
const result = submitDispute({
orderId: id,
initiatorId: userId,
initiatorName: userName,
reason,
evidence: files,
})
if (!result.decision.ok) {
notifyInfo(result.decision.message ?? "提交争议失败")
return
}
router.replace(`/dispute/${id}?submitted=1`)
}
@@ -128,13 +133,26 @@ export default function DisputePage({ params }: { params: Promise<{ id: string }
)
}
const isParticipant = Boolean(
userId && (order.consumerId === userId || order.playerId === userId),
)
if (!isParticipant) {
return (
<div className="container mx-auto py-8 px-4 max-w-lg text-center text-muted-foreground">
访
</div>
)
}
if (existingDispute) {
const isInitiator = userId === existingDispute.initiatorId
const canRespond =
isParticipant &&
!isInitiator &&
!existingDispute.respondentReason &&
(existingDispute.status === "open" || existingDispute.status === "reviewing")
const canAppeal = existingDispute.status === "resolved" && !existingDispute.appealedAt
const canAppeal =
isParticipant && existingDispute.status === "resolved" && !existingDispute.appealedAt
return (
<div className="container mx-auto py-8 px-4 max-w-2xl">
@@ -267,9 +285,18 @@ export default function DisputePage({ params }: { params: Promise<{ id: string }
</div>
)}
<Button
onClick={() =>
submitResponse(existingDispute.id, responseReason, responseFiles)
}
onClick={() => {
if (!userId) return
const decision = submitResponse(
existingDispute.id,
userId,
responseReason,
responseFiles,
)
if (!decision.ok) {
notifyInfo(decision.message ?? "提交回应失败")
}
}}
disabled={!responseReason.trim()}
>
@@ -308,7 +335,13 @@ export default function DisputePage({ params }: { params: Promise<{ id: string }
/>
<Button
variant="outline"
onClick={() => submitAppeal(existingDispute.id, appealReason)}
onClick={() => {
if (!userId) return
const decision = submitAppeal(existingDispute.id, userId, appealReason)
if (!decision.ok) {
notifyInfo(decision.message ?? "提交申诉失败")
}
}}
disabled={!appealReason.trim()}
>