|
|
@@ -66,6 +66,8 @@ public class DanReportParseService {
|
|
|
return parseB6Report(lineList, fullText);
|
|
|
} else if ("campus".equals(dimension)) {
|
|
|
return parseC1Report(lineList, fullText);
|
|
|
+ } else if ("adolescent".equals(dimension)) {
|
|
|
+ return parseB5Report(lineList, fullText);
|
|
|
}
|
|
|
return DanParsedReport.empty();
|
|
|
}
|
|
|
@@ -831,6 +833,209 @@ public class DanReportParseService {
|
|
|
return new DanParsedReport("C1", "campus", items, extra, summary.toString(), suggestions.toString());
|
|
|
}
|
|
|
|
|
|
+ // ======================== B5 报告解析(青春期/adolescent) ========================
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 解析 B5 青春期挑战报告(文本部分,不含指南针图像分析)
|
|
|
+ * - 情绪调节策略2维:认知重评、表达抑制
|
|
|
+ * - 学业压力源5维:学业负担、家庭期望、师生关系、自我期望、同伴竞争
|
|
|
+ * - 人际关系问题4维:母亲关系、父亲关系、师生关系、同伴关系
|
|
|
+ * - 社交能力5维:主动交往、情感支持、情感表达、表达影响、冲突解决
|
|
|
+ * - 睡眠数据:时长、效率、入睡时间、主观质量、日间功能、催眠药物
|
|
|
+ * - 运动数据:时长、强度
|
|
|
+ * - 网络依赖数据
|
|
|
+ */
|
|
|
+ private DanParsedReport parseB5Report(List<String> lines, String fullText) {
|
|
|
+ List<DataItem> items = new ArrayList<>();
|
|
|
+ Map<String, Object> extra = new LinkedHashMap<>();
|
|
|
+ StringBuilder summary = new StringBuilder();
|
|
|
+ StringBuilder suggestions = new StringBuilder();
|
|
|
+
|
|
|
+ String reportDate = findFieldValue(lines, "测评日期", "报告日期", "评估日期");
|
|
|
+ String name = findFieldValue(lines, "姓名", "学生姓名", "被评估人");
|
|
|
+ extra.put("name", name);
|
|
|
+ extra.put("reportDate", reportDate);
|
|
|
+
|
|
|
+ Map<String, String> scorePct = extractTotalScoreAndPercentile(fullText);
|
|
|
+ if (scorePct.get("总分") != null) {
|
|
|
+ items.add(new DataItem("total_score", "总分", scorePct.get("总分"), "overall"));
|
|
|
+ extra.put("totalScore", scorePct.get("总分"));
|
|
|
+ }
|
|
|
+ if (scorePct.get("百分位") != null) {
|
|
|
+ items.add(new DataItem("percentile", "百分位", scorePct.get("百分位"), "overall"));
|
|
|
+ extra.put("percentile", scorePct.get("百分位"));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 1. 情绪调节策略2维
|
|
|
+ String[] erLabels = {"认知重评", "表达抑制"};
|
|
|
+ String[] erCodes = {"er_reappraisal", "er_suppression"};
|
|
|
+ String[] erEng = {"Cognitive Reappraisal", "Expressive Suppression"};
|
|
|
+ for (int i = 0; i < erLabels.length; i++) {
|
|
|
+ Pattern p1 = Pattern.compile(
|
|
|
+ Pattern.quote(erLabels[i]) + ".*?" + Pattern.quote(erEng[i]) + "\\s*(\\d+(?:\\.\\d+)?)\\s*分",
|
|
|
+ Pattern.DOTALL);
|
|
|
+ Matcher m1 = p1.matcher(fullText);
|
|
|
+ if (m1.find()) {
|
|
|
+ items.add(new DataItem(erCodes[i], erLabels[i], m1.group(1), "emotionRegulation"));
|
|
|
+ extra.put(erCodes[i], m1.group(1));
|
|
|
+ } else {
|
|
|
+ String val = extractScoreWithLabel(fullText, erLabels[i]);
|
|
|
+ if (val != null) {
|
|
|
+ items.add(new DataItem(erCodes[i], erLabels[i], val, "emotionRegulation"));
|
|
|
+ extra.put(erCodes[i], val);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 学业压力源5维
|
|
|
+ String[] stressDims = {"学业负担", "家庭期望", "师生关系", "自我期望", "同伴竞争"};
|
|
|
+ String[] stressCodes = {"stress_burden", "stress_family", "stress_teacher", "stress_self", "stress_peer"};
|
|
|
+ String stressSection = extractRegion(fullText,
|
|
|
+ new String[]{"学业压力源", "学业压力"},
|
|
|
+ new String[]{"人际关系问题", "社交能力", "睡眠"});
|
|
|
+ for (int i = 0; i < stressDims.length; i++) {
|
|
|
+ String val = extractNamedScore(stressSection != null ? stressSection : fullText, stressDims[i]);
|
|
|
+ if (val != null) {
|
|
|
+ items.add(new DataItem(stressCodes[i], "学业压力_" + stressDims[i], val, "academicStress"));
|
|
|
+ extra.put(stressCodes[i], val);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 人际关系问题4维
|
|
|
+ String[] relationDims = {"母亲关系", "父亲关系", "师生关系", "同伴关系"};
|
|
|
+ String[] relationCodes = {"rel_mother", "rel_father", "rel_teacher", "rel_peer"};
|
|
|
+ String relSection = extractRegion(fullText,
|
|
|
+ new String[]{"人际关系问题"},
|
|
|
+ new String[]{"社交能力"});
|
|
|
+ for (int i = 0; i < relationDims.length; i++) {
|
|
|
+ Pattern p = Pattern.compile(
|
|
|
+ Pattern.quote(relationDims[i]) + "\\n[^\\n]*。\\n(\\d+(?:\\.\\d+)?)",
|
|
|
+ Pattern.DOTALL);
|
|
|
+ Matcher m = p.matcher(relSection != null ? relSection : fullText);
|
|
|
+ if (m.find()) {
|
|
|
+ items.add(new DataItem(relationCodes[i], "人际_" + relationDims[i], m.group(1), "interpersonal"));
|
|
|
+ extra.put(relationCodes[i], m.group(1));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 4. 社交能力5维
|
|
|
+ String[] socialDims = {"主动交往", "情感支持", "情感表达", "表达影响", "冲突解决"};
|
|
|
+ String[] socialCodes = {"soc_initiative", "soc_support", "soc_expression", "soc_influence", "soc_conflict"};
|
|
|
+ String socSection = extractRegion(fullText,
|
|
|
+ new String[]{"社交能力"},
|
|
|
+ new String[]{"睡眠"});
|
|
|
+ Pattern socP = Pattern.compile(
|
|
|
+ "同龄人平均水平\\s*\\n\\s*(\\d+(?:\\.\\d+)?)\\s*\\n\\s*(\\d+(?:\\.\\d+)?)\\s*\\n\\s*(\\d+(?:\\.\\d+)?)\\s*\\n\\s*(\\d+(?:\\.\\d+)?)\\s*\\n\\s*(\\d+(?:\\.\\d+)?)",
|
|
|
+ Pattern.DOTALL);
|
|
|
+ Matcher socM = socP.matcher(socSection != null ? socSection : fullText);
|
|
|
+ if (socM.find()) {
|
|
|
+ for (int i = 0; i < socialDims.length && i + 1 <= socM.groupCount(); i++) {
|
|
|
+ items.add(new DataItem(socialCodes[i], "社交_" + socialDims[i], socM.group(i + 1), "socialAbility"));
|
|
|
+ extra.put(socialCodes[i], socM.group(i + 1));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 5. 睡眠数据
|
|
|
+ Pattern sleepH = Pattern.compile("睡眠时长[^。]*?(\\d+(?:\\.\\d+)?)\\s*小时");
|
|
|
+ Matcher sleepHM = sleepH.matcher(fullText);
|
|
|
+ if (sleepHM.find()) {
|
|
|
+ items.add(new DataItem("sleep_hours", "睡眠时长", sleepHM.group(1), "sleep"));
|
|
|
+ extra.put("sleepHours", sleepHM.group(1));
|
|
|
+ }
|
|
|
+
|
|
|
+ Pattern sleepE = Pattern.compile("睡眠效率[^。]*?(\\d+(?:\\.\\d+)?)%");
|
|
|
+ Matcher sleepEM = sleepE.matcher(fullText);
|
|
|
+ if (sleepEM.find()) {
|
|
|
+ items.add(new DataItem("sleep_efficiency", "睡眠效率", sleepEM.group(1), "sleep"));
|
|
|
+ extra.put("sleepEfficiency", sleepEM.group(1));
|
|
|
+ }
|
|
|
+
|
|
|
+ Pattern sleepL = Pattern.compile("入睡时间[^。]*?(\\d+)-(\\d+)分钟");
|
|
|
+ Matcher sleepLM = sleepL.matcher(fullText);
|
|
|
+ if (sleepLM.find()) {
|
|
|
+ items.add(new DataItem("sleep_latency", "入睡时间", sleepLM.group(1), "sleep"));
|
|
|
+ extra.put("sleepLatency", sleepLM.group(1));
|
|
|
+ }
|
|
|
+
|
|
|
+ String[][] sleepQual = {
|
|
|
+ {"主观睡眠质量", "sleep_quality"},
|
|
|
+ {"日间功能障碍", "sleep_dysfunction"},
|
|
|
+ {"催眠药物", "sleep_medication"},
|
|
|
+ };
|
|
|
+ for (String[] dim : sleepQual) {
|
|
|
+ String val = extractNamedScore(fullText, dim[0]);
|
|
|
+ if (val != null) {
|
|
|
+ items.add(new DataItem(dim[1], "睡眠_" + dim[0], val, "sleep"));
|
|
|
+ extra.put(dim[1], val);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 6. 运动/网络
|
|
|
+ Pattern exercise = Pattern.compile("运动时长[^。]*?(\\d+(?:\\.\\d+)?)\\s*(小时|分钟|h)");
|
|
|
+ Matcher exM = exercise.matcher(fullText);
|
|
|
+ if (exM.find()) {
|
|
|
+ items.add(new DataItem("exercise_duration", "运动时长", exM.group(1), "exercise"));
|
|
|
+ extra.put("exerciseDuration", exM.group(1));
|
|
|
+ }
|
|
|
+
|
|
|
+ Pattern internet = Pattern.compile("网络使用[^。]*?(\\d+(?:\\.\\d+)?)\\s*小时");
|
|
|
+ Matcher inM = internet.matcher(fullText);
|
|
|
+ if (inM.find()) {
|
|
|
+ items.add(new DataItem("internet_duration", "网络使用时长", inM.group(1), "internet"));
|
|
|
+ extra.put("internetDuration", inM.group(1));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 通用正则提取
|
|
|
+ Pattern scorePattern = Pattern.compile("([\\u4e00-\\u9fa5]{2,8})[::]\\s*(\\d+(\\.\\d+)?)");
|
|
|
+ Matcher matcher = scorePattern.matcher(fullText);
|
|
|
+ while (matcher.find()) {
|
|
|
+ String key = matcher.group(1).trim();
|
|
|
+ String val = matcher.group(2).trim();
|
|
|
+ boolean exists = false;
|
|
|
+ for (DataItem item : items) {
|
|
|
+ if (item.getName().equals(key)) { exists = true; break; }
|
|
|
+ }
|
|
|
+ if (!exists && !key.contains("日期") && !key.contains("姓名")) {
|
|
|
+ items.add(new DataItem("score_" + key.hashCode(), key, val, "auto"));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ summary.append(extractSection(fullText, "测评总结", "成长建议"));
|
|
|
+ suggestions.append(extractSection(fullText, "成长建议", null));
|
|
|
+ return new DanParsedReport("B5", "adolescent", items, extra, summary.toString(), suggestions.toString());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 提取两个标记之间的文本区域
|
|
|
+ */
|
|
|
+ private String extractRegion(String fullText, String[] startMarkers, String[] endMarkers) {
|
|
|
+ int start = -1;
|
|
|
+ for (String marker : startMarkers) {
|
|
|
+ int idx = fullText.indexOf(marker);
|
|
|
+ if (idx >= 0) { start = idx; break; }
|
|
|
+ }
|
|
|
+ if (start < 0) return null;
|
|
|
+ int end = fullText.length();
|
|
|
+ for (String marker : endMarkers) {
|
|
|
+ int idx = fullText.indexOf(marker, start + 1);
|
|
|
+ if (idx >= 0 && idx < end) { end = idx; }
|
|
|
+ }
|
|
|
+ return end > start ? fullText.substring(start, end) : null;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 提取区域中维度名后的分数 (格式: "维度名\n分数")
|
|
|
+ */
|
|
|
+ private String extractNamedScore(String text, String label) {
|
|
|
+ if (text == null) return null;
|
|
|
+ Pattern p = Pattern.compile(Pattern.quote(label) + "\\s*\\n\\s*(\\d+(?:\\.\\d+)?)");
|
|
|
+ Matcher m = p.matcher(text);
|
|
|
+ if (m.find()) {
|
|
|
+ return m.group(1);
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 从文本中提取标签后的分数 (格式: "行为表现 9分")
|
|
|
*/
|