feat: add characters
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"net/url"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -36,6 +37,8 @@ type Service struct {
|
||||
ad AdVerifier
|
||||
iap IAPVerifier
|
||||
|
||||
publicURL string // مبدأِ عمومیِ سرور برای ساختِ URL کاملِ تصاویر
|
||||
|
||||
mu sync.RWMutex // محافظ کشِ کاتالوگ
|
||||
cat Catalog // کشِ کاتالوگ خواندهشده از DB
|
||||
}
|
||||
@@ -44,6 +47,18 @@ func New(db *sql.DB, ad AdVerifier, iap IAPVerifier) *Service {
|
||||
return &Service{db: db, ad: ad, iap: iap}
|
||||
}
|
||||
|
||||
// SetPublicURL مبدأِ عمومیِ سرور را تنظیم میکند (برای URLهای کاملِ تصاویر).
|
||||
func (s *Service) SetPublicURL(u string) { s.publicURL = strings.TrimRight(u, "/") }
|
||||
|
||||
// imgURL یک URLِ کاملِ URL-safe برای فایلی در پوشهی [dir] میسازد ("" اگر نام خالی
|
||||
// باشد). نامِ فایل encode میشود تا فاصله/کاراکترِ خاص مشکلی ایجاد نکند.
|
||||
func (s *Service) imgURL(dir, name string) string {
|
||||
if name == "" {
|
||||
return ""
|
||||
}
|
||||
return s.publicURL + "/" + dir + "/" + url.PathEscape(name)
|
||||
}
|
||||
|
||||
// Profile وضعیت اقتصادی کاربر.
|
||||
type Profile struct {
|
||||
Coins int64 `json:"coins"`
|
||||
@@ -58,6 +73,9 @@ type Profile struct {
|
||||
SelectedCard string `json:"selected_card"`
|
||||
SelectedCarpet string `json:"selected_carpet"`
|
||||
SelectedFrame string `json:"selected_frame"`
|
||||
SelectedAvatar string `json:"selected_avatar"` // شناسهی شخصیتِ انتخابی (برای وضعیتِ انتخاب در فروشگاه)
|
||||
SelectedAvatarImg string `json:"selected_avatar_img"` // URL کاملِ تصویرِ شخصیت — خالی ⇒ آواتارِ تصادفی
|
||||
SelectedCarpetImg string `json:"selected_carpet_img"` // URL کاملِ تصویرِ فرش — خالی ⇒ میزِ کلاسیک
|
||||
RankPoints int64 `json:"rank_points"`
|
||||
RankTier string `json:"rank_tier"` // bronze..king
|
||||
RankIndex int `json:"rank_index"` // ۰..۴
|
||||
@@ -71,10 +89,12 @@ type Profile struct {
|
||||
// GetProfile وضعیت اقتصادی کاربر را میخواند.
|
||||
func (s *Service) GetProfile(ctx context.Context, userID int64) (*Profile, error) {
|
||||
var p Profile
|
||||
var vipUntil, avatar, lastDaily sql.NullString
|
||||
var vipUntil, avatar, lastDaily, avatarImg sql.NullString
|
||||
err := s.db.QueryRowContext(ctx,
|
||||
`SELECT coins, tickets, xp, trophies, vip_until, selected_card, selected_carpet, selected_frame, rank_points, avatar, daily_streak, best_streak, last_daily_at FROM users WHERE id = ?`, userID).
|
||||
Scan(&p.Coins, &p.Tickets, &p.XP, &p.Trophies, &vipUntil, &p.SelectedCard, &p.SelectedCarpet, &p.SelectedFrame, &p.RankPoints, &avatar, &p.DailyStreak, &p.BestStreak, &lastDaily)
|
||||
`SELECT coins, tickets, xp, trophies, vip_until, selected_card, selected_carpet, selected_frame, selected_avatar,
|
||||
(SELECT id || '.' || img_ext FROM avatars WHERE id = users.selected_avatar AND enabled = 1),
|
||||
rank_points, avatar, daily_streak, best_streak, last_daily_at FROM users WHERE id = ?`, userID).
|
||||
Scan(&p.Coins, &p.Tickets, &p.XP, &p.Trophies, &vipUntil, &p.SelectedCard, &p.SelectedCarpet, &p.SelectedFrame, &p.SelectedAvatar, &avatarImg, &p.RankPoints, &avatar, &p.DailyStreak, &p.BestStreak, &lastDaily)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
@@ -82,6 +102,11 @@ func (s *Service) GetProfile(ctx context.Context, userID int64) (*Profile, error
|
||||
return nil, err
|
||||
}
|
||||
p.Avatar = avatar.String
|
||||
p.SelectedAvatarImg = s.imgURL("avatars", avatarImg.String)
|
||||
// فرش: 'classic' تصویری ندارد (میزِ ترسیمی)؛ بقیه از /carpets/<id>.jpg.
|
||||
if p.SelectedCarpet != "" && p.SelectedCarpet != "classic" {
|
||||
p.SelectedCarpetImg = s.imgURL("carpets", p.SelectedCarpet+".jpg")
|
||||
}
|
||||
p.Level, p.XPInto, p.XPNeed = LevelInfo(p.XP)
|
||||
p.RankTier, p.RankIndex = RankTier(p.RankPoints)
|
||||
if vipUntil.Valid {
|
||||
|
||||
Reference in New Issue
Block a user