diff --git a/store/chat.ts b/store/chat.ts deleted file mode 100644 index d74b138..0000000 --- a/store/chat.ts +++ /dev/null @@ -1,104 +0,0 @@ -import { generateId } from "@/lib/id" -import type { ChatMessage, ChatSession, Order } from "@/lib/types" -import { create } from "zustand" - -interface ChatState { - sessions: ChatSession[] - messages: ChatMessage[] - ensureOrderSession: (order: Order) => ChatSession - sendTextMessage: (sessionId: string, actorId: string, content: string) => void - sendImageMessage: (sessionId: string, actorId: string, imageUrl: string) => void -} - -function resolveAvatar(_userId: string) { - return "" -} - -export const useChatStore = create((set, get) => ({ - sessions: [], - messages: [], - ensureOrderSession: (order) => { - const existing = get().sessions.find( - (session) => session.type === "order" && session.orderId === order.id, - ) - - if (existing) { - return existing - } - - const session: ChatSession = { - id: `chat-${order.id}`, - type: "order", - orderId: order.id, - participants: [ - { - id: order.consumerId, - nickname: "", - avatar: resolveAvatar(order.consumerId), - }, - { - id: order.playerId, - nickname: "", - avatar: resolveAvatar(order.playerId), - }, - ], - unreadCount: 0, - } - - set((state) => ({ - sessions: [session, ...state.sessions], - })) - - return session - }, - sendTextMessage: (sessionId, actorId, content) => { - const text = content.trim() - if (!text) return - const session = get().sessions.find((item) => item.id === sessionId) - if (!session) return - - const sender = session.participants.find((participant) => participant.id === actorId) - if (!sender) return - - const now = new Date().toISOString() - const message: ChatMessage = { - id: generateId("msg"), - sessionId, - senderId: actorId, - type: "text", - content: text, - createdAt: now, - } - - set((state) => ({ - messages: [...state.messages, message], - 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) return - - const sender = session.participants.find((participant) => participant.id === actorId) - if (!sender) return - - const now = new Date().toISOString() - const message: ChatMessage = { - id: generateId("msg"), - sessionId, - senderId: actorId, - type: "image", - content, - createdAt: now, - } - - set((state) => ({ - messages: [...state.messages, message], - sessions: state.sessions.map((item) => - item.id === sessionId ? { ...item, lastMessage: "[图片]" } : item, - ), - })) - }, -})) diff --git a/store/comments.ts b/store/comments.ts deleted file mode 100644 index 78d84ab..0000000 --- a/store/comments.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { generateId } from "@/lib/id" -import type { Comment, User } from "@/lib/types" -import { create } from "zustand" - -interface CommentState { - comments: Comment[] - addComment: (postId: string, author: User, content: string) => Comment | null - toggleCommentLike: (commentId: string) => void -} - -export const useCommentStore = create((set) => ({ - comments: [], - addComment: (postId, author, content) => { - const normalizedContent = content.trim() - if (!normalizedContent) return null - - const comment: Comment = { - id: generateId("comment"), - author, - content: normalizedContent, - likeCount: 0, - liked: false, - createdAt: new Date().toISOString(), - } - - set((state) => ({ - comments: [...state.comments, comment], - })) - - return comment - }, - toggleCommentLike: (commentId) => { - set((state) => ({ - comments: state.comments.map((comment) => { - if (comment.id !== commentId) return comment - const liked = !comment.liked - return { - ...comment, - liked, - likeCount: liked ? comment.likeCount + 1 : Math.max(0, comment.likeCount - 1), - } - }), - })) - }, -})) diff --git a/store/disputes.ts b/store/disputes.ts deleted file mode 100644 index 2be260d..0000000 --- a/store/disputes.ts +++ /dev/null @@ -1,236 +0,0 @@ -import type { Actor } from "@/lib/actor" -import { allow, deny } from "@/lib/decision" -import type { ApiDecision } from "@/lib/errors" -import { generateId } from "@/lib/id" -import type { Dispute } from "@/lib/types" -import { useAuthStore } from "@/store/auth" -import { useNotificationStore } from "@/store/notifications" -import { useOrderStore } from "@/store/orders" -import { create } from "zustand" - -type DisputeTimelineType = "created" | "response" | "reviewing" | "resolved" | "appealed" - -interface DisputeTimelineItem { - id: string - type: DisputeTimelineType - content: string - createdAt: string -} - -export interface DisputeRecord extends Dispute { - respondentReason?: string - respondentEvidence: string[] - appealReason?: string - appealedAt?: string - timeline: DisputeTimelineItem[] -} - -interface SubmitDisputeInput { - orderId: string - reason: string - evidence: string[] -} - -interface DisputeMutationResult { - decision: ApiDecision - dispute?: DisputeRecord -} - -interface DisputeState { - disputes: DisputeRecord[] - getDisputeByOrderId: (orderId: string) => DisputeRecord | undefined - submitDispute: (input: SubmitDisputeInput) => DisputeMutationResult - submitResponse: ( - disputeId: string, - actorId: string, - reason: string, - evidence: string[], - ) => ApiDecision - submitAppeal: (disputeId: string, actorId: string, reason: string) => ApiDecision -} - -function resolveParticipantActor(orderId: string, userId: string): Actor | null { - const order = useOrderStore.getState().orders.find((item) => item.id === orderId) - if (!order) return null - - if (order.consumerId === userId) { - return { - userId, - role: "consumer", - shopId: order.shopId, - } - } - - if (order.playerId === userId) { - return { - userId, - role: "player", - shopId: order.shopId, - } - } - - return null -} - -function notifyDispute(orderId: string, title: string, content: string) { - if (!useAuthStore.getState().notificationPrefs.order) { - return - } - - useNotificationStore.getState().addNotification({ - type: "order", - title, - content, - link: `/dispute/${orderId}`, - }) -} - -export const useDisputeStore = create((set, get) => { - return { - disputes: [], - getDisputeByOrderId: (orderId) => get().disputes.find((dispute) => dispute.orderId === orderId), - submitDispute: (input) => { - const order = useOrderStore.getState().orders.find((item) => item.id === input.orderId) - if (!order) { - return { decision: deny(404, "订单不存在") } - } - - if (order.status !== "in_progress" && order.status !== "pending_close") { - return { decision: deny(400, "当前阶段不可发起争议") } - } - - if (!input.reason.trim()) { - return { decision: deny(400, "请填写争议原因") } - } - - const actor = resolveParticipantActor(input.orderId, useAuthStore.getState().user?.id ?? "") - if (!actor) { - return { decision: deny(403, "仅订单参与方可发起争议") } - } - - const createdAt = new Date().toISOString() - const dispute: DisputeRecord = { - id: generateId("disp"), - orderId: input.orderId, - reason: input.reason.trim(), - evidence: input.evidence, - status: "open", - createdAt, - respondentEvidence: [], - timeline: [ - { - id: generateId("timeline"), - type: "created", - content: "争议已提交", - createdAt, - }, - ], - } - - set((state) => ({ disputes: [dispute, ...state.disputes] })) - return { decision: allow(), dispute } - }, - submitResponse: (disputeId, actorId, reason, evidence) => { - const dispute = get().disputes.find((item) => item.id === disputeId) - if (!dispute) { - 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 (dispute.respondentReason) { - return deny(400, "回应已提交") - } - - if (dispute.status !== "open" && dispute.status !== "reviewing") { - return deny(400, "当前状态不可提交回应") - } - - if (!reason.trim()) { - return deny(400, "请填写回应理由") - } - - set((state) => ({ - disputes: state.disputes.map((item) => { - if (item.id !== disputeId) return item - - return { - ...item, - respondentReason: reason.trim(), - respondentEvidence: evidence, - timeline: [ - ...item.timeline, - { - id: generateId("timeline"), - type: "response", - content: "对方已提交回应材料", - createdAt: new Date().toISOString(), - }, - ], - } - }), - })) - - notifyDispute(dispute.orderId, "争议收到回应", "对方已提交争议回应材料") - - return allow() - }, - submitAppeal: (disputeId, actorId, reason) => { - const dispute = get().disputes.find((item) => item.id === disputeId) - if (!dispute) { - return deny(404, "争议不存在") - } - - const actor = resolveParticipantActor(dispute.orderId, actorId) - if (!actor) { - return deny(403, "仅订单参与方可提交申诉") - } - - if (dispute.status !== "resolved") { - return deny(400, "当前状态不可申诉") - } - - if (dispute.appealedAt) { - return deny(400, "该争议已申诉过") - } - - if (!reason.trim()) { - return deny(400, "请填写申诉理由") - } - - set((state) => ({ - disputes: state.disputes.map((item) => { - if (item.id !== disputeId) return item - - return { - ...item, - status: "appealed", - appealReason: reason.trim(), - appealedAt: new Date().toISOString(), - timeline: [ - ...item.timeline, - { - id: generateId("timeline"), - type: "appealed", - content: "已提交申诉,平台将复核", - createdAt: new Date().toISOString(), - }, - ], - } - }), - })) - - notifyDispute(dispute.orderId, "争议已申诉", "申诉已提交,平台将继续复核") - - return allow() - }, - } -}) diff --git a/store/favorites.ts b/store/favorites.ts deleted file mode 100644 index dbaeca2..0000000 --- a/store/favorites.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { generateId } from "@/lib/id" -import type { Favorite } from "@/lib/types" -import { create } from "zustand" - -interface FavoriteState { - favorites: Favorite[] - listFavoritesByUser: (userId: string) => Favorite[] - isFavorited: (userId: string, targetType: "player" | "shop", targetId: string) => boolean - toggleFavorite: (userId: string, targetType: "player" | "shop", targetId: string) => boolean -} - -export const useFavoriteStore = create((set, get) => ({ - favorites: [], - listFavoritesByUser: (userId) => get().favorites.filter((favorite) => favorite.userId === userId), - isFavorited: (userId, targetType, targetId) => - get().favorites.some( - (favorite) => - favorite.userId === userId && - favorite.targetType === targetType && - favorite.targetId === targetId, - ), - toggleFavorite: (userId, targetType, targetId) => { - const state = get() - const existing = state.favorites.find( - (favorite) => - favorite.userId === userId && - favorite.targetType === targetType && - favorite.targetId === targetId, - ) - - if (existing) { - set((prev) => ({ - favorites: prev.favorites.filter((favorite) => favorite.id !== existing.id), - })) - return false - } - - const next: Favorite = { - id: generateId("fav"), - userId, - targetType, - targetId, - createdAt: new Date().toISOString(), - } - - set((prev) => ({ - favorites: [next, ...prev.favorites], - })) - return true - }, -})) diff --git a/store/player-status.ts b/store/player-status.ts deleted file mode 100644 index 1473238..0000000 --- a/store/player-status.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type { Player } from "@/lib/types" -import { create } from "zustand" - -type PlayerStatus = Player["status"] - -interface PlayerStatusState { - statuses: Record - setStatus: (playerId: string, status: PlayerStatus) => void -} - -export const usePlayerStatusStore = create((set) => ({ - statuses: { - u2: "available", - u4: "busy", - u5: "available", - }, - setStatus: (playerId, status) => - set((state) => ({ - statuses: { ...state.statuses, [playerId]: status }, - })), -})) diff --git a/store/players.ts b/store/players.ts deleted file mode 100644 index 2ebeb5d..0000000 --- a/store/players.ts +++ /dev/null @@ -1,24 +0,0 @@ -import type { Player } from "@/lib/types" -import { create } from "zustand" - -interface PlayerState { - players: Player[] - assignToShop: (playerId: string, shopId: string, shopName: string) => void - removeFromShop: (playerId: string) => void -} - -export const usePlayerStore = create((set) => ({ - players: [], - assignToShop: (playerId, shopId, shopName) => - set((state) => ({ - players: state.players.map((player) => - player.id === playerId ? { ...player, shopId, shopName } : player, - ), - })), - removeFromShop: (playerId) => - set((state) => ({ - players: state.players.map((player) => - player.id === playerId ? { ...player, shopId: undefined, shopName: undefined } : player, - ), - })), -})) diff --git a/store/posts.ts b/store/posts.ts deleted file mode 100644 index 7d8e423..0000000 --- a/store/posts.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { generateId } from "@/lib/id" -import type { Post, User } from "@/lib/types" -import { create } from "zustand" - -interface CreatePostInput { - author: User - title: string - content: string - images: string[] - tags: string[] - linkedOrderId?: string -} - -interface PostState { - posts: Post[] - createPost: (input: CreatePostInput) => Post - togglePostLike: (postId: string) => void - incrementCommentCount: (postId: string) => void -} - -export const usePostStore = create((set) => ({ - posts: [], - createPost: (input) => { - const post: Post = { - id: generateId("post"), - author: input.author, - title: input.title.trim(), - content: input.content.trim(), - images: input.images, - tags: input.tags, - linkedOrderId: input.linkedOrderId ? Number(input.linkedOrderId) : undefined, - pinned: false, - likeCount: 0, - commentCount: 0, - liked: false, - createdAt: new Date().toISOString(), - } - - set((state) => ({ - posts: [post, ...state.posts], - })) - - return post - }, - togglePostLike: (postId) => - set((state) => ({ - posts: state.posts.map((post) => { - if (post.id !== postId) return post - const liked = !post.liked - return { - ...post, - liked, - likeCount: liked ? post.likeCount + 1 : Math.max(0, post.likeCount - 1), - } - }), - })), - incrementCommentCount: (postId) => - set((state) => ({ - posts: state.posts.map((post) => - post.id === postId ? { ...post, commentCount: post.commentCount + 1 } : post, - ), - })), -})) diff --git a/store/reviews.ts b/store/reviews.ts deleted file mode 100644 index 7c4766f..0000000 --- a/store/reviews.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { allow, deny } from "@/lib/decision" -import type { ApiDecision } from "@/lib/errors" -import { generateId } from "@/lib/id" -import type { Review } from "@/lib/types" -import { create } from "zustand" - -interface SubmitReviewInput { - orderId: string - fromUserId: string - rating: number - content?: string -} - -interface ReviewState { - reviews: Review[] - submitReview: (input: SubmitReviewInput) => ApiDecision - getReviewsByOrder: (orderId: string) => Review[] - hasUserReviewed: (orderId: string, userId: string) => boolean -} - -export const useReviewStore = create((set, get) => ({ - reviews: [], - 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(401, "请先登录") - } - - if (!Number.isFinite(input.rating) || input.rating < 1 || input.rating > 5) { - return deny(400, "评分范围应为 1-5") - } - - const exists = get().hasUserReviewed(input.orderId, input.fromUserId) - if (exists) { - return deny(400, "该订单已提交过评价") - } - - const createdAt = new Date().toISOString() - - const review: Review = { - id: generateId("rev"), - orderId: input.orderId, - fromUserId: input.fromUserId, - fromUserName: "", - rating: input.rating, - content: input.content?.trim() ?? "", - 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, - ), - })) - } - - return allow() - }, -})) diff --git a/store/services.ts b/store/services.ts deleted file mode 100644 index 979a87d..0000000 --- a/store/services.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { generateId } from "@/lib/id" -import type { PlayerService } from "@/lib/types" -import { create } from "zustand" - -interface ServiceState { - services: PlayerService[] - createService: (service: Omit) => void - updateService: (serviceId: string, patch: Partial>) => void - deleteService: (serviceId: string) => void -} - -export const useServiceStore = create((set) => ({ - services: [], - createService: (service) => - set((state) => ({ - services: [ - ...state.services, - { - ...service, - id: generateId("svc"), - }, - ], - })), - updateService: (serviceId, patch) => - set((state) => ({ - services: state.services.map((service) => - service.id === serviceId ? { ...service, ...patch } : service, - ), - })), - deleteService: (serviceId) => - set((state) => ({ - services: state.services.filter((service) => service.id !== serviceId), - })), -})) diff --git a/store/shops.ts b/store/shops.ts deleted file mode 100644 index ee700a3..0000000 --- a/store/shops.ts +++ /dev/null @@ -1,56 +0,0 @@ -import type { Shop, ShopSection } from "@/lib/types" -import { create } from "zustand" - -interface ShopState { - shops: Shop[] - updateShop: (shopId: string, patch: Partial>) => void - updateTemplateSections: (shopId: string, sections: ShopSection[]) => void - updateAnnouncement: (shopId: string, index: number, announcement: string) => void - addAnnouncement: (shopId: string, announcement: string) => void -} - -export const useShopStore = create((set) => ({ - shops: [], - updateShop: (shopId, patch) => - set((state) => ({ - shops: state.shops.map((shop) => (shop.id === shopId ? { ...shop, ...patch } : shop)), - })), - updateTemplateSections: (shopId, sections) => - set((state) => ({ - shops: state.shops.map((shop) => - shop.id === shopId - ? { - ...shop, - templateConfig: { - ...shop.templateConfig, - sections, - }, - } - : shop, - ), - })), - updateAnnouncement: (shopId, index, announcement) => - set((state) => ({ - shops: state.shops.map((shop) => - shop.id === shopId - ? { - ...shop, - announcements: shop.announcements.map((item, currentIndex) => - currentIndex === index ? announcement : item, - ), - } - : shop, - ), - })), - addAnnouncement: (shopId, announcement) => - set((state) => ({ - shops: state.shops.map((shop) => - shop.id === shopId - ? { - ...shop, - announcements: [...shop.announcements, announcement], - } - : shop, - ), - })), -}))