scene.py 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775
  1. import json
  2. import logging
  3. import time
  4. from typing import Any, Optional
  5. from fastapi import APIRouter, Depends, Request
  6. from pydantic import BaseModel
  7. from sqlalchemy.orm import Session
  8. from database import get_db
  9. from models.scene import (
  10. FirstScene,
  11. RecognitionRecord,
  12. Scene,
  13. SceneTemplate,
  14. SecondScene,
  15. ThirdScene,
  16. )
  17. router = APIRouter()
  18. logger = logging.getLogger(__name__)
  19. def _get_user_code(user: Any) -> str:
  20. return (
  21. getattr(user, "userCode", None)
  22. or getattr(user, "user_code", None)
  23. or getattr(user, "account", "")
  24. )
  25. def _load_hazard_details(record: RecognitionRecord):
  26. if not record.hazard_details:
  27. return []
  28. try:
  29. data = json.loads(record.hazard_details)
  30. return data if isinstance(data, list) else []
  31. except Exception:
  32. return []
  33. def _split_labels(labels):
  34. if not labels:
  35. return []
  36. if isinstance(labels, list):
  37. return [str(item).strip() for item in labels if str(item).strip()]
  38. return [
  39. item.strip()
  40. for item in str(labels).replace(",", ",").split(",")
  41. if item.strip()
  42. ]
  43. def _unique_ordered(items):
  44. seen = set()
  45. ordered = []
  46. for item in items:
  47. if not item or item in seen:
  48. continue
  49. seen.add(item)
  50. ordered.append(item)
  51. return ordered
  52. def _build_record_view(record: RecognitionRecord, db: Session = None):
  53. hazard_details = _load_hazard_details(record)
  54. derived_labels = _unique_ordered(
  55. [
  56. str(item.get("label") or "").strip()
  57. for item in hazard_details
  58. if str(item.get("label") or "").strip()
  59. ]
  60. )
  61. display_labels = _split_labels(record.labels) or derived_labels
  62. if record.description:
  63. third_scenes = [item for item in str(record.description).split(" ") if item]
  64. else:
  65. third_scenes = derived_labels
  66. detections = [
  67. {
  68. "label": item.get("label", ""),
  69. "box": item.get("bbox") or item.get("box") or [],
  70. "bbox": item.get("bbox") or item.get("box") or [],
  71. "confidence": item.get("confidence", 0),
  72. }
  73. for item in hazard_details
  74. ]
  75. # Rebuild element_hazards from database if db session is provided
  76. element_hazards = {}
  77. if db and display_labels:
  78. try:
  79. from models.scene import SecondScene, ThirdScene
  80. from utils.label_translator import translate_scenes_for_query
  81. for label in display_labels:
  82. # Translate sub-secondary scene to secondary scene for database query
  83. # e.g., "加油机" -> "加油设施_附属场地"
  84. query_label = translate_scenes_for_query([label])[0] if label else label
  85. # Find matching secondary scene using translated name
  86. second_scene = (
  87. db.query(SecondScene)
  88. .filter(
  89. SecondScene.second_scene_name == query_label,
  90. SecondScene.is_deleted == 0,
  91. )
  92. .first()
  93. )
  94. if second_scene:
  95. # Get all third scenes for this secondary scene
  96. third_scene_records = (
  97. db.query(ThirdScene)
  98. .filter(
  99. ThirdScene.second_scene_id == second_scene.id,
  100. ThirdScene.is_deleted == 0,
  101. )
  102. .all()
  103. )
  104. if third_scene_records:
  105. # Use original label as key for frontend matching
  106. element_hazards[label] = [
  107. ts.third_scene_name for ts in third_scene_records
  108. ]
  109. else:
  110. element_hazards[label] = []
  111. else:
  112. element_hazards[label] = []
  113. except Exception as e:
  114. logger.warning(
  115. "_build_record_view failed to rebuild element_hazards: record_id=%s error=%s",
  116. record.id,
  117. e,
  118. )
  119. return {
  120. "id": record.id,
  121. "title": record.title or "隐患提示记录",
  122. "description": record.description or " ".join(third_scenes),
  123. "original_image_url": record.original_image_url,
  124. "recognition_image_url": record.recognition_image_url,
  125. "labels": record.labels or ",".join(display_labels),
  126. "display_labels": display_labels,
  127. "third_scenes": third_scenes,
  128. "element_hazards": element_hazards,
  129. "tag_type": record.tag_type or record.scene_type,
  130. "scene_type": record.scene_type,
  131. "effect_evaluation": record.effect_evaluation,
  132. "hazard_details": hazard_details,
  133. "detections": detections,
  134. }
  135. def _resolve_record_id(
  136. recognition_id: Optional[int] = None,
  137. recognition_record_id: Optional[int] = None,
  138. ):
  139. return recognition_id or recognition_record_id
  140. @router.get("/get_scene_list")
  141. async def get_scene_list(db: Session = Depends(get_db)):
  142. scenes = db.query(Scene).filter(Scene.is_deleted == 0).all()
  143. return {
  144. "statusCode": 200,
  145. "msg": "success",
  146. "data": [
  147. {
  148. "id": s.id,
  149. "scene_name": s.scene_name,
  150. "scene_en_name": s.scene_en_name,
  151. }
  152. for s in scenes
  153. ],
  154. }
  155. @router.get("/get_first_scene_list")
  156. async def get_first_scene_list(scene_id: int, db: Session = Depends(get_db)):
  157. scenes = (
  158. db.query(FirstScene)
  159. .filter(FirstScene.scene_id == scene_id, FirstScene.is_deleted == 0)
  160. .all()
  161. )
  162. return {
  163. "statusCode": 200,
  164. "msg": "success",
  165. "data": [{"id": s.id, "first_scene_name": s.first_scene_name} for s in scenes],
  166. }
  167. @router.get("/get_second_scene_list")
  168. async def get_second_scene_list(
  169. first_scene_id: int, db: Session = Depends(get_db)
  170. ):
  171. scenes = (
  172. db.query(SecondScene)
  173. .filter(
  174. SecondScene.first_scene_id == first_scene_id,
  175. SecondScene.is_deleted == 0,
  176. )
  177. .all()
  178. )
  179. return {
  180. "statusCode": 200,
  181. "msg": "success",
  182. "data": [{"id": s.id, "second_scene_name": s.second_scene_name} for s in scenes],
  183. }
  184. @router.get("/get_third_scene_list")
  185. async def get_third_scene_list(
  186. second_scene_id: int, db: Session = Depends(get_db)
  187. ):
  188. scenes = (
  189. db.query(ThirdScene)
  190. .filter(
  191. ThirdScene.second_scene_id == second_scene_id,
  192. ThirdScene.is_deleted == 0,
  193. )
  194. .all()
  195. )
  196. return {
  197. "statusCode": 200,
  198. "msg": "success",
  199. "data": [
  200. {
  201. "id": s.id,
  202. "third_scene_name": s.third_scene_name,
  203. "correct_example_image": s.correct_example_image,
  204. "wrong_example_image": s.wrong_example_image,
  205. }
  206. for s in scenes
  207. ],
  208. }
  209. @router.get("/get_third_scene_example_image")
  210. async def get_third_scene_example_image(
  211. third_scene_name: str, db: Session = Depends(get_db)
  212. ):
  213. if not third_scene_name:
  214. return {"statusCode": 400, "msg": "三级场景名称不能为空"}
  215. scene = (
  216. db.query(ThirdScene)
  217. .filter(
  218. ThirdScene.third_scene_name == third_scene_name,
  219. ThirdScene.is_deleted == 0,
  220. )
  221. .first()
  222. )
  223. if not scene:
  224. return {"statusCode": 404, "msg": "三级场景不存在"}
  225. return {
  226. "statusCode": 200,
  227. "msg": "success",
  228. "data": {
  229. "id": scene.id,
  230. "third_scene_name": scene.third_scene_name,
  231. "correct_example_image": scene.correct_example_image,
  232. "wrong_example_image": scene.wrong_example_image,
  233. },
  234. }
  235. @router.get("/get_history_recognition_record")
  236. async def get_history_recognition_record(
  237. request: Request, db: Session = Depends(get_db)
  238. ):
  239. # 让日志更容易被全局日志系统捕获:同时输出 INFO / WARNING / DEBUG 关键点
  240. logger.warning(
  241. "=== get_history_recognition_record ENTER === path=%s method=%s client=%s",
  242. request.url.path,
  243. request.method,
  244. request.client.host if request.client else None,
  245. )
  246. request_id = getattr(request.state, "request_id", None) or getattr(
  247. request.state, "trace_id", None
  248. )
  249. logger.warning(
  250. "get_history_recognition_record request context: request_id=%s state_keys=%s",
  251. request_id,
  252. list(getattr(request.state, "__dict__", {}).keys()),
  253. )
  254. user = getattr(request.state, "user", None)
  255. if not user:
  256. logger.warning(
  257. "get_history_recognition_record unauthorized: request_id=%s missing request.state.user headers=%s",
  258. request_id,
  259. {
  260. "authorization": request.headers.get("authorization"),
  261. "cookie": request.headers.get("cookie"),
  262. },
  263. )
  264. return {"statusCode": 401, "msg": "未授权"}
  265. user_code = _get_user_code(user)
  266. user_id = getattr(user, "user_id", None)
  267. account = getattr(user, "account", None)
  268. user_snapshot = {
  269. "userCode": getattr(user, "userCode", None),
  270. "user_code": getattr(user, "user_code", None),
  271. "user_id": user_id,
  272. "account": account,
  273. "type": type(user).__name__,
  274. "raw_repr": repr(user),
  275. }
  276. logger.warning(
  277. "get_history_recognition_record user resolved: request_id=%s user_code=%s user_id=%s account=%s user_snapshot=%s",
  278. request_id,
  279. user_code,
  280. user_id,
  281. account,
  282. user_snapshot,
  283. )
  284. query_filter = [
  285. RecognitionRecord.user_id == user_id,
  286. RecognitionRecord.is_deleted == 0,
  287. ]
  288. logger.warning(
  289. "get_history_recognition_record query prepared: request_id=%s filters=user_id==%s,is_deleted==0 order_by=updated_at.desc()",
  290. request_id,
  291. user_id,
  292. )
  293. logger.warning(
  294. "get_history_recognition_record query detail: request_id=%s sql_filter_user_id_value=%r sql_filter_is_deleted=0",
  295. request_id,
  296. user_id,
  297. )
  298. logger.warning(
  299. "get_history_recognition_record query mismatch check: request_id=%s record.user_id_column_against=user_id(%r) user_code_attr=%r",
  300. request_id,
  301. user_id,
  302. user_code,
  303. )
  304. try:
  305. total = db.query(RecognitionRecord).filter(*query_filter).count()
  306. logger.warning(
  307. "get_history_recognition_record count done: request_id=%s user_id=%s total=%s",
  308. request_id,
  309. user_id,
  310. total,
  311. )
  312. records = (
  313. db.query(RecognitionRecord)
  314. .filter(*query_filter)
  315. .order_by(RecognitionRecord.updated_at.desc())
  316. .all()
  317. )
  318. logger.warning(
  319. "get_history_recognition_record records loaded: request_id=%s user_id=%s loaded=%s record_ids=%s",
  320. request_id,
  321. user_id,
  322. len(records),
  323. [record.id for record in records[:20]],
  324. )
  325. logger.warning(
  326. "get_history_recognition_record records debug sample: request_id=%s sample=%s",
  327. request_id,
  328. [
  329. {
  330. "id": r.id,
  331. "user_id": r.user_id,
  332. "scene_type": r.scene_type,
  333. "created_at": r.created_at,
  334. "updated_at": r.updated_at,
  335. "is_deleted": r.is_deleted,
  336. }
  337. for r in records[:3]
  338. ],
  339. )
  340. data = []
  341. for index, record in enumerate(records):
  342. try:
  343. record_view = _build_record_view(record)
  344. logger.warning(
  345. "get_history_recognition_record record built: request_id=%s index=%s record_id=%s scene_type=%s tag_type=%s labels=%s hazard_count=%s effect_evaluation=%s created_at=%s updated_at=%s",
  346. request_id,
  347. index,
  348. record.id,
  349. record.scene_type,
  350. record_view.get("tag_type"),
  351. record_view.get("labels"),
  352. len(record_view.get("hazard_details") or []),
  353. record.effect_evaluation,
  354. record.created_at,
  355. record.updated_at,
  356. )
  357. data.append(
  358. {
  359. **record_view,
  360. "created_at": record.created_at,
  361. }
  362. )
  363. except Exception as record_error:
  364. logger.exception(
  365. "get_history_recognition_record record build failed: request_id=%s index=%s record_id=%s error=%s",
  366. request_id,
  367. index,
  368. getattr(record, "id", None),
  369. record_error,
  370. )
  371. logger.warning(
  372. "get_history_recognition_record success: request_id=%s user_id=%s total=%s returned=%s",
  373. request_id,
  374. user_id,
  375. total,
  376. len(data),
  377. )
  378. logger.warning(
  379. "get_history_recognition_record exit summary: request_id=%s user_code=%s user_id=%s account=%s total=%s returned=%s",
  380. request_id,
  381. user_code,
  382. user_id,
  383. account,
  384. total,
  385. len(data),
  386. )
  387. logger.warning("=== get_history_recognition_record EXIT OK === request_id=%s", request_id)
  388. return {
  389. "statusCode": 200,
  390. "msg": "success",
  391. "data": data,
  392. "total": total,
  393. }
  394. except Exception as exc:
  395. logger.exception(
  396. "get_history_recognition_record failed: request_id=%s user_code=%s user_id=%s account=%s error=%s",
  397. request_id,
  398. user_code,
  399. user_id,
  400. account,
  401. exc,
  402. )
  403. logger.error(
  404. "=== get_history_recognition_record EXIT ERR === request_id=%s user_code=%s user_id=%s account=%s",
  405. request_id,
  406. user_code,
  407. user_id,
  408. account,
  409. )
  410. return {"statusCode": 500, "msg": "获取历史记录失败"}
  411. @router.get("/get_recognition_record_detail")
  412. async def get_recognition_record_detail(
  413. recognition_id: Optional[int] = None,
  414. recognition_record_id: Optional[int] = None,
  415. db: Session = Depends(get_db),
  416. ):
  417. record_id = _resolve_record_id(recognition_id, recognition_record_id)
  418. logger.info(
  419. "get_recognition_record_detail start: recognition_id=%s recognition_record_id=%s resolved_record_id=%s",
  420. recognition_id,
  421. recognition_record_id,
  422. record_id,
  423. )
  424. if not record_id:
  425. logger.warning("get_recognition_record_detail invalid request: missing record id")
  426. return {"statusCode": 422, "msg": "recognition_id 不能为空"}
  427. record = (
  428. db.query(RecognitionRecord)
  429. .filter(RecognitionRecord.id == record_id, RecognitionRecord.is_deleted == 0)
  430. .first()
  431. )
  432. if not record:
  433. logger.warning("get_recognition_record_detail not found: record_id=%s", record_id)
  434. return {"statusCode": 404, "msg": "记录不存在"}
  435. record_view = _build_record_view(record, db)
  436. logger.info(
  437. "get_recognition_record_detail success: record_id=%s user_id=%s scene_type=%s element_hazards_count=%s",
  438. record.id,
  439. record.user_id,
  440. record.scene_type,
  441. len(record_view.get("element_hazards", {})),
  442. )
  443. return {
  444. "statusCode": 200,
  445. "msg": "success",
  446. "data": {
  447. "id": record.id,
  448. "user_id": record.user_id,
  449. "title": record_view["title"],
  450. "description": record_view["description"],
  451. "original_image_url": record.original_image_url,
  452. "recognition_image_url": record.recognition_image_url,
  453. "labels": record_view["labels"],
  454. "display_labels": record_view["display_labels"],
  455. "third_scenes": record_view["third_scenes"],
  456. "element_hazards": record_view["element_hazards"],
  457. "tag_type": record_view["tag_type"],
  458. "scene_type": record.scene_type,
  459. "scene_match": record.scene_match,
  460. "tip_accuracy": record.tip_accuracy,
  461. "effect_evaluation": record.effect_evaluation,
  462. "user_remark": record.user_remark,
  463. "hazard_details": record_view["hazard_details"],
  464. "detections": record_view["detections"],
  465. "created_at": record.created_at,
  466. "updated_at": record.updated_at,
  467. },
  468. }
  469. class DeleteRecognitionRequest(BaseModel):
  470. recognition_id: Optional[int] = None
  471. recognition_record_id: Optional[int] = None
  472. @router.post("/delete_recognition_record")
  473. async def delete_recognition_record(
  474. data: DeleteRecognitionRequest,
  475. request: Request,
  476. db: Session = Depends(get_db),
  477. ):
  478. user = request.state.user
  479. if not user:
  480. logger.warning("delete_recognition_record unauthorized: missing request.state.user")
  481. return {"statusCode": 401, "msg": "未授权"}
  482. record_id = _resolve_record_id(data.recognition_id, data.recognition_record_id)
  483. logger.info(
  484. "delete_recognition_record start: user_code=%s record_id=%s",
  485. _get_user_code(user),
  486. record_id,
  487. )
  488. if not record_id:
  489. logger.warning("delete_recognition_record invalid request: missing record id")
  490. return {"statusCode": 422, "msg": "recognition_id 不能为空"}
  491. affected_rows = (
  492. db.query(RecognitionRecord)
  493. .filter(
  494. RecognitionRecord.id == record_id,
  495. RecognitionRecord.user_id == _get_user_code(user),
  496. )
  497. .update({"is_deleted": 1, "deleted_at": int(time.time())})
  498. )
  499. db.commit()
  500. logger.info(
  501. "delete_recognition_record success: user_code=%s record_id=%s affected_rows=%s",
  502. _get_user_code(user),
  503. record_id,
  504. affected_rows,
  505. )
  506. return {"statusCode": 200, "msg": "删除成功"}
  507. class EvaluationRequest(BaseModel):
  508. id: int
  509. scene_match: Optional[int] = None
  510. tip_accuracy: Optional[int] = None
  511. effect_evaluation: Optional[int] = None
  512. user_remark: Optional[str] = None
  513. @router.post("/submit_evaluation")
  514. async def submit_evaluation(data: EvaluationRequest, db: Session = Depends(get_db)):
  515. logger.info(
  516. "submit_evaluation start: id=%s scene_match=%s tip_accuracy=%s effect_evaluation=%s has_user_remark=%s",
  517. data.id,
  518. data.scene_match,
  519. data.tip_accuracy,
  520. data.effect_evaluation,
  521. data.user_remark is not None,
  522. )
  523. record = (
  524. db.query(RecognitionRecord)
  525. .filter(RecognitionRecord.id == data.id, RecognitionRecord.is_deleted == 0)
  526. .first()
  527. )
  528. if not record:
  529. logger.warning("submit_evaluation record not found: id=%s", data.id)
  530. return {"statusCode": 404, "msg": "记录不存在"}
  531. if data.scene_match is not None:
  532. record.scene_match = data.scene_match
  533. if data.tip_accuracy is not None:
  534. record.tip_accuracy = data.tip_accuracy
  535. if data.effect_evaluation is not None:
  536. record.effect_evaluation = data.effect_evaluation
  537. if data.user_remark is not None:
  538. record.user_remark = data.user_remark
  539. record.updated_at = int(time.time())
  540. db.commit()
  541. logger.info("submit_evaluation success: id=%s", data.id)
  542. return {"statusCode": 200, "msg": "success"}
  543. @router.get("/get_latest_recognition_record")
  544. async def get_latest_recognition_record(
  545. request: Request, db: Session = Depends(get_db)
  546. ):
  547. user = request.state.user
  548. if not user:
  549. logger.warning("get_latest_recognition_record unauthorized: missing request.state.user")
  550. return {"statusCode": 401, "msg": "未授权"}
  551. user_code = _get_user_code(user)
  552. logger.info("get_latest_recognition_record start: user_code=%s", user_code)
  553. record = (
  554. db.query(RecognitionRecord)
  555. .filter(
  556. RecognitionRecord.user_id == _get_user_code(user),
  557. RecognitionRecord.is_deleted == 0,
  558. )
  559. .order_by(RecognitionRecord.created_at.desc())
  560. .first()
  561. )
  562. if not record:
  563. return {
  564. "statusCode": 200,
  565. "msg": "success",
  566. "data": {"effect_evaluation": 1},
  567. }
  568. return {
  569. "statusCode": 200,
  570. "msg": "success",
  571. "data": {
  572. "id": record.id,
  573. "title": record.title,
  574. "original_image_url": record.original_image_url,
  575. "recognition_image_url": record.recognition_image_url,
  576. "labels": record.labels,
  577. "created_at": record.created_at,
  578. "effect_evaluation": record.effect_evaluation,
  579. },
  580. }
  581. class SceneTemplateCreate(BaseModel):
  582. scene_name: str
  583. scene_type: str
  584. scene_desc: str = ""
  585. model_name: str
  586. @router.post("/scene_template")
  587. async def create_scene_template(
  588. data: SceneTemplateCreate, db: Session = Depends(get_db)
  589. ):
  590. template = SceneTemplate(
  591. scene_name=data.scene_name,
  592. scene_type=data.scene_type,
  593. scene_desc=data.scene_desc,
  594. model_name=data.model_name,
  595. created_at=int(time.time()),
  596. updated_at=int(time.time()),
  597. is_deleted=0,
  598. )
  599. db.add(template)
  600. db.commit()
  601. db.refresh(template)
  602. return {
  603. "statusCode": 200,
  604. "msg": "创建成功",
  605. "data": {"id": template.id},
  606. }
  607. @router.get("/scene_templates")
  608. async def get_scene_templates(
  609. page: int = 1,
  610. page_size: int = 20,
  611. db: Session = Depends(get_db),
  612. ):
  613. if page_size > 100:
  614. page_size = 100
  615. offset = (page - 1) * page_size
  616. total = db.query(SceneTemplate).filter(SceneTemplate.is_deleted == 0).count()
  617. templates = (
  618. db.query(SceneTemplate)
  619. .filter(SceneTemplate.is_deleted == 0)
  620. .order_by(SceneTemplate.created_at.desc())
  621. .offset(offset)
  622. .limit(page_size)
  623. .all()
  624. )
  625. return {
  626. "statusCode": 200,
  627. "msg": "success",
  628. "data": {
  629. "total": total,
  630. "items": [
  631. {
  632. "id": template.id,
  633. "scene_name": template.scene_name,
  634. "scene_type": template.scene_type,
  635. "scene_desc": template.scene_desc,
  636. "model_name": template.model_name,
  637. "created_at": template.created_at,
  638. }
  639. for template in templates
  640. ],
  641. },
  642. }
  643. @router.get("/recognition_records")
  644. async def get_recognition_records(
  645. request: Request,
  646. scene_type: str = "",
  647. page: int = 1,
  648. page_size: int = 20,
  649. db: Session = Depends(get_db),
  650. ):
  651. user = request.state.user
  652. if not user:
  653. return {"statusCode": 401, "msg": "未授权"}
  654. if page_size > 100:
  655. page_size = 100
  656. query = db.query(RecognitionRecord).filter(
  657. RecognitionRecord.user_id == _get_user_code(user),
  658. RecognitionRecord.is_deleted == 0,
  659. )
  660. if scene_type:
  661. query = query.filter(RecognitionRecord.scene_type == scene_type)
  662. total = query.count()
  663. offset = (page - 1) * page_size
  664. records = (
  665. query.order_by(RecognitionRecord.created_at.desc())
  666. .offset(offset)
  667. .limit(page_size)
  668. .all()
  669. )
  670. return {
  671. "statusCode": 200,
  672. "msg": "success",
  673. "data": {
  674. "total": total,
  675. "items": [
  676. {
  677. "id": record.id,
  678. "scene_type": record.scene_type,
  679. "original_image_url": record.original_image_url,
  680. "result_image_url": record.recognition_image_url,
  681. "hazard_count": record.hazard_count,
  682. "current_step": record.current_step,
  683. "created_at": record.created_at,
  684. }
  685. for record in records
  686. ],
  687. },
  688. }