后端重写提交

This commit is contained in:
2026-04-22 12:34:49 +08:00
parent cf8da3f50f
commit 997a8daada
438 changed files with 53355 additions and 865 deletions

View File

@@ -1,25 +1,15 @@
use axum::Json;
use serde::Serialize;
use serde_json::{Value, json};
use serde_json::Value;
#[cfg(test)]
use serde_json::json;
use shared_contracts::api::{
API_VERSION, ApiErrorEnvelope, ApiErrorPayload, ApiResponseMeta, ApiSuccessEnvelope,
LegacyApiErrorResponse,
};
use time::{OffsetDateTime, format_description::well_known::Rfc3339};
use crate::{http_error::ApiErrorPayload, request_context::RequestContext};
pub const API_VERSION: &str = "2026-04-08";
#[derive(Debug, Serialize)]
struct ApiResponseMeta {
#[serde(rename = "apiVersion")]
api_version: &'static str,
#[serde(rename = "requestId", skip_serializing_if = "Option::is_none")]
request_id: Option<String>,
#[serde(rename = "routeVersion")]
route_version: &'static str,
operation: Option<String>,
#[serde(rename = "latencyMs")]
latency_ms: u64,
timestamp: String,
}
use crate::request_context::RequestContext;
// 当前阶段先把成功响应 envelope helper 准备好,后续 `/healthz` 与业务 handler 会直接复用这里的输出逻辑。
#[allow(dead_code)]
@@ -30,12 +20,13 @@ where
if let Some(context) = request_context
&& context.wants_envelope()
{
return Json(json!({
"ok": true,
"data": data,
"error": null,
"meta": build_api_response_meta(Some(context)),
}));
return Json(
serde_json::to_value(ApiSuccessEnvelope::new(
data,
build_api_response_meta(Some(context)),
))
.unwrap_or(Value::Null),
);
}
Json(serde_json::to_value(data).unwrap_or(Value::Null))
@@ -48,33 +39,30 @@ pub fn json_error_body(
let meta = build_api_response_meta(request_context);
if request_context.is_some_and(RequestContext::wants_envelope) {
return Json(json!({
"ok": false,
"data": null,
"error": error,
"meta": meta,
}));
return Json(
serde_json::to_value(ApiErrorEnvelope::new(error.clone(), meta)).unwrap_or(Value::Null),
);
}
Json(json!({
"error": error,
"meta": meta,
}))
Json(
serde_json::to_value(LegacyApiErrorResponse::new(error.clone(), meta))
.unwrap_or(Value::Null),
)
}
fn build_api_response_meta(request_context: Option<&RequestContext>) -> ApiResponseMeta {
ApiResponseMeta {
api_version: API_VERSION,
request_id: request_context.map(|context| context.request_id().to_string()),
route_version: API_VERSION,
operation: request_context.map(|context| context.operation().to_string()),
latency_ms: request_context
ApiResponseMeta::new(
API_VERSION,
request_context.map(|context| context.request_id().to_string()),
API_VERSION,
request_context.map(|context| context.operation().to_string()),
request_context
.map(RequestContext::elapsed)
.unwrap_or_default(),
timestamp: OffsetDateTime::now_utc()
OffsetDateTime::now_utc()
.format(&Rfc3339)
.unwrap_or_else(|_| "1970-01-01T00:00:00Z".to_string()),
}
)
}
#[cfg(test)]
@@ -122,7 +110,7 @@ mod tests {
fn error_body_returns_legacy_shape_without_envelope_header() {
let request_context = build_request_context(false);
let error = ApiErrorPayload {
code: "NOT_FOUND",
code: "NOT_FOUND".to_string(),
message: "资源不存在".to_string(),
details: None,
};