补齐业务路由与页面权限控制

This commit is contained in:
Cheng Zhou
2026-05-28 10:56:28 +08:00
parent fa961f1391
commit 8611bd66f8
39 changed files with 458 additions and 250 deletions
+2
View File
@@ -1,3 +1,5 @@
from __future__ import annotations
import uuid
from typing import Sequence
+2
View File
@@ -1,3 +1,5 @@
from __future__ import annotations
import uuid
from typing import Sequence
+2
View File
@@ -1,3 +1,5 @@
from __future__ import annotations
import uuid
from typing import Sequence
+2
View File
@@ -1,5 +1,7 @@
from __future__ import annotations
from __future__ import annotations
import uuid
from typing import Sequence
+11 -17
View File
@@ -9,7 +9,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
from app.models.site import Site
from app.models.startup_feasibility import StartupFeasibility
from app.models.startup_ethics import StartupEthics
from app.models.finance_contract import FinanceContract
from app.models.contract_fee import ContractFee
from app.models.kickoff_meeting import KickoffMeeting
from app.models.subject import Subject
@@ -59,7 +59,7 @@ async def get_project_overview(db: AsyncSession, study_id: uuid.UUID) -> Dict:
institution_status = "COMPLETED" if feas.get("approved_date") else "IN_PROGRESS" if feas.get("submit_date") else "NOT_STARTED"
institution_completed = feas.get("approved_date")
# 伦理审批状态 - 依赖机构立项完成
# 伦理记录状态 - 依赖机构立项完成
eth = ethics_data.get(site_id_str, {})
if institution_status == "COMPLETED":
ethics_status = "COMPLETED" if eth.get("approved_date") else "IN_PROGRESS" if eth.get("submit_date") else "NOT_STARTED"
@@ -68,7 +68,7 @@ async def get_project_overview(db: AsyncSession, study_id: uuid.UUID) -> Dict:
ethics_status = "NOT_STARTED"
ethics_completed = eth.get("approved_date") if institution_status == "COMPLETED" else None
# 合同签署状态 - 依赖伦理审批完成
# 合同签署状态 - 依赖伦理记录完成
contract = contract_data.get(site_id_str, {})
if ethics_status == "COMPLETED":
contract_status = "COMPLETED" if contract.get("signed_date") else "NOT_STARTED"
@@ -202,28 +202,22 @@ async def _get_ethics_data(db: AsyncSession, study_id: uuid.UUID, site_ids: List
async def _get_contract_data(db: AsyncSession, study_id: uuid.UUID, site_ids: List[uuid.UUID]) -> Dict:
"""获取各中心的合同签署数据"""
# FinanceContract 通过 site_name 关联,需要先获取site名称映射
stmt_sites = select(Site.id, Site.name).where(Site.id.in_(site_ids))
result = await db.execute(stmt_sites)
site_name_map = {row.name: str(row.id) for row in result.all()}
stmt = select(
FinanceContract.site_name,
func.min(FinanceContract.signed_date).label("signed_date")
ContractFee.center_id,
func.min(ContractFee.signed_date).label("signed_date")
).where(
FinanceContract.study_id == study_id
).group_by(FinanceContract.site_name)
ContractFee.project_id == study_id,
ContractFee.center_id.in_(site_ids),
).group_by(ContractFee.center_id)
result = await db.execute(stmt)
rows = result.all()
data = {}
for row in rows:
site_id_str = site_name_map.get(row.site_name)
if site_id_str:
data[site_id_str] = {
"signed_date": row.signed_date.isoformat() if row.signed_date else None,
}
data[str(row.center_id)] = {
"signed_date": row.signed_date.isoformat() if row.signed_date else None,
}
return data
+11 -25
View File
@@ -1,3 +1,5 @@
from __future__ import annotations
import uuid
from pathlib import Path
from typing import Iterable, Sequence
@@ -16,9 +18,8 @@ from app.models.distribution import Distribution
from app.models.drug_shipment import DrugShipment
from app.models.fee_attachment import FeeAttachment
from app.models.finance import FinanceItem
from app.models.finance_contract import FinanceContract
from app.models.kickoff_meeting import KickoffMeeting
from app.models.knowledge_note import KnowledgeNote
from app.models.precaution import Precaution
from app.models.milestone import Milestone
from app.models.monitoring_visit_issue import MonitoringVisitIssue
from app.models.site import Site
@@ -177,19 +178,11 @@ async def delete_site_and_related(db: AsyncSession, site: Site) -> None:
)
)
).scalars().all()
finance_contract_ids = (
precaution_ids = (
await db.execute(
select(FinanceContract.id).where(
FinanceContract.study_id == study_id,
FinanceContract.site_name == site_name,
)
)
).scalars().all()
knowledge_note_ids = (
await db.execute(
select(KnowledgeNote.id).where(
KnowledgeNote.study_id == study_id,
KnowledgeNote.site_name == site_name,
select(Precaution.id).where(
Precaution.study_id == study_id,
Precaution.site_name == site_name,
)
)
).scalars().all()
@@ -220,8 +213,7 @@ async def delete_site_and_related(db: AsyncSession, site: Site) -> None:
"startup_kickoff_signin": kickoff_ids,
"startup_kickoff_ppt": kickoff_ids,
"training_authorization": training_ids,
"finance_contract": finance_contract_ids,
"knowledge_note": knowledge_note_ids,
"precaution": precaution_ids,
"drug_shipment": drug_shipment_ids,
}
attachment_paths: list[str] = []
@@ -311,15 +303,9 @@ async def delete_site_and_related(db: AsyncSession, site: Site) -> None:
)
)
await db.execute(
delete(FinanceContract).where(
FinanceContract.study_id == study_id,
FinanceContract.site_name == site_name,
)
)
await db.execute(
delete(KnowledgeNote).where(
KnowledgeNote.study_id == study_id,
KnowledgeNote.site_name == site_name,
delete(Precaution).where(
Precaution.study_id == study_id,
Precaution.site_name == site_name,
)
)
+5 -5
View File
@@ -1,3 +1,5 @@
from __future__ import annotations
import uuid
from typing import Sequence
@@ -99,7 +101,6 @@ async def delete(db: AsyncSession, study_id: uuid.UUID) -> None:
from app.models.visit import Visit
from app.models.ae import AdverseEvent
from app.models.finance import FinanceItem
from app.models.finance_contract import FinanceContract
from app.models.contract_fee import ContractFee
from app.models.contract_fee_payment import ContractFeePayment
from app.models.milestone import Milestone
@@ -111,7 +112,7 @@ async def delete(db: AsyncSession, study_id: uuid.UUID) -> None:
from app.models.startup_feasibility import StartupFeasibility
from app.models.startup_ethics import StartupEthics
from app.models.kickoff_meeting import KickoffMeeting
from app.models.knowledge_note import KnowledgeNote
from app.models.precaution import Precaution
from app.models.faq_category import FaqCategory
from app.models.faq_item import FaqItem
from app.models.faq_reply import FaqReply
@@ -129,8 +130,8 @@ async def delete(db: AsyncSession, study_id: uuid.UUID) -> None:
await db.execute(sa_delete(FaqItem).where(FaqItem.study_id == study_id))
await db.execute(sa_delete(FaqCategory).where(FaqCategory.study_id == study_id))
# 3. 删除知识库
await db.execute(sa_delete(KnowledgeNote).where(KnowledgeNote.study_id == study_id))
# 3. 删除注意事项
await db.execute(sa_delete(Precaution).where(Precaution.study_id == study_id))
# 4. 删除启动相关
await db.execute(sa_delete(KickoffMeeting).where(KickoffMeeting.study_id == study_id))
@@ -158,7 +159,6 @@ async def delete(db: AsyncSession, study_id: uuid.UUID) -> None:
)
)
await db.execute(sa_delete(ContractFee).where(ContractFee.project_id == study_id))
await db.execute(sa_delete(FinanceContract).where(FinanceContract.study_id == study_id))
await db.execute(sa_delete(FinanceItem).where(FinanceItem.study_id == study_id))
# 10. 删除不良事件
+2
View File
@@ -1,5 +1,7 @@
from __future__ import annotations
from __future__ import annotations
import uuid
from typing import Sequence