|
@@ -38,6 +38,7 @@ import com.etotem.cfc.service.ReportBlockAssembler;
|
|
|
import com.etotem.cfc.service.ReportBlockService;
|
|
import com.etotem.cfc.service.ReportBlockService;
|
|
|
import com.etotem.cfc.service.StorageService;
|
|
import com.etotem.cfc.service.StorageService;
|
|
|
import com.etotem.cfc.service.TongueDiagnosisService;
|
|
import com.etotem.cfc.service.TongueDiagnosisService;
|
|
|
|
|
+import com.etotem.cfc.service.BodyFatScaleService;
|
|
|
import com.etotem.cfc.service.api.WechatServiceInterface;
|
|
import com.etotem.cfc.service.api.WechatServiceInterface;
|
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
@@ -547,6 +548,40 @@ public class HealthReportController {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 体脂秤图片上传 + AI 识别(一步完成:上传→识别→存库)
|
|
|
|
|
+ */
|
|
|
|
|
+ @Operation(summary = "体脂秤图片上传并自动识别指标")
|
|
|
|
|
+ @PostMapping("/report/bodyfat-upload")
|
|
|
|
|
+ public Result<Map<String, Object>> uploadBodyFat(
|
|
|
|
|
+ @RequestParam("file") MultipartFile file,
|
|
|
|
|
+ @RequestAttribute("userId") Long userId,
|
|
|
|
|
+ @RequestAttribute(value = "currentMemberId", required = false) Long currentMemberId) {
|
|
|
|
|
+ if (file.isEmpty()) {
|
|
|
|
|
+ return Result.error("文件不能为空");
|
|
|
|
|
+ }
|
|
|
|
|
+ String contentType = file.getContentType();
|
|
|
|
|
+ if (contentType == null || !contentType.startsWith("image/")) {
|
|
|
|
|
+ return Result.error("仅支持图片文件(JPG/PNG)");
|
|
|
|
|
+ }
|
|
|
|
|
+ try {
|
|
|
|
|
+ byte[] imageBytes = file.getBytes();
|
|
|
|
|
+ if (!wechatService.checkImage(imageBytes)) {
|
|
|
|
|
+ return Result.error("图片内容包含敏感信息,上传失败");
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ return Result.error("图片读取失败: " + e.getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+ String fileUrl = storageService.storeFile(file, "health-reports");
|
|
|
|
|
+ Map<String, Object> analysisResult = bodyFatScaleService.analyzeImage(fileUrl);
|
|
|
|
|
+ List<Map<String, Object>> metrics = (List<Map<String, Object>>) analysisResult.get("metrics");
|
|
|
|
|
+ if (metrics == null) metrics = Collections.emptyList();
|
|
|
|
|
+ Map<String, Object> result = new LinkedHashMap<>();
|
|
|
|
|
+ result.put("metrics", metrics);
|
|
|
|
|
+ result.put("fileUrl", fileUrl);
|
|
|
|
|
+ return Result.success(result);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
* 两阶段入库 Phase 1: 按 draftId 触发解析。读取已保存的文件,
|
|
* 两阶段入库 Phase 1: 按 draftId 触发解析。读取已保存的文件,
|
|
|
* 执行 LangGraph / 本地 Java 解析,更新草稿载荷后返回完整解析结果。
|
|
* 执行 LangGraph / 本地 Java 解析,更新草稿载荷后返回完整解析结果。
|