636 lines
22 KiB
Python
636 lines
22 KiB
Python
import json
|
||
import os
|
||
import uuid
|
||
from datetime import datetime
|
||
from flask import Flask, jsonify, request, send_from_directory
|
||
from flask_cors import CORS
|
||
|
||
app = Flask(__name__, static_folder='../platform', static_url_path='')
|
||
CORS(app)
|
||
|
||
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
||
DB_DIR = os.path.join(BASE_DIR, 'db')
|
||
|
||
|
||
def load_json(filename):
|
||
path = os.path.join(DB_DIR, filename)
|
||
if not os.path.exists(path):
|
||
return []
|
||
with open(path, 'r', encoding='utf-8') as f:
|
||
return json.load(f)
|
||
|
||
|
||
def save_json(filename, data):
|
||
path = os.path.join(DB_DIR, filename)
|
||
with open(path, 'w', encoding='utf-8') as f:
|
||
json.dump(data, f, ensure_ascii=False, indent=2)
|
||
|
||
|
||
# ── helpers ──
|
||
|
||
def gen_id(prefix='ORD'):
|
||
return f"{prefix}-{uuid.uuid4().hex[:8].upper()}"
|
||
|
||
|
||
def calc_distance(lat1, lng1, lat2, lng2):
|
||
from math import radians, cos, sin, asin, sqrt
|
||
r = 6371
|
||
d_lat = radians(lat2 - lat1)
|
||
d_lng = radians(lng2 - lng1)
|
||
a = sin(d_lat/2)**2 + cos(radians(lat1)) * cos(radians(lat2)) * sin(d_lng/2)**2
|
||
return round(2 * r * asin(sqrt(a)), 1)
|
||
|
||
|
||
def calc_price(dist_km, weight_kg, urgency):
|
||
base = dist_km * 12 + weight_kg * 5
|
||
if urgency == 'urgent':
|
||
base *= 1.3
|
||
elif urgency == 'ASAP':
|
||
base *= 1.5
|
||
return max(int(base), 30)
|
||
|
||
|
||
# ── static files ──
|
||
|
||
@app.route('/')
|
||
def index():
|
||
return send_from_directory(app.static_folder, 'index.html')
|
||
|
||
|
||
# ── Auth ──
|
||
|
||
@app.route('/api/login', methods=['POST'])
|
||
def login():
|
||
data = request.get_json()
|
||
phone = data.get('phone', '')
|
||
role = data.get('role', 'demander')
|
||
users = load_json('users.json')
|
||
user = next((u for u in users if u['phone'] == phone), None)
|
||
if not user:
|
||
user = {
|
||
'id': gen_id('U'),
|
||
'name': phone[-4:],
|
||
'role': role,
|
||
'phone': phone,
|
||
'company': '',
|
||
'avatar': phone[-1],
|
||
'credit_score': 800,
|
||
'created_at': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||
}
|
||
users.append(user)
|
||
save_json('users.json', users)
|
||
return jsonify({'code': 0, 'data': user})
|
||
|
||
|
||
# ── Users ──
|
||
|
||
@app.route('/api/users/<user_id>')
|
||
def get_user(user_id):
|
||
users = load_json('users.json')
|
||
user = next((u for u in users if u['id'] == user_id), None)
|
||
if not user:
|
||
return jsonify({'code': 404, 'msg': '用户不存在'}), 404
|
||
return jsonify({'code': 0, 'data': user})
|
||
|
||
|
||
@app.route('/api/users/<user_id>', methods=['PUT'])
|
||
def update_user(user_id):
|
||
users = load_json('users.json')
|
||
idx = next((i for i, u in enumerate(users) if u['id'] == user_id), None)
|
||
if idx is None:
|
||
return jsonify({'code': 404, 'msg': '用户不存在'}), 404
|
||
data = request.get_json()
|
||
for k in ['name', 'company', 'phone']:
|
||
if k in data:
|
||
users[idx][k] = data[k]
|
||
save_json('users.json', users)
|
||
return jsonify({'code': 0, 'data': users[idx]})
|
||
|
||
|
||
# ── Drones ──
|
||
|
||
@app.route('/api/drones')
|
||
def list_drones():
|
||
drones = load_json('drones.json')
|
||
status = request.args.get('status')
|
||
user_id = request.args.get('user_id')
|
||
if status:
|
||
drones = [d for d in drones if d['status'] == status]
|
||
if user_id:
|
||
drones = [d for d in drones if d['user_id'] == user_id]
|
||
return jsonify({'code': 0, 'data': drones})
|
||
|
||
|
||
@app.route('/api/drones/available')
|
||
def available_drones():
|
||
drones = load_json('drones.json')
|
||
available = [d for d in drones if d['status'] == 'available']
|
||
return jsonify({'code': 0, 'data': available})
|
||
|
||
|
||
@app.route('/api/drones', methods=['POST'])
|
||
def add_drone():
|
||
data = request.get_json()
|
||
drones = load_json('drones.json')
|
||
drone = {
|
||
'id': gen_id('D'),
|
||
'name': data.get('name', '未知机型'),
|
||
'model': data.get('model', '通用'),
|
||
'max_weight': data.get('max_weight', 10),
|
||
'range_km': data.get('range_km', 10),
|
||
'status': 'available',
|
||
'location': data.get('location', '未设置'),
|
||
'total_flights': 0,
|
||
'user_id': data.get('user_id'),
|
||
'price_per_km': data.get('price_per_km', 15),
|
||
'insurance_expiry': data.get('insurance_expiry', '2026-12-31')
|
||
}
|
||
drones.append(drone)
|
||
save_json('drones.json', drones)
|
||
return jsonify({'code': 0, 'data': drone}), 201
|
||
|
||
|
||
@app.route('/api/drones/<drone_id>', methods=['PUT'])
|
||
def update_drone(drone_id):
|
||
drones = load_json('drones.json')
|
||
idx = next((i for i, d in enumerate(drones) if d['id'] == drone_id), None)
|
||
if idx is None:
|
||
return jsonify({'code': 404, 'msg': '无人机不存在'}), 404
|
||
data = request.get_json()
|
||
for k in ['name', 'status', 'location', 'price_per_km']:
|
||
if k in data:
|
||
drones[idx][k] = data[k]
|
||
save_json('drones.json', drones)
|
||
return jsonify({'code': 0, 'data': drones[idx]})
|
||
|
||
|
||
# ── Orders ──
|
||
|
||
@app.route('/api/orders')
|
||
def list_orders():
|
||
orders = load_json('orders.json')
|
||
role = request.args.get('role')
|
||
user_id = request.args.get('user_id')
|
||
status = request.args.get('status')
|
||
|
||
if role == 'demander' and user_id:
|
||
orders = [o for o in orders if o['demander_id'] == user_id]
|
||
elif role == 'provider' and user_id:
|
||
orders = [o for o in orders if o.get('provider_id') == user_id]
|
||
|
||
if status:
|
||
orders = [o for o in orders if o['status'] == status]
|
||
else:
|
||
ordering = {'pending': 0, 'matching': 1, 'accepted': 2,
|
||
'in_transit': 3, 'completed': 4, 'cancelled': 5}
|
||
orders.sort(key=lambda o: ordering.get(o['status'], 9))
|
||
|
||
return jsonify({'code': 0, 'data': orders})
|
||
|
||
|
||
@app.route('/api/orders/pending')
|
||
def pending_orders():
|
||
orders = load_json('orders.json')
|
||
pending = [o for o in orders if o['status'] == 'pending']
|
||
return jsonify({'code': 0, 'data': pending})
|
||
|
||
|
||
@app.route('/api/orders', methods=['POST'])
|
||
def create_order():
|
||
data = request.get_json()
|
||
orders = load_json('orders.json')
|
||
|
||
order = {
|
||
'id': gen_id('ORDER'),
|
||
'from_addr': data['from_addr'],
|
||
'to_addr': data['to_addr'],
|
||
'from_lat': data.get('from_lat', 39.904),
|
||
'from_lng': data.get('from_lng', 116.407),
|
||
'to_lat': data.get('to_lat', 39.904),
|
||
'to_lng': data.get('to_lng', 116.407),
|
||
'cargo': data.get('cargo', '普通包裹'),
|
||
'weight': float(data.get('weight', 1)),
|
||
'price': 0,
|
||
'distance_km': 0,
|
||
'status': 'pending',
|
||
'urgency': data.get('urgency', 'normal'),
|
||
'insurance': float(data.get('insurance', 0)),
|
||
'note': data.get('note', ''),
|
||
'demander_id': data.get('user_id', ''),
|
||
'provider_id': None,
|
||
'drone_id': None,
|
||
'created_at': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
|
||
'updated_at': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
|
||
'completed_at': None,
|
||
'rating': None,
|
||
'comment': None
|
||
}
|
||
|
||
dist = calc_distance(order['from_lat'], order['from_lng'],
|
||
order['to_lat'], order['to_lng'])
|
||
price = calc_price(dist, order['weight'], order['urgency'])
|
||
order['distance_km'] = dist
|
||
order['price'] = price
|
||
|
||
orders.insert(0, order)
|
||
save_json('orders.json', orders)
|
||
|
||
# auto generate quotes from available providers
|
||
auto_generate_quotes(order)
|
||
|
||
return jsonify({'code': 0, 'data': order}), 201
|
||
|
||
|
||
def auto_generate_quotes(order):
|
||
drones = load_json('drones.json')
|
||
users = load_json('users.json')
|
||
quotes = load_json('quotes.json')
|
||
available = [d for d in drones if d['status'] == 'available']
|
||
for drone in available[:3]:
|
||
provider = next((u for u in users if u['id'] == drone['user_id']), None)
|
||
if not provider:
|
||
continue
|
||
base_price = order['price']
|
||
variance = 0.8 + (hash(drone['id']) % 40) / 100
|
||
quote = {
|
||
'id': gen_id('Q'),
|
||
'order_id': order['id'],
|
||
'provider_id': drone['user_id'],
|
||
'drone_id': drone['id'],
|
||
'price': int(base_price * variance),
|
||
'eta_minutes': max(int(order['distance_km'] / 1.2 * (0.8 + (hash(drone['name']) % 40) / 100)), 5),
|
||
'distance_km': order['distance_km'],
|
||
'provider_name': provider['name'],
|
||
'drone_name': drone['name'],
|
||
'provider_rating': provider.get('credit_score', 800) / 100 * 0.5 + 2.5,
|
||
'provider_flights': drone['total_flights'],
|
||
'status': 'active',
|
||
'created_at': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||
}
|
||
quote['provider_rating'] = round(min(quote['provider_rating'], 5.0), 1)
|
||
quotes.append(quote)
|
||
save_json('quotes.json', quotes)
|
||
|
||
|
||
@app.route('/api/orders/<order_id>')
|
||
def get_order(order_id):
|
||
orders = load_json('orders.json')
|
||
order = next((o for o in orders if o['id'] == order_id), None)
|
||
if not order:
|
||
return jsonify({'code': 404, 'msg': '订单不存在'}), 404
|
||
return jsonify({'code': 0, 'data': order})
|
||
|
||
|
||
@app.route('/api/orders/<order_id>/accept', methods=['POST'])
|
||
def accept_order(order_id):
|
||
orders = load_json('orders.json')
|
||
idx = next((i for i, o in enumerate(orders) if o['id'] == order_id), None)
|
||
if idx is None:
|
||
return jsonify({'code': 404, 'msg': '订单不存在'}), 404
|
||
|
||
data = request.get_json()
|
||
orders[idx]['status'] = 'accepted'
|
||
orders[idx]['provider_id'] = data.get('provider_id')
|
||
orders[idx]['drone_id'] = data.get('drone_id')
|
||
orders[idx]['updated_at'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||
|
||
# update drone status
|
||
drone_id = data.get('drone_id')
|
||
if drone_id:
|
||
drones = load_json('drones.json')
|
||
for d in drones:
|
||
if d['id'] == drone_id:
|
||
d['status'] = 'busy'
|
||
break
|
||
save_json('drones.json', drones)
|
||
|
||
save_json('orders.json', orders)
|
||
return jsonify({'code': 0, 'data': orders[idx]})
|
||
|
||
|
||
@app.route('/api/orders/<order_id>/transit', methods=['POST'])
|
||
def start_transit(order_id):
|
||
orders = load_json('orders.json')
|
||
idx = next((i for i, o in enumerate(orders) if o['id'] == order_id), None)
|
||
if idx is None:
|
||
return jsonify({'code': 404, 'msg': '订单不存在'}), 404
|
||
orders[idx]['status'] = 'in_transit'
|
||
orders[idx]['updated_at'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||
save_json('orders.json', orders)
|
||
return jsonify({'code': 0, 'data': orders[idx]})
|
||
|
||
|
||
@app.route('/api/orders/<order_id>/complete', methods=['POST'])
|
||
def complete_order(order_id):
|
||
orders = load_json('orders.json')
|
||
idx = next((i for i, o in enumerate(orders) if o['id'] == order_id), None)
|
||
if idx is None:
|
||
return jsonify({'code': 404, 'msg': '订单不存在'}), 404
|
||
orders[idx]['status'] = 'completed'
|
||
orders[idx]['completed_at'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||
orders[idx]['updated_at'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||
# release drone
|
||
drone_id = orders[idx].get('drone_id')
|
||
if drone_id:
|
||
drones = load_json('drones.json')
|
||
for d in drones:
|
||
if d['id'] == drone_id:
|
||
d['status'] = 'available'
|
||
d['total_flights'] = d.get('total_flights', 0) + 1
|
||
break
|
||
save_json('drones.json', drones)
|
||
save_json('orders.json', orders)
|
||
return jsonify({'code': 0, 'data': orders[idx]})
|
||
|
||
|
||
@app.route('/api/orders/<order_id>/cancel', methods=['POST'])
|
||
def cancel_order(order_id):
|
||
orders = load_json('orders.json')
|
||
idx = next((i for i, o in enumerate(orders) if o['id'] == order_id), None)
|
||
if idx is None:
|
||
return jsonify({'code': 404, 'msg': '订单不存在'}), 404
|
||
orders[idx]['status'] = 'cancelled'
|
||
orders[idx]['updated_at'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||
drone_id = orders[idx].get('drone_id')
|
||
if drone_id:
|
||
drones = load_json('drones.json')
|
||
for d in drones:
|
||
if d['id'] == drone_id:
|
||
d['status'] = 'available'
|
||
break
|
||
save_json('drones.json', drones)
|
||
save_json('orders.json', orders)
|
||
return jsonify({'code': 0, 'data': orders[idx]})
|
||
|
||
|
||
@app.route('/api/orders/<order_id>/rate', methods=['POST'])
|
||
def rate_order(order_id):
|
||
data = request.get_json()
|
||
orders = load_json('orders.json')
|
||
idx = next((i for i, o in enumerate(orders) if o['id'] == order_id), None)
|
||
if idx is None:
|
||
return jsonify({'code': 404, 'msg': '订单不存在'}), 404
|
||
orders[idx]['rating'] = data.get('rating', 5)
|
||
orders[idx]['comment'] = data.get('comment', '')
|
||
save_json('orders.json', orders)
|
||
return jsonify({'code': 0, 'data': orders[idx]})
|
||
|
||
|
||
# ── Quotes ──
|
||
|
||
@app.route('/api/quotes')
|
||
def list_quotes():
|
||
quotes = load_json('quotes.json')
|
||
order_id = request.args.get('order_id')
|
||
if order_id:
|
||
quotes = [q for q in quotes if q['order_id'] == order_id]
|
||
return jsonify({'code': 0, 'data': quotes})
|
||
|
||
|
||
@app.route('/api/quotes', methods=['POST'])
|
||
def create_quote():
|
||
data = request.get_json()
|
||
quotes = load_json('quotes.json')
|
||
quote = {
|
||
'id': gen_id('Q'),
|
||
'order_id': data['order_id'],
|
||
'provider_id': data['provider_id'],
|
||
'drone_id': data['drone_id'],
|
||
'price': data.get('price', 100),
|
||
'eta_minutes': data.get('eta_minutes', 15),
|
||
'distance_km': data.get('distance_km', 5),
|
||
'provider_name': data.get('provider_name', '未知'),
|
||
'drone_name': data.get('drone_name', '未知机型'),
|
||
'provider_rating': data.get('provider_rating', 4.5),
|
||
'provider_flights': data.get('provider_flights', 0),
|
||
'status': 'active',
|
||
'created_at': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||
}
|
||
quotes.append(quote)
|
||
save_json('quotes.json', quotes)
|
||
return jsonify({'code': 0, 'data': quote}), 201
|
||
|
||
|
||
@app.route('/api/quotes/<quote_id>/accept', methods=['POST'])
|
||
def accept_quote(quote_id):
|
||
quotes = load_json('quotes.json')
|
||
q_idx = next((i for i, q in enumerate(quotes) if q['id'] == quote_id), None)
|
||
if q_idx is None:
|
||
return jsonify({'code': 404, 'msg': '报价不存在'}), 404
|
||
|
||
quote = quotes[q_idx]
|
||
quotes[q_idx]['status'] = 'accepted'
|
||
save_json('quotes.json', quotes)
|
||
|
||
orders = load_json('orders.json')
|
||
o_idx = next((i for i, o in enumerate(orders) if o['id'] == quote['order_id']), None)
|
||
if o_idx is not None:
|
||
orders[o_idx]['status'] = 'accepted'
|
||
orders[o_idx]['provider_id'] = quote['provider_id']
|
||
orders[o_idx]['drone_id'] = quote['drone_id']
|
||
orders[o_idx]['price'] = quote['price']
|
||
orders[o_idx]['updated_at'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||
save_json('orders.json', orders)
|
||
|
||
return jsonify({'code': 0, 'data': quote})
|
||
|
||
|
||
# ── Dashboard Stats ──
|
||
|
||
@app.route('/api/dashboard/stats')
|
||
def dashboard_stats():
|
||
role = request.args.get('role', 'demander')
|
||
user_id = request.args.get('user_id')
|
||
orders = load_json('orders.json')
|
||
|
||
if user_id:
|
||
if role == 'demander':
|
||
orders = [o for o in orders if o['demander_id'] == user_id]
|
||
else:
|
||
orders = [o for o in orders if o.get('provider_id') == user_id]
|
||
|
||
total = len(orders)
|
||
pending = len([o for o in orders if o['status'] == 'pending'])
|
||
in_transit = len([o for o in orders if o['status'] in ('accepted', 'in_transit')])
|
||
completed = len([o for o in orders if o['status'] == 'completed'])
|
||
cancelled = len([o for o in orders if o['status'] == 'cancelled'])
|
||
|
||
total_revenue = sum(o['price'] for o in orders if o['status'] == 'completed')
|
||
avg_rating = 0
|
||
rated = [o['rating'] for o in orders if o.get('rating')]
|
||
if rated:
|
||
avg_rating = round(sum(rated) / len(rated), 1)
|
||
|
||
drones_online = 0
|
||
if role == 'provider':
|
||
user_drones = [d for d in load_json('drones.json') if d.get('user_id') == user_id]
|
||
drones_online = len([d for d in user_drones if d['status'] == 'available'])
|
||
else:
|
||
drones_online = len([d for d in load_json('drones.json') if d['status'] == 'available'])
|
||
|
||
stats = {
|
||
'total_orders': total,
|
||
'pending_orders': pending,
|
||
'in_transit_orders': in_transit,
|
||
'completed_orders': completed,
|
||
'cancelled_orders': cancelled,
|
||
'total_revenue': total_revenue,
|
||
'avg_rating': avg_rating,
|
||
'online_drones': drones_online,
|
||
'completion_rate': round(completed / total * 100, 1) if total > 0 else 0
|
||
}
|
||
return jsonify({'code': 0, 'data': stats})
|
||
|
||
|
||
# ── Agent analysis (AI simulation) ──
|
||
|
||
@app.route('/api/agent/analyze', methods=['POST'])
|
||
def agent_analyze():
|
||
data = request.get_json()
|
||
action = data.get('action', '')
|
||
|
||
if action == 'credit_check':
|
||
orders = load_json('orders.json')
|
||
flags = []
|
||
for o in orders:
|
||
if o['status'] == 'cancelled':
|
||
flags.append({
|
||
'order_id': o['id'],
|
||
'issue': '订单被取消',
|
||
'severity': '轻微',
|
||
'suggestion': '建议关注取消原因'
|
||
})
|
||
if o.get('rating') and o['rating'] <= 2:
|
||
flags.append({
|
||
'order_id': o['id'],
|
||
'issue': '低分评价',
|
||
'severity': '中等',
|
||
'suggestion': '建议回访用户了解问题'
|
||
})
|
||
return jsonify({'code': 0, 'data': {'issues': flags, 'summary': f'发现 {len(flags)} 个值得关注的问题'}})
|
||
|
||
elif action == 'pricing_suggestion':
|
||
orders = load_json('orders.json')
|
||
from datetime import datetime
|
||
now = datetime.now()
|
||
hour = now.hour
|
||
if 10 <= hour <= 12 or 17 <= hour <= 19:
|
||
multiplier = 1.2
|
||
reason = '当前为高峰时段,建议加价20%'
|
||
elif 22 <= hour or hour <= 6:
|
||
multiplier = 1.3
|
||
reason = '当前为夜间时段,建议加价30%'
|
||
else:
|
||
multiplier = 1.0
|
||
reason = '当前为平峰时段,维持基础费率'
|
||
return jsonify({
|
||
'code': 0,
|
||
'data': {
|
||
'multiplier': multiplier,
|
||
'reason': reason,
|
||
'suggestion': f'建议当前时段基础费率调整为 {multiplier}x'
|
||
}
|
||
})
|
||
|
||
elif action == 'anomaly_detect':
|
||
orders = load_json('orders.json')
|
||
anomalies = []
|
||
for o in orders:
|
||
if o['status'] == 'in_transit':
|
||
duration_hours = (datetime.now() - datetime.strptime(o['created_at'], '%Y-%m-%d %H:%M:%S')).total_seconds() / 3600
|
||
if duration_hours > 1:
|
||
anomalies.append({
|
||
'order_id': o['id'],
|
||
'type': '配送超时',
|
||
'detail': f'配送已超过{duration_hours:.1f}小时',
|
||
'suggestion': '建议联系承运方确认配送状态'
|
||
})
|
||
return jsonify({'code': 0, 'data': {'anomalies': anomalies, 'count': len(anomalies)}})
|
||
|
||
return jsonify({'code': 400, 'msg': '未知的分析类型'})
|
||
|
||
|
||
# ── Agent management (for future real AI integration) ──
|
||
|
||
@app.route('/api/agent/settings', methods=['GET'])
|
||
def agent_settings():
|
||
path = os.path.join(DB_DIR, 'agent_settings.json')
|
||
if not os.path.exists(path):
|
||
default = {
|
||
'providers': {
|
||
'health_check_enabled': True,
|
||
'auto_reconciliation': True,
|
||
'cert_reminder_days': 30
|
||
},
|
||
'pricing': {
|
||
'dynamic_pricing_enabled': True,
|
||
'max_surge_multiplier': 2.0,
|
||
'peak_hours': ['10:00-12:00', '17:00-20:00']
|
||
},
|
||
'anomaly': {
|
||
'auto_handle_l1': True,
|
||
'auto_handle_l2': False,
|
||
'notification_channels': ['sms', 'app']
|
||
},
|
||
'credit': {
|
||
'fraud_detection_enabled': True,
|
||
'min_rating_threshold': 3,
|
||
'auto_penalty': True
|
||
}
|
||
}
|
||
save_json('agent_settings.json', default)
|
||
return jsonify({'code': 0, 'data': default})
|
||
return jsonify({'code': 0, 'data': load_json('agent_settings.json')})
|
||
|
||
|
||
@app.route('/api/agent/settings', methods=['PUT'])
|
||
def update_agent_settings():
|
||
data = request.get_json()
|
||
save_json('agent_settings.json', data)
|
||
return jsonify({'code': 0, 'msg': 'Agent设置已更新'})
|
||
|
||
|
||
# ── Logging ──
|
||
|
||
@app.route('/api/logs')
|
||
def get_logs():
|
||
path = os.path.join(DB_DIR, 'logs.json')
|
||
if not os.path.exists(path):
|
||
return jsonify({'code': 0, 'data': []})
|
||
logs = load_json('logs.json')
|
||
logs.sort(key=lambda x: x.get('timestamp', ''), reverse=True)
|
||
return jsonify({'code': 0, 'data': logs[:100]})
|
||
|
||
|
||
@app.route('/api/logs', methods=['POST'])
|
||
def add_log():
|
||
data = request.get_json()
|
||
logs = load_json('logs.json') if os.path.exists(os.path.join(DB_DIR, 'logs.json')) else []
|
||
log = {
|
||
'id': gen_id('LOG'),
|
||
'type': data.get('type', 'info'),
|
||
'message': data.get('message', ''),
|
||
'timestamp': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||
}
|
||
logs.append(log)
|
||
save_json('logs.json', logs)
|
||
return jsonify({'code': 0, 'data': log}), 201
|
||
|
||
|
||
# ── Health ──
|
||
|
||
@app.route('/api/health')
|
||
def health():
|
||
return jsonify({'status': 'ok', 'timestamp': datetime.now().strftime('%Y-%m-%d %H:%M:%S')})
|
||
|
||
|
||
if __name__ == '__main__':
|
||
print("""
|
||
╔══════════════════════════════════════════╗
|
||
║ 空运宝 - 后端服务 v1.0 ║
|
||
║ Listening at: http://localhost:5000 ║
|
||
║ API: http://localhost:5000/api/... ║
|
||
╚══════════════════════════════════════════╝
|
||
""")
|
||
app.run(host='0.0.0.0', port=5000, debug=True)
|