refactor(order): createPaidOrder uses id references
This commit is contained in:
@@ -196,14 +196,10 @@ export default function NewOrderPage() {
|
|||||||
|
|
||||||
const result = createPaidOrder(
|
const result = createPaidOrder(
|
||||||
{
|
{
|
||||||
consumerId: authUser.id,
|
|
||||||
consumerName: authUser.nickname,
|
|
||||||
playerId: player.id,
|
playerId: player.id,
|
||||||
playerName: player.user.nickname,
|
serviceId: service.id,
|
||||||
shopId: player.shopId,
|
shopId: player.shopId,
|
||||||
shopName: player.shopName,
|
quantity,
|
||||||
service,
|
|
||||||
totalPrice,
|
|
||||||
note,
|
note,
|
||||||
},
|
},
|
||||||
actor,
|
actor,
|
||||||
|
|||||||
+2
-7
@@ -2,7 +2,6 @@ import type { Actor } from "@/lib/actor"
|
|||||||
import { allow, deny } from "@/lib/decision"
|
import { allow, deny } from "@/lib/decision"
|
||||||
import { resolveOwnerShop } from "@/lib/domain/resolve-current-shop"
|
import { resolveOwnerShop } from "@/lib/domain/resolve-current-shop"
|
||||||
import type { ApiDecision } from "@/lib/errors"
|
import type { ApiDecision } from "@/lib/errors"
|
||||||
import type { PlayerService } from "@/lib/types"
|
|
||||||
import { useAuthStore } from "@/store/auth"
|
import { useAuthStore } from "@/store/auth"
|
||||||
import { useOrderStore } from "@/store/orders"
|
import { useOrderStore } from "@/store/orders"
|
||||||
import { useShopStore } from "@/store/shops"
|
import { useShopStore } from "@/store/shops"
|
||||||
@@ -20,14 +19,10 @@ export function listOrdersByConsumer(consumerId: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface CreatePaidOrderInput {
|
interface CreatePaidOrderInput {
|
||||||
consumerId: string
|
|
||||||
consumerName: string
|
|
||||||
playerId: string
|
playerId: string
|
||||||
playerName: string
|
serviceId: string
|
||||||
shopId?: string
|
shopId?: string
|
||||||
shopName?: string
|
quantity: number
|
||||||
service: PlayerService
|
|
||||||
totalPrice: number
|
|
||||||
note?: string
|
note?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+105
-35
@@ -9,30 +9,25 @@ import { evaluateOrderTransition, type OrderAction } from "@/lib/domain/order-ma
|
|||||||
import type { ApiDecision } from "@/lib/errors"
|
import type { ApiDecision } from "@/lib/errors"
|
||||||
import { generateId } from "@/lib/id"
|
import { generateId } from "@/lib/id"
|
||||||
import { mockOrders } from "@/lib/mock"
|
import { mockOrders } from "@/lib/mock"
|
||||||
import type { Order, OrderStatus, PlayerService } from "@/lib/types"
|
import type { Order, OrderStatus } from "@/lib/types"
|
||||||
import { useAuthStore } from "@/store/auth"
|
import { useAuthStore } from "@/store/auth"
|
||||||
import { useChatStore } from "@/store/chat"
|
import { useChatStore } from "@/store/chat"
|
||||||
import { useNotificationStore } from "@/store/notifications"
|
import { useNotificationStore } from "@/store/notifications"
|
||||||
|
import { usePlayerStore } from "@/store/players"
|
||||||
|
import { useServiceStore } from "@/store/services"
|
||||||
import { useShopStore } from "@/store/shops"
|
import { useShopStore } from "@/store/shops"
|
||||||
import { useWalletStore } from "@/store/wallet"
|
import { useWalletStore } from "@/store/wallet"
|
||||||
import { create } from "zustand"
|
import { create } from "zustand"
|
||||||
|
|
||||||
interface CreateOrderInput {
|
interface CreateOrderInput {
|
||||||
consumerId: string
|
|
||||||
consumerName: string
|
|
||||||
playerId: string
|
playerId: string
|
||||||
playerName: string
|
serviceId: string
|
||||||
shopId?: string
|
shopId?: string
|
||||||
shopName?: string
|
quantity: number
|
||||||
service: PlayerService
|
|
||||||
totalPrice: number
|
|
||||||
note?: string
|
note?: string
|
||||||
status?: OrderStatus
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CreatePaidOrderInput extends CreateOrderInput {
|
type CreatePaidOrderInput = CreateOrderInput
|
||||||
status?: never
|
|
||||||
}
|
|
||||||
|
|
||||||
interface OrderMutationResult {
|
interface OrderMutationResult {
|
||||||
decision: ApiDecision
|
decision: ApiDecision
|
||||||
@@ -41,7 +36,7 @@ interface OrderMutationResult {
|
|||||||
|
|
||||||
interface OrderState {
|
interface OrderState {
|
||||||
orders: Order[]
|
orders: Order[]
|
||||||
createOrder: (input: CreateOrderInput) => Order
|
createOrder: (input: CreateOrderInput, actor: Actor) => OrderMutationResult
|
||||||
createPaidOrder: (input: CreatePaidOrderInput, actor: Actor) => OrderMutationResult
|
createPaidOrder: (input: CreatePaidOrderInput, actor: Actor) => OrderMutationResult
|
||||||
payOrder: (orderId: string, actor: Actor) => OrderMutationResult
|
payOrder: (orderId: string, actor: Actor) => OrderMutationResult
|
||||||
acceptOrder: (orderId: string, actor: Actor) => OrderMutationResult
|
acceptOrder: (orderId: string, actor: Actor) => OrderMutationResult
|
||||||
@@ -307,18 +302,58 @@ export const useOrderStore = create<OrderState>((set, get) => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
orders: mockOrders,
|
orders: mockOrders,
|
||||||
createOrder: (input) => {
|
createOrder: (input, actor) => {
|
||||||
|
if (actor.role !== "consumer") {
|
||||||
|
return { decision: deny(403, "仅客户可下单") }
|
||||||
|
}
|
||||||
|
|
||||||
|
const consumer = useAuthStore.getState().user
|
||||||
|
if (!consumer || consumer.id !== actor.userId) {
|
||||||
|
return { decision: deny(403, "仅本人可下单") }
|
||||||
|
}
|
||||||
|
|
||||||
|
const service = useServiceStore
|
||||||
|
.getState()
|
||||||
|
.services.find((item) => item.id === input.serviceId)
|
||||||
|
if (!service) {
|
||||||
|
return { decision: deny(404, "服务不存在") }
|
||||||
|
}
|
||||||
|
|
||||||
|
const player = usePlayerStore.getState().players.find((item) => item.id === input.playerId)
|
||||||
|
if (!player) {
|
||||||
|
return { decision: deny(404, "打手不存在") }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (service.playerId !== player.id) {
|
||||||
|
return { decision: deny(400, "服务与打手不匹配") }
|
||||||
|
}
|
||||||
|
|
||||||
|
const resolvedShopId = input.shopId ?? player.shopId
|
||||||
|
if (input.shopId && player.shopId && input.shopId !== player.shopId) {
|
||||||
|
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, "数量不合法") }
|
||||||
|
}
|
||||||
|
|
||||||
|
const totalPrice = service.price * quantity
|
||||||
const order: Order = {
|
const order: Order = {
|
||||||
id: generateId("ord"),
|
id: generateId("ord"),
|
||||||
consumerId: input.consumerId,
|
consumerId: consumer.id,
|
||||||
consumerName: input.consumerName,
|
consumerName: consumer.nickname,
|
||||||
playerId: input.playerId,
|
playerId: player.id,
|
||||||
playerName: input.playerName,
|
playerName: player.user.nickname,
|
||||||
shopId: input.shopId,
|
shopId: resolvedShopId,
|
||||||
shopName: input.shopName,
|
shopName: resolvedShopName,
|
||||||
service: input.service,
|
service,
|
||||||
status: input.status ?? "pending_payment",
|
status: "pending_payment",
|
||||||
totalPrice: input.totalPrice,
|
totalPrice,
|
||||||
note: input.note?.trim() ? input.note.trim() : undefined,
|
note: input.note?.trim() ? input.note.trim() : undefined,
|
||||||
createdAt: new Date().toISOString(),
|
createdAt: new Date().toISOString(),
|
||||||
}
|
}
|
||||||
@@ -328,21 +363,56 @@ export const useOrderStore = create<OrderState>((set, get) => {
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
scheduleOrderTimeout(order.id, order.status)
|
scheduleOrderTimeout(order.id, order.status)
|
||||||
|
return { decision: allow(), order }
|
||||||
return order
|
|
||||||
},
|
},
|
||||||
createPaidOrder: (input, actor) => {
|
createPaidOrder: (input, actor) => {
|
||||||
if (actor.role !== "consumer" || actor.userId !== input.consumerId) {
|
if (actor.role !== "consumer") {
|
||||||
return { decision: deny(403, "仅客户可下单支付") }
|
return { decision: deny(403, "仅客户可下单支付") }
|
||||||
}
|
}
|
||||||
|
|
||||||
const dedupeKey = `${input.consumerId}-${input.service.id}`
|
const consumer = useAuthStore.getState().user
|
||||||
|
if (!consumer || consumer.id !== actor.userId) {
|
||||||
|
return { decision: deny(403, "仅本人可下单支付") }
|
||||||
|
}
|
||||||
|
|
||||||
|
const service = useServiceStore
|
||||||
|
.getState()
|
||||||
|
.services.find((item) => item.id === input.serviceId)
|
||||||
|
if (!service) {
|
||||||
|
return { decision: deny(404, "服务不存在") }
|
||||||
|
}
|
||||||
|
|
||||||
|
const player = usePlayerStore.getState().players.find((item) => item.id === input.playerId)
|
||||||
|
if (!player) {
|
||||||
|
return { decision: deny(404, "打手不存在") }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (service.playerId !== player.id) {
|
||||||
|
return { decision: deny(400, "服务与打手不匹配") }
|
||||||
|
}
|
||||||
|
|
||||||
|
const resolvedShopId = input.shopId ?? player.shopId
|
||||||
|
if (input.shopId && player.shopId && input.shopId !== player.shopId) {
|
||||||
|
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, "数量不合法") }
|
||||||
|
}
|
||||||
|
|
||||||
|
const totalPrice = service.price * quantity
|
||||||
|
const dedupeKey = `${consumer.id}-${service.id}`
|
||||||
if (pendingCreations.has(dedupeKey)) {
|
if (pendingCreations.has(dedupeKey)) {
|
||||||
return { decision: deny(400, "订单正在创建中,请勿重复提交") }
|
return { decision: deny(400, "订单正在创建中,请勿重复提交") }
|
||||||
}
|
}
|
||||||
pendingCreations.add(dedupeKey)
|
pendingCreations.add(dedupeKey)
|
||||||
const orderId = generateId("ord")
|
const orderId = generateId("ord")
|
||||||
const paid = useWalletStore.getState().deductBalance(orderId, input.totalPrice)
|
const paid = useWalletStore.getState().deductBalance(orderId, totalPrice)
|
||||||
if (!paid) {
|
if (!paid) {
|
||||||
pendingCreations.delete(dedupeKey)
|
pendingCreations.delete(dedupeKey)
|
||||||
return { decision: deny(400, "余额不足或订单已支付") }
|
return { decision: deny(400, "余额不足或订单已支付") }
|
||||||
@@ -350,15 +420,15 @@ export const useOrderStore = create<OrderState>((set, get) => {
|
|||||||
|
|
||||||
const order: Order = {
|
const order: Order = {
|
||||||
id: orderId,
|
id: orderId,
|
||||||
consumerId: input.consumerId,
|
consumerId: consumer.id,
|
||||||
consumerName: input.consumerName,
|
consumerName: consumer.nickname,
|
||||||
playerId: input.playerId,
|
playerId: player.id,
|
||||||
playerName: input.playerName,
|
playerName: player.user.nickname,
|
||||||
shopId: input.shopId,
|
shopId: resolvedShopId,
|
||||||
shopName: input.shopName,
|
shopName: resolvedShopName,
|
||||||
service: input.service,
|
service,
|
||||||
status: "pending_accept",
|
status: "pending_accept",
|
||||||
totalPrice: input.totalPrice,
|
totalPrice,
|
||||||
note: input.note?.trim() ? input.note.trim() : undefined,
|
note: input.note?.trim() ? input.note.trim() : undefined,
|
||||||
createdAt: new Date().toISOString(),
|
createdAt: new Date().toISOString(),
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user