diff --git a/README.md b/README.md
index e49f5b46..0a283fd4 100644
--- a/README.md
+++ b/README.md
@@ -135,6 +135,11 @@ Execute tests via pytest
pytest -vs --disable-warnings
```
+## Contributors
+
+
+
+
## Thanks
- [FastAPI](https://fastapi.tiangolo.com/)
@@ -144,6 +149,21 @@ Execute tests via pytest
- [Ruff](https://beta.ruff.rs/docs/)
- ......
+## Sponsor
+
+> If this program has helped you, you can sponsor the author with some coffee beans :coffee:
+
+
+
+
+ |
+ |
+
+ | Wechat Pay |
+ Ali Pay |
+
+
+
## License
This project is licensed under the terms of the MIT license
diff --git a/README.zh-CN.md b/README.zh-CN.md
index 90e18b38..ac8b1311 100644
--- a/README.zh-CN.md
+++ b/README.zh-CN.md
@@ -131,7 +131,12 @@
```shell
pytest -vs --disable-warnings
- ```
+ ```
+
+## 贡献者
+
+
+
## 鸣谢
@@ -142,6 +147,21 @@
- [Ruff](https://beta.ruff.rs/docs/)
- ......
+## 赞助
+
+> 如果此项目能够帮助到你,你可以赞助作者一些咖啡豆表示鼓励 :coffee:
+
+
+
+
+ |
+ |
+
+ | 微信 |
+ 支付宝 |
+
+
+
## 许可证
本项目根据 MIT 许可证的条款进行许可
diff --git a/backend/app/api/v1/user.py b/backend/app/api/v1/user.py
index 72074f06..ba8e96e2 100644
--- a/backend/app/api/v1/user.py
+++ b/backend/app/api/v1/user.py
@@ -9,7 +9,15 @@ from backend.app.common.jwt import DependsJwtAuth
from backend.app.common.pagination import paging_data, PageDepends
from backend.app.common.response.response_schema import response_base
from backend.app.database.db_mysql import CurrentSession
-from backend.app.schemas.user import CreateUser, GetAllUserInfo, ResetPassword, UpdateUser, Avatar, GetCurrentUserInfo
+from backend.app.schemas.user import (
+ CreateUser,
+ GetAllUserInfo,
+ ResetPassword,
+ UpdateUser,
+ Avatar,
+ GetCurrentUserInfo,
+ UpdateUserRole,
+)
from backend.app.services.user_service import UserService
from backend.app.utils.serializers import select_to_json
@@ -31,7 +39,7 @@ async def password_reset(request: Request, obj: ResetPassword):
@router.get('/me', summary='获取当前用户信息', dependencies=[DependsJwtAuth])
-async def current_userinfo(request: Request):
+async def get_current_userinfo(request: Request):
data = GetCurrentUserInfo(**select_to_json(request.user))
return await response_base.success(data=data, exclude={'password'})
@@ -51,6 +59,12 @@ async def update_userinfo(request: Request, username: str, obj: UpdateUser):
return await response_base.fail()
+@router.put('/{username}/role', summary='更新用户角色', dependencies=[DependsRBAC])
+async def update_user_role(request: Request, username: str, obj: UpdateUserRole):
+ await UserService.update_role(request=request, username=username, obj=obj)
+ return await response_base.success()
+
+
@router.put('/{username}/avatar', summary='更新头像', dependencies=[DependsJwtAuth])
async def update_avatar(request: Request, username: str, avatar: Avatar):
count = await UserService.update_avatar(request=request, username=username, avatar=avatar)
@@ -72,7 +86,7 @@ async def get_all_users(
return await response_base.success(data=page_data)
-@router.post('/{pk}/super', summary='修改用户超级权限', dependencies=[DependsRBAC])
+@router.put('/{pk}/super', summary='修改用户超级权限', dependencies=[DependsRBAC])
async def super_set(request: Request, pk: int):
count = await UserService.update_permission(request=request, pk=pk)
if count > 0:
@@ -80,7 +94,7 @@ async def super_set(request: Request, pk: int):
return await response_base.fail()
-@router.post('/{pk}/action', summary='修改用户状态', dependencies=[DependsRBAC])
+@router.put('/{pk}/status', summary='修改用户状态', dependencies=[DependsRBAC])
async def status_set(request: Request, pk: int):
count = await UserService.update_status(request=request, pk=pk)
if count > 0:
@@ -88,7 +102,7 @@ async def status_set(request: Request, pk: int):
return await response_base.fail()
-@router.post('/{pk}/multi', summary='修改用户多点登录状态', dependencies=[DependsRBAC])
+@router.put('/{pk}/multi', summary='修改用户多点登录状态', dependencies=[DependsRBAC])
async def multi_set(request: Request, pk: int):
count = await UserService.update_multi_login(request=request, pk=pk)
if count > 0:
diff --git a/backend/app/crud/base.py b/backend/app/crud/base.py
index dfd37da0..7af9da0a 100644
--- a/backend/app/crud/base.py
+++ b/backend/app/crud/base.py
@@ -36,7 +36,8 @@ class CRUDBase(Generic[ModelType, CreateSchemaType, UpdateSchemaType]):
:param del_flag:
:return:
"""
- assert pk is not None or name is not None, '查询错误, pk 和 name 参数不能同时为空'
+ assert pk is not None or name is not None, '查询错误, pk 和 name 参数不能同时存在'
+ assert pk is None or name is None, '查询错误, pk 和 name 参数不能同时为空'
where_list = [self.model.id == pk] if pk is not None else [self.model.name == name]
if status is not None:
assert status in (0, 1), '查询错误, status 参数只能为 0 或 1'
diff --git a/backend/app/crud/crud_dept.py b/backend/app/crud/crud_dept.py
index 9f78f994..b283f048 100644
--- a/backend/app/crud/crud_dept.py
+++ b/backend/app/crud/crud_dept.py
@@ -16,7 +16,7 @@ class CRUDDept(CRUDBase[Dept, CreateDept, UpdateDept]):
return await self.get_(db, pk=dept_id, del_flag=0)
async def get_by_name(self, db: AsyncSession, name: str) -> Dept | None:
- return await self.get_(db, name=name)
+ return await self.get_(db, name=name, del_flag=0)
async def get_all(
self, db: AsyncSession, name: str = None, leader: str = None, phone: str = None, status: int = None
diff --git a/backend/app/crud/crud_user.py b/backend/app/crud/crud_user.py
index 6c0236e2..efe5794d 100644
--- a/backend/app/crud/crud_user.py
+++ b/backend/app/crud/crud_user.py
@@ -11,7 +11,7 @@ from sqlalchemy.sql import Select
from backend.app.common import jwt
from backend.app.crud.base import CRUDBase
from backend.app.models import User, Role
-from backend.app.schemas.user import CreateUser, UpdateUser, Avatar
+from backend.app.schemas.user import CreateUser, UpdateUser, Avatar, UpdateUserRole
class CRUDUser(CRUDBase[User, CreateUser, UpdateUser]):
@@ -35,13 +35,15 @@ class CRUDUser(CRUDBase[User, CreateUser, UpdateUser]):
role_list = []
for role_id in create.roles:
role_list.append(await db.get(Role, role_id))
- new_user.roles.append(*role_list)
+ new_user.roles.extend(role_list)
db.add(new_user)
async def update_userinfo(self, db: AsyncSession, input_user: User, obj: UpdateUser) -> int:
- user = await db.execute(
- update(self.model).where(self.model.id == input_user.id).values(**obj.dict(exclude={'roles'}))
- )
+ user = await db.execute(update(self.model).where(self.model.id == input_user.id).values(**obj.dict()))
+ return user.rowcount
+
+ @staticmethod
+ async def update_role(db: AsyncSession, input_user: User, obj: UpdateUserRole) -> None:
# 删除用户所有角色
for i in list(input_user.roles):
input_user.roles.remove(i)
@@ -49,11 +51,10 @@ class CRUDUser(CRUDBase[User, CreateUser, UpdateUser]):
role_list = []
for role_id in obj.roles:
role_list.append(await db.get(Role, role_id))
- input_user.roles.append(*role_list)
- return user.rowcount
+ input_user.roles.extend(role_list)
async def update_avatar(self, db: AsyncSession, current_user: User, avatar: Avatar) -> int:
- user = await db.execute(update(self.model).where(self.model.id == current_user.id).values(avatar=avatar))
+ user = await db.execute(update(self.model).where(self.model.id == current_user.id).values(avatar=avatar.url))
return user.rowcount
async def delete(self, db: AsyncSession, user_id: int) -> int:
diff --git a/backend/app/models/sys_dept.py b/backend/app/models/sys_dept.py
index bcc487b1..b76ba63f 100644
--- a/backend/app/models/sys_dept.py
+++ b/backend/app/models/sys_dept.py
@@ -14,7 +14,7 @@ class Dept(Base):
__tablename__ = 'sys_dept'
id: Mapped[id_key] = mapped_column(init=False)
- name: Mapped[str] = mapped_column(String(50), unique=True, comment='部门名称')
+ name: Mapped[str] = mapped_column(String(50), comment='部门名称')
level: Mapped[int] = mapped_column(default=0, comment='部门层级')
sort: Mapped[int] = mapped_column(default=0, comment='排序')
leader: Mapped[str | None] = mapped_column(String(20), default=None, comment='负责人')
diff --git a/backend/app/schemas/user.py b/backend/app/schemas/user.py
index 481f4ed2..45a22883 100644
--- a/backend/app/schemas/user.py
+++ b/backend/app/schemas/user.py
@@ -21,7 +21,7 @@ class AuthLogin(Auth):
class CreateUser(Auth):
- dept_id: int
+ dept_id: int | None = None
roles: list[int]
nickname: str
email: str = Field(..., example='user@example.com')
@@ -36,7 +36,7 @@ class CreateUser(Auth):
class _UserInfoBase(SchemaBase):
- dept_id: int
+ dept_id: int | None = None
username: str
nickname: str
email: str = Field(..., example='user@example.com')
@@ -58,6 +58,10 @@ class _UserInfoBase(SchemaBase):
class UpdateUser(_UserInfoBase):
+ pass
+
+
+class UpdateUserRole(SchemaBase):
roles: list[int]
diff --git a/backend/app/services/user_service.py b/backend/app/services/user_service.py
index b4c6b945..5ce23937 100644
--- a/backend/app/services/user_service.py
+++ b/backend/app/services/user_service.py
@@ -15,7 +15,7 @@ from backend.app.crud.crud_role import RoleDao
from backend.app.crud.crud_user import UserDao
from backend.app.database.db_mysql import async_db_session
from backend.app.models import User
-from backend.app.schemas.user import CreateUser, ResetPassword, UpdateUser, Avatar
+from backend.app.schemas.user import CreateUser, ResetPassword, UpdateUser, Avatar, UpdateUserRole
class UserService:
@@ -82,17 +82,30 @@ class UserService:
dept = await DeptDao.get(db, obj.dept_id)
if not dept:
raise errors.NotFoundError(msg='部门不存在')
- for role_id in obj.roles:
- role = await RoleDao.get(db, role_id)
- if not role:
- raise errors.NotFoundError(msg='角色不存在')
count = await UserDao.update_userinfo(db, input_user, obj)
return count
+ @staticmethod
+ async def update_role(*, request: Request, username: str, obj: UpdateUserRole) -> None:
+ async with async_db_session.begin() as db:
+ if not request.user.is_superuser:
+ if request.user.username != username:
+ raise errors.ForbiddenError(msg='你只能修改自己的角色')
+ input_user = await UserDao.get_with_relation(db, username=username)
+ if not input_user:
+ raise errors.NotFoundError(msg='用户不存在')
+ for role_id in obj.roles:
+ role = await RoleDao.get(db, role_id)
+ if not role:
+ raise errors.NotFoundError(msg='角色不存在')
+ await UserDao.update_role(db, input_user, obj)
+
@staticmethod
async def update_avatar(*, request: Request, username: str, avatar: Avatar) -> int:
async with async_db_session.begin() as db:
- await jwt.superuser_verify(request)
+ if not request.user.is_superuser:
+ if request.user.username != username:
+ raise errors.ForbiddenError(msg='你只能修改自己的头像')
input_user = await UserDao.get_by_username(db, username)
if not input_user:
raise errors.NotFoundError(msg='用户不存在')
@@ -111,7 +124,7 @@ class UserService:
raise errors.NotFoundError(msg='用户不存在')
else:
if pk == request.user.id:
- raise errors.ForbiddenError(msg='禁止修改自身权限')
+ raise errors.ForbiddenError(msg='禁止修改自身管理员权限')
count = await UserDao.set_super(db, pk)
return count
diff --git a/backend/app/utils/redis_info.py b/backend/app/utils/redis_info.py
index fbc82531..54442a86 100644
--- a/backend/app/utils/redis_info.py
+++ b/backend/app/utils/redis_info.py
@@ -5,7 +5,6 @@ from backend.app.utils.server_info import server_info
class RedisInfo:
-
@staticmethod
async def get_info():
info = await redis_client.info()
diff --git a/backend/sql/init_test_data.sql b/backend/sql/init_test_data.sql
index 51b4df88..65016232 100644
--- a/backend/sql/init_test_data.sql
+++ b/backend/sql/init_test_data.sql
@@ -22,6 +22,7 @@ values (1, 'test', 0, 0, null, null, 0, null, null, 1, null, null, '2023-06-26
(18, 'monitor', 0, 88, 'icon-computer', null, 0, null, null, 1, null, null, '2023-07-11 20:20:20', null, 1, 1, '系统监控'),
(19, 'Redis', 0, 0, null, null, 1, '/monitor/redis/index.vue', null, 1, null, 18, '2023-07-11 20:21:28', null, 1, 1, 'Redis监控'),
(20, 'Server', 0, 0, null, 'server', 1, '/monitor/server/index.vue', null, 1, null, 18, '2023-07-11 20:23:43', null, 1, 1, '服务器监控');
+ (21, 'SysUser', 0, 0, null, 'sys-user', 1, '/admin/user/index.vue', null, 1, null, 8, '2023-07-13 03:32:47', null, 1, 1, '用户管理');
INSERT INTO fba.sys_role (id, name, data_scope, status, remark, created_time, updated_time)
VALUES (1, 'test', 2, 1, null, '2023-06-26 17:13:45', null);