daShangDao_miniProgram/service/selectBookImage.js
2025-11-24 10:25:20 +08:00

378 lines
11 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 获取在售商品信息
* @param {String} keyword - 搜索关键词ISBN或书名
* @param {String} sortType - 排序类型
* @param {String} conditionValue - 品相条件
* @param {String} cookies - 用户cookies
* @param {Object} options - 可选参数对象
* @param {String} options.publisher - 出版社(版权页比价时使用)
* @param {String} options.author - 作者(版权页比价时使用)
* @param {Boolean} options.autoSwitchAccount - 是否自动切换账号默认为true
* @param {Number} options.retryCount - 重试次数(内部使用)
* @returns {Promise<Array>} - 返回在售商品信息数组
*/
export const fetchOnSaleProducts = async (keyword, sortType, conditionValue, cookies, options = {}) => {
try {
// 设置默认值
const autoSwitchAccount = options.autoSwitchAccount !== false; // 默认为true
const retryCount = options.retryCount || 0;
const MAX_RETRY = 3; // 最大重试次数
// 首先检查cookies是否存在不存在则尝试从本地获取最新的
if (!cookies) {
cookies = uni.getStorageSync('UserInfoCookies');
console.log('从本地存储获取cookies:', cookies);
}
// 如果还是没有cookies提示用户登录
if (!cookies) {
uni.showToast({
title: '请先在设置页面登录孔网账号',
icon: 'none',
duration: 2000
});
return [];
}
// 构建直接调用孔夫子API的参数
const searchParams = {
searchType: 'category',
dataType: '0',
page: '1',
keyword: keyword,
sortType: sortType,
quality: conditionValue,
actionPath: 'sortType,quality',
quaSelect: '2',
userArea: '13003000000'
};
// 如果是版权页比价,添加出版社和作者参数
if (options.publisher) {
searchParams.press = options.publisher;
}
if (options.author) {
searchParams.author = options.author;
}
// 构建URL查询字符串
const queryString = Object.entries(searchParams)
.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
.join('&');
// 孔夫子API的URL
const apiUrl = `https://search.kongfz.com/pc-gw/search-web/client/pc/product/keyword/list?${queryString}`;
// 调用孔夫子API
const [err, res] = await uni.request({
url: apiUrl,
method: 'GET',
header: {
'Cookie': `PHPSESSID=${cookies}`
}
});
// 处理错误:请求失败时 err 不为 null
if (err) {
console.error('请求失败:', err);
// 如果允许自动切换账号且未超过最大重试次数
if (autoSwitchAccount && retryCount < MAX_RETRY) {
console.log(`尝试切换账号并重试(${retryCount + 1}/${MAX_RETRY})...`);
const newCookie = await tryNextAccount();
if (newCookie) {
// 使用新cookie重试
return fetchOnSaleProducts(keyword, sortType, conditionValue, newCookie, {
...options,
retryCount: retryCount + 1
});
}
}
uni.showToast({
title: '网络请求失败',
icon: 'none'
});
return [];
}
// 处理接口返回的数据
console.log("res", res);
const responseData = res.data || {}; // 避免 res.data 为 undefined
console.log("在售商品原始数据", responseData);
// 检查孔夫子API返回的状态
if (!responseData || responseData.status !== 1) {
const errorMsg = responseData.message || '请求出错';
console.error('孔夫子API错误:', errorMsg);
// 检查是否是登录失效
const isLoginExpired = errorMsg.includes('登录') ||
errorMsg.includes('cookie') ||
(responseData.errCode && ["102", "1000", "1001", "1002", "1003", "1004", "1005", "1006", "1007", "1008", "1009"].includes(responseData.errCode))||
(responseData.errType && ["102", "1000", "1001", "1002", "1003", "1004", "1005", "1006", "1007", "1008", "1009"].includes(responseData.errType));
console.log("isLoginExpired", isLoginExpired);
if (isLoginExpired) {
// 如果允许自动切换账号且未超过最大重试次数
if (autoSwitchAccount && retryCount < MAX_RETRY) {
console.log(`登录已过期,尝试切换账号并重试(${retryCount + 1}/${MAX_RETRY})...`);
const newCookie = await tryNextAccount();
if (newCookie) {
// 使用新cookie重试
return fetchOnSaleProducts(keyword, sortType, conditionValue, newCookie, {
...options,
retryCount: retryCount + 1
});
}
}
uni.showToast({
title: '登录已过期,请重新登录',
icon: 'none',
duration: 2000
});
} else {
uni.showToast({
title: errorMsg,
icon: 'none',
duration: 2000
});
}
return [];
}
// 解析孔夫子API的响应数据
const products = [];
if (responseData.data && responseData.data.itemResponse && responseData.data.itemResponse.list) {
const list = responseData.data.itemResponse.list;
// 只取前12条记录
const limit = Math.min(12, list.length);
for (let i = 0; i < limit; i++) {
const item = list[i];
// 解析运费信息
let shippingFee = 0;
let shippingFeeText = '0';
if (item.postage && item.postage.shippingList && item.postage.shippingList.length > 0) {
const shipping = item.postage.shippingList[0];
shippingFee = parseFloat(shipping.shippingFee || 0);
shippingFeeText = shipping.shippingFeeText || '0';
}
// 处理价格,去除非数字字符
const priceText = item.priceText || '0';
const cleanPrice = parseFloat(priceText.replace(/[^\d.]/g, ''));
const totalPrice = Number((cleanPrice + shippingFee).toFixed(2));
products.push({
id: item.id || null,
bookName: item.title || '未知书名',
imageUrl: item.imgBigUrl ? item.imgBigUrl.trim() : null,
totalPrice: totalPrice,
bookPrice: cleanPrice,
shopName: item.shopName,
shippingFee: shippingFee,
shippingFeeText: shippingFeeText,
author: item.author || '未知作者',
publisher: item.press || '未知出版社',
qualityText: item.qualityText || '未知品相',
});
}
}
console.log('获取在售商品信息成功:', products);
return products;
} catch (error) {
console.error('获取在售商品信息失败:', error);
uni.showToast({
title: '获取在售商品信息失败',
icon: 'none'
});
return [];
}
};
/**
* 刷新cookies
* @param {String} username - 孔网账号
* @param {String} password - 孔网密码
* @returns {Promise<Object>} - 返回登录结果包含cookies
*/
const refreshCookies = async (username, password) => {
try {
// 第一步先发送一个GET请求获取初始会话Cookie
const initResponse = await uniRequestPromise({
url: 'https://login.kongfz.com/Pc/Login/account',
method: 'GET'
});
// 提取初始响应中的Cookie
const initCookies = extractCookiesFromHeaders(initResponse.header);
// 第二步:发送登录请求,携带用户名和密码
const loginData = {
loginName: username,
loginPass: password
};
const loginResponse = await uniRequestPromise({
url: 'https://login.kongfz.com/Pc/Login/account',
method: 'POST',
data: loginData,
header: {
'Content-Type': 'application/x-www-form-urlencoded',
'Cookie': formatCookieHeader(initCookies)
}
});
// 提取登录响应中的Cookie
const loginCookies = extractCookiesFromHeaders(loginResponse.header);
// 合并所有Cookie
const allCookies = {
...initCookies,
...loginCookies
};
return {
success: true,
cookies: allCookies,
responseData: loginResponse.data
};
} catch (error) {
console.error('刷新cookies失败:', error);
return {
success: false,
error: error.message || '登录请求发生错误'
};
}
};
/**
* 尝试切换到下一个可用账号并获取新cookie
* @returns {Promise<String|null>} 返回新的cookie或null如果无法切换账号
*/
const tryNextAccount = async () => {
try {
// 获取账号列表
const accountsStr = uni.getStorageSync('accounts');
if (!accountsStr) {
console.log('没有可用的备用账号');
return null;
}
const accounts = JSON.parse(accountsStr);
if (!accounts || accounts.length === 0) {
console.log('账号列表为空');
return null;
}
// 获取当前活跃账号索引
let currentIndex = uni.getStorageSync('currentAccountIndex');
currentIndex = currentIndex !== '' ? parseInt(currentIndex) : 0;
// 计算下一个账号索引
const nextIndex = (currentIndex + 1) % accounts.length;
// 如果循环回到了当前账号,说明只有一个账号或已经尝试了所有账号
if (nextIndex === currentIndex) {
console.log('已尝试所有账号,无法继续切换');
return null;
}
// 获取下一个账号信息
const nextAccount = accounts[nextIndex];
// 清除原有cookie
uni.removeStorageSync('cookies');
uni.removeStorageSync('UserInfoCookies');
// 使用下一个账号登录
const result = await refreshCookies(nextAccount.username, nextAccount.password);
// 检查登录结果
if (result.success && result.cookies && result.cookies.PHPSESSID) {
// 更新账号状态
accounts.forEach(acc => acc.isActive = false);
accounts[nextIndex].isActive = true;
// 保存cookie
const newCookie = result.cookies.PHPSESSID;
uni.setStorageSync('cookies', newCookie);
uni.setStorageSync('UserInfoCookies', newCookie);
uni.setStorageSync('KongfzUserName', nextAccount.username);
// 保存更新后的账号列表和当前索引
uni.setStorageSync('accounts', JSON.stringify(accounts));
uni.setStorageSync('currentAccountIndex', nextIndex);
// 发送自定义事件通知页面更新账号状态
uni.$emit('accountSwitched', {
index: nextIndex,
username: nextAccount.username
});
console.log(`成功切换到账号: ${nextAccount.username}`);
return newCookie;
} else {
console.log('切换账号失败,尝试下一个账号');
// 递归尝试下一个账号
accounts[nextIndex].isActive = false;
uni.setStorageSync('accounts', JSON.stringify(accounts));
uni.setStorageSync('currentAccountIndex', nextIndex);
return tryNextAccount();
}
} catch (error) {
console.error('切换账号出错:', error);
return null;
}
};
/**
* 将 uni.request 转换为 Promise 形式
*/
const uniRequestPromise = (options) => {
return new Promise((resolve, reject) => {
uni.request({
...options,
success: (res) => resolve(res),
fail: (err) => reject(err)
});
});
};
/**
* 从响应头中提取 Cookies
*/
const extractCookiesFromHeaders = (headers) => {
const cookies = {};
const cookieHeaders = headers['Set-Cookie'] || headers['set-cookie'];
if (!cookieHeaders) return cookies;
// 处理可能是数组或字符串的 Cookie 头
const cookieList = Array.isArray(cookieHeaders) ?
cookieHeaders : [cookieHeaders];
cookieList.forEach(cookieStr => {
// 提取 cookie 名值对(忽略路径、过期时间等属性)
const cookieParts = cookieStr.split(';')[0].split('=');
if (cookieParts.length >= 2) {
cookies[cookieParts[0].trim()] = cookieParts[1].trim();
}
});
return cookies;
};
/**
* 将 Cookie 对象格式化为请求头字符串
*/
const formatCookieHeader = (cookies) => {
return Object.entries(cookies)
.map(([key, value]) => `${key}=${value}`)
.join('; ');
};