173 lines
4.2 KiB
Go
173 lines
4.2 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"time"
|
|
|
|
"git.lzcwy.cn/zhengchiliu/zt-closet-backend/global"
|
|
"git.lzcwy.cn/zhengchiliu/zt-closet-backend/internal/dto"
|
|
"git.lzcwy.cn/zhengchiliu/zt-closet-backend/internal/mysqldao"
|
|
"git.lzcwy.cn/zhengchiliu/zt-closet-backend/internal/mysqlmodel"
|
|
"git.lzcwy.cn/zhengchiliu/zt-closet-backend/internal/redisdao"
|
|
"git.lzcwy.cn/zhengchiliu/zt-closet-backend/pkg/errcode"
|
|
"git.lzcwy.cn/zhengchiliu/zt-closet-backend/pkg/tokenhub"
|
|
)
|
|
|
|
// HoroscopeService 今日运势业务服务。
|
|
type HoroscopeService struct {
|
|
horoscopeDao *mysqldao.HoroscopeDao
|
|
userDao *mysqldao.UserDao
|
|
}
|
|
|
|
// NewHoroscopeService 创建运势服务。
|
|
func NewHoroscopeService() *HoroscopeService {
|
|
return &HoroscopeService{
|
|
horoscopeDao: mysqldao.NewHoroscopeDao(),
|
|
userDao: mysqldao.NewUserDao(),
|
|
}
|
|
}
|
|
|
|
// GetToday 获取当前用户今日运势:由生日推算星座,Redis 优先、未命中回落 MySQL 并回填缓存。
|
|
func (s *HoroscopeService) GetToday(ctx context.Context, userID int64) (*dto.HoroscopeResponse, *errcode.Error) {
|
|
ctx, span := global.Tracer.Start(ctx, "svc.HoroscopeGetToday")
|
|
defer span.End()
|
|
|
|
user, err := s.userDao.UserGetByID(ctx, userID)
|
|
if err != nil {
|
|
if isNotFound(err) {
|
|
return nil, errcode.UserNotFound
|
|
}
|
|
return nil, errcode.DatabaseError
|
|
}
|
|
if user.BirthDay == 0 {
|
|
return nil, errcode.InvalidParams.WithDetails("请先完善生日信息")
|
|
}
|
|
|
|
zodiac := zodiacByUnix(user.BirthDay)
|
|
statDate := todayStatDate()
|
|
|
|
// 1. Redis 优先。
|
|
if data, cerr := redisdao.HoroscopeGet(ctx, zodiac, statDate); cerr == nil && len(data) > 0 {
|
|
var response dto.HoroscopeResponse
|
|
if json.Unmarshal(data, &response) == nil {
|
|
return &response, nil
|
|
}
|
|
}
|
|
|
|
// 2. 回落 MySQL。
|
|
h, derr := s.horoscopeDao.HoroscopeGetByZodiacAndDate(ctx, zodiac, statDate)
|
|
if derr != nil {
|
|
if isNotFound(derr) {
|
|
// 今日运势尚未生成,异步触发兜底生成,本次返回未生成。
|
|
tokenhub.EnsureTodayFortunes()
|
|
return nil, errcode.HoroscopeNotFound
|
|
}
|
|
return nil, errcode.DatabaseError
|
|
}
|
|
|
|
response := buildHoroscopeResponse(h)
|
|
|
|
// 回填缓存,TTL 至次日凌晨。
|
|
if data, merr := json.Marshal(response); merr == nil {
|
|
_ = redisdao.HoroscopeSet(ctx, zodiac, statDate, data, ttlUntilNextMidnight())
|
|
}
|
|
|
|
return &response, nil
|
|
}
|
|
|
|
// buildHoroscopeResponse 将运势模型映射为响应。
|
|
func buildHoroscopeResponse(h *mysqlmodel.Horoscope) dto.HoroscopeResponse {
|
|
return dto.HoroscopeResponse{
|
|
StatDate: h.StatDate,
|
|
Zodiac: h.Zodiac,
|
|
OverallScore: h.OverallScore,
|
|
LoveScore: h.LoveScore,
|
|
CareerScore: h.CareerScore,
|
|
WealthScore: h.WealthScore,
|
|
DailyText: h.DailyText,
|
|
LuckyColor: h.LuckyColor,
|
|
LuckyNumber: h.LuckyNumber,
|
|
}
|
|
}
|
|
|
|
// ttlUntilNextMidnight 计算到次日 0 点的时长,用于运势缓存过期。
|
|
func ttlUntilNextMidnight() time.Duration {
|
|
now := time.Now()
|
|
next := time.Date(now.Year(), now.Month(), now.Day()+1, 0, 0, 0, 0, now.Location())
|
|
return next.Sub(now)
|
|
}
|
|
|
|
// zodiacByUnix 根据生日(秒级 unix 时间戳)计算星座(中文名,与运势数据对齐)。
|
|
func zodiacByUnix(birthUnix int64) string {
|
|
t := time.Unix(birthUnix, 0)
|
|
return zodiacByMonthDay(int(t.Month()), t.Day())
|
|
}
|
|
|
|
// zodiacByMonthDay 根据月日返回星座中文名。
|
|
func zodiacByMonthDay(month, day int) string {
|
|
switch month {
|
|
case 1:
|
|
if day <= 19 {
|
|
return "摩羯座"
|
|
}
|
|
return "水瓶座"
|
|
case 2:
|
|
if day <= 18 {
|
|
return "水瓶座"
|
|
}
|
|
return "双鱼座"
|
|
case 3:
|
|
if day <= 20 {
|
|
return "双鱼座"
|
|
}
|
|
return "白羊座"
|
|
case 4:
|
|
if day <= 19 {
|
|
return "白羊座"
|
|
}
|
|
return "金牛座"
|
|
case 5:
|
|
if day <= 20 {
|
|
return "金牛座"
|
|
}
|
|
return "双子座"
|
|
case 6:
|
|
if day <= 21 {
|
|
return "双子座"
|
|
}
|
|
return "巨蟹座"
|
|
case 7:
|
|
if day <= 22 {
|
|
return "巨蟹座"
|
|
}
|
|
return "狮子座"
|
|
case 8:
|
|
if day <= 22 {
|
|
return "狮子座"
|
|
}
|
|
return "处女座"
|
|
case 9:
|
|
if day <= 22 {
|
|
return "处女座"
|
|
}
|
|
return "天秤座"
|
|
case 10:
|
|
if day <= 23 {
|
|
return "天秤座"
|
|
}
|
|
return "天蝎座"
|
|
case 11:
|
|
if day <= 22 {
|
|
return "天蝎座"
|
|
}
|
|
return "射手座"
|
|
case 12:
|
|
if day <= 21 {
|
|
return "射手座"
|
|
}
|
|
return "摩羯座"
|
|
}
|
|
return "摩羯座"
|
|
}
|