PayrollManagementSystem/backend/app/repositories/calc_template_repository.py
liuwenxin 1461a8d113 feat(后端): 添加授权体系、系统监控与试用期计提
按 2026-11-02 需求补齐计划交付后端能力:
- 角色授权:权限目录化,接口统一准入校验,未登记默认拒绝;
  管理员判断改为按数据库角色重新判定,防止权限码持有者绕过
- 系统监控:单进程采集资源、请求与异常指标,分钟级聚合,
  阈值配置化;敏感配置增加 repr 保护,Redis 密码 URL 转义
- 试用期计提:按回款日判定并写入快照,年度结清与调整单联动;
  人员模型补试用期、离职日期与工资版本历史字段
- 计提模板:公式骨架受控,页面维护名称与参数绑定
- T+ 采集:自动化配置表与进度查询

内置角色禁停用保护对齐模块注释;核算规则版本提为常量并
补充批量核算失败堆栈日志。

影响范围:后端全部模块、数据库迁移 V22-V28
验证:uv run ruff check 通过;922 个单元测试全部通过;
集成测试因本机 MySQL 数据卷被其他容器锁定暂未运行
2026-11-03 11:20:55 +08:00

67 lines
2.5 KiB
Python

"""计提公式模板数据访问。
本层只 flush 不 commit —— 事务边界由 services 层控制。
"""
from sqlalchemy import func, select
from sqlalchemy.orm import Session
from app.models import BusinessRole, CalcTemplateDefinition
def get_by_id(session: Session, template_id: int, *, for_update: bool = False) -> CalcTemplateDefinition | None:
"""按主键查询;删除校验走行锁。"""
if for_update:
stmt = select(CalcTemplateDefinition).where(CalcTemplateDefinition.id == template_id).with_for_update()
return session.execute(stmt).scalar_one_or_none()
return session.get(CalcTemplateDefinition, template_id)
def get_by_code(session: Session, template_code: str) -> CalcTemplateDefinition | None:
"""按编码查询模板。编码是岗位绑定与参数解析的锚点。"""
stmt = select(CalcTemplateDefinition).where(CalcTemplateDefinition.template_code == template_code)
return session.execute(stmt).scalar_one_or_none()
def list_all(session: Session) -> list[CalcTemplateDefinition]:
"""全部模板(含停用,供管理列表展示引用状态)。"""
stmt = select(CalcTemplateDefinition).order_by(
CalcTemplateDefinition.sort_order, CalcTemplateDefinition.id
)
return list(session.execute(stmt).scalars())
def list_active(session: Session) -> list[CalcTemplateDefinition]:
"""启用模板,供岗位表单下拉。"""
stmt = (
select(CalcTemplateDefinition)
.where(CalcTemplateDefinition.status == 1)
.order_by(CalcTemplateDefinition.sort_order, CalcTemplateDefinition.id)
)
return list(session.execute(stmt).scalars())
def count_roles_by_codes(session: Session, codes: list[str]) -> dict[str, int]:
"""批量统计各模板被岗位引用的数量,用于删除保护与列表展示。"""
if not codes:
return {}
rows = session.execute(
select(BusinessRole.calc_template, func.count(BusinessRole.id))
.where(BusinessRole.calc_template.in_(codes))
.group_by(BusinessRole.calc_template)
).all()
return {code: int(count) for code, count in rows}
def create(session: Session, template: CalcTemplateDefinition) -> CalcTemplateDefinition:
"""新增模板。只 flush 以取得自增 id。"""
session.add(template)
session.flush()
return template
def delete(session: Session, template: CalcTemplateDefinition) -> None:
"""删除模板。调用方须先完成保护规则校验。"""
session.delete(template)
session.flush()