补充客户信息
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
package com.rj;
|
package com.rj;
|
||||||
|
|
||||||
|
import com.rj.config.AmapProperties;
|
||||||
import com.rj.config.YihangyiVllmAsrProperties;
|
import com.rj.config.YihangyiVllmAsrProperties;
|
||||||
import org.mybatis.spring.annotation.MapperScan;
|
import org.mybatis.spring.annotation.MapperScan;
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
@@ -39,7 +40,7 @@ import org.springframework.scheduling.annotation.EnableScheduling;
|
|||||||
@MapperScan("com.rj.mapper")
|
@MapperScan("com.rj.mapper")
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
@EnableScheduling
|
@EnableScheduling
|
||||||
@EnableConfigurationProperties(YihangyiVllmAsrProperties.class)
|
@EnableConfigurationProperties({YihangyiVllmAsrProperties.class, AmapProperties.class})
|
||||||
public class AISmartCard20251230Application {
|
public class AISmartCard20251230Application {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
|||||||
17
src/main/java/com/rj/config/AmapProperties.java
Normal file
17
src/main/java/com/rj/config/AmapProperties.java
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package com.rj.config;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 高德开放平台 Web 服务 Key(地理编码、周边搜索等)。
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ConfigurationProperties(prefix = "amap")
|
||||||
|
public class AmapProperties {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Web 服务类型的 Key,对应控制台「应用 Key」。
|
||||||
|
*/
|
||||||
|
private String webServiceKey = "";
|
||||||
|
}
|
||||||
@@ -6,11 +6,13 @@ import com.rj.common.AudioManagementConstants;
|
|||||||
import com.rj.common.DataEnrichmentUtil;
|
import com.rj.common.DataEnrichmentUtil;
|
||||||
import com.rj.dto.CustomerPhotoPreviewResult;
|
import com.rj.dto.CustomerPhotoPreviewResult;
|
||||||
import com.rj.dto.CustomerPhotoUploadResult;
|
import com.rj.dto.CustomerPhotoUploadResult;
|
||||||
|
import com.rj.dto.NearbyMerchantSearchResult;
|
||||||
import com.rj.entity.AudioManagement;
|
import com.rj.entity.AudioManagement;
|
||||||
import com.rj.entity.CustomerManagement;
|
import com.rj.entity.CustomerManagement;
|
||||||
import com.rj.entity.SalesManagement;
|
import com.rj.entity.SalesManagement;
|
||||||
import com.rj.service.IAudioManagementService;
|
import com.rj.service.IAudioManagementService;
|
||||||
import com.rj.service.ICustomerManagementService;
|
import com.rj.service.ICustomerManagementService;
|
||||||
|
import com.rj.service.INearbyMerchantSearchService;
|
||||||
import com.rj.service.ISalesManagementService;
|
import com.rj.service.ISalesManagementService;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
@@ -53,6 +55,9 @@ public class CustomerManagementController {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private ISalesManagementService salesManagementService;
|
private ISalesManagementService salesManagementService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private INearbyMerchantSearchService nearbyMerchantSearchService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 新增客户
|
* 新增客户
|
||||||
*/
|
*/
|
||||||
@@ -202,6 +207,30 @@ public class CustomerManagementController {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private DataEnrichmentUtil dataEnrichmentUtil;
|
private DataEnrichmentUtil dataEnrichmentUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据城市与地址做地理编码,再按半径搜索周边商家(高德 place/around,逻辑同 AllShopCollectorV5)。
|
||||||
|
*/
|
||||||
|
@GetMapping("/searchNearbyMerchants")
|
||||||
|
@Operation(summary = "附近商家检索", description = "根据 CITY、ADDRESS 解析坐标,按 RADIUS、MAX_PAGE、maxRecordsOfpage 调用高德周边搜索")
|
||||||
|
public ResponseEntity<Map<String, Object>> searchNearbyMerchants(
|
||||||
|
@Parameter(description = "城市(传给高德地理编码 city)", required = true) @RequestParam("CITY") String city,
|
||||||
|
@Parameter(description = "地址/地标(传给高德地理编码 address)", required = true) @RequestParam("ADDRESS") String address,
|
||||||
|
@Parameter(description = "搜索半径(米)", required = true) @RequestParam("RADIUS") int radius,
|
||||||
|
@Parameter(description = "最大请求页数", required = true) @RequestParam("MAX_PAGE") int maxPage,
|
||||||
|
@Parameter(description = "每页条数(1~50,高德 offset)", required = true) @RequestParam("maxRecordsOfpage") int maxRecordsOfpage) {
|
||||||
|
NearbyMerchantSearchResult search = nearbyMerchantSearchService.searchNearbyMerchants(
|
||||||
|
city, address, radius, maxPage, maxRecordsOfpage);
|
||||||
|
Map<String, Object> result = new HashMap<>();
|
||||||
|
result.put("success", search.isSuccess());
|
||||||
|
result.put("message", search.getMessage());
|
||||||
|
if (search.isSuccess()) {
|
||||||
|
result.put("centerLocation", search.getCenterLocation());
|
||||||
|
result.put("totalCount", search.getTotalCount());
|
||||||
|
result.put("data", search.getMerchants());
|
||||||
|
}
|
||||||
|
return ResponseEntity.status(search.getHttpStatus()).body(result);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 分页查询客户列表
|
* 分页查询客户列表
|
||||||
*/
|
*/
|
||||||
|
|||||||
21
src/main/java/com/rj/dto/NearbyMerchantItem.java
Normal file
21
src/main/java/com/rj/dto/NearbyMerchantItem.java
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
package com.rj.dto;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 高德周边 POI 中的一条商家记录(与 {@code AllShopCollectorV5} 解析字段一致)。
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class NearbyMerchantItem {
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
private String address;
|
||||||
|
private String tel;
|
||||||
|
private String type;
|
||||||
|
/** 经纬度,格式 "lng,lat" */
|
||||||
|
private String location;
|
||||||
|
}
|
||||||
43
src/main/java/com/rj/dto/NearbyMerchantSearchResult.java
Normal file
43
src/main/java/com/rj/dto/NearbyMerchantSearchResult.java
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
package com.rj.dto;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据城市+地址做地理编码后,按半径周边搜索商家的结果。
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
public class NearbyMerchantSearchResult {
|
||||||
|
|
||||||
|
private final int httpStatus;
|
||||||
|
private final boolean success;
|
||||||
|
private final String message;
|
||||||
|
private final String centerLocation;
|
||||||
|
private final List<NearbyMerchantItem> merchants;
|
||||||
|
/** 高德返回的本次检索总条数(字符串),可能大于已拉取页数之和 */
|
||||||
|
private final String totalCount;
|
||||||
|
|
||||||
|
private NearbyMerchantSearchResult(int httpStatus, boolean success, String message,
|
||||||
|
String centerLocation, List<NearbyMerchantItem> merchants, String totalCount) {
|
||||||
|
this.httpStatus = httpStatus;
|
||||||
|
this.success = success;
|
||||||
|
this.message = message;
|
||||||
|
this.centerLocation = centerLocation;
|
||||||
|
this.merchants = merchants == null ? Collections.emptyList() : merchants;
|
||||||
|
this.totalCount = totalCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static NearbyMerchantSearchResult ok(String centerLocation, List<NearbyMerchantItem> merchants, String totalCount) {
|
||||||
|
return new NearbyMerchantSearchResult(200, true, "查询成功", centerLocation, merchants, totalCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static NearbyMerchantSearchResult badRequest(String message) {
|
||||||
|
return new NearbyMerchantSearchResult(400, false, message, null, Collections.emptyList(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static NearbyMerchantSearchResult serverError(String message) {
|
||||||
|
return new NearbyMerchantSearchResult(500, false, message, null, Collections.emptyList(), null);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package com.rj.service;
|
||||||
|
|
||||||
|
import com.rj.dto.NearbyMerchantSearchResult;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基于高德地图:地址转坐标 + 周边全行业 POI(商家)检索。
|
||||||
|
*/
|
||||||
|
public interface INearbyMerchantSearchService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据城市、详细地址得到中心点,再按半径分页拉取周边商家。
|
||||||
|
*
|
||||||
|
* @param city 城市(传给地理编码 city 参数,如「河北省廊坊市三河市燕郊镇」)
|
||||||
|
* @param address 地址/地标(传给地理编码 address 参数)
|
||||||
|
* @param radiusMeters 周边搜索半径(米)
|
||||||
|
* @param maxPage 最多请求页数(从 1 递增)
|
||||||
|
* @param maxRecordsPerPage 每页条数,高德允许 1~50,超出会被裁剪
|
||||||
|
*/
|
||||||
|
NearbyMerchantSearchResult searchNearbyMerchants(String city, String address,
|
||||||
|
int radiusMeters, int maxPage, int maxRecordsPerPage);
|
||||||
|
}
|
||||||
@@ -0,0 +1,154 @@
|
|||||||
|
package com.rj.service.impl;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.rj.config.AmapProperties;
|
||||||
|
import com.rj.dto.NearbyMerchantItem;
|
||||||
|
import com.rj.dto.NearbyMerchantSearchResult;
|
||||||
|
import com.rj.service.INearbyMerchantSearchService;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
import org.springframework.web.util.UriComponentsBuilder;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 逻辑参考 {@link com.rj.gaode.AllShopCollectorV5}:地理编码 + place/around 全行业周边搜索。
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
public class NearbyMerchantSearchServiceImpl implements INearbyMerchantSearchService {
|
||||||
|
|
||||||
|
private static final String GEOCODE_URL = "https://restapi.amap.com/v3/geocode/geo";
|
||||||
|
private static final String AROUND_URL = "https://restapi.amap.com/v3/place/around";
|
||||||
|
private static final int AMAP_OFFSET_MAX = 50;
|
||||||
|
private static final int AMAP_PAGE_DELAY_MS = 800;
|
||||||
|
|
||||||
|
private final RestTemplate restTemplate;
|
||||||
|
private final ObjectMapper objectMapper;
|
||||||
|
private final AmapProperties amapProperties;
|
||||||
|
|
||||||
|
public NearbyMerchantSearchServiceImpl(RestTemplate restTemplate,
|
||||||
|
ObjectMapper objectMapper,
|
||||||
|
AmapProperties amapProperties) {
|
||||||
|
this.restTemplate = restTemplate;
|
||||||
|
this.objectMapper = objectMapper;
|
||||||
|
this.amapProperties = amapProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NearbyMerchantSearchResult searchNearbyMerchants(String city, String address,
|
||||||
|
int radiusMeters, int maxPage, int maxRecordsPerPage) {
|
||||||
|
if (!StringUtils.hasText(amapProperties.getWebServiceKey())) {
|
||||||
|
return NearbyMerchantSearchResult.badRequest("未配置高德 Web 服务 Key,请在配置中设置 amap.web-service-key");
|
||||||
|
}
|
||||||
|
if (!StringUtils.hasText(city) || !StringUtils.hasText(address)) {
|
||||||
|
return NearbyMerchantSearchResult.badRequest("CITY 与 ADDRESS 不能为空");
|
||||||
|
}
|
||||||
|
if (radiusMeters <= 0) {
|
||||||
|
return NearbyMerchantSearchResult.badRequest("RADIUS 须为正整数(米)");
|
||||||
|
}
|
||||||
|
if (maxPage < 1) {
|
||||||
|
return NearbyMerchantSearchResult.badRequest("MAX_PAGE 须大于等于 1");
|
||||||
|
}
|
||||||
|
int offset = Math.min(AMAP_OFFSET_MAX, Math.max(1, maxRecordsPerPage));
|
||||||
|
int pages = Math.min(maxPage, 100);
|
||||||
|
|
||||||
|
try {
|
||||||
|
String location = geocodeToLocation(city.trim(), address.trim());
|
||||||
|
List<NearbyMerchantItem> all = new ArrayList<>();
|
||||||
|
String totalCount = null;
|
||||||
|
for (int page = 1; page <= pages; page++) {
|
||||||
|
String json = fetchAroundPage(location, radiusMeters, offset, page);
|
||||||
|
JsonNode root = objectMapper.readTree(json);
|
||||||
|
if (!"1".equals(root.path("status").asText())) {
|
||||||
|
String info = root.path("info").asText("高德接口返回失败");
|
||||||
|
return NearbyMerchantSearchResult.badRequest("周边搜索失败:" + info);
|
||||||
|
}
|
||||||
|
if (totalCount == null) {
|
||||||
|
totalCount = root.path("count").asText(null);
|
||||||
|
}
|
||||||
|
JsonNode pois = root.path("pois");
|
||||||
|
if (pois.isArray()) {
|
||||||
|
for (JsonNode p : pois) {
|
||||||
|
all.add(new NearbyMerchantItem(
|
||||||
|
text(p, "name"),
|
||||||
|
text(p, "address"),
|
||||||
|
text(p, "tel"),
|
||||||
|
text(p, "type"),
|
||||||
|
text(p, "location")
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (page < pages) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(AMAP_PAGE_DELAY_MS);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
return NearbyMerchantSearchResult.serverError("查询被中断");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NearbyMerchantSearchResult.ok(location, all, totalCount);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
return NearbyMerchantSearchResult.badRequest(e.getMessage());
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("附近商家检索异常: {}", e.getMessage());
|
||||||
|
return NearbyMerchantSearchResult.serverError("附近商家检索异常:" + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String geocodeToLocation(String city, String address) throws Exception {
|
||||||
|
String url = UriComponentsBuilder.fromUriString(GEOCODE_URL)
|
||||||
|
.queryParam("key", amapProperties.getWebServiceKey())
|
||||||
|
.queryParam("city", city)
|
||||||
|
.queryParam("address", address)
|
||||||
|
.queryParam("output", "json")
|
||||||
|
.build()
|
||||||
|
.encode()
|
||||||
|
.toUriString();
|
||||||
|
String json = restTemplate.getForObject(url, String.class);
|
||||||
|
JsonNode root = objectMapper.readTree(json);
|
||||||
|
if (!"1".equals(root.path("status").asText())) {
|
||||||
|
String info = root.path("info").asText("地理编码失败");
|
||||||
|
throw new IllegalArgumentException("地理编码失败:" + info);
|
||||||
|
}
|
||||||
|
JsonNode geocodes = root.path("geocodes");
|
||||||
|
if (!geocodes.isArray() || geocodes.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("未解析到该地址的经纬度,请检查 CITY、ADDRESS");
|
||||||
|
}
|
||||||
|
String loc = geocodes.get(0).path("location").asText("");
|
||||||
|
if (!StringUtils.hasText(loc)) {
|
||||||
|
throw new IllegalArgumentException("地理编码结果中无 location 字段");
|
||||||
|
}
|
||||||
|
return loc;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String fetchAroundPage(String location, int radius, int offset, int page) {
|
||||||
|
String url = UriComponentsBuilder.fromUriString(AROUND_URL)
|
||||||
|
.queryParam("key", amapProperties.getWebServiceKey())
|
||||||
|
.queryParam("location", location)
|
||||||
|
.queryParam("radius", radius)
|
||||||
|
.queryParam("offset", offset)
|
||||||
|
.queryParam("page", page)
|
||||||
|
.queryParam("output", "json")
|
||||||
|
.build()
|
||||||
|
.encode()
|
||||||
|
.toUriString();
|
||||||
|
String body = restTemplate.getForObject(url, String.class);
|
||||||
|
if (body == null) {
|
||||||
|
throw new IllegalStateException("高德周边搜索返回空响应");
|
||||||
|
}
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String text(JsonNode node, String field) {
|
||||||
|
if (node == null || !node.has(field)) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return node.get(field).asText("");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -271,6 +271,10 @@ dify:
|
|||||||
top-sales-alldim-token: app-99nVuuKfcRR2vikxgOTiNQm0
|
top-sales-alldim-token: app-99nVuuKfcRR2vikxgOTiNQm0
|
||||||
classification-token: app-Id1eOuv5yBb1pexpEWVDqSS3
|
classification-token: app-Id1eOuv5yBb1pexpEWVDqSS3
|
||||||
|
|
||||||
|
# 高德地图 Web 服务(地理编码、周边 POI);本地可设环境变量 AMAP_WEB_SERVICE_KEY
|
||||||
|
amap:
|
||||||
|
web-service-key: ${AMAP_WEB_SERVICE_KEY:}
|
||||||
|
|
||||||
# MinIO 对象存储配置
|
# MinIO 对象存储配置
|
||||||
minio:
|
minio:
|
||||||
# MinIO服务器地址
|
# MinIO服务器地址
|
||||||
|
|||||||
114
src/test/java/com/rj/gaode/AllShopCollectorV5.java
Normal file
114
src/test/java/com/rj/gaode/AllShopCollectorV5.java
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
package com.rj.gaode;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
|
||||||
|
public class AllShopCollectorV5 {
|
||||||
|
|
||||||
|
// ==================== 只需要改这里 5 项 ====================
|
||||||
|
private static final String AMAP_KEY = "34eaa47387aa9e8c1a8ca0d4c8abaa1d"; // 替换成你的Key
|
||||||
|
private static final String CITY = "河北省廊坊市三河市燕郊镇"; // 城市
|
||||||
|
private static final String ADDRESS = "燕顺路星河皓月小区"; // 地标/街道/路(自动转经纬度)
|
||||||
|
private static final int RADIUS = 800; // 半径800米(可改300/500/1000)
|
||||||
|
private static final int MAX_PAGE = 2; // 采集页数
|
||||||
|
// ==========================================================
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
try {
|
||||||
|
System.out.println("正在获取地址:" + ADDRESS + " 的经纬度...");
|
||||||
|
String location = getLocationByAddress(CITY, ADDRESS);
|
||||||
|
System.out.println("经纬度获取成功:" + location);
|
||||||
|
|
||||||
|
// 开始全行业采集
|
||||||
|
for (int page = 1; page <= MAX_PAGE; page++) {
|
||||||
|
System.out.println("\n===== 全量采集第 " + page + " 页商家 =====");
|
||||||
|
String json = getAllShops(location, page);
|
||||||
|
parseAndPrint(json);
|
||||||
|
Thread.sleep(800); // 风控间隔,稳定不封号
|
||||||
|
}
|
||||||
|
System.out.println("\n===== 全行业商家采集完成 =====");
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. 地址自动转经纬度(核心:不用手动打开网页)
|
||||||
|
*/
|
||||||
|
public static String getLocationByAddress(String city, String address) throws Exception {
|
||||||
|
String url = "https://restapi.amap.com/v3/geocode/geo"
|
||||||
|
+ "?key=" + AMAP_KEY
|
||||||
|
+ "&city=" + URLEncoder.encode(city, "UTF-8")
|
||||||
|
+ "&address=" + URLEncoder.encode(address, "UTF-8")
|
||||||
|
+ "&output=json";
|
||||||
|
String json = sendGet(url);
|
||||||
|
int start = json.indexOf("\"location\":\"") + 12;
|
||||||
|
int end = json.indexOf("\"", start);
|
||||||
|
return json.substring(start, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 2. 【不指定行业】搜索周边所有商家(核心)
|
||||||
|
*/
|
||||||
|
public static String getAllShops(String location, int page) throws Exception {
|
||||||
|
String url = "https://restapi.amap.com/v3/place/around" // 周边搜索(无关键词=全行业)
|
||||||
|
+ "?key=" + AMAP_KEY
|
||||||
|
+ "&location=" + location // 中心点经纬度
|
||||||
|
+ "&radius=" + RADIUS // 半径范围
|
||||||
|
+ "&offset=50" // 每页50条
|
||||||
|
+ "&page=" + page // 页码
|
||||||
|
+ "&output=json";
|
||||||
|
return sendGet(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP请求工具
|
||||||
|
*/
|
||||||
|
public static String sendGet(String url) throws Exception {
|
||||||
|
HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
|
||||||
|
conn.setRequestMethod("GET");
|
||||||
|
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
|
||||||
|
String inputLine;
|
||||||
|
StringBuilder content = new StringBuilder();
|
||||||
|
while ((inputLine = in.readLine()) != null) {
|
||||||
|
content.append(inputLine);
|
||||||
|
}
|
||||||
|
in.close();
|
||||||
|
conn.disconnect();
|
||||||
|
return content.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析所有商家信息
|
||||||
|
*/
|
||||||
|
public static void parseAndPrint(String json) {
|
||||||
|
String[] shops = json.split("\"name\":\"");
|
||||||
|
for (int i = 1; i < shops.length; i++) {
|
||||||
|
String s = shops[i];
|
||||||
|
String name = get(s, "");
|
||||||
|
String addr = get(s, "address\":\"");
|
||||||
|
String tel = get(s, "tel\":\"");
|
||||||
|
String type = get(s, "type\":\"");
|
||||||
|
String loc = get(s, "location\":\"");
|
||||||
|
|
||||||
|
System.out.println("店名:" + name);
|
||||||
|
System.out.println("地址:" + addr);
|
||||||
|
System.out.println("电话:" + tel);
|
||||||
|
System.out.println("行业:" + type);
|
||||||
|
System.out.println("坐标:" + loc);
|
||||||
|
System.out.println("---------------------------");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String get(String s, String pre) {
|
||||||
|
try {
|
||||||
|
int i = s.indexOf(pre) + pre.length();
|
||||||
|
return s.substring(i, s.indexOf("\"", i));
|
||||||
|
} catch (Exception e) {
|
||||||
|
return "无";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
133
src/test/java/com/rj/gaode/AutoLocationCollector.java
Normal file
133
src/test/java/com/rj/gaode/AutoLocationCollector.java
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
package com.rj.gaode;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
|
||||||
|
public class AutoLocationCollector {
|
||||||
|
|
||||||
|
// ==================== 配置区(只改这里)====================
|
||||||
|
private static final String AMAP_KEY = "你的Web服务Key";
|
||||||
|
private static final String CITY = "杭州市"; // 城市
|
||||||
|
private static final String ADDRESS = "万达广场"; // 地标/街道/路/建筑(自动转经纬度)
|
||||||
|
private static final String KEYWORDS = "餐饮"; // 招商行业
|
||||||
|
private static final String TYPES = "050000"; // 行业编码
|
||||||
|
private static final int RADIUS = 800; // 半径800米
|
||||||
|
private static final int MAX_PAGE = 3; // 采集页数
|
||||||
|
// ==========================================================
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
try {
|
||||||
|
System.out.println("正在获取地址:" + ADDRESS + " 的经纬度...");
|
||||||
|
// 1. 自动获取经纬度
|
||||||
|
String location = getLocationByAddress(CITY, ADDRESS);
|
||||||
|
System.out.println("获取成功:经纬度 = " + location);
|
||||||
|
|
||||||
|
// 2. 自动采集周边商家
|
||||||
|
for (int page = 1; page <= MAX_PAGE; page++) {
|
||||||
|
System.out.println("\n===== 正在采集第 " + page + " 页商家 =====");
|
||||||
|
String json = getShopList(location, page);
|
||||||
|
parseAndPrint(json);
|
||||||
|
Thread.sleep(800);
|
||||||
|
}
|
||||||
|
System.out.println("\n===== 全自动招商采集完成 =====");
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 高德地理编码API:地址 → 自动获取经纬度
|
||||||
|
*/
|
||||||
|
public static String getLocationByAddress(String city, String address) throws Exception {
|
||||||
|
String url = "https://restapi.amap.com/v3/geocode/geo"
|
||||||
|
+ "?key=" + AMAP_KEY
|
||||||
|
+ "&city=" + URLEncoder.encode(city, "UTF-8")
|
||||||
|
+ "&address=" + URLEncoder.encode(address, "UTF-8")
|
||||||
|
+ "&output=json";
|
||||||
|
|
||||||
|
String json = sendHttpRequest(url);
|
||||||
|
return extractLocation(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 调用POI搜索,获取周边商家
|
||||||
|
*/
|
||||||
|
public static String getShopList(String location, int page) throws Exception {
|
||||||
|
String url = "https://restapi.amap.com/v3/place/text"
|
||||||
|
+ "?key=" + AMAP_KEY
|
||||||
|
+ "&city=" + URLEncoder.encode(CITY, "UTF-8")
|
||||||
|
+ "&keywords=" + URLEncoder.encode(KEYWORDS, "UTF-8")
|
||||||
|
+ "&types=" + TYPES
|
||||||
|
+ "&location=" + location
|
||||||
|
+ "&radius=" + RADIUS
|
||||||
|
+ "&citylimit=true"
|
||||||
|
+ "&offset=50"
|
||||||
|
+ "&page=" + page
|
||||||
|
+ "&output=json";
|
||||||
|
return sendHttpRequest(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统一HTTP请求工具
|
||||||
|
*/
|
||||||
|
public static String sendHttpRequest(String urlStr) throws Exception {
|
||||||
|
URL url = new URL(urlStr);
|
||||||
|
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||||
|
conn.setRequestMethod("GET");
|
||||||
|
conn.setConnectTimeout(5000);
|
||||||
|
conn.setReadTimeout(5000);
|
||||||
|
|
||||||
|
BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
|
||||||
|
String line;
|
||||||
|
StringBuilder result = new StringBuilder();
|
||||||
|
while ((line = br.readLine()) != null) {
|
||||||
|
result.append(line);
|
||||||
|
}
|
||||||
|
br.close();
|
||||||
|
conn.disconnect();
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从地理编码结果中提取经纬度
|
||||||
|
*/
|
||||||
|
public static String extractLocation(String json) {
|
||||||
|
int start = json.indexOf("\"location\":\"") + 12;
|
||||||
|
int end = json.indexOf("\"", start);
|
||||||
|
return json.substring(start, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析并打印商家信息
|
||||||
|
*/
|
||||||
|
public static void parseAndPrint(String json) {
|
||||||
|
String[] shops = json.split("\"name\":\"");
|
||||||
|
for (int i = 1; i < shops.length; i++) {
|
||||||
|
String item = shops[i];
|
||||||
|
String name = getItem(item, "");
|
||||||
|
String address = getItem(item, "address\":\"");
|
||||||
|
String tel = getItem(item, "tel\":\"");
|
||||||
|
String type = getItem(item, "type\":\"");
|
||||||
|
String location = getItem(item, "location\":\"");
|
||||||
|
|
||||||
|
System.out.println("店名:" + name);
|
||||||
|
System.out.println("地址:" + address);
|
||||||
|
System.out.println("电话:" + tel);
|
||||||
|
System.out.println("类型:" + type);
|
||||||
|
System.out.println("坐标:" + location);
|
||||||
|
System.out.println("---------------------------");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getItem(String s, String pre) {
|
||||||
|
try {
|
||||||
|
int i = s.indexOf(pre) + pre.length();
|
||||||
|
return s.substring(i, s.indexOf("\"", i));
|
||||||
|
} catch (Exception e) {
|
||||||
|
return "无";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user