feat: user admin add

This commit is contained in:
xtaodada 2024-11-13 15:27:41 +08:00
parent dbc3885f9f
commit 293f3ba937
Signed by: xtaodada
GPG Key ID: 4CBB3F4FA8C85659
9 changed files with 158 additions and 41 deletions

View File

@ -84,9 +84,9 @@
methods: {
// ok
ok: function() {
sa.ajax2('/user/add', function(res) {
sa.alert('数据添加, 参数为:' + JSON.stringify(this.m));
}.bind(this))
sa.ajax('/user/admin/add', this.m, function(res) {
sa.alert('数据添加成功');
}.bind(this), {})
}
}
})

View File

@ -113,7 +113,7 @@
<el-table-column prop="address" label="操作">
<template slot-scope="s">
<el-button class="c-btn" type="success" icon="el-icon-view" @click="get(s.row)">详情</el-button>
<el-button class="c-btn" type="danger" icon="el-icon-delete" @click="del(s.row)">禁用</el-button>
<el-button class="c-btn" type="danger" icon="el-icon-delete" @click="del(s.row)">/启</el-button>
</template>
</el-table-column>
</el-table>
@ -165,6 +165,7 @@
sa.ajax('/user/admin/list', this.p, function(res){
this.dataList = res.data.data; // 数据
for (let i = 0; i < this.dataList.length; i++) {
this.dataList[i].avatar = this.dataList[i].avatar ? this.dataList[i].avatar : '/user/avatar/default';
this.dataList[i].status = this.dataList[i].is_active ? 1 : 0;
}
this.dataCount = res.data.count; // 分页
@ -202,12 +203,16 @@
},
// 删除
del: function(data) {
sa.confirm('是否删除,此操作不可撤销', function() {
sa.ajax2('/user/delete?id=' + data.id, function(res) {
sa.arrayDelete(this.dataList, data);
sa.ok('删除成功');
sa.f5TableHeight(); // 刷新表格高度
}.bind(this))
sa.confirm('是否修改,此操作可撤销', function() {
sa.ajax('/user/admin/change_status', {user_id: data.id, is_active: !data.is_active}, function(res) {
// sa.arrayDelete(this.dataList, data);
if (data.is_active) {
sa.ok('禁用成功');
} else {
sa.ok('启用成功');
}
this.f5();
}.bind(this), {})
}.bind(this));
},
// 批量删除
@ -219,8 +224,8 @@
return sa.msg('请至少选择一条数据')
}
// 提交删除
sa.confirm('是否批量删除选中数据?此操作不可撤销', function() {
sa.ajax2('/SysType/deleteByIds', {ids: ids.join(',')}, function(res) {
sa.confirm('是否批量禁用选中用户?此操作可撤销', function() {
sa.ajax2('/user/admin/change_status', {ids: ids}, function(res) {
sa.arrayDelete(this.dataList, selection);
sa.ok('删除成功');
sa.f5TableHeight(); // 刷新表格高度

View File

@ -1,5 +1,5 @@
import datetime
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Union
from fastapi_amis_admin.crud import BaseApiOut
@ -17,6 +17,7 @@ from src.services.users.schemas import (
UserLoginOut,
UserRoleEnum,
CreateTypeEnum,
UserAdminAdd,
)
from src.services.users.services import UserServices, UserRoleServices
@ -33,30 +34,31 @@ class UserRoutes(Plugin):
self.user_services = user_services
self.user_role_services = user_role_services
@handler.post("/register", admin=False)
async def register(self, data: UserRegIn):
if data.username.upper() in SystemUserEnum.__members__:
async def before_reg(self, username: str, student_id: str, phone: str):
if username.upper() in SystemUserEnum.__members__:
return BaseApiOut(status=500, msg="用户名已被注册")
user = await self.user_services.get_user(username=data.username)
user = await self.user_services.get_user(username=username)
if user:
return BaseApiOut(status=500, msg="用户名已被注册")
role = UserRoleEnum.STUDENT.value
create_type = CreateTypeEnum.STUDENT
if not (data.student_id or data.phone):
if not (student_id or phone):
return BaseApiOut(status=500, msg="学号或手机号至少填写一项")
if data.student_id:
user = await self.user_services.get_user(student_id=data.student_id)
if student_id:
user = await self.user_services.get_user(student_id=student_id)
if user:
return BaseApiOut(status=500, msg="学号已被注册")
role = UserRoleEnum.STUDENT.value
create_type = CreateTypeEnum.STUDENT
if data.phone:
user = await self.user_services.get_user(phone=data.phone)
if phone:
user = await self.user_services.get_user(phone=phone)
if user:
return BaseApiOut(status=500, msg="手机号已被注册")
role = UserRoleEnum.OUT.value
create_type = CreateTypeEnum.PHONE
# 检查通过,注册用户
return None
async def create_user(
self,
data: Union[UserRegIn, UserAdminAdd],
create_type: CreateTypeEnum,
role: UserRoleEnum,
):
sex = "" if not hasattr(data, "sex") else data.sex
try:
user = await self.user_services.register_user(
username=data.username,
@ -64,19 +66,39 @@ class UserRoutes(Plugin):
student_id=data.student_id,
phone=data.phone,
real_name=data.real_name,
sex=sex,
create_type=create_type.value,
)
if not await self.user_role_services.is_user_in_role_group(
data.username, role
data.username,
role.value,
):
await self.user_role_services.add_user_to_role_group(
data.username, role
data.username,
role.value,
)
return user
except Exception as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Error Execute SQL{e}",
) from e
@handler.post("/register", admin=False)
async def register(self, data: UserRegIn):
check = await self.before_reg(data.username, data.student_id, data.phone)
if check:
return check
role = UserRoleEnum.STUDENT
create_type = CreateTypeEnum.STUDENT
if data.student_id:
role = UserRoleEnum.STUDENT
create_type = CreateTypeEnum.STUDENT
if data.phone:
role = UserRoleEnum.OUT
create_type = CreateTypeEnum.PHONE
# 检查通过,注册用户
user = await self.create_user(data, create_type, role)
# 注册成功,设置用户信息
token_info = UserLoginOut.model_validate(user)
token_info.access_token = await self.user_services.login_user(user)

View File

@ -4,18 +4,27 @@ from starlette import status
from src.plugin import handler
from src.plugin.plugin import Plugin
from src.services.users.schemas import UserList
from src.route.users import UserRoutes
from src.services.users.schemas import (
UserList,
DisableOrEnableUser,
UserAdminAdd,
UserRoleEnum,
CreateTypeEnum,
)
from src.services.users.services import UserServices
class UserRoutes(Plugin):
class UserAdminRoutes(Plugin):
_prefix = "/user/admin"
def __init__(
self,
user_services: UserServices,
user_routes: UserRoutes,
):
self.user_services = user_services
self.user_routes = user_routes
@handler.post("/list", student=True)
async def get_user_list(self, data: UserList):
@ -50,3 +59,31 @@ class UserRoutes(Plugin):
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Error Execute SQL{e}",
) from e
@handler.post("/change_status", student=True)
async def disable_or_enable_user(self, data: DisableOrEnableUser):
try:
if data.is_active:
result = await self.user_services.enable_user(data.user_id)
else:
result = await self.user_services.disable_user(data.user_id)
if not result:
return BaseApiOut(code=500, msg="操作失败")
return BaseApiOut(code=0, msg="请求成功", data={})
except Exception as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Error Execute SQL{e}",
) from e
@handler.post("/add", student=True)
async def add_user(self, data: UserAdminAdd):
check = await self.user_routes.before_reg(
data.username, data.student_id, data.phone
)
if check:
return check
role = data.role_id
create_type = CreateTypeEnum.ADMIN
user = await self.user_routes.create_user(data, create_type, role)
return BaseApiOut(code=0, msg="注册成功", data=user)

View File

@ -11,6 +11,7 @@ from src.plugin.plugin import Plugin
from src.services.users.models import UserModel
from src.services.users.schemas import UserUpdate, UserUpdateAvatar
from src.services.users.services import UserServices, UserRoleServices
from src.utils import AVATAR_DATA_PATH
from src.utils.upload_file import get_avatar, save_avatar, check_avatar
@ -25,6 +26,7 @@ class UserUpdateRoutes(Plugin):
self.user_services = user_services
self.user_role_services = user_role_services
self.avatar_path = "/user/avatar/"
self.avatar_path_default = AVATAR_DATA_PATH / "avatar.jpg"
@handler.get("/me", student=True, out=True)
async def get_me(self, request: Request):
@ -69,6 +71,10 @@ class UserUpdateRoutes(Plugin):
detail=f"Error Execute SQL{e}",
) from e
@handler.get("/avatar/default", student=True, out=True)
async def get_default_avatar(self):
return FileResponse(self.avatar_path_default)
@handler.get("/avatar/{uid}/{file_path}", student=True, out=True)
async def get_avatar(self, request: Request, uid: int, file_path: str):
can_see = False

View File

@ -46,6 +46,7 @@ class UserRepo(AsyncInitializingComponent):
student_id: Optional[str],
phone: Optional[str],
real_name: Optional[str],
sex: Optional[str],
create_type: int,
):
password = self.AUTH.pwd_context.hash(password.get_secret_value())
@ -55,6 +56,7 @@ class UserRepo(AsyncInitializingComponent):
"student_id": student_id,
"phone": phone,
"real_name": real_name,
"sex": sex,
"create_type": create_type,
}
user = self.user_model.model_validate(values)
@ -66,12 +68,15 @@ class UserRepo(AsyncInitializingComponent):
async def get_user(
self,
user_id: Optional[int] = None,
username: Optional[str] = None,
student_id: Optional[str] = None,
phone: Optional[str] = None,
) -> Optional[UserModel]:
async with AsyncSession(self.engine) as session:
statement = select(self.user_model)
if user_id:
statement = statement.where(self.user_model.id == user_id)
if username:
statement = statement.where(self.user_model.username == username)
if student_id:
@ -159,8 +164,8 @@ class UserRepo(AsyncInitializingComponent):
phone: str,
student_id: str,
create_type: int,
start_time: datetime,
end_time: datetime,
start_time: Optional[datetime],
end_time: Optional[datetime],
page_no: int,
page_size: int,
) -> Tuple[Sequence[UserModel], int]:

View File

@ -128,19 +128,42 @@ class UserList(BaseModel):
create_type: CreateTypeEnum = CreateTypeEnum.ALL
sortType: int = 1
start_time: str
end_time: str
start_time: Optional[str] = ""
end_time: Optional[str] = ""
pageNo: int = 1
pageSize: int = 10
@property
def start(self):
if not self.start_time:
return None
y, m, d = self.start_time.split("-")
start_time = date(int(y), int(m), int(d))
return datetime.combine(start_time, datetime.min.time())
@property
def end(self):
if not self.end_time:
return None
y, m, d = self.end_time.split("-")
end_time = date(int(y), int(m), int(d))
return datetime.combine(end_time, datetime.max.time())
class DisableOrEnableUser(BaseModel):
user_id: int
is_active: bool = False
class UserAdminAdd(
UsernameMixin,
RealNameMixin,
PasswordMixin,
StudentIdMixin,
PhoneMixin,
SexMixin,
):
"""管理员添加"""
nickname: Optional[str] = Field("", title=_("Nickname"), max_length=40)
role_id: UserRoleEnum

View File

@ -26,6 +26,7 @@ class UserServices(AsyncInitializingComponent):
student_id: Optional[str],
phone: Optional[str],
real_name: Optional[str],
sex: Optional[str],
create_type: int = 0,
):
return await self.repo.register_user(
@ -34,6 +35,7 @@ class UserServices(AsyncInitializingComponent):
student_id,
phone,
real_name,
sex,
create_type,
)
@ -42,11 +44,12 @@ class UserServices(AsyncInitializingComponent):
async def get_user(
self,
user_id: Optional[int] = None,
username: Optional[str] = None,
student_id: Optional[str] = None,
phone: Optional[str] = None,
) -> Optional[UserModel]:
return await self.repo.get_user(username, student_id, phone)
return await self.repo.get_user(user_id, username, student_id, phone)
async def create_login_history(
self, user: "UserModel", ip: str, ua: str, forwarded_for: str
@ -108,8 +111,8 @@ class UserServices(AsyncInitializingComponent):
phone: str,
student_id: str,
create_type: int,
start_time: datetime,
end_time: datetime,
start_time: Optional[datetime],
end_time: Optional[datetime],
page_no: int,
page_size: int,
) -> Tuple[Sequence[UserModel], int]:
@ -127,6 +130,22 @@ class UserServices(AsyncInitializingComponent):
page_size,
)
async def disable_user(self, user_id: int) -> bool:
user = await self.get_user(user_id)
if not user or not user.is_active:
return False
user.is_active = False
await self.repo.update_user(user)
return True
async def enable_user(self, user_id: int) -> bool:
user = await self.get_user(user_id)
if not user or user.is_active:
return False
user.is_active = True
await self.repo.update_user(user)
return True
class UserRoleServices(AsyncInitializingComponent):
__order__ = 1

View File

@ -1 +1 @@
from ._path import PROJECT_ROOT, SERVICES_PATH, FRONTEND_PATH
from ._path import PROJECT_ROOT, SERVICES_PATH, FRONTEND_PATH, AVATAR_DATA_PATH