feat(vr_ticket): 新增坐标反查城市 API(离线,基于 Region 表,无外部依赖)
parent
73e21da656
commit
9faa2ca069
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -75,7 +75,7 @@ class GeoCityService
|
|||
$region['lat'],
|
||||
$lng,
|
||||
$lat,
|
||||
2 // 返回米
|
||||
2 // 返回 km(GeoTransUtil::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), // km(GetDistance 第 4 参 2 已是 km,保留 2 位小数)
|
||||
'lng' => $region['lng'],
|
||||
'lat' => $region['lat'],
|
||||
];
|
||||
|
|
|
|||
Loading…
Reference in New Issue