- 后端 /api/v1/dashboard 单端点返回四区块:经营总览(绩效/应付/人数/团队数/月结状态/近 6 个月趋势)、待办(待审批 + 回款未指定批次 + 同步显式降级)、风险预警(回款逾期/审批超时,阈值参数化)、最近动态(操作日志) - dashboard_service 实时派生(不落表、不引调度器);参数快照一次解析;总览只做 SQL 聚合,不复用报表的重查询 - V10 迁移新增预警参数三项(warning.enabled / warning.receipt.overdue.days=90 / warning.approval.timeout.days=3),参数注册表 19→22,init-dev 与 V10 SQL 双轨同步 - 测试:test_dashboard 5 例(权限文案/总览对账+非法月份 400/待办三类/预警命中与参数关闭/动态来源);全量 852 passed - 依赖闭包并入:core/month.py 月份校验、api/v1/base/business_roles.py(router 已挂载的岗位只读端点)、test_month_validation - 修复:alembic --sql 在 Windows 按控制台 GBK 输出,V10 SQL 以 UTF-8 重新导出(README 已记) Co-Authored-By: Claude Code <noreply@anthropic.com>
27 lines
972 B
Python
27 lines
972 B
Python
"""月份参数校验(YYYY-MM)——叶子模块。
|
||
|
||
报表、月结与调整单共用同一套校验:只校验长度会放过 `2026-13` 这类
|
||
非法月份,一路传到 `calendar.monthrange` 才抛 ValueError,最终被全局
|
||
处理器当成服务端异常(500)。月份范围必须在入口处拦住,才能返回 400。
|
||
"""
|
||
|
||
import re
|
||
|
||
_MONTH_RE = re.compile(r"^\d{4}-(0[1-9]|1[0-2])$")
|
||
|
||
|
||
def validate_month(value: str) -> str:
|
||
"""校验 YYYY-MM 且月份 01-12;非法抛 ValueError(Pydantic 友好)。
|
||
|
||
年份 0000 也拒绝:`date(0, ...)` 同样会抛 ValueError,且业务上不存在。
|
||
"""
|
||
if not _MONTH_RE.match(value) or int(value[:4]) == 0:
|
||
raise ValueError("月份格式须为 YYYY-MM(01-12)")
|
||
return value
|
||
|
||
|
||
def parse_month(value: str) -> tuple[int, int]:
|
||
"""校验并返回 (year, month),供服务层计算月末等使用。"""
|
||
validate_month(value)
|
||
return int(value[:4]), int(value[5:7])
|