mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-22 15:13:55 +08:00
106 lines
3.4 KiB
Python
106 lines
3.4 KiB
Python
from __future__ import annotations
|
|
|
|
import dataclasses
|
|
import re
|
|
from collections.abc import Mapping, Sequence
|
|
from datetime import date, datetime, time
|
|
from decimal import Decimal
|
|
from enum import Enum
|
|
from pathlib import Path
|
|
from typing import Any
|
|
from zoneinfo import ZoneInfo
|
|
|
|
from pydantic import BaseModel
|
|
|
|
from app.core.config import get_settings
|
|
|
|
_SNAKE_PART = re.compile(r"_([a-zA-Z0-9])")
|
|
_LONG_FIELD_NAMES = {
|
|
"id",
|
|
"userId",
|
|
"creator",
|
|
"updater",
|
|
"createUserId",
|
|
"updateUserId",
|
|
"createDateTimestamp",
|
|
"createTime",
|
|
"createTimeFrom",
|
|
"createTimeTo",
|
|
"fileSize",
|
|
"lastConnectedAtTimestamp",
|
|
"pid",
|
|
"reportTime",
|
|
"size",
|
|
"timestamp",
|
|
"tokenCount",
|
|
"tokenNum",
|
|
"totalDocCount",
|
|
"totalTokenCount",
|
|
"updateTime",
|
|
}
|
|
|
|
|
|
class JavaMap(dict[str, Any]):
|
|
"""Marker for Java ``Map`` payloads whose keys Jackson leaves untouched."""
|
|
|
|
|
|
def preserve_java_map_keys(value: Any) -> Any:
|
|
"""Recursively mark a dynamic Java Map/List graph as key-preserving."""
|
|
|
|
if isinstance(value, Mapping):
|
|
return JavaMap({str(key): preserve_java_map_keys(item) for key, item in value.items()})
|
|
if isinstance(value, Sequence) and not isinstance(value, str | bytes | bytearray):
|
|
return [preserve_java_map_keys(item) for item in value]
|
|
return value
|
|
|
|
|
|
def snake_to_camel(value: str) -> str:
|
|
return _SNAKE_PART.sub(lambda match: match.group(1).upper(), value)
|
|
|
|
|
|
def _is_long_field(name: str | None) -> bool:
|
|
if not name:
|
|
return False
|
|
return name in _LONG_FIELD_NAMES or name.endswith("Id") or name.endswith("Ids")
|
|
|
|
|
|
def java_compatible(value: Any, *, field_name: str | None = None) -> Any:
|
|
if value is None or isinstance(value, str | bool | float):
|
|
return value
|
|
if isinstance(value, BaseModel):
|
|
return java_compatible(value.model_dump(by_alias=True, exclude_unset=False), field_name=field_name)
|
|
if dataclasses.is_dataclass(value) and not isinstance(value, type):
|
|
return java_compatible(dataclasses.asdict(value), field_name=field_name)
|
|
if isinstance(value, Enum):
|
|
return java_compatible(value.value, field_name=field_name)
|
|
if isinstance(value, datetime):
|
|
timezone = ZoneInfo(get_settings().timezone)
|
|
localized = value.astimezone(timezone) if value.tzinfo else value
|
|
return localized.strftime("%Y-%m-%d %H:%M:%S")
|
|
if isinstance(value, date):
|
|
return value.strftime("%Y-%m-%d")
|
|
if isinstance(value, time):
|
|
return value.strftime("%H:%M:%S")
|
|
if isinstance(value, Decimal):
|
|
return float(value)
|
|
if isinstance(value, int):
|
|
return str(value) if _is_long_field(field_name) or not -(2**31) <= value < 2**31 else value
|
|
if isinstance(value, bytes):
|
|
return value
|
|
if isinstance(value, Path):
|
|
return str(value)
|
|
if isinstance(value, JavaMap):
|
|
return {
|
|
str(raw_key): java_compatible(item, field_name=snake_to_camel(str(raw_key)))
|
|
for raw_key, item in value.items()
|
|
}
|
|
if isinstance(value, Mapping):
|
|
result: dict[str, Any] = {}
|
|
for raw_key, item in value.items():
|
|
key = snake_to_camel(str(raw_key))
|
|
result[key] = java_compatible(item, field_name=key)
|
|
return result
|
|
if isinstance(value, Sequence) and not isinstance(value, str | bytes | bytearray):
|
|
return [java_compatible(item, field_name=field_name) for item in value]
|
|
return value
|