Parcourir la source

fix(document-service): 修复TextElement反序列化类型转换错误

问题:PostgreSqlJsonbTypeHandler无法正确反序列化List<TextElement>,
导致从数据库读取后elements字段变成List<LinkedHashMap>,
在调用getPlainText()等方法时抛出ClassCastException。

解决方案:
1. 新增TextElementListTypeHandler,使用TypeReference正确反序列化泛型类型
2. 修改DocumentBlock.elements字段使用新的TypeHandler
何文松 il y a 1 mois
Parent
commit
9947c7f1e1

+ 2 - 1
backend/document-service/src/main/java/com/lingyue/document/entity/DocumentBlock.java

@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableName;
 import com.lingyue.common.domain.entity.SimpleModel;
 import com.lingyue.common.mybatis.PostgreSqlJsonbTypeHandler;
+import com.lingyue.document.mybatis.TextElementListTypeHandler;
 import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
@@ -49,7 +50,7 @@ public class DocumentBlock extends SimpleModel {
     
     @Schema(description = "块内元素列表(TextElement数组)", 
             example = "[{\"type\":\"text_run\",\"content\":\"Hello\"},{\"type\":\"entity\",\"entityId\":\"xxx\"}]")
-    @TableField(value = "elements", typeHandler = PostgreSqlJsonbTypeHandler.class)
+    @TableField(value = "elements", typeHandler = TextElementListTypeHandler.class)
     private List<TextElement> elements;
     
     @Schema(description = "块样式")

+ 72 - 0
backend/document-service/src/main/java/com/lingyue/document/mybatis/TextElementListTypeHandler.java

@@ -0,0 +1,72 @@
+package com.lingyue.document.mybatis;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.lingyue.document.entity.DocumentBlock.TextElement;
+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 org.postgresql.util.PGobject;
+
+import java.sql.CallableStatement;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.List;
+
+/**
+ * TextElement 列表类型处理器
+ * 
+ * 专门处理 DocumentBlock.elements 字段的序列化和反序列化。
+ * 解决通用 PostgreSqlJsonbTypeHandler 无法正确反序列化泛型类型的问题。
+ * 
+ * @author lingyue
+ * @since 2026-01-28
+ */
+@MappedTypes({List.class})
+@MappedJdbcTypes(JdbcType.OTHER)
+public class TextElementListTypeHandler extends BaseTypeHandler<List<TextElement>> {
+    
+    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
+    private static final TypeReference<List<TextElement>> TYPE_REF = new TypeReference<>() {};
+    
+    @Override
+    public void setNonNullParameter(PreparedStatement ps, int i, List<TextElement> parameter, JdbcType jdbcType) throws SQLException {
+        PGobject pgObject = new PGobject();
+        pgObject.setType("jsonb");
+        try {
+            pgObject.setValue(OBJECT_MAPPER.writeValueAsString(parameter));
+        } catch (JsonProcessingException e) {
+            throw new SQLException("Error converting List<TextElement> to JSONB: " + e.getMessage(), e);
+        }
+        ps.setObject(i, pgObject);
+    }
+    
+    @Override
+    public List<TextElement> getNullableResult(ResultSet rs, String columnName) throws SQLException {
+        return parseJson(rs.getString(columnName));
+    }
+    
+    @Override
+    public List<TextElement> getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
+        return parseJson(rs.getString(columnIndex));
+    }
+    
+    @Override
+    public List<TextElement> getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
+        return parseJson(cs.getString(columnIndex));
+    }
+    
+    private List<TextElement> parseJson(String json) throws SQLException {
+        if (json == null || json.isEmpty()) {
+            return null;
+        }
+        try {
+            return OBJECT_MAPPER.readValue(json, TYPE_REF);
+        } catch (JsonProcessingException e) {
+            throw new SQLException("Error parsing JSONB to List<TextElement>: " + e.getMessage(), e);
+        }
+    }
+}