补齐源快照、资料预演、正式导入及工资核算的依赖链路。 新增版本化输入、逐段 FIFO、生成锁和结构化当月调整, 并提供默认关闭的周期采集、有限重试及 Windows 托管。 原始客户资料、私有配置及首页改动不纳入本次提交。 BREAKING CHANGE: 升级前需应用新增迁移,并通过 PAYROLL_MAKER_USER_ID 绑定实际工资制作人,否则业务只读。 旧单批次核算默认关闭;历史兼容需显式启用 PAYROLL_ALLOW_LEGACY_CALCULATION。生产启用和真实金额待验收。
36 lines
1.5 KiB
Python
36 lines
1.5 KiB
Python
"""只按明确的源驱动错误分类,不把含凭据的驱动文本写入响应。"""
|
||
|
||
from sqlalchemy.exc import TimeoutError as PoolTimeoutError
|
||
|
||
TRANSIENT_CODES = frozenset({"SOURCE_CONNECTION_LOST", "SOURCE_TIMEOUT"})
|
||
MESSAGES = {
|
||
"SOURCE_CONNECTION_LOST": "源连接中断,请检查网络和只读连接",
|
||
"SOURCE_TIMEOUT": "源连接或查询超时",
|
||
"SOURCE_AUTH_ERROR": "源账号认证失败,请核对只读账号配置",
|
||
"SOURCE_SCHEMA_ERROR": "源表或字段不匹配,请核对版本与映射",
|
||
"SOURCE_CONFIG_ERROR": "源连接配置不完整或任务排队后发生变化",
|
||
"SOURCE_DRIVER_ERROR": "源数据库驱动不可用,请检查已验证的ODBC驱动",
|
||
"QUERY_FILE_MISSING": "缺少源查询文件,请核对部署包",
|
||
}
|
||
|
||
|
||
def classify_source_error(exc, fallback="READ_ERROR"):
|
||
if isinstance(exc, (TimeoutError, PoolTimeoutError)):
|
||
return "SOURCE_TIMEOUT"
|
||
if isinstance(exc, ModuleNotFoundError):
|
||
return "SOURCE_DRIVER_ERROR"
|
||
if isinstance(exc, FileNotFoundError):
|
||
return "QUERY_FILE_MISSING"
|
||
original = getattr(exc, "orig", exc)
|
||
args = getattr(original, "args", ())
|
||
state = str(args[0]).upper() if args else ""
|
||
if state in {"HYT00", "HYT01"}:
|
||
return "SOURCE_TIMEOUT"
|
||
if state in {"08S01", "08001", "08003", "08006"}:
|
||
return "SOURCE_CONNECTION_LOST"
|
||
if state == "28000":
|
||
return "SOURCE_AUTH_ERROR"
|
||
if state in {"42S02", "42S22", "07002"}:
|
||
return "SOURCE_SCHEMA_ERROR"
|
||
return fallback
|