1
This commit is contained in:
@@ -6,6 +6,7 @@ import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"pointly-tel/amap/model/dto"
|
||||
|
||||
@@ -92,22 +93,34 @@ func (s *poiService) searchText(ctx context.Context, apiKey string, req *dto.Sea
|
||||
const baseUrl = "https://restapi.amap.com/v3/place/text"
|
||||
needTotal := page * offset
|
||||
all := make([]*dto.PoiItem, 0, needTotal)
|
||||
var noPhone []*amapPoi
|
||||
|
||||
for ap := 1; ap <= maxPage; ap++ {
|
||||
if ap > 1 {
|
||||
time.Sleep(80 * time.Millisecond)
|
||||
}
|
||||
pois, _, err := s.fetchTextPage(ctx, apiKey, baseUrl, req.Keywords, req.City, ap, offset)
|
||||
if err != nil || len(pois) == 0 {
|
||||
break
|
||||
}
|
||||
for _, p := range pois {
|
||||
if string(p.Tel) == "" {
|
||||
noPhone = append(noPhone, p)
|
||||
continue
|
||||
}
|
||||
all = append(all, s.toPoiItem(p))
|
||||
}
|
||||
if len(all) >= needTotal || len(pois) < offset {
|
||||
if len(pois) < offset {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 兜底:对无电话POI按名称搜索,尝试找到电话
|
||||
if len(all) < needTotal {
|
||||
extra := s.fallbackPhone(ctx, apiKey, noPhone, req.City, "")
|
||||
all = append(all, extra...)
|
||||
}
|
||||
|
||||
return s.buildResult(all, page, offset), nil
|
||||
}
|
||||
|
||||
@@ -125,22 +138,34 @@ func (s *poiService) searchAround(ctx context.Context, apiKey string, req *dto.S
|
||||
locStr := fmt.Sprintf("%.6f,%.6f", req.Longitude, req.Latitude)
|
||||
needTotal := page * offset
|
||||
all := make([]*dto.PoiItem, 0, needTotal)
|
||||
var noPhone []*amapPoi
|
||||
|
||||
for ap := 1; ap <= maxPage; ap++ {
|
||||
if ap > 1 {
|
||||
time.Sleep(80 * time.Millisecond)
|
||||
}
|
||||
pois, _, err := s.fetchAroundPage(ctx, apiKey, baseUrl, req.Keywords, locStr, radius, ap, offset)
|
||||
if err != nil || len(pois) == 0 {
|
||||
break
|
||||
}
|
||||
for _, p := range pois {
|
||||
if string(p.Tel) == "" {
|
||||
noPhone = append(noPhone, p)
|
||||
continue
|
||||
}
|
||||
all = append(all, s.toPoiItem(p))
|
||||
}
|
||||
if len(all) >= needTotal || len(pois) < offset {
|
||||
if len(pois) < offset {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 兜底:对无电话POI按名称搜索
|
||||
if len(all) < needTotal {
|
||||
extra := s.fallbackPhone(ctx, apiKey, noPhone, "", locStr)
|
||||
all = append(all, extra...)
|
||||
}
|
||||
|
||||
return s.buildResult(all, page, offset), nil
|
||||
}
|
||||
|
||||
@@ -196,6 +221,46 @@ func (s *poiService) buildResult(all []*dto.PoiItem, page, offset int) *dto.Sear
|
||||
}
|
||||
}
|
||||
|
||||
// fallbackPhone 对无电话POI按名称搜索电话(最多查10个,间隔200ms避免限流)
|
||||
func (s *poiService) fallbackPhone(ctx context.Context, apiKey string, noPhone []*amapPoi, city, location string) []*dto.PoiItem {
|
||||
const baseUrl = "https://restapi.amap.com/v3/place/text"
|
||||
const maxSearch = 50
|
||||
var found []*dto.PoiItem
|
||||
seen := map[string]bool{}
|
||||
|
||||
for i, np := range noPhone {
|
||||
if np.Name == "" || i >= maxSearch {
|
||||
continue
|
||||
}
|
||||
if i > 0 {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
|
||||
pois, _, err := s.fetchTextPage(ctx, apiKey, baseUrl, np.Name, city, 1, 10)
|
||||
if err != nil || len(pois) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, p := range pois {
|
||||
if string(p.Tel) == "" {
|
||||
continue
|
||||
}
|
||||
if !strings.Contains(p.Name, np.Name) && !strings.Contains(np.Name, p.Name) {
|
||||
continue
|
||||
}
|
||||
if seen[p.Id] {
|
||||
continue
|
||||
}
|
||||
seen[p.Id] = true
|
||||
found = append(found, s.toPoiItem(p))
|
||||
g.Log().Info(ctx, "电话补全", "商户", np.Name, "电话", string(p.Tel))
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return found
|
||||
}
|
||||
|
||||
// fetchTextPage 文本搜索单页
|
||||
func (s *poiService) fetchTextPage(ctx context.Context, apiKey, baseUrl, keywords, city string, page, offset int) ([]*amapPoi, int, error) {
|
||||
pois, total := s.fetchAmapPage(ctx, apiKey, baseUrl, keywords, city, page, offset)
|
||||
@@ -222,8 +287,10 @@ func (s *poiService) fetchAroundPage(ctx context.Context, apiKey, baseUrl, keywo
|
||||
}
|
||||
defer response.Close()
|
||||
|
||||
bodyBytes := response.ReadAll()
|
||||
|
||||
var amapResp amapResponse
|
||||
if err := json.Unmarshal(response.ReadAll(), &amapResp); err != nil {
|
||||
if err := json.Unmarshal(bodyBytes, &amapResp); err != nil {
|
||||
g.Log().Warning(ctx, "高德API响应解析失败", "error", err)
|
||||
return nil, 0, err
|
||||
}
|
||||
@@ -257,8 +324,10 @@ func (s *poiService) fetchAmapPage(ctx context.Context, apiKey, baseUrl, keyword
|
||||
}
|
||||
defer response.Close()
|
||||
|
||||
bodyBytes := response.ReadAll()
|
||||
|
||||
var amapResp amapResponse
|
||||
if err := json.Unmarshal(response.ReadAll(), &amapResp); err != nil {
|
||||
if err := json.Unmarshal(bodyBytes, &amapResp); err != nil {
|
||||
g.Log().Warning(ctx, "高德API响应解析失败", "error", err)
|
||||
return nil, 0
|
||||
}
|
||||
|
||||
+7
-1
@@ -270,7 +270,13 @@ watch(city, (val, oldVal) => {
|
||||
centerLat.value = 0
|
||||
centerLng.value = 0
|
||||
mapRef.value.clearRadiusCircle()
|
||||
mapRef.value.flyTo(coord[0], coord[1], 12)
|
||||
})
|
||||
|
||||
// 范围变更时只重绘圆圈大小,不改变地图视角
|
||||
watch(radius, (val) => {
|
||||
if (centerLng.value && centerLat.value && mapRef.value) {
|
||||
mapRef.value.updateRadiusCircle(val)
|
||||
}
|
||||
})
|
||||
|
||||
async function doSearch() {
|
||||
|
||||
@@ -57,7 +57,6 @@ function addMarkers(pois) {
|
||||
clearMarkers()
|
||||
if (!map || !pois.length) return
|
||||
|
||||
const bounds = L.latLngBounds()
|
||||
const pinIcon = L.divIcon({
|
||||
className: '',
|
||||
html: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 42" width="32" height="42">
|
||||
@@ -92,12 +91,6 @@ function addMarkers(pois) {
|
||||
bounds.extend([lat, lng])
|
||||
})
|
||||
|
||||
// 自动调整视野
|
||||
if (pois.length === 1) {
|
||||
map.setView(bounds.getCenter(), 15)
|
||||
} else {
|
||||
map.fitBounds(bounds, { padding: [40, 40], maxZoom: 16 })
|
||||
}
|
||||
}
|
||||
|
||||
function focusMarker(poi) {
|
||||
@@ -105,7 +98,6 @@ function focusMarker(poi) {
|
||||
const lng = parseFloat(poi.longitude)
|
||||
if (isNaN(lat) || isNaN(lng)) return
|
||||
|
||||
map.setView([lat, lng], 16)
|
||||
for (const marker of currentMarkers) {
|
||||
const pos = marker.getLatLng()
|
||||
if (Math.abs(pos.lat - lat) < 0.001 && Math.abs(pos.lng - lng) < 0.001) {
|
||||
@@ -133,7 +125,6 @@ function drawRadiusCircle(lat, lng, radiusMeters) {
|
||||
weight: 2,
|
||||
dashArray: '6, 6',
|
||||
}).addTo(map)
|
||||
map.fitBounds(radiusCircle.getBounds(), { padding: [40, 40] })
|
||||
}
|
||||
|
||||
function clearRadiusCircle() {
|
||||
@@ -143,7 +134,14 @@ function clearRadiusCircle() {
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({ addMarkers, clearMarkers, focusMarker, drawRadiusCircle, clearRadiusCircle, flyTo })
|
||||
// 只更新已有圆圈的大小,不改变地图视角
|
||||
function updateRadiusCircle(radiusMeters) {
|
||||
if (radiusCircle) {
|
||||
radiusCircle.setRadius(radiusMeters)
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({ addMarkers, clearMarkers, focusMarker, drawRadiusCircle, clearRadiusCircle, updateRadiusCircle, flyTo })
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
Reference in New Issue
Block a user