DanAssessmentController.java 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. package com.etotem.cfc.controller;
  2. import com.etotem.cfc.common.Result;
  3. import com.etotem.cfc.entity.*;
  4. import com.etotem.cfc.service.*;
  5. import com.etotem.cfc.service.DanReportParseService.DanParsedReport;
  6. import io.swagger.v3.oas.annotations.Operation;
  7. import io.swagger.v3.oas.annotations.tags.Tag;
  8. import org.springframework.web.bind.annotation.*;
  9. import org.springframework.web.multipart.MultipartFile;
  10. import javax.annotation.Resource;
  11. import java.util.LinkedHashMap;
  12. import java.util.List;
  13. import java.util.Map;
  14. @Tag(name = "DAN测评", description = "DAN专注力测评相关接口")
  15. @RestController
  16. @RequestMapping("/api/dan-assessment")
  17. public class DanAssessmentController {
  18. @Resource
  19. private AssessmentService assessmentService;
  20. @Resource
  21. private AssessmentAppointmentService appointmentService;
  22. @Resource
  23. private AssessmentOrderService orderService;
  24. @Resource
  25. private AssessmentPermissionService permissionService;
  26. @Resource
  27. private com.etotem.cfc.mapper.DanAssessmentResultMapper danAssessmentResultMapper;
  28. @Resource
  29. private com.etotem.cfc.mapper.UserMapper userMapper;
  30. @Resource
  31. private DanReportParseService danReportParseService;
  32. @Resource
  33. private DimensionScoreService dimensionScoreService;
  34. @Resource
  35. private DanReportUploadService danReportUploadService;
  36. // ===== 测评材料 =====
  37. @Operation(summary = "获取当前测评材料")
  38. @PostMapping("/material/active")
  39. public Result<AssessmentMaterial> getActiveMaterial() {
  40. AssessmentMaterial material = assessmentService.getActiveMaterial();
  41. return Result.success(material);
  42. }
  43. @Operation(summary = "获取测评材料列表")
  44. @PostMapping("/material/list")
  45. public Result<List<AssessmentMaterial>> getMaterialList() {
  46. return Result.success(assessmentService.getMaterialList());
  47. }
  48. // ===== 积分配置 =====
  49. @Operation(summary = "获取积分配置")
  50. @PostMapping("/points-config")
  51. public Result<AssessmentPointsConfig> getPointsConfig() {
  52. return Result.success(assessmentService.getPointsConfig());
  53. }
  54. // ===== 家庭配置 =====
  55. @Operation(summary = "获取家庭测评配置")
  56. @PostMapping("/family/config")
  57. public Result<FamilyAssessmentConfig> getFamilyConfig(@RequestParam Long familyId) {
  58. return Result.success(assessmentService.getFamilyConfig(familyId));
  59. }
  60. @Operation(summary = "启用家庭测评")
  61. @PostMapping("/family/enable")
  62. public Result<String> enableFamilyAssessment(@RequestBody Map<String, Object> body) {
  63. Long familyId = Long.valueOf(body.get("familyId").toString());
  64. Long materialId = body.get("materialId") != null
  65. ? Long.valueOf(body.get("materialId").toString()) : null;
  66. Integer remindMonths = body.get("remindMonths") != null
  67. ? Integer.valueOf(body.get("remindMonths").toString()) : 6;
  68. Long createdBy = body.get("createdBy") != null
  69. ? Long.valueOf(body.get("createdBy").toString()) : null;
  70. boolean ok = assessmentService.enableFamilyAssessment(familyId, materialId, remindMonths, createdBy);
  71. return ok ? Result.success("启用成功") : Result.error("启用失败");
  72. }
  73. @Operation(summary = "禁用家庭测评")
  74. @PostMapping("/family/disable")
  75. public Result<String> disableFamilyAssessment(@RequestBody Map<String, Object> body) {
  76. Long familyId = Long.valueOf(body.get("familyId").toString());
  77. boolean ok = assessmentService.disableFamilyAssessment(familyId);
  78. return ok ? Result.success("禁用成功") : Result.error("禁用失败");
  79. }
  80. // ===== 测评记录 =====
  81. @Operation(summary = "创建测评记录")
  82. @PostMapping("/record/create")
  83. public Result<AssessmentRecord> createRecord(@RequestBody Map<String, Object> body) {
  84. Long familyId = Long.valueOf(body.get("familyId").toString());
  85. Long childId = Long.valueOf(body.get("childId").toString());
  86. Long materialId = body.get("materialId") != null
  87. ? Long.valueOf(body.get("materialId").toString()) : null;
  88. return Result.success(assessmentService.createRecord(familyId, childId, materialId));
  89. }
  90. @Operation(summary = "完成测评记录")
  91. @PostMapping("/record/complete")
  92. public Result<String> completeRecord(@RequestBody Map<String, Object> body) {
  93. Long recordId = Long.valueOf(body.get("recordId").toString());
  94. boolean ok = assessmentService.completeRecord(recordId);
  95. return ok ? Result.success("完成成功") : Result.error("记录不存在");
  96. }
  97. @Operation(summary = "获取家庭测评记录")
  98. @PostMapping("/record/family")
  99. public Result<List<AssessmentRecord>> getFamilyRecords(@RequestParam Long familyId) {
  100. return Result.success(assessmentService.getFamilyRecords(familyId));
  101. }
  102. @Operation(summary = "获取当前测评")
  103. @PostMapping("/record/current")
  104. public Result<AssessmentRecord> getCurrentAssessment(@RequestParam Long familyId) {
  105. return Result.success(assessmentService.getCurrentAssessment(familyId));
  106. }
  107. // ===== 测评结果 =====
  108. @Operation(summary = "记录/提交测评结果")
  109. @PostMapping("/result/record")
  110. public Result<DanAssessmentResult> recordResult(@RequestBody DanAssessmentResult result) {
  111. return Result.success(assessmentService.recordResult(result));
  112. }
  113. @Operation(summary = "获取我的测评报告列表")
  114. @PostMapping("/my-results")
  115. public Result<List<DanAssessmentResult>> getMyResults(@RequestBody Map<String, Object> body) {
  116. Long userId = body.get("userId") != null ? Long.valueOf(body.get("userId").toString()) : null;
  117. if (userId == null) {
  118. return Result.success(assessmentService.getAccessibleResults(0L, "teacher"));
  119. }
  120. // 这里简化为返回该用户相关的报告,实际可根据角色判断
  121. return Result.success(assessmentService.getAccessibleResults(userId, "teacher"));
  122. }
  123. @Operation(summary = "获取下级成长规划师的测评报告")
  124. @PostMapping("/child-results")
  125. public Result<List<DanAssessmentResult>> getChildResults(@RequestBody Map<String, Object> body) {
  126. Long userId = body.get("userId") != null ? Long.valueOf(body.get("userId").toString()) : null;
  127. List<DanAssessmentResult> results = assessmentService.getAccessibleResults(
  128. userId != null ? userId : 0L, "teacher");
  129. return Result.success(results);
  130. }
  131. @Operation(summary = "获取测评报告详情")
  132. @PostMapping("/result/{id}")
  133. public Result<DanAssessmentResult> getResult(@PathVariable Long id) {
  134. return Result.success(assessmentService.getByAppointmentId(id));
  135. }
  136. // ===== 预约 =====
  137. @Operation(summary = "创建测评预约")
  138. @PostMapping("/appointment/create")
  139. public Result<AssessmentAppointment> createAppointment(@RequestBody Map<String, Object> body) {
  140. Long userId = body.get("userId") != null ? Long.valueOf(body.get("userId").toString()) : null;
  141. Long familyId = body.get("familyId") != null ? Long.valueOf(body.get("familyId").toString()) : null;
  142. Long childId = Long.valueOf(body.get("childId").toString());
  143. // 兼容前端 teacherId / guideId 字段,允许为 null(系统安排)
  144. Long guideId = null;
  145. if (body.get("guideId") != null) {
  146. guideId = Long.valueOf(body.get("guideId").toString());
  147. } else if (body.get("teacherId") != null) {
  148. guideId = Long.valueOf(body.get("teacherId").toString());
  149. }
  150. Long packageId = body.get("packageId") != null ? Long.valueOf(body.get("packageId").toString()) : null;
  151. String appointmentDate = (String) body.get("appointmentDate");
  152. String appointmentTime = (String) body.get("appointmentTime");
  153. String appointmentType = body.get("appointmentType") != null ? body.get("appointmentType").toString() : null;
  154. Integer durationMinutes = body.get("durationMinutes") != null
  155. ? Integer.valueOf(body.get("durationMinutes").toString()) : null;
  156. String notes = (String) body.get("notes");
  157. AssessmentAppointment appointment = appointmentService.createAppointment(
  158. userId, familyId, childId, guideId, packageId,
  159. appointmentDate, appointmentTime, appointmentType, durationMinutes, notes);
  160. return Result.success(appointment);
  161. }
  162. @Operation(summary = "确认预约")
  163. @PostMapping("/appointment/confirm")
  164. public Result<String> confirmAppointment(@RequestBody Map<String, Object> body) {
  165. Long id = Long.valueOf(body.get("id").toString());
  166. Long teacherId = Long.valueOf(body.get("teacherId").toString());
  167. boolean ok = appointmentService.confirmAppointment(id, teacherId);
  168. return ok ? Result.success("确认成功") : Result.error("确认失败");
  169. }
  170. @Operation(summary = "完成预约")
  171. @PostMapping("/appointment/complete")
  172. public Result<String> completeAppointment(@RequestBody Map<String, Object> body) {
  173. Long id = Long.valueOf(body.get("id").toString());
  174. Long teacherId = Long.valueOf(body.get("teacherId").toString());
  175. boolean ok = appointmentService.completeAppointment(id, teacherId);
  176. return ok ? Result.success("完成成功") : Result.error("完成失败");
  177. }
  178. @Operation(summary = "取消预约")
  179. @PostMapping("/appointment/cancel")
  180. public Result<String> cancelAppointment(@RequestBody Map<String, Object> body) {
  181. Long id = Long.valueOf(body.get("id").toString());
  182. boolean ok = appointmentService.cancelAppointment(id);
  183. return ok ? Result.success("取消成功") : Result.error("取消失败");
  184. }
  185. @Operation(summary = "获取用户的预约列表")
  186. @PostMapping("/appointment/user")
  187. public Result<List<Map<String, Object>>> getUserAppointments(@RequestBody Map<String, Object> body) {
  188. Long userId = body.get("userId") != null ? Long.valueOf(body.get("userId").toString()) : null;
  189. if (userId == null) return Result.error("userId 不能为空");
  190. List<AssessmentAppointment> list = appointmentService.getByUserId(userId);
  191. List<Map<String, Object>> result = new java.util.ArrayList<>();
  192. for (AssessmentAppointment app : list) {
  193. Map<String, Object> item = new java.util.LinkedHashMap<>();
  194. item.put("id", app.getId());
  195. item.put("userId", app.getUserId());
  196. item.put("familyId", app.getFamilyId());
  197. item.put("childId", app.getChildId());
  198. item.put("guideId", app.getGuideId());
  199. item.put("packageId", app.getPackageId());
  200. item.put("appointmentDate", app.getAppointmentDate());
  201. item.put("appointmentTime", app.getAppointmentTime());
  202. item.put("durationMinutes", app.getDurationMinutes());
  203. item.put("status", app.getStatus());
  204. item.put("appointmentType", app.getAppointmentType());
  205. item.put("notes", app.getNotes());
  206. item.put("contactPhone", app.getContactPhone());
  207. item.put("meetLink", app.getMeetLink());
  208. item.put("createdAt", app.getCreatedAt());
  209. // 关联查询规划师名称
  210. if (app.getGuideId() != null) {
  211. com.etotem.cfc.entity.User guide = userMapper.selectById(app.getGuideId());
  212. item.put("guideName", guide != null ? guide.getNickname() : "");
  213. } else {
  214. item.put("guideName", "");
  215. }
  216. result.add(item);
  217. }
  218. return Result.success(result);
  219. }
  220. @Operation(summary = "获取规划师的预约列表")
  221. @PostMapping("/appointment/guide")
  222. public Result<List<AssessmentAppointment>> getGuideAppointments(@RequestParam Long guideId) {
  223. return Result.success(appointmentService.getByGuideId(guideId));
  224. }
  225. // ===== 订单 =====
  226. @Operation(summary = "创建测评订单(含预约)")
  227. @PostMapping("/order/create")
  228. public Result<AssessmentOrder> createOrder(@RequestBody Map<String, Object> body) {
  229. Long familyId = body.get("familyId") != null ? Long.valueOf(body.get("familyId").toString()) : null;
  230. Long userId = body.get("userId") != null ? Long.valueOf(body.get("userId").toString()) : null;
  231. Long childId = Long.valueOf(body.get("childId").toString());
  232. Long guideId = Long.valueOf(body.get("guideId").toString());
  233. Long packageId = body.get("packageId") != null ? Long.valueOf(body.get("packageId").toString()) : null;
  234. String guideName = (String) body.get("guideName");
  235. String packageName = (String) body.get("packageName");
  236. Long totalPrice = body.get("totalPrice") != null ? Long.valueOf(body.get("totalPrice").toString()) : 0L;
  237. Long discountAmount = body.get("discountAmount") != null
  238. ? Long.valueOf(body.get("discountAmount").toString()) : 0L;
  239. AssessmentOrder order = orderService.createOrder(familyId, userId, childId,
  240. guideId, packageId, guideName, packageName, totalPrice, discountAmount);
  241. return Result.success(order);
  242. }
  243. @Operation(summary = "支付测评订单")
  244. @PostMapping("/order/pay")
  245. public Result<String> payOrder(@RequestBody Map<String, Object> body) {
  246. String orderNo = (String) body.get("orderNo");
  247. String payType = (String) body.get("payType");
  248. boolean ok = orderService.payOrder(orderNo, payType);
  249. return ok ? Result.success("支付成功") : Result.error("支付失败");
  250. }
  251. @Operation(summary = "取消订单")
  252. @PostMapping("/order/cancel")
  253. public Result<String> cancelOrder(@RequestBody Map<String, Object> body) {
  254. String orderNo = (String) body.get("orderNo");
  255. boolean ok = orderService.cancelOrder(orderNo);
  256. return ok ? Result.success("取消成功") : Result.error("取消失败");
  257. }
  258. @Operation(summary = "获取订单详情")
  259. @PostMapping("/order/detail")
  260. public Result<AssessmentOrder> getOrderDetail(@RequestParam String orderNo) {
  261. return Result.success(orderService.getByOrderNo(orderNo));
  262. }
  263. @Operation(summary = "获取家庭订单列表")
  264. @PostMapping("/order/family")
  265. public Result<List<AssessmentOrder>> getFamilyOrders(@RequestParam Long familyId) {
  266. return Result.success(orderService.getFamilyOrders(familyId));
  267. }
  268. // ===== 结构化报告 =====
  269. @Operation(summary = "获取测评结果结构化分析")
  270. @PostMapping("/results/{id}/structured")
  271. public Result<Map<String, Object>> getStructuredResult(@PathVariable Long id) {
  272. DanAssessmentResult result = danAssessmentResultMapper.selectById(id);
  273. if (result == null) {
  274. return Result.error("测评结果不存在");
  275. }
  276. Map<String, Object> data = new java.util.LinkedHashMap<>();
  277. data.put("resultId", result.getId());
  278. data.put("childName", result.getChildName());
  279. data.put("assessmentDate", result.getAssessmentDate());
  280. data.put("structuredAnalysis", result.getStructuredAnalysis());
  281. return Result.success(data);
  282. }
  283. // ===== DAN 报告上传与解析 =====
  284. @Operation(summary = "上传 DAN 报告并预览解析结果(Phase 1)— 已迁移到 dan_report_uploads 表")
  285. @PostMapping("/report/parse-preview")
  286. public Result<Map<String, Object>> parsePreview(
  287. @RequestParam("file") MultipartFile file,
  288. @RequestParam("dimension") String dimension,
  289. @RequestParam("memberId") Long memberId,
  290. @RequestAttribute("userId") Long userId) {
  291. Result<Map<String, Object>> r = danReportUploadService.uploadAndParse(userId, memberId, dimension, file);
  292. if (r.getCode() == null || r.getCode() != 200 || r.getData() == null) return r;
  293. // 兼容旧字段名 draftId → uploadId
  294. Object uploadId = r.getData().get("uploadId");
  295. r.getData().put("draftId", uploadId);
  296. return r;
  297. }
  298. @Operation(summary = "确认并保存 DAN 报告(Phase 2)— 已迁移到 dan_report_uploads 表")
  299. @PostMapping("/report/confirm")
  300. public Result<String> confirmReport(@RequestBody Map<String, Object> body,
  301. @RequestAttribute("userId") Long userId) {
  302. Long draftId = body.get("draftId") != null ? Long.valueOf(body.get("draftId").toString()) : null;
  303. if (draftId == null) {
  304. return Result.error("draftId 不能为空");
  305. }
  306. String childName = (String) body.get("childName");
  307. String assessmentDateStr = (String) body.get("assessmentDate");
  308. Result<?> r = danReportUploadService.confirmUpload(draftId, userId, childName, assessmentDateStr);
  309. if (r.getCode() != null && r.getCode() == 200) {
  310. return Result.success("报告已确认并保存");
  311. }
  312. return Result.error(r.getCode() == null ? 500 : r.getCode(), r.getMessage());
  313. }
  314. @Operation(summary = "获取某孩子某维度的 DAN 报告列表")
  315. @PostMapping("/report/child")
  316. public Result<List<DanAssessmentResult>> getChildDanReports(@RequestBody Map<String, Object> body) {
  317. Long memberId = body.get("memberId") != null ? Long.valueOf(body.get("memberId").toString()) : null;
  318. String dimension = (String) body.get("dimension");
  319. if (memberId == null || dimension == null) {
  320. return Result.error("memberId 和 dimension 不能为空");
  321. }
  322. List<DanAssessmentResult> list = danAssessmentResultMapper.selectList(
  323. new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<DanAssessmentResult>()
  324. .eq(DanAssessmentResult::getDimension, dimension)
  325. .eq(DanAssessmentResult::getDraftStatus, "confirmed")
  326. .orderByDesc(DanAssessmentResult::getCreatedAt));
  327. return Result.success(list);
  328. }
  329. /**
  330. * 将 DanParsedReport 序列化为 JSON 字符串
  331. */
  332. private String parsedToJson(DanParsedReport parsed) {
  333. try {
  334. return new com.fasterxml.jackson.databind.ObjectMapper().writeValueAsString(parsed);
  335. } catch (Exception e) {
  336. // fallback: 手动构建简单 JSON
  337. StringBuilder sb = new StringBuilder();
  338. sb.append("{\"reportType\":\"").append(parsed.getReportType()).append("\",");
  339. sb.append("\"dimension\":\"").append(parsed.getDimension()).append("\",");
  340. sb.append("\"items\":[");
  341. if (parsed.getItems() != null) {
  342. boolean first = true;
  343. for (DanReportParseService.DataItem item : parsed.getItems()) {
  344. if (!first) sb.append(",");
  345. sb.append("{\"code\":\"").append(item.getCode()).append("\",");
  346. sb.append("\"name\":\"").append(item.getName()).append("\",");
  347. sb.append("\"value\":\"").append(item.getValue()).append("\",");
  348. sb.append("\"category\":\"").append(item.getCategory()).append("\"}");
  349. first = false;
  350. }
  351. }
  352. sb.append("],");
  353. sb.append("\"summary\":\"").append(parsed.getSummary() != null ? parsed.getSummary().replace("\"", "\\\"") : "").append("\",");
  354. sb.append("\"suggestions\":\"").append(parsed.getSuggestions() != null ? parsed.getSuggestions().replace("\"", "\\\"") : "").append("\"}");
  355. return sb.toString();
  356. }
  357. }
  358. }