① 地址查询
② 地址详情
从左侧选择一条候选地址后,这里会显示建筑图片、地址详情,以及住宅/商业判定结果。
从左侧选择一条候选地址后,这里会显示建筑图片、地址详情,以及住宅/商业判定结果。
——
典型流程两步:① 用 /v1/addresses/autocomplete 把用户输入的模糊地址变成候选列表让用户选;② 用选中的 placeId 调 /v1/addresses/{placeId}/detail 拿规范地址、住宅/商业判定与图片。把第 ① 步返回的 session 原样传给第 ② 步,Google 会按一次会话计费。
| 项目 | 说明 |
|---|---|
| Base URL | (当前页面所在域名) |
| 鉴权 | 请求头 X-API-Key: <key>,或 Authorization: Bearer <key>。图片类接口(/image、/v1/photos/)另外接受 ?api_key= 查询参数。后端 API_KEY 留空时不校验(仅调试)。 |
| 响应格式 | JSON,UTF-8。图片接口返回二进制。 |
| 国家 | 只查澳大利亚(includedRegionCodes=au)。 |
| 缓存 | 同一 placeId 的详情与图片在后端缓存 6 小时(CACHE_TTL),期间不重复计费。 |
| 超时 | 上游 Google 调用 8 秒超时(UPSTREAM_TIMEOUT),超时返回 504 upstream_timeout。 |
输入澳大利亚的模糊地址,返回最多 10 条候选详细地址。街道地址和商户名(如 bunnings richmond)都能命中。
| 参数 | 类型 | 说明 |
|---|---|---|
| q | string | 必填 模糊地址,去掉首尾空白后至少 3 个字符。也可用 query。 |
| session | string | 可选 32 位十六进制会话令牌。传入上一次响应里的值可让自动补全与详情合并计费;不传或格式不对则后端新建一个。 |
| limit | int | 可选 返回条数,1–10,默认 5,越界取边界值。 |
| X-API-Key | header | 可选 后端配置了 API_KEY 时必填。 |
| 字段 | 类型 | 说明 |
|---|---|---|
| query | string | 规范化后的查询词(合并多余空白) |
| country | string | 固定 AU |
| session | string | 本次会话令牌,详情请求时回传 |
| count | int | 候选条数 |
| suggestions[] | array | 候选列表,按 Google 相关度排序 |
| suggestions[].placeId | string | 地点 ID,用于详情/图片接口。Google 的 placeId 可能变化,不要长期持久化超过几个月 |
| suggestions[].primaryText | string | 主文本,一般是门牌+街道或商户名,如 71 Emerald Court |
| suggestions[].secondaryText | string | 次文本,一般是 区 州 邮编, Australia |
| suggestions[].fullText | string | 完整地址文本,展示给用户选择 |
| suggestions[].types[] | array | Google 地点类型,如 premise、street_address、subpremise、hardware_store;可选字段 |
| suggestions[].suburb / state / postcode | string | 从 secondaryText 拆出的区 / 州 / 邮编;拆不出时缺省 |
| suggestions[].distanceMeters | int | 与查询原点的距离,目前不传原点,通常缺省 |
| mockMode | bool | 仅 MOCK 模式下出现且为 true,表示后端没有配置 Google 凭证 |
| hint | string | 仅 MOCK 模式下出现的提示 |
curl -H "X-API-Key: <key>" "{{BASE}}/v1/addresses/autocomplete?q=71%20Emerald%20Court%20Singleton&limit=3"
{
"query": "71 Emerald Court Singleton",
"country": "AU",
"session": "93d961e6063b853c9b8a9a6b8f6f170d",
"count": 1,
"suggestions": [
{
"placeId": "ChIJj_5Cqvh9MioRf4LCljxjCdo",
"primaryText": "71 Emerald Court",
"secondaryText": "Singleton WA 6175, Australia",
"fullText": "71 Emerald Court, Singleton WA 6175, Australia",
"types": ["premise", "geocode"],
"suburb": "Singleton", "state": "WA", "postcode": "6175"
}
]
}
根据 placeId 返回该建筑的规范地址、经纬度、结构化地址组件、住宅/商业判定结果与图片信息。这是计费依据所在的接口。
| 参数 | 类型 | 说明 |
|---|---|---|
| placeId | path | 必填 来自自动补全结果的地点 ID,需 URL 编码 |
| session | string | 可选 自动补全返回的会话令牌,用于合并计费 |
| image | bool | 可选 传 1 / true 时 image 对象返回完整图片描述;不传时只返回图片接口路径 |
| imageWidth | int | 可选 图片宽度像素,200–4800,默认 1200,用于生成 image.rawEndpoint |
| 字段 | 类型 | 说明 |
|---|---|---|
| place.placeId | string | 地点 ID |
| place.displayName | string | 商户名,如 Bunnings Collingwood;纯住宅地址无此字段 |
| place.formattedAddress | string | Google 规范化的完整地址,如 71 Emerald Ct, Singleton WA 6175, Australia |
| place.shortFormattedAddress | string | 去掉国家的短地址;可选 |
| place.lat / lng | float | 经纬度(WGS84) |
| place.types[] | array | Google 地点类型全集 |
| place.primaryType | string | 主类型,如 premise、home_improvement_store、apartment_building;判定依据之一 |
| place.primaryTypeDisplayName | string | 主类型的英文展示名;可选 |
| place.businessStatus | string | 商户营业状态:OPERATIONAL / CLOSED_TEMPORARILY / CLOSED_PERMANENTLY;非商户无此字段 |
| place.addressComponents[] | array | Google 原始地址组件,每项含 longText、shortText、types[] |
| place.addressParts | object | 已拆分好的物流常用字段(见下) |
| place.addressParts.unitNumber | string | 单元号 / 套房号,如 3;无则缺省 |
| place.addressParts.streetNumber | string | 门牌号,如 71 |
| place.addressParts.route | string | 街道全名,如 Emerald Court |
| place.addressParts.suburb | string | 区 / 郊区(locality) |
| place.addressParts.state | string | 州缩写:NSW / VIC / QLD / WA / SA / TAS / ACT / NT |
| place.addressParts.postcode | string | 4 位邮编 |
| place.addressParts.country | string | 固定 AU |
| place.addressParts.poBox | bool | 是否 PO Box 地址(邮政信箱,非派送点) |
| place.addressParts.hasUnit | bool | 是否含单元号 |
| 字段 | 类型 | 说明 |
|---|---|---|
| classification.kind | string | residential 住宅 / commercial 商业 / unknown 无法判定 |
| classification.subType | string | 细分:residential、apartment、unit、business、warehouse、retail、hospitality、office、institution、industrial、premise、po_box、street_address、residential_unconfirmed、business_unconfirmed、unknown |
| classification.residentialSurcharge | bool | 计费用 是否应收住宅附加费。只有确定的住宅判定为 true |
| classification.confidence | float | 置信度 0–1 |
| classification.needsReview | bool | true 表示两个来源都没能给出确定结论(普通门牌无商户信息、或 Google 只定位到街道级),应转人工 |
| classification.autoBillable | bool | 计费用 能否不经人工直接按此结果计费。住宅判定需置信度 ≥ 0.8;商业判定不收费故为 true;needsReview 为 true 时必为 false |
| classification.reviewReason | string | autoBillable=false 时给出的人工复核原因;可选 |
| classification.reasons[] | array | 判定依据(英文短句),可直接给客服或客户解释 |
| classification.source | string | google-address-validation(Google 直给的物业类型,最可靠)/ places-rules(Google 未返回 metadata 时回落到规则链) |
| classification.addressValidation | object | Address Validation 的原始结论,仅 source=google-address-validation 时出现,便于审计(见下) |
| classification.addressValidation.metadata | object | {"business":bool,"residential":bool,"poBox":bool},Google 自己的物业类型标志位 |
| classification.addressValidation.metadataProvided | bool | Google 是否真的返回了标志位;false 表示三项全 false 是"没有答案"而非"非住宅" |
| classification.addressValidation.verdict | object | 验证质量:validationGranularity(PREMISE / SUB_PREMISE = 定位到建筑;ROUTE 等 = 只到街道)、inputGranularity、geocodeGranularity、addressComplete、hasUnconfirmedComponents、hasInferredComponents、possibleNextAction(ACCEPT / CONFIRM / FIX) |
| classification.addressValidation.formattedAddress | string | Address Validation 规范化的地址;可选 |
| classification.addressValidation.placeId / lat / lng | string / float | Address Validation 的地理编码结果;可选 |
不传 image=1 时:
| 字段 | 类型 | 说明 |
|---|---|---|
| image.photoRef | string | Google 照片资源名(可用于 /v1/photos/);无照片时为空串 |
| image.endpoint | string | 图片接口相对路径 /v1/addresses/{placeId}/image |
| image.hint | string | 提示文本 |
传 image=1 时:
| 字段 | 类型 | 说明 |
|---|---|---|
| image.source | string | google-place-photo(商户实拍照片)/ google-static-map(无照片时的卫星图兜底)/ none(无照片且后端未配 API Key)/ mock-svg |
| image.available | bool | 仅 source=none 时出现且为 false,表示图片接口会返回 404,前端应跳过图片 |
| image.rawEndpoint | string | 图片接口相对路径(已带 width),拼上 Base URL 可直接用于 <img src> |
| image.contentType | string | 预期 MIME:照片 image/jpeg,卫星图 image/png |
| image.description | string | 图片说明 |
| image.photoRef | string | 有商户照片时的 Google 照片资源名 |
| image.googlePhotoEndpoint | string | 有商户照片时的代理路径 /v1/photos/{photoRef} |
| image.inline | string | 仅 MOCK 模式:SVG 的 data URI |
| 字段 | 类型 | 说明 |
|---|---|---|
| mockMode | bool | 是否 MOCK 模式 |
curl -H "X-API-Key: <key>" "{{BASE}}/v1/addresses/ChIJj_5Cqvh9MioRf4LCljxjCdo/detail?session=93d961e6...&image=1"
{
"place": {
"placeId": "ChIJj_5Cqvh9MioRf4LCljxjCdo",
"formattedAddress": "71 Emerald Ct, Singleton WA 6175, Australia",
"lat": -32.442122, "lng": 115.7636893,
"types": ["premise", "street_address"],
"primaryType": "premise",
"addressComponents": [ { "longText": "71", "shortText": "71", "types": ["street_number"] }, … ],
"addressParts": { "streetNumber": "71", "route": "Emerald Court", "suburb": "Singleton",
"state": "WA", "postcode": "6175", "country": "AU", "poBox": false, "hasUnit": false }
},
"classification": {
"kind": "residential",
"subType": "residential",
"residentialSurcharge": true,
"confidence": 0.95,
"needsReview": false,
"autoBillable": true,
"reasons": ["google address validation reports the property is residential",
"validation reached building level (PREMISE)"],
"source": "google-address-validation",
"addressValidation": {
"metadata": { "business": false, "residential": true, "poBox": false },
"metadataProvided": true,
"verdict": { "inputGranularity": "PREMISE", "validationGranularity": "PREMISE",
"geocodeGranularity": "PREMISE", "addressComplete": true, "possibleNextAction": "ACCEPT" },
"formattedAddress": "71 Emerald Court, Singleton WA 6175, Australia"
}
},
"image": {
"source": "google-static-map",
"rawEndpoint": "/v1/addresses/ChIJj_5Cqvh9MioRf4LCljxjCdo/image?width=1200",
"contentType": "image/jpeg",
"description": "building photo for the selected address"
},
"mockMode": false
}
计费接入建议:只看两个字段——classification.autoBillable 为 true 时直接按 classification.residentialSurcharge 决定是否加收住宅费;为 false 时把 reviewReason、reasons 和图片一起推给人工复核。
直接返回建筑图片的二进制内容。优先使用 Google 商户实拍照片;该地点没有照片时自动回退为以该经纬度为中心的卫星地图截图(zoom 18,最大 640px,需后端配置 API Key)。两者都没有时返回 404 image_unavailable。
| 参数 | 类型 | 说明 |
|---|---|---|
| placeId | path | 必填 地点 ID,需 URL 编码 |
| width | int | 可选 宽度像素,200–4800,默认 1200;卫星图会压到 640 |
| api_key | string | 可选 鉴权开启时,图片类接口额外接受查询参数形式的 Key,方便直接写进 <img src>(浏览器无法给 <img> 加请求头)。注意 Key 会出现在 URL 和访问日志里,公网页面建议改为后端 fetch 带头转发或代理 |
| 项目 | 说明 |
|---|---|
| Body | 图片二进制 |
| Content-Type | image/jpeg(商户照片)或 image/png(卫星图) |
| Content-Length | 字节数 |
| Cache-Control | public, max-age=86400,可在 CDN / 浏览器缓存一天 |
curl -H "X-API-Key: <key>" "{{BASE}}/v1/addresses/ChIJj_5Cqvh9MioRf4LCljxjCdo/image?width=800" -o building.png
<img src="{{BASE}}/v1/addresses/ChIJj_5Cqvh9MioRf4LCljxjCdo/image?width=800&api_key=<key>">
Google 商户照片代理。把详情里的 image.photoRef(形如 places/ChIJ.../photos/AeJ...)URL 编码后作为单个路径段传入,后端代为转发并缓存,避免把 Google 凭证暴露给前端。一般直接用 /image 接口即可,这个接口用于需要指定某一张照片的场景。
| 项目 | 说明 |
|---|---|
| Body | 照片二进制,最大宽度 1200px |
| Content-Type | 通常 image/jpeg |
| Cache-Control | public, max-age=86400 |
| X-Cache | hit / miss,是否命中后端缓存 |
同样支持 api_key 查询参数。
curl -H "X-API-Key: <key>" "{{BASE}}/v1/photos/places%2FChIJ...%2Fphotos%2FAeJ..." -o photo.jpg
服务能力自检,对接方可用它确认后端处于哪种模式、用了什么凭证。不需要鉴权以外的参数。
| 字段 | 类型 | 说明 |
|---|---|---|
| service | string | 固定 logistics-address-api |
| version | string | 服务版本 |
| country | string | 固定 AU |
| mockMode | bool | true = 未配置任何 Google 凭证,返回内置样例 |
| authEnabled | bool | 是否要求 X-API-Key |
| authHeader | string | 固定 X-API-Key |
| upstream | string | google-places-api-new 或 mock |
| googleAuth | string | api-key / service-account / none,Places 与 Address Validation 当前用的凭证类型 |
| staticMapFallback | bool | 是否配置了 API Key,决定没有商户照片的地址有没有卫星图兜底 |
| cacheTtl | string | 缓存时长,如 6h0m0s |
| sources.addressValidation.enabled | bool | Address Validation 是否启用;关掉后判定只剩规则链 |
| sources.addressValidation.role / sources.placesRules.role | string | 两个证据来源的角色说明 |
| classification.kinds[] | array | ["commercial","residential","unknown"] |
| classification.autoBillConfidence | float | 住宅判定可自动计费的置信度门槛,当前 0.8 |
| classification.note | string | 说明文字 |
健康检查,供负载均衡 / 监控探活。
| 字段 | 类型 | 说明 |
|---|---|---|
| status | string | 固定 ok |
| mockMode | bool | 是否 MOCK 模式 |
| authEnabled | bool | 是否要求 API Key |
| cachedPlaces | int | 当前缓存的 placeId 条数 |
| time | string | 服务器时间,RFC 3339 |
所有失败统一返回 {"error":{"code":"...","message":"..."}},HTTP 状态码与 code 对应如下。
| HTTP | code | 含义与处理 |
|---|---|---|
| 400 | invalid_query | q 缺失或不足 3 个字符 |
| 400 | invalid_place_id / invalid_path | placeId 为空,或路径不是 /detail /image |
| 400 | invalid_photo_ref | 照片资源名格式不对 |
| 401 | invalid_api_key | 缺少或错误的 X-API-Key |
| 404 | place_not_found | placeId 不存在或已失效,让用户重新搜索 |
| 404 | image_unavailable | 该地址没有商户照片且后端未配 API Key,无法生成卫星图;前端跳过图片即可 |
| 404 | not_found / unknown_action | Google 侧资源不存在,或未知子路径 |
| 405 | method_not_allowed | 只支持 GET |
| 429 | upstream_quota_exceeded | Google 配额用尽,稍后重试或提高配额 |
| 500 | google_key_rejected | 后端 Google 凭证被拒(Key 无效、服务账号换 token 失败、API 未开通、账单未启用)。运维问题,重试无效 |
| 502 | upstream_error | Google 返回了其他错误 |
| 504 | upstream_timeout | Google 超时,可重试一次 |
Google 的原始报错不会透传给调用方,只会写到服务端日志。
后端共调用 Google 的三个 API,都需要在同一个 Google Cloud 项目里开通并启用账单:
| API | 用途 | 计费点 |
|---|---|---|
| Places API (New) | 模糊地址自动补全、地址详情、商户照片 | Autocomplete / Place Details / Place Photo 各自计价(有免费额度) |
| Address Validation API | 拿 metadata 里的 business / residential / poBox 标志位 | 按验证次数计价,同一地址缓存期内只调用一次 |
| Maps Static API | 该地点没有商户照片时,回退生成卫星地图截图 | 按请求数计价 |
Address Validation 默认开启,可用环境变量 ADDRESS_VALIDATION=false 关掉(省一次计费,但判定就只剩规则链)。它调用失败时不会让请求失败,只记日志并回落到规则链。
.env,不会下发给浏览器)| 环境变量 | 凭证 | 覆盖范围 |
|---|---|---|
GOOGLE_MAPS_API_KEY | Maps Platform API Key | 三个 API 全覆盖 |
GOOGLE_SERVICE_ACCOUNT_FILE | 服务账号 JSON 文件路径 | Places API (New) 和 Address Validation(走 OAuth,后端自动签 JWT 换 token 并续期)。Maps Static API 不接受 OAuth,只有服务账号时没有卫星图兜底 |
二选一或都填;都填时 Places / Validation 走服务账号,API Key 只用于卫星图。两个都空 = MOCK 模式。服务账号不需要任何 IAM 角色,只要所属项目开通了对应 API 即可。
判定有两个证据来源,按优先级短路,结果里的 source 字段会告诉你这一单到底靠哪个来源下的结论:
| 优先级 | 来源 | 说明 | 置信度 |
|---|---|---|---|
| 1 | google-address-validation | Address Validation API 的 result.metadata 会直接给出 {"business":…,"residential":…,"poBox":…}。这是 Google 自己的结论,优先采用。 | 0.95 |
| 2 | places-rules | 上面那个字段并非每个地址都返回。缺失时回落到规则链(见下),而不是当作"非住宅"。 | 0.55–0.90 |
规则链(仅在 Google 未返回 metadata 时生效):
| 顺序 | 规则 | 结果 | autoBillable |
|---|---|---|---|
| a | 地址组件里含 PO Box | 商业(非派送点),不收住宅费,置信度 0.90 | true |
| b | 地点类型为公寓 / 住宅楼 | 住宅,收住宅费,置信度 0.85 | true |
| c | 地点类型命中仓库、店铺、餐饮、办公、学校、医院、车行等商业类型 | 商业,不收住宅费,置信度 0.90(已停业则降为 0.75 并提示复核) | true |
| d | 有单元号但无任何商业信息 | 住宅,收住宅费,置信度 0.75 | false(低于 0.8 门槛,转人工) |
| e | 普通门牌地址、既非商业也无单元号 | unknown 需人工复核,不自动加收住宅费 | false |
一个重要的安全设计:Google 的 metadata 只有在 validationGranularity 为 PREMISE 或 SUB_PREMISE(即已定位到具体建筑)时才直接采信。如果只解析到街道级,residential=true 可能描述的是整条街,此时不会自动加收住宅费,而是给出 subType=residential_unconfirmed、置信度 0.6、needsReview=true 转人工。
建议:autoBillable=false 的订单先不要计费,转人工确认。若要把判定做到可对外收费的强度,下一阶段可接入澳洲官方地址库 Geoscape G-NAF 或 Australia Post PAF,其中带有物业用途标志位。