| | |
| | | @Resource |
| | | private JdbcTemplate jdbcTemplate; |
| | | |
| | | @Resource |
| | | private DesensitizeService desensitizeService; |
| | | |
| | | /** 禁止外发到大模型的字段:个人与主体身份类信息(在可查列、过滤列、排序列中一并屏蔽) */ |
| | | private static final java.util.Set<String> BLOCKED_COLUMNS = new java.util.TreeSet<>(Arrays.asList( |
| | | "unified_credit_code", "contact_person", "contact_phone", |
| | | "unit_leader", "stats_leader", "report_unit", "enterprise_code")); |
| | | |
| | | /** 自由文本里残留的手机号 */ |
| | | private static final Pattern PHONE = Pattern.compile("(?<!\\d)1[3-9]\\d{9}(?!\\d)"); |
| | | |
| | | public static final Set<String> ALLOWED_TABLES = new TreeSet<>(); |
| | | |
| | | private static final Map<String, TableMeta> TABLES = new LinkedHashMap<>(); |
| | |
| | | for (TableMeta meta : TABLES.values()) { |
| | | sb.append(meta.table).append("【").append(meta.desc).append("】: "); |
| | | List<String> parts = new ArrayList<>(); |
| | | for (String c : meta.cols) { |
| | | for (String c : selectable(meta)) { |
| | | parts.add(snakeToCamel(c) + "(" + meta.labels.getOrDefault(c, snakeToCamel(c)) + ")"); |
| | | } |
| | | sb.append(String.join(", ", parts)).append("\n"); |
| | |
| | | sql.append(" LIMIT ").append(lim); |
| | | |
| | | List<Map<String, Object>> rows = jdbcTemplate.queryForList(sql.toString(), w.args.toArray()); |
| | | maskRows(rows); |
| | | out.set("success", true); |
| | | out.set("rows", rows); |
| | | out.set("count", rows.size()); |
| | |
| | | if (columnsParam == null) { |
| | | return aliased(meta.defaults); |
| | | } |
| | | // 传入 "*" 时只返回默认列,不再放开整表(敏感字段不参与查询) |
| | | if (columnsParam instanceof String && "*".equals(columnsParam)) { |
| | | return aliased(meta.cols); |
| | | return aliased(meta.defaults); |
| | | } |
| | | if (columnsParam instanceof List) { |
| | | List<String> out = new ArrayList<>(); |
| | | List<String> usable = selectable(meta); |
| | | for (Object o : (List<?>) columnsParam) { |
| | | String c = normalizeCol(String.valueOf(o)); |
| | | if (!meta.cols.contains(c)) { |
| | | throw new IllegalArgumentException("列名不合法: " + o + ",该表可用列: " + camelList(meta.cols)); |
| | | if (BLOCKED_COLUMNS.contains(c)) { |
| | | throw new IllegalArgumentException("该字段不允许查询: " + o); |
| | | } |
| | | if (!usable.contains(c)) { |
| | | throw new IllegalArgumentException("列名不合法: " + o + ",该表可用列: " + camelList(usable)); |
| | | } |
| | | out.add(c); |
| | | } |
| | | return aliased(out.isEmpty() ? meta.defaults : out); |
| | | } |
| | | return aliased(meta.defaults); |
| | | } |
| | | |
| | | /** 外发前脱敏:企业名称替换为别名,自由文本中的企业名称与手机号一并处理 */ |
| | | private void maskRows(List<Map<String, Object>> rows) { |
| | | if (rows == null) { |
| | | return; |
| | | } |
| | | for (Map<String, Object> row : rows) { |
| | | Object nameObj = row.get("enterpriseName"); |
| | | String realName = nameObj == null ? null : String.valueOf(nameObj); |
| | | String alias = null; |
| | | if (realName != null && !realName.isEmpty()) { |
| | | alias = desensitizeService.maskEnterprise(realName); |
| | | row.put("enterpriseName", alias); |
| | | } |
| | | for (Map.Entry<String, Object> e : row.entrySet()) { |
| | | Object v = e.getValue(); |
| | | if (!(v instanceof String)) { |
| | | continue; |
| | | } |
| | | String s = (String) v; |
| | | String outText = s; |
| | | if (realName != null && !realName.isEmpty() && outText.contains(realName)) { |
| | | outText = outText.replace(realName, alias); |
| | | } |
| | | outText = PHONE.matcher(outText).replaceAll("手机号已脱敏"); |
| | | if (!outText.equals(s)) { |
| | | e.setValue(outText); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** 该表可查字段 = 全部字段 - 敏感字段 */ |
| | | private static List<String> selectable(TableMeta meta) { |
| | | List<String> out = new ArrayList<>(); |
| | | for (String c : meta.cols) { |
| | | if (!BLOCKED_COLUMNS.contains(c)) { |
| | | out.add(c); |
| | | } |
| | | } |
| | | return out; |
| | | } |
| | | |
| | | private List<String> aliased(List<String> cols) { |
| | |
| | | } |
| | | for (Map.Entry<String, Object> e : filters.entrySet()) { |
| | | String col = normalizeCol(String.valueOf(e.getKey())); |
| | | if (!meta.cols.contains(col)) { |
| | | throw new IllegalArgumentException("列名不合法: " + e.getKey() + ",该表可用列: " + camelList(meta.cols)); |
| | | List<String> usable = selectable(meta); |
| | | if (BLOCKED_COLUMNS.contains(col) || !usable.contains(col)) { |
| | | throw new IllegalArgumentException("列名不合法: " + e.getKey() + ",该表可用列: " + camelList(usable)); |
| | | } |
| | | Object v = e.getValue(); |
| | | if (v instanceof List) { |
| | |
| | | s = s.substring(0, s.length() - 4).trim(); |
| | | } |
| | | String col = normalizeCol(s); |
| | | if (!meta.cols.contains(col)) { |
| | | if (BLOCKED_COLUMNS.contains(col) || !selectable(meta).contains(col)) { |
| | | throw new IllegalArgumentException("排序列不合法: " + orderBy); |
| | | } |
| | | return col + (desc ? " DESC" : " ASC"); |