package services import ( "context" "errors" "time" "quyun/v2/app/errorx" super_dto "quyun/v2/app/http/super/v1/dto" "quyun/v2/app/requests" "quyun/v2/database/models" "quyun/v2/pkg/consts" "github.com/google/uuid" "github.com/spf13/cast" "go.ipao.vip/gen/types" "gorm.io/gorm" ) // @provider type super struct{} func (s *super) Login(ctx context.Context, form *super_dto.LoginForm) (*super_dto.LoginResponse, error) { // TODO: Admin specific login or reuse User service return &super_dto.LoginResponse{}, nil } func (s *super) CheckToken(ctx context.Context) (*super_dto.LoginResponse, error) { return &super_dto.LoginResponse{}, nil } func (s *super) ListUsers(ctx context.Context, filter *super_dto.UserListFilter) (*requests.Pager, error) { tbl, q := models.UserQuery.QueryContext(ctx) if filter.Username != nil && *filter.Username != "" { q = q.Where(tbl.Username.Like("%" + *filter.Username + "%")).Or(tbl.Nickname.Like("%" + *filter.Username + "%")) } filter.Pagination.Format() total, err := q.Count() if err != nil { return nil, errorx.ErrDatabaseError.WithCause(err) } list, err := q.Offset(int(filter.Pagination.Offset())).Limit(int(filter.Pagination.Limit)).Order(tbl.ID.Desc()).Find() if err != nil { return nil, errorx.ErrDatabaseError.WithCause(err) } var data []super_dto.UserItem for _, u := range list { data = append(data, super_dto.UserItem{ SuperUserLite: super_dto.SuperUserLite{ ID: u.ID, Username: u.Username, Roles: u.Roles, Status: u.Status, StatusDescription: u.Status.Description(), CreatedAt: u.CreatedAt.Format(time.RFC3339), UpdatedAt: u.UpdatedAt.Format(time.RFC3339), }, Balance: u.Balance, BalanceFrozen: u.BalanceFrozen, }) } return &requests.Pager{ Pagination: filter.Pagination, Total: total, Items: data, }, nil } func (s *super) GetUser(ctx context.Context, id int64) (*super_dto.UserItem, error) { tbl, q := models.UserQuery.QueryContext(ctx) u, err := q.Where(tbl.ID.Eq(id)).First() if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, errorx.ErrRecordNotFound } return nil, errorx.ErrDatabaseError.WithCause(err) } return &super_dto.UserItem{ SuperUserLite: super_dto.SuperUserLite{ ID: u.ID, Username: u.Username, Roles: u.Roles, Status: u.Status, StatusDescription: u.Status.Description(), CreatedAt: u.CreatedAt.Format(time.RFC3339), UpdatedAt: u.UpdatedAt.Format(time.RFC3339), }, Balance: u.Balance, BalanceFrozen: u.BalanceFrozen, }, nil } func (s *super) UpdateUserStatus(ctx context.Context, id int64, form *super_dto.UserStatusUpdateForm) error { tbl, q := models.UserQuery.QueryContext(ctx) _, err := q.Where(tbl.ID.Eq(id)).Update(tbl.Status, consts.UserStatus(form.Status)) if err != nil { return errorx.ErrDatabaseError.WithCause(err) } return nil } func (s *super) UpdateUserRoles(ctx context.Context, id int64, form *super_dto.UserRolesUpdateForm) error { var roles types.Array[consts.Role] for _, r := range form.Roles { roles = append(roles, r) } tbl, q := models.UserQuery.QueryContext(ctx) _, err := q.Where(tbl.ID.Eq(id)).Update(tbl.Roles, roles) if err != nil { return errorx.ErrDatabaseError.WithCause(err) } return nil } func (s *super) ListTenants(ctx context.Context, filter *super_dto.TenantListFilter) (*requests.Pager, error) { tbl, q := models.TenantQuery.QueryContext(ctx) if filter.Name != nil && *filter.Name != "" { q = q.Where(tbl.Name.Like("%" + *filter.Name + "%")) } filter.Pagination.Format() total, err := q.Count() if err != nil { return nil, errorx.ErrDatabaseError.WithCause(err) } list, err := q.Offset(int(filter.Pagination.Offset())).Limit(int(filter.Pagination.Limit)).Order(tbl.ID.Desc()).Find() if err != nil { return nil, errorx.ErrDatabaseError.WithCause(err) } var data []super_dto.TenantItem for _, t := range list { data = append(data, super_dto.TenantItem{ ID: t.ID, UUID: t.UUID.String(), Name: t.Name, Code: t.Code, Status: t.Status, StatusDescription: t.Status.Description(), UserID: t.UserID, CreatedAt: t.CreatedAt.Format(time.RFC3339), UpdatedAt: t.UpdatedAt.Format(time.RFC3339), }) } return &requests.Pager{ Pagination: filter.Pagination, Total: total, Items: data, }, nil } func (s *super) CreateTenant(ctx context.Context, form *super_dto.TenantCreateForm) error { uid := cast.ToInt64(form.AdminUserID) if _, err := models.UserQuery.WithContext(ctx).Where(models.UserQuery.ID.Eq(uid)).First(); err != nil { return errorx.ErrRecordNotFound.WithMsg("用户不存在") } t := &models.Tenant{ UserID: uid, Name: form.Name, Code: form.Code, UUID: types.UUID(uuid.New()), Status: consts.TenantStatusVerified, } if err := models.TenantQuery.WithContext(ctx).Create(t); err != nil { return errorx.ErrDatabaseError.WithCause(err) } return nil } func (s *super) GetTenant(ctx context.Context, id int64) (*super_dto.TenantItem, error) { tbl, q := models.TenantQuery.QueryContext(ctx) t, err := q.Where(tbl.ID.Eq(id)).First() if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, errorx.ErrRecordNotFound } return nil, errorx.ErrDatabaseError.WithCause(err) } return &super_dto.TenantItem{ ID: t.ID, UUID: t.UUID.String(), Name: t.Name, Code: t.Code, Status: t.Status, StatusDescription: t.Status.Description(), UserID: t.UserID, CreatedAt: t.CreatedAt.Format(time.RFC3339), UpdatedAt: t.UpdatedAt.Format(time.RFC3339), }, nil } func (s *super) UpdateTenantStatus(ctx context.Context, id int64, form *super_dto.TenantStatusUpdateForm) error { tbl, q := models.TenantQuery.QueryContext(ctx) _, err := q.Where(tbl.ID.Eq(id)).Update(tbl.Status, consts.TenantStatus(form.Status)) if err != nil { return errorx.ErrDatabaseError.WithCause(err) } return nil } func (s *super) UpdateTenantExpire(ctx context.Context, id int64, form *super_dto.TenantExpireUpdateForm) error { expire := time.Now().AddDate(0, 0, form.Duration) tbl, q := models.TenantQuery.QueryContext(ctx) _, err := q.Where(tbl.ID.Eq(id)).Update(tbl.ExpiredAt, expire) if err != nil { return errorx.ErrDatabaseError.WithCause(err) } return nil } func (s *super) ListContents(ctx context.Context, filter *super_dto.SuperContentListFilter) (*requests.Pager, error) { tbl, q := models.ContentQuery.QueryContext(ctx) filter.Pagination.Format() total, err := q.Count() if err != nil { return nil, errorx.ErrDatabaseError.WithCause(err) } list, err := q.Offset(int(filter.Pagination.Offset())).Limit(int(filter.Pagination.Limit)).Order(tbl.ID.Desc()).Find() if err != nil { return nil, errorx.ErrDatabaseError.WithCause(err) } // Simplified DTO for list var data []any for _, c := range list { data = append(data, c) // TODO: Map to DTO } return &requests.Pager{ Pagination: filter.Pagination, Total: total, Items: data, }, nil } func (s *super) UpdateContentStatus(ctx context.Context, tenantID, contentID int64, form *super_dto.SuperTenantContentStatusUpdateForm) error { tbl, q := models.ContentQuery.QueryContext(ctx) _, err := q.Where(tbl.ID.Eq(contentID), tbl.TenantID.Eq(tenantID)).Update(tbl.Status, consts.ContentStatus(form.Status)) if err != nil { return errorx.ErrDatabaseError.WithCause(err) } return nil } func (s *super) ListOrders(ctx context.Context, filter *super_dto.SuperOrderListFilter) (*requests.Pager, error) { tbl, q := models.OrderQuery.QueryContext(ctx) filter.Pagination.Format() total, err := q.Count() if err != nil { return nil, errorx.ErrDatabaseError.WithCause(err) } list, err := q.Offset(int(filter.Pagination.Offset())).Limit(int(filter.Pagination.Limit)).Order(tbl.ID.Desc()).Find() if err != nil { return nil, errorx.ErrDatabaseError.WithCause(err) } // TODO: Map to DTO return &requests.Pager{ Pagination: filter.Pagination, Total: total, Items: list, }, nil } func (s *super) GetOrder(ctx context.Context, id int64) (*super_dto.SuperOrderDetail, error) { return &super_dto.SuperOrderDetail{}, nil } func (s *super) RefundOrder(ctx context.Context, id int64, form *super_dto.SuperOrderRefundForm) error { return nil } func (s *super) OrderStatistics(ctx context.Context) (*super_dto.OrderStatisticsResponse, error) { return &super_dto.OrderStatisticsResponse{}, nil } func (s *super) UserStatistics(ctx context.Context) ([]super_dto.UserStatistics, error) { return []super_dto.UserStatistics{}, nil } func (s *super) UserStatuses(ctx context.Context) ([]requests.KV, error) { return consts.UserStatusItems(), nil } func (s *super) TenantStatuses(ctx context.Context) ([]requests.KV, error) { return consts.TenantStatusItems(), nil } func (s *super) ListWithdrawals(ctx context.Context, filter *super_dto.SuperOrderListFilter) (*requests.Pager, error) { tbl, q := models.OrderQuery.QueryContext(ctx) q = q.Where(tbl.Type.Eq(consts.OrderTypeWithdrawal)) filter.Pagination.Format() total, err := q.Count() if err != nil { return nil, errorx.ErrDatabaseError.WithCause(err) } list, err := q.Offset(int(filter.Pagination.Offset())).Limit(int(filter.Pagination.Limit)).Order(tbl.ID.Desc()).Find() if err != nil { return nil, errorx.ErrDatabaseError.WithCause(err) } // TODO: Map to SuperOrderItem properly with Tenant/User lookup return &requests.Pager{ Pagination: filter.Pagination, Total: total, Items: list, }, nil } func (s *super) ApproveWithdrawal(ctx context.Context, id int64) error { o, err := models.OrderQuery.WithContext(ctx).Where(models.OrderQuery.ID.Eq(id)).First() if err != nil { return errorx.ErrRecordNotFound } if o.Status != consts.OrderStatusCreated { return errorx.ErrStatusConflict.WithMsg("订单状态不正确") } // Mark as Paid (Assumes external transfer done) _, err = models.OrderQuery.WithContext(ctx).Where(models.OrderQuery.ID.Eq(id)).Updates(&models.Order{ Status: consts.OrderStatusPaid, PaidAt: time.Now(), UpdatedAt: time.Now(), }) if err == nil && Audit != nil { Audit.Log(ctx, "approve_withdrawal", cast.ToString(id), "Approved withdrawal") } return err } func (s *super) RejectWithdrawal(ctx context.Context, id int64, reason string) error { err := models.Q.Transaction(func(tx *models.Query) error { o, err := tx.Order.WithContext(ctx).Where(tx.Order.ID.Eq(id)).First() if err != nil { return errorx.ErrRecordNotFound } if o.Status != consts.OrderStatusCreated { return errorx.ErrStatusConflict.WithMsg("订单状态不正确") } // Refund User Balance _, err = tx.User.WithContext(ctx).Where(tx.User.ID.Eq(o.UserID)).Update(tx.User.Balance, gorm.Expr("balance + ?", o.AmountPaid)) if err != nil { return err } // Update Order _, err = tx.Order.WithContext(ctx).Where(tx.Order.ID.Eq(id)).Updates(&models.Order{ Status: consts.OrderStatusFailed, // or Canceled RefundReason: reason, UpdatedAt: time.Now(), }) if err != nil { return err } // Create Ledger (Adjustment/Unfreeze) ledger := &models.TenantLedger{ TenantID: o.TenantID, UserID: o.UserID, OrderID: o.ID, Type: consts.TenantLedgerTypeAdjustment, Amount: o.AmountPaid, Remark: "提现拒绝返还: " + reason, OperatorUserID: 0, // System/Admin IdempotencyKey: uuid.NewString(), } if err := tx.TenantLedger.WithContext(ctx).Create(ledger); err != nil { return err } return nil }) if err == nil && Audit != nil { Audit.Log(ctx, "reject_withdrawal", cast.ToString(id), "Rejected: "+reason) } return err }