refactor(api):重构API服务接口与实现

- 移除多余的接口定义文件,简化依赖关系- 更新控制器和服务实现类的注入方式-优化请求参数处理逻辑
- 统一响应数据结构格式- 调整方法签名以提高一致性
- 删除冗余注释和无用代码- 修改系统API调用路径引用位置
- 简化认证服务实现并移除不必要的抽象层
- 优化Excel文件解析相关功能
- 清理无用的工具类和配置项
- 调整错误上报机制的依赖注入方式
- 更新跟卖精灵服务的实现细节- 优化HTTP请求工具函数结构
- 移除废弃的缓存管理服务接口定义
- 调整设备配额检查逻辑复用性
- 优化订单服务的数据返回格式
- 更新产品服务中的数据处理方式
- 重构客户端账户控制器中的设备限制检查逻辑
This commit is contained in:
2025-10-21 10:15:33 +08:00
parent 17f03c3ade
commit 281ae6a846
29 changed files with 237 additions and 599 deletions

View File

@@ -1,35 +1,17 @@
import { http } from './http';
export const amazonApi = {
// 上传Excel文件解析ASIN列表
importAsinFromExcel(file: File) {
const formData = new FormData();
formData.append('file', file);
return http.upload<{ code: number, data: { asinList: string[], total: number }, msg: string | null }>('/api/amazon/import/asin', formData);
},
getProductsBatch(asinList: string[], batchId: string, region: string) {
return http.post<{ code: number, data: { products: any[] }, msg: string | null }>('/api/amazon/products/batch', { asinList, batchId, region });
getProductsBatch(asinList: string[], batchId: string, region: string, signal?: AbortSignal) {
return http.post<{ code: number, data: { products: any[] }, msg: string | null }>('/api/amazon/products/batch', { asinList, batchId, region }, signal);
},
getLatestProducts() {
return http.get<{ code: number, data: { products: any[] }, msg: string | null }>('/api/amazon/products/latest');
},
getProductsByBatch(batchId: string) {
return http.get<{ products: any[] }>(`/api/amazon/products/batch/${batchId}`);
},
updateProduct(productData: unknown) {
return http.post('/api/amazon/products/update', productData);
},
deleteProduct(productId: string) {
return http.post('/api/amazon/products/delete', { id: productId });
},
getProductStats() {
return http.get('/api/amazon/stats');
},
searchProducts(searchParams: Record<string, unknown>) {
return http.get('/api/amazon/products/search', searchParams);
},
openGenmaiSpirit() {
return http.post('/api/system/genmai/open');
},
}
};

View File

@@ -1,16 +1,13 @@
// HTTP 工具:统一管理后端服务配置和请求
export type HttpMethod = 'GET' | 'POST' | 'DELETE';
// 集中管理所有后端服务配置
export const CONFIG = {
CLIENT_BASE: 'http://localhost:8081',
// RUOYI_BASE: 'http://192.168.1.89:8085',
RUOYI_BASE: 'http://8.138.23.49:8085',
SSE_URL: 'http://8.138.23.49:8085/monitor/account/events'
//RUOYI_BASE: 'http://8.138.23.49:8085',
RUOYI_BASE: 'http://192.168.1.89:8085',
SSE_URL: 'http://192.168.1.89:8085/monitor/account/events'
} as const;
function resolveBase(path: string): string {
// RuoYi 后端路径:鉴权、设备、反馈、版本、工具
if (path.startsWith('/monitor/') || path.startsWith('/system/') || path.startsWith('/tool/banma')) {
return CONFIG.RUOYI_BASE;
}
@@ -26,16 +23,17 @@ function buildQuery(params?: Record<string, unknown>): string {
return query.toString() ? `?${query}` : '';
}
async function request<T>(path: string, options: RequestInit): Promise<T> {
// 获取token
let token = '';
async function getToken(): Promise<string> {
try {
const tokenModule = await import('../utils/token');
token = tokenModule.getToken() || '';
} catch (e) {
console.warn('获取token失败:', e);
return tokenModule.getToken() || '';
} catch {
return '';
}
}
async function request<T>(path: string, options: RequestInit & { signal?: AbortSignal }): Promise<T> {
const token = await getToken();
const res = await fetch(`${resolveBase(path)}${path}`, {
credentials: 'omit',
cache: 'no-store',
@@ -55,9 +53,6 @@ async function request<T>(path: string, options: RequestInit): Promise<T> {
const contentType = res.headers.get('content-type') || '';
if (contentType.includes('application/json')) {
const json: any = await res.json();
// 业务状态码判断:支持两种格式
// - erp_client_sb (本地服务): code=0 表示成功
// - RuoYi 后端: code=200 表示成功
if (json.code !== undefined && json.code !== 0 && json.code !== 200) {
throw new Error(json.msg || '请求失败');
}
@@ -68,13 +63,14 @@ async function request<T>(path: string, options: RequestInit): Promise<T> {
}
export const http = {
get<T>(path: string, params?: Record<string, unknown>) {
return request<T>(`${path}${buildQuery(params)}`, { method: 'GET' });
get<T>(path: string, params?: Record<string, unknown>, signal?: AbortSignal) {
return request<T>(`${path}${buildQuery(params)}`, { method: 'GET', signal });
},
post<T>(path: string, body?: unknown) {
post<T>(path: string, body?: unknown, signal?: AbortSignal) {
return request<T>(path, {
method: 'POST',
body: body ? JSON.stringify(body) : undefined
body: body ? JSON.stringify(body) : undefined,
signal
});
},
@@ -82,42 +78,31 @@ export const http = {
return request<T>(path, { method: 'DELETE' });
},
async upload<T>(path: string, form: FormData) {
// 获取token
let token = '';
try {
const tokenModule = await import('../utils/token');
token = tokenModule.getToken() || '';
} catch (e) {
console.warn('获取token失败:', e);
}
const headers: Record<string, string> = {};
if (token) {
headers['Authorization'] = `Bearer ${token}`;
}
return fetch(`${resolveBase(path)}${path}`, {
async upload<T>(path: string, form: FormData, signal?: AbortSignal) {
const token = await getToken();
const res = await fetch(`${resolveBase(path)}${path}`, {
method: 'POST',
body: form,
credentials: 'omit',
cache: 'no-store',
headers
}).then(async res => {
if (!res.ok) {
const text = await res.text().catch(() => '');
throw new Error(text || `HTTP ${res.status}`);
}
const contentType = res.headers.get('content-type') || '';
if (contentType.includes('application/json')) {
const json: any = await res.json();
if (json.code !== undefined && json.code !== 0 && json.code !== 200) {
throw new Error(json.msg || '请求失败');
}
return json as T;
}
return (await res.text()) as unknown as T;
headers: token ? { 'Authorization': `Bearer ${token}` } : {},
signal
});
if (!res.ok) {
const text = await res.text().catch(() => '');
throw new Error(text || `HTTP ${res.status}`);
}
const contentType = res.headers.get('content-type') || '';
if (contentType.includes('application/json')) {
const json: any = await res.json();
if (json.code !== undefined && json.code !== 0 && json.code !== 200) {
throw new Error(json.msg || '请求失败');
}
return json as T;
}
return (await res.text()) as unknown as T;
}
};

View File

@@ -0,0 +1,12 @@
import { http } from './http';
export const systemApi = {
openGenmaiSpirit() {
return http.post('/api/system/genmai/open');
},
clearCache() {
return http.post('/api/system/cache/clear');
}
};

View File

@@ -18,23 +18,11 @@ export const zebraApi = {
return http.get('/api/banma/shops', params as Record<string, unknown>)
},
getOrders(params: any) {
return http.get('/api/banma/orders', params as Record<string, unknown>)
},
getOrdersByBatch(batchId: string) {
return http.get(`/api/banma/orders/batch/${batchId}`)
getOrders(params: any, signal?: AbortSignal) {
return http.get('/api/banma/orders', params as Record<string, unknown>, signal)
},
getLatestOrders() {
return http.get('/api/banma/orders/latest')
},
getOrderStats() {
return http.get('/api/banma/orders/stats')
},
searchOrders(searchParams: Record<string, unknown>) {
return http.get('/api/banma/orders/search', searchParams)
}
}

View File

@@ -2,6 +2,7 @@
import { ref, computed, onMounted, defineAsyncComponent, inject } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { amazonApi } from '../../api/amazon'
import { systemApi } from '../../api/system'
import { handlePlatformFileExport } from '../../utils/settings'
const TrialExpiredDialog = defineAsyncComponent(() => import('../common/TrialExpiredDialog.vue'))
@@ -21,6 +22,7 @@ const progressVisible = ref(false) // 进度条是否显示(完成后仍保留
const localProductData = ref<any[]>([]) // 本地产品数据
const currentAsin = ref('') // 当前处理的ASIN
const genmaiLoading = ref(false) // Genmai Spirit加载状态
let abortController: AbortController | null = null // 请求取消控制器
// 分页配置
const currentPage = ref(1)
@@ -130,7 +132,7 @@ async function batchGetProductInfo(asinList: string[]) {
currentAsin.value = `正在处理第${i + 1}/${totalBatches}批 (${batchAsins.join(', ')})`
try {
const result = await amazonApi.getProductsBatch(batchAsins, batchId, region.value)
const result = await amazonApi.getProductsBatch(batchAsins, batchId, region.value, abortController?.signal)
if (result?.data?.products?.length > 0) {
localProductData.value.push(...result.data.products)
@@ -142,7 +144,8 @@ async function batchGetProductInfo(asinList: string[]) {
const actualCount = result?.data?.products?.length || 0
failedCount += Math.max(0, expectedCount - actualCount)
} catch (error) {
} catch (error: any) {
if (error.name === 'AbortError') break
failedCount += batchAsins.length
console.error(`批次${i + 1}失败:`, error)
}
@@ -164,8 +167,10 @@ async function batchGetProductInfo(asinList: string[]) {
} catch (error: any) {
showMessage(error.message || '批量获取产品信息失败', 'error')
currentAsin.value = '处理失败'
if (error.name !== 'AbortError') {
showMessage(error.message || '批量获取产品信息失败', 'error')
currentAsin.value = '处理失败'
}
} finally {
tableLoading.value = false
}
@@ -177,6 +182,7 @@ async function startQueuedFetch() {
showMessage('请先导入ASIN列表', 'warning')
return
}
abortController = new AbortController()
loading.value = true
progressVisible.value = true
tableLoading.value = true
@@ -185,6 +191,7 @@ async function startQueuedFetch() {
} finally {
tableLoading.value = false
loading.value = false
abortController = null
}
}
@@ -240,12 +247,13 @@ function isOutOfStock(product: any) {
// 停止获取操作
function stopFetch() {
abortController?.abort()
abortController = null
loading.value = false
currentAsin.value = '已停止'
showMessage('已停止获取产品数据', 'info')
}
// 打开Genmai Spirit工具
async function openGenmaiSpirit() {
try {
await ElMessageBox.confirm('打开跟卖精灵会关闭所有谷歌浏览器进程,是否继续?', '提示', {
@@ -255,7 +263,7 @@ async function openGenmaiSpirit() {
})
genmaiLoading.value = true
try {
await amazonApi.openGenmaiSpirit()
await systemApi.openGenmaiSpirit()
} catch (error: any) {
showMessage(error.message || '打开跟卖精灵失败', 'error')
} finally {

View File

@@ -40,6 +40,7 @@ const fetchCurrentPage = ref(1)
const fetchTotalPages = ref(0)
const fetchTotalItems = ref(0)
const isFetching = ref(false)
let abortController: AbortController | null = null
// 试用期过期弹框
const showTrialExpiredDialog = ref(false)
@@ -113,6 +114,7 @@ async function fetchData() {
return
}
abortController = new AbortController()
loading.value = true
isFetching.value = true
showProgress.value = true
@@ -130,7 +132,7 @@ async function fetchPageData(startDate: string, endDate: string) {
if (!isFetching.value) return
try {
const data = await zebraApi.getOrders({
const response = await zebraApi.getOrders({
accountId: Number(accountId.value) || undefined,
startDate,
endDate,
@@ -138,8 +140,9 @@ async function fetchPageData(startDate: string, endDate: string) {
pageSize: 50,
shopIds: selectedShops.value.join(','),
batchId: currentBatchId.value
})
}, abortController?.signal)
const data = (response as any)?.data || response
const orders = data.orders || []
allOrderData.value = [...allOrderData.value, ...orders]
@@ -154,8 +157,10 @@ async function fetchPageData(startDate: string, endDate: string) {
progressPercentage.value = 100
finishFetching()
}
} catch (e) {
console.error('获取订单数据失败:', e)
} catch (e: any) {
if (e.name !== 'AbortError') {
console.error('获取订单数据失败:', e)
}
finishFetching()
}
}
@@ -163,6 +168,7 @@ async function fetchPageData(startDate: string, endDate: string) {
function finishFetching() {
isFetching.value = false
loading.value = false
abortController = null
// 确保进度条完全填满
progressPercentage.value = 100
currentPage.value = 1
@@ -170,6 +176,8 @@ function finishFetching() {
}
function stopFetch() {
abortController?.abort()
abortController = null
isFetching.value = false
loading.value = false
// 进度条保留显示,不自动隐藏