// +---------------------------------------------------------------------- // | Tool模块API接口 // +---------------------------------------------------------------------- // | 包含:食谱计算器、AI营养师、饮食打卡、食物百科、营养知识等接口 // +---------------------------------------------------------------------- import request from "@/utils/request.js"; import { HTTP_REQUEST_URL, TOKENNAME } from "@/config/app.js"; import store from "@/store"; // ==================== 食谱计算器相关 ==================== /** * 计算营养方案 * @param {Object} data - 计算参数 * @param {String} data.gender - 性别:male/female * @param {Number} data.age - 年龄 * @param {Number} data.height - 身高(cm) * @param {Boolean} data.dialysis - 是否透析:true/false * @param {String} data.dialysisType - 透析类型:hemodialysis/peritoneal (可选) * @param {Number} data.dryWeight - 干体重(kg) * @param {Number} data.creatinine - 血肌酐(μmol/L) */ export function calculateNutrition(data) { return request.post('tool/calculator/calculate', data); } /** * 获取计算结果详情 * @param {Number} id - 计算结果ID */ export function getCalculatorResult(id) { return request.get('tool/calculator/result/' + id); } /** * 采纳营养计划 * @param {Number} resultId - 计算结果ID */ export function adoptNutritionPlan(resultId) { return request.post('tool/calculator/adopt', { resultId }); } // ==================== AI营养师相关 ==================== /** * 发送消息给AI营养师 * @param {Object} data - 消息数据 * @param {String} data.content - 消息内容 * @param {String} data.type - 消息类型:text/image/voice * @param {String} data.imageUrl - 图片URL(当type为image时) * @param {String} data.voiceUrl - 语音URL(当type为voice时) * @param {String} data.conversationId - 会话ID(可选,用于继续对话) */ export function sendAIMessage(data) { return request.post('tool/ai-nutritionist/message', data); } /** * 获取AI回复 * @param {String} messageId - 消息ID */ export function getAIResponse(messageId) { return request.get('tool/ai-nutritionist/response/' + messageId); } /** * 获取对话历史 * @param {Object} data - 查询参数 * @param {String} data.conversationId - 会话ID(可选) * @param {Number} data.page - 页码 * @param {Number} data.limit - 每页数量 */ export function getConversationHistory(data) { return request.get('tool/ai-nutritionist/history', data); } /** * 清空对话历史 * @param {String} conversationId - 会话ID(可选) */ export function clearConversation(conversationId) { return request.post('tool/ai-nutritionist/clear', { conversationId }); } // ==================== 饮食打卡相关 ==================== /** * 提交打卡记录 * @param {Object} data - 打卡数据 * @param {String} data.mealType - 餐次:breakfast/lunch/dinner/snack * @param {String} data.date - 打卡日期:YYYY-MM-DD * @param {Array} data.images - 图片URL数组 * @param {String} data.remark - 备注 * @param {String} data.voiceUrl - 语音备注URL(可选) * @param {String} data.taskId - 语音识别任务ID(可选) * @param {String} data.dishes - 菜品清单(JSON字符串) * @param {Object} data.nutrition - 营养数据(可选) * @param {Boolean} data.enableAIVideo - 是否生成AI视频 * @param {Boolean} data.enableAIAnalysis - 是否开启AI识别 */ export function submitCheckin(data) { return request.post('tool/checkin/submit', data); } /** * 获取打卡记录列表 * @param {Object} data - 查询参数 * @param {String} data.date - 日期:YYYY-MM-DD(可选) * @param {String} data.mealType - 餐次(可选) * @param {Number} data.page - 页码 * @param {Number} data.limit - 每页数量 */ export function getCheckinList(data) { return request.get('tool/checkin/list', data); } /** * 获取打卡记录详情 * @param {Number} id - 打卡记录ID */ export function getCheckinDetail(id) { return request.get('tool/checkin/detail/' + id); } /** * 获取连续打卡统计 */ export function getCheckinStreak() { return request.get('tool/checkin/streak'); } /** * 获取打卡日历数据 * @param {String} yearMonth - 年月:YYYY-MM */ export function getCheckinCalendar(yearMonth) { return request.get('tool/checkin/calendar', { yearMonth }); } /** * 获取打卡任务列表 */ export function getCheckinTasks() { return request.get('tool/checkin/tasks'); } /** * 查询视频任务状态 * @param {String} taskId - 任务ID */ export function getVideoTaskStatus(taskId) { return request.get(`kieai/video/task/${taskId}`); } /** * 一键复制打卡 * @param {Number} sourceRecordId - 源打卡记录ID * @param {Object} data - 修改后的数据(可选) */ export function copyCheckin(sourceRecordId, data) { return request.post('tool/checkin/copy', { sourceRecordId, ...data }); } /** * 一键借鉴打卡 * @param {Number} sourcePostId - 源社区内容ID * @param {Object} data - 修改后的数据(可选) */ export function learnCheckin(sourcePostId, data) { return request.post('tool/checkin/learn', { sourcePostId, ...data }); } /** * 分享打卡记录到社区 * @param {Number} checkinId - 打卡记录ID */ export function shareCheckinToCommunity(checkinId) { return request.post('tool/checkin/' + checkinId + '/share-to-community'); } // ==================== 食物百科相关 ==================== /** * 搜索食物 * @param {Object} data - 搜索参数 * @param {String} data.keyword - 搜索关键词 * @param {String} data.category - 分类(可选) * @param {Number} data.page - 页码 * @param {Number} data.limit - 每页数量 */ export function searchFood(data) { return request.get('tool/food/search', data); } /** * 获取食物列表(按分类) * @param {Object} data - 查询参数 * @param {String} data.category - 分类:all/grain/vegetable/fruit/meat/seafood * @param {Number} data.page - 页码 * @param {Number} data.limit - 每页数量 */ export function getFoodList(data) { return request.get('tool/food/list', data); } /** * 获取食物详情(后端仅接受 Long 类型 id,传 name 会 400) * @param {Number|String} id - 食物ID(必须为数字,不能传名称) */ export function getFoodDetail(id) { const numId = typeof id === 'number' && !isNaN(id) ? id : parseInt(String(id), 10); // 打印请求参数便于确认(后端仅接受 Long 类型 id,传 name 会 400) const apiPath = 'tool/food/detail/' + numId; console.log('[api/tool] getFoodDetail 请求参数:', { id, numId, type: typeof id, apiPath }); if (isNaN(numId)) { return Promise.reject(new Error('食物详情接口需要数字ID,当前传入: ' + id)); } return request.get(apiPath); } /** * 获取相似食物推荐 * @param {String} foodId - 食物ID */ export function getSimilarFoods(foodId) { return request.get('tool/food/similar/' + foodId); } // ==================== 营养知识相关 ==================== /** * 获取营养知识列表 * @param {Object} data - 查询参数 * @param {String} data.type - 类型:nutrients/guide/article * @param {String} data.category - 分类(可选) * @param {Number} data.page - 页码 * @param {Number} data.limit - 每页数量 */ export function getKnowledgeList(data) { return request.get('tool/knowledge/list', data); } /** * 获取营养知识详情 * @param {Number} id - 知识ID */ export function getKnowledgeDetail(id) { return request.get('tool/knowledge/detail/' + id); } /** * 获取营养素详情 * @param {String} name - 营养素名称 */ export function getNutrientDetail(name) { return request.get('tool/knowledge/nutrient/' + name); } /** * AI 营养估算:根据饮食描述文本返回估算的热量、蛋白质、钾、磷(T06-T09) * @param {String} text - 饮食描述 * @returns {Promise<{data: {energyKcal?, proteinG?, potassiumMg?, phosphorusMg?}}>} */ export function getAiNutritionFill(text) { return request.post('tool/nutrition/fill-ai', { text: text || '' }); } // ==================== 打卡社区相关 ==================== /** * 获取社区内容列表 * @param {Object} data - 查询参数 * @param {String} data.tab - Tab类型:recommend/latest/follow/hot * @param {Number} data.page - 页码 * @param {Number} data.limit - 每页数量 */ export function getCommunityList(data) { return request.get('tool/community/list', data); } /** * 获取社区内容详情 * @param {Number} id - 内容ID */ export function getCommunityDetail(id) { return request.get('tool/community/detail/' + id); } /** * 发布社区内容 * @param {Object} data - 内容数据 * @param {String} data.title - 标题 * @param {String} data.content - 内容 * @param {Array} data.images - 图片URL数组 * @param {Array} data.tags - 标签数组 * @param {Number} data.checkInRecordId - 关联的打卡记录ID(可选) */ export function publishCommunityPost(data) { return request.post('tool/community/publish', data); } /** * 点赞/取消点赞 * @param {Number} postId - 内容ID(会被转为数字以匹配后端 Long) * @param {Boolean} isLike - 是否点赞:true/false */ export function toggleLike(postId, isLike) { const id = typeof postId === 'number' && !isNaN(postId) ? postId : parseInt(postId, 10); return request.post('tool/community/like', { postId: id, isLike: !!isLike }); } /** * 收藏/取消收藏 * @param {Number} postId - 内容ID(会被转为数字以匹配后端 Long) * @param {Boolean} isCollect - 是否收藏:true/false */ export function toggleCollect(postId, isCollect) { const id = typeof postId === 'number' && !isNaN(postId) ? postId : parseInt(postId, 10); return request.post('tool/community/collect', { postId: id, isCollect: !!isCollect }); } /** * 发表评论 * @param {Object} data - 评论数据 * @param {Number} data.postId - 内容ID * @param {String} data.content - 评论内容 * @param {Number} data.parentCommentId - 父评论ID(可选,用于回复) * @param {Number} data.replyToUserId - 回复的用户ID(可选) */ export function addComment(data) { return request.post('tool/community/comment', data); } /** * 获取评论列表 * @param {Number} postId - 内容ID * @param {Object} data - 查询参数 * @param {Number} data.page - 页码 * @param {Number} data.limit - 每页数量 */ export function getCommentList(postId, data) { return request.get('tool/community/comment/list/' + postId, data); } /** * 关注/取消关注用户 * @param {Number} userId - 用户ID * @param {Boolean} isFollow - 是否关注:true/false */ export function toggleFollow(userId, isFollow) { return request.post('tool/community/follow', { userId, isFollow }); } /** * 分享内容 * @param {Number} postId - 内容ID */ export function sharePost(postId) { return request.post('tool/community/share', { postId }); } /** * 填充帖子营养数据(服务端根据帖子内容/打卡补充营养并回写) * @param {Number|String} postId - 帖子ID * @returns {Promise<{data?: object}>} 返回更新后的帖子或营养数据 */ export function fillPostNutrition(postId) { const id = typeof postId === 'number' && !isNaN(postId) ? postId : parseInt(postId, 10); return request.post('tool/community/post/' + id + '/fill-nutrition'); } // ==================== 积分系统相关 ==================== /** * 获取用户积分信息 */ export function getUserPoints() { return request.get('tool/points/info'); } /** * 获取积分规则 */ export function getPointsRules() { return request.get('tool/points/rules'); } /** * 获取积分流水 * @param {Object} data - 查询参数 * @param {Number} data.page - 页码 * @param {Number} data.limit - 每页数量 * @param {String} data.type - 类型:earn/consume(可选) */ export function getPointsHistory(data) { return request.get('tool/points/history', data); } /** * 获取积分兑换列表 */ export function getPointsExchangeList() { return request.get('tool/points/exchange/list'); } /** * 积分兑换 * @param {Number} exchangeId - 兑换项ID */ export function exchangePoints(exchangeId) { return request.post('tool/points/exchange', { exchangeId }); } // ==================== 首页数据相关 ==================== /** * 获取首页数据 */ export function getHomeData() { return request.get('tool/home/data'); } /** * 获取推荐食谱列表(首页展示,无需登录即可浏览) * @param {Object} data - 查询参数 * @param {Number} data.limit - 数量限制 */ export function getRecommendedRecipes(data) { return request.get('tool/home/recipes', data, { noAuth: true }); } /** * 获取推荐营养知识(首页展示,无需登录即可浏览) * @param {Object} data - 查询参数 * @param {Number} data.limit - 数量限制 */ export function getRecommendedKnowledge(data) { return request.get('tool/home/knowledge', data, { noAuth: true }); } /** * 获取用户健康档案状态(首页展示,未登录时返回默认状态) */ export function getUserHealthStatus() { return request.get('tool/home/health-status', {}, { noAuth: true }); } /** * 获取首页展示配置(如四大功能入口是否显示,由系统配置 field01 控制:1=显示) */ export function getHomeDisplayConfig() { return request.get('tool/home/display-config', {}, { noAuth: true }); } // ==================== 食谱相关 ==================== /** * 获取食谱列表 * @param {Object} data - 查询参数 * @param {String} data.mealType - 餐次(可选) * @param {Number} data.page - 页码 * @param {Number} data.limit - 每页数量 */ export function getRecipeList(data) { return request.get('tool/recipe/list', data); } /** * 获取食谱详情 * @param {Number} id - 食谱ID */ export function getRecipeDetail(id) { return request.get('tool/recipe/detail/' + id); } /** * 收藏/取消收藏食谱 * @param {Number} recipeId - 食谱ID * @param {Boolean} isFavorite - 是否收藏:true/false */ export function toggleRecipeFavorite(recipeId, isFavorite) { return request.post('tool/recipe/favorite', { recipeId, isFavorite }); } // T09: 食谱营养 AI 回填 export function fillRecipeNutrition(recipeId) { return request.post("tool/recipe/" + recipeId + "/fill-nutrition") } // ==================== 文件上传相关 ==================== /** * 上传图片 * @param {String} filePath - 图片临时路径 * @param {String} type - 上传类型:checkin/community/avatar */ export function uploadImage(filePath, type = 'checkin') { return new Promise((resolve, reject) => { const token = store.state.app.token || ''; const header = {}; if (token) { header[TOKENNAME] = token; } uni.uploadFile({ url: HTTP_REQUEST_URL + '/api/front/tool/upload/image', filePath: filePath, name: 'file', formData: { type: type }, header: header, success: (res) => { try { const data = typeof res.data === 'string' ? JSON.parse(res.data) : res.data; if (data.code === 200) { resolve(data.data); } else { reject(data.message || '上传失败'); } } catch (e) { reject('解析响应失败'); } }, fail: (err) => { reject('上传失败:' + (err.errMsg || '网络错误')); } }); }); } /** * 上传语音 * @param {String} filePath - 语音临时路径 */ export function uploadVoice(filePath) { return new Promise((resolve, reject) => { const token = store.state.app.token || ''; const header = {}; if (token) { header[TOKENNAME] = token; } uni.uploadFile({ url: HTTP_REQUEST_URL + '/api/front/tool/upload/voice', filePath: filePath, name: 'file', header: header, success: (res) => { try { const data = typeof res.data === 'string' ? JSON.parse(res.data) : res.data; if (data.code === 200) { resolve(data.data); } else { reject(data.message || '上传失败'); } } catch (e) { reject('解析响应失败'); } }, fail: (err) => { reject('上传失败:' + (err.errMsg || '网络错误')); } }); }); }