diff --git a/msh_crmeb_22/crmeb-service/src/main/java/com/zbkj/service/service/impl/tool/ToolCommunityServiceImpl.java b/msh_crmeb_22/crmeb-service/src/main/java/com/zbkj/service/service/impl/tool/ToolCommunityServiceImpl.java index 81095de..2e91d2b 100644 --- a/msh_crmeb_22/crmeb-service/src/main/java/com/zbkj/service/service/impl/tool/ToolCommunityServiceImpl.java +++ b/msh_crmeb_22/crmeb-service/src/main/java/com/zbkj/service/service/impl/tool/ToolCommunityServiceImpl.java @@ -32,6 +32,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; import javax.annotation.Resource; import java.util.*; @@ -231,6 +232,28 @@ public class ToolCommunityServiceImpl implements ToolCommunityService { post.setViewCount(post.getViewCount() + 1); v2CommunityPostDao.updateById(post); + + // 如果营养数据为空,使用 AI 自动填充 + if (StrUtil.isBlank(post.getNutritionDataJson())) { + try { + log.info("帖子 {} 营养数据为空,调用 AI 自动填充", id); + Map nutritionData = fillNutritionByAi(post); + if (nutritionData != null && !nutritionData.isEmpty()) { + // 存库使用嵌套格式(value/unit) + @SuppressWarnings("unchecked") + Map nestedData = (Map) nutritionData.get("nested"); + if (nestedData != null && !nestedData.isEmpty()) { + post.setNutritionDataJson(JSON.toJSONString(nestedData)); + post.setUpdatedAt(new Date()); + v2CommunityPostDao.updateById(post); + } + } + } catch (Exception e) { + log.warn("帖子 {} AI 营养填充失败: {}", id, e.getMessage()); + // 不影响详情返回,只是记录日志 + } + } + Map map = new HashMap<>(); map.put("id", post.getPostId()); map.put("postId", post.getPostId()); @@ -614,15 +637,35 @@ public class ToolCommunityServiceImpl implements ToolCommunityService { if (userId.longValue() != post.getUserId()) throw new CrmebException("只能为自己的帖子填充营养数据"); StringBuilder text = new StringBuilder(); - if (StrUtil.isNotBlank(post.getTitle())) text.append(post.getTitle()).append(" "); + if (StrUtil.isNotBlank(post.getTitle())) text.append(post.getTitle()).append("\n"); if (StrUtil.isNotBlank(post.getContent())) text.append(post.getContent()); if (text.length() == 0) throw new CrmebException("帖子标题和内容为空,无法估算营养"); + // 获取第一张图片 URL + String firstImageUrl = null; + if (StrUtil.isNotBlank(post.getImagesJson())) { + try { + JSONArray images = JSON.parseArray(post.getImagesJson()); + if (images != null && !images.isEmpty()) { + firstImageUrl = images.getString(0); + } + } catch (Exception e) { + log.warn("帖子 {} 图片 JSON 解析失败: {}", postId, e.getMessage()); + } + } + // 如果没有图片数组,尝试使用封面图 + if (StrUtil.isBlank(firstImageUrl) && StrUtil.isNotBlank(post.getCoverImage())) { + firstImageUrl = post.getCoverImage(); + } + // 使用 Coze 工作流进行营养成分分析 CozeWorkflowRequest workflowRequest = new CozeWorkflowRequest(); workflowRequest.setWorkflowId("7613879935811354687"); Map params = new HashMap<>(); - params.put("text", text.toString()); + params.put("input", text.toString()); + if (StrUtil.isNotBlank(firstImageUrl)) { + params.put("img", firstImageUrl); + } workflowRequest.setParameters(params); CozeBaseResponse response = toolCozeService.workflow(workflowRequest); @@ -714,4 +757,95 @@ public class ToolCommunityServiceImpl implements ToolCommunityService { } } } + + /** + * AI 自动填充帖子营养数据 + * 调用 Coze 工作流 7613879935811354687,使用帖子图片和内容进行分析 + * + * @param post 帖子实体 + * @return 包含 flat 和 nested 格式的营养数据 Map + */ + private Map fillNutritionByAi(V2CommunityPost post) { + if (post == null) { + return null; + } + + // 获取帖子内容 + StringBuilder text = new StringBuilder(); + if (StrUtil.isNotBlank(post.getTitle())) text.append(post.getTitle()).append("\n"); + if (StrUtil.isNotBlank(post.getContent())) text.append(post.getContent()); + + String content = text.toString().trim(); + if (StrUtil.isBlank(content)) { + log.warn("帖子 {} 标题和内容为空,无法估算营养", post.getPostId()); + return null; + } + + // 获取第一张图片 URL + String firstImageUrl = null; + if (StrUtil.isNotBlank(post.getImagesJson())) { + try { + JSONArray images = JSON.parseArray(post.getImagesJson()); + if (images != null && !images.isEmpty()) { + firstImageUrl = images.getString(0); + } + } catch (Exception e) { + log.warn("帖子 {} 图片 JSON 解析失败: {}", post.getPostId(), e.getMessage()); + } + } + + // 如果没有图片数组,尝试使用封面图 + if (StrUtil.isBlank(firstImageUrl) && StrUtil.isNotBlank(post.getCoverImage())) { + firstImageUrl = post.getCoverImage(); + } + + log.info("帖子 {} AI 营养填充,图片: {}, 内容长度: {}", + post.getPostId(), + firstImageUrl != null ? "有" : "无", + content.length() + ); + + // 调用 Coze 工作流进行营养成分分析 + CozeWorkflowRequest workflowRequest = new CozeWorkflowRequest(); + workflowRequest.setWorkflowId("7613879935811354687"); + Map params = new HashMap<>(); + params.put("input", content); + if (StrUtil.isNotBlank(firstImageUrl)) { + params.put("img", firstImageUrl); + } + workflowRequest.setParameters(params); + + CozeBaseResponse response = toolCozeService.workflow(workflowRequest); + if (response == null || response.getData() == null) { + log.warn("帖子 {} Coze 工作流调用失败", post.getPostId()); + return null; + } + + // 解析工作流返回结果 + Map workflowResult = parseWorkflowResponse(response.getData()); + if (workflowResult == null || workflowResult.isEmpty()) { + log.warn("帖子 {} AI 未能估算出营养数据", post.getPostId()); + return null; + } + + // 提取 output 中的营养数据(嵌套格式) + @SuppressWarnings("unchecked") + Map output = (Map) workflowResult.get("output"); + if (output == null || output.isEmpty()) { + log.warn("帖子 {} AI 返回数据格式错误", post.getPostId()); + return null; + } + + // 将嵌套格式转为扁平格式 + Map flatNutrition = nestedToFlat(output); + + // 返回两种格式的数据 + Map result = new HashMap<>(); + result.put("flat", flatNutrition); + result.put("nested", output); + + log.info("帖子 {} AI 营养填充成功", post.getPostId()); + return result; + } } +