routes.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  1. from flask import request, jsonify
  2. from app.api.data_model import bp
  3. from app.models.result import success, failed
  4. from app.api.graph.routes import MyEncoder
  5. from app.core.data_model import model as model_functions
  6. import json
  7. import logging
  8. # Configure logger
  9. logger = logging.getLogger(__name__)
  10. # 2024.09.11 数据模型血缘关系(传入数据资源id)
  11. @bp.route('/model/data/relation', methods=['POST'])
  12. def data_model_relation():
  13. try:
  14. # 传入请求参数
  15. receiver = request.get_json()
  16. resource_ids = receiver['id'] # 给定一个数据资源的id
  17. response_data = model_functions.handle_model_relation(resource_ids)
  18. res = success(response_data, "success")
  19. return json.dumps(res, ensure_ascii=False, cls=MyEncoder)
  20. except Exception as e:
  21. res = failed({}, {"error": f"{e}"})
  22. return json.dumps(res, ensure_ascii=False, cls=MyEncoder)
  23. # 传入一个数据资源的id,返回多个有血缘关系的数据资源
  24. @bp.route('/model/relatives/relation', methods=['POST'])
  25. def data_relatives_relation():
  26. try:
  27. # 传入请求参数
  28. receiver = request.get_json()
  29. page = int(receiver.get('current', 1))
  30. page_size = int(receiver.get('size', 10))
  31. id = receiver['id']
  32. name_filter = receiver.get('name', None)
  33. category = receiver.get('category', None)
  34. time = receiver.get('time', None)
  35. # 计算跳过的记录的数量
  36. skip_count = (page - 1) * page_size
  37. data, total = model_functions.model_resource_list(skip_count, page_size, name_filter, id, category, time)
  38. response_data = {'records': data, 'total': total, 'size': page_size, 'current': page}
  39. res = success(response_data, "success")
  40. return json.dumps(res, ensure_ascii=False, cls=MyEncoder)
  41. except Exception as e:
  42. res = failed({}, {"error": f"{e}"})
  43. return json.dumps(res, ensure_ascii=False, cls=MyEncoder)
  44. # DDL数据模型保存
  45. @bp.route('/data/model/save', methods=['POST'])
  46. def data_model_save():
  47. # 传入请求参数
  48. receiver = request.get_json()
  49. data_model = receiver['name']
  50. id_list = receiver['id_list']
  51. # resource_id和meta_id构成json格式
  52. result = json.dumps(id_list, ensure_ascii=False)
  53. try:
  54. # 从DDL中选取保存数据模型
  55. result_list = [receiver['en_name']]
  56. id, data_model_node = model_functions.handle_data_model(data_model, result_list, result, receiver)
  57. model_functions.handle_no_meta_data_model(id_list, receiver, data_model_node)
  58. model_functions.calculate_model_level(id)
  59. res = success({}, "success")
  60. return json.dumps(res, ensure_ascii=False, cls=MyEncoder)
  61. except Exception as e:
  62. res = failed({}, {"error": f"{e}"})
  63. return json.dumps(res, ensure_ascii=False, cls=MyEncoder)
  64. # 新建数据模型请求接口(从数据资源中选取)
  65. # @bp.route('/model/data/search', methods=['POST'])
  66. @bp.route('/data/search', methods=['POST'])
  67. def data_model_search():
  68. # 传入请求参数
  69. receiver = request.get_json()
  70. data_model = receiver['name']
  71. id_list = receiver['id_list']
  72. # resource_id和meta_id构成json格式
  73. result = json.dumps(id_list, ensure_ascii=False)
  74. try:
  75. from app.core.meta_data import translate_and_parse
  76. result_list = translate_and_parse(data_model)
  77. id, data_model_node = model_functions.handle_data_model(data_model, result_list, result, receiver)
  78. if id_list:
  79. model_functions.resource_handle_meta_data_model(id_list, id)
  80. model_functions.calculate_model_level(id)
  81. res = success({}, "success")
  82. return json.dumps(res, ensure_ascii=False, cls=MyEncoder)
  83. except Exception as e:
  84. res = failed({}, {"error": f"{e}"})
  85. return json.dumps(res, ensure_ascii=False, cls=MyEncoder)
  86. # 新建数据模型请求接口(从数据模型中选取)
  87. @bp.route('/model/data/model/add', methods=['POST'])
  88. def data_model_model_add():
  89. # 传入请求参数
  90. receiver = request.get_json()
  91. data_model = receiver['name']
  92. id_list = receiver['id_list']
  93. # model_id和meta_id构成json格式
  94. result = json.dumps(id_list, ensure_ascii=False)
  95. try:
  96. from app.core.meta_data import translate_and_parse
  97. result_list = translate_and_parse(data_model)
  98. node_id, data_model_node = model_functions.handle_data_model(data_model, result_list, result, receiver)
  99. model_functions.model_handle_meta_data_model(id_list, node_id)
  100. model_functions.calculate_model_level(node_id)
  101. # 构建响应数据
  102. response_data = {
  103. "id": node_id,
  104. "name": data_model_node.get('name'),
  105. "en_name": data_model_node.get('en_name'),
  106. "description": data_model_node.get('description'),
  107. "category": data_model_node.get('category'),
  108. "time": data_model_node.get('time'),
  109. "level": data_model_node.get('level'),
  110. "tag": data_model_node.get('tag'),
  111. "childrenId": data_model_node.get('childrenId', []),
  112. "leader": data_model_node.get('leader'),
  113. "origin": data_model_node.get('origin'),
  114. "frequency": data_model_node.get('frequency'),
  115. "organization": data_model_node.get('organization'),
  116. "data_sensitivity": data_model_node.get('data_sensitivity'),
  117. "status": data_model_node.get('status')
  118. }
  119. res = success(response_data, "success")
  120. return json.dumps(res, ensure_ascii=False, cls=MyEncoder)
  121. except Exception as e:
  122. res = failed({}, {"error": f"{e}"})
  123. return json.dumps(res, ensure_ascii=False, cls=MyEncoder)
  124. # 数据模型-详情接口
  125. @bp.route('/data/model/detail', methods=['POST'])
  126. def data_model_detail():
  127. try:
  128. # 传入请求参数
  129. receiver = request.get_json()
  130. # 直接使用字符串ID,不做类型转换
  131. id = receiver.get('id')
  132. print(f"Received id from frontend: {id}")
  133. response_data = model_functions.handle_id_model(id)
  134. res = success(response_data, "success")
  135. return json.dumps(res, ensure_ascii=False, cls=MyEncoder)
  136. except Exception as e:
  137. import traceback
  138. traceback.print_exc()
  139. res = failed({}, {"error": f"{e}"})
  140. return json.dumps(res, ensure_ascii=False, cls=MyEncoder)
  141. # 删除数据模型
  142. @bp.route('/data/model/delete', methods=['POST'])
  143. def data_model_delete():
  144. try:
  145. # 传入请求参数
  146. receiver = request.get_json()
  147. id = receiver.get('id')
  148. print(f"Deleting data model with id: {id}")
  149. # 首先删除数据模型节点
  150. from app.services.neo4j_driver import neo4j_driver
  151. query = """
  152. MATCH (n:DataModel) where id(n) = $nodeId
  153. detach delete n
  154. """
  155. with neo4j_driver.get_session() as session:
  156. session.run(query, nodeId=id)
  157. res = success({}, "success")
  158. return json.dumps(res, ensure_ascii=False, cls=MyEncoder)
  159. except Exception as e:
  160. import traceback
  161. traceback.print_exc()
  162. res = failed({}, {"error": f"{e}"})
  163. return json.dumps(res, ensure_ascii=False, cls=MyEncoder)
  164. # 列表 数据模型查询
  165. @bp.route('/data/model/list', methods=['POST'])
  166. def data_model_list():
  167. try:
  168. # 传入请求参数
  169. receiver = request.get_json()
  170. page = int(receiver.get('current', 1))
  171. page_size = int(receiver.get('size', 10))
  172. name_filter = receiver.get('name', None)
  173. en_name_filter = receiver.get('en_name', None)
  174. category = receiver.get('category', None)
  175. tag = receiver.get('tag', None)
  176. level = receiver.get('level', None)
  177. # 计算跳过的记录的数量
  178. skip_count = (page - 1) * page_size
  179. data, total = model_functions.model_list(skip_count, page_size, en_name_filter,
  180. name_filter, category, tag, level)
  181. response_data = {'records': data, 'total': total, 'size': page_size, 'current': page}
  182. res = success(response_data, "success")
  183. return json.dumps(res, ensure_ascii=False, cls=MyEncoder)
  184. except Exception as e:
  185. res = failed({}, {"error": f"{e}"})
  186. return json.dumps(res, ensure_ascii=False, cls=MyEncoder)
  187. # 数据模型的图谱(血缘关系Kinship+影响关系Impact+所有关系all+社区关系community)
  188. @bp.route('/data/model/graph/all', methods=['POST'])
  189. def data_model_graph_all():
  190. try:
  191. # 传入请求参数
  192. receiver = request.get_json()
  193. type = receiver['type'] # kinship/impact/all/community
  194. if type == 'community':
  195. # 社区图谱查询,提取tag参数
  196. tag = receiver.get('tag', None)
  197. result = model_functions.model_community(tag)
  198. else:
  199. # 非社区查询时才提取nodeid和meta参数
  200. nodeid = receiver.get('id')
  201. meta = receiver.get('meta', False) # true/false 是否返回元数据
  202. if type == 'kinship':
  203. result = model_functions.model_kinship_graph(nodeid, meta)
  204. elif type == 'impact':
  205. result = model_functions.model_impact_graph(nodeid, meta)
  206. else:
  207. result = model_functions.model_all_graph(nodeid, meta)
  208. return json.dumps(success(result, "success"), ensure_ascii=False, cls=MyEncoder)
  209. except Exception as e:
  210. return json.dumps(failed({}, str(e)), ensure_ascii=False, cls=MyEncoder)
  211. # 数据模型的列表图谱
  212. @bp.route('/data/model/list/graph', methods=['POST'])
  213. def data_model_list_graph():
  214. try:
  215. # 传入请求参数
  216. receiver = request.get_json()
  217. if not receiver or 'tag' not in receiver:
  218. raise ValueError("Missing 'tag' parameter in request body")
  219. nodeid = receiver['tag']
  220. # 构建查询条件
  221. params = {}
  222. where_clause = ""
  223. if nodeid is not None:
  224. where_clause = "MATCH (n)-[:label]->(la) WHERE id(la) = $nodeId"
  225. params['nodeId'] = nodeid
  226. from app.services.neo4j_driver import neo4j_driver
  227. query = f"""
  228. MATCH (n:DataModel)
  229. OPTIONAL MATCH (n)-[:child]->(child)
  230. {where_clause}
  231. WITH
  232. collect(DISTINCT {{id: toString(id(n)), text: n.name, type: split(labels(n)[0], '_')[1]}}) AS nodes,
  233. collect(DISTINCT {{id: toString(id(child)), text: child.name, type: split(labels(child)[0], '_')[1]}}) AS nodes2,
  234. collect(DISTINCT {{from: toString(id(n)), to: toString(id(child)), text: '下级'}}) AS lines
  235. RETURN nodes + nodes2 AS nodes, lines AS lines
  236. """
  237. with neo4j_driver.get_session() as session:
  238. result = session.run(query, **params)
  239. data = result.data()
  240. if len(data) > 0:
  241. # 过滤掉空节点(即 id 为 null 的节点)
  242. nodes = []
  243. for node in data[0]['nodes']:
  244. if node['id'] != 'null':
  245. nodes.append(node)
  246. lines = data[0]['lines']
  247. response_data = {
  248. 'nodes': nodes,
  249. 'edges': lines
  250. }
  251. return json.dumps(success(response_data, "success"), ensure_ascii=False, cls=MyEncoder)
  252. else:
  253. return json.dumps(success({'nodes': [], 'edges': []}, "No data found"), ensure_ascii=False, cls=MyEncoder)
  254. except Exception as e:
  255. return json.dumps(failed({}, str(e)), ensure_ascii=False, cls=MyEncoder)
  256. # 更新数据模型
  257. @bp.route('/data/model/update', methods=['POST'])
  258. def data_model_update():
  259. try:
  260. # 传入请求参数
  261. receiver = request.get_json()
  262. result = model_functions.data_model_edit(receiver)
  263. return json.dumps(success(result, "success"), ensure_ascii=False, cls=MyEncoder)
  264. except Exception as e:
  265. return json.dumps(failed({}, str(e)), ensure_ascii=False, cls=MyEncoder)
  266. # 数据模型关联元数据搜索
  267. @bp.route('/search', methods=['POST'])
  268. def data_model_metadata_search():
  269. """数据模型关联元数据搜索"""
  270. try:
  271. # 获取分页和筛选参数
  272. page = int(request.json.get('current', 1))
  273. page_size = int(request.json.get('size', 10))
  274. model_id = request.json.get('id')
  275. en_name_filter = request.json.get('en_name')
  276. name_filter = request.json.get('name')
  277. category_filter = request.json.get('category')
  278. tag_filter = request.json.get('tag')
  279. if model_id is None:
  280. return json.dumps(failed({}, "模型ID不能为空"), ensure_ascii=False, cls=MyEncoder)
  281. # 确保传入的ID为整数
  282. try:
  283. model_id = int(model_id)
  284. except (ValueError, TypeError):
  285. return json.dumps(failed({}, f"模型ID必须为整数, 收到的是: {model_id}"), ensure_ascii=False, cls=MyEncoder)
  286. # 记录请求信息
  287. logger.info(f"获取数据模型关联元数据请求,ID: {model_id}")
  288. # 调用业务逻辑查询关联元数据
  289. metadata_list, total_count = model_functions.model_search_list(
  290. model_id,
  291. page,
  292. page_size,
  293. en_name_filter,
  294. name_filter,
  295. category_filter,
  296. tag_filter
  297. )
  298. # 返回结果
  299. response_data = {
  300. "records": metadata_list,
  301. "total": total_count,
  302. "size": page_size,
  303. "current": page
  304. }
  305. res = success(response_data, "success")
  306. return json.dumps(res, ensure_ascii=False, cls=MyEncoder)
  307. except Exception as e:
  308. logger.error(f"数据模型关联元数据搜索失败: {str(e)}")
  309. res = failed({}, str(e))
  310. return json.dumps(res, ensure_ascii=False, cls=MyEncoder)