208 lines
8.4 KiB
Python
208 lines
8.4 KiB
Python
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) |