From 226efc1aa03654d87e6fae520f39d3acc01dd9be Mon Sep 17 00:00:00 2001 From: martsforever Date: Wed, 10 Sep 2025 18:53:21 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20Project=E9=80=9A=E8=BF=87=E5=85=B3?= =?UTF-8?q?=E8=81=94=E6=9F=A5=E8=AF=A2=E5=87=BA=E6=9D=A5=E6=89=80=E6=9C=89?= =?UTF-8?q?=E5=AD=90=E8=A1=A8=E5=AE=A1=E6=89=B9=E5=8D=95=E8=AE=B0=E5=BD=95?= =?UTF-8?q?=E7=9A=84=E9=87=91=E9=A2=9D=E6=80=BB=E5=92=8C=E6=9D=A5=E8=AE=A1?= =?UTF-8?q?=E7=AE=97=E5=B7=B2=E8=8A=B1=E8=B4=B9=E9=87=91=E9=A2=9D=E4=BB=A5?= =?UTF-8?q?=E5=8F=8A=E5=89=A9=E4=BD=99=E9=A2=84=E7=AE=97=E4=BD=99=E9=A2=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/controller/add_hotel_route.py | 3 +- app/model/ProjectModel.py | 62 +++++++++++++++++++++++++++---- app/model/ProjectService.py | 58 ----------------------------- app/server.py | 2 +- app/tools/tool_query_projects.py | 2 +- 5 files changed, 58 insertions(+), 69 deletions(-) delete mode 100644 app/model/ProjectService.py diff --git a/app/controller/add_hotel_route.py b/app/controller/add_hotel_route.py index 7068fa2..b6c2c8d 100644 --- a/app/controller/add_hotel_route.py +++ b/app/controller/add_hotel_route.py @@ -9,8 +9,7 @@ from pydantic import BaseModel from app.model.ApproveModel import ApproveModel, ApproveService from app.model.HotelModel import HotelService, HotelModel from app.model.OrderModel import OrderService -from app.model.ProjectModel import ProjectModel -from app.model.ProjectService import ProjectService +from app.model.ProjectModel import ProjectModel, ProjectService from app.model.ReimburseModel import ReimburseService, ReimburseModel from app.model.ReimburseOtherModel import ReimburseOtherService from app.model.UserModel import UserServiceModel, UserService diff --git a/app/model/ProjectModel.py b/app/model/ProjectModel.py index bd840f4..d5410c1 100644 --- a/app/model/ProjectModel.py +++ b/app/model/ProjectModel.py @@ -1,11 +1,13 @@ from decimal import Decimal -from typing import Optional +from typing import Optional, List -from pydantic import computed_field, PrivateAttr -from sqlmodel import Field, Relationship +from pydantic import computed_field +from sqlalchemy.orm import selectinload +from sqlmodel import Field, Relationship, select 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): @@ -13,12 +15,9 @@ class ProjectModel(BasicModel, table=True): name: str = Field(default=None, description="项目名称") description: str = Field(default=None, description="项目描述") - budget: float = Field(default=None, description="项目预算金额") + budget: Decimal = Field(default=None, description="项目预算金额") version: int = Field(default=None, description="项目版本号") - spent: Decimal = Field(default=None, description="项目花费金额") - balance: Decimal = Field(default=None, description="项目预算余额") - # /*---------------------------------------leader_id-------------------------------------------*/ leader_id: str = Field( @@ -47,3 +46,52 @@ class ProjectModel(BasicModel, table=True): @leader_name.setter def leader_name(self, value: Optional[str]) -> None: pass + + # /*---------------------------------------approve_list-------------------------------------------*/ + + approve_list_relationship: Optional[List["ApproveModel"]] = Relationship( + sa_relationship_kwargs={ + "foreign_keys": "ApproveModel.proj_id", + "primaryjoin": "ProjectModel.id == ApproveModel.proj_id", + } + ) + + # /*---------------------------------------spent: 已花费金额-------------------------------------------*/ + @computed_field + @property + def spent(self) -> Optional[Decimal]: + try: + total_amount = Decimal(0) + for approve_item in self.approve_list_relationship: + if approve_item.status == 'approved': + total_amount = total_amount + approve_item.amount + return total_amount + except: + return None + + @spent.setter + def spent(self, value: Optional[Decimal]) -> None: + pass + + # /*---------------------------------------balance:剩余预算金额-------------------------------------------*/ + @computed_field + @property + def balance(self) -> Optional[Decimal]: + if self.spent is not None: + return self.budget - self.spent + else: + return None + + @balance.setter + def balance(self, value: Optional[Decimal]) -> None: + pass + + +ProjectService = create_model_service( + Cls=ProjectModel, + custom_query=lambda: select(ProjectModel) + .options( + selectinload(ProjectModel.leader), + selectinload(ProjectModel.approve_list_relationship) + ), +) diff --git a/app/model/ProjectService.py b/app/model/ProjectService.py deleted file mode 100644 index 5b54a5d..0000000 --- a/app/model/ProjectService.py +++ /dev/null @@ -1,58 +0,0 @@ -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'] - - -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 bc663d4..06ae1bc 100644 --- a/app/server.py +++ b/app/server.py @@ -37,7 +37,7 @@ from app.model.LlmProduct import LlmProductService from app.model.OrderModel import OrderService from app.model.OrgModel import OrgService from app.model.PosModel import PosService -from app.model.ProjectService import ProjectService +from app.model.ProjectModel import ProjectService from app.model.ReimburseModel import ReimburseService from app.model.ReimburseOtherModel import ReimburseOtherService from app.model.ReimburseTravelModel import ReimburseTravelService diff --git a/app/tools/tool_query_projects.py b/app/tools/tool_query_projects.py index c422839..e573725 100644 --- a/app/tools/tool_query_projects.py +++ b/app/tools/tool_query_projects.py @@ -1,7 +1,7 @@ from langchain_core.runnables import RunnableConfig from langchain_core.tools import tool -from app.model.ProjectService import ProjectService +from app.model.ProjectModel import ProjectService from app.model.RelProjUserModel import RelProjUserService from app.utils.PageQueryParams import PageQueryParams from app.utils.db_utils import async_session