1、数据库PG切换为达梦的修改版本。

This commit is contained in:
2026-05-28 14:58:00 +08:00
parent a360895292
commit a168f74653
119 changed files with 2758 additions and 2090 deletions
@@ -1,47 +1,86 @@
package com.Modules.etl.handler;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;
import java.sql.*;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Base64;
import java.util.List;
/**
* 字节数组类型处理器 - 达梦数据库兼容版本(JSON格式)
*
* 将 Java byte[][] 与数据库 VARCHAR 列进行互转。
* 存储格式: JSON 数组,每个元素为 Base64 编码字符串,如 ["YWJj","ZGVm"]
* 空数组: 存储为 "[]"
* null值: 存储为 NULL
*
* 原 PostgreSQL 版本使用 createArrayOf("bytea", ...) 创建原生 bytea 数组,
* 达梦数据库不兼容此 API,改为 VARCHAR + JSON + Base64 存储。
*/
@MappedTypes(byte[][].class)
@MappedJdbcTypes(JdbcType.ARRAY)
@MappedJdbcTypes(JdbcType.VARCHAR)
public class ArrayByteTypeHandler extends BaseTypeHandler<byte[][]> {
private static final ObjectMapper MAPPER = new ObjectMapper();
@Override
public void setNonNullParameter(PreparedStatement ps, int i, byte[][] parameter, JdbcType jdbcType) throws SQLException {
Array array = ps.getConnection().createArrayOf("bytea", parameter);
ps.setArray(i, array);
try {
String[] encoded = new String[parameter.length];
for (int j = 0; j < parameter.length; j++) {
encoded[j] = Base64.getEncoder().encodeToString(parameter[j]);
}
ps.setString(i, MAPPER.writeValueAsString(encoded));
} catch (JsonProcessingException e) {
throw new SQLException("Failed to serialize byte[][] to JSON", e);
}
}
@Override
public byte[][] getNullableResult(ResultSet rs, String columnName) throws SQLException {
return getArray(rs.getArray(columnName));
return parseArray(rs.getString(columnName));
}
@Override
public byte[][] getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return getArray(rs.getArray(columnIndex));
return parseArray(rs.getString(columnIndex));
}
@Override
public byte[][] getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return getArray(cs.getArray(columnIndex));
return parseArray(cs.getString(columnIndex));
}
private byte[][] getArray(Array array) throws SQLException {
if (array != null) {
Object[] objArray = (Object[]) array.getArray();
byte[][] result = new byte[objArray.length][];
for (int i = 0; i < objArray.length; i++) {
result[i] = (byte[]) objArray[i];
private byte[][] parseArray(String value) {
if (value == null || value.isEmpty()) {
return new byte[0][];
}
try {
// JSON 格式: ["abc","def"]
List<String> list = MAPPER.readValue(value, MAPPER.getTypeFactory().constructCollectionType(List.class, String.class));
byte[][] result = new byte[list.size()][];
for (int i = 0; i < list.size(); i++) {
String s = list.get(i);
result[i] = (s == null || s.isEmpty()) ? new byte[0] : Base64.getDecoder().decode(s);
}
return result;
} catch (JsonProcessingException e) {
// 兼容旧的逗号分隔格式
String[] parts = value.split(",", -1);
byte[][] result = new byte[parts.length][];
for (int i = 0; i < parts.length; i++) {
String part = parts[i].trim();
result[i] = part.isEmpty() ? new byte[0] : Base64.getDecoder().decode(part);
}
return result;
}
return null;
}
}
}
@@ -1,43 +1,73 @@
package com.Modules.etl.handler;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;
import java.sql.*;
import java.util.Arrays;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* 整型数组类型处理器 - 达梦数据库兼容版本(JSON格式)
*
* 将 Java Integer[] 与数据库 VARCHAR 列进行互转。
* 存储格式: JSON 数组,如 [1,2,3]
* 空数组: 存储为 "[]"
* null值: 存储为 NULL
*
* 原 PostgreSQL 版本使用 createArrayOf("integer", ...) 创建原生数组,
* 达梦数据库不兼容此 API,改为 VARCHAR + JSON 存储。
*/
@MappedTypes(Integer[].class)
@MappedJdbcTypes(JdbcType.ARRAY)
@MappedJdbcTypes(JdbcType.VARCHAR)
public class ArrayIntegerTypeHandler extends BaseTypeHandler<Integer[]> {
private static final ObjectMapper MAPPER = new ObjectMapper();
@Override
public void setNonNullParameter(PreparedStatement ps, int i, Integer[] parameter, JdbcType jdbcType) throws SQLException {
Array array = ps.getConnection().createArrayOf("integer", parameter);
ps.setArray(i, array);
try {
ps.setString(i, MAPPER.writeValueAsString(parameter));
} catch (JsonProcessingException e) {
throw new SQLException("Failed to serialize Integer[] to JSON", e);
}
}
@Override
public Integer[] getNullableResult(ResultSet rs, String columnName) throws SQLException {
return getArray(rs.getArray(columnName));
return parseArray(rs.getString(columnName));
}
@Override
public Integer[] getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return getArray(rs.getArray(columnIndex));
return parseArray(rs.getString(columnIndex));
}
@Override
public Integer[] getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return getArray(cs.getArray(columnIndex));
return parseArray(cs.getString(columnIndex));
}
private Integer[] getArray(Array array) throws SQLException {
if (array != null) {
Object[] objArray = (Object[]) array.getArray();
return Arrays.copyOf(objArray, objArray.length, Integer[].class);
private Integer[] parseArray(String value) {
if (value == null || value.isEmpty()) {
return new Integer[0];
}
try {
return MAPPER.readValue(value, Integer[].class);
} catch (JsonProcessingException e) {
// 兼容旧的逗号分隔格式
String[] parts = value.split(",", -1);
Integer[] result = new Integer[parts.length];
for (int i = 0; i < parts.length; i++) {
String part = parts[i].trim();
result[i] = part.isEmpty() ? null : Integer.parseInt(part);
}
return result;
}
return null;
}
}
}
@@ -1,43 +1,67 @@
package com.Modules.etl.handler;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;
import java.sql.*;
import java.util.Arrays;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* 字符串数组类型处理器 - 达梦数据库兼容版本(JSON格式)
*
* 将 Java String[] 与数据库 VARCHAR 列进行互转。
* 存储格式: JSON 数组,如 ["value1","value2","value3"]
* 空数组: 存储为 "[]"
* null值: 存储为 NULL
*
* 原 PostgreSQL 版本使用 createArrayOf("text", ...) 创建原生数组,
* 达梦数据库不兼容此 API,改为 VARCHAR + JSON 存储。
*/
@MappedTypes(String[].class)
@MappedJdbcTypes(JdbcType.ARRAY)
@MappedJdbcTypes(JdbcType.VARCHAR)
public class ArrayStringTypeHandler extends BaseTypeHandler<String[]> {
private static final ObjectMapper MAPPER = new ObjectMapper();
@Override
public void setNonNullParameter(PreparedStatement ps, int i, String[] parameter, JdbcType jdbcType) throws SQLException {
Array array = ps.getConnection().createArrayOf("text", parameter);
ps.setArray(i, array);
try {
ps.setString(i, MAPPER.writeValueAsString(parameter));
} catch (JsonProcessingException e) {
throw new SQLException("Failed to serialize String[] to JSON", e);
}
}
@Override
public String[] getNullableResult(ResultSet rs, String columnName) throws SQLException {
return getArray(rs.getArray(columnName));
return parseArray(rs.getString(columnName));
}
@Override
public String[] getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return getArray(rs.getArray(columnIndex));
return parseArray(rs.getString(columnIndex));
}
@Override
public String[] getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return getArray(cs.getArray(columnIndex));
return parseArray(cs.getString(columnIndex));
}
private String[] getArray(Array array) throws SQLException {
if (array != null) {
Object[] objArray = (Object[]) array.getArray();
return Arrays.copyOf(objArray, objArray.length, String[].class);
private String[] parseArray(String value) {
if (value == null || value.isEmpty()) {
return new String[0];
}
try {
return MAPPER.readValue(value, String[].class);
} catch (JsonProcessingException e) {
// 兼容旧的逗号分隔格式
return value.split(",", -1);
}
return null;
}
}
}
@@ -1,6 +1,6 @@
package com.common.entity;
import java.time.OffsetDateTime;
import java.time.LocalDateTime;
/**
* 联动设备表实体类(防火墙设备信息)
@@ -17,9 +17,9 @@ public class DeviceInterlocking {
private String tenantId;
private Long createDept;
private Long createBy;
private OffsetDateTime createTime;
private LocalDateTime createTime;
private Long updateBy;
private OffsetDateTime updateTime;
private LocalDateTime updateTime;
private String remark;
private String authUsername; // 用户名
private String authPassword; // 密码
@@ -55,14 +55,14 @@ public class DeviceInterlocking {
public Long getCreateBy() { return createBy; }
public void setCreateBy(Long createBy) { this.createBy = createBy; }
public OffsetDateTime getCreateTime() { return createTime; }
public void setCreateTime(OffsetDateTime createTime) { this.createTime = createTime; }
public LocalDateTime getCreateTime() { return createTime; }
public void setCreateTime(LocalDateTime createTime) { this.createTime = createTime; }
public Long getUpdateBy() { return updateBy; }
public void setUpdateBy(Long updateBy) { this.updateBy = updateBy; }
public OffsetDateTime getUpdateTime() { return updateTime; }
public void setUpdateTime(OffsetDateTime updateTime) { this.updateTime = updateTime; }
public LocalDateTime getUpdateTime() { return updateTime; }
public void setUpdateTime(LocalDateTime updateTime) { this.updateTime = updateTime; }
public String getRemark() { return remark; }
public void setRemark(String remark) { this.remark = remark; }
@@ -4,9 +4,8 @@ import com.Modules.etl.handler.ArrayIntegerTypeHandler;
import com.Modules.etl.handler.ArrayStringTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;
import org.apache.ibatis.type.ArrayTypeHandler;
import java.time.OffsetDateTime;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
@@ -25,8 +24,8 @@ public class DeviceInterlockingCmd {
private String banType; // 封禁类型(1:白名单、0:黑名单)
private String cmdStatus; // 指令状态(0:未执行、1:已完成、2:执行中)
private Integer banDuration; // 封禁时长(秒,-1表示永久)
private OffsetDateTime createTime;
private OffsetDateTime updateTime;
private LocalDateTime createTime;
private LocalDateTime updateTime;
private String tenantId;
private Long createDept;
private Long createBy;
@@ -65,11 +64,11 @@ public class DeviceInterlockingCmd {
public Integer getBanDuration() { return banDuration; }
public void setBanDuration(Integer banDuration) { this.banDuration = banDuration; }
public OffsetDateTime getCreateTime() { return createTime; }
public void setCreateTime(OffsetDateTime createTime) { this.createTime = createTime; }
public LocalDateTime getCreateTime() { return createTime; }
public void setCreateTime(LocalDateTime createTime) { this.createTime = createTime; }
public OffsetDateTime getUpdateTime() { return updateTime; }
public void setUpdateTime(OffsetDateTime updateTime) { this.updateTime = updateTime; }
public LocalDateTime getUpdateTime() { return updateTime; }
public void setUpdateTime(LocalDateTime updateTime) { this.updateTime = updateTime; }
public String getTenantId() { return tenantId; }
public void setTenantId(String tenantId) { this.tenantId = tenantId; }
@@ -1,6 +1,6 @@
package com.common.entity;
import java.time.OffsetDateTime;
import java.time.LocalDateTime;
/**
* 封禁记录表实体类
@@ -12,15 +12,15 @@ public class DeviceInterlockingLog {
private Long deviceInterlockingId; // 封禁设备ID
private String banIp; // 封禁IP地址
private String deviceName; // 封禁设备名称
private OffsetDateTime banTime; // 封禁时间
private LocalDateTime banTime; // 封禁时间
private String banMethod; // 封禁方式(0.人工、1.自动化封禁)
private Integer banResult; // 联动结果(成功:1、失败:0
private String tenantId;
private Long createDept;
private Long createBy;
private OffsetDateTime createTime;
private LocalDateTime createTime;
private Long updateBy;
private OffsetDateTime updateTime;
private LocalDateTime updateTime;
private String remark;
private String respBody; // 响应body
private String reqBody; // 请求body
@@ -41,8 +41,8 @@ public class DeviceInterlockingLog {
public String getDeviceName() { return deviceName; }
public void setDeviceName(String deviceName) { this.deviceName = deviceName; }
public OffsetDateTime getBanTime() { return banTime; }
public void setBanTime(OffsetDateTime banTime) { this.banTime = banTime; }
public LocalDateTime getBanTime() { return banTime; }
public void setBanTime(LocalDateTime banTime) { this.banTime = banTime; }
public String getBanMethod() { return banMethod; }
public void setBanMethod(String banMethod) { this.banMethod = banMethod; }
@@ -59,14 +59,14 @@ public class DeviceInterlockingLog {
public Long getCreateBy() { return createBy; }
public void setCreateBy(Long createBy) { this.createBy = createBy; }
public OffsetDateTime getCreateTime() { return createTime; }
public void setCreateTime(OffsetDateTime createTime) { this.createTime = createTime; }
public LocalDateTime getCreateTime() { return createTime; }
public void setCreateTime(LocalDateTime createTime) { this.createTime = createTime; }
public Long getUpdateBy() { return updateBy; }
public void setUpdateBy(Long updateBy) { this.updateBy = updateBy; }
public OffsetDateTime getUpdateTime() { return updateTime; }
public void setUpdateTime(OffsetDateTime updateTime) { this.updateTime = updateTime; }
public LocalDateTime getUpdateTime() { return updateTime; }
public void setUpdateTime(LocalDateTime updateTime) { this.updateTime = updateTime; }
public String getRemark() { return remark; }
public void setRemark(String remark) { this.remark = remark; }
@@ -15,7 +15,7 @@ public interface AlarmMapper {
"INSERT INTO alarm (",
"id, created_at, alarm_name, alarm_level, alarm_type, ",
"alarm_major_type, alarm_minor_type,alarm_area_id, attack_ip, victim_ip, victim_web_url, ",
"device_id, comment,origin_log_ids,log_start_at, log_end_at, window_time, http_status, ",
"device_id, \"comment\",origin_log_ids,log_start_at, log_end_at, window_time, http_status, ",
"attack_port, victim_port, attack_method, etl_time, log_count, ",
"attack_chain_phase, disposition_advice, attack_direction, ",
"judged_state, disposed_state, attack_result, fall, payload, dns_info, engine_type, " ,
@@ -52,7 +52,7 @@ public interface AlarmMapper {
@Insert("INSERT INTO alarm (" +
"id, created_at, alarm_name, alarm_level, alarm_type, " +
"alarm_major_type, alarm_minor_type,alarm_area_id, attack_ip, victim_ip, victim_web_url, " +
"device_id, comment,origin_log_ids, log_start_at, log_end_at, window_time, http_status, " +
"device_id, \"comment\",origin_log_ids, log_start_at, log_end_at, window_time, http_status, " +
"attack_port, victim_port, attack_method, etl_time, log_count, " +
"attack_chain_phase, disposition_advice, attack_direction, " +
"judged_state, disposed_state, attack_result, fall, payload, dns_info, engine_type, " +
@@ -19,7 +19,7 @@ public interface AlarmVisitMapper {
"INSERT INTO alarm_visit (",
"id, created_at, alarm_name, alarm_level, alarm_type, ",
"alarm_major_type, alarm_minor_type,alarm_area_id, attack_ip, victim_ip, victim_web_url, ",
"device_id, comment,origin_log_ids,log_start_at, log_end_at,window_time, http_status, ",
"device_id, \"comment\",origin_log_ids,log_start_at, log_end_at,window_time, http_status, ",
"attack_port, victim_port, attack_method, etl_time, log_count, ",
"attack_chain_phase, disposition_advice, attack_direction, ",
"judged_state, disposed_state, attack_result, fall, payload, dns_info, engine_type, " ,
@@ -56,7 +56,7 @@ public interface AlarmVisitMapper {
@Insert("INSERT INTO alarm_visit (" +
"id, created_at, alarm_name, alarm_level, alarm_type, " +
"alarm_major_type, alarm_minor_type,alarm_area_id, attack_ip, victim_ip, victim_web_url, " +
"device_id, comment,origin_log_ids, log_start_at, log_end_at, window_time,http_status, " +
"device_id, \"comment\",origin_log_ids, log_start_at, log_end_at, window_time,http_status, " +
"attack_port, victim_port, attack_method, etl_time, log_count, " +
"attack_chain_phase, disposition_advice, attack_direction, " +
"judged_state, disposed_state, attack_result, fall, payload, dns_info,engine_type, " +
@@ -37,22 +37,37 @@ public interface DeviceCollectHeartbeatMapper {
/**
* 插入或更新(根据collect_id
* 达梦数据库使用 MERGE INTO 实现 upsert
*/
@Insert("INSERT INTO device_collect_heartbeat (" +
@Update("MERGE INTO device_collect_heartbeat t " +
"USING (SELECT " +
"#{collectId} AS collect_id, " +
"#{collectName} AS collect_name, " +
"#{deviceIp} AS device_ip, " +
"#{appVersion} AS app_version, " +
"#{lastHeartbeat} AS last_heartbeat, " +
"#{heartbeatCount} AS heartbeat_count, " +
"#{status} AS status, " +
"#{failCount} AS fail_count, " +
"#{updateTime} AS update_time " +
"FROM DUAL) s " +
"ON (t.collect_id = s.collect_id) " +
"WHEN MATCHED THEN UPDATE SET " +
"t.collect_name = s.collect_name, " +
"t.device_ip = s.device_ip, " +
"t.app_version = s.app_version, " +
"t.last_heartbeat = s.last_heartbeat, " +
"t.heartbeat_count = s.heartbeat_count, " +
"t.status = s.status, " +
"t.fail_count = s.fail_count, " +
"t.update_time = s.update_time " +
"WHEN NOT MATCHED THEN INSERT (" +
"collect_id, collect_name, device_ip, app_version, last_heartbeat, " +
"heartbeat_count, status, fail_count, update_time " +
"heartbeat_count, status, fail_count, update_time" +
") VALUES (" +
"#{collectId}, #{collectName}, #{deviceIp}, #{appVersion}, #{lastHeartbeat}, " +
"#{heartbeatCount}, #{status}, #{failCount}, #{updateTime} " +
") ON CONFLICT (collect_id) DO UPDATE SET " +
"collect_name = EXCLUDED.collect_name, " +
"device_ip = EXCLUDED.device_ip, " +
"app_version = EXCLUDED.app_version, " +
"last_heartbeat = EXCLUDED.last_heartbeat, " +
"heartbeat_count = EXCLUDED.heartbeat_count, " +
"status = EXCLUDED.status, " +
"fail_count = EXCLUDED.fail_count, " +
"update_time = EXCLUDED.update_time")
"s.collect_id, s.collect_name, s.device_ip, s.app_version, s.last_heartbeat, " +
"s.heartbeat_count, s.status, s.fail_count, s.update_time" +
")")
int upsert(DeviceCollectHeartbeat heartbeat);
/**
@@ -155,12 +155,12 @@ public interface DeviceCollectTaskMapper extends BaseMapper<DeviceCollectTask>{
"<foreach collection='tasks' item='task' separator=';'>" +
"UPDATE device_collect_task SET " +
" first_time = CASE " +
" WHEN first_time IS NULL AND #{task.firstTime}::TIMESTAMP IS NOT NULL THEN #{task.firstTime}::TIMESTAMP " +
" WHEN first_time IS NULL AND #{task.firstTime} IS NOT NULL THEN #{task.firstTime} " +
" ELSE first_time " +
" END, " +
" last_success_time = #{task.lastSuccessTime}::TIMESTAMP, " +
" last_failed_time = #{task.lastFailedTime}::TIMESTAMP, " +
" updated_at = #{task.updatedAt}::TIMESTAMP " +
" last_success_time = #{task.lastSuccessTime}, " +
" last_failed_time = #{task.lastFailedTime}, " +
" updated_at = #{task.updatedAt} " +
"WHERE id = #{task.id}" +
"</foreach>" +
"</script>")
@@ -171,12 +171,12 @@ public interface DeviceCollectTaskMapper extends BaseMapper<DeviceCollectTask>{
*/
@Update("UPDATE device_collect_task " +
"SET first_time = CASE " +
" WHEN first_time IS NULL AND #{firstTime}::TIMESTAMP IS NOT NULL THEN #{firstTime}::TIMESTAMP " +
" WHEN first_time IS NULL AND #{firstTime} IS NOT NULL THEN #{firstTime} " +
" ELSE first_time " +
" END, " +
" last_success_time = #{lastSuccessTime}::TIMESTAMP, " +
" last_failed_time = #{lastFailTime}::TIMESTAMP, " +
" updated_at = #{updateTime}::TIMESTAMP " +
" last_success_time = #{lastSuccessTime}, " +
" last_failed_time = #{lastFailTime}, " +
" updated_at = #{updateTime} " +
"WHERE id = #{deviceCollectId}")
int updateTaskTime(@Param("deviceCollectId") String deviceCollectId,
@Param("firstTime") LocalDateTime firstTime,
@@ -61,8 +61,11 @@ public interface DeviceInterlockingCmdMapper {
@Insert("INSERT INTO device_interlocking_cmd (probe_id, probe_ip, device_interlocking_id, device_interlocking_ip, " +
"ban_ips, ban_method, ban_type, cmd_status, ban_duration, create_time, update_time, " +
"tenant_id, create_dept, create_by, remark, ban_operation_type) " +
"VALUES (#{probeId}, #{probeIp}, ARRAY[:ids], ARRAY[:ips], ARRAY[:banIps], " +
"#{banMethod}, #{banType}, #{cmdStatus}, #{banDuration}, NOW(), NOW(), " +
"VALUES (#{probeId}, #{probeIp}, " +
"#{deviceInterlockingId, typeHandler=com.Modules.etl.handler.ArrayIntegerTypeHandler}, " +
"#{deviceInterlockingIp, typeHandler=com.Modules.etl.handler.ArrayStringTypeHandler}, " +
"#{banIps, typeHandler=com.Modules.etl.handler.ArrayStringTypeHandler}, " +
"#{banMethod}, #{banType}, #{cmdStatus}, #{banDuration}, SYSDATE, SYSDATE, " +
"#{tenantId}, #{createDept}, #{createBy}, #{remark}, #{banOperationType})")
@Options(useGeneratedKeys = true, keyProperty = "id")
int insert(DeviceInterlockingCmd cmd);
@@ -73,25 +76,25 @@ public interface DeviceInterlockingCmdMapper {
* @param cmdStatus 新状态
* @return 影响行数
*/
@Update("UPDATE device_interlocking_cmd SET cmd_status = #{cmdStatus}, update_time = NOW() WHERE id = #{id}")
@Update("UPDATE device_interlocking_cmd SET cmd_status = #{cmdStatus}, update_time = SYSDATE WHERE id = #{id}")
int updateStatus(@Param("id") Long id, @Param("cmdStatus") String cmdStatus);
/**
* 更新指令状态为执行中
*/
@Update("UPDATE device_interlocking_cmd SET cmd_status = '2', update_time = NOW() WHERE id = #{id}")
@Update("UPDATE device_interlocking_cmd SET cmd_status = '2', update_time = SYSDATE WHERE id = #{id}")
int updateStatusToExecuting(@Param("id") Long id);
/**
* 更新指令状态为执行完成
*/
@Update("UPDATE device_interlocking_cmd SET cmd_status = '1', update_time = NOW() WHERE id = #{id}")
@Update("UPDATE device_interlocking_cmd SET cmd_status = '1', update_time = SYSDATE WHERE id = #{id}")
int updateStatusToCompleted(@Param("id") Long id);
/**
* 更新指令状态为执行失败
*/
@Update("UPDATE device_interlocking_cmd SET cmd_status = '3', update_time = NOW() WHERE id = #{id}")
@Update("UPDATE device_interlocking_cmd SET cmd_status = '3', update_time = SYSDATE WHERE id = #{id}")
int updateStatusToFailed(@Param("id") Long id);
/**
@@ -84,7 +84,7 @@ public interface DeviceReceiveLogMapper {
*/
@Select("SELECT device_collect_id, MAX(created_at) AS last_success_time " +
"FROM device_receive_log " +
"WHERE push_success = true " +
"WHERE push_success = 1 " +
"AND created_at >= CURRENT_DATE " +
"GROUP BY device_collect_id")
List<DeviceCollectTaskTime> selectDailySuccessTimes();
@@ -94,7 +94,7 @@ public interface DeviceReceiveLogMapper {
*/
@Select("SELECT device_collect_id, MAX(created_at) AS last_fail_time " +
"FROM device_receive_log " +
"WHERE push_success = false " +
"WHERE push_success = 0 " +
"AND created_at >= CURRENT_DATE " +
"GROUP BY device_collect_id")
List<DeviceCollectTaskTime> selectDailyFailTimes();
@@ -104,7 +104,7 @@ public interface DeviceReceiveLogMapper {
*/
@Select("SELECT device_collect_id, MIN(created_at) AS first_success_time " +
"FROM device_receive_log " +
"WHERE push_success = true " +
"WHERE push_success = 1 " +
"GROUP BY device_collect_id")
List<DeviceCollectTaskTime> selectFirstSuccessTimes();
@@ -114,7 +114,7 @@ public interface DeviceReceiveLogMapper {
@Select("SELECT device_collect_id, MIN(created_at) AS first_time, " +
"MAX(created_at) AS last_success_time " +
"FROM device_receive_log " +
"WHERE push_success = true " +
"WHERE push_success = 1 " +
"AND created_at >= #{startTime} " +
"AND created_at < #{endTime} " +
"GROUP BY device_collect_id")
@@ -49,30 +49,29 @@ public interface SyslogNormalAlarmMapper {
*/
@Select("SELECT " +
"to_char(log_time, 'YYYYMMDD') as log_date, " +
"ARRAY_AGG(DISTINCT host(src_ip)::text) as attack_ips, " +
"WM_CONCAT(DISTINCT src_ip) as attack_ips, " +
"origin_event_name, " +
"MAX(attack_result) as attack_result, " +
"MIN(log_time) as min_log_time, " +
"MAX(log_time) as max_log_time, " +
"COUNT(1) as log_count, " +
"ARRAY_AGG(DISTINCT host(dest_ip)::text) as victim_ips, " +
"ARRAY_AGG(DISTINCT http_url) as victim_web_urls, " +
"ARRAY_AGG(DISTINCT device_id) as device_ids, " +
"ARRAY_AGG(DISTINCT id) as origin_log_ids, " +
"WM_CONCAT(DISTINCT dest_ip) as victim_ips, " +
"WM_CONCAT(DISTINCT http_url) as victim_web_urls, " +
"WM_CONCAT(DISTINCT device_id) as device_ids, " +
"WM_CONCAT(DISTINCT id) as origin_log_ids, " +
"MAX(event_level) as max_event_level, " +
"MIN(origin_event_type) AS first_event_type, " +
"MAX(origin_event_type) as event_type, " +
"MIN(event_type) as min_event_type, " +
"ARRAY_AGG(DISTINCT src_port::int4) as attack_ports, " +
"ARRAY_AGG(DISTINCT dest_port::int4) as victim_ports, " +
"ARRAY_AGG(DISTINCT http_resp_codes::text) as http_status_codes, " +
"ARRAY_AGG(DISTINCT payload::BYTEA) as payload_samples, " +
"ARRAY_AGG(DISTINCT http_req_header) as httpReqHeaders, " +
"ARRAY_AGG(DISTINCT http_req_body) as httpReqBodys, " +
"ARRAY_AGG(DISTINCT http_resp_header) as httpRespHeaders, " +
"ARRAY_AGG(DISTINCT http_resp_body) as httpRespBodys, " +
"MODE() WITHIN GROUP (ORDER BY dest_domain) as dns_info, " +
"STRING_AGG(DISTINCT COALESCE(host(dest_ip)::text, ''), ',') as victim_ips_str " +
"WM_CONCAT(DISTINCT src_port) as attack_ports, " +
"WM_CONCAT(DISTINCT dest_port) as victim_ports, " +
"WM_CONCAT(DISTINCT http_resp_codes) as http_status_codes, " +
"WM_CONCAT(DISTINCT payload) as payload_samples, " +
"WM_CONCAT(DISTINCT http_req_header) as httpReqHeaders, " +
"WM_CONCAT(DISTINCT http_req_body) as httpReqBodys, " +
"WM_CONCAT(DISTINCT http_resp_header) as httpRespHeaders, " +
"WM_CONCAT(DISTINCT http_resp_body) as httpRespBodys, " +
"LISTAGG(DISTINCT COALESCE(dest_ip, ''), ',') as victim_ips_str " +
"FROM syslog_normal_alarm " +
"WHERE log_time >= #{startTime} AND log_time < #{endTime} " +
"AND event_level >= 1 AND src_ip NOT IN ('127.0.0.1', '127.0.0.2') " +
@@ -46,30 +46,29 @@ public interface SyslogNormalDataMapper {
*/
@Select("SELECT " +
"to_char(log_time, 'YYYYMMDD') as log_date, " +
"ARRAY_AGG(DISTINCT host(src_ip)::text) as attack_ips, " +
"WM_CONCAT(DISTINCT src_ip) as attack_ips, " +
"origin_event_name, " +
"MAX(attack_result) as attack_result, " +
"MIN(log_time) as min_log_time, " +
"MAX(log_time) as max_log_time, " +
"COUNT(1) as log_count, " +
"ARRAY_AGG(DISTINCT host(dest_ip)::text) as victim_ips, " +
"ARRAY_AGG(DISTINCT http_url) as victim_web_urls, " +
"ARRAY_AGG(DISTINCT device_id) as device_ids, " +
"ARRAY_AGG(DISTINCT id) as origin_log_ids, " +
"WM_CONCAT(DISTINCT dest_ip) as victim_ips, " +
"WM_CONCAT(DISTINCT http_url) as victim_web_urls, " +
"WM_CONCAT(DISTINCT device_id) as device_ids, " +
"WM_CONCAT(DISTINCT id) as origin_log_ids, " +
"MAX(event_level) as max_event_level, " +
"MIN(origin_event_type) AS first_event_type, " +
"MAX(origin_event_type) as event_type, " +
"MIN(event_type) as min_event_type, " +
"ARRAY_AGG(DISTINCT src_port::int4) as attack_ports, " +
"ARRAY_AGG(DISTINCT dest_port::int4) as victim_ports, " +
"ARRAY_AGG(DISTINCT http_resp_codes::text) as http_status_codes, " +
"ARRAY_AGG(DISTINCT payload::BYTEA) as payload_samples, " +
"ARRAY_AGG(DISTINCT http_req_header) as httpReqHeaders, " +
"ARRAY_AGG(DISTINCT http_req_body) as httpReqBodys, " +
"ARRAY_AGG(DISTINCT http_resp_header) as httpRespHeaders, " +
"ARRAY_AGG(DISTINCT http_resp_body) as httpRespBodys, " +
"MODE() WITHIN GROUP (ORDER BY dest_domain) as dns_info, " +
"STRING_AGG(DISTINCT COALESCE(host(dest_ip)::text, ''), ',') as victim_ips_str " +
"WM_CONCAT(DISTINCT src_port) as attack_ports, " +
"WM_CONCAT(DISTINCT dest_port) as victim_ports, " +
"WM_CONCAT(DISTINCT http_resp_codes) as http_status_codes, " +
"WM_CONCAT(DISTINCT payload) as payload_samples, " +
"WM_CONCAT(DISTINCT http_req_header) as httpReqHeaders, " +
"WM_CONCAT(DISTINCT http_req_body) as httpReqBodys, " +
"WM_CONCAT(DISTINCT http_resp_header) as httpRespHeaders, " +
"WM_CONCAT(DISTINCT http_resp_body) as httpRespBodys, " +
"LISTAGG(DISTINCT COALESCE(dest_ip, ''), ',') as victim_ips_str " +
"FROM syslog_normal_data " +
"WHERE log_time >= #{startTime} AND log_time < #{endTime} " +
"AND http_resp_codes =200 and origin_event_type <> '' and origin_event_name='访问日志' AND src_ip NOT IN ('127.0.0.1', '127.0.0.2') " +
@@ -18,12 +18,12 @@ public interface WecomNotificationMapper {
"wecom_notification_time, tenant_id, create_dept, create_by, create_time, " +
"update_by, update_time, remark, wecom_notification_status" +
") VALUES (" +
"nextval('seq_wecom_notification'), #{userId}, #{wecomNotificationName}, #{wecomNotificationIp}, " +
"seq_wecom_notification.NEXTVAL, #{userId}, #{wecomNotificationName}, #{wecomNotificationIp}, " +
"#{wecomNotificationType}, #{wecomNotificationLevel}, #{wecomNotificationContent}, " +
"#{wecomNotificationTime}, #{tenantId}, #{createDept}, #{createBy}, #{createTime}, " +
"#{updateBy}, #{updateTime}, #{remark}, #{wecomNotificationStatus}" +
")")
@SelectKey(statement = "SELECT currval('seq_wecom_notification')", keyProperty = "wecomNotificationId", resultType = Long.class, before = false)
@SelectKey(statement = "SELECT seq_wecom_notification.currval", keyProperty = "wecomNotificationId", resultType = Long.class, before = false)
int insert(WecomNotification notification);
/**
@@ -41,7 +41,7 @@ public interface WecomNotificationMapper {
/**
* 更新通知状态
*/
@Update("UPDATE wecom_notification SET wecom_notification_status = #{status}, update_time = NOW() " +
@Update("UPDATE wecom_notification SET wecom_notification_status = #{status}, update_time = SYSDATE " +
"WHERE wecom_notification_id = #{wecomNotificationId}")
int updateStatus(@Param("wecomNotificationId") Long wecomNotificationId, @Param("status") String status);
}
@@ -4,7 +4,7 @@ import com.common.entity.DeviceInterlockingLog;
import com.common.mapper.DeviceInterlockingLogMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.time.OffsetDateTime;
import java.time.LocalDateTime;
import java.util.List;
@Service
@@ -32,7 +32,7 @@ public class DeviceInterlockingLogService {
*/
public int insert(DeviceInterlockingLog log) {
if (log.getBanTime() == null) {
log.setBanTime(OffsetDateTime.now());
log.setBanTime(LocalDateTime.now());
}
return logMapper.insert(log);
}
@@ -44,7 +44,7 @@ public class DeviceInterlockingLogService {
if (logs != null && !logs.isEmpty()) {
for (DeviceInterlockingLog log : logs) {
if (log.getBanTime() == null) {
log.setBanTime(OffsetDateTime.now());
log.setBanTime(LocalDateTime.now());
}
}
}
@@ -77,7 +77,7 @@ public class DmNormalizeRuleService {
List<Map<String, Object>> ruleMap=dmNormalizeRuleMapper.selectByDeviceId(id);
sqlSession.commit();
return ruleMap;
return convertClobToString(ruleMap);
} catch (Exception e) {
logger.error("DmNormalizeRuleService MyBatisUtil getSqlSession 异常", e);
@@ -95,7 +95,41 @@ public class DmNormalizeRuleService {
{
System.out.println("调用selectByDeviceIdAuto 方法,id:"+id);
List<Map<String, Object>> ruleMap=dmNormalizeRuleMapper.selectByDeviceId(id);
return ruleMap;
return convertClobToString(ruleMap);
}
/**
* 将达梦 JDBC CLOB/NCLOB 对象转换为 String,避免缓存序列化报错
* 达梦驱动返回的 TEXT/CLOB 列可能是 dm.jdbc.driver.DmdbNClob 等内部类型,
* toString() 只返回对象引用(如 DmdbNClob@xxx),必须通过 Clob 接口获取实际文本
*/
private List<Map<String, Object>> convertClobToString(List<Map<String, Object>> list) {
if (list == null) return null;
for (Map<String, Object> map : list) {
if (map == null) continue;
for (Map.Entry<String, Object> entry : map.entrySet()) {
Object value = entry.getValue();
if (value != null && value.getClass().getName().startsWith("dm.jdbc.")) {
try {
if (value instanceof java.sql.Clob) {
java.sql.Clob clob = (java.sql.Clob) value;
long length = clob.length();
if (length > 0) {
entry.setValue(clob.getSubString(1, (int) length));
} else {
entry.setValue("");
}
} else {
entry.setValue(value.toString());
}
} catch (Exception e) {
logger.warn("CLOB/NCLOB 转换 String 失败: " + e.getMessage());
entry.setValue(null);
}
}
}
}
return list;
}
}
@@ -734,7 +734,25 @@ public class RealtimeAnalysisEngine implements AnalysisEngine {
return result;
}
// PostgreSQL数组以字符串形式返回,如 "{ip1,ip2,ip3}"
// 达梦 JSONB_AGG 返回 JSON 数组格式,如 "[41614, 8080]"
String str = value.toString();
if (str.startsWith("[") && str.endsWith("]")) {
str = str.substring(1, str.length() - 1).trim();
if (str.isEmpty()) {
return new String[0];
}
// 拆分 JSON 数组元素(兼容带引号和纯数字)
String[] parts = str.split(",");
String[] result = new String[parts.length];
for (int i = 0; i < parts.length; i++) {
String part = parts[i].trim();
if (part.startsWith("\"") && part.endsWith("\"")) {
part = part.substring(1, part.length() - 1);
}
result[i] = part;
}
return result;
}
if (str.startsWith("{") && str.endsWith("}")) {
str = str.substring(1, str.length() - 1);
return str.split(",");
@@ -747,19 +747,19 @@ public class SqlGeneratorServiceImpl implements SqlGeneratorService {
return "AVG(" + columnName + ")";
case "DUPLICATESANDSPLICE":
if (StringUtils.isNotBlank(argsStr)) {
return "STRING_AGG(DISTINCT " + columnName + ", '" + argsStr + "')";
return "LISTAGG(DISTINCT " + columnName + ", '" + argsStr + "')";
}
return "STRING_AGG(DISTINCT " + columnName + ", ',')";
return "LISTAGG(DISTINCT " + columnName + ", ',')";
case "CONCAT_AGG":
if (StringUtils.isNotBlank(argsStr)) {
return "STRING_AGG(" + columnName + ", '" + argsStr + "')";
return "LISTAGG(" + columnName + ", '" + argsStr + "')";
}
return "STRING_AGG(" + columnName + ", ',')";
return "LISTAGG(" + columnName + ", ',')";
case "CONCAT_AGG_ID":
if (StringUtils.isNotBlank(argsStr)) {
return "STRING_AGG(" + columnName + ", '" + argsStr + "')";
return "LISTAGG(" + columnName + ", '" + argsStr + "')";
}
return "STRING_AGG(" + columnName + ", ',')";
return "LISTAGG(" + columnName + ", ',')";
case "SPLIT_DISTINCT_CONCAT":
if (StringUtils.isNotBlank(argsStr)) {
String[] splitArgs = argsStr.split(",");
@@ -767,23 +767,23 @@ public class SqlGeneratorServiceImpl implements SqlGeneratorService {
String separator = splitArgs[0].trim();
String delimiter = splitArgs[1].trim();
String limit = splitArgs[2].trim();
return "STRING_AGG(DISTINCT REGEXP_SPLIT(" + columnName + ", '" + delimiter + "'), '" + separator + "') LIMIT " + limit;
return "LISTAGG(DISTINCT REGEXP_SPLIT(" + columnName + ", '" + delimiter + "'), '" + separator + "') LIMIT " + limit;
}
}
return columnName;
//自定添加方法
case "MODE_WITH_GROUP":
return "MODE() WITHIN GROUP (ORDER BY " + columnName + ")";
return columnName;
// 聚合函数(兼容旧代码)
case "ARRAY_AGG":
return "ARRAY_AGG(DISTINCT " + columnName + ")";
return "WM_CONCAT(DISTINCT " + columnName + ")";
case "STRING_AGG":
if (StringUtils.isNotBlank(argsStr)) {
return "STRING_AGG(" + columnSafeWrap(columnName) + ", " + argsStr + ")";
return "LISTAGG(" + columnSafeWrap(columnName) + ", " + argsStr + ")";
}
return "STRING_AGG(DISTINCT " + columnName + ", ',')";
return "LISTAGG(DISTINCT " + columnName + ", ',')";
// 时间函数
case "YEAR":
@@ -847,7 +847,7 @@ public class SqlGeneratorServiceImpl implements SqlGeneratorService {
case "TO_CHAR":
return "TO_CHAR(" + columnName + ", 'YYYYMMDD')";
case "HOST":
return "HOST(" + columnName + ")::text";
return columnName;
default:
return functionName + "(" + columnName + ")";
}
@@ -938,12 +938,7 @@ public class SqlGeneratorServiceImpl implements SqlGeneratorService {
* 列名安全包装(处理类型转换)
*/
private String columnSafeWrap(String columnName) {
if (columnName.toLowerCase().contains("ip")) {
return "host(" + columnName + ")::text";
}
if (columnName.toLowerCase().contains("port")) {
return columnName + "::int4";
}
// DM不需要类型转换,直接返回列名
return columnName;
}
@@ -22,6 +22,18 @@ public class JsonbUtil {
return null;
}
// 达梦数据库:TEXT/CLOB 列返回 dm.jdbc.driver.DmdbNClob 对象,
// toString() 只返回对象引用而非实际内容,必须通过 Clob 接口读取
if (value instanceof java.sql.Clob) {
try {
java.sql.Clob clob = (java.sql.Clob) value;
long length = clob.length();
value = length > 0 ? clob.getSubString(1, (int) length) : "";
} catch (Exception e) {
value = value.toString();
}
}
// 如果已经是字符串,直接返回
if (value instanceof String) {
String strValue = (String) value;
@@ -1,8 +1,16 @@
package com.config;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.jsontype.BasicPolymorphicTypeValidator;
import com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
@@ -15,6 +23,7 @@ import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSeriali
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import com.fasterxml.jackson.databind.DeserializationFeature;
import java.io.IOException;
import java.time.Duration;
import java.util.Collections;
import org.springframework.context.annotation.Primary;
@@ -35,6 +44,33 @@ public class CacheConfig {
// 禁用将日期序列化为时间戳
mapper.disable(com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
// 注册达梦 JDBC 安全序列化器 — 拦截 dm.jdbc.* 类,直接返回 null 防止循环引用 StackOverflow
SimpleModule dmSafeModule = new SimpleModule("dm-safe");
dmSafeModule.setSerializerModifier(new BeanSerializerModifier() {
@Override
public JsonSerializer<?> modifySerializer(SerializationConfig config,
BeanDescription beanDesc,
JsonSerializer<?> serializer) {
if (beanDesc.getBeanClass().getName().startsWith("dm.jdbc.")) {
return new JsonSerializer<Object>() {
@Override
public void serialize(Object value, JsonGenerator gen,
SerializerProvider serializers) throws IOException {
gen.writeNull();
}
@Override
public void serializeWithType(Object value, JsonGenerator gen,
SerializerProvider serializers, TypeSerializer typeSer) throws IOException {
gen.writeNull();
}
};
}
return serializer;
}
});
mapper.registerModule(dmSafeModule);
// 启用类型信息,解决 LinkedHashMap 转换问题
PolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator.builder()
.allowIfSubType("com.common.entity.") // 允许你的实体类包
@@ -1,6 +1,14 @@
package com.config;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -9,6 +17,8 @@ import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.io.IOException;
@Configuration
public class RedisConfig {
@@ -23,6 +33,32 @@ public class RedisConfig {
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
// 注册达梦 JDBC 安全序列化器 — 拦截 dm.jdbc.* 类,直接返回 null 防止循环引用 StackOverflow
SimpleModule dmSafeModule = new SimpleModule("dm-safe");
dmSafeModule.setSerializerModifier(new BeanSerializerModifier() {
@Override
public JsonSerializer<?> modifySerializer(SerializationConfig config,
BeanDescription beanDesc,
JsonSerializer<?> serializer) {
if (beanDesc.getBeanClass().getName().startsWith("dm.jdbc.")) {
return new JsonSerializer<Object>() {
@Override
public void serialize(Object value, JsonGenerator gen,
SerializerProvider serializers) throws IOException {
gen.writeNull();
}
@Override
public void serializeWithType(Object value, JsonGenerator gen,
SerializerProvider serializers, TypeSerializer typeSer) throws IOException {
gen.writeNull();
}
};
}
return serializer;
}
});
mapper.registerModule(dmSafeModule);
mapper.activateDefaultTyping(
mapper.getPolymorphicTypeValidator(),
ObjectMapper.DefaultTyping.NON_FINAL
@@ -1,8 +1,16 @@
package com.config;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -10,6 +18,7 @@ import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.io.IOException;
import java.util.List;
@Configuration
@@ -28,6 +37,33 @@ public class WebConfig implements WebMvcConfigurer {
// 禁用将日期序列化为时间戳
mapper.disable(com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
// 注册达梦 JDBC 安全序列化器 — 拦截 dm.jdbc.* 类,直接返回 null 防止循环引用 StackOverflow
SimpleModule dmSafeModule = new SimpleModule("dm-safe");
dmSafeModule.setSerializerModifier(new BeanSerializerModifier() {
@Override
public JsonSerializer<?> modifySerializer(SerializationConfig config,
BeanDescription beanDesc,
JsonSerializer<?> serializer) {
if (beanDesc.getBeanClass().getName().startsWith("dm.jdbc.")) {
return new JsonSerializer<Object>() {
@Override
public void serialize(Object value, JsonGenerator gen,
SerializerProvider serializers) throws IOException {
gen.writeNull();
}
@Override
public void serializeWithType(Object value, JsonGenerator gen,
SerializerProvider serializers, TypeSerializer typeSer) throws IOException {
gen.writeNull();
}
};
}
return serializer;
}
});
mapper.registerModule(dmSafeModule);
// 忽略未知属性
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);