220 lines
5.9 KiB
Go
220 lines
5.9 KiB
Go
package repository
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
"github.com/Wei-Shaw/sub2api/internal/pkg/pagination"
|
|
"github.com/Wei-Shaw/sub2api/internal/service"
|
|
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
type redeemCodeRepository struct {
|
|
db *gorm.DB
|
|
}
|
|
|
|
func NewRedeemCodeRepository(db *gorm.DB) service.RedeemCodeRepository {
|
|
return &redeemCodeRepository{db: db}
|
|
}
|
|
|
|
func (r *redeemCodeRepository) Create(ctx context.Context, code *service.RedeemCode) error {
|
|
m := redeemCodeModelFromService(code)
|
|
err := r.db.WithContext(ctx).Create(m).Error
|
|
if err == nil {
|
|
applyRedeemCodeModelToService(code, m)
|
|
}
|
|
return err
|
|
}
|
|
|
|
func (r *redeemCodeRepository) CreateBatch(ctx context.Context, codes []service.RedeemCode) error {
|
|
if len(codes) == 0 {
|
|
return nil
|
|
}
|
|
models := make([]redeemCodeModel, 0, len(codes))
|
|
for i := range codes {
|
|
m := redeemCodeModelFromService(&codes[i])
|
|
if m != nil {
|
|
models = append(models, *m)
|
|
}
|
|
}
|
|
return r.db.WithContext(ctx).Create(&models).Error
|
|
}
|
|
|
|
func (r *redeemCodeRepository) GetByID(ctx context.Context, id int64) (*service.RedeemCode, error) {
|
|
var m redeemCodeModel
|
|
err := r.db.WithContext(ctx).First(&m, id).Error
|
|
if err != nil {
|
|
return nil, translatePersistenceError(err, service.ErrRedeemCodeNotFound, nil)
|
|
}
|
|
return redeemCodeModelToService(&m), nil
|
|
}
|
|
|
|
func (r *redeemCodeRepository) GetByCode(ctx context.Context, code string) (*service.RedeemCode, error) {
|
|
var m redeemCodeModel
|
|
err := r.db.WithContext(ctx).Where("code = ?", code).First(&m).Error
|
|
if err != nil {
|
|
return nil, translatePersistenceError(err, service.ErrRedeemCodeNotFound, nil)
|
|
}
|
|
return redeemCodeModelToService(&m), nil
|
|
}
|
|
|
|
func (r *redeemCodeRepository) Delete(ctx context.Context, id int64) error {
|
|
return r.db.WithContext(ctx).Delete(&redeemCodeModel{}, id).Error
|
|
}
|
|
|
|
func (r *redeemCodeRepository) List(ctx context.Context, params pagination.PaginationParams) ([]service.RedeemCode, *pagination.PaginationResult, error) {
|
|
return r.ListWithFilters(ctx, params, "", "", "")
|
|
}
|
|
|
|
func (r *redeemCodeRepository) ListWithFilters(ctx context.Context, params pagination.PaginationParams, codeType, status, search string) ([]service.RedeemCode, *pagination.PaginationResult, error) {
|
|
var codes []redeemCodeModel
|
|
var total int64
|
|
|
|
db := r.db.WithContext(ctx).Model(&redeemCodeModel{})
|
|
|
|
if codeType != "" {
|
|
db = db.Where("type = ?", codeType)
|
|
}
|
|
if status != "" {
|
|
db = db.Where("status = ?", status)
|
|
}
|
|
if search != "" {
|
|
searchPattern := "%" + search + "%"
|
|
db = db.Where("code ILIKE ?", searchPattern)
|
|
}
|
|
|
|
if err := db.Count(&total).Error; err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
if err := db.Preload("User").Preload("Group").Offset(params.Offset()).Limit(params.Limit()).Order("id DESC").Find(&codes).Error; err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
outCodes := make([]service.RedeemCode, 0, len(codes))
|
|
for i := range codes {
|
|
outCodes = append(outCodes, *redeemCodeModelToService(&codes[i]))
|
|
}
|
|
|
|
return outCodes, paginationResultFromTotal(total, params), nil
|
|
}
|
|
|
|
func (r *redeemCodeRepository) Update(ctx context.Context, code *service.RedeemCode) error {
|
|
m := redeemCodeModelFromService(code)
|
|
err := r.db.WithContext(ctx).Save(m).Error
|
|
if err == nil {
|
|
applyRedeemCodeModelToService(code, m)
|
|
}
|
|
return err
|
|
}
|
|
|
|
func (r *redeemCodeRepository) Use(ctx context.Context, id, userID int64) error {
|
|
now := time.Now()
|
|
result := r.db.WithContext(ctx).Model(&redeemCodeModel{}).
|
|
Where("id = ? AND status = ?", id, service.StatusUnused).
|
|
Updates(map[string]any{
|
|
"status": service.StatusUsed,
|
|
"used_by": userID,
|
|
"used_at": now,
|
|
})
|
|
if result.Error != nil {
|
|
return result.Error
|
|
}
|
|
if result.RowsAffected == 0 {
|
|
return service.ErrRedeemCodeUsed.WithCause(gorm.ErrRecordNotFound)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (r *redeemCodeRepository) ListByUser(ctx context.Context, userID int64, limit int) ([]service.RedeemCode, error) {
|
|
if limit <= 0 {
|
|
limit = 10
|
|
}
|
|
|
|
var codes []redeemCodeModel
|
|
err := r.db.WithContext(ctx).
|
|
Preload("Group").
|
|
Where("used_by = ?", userID).
|
|
Order("used_at DESC").
|
|
Limit(limit).
|
|
Find(&codes).Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
outCodes := make([]service.RedeemCode, 0, len(codes))
|
|
for i := range codes {
|
|
outCodes = append(outCodes, *redeemCodeModelToService(&codes[i]))
|
|
}
|
|
return outCodes, nil
|
|
}
|
|
|
|
type redeemCodeModel struct {
|
|
ID int64 `gorm:"primaryKey"`
|
|
Code string `gorm:"uniqueIndex;size:32;not null"`
|
|
Type string `gorm:"size:20;default:balance;not null"`
|
|
Value float64 `gorm:"type:decimal(20,8);not null"`
|
|
Status string `gorm:"size:20;default:unused;not null"`
|
|
UsedBy *int64 `gorm:"index"`
|
|
UsedAt *time.Time
|
|
Notes string `gorm:"type:text"`
|
|
CreatedAt time.Time `gorm:"not null"`
|
|
|
|
GroupID *int64 `gorm:"index"`
|
|
ValidityDays int `gorm:"default:30"`
|
|
|
|
User *userModel `gorm:"foreignKey:UsedBy"`
|
|
Group *groupModel `gorm:"foreignKey:GroupID"`
|
|
}
|
|
|
|
func (redeemCodeModel) TableName() string { return "redeem_codes" }
|
|
|
|
func redeemCodeModelToService(m *redeemCodeModel) *service.RedeemCode {
|
|
if m == nil {
|
|
return nil
|
|
}
|
|
return &service.RedeemCode{
|
|
ID: m.ID,
|
|
Code: m.Code,
|
|
Type: m.Type,
|
|
Value: m.Value,
|
|
Status: m.Status,
|
|
UsedBy: m.UsedBy,
|
|
UsedAt: m.UsedAt,
|
|
Notes: m.Notes,
|
|
CreatedAt: m.CreatedAt,
|
|
GroupID: m.GroupID,
|
|
ValidityDays: m.ValidityDays,
|
|
User: userModelToService(m.User),
|
|
Group: groupModelToService(m.Group),
|
|
}
|
|
}
|
|
|
|
func redeemCodeModelFromService(r *service.RedeemCode) *redeemCodeModel {
|
|
if r == nil {
|
|
return nil
|
|
}
|
|
return &redeemCodeModel{
|
|
ID: r.ID,
|
|
Code: r.Code,
|
|
Type: r.Type,
|
|
Value: r.Value,
|
|
Status: r.Status,
|
|
UsedBy: r.UsedBy,
|
|
UsedAt: r.UsedAt,
|
|
Notes: r.Notes,
|
|
CreatedAt: r.CreatedAt,
|
|
GroupID: r.GroupID,
|
|
ValidityDays: r.ValidityDays,
|
|
}
|
|
}
|
|
|
|
func applyRedeemCodeModelToService(code *service.RedeemCode, m *redeemCodeModel) {
|
|
if code == nil || m == nil {
|
|
return
|
|
}
|
|
code.ID = m.ID
|
|
code.CreatedAt = m.CreatedAt
|
|
}
|