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