// 简化版API文件 - 仅包含必要功能 // 智能API地址选择器 - 根据环境自动切换基础URL let BASE_URL; // 配置常量 const SERVER_CONFIG = { PRODUCTION: 'https://youniao.icu', // 生产服务器地址 LOCALHOST: 'http://localhost:3003', // 本地服务器地址 DEFAULT_LOCAL_IP: 'http://192.168.1.100:3003' // 默认本地IP地址 }; // 强制清理可能导致端口错误的本地存储 function cleanupTestModeStorage() { try { wx.removeStorageSync('__TEST_MODE__'); wx.removeStorageSync('__TEST_SERVER_IP__'); wx.removeStorageSync('__DEVICE_TYPE__'); console.log('已清理可能导致端口错误的本地存储配置'); } catch (e) { console.warn('清理存储时出错:', e); } } // 立即执行清理 cleanupTestModeStorage(); // 重要提示:真机调试时,请确保以下操作: // 1. 将手机和开发机连接到同一WiFi网络 // 2. 修改上方DEFAULT_LOCAL_IP为您开发机的实际IP地址 // 3. 或者在test-mode-switch页面中设置自定义IP // 检测当前环境是否为真机调试 function isRealDevice() { try { // 检查是否有明确设置的设备类型标记 const deviceType = wx.getStorageSync('__DEVICE_TYPE__'); if (deviceType === 'real') return true; if (deviceType === 'simulator') return false; // 尝试通过系统信息判断环境 - 增强版 try { const deviceInfo = wx.getDeviceInfo(); // 明确识别开发者工具环境 if (deviceInfo && (deviceInfo.platform === 'devtools' || deviceInfo.environment === 'devtools')) { return false; } // 其他平台都认为是真机 return true; } catch (infoErr) { console.warn('系统信息获取失败,尝试其他方式判断环境:', infoErr); } // 最终默认:如果无法确定,优先认为是真机环境,确保用户体验 return true; } catch (e) { console.warn('环境检测失败,默认使用真机地址:', e); return true; } } // 设置测试模式 - 用于真机调试连接本地服务器 function setTestMode(enable, customIp = null) { try { wx.setStorageSync('__TEST_MODE__', enable); if (enable && customIp) { wx.setStorageSync('__TEST_SERVER_IP__', customIp); } console.log('测试模式设置:', enable ? '开启' : '关闭', customIp ? `自定义IP: ${customIp}` : ''); // 重新初始化基础URL initBaseUrl(); return true; } catch (e) { console.error('设置测试模式失败:', e); return false; } } // 获取当前是否为测试模式 function isTestMode() { try { return !!wx.getStorageSync('__TEST_MODE__'); } catch (e) { console.warn('获取测试模式失败,默认关闭:', e); return false; } } // 设置设备类型 - 手动控制环境类型 function setDeviceType(deviceType) { try { if (deviceType === 'real' || deviceType === 'simulator' || deviceType === null) { if (deviceType === null) { wx.removeStorageSync('__DEVICE_TYPE__'); console.log('设备类型设置已清除,将使用自动检测'); } else { wx.setStorageSync('__DEVICE_TYPE__', deviceType); console.log('设备类型设置为:', deviceType === 'real' ? '真机' : '模拟器'); } // 重新初始化基础URL initBaseUrl(); return true; } else { console.error('无效的设备类型,必须是 "real", "simulator" 或 null'); return false; } } catch (e) { console.error('设置设备类型失败:', e); return false; } } // 定义基础API导出 - 先不导出request,因为它在后面定义 const baseApi = { setTestMode: setTestMode, isTestMode: isTestMode, setDeviceType: setDeviceType } // 初始化基础URL - 增强版,更可靠的服务器地址选择策略 function initBaseUrl() { try { // 检查是否启用测试模式 const testMode = isTestMode(); const realDevice = isRealDevice(); console.log('环境检测结果:', { testMode: testMode, isRealDevice: realDevice }); // 测试模式优先级最高 if (testMode) { // 优先使用自定义IP const customIp = wx.getStorageSync('__TEST_SERVER_IP__'); if (customIp) { BASE_URL = customIp; console.log('✅ 使用自定义测试服务器地址:', BASE_URL); } else { // 根据设备类型选择合适的地址 if (realDevice) { // 真机必须使用IP地址,不能使用localhost BASE_URL = SERVER_CONFIG.DEFAULT_LOCAL_IP; console.log('⚠️ 使用真机测试模式,默认本地IP地址:', BASE_URL); console.log('⚠️ 请确保:1.手机和电脑在同一WiFi 2.此IP是您电脑的实际IP'); } else { BASE_URL = SERVER_CONFIG.LOCALHOST; console.log('✅ 使用模拟器测试模式,localhost地址:', BASE_URL); } } } else { // 非测试模式 if (realDevice) { // 真机环境默认使用生产地址 BASE_URL = SERVER_CONFIG.PRODUCTION; console.log('✅ 使用生产环境服务器地址:', BASE_URL); // 额外检查:如果生产地址连接失败,可以手动切换到测试模式 console.log('💡 提示:如果生产环境连接失败,可以进入test-mode-switch页面开启测试模式'); } else { // 模拟器环境使用localhost BASE_URL = SERVER_CONFIG.LOCALHOST; console.log('✅ 使用模拟器环境服务器地址:', BASE_URL); } } // 保存当前使用的服务器地址用于调试 wx.setStorageSync('__CURRENT_SERVER__', BASE_URL); } catch (e) { console.error('初始化基础URL失败:', e); // 发生错误时,根据环境类型选择更合适的默认地址 const realDevice = isRealDevice(); if (realDevice) { BASE_URL = SERVER_CONFIG.PRODUCTION; } else { BASE_URL = SERVER_CONFIG.LOCALHOST; } console.log('初始化失败,使用默认地址:', BASE_URL); } } // 初始化基础URL initBaseUrl(); function request(url, method, data, requestContext = {}) { return new Promise(function (resolve, reject) { // 每次请求都重新初始化BASE_URL,确保使用最新配置 initBaseUrl(); console.log('发送请求:', { url: BASE_URL + url, method: method || 'GET', data: data || {} }); // 构建请求头 const header = { 'content-type': 'application/json', 'Accept': 'application/json' }; // 生成Authorization签名认证信息 // 注意:实际项目中需要根据微信支付文档要求的签名算法生成 // 这里仅作为示例,实际应实现完整的签名逻辑 if (url.includes('/pay/')) { // 支付相关接口需要Authorization const timestamp = Date.now().toString(); const nonceStr = Math.random().toString(36).substr(2, 15); const signature = 'TODO: 根据微信支付文档生成签名'; header['Authorization'] = `WECHATPAY2-SHA256-RSA2048 mchid="1234567890",nonce_str="${nonceStr}",signature="${signature}",timestamp="${timestamp}",serial_no="TODO: 商户API证书序列号"`; } const requestTask = wx.request({ url: BASE_URL + url, method: method || 'GET', data: data || {}, header: header, success: function (res) { console.log('请求成功响应:', { url: BASE_URL + url, statusCode: res.statusCode, data: res.data }); // 处理200成功响应 if (res.statusCode === 200) { // 检查响应数据是否有效 - 增强版数据验证 const isResponseValid = res.data && (typeof res.data === 'object' || typeof res.data === 'string'); // 特殊情况处理:如果响应数据为空对象 if (res.data && typeof res.data === 'object' && Object.keys(res.data).length === 0) { console.warn('警告: 服务器返回空对象数据'); } resolve(res.data); } else { console.error('请求失败,状态码:', res.statusCode, '响应:', res.data); // 为所有非200响应创建统一的错误对象 let errorMessage = '请求失败: ' + res.statusCode; // 如果服务器返回了message,优先使用服务器返回的消息 if (res.data && res.data.message) { errorMessage = res.data.message; } const error = new Error(errorMessage); error.statusCode = res.statusCode; error.responseData = res.data; error.isServerError = res.statusCode >= 500; error.isClientError = res.statusCode >= 400 && res.statusCode < 500; // 特别标记需要重新登录的情况 error.needRelogin = res.data && res.data.needRelogin; reject(error); } }, fail: function (err) { console.error('请求网络错误详细信息:', { url: BASE_URL + url, error: err, errMsg: err.errMsg, errCode: err.errCode }); // 创建基础错误对象 let errorMessage = '网络连接失败'; const error = new Error(errorMessage); error.isNetworkError = true; error.originalError = err; // 尝试从错误中提取状态码和响应数据 if (err.errMsg && err.errMsg.includes('request:fail')) { // 尝试解析可能的HTTP状态码 const statusMatch = err.errMsg.match(/request:fail (\d+)/); if (statusMatch && statusMatch[1]) { const statusCode = parseInt(statusMatch[1]); error.statusCode = statusCode; error.isServerError = statusCode >= 500; error.isClientError = statusCode >= 400 && statusCode < 500; // 关键修复:当状态码为401时,设置needRelogin标志 if (statusCode === 401) { error.needRelogin = true; errorMessage = '用户未登录或登录已过期'; } else if (statusCode === 500) { errorMessage = '服务器内部错误'; } else if (statusCode >= 500) { errorMessage = '服务器暂时不可用'; } else if (statusCode === 400) { errorMessage = '请求参数错误'; } else if (statusCode === 403) { errorMessage = '无权限访问'; } else if (statusCode === 404) { errorMessage = '请求的资源不存在'; } else { errorMessage = '请求失败,请稍后再试'; } } else { // 处理其他类型的连接错误 if (err.errMsg.includes('connect')) { errorMessage = '无法连接到服务器,请检查服务器是否运行'; } else if (err.errMsg.includes('timeout')) { errorMessage = '服务器响应超时'; } else { errorMessage = '请求失败,请稍后再试'; } } } // 更新错误消息 error.message = errorMessage; // 尝试提取响应数据(如果存在) if (err.response && err.response.data) { error.responseData = err.response.data; // 从响应数据中提取needRelogin标志 if (err.response.data.needRelogin) { error.needRelogin = true; } } reject(error); }, complete: function (res) { console.log('请求完成详细信息:', { url: BASE_URL + url, type: res.errMsg.includes('ok') ? '成功' : '失败', statusCode: res.statusCode, errMsg: res.errMsg, responseData: res.data }); } }); // 如果提供了请求上下文,保存请求任务对象 if (requestContext) { requestContext.task = requestTask; } }); } // 添加用户踪迹记录功能 function addUserTrace(traceData) { console.log('API.addUserTrace - traceData:', traceData); // 获取必要的用户信息 const userId = wx.getStorageSync('userId'); const userPhone = wx.getStorageSync('userInfo')?.phoneNumber || wx.getStorageSync('phoneNumber') || ''; if (!userId) { console.warn('用户未登录,无法记录用户踪迹'); return Promise.resolve({ success: false, message: '用户未登录' }); } const requestData = { phoneNumber: userPhone, userId: userId, originalData: traceData }; return request('/api/user-trace/add', 'POST', requestData).then(res => { console.log('用户踪迹记录成功:', res); return res; }).catch(err => { console.error('用户踪迹记录失败:', err); // 即使记录失败,也不影响主流程 return { success: false, message: '用户踪迹记录失败' }; }); } // 导出统一的API对象 - 合并基础API和其他功能 module.exports = { // 包含基础API功能 ...baseApi, // 添加request函数 request: request, // 添加用户踪迹记录功能 addUserTrace: addUserTrace, // 添加商品到购物车 - 增强版本,即使本地找不到商品也尝试直接请求服务器 addToCart: function (goodsItem) { return new Promise((resolve, reject) => { // 获取用户信息,包含手机号 const users = wx.getStorageSync('users') || {}; const userId = wx.getStorageSync('userId'); let userPhone = null; // 尝试从users中获取手机号 if (userId && users[userId] && users[userId].phoneNumber) { userPhone = users[userId].phoneNumber; } else { // 尝试从全局用户信息获取 const userInfo = wx.getStorageSync('userInfo'); if (userInfo && userInfo.phoneNumber) { userPhone = userInfo.phoneNumber; } else { // 尝试从直接存储的phoneNumber获取 userPhone = wx.getStorageSync('phoneNumber'); } } console.log('API.addToCart - userPhone:', userPhone, 'goodsItem:', goodsItem); // 1. 验证用户登录状态 if (!userPhone) { return reject(new Error('用户未登录')); } // 2. 验证商品信息是否存在 if (!goodsItem) { console.error('添加到购物车失败:商品信息为空'); return reject(new Error('商品信息不存在')); } // 3. 获取商品ID并验证 const productId = goodsItem.productId || goodsItem.id; if (!productId) { console.error('添加到购物车失败:商品ID为空'); return reject(new Error('商品ID不存在')); } // 构建基础的商品对象,至少包含必要字段 const basicProduct = this.buildProductObject(goodsItem); // 4. 从本地存储获取完整的商品列表,验证当前商品ID是否有效 const allGoods = wx.getStorageSync('goods') || []; // 确保使用字符串类型进行比较,避免类型不匹配 const productIdStr = String(productId); const validProduct = allGoods.find(item => String(item.id) === productIdStr || String(item.productId) === productIdStr ); // 重要优化:使用最多两次尝试机制,确保新创建的货源也能正确处理 let attempts = 0; const maxAttempts = 2; const tryAddToCart = () => { attempts++; console.log(`尝试添加到购物车,第${attempts}/${maxAttempts}次尝试`); // 总是先尝试直接向服务器发送请求(这是修复新创建货源的关键) this.sendAddToCartRequest(userPhone, basicProduct).then(resolve).catch(err => { console.error('添加到购物车请求失败:', err.message, '尝试次数:', attempts); // 检查是否为外键约束错误或者服务器找不到商品的情况 const isForeignKeyError = err && (err.isForeignKeyError || err.message.includes('外键') || err.message.includes('500') || err.message.includes('child row') || err.message.includes('constraint')); // 如果是外键约束错误且还有尝试次数,先刷新商品列表再试 if (isForeignKeyError && attempts < maxAttempts) { console.log('检测到外键约束相关错误,刷新商品列表后重试...'); this.getProducts().then(() => { console.log('商品列表刷新成功,准备再次尝试'); // 从刷新后的商品列表中获取最新的商品信息 const refreshedGoods = wx.getStorageSync('goods') || []; const refreshedProduct = refreshedGoods.find(item => String(item.id) === productIdStr || String(item.productId) === productIdStr ); // 即使找不到,也再次尝试,因为可能是新创建的商品还没完全同步 const updatedProduct = refreshedProduct ? this.buildProductObject(refreshedProduct) : basicProduct; // 使用原始商品信息再次尝试 console.log('使用的商品信息:', updatedProduct); // 直接再次尝试,不再经过复杂的判断 this.sendAddToCartRequest(userPhone, updatedProduct).then(resolve).catch(reject); }).catch(innerErr => { console.error('刷新商品列表失败:', innerErr); // 刷新失败也尝试再次发送请求,不轻易放弃 if (attempts < maxAttempts) { console.log('刷新失败,但仍尝试再次发送请求'); this.sendAddToCartRequest(userPhone, basicProduct).then(resolve).catch(reject); } else { reject(new Error('商品信息已更新,请稍后重试')); } }); } else { // 其他错误或已达到最大尝试次数,返回错误 reject(err); } }); }; // 开始尝试添加到购物车 tryAddToCart(); }); }, // 构建商品对象的辅助方法 - 增强版,确保所有必要字段都有默认值和正确格式 buildProductObject: function (goodsItem) { console.log('构建product对象,原始goodsItem:', goodsItem); const product = { productId: this.sanitizeProductId(goodsItem.productId || goodsItem.id), // 安全处理商品ID,确保为有效的字符串格式 id: this.sanitizeProductId(goodsItem.productId || goodsItem.id), // 确保id字段也存在且格式正确 - 优先使用productId字符串 productName: goodsItem.productName || goodsItem.name || '未命名商品', // 品种 quantity: goodsItem.quantity || 1, // 使用传入的数量,如果没有则默认为1 price: goodsItem.price || '', // 确保价格有默认值,使用空字符串支持字符串类型 specification: goodsItem.specification || goodsItem.spec || '', grossWeight: goodsItem.grossWeight || goodsItem.weight || '', // 使用空字符串支持字符串类型 yolk: goodsItem.yolk || goodsItem.variety || '', // 蛋黄(原品种) // 添加额外字段以提高兼容性 name: goodsItem.productName || goodsItem.name || '未命名商品', // 不包含productQuantity字段,因为数据库中不存在该字段 // 关闭testMode,允许真实的数据库操作 testMode: false }; console.log('构建完成的product对象:', product); return product; }, // 商品ID安全处理方法,确保返回有效的字符串格式 sanitizeProductId: function (id) { if (!id) return ''; // 移除任何可能导致问题的前缀或特殊字符 const idStr = String(id).replace(/[^0-9a-zA-Z\-_]/g, ''); console.log('ID安全处理结果:', { original: id, sanitized: idStr }); return idStr; }, // 发送添加到购物车请求的辅助方法 - 完全符合服务器格式版 sendAddToCartRequest: function (userPhone, product) { return new Promise((resolve, reject) => { // 重要:直接使用传入的userPhone参数 console.log('构建的product对象:', product); console.log('发送添加到购物车请求,productId:', product.productId, '类型:', typeof product.productId); console.log('用户手机号:', userPhone); console.log('请求URL:', '/api/cart/add'); // 前置验证:确保productId存在且类型正确 if (!product.productId) { console.error('productId为空,无法添加到购物车'); return reject(new Error('商品信息不完整,请刷新页面后重试')); } // 确保productId是字符串格式 const productIdStr = String(product.productId); if (!productIdStr || productIdStr === 'undefined' || productIdStr === 'null') { console.error('无效的productId:', productIdStr); return reject(new Error('商品信息不完整,请刷新页面后重试')); } // 不修改原始商品ID,直接使用传入的商品ID // 重要:服务器需要原始的商品ID才能正确匹配 const finalProductId = productIdStr; console.log('使用原始商品ID,不进行转换:', finalProductId); // 创建新的product对象,确保所有必要字段完整 // 根据服务器端代码分析,这是最有效的请求格式 const safeProduct = { productId: finalProductId, productName: product.productName || '未命名商品', quantity: product.quantity || 1, price: product.price || '', // 使用空字符串支持字符串类型 specification: product.specification || '', grossWeight: product.grossWeight || '', // 使用空字符串支持字符串类型 yolk: product.yolk || '', testMode: false, // 确保id字段也设置为与productId相同的值(服务器端会检查这两个字段) id: finalProductId, name: product.productName || '未命名商品' }; // 根据服务器端代码分析,服务器期望的格式是 { user_phone, product: {...} } // 这是最简单直接的格式,服务器会自动从product对象中提取数据 const requestData = { user_phone: userPhone, product: safeProduct }; console.log('最终发送的请求数据完整结构:', requestData); request('/api/cart/add', 'POST', requestData).then(res => { console.log('服务器原始响应:', res); // 增强响应处理:即使服务器没有返回success标志,也要检查是否有成功的迹象 if (res && (res.success || res.code === 200 || res.status === 'success')) { console.log('添加到购物车成功'); // 规范化响应格式,确保上层代码能正确识别success标志和预约人数 // 优先使用cart_items表的selected字段作为预约人数 const normalizedRes = { success: true, ...res, // 确保返回reservedCount字段,优先使用服务器返回的selected字段,然后是reservedCount或reservationCount // 如果服务器都没有返回,则返回1表示预约成功 reservedCount: res.selected !== undefined ? res.selected : (res.reservedCount !== undefined ? res.reservedCount : (res.reservationCount || 1)) }; console.log('规范化后的响应(包含预约人数,优先使用selected字段):', normalizedRes); resolve(normalizedRes); } else { console.error('添加到购物车失败,服务器返回:', res); // 增强的错误处理逻辑 let errorMessage = res && res.message ? res.message : '添加到购物车失败'; reject(new Error(errorMessage)); } }).catch(err => { console.error('添加到购物车请求失败:', err); console.error('错误详情:', { message: err.message, statusCode: err.statusCode, responseData: err.responseData }); // 打印完整的请求数据,而不仅仅是部分参数 console.error('完整请求数据:', requestData); // 特别打印missingFields(如果存在) if (err.responseData && err.responseData.missingFields) { console.error('服务器认为缺少的字段:', err.responseData.missingFields); } // 增强的错误判断逻辑 - 无论状态码是什么,都从responseData中提取错误信息 if (err.responseData) { // 提取服务器返回的具体错误信息 console.log('服务器返回错误,详细信息:', err.responseData); const res = err.responseData; let errorMessage = res.message || '添加到购物车失败'; // 更全面的错误分类处理 // 增强的外键约束错误检测,确保所有可能的外键错误都能被正确识别 if ((res.error && (res.error.includes('外键') || res.error.includes('constraint') || res.error.includes('foreign key') || res.error.includes('key') || res.error.includes('Child row'))) || res.errorDetails?.name === 'SequelizeForeignKeyConstraintError' || res.error?.includes('child row') || res.error?.includes('cannot add or update') || res.error?.includes('referenced column')) { // 增加对child row错误的检测,这是外键约束失败的典型错误信息 console.log('检测到外键约束相关错误:', res.error); // 更详细的外键约束错误处理 console.log('检测到外键约束相关错误,准备抛出特殊错误对象:', res.error); // 1. 创建带有明确标识和丰富信息的错误对象 const foreignKeyError = new Error('商品信息已更新,请刷新页面后重试'); foreignKeyError.isForeignKeyError = true; foreignKeyError.originalError = err; // 保存原始错误对象 foreignKeyError.productId = safeProduct.productId; // 保存尝试添加的商品ID foreignKeyError.timestamp = new Date().toISOString(); // 添加时间戳便于调试 // 保留原始错误对象的标志 if (err) { foreignKeyError.statusCode = err.statusCode || 500; foreignKeyError.responseData = err.responseData || res; foreignKeyError.isServerError = err.isServerError; foreignKeyError.isClientError = err.isClientError; } else { foreignKeyError.statusCode = 500; foreignKeyError.responseData = res; } // 2. 尝试后台静默刷新商品列表,为下一次操作做准备 console.log('尝试后台刷新商品列表'); this.getProducts().catch(err => { console.error('后台刷新商品列表失败:', err); }); // 3. 打印详细的错误上下文信息,便于调试 console.error('外键约束错误详细信息:', { productId: safeProduct.productId, requestData: requestData, errorResponse: res }); // 4. 抛出带有明确标识的错误,让上层能区分处理 reject(foreignKeyError); return; // 提前返回,避免重复reject } else if ((res.error && (res.error.includes('userId') || res.error.includes('用户') || res.errorDetails?.error.includes('userId'))) || res.details?.userId === null || res.code === 403 || res.code === 401) { errorMessage = '用户信息已过期,请重新登录后重试'; console.log('检测到用户信息错误,提示重新登录'); } else if ((res.error && (res.error.includes('productId') || res.error.includes('商品') || res.errorDetails?.error.includes('productId'))) || res.details?.productId === null || res.error?.includes('不存在') || res.error?.includes('已下架')) { errorMessage = '商品信息已更新,请刷新页面后重试'; console.log('检测到商品信息错误,提示刷新页面'); } else if (res.code === 500 || (err.message && err.message.includes('500'))) { // 即使服务器返回500,也要提供友好的用户提示 // 特别处理包含外键约束的500错误 let isForeignKeyError = false; if (res.error && (res.error.includes('外键') || res.error.includes('constraint') || res.error.includes('foreign key') || res.error.includes('key') || res.error.includes('child row'))) { errorMessage = '商品信息已更新,请刷新页面后重试'; isForeignKeyError = true; console.log('服务器500错误,但包含外键约束信息'); } else { errorMessage = '系统繁忙,请稍后再试'; console.log('服务器内部错误,但提供友好提示'); } // 创建错误对象并保留原始错误的标志 const error = new Error(errorMessage); if (err) { error.statusCode = err.statusCode; error.responseData = err.responseData; error.isServerError = err.isServerError; error.isClientError = err.isClientError; } // 设置外键约束错误标志 error.isForeignKeyError = isForeignKeyError; reject(error); } else { // 其他错误 const error = new Error(errorMessage); if (err) { error.statusCode = err.statusCode; error.responseData = err.responseData; error.isServerError = err.isServerError; error.isClientError = err.isClientError; } reject(error); } } else if (err.message && err.message.includes('网络')) { // 网络连接错误 console.log('网络连接失败,检查服务器连接状态'); reject(new Error('网络连接失败,请检查网络设置后重试')); } else if (err.message && err.message.includes('500')) { // 处理直接返回500状态码的情况 console.log('检测到服务器500错误'); reject(new Error('系统繁忙,请稍后再试')); } else { // 其他错误 console.log('未分类的错误:', err.message, '响应数据:', err.responseData); reject(new Error('添加到购物车失败,请稍后重试')); } }); }); }, // 处理上传队列的方法 - 加强版,确保严格串行执行,避免连接数超限 _processUploadQueue: function () { // 关键防御:检查是否正在处理队列或有上传任务正在执行,或者队列为空 if (this._isProcessingQueue || this._isUploading || this._uploadQueue.length === 0) { console.log('_processUploadQueue 跳过,原因:', this._isProcessingQueue ? '正在处理队列' : this._isUploading ? '有上传任务正在执行' : '队列为空', '活跃上传数:', this._activeUploadCount || 0); return; } // 关键修复:检查活跃上传计数,如果异常(大于0),强制重置 if ((this._activeUploadCount || 0) > 0) { console.error('检测到异常的活跃上传计数:', this._activeUploadCount, ',强制重置为0'); this._activeUploadCount = 0; } console.log('开始处理上传队列,队列长度:', this._uploadQueue.length, '活跃上传数:', this._activeUploadCount || 0); this._isProcessingQueue = true; // 从队列中取出第一个任务 const task = this._uploadQueue.shift(); if (task) { console.log('执行队列任务,剩余队列长度:', this._uploadQueue.length); // 关键优化:增加延迟时间到1000ms,确保前一个任务完全释放所有资源 setTimeout(() => { // 再次检查上传状态,确保没有并发任务 if (this._isUploading) { console.error('严重错误:队列处理时检测到上传状态已被占用,重新入队'); // 将任务重新入队 this._uploadQueue.unshift(task); this._isProcessingQueue = false; // 短暂延迟后重新尝试 setTimeout(() => { this._processUploadQueue(); }, 100); return; } // 执行队列任务 this.uploadProductWithRecursiveImages( task.productData, task.imageUrls, task.uploadedImageUrls, task.currentIndex, task.retryInfo ) .then(task.resolve) .catch(task.reject) .finally(() => { console.log('队列任务执行完成,重置处理状态', '活跃上传数:', this._activeUploadCount || 0); this._isProcessingQueue = false; // 确保上传状态一致性 if (this._isUploading) { console.warn('警告:队列任务完成后上传状态仍为true,强制重置为false'); this._isUploading = false; } // 确保活跃计数一致性 if ((this._activeUploadCount || 0) > 0) { console.warn('警告:队列任务完成后活跃上传计数仍大于0,强制重置为0'); this._activeUploadCount = 0; } // 继续处理队列中的下一个任务,进一步增加延迟确保资源完全释放 setTimeout(() => { this._processUploadQueue(); }, 1000); // 增加延迟时间到1000ms }); }, 1000); // 增加延迟时间到1000ms } else { console.log('队列中无任务,结束处理'); this._isProcessingQueue = false; } }, // 获取商品列表的方法(用于刷新商品信息)- 修复接口路径和请求方式 getProducts: function (page = 1, pageSize = 20, status = 'all', keyword = '') { return new Promise((resolve, reject) => { // 从本地存储获取openid const openid = wx.getStorageSync('openid') || ''; // 使用正确的接口路径和POST请求方式 request('/api/product/list', 'POST', { openid: openid, status: status, // 请求指定状态的商品 viewMode: 'buyer', // 使用buyer模式获取所有商品,而非仅当前用户的商品 page: page, pageSize: pageSize, keyword: keyword // 添加关键词搜索 }).then(res => { if (res && (res.code === 200 || res.success)) { const products = res.products || []; // 将商品列表存储到本地缓存 wx.setStorageSync('goods', products || []); // 计算是否有更多数据 - 当返回的商品数量等于请求的页码大小时,认为还有更多数据 const hasMore = products.length >= pageSize; const total = res.total || products.length; console.log('API.getProducts 返回数据:', { page, pageSize, productsLength: products.length, total, hasMore }); resolve({ products: products, total: total, hasMore: hasMore }); } else { reject(new Error('获取商品列表失败')); } }).catch(err => { reject(new Error('获取商品列表失败,请稍后重试')); }); }); }, /** * JSAPI支付下单接口 * 参考文档:https://pay.weixin.qq.com/doc/v3/merchant/4012791856 * @param {Object} orderData 订单数据 * @param {string} orderData.productId 商品ID * @param {string} orderData.productName 商品名称 * @param {string} orderData.productPrice 商品价格 * @param {string} orderData.productImage 商品图片URL * @param {string} orderData.description 商品描述(必填,用户微信账单可见,不超过127字符) * @param {number} orderData.quantity 购买数量 * @param {string} orderData.outTradeNo 商户订单号(必填,6-32字符,唯一) * @param {string} orderData.timeExpire 支付结束时间(选填,rfc3339格式) * @param {string} orderData.attach 商户数据包(选填,不超过128字符) * @param {string} orderData.goodsTag 订单优惠标记(选填) * @returns {Promise} 支付请求结果 */ jsapiPay: function (orderData) { console.log('API.jsapiPay - 订单数据:', orderData); // 获取用户登录信息 const openid = wx.getStorageSync('openid'); const userId = wx.getStorageSync('userId'); const userPhone = wx.getStorageSync('userInfo')?.phoneNumber || ''; // 用户登录状态检查 if (!openid || !userId || !userPhone) { return Promise.reject(new Error('用户未登录')); } // 生成唯一商户订单号(如果未提供) const outTradeNo = orderData.outTradeNo || `ORD${Date.now()}${Math.random().toString(36).substr(2, 9)}`; // 计算订单金额(单位:分) const price = parseFloat(orderData.productPrice || '0'); const totalAmount = Math.round(price * 100); // 转换为分 // 构建JSAPI支付请求数据 // 根据微信支付文档要求的参数格式 const requestData = { // 用户信息 openid: openid, userId: userId, userPhone: userPhone, // JSAPI支付必填参数 appid: wx.getAccountInfoSync().miniProgram.appId, // 小程序appId mchid: '1234567890', // 商户号,实际应从配置或后端获取 description: orderData.description, // 商品描述(必填) out_trade_no: outTradeNo, // 商户订单号(必填) notify_url: 'https://yourdomain.com/api/pay/notify', // 回调地址,实际应从配置获取 amount: { total: totalAmount, // 订单总金额(单位:分) currency: 'CNY' // 货币类型 }, payer: { openid: openid // 支付者openid }, // 选填参数 time_expire: orderData.timeExpire || '', // 支付结束时间 attach: orderData.attach || '', // 商户数据包 goods_tag: orderData.goodsTag || '', // 订单优惠标记 // 商品详情 detail: { goods_details: [ { merchant_goods_id: orderData.productId, wechatpay_goods_id: orderData.productId, // 可选,微信支付商品编码 goods_name: orderData.productName, quantity: orderData.quantity || 1, unit_price: totalAmount // 商品单价(单位:分) } ] }, // 场景信息(可选) scene_info: { payer_client_ip: '', // 调用微信支付API的机器IP device_id: '', // 商户端设备号 store_info: { id: '1', // 门店编号 name: '门店名称', // 门店名称 area_code: '440100', // 门店行政区划码 address: '门店详细地址' // 门店详细地址 } } }; console.log('JSAPI支付请求数据:', requestData); // 设置HTTP头参数(在request函数内部处理) // Content-Type: application/json // Accept: application/json // Authorization: 签名认证信息 return request('/api/pay/jsapi', 'POST', requestData); }, // 从购物车移除商品 removeFromCart: function (goodsId) { var openid = wx.getStorageSync('openid'); console.log('API.removeFromCart - openid:', openid, 'goodsId:', goodsId); if (!openid) { return Promise.reject(new Error('用户未登录')); } // 注意:当前服务器端可能未实现/api/cart/remove接口 // 此方法会尝试调用服务器接口,但即使失败也会返回成功,确保本地操作不受影响 return request('/api/cart/remove', 'POST', { openid: openid, goodsId: goodsId }).catch(err => { console.warn('服务器移除购物车商品失败(可能是接口未实现):', err); // 即使服务器移除失败,也返回成功,确保本地操作能继续 return { success: true, message: '本地已移除,服务器移除失败(接口可能未实现)' }; }); }, // 获取goods_root表数据 getGoodsRoot: function () { return new Promise((resolve, reject) => { // 从本地存储获取openid const openid = wx.getStorageSync('openid') || ''; console.log('API.getGoodsRoot 开始请求,openid:', openid); // 使用POST请求获取goods_root数据,与其他API方法保持一致 request('/api/goods/root', 'POST', { openid: openid }).then(res => { console.log('API.getGoodsRoot 响应数据:', res); if (res && (res.code === 200 || res.success)) { const roots = res.roots || []; console.log('API.getGoodsRoot 返回的roots数据:', roots); console.log('API.getGoodsRoot 返回的roots长度:', roots.length); resolve(roots); } else { console.error('API.getGoodsRoot 响应失败:', res); reject(new Error('获取货源分类失败')); } }).catch(err => { console.error('API.getGoodsRoot 请求失败:', err); reject(new Error('获取货源分类失败,请稍后重试')); }); }); }, // 从所有用户的购物车中移除指定商品 removeFromAllCarts: function (supplyId) { var openid = wx.getStorageSync('openid'); console.log('API.removeFromAllCarts - openid:', openid, 'supplyId:', supplyId); if (!openid) { return Promise.reject(new Error('用户未登录')); } // 检查是否启用服务器清理购物车功能(默认为不启用,避免调用不存在的接口) const enableServerCleanup = false; // 可以根据实际情况修改为true if (!enableServerCleanup) { console.log('服务器清理购物车功能已禁用,跳过服务器调用'); return Promise.resolve({ success: true, message: '服务器清理购物车功能已禁用,仅执行本地清理' }); } // 如果启用了服务器清理功能,则尝试调用接口 // 注意:当前服务器端可能未实现/api/cart/removeFromAll接口 return request('/api/cart/removeFromAll', 'POST', { openid: openid, supplyId: supplyId }).catch(err => { console.warn('服务器清理所有购物车商品失败(可能是接口未实现):', err); // 即使服务器操作失败,也返回成功,确保本地操作不受影响 return { success: true, message: '本地已清理,服务器清理失败(接口可能未实现)' }; }); }, // 获取购物车信息 getCart: function () { const openid = wx.getStorageSync('openid'); console.log('API.getCart - openid:', openid); if (!openid) { return Promise.reject(new Error('用户未登录')); } return request('/api/cart/get', 'POST', { openid: openid }); }, // 评论相关API函数 // 获取商品评论列表 getComments: function (productId) { console.log('API.getComments - productId:', productId); // 尝试使用不同的参数名,看看是否能获取到所有评论 return request('/api/comments/get', 'POST', { productId: productId, // 尝试添加多个参数,明确请求所有评论 allComments: true, includeAllUsers: true, viewMode: 'all' }); }, // 提交评论 submitComment: function (commentData) { console.log('API.submitComment - 开始提交评论'); console.log('API.submitComment - 评论数据:', commentData); console.log('API.submitComment - 请求URL:', '/api/comments/add'); console.log('API.submitComment - 请求方法:', 'POST'); return request('/api/comments/add', 'POST', commentData) .then(res => { console.log('API.submitComment - 请求成功响应:', res); return res; }) .catch(err => { console.error('API.submitComment - 请求失败:', err); throw err; }); }, // 更新评论点赞数 updateCommentLike: function (data) { console.log('API.updateCommentLike - data:', data); return request('/api/comments/updateLike', 'POST', data); }, // 更新评论点踩数 updateCommentHate: function (data) { console.log('API.updateCommentHate - data:', data); return request('/api/comments/updateHate', 'POST', data); }, // 删除评论 deleteComment: function (commentId, currentUserPhone, commentPhone) { console.log('API.deleteComment - 开始删除评论'); console.log('API.deleteComment - 评论ID:', commentId); console.log('API.deleteComment - 当前用户手机号:', currentUserPhone); console.log('API.deleteComment - 评论所属手机号:', commentPhone); console.log('API.deleteComment - 请求URL:', '/api/comments/delete'); console.log('API.deleteComment - 请求方法:', 'POST'); // 参数验证 if (!commentId) { console.error('API.deleteComment - 评论ID不能为空'); return Promise.reject(new Error('评论ID不能为空')); } // 尝试调用服务器接口删除评论 return request('/api/comments/delete', 'POST', { commentId: commentId, currentUserPhone: currentUserPhone, commentPhone: commentPhone }) .then(res => { console.log('API.deleteComment - 请求成功响应:', res); return res; }) .catch(err => { console.warn('API.deleteComment - 服务器删除评论失败:', err); // 只有当接口不存在(404)时,才返回成功让客户端在本地删除 // 这样可以避免用户看到不必要的错误提示 if (err && (err.statusCode === 404 || err.errMsg && err.errMsg.includes('404'))) { console.warn('API.deleteComment - 接口不存在,返回本地删除成功'); return { success: true, message: '评论已删除' }; } // 其他错误类型直接抛出,让上层处理 console.error('API.deleteComment - 删除评论失败:', err.message); throw err; }); }, // 发送讲价消息 sendBargainMessage: function (bargainData) { console.log('API.sendBargainMessage - 开始发送讲价消息'); console.log('API.sendBargainMessage - 讲价数据:', bargainData); console.log('API.sendBargainMessage - 请求URL:', '/api/bargain/send'); console.log('API.sendBargainMessage - 请求方法:', 'POST'); // 参数验证 if (!bargainData.bargainPrice) { console.error('API.sendBargainMessage - 讲价价格不能为空'); return Promise.reject(new Error('讲价价格不能为空')); } if (!bargainData.contactPhone) { console.error('API.sendBargainMessage - 联系电话不能为空'); return Promise.reject(new Error('联系电话不能为空')); } // 尝试调用服务器接口发送讲价消息 return request('/api/bargain/send', 'POST', bargainData) .then(res => { console.log('API.sendBargainMessage - 请求成功响应:', res); return res; }) .catch(err => { console.warn('API.sendBargainMessage - 服务器发送讲价消息失败:', err); // 如果接口不存在(404),返回成功让客户端认为已发送 if (err && (err.statusCode === 404 || err.errMsg && err.errMsg.includes('404'))) { console.warn('API.sendBargainMessage - 接口不存在,返回发送成功'); return { success: true, message: '讲价请求已发送' }; } // 其他错误类型直接抛出,让上层处理 console.error('API.sendBargainMessage - 发送讲价消息失败:', err.message); throw err; }); }, // 发布商品 - 支持图片上传(修复sellerId问题) publishProduct: function (product) { console.log('===== publishProduct调用开始 =====') console.log('当前时间:', new Date().toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' })) const openid = wx.getStorageSync('openid') const userId = wx.getStorageSync('userId') // 【新增】获取userId console.log('API.publishProduct - openid:', openid) console.log('API.publishProduct - userId:', userId) // 【新增】打印userId console.log('API.publishProduct - 原始商品数据:', product) // 检查商品数据字段 if (!product) { console.error('错误:商品数据为空') return Promise.reject({ errMsg: '商品数据为空' }) } // 验证必要字段 const requiredFields = ['productName', 'price', 'quantity'] const missingFields = requiredFields.filter(field => !(field in product)) if (missingFields.length > 0) { console.error('错误:缺少必要字段:', missingFields) } else { console.log('所有必要字段都已提供') } if (!openid) { console.error('API.publishProduct - 错误: 用户未登录') const error = new Error('用户未登录或登录已过期') error.needRelogin = true return Promise.reject(error) } if (!userId) { console.error('API.publishProduct - 错误: 用户ID不存在') const error = new Error('用户信息不完整,请重新登录') error.needRelogin = true return Promise.reject(error) } // 【关键修复】构建正确的数据格式,使用userId而不是openid const requestData = { productName: product.productName || '', price: String(product.price !== undefined ? product.price || '' : ''), // 确保以字符串形式传递 quantity: parseInt(product.quantity) || 0, sellerId: userId, // 【关键修复】使用userId而不是openid openid: openid, // 【新增】同时传递openid用于其他验证 grossWeight: String(product.grossWeight !== undefined ? product.grossWeight || '' : ''), // 确保转换为字符串 yolk: product.yolk || '', specification: product.specification || '', region: product.region || '', // 【新增】添加地区字段 category: product.category || '', // 【新增】添加种类字段 // imageUrls字段会被忽略,因为图片会通过wx.uploadFile上传 } console.log('API.publishProduct - 发送请求数据:', requestData) // 如果有图片需要上传,使用wx.uploadFile if ((product.images && product.images.length > 0) || (product.imageUrls && product.imageUrls.length > 0)) { const imagesToUpload = product.images && product.images.length > 0 ? product.images : product.imageUrls console.log(`检测到${imagesToUpload.length}张图片,准备上传`) return this.uploadProductWithImages(requestData, imagesToUpload) } else { // 没有图片,使用普通的请求 console.log('没有检测到图片,使用普通请求') return request('/api/products/upload', 'POST', { productData: JSON.stringify(requestData) }).then(res => { console.log('===== 发布商品成功 =====') console.log('响应状态码:', res.statusCode || '未提供') console.log('响应数据:', res) return res }).catch(error => { console.error('===== 发布商品失败 =====') console.error('错误详情:', error) console.error('错误消息:', error.errMsg || error.message || '未知错误') throw error }) } }, // 下架商品 - 修改label字段为1 unpublishProduct: function(data) { console.log('===== unpublishProduct调用开始 =====') console.log('请求参数:', data) return request('/api/products/unpublish', 'POST', data) .then(res => { console.log('===== 下架商品成功 =====') console.log('响应数据:', res) return res }) .catch(error => { console.error('===== 下架商品失败 =====') console.error('错误详情:', error) throw error }) }, // 更新商品图片 - 专门用于为已存在的商品上传图片 updateProductImages: function (productId, imageUrls, uploadData) { console.log('===== updateProductImages调用开始 ====='); console.log('商品ID:', productId); console.log('图片数量:', imageUrls.length); console.log('上传数据:', uploadData); // 使用现有的上传方法,但传递正确的参数 return this.uploadProductWithRecursiveImages(uploadData, imageUrls); }, // 获取封面图片列表 getCovers: function () { console.log('API.getCovers - 获取封面图片列表'); return request('/api/cover', 'GET', {}); }, // 添加聊天记录 - 为用户和客服创建双向聊天记录,避免重复创建 addChatRecord: function (user_phone, manager_phone, unread = 0) { console.log('API.addChatRecord - user_phone:', user_phone, 'manager_phone:', manager_phone, 'unread:', unread); if (!user_phone || !manager_phone) { return Promise.reject(new Error('用户手机号和客服手机号不能为空')); } // 1. 先获取用户的聊天列表,检查是否已经存在该聊天记录 return this.getChatList(user_phone).then(chatList => { console.log('获取到的聊天列表:', chatList); // 检查聊天列表中是否已经存在该聊天记录 // 需要检查两种组合:user_phone <-> manager_phone 和 manager_phone <-> user_phone const chatExists = Array.isArray(chatList) && chatList.some(chat => { return (chat.user_phone === user_phone && chat.manager_phone === manager_phone) || (chat.user_phone === manager_phone && chat.manager_phone === user_phone); }); if (chatExists) { console.log('聊天记录已存在,不需要重复创建'); return { success: true, message: '聊天记录已存在' }; } // 2. 如果不存在,则调用服务器API创建聊天记录 return request('/api/chat/add', 'POST', { user_phone: user_phone, manager_phone: manager_phone, unread: unread // 使用传入的未读消息数或默认值0 }).then(res => { console.log('添加聊天记录服务器响应:', res); // 服务器返回200状态码或success=true都表示成功,包括"聊天记录已存在"的情况 if (res && (res.success || res.code === 200)) { console.log('添加聊天记录成功:', res.message || '聊天记录已创建'); return res; } else { console.error('添加聊天记录失败,服务器返回:', res); let errorMessage = res && res.message ? res.message : '添加聊天记录失败'; return Promise.reject(new Error(errorMessage)); } }); }).catch(err => { console.error('添加聊天记录请求失败:', err); console.error('错误详情:', { message: err.message, statusCode: err.statusCode, responseData: err.responseData }); // 如果是获取聊天列表失败,尝试直接创建聊天记录(作为降级策略) if (err.message.includes('获取聊天列表失败')) { console.log('获取聊天列表失败,尝试直接创建聊天记录'); return request('/api/chat/add', 'POST', { user_phone: user_phone, manager_phone: manager_phone, unread: unread // 使用传入的未读消息数或默认值0 }).then(res => { console.log('添加聊天记录服务器响应:', res); if (res && (res.success || res.code === 200)) { console.log('添加聊天记录成功:', res.message || '聊天记录已创建'); return res; } else { console.error('添加聊天记录失败,服务器返回:', res); let errorMessage = res && res.message ? res.message : '添加聊天记录失败'; return Promise.reject(new Error(errorMessage)); } }).catch(directErr => { console.error('直接创建聊天记录也失败:', directErr); return Promise.reject(new Error('添加聊天记录失败,请稍后重试')); }); } return Promise.reject(new Error('添加聊天记录失败,请稍后重试')); }); }, // 修复函数:确保用户和客服之间的聊天记录是双向的(成双成对) fixChatRecordsPair: function (user_phone, manager_phone) { console.log('API.fixChatRecordsPair - 用户手机号:', user_phone, '客服手机号:', manager_phone); if (!user_phone || !manager_phone) { return Promise.reject(new Error('用户手机号和客服手机号不能为空')); } // 注意:服务器端的/api/chat/add接口已经在内部处理了双向记录的创建 // 它会自动插入两条记录:用户->客服和客服->用户,所以只需要调用一次 return this.addChatRecord(user_phone, manager_phone) .then(res => { console.log('✓ 服务器已处理聊天记录创建:', res.message); console.log('🎉 聊天记录修复完成,用户和客服之间已建立双向聊天记录'); return { success: true, message: '聊天记录修复完成,已建立双向聊天记录' }; }) .catch(err => { console.error('修复聊天记录失败:', err.message); return { success: false, message: '聊天记录修复失败:' + err.message }; }); }, // 获取商品列表方法,用于goods页面 getGoodsList: function (params) { console.log('API.getGoodsList - params:', params); const openid = wx.getStorageSync('openid'); if (!openid) { return Promise.reject(new Error('用户未登录')); } const requestData = { openid: openid, page: params.page || 1, pageSize: params.pageSize || 20, status: 'all', // 获取所有状态的货源,包括已上架和已下架 viewMode: 'buyer', // 使用buyer模式获取所有商品 dateRange: 'all', // 明确指定获取所有时间段的数据 showAll: true, // 添加showAll参数,确保获取所有数据 _t: new Date().getTime() // 添加时间戳防止缓存 }; // 如果有搜索关键词,添加到请求参数中 if (params.keyword) { requestData.keyword = params.keyword; } return request('/api/product/list', 'POST', requestData).then(data => { // 添加字段映射,确保产品名称正确显示 if (data.success && data.products && Array.isArray(data.products)) { data.products = data.products.map(product => { // 处理产品名称映射 const name = product.productName || product.name || '未命名商品'; // 处理创建者信息 let seller = product.seller || {}; // 确保seller对象结构正确 if (seller && typeof seller === 'object') { // 确保sellerNickName字段存在,同时支持nickName字段作为备选 const sellerNickName = seller.sellerNickName || seller.nickName || '未知'; // 确保seller对象包含所有必要字段,特别是nickName字段 seller = { ...seller, nickName: sellerNickName, // 确保nickName字段存在,与sellerNickName保持一致 sellerNickName: sellerNickName, // 确保sellerNickName字段存在 name: seller.name || sellerNickName // 不再移除name字段,保留原始信息 }; } else { // 如果seller不是对象,创建默认对象,只包含nickName相关字段 seller = { nickName: '未知', sellerNickName: '未知', name: '未知' }; } return { ...product, // 确保name字段存在,优先使用productName,其次使用name name: name, // 确保seller对象结构正确 seller: seller, // 确保costprice字段存在,使用三元运算符处理,保留0值 costprice: product.costprice !== undefined && product.costprice !== null ? product.costprice : '' }; }); } return data; }); }, // 上传带图片的商品 - 改进版,确保所有图片都被实际上传到服务器 uploadProductWithImages: function (productData, imageUrls) { console.log('===== 开始上传带图片的商品 ====='); console.log('商品数据:', productData); console.log('图片数量:', imageUrls.length); // 【关键修复】确保sellerId使用userId,无论是否已存在 const userId = wx.getStorageSync('userId'); if (userId) { console.log('【修复】确保sellerId使用userId:', userId); productData.sellerId = userId; } else { console.error('【错误】本地缓存中没有userId,请重新登录'); return Promise.reject(new Error('用户未登录,请重新登录')); } // 如果没有图片,使用普通请求 if (!imageUrls || imageUrls.length === 0) { console.log('没有检测到图片,使用普通请求'); return request('/api/products/upload', 'POST', { productData: JSON.stringify(productData) }); } // 创建包含所有图片URL的商品数据 const productDataWithAllImages = { ...productData, sellerId: userId, // 【确保】使用userId imageUrls: imageUrls, // 设置imageUrls字段,确保服务器端能正确识别 allImageUrls: imageUrls, // 添加完整的图片URL列表(备用字段) // 生成会话ID,确保所有图片上传关联同一个商品 sessionId: `upload_${Date.now()}_${Math.floor(Math.random() * 1000000)}`, uploadSessionId: `upload_${Date.now()}_${Math.floor(Math.random() * 1000000)}` }; console.log('使用增强版商品数据,包含所有图片URL和会话ID'); console.log('会话ID:', productDataWithAllImages.sessionId); console.log('sellerId:', productDataWithAllImages.sellerId); // 【新增】确认sellerId // 关键修改:传递包含所有图片URL的完整商品数据 return this.uploadProductWithRecursiveImages(productDataWithAllImages, imageUrls); }, // 最终版多图片上传方法 - 修复多图片上传创建重复商品问题 uploadProductWithRecursiveImages: function (productData, imageUrls = []) { console.log('===== 最终版uploadProductWithRecursiveImages开始执行 ====='); console.log('待上传图片数量:', imageUrls.length); // 【关键修复】确保sellerId使用userId,无论是否已存在 const userId = wx.getStorageSync('userId'); if (userId) { console.log('【修复】确保sellerId使用userId:', userId); productData.sellerId = userId; } else { console.error('【错误】本地缓存中没有userId,请重新登录'); return Promise.reject(new Error('用户未登录,请重新登录')); } // 防御性检查 if (!Array.isArray(imageUrls)) { console.error('参数错误:imageUrls不是数组'); return Promise.reject(new Error('参数错误')); } // 如果没有图片,使用普通请求 if (!imageUrls || imageUrls.length === 0) { console.log('没有图片,直接发送创建请求'); return request('/api/products/upload', 'POST', { productData: JSON.stringify({ ...productData, isNewProduct: true }) }); } // 深度克隆数据,避免引用问题 const clonedProductData = JSON.parse(JSON.stringify(productData || {})); const clonedImageUrls = JSON.parse(JSON.stringify(imageUrls || [])); // 使用从调用方传递过来的会话ID,如果没有则生成新的 const sessionId = clonedProductData.sessionId || clonedProductData.uploadSessionId || `upload_${Date.now()}_${Math.floor(Math.random() * 1000000)}`; console.log('使用会话ID:', sessionId); // 确保productData中有会话ID clonedProductData.sessionId = sessionId; clonedProductData.uploadSessionId = sessionId; // 成功上传的图片URL数组 const uploadedImageUrls = []; // 确保BASE_URL存在 if (!BASE_URL) { console.error('BASE_URL未定义'); return Promise.reject(new Error('服务器地址未配置')); } const uploadUrl = BASE_URL + '/api/products/upload'; console.log('上传接口URL:', uploadUrl); // 上传单个图片的函数 - 返回上传成功的URL // 【关键修复】只上传必要的标识信息,不发送完整的商品数据,避免创建重复商品 const uploadSingleImage = (imagePath, index) => { return new Promise((resolve) => { console.log(`\n===== 上传第${index + 1}/${clonedImageUrls.length}张图片 =====`); console.log(`图片路径:`, imagePath); console.log(`会话ID:`, sessionId); // 关键修复:只传递必要的标识信息,不包含完整的商品数据 // 这样服务器只会处理图片上传,不会创建新的商品记录 const formData = { sessionId: sessionId, uploadSessionId: sessionId, productId: sessionId, imageIndex: index.toString(), totalImages: clonedImageUrls.length.toString(), uploadedCount: uploadedImageUrls.length.toString(), // 关键修复:传递完整的商品数据用于验证 productData: JSON.stringify({ ...clonedProductData, isFinalUpload: false, isFinalStep: false, currentImageIndex: index, totalImages: clonedImageUrls.length }), // 确保包含必要的商品字段 productName: clonedProductData.productName || '', price: clonedProductData.price || '', // 使用空字符串支持字符串类型 quantity: clonedProductData.quantity || 0, sellerId: clonedProductData.sellerId || '', grossWeight: clonedProductData.grossWeight || '' }; let retryCount = 0; const maxRetries = 3; const doUpload = () => { console.log(`发送上传请求,重试次数:`, retryCount); console.log(`上传参数:`, { url: uploadUrl, filePath: imagePath, name: 'images', formData: Object.keys(formData) }); wx.uploadFile({ url: uploadUrl, filePath: imagePath, name: 'images', formData: formData, timeout: 180000, success: (res) => { try { console.log(`第${index + 1}张图片上传响应状态码:`, res.statusCode); console.log(`原始响应数据:`, res.data); // 检查响应状态 if (res.statusCode >= 200 && res.statusCode < 300) { // 尝试解析响应数据 let data = null; try { data = JSON.parse(res.data); console.log(`解析后的响应数据:`, data); } catch (parseError) { console.error(`解析响应失败:`, parseError); // 即使解析失败也继续,尝试创建一个临时URL,使用placeholder://协议前缀 resolve(`placeholder://temp_${index}_${Date.now()}`); return; } // 【关键修复】从多个位置提取所有可能的图片URL // 1. 首先检查是否有完整的URL列表 if (data && data.imageUrls && Array.isArray(data.imageUrls) && data.imageUrls.length > 0) { console.log(`发现完整图片URL列表,长度:`, data.imageUrls.length); // 清空并更新uploadedImageUrls数组 uploadedImageUrls.length = 0; data.imageUrls.forEach(url => { if (url && typeof url === 'string' && url.trim()) { uploadedImageUrls.push(url.trim()); } }); // 返回当前图片的URL(如果能确定) const currentUrl = data.imageUrls[index] || data.imageUrls[data.imageUrls.length - 1]; resolve(currentUrl || `server_${index}`); return; } // 2. 检查是否有单个图片URL let imageUrl = null; if (data) { // 尝试所有可能的URL字段名 const urlFields = ['imageUrl', 'imgUrl', 'url', 'fileUrl', 'image', 'img']; for (const field of urlFields) { if (data[field] && typeof data[field] === 'string') { imageUrl = data[field].trim(); if (imageUrl) { console.log(`从字段${field}提取到URL:`, imageUrl); break; } } } // 检查嵌套结构中的URL if (!imageUrl && data.product && data.product.imageUrl) { imageUrl = data.product.imageUrl.trim(); } if (!imageUrl && data.data && data.data.imageUrl) { imageUrl = data.data.imageUrl.trim(); } } // 3. 如果找到了URL,添加到数组 if (imageUrl) { // 避免重复添加 if (!uploadedImageUrls.includes(imageUrl)) { uploadedImageUrls.push(imageUrl); console.log(`第${index + 1}张图片URL添加到数组,当前长度:`, uploadedImageUrls.length); } resolve(imageUrl); } else { // 4. 如果没有找到URL,创建一个临时URL并添加到数组,使用placeholder://协议前缀 const tempUrl = `placeholder://temp_${index}_${Date.now()}`; uploadedImageUrls.push(tempUrl); console.log(`未找到URL,使用临时URL:`, tempUrl); resolve(tempUrl); } } else { console.error(`第${index + 1}张图片上传失败,HTTP状态码:`, res.statusCode); handleError(`HTTP错误: ${res.statusCode}`); } } catch (error) { console.error(`处理第${index + 1}张图片响应时出错:`, error); handleError(error.message || '处理响应错误'); } }, fail: (error) => { console.error(`第${index + 1}张图片上传API调用失败:`, error); handleError(error.errMsg || '上传失败'); }, complete: () => { console.log(`第${index + 1}张图片上传请求完成,当前已上传URL数量:`, uploadedImageUrls.length); } }); }; const handleError = (errorMsg) => { if (retryCount < maxRetries) { retryCount++; console.log(`【重试】第${retryCount}次重试上传第${index + 1}张图片`); // 固定延迟1秒,确保稳定重试 setTimeout(() => doUpload(), 1000); } else { console.error(`第${index + 1}张图片上传彻底失败,已达到最大重试次数`); // 创建失败标记URL - 使用placeholder://协议前缀,明确标识这是占位符而非真实URL const failedUrl = `placeholder://failed_${index}_${Date.now()}`; uploadedImageUrls.push(failedUrl); console.log(`添加失败标记URL:`, failedUrl); resolve(failedUrl); // 返回失败标记,继续处理 } }; doUpload(); }); }; // 核心上传函数 - 使用async/await确保顺序执行 const uploadAllImages = async () => { console.log('开始顺序上传所有图片,会话ID:', sessionId); // 顺序上传每张图片 for (let i = 0; i < clonedImageUrls.length; i++) { console.log(`\n----- 开始处理第${i + 1}张图片 -----`); console.log(`待上传路径:`, clonedImageUrls[i]); console.log(`已上传URL数量:`, uploadedImageUrls.length); // 执行上传 const url = await uploadSingleImage(clonedImageUrls[i], i); console.log(`第${i + 1}张图片处理完成,返回URL:`, url); console.log(`当前已上传URL列表:`, uploadedImageUrls); // 添加延迟,确保服务器有足够时间处理 if (i < clonedImageUrls.length - 1) { console.log(`等待500ms后上传下一张图片`); await new Promise(resolve => setTimeout(resolve, 500)); } } console.log('\n===== 所有图片上传处理完成 ====='); console.log('总图片数量:', clonedImageUrls.length); console.log('成功处理URL数量:', uploadedImageUrls.length); console.log('最终URL列表:', uploadedImageUrls); return uploadedImageUrls; }; // 最终提交商品数据 // 【关键修复】只在这一步发送完整的商品数据,确保服务器只创建一个商品记录 const submitProduct = async (imageUrls) => { console.log('\n===== 开始最终商品提交 ====='); console.log('会话ID:', sessionId); console.log('提交图片URL数量:', imageUrls.length); // 确保至少有一个URL if (imageUrls.length === 0) { console.error('错误:所有图片上传失败'); throw new Error('所有图片上传失败'); } // 准备最终提交数据 // 【关键修复】在这一步发送完整的商品数据,包含所有必要的商品信息 const finalData = { sessionId: sessionId, uploadSessionId: sessionId, productId: sessionId, // 标记这是最终提交,服务器应该创建商品 isFinalUpload: 'true', isFinalStep: 'true', // 只在最终提交时发送完整的商品数据 productData: JSON.stringify({ ...clonedProductData, imageUrls: imageUrls, allImageUrls: imageUrls, isFinalUpload: true, isFinalStep: true, totalImages: imageUrls.length, hasMultipleImages: imageUrls.length > 1 }), uploadedImageUrls: JSON.stringify(imageUrls), totalImagesUploaded: imageUrls.length.toString() }; console.log('最终提交参数:', Object.keys(finalData)); console.log('发送完整商品数据,包含所有图片URLs'); // 发送最终请求 try { const result = await request('/api/products/upload', 'POST', finalData); console.log('最终提交成功:', result); return { ...result, // 确保返回包含所有上传的URL imageUrls: imageUrls, uploadedImageUrls: imageUrls }; } catch (error) { console.error('最终提交失败:', error); // 即使最终提交失败,也返回已上传的URL信息 return { success: false, error: error.message, imageUrls: imageUrls, uploadedImageUrls: imageUrls }; } }; // 主流程 return uploadAllImages() .then(submitProduct) .catch(error => { console.error('上传过程中发生错误:', error); // 即使流程出错,也返回已上传的URL return { success: false, error: error.message, imageUrls: uploadedImageUrls, uploadedImageUrls: uploadedImageUrls }; }); }, // 获取商品列表 - 支持未登录用户查看公开商品 getProductList: function (status = 'published', options = {}, requestContext = {}) { const openid = wx.getStorageSync('openid'); // 处理status参数,支持字符串或数组格式 let statusList = []; if (Array.isArray(status)) { // 如果传入的是数组,直接使用 statusList = status; } else if (typeof status === 'string' && status.includes(',')) { // 如果是逗号分隔的字符串,转换为数组 statusList = status.split(',').map(s => s.trim()); } else { // 单个状态值,转换为数组 statusList = [status]; } console.log('API.getProductList - openid:', openid ? '存在' : '不存在', 'statusList:', statusList); // 不再因为没有openid而拒绝请求,允许未登录用户查看公开商品 // 确保分页参数存在,默认为page=1, pageSize=10 const page = options.page || 1; const pageSize = options.pageSize || 10; // 添加时间戳参数防止请求缓存 const requestData = { status: statusList, // 传递状态数组 // 不设置默认的viewMode,让调用方根据需要设置 _t: options.timestamp || new Date().getTime(), // 添加时间戳参数防止缓存 // 始终包含分页参数 page: page, pageSize: pageSize }; // 无论openid是否存在,都添加到请求参数中,确保服务器接收到该参数 requestData.openid = openid || ''; // 如果options中包含viewMode,则添加到请求数据中 if (options.viewMode) { requestData.viewMode = options.viewMode; } // 如果options中包含category,则添加到请求数据中 if (options.category) { requestData.category = options.category; } // 如果options中包含keyword,则添加到请求数据中 if (options.keyword) { requestData.keyword = options.keyword; console.log('API.getProductList - 添加搜索关键词:', options.keyword); } else { console.log('API.getProductList - 未指定搜索关键词'); } console.log('API.getProductList - 分页参数:', { page: page, pageSize: pageSize }); console.log('API.getProductList - 查询状态:', statusList); console.log('API.getProductList - 请求数据:', requestData); console.log('API.getProductList - 请求URL:', BASE_URL + '/api/product/list'); console.log('API.getProductList - 请求方法:', 'POST'); return request('/api/product/list', 'POST', requestData).then(data => { // 添加详细的日志记录,查看服务器返回的完整数据 console.log('===== 服务器返回的商品列表数据 ====='); console.log('完整响应数据:', data); if (data && data.products && Array.isArray(data.products)) { console.log('商品数量:', data.products.length); // 增强处理:确保每个商品都包含正确的selected字段和有效的图片URL // 同时处理前后端字段映射:productId -> id, productName -> name const processedProducts = data.products.map(product => { // 优先使用product.selected,其次使用其他可能的字段 // 这确保了即使服务器返回的数据格式不一致,前端也能正确显示预约人数 const selectedCount = product.selected !== undefined ? product.selected : (product.reservedCount !== undefined ? product.reservedCount : (product.reservationCount || 0)); // 记录特定商品的selected字段信息 if (String(product.id) === 'product_1760080711896_9gb6u2tig' || String(product.productId) === 'product_1760080711896_9gb6u2tig') { console.log('===== 特定商品信息 ====='); console.log('原始商品ID:', product.id, 'productId:', product.productId); console.log('原始商品名称:', product.name, 'productName:', product.productName); console.log('原始selected字段值:', product.selected); console.log('原始reservedCount字段值:', product.reservedCount); console.log('原始reservationCount字段值:', product.reservationCount); console.log('处理后的selectedCount值:', selectedCount); } // 返回处理后的商品数据,确保包含selected字段、正确的ID和名称字段映射,以及原始图片URL return { ...product, // 确保id字段存在,优先使用productId,其次使用id id: product.productId || product.id, // 确保name字段存在,优先使用productName,其次使用name name: product.productName || product.name, // 确保selected字段存在 selected: selectedCount, // 确保displayGrossWeight字段存在(如果前端需要) displayGrossWeight: product.grossWeight ? `${product.grossWeight}` : '0', // 确保price字段存在 price: product.price !== undefined ? product.price : '' }; }); // 打印第一个商品的详细信息 if (processedProducts.length > 0) { console.log('第一个商品的详细信息:'); console.log('- productId:', processedProducts[0].productId); console.log('- productName:', processedProducts[0].productName); console.log('- grossWeight:', processedProducts[0].grossWeight, '(类型:', typeof processedProducts[0].grossWeight, ')'); console.log('- selected:', processedProducts[0].selected, '(类型:', typeof processedProducts[0].selected, ')'); console.log('- 图片URL数量:', processedProducts[0].imageUrls ? (Array.isArray(processedProducts[0].imageUrls) ? processedProducts[0].imageUrls.length : 0) : 0); console.log('- 所有可用字段:', Object.keys(processedProducts[0])); } // 返回处理后的数据 return { ...data, products: processedProducts }; } return data; }); }, // 获取当前用户创建的所有货源(包括草稿和已发布)- 支持分页 getAllSupplies: function (requestData = {}) { const openid = wx.getStorageSync('openid'); console.log('API.getAllSupplies - openid:', openid, 'requestData:', requestData); if (!openid && !requestData.testMode) { return Promise.reject(new Error('用户未登录')); } // 设置默认参数 const defaultData = { openid: openid, viewMode: 'seller', page: 1, pageSize: 20 }; // 合并参数 const finalRequestData = { ...defaultData, ...requestData }; console.log('API.getAllSupplies - 最终请求参数:', finalRequestData); return request('/api/product/list', 'POST', finalRequestData); }, // 测试方法 - 用于调试 testAPI: function () { console.log('测试API方法调用成功'); return Promise.resolve({ success: true, message: 'API正常工作' }); }, // 获取openid getOpenid: function (code) { console.log('API.getOpenid - code:', code); return request('/api/wechat/getOpenid', 'POST', { code: code }); }, // 获取personnel表数据 getPersonnelData: function () { console.log('获取personnel表数据...'); return new Promise((resolve) => { // 使用POST请求直接查询personnel表的所有数据 request('/api/personnel/getAll', 'POST', {}) .then(res => { console.log('获取personnel表数据成功:', res); // 适配不同的数据返回格式 const data = res && res.data && Array.isArray(res.data) ? res.data : res && Array.isArray(res) ? res : []; resolve(data); }) .catch(err => { console.error('获取personnel表数据失败:', err); // 失败时使用旧的managers接口作为备选 request('/api/managers', 'GET', { type: 'buyer' }) // 查询采购员 .then(res => { console.log('使用备选接口获取personnel表数据成功:', res); // 适配不同的数据返回格式 const data = res && res.data && Array.isArray(res.data) ? res.data : res && Array.isArray(res) ? res : []; resolve(data); }) .catch(err => { console.error('备选接口也失败:', err); resolve([]); }); }); }); }, // 获取销售员列表 - projectName='销售员'的人员 getSalesPersonnel: function () { console.log('获取销售员列表...'); return new Promise((resolve) => { request('/api/managers', 'GET', { type: 'seller' }) // type=seller查询销售员 .then(res => { console.log('获取销售员列表成功:', res); // 适配不同的数据返回格式 const data = res && res.data && Array.isArray(res.data) ? res.data : res && Array.isArray(res) ? res : []; resolve(data); }) .catch(err => { console.error('获取销售员列表失败:', err); resolve([]); }); }); }, // 根据手机号获取客服的managerId - 优化版 getManagerIdByPhone: function (phoneNumber) { console.log('根据手机号获取managerId:', phoneNumber); return new Promise(async (resolve) => { // 严格验证手机号参数 if (!phoneNumber || typeof phoneNumber !== 'string' || phoneNumber.trim() === '') { console.log('无效的手机号参数'); resolve(null); return; } try { // 获取personnel表数据 const data = await this.getPersonnelData(); // 精确匹配手机号,确保找到正确的客服记录 const personnel = data.find(item => { // 严格检查phoneNumber字段,不依赖其他工作岗位字段 return item && item.phoneNumber === phoneNumber; }); if (personnel) { // 优先使用userId字段,其次使用id字段 const managerId = personnel.userId || personnel.id || null; console.log(`找到客服记录,managerId: ${managerId}`); resolve(managerId); } else { console.log(`在personnel表中未找到手机号为${phoneNumber}的客服记录`); resolve(null); } } catch (error) { console.error('查询personnel表失败:', error); resolve(null); } }); }, // 检查手机号是否在personnel表中 checkPhoneInPersonnel: function (phoneNumber) { console.log('API.checkPhoneInPersonnel - phoneNumber:', phoneNumber); return new Promise((resolve) => { // 如果没有电话号码,直接返回false if (!phoneNumber) { console.log('没有电话号码'); resolve(false); return; } // 使用/api/personnel/get接口根据电话号码直接查询 request('/api/personnel/get', 'POST', { phone: phoneNumber }) .then(res => { console.log('检查手机号结果:', res); if (res && res.success && res.data && res.data.length > 0) { resolve(true); } else { resolve(false); } }) .catch(err => { console.error('检查手机号失败:', err); resolve(false); }); }); }, // 检查用户是否为客服(通过查询personnel表) checkIfUserIsCustomerService: function (phoneNumber = null) { console.log('API.checkIfUserIsCustomerService - phoneNumber:', phoneNumber); return new Promise(async (resolve) => { // 如果没有电话号码,直接返回false if (!phoneNumber) { console.log('没有电话号码,不是客服'); resolve(false); return; } // 使用getManagerIdByPhone来判断是否为客服 const managerId = await this.getManagerIdByPhone(phoneNumber); const isCustomerService = !!managerId; console.log(`用户手机号 ${phoneNumber} 客服检查结果: ${isCustomerService}`); resolve(isCustomerService); }); }, // 统一的用户类型设置函数 setUserType: function (userId, phoneNumber, currentType = '') { console.log('API.setUserType - userId:', userId, 'phoneNumber:', phoneNumber, 'currentType:', currentType); return new Promise(async (resolve) => { try { // 初始化用户类型 let userType = currentType || ''; let managerId = null; // 获取本地存储的users信息 const users = wx.getStorageSync('users') || {}; // 检查是否为客服并获取managerId if (phoneNumber) { managerId = await this.getManagerIdByPhone(phoneNumber); } // 明确的用户类型判断逻辑 const isManager = !!managerId; const isRegularUser = !isManager && (currentType.includes('buyer') || currentType.includes('seller') || currentType.includes('both')); // 设置用户类型 if (isManager) { // 客服身份:仅设置为manager userType = 'manager'; // 保存managerId到本地存储 wx.setStorageSync('managerId', managerId); console.log(`用户被识别为客服,managerId: ${managerId}`); } else { // 普通用户:确保只包含buyer、seller或both if (currentType.includes('both')) { userType = 'both'; } else if (currentType.includes('seller')) { userType = 'seller'; } else if (currentType.includes('buyer')) { userType = 'buyer'; } else { userType = ''; // 默认为空类型,不设置默认值 } // 清除managerId wx.removeStorageSync('managerId'); console.log('用户被识别为普通用户,类型:', userType); } // 更新users存储 if (userId) { if (!users[userId]) { users[userId] = {}; } users[userId].type = userType; if (managerId) { users[userId].managerId = managerId; } else { delete users[userId].managerId; } wx.setStorageSync('users', users); } // 存储用户类型信息 wx.setStorageSync('userType', userType); // 更新全局数据 if (getApp && getApp().globalData) { getApp().globalData.userType = userType; getApp().globalData.isManager = isManager; // 添加isCustomer标识,用于聊天功能判断 getApp().globalData.isCustomer = !isManager && (userType.includes('buyer') || userType.includes('seller') || userType.includes('both')); } // 确定是否为customer身份 const isCustomer = !isManager && (userType.includes('buyer') || userType.includes('seller') || userType.includes('both')); resolve({ success: true, userType: userType, isManager: isManager, isCustomer: isCustomer, managerId: managerId }); } catch (error) { console.error('设置用户类型失败:', error); resolve({ success: false, error: error.message }); } }); }, // 微信登录函数 - 增强版,强制要求手机号授权登录 login: function (encryptedData = null, iv = null) { return new Promise((resolve, reject) => { // 强制要求手机号授权 if (!encryptedData || !iv) { return reject(new Error('登录必须进行手机号授权')); } // 1. 调用微信登录接口获取code wx.login({ success: loginRes => { if (loginRes.code) { console.log('微信登录成功,获取到code:', loginRes.code); // 2. 使用code获取openid和session_key this.getOpenid(loginRes.code).then(openidRes => { console.log('获取openid成功,响应数据:', openidRes); // 检查响应格式并提取数据 - 增强版支持多种格式 let openid = null; let userId = null; let sessionKey = null; // 增强版格式处理:支持多种响应格式 if (typeof openidRes === 'object' && openidRes !== null) { // 优先检查data字段(标准格式) if (openidRes.data && typeof openidRes.data === 'object') { openid = openidRes.data.openid || null; userId = openidRes.data.userId || null; sessionKey = openidRes.data.session_key || openidRes.data.sessionKey || null; } // 如果data中没有找到,直接从响应体中查找 if (!openid) { openid = openidRes.openid || null; userId = openidRes.userId || null; sessionKey = openidRes.session_key || openidRes.sessionKey || null; } } // 检查服务器返回的状态信息 const isSuccess = openidRes && (openidRes.success === true || openidRes.code === 200); const serverMessage = openidRes && (openidRes.message || (openidRes.data && openidRes.data.message)); // 增强的错误处理:即使没有openid也提供更具体的错误信息 if (openid) { // 存储openid、userId和sessionKey wx.setStorageSync('openid', openid); if (userId) { wx.setStorageSync('userId', userId); } if (sessionKey) { wx.setStorageSync('sessionKey', sessionKey); } console.log('登录成功,openid:', openid); // 如果有手机号信息,上传到服务器 if (encryptedData && iv) { console.log('上传手机号信息到服务器'); this.uploadPhoneNumberData({ openid: openid, encryptedData: encryptedData, iv: iv, code: loginRes.code }).then(phoneRes => { console.log('手机号上传成功:', phoneRes); // 更新userId(如果服务器返回了新的userId) if (phoneRes.data && phoneRes.data.userId) { wx.setStorageSync('userId', phoneRes.data.userId); userId = phoneRes.data.userId; } // 获取解密后的手机号 let phoneNumber = null; if (phoneRes.data && phoneRes.data.phoneNumber) { phoneNumber = phoneRes.data.phoneNumber; } // 获取用户信息 this.getUserInfo(openid).then(userInfoRes => { console.log('获取用户信息成功:', userInfoRes); let userInfo = null; let userType = ''; // 默认空类型 // 处理不同格式的响应 if (userInfoRes && userInfoRes.data) { userInfo = userInfoRes.data; } // 获取本地存储的users信息 const users = wx.getStorageSync('users') || {}; let currentType = ''; // 如果有userId,从users中获取当前类型 if (userId && users[userId] && users[userId].type) { currentType = users[userId].type; console.log('从本地存储获取用户类型:', currentType); } // 先设置基础用户类型 userType = currentType || ''; // 使用客服身份判断逻辑:查询personnel表 Promise.all([ this.checkIfUserIsCustomerService(phoneNumber), this.getManagerIdByPhone(phoneNumber) ]).then(([isCustomerService, managerId]) => { if (isCustomerService && managerId) { // 如果是客服,确保userType包含manager if (!userType.includes('manager')) { userType = userType ? userType + ',manager' : 'manager'; } // 保存managerId到本地存储,用于WebSocket认证 console.log(`保存客服的managerId: ${managerId} 到本地存储`); wx.setStorageSync('managerId', managerId); // 更新users存储,包含managerId if (userId) { if (!users[userId]) { users[userId] = {}; } users[userId].managerId = managerId; } console.log('用户被识别为客服:', phoneNumber, '用户类型:', userType); } else { // 如果不是客服,移除manager标识 userType = userType.replace(/,?manager/g, '').replace(/^,|,$/g, ''); // 清除managerId wx.removeStorageSync('managerId'); console.log('用户被识别为普通用户:', phoneNumber, '用户类型:', userType); } // 更新users存储中的类型信息 if (userId) { if (!users[userId]) { users[userId] = {}; } users[userId].type = userType; // 直接存储用户类型 wx.setStorageSync('users', users); } // 存储用户类型信息 wx.setStorageSync('userType', userType); // 更新全局用户信息 if (getApp && getApp().globalData) { getApp().globalData.userInfo = userInfo; getApp().globalData.userType = userType; } // 将用户信息存储到本地存储 wx.setStorageSync('userInfo', userInfo); console.log('用户信息已存储到本地存储:', userInfo); // 将用户类型更新到服务器数据库 if (isCustomerService) { console.log('将客服身份更新到服务器数据库'); this.updateUserType('').catch(err => { console.error('更新客服身份到服务器失败:', err); // 失败不影响登录流程 }); } // 登录成功后请求位置授权 - 已在登录页面手机号授权时同时请求,避免重复授权 // console.log('登录成功后请求位置授权'); // wx.authorize({ // scope: 'scope.userLocation', // success() { // // 授权成功,获取用户位置 // wx.getLocation({ // type: 'gcj02', // success(res) { // const latitude = res.latitude; // const longitude = res.longitude; // console.log('获取位置成功:', { latitude, longitude }); // // 可以将位置信息存储到本地 // wx.setStorageSync('userLocation', { latitude, longitude }); // }, // fail() { // console.error('获取位置失败'); // } // }); // }, // fail() { // // 授权失败,弹出模态框引导用户重新授权 // wx.showModal({ // title: '需要位置授权', // content: '请在设置中开启位置授权,以便我们为您提供相关服务', // showCancel: true, // cancelText: '取消', // confirmText: '去授权', // success: (res) => { // if (res.confirm) { // // 打开设置页面让用户手动开启授权 // wx.openSetting({ // success: (settingRes) => { // if (settingRes.authSetting['scope.userLocation']) { // // 用户在设置中开启了位置授权 // wx.getLocation({ // type: 'gcj02', // success(res) { // const latitude = res.latitude; // const longitude = res.longitude; // console.log('获取位置成功:', { latitude, longitude }); // // 可以将位置信息存储到本地 // wx.setStorageSync('userLocation', { latitude, longitude }); // }, // fail() { // console.error('获取位置失败'); // } // }); // } // }, // fail: () => { // console.error('打开设置失败'); // } // }); // } // } // }); // } // }); // 登录成功后请求位置授权 - 已在登录页面手机号授权时同时请求,避免重复授权 // console.log('登录成功后请求位置授权'); // wx.authorize({ // scope: 'scope.userLocation', // success() { // // 授权成功,获取用户位置 // wx.getLocation({ // type: 'gcj02', // success(res) { // const latitude = res.latitude; // const longitude = res.longitude; // console.log('获取位置成功:', { latitude, longitude }); // // 可以将位置信息存储到本地 // wx.setStorageSync('userLocation', { latitude, longitude }); // }, // fail() { // console.error('获取位置失败'); // } // }); // }, // fail() { // // 授权失败,弹出模态框引导用户重新授权 // wx.showModal({ // title: '需要位置授权', // content: '请在设置中开启位置授权,以便我们为您提供相关服务', // showCancel: true, // cancelText: '取消', // confirmText: '去授权', // success: (res) => { // if (res.confirm) { // // 打开设置页面让用户手动开启授权 // wx.openSetting({ // success: (settingRes) => { // if (settingRes.authSetting['scope.userLocation']) { // // 用户在设置中开启了位置授权 // wx.getLocation({ // type: 'gcj02', // success(res) { // const latitude = res.latitude; // const longitude = res.longitude; // console.log('获取位置成功:', { latitude, longitude }); // // 可以将位置信息存储到本地 // wx.setStorageSync('userLocation', { latitude, longitude }); // }, // fail() { // console.error('获取位置失败'); // } // }); // } // }, // fail: () => { // console.error('打开设置失败'); // } // }); // } // } // }); // } // }); // 以下位置授权代码已在登录页面实现,此处注释掉 // wx.authorize({ // scope: 'scope.userLocation', // success() { // // 授权成功,获取用户位置 // wx.getLocation({ // type: 'gcj02', // success(res) { // const latitude = res.latitude; // const longitude = res.longitude; // console.log('获取位置成功:', { latitude, longitude }); // // 可以将位置信息存储到本地 // wx.setStorageSync('userLocation', { latitude, longitude }); // }, // fail() { // console.error('获取位置失败'); // } // }); // }, // fail() { // // 授权失败,弹出模态框引导用户重新授权 // wx.showModal({ // title: '需要位置授权', // content: '请在设置中开启位置授权,以便我们为您提供相关服务', // showCancel: true, // cancelText: '取消', // confirmText: '去授权', // success: (res) => { // if (res.confirm) { // // 打开设置页面让用户手动开启授权 // wx.openSetting({ // success: (settingRes) => { // if (settingRes.authSetting['scope.userLocation']) { // // 用户在设置中开启了位置授权 // wx.getLocation({ // type: 'gcj02', // success(res) { // const latitude = res.latitude; // const longitude = res.longitude; // console.log('获取位置成功:', { latitude, longitude }); // // 可以将位置信息存储到本地 // wx.setStorageSync('userLocation', { latitude, longitude }); // }, // fail() { // console.error('获取位置失败'); // } // }); // } // }, // fail: () => { // console.error('打开设置失败'); // } // }); // } // } // }); // } // }); resolve({ success: true, data: { openid, userId, sessionKey, phoneRes, userInfo, userType, phoneNumber } }); }).catch(checkErr => { console.error('检查客服身份失败:', checkErr); // 检查失败时默认为空类型 wx.setStorageSync('userType', ''); // 登录成功后请求位置授权 console.log('登录成功后请求位置授权'); wx.authorize({ scope: 'scope.userLocation', success() { // 授权成功,获取用户位置 wx.getLocation({ type: 'gcj02', success(res) { const latitude = res.latitude; const longitude = res.longitude; console.log('获取位置成功:', { latitude, longitude }); // 可以将位置信息存储到本地 wx.setStorageSync('userLocation', { latitude, longitude }); }, fail() { console.error('获取位置失败'); } }); }, fail() { // 授权失败,弹出模态框引导用户重新授权 wx.showModal({ title: '需要位置授权', content: '请在设置中开启位置授权,以便我们为您提供相关服务', showCancel: true, cancelText: '取消', confirmText: '去授权', success: (res) => { if (res.confirm) { // 打开设置页面让用户手动开启授权 wx.openSetting({ success: (settingRes) => { if (settingRes.authSetting['scope.userLocation']) { // 用户在设置中开启了位置授权 wx.getLocation({ type: 'gcj02', success(res) { const latitude = res.latitude; const longitude = res.longitude; console.log('获取位置成功:', { latitude, longitude }); // 可以将位置信息存储到本地 wx.setStorageSync('userLocation', { latitude, longitude }); }, fail() { console.error('获取位置失败'); } }); } }, fail: () => { console.error('打开设置失败'); } }); } } }); } }); // 登录成功后请求位置授权 console.log('登录成功后请求位置授权'); wx.authorize({ scope: 'scope.userLocation', success() { // 授权成功,获取用户位置 wx.getLocation({ type: 'gcj02', success(res) { const latitude = res.latitude; const longitude = res.longitude; console.log('获取位置成功:', { latitude, longitude }); // 可以将位置信息存储到本地 wx.setStorageSync('userLocation', { latitude, longitude }); }, fail() { console.error('获取位置失败'); } }); }, fail() { // 授权失败,弹出模态框引导用户重新授权 wx.showModal({ title: '需要位置授权', content: '请在设置中开启位置授权,以便我们为您提供相关服务', showCancel: true, cancelText: '取消', confirmText: '去授权', success: (res) => { if (res.confirm) { // 打开设置页面让用户手动开启授权 wx.openSetting({ success: (settingRes) => { if (settingRes.authSetting['scope.userLocation']) { // 用户在设置中开启了位置授权 wx.getLocation({ type: 'gcj02', success(res) { const latitude = res.latitude; const longitude = res.longitude; console.log('获取位置成功:', { latitude, longitude }); // 可以将位置信息存储到本地 wx.setStorageSync('userLocation', { latitude, longitude }); }, fail() { console.error('获取位置失败'); } }); } }, fail: () => { console.error('打开设置失败'); } }); } } }); } }); resolve({ success: true, data: { openid, userId, sessionKey, phoneRes, userInfo, userType: '', phoneNumber } }); }); }).catch(userInfoErr => { console.warn('获取用户信息失败(不影响登录):', userInfoErr); // 获取本地存储的users信息 const users = wx.getStorageSync('users') || {}; let currentType = ''; // 如果有userId,从users中获取当前类型 if (userId && users[userId] && users[userId].type) { currentType = users[userId].type; } // 先设置基础用户类型 userType = currentType || ''; // 检查客服身份并获取managerId Promise.all([ this.checkIfUserIsCustomerService(phoneNumber), this.getManagerIdByPhone(phoneNumber) ]).then(([isCustomerService, managerId]) => { if (isCustomerService && managerId) { // 如果是客服,确保userType包含manager if (!userType.includes('manager')) { userType = userType ? userType + ',manager' : 'manager'; } // 保存managerId到本地存储,用于WebSocket认证 console.log(`保存客服的managerId: ${managerId} 到本地存储`); wx.setStorageSync('managerId', managerId); // 更新users存储,包含managerId if (userId) { if (!users[userId]) { users[userId] = {}; } users[userId].managerId = managerId; } } else { // 如果不是客服,移除manager标识 userType = userType.replace(/,?manager/g, '').replace(/^,|,$/g, ''); // 清除managerId wx.removeStorageSync('managerId'); } // 更新users存储 if (userId) { if (!users[userId]) { users[userId] = {}; } users[userId].type = userType; wx.setStorageSync('users', users); } wx.setStorageSync('userType', userType); // 更新全局用户信息 if (getApp && getApp().globalData) { getApp().globalData.userType = userType; } // 将用户类型更新到服务器数据库 if (isCustomerService) { console.log('将客服身份更新到服务器数据库'); this.updateUserType('').catch(err => { console.error('更新客服身份到服务器失败:', err); // 失败不影响登录流程 }); } // 登录成功后请求位置授权 console.log('登录成功后请求位置授权'); wx.authorize({ scope: 'scope.userLocation', success() { // 授权成功,获取用户位置 wx.getLocation({ type: 'gcj02', success(res) { const latitude = res.latitude; const longitude = res.longitude; console.log('获取位置成功:', { latitude, longitude }); // 可以将位置信息存储到本地 wx.setStorageSync('userLocation', { latitude, longitude }); }, fail() { console.error('获取位置失败'); } }); }, fail() { // 授权失败,弹出模态框引导用户重新授权 wx.showModal({ title: '需要位置授权', content: '请在设置中开启位置授权,以便我们为您提供相关服务', showCancel: true, cancelText: '取消', confirmText: '去授权', success: (res) => { if (res.confirm) { // 打开设置页面让用户手动开启授权 wx.openSetting({ success: (settingRes) => { if (settingRes.authSetting['scope.userLocation']) { // 用户在设置中开启了位置授权 wx.getLocation({ type: 'gcj02', success(res) { const latitude = res.latitude; const longitude = res.longitude; console.log('获取位置成功:', { latitude, longitude }); // 可以将位置信息存储到本地 wx.setStorageSync('userLocation', { latitude, longitude }); }, fail() { console.error('获取位置失败'); } }); } }, fail: () => { console.error('打开设置失败'); } }); } } }); } }); resolve({ success: true, data: { openid, userId, sessionKey, phoneRes, userType, phoneNumber } }); }).catch(() => { // 如果检查也失败,默认为空类型 wx.setStorageSync('userType', ''); // 登录成功后请求位置授权 console.log('登录成功后请求位置授权'); wx.authorize({ scope: 'scope.userLocation', success() { // 授权成功,获取用户位置 wx.getLocation({ type: 'gcj02', success(res) { const latitude = res.latitude; const longitude = res.longitude; console.log('获取位置成功:', { latitude, longitude }); // 可以将位置信息存储到本地 wx.setStorageSync('userLocation', { latitude, longitude }); }, fail() { console.error('获取位置失败'); } }); }, fail() { // 授权失败,弹出模态框引导用户重新授权 wx.showModal({ title: '需要位置授权', content: '请在设置中开启位置授权,以便我们为您提供相关服务', showCancel: true, cancelText: '取消', confirmText: '去授权', success: (res) => { if (res.confirm) { // 打开设置页面让用户手动开启授权 wx.openSetting({ success: (settingRes) => { if (settingRes.authSetting['scope.userLocation']) { // 用户在设置中开启了位置授权 wx.getLocation({ type: 'gcj02', success(res) { const latitude = res.latitude; const longitude = res.longitude; console.log('获取位置成功:', { latitude, longitude }); // 可以将位置信息存储到本地 wx.setStorageSync('userLocation', { latitude, longitude }); }, fail() { console.error('获取位置失败'); } }); } }, fail: () => { console.error('打开设置失败'); } }); } } }); } }); resolve({ success: true, data: { openid, userId, sessionKey, phoneRes, userType: '', phoneNumber } }); }); }); }).catch(phoneErr => { console.error('手机号上传失败:', phoneErr); // 手机号上传失败导致登录失败 reject(new Error('手机号授权失败: ' + (phoneErr.message || '未知错误'))); }); } else { // 没有手机号信息,直接返回登录成功 // 获取用户信息 this.getUserInfo(openid).then(userInfoRes => { console.log('获取用户信息成功:', userInfoRes); let userInfo = null; let userType = 'customer'; // 默认客户类型 let phoneNumber = null; // 处理不同格式的响应 if (userInfoRes && userInfoRes.data) { userInfo = userInfoRes.data; // 尝试从用户信息中获取手机号 if (userInfo.phoneNumber) { phoneNumber = userInfo.phoneNumber; } } // 获取本地存储的users信息 const users = wx.getStorageSync('users') || {}; let currentType = ''; // 如果有userId,从users中获取当前类型 if (userId && users[userId] && users[userId].type) { currentType = users[userId].type; } // 先设置基础用户类型 userType = currentType || 'customer'; // 如果有手机号,尝试检查是否为客服 if (phoneNumber) { this.checkIfUserIsCustomerService(phoneNumber).then(isCustomerService => { if (isCustomerService) { // 如果是客服,确保userType包含manager if (!userType.includes('manager')) { userType = userType === 'customer' ? 'manager' : userType + ',manager'; } console.log('用户被识别为客服:', phoneNumber, '用户类型:', userType); } else { // 如果不是客服,移除manager标识 userType = userType.replace(/,?manager/g, '').replace(/^,|,$/g, '') || 'customer'; console.log('用户被识别为普通用户:', phoneNumber, '用户类型:', userType); } // 更新users存储 if (userId) { if (!users[userId]) { users[userId] = {}; } users[userId].type = userType.replace('customer', ''); wx.setStorageSync('users', users); } // 存储用户类型信息 wx.setStorageSync('userType', userType); // 更新全局用户信息 if (getApp && getApp().globalData) { getApp().globalData.userInfo = userInfo; getApp().globalData.userType = userType; } // 登录成功后请求位置授权 console.log('登录成功后请求位置授权'); wx.authorize({ scope: 'scope.userLocation', success() { // 授权成功,获取用户位置 wx.getLocation({ type: 'gcj02', success(res) { const latitude = res.latitude; const longitude = res.longitude; console.log('获取位置成功:', { latitude, longitude }); // 可以将位置信息存储到本地 wx.setStorageSync('userLocation', { latitude, longitude }); }, fail() { console.error('获取位置失败'); } }); }, fail() { // 授权失败,弹出模态框引导用户重新授权 wx.showModal({ title: '需要位置授权', content: '请在设置中开启位置授权,以便我们为您提供相关服务', showCancel: true, cancelText: '取消', confirmText: '去授权', success: (res) => { if (res.confirm) { // 打开设置页面让用户手动开启授权 wx.openSetting({ success: (settingRes) => { if (settingRes.authSetting['scope.userLocation']) { // 用户在设置中开启了位置授权 wx.getLocation({ type: 'gcj02', success(res) { const latitude = res.latitude; const longitude = res.longitude; console.log('获取位置成功:', { latitude, longitude }); // 可以将位置信息存储到本地 wx.setStorageSync('userLocation', { latitude, longitude }); }, fail() { console.error('获取位置失败'); } }); } }, fail: () => { console.error('打开设置失败'); } }); } } }); } }); resolve({ success: true, data: { openid, userId, sessionKey, userInfo, userType, phoneNumber } }); }).catch(() => { // 检查失败时默认为普通用户 wx.setStorageSync('userType', 'customer'); // 登录成功后请求位置授权 console.log('登录成功后请求位置授权'); wx.authorize({ scope: 'scope.userLocation', success() { // 授权成功,获取用户位置 wx.getLocation({ type: 'gcj02', success(res) { const latitude = res.latitude; const longitude = res.longitude; console.log('获取位置成功:', { latitude, longitude }); // 可以将位置信息存储到本地 wx.setStorageSync('userLocation', { latitude, longitude }); }, fail() { console.error('获取位置失败'); } }); }, fail() { // 授权失败,弹出模态框引导用户重新授权 wx.showModal({ title: '需要位置授权', content: '请在设置中开启位置授权,以便我们为您提供相关服务', showCancel: true, cancelText: '取消', confirmText: '去授权', success: (res) => { if (res.confirm) { // 打开设置页面让用户手动开启授权 wx.openSetting({ success: (settingRes) => { if (settingRes.authSetting['scope.userLocation']) { // 用户在设置中开启了位置授权 wx.getLocation({ type: 'gcj02', success(res) { const latitude = res.latitude; const longitude = res.longitude; console.log('获取位置成功:', { latitude, longitude }); // 可以将位置信息存储到本地 wx.setStorageSync('userLocation', { latitude, longitude }); }, fail() { console.error('获取位置失败'); } }); } }, fail: () => { console.error('打开设置失败'); } }); } } }); } }); resolve({ success: true, data: { openid, userId, sessionKey, userInfo, userType: 'customer', phoneNumber } }); }); } else { // 没有手机号,默认为普通用户 wx.setStorageSync('userType', 'customer'); // 更新全局用户信息 if (getApp && getApp().globalData) { getApp().globalData.userInfo = userInfo; getApp().globalData.userType = userType; } // 登录成功后请求位置授权 console.log('登录成功后请求位置授权'); wx.authorize({ scope: 'scope.userLocation', success() { // 授权成功,获取用户位置 wx.getLocation({ type: 'gcj02', success(res) { const latitude = res.latitude; const longitude = res.longitude; console.log('获取位置成功:', { latitude, longitude }); // 可以将位置信息存储到本地 wx.setStorageSync('userLocation', { latitude, longitude }); }, fail() { console.error('获取位置失败'); } }); }, fail() { // 授权失败,弹出模态框引导用户重新授权 wx.showModal({ title: '需要位置授权', content: '请在设置中开启位置授权,以便我们为您提供相关服务', showCancel: true, cancelText: '取消', confirmText: '去授权', success: (res) => { if (res.confirm) { // 打开设置页面让用户手动开启授权 wx.openSetting({ success: (settingRes) => { if (settingRes.authSetting['scope.userLocation']) { // 用户在设置中开启了位置授权 wx.getLocation({ type: 'gcj02', success(res) { const latitude = res.latitude; const longitude = res.longitude; console.log('获取位置成功:', { latitude, longitude }); // 可以将位置信息存储到本地 wx.setStorageSync('userLocation', { latitude, longitude }); }, fail() { console.error('获取位置失败'); } }); } }, fail: () => { console.error('打开设置失败'); } }); } } }); } }); resolve({ success: true, data: { openid, userId, sessionKey, userInfo, userType } }); } }).catch(userInfoErr => { console.warn('获取用户信息失败(不影响登录):', userInfoErr); // 如果获取用户信息失败,仍然返回登录成功,但用户类型默认为客户 wx.setStorageSync('userType', 'customer'); // 更新全局用户信息 if (getApp && getApp().globalData) { getApp().globalData.userType = 'customer'; } // 登录成功后请求位置授权 console.log('登录成功后请求位置授权'); wx.authorize({ scope: 'scope.userLocation', success() { // 授权成功,获取用户位置 wx.getLocation({ type: 'gcj02', success(res) { const latitude = res.latitude; const longitude = res.longitude; console.log('获取位置成功:', { latitude, longitude }); // 可以将位置信息存储到本地 wx.setStorageSync('userLocation', { latitude, longitude }); }, fail() { console.error('获取位置失败'); } }); }, fail() { // 授权失败,弹出模态框引导用户重新授权 wx.showModal({ title: '需要位置授权', content: '请在设置中开启位置授权,以便我们为您提供相关服务', showCancel: true, cancelText: '取消', confirmText: '去授权', success: (res) => { if (res.confirm) { // 打开设置页面让用户手动开启授权 wx.openSetting({ success: (settingRes) => { if (settingRes.authSetting['scope.userLocation']) { // 用户在设置中开启了位置授权 wx.getLocation({ type: 'gcj02', success(res) { const latitude = res.latitude; const longitude = res.longitude; console.log('获取位置成功:', { latitude, longitude }); // 可以将位置信息存储到本地 wx.setStorageSync('userLocation', { latitude, longitude }); }, fail() { console.error('获取位置失败'); } }); } }, fail: () => { console.error('打开设置失败'); } }); } } }); } }); resolve({ success: true, data: { openid, userId, sessionKey, userType: 'customer' } }); }); } } else { console.error('登录失败,无法获取openid:', openidRes); // 构建更具体的错误信息 let errorMsg = '登录失败,无法获取openid'; if (serverMessage) { errorMsg += ' - ' + serverMessage; } else if (isSuccess) { errorMsg = '登录响应格式不匹配,服务器返回成功但缺少必要数据'; } reject(new Error(errorMsg)); } }).catch(err => { console.error('获取openid失败:', err); // 增强的错误处理,提供更多上下文 const errorMsg = err && err.message ? `获取openid失败: ${err.message}` : '获取openid失败,请稍后重试'; reject(new Error(errorMsg)); }); } else { console.error('获取登录code失败:', loginRes); reject(new Error('获取登录code失败: ' + loginRes.errMsg)); } }, fail: err => { console.error('wx.login失败:', err); reject(new Error('微信登录失败: ' + err.errMsg)); } }); }); }, // 上传手机号加密数据到服务器解密 - 增强版,支持401错误自动重试 uploadPhoneNumberData: function (phoneData) { console.log('API.uploadPhoneNumberData - phoneData:', phoneData); // 定义重试次数 const maxRetries = 1; let retries = 0; // 创建递归重试函数 const tryUpload = () => { const openid = wx.getStorageSync('openid'); const sessionKey = wx.getStorageSync('sessionKey'); if (!openid) { // 如果没有openid,返回错误,不自动登录 return Promise.reject(new Error('用户未登录')); } // 检查是否包含openid,如果没有则添加 const data = { ...phoneData, openid: phoneData.openid || openid, sessionKey: phoneData.sessionKey || sessionKey || '' }; return request('/api/user/decodePhone', 'POST', data).catch(error => { console.error('上传手机号数据失败:', error); // 检查是否是401错误且还有重试次数 if ((error.statusCode === 401 || (error.responseData && error.responseData.needRelogin)) && retries < maxRetries) { console.log(`检测到登录过期,第${++retries}次重试登录...`); // 清除过期的登录信息 try { wx.removeStorageSync('openid'); wx.removeStorageSync('userId'); wx.removeStorageSync('sessionKey'); } catch (e) { console.error('清除过期登录信息失败:', e); } // 重新登录后重试 return this.login().then(loginRes => { return tryUpload(); }); } else { // 其他错误或重试次数用完,直接抛出 throw error; } }); }; // 开始第一次尝试 return tryUpload(); }, // 添加收藏 addFavorite: function (productId) { console.log('API.addFavorite - productId:', productId); return new Promise((resolve, reject) => { // 导入时间工具函数 const timeUtils = require('./time.js'); const userId = wx.getStorageSync('userId'); // 获取用户信息,包含手机号 const users = wx.getStorageSync('users') || {}; let userPhone = null; // 尝试从users中获取手机号 if (userId && users[userId] && users[userId].phoneNumber) { userPhone = users[userId].phoneNumber; } else { // 尝试从全局用户信息获取 const userInfo = wx.getStorageSync('userInfo'); if (userInfo && userInfo.phoneNumber) { userPhone = userInfo.phoneNumber; } else { // 尝试从直接存储的phoneNumber获取 userPhone = wx.getStorageSync('phoneNumber'); } } // 如果没有手机号,直接返回错误 if (!userPhone) { reject(new Error('请先登录并绑定手机号')); return; } // 构建收藏数据 const favoriteData = { user_phone: userPhone, productId: productId, date: timeUtils.toBeijingTimeISOString() // 当前时间 }; console.log('添加收藏请求数据:', favoriteData); // 发送请求到服务器 request('/api/favorites/add', 'POST', favoriteData).then(res => { console.log('添加收藏成功:', res); resolve(res); }).catch(error => { console.error('添加收藏失败:', error); reject(new Error('添加收藏失败,请稍后重试')); }); }); }, // 取消收藏 cancelFavorite: function (productId) { console.log('API.cancelFavorite - productId:', productId); return new Promise((resolve, reject) => { // 获取用户信息,包含手机号 const users = wx.getStorageSync('users') || {}; const userId = wx.getStorageSync('userId'); let userPhone = null; // 尝试从users中获取手机号 if (userId && users[userId] && users[userId].phoneNumber) { userPhone = users[userId].phoneNumber; } else { // 尝试从全局用户信息获取 const userInfo = wx.getStorageSync('userInfo'); if (userInfo && userInfo.phoneNumber) { userPhone = userInfo.phoneNumber; } else { // 尝试从直接存储的phoneNumber获取 userPhone = wx.getStorageSync('phoneNumber'); } } // 如果没有手机号,直接返回错误 if (!userPhone) { reject(new Error('请先登录并绑定手机号')); return; } const cancelData = { user_phone: userPhone, productId: productId }; console.log('取消收藏请求数据:', cancelData); request('/api/favorites/cancel', 'POST', cancelData).then(res => { console.log('取消收藏成功:', res); resolve(res); }).catch(error => { console.error('取消收藏失败:', error); reject(new Error('取消收藏失败,请稍后重试')); }); }); }, // 获取用户收藏列表 getFavorites: function (user_phone) { console.log('API.getFavorites - user_phone:', user_phone); return new Promise((resolve, reject) => { // 必须传入user_phone参数且不能为空 if (!user_phone) { reject(new Error('参数错误:必须传入有效的手机号')); return; } const userPhone = user_phone; const requestData = { user_phone: userPhone }; console.log('获取收藏列表请求数据:', requestData); // 将GET请求改为POST请求,因为GET请求通常不处理请求体 request('/api/favorites/list', 'POST', requestData).then(res => { console.log('获取收藏列表成功:', res); resolve(res); }).catch(error => { console.error('获取收藏列表失败:', error); reject(new Error('获取收藏列表失败,请稍后重试')); }); }); }, // 上传用户信息到服务器 uploadUserInfo: function (userInfo) { console.log('API.uploadUserInfo - userInfo:', userInfo); const openid = wx.getStorageSync('openid'); if (!openid && !userInfo.openid) { return Promise.reject(new Error('用户未登录')); } // 确保包含openid const data = { ...userInfo, openid: userInfo.openid || openid }; return request('/api/user/upload', 'POST', data); }, // 更新用户类型 - 恢复原有的类型转换功能 updateUserType: async function (typeToAdd) { try { const userId = wx.getStorageSync('userId'); const openid = wx.getStorageSync('openid'); const userInfo = wx.getStorageSync('userInfo'); if (!userId || !openid) { console.log('用户未登录,无法更新用户类型'); return; } // 获取当前用户类型 let users = wx.getStorageSync('users') || {}; let currentType = users[userId] && users[userId].type ? users[userId].type : ''; // 计算新的用户类型(支持buyer、seller、both、manager) let newType = currentType; // 标记是否需要强制更新到服务器(即使类型没有变化) let forceUpdate = false; // 处理空字符串参数 - 可能是从客服登录流程调用的 if (typeToAdd === '') { // 检查当前类型是否包含manager if (currentType.includes('manager')) { forceUpdate = true; // 客服类型需要强制同步到服务器 // 确保type设置为manager,而不是空字符串 newType = 'manager'; console.log('检测到客服类型,将强制同步manager类型到服务器'); } } // 只处理buyer、seller类型转换,manager由系统自动设置 else if (typeToAdd === 'buyer' || typeToAdd === 'seller') { // 如果当前类型为空,则直接设置为要添加的类型 if (!newType) { newType = typeToAdd; } else { // 如果当前类型是buyer,要添加seller,则变为both if (newType === 'buyer' && typeToAdd === 'seller') { newType = 'both'; } // 如果当前类型是seller,要添加buyer,则变为both else if (newType === 'seller' && typeToAdd === 'buyer') { newType = 'both'; } // 如果当前类型是both,不做改变 else if (newType === 'both') { // 但仍然需要检查是否是客服,是的话需要强制更新 if (currentType.includes('manager')) { forceUpdate = true; } } } } // 正常情况下,如果类型没有变化且不强制更新,不需要继续 if (newType === currentType && !forceUpdate) { return; } // 保留manager标识(如果有) const hasManager = currentType.includes('manager'); if (hasManager && !newType.includes('manager')) { newType = newType ? newType + ',manager' : 'manager'; } // 更新本地存储 if (!users[userId]) { users[userId] = {}; } users[userId].type = newType.replace('customer', ''); // 不存储customer类型 wx.setStorageSync('users', users); // 更新全局数据 const app = getApp(); if (app && app.globalData) { app.globalData.userType = newType; } // 同时更新本地的userType存储 wx.setStorageSync('userType', newType); console.log('用户类型更新成功:', newType); // 上传到服务器 - 只上传必要的字段,避免覆盖其他数据 const uploadData = { userId, openid, type: newType, timestamp: Date.now() }; console.log('准备上传用户类型到服务器:', uploadData); // 调用用户信息更新API return request('/api/user/update', 'POST', uploadData); } catch (error) { console.error('更新用户类型失败:', error); throw error; } }, // 获取用户信息用于调试 getUserInfoForDebug: function () { const openid = wx.getStorageSync('openid'); console.log('API.getUserInfoForDebug - openid:', openid); if (!openid) { return Promise.reject(new Error('用户未登录')); } return request('/api/user/debug', 'POST', { openid: openid }); }, // 获取用户信息 getUserInfo: function (openid) { console.log('API.getUserInfo - openid:', openid); // 如果没有提供openid,尝试从本地存储获取 const userOpenid = openid || wx.getStorageSync('openid'); if (!userOpenid) { return Promise.reject(new Error('用户未登录')); } return request('/api/user/get', 'POST', { openid: userOpenid }); }, // 验证用户登录状态 validateUserLogin: function () { const openid = wx.getStorageSync('openid'); const phoneNumber = wx.getStorageSync('phoneNumber') || ''; console.log('API.validateUserLogin - openid:', openid, 'phoneNumber:', phoneNumber); // 验证登录状态和手机号 if (!openid) { return Promise.reject(new Error('用户未登录')); } if (!phoneNumber) { return Promise.reject(new Error('用户未完成手机号授权')); } return request('/api/user/validate', 'POST', { openid: openid, phoneNumber: phoneNumber }); }, // 撤回备案申请 withdrawSettlementApplication: function (openid) { return request('/api/settlement/withdraw', 'POST', { openid: openid }); }, // 重新提交备案申请 resubmitSettlementApplication: function (applicationId) { const openid = wx.getStorageSync('openid'); console.log('API.resubmitSettlementApplication - applicationId:', applicationId, 'openid:', openid); if (!openid) { return Promise.reject(new Error('用户未登录')); } return request(`/api/settlement/resubmit/${applicationId}`, 'POST', { openid: openid }); }, // 将商品状态设置为隐藏(软删除) deleteProduct: function (productId) { const openid = wx.getStorageSync('openid'); const sellerId = wx.getStorageSync('userId'); console.log('API.deleteProduct - openid:', openid, 'productId:', productId, 'sellerId:', sellerId); if (!openid) { return Promise.reject(new Error('用户未登录')); } return request('/api/products/delete', 'POST', { openid: openid, productId: productId, sellerId: sellerId }); }, // 将商品状态设置为下架 hideProduct: function (productId) { const openid = wx.getStorageSync('openid'); console.log('API.hideProduct - openid:', openid, 'productId:', productId); if (!openid) { return Promise.reject(new Error('用户未登录')); } return request('/api/product/hide', 'POST', { openid: openid, productId: productId }); }, // 添加BASE_URL属性,方便其他地方使用 BASE_URL: BASE_URL, // 正确导出withdrawSettlementApplication方法 withdrawSettlementApplication: function (openid) { return request('/api/settlement/withdraw', 'POST', { openid: openid }); }, // 增加用户估价点击次数 incrementAppraisalNum: function () { const openid = wx.getStorageSync('openid'); const userId = wx.getStorageSync('userId'); console.log('API.incrementAppraisalNum - openid:', openid, 'userId:', userId); if (!openid || !userId) { return Promise.reject(new Error('用户未登录')); } return request('/api/user/increment-appraisal', 'POST', { openid: openid, userId: userId }).catch(err => { console.error('增加估价次数失败:', err); // 即使失败也返回成功,不影响主流程 return { success: true }; }); }, // 增加用户对比价格点击次数 incrementCompareNum: function () { const openid = wx.getStorageSync('openid'); const userId = wx.getStorageSync('userId'); console.log('API.incrementCompareNum - openid:', openid, 'userId:', userId); if (!openid || !userId) { return Promise.reject(new Error('用户未登录')); } return request('/api/user/increment-compare', 'POST', { openid: openid, userId: userId }).catch(err => { console.error('增加对比价格次数失败:', err); // 即使失败也返回成功,不影响主流程 return { success: true }; }); }, // 编辑商品方法 - 修复版 editProduct: function (productId, productData) { const openid = wx.getStorageSync('openid'); console.log('API.editProduct - openid:', openid, 'productId:', productId); console.log('API.editProduct - 商品数据:', productData); if (!openid) { return Promise.reject(new Error('用户未登录')); } if (!productId) { return Promise.reject(new Error('商品ID不能为空')); } return new Promise((resolve, reject) => { // 【关键修复】确保包含现有的图片URL if (!productData.imageUrls || productData.imageUrls.length === 0) { // 从当前商品数据中获取现有图片URL console.log('【前端修复】商品数据中没有图片URL,尝试从本地数据中获取'); // 在所有货源列表中查找当前商品 let currentProduct = null; const allSupplies = [ ...this.data.publishedSupplies, ...this.data.pendingSupplies, ...this.data.rejectedSupplies, ...this.data.draftSupplies ]; currentProduct = allSupplies.find(s => s.id === productId || s.serverProductId === productId ); if (currentProduct && currentProduct.imageUrls) { productData.imageUrls = currentProduct.imageUrls; console.log('【前端修复】成功从本地数据获取图片URL,数量:', productData.imageUrls.length); } else { console.warn('【前端修复】无法从本地数据获取图片URL,使用空数组'); productData.imageUrls = []; } } else { console.log('【前端修复】商品数据中已有图片URL,数量:', productData.imageUrls.length); } // 【修复】使用正确的图片预处理逻辑 this.processEditProductImages(productId, productData, openid) .then(processedProductData => { // 构建请求数据,确保包含所有必要字段并转换为正确类型 const requestData = { openid: openid, productId: productId, product: { productName: processedProductData.productName || '', price: String(processedProductData.price !== undefined ? processedProductData.price || '' : ''), // 确保以字符串形式传递 quantity: parseInt(processedProductData.quantity) || 0, // 添加其他可选字段 grossWeight: String(processedProductData.grossWeight !== undefined ? processedProductData.grossWeight || '' : ''), // 确保转换为字符串 yolk: processedProductData.yolk || '', specification: processedProductData.specification || '', region: productData.region || '', // 【重要】确保地区字段在product对象中 imageUrls: processedProductData.imageUrls || [], // 【重要】包含处理后的图片URL status: processedProductData.status || '', // 新增:联系人和联系电话 product_contact: processedProductData.product_contact || '', contact_phone: processedProductData.contact_phone || '', }, // 同时在顶层也传递status参数,确保服务器端能正确接收 status: processedProductData.status || '' }; console.log('API.editProduct - 发送请求数据:', requestData); console.log('API.editProduct - 请求URL:', BASE_URL + '/api/product/edit'); return request('/api/product/edit', 'POST', requestData); }) .then(res => { console.log('===== 编辑商品成功 ====='); console.log('响应数据:', res); resolve(res); }) .catch(error => { console.error('===== 编辑商品失败 ====='); console.error('错误详情:', error); console.error('错误消息:', error.errMsg || error.message || '未知错误'); reject(error); }); }); }, // 辅助方法:根据商品ID查找商品 findProductById: function (productId) { const allSupplies = [ ...this.data.publishedSupplies, ...this.data.pendingSupplies, ...this.data.rejectedSupplies, ...this.data.draftSupplies ]; return allSupplies.find(s => s.id === productId || s.serverProductId === productId ); }, // 【修复】编辑商品图片预处理方法 - 使用现有的上传方法 processEditProductImages: function (productId, productData, openid) { return new Promise((resolve, reject) => { console.log('【图片预处理】开始处理编辑商品图片...'); const imageUrls = productData.imageUrls || []; console.log('【图片预处理】原始图片URL:', imageUrls); // 识别临时图片 const tempImageUrls = imageUrls.filter(url => url && (url.startsWith('http://tmp/') || url.startsWith('wxfile://')) ); if (tempImageUrls.length === 0) { console.log('【图片预处理】没有临时图片需要处理'); resolve(productData); return; } console.log(`【图片预处理】发现${tempImageUrls.length}张临时图片,开始上传...`); // 【关键修复】上传临时图片时传递商品ID,确保使用相同的文件夹 const uploadData = { productId: productId, // 传递商品ID,服务器端使用相同的文件夹 openid: openid, isEdit: true // 标记为编辑操作 }; this.uploadProductImages(productId, tempImageUrls, uploadData) .then(uploadResult => { console.log('【图片预处理】临时图片上传成功:', uploadResult); // 【关键修复】避免重复添加图片URL // 只保留非临时图片URL(已存在的OSS图片) const existingOssImageUrls = imageUrls.filter(url => url && !url.startsWith('http://tmp/') && !url.startsWith('wxfile://') ); // 【修复】使用去重后的图片URL // 从上传结果中获取所有图片URL(包括原有的和新上传的) let allImageUrls = []; if (uploadResult.allImageUrls && Array.isArray(uploadResult.allImageUrls)) { allImageUrls = [...new Set(uploadResult.allImageUrls)]; // 去重 } else if (uploadResult.imageUrls && Array.isArray(uploadResult.imageUrls)) { allImageUrls = [...new Set(uploadResult.imageUrls)]; // 去重 } else { // 如果没有返回完整的图片列表,则合并现有和新上传的图片 let uploadedImageUrls = []; if (uploadResult.results && Array.isArray(uploadResult.results)) { uploadResult.results.forEach(result => { if (result.imageUrls && Array.isArray(result.imageUrls)) { uploadedImageUrls = [...uploadedImageUrls, ...result.imageUrls]; } }); } allImageUrls = [...new Set([...existingOssImageUrls, ...uploadedImageUrls])]; // 合并并去重 } console.log('【图片预处理】最终图片URL(去重后):', allImageUrls); // 返回处理后的商品数据 resolve({ ...productData, imageUrls: allImageUrls }); }) .catch(error => { console.error('【图片预处理】临时图片上传失败:', error); reject(new Error('图片上传失败: ' + (error.message || '未知错误'))); }); }); }, // 【确保这个方法存在】上传商品图片 - 修复版,专门用于为已存在商品上传图片 uploadProductImages: function (productId, imageUrls) { return new Promise((resolve, reject) => { if (!productId) { reject(new Error('商品ID不能为空')) return } if (!imageUrls || imageUrls.length === 0) { resolve({ success: true, message: '没有图片需要上传' }) return } console.log('开始为已存在商品上传图片,商品ID:', productId, '图片数量:', imageUrls.length) // 获取openid const openid = wx.getStorageSync('openid') if (!openid) { reject(new Error('用户未登录')) return } // 【关键修复】使用专门的图片上传方法,而不是创建新商品 this.uploadImagesToExistingProduct(productId, imageUrls, openid) .then(resolve) .catch(reject) }) }, // 【确保这个方法存在】上传商品图片 - 确保顺序执行 uploadImagesToExistingProduct: function (productId, imageUrls, openid) { return new Promise((resolve, reject) => { console.log('【图片上传】开始为已存在商品上传图片,商品ID:', productId); // 【关键修复】顺序上传图片,避免并发问题 const uploadSequentially = async () => { const results = []; for (let i = 0; i < imageUrls.length; i++) { try { console.log(`顺序上传第${i + 1}/${imageUrls.length}张图片`); const result = await new Promise((resolveUpload, rejectUpload) => { const formData = { productId: productId, openid: openid, action: 'add_images_only', imageIndex: i, totalImages: imageUrls.length, isUpdate: 'true', timestamp: Date.now() }; // 【修复】使用模块内部的 BASE_URL 而不是 API.BASE_URL wx.uploadFile({ url: BASE_URL + '/api/products/upload', // 直接使用 BASE_URL filePath: imageUrls[i], name: 'images', formData: formData, success: (res) => { if (res.statusCode === 200) { try { const data = JSON.parse(res.data); if (data.success) { console.log(`第${i + 1}张图片上传成功,当前总数:`, data.totalCount); resolveUpload(data); } else { rejectUpload(new Error(data.message || '图片上传失败')); } } catch (parseError) { rejectUpload(new Error('服务器响应格式错误')); } } else { rejectUpload(new Error(`HTTP ${res.statusCode}`)); } }, fail: (err) => { rejectUpload(new Error('网络错误: ' + err.errMsg)); } }); }); results.push(result); // 添加延迟,避免服务器处理压力过大 if (i < imageUrls.length - 1) { await new Promise(resolve => setTimeout(resolve, 500)); } } catch (error) { console.error(`第${i + 1}张图片上传失败:`, error); // 继续上传其他图片,不中断流程 results.push({ success: false, error: error.message }); } } return results; }; uploadSequentially() .then(results => { // 取最后一个成功的结果作为最终状态 const successfulResults = results.filter(r => r && r.success); if (successfulResults.length > 0) { const lastResult = successfulResults[successfulResults.length - 1]; // 【修复】确保返回完整的图片URL列表 const allImageUrls = lastResult.allImageUrls || lastResult.imageUrls || []; resolve({ success: true, message: `成功上传${successfulResults.length}张图片`, imageUrls: lastResult.imageUrls || [], allImageUrls: allImageUrls, // 确保包含所有图片 uploadedCount: successfulResults.length, totalCount: lastResult.totalCount || allImageUrls.length, results: results }); } else { reject(new Error('所有图片上传失败')); } }) .catch(error => { console.error('图片上传失败:', error); reject(error); }); }); }, // 更新商品联系人信息 updateProductContacts: function () { return request('/api/products/update-contacts', 'POST'); }, // 获取产品详情 getProductDetail: function ({ productId }) { console.log('API.getProductDetail - productId:', productId); return request('/api/products/detail', 'POST', { productId: productId }).then(data => { console.log('商品详情API响应:', data); if (data && data.data) { console.log('商品详情字段检查:'); console.log('- 是否包含sourceType:', 'sourceType' in data.data); console.log('- 是否包含supplyStatus:', 'supplyStatus' in data.data); console.log('- sourceType值:', data.data.sourceType); console.log('- supplyStatus值:', data.data.supplyStatus); console.log('- 是否包含product_contact:', 'product_contact' in data.data); console.log('- 是否包含contact_phone:', 'contact_phone' in data.data); console.log('- product_contact值:', data.data.product_contact); console.log('- contact_phone值:', data.data.contact_phone); console.log('- 是否包含producting字段:', 'producting' in data.data); console.log('- producting值:', data.data.producting); // 添加spec_status字段检查 console.log('- 是否包含spec_status:', 'spec_status' in data.data); console.log('- spec_status值:', data.data.spec_status); console.log('- 是否包含bargaining:', 'bargaining' in data.data); console.log('- bargaining值:', data.data.bargaining); console.log('- 完整字段:', Object.keys(data.data)); } return data; }); }, // 增加商品点击查看次数 incrementProductFrequency: function ({ productId }) { console.log('API.incrementProductFrequency - productId:', productId); return request('/api/products/increment-frequency', 'POST', { productId: productId }); }, // 预约商品 reserveProduct: function ({ id }) { return request('/api/products/reserve', 'POST', { productId: id }); }, /** * 上传入驻申请文件 * @param {String} filePath - 本地文件路径 * @param {String} fileType - 文件类型(如:license, proof, brand) * @returns {Promise} - 上传结果 */ uploadSettlementFile: function (filePath, fileType) { return new Promise((resolve, reject) => { const openid = wx.getStorageSync('openid'); const userId = wx.getStorageSync('userId'); if (!openid) { reject(new Error('用户未登录')); return; } if (!filePath) { reject(new Error('文件路径不能为空')); return; } if (!fileType) { reject(new Error('文件类型不能为空')); return; } // 生成会话ID,确保文件上传的唯一性 const sessionId = `settlement_${Date.now()}_${Math.floor(Math.random() * 1000000)}`; console.log('开始上传入驻文件:', filePath, '文件类型:', fileType); console.log('上传会话ID:', sessionId); // 使用wx.uploadFile直接上传文件,参考publish页面的上传实现 wx.uploadFile({ url: BASE_URL + '/api/settlement/upload', filePath: filePath, name: 'file', formData: { openid: openid, userId: userId || '', fileType: fileType, sessionId: sessionId, uploadSessionId: sessionId }, timeout: 180000, // 3分钟超时 success: (res) => { try { console.log('入驻文件上传响应状态码:', res.statusCode); console.log('原始响应数据:', res.data); // 检查响应状态 if (res.statusCode >= 200 && res.statusCode < 300) { const data = JSON.parse(res.data); console.log('解析后的响应数据:', data); if (data.success) { console.log('入驻文件上传成功:', data.data?.fileUrl || ''); resolve(data.data || {}); } else { console.error('入驻文件上传失败:', data.message); reject(new Error(data.message || '文件上传失败')); } } else { console.error('入驻文件上传失败,HTTP状态码:', res.statusCode); reject(new Error(`HTTP错误: ${res.statusCode}`)); } } catch (e) { console.error('解析上传响应失败:', e); reject(new Error('服务器响应格式错误')); } }, fail: (err) => { console.error('入驻文件上传API调用失败:', err); reject(new Error('网络错误: ' + err.errMsg)); } }); }); }, // 获取收藏商品列表 getFavorites: function (phoneNumber) { console.log('API.getFavorites - phoneNumber:', phoneNumber); if (!phoneNumber) { return Promise.reject(new Error('用户未登录')); } return request('/api/favorites/list', 'POST', { user_phone: phoneNumber }); }, // 取消收藏商品 cancelFavorite: function (productId) { console.log('API.cancelFavorite - productId:', productId); return new Promise((resolve, reject) => { // 获取用户信息,包含手机号 const users = wx.getStorageSync('users') || {}; const userId = wx.getStorageSync('userId'); let userPhone = null; // 尝试从users中获取手机号 if (userId && users[userId] && users[userId].phoneNumber) { userPhone = users[userId].phoneNumber; } else { // 尝试从全局用户信息获取 const userInfo = wx.getStorageSync('userInfo'); if (userInfo && userInfo.phoneNumber) { userPhone = userInfo.phoneNumber; } else { // 尝试从直接存储的phoneNumber获取 userPhone = wx.getStorageSync('phoneNumber'); } } // 如果没有手机号,直接返回错误 if (!userPhone) { reject(new Error('请先登录并绑定手机号')); return; } const requestData = { user_phone: userPhone, productId: productId }; console.log('取消收藏请求数据:', requestData); request('/api/favorites/remove', 'POST', requestData).then(res => { console.log('取消收藏成功:', res); resolve(res); }).catch(error => { console.error('取消收藏失败:', error); reject(new Error('取消收藏失败,请稍后重试')); }); }); }, // 获取聊天记录 getChatMessages: function (chatId, userPhone, options = {}) { return new Promise((resolve, reject) => { console.log('API.getChatMessages - chatId:', chatId, 'userPhone:', userPhone, 'options:', options); // 如果没有传入手机号,尝试从本地存储获取 if (!userPhone) { // 获取用户信息,包含手机号 const users = wx.getStorageSync('users') || {}; const userId = wx.getStorageSync('userId'); // 尝试从users中获取手机号 if (userId && users[userId] && users[userId].phoneNumber) { userPhone = users[userId].phoneNumber; } else { // 尝试从全局用户信息获取 const userInfo = wx.getStorageSync('userInfo'); if (userInfo && userInfo.phoneNumber) { userPhone = userInfo.phoneNumber; } else { // 尝试从直接存储的phoneNumber获取 userPhone = wx.getStorageSync('phoneNumber'); } } } // 如果没有手机号,直接返回错误 if (!userPhone) { const error = new Error('请先登录并绑定手机号'); console.error('API.getChatMessages - 错误:', error); reject(error); return; } // 如果没有chatId,返回空数组 if (!chatId) { console.log('API.getChatMessages - 没有chatId,返回空数组'); resolve([]); return; } const requestData = { chat_id: chatId, user_phone: userPhone }; // 添加分页参数 if (options.before) { requestData.before = options.before; } if (options.limit) { requestData.limit = options.limit; } console.log('API.getChatMessages - 请求数据:', requestData); request('/api/chat/messages', 'POST', requestData).then(res => { console.log('API.getChatMessages - 响应数据:', res); // 增强数据结构处理 let messages = []; if (Array.isArray(res)) { messages = res; } else if (res && Array.isArray(res.messages)) { messages = res.messages; } else if (res && Array.isArray(res.data)) { messages = res.data; } else if (res && res.data && Array.isArray(res.data.messages)) { messages = res.data.messages; } console.log('API.getChatMessages - 处理后的消息数量:', messages.length); resolve(messages); }).catch(error => { console.error('API.getChatMessages - 请求失败:', error); reject(error); }); }); }, // 发送聊天消息 sendMessage: function (senderPhone, receiverPhone, content) { return new Promise((resolve, reject) => { console.log('API.sendMessage - senderPhone:', senderPhone, 'receiverPhone:', receiverPhone, 'content:', content); // 验证必要参数 if (!senderPhone || !receiverPhone || !content) { const error = new Error('发送者电话、接收者电话和消息内容不能为空'); console.error('API.sendMessage - 错误:', error); reject(error); return; } const requestData = { sender_phone: senderPhone, receiver_phone: receiverPhone, content: content }; console.log('API.sendMessage - 请求数据:', requestData); request('/api/chat/send', 'POST', requestData).then(res => { console.log('API.sendMessage - 响应数据:', res); // 处理不同格式的响应 if (res && (res.success || res.code === 200)) { resolve(res); } else { const errorMessage = res && res.message ? res.message : '发送消息失败'; reject(new Error(errorMessage)); } }).catch(error => { console.error('API.sendMessage - 请求失败:', error); reject(error); }); }); }, // 更新聊天列表的未读消息数 updateUnreadCount: function (managerPhone, increment = 1) { return new Promise((resolve, reject) => { console.log('API.updateUnreadCount - managerPhone:', managerPhone, 'increment:', increment); // 验证必要参数 if (!managerPhone) { const error = new Error('聊天对象手机号不能为空'); console.error('API.updateUnreadCount - 错误:', error); reject(error); return; } // 获取当前用户的手机号 const users = wx.getStorageSync('users') || {}; const userId = wx.getStorageSync('userId'); let userPhone = null; // 尝试从users中获取手机号 if (userId && users[userId]) { if (users[userId].phoneNumber) { userPhone = users[userId].phoneNumber; } else if (users[userId].phone) { userPhone = users[userId].phone; } } // 如果还没有获取到,尝试从全局用户信息获取 if (!userPhone) { const userInfo = wx.getStorageSync('userInfo'); if (userInfo) { if (userInfo.phoneNumber) { userPhone = userInfo.phoneNumber; } else if (userInfo.phone) { userPhone = userInfo.phone; } } } // 如果还没有获取到,尝试从直接存储获取 if (!userPhone) { userPhone = wx.getStorageSync('phoneNumber') || wx.getStorageSync('phone'); } if (!userPhone) { const error = new Error('用户手机号不能为空'); console.error('API.updateUnreadCount - 错误:', error); reject(error); return; } const requestData = { user_phone: userPhone, manager_phone: managerPhone, increment: increment }; console.log('API.updateUnreadCount - 请求数据:', requestData); request('/api/chat/updateUnread', 'POST', requestData).then(res => { console.log('API.updateUnreadCount - 响应数据:', res); // 处理不同格式的响应 if (res && (res.success || res.code === 200)) { resolve(res); } else { const errorMessage = res && res.message ? res.message : '更新未读消息数失败'; reject(new Error(errorMessage)); } }).catch(error => { console.error('API.updateUnreadCount - 请求失败:', error); reject(error); }); }); }, // 删除聊天消息(单向删除,删除数据库对应的两个手机号码的数据) deleteChatMessage: function (userPhone, managerPhone) { console.log('API.deleteChatMessage - userPhone:', userPhone, 'managerPhone:', managerPhone); return new Promise((resolve, reject) => { // 验证必要参数 if (!userPhone || !managerPhone) { reject(new Error('发送者电话和接收者电话不能为空')); return; } const requestData = { user_phone: userPhone, manager_phone: managerPhone }; request('/api/chat/delete', 'POST', requestData).then(res => { console.log('删除聊天消息成功:', res); resolve(res); }).catch(error => { console.error('删除聊天消息失败:', error); reject(new Error(error.message || '删除聊天消息失败,请稍后重试')); }); }); }, // 获取聊天列表 getChatList: function (userPhone) { console.log('API.getChatList - userPhone:', userPhone); return new Promise((resolve, reject) => { // 如果没有传入手机号,尝试从本地存储获取 if (!userPhone) { // 获取用户信息,包含手机号 const users = wx.getStorageSync('users') || {}; const userId = wx.getStorageSync('userId'); // 尝试从users中获取手机号 if (userId && users[userId] && users[userId].phoneNumber) { userPhone = users[userId].phoneNumber; } else { // 尝试从全局用户信息获取 const userInfo = wx.getStorageSync('userInfo'); if (userInfo && userInfo.phoneNumber) { userPhone = userInfo.phoneNumber; } else { // 尝试从直接存储的phoneNumber获取 userPhone = wx.getStorageSync('phoneNumber'); } } } // 如果没有手机号,直接返回错误 if (!userPhone) { reject(new Error('请先登录并绑定手机号')); return; } const requestData = { user_phone: userPhone }; console.log('获取聊天列表请求数据:', requestData); request('/api/chat/list', 'POST', requestData).then(res => { console.log('获取聊天列表成功:', res); // 标准化响应格式 if (res && Array.isArray(res)) { resolve(res); } else if (res && res.data) { resolve(Array.isArray(res.data) ? res.data : []); } else { resolve([]); } }).catch(error => { console.error('获取聊天列表失败:', error); reject(new Error(error.message || '获取聊天列表失败,请稍后重试')); }); }); }, // 获取业务员详细信息 getSalesPersonnelInfo: function (phone) { console.log('API.getSalesPersonnelInfo - phone:', phone); return new Promise((resolve, reject) => { if (!phone) { reject(new Error('手机号不能为空')); return; } const requestData = { phone: phone }; console.log('获取业务员信息请求数据:', requestData); request('/api/personnel/get', 'POST', requestData).then(res => { console.log('获取业务员信息成功:', res); // 标准化响应格式 if (res && res.data) { resolve(res.data); } else { resolve(res); } }).catch(error => { console.error('获取业务员信息失败:', error); reject(new Error(error.message || '获取业务员信息失败,请稍后重试')); }); }); }, // 标记消息为已读 markMessagesAsRead: function (chatId, userPhone) { console.log('API.markMessagesAsRead - chatId:', chatId, 'userPhone:', userPhone); return new Promise((resolve, reject) => { // 如果没有传入手机号,尝试从本地存储获取 if (!userPhone) { // 获取用户信息,包含手机号 const users = wx.getStorageSync('users') || {}; const userId = wx.getStorageSync('userId'); // 尝试从users中获取手机号 if (userId && users[userId] && users[userId].phoneNumber) { userPhone = users[userId].phoneNumber; } else { // 尝试从全局用户信息获取 const userInfo = wx.getStorageSync('userInfo'); if (userInfo && userInfo.phoneNumber) { userPhone = userInfo.phoneNumber; } else { // 尝试从直接存储的phoneNumber获取 userPhone = wx.getStorageSync('phoneNumber'); } } } // 如果没有手机号,直接返回错误 if (!userPhone) { reject(new Error('请先登录并绑定手机号')); return; } // 如果没有chatId,直接返回错误 if (!chatId) { reject(new Error('聊天ID不能为空')); return; } const requestData = { chat_id: chatId, user_phone: userPhone }; console.log('API.markMessagesAsRead - 请求数据:', requestData); request('/api/chat/read', 'POST', requestData).then(res => { console.log('API.markMessagesAsRead - 响应数据:', res); resolve(res); }).catch(error => { console.error('API.markMessagesAsRead - 请求失败:', error); // 标记为已读的API可能尚未实现,所以不抛出错误,让前端继续执行 resolve({}); }); }); }, // 获取商品分类列表 - 从product表的category字段去重获取 getProductCategories: function () { return new Promise((resolve, reject) => { const openid = wx.getStorageSync('openid') || ''; request('/api/product/categories', 'GET', { openid: openid }).then(res => { console.log('获取商品分类列表成功:', res); if (res && res.success && res.categories) { resolve(res.categories); } else { // 如果接口不存在,返回默认分类 console.warn('获取分类接口未实现,返回默认分类'); resolve(['全部', '粉壳', '褐壳', '绿壳', '白壳']); } }).catch(err => { console.error('获取商品分类列表失败:', err); // 失败时返回默认分类 resolve(['全部', '粉壳', '褐壳', '绿壳', '白壳']); }); }); }, // 价格评估API方法 evaluatePrice: function(month, day, region, breed) { return new Promise((resolve, reject) => { console.log('调用价格评估API:', { month, day, region, breed }); const requestData = { month: month, day: day, region: region, breed: breed }; request('/api/evaluate/price', 'POST', requestData) .then(response => { console.log('价格评估API响应:', response); if (response.success) { resolve(response.data); } else { reject(new Error(response.message || '价格评估失败')); } }) .catch(error => { console.error('价格评估API错误:', error); reject(error); }); }); }, // 根据手机号查询personnel表中是否存在该用户 checkPersonnelByPhone: function (phoneNumber) { console.log('API.checkPersonnelByPhone - phoneNumber:', phoneNumber); return new Promise((resolve, reject) => { if (!phoneNumber) { console.warn('手机号为空,不查询personnel表'); resolve({ exists: false }); return; } const requestData = { phone: phoneNumber }; console.log('查询personnel表请求数据:', requestData); request('/api/personnel/get', 'POST', requestData) .then(res => { console.log('查询personnel表响应:', res); // 如果能获取到数据,说明该用户存在于personnel表中 if (res && (res.data || res.phone || res.id)) { resolve({ exists: true, data: res.data || res }); } else { resolve({ exists: false }); } }) .catch(error => { console.error('查询personnel表失败:', error); // 查询失败时不阻止侧边栏打开,只是没有权限 resolve({ exists: false, error: error.message }); }); }); }, // 获取热门话题 getHotTopics: function() { return new Promise((resolve, reject) => { request('/api/eggbar/hot-topics', 'GET') .then(response => { console.log('获取热门话题成功:', response); resolve(response); }) .catch(error => { console.error('获取热门话题失败:', error); reject(error); }); }); }, // 获取动态列表 getPosts: function(params) { return new Promise((resolve, reject) => { request('/api/eggbar/posts', 'GET', params) .then(response => { console.log('获取动态列表成功:', response); resolve(response); }) .catch(error => { console.error('获取动态列表失败:', error); reject(error); }); }); }, // 点赞动态 likePost: function(postId, phone) { return new Promise((resolve, reject) => { request(`/api/eggbar/posts/${postId}/like`, 'POST', { phone: phone }) .then(response => { console.log('点赞成功:', response); resolve(response); }) .catch(error => { console.error('点赞失败:', error); reject(error); }); }); }, // 创建动态 createPost: function(postData) { return new Promise((resolve, reject) => { request('/api/eggbar/posts', 'POST', postData) .then(response => { console.log('创建动态成功:', response); resolve(response); }) .catch(error => { console.error('创建动态失败:', error); reject(error); }); }); } };