分布式电子考场项目部署
提示词:给出Windows下使用Python开发的电商订单分布微服务系统:订单服务、支付服务、库存服务,NACOS服务注册发现,MongoDB数据存储,统一网关服务并托管兼作页面web服务器,HTML/CSS/JS页面设计[相互关联的订单、支付、库存]。inventorySvc.py完整代码from flask import Flask, jsonify, requestfrom pymongo
1 程序基本结构
提示词:给出Windows下使用Python开发的电商订单分布微服务系统:订单服务、支付服务、库存服务,NACOS服务注册发现,MongoDB数据存储,统一网关服务并托管兼作页面web服务器,HTML/CSS/JS页面设计[相互关联的订单、支付、库存]。
2 系统架构设计概览


3 核心微服务开发
3.1 库存服务
inventorySvc.py完整代码
from flask import Flask, jsonify, request
from pymongo import MongoClient
from bson.objectid import ObjectId
import threading
import time
import requests
app = Flask(__name__)
# MongoDB连接配置
client = MongoClient('mongodb://localhost:27017/')
db = client['ecommerce_db']
products_collection = db['products']
# Nacos配置
NACOS_SERVER = "http://localhost:8848"
SERVICE_IP = "127.0.0.1"
SERVICE_PORT = 5002
SERVICE_NAME = "inventorySvc"
def register_to_nacos():
"""向Nacos注册服务"""
url = f"{NACOS_SERVER}/nacos/v1/ns/instance"
params = {
"serviceName": SERVICE_NAME,
"ip": SERVICE_IP,
"port": SERVICE_PORT,
"weight": 1.0,
"enable": True,
"healthy": True,
"metadata": '{"preserved.register.source":"PYTHON"}'
}
try:
response = requests.post(url, params=params)
if response.status_code == 200:
print(f"服务 {SERVICE_NAME} 注册成功")
else:
print(f"注册失败: {response.text}")
except Exception as e:
print(f"连接Nacos失败: {e}")
def send_heartbeat():
"""发送心跳维持注册状态"""
url = f"{NACOS_SERVER}/nacos/v1/ns/instance/beat"
params = {
"serviceName": SERVICE_NAME,
"ip": SERVICE_IP,
"port": SERVICE_PORT,
"beat": f'{{"ip":"{SERVICE_IP}","port":{SERVICE_PORT},"serviceName":"{SERVICE_NAME}"}}'
}
try:
requests.put(url, params=params)
except:
pass
def start_heartbeat_thread():
"""启动心跳线程"""
def heartbeat_loop():
while True:
send_heartbeat()
time.sleep(5)
threading.Thread(target=heartbeat_loop, daemon=True).start()
def product_serializer(product):
"""将MongoDB文档序列化为JSON友好的字典"""
return {
"id": str(product['_id']),
"name": product.get('name'),
"price": product.get('price'),
"stock": product.get('stock'),
"specs": product.get('specs', {})
}
@app.route('/products', methods=['GET'])
def get_products():
"""获取商品列表接口,支持模糊查询"""
try:
query_name = request.args.get('name')
query = {}
if query_name:
query['name'] = {"$regex": query_name, "$options": "i"}
products_cursor = products_collection.find(query)
products_list = [product_serializer(product) for product in products_cursor]
return jsonify({
"code": 200,
"message": "查询成功",
"data": products_list
}), 200
except Exception as e:
return jsonify({"code": 500, "message": f"服务器错误: {str(e)}"}), 500
@app.route('/inventory/check', methods=['POST'])
def check_and_reduce_stock():
"""检查并扣减库存(供订单服务调用)"""
data = request.json
product_id = data.get('product_id')
quantity = data.get('quantity')
if not product_id or quantity is None:
return jsonify({"error": "参数缺失"}), 400
try:
product = products_collection.find_one({"_id": ObjectId(product_id)})
if product and product.get('stock', 0) >= quantity:
new_stock = product['stock'] - quantity
products_collection.update_one(
{"_id": ObjectId(product_id)},
{"$set": {"stock": new_stock}}
)
return jsonify({
"available": True,
"price": product.get('price', 0)
}), 200
return jsonify({"available": False, "message": "库存不足"}), 200
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/inventory/restore', methods=['POST'])
def restore_stock():
"""恢复库存(用于订单取消或支付失败时的回滚)"""
data = request.json
product_id = data.get('product_id')
quantity = data.get('quantity')
if not product_id or quantity is None:
return jsonify({"error": "参数缺失"}), 400
try:
products_collection.update_one(
{"_id": ObjectId(product_id)},
{"$inc": {"stock": quantity}}
)
return jsonify({"message": "库存恢复成功"}), 200
except Exception as e:
return jsonify({"error": str(e)}), 500
def init_db_data():
"""初始化示例数据"""
if products_collection.count_documents({}) == 0:
sample_products = [
{"name": "华为Mate60", "price": 6999.00, "stock": 100,
"specs": {"color": "黑色", "ram": "12GB", "storage": "256GB"}},
{"name": "iPhone 15 Pro", "price": 8999.00, "stock": 80,
"specs": {"color": "白色", "ram": "8GB", "storage": "256GB"}},
{"name": "小米14 Ultra", "price": 5999.00, "stock": 120,
"specs": {"color": "绿色", "ram": "16GB", "storage": "512GB"}},
{"name": "vivo X100 Pro", "price": 4999.00, "stock": 60,
"specs": {"color": "蓝色", "ram": "12GB", "storage": "256GB"}},
{"name": "OPPO Find X7", "price": 3999.00, "stock": 90,
"specs": {"color": "灰色", "ram": "12GB", "storage": "256GB"}}
]
products_collection.insert_many(sample_products)
print("数据库初始化完成,已插入示例商品。")
if __name__ == '__main__':
init_db_data()
register_to_nacos()
start_heartbeat_thread()
app.run(port=SERVICE_PORT, debug=True)
3.2 订单服务
orderSvc.py
from flask import Flask, jsonify, request
from pymongo import MongoClient
from bson.objectid import ObjectId
import requests
import threading
import time
from datetime import datetime
app = Flask(__name__)
# MongoDB连接配置
client = MongoClient('mongodb://localhost:27017/')
db = client['ecommerce_db']
orders_collection = db['orders']
# Nacos配置
NACOS_SERVER = "http://localhost:8848"
SERVICE_IP = "127.0.0.1"
SERVICE_PORT = 5001
SERVICE_NAME = "orderSvc"
# 服务地址映射(生产环境应从Nacos动态获取)
SERVICE_MAP = {
"inventorySvc": "http://localhost:5002",
"paymentSvc": "http://localhost:5003"
}
def register_to_nacos():
"""向Nacos注册服务"""
url = f"{NACOS_SERVER}/nacos/v1/ns/instance"
params = {
"serviceName": SERVICE_NAME,
"ip": SERVICE_IP,
"port": SERVICE_PORT,
"weight": 1.0,
"enable": True,
"healthy": True
}
try:
response = requests.post(url, params=params)
if response.status_code == 200:
print(f"服务 {SERVICE_NAME} 注册成功")
except Exception as e:
print(f"连接Nacos失败: {e}")
def send_heartbeat():
"""发送心跳"""
url = f"{NACOS_SERVER}/nacos/v1/ns/instance/beat"
params = {
"serviceName": SERVICE_NAME,
"ip": SERVICE_IP,
"port": SERVICE_PORT,
"beat": f'{{"ip":"{SERVICE_IP}","port":{SERVICE_PORT},"serviceName":"{SERVICE_NAME}"}}'
}
try:
requests.put(url, params=params)
except:
pass
def start_heartbeat_thread():
"""启动心跳线程"""
def heartbeat_loop():
while True:
send_heartbeat()
time.sleep(5)
threading.Thread(target=heartbeat_loop, daemon=True).start()
@app.route('/orders', methods=['POST'])
def create_order():
"""创建订单"""
data = request.json
user_id = data.get('user_id')
product_id = data.get('product_id')
quantity = data.get('quantity', 1)
if not all([user_id, product_id]):
return jsonify({"error": "参数缺失"}), 400
try:
# 1. 调用库存服务检查库存
inventory_url = f"{SERVICE_MAP['inventorySvc']}/inventory/check"
inv_response = requests.post(inventory_url, json={
"product_id": product_id,
"quantity": quantity
}, timeout=5)
if inv_response.status_code != 200:
return jsonify({"error": "库存服务不可用"}), 503
inv_data = inv_response.json()
if not inv_data.get('available'):
return jsonify({"error": "库存不足"}), 400
# 2. 创建订单
order_doc = {
"user_id": user_id,
"product_id": product_id,
"quantity": quantity,
"status": "CREATED",
"total_price": inv_data.get('price', 0) * quantity,
"created_at": datetime.now(),
"updated_at": datetime.now()
}
result = orders_collection.insert_one(order_doc)
order_id = str(result.inserted_id)
return jsonify({
"message": "订单创建成功",
"order_id": order_id,
"total_price": order_doc['total_price']
}), 201
except requests.exceptions.Timeout:
return jsonify({"error": "库存服务超时"}), 504
except Exception as e:
return jsonify({"error": f"创建订单失败: {str(e)}"}), 500
@app.route('/orders/<order_id>', methods=['GET'])
def get_order(order_id):
"""查询订单详情"""
try:
order = orders_collection.find_one({"_id": ObjectId(order_id)})
if order:
order['_id'] = str(order['_id'])
order['created_at'] = order['created_at'].isoformat()
order['updated_at'] = order['updated_at'].isoformat()
return jsonify({"code": 200, "data": order}), 200
return jsonify({"error": "订单不存在"}), 404
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/orders/user/<user_id>', methods=['GET'])
def get_user_orders(user_id):
"""查询用户的所有订单"""
try:
orders = list(orders_collection.find({"user_id": user_id}))
for order in orders:
order['_id'] = str(order['_id'])
order['created_at'] = order['created_at'].isoformat()
order['updated_at'] = order['updated_at'].isoformat()
return jsonify({"code": 200, "data": orders}), 200
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/orders/<order_id>/status', methods=['PUT'])
def update_order_status(order_id):
"""更新订单状态"""
data = request.json
new_status = data.get('status')
valid_statuses = ['CREATED', 'PAID', 'SHIPPED', 'DELIVERED', 'CANCELLED']
if new_status not in valid_statuses:
return jsonify({"error": "无效的状态"}), 400
try:
result = orders_collection.update_one(
{"_id": ObjectId(order_id)},
{"$set": {"status": new_status, "updated_at": datetime.now()}}
)
if result.modified_count > 0:
return jsonify({"message": "状态更新成功"}), 200
return jsonify({"error": "订单不存在"}), 404
except Exception as e:
return jsonify({"error": str(e)}), 500
if __name__ == '__main__':
register_to_nacos()
start_heartbeat_thread()
app.run(port=SERVICE_PORT, debug=True)
3.3 支付服务
paymentSvc.py
from flask import Flask, jsonify, request
from pymongo import MongoClient
from bson.objectid import ObjectId
import requests
import threading
import time
from datetime import datetime
import uuid
app = Flask(__name__)
# MongoDB连接配置
client = MongoClient('mongodb://localhost:27017/')
db = client['ecommerce_db']
payments_collection = db['payments']
# Nacos配置
NACOS_SERVER = "http://localhost:8848"
SERVICE_IP = "127.0.0.1"
SERVICE_PORT = 5003
SERVICE_NAME = "paymentSvc"
def register_to_nacos():
"""向Nacos注册服务"""
url = f"{NACOS_SERVER}/nacos/v1/ns/instance"
params = {
"serviceName": SERVICE_NAME,
"ip": SERVICE_IP,
"port": SERVICE_PORT,
"weight": 1.0,
"enable": True,
"healthy": True
}
try:
response = requests.post(url, params=params)
if response.status_code == 200:
print(f"服务 {SERVICE_NAME} 注册成功")
except Exception as e:
print(f"连接Nacos失败: {e}")
def send_heartbeat():
"""发送心跳"""
url = f"{NACOS_SERVER}/nacos/v1/ns/instance/beat"
params = {
"serviceName": SERVICE_NAME,
"ip": SERVICE_IP,
"port": SERVICE_PORT,
"beat": f'{{"ip":"{SERVICE_IP}","port":{SERVICE_PORT},"serviceName":"{SERVICE_NAME}"}}'
}
try:
requests.put(url, params=params)
except:
pass
def start_heartbeat_thread():
"""启动心跳线程"""
def heartbeat_loop():
while True:
send_heartbeat()
time.sleep(5)
threading.Thread(target=heartbeat_loop, daemon=True).start()
@app.route('/pay', methods=['POST'])
def process_payment():
"""处理支付"""
data = request.json
order_id = data.get('order_id')
amount = data.get('amount')
payment_method = data.get('payment_method', 'credit_card')
if not all([order_id, amount]):
return jsonify({"error": "参数缺失"}), 400
try:
# 模拟支付处理
transaction_id = f"TXN{datetime.now().strftime('%Y%m%d%H%M%S')}{uuid.uuid4().hex[:6].upper()}"
payment_doc = {
"order_id": order_id,
"amount": amount,
"payment_method": payment_method,
"status": "SUCCESS",
"transaction_id": transaction_id,
"created_at": datetime.now()
}
result = payments_collection.insert_one(payment_doc)
# 支付成功后,通知订单服务更新状态
try:
order_service_url = "http://localhost:5001"
requests.put(
f"{order_service_url}/orders/{order_id}/status",
json={"status": "PAID"},
timeout=3
)
except:
print(f"通知订单服务失败,订单ID: {order_id}")
return jsonify({
"message": "支付成功",
"status": "SUCCESS",
"transaction_id": transaction_id,
"payment_id": str(result.inserted_id)
}), 200
except Exception as e:
return jsonify({"error": f"支付处理失败: {str(e)}"}), 500
@app.route('/payments/<order_id>', methods=['GET'])
def get_payment(order_id):
"""查询支付记录"""
try:
payment = payments_collection.find_one({"order_id": order_id})
if payment:
payment['_id'] = str(payment['_id'])
payment['created_at'] = payment['created_at'].isoformat()
return jsonify({"code": 200, "data": payment}), 200
return jsonify({"error": "支付记录不存在"}), 404
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/refund', methods=['POST'])
def process_refund():
"""处理退款"""
data = request.json
order_id = data.get('order_id')
if not order_id:
return jsonify({"error": "参数缺失"}), 400
try:
payment = payments_collection.find_one({"order_id": order_id})
if not payment:
return jsonify({"error": "支付记录不存在"}), 404
if payment['status'] != 'SUCCESS':
return jsonify({"error": "该订单未支付成功"}), 400
# 模拟退款处理
payments_collection.update_one(
{"order_id": order_id},
{"$set": {"status": "REFUNDED", "refund_time": datetime.now()}}
)
# 通知订单服务更新状态
try:
order_service_url = "http://localhost:5001"
requests.put(
f"{order_service_url}/orders/{order_id}/status",
json={"status": "CANCELLED"},
timeout=3
)
except:
print(f"通知订单服务失败,订单ID: {order_id}")
return jsonify({"message": "退款成功"}), 200
except Exception as e:
return jsonify({"error": f"退款处理失败: {str(e)}"}), 500
if __name__ == '__main__':
register_to_nacos()
start_heartbeat_thread()
app.run(port=SERVICE_PORT, debug=True)
4 Nacos服务注册与发现集成
registerNacosAll.py
import requests
import threading
import time
NACOS_SERVER = "http://localhost:8848"
SERVICE_IP = "127.0.0.1"
# 服务配置列表
SERVICES = [
{"name": "orderSvc", "port": 5001},
{"name": "inventorySvc", "port": 5002},
{"name": "paymentSvc", "port": 5003},
{"name": "gateway", "port": 8080}
]
def register_service(service_name, service_port):
"""注册单个服务到Nacos"""
url = f"{NACOS_SERVER}/nacos/v1/ns/instance"
params = {
"serviceName": service_name,
"ip": SERVICE_IP,
"port": service_port,
"weight": 1.0,
"enable": True,
"healthy": True,
"metadata": '{"preserved.register.source":"PYTHON"}'
}
try:
response = requests.post(url, params=params)
if response.status_code == 200:
print(f"✅ 服务 {service_name} (端口:{service_port}) 注册成功")
else:
print(f"❌ 服务 {service_name} 注册失败: {response.text}")
except Exception as e:
print(f"❌ 连接Nacos失败: {e}")
def send_heartbeat(service_name, service_port):
"""发送心跳"""
url = f"{NACOS_SERVER}/nacos/v1/ns/instance/beat"
params = {
"serviceName": service_name,
"ip": SERVICE_IP,
"port": service_port,
"beat": f'{{"ip":"{SERVICE_IP}","port":{service_port},"serviceName":"{service_name}"}}'
}
try:
requests.put(url, params=params)
except:
pass
def heartbeat_loop():
"""心跳循环"""
while True:
for service in SERVICES:
send_heartbeat(service["name"], service["port"])
time.sleep(5)
if __name__ == '__main__':
print("�� 开始注册所有服务到Nacos...")
print(f"�� Nacos服务器地址: {NACOS_SERVER}")
print(f"�� 本机IP: {SERVICE_IP}")
print("-" * 50)
# 注册所有服务
for service in SERVICES:
register_service(service["name"], service["port"])
print("-" * 50)
print("✅ 所有服务注册完成,开始发送心跳...")
# 启动心跳线程
threading.Thread(target=heartbeat_loop, daemon=True).start()
# 保持主线程运行
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
print("\n�� 程序退出")
5 统一网关与前端页面设计
5.1 网关服务实现
gateway.py
from flask import Flask, request, jsonify, send_from_directory
import requests
import threading
import time
app = Flask(__name__, static_folder='static')
# 服务路由映射表(生产环境应从Nacos动态获取)
SERVICE_MAP = {
"orderSvc": "http://localhost:5001",
"paymentSvc": "http://localhost:5003",
"inventorySvc": "http://localhost:5002"
}
# Nacos配置
NACOS_SERVER = "http://localhost:8848"
SERVICE_IP = "127.0.0.1"
SERVICE_PORT = 8080
SERVICE_NAME = "gateway"
def register_to_nacos():
"""向Nacos注册网关服务"""
url = f"{NACOS_SERVER}/nacos/v1/ns/instance"
params = {
"serviceName": SERVICE_NAME,
"ip": SERVICE_IP,
"port": SERVICE_PORT,
"weight": 1.0,
"enable": True,
"healthy": True
}
try:
response = requests.post(url, params=params)
if response.status_code == 200:
print(f"网关服务 {SERVICE_NAME} 注册成功")
except Exception as e:
print(f"连接Nacos失败: {e}")
def send_heartbeat():
"""发送心跳"""
url = f"{NACOS_SERVER}/nacos/v1/ns/instance/beat"
params = {
"serviceName": SERVICE_NAME,
"ip": SERVICE_IP,
"port": SERVICE_PORT,
"beat": f'{{"ip":"{SERVICE_IP}","port":{SERVICE_PORT},"serviceName":"{SERVICE_NAME}"}}'
}
try:
requests.put(url, params=params)
except:
pass
def start_heartbeat_thread():
"""启动心跳线程"""
def heartbeat_loop():
while True:
send_heartbeat()
time.sleep(5)
threading.Thread(target=heartbeat_loop, daemon=True).start()
@app.route('/api/<service_name>/<path:path>', methods=['GET', 'POST', 'PUT', 'DELETE'])
def proxy(service_name, path):
"""API路由转发"""
target_url = SERVICE_MAP.get(service_name)
if not target_url:
return jsonify({"error": "Service not found"}), 404
url = f"{target_url}/{path}"
try:
# 转发请求到目标服务
resp = requests.request(
method=request.method,
url=url,
headers={k: v for k, v in request.headers if k != 'Host'},
data=request.get_data(),
params=request.args,
timeout=10
)
# 返回响应
excluded_headers = ['content-encoding', 'content-length', 'transfer-encoding', 'connection']
headers = [(name, value) for name, value in resp.raw.headers.items()
if name.lower() not in excluded_headers]
return (resp.content, resp.status_code, headers)
except requests.exceptions.Timeout:
return jsonify({"error": "服务超时"}), 504
except requests.exceptions.ConnectionError:
return jsonify({"error": "服务不可用"}), 503
except Exception as e:
return jsonify({"error": f"代理请求失败: {str(e)}"}), 500
# 托管前端页面
@app.route('/')
def index():
return send_from_directory('static', 'index.html')
@app.route('/<path:filename>')
def static_files(filename):
return send_from_directory('static', filename)
if __name__ == '__main__':
register_to_nacos()
start_heartbeat_thread()
app.run(port=SERVICE_PORT, debug=True)
5.2 前端页面设计
static/index.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>电商微服务系统</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1400px;
margin: 0 auto;
}
header {
background: white;
border-radius: 15px;
padding: 30px;
margin-bottom: 30px;
box-shadow: 0 10px 40px rgba(0,0,0,0.1);
text-align: center;
}
header h1 {
color: #333;
font-size: 2.5em;
margin-bottom: 10px;
}
header p {
color: #666;
font-size: 1.1em;
}
.dashboard {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 30px;
margin-bottom: 30px;
}
.card {
background: white;
border-radius: 15px;
padding: 25px;
box-shadow: 0 10px 40px rgba(0,0,0,0.1);
transition: transform 0.3s ease;
}
.card:hover {
transform: translateY(-5px);
}
.card h2 {
color: #333;
margin-bottom: 20px;
padding-bottom: 15px;
border-bottom: 2px solid #f0f0f0;
display: flex;
align-items: center;
gap: 10px;
}
.card h2 .icon {
font-size: 1.5em;
}
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: block;
margin-bottom: 5px;
color: #555;
font-weight: 500;
}
.form-group input,
.form-group select {
width: 100%;
padding: 10px 15px;
border: 2px solid #e0e0e0;
border-radius: 8px;
font-size: 14px;
transition: border-color 0.3s ease;
}
.form-group input:focus,
.form-group select:focus {
outline: none;
border-color: #667eea;
}
.btn {
padding: 10px 25px;
border: none;
border-radius: 8px;
font-size: 14px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
margin-right: 10px;
margin-bottom: 10px;
}
.btn-primary {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 5px 20px rgba(102, 126, 234, 0.4);
}
.btn-success {
background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
color: white;
}
.btn-success:hover {
transform: translateY(-2px);
box-shadow: 0 5px 20px rgba(17, 153, 142, 0.4);
}
.btn-danger {
background: linear-gradient(135deg, #eb3349 0%, #f45c43 100%);
color: white;
}
.btn-danger:hover {
transform: translateY(-2px);
box-shadow: 0 5px 20px rgba(235, 51, 73, 0.4);
}
.btn-info {
background: linear-gradient(135deg, #2193b0 0%, #6dd5ed 100%);
color: white;
}
.btn-info:hover {
transform: translateY(-2px);
box-shadow: 0 5px 20px rgba(33, 147, 176, 0.4);
}
.result-area {
margin-top: 20px;
padding: 15px;
background: #f8f9fa;
border-radius: 8px;
border: 1px solid #e0e0e0;
max-height: 300px;
overflow-y: auto;
}
.result-area pre {
white-space: pre-wrap;
word-wrap: break-word;
font-family: 'Courier New', monospace;
font-size: 13px;
color: #333;
}
.status-badge {
display: inline-block;
padding: 3px 10px;
border-radius: 12px;
font-size: 12px;
font-weight: 600;
}
.status-created { background: #fff3cd; color: #856404; }
.status-paid { background: #d4edda; color: #155724; }
.status-shipped { background: #cce5ff; color: #004085; }
.status-delivered { background: #d1ecf1; color: #0c5460; }
.status-cancelled { background: #f8d7da; color: #721c24; }
.full-width {
grid-column: 1 / -1;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 15px;
margin-bottom: 20px;
}
.stat-item {
text-align: center;
padding: 15px;
background: #f8f9fa;
border-radius: 10px;
}
.stat-item .number {
font-size: 2em;
font-weight: 700;
color: #667eea;
}
.stat-item .label {
color: #666;
margin-top: 5px;
}
@media (max-width: 768px) {
.dashboard {
grid-template-columns: 1fr;
}
.stats-grid {
grid-template-columns: 1fr;
}
header h1 {
font-size: 1.8em;
}
}
</style>
</head>
<body>
<div class="container">
<header>
<h1>�� 电商微服务系统</h1>
<p>基于Python + Flask + MongoDB + Nacos 构建的分布式电商平台</p>
</header>
<div class="stats-grid">
<div class="stat-item">
<div class="number" id="productCount">0</div>
<div class="label">商品总数</div>
</div>
<div class="stat-item">
<div class="number" id="orderCount">0</div>
<div class="label">订单总数</div>
</div>
<div class="stat-item">
<div class="number" id="paymentCount">0</div>
<div class="label">支付成功数</div>
</div>
</div>
<div class="dashboard">
<!-- 商品管理 -->
<div class="card">
<h2><span class="icon">��</span> 商品管理</h2>
<div class="form-group">
<label>商品名称</label>
<input type="text" id="productName" placeholder="输入商品名称搜索">
</div>
<div>
<button class="btn btn-primary" onclick="searchProducts()">�� 搜索商品</button>
<button class="btn btn-info" onclick="loadAllProducts()">�� 加载全部</button>
</div>
<div class="result-area" id="productResult">
<pre>点击按钮加载商品数据...</pre>
</div>
</div>
<!-- 订单管理 -->
<div class="card">
<h2><span class="icon">��</span> 订单管理</h2>
<div class="form-group">
<label>用户ID</label>
<input type="text" id="userId" value="user_001" placeholder="输入用户ID">
</div>
<div class="form-group">
<label>商品ID</label>
<input type="text" id="orderProductId" placeholder="输入商品ID">
</div>
<div class="form-group">
<label>数量</label>
<input type="number" id="orderQuantity" value="1" min="1">
</div>
<div>
<button class="btn btn-success" onclick="createOrder()">�� 创建订单</button>
<button class="btn btn-info" onclick="getUserOrders()">�� 我的订单</button>
</div>
<div class="result-area" id="orderResult">
<pre>填写信息后创建订单...</pre>
</div>
</div>
<!-- 支付管理 -->
<div class="card">
<h2><span class="icon">��</span> 支付管理</h2>
<div class="form-group">
<label>订单ID</label>
<input type="text" id="paymentOrderId" placeholder="输入订单ID">
</div>
<div class="form-group">
<label>支付金额</label>
<input type="number" id="paymentAmount" step="0.01" placeholder="输入支付金额">
</div>
<div class="form-group">
<label>支付方式</label>
<select id="paymentMethod">
<option value="credit_card">信用卡</option>
<option value="alipay">支付宝</option>
<option value="wechat">微信支付</option>
<option value="bank_transfer">银行转账</option>
</select>
</div>
<div>
<button class="btn btn-success" onclick="processPayment()">�� 支付</button>
<button class="btn btn-danger" onclick="processRefund()">↩️ 退款</button>
<button class="btn btn-info" onclick="checkPayment()">�� 查询支付</button>
</div>
<div class="result-area" id="paymentResult">
<pre>填写信息后进行支付操作...</pre>
</div>
</div>
<!-- 库存管理 -->
<div class="card">
<h2><span class="icon">��</span> 库存管理</h2>
<div class="form-group">
<label>商品ID</label>
<input type="text" id="stockProductId" placeholder="输入商品ID">
</div>
<div class="form-group">
<label>扣减数量</label>
<input type="number" id="stockQuantity" value="1" min="1">
</div>
<div>
<button class="btn btn-primary" onclick="checkStock()">✅ 检查库存</button>
<button class="btn btn-danger" onclick="reduceStock()">�� 扣减库存</button>
<button class="btn btn-success" onclick="restoreStock()">�� 恢复库存</button>
</div>
<div class="result-area" id="stockResult">
<pre>输入商品ID进行库存操作...</pre>
</div>
</div>
</div>
</div>
<script>
const API_BASE = '/api';
// 显示结果
function showResult(elementId, data) {
const element = document.getElementById(elementId);
element.innerHTML = `<pre>${JSON.stringify(data, null, 2)}</pre>`;
}
// 显示错误
function showError(elementId, error) {
const element = document.getElementById(elementId);
element.innerHTML = `<pre style="color: red;">错误: ${error}</pre>`;
}
// 加载所有商品
function loadAllProducts() {
fetch(`${API_BASE}/inventorySvc/products`)
.then(response => response.json())
.then(data => {
showResult('productResult', data);
if (data.data) {
document.getElementById('productCount').textContent = data.data.length;
}
})
.catch(error => showError('productResult', error));
}
// 搜索商品
function searchProducts() {
const name = document.getElementById('productName').value;
const url = name ?
`${API_BASE}/inventorySvc/products?name=${encodeURIComponent(name)}` :
`${API_BASE}/inventorySvc/products`;
fetch(url)
.then(response => response.json())
.then(data => showResult('productResult', data))
.catch(error => showError('productResult', error));
}
// 创建订单
function createOrder() {
const userId = document.getElementById('userId').value;
const productId = document.getElementById('orderProductId').value;
const quantity = parseInt(document.getElementById('orderQuantity').value);
if (!userId || !productId) {
showError('orderResult', '请填写用户ID和商品ID');
return;
}
fetch(`${API_BASE}/orderSvc/orders`, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
user_id: userId,
product_id: productId,
quantity: quantity
})
})
.then(response => response.json())
.then(data => {
showResult('orderResult', data);
if (data.order_id) {
document.getElementById('paymentOrderId').value = data.order_id;
document.getElementById('paymentAmount').value = data.total_price;
}
})
.catch(error => showError('orderResult', error));
}
// 获取用户订单
function getUserOrders() {
const userId = document.getElementById('userId').value;
if (!userId) {
showError('orderResult', '请填写用户ID');
return;
}
fetch(`${API_BASE}/orderSvc/orders/user/${userId}`)
.then(response => response.json())
.then(data => {
showResult('orderResult', data);
if (data.data) {
document.getElementById('orderCount').textContent = data.data.length;
}
})
.catch(error => showError('orderResult', error));
}
// 处理支付
function processPayment() {
const orderId = document.getElementById('paymentOrderId').value;
const amount = parseFloat(document.getElementById('paymentAmount').value);
const paymentMethod = document.getElementById('paymentMethod').value;
if (!orderId || !amount) {
showError('paymentResult', '请填写订单ID和支付金额');
return;
}
fetch(`${API_BASE}/paymentSvc/pay`, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
order_id: orderId,
amount: amount,
payment_method: paymentMethod
})
})
.then(response => response.json())
.then(data => {
showResult('paymentResult', data);
if (data.status === 'SUCCESS') {
document.getElementById('paymentCount').textContent =
parseInt(document.getElementById('paymentCount').textContent) + 1;
}
})
.catch(error => showError('paymentResult', error));
}
// 处理退款
function processRefund() {
const orderId = document.getElementById('paymentOrderId').value;
if (!orderId) {
showError('paymentResult', '请填写订单ID');
return;
}
fetch(`${API_BASE}/paymentSvc/refund`, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({order_id: orderId})
})
.then(response => response.json())
.then(data => showResult('paymentResult', data))
.catch(error => showError('paymentResult', error));
}
// 查询支付
function checkPayment() {
const orderId = document.getElementById('paymentOrderId').value;
if (!orderId) {
showError('paymentResult', '请填写订单ID');
return;
}
fetch(`${API_BASE}/paymentSvc/payments/${orderId}`)
.then(response => response.json())
.then(data => showResult('paymentResult', data))
.catch(error => showError('paymentResult', error));
}
// 检查库存
function checkStock() {
const productId = document.getElementById('stockProductId').value;
if (!productId) {
showError('stockResult', '请填写商品ID');
return;
}
fetch(`${API_BASE}/inventorySvc/inventory/check`, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
product_id: productId,
quantity: 0
})
})
.then(response => response.json())
.then(data => showResult('stockResult', data))
.catch(error => showError('stockResult', error));
}
// 扣减库存
function reduceStock() {
const productId = document.getElementById('stockProductId').value;
const quantity = parseInt(document.getElementById('stockQuantity').value);
if (!productId) {
showError('stockResult', '请填写商品ID');
return;
}
fetch(`${API_BASE}/inventorySvc/inventory/check`, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
product_id: productId,
quantity: quantity
})
})
.then(response => response.json())
.then(data => showResult('stockResult', data))
.catch(error => showError('stockResult', error));
}
// 恢复库存
function restoreStock() {
const productId = document.getElementById('stockProductId').value;
const quantity = parseInt(document.getElementById('stockQuantity').value);
if (!productId) {
showError('stockResult', '请填写商品ID');
return;
}
fetch(`${API_BASE}/inventorySvc/inventory/restore`, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
product_id: productId,
quantity: quantity
})
})
.then(response => response.json())
.then(data => showResult('stockResult', data))
.catch(error => showError('stockResult', error));
}
// 页面加载时自动加载商品
window.onload = function() {
loadAllProducts();
};
</script>
</body>
</html>
JS交互static/script.js
const API_GATEWAY = 'http://localhost:8080/api'
async function loadProducts() { // 加载商品列表
const response = await fetch(`${API_GATEWAY}/inventorySvc/products`)
const products = await response.json(); const listDiv = document.getElementById('product-list')
listDiv.innerHTML = products.data.map(p =>
`<div class="product">
<span>${p.name} - 库存: ${p.stock}</span>
<button onclick="document.getElementById('product-id').value='${p.id}'">选择</button>
</div>`
).join('')
}
async function createOrder() { // 创建订单
const userId = document.getElementById('user-id').value
const productId = document.getElementById('product-id').value
const response = await fetch(`${API_GATEWAY}/orderSvc/orders`, {
method: 'POST', headers: {'Content-Type': 'application/json'},
body: JSON.stringify({user_id: userId, product_id: productId, quantity: 1})
})
const result = await response.json(); alert(result.message || result.error)
async function payOrder() { // 支付订单
const orderId = document.getElementById('order-id').value
// 简化:假设支付金额固定
const response = await fetch(`${API_GATEWAY}/paymentSvc/pay`, {
method: 'POST', headers: {'Content-Type': 'application/json'},
body: JSON.stringify({order_id: orderId, amount: 100}) })
const result = await response.json()
alert(result.message)
}
window.onload = loadProducts // 页面加载时初始化
6 部署运行测试
6.1 提前准备
安装python,mongodb,navicat管理器,JDK1.8以上版本,安装并运行nacos
6.2 核心依赖库清单
requirements.txt,更新后的库清单:
annotated-doc==0.0.4
annotated-types==0.7.0
anyio==4.13.0
blinker==1.9.0
certifi==2026.5.20
charset-normalizer==3.4.7
click==8.4.1
colorama==0.4.6
dnspython==2.8.0
fastapi==0.136.3
Flask==3.1.3
h11==0.16.0
idna==3.18
itsdangerous==2.2.0
Jinja2==3.1.6
MarkupSafe==3.0.3
pydantic==2.13.4
pydantic_core==2.46.4
pymongo==4.17.0
python-multipart==0.0.30
python-nacos==0.1.1
requests==2.34.2
starlette==1.2.1
typing-inspection==0.4.2
typing_extensions==4.15.0
urllib3==2.7.0
uvicorn==0.48.0
Werkzeug==3.1.8
6.3 虚拟环境操作指南
6.3.1 创建虚拟环境
Python 3.3+ 内置了 venv 模块,推荐在项目根目录下执行以下命令创建名为 venv 的虚拟环境:python -m venv venv。该命令会在当前目录下创建一个包含独立Python解释器和pip工具的文件夹。
6.3.2 激活虚拟环境
创建完成后,必须激活环境才能在其中安装依赖。在Windows命令提示符(CMD)或PowerShell中,执行:
在CMD中,venv\Scripts\activate.bat。
在PowerShell中 (可能需要先执行 Set-ExecutionPolicy RemoteSigned),venv\Scripts\Activate.ps1。
激活成功后,终端提示符前会出现 (venv) 前缀,表明已进入隔离环境。
6.3.3 安装依赖
在激活的虚拟环境中,使用pip安装 requirements.txt 中列出的依赖:
pip install -r requirements.txt
此操作会将所有依赖安装到 venv\Lib\site-packages 目录下,与系统全局环境完全隔离。
6.3.4 依赖管理与导出
在开发过程中,如果新增了依赖库,应及时更新 requirements.txt 文件,以便团队协作和部署:pip freeze > requirements.txt。该命令会将当前环境所有包及其精确版本号写入文件,确保环境可复现。
6.4 部署运行与调式
6.4.1 本地开发调试
在开发阶段,可以直接使用Flask内置服务器进行调试。
调试模式:在代码中设置 app.run(debug=True),开启调试模式后,代码修改会自动重载,便于快速迭代。
运行服务:确保虚拟环境已激活,运行服务5个py文件
python orderSvc.py
python invertorySvc.py
python paymentSvc.py
python gateway.py
python registerNacosAll.py
6.4.2 进入页面
localhost:8080查看页面

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐
所有评论(0)