diff --git a/amap/service/poi_service.go b/amap/service/poi_service.go index 4ced67f..fe8b9fb 100644 --- a/amap/service/poi_service.go +++ b/amap/service/poi_service.go @@ -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 } diff --git a/web/src/App.vue b/web/src/App.vue index 87664ce..9405718 100644 --- a/web/src/App.vue +++ b/web/src/App.vue @@ -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() { diff --git a/web/src/components/PoiMap.vue b/web/src/components/PoiMap.vue index 735afcc..95ca0f5 100644 --- a/web/src/components/PoiMap.vue +++ b/web/src/components/PoiMap.vue @@ -57,7 +57,6 @@ function addMarkers(pois) { clearMarkers() if (!map || !pois.length) return - const bounds = L.latLngBounds() const pinIcon = L.divIcon({ className: '', html: ` @@ -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 })