243 lines
7.2 KiB
Go
243 lines
7.2 KiB
Go
package model
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"time"
|
|
|
|
"gorm.io/gorm"
|
|
|
|
"spark-server/internal/module/client/entity"
|
|
)
|
|
|
|
type Repo struct {
|
|
db *gorm.DB
|
|
}
|
|
|
|
func NewRepo(db *gorm.DB) *Repo {
|
|
if db == nil {
|
|
return nil
|
|
}
|
|
return &Repo{db: db}
|
|
}
|
|
|
|
type legalRow struct {
|
|
DocKey string `gorm:"column:doc_key;primaryKey"`
|
|
Lang string `gorm:"column:lang;primaryKey"`
|
|
Title string `gorm:"column:title"`
|
|
Content string `gorm:"column:content"`
|
|
Version string `gorm:"column:version"`
|
|
UpdatedAt time.Time `gorm:"column:updated_at"`
|
|
}
|
|
|
|
func (legalRow) TableName() string { return "t_legal_documents" }
|
|
|
|
type feedbackRow struct {
|
|
ID int64 `gorm:"column:id;primaryKey;autoIncrement"`
|
|
UserID *int64 `gorm:"column:user_id"`
|
|
AuthType string `gorm:"column:auth_type"`
|
|
Email string `gorm:"column:email"`
|
|
Contact string `gorm:"column:contact"`
|
|
Content string `gorm:"column:content"`
|
|
AppVersion string `gorm:"column:app_version"`
|
|
Language string `gorm:"column:language"`
|
|
Status string `gorm:"column:status"`
|
|
CreatedAt time.Time `gorm:"column:created_at"`
|
|
}
|
|
|
|
func (feedbackRow) TableName() string { return "t_feedback" }
|
|
|
|
type planRow struct {
|
|
PlanID string `gorm:"column:plan_id;primaryKey"`
|
|
Lang string `gorm:"column:lang;primaryKey"`
|
|
Title string `gorm:"column:title"`
|
|
Subtitle string `gorm:"column:subtitle"`
|
|
PriceDisplay string `gorm:"column:price_display"`
|
|
PriceCents int `gorm:"column:price_cents"`
|
|
Currency string `gorm:"column:currency"`
|
|
PeriodDays int `gorm:"column:period_days"`
|
|
Badge string `gorm:"column:badge"`
|
|
SortOrder int `gorm:"column:sort_order"`
|
|
Enabled bool `gorm:"column:enabled"`
|
|
ProductID string `gorm:"column:product_id"`
|
|
UpdatedAt time.Time `gorm:"column:updated_at"`
|
|
}
|
|
|
|
func (planRow) TableName() string { return "t_subscription_plans" }
|
|
|
|
type passwordRow struct {
|
|
UserID int64 `gorm:"column:user_id;primaryKey"`
|
|
PasswordHash string `gorm:"column:password_hash"`
|
|
UpdatedAt time.Time `gorm:"column:updated_at"`
|
|
}
|
|
|
|
func (passwordRow) TableName() string { return "t_user_passwords" }
|
|
|
|
func (r *Repo) GetLegal(ctx context.Context, lang string) (privacy, terms *entity.LegalDocument, err error) {
|
|
var rows []legalRow
|
|
if err = r.db.WithContext(ctx).Where("lang = ?", lang).Find(&rows).Error; err != nil {
|
|
return nil, nil, err
|
|
}
|
|
for i := range rows {
|
|
d := &entity.LegalDocument{
|
|
Key: rows[i].DocKey, Lang: rows[i].Lang, Title: rows[i].Title,
|
|
Content: rows[i].Content, Version: rows[i].Version,
|
|
}
|
|
switch rows[i].DocKey {
|
|
case entity.DocPrivacy:
|
|
privacy = d
|
|
case entity.DocTerms:
|
|
terms = d
|
|
}
|
|
}
|
|
return privacy, terms, nil
|
|
}
|
|
|
|
func (r *Repo) UpsertLegal(ctx context.Context, d *entity.LegalDocument) error {
|
|
if d == nil {
|
|
return errors.New("client: nil legal document")
|
|
}
|
|
row := legalRow{
|
|
DocKey: d.Key, Lang: d.Lang, Title: d.Title, Content: d.Content,
|
|
Version: d.Version, UpdatedAt: time.Now(),
|
|
}
|
|
if row.Version == "" {
|
|
row.Version = "1.0"
|
|
}
|
|
return r.db.WithContext(ctx).Save(&row).Error
|
|
}
|
|
|
|
func (r *Repo) InsertFeedback(ctx context.Context, f *entity.Feedback) error {
|
|
status := f.Status
|
|
if status == "" {
|
|
status = "open"
|
|
}
|
|
authType := f.AuthType
|
|
if authType == "" {
|
|
authType = entity.FeedbackAuthGuest
|
|
}
|
|
row := feedbackRow{
|
|
UserID: f.UserID, AuthType: authType, Email: f.Email, Contact: f.Contact,
|
|
Content: f.Content, AppVersion: f.AppVersion, Language: f.Language,
|
|
Status: status, CreatedAt: f.CreatedAt,
|
|
}
|
|
if err := r.db.WithContext(ctx).Create(&row).Error; err != nil {
|
|
return err
|
|
}
|
|
f.ID = row.ID
|
|
f.Status = status
|
|
f.AuthType = authType
|
|
return nil
|
|
}
|
|
|
|
func (r *Repo) ListFeedback(ctx context.Context, page, size int, authType string) ([]entity.Feedback, int64, error) {
|
|
if page < 0 {
|
|
page = 0
|
|
}
|
|
if size <= 0 || size > 200 {
|
|
size = 50
|
|
}
|
|
q := r.db.WithContext(ctx).Model(&feedbackRow{})
|
|
if authType == entity.FeedbackAuthLogin || authType == entity.FeedbackAuthGuest {
|
|
q = q.Where("auth_type = ?", authType)
|
|
}
|
|
var total int64
|
|
if err := q.Count(&total).Error; err != nil {
|
|
return nil, 0, err
|
|
}
|
|
var rows []feedbackRow
|
|
listQ := r.db.WithContext(ctx).Order("id DESC").Offset(page * size).Limit(size)
|
|
if authType == entity.FeedbackAuthLogin || authType == entity.FeedbackAuthGuest {
|
|
listQ = listQ.Where("auth_type = ?", authType)
|
|
}
|
|
err := listQ.Find(&rows).Error
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
out := make([]entity.Feedback, 0, len(rows))
|
|
for i := range rows {
|
|
row := &rows[i]
|
|
out = append(out, entity.Feedback{
|
|
ID: row.ID, UserID: row.UserID, AuthType: row.AuthType, Email: row.Email,
|
|
Contact: row.Contact, Content: row.Content, AppVersion: row.AppVersion,
|
|
Language: row.Language, Status: row.Status, CreatedAt: row.CreatedAt,
|
|
})
|
|
}
|
|
return out, total, nil
|
|
}
|
|
|
|
func (r *Repo) UpdateFeedbackStatus(ctx context.Context, id int64, status string) error {
|
|
return r.db.WithContext(ctx).Model(&feedbackRow{}).Where("id = ?", id).Update("status", status).Error
|
|
}
|
|
|
|
func (r *Repo) ListPlans(ctx context.Context, lang string) ([]entity.SubscriptionPlan, error) {
|
|
var rows []planRow
|
|
err := r.db.WithContext(ctx).
|
|
Where("lang = ? AND enabled = 1", lang).
|
|
Order("sort_order ASC, plan_id ASC").
|
|
Find(&rows).Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return mapPlans(rows), nil
|
|
}
|
|
|
|
func (r *Repo) ListPlansAdmin(ctx context.Context, lang string) ([]entity.SubscriptionPlan, error) {
|
|
q := r.db.WithContext(ctx).Order("lang ASC, sort_order ASC, plan_id ASC")
|
|
if lang != "" {
|
|
q = q.Where("lang = ?", lang)
|
|
}
|
|
var rows []planRow
|
|
if err := q.Find(&rows).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
return mapPlans(rows), nil
|
|
}
|
|
|
|
func mapPlans(rows []planRow) []entity.SubscriptionPlan {
|
|
out := make([]entity.SubscriptionPlan, 0, len(rows))
|
|
for i := range rows {
|
|
row := &rows[i]
|
|
out = append(out, entity.SubscriptionPlan{
|
|
PlanID: row.PlanID, Lang: row.Lang, Title: row.Title, Subtitle: row.Subtitle,
|
|
PriceDisplay: row.PriceDisplay, PriceCents: row.PriceCents, Currency: row.Currency,
|
|
PeriodDays: row.PeriodDays, Badge: row.Badge, SortOrder: row.SortOrder,
|
|
Enabled: row.Enabled, ProductID: row.ProductID,
|
|
})
|
|
}
|
|
return out
|
|
}
|
|
|
|
func (r *Repo) UpsertPlan(ctx context.Context, p *entity.SubscriptionPlan) error {
|
|
if p == nil {
|
|
return errors.New("client: nil subscription plan")
|
|
}
|
|
row := planRow{
|
|
PlanID: p.PlanID, Lang: p.Lang, Title: p.Title, Subtitle: p.Subtitle,
|
|
PriceDisplay: p.PriceDisplay, PriceCents: p.PriceCents, Currency: p.Currency,
|
|
PeriodDays: p.PeriodDays, Badge: p.Badge, SortOrder: p.SortOrder,
|
|
Enabled: p.Enabled, ProductID: p.ProductID, UpdatedAt: time.Now(),
|
|
}
|
|
if row.Currency == "" {
|
|
row.Currency = "USD"
|
|
}
|
|
return r.db.WithContext(ctx).Save(&row).Error
|
|
}
|
|
|
|
func (r *Repo) GetPasswordHash(ctx context.Context, userID int64) (string, error) {
|
|
var row passwordRow
|
|
err := r.db.WithContext(ctx).Where("user_id = ?", userID).Take(&row).Error
|
|
if err == gorm.ErrRecordNotFound {
|
|
return "", nil
|
|
}
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return row.PasswordHash, nil
|
|
}
|
|
|
|
func (r *Repo) SetPasswordHash(ctx context.Context, userID int64, hash string) error {
|
|
row := passwordRow{UserID: userID, PasswordHash: hash, UpdatedAt: time.Now()}
|
|
return r.db.WithContext(ctx).Save(&row).Error
|
|
}
|