From 272b2cb71e2cdc04a9e1f1468f15c7867dd383b2 Mon Sep 17 00:00:00 2001 From: yml Date: Thu, 18 Jun 2026 00:44:45 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=AE=A2=E6=9C=8D=E7=BE=A4?= =?UTF-8?q?=E8=81=8A=E8=AE=A2=E5=8D=95=E4=BF=A1=E6=81=AF=E5=B1=95=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/internal/modules/chat/conversation.go | 1 + backend/internal/modules/chat/dto.go | 1 + backend/internal/modules/chat/participant.go | 4 +- backend/internal/modules/chat/support.go | 1 + .../internal/modules/order/handler_admin.go | 13 + backend/internal/modules/order/queries.go | 16 + backend/internal/modules/order/service.go | 10 + backend/internal/router/router.go | 1 + .../src/features/admin/api/adminPayments.ts | 7 +- .../features/admin/views/AdminChatsView.vue | 397 ++++++++++++++++-- .../admin/views/AdminOrderDetailView.vue | 29 +- frontend/src/features/chats/api/chats.ts | 1 + .../listings/components/ListingCard.vue | 15 +- frontend/src/features/orders/api/orders.ts | 7 + frontend/src/shared/utils/listingDisplay.ts | 31 +- 15 files changed, 479 insertions(+), 55 deletions(-) diff --git a/backend/internal/modules/chat/conversation.go b/backend/internal/modules/chat/conversation.go index 3d5d779..1d9171f 100644 --- a/backend/internal/modules/chat/conversation.go +++ b/backend/internal/modules/chat/conversation.go @@ -54,6 +54,7 @@ func (r *Repository) FindConversation(ctx context.Context, principal Principal, dto := ConversationDTO{ ID: conversation.ID, OrderID: conversation.OrderID, + ListingID: conversation.ListingID, Type: conversation.Type, Title: conversation.Title, Status: conversation.Status, diff --git a/backend/internal/modules/chat/dto.go b/backend/internal/modules/chat/dto.go index 823241d..4985c51 100644 --- a/backend/internal/modules/chat/dto.go +++ b/backend/internal/modules/chat/dto.go @@ -10,6 +10,7 @@ type Principal struct { type ConversationDTO struct { ID uint64 `json:"id"` OrderID *uint64 `json:"order_id"` + ListingID *uint64 `json:"listing_id"` Type string `json:"type"` Title string `json:"title"` Status string `json:"status"` diff --git a/backend/internal/modules/chat/participant.go b/backend/internal/modules/chat/participant.go index 37cda7b..e719430 100644 --- a/backend/internal/modules/chat/participant.go +++ b/backend/internal/modules/chat/participant.go @@ -14,6 +14,7 @@ import ( type conversationRow struct { ID uint64 OrderID *uint64 + ListingID *uint64 Type string Title string Status string @@ -28,7 +29,7 @@ type conversationRow struct { func (r *Repository) conversationQuery(ctx context.Context, principal Principal) *gorm.DB { return r.db.WithContext(ctx).Table("chat_conversations AS c"). - Select(`c.id, c.order_id, c.type, c.title, c.status, c.last_message_id, + Select(`c.id, c.order_id, c.listing_id, c.type, c.title, c.status, c.last_message_id, c.last_message_preview, c.last_message_at, c.created_at, c.updated_at, cp.role, ( SELECT COUNT(1) @@ -282,6 +283,7 @@ func (row conversationRow) toDTO(participants []ParticipantDTO) ConversationDTO return ConversationDTO{ ID: row.ID, OrderID: row.OrderID, + ListingID: row.ListingID, Type: row.Type, Title: row.Title, Status: row.Status, diff --git a/backend/internal/modules/chat/support.go b/backend/internal/modules/chat/support.go index 0d4fc5c..029d59c 100644 --- a/backend/internal/modules/chat/support.go +++ b/backend/internal/modules/chat/support.go @@ -141,6 +141,7 @@ func (r *Repository) ListConversationsWithFilter(ctx context.Context, principal items = append(items, ConversationDTO{ ID: conv.ID, OrderID: conv.OrderID, + ListingID: conv.ListingID, Type: conv.Type, Title: conv.Title, Status: conv.Status, diff --git a/backend/internal/modules/order/handler_admin.go b/backend/internal/modules/order/handler_admin.go index 11dda71..d6564f6 100644 --- a/backend/internal/modules/order/handler_admin.go +++ b/backend/internal/modules/order/handler_admin.go @@ -32,6 +32,19 @@ func (h *Handler) AdminDetail(c *gin.Context) { response.OK(c, item) } +func (h *Handler) AdminLatestByListing(c *gin.Context) { + id, ok := parseID(c) + if !ok { + return + } + item, err := h.service.FindLatestAdminByListing(c.Request.Context(), id) + if err != nil { + writeOrderError(c, err) + return + } + response.OK(c, item) +} + func (h *Handler) AdminHandoffRecords(c *gin.Context) { id, ok := parseID(c) if !ok { diff --git a/backend/internal/modules/order/queries.go b/backend/internal/modules/order/queries.go index cd082d7..bd3f74b 100644 --- a/backend/internal/modules/order/queries.go +++ b/backend/internal/modules/order/queries.go @@ -80,6 +80,22 @@ func (r *Repository) FindAdmin(ctx context.Context, orderID uint64) (*OrderDTO, return &dto, nil } +func (r *Repository) FindLatestAdminByListing(ctx context.Context, listingID uint64) (*OrderDTO, error) { + var row orderRow + db := r.db.WithContext(ctx) + if err := r.adminQuery(ctx). + Where("o.listing_id = ?", listingID). + Order("o.id DESC"). + First(&row).Error; err != nil { + return nil, err + } + dto := row.toAdminDTO() + applyPaymentDeadline(&dto, row.RentalOrder, pendingPaymentTimeoutMinutes(db)) + dto.ActiveDispute = r.activeDisputeDTO(ctx, row.ID) + dto.Checkout = r.latestCheckoutAdminDTO(ctx, row.ID) + return &dto, nil +} + func (r *Repository) FindForUser(ctx context.Context, userID uint64, orderID uint64) (*OrderDTO, error) { var row orderRow db := r.db.WithContext(ctx) diff --git a/backend/internal/modules/order/service.go b/backend/internal/modules/order/service.go index 5aec901..568f2f9 100644 --- a/backend/internal/modules/order/service.go +++ b/backend/internal/modules/order/service.go @@ -159,6 +159,16 @@ func (s *Service) FindAdmin(ctx context.Context, orderID uint64) (*OrderDTO, err return s.repo.FindAdmin(ctx, orderID) } +func (s *Service) FindLatestAdminByListing(ctx context.Context, listingID uint64) (*OrderDTO, error) { + if s.repo == nil { + return nil, ErrDependencyUnavailable + } + if listingID == 0 { + return nil, ErrListingUnavailable + } + return s.repo.FindLatestAdminByListing(ctx, listingID) +} + func (s *Service) HandoffRecordsAdmin(ctx context.Context, orderID uint64) ([]HandoffRecordDTO, error) { if s.repo == nil { return nil, ErrDependencyUnavailable diff --git a/backend/internal/router/router.go b/backend/internal/router/router.go index c86000b..4d4d492 100644 --- a/backend/internal/router/router.go +++ b/backend/internal/router/router.go @@ -489,6 +489,7 @@ func New(cfg config.Config, deps Dependencies, logger *zap.Logger) *gin.Engine { adminRoutes.POST("/users/:id/deposit-free-quota", requirePerm("user:deposit_free"), adminUserHandler.SetDepositFreeQuota) adminRoutes.POST("/users/:id/revoke-realname", requirePerm("user:revoke_realname"), adminUserHandler.RevokeRealname) adminRoutes.GET("/orders", requirePerm("order:view"), orderHandler.AdminList) + adminRoutes.GET("/listing-orders/:id/latest", requirePerm("order:view"), orderHandler.AdminLatestByListing) adminRoutes.GET("/orders/:id", requirePerm("order:view"), orderHandler.AdminDetail) adminRoutes.GET("/orders/:id/handoff-records", requirePerm("order:view"), orderHandler.AdminHandoffRecords) adminRoutes.POST("/orders/:id/close", requirePerm("order:close"), orderHandler.AdminClose) diff --git a/frontend/src/features/admin/api/adminPayments.ts b/frontend/src/features/admin/api/adminPayments.ts index 0ffc002..aea7133 100644 --- a/frontend/src/features/admin/api/adminPayments.ts +++ b/frontend/src/features/admin/api/adminPayments.ts @@ -36,15 +36,18 @@ export interface AdminPaymentQuery { provider?: string page?: number page_size?: number + silent?: boolean } export async function fetchAdminPayments(query: AdminPaymentQuery = {}) { const params = Object.fromEntries( - Object.entries(query).filter(([, value]) => value !== '' && value !== undefined) + Object.entries(query).filter( + ([key, value]) => key !== 'silent' && value !== '' && value !== undefined + ) ) const { data } = await apiClient.get>>( '/admin/payments', - { params } + { params, silent: query.silent } ) const result = data.data return { diff --git a/frontend/src/features/admin/views/AdminChatsView.vue b/frontend/src/features/admin/views/AdminChatsView.vue index c91e0b8..aaad64d 100644 --- a/frontend/src/features/admin/views/AdminChatsView.vue +++ b/frontend/src/features/admin/views/AdminChatsView.vue @@ -1,5 +1,6 @@