|
@@ -1,22 +1,27 @@
|
|
|
package com.etotem.cfc.controller.growth;
|
|
package com.etotem.cfc.controller.growth;
|
|
|
|
|
|
|
|
import com.etotem.cfc.common.Result;
|
|
import com.etotem.cfc.common.Result;
|
|
|
|
|
+import com.etotem.cfc.dto.ParsedReport;
|
|
|
import com.etotem.cfc.entity.Family;
|
|
import com.etotem.cfc.entity.Family;
|
|
|
import com.etotem.cfc.entity.GrowthRecord;
|
|
import com.etotem.cfc.entity.GrowthRecord;
|
|
|
import com.etotem.cfc.service.AssessmentOrderService;
|
|
import com.etotem.cfc.service.AssessmentOrderService;
|
|
|
import com.etotem.cfc.service.GrowthRecordService;
|
|
import com.etotem.cfc.service.GrowthRecordService;
|
|
|
|
|
+import com.etotem.cfc.service.ReportParseService;
|
|
|
|
|
+import com.etotem.cfc.service.StorageService;
|
|
|
import com.etotem.cfc.mapper.FamilyMapper;
|
|
import com.etotem.cfc.mapper.FamilyMapper;
|
|
|
import io.swagger.v3.oas.annotations.Operation;
|
|
import io.swagger.v3.oas.annotations.Operation;
|
|
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
|
|
import org.springframework.web.bind.annotation.*;
|
|
import org.springframework.web.bind.annotation.*;
|
|
|
|
|
+import org.springframework.web.multipart.MultipartFile;
|
|
|
|
|
|
|
|
import javax.annotation.Resource;
|
|
import javax.annotation.Resource;
|
|
|
-
|
|
|
|
|
|
|
+import java.text.SimpleDateFormat;
|
|
|
|
|
+import java.util.Date;
|
|
|
import java.util.HashMap;
|
|
import java.util.HashMap;
|
|
|
import java.util.List;
|
|
import java.util.List;
|
|
|
import java.util.Map;
|
|
import java.util.Map;
|
|
|
|
|
|
|
|
-@Tag(name = "成长档案", description = "成长档案创建、查询、外部同步等接口")
|
|
|
|
|
|
|
+@Tag(name = "成长档案", description = "成长档案创建、查询、上传解析、补充记录等接口")
|
|
|
@RestController
|
|
@RestController
|
|
|
@RequestMapping("/api/growth")
|
|
@RequestMapping("/api/growth")
|
|
|
public class GrowthRecordController {
|
|
public class GrowthRecordController {
|
|
@@ -30,6 +35,14 @@ public class GrowthRecordController {
|
|
|
@Resource
|
|
@Resource
|
|
|
private FamilyMapper familyMapper;
|
|
private FamilyMapper familyMapper;
|
|
|
|
|
|
|
|
|
|
+ @Resource
|
|
|
|
|
+ private StorageService storageService;
|
|
|
|
|
+
|
|
|
|
|
+ @Resource
|
|
|
|
|
+ private ReportParseService reportParseService;
|
|
|
|
|
+
|
|
|
|
|
+ // ========== Original endpoints (enhanced) ==========
|
|
|
|
|
+
|
|
|
@Operation(summary = "创建成长档案")
|
|
@Operation(summary = "创建成长档案")
|
|
|
@PostMapping("/record/create")
|
|
@PostMapping("/record/create")
|
|
|
public Result<GrowthRecord> createRecord(@RequestBody GrowthRecord record,
|
|
public Result<GrowthRecord> createRecord(@RequestBody GrowthRecord record,
|
|
@@ -47,7 +60,6 @@ public class GrowthRecordController {
|
|
|
public Result<Map<String, Object>> createRecordWithOrder(
|
|
public Result<Map<String, Object>> createRecordWithOrder(
|
|
|
@RequestBody Map<String, Object> body,
|
|
@RequestBody Map<String, Object> body,
|
|
|
@RequestAttribute("userId") Long userId) {
|
|
@RequestAttribute("userId") Long userId) {
|
|
|
- // 解析成长档案字段
|
|
|
|
|
GrowthRecord record = new GrowthRecord();
|
|
GrowthRecord record = new GrowthRecord();
|
|
|
if (body.get("childId") != null) {
|
|
if (body.get("childId") != null) {
|
|
|
record.setChildId(Long.valueOf(body.get("childId").toString()));
|
|
record.setChildId(Long.valueOf(body.get("childId").toString()));
|
|
@@ -65,10 +77,8 @@ public class GrowthRecordController {
|
|
|
return Result.error("请选择孩子");
|
|
return Result.error("请选择孩子");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 创建成长档案
|
|
|
|
|
GrowthRecord growthResult = growthRecordService.createRecord(record);
|
|
GrowthRecord growthResult = growthRecordService.createRecord(record);
|
|
|
|
|
|
|
|
- // 检查是否同时购买测评
|
|
|
|
|
boolean alsoPurchase = body.get("alsoPurchaseAssessment") != null
|
|
boolean alsoPurchase = body.get("alsoPurchaseAssessment") != null
|
|
|
&& Boolean.parseBoolean(body.get("alsoPurchaseAssessment").toString());
|
|
&& Boolean.parseBoolean(body.get("alsoPurchaseAssessment").toString());
|
|
|
|
|
|
|
@@ -86,13 +96,11 @@ public class GrowthRecordController {
|
|
|
return Result.error("购买测评需要 familyId 和 packageId");
|
|
return Result.error("购买测评需要 familyId 和 packageId");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 自动分配规划师
|
|
|
|
|
Family family = familyMapper.selectById(familyId);
|
|
Family family = familyMapper.selectById(familyId);
|
|
|
Long guideId = (family != null && family.getTeacherId() != null)
|
|
Long guideId = (family != null && family.getTeacherId() != null)
|
|
|
? family.getTeacherId() : null;
|
|
? family.getTeacherId() : null;
|
|
|
String guideName = null;
|
|
String guideName = null;
|
|
|
|
|
|
|
|
- // 解析价格
|
|
|
|
|
Long totalPrice = body.get("totalPrice") != null
|
|
Long totalPrice = body.get("totalPrice") != null
|
|
|
? Long.valueOf(body.get("totalPrice").toString()) : 0L;
|
|
? Long.valueOf(body.get("totalPrice").toString()) : 0L;
|
|
|
Long discountAmount = body.get("discountAmount") != null
|
|
Long discountAmount = body.get("discountAmount") != null
|
|
@@ -164,4 +172,178 @@ public class GrowthRecordController {
|
|
|
}
|
|
}
|
|
|
return Result.success(record);
|
|
return Result.success(record);
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ // ========== New endpoints: upload, parse, supplement, dedup ==========
|
|
|
|
|
+
|
|
|
|
|
+ @Operation(summary = "上传测评报告PDF并解析(Stage 1)")
|
|
|
|
|
+ @PostMapping("/record/upload-and-parse")
|
|
|
|
|
+ public Result<Map<String, Object>> uploadAndParse(
|
|
|
|
|
+ @RequestParam("file") MultipartFile file) {
|
|
|
|
|
+ if (file.isEmpty()) {
|
|
|
|
|
+ return Result.error("请选择文件");
|
|
|
|
|
+ }
|
|
|
|
|
+ String sourceFileUrl;
|
|
|
|
|
+ try {
|
|
|
|
|
+ sourceFileUrl = storageService.storeFile(file);
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ return Result.error("文件存储失败: " + e.getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ String localPath = storageService.getFilePath(sourceFileUrl);
|
|
|
|
|
+ ParsedReport parsed = reportParseService.parseReport(localPath);
|
|
|
|
|
+
|
|
|
|
|
+ String reportDateStr = null;
|
|
|
|
|
+ if (parsed.getReportDate() != null) {
|
|
|
|
|
+ reportDateStr = new SimpleDateFormat("yyyy-MM-dd").format(parsed.getReportDate());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ Map<String, Object> result = new HashMap<>();
|
|
|
|
|
+ result.put("sourceFileUrl", sourceFileUrl);
|
|
|
|
|
+ result.put("parsed", parsed);
|
|
|
|
|
+ result.put("reportDateStr", reportDateStr);
|
|
|
|
|
+ result.put("hasDate", parsed.getReportDate() != null);
|
|
|
|
|
+ return Result.success(result);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Operation(summary = "上传解析后创建成长档案(Stage 2)")
|
|
|
|
|
+ @PostMapping("/record/create-from-upload")
|
|
|
|
|
+ public Result<GrowthRecord> createFromUpload(@RequestBody Map<String, Object> body,
|
|
|
|
|
+ @RequestAttribute("userId") Long userId) {
|
|
|
|
|
+ Long childId = body.get("childId") != null ? Long.valueOf(body.get("childId").toString()) : null;
|
|
|
|
|
+ if (childId == null) {
|
|
|
|
|
+ return Result.error("请选择孩子");
|
|
|
|
|
+ }
|
|
|
|
|
+ String sourceFileUrl = body.get("sourceFileUrl") != null ? body.get("sourceFileUrl").toString() : null;
|
|
|
|
|
+ if (sourceFileUrl == null || sourceFileUrl.isEmpty()) {
|
|
|
|
|
+ return Result.error("缺少源文件URL");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ ParsedReport parsed = buildParsedReportFromMap(body);
|
|
|
|
|
+
|
|
|
|
|
+ // Manual report date override (user-entered if PDF parsing failed)
|
|
|
|
|
+ Date manualReportDate = null;
|
|
|
|
|
+ if (body.get("manualReportDate") != null) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ manualReportDate = new SimpleDateFormat("yyyy-MM-dd")
|
|
|
|
|
+ .parse(body.get("manualReportDate").toString());
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ return Result.error("日期格式错误,请使用 yyyy-MM-dd 格式");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ GrowthRecord record = growthRecordService.createFromUpload(
|
|
|
|
|
+ childId, userId, parsed, sourceFileUrl, manualReportDate);
|
|
|
|
|
+ return Result.success(record);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Operation(summary = "创建补充记录(补充材料)")
|
|
|
|
|
+ @PostMapping("/record/supplement")
|
|
|
|
|
+ public Result<GrowthRecord> createSupplement(@RequestBody Map<String, Object> body,
|
|
|
|
|
+ @RequestAttribute("userId") Long userId) {
|
|
|
|
|
+ Long parentRecordId = body.get("parentRecordId") != null
|
|
|
|
|
+ ? Long.valueOf(body.get("parentRecordId").toString()) : null;
|
|
|
|
|
+ Long childId = body.get("childId") != null ? Long.valueOf(body.get("childId").toString()) : null;
|
|
|
|
|
+ if (parentRecordId == null || childId == null) {
|
|
|
|
|
+ return Result.error("缺少必要参数: parentRecordId, childId");
|
|
|
|
|
+ }
|
|
|
|
|
+ String sourceFileUrl = body.get("sourceFileUrl") != null ? body.get("sourceFileUrl").toString() : null;
|
|
|
|
|
+
|
|
|
|
|
+ ParsedReport parsed = buildParsedReportFromMap(body);
|
|
|
|
|
+
|
|
|
|
|
+ Date reportDate = null;
|
|
|
|
|
+ if (body.get("reportDate") != null) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ reportDate = new SimpleDateFormat("yyyy-MM-dd")
|
|
|
|
|
+ .parse(body.get("reportDate").toString());
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ return Result.error("日期格式错误,请使用 yyyy-MM-dd 格式");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ GrowthRecord record = growthRecordService.createSupplement(
|
|
|
|
|
+ parentRecordId, childId, userId, parsed, sourceFileUrl, reportDate);
|
|
|
|
|
+ return Result.success(record);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Operation(summary = "检查是否已存在相同哈希的记录(去重)")
|
|
|
|
|
+ @PostMapping("/record/check-duplicate")
|
|
|
|
|
+ public Result<Map<String, Object>> checkDuplicate(@RequestBody Map<String, Object> body) {
|
|
|
|
|
+ Long childId = body.get("childId") != null ? Long.valueOf(body.get("childId").toString()) : null;
|
|
|
|
|
+ String sourceType = body.get("sourceType") != null ? body.get("sourceType").toString() : "self_upload";
|
|
|
|
|
+ if (childId == null) {
|
|
|
|
|
+ return Result.error("缺少 childId");
|
|
|
|
|
+ }
|
|
|
|
|
+ Date reportDate = null;
|
|
|
|
|
+ if (body.get("reportDate") != null) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ reportDate = new SimpleDateFormat("yyyy-MM-dd")
|
|
|
|
|
+ .parse(body.get("reportDate").toString());
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ return Result.error("日期格式错误");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ GrowthRecord existing = growthRecordService.checkDuplicate(childId, reportDate, sourceType);
|
|
|
|
|
+ Map<String, Object> result = new HashMap<>();
|
|
|
|
|
+ result.put("duplicate", existing != null);
|
|
|
|
|
+ if (existing != null) {
|
|
|
|
|
+ result.put("existingRecord", existing);
|
|
|
|
|
+ }
|
|
|
|
|
+ return Result.success(result);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Operation(summary = "获取某条记录的所有补充记录")
|
|
|
|
|
+ @PostMapping("/record/{id}/supplements")
|
|
|
|
|
+ public Result<List<GrowthRecord>> getSupplements(@PathVariable Long id) {
|
|
|
|
|
+ List<GrowthRecord> supplements = growthRecordService.getSupplements(id);
|
|
|
|
|
+ return Result.success(supplements);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // ========== Private helpers ==========
|
|
|
|
|
+
|
|
|
|
|
+ private ParsedReport buildParsedReportFromMap(Map<String, Object> body) {
|
|
|
|
|
+ ParsedReport.ParsedReportBuilder builder = ParsedReport.builder();
|
|
|
|
|
+
|
|
|
|
|
+ if (body.get("danLevel") != null) {
|
|
|
|
|
+ builder.danLevel(body.get("danLevel").toString());
|
|
|
|
|
+ }
|
|
|
|
|
+ if (body.get("overallScore") != null) {
|
|
|
|
|
+ builder.overallScore(Integer.valueOf(body.get("overallScore").toString()));
|
|
|
|
|
+ }
|
|
|
|
|
+ if (body.get("attentionScore") != null) {
|
|
|
|
|
+ builder.attentionScore(Integer.valueOf(body.get("attentionScore").toString()));
|
|
|
|
|
+ }
|
|
|
|
|
+ if (body.get("focusScore") != null) {
|
|
|
|
|
+ builder.focusScore(Integer.valueOf(body.get("focusScore").toString()));
|
|
|
|
|
+ }
|
|
|
|
|
+ if (body.get("memoryScore") != null) {
|
|
|
|
|
+ builder.memoryScore(Integer.valueOf(body.get("memoryScore").toString()));
|
|
|
|
|
+ }
|
|
|
|
|
+ if (body.get("logicScore") != null) {
|
|
|
|
|
+ builder.logicScore(Integer.valueOf(body.get("logicScore").toString()));
|
|
|
|
|
+ }
|
|
|
|
|
+ if (body.get("perceptionScore") != null) {
|
|
|
|
|
+ builder.perceptionScore(Integer.valueOf(body.get("perceptionScore").toString()));
|
|
|
|
|
+ }
|
|
|
|
|
+ if (body.get("spatialScore") != null) {
|
|
|
|
|
+ builder.spatialScore(Integer.valueOf(body.get("spatialScore").toString()));
|
|
|
|
|
+ }
|
|
|
|
|
+ if (body.get("processingSpeedScore") != null) {
|
|
|
|
|
+ builder.processingSpeedScore(Integer.valueOf(body.get("processingSpeedScore").toString()));
|
|
|
|
|
+ }
|
|
|
|
|
+ if (body.get("emotionScore") != null) {
|
|
|
|
|
+ builder.emotionScore(Integer.valueOf(body.get("emotionScore").toString()));
|
|
|
|
|
+ }
|
|
|
|
|
+ if (body.get("resilienceScore") != null) {
|
|
|
|
|
+ builder.resilienceScore(Integer.valueOf(body.get("resilienceScore").toString()));
|
|
|
|
|
+ }
|
|
|
|
|
+ if (body.get("assessmentSummary") != null) {
|
|
|
|
|
+ builder.assessmentSummary(body.get("assessmentSummary").toString());
|
|
|
|
|
+ }
|
|
|
|
|
+ if (body.get("growthSuggestions") != null) {
|
|
|
|
|
+ builder.growthSuggestions(body.get("growthSuggestions").toString());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return builder.build();
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|