accounting/wwwroot/api/dayend.dspy

74 lines
3.7 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 日终切日任务端点:日结余额快照 + 日结总账 + 营业日期推进
# GET /accounting/api/dayend.dspy
#
# 安全:本端点是定时任务入口(写库),只允许本机调用——与 fetch_forex_rates.dspy 同模式。
# 判定依据 client_ipnginx 用 $proxy_add_x_forwarded_for 追加模式、中间件取链尾值,
# 外部伪造 X-Forwarded-For 会被追加真实 IP 到链尾,伪造不成立;且应用端口不对外开放。
_ip = request.get('client_ip') or ''
if _ip not in ('127.0.0.1', '::1', 'localhost'):
return json.dumps({'success': False, 'message': '仅允许本机调用(定时任务入口)'},
ensure_ascii=False)
from sqlor.dbpools import get_sor_context
# 切日catch-up 语义,同一 sor 上下文,全成才提交):
# 循环执行三步直至 business_date 追平今天:
# 1. dayend_balance —— 上一营业日余额快照acc_balance 补齐无当日行的账户)
# 2. accounting_ledger —— 上一营业日总账delete+insert 幂等重建 ledger 当日行)
# 3. new_business_date —— params.business_date 推进 +1 天
# 停机多日/cron 漏跑后一次调用自动追平,不会漏日;同日重放 0 步跳过(幂等)。
# _MAX_CATCHUP 防御异常配置(如 business_date 误写远古值)导致超长循环。
_MAX_CATCHUP = 400
async with get_sor_context(request._run_ns, 'accounting') as sor:
try:
from datetime import date
from accounting.dayend_balance import dayend_balance
from accounting.ledger import accounting_ledger
from appbase.businessdate import get_business_date, new_business_date
today = date.today().isoformat()
bd = await get_business_date(sor)
start_bd = bd
if bd > today:
return json.dumps({
'success': False,
'business_date': bd,
'message': 'business_date 超前于今天(配置异常),拒绝切日',
}, ensure_ascii=False)
steps = 0
snapshots = []
while bd < today and steps < _MAX_CATCHUP:
snap_date = await dayend_balance(sor)
await accounting_ledger(sor)
await new_business_date(sor)
bd = await get_business_date(sor)
snapshots.append(snap_date)
steps += 1
if steps == 0:
return json.dumps({
'success': True, 'skipped': True,
'business_date': bd,
'message': '已是当日营业日期,跳过切日(幂等)',
}, ensure_ascii=False)
if bd < today:
# 触达防御上限仍未追平:已推进的天数是完整日结单元、照常生效(可续跑),
# 但 business_date 疑似配置异常(如误写远古值),必须人工核查后再续。
return json.dumps({
'success': False,
'business_date': '%s -> %s' % (start_bd, bd),
'steps': steps,
'message': 'catch-up 达 %d 天上限仍未追平今天business_date 疑似配置异常,'
'请人工核查后重跑(已推进的 %d 天日结完整生效,重跑自动续推)'
% (_MAX_CATCHUP, steps),
}, ensure_ascii=False)
await sor.sqlExe("COMMIT", {})
return json.dumps({
'success': True,
'business_date': '%s -> %s' % (start_bd, bd),
'steps': steps,
'snapshots': snapshots[-5:],
}, ensure_ascii=False)
except Exception as e:
return json.dumps({'success': False, 'message': 'dayend failed: %s' % str(e)[:200]},
ensure_ascii=False)