diff --git a/backend/internal/modules/order/dto.go b/backend/internal/modules/order/dto.go index 057896b..8cfbdfc 100644 --- a/backend/internal/modules/order/dto.go +++ b/backend/internal/modules/order/dto.go @@ -83,6 +83,15 @@ type AdminActionRequest struct { Reason string `json:"reason" binding:"required"` } +type AdminOrderQuery struct { + Page int + PageSize int + Keyword string + Status string + HandoffStatus string + SettlementStatus string +} + type AuditMeta = auditlog.Meta type PaginatedResult struct { diff --git a/backend/internal/modules/order/handler_admin.go b/backend/internal/modules/order/handler_admin.go index d6564f6..681901f 100644 --- a/backend/internal/modules/order/handler_admin.go +++ b/backend/internal/modules/order/handler_admin.go @@ -10,8 +10,8 @@ import ( ) func (h *Handler) AdminList(c *gin.Context) { - page, pageSize := parsePagination(c) - result, err := h.service.ListAdmin(c.Request.Context(), page, pageSize) + query := parseAdminOrderQuery(c) + result, err := h.service.ListAdmin(c.Request.Context(), query) if err != nil { writeOrderError(c, err) return diff --git a/backend/internal/modules/order/handler_parse.go b/backend/internal/modules/order/handler_parse.go index c80510e..5fe77e1 100644 --- a/backend/internal/modules/order/handler_parse.go +++ b/backend/internal/modules/order/handler_parse.go @@ -2,6 +2,7 @@ package order import ( "strconv" + "strings" "hfb_sys/backend/internal/middleware" "hfb_sys/backend/pkg/response" @@ -50,3 +51,15 @@ func parsePagination(c *gin.Context) (int, int) { } return page, pageSize } + +func parseAdminOrderQuery(c *gin.Context) AdminOrderQuery { + page, pageSize := parsePagination(c) + return AdminOrderQuery{ + Page: page, + PageSize: pageSize, + Keyword: strings.TrimSpace(c.Query("keyword")), + Status: strings.TrimSpace(c.Query("status")), + HandoffStatus: strings.TrimSpace(c.Query("handoff_status")), + SettlementStatus: strings.TrimSpace(c.Query("settlement_status")), + } +} diff --git a/backend/internal/modules/order/queries.go b/backend/internal/modules/order/queries.go index bd3f74b..6cd2625 100644 --- a/backend/internal/modules/order/queries.go +++ b/backend/internal/modules/order/queries.go @@ -3,6 +3,7 @@ package order import ( "context" "strconv" + "strings" "time" "hfb_sys/backend/internal/model" @@ -33,16 +34,25 @@ func (r *Repository) ListForUser(ctx context.Context, userID uint64) ([]OrderDTO return items, nil } -func (r *Repository) ListAdmin(ctx context.Context, page, pageSize int) (*PaginatedResult, error) { +func (r *Repository) ListAdmin(ctx context.Context, query AdminOrderQuery) (*PaginatedResult, error) { var total int64 db := r.db.WithContext(ctx) - if err := r.adminQuery(ctx).Count(&total).Error; err != nil { + countDB := applyAdminOrderFilters(r.adminOrderJoinQuery(ctx), query) + if err := countDB.Count(&total).Error; err != nil { return nil, err } + page := query.Page + pageSize := query.PageSize + if page < 1 { + page = 1 + } + if pageSize < 1 { + pageSize = 20 + } offset := (page - 1) * pageSize var rows []orderRow - err := r.adminQuery(ctx). + err := applyAdminOrderFilters(r.adminQuery(ctx), query). Order("o.id DESC"). Limit(pageSize). Offset(offset). @@ -67,6 +77,45 @@ func (r *Repository) ListAdmin(ctx context.Context, page, pageSize int) (*Pagina }, nil } +func applyAdminOrderFilters(db *gorm.DB, query AdminOrderQuery) *gorm.DB { + if query.Status != "" { + db = db.Where("o.status = ?", query.Status) + } + if query.HandoffStatus != "" { + db = db.Where("o.handoff_status = ?", query.HandoffStatus) + } + if query.SettlementStatus != "" { + db = db.Where("o.settlement_status = ?", query.SettlementStatus) + } + if keyword := strings.TrimSpace(query.Keyword); keyword != "" { + like := "%" + keyword + "%" + if id, err := strconv.ParseUint(keyword, 10, 64); err == nil { + db = db.Where( + `(o.order_no LIKE ? OR l.listing_no LIKE ? OR a.title LIKE ? OR owner.phone LIKE ? OR renter.phone LIKE ? OR o.id = ? OR o.listing_id = ? OR o.owner_id = ? OR o.renter_id = ?)`, + like, + like, + like, + like, + like, + id, + id, + id, + id, + ) + } else { + db = db.Where( + `(o.order_no LIKE ? OR l.listing_no LIKE ? OR a.title LIKE ? OR owner.phone LIKE ? OR renter.phone LIKE ?)`, + like, + like, + like, + like, + like, + ) + } + } + return db +} + func (r *Repository) FindAdmin(ctx context.Context, orderID uint64) (*OrderDTO, error) { var row orderRow db := r.db.WithContext(ctx) @@ -191,8 +240,12 @@ func (r *Repository) baseQuery(ctx context.Context) *gorm.DB { } func (r *Repository) adminQuery(ctx context.Context) *gorm.DB { + return r.adminOrderJoinQuery(ctx). + Select("o.*, l.listing_no, a.title, a.server_region, a.login_platform, owner.phone AS owner_phone, renter.phone AS renter_phone") +} + +func (r *Repository) adminOrderJoinQuery(ctx context.Context) *gorm.DB { return r.db.WithContext(ctx).Table("rental_orders AS o"). - Select("o.*, l.listing_no, a.title, a.server_region, a.login_platform, owner.phone AS owner_phone, renter.phone AS renter_phone"). Joins("JOIN rental_listings AS l ON l.id = o.listing_id"). Joins("JOIN game_accounts AS a ON a.id = o.account_id"). Joins("JOIN users AS owner ON owner.id = o.owner_id"). diff --git a/backend/internal/modules/order/service.go b/backend/internal/modules/order/service.go index 568f2f9..af673d1 100644 --- a/backend/internal/modules/order/service.go +++ b/backend/internal/modules/order/service.go @@ -145,11 +145,11 @@ func (s *Service) ListForUser(ctx context.Context, userID uint64) ([]OrderDTO, e return s.repo.ListForUser(ctx, userID) } -func (s *Service) ListAdmin(ctx context.Context, page, pageSize int) (*PaginatedResult, error) { +func (s *Service) ListAdmin(ctx context.Context, query AdminOrderQuery) (*PaginatedResult, error) { if s.repo == nil { return nil, ErrDependencyUnavailable } - return s.repo.ListAdmin(ctx, page, pageSize) + return s.repo.ListAdmin(ctx, query) } func (s *Service) FindAdmin(ctx context.Context, orderID uint64) (*OrderDTO, error) { diff --git a/frontend/components.d.ts b/frontend/components.d.ts index 67cffaa..39945f3 100644 --- a/frontend/components.d.ts +++ b/frontend/components.d.ts @@ -14,18 +14,7 @@ declare module 'vue' { ChatAttachmentImage: typeof import('./src/components/ChatAttachmentImage.vue')['default'] ElAlert: typeof import('element-plus/es')['ElAlert'] ElAvatar: typeof import('element-plus/es')['ElAvatar'] - ElBadge: typeof import('element-plus/es')['ElBadge'] ElButton: typeof import('element-plus/es')['ElButton'] - ElCard: typeof import('element-plus/es')['ElCard'] - ElCarousel: typeof import('element-plus/es')['ElCarousel'] - ElCarouselItem: typeof import('element-plus/es')['ElCarouselItem'] - ElCheckbox: typeof import('element-plus/es')['ElCheckbox'] - ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup'] - ElCollapse: typeof import('element-plus/es')['ElCollapse'] - ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem'] - ElDatePicker: typeof import('element-plus/es')['ElDatePicker'] - ElDescriptions: typeof import('element-plus/es')['ElDescriptions'] - ElDescriptionsItem: typeof import('element-plus/es')['ElDescriptionsItem'] ElDialog: typeof import('element-plus/es')['ElDialog'] ElDropdown: typeof import('element-plus/es')['ElDropdown'] ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem'] @@ -34,33 +23,17 @@ declare module 'vue' { ElForm: typeof import('element-plus/es')['ElForm'] ElFormItem: typeof import('element-plus/es')['ElFormItem'] ElIcon: typeof import('element-plus/es')['ElIcon'] - ElImage: typeof import('element-plus/es')['ElImage'] ElInput: typeof import('element-plus/es')['ElInput'] ElInputNumber: typeof import('element-plus/es')['ElInputNumber'] - ElLink: typeof import('element-plus/es')['ElLink'] ElMenu: typeof import('element-plus/es')['ElMenu'] ElMenuItem: typeof import('element-plus/es')['ElMenuItem'] ElOption: typeof import('element-plus/es')['ElOption'] - ElPagination: typeof import('element-plus/es')['ElPagination'] - ElRadio: typeof import('element-plus/es')['ElRadio'] - ElRadioButton: typeof import('element-plus/es')['ElRadioButton'] - ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup'] - ElSegmented: typeof import('element-plus/es')['ElSegmented'] ElSelect: typeof import('element-plus/es')['ElSelect'] - ElSlider: typeof import('element-plus/es')['ElSlider'] - ElStep: typeof import('element-plus/es')['ElStep'] - ElSteps: typeof import('element-plus/es')['ElSteps'] - ElSwitch: typeof import('element-plus/es')['ElSwitch'] ElTable: typeof import('element-plus/es')['ElTable'] ElTableColumn: typeof import('element-plus/es')['ElTableColumn'] - ElTabPane: typeof import('element-plus/es')['ElTabPane'] - ElTabs: typeof import('element-plus/es')['ElTabs'] ElTag: typeof import('element-plus/es')['ElTag'] ElTimeline: typeof import('element-plus/es')['ElTimeline'] ElTimelineItem: typeof import('element-plus/es')['ElTimelineItem'] - ElTimePicker: typeof import('element-plus/es')['ElTimePicker'] - ElTooltip: typeof import('element-plus/es')['ElTooltip'] - ElUpload: typeof import('element-plus/es')['ElUpload'] GuideImage: typeof import('./src/components/GuideImage.vue')['default'] InAppBrowserPrompt: typeof import('./src/components/InAppBrowserPrompt.vue')['default'] MobileBottomNav: typeof import('./src/components/MobileBottomNav.vue')['default'] @@ -69,26 +42,10 @@ declare module 'vue' { VanButton: typeof import('vant/es')['Button'] VanCell: typeof import('vant/es')['Cell'] VanCellGroup: typeof import('vant/es')['CellGroup'] - VanCheckbox: typeof import('vant/es')['Checkbox'] - VanCollapse: typeof import('vant/es')['Collapse'] - VanCollapseItem: typeof import('vant/es')['CollapseItem'] VanEmpty: typeof import('vant/es')['Empty'] VanField: typeof import('vant/es')['Field'] VanIcon: typeof import('vant/es')['Icon'] - VanList: typeof import('vant/es')['List'] - VanLoading: typeof import('vant/es')['Loading'] - VanNoticeBar: typeof import('vant/es')['NoticeBar'] - VanPopup: typeof import('vant/es')['Popup'] - VanPullRefresh: typeof import('vant/es')['PullRefresh'] - VanRadio: typeof import('vant/es')['Radio'] - VanRadioGroup: typeof import('vant/es')['RadioGroup'] - VanSearch: typeof import('vant/es')['Search'] VanStepper: typeof import('vant/es')['Stepper'] - VanSwipe: typeof import('vant/es')['Swipe'] - VanSwipeItem: typeof import('vant/es')['SwipeItem'] - VanTab: typeof import('vant/es')['Tab'] - VanTabs: typeof import('vant/es')['Tabs'] - VanTag: typeof import('vant/es')['Tag'] } export interface GlobalDirectives { vLoading: typeof import('element-plus/es')['ElLoadingDirective'] diff --git a/frontend/src/features/admin/views/AdminOrdersView.vue b/frontend/src/features/admin/views/AdminOrdersView.vue index 7fd94ec..f94d655 100644 --- a/frontend/src/features/admin/views/AdminOrdersView.vue +++ b/frontend/src/features/admin/views/AdminOrdersView.vue @@ -1,7 +1,8 @@