diff --git a/backend/internal/e2e/rental_flow_test.go b/backend/internal/e2e/rental_flow_test.go index f7fbc2b..17c625e 100644 --- a/backend/internal/e2e/rental_flow_test.go +++ b/backend/internal/e2e/rental_flow_test.go @@ -380,7 +380,7 @@ func seedMockPaymentConfig(t *testing.T, service *paymentconfig.Service, adminID func createOwnerPaymentAccount(t *testing.T, service *paymentaccount.Service, ownerID uint64) *paymentaccount.PaymentAccountDTO { t.Helper() - account, err := service.Create(ownerID, paymentaccount.CreatePaymentAccountRequest{ + account, err := service.Create(t.Context(), ownerID, paymentaccount.CreatePaymentAccountRequest{ AccountType: "alipay", AccountName: "张三", AccountNo: "owner@example.com", @@ -458,7 +458,7 @@ func assertFinanceDashboard(t *testing.T, service *adminfinance.Service, orderNo StartDate: now.Add(-time.Hour), EndDate: now.Add(time.Hour), } - dashboard, err := service.Dashboard(query) + dashboard, err := service.Dashboard(t.Context(), query) if err != nil { t.Fatalf("读取财务看板失败: %v", err) } @@ -469,7 +469,7 @@ func assertFinanceDashboard(t *testing.T, service *adminfinance.Service, orderNo assertEqual(t, "财务号主钱包入账", dashboard.Summary.OwnerWalletIncomeAmountCent, int64(27500)) assertEqual(t, "财务结算订单数", dashboard.Summary.SettledOrderCount, int64(1)) - details, err := service.Details(adminfinance.DetailQuery{ + details, err := service.Details(t.Context(), adminfinance.DetailQuery{ OrderNo: orderNo, DateType: "settled", StartDate: query.StartDate, @@ -497,7 +497,7 @@ func assertWalletAndWithdrawal(t *testing.T, walletService *wallet.Service, with } assertEqual(t, "提现前可用余额", accountBefore.AvailableBalanceCent, int64(27500)) - req, err := withdrawalService.Create(ownerID, withdrawal.CreateWithdrawalRequest{ + req, err := withdrawalService.Create(t.Context(), ownerID, withdrawal.CreateWithdrawalRequest{ PaymentAccountID: paymentAccountID, AmountCent: 10000, }) @@ -507,7 +507,7 @@ func assertWalletAndWithdrawal(t *testing.T, walletService *wallet.Service, with assertEqual(t, "提现申请金额", req.AmountCent, int64(10000)) assertEqual(t, "提现申请状态", req.Status, "pending") - reviewed, err := withdrawalService.Review(adminID, req.ID, withdrawal.ReviewWithdrawalRequest{ + reviewed, err := withdrawalService.Review(t.Context(), adminID, req.ID, withdrawal.ReviewWithdrawalRequest{ Approved: true, Remark: "E2E 审核通过", }) @@ -516,7 +516,7 @@ func assertWalletAndWithdrawal(t *testing.T, walletService *wallet.Service, with } assertEqual(t, "提现审核后状态", reviewed.Status, "processing") - paid, err := withdrawalService.ConfirmPayment(adminID, req.ID, withdrawal.ConfirmPaymentRequest{ + paid, err := withdrawalService.ConfirmPayment(t.Context(), adminID, req.ID, withdrawal.ConfirmPaymentRequest{ PaymentProofURL: "https://example.com/proof.png", Remark: "E2E 已打款", }) diff --git a/backend/internal/middleware/permission.go b/backend/internal/middleware/permission.go index 3f59de2..0b39ecc 100644 --- a/backend/internal/middleware/permission.go +++ b/backend/internal/middleware/permission.go @@ -1,7 +1,6 @@ package middleware import ( - "context" "encoding/json" "errors" "fmt" @@ -53,9 +52,8 @@ func getPermCodes(c *gin.Context, rdb *redis.Client, adminID uint64) ([]string, if rdb == nil { return nil, errors.New("redis unavailable") } - ctx := context.Background() key := fmt.Sprintf("admin:perms:%d", adminID) - raw, err := rdb.Get(ctx, key).Result() + raw, err := rdb.Get(c.Request.Context(), key).Result() if errors.Is(err, redis.Nil) { return nil, nil // 缓存未命中,视为无权限 } diff --git a/backend/internal/modules/adminauth/repository.go b/backend/internal/modules/adminauth/repository.go index 7eef6b7..f335d70 100644 --- a/backend/internal/modules/adminauth/repository.go +++ b/backend/internal/modules/adminauth/repository.go @@ -197,7 +197,7 @@ func (r *Repository) loadRolesAndPerms(ctx context.Context, dto *AdminDTO) { for _, role := range roles { if role.Code == "super_admin" { dto.Permissions = []string{"*"} - cachePermissions(r, dto.ID, dto.Permissions) + cachePermissions(ctx, r, dto.ID, dto.Permissions) return } } @@ -212,14 +212,13 @@ func (r *Repository) loadRolesAndPerms(ctx context.Context, dto *AdminDTO) { dto.Permissions = permCodes // 缓存权限到 Redis - cachePermissions(r, dto.ID, permCodes) + cachePermissions(ctx, r, dto.ID, permCodes) } -func cachePermissions(r *Repository, adminID uint64, permCodes []string) { +func cachePermissions(ctx context.Context, r *Repository, adminID uint64, permCodes []string) { if r.redis == nil || len(permCodes) == 0 { return } - ctx := context.Background() key := fmt.Sprintf("admin:perms:%d", adminID) raw, _ := json.Marshal(permCodes) r.redis.Set(ctx, key, string(raw), 2*time.Hour) diff --git a/backend/internal/modules/admindashboard/handler.go b/backend/internal/modules/admindashboard/handler.go index 1b24db4..f46d9b3 100644 --- a/backend/internal/modules/admindashboard/handler.go +++ b/backend/internal/modules/admindashboard/handler.go @@ -18,7 +18,7 @@ func NewHandler(service *Service) *Handler { } func (h *Handler) Summary(c *gin.Context) { - item, err := h.service.Summary() + item, err := h.service.Summary(c.Request.Context()) if err != nil { writeDashboardError(c, err) return diff --git a/backend/internal/modules/admindashboard/repository.go b/backend/internal/modules/admindashboard/repository.go index 231d164..d0b7711 100644 --- a/backend/internal/modules/admindashboard/repository.go +++ b/backend/internal/modules/admindashboard/repository.go @@ -1,6 +1,7 @@ package admindashboard import ( + "context" "time" "hfb_sys/backend/internal/model" @@ -16,56 +17,57 @@ func NewRepository(db *gorm.DB) *Repository { return &Repository{db: db} } -func (r *Repository) Summary() (*DashboardDTO, error) { +func (r *Repository) Summary(ctx context.Context) (*DashboardDTO, error) { + db := r.db.WithContext(ctx) now := time.Now() today := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location()) metrics := MetricsDTO{} pending := PendingDTO{} - if err := r.db.Model(&model.User{}).Count(&metrics.TotalUsers).Error; err != nil { + if err := db.Model(&model.User{}).Count(&metrics.TotalUsers).Error; err != nil { return nil, err } - if err := r.db.Model(&model.User{}).Where("realname_status = ?", "verified").Count(&metrics.VerifiedUsers).Error; err != nil { + if err := db.Model(&model.User{}).Where("realname_status = ?", "verified").Count(&metrics.VerifiedUsers).Error; err != nil { return nil, err } - if err := r.db.Model(&model.RentalListing{}).Count(&metrics.TotalListings).Error; err != nil { + if err := db.Model(&model.RentalListing{}).Count(&metrics.TotalListings).Error; err != nil { return nil, err } - if err := r.db.Model(&model.RentalListing{}).Where("status = ? AND review_status = ?", "published", "approved").Count(&metrics.PublishedListings).Error; err != nil { + if err := db.Model(&model.RentalListing{}).Where("status = ? AND review_status = ?", "published", "approved").Count(&metrics.PublishedListings).Error; err != nil { return nil, err } - if err := r.db.Model(&model.RentalOrder{}).Count(&metrics.TotalOrders).Error; err != nil { + if err := db.Model(&model.RentalOrder{}).Count(&metrics.TotalOrders).Error; err != nil { return nil, err } - if err := r.db.Model(&model.RentalOrder{}).Where("status = ?", "renting").Count(&metrics.RentingOrders).Error; err != nil { + if err := db.Model(&model.RentalOrder{}).Where("status = ?", "renting").Count(&metrics.RentingOrders).Error; err != nil { return nil, err } - if err := r.db.Model(&model.RentalOrder{}).Where("created_at >= ?", today).Count(&metrics.TodayOrders).Error; err != nil { + if err := db.Model(&model.RentalOrder{}).Where("created_at >= ?", today).Count(&metrics.TodayOrders).Error; err != nil { return nil, err } - if err := r.db.Model(&model.WalletLedger{}). + if err := db.Model(&model.WalletLedger{}). Select("COALESCE(SUM(amount_cent), 0)"). Where("created_at >= ?", today). Scan(&metrics.TodayLedgerAmountCent).Error; err != nil { return nil, err } - if err := r.db.Model(&model.RentalListing{}).Where("review_status = ?", "pending").Count(&pending.ListingReviews).Error; err != nil { + if err := db.Model(&model.RentalListing{}).Where("review_status = ?", "pending").Count(&pending.ListingReviews).Error; err != nil { return nil, err } - if err := r.db.Model(&model.Dispute{}).Where("status IN ?", []string{"open", "processing"}).Count(&pending.Disputes).Error; err != nil { + if err := db.Model(&model.Dispute{}).Where("status IN ?", []string{"open", "processing"}).Count(&pending.Disputes).Error; err != nil { return nil, err } - if err := r.db.Model(&model.RentalOrder{}).Where("status = ? AND handoff_status IN ?", "pending_handoff", []string{"pending_owner", "pending_renter_confirm"}).Count(&pending.PendingHandoffs).Error; err != nil { + if err := db.Model(&model.RentalOrder{}).Where("status = ? AND handoff_status IN ?", "pending_handoff", []string{"pending_owner", "pending_renter_confirm"}).Count(&pending.PendingHandoffs).Error; err != nil { return nil, err } - if err := r.db.Model(&model.RentalOrder{}).Where("status IN ?", []string{"pending_checkout_confirm", "pending_checkout_accept"}).Count(&pending.PendingReturnConfirms).Error; err != nil { + if err := db.Model(&model.RentalOrder{}).Where("status IN ?", []string{"pending_checkout_confirm", "pending_checkout_accept"}).Count(&pending.PendingReturnConfirms).Error; err != nil { return nil, err } - recentOrders, err := r.recentOrders() + recentOrders, err := r.recentOrders(ctx) if err != nil { return nil, err } - recentDisputes, err := r.recentDisputes() + recentDisputes, err := r.recentDisputes(ctx) if err != nil { return nil, err } @@ -78,9 +80,9 @@ func (r *Repository) Summary() (*DashboardDTO, error) { }, nil } -func (r *Repository) recentOrders() ([]RecentOrderDTO, error) { +func (r *Repository) recentOrders(ctx context.Context) ([]RecentOrderDTO, error) { rows := make([]RecentOrderDTO, 0) - err := r.db.Table("rental_orders AS o"). + err := r.db.WithContext(ctx).Table("rental_orders AS o"). Select("o.id, o.order_no, a.title, o.renter_id, o.owner_id, o.status, o.rent_amount_cent, o.deposit_amount_cent, o.created_at"). Joins("JOIN game_accounts AS a ON a.id = o.account_id"). Order("o.id DESC"). @@ -89,9 +91,9 @@ func (r *Repository) recentOrders() ([]RecentOrderDTO, error) { return rows, err } -func (r *Repository) recentDisputes() ([]RecentDisputeDTO, error) { +func (r *Repository) recentDisputes(ctx context.Context) ([]RecentDisputeDTO, error) { rows := make([]RecentDisputeDTO, 0) - err := r.db.Table("disputes AS d"). + err := r.db.WithContext(ctx).Table("disputes AS d"). Select("d.id, d.order_id, o.order_no, a.title, d.type, d.status, d.initiator_id, d.created_at"). Joins("JOIN rental_orders AS o ON o.id = d.order_id"). Joins("JOIN game_accounts AS a ON a.id = o.account_id"). diff --git a/backend/internal/modules/admindashboard/service.go b/backend/internal/modules/admindashboard/service.go index fd13ae6..f243910 100644 --- a/backend/internal/modules/admindashboard/service.go +++ b/backend/internal/modules/admindashboard/service.go @@ -1,6 +1,9 @@ package admindashboard -import "errors" +import ( + "context" + "errors" +) var ErrDependencyUnavailable = errors.New("dependency unavailable") @@ -12,9 +15,9 @@ func NewService(repo *Repository) *Service { return &Service{repo: repo} } -func (s *Service) Summary() (*DashboardDTO, error) { +func (s *Service) Summary(ctx context.Context) (*DashboardDTO, error) { if s.repo == nil { return nil, ErrDependencyUnavailable } - return s.repo.Summary() + return s.repo.Summary(ctx) } diff --git a/backend/internal/modules/adminfinance/handler.go b/backend/internal/modules/adminfinance/handler.go index 673ad77..63054f0 100644 --- a/backend/internal/modules/adminfinance/handler.go +++ b/backend/internal/modules/adminfinance/handler.go @@ -25,7 +25,7 @@ func (h *Handler) Dashboard(c *gin.Context) { if !ok { return } - item, err := h.service.Dashboard(query) + item, err := h.service.Dashboard(c.Request.Context(), query) if err != nil { writeFinanceError(c, err) return @@ -38,7 +38,7 @@ func (h *Handler) Details(c *gin.Context) { if !ok { return } - result, err := h.service.Details(query) + result, err := h.service.Details(c.Request.Context(), query) if err != nil { writeFinanceError(c, err) return diff --git a/backend/internal/modules/adminfinance/repository.go b/backend/internal/modules/adminfinance/repository.go index 5f645c3..ae7c783 100644 --- a/backend/internal/modules/adminfinance/repository.go +++ b/backend/internal/modules/adminfinance/repository.go @@ -1,6 +1,7 @@ package adminfinance import ( + "context" "time" "hfb_sys/backend/internal/timeutil" @@ -16,12 +17,12 @@ func NewRepository(db *gorm.DB) *Repository { return &Repository{db: db} } -func (r *Repository) Dashboard(query DashboardQuery) (*DashboardDTO, error) { - dailyItems, err := r.dailyItems(query) +func (r *Repository) Dashboard(ctx context.Context, query DashboardQuery) (*DashboardDTO, error) { + dailyItems, err := r.dailyItems(ctx, query) if err != nil { return nil, err } - summary, err := r.summary(query) + summary, err := r.summary(ctx, query) if err != nil { return nil, err } @@ -32,10 +33,11 @@ func (r *Repository) Dashboard(query DashboardQuery) (*DashboardDTO, error) { }, nil } -func (r *Repository) Details(query DetailQuery) (*PaginatedResult, error) { - db := r.financeDetailBaseQuery(query) +func (r *Repository) Details(ctx context.Context, query DetailQuery) (*PaginatedResult, error) { + baseDB := r.db.WithContext(ctx) + db := r.financeDetailBaseQuery(ctx, query) var total int64 - countDB := r.db.Table("(?) AS finance_rows", db) + countDB := baseDB.Table("(?) AS finance_rows", db) if err := countDB.Count(&total).Error; err != nil { return nil, err } @@ -52,9 +54,10 @@ func (r *Repository) Details(query DetailQuery) (*PaginatedResult, error) { return &PaginatedResult{Items: items, Total: total, Page: query.Page, PageSize: query.PageSize}, nil } -func (r *Repository) summary(query DashboardQuery) (*FinanceSummaryDTO, error) { +func (r *Repository) summary(ctx context.Context, query DashboardQuery) (*FinanceSummaryDTO, error) { + db := r.db.WithContext(ctx) var payment paymentSummaryRow - if err := r.db.Table("payment_orders"). + if err := db.Table("payment_orders"). Select(`COALESCE(SUM(CASE WHEN biz_type IN ('order_pay', 'wallet_recharge') AND status = 'paid' THEN amount_cent ELSE 0 END), 0) AS total_flow_amount_cent, COALESCE(SUM(CASE WHEN biz_type IN ? AND status = 'refunded' THEN amount_cent ELSE 0 END), 0) AS total_refund_amount_cent, COALESCE(SUM(CASE WHEN biz_type IN ? AND status = 'refunding' THEN amount_cent ELSE 0 END), 0) AS pending_refund_amount_cent, @@ -68,20 +71,20 @@ func (r *Repository) summary(query DashboardQuery) (*FinanceSummaryDTO, error) { } var settlement settlementSummaryRow - if err := r.db.Table("rental_orders AS ro"). + if err := db.Table("rental_orders AS ro"). Select(`COALESCE(SUM(oc.platform_fee_cent), 0) AS platform_income_amount_cent, COALESCE(SUM(oc.owner_income_amount_cent), 0) AS owner_should_income_amount_cent, COALESCE(SUM(COALESCE(w.owner_wallet_income_amount_cent, 0)), 0) AS owner_wallet_income_amount_cent, COUNT(ro.id) AS settled_order_count`). Joins("JOIN order_checkouts AS oc ON oc.order_id = ro.id AND oc.status = 'accepted'"). - Joins("LEFT JOIN (?) AS w ON w.order_id = ro.id", ownerWalletIncomeSubquery(r.db)). + Joins("LEFT JOIN (?) AS w ON w.order_id = ro.id", ownerWalletIncomeSubquery(db)). Where("ro.settled_at >= ? AND ro.settled_at <= ?", query.StartDate, query.EndDate). Scan(&settlement).Error; err != nil { return nil, err } var exceptionCount int64 - if err := r.db.Table("(?) AS d", r.financeDetailBaseQuery(DetailQuery{ + if err := db.Table("(?) AS d", r.financeDetailBaseQuery(ctx, DetailQuery{ DateType: "settled", StartDate: query.StartDate, EndDate: query.EndDate, @@ -108,9 +111,10 @@ func (r *Repository) summary(query DashboardQuery) (*FinanceSummaryDTO, error) { }, nil } -func (r *Repository) dailyItems(query DashboardQuery) ([]FinanceDailyDTO, error) { +func (r *Repository) dailyItems(ctx context.Context, query DashboardQuery) ([]FinanceDailyDTO, error) { + db := r.db.WithContext(ctx) payments := make([]dailyPaymentRow, 0) - if err := r.db.Table("payment_orders"). + if err := db.Table("payment_orders"). Select(`DATE(created_at) AS date, COALESCE(SUM(CASE WHEN biz_type IN ('order_pay', 'wallet_recharge') AND status = 'paid' THEN amount_cent ELSE 0 END), 0) AS total_flow_amount_cent, COALESCE(SUM(CASE WHEN biz_type IN ? AND status = 'refunded' THEN amount_cent ELSE 0 END), 0) AS total_refund_amount_cent, @@ -126,14 +130,14 @@ func (r *Repository) dailyItems(query DashboardQuery) ([]FinanceDailyDTO, error) } settlements := make([]dailySettlementRow, 0) - if err := r.db.Table("rental_orders AS ro"). + if err := db.Table("rental_orders AS ro"). Select(`DATE(ro.settled_at) AS date, COALESCE(SUM(oc.platform_fee_cent), 0) AS platform_income_amount_cent, COALESCE(SUM(oc.owner_income_amount_cent), 0) AS owner_should_income_amount_cent, COALESCE(SUM(COALESCE(w.owner_wallet_income_amount_cent, 0)), 0) AS owner_wallet_income_amount_cent, COUNT(ro.id) AS settled_order_count`). Joins("JOIN order_checkouts AS oc ON oc.order_id = ro.id AND oc.status = 'accepted'"). - Joins("LEFT JOIN (?) AS w ON w.order_id = ro.id", ownerWalletIncomeSubquery(r.db)). + Joins("LEFT JOIN (?) AS w ON w.order_id = ro.id", ownerWalletIncomeSubquery(db)). Where("ro.settled_at >= ? AND ro.settled_at <= ?", query.StartDate, query.EndDate). Group("DATE(ro.settled_at)"). Scan(&settlements).Error; err != nil { @@ -175,8 +179,8 @@ func (r *Repository) dailyItems(query DashboardQuery) ([]FinanceDailyDTO, error) return items, nil } -func (r *Repository) financeDetailBaseQuery(query DetailQuery) *gorm.DB { - db := r.db.Table("rental_orders AS ro"). +func (r *Repository) financeDetailBaseQuery(ctx context.Context, query DetailQuery) *gorm.DB { + db := r.db.WithContext(ctx).Table("rental_orders AS ro"). Select(`ro.id AS order_id, ro.order_no, ro.status AS order_status, ro.settlement_status, ro.refund_status, ro.renter_id, COALESCE(ru.phone, '') AS renter_phone, COALESCE(ru.nickname, '') AS renter_nickname, ro.owner_id, COALESCE(ou.phone, '') AS owner_phone, COALESCE(ou.nickname, '') AS owner_nickname, @@ -202,9 +206,9 @@ func (r *Repository) financeDetailBaseQuery(query DetailQuery) *gorm.DB { ro.created_at, ro.settled_at`). Joins("LEFT JOIN users AS ru ON ru.id = ro.renter_id"). Joins("LEFT JOIN users AS ou ON ou.id = ro.owner_id"). - Joins("LEFT JOIN (?) AS oc ON oc.order_id = ro.id", acceptedCheckoutSubquery(r.db)). - Joins("LEFT JOIN (?) AS p ON p.order_id = ro.id", orderPaymentSubquery(r.db)). - Joins("LEFT JOIN (?) AS w ON w.order_id = ro.id", ownerWalletIncomeSubquery(r.db)) + Joins("LEFT JOIN (?) AS oc ON oc.order_id = ro.id", acceptedCheckoutSubquery(r.db.WithContext(ctx))). + Joins("LEFT JOIN (?) AS p ON p.order_id = ro.id", orderPaymentSubquery(r.db.WithContext(ctx))). + Joins("LEFT JOIN (?) AS w ON w.order_id = ro.id", ownerWalletIncomeSubquery(r.db.WithContext(ctx))) if query.OrderNo != "" { db = db.Where("ro.order_no = ?", query.OrderNo) diff --git a/backend/internal/modules/adminfinance/service.go b/backend/internal/modules/adminfinance/service.go index 760c53f..f06ca49 100644 --- a/backend/internal/modules/adminfinance/service.go +++ b/backend/internal/modules/adminfinance/service.go @@ -1,6 +1,9 @@ package adminfinance -import "errors" +import ( + "context" + "errors" +) var ErrDependencyUnavailable = errors.New("dependency unavailable") @@ -12,14 +15,14 @@ func NewService(repo *Repository) *Service { return &Service{repo: repo} } -func (s *Service) Dashboard(query DashboardQuery) (*DashboardDTO, error) { +func (s *Service) Dashboard(ctx context.Context, query DashboardQuery) (*DashboardDTO, error) { if s.repo == nil { return nil, ErrDependencyUnavailable } - return s.repo.Dashboard(query) + return s.repo.Dashboard(ctx, query) } -func (s *Service) Details(query DetailQuery) (*PaginatedResult, error) { +func (s *Service) Details(ctx context.Context, query DetailQuery) (*PaginatedResult, error) { if s.repo == nil { return nil, ErrDependencyUnavailable } @@ -32,5 +35,5 @@ func (s *Service) Details(query DetailQuery) (*PaginatedResult, error) { if query.PageSize > 100 { query.PageSize = 100 } - return s.repo.Details(query) + return s.repo.Details(ctx, query) } diff --git a/backend/internal/modules/adminmgr/handler.go b/backend/internal/modules/adminmgr/handler.go index 08f2d3f..1ca8b29 100644 --- a/backend/internal/modules/adminmgr/handler.go +++ b/backend/internal/modules/adminmgr/handler.go @@ -22,7 +22,7 @@ func NewHandler(service *Service) *Handler { func (h *Handler) List(c *gin.Context) { page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20")) - result, err := h.service.List(page, pageSize) + result, err := h.service.List(c.Request.Context(), page, pageSize) if err != nil { writeError(c, err) return @@ -35,7 +35,7 @@ func (h *Handler) FindByID(c *gin.Context) { if !ok { return } - item, err := h.service.FindByID(id) + item, err := h.service.FindByID(c.Request.Context(), id) if err != nil { writeError(c, err) return @@ -49,7 +49,7 @@ func (h *Handler) Create(c *gin.Context) { response.BadRequest(c, "用户名和密码不能为空") return } - item, err := h.service.Create(req) + item, err := h.service.Create(c.Request.Context(), req) if err != nil { writeError(c, err) return @@ -67,7 +67,7 @@ func (h *Handler) Update(c *gin.Context) { response.BadRequest(c, "请求格式不正确") return } - item, err := h.service.Update(id, req) + item, err := h.service.Update(c.Request.Context(), id, req) if err != nil { writeError(c, err) return @@ -81,7 +81,7 @@ func (h *Handler) Delete(c *gin.Context) { return } currentAdminID, _ := c.Get(middleware.ContextAdminID) - if err := h.service.Delete(id, currentAdminID.(uint64)); err != nil { + if err := h.service.Delete(c.Request.Context(), id, currentAdminID.(uint64)); err != nil { writeError(c, err) return } @@ -98,7 +98,7 @@ func (h *Handler) AssignRoles(c *gin.Context) { response.BadRequest(c, "请求格式不正确") return } - if err := h.service.AssignRoles(id, req); err != nil { + if err := h.service.AssignRoles(c.Request.Context(), id, req); err != nil { writeError(c, err) return } @@ -115,7 +115,7 @@ func (h *Handler) ChangePassword(c *gin.Context) { response.BadRequest(c, "密码不能为空") return } - if err := h.service.ChangePassword(id, req); err != nil { + if err := h.service.ChangePassword(c.Request.Context(), id, req); err != nil { writeError(c, err) return } diff --git a/backend/internal/modules/adminmgr/repository.go b/backend/internal/modules/adminmgr/repository.go index f63c3b5..a71ca93 100644 --- a/backend/internal/modules/adminmgr/repository.go +++ b/backend/internal/modules/adminmgr/repository.go @@ -23,19 +23,20 @@ func NewRepository(db *gorm.DB, redis *redis.Client) *Repository { return &Repository{db: db, redis: redis} } -func (r *Repository) List(page, pageSize int) (*PaginatedResult, error) { +func (r *Repository) List(ctx context.Context, page, pageSize int) (*PaginatedResult, error) { + db := r.db.WithContext(ctx) var total int64 - if err := r.db.Model(&model.AdminUser{}).Count(&total).Error; err != nil { + if err := db.Model(&model.AdminUser{}).Count(&total).Error; err != nil { return nil, err } offset := (page - 1) * pageSize var admins []model.AdminUser - if err := r.db.Order("id DESC").Offset(offset).Limit(pageSize).Find(&admins).Error; err != nil { + if err := db.Order("id DESC").Offset(offset).Limit(pageSize).Find(&admins).Error; err != nil { return nil, err } items := make([]AdminUserDTO, 0, len(admins)) for _, admin := range admins { - roles, _ := r.getAdminRoles(admin.ID) + roles, _ := r.getAdminRoles(ctx, admin.ID) items = append(items, AdminUserDTO{ ID: admin.ID, Username: admin.Username, @@ -50,12 +51,12 @@ func (r *Repository) List(page, pageSize int) (*PaginatedResult, error) { return &PaginatedResult{Items: items, Total: total, Page: page, PageSize: pageSize}, nil } -func (r *Repository) FindByID(id uint64) (*AdminUserDTO, error) { +func (r *Repository) FindByID(ctx context.Context, id uint64) (*AdminUserDTO, error) { var admin model.AdminUser - if err := r.db.First(&admin, id).Error; err != nil { + if err := r.db.WithContext(ctx).First(&admin, id).Error; err != nil { return nil, err } - roles, _ := r.getAdminRoles(admin.ID) + roles, _ := r.getAdminRoles(ctx, admin.ID) dto := AdminUserDTO{ ID: admin.ID, Username: admin.Username, @@ -69,7 +70,7 @@ func (r *Repository) FindByID(id uint64) (*AdminUserDTO, error) { return &dto, nil } -func (r *Repository) Create(req CreateAdminRequest) (*AdminUserDTO, error) { +func (r *Repository) Create(ctx context.Context, req CreateAdminRequest) (*AdminUserDTO, error) { hash, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost) if err != nil { return nil, err @@ -80,15 +81,16 @@ func (r *Repository) Create(req CreateAdminRequest) (*AdminUserDTO, error) { Nickname: req.Nickname, Status: "active", } - if err := r.db.Create(&admin).Error; err != nil { + if err := r.db.WithContext(ctx).Create(&admin).Error; err != nil { return nil, err } - return r.FindByID(admin.ID) + return r.FindByID(ctx, admin.ID) } -func (r *Repository) Update(id uint64, req UpdateAdminRequest) (*AdminUserDTO, error) { +func (r *Repository) Update(ctx context.Context, id uint64, req UpdateAdminRequest) (*AdminUserDTO, error) { + db := r.db.WithContext(ctx) var admin model.AdminUser - if err := r.db.First(&admin, id).Error; err != nil { + if err := db.First(&admin, id).Error; err != nil { return nil, err } if req.Nickname != "" { @@ -97,46 +99,48 @@ func (r *Repository) Update(id uint64, req UpdateAdminRequest) (*AdminUserDTO, e if req.Status != "" { admin.Status = req.Status } - if err := r.db.Save(&admin).Error; err != nil { + if err := db.Save(&admin).Error; err != nil { return nil, err } - return r.FindByID(id) + return r.FindByID(ctx, id) } -func (r *Repository) Delete(id uint64, currentAdminID uint64) error { +func (r *Repository) Delete(ctx context.Context, id uint64, currentAdminID uint64) error { if id == currentAdminID { return ErrCannotDeleteSelf } + db := r.db.WithContext(ctx) var admin model.AdminUser - if err := r.db.First(&admin, id).Error; err != nil { + if err := db.First(&admin, id).Error; err != nil { return err } // 检查是否是最后一个 super_admin - isLastSuper, err := r.isLastSuperAdmin(id) + isLastSuper, err := r.isLastSuperAdmin(ctx, id) if err != nil { return err } if isLastSuper { return ErrLastSuperAdmin } - return r.db.Transaction(func(tx *gorm.DB) error { + return db.Transaction(func(tx *gorm.DB) error { if err := tx.Where("admin_user_id = ?", id).Delete(&model.AdminUserRole{}).Error; err != nil { return err } if err := tx.Delete(&model.AdminUser{}, id).Error; err != nil { return err } - r.invalidatePermCache(id) + r.invalidatePermCache(ctx, id) return nil }) } -func (r *Repository) AssignRoles(adminID uint64, roleIDs []uint64) error { +func (r *Repository) AssignRoles(ctx context.Context, adminID uint64, roleIDs []uint64) error { + db := r.db.WithContext(ctx) var admin model.AdminUser - if err := r.db.First(&admin, adminID).Error; err != nil { + if err := db.First(&admin, adminID).Error; err != nil { return err } - err := r.db.Transaction(func(tx *gorm.DB) error { + err := db.Transaction(func(tx *gorm.DB) error { if err := tx.Where("admin_user_id = ?", adminID).Delete(&model.AdminUserRole{}).Error; err != nil { return err } @@ -151,13 +155,13 @@ func (r *Repository) AssignRoles(adminID uint64, roleIDs []uint64) error { if err != nil { return err } - r.invalidatePermCache(adminID) + r.invalidatePermCache(ctx, adminID) return nil } -func (r *Repository) ChangePassword(id uint64, oldPwd, newPwd string) error { +func (r *Repository) ChangePassword(ctx context.Context, id uint64, oldPwd, newPwd string) error { var admin model.AdminUser - if err := r.db.First(&admin, id).Error; err != nil { + if err := r.db.WithContext(ctx).First(&admin, id).Error; err != nil { return err } if err := bcrypt.CompareHashAndPassword([]byte(admin.PasswordHash), []byte(oldPwd)); err != nil { @@ -167,12 +171,12 @@ func (r *Repository) ChangePassword(id uint64, oldPwd, newPwd string) error { if err != nil { return err } - return r.db.Model(&admin).Update("password_hash", string(hash)).Error + return r.db.WithContext(ctx).Model(&admin).Update("password_hash", string(hash)).Error } -func (r *Repository) GetPermissionCodes(adminID uint64) ([]string, error) { +func (r *Repository) GetPermissionCodes(ctx context.Context, adminID uint64) ([]string, error) { var codes []string - err := r.db.Table("admin_user_roles aur"). + err := r.db.WithContext(ctx).Table("admin_user_roles aur"). Select("DISTINCT p.code"). Joins("JOIN role_permissions rp ON rp.role_id = aur.role_id"). Joins("JOIN permissions p ON p.id = rp.permission_id"). @@ -181,9 +185,9 @@ func (r *Repository) GetPermissionCodes(adminID uint64) ([]string, error) { return codes, err } -func (r *Repository) getAdminRoles(adminID uint64) ([]adminrole.RoleDTO, error) { +func (r *Repository) getAdminRoles(ctx context.Context, adminID uint64) ([]adminrole.RoleDTO, error) { var roles []model.Role - err := r.db.Table("roles"). + err := r.db.WithContext(ctx).Table("roles"). Joins("JOIN admin_user_roles aur ON aur.role_id = roles.id"). Where("aur.admin_user_id = ?", adminID). Find(&roles).Error @@ -199,23 +203,23 @@ func (r *Repository) getAdminRoles(adminID uint64) ([]adminrole.RoleDTO, error) return result, nil } -func (r *Repository) isLastSuperAdmin(adminID uint64) (bool, error) { +func (r *Repository) isLastSuperAdmin(ctx context.Context, adminID uint64) (bool, error) { var superRole model.Role - if err := r.db.Where("code = ?", "super_admin").First(&superRole).Error; err != nil { + db := r.db.WithContext(ctx) + if err := db.Where("code = ?", "super_admin").First(&superRole).Error; err != nil { return false, nil // 没有 super_admin 角色,不限制 } var count int64 - err := r.db.Model(&model.AdminUserRole{}). + err := db.Model(&model.AdminUserRole{}). Where("role_id = ? AND admin_user_id != ?", superRole.ID, adminID). Count(&count).Error return count == 0, err } -func (r *Repository) invalidatePermCache(adminID uint64) { +func (r *Repository) invalidatePermCache(ctx context.Context, adminID uint64) { if r.redis == nil { return } - ctx := context.Background() r.redis.Del(ctx, permCacheKey(adminID)) } diff --git a/backend/internal/modules/adminmgr/service.go b/backend/internal/modules/adminmgr/service.go index c2866a6..88c0760 100644 --- a/backend/internal/modules/adminmgr/service.go +++ b/backend/internal/modules/adminmgr/service.go @@ -1,6 +1,9 @@ package adminmgr -import "errors" +import ( + "context" + "errors" +) var ErrDependencyUnavailable = errors.New("dependency unavailable") @@ -12,7 +15,7 @@ func NewService(repo *Repository) *Service { return &Service{repo: repo} } -func (s *Service) List(page, pageSize int) (*PaginatedResult, error) { +func (s *Service) List(ctx context.Context, page, pageSize int) (*PaginatedResult, error) { if s.repo == nil { return nil, ErrDependencyUnavailable } @@ -22,53 +25,53 @@ func (s *Service) List(page, pageSize int) (*PaginatedResult, error) { if pageSize < 1 || pageSize > 100 { pageSize = 20 } - return s.repo.List(page, pageSize) + return s.repo.List(ctx, page, pageSize) } -func (s *Service) FindByID(id uint64) (*AdminUserDTO, error) { +func (s *Service) FindByID(ctx context.Context, id uint64) (*AdminUserDTO, error) { if s.repo == nil { return nil, ErrDependencyUnavailable } - return s.repo.FindByID(id) + return s.repo.FindByID(ctx, id) } -func (s *Service) Create(req CreateAdminRequest) (*AdminUserDTO, error) { +func (s *Service) Create(ctx context.Context, req CreateAdminRequest) (*AdminUserDTO, error) { if s.repo == nil { return nil, ErrDependencyUnavailable } if len(req.Password) < 6 { return nil, errors.New("password too short") } - return s.repo.Create(req) + return s.repo.Create(ctx, req) } -func (s *Service) Update(id uint64, req UpdateAdminRequest) (*AdminUserDTO, error) { +func (s *Service) Update(ctx context.Context, id uint64, req UpdateAdminRequest) (*AdminUserDTO, error) { if s.repo == nil { return nil, ErrDependencyUnavailable } - return s.repo.Update(id, req) + return s.repo.Update(ctx, id, req) } -func (s *Service) Delete(id uint64, currentAdminID uint64) error { +func (s *Service) Delete(ctx context.Context, id uint64, currentAdminID uint64) error { if s.repo == nil { return ErrDependencyUnavailable } - return s.repo.Delete(id, currentAdminID) + return s.repo.Delete(ctx, id, currentAdminID) } -func (s *Service) AssignRoles(adminID uint64, req AssignRolesRequest) error { +func (s *Service) AssignRoles(ctx context.Context, adminID uint64, req AssignRolesRequest) error { if s.repo == nil { return ErrDependencyUnavailable } - return s.repo.AssignRoles(adminID, req.RoleIDs) + return s.repo.AssignRoles(ctx, adminID, req.RoleIDs) } -func (s *Service) ChangePassword(id uint64, req ChangePasswordRequest) error { +func (s *Service) ChangePassword(ctx context.Context, id uint64, req ChangePasswordRequest) error { if s.repo == nil { return ErrDependencyUnavailable } if len(req.NewPassword) < 6 { return errors.New("new password too short") } - return s.repo.ChangePassword(id, req.OldPassword, req.NewPassword) + return s.repo.ChangePassword(ctx, id, req.OldPassword, req.NewPassword) } diff --git a/backend/internal/modules/adminrole/handler.go b/backend/internal/modules/adminrole/handler.go index 28aa41a..f13edd1 100644 --- a/backend/internal/modules/adminrole/handler.go +++ b/backend/internal/modules/adminrole/handler.go @@ -19,7 +19,7 @@ func NewHandler(service *Service) *Handler { } func (h *Handler) List(c *gin.Context) { - items, err := h.service.List() + items, err := h.service.List(c.Request.Context()) if err != nil { writeError(c, err) return @@ -32,7 +32,7 @@ func (h *Handler) FindByID(c *gin.Context) { if !ok { return } - item, err := h.service.FindByID(id) + item, err := h.service.FindByID(c.Request.Context(), id) if err != nil { writeError(c, err) return @@ -46,7 +46,7 @@ func (h *Handler) Create(c *gin.Context) { response.BadRequest(c, "角色编码和名称不能为空") return } - item, err := h.service.Create(req) + item, err := h.service.Create(c.Request.Context(), req) if err != nil { writeError(c, err) return @@ -64,7 +64,7 @@ func (h *Handler) Update(c *gin.Context) { response.BadRequest(c, "角色名称不能为空") return } - item, err := h.service.Update(id, req) + item, err := h.service.Update(c.Request.Context(), id, req) if err != nil { writeError(c, err) return @@ -77,7 +77,7 @@ func (h *Handler) Delete(c *gin.Context) { if !ok { return } - if err := h.service.Delete(id); err != nil { + if err := h.service.Delete(c.Request.Context(), id); err != nil { writeError(c, err) return } @@ -94,7 +94,7 @@ func (h *Handler) AssignPermissions(c *gin.Context) { response.BadRequest(c, "请求格式不正确") return } - if err := h.service.AssignPermissions(id, req); err != nil { + if err := h.service.AssignPermissions(c.Request.Context(), id, req); err != nil { writeError(c, err) return } @@ -102,7 +102,7 @@ func (h *Handler) AssignPermissions(c *gin.Context) { } func (h *Handler) ListPermissions(c *gin.Context) { - items, err := h.service.ListPermissions() + items, err := h.service.ListPermissions(c.Request.Context()) if err != nil { writeError(c, err) return diff --git a/backend/internal/modules/adminrole/repository.go b/backend/internal/modules/adminrole/repository.go index e09ccc1..93c49e7 100644 --- a/backend/internal/modules/adminrole/repository.go +++ b/backend/internal/modules/adminrole/repository.go @@ -1,6 +1,7 @@ package adminrole import ( + "context" "errors" "hfb_sys/backend/internal/model" @@ -17,18 +18,19 @@ func NewRepository(db *gorm.DB) *Repository { return &Repository{db: db} } -func (r *Repository) List() ([]RoleDTO, error) { +func (r *Repository) List(ctx context.Context) ([]RoleDTO, error) { + db := r.db.WithContext(ctx) var roles []model.Role - if err := r.db.Order("id ASC").Find(&roles).Error; err != nil { + if err := db.Order("id ASC").Find(&roles).Error; err != nil { return nil, err } result := make([]RoleDTO, 0, len(roles)) for _, role := range roles { var count int64 if role.Code == "super_admin" { - r.db.Model(&model.Permission{}).Count(&count) + db.Model(&model.Permission{}).Count(&count) } else { - r.db.Model(&model.RolePermission{}).Where("role_id = ?", role.ID).Count(&count) + db.Model(&model.RolePermission{}).Where("role_id = ?", role.ID).Count(&count) } result = append(result, RoleDTO{ ID: role.ID, @@ -43,17 +45,17 @@ func (r *Repository) List() ([]RoleDTO, error) { return result, nil } -func (r *Repository) FindByID(id uint64) (*RoleDTO, error) { +func (r *Repository) FindByID(ctx context.Context, id uint64) (*RoleDTO, error) { var role model.Role - if err := r.db.First(&role, id).Error; err != nil { + if err := r.db.WithContext(ctx).First(&role, id).Error; err != nil { return nil, err } var perms []PermissionDTO var err error if role.Code == "super_admin" { - perms, err = r.ListPermissions() + perms, err = r.ListPermissions(ctx) } else { - perms, err = r.getRolePermissions(id) + perms, err = r.getRolePermissions(ctx, id) } if err != nil { return nil, err @@ -71,40 +73,42 @@ func (r *Repository) FindByID(id uint64) (*RoleDTO, error) { return &dto, nil } -func (r *Repository) Create(req CreateRoleRequest) (*RoleDTO, error) { +func (r *Repository) Create(ctx context.Context, req CreateRoleRequest) (*RoleDTO, error) { role := model.Role{ Code: req.Code, Name: req.Name, Description: req.Description, } - if err := r.db.Create(&role).Error; err != nil { + if err := r.db.WithContext(ctx).Create(&role).Error; err != nil { return nil, err } - return r.FindByID(role.ID) + return r.FindByID(ctx, role.ID) } -func (r *Repository) Update(id uint64, req UpdateRoleRequest) (*RoleDTO, error) { +func (r *Repository) Update(ctx context.Context, id uint64, req UpdateRoleRequest) (*RoleDTO, error) { + db := r.db.WithContext(ctx) var role model.Role - if err := r.db.First(&role, id).Error; err != nil { + if err := db.First(&role, id).Error; err != nil { return nil, err } role.Name = req.Name role.Description = req.Description - if err := r.db.Save(&role).Error; err != nil { + if err := db.Save(&role).Error; err != nil { return nil, err } - return r.FindByID(id) + return r.FindByID(ctx, id) } -func (r *Repository) Delete(id uint64) error { +func (r *Repository) Delete(ctx context.Context, id uint64) error { + db := r.db.WithContext(ctx) var role model.Role - if err := r.db.First(&role, id).Error; err != nil { + if err := db.First(&role, id).Error; err != nil { return err } if role.Code == "super_admin" { return ErrProtectedRole } - return r.db.Transaction(func(tx *gorm.DB) error { + return db.Transaction(func(tx *gorm.DB) error { if err := tx.Where("role_id = ?", id).Delete(&model.RolePermission{}).Error; err != nil { return err } @@ -115,12 +119,13 @@ func (r *Repository) Delete(id uint64) error { }) } -func (r *Repository) AssignPermissions(roleID uint64, permIDs []uint64) error { +func (r *Repository) AssignPermissions(ctx context.Context, roleID uint64, permIDs []uint64) error { + db := r.db.WithContext(ctx) var role model.Role - if err := r.db.First(&role, roleID).Error; err != nil { + if err := db.First(&role, roleID).Error; err != nil { return err } - return r.db.Transaction(func(tx *gorm.DB) error { + return db.Transaction(func(tx *gorm.DB) error { if role.Code == "super_admin" { var allPermIDs []uint64 if err := tx.Model(&model.Permission{}).Pluck("id", &allPermIDs).Error; err != nil { @@ -141,9 +146,9 @@ func (r *Repository) AssignPermissions(roleID uint64, permIDs []uint64) error { }) } -func (r *Repository) ListPermissions() ([]PermissionDTO, error) { +func (r *Repository) ListPermissions(ctx context.Context) ([]PermissionDTO, error) { var perms []model.Permission - if err := r.db.Order("resource ASC, action ASC").Find(&perms).Error; err != nil { + if err := r.db.WithContext(ctx).Order("resource ASC, action ASC").Find(&perms).Error; err != nil { return nil, err } result := make([]PermissionDTO, 0, len(perms)) @@ -155,9 +160,10 @@ func (r *Repository) ListPermissions() ([]PermissionDTO, error) { return result, nil } -func (r *Repository) getRolePermissions(roleID uint64) ([]PermissionDTO, error) { +func (r *Repository) getRolePermissions(ctx context.Context, roleID uint64) ([]PermissionDTO, error) { + db := r.db.WithContext(ctx) var rps []model.RolePermission - if err := r.db.Where("role_id = ?", roleID).Find(&rps).Error; err != nil { + if err := db.Where("role_id = ?", roleID).Find(&rps).Error; err != nil { return nil, err } if len(rps) == 0 { @@ -168,7 +174,7 @@ func (r *Repository) getRolePermissions(roleID uint64) ([]PermissionDTO, error) ids = append(ids, rp.PermissionID) } var perms []model.Permission - if err := r.db.Where("id IN ?", ids).Find(&perms).Error; err != nil { + if err := db.Where("id IN ?", ids).Find(&perms).Error; err != nil { return nil, err } result := make([]PermissionDTO, 0, len(perms)) diff --git a/backend/internal/modules/adminrole/service.go b/backend/internal/modules/adminrole/service.go index c831136..ce82440 100644 --- a/backend/internal/modules/adminrole/service.go +++ b/backend/internal/modules/adminrole/service.go @@ -1,6 +1,9 @@ package adminrole -import "errors" +import ( + "context" + "errors" +) var ErrDependencyUnavailable = errors.New("dependency unavailable") @@ -12,54 +15,54 @@ func NewService(repo *Repository) *Service { return &Service{repo: repo} } -func (s *Service) List() ([]RoleDTO, error) { +func (s *Service) List(ctx context.Context) ([]RoleDTO, error) { if s.repo == nil { return nil, ErrDependencyUnavailable } - return s.repo.List() + return s.repo.List(ctx) } -func (s *Service) FindByID(id uint64) (*RoleDTO, error) { +func (s *Service) FindByID(ctx context.Context, id uint64) (*RoleDTO, error) { if s.repo == nil { return nil, ErrDependencyUnavailable } - return s.repo.FindByID(id) + return s.repo.FindByID(ctx, id) } -func (s *Service) Create(req CreateRoleRequest) (*RoleDTO, error) { +func (s *Service) Create(ctx context.Context, req CreateRoleRequest) (*RoleDTO, error) { if s.repo == nil { return nil, ErrDependencyUnavailable } if req.Code == "" || req.Name == "" { return nil, errors.New("code and name are required") } - return s.repo.Create(req) + return s.repo.Create(ctx, req) } -func (s *Service) Update(id uint64, req UpdateRoleRequest) (*RoleDTO, error) { +func (s *Service) Update(ctx context.Context, id uint64, req UpdateRoleRequest) (*RoleDTO, error) { if s.repo == nil { return nil, ErrDependencyUnavailable } - return s.repo.Update(id, req) + return s.repo.Update(ctx, id, req) } -func (s *Service) Delete(id uint64) error { +func (s *Service) Delete(ctx context.Context, id uint64) error { if s.repo == nil { return ErrDependencyUnavailable } - return s.repo.Delete(id) + return s.repo.Delete(ctx, id) } -func (s *Service) AssignPermissions(roleID uint64, req AssignPermissionsRequest) error { +func (s *Service) AssignPermissions(ctx context.Context, roleID uint64, req AssignPermissionsRequest) error { if s.repo == nil { return ErrDependencyUnavailable } - return s.repo.AssignPermissions(roleID, req.PermissionIDs) + return s.repo.AssignPermissions(ctx, roleID, req.PermissionIDs) } -func (s *Service) ListPermissions() ([]PermissionDTO, error) { +func (s *Service) ListPermissions(ctx context.Context) ([]PermissionDTO, error) { if s.repo == nil { return nil, ErrDependencyUnavailable } - return s.repo.ListPermissions() + return s.repo.ListPermissions(ctx) } diff --git a/backend/internal/modules/announcement/handler.go b/backend/internal/modules/announcement/handler.go index 1320d36..6376a50 100644 --- a/backend/internal/modules/announcement/handler.go +++ b/backend/internal/modules/announcement/handler.go @@ -27,7 +27,7 @@ func (h *Handler) List(c *gin.Context) { } query.Page, query.PageSize = parsePagination(c) - result, err := h.service.List(query) + result, err := h.service.List(c.Request.Context(), query) if err != nil { response.InternalServerError(c, "获取公告列表失败") return @@ -44,7 +44,7 @@ func (h *Handler) GetByID(c *gin.Context) { return } - announcement, err := h.service.GetByID(id) + announcement, err := h.service.GetByID(c.Request.Context(), id) if err != nil { if errors.Is(err, ErrNotFound) { response.NotFound(c, "公告不存在") @@ -65,7 +65,7 @@ func (h *Handler) AdminList(c *gin.Context) { } query.Page, query.PageSize = parsePagination(c) - result, err := h.service.AdminList(query) + result, err := h.service.AdminList(c.Request.Context(), query) if err != nil { response.InternalServerError(c, "获取公告列表失败") return @@ -82,7 +82,7 @@ func (h *Handler) AdminGetByID(c *gin.Context) { return } - announcement, err := h.service.AdminGetByID(id) + announcement, err := h.service.AdminGetByID(c.Request.Context(), id) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { response.NotFound(c, "公告不存在") @@ -109,7 +109,7 @@ func (h *Handler) Create(c *gin.Context) { return } - announcement, err := h.service.Create(req, adminID) + announcement, err := h.service.Create(c.Request.Context(), req, adminID) if err != nil { response.InternalServerError(c, "创建公告失败") return @@ -132,7 +132,7 @@ func (h *Handler) Update(c *gin.Context) { return } - announcement, err := h.service.Update(id, req) + announcement, err := h.service.Update(c.Request.Context(), id, req) if err != nil { response.InternalServerError(c, "更新公告失败") return @@ -149,7 +149,7 @@ func (h *Handler) Publish(c *gin.Context) { return } - if err := h.service.Publish(id); err != nil { + if err := h.service.Publish(c.Request.Context(), id); err != nil { response.InternalServerError(c, "发布公告失败") return } @@ -165,7 +165,7 @@ func (h *Handler) Archive(c *gin.Context) { return } - if err := h.service.Archive(id); err != nil { + if err := h.service.Archive(c.Request.Context(), id); err != nil { response.InternalServerError(c, "归档公告失败") return } @@ -181,7 +181,7 @@ func (h *Handler) Delete(c *gin.Context) { return } - if err := h.service.Delete(id); err != nil { + if err := h.service.Delete(c.Request.Context(), id); err != nil { response.InternalServerError(c, "删除公告失败") return } diff --git a/backend/internal/modules/announcement/repository.go b/backend/internal/modules/announcement/repository.go index 50e7eab..ee0fa41 100644 --- a/backend/internal/modules/announcement/repository.go +++ b/backend/internal/modules/announcement/repository.go @@ -1,6 +1,7 @@ package announcement import ( + "context" "time" "hfb_sys/backend/internal/model" @@ -17,9 +18,9 @@ func NewRepository(db *gorm.DB) *Repository { } // List 获取公告列表(前台用户) -func (r *Repository) List(query AnnouncementListQuery) (*PaginatedResult, error) { +func (r *Repository) List(ctx context.Context, query AnnouncementListQuery) (*PaginatedResult, error) { var total int64 - tx := r.db.Model(&model.Announcement{}).Where("status = ?", "published") + tx := r.db.WithContext(ctx).Model(&model.Announcement{}).Where("status = ?", "published") if query.Category != "" { tx = tx.Where("category = ?", query.Category) @@ -52,23 +53,24 @@ func (r *Repository) List(query AnnouncementListQuery) (*PaginatedResult, error) } // GetByID 获取公告详情 -func (r *Repository) GetByID(id uint64) (*AnnouncementDTO, error) { +func (r *Repository) GetByID(ctx context.Context, id uint64) (*AnnouncementDTO, error) { var announcement model.Announcement - if err := r.db.Where("id = ? AND status = ?", id, "published").First(&announcement).Error; err != nil { + db := r.db.WithContext(ctx) + if err := db.Where("id = ? AND status = ?", id, "published").First(&announcement).Error; err != nil { return nil, err } // 增加查看次数 - r.db.Model(&model.Announcement{}).Where("id = ?", id).UpdateColumn("view_count", gorm.Expr("view_count + ?", 1)) + db.Model(&model.Announcement{}).Where("id = ?", id).UpdateColumn("view_count", gorm.Expr("view_count + ?", 1)) dto := toAnnouncementDTO(announcement) return &dto, nil } // AdminList 管理员获取公告列表 -func (r *Repository) AdminList(query AnnouncementListQuery) (*PaginatedResult, error) { +func (r *Repository) AdminList(ctx context.Context, query AnnouncementListQuery) (*PaginatedResult, error) { var total int64 - tx := r.db.Model(&model.Announcement{}) + tx := r.db.WithContext(ctx).Model(&model.Announcement{}) if query.Status != "" { tx = tx.Where("status = ?", query.Status) @@ -104,9 +106,9 @@ func (r *Repository) AdminList(query AnnouncementListQuery) (*PaginatedResult, e } // AdminGetByID 管理员获取公告详情 -func (r *Repository) AdminGetByID(id uint64) (*AnnouncementDTO, error) { +func (r *Repository) AdminGetByID(ctx context.Context, id uint64) (*AnnouncementDTO, error) { var announcement model.Announcement - if err := r.db.Where("id = ?", id).First(&announcement).Error; err != nil { + if err := r.db.WithContext(ctx).Where("id = ?", id).First(&announcement).Error; err != nil { return nil, err } @@ -115,7 +117,7 @@ func (r *Repository) AdminGetByID(id uint64) (*AnnouncementDTO, error) { } // Create 创建公告 -func (r *Repository) Create(req CreateAnnouncementRequest, createdBy uint64) (*AnnouncementDTO, error) { +func (r *Repository) Create(ctx context.Context, req CreateAnnouncementRequest, createdBy uint64) (*AnnouncementDTO, error) { announcement := model.Announcement{ Title: req.Title, Content: req.Content, @@ -127,7 +129,7 @@ func (r *Repository) Create(req CreateAnnouncementRequest, createdBy uint64) (*A CreatedBy: &createdBy, } - if err := r.db.Create(&announcement).Error; err != nil { + if err := r.db.WithContext(ctx).Create(&announcement).Error; err != nil { return nil, err } @@ -136,7 +138,7 @@ func (r *Repository) Create(req CreateAnnouncementRequest, createdBy uint64) (*A } // Update 更新公告 -func (r *Repository) Update(id uint64, req UpdateAnnouncementRequest) (*AnnouncementDTO, error) { +func (r *Repository) Update(ctx context.Context, id uint64, req UpdateAnnouncementRequest) (*AnnouncementDTO, error) { updates := make(map[string]interface{}) if req.Title != "" { @@ -152,17 +154,17 @@ func (r *Repository) Update(id uint64, req UpdateAnnouncementRequest) (*Announce updates["is_pinned"] = req.IsPinned updates["is_important"] = req.IsImportant - if err := r.db.Model(&model.Announcement{}).Where("id = ?", id).Updates(updates).Error; err != nil { + if err := r.db.WithContext(ctx).Model(&model.Announcement{}).Where("id = ?", id).Updates(updates).Error; err != nil { return nil, err } - return r.AdminGetByID(id) + return r.AdminGetByID(ctx, id) } // Publish 发布公告 -func (r *Repository) Publish(id uint64) error { +func (r *Repository) Publish(ctx context.Context, id uint64) error { now := time.Now() - return r.db.Model(&model.Announcement{}). + return r.db.WithContext(ctx).Model(&model.Announcement{}). Where("id = ?", id). Updates(map[string]interface{}{ "status": "published", @@ -171,15 +173,15 @@ func (r *Repository) Publish(id uint64) error { } // Archive 归档公告 -func (r *Repository) Archive(id uint64) error { - return r.db.Model(&model.Announcement{}). +func (r *Repository) Archive(ctx context.Context, id uint64) error { + return r.db.WithContext(ctx).Model(&model.Announcement{}). Where("id = ?", id). Update("status", "archived").Error } // Delete 删除公告 -func (r *Repository) Delete(id uint64) error { - return r.db.Where("id = ?", id).Delete(&model.Announcement{}).Error +func (r *Repository) Delete(ctx context.Context, id uint64) error { + return r.db.WithContext(ctx).Where("id = ?", id).Delete(&model.Announcement{}).Error } func toAnnouncementDTO(a model.Announcement) AnnouncementDTO { diff --git a/backend/internal/modules/announcement/service.go b/backend/internal/modules/announcement/service.go index 2df557d..921748d 100644 --- a/backend/internal/modules/announcement/service.go +++ b/backend/internal/modules/announcement/service.go @@ -1,6 +1,7 @@ package announcement import ( + "context" "errors" "gorm.io/gorm" @@ -15,13 +16,13 @@ func NewService(repo *Repository) *Service { } var ( - ErrNotFound = errors.New("公告不存在") - ErrInvalidRequest = errors.New("请求参数不正确") - ErrUnauthorized = errors.New("未授权") + ErrNotFound = errors.New("公告不存在") + ErrInvalidRequest = errors.New("请求参数不正确") + ErrUnauthorized = errors.New("未授权") ) // List 获取公告列表(前台用户) -func (s *Service) List(query AnnouncementListQuery) (*PaginatedResult, error) { +func (s *Service) List(ctx context.Context, query AnnouncementListQuery) (*PaginatedResult, error) { if query.Page < 1 { query.Page = 1 } @@ -32,12 +33,12 @@ func (s *Service) List(query AnnouncementListQuery) (*PaginatedResult, error) { query.PageSize = 100 } - return s.repo.List(query) + return s.repo.List(ctx, query) } // GetByID 获取公告详情 -func (s *Service) GetByID(id uint64) (*AnnouncementDTO, error) { - dto, err := s.repo.GetByID(id) +func (s *Service) GetByID(ctx context.Context, id uint64) (*AnnouncementDTO, error) { + dto, err := s.repo.GetByID(ctx, id) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, ErrNotFound @@ -48,7 +49,7 @@ func (s *Service) GetByID(id uint64) (*AnnouncementDTO, error) { } // AdminList 管理员获取公告列表 -func (s *Service) AdminList(query AnnouncementListQuery) (*PaginatedResult, error) { +func (s *Service) AdminList(ctx context.Context, query AnnouncementListQuery) (*PaginatedResult, error) { if query.Page < 1 { query.Page = 1 } @@ -59,12 +60,12 @@ func (s *Service) AdminList(query AnnouncementListQuery) (*PaginatedResult, erro query.PageSize = 100 } - return s.repo.AdminList(query) + return s.repo.AdminList(ctx, query) } // AdminGetByID 管理员获取公告详情 -func (s *Service) AdminGetByID(id uint64) (*AnnouncementDTO, error) { - dto, err := s.repo.AdminGetByID(id) +func (s *Service) AdminGetByID(ctx context.Context, id uint64) (*AnnouncementDTO, error) { + dto, err := s.repo.AdminGetByID(ctx, id) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, ErrNotFound @@ -75,26 +76,26 @@ func (s *Service) AdminGetByID(id uint64) (*AnnouncementDTO, error) { } // Create 创建公告 -func (s *Service) Create(req CreateAnnouncementRequest, createdBy uint64) (*AnnouncementDTO, error) { - return s.repo.Create(req, createdBy) +func (s *Service) Create(ctx context.Context, req CreateAnnouncementRequest, createdBy uint64) (*AnnouncementDTO, error) { + return s.repo.Create(ctx, req, createdBy) } // Update 更新公告 -func (s *Service) Update(id uint64, req UpdateAnnouncementRequest) (*AnnouncementDTO, error) { - return s.repo.Update(id, req) +func (s *Service) Update(ctx context.Context, id uint64, req UpdateAnnouncementRequest) (*AnnouncementDTO, error) { + return s.repo.Update(ctx, id, req) } // Publish 发布公告 -func (s *Service) Publish(id uint64) error { - return s.repo.Publish(id) +func (s *Service) Publish(ctx context.Context, id uint64) error { + return s.repo.Publish(ctx, id) } // Archive 归档公告 -func (s *Service) Archive(id uint64) error { - return s.repo.Archive(id) +func (s *Service) Archive(ctx context.Context, id uint64) error { + return s.repo.Archive(ctx, id) } // Delete 删除公告 -func (s *Service) Delete(id uint64) error { - return s.repo.Delete(id) +func (s *Service) Delete(ctx context.Context, id uint64) error { + return s.repo.Delete(ctx, id) } diff --git a/backend/internal/modules/paymentaccount/handler.go b/backend/internal/modules/paymentaccount/handler.go index 202bbac..e6ed4c9 100644 --- a/backend/internal/modules/paymentaccount/handler.go +++ b/backend/internal/modules/paymentaccount/handler.go @@ -25,7 +25,7 @@ func (h *Handler) List(c *gin.Context) { page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20")) - result, err := h.service.List(userID, page, pageSize) + result, err := h.service.List(c.Request.Context(), userID, page, pageSize) if err != nil { writeError(c, err) return @@ -46,7 +46,7 @@ func (h *Handler) FindByID(c *gin.Context) { return } - account, err := h.service.FindByID(userID, id) + account, err := h.service.FindByID(c.Request.Context(), userID, id) if err != nil { writeError(c, err) return @@ -68,7 +68,7 @@ func (h *Handler) Create(c *gin.Context) { return } - account, err := h.service.Create(userID, req) + account, err := h.service.Create(c.Request.Context(), userID, req) if err != nil { writeError(c, err) return @@ -95,7 +95,7 @@ func (h *Handler) Update(c *gin.Context) { return } - account, err := h.service.Update(userID, id, req) + account, err := h.service.Update(c.Request.Context(), userID, id, req) if err != nil { writeError(c, err) return @@ -116,7 +116,7 @@ func (h *Handler) Delete(c *gin.Context) { return } - if err := h.service.Delete(userID, id); err != nil { + if err := h.service.Delete(c.Request.Context(), userID, id); err != nil { writeError(c, err) return } @@ -136,7 +136,7 @@ func (h *Handler) SetDefault(c *gin.Context) { return } - if err := h.service.SetDefault(userID, id); err != nil { + if err := h.service.SetDefault(c.Request.Context(), userID, id); err != nil { writeError(c, err) return } diff --git a/backend/internal/modules/paymentaccount/repository.go b/backend/internal/modules/paymentaccount/repository.go index 67b7480..3e011df 100644 --- a/backend/internal/modules/paymentaccount/repository.go +++ b/backend/internal/modules/paymentaccount/repository.go @@ -1,6 +1,7 @@ package paymentaccount import ( + "context" "encoding/json" "errors" "strings" @@ -22,9 +23,10 @@ func NewRepository(db *gorm.DB) *Repository { return &Repository{db: db} } -func (r *Repository) List(userID uint64, page, pageSize int) (*PaginatedResult, error) { +func (r *Repository) List(ctx context.Context, userID uint64, page, pageSize int) (*PaginatedResult, error) { + db := r.db.WithContext(ctx) var total int64 - if err := r.db.Model(&model.UserPaymentAccount{}). + if err := db.Model(&model.UserPaymentAccount{}). Where("user_id = ? AND status = ?", userID, "active"). Count(&total).Error; err != nil { return nil, err @@ -32,7 +34,7 @@ func (r *Repository) List(userID uint64, page, pageSize int) (*PaginatedResult, offset := (page - 1) * pageSize var accounts []model.UserPaymentAccount - if err := r.db.Where("user_id = ? AND status = ?", userID, "active"). + if err := db.Where("user_id = ? AND status = ?", userID, "active"). Order("is_default DESC, created_at DESC"). Offset(offset).Limit(pageSize). Find(&accounts).Error; err != nil { @@ -56,9 +58,9 @@ func (r *Repository) List(userID uint64, page, pageSize int) (*PaginatedResult, }, nil } -func (r *Repository) FindByID(userID, id uint64) (*PaymentAccountDTO, error) { +func (r *Repository) FindByID(ctx context.Context, userID, id uint64) (*PaymentAccountDTO, error) { var account model.UserPaymentAccount - if err := r.db.Where("id = ? AND user_id = ? AND status = ?", id, userID, "active"). + if err := r.db.WithContext(ctx).Where("id = ? AND user_id = ? AND status = ?", id, userID, "active"). First(&account).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, ErrAccountNotFound @@ -68,7 +70,8 @@ func (r *Repository) FindByID(userID, id uint64) (*PaymentAccountDTO, error) { return r.toDTO(account) } -func (r *Repository) Create(userID uint64, req CreatePaymentAccountRequest) (*PaymentAccountDTO, error) { +func (r *Repository) Create(ctx context.Context, userID uint64, req CreatePaymentAccountRequest) (*PaymentAccountDTO, error) { + db := r.db.WithContext(ctx) // 加密账号 encryptedNo, err := crypto.Encrypt(req.AccountNo) if err != nil { @@ -84,7 +87,7 @@ func (r *Repository) Create(userID uint64, req CreatePaymentAccountRequest) (*Pa // 如果是第一个账号,自动设为默认 isDefault := false var count int64 - r.db.Model(&model.UserPaymentAccount{}).Where("user_id = ? AND status = ?", userID, "active").Count(&count) + db.Model(&model.UserPaymentAccount{}).Where("user_id = ? AND status = ?", userID, "active").Count(&count) if count == 0 { isDefault = true } @@ -101,16 +104,17 @@ func (r *Repository) Create(userID uint64, req CreatePaymentAccountRequest) (*Pa Status: "active", } - if err := r.db.Create(&account).Error; err != nil { + if err := db.Create(&account).Error; err != nil { return nil, err } - return r.FindByID(userID, account.ID) + return r.FindByID(ctx, userID, account.ID) } -func (r *Repository) Update(userID, id uint64, req UpdatePaymentAccountRequest) (*PaymentAccountDTO, error) { +func (r *Repository) Update(ctx context.Context, userID, id uint64, req UpdatePaymentAccountRequest) (*PaymentAccountDTO, error) { + db := r.db.WithContext(ctx) var account model.UserPaymentAccount - if err := r.db.Where("id = ? AND user_id = ? AND status = ?", id, userID, "active"). + if err := db.Where("id = ? AND user_id = ? AND status = ?", id, userID, "active"). First(&account).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, ErrAccountNotFound @@ -131,24 +135,25 @@ func (r *Repository) Update(userID, id uint64, req UpdatePaymentAccountRequest) if req.IsDefault != nil && *req.IsDefault { // 先取消其他默认账号 - r.db.Model(&model.UserPaymentAccount{}). + db.Model(&model.UserPaymentAccount{}). Where("user_id = ? AND id != ?", userID, id). Update("is_default", false) updates["is_default"] = true } if len(updates) > 0 { - if err := r.db.Model(&account).Updates(updates).Error; err != nil { + if err := db.Model(&account).Updates(updates).Error; err != nil { return nil, err } } - return r.FindByID(userID, id) + return r.FindByID(ctx, userID, id) } -func (r *Repository) Delete(userID, id uint64) error { +func (r *Repository) Delete(ctx context.Context, userID, id uint64) error { + db := r.db.WithContext(ctx) var account model.UserPaymentAccount - if err := r.db.Where("id = ? AND user_id = ? AND status = ?", id, userID, "active"). + if err := db.Where("id = ? AND user_id = ? AND status = ?", id, userID, "active"). First(&account).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return ErrAccountNotFound @@ -157,13 +162,13 @@ func (r *Repository) Delete(userID, id uint64) error { } // 软删除 - return r.db.Model(&account).Update("status", "disabled").Error + return db.Model(&account).Update("status", "disabled").Error } -func (r *Repository) SetDefault(userID, id uint64) error { +func (r *Repository) SetDefault(ctx context.Context, userID, id uint64) error { // 验证账号存在 var account model.UserPaymentAccount - if err := r.db.Where("id = ? AND user_id = ? AND status = ?", id, userID, "active"). + if err := r.db.WithContext(ctx).Where("id = ? AND user_id = ? AND status = ?", id, userID, "active"). First(&account).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return ErrAccountNotFound @@ -171,7 +176,7 @@ func (r *Repository) SetDefault(userID, id uint64) error { return err } - return r.db.Transaction(func(tx *gorm.DB) error { + return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { // 取消其他默认账号 if err := tx.Model(&model.UserPaymentAccount{}). Where("user_id = ? AND id != ?", userID, id). @@ -183,17 +188,18 @@ func (r *Repository) SetDefault(userID, id uint64) error { }) } -func (r *Repository) CountByUser(userID uint64) (int64, error) { +func (r *Repository) CountByUser(ctx context.Context, userID uint64) (int64, error) { var count int64 - err := r.db.Model(&model.UserPaymentAccount{}). + err := r.db.WithContext(ctx).Model(&model.UserPaymentAccount{}). Where("user_id = ? AND status = ?", userID, "active"). Count(&count).Error return count, err } -func (r *Repository) ValidateRealname(userID uint64, accountName string) error { +func (r *Repository) ValidateRealname(ctx context.Context, userID uint64, accountName string) error { var user model.User - if err := r.db.First(&user, userID).Error; err != nil { + db := r.db.WithContext(ctx) + if err := db.First(&user, userID).Error; err != nil { return err } @@ -203,7 +209,7 @@ func (r *Repository) ValidateRealname(userID uint64, accountName string) error { // 获取实名信息 var realname model.UserRealname - if err := r.db.Where("user_id = ? AND status = ?", userID, "verified"). + if err := db.Where("user_id = ? AND status = ?", userID, "verified"). First(&realname).Error; err != nil { return ErrRealnameRequired } @@ -303,9 +309,9 @@ func maskAccountNo(accountNo, accountType string) string { } // 获取解密后的账号(仅供内部使用,如提现申请时) -func (r *Repository) GetDecryptedAccountNo(userID, id uint64) (string, error) { +func (r *Repository) GetDecryptedAccountNo(ctx context.Context, userID, id uint64) (string, error) { var account model.UserPaymentAccount - if err := r.db.Where("id = ? AND user_id = ?", id, userID).First(&account).Error; err != nil { + if err := r.db.WithContext(ctx).Where("id = ? AND user_id = ?", id, userID).First(&account).Error; err != nil { return "", err } return crypto.Decrypt(account.AccountNo) diff --git a/backend/internal/modules/paymentaccount/service.go b/backend/internal/modules/paymentaccount/service.go index e96e9cb..2dc2b0c 100644 --- a/backend/internal/modules/paymentaccount/service.go +++ b/backend/internal/modules/paymentaccount/service.go @@ -1,6 +1,9 @@ package paymentaccount -import "errors" +import ( + "context" + "errors" +) var ( ErrDependencyUnavailable = errors.New("dependency unavailable") @@ -19,7 +22,7 @@ func NewService(repo *Repository) *Service { return &Service{repo: repo} } -func (s *Service) List(userID uint64, page, pageSize int) (*PaginatedResult, error) { +func (s *Service) List(ctx context.Context, userID uint64, page, pageSize int) (*PaginatedResult, error) { if s.repo == nil { return nil, ErrDependencyUnavailable } @@ -29,52 +32,52 @@ func (s *Service) List(userID uint64, page, pageSize int) (*PaginatedResult, err if pageSize < 1 || pageSize > 100 { pageSize = 20 } - return s.repo.List(userID, page, pageSize) + return s.repo.List(ctx, userID, page, pageSize) } -func (s *Service) FindByID(userID, id uint64) (*PaymentAccountDTO, error) { +func (s *Service) FindByID(ctx context.Context, userID, id uint64) (*PaymentAccountDTO, error) { if s.repo == nil { return nil, ErrDependencyUnavailable } - return s.repo.FindByID(userID, id) + return s.repo.FindByID(ctx, userID, id) } -func (s *Service) Create(userID uint64, req CreatePaymentAccountRequest) (*PaymentAccountDTO, error) { +func (s *Service) Create(ctx context.Context, userID uint64, req CreatePaymentAccountRequest) (*PaymentAccountDTO, error) { if s.repo == nil { return nil, ErrDependencyUnavailable } // 验证实名状态 - if err := s.repo.ValidateRealname(userID, req.AccountName); err != nil { + if err := s.repo.ValidateRealname(ctx, userID, req.AccountName); err != nil { return nil, err } // 检查账号数量限制(最多5个) - count, err := s.repo.CountByUser(userID) + count, err := s.repo.CountByUser(ctx, userID) if err != nil { return nil, err } if count >= 5 { return nil, ErrAccountLimit } - return s.repo.Create(userID, req) + return s.repo.Create(ctx, userID, req) } -func (s *Service) Update(userID, id uint64, req UpdatePaymentAccountRequest) (*PaymentAccountDTO, error) { +func (s *Service) Update(ctx context.Context, userID, id uint64, req UpdatePaymentAccountRequest) (*PaymentAccountDTO, error) { if s.repo == nil { return nil, ErrDependencyUnavailable } - return s.repo.Update(userID, id, req) + return s.repo.Update(ctx, userID, id, req) } -func (s *Service) Delete(userID, id uint64) error { +func (s *Service) Delete(ctx context.Context, userID, id uint64) error { if s.repo == nil { return ErrDependencyUnavailable } - return s.repo.Delete(userID, id) + return s.repo.Delete(ctx, userID, id) } -func (s *Service) SetDefault(userID, id uint64) error { +func (s *Service) SetDefault(ctx context.Context, userID, id uint64) error { if s.repo == nil { return ErrDependencyUnavailable } - return s.repo.SetDefault(userID, id) + return s.repo.SetDefault(ctx, userID, id) } diff --git a/backend/internal/modules/withdrawal/handler.go b/backend/internal/modules/withdrawal/handler.go index bc371fb..67f3e75 100644 --- a/backend/internal/modules/withdrawal/handler.go +++ b/backend/internal/modules/withdrawal/handler.go @@ -30,7 +30,7 @@ func (h *Handler) Create(c *gin.Context) { return } - withdrawal, err := h.service.Create(userID, req) + withdrawal, err := h.service.Create(c.Request.Context(), userID, req) if err != nil { writeError(c, err) return @@ -49,7 +49,7 @@ func (h *Handler) List(c *gin.Context) { page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20")) - result, err := h.service.List(userID, page, pageSize) + result, err := h.service.List(c.Request.Context(), userID, page, pageSize) if err != nil { writeError(c, err) return @@ -70,7 +70,7 @@ func (h *Handler) FindByID(c *gin.Context) { return } - withdrawal, err := h.service.FindByID(userID, id) + withdrawal, err := h.service.FindByID(c.Request.Context(), userID, id) if err != nil { writeError(c, err) return @@ -91,7 +91,7 @@ func (h *Handler) Cancel(c *gin.Context) { return } - if err := h.service.Cancel(userID, id); err != nil { + if err := h.service.Cancel(c.Request.Context(), userID, id); err != nil { writeError(c, err) return } @@ -108,7 +108,7 @@ func (h *Handler) AdminList(c *gin.Context) { query.Size = 20 } - result, err := h.service.AdminList(query) + result, err := h.service.AdminList(c.Request.Context(), query) if err != nil { writeError(c, err) return @@ -123,7 +123,7 @@ func (h *Handler) AdminFindByID(c *gin.Context) { return } - withdrawal, err := h.service.AdminFindByID(id) + withdrawal, err := h.service.AdminFindByID(c.Request.Context(), id) if err != nil { writeError(c, err) return @@ -150,7 +150,7 @@ func (h *Handler) Review(c *gin.Context) { return } - withdrawal, err := h.service.Review(adminID, id, req) + withdrawal, err := h.service.Review(c.Request.Context(), adminID, id, req) if err != nil { writeError(c, err) return @@ -177,7 +177,7 @@ func (h *Handler) ConfirmPayment(c *gin.Context) { return } - withdrawal, err := h.service.ConfirmPayment(adminID, id, req) + withdrawal, err := h.service.ConfirmPayment(c.Request.Context(), adminID, id, req) if err != nil { writeError(c, err) return diff --git a/backend/internal/modules/withdrawal/repository.go b/backend/internal/modules/withdrawal/repository.go index 49b491f..91e4250 100644 --- a/backend/internal/modules/withdrawal/repository.go +++ b/backend/internal/modules/withdrawal/repository.go @@ -1,6 +1,7 @@ package withdrawal import ( + "context" "crypto/rand" "encoding/hex" "encoding/json" @@ -28,10 +29,11 @@ func NewRepository(db *gorm.DB, walletRepo *wallet.Repository) *Repository { } // 用户创建提现申请 -func (r *Repository) Create(userID uint64, req CreateWithdrawalRequest) (*WithdrawalDTO, error) { +func (r *Repository) Create(ctx context.Context, userID uint64, req CreateWithdrawalRequest) (*WithdrawalDTO, error) { + db := r.db.WithContext(ctx) // 验证收款账号 var paymentAccount model.UserPaymentAccount - if err := r.db.Where("id = ? AND user_id = ? AND status = ?", req.PaymentAccountID, userID, "active"). + if err := db.Where("id = ? AND user_id = ? AND status = ?", req.PaymentAccountID, userID, "active"). First(&paymentAccount).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, errors.New("payment account not found") @@ -71,7 +73,7 @@ func (r *Repository) Create(userID uint64, req CreateWithdrawalRequest) (*Withdr } // 事务处理 - err = r.db.Transaction(func(tx *gorm.DB) error { + err = db.Transaction(func(tx *gorm.DB) error { // 创建提现申请 if err := tx.Create(&withdrawal).Error; err != nil { return err @@ -105,13 +107,14 @@ func (r *Repository) Create(userID uint64, req CreateWithdrawalRequest) (*Withdr return nil, err } - return r.FindByID(userID, withdrawal.ID) + return r.FindByID(ctx, userID, withdrawal.ID) } // 用户查询提现列表 -func (r *Repository) List(userID uint64, page, pageSize int) (*PaginatedResult, error) { +func (r *Repository) List(ctx context.Context, userID uint64, page, pageSize int) (*PaginatedResult, error) { + db := r.db.WithContext(ctx) var total int64 - if err := r.db.Model(&model.WithdrawalRequest{}). + if err := db.Model(&model.WithdrawalRequest{}). Where("user_id = ?", userID). Count(&total).Error; err != nil { return nil, err @@ -119,7 +122,7 @@ func (r *Repository) List(userID uint64, page, pageSize int) (*PaginatedResult, offset := (page - 1) * pageSize var withdrawals []model.WithdrawalRequest - if err := r.db.Where("user_id = ?", userID). + if err := db.Where("user_id = ?", userID). Order("created_at DESC"). Offset(offset).Limit(pageSize). Find(&withdrawals).Error; err != nil { @@ -140,9 +143,9 @@ func (r *Repository) List(userID uint64, page, pageSize int) (*PaginatedResult, } // 用户查询提现详情 -func (r *Repository) FindByID(userID, id uint64) (*WithdrawalDTO, error) { +func (r *Repository) FindByID(ctx context.Context, userID, id uint64) (*WithdrawalDTO, error) { var withdrawal model.WithdrawalRequest - if err := r.db.Where("id = ? AND user_id = ?", id, userID). + if err := r.db.WithContext(ctx).Where("id = ? AND user_id = ?", id, userID). First(&withdrawal).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, ErrWithdrawalNotFound @@ -154,9 +157,10 @@ func (r *Repository) FindByID(userID, id uint64) (*WithdrawalDTO, error) { } // 用户取消提现 -func (r *Repository) Cancel(userID, id uint64) error { +func (r *Repository) Cancel(ctx context.Context, userID, id uint64) error { + db := r.db.WithContext(ctx) var withdrawal model.WithdrawalRequest - if err := r.db.Where("id = ? AND user_id = ?", id, userID). + if err := db.Where("id = ? AND user_id = ?", id, userID). First(&withdrawal).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return ErrWithdrawalNotFound @@ -169,7 +173,7 @@ func (r *Repository) Cancel(userID, id uint64) error { return ErrWithdrawalLocked } - return r.db.Transaction(func(tx *gorm.DB) error { + return db.Transaction(func(tx *gorm.DB) error { // 更新状态 if err := tx.Model(&withdrawal).Update("status", "cancelled").Error; err != nil { return err @@ -201,8 +205,8 @@ func (r *Repository) Cancel(userID, id uint64) error { } // 管理员查询提现列表 -func (r *Repository) AdminList(query AdminListQuery) (*AdminPaginatedResult, error) { - db := r.db.Model(&model.WithdrawalRequest{}) +func (r *Repository) AdminList(ctx context.Context, query AdminListQuery) (*AdminPaginatedResult, error) { + db := r.db.WithContext(ctx).Model(&model.WithdrawalRequest{}) if query.Status != "" { db = db.Where("status = ?", query.Status) @@ -226,7 +230,7 @@ func (r *Repository) AdminList(query AdminListQuery) (*AdminPaginatedResult, err items := make([]WithdrawalDetailDTO, 0, len(withdrawals)) for _, w := range withdrawals { - dto, err := r.toDetailDTO(w) + dto, err := r.toDetailDTO(ctx, w) if err != nil { continue } @@ -242,21 +246,22 @@ func (r *Repository) AdminList(query AdminListQuery) (*AdminPaginatedResult, err } // 管理员查询提现详情 -func (r *Repository) AdminFindByID(id uint64) (*WithdrawalDetailDTO, error) { +func (r *Repository) AdminFindByID(ctx context.Context, id uint64) (*WithdrawalDetailDTO, error) { var withdrawal model.WithdrawalRequest - if err := r.db.First(&withdrawal, id).Error; err != nil { + if err := r.db.WithContext(ctx).First(&withdrawal, id).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, ErrWithdrawalNotFound } return nil, err } - return r.toDetailDTO(withdrawal) + return r.toDetailDTO(ctx, withdrawal) } // 管理员审核提现 -func (r *Repository) Review(adminID, id uint64, req ReviewWithdrawalRequest) (*WithdrawalDetailDTO, error) { +func (r *Repository) Review(ctx context.Context, adminID, id uint64, req ReviewWithdrawalRequest) (*WithdrawalDetailDTO, error) { + db := r.db.WithContext(ctx) var withdrawal model.WithdrawalRequest - if err := r.db.First(&withdrawal, id).Error; err != nil { + if err := db.First(&withdrawal, id).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, ErrWithdrawalNotFound } @@ -282,7 +287,7 @@ func (r *Repository) Review(adminID, id uint64, req ReviewWithdrawalRequest) (*W withdrawal.ReviewedAt = &now withdrawal.ReviewRemark = req.Remark - err := r.db.Transaction(func(tx *gorm.DB) error { + err := db.Transaction(func(tx *gorm.DB) error { if err := tx.Save(&withdrawal).Error; err != nil { return err } @@ -317,13 +322,14 @@ func (r *Repository) Review(adminID, id uint64, req ReviewWithdrawalRequest) (*W return nil, err } - return r.AdminFindByID(id) + return r.AdminFindByID(ctx, id) } // 管理员确认打款 -func (r *Repository) ConfirmPayment(adminID, id uint64, req ConfirmPaymentRequest) (*WithdrawalDetailDTO, error) { +func (r *Repository) ConfirmPayment(ctx context.Context, adminID, id uint64, req ConfirmPaymentRequest) (*WithdrawalDetailDTO, error) { + db := r.db.WithContext(ctx) var withdrawal model.WithdrawalRequest - if err := r.db.First(&withdrawal, id).Error; err != nil { + if err := db.First(&withdrawal, id).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, ErrWithdrawalNotFound } @@ -342,7 +348,7 @@ func (r *Repository) ConfirmPayment(adminID, id uint64, req ConfirmPaymentReques withdrawal.PaymentProofURL = req.PaymentProofURL withdrawal.PaymentRemark = req.Remark - err := r.db.Transaction(func(tx *gorm.DB) error { + err := db.Transaction(func(tx *gorm.DB) error { if err := tx.Save(&withdrawal).Error; err != nil { return err } @@ -367,7 +373,7 @@ func (r *Repository) ConfirmPayment(adminID, id uint64, req ConfirmPaymentReques return nil, err } - return r.AdminFindByID(id) + return r.AdminFindByID(ctx, id) } // 转换为用户DTO @@ -393,16 +399,17 @@ func toDTO(w model.WithdrawalRequest) WithdrawalDTO { } // 转换为管理员详细DTO -func (r *Repository) toDetailDTO(w model.WithdrawalRequest) (*WithdrawalDetailDTO, error) { +func (r *Repository) toDetailDTO(ctx context.Context, w model.WithdrawalRequest) (*WithdrawalDetailDTO, error) { + db := r.db.WithContext(ctx) // 查询用户信息 var user model.User - r.db.Select("nickname, phone").First(&user, w.UserID) + db.Select("nickname, phone").First(&user, w.UserID) // 查询审核人信息 var reviewedByName string if w.ReviewedBy != nil { var admin model.AdminUser - if err := r.db.Select("nickname").First(&admin, *w.ReviewedBy).Error; err == nil { + if err := db.Select("nickname").First(&admin, *w.ReviewedBy).Error; err == nil { reviewedByName = admin.Nickname } } @@ -411,7 +418,7 @@ func (r *Repository) toDetailDTO(w model.WithdrawalRequest) (*WithdrawalDetailDT var paidByName string if w.PaidBy != nil { var admin model.AdminUser - if err := r.db.Select("nickname").First(&admin, *w.PaidBy).Error; err == nil { + if err := db.Select("nickname").First(&admin, *w.PaidBy).Error; err == nil { paidByName = admin.Nickname } } @@ -421,7 +428,7 @@ func (r *Repository) toDetailDTO(w model.WithdrawalRequest) (*WithdrawalDetailDT var certificateURLs []string if w.PaymentAccountID != nil { var paymentAccount model.UserPaymentAccount - if err := r.db.First(&paymentAccount, *w.PaymentAccountID).Error; err == nil { + if err := db.First(&paymentAccount, *w.PaymentAccountID).Error; err == nil { // 解密账号 decrypted, err := crypto.Decrypt(paymentAccount.AccountNo) if err == nil { diff --git a/backend/internal/modules/withdrawal/service.go b/backend/internal/modules/withdrawal/service.go index 92043b1..4f5c535 100644 --- a/backend/internal/modules/withdrawal/service.go +++ b/backend/internal/modules/withdrawal/service.go @@ -1,6 +1,9 @@ package withdrawal -import "errors" +import ( + "context" + "errors" +) var ( ErrDependencyUnavailable = errors.New("dependency unavailable") @@ -14,7 +17,7 @@ var ( ) const ( - MinWithdrawalAmountCent = 1000 // 最低提现金额:10元 = 1000分 + MinWithdrawalAmountCent = 1000 // 最低提现金额:10元 = 1000分 MaxWithdrawalAmountCent = 500000 // 单笔最高提现金额:5000元 = 500000分 WithdrawalFeeRate = 0.0 // 手续费率(暂时0%) ) @@ -28,7 +31,7 @@ func NewService(repo *Repository) *Service { } // 用户端方法 -func (s *Service) Create(userID uint64, req CreateWithdrawalRequest) (*WithdrawalDTO, error) { +func (s *Service) Create(ctx context.Context, userID uint64, req CreateWithdrawalRequest) (*WithdrawalDTO, error) { if s.repo == nil { return nil, ErrDependencyUnavailable } @@ -41,10 +44,10 @@ func (s *Service) Create(userID uint64, req CreateWithdrawalRequest) (*Withdrawa return nil, ErrMaxWithdrawalAmount } - return s.repo.Create(userID, req) + return s.repo.Create(ctx, userID, req) } -func (s *Service) List(userID uint64, page, pageSize int) (*PaginatedResult, error) { +func (s *Service) List(ctx context.Context, userID uint64, page, pageSize int) (*PaginatedResult, error) { if s.repo == nil { return nil, ErrDependencyUnavailable } @@ -54,25 +57,25 @@ func (s *Service) List(userID uint64, page, pageSize int) (*PaginatedResult, err if pageSize < 1 || pageSize > 100 { pageSize = 20 } - return s.repo.List(userID, page, pageSize) + return s.repo.List(ctx, userID, page, pageSize) } -func (s *Service) FindByID(userID, id uint64) (*WithdrawalDTO, error) { +func (s *Service) FindByID(ctx context.Context, userID, id uint64) (*WithdrawalDTO, error) { if s.repo == nil { return nil, ErrDependencyUnavailable } - return s.repo.FindByID(userID, id) + return s.repo.FindByID(ctx, userID, id) } -func (s *Service) Cancel(userID, id uint64) error { +func (s *Service) Cancel(ctx context.Context, userID, id uint64) error { if s.repo == nil { return ErrDependencyUnavailable } - return s.repo.Cancel(userID, id) + return s.repo.Cancel(ctx, userID, id) } // 管理员端方法 -func (s *Service) AdminList(query AdminListQuery) (*AdminPaginatedResult, error) { +func (s *Service) AdminList(ctx context.Context, query AdminListQuery) (*AdminPaginatedResult, error) { if s.repo == nil { return nil, ErrDependencyUnavailable } @@ -82,26 +85,26 @@ func (s *Service) AdminList(query AdminListQuery) (*AdminPaginatedResult, error) if query.Size < 1 || query.Size > 100 { query.Size = 20 } - return s.repo.AdminList(query) + return s.repo.AdminList(ctx, query) } -func (s *Service) AdminFindByID(id uint64) (*WithdrawalDetailDTO, error) { +func (s *Service) AdminFindByID(ctx context.Context, id uint64) (*WithdrawalDetailDTO, error) { if s.repo == nil { return nil, ErrDependencyUnavailable } - return s.repo.AdminFindByID(id) + return s.repo.AdminFindByID(ctx, id) } -func (s *Service) Review(adminID, id uint64, req ReviewWithdrawalRequest) (*WithdrawalDetailDTO, error) { +func (s *Service) Review(ctx context.Context, adminID, id uint64, req ReviewWithdrawalRequest) (*WithdrawalDetailDTO, error) { if s.repo == nil { return nil, ErrDependencyUnavailable } - return s.repo.Review(adminID, id, req) + return s.repo.Review(ctx, adminID, id, req) } -func (s *Service) ConfirmPayment(adminID, id uint64, req ConfirmPaymentRequest) (*WithdrawalDetailDTO, error) { +func (s *Service) ConfirmPayment(ctx context.Context, adminID, id uint64, req ConfirmPaymentRequest) (*WithdrawalDetailDTO, error) { if s.repo == nil { return nil, ErrDependencyUnavailable } - return s.repo.ConfirmPayment(adminID, id, req) + return s.repo.ConfirmPayment(ctx, adminID, id, req) }