refactor(project): use unified utcnow
This commit is contained in:
13
app/auth.py
13
app/auth.py
@@ -1,6 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import UTC, datetime, timedelta
|
||||
from datetime import timedelta
|
||||
import hashlib
|
||||
import re
|
||||
import secrets
|
||||
@@ -12,6 +12,7 @@ from app.database import (
|
||||
User,
|
||||
)
|
||||
from app.log import logger
|
||||
from app.utils import utcnow
|
||||
|
||||
import bcrypt
|
||||
from jose import JWTError, jwt
|
||||
@@ -150,9 +151,9 @@ def create_access_token(data: dict, expires_delta: timedelta | None = None) -> s
|
||||
"""创建访问令牌"""
|
||||
to_encode = data.copy()
|
||||
if expires_delta:
|
||||
expire = datetime.now(UTC) + expires_delta
|
||||
expire = utcnow() + expires_delta
|
||||
else:
|
||||
expire = datetime.now(UTC) + timedelta(minutes=settings.access_token_expire_minutes)
|
||||
expire = utcnow() + timedelta(minutes=settings.access_token_expire_minutes)
|
||||
|
||||
to_encode.update({"exp": expire, "random": secrets.token_hex(16)})
|
||||
encoded_jwt = jwt.encode(to_encode, settings.secret_key, algorithm=settings.algorithm)
|
||||
@@ -206,7 +207,7 @@ async def store_token(
|
||||
expires_in: int,
|
||||
) -> OAuthToken:
|
||||
"""存储令牌到数据库"""
|
||||
expires_at = datetime.utcnow() + timedelta(seconds=expires_in)
|
||||
expires_at = utcnow() + timedelta(seconds=expires_in)
|
||||
|
||||
# 删除用户的旧令牌
|
||||
statement = select(OAuthToken).where(OAuthToken.user_id == user_id, OAuthToken.client_id == client_id)
|
||||
@@ -238,7 +239,7 @@ async def get_token_by_access_token(db: AsyncSession, access_token: str) -> OAut
|
||||
"""根据访问令牌获取令牌记录"""
|
||||
statement = select(OAuthToken).where(
|
||||
OAuthToken.access_token == access_token,
|
||||
OAuthToken.expires_at > datetime.utcnow(),
|
||||
OAuthToken.expires_at > utcnow(),
|
||||
)
|
||||
return (await db.exec(statement)).first()
|
||||
|
||||
@@ -247,7 +248,7 @@ async def get_token_by_refresh_token(db: AsyncSession, refresh_token: str) -> OA
|
||||
"""根据刷新令牌获取令牌记录"""
|
||||
statement = select(OAuthToken).where(
|
||||
OAuthToken.refresh_token == refresh_token,
|
||||
OAuthToken.expires_at > datetime.utcnow(),
|
||||
OAuthToken.expires_at > utcnow(),
|
||||
)
|
||||
return (await db.exec(statement)).first()
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
from datetime import UTC, datetime
|
||||
from datetime import datetime
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from app.config import settings
|
||||
from app.models.achievement import MEDALS, Achievement
|
||||
from app.models.model import UTCBaseModel
|
||||
from app.models.notification import UserAchievementUnlock
|
||||
from app.utils import utcnow
|
||||
|
||||
from .events import Event, EventType
|
||||
|
||||
@@ -28,7 +29,7 @@ if TYPE_CHECKING:
|
||||
|
||||
class UserAchievementBase(SQLModel, UTCBaseModel):
|
||||
achievement_id: int
|
||||
achieved_at: datetime = Field(default=datetime.now(UTC), sa_column=Column(DateTime(timezone=True)))
|
||||
achieved_at: datetime = Field(default_factory=utcnow, sa_column=Column(DateTime(timezone=True)))
|
||||
|
||||
|
||||
class UserAchievement(UserAchievementBase, table=True):
|
||||
@@ -56,7 +57,7 @@ async def process_achievements(session: AsyncSession, redis: Redis, score_id: in
|
||||
).all()
|
||||
not_achieved = {k: v for k, v in MEDALS.items() if k.id not in achieved}
|
||||
result: list[Achievement] = []
|
||||
now = datetime.now(UTC)
|
||||
now = utcnow()
|
||||
for k, v in not_achieved.items():
|
||||
if await v(session, score, score.beatmap):
|
||||
result.append(k)
|
||||
|
||||
@@ -3,6 +3,7 @@ import secrets
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from app.models.model import UTCBaseModel
|
||||
from app.utils import utcnow
|
||||
|
||||
from sqlalchemy import Column, DateTime
|
||||
from sqlmodel import (
|
||||
@@ -30,7 +31,7 @@ class OAuthToken(UTCBaseModel, SQLModel, table=True):
|
||||
token_type: str = Field(default="Bearer", max_length=20)
|
||||
scope: str = Field(default="*", max_length=100)
|
||||
expires_at: datetime = Field(sa_column=Column(DateTime))
|
||||
created_at: datetime = Field(default_factory=datetime.utcnow, sa_column=Column(DateTime))
|
||||
created_at: datetime = Field(default_factory=utcnow, sa_column=Column(DateTime))
|
||||
|
||||
user: "User" = Relationship()
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from datetime import UTC, datetime
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from app.database.events import Event, EventType
|
||||
from app.utils import utcnow
|
||||
|
||||
from pydantic import BaseModel
|
||||
from sqlalchemy.ext.asyncio import AsyncAttrs
|
||||
@@ -71,7 +71,7 @@ async def process_beatmap_playcount(session: AsyncSession, user_id: int, beatmap
|
||||
existing_playcount.playcount += 1
|
||||
if existing_playcount.playcount % 100 == 0:
|
||||
playcount_event = Event(
|
||||
created_at=datetime.now(UTC),
|
||||
created_at=utcnow(),
|
||||
type=EventType.BEATMAP_PLAYCOUNT,
|
||||
user_id=user_id,
|
||||
)
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
from datetime import UTC, datetime
|
||||
from datetime import datetime
|
||||
from enum import Enum
|
||||
from typing import Self
|
||||
|
||||
from app.database.lazer_user import RANKING_INCLUDES, User, UserResp
|
||||
from app.models.model import UTCBaseModel
|
||||
from app.utils import utcnow
|
||||
|
||||
from pydantic import BaseModel
|
||||
from redis.asyncio import Redis
|
||||
@@ -170,7 +171,7 @@ class ChatMessageBase(UTCBaseModel, SQLModel):
|
||||
content: str = Field(sa_column=Column(VARCHAR(1000)))
|
||||
message_id: int = Field(index=True, primary_key=True, default=None)
|
||||
sender_id: int = Field(sa_column=Column(BigInteger, ForeignKey("lazer_users.id"), index=True))
|
||||
timestamp: datetime = Field(sa_column=Column(DateTime, index=True), default=datetime.now(UTC))
|
||||
timestamp: datetime = Field(sa_column=Column(DateTime, index=True), default_factory=utcnow)
|
||||
type: MessageType = Field(default=MessageType.PLAIN, index=True, exclude=True)
|
||||
uuid: str | None = Field(default=None)
|
||||
|
||||
@@ -208,7 +209,7 @@ class SilenceUser(UTCBaseModel, SQLModel, table=True):
|
||||
channel_id: int = Field(foreign_key="chat_channels.channel_id", index=True)
|
||||
until: datetime | None = Field(sa_column=Column(DateTime, index=True), default=None)
|
||||
reason: str | None = Field(default=None, sa_column=Column(VARCHAR(255), index=True))
|
||||
banned_at: datetime = Field(sa_column=Column(DateTime, index=True), default=datetime.now(UTC))
|
||||
banned_at: datetime = Field(sa_column=Column(DateTime, index=True), default_factory=utcnow)
|
||||
|
||||
|
||||
class UserSilenceResp(SQLModel):
|
||||
|
||||
@@ -2,7 +2,7 @@ from datetime import UTC, datetime, timedelta
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from app.models.model import UTCBaseModel
|
||||
from app.utils import are_adjacent_weeks
|
||||
from app.utils import are_adjacent_weeks, utcnow
|
||||
|
||||
from sqlmodel import (
|
||||
BigInteger,
|
||||
@@ -79,7 +79,7 @@ async def process_daily_challenge_score(session: AsyncSession, user_id: int, roo
|
||||
session.add(stats)
|
||||
|
||||
stats.playcount += 1
|
||||
now = datetime.now(UTC)
|
||||
now = utcnow()
|
||||
if stats.last_update is None:
|
||||
stats.daily_streak_best = 1
|
||||
stats.daily_streak_current = 1
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import UTC, datetime
|
||||
from datetime import datetime
|
||||
|
||||
from app.utils import utcnow
|
||||
|
||||
from sqlalchemy import BigInteger, Column, ForeignKey
|
||||
from sqlmodel import Field, SQLModel
|
||||
@@ -19,7 +21,7 @@ class EmailVerification(SQLModel, table=True):
|
||||
user_id: int = Field(sa_column=Column(BigInteger, ForeignKey("lazer_users.id"), nullable=False, index=True))
|
||||
email: str = Field(index=True)
|
||||
verification_code: str = Field(max_length=8) # 8位验证码
|
||||
created_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
|
||||
created_at: datetime = Field(default_factory=utcnow)
|
||||
expires_at: datetime = Field() # 验证码过期时间
|
||||
is_used: bool = Field(default=False) # 是否已使用
|
||||
used_at: datetime | None = Field(default=None)
|
||||
@@ -39,7 +41,7 @@ class LoginSession(SQLModel, table=True):
|
||||
user_agent: str | None = Field(default=None, max_length=250)
|
||||
country_code: str | None = Field(default=None)
|
||||
is_verified: bool = Field(default=False) # 是否已验证
|
||||
created_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
|
||||
created_at: datetime = Field(default_factory=lambda: utcnow())
|
||||
verified_at: datetime | None = Field(default=None)
|
||||
expires_at: datetime = Field() # 会话过期时间
|
||||
is_new_location: bool = Field(default=False) # 是否新位置登录
|
||||
|
||||
@@ -3,6 +3,7 @@ from enum import Enum
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from app.models.model import UTCBaseModel
|
||||
from app.utils import utcnow
|
||||
|
||||
from pydantic import model_serializer
|
||||
from sqlmodel import (
|
||||
@@ -40,7 +41,7 @@ class EventType(str, Enum):
|
||||
class Event(UTCBaseModel, SQLModel, table=True):
|
||||
__tablename__: str = "user_events"
|
||||
id: int = Field(default=None, primary_key=True)
|
||||
created_at: datetime = Field(sa_column=Column(DateTime(timezone=True), default=datetime.now(UTC)))
|
||||
created_at: datetime = Field(sa_column=Column(DateTime(timezone=True), default_factory=utcnow))
|
||||
type: EventType
|
||||
event_payload: dict = Field(exclude=True, default_factory=dict, sa_column=Column(JSON))
|
||||
user_id: int | None = Field(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from datetime import UTC, datetime, timedelta
|
||||
from datetime import datetime, timedelta
|
||||
import json
|
||||
from typing import TYPE_CHECKING, NotRequired, TypedDict
|
||||
|
||||
@@ -6,6 +6,7 @@ from app.models.model import UTCBaseModel
|
||||
from app.models.score import GameMode
|
||||
from app.models.user import Country, Page
|
||||
from app.path import STATIC_DIR
|
||||
from app.utils import utcnow
|
||||
|
||||
from .achievement import UserAchievement, UserAchievementResp
|
||||
from .beatmap_playcounts import BeatmapPlaycounts
|
||||
@@ -75,7 +76,7 @@ class UserBase(UTCBaseModel, SQLModel):
|
||||
is_active: bool = True
|
||||
is_bot: bool = False
|
||||
is_supporter: bool = False
|
||||
last_visit: datetime | None = Field(default=datetime.now(UTC), sa_column=Column(DateTime(timezone=True)))
|
||||
last_visit: datetime | None = Field(default_factory=utcnow, sa_column=Column(DateTime(timezone=True)))
|
||||
pm_friends_only: bool = False
|
||||
profile_colour: str | None = None
|
||||
username: str = Field(max_length=32, unique=True, index=True)
|
||||
@@ -99,7 +100,7 @@ class UserBase(UTCBaseModel, SQLModel):
|
||||
discord: str | None = None
|
||||
has_supported: bool = False
|
||||
interests: str | None = None
|
||||
join_date: datetime = Field(default=datetime.now(UTC))
|
||||
join_date: datetime = Field(default_factory=utcnow)
|
||||
location: str | None = None
|
||||
max_blocks: int = 50
|
||||
max_friends: int = 500
|
||||
@@ -408,7 +409,7 @@ class UserResp(UserBase):
|
||||
Score.user_id == obj.id,
|
||||
Score.gamemode == ruleset,
|
||||
col(Score.passed).is_(True),
|
||||
Score.ended_at > datetime.now(UTC) - timedelta(hours=24),
|
||||
Score.ended_at > utcnow() - timedelta(hours=24),
|
||||
)
|
||||
)
|
||||
).one()
|
||||
@@ -437,7 +438,7 @@ class UserResp(UserBase):
|
||||
select(LoginSession).where(
|
||||
LoginSession.user_id == obj.id,
|
||||
col(LoginSession.is_verified).is_(False),
|
||||
LoginSession.expires_at > datetime.now(UTC),
|
||||
LoginSession.expires_at > utcnow(),
|
||||
)
|
||||
)
|
||||
).first()
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
from datetime import UTC, datetime
|
||||
from datetime import datetime
|
||||
from typing import Any
|
||||
|
||||
from app.models.model import UTCBaseModel
|
||||
from app.utils import utcnow
|
||||
|
||||
from sqlmodel import (
|
||||
JSON,
|
||||
@@ -24,7 +25,7 @@ class MultiplayerEventBase(SQLModel, UTCBaseModel):
|
||||
sa_column=Column(
|
||||
DateTime(timezone=True),
|
||||
),
|
||||
default=datetime.now(UTC),
|
||||
default_factory=utcnow,
|
||||
)
|
||||
event_type: str = Field(index=True)
|
||||
|
||||
@@ -40,7 +41,7 @@ class MultiplayerEvent(MultiplayerEventBase, table=True):
|
||||
sa_column=Column(
|
||||
DateTime(timezone=True),
|
||||
),
|
||||
default=datetime.now(UTC),
|
||||
default_factory=utcnow,
|
||||
)
|
||||
event_detail: dict[str, Any] | None = Field(
|
||||
sa_column=Column(JSON),
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
from datetime import UTC, datetime
|
||||
from datetime import datetime
|
||||
from typing import Any
|
||||
|
||||
from app.models.notification import NotificationDetail, NotificationName
|
||||
from app.utils import utcnow
|
||||
|
||||
from sqlmodel import (
|
||||
JSON,
|
||||
@@ -54,7 +55,7 @@ async def insert_notification(session: AsyncSession, detail: NotificationDetail)
|
||||
object_id=detail.object_id,
|
||||
source_user_id=detail.source_user_id,
|
||||
details=detail.model_dump(),
|
||||
created_at=datetime.now(UTC),
|
||||
created_at=utcnow(),
|
||||
)
|
||||
session.add(notification)
|
||||
await session.commit()
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import UTC, datetime
|
||||
from datetime import datetime
|
||||
|
||||
from app.utils import utcnow
|
||||
|
||||
from sqlalchemy import BigInteger, Column, ForeignKey
|
||||
from sqlmodel import Field, SQLModel
|
||||
@@ -19,7 +21,7 @@ class PasswordReset(SQLModel, table=True):
|
||||
user_id: int = Field(sa_column=Column(BigInteger, ForeignKey("lazer_users.id"), nullable=False, index=True))
|
||||
email: str = Field(index=True)
|
||||
reset_code: str = Field(max_length=8) # 8位重置验证码
|
||||
created_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
|
||||
created_at: datetime = Field(default_factory=utcnow)
|
||||
expires_at: datetime = Field() # 验证码过期时间
|
||||
is_used: bool = Field(default=False) # 是否已使用
|
||||
used_at: datetime | None = Field(default=None)
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
from datetime import (
|
||||
UTC,
|
||||
date as dt,
|
||||
datetime,
|
||||
)
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from app.models.score import GameMode
|
||||
from app.utils import utcnow
|
||||
|
||||
from pydantic import BaseModel
|
||||
from sqlmodel import (
|
||||
@@ -33,7 +32,7 @@ class RankHistory(SQLModel, table=True):
|
||||
mode: GameMode
|
||||
rank: int
|
||||
date: dt = Field(
|
||||
default_factory=lambda: datetime.now(UTC).date(),
|
||||
default_factory=lambda: utcnow().date(),
|
||||
sa_column=Column(Date, index=True),
|
||||
)
|
||||
|
||||
@@ -48,7 +47,7 @@ class RankTop(SQLModel, table=True):
|
||||
mode: GameMode
|
||||
rank: int
|
||||
date: dt = Field(
|
||||
default_factory=lambda: datetime.now(UTC).date(),
|
||||
default_factory=lambda: utcnow().date(),
|
||||
sa_column=Column(Date, index=True),
|
||||
)
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from datetime import UTC, datetime
|
||||
from datetime import datetime
|
||||
|
||||
from app.database.playlist_attempts import PlaylistAggregateScore
|
||||
from app.database.room_participated_user import RoomParticipatedUser
|
||||
@@ -12,6 +12,7 @@ from app.models.room import (
|
||||
RoomPlaylistItemStats,
|
||||
RoomStatus,
|
||||
)
|
||||
from app.utils import utcnow
|
||||
|
||||
from .lazer_user import User, UserResp
|
||||
from .playlists import Playlist, PlaylistResp
|
||||
@@ -39,7 +40,7 @@ class RoomBase(SQLModel, UTCBaseModel):
|
||||
sa_column=Column(
|
||||
DateTime(timezone=True),
|
||||
),
|
||||
default=datetime.now(UTC),
|
||||
default_factory=utcnow,
|
||||
)
|
||||
ends_at: datetime | None = Field(
|
||||
sa_column=Column(
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
from datetime import UTC, datetime
|
||||
from datetime import datetime
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from app.utils import utcnow
|
||||
|
||||
from sqlalchemy.ext.asyncio import AsyncAttrs
|
||||
from sqlmodel import (
|
||||
BigInteger,
|
||||
@@ -25,7 +27,7 @@ class RoomParticipatedUser(AsyncAttrs, SQLModel, table=True):
|
||||
user_id: int = Field(sa_column=Column(BigInteger, ForeignKey("lazer_users.id"), nullable=False))
|
||||
joined_at: datetime = Field(
|
||||
sa_column=Column(DateTime(timezone=True), nullable=False),
|
||||
default=datetime.now(UTC),
|
||||
default_factory=utcnow,
|
||||
)
|
||||
left_at: datetime | None = Field(sa_column=Column(DateTime(timezone=True), nullable=True), default=None)
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from collections.abc import Sequence
|
||||
from datetime import UTC, date, datetime
|
||||
from datetime import date, datetime
|
||||
import json
|
||||
import math
|
||||
import sys
|
||||
@@ -31,6 +31,7 @@ from app.models.score import (
|
||||
ScoreStatistics,
|
||||
SoloScoreSubmissionInfo,
|
||||
)
|
||||
from app.utils import utcnow
|
||||
|
||||
from .beatmap import Beatmap, BeatmapResp
|
||||
from .beatmap_playcounts import process_beatmap_playcount
|
||||
@@ -733,7 +734,7 @@ async def process_user(
|
||||
if i < score_range and displaced_position > score_range and displaced_position is not None:
|
||||
# Create rank lost event for the displaced user
|
||||
rank_lost_event = Event(
|
||||
created_at=datetime.now(UTC),
|
||||
created_at=utcnow(),
|
||||
type=EventType.RANK_LOST,
|
||||
user_id=displaced_score.user_id,
|
||||
)
|
||||
@@ -843,7 +844,7 @@ async def process_score(
|
||||
total_score=info.total_score,
|
||||
total_score_without_mods=info.total_score_without_mods,
|
||||
beatmap_id=beatmap_id,
|
||||
ended_at=datetime.now(UTC),
|
||||
ended_at=utcnow(),
|
||||
gamemode=gamemode,
|
||||
started_at=score_token.created_at,
|
||||
user_id=user.id,
|
||||
|
||||
@@ -2,6 +2,7 @@ from datetime import datetime
|
||||
|
||||
from app.models.model import UTCBaseModel
|
||||
from app.models.score import GameMode
|
||||
from app.utils import utcnow
|
||||
|
||||
from .beatmap import Beatmap
|
||||
from .lazer_user import User
|
||||
@@ -15,8 +16,8 @@ class ScoreTokenBase(SQLModel, UTCBaseModel):
|
||||
score_id: int | None = Field(sa_column=Column(BigInteger), default=None)
|
||||
ruleset_id: GameMode
|
||||
playlist_item_id: int | None = Field(default=None) # playlist
|
||||
created_at: datetime = Field(default_factory=datetime.utcnow, sa_column=Column(DateTime))
|
||||
updated_at: datetime = Field(default_factory=datetime.utcnow, sa_column=Column(DateTime))
|
||||
created_at: datetime = Field(default_factory=utcnow, sa_column=Column(DateTime))
|
||||
updated_at: datetime = Field(default_factory=utcnow, sa_column=Column(DateTime))
|
||||
|
||||
|
||||
class ScoreToken(ScoreTokenBase, table=True):
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
from datetime import UTC, datetime, timedelta
|
||||
from datetime import timedelta
|
||||
import math
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from app.models.score import GameMode
|
||||
from app.utils import utcnow
|
||||
|
||||
from .rank_history import RankHistory
|
||||
|
||||
@@ -134,7 +135,7 @@ class UserStatisticsResp(UserStatisticsBase):
|
||||
rank_best = (
|
||||
await session.exec(
|
||||
select(func.max(RankHistory.rank)).where(
|
||||
RankHistory.date > datetime.now(UTC) - timedelta(days=30),
|
||||
RankHistory.date > utcnow() - timedelta(days=30),
|
||||
RankHistory.user_id == obj.user_id,
|
||||
)
|
||||
)
|
||||
@@ -171,7 +172,7 @@ async def get_rank(session: AsyncSession, statistics: UserStatistics, country: s
|
||||
return None
|
||||
|
||||
if country is None:
|
||||
today = datetime.now(UTC).date()
|
||||
today = utcnow().date()
|
||||
rank_history = (
|
||||
await session.exec(
|
||||
select(RankHistory).where(
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
from datetime import UTC, datetime
|
||||
from datetime import datetime
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from app.models.model import UTCBaseModel
|
||||
from app.utils import utcnow
|
||||
|
||||
from sqlalchemy import Column, DateTime
|
||||
from sqlmodel import BigInteger, Field, ForeignKey, Relationship, SQLModel
|
||||
@@ -18,7 +19,7 @@ class Team(SQLModel, UTCBaseModel, table=True):
|
||||
short_name: str = Field(max_length=10)
|
||||
flag_url: str | None = Field(default=None)
|
||||
cover_url: str | None = Field(default=None)
|
||||
created_at: datetime = Field(default=datetime.now(UTC), sa_column=Column(DateTime))
|
||||
created_at: datetime = Field(default_factory=utcnow, sa_column=Column(DateTime))
|
||||
leader_id: int = Field(sa_column=Column(BigInteger, ForeignKey("lazer_users.id")))
|
||||
|
||||
leader: "User" = Relationship()
|
||||
@@ -30,7 +31,7 @@ class TeamMember(SQLModel, UTCBaseModel, table=True):
|
||||
|
||||
user_id: int = Field(sa_column=Column(BigInteger, ForeignKey("lazer_users.id"), primary_key=True))
|
||||
team_id: int = Field(foreign_key="teams.id")
|
||||
joined_at: datetime = Field(default_factory=datetime.utcnow, sa_column=Column(DateTime))
|
||||
joined_at: datetime = Field(default_factory=utcnow, sa_column=Column(DateTime))
|
||||
|
||||
user: "User" = Relationship(back_populates="team_membership", sa_relationship_kwargs={"lazy": "joined"})
|
||||
team: "Team" = Relationship(back_populates="members", sa_relationship_kwargs={"lazy": "joined"})
|
||||
@@ -41,7 +42,7 @@ class TeamRequest(SQLModel, UTCBaseModel, table=True):
|
||||
|
||||
user_id: int = Field(sa_column=Column(BigInteger, ForeignKey("lazer_users.id"), primary_key=True))
|
||||
team_id: int = Field(foreign_key="teams.id", primary_key=True)
|
||||
requested_at: datetime = Field(default=datetime.now(UTC), sa_column=Column(DateTime))
|
||||
requested_at: datetime = Field(default_factory=utcnow, sa_column=Column(DateTime))
|
||||
|
||||
user: "User" = Relationship(sa_relationship_kwargs={"lazy": "joined"})
|
||||
team: "Team" = Relationship(sa_relationship_kwargs={"lazy": "joined"})
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
from datetime import UTC, datetime
|
||||
from datetime import datetime
|
||||
from enum import Enum
|
||||
|
||||
from app.models.model import UTCBaseModel
|
||||
from app.utils import utcnow
|
||||
|
||||
from sqlmodel import BigInteger, Column, Field, ForeignKey, Integer, SQLModel
|
||||
|
||||
@@ -17,7 +18,7 @@ class UserAccountHistoryBase(SQLModel, UTCBaseModel):
|
||||
description: str | None = None
|
||||
length: int
|
||||
permanent: bool = False
|
||||
timestamp: datetime = Field(default=datetime.now(UTC))
|
||||
timestamp: datetime = Field(default_factory=utcnow)
|
||||
type: UserAccountHistoryType
|
||||
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@ User Login Log Database Model
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from app.utils import utcnow
|
||||
|
||||
from sqlmodel import Field, SQLModel
|
||||
|
||||
|
||||
@@ -16,7 +18,7 @@ class UserLoginLog(SQLModel, table=True):
|
||||
user_id: int = Field(index=True, description="User ID")
|
||||
ip_address: str = Field(max_length=45, index=True, description="IP address (supports IPv4 and IPv6)")
|
||||
user_agent: str | None = Field(default=None, max_length=500, description="User agent information")
|
||||
login_time: datetime = Field(default_factory=datetime.utcnow, description="Login time")
|
||||
login_time: datetime = Field(default_factory=utcnow, description="Login time")
|
||||
|
||||
# GeoIP information
|
||||
country_code: str | None = Field(default=None, max_length=2, description="Country code")
|
||||
|
||||
@@ -4,7 +4,7 @@ from abc import ABC, abstractmethod
|
||||
import asyncio
|
||||
from collections.abc import Awaitable, Callable
|
||||
from dataclasses import dataclass
|
||||
from datetime import UTC, datetime, timedelta
|
||||
from datetime import datetime, timedelta
|
||||
from enum import IntEnum
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
@@ -21,6 +21,7 @@ from app.database.beatmap import Beatmap
|
||||
from app.dependencies.database import with_db
|
||||
from app.dependencies.fetcher import get_fetcher
|
||||
from app.exception import InvokeException
|
||||
from app.utils import utcnow
|
||||
|
||||
from .mods import API_MODS, APIMod
|
||||
from .room import (
|
||||
@@ -558,7 +559,7 @@ class MultiplayerQueue:
|
||||
from app.database import Playlist
|
||||
|
||||
async with with_db() as session:
|
||||
played_at = datetime.now(UTC)
|
||||
played_at = utcnow()
|
||||
await session.execute(
|
||||
update(Playlist)
|
||||
.where(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import UTC, datetime, timedelta
|
||||
from datetime import timedelta
|
||||
import re
|
||||
from typing import Literal
|
||||
|
||||
@@ -36,6 +36,7 @@ from app.service.email_verification_service import (
|
||||
)
|
||||
from app.service.login_log_service import LoginLogService
|
||||
from app.service.password_reset_service import password_reset_service
|
||||
from app.utils import utcnow
|
||||
|
||||
from fastapi import APIRouter, Depends, Form, Request
|
||||
from fastapi.responses import JSONResponse
|
||||
@@ -156,8 +157,8 @@ async def register_user(
|
||||
pw_bcrypt=get_password_hash(user_password),
|
||||
priv=1, # 普通用户权限
|
||||
country_code=country_code, # 根据 IP 地理位置设置国家
|
||||
join_date=datetime.now(UTC),
|
||||
last_visit=datetime.now(UTC),
|
||||
join_date=utcnow(),
|
||||
last_visit=utcnow(),
|
||||
is_supporter=settings.enable_supporter_for_all_users,
|
||||
support_level=int(settings.enable_supporter_for_all_users),
|
||||
)
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import UTC, datetime
|
||||
|
||||
from app.config import settings
|
||||
from app.database.lazer_user import User
|
||||
from app.database.notification import Notification, UserNotification
|
||||
@@ -9,6 +7,7 @@ from app.dependencies.database import Database
|
||||
from app.dependencies.user import get_client_user
|
||||
from app.models.chat import ChatEvent
|
||||
from app.router.v2 import api_v2_router as router
|
||||
from app.utils import utcnow
|
||||
|
||||
from . import channel, message # noqa: F401
|
||||
from .server import (
|
||||
@@ -135,7 +134,7 @@ async def mark_notifications_as_read(
|
||||
data={
|
||||
"notifications": [i.model_dump() for i in identities],
|
||||
"read_count": len(user_notifications),
|
||||
"timestamp": datetime.now(UTC).isoformat(),
|
||||
"timestamp": utcnow().isoformat(),
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
@@ -7,7 +7,7 @@ import json
|
||||
|
||||
from app.dependencies.database import get_redis, get_redis_message
|
||||
from app.log import logger
|
||||
from app.utils import bg_tasks
|
||||
from app.utils import bg_tasks, utcnow
|
||||
|
||||
from .router import router
|
||||
|
||||
@@ -74,7 +74,7 @@ async def get_server_stats() -> ServerStats:
|
||||
registered_users=registered_count,
|
||||
online_users=online_count,
|
||||
playing_users=playing_count,
|
||||
timestamp=datetime.utcnow(),
|
||||
timestamp=utcnow(),
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting server stats: {e}")
|
||||
@@ -83,7 +83,7 @@ async def get_server_stats() -> ServerStats:
|
||||
registered_users=0,
|
||||
online_users=0,
|
||||
playing_users=0,
|
||||
timestamp=datetime.utcnow(),
|
||||
timestamp=utcnow(),
|
||||
)
|
||||
|
||||
|
||||
@@ -158,7 +158,7 @@ async def get_stats_debug_info():
|
||||
try:
|
||||
from app.service.enhanced_interval_stats import EnhancedIntervalStatsManager
|
||||
|
||||
current_time = datetime.utcnow()
|
||||
current_time = utcnow()
|
||||
current_interval = await EnhancedIntervalStatsManager.get_current_interval_info()
|
||||
interval_stats = await EnhancedIntervalStatsManager.get_current_interval_stats()
|
||||
|
||||
@@ -334,7 +334,7 @@ async def record_hourly_stats() -> None:
|
||||
online_count = await _get_online_users_count(redis_async)
|
||||
playing_count = await _get_playing_users_count(redis_async)
|
||||
|
||||
current_time = datetime.utcnow()
|
||||
current_time = utcnow()
|
||||
history_point = {
|
||||
"timestamp": current_time.isoformat(),
|
||||
"online_count": online_count,
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import UTC, datetime
|
||||
import hashlib
|
||||
|
||||
from app.database.lazer_user import BASE_INCLUDES, User, UserResp
|
||||
@@ -15,7 +14,7 @@ from app.models.notification import (
|
||||
)
|
||||
from app.router.notification import server
|
||||
from app.storage.base import StorageService
|
||||
from app.utils import check_image
|
||||
from app.utils import check_image, utcnow
|
||||
|
||||
from .router import router
|
||||
|
||||
@@ -53,7 +52,7 @@ async def create_team(
|
||||
check_image(flag, 2 * 1024 * 1024, 240, 120)
|
||||
check_image(cover, 10 * 1024 * 1024, 3000, 2000)
|
||||
|
||||
now = datetime.now(UTC)
|
||||
now = utcnow()
|
||||
team = Team(name=name, short_name=short_name, leader_id=user_id, created_at=now)
|
||||
session.add(team)
|
||||
await session.commit()
|
||||
@@ -196,7 +195,7 @@ async def request_join_team(
|
||||
)
|
||||
).first():
|
||||
raise HTTPException(status_code=409, detail="Join request already exists")
|
||||
team_request = TeamRequest(user_id=current_user.id, team_id=team_id, requested_at=datetime.now(UTC))
|
||||
team_request = TeamRequest(user_id=current_user.id, team_id=team_id, requested_at=utcnow())
|
||||
session.add(team_request)
|
||||
await session.commit()
|
||||
await session.refresh(team_request)
|
||||
@@ -233,7 +232,7 @@ async def handle_request(
|
||||
if (await session.exec(select(exists()).where(TeamMember.user_id == user_id))).first():
|
||||
raise HTTPException(status_code=409, detail="User is already a member of the team")
|
||||
|
||||
session.add(TeamMember(user_id=user_id, team_id=team_id, joined_at=datetime.now(UTC)))
|
||||
session.add(TeamMember(user_id=user_id, team_id=team_id, joined_at=utcnow()))
|
||||
|
||||
await server.new_private_notification(TeamApplicationAccept.init(team_request))
|
||||
else:
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import UTC, datetime
|
||||
|
||||
from app.auth import validate_username
|
||||
from app.config import settings
|
||||
from app.database.events import Event, EventType
|
||||
from app.database.lazer_user import User
|
||||
from app.dependencies.database import Database
|
||||
from app.dependencies.user import get_client_user
|
||||
from app.utils import utcnow
|
||||
|
||||
from .router import router
|
||||
|
||||
@@ -47,7 +46,7 @@ async def user_rename(
|
||||
current_user.username = new_name
|
||||
current_user.previous_usernames = previous_username
|
||||
rename_event = Event(
|
||||
created_at=datetime.now(UTC),
|
||||
created_at=utcnow(),
|
||||
type=EventType.USERNAME_CHANGE,
|
||||
user_id=current_user.id,
|
||||
user=current_user,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import UTC, datetime, timedelta
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Literal
|
||||
|
||||
from app.database.pp_best_score import PPBestScore
|
||||
@@ -8,6 +8,7 @@ from app.database.score import Score, get_leaderboard
|
||||
from app.dependencies.database import Database
|
||||
from app.models.mods import int_to_mods, mod_to_save, mods_to_int
|
||||
from app.models.score import GameMode, LeaderboardType
|
||||
from app.utils import utcnow
|
||||
|
||||
from .router import AllStrModel, router
|
||||
|
||||
@@ -112,7 +113,7 @@ async def get_user_recent(
|
||||
.where(
|
||||
Score.user_id == user if type == "id" or user.isdigit() else col(Score.user).has(username=user),
|
||||
Score.gamemode == GameMode.from_int_extra(ruleset_id),
|
||||
Score.ended_at > datetime.now(UTC) - timedelta(hours=24),
|
||||
Score.ended_at > utcnow() - timedelta(hours=24),
|
||||
)
|
||||
.order_by(col(Score.pp).desc())
|
||||
.options(joinedload(Score.beatmap))
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import UTC, datetime
|
||||
from datetime import UTC
|
||||
from typing import Literal
|
||||
|
||||
from app.database.beatmap import Beatmap, BeatmapResp
|
||||
@@ -17,6 +17,7 @@ from app.dependencies.user import get_client_user, get_current_user
|
||||
from app.models.room import RoomCategory, RoomStatus
|
||||
from app.service.room import create_playlist_room_from_api
|
||||
from app.signalr.hub import MultiplayerHubs
|
||||
from app.utils import utcnow
|
||||
|
||||
from .router import router
|
||||
|
||||
@@ -50,7 +51,7 @@ async def get_all_rooms(
|
||||
):
|
||||
resp_list: list[RoomResp] = []
|
||||
where_clauses: list[ColumnElement[bool]] = [col(Room.category) == category]
|
||||
now = datetime.now(UTC)
|
||||
now = utcnow()
|
||||
if status is not None:
|
||||
where_clauses.append(col(Room.status) == status)
|
||||
if mode == "open":
|
||||
@@ -112,12 +113,12 @@ async def _participate_room(room_id: int, user_id: int, db_room: Room, session:
|
||||
participated_user = RoomParticipatedUser(
|
||||
room_id=room_id,
|
||||
user_id=user_id,
|
||||
joined_at=datetime.now(UTC),
|
||||
joined_at=utcnow(),
|
||||
)
|
||||
session.add(participated_user)
|
||||
else:
|
||||
participated_user.left_at = None
|
||||
participated_user.joined_at = datetime.now(UTC)
|
||||
participated_user.joined_at = utcnow()
|
||||
db_room.participant_count += 1
|
||||
|
||||
await redis.publish("chat:room:joined", f"{db_room.channel_id}:{user_id}")
|
||||
@@ -185,7 +186,7 @@ async def delete_room(
|
||||
if db_room is None:
|
||||
raise HTTPException(404, "Room not found")
|
||||
else:
|
||||
db_room.ends_at = datetime.now(UTC)
|
||||
db_room.ends_at = utcnow()
|
||||
await db.commit()
|
||||
return None
|
||||
|
||||
@@ -238,7 +239,7 @@ async def remove_user_from_room(
|
||||
)
|
||||
).first()
|
||||
if participated_user is not None:
|
||||
participated_user.left_at = datetime.now(UTC)
|
||||
participated_user.left_at = utcnow()
|
||||
if db_room.participant_count > 0:
|
||||
db_room.participant_count -= 1
|
||||
await redis.publish("chat:room:left", f"{db_room.channel_id}:{user_id}")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import UTC, date, datetime
|
||||
from datetime import UTC, date
|
||||
import math
|
||||
import time
|
||||
|
||||
@@ -50,6 +50,7 @@ from app.models.score import (
|
||||
from app.service.user_cache_service import get_user_cache_service
|
||||
from app.storage.base import StorageService
|
||||
from app.storage.local import LocalStorageService
|
||||
from app.utils import utcnow
|
||||
|
||||
from .router import router
|
||||
|
||||
@@ -166,7 +167,7 @@ async def submit_score(
|
||||
total_users = (await db.exec(select(func.count()).select_from(User))).one()
|
||||
if resp.rank_global is not None and resp.rank_global <= min(math.ceil(float(total_users) * 0.01), 50):
|
||||
rank_event = Event(
|
||||
created_at=datetime.now(UTC),
|
||||
created_at=utcnow(),
|
||||
type=EventType.RANK,
|
||||
user_id=score.user_id,
|
||||
user=score.user,
|
||||
@@ -451,7 +452,7 @@ async def create_playlist_score(
|
||||
if not room:
|
||||
raise HTTPException(status_code=404, detail="Room not found")
|
||||
db_room_time = room.ends_at.replace(tzinfo=UTC) if room.ends_at else None
|
||||
if db_room_time and db_room_time < datetime.now(UTC).replace(tzinfo=UTC):
|
||||
if db_room_time and db_room_time < utcnow().replace(tzinfo=UTC):
|
||||
raise HTTPException(status_code=400, detail="Room has ended")
|
||||
item = (await session.exec(select(Playlist).where(Playlist.id == playlist_id, Playlist.room_id == room_id))).first()
|
||||
if not item:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import UTC, datetime, timedelta
|
||||
from datetime import timedelta
|
||||
from typing import Literal
|
||||
|
||||
from app.config import settings
|
||||
@@ -22,6 +22,7 @@ from app.log import logger
|
||||
from app.models.score import GameMode
|
||||
from app.models.user import BeatmapsetType
|
||||
from app.service.user_cache_service import get_user_cache_service
|
||||
from app.utils import utcnow
|
||||
|
||||
from .router import router
|
||||
|
||||
@@ -338,7 +339,7 @@ async def get_user_scores(
|
||||
where_clause &= exists().where(col(PPBestScore.score_id) == Score.id)
|
||||
order_by = col(Score.pp).desc()
|
||||
elif type == "recent":
|
||||
where_clause &= Score.ended_at > datetime.now(UTC) - timedelta(hours=24)
|
||||
where_clause &= Score.ended_at > utcnow() - timedelta(hours=24)
|
||||
order_by = col(Score.ended_at).desc()
|
||||
elif type == "firsts":
|
||||
# TODO
|
||||
|
||||
@@ -5,13 +5,14 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from datetime import UTC, datetime, timedelta
|
||||
from datetime import timedelta
|
||||
|
||||
from app.config import settings
|
||||
from app.database.score import Score
|
||||
from app.dependencies.database import get_redis
|
||||
from app.log import logger
|
||||
from app.service.user_cache_service import get_user_cache_service
|
||||
from app.utils import utcnow
|
||||
|
||||
from sqlmodel import col, func, select
|
||||
|
||||
@@ -34,7 +35,7 @@ async def schedule_user_cache_preload_task():
|
||||
|
||||
async with with_db() as session:
|
||||
# 获取最近24小时内活跃的用户(提交过成绩的用户)
|
||||
recent_time = datetime.now(UTC) - timedelta(hours=24)
|
||||
recent_time = utcnow() - timedelta(hours=24)
|
||||
|
||||
score_count = func.count().label("score_count")
|
||||
active_user_ids = (
|
||||
|
||||
@@ -6,11 +6,12 @@ Beatmap缓存预取服务
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from datetime import UTC, datetime, timedelta
|
||||
from datetime import timedelta
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from app.config import settings
|
||||
from app.log import logger
|
||||
from app.utils import utcnow
|
||||
|
||||
from redis.asyncio import Redis
|
||||
from sqlmodel import col, func, select
|
||||
@@ -40,7 +41,7 @@ class BeatmapCacheService:
|
||||
logger.info(f"Starting preload of top {limit} popular beatmaps")
|
||||
|
||||
# 获取过去24小时内最热门的beatmap
|
||||
recent_time = datetime.now(UTC) - timedelta(hours=24)
|
||||
recent_time = utcnow() - timedelta(hours=24)
|
||||
|
||||
from app.database.score import Score
|
||||
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import UTC, datetime, timedelta
|
||||
from datetime import timedelta
|
||||
|
||||
from app.database import RankHistory, UserStatistics
|
||||
from app.database.rank_history import RankTop
|
||||
from app.dependencies.database import with_db
|
||||
from app.dependencies.scheduler import get_scheduler
|
||||
from app.models.score import GameMode
|
||||
from app.utils import utcnow
|
||||
|
||||
from sqlmodel import col, exists, select, update
|
||||
|
||||
|
||||
@get_scheduler().scheduled_job("cron", hour=0, minute=0, second=0, id="calculate_user_rank")
|
||||
async def calculate_user_rank(is_today: bool = False):
|
||||
today = datetime.now(UTC).date()
|
||||
today = utcnow().date()
|
||||
target_date = today if is_today else today - timedelta(days=1)
|
||||
async with with_db() as session:
|
||||
for gamemode in GameMode:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import UTC, datetime, timedelta
|
||||
from datetime import UTC, timedelta
|
||||
import json
|
||||
from math import ceil
|
||||
|
||||
@@ -17,7 +17,7 @@ from app.log import logger
|
||||
from app.models.metadata_hub import DailyChallengeInfo
|
||||
from app.models.mods import APIMod, get_available_mods
|
||||
from app.models.room import RoomCategory
|
||||
from app.utils import are_same_weeks
|
||||
from app.utils import are_same_weeks, utcnow
|
||||
|
||||
from .room import create_playlist_room
|
||||
|
||||
@@ -32,7 +32,7 @@ async def create_daily_challenge_room(
|
||||
allowed_mods: list[APIMod] = [],
|
||||
) -> Room:
|
||||
async with with_db() as session:
|
||||
today = datetime.now(UTC).date()
|
||||
today = utcnow().date()
|
||||
return await create_playlist_room(
|
||||
session=session,
|
||||
name=str(today),
|
||||
@@ -57,7 +57,7 @@ async def create_daily_challenge_room(
|
||||
async def daily_challenge_job():
|
||||
from app.signalr.hub import MetadataHubs
|
||||
|
||||
now = datetime.now(UTC)
|
||||
now = utcnow()
|
||||
redis = get_redis()
|
||||
key = f"daily_challenge:{now.date()}"
|
||||
if not await redis.exists(key):
|
||||
@@ -67,7 +67,7 @@ async def daily_challenge_job():
|
||||
await session.exec(
|
||||
select(Room).where(
|
||||
Room.category == RoomCategory.DAILY_CHALLENGE,
|
||||
col(Room.ends_at) > datetime.now(UTC),
|
||||
col(Room.ends_at) > utcnow(),
|
||||
)
|
||||
)
|
||||
).first()
|
||||
@@ -87,7 +87,7 @@ async def daily_challenge_job():
|
||||
get_scheduler().add_job(
|
||||
daily_challenge_job,
|
||||
"date",
|
||||
run_date=datetime.now(UTC) + timedelta(minutes=5),
|
||||
run_date=utcnow() + timedelta(minutes=5),
|
||||
)
|
||||
return
|
||||
|
||||
@@ -121,14 +121,14 @@ async def daily_challenge_job():
|
||||
get_scheduler().add_job(
|
||||
daily_challenge_job,
|
||||
"date",
|
||||
run_date=datetime.now(UTC) + timedelta(minutes=5),
|
||||
run_date=utcnow() + timedelta(minutes=5),
|
||||
)
|
||||
|
||||
|
||||
@get_scheduler().scheduled_job("cron", hour=0, minute=1, second=0, id="daily_challenge_last_top")
|
||||
async def process_daily_challenge_top():
|
||||
async with with_db() as session:
|
||||
now = datetime.now(UTC)
|
||||
now = utcnow()
|
||||
room = (
|
||||
await session.exec(
|
||||
select(Room).where(
|
||||
|
||||
@@ -4,10 +4,11 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import UTC, datetime, timedelta
|
||||
from datetime import timedelta
|
||||
|
||||
from app.database.email_verification import EmailVerification, LoginSession
|
||||
from app.log import logger
|
||||
from app.utils import utcnow
|
||||
|
||||
from sqlmodel import col, select
|
||||
from sqlmodel.ext.asyncio.session import AsyncSession
|
||||
@@ -29,7 +30,7 @@ class DatabaseCleanupService:
|
||||
"""
|
||||
try:
|
||||
# 查找过期的验证码记录
|
||||
current_time = datetime.now(UTC)
|
||||
current_time = utcnow()
|
||||
|
||||
stmt = select(EmailVerification).where(EmailVerification.expires_at < current_time)
|
||||
result = await db.exec(stmt)
|
||||
@@ -66,7 +67,7 @@ class DatabaseCleanupService:
|
||||
"""
|
||||
try:
|
||||
# 查找过期的登录会话记录
|
||||
current_time = datetime.now(UTC)
|
||||
current_time = utcnow()
|
||||
|
||||
stmt = select(LoginSession).where(LoginSession.expires_at < current_time)
|
||||
result = await db.exec(stmt)
|
||||
@@ -104,7 +105,7 @@ class DatabaseCleanupService:
|
||||
"""
|
||||
try:
|
||||
# 查找指定天数前的已使用验证码记录
|
||||
cutoff_time = datetime.now(UTC) - timedelta(days=days_old)
|
||||
cutoff_time = utcnow() - timedelta(days=days_old)
|
||||
|
||||
stmt = select(EmailVerification).where(col(EmailVerification.is_used).is_(True))
|
||||
result = await db.exec(stmt)
|
||||
@@ -147,7 +148,7 @@ class DatabaseCleanupService:
|
||||
"""
|
||||
try:
|
||||
# 查找指定天数前的已验证会话记录
|
||||
cutoff_time = datetime.now(UTC) - timedelta(days=days_old)
|
||||
cutoff_time = utcnow() - timedelta(days=days_old)
|
||||
|
||||
stmt = select(LoginSession).where(col(LoginSession.is_verified).is_(True))
|
||||
result = await db.exec(stmt)
|
||||
@@ -225,7 +226,7 @@ class DatabaseCleanupService:
|
||||
dict: 统计信息
|
||||
"""
|
||||
try:
|
||||
current_time = datetime.now(UTC)
|
||||
current_time = utcnow()
|
||||
cutoff_7_days = current_time - timedelta(days=7)
|
||||
cutoff_30_days = current_time - timedelta(days=30)
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import UTC, datetime, timedelta
|
||||
from datetime import timedelta
|
||||
import secrets
|
||||
import string
|
||||
|
||||
@@ -12,6 +12,7 @@ from app.config import settings
|
||||
from app.database.email_verification import EmailVerification, LoginSession
|
||||
from app.log import logger
|
||||
from app.service.email_queue import email_queue # 导入邮件队列
|
||||
from app.utils import utcnow
|
||||
|
||||
from redis.asyncio import Redis
|
||||
from sqlmodel import col, select
|
||||
@@ -200,7 +201,7 @@ This email was sent automatically, please do not reply.
|
||||
select(EmailVerification).where(
|
||||
EmailVerification.user_id == user_id,
|
||||
col(EmailVerification.is_used).is_(False),
|
||||
EmailVerification.expires_at > datetime.now(UTC),
|
||||
EmailVerification.expires_at > utcnow(),
|
||||
)
|
||||
)
|
||||
existing = existing_result.first()
|
||||
@@ -217,7 +218,7 @@ This email was sent automatically, please do not reply.
|
||||
user_id=user_id,
|
||||
email=email,
|
||||
verification_code=code,
|
||||
expires_at=datetime.now(UTC) + timedelta(minutes=10), # 10分钟过期
|
||||
expires_at=utcnow() + timedelta(minutes=10), # 10分钟过期
|
||||
ip_address=ip_address,
|
||||
user_agent=user_agent,
|
||||
)
|
||||
@@ -306,7 +307,7 @@ This email was sent automatically, please do not reply.
|
||||
EmailVerification.user_id == user_id,
|
||||
EmailVerification.verification_code == code,
|
||||
col(EmailVerification.is_used).is_(False),
|
||||
EmailVerification.expires_at > datetime.now(UTC),
|
||||
EmailVerification.expires_at > utcnow(),
|
||||
)
|
||||
)
|
||||
|
||||
@@ -316,7 +317,7 @@ This email was sent automatically, please do not reply.
|
||||
|
||||
# 标记为已使用
|
||||
verification.is_used = True
|
||||
verification.used_at = datetime.now(UTC)
|
||||
verification.used_at = utcnow()
|
||||
|
||||
# 同时更新对应的登录会话状态
|
||||
await LoginSessionService.mark_session_verified(db, user_id)
|
||||
@@ -397,7 +398,7 @@ class LoginSessionService:
|
||||
user_agent=None,
|
||||
country_code=country_code,
|
||||
is_new_location=is_new_location,
|
||||
expires_at=datetime.now(UTC) + timedelta(hours=24), # 24小时过期
|
||||
expires_at=utcnow() + timedelta(hours=24), # 24小时过期
|
||||
is_verified=not is_new_location, # 新位置需要验证
|
||||
)
|
||||
|
||||
@@ -446,7 +447,7 @@ class LoginSessionService:
|
||||
session = result.first()
|
||||
if session:
|
||||
session.is_verified = True
|
||||
session.verified_at = datetime.now(UTC)
|
||||
session.verified_at = utcnow()
|
||||
await db.commit()
|
||||
|
||||
logger.info(f"[Login Session] User {user_id} session verification successful")
|
||||
@@ -463,7 +464,7 @@ class LoginSessionService:
|
||||
"""检查是否为新位置登录"""
|
||||
try:
|
||||
# 查看过去30天内是否有相同IP或相同国家的登录记录
|
||||
thirty_days_ago = datetime.now(UTC) - timedelta(days=30)
|
||||
thirty_days_ago = utcnow() - timedelta(days=30)
|
||||
|
||||
result = await db.exec(
|
||||
select(LoginSession).where(
|
||||
@@ -492,7 +493,7 @@ class LoginSessionService:
|
||||
select(LoginSession).where(
|
||||
LoginSession.user_id == user_id,
|
||||
col(LoginSession.is_verified).is_(False),
|
||||
LoginSession.expires_at > datetime.now(UTC),
|
||||
LoginSession.expires_at > utcnow(),
|
||||
)
|
||||
)
|
||||
|
||||
@@ -501,7 +502,7 @@ class LoginSessionService:
|
||||
# 标记所有会话为已验证
|
||||
for session in sessions:
|
||||
session.is_verified = True
|
||||
session.verified_at = datetime.now(UTC)
|
||||
session.verified_at = utcnow()
|
||||
|
||||
if sessions:
|
||||
logger.info(f"[Login Session] Marked {len(sessions)} session(s) as verified for user {user_id}")
|
||||
|
||||
@@ -16,6 +16,7 @@ from app.router.private.stats import (
|
||||
_get_playing_users_count,
|
||||
_redis_exec,
|
||||
)
|
||||
from app.utils import utcnow
|
||||
|
||||
# Redis keys for interval statistics
|
||||
INTERVAL_STATS_BASE_KEY = "server:interval_stats"
|
||||
@@ -34,7 +35,7 @@ class IntervalInfo:
|
||||
|
||||
def is_current(self) -> bool:
|
||||
"""检查是否是当前区间"""
|
||||
now = datetime.utcnow()
|
||||
now = utcnow()
|
||||
return self.start_time <= now < self.end_time
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
@@ -101,7 +102,7 @@ class EnhancedIntervalStatsManager:
|
||||
@staticmethod
|
||||
def get_current_interval_boundaries() -> tuple[datetime, datetime]:
|
||||
"""获取当前30分钟区间的边界"""
|
||||
now = datetime.utcnow()
|
||||
now = utcnow()
|
||||
# 计算区间开始时间(向下取整到最近的30分钟)
|
||||
minute = (now.minute // 30) * 30
|
||||
start_time = now.replace(minute=minute, second=0, microsecond=0)
|
||||
@@ -157,7 +158,7 @@ class EnhancedIntervalStatsManager:
|
||||
peak_online_count=0,
|
||||
peak_playing_count=0,
|
||||
total_samples=0,
|
||||
created_at=datetime.utcnow(),
|
||||
created_at=utcnow(),
|
||||
)
|
||||
|
||||
await _redis_exec(
|
||||
@@ -195,7 +196,7 @@ class EnhancedIntervalStatsManager:
|
||||
needed_points = 48 - history_length
|
||||
|
||||
# 从当前时间往前推,创建缺失的时间点(都填充为0)
|
||||
current_time = datetime.utcnow() # noqa: F841
|
||||
current_time = utcnow() # noqa: F841
|
||||
current_interval_start, _ = EnhancedIntervalStatsManager.get_current_interval_boundaries()
|
||||
|
||||
# 从当前区间开始往前推,创建历史数据点(确保时间对齐到30分钟边界)
|
||||
@@ -323,7 +324,7 @@ class EnhancedIntervalStatsManager:
|
||||
peak_online_count=current_online,
|
||||
peak_playing_count=current_playing,
|
||||
total_samples=1,
|
||||
created_at=datetime.utcnow(),
|
||||
created_at=utcnow(),
|
||||
)
|
||||
|
||||
# 更新独特用户数
|
||||
@@ -431,7 +432,7 @@ class EnhancedIntervalStatsManager:
|
||||
|
||||
try:
|
||||
# 删除过期的区间统计数据(超过2小时的)
|
||||
cutoff_time = datetime.utcnow() - timedelta(hours=2)
|
||||
cutoff_time = utcnow() - timedelta(hours=2)
|
||||
pattern = f"{INTERVAL_STATS_BASE_KEY}:*"
|
||||
|
||||
keys = await redis_async.keys(pattern)
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from datetime import datetime
|
||||
|
||||
from app.database.user_login_log import UserLoginLog
|
||||
from app.dependencies.geoip import get_client_ip, get_geoip_helper, normalize_ip
|
||||
from app.log import logger
|
||||
from app.utils import utcnow
|
||||
|
||||
from fastapi import Request
|
||||
from sqlmodel.ext.asyncio.session import AsyncSession
|
||||
@@ -56,7 +56,7 @@ class LoginLogService:
|
||||
user_id=user_id,
|
||||
ip_address=ip_address,
|
||||
user_agent=user_agent,
|
||||
login_time=datetime.utcnow(),
|
||||
login_time=utcnow(),
|
||||
login_success=login_success,
|
||||
login_method=login_method,
|
||||
notes=notes,
|
||||
|
||||
@@ -6,11 +6,10 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from app.dependencies.database import get_redis
|
||||
from app.log import logger
|
||||
from app.router.private.stats import add_online_user
|
||||
from app.utils import utcnow
|
||||
|
||||
|
||||
class OnlineStatusManager:
|
||||
@@ -37,7 +36,7 @@ class OnlineStatusManager:
|
||||
|
||||
# 3. 设置最后活跃时间戳
|
||||
last_seen_key = f"user:last_seen:{user_id}"
|
||||
await redis.set(last_seen_key, int(datetime.utcnow().timestamp()), ex=7200)
|
||||
await redis.set(last_seen_key, int(utcnow().timestamp()), ex=7200)
|
||||
|
||||
logger.debug(f"[OnlineStatusManager] User {user_id} set online via {hub_type}")
|
||||
|
||||
@@ -62,7 +61,7 @@ class OnlineStatusManager:
|
||||
|
||||
# 刷新最后活跃时间
|
||||
last_seen_key = f"user:last_seen:{user_id}"
|
||||
await redis.set(last_seen_key, int(datetime.utcnow().timestamp()), ex=7200)
|
||||
await redis.set(last_seen_key, int(utcnow().timestamp()), ex=7200)
|
||||
|
||||
logger.debug(f"[OnlineStatusManager] Refreshed online status for user {user_id}")
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import UTC, datetime
|
||||
from datetime import datetime
|
||||
import json
|
||||
import secrets
|
||||
import string
|
||||
@@ -15,6 +15,7 @@ from app.dependencies.database import with_db
|
||||
from app.log import logger
|
||||
from app.service.email_queue import email_queue # 导入邮件队列
|
||||
from app.service.email_service import EmailService
|
||||
from app.utils import utcnow
|
||||
|
||||
from redis.asyncio import Redis
|
||||
from sqlmodel import select
|
||||
@@ -88,7 +89,7 @@ class PasswordResetService:
|
||||
"user_id": user.id,
|
||||
"email": email,
|
||||
"reset_code": reset_code,
|
||||
"created_at": datetime.now(UTC).isoformat(),
|
||||
"created_at": utcnow().isoformat(),
|
||||
"ip_address": ip_address,
|
||||
"user_agent": user_agent,
|
||||
"used": False,
|
||||
@@ -346,7 +347,7 @@ class PasswordResetService:
|
||||
try:
|
||||
# 先标记验证码为已使用(在数据库操作之前)
|
||||
reset_data["used"] = True
|
||||
reset_data["used_at"] = datetime.now(UTC).isoformat()
|
||||
reset_data["used_at"] = utcnow().isoformat()
|
||||
|
||||
# 保存用户ID用于日志记录
|
||||
user_id = user.id
|
||||
@@ -391,7 +392,7 @@ class PasswordResetService:
|
||||
|
||||
# 计算剩余的TTL时间
|
||||
created_at = datetime.fromisoformat(reset_data.get("created_at", ""))
|
||||
elapsed = (datetime.now(UTC) - created_at).total_seconds()
|
||||
elapsed = (utcnow() - created_at).total_seconds()
|
||||
remaining_ttl = max(0, 600 - int(elapsed)) # 600秒总过期时间
|
||||
|
||||
if remaining_ttl > 0:
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from datetime import UTC, datetime
|
||||
from datetime import datetime
|
||||
import json
|
||||
from typing import TYPE_CHECKING, Literal
|
||||
|
||||
@@ -14,6 +14,7 @@ from app.config import settings
|
||||
from app.database.statistics import UserStatistics, UserStatisticsResp
|
||||
from app.log import logger
|
||||
from app.models.score import GameMode
|
||||
from app.utils import utcnow
|
||||
|
||||
from redis.asyncio import Redis
|
||||
from sqlmodel import col, select
|
||||
@@ -258,7 +259,7 @@ class RankingCacheService:
|
||||
# 计算统计信息
|
||||
stats = {
|
||||
"total_users": total_users,
|
||||
"last_updated": datetime.now(UTC).isoformat(),
|
||||
"last_updated": utcnow().isoformat(),
|
||||
"type": type,
|
||||
"ruleset": ruleset,
|
||||
"country": country,
|
||||
@@ -370,7 +371,7 @@ class RankingCacheService:
|
||||
# 计算统计信息
|
||||
stats = {
|
||||
"total_countries": len(country_stats_list),
|
||||
"last_updated": datetime.now(UTC).isoformat(),
|
||||
"last_updated": utcnow().isoformat(),
|
||||
"ruleset": ruleset,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import UTC, datetime, timedelta
|
||||
from datetime import timedelta
|
||||
|
||||
from app.database.beatmap import Beatmap
|
||||
from app.database.chat import ChannelType, ChatChannel
|
||||
@@ -8,6 +8,7 @@ from app.database.playlists import Playlist
|
||||
from app.database.room import APIUploadedRoom, Room
|
||||
from app.dependencies.fetcher import get_fetcher
|
||||
from app.models.room import MatchType, QueueMode, RoomCategory, RoomStatus
|
||||
from app.utils import utcnow
|
||||
|
||||
from sqlalchemy import exists
|
||||
from sqlmodel import col, select
|
||||
@@ -17,7 +18,7 @@ from sqlmodel.ext.asyncio.session import AsyncSession
|
||||
async def create_playlist_room_from_api(session: AsyncSession, room: APIUploadedRoom, host_id: int) -> Room:
|
||||
db_room = room.to_room()
|
||||
db_room.host_id = host_id
|
||||
db_room.starts_at = datetime.now(UTC)
|
||||
db_room.starts_at = utcnow()
|
||||
db_room.ends_at = db_room.starts_at + timedelta(minutes=db_room.duration if db_room.duration is not None else 0)
|
||||
session.add(db_room)
|
||||
await session.commit()
|
||||
@@ -52,8 +53,8 @@ async def create_playlist_room(
|
||||
name=name,
|
||||
category=category,
|
||||
duration=duration,
|
||||
starts_at=datetime.now(UTC),
|
||||
ends_at=datetime.now(UTC) + timedelta(minutes=duration),
|
||||
starts_at=utcnow(),
|
||||
ends_at=utcnow() + timedelta(minutes=duration),
|
||||
participant_count=0,
|
||||
max_attempts=max_attempts,
|
||||
type=MatchType.PLAYLISTS,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
from datetime import timedelta
|
||||
|
||||
from app.dependencies.database import get_redis, get_redis_message
|
||||
from app.log import logger
|
||||
@@ -9,6 +9,7 @@ from app.router.private.stats import (
|
||||
REDIS_PLAYING_USERS_KEY,
|
||||
_redis_exec,
|
||||
)
|
||||
from app.utils import utcnow
|
||||
|
||||
|
||||
async def cleanup_stale_online_users() -> tuple[int, int]:
|
||||
@@ -25,7 +26,7 @@ async def cleanup_stale_online_users() -> tuple[int, int]:
|
||||
playing_users = await _redis_exec(redis_sync.smembers, REDIS_PLAYING_USERS_KEY)
|
||||
|
||||
# 检查在线用户的最后活动时间
|
||||
current_time = datetime.utcnow()
|
||||
current_time = utcnow()
|
||||
stale_threshold = current_time - timedelta(hours=2) # 2小时无活动视为过期 # noqa: F841
|
||||
|
||||
# 对于在线用户,我们检查metadata在线标记
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from datetime import datetime, timedelta
|
||||
from datetime import timedelta
|
||||
|
||||
from app.log import logger
|
||||
from app.router.private.stats import record_hourly_stats, update_registered_users_count
|
||||
@@ -10,6 +10,7 @@ from app.service.stats_cleanup import (
|
||||
cleanup_stale_online_users,
|
||||
refresh_redis_key_expiry,
|
||||
)
|
||||
from app.utils import utcnow
|
||||
|
||||
|
||||
class StatsScheduler:
|
||||
@@ -60,7 +61,7 @@ class StatsScheduler:
|
||||
while self._running:
|
||||
try:
|
||||
# 计算下次区间结束时间
|
||||
now = datetime.utcnow()
|
||||
now = utcnow()
|
||||
|
||||
# 计算当前区间的结束时间
|
||||
current_minute = (now.minute // 30) * 30
|
||||
@@ -93,15 +94,11 @@ class StatsScheduler:
|
||||
# 完成当前区间并记录到历史
|
||||
finalized_stats = await EnhancedIntervalStatsManager.finalize_interval()
|
||||
if finalized_stats:
|
||||
logger.info(
|
||||
f"Finalized enhanced interval statistics at {datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')}"
|
||||
)
|
||||
logger.info(f"Finalized enhanced interval statistics at {utcnow().strftime('%Y-%m-%d %H:%M:%S')}")
|
||||
else:
|
||||
# 如果区间完成失败,使用原有方式记录
|
||||
await record_hourly_stats()
|
||||
logger.info(
|
||||
f"Recorded hourly statistics (fallback) at {datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')}"
|
||||
)
|
||||
logger.info(f"Recorded hourly statistics (fallback) at {utcnow().strftime('%Y-%m-%d %H:%M:%S')}")
|
||||
|
||||
# 开始新的区间统计
|
||||
await EnhancedIntervalStatsManager.initialize_current_interval()
|
||||
|
||||
@@ -3,7 +3,6 @@ from __future__ import annotations
|
||||
import asyncio
|
||||
from collections import defaultdict
|
||||
from collections.abc import Coroutine
|
||||
from datetime import UTC, datetime
|
||||
import math
|
||||
from typing import override
|
||||
|
||||
@@ -27,6 +26,7 @@ from app.models.metadata_hub import (
|
||||
)
|
||||
from app.models.room import RoomCategory
|
||||
from app.service.subscribers.score_processed import ScoreSubscriber
|
||||
from app.utils import utcnow
|
||||
|
||||
from .hub import Client, Hub
|
||||
|
||||
@@ -41,11 +41,11 @@ class MetadataHub(Hub[MetadataClientState]):
|
||||
self.subscriber = ScoreSubscriber()
|
||||
self.subscriber.metadata_hub = self
|
||||
self._daily_challenge_stats: MultiplayerRoomStats | None = None
|
||||
self._today = datetime.now(UTC).date()
|
||||
self._today = utcnow().date()
|
||||
self._lock = asyncio.Lock()
|
||||
|
||||
def get_daily_challenge_stats(self, daily_challenge_room: int) -> MultiplayerRoomStats:
|
||||
if self._daily_challenge_stats is None or self._today != datetime.now(UTC).date():
|
||||
if self._daily_challenge_stats is None or self._today != utcnow().date():
|
||||
self._daily_challenge_stats = MultiplayerRoomStats(
|
||||
room_id=daily_challenge_room,
|
||||
playlist_item_stats={},
|
||||
@@ -98,7 +98,7 @@ class MetadataHub(Hub[MetadataClientState]):
|
||||
async with with_db() as session:
|
||||
async with session.begin():
|
||||
user = (await session.exec(select(User).where(User.id == int(state.connection_id)))).one()
|
||||
user.last_visit = datetime.now(UTC)
|
||||
user.last_visit = utcnow()
|
||||
await session.commit()
|
||||
|
||||
@override
|
||||
@@ -149,7 +149,7 @@ class MetadataHub(Hub[MetadataClientState]):
|
||||
daily_challenge_room = (
|
||||
await session.exec(
|
||||
select(Room).where(
|
||||
col(Room.ends_at) > datetime.now(UTC),
|
||||
col(Room.ends_at) > utcnow(),
|
||||
Room.category == RoomCategory.DAILY_CHALLENGE,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from datetime import UTC, datetime, timedelta
|
||||
from datetime import timedelta
|
||||
from typing import override
|
||||
|
||||
from app.database import Room
|
||||
@@ -44,6 +44,7 @@ from app.models.room import (
|
||||
RoomStatus,
|
||||
)
|
||||
from app.models.score import GameMode
|
||||
from app.utils import utcnow
|
||||
|
||||
from .hub import Client, Hub
|
||||
|
||||
@@ -233,7 +234,7 @@ class MultiplayerHub(Hub[MultiplayerClientState]):
|
||||
item = room.playlist[0]
|
||||
item.owner_id = client.user_id
|
||||
room.room_id = db_room.id
|
||||
starts_at = db_room.starts_at or datetime.now(UTC)
|
||||
starts_at = db_room.starts_at or utcnow()
|
||||
beatmap_exists = await session.exec(select(exists().where(col(Beatmap.id) == item.beatmap_id)))
|
||||
if not beatmap_exists.one():
|
||||
fetcher = await get_fetcher()
|
||||
@@ -306,7 +307,7 @@ class MultiplayerHub(Hub[MultiplayerClientState]):
|
||||
session.add(participated_user)
|
||||
else:
|
||||
participated_user.left_at = None
|
||||
participated_user.joined_at = datetime.now(UTC)
|
||||
participated_user.joined_at = utcnow()
|
||||
|
||||
db_room = await session.get(Room, room_id)
|
||||
if db_room is None:
|
||||
@@ -1056,7 +1057,7 @@ class MultiplayerHub(Hub[MultiplayerClientState]):
|
||||
)
|
||||
).first()
|
||||
if participated_user is not None:
|
||||
participated_user.left_at = datetime.now(UTC)
|
||||
participated_user.left_at = utcnow()
|
||||
|
||||
db_room = await session.get(Room, room.room.room_id)
|
||||
if db_room is None:
|
||||
@@ -1087,7 +1088,7 @@ class MultiplayerHub(Hub[MultiplayerClientState]):
|
||||
.where(col(Room.id) == room.room.room_id)
|
||||
.values(
|
||||
name=room.room.settings.name,
|
||||
ends_at=datetime.now(UTC),
|
||||
ends_at=utcnow(),
|
||||
type=room.room.settings.match_type,
|
||||
queue_mode=room.room.settings.queue_mode,
|
||||
auto_skip=room.room.settings.auto_skip,
|
||||
|
||||
@@ -2,7 +2,7 @@ from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from collections.abc import Awaitable, Callable, Sequence
|
||||
from datetime import datetime
|
||||
from datetime import UTC, datetime
|
||||
import functools
|
||||
import inspect
|
||||
from io import BytesIO
|
||||
@@ -270,3 +270,7 @@ class BackgroundTasks:
|
||||
|
||||
|
||||
bg_tasks = BackgroundTasks()
|
||||
|
||||
|
||||
def utcnow() -> datetime:
|
||||
return datetime.now(tz=UTC)
|
||||
|
||||
5
main.py
5
main.py
@@ -1,7 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from contextlib import asynccontextmanager
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
from app.config import settings
|
||||
@@ -38,7 +37,7 @@ from app.service.online_status_maintenance import schedule_online_status_mainten
|
||||
from app.service.osu_rx_statistics import create_rx_statistics
|
||||
from app.service.redis_message_system import redis_message_system
|
||||
from app.service.stats_scheduler import start_stats_scheduler, stop_stats_scheduler
|
||||
from app.utils import bg_tasks
|
||||
from app.utils import bg_tasks, utcnow
|
||||
|
||||
from fastapi import FastAPI, HTTPException, Request
|
||||
from fastapi.exceptions import RequestValidationError
|
||||
@@ -157,7 +156,7 @@ async def root():
|
||||
@app.get("/health", include_in_schema=False)
|
||||
async def health_check():
|
||||
"""健康检查端点"""
|
||||
return {"status": "ok", "timestamp": datetime.utcnow().isoformat()}
|
||||
return {"status": "ok", "timestamp": utcnow().isoformat()}
|
||||
|
||||
|
||||
@app.exception_handler(RequestValidationError)
|
||||
|
||||
Reference in New Issue
Block a user