|
@@ -1841,19 +1841,26 @@ public class HealthReportService {
|
|
|
oldReport.setShannonIndex(scoreSource.get("shannonIndex").toString());
|
|
oldReport.setShannonIndex(scoreSource.get("shannonIndex").toString());
|
|
|
}
|
|
}
|
|
|
// 用户可手动修正报告日期(AI 解析结果不准确时)
|
|
// 用户可手动修正报告日期(AI 解析结果不准确时)
|
|
|
|
|
+ // 严格解析:lenient=false(拒绝 2025-13-45 这种"静默成功")+ 整串必须被消费
|
|
|
|
|
+ // 解析失败/未来日期一律抛业务异常(GlobalExceptionHandler → 400 + 明确提示),绝不静默吞掉
|
|
|
|
|
+ boolean reportDateChanged = false;
|
|
|
Object reportDateObj = scoreSource.get("reportDate");
|
|
Object reportDateObj = scoreSource.get("reportDate");
|
|
|
if (reportDateObj != null && !String.valueOf(reportDateObj).trim().isEmpty()) {
|
|
if (reportDateObj != null && !String.valueOf(reportDateObj).trim().isEmpty()) {
|
|
|
String reportDateStr = String.valueOf(reportDateObj).trim();
|
|
String reportDateStr = String.valueOf(reportDateObj).trim();
|
|
|
- try {
|
|
|
|
|
- java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd");
|
|
|
|
|
- java.util.Date parsedDate = sdf.parse(reportDateStr);
|
|
|
|
|
- if (parsedDate != null) {
|
|
|
|
|
- oldReport.setReportDate(new java.sql.Date(parsedDate.getTime()));
|
|
|
|
|
- log.info("报告日期已更新: reportId={}, reportDate={}", reportId, reportDateStr);
|
|
|
|
|
- }
|
|
|
|
|
- } catch (Exception e) {
|
|
|
|
|
- log.warn("reportDate 格式解析失败 reportId={}, value={}: {}", reportId, reportDateStr, e.getMessage());
|
|
|
|
|
|
|
+ java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd");
|
|
|
|
|
+ sdf.setLenient(false);
|
|
|
|
|
+ java.text.ParsePosition parsePos = new java.text.ParsePosition(0);
|
|
|
|
|
+ java.util.Date parsedDate = sdf.parse(reportDateStr, parsePos);
|
|
|
|
|
+ if (parsedDate == null || parsePos.getIndex() != reportDateStr.length()) {
|
|
|
|
|
+ throw new IllegalArgumentException("报告日期格式应为 yyyy-MM-dd,当前值:" + reportDateStr);
|
|
|
}
|
|
}
|
|
|
|
|
+ // 当天允许,未来日期拒绝
|
|
|
|
|
+ if (parsedDate.after(new Date())) {
|
|
|
|
|
+ throw new IllegalArgumentException("报告日期不能晚于今天,当前值:" + reportDateStr);
|
|
|
|
|
+ }
|
|
|
|
|
+ oldReport.setReportDate(new java.sql.Date(parsedDate.getTime()));
|
|
|
|
|
+ reportDateChanged = true;
|
|
|
|
|
+ log.info("报告日期已更新: reportId={}, reportDate={}", reportId, reportDateStr);
|
|
|
}
|
|
}
|
|
|
oldReport.setUpdatedAt(new Date());
|
|
oldReport.setUpdatedAt(new Date());
|
|
|
healthReportMapper.updateById(oldReport);
|
|
healthReportMapper.updateById(oldReport);
|
|
@@ -2016,6 +2023,32 @@ public class HealthReportService {
|
|
|
} catch (Exception e) {
|
|
} catch (Exception e) {
|
|
|
log.warn("归一化管道刷新失败 reportId={}: {}", reportId, e.getMessage());
|
|
log.warn("归一化管道刷新失败 reportId={}: {}", reportId, e.getMessage());
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ // 8. 报告日期变更后的联动(须在归一化管道之后:管道重写 indicator_values 时序行)
|
|
|
|
|
+ if (reportDateChanged) {
|
|
|
|
|
+ // a) 刷新成员指标快照:改日期改变 report_date 排序,"上一期"定位需重算
|
|
|
|
|
+ // 内部有 subjectId/reportId null 保护;effectiveSubjectId 为本次编辑后的实际归属
|
|
|
|
|
+ Long effectiveSubjectId = (subjectId != null && subjectId > 0) ? subjectId : oldSubjectId;
|
|
|
|
|
+ try {
|
|
|
|
|
+ refreshMemberIndicatorCurrent(effectiveSubjectId, reportId);
|
|
|
|
|
+ log.info("报告日期变更后已刷新成员{}的指标快照", effectiveSubjectId);
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.warn("报告日期变更后刷新成员{}指标快照失败 reportId={}: {}",
|
|
|
|
|
+ effectiveSubjectId, reportId, e.getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+ // b) 手动修正的日期打 date_source='real' 标记(T01 迁移列;列不存在时跳过不崩)
|
|
|
|
|
+ try {
|
|
|
|
|
+ if (columnExists("indicator_values", "date_source")) {
|
|
|
|
|
+ int updated = jdbcTemplate.update(
|
|
|
|
|
+ "UPDATE indicator_values SET date_source = 'real' WHERE report_id = ?", reportId);
|
|
|
|
|
+ log.info("已标记报告{}的时序行 date_source='real',共{}行", reportId, updated);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ log.info("indicator_values 无 date_source 列(T01 迁移未落地),跳过 date_source 标记 reportId={}", reportId);
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.warn("date_source 标记失败 reportId={}: {}", reportId, e.getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private Integer toInteger(Object value) {
|
|
private Integer toInteger(Object value) {
|
|
@@ -2027,6 +2060,21 @@ public class HealthReportService {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 列是否存在(与 DatabaseInitializer.columnExists 同实现思路)
|
|
|
|
|
+ * 用于可选列(如 T01 的 indicator_values.date_source)做兜底判断:列不存在时跳过写入,不报错
|
|
|
|
|
+ */
|
|
|
|
|
+ private boolean columnExists(String table, String column) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ Integer count = jdbcTemplate.queryForObject(
|
|
|
|
|
+ "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ? AND COLUMN_NAME = ?",
|
|
|
|
|
+ Integer.class, table, column);
|
|
|
|
|
+ return count != null && count > 0;
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
* 保存 payload 快照并记录编辑日志(action: create/edit)
|
|
* 保存 payload 快照并记录编辑日志(action: create/edit)
|
|
|
*/
|
|
*/
|