完成剩余模块 Context 超时控制改造

This commit is contained in:
yml2213
2026-06-10 12:58:59 +08:00
parent d2858c529d
commit 6ae8f0e830
24 changed files with 370 additions and 323 deletions
+6 -6
View File
@@ -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 { func createOwnerPaymentAccount(t *testing.T, service *paymentaccount.Service, ownerID uint64) *paymentaccount.PaymentAccountDTO {
t.Helper() t.Helper()
account, err := service.Create(ownerID, paymentaccount.CreatePaymentAccountRequest{ account, err := service.Create(t.Context(), ownerID, paymentaccount.CreatePaymentAccountRequest{
AccountType: "alipay", AccountType: "alipay",
AccountName: "张三", AccountName: "张三",
AccountNo: "owner@example.com", AccountNo: "owner@example.com",
@@ -458,7 +458,7 @@ func assertFinanceDashboard(t *testing.T, service *adminfinance.Service, orderNo
StartDate: now.Add(-time.Hour), StartDate: now.Add(-time.Hour),
EndDate: now.Add(time.Hour), EndDate: now.Add(time.Hour),
} }
dashboard, err := service.Dashboard(query) dashboard, err := service.Dashboard(t.Context(), query)
if err != nil { if err != nil {
t.Fatalf("读取财务看板失败: %v", err) 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.OwnerWalletIncomeAmountCent, int64(27500))
assertEqual(t, "财务结算订单数", dashboard.Summary.SettledOrderCount, int64(1)) assertEqual(t, "财务结算订单数", dashboard.Summary.SettledOrderCount, int64(1))
details, err := service.Details(adminfinance.DetailQuery{ details, err := service.Details(t.Context(), adminfinance.DetailQuery{
OrderNo: orderNo, OrderNo: orderNo,
DateType: "settled", DateType: "settled",
StartDate: query.StartDate, StartDate: query.StartDate,
@@ -497,7 +497,7 @@ func assertWalletAndWithdrawal(t *testing.T, walletService *wallet.Service, with
} }
assertEqual(t, "提现前可用余额", accountBefore.AvailableBalanceCent, int64(27500)) assertEqual(t, "提现前可用余额", accountBefore.AvailableBalanceCent, int64(27500))
req, err := withdrawalService.Create(ownerID, withdrawal.CreateWithdrawalRequest{ req, err := withdrawalService.Create(t.Context(), ownerID, withdrawal.CreateWithdrawalRequest{
PaymentAccountID: paymentAccountID, PaymentAccountID: paymentAccountID,
AmountCent: 10000, AmountCent: 10000,
}) })
@@ -507,7 +507,7 @@ func assertWalletAndWithdrawal(t *testing.T, walletService *wallet.Service, with
assertEqual(t, "提现申请金额", req.AmountCent, int64(10000)) assertEqual(t, "提现申请金额", req.AmountCent, int64(10000))
assertEqual(t, "提现申请状态", req.Status, "pending") 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, Approved: true,
Remark: "E2E 审核通过", Remark: "E2E 审核通过",
}) })
@@ -516,7 +516,7 @@ func assertWalletAndWithdrawal(t *testing.T, walletService *wallet.Service, with
} }
assertEqual(t, "提现审核后状态", reviewed.Status, "processing") 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", PaymentProofURL: "https://example.com/proof.png",
Remark: "E2E 已打款", Remark: "E2E 已打款",
}) })
+1 -3
View File
@@ -1,7 +1,6 @@
package middleware package middleware
import ( import (
"context"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
@@ -53,9 +52,8 @@ func getPermCodes(c *gin.Context, rdb *redis.Client, adminID uint64) ([]string,
if rdb == nil { if rdb == nil {
return nil, errors.New("redis unavailable") return nil, errors.New("redis unavailable")
} }
ctx := context.Background()
key := fmt.Sprintf("admin:perms:%d", adminID) 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) { if errors.Is(err, redis.Nil) {
return nil, nil // 缓存未命中,视为无权限 return nil, nil // 缓存未命中,视为无权限
} }
@@ -197,7 +197,7 @@ func (r *Repository) loadRolesAndPerms(ctx context.Context, dto *AdminDTO) {
for _, role := range roles { for _, role := range roles {
if role.Code == "super_admin" { if role.Code == "super_admin" {
dto.Permissions = []string{"*"} dto.Permissions = []string{"*"}
cachePermissions(r, dto.ID, dto.Permissions) cachePermissions(ctx, r, dto.ID, dto.Permissions)
return return
} }
} }
@@ -212,14 +212,13 @@ func (r *Repository) loadRolesAndPerms(ctx context.Context, dto *AdminDTO) {
dto.Permissions = permCodes dto.Permissions = permCodes
// 缓存权限到 Redis // 缓存权限到 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 { if r.redis == nil || len(permCodes) == 0 {
return return
} }
ctx := context.Background()
key := fmt.Sprintf("admin:perms:%d", adminID) key := fmt.Sprintf("admin:perms:%d", adminID)
raw, _ := json.Marshal(permCodes) raw, _ := json.Marshal(permCodes)
r.redis.Set(ctx, key, string(raw), 2*time.Hour) r.redis.Set(ctx, key, string(raw), 2*time.Hour)
@@ -18,7 +18,7 @@ func NewHandler(service *Service) *Handler {
} }
func (h *Handler) Summary(c *gin.Context) { func (h *Handler) Summary(c *gin.Context) {
item, err := h.service.Summary() item, err := h.service.Summary(c.Request.Context())
if err != nil { if err != nil {
writeDashboardError(c, err) writeDashboardError(c, err)
return return
@@ -1,6 +1,7 @@
package admindashboard package admindashboard
import ( import (
"context"
"time" "time"
"hfb_sys/backend/internal/model" "hfb_sys/backend/internal/model"
@@ -16,56 +17,57 @@ func NewRepository(db *gorm.DB) *Repository {
return &Repository{db: db} 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() now := time.Now()
today := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location()) today := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
metrics := MetricsDTO{} metrics := MetricsDTO{}
pending := PendingDTO{} 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 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 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 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 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 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 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 return nil, err
} }
if err := r.db.Model(&model.WalletLedger{}). if err := db.Model(&model.WalletLedger{}).
Select("COALESCE(SUM(amount_cent), 0)"). Select("COALESCE(SUM(amount_cent), 0)").
Where("created_at >= ?", today). Where("created_at >= ?", today).
Scan(&metrics.TodayLedgerAmountCent).Error; err != nil { Scan(&metrics.TodayLedgerAmountCent).Error; err != nil {
return nil, err 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 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 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 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 return nil, err
} }
recentOrders, err := r.recentOrders() recentOrders, err := r.recentOrders(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
recentDisputes, err := r.recentDisputes() recentDisputes, err := r.recentDisputes(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -78,9 +80,9 @@ func (r *Repository) Summary() (*DashboardDTO, error) {
}, nil }, nil
} }
func (r *Repository) recentOrders() ([]RecentOrderDTO, error) { func (r *Repository) recentOrders(ctx context.Context) ([]RecentOrderDTO, error) {
rows := make([]RecentOrderDTO, 0) 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"). 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"). Joins("JOIN game_accounts AS a ON a.id = o.account_id").
Order("o.id DESC"). Order("o.id DESC").
@@ -89,9 +91,9 @@ func (r *Repository) recentOrders() ([]RecentOrderDTO, error) {
return rows, err return rows, err
} }
func (r *Repository) recentDisputes() ([]RecentDisputeDTO, error) { func (r *Repository) recentDisputes(ctx context.Context) ([]RecentDisputeDTO, error) {
rows := make([]RecentDisputeDTO, 0) 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"). 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 rental_orders AS o ON o.id = d.order_id").
Joins("JOIN game_accounts AS a ON a.id = o.account_id"). Joins("JOIN game_accounts AS a ON a.id = o.account_id").
@@ -1,6 +1,9 @@
package admindashboard package admindashboard
import "errors" import (
"context"
"errors"
)
var ErrDependencyUnavailable = errors.New("dependency unavailable") var ErrDependencyUnavailable = errors.New("dependency unavailable")
@@ -12,9 +15,9 @@ func NewService(repo *Repository) *Service {
return &Service{repo: repo} return &Service{repo: repo}
} }
func (s *Service) Summary() (*DashboardDTO, error) { func (s *Service) Summary(ctx context.Context) (*DashboardDTO, error) {
if s.repo == nil { if s.repo == nil {
return nil, ErrDependencyUnavailable return nil, ErrDependencyUnavailable
} }
return s.repo.Summary() return s.repo.Summary(ctx)
} }
@@ -25,7 +25,7 @@ func (h *Handler) Dashboard(c *gin.Context) {
if !ok { if !ok {
return return
} }
item, err := h.service.Dashboard(query) item, err := h.service.Dashboard(c.Request.Context(), query)
if err != nil { if err != nil {
writeFinanceError(c, err) writeFinanceError(c, err)
return return
@@ -38,7 +38,7 @@ func (h *Handler) Details(c *gin.Context) {
if !ok { if !ok {
return return
} }
result, err := h.service.Details(query) result, err := h.service.Details(c.Request.Context(), query)
if err != nil { if err != nil {
writeFinanceError(c, err) writeFinanceError(c, err)
return return
@@ -1,6 +1,7 @@
package adminfinance package adminfinance
import ( import (
"context"
"time" "time"
"hfb_sys/backend/internal/timeutil" "hfb_sys/backend/internal/timeutil"
@@ -16,12 +17,12 @@ func NewRepository(db *gorm.DB) *Repository {
return &Repository{db: db} return &Repository{db: db}
} }
func (r *Repository) Dashboard(query DashboardQuery) (*DashboardDTO, error) { func (r *Repository) Dashboard(ctx context.Context, query DashboardQuery) (*DashboardDTO, error) {
dailyItems, err := r.dailyItems(query) dailyItems, err := r.dailyItems(ctx, query)
if err != nil { if err != nil {
return nil, err return nil, err
} }
summary, err := r.summary(query) summary, err := r.summary(ctx, query)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -32,10 +33,11 @@ func (r *Repository) Dashboard(query DashboardQuery) (*DashboardDTO, error) {
}, nil }, nil
} }
func (r *Repository) Details(query DetailQuery) (*PaginatedResult, error) { func (r *Repository) Details(ctx context.Context, query DetailQuery) (*PaginatedResult, error) {
db := r.financeDetailBaseQuery(query) baseDB := r.db.WithContext(ctx)
db := r.financeDetailBaseQuery(ctx, query)
var total int64 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 { if err := countDB.Count(&total).Error; err != nil {
return nil, err 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 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 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, 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 = '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, 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 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, 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(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, COALESCE(SUM(COALESCE(w.owner_wallet_income_amount_cent, 0)), 0) AS owner_wallet_income_amount_cent,
COUNT(ro.id) AS settled_order_count`). COUNT(ro.id) AS settled_order_count`).
Joins("JOIN order_checkouts AS oc ON oc.order_id = ro.id AND oc.status = 'accepted'"). 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). Where("ro.settled_at >= ? AND ro.settled_at <= ?", query.StartDate, query.EndDate).
Scan(&settlement).Error; err != nil { Scan(&settlement).Error; err != nil {
return nil, err return nil, err
} }
var exceptionCount int64 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", DateType: "settled",
StartDate: query.StartDate, StartDate: query.StartDate,
EndDate: query.EndDate, EndDate: query.EndDate,
@@ -108,9 +111,10 @@ func (r *Repository) summary(query DashboardQuery) (*FinanceSummaryDTO, error) {
}, nil }, 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) payments := make([]dailyPaymentRow, 0)
if err := r.db.Table("payment_orders"). if err := db.Table("payment_orders").
Select(`DATE(created_at) AS date, 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 ('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 = '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) 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, Select(`DATE(ro.settled_at) AS date,
COALESCE(SUM(oc.platform_fee_cent), 0) AS platform_income_amount_cent, 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(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, COALESCE(SUM(COALESCE(w.owner_wallet_income_amount_cent, 0)), 0) AS owner_wallet_income_amount_cent,
COUNT(ro.id) AS settled_order_count`). COUNT(ro.id) AS settled_order_count`).
Joins("JOIN order_checkouts AS oc ON oc.order_id = ro.id AND oc.status = 'accepted'"). 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). Where("ro.settled_at >= ? AND ro.settled_at <= ?", query.StartDate, query.EndDate).
Group("DATE(ro.settled_at)"). Group("DATE(ro.settled_at)").
Scan(&settlements).Error; err != nil { Scan(&settlements).Error; err != nil {
@@ -175,8 +179,8 @@ func (r *Repository) dailyItems(query DashboardQuery) ([]FinanceDailyDTO, error)
return items, nil return items, nil
} }
func (r *Repository) financeDetailBaseQuery(query DetailQuery) *gorm.DB { func (r *Repository) financeDetailBaseQuery(ctx context.Context, query DetailQuery) *gorm.DB {
db := r.db.Table("rental_orders AS ro"). 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, 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.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, 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`). ro.created_at, ro.settled_at`).
Joins("LEFT JOIN users AS ru ON ru.id = ro.renter_id"). 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 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 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)). 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)) Joins("LEFT JOIN (?) AS w ON w.order_id = ro.id", ownerWalletIncomeSubquery(r.db.WithContext(ctx)))
if query.OrderNo != "" { if query.OrderNo != "" {
db = db.Where("ro.order_no = ?", query.OrderNo) db = db.Where("ro.order_no = ?", query.OrderNo)
@@ -1,6 +1,9 @@
package adminfinance package adminfinance
import "errors" import (
"context"
"errors"
)
var ErrDependencyUnavailable = errors.New("dependency unavailable") var ErrDependencyUnavailable = errors.New("dependency unavailable")
@@ -12,14 +15,14 @@ func NewService(repo *Repository) *Service {
return &Service{repo: repo} 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 { if s.repo == nil {
return nil, ErrDependencyUnavailable 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 { if s.repo == nil {
return nil, ErrDependencyUnavailable return nil, ErrDependencyUnavailable
} }
@@ -32,5 +35,5 @@ func (s *Service) Details(query DetailQuery) (*PaginatedResult, error) {
if query.PageSize > 100 { if query.PageSize > 100 {
query.PageSize = 100 query.PageSize = 100
} }
return s.repo.Details(query) return s.repo.Details(ctx, query)
} }
+7 -7
View File
@@ -22,7 +22,7 @@ func NewHandler(service *Service) *Handler {
func (h *Handler) List(c *gin.Context) { func (h *Handler) List(c *gin.Context) {
page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20")) 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 { if err != nil {
writeError(c, err) writeError(c, err)
return return
@@ -35,7 +35,7 @@ func (h *Handler) FindByID(c *gin.Context) {
if !ok { if !ok {
return return
} }
item, err := h.service.FindByID(id) item, err := h.service.FindByID(c.Request.Context(), id)
if err != nil { if err != nil {
writeError(c, err) writeError(c, err)
return return
@@ -49,7 +49,7 @@ func (h *Handler) Create(c *gin.Context) {
response.BadRequest(c, "用户名和密码不能为空") response.BadRequest(c, "用户名和密码不能为空")
return return
} }
item, err := h.service.Create(req) item, err := h.service.Create(c.Request.Context(), req)
if err != nil { if err != nil {
writeError(c, err) writeError(c, err)
return return
@@ -67,7 +67,7 @@ func (h *Handler) Update(c *gin.Context) {
response.BadRequest(c, "请求格式不正确") response.BadRequest(c, "请求格式不正确")
return return
} }
item, err := h.service.Update(id, req) item, err := h.service.Update(c.Request.Context(), id, req)
if err != nil { if err != nil {
writeError(c, err) writeError(c, err)
return return
@@ -81,7 +81,7 @@ func (h *Handler) Delete(c *gin.Context) {
return return
} }
currentAdminID, _ := c.Get(middleware.ContextAdminID) 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) writeError(c, err)
return return
} }
@@ -98,7 +98,7 @@ func (h *Handler) AssignRoles(c *gin.Context) {
response.BadRequest(c, "请求格式不正确") response.BadRequest(c, "请求格式不正确")
return 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) writeError(c, err)
return return
} }
@@ -115,7 +115,7 @@ func (h *Handler) ChangePassword(c *gin.Context) {
response.BadRequest(c, "密码不能为空") response.BadRequest(c, "密码不能为空")
return 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) writeError(c, err)
return return
} }
+39 -35
View File
@@ -23,19 +23,20 @@ func NewRepository(db *gorm.DB, redis *redis.Client) *Repository {
return &Repository{db: db, redis: redis} 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 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 return nil, err
} }
offset := (page - 1) * pageSize offset := (page - 1) * pageSize
var admins []model.AdminUser 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 return nil, err
} }
items := make([]AdminUserDTO, 0, len(admins)) items := make([]AdminUserDTO, 0, len(admins))
for _, admin := range admins { for _, admin := range admins {
roles, _ := r.getAdminRoles(admin.ID) roles, _ := r.getAdminRoles(ctx, admin.ID)
items = append(items, AdminUserDTO{ items = append(items, AdminUserDTO{
ID: admin.ID, ID: admin.ID,
Username: admin.Username, 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 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 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 return nil, err
} }
roles, _ := r.getAdminRoles(admin.ID) roles, _ := r.getAdminRoles(ctx, admin.ID)
dto := AdminUserDTO{ dto := AdminUserDTO{
ID: admin.ID, ID: admin.ID,
Username: admin.Username, Username: admin.Username,
@@ -69,7 +70,7 @@ func (r *Repository) FindByID(id uint64) (*AdminUserDTO, error) {
return &dto, nil 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) hash, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -80,15 +81,16 @@ func (r *Repository) Create(req CreateAdminRequest) (*AdminUserDTO, error) {
Nickname: req.Nickname, Nickname: req.Nickname,
Status: "active", 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 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 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 return nil, err
} }
if req.Nickname != "" { if req.Nickname != "" {
@@ -97,46 +99,48 @@ func (r *Repository) Update(id uint64, req UpdateAdminRequest) (*AdminUserDTO, e
if req.Status != "" { if req.Status != "" {
admin.Status = 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 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 { if id == currentAdminID {
return ErrCannotDeleteSelf return ErrCannotDeleteSelf
} }
db := r.db.WithContext(ctx)
var admin model.AdminUser 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 return err
} }
// 检查是否是最后一个 super_admin // 检查是否是最后一个 super_admin
isLastSuper, err := r.isLastSuperAdmin(id) isLastSuper, err := r.isLastSuperAdmin(ctx, id)
if err != nil { if err != nil {
return err return err
} }
if isLastSuper { if isLastSuper {
return ErrLastSuperAdmin 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 { if err := tx.Where("admin_user_id = ?", id).Delete(&model.AdminUserRole{}).Error; err != nil {
return err return err
} }
if err := tx.Delete(&model.AdminUser{}, id).Error; err != nil { if err := tx.Delete(&model.AdminUser{}, id).Error; err != nil {
return err return err
} }
r.invalidatePermCache(id) r.invalidatePermCache(ctx, id)
return nil 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 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 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 { if err := tx.Where("admin_user_id = ?", adminID).Delete(&model.AdminUserRole{}).Error; err != nil {
return err return err
} }
@@ -151,13 +155,13 @@ func (r *Repository) AssignRoles(adminID uint64, roleIDs []uint64) error {
if err != nil { if err != nil {
return err return err
} }
r.invalidatePermCache(adminID) r.invalidatePermCache(ctx, adminID)
return nil 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 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 return err
} }
if err := bcrypt.CompareHashAndPassword([]byte(admin.PasswordHash), []byte(oldPwd)); err != nil { 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 { if err != nil {
return err 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 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"). Select("DISTINCT p.code").
Joins("JOIN role_permissions rp ON rp.role_id = aur.role_id"). Joins("JOIN role_permissions rp ON rp.role_id = aur.role_id").
Joins("JOIN permissions p ON p.id = rp.permission_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 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 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"). Joins("JOIN admin_user_roles aur ON aur.role_id = roles.id").
Where("aur.admin_user_id = ?", adminID). Where("aur.admin_user_id = ?", adminID).
Find(&roles).Error Find(&roles).Error
@@ -199,23 +203,23 @@ func (r *Repository) getAdminRoles(adminID uint64) ([]adminrole.RoleDTO, error)
return result, nil 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 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 角色,不限制 return false, nil // 没有 super_admin 角色,不限制
} }
var count int64 var count int64
err := r.db.Model(&model.AdminUserRole{}). err := db.Model(&model.AdminUserRole{}).
Where("role_id = ? AND admin_user_id != ?", superRole.ID, adminID). Where("role_id = ? AND admin_user_id != ?", superRole.ID, adminID).
Count(&count).Error Count(&count).Error
return count == 0, err return count == 0, err
} }
func (r *Repository) invalidatePermCache(adminID uint64) { func (r *Repository) invalidatePermCache(ctx context.Context, adminID uint64) {
if r.redis == nil { if r.redis == nil {
return return
} }
ctx := context.Background()
r.redis.Del(ctx, permCacheKey(adminID)) r.redis.Del(ctx, permCacheKey(adminID))
} }
+18 -15
View File
@@ -1,6 +1,9 @@
package adminmgr package adminmgr
import "errors" import (
"context"
"errors"
)
var ErrDependencyUnavailable = errors.New("dependency unavailable") var ErrDependencyUnavailable = errors.New("dependency unavailable")
@@ -12,7 +15,7 @@ func NewService(repo *Repository) *Service {
return &Service{repo: repo} 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 { if s.repo == nil {
return nil, ErrDependencyUnavailable return nil, ErrDependencyUnavailable
} }
@@ -22,53 +25,53 @@ func (s *Service) List(page, pageSize int) (*PaginatedResult, error) {
if pageSize < 1 || pageSize > 100 { if pageSize < 1 || pageSize > 100 {
pageSize = 20 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 { if s.repo == nil {
return nil, ErrDependencyUnavailable 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 { if s.repo == nil {
return nil, ErrDependencyUnavailable return nil, ErrDependencyUnavailable
} }
if len(req.Password) < 6 { if len(req.Password) < 6 {
return nil, errors.New("password too short") 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 { if s.repo == nil {
return nil, ErrDependencyUnavailable 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 { if s.repo == nil {
return ErrDependencyUnavailable 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 { if s.repo == nil {
return ErrDependencyUnavailable 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 { if s.repo == nil {
return ErrDependencyUnavailable return ErrDependencyUnavailable
} }
if len(req.NewPassword) < 6 { if len(req.NewPassword) < 6 {
return errors.New("new password too short") 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)
} }
@@ -19,7 +19,7 @@ func NewHandler(service *Service) *Handler {
} }
func (h *Handler) List(c *gin.Context) { func (h *Handler) List(c *gin.Context) {
items, err := h.service.List() items, err := h.service.List(c.Request.Context())
if err != nil { if err != nil {
writeError(c, err) writeError(c, err)
return return
@@ -32,7 +32,7 @@ func (h *Handler) FindByID(c *gin.Context) {
if !ok { if !ok {
return return
} }
item, err := h.service.FindByID(id) item, err := h.service.FindByID(c.Request.Context(), id)
if err != nil { if err != nil {
writeError(c, err) writeError(c, err)
return return
@@ -46,7 +46,7 @@ func (h *Handler) Create(c *gin.Context) {
response.BadRequest(c, "角色编码和名称不能为空") response.BadRequest(c, "角色编码和名称不能为空")
return return
} }
item, err := h.service.Create(req) item, err := h.service.Create(c.Request.Context(), req)
if err != nil { if err != nil {
writeError(c, err) writeError(c, err)
return return
@@ -64,7 +64,7 @@ func (h *Handler) Update(c *gin.Context) {
response.BadRequest(c, "角色名称不能为空") response.BadRequest(c, "角色名称不能为空")
return return
} }
item, err := h.service.Update(id, req) item, err := h.service.Update(c.Request.Context(), id, req)
if err != nil { if err != nil {
writeError(c, err) writeError(c, err)
return return
@@ -77,7 +77,7 @@ func (h *Handler) Delete(c *gin.Context) {
if !ok { if !ok {
return return
} }
if err := h.service.Delete(id); err != nil { if err := h.service.Delete(c.Request.Context(), id); err != nil {
writeError(c, err) writeError(c, err)
return return
} }
@@ -94,7 +94,7 @@ func (h *Handler) AssignPermissions(c *gin.Context) {
response.BadRequest(c, "请求格式不正确") response.BadRequest(c, "请求格式不正确")
return 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) writeError(c, err)
return return
} }
@@ -102,7 +102,7 @@ func (h *Handler) AssignPermissions(c *gin.Context) {
} }
func (h *Handler) ListPermissions(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 { if err != nil {
writeError(c, err) writeError(c, err)
return return
@@ -1,6 +1,7 @@
package adminrole package adminrole
import ( import (
"context"
"errors" "errors"
"hfb_sys/backend/internal/model" "hfb_sys/backend/internal/model"
@@ -17,18 +18,19 @@ func NewRepository(db *gorm.DB) *Repository {
return &Repository{db: db} 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 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 return nil, err
} }
result := make([]RoleDTO, 0, len(roles)) result := make([]RoleDTO, 0, len(roles))
for _, role := range roles { for _, role := range roles {
var count int64 var count int64
if role.Code == "super_admin" { if role.Code == "super_admin" {
r.db.Model(&model.Permission{}).Count(&count) db.Model(&model.Permission{}).Count(&count)
} else { } 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{ result = append(result, RoleDTO{
ID: role.ID, ID: role.ID,
@@ -43,17 +45,17 @@ func (r *Repository) List() ([]RoleDTO, error) {
return result, nil 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 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 return nil, err
} }
var perms []PermissionDTO var perms []PermissionDTO
var err error var err error
if role.Code == "super_admin" { if role.Code == "super_admin" {
perms, err = r.ListPermissions() perms, err = r.ListPermissions(ctx)
} else { } else {
perms, err = r.getRolePermissions(id) perms, err = r.getRolePermissions(ctx, id)
} }
if err != nil { if err != nil {
return nil, err return nil, err
@@ -71,40 +73,42 @@ func (r *Repository) FindByID(id uint64) (*RoleDTO, error) {
return &dto, nil 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{ role := model.Role{
Code: req.Code, Code: req.Code,
Name: req.Name, Name: req.Name,
Description: req.Description, 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 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 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 return nil, err
} }
role.Name = req.Name role.Name = req.Name
role.Description = req.Description 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 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 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 return err
} }
if role.Code == "super_admin" { if role.Code == "super_admin" {
return ErrProtectedRole 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 { if err := tx.Where("role_id = ?", id).Delete(&model.RolePermission{}).Error; err != nil {
return err 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 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 err
} }
return r.db.Transaction(func(tx *gorm.DB) error { return db.Transaction(func(tx *gorm.DB) error {
if role.Code == "super_admin" { if role.Code == "super_admin" {
var allPermIDs []uint64 var allPermIDs []uint64
if err := tx.Model(&model.Permission{}).Pluck("id", &allPermIDs).Error; err != nil { 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 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 return nil, err
} }
result := make([]PermissionDTO, 0, len(perms)) result := make([]PermissionDTO, 0, len(perms))
@@ -155,9 +160,10 @@ func (r *Repository) ListPermissions() ([]PermissionDTO, error) {
return result, nil 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 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 return nil, err
} }
if len(rps) == 0 { if len(rps) == 0 {
@@ -168,7 +174,7 @@ func (r *Repository) getRolePermissions(roleID uint64) ([]PermissionDTO, error)
ids = append(ids, rp.PermissionID) ids = append(ids, rp.PermissionID)
} }
var perms []model.Permission 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 return nil, err
} }
result := make([]PermissionDTO, 0, len(perms)) result := make([]PermissionDTO, 0, len(perms))
+18 -15
View File
@@ -1,6 +1,9 @@
package adminrole package adminrole
import "errors" import (
"context"
"errors"
)
var ErrDependencyUnavailable = errors.New("dependency unavailable") var ErrDependencyUnavailable = errors.New("dependency unavailable")
@@ -12,54 +15,54 @@ func NewService(repo *Repository) *Service {
return &Service{repo: repo} return &Service{repo: repo}
} }
func (s *Service) List() ([]RoleDTO, error) { func (s *Service) List(ctx context.Context) ([]RoleDTO, error) {
if s.repo == nil { if s.repo == nil {
return nil, ErrDependencyUnavailable 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 { if s.repo == nil {
return nil, ErrDependencyUnavailable 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 { if s.repo == nil {
return nil, ErrDependencyUnavailable return nil, ErrDependencyUnavailable
} }
if req.Code == "" || req.Name == "" { if req.Code == "" || req.Name == "" {
return nil, errors.New("code and name are required") 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 { if s.repo == nil {
return nil, ErrDependencyUnavailable 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 { if s.repo == nil {
return ErrDependencyUnavailable 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 { if s.repo == nil {
return ErrDependencyUnavailable 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 { if s.repo == nil {
return nil, ErrDependencyUnavailable return nil, ErrDependencyUnavailable
} }
return s.repo.ListPermissions() return s.repo.ListPermissions(ctx)
} }
@@ -27,7 +27,7 @@ func (h *Handler) List(c *gin.Context) {
} }
query.Page, query.PageSize = parsePagination(c) query.Page, query.PageSize = parsePagination(c)
result, err := h.service.List(query) result, err := h.service.List(c.Request.Context(), query)
if err != nil { if err != nil {
response.InternalServerError(c, "获取公告列表失败") response.InternalServerError(c, "获取公告列表失败")
return return
@@ -44,7 +44,7 @@ func (h *Handler) GetByID(c *gin.Context) {
return return
} }
announcement, err := h.service.GetByID(id) announcement, err := h.service.GetByID(c.Request.Context(), id)
if err != nil { if err != nil {
if errors.Is(err, ErrNotFound) { if errors.Is(err, ErrNotFound) {
response.NotFound(c, "公告不存在") response.NotFound(c, "公告不存在")
@@ -65,7 +65,7 @@ func (h *Handler) AdminList(c *gin.Context) {
} }
query.Page, query.PageSize = parsePagination(c) query.Page, query.PageSize = parsePagination(c)
result, err := h.service.AdminList(query) result, err := h.service.AdminList(c.Request.Context(), query)
if err != nil { if err != nil {
response.InternalServerError(c, "获取公告列表失败") response.InternalServerError(c, "获取公告列表失败")
return return
@@ -82,7 +82,7 @@ func (h *Handler) AdminGetByID(c *gin.Context) {
return return
} }
announcement, err := h.service.AdminGetByID(id) announcement, err := h.service.AdminGetByID(c.Request.Context(), id)
if err != nil { if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) { if errors.Is(err, gorm.ErrRecordNotFound) {
response.NotFound(c, "公告不存在") response.NotFound(c, "公告不存在")
@@ -109,7 +109,7 @@ func (h *Handler) Create(c *gin.Context) {
return return
} }
announcement, err := h.service.Create(req, adminID) announcement, err := h.service.Create(c.Request.Context(), req, adminID)
if err != nil { if err != nil {
response.InternalServerError(c, "创建公告失败") response.InternalServerError(c, "创建公告失败")
return return
@@ -132,7 +132,7 @@ func (h *Handler) Update(c *gin.Context) {
return return
} }
announcement, err := h.service.Update(id, req) announcement, err := h.service.Update(c.Request.Context(), id, req)
if err != nil { if err != nil {
response.InternalServerError(c, "更新公告失败") response.InternalServerError(c, "更新公告失败")
return return
@@ -149,7 +149,7 @@ func (h *Handler) Publish(c *gin.Context) {
return return
} }
if err := h.service.Publish(id); err != nil { if err := h.service.Publish(c.Request.Context(), id); err != nil {
response.InternalServerError(c, "发布公告失败") response.InternalServerError(c, "发布公告失败")
return return
} }
@@ -165,7 +165,7 @@ func (h *Handler) Archive(c *gin.Context) {
return return
} }
if err := h.service.Archive(id); err != nil { if err := h.service.Archive(c.Request.Context(), id); err != nil {
response.InternalServerError(c, "归档公告失败") response.InternalServerError(c, "归档公告失败")
return return
} }
@@ -181,7 +181,7 @@ func (h *Handler) Delete(c *gin.Context) {
return return
} }
if err := h.service.Delete(id); err != nil { if err := h.service.Delete(c.Request.Context(), id); err != nil {
response.InternalServerError(c, "删除公告失败") response.InternalServerError(c, "删除公告失败")
return return
} }
@@ -1,6 +1,7 @@
package announcement package announcement
import ( import (
"context"
"time" "time"
"hfb_sys/backend/internal/model" "hfb_sys/backend/internal/model"
@@ -17,9 +18,9 @@ func NewRepository(db *gorm.DB) *Repository {
} }
// List 获取公告列表(前台用户) // List 获取公告列表(前台用户)
func (r *Repository) List(query AnnouncementListQuery) (*PaginatedResult, error) { func (r *Repository) List(ctx context.Context, query AnnouncementListQuery) (*PaginatedResult, error) {
var total int64 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 != "" { if query.Category != "" {
tx = tx.Where("category = ?", query.Category) tx = tx.Where("category = ?", query.Category)
@@ -52,23 +53,24 @@ func (r *Repository) List(query AnnouncementListQuery) (*PaginatedResult, error)
} }
// GetByID 获取公告详情 // GetByID 获取公告详情
func (r *Repository) GetByID(id uint64) (*AnnouncementDTO, error) { func (r *Repository) GetByID(ctx context.Context, id uint64) (*AnnouncementDTO, error) {
var announcement model.Announcement 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 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) dto := toAnnouncementDTO(announcement)
return &dto, nil return &dto, nil
} }
// AdminList 管理员获取公告列表 // AdminList 管理员获取公告列表
func (r *Repository) AdminList(query AnnouncementListQuery) (*PaginatedResult, error) { func (r *Repository) AdminList(ctx context.Context, query AnnouncementListQuery) (*PaginatedResult, error) {
var total int64 var total int64
tx := r.db.Model(&model.Announcement{}) tx := r.db.WithContext(ctx).Model(&model.Announcement{})
if query.Status != "" { if query.Status != "" {
tx = tx.Where("status = ?", query.Status) tx = tx.Where("status = ?", query.Status)
@@ -104,9 +106,9 @@ func (r *Repository) AdminList(query AnnouncementListQuery) (*PaginatedResult, e
} }
// AdminGetByID 管理员获取公告详情 // AdminGetByID 管理员获取公告详情
func (r *Repository) AdminGetByID(id uint64) (*AnnouncementDTO, error) { func (r *Repository) AdminGetByID(ctx context.Context, id uint64) (*AnnouncementDTO, error) {
var announcement model.Announcement 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 return nil, err
} }
@@ -115,7 +117,7 @@ func (r *Repository) AdminGetByID(id uint64) (*AnnouncementDTO, error) {
} }
// Create 创建公告 // 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{ announcement := model.Announcement{
Title: req.Title, Title: req.Title,
Content: req.Content, Content: req.Content,
@@ -127,7 +129,7 @@ func (r *Repository) Create(req CreateAnnouncementRequest, createdBy uint64) (*A
CreatedBy: &createdBy, 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 return nil, err
} }
@@ -136,7 +138,7 @@ func (r *Repository) Create(req CreateAnnouncementRequest, createdBy uint64) (*A
} }
// Update 更新公告 // 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{}) updates := make(map[string]interface{})
if req.Title != "" { if req.Title != "" {
@@ -152,17 +154,17 @@ func (r *Repository) Update(id uint64, req UpdateAnnouncementRequest) (*Announce
updates["is_pinned"] = req.IsPinned updates["is_pinned"] = req.IsPinned
updates["is_important"] = req.IsImportant 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 nil, err
} }
return r.AdminGetByID(id) return r.AdminGetByID(ctx, id)
} }
// Publish 发布公告 // Publish 发布公告
func (r *Repository) Publish(id uint64) error { func (r *Repository) Publish(ctx context.Context, id uint64) error {
now := time.Now() now := time.Now()
return r.db.Model(&model.Announcement{}). return r.db.WithContext(ctx).Model(&model.Announcement{}).
Where("id = ?", id). Where("id = ?", id).
Updates(map[string]interface{}{ Updates(map[string]interface{}{
"status": "published", "status": "published",
@@ -171,15 +173,15 @@ func (r *Repository) Publish(id uint64) error {
} }
// Archive 归档公告 // Archive 归档公告
func (r *Repository) Archive(id uint64) error { func (r *Repository) Archive(ctx context.Context, id uint64) error {
return r.db.Model(&model.Announcement{}). return r.db.WithContext(ctx).Model(&model.Announcement{}).
Where("id = ?", id). Where("id = ?", id).
Update("status", "archived").Error Update("status", "archived").Error
} }
// Delete 删除公告 // Delete 删除公告
func (r *Repository) Delete(id uint64) error { func (r *Repository) Delete(ctx context.Context, id uint64) error {
return r.db.Where("id = ?", id).Delete(&model.Announcement{}).Error return r.db.WithContext(ctx).Where("id = ?", id).Delete(&model.Announcement{}).Error
} }
func toAnnouncementDTO(a model.Announcement) AnnouncementDTO { func toAnnouncementDTO(a model.Announcement) AnnouncementDTO {
@@ -1,6 +1,7 @@
package announcement package announcement
import ( import (
"context"
"errors" "errors"
"gorm.io/gorm" "gorm.io/gorm"
@@ -15,13 +16,13 @@ func NewService(repo *Repository) *Service {
} }
var ( var (
ErrNotFound = errors.New("公告不存在") ErrNotFound = errors.New("公告不存在")
ErrInvalidRequest = errors.New("请求参数不正确") ErrInvalidRequest = errors.New("请求参数不正确")
ErrUnauthorized = errors.New("未授权") ErrUnauthorized = errors.New("未授权")
) )
// List 获取公告列表(前台用户) // List 获取公告列表(前台用户)
func (s *Service) List(query AnnouncementListQuery) (*PaginatedResult, error) { func (s *Service) List(ctx context.Context, query AnnouncementListQuery) (*PaginatedResult, error) {
if query.Page < 1 { if query.Page < 1 {
query.Page = 1 query.Page = 1
} }
@@ -32,12 +33,12 @@ func (s *Service) List(query AnnouncementListQuery) (*PaginatedResult, error) {
query.PageSize = 100 query.PageSize = 100
} }
return s.repo.List(query) return s.repo.List(ctx, query)
} }
// GetByID 获取公告详情 // GetByID 获取公告详情
func (s *Service) GetByID(id uint64) (*AnnouncementDTO, error) { func (s *Service) GetByID(ctx context.Context, id uint64) (*AnnouncementDTO, error) {
dto, err := s.repo.GetByID(id) dto, err := s.repo.GetByID(ctx, id)
if err != nil { if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) { if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, ErrNotFound return nil, ErrNotFound
@@ -48,7 +49,7 @@ func (s *Service) GetByID(id uint64) (*AnnouncementDTO, error) {
} }
// AdminList 管理员获取公告列表 // AdminList 管理员获取公告列表
func (s *Service) AdminList(query AnnouncementListQuery) (*PaginatedResult, error) { func (s *Service) AdminList(ctx context.Context, query AnnouncementListQuery) (*PaginatedResult, error) {
if query.Page < 1 { if query.Page < 1 {
query.Page = 1 query.Page = 1
} }
@@ -59,12 +60,12 @@ func (s *Service) AdminList(query AnnouncementListQuery) (*PaginatedResult, erro
query.PageSize = 100 query.PageSize = 100
} }
return s.repo.AdminList(query) return s.repo.AdminList(ctx, query)
} }
// AdminGetByID 管理员获取公告详情 // AdminGetByID 管理员获取公告详情
func (s *Service) AdminGetByID(id uint64) (*AnnouncementDTO, error) { func (s *Service) AdminGetByID(ctx context.Context, id uint64) (*AnnouncementDTO, error) {
dto, err := s.repo.AdminGetByID(id) dto, err := s.repo.AdminGetByID(ctx, id)
if err != nil { if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) { if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, ErrNotFound return nil, ErrNotFound
@@ -75,26 +76,26 @@ func (s *Service) AdminGetByID(id uint64) (*AnnouncementDTO, error) {
} }
// Create 创建公告 // Create 创建公告
func (s *Service) Create(req CreateAnnouncementRequest, createdBy uint64) (*AnnouncementDTO, error) { func (s *Service) Create(ctx context.Context, req CreateAnnouncementRequest, createdBy uint64) (*AnnouncementDTO, error) {
return s.repo.Create(req, createdBy) return s.repo.Create(ctx, req, createdBy)
} }
// Update 更新公告 // Update 更新公告
func (s *Service) Update(id uint64, req UpdateAnnouncementRequest) (*AnnouncementDTO, error) { func (s *Service) Update(ctx context.Context, id uint64, req UpdateAnnouncementRequest) (*AnnouncementDTO, error) {
return s.repo.Update(id, req) return s.repo.Update(ctx, id, req)
} }
// Publish 发布公告 // Publish 发布公告
func (s *Service) Publish(id uint64) error { func (s *Service) Publish(ctx context.Context, id uint64) error {
return s.repo.Publish(id) return s.repo.Publish(ctx, id)
} }
// Archive 归档公告 // Archive 归档公告
func (s *Service) Archive(id uint64) error { func (s *Service) Archive(ctx context.Context, id uint64) error {
return s.repo.Archive(id) return s.repo.Archive(ctx, id)
} }
// Delete 删除公告 // Delete 删除公告
func (s *Service) Delete(id uint64) error { func (s *Service) Delete(ctx context.Context, id uint64) error {
return s.repo.Delete(id) return s.repo.Delete(ctx, id)
} }
@@ -25,7 +25,7 @@ func (h *Handler) List(c *gin.Context) {
page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20")) 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 { if err != nil {
writeError(c, err) writeError(c, err)
return return
@@ -46,7 +46,7 @@ func (h *Handler) FindByID(c *gin.Context) {
return return
} }
account, err := h.service.FindByID(userID, id) account, err := h.service.FindByID(c.Request.Context(), userID, id)
if err != nil { if err != nil {
writeError(c, err) writeError(c, err)
return return
@@ -68,7 +68,7 @@ func (h *Handler) Create(c *gin.Context) {
return return
} }
account, err := h.service.Create(userID, req) account, err := h.service.Create(c.Request.Context(), userID, req)
if err != nil { if err != nil {
writeError(c, err) writeError(c, err)
return return
@@ -95,7 +95,7 @@ func (h *Handler) Update(c *gin.Context) {
return return
} }
account, err := h.service.Update(userID, id, req) account, err := h.service.Update(c.Request.Context(), userID, id, req)
if err != nil { if err != nil {
writeError(c, err) writeError(c, err)
return return
@@ -116,7 +116,7 @@ func (h *Handler) Delete(c *gin.Context) {
return 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) writeError(c, err)
return return
} }
@@ -136,7 +136,7 @@ func (h *Handler) SetDefault(c *gin.Context) {
return 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) writeError(c, err)
return return
} }
@@ -1,6 +1,7 @@
package paymentaccount package paymentaccount
import ( import (
"context"
"encoding/json" "encoding/json"
"errors" "errors"
"strings" "strings"
@@ -22,9 +23,10 @@ func NewRepository(db *gorm.DB) *Repository {
return &Repository{db: db} 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 var total int64
if err := r.db.Model(&model.UserPaymentAccount{}). if err := db.Model(&model.UserPaymentAccount{}).
Where("user_id = ? AND status = ?", userID, "active"). Where("user_id = ? AND status = ?", userID, "active").
Count(&total).Error; err != nil { Count(&total).Error; err != nil {
return nil, err return nil, err
@@ -32,7 +34,7 @@ func (r *Repository) List(userID uint64, page, pageSize int) (*PaginatedResult,
offset := (page - 1) * pageSize offset := (page - 1) * pageSize
var accounts []model.UserPaymentAccount 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"). Order("is_default DESC, created_at DESC").
Offset(offset).Limit(pageSize). Offset(offset).Limit(pageSize).
Find(&accounts).Error; err != nil { Find(&accounts).Error; err != nil {
@@ -56,9 +58,9 @@ func (r *Repository) List(userID uint64, page, pageSize int) (*PaginatedResult,
}, nil }, 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 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 { First(&account).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) { if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, ErrAccountNotFound return nil, ErrAccountNotFound
@@ -68,7 +70,8 @@ func (r *Repository) FindByID(userID, id uint64) (*PaymentAccountDTO, error) {
return r.toDTO(account) 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) encryptedNo, err := crypto.Encrypt(req.AccountNo)
if err != nil { if err != nil {
@@ -84,7 +87,7 @@ func (r *Repository) Create(userID uint64, req CreatePaymentAccountRequest) (*Pa
// 如果是第一个账号,自动设为默认 // 如果是第一个账号,自动设为默认
isDefault := false isDefault := false
var count int64 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 { if count == 0 {
isDefault = true isDefault = true
} }
@@ -101,16 +104,17 @@ func (r *Repository) Create(userID uint64, req CreatePaymentAccountRequest) (*Pa
Status: "active", Status: "active",
} }
if err := r.db.Create(&account).Error; err != nil { if err := db.Create(&account).Error; err != nil {
return nil, err 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 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 { First(&account).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) { if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, ErrAccountNotFound return nil, ErrAccountNotFound
@@ -131,24 +135,25 @@ func (r *Repository) Update(userID, id uint64, req UpdatePaymentAccountRequest)
if req.IsDefault != nil && *req.IsDefault { if req.IsDefault != nil && *req.IsDefault {
// 先取消其他默认账号 // 先取消其他默认账号
r.db.Model(&model.UserPaymentAccount{}). db.Model(&model.UserPaymentAccount{}).
Where("user_id = ? AND id != ?", userID, id). Where("user_id = ? AND id != ?", userID, id).
Update("is_default", false) Update("is_default", false)
updates["is_default"] = true updates["is_default"] = true
} }
if len(updates) > 0 { 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 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 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 { First(&account).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) { if errors.Is(err, gorm.ErrRecordNotFound) {
return ErrAccountNotFound 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 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 { First(&account).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) { if errors.Is(err, gorm.ErrRecordNotFound) {
return ErrAccountNotFound return ErrAccountNotFound
@@ -171,7 +176,7 @@ func (r *Repository) SetDefault(userID, id uint64) error {
return err 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{}). if err := tx.Model(&model.UserPaymentAccount{}).
Where("user_id = ? AND id != ?", userID, id). 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 var count int64
err := r.db.Model(&model.UserPaymentAccount{}). err := r.db.WithContext(ctx).Model(&model.UserPaymentAccount{}).
Where("user_id = ? AND status = ?", userID, "active"). Where("user_id = ? AND status = ?", userID, "active").
Count(&count).Error Count(&count).Error
return count, err 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 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 return err
} }
@@ -203,7 +209,7 @@ func (r *Repository) ValidateRealname(userID uint64, accountName string) error {
// 获取实名信息 // 获取实名信息
var realname model.UserRealname 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 { First(&realname).Error; err != nil {
return ErrRealnameRequired 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 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 "", err
} }
return crypto.Decrypt(account.AccountNo) return crypto.Decrypt(account.AccountNo)
@@ -1,6 +1,9 @@
package paymentaccount package paymentaccount
import "errors" import (
"context"
"errors"
)
var ( var (
ErrDependencyUnavailable = errors.New("dependency unavailable") ErrDependencyUnavailable = errors.New("dependency unavailable")
@@ -19,7 +22,7 @@ func NewService(repo *Repository) *Service {
return &Service{repo: repo} 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 { if s.repo == nil {
return nil, ErrDependencyUnavailable return nil, ErrDependencyUnavailable
} }
@@ -29,52 +32,52 @@ func (s *Service) List(userID uint64, page, pageSize int) (*PaginatedResult, err
if pageSize < 1 || pageSize > 100 { if pageSize < 1 || pageSize > 100 {
pageSize = 20 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 { if s.repo == nil {
return nil, ErrDependencyUnavailable 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 { if s.repo == nil {
return nil, ErrDependencyUnavailable 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 return nil, err
} }
// 检查账号数量限制(最多5个) // 检查账号数量限制(最多5个)
count, err := s.repo.CountByUser(userID) count, err := s.repo.CountByUser(ctx, userID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if count >= 5 { if count >= 5 {
return nil, ErrAccountLimit 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 { if s.repo == nil {
return nil, ErrDependencyUnavailable 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 { if s.repo == nil {
return ErrDependencyUnavailable 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 { if s.repo == nil {
return ErrDependencyUnavailable return ErrDependencyUnavailable
} }
return s.repo.SetDefault(userID, id) return s.repo.SetDefault(ctx, userID, id)
} }
@@ -30,7 +30,7 @@ func (h *Handler) Create(c *gin.Context) {
return return
} }
withdrawal, err := h.service.Create(userID, req) withdrawal, err := h.service.Create(c.Request.Context(), userID, req)
if err != nil { if err != nil {
writeError(c, err) writeError(c, err)
return return
@@ -49,7 +49,7 @@ func (h *Handler) List(c *gin.Context) {
page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20")) 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 { if err != nil {
writeError(c, err) writeError(c, err)
return return
@@ -70,7 +70,7 @@ func (h *Handler) FindByID(c *gin.Context) {
return return
} }
withdrawal, err := h.service.FindByID(userID, id) withdrawal, err := h.service.FindByID(c.Request.Context(), userID, id)
if err != nil { if err != nil {
writeError(c, err) writeError(c, err)
return return
@@ -91,7 +91,7 @@ func (h *Handler) Cancel(c *gin.Context) {
return 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) writeError(c, err)
return return
} }
@@ -108,7 +108,7 @@ func (h *Handler) AdminList(c *gin.Context) {
query.Size = 20 query.Size = 20
} }
result, err := h.service.AdminList(query) result, err := h.service.AdminList(c.Request.Context(), query)
if err != nil { if err != nil {
writeError(c, err) writeError(c, err)
return return
@@ -123,7 +123,7 @@ func (h *Handler) AdminFindByID(c *gin.Context) {
return return
} }
withdrawal, err := h.service.AdminFindByID(id) withdrawal, err := h.service.AdminFindByID(c.Request.Context(), id)
if err != nil { if err != nil {
writeError(c, err) writeError(c, err)
return return
@@ -150,7 +150,7 @@ func (h *Handler) Review(c *gin.Context) {
return return
} }
withdrawal, err := h.service.Review(adminID, id, req) withdrawal, err := h.service.Review(c.Request.Context(), adminID, id, req)
if err != nil { if err != nil {
writeError(c, err) writeError(c, err)
return return
@@ -177,7 +177,7 @@ func (h *Handler) ConfirmPayment(c *gin.Context) {
return return
} }
withdrawal, err := h.service.ConfirmPayment(adminID, id, req) withdrawal, err := h.service.ConfirmPayment(c.Request.Context(), adminID, id, req)
if err != nil { if err != nil {
writeError(c, err) writeError(c, err)
return return
@@ -1,6 +1,7 @@
package withdrawal package withdrawal
import ( import (
"context"
"crypto/rand" "crypto/rand"
"encoding/hex" "encoding/hex"
"encoding/json" "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 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 { First(&paymentAccount).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) { if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, errors.New("payment account not found") 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 { if err := tx.Create(&withdrawal).Error; err != nil {
return err return err
@@ -105,13 +107,14 @@ func (r *Repository) Create(userID uint64, req CreateWithdrawalRequest) (*Withdr
return nil, err 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 var total int64
if err := r.db.Model(&model.WithdrawalRequest{}). if err := db.Model(&model.WithdrawalRequest{}).
Where("user_id = ?", userID). Where("user_id = ?", userID).
Count(&total).Error; err != nil { Count(&total).Error; err != nil {
return nil, err return nil, err
@@ -119,7 +122,7 @@ func (r *Repository) List(userID uint64, page, pageSize int) (*PaginatedResult,
offset := (page - 1) * pageSize offset := (page - 1) * pageSize
var withdrawals []model.WithdrawalRequest var withdrawals []model.WithdrawalRequest
if err := r.db.Where("user_id = ?", userID). if err := db.Where("user_id = ?", userID).
Order("created_at DESC"). Order("created_at DESC").
Offset(offset).Limit(pageSize). Offset(offset).Limit(pageSize).
Find(&withdrawals).Error; err != nil { 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 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 { First(&withdrawal).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) { if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, ErrWithdrawalNotFound 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 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 { First(&withdrawal).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) { if errors.Is(err, gorm.ErrRecordNotFound) {
return ErrWithdrawalNotFound return ErrWithdrawalNotFound
@@ -169,7 +173,7 @@ func (r *Repository) Cancel(userID, id uint64) error {
return ErrWithdrawalLocked 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 { if err := tx.Model(&withdrawal).Update("status", "cancelled").Error; err != nil {
return err return err
@@ -201,8 +205,8 @@ func (r *Repository) Cancel(userID, id uint64) error {
} }
// 管理员查询提现列表 // 管理员查询提现列表
func (r *Repository) AdminList(query AdminListQuery) (*AdminPaginatedResult, error) { func (r *Repository) AdminList(ctx context.Context, query AdminListQuery) (*AdminPaginatedResult, error) {
db := r.db.Model(&model.WithdrawalRequest{}) db := r.db.WithContext(ctx).Model(&model.WithdrawalRequest{})
if query.Status != "" { if query.Status != "" {
db = db.Where("status = ?", 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)) items := make([]WithdrawalDetailDTO, 0, len(withdrawals))
for _, w := range withdrawals { for _, w := range withdrawals {
dto, err := r.toDetailDTO(w) dto, err := r.toDetailDTO(ctx, w)
if err != nil { if err != nil {
continue 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 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) { if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, ErrWithdrawalNotFound return nil, ErrWithdrawalNotFound
} }
return nil, err 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 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) { if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, ErrWithdrawalNotFound return nil, ErrWithdrawalNotFound
} }
@@ -282,7 +287,7 @@ func (r *Repository) Review(adminID, id uint64, req ReviewWithdrawalRequest) (*W
withdrawal.ReviewedAt = &now withdrawal.ReviewedAt = &now
withdrawal.ReviewRemark = req.Remark 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 { if err := tx.Save(&withdrawal).Error; err != nil {
return err return err
} }
@@ -317,13 +322,14 @@ func (r *Repository) Review(adminID, id uint64, req ReviewWithdrawalRequest) (*W
return nil, err 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 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) { if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, ErrWithdrawalNotFound return nil, ErrWithdrawalNotFound
} }
@@ -342,7 +348,7 @@ func (r *Repository) ConfirmPayment(adminID, id uint64, req ConfirmPaymentReques
withdrawal.PaymentProofURL = req.PaymentProofURL withdrawal.PaymentProofURL = req.PaymentProofURL
withdrawal.PaymentRemark = req.Remark 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 { if err := tx.Save(&withdrawal).Error; err != nil {
return err return err
} }
@@ -367,7 +373,7 @@ func (r *Repository) ConfirmPayment(adminID, id uint64, req ConfirmPaymentReques
return nil, err return nil, err
} }
return r.AdminFindByID(id) return r.AdminFindByID(ctx, id)
} }
// 转换为用户DTO // 转换为用户DTO
@@ -393,16 +399,17 @@ func toDTO(w model.WithdrawalRequest) WithdrawalDTO {
} }
// 转换为管理员详细DTO // 转换为管理员详细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 var user model.User
r.db.Select("nickname, phone").First(&user, w.UserID) db.Select("nickname, phone").First(&user, w.UserID)
// 查询审核人信息 // 查询审核人信息
var reviewedByName string var reviewedByName string
if w.ReviewedBy != nil { if w.ReviewedBy != nil {
var admin model.AdminUser 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 reviewedByName = admin.Nickname
} }
} }
@@ -411,7 +418,7 @@ func (r *Repository) toDetailDTO(w model.WithdrawalRequest) (*WithdrawalDetailDT
var paidByName string var paidByName string
if w.PaidBy != nil { if w.PaidBy != nil {
var admin model.AdminUser 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 paidByName = admin.Nickname
} }
} }
@@ -421,7 +428,7 @@ func (r *Repository) toDetailDTO(w model.WithdrawalRequest) (*WithdrawalDetailDT
var certificateURLs []string var certificateURLs []string
if w.PaymentAccountID != nil { if w.PaymentAccountID != nil {
var paymentAccount model.UserPaymentAccount 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) decrypted, err := crypto.Decrypt(paymentAccount.AccountNo)
if err == nil { if err == nil {
+21 -18
View File
@@ -1,6 +1,9 @@
package withdrawal package withdrawal
import "errors" import (
"context"
"errors"
)
var ( var (
ErrDependencyUnavailable = errors.New("dependency unavailable") ErrDependencyUnavailable = errors.New("dependency unavailable")
@@ -14,7 +17,7 @@ var (
) )
const ( const (
MinWithdrawalAmountCent = 1000 // 最低提现金额:10元 = 1000分 MinWithdrawalAmountCent = 1000 // 最低提现金额:10元 = 1000分
MaxWithdrawalAmountCent = 500000 // 单笔最高提现金额:5000元 = 500000分 MaxWithdrawalAmountCent = 500000 // 单笔最高提现金额:5000元 = 500000分
WithdrawalFeeRate = 0.0 // 手续费率(暂时0% 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 { if s.repo == nil {
return nil, ErrDependencyUnavailable return nil, ErrDependencyUnavailable
} }
@@ -41,10 +44,10 @@ func (s *Service) Create(userID uint64, req CreateWithdrawalRequest) (*Withdrawa
return nil, ErrMaxWithdrawalAmount 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 { if s.repo == nil {
return nil, ErrDependencyUnavailable return nil, ErrDependencyUnavailable
} }
@@ -54,25 +57,25 @@ func (s *Service) List(userID uint64, page, pageSize int) (*PaginatedResult, err
if pageSize < 1 || pageSize > 100 { if pageSize < 1 || pageSize > 100 {
pageSize = 20 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 { if s.repo == nil {
return nil, ErrDependencyUnavailable 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 { if s.repo == nil {
return ErrDependencyUnavailable 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 { if s.repo == nil {
return nil, ErrDependencyUnavailable return nil, ErrDependencyUnavailable
} }
@@ -82,26 +85,26 @@ func (s *Service) AdminList(query AdminListQuery) (*AdminPaginatedResult, error)
if query.Size < 1 || query.Size > 100 { if query.Size < 1 || query.Size > 100 {
query.Size = 20 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 { if s.repo == nil {
return nil, ErrDependencyUnavailable 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 { if s.repo == nil {
return nil, ErrDependencyUnavailable 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 { if s.repo == nil {
return nil, ErrDependencyUnavailable return nil, ErrDependencyUnavailable
} }
return s.repo.ConfirmPayment(adminID, id, req) return s.repo.ConfirmPayment(ctx, adminID, id, req)
} }