分布微服务电商订单系统Rust编码开发[下]
5 网关与页面服务设计
5.1 功能设计
A. 网关功能:
统一API入口,转发请求到对应的微服务;
提供静态文件服务,包括HTML/CSS/JS;
统一错误处理和日志记录。
B. 路由设计:
/api/orders - 订单服务相关路由;
/api/payment - 支付服务相关路由;
/api/inventory - 库存服务相关路由。
/ordersHtml - 订单管理页面;
/paymentHtml - 支付管理页面;
/inventoryHtml - 库存管理页面。
C. 前端交互:
使用纯HTML/CSS/JS实现简单界面;
通过fetch API与后端交互;
表单提交处理各种业务操作。
D. 微服务集成:
使用reqwest库作为HTTP客户端;
支持配置不同的微服务地址;
转发请求并返回响应。
E. 错误处理:
自定义GatewayError类型;
统一处理各种错误情况;
返回适当的HTTP状态码。
5.2 主程序设计--main.rs
网关路由和Nacos注册。
use actix_web::{web, App, HttpServer, middleware::Logger};
use std::collections::HashMap; use log::info; use tera::Tera;
use nacos_sdk::api::naming::{NamingService, NamingServiceBuilder, ServiceInstance};
use nacos_sdk::api::props::ClientProps; mod routes;
#[actix_web::main]
async fn main() -> std::io::Result<()> {
env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
info!("Starting gateway service");
// 服务配置
let service_name = "gateway".to_string();
let group = "ecommerce".to_string(); let ip = "127.0.0.1"; let port = 9000;
// 创建Nacos客户端
let client = NamingServiceBuilder::new(
ClientProps::new().server_addr("127.0.0.1:8848").namespace("public"),)
.build().map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
// 构建服务实例
let instance = ServiceInstance { service_name: Some(service_name.clone()),
ip: ip.to_string(), port, weight: 1.0, healthy: true, enabled: true,
ephemeral: true, instance_id: None, cluster_name: Some("DEFAULT".to_string()),
metadata: HashMap::from([ ("version".into(), "1.0".into()),
("service".into(), "gateway".into()) ])
};
// 注册服务
client.register_instance(service_name.clone(),Some(group.clone()),instance.clone())
.await.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
println!("✅ 订单服务注册成功");
// 微服务地址配置
let services = web::Data::new(HashMap::from([
("order".to_string(), "http://localhost:9001".to_string()),
("payment".to_string(), "http://localhost:9002".to_string()),
("inventory".to_string(), "http://localhost:9003".to_string()) ]));
// 模板引擎初始化
let tera = Tera::new("templates/**/*.html").expect("Failed to initialize Tera");
// HTTP客户端
let client = web::Data::new(reqwest::Client::new());
HttpServer::new(move || {
App::new().app_data(web::Data::new(tera.clone()))
.app_data(services.clone()).app_data(client.clone())
.service(actix_files::Files::new("/static", "static"))
.wrap(Logger::default()).configure(routes::config)
}).bind("127.0.0.1:9000")?
.run().await
}
5.3 路由程序设计--routes.rs
各个微服务的动态路由和网页的Web服务器实现。
use actix_web::{web, HttpResponse, Responder, HttpRequest};
use reqwest::Client; use std::collections::HashMap; use tera::Tera;
// 修改后的代理函数 - 支持完整路径和查询参数
pub async fn proxy(client: web::Data<Client>,services: web::Data<HashMap<String, String>>,
service_name: String,req: HttpRequest, body: web::Bytes,) -> impl Responder {
// 获取完整路径(含查询参数)
let full_path = req.uri().path_and_query()
.map(|pq| pq.as_str()).unwrap_or(req.uri().path());
let target_url = match services.get(&service_name) {
Some(base_url) => format!("{}{}", base_url, full_path),
None => return HttpResponse::BadRequest().body("Invalid service name"),
};
// 构建转发请求
let mut request_builder = client.request(req.method().clone(), &target_url);
// 复制原始请求头
for (key, value) in req.headers().iter() {
request_builder = request_builder.header(key, value);
}
match request_builder.body(body).send().await {
Ok(res) => HttpResponse::build(res.status())
.insert_header(("X-Proxy-Source", "gateway"))
.streaming(res.bytes_stream()),
Err(e) => { log::error!("Proxy error to {}: {}", target_url, e);
HttpResponse::BadGateway().body(format!("Service unavailable: {}", e))
}
}
}
// 渲染HTML页面
pub async fn render_template(tmpl: web::Data<Tera>,
template_name: &str, ) -> impl Responder {
let ctx = tera::Context::new();
match tmpl.render(template_name, &ctx) {
Ok(html) => HttpResponse::Ok().content_type("text/html").body(html),
Err(e) => HttpResponse::InternalServerError().body(e.to_string()),
}
}
pub fn config(cfg: &mut web::ServiceConfig) {
// 页面路由
cfg.service(web::resource("/").to(|tmpl: web::Data<Tera>| async move {
render_template(tmpl, "index.html").await
}));
cfg.service(web::resource("/ordersHtml").to(|tmpl: web::Data<Tera>| async move {
render_template(tmpl, "orders.html").await
}));
cfg.service(web::resource("/paymentHtml").to(|tmpl: web::Data<Tera>| async move {
render_template(tmpl, "payment.html").await
}));
cfg.service(web::resource("/inventoryHtml").to(|tmpl: web::Data<Tera>| async move {
render_template(tmpl, "inventory.html").await
}));
// 订单服务路由: 创建订单 (POST /orders)
cfg.service( web::resource("/orders").route(web::post().to(|client: web::Data<Client>,
services: web::Data<HashMap<String, String>>, req: HttpRequest,
body: web::Bytes| async move {
proxy(client, services, "order".to_string(), req, body).await}, )) );
// 订单服务路由: 获取订单详情 (GET /orders/{order_id})
cfg.service( web::resource("/orders/{order_id}").route(web::get().to(|client: web::Data<Client>,
services: web::Data<HashMap<String, String>>, req: HttpRequest,
body: web::Bytes| async move {
proxy(client, services, "order".to_string(), req, body).await}, )) );
// 订单服务路由: 确认订单 (PUT /orders/{order_id}/confirm)
cfg.service( web::resource("/orders/{order_id}/confirm")
.route(web::put().to(|client: web::Data<Client>,
services: web::Data<HashMap<String, String>>, req: HttpRequest,
body: web::Bytes| async move {
proxy(client, services, "order".to_string(), req, body).await}, )) );
// 订单服务路由: 刷新订单 (PUT /orders/{order_id}/refresh)
cfg.service( web::resource("/orders/{order_id}/refresh")
.route(web::put().to(|client: web::Data<Client>,
services: web::Data<HashMap<String, String>>, req: HttpRequest,
body: web::Bytes| async move {
proxy(client, services, "order".to_string(), req, body).await}, )) );
// 支付服务路由: 支付订单 (POST /orders/{order_id}/pay)
cfg.service( web::resource("/payment/{order_id}/pay")
.route(web::post().to(|client: web::Data<Client>,
services: web::Data<HashMap<String, String>>, req: HttpRequest,
body: web::Bytes| async move {
proxy(client, services, "payment".to_string(), req, body).await },)) );
// 支付服务路由: 获取支付订单详情 (GET /orders/{order_id})
cfg.service( web::resource("/payment/{order_id}")
.route(web::get().to(|client: web::Data<Client>,
services: web::Data<HashMap<String, String>>, req: HttpRequest,
body: web::Bytes| async move {
proxy(client, services, "payment".to_string(), req, body).await }, )) );
// 支付服务路由: 查询用户己支付订单 (GET /orders?user_id=...&pay_status=...)
cfg.service( web::resource("/payment").route(web::get().to(|client: web::Data<Client>,
services: web::Data<HashMap<String, String>>, req: HttpRequest,
body: web::Bytes| async move {
proxy(client, services, "payment".to_string(), req, body).await }, )) );
// 库服务路由: 库存生成与查询
cfg.service(
web::resource("/api/inventory")
.route(web::post().to( |client: web::Data<Client>,
services: web::Data<HashMap<String, String>>, req: HttpRequest,
body: web::Bytes| async move {
proxy(client, services, "inventory".to_string(), req, body).await }, ))
.route(web::get().to(
|client: web::Data<Client>,
services: web::Data<HashMap<String, String>>,
req: HttpRequest,
body: web::Bytes| async move {
proxy(client, services, "inventory".to_string(), req, body).await }, )) );
// 库服务路由: 库存更新
cfg.service( web::resource("/api/inventory/update")
.route(web::post().to(|client: web::Data<Client>,
services: web::Data<HashMap<String, String>>, req: HttpRequest,
body: web::Bytes| async move {
proxy(client, services, "inventory".to_string(), req, body).await }, )) );
// 库服务路由: 库存预占
cfg.service( web::resource("/api/inventory/reserve")
.route(web::post().to( |client: web::Data<Client>,
services: web::Data<HashMap<String, String>>, req: HttpRequest,
body: web::Bytes| async move {
proxy(client, services, "inventory".to_string(), req, body).await }, )) );
// 库服务路由: 库存释放
cfg.service( web::resource("/api/inventory/release")
.route(web::post().to( |client: web::Data<Client>,
services: web::Data<HashMap<String, String>>, req: HttpRequest,
body: web::Bytes| async move {
proxy(client, services, "inventory".to_string(), req, body).await }, )) );
}
5.4 子项目及其依赖配置--Cargo.toml
[package]
name = "gateway"
version = "0.1.0"
edition = "2024"
[dependencies]
actix-web = "4.0" # Web框架
reqwest = { version = "0.11", features = ["json", "stream"] } # HTTP客户端
tera = "1.0" # 模板引擎
log = "0.4" # 日志
env_logger = "0.9"
actix-files = "0.6" # 静态文件服务专用库[1](@ref)[7](@ref)
nacos-sdk = { version = "0.4", features = ["naming"] }
6 Web交互展现页面设计
6.1 主页--indexHtml.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>电商微服务网关</title>
<link rel="stylesheet" href="/static/style.css">
</head>
<body>
<header><h1>电商微服务网关</h1></header>
<nav>
<ul>
<li><a href="/">首页</a></li>
<li><a href="/ordersHtml">订单管理</a></li>
<li><a href="/paymentHtml">支付管理</a></li>
<li><a href="/inventoryHtml">库存管理</a></li>
</ul>
</nav>
<main>
<section class="intro">
<h2>欢迎使用电商微服务网关</h2>
<p>本系统提供订单、支付和库存微服务的统一管理界面。</p>
</section>
</main>
<footer><p>© 2025 电商微服务系统</p></footer>
</body>
<script src="/static/script.js"></script>
</html>
6.2 订单页面--ordersHtml.httml
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>订单管理</title>
<link rel="stylesheet" href="/static/style.css">
</head>
<body>
<header><h1>订单管理</h1></header>
<nav>
<ul>
<li><a href="/">首页</a></li>
<li><a href="/ordersHtml">订单管理</a></li>
<li><a href="/paymentHtml">支付管理</a></li>
<li><a href="/inventoryHtml">库存管理</a></li>
</ul>
</nav>
<main>
<section class="order-form">
<h2>创建新订单</h2>
<form id="createOrderForm">
<div class="form-group">
<label for="userId">用户ID:</label>
<input type="text" id="userId" name="user_id" required>
</div>
<div class="form-group">
<label for="productId">商品ID:</label>
<input type="text" id="productId" name="product_id" required>
</div>
<div class="form-group">
<label for="quantity">数量:</label>
<input type="number" id="quantity" name="quantity" required>
</div>
<div class="form-group">
<label for="totalPrice">总价:</label>
<input type="number" step="0.01" id="totalPrice" name="total_price" required>
</div>
<button type="submit">创建订单</button>
</form>
</section>
<section class="order-actions">
<h2>订单操作</h2>
<div class="form-group">
<label for="orderId">订单ID:</label>
<input type="text" id="orderId" name="order_id" required>
</div>
<button id="getOrderBtn">查询订单</button>
<button id="confirmOrderBtn">确认订单</button>
<button id="refreshOrderBtn">刷新订单</button>
</section>
<section class="order-list">
<h2>订单列表</h2>
<div id="orderList"></div>
</section>
</main>
<footer><p>© 2025 电商微服务系统</p></footer>
</body>
<script src="/static/script.js"></script>
</html>
6.3 支付页面--paymentHtml.httml
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>支付管理</title>
<link rel="stylesheet" href="/static/style.css">
</head>
<body>
<header><h1>支付管理</h1></header>
<nav>
<ul>
<li><a href="/">首页</a></li>
<li><a href="/ordersHtml">订单管理</a></li>
<li><a href="/paymentHtml">支付管理</a></li>
<li><a href="/inventoryHtml">库存管理</a></li>
</ul>
</nav>
<main>
<section class="payment-form">
<h2>支付订单</h2>
<form id="payOrderForm">
<div class="form-group">
<label for="payOrderId">订单ID:</label>
<input type="text" id="payOrderId" name="order_id" required>
</div>
<div class="form-group">
<label for="currency">货币:</label>
<select id="currency" name="currency" required>
<option value="CNY">人民币 (CNY)</option>
<option value="USD">美元 (USD)</option>
<option value="EUR">欧元 (EUR)</option>
</select>
</div>
<div class="form-group">
<label for="paymentMethod">支付方式:</label>
<select id="paymentMethod" name="payment_method" required>
<option value="WeChatPay">微信支付</option>
<option value="Alipay">支付宝</option>
<option value="CreditCard">信用卡</option>
</select>
</div>
<button type="submit">支付</button>
</form>
</section>
<section class="payment-status">
<h2>支付状态查询</h2>
<div class="form-group">
<label for="queryUserId">用户ID:</label>
<input type="text" id="queryUserId" name="user_id">
</div>
<div class="form-group">
<label for="queryPayStatus">支付状态:</label>
<select id="queryPayStatus" name="pay_status">
<option value="">全部</option>
<option value="Paid">已支付</option>
<option value="Unpaid">未支付</option>
<option value="Failed">支付失败</option>
</select>
</div>
<button id="queryPaymentBtn">查询</button>
<div id="paymentList"></div>
</section>
</main>
<footer><p>© 2025 电商微服务系统</p></footer>
</body>
<script src="/static/script.js"></script>
</html>
6.4 库存页面--inventoryHtml.httml
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>库存管理</title>
<link rel="stylesheet" href="/static/style.css">
</head>
<body>
<header><h1>库存管理</h1></header>
<nav>
<ul>
<li><a href="/">首页</a></li>
<li><a href="/ordersHtml">订单管理</a></li>
<li><a href="/paymentHtml">支付管理</a></li>
<li><a href="/inventoryHtml">库存管理</a></li>
</ul>
</nav>
<main>
<section class="inventory-form">
<h2>库存操作</h2>
<div class="form-group">
<label for="productId">商品ID:</label>
<input type="text" id="productId" name="product_id" required>
</div>
<button id="genInventoryBtn">创建库存</button>
<button id="getInventoryBtn">查询库存</button>
</section>
<section class="inventory-actions">
<h2>库存变更</h2>
<form id="updateInventoryForm">
<div class="form-group">
<label for="delta">变更数量 (正数增加,负数减少):</label>
<input type="number" id="delta" name="delta" required>
</div>
<button type="submit">更新库存</button>
</form>
<form id="reserveInventoryForm">
<div class="form-group">
<label for="reserveQuantity">预留数量:</label>
<input type="number" id="reserveQuantity" name="quantity" required>
</div>
<div class="form-group">
<label for="reserveOrderId">订单ID:</label>
<input type="text" id="reserveOrderId" name="order_id" required>
</div>
<button type="submit" onclick="fechtData(false);">预留库存</button>
<button type="submit" onclick="fechtData(true);" >释放库存</button>
</form>
</section>
<section class="inventory-list">
<h2>库存信息</h2>
<div id="inventoryInfo"></div>
</section>
</main>
<footer><p>© 2025 电商微服务系统</p></footer>
</body>
<script src="/static/script.js"></script>
</html>
6.5 页面样式--style.css
/* 全局样式 */
body { font-family: Arial, sans-serif; line-height: 1.6; margin: 0;
padding: 0; color: #333; background-color: #f5f5f5; }
h1, h2, p, div { margin-top: 0.1rem; margin-bottom: 0.1rem }
header { background-color: #4CAF50; color: white; padding: 0.5rem; text-align: center; }
nav { background-color: #333; color: white; padding: 0.5rem; }
nav ul { list-style-type: none; margin: 0; padding: 0;
display: flex; justify-content: space-around; }
nav a { color: white; text-decoration: none; }
nav a:hover { text-decoration: underline; }
main { padding: 1rem; max-width: 1200px; margin: 0 auto; }
footer { background-color: #333; color: white; text-align: center;
padding: 0.5rem; position: fixed; bottom: 0; width: 100%; }
/* 表单样式 */
.form-group { margin-bottom: 0.1rem; }
label { display: block; margin-bottom: 0.1rem; }
input, select { width: 95%; padding: 0.5rem; border: 1px solid #ddd; border-radius: 4px; }
button { background-color: #4CAF50; color: white; border: none; padding: 0.5rem 1rem;
border-radius: 4px; cursor: pointer; margin-top: 0.2rem; }
button:hover { background-color: #45a049; }
/* 分区样式 */
section { background-color: white; padding: 0.5rem; margin-bottom: 0.1rem;
border-radius: 4px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
.intro { text-align: center; padding: 2rem; }
6.6 页面行为控制--script.js
document.addEventListener('DOMContentLoaded', function() {
// 创建订单
if (document.getElementById('createOrderForm')) {
document.getElementById('createOrderForm').addEventListener('submit', function(e) {
e.preventDefault();
const formData = { user_id: document.getElementById('userId').value,
product_id: document.getElementById('productId').value,
quantity: parseInt(document.getElementById('quantity').value),
total_price: parseFloat(document.getElementById('totalPrice').value)
};
fetch('/orders', { method:'POST', headers:{'Content-Type':'application/json'},
body: JSON.stringify(formData) }).then(response => response.json())
.then(data => { alert(`订单创建成功! 订单ID: ${data.id}`); })
.catch(error => { console.error('Error:', error);
alert('创建订单失败'); });
});
}
// 查询订单
if (document.getElementById('getOrderBtn')) {
document.getElementById('getOrderBtn').addEventListener('click', function() {
const orderId = document.getElementById('orderId').value;
fetch(`/orders/${orderId}`).then(response => response.json())
.then(data => {
document.getElementById('orderList').innerHTML =
`<pre>${JSON.stringify(data, null, 2)}</pre>`;
})
.catch(error => { console.error('Error:', error);
alert('查询订单失败'); });
});
}
// 确认订单
if (document.getElementById('confirmOrderBtn')) {
document.getElementById('confirmOrderBtn').addEventListener('click', function() {
const orderId = document.getElementById('orderId').value;
fetch(`/orders/${orderId}/confirm`, { method: 'PUT',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({ status: 'confirmed' }) })
.then(response => response.json())
.then(data => { alert('订单确认成功'); })
.catch(error => { console.error('Error:', error);
alert('确认订单失败'); });
});
}
// 刷新订单
if (document.getElementById('refreshOrderBtn')) {
document.getElementById('refreshOrderBtn').addEventListener('click', function() {
const orderId = document.getElementById('orderId').value;
fetch(`/api/orders/${orderId}/refresh`, { method: 'PUT' })
.then(response => response.json())
.then(data => { alert('订单刷新成功'); })
.catch(error => { console.error('Error:', error);
alert('刷新订单失败'); });
});
}
// 支付订单
if (document.getElementById('payOrderForm')) {
document.getElementById('payOrderForm').addEventListener('submit', function(e) {
e.preventDefault();
const formData = { order_id: document.getElementById('payOrderId').value,
currency: document.getElementById('currency').value,
payment_method: document.getElementById('paymentMethod').value
};
fetch('/payment/'+formData.order_id+'/pay', { method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(formData) }).then(response => response.json())
.then(data => { alert('支付请求已提交'); })
.catch(error => { console.error('Error:', error);
alert('支付失败'); });
});
}
// 查询支付状态
if (document.getElementById('queryPaymentBtn')) {
document.getElementById('queryPaymentBtn').addEventListener('click', function() {
const userId = document.getElementById('queryUserId').value;
const payStatus = document.getElementById('queryPayStatus').value;
let queryParams = {}; if (userId) queryParams.user_id = userId;
if (payStatus) queryParams.pay_status = payStatus;
const queryString = new URLSearchParams(queryParams).toString();
fetch(`/payment?${queryString}`)
.then(response => response.json())
.then(data => {
document.getElementById('paymentList').innerHTML =
`<pre>${JSON.stringify(data, null, 2)}</pre>`;
})
.catch(error => { console.error('Error:', error);
alert('查询支付状态失败'); });
});
}
// 创建库存
if (document.getElementById('genInventoryBtn')) {
document.getElementById('genInventoryBtn').addEventListener('click', function() {
const formData = { product_id: document.getElementById('productId').value, };
fetch('/api/inventory', { method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(formData)
}).then(response => response.json())
.then(data => { alert('库存创建成功'); })
.catch(error => { console.error('Error:', error);
alert('创建库存失败'); });
});
}
// 查询库存
if (document.getElementById('getInventoryBtn')) {
document.getElementById('getInventoryBtn').addEventListener('click', function() {
const productId = document.getElementById('productId').value;
fetch(`/api/inventory?product_id=${productId}`)
.then(response => response.json())
.then(data => {
document.getElementById('inventoryInfo').innerHTML =
`<pre>${JSON.stringify(data, null, 2)}</pre>`;
})
.catch(error => { console.error('Error:', error); alert('查询库存失败'); });
});
}
// 更新库存
if (document.getElementById('updateInventoryForm')) {
document.getElementById('updateInventoryForm').addEventListener('submit', function(e) {
e.preventDefault();
const formData = { product_id: document.getElementById('productId').value,
delta: parseInt(document.getElementById('delta').value)
};
fetch('/api/inventory/update', { method: 'POST',
headers: { 'Content-Type': 'application/json'},
body: JSON.stringify(formData)
}).then(response => response.json())
.then(data => { alert('库存更新成功'); })
.catch(error => { console.error('Error:', error); alert('更新库存失败'); });
});
}
if (document.getElementById('reserveInventoryForm')) {
document.getElementById('reserveInventoryForm').addEventListener('submit', function(e) {
e.preventDefault();
});
}
});
// 预留/释放库存
function fechtData(svTp) {
let t = "/api/inventory/reserve", k = "预留"
if (svTp === true) { t = "/api/inventory/release"; k = "释放" }
const formData = { product_id: document.getElementById('productId').value,
quantity: parseInt(document.getElementById('reserveQuantity').value),
order_id: document.getElementById('reserveOrderId').value
};
fetch(t, { method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(formData) }).then(response => response.json())
.then(data => { alert(k+'库存成功'); })
.catch(error => { console.error('Error:', error); alert(k+'库存失败'); });
}
7 测调试与运行
7.1 独立微服测试
A. 订单
# bash,创建订单
curl.exe -X POST http://localhost:9001/orders -H "Content-Type: application/json" \
-d '{"user_id": "user123", "product_id": "prod456", "quantity": 2, "total_price": 99.98}'
# bash,查询订单
curl.exe http://localhost:9001/orders/ {order_id}
# bash,确认订单:
curl -X PUT http://localhost:9001/orders/ {order_id}/confirm -H "Content-Type: application/json" \
-d '{"status": "confirmed"}'
# bash,刷新订单:
curl -X PUT http://localhost:9001/orders/ {order_id}/refresh
浏览器测试、ApiFox测试、IDE跟踪调试、Navicat数据库监护的典型过程截图,如图2~4所示。
图2 “电商订单微服务”测试与监护组合截图
图3 “电商订单微服务”测试组合截图
图4 “电商订单微服务”RustRoverIDE跟踪调试组合截图
B. 支付
# bash,支付订单
curl.exe -X POST http://localhost:9002/orders/5364b201-e155-4967-b796-96cfc44aa8ac/pay -H "Content-Type: application/json" -d '{"order_id": "orders/5364b201-e155-4967-b796-96cfc44aa8ac", "currency":"CNY", "payment_method":"WeChatPay"}'
# bash,查询订单
curl http://localhost:9002/orders/5364b201-e155-4967-b796-96cfc44aa8ac
# bash,查询用户订单:
curl " http://localhost:9002/orders?user_id=user123&pay_status=Paid "
浏览器测试、ApiFox测试、IDE跟踪调试、Navicat数据库监护的典型过程截图,如图5~7所示。由于逐步开发,作订单设计表时没有考虑订单相应的支付表例,故测试前,需要在Navicat管理器中先向orders集合中添加字段--pay_status及其默认值Pending,如图5所示。
图5 “电商支付微服务”数据库操作与监护组合截图
图6 “电商支付微服务”测试组合截图
图7 “电商支付微服务”RustRoverIDE跟踪调试组合截图
C. 库存
a. 测试操作
# 创建库存记录:
POST /api/inventory
请求体:{"product_id": "prod123"}
# 查询库存:
GET /api/inventory?product_id=prod123
# 更新库存:
POST /api/inventory/update
请求体:{"product_id": "prod123", "delta": 10} (正数增加,负数减少)
# 预留库存:
POST /api/inventory/reserve
请求体:{"product_id": "prod123", "quantity": 5, "order_id": "order123"}
b. 过程记录
浏览器测试、ApiFox测试、IDE跟踪调试、Navicat数据库监护的典型截图,如图8~10所示。
图8 “电商库存微服务”测试组合截图
图9 “电商库存微服务”测试与数据库操作及监护组合截图
图10 “电商库存微服务”RustRoverIDE跟踪调试组合截图
D. 网关
ApiFox测试、IDE跟踪调试的典型过程截图,如图11~12所示。
图11 “网关路由”的IDE跟踪调试过程截图
图12 “网关路由”的ApiFox典型测试过程组合截图
7.2 整体运行与测试
编译得到四个可执行文件:gateway.exe、inventory_service.exe、order_service.exe、payment_service.exe,检查并启动MongoDB数据库,启动Nacos注册配置中心[startup.cmd -m standalone],再逐一启动四个可执行文件。成功运行后的Nacos、gateway.exe、inventory_service.exe、order_service.exe、payment_service.exe,如13组合截图所示。Nacos的浏览器监护页面如图14所示。注意,gateway.exe运行时需要templates和static目录页面文件,可把这两个目录拷贝到gateway.exe同一目录下,两启动,以免页面访问不能打开。
图13 “电商订单系统”开发及其支撑软件启动与运行组合截图
图14 “电商订单系统”的Nacos浏览器监护截图
浏览器交互访问及其跟踪调试截图,如图15~18所示。
图15 “电商订单系统”的主页操作浏览及其跟踪调试截图
图16 “电商订单系统”的订单页操作浏览及其跟踪调试截图
图17 “电商订单系统”的支付操作浏览及其跟踪调试截图
图18 “电商订单系统”的库存操作浏览及其跟踪调试截图