地理位置服务设计文档.md 5.0 KB

地理位置服务设计文档

概述: 本文档描述系统提供的地理位置相关接口(当前仅包含“最近位置”查询),包含外部服务调用、错误处理、配置建议与使用示例。对应实现:src/main/java/.../controller/GeoController.java

目标: 根据给定经纬度返回最近的位置信息字符串,供前端展示或作为地址回填使用;对外部地理服务调用做统一封装、错误与超时处理,以及可配置的服务端点。

接口清单:

  • 获取最近位置

    • 路径: GET /geo/nearest
    • 描述: 根据 latitudelongitude 查询最近位置字符串(同步返回)。
    • 请求参数(Query):
    • latitude (double) - 纬度,必填
    • longitude (double) - 经度,必填
    • 响应: R<String>,成功返回 200 和字符串内容;失败返回 ErrorCode.GEO_REQUEST_FAILED
    • 示例:

      GET /geo/nearest?latitude=30.12345&longitude=114.12345
      

实现细节(目前代码)

  • Controller 使用注入的 RestTemplate 直接拼接请求 URL:
    • 默认硬编码外部地址为 http://45.207.222.6/geo/nearest.php?latitude={latitude}&longitude={longitude}
    • 使用 restTemplate.getForEntity(url, String.class) 获取原始响应体并直接返回给客户端。
    • 发生异常时捕获并记录日志,返回 R.fail(ErrorCode.GEO_REQUEST_FAILED)

问题与改进建议

  1. 配置化外部服务地址与超时

    • 把外部 URL 提取到 application.yml(例如 geo.service.url),避免在代码中硬编码 IP/路径,方便切换环境与容错。
    • 使用带连接/读取超时的 RestTemplate(或 WebClient)并从配置加载超时值,防止外部服务阻塞线程。
  2. 错误处理与降级策略

    • 明确区分网络错误、HTTP 非 200 响应与响应解析错误,设置不同日志级别与错误码(例如 GEO_SERVICE_UNAVAILABLE)。
    • 建议实现简单的熔断/重试策略:当外部服务短期内失败多次时返回降级提示或缓存上次成功结果。
    • 对常见异常(连接超时、读取超时、SocketException)给出可操作日志,便于后续排查。
  3. 响应格式与语义

    • 当前直接返回外部服务的原始字符串;建议统一约定返回内容格式(例如 JSON 包装 { "address": "...", "source": "providerA" }),便于前端解析与兼容多个提供方。
    • 若外部服务返回 HTML 或非 JSON 字符串,建议在后端做清洗或封装,确保返回给客户端的是可预测的结构或明确的错误信息。
  4. 缓存与频次限制

    • 对同一经纬度(或经纬度格网)短时间内的重复请求进行缓存(例如 1 分钟),减少对外部服务依赖并降低延迟。
    • 在高并发场景考虑在 Controller 层或 API 网关做节流/限流。
  5. 安全与审计

    • 若使用收费或有请求配额的第三方服务,请在调用日志中记录 provider、请求结果码与耗时,用于计费和问题追踪。
    • 记录调用链日志:请求来源(userId / IP)、输入经纬度、外部请求耗时与返回状态。

建议的配置示例 (application.yml)

geo:
  service:
    url: "http://45.207.222.6/geo/nearest.php"
    connect-timeout-ms: 2000
    read-timeout-ms: 3000
    cache-ttl-seconds: 60

参考实现建议要点

  • 使用 RestTemplateBuilderWebClient 从配置构造带超时的 HTTP 客户端。
  • 将实际调用封装到 GeoService,Controller 只负责参数校验与统一响应。
  • GeoService 中实现:配置化 URL、参数编码、错误分类、重试/熔断策略、缓存层(如 Caffeine 或 Redis)的接入点。

错误码与日志策略

  • 继续使用 ErrorCode.GEO_REQUEST_FAILED 作为通用失败返回;可考虑增加 GEO_SERVICE_UNAVAILABLE(已在 ErrorCode 中预留)用于第三方服务不可用场景。
  • 记录日志字段:userId(如果可用)、latitudelongitudeexternalUrlstatusCodelatencyMserrorMessage

示例:改进后的调用流程(伪代码)

  1. Controller 校验入参(latitude/longitude)
  2. Controller 调用 geoService.getNearest(latitude, longitude)
  3. GeoService
    • 检查缓存(key = 经度/纬度格网或经纬度拼接)
    • 若缓存命中返回缓存值
    • 构建外部请求 URL(来自配置),执行 HTTP 请求并记录耗时
    • 根据响应解析出地址字符串,缓存并返回
    • 捕获异常并抛出明确的业务异常,Controller 根据异常返回对应错误码

示例响应

  • 成功:R.success(200, "ok", "湖北省武汉市xx区...")
  • 失败:R.fail(ErrorCode.GEO_REQUEST_FAILED.getCode(), ErrorCode.GEO_REQUEST_FAILED.getMessage(), null)

后续工作建议

  • 将外部 URL 配置化并添加超时、重试与熔断策略。
  • 考虑对经纬度查询结果做缓存,降低请求量并改善响应速度。
  • 如需兼容多个地理服务,设计统一的适配器层以便在 provider 间切换。