routes.py 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338
  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, talent_get_tags, talent_update_tags, get_business_card, get_hotel_positions_list, add_hotel_positions, update_hotel_positions, query_hotel_positions, delete_hotel_positions, get_hotel_group_brands_list, add_hotel_group_brands, update_hotel_group_brands, query_hotel_group_brands, delete_hotel_group_brands, get_duplicate_records, process_duplicate_record, get_duplicate_record_detail
  4. # 导入新的名片图片解析函数和添加名片函数
  5. from app.core.data_parse.parse_card import process_business_card_image, add_business_card
  6. from app.config.config import DevelopmentConfig, ProductionConfig
  7. import logging
  8. import boto3
  9. from botocore.config import Config
  10. from botocore.exceptions import ClientError
  11. from io import BytesIO
  12. import base64
  13. import os
  14. import urllib.parse
  15. from minio import Minio
  16. # Define logger
  17. logger = logging.getLogger(__name__)
  18. # For failure responses
  19. def failed(message, code=500):
  20. return {
  21. 'success': False,
  22. 'message': message,
  23. 'data': None
  24. }, code
  25. # 根据环境选择配置
  26. if os.environ.get('FLASK_ENV') == 'production':
  27. config = ProductionConfig()
  28. else:
  29. config = DevelopmentConfig()
  30. # 使用配置变量
  31. minio_url = f"{'https' if config.MINIO_SECURE else 'http'}://{config.MINIO_HOST}"
  32. minio_access_key = config.MINIO_USER
  33. minio_secret_key = config.MINIO_PASSWORD
  34. minio_bucket = config.MINIO_BUCKET
  35. use_ssl = config.MINIO_SECURE
  36. def get_minio_client():
  37. """获取 MinIO 客户端实例"""
  38. return Minio(
  39. '192.168.3.143:9000',
  40. access_key=config.MINIO_USER,
  41. secret_key=config.MINIO_PASSWORD,
  42. secure=config.MINIO_SECURE
  43. )
  44. # 测试用的解析数据接口。没有实际使用。
  45. @bp.route('/parse', methods=['POST'])
  46. def parse():
  47. """
  48. 解析数据的接口
  49. """
  50. try:
  51. data = request.get_json()
  52. if not data:
  53. return jsonify({'error': 'No data provided'}), 400
  54. result = parse_data(data)
  55. return jsonify(result), 200
  56. except Exception as e:
  57. return jsonify({'error': str(e)}), 500
  58. # 名片解析接口
  59. @bp.route('/business-card-parse', methods=['POST'])
  60. def parse_business_card_route():
  61. """
  62. 解析名片图片并提取信息的API接口(仅解析,不保存到数据库)
  63. 请求参数:
  64. - image: 名片图片文件 (multipart/form-data)
  65. 返回:
  66. - JSON: 包含提取的名片信息和处理状态
  67. 注意:此接口仅负责图片解析和信息提取,不会将数据保存到数据库
  68. """
  69. # 检查是否上传了文件
  70. if 'image' not in request.files:
  71. return jsonify({
  72. 'success': False,
  73. 'message': '未上传图片',
  74. 'data': None
  75. }), 400
  76. image_file = request.files['image']
  77. # 检查文件是否为空
  78. if image_file.filename == '':
  79. return jsonify({
  80. 'success': False,
  81. 'message': '未选择文件',
  82. 'data': None
  83. }), 400
  84. # 检查文件类型是否为图片
  85. if not image_file.content_type.startswith('image/'):
  86. return jsonify({
  87. 'success': False,
  88. 'message': '上传的文件不是图片',
  89. 'data': None
  90. }), 400
  91. # 处理名片图片
  92. result = process_business_card_image(image_file)
  93. if result['success']:
  94. return jsonify(result), 200
  95. else:
  96. return jsonify(result), 500
  97. # 添加名片记录接口
  98. @bp.route('/add-business-card', methods=['POST'])
  99. def add_business_card_route():
  100. """
  101. 添加名片记录的API接口(解析图片并保存到数据库)
  102. 请求参数:
  103. - card_data: 名片信息数据 (JSON格式,可以通过form-data或JSON body传递)
  104. - image: 名片图片文件 (multipart/form-data,可选)
  105. 返回:
  106. - JSON: 包含保存结果和处理状态
  107. 注意:此接口负责业务逻辑处理,包括重复检查、MinIO上传和数据库保存
  108. """
  109. try:
  110. # 获取名片数据 - 支持两种方式
  111. card_data = None
  112. # 方式1:通过JSON body传递
  113. if request.is_json:
  114. card_data = request.get_json()
  115. # 方式2:通过form-data传递card_data字段
  116. elif 'card_data' in request.form:
  117. import json
  118. try:
  119. card_data = json.loads(request.form['card_data'])
  120. except json.JSONDecodeError:
  121. return jsonify({
  122. 'success': False,
  123. 'message': 'card_data格式错误,必须是有效的JSON字符串',
  124. 'data': None
  125. }), 400
  126. # 检查是否提供了名片数据
  127. if not card_data:
  128. return jsonify({
  129. 'success': False,
  130. 'message': '未提供名片数据,请通过JSON body或form-data的card_data字段传递',
  131. 'data': None
  132. }), 400
  133. # 获取可选的图片文件
  134. image_file = None
  135. if 'image' in request.files:
  136. image_file = request.files['image']
  137. # 检查文件是否为空
  138. if image_file.filename == '':
  139. image_file = None
  140. # 检查文件类型是否为图片
  141. elif not image_file.content_type.startswith('image/'):
  142. return jsonify({
  143. 'success': False,
  144. 'message': '上传的文件不是图片',
  145. 'data': None
  146. }), 400
  147. # 调用业务逻辑函数处理名片数据
  148. result = add_business_card(card_data, image_file)
  149. # 根据处理结果设置HTTP状态码
  150. if result['success']:
  151. if result['code'] == 200:
  152. status_code = 200
  153. elif result['code'] == 202:
  154. status_code = 202 # Accepted - 创建成功但有疑似重复记录
  155. else:
  156. status_code = 200
  157. else:
  158. if result['code'] == 400:
  159. status_code = 400
  160. else:
  161. status_code = 500
  162. return jsonify(result), status_code
  163. except Exception as e:
  164. logger.error(f"添加名片记录失败: {str(e)}")
  165. return jsonify({
  166. 'success': False,
  167. 'message': f'添加名片记录失败: {str(e)}',
  168. 'data': None
  169. }), 500
  170. # 更新名片信息接口
  171. @bp.route('/business-cards/<int:card_id>', methods=['PUT'])
  172. def update_business_card_route(card_id):
  173. """
  174. 更新名片信息的API接口
  175. 路径参数:
  176. - card_id: 名片记录ID
  177. 请求参数:
  178. - JSON格式的名片信息
  179. 返回:
  180. - JSON: 包含更新后的名片信息和处理状态
  181. """
  182. # 获取请求数据
  183. data = request.json
  184. if not data:
  185. return jsonify({
  186. 'success': False,
  187. 'message': '请求数据为空',
  188. 'data': None
  189. }), 400
  190. # 调用业务逻辑函数处理更新
  191. result = update_business_card(card_id, data)
  192. # 根据处理结果设置HTTP状态码
  193. status_code = 200 if result['success'] else 500
  194. if 'not found' in result.get('message', '').lower() or '未找到' in result.get('message', ''):
  195. status_code = 404
  196. return jsonify(result), status_code
  197. # 获取所有名片记录的API接口
  198. @bp.route('/get-business-cards', methods=['GET'])
  199. def get_business_cards_route():
  200. """
  201. 获取所有名片记录的API接口
  202. 返回:
  203. - JSON: 包含名片记录列表和处理状态
  204. """
  205. # 调用业务逻辑函数获取名片列表
  206. result = get_business_cards()
  207. # 根据处理结果设置HTTP状态码
  208. status_code = 200 if result['success'] else 500
  209. return jsonify(result), status_code
  210. @bp.route('/update-business-cards/<int:card_id>/status', methods=['PUT'])
  211. def update_business_card_status_route(card_id):
  212. """
  213. 更新名片状态的API接口
  214. 路径参数:
  215. - card_id: 名片记录ID
  216. 请求参数:
  217. - JSON格式,包含status字段
  218. 返回:
  219. - JSON: 包含更新后的名片信息和处理状态
  220. """
  221. # 获取请求数据
  222. data = request.json
  223. if not data or 'status' not in data:
  224. return jsonify({
  225. 'success': False,
  226. 'message': '请求数据为空或缺少status字段',
  227. 'data': None
  228. }), 400
  229. status = data['status']
  230. # 调用业务逻辑函数处理状态更新
  231. result = update_business_card_status(card_id, status)
  232. # 根据处理结果设置HTTP状态码
  233. status_code = 200 if result['success'] else 500
  234. if 'not found' in result.get('message', '').lower() or '未找到' in result.get('message', ''):
  235. status_code = 404
  236. return jsonify(result), status_code
  237. # 从MinIO获取名片图片的API接口
  238. @bp.route('/business-cards/image/<path:image_path>', methods=['GET'])
  239. def get_business_card_image(image_path):
  240. """
  241. 从MinIO获取名片图片的API接口
  242. 路径参数:
  243. - image_path: MinIO中的图片路径
  244. 返回:
  245. - 图片数据流
  246. """
  247. try:
  248. # 记录下载请求信息,便于调试
  249. logger.info(f"获取名片图片请求: {image_path}")
  250. # 获取 MinIO 客户端
  251. minio_client = get_minio_client()
  252. if not minio_client:
  253. return jsonify(failed("MinIO客户端初始化失败")), 500
  254. try:
  255. # 使用正确的MinIO客户端方法
  256. data = minio_client.get_object(minio_bucket, image_path)
  257. # 创建内存文件流
  258. file_stream = BytesIO(data.read())
  259. # 获取文件名
  260. file_name = image_path.split('/')[-1]
  261. # 返回文件
  262. return send_file(
  263. file_stream,
  264. as_attachment=False, # 设置为False,让浏览器直接显示图片
  265. download_name=file_name,
  266. mimetype='image/jpeg' # 根据实际图片类型设置
  267. )
  268. except Exception as e:
  269. logger.error(f"MinIO获取文件失败: {str(e)}")
  270. return jsonify(failed(f"文件获取失败: {str(e)}")), 404
  271. except Exception as e:
  272. logger.error(f"文件下载失败: {str(e)}")
  273. return jsonify(failed(str(e))), 500
  274. finally:
  275. # 确保关闭数据流
  276. if 'data' in locals():
  277. data.close()
  278. # 创建人才标签接口
  279. @bp.route('/create-talent-tag', methods=['POST'])
  280. def create_talent_tag_route():
  281. """
  282. 创建人才标签的API接口
  283. 请求参数:
  284. - JSON格式,包含以下字段:
  285. - name: 标签名称
  286. - category: 标签分类
  287. - description: 标签描述
  288. - status: 启用状态,默认为'active'
  289. 返回:
  290. - JSON: 包含创建结果和标签信息
  291. """
  292. try:
  293. # 获取请求数据
  294. data = request.get_json()
  295. if not data:
  296. return jsonify({
  297. 'success': False,
  298. 'message': '请求数据为空',
  299. 'data': None
  300. }), 400
  301. # 验证必要字段
  302. if 'name' not in data or not data['name']:
  303. return jsonify({
  304. 'success': False,
  305. 'message': '标签名称不能为空',
  306. 'data': None
  307. }), 400
  308. # 处理分类字段,如果未提供则设置默认值
  309. if 'category' not in data or not data['category']:
  310. data['category'] = '未分类'
  311. # 调用业务逻辑函数处理创建
  312. result = create_talent_tag(data)
  313. # 根据处理结果设置HTTP状态码
  314. status_code = 200 if result['success'] else 500
  315. return jsonify(result), status_code
  316. except Exception as e:
  317. logger.error(f"创建人才标签失败: {str(e)}")
  318. return jsonify({
  319. 'success': False,
  320. 'message': f'创建人才标签失败: {str(e)}',
  321. 'data': None
  322. }), 500
  323. # 获取人才标签列表接口
  324. @bp.route('/get-talent-tag-list', methods=['GET'])
  325. def get_talent_tag_list_route():
  326. """
  327. 获取人才标签列表的API接口
  328. 返回:
  329. - JSON: 包含人才标签列表和处理状态
  330. """
  331. try:
  332. # 调用业务逻辑函数获取人才标签列表
  333. result = get_talent_tag_list()
  334. # 根据处理结果设置HTTP状态码
  335. status_code = 200 if result['success'] else 500
  336. return jsonify(result), status_code
  337. except Exception as e:
  338. logger.error(f"获取人才标签列表失败: {str(e)}")
  339. return jsonify({
  340. 'success': False,
  341. 'message': f'获取人才标签列表失败: {str(e)}',
  342. 'data': []
  343. }), 500
  344. # 更新人才标签接口
  345. @bp.route('/update-talent-tag/<int:tag_id>', methods=['PUT'])
  346. def update_talent_tag_route(tag_id):
  347. """
  348. 更新人才标签的API接口
  349. 路径参数:
  350. - tag_id: 标签节点ID
  351. 请求参数:
  352. - JSON格式,可能包含以下字段:
  353. - name: 标签名称
  354. - category: 标签分类
  355. - description: 标签描述
  356. - status: 启用状态
  357. 返回:
  358. - JSON: 包含更新结果和标签信息
  359. """
  360. try:
  361. # 获取请求数据
  362. data = request.get_json()
  363. if not data:
  364. return jsonify({
  365. 'success': False,
  366. 'message': '请求数据为空',
  367. 'data': None
  368. }), 400
  369. # 调用业务逻辑函数处理更新
  370. result = update_talent_tag(tag_id, data)
  371. # 根据处理结果设置HTTP状态码
  372. if not result['success']:
  373. if result['code'] == 404:
  374. status_code = 404
  375. elif result['code'] == 400:
  376. status_code = 400
  377. else:
  378. status_code = 500
  379. else:
  380. status_code = 200
  381. return jsonify(result), status_code
  382. except Exception as e:
  383. logger.error(f"更新人才标签失败: {str(e)}")
  384. return jsonify({
  385. 'success': False,
  386. 'message': f'更新人才标签失败: {str(e)}',
  387. 'data': None
  388. }), 500
  389. # 删除人才标签接口
  390. @bp.route('/delete-talent-tag/<int:tag_id>', methods=['DELETE'])
  391. def delete_talent_tag_route(tag_id):
  392. """
  393. 删除人才标签的API接口
  394. 路径参数:
  395. - tag_id: 标签节点ID
  396. 返回:
  397. - JSON: 包含删除结果和被删除的标签信息
  398. """
  399. try:
  400. # 调用业务逻辑函数执行删除
  401. result = delete_talent_tag(tag_id)
  402. # 根据处理结果设置HTTP状态码
  403. if not result['success']:
  404. if result['code'] == 404:
  405. status_code = 404
  406. else:
  407. status_code = 500
  408. else:
  409. status_code = 200
  410. return jsonify(result), status_code
  411. except Exception as e:
  412. logger.error(f"删除人才标签失败: {str(e)}")
  413. return jsonify({
  414. 'success': False,
  415. 'message': f'删除人才标签失败: {str(e)}',
  416. 'data': None
  417. }), 500
  418. @bp.route('/query-kg', methods=['POST'])
  419. def query_kg():
  420. """
  421. 查询知识图谱API接口
  422. 请求参数:
  423. - query_requirement: 查询需求描述(JSON格式)
  424. 返回:
  425. - JSON: 包含查询结果和处理状态
  426. """
  427. try:
  428. # 获取请求数据
  429. data = request.json
  430. if not data or 'query_requirement' not in data:
  431. return jsonify({
  432. 'code': 400,
  433. 'success': False,
  434. 'message': '请求数据为空或缺少query_requirement字段',
  435. 'data': []
  436. }), 400
  437. query_requirement = data['query_requirement']
  438. # 调用业务逻辑函数执行查询
  439. result = query_neo4j_graph(query_requirement)
  440. # 根据处理结果设置HTTP状态码
  441. status_code = 200 if result['success'] else 500
  442. return jsonify(result), status_code
  443. except Exception as e:
  444. logger.error(f"查询知识图谱失败: {str(e)}")
  445. return jsonify({
  446. 'code': 500,
  447. 'success': False,
  448. 'message': f"查询知识图谱失败: {str(e)}",
  449. 'data': []
  450. }), 500
  451. @bp.route('/talent-get-tags/<int:talent_id>', methods=['GET'])
  452. def talent_get_tags_route(talent_id):
  453. """
  454. 获取人才标签的API接口
  455. 路径参数:
  456. - talent_id: 人才节点ID
  457. 返回:
  458. - JSON: 包含人才关联的标签列表和处理状态
  459. """
  460. try:
  461. # 调用业务逻辑函数获取人才标签
  462. result = talent_get_tags(talent_id)
  463. # 根据处理结果设置HTTP状态码
  464. status_code = 200 if result['success'] else 500
  465. return jsonify(result), status_code
  466. except Exception as e:
  467. logger.error(f"获取人才标签失败: {str(e)}")
  468. return jsonify({
  469. 'code': 500,
  470. 'success': False,
  471. 'message': f"获取人才标签失败: {str(e)}",
  472. 'data': []
  473. }), 500
  474. @bp.route('/talent-update-tags', methods=['POST'])
  475. def talent_update_tags_route():
  476. """
  477. 更新人才标签关系的API接口
  478. 请求参数:
  479. - JSON数组,包含talent和tag字段的对象列表
  480. 例如: [
  481. {"talent": 12345, "tag": "市场营销"},
  482. {"talent": 12345, "tag": "酒店管理"}
  483. ]
  484. 返回:
  485. - JSON: 包含更新结果的状态信息
  486. """
  487. try:
  488. # 获取请求数据
  489. data = request.json
  490. if not data:
  491. return jsonify({
  492. 'code': 400,
  493. 'success': False,
  494. 'message': '请求数据为空',
  495. 'data': None
  496. }), 400
  497. # 调用业务逻辑函数处理标签关系更新
  498. result = talent_update_tags(data)
  499. # 根据处理结果设置HTTP状态码
  500. if result['code'] == 200:
  501. status_code = 200
  502. elif result['code'] == 206:
  503. status_code = 206 # Partial Content
  504. elif result['code'] == 400:
  505. status_code = 400 # Bad Request
  506. elif result['code'] == 404:
  507. status_code = 404 # Not Found
  508. else:
  509. status_code = 500 # Internal Server Error
  510. return jsonify(result), status_code
  511. except Exception as e:
  512. logger.error(f"更新人才标签关系失败: {str(e)}")
  513. return jsonify({
  514. 'code': 500,
  515. 'success': False,
  516. 'message': f"更新人才标签关系失败: {str(e)}",
  517. 'data': None
  518. }), 500
  519. # 测试MinIO连接
  520. def test_minio_connection():
  521. """测试MinIO连接是否正常"""
  522. try:
  523. client = get_minio_client()
  524. if client.bucket_exists(minio_bucket):
  525. return {
  526. 'success': True,
  527. 'message': f'连接MinIO服务器成功,存储桶 {minio_bucket} 存在',
  528. 'config': {
  529. 'host': config.MINIO_HOST,
  530. 'bucket': minio_bucket,
  531. 'secure': use_ssl
  532. }
  533. }
  534. else:
  535. return {
  536. 'success': False,
  537. 'message': f'连接MinIO服务器成功,但存储桶 {minio_bucket} 不存在',
  538. 'config': {
  539. 'host': config.MINIO_HOST,
  540. 'bucket': minio_bucket,
  541. 'secure': use_ssl
  542. }
  543. }
  544. except Exception as e:
  545. return {
  546. 'success': False,
  547. 'message': f'连接MinIO服务器失败: {str(e)}',
  548. 'config': {
  549. 'host': config.MINIO_HOST,
  550. 'bucket': minio_bucket,
  551. 'secure': use_ssl
  552. }
  553. }
  554. # MinIO测试接口
  555. @bp.route('/test-minio-connection', methods=['GET'])
  556. def test_minio_connection_route():
  557. """
  558. 测试MinIO连接的API接口
  559. 返回:
  560. - JSON: 包含连接测试结果
  561. """
  562. try:
  563. result = test_minio_connection()
  564. status_code = 200 if result['success'] else 500
  565. return jsonify(result), status_code
  566. except Exception as e:
  567. logger.error(f"测试MinIO连接失败: {str(e)}")
  568. return jsonify({
  569. 'success': False,
  570. 'message': f'测试MinIO连接失败: {str(e)}',
  571. 'config': {
  572. 'host': config.MINIO_HOST,
  573. 'bucket': minio_bucket,
  574. 'secure': use_ssl
  575. }
  576. }), 500
  577. # 获取单个名片记录的API接口
  578. @bp.route('/get-business-card/<int:card_id>', methods=['GET'])
  579. def get_business_card_route(card_id):
  580. """
  581. 获取单个名片记录的API接口
  582. 路径参数:
  583. - card_id: 名片记录ID
  584. 返回:
  585. - JSON: 包含名片记录信息和处理状态
  586. """
  587. # 调用业务逻辑函数获取名片记录
  588. result = get_business_card(card_id)
  589. # 根据处理结果设置HTTP状态码
  590. if not result['success']:
  591. if result['code'] == 404:
  592. status_code = 404
  593. else:
  594. status_code = 500
  595. else:
  596. status_code = 200
  597. return jsonify(result), status_code
  598. @bp.route('/get-hotel-positions-list', methods=['GET'])
  599. def get_hotel_positions_list_route():
  600. """
  601. 获取酒店职位数据表全部记录的API接口
  602. 返回:
  603. - JSON: 包含酒店职位记录列表和处理状态
  604. """
  605. try:
  606. # 调用业务逻辑函数获取酒店职位列表
  607. result = get_hotel_positions_list()
  608. # 根据处理结果设置HTTP状态码
  609. status_code = 200 if result['success'] else 500
  610. return jsonify(result), status_code
  611. except Exception as e:
  612. # 处理未预期的异常
  613. error_msg = f"获取酒店职位列表时发生错误: {str(e)}"
  614. logger.error(error_msg, exc_info=True)
  615. return jsonify({
  616. 'success': False,
  617. 'message': error_msg,
  618. 'data': [],
  619. 'count': 0
  620. }), 500
  621. @bp.route('/add-hotel-positions', methods=['POST'])
  622. def add_hotel_positions_route():
  623. """
  624. 新增酒店职位数据表记录的API接口
  625. 请求参数:
  626. - JSON格式,包含以下字段:
  627. - department_zh: 部门中文名称 (必填)
  628. - department_en: 部门英文名称 (必填)
  629. - position_zh: 职位中文名称 (必填)
  630. - position_en: 职位英文名称 (必填)
  631. - position_abbr: 职位英文缩写 (可选)
  632. - level_zh: 职级中文名称 (必填)
  633. - level_en: 职级英文名称 (必填)
  634. - created_by: 创建者 (可选)
  635. - updated_by: 更新者 (可选)
  636. - status: 状态 (可选)
  637. 返回:
  638. - JSON: 包含创建结果和职位信息
  639. """
  640. try:
  641. # 获取请求数据
  642. data = request.get_json()
  643. if not data:
  644. return jsonify({
  645. 'success': False,
  646. 'message': '请求数据为空',
  647. 'data': None
  648. }), 400
  649. # 调用业务逻辑函数处理创建
  650. result = add_hotel_positions(data)
  651. # 根据处理结果设置HTTP状态码
  652. if result['code'] == 200:
  653. status_code = 201 # Created
  654. elif result['code'] == 400:
  655. status_code = 400 # Bad Request
  656. elif result['code'] == 409:
  657. status_code = 409 # Conflict
  658. else:
  659. status_code = 500 # Internal Server Error
  660. return jsonify(result), status_code
  661. except Exception as e:
  662. # 处理未预期的异常
  663. error_msg = f"创建酒店职位记录时发生错误: {str(e)}"
  664. logger.error(error_msg, exc_info=True)
  665. return jsonify({
  666. 'success': False,
  667. 'message': error_msg,
  668. 'data': None
  669. }), 500
  670. @bp.route('/update-hotel-positions/<int:position_id>', methods=['PUT'])
  671. def update_hotel_positions_route(position_id):
  672. """
  673. 修改酒店职位数据表记录的API接口
  674. 路径参数:
  675. - position_id: 职位记录ID
  676. 请求参数:
  677. - JSON格式,可能包含以下字段:
  678. - department_zh: 部门中文名称
  679. - department_en: 部门英文名称
  680. - position_zh: 职位中文名称
  681. - position_en: 职位英文名称
  682. - position_abbr: 职位英文缩写
  683. - level_zh: 职级中文名称
  684. - level_en: 职级英文名称
  685. - updated_by: 更新者
  686. - status: 状态
  687. 返回:
  688. - JSON: 包含更新结果和职位信息
  689. """
  690. try:
  691. # 获取请求数据
  692. data = request.get_json()
  693. if not data:
  694. return jsonify({
  695. 'success': False,
  696. 'message': '请求数据为空',
  697. 'data': None
  698. }), 400
  699. # 调用业务逻辑函数处理更新
  700. result = update_hotel_positions(position_id, data)
  701. # 根据处理结果设置HTTP状态码
  702. if result['code'] == 200:
  703. status_code = 200 # OK
  704. elif result['code'] == 400:
  705. status_code = 400 # Bad Request
  706. elif result['code'] == 404:
  707. status_code = 404 # Not Found
  708. elif result['code'] == 409:
  709. status_code = 409 # Conflict
  710. else:
  711. status_code = 500 # Internal Server Error
  712. return jsonify(result), status_code
  713. except Exception as e:
  714. # 处理未预期的异常
  715. error_msg = f"更新酒店职位记录时发生错误: {str(e)}"
  716. logger.error(error_msg, exc_info=True)
  717. return jsonify({
  718. 'success': False,
  719. 'message': error_msg,
  720. 'data': None
  721. }), 500
  722. @bp.route('/query-hotel-positions/<int:position_id>', methods=['GET'])
  723. def query_hotel_positions_route(position_id):
  724. """
  725. 查找指定ID的酒店职位数据表记录的API接口
  726. 路径参数:
  727. - position_id: 职位记录ID
  728. 返回:
  729. - JSON: 包含查找结果和职位信息
  730. """
  731. try:
  732. # 调用业务逻辑函数查找职位记录
  733. result = query_hotel_positions(position_id)
  734. # 根据处理结果设置HTTP状态码
  735. if result['code'] == 200:
  736. status_code = 200 # OK
  737. elif result['code'] == 404:
  738. status_code = 404 # Not Found
  739. else:
  740. status_code = 500 # Internal Server Error
  741. return jsonify(result), status_code
  742. except Exception as e:
  743. # 处理未预期的异常
  744. error_msg = f"查找酒店职位记录时发生错误: {str(e)}"
  745. logger.error(error_msg, exc_info=True)
  746. return jsonify({
  747. 'success': False,
  748. 'message': error_msg,
  749. 'data': None
  750. }), 500
  751. @bp.route('/delete-hotel-positions/<int:position_id>', methods=['DELETE'])
  752. def delete_hotel_positions_route(position_id):
  753. """
  754. 删除指定ID的酒店职位数据表记录的API接口
  755. 路径参数:
  756. - position_id: 职位记录ID
  757. 返回:
  758. - JSON: 包含删除结果和被删除的职位信息
  759. """
  760. try:
  761. # 调用业务逻辑函数删除职位记录
  762. result = delete_hotel_positions(position_id)
  763. # 根据处理结果设置HTTP状态码
  764. if result['code'] == 200:
  765. status_code = 200 # OK
  766. elif result['code'] == 404:
  767. status_code = 404 # Not Found
  768. else:
  769. status_code = 500 # Internal Server Error
  770. return jsonify(result), status_code
  771. except Exception as e:
  772. # 处理未预期的异常
  773. error_msg = f"删除酒店职位记录时发生错误: {str(e)}"
  774. logger.error(error_msg, exc_info=True)
  775. return jsonify({
  776. 'success': False,
  777. 'message': error_msg,
  778. 'data': None
  779. }), 500
  780. @bp.route('/get-hotel-group-brands-list', methods=['GET'])
  781. def get_hotel_group_brands_list_route():
  782. """
  783. 获取酒店集团子品牌数据表全部记录的API接口
  784. 返回:
  785. - JSON: 包含酒店集团品牌记录列表和处理状态
  786. """
  787. try:
  788. # 调用业务逻辑函数获取酒店集团品牌列表
  789. result = get_hotel_group_brands_list()
  790. # 根据处理结果设置HTTP状态码
  791. status_code = 200 if result['success'] else 500
  792. return jsonify(result), status_code
  793. except Exception as e:
  794. # 处理未预期的异常
  795. error_msg = f"获取酒店集团品牌列表时发生错误: {str(e)}"
  796. logger.error(error_msg, exc_info=True)
  797. return jsonify({
  798. 'success': False,
  799. 'message': error_msg,
  800. 'data': [],
  801. 'count': 0
  802. }), 500
  803. @bp.route('/add-hotel-group-brands', methods=['POST'])
  804. def add_hotel_group_brands_route():
  805. """
  806. 新增酒店集团子品牌数据表记录的API接口
  807. 请求参数:
  808. - JSON格式,包含以下字段:
  809. - group_name_en: 集团英文名称 (必填)
  810. - group_name_zh: 集团中文名称 (必填)
  811. - brand_name_en: 品牌英文名称 (必填)
  812. - brand_name_zh: 品牌中文名称 (必填)
  813. - positioning_level_en: 定位级别英文名称 (必填)
  814. - positioning_level_zh: 定位级别中文名称 (必填)
  815. - created_by: 创建者 (可选)
  816. - updated_by: 更新者 (可选)
  817. - status: 状态 (可选)
  818. 返回:
  819. - JSON: 包含创建结果和品牌信息
  820. """
  821. try:
  822. # 获取请求数据
  823. data = request.get_json()
  824. if not data:
  825. return jsonify({
  826. 'success': False,
  827. 'message': '请求数据为空',
  828. 'data': None
  829. }), 400
  830. # 调用业务逻辑函数处理创建
  831. result = add_hotel_group_brands(data)
  832. # 根据处理结果设置HTTP状态码
  833. if result['code'] == 200:
  834. status_code = 201 # Created
  835. elif result['code'] == 400:
  836. status_code = 400 # Bad Request
  837. elif result['code'] == 409:
  838. status_code = 409 # Conflict
  839. else:
  840. status_code = 500 # Internal Server Error
  841. return jsonify(result), status_code
  842. except Exception as e:
  843. # 处理未预期的异常
  844. error_msg = f"创建酒店集团品牌记录时发生错误: {str(e)}"
  845. logger.error(error_msg, exc_info=True)
  846. return jsonify({
  847. 'success': False,
  848. 'message': error_msg,
  849. 'data': None
  850. }), 500
  851. @bp.route('/update-hotel-group-brands/<int:brand_id>', methods=['PUT'])
  852. def update_hotel_group_brands_route(brand_id):
  853. """
  854. 修改酒店集团子品牌数据表记录的API接口
  855. 路径参数:
  856. - brand_id: 品牌记录ID
  857. 请求参数:
  858. - JSON格式,可能包含以下字段:
  859. - group_name_en: 集团英文名称
  860. - group_name_zh: 集团中文名称
  861. - brand_name_en: 品牌英文名称
  862. - brand_name_zh: 品牌中文名称
  863. - positioning_level_en: 定位级别英文名称
  864. - positioning_level_zh: 定位级别中文名称
  865. - updated_by: 更新者
  866. - status: 状态
  867. 返回:
  868. - JSON: 包含更新结果和品牌信息
  869. """
  870. try:
  871. # 获取请求数据
  872. data = request.get_json()
  873. if not data:
  874. return jsonify({
  875. 'success': False,
  876. 'message': '请求数据为空',
  877. 'data': None
  878. }), 400
  879. # 调用业务逻辑函数处理更新
  880. result = update_hotel_group_brands(brand_id, data)
  881. # 根据处理结果设置HTTP状态码
  882. if result['code'] == 200:
  883. status_code = 200 # OK
  884. elif result['code'] == 400:
  885. status_code = 400 # Bad Request
  886. elif result['code'] == 404:
  887. status_code = 404 # Not Found
  888. elif result['code'] == 409:
  889. status_code = 409 # Conflict
  890. else:
  891. status_code = 500 # Internal Server Error
  892. return jsonify(result), status_code
  893. except Exception as e:
  894. # 处理未预期的异常
  895. error_msg = f"更新酒店集团品牌记录时发生错误: {str(e)}"
  896. logger.error(error_msg, exc_info=True)
  897. return jsonify({
  898. 'success': False,
  899. 'message': error_msg,
  900. 'data': None
  901. }), 500
  902. @bp.route('/query-hotel-group-brands/<int:brand_id>', methods=['GET'])
  903. def query_hotel_group_brands_route(brand_id):
  904. """
  905. 查找指定ID的酒店集团子品牌数据表记录的API接口
  906. 路径参数:
  907. - brand_id: 品牌记录ID
  908. 返回:
  909. - JSON: 包含查找结果和品牌信息
  910. """
  911. try:
  912. # 调用业务逻辑函数查找品牌记录
  913. result = query_hotel_group_brands(brand_id)
  914. # 根据处理结果设置HTTP状态码
  915. if result['code'] == 200:
  916. status_code = 200 # OK
  917. elif result['code'] == 404:
  918. status_code = 404 # Not Found
  919. else:
  920. status_code = 500 # Internal Server Error
  921. return jsonify(result), status_code
  922. except Exception as e:
  923. # 处理未预期的异常
  924. error_msg = f"查找酒店集团品牌记录时发生错误: {str(e)}"
  925. logger.error(error_msg, exc_info=True)
  926. return jsonify({
  927. 'success': False,
  928. 'message': error_msg,
  929. 'data': None
  930. }), 500
  931. @bp.route('/delete-hotel-group-brands/<int:brand_id>', methods=['DELETE'])
  932. def delete_hotel_group_brands_route(brand_id):
  933. """
  934. 删除指定ID的酒店集团子品牌数据表记录的API接口
  935. 路径参数:
  936. - brand_id: 品牌记录ID
  937. 返回:
  938. - JSON: 包含删除结果和被删除的品牌信息
  939. """
  940. try:
  941. # 调用业务逻辑函数删除品牌记录
  942. result = delete_hotel_group_brands(brand_id)
  943. # 根据处理结果设置HTTP状态码
  944. if result['code'] == 200:
  945. status_code = 200 # OK
  946. elif result['code'] == 404:
  947. status_code = 404 # Not Found
  948. else:
  949. status_code = 500 # Internal Server Error
  950. return jsonify(result), status_code
  951. except Exception as e:
  952. # 处理未预期的异常
  953. error_msg = f"删除酒店集团品牌记录时发生错误: {str(e)}"
  954. logger.error(error_msg, exc_info=True)
  955. return jsonify({
  956. 'success': False,
  957. 'message': error_msg,
  958. 'data': None
  959. }), 500
  960. # ==================================
  961. # 重复记录处理API接口
  962. # ==================================
  963. @bp.route('/get-duplicate-records', methods=['GET'])
  964. def get_duplicate_records_route():
  965. """
  966. 获取重复记录列表的API接口
  967. 查询参数:
  968. - status: 可选,筛选特定状态的记录 ('pending', 'processed', 'ignored')
  969. 返回:
  970. - JSON: 包含重复记录列表和处理状态
  971. """
  972. try:
  973. # 获取查询参数
  974. status = request.args.get('status', None)
  975. # 验证status参数的有效性
  976. if status and status not in ['pending', 'processed', 'ignored']:
  977. return jsonify({
  978. 'success': False,
  979. 'message': 'status参数无效,必须为 pending、processed 或 ignored',
  980. 'data': None
  981. }), 400
  982. # 调用业务逻辑函数获取重复记录列表
  983. result = get_duplicate_records(status)
  984. # 根据处理结果设置HTTP状态码
  985. status_code = 200 if result['success'] else 500
  986. return jsonify(result), status_code
  987. except Exception as e:
  988. # 处理未预期的异常
  989. error_msg = f"获取重复记录列表时发生错误: {str(e)}"
  990. logger.error(error_msg, exc_info=True)
  991. return jsonify({
  992. 'success': False,
  993. 'message': error_msg,
  994. 'data': [],
  995. 'count': 0
  996. }), 500
  997. @bp.route('/process-duplicate-record/<int:duplicate_id>', methods=['POST'])
  998. def process_duplicate_record_route(duplicate_id):
  999. """
  1000. 处理重复记录的API接口
  1001. 路径参数:
  1002. - duplicate_id: 重复记录ID
  1003. 请求参数:
  1004. - JSON格式,包含以下字段:
  1005. - action: 处理动作 (必填) ('merge_to_suspected', 'keep_main', 'ignore')
  1006. - selected_duplicate_id: 当action为'merge_to_suspected'时,选择的疑似重复记录ID (可选)
  1007. - processed_by: 处理人 (可选)
  1008. - notes: 处理备注 (可选)
  1009. 返回:
  1010. - JSON: 包含处理结果和状态信息
  1011. """
  1012. try:
  1013. # 获取请求数据
  1014. data = request.get_json()
  1015. if not data:
  1016. return jsonify({
  1017. 'success': False,
  1018. 'message': '请求数据为空',
  1019. 'data': None
  1020. }), 400
  1021. # 验证必填字段
  1022. action = data.get('action')
  1023. if not action:
  1024. return jsonify({
  1025. 'success': False,
  1026. 'message': '缺少必填字段: action',
  1027. 'data': None
  1028. }), 400
  1029. # 验证action参数的有效性
  1030. if action not in ['merge_to_suspected', 'keep_main', 'ignore']:
  1031. return jsonify({
  1032. 'success': False,
  1033. 'message': 'action参数无效,必须为 merge_to_suspected、keep_main 或 ignore',
  1034. 'data': None
  1035. }), 400
  1036. # 提取其他参数
  1037. selected_duplicate_id = data.get('selected_duplicate_id')
  1038. processed_by = data.get('processed_by')
  1039. notes = data.get('notes')
  1040. # 特殊验证:如果action为merge_to_suspected,必须提供selected_duplicate_id
  1041. if action == 'merge_to_suspected' and not selected_duplicate_id:
  1042. return jsonify({
  1043. 'success': False,
  1044. 'message': '执行merge_to_suspected操作时必须提供selected_duplicate_id',
  1045. 'data': None
  1046. }), 400
  1047. # 调用业务逻辑函数处理重复记录
  1048. result = process_duplicate_record(
  1049. duplicate_id=duplicate_id,
  1050. action=action,
  1051. selected_duplicate_id=selected_duplicate_id,
  1052. processed_by=processed_by,
  1053. notes=notes
  1054. )
  1055. # 根据处理结果设置HTTP状态码
  1056. if result['code'] == 200:
  1057. status_code = 200 # OK
  1058. elif result['code'] == 400:
  1059. status_code = 400 # Bad Request
  1060. elif result['code'] == 404:
  1061. status_code = 404 # Not Found
  1062. else:
  1063. status_code = 500 # Internal Server Error
  1064. return jsonify(result), status_code
  1065. except Exception as e:
  1066. # 处理未预期的异常
  1067. error_msg = f"处理重复记录时发生错误: {str(e)}"
  1068. logger.error(error_msg, exc_info=True)
  1069. return jsonify({
  1070. 'success': False,
  1071. 'message': error_msg,
  1072. 'data': None
  1073. }), 500
  1074. @bp.route('/get-duplicate-record-detail/<int:duplicate_id>', methods=['GET'])
  1075. def get_duplicate_record_detail_route(duplicate_id):
  1076. """
  1077. 获取指定重复记录详细信息的API接口
  1078. 路径参数:
  1079. - duplicate_id: 重复记录ID
  1080. 返回:
  1081. - JSON: 包含重复记录详细信息
  1082. """
  1083. try:
  1084. # 调用业务逻辑函数获取重复记录详情
  1085. result = get_duplicate_record_detail(duplicate_id)
  1086. # 根据处理结果设置HTTP状态码
  1087. if result['code'] == 200:
  1088. status_code = 200 # OK
  1089. elif result['code'] == 404:
  1090. status_code = 404 # Not Found
  1091. else:
  1092. status_code = 500 # Internal Server Error
  1093. return jsonify(result), status_code
  1094. except Exception as e:
  1095. # 处理未预期的异常
  1096. error_msg = f"获取重复记录详情时发生错误: {str(e)}"
  1097. logger.error(error_msg, exc_info=True)
  1098. return jsonify({
  1099. 'success': False,
  1100. 'message': error_msg,
  1101. 'data': None
  1102. }), 500