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.stereotype.Component;
|
||||||
import org.springframework.web.servlet.HandlerInterceptor;
|
import org.springframework.web.servlet.HandlerInterceptor;
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class AuthInterceptor implements HandlerInterceptor {
|
public class AuthInterceptor implements HandlerInterceptor {
|
||||||
@@ -18,7 +19,7 @@ public class AuthInterceptor implements HandlerInterceptor {
|
|||||||
@Value("${app.token-header:X-Token}")
|
@Value("${app.token-header:X-Token}")
|
||||||
private String tokenHeader;
|
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/login",
|
||||||
"/api/auth/register",
|
"/api/auth/register",
|
||||||
"/api/public"
|
"/api/public"
|
||||||
@@ -37,17 +38,23 @@ public class AuthInterceptor implements HandlerInterceptor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String uri = request.getRequestURI();
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
String token = request.getHeader(tokenHeader);
|
String token = request.getHeader(tokenHeader);
|
||||||
if (token == null || token.isBlank()) {
|
if (token == null || token.isBlank()) {
|
||||||
|
response.setContentType("application/json;charset=UTF-8");
|
||||||
response.setStatus(401);
|
response.setStatus(401);
|
||||||
response.getWriter().write("{\"code\":401,\"message\":\"请先登录\",\"data\":null}");
|
response.getWriter().write("{\"code\":401,\"message\":\"请先登录\",\"data\":null}");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
User user = userRepository.findByToken(token).orElse(null);
|
User user = userRepository.findByToken(token).orElse(null);
|
||||||
if (user == null || !Boolean.TRUE.equals(user.getEnabled())) {
|
if (user == null || !Boolean.TRUE.equals(user.getEnabled())) {
|
||||||
|
response.setContentType("application/json;charset=UTF-8");
|
||||||
response.setStatus(401);
|
response.setStatus(401);
|
||||||
response.getWriter().write("{\"code\":401,\"message\":\"登录状态无效\",\"data\":null}");
|
response.getWriter().write("{\"code\":401,\"message\":\"登录状态无效\",\"data\":null}");
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ public class FileUploadController {
|
|||||||
@Value("${app.upload-url-prefix:/uploads}")
|
@Value("${app.upload-url-prefix:/uploads}")
|
||||||
private String uploadUrlPrefix;
|
private String uploadUrlPrefix;
|
||||||
|
|
||||||
@PostMapping("/upload")
|
@PostMapping("/upload/file")
|
||||||
public ApiResponse<String> uploadFile(@RequestParam("file") MultipartFile file) {
|
public ApiResponse<String> uploadFile(@RequestParam("file") MultipartFile file) {
|
||||||
if (file.isEmpty()) {
|
if (file.isEmpty()) {
|
||||||
return ApiResponse.fail("请选择要上传的文件", String.class);
|
return ApiResponse.fail("请选择要上传的文件", String.class);
|
||||||
|
|||||||
@@ -15,6 +15,11 @@ spring:
|
|||||||
format_sql: true
|
format_sql: true
|
||||||
jackson:
|
jackson:
|
||||||
time-zone: Asia/Shanghai
|
time-zone: Asia/Shanghai
|
||||||
|
servlet:
|
||||||
|
multipart:
|
||||||
|
max-file-size: 10MB
|
||||||
|
max-request-size: 100MB
|
||||||
|
|
||||||
app:
|
app:
|
||||||
token-header: X-Token
|
token-header: X-Token
|
||||||
|
upload-path: /Users/apple/code/bs/mying/backend/uploads
|
||||||
|
|||||||
@@ -1,69 +1,68 @@
|
|||||||
import http from './http'
|
import http from './http'
|
||||||
|
|
||||||
export const api = {
|
export const api = {
|
||||||
register: (payload) => http.post('/api/auth/register', payload),
|
register: (payload) => http.post('/auth/register', payload),
|
||||||
login: (payload) => http.post('/api/auth/login', payload),
|
login: (payload) => http.post('/auth/login', payload),
|
||||||
me: () => http.get('/api/auth/me'),
|
me: () => http.get('/auth/me'),
|
||||||
updateMe: (payload) => http.put('/api/auth/me', payload),
|
updateMe: (payload) => http.put('/auth/me', payload),
|
||||||
|
|
||||||
banners: () => http.get('/api/public/banners'),
|
banners: () => http.get('/public/banners'),
|
||||||
products: (keyword = '') => http.get('/api/public/products', { params: { keyword } }),
|
products: (keyword = '') => http.get('/public/products', { params: { keyword } }),
|
||||||
|
|
||||||
customerCart: () => http.get('/api/customer/cart'),
|
customerCart: () => http.get('/customer/cart'),
|
||||||
customerCartViews: () => http.get('/api/customer/cart/views'),
|
customerCartViews: () => http.get('/customer/cart/views'),
|
||||||
addCart: (payload) => http.post('/api/customer/cart', payload),
|
addCart: (payload) => http.post('/customer/cart', payload),
|
||||||
delCart: (productId) => http.delete(`/api/customer/cart/${productId}`),
|
delCart: (productId) => http.delete(`/customer/cart/${productId}`),
|
||||||
checkout: (payload) => http.post('/api/customer/orders/checkout', payload),
|
checkout: (payload) => http.post('/customer/orders/checkout', payload),
|
||||||
customerBuyNow: (payload) => http.post('/api/customer/orders/buy-now', payload),
|
customerBuyNow: (payload) => http.post('/customer/orders/buy-now', payload),
|
||||||
customerOrders: () => http.get('/api/customer/orders'),
|
customerOrders: () => http.get('/customer/orders'),
|
||||||
refundOrder: (id, payload) => http.put(`/api/customer/orders/${id}/refund`, payload),
|
refundOrder: (id, payload) => http.put(`/customer/orders/${id}/refund`, payload),
|
||||||
updateOrderAddress: (id, payload) => http.put(`/api/customer/orders/${id}/address`, payload),
|
updateOrderAddress: (id, payload) => http.put(`/customer/orders/${id}/address`, payload),
|
||||||
deleteOrder: (id) => http.delete(`/api/customer/orders/${id}`),
|
deleteOrder: (id) => http.delete(`/customer/orders/${id}`),
|
||||||
orderLogistics: (id) => http.get(`/api/customer/orders/${id}/logistics`),
|
orderLogistics: (id) => http.get(`/customer/orders/${id}/logistics`),
|
||||||
customerFavorites: () => http.get('/api/customer/favorites'),
|
customerFavorites: () => http.get('/customer/favorites'),
|
||||||
customerFavoriteViews: () => http.get('/api/customer/favorites/views'),
|
customerFavoriteViews: () => http.get('/customer/favorites/views'),
|
||||||
addFavorite: (payload) => http.post('/api/customer/favorites', payload),
|
addFavorite: (payload) => http.post('/customer/favorites', payload),
|
||||||
deleteFavorite: (productId) => http.delete(`/api/customer/favorites/${productId}`),
|
deleteFavorite: (productId) => http.delete(`/customer/favorites/${productId}`),
|
||||||
addReview: (payload) => http.post('/api/customer/reviews', payload),
|
addReview: (payload) => http.post('/customer/reviews', payload),
|
||||||
orderItems: (orderId) => http.get(`/api/customer/orders/${orderId}/items`),
|
orderItems: (orderId) => http.get(`/customer/orders/${orderId}/items`),
|
||||||
applyMerchant: (payload) => http.post('/api/customer/merchant-applications', payload),
|
applyMerchant: (payload) => http.post('/customer/merchant-applications', payload),
|
||||||
|
|
||||||
merchantOverview: () => http.get('/api/merchant/overview'),
|
merchantOverview: () => http.get('/merchant/overview'),
|
||||||
merchantProducts: () => http.get('/api/merchant/products'),
|
merchantProducts: () => http.get('/merchant/products'),
|
||||||
saveMerchantProduct: (payload) => http.post('/api/merchant/products', payload),
|
saveMerchantProduct: (payload) => http.post('/merchant/products', payload),
|
||||||
deleteMerchantProduct: (id) => http.delete(`/api/merchant/products/${id}`),
|
deleteMerchantProduct: (id) => http.delete(`/merchant/products/${id}`),
|
||||||
merchantOrders: () => http.get('/api/merchant/orders'),
|
merchantOrders: () => http.get('/merchant/orders'),
|
||||||
shipOrder: (id, payload) => http.put(`/api/merchant/orders/${id}/ship`, payload),
|
shipOrder: (id, payload) => http.put(`/merchant/orders/${id}/ship`, payload),
|
||||||
merchantRefund: (id, payload) => http.put(`/api/merchant/orders/${id}/refund`, payload),
|
merchantRefund: (id, payload) => http.put(`/merchant/orders/${id}/refund`, payload),
|
||||||
merchantReviews: () => http.get('/api/merchant/reviews'),
|
merchantReviews: () => http.get('/merchant/reviews'),
|
||||||
merchantLogistics: () => http.get('/api/merchant/logistics'),
|
merchantLogistics: () => http.get('/merchant/logistics'),
|
||||||
merchantInventory: () => http.get('/api/merchant/inventory'),
|
merchantInventory: () => http.get('/merchant/inventory'),
|
||||||
deleteMerchantInventory: (id) => http.delete(`/api/merchant/inventory/${id}`),
|
deleteMerchantInventory: (id) => http.delete(`/merchant/inventory/${id}`),
|
||||||
|
|
||||||
adminOverview: () => http.get('/api/admin/overview'),
|
adminOverview: () => http.get('/admin/overview'),
|
||||||
adminUsers: () => http.get('/api/admin/users'),
|
adminUsers: () => http.get('/admin/users'),
|
||||||
adminSaveUser: (payload) => http.post('/api/admin/users', payload),
|
adminSaveUser: (payload) => http.post('/admin/users', payload),
|
||||||
adminDeleteUser: (id) => http.delete(`/api/admin/users/${id}`),
|
adminDeleteUser: (id) => http.delete(`/admin/users/${id}`),
|
||||||
adminOrders: () => http.get('/api/admin/orders'),
|
adminOrders: () => http.get('/admin/orders'),
|
||||||
adminUpdateOrder: (id, payload) => http.put(`/api/admin/orders/${id}`, payload),
|
adminUpdateOrder: (id, payload) => http.put(`/admin/orders/${id}`, payload),
|
||||||
adminOrderRisks: () => http.get('/api/admin/orders/risk'),
|
adminOrderRisks: () => http.get('/admin/orders/risk'),
|
||||||
adminAuditRefund: (id, payload) => http.put(`/api/admin/orders/${id}/refund-audit`, payload),
|
adminAuditRefund: (id, payload) => http.put(`/admin/orders/${id}/refund-audit`, payload),
|
||||||
adminAuditShipment: (id, payload) => http.put(`/api/admin/orders/${id}/ship-audit`, payload),
|
adminAuditShipment: (id, payload) => http.put(`/admin/orders/${id}/ship-audit`, payload),
|
||||||
adminMerchantApplications: () => http.get('/api/admin/merchant-applications'),
|
adminMerchantApplications: () => http.get('/admin/merchant-applications'),
|
||||||
adminAuditMerchantApplication: (id, payload) => http.put(`/api/admin/merchant-applications/${id}`, payload),
|
adminAuditMerchantApplication: (id, payload) => http.put(`/admin/merchant-applications/${id}`, payload),
|
||||||
adminBanners: () => http.get('/api/admin/banners'),
|
adminBanners: () => http.get('/admin/banners'),
|
||||||
adminSaveBanner: (payload) => http.post('/api/admin/banners', payload),
|
adminSaveBanner: (payload) => http.post('/admin/banners', payload),
|
||||||
adminDeleteBanner: (id) => http.delete(`/api/admin/banners/${id}`),
|
adminDeleteBanner: (id) => http.delete(`/admin/banners/${id}`),
|
||||||
adminProducts: () => http.get('/api/admin/products'),
|
adminProducts: () => http.get('/admin/products'),
|
||||||
adminProductViews: () => http.get('/api/admin/products/views'),
|
adminProductViews: () => http.get('/admin/products/views'),
|
||||||
adminSaveProduct: (payload) => http.post('/api/admin/products', payload),
|
adminSaveProduct: (payload) => http.post('/admin/products', payload),
|
||||||
adminApproveProduct: (id, payload) => http.put(`/api/admin/products/${id}/approve`, payload),
|
adminApproveProduct: (id, payload) => http.put(`/admin/products/${id}/approve`, payload),
|
||||||
adminDeleteProduct: (id) => http.delete(`/api/admin/products/${id}`),
|
adminDeleteProduct: (id) => http.delete(`/admin/products/${id}`),
|
||||||
adminReviews: () => http.get('/api/admin/reviews'),
|
adminReviews: () => http.get('/admin/reviews'),
|
||||||
adminLogistics: () => http.get('/api/admin/logistics'),
|
adminLogistics: () => http.get('/admin/logistics'),
|
||||||
adminInventory: () => http.get('/api/admin/inventory'),
|
adminInventory: () => http.get('/admin/inventory'),
|
||||||
|
|
||||||
// File Upload
|
|
||||||
uploadImage: (file) => {
|
uploadImage: (file) => {
|
||||||
const formData = new FormData()
|
const formData = new FormData()
|
||||||
formData.append('file', file)
|
formData.append('file', file)
|
||||||
@@ -74,7 +73,7 @@ export const api = {
|
|||||||
uploadFile: (file) => {
|
uploadFile: (file) => {
|
||||||
const formData = new FormData()
|
const formData = new FormData()
|
||||||
formData.append('file', file)
|
formData.append('file', file)
|
||||||
return http.post('/api/upload', formData, {
|
return http.post('/upload', formData, {
|
||||||
headers: { 'Content-Type': 'multipart/form-data' }
|
headers: { 'Content-Type': 'multipart/form-data' }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -109,13 +109,22 @@ const beforeUpload = (file) => {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
const onUploadSuccess = (response) => {
|
const onUploadSuccess = (uploadInfo) => {
|
||||||
if (response && response.data) {
|
const response = uploadInfo.response
|
||||||
bannerForm.imageUrl = response.data
|
console.log('Upload server response:', response)
|
||||||
Message.success('图片上传成功')
|
if (response && response.code === 0 && response.data) {
|
||||||
} else if (typeof response === 'string') {
|
const imageUrl = response.data
|
||||||
bannerForm.imageUrl = response
|
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('图片上传成功')
|
Message.success('图片上传成功')
|
||||||
|
} else {
|
||||||
|
console.warn('Unexpected response format:', response)
|
||||||
|
Message.warning('上传成功,但无法解析图片地址')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -145,13 +145,22 @@ const beforeUpload = (file) => {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
const onUploadSuccess = (response) => {
|
const onUploadSuccess = (uploadInfo) => {
|
||||||
if (response && response.data) {
|
const response = uploadInfo.response
|
||||||
productForm.imageUrl = response.data
|
console.log('Upload server response:', response)
|
||||||
Message.success('图片上传成功')
|
if (response && response.code === 0 && response.data) {
|
||||||
} else if (typeof response === 'string') {
|
const imageUrl = response.data
|
||||||
productForm.imageUrl = response
|
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('图片上传成功')
|
Message.success('图片上传成功')
|
||||||
|
} else {
|
||||||
|
console.warn('Unexpected response format:', response)
|
||||||
|
Message.warning('上传成功,但无法解析图片地址')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -129,13 +129,22 @@ const beforeUpload = (file) => {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
const onUploadSuccess = (response) => {
|
const onUploadSuccess = (uploadInfo) => {
|
||||||
if (response && response.data) {
|
const response = uploadInfo.response
|
||||||
productForm.imageUrl = response.data
|
console.log('Upload server response:', response)
|
||||||
Message.success('图片上传成功')
|
if (response && response.code === 0 && response.data) {
|
||||||
} else if (typeof response === 'string') {
|
const imageUrl = response.data
|
||||||
productForm.imageUrl = response
|
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('图片上传成功')
|
Message.success('图片上传成功')
|
||||||
|
} else {
|
||||||
|
console.warn('Unexpected response format:', response)
|
||||||
|
Message.warning('上传成功,但无法解析图片地址')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import vue from '@vitejs/plugin-vue'
|
|||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [vue()],
|
plugins: [vue()],
|
||||||
server: {
|
server: {
|
||||||
port: 5173,
|
port: 5174,
|
||||||
proxy: {
|
proxy: {
|
||||||
'/api': {
|
'/api': {
|
||||||
target: 'http://localhost:8080',
|
target: 'http://localhost:8080',
|
||||||
|
|||||||
Reference in New Issue
Block a user