后端功能增强:全局异常处理、API控制器、JSP视图和单元测试

- 添加 GlobalExceptionHandler 全局异常处理
- 添加 ApiController REST API 控制器
- 更新 WebConfig 跨域配置和 ProductRepository 查询方法
- 新增 monitor/product-detail/profile JSP 视图页面
- 添加 FlashSaleServiceTest 秒杀服务单元测试
- 更新 application.yml 配置
This commit is contained in:
2026-03-05 20:30:48 +08:00
parent 923e877759
commit 989c2741a2
63 changed files with 15508 additions and 1 deletions

View File

@@ -0,0 +1,260 @@
<template>
<div class="home-page">
<!-- 轮播图 -->
<el-carousel height="400px" :interval="5000" arrow="hover">
<el-carousel-item v-for="item in banners" :key="item.id">
<div class="banner-content" :style="{ background: item.bgColor }">
<div class="container mx-auto px-4 h-full">
<div class="flex items-center h-full">
<div class="w-1/2">
<h1 class="text-4xl font-bold text-white mb-4">
<el-icon :size="40"><Lightning /></el-icon>
{{ item.title }}
</h1>
<p class="text-xl text-white mb-6">{{ item.subtitle }}</p>
<div class="space-x-4">
<el-button size="large" type="primary" @click="router.push(item.link)">
{{ item.buttonText }}
</el-button>
<el-button size="large" @click="router.push('/products')">
浏览商品
</el-button>
</div>
</div>
<div class="w-1/2 text-center">
<el-icon :size="200" class="text-white opacity-50">
<component :is="item.icon" />
</el-icon>
</div>
</div>
</div>
</div>
</el-carousel-item>
</el-carousel>
<div class="container mx-auto px-4 py-8">
<!-- 正在秒杀 -->
<section class="mb-12">
<div class="flex justify-between items-center mb-6">
<h2 class="text-2xl font-bold flex items-center">
<el-icon class="text-red-500 mr-2"><Lightning /></el-icon>
正在秒杀
</h2>
<el-button text @click="router.push('/flashsale')">
查看全部
<el-icon class="ml-1"><ArrowRight /></el-icon>
</el-button>
</div>
<div v-if="loadingFlashSales" class="text-center py-8">
<el-icon :size="40" class="animate-spin"><Loading /></el-icon>
<p class="mt-2 text-gray-500">加载中...</p>
</div>
<div v-else-if="activeFlashSales.length === 0" class="text-center py-8">
<el-empty description="暂无进行中的秒杀活动" />
</div>
<div v-else class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
<FlashSaleCard
v-for="item in activeFlashSales"
:key="item.id"
:data="item"
@participate="handleParticipate"
/>
</div>
</section>
<!-- 热门商品 -->
<section class="mb-12">
<div class="flex justify-between items-center mb-6">
<h2 class="text-2xl font-bold flex items-center">
<el-icon class="text-orange-500 mr-2"><Star /></el-icon>
热门商品
</h2>
<el-button text @click="router.push('/products')">
查看全部
<el-icon class="ml-1"><ArrowRight /></el-icon>
</el-button>
</div>
<div v-if="loadingProducts" class="text-center py-8">
<el-icon :size="40" class="animate-spin"><Loading /></el-icon>
<p class="mt-2 text-gray-500">加载中...</p>
</div>
<div v-else-if="hotProducts.length === 0" class="text-center py-8">
<el-empty description="暂无热门商品" />
</div>
<div v-else class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
<ProductCard
v-for="item in hotProducts"
:key="item.id"
:data="item"
@add-to-cart="handleAddToCart"
/>
</div>
</section>
<!-- 系统特性 -->
<section class="mb-12">
<h2 class="text-2xl font-bold text-center mb-8">系统特性</h2>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
<div class="feature-card">
<el-icon :size="40" class="text-red-500 mb-4"><Lightning /></el-icon>
<h3 class="text-lg font-semibold mb-2">秒杀抢购</h3>
<p class="text-gray-600">高并发秒杀系统支持大量用户同时抢购</p>
</div>
<div class="feature-card">
<el-icon :size="40" class="text-green-500 mb-4"><Lock /></el-icon>
<h3 class="text-lg font-semibold mb-2">防超卖</h3>
<p class="text-gray-600">分布式锁机制确保库存数据一致性</p>
</div>
<div class="feature-card">
<el-icon :size="40" class="text-blue-500 mb-4"><Coin /></el-icon>
<h3 class="text-lg font-semibold mb-2">Redis缓存</h3>
<p class="text-gray-600">五种数据类型应用毫秒级响应</p>
</div>
<div class="feature-card">
<el-icon :size="40" class="text-orange-500 mb-4"><Speedometer /></el-icon>
<h3 class="text-lg font-semibold mb-2">接口限流</h3>
<p class="text-gray-600">多种限流策略防止恶意刷单</p>
</div>
</div>
</section>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
import FlashSaleCard from '@/components/business/FlashSaleCard.vue'
import ProductCard from '@/components/business/ProductCard.vue'
import { flashsaleApi } from '@/api/modules/flashsale'
import { productApi } from '@/api/modules/product'
import { useCartStore } from '@/stores/cart'
import { useUserStore } from '@/stores/user'
import type { FlashSale, Product } from '@/types/api'
const router = useRouter()
const cartStore = useCartStore()
const userStore = useUserStore()
// 轮播图数据
const banners = [
{
id: 1,
title: '秒杀系统',
subtitle: '基于Redis集群构建的高并发秒杀系统',
buttonText: '立即抢购',
link: '/flashsale',
bgColor: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
icon: 'Lightning'
},
{
id: 2,
title: '防超卖机制',
subtitle: '采用分布式锁和Lua脚本确保数据一致性',
buttonText: '了解更多',
link: '/flashsale',
bgColor: 'linear-gradient(135deg, #f093fb 0%, #f5576c 100%)',
icon: 'Lock'
},
{
id: 3,
title: '高性能缓存',
subtitle: 'Redis集群架构毫秒级响应',
buttonText: '查看商品',
link: '/products',
bgColor: 'linear-gradient(135deg, #4facfe 0%, #00f2fe 100%)',
icon: 'Speedometer'
}
]
// 数据状态
const loadingFlashSales = ref(false)
const loadingProducts = ref(false)
const activeFlashSales = ref<FlashSale[]>([])
const hotProducts = ref<Product[]>([])
// 加载秒杀活动
const loadFlashSales = async () => {
loadingFlashSales.value = true
try {
const res = await flashsaleApi.getActive(4)
if (res.success) {
activeFlashSales.value = res.data
}
} catch (error) {
console.error('加载秒杀活动失败:', error)
} finally {
loadingFlashSales.value = false
}
}
// 加载热门商品
const loadProducts = async () => {
loadingProducts.value = true
try {
const res = await productApi.getHot(8)
if (res.success) {
hotProducts.value = res.data
}
} catch (error) {
console.error('加载热门商品失败:', error)
} finally {
loadingProducts.value = false
}
}
// 参与秒杀
const handleParticipate = async (flashSaleId: number) => {
if (!userStore.isLoggedIn) {
ElMessage.warning('请先登录')
router.push('/login')
return
}
// 跳转到秒杀详情页
router.push(`/flashsale/${flashSaleId}`)
}
// 添加到购物车
const handleAddToCart = async (productId: number) => {
if (!userStore.isLoggedIn) {
ElMessage.warning('请先登录')
router.push('/login')
return
}
await cartStore.addToCart(productId)
}
onMounted(() => {
loadFlashSales()
loadProducts()
})
</script>
<style scoped lang="scss">
.home-page {
min-height: 100vh;
}
.banner-content {
height: 100%;
display: flex;
align-items: center;
}
.feature-card {
@apply bg-white p-6 rounded-lg shadow-md text-center hover:shadow-lg transition-shadow;
}
:deep(.el-carousel__item) {
overflow: hidden;
}
</style>