PayrollManagementSystem/backend/app/api/v1/system/tplus_workbench.py
liuwenxin 71b7846a79 refactor(后端): 将工资与工作区业务逻辑下沉服务层
将月份状态、资料输出、调整单编排及工作区查询、
CSV 生成移至现有服务层,路由仅绑定参数并返回响应。

保持接口路径、参数、响应模型和权限依赖一致,
补充九项内存库行为回归并同步测试记录。
2026-11-02 09:42:55 +08:00

81 lines
3.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""T+ 基础导入和预演的管理员入口;HTTP 不执行长查询或核算。"""
from typing import Annotated
from fastapi import APIRouter, Path, Query
from starlette.responses import StreamingResponse
from app.api.deps import DbSession, PayrollWriter, WorkbenchReader
from app.core.response import ApiResponse, PageQuery, PageResult, ok
from app.schemas.tplus_workbench import FactIn, SourceChoiceQuery, WorkCreateIn, WorkQuery
from app.services.tplus import workbench
router = APIRouter()
RunNo = Annotated[str, Path(pattern=r"^[A-Za-z0-9_-]+$", max_length=32)]
@router.get("/facts", response_model=ApiResponse[PageResult[dict]])
def facts(session: DbSession, _: WorkbenchReader, query: Annotated[WorkQuery, Query()]):
"""有界查询每项补充资料的当前版本。"""
return ok(workbench.list_facts(session, query))
@router.post("/facts", response_model=ApiResponse[dict])
def save_fact(session: DbSession, admin: PayrollWriter, payload: FactIn):
"""追加有依据的人工事实;旧版本不覆盖。"""
return ok(workbench.save_fact(session, payload, admin.id))
@router.get("/facts/{fact_id}/history", response_model=ApiResponse[PageResult[dict]])
def fact_history(
session: DbSession, _: WorkbenchReader, fact_id: int, query: Annotated[PageQuery, Query()]
):
"""按事实身份分页查看修改历史,不允许跨账套读取。"""
return ok(workbench.fact_history(session, fact_id, query))
@router.post("/work-runs", response_model=ApiResponse[dict])
def create_work(session: DbSession, admin: PayrollWriter, payload: WorkCreateIn):
"""创建导入预览、执行导入或核算预演任务。"""
return ok(workbench.queue_work(session, payload, admin.id))
@router.get("/work-runs", response_model=ApiResponse[PageResult[dict]])
def work_runs(session: DbSession, _: WorkbenchReader, query: Annotated[WorkQuery, Query()]):
"""分页查询任务,所有金额结果仅为预演。"""
return ok(workbench.list_work(session, query))
@router.get("/work-runs/{run_no}", response_model=ApiResponse[dict])
def work_detail(session: DbSession, _: WorkbenchReader, run_no: RunNo):
"""查询规则版本、输入指纹及结果。"""
return ok(workbench.work_out(workbench.get_run(session, run_no)))
@router.get("/work-runs/{run_no}/lines", response_model=ApiResponse[PageResult[dict]])
def work_lines(
session: DbSession, _: WorkbenchReader, run_no: RunNo, query: Annotated[WorkQuery, Query()]
):
"""分页查看新增、待补及分配明细,不公开原始快照 JSON。"""
return ok(workbench.list_lines(session, run_no, query))
@router.get("/source-records", response_model=ApiResponse[PageResult[dict]])
def source_choices(
session: DbSession,
_: WorkbenchReader,
query: Annotated[SourceChoiceQuery, Query()],
):
"""仅提供挑选资料所需的源编号和名称,不返回电话、地址和原始载荷。"""
return ok(workbench.source_choices(session, query))
@router.get("/work-runs/{run_no}/report")
def work_report(session: DbSession, _: WorkbenchReader, run_no: RunNo):
"""导出全部预演明细,防止源单号被电子表格当作公式执行。"""
return StreamingResponse(
workbench.report_csv(session, run_no),
media_type="text/csv; charset=utf-8",
headers={"Content-Disposition": f'attachment; filename="{run_no}-preview.csv"'},
)