routes.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  1. from flask import jsonify, request, make_response, Blueprint, current_app, send_file
  2. from app.api.data_parse import bp
  3. from app.core.data_parse.parse import parse_data, process_business_card, update_business_card, get_business_cards, update_business_card_status, create_talent_tag, get_talent_tag_list, update_talent_tag, delete_talent_tag, query_neo4j_graph
  4. from app.config.config import DevelopmentConfig, ProductionConfig
  5. import logging
  6. import boto3
  7. from botocore.config import Config
  8. from botocore.exceptions import ClientError
  9. from io import BytesIO
  10. import base64
  11. import os
  12. import urllib.parse
  13. from minio import Minio
  14. # Define logger
  15. logger = logging.getLogger(__name__)
  16. # For failure responses
  17. def failed(message, code=500):
  18. return {
  19. 'success': False,
  20. 'message': message,
  21. 'data': None
  22. }, code
  23. # 根据环境选择配置
  24. if os.environ.get('FLASK_ENV') == 'production':
  25. config = ProductionConfig()
  26. else:
  27. config = DevelopmentConfig()
  28. # 使用配置变量
  29. minio_url = f"{'https' if config.MINIO_SECURE else 'http'}://{config.MINIO_HOST}"
  30. minio_access_key = config.MINIO_USER
  31. minio_secret_key = config.MINIO_PASSWORD
  32. minio_bucket = config.MINIO_BUCKET
  33. use_ssl = config.MINIO_SECURE
  34. def get_minio_client():
  35. """获取 MinIO 客户端实例"""
  36. return Minio(
  37. '192.168.3.143:9000',
  38. access_key=config.MINIO_USER,
  39. secret_key=config.MINIO_PASSWORD,
  40. secure=config.MINIO_SECURE
  41. )
  42. # 测试用的解析数据接口。没有实际使用。
  43. @bp.route('/parse', methods=['POST'])
  44. def parse():
  45. """
  46. 解析数据的接口
  47. """
  48. try:
  49. data = request.get_json()
  50. if not data:
  51. return jsonify({'error': 'No data provided'}), 400
  52. result = parse_data(data)
  53. return jsonify(result), 200
  54. except Exception as e:
  55. return jsonify({'error': str(e)}), 500
  56. # 名片解析接口
  57. @bp.route('/business-card-parse', methods=['POST'])
  58. def parse_business_card_route():
  59. """
  60. 处理名片图片并提取信息的API接口
  61. 请求参数:
  62. - image: 名片图片文件 (multipart/form-data)
  63. 返回:
  64. - JSON: 包含提取的名片信息和处理状态
  65. """
  66. # 检查是否上传了文件
  67. if 'image' not in request.files:
  68. return jsonify({
  69. 'success': False,
  70. 'message': '未上传图片',
  71. 'data': None
  72. }), 400
  73. image_file = request.files['image']
  74. # 检查文件是否为空
  75. if image_file.filename == '':
  76. return jsonify({
  77. 'success': False,
  78. 'message': '未选择文件',
  79. 'data': None
  80. }), 400
  81. # 检查文件类型是否为图片
  82. if not image_file.content_type.startswith('image/'):
  83. return jsonify({
  84. 'success': False,
  85. 'message': '上传的文件不是图片',
  86. 'data': None
  87. }), 400
  88. # 处理名片图片
  89. result = process_business_card(image_file)
  90. if result['success']:
  91. return jsonify(result), 200
  92. else:
  93. return jsonify(result), 500
  94. # 更新名片信息接口
  95. @bp.route('/business-cards/<int:card_id>', methods=['PUT'])
  96. def update_business_card_route(card_id):
  97. """
  98. 更新名片信息的API接口
  99. 路径参数:
  100. - card_id: 名片记录ID
  101. 请求参数:
  102. - JSON格式的名片信息
  103. 返回:
  104. - JSON: 包含更新后的名片信息和处理状态
  105. """
  106. # 获取请求数据
  107. data = request.json
  108. if not data:
  109. return jsonify({
  110. 'success': False,
  111. 'message': '请求数据为空',
  112. 'data': None
  113. }), 400
  114. # 调用业务逻辑函数处理更新
  115. result = update_business_card(card_id, data)
  116. # 根据处理结果设置HTTP状态码
  117. status_code = 200 if result['success'] else 500
  118. if 'not found' in result.get('message', '').lower() or '未找到' in result.get('message', ''):
  119. status_code = 404
  120. return jsonify(result), status_code
  121. # 获取所有名片记录的API接口
  122. @bp.route('/get-business-cards', methods=['GET'])
  123. def get_business_cards_route():
  124. """
  125. 获取所有名片记录的API接口
  126. 返回:
  127. - JSON: 包含名片记录列表和处理状态
  128. """
  129. # 调用业务逻辑函数获取名片列表
  130. result = get_business_cards()
  131. # 根据处理结果设置HTTP状态码
  132. status_code = 200 if result['success'] else 500
  133. return jsonify(result), status_code
  134. @bp.route('/update-business-cards/<int:card_id>/status', methods=['PUT'])
  135. def update_business_card_status_route(card_id):
  136. """
  137. 更新名片状态的API接口
  138. 路径参数:
  139. - card_id: 名片记录ID
  140. 请求参数:
  141. - JSON格式,包含status字段
  142. 返回:
  143. - JSON: 包含更新后的名片信息和处理状态
  144. """
  145. # 获取请求数据
  146. data = request.json
  147. if not data or 'status' not in data:
  148. return jsonify({
  149. 'success': False,
  150. 'message': '请求数据为空或缺少status字段',
  151. 'data': None
  152. }), 400
  153. status = data['status']
  154. # 调用业务逻辑函数处理状态更新
  155. result = update_business_card_status(card_id, status)
  156. # 根据处理结果设置HTTP状态码
  157. status_code = 200 if result['success'] else 500
  158. if 'not found' in result.get('message', '').lower() or '未找到' in result.get('message', ''):
  159. status_code = 404
  160. return jsonify(result), status_code
  161. # 从MinIO获取名片图片的API接口
  162. @bp.route('/business-cards/image/<path:image_path>', methods=['GET'])
  163. def get_business_card_image(image_path):
  164. """
  165. 从MinIO获取名片图片的API接口
  166. 路径参数:
  167. - image_path: MinIO中的图片路径
  168. 返回:
  169. - 图片数据流
  170. """
  171. try:
  172. # 记录下载请求信息,便于调试
  173. logger.info(f"获取名片图片请求: {image_path}")
  174. # 获取 MinIO 客户端
  175. minio_client = get_minio_client()
  176. if not minio_client:
  177. return jsonify(failed("MinIO客户端初始化失败")), 500
  178. try:
  179. # 使用正确的MinIO客户端方法
  180. data = minio_client.get_object(minio_bucket, image_path)
  181. # 创建内存文件流
  182. file_stream = BytesIO(data.read())
  183. # 获取文件名
  184. file_name = image_path.split('/')[-1]
  185. # 返回文件
  186. return send_file(
  187. file_stream,
  188. as_attachment=False, # 设置为False,让浏览器直接显示图片
  189. download_name=file_name,
  190. mimetype='image/jpeg' # 根据实际图片类型设置
  191. )
  192. except Exception as e:
  193. logger.error(f"MinIO获取文件失败: {str(e)}")
  194. return jsonify(failed(f"文件获取失败: {str(e)}")), 404
  195. except Exception as e:
  196. logger.error(f"文件下载失败: {str(e)}")
  197. return jsonify(failed(str(e))), 500
  198. finally:
  199. # 确保关闭数据流
  200. if 'data' in locals():
  201. data.close()
  202. # 创建人才标签接口
  203. @bp.route('/create-talent-tag', methods=['POST'])
  204. def create_talent_tag_route():
  205. """
  206. 创建人才标签的API接口
  207. 请求参数:
  208. - JSON格式,包含以下字段:
  209. - name: 标签名称
  210. - category: 标签分类
  211. - description: 标签描述
  212. - status: 启用状态,默认为'active'
  213. 返回:
  214. - JSON: 包含创建结果和标签信息
  215. """
  216. try:
  217. # 获取请求数据
  218. data = request.get_json()
  219. if not data:
  220. return jsonify({
  221. 'success': False,
  222. 'message': '请求数据为空',
  223. 'data': None
  224. }), 400
  225. # 验证必要字段
  226. if 'name' not in data or not data['name']:
  227. return jsonify({
  228. 'success': False,
  229. 'message': '标签名称不能为空',
  230. 'data': None
  231. }), 400
  232. # 处理分类字段,如果未提供则设置默认值
  233. if 'category' not in data or not data['category']:
  234. data['category'] = '未分类'
  235. # 调用业务逻辑函数处理创建
  236. result = create_talent_tag(data)
  237. # 根据处理结果设置HTTP状态码
  238. status_code = 200 if result['success'] else 500
  239. return jsonify(result), status_code
  240. except Exception as e:
  241. logger.error(f"创建人才标签失败: {str(e)}")
  242. return jsonify({
  243. 'success': False,
  244. 'message': f'创建人才标签失败: {str(e)}',
  245. 'data': None
  246. }), 500
  247. # 获取人才标签列表接口
  248. @bp.route('/get-talent-tag-list', methods=['GET'])
  249. def get_talent_tag_list_route():
  250. """
  251. 获取人才标签列表的API接口
  252. 返回:
  253. - JSON: 包含人才标签列表和处理状态
  254. """
  255. try:
  256. # 调用业务逻辑函数获取人才标签列表
  257. result = get_talent_tag_list()
  258. # 根据处理结果设置HTTP状态码
  259. status_code = 200 if result['success'] else 500
  260. return jsonify(result), status_code
  261. except Exception as e:
  262. logger.error(f"获取人才标签列表失败: {str(e)}")
  263. return jsonify({
  264. 'success': False,
  265. 'message': f'获取人才标签列表失败: {str(e)}',
  266. 'data': []
  267. }), 500
  268. # 更新人才标签接口
  269. @bp.route('/update-talent-tag/<int:tag_id>', methods=['PUT'])
  270. def update_talent_tag_route(tag_id):
  271. """
  272. 更新人才标签的API接口
  273. 路径参数:
  274. - tag_id: 标签节点ID
  275. 请求参数:
  276. - JSON格式,可能包含以下字段:
  277. - name: 标签名称
  278. - category: 标签分类
  279. - description: 标签描述
  280. - status: 启用状态
  281. 返回:
  282. - JSON: 包含更新结果和标签信息
  283. """
  284. try:
  285. # 获取请求数据
  286. data = request.get_json()
  287. if not data:
  288. return jsonify({
  289. 'success': False,
  290. 'message': '请求数据为空',
  291. 'data': None
  292. }), 400
  293. # 调用业务逻辑函数处理更新
  294. result = update_talent_tag(tag_id, data)
  295. # 根据处理结果设置HTTP状态码
  296. if not result['success']:
  297. if result['code'] == 404:
  298. status_code = 404
  299. elif result['code'] == 400:
  300. status_code = 400
  301. else:
  302. status_code = 500
  303. else:
  304. status_code = 200
  305. return jsonify(result), status_code
  306. except Exception as e:
  307. logger.error(f"更新人才标签失败: {str(e)}")
  308. return jsonify({
  309. 'success': False,
  310. 'message': f'更新人才标签失败: {str(e)}',
  311. 'data': None
  312. }), 500
  313. # 删除人才标签接口
  314. @bp.route('/delete-talent-tag/<int:tag_id>', methods=['DELETE'])
  315. def delete_talent_tag_route(tag_id):
  316. """
  317. 删除人才标签的API接口
  318. 路径参数:
  319. - tag_id: 标签节点ID
  320. 返回:
  321. - JSON: 包含删除结果和被删除的标签信息
  322. """
  323. try:
  324. # 调用业务逻辑函数执行删除
  325. result = delete_talent_tag(tag_id)
  326. # 根据处理结果设置HTTP状态码
  327. if not result['success']:
  328. if result['code'] == 404:
  329. status_code = 404
  330. else:
  331. status_code = 500
  332. else:
  333. status_code = 200
  334. return jsonify(result), status_code
  335. except Exception as e:
  336. logger.error(f"删除人才标签失败: {str(e)}")
  337. return jsonify({
  338. 'success': False,
  339. 'message': f'删除人才标签失败: {str(e)}',
  340. 'data': None
  341. }), 500
  342. @bp.route('/query-kg', methods=['POST'])
  343. def query_kg():
  344. """
  345. 查询知识图谱API接口
  346. 请求参数:
  347. - query_requirement: 查询需求描述(JSON格式)
  348. 返回:
  349. - JSON: 包含查询结果和处理状态
  350. """
  351. try:
  352. # 获取请求数据
  353. data = request.json
  354. if not data or 'query_requirement' not in data:
  355. return jsonify({
  356. 'code': 400,
  357. 'success': False,
  358. 'message': '请求数据为空或缺少query_requirement字段',
  359. 'data': []
  360. }), 400
  361. query_requirement = data['query_requirement']
  362. # 调用业务逻辑函数执行查询
  363. result = query_neo4j_graph(query_requirement)
  364. # 根据处理结果设置HTTP状态码
  365. status_code = 200 if result['success'] else 500
  366. return jsonify(result), status_code
  367. except Exception as e:
  368. logger.error(f"查询知识图谱失败: {str(e)}")
  369. return jsonify({
  370. 'code': 500,
  371. 'success': False,
  372. 'message': f"查询知识图谱失败: {str(e)}",
  373. 'data': []
  374. }), 500