diff --git a/backend/internal/modules/dispute/dto.go b/backend/internal/modules/dispute/dto.go index f578fac..eb19b56 100644 --- a/backend/internal/modules/dispute/dto.go +++ b/backend/internal/modules/dispute/dto.go @@ -51,6 +51,15 @@ type ArbitrateRequest struct { Remark string `json:"remark" binding:"required"` AmountCent int64 `json:"amount_cent"` } + +type AdminListQuery struct { + Page int + PageSize int + Keyword string + Status string + Type string +} + type AuditMeta = auditlog.Meta type PaginatedResult struct { diff --git a/backend/internal/modules/dispute/handler.go b/backend/internal/modules/dispute/handler.go index fb3a313..7648521 100644 --- a/backend/internal/modules/dispute/handler.go +++ b/backend/internal/modules/dispute/handler.go @@ -4,6 +4,7 @@ import ( "errors" "net/http" "strconv" + "strings" "hfb_sys/backend/internal/middleware" "hfb_sys/backend/pkg/response" @@ -113,7 +114,14 @@ func (h *Handler) CancelByOrder(c *gin.Context) { func (h *Handler) AdminList(c *gin.Context) { page, pageSize := parsePagination(c) - result, err := h.service.ListAdmin(c.Request.Context(), page, pageSize) + query := AdminListQuery{ + Page: page, + PageSize: pageSize, + Keyword: strings.TrimSpace(c.Query("keyword")), + Status: strings.TrimSpace(c.Query("status")), + Type: strings.TrimSpace(c.Query("type")), + } + result, err := h.service.ListAdmin(c.Request.Context(), query) if err != nil { writeDisputeError(c, err) return diff --git a/backend/internal/modules/dispute/query.go b/backend/internal/modules/dispute/query.go index bcf5784..a26c35b 100644 --- a/backend/internal/modules/dispute/query.go +++ b/backend/internal/modules/dispute/query.go @@ -2,6 +2,8 @@ package dispute import ( "context" + "strconv" + "strings" "hfb_sys/backend/internal/model" @@ -39,14 +41,27 @@ func (r *Repository) FindForUser(ctx context.Context, userID uint64, id uint64) return &dto, nil } -func (r *Repository) ListAdmin(ctx context.Context, page, pageSize int) (*PaginatedResult, error) { +func (r *Repository) ListAdmin(ctx context.Context, query AdminListQuery) (*PaginatedResult, error) { var total int64 - if err := r.db.WithContext(ctx).Model(&model.Dispute{}).Count(&total).Error; err != nil { + countDB := applyAdminListFilters(r.adminFilterQuery(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 []disputeRow - err := r.baseQuery(ctx).Order("d.id DESC").Offset(offset).Limit(pageSize).Scan(&rows).Error + err := applyAdminListFilters(r.baseQuery(ctx), query). + Order("d.id DESC"). + Offset(offset). + Limit(pageSize). + Scan(&rows).Error if err != nil { return nil, err } @@ -54,13 +69,54 @@ func (r *Repository) ListAdmin(ctx context.Context, page, pageSize int) (*Pagina } func (r *Repository) baseQuery(ctx context.Context) *gorm.DB { - return r.db.WithContext(ctx).Table("disputes AS d"). + return r.adminFilterQuery(ctx). Select(`d.*, o.order_no, o.status AS order_status, o.handoff_status, o.settlement_status, o.owner_id, o.renter_id, owner.phone AS owner_phone, renter.phone AS renter_phone, - l.listing_no, a.title`). + l.listing_no, a.title`) +} + +func (r *Repository) adminFilterQuery(ctx context.Context) *gorm.DB { + return r.db.WithContext(ctx).Table("disputes AS d"). Joins("JOIN rental_orders AS o ON o.id = d.order_id"). 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"). Joins("JOIN users AS renter ON renter.id = o.renter_id") } + +func applyAdminListFilters(db *gorm.DB, query AdminListQuery) *gorm.DB { + if query.Status != "" { + db = db.Where("d.status = ?", query.Status) + } + if query.Type != "" { + db = db.Where("d.type = ?", query.Type) + } + 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 d.id = ? OR d.order_id = ? OR o.listing_id = ? OR o.owner_id = ? OR o.renter_id = ?)`, + like, + like, + like, + like, + like, + id, + 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 +} diff --git a/backend/internal/modules/dispute/service.go b/backend/internal/modules/dispute/service.go index cc621b6..4884432 100644 --- a/backend/internal/modules/dispute/service.go +++ b/backend/internal/modules/dispute/service.go @@ -66,11 +66,11 @@ func (s *Service) CancelByOrder(ctx context.Context, userID uint64, orderID uint return s.repo.CancelByOrder(ctx, userID, orderID) } -func (s *Service) ListAdmin(ctx context.Context, page, pageSize int) (*PaginatedResult, error) { +func (s *Service) ListAdmin(ctx context.Context, query AdminListQuery) (*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) Arbitrate(ctx context.Context, adminID uint64, id uint64, req ArbitrateRequest, meta AuditMeta) (*DisputeDTO, error) { diff --git a/backend/internal/modules/listing/dto.go b/backend/internal/modules/listing/dto.go index a89ba67..4648428 100644 --- a/backend/internal/modules/listing/dto.go +++ b/backend/internal/modules/listing/dto.go @@ -73,6 +73,7 @@ type TransferOwnerRequest struct { type AdminListQuery struct { OwnerID uint64 + Keyword string Status string ReviewStatus string Limit int diff --git a/backend/internal/modules/listing/handler_parse.go b/backend/internal/modules/listing/handler_parse.go index f006378..ef9bc9b 100644 --- a/backend/internal/modules/listing/handler_parse.go +++ b/backend/internal/modules/listing/handler_parse.go @@ -71,6 +71,7 @@ func parseAdminListQuery(c *gin.Context) (AdminListQuery, bool) { } query.PageSize = value } + query.Keyword = strings.TrimSpace(c.Query("keyword")) query.Status = c.Query("status") query.ReviewStatus = c.Query("review_status") return query, true diff --git a/backend/internal/modules/listing/query.go b/backend/internal/modules/listing/query.go index 524dc24..2a35ad5 100644 --- a/backend/internal/modules/listing/query.go +++ b/backend/internal/modules/listing/query.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "strconv" + "strings" "time" "hfb_sys/backend/internal/model" @@ -56,6 +57,25 @@ func (r *Repository) applyAdminListFilters(db *gorm.DB, query AdminListQuery) *g if query.OwnerID > 0 { db = db.Where("l.owner_id = ?", query.OwnerID) } + if keyword := strings.TrimSpace(query.Keyword); keyword != "" { + like := "%" + keyword + "%" + if id, err := strconv.ParseUint(keyword, 10, 64); err == nil { + db = db.Where( + `(l.listing_no LIKE ? OR EXISTS (SELECT 1 FROM rental_orders AS o WHERE o.listing_id = l.id AND o.order_no LIKE ?) OR l.id = ? OR l.account_id = ? OR l.owner_id = ?)`, + like, + like, + id, + id, + id, + ) + } else { + db = db.Where( + `(l.listing_no LIKE ? OR EXISTS (SELECT 1 FROM rental_orders AS o WHERE o.listing_id = l.id AND o.order_no LIKE ?))`, + like, + like, + ) + } + } if query.Status != "" { db = db.Where("l.status = ?", query.Status) } diff --git a/frontend/src/features/admin/views/AdminDisputesView.vue b/frontend/src/features/admin/views/AdminDisputesView.vue index fa7b9b2..7c02318 100644 --- a/frontend/src/features/admin/views/AdminDisputesView.vue +++ b/frontend/src/features/admin/views/AdminDisputesView.vue @@ -1,5 +1,6 @@