|
|
@@ -56,6 +56,16 @@ public class DanReportParseService {
|
|
|
return parseA2Report(lineList, fullText);
|
|
|
} else if ("wisdom".equals(dimension)) {
|
|
|
return parseB4Report(lineList, fullText);
|
|
|
+ } else if ("cognition".equals(dimension)) {
|
|
|
+ return parseA1Report(lineList, fullText);
|
|
|
+ } else if ("learning".equals(dimension)) {
|
|
|
+ return parseB3Report(lineList, fullText);
|
|
|
+ } else if ("behavior".equals(dimension)) {
|
|
|
+ return parseB2Report(lineList, fullText);
|
|
|
+ } else if ("career".equals(dimension)) {
|
|
|
+ return parseB6Report(lineList, fullText);
|
|
|
+ } else if ("campus".equals(dimension)) {
|
|
|
+ return parseC1Report(lineList, fullText);
|
|
|
}
|
|
|
return DanParsedReport.empty();
|
|
|
}
|
|
|
@@ -235,6 +245,688 @@ public class DanReportParseService {
|
|
|
return new DanParsedReport("B4", "wisdom", items, extra, summary.toString(), suggestions.toString());
|
|
|
}
|
|
|
|
|
|
+// ======================== A1 报告解析(认知/cognition) ========================
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 解析 A1 儿童核心认知发展报告(6个认知维度)
|
|
|
+ * 维度:感知觉、注意力、记忆力、推理能力、空间能力、加工速度
|
|
|
+ * 提取总分/百分位 + 各维度百分位 + 各维度原始分
|
|
|
+ */
|
|
|
+ private DanParsedReport parseA1Report(List<String> lines, String fullText) {
|
|
|
+ List<DataItem> items = new ArrayList<>();
|
|
|
+ Map<String, Object> extra = new LinkedHashMap<>();
|
|
|
+ StringBuilder summary = new StringBuilder();
|
|
|
+ StringBuilder suggestions = new StringBuilder();
|
|
|
+
|
|
|
+ // 1. 基本信息和日期
|
|
|
+ String reportDate = findFieldValue(lines, "测评日期", "报告日期", "评估日期");
|
|
|
+ String name = findFieldValue(lines, "姓名", "学生姓名", "被评估人");
|
|
|
+ extra.put("name", name);
|
|
|
+ extra.put("reportDate", reportDate);
|
|
|
+
|
|
|
+ // 2. 总分和百分位(通用提取)
|
|
|
+ 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("百分位"));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 6个核心认知维度
|
|
|
+ String[] cognitiveDims = {"感知觉", "注意力", "记忆力", "推理能力", "空间能力", "加工速度"};
|
|
|
+ String[] dimCodes = {"perception", "attention", "memory", "reasoning", "spatial", "processingSpeed"};
|
|
|
+
|
|
|
+ // 方法1: 从summary页面提取百分位 (格式: "感知觉 | Perception\n百分位(%)\n14")
|
|
|
+ for (int i = 0; i < cognitiveDims.length; i++) {
|
|
|
+ String dim = cognitiveDims[i];
|
|
|
+ String code = dimCodes[i];
|
|
|
+ Pattern p1 = Pattern.compile(
|
|
|
+ Pattern.quote(dim) + "\\s*\\|[^\\n]*\\n\\s*百分位(%)\\s*\\n\\s*(\\d+)");
|
|
|
+ Matcher m1 = p1.matcher(fullText);
|
|
|
+ if (m1.find()) {
|
|
|
+ String val = m1.group(1);
|
|
|
+ items.add(new DataItem(code + "_pct", dim + "百分位", val, "cognitive"));
|
|
|
+ extra.put(code + "_pct", val);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 方法2: 从detail页面提取原始分和百分位 (格式: "我的感知觉得分 41 | 14%")
|
|
|
+ for (int i = 0; i < cognitiveDims.length; i++) {
|
|
|
+ String dim = cognitiveDims[i];
|
|
|
+ String code = dimCodes[i];
|
|
|
+ Pattern p2 = Pattern.compile(
|
|
|
+ "我的" + Pattern.quote(dim) + "得分\\s*(\\d+)\\s*\\|\\s*(\\d+)%");
|
|
|
+ Matcher m2 = p2.matcher(fullText);
|
|
|
+ if (m2.find()) {
|
|
|
+ String score = m2.group(1);
|
|
|
+ String pct = m2.group(2);
|
|
|
+ items.add(new DataItem(code + "_score", dim + "得分", score, "cognitive"));
|
|
|
+ extra.put(code + "_score", score);
|
|
|
+ if (!extra.containsKey(code + "_pct")) {
|
|
|
+ items.add(new DataItem(code + "_pct", dim + "百分位", pct, "cognitive"));
|
|
|
+ extra.put(code + "_pct", pct);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 方法3: 后备 - 直接搜索 "维度名\n百分位(%)\n数字" (某些PDF格式)
|
|
|
+ int foundPct = 0;
|
|
|
+ for (String code : dimCodes) {
|
|
|
+ if (extra.containsKey(code + "_pct")) foundPct++;
|
|
|
+ }
|
|
|
+ if (foundPct < 6) {
|
|
|
+ for (int i = 0; i < cognitiveDims.length; i++) {
|
|
|
+ String dim = cognitiveDims[i];
|
|
|
+ String code = dimCodes[i];
|
|
|
+ if (!extra.containsKey(code + "_pct")) {
|
|
|
+ Pattern p3 = Pattern.compile(
|
|
|
+ Pattern.quote(dim) + ".*?百分位(%).*?(\\d+)", Pattern.DOTALL);
|
|
|
+ Matcher m3 = p3.matcher(fullText);
|
|
|
+ if (m3.find()) {
|
|
|
+ String val = m3.group(1);
|
|
|
+ items.add(new DataItem(code + "_pct", dim + "百分位", val, "cognitive"));
|
|
|
+ extra.put(code + "_pct", val);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 4. 通用正则:提取所有 "维度名: 分数" 模式
|
|
|
+ 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("A1", "cognition", items, extra, summary.toString(), suggestions.toString());
|
|
|
+ }
|
|
|
+
|
|
|
+ // ======================== B3 报告解析(学习/learning) ========================
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 解析 B3 核心学习能力报告
|
|
|
+ * - 执行功能3维:抑制控制、工作记忆、认知灵活性(百分位)
|
|
|
+ * - 学习动机3维:深层动机、表面动机、自我效能感(十分制)
|
|
|
+ * - 学习策略3维:深层方法与策略、表面方法与策略、学习自我调节(十分制)
|
|
|
+ */
|
|
|
+ private DanParsedReport parseB3Report(List<String> lines, String fullText) {
|
|
|
+ List<DataItem> items = new ArrayList<>();
|
|
|
+ Map<String, Object> extra = new LinkedHashMap<>();
|
|
|
+ StringBuilder summary = new StringBuilder();
|
|
|
+ StringBuilder suggestions = new StringBuilder();
|
|
|
+
|
|
|
+ // 1. 基本信息和日期
|
|
|
+ String reportDate = findFieldValue(lines, "测评日期", "报告日期", "评估日期");
|
|
|
+ String name = findFieldValue(lines, "姓名", "学生姓名", "被评估人");
|
|
|
+ extra.put("name", name);
|
|
|
+ extra.put("reportDate", reportDate);
|
|
|
+
|
|
|
+ // 2. 总分和百分位
|
|
|
+ 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("百分位"));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 执行功能 (3维度, 百分位) 格式: "抑制控制 94% 工作记忆 89% 认知灵活性 60%"
|
|
|
+ String[][] execDims = {
|
|
|
+ {"抑制控制", "inhibitoryControl"},
|
|
|
+ {"工作记忆", "workingMemory"},
|
|
|
+ {"认知灵活性", "cognitiveFlexibility"}
|
|
|
+ };
|
|
|
+ for (String[] dim : execDims) {
|
|
|
+ Pattern p = Pattern.compile(Pattern.quote(dim[0]) + "\\s*(\\d+)%");
|
|
|
+ Matcher m = p.matcher(fullText);
|
|
|
+ if (m.find()) {
|
|
|
+ String val = m.group(1);
|
|
|
+ items.add(new DataItem(dim[1], dim[0] + "百分位", val, "executiveFunction"));
|
|
|
+ extra.put(dim[1], val);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 4. 学习动机 (3维度, 十分制) 格式: "深层动机\n我的得分:8分"
|
|
|
+ String[][] motivationDims = {
|
|
|
+ {"深层动机", "deepMotivation"},
|
|
|
+ {"表面动机", "surfaceMotivation"},
|
|
|
+ {"自我效能感", "selfEfficacy"}
|
|
|
+ };
|
|
|
+ for (String[] dim : motivationDims) {
|
|
|
+ String val = extractScoreAfterLabel(fullText, dim[0]);
|
|
|
+ if (val != null) {
|
|
|
+ items.add(new DataItem(dim[1], dim[0], val, "learningMotivation"));
|
|
|
+ extra.put(dim[1], val);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 5. 学习策略 (3维度, 十分制) 格式: "深层方法与策略\n我的得分:6.8分"
|
|
|
+ String[][] strategyDims = {
|
|
|
+ {"深层方法与策略", "deepStrategy"},
|
|
|
+ {"表面方法与策略", "surfaceStrategy"},
|
|
|
+ {"学习自我调节", "selfRegulation"}
|
|
|
+ };
|
|
|
+ for (String[] dim : strategyDims) {
|
|
|
+ String val = extractScoreAfterLabel(fullText, dim[0]);
|
|
|
+ if (val != null) {
|
|
|
+ items.add(new DataItem(dim[1], dim[0], val, "learningStrategy"));
|
|
|
+ extra.put(dim[1], val);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 6. 通用正则提取
|
|
|
+ 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("B3", "learning", items, extra, summary.toString(), suggestions.toString());
|
|
|
+ }
|
|
|
+
|
|
|
+ // ======================== B2 报告解析(行为/behavior) ========================
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 解析 B2 儿童自我与家庭教养报告
|
|
|
+ * - 自我概念6维:行为表现、能力与学校表现、躯体外貌、情绪状态、合群、幸福与满足
|
|
|
+ * - 儿童行为8维:品行问题、情绪问题、学习问题、社交问题、生活习惯、多动倾向、刻板行为、拖延行为
|
|
|
+ * - 家庭环境10维:亲密、情感表达、和谐、独立性、成就向导、文化氛围、娱乐活动、道德观念、家务安排、家庭规则
|
|
|
+ */
|
|
|
+ private DanParsedReport parseB2Report(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. 自我概念6维 (格式: "行为表现 9分")
|
|
|
+ String[][] selfConceptDims = {
|
|
|
+ {"行为表现", "selfConcept_behavior"},
|
|
|
+ {"能力与学校表现", "selfConcept_school"},
|
|
|
+ {"躯体外貌", "selfConcept_appearance"},
|
|
|
+ {"情绪状态", "selfConcept_emotion"},
|
|
|
+ {"合群", "selfConcept_sociability"},
|
|
|
+ {"幸福与满足", "selfConcept_happiness"},
|
|
|
+ };
|
|
|
+ for (String[] dim : selfConceptDims) {
|
|
|
+ String val = extractScoreWithLabel(fullText, dim[0]);
|
|
|
+ if (val != null) {
|
|
|
+ items.add(new DataItem(dim[1], "自我概念_" + dim[0], val, "selfConcept"));
|
|
|
+ extra.put(dim[1], val);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 儿童行为8维
|
|
|
+ String[][] behaviorDims = {
|
|
|
+ {"品行问题", "conduct"},
|
|
|
+ {"情绪问题", "emotional"},
|
|
|
+ {"学习问题", "learning"},
|
|
|
+ {"社交问题", "social"},
|
|
|
+ {"生活习惯", "habits"},
|
|
|
+ {"多动倾向", "hyperactivity"},
|
|
|
+ {"刻板行为", "stereotypic"},
|
|
|
+ {"拖延行为", "procrastination"},
|
|
|
+ };
|
|
|
+ for (String[] dim : behaviorDims) {
|
|
|
+ // 方法1: 中文匹配 "品行问题 1分"
|
|
|
+ Pattern p1 = Pattern.compile(Pattern.quote(dim[0]) + "\\s*(\\d+)\\s*分");
|
|
|
+ Matcher m1 = p1.matcher(fullText);
|
|
|
+ if (m1.find()) {
|
|
|
+ items.add(new DataItem(dim[1], "行为_" + dim[0], m1.group(1), "behavior"));
|
|
|
+ extra.put(dim[1], m1.group(1));
|
|
|
+ } else {
|
|
|
+ // 方法2: 英文关键词
|
|
|
+ String engPattern = getBehaviorEnglishPattern(dim[0]);
|
|
|
+ if (engPattern != null) {
|
|
|
+ Pattern p2 = Pattern.compile(engPattern + "\\s+(\\d+)\\s*分");
|
|
|
+ Matcher m2 = p2.matcher(fullText);
|
|
|
+ if (m2.find()) {
|
|
|
+ items.add(new DataItem(dim[1], "行为_" + dim[0], m2.group(1), "behavior"));
|
|
|
+ extra.put(dim[1], m2.group(1));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 家庭环境10维
|
|
|
+ String[][] familyDims = {
|
|
|
+ {"亲密", "family_intimacy"},
|
|
|
+ {"情感表达", "family_expression"},
|
|
|
+ {"和谐", "family_harmony"},
|
|
|
+ {"独立性", "family_independence"},
|
|
|
+ {"成就向导", "family_achievement"},
|
|
|
+ {"文化氛围", "family_culture"},
|
|
|
+ {"娱乐活动", "family_recreation"},
|
|
|
+ {"道德观念", "family_morality"},
|
|
|
+ {"家务安排", "family_chores"},
|
|
|
+ {"家庭规则", "family_rules"},
|
|
|
+ };
|
|
|
+ for (String[] dim : familyDims) {
|
|
|
+ String val = extractScoreWithLabel(fullText, dim[0]);
|
|
|
+ if (val != null) {
|
|
|
+ items.add(new DataItem(dim[1], "家庭_" + dim[0], val, "family"));
|
|
|
+ extra.put(dim[1], val);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 通用正则提取
|
|
|
+ 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("B2", "behavior", items, extra, summary.toString(), suggestions.toString());
|
|
|
+ }
|
|
|
+
|
|
|
+ // ======================== B6 报告解析(职业/career) ========================
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 解析 B6 职业发展报告
|
|
|
+ * - Holland兴趣6型:艺术型、社会型、事业型、常规型、现实型、研究型
|
|
|
+ * - 多元智能8维:内省能力、空间能力、音乐能力、人际关系能力、自然能力、身体运动能力、语言能力、逻辑数学能力
|
|
|
+ * - 职业价值观:代表分数
|
|
|
+ */
|
|
|
+ private DanParsedReport parseB6Report(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. Holland职业兴趣6型 (格式: "艺术型 Artistic 7分")
|
|
|
+ String[][] interestDims = {
|
|
|
+ {"艺术型", "interest_artistic"},
|
|
|
+ {"社会型", "interest_social"},
|
|
|
+ {"事业型", "interest_enterprising"},
|
|
|
+ {"常规型", "interest_conventional"},
|
|
|
+ {"现实型", "interest_realistic"},
|
|
|
+ {"研究型", "interest_investigative"},
|
|
|
+ };
|
|
|
+ for (String[] dim : interestDims) {
|
|
|
+ Pattern p = Pattern.compile(Pattern.quote(dim[0]) + "\\s+\\w+\\s+(\\d+)\\s*分");
|
|
|
+ Matcher m = p.matcher(fullText);
|
|
|
+ if (m.find()) {
|
|
|
+ items.add(new DataItem(dim[1], "兴趣_" + dim[0], m.group(1), "hollandInterest"));
|
|
|
+ extra.put(dim[1], m.group(1));
|
|
|
+ } else {
|
|
|
+ // fallback: "艺术型 7分"
|
|
|
+ String val = extractScoreWithLabel(fullText, dim[0]);
|
|
|
+ if (val != null) {
|
|
|
+ items.add(new DataItem(dim[1], "兴趣_" + dim[0], val, "hollandInterest"));
|
|
|
+ extra.put(dim[1], val);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 多元智能8维 (格式: "内省能力 INTRAPERSONAL 8分")
|
|
|
+ String[][] abilityDims = {
|
|
|
+ {"内省能力", "ability_intrapersonal"},
|
|
|
+ {"空间能力", "ability_spatial"},
|
|
|
+ {"音乐能力", "ability_musical"},
|
|
|
+ {"人际关系能力", "ability_interpersonal"},
|
|
|
+ {"自然能力", "ability_naturalist"},
|
|
|
+ {"身体运动能力", "ability_kinesthetic"},
|
|
|
+ {"语言能力", "ability_linguistic"},
|
|
|
+ {"逻辑数学能力", "ability_logical"},
|
|
|
+ };
|
|
|
+ for (String[] dim : abilityDims) {
|
|
|
+ Pattern p = Pattern.compile(Pattern.quote(dim[0]) + "\\s+\\w+\\s+(\\d+)\\s*分");
|
|
|
+ Matcher m = p.matcher(fullText);
|
|
|
+ if (m.find()) {
|
|
|
+ int val = Integer.parseInt(m.group(1));
|
|
|
+ if (val >= 1 && val <= 15) {
|
|
|
+ items.add(new DataItem(dim[1], "能力_" + dim[0], m.group(1), "multipleIntelligence"));
|
|
|
+ extra.put(dim[1], m.group(1));
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // fallback: "内省能力 8分"
|
|
|
+ Pattern p2 = Pattern.compile(Pattern.quote(dim[0]) + "\\s*(\\d+)\\s*分");
|
|
|
+ Matcher m2 = p2.matcher(fullText);
|
|
|
+ if (m2.find()) {
|
|
|
+ int val = Integer.parseInt(m2.group(1));
|
|
|
+ if (val >= 1 && val <= 15) {
|
|
|
+ items.add(new DataItem(dim[1], "能力_" + dim[0], m2.group(1), "multipleIntelligence"));
|
|
|
+ extra.put(dim[1], m2.group(1));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 职业价值观
|
|
|
+ if (fullText.contains("我的职业价值观")) {
|
|
|
+ Pattern p = Pattern.compile("我的职业价值观.*?(\\d+(?:\\.\\d+)?)", Pattern.DOTALL);
|
|
|
+ Matcher m = p.matcher(fullText);
|
|
|
+ if (m.find()) {
|
|
|
+ double val = Double.parseDouble(m.group(1));
|
|
|
+ if (val >= 1 && val <= 15) {
|
|
|
+ items.add(new DataItem("career_value", "职业价值观", m.group(1), "careerValue"));
|
|
|
+ extra.put("careerValue", m.group(1));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (!extra.containsKey("careerValue") && fullText.contains("职业价值观")) {
|
|
|
+ Pattern p = Pattern.compile("职业价值观.*?最高分[^\\d]*(\\d+(?:\\.\\d+)?)", Pattern.DOTALL);
|
|
|
+ Matcher m = p.matcher(fullText);
|
|
|
+ if (m.find()) {
|
|
|
+ double val = Double.parseDouble(m.group(1));
|
|
|
+ if (val >= 1 && val <= 15) {
|
|
|
+ items.add(new DataItem("career_value", "职业价值观", m.group(1), "careerValue"));
|
|
|
+ extra.put("careerValue", m.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("B6", "career", items, extra, summary.toString(), suggestions.toString());
|
|
|
+ }
|
|
|
+
|
|
|
+ // ======================== C1 报告解析(校园/campus) ========================
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 解析 C1 校园标准版报告
|
|
|
+ * - 核心认知6维(复用A1逻辑):感知觉、注意力、记忆力、推理能力、空间能力、加工速度
|
|
|
+ * - 大五人格5维(复用A2逻辑):开放性、宜人性、责任心、外倾性、神经质
|
|
|
+ * - 自驱力3维(复用B4逻辑):自主性、胜任感、归属感
|
|
|
+ * - 自我概念6维(复用B2逻辑):行为表现、能力与学校、躯体外貌、情绪状态、合群、幸福与满足
|
|
|
+ */
|
|
|
+ private DanParsedReport parseC1Report(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. 核心认知6维 (复用A1逻辑: "我的感知觉得分 41 | 14%")
|
|
|
+ String[] cognitiveDims = {"感知觉", "注意力", "记忆力", "推理能力", "空间能力", "加工速度"};
|
|
|
+ String[] cogCodes = {"perception", "attention", "memory", "reasoning", "spatial", "processingSpeed"};
|
|
|
+ for (int i = 0; i < cognitiveDims.length; i++) {
|
|
|
+ String dim = cognitiveDims[i];
|
|
|
+ String code = cogCodes[i];
|
|
|
+ // 格式: "我的感知觉得分 41 | 14%"
|
|
|
+ Pattern p = Pattern.compile("我的" + Pattern.quote(dim) + "得分\\s*(\\d+)\\s*\\|\\s*(\\d+)%");
|
|
|
+ Matcher m = p.matcher(fullText);
|
|
|
+ if (m.find()) {
|
|
|
+ items.add(new DataItem(code + "_score", dim + "得分", m.group(1), "cognitive"));
|
|
|
+ extra.put(code + "_score", m.group(1));
|
|
|
+ items.add(new DataItem(code + "_pct", dim + "百分位", m.group(2), "cognitive"));
|
|
|
+ extra.put(code + "_pct", m.group(2));
|
|
|
+ } else {
|
|
|
+ // fallback: "感知觉 | Perception\n百分位(%)\n14"
|
|
|
+ Pattern p2 = Pattern.compile(Pattern.quote(dim) + "\\s*\\|[^\\n]*\\n\\s*百分位(%)\\s*\\n\\s*(\\d+)");
|
|
|
+ Matcher m2 = p2.matcher(fullText);
|
|
|
+ if (m2.find()) {
|
|
|
+ items.add(new DataItem(code + "_pct", dim + "百分位", m2.group(1), "cognitive"));
|
|
|
+ extra.put(code + "_pct", m2.group(1));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 大五人格5维 (复用A2逻辑)
|
|
|
+ String[] big5Names = {"开放性", "宜人性", "责任心", "外倾性", "神经质"};
|
|
|
+ for (String dim : big5Names) {
|
|
|
+ Pattern p = Pattern.compile("您在[\"\"「]" + Pattern.quote(dim) + "[\"\"」].*?得分是\\s*(\\d+(?:\\.\\d+)?)\\s*分");
|
|
|
+ Matcher m = p.matcher(fullText);
|
|
|
+ if (m.find()) {
|
|
|
+ items.add(new DataItem("big5_" + dim, dim, m.group(1), "bigFive"));
|
|
|
+ extra.put("big5_" + dim, m.group(1));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 自驱力3维 (使用"我的得分"模式)
|
|
|
+ Pattern drivingP = Pattern.compile("我的得分[::](\\d+\\.?\\d*)");
|
|
|
+ Matcher drivingM = drivingP.matcher(fullText);
|
|
|
+ List<String> drivingScores = new ArrayList<>();
|
|
|
+ while (drivingM.find()) {
|
|
|
+ drivingScores.add(drivingM.group(1));
|
|
|
+ }
|
|
|
+ String[] drivingNames = {"自主性", "胜任感", "归属感"};
|
|
|
+ String[] drivingCodes = {"autonomy", "competence", "belonging"};
|
|
|
+ for (int i = 0; i < drivingScores.size() && i < drivingNames.length; i++) {
|
|
|
+ items.add(new DataItem(drivingCodes[i], drivingNames[i], drivingScores.get(i), "selfDriving"));
|
|
|
+ extra.put(drivingCodes[i], drivingScores.get(i));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 4. 自我概念6维 (在SELF-CONCEPT标记前找百分位)
|
|
|
+ int scStart = fullText.indexOf("SELF-CONCEPT");
|
|
|
+ if (scStart >= 0) {
|
|
|
+ String beforeSC = fullText.substring(0, scStart);
|
|
|
+ Pattern pctP = Pattern.compile("(\\d+)%");
|
|
|
+ Matcher pctM = pctP.matcher(beforeSC);
|
|
|
+ List<String> pcts = new ArrayList<>();
|
|
|
+ while (pctM.find()) {
|
|
|
+ pcts.add(pctM.group(1));
|
|
|
+ }
|
|
|
+ String[] scDims = {"行为表现", "能力与学校", "躯体外貌", "情绪状态", "合群", "幸福与满足"};
|
|
|
+ String[] scCodes = {"sc_behavior", "sc_school", "sc_appearance", "sc_emotion", "sc_sociability", "sc_happiness"};
|
|
|
+ int startIdx = Math.max(0, pcts.size() - 12);
|
|
|
+ for (int i = 0; i < scDims.length; i++) {
|
|
|
+ int idx = startIdx + i * 2 + 1;
|
|
|
+ if (idx < pcts.size()) {
|
|
|
+ items.add(new DataItem(scCodes[i], "自我概念_" + scDims[i], pcts.get(idx), "selfConcept"));
|
|
|
+ extra.put(scCodes[i], pcts.get(idx));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 通用正则提取
|
|
|
+ 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("C1", "campus", items, extra, summary.toString(), suggestions.toString());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 从文本中提取标签后的分数 (格式: "行为表现 9分")
|
|
|
+ */
|
|
|
+ private String extractScoreWithLabel(String fullText, String label) {
|
|
|
+ Pattern p = Pattern.compile(Pattern.quote(label) + "\\s*(\\d+)\\s*分");
|
|
|
+ Matcher m = p.matcher(fullText);
|
|
|
+ if (m.find()) {
|
|
|
+ return m.group(1);
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 儿童行为维度的英文关键词映射
|
|
|
+ */
|
|
|
+ private String getBehaviorEnglishPattern(String cnName) {
|
|
|
+ switch (cnName) {
|
|
|
+ case "品行问题": return "Conduct problems";
|
|
|
+ case "情绪问题": return "Emotional state";
|
|
|
+ case "学习问题": return "Learning situation";
|
|
|
+ case "社交问题": return "Social situation";
|
|
|
+ case "生活习惯": return "Habits.*?customs";
|
|
|
+ case "多动倾向": return "Hyperactivity";
|
|
|
+ case "刻板行为": return "Stereotypic";
|
|
|
+ case "拖延行为": return "Procrastination";
|
|
|
+ default: return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 提取总分和百分位(通用,所有报告类型共用)
|
|
|
+ * PDF格式: "112\n总得分\nTotal Score\n79\n百分位(%)"
|
|
|
+ */
|
|
|
+ private Map<String, String> extractTotalScoreAndPercentile(String fullText) {
|
|
|
+ Map<String, String> result = new LinkedHashMap<>();
|
|
|
+ if (fullText == null) return result;
|
|
|
+
|
|
|
+ // 主格式: 数字\n总得分
|
|
|
+ Pattern scoreP = Pattern.compile("(\\d+)\\s*\\n\\s*总得分");
|
|
|
+ Matcher scoreM = scoreP.matcher(fullText);
|
|
|
+ if (scoreM.find()) {
|
|
|
+ result.put("总分", scoreM.group(1));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 百分位: 总得分...数字...百分位
|
|
|
+ Pattern pctP = Pattern.compile("总得分.*?(\\d+)\\s*百分位", Pattern.DOTALL);
|
|
|
+ Matcher pctM = pctP.matcher(fullText);
|
|
|
+ if (pctM.find()) {
|
|
|
+ result.put("百分位", pctM.group(1));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 后备1: 旧版 "总分" 格式
|
|
|
+ if (!result.containsKey("总分")) {
|
|
|
+ Pattern fallbackP = Pattern.compile("总分[^\\d]*(\\d+)");
|
|
|
+ Matcher fallbackM = fallbackP.matcher(fullText);
|
|
|
+ if (fallbackM.find()) {
|
|
|
+ result.put("总分", fallbackM.group(1));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 后备2: 旧版 "百分位" 格式
|
|
|
+ if (!result.containsKey("百分位")) {
|
|
|
+ Pattern fallbackP2 = Pattern.compile("百分位[^\\d]*(\\d+)");
|
|
|
+ Matcher fallbackM2 = fallbackP2.matcher(fullText);
|
|
|
+ if (fallbackM2.find()) {
|
|
|
+ result.put("百分位", fallbackM2.group(1));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 提取标签后的分数值。支持格式:
|
|
|
+ * - "深层动机\n我的得分:8分"
|
|
|
+ * - "深层动机 8分"
|
|
|
+ */
|
|
|
+ private String extractScoreAfterLabel(String fullText, String label) {
|
|
|
+ // 方法1: "深层动机...我的得分:8分"
|
|
|
+ Pattern p1 = Pattern.compile(
|
|
|
+ Pattern.quote(label) + ".*?我的得分[::]\\s*(\\d+(?:\\.\\d+)?)",
|
|
|
+ Pattern.DOTALL);
|
|
|
+ Matcher m1 = p1.matcher(fullText);
|
|
|
+ if (m1.find()) {
|
|
|
+ return m1.group(1);
|
|
|
+ }
|
|
|
+ // 方法2: "深层动机 8分"
|
|
|
+ Pattern p2 = Pattern.compile(
|
|
|
+ Pattern.quote(label) + "\\s*(\\d+(?:\\.\\d+)?)\\s*分");
|
|
|
+ Matcher m2 = p2.matcher(fullText);
|
|
|
+ if (m2.find()) {
|
|
|
+ return m2.group(1);
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
// ======================== 解析辅助方法 ========================
|
|
|
|
|
|
/**
|