refactor(store): adapt Zustand stores to backend-aligned types

- orders: remove name fields from creation, keep dispatchMode logic
- chat: remove readonly/senderName/senderAvatar references
- reviews: remove fromUserAvatar/toUserId, simplify participant check
- disputes: remove initiatorId/initiatorName from creation
- posts: remove authorRole/quotedPostId, keep linkedOrderId as number
- comments: remove postId from creation
- wallet: use string amounts
This commit is contained in:
zetaloop
2026-04-23 21:15:11 +08:00
parent 4037816998
commit 12284290cc
7 changed files with 28 additions and 125 deletions
+7 -39
View File
@@ -14,10 +14,6 @@ function resolveAvatar(_userId: string) {
return "" return ""
} }
function shouldReadonly(status: Order["status"]) {
return status === "pending_review" || status === "completed" || status === "cancelled"
}
export const useChatStore = create<ChatState>((set, get) => ({ export const useChatStore = create<ChatState>((set, get) => ({
sessions: [], sessions: [],
messages: [], messages: [],
@@ -26,17 +22,8 @@ export const useChatStore = create<ChatState>((set, get) => ({
(session) => session.type === "order" && session.orderId === order.id, (session) => session.type === "order" && session.orderId === order.id,
) )
const readonly = shouldReadonly(order.status)
if (existing) { if (existing) {
if (existing.readonly !== readonly) { return existing
set((state) => ({
sessions: state.sessions.map((session) =>
session.id === existing.id ? { ...session, readonly } : session,
),
}))
}
return get().sessions.find((session) => session.id === existing.id) ?? existing
} }
const session: ChatSession = { const session: ChatSession = {
@@ -46,17 +33,16 @@ export const useChatStore = create<ChatState>((set, get) => ({
participants: [ participants: [
{ {
id: order.consumerId, id: order.consumerId,
name: order.consumerName, nickname: "",
avatar: resolveAvatar(order.consumerId), avatar: resolveAvatar(order.consumerId),
}, },
{ {
id: order.playerId, id: order.playerId,
name: order.playerName, nickname: "",
avatar: resolveAvatar(order.playerId), avatar: resolveAvatar(order.playerId),
}, },
], ],
unreadCount: 0, unreadCount: 0,
readonly,
} }
set((state) => ({ set((state) => ({
@@ -69,7 +55,7 @@ export const useChatStore = create<ChatState>((set, get) => ({
const text = content.trim() const text = content.trim()
if (!text) return if (!text) return
const session = get().sessions.find((item) => item.id === sessionId) const session = get().sessions.find((item) => item.id === sessionId)
if (!session || session.readonly) return if (!session) return
const sender = session.participants.find((participant) => participant.id === actorId) const sender = session.participants.find((participant) => participant.id === actorId)
if (!sender) return if (!sender) return
@@ -79,8 +65,6 @@ export const useChatStore = create<ChatState>((set, get) => ({
id: generateId("msg"), id: generateId("msg"),
sessionId, sessionId,
senderId: actorId, senderId: actorId,
senderName: sender.name,
senderAvatar: sender.avatar,
type: "text", type: "text",
content: text, content: text,
createdAt: now, createdAt: now,
@@ -88,22 +72,14 @@ export const useChatStore = create<ChatState>((set, get) => ({
set((state) => ({ set((state) => ({
messages: [...state.messages, message], messages: [...state.messages, message],
sessions: state.sessions.map((session) => sessions: state.sessions.map((s) => (s.id === sessionId ? { ...s, lastMessage: text } : s)),
session.id === sessionId
? {
...session,
lastMessage: text,
lastMessageAt: now,
}
: session,
),
})) }))
}, },
sendImageMessage: (sessionId, actorId, imageUrl) => { sendImageMessage: (sessionId, actorId, imageUrl) => {
const content = imageUrl.trim() const content = imageUrl.trim()
if (!content) return if (!content) return
const session = get().sessions.find((item) => item.id === sessionId) const session = get().sessions.find((item) => item.id === sessionId)
if (!session || session.readonly) return if (!session) return
const sender = session.participants.find((participant) => participant.id === actorId) const sender = session.participants.find((participant) => participant.id === actorId)
if (!sender) return if (!sender) return
@@ -113,8 +89,6 @@ export const useChatStore = create<ChatState>((set, get) => ({
id: generateId("msg"), id: generateId("msg"),
sessionId, sessionId,
senderId: actorId, senderId: actorId,
senderName: sender.name,
senderAvatar: sender.avatar,
type: "image", type: "image",
content, content,
createdAt: now, createdAt: now,
@@ -123,13 +97,7 @@ export const useChatStore = create<ChatState>((set, get) => ({
set((state) => ({ set((state) => ({
messages: [...state.messages, message], messages: [...state.messages, message],
sessions: state.sessions.map((item) => sessions: state.sessions.map((item) =>
item.id === sessionId item.id === sessionId ? { ...item, lastMessage: "[图片]" } : item,
? {
...item,
lastMessage: "[图片]",
lastMessageAt: now,
}
: item,
), ),
})) }))
}, },
-1
View File
@@ -16,7 +16,6 @@ export const useCommentStore = create<CommentState>((set) => ({
const comment: Comment = { const comment: Comment = {
id: generateId("comment"), id: generateId("comment"),
postId,
author, author,
content: normalizedContent, content: normalizedContent,
likeCount: 0, likeCount: 0,
+7 -10
View File
@@ -28,8 +28,6 @@ export interface DisputeRecord extends Dispute {
interface SubmitDisputeInput { interface SubmitDisputeInput {
orderId: string orderId: string
initiatorId: string
initiatorName: string
reason: string reason: string
evidence: string[] evidence: string[]
} }
@@ -180,7 +178,7 @@ export const useDisputeStore = create<DisputeState>((set, get) => {
return { decision: deny(400, "请填写争议原因") } return { decision: deny(400, "请填写争议原因") }
} }
const actor = resolveParticipantActor(input.orderId, input.initiatorId) const actor = resolveParticipantActor(input.orderId, useAuthStore.getState().user?.id ?? "")
if (!actor) { if (!actor) {
return { decision: deny(403, "仅订单参与方可发起争议") } return { decision: deny(403, "仅订单参与方可发起争议") }
} }
@@ -194,8 +192,6 @@ export const useDisputeStore = create<DisputeState>((set, get) => {
const dispute: DisputeRecord = { const dispute: DisputeRecord = {
id: generateId("disp"), id: generateId("disp"),
orderId: input.orderId, orderId: input.orderId,
initiatorId: input.initiatorId,
initiatorName: input.initiatorName,
reason: input.reason.trim(), reason: input.reason.trim(),
evidence: input.evidence, evidence: input.evidence,
status: "open", status: "open",
@@ -205,7 +201,7 @@ export const useDisputeStore = create<DisputeState>((set, get) => {
{ {
id: generateId("timeline"), id: generateId("timeline"),
type: "created", type: "created",
content: `${input.initiatorName} 提交争议`, content: "争议已提交",
createdAt, createdAt,
}, },
], ],
@@ -221,15 +217,16 @@ export const useDisputeStore = create<DisputeState>((set, get) => {
return deny(404, "争议不存在") return deny(404, "争议不存在")
} }
const order = useOrderStore.getState().orders.find((item) => item.id === dispute.orderId)
if (!order) {
return deny(404, "关联订单不存在")
}
const actor = resolveParticipantActor(dispute.orderId, actorId) const actor = resolveParticipantActor(dispute.orderId, actorId)
if (!actor) { if (!actor) {
return deny(403, "仅订单参与方可提交回应") return deny(403, "仅订单参与方可提交回应")
} }
if (actorId === dispute.initiatorId) {
return deny(403, "争议发起方不可提交回应")
}
if (dispute.respondentReason) { if (dispute.respondentReason) {
return deny(400, "回应已提交") return deny(400, "回应已提交")
} }
+1 -34
View File
@@ -14,7 +14,6 @@ import { useChatStore } from "@/store/chat"
import { useNotificationStore } from "@/store/notifications" import { useNotificationStore } from "@/store/notifications"
import { usePlayerStore } from "@/store/players" import { usePlayerStore } from "@/store/players"
import { useServiceStore } from "@/store/services" import { useServiceStore } from "@/store/services"
import { useShopStore } from "@/store/shops"
import { useWalletStore } from "@/store/wallet" import { useWalletStore } from "@/store/wallet"
import { create } from "zustand" import { create } from "zustand"
@@ -112,22 +111,15 @@ function applyStatus(order: Order, status: OrderStatus): Order {
acceptedAt: order.acceptedAt ?? now, acceptedAt: order.acceptedAt ?? now,
} }
case "pending_close": case "pending_close":
return {
...order,
status,
closedAt: order.closedAt ?? now,
}
case "pending_review": case "pending_review":
return { return {
...order, ...order,
status, status,
closedAt: order.closedAt ?? now,
} }
case "completed": case "completed":
return { return {
...order, ...order,
status, status,
closedAt: order.closedAt ?? now,
completedAt: order.completedAt ?? now, completedAt: order.completedAt ?? now,
} }
default: default:
@@ -205,7 +197,7 @@ function notifyOrderStatus(order: Order) {
}, },
in_progress: { in_progress: {
title: "订单已接单", title: "订单已接单",
content: `${order.playerName} 已开始服务`, content: `订单 ${order.service.title} 已开始服务`,
}, },
pending_close: { pending_close: {
title: "订单发起结单", title: "订单发起结单",
@@ -332,10 +324,6 @@ export const useOrderStore = create<OrderState>((set, get) => {
return { decision: deny(400, "店铺信息与打手不匹配") } return { decision: deny(400, "店铺信息与打手不匹配") }
} }
const resolvedShopName = resolvedShopId
? useShopStore.getState().shops.find((item) => item.id === resolvedShopId)?.name
: undefined
const quantity = Number.isFinite(input.quantity) ? Math.floor(input.quantity) : Number.NaN const quantity = Number.isFinite(input.quantity) ? Math.floor(input.quantity) : Number.NaN
if (!quantity || quantity < 1) { if (!quantity || quantity < 1) {
return { decision: deny(400, "数量不合法") } return { decision: deny(400, "数量不合法") }
@@ -345,11 +333,8 @@ export const useOrderStore = create<OrderState>((set, get) => {
const order: Order = { const order: Order = {
id: generateId("ord"), id: generateId("ord"),
consumerId: consumer.id, consumerId: consumer.id,
consumerName: consumer.nickname,
playerId: player.id, playerId: player.id,
playerName: player.user.nickname,
shopId: resolvedShopId, shopId: resolvedShopId,
shopName: resolvedShopName,
service, service,
status: "pending_payment", status: "pending_payment",
totalPrice, totalPrice,
@@ -395,10 +380,6 @@ export const useOrderStore = create<OrderState>((set, get) => {
return { decision: deny(400, "店铺信息与打手不匹配") } return { decision: deny(400, "店铺信息与打手不匹配") }
} }
const resolvedShopName = resolvedShopId
? useShopStore.getState().shops.find((item) => item.id === resolvedShopId)?.name
: undefined
const quantity = Number.isFinite(input.quantity) ? Math.floor(input.quantity) : Number.NaN const quantity = Number.isFinite(input.quantity) ? Math.floor(input.quantity) : Number.NaN
if (!quantity || quantity < 1) { if (!quantity || quantity < 1) {
return { decision: deny(400, "数量不合法") } return { decision: deny(400, "数量不合法") }
@@ -420,11 +401,8 @@ export const useOrderStore = create<OrderState>((set, get) => {
const order: Order = { const order: Order = {
id: orderId, id: orderId,
consumerId: consumer.id, consumerId: consumer.id,
consumerName: consumer.nickname,
playerId: player.id, playerId: player.id,
playerName: player.user.nickname,
shopId: resolvedShopId, shopId: resolvedShopId,
shopName: resolvedShopName,
service, service,
status: "pending_accept", status: "pending_accept",
totalPrice, totalPrice,
@@ -465,17 +443,6 @@ useOrderStore.subscribe((state, prevState) => {
const prevOrder = prevState.orders.find((item) => item.id === order.id) const prevOrder = prevState.orders.find((item) => item.id === order.id)
if (!prevOrder || prevOrder.status !== order.status) { if (!prevOrder || prevOrder.status !== order.status) {
scheduleOrderTimeout(order.id, order.status) scheduleOrderTimeout(order.id, order.status)
if (order.status === "pending_accept" && order.shopId) {
const shop = useShopStore.getState().shops.find((s) => s.id === order.shopId)
if (shop?.dispatchMode === "auto") {
setTimeout(() => {
const current = useOrderStore.getState().orders.find((o) => o.id === order.id)
if (!current || current.status !== "pending_accept") return
const actor: Actor = { userId: order.playerId, role: "player", shopId: order.shopId }
useOrderStore.getState().acceptOrder(order.id, actor)
}, 3000)
}
}
} }
}) })
prevState.orders.forEach((order) => { prevState.orders.forEach((order) => {
+3 -7
View File
@@ -1,16 +1,14 @@
import { generateId } from "@/lib/id" import { generateId } from "@/lib/id"
import type { Post, User, UserRole } from "@/lib/types" import type { Post, User } from "@/lib/types"
import { create } from "zustand" import { create } from "zustand"
interface CreatePostInput { interface CreatePostInput {
author: User author: User
authorRole: UserRole
title: string title: string
content: string content: string
images: string[] images: string[]
tags: string[] tags: string[]
linkedOrderId?: string linkedOrderId?: string
quotedPostId?: string
} }
interface PostState { interface PostState {
@@ -26,17 +24,15 @@ export const usePostStore = create<PostState>((set) => ({
const post: Post = { const post: Post = {
id: generateId("post"), id: generateId("post"),
author: input.author, author: input.author,
authorRole: input.authorRole,
title: input.title.trim(), title: input.title.trim(),
content: input.content.trim(), content: input.content.trim(),
images: input.images, images: input.images,
tags: input.tags, tags: input.tags,
linkedOrderId: input.linkedOrderId, linkedOrderId: input.linkedOrderId ? Number(input.linkedOrderId) : undefined,
quotedPostId: input.quotedPostId, pinned: false,
likeCount: 0, likeCount: 0,
commentCount: 0, commentCount: 0,
liked: false, liked: false,
pinned: false,
createdAt: new Date().toISOString(), createdAt: new Date().toISOString(),
} }
+5 -29
View File
@@ -19,29 +19,6 @@ interface ReviewState {
hasUserReviewed: (orderId: string, userId: string) => boolean hasUserReviewed: (orderId: string, userId: string) => boolean
} }
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) => ({ export const useReviewStore = create<ReviewState>((set, get) => ({
reviews: [], reviews: [],
getReviewsByOrder: (orderId) => get().reviews.filter((review) => review.orderId === orderId), getReviewsByOrder: (orderId) => get().reviews.filter((review) => review.orderId === orderId),
@@ -65,8 +42,9 @@ export const useReviewStore = create<ReviewState>((set, get) => ({
return deny(400, "仅待评价订单可提交评价") return deny(400, "仅待评价订单可提交评价")
} }
const relation = resolveOrderUser(input.orderId, input.fromUserId) const isParticipant =
if (!relation) { order.consumerId === input.fromUserId || order.playerId === input.fromUserId
if (!isParticipant) {
return deny(403, "仅订单参与方可评价") return deny(403, "仅订单参与方可评价")
} }
@@ -81,11 +59,9 @@ export const useReviewStore = create<ReviewState>((set, get) => ({
id: generateId("rev"), id: generateId("rev"),
orderId: input.orderId, orderId: input.orderId,
fromUserId: input.fromUserId, fromUserId: input.fromUserId,
fromUserName: relation.fromUserName, fromUserName: "",
fromUserAvatar: "",
toUserId: relation.toUserId,
rating: input.rating, rating: input.rating,
content: input.content?.trim() || undefined, content: input.content?.trim() ?? "",
sealed: true, sealed: true,
createdAt, createdAt,
} }
+5 -5
View File
@@ -27,7 +27,7 @@ export const useWalletStore = create<WalletState>((set, get) => ({
{ {
id: generateId("tx"), id: generateId("tx"),
type: "topup", type: "topup",
amount, amount: String(amount),
description: "充值", description: "充值",
createdAt: now, createdAt: now,
}, },
@@ -43,7 +43,7 @@ export const useWalletStore = create<WalletState>((set, get) => ({
{ {
id: generateId("tx"), id: generateId("tx"),
type: "withdrawal", type: "withdrawal",
amount: -amount, amount: String(-amount),
description: "提现到银行卡", description: "提现到银行卡",
createdAt: now, createdAt: now,
}, },
@@ -69,7 +69,7 @@ export const useWalletStore = create<WalletState>((set, get) => ({
{ {
id: generateId("tx"), id: generateId("tx"),
type: "payment", type: "payment",
amount: -amount, amount: String(-amount),
description: `支付订单 ${orderId}`, description: `支付订单 ${orderId}`,
createdAt: now, createdAt: now,
}, },
@@ -100,7 +100,7 @@ export const useWalletStore = create<WalletState>((set, get) => ({
{ {
id: generateId("tx"), id: generateId("tx"),
type: "refund", type: "refund",
amount, amount: String(amount),
description: `订单 ${orderId} 退款`, description: `订单 ${orderId} 退款`,
createdAt: now, createdAt: now,
}, },
@@ -130,7 +130,7 @@ export const useWalletStore = create<WalletState>((set, get) => ({
{ {
id: generateId("tx"), id: generateId("tx"),
type: "income", type: "income",
amount: income, amount: String(income),
description: `订单 ${orderId} 收入(${commissionLabel}`, description: `订单 ${orderId} 收入(${commissionLabel}`,
createdAt: now, createdAt: now,
}, },