This commit is contained in:
Felix
2025-12-05 21:08:43 +08:00
parent 586557935d
commit 1409d05771

View File

@@ -462,7 +462,28 @@ class ApiManager {
async smartLogin(forceRefresh: boolean = false): Promise<ILoginResponse | null> {
if (USE_CLOUD) {
const app = getApp<IAppOption>()
const dictLevel = wx.getStorageSync('dictLevel') || 'PRIMARY'
let dictLevel = wx.getStorageSync('dictLevel') || 'LEVEL1'
try {
const resp = await this.request<{ user: IUserInfo; dict_level?: string; session_uuid?: string; settings?: { dict_level?: string }; points?: { balance: number; expired_time: string } }>(
'/api/v1/wx/user',
'GET',
undefined,
false
)
const data = (resp as any)?.data || resp
if (data.user) {
app.globalData.userInfo = data.user
wx.setStorageSync('userInfo', data.user)
}
const dl = data.dict_level || 'LEVEL1'
if (dl) {
dictLevel = dl
wx.setStorageSync('dictLevel', dictLevel)
}
if (data.session_uuid) {
wx.setStorageSync('sessionUuid', data.session_uuid)
}
} catch (e) {}
app.globalData.isLoggedIn = true
app.globalData.dictLevel = dictLevel
return {
@@ -483,12 +504,12 @@ class ApiManager {
if (!isExpired) {
app.globalData.isLoggedIn = true
app.globalData.token = authInfo.token
app.globalData.dictLevel = authInfo.dictLevel || 'PRIMARY'
app.globalData.dictLevel = authInfo.dictLevel || 'LEVEL1'
return {
access_token: authInfo.token,
access_token_expire_time: new Date(authInfo.tokenExpiry).toISOString(),
session_uuid: authInfo.sessionUuid || '',
dict_level: authInfo.dictLevel || 'PRIMARY'
dict_level: authInfo.dictLevel || 'LEVEL1'
}
} else {
this.clearAuthData()
@@ -641,67 +662,89 @@ class ApiManager {
// 上传文件第一步上传文件获取ID
async uploadFile(filePath: string, retryCount: number = 0): Promise<string> {
const maxRetries = 1 // 最多重试1次
if (USE_CLOUD) {
return new Promise((resolve, reject) => {
const fs = wx.getFileSystemManager()
fs.getFileInfo({
filePath,
success: async (infoRes) => {
try {
const filename = this.extractFileName(filePath)
const mime = this.getMimeType(filePath)
const initResp = await this.wx_request<{ file_id: string; cloud_path: string; env: string }>(
'/api/v1/file/upload/init',
'POST',
{ filename, size: infoRes.size, mime, biz_type: 'file' }
)
const initData = (initResp as any).data || initResp
const file_id = initData.file_id
const cloud_path = initData.cloud_path
const env = initData.env
const task = wx.cloud.uploadFile({
cloudPath: cloud_path,
filePath,
config: { env },
success: async (upRes) => {
try {
const completeResp = await this.wx_request<{ id: string; url?: string }>(
'/api/v1/file/upload/complete',
'POST',
{ file_id, cloud_path, fileID: upRes.fileID, size: infoRes.size, mime }
)
resolve(file_id)
} catch (e) {
reject(e)
}
},
fail: (e) => {
const info = String(e)
if (info.indexOf('abort') !== -1) {
reject(new Error('上传中断'))
} else {
reject(new Error('上传失败'))
}
}
})
} catch (err) {
reject(err)
}
},
fail: (err) => reject(err)
})
})
}
const maxRetries = 1
return new Promise(async (resolve, reject) => {
console.log('上传文件请求:', { filePath, retryCount })
wx.uploadFile({
url: `${BASE_URL}/api/v1/file/upload`,
filePath,
name: 'file', // 根据API要求调整参数名
name: 'file',
header: this.getHeaders(),
success: async (res) => {
console.log('上传文件响应:', {
statusCode: res.statusCode,
data: res.data,
retryCount
})
if (res.statusCode === 200) {
try {
const response = JSON.parse(res.data) as IApiResponse<{ id: string }>
if (response.code === 0 || response.code === 200) {
if (response.data?.id) {
console.log('文件上传成功获得ID:', response.data.id)
resolve(response.data.id)
} else {
reject(new Error('服务器返回数据中缺少文件ID'))
}
} else {
const errorMsg = response.message || response.msg || '文件上传失败'
console.error('文件上传业务错误:', errorMsg, response)
wx.showToast({
title: errorMsg,
icon: 'none'
})
wx.showToast({ title: errorMsg, icon: 'none' })
reject(new Error(errorMsg))
}
} catch (error) {
console.error('文件上传响应解析错误:', error)
wx.showToast({
title: '数据解析错误',
icon: 'none'
})
wx.showToast({ title: '数据解析错误', icon: 'none' })
reject(error)
}
} else if (res.statusCode === 401) {
console.warn('文件上传401认证失败尝试自动登录', { retryCount, maxRetries })
// 如果还有重试机会,尝试自动登录并重试请求
if (retryCount < maxRetries) {
try {
console.log('开始文件上传401自动登录重试')
// 清理过期的认证数据
this.clearAuthData()
// 进行智能登录
const loginResult = await this.smartLogin(true) // 强制刷新token
const loginResult = await this.smartLogin(true)
if (loginResult) {
console.log('文件上传401自动登录成功重试请求')
// 登录成功后重试原请求
try {
const retryResult = await this.uploadFile(filePath, retryCount + 1)
resolve(retryResult)
@@ -709,64 +752,24 @@ class ApiManager {
reject(retryError)
}
} else {
console.error('文件上传401自动登录失败')
this.handleTokenExpired()
reject(new Error('登录已过期'))
}
} catch (loginError) {
console.error('文件上传401自动登录发生错误:', loginError)
this.handleTokenExpired()
reject(new Error('登录已过期'))
}
} else {
// 重试次数耗尽直接处理token过期
console.error('文件上传401重试次数耗尽跳转登录页')
this.handleTokenExpired()
reject(new Error('登录已过期'))
}
} else {
// Check for 413 Request Entity Too Large error
if (res.statusCode === 413) {
// Get file size for debugging
const fs = wx.getFileSystemManager();
fs.getFileInfo({
filePath: filePath,
success: (infoRes) => {
const fileSizeInMB = (infoRes.size / (1024 * 1024)).toFixed(4);
console.error('文件上传413错误 - 请求实体过大:', {
statusCode: res.statusCode,
fileSize: `${fileSizeInMB} MB`,
fileSizeInBytes: infoRes.size,
filePath: filePath,
data: res.data
});
},
fail: (infoError) => {
console.error('文件上传413错误 - 无法获取文件大小:', {
statusCode: res.statusCode,
filePath: filePath,
fileInfoError: infoError,
data: res.data
});
}
});
} else {
console.error('文件上传HTTP错误:', res.statusCode, res.data);
}
wx.showToast({
title: '文件上传失败',
icon: 'none'
});
reject(new Error(`HTTP ${res.statusCode}`));
wx.showToast({ title: '文件上传失败', icon: 'none' })
reject(new Error(`HTTP ${res.statusCode}`))
}
},
fail: (error) => {
console.error('文件上传请求失败:', error)
wx.showToast({
title: '文件上传失败',
icon: 'none'
})
wx.showToast({ title: '文件上传失败', icon: 'none' })
reject(error)
}
})
@@ -775,67 +778,89 @@ class ApiManager {
// 上传文件第一步上传文件获取ID
async uploadImageFile(filePath: string, retryCount: number = 0): Promise<string> {
const maxRetries = 1 // 最多重试1次
if (USE_CLOUD) {
return new Promise((resolve, reject) => {
const fs = wx.getFileSystemManager()
fs.getFileInfo({
filePath,
success: async (infoRes) => {
try {
const filename = this.extractFileName(filePath)
const mime = this.getMimeType(filePath)
const initResp = await this.wx_request<{ file_id: string; cloud_path: string; env: string }>(
'/api/v1/file/upload/init',
'POST',
{ filename, size: infoRes.size, mime, biz_type: 'image' }
)
const initData = (initResp as any).data || initResp
const file_id = initData.file_id
const cloud_path = initData.cloud_path
const env = initData.env
const task = wx.cloud.uploadFile({
cloudPath: cloud_path,
filePath,
config: { env },
success: async (upRes) => {
try {
const completeResp = await this.wx_request<{ id: string; url?: string }>(
'/api/v1/file/upload/complete',
'POST',
{ file_id, cloud_path, fileID: upRes.fileID, size: infoRes.size, mime }
)
resolve(file_id)
} catch (e) {
reject(e)
}
},
fail: (e) => {
const info = String(e)
if (info.indexOf('abort') !== -1) {
reject(new Error('上传中断'))
} else {
reject(new Error('上传失败'))
}
}
})
} catch (err) {
reject(err)
}
},
fail: (err) => reject(err)
})
})
}
const maxRetries = 1
return new Promise(async (resolve, reject) => {
console.log('上传文件请求:', { filePath, retryCount })
wx.uploadFile({
url: `${BASE_URL}/api/v1/file/upload_image`,
filePath,
name: 'file', // 根据API要求调整参数名
name: 'file',
header: this.getHeaders(),
success: async (res) => {
console.log('上传文件响应:', {
statusCode: res.statusCode,
data: res.data,
retryCount
})
if (res.statusCode === 200) {
try {
const response = JSON.parse(res.data) as IApiResponse<{ id: string }>
if (response.code === 0 || response.code === 200) {
if (response.data?.id) {
console.log('文件上传成功获得ID:', response.data.id)
resolve(response.data.id)
} else {
reject(new Error('服务器返回数据中缺少文件ID'))
}
} else {
const errorMsg = response.message || response.msg || '文件上传失败'
console.error('文件上传业务错误:', errorMsg, response)
wx.showToast({
title: errorMsg,
icon: 'none'
})
wx.showToast({ title: errorMsg, icon: 'none' })
reject(new Error(errorMsg))
}
} catch (error) {
console.error('文件上传响应解析错误:', error)
wx.showToast({
title: '数据解析错误',
icon: 'none'
})
wx.showToast({ title: '数据解析错误', icon: 'none' })
reject(error)
}
} else if (res.statusCode === 401) {
console.warn('文件上传401认证失败尝试自动登录', { retryCount, maxRetries })
// 如果还有重试机会,尝试自动登录并重试请求
if (retryCount < maxRetries) {
try {
console.log('开始文件上传401自动登录重试')
// 清理过期的认证数据
this.clearAuthData()
// 进行智能登录
const loginResult = await this.smartLogin(true) // 强制刷新token
const loginResult = await this.smartLogin(true)
if (loginResult) {
console.log('文件上传401自动登录成功重试请求')
// 登录成功后重试原请求
try {
const retryResult = await this.uploadFile(filePath, retryCount + 1)
resolve(retryResult)
@@ -843,78 +868,57 @@ class ApiManager {
reject(retryError)
}
} else {
console.error('文件上传401自动登录失败')
this.handleTokenExpired()
reject(new Error('登录已过期'))
}
} catch (loginError) {
console.error('文件上传401自动登录发生错误:', loginError)
this.handleTokenExpired()
reject(new Error('登录已过期'))
}
} else {
// 重试次数耗尽直接处理token过期
console.error('文件上传401重试次数耗尽跳转登录页')
this.handleTokenExpired()
reject(new Error('登录已过期'))
}
} else {
// Check for 413 Request Entity Too Large error
if (res.statusCode === 413) {
// Get file size for debugging
const fs = wx.getFileSystemManager();
fs.getFileInfo({
filePath: filePath,
success: (infoRes) => {
const fileSizeInMB = (infoRes.size / (1024 * 1024)).toFixed(4);
console.error('文件上传413错误 - 请求实体过大:', {
statusCode: res.statusCode,
fileSize: `${fileSizeInMB} MB`,
fileSizeInBytes: infoRes.size,
filePath: filePath,
data: res.data
});
},
fail: (infoError) => {
console.error('文件上传413错误 - 无法获取文件大小:', {
statusCode: res.statusCode,
filePath: filePath,
fileInfoError: infoError,
data: res.data
});
}
});
} else {
console.error('文件上传HTTP错误:', res.statusCode, res.data);
}
wx.showToast({
title: '文件上传失败',
icon: 'none'
});
reject(new Error(`HTTP ${res.statusCode}`));
wx.showToast({ title: '文件上传失败', icon: 'none' })
reject(new Error(`HTTP ${res.statusCode}`))
}
},
fail: (error) => {
console.error('文件上传请求失败:', error)
wx.showToast({
title: '文件上传失败',
icon: 'none'
})
wx.showToast({ title: '文件上传失败', icon: 'none' })
reject(error)
}
})
})
}
private extractFileName(path: string): string {
const i = path.lastIndexOf('/')
return i >= 0 ? path.substring(i + 1) : path
}
private getMimeType(path: string): string {
const i = path.lastIndexOf('.')
const ext = i >= 0 ? path.substring(i + 1).toLowerCase() : ''
const map: Record<string, string> = {
jpg: 'image/jpeg',
jpeg: 'image/jpeg',
png: 'image/png',
gif: 'image/gif',
webp: 'image/webp',
bmp: 'image/bmp'
}
return map[ext] || 'application/octet-stream'
}
// 图片识别第二步通过文件ID进行识别
async recognizeImageAsync(fileId: string, type: string = 'word'): Promise<{task_id: string, status: string, message: string}> {
console.log('开始图片识别请求:', { fileId, type })
// 获取当前的词典等级配置
const app = getApp<IAppOption>()
const dictLevel = app.globalData.dictLevel || wx.getStorageSync('dictLevel') || 'PRIMARY'
const dictLevel = app.globalData.dictLevel || wx.getStorageSync('dictLevel') || 'LEVEL1'
console.log('开始图片识别请求:', { fileId, type, dictLevel })
const response = await this.request<{task_id: string, status: string, message: string}>('/api/v1/image/recognize/async', 'POST', {
file_id: fileId,
type: type,
@@ -1578,56 +1582,77 @@ class ApiManager {
// 安全下载文件接口
async downloadFile(fileId: string): Promise<string> {
if (USE_CLOUD) {
try {
const resp = await this.wx_request<{ url: string }>(`/api/v1/file/temp_url/${fileId}`, 'GET')
const tempUrl = (resp as any)?.data?.url || (resp as any)?.url
if (!tempUrl) {
throw new Error('无法获取临时下载链接')
}
return new Promise((resolve, reject) => {
wx.downloadFile({
url: tempUrl,
success: (res) => {
if (res.statusCode === 200) {
const fs = wx.getFileSystemManager()
const tempPath = res.tempFilePath
const extMatch = tempPath && tempPath.match(/\.[a-zA-Z0-9]+$/)
if (!extMatch) {
resolve(tempPath)
return
}
const ext = extMatch[0]
const filePath = `${wx.env.USER_DATA_PATH}/${fileId}${ext}`
fs.saveFile({
tempFilePath: tempPath,
filePath,
success: () => resolve(filePath),
fail: () => resolve(tempPath)
})
} else {
reject(new Error(`文件下载失败,状态码: ${res.statusCode}`))
}
},
fail: (error) => reject(new Error('文件下载失败'))
})
})
} catch (e) {
throw e
}
}
return new Promise((resolve, reject) => {
// console.log('开始安全下载文件:', fileId);
// 构建文件下载URL
const fileUrl = `${BASE_URL}/api/v1/file/${fileId}`;
// 获取认证头
const headers = this.getHeaders();
// 使用微信的下载文件API
const fileUrl = `${BASE_URL}/api/v1/file/${fileId}`
const headers = this.getHeaders()
wx.downloadFile({
url: fileUrl,
header: headers,
success: (res) => {
if (res.statusCode === 200) {
const fs = wx.getFileSystemManager();
const tempPath = res.tempFilePath;
const extMatch = tempPath && tempPath.match(/\.[a-zA-Z0-9]+$/);
const fs = wx.getFileSystemManager()
const tempPath = res.tempFilePath
const extMatch = tempPath && tempPath.match(/\.[a-zA-Z0-9]+$/)
if (!extMatch) {
resolve(tempPath);
return;
resolve(tempPath)
return
}
const ext = extMatch[0];
const filePath = `${wx.env.USER_DATA_PATH}/${fileId}${ext}`;
const ext = extMatch[0]
const filePath = `${wx.env.USER_DATA_PATH}/${fileId}${ext}`
fs.saveFile({
tempFilePath: tempPath,
filePath,
success: () => {
resolve(filePath);
},
fail: () => {
resolve(tempPath);
}
});
success: () => resolve(filePath),
fail: () => resolve(tempPath)
})
} else if (res.statusCode === 401) {
// 处理认证失败
console.error('文件下载认证失败');
this.handleTokenExpired();
reject(new Error('认证失败,请重新登录'));
this.handleTokenExpired()
reject(new Error('认证失败,请重新登录'))
} else {
console.error('文件下载失败,状态码:', res.statusCode);
reject(new Error(`文件下载失败,状态码: ${res.statusCode}`));
reject(new Error(`文件下载失败,状态码: ${res.statusCode}`))
}
},
fail: (error) => {
console.error('文件下载请求失败:', error);
reject(new Error('文件下载失败'));
}
});
});
fail: (error) => reject(new Error('文件下载失败'))
})
})
}
// 将文件ID转换为可显示的图片URL