This commit is contained in:
2026-06-14 10:01:47 +08:00
parent 6fbf610277
commit ca39190ad7
11 changed files with 556 additions and 13 deletions
+80
View File
@@ -0,0 +1,80 @@
"""每日花园状态服务:连续天数 + 树渴了提醒"""
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