feat(vr_ticket): 新增坐标反查城市 API(离线,基于 Region 表,无外部依赖)

main
Council 2026-06-29 00:26:40 +08:00
parent 73e21da656
commit 9faa2ca069
2 changed files with 111 additions and 2 deletions

View File

@ -0,0 +1,109 @@
<?php
/**
* VR票务插件 - 坐标反查城市 API 控制器
*
* 路由:/api.php?s=plugins/index&pluginsname=vr_ticket&pluginscontrol=geo&pluginsaction=city
* class = \app\plugins\vr_ticket\api\Geo (ucfirst('geo') = 'Geo')
* method = ucfirst('city') = 'City'
* \app\plugins\vr_ticket\api\Geo::City($params)
*
* 用途:根据经纬度反查最近的可服务城市(基于 Region 表,离线查找)
* 不依赖:任何地图厂商 AK / 外部 SDK
*
* 设计要点:
* 1. 仅返回可服务城市Region 表里有坐标 + is_enable=1 的城市)
* 2. 距离超过 150km 视为"无附近服务城市"
* 3. 缓存按 5 位小数(~1m聚合30 天有效
* 4. 失败时不抛异常,统一返回 DataReturn 格式
*
* @package vr_ticket\api
*/
namespace app\plugins\vr_ticket\api;
use app\plugins\vr_ticket\service\GeoCityService;
class Geo
{
/**
* 无附近城市的最大距离阈值km
* Region 表只存"我们提供服务的城市"坐标;超出此值视为超出服务范围
*/
const MAX_NEARBY_DISTANCE_KM = 150;
/**
* 缓存天数
*/
const CACHE_DAYS = 30;
/**
* 根据坐标获取最近的可服务城市
*
* @param array $params 必须包含 lng, lat
* @return array DataReturn 结构 { code, msg, data }
*/
public function City($params = [])
{
// 1. 参数校验
$lng = isset($params['lng']) ? $params['lng'] : '';
$lat = isset($params['lat']) ? $params['lat'] : '';
if ($lng === '' || $lat === '') {
return DataReturn('lng/lat 必填', -1);
}
if (!is_numeric($lng) || !is_numeric($lat)) {
return DataReturn('lng/lat 必须为数字', -1);
}
$lng = (float)$lng;
$lat = (float)$lat;
if ($lng < -180 || $lng > 180 || $lat < -90 || $lat > 90) {
return DataReturn('坐标越界', -1);
}
// 2. 缓存命中优先(按 ~1m 精度聚合30 天)
$cache_key = 'vr_ticket_geo_regeocode_' . round($lng, 5) . '_' . round($lat, 5);
$cached = MyCache($cache_key);
if (is_array($cached) && isset($cached['city'])) {
return DataReturn('success', 0, $cached);
}
// 3. 查 Region 表
// FindNearestCity 已在 2026-06-28 修复距离单位 bug
// 修复前 distance = 真实 km 数 / 1000错误
// 修复后 distance = 真实 km 数(正确)
$nearest = GeoCityService::FindNearestCity($lng, $lat, 3);
// 4. 没匹配到任何城市
if (empty($nearest) || empty($nearest['id']) || empty($nearest['name'])) {
return DataReturn('暂无服务城市数据', -1);
}
// 5. 距离超阈值 → 视为无附近服务城市
$distance_km = (float)($nearest['distance'] ?? 0);
if ($distance_km > self::MAX_NEARBY_DISTANCE_KM) {
return DataReturn(
'当前位置暂无服务城市(最近 ' . $nearest['name'] . ' 约 ' . $distance_km . 'km',
-1
);
}
// 6. 组装统一返回
$result = [
'city' => (string)$nearest['name'],
'city_id' => (int)$nearest['id'],
'distance' => $distance_km,
'input' => [
'lng' => $lng,
'lat' => $lat,
],
'matched' => [
'lng' => (float)($nearest['lng'] ?? 0),
'lat' => (float)($nearest['lat'] ?? 0),
],
];
// 7. 写缓存
MyCache($cache_key, $result, 86400 * self::CACHE_DAYS);
return DataReturn('success', 0, $result);
}
}

View File

@ -75,7 +75,7 @@ class GeoCityService
$region['lat'],
$lng,
$lat,
2 // 返回
2 // 返回 kmGeoTransUtil::GetDistance 第 4 参 2=km
);
if ($distance < $min_distance) {
@ -123,7 +123,7 @@ class GeoCityService
$nearest = [
'id' => $region['id'],
'name' => $region['name'],
'distance' => round($distance / 1000, 2), // 转为km保留2位小数
'distance' => round($distance, 2), // kmGetDistance 第 4 参 2 已是 km保留 2 位小数)
'lng' => $region['lng'],
'lat' => $region['lat'],
];