From 7a30b179e898bc48032cf44b974b587c974e81b8 Mon Sep 17 00:00:00 2001 From: martsforever Date: Sun, 24 Aug 2025 02:18:27 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=8B=86=E5=88=86project=20model?= =?UTF-8?q?=E4=B8=8Eproject=20service?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/model/ProjectModel.py | 36 ++++++++++++++------- app/model/ProjectService.py | 64 +++++++++++++++++++++++++++++++++++++ app/server.py | 2 +- 3 files changed, 89 insertions(+), 13 deletions(-) create mode 100644 app/model/ProjectService.py diff --git a/app/model/ProjectModel.py b/app/model/ProjectModel.py index 2f78f71..1749439 100644 --- a/app/model/ProjectModel.py +++ b/app/model/ProjectModel.py @@ -1,12 +1,11 @@ +from decimal import Decimal from typing import Optional -from pydantic import computed_field -from sqlalchemy.orm import selectinload -from sqlmodel import Field, Relationship, select +from pydantic import computed_field, PrivateAttr +from sqlmodel import Field, Relationship from app.model.BasicModel import BasicModel from app.model.UserModel import UserServiceModel -from app.utils.create_module_service import create_model_service class ProjectModel(BasicModel, table=True): @@ -17,6 +16,27 @@ class ProjectModel(BasicModel, table=True): budget: float = Field(default=None, description="项目预算金额") version: int = Field(default=None, description="项目版本号") + # 使用 PrivateAttr 声明私有属性 + _spent: Optional[Decimal] = PrivateAttr(default=None) + _balance: Optional[Decimal] = PrivateAttr(default=None) + + # 属性访问器 + @property + def spent(self) -> Optional[Decimal]: + return self._spent + + @spent.setter + def spent(self, value: Optional[Decimal]) -> None: + self._spent = value + + @property + def balance(self) -> Optional[Decimal]: + return self._balance + + @balance.setter + def balance(self, value: Optional[Decimal]) -> None: + self._balance = value + # /*---------------------------------------leader_id-------------------------------------------*/ leader_id: str = Field( @@ -42,11 +62,3 @@ class ProjectModel(BasicModel, table=True): @leader_name.setter def leader_name(self, value: Optional[str]) -> None: pass - - -ProjectService = create_model_service( - Cls=ProjectModel, - custom_query=lambda: select(ProjectModel).options( - selectinload(ProjectModel.leader), - ), -) diff --git a/app/model/ProjectService.py b/app/model/ProjectService.py new file mode 100644 index 0000000..4b58d54 --- /dev/null +++ b/app/model/ProjectService.py @@ -0,0 +1,64 @@ +from decimal import Decimal +from typing import List + +from sqlalchemy.orm import selectinload +from sqlmodel import select + +from app.model.ApproveModel import ApproveService, ApproveModel +from app.model.ProjectModel import ProjectModel +from app.utils.create_module_service import create_model_service +from app.utils.db_utils import AsyncSessionDep + +ProjectService = create_model_service( + Cls=ProjectModel, + custom_query=lambda: select(ProjectModel).options( + selectinload(ProjectModel.leader), + ), + after_query_list=lambda query_cls_list, has_next, query_param, session: set_list_spent(query_cls_list, session), + after_query_item=lambda item_cls, row_dict, session: set_item_spent(item_cls, session), + after_insert=lambda insert_cls, row_dict, session: set_item_spent(insert_cls, session), + after_update=lambda update_cls, row_dict, session: set_item_spent(update_cls, session), + after_batch_insert=lambda refresh_cls_list, row_dict_list, session: set_list_spent(refresh_cls_list, session), + after_batch_update=lambda refresh_cls_list, row_dict_list, session: set_list_spent(refresh_cls_list, session), +) + + +async def set_list_spent(cls_list: List[ProjectModel], session: AsyncSessionDep): + project_dict_list, project_dict_map = await query_proj_spent([item.id for item in cls_list], session) + for item in cls_list: + target = project_dict_map.get(item.id, None) + if target: + item.spent = target['spent'] + item.balance = target['balance'] + + for item in cls_list: + print("==============>>>>>>>>>>>>") + print(item.id, item.spent, item.balance) + dict_val = item.model_dump() + print(dict_val.get('spent'), dict_val.get('balance')) + + +async def set_item_spent(cls: ProjectModel, session: AsyncSessionDep): + project_dict_list, project_dict_map = await query_proj_spent([cls.id], session) + target = project_dict_map.get(cls.id, None) + if target: + cls.spent = target['spent'] + cls.balance = target['balance'] + + +async def query_proj_spent(proj_id_list: List[str], session: AsyncSessionDep): + result = {item: Decimal(0) for item in proj_id_list} + approve_cls_list = (await session.execute(ApproveService.select_cls().where(ApproveModel.proj_id.in_(proj_id_list)))).scalars().all() + for approve_item in approve_cls_list: + if approve_item.status == 'approved': + result[approve_item.proj_id] += Decimal(approve_item.amount) + + project_cls_list = (await session.execute(ProjectService.select_cls().where(ProjectModel.id.in_(proj_id_list)))).scalars().all() + project_dict_list = [item.model_dump() for item in project_cls_list] + for dict_item in project_dict_list: + # 已花费金额 + dict_item['spent'] = result[dict_item['id']] + # 剩余金额 + dict_item['balance'] = Decimal(dict_item['budget']) - Decimal(dict_item['spent']) + project_dict_map = {item['id']: item for item in project_dict_list} + return project_dict_list, project_dict_map diff --git a/app/server.py b/app/server.py index e4ecf41..59fd15d 100644 --- a/app/server.py +++ b/app/server.py @@ -27,7 +27,7 @@ from app.model.LlmOrder import LlmOrderService from app.model.LlmProduct import LlmProductService from app.model.OrgModel import OrgService from app.model.PosModel import PosService -from app.model.ProjectModel import ProjectService +from app.model.ProjectService import ProjectService from app.model.ReimburseModel import ReimburseService from app.model.ReimburseOtherModel import ReimburseOtherService from app.model.ReimburseTravelModel import ReimburseTravelService