后台完成修复,初始化项目
This commit is contained in:
472
src/main/webapp/WEB-INF/views/index.jsp
Normal file
472
src/main/webapp/WEB-INF/views/index.jsp
Normal file
@@ -0,0 +1,472 @@
|
||||
<%@ 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 id="heroCarousel" class="carousel slide" data-bs-ride="carousel">
|
||||
<div class="carousel-indicators">
|
||||
<button type="button" data-bs-target="#heroCarousel" data-bs-slide-to="0" class="active"></button>
|
||||
<button type="button" data-bs-target="#heroCarousel" data-bs-slide-to="1"></button>
|
||||
<button type="button" data-bs-target="#heroCarousel" data-bs-slide-to="2"></button>
|
||||
</div>
|
||||
<div class="carousel-inner">
|
||||
<div class="carousel-item active">
|
||||
<div class="bg-gradient-danger text-white py-5" style="min-height: 400px;">
|
||||
<div class="container d-flex align-items-center h-100">
|
||||
<div class="row w-100">
|
||||
<div class="col-md-6">
|
||||
<h1 class="display-4 fw-bold mb-4">
|
||||
<i class="fas fa-bolt"></i> 秒杀系统
|
||||
</h1>
|
||||
<p class="lead mb-4">基于Redis集群构建的高并发秒杀系统,支持分布式锁、接口限流、库存预热等核心功能。</p>
|
||||
<div class="d-flex gap-3">
|
||||
<a href="${pageContext.request.contextPath}/flashsales" class="btn btn-light btn-lg">
|
||||
<i class="fas fa-fire"></i> 立即抢购
|
||||
</a>
|
||||
<a href="${pageContext.request.contextPath}/products" class="btn btn-outline-light btn-lg">
|
||||
<i class="fas fa-shopping-bag"></i> 浏览商品
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 text-center">
|
||||
<i class="fas fa-rocket fa-10x opacity-50"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="carousel-item">
|
||||
<div class="bg-gradient-primary text-white py-5" style="min-height: 400px;">
|
||||
<div class="container d-flex align-items-center h-100">
|
||||
<div class="row w-100">
|
||||
<div class="col-md-6">
|
||||
<h1 class="display-4 fw-bold mb-4">
|
||||
<i class="fas fa-shield-alt"></i> 防超卖机制
|
||||
</h1>
|
||||
<p class="lead mb-4">采用Redis分布式锁和Lua脚本,确保高并发场景下的数据一致性,彻底解决超卖问题。</p>
|
||||
<a href="#features" class="btn btn-light btn-lg">
|
||||
<i class="fas fa-info-circle"></i> 了解更多
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-md-6 text-center">
|
||||
<i class="fas fa-lock fa-10x opacity-50"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="carousel-item">
|
||||
<div class="bg-gradient-success text-white py-5" style="min-height: 400px;">
|
||||
<div class="container d-flex align-items-center h-100">
|
||||
<div class="row w-100">
|
||||
<div class="col-md-6">
|
||||
<h1 class="display-4 fw-bold mb-4">
|
||||
<i class="fas fa-tachometer-alt"></i> 高性能缓存
|
||||
</h1>
|
||||
<p class="lead mb-4">Redis集群架构,支持五种数据类型应用,实现毫秒级响应,轻松应对高并发访问。</p>
|
||||
<a href="#performance" class="btn btn-light btn-lg">
|
||||
<i class="fas fa-chart-line"></i> 性能指标
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-md-6 text-center">
|
||||
<i class="fas fa-database fa-10x opacity-50"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button class="carousel-control-prev" type="button" data-bs-target="#heroCarousel" data-bs-slide="prev">
|
||||
<span class="carousel-control-prev-icon"></span>
|
||||
</button>
|
||||
<button class="carousel-control-next" type="button" data-bs-target="#heroCarousel" data-bs-slide="next">
|
||||
<span class="carousel-control-next-icon"></span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="container my-5">
|
||||
<!-- 正在进行的秒杀活动 -->
|
||||
<section class="mb-5">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h2 class="fw-bold">
|
||||
<i class="fas fa-fire text-danger"></i> 正在秒杀
|
||||
</h2>
|
||||
<a href="${pageContext.request.contextPath}/flashsales" class="btn btn-outline-danger">
|
||||
查看全部 <i class="fas fa-arrow-right"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="activeFlashSales" class="row">
|
||||
<!-- 动态加载秒杀活动 -->
|
||||
<div class="col-12 text-center py-5">
|
||||
<i class="fas fa-spinner fa-spin fa-2x text-muted"></i>
|
||||
<p class="text-muted mt-2">加载中...</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 热门商品 -->
|
||||
<section class="mb-5">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h2 class="fw-bold">
|
||||
<i class="fas fa-star text-warning"></i> 热门商品
|
||||
</h2>
|
||||
<a href="${pageContext.request.contextPath}/products" class="btn btn-outline-primary">
|
||||
查看全部 <i class="fas fa-arrow-right"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="hotProducts" class="row">
|
||||
<!-- 动态加载热门商品 -->
|
||||
<div class="col-12 text-center py-5">
|
||||
<i class="fas fa-spinner fa-spin fa-2x text-muted"></i>
|
||||
<p class="text-muted mt-2">加载中...</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 系统特性 -->
|
||||
<section id="features" class="mb-5">
|
||||
<h2 class="text-center fw-bold mb-5">
|
||||
<i class="fas fa-cogs"></i> 系统特性
|
||||
</h2>
|
||||
|
||||
<div class="row g-4">
|
||||
<div class="col-md-3 col-sm-6">
|
||||
<div class="card h-100 text-center border-0 shadow-sm">
|
||||
<div class="card-body">
|
||||
<i class="fas fa-bolt fa-3x text-danger mb-3"></i>
|
||||
<h5 class="card-title">秒杀抢购</h5>
|
||||
<p class="card-text text-muted">高并发秒杀系统,支持大量用户同时抢购</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3 col-sm-6">
|
||||
<div class="card h-100 text-center border-0 shadow-sm">
|
||||
<div class="card-body">
|
||||
<i class="fas fa-shield-alt fa-3x text-success mb-3"></i>
|
||||
<h5 class="card-title">防超卖</h5>
|
||||
<p class="card-text text-muted">分布式锁机制,确保库存数据一致性</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3 col-sm-6">
|
||||
<div class="card h-100 text-center border-0 shadow-sm">
|
||||
<div class="card-body">
|
||||
<i class="fas fa-database fa-3x text-info mb-3"></i>
|
||||
<h5 class="card-title">Redis缓存</h5>
|
||||
<p class="card-text text-muted">五种数据类型应用,毫秒级响应</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3 col-sm-6">
|
||||
<div class="card h-100 text-center border-0 shadow-sm">
|
||||
<div class="card-body">
|
||||
<i class="fas fa-tachometer-alt fa-3x text-warning mb-3"></i>
|
||||
<h5 class="card-title">接口限流</h5>
|
||||
<p class="card-text text-muted">多种限流策略,防止恶意刷单</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 性能指标 -->
|
||||
<section id="performance" class="mb-5">
|
||||
<h2 class="text-center fw-bold mb-5">
|
||||
<i class="fas fa-chart-line"></i> 性能指标
|
||||
</h2>
|
||||
|
||||
<div class="row g-4">
|
||||
<div class="col-md-3 col-sm-6">
|
||||
<div class="card border-primary">
|
||||
<div class="card-body text-center">
|
||||
<h3 class="text-primary fw-bold" id="qpsCounter">10000+</h3>
|
||||
<p class="card-text">QPS并发处理</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3 col-sm-6">
|
||||
<div class="card border-success">
|
||||
<div class="card-body text-center">
|
||||
<h3 class="text-success fw-bold" id="responseTime"><100ms</h3>
|
||||
<p class="card-text">平均响应时间</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3 col-sm-6">
|
||||
<div class="card border-warning">
|
||||
<div class="card-body text-center">
|
||||
<h3 class="text-warning fw-bold" id="successRate">99.9%</h3>
|
||||
<p class="card-text">系统可用性</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3 col-sm-6">
|
||||
<div class="card border-info">
|
||||
<div class="card-body text-center">
|
||||
<h3 class="text-info fw-bold" id="concurrentUsers">50000+</h3>
|
||||
<p class="card-text">并发用户支持</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
// 加载正在进行的秒杀活动
|
||||
loadActiveFlashSales();
|
||||
|
||||
// 加载热门商品
|
||||
loadHotProducts();
|
||||
|
||||
// 启动性能指标动画
|
||||
animateCounters();
|
||||
});
|
||||
|
||||
// 加载正在进行的秒杀活动
|
||||
function loadActiveFlashSales() {
|
||||
$.get('${pageContext.request.contextPath}/api/flashsale/active')
|
||||
.done(function(response) {
|
||||
if (response.success && response.data.length > 0) {
|
||||
renderFlashSales(response.data.slice(0, 4)); // 只显示前4个
|
||||
} else {
|
||||
$('#activeFlashSales').html(`
|
||||
<div class="col-12 text-center py-5">
|
||||
<i class="fas fa-info-circle fa-2x text-muted"></i>
|
||||
<p class="text-muted mt-2">暂无进行中的秒杀活动</p>
|
||||
</div>
|
||||
`);
|
||||
}
|
||||
})
|
||||
.fail(function() {
|
||||
$('#activeFlashSales').html(`
|
||||
<div class="col-12 text-center py-5">
|
||||
<i class="fas fa-exclamation-triangle fa-2x text-warning"></i>
|
||||
<p class="text-muted mt-2">加载失败,请刷新页面重试</p>
|
||||
</div>
|
||||
`);
|
||||
});
|
||||
}
|
||||
|
||||
// 渲染秒杀活动
|
||||
function renderFlashSales(flashSales) {
|
||||
let html = '';
|
||||
|
||||
flashSales.forEach(function(flashSale) {
|
||||
const discountPercent = Math.round((1 - flashSale.flashPrice / flashSale.originalPrice) * 100);
|
||||
|
||||
html += `
|
||||
<div class="col-lg-3 col-md-6 mb-4">
|
||||
<div class="card h-100 border-danger">
|
||||
<div class="position-relative">
|
||||
<img src="` + (flashSale.productImageUrl || '${pageContext.request.contextPath}/images/default-product.svg') + `"
|
||||
class="card-img-top" alt="` + flashSale.productName + `" style="height: 200px; object-fit: cover;"
|
||||
onerror="this.src='${pageContext.request.contextPath}/images/default-product.svg'; this.onerror=null;">
|
||||
<div class="position-absolute top-0 start-0 bg-danger text-white px-2 py-1 rounded-end">
|
||||
<small><i class="fas fa-fire"></i> 秒杀中</small>
|
||||
</div>
|
||||
<div class="position-absolute top-0 end-0 bg-warning text-dark px-2 py-1 rounded-start">
|
||||
<small>${discountPercent}% OFF</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h6 class="card-title text-truncate">` + flashSale.productName + `</h6>
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<div>
|
||||
<span class="text-danger fw-bold fs-5">¥` + (flashSale.flashPrice ? flashSale.flashPrice.toFixed(2) : '0.00') + `</span>
|
||||
<small class="text-muted text-decoration-line-through ms-2">¥` + (flashSale.originalPrice ? flashSale.originalPrice.toFixed(2) : '0.00') + `</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<small class="text-muted">剩余: ` + (flashSale.remainingStock || 0) + `件</small>
|
||||
<div class="progress" style="height: 4px;">
|
||||
<div class="progress-bar bg-danger" style="width: ` + ((flashSale.remainingStock || 0) / (flashSale.flashStock || 1) * 100) + `%"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<div class="text-danger fw-bold mb-2" id="countdown_${flashSale.id}">
|
||||
计算中...
|
||||
</div>
|
||||
<button class="btn btn-danger btn-sm w-100" onclick="participateFlashSale(${flashSale.id})">
|
||||
<i class="fas fa-bolt"></i> 立即抢购
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// 启动倒计时
|
||||
setTimeout(() => {
|
||||
if (flashSale.timeToEnd > 0) {
|
||||
countdown(Date.now() + flashSale.timeToEnd, 'countdown_' + flashSale.id);
|
||||
}
|
||||
}, 100);
|
||||
});
|
||||
|
||||
$('#activeFlashSales').html(html);
|
||||
}
|
||||
|
||||
// 加载热门商品
|
||||
function loadHotProducts() {
|
||||
$.get('${pageContext.request.contextPath}/api/product/hot?limit=8')
|
||||
.done(function(response) {
|
||||
if (response.success && response.data.length > 0) {
|
||||
renderHotProducts(response.data);
|
||||
} else {
|
||||
$('#hotProducts').html(`
|
||||
<div class="col-12 text-center py-5">
|
||||
<i class="fas fa-info-circle fa-2x text-muted"></i>
|
||||
<p class="text-muted mt-2">暂无热门商品</p>
|
||||
</div>
|
||||
`);
|
||||
}
|
||||
})
|
||||
.fail(function() {
|
||||
$('#hotProducts').html(`
|
||||
<div class="col-12 text-center py-5">
|
||||
<i class="fas fa-exclamation-triangle fa-2x text-warning"></i>
|
||||
<p class="text-muted mt-2">加载失败,请刷新页面重试</p>
|
||||
</div>
|
||||
`);
|
||||
});
|
||||
}
|
||||
|
||||
// 渲染热门商品
|
||||
function renderHotProducts(products) {
|
||||
let html = '';
|
||||
|
||||
products.forEach(function(product) {
|
||||
html += `
|
||||
<div class="col-lg-3 col-md-6 mb-4">
|
||||
<div class="card h-100">
|
||||
<img src="${product.imageUrl || '${pageContext.request.contextPath}/images/default-product.svg'}"
|
||||
class="card-img-top" alt="${product.name}" style="height: 200px; object-fit: cover;"
|
||||
onerror="this.src='${pageContext.request.contextPath}/images/default-product.svg'; this.onerror=null;">
|
||||
<div class="card-body">
|
||||
<h6 class="card-title text-truncate">` + product.name + `</h6>
|
||||
<p class="card-text text-muted small text-truncate">` + (product.description || '暂无描述') + `</p>
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<span class="text-primary fw-bold">¥` + (product.price ? product.price.toFixed(2) : '0.00') + `</span>
|
||||
<small class="text-muted">库存: ` + (product.stock || 0) + `</small>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<button class="btn btn-primary btn-sm w-100" onclick="addToCart(` + product.id + `)">
|
||||
<i class="fas fa-cart-plus"></i> 加入购物车
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
|
||||
$('#hotProducts').html(html);
|
||||
}
|
||||
|
||||
// 参与秒杀
|
||||
function participateFlashSale(flashSaleId) {
|
||||
<c:choose>
|
||||
<c:when test="${not empty sessionScope.user}">
|
||||
if (confirm('确定要参与这个秒杀活动吗?')) {
|
||||
$.ajax({
|
||||
url: '${pageContext.request.contextPath}/api/flashsale/participate',
|
||||
type: 'POST',
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify({
|
||||
flashSaleId: flashSaleId,
|
||||
quantity: 1
|
||||
}),
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
showMessage('秒杀成功!订单已生成', 'success');
|
||||
setTimeout(() => {
|
||||
window.location.href = '${pageContext.request.contextPath}/orders';
|
||||
}, 2000);
|
||||
} else {
|
||||
showMessage(response.message, 'error');
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
showMessage('秒杀失败,请重试', 'error');
|
||||
}
|
||||
});
|
||||
}
|
||||
</c:when>
|
||||
<c:otherwise>
|
||||
showMessage('请先登录', 'warning');
|
||||
setTimeout(() => {
|
||||
window.location.href = '${pageContext.request.contextPath}/login';
|
||||
}, 1000);
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
}
|
||||
|
||||
// 添加到购物车
|
||||
function addToCart(productId) {
|
||||
<c:choose>
|
||||
<c:when test="${not empty sessionScope.user}">
|
||||
$.ajax({
|
||||
url: '${pageContext.request.contextPath}/api/cart/add',
|
||||
type: 'POST',
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify({
|
||||
productId: productId,
|
||||
quantity: 1
|
||||
}),
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
showMessage('商品已添加到购物车', 'success');
|
||||
updateCartCount();
|
||||
} else {
|
||||
showMessage(response.message, 'error');
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
showMessage('添加失败,请重试', 'error');
|
||||
}
|
||||
});
|
||||
</c:when>
|
||||
<c:otherwise>
|
||||
showMessage('请先登录', 'warning');
|
||||
setTimeout(() => {
|
||||
window.location.href = '${pageContext.request.contextPath}/login';
|
||||
}, 1000);
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
}
|
||||
|
||||
// 性能指标动画
|
||||
function animateCounters() {
|
||||
const counters = [
|
||||
{ id: 'qpsCounter', target: 10000, suffix: '+' },
|
||||
{ id: 'concurrentUsers', target: 50000, suffix: '+' }
|
||||
];
|
||||
|
||||
counters.forEach(counter => {
|
||||
animateCounter(counter.id, counter.target, counter.suffix);
|
||||
});
|
||||
}
|
||||
|
||||
function animateCounter(elementId, target, suffix = '') {
|
||||
const element = document.getElementById(elementId);
|
||||
let current = 0;
|
||||
const increment = target / 100;
|
||||
const timer = setInterval(() => {
|
||||
current += increment;
|
||||
if (current >= target) {
|
||||
current = target;
|
||||
clearInterval(timer);
|
||||
}
|
||||
element.textContent = Math.floor(current).toLocaleString() + suffix;
|
||||
}, 20);
|
||||
}
|
||||
</script>
|
||||
|
||||
<%@ include file="common/footer.jsp" %>
|
||||
Reference in New Issue
Block a user