fix: 修复图片上传回显、登录认证和API路径问题
- 修复上传图片响应解析,正确处理 Arco Upload 返回的 response 对象 - 修复后端 AuthInterceptor 路径匹配,正确放行 /api/auth/login 等公开接口 - 统一前端 API 路径配置,移除重复 /api 前缀 - 添加 /uploads 静态资源代理配置 - 修复图片 URL 生成,添加 origin 前缀确保回显正常
This commit is contained in:
@@ -9,7 +9,8 @@ import org.springframework.http.HttpMethod;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
public class AuthInterceptor implements HandlerInterceptor {
|
||||
@@ -18,7 +19,7 @@ public class AuthInterceptor implements HandlerInterceptor {
|
||||
@Value("${app.token-header:X-Token}")
|
||||
private String tokenHeader;
|
||||
|
||||
private static final Set<String> PUBLIC_PREFIX = Set.of(
|
||||
private static final java.util.List<String> PUBLIC_PATHS = java.util.Arrays.asList(
|
||||
"/api/auth/login",
|
||||
"/api/auth/register",
|
||||
"/api/public"
|
||||
@@ -37,17 +38,23 @@ public class AuthInterceptor implements HandlerInterceptor {
|
||||
}
|
||||
|
||||
String uri = request.getRequestURI();
|
||||
if (uri.equals("/") || uri.startsWith("/error") || PUBLIC_PREFIX.stream().anyMatch(uri::startsWith)) {
|
||||
String contextPath = request.getContextPath();
|
||||
String path = contextPath.isEmpty() ? uri : uri.substring(contextPath.length());
|
||||
|
||||
if (uri.equals("/") || uri.startsWith("/error") ||
|
||||
PUBLIC_PATHS.stream().anyMatch(p -> path.equals(p) || path.startsWith(p + "/") || path.startsWith(p + "?"))) {
|
||||
return true;
|
||||
}
|
||||
String token = request.getHeader(tokenHeader);
|
||||
if (token == null || token.isBlank()) {
|
||||
response.setContentType("application/json;charset=UTF-8");
|
||||
response.setStatus(401);
|
||||
response.getWriter().write("{\"code\":401,\"message\":\"请先登录\",\"data\":null}");
|
||||
return false;
|
||||
}
|
||||
User user = userRepository.findByToken(token).orElse(null);
|
||||
if (user == null || !Boolean.TRUE.equals(user.getEnabled())) {
|
||||
response.setContentType("application/json;charset=UTF-8");
|
||||
response.setStatus(401);
|
||||
response.getWriter().write("{\"code\":401,\"message\":\"登录状态无效\",\"data\":null}");
|
||||
return false;
|
||||
|
||||
@@ -22,7 +22,7 @@ public class FileUploadController {
|
||||
@Value("${app.upload-url-prefix:/uploads}")
|
||||
private String uploadUrlPrefix;
|
||||
|
||||
@PostMapping("/upload")
|
||||
@PostMapping("/upload/file")
|
||||
public ApiResponse<String> uploadFile(@RequestParam("file") MultipartFile file) {
|
||||
if (file.isEmpty()) {
|
||||
return ApiResponse.fail("请选择要上传的文件", String.class);
|
||||
|
||||
@@ -15,6 +15,11 @@ spring:
|
||||
format_sql: true
|
||||
jackson:
|
||||
time-zone: Asia/Shanghai
|
||||
servlet:
|
||||
multipart:
|
||||
max-file-size: 10MB
|
||||
max-request-size: 100MB
|
||||
|
||||
app:
|
||||
token-header: X-Token
|
||||
upload-path: /Users/apple/code/bs/mying/backend/uploads
|
||||
|
||||
@@ -1,69 +1,68 @@
|
||||
import http from './http'
|
||||
|
||||
export const api = {
|
||||
register: (payload) => http.post('/api/auth/register', payload),
|
||||
login: (payload) => http.post('/api/auth/login', payload),
|
||||
me: () => http.get('/api/auth/me'),
|
||||
updateMe: (payload) => http.put('/api/auth/me', payload),
|
||||
register: (payload) => http.post('/auth/register', payload),
|
||||
login: (payload) => http.post('/auth/login', payload),
|
||||
me: () => http.get('/auth/me'),
|
||||
updateMe: (payload) => http.put('/auth/me', payload),
|
||||
|
||||
banners: () => http.get('/api/public/banners'),
|
||||
products: (keyword = '') => http.get('/api/public/products', { params: { keyword } }),
|
||||
banners: () => http.get('/public/banners'),
|
||||
products: (keyword = '') => http.get('/public/products', { params: { keyword } }),
|
||||
|
||||
customerCart: () => http.get('/api/customer/cart'),
|
||||
customerCartViews: () => http.get('/api/customer/cart/views'),
|
||||
addCart: (payload) => http.post('/api/customer/cart', payload),
|
||||
delCart: (productId) => http.delete(`/api/customer/cart/${productId}`),
|
||||
checkout: (payload) => http.post('/api/customer/orders/checkout', payload),
|
||||
customerBuyNow: (payload) => http.post('/api/customer/orders/buy-now', payload),
|
||||
customerOrders: () => http.get('/api/customer/orders'),
|
||||
refundOrder: (id, payload) => http.put(`/api/customer/orders/${id}/refund`, payload),
|
||||
updateOrderAddress: (id, payload) => http.put(`/api/customer/orders/${id}/address`, payload),
|
||||
deleteOrder: (id) => http.delete(`/api/customer/orders/${id}`),
|
||||
orderLogistics: (id) => http.get(`/api/customer/orders/${id}/logistics`),
|
||||
customerFavorites: () => http.get('/api/customer/favorites'),
|
||||
customerFavoriteViews: () => http.get('/api/customer/favorites/views'),
|
||||
addFavorite: (payload) => http.post('/api/customer/favorites', payload),
|
||||
deleteFavorite: (productId) => http.delete(`/api/customer/favorites/${productId}`),
|
||||
addReview: (payload) => http.post('/api/customer/reviews', payload),
|
||||
orderItems: (orderId) => http.get(`/api/customer/orders/${orderId}/items`),
|
||||
applyMerchant: (payload) => http.post('/api/customer/merchant-applications', payload),
|
||||
customerCart: () => http.get('/customer/cart'),
|
||||
customerCartViews: () => http.get('/customer/cart/views'),
|
||||
addCart: (payload) => http.post('/customer/cart', payload),
|
||||
delCart: (productId) => http.delete(`/customer/cart/${productId}`),
|
||||
checkout: (payload) => http.post('/customer/orders/checkout', payload),
|
||||
customerBuyNow: (payload) => http.post('/customer/orders/buy-now', payload),
|
||||
customerOrders: () => http.get('/customer/orders'),
|
||||
refundOrder: (id, payload) => http.put(`/customer/orders/${id}/refund`, payload),
|
||||
updateOrderAddress: (id, payload) => http.put(`/customer/orders/${id}/address`, payload),
|
||||
deleteOrder: (id) => http.delete(`/customer/orders/${id}`),
|
||||
orderLogistics: (id) => http.get(`/customer/orders/${id}/logistics`),
|
||||
customerFavorites: () => http.get('/customer/favorites'),
|
||||
customerFavoriteViews: () => http.get('/customer/favorites/views'),
|
||||
addFavorite: (payload) => http.post('/customer/favorites', payload),
|
||||
deleteFavorite: (productId) => http.delete(`/customer/favorites/${productId}`),
|
||||
addReview: (payload) => http.post('/customer/reviews', payload),
|
||||
orderItems: (orderId) => http.get(`/customer/orders/${orderId}/items`),
|
||||
applyMerchant: (payload) => http.post('/customer/merchant-applications', payload),
|
||||
|
||||
merchantOverview: () => http.get('/api/merchant/overview'),
|
||||
merchantProducts: () => http.get('/api/merchant/products'),
|
||||
saveMerchantProduct: (payload) => http.post('/api/merchant/products', payload),
|
||||
deleteMerchantProduct: (id) => http.delete(`/api/merchant/products/${id}`),
|
||||
merchantOrders: () => http.get('/api/merchant/orders'),
|
||||
shipOrder: (id, payload) => http.put(`/api/merchant/orders/${id}/ship`, payload),
|
||||
merchantRefund: (id, payload) => http.put(`/api/merchant/orders/${id}/refund`, payload),
|
||||
merchantReviews: () => http.get('/api/merchant/reviews'),
|
||||
merchantLogistics: () => http.get('/api/merchant/logistics'),
|
||||
merchantInventory: () => http.get('/api/merchant/inventory'),
|
||||
deleteMerchantInventory: (id) => http.delete(`/api/merchant/inventory/${id}`),
|
||||
merchantOverview: () => http.get('/merchant/overview'),
|
||||
merchantProducts: () => http.get('/merchant/products'),
|
||||
saveMerchantProduct: (payload) => http.post('/merchant/products', payload),
|
||||
deleteMerchantProduct: (id) => http.delete(`/merchant/products/${id}`),
|
||||
merchantOrders: () => http.get('/merchant/orders'),
|
||||
shipOrder: (id, payload) => http.put(`/merchant/orders/${id}/ship`, payload),
|
||||
merchantRefund: (id, payload) => http.put(`/merchant/orders/${id}/refund`, payload),
|
||||
merchantReviews: () => http.get('/merchant/reviews'),
|
||||
merchantLogistics: () => http.get('/merchant/logistics'),
|
||||
merchantInventory: () => http.get('/merchant/inventory'),
|
||||
deleteMerchantInventory: (id) => http.delete(`/merchant/inventory/${id}`),
|
||||
|
||||
adminOverview: () => http.get('/api/admin/overview'),
|
||||
adminUsers: () => http.get('/api/admin/users'),
|
||||
adminSaveUser: (payload) => http.post('/api/admin/users', payload),
|
||||
adminDeleteUser: (id) => http.delete(`/api/admin/users/${id}`),
|
||||
adminOrders: () => http.get('/api/admin/orders'),
|
||||
adminUpdateOrder: (id, payload) => http.put(`/api/admin/orders/${id}`, payload),
|
||||
adminOrderRisks: () => http.get('/api/admin/orders/risk'),
|
||||
adminAuditRefund: (id, payload) => http.put(`/api/admin/orders/${id}/refund-audit`, payload),
|
||||
adminAuditShipment: (id, payload) => http.put(`/api/admin/orders/${id}/ship-audit`, payload),
|
||||
adminMerchantApplications: () => http.get('/api/admin/merchant-applications'),
|
||||
adminAuditMerchantApplication: (id, payload) => http.put(`/api/admin/merchant-applications/${id}`, payload),
|
||||
adminBanners: () => http.get('/api/admin/banners'),
|
||||
adminSaveBanner: (payload) => http.post('/api/admin/banners', payload),
|
||||
adminDeleteBanner: (id) => http.delete(`/api/admin/banners/${id}`),
|
||||
adminProducts: () => http.get('/api/admin/products'),
|
||||
adminProductViews: () => http.get('/api/admin/products/views'),
|
||||
adminSaveProduct: (payload) => http.post('/api/admin/products', payload),
|
||||
adminApproveProduct: (id, payload) => http.put(`/api/admin/products/${id}/approve`, payload),
|
||||
adminDeleteProduct: (id) => http.delete(`/api/admin/products/${id}`),
|
||||
adminReviews: () => http.get('/api/admin/reviews'),
|
||||
adminLogistics: () => http.get('/api/admin/logistics'),
|
||||
adminInventory: () => http.get('/api/admin/inventory'),
|
||||
adminOverview: () => http.get('/admin/overview'),
|
||||
adminUsers: () => http.get('/admin/users'),
|
||||
adminSaveUser: (payload) => http.post('/admin/users', payload),
|
||||
adminDeleteUser: (id) => http.delete(`/admin/users/${id}`),
|
||||
adminOrders: () => http.get('/admin/orders'),
|
||||
adminUpdateOrder: (id, payload) => http.put(`/admin/orders/${id}`, payload),
|
||||
adminOrderRisks: () => http.get('/admin/orders/risk'),
|
||||
adminAuditRefund: (id, payload) => http.put(`/admin/orders/${id}/refund-audit`, payload),
|
||||
adminAuditShipment: (id, payload) => http.put(`/admin/orders/${id}/ship-audit`, payload),
|
||||
adminMerchantApplications: () => http.get('/admin/merchant-applications'),
|
||||
adminAuditMerchantApplication: (id, payload) => http.put(`/admin/merchant-applications/${id}`, payload),
|
||||
adminBanners: () => http.get('/admin/banners'),
|
||||
adminSaveBanner: (payload) => http.post('/admin/banners', payload),
|
||||
adminDeleteBanner: (id) => http.delete(`/admin/banners/${id}`),
|
||||
adminProducts: () => http.get('/admin/products'),
|
||||
adminProductViews: () => http.get('/admin/products/views'),
|
||||
adminSaveProduct: (payload) => http.post('/admin/products', payload),
|
||||
adminApproveProduct: (id, payload) => http.put(`/admin/products/${id}/approve`, payload),
|
||||
adminDeleteProduct: (id) => http.delete(`/admin/products/${id}`),
|
||||
adminReviews: () => http.get('/admin/reviews'),
|
||||
adminLogistics: () => http.get('/admin/logistics'),
|
||||
adminInventory: () => http.get('/admin/inventory'),
|
||||
|
||||
// File Upload
|
||||
uploadImage: (file) => {
|
||||
const formData = new FormData()
|
||||
formData.append('file', file)
|
||||
@@ -74,7 +73,7 @@ export const api = {
|
||||
uploadFile: (file) => {
|
||||
const formData = new FormData()
|
||||
formData.append('file', file)
|
||||
return http.post('/api/upload', formData, {
|
||||
return http.post('/upload', formData, {
|
||||
headers: { 'Content-Type': 'multipart/form-data' }
|
||||
})
|
||||
}
|
||||
|
||||
@@ -109,13 +109,22 @@ const beforeUpload = (file) => {
|
||||
return true
|
||||
}
|
||||
|
||||
const onUploadSuccess = (response) => {
|
||||
if (response && response.data) {
|
||||
bannerForm.imageUrl = response.data
|
||||
Message.success('图片上传成功')
|
||||
} else if (typeof response === 'string') {
|
||||
bannerForm.imageUrl = response
|
||||
const onUploadSuccess = (uploadInfo) => {
|
||||
const response = uploadInfo.response
|
||||
console.log('Upload server response:', response)
|
||||
if (response && response.code === 0 && response.data) {
|
||||
const imageUrl = response.data
|
||||
console.log('Image URL from response:', imageUrl)
|
||||
if (imageUrl.startsWith('/')) {
|
||||
bannerForm.imageUrl = window.location.origin + imageUrl
|
||||
} else {
|
||||
bannerForm.imageUrl = imageUrl
|
||||
}
|
||||
console.log('Final imageUrl:', bannerForm.imageUrl)
|
||||
Message.success('图片上传成功')
|
||||
} else {
|
||||
console.warn('Unexpected response format:', response)
|
||||
Message.warning('上传成功,但无法解析图片地址')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -145,13 +145,22 @@ const beforeUpload = (file) => {
|
||||
return true
|
||||
}
|
||||
|
||||
const onUploadSuccess = (response) => {
|
||||
if (response && response.data) {
|
||||
productForm.imageUrl = response.data
|
||||
Message.success('图片上传成功')
|
||||
} else if (typeof response === 'string') {
|
||||
productForm.imageUrl = response
|
||||
const onUploadSuccess = (uploadInfo) => {
|
||||
const response = uploadInfo.response
|
||||
console.log('Upload server response:', response)
|
||||
if (response && response.code === 0 && response.data) {
|
||||
const imageUrl = response.data
|
||||
console.log('Image URL from response:', imageUrl)
|
||||
if (imageUrl.startsWith('/')) {
|
||||
productForm.imageUrl = window.location.origin + imageUrl
|
||||
} else {
|
||||
productForm.imageUrl = imageUrl
|
||||
}
|
||||
console.log('Final imageUrl:', productForm.imageUrl)
|
||||
Message.success('图片上传成功')
|
||||
} else {
|
||||
console.warn('Unexpected response format:', response)
|
||||
Message.warning('上传成功,但无法解析图片地址')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -129,13 +129,22 @@ const beforeUpload = (file) => {
|
||||
return true
|
||||
}
|
||||
|
||||
const onUploadSuccess = (response) => {
|
||||
if (response && response.data) {
|
||||
productForm.imageUrl = response.data
|
||||
Message.success('图片上传成功')
|
||||
} else if (typeof response === 'string') {
|
||||
productForm.imageUrl = response
|
||||
const onUploadSuccess = (uploadInfo) => {
|
||||
const response = uploadInfo.response
|
||||
console.log('Upload server response:', response)
|
||||
if (response && response.code === 0 && response.data) {
|
||||
const imageUrl = response.data
|
||||
console.log('Image URL from response:', imageUrl)
|
||||
if (imageUrl.startsWith('/')) {
|
||||
productForm.imageUrl = window.location.origin + imageUrl
|
||||
} else {
|
||||
productForm.imageUrl = imageUrl
|
||||
}
|
||||
console.log('Final imageUrl:', productForm.imageUrl)
|
||||
Message.success('图片上传成功')
|
||||
} else {
|
||||
console.warn('Unexpected response format:', response)
|
||||
Message.warning('上传成功,但无法解析图片地址')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import vue from '@vitejs/plugin-vue'
|
||||
export default defineConfig({
|
||||
plugins: [vue()],
|
||||
server: {
|
||||
port: 5173,
|
||||
port: 5174,
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'http://localhost:8080',
|
||||
|
||||
Reference in New Issue
Block a user