diff --git a/backend/internal/model/mohong.go b/backend/internal/model/mohong.go index 5d2f6fd..2efd56e 100644 --- a/backend/internal/model/mohong.go +++ b/backend/internal/model/mohong.go @@ -92,6 +92,7 @@ const ( MohongOrderStatusPendingPayment = "pending_payment" MohongOrderStatusPaid = "paid" + MohongOrderStatusReceiving = "receiving" // 接待中:已有人接待买家 MohongOrderStatusCompleted = "completed" MohongOrderStatusCancelled = "cancelled" MohongOrderStatusRefunded = "refunded" diff --git a/backend/internal/modules/mohong/dto.go b/backend/internal/modules/mohong/dto.go index fe3a93e..7f0b617 100644 --- a/backend/internal/modules/mohong/dto.go +++ b/backend/internal/modules/mohong/dto.go @@ -151,6 +151,10 @@ type AdminCompleteOrderRequest struct { Remark string `json:"remark"` } +type AdminReceiveOrderRequest struct { + Remark string `json:"remark"` +} + type AdminCancelOrderRequest struct { Reason string `json:"reason"` } diff --git a/backend/internal/modules/mohong/errors.go b/backend/internal/modules/mohong/errors.go index 1da9bf5..dd2a97d 100644 --- a/backend/internal/modules/mohong/errors.go +++ b/backend/internal/modules/mohong/errors.go @@ -11,6 +11,7 @@ var ( ErrOrderNotFound = errors.New("order not found") ErrOrderCannotPay = errors.New("order cannot pay") ErrOrderCannotCancel = errors.New("order cannot cancel") + ErrOrderCannotReceive = errors.New("order cannot receive") ErrOrderCannotComplete = errors.New("order cannot complete") ErrUnauthorized = errors.New("unauthorized") ) diff --git a/backend/internal/modules/mohong/handler.go b/backend/internal/modules/mohong/handler.go index f38b0bd..9721917 100644 --- a/backend/internal/modules/mohong/handler.go +++ b/backend/internal/modules/mohong/handler.go @@ -292,6 +292,21 @@ func (h *Handler) AdminGetOrder(c *gin.Context) { response.OK(c, item) } +func (h *Handler) AdminReceiveOrder(c *gin.Context) { + id, ok := parseID(c) + if !ok { + return + } + var req AdminReceiveOrderRequest + _ = c.ShouldBindJSON(&req) + item, err := h.service.AdminReceiveOrder(c.Request.Context(), id, req.Remark) + if err != nil { + writeError(c, err) + return + } + response.OK(c, item) +} + func (h *Handler) AdminCompleteOrder(c *gin.Context) { id, ok := parseID(c) if !ok { @@ -357,6 +372,8 @@ func writeError(c *gin.Context, err error) { response.Error(c, 409, "order_cannot_pay", "订单无法支付") case errors.Is(err, ErrOrderCannotCancel): response.Error(c, 409, "order_cannot_cancel", "订单无法取消") + case errors.Is(err, ErrOrderCannotReceive): + response.Error(c, 409, "order_cannot_receive", "订单无法标记为接待中") case errors.Is(err, ErrOrderCannotComplete): response.Error(c, 409, "order_cannot_complete", "订单无法完成") case errors.Is(err, ErrInvalidRequest): diff --git a/backend/internal/modules/mohong/order_repo.go b/backend/internal/modules/mohong/order_repo.go index 8dcc1ce..d286bf9 100644 --- a/backend/internal/modules/mohong/order_repo.go +++ b/backend/internal/modules/mohong/order_repo.go @@ -266,13 +266,15 @@ func (r *Repository) AdminCancelOrder(ctx context.Context, orderID uint64, reaso } return err } - if order.Status != model.MohongOrderStatusPendingPayment && order.Status != model.MohongOrderStatusPaid { + if order.Status != model.MohongOrderStatusPendingPayment && + order.Status != model.MohongOrderStatusPaid && + order.Status != model.MohongOrderStatusReceiving { return ErrOrderCannotCancel } if order.Status == model.MohongOrderStatusPendingPayment { return r.cancelPendingOrderTx(tx, &order, firstNonEmpty(reason, "后台取消")) } - // 已支付:仅标记取消(退款后续可接支付退款)。 + // 已支付/接待中:仅标记取消(退款后续可接支付退款)。 now := time.Now() order.Status = model.MohongOrderStatusCancelled order.CancelledAt = &now @@ -285,6 +287,31 @@ func (r *Repository) AdminCancelOrder(ctx context.Context, orderID uint64, reaso return r.AdminFindOrder(ctx, orderID) } +// AdminReceiveOrder 标记订单为接待中(已支付 → 接待中)。 +func (r *Repository) AdminReceiveOrder(ctx context.Context, orderID uint64, remark string) (*OrderDTO, error) { + err := r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { + var order model.MohongOrder + if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).First(&order, orderID).Error; err != nil { + if err == gorm.ErrRecordNotFound { + return ErrOrderNotFound + } + return err + } + if order.Status != model.MohongOrderStatusPaid { + return ErrOrderCannotReceive + } + order.Status = model.MohongOrderStatusReceiving + if strings.TrimSpace(remark) != "" { + order.AdminRemark = strings.TrimSpace(remark) + } + return tx.Save(&order).Error + }) + if err != nil { + return nil, err + } + return r.AdminFindOrder(ctx, orderID) +} + func (r *Repository) AdminCompleteOrder(ctx context.Context, orderID uint64, remark string) (*OrderDTO, error) { err := r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { var order model.MohongOrder @@ -294,7 +321,7 @@ func (r *Repository) AdminCompleteOrder(ctx context.Context, orderID uint64, rem } return err } - if order.Status != model.MohongOrderStatusPaid { + if order.Status != model.MohongOrderStatusPaid && order.Status != model.MohongOrderStatusReceiving { return ErrOrderCannotComplete } now := time.Now() @@ -319,6 +346,7 @@ func (r *Repository) ConfirmPaidFromChannelTx(tx *gorm.DB, orderID uint64) (uint return 0, err } if order.Status == model.MohongOrderStatusPaid || + order.Status == model.MohongOrderStatusReceiving || order.Status == model.MohongOrderStatusCompleted { return 0, nil } diff --git a/backend/internal/modules/mohong/service.go b/backend/internal/modules/mohong/service.go index 9d33a43..4d47d80 100644 --- a/backend/internal/modules/mohong/service.go +++ b/backend/internal/modules/mohong/service.go @@ -136,6 +136,13 @@ func (s *Service) AdminFindOrder(ctx context.Context, orderID uint64) (*OrderDTO return s.repo.AdminFindOrder(ctx, orderID) } +func (s *Service) AdminReceiveOrder(ctx context.Context, orderID uint64, remark string) (*OrderDTO, error) { + if s.repo == nil { + return nil, ErrDependencyUnavailable + } + return s.repo.AdminReceiveOrder(ctx, orderID, remark) +} + func (s *Service) AdminCompleteOrder(ctx context.Context, orderID uint64, remark string) (*OrderDTO, error) { if s.repo == nil { return nil, ErrDependencyUnavailable diff --git a/backend/internal/router/router.go b/backend/internal/router/router.go index d25aeb9..92dd97a 100644 --- a/backend/internal/router/router.go +++ b/backend/internal/router/router.go @@ -617,6 +617,7 @@ func New(cfg config.Config, deps Dependencies, logger *zap.Logger) *gin.Engine { adminRoutes.DELETE(prefix+"/products/:id", requirePerm("mohong:product_manage"), mohongHandler.AdminDeleteProduct) adminRoutes.GET(prefix+"/orders", requirePerm("mohong:order_view"), mohongHandler.AdminListOrders) adminRoutes.GET(prefix+"/orders/:id", requirePerm("mohong:order_view"), mohongHandler.AdminGetOrder) + adminRoutes.POST(prefix+"/orders/:id/receive", requirePerm("mohong:order_manage"), mohongHandler.AdminReceiveOrder) adminRoutes.POST(prefix+"/orders/:id/complete", requirePerm("mohong:order_manage"), mohongHandler.AdminCompleteOrder) adminRoutes.POST(prefix+"/orders/:id/cancel", requirePerm("mohong:order_manage"), mohongHandler.AdminCancelOrder) adminRoutes.GET(prefix+"/config", requirePerm("mohong:config"), mohongHandler.AdminGetConfig) diff --git a/frontend/src/features/admin/views/AdminMohongOrdersView.vue b/frontend/src/features/admin/views/AdminMohongOrdersView.vue index 80c0371..522b132 100644 --- a/frontend/src/features/admin/views/AdminMohongOrdersView.vue +++ b/frontend/src/features/admin/views/AdminMohongOrdersView.vue @@ -7,6 +7,7 @@ import { completeAdminMohongOrder, fetchAdminMohongOrders, mohongOrderStatusLabel, + receiveAdminMohongOrder, type MohongOrder, } from '@/features/mohong/api/mohong' import { formatCent } from '@/shared/utils/money' @@ -43,6 +44,19 @@ async function load() { } } +async function handleReceive(row: MohongOrder) { + try { + await ElMessageBox.confirm(`确认接待订单 ${row.order_no}?标记后状态变为「接待中」。`, '提示', { + type: 'info', + }) + await receiveAdminMohongOrder(row.id) + ElMessage.success('已标记为接待中') + await load() + } catch (error) { + if (error !== 'cancel') ElMessage.error(readError(error, '操作失败')) + } +} + async function handleComplete(row: MohongOrder) { try { await ElMessageBox.confirm(`确认将订单 ${row.order_no} 标记为已完成?`, '提示', { @@ -71,6 +85,20 @@ async function handleCancel(row: MohongOrder) { } } +function canReceive(row: MohongOrder) { + return row.status === 'paid' +} + +function canComplete(row: MohongOrder) { + return row.status === 'paid' || row.status === 'receiving' +} + +function canCancel(row: MohongOrder) { + return ( + row.status === 'pending_payment' || row.status === 'paid' || row.status === 'receiving' + ) +} + async function copyText(text: string) { try { await navigator.clipboard.writeText(text) @@ -86,7 +114,7 @@ async function copyText(text: string) {
查看订单、完成履约、复制用户订单信息
+查看订单、接待买家、完成履约、复制用户订单信息