2026-10-12 08:24:17 -04:00
|
|
|
|
"""计提参数管理接口(需求 #28:参数配置化 + 版本管理 + 留痕)。
|
|
|
|
|
|
|
2026-10-18 23:19:16 -04:00
|
|
|
|
权限分层(澄清 #17 业务答复 2026-10-19「查看可、修改仅管理员」):只读
|
|
|
|
|
|
端点(目录/变更记录/批次明细/对比/导出/预览/历史)要求登录即可;写操作
|
|
|
|
|
|
(提交/撤回)仅管理员。写操作经 `RequestMetaDep` 带入请求元信息写入操作
|
|
|
|
|
|
日志,变更批次本身另行留痕(变更人/时间/项/旧值/新值/生效日期)——双保险。
|
2026-10-12 08:24:17 -04:00
|
|
|
|
导出为文件流(非统一信封),前端以 blob 接收。
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
from datetime import date
|
|
|
|
|
|
from typing import Annotated
|
|
|
|
|
|
|
|
|
|
|
|
from fastapi import APIRouter, Path, Query
|
|
|
|
|
|
from fastapi.responses import StreamingResponse
|
|
|
|
|
|
|
2026-10-18 23:19:16 -04:00
|
|
|
|
from app.api.deps import AdminUser, DbSession, LoginUser, RequestMetaDep
|
2026-10-12 08:24:17 -04:00
|
|
|
|
from app.core.response import ApiResponse, PageResult, ok
|
|
|
|
|
|
from app.schemas.param import (
|
|
|
|
|
|
ParamBatchDetailOut,
|
|
|
|
|
|
ParamBatchOut,
|
|
|
|
|
|
ParamBatchQuery,
|
|
|
|
|
|
ParamCatalogItemOut,
|
|
|
|
|
|
ParamChangeBatchOut,
|
|
|
|
|
|
ParamChangeIn,
|
|
|
|
|
|
ParamDiffOut,
|
|
|
|
|
|
ParamHistoryOut,
|
|
|
|
|
|
ParamHistoryQuery,
|
|
|
|
|
|
ParamPreviewIn,
|
|
|
|
|
|
ParamPreviewOut,
|
|
|
|
|
|
)
|
|
|
|
|
|
from app.services import param_service
|
|
|
|
|
|
|
|
|
|
|
|
router = APIRouter(prefix="/params", tags=["系统管理 · 计提参数"])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get(
|
|
|
|
|
|
"", response_model=ApiResponse[list[ParamCatalogItemOut]], summary="参数目录(含当前生效值)"
|
|
|
|
|
|
)
|
|
|
|
|
|
def get_catalog(
|
|
|
|
|
|
session: DbSession,
|
2026-10-18 23:19:16 -04:00
|
|
|
|
_: LoginUser,
|
2026-10-12 08:24:17 -04:00
|
|
|
|
as_of: Annotated[date | None, Query(description="业务日期,默认今日")] = None,
|
|
|
|
|
|
) -> ApiResponse[list[ParamCatalogItemOut]]:
|
|
|
|
|
|
"""全部启用参数的定义元数据与截至 asOf 的生效值(前端按分组展示)。"""
|
|
|
|
|
|
return ok(param_service.get_catalog(session, as_of=as_of))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.post("/changes", response_model=ApiResponse[ParamChangeBatchOut], summary="提交变更批次")
|
|
|
|
|
|
def submit_change(
|
|
|
|
|
|
session: DbSession,
|
|
|
|
|
|
current_user: AdminUser,
|
|
|
|
|
|
payload: ParamChangeIn,
|
|
|
|
|
|
meta: RequestMetaDep,
|
|
|
|
|
|
) -> ApiResponse[ParamChangeBatchOut]:
|
|
|
|
|
|
"""提交一批参数变更(1~50 项),返回版本号;立即生效或指定日期生效。"""
|
|
|
|
|
|
return ok(
|
|
|
|
|
|
param_service.submit_change(session, payload, operator=current_user, meta=meta),
|
|
|
|
|
|
message="变更已提交",
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get(
|
|
|
|
|
|
"/changes", response_model=ApiResponse[PageResult[ParamBatchOut]], summary="变更记录分页"
|
|
|
|
|
|
)
|
|
|
|
|
|
def list_changes(
|
|
|
|
|
|
session: DbSession,
|
2026-10-18 23:19:16 -04:00
|
|
|
|
_: LoginUser,
|
2026-10-12 08:24:17 -04:00
|
|
|
|
query: Annotated[ParamBatchQuery, Query()],
|
|
|
|
|
|
) -> ApiResponse[PageResult[ParamBatchOut]]:
|
|
|
|
|
|
"""按生效日期区间分页查询变更批次(创建时间倒序)。"""
|
|
|
|
|
|
return ok(param_service.list_batches(session, query))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get(
|
|
|
|
|
|
"/changes/{batch_id}", response_model=ApiResponse[ParamBatchDetailOut], summary="批次明细"
|
|
|
|
|
|
)
|
|
|
|
|
|
def get_batch(
|
|
|
|
|
|
session: DbSession,
|
2026-10-18 23:19:16 -04:00
|
|
|
|
_: LoginUser,
|
2026-10-12 08:24:17 -04:00
|
|
|
|
batch_id: Annotated[int, Path(ge=1)],
|
|
|
|
|
|
) -> ApiResponse[ParamBatchDetailOut]:
|
|
|
|
|
|
"""批次头 + 逐项旧值 → 新值。"""
|
|
|
|
|
|
return ok(param_service.get_batch_detail(session, batch_id))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.delete("/changes/{batch_id}", response_model=ApiResponse[None], summary="撤回未生效批次")
|
|
|
|
|
|
def withdraw_batch(
|
|
|
|
|
|
session: DbSession,
|
|
|
|
|
|
current_user: AdminUser,
|
|
|
|
|
|
batch_id: Annotated[int, Path(ge=1)],
|
|
|
|
|
|
meta: RequestMetaDep,
|
|
|
|
|
|
) -> ApiResponse[None]:
|
|
|
|
|
|
"""撤回尚未生效的批次;已生效批次不可撤回。"""
|
|
|
|
|
|
param_service.withdraw_batch(session, batch_id, operator=current_user, meta=meta)
|
|
|
|
|
|
return ok(message="批次已撤回")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/compare", response_model=ApiResponse[list[ParamDiffOut]], summary="两日期快照对比")
|
|
|
|
|
|
def compare(
|
|
|
|
|
|
session: DbSession,
|
2026-10-18 23:19:16 -04:00
|
|
|
|
_: LoginUser,
|
2026-10-12 08:24:17 -04:00
|
|
|
|
from_date: Annotated[date, Query(description="起始业务日期(含)")],
|
|
|
|
|
|
to_date: Annotated[date, Query(description="截止业务日期(含)")],
|
|
|
|
|
|
) -> ApiResponse[list[ParamDiffOut]]:
|
|
|
|
|
|
"""两个业务日期的全量参数快照逐项对比,changed 标记是否变化。"""
|
|
|
|
|
|
return ok(param_service.compare(session, from_date=from_date, to_date=to_date))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/export", summary="导出变更记录 Excel")
|
|
|
|
|
|
def export_changes(
|
|
|
|
|
|
session: DbSession,
|
2026-10-18 23:19:16 -04:00
|
|
|
|
_: LoginUser,
|
2026-10-12 08:24:17 -04:00
|
|
|
|
start_date: Annotated[date | None, Query(description="生效日期起(含)")] = None,
|
|
|
|
|
|
end_date: Annotated[date | None, Query(description="生效日期止(含)")] = None,
|
|
|
|
|
|
) -> StreamingResponse:
|
|
|
|
|
|
"""变更记录导出为 Excel 文件流(非统一信封,前端以 blob 接收)。"""
|
|
|
|
|
|
content = param_service.export_changes(session, start_date=start_date, end_date=end_date)
|
|
|
|
|
|
filename = f"param_changes_{date.today():%Y%m%d}.xlsx"
|
|
|
|
|
|
return StreamingResponse(
|
|
|
|
|
|
iter([content]),
|
|
|
|
|
|
media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
|
|
|
|
headers={"Content-Disposition": f'attachment; filename="{filename}"'},
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.post("/preview", response_model=ApiResponse[ParamPreviewOut], summary="计算预览")
|
|
|
|
|
|
def preview(
|
|
|
|
|
|
session: DbSession,
|
2026-10-18 23:19:16 -04:00
|
|
|
|
_: LoginUser,
|
2026-10-12 08:24:17 -04:00
|
|
|
|
payload: ParamPreviewIn,
|
|
|
|
|
|
) -> ApiResponse[ParamPreviewOut]:
|
|
|
|
|
|
"""输入业务场景(岗位/产品/发货/回款/金额),按回款到账日取参数实时试算。"""
|
|
|
|
|
|
return ok(param_service.preview(session, payload))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get(
|
|
|
|
|
|
"/{param_code}/history",
|
|
|
|
|
|
response_model=ApiResponse[PageResult[ParamHistoryOut]],
|
|
|
|
|
|
summary="参数版本历史",
|
|
|
|
|
|
)
|
|
|
|
|
|
def get_history(
|
|
|
|
|
|
session: DbSession,
|
2026-10-18 23:19:16 -04:00
|
|
|
|
_: LoginUser,
|
2026-10-12 08:24:17 -04:00
|
|
|
|
param_code: Annotated[str, Path(max_length=64)],
|
|
|
|
|
|
query: Annotated[ParamHistoryQuery, Query()],
|
|
|
|
|
|
) -> ApiResponse[PageResult[ParamHistoryOut]]:
|
|
|
|
|
|
"""单参数的版本历史(含版本号、变更人与旧值 → 新值),最新在前。"""
|
|
|
|
|
|
return ok(param_service.get_history(session, param_code, query))
|