feat(auth、user):新增用户的粉丝数、关注数、简介

This commit is contained in:
xingc
2025-10-16 15:28:07 +08:00
parent cddb0ab0dd
commit 0cf3145e86
5 changed files with 99 additions and 11 deletions

View File

@@ -11,9 +11,9 @@ from typing import List
import ujson import ujson
from sqlalchemy import ( from sqlalchemy import (
Column, Integer, String, Text, Date, Integer, String, Text, Date,
ForeignKey, DECIMAL, CheckConstraint, ForeignKey, DECIMAL, CheckConstraint,
UniqueConstraint, Index, Sequence, JSON, Enum, Boolean, UUID, Table UniqueConstraint, Index, Sequence, JSON, Enum, Boolean
) )
from sqlalchemy.orm import ( from sqlalchemy.orm import (
DeclarativeBase, Mapped, mapped_column, relationship DeclarativeBase, Mapped, mapped_column, relationship
@@ -80,6 +80,10 @@ class User(BaseModel, DateModel):
phone: Mapped[str] = mapped_column(String(11), nullable=True, comment='手机号码') phone: Mapped[str] = mapped_column(String(11), nullable=True, comment='手机号码')
vip_expire_date: Mapped[date] = mapped_column(Date, default=set_default_vip_expire_date, comment='会员到期时间') vip_expire_date: Mapped[date] = mapped_column(Date, default=set_default_vip_expire_date, comment='会员到期时间')
points: Mapped[int] = mapped_column(Integer, default=0, comment='会员积分') points: Mapped[int] = mapped_column(Integer, default=0, comment='会员积分')
description: Mapped[str] = mapped_column(String(200), default='', comment='用户简介')
fans_count: Mapped[int] = mapped_column(Integer, default=0, comment='粉丝数')
followers_count: Mapped[int] = mapped_column(Integer, default=0, comment='关注数')
last_active_at: Mapped[datetime] = mapped_column(nullable=True, comment='最后活跃时间')
collections: Mapped[list['UserCollection']] = relationship( collections: Mapped[list['UserCollection']] = relationship(
back_populates='user', back_populates='user',

View File

@@ -5,6 +5,7 @@
# @Author : xingc # @Author : xingc
# @Desc : # @Desc :
import logging import logging
from datetime import datetime
from typing import Annotated from typing import Annotated
import ujson import ujson
@@ -21,7 +22,7 @@ from core.db.crud import CRUD
from core.db.engine import get_async_session, get_cache from core.db.engine import get_async_session, get_cache
from core.db.models import User from core.db.models import User
from core.utils.helper import generate_keyname from core.utils.helper import generate_keyname
from core.wechat.api import wechat_api, decrypt_sensitive_data from core.wechat.api import wechat_api
from . import schema from . import schema
from .services import create_access_token from .services import create_access_token
@@ -96,10 +97,16 @@ async def wechat_login(
'wechat_union_id': union_id, 'wechat_union_id': union_id,
'phone': payload.phone, 'phone': payload.phone,
'avatar_url': payload.avatar_url, 'avatar_url': payload.avatar_url,
'last_active_at': datetime.now(),
}, },
filters={'wechat_openid': openid} filters={'wechat_openid': openid}
) )
await redis_conn.delete(keyname) await redis_conn.delete(keyname)
else:
await db_crud.update(
data={'last_active_at': datetime.now()},
filters={'wechat_openid': openid}
)
result = create_access_token({ result = create_access_token({
'sub': str(user.user_id) 'sub': str(user.user_id)

View File

@@ -0,0 +1,62 @@
# -*- coding: utf-8 -*-
# @File: actions.py
# @Date: 2025/10/15 18:58
# @Software: PyCharm
# @Author: xingc
# @Desc:
import logging
from sqlalchemy.ext.asyncio import AsyncSession
from core.db.crud import CRUD
from core.db.engine import async_db_session
from core.db.models import User, UserFollows
logger = logging.getLogger(__name__)
async def update_user_follower_total(
follower_id: int,
followed_id: int,
db_session: AsyncSession
):
"""
更新用户计数
:param follower_id: 关注者id
:param followed_id: 被关注者id
:param db_session:
:return:
"""
db_crud = CRUD(db_session, UserFollows)
# 更新关注数
followers_count = await db_crud.total(
filters={
'follower_id': follower_id,
'followed_id': followed_id,
}
)
await db_crud.update(
model=User,
data={
'followers_count': followers_count,
},
filters={
'user_id': follower_id,
}
)
# 更新粉丝数
fans_count = await db_crud.total(
filters={
'followed_id': followed_id,
}
)
await db_crud.update(
model=User,
data={
'fans_count': fans_count,
},
filters={
'user_id': followed_id,
}
)

View File

@@ -18,6 +18,7 @@ from core.routers.auth.schema import LoginUser
from core.routers.auth.services import get_current_user_form_http from core.routers.auth.services import get_current_user_form_http
from . import schema from . import schema
from .actions import update_user_follower_total
user_router = APIRouter(prefix='/user', tags=['用户模块']) user_router = APIRouter(prefix='/user', tags=['用户模块'])
@@ -158,10 +159,15 @@ async def follow_user(
} }
if action == 'follower': if action == 'follower':
await db_crud.create(data=record, filters=record) await db_crud.create(data=record, filters=record)
elif action == 'unfollower':
if action == 'unfollower':
await db_crud.delete(filters=record) await db_crud.delete(filters=record)
await update_user_follower_total(
follower_id=login_user.user_id,
followed_id=follower_user.user_id,
db_session=db_session
)
return {'message': 'ok'} return {'message': 'ok'}

View File

@@ -7,11 +7,12 @@
from datetime import date from datetime import date
from typing import Optional from typing import Optional
from fastapi.openapi.models import Schema from pydantic import constr
from pydantic import BaseModel, constr
from core.base.schema import BaseMixin
class User(BaseModel): class User(BaseMixin):
nickname: str nickname: str
user_id: int user_id: int
gender: Optional[int] = None gender: Optional[int] = None
@@ -22,9 +23,13 @@ class User(BaseModel):
vip_expire_date: Optional[date] = None vip_expire_date: Optional[date] = None
points: Optional[int] = 0 points: Optional[int] = 0
displayed_card_total: Optional[int] = 0 displayed_card_total: Optional[int] = 0
description: Optional[str | None] = None
last_active_at: Optional[str | None] = None
fans_count: Optional[int] = 0
followers_count: Optional[int] = 0
class LiteUser(BaseModel): class LiteUser(BaseMixin):
nickname: str nickname: str
user_id: int user_id: int
gender: Optional[int] = None gender: Optional[int] = None
@@ -35,16 +40,20 @@ class LiteUser(BaseModel):
class VisitUser(LiteUser): class VisitUser(LiteUser):
is_follow: Optional[bool] = False is_follow: Optional[bool] = False
displayed_card_total: Optional[int] = 0 displayed_card_total: Optional[int] = 0
description: Optional[str | None] = None
last_active_at: Optional[str | None] = None
fans_count: Optional[int] = 0
class UserFollows(BaseModel): class UserFollows(BaseMixin):
nickname: str nickname: str
user_id: int user_id: int
gender: Optional[int] = None gender: Optional[int] = None
avatar_url: Optional[int] = None avatar_url: Optional[int] = None
class UpdateUserIn(BaseModel): class UpdateUserIn(BaseMixin):
nickname: Optional[constr(max_length=20)] = None nickname: Optional[constr(max_length=20)] = None
gender: Optional[int] = None gender: Optional[int] = None
avatar_url: Optional[constr(pattern='^http[a-zA-Z0-9:/\.]+')] = None avatar_url: Optional[constr(pattern='^http[a-zA-Z0-9:/\.]+')] = None
description: Optional[constr(max_length=200) | None] = None