"""已办中心实例级财务备注;权限与本人已办范围同时满足才可访问。""" from dataclasses import dataclass from datetime import datetime from typing import Any, Dict, List from sqlalchemy.orm import Session from app.models.models import OaInstance, OaTask, SysUser from app.services.web_auth_roles import iter_user_menu_perm_codes PERMISSION = "oa:done:finance-remark" DONE_STATUSES = ("approved", "rejected", "returned", "transferred") class OaFinanceRemarkError(ValueError): def __init__(self, status_code: int, message: str): super().__init__(message) self.status_code = status_code @dataclass(frozen=True) class FinanceRemarkAccess: user_id: str tenant_no: str can_manage: bool def resolve_access(db: Session, user: dict) -> FinanceRemarkAccess: # 不使用缺省租户、客户端租户或角色名称作为敏感字段的授权依据。 uid = str(user.get("user_id") or "").strip() identity = db.query(SysUser.id, SysUser.tenant_no).filter( SysUser.id == uid, SysUser.del_flag == 0, SysUser.status == "1" ).first() if not identity or not str(identity.tenant_no or "").strip(): raise OaFinanceRemarkError(403, "用户不可用") tenant = str(identity.tenant_no).strip() granted = iter_user_menu_perm_codes(db, uid, tenant_no=tenant) return FinanceRemarkAccess(uid, tenant, PERMISSION in granted) def _accessible_instances(db: Session, access: FinanceRemarkAccess): own_done = db.query(OaTask.id).filter( OaTask.instance_id == OaInstance.id, OaTask.tenant_no == access.tenant_no, OaTask.assignee_user_id == access.user_id, OaTask.task_type == "approval", OaTask.status.in_(DONE_STATUSES), OaTask.del_flag == 0, ).exists() return db.query(OaInstance).filter( OaInstance.tenant_no == access.tenant_no, OaInstance.del_flag == 0, own_done, ) def _require_instance(db: Session, access: FinanceRemarkAccess, instance_id: str) -> OaInstance: if not access.can_manage: raise OaFinanceRemarkError(403, "无权查看或修改财务备注") instance = _accessible_instances(db, access).filter(OaInstance.id == instance_id).first() if instance is None: raise OaFinanceRemarkError(404, "审批单不在本人已办范围内") return instance def _remark_data(instance: OaInstance) -> Dict[str, str]: return {"instanceId": instance.id, "financeRemark": instance.finance_remark or ""} def get_finance_remark(db: Session, access: FinanceRemarkAccess, instance_id: str) -> Dict[str, str]: return _remark_data(_require_instance(db, access, instance_id)) def update_finance_remark( db: Session, access: FinanceRemarkAccess, instance_id: str, finance_remark: str ) -> Dict[str, str]: instance = _require_instance(db, access, instance_id) if not isinstance(finance_remark, str) or len(finance_remark) > 512: raise OaFinanceRemarkError(422, "财务备注最多 512 字") instance.finance_remark = finance_remark instance.update_by = access.user_id instance.update_time = datetime.now() db.flush() return _remark_data(instance) def add_list_remarks(db: Session, access: FinanceRemarkAccess, rows: List[Dict[str, Any]]) -> None: # 即使调用方未来调整序列化,也先移除敏感字段,再按权限补充。 for row in rows: row.pop("financeRemark", None) if not access.can_manage: return ids = {row.get("instanceId") for row in rows if row.get("instanceId")} if not ids: return instances = _accessible_instances(db, access).filter(OaInstance.id.in_(ids)).all() remarks = {inst.id: inst.finance_remark or "" for inst in instances} for row in rows: if row.get("instanceId") in remarks: row["financeRemark"] = remarks[row["instanceId"]]