feat(auth、user):新增用户的粉丝数、关注数、简介
This commit is contained in:
@@ -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',
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
62
core/routers/user/actions.py
Normal file
62
core/routers/user/actions.py
Normal 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,
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -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'}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user