diff --git a/alembic/README.md b/alembic/README.md new file mode 100644 index 0000000..005e36e --- /dev/null +++ b/alembic/README.md @@ -0,0 +1,13 @@ +# 数据库迁移文件 + +## 生成数据库迁移文件 + +```shell +alembic revision --autogenerate -m "create table user" +``` + +## 执行数据库迁移 + +```shell +alembic upgrade head +``` diff --git a/alembic/versions/3785e9a2a0c0_users.py b/alembic/versions/1d50ca81be81_users.py similarity index 95% rename from alembic/versions/3785e9a2a0c0_users.py rename to alembic/versions/1d50ca81be81_users.py index ef92fd1..5b25557 100644 --- a/alembic/versions/3785e9a2a0c0_users.py +++ b/alembic/versions/1d50ca81be81_users.py @@ -1,8 +1,8 @@ """users -Revision ID: 3785e9a2a0c0 +Revision ID: 1d50ca81be81 Revises: -Create Date: 2024-11-04 19:12:43.374773 +Create Date: 2024-11-07 13:45:48.829209 """ @@ -12,7 +12,7 @@ import sqlmodel from fastapi_user_auth.utils.sqltypes import SecretStrType # revision identifiers, used by Alembic. -revision = "3785e9a2a0c0" +revision = "1d50ca81be81" down_revision = None branch_labels = None depends_on = None @@ -162,6 +162,12 @@ def upgrade() -> None: sqlmodel.sql.sqltypes.AutoString(length=255), nullable=True, ), + sa.Column("sex", sqlmodel.sql.sqltypes.AutoString(length=15), nullable=True), + sa.Column( + "real_name", + sqlmodel.sql.sqltypes.AutoString(length=15), + nullable=True, + ), sa.Column("phone", sqlmodel.sql.sqltypes.AutoString(length=15), nullable=True), sa.Column( "student_id", diff --git a/src/route/users.py b/src/route/users.py index c6e4bb5..2b2f83e 100644 --- a/src/route/users.py +++ b/src/route/users.py @@ -34,22 +34,22 @@ class UserRoutes(Plugin): @handler.post("/register", admin=False) async def register(self, data: UserRegIn): if data.username.upper() in SystemUserEnum.__members__: - return BaseApiOut(status=500, msg="用户名已被注册", data=None) + return BaseApiOut(status=500, msg="用户名已被注册") user = await self.user_services.get_user(username=data.username) if user: - return BaseApiOut(status=500, msg="用户名已被注册", data=None) + return BaseApiOut(status=500, msg="用户名已被注册") role = UserRoleEnum.STUDENT.value if not (data.student_id or data.phone): - return BaseApiOut(status=500, msg="学号或手机号至少填写一项", data=None) + return BaseApiOut(status=500, msg="学号或手机号至少填写一项") if data.student_id: user = await self.user_services.get_user(student_id=data.student_id) if user: - return BaseApiOut(status=500, msg="学号已被注册", data=None) + return BaseApiOut(status=500, msg="学号已被注册") role = UserRoleEnum.STUDENT.value if data.phone: user = await self.user_services.get_user(phone=data.phone) if user: - return BaseApiOut(status=500, msg="手机号已被注册", data=None) + return BaseApiOut(status=500, msg="手机号已被注册") role = UserRoleEnum.OUT.value # 检查通过,注册用户 try: @@ -58,6 +58,7 @@ class UserRoutes(Plugin): password=data.password, student_id=data.student_id, phone=data.phone, + real_name=data.real_name, ) if not await self.user_role_services.is_user_in_role_group( data.username, role @@ -124,7 +125,3 @@ class UserRoutes(Plugin): ) response.set_cookie("Authorization", f"bearer {token_info.access_token}") return BaseApiOut(code=0, data=token_info) - - @handler.get("/need_login", student=True, out=True) - async def need_login(self): - return {} diff --git a/src/services/users/models.py b/src/services/users/models.py index 8a9d565..27de668 100644 --- a/src/services/users/models.py +++ b/src/services/users/models.py @@ -13,7 +13,15 @@ class PhoneMixin(SQLModel): phone: Optional[str] = Field("", title="电话号码", max_length=15) -class UserModel(BaseUser, StudentIdMixin, PhoneMixin, table=True): - __table_args__ = {"extend_existing": True} +class RealNameMixin(SQLModel): + real_name: Optional[str] = Field("", title="真实姓名", max_length=15) - email: Optional[str] = None + +class SexMixin(SQLModel): + sex: Optional[str] = Field("", title="性别", max_length=15) + + +class UserModel( + BaseUser, StudentIdMixin, PhoneMixin, RealNameMixin, SexMixin, table=True +): + __table_args__ = {"extend_existing": True} diff --git a/src/services/users/repositories.py b/src/services/users/repositories.py index 9bd5e1d..5bd71f8 100644 --- a/src/services/users/repositories.py +++ b/src/services/users/repositories.py @@ -3,7 +3,6 @@ from typing import Optional from fastapi_user_auth.auth import Auth from fastapi_user_auth.auth.backends.redis import RedisTokenStore from fastapi_user_auth.auth.models import CasbinRule, LoginHistory -from fastapi_user_auth.utils.casbin import update_subject_roles from persica.factory.component import AsyncInitializingComponent from pydantic import SecretStr from sqlmodel import select @@ -44,6 +43,7 @@ class UserRepo(AsyncInitializingComponent): password: SecretStr, student_id: Optional[str], phone: Optional[str], + real_name: Optional[str], ): password = self.AUTH.pwd_context.hash(password.get_secret_value()) values = { @@ -51,6 +51,7 @@ class UserRepo(AsyncInitializingComponent): "password": password, "student_id": student_id, "phone": phone, + "real_name": real_name, } user = self.user_model.model_validate(values) async with AsyncSession(self.engine) as session: diff --git a/src/services/users/schemas.py b/src/services/users/schemas.py index 4055ba6..d96a26f 100644 --- a/src/services/users/schemas.py +++ b/src/services/users/schemas.py @@ -8,7 +8,7 @@ from fastapi_user_auth.utils.sqltypes import SecretStrType from pydantic import BaseModel, SecretStr from sqlmodel import Field -from .models import UserModel, StudentIdMixin, PhoneMixin +from .models import UserModel, StudentIdMixin, PhoneMixin, RealNameMixin, SexMixin class BaseTokenData(BaseModel): @@ -35,7 +35,13 @@ class UserLoginOut(UserModel): ) -class UserRegIn(UsernameMixin, PasswordMixin, StudentIdMixin, PhoneMixin): +class UserRegIn( + UsernameMixin, + PasswordMixin, + StudentIdMixin, + PhoneMixin, + RealNameMixin, +): """用户注册""" password2: str = Field(title=_("Confirm Password"), max_length=128) diff --git a/src/services/users/services.py b/src/services/users/services.py index 6d6033c..f6fa22b 100644 --- a/src/services/users/services.py +++ b/src/services/users/services.py @@ -23,12 +23,14 @@ class UserServices(AsyncInitializingComponent): password: SecretStr, student_id: Optional[str], phone: Optional[str], + real_name: Optional[str], ): return await self.repo.register_user( username, password, student_id, phone, + real_name, ) async def login_user(self, user: "UserModel") -> str: