114 lines
3.3 KiB
TypeScript
114 lines
3.3 KiB
TypeScript
import { generateId } from "@/lib/id"
|
|
import { mockReviews, mockUsers } from "@/lib/mock"
|
|
import { allow, deny } from "@/lib/policy/assert"
|
|
import type { PolicyDecision } from "@/lib/policy/decision"
|
|
import type { Review } from "@/lib/types"
|
|
import { useOrderStore } from "@/store/orders"
|
|
import { create } from "zustand"
|
|
|
|
interface SubmitReviewInput {
|
|
orderId: string
|
|
fromUserId: string
|
|
rating: number
|
|
content?: string
|
|
}
|
|
|
|
interface ReviewState {
|
|
reviews: Review[]
|
|
submitReview: (input: SubmitReviewInput) => PolicyDecision
|
|
getReviewsByOrder: (orderId: string) => Review[]
|
|
hasUserReviewed: (orderId: string, userId: string) => boolean
|
|
}
|
|
|
|
function resolveUser(userId: string) {
|
|
return mockUsers.find((user) => user.id === userId)
|
|
}
|
|
|
|
function resolveOrderUser(orderId: string, userId: string) {
|
|
const order = useOrderStore.getState().orders.find((item) => item.id === orderId)
|
|
if (!order) return null
|
|
|
|
if (order.consumerId === userId) {
|
|
return {
|
|
fromUserName: order.consumerName,
|
|
toUserId: order.playerId,
|
|
toUserName: order.playerName,
|
|
}
|
|
}
|
|
|
|
if (order.playerId === userId) {
|
|
return {
|
|
fromUserName: order.playerName,
|
|
toUserId: order.consumerId,
|
|
toUserName: order.consumerName,
|
|
}
|
|
}
|
|
|
|
return null
|
|
}
|
|
|
|
export const useReviewStore = create<ReviewState>((set, get) => ({
|
|
reviews: mockReviews,
|
|
getReviewsByOrder: (orderId) => get().reviews.filter((review) => review.orderId === orderId),
|
|
hasUserReviewed: (orderId, userId) =>
|
|
get().reviews.some((review) => review.orderId === orderId && review.fromUserId === userId),
|
|
submitReview: (input) => {
|
|
if (!input.fromUserId) {
|
|
return deny("AUTH_REQUIRED", "请先登录")
|
|
}
|
|
|
|
if (!Number.isFinite(input.rating) || input.rating < 1 || input.rating > 5) {
|
|
return deny("VALIDATION_FAILED", "评分范围应为 1-5")
|
|
}
|
|
|
|
const order = useOrderStore.getState().orders.find((item) => item.id === input.orderId)
|
|
if (!order) {
|
|
return deny("NOT_FOUND", "订单不存在")
|
|
}
|
|
|
|
if (order.status !== "pending_review") {
|
|
return deny("INVALID_STATUS", "仅待评价订单可提交评价")
|
|
}
|
|
|
|
const relation = resolveOrderUser(input.orderId, input.fromUserId)
|
|
if (!relation) {
|
|
return deny("NOT_PARTICIPANT", "仅订单参与方可评价")
|
|
}
|
|
|
|
const exists = get().hasUserReviewed(input.orderId, input.fromUserId)
|
|
if (exists) {
|
|
return deny("ALREADY_DONE", "该订单已提交过评价")
|
|
}
|
|
|
|
const fromUser = resolveUser(input.fromUserId)
|
|
const createdAt = new Date().toISOString()
|
|
|
|
const review: Review = {
|
|
id: generateId("rev"),
|
|
orderId: input.orderId,
|
|
fromUserId: input.fromUserId,
|
|
fromUserName: relation.fromUserName,
|
|
fromUserAvatar: fromUser?.avatar ?? "",
|
|
toUserId: relation.toUserId,
|
|
rating: input.rating,
|
|
content: input.content?.trim() || undefined,
|
|
sealed: true,
|
|
createdAt,
|
|
}
|
|
|
|
set((state) => ({ reviews: [...state.reviews, review] }))
|
|
|
|
const orderReviews = get().getReviewsByOrder(input.orderId)
|
|
if (orderReviews.length >= 2) {
|
|
set((state) => ({
|
|
reviews: state.reviews.map((item) =>
|
|
item.orderId === input.orderId ? { ...item, sealed: false } : item,
|
|
),
|
|
}))
|
|
useOrderStore.getState().autoTimeoutPendingReview(input.orderId)
|
|
}
|
|
|
|
return allow()
|
|
},
|
|
}))
|