error_schemas.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565
  1. """
  2. 施工方案审查API错误码统一定义
  3. 集中管理所有接口的错误码和错误响应格式
  4. """
  5. from typing import Dict, Any
  6. from fastapi import HTTPException
  7. from foundation.observability.logger.loggering import review_logger as logger
  8. class ErrorCodes:
  9. """错误码常量定义"""
  10. # 文件上传接口错误码 (WJSC001-WJSC008)
  11. WJSC001 = {
  12. "code": "WJSC001",
  13. "error_type": "FILE_MISSING",
  14. "message": "未上传文件",
  15. "status_code": 400
  16. }
  17. WJSC002 = {
  18. "code": "WJSC002",
  19. "error_type": "FILE_MULTIPLE",
  20. "message": "仅支持单文件上传",
  21. "status_code": 400
  22. }
  23. WJSC003 = {
  24. "code": "WJSC003",
  25. "error_type": "FILE_REJECTED",
  26. "message": "格式错误、内容违规、文件为空",
  27. "status_code": 400
  28. }
  29. WJSC004 = {
  30. "code": "WJSC004",
  31. "error_type": "FILE_FORMAT_UNSUPPORTED",
  32. "message": "文件格式不支持(仅允许pdf/doc/docx)",
  33. "status_code": 400
  34. }
  35. WJSC005 = {
  36. "code": "WJSC005",
  37. "error_type": "FILE_SIZE_EXCEEDED",
  38. "message": "文件过大(最大不超过50MB)",
  39. "status_code": 400
  40. }
  41. WJSC007 = {
  42. "code": "WJSC007",
  43. "error_type": "UNAUTHORIZED",
  44. "message": "认证失败(未提供或无效的Authorization)",
  45. "status_code": 401
  46. }
  47. WJSC008 = {
  48. "code": "WJSC008",
  49. "error_type": "INVALID_USER",
  50. "message": "用户标识未提供或无效",
  51. "status_code": 403
  52. }
  53. WJSC009 = {
  54. "code": "WJSC009",
  55. "error_type": "INVALID_PARAMETERS",
  56. "message": "请求参数无效或不支持",
  57. "status_code": 400
  58. }
  59. WJSC011 = {
  60. "code": "WJSC011",
  61. "error_type": "INTERNAL_ERROR",
  62. "message": "服务端内部错误",
  63. "status_code": 500
  64. }
  65. # 启动审查接口错误码 (QDSC001-QDSC006)
  66. QDSC001 = {
  67. "code": "QDSC001",
  68. "error_type": "MISSING_PARAMETERS",
  69. "message": "请求参数缺失",
  70. "status_code": 400
  71. }
  72. QDSC002 = {
  73. "code": "QDSC002",
  74. "error_type": "INVALID_PARAM_FORMAT",
  75. "message": "请求参数格式错误",
  76. "status_code": 400
  77. }
  78. QDSC003 = {
  79. "code": "QDSC003",
  80. "error_type": "UNAUTHORIZED",
  81. "message": "认证失败(未提供或无效的Authorization)",
  82. "status_code": 401
  83. }
  84. QDSC004 = {
  85. "code": "QDSC004",
  86. "error_type": "INVALID_USER",
  87. "message": "用户标识未提供或无效",
  88. "status_code": 403
  89. }
  90. QDSC005 = {
  91. "code": "QDSC005",
  92. "error_type": "TASK_NOT_FOUND",
  93. "message": "任务ID不存在或已过期",
  94. "status_code": 404
  95. }
  96. QDSC006 = {
  97. "code": "QDSC006",
  98. "error_type": "TASK_ALREADY_EXISTS",
  99. "message": "任务已存在,请勿重复提交",
  100. "status_code": 409
  101. }
  102. QDSC007 = {
  103. "code": "QDSC007",
  104. "error_type": "PROJECT_PLAN_TYPE_INVALID",
  105. "message": "无效工程方案类型(未提供或未注册)",
  106. "status_code": 400
  107. }
  108. QDSC008 = {
  109. "code": "QDSC008",
  110. "error_type": "ENUM_TYPE_INVALID",
  111. "message": "审查枚举类型无效",
  112. "status_code": 400
  113. }
  114. QDSC009 = {
  115. "code": "QDSC009",
  116. "error_type": "ENUM_TYPE_CANNOT_BE_NULL",
  117. "message": "审查枚举类型不能为空",
  118. "status_code": 400
  119. }
  120. QDSC010 = {
  121. "code": "QDSC010",
  122. "error_type": "FILE_INFO_NOT_FOUND",
  123. "message": "文件信息获取失败",
  124. "status_code": 500
  125. }
  126. QDSC011 = {
  127. "code": "QDSC011",
  128. "error_type": "SERVER_INTERNAL_ERROR",
  129. "message": "服务端内部错误",
  130. "status_code": 500
  131. }
  132. QDSC012 = {
  133. "code": "QDSC012",
  134. "error_type": "TASK_NOT_FOUND_OR_EXPIRED",
  135. "message": "任务ID不存在或已过期,请重新检查callback_task_id是否正确,或重新上传文件",
  136. "status_code": 404
  137. }
  138. QDSC013 = {
  139. "code": "QDSC013",
  140. "error_type": "FILE_INFO_NOT_FOUND",
  141. "message": "文件信息获取失败,任务ID不存在或已过期",
  142. "status_code": 404
  143. }
  144. QDSC014 = {
  145. "code": "QDSC014",
  146. "error_type": "TENDENCY_REVIEW_ROLE_INVALID",
  147. "message": "倾向性审查角色无效(未提供或未注册)",
  148. "status_code": 400
  149. }
  150. QDSC015 = {
  151. "code": "QDSC015",
  152. "error_type": "INVALID_REVIEW_ITEM_FORMAT",
  153. "message": "审查项配置格式错误,必须为「chapter_code_review_dimension」格式",
  154. "status_code": 400
  155. }
  156. QDSC016 = {
  157. "code": "QDSC016",
  158. "error_type": "INVALID_CHAPTER_CODE",
  159. "message": "章节code不支持",
  160. "status_code": 400
  161. }
  162. QDSC017 = {
  163. "code": "QDSC017",
  164. "error_type": "INVALID_REVIEW_DIMENSION",
  165. "message": "审查项code不支持",
  166. "status_code": 400
  167. }
  168. QDSC018 = {
  169. "code": "QDSC018",
  170. "error_type": "MUTUALLY_EXCLUSIVE_CONFIG",
  171. "message": "review_config与review_item_config互斥,只能提供其中一个",
  172. "status_code": 400
  173. }
  174. QDSC019 = {
  175. "code": "QDSC019",
  176. "error_type": "REVIEW_CONFIG_REQUIRED",
  177. "message": "必须提供review_config或review_item_config中的其中一个",
  178. "status_code": 400
  179. }
  180. QDSC020 = {
  181. "code": "QDSC020",
  182. "error_type": "DUPLICATE_REVIEW_ITEMS",
  183. "message": "审查项配置中存在重复项",
  184. "status_code": 400
  185. }
  186. QDSC021 = {
  187. "code": "QDSC021",
  188. "error_type": "CATALOGUE_COMPLETENESS_ONLY",
  189. "message": "目录章节仅支持完整性审查",
  190. "status_code": 400
  191. }
  192. # 审查结果接口错误码 (SCJG001-SCJG008)
  193. SCJG001 = {
  194. "code": "SCJG001",
  195. "error_type": "INVALID_TYPE",
  196. "message": "结果类型无效(非'summary'或'issues')",
  197. "status_code": 400
  198. }
  199. SCJG002 = {
  200. "code": "SCJG002",
  201. "error_type": "MISSING_PARAM_ID",
  202. "message": "callback_task_id缺失",
  203. "status_code": 400
  204. }
  205. SCJG003 = {
  206. "code": "SCJG003",
  207. "error_type": "INVALID_ID_FORMAT",
  208. "message": "callback_task_id格式错误",
  209. "status_code": 400
  210. }
  211. SCJG004 = {
  212. "code": "SCJG004",
  213. "error_type": "UNAUTHORIZED",
  214. "message": "认证失败(未提供或无效的Authorization)",
  215. "status_code": 401
  216. }
  217. SCJG005 = {
  218. "code": "SCJG005",
  219. "error_type": "INVALID_USER",
  220. "message": "用户标识未提供或无效",
  221. "status_code": 403
  222. }
  223. SCJG006 = {
  224. "code": "SCJG006",
  225. "error_type": "TASK_NOT_FOUND",
  226. "message": "callback_task_id不存在或已过期",
  227. "status_code": 404
  228. }
  229. SCJG007 = {
  230. "code": "SCJG007",
  231. "error_type": "NO_REVIEW_RESULTS",
  232. "message": "无审查结果数据",
  233. "status_code": 404
  234. }
  235. SCJG008 = {
  236. "code": "SCJG008",
  237. "error_type": "SERVER_ERROR",
  238. "message": "服务端内部错误(审查结果生成失败)",
  239. "status_code": 500
  240. }
  241. def create_http_exception(error_code: Dict[str, Any], custom_message: str = None) -> HTTPException:
  242. """
  243. 创建HTTP异常
  244. Args:
  245. error_code: 错误码字典
  246. custom_message: 自定义错误消息,可选
  247. Returns:
  248. HTTPException: FastAPI异常对象
  249. """
  250. detail = {
  251. "code": error_code["code"],
  252. "error_type": error_code["error_type"],
  253. "message": custom_message or error_code["message"]
  254. }
  255. return HTTPException(
  256. status_code=error_code["status_code"],
  257. detail=detail
  258. )
  259. def create_server_error(error_code: str, original_error: Exception) -> HTTPException:
  260. """
  261. 创建服务器内部错误异常
  262. Args:
  263. error_code: 错误码 (如 "WJSC008", "QDSC006", "SCJG008")
  264. original_error: 原始异常
  265. Returns:
  266. HTTPException: FastAPI异常对象
  267. """
  268. error_map = {
  269. "WJSC011": ErrorCodes.WJSC011,
  270. "QDSC006": ErrorCodes.QDSC006,
  271. "SCJG008": ErrorCodes.SCJG008
  272. }
  273. error_config = error_map.get(error_code, ErrorCodes.WJSC008)
  274. message = f"{error_config['message']}: {str(original_error)}"
  275. return create_http_exception(error_config, message)
  276. # 便捷的错误创建函数
  277. class FileUploadErrors:
  278. """文件上传接口错误"""
  279. @staticmethod
  280. def file_missing():
  281. logger.error(ErrorCodes.WJSC001)
  282. return create_http_exception(ErrorCodes.WJSC001)
  283. @staticmethod
  284. def file_multiple():
  285. logger.error(ErrorCodes.WJSC002)
  286. return create_http_exception(ErrorCodes.WJSC002)
  287. @staticmethod
  288. def file_rejected(message: str = None):
  289. logger.error(ErrorCodes.WJSC003)
  290. return create_http_exception(ErrorCodes.WJSC003, message)
  291. @staticmethod
  292. def file_format_unsupported():
  293. logger.error(ErrorCodes.WJSC004)
  294. return create_http_exception(ErrorCodes.WJSC004)
  295. @staticmethod
  296. def file_size_exceeded():
  297. logger.error(ErrorCodes.WJSC005)
  298. return create_http_exception(ErrorCodes.WJSC005)
  299. @staticmethod
  300. def project_plan_type_invalid():
  301. logger.error(ErrorCodes.WJSC006)
  302. return create_http_exception(ErrorCodes.WJSC006)
  303. @staticmethod
  304. def unauthorized():
  305. logger.error(ErrorCodes.WJSC007)
  306. return create_http_exception(ErrorCodes.WJSC007)
  307. @staticmethod
  308. def invalid_user():
  309. logger.error(ErrorCodes.WJSC008)
  310. return create_http_exception(ErrorCodes.WJSC008)
  311. @staticmethod
  312. def task_already_exists():
  313. logger.error(ErrorCodes.WJSC010)
  314. return create_http_exception(ErrorCodes.WJSC010)
  315. @staticmethod
  316. def invalid_parameters():
  317. logger.error(ErrorCodes.WJSC009)
  318. return create_http_exception(ErrorCodes.WJSC009)
  319. @staticmethod
  320. def internal_error(original_error: Exception):
  321. logger.error(ErrorCodes.WJSC011)
  322. return create_server_error("WJSC011", original_error)
  323. class LaunchReviewErrors:
  324. """启动审查接口错误"""
  325. @staticmethod
  326. def missing_parameters():
  327. logger.error(ErrorCodes.QDSC001)
  328. return create_http_exception(ErrorCodes.QDSC001)
  329. @staticmethod
  330. def invalid_param_format():
  331. logger.error(ErrorCodes.QDSC002)
  332. return create_http_exception(ErrorCodes.QDSC002)
  333. @staticmethod
  334. def unauthorized():
  335. logger.error(ErrorCodes.QDSC003)
  336. return create_http_exception(ErrorCodes.QDSC003)
  337. @staticmethod
  338. def invalid_user():
  339. logger.error(ErrorCodes.QDSC004)
  340. return create_http_exception(ErrorCodes.QDSC004)
  341. @staticmethod
  342. def task_not_found():
  343. logger.error(ErrorCodes.QDSC005)
  344. return create_http_exception(ErrorCodes.QDSC005)
  345. @staticmethod
  346. def task_already_exists():
  347. logger.error(ErrorCodes.QDSC006)
  348. return create_http_exception(ErrorCodes.QDSC006)
  349. @staticmethod
  350. def project_plan_type_invalid():
  351. logger.error(ErrorCodes.QDSC007)
  352. return create_http_exception(ErrorCodes.QDSC007)
  353. @staticmethod
  354. def enum_type_invalid():
  355. logger.error(ErrorCodes.QDSC008)
  356. return create_http_exception(ErrorCodes.QDSC008)
  357. @staticmethod
  358. def enum_type_cannot_be_null():
  359. logger.error(ErrorCodes.QDSC009)
  360. return create_http_exception(ErrorCodes.QDSC009)
  361. @staticmethod
  362. def file_info_not_found(original_error: Exception):
  363. logger.error(ErrorCodes.QDSC010)
  364. return create_server_error("QDSC010", original_error)
  365. @staticmethod
  366. def internal_error(original_error: Exception):
  367. logger.error(ErrorCodes.QDSC011)
  368. return create_server_error("QDSC011", original_error)
  369. @staticmethod
  370. def task_not_found_or_expired():
  371. logger.error(ErrorCodes.QDSC012)
  372. return create_http_exception(ErrorCodes.QDSC012)
  373. @staticmethod
  374. def file_info_not_found():
  375. logger.error(ErrorCodes.QDSC013)
  376. return create_http_exception(ErrorCodes.QDSC013)
  377. @staticmethod
  378. def tendency_review_role_invalid():
  379. logger.error(ErrorCodes.QDSC014)
  380. return create_http_exception(ErrorCodes.QDSC014)
  381. @staticmethod
  382. def invalid_review_item_format(invalid_items: list = None):
  383. """审查项配置格式错误"""
  384. logger.error(f"审查项配置格式错误: {invalid_items}")
  385. message = f"审查项配置格式错误,必须为「chapter_code_review_dimension」格式(如 basis_sensitive_word_check)。无效项: {invalid_items}"
  386. return create_http_exception(ErrorCodes.QDSC015, message)
  387. @staticmethod
  388. def invalid_chapter_code(invalid_codes: list = None):
  389. """章节code不支持"""
  390. logger.error(f"章节code不支持: {invalid_codes}")
  391. message = f"章节code不支持。无效的章节code: {invalid_codes}"
  392. return create_http_exception(ErrorCodes.QDSC016, message)
  393. @staticmethod
  394. def invalid_review_dimension(invalid_dimensions: list = None):
  395. """审查项code不支持"""
  396. logger.error(f"审查项code不支持: {invalid_dimensions}")
  397. message = f"审查项code不支持。无效的审查项code: {invalid_dimensions}"
  398. return create_http_exception(ErrorCodes.QDSC017, message)
  399. @staticmethod
  400. def mutually_exclusive_config():
  401. """review_config与review_item_config互斥"""
  402. logger.error("review_config与review_item_config同时提供,但两者互斥")
  403. message = "参数错误:review_config(审查维度列表)与review_item_config(章节_审查维度列表)互斥,只能提供其中一个,不能同时提供。"
  404. return create_http_exception(ErrorCodes.QDSC018, message)
  405. @staticmethod
  406. def review_config_required():
  407. """必须提供review_config或review_item_config"""
  408. logger.error("未提供review_config或review_item_config")
  409. message = "参数错误:必须提供review_config(审查维度列表)或review_item_config(章节_审查维度列表)中的其中一个。"
  410. return create_http_exception(ErrorCodes.QDSC019, message)
  411. @staticmethod
  412. def duplicate_review_items(duplicates: list = None):
  413. """审查项配置中存在重复项"""
  414. logger.error(f"审查项配置中存在重复项: {duplicates}")
  415. message = f"参数错误:review_item_config 中存在重复的审查项。重复项: {duplicates}"
  416. return create_http_exception(ErrorCodes.QDSC020, message)
  417. @staticmethod
  418. def catalogue_completeness_only(invalid_items: list = None):
  419. """目录章节仅支持完整性审查"""
  420. logger.error(f"目录章节使用了非完整性审查: {invalid_items}")
  421. message = f"参数错误:目录章节(catalogue)仅支持完整性审查(completeness_check)。无效配置项: {invalid_items}"
  422. return create_http_exception(ErrorCodes.QDSC021, message)
  423. class ReviewResultsErrors:
  424. """审查结果接口错误"""
  425. @staticmethod
  426. def invalid_type():
  427. logger.error(ErrorCodes.SCJG001)
  428. return create_http_exception(ErrorCodes.SCJG001)
  429. @staticmethod
  430. def missing_param_id():
  431. logger.error(ErrorCodes.SCJG002)
  432. return create_http_exception(ErrorCodes.SCJG002)
  433. @staticmethod
  434. def invalid_id_format():
  435. logger.error(ErrorCodes.SCJG003)
  436. return create_http_exception(ErrorCodes.SCJG003)
  437. @staticmethod
  438. def unauthorized():
  439. logger.error(ErrorCodes.SCJG004)
  440. return create_http_exception(ErrorCodes.SCJG004)
  441. @staticmethod
  442. def invalid_user():
  443. logger.error(ErrorCodes.SCJG005)
  444. return create_http_exception(ErrorCodes.SCJG005)
  445. @staticmethod
  446. def task_not_found():
  447. logger.error(ErrorCodes.SCJG006)
  448. return create_http_exception(ErrorCodes.SCJG006)
  449. @staticmethod
  450. def no_review_results():
  451. logger.error(ErrorCodes.SCJG007)
  452. return create_http_exception(ErrorCodes.SCJG007)
  453. @staticmethod
  454. def server_error(original_error: Exception):
  455. logger.error(ErrorCodes.SCJG008)
  456. return create_server_error("SCJG008", original_error)