后端功能增强:全局异常处理、API控制器、JSP视图和单元测试
- 添加 GlobalExceptionHandler 全局异常处理 - 添加 ApiController REST API 控制器 - 更新 WebConfig 跨域配置和 ProductRepository 查询方法 - 新增 monitor/product-detail/profile JSP 视图页面 - 添加 FlashSaleServiceTest 秒杀服务单元测试 - 更新 application.yml 配置
This commit is contained in:
42
flash-sale-frontend/src/api/modules/cart.ts
Normal file
42
flash-sale-frontend/src/api/modules/cart.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { request } from '../request'
|
||||
import type { ApiResponse, CartItem } from '@/types/api'
|
||||
|
||||
export const cartApi = {
|
||||
// 获取购物车
|
||||
getCart(): Promise<ApiResponse<CartItem[]>> {
|
||||
return request.get('/api/cart')
|
||||
},
|
||||
|
||||
// 添加到购物车
|
||||
addToCart(data: {
|
||||
productId: number;
|
||||
quantity: number
|
||||
}): Promise<ApiResponse> {
|
||||
return request.post('/api/cart/add', data)
|
||||
},
|
||||
|
||||
// 更新数量
|
||||
updateQuantity(itemId: string, quantity: number): Promise<ApiResponse> {
|
||||
return request.put(`/api/cart/item/${itemId}`, { quantity })
|
||||
},
|
||||
|
||||
// 删除商品
|
||||
removeItem(itemId: string): Promise<ApiResponse> {
|
||||
return request.delete(`/api/cart/item/${itemId}`)
|
||||
},
|
||||
|
||||
// 批量删除
|
||||
batchRemove(ids: string[]): Promise<ApiResponse> {
|
||||
return request.post('/api/cart/batch-remove', { ids })
|
||||
},
|
||||
|
||||
// 清空购物车
|
||||
clearCart(): Promise<ApiResponse> {
|
||||
return request.delete('/api/cart/clear')
|
||||
},
|
||||
|
||||
// 获取购物车数量
|
||||
getCount(): Promise<ApiResponse<{ count: number }>> {
|
||||
return request.get('/api/cart/count')
|
||||
},
|
||||
}
|
||||
42
flash-sale-frontend/src/api/modules/flashsale.ts
Normal file
42
flash-sale-frontend/src/api/modules/flashsale.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { request } from '../request'
|
||||
import type { ApiResponse, FlashSale, PageParams, PageResponse } from '@/types/api'
|
||||
|
||||
export const flashsaleApi = {
|
||||
// 获取秒杀活动列表
|
||||
getList(params?: PageParams & { status?: string }): Promise<ApiResponse<PageResponse<FlashSale>>> {
|
||||
return request.get('/api/flashsale/list', params)
|
||||
},
|
||||
|
||||
// 获取正在进行的秒杀活动
|
||||
getActive(limit?: number): Promise<ApiResponse<FlashSale[]>> {
|
||||
return request.get('/api/flashsale/active', { limit })
|
||||
},
|
||||
|
||||
// 获取秒杀活动详情
|
||||
getDetail(id: number): Promise<ApiResponse<FlashSale>> {
|
||||
return request.get(`/api/flashsale/${id}`)
|
||||
},
|
||||
|
||||
// 参与秒杀
|
||||
participate(data: {
|
||||
flashSaleId: number;
|
||||
quantity: number;
|
||||
timestamp?: number;
|
||||
}): Promise<ApiResponse<{ orderId: number }>> {
|
||||
return request.post('/api/flashsale/participate', data)
|
||||
},
|
||||
|
||||
// 获取用户参与记录
|
||||
getUserRecords(): Promise<ApiResponse<any[]>> {
|
||||
return request.get('/api/flashsale/user-records')
|
||||
},
|
||||
|
||||
// 检查用户是否可以参与
|
||||
checkEligibility(flashSaleId: number): Promise<ApiResponse<{
|
||||
eligible: boolean;
|
||||
reason?: string;
|
||||
remainingQuota?: number;
|
||||
}>> {
|
||||
return request.get(`/api/flashsale/${flashSaleId}/check-eligibility`)
|
||||
},
|
||||
}
|
||||
57
flash-sale-frontend/src/api/modules/order.ts
Normal file
57
flash-sale-frontend/src/api/modules/order.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import { request } from '../request'
|
||||
import type { ApiResponse, Order, PageParams, PageResponse } from '@/types/api'
|
||||
|
||||
export const orderApi = {
|
||||
// 创建订单
|
||||
create(data: {
|
||||
items: Array<{ productId: number; quantity: number }>
|
||||
addressId?: number
|
||||
remark?: string
|
||||
}): Promise<ApiResponse<Order>> {
|
||||
return request.post('/api/order/create', data)
|
||||
},
|
||||
|
||||
// 获取订单列表
|
||||
getList(params?: PageParams & {
|
||||
status?: string
|
||||
}): Promise<ApiResponse<PageResponse<Order>>> {
|
||||
return request.get('/api/order/list', params)
|
||||
},
|
||||
|
||||
// 获取订单详情
|
||||
getDetail(id: number): Promise<ApiResponse<Order>> {
|
||||
return request.get(`/api/order/${id}`)
|
||||
},
|
||||
|
||||
// 取消订单
|
||||
cancel(id: number): Promise<ApiResponse> {
|
||||
return request.post(`/api/order/${id}/cancel`)
|
||||
},
|
||||
|
||||
// 支付订单
|
||||
pay(id: number, paymentMethod: string): Promise<ApiResponse> {
|
||||
return request.post(`/api/order/${id}/pay`, { paymentMethod })
|
||||
},
|
||||
|
||||
// 确认收货
|
||||
confirm(id: number): Promise<ApiResponse> {
|
||||
return request.post(`/api/order/${id}/confirm`)
|
||||
},
|
||||
|
||||
// 删除订单
|
||||
delete(id: number): Promise<ApiResponse> {
|
||||
return request.delete(`/api/order/${id}`)
|
||||
},
|
||||
|
||||
// 获取订单统计
|
||||
getStatistics(): Promise<ApiResponse<{
|
||||
total: number
|
||||
pending: number
|
||||
paid: number
|
||||
shipped: number
|
||||
completed: number
|
||||
cancelled: number
|
||||
}>> {
|
||||
return request.get('/api/order/statistics')
|
||||
},
|
||||
}
|
||||
34
flash-sale-frontend/src/api/modules/product.ts
Normal file
34
flash-sale-frontend/src/api/modules/product.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { request } from '../request'
|
||||
import type { ApiResponse, Product, PageParams, PageResponse } from '@/types/api'
|
||||
|
||||
export const productApi = {
|
||||
// 获取商品列表
|
||||
getList(params?: PageParams & {
|
||||
keyword?: string;
|
||||
category?: string;
|
||||
minPrice?: number;
|
||||
maxPrice?: number;
|
||||
}): Promise<ApiResponse<PageResponse<Product>>> {
|
||||
return request.get('/api/product/list', params)
|
||||
},
|
||||
|
||||
// 获取热门商品
|
||||
getHot(limit = 8): Promise<ApiResponse<Product[]>> {
|
||||
return request.get('/api/product/hot', { limit })
|
||||
},
|
||||
|
||||
// 获取商品详情
|
||||
getDetail(id: number): Promise<ApiResponse<Product>> {
|
||||
return request.get(`/api/product/${id}`)
|
||||
},
|
||||
|
||||
// 搜索商品
|
||||
search(keyword: string): Promise<ApiResponse<Product[]>> {
|
||||
return request.get('/api/product/search', { keyword })
|
||||
},
|
||||
|
||||
// 获取商品分类
|
||||
getCategories(): Promise<ApiResponse<string[]>> {
|
||||
return request.get('/api/product/categories')
|
||||
},
|
||||
}
|
||||
34
flash-sale-frontend/src/api/modules/user.ts
Normal file
34
flash-sale-frontend/src/api/modules/user.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { request } from '../request'
|
||||
import type { ApiResponse, User, LoginParams, RegisterParams } from '@/types/api'
|
||||
|
||||
export const userApi = {
|
||||
// 登录
|
||||
login(params: LoginParams): Promise<ApiResponse<{ token: string; user: User }>> {
|
||||
return request.post('/api/auth/login', params)
|
||||
},
|
||||
|
||||
// 注册
|
||||
register(params: RegisterParams): Promise<ApiResponse<User>> {
|
||||
return request.post('/api/auth/register', params)
|
||||
},
|
||||
|
||||
// 退出登录
|
||||
logout(): Promise<ApiResponse> {
|
||||
return request.post('/api/auth/logout')
|
||||
},
|
||||
|
||||
// 获取用户信息
|
||||
getInfo(): Promise<ApiResponse<User>> {
|
||||
return request.get('/api/user/info')
|
||||
},
|
||||
|
||||
// 更新用户信息
|
||||
updateInfo(data: Partial<User>): Promise<ApiResponse<User>> {
|
||||
return request.put('/api/user/info', data)
|
||||
},
|
||||
|
||||
// 修改密码
|
||||
changePassword(data: { oldPassword: string; newPassword: string }): Promise<ApiResponse> {
|
||||
return request.post('/api/user/change-password', data)
|
||||
},
|
||||
}
|
||||
118
flash-sale-frontend/src/api/request.ts
Normal file
118
flash-sale-frontend/src/api/request.ts
Normal file
@@ -0,0 +1,118 @@
|
||||
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import router from '@/router'
|
||||
|
||||
// 创建axios实例
|
||||
const service: AxiosInstance = axios.create({
|
||||
baseURL: import.meta.env.VITE_API_BASE_URL,
|
||||
timeout: Number(import.meta.env.VITE_TIMEOUT) || 10000,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
})
|
||||
|
||||
// 请求拦截器
|
||||
service.interceptors.request.use(
|
||||
(config: AxiosRequestConfig) => {
|
||||
const userStore = useUserStore()
|
||||
|
||||
// 添加token
|
||||
if (userStore.token) {
|
||||
config.headers = config.headers || {}
|
||||
config.headers['Authorization'] = `Bearer ${userStore.token}`
|
||||
}
|
||||
|
||||
return config
|
||||
},
|
||||
(error) => {
|
||||
console.error('请求错误:', error)
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
||||
// 响应拦截器
|
||||
service.interceptors.response.use(
|
||||
(response: AxiosResponse) => {
|
||||
const res = response.data
|
||||
|
||||
// 自定义状态码处理
|
||||
if (res.code !== 200 && res.code !== 0) {
|
||||
// 业务错误
|
||||
if (res.code === 401) {
|
||||
// 未登录或token失效
|
||||
ElMessageBox.confirm('登录已过期,请重新登录', '提示', {
|
||||
confirmButtonText: '重新登录',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}).then(() => {
|
||||
const userStore = useUserStore()
|
||||
userStore.logout()
|
||||
router.push('/login')
|
||||
})
|
||||
} else if (res.code === 403) {
|
||||
// 无权限
|
||||
ElMessage.error('无权限访问')
|
||||
} else {
|
||||
// 其他业务错误
|
||||
ElMessage.error(res.message || '请求失败')
|
||||
}
|
||||
|
||||
return Promise.reject(new Error(res.message || '请求失败'))
|
||||
}
|
||||
|
||||
return res
|
||||
},
|
||||
(error) => {
|
||||
console.error('响应错误:', error)
|
||||
|
||||
if (error.response) {
|
||||
switch (error.response.status) {
|
||||
case 401:
|
||||
ElMessage.error('未授权,请登录')
|
||||
break
|
||||
case 403:
|
||||
ElMessage.error('拒绝访问')
|
||||
break
|
||||
case 404:
|
||||
ElMessage.error('请求地址不存在')
|
||||
break
|
||||
case 429:
|
||||
ElMessage.error('请求过于频繁,请稍后再试')
|
||||
break
|
||||
case 500:
|
||||
ElMessage.error('服务器内部错误')
|
||||
break
|
||||
default:
|
||||
ElMessage.error(error.response.data?.message || '请求失败')
|
||||
}
|
||||
} else if (error.request) {
|
||||
ElMessage.error('网络错误,请检查网络连接')
|
||||
} else {
|
||||
ElMessage.error('请求配置错误')
|
||||
}
|
||||
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
||||
// 通用请求方法
|
||||
export const request = {
|
||||
get<T = any>(url: string, params?: any): Promise<T> {
|
||||
return service.get(url, { params })
|
||||
},
|
||||
|
||||
post<T = any>(url: string, data?: any): Promise<T> {
|
||||
return service.post(url, data)
|
||||
},
|
||||
|
||||
put<T = any>(url: string, data?: any): Promise<T> {
|
||||
return service.put(url, data)
|
||||
},
|
||||
|
||||
delete<T = any>(url: string, params?: any): Promise<T> {
|
||||
return service.delete(url, { params })
|
||||
},
|
||||
}
|
||||
|
||||
export default service
|
||||
Reference in New Issue
Block a user