feat: add public h5 dashboard and publish traceability

This commit is contained in:
Wyndham ARR
2026-08-03 13:04:41 +08:00
parent 7e7470821b
commit 2107f00e32
29 changed files with 1047 additions and 71 deletions

View File

@@ -76,6 +76,21 @@ LOGIN_STATIC_ROUTES = {
}
LOGIN_DOCUMENT_ROUTES = {"/login", "/login.html"}
H5_DOCUMENT_ROUTES = {"/h5", "/h5.html"}
PUBLIC_H5_STATIC_ROUTES = H5_DOCUMENT_ROUTES | {
"/assets/h5.css",
"/assets/h5.js",
}
_PUBLIC_H5_DASHBOARD_KEYS = (
"version",
"month_key",
"updated_at",
"min_arrival_date",
"max_arrival_date",
"overall",
"channels",
)
def _paged_success(
@@ -97,6 +112,14 @@ def _paged_success(
)
def _public_h5_dashboard(payload: Mapping[str, Any]) -> Dict[str, Any]:
return {
key: payload[key]
for key in _PUBLIC_H5_DASHBOARD_KEYS
if key in payload
}
def _strict_json(raw: bytes) -> Mapping[str, Any]:
def pairs(values: list[tuple[str, Any]]) -> Dict[str, Any]:
output: Dict[str, Any] = {}
@@ -261,6 +284,31 @@ class PortalApplication:
return self._static(route.path)
if method == "POST" and route.path == "/api/login":
return self._login(body, normalized_headers, client_id)
if method == "GET" and route.path in PUBLIC_H5_STATIC_ROUTES:
return self._static(route.path)
if method == "GET" and route.path == "/api/public/h5/months":
return Response.json(
200,
success(
[
{
"month_key": item.get("month_key"),
"updated_at": item.get("updated_at"),
"max_arrival_date": item.get("max_arrival_date"),
}
for item in self._repository.list_months()
]
),
)
if method == "GET" and route.path == "/api/public/h5/analytics":
query = parse_qs(route.query, keep_blank_values=True)
month = validate_month(
self._one(query, "month", self._current_month())
)
return Response.json(
200,
success(_public_h5_dashboard(self._dashboard_payload(month))),
)
if current_session is None:
return self._authentication_required(method, route.path)
if method == "GET" and route.path in STATIC_ROUTES:
@@ -410,17 +458,7 @@ class PortalApplication:
error.safe_message,
422,
) from None
try:
dashboard = self._repository.read_dashboard(month)
except PortalDataError as error:
if error.code == "ANALYTICS_MONTH_NOT_FOUND":
raise PortalError(
"MONTHLY_NOT_FOUND",
"找不到该月份的正式月报。",
404,
) from None
raise
return Response.json(200, dashboard)
return Response.json(200, self._dashboard_payload(month))
if method == "GET" and route.path == "/api/channel-detail":
query = parse_qs(route.query, keep_blank_values=True)
month = validate_month(self._one(query, "month", self._current_month()))
@@ -648,7 +686,8 @@ class PortalApplication:
"请先完成或放弃当前 Excel 提取草稿",
409,
)
if self._booking_sources.current() is None:
current_source = self._booking_sources.current()
if current_source is None:
raise PortalError(
"BOOKING_EXCEL_SOURCE_REQUIRED",
"请先上传并校验 Excel 报表",
@@ -671,7 +710,13 @@ class PortalApplication:
)
return Response.json(
202,
success(self._company_reports.create(report_month, period)),
success(
self._company_reports.create(
report_month,
period,
source=current_source,
)
),
)
raise PortalError("ROUTE_NOT_FOUND", "页面或接口不存在", 404)
except PortalDataError as error:
@@ -701,6 +746,18 @@ class PortalApplication:
{"Cache-Control": "no-store"},
)
def _dashboard_payload(self, month: str) -> Dict[str, Any]:
try:
return self._repository.read_dashboard(month)
except PortalDataError as error:
if error.code == "ANALYTICS_MONTH_NOT_FOUND":
raise PortalError(
"MONTHLY_NOT_FOUND",
"找不到该月份的正式月报。",
404,
) from None
raise
def _login(
self,
body: bytes,