订单展示支付流程

This commit is contained in:
2025-07-03 11:10:16 +08:00
parent bd9330675e
commit 6294765388
3 changed files with 828 additions and 14 deletions

View File

@@ -181,6 +181,51 @@ public class MessageListenerService {
log.debug("推荐替代商品: 用户ID={}, 秒杀ID={}", userId, flashSaleId); log.debug("推荐替代商品: 用户ID={}, 秒杀ID={}", userId, flashSaleId);
} }
/**
* 提取Long值
*/
private Long extractLongValue(Object value) {
if (value == null) return null;
if (value instanceof Long) return (Long) value;
if (value instanceof Integer) return ((Integer) value).longValue();
if (value instanceof String) return Long.valueOf((String) value);
return Long.valueOf(value.toString());
}
/**
* 提取Integer值
*/
private Integer extractIntegerValue(Object value) {
if (value == null) return null;
if (value instanceof Integer) return (Integer) value;
if (value instanceof Long) return ((Long) value).intValue();
if (value instanceof String) return Integer.valueOf((String) value);
return Integer.valueOf(value.toString());
}
/**
* 解析Redisson消息格式
*/
private Map<String, Object> parseRedissonMessage(String messageBody) throws Exception {
// 处理Redisson序列化的数据格式
if (messageBody.startsWith("[") && messageBody.contains("java.util.HashMap")) {
// 解析Redisson序列化格式: ["java.util.HashMap",{...}]
int startIndex = messageBody.indexOf('{');
int endIndex = messageBody.lastIndexOf('}') + 1;
if (startIndex > 0 && endIndex > startIndex) {
String jsonPart = messageBody.substring(startIndex, endIndex);
// 处理Redisson的类型信息格式: ["java.lang.Long",11] -> 11
jsonPart = jsonPart.replaceAll("\\[\"java\\.lang\\.(Long|Integer|String)\",([^\\]]+)\\]", "$2");
return objectMapper.readValue(jsonPart, Map.class);
} else {
throw new RuntimeException("无法解析Redisson消息格式");
}
} else {
// 标准JSON格式
return objectMapper.readValue(messageBody, Map.class);
}
}
/** /**
* 订单状态变更监听器 * 订单状态变更监听器
*/ */
@@ -189,11 +234,13 @@ public class MessageListenerService {
public void onMessage(Message message, byte[] pattern) { public void onMessage(Message message, byte[] pattern) {
try { try {
String messageBody = new String(message.getBody()); String messageBody = new String(message.getBody());
Map<String, Object> data = objectMapper.readValue(messageBody, Map.class); log.debug("接收到订单状态变更消息: {}", messageBody);
Long orderId = Long.valueOf(data.get("orderId").toString()); Map<String, Object> data = parseRedissonMessage(messageBody);
Long userId = Long.valueOf(data.get("userId").toString());
Integer status = Integer.valueOf(data.get("status").toString()); Long orderId = extractLongValue(data.get("orderId"));
Long userId = extractLongValue(data.get("userId"));
Integer status = extractIntegerValue(data.get("status"));
String action = data.get("action").toString(); String action = data.get("action").toString();
log.info("订单状态变更: 订单ID={}, 用户ID={}, 状态={}, 操作={}", log.info("订单状态变更: 订单ID={}, 用户ID={}, 状态={}, 操作={}",
@@ -216,10 +263,12 @@ public class MessageListenerService {
public void onMessage(Message message, byte[] pattern) { public void onMessage(Message message, byte[] pattern) {
try { try {
String messageBody = new String(message.getBody()); String messageBody = new String(message.getBody());
Map<String, Object> data = objectMapper.readValue(messageBody, Map.class); log.debug("接收到库存变更消息: {}", messageBody);
Long productId = Long.valueOf(data.get("productId").toString()); Map<String, Object> data = parseRedissonMessage(messageBody);
Integer quantity = Integer.valueOf(data.get("quantity").toString());
Long productId = extractLongValue(data.get("productId"));
Integer quantity = extractIntegerValue(data.get("quantity"));
String operation = data.get("operation").toString(); String operation = data.get("operation").toString();
log.info("库存变更: 商品ID={}, 数量={}, 操作={}", productId, quantity, operation); log.info("库存变更: 商品ID={}, 数量={}, 操作={}", productId, quantity, operation);
@@ -241,10 +290,12 @@ public class MessageListenerService {
public void onMessage(Message message, byte[] pattern) { public void onMessage(Message message, byte[] pattern) {
try { try {
String messageBody = new String(message.getBody()); String messageBody = new String(message.getBody());
Map<String, Object> data = objectMapper.readValue(messageBody, Map.class); log.debug("接收到秒杀结果消息: {}", messageBody);
Long userId = Long.valueOf(data.get("userId").toString()); Map<String, Object> data = parseRedissonMessage(messageBody);
Long flashSaleId = Long.valueOf(data.get("flashSaleId").toString());
Long userId = extractLongValue(data.get("userId"));
Long flashSaleId = extractLongValue(data.get("flashSaleId"));
Boolean success = Boolean.valueOf(data.get("success").toString()); Boolean success = Boolean.valueOf(data.get("success").toString());
log.info("秒杀结果: 用户ID={}, 秒杀ID={}, 成功={}", userId, flashSaleId, success); log.info("秒杀结果: 用户ID={}, 秒杀ID={}, 成功={}", userId, flashSaleId, success);
@@ -266,9 +317,11 @@ public class MessageListenerService {
public void onMessage(Message message, byte[] pattern) { public void onMessage(Message message, byte[] pattern) {
try { try {
String messageBody = new String(message.getBody()); String messageBody = new String(message.getBody());
Map<String, Object> data = objectMapper.readValue(messageBody, Map.class); log.debug("接收到用户行为消息: {}", messageBody);
Long userId = Long.valueOf(data.get("userId").toString()); Map<String, Object> data = parseRedissonMessage(messageBody);
Long userId = extractLongValue(data.get("userId"));
String action = data.get("action").toString(); String action = data.get("action").toString();
log.info("用户行为: 用户ID={}, 行为={}", userId, action); log.info("用户行为: 用户ID={}, 行为={}", userId, action);

View File

@@ -0,0 +1,571 @@
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fn" uri="http://flashsale.org/functions" %>
<c:set var="pageTitle" value="订单详情"/>
<%@ include file="common/header.jsp" %>
<div class="container my-4">
<div class="row">
<div class="col-12">
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="${pageContext.request.contextPath}/">首页</a></li>
<li class="breadcrumb-item"><a href="${pageContext.request.contextPath}/orders">我的订单</a></li>
<li class="breadcrumb-item active">订单详情</li>
</ol>
</nav>
</div>
</div>
<!-- 加载中状态 -->
<div id="loadingOrder" class="text-center py-5">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">加载中...</span>
</div>
<p class="text-muted mt-2">正在加载订单详情...</p>
</div>
<!-- 订单详情内容 -->
<div id="orderDetail" style="display: none;">
<!-- 订单状态和基本信息 -->
<div class="row mb-4">
<div class="col-12">
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<h5 class="mb-0">
<i class="fas fa-receipt text-primary"></i> 订单信息
</h5>
<div id="orderActions">
<!-- 订单操作按钮将动态生成 -->
</div>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6">
<table class="table table-borderless">
<tr>
<td class="text-muted" style="width: 120px;">订单号:</td>
<td id="orderNo">-</td>
</tr>
<tr>
<td class="text-muted">订单状态:</td>
<td><span id="orderStatus" class="badge">-</span></td>
</tr>
<tr>
<td class="text-muted">订单类型:</td>
<td id="orderType">-</td>
</tr>
<tr>
<td class="text-muted">下单时间:</td>
<td id="createdAt">-</td>
</tr>
</table>
</div>
<div class="col-md-6">
<table class="table table-borderless">
<tr>
<td class="text-muted" style="width: 120px;">商品数量:</td>
<td id="quantity">-</td>
</tr>
<tr>
<td class="text-muted">订单金额:</td>
<td class="text-danger fw-bold fs-5" id="totalPrice">¥0.00</td>
</tr>
<tr>
<td class="text-muted">更新时间:</td>
<td id="updatedAt">-</td>
</tr>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 商品信息 -->
<div class="row mb-4">
<div class="col-12">
<div class="card">
<div class="card-header">
<h5 class="mb-0">
<i class="fas fa-box text-success"></i> 商品信息
</h5>
</div>
<div class="card-body" id="productInfo">
<!-- 商品信息将动态生成 -->
</div>
</div>
</div>
</div>
<!-- 订单操作历史 -->
<div class="row mb-4">
<div class="col-12">
<div class="card">
<div class="card-header">
<h5 class="mb-0">
<i class="fas fa-history text-info"></i> 订单状态记录
</h5>
</div>
<div class="card-body">
<div id="statusHistory">
<!-- 状态历史将动态生成 -->
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 错误状态 -->
<div id="errorState" class="text-center py-5" style="display: none;">
<i class="fas fa-exclamation-triangle fa-4x text-warning mb-3"></i>
<h4 class="text-muted">订单信息加载失败</h4>
<p class="text-muted" id="errorMessage">请稍后重试或联系客服</p>
<a href="${pageContext.request.contextPath}/orders" class="btn btn-primary">
<i class="fas fa-arrow-left"></i> 返回订单列表
</a>
</div>
</div>
<!-- 支付确认模态框 -->
<div class="modal fade" id="paymentModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">确认支付</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div class="text-center mb-3">
<i class="fas fa-credit-card fa-3x text-primary"></i>
</div>
<p class="text-center">确定要支付此订单吗?</p>
<div class="alert alert-info">
<small>
<i class="fas fa-info-circle"></i>
这是模拟支付99%概率成功
</small>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
<button type="button" class="btn btn-primary" onclick="processPayment()">
<i class="fas fa-credit-card"></i> 确认支付
</button>
</div>
</div>
</div>
</div>
<script>
const orderId = ${orderId};
let currentOrder = null;
$(document).ready(function () {
loadOrderDetail();
});
// 加载订单详情
function loadOrderDetail() {
$('#loadingOrder').show();
$('#orderDetail').hide();
$('#errorState').hide();
console.log('Loading order detail for orderId:', orderId);
$.ajax({
url: '${pageContext.request.contextPath}/api/order/' + orderId,
type: 'GET',
success: function (response) {
console.log('Order detail response:', response);
if (response.success) {
currentOrder = response.data;
console.log('Current order data:', currentOrder);
renderOrderDetail(currentOrder);
$('#orderDetail').show();
} else {
console.error('Failed to get order:', response.message);
showError(response.message || '订单信息获取失败');
}
},
error: function (xhr, status, error) {
console.error('Error loading order:', xhr.status, xhr.responseText);
let errorMessage = '网络错误,请稍后重试';
if (xhr.status === 404) {
errorMessage = '订单不存在或已被删除';
} else if (xhr.status === 401) {
errorMessage = '请先登录';
setTimeout(() => {
window.location.href = '${pageContext.request.contextPath}/login?returnUrl=' + encodeURIComponent(window.location.pathname);
}, 1500);
} else if (xhr.status === 403) {
errorMessage = '无权限查看此订单';
}
showError(errorMessage);
},
complete: function () {
$('#loadingOrder').hide();
}
});
}
// 渲染订单详情
function renderOrderDetail(order) {
if (!order) {
console.error('Order data is null or undefined');
showError('订单数据为空');
return;
}
console.log('Rendering order detail:', order);
// 基本信息
$('#orderNo').text('#' + (order.id || 'unknown'));
$('#quantity').text((order.quantity || 0) + ' 件');
$('#totalPrice').text('¥' + (order.totalPrice ? parseFloat(order.totalPrice).toFixed(2) : '0.00'));
$('#createdAt').text(formatDateTime(order.createdAt));
$('#updatedAt').text(formatDateTime(order.updatedAt));
// 订单状态
const statusInfo = getStatusInfo(order.status || 0);
$('#orderStatus').removeClass().addClass('badge ' + statusInfo.class).text(statusInfo.text);
// 订单类型
const typeText = (order.orderType === 2) ? '秒杀订单' : '普通订单';
$('#orderType').text(typeText);
// 商品信息
renderProductInfo(order);
// 订单操作按钮
renderOrderActions(order);
// 状态历史
renderStatusHistory(order);
}
// 渲染商品信息
function renderProductInfo(order) {
// 计算单价
const totalPrice = order.totalPrice ? parseFloat(order.totalPrice) : 0;
const quantity = order.quantity || 1;
const unitPrice = totalPrice / quantity;
const productName = order.productName || '未知商品';
const productId = order.productId || '-';
const productImageUrl = order.productImageUrl || '${pageContext.request.contextPath}/images/default-product.svg';
const isFlashSale = order.orderType === 2;
const productHtml = `
<div class="row align-items-center">
<div class="col-md-2">
<img src="` + productImageUrl + `"
class="img-fluid rounded" alt="` + productName + `"
onerror="this.src='${pageContext.request.contextPath}/images/default-product.svg'; this.onerror=null;"
style="max-height: 100px; object-fit: cover;">
</div>
<div class="col-md-6">
<h6 class="mb-1">` + productName + `</h6>
<p class="text-muted small mb-0">商品ID: ` + productId + `</p>
` + (isFlashSale ? '<span class="badge bg-danger small">秒杀商品</span>' : '<span class="badge bg-primary small">普通商品</span>') + `
</div>
<div class="col-md-2 text-center">
<span class="text-primary fw-bold">¥` + unitPrice.toFixed(2) + `</span>
<br><small class="text-muted">单价</small>
</div>
<div class="col-md-2 text-center">
<span class="badge bg-light text-dark fs-6">× ` + quantity + `</span>
<br><small class="text-muted">数量</small>
</div>
</div>
`;
$('#productInfo').html(productHtml);
}
// 渲染订单操作按钮
function renderOrderActions(order) {
let actionsHtml = '';
switch (order.status) {
case 1: // 待支付
actionsHtml = `
<button class="btn btn-primary btn-sm me-2" onclick="showPaymentModal()">
<i class="fas fa-credit-card"></i> 立即支付
</button>
<button class="btn btn-outline-danger btn-sm" onclick="cancelOrder()">
<i class="fas fa-times"></i> 取消订单
</button>
`;
break;
case 2: // 已支付
actionsHtml = `
<button class="btn btn-info btn-sm" onclick="confirmReceipt()" disabled>
<i class="fas fa-truck"></i> 等待发货
</button>
`;
break;
case 3: // 已发货
actionsHtml = `
<button class="btn btn-success btn-sm" onclick="confirmReceipt()">
<i class="fas fa-check"></i> 确认收货
</button>
`;
break;
case 4: // 已完成
actionsHtml = `
<span class="text-success">
<i class="fas fa-check-circle"></i> 订单已完成
</span>
`;
break;
case 5: // 已取消
actionsHtml = `
<span class="text-muted">
<i class="fas fa-ban"></i> 订单已取消
</span>
`;
break;
}
$('#orderActions').html(actionsHtml);
}
// 渲染状态历史
function renderStatusHistory(order) {
const statusSteps = [
{status: 1, text: '订单创建', icon: 'fas fa-plus-circle', time: order.createdAt},
{status: 2, text: '支付完成', icon: 'fas fa-credit-card'},
{status: 3, text: '商品发货', icon: 'fas fa-truck'},
{status: 4, text: '订单完成', icon: 'fas fa-check-circle'}
];
let historyHtml = '<div class="timeline">';
statusSteps.forEach((step, index) => {
const isActive = order.status >= step.status;
const isCurrent = order.status === step.status;
const statusClass = isActive ? 'text-success' : 'text-muted';
historyHtml += `
<div class="timeline-item ` + (isActive ? 'active' : '') + `">
<div class="timeline-marker">
<i class="` + step.icon + ` ` + statusClass + `"></i>
</div>
<div class="timeline-content">
<h6 class="` + statusClass + `">` + step.text + `</h6>
` + (step.time ? `<small class="text-muted">` + formatDateTime(step.time) + `</small>` : '') + `
</div>
</div>
`;
});
historyHtml += '</div>';
$('#statusHistory').html(historyHtml);
}
// 显示支付模态框
function showPaymentModal() {
$('#paymentModal').modal('show');
}
// 处理支付
function processPayment() {
$('#paymentModal').modal('hide');
const payBtn = $('button[onclick="processPayment()"]');
const originalText = payBtn.html();
payBtn.prop('disabled', true).html('<i class="fas fa-spinner fa-spin"></i> 支付中...');
$.ajax({
url: '${pageContext.request.contextPath}/api/order/' + orderId + '/pay',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({}),
success: function (response) {
if (response.success) {
showMessage('💳 支付成功!', 'success');
setTimeout(() => {
loadOrderDetail(); // 重新加载订单详情
}, 1000);
} else {
showMessage('❌ 支付失败:' + response.message, 'error');
}
},
error: function () {
showMessage('❌ 支付失败,请重试', 'error');
},
complete: function () {
payBtn.prop('disabled', false).html(originalText);
}
});
}
// 取消订单
function cancelOrder() {
if (!confirm('确定要取消这个订单吗?\n\n取消后无法恢复。')) {
return;
}
$.ajax({
url: '${pageContext.request.contextPath}/api/order/' + orderId + '/cancel',
type: 'POST',
success: function (response) {
if (response.success) {
showMessage('订单已取消', 'success');
setTimeout(() => {
loadOrderDetail(); // 重新加载订单详情
}, 1000);
} else {
showMessage('取消失败:' + response.message, 'error');
}
},
error: function () {
showMessage('取消失败,请重试', 'error');
}
});
}
// 确认收货
function confirmReceipt() {
if (!confirm('确定已收到商品吗?\n\n确认后订单将标记为完成。')) {
return;
}
$.ajax({
url: '${pageContext.request.contextPath}/api/order/' + orderId + '/confirm',
type: 'POST',
success: function (response) {
if (response.success) {
showMessage('✅ 确认收货成功!', 'success');
setTimeout(() => {
loadOrderDetail(); // 重新加载订单详情
}, 1000);
} else {
showMessage('确认失败:' + response.message, 'error');
}
},
error: function () {
showMessage('确认失败,请重试', 'error');
}
});
}
// 获取状态信息
function getStatusInfo(status) {
switch (status) {
case 1:
return {text: '待支付', class: 'bg-warning'};
case 2:
return {text: '已支付', class: 'bg-info'};
case 3:
return {text: '已发货', class: 'bg-primary'};
case 4:
return {text: '已完成', class: 'bg-success'};
case 5:
return {text: '已取消', class: 'bg-secondary'};
default:
return {text: '未知状态', class: 'bg-secondary'};
}
}
// 格式化日期时间
function formatDateTime(dateTimeStr) {
if (!dateTimeStr) return '-';
const date = new Date(dateTimeStr);
return date.toLocaleString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit'
});
}
// 显示错误
function showError(message) {
$('#errorMessage').text(message);
$('#errorState').show();
}
// 显示消息
function showMessage(message, type = 'info') {
const alertDiv = document.createElement('div');
alertDiv.className = 'alert alert-' + (type == 'error' ? 'danger' : type) + ' alert-dismissible fade show position-fixed';
alertDiv.style.cssText = 'top: 20px; right: 20px; z-index: 9999; min-width: 300px;';
alertDiv.innerHTML = message + '<button type="button" class="btn-close" data-bs-dismiss="alert"></button>';
document.body.appendChild(alertDiv);
// 3秒后自动消失
setTimeout(() => {
if (alertDiv.parentNode) {
alertDiv.remove();
}
}, 3000);
}
</script>
<style>
.timeline {
position: relative;
padding-left: 30px;
}
.timeline-item {
position: relative;
padding-bottom: 20px;
}
.timeline-item:not(:last-child)::before {
content: '';
position: absolute;
left: -22px;
top: 20px;
bottom: -20px;
width: 2px;
background: #dee2e6;
}
.timeline-item.active:not(:last-child)::before {
background: #28a745;
}
.timeline-marker {
position: absolute;
left: -30px;
top: 0;
width: 16px;
height: 16px;
background: #fff;
border: 2px solid #dee2e6;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.timeline-item.active .timeline-marker {
border-color: #28a745;
background: #28a745;
color: white;
}
.timeline-content {
margin-left: 10px;
}
.card {
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
border: none;
}
.card-header {
background: #f8f9fa;
border-bottom: 1px solid #dee2e6;
}
</style>
<%@ include file="common/footer.jsp" %>

View File

@@ -494,11 +494,142 @@
// 支付订单 // 支付订单
function payOrder(orderId) { function payOrder(orderId) {
if (confirm('确定要支付这个订单吗?')) { if (confirm('确定要支付这个订单吗?')) {
// 这里可以集成真实的支付接口 // 显示支付方式选择模态框
showMessage('支付功能开发中...', 'info'); showPaymentModal(orderId);
} }
} }
// 显示支付方式选择模态框
function showPaymentModal(orderId) {
const modalHtml = `
<div class="modal fade" id="paymentModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">选择支付方式</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div class="payment-methods">
<div class="form-check mb-3" data-payment-method="alipay">
<input class="form-check-input" type="radio" name="paymentMethod" id="alipay" value="alipay">
<label class="form-check-label" for="alipay">
<i class="fab fa-alipay text-primary"></i> 支付宝
</label>
</div>
<div class="form-check mb-3" data-payment-method="wechat">
<input class="form-check-input" type="radio" name="paymentMethod" id="wechat" value="wechat">
<label class="form-check-label" for="wechat">
<i class="fab fa-weixin text-success"></i> 微信支付
</label>
</div>
<div class="form-check mb-3" data-payment-method="unionpay">
<input class="form-check-input" type="radio" name="paymentMethod" id="unionpay" value="unionpay">
<label class="form-check-label" for="unionpay">
<i class="fas fa-credit-card text-info"></i> 银联支付
</label>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
<div id="paymentStatus" class="text-muted me-auto">
<small>选择支付方式后将自动处理支付</small>
</div>
</div>
</div>
</div>
</div>
`;
// 移除现有的支付模态框
$('#paymentModal').remove();
// 添加新的支付模态框
$('body').append(modalHtml);
// 设置订单ID到模态框数据属性中
$('#paymentModal').attr('data-order-id', orderId);
// 添加支付方式点击事件
$('#paymentModal').on('click', '.form-check[data-payment-method]', function () {
const paymentMethod = $(this).attr('data-payment-method');
const currentOrderId = $('#paymentModal').attr('data-order-id');
selectPaymentMethod(paymentMethod, currentOrderId);
});
// 在模态框关闭时移除事件监听器
$('#paymentModal').on('hidden.bs.modal', function () {
$(this).off('click');
$(this).remove();
});
// 显示模态框
$('#paymentModal').modal('show');
}
// 选择支付方式并直接处理支付
function selectPaymentMethod(paymentMethod, orderId) {
// 如果orderId未定义从模态框数据属性中获取
if (!orderId || orderId === 'undefined') {
orderId = $('#paymentModal').attr('data-order-id');
}
// 验证orderId
if (!orderId || orderId === 'undefined') {
showMessage('订单ID错误请重新尝试', 'error');
return;
}
console.log('Processing payment for order:', orderId, 'with method:', paymentMethod);
// 选中对应的单选框
$('#paymentModal input[value="' + paymentMethod + '"]').prop('checked', true);
// 显示支付处理中状态
$('#paymentStatus').html('<i class="fas fa-spinner fa-spin text-primary"></i> <small class="text-primary">正在处理支付...</small>');
// 禁用所有支付方式选项
$('.payment-methods .form-check').css('pointer-events', 'none').css('opacity', '0.6');
// 模拟短暂延迟后调用后端支付接口
setTimeout(function () {
$.ajax({
url: '${pageContext.request.contextPath}/api/order/' + orderId + '/pay',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({
paymentMethod: paymentMethod
}),
success: function (response) {
if (response.success) {
$('#paymentStatus').html('<i class="fas fa-check-circle text-success"></i> <small class="text-success">支付成功!</small>');
// 2秒后关闭模态框并刷新订单列表
setTimeout(function () {
$('#paymentModal').modal('hide');
showMessage('支付成功!订单状态已更新为已支付', 'success');
refreshOrders();
}, 2000);
} else {
$('#paymentStatus').html('<i class="fas fa-times-circle text-danger"></i> <small class="text-danger">支付失败: ' + response.message + '</small>');
// 恢复支付方式选项
$('.payment-methods .form-check').css('pointer-events', 'auto').css('opacity', '1');
}
},
error: function (xhr, status, error) {
let errorMessage = '支付失败,请稍后重试';
if (xhr.responseJSON && xhr.responseJSON.message) {
errorMessage = xhr.responseJSON.message;
}
$('#paymentStatus').html('<i class="fas fa-times-circle text-danger"></i> <small class="text-danger">' + errorMessage + '</small>');
// 恢复支付方式选项
$('.payment-methods .form-check').css('pointer-events', 'auto').css('opacity', '1');
}
});
}, 1000); // 1秒延迟模拟支付处理
}
// 取消订单 // 取消订单
function cancelOrder(orderId) { function cancelOrder(orderId) {
if (confirm('确定要取消这个订单吗?')) { if (confirm('确定要取消这个订单吗?')) {
@@ -626,6 +757,65 @@
font-weight: 500; font-weight: 500;
width: 30%; width: 30%;
} }
/* 支付方式样式 */
.payment-methods .form-check {
border: 2px solid #dee2e6;
border-radius: 0.5rem;
padding: 1rem;
transition: all 0.3s ease;
cursor: pointer;
position: relative;
background: #fff;
}
.payment-methods .form-check:hover {
border-color: #0d6efd;
box-shadow: 0 4px 12px rgba(13, 110, 253, 0.15);
transform: translateY(-2px);
}
.payment-methods .form-check-input {
position: absolute;
top: 1rem;
right: 1rem;
transform: scale(1.2);
}
.payment-methods .form-check-input:checked + .form-check-label {
color: #0d6efd;
font-weight: 600;
}
.payment-methods .form-check-input:checked {
background-color: #0d6efd;
border-color: #0d6efd;
}
.payment-methods .form-check:has(.form-check-input:checked) {
border-color: #0d6efd;
background-color: rgba(13, 110, 253, 0.05);
}
.payment-methods .form-check-label {
cursor: pointer;
font-size: 1.1rem;
display: block;
margin-bottom: 0;
padding-right: 2.5rem;
}
.payment-methods .form-check-label i {
font-size: 1.3rem;
margin-right: 0.5rem;
vertical-align: middle;
}
#paymentStatus {
display: flex;
align-items: center;
gap: 0.5rem;
}
</style> </style>
<%@ include file="common/footer.jsp" %> <%@ include file="common/footer.jsp" %>