diff --git a/ebike-operations/src/main/java/com/cdzy/operations/config/JacksonConfig.java b/ebike-operations/src/main/java/com/cdzy/operations/config/JacksonConfig.java index 2f4904f..b28f829 100644 --- a/ebike-operations/src/main/java/com/cdzy/operations/config/JacksonConfig.java +++ b/ebike-operations/src/main/java/com/cdzy/operations/config/JacksonConfig.java @@ -3,6 +3,8 @@ package com.cdzy.operations.config; import cn.hutool.core.date.DatePattern; import com.cdzy.operations.handler.PGpointDeserializer; import com.cdzy.operations.handler.PGpointSerializer; +import com.cdzy.operations.handler.PGpolygonDeserializer; +import com.cdzy.operations.handler.PGpolygonSerializer; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; @@ -12,6 +14,7 @@ import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer; import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer; import org.postgresql.geometric.PGpoint; +import org.postgresql.geometric.PGpolygon; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; import org.springframework.context.annotation.Bean; @@ -38,11 +41,13 @@ public class JacksonConfig { javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DatePattern.NORM_DATE_FORMATTER)); javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DatePattern.NORM_DATETIME_FORMATTER)); javaTimeModule.addSerializer(PGpoint.class, new PGpointSerializer()); + javaTimeModule.addSerializer(PGpolygon.class, new PGpolygonSerializer()); javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DatePattern.NORM_TIME_FORMATTER)); javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DatePattern.NORM_DATE_FORMATTER)); javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DatePattern.NORM_DATETIME_FORMATTER)); javaTimeModule.addDeserializer(PGpoint.class, new PGpointDeserializer()); + javaTimeModule.addDeserializer(PGpolygon.class, new PGpolygonDeserializer()); return javaTimeModule; } diff --git a/ebike-operations/src/main/java/com/cdzy/operations/controller/EbikeBikeInfoController.java b/ebike-operations/src/main/java/com/cdzy/operations/controller/EbikeBikeInfoController.java index 79badce..52e17f0 100644 --- a/ebike-operations/src/main/java/com/cdzy/operations/controller/EbikeBikeInfoController.java +++ b/ebike-operations/src/main/java/com/cdzy/operations/controller/EbikeBikeInfoController.java @@ -1,14 +1,14 @@ package com.cdzy.operations.controller; import com.cdzy.common.model.response.JsonResult; +import com.cdzy.operations.model.entity.EbikeBikeInfo; import com.cdzy.operations.model.vo.EbikeBikeBindVo; import com.cdzy.operations.service.EbikeBikeInfoService; import jakarta.annotation.Resource; import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; + +import java.util.List; /** * 车辆基本信息 控制层。 @@ -35,4 +35,24 @@ public class EbikeBikeInfoController { return JsonResult.success(); } + /** + * 车辆列表 + * @return 列表 + */ + @GetMapping("list") + public JsonResult list() { + List list = ebikeBikeInfoService.list(); + return JsonResult.success(list); + } + + /** + * 生成车辆 + * @return 列表 + */ + @PostMapping("save") + public JsonResult save(@Validated @RequestBody EbikeBikeInfo ebikeBikeInfo) { + ebikeBikeInfoService.save(ebikeBikeInfo); + return JsonResult.success(); + } + } diff --git a/ebike-operations/src/main/java/com/cdzy/operations/handler/PGpointDeserializer.java b/ebike-operations/src/main/java/com/cdzy/operations/handler/PGpointDeserializer.java index 799d35b..7685971 100644 --- a/ebike-operations/src/main/java/com/cdzy/operations/handler/PGpointDeserializer.java +++ b/ebike-operations/src/main/java/com/cdzy/operations/handler/PGpointDeserializer.java @@ -23,11 +23,12 @@ public class PGpointDeserializer extends JsonDeserializer { try { // 方式1: GeoJSON 格式 - if (node.isObject() && node.has("type") && "Point".equals(node.get("type").asText())) { - JsonNode coordinates = node.get("coordinates"); - if (coordinates != null && coordinates.isArray() && coordinates.size() >= 2) { - double x = coordinates.get(0).asDouble(); - double y = coordinates.get(1).asDouble(); + if (node.isObject()) { + JsonNode longitude = node.get("longitude"); + JsonNode latitude = node.get("latitude"); + if (longitude != null && latitude != null) { + double x = longitude.asDouble(); + double y =latitude.asDouble(); return new PGpoint(x, y); } } diff --git a/ebike-operations/src/main/java/com/cdzy/operations/handler/PGpolygonDeserializer.java b/ebike-operations/src/main/java/com/cdzy/operations/handler/PGpolygonDeserializer.java new file mode 100644 index 0000000..8a55795 --- /dev/null +++ b/ebike-operations/src/main/java/com/cdzy/operations/handler/PGpolygonDeserializer.java @@ -0,0 +1,171 @@ +package com.cdzy.operations.handler; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import org.postgresql.geometric.PGpoint; +import org.postgresql.geometric.PGpolygon; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * PGpolygon 反序列化器 - 将 JSON 反序列化为 PGpolygon + */ +public class PGpolygonDeserializer extends JsonDeserializer { + + @Override + public PGpolygon deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + JsonNode node = p.getCodec().readTree(p); + + if (node.isNull()) { + return null; + } + + try { + // 支持多种 JSON 格式 + + // 格式1: 自定义格式 {"type": "polygon", "coordinates": [[x1,y1], [x2,y2], ...]} + if (node.isObject() && node.has("type") && "polygon".equals(node.get("type").asText())) { + return parseFromCustomFormat(node); + } + + // 格式2: GeoJSON 格式 {"type": "Polygon", "coordinates": [[[x1,y1], [x2,y2], ...]]} + if (node.isObject() && node.has("type") && "Polygon".equals(node.get("type").asText())) { + return parseFromGeoJSON(node); + } + + // 格式3: 直接坐标数组 [[x1,y1], [x2,y2], ...] + if (node.isArray()) { + return parseFromArray(node); + } + + // 格式4: 字符串格式 "((x1 y1, x2 y2, ...))" + if (node.isTextual()) { + return parseFromString(node.asText()); + } + + throw new IOException("不支持的 PGpolygon JSON 格式"); + + } catch (Exception e) { + throw new IOException("PGpolygon 反序列化失败: " + e.getMessage(), e); + } + } + + @Override + public Class handledType() { + return PGpolygon.class; + } + + /** + * 解析自定义格式 + */ + private PGpolygon parseFromCustomFormat(JsonNode node) throws IOException { + JsonNode coordinatesNode = node.get("coordinates"); + if (coordinatesNode == null || !coordinatesNode.isArray()) { + throw new IOException("coordinates 字段缺失或格式错误"); + } + + List points = new ArrayList<>(); + for (JsonNode coordNode : coordinatesNode) { + if (coordNode.isArray() && coordNode.size() >= 2) { + double x = coordNode.get(0).asDouble(); + double y = coordNode.get(1).asDouble(); + points.add(new PGpoint(x, y)); + } + } + + return createPGpolygonFromPoints(points); + } + + /** + * 解析 GeoJSON 格式 + */ + private PGpolygon parseFromGeoJSON(JsonNode node) throws IOException { + JsonNode coordinatesNode = node.get("coordinates"); + if (coordinatesNode == null || !coordinatesNode.isArray() || coordinatesNode.isEmpty()) { + throw new IOException("coordinates 字段缺失或格式错误"); + } + + // GeoJSON 格式: coordinates 是三维数组 [[[x1,y1], [x2,y2], ...]] + JsonNode ringNode = coordinatesNode.get(0); + if (!ringNode.isArray()) { + throw new IOException("GeoJSON coordinates 格式错误"); + } + + List points = new ArrayList<>(); + for (JsonNode coordNode : ringNode) { + if (coordNode.isArray() && coordNode.size() >= 2) { + double x = coordNode.get(0).asDouble(); + double y = coordNode.get(1).asDouble(); + points.add(new PGpoint(x, y)); + } + } + + return createPGpolygonFromPoints(points); + } + + /** + * 解析坐标数组格式 + */ + private PGpolygon parseFromArray(JsonNode node) throws IOException { + List points = new ArrayList<>(); + for (JsonNode coordNode : node) { + if (coordNode.isArray() && coordNode.size() >= 2) { + double x = coordNode.get(0).asDouble(); + double y = coordNode.get(1).asDouble(); + points.add(new PGpoint(x, y)); + } + } + + return createPGpolygonFromPoints(points); + } + + /** + * 解析字符串格式 + */ + private PGpolygon parseFromString(String polygonString) throws IOException { + try { + return new PGpolygon(polygonString); + } catch (Exception e) { + throw new IOException("PGpolygon 字符串格式解析失败: " + polygonString, e); + } + } + + /** + * 从点列表创建 PGpolygon + */ + private PGpolygon createPGpolygonFromPoints(List points) throws IOException { + if (points == null || points.size() < 3) { + throw new IOException("多边形至少需要3个点"); + } + + try { + // 构建多边形字符串格式:((x1 y1, x2 y2, x3 y3, x1 y1)) + StringBuilder sb = new StringBuilder("(("); + for (int i = 0; i < points.size(); i++) { + PGpoint point = points.get(i); + sb.append(point.x).append(" ").append(point.y); + if (i < points.size() - 1) { + sb.append(", "); + } + } + + // 确保多边形闭合(首尾点相同) + PGpoint firstPoint = points.get(0); + PGpoint lastPoint = points.get(points.size() - 1); + if (firstPoint.x != lastPoint.x || firstPoint.y != lastPoint.y) { + sb.append(", ").append(firstPoint.x).append(" ").append(firstPoint.y); + } + + sb.append("))"); + + return new PGpolygon(sb.toString()); + + } catch (Exception e) { + throw new IOException("创建 PGpolygon 失败", e); + } + } +} \ No newline at end of file diff --git a/ebike-operations/src/main/java/com/cdzy/operations/handler/PGpolygonSerializer.java b/ebike-operations/src/main/java/com/cdzy/operations/handler/PGpolygonSerializer.java new file mode 100644 index 0000000..8ad10e3 --- /dev/null +++ b/ebike-operations/src/main/java/com/cdzy/operations/handler/PGpolygonSerializer.java @@ -0,0 +1,70 @@ +package com.cdzy.operations.handler; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import org.postgresql.geometric.PGpolygon; +import org.postgresql.geometric.PGpoint; + +import java.io.IOException; + +/** + * PGpolygon 序列化器 - 将 PGpolygon 序列化为 JSON + */ +public class PGpolygonSerializer extends JsonSerializer { + + @Override + public void serialize(PGpolygon polygon, JsonGenerator gen, SerializerProvider serializers) throws IOException { + if (polygon == null) { + gen.writeNull(); + return; + } + + // 方式1: 序列化为自定义格式 + gen.writeStartObject(); + gen.writeStringField("type", "polygon"); + + // 解析多边形字符串获取点坐标 + PGpoint[] points = parsePointsFromPolygon(polygon); + gen.writeArrayFieldStart("coordinates"); + for (PGpoint point : points) { + gen.writeStartArray(); + gen.writeNumber(point.x); + gen.writeNumber(point.y); + gen.writeEndArray(); + } + gen.writeEndArray(); + gen.writeEndObject(); + } + + @Override + public Class handledType() { + return PGpolygon.class; + } + + /** + * 从 PGpolygon 解析点坐标 + */ + private PGpoint[] parsePointsFromPolygon(PGpolygon polygon) { + try { + String polygonString = polygon.toString(); + // 解析格式:((x1 y1, x2 y2, x3 y3, x1 y1)) + String pointsStr = polygonString.replaceAll("[()]", "").trim(); + String[] pointPairs = pointsStr.split(","); + + PGpoint[] points = new PGpoint[pointPairs.length]; + for (int i = 0; i < pointPairs.length; i++) { + String pointPair = pointPairs[i].trim(); + String[] coords = pointPair.split("\\s+"); + if (coords.length >= 2) { + double x = Double.parseDouble(coords[0]); + double y = Double.parseDouble(coords[1]); + points[i] = new PGpoint(x, y); + } + } + return points; + } catch (Exception e) { + throw new RuntimeException("Failed to parse points from polygon", e); + } + } +} \ No newline at end of file diff --git a/ebike-operations/src/main/java/com/cdzy/operations/handler/PGpolygonTypeHandler.java b/ebike-operations/src/main/java/com/cdzy/operations/handler/PGpolygonTypeHandler.java new file mode 100644 index 0000000..6abf56a --- /dev/null +++ b/ebike-operations/src/main/java/com/cdzy/operations/handler/PGpolygonTypeHandler.java @@ -0,0 +1,202 @@ +package com.cdzy.operations.handler; + +import org.apache.ibatis.type.JdbcType; +import org.apache.ibatis.type.TypeHandler; +import org.postgresql.geometric.PGpolygon; +import org.postgresql.geometric.PGpoint; +import org.postgresql.util.PGobject; +import com.cdzy.common.utils.CoordinateUtil; + +import java.sql.*; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Pattern; +import java.util.regex.Matcher; + +/** + * PGpolygon 类型处理器 - 集成 CoordinateUtil 实现坐标转换 + * 写入时:GCJ-02 → WGS-84 + * 读取时:WGS-84 → GCJ-02 + */ +public class PGpolygonTypeHandler implements TypeHandler { + + // 正则表达式用于解析多边形字符串格式:((x1 y1, x2 y2, x3 y3, x1 y1)) + private static final Pattern POLYGON_PATTERN = Pattern.compile("\\(\\s*\\(([^)]+)\\)\\s*\\)"); + + @Override + public void setParameter(PreparedStatement ps, int i, PGpolygon parameter, JdbcType jdbcType) throws SQLException { + if (parameter == null) { + ps.setNull(i, Types.OTHER); + } else { + // 写入数据库时:GCJ-02 → WGS-84 + PGpolygon wgs84Polygon = gcj02ToWgs84(parameter); + ps.setObject(i, wgs84Polygon); + } + } + + @Override + public PGpolygon getResult(ResultSet rs, String columnName) throws SQLException { + Object object = rs.getObject(columnName); + PGpolygon wgs84Polygon = convertToPGpolygon(object); + // 读取时:WGS-84 → GCJ-02 + return wgs84ToGcj02(wgs84Polygon); + } + + @Override + public PGpolygon getResult(ResultSet rs, int columnIndex) throws SQLException { + Object object = rs.getObject(columnIndex); + PGpolygon wgs84Polygon = convertToPGpolygon(object); + // 读取时:WGS-84 → GCJ-02 + return wgs84ToGcj02(wgs84Polygon); + } + + @Override + public PGpolygon getResult(CallableStatement cs, int columnIndex) throws SQLException { + Object object = cs.getObject(columnIndex); + PGpolygon wgs84Polygon = convertToPGpolygon(object); + // 读取时:WGS-84 → GCJ-02 + return wgs84ToGcj02(wgs84Polygon); + } + + /** + * 将数据库对象转换为 PGpolygon + */ + private PGpolygon convertToPGpolygon(Object object) { + if (object == null) { + return null; + } + + try { + if (object instanceof PGpolygon) { + // 直接是 PGpolygon 类型 + return (PGpolygon) object; + } else if (object instanceof String) { + // 字符串格式,如 "((x1 y1, x2 y2, x3 y3, x1 y1))" + return new PGpolygon((String) object); + } else if (object instanceof PGobject pgObject) { + // PGobject 类型,获取其字符串值 + return new PGpolygon(pgObject.getValue()); + } + } catch (Exception e) { + throw new RuntimeException("Failed to convert to PGpolygon: " + object, e); + } + + return null; + } + + /** + * GCJ-02 转 WGS-84(写入数据库时调用) + */ + private PGpolygon gcj02ToWgs84(PGpolygon gcj02Polygon) { + if (gcj02Polygon == null) { + return null; + } + + try { + // 通过字符串解析获取点坐标 + String polygonString = gcj02Polygon.toString(); + PGpoint[] points = parsePointsFromPolygonString(polygonString); + List wgs84Points = new ArrayList<>(); + + // 对每个点进行坐标转换 + for (PGpoint gcj02Point : points) { + double[] wgs84 = CoordinateUtil.GCJ02ToWGS84(gcj02Point.x, gcj02Point.y); + wgs84Points.add(new PGpoint(wgs84[0], wgs84[1])); + } + + // 创建新的 WGS-84 多边形 + return createPGpolygonFromPoints(wgs84Points); + + } catch (Exception e) { + throw new RuntimeException("GCJ-02 转 WGS-84 区域转换错误", e); + } + } + + /** + * WGS-84 转 GCJ-02(从数据库读取时调用) + */ + private PGpolygon wgs84ToGcj02(PGpolygon wgs84Polygon) { + if (wgs84Polygon == null) { + return null; + } + + try { + // 通过字符串解析获取点坐标 + String polygonString = wgs84Polygon.toString(); + PGpoint[] points = parsePointsFromPolygonString(polygonString); + List gcj02Points = new ArrayList<>(); + + // 对每个点进行坐标转换 + for (PGpoint wgs84Point : points) { + double[] gcj02 = CoordinateUtil.WGS84ToGCJ02(wgs84Point.x, wgs84Point.y); + gcj02Points.add(new PGpoint(gcj02[0], gcj02[1])); + } + + // 创建新的 GCJ-02 多边形 + return createPGpolygonFromPoints(gcj02Points); + + } catch (Exception e) { + throw new RuntimeException("GCJ-02 转 WGS-84 区域转换错误", e); + } + } + + /** + * 从多边形字符串中解析点坐标 + */ + private PGpoint[] parsePointsFromPolygonString(String polygonString) { + List points = new ArrayList<>(); + + try { + Matcher matcher = POLYGON_PATTERN.matcher(polygonString); + if (matcher.find()) { + String pointsStr = matcher.group(1); + // 分割点坐标:x1 y1, x2 y2, x3 y3 + String[] pointPairs = pointsStr.split(","); + + for (String pointPair : pointPairs) { + pointPair = pointPair.trim(); + String[] coords = pointPair.split("\\s+"); + if (coords.length >= 2) { + double x = Double.parseDouble(coords[0]); + double y = Double.parseDouble(coords[1]); + points.add(new PGpoint(x, y)); + } + } + } + } catch (Exception e) { + throw new RuntimeException("Failed to parse points from polygon string: " + polygonString, e); + } + + return points.toArray(new PGpoint[0]); + } + + /** + * 从点列表创建 PGpolygon + */ + private PGpolygon createPGpolygonFromPoints(List points) { + if (points.isEmpty()) { + return null; + } + + try { + // 构建多边形字符串格式:((x1 y1, x2 y2, x3 y3, x1 y1)) + StringBuilder sb = new StringBuilder("(("); + for (int i = 0; i < points.size(); i++) { + PGpoint point = points.get(i); + sb.append(point.x).append(" ").append(point.y); + if (i < points.size() - 1) { + sb.append(", "); + } + } + // 确保多边形闭合(首尾点相同) + PGpoint firstPoint = points.get(0); + sb.append(", ").append(firstPoint.x).append(" ").append(firstPoint.y); + sb.append("))"); + + return new PGpolygon(sb.toString()); + + } catch (Exception e) { + throw new RuntimeException("区域点列表创建错误", e); + } + } +} \ No newline at end of file