**地理位置服务设计文档** **概述**: 本文档描述系统提供的地理位置相关接口(当前仅包含“最近位置”查询),包含外部服务调用、错误处理、配置建议与使用示例。对应实现:`src/main/java/.../controller/GeoController.java`。 **目标**: 根据给定经纬度返回最近的位置信息字符串,供前端展示或作为地址回填使用;对外部地理服务调用做统一封装、错误与超时处理,以及可配置的服务端点。 **接口清单**: - **获取最近位置** - 路径: `GET /geo/nearest` - 描述: 根据 `latitude` 与 `longitude` 查询最近位置字符串(同步返回)。 - 请求参数(Query): - `latitude` (double) - 纬度,必填 - `longitude` (double) - 经度,必填 - 响应: `R`,成功返回 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)** ```yaml geo: service: url: "http://45.207.222.6/geo/nearest.php" connect-timeout-ms: 2000 read-timeout-ms: 3000 cache-ttl-seconds: 60 ``` **参考实现建议要点** - 使用 `RestTemplateBuilder` 或 `WebClient` 从配置构造带超时的 HTTP 客户端。 - 将实际调用封装到 `GeoService`,Controller 只负责参数校验与统一响应。 - 在 `GeoService` 中实现:配置化 URL、参数编码、错误分类、重试/熔断策略、缓存层(如 Caffeine 或 Redis)的接入点。 **错误码与日志策略** - 继续使用 `ErrorCode.GEO_REQUEST_FAILED` 作为通用失败返回;可考虑增加 `GEO_SERVICE_UNAVAILABLE`(已在 `ErrorCode` 中预留)用于第三方服务不可用场景。 - 记录日志字段:`userId`(如果可用)、`latitude`、`longitude`、`externalUrl`、`statusCode`、`latencyMs`、`errorMessage`。 **示例:改进后的调用流程(伪代码)** 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 间切换。