You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

5060 lines
209 KiB

3 months ago
// 简化版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();
3 months ago
// 重要提示:真机调试时,请确保以下操作:
// 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();
3 months ago
// 明确识别开发者工具环境
if (deviceInfo && (deviceInfo.platform === 'devtools' || deviceInfo.environment === 'devtools')) {
3 months ago
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 = {
3 months ago
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 = {}) {
3 months ago
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({
3 months ago
url: BASE_URL + url,
method: method || 'GET',
data: data || {},
header: header,
3 months ago
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('请求网络错误详细信息:', {
3 months ago
url: BASE_URL + url,
error: err,
errMsg: err.errMsg,
errCode: err.errCode
3 months ago
});
// 创建基础错误对象
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('请求完成详细信息:', {
3 months ago
url: BASE_URL + url,
type: res.errMsg.includes('ok') ? '成功' : '失败',
statusCode: res.statusCode,
errMsg: res.errMsg,
responseData: res.data
3 months ago
});
}
});
// 如果提供了请求上下文,保存请求任务对象
if (requestContext) {
requestContext.task = requestTask;
}
3 months ago
});
}
// 添加用户踪迹记录功能
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和其他功能
3 months ago
module.exports = {
// 包含基础API功能
...baseApi,
// 添加request函数
request: request,
// 添加用户踪迹记录功能
addUserTrace: addUserTrace,
3 months ago
// 添加商品到购物车 - 增强版本,即使本地找不到商品也尝试直接请求服务器
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);
3 months ago
// 1. 验证用户登录状态
if (!userPhone) {
3 months ago
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 => {
3 months ago
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);
3 months ago
}).catch(innerErr => {
console.error('刷新商品列表失败:', innerErr);
// 刷新失败也尝试再次发送请求,不轻易放弃
if (attempts < maxAttempts) {
console.log('刷新失败,但仍尝试再次发送请求');
this.sendAddToCartRequest(userPhone, basicProduct).then(resolve).catch(reject);
3 months ago
} 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字符串
3 months ago
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) {
3 months ago
return new Promise((resolve, reject) => {
// 重要:直接使用传入的userPhone参数
3 months ago
console.log('构建的product对象:', product);
console.log('发送添加到购物车请求,productId:', product.productId, '类型:', typeof product.productId);
console.log('用户手机号:', userPhone);
3 months ago
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: {...} }
3 months ago
// 这是最简单直接的格式,服务器会自动从product对象中提取数据
const requestData = {
user_phone: userPhone,
3 months ago
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 = '') {
3 months ago
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 // 添加关键词搜索
3 months ago
}).then(res => {
if (res && (res.code === 200 || res.success)) {
const products = res.products || [];
3 months ago
// 将商品列表存储到本地缓存
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
});
3 months ago
} 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<Object>} 支付请求结果
*/
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);
},
3 months ago
// 从购物车移除商品
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('获取货源分类失败,请稍后重试'));
});
});
},
3 months ago
// 从所有用户的购物车中移除指定商品
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;
});
},
// 记录用户操作历史
addUserHistory: function (operation) {
console.log('API.addUserHistory - 操作类型:', operation);
// 获取用户手机号
let userPhone = '';
try {
const userInfo = wx.getStorageSync('userInfo');
if (userInfo && userInfo.phoneNumber) {
userPhone = userInfo.phoneNumber;
} else {
const users = wx.getStorageSync('users') || {};
const userId = wx.getStorageSync('userId');
if (userId && users[userId] && users[userId].phoneNumber) {
userPhone = users[userId].phoneNumber;
} else {
userPhone = wx.getStorageSync('phoneNumber');
}
}
} catch (e) {
console.error('获取用户手机号失败:', e);
}
if (!userPhone) {
console.warn('用户未登录,无法记录操作历史');
return Promise.resolve({ success: false, message: '用户未登录' });
}
const requestData = {
phone: userPhone,
operation: operation
};
return request('/api/user-history/add', 'POST', requestData).then(res => {
console.log('操作历史记录成功:', res);
return res;
}).catch(err => {
console.error('操作历史记录失败:', err);
// 即使记录失败,也不影响主流程
return { success: false, message: '操作历史记录失败' };
});
},
// 计算物流运费
calculateFreight: function (params) {
console.log('API.calculateFreight - 开始计算运费');
console.log('API.calculateFreight - 参数:', params);
// 参数验证
if (!params.origin || !params.destination) {
console.error('API.calculateFreight - 出发地和目的地不能为空');
return Promise.reject(new Error('出发地和目的地不能为空'));
}
// 如果没有重量和体积,设置默认值 1
if (!params.goodsInfo) {
params.goodsInfo = { weight: 1 };
} else if (!params.goodsInfo.weight && !params.goodsInfo.volume) {
params.goodsInfo.weight = 1;
}
// 直接使用腾讯地图API计算真实距离
return new Promise((resolve, reject) => {
// 定义获取经纬度的函数
function getCoordinates(address) {
return new Promise((resolve, reject) => {
wx.request({
url: 'https://apis.map.qq.com/ws/geocoder/v1/',
data: {
address: address,
key: 'OB4BZ-D4W3U-B7VVO-4PJWW-6TKDJ-WPB77'
},
success: function (response) {
if (response.data.status === 0 && response.data.result && response.data.result.location) {
resolve({
latitude: response.data.result.location.lat,
longitude: response.data.result.location.lng
});
} else {
reject(new Error('地址解析失败'));
}
},
fail: function (err) {
reject(err);
}
});
});
}
// 检查是否有经纬度信息
if (params.origin.latitude && params.origin.longitude && params.destination.latitude && params.destination.longitude) {
// 直接使用经纬度计算距离
const origin = `${params.origin.latitude},${params.origin.longitude}`;
const destination = `${params.destination.latitude},${params.destination.longitude}`;
wx.request({
url: 'https://apis.map.qq.com/ws/distance/v1/matrix',
data: {
mode: 'driving',
from: origin,
to: destination,
key: 'OB4BZ-D4W3U-B7VVO-4PJWW-6TKDJ-WPB77'
},
success: function (response) {
console.log('腾讯地图API距离计算成功:', response);
console.log('腾讯地图API响应状态:', response.data.status);
console.log('腾讯地图API响应结果:', response.data.result);
if (response.data.status === 0 && response.data.result) {
// 检查result结构,适应不同的API响应格式
let distance;
try {
// 优先检查 rows 字段(腾讯地图 API 的标准格式)
if (response.data.result.rows && response.data.result.rows.length > 0) {
const row = response.data.result.rows[0];
if (row.elements && row.elements.length > 0) {
distance = row.elements[0].distance / 1000; // 转换为公里
}
}
// 然后检查 elements 字段
else if (response.data.result.elements && response.data.result.elements.length > 0) {
distance = response.data.result.elements[0].distance / 1000; // 转换为公里
}
// 最后检查 distance 字段
else if (response.data.result.distance) {
distance = response.data.result.distance / 1000; // 转换为公里
}
// 如果都没有,抛出错误
else {
console.error('无法获取距离数据,响应结构:', response.data.result);
throw new Error('无法获取距离数据');
}
const baseRate = 0.5; // 默认基础费率(元/公里/公斤)
const weight = params.goodsInfo.weight || 1;
const freight = Math.floor(distance * baseRate * weight);
const deliveryTime = Math.ceil(distance / 200); // 公路:200公里/天
console.log('腾讯地图API距离计算成功,距离:', distance, '公里');
resolve({
success: true,
data: {
freight: freight,
distance: Math.round(distance * 10) / 10, // 保留一位小数
deliveryTime: deliveryTime
}
});
} catch (error) {
console.error('处理距离数据时出错:', error);
// 如果处理距离数据时出错,使用随机数据作为fallback
const mockDistance = Math.floor(Math.random() * 1000) + 100; // 100-1100公里
const baseRate = 0.5;
const weight = params.goodsInfo.weight || 1;
const mockFreight = Math.floor(mockDistance * baseRate * weight);
const mockDeliveryTime = Math.ceil(mockDistance / 200);
resolve({
success: true,
data: {
freight: mockFreight,
distance: mockDistance,
deliveryTime: mockDeliveryTime
}
});
}
} else {
console.error('腾讯地图API距离计算失败:', response.data);
// 如果腾讯地图API调用失败,使用随机数据作为fallback
const mockDistance = Math.floor(Math.random() * 1000) + 100; // 100-1100公里
const baseRate = 0.5;
const weight = params.goodsInfo.weight || 1;
const mockFreight = Math.floor(mockDistance * baseRate * weight);
const mockDeliveryTime = Math.ceil(mockDistance / 200);
resolve({
success: true,
data: {
freight: mockFreight,
distance: mockDistance,
deliveryTime: mockDeliveryTime
}
});
}
},
fail: function (err) {
console.error('腾讯地图API调用失败:', err);
// 如果API调用失败,使用随机数据作为fallback
const mockDistance = Math.floor(Math.random() * 1000) + 100; // 100-1100公里
const baseRate = 0.5;
const weight = params.goodsInfo.weight || 1;
const mockFreight = Math.floor(mockDistance * baseRate * weight);
const mockDeliveryTime = Math.ceil(mockDistance / 200);
resolve({
success: true,
data: {
freight: mockFreight,
distance: mockDistance,
deliveryTime: mockDeliveryTime
}
});
}
});
} else {
// 没有经纬度信息,先进行地址解析
const originAddress = `${params.origin.province}${params.origin.city}${params.origin.district}${params.origin.detail}`;
const destinationAddress = `${params.destination.province}${params.destination.city}${params.destination.district}${params.destination.detail}`;
Promise.all([
getCoordinates(originAddress),
getCoordinates(destinationAddress)
]).then(([originCoords, destinationCoords]) => {
// 使用解析得到的经纬度计算距离
const origin = `${originCoords.latitude},${originCoords.longitude}`;
const destination = `${destinationCoords.latitude},${destinationCoords.longitude}`;
wx.request({
url: 'https://apis.map.qq.com/ws/distance/v1/matrix',
data: {
mode: 'driving',
from: origin,
to: destination,
key: 'OB4BZ-D4W3U-B7VVO-4PJWW-6TKDJ-WPB77'
},
success: function (response) {
console.log('腾讯地图API距离计算成功:', response);
console.log('腾讯地图API响应状态:', response.data.status);
console.log('腾讯地图API响应结果:', response.data.result);
if (response.data.status === 0 && response.data.result) {
// 检查result结构,适应不同的API响应格式
let distance;
try {
// 优先检查 rows 字段(腾讯地图 API 的标准格式)
if (response.data.result.rows && response.data.result.rows.length > 0) {
const row = response.data.result.rows[0];
if (row.elements && row.elements.length > 0) {
distance = row.elements[0].distance / 1000; // 转换为公里
}
}
// 然后检查 elements 字段
else if (response.data.result.elements && response.data.result.elements.length > 0) {
distance = response.data.result.elements[0].distance / 1000; // 转换为公里
}
// 最后检查 distance 字段
else if (response.data.result.distance) {
distance = response.data.result.distance / 1000; // 转换为公里
}
// 如果都没有,抛出错误
else {
console.error('无法获取距离数据,响应结构:', response.data.result);
throw new Error('无法获取距离数据');
}
const baseRate = 0.5; // 默认基础费率(元/公里/公斤)
const weight = params.goodsInfo.weight || 1;
const freight = Math.floor(distance * baseRate * weight);
const deliveryTime = Math.ceil(distance / 200); // 公路:200公里/天
console.log('腾讯地图API距离计算成功,距离:', distance, '公里');
resolve({
success: true,
data: {
freight: freight,
distance: Math.round(distance * 10) / 10, // 保留一位小数
deliveryTime: deliveryTime
}
});
} catch (error) {
console.error('处理距离数据时出错:', error);
// 如果处理距离数据时出错,使用随机数据作为fallback
const mockDistance = Math.floor(Math.random() * 1000) + 100; // 100-1100公里
const baseRate = 0.5;
const weight = params.goodsInfo.weight || 1;
const mockFreight = Math.floor(mockDistance * baseRate * weight);
const mockDeliveryTime = Math.ceil(mockDistance / 200);
resolve({
success: true,
data: {
freight: mockFreight,
distance: mockDistance,
deliveryTime: mockDeliveryTime
}
});
}
} else {
console.error('腾讯地图API距离计算失败:', response.data);
// 如果腾讯地图API调用失败,使用随机数据作为fallback
const mockDistance = Math.floor(Math.random() * 1000) + 100; // 100-1100公里
const baseRate = 0.5;
const weight = params.goodsInfo.weight || 1;
const mockFreight = Math.floor(mockDistance * baseRate * weight);
const mockDeliveryTime = Math.ceil(mockDistance / 200);
resolve({
success: true,
data: {
freight: mockFreight,
distance: mockDistance,
deliveryTime: mockDeliveryTime
}
});
}
},
fail: function (err) {
console.error('腾讯地图API调用失败:', err);
// 如果API调用失败,使用随机数据作为fallback
const mockDistance = Math.floor(Math.random() * 1000) + 100; // 100-1100公里
const baseRate = 0.5;
const weight = params.goodsInfo.weight || 1;
const mockFreight = Math.floor(mockDistance * baseRate * weight);
const mockDeliveryTime = Math.ceil(mockDistance / 200);
resolve({
success: true,
data: {
freight: mockFreight,
distance: mockDistance,
deliveryTime: mockDeliveryTime
}
});
}
});
}).catch(err => {
console.error('地址解析失败:', err);
// 如果地址解析失败,使用随机数据作为fallback
const mockDistance = Math.floor(Math.random() * 1000) + 100; // 100-1100公里
const baseRate = 0.5;
const weight = params.goodsInfo.weight || 1;
const mockFreight = Math.floor(mockDistance * baseRate * weight);
const mockDeliveryTime = Math.ceil(mockDistance / 200);
resolve({
success: true,
data: {
freight: mockFreight,
distance: mockDistance,
deliveryTime: mockDeliveryTime
}
});
});
}
});
},
3 months ago
// 发布商品 - 支持图片上传(修复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 || '', // 【新增】添加种类字段
3 months ago
// 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
})
},
3 months ago
// 更新商品图片 - 专门用于为已存在的商品上传图片
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;
});
},
3 months ago
// 上传带图片的商品 - 改进版,确保所有图片都被实际上传到服务器
uploadProductWithImages: function (productData, imageUrls) {
console.log('===== 开始上传带图片的商品 =====');
console.log('商品数据:', productData);
console.log('图片数量:', imageUrls.length);
// 【关键修复】确保sellerId使用userId,无论是否已存在
3 months ago
const userId = wx.getStorageSync('userId');
if (userId) {
3 months ago
console.log('【修复】确保sellerId使用userId:', userId);
productData.sellerId = userId;
} else {
console.error('【错误】本地缓存中没有userId,请重新登录');
return Promise.reject(new Error('用户未登录,请重新登录'));
3 months ago
}
// 如果没有图片,使用普通请求
if (!imageUrls || imageUrls.length === 0) {
console.log('没有检测到图片,使用普通请求');
return request('/api/products/upload', 'POST', {
productData: JSON.stringify(productData)
});
}
// 创建包含所有图片URL的商品数据
const productDataWithAllImages = {
...productData,
sellerId: userId, // 【确保】使用userId
3 months ago
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,无论是否已存在
3 months ago
const userId = wx.getStorageSync('userId');
if (userId) {
3 months ago
console.log('【修复】确保sellerId使用userId:', userId);
productData.sellerId = userId;
} else {
console.error('【错误】本地缓存中没有userId,请重新登录');
return Promise.reject(new Error('用户未登录,请重新登录'));
3 months ago
}
// 防御性检查
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 = {}) {
3 months ago
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);
3 months ago
// 不再因为没有openid而拒绝请求,允许未登录用户查看公开商品
3 months ago
// 确保分页参数存在,默认为page=1, pageSize=10
const page = options.page || 1;
const pageSize = options.pageSize || 10;
// 添加时间戳参数防止请求缓存
const requestData = {
status: statusList, // 传递状态数组
3 months ago
// 不设置默认的viewMode,让调用方根据需要设置
_t: options.timestamp || new Date().getTime(), // 添加时间戳参数防止缓存
// 始终包含分页参数
page: page,
pageSize: pageSize
};
3 months ago
// 无论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 - 未指定搜索关键词');
}
3 months ago
console.log('API.getProductList - 分页参数:', { page: page, pageSize: pageSize });
console.log('API.getProductList - 查询状态:', statusList);
3 months ago
console.log('API.getProductList - 请求数据:', requestData);
console.log('API.getProductList - 请求URL:', BASE_URL + '/api/product/list');
console.log('API.getProductList - 请求方法:', 'POST');
3 months ago
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
3 months ago
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);
3 months ago
console.log('原始selected字段值:', product.selected);
console.log('原始reservedCount字段值:', product.reservedCount);
console.log('原始reservationCount字段值:', product.reservationCount);
console.log('处理后的selectedCount值:', selectedCount);
}
// 返回处理后的商品数据,确保包含selected字段、正确的ID和名称字段映射,以及原始图片URL
3 months ago
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 : ''
3 months ago
};
});
// 打印第一个商品的详细信息
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
});
}
});
},
// 微信登录函数 - 增强版,强制要求手机号授权登录
3 months ago
login: function (encryptedData = null, iv = null) {
return new Promise((resolve, reject) => {
// 强制要求手机号授权
if (!encryptedData || !iv) {
return reject(new Error('登录必须进行手机号授权'));
}
3 months ago
// 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);
3 months ago
// 如果有手机号信息,上传到服务器
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 }
});
});
3 months ago
});
}).catch(phoneErr => {
console.error('手机号上传失败:', phoneErr);
// 手机号上传失败导致登录失败
reject(new Error('手机号授权失败: ' + (phoneErr.message || '未知错误')));
3 months ago
});
} 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' }
});
3 months ago
});
}
} 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('用户未登录'));
3 months ago
}
// 检查是否包含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');
3 months ago
wx.removeStorageSync('sessionKey');
} catch (e) {
console.error('清除过期登录信息失败:', e);
}
// 重新登录后重试
return this.login().then(loginRes => {
3 months ago
return tryUpload();
});
} else {
// 其他错误或重试次数用完,直接抛出
throw error;
3 months ago
}
});
};
// 开始第一次尝试
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('获取收藏列表失败,请稍后重试'));
});
});
},
3 months ago
// 上传用户信息到服务器
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;
}
},
3 months ago
// 获取用户信息用于调试
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);
// 验证登录状态和手机号
3 months ago
if (!openid) {
return Promise.reject(new Error('用户未登录'));
}
if (!phoneNumber) {
return Promise.reject(new Error('用户未完成手机号授权'));
}
3 months ago
return request('/api/user/validate', 'POST', {
openid: openid,
phoneNumber: phoneNumber
3 months ago
});
},
3 months ago
// 撤回备案申请
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
3 months ago
});
},
// 将商品状态设置为隐藏(软删除)
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 };
});
},
3 months ago
// 编辑商品方法 - 修复版
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 || '',
3 months ago
},
// 同时在顶层也传递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);
2 weeks ago
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 });
},
3 months ago
/**
* 上传入驻申请文件
* @param {String} filePath - 本地文件路径
* @param {String} fileType - 文件类型license, proof, brand
* @returns {Promise<Object>} - 上传结果
*/
uploadSettlementFile: function (filePath, fileType) {
return new Promise((resolve, reject) => {
const openid = wx.getStorageSync('openid');
const userId = wx.getStorageSync('userId');
3 months ago
if (!openid) {
reject(new Error('用户未登录'));
return;
}
3 months ago
if (!filePath) {
reject(new Error('文件路径不能为空'));
return;
}
3 months ago
if (!fileType) {
reject(new Error('文件类型不能为空'));
return;
}
3 months ago
// 生成会话ID,确保文件上传的唯一性
const sessionId = `settlement_${Date.now()}_${Math.floor(Math.random() * 1000000)}`;
3 months ago
console.log('开始上传入驻文件:', filePath, '文件类型:', fileType);
console.log('上传会话ID:', sessionId);
3 months ago
// 使用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);
3 months ago
// 检查响应状态
if (res.statusCode >= 200 && res.statusCode < 300) {
const data = JSON.parse(res.data);
console.log('解析后的响应数据:', data);
3 months ago
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);
3 months ago
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);
});
});
},
// 获取二维码合集
getQrCollection: function (params = {}) {
console.log('API.getQrCollection - params:', params);
return new Promise((resolve, reject) => {
request('/api/certificate/getQrCollection', 'GET', params)
.then(response => {
console.log('获取二维码合集成功:', response);
resolve(response);
})
.catch(error => {
console.error('获取二维码合集失败:', error);
reject(error);
});
});
}
3 months ago
};