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