81 lines
3.0 KiB
Python
81 lines
3.0 KiB
Python
"""每日花园状态服务:连续天数 + 树渴了提醒"""
|
|
|
|
from datetime import date, datetime, timedelta
|
|
|
|
from sqlalchemy import select, or_
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from app.models.user_streak import UserStreak
|
|
from app.models.friendship_tree import FriendshipTree
|
|
from app.models.friend import Friend
|
|
from app.models.user import User
|
|
|
|
|
|
class DailyService:
|
|
def __init__(self, db: AsyncSession):
|
|
self.db = db
|
|
|
|
async def get_daily_status(self, user_id: str) -> dict:
|
|
"""获取/更新今日花园状态"""
|
|
today = date.today()
|
|
result = await self.db.execute(
|
|
select(UserStreak).where(UserStreak.user_id == user_id)
|
|
)
|
|
streak_row = result.scalars().first()
|
|
|
|
is_new_day_open = False
|
|
if not streak_row:
|
|
streak_row = UserStreak(
|
|
user_id=user_id, streak=1, last_open_date=today, total_days=1,
|
|
)
|
|
self.db.add(streak_row)
|
|
is_new_day_open = True
|
|
elif streak_row.last_open_date != today:
|
|
# 计算连续天数
|
|
if streak_row.last_open_date == today - timedelta(days=1):
|
|
streak_row.streak += 1 # 连续
|
|
else:
|
|
streak_row.streak = 1 # 断了,重新计数
|
|
streak_row.last_open_date = today
|
|
streak_row.total_days += 1
|
|
is_new_day_open = True
|
|
await self.db.flush()
|
|
|
|
# 找口渴的树(24 小时没浇水的)
|
|
thirsty_trees = await self._get_thirsty_trees(user_id)
|
|
|
|
return {
|
|
"streak": streak_row.streak,
|
|
"total_days": streak_row.total_days,
|
|
"is_new_day_open": is_new_day_open,
|
|
"thirsty_count": len(thirsty_trees),
|
|
"thirsty_trees": thirsty_trees[:3], # 最多提示 3 棵
|
|
}
|
|
|
|
async def _get_thirsty_trees(self, user_id: str) -> list[dict]:
|
|
"""获取口渴的好友之树(24h 未浇水)"""
|
|
cutoff = datetime.utcnow() - timedelta(hours=24)
|
|
# 查该用户参与的所有树
|
|
result = await self.db.execute(
|
|
select(FriendshipTree).where(
|
|
or_(
|
|
FriendshipTree.user_a_id == user_id,
|
|
FriendshipTree.user_b_id == user_id,
|
|
)
|
|
)
|
|
)
|
|
thirsty = []
|
|
for tree in result.scalars().all():
|
|
if tree.last_watered_at is None or tree.last_watered_at < cutoff:
|
|
friend_id = tree.user_b_id if tree.user_a_id == user_id else tree.user_a_id
|
|
# 取好友信息
|
|
fr = await self.db.execute(select(User).where(User.id == friend_id))
|
|
friend = fr.scalars().first()
|
|
if friend:
|
|
thirsty.append({
|
|
"friend_id": friend_id,
|
|
"friend_name": friend.nickname or friend.username,
|
|
"hours_since": int((datetime.utcnow() - (tree.last_watered_at or tree.created_at)).total_seconds() / 3600),
|
|
})
|
|
return thirsty
|