From 91c872a1a0c0be663776f60c53fcd0e117fe75de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=90=E9=A3=9E=E6=B4=8B?= <15778543+xufeiyang6017@user.noreply.gitee.com> Date: Thu, 29 Jan 2026 10:19:36 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=9E=E7=8E=B0=E5=AE=8C=E6=95=B4?= =?UTF-8?q?=E7=9A=84=E7=82=B9=E8=B5=9E=E5=8A=9F=E8=83=BD=E5=92=8C=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E5=8A=A8=E6=80=81=E6=98=BE=E7=A4=BA=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pages/eggbar/eggbar.js | 59 ++++++- pages/eggbar/eggbar.wxml | 6 +- server-example/server-mysql.js | 291 +++++++++++++++++++++++++++++---- utils/api.js | 4 +- 4 files changed, 321 insertions(+), 39 deletions(-) diff --git a/pages/eggbar/eggbar.js b/pages/eggbar/eggbar.js index 2bcda6e..ee665ad 100644 --- a/pages/eggbar/eggbar.js +++ b/pages/eggbar/eggbar.js @@ -83,12 +83,25 @@ Page({ loading: true }); - API.getPosts({ + // 获取用户电话号码 + const userInfo = wx.getStorageSync('userInfo'); + const phoneNumber = userInfo?.phoneNumber || wx.getStorageSync('phoneNumber'); + + // 只有当电话号码存在时才传递,避免传递空值 + const params = { page: this.data.page, pageSize: this.data.pageSize - }).then(res => { + }; + if (phoneNumber) { + params.phone = phoneNumber; + } + + API.getPosts(params).then(res => { + console.log('后端返回的完整响应:', res); // 正确处理后端返回的响应格式 let newPosts = res.data && res.data.posts ? res.data.posts : []; + console.log('后端返回的动态数量:', newPosts.length); + console.log('后端返回的动态数据:', newPosts); // 处理images字段,确保它是一个数组 newPosts = newPosts.map(post => { @@ -243,6 +256,20 @@ Page({ likePost(e) { const postId = e.currentTarget.dataset.id; + + // 获取用户电话号码 + const userInfo = wx.getStorageSync('userInfo'); + const phoneNumber = userInfo?.phoneNumber || wx.getStorageSync('phoneNumber'); + + if (!phoneNumber) { + wx.showToast({ + title: '请先登录获取电话号码', + icon: 'none' + }); + return; + } + + // 前端临时更新状态 const posts = this.data.posts.map(post => { if (post.id === postId) { return { @@ -256,10 +283,34 @@ Page({ this.setData({ posts }); - API.likePost(postId).then(res => { - console.log('点赞成功'); + // 调用API + API.likePost(postId, phoneNumber).then(res => { + console.log('点赞成功:', res); + wx.showToast({ + title: res.message || '操作成功', + icon: 'success' + }); }).catch(err => { console.error('点赞失败:', err); + + // 恢复原始状态 + const originalPosts = this.data.posts.map(post => { + if (post.id === postId) { + return { + ...post, + liked: !post.liked, + likes: post.liked ? post.likes + 1 : post.likes - 1 + }; + } + return post; + }); + + this.setData({ posts: originalPosts }); + + wx.showToast({ + title: '操作失败,请重试', + icon: 'none' + }); }); }, diff --git a/pages/eggbar/eggbar.wxml b/pages/eggbar/eggbar.wxml index 5bdeceb..a75559c 100644 --- a/pages/eggbar/eggbar.wxml +++ b/pages/eggbar/eggbar.wxml @@ -42,15 +42,15 @@ - + {{item.liked ? '❤️' : '🤍'}} {{item.likes}} - + 💬 {{item.comments}} - + 📤 分享 diff --git a/server-example/server-mysql.js b/server-example/server-mysql.js index cb7a77f..f30dc2b 100644 --- a/server-example/server-mysql.js +++ b/server-example/server-mysql.js @@ -211,6 +211,9 @@ app.get('/api/eggbar/posts', async (req, res) => { } ); + console.log('数据库查询结果数量:', posts.length); + console.log('数据库查询结果:', posts); + // 关闭临时连接 await tempSequelize.close(); @@ -233,20 +236,78 @@ app.get('/api/eggbar/posts', async (req, res) => { }); // 格式化响应数据 - const formattedPosts = paginatedPosts.map(post => ({ - id: post.id, - user_id: post.user_id, - phone: post.phone, - content: post.content, - images: post.images, - topic: post.topic, - likes: post.likes || 0, - comments: post.comments || 0, - shares: post.shares || 0, - status: post.status, - created_at: post.created_at, - updated_at: post.updated_at - })); + let formattedPosts = paginatedPosts.map(post => { + // 解析images字段,确保它是一个数组 + let images = []; + if (post.images) { + if (typeof post.images === 'string') { + try { + images = JSON.parse(post.images); + if (!Array.isArray(images)) { + images = []; + } + } catch (e) { + images = []; + } + } else if (Array.isArray(post.images)) { + images = post.images; + } else { + images = []; + } + } + + return { + id: post.id, + user_id: post.user_id, + phone: post.phone, + content: post.content, + images: images, + topic: post.topic, + likes: post.likes || 0, + comments: post.comments || 0, + shares: post.shares || 0, + status: post.status, + created_at: post.created_at, + updated_at: post.updated_at + }; + }); + + // 检查用户是否已点赞 + const phone = req.query.phone || req.headers['x-phone']; + if (phone) { + try { + // 批量检查点赞状态 + const postsWithLikedStatus = await Promise.all(formattedPosts.map(async post => { + const existingLike = await EggbarLike.findOne({ + where: { + post_id: post.id, + phone: phone + } + }); + return { + ...post, + liked: !!existingLike + }; + })); + formattedPosts = postsWithLikedStatus; + } catch (error) { + console.warn('批量检查点赞状态时出错:', error); + // 如果出错,给所有帖子添加默认未点赞状态 + formattedPosts = formattedPosts.map(post => ({ + ...post, + liked: false + })); + } + } else { + // 没有电话号码,给所有帖子添加默认未点赞状态 + formattedPosts = formattedPosts.map(post => ({ + ...post, + liked: false + })); + } + + console.log('5. 帖子列表格式化完成,带点赞状态'); + console.log('格式化后的数据数量:', formattedPosts.length); res.json({ success: true, @@ -422,21 +483,125 @@ app.post('/api/eggbar/upload', upload.single('image'), async (req, res) => { }); } - // 使用OSS上传图片 - const imageUrl = await OssUploader.uploadFile(req.file.path, 'eggbar', 'image'); + const tempFilePath = req.file.path; + + try { + // 使用OSS上传图片 + const imageUrl = await OssUploader.uploadFile(tempFilePath, 'eggbar', 'image'); + + console.log('3. 图片上传成功,URL:', imageUrl); + + // 删除临时文件 + if (fs.existsSync(tempFilePath)) { + fs.unlinkSync(tempFilePath); + console.log('4. 临时文件已删除:', tempFilePath); + } + + res.json({ + success: true, + message: '图片上传成功', + imageUrl: imageUrl + }); + } finally { + // 确保临时文件被删除,即使OSS上传失败 + if (tempFilePath && fs.existsSync(tempFilePath)) { + try { + fs.unlinkSync(tempFilePath); + console.log('临时文件已清理:', tempFilePath); + } catch (cleanupError) { + console.warn('清理临时文件时出错:', cleanupError); + } + } + } + } catch (error) { + console.error('上传图片失败:', error); + res.status(500).json({ + success: false, + message: '上传图片失败: ' + error.message + }); + } +}); + +// Eggbar 点赞接口 +app.post('/api/eggbar/posts/:postId/like', async (req, res) => { + try { + const postId = parseInt(req.params.postId); + const { phone } = req.body; + + console.log('===== 收到点赞请求 ====='); + console.log('1. 动态ID:', postId); + console.log('2. 电话号码:', phone); + + // 数据验证 + if (!postId || !phone) { + return res.status(400).json({ + success: false, + code: 400, + message: '缺少必要参数' + }); + } + + // 检查动态是否存在 + const post = await EggbarPost.findByPk(postId); + if (!post) { + return res.status(404).json({ + success: false, + code: 404, + message: '动态不存在' + }); + } + + // 检查用户是否已经点过赞 + const existingLike = await EggbarLike.findOne({ + where: { + post_id: postId, + phone: phone + } + }); + + let isLiked = false; + let newLikeCount = post.likes || 0; + + if (existingLike) { + // 已经点过赞,取消点赞 + await existingLike.destroy(); + newLikeCount = Math.max(0, newLikeCount - 1); + isLiked = false; + console.log('3. 取消点赞成功'); + } else { + // 没点过赞,添加点赞 + await EggbarLike.create({ + post_id: postId, + phone: phone + }); + newLikeCount = newLikeCount + 1; + isLiked = true; + console.log('3. 点赞成功'); + } + + // 更新动态的点赞数 + await EggbarPost.update( + { likes: newLikeCount }, + { where: { id: postId } } + ); - console.log('3. 图片上传成功,URL:', imageUrl); + console.log('4. 点赞数更新成功,新点赞数:', newLikeCount); res.json({ success: true, - message: '图片上传成功', - imageUrl: imageUrl + code: 200, + message: isLiked ? '点赞成功' : '取消点赞成功', + data: { + isLiked: isLiked, + likes: newLikeCount + } }); } catch (error) { - console.error('上传图片失败:', error); + console.error('点赞操作失败:', error); res.status(500).json({ success: false, - message: '上传图片失败: ' + error.message + code: 500, + message: '点赞操作失败: ' + error.message }); } }); @@ -1642,6 +1807,37 @@ EggbarPost.init({ timestamps: false }); +// Eggbar 点赞模型 - 用于存储用户点赞记录 +class EggbarLike extends Model { } +EggbarLike.init({ + id: { + type: DataTypes.INTEGER, + primaryKey: true, + autoIncrement: true, + comment: '点赞记录ID' + }, + post_id: { + type: DataTypes.INTEGER, + allowNull: false, + comment: '动态ID' + }, + phone: { + type: DataTypes.STRING(255), + allowNull: false, + comment: '电话号码' + }, + created_at: { + type: DataTypes.DATE, + defaultValue: Sequelize.NOW, + comment: '创建时间' + } +}, { + sequelize: eggbarSequelize, + modelName: 'EggbarLike', + tableName: 'eggbar_likes', + timestamps: false +}); + // 定义模型之间的关联关系 // 用户和商品的一对多关系 (卖家发布商品) @@ -2907,7 +3103,11 @@ app.post('/api/products/upload', upload.array('images', 10), async (req, res) => const uploadedFileUrls = new Set(); // 准备文件路径数组 - const filePaths = uploadedFiles.map(file => file.path); + const filePaths = uploadedFiles.map(file => { + // 添加到临时文件清理列表 + tempFilesToClean.push(file.path); + return file.path; + }); // 使用商品名称作为文件夹名,确保每个商品的图片独立存储 // 移除商品名称中的特殊字符,确保可以作为合法的文件夹名 @@ -4081,15 +4281,30 @@ app.post('/api/products/upload', upload.array('images', 10), async (req, res) => code: 500, error: err.message }); + } finally { + // 确保临时文件被清理 + if (tempFilesToClean.length > 0) { + try { + cleanTempFiles(tempFilesToClean); + } catch (cleanupError) { + console.warn('清理临时文件时出错:', cleanupError); + } + } } }); // 【关键修复】在 handleAddImagesToExistingProduct 函数中加强图片合并逻辑 async function handleAddImagesToExistingProduct(req, res, existingProductId, uploadedFiles) { let transaction; + let tempFilesToClean = []; try { console.log('【图片更新模式】开始处理图片上传到已存在商品,商品ID:', existingProductId); + // 收集需要清理的临时文件路径 + for (const file of uploadedFiles) { + tempFilesToClean.push(file.path); + } + // 使用事务确保数据一致性 transaction = await sequelize.transaction(); @@ -4247,6 +4462,15 @@ async function handleAddImagesToExistingProduct(req, res, existingProductId, upl code: 500, error: error.message }); + } finally { + // 确保临时文件被清理 + if (tempFilesToClean.length > 0) { + try { + cleanTempFiles(tempFilesToClean); + } catch (cleanupError) { + console.warn('清理临时文件时出错:', cleanupError); + } + } } } @@ -7506,6 +7730,7 @@ app.post('/api/settlement/submit', async (req, res) => { // 上传入驻文件 app.post('/api/settlement/upload', upload.single('file'), async (req, res) => { + let tempFilePath = null; try { const { openid, fileType } = req.body; @@ -7527,12 +7752,13 @@ app.post('/api/settlement/upload', upload.single('file'), async (req, res) => { }); } + tempFilePath = req.file.path; + // 上传文件到OSS - 使用静态方法调用 // 注意:OssUploader.uploadFile直接返回URL字符串,而不是包含url属性的对象 - const fileUrl = await OssUploader.uploadFile(req.file.path, `settlement/${fileType}/${Date.now()}_${req.file.originalname}`); + const fileUrl = await OssUploader.uploadFile(tempFilePath, `settlement/${fileType}/${Date.now()}_${req.file.originalname}`); - // 删除临时文件 - fs.unlinkSync(req.file.path); + // 确保返回的URL是干净的字符串,移除可能存在的反引号和空格 const cleanFileUrl = String(fileUrl).replace(/[` ]/g, ''); @@ -7549,16 +7775,21 @@ app.post('/api/settlement/upload', upload.single('file'), async (req, res) => { } catch (error) { console.error('入驻文件上传失败:', error); - // 清理临时文件 - if (req.file && fs.existsSync(req.file.path)) { - fs.unlinkSync(req.file.path); - } - res.status(500).json({ success: false, code: 500, message: '文件上传失败: ' + error.message }); + } finally { + // 确保临时文件被清理 + if (tempFilePath && fs.existsSync(tempFilePath)) { + try { + fs.unlinkSync(tempFilePath); + console.log('临时文件已清理:', tempFilePath); + } catch (cleanupError) { + console.warn('清理临时文件时出错:', cleanupError); + } + } } }); diff --git a/utils/api.js b/utils/api.js index 7bbc547..d1052ff 100644 --- a/utils/api.js +++ b/utils/api.js @@ -4539,9 +4539,9 @@ module.exports = { }, // 点赞动态 - likePost: function(postId) { + likePost: function(postId, phone) { return new Promise((resolve, reject) => { - request(`/api/eggbar/posts/${postId}/like`, 'POST') + request(`/api/eggbar/posts/${postId}/like`, 'POST', { phone: phone }) .then(response => { console.log('点赞成功:', response); resolve(response);