compliance/mock_dms_server.py
gongwenxin 1901cf611e 集成
2025-07-24 17:22:36 +08:00

208 lines
8.4 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import json
import logging
from flask import Flask, jsonify, request
from pathlib import Path
app = Flask(__name__)
logging.basicConfig(level=logging.INFO)
# --- 模拟数据和内存数据库 ---
DMS_ASSETS_DIR = Path(__file__).parent / 'assets' / 'doc' / 'dms'
API_LIST_DATA = {}
MODEL_DATA = {}
MOCK_SCHEMAS_CACHE = {}
IN_MEMORY_DB = {} # 新增: 作为内存数据库
try:
with open(DMS_ASSETS_DIR / '列表.json', 'r', encoding='utf-8') as f:
API_LIST_DATA = json.load(f)
except FileNotFoundError as e:
logging.error(f"错误: 模拟数据文件 '列表.json' 未找到。详情: {e}")
API_LIST_DATA = {"code": 500, "message": "模拟数据 '列表.json' 未找到."}
def generate_fake_schema(record: dict) -> dict:
"""根据列表中的单条记录生成一个假的、但结构合理的schema"""
name = record.get('name', 'unknown_name')
title = record.get('title', 'Unknown Title')
version = record.get('version', '0.0.0')
# 假设第一个属性是主键
pk_name = f"{name}_id"
schema_properties = {
pk_name: {"type": "string", "title": "主键", "description": f"主键 for {name}"},
"description": {"type": "string", "title": "描述", "description": "一个简单的描述字段"},
"update_date": {"type": "string", "format": "date-time", "title": "更新日期"},
"status": {
"type": "string", "title": "状态", "enum": ["active", "inactive", "archived"]
},
"record_count": {
"type": "integer", "title": "记录数", "minimum": 0, "maximum": 10000
}
}
model = {
"type": "object",
"title": title,
"properties": schema_properties,
"required": [pk_name, "status"],
"identityId": [pk_name]
}
return {
"code": 0, "message": "操作处理成功",
"data":model,
}
def preload_schemas():
"""在服务器启动时预先生成并缓存所有schema"""
if API_LIST_DATA.get("code") == 0:
records = API_LIST_DATA.get("data", {}).get("records", [])
logging.info(f"预加载 {len(records)} 个API的模拟schema...")
for record in records:
record_id = record.get("id")
if record_id:
MOCK_SCHEMAS_CACHE[record_id] = generate_fake_schema(record)
logging.info("Schema预加载完成。")
# --- 元数据模拟端点 ---
@app.route('/api/schema/manage/schema', methods=['GET'])
def get_api_list():
"""模拟获取DMS中所有API列表的接口。"""
logging.info("Mock服务器: 收到API列表请求。")
print(f"API_LIST_DATA: {API_LIST_DATA}")
return jsonify(API_LIST_DATA)
@app.route('/api/schema/manage/schema/<string:model_id>', methods=['GET'])
def get_schema_details(model_id):
"""模拟根据ID获取单个API模型(schema)的接口。"""
logging.info(f"Mock服务器: 收到ID为 '{model_id}' 的schema详情请求。")
schema_data = MOCK_SCHEMAS_CACHE.get(model_id)
if schema_data:
return jsonify(schema_data)
else:
return jsonify({"code": 404, "message": f"未找到ID为 '{model_id}' 的schema。"}), 404
# --- CRUD 模拟端点 ---
def get_pk_name_from_model(name: str) -> str:
"""辅助函数:根据资源名找到其主键字段名"""
# 这是一个简化逻辑。真实场景可能更复杂。
# 我们根据generate_fake_schema的逻辑假设主键是 '资源名_id'
return f"{name}_id"
@app.route('/api/dms/<string:dms_instance_code>/v1/<string:name>', methods=['POST'])
def create_resource(dms_instance_code, name):
"""Create (POST): 创建资源"""
logging.info(f"Mock服务器: 收到对 '{name}' 的CREATE请求")
request_data = request.get_json(silent=True)
if not request_data or 'data' not in request_data or not isinstance(request_data['data'], list):
print(f"Mock服务器: 收到对 '{name}' 的CREATE请求, 请求体格式错误: {request_data}")
return jsonify({"code": 400, "message": "请求体格式错误,应为 {'data': [...]}"}), 400
if name not in IN_MEMORY_DB:
IN_MEMORY_DB[name] = {}
pk_name = get_pk_name_from_model(name)
for item in request_data['data']:
if pk_name not in item:
return jsonify({"code": 400, "message": f"创建失败,数据项缺少主键 '{pk_name}'"}), 400
pk_value = item[pk_name]
IN_MEMORY_DB[name][pk_value] = item
logging.info(f" > 在 '{name}' 中创建/更新了资源 ID: {pk_value}")
return jsonify({"code": 0, "message": "创建成功", "data": True})
@app.route('/api/dms/<string:dms_instance_code>/v1/<string:name>', methods=['PUT'])
def update_resource(dms_instance_code, name):
"""Update (PUT): 更新资源"""
logging.info(f"Mock服务器: 收到对 '{name}' 的UPDATE请求")
# 实现逻辑与Create完全相同
return create_resource(dms_instance_code, name)
@app.route('/api/dms/<string:dms_instance_code>/v1/<string:name>/<string:version>/<string:id>', methods=['GET'])
def read_resource(dms_instance_code, name, version, id):
"""Read (GET by ID): 读取单个资源"""
logging.info(f"Mock服务器: 收到对 '{name}' 的READ请求, ID: {id}")
resource = IN_MEMORY_DB.get(name, {}).get(id)
if resource:
return jsonify({"code": 0, "message": "读取成功", "data": resource})
else:
return jsonify({"code": 404, "message": f"资源 '{name}' with ID '{id}' 未找到."}), 404
@app.route('/api/dms/<string:dms_instance_code>/v1/<string:name>', methods=['DELETE'])
def delete_resource(dms_instance_code, name):
"""Delete (DELETE): 删除资源"""
logging.info(f"Mock服务器: 收到对 '{name}' 的DELETE请求")
data = request.json.get('data', [])
if not data or not isinstance(data, list):
return jsonify({"code": 400, "message": "请求体格式错误,需要'data'字段且为数组", "data": False}), 400
pk_name = get_pk_name_from_model(name)
ids_to_delete = set()
# Check if the items in data are dicts (old format) or strings (new format)
if data and isinstance(data[0], dict):
# Legacy format: [{"pk_name": "value"}]
ids_to_delete = {item.get(pk_name) for item in data if pk_name in item}
else:
# New format: ["value1", "value2"]
ids_to_delete = set(data)
if not ids_to_delete:
return jsonify({"code": 400, "message": "未在请求中提供有效的ID", "data": False}), 400
deleted_count = 0
if name in IN_MEMORY_DB:
# Correctly pop items from the dictionary
for id_val in ids_to_delete:
if IN_MEMORY_DB[name].pop(id_val, None):
logging.info(f" > 从 '{name}' 删除了资源 ID: {id_val}")
deleted_count += 1
if deleted_count > 0:
return jsonify({"code": 0, "message": f"成功删除 {deleted_count} 条记录", "data": True})
else:
return jsonify({"code": 404, "message": "未找到要删除的资源", "data": False})
@app.route('/api/dms/<string:dms_instance_code>/v1/<string:name>/<string:version>', methods=['POST'])
def list_resources(dms_instance_code, name, version):
"""List (POST): 获取资源列表"""
logging.info(f"Mock服务器: 收到对 '{name}' 的LIST请求")
all_resources = list(IN_MEMORY_DB.get(name, {}).values())
return jsonify({
"code": 0,
"message": f"获取 '{name}' 列表成功",
"data": all_resources
})
# --- Schema Endpoints ---
@app.route('/api/dms/wb_ml/v1/schemas/<name>', methods=['GET'])
def get_schema_by_name(name):
"""模拟根据名称获取单个API模型(schema)的接口。"""
logging.info(f"Mock服务器: 收到名称为 '{name}' 的schema详情请求。")
schema_data = MOCK_SCHEMAS_CACHE.get(name)
if schema_data:
return jsonify(schema_data)
else:
return jsonify({"code": 404, "message": f"未找到名称为 '{name}' 的schema。"}), 404
def print_routes(app):
"""打印应用中所有已注册的路由。"""
logging.info("\n--- 已注册的API路由 ---")
for rule in sorted(app.url_map.iter_rules(), key=lambda r: str(r)):
methods = ','.join(sorted(list(rule.methods)))
logging.info(f"URL: {rule!s:55s} 方法: {methods:25s} 端点: {rule.endpoint}")
logging.info("-------------------------\n")
if __name__ == '__main__':
preload_schemas()
print_routes(app)
port = 5001
logging.info(f"正在启动用于DMS API的Flask模拟服务器地址为 http://127.0.0.1:{port}")
app.run(host='0.0.0.0', port=port, debug=False)