diff --git a/backend/apps/system/api/workspace.py b/backend/apps/system/api/workspace.py index 926a46a8..38444a59 100644 --- a/backend/apps/system/api/workspace.py +++ b/backend/apps/system/api/workspace.py @@ -1,6 +1,8 @@ from typing import Optional from fastapi import APIRouter, HTTPException, Query -from sqlmodel import exists, or_, select +from sqlmodel import exists, or_, select, delete as sqlmodel_delete +from apps.system.crud.user import clean_user_cache +from apps.system.crud.workspace import reset_single_user_oid, reset_user_oid from apps.system.models.system_model import UserWsModel, WorkspaceBase, WorkspaceEditor, WorkspaceModel from apps.system.models.user import UserModel from apps.system.schemas.system_schema import UserWsBase, UserWsDTO, UserWsEditor, UserWsOption, WorkspaceUser @@ -118,21 +120,27 @@ async def create(session: SessionDep, current_user: CurrentUser, creator: UserWs }) for uid in creator.uid_list ] + for uid in creator.uid_list: + await reset_single_user_oid(session, uid, oid) + await clean_user_cache(uid) + session.add_all(db_model_list) session.commit() @router.put("/uws") async def edit(session: SessionDep, editor: UserWsEditor): if not editor.oid or not editor.uid: - raise RuntimeError("param [oid, uid] miss") + raise HTTPException("param [oid, uid] miss") db_model = session.exec(select(UserWsModel).where(UserWsModel.uid == editor.uid, UserWsModel.oid == editor.oid)).first() if not db_model: - raise RuntimeError("uws not exist") + raise HTTPException("uws not exist") if editor.weight == db_model.weight: return db_model.weight = editor.weight session.add(db_model) + + await clean_user_cache(editor.uid) session.commit() @router.delete("/uws") @@ -145,6 +153,11 @@ async def delete(session: SessionDep, current_user: CurrentUser, dto: UserWsBase raise HTTPException(f"UserWsModel not found") for db_model in db_model_list: session.delete(db_model) + + for uid in dto.uid_list: + await reset_single_user_oid(session, uid, oid, False) + await clean_user_cache(uid) + session.commit() @router.get("", response_model=list[WorkspaceModel]) @@ -167,7 +180,7 @@ async def update(session: SessionDep, editor: WorkspaceEditor): id = editor.id db_model = session.get(WorkspaceModel, id) if not db_model: - raise ValueError(f"WorkspaceModel with id {id} not found") + raise HTTPException(f"WorkspaceModel with id {id} not found") update_data = WorkspaceModel.model_validate(editor) db_model.sqlmodel_update(update_data) session.add(db_model) @@ -177,16 +190,27 @@ async def update(session: SessionDep, editor: WorkspaceEditor): async def get_one(session: SessionDep, trans: Trans, id: int): db_model = session.get(WorkspaceModel, id) if not db_model: - raise ValueError(f"WorkspaceModel with id {id} not found") + raise HTTPException(f"WorkspaceModel with id {id} not found") if db_model.name.startswith('i18n'): db_model.name = trans(db_model.name) return db_model @router.delete("/{id}") -async def delete(session: SessionDep, id: int): +async def single_delete(session: SessionDep, id: int): db_model = session.get(WorkspaceModel, id) if not db_model: - raise ValueError(f"WorkspaceModel with id {id} not found") + raise HTTPException(f"WorkspaceModel with id {id} not found") + + user_ws_list = session.exec(select(UserWsModel).where(UserWsModel.oid == id)).all() + if user_ws_list: + # clean user cache + for user_ws in user_ws_list: + await clean_user_cache(user_ws.uid) + # reset user default oid + await reset_user_oid(session, id) + # delete user_ws + session.exec(sqlmodel_delete(UserWsModel).where(UserWsModel.oid == id)) + session.delete(db_model) session.commit() diff --git a/backend/apps/system/crud/user.py b/backend/apps/system/crud/user.py index 1431b971..8731aa7e 100644 --- a/backend/apps/system/crud/user.py +++ b/backend/apps/system/crud/user.py @@ -1,7 +1,7 @@ from typing import Optional from sqlmodel import Session, select, delete as sqlmodel_delete - +import logging from apps.system.models.system_model import UserWsModel, WorkspaceModel from apps.system.schemas.auth import CacheName, CacheNamespace from apps.system.schemas.system_schema import BaseUserDTO, UserInfoDTO, UserWs @@ -64,4 +64,8 @@ async def single_delete(session: SessionDep, id: int): del_stmt = sqlmodel_delete(UserWsModel).where(UserWsModel.uid == id) session.exec(del_stmt) session.delete(user_model) - session.commit() \ No newline at end of file + session.commit() + +@clear_cache(namespace=CacheNamespace.AUTH_INFO, cacheName=CacheName.USER_INFO, keyExpression="id") +async def clean_user_cache(id: int): + logging.info(f"User cache for [{id}] has been cleaned") diff --git a/backend/apps/system/crud/workspace.py b/backend/apps/system/crud/workspace.py new file mode 100644 index 00000000..38571fa9 --- /dev/null +++ b/backend/apps/system/crud/workspace.py @@ -0,0 +1,62 @@ + +from typing import Optional +from sqlmodel import Session, func, select, update + +from apps.system.models.system_model import UserWsModel +from apps.system.models.user import UserModel + +async def reset_single_user_oid(session: Session, uid: int, oid: int, add: Optional[bool] = True): + user_model = session.get(UserModel, uid) + if not user_model: + return + origin_oid = user_model.oid + if add and (not origin_oid or origin_oid == 0): + user_model.oid = oid + session.add(user_model) + if not add and origin_oid and origin_oid == oid: + user_model.oid = 0 + user_ws = session.exec(select(UserWsModel).where(UserWsModel.uid == uid, UserWsModel.oid != oid)).first() + if user_ws: + user_model.oid = user_ws.oid + session.add(user_model) + +async def reset_user_oid(session: Session, oid: int): + stmt = ( + select( + UserModel.id, + UserModel.oid, + func.coalesce( + func.array_remove( + func.array_agg(UserWsModel.oid), + None + ), + [] + ).label("oid_list") + ) + .join(UserWsModel, UserModel.id == UserWsModel.uid, isouter=True) + .where(UserModel.id != 1) + .group_by(UserModel.id) + ) + + user_filter = ( + select(UserModel.id) + .join(UserWsModel, UserModel.id == UserWsModel.uid) + .where(UserWsModel.oid == oid) + .distinct() + ) + stmt = stmt.where(UserModel.id.in_(user_filter)) + + result_user_list = session.exec(stmt) + for row in result_user_list: + result_dict = {} + for item, key in zip(row, row._fields): + result_dict[key] = item + + origin_oid = result_dict['oid'] + oid_list: list = list(filter(lambda x: x != oid, result_dict['oid_list'])) + if origin_oid not in oid_list: + result_dict['oid'] = oid_list[0] if oid_list else 0 + if result_dict['oid'] != origin_oid: + result_dict.pop("oid_list", None) + update_stmt = update(UserModel).where(UserModel.id == result_dict['id']).values(oid=result_dict['oid']) + session.exec(update_stmt) diff --git a/backend/common/core/pagination.py b/backend/common/core/pagination.py index 783e3198..5423c7ec 100644 --- a/backend/common/core/pagination.py +++ b/backend/common/core/pagination.py @@ -14,7 +14,7 @@ class Paginator: result_dict = {} for item, key in zip(row, row._fields): if isinstance(item, SQLModel): - result_dict.update(item.dict()) + result_dict.update(item.model_dump()) else: result_dict[key] = item diff --git a/frontend/src/api/workspace.ts b/frontend/src/api/workspace.ts index 729d57c9..2561c43b 100644 --- a/frontend/src/api/workspace.ts +++ b/frontend/src/api/workspace.ts @@ -11,6 +11,6 @@ export const workspaceUwsUpdate = (data: any) => request.put('/system/workspace/ export const workspaceCreate = (data: any) => request.post('/system/workspace', data) export const workspaceUpdate = (data: any) => request.put('/system/workspace', data) export const workspaceUwsDelete = (data: any) => request.delete('/system/workspace/uws', { data }) -export const workspaceDelete = (data: any) => request.delete('/system/workspace', { data }) +export const workspaceDelete = (id: any) => request.delete(`/system/workspace/${id}`) export const workspaceList = () => request.get('/system/workspace') export const workspaceDetail = (id: any) => request.get(`/system/workspace/${id}`) diff --git a/frontend/src/views/system/workspace/index.vue b/frontend/src/views/system/workspace/index.vue index 4b9b633e..09e3e446 100644 --- a/frontend/src/views/system/workspace/index.vue +++ b/frontend/src/views/system/workspace/index.vue @@ -236,9 +236,7 @@ const delWorkspace = (row: any) => { customClass: 'confirm-no_icon', autofocus: false, }).then(() => { - workspaceDelete({ - id: row.id, - }).then(() => { + workspaceDelete(row.id).then(() => { ElMessage({ type: 'success', message: t('dashboard.delete_success'),