- xcx_runtime_clock.py: require_approved 是 factory,Depends 必须 ()
调用,否则 user 是 function 不是 CurrentUser → AttributeError 500
→ 沙盒在小程序所有页面失效的根因(getBusinessClock 一直降级 localFallback)
- admin_service.py:retry_trigger_job INSERT payload 字段是 jsonb,
psycopg2 读出是 dict,未 Json() wrap 直接 INSERT 触发
"can't adapt type 'dict'" → 生产环境点重试必 500
(该 bug 在 6f8f1231 即引入,F1-5a 走查时通过 SQL 复现端到端验证暴露)
走查覆盖:
- xcx_runtime_clock: 修后小程序 GET /api/xcx/runtime/clock 200,
返回完整 sandbox ctx(business_date / sandbox_instance_id)
- retry_trigger_job: SQL 复现 INSERT 包含真实 jsonb payload
({foo:bar,n:42}),修后 runtime_mode=sandbox + sandbox_instance_id
+ payload 完整保留全部 PASS
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
62 lines
2.1 KiB
Python
62 lines
2.1 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""小程序业务时钟路由。
|
||
|
||
仅用于小程序读取当前门店的"业务日 / 业务年月 / 模式"——sandbox 模式下,
|
||
小程序的 performance / task-list / customer-records 等页面应以 RuntimeContext
|
||
返回的业务时钟为准,禁止再用 ``new Date()`` 构造请求参数。
|
||
|
||
端点:
|
||
- GET /api/xcx/runtime/clock — 返回当前门店的业务时钟与运行模式(live / sandbox)。
|
||
|
||
所有端点均需 JWT(approved 状态),但不要求特定模块权限。
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from fastapi import APIRouter, Depends
|
||
|
||
from app.auth.dependencies import CurrentUser
|
||
from app.middleware.permission import require_approved
|
||
from app.services.runtime_context import get_runtime_context
|
||
from app.trace.decorators import trace_service
|
||
|
||
router = APIRouter(prefix="/api/xcx/runtime", tags=["小程序业务时钟"])
|
||
|
||
|
||
@router.get("/clock")
|
||
@trace_service("获取业务时钟", "Get business clock")
|
||
async def get_business_clock(
|
||
user: CurrentUser = Depends(require_approved()),
|
||
):
|
||
"""返回当前门店的业务时钟。
|
||
|
||
返回示例(live)::
|
||
|
||
{
|
||
"mode": "live",
|
||
"business_date": "2026-05-02",
|
||
"business_year": 2026,
|
||
"business_month": 5,
|
||
"business_year_month": "2026-05",
|
||
"is_sandbox": false,
|
||
"sandbox_date": null
|
||
}
|
||
|
||
sandbox 模式下 ``business_date`` 等于配置的 ``sandbox_date``。
|
||
小程序页面应使用本接口结果替代 ``new Date()``,以确保 sandbox 模式下
|
||
展示和请求都对齐到 sandbox_date。
|
||
"""
|
||
ctx = get_runtime_context(user.site_id)
|
||
bd = ctx.business_date
|
||
return {
|
||
"mode": ctx.mode,
|
||
"business_date": bd.isoformat(),
|
||
"business_year": bd.year,
|
||
"business_month": bd.month,
|
||
"business_year_month": f"{bd.year:04d}-{bd.month:02d}",
|
||
"business_now": ctx.business_now.isoformat(),
|
||
"is_sandbox": ctx.is_sandbox,
|
||
"sandbox_date": ctx.sandbox_date.isoformat() if ctx.sandbox_date else None,
|
||
"sandbox_instance_id": ctx.sandbox_instance_id,
|
||
}
|