chenchaoyun commited on
Commit ·
8f26f9a
1
Parent(s): 9a7c81e
feat: 增加 app_analyze 接口异步记录设备信息功能
Browse files- api_routes.py +30 -0
- database.py +79 -0
api_routes.py
CHANGED
|
@@ -53,6 +53,7 @@ from database import (
|
|
| 53 |
fetch_records_by_paths,
|
| 54 |
infer_category_from_filename,
|
| 55 |
fetch_today_category_counts,
|
|
|
|
| 56 |
)
|
| 57 |
|
| 58 |
SERVER_HOSTNAME = os.environ.get("HOSTNAME", "")
|
|
@@ -1574,6 +1575,35 @@ async def app_analyze_face(
|
|
| 1574 |
"""
|
| 1575 |
App专用人脸分析接口,参数与原 /analyze 保持完全一致并透传。
|
| 1576 |
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1577 |
return await analyze_face(
|
| 1578 |
request=request,
|
| 1579 |
file=file,
|
|
|
|
| 53 |
fetch_records_by_paths,
|
| 54 |
infer_category_from_filename,
|
| 55 |
fetch_today_category_counts,
|
| 56 |
+
upsert_device_record,
|
| 57 |
)
|
| 58 |
|
| 59 |
SERVER_HOSTNAME = os.environ.get("HOSTNAME", "")
|
|
|
|
| 1575 |
"""
|
| 1576 |
App专用人脸分析接口,参数与原 /analyze 保持完全一致并透传。
|
| 1577 |
"""
|
| 1578 |
+
# 异步记录设备信息
|
| 1579 |
+
device_id = request.headers.get("x-device-id")
|
| 1580 |
+
if device_id:
|
| 1581 |
+
try:
|
| 1582 |
+
# 提取公共头
|
| 1583 |
+
ts_str = request.headers.get("x-timestamp")
|
| 1584 |
+
ts_ms = int(ts_str) if ts_str and ts_str.isdigit() else None
|
| 1585 |
+
|
| 1586 |
+
device_info = {
|
| 1587 |
+
"device_id": device_id,
|
| 1588 |
+
"device_type": request.headers.get("x-device-type"),
|
| 1589 |
+
"device_model": request.headers.get("x-device-model"),
|
| 1590 |
+
"os_version": request.headers.get("x-os-version"),
|
| 1591 |
+
"app_id": request.headers.get("x-app-id"),
|
| 1592 |
+
"app_version": request.headers.get("x-app-version"),
|
| 1593 |
+
"bundle_id": request.headers.get("x-bundle-id"),
|
| 1594 |
+
"timezone": request.headers.get("x-timezone"),
|
| 1595 |
+
"region": request.headers.get("x-region"),
|
| 1596 |
+
"language": request.headers.get("Accept-Language"),
|
| 1597 |
+
"request_id": request.headers.get("x-request-id"),
|
| 1598 |
+
"timestamp_ms": ts_ms,
|
| 1599 |
+
"nonce": request.headers.get("x-nonce"),
|
| 1600 |
+
"nickname": nickname
|
| 1601 |
+
}
|
| 1602 |
+
# 创建异步任务,不阻塞当前请求
|
| 1603 |
+
asyncio.create_task(upsert_device_record(**device_info))
|
| 1604 |
+
except Exception as e:
|
| 1605 |
+
logger.warning(f"Failed to record device info for {device_id}: {e}")
|
| 1606 |
+
|
| 1607 |
return await analyze_face(
|
| 1608 |
request=request,
|
| 1609 |
file=file,
|
database.py
CHANGED
|
@@ -334,6 +334,85 @@ def infer_category_from_filename(filename: str, default: str = "other") -> str:
|
|
| 334 |
|
| 335 |
from config import HOSTNAME
|
| 336 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 337 |
async def record_image_creation(
|
| 338 |
*,
|
| 339 |
file_path: str,
|
|
|
|
| 334 |
|
| 335 |
from config import HOSTNAME
|
| 336 |
|
| 337 |
+
async def upsert_device_record(
|
| 338 |
+
*,
|
| 339 |
+
device_id: str,
|
| 340 |
+
device_type: Optional[str] = None,
|
| 341 |
+
device_model: Optional[str] = None,
|
| 342 |
+
os_version: Optional[str] = None,
|
| 343 |
+
app_id: Optional[str] = None,
|
| 344 |
+
app_version: Optional[str] = None,
|
| 345 |
+
bundle_id: Optional[str] = None,
|
| 346 |
+
timezone: Optional[str] = None,
|
| 347 |
+
region: Optional[str] = None,
|
| 348 |
+
language: Optional[str] = None,
|
| 349 |
+
request_id: Optional[str] = None,
|
| 350 |
+
timestamp_ms: Optional[int] = None,
|
| 351 |
+
nonce: Optional[str] = None,
|
| 352 |
+
nickname: Optional[str] = None,
|
| 353 |
+
) -> None:
|
| 354 |
+
"""写入或更新设备记录"""
|
| 355 |
+
query = """
|
| 356 |
+
INSERT INTO tpl_app_user_devices (
|
| 357 |
+
device_id,
|
| 358 |
+
device_type,
|
| 359 |
+
device_model,
|
| 360 |
+
os_version,
|
| 361 |
+
app_id,
|
| 362 |
+
app_version,
|
| 363 |
+
bundle_id,
|
| 364 |
+
timezone,
|
| 365 |
+
region,
|
| 366 |
+
language,
|
| 367 |
+
request_id,
|
| 368 |
+
timestamp_ms,
|
| 369 |
+
nonce,
|
| 370 |
+
nickname
|
| 371 |
+
) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
|
| 372 |
+
ON DUPLICATE KEY UPDATE
|
| 373 |
+
device_type = VALUES(device_type),
|
| 374 |
+
device_model = VALUES(device_model),
|
| 375 |
+
os_version = VALUES(os_version),
|
| 376 |
+
app_id = VALUES(app_id),
|
| 377 |
+
app_version = VALUES(app_version),
|
| 378 |
+
bundle_id = VALUES(bundle_id),
|
| 379 |
+
timezone = VALUES(timezone),
|
| 380 |
+
region = VALUES(region),
|
| 381 |
+
language = VALUES(language),
|
| 382 |
+
request_id = VALUES(request_id),
|
| 383 |
+
timestamp_ms = VALUES(timestamp_ms),
|
| 384 |
+
nonce = VALUES(nonce),
|
| 385 |
+
nickname = VALUES(nickname),
|
| 386 |
+
updated_at = CURRENT_TIMESTAMP
|
| 387 |
+
"""
|
| 388 |
+
import time
|
| 389 |
+
start_time = time.perf_counter()
|
| 390 |
+
try:
|
| 391 |
+
await execute(
|
| 392 |
+
query,
|
| 393 |
+
(
|
| 394 |
+
device_id,
|
| 395 |
+
device_type,
|
| 396 |
+
device_model,
|
| 397 |
+
os_version,
|
| 398 |
+
app_id,
|
| 399 |
+
app_version,
|
| 400 |
+
bundle_id,
|
| 401 |
+
timezone,
|
| 402 |
+
region,
|
| 403 |
+
language,
|
| 404 |
+
request_id,
|
| 405 |
+
timestamp_ms,
|
| 406 |
+
nonce,
|
| 407 |
+
nickname,
|
| 408 |
+
),
|
| 409 |
+
)
|
| 410 |
+
duration = (time.perf_counter() - start_time) * 1000
|
| 411 |
+
logger.info(f"Device record updated for {device_id}, cost: {duration:.2f}ms")
|
| 412 |
+
except Exception as exc:
|
| 413 |
+
logger.warning(f"写入设备记录失败: {exc}")
|
| 414 |
+
|
| 415 |
+
|
| 416 |
async def record_image_creation(
|
| 417 |
*,
|
| 418 |
file_path: str,
|