From 39953cca84cdb372e9fb1d48370f44c8ed5bc8c3 Mon Sep 17 00:00:00 2001 From: guotao <499836921@qq.com> Date: Mon, 12 Jan 2026 18:50:22 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=E4=BB=A5=E4=B8=8B=E7=AC=AC?= =?UTF-8?q?=E4=B8=89=E6=96=B9=E6=8E=A5=E5=8F=A3=E5=AF=B9=E6=8E=A5:=201.?= =?UTF-8?q?=E5=9C=B0=E5=9B=BE=E5=8A=9F=E8=83=BD-=E5=9C=B0=E7=90=86/?= =?UTF-8?q?=E9=80=86=E5=9C=B0=E7=90=86=E7=BC=96=E7=A0=81=202.=E5=8F=91?= =?UTF-8?q?=E9=80=81=E7=9F=AD=E4=BF=A1=E9=AA=8C=E8=AF=81=E7=A0=81=203.COS?= =?UTF-8?q?=E4=BA=91=E5=AF=B9=E8=B1=A1=E5=AD=98=E5=82=A8=E5=AF=B9=E6=8E=A5?= =?UTF-8?q?=20=E5=85=B6=E4=BB=96:=20=E5=AE=8C=E6=88=90=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E4=B8=8A=E4=BC=A0/=E9=A2=84=E8=A7=88=E5=85=AC=E5=85=B1?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 34 +++ .../common/openapi/TencentCOSService.java | 75 +++++++ .../openapi/TencentCloudSMSService.java | 57 +++++ .../common/openapi/amap/AmapFeignClient.java | 35 +++ .../common/openapi/amap/AmapService.java | 39 ++++ .../amap/response/AmapGeocodeResponse.java | 95 ++++++++ .../amap/response/AmapReGeocodeResponse.java | 207 ++++++++++++++++++ .../openapi/dto/CosPutObjectResult.java | 13 ++ .../findmemerchant/config/FeignConfig.java | 13 ++ .../config/ThirdOpenApiConfig.java | 36 +++ src/main/resources/application.yml | 19 +- 11 files changed, 622 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/xjhs/findmemerchant/common/openapi/TencentCOSService.java create mode 100644 src/main/java/com/xjhs/findmemerchant/common/openapi/TencentCloudSMSService.java create mode 100644 src/main/java/com/xjhs/findmemerchant/common/openapi/amap/AmapFeignClient.java create mode 100644 src/main/java/com/xjhs/findmemerchant/common/openapi/amap/AmapService.java create mode 100644 src/main/java/com/xjhs/findmemerchant/common/openapi/amap/response/AmapGeocodeResponse.java create mode 100644 src/main/java/com/xjhs/findmemerchant/common/openapi/amap/response/AmapReGeocodeResponse.java create mode 100644 src/main/java/com/xjhs/findmemerchant/common/openapi/dto/CosPutObjectResult.java create mode 100644 src/main/java/com/xjhs/findmemerchant/config/FeignConfig.java create mode 100644 src/main/java/com/xjhs/findmemerchant/config/ThirdOpenApiConfig.java diff --git a/pom.xml b/pom.xml index 28f0542..ab1ae6b 100644 --- a/pom.xml +++ b/pom.xml @@ -17,7 +17,25 @@ 25 + + + + + + org.springframework.cloud + spring-cloud-dependencies + 2025.0.1 + pom + import + + + + + + org.springframework.cloud + spring-cloud-starter-openfeign + org.springframework.boot spring-boot-starter-web @@ -76,6 +94,22 @@ commons-lang3 3.18.0 + + + com.tencentcloudapi + tencentcloud-sdk-java + 3.1.1396 + + + com.qcloud + cos_api + 5.6.246 + + + org.apache.tika + tika-core + 3.2.3 + diff --git a/src/main/java/com/xjhs/findmemerchant/common/openapi/TencentCOSService.java b/src/main/java/com/xjhs/findmemerchant/common/openapi/TencentCOSService.java new file mode 100644 index 0000000..63ae89c --- /dev/null +++ b/src/main/java/com/xjhs/findmemerchant/common/openapi/TencentCOSService.java @@ -0,0 +1,75 @@ +package com.xjhs.findmemerchant.common.openapi; + +import com.qcloud.cos.COSClient; +import com.qcloud.cos.exception.CosClientException; +import com.qcloud.cos.model.GetObjectRequest; +import com.qcloud.cos.model.PutObjectRequest; +import com.xjhs.findmemerchant.common.openapi.dto.CosPutObjectResult; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import java.io.File; +import java.io.InputStream; +import java.util.UUID; + +@Slf4j +@Service +@RequiredArgsConstructor +public class TencentCOSService { + @Value("${appconfig.tencentCos.bucketName}") + private String bucketName; + private final COSClient tencentCosClient; + + /** + * 文件上传到Cos存储桶 + * + * @param file 文件 + * @return 存储对象信息 + */ + public CosPutObjectResult putObject(File file) throws Exception { + try { + var key = UUID.randomUUID().toString(); + var req = new PutObjectRequest(this.bucketName, key, file); + var result = tencentCosClient.putObject(req); + return new CosPutObjectResult(key, result.getETag()); + } catch (CosClientException e) { + log.error("文件上传到对象存储失败", e); + throw new Exception("文件上传到对象存储失败"); + } + } + + /** + * 获取文件输入流(下载文件) + * @param key 对象key + * @return 输入流 + * @throws Exception 下载失败 + */ + public InputStream getObject(String key) throws Exception{ + try { + var req = new GetObjectRequest(this.bucketName, key); + var cosObject = this.tencentCosClient.getObject(req); + return cosObject.getObjectContent(); + } catch (CosClientException e) { + log.error("从对象存储下载文件失败",e); + throw new Exception("从对象存储下载文件失败"); + } + } + + + /** + * 删除存储对象 + * + * @param key 对象key + * @throws Exception 删除失败 + */ + public void deleteObject(String key) throws Exception { + try { + this.tencentCosClient.deleteObject(this.bucketName, key); + } catch (CosClientException e) { + log.error("文件上传到对象存储失败", e); + throw new Exception("文件上传到对象存储失败"); + } + } +} diff --git a/src/main/java/com/xjhs/findmemerchant/common/openapi/TencentCloudSMSService.java b/src/main/java/com/xjhs/findmemerchant/common/openapi/TencentCloudSMSService.java new file mode 100644 index 0000000..8c4c583 --- /dev/null +++ b/src/main/java/com/xjhs/findmemerchant/common/openapi/TencentCloudSMSService.java @@ -0,0 +1,57 @@ +package com.xjhs.findmemerchant.common.openapi; + + +import com.tencentcloudapi.common.exception.TencentCloudSDKException; +import com.tencentcloudapi.sms.v20190711.models.SendSmsRequest; +import com.tencentcloudapi.sms.v20190711.SmsClient; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +@RequiredArgsConstructor() +public class TencentCloudSMSService { + @Value("${appconfig.tencentSms.sdkAppId}") + private String sdkAppid; + @Value("${appconfig.tencentSms.templateId}") + private String templateId; + @Value("${appconfig.tencentSms.signName}") + private String signName; + private final SmsClient tencentSmsClient; + + + /** + * 发送短信验证码 + * + * @param phone 11位国内手机号码 + * @param code 验证码 + * @return 验证码值 + * @throws Exception 发送失败 + */ + public String sendVerifyCode(String phone, String code) throws Exception { + try { + if (!phone.startsWith("+86")) { + phone = "+86" + phone; + } + var req = new SendSmsRequest(); + req.setSmsSdkAppid(this.sdkAppid); + req.setTemplateID(this.templateId); + req.setSign(this.signName); + req.setPhoneNumberSet(new String[]{phone}); + req.setTemplateParamSet(new String[]{code}); + var resp = this.tencentSmsClient.SendSms(req); + if (resp.getSendStatusSet().length == 0){ + throw new Exception("取回短信发送结果失败"); + } + if (!"ok".equalsIgnoreCase(resp.getSendStatusSet()[0].getCode())){ + throw new Exception(resp.getSendStatusSet()[0].getMessage()); + } + return code; + } catch (TencentCloudSDKException e) { + log.error("验证码发送失败", e); + throw new Exception("系统错误,验证码发送失败"); + } + } +} diff --git a/src/main/java/com/xjhs/findmemerchant/common/openapi/amap/AmapFeignClient.java b/src/main/java/com/xjhs/findmemerchant/common/openapi/amap/AmapFeignClient.java new file mode 100644 index 0000000..a98d2bf --- /dev/null +++ b/src/main/java/com/xjhs/findmemerchant/common/openapi/amap/AmapFeignClient.java @@ -0,0 +1,35 @@ +package com.xjhs.findmemerchant.common.openapi.amap; + + +import com.xjhs.findmemerchant.common.openapi.amap.response.AmapGeocodeResponse; +import com.xjhs.findmemerchant.common.openapi.amap.response.AmapReGeocodeResponse; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; + +/** + * 高德地图服务接口 + */ +@FeignClient(name = "amapFeignClient", url = "https://restapi.amap.com") +public interface AmapFeignClient { + + /** + * 地理编码 API 服务 + * @param key 高德Key + * @param address 结构化地址信息,规则遵循:国家、省份、城市、区县、城镇、乡村、街道、门牌号码、屋邨、大厦,如:北京市朝阳区阜通东大街6号。 + * @return 响应信息 + */ + @GetMapping("/v3/geocode/geo") + AmapGeocodeResponse getGeo(@RequestParam(name = "key") String key, + @RequestParam(name = "address") String address); + + + /** + * 逆地理编码 API 服务地址 + * @param key 高德Key + * @param location 经纬度坐标 传入内容规则:经度在前,纬度在后,经纬度间以“,”分割,经纬度小数点后不要超过 6 位。 + */ + @GetMapping("/v3/geocode/regeo") + AmapReGeocodeResponse getReGeo(@RequestParam(name = "key") String key, + @RequestParam(name = "location") String location); +} diff --git a/src/main/java/com/xjhs/findmemerchant/common/openapi/amap/AmapService.java b/src/main/java/com/xjhs/findmemerchant/common/openapi/amap/AmapService.java new file mode 100644 index 0000000..a9583fb --- /dev/null +++ b/src/main/java/com/xjhs/findmemerchant/common/openapi/amap/AmapService.java @@ -0,0 +1,39 @@ +package com.xjhs.findmemerchant.common.openapi.amap; + +import com.xjhs.findmemerchant.common.openapi.amap.response.AmapGeocodeResponse; +import com.xjhs.findmemerchant.common.openapi.amap.response.AmapReGeocodeResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +@RequiredArgsConstructor +public class AmapService { + + @Value("${appconfig.amapKey}") + private String amapKey = "c618de6e686c43095a8593db836c7de2"; + private final AmapFeignClient amapFeignClient; + + /** + * 地址转经纬度 + * + * @param address 地址信息 + * @return 经纬度信息结果 + */ + public AmapGeocodeResponse getGeo(String address) { + return this.amapFeignClient.getGeo(this.amapKey, address); + } + + /** + * 经纬度转地理位置信息 + * + * @param lng 经度 + * @param lat 纬度 + * @return 位置信息 + */ + public AmapReGeocodeResponse getRegeo(double lng, double lat) { + return this.amapFeignClient.getReGeo(this.amapKey, lng + "," + lat); + } +} diff --git a/src/main/java/com/xjhs/findmemerchant/common/openapi/amap/response/AmapGeocodeResponse.java b/src/main/java/com/xjhs/findmemerchant/common/openapi/amap/response/AmapGeocodeResponse.java new file mode 100644 index 0000000..0969541 --- /dev/null +++ b/src/main/java/com/xjhs/findmemerchant/common/openapi/amap/response/AmapGeocodeResponse.java @@ -0,0 +1,95 @@ +package com.xjhs.findmemerchant.common.openapi.amap.response; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; + +/** + * 地理编码 API 服务响应内容 + */ +@Data +public class AmapGeocodeResponse { + /** + * 请求状态值,0 表示请求失败,1 表示请求成功 + */ + private int status; + + /** + * 返回结果的数量 + */ + private int count; + + /** + * 返回结果的详细说明,当 status 为 0 时,info 会包含错误原因;否则返回 "OK" + */ + private String info; + + /** + * 地理编码信息列表,包含多个地址组件 + */ + private List geocodes = new ArrayList<>(); + + /** + * 地理编码信息的详细字段 + */ + @Data + public static class GeocodeDetails { + + @JsonProperty("formatted_address") + private String address; + + /** + * 国家,默认返回中国 + */ + private String country; + + /** + * 省份名称,例如:北京市 + */ + private String province; + + /** + * 城市名称,例如:北京市 + */ + private String city; + + /** + * 城市编码,例如:010 + */ + private String citycode; + + /** + * 区域名称,例如:朝阳区 + */ + private String district; + + /** + * 街道名称,例如:单通东大街 + */ + private String street; + + /** + * 门牌号,例如:6号 + */ + private String number; + + /** + * 区域编码,例如:110101 + */ + private String adcode; + + /** + * 经纬度坐标,逗号分隔 + */ + private String location; + + /** + * 匹配级别,用于地理匹配的精确度 + */ + private String level; + } + + +} diff --git a/src/main/java/com/xjhs/findmemerchant/common/openapi/amap/response/AmapReGeocodeResponse.java b/src/main/java/com/xjhs/findmemerchant/common/openapi/amap/response/AmapReGeocodeResponse.java new file mode 100644 index 0000000..11d01dd --- /dev/null +++ b/src/main/java/com/xjhs/findmemerchant/common/openapi/amap/response/AmapReGeocodeResponse.java @@ -0,0 +1,207 @@ +package com.xjhs.findmemerchant.common.openapi.amap.response; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; + +/** + * 逆地理位置服务响应内容 + */ +@Data +public class AmapReGeocodeResponse { + + /** + * 请求状态,1 表示请求成功 + */ + private String status; + + /** + * 地址组件信息 + */ + private Regeocode regeocode; + + /** + * 返回的状态信息 + */ + private String info; + + /** + * 状态码,10000 表示成功 + */ + private String infocode; + + /** + * 地址组件类,包含具体的地理信息 + */ + @Data + public static class Regeocode { + + /** + * 地址组件,包含城市、区、街道等信息 + */ + private AddressComponent addressComponent; + + /** + * 格式化后的地址,整合了详细信息 + */ + @JsonProperty("formatted_address") + private String address; + } + + /** + * 地址组件,包含详细的地理信息 + */ + @Data + public static class AddressComponent { + + /** + * 城市名称,可能为空 + */ + private List city = new ArrayList<>(); + + /** + * 省份名称 + */ + private String province; + + /** + * 区域编码 + */ + private String adcode; + + /** + * 区域名称 + */ + private String district; + + /** + * 城镇编码 + */ + private String towncode; + + /** + * 街道信息 + */ + private StreetNumber streetNumber; + + /** + * 国家名称 + */ + private String country; + + /** + * 街道所属乡镇名称 + */ + private String township; + + /** + * 商圈信息,包含多个商圈 + */ + private List businessAreas; + + /** + * 建筑信息 + */ + private Building building; + + /** + * 邻里信息 + */ + private Neighborhood neighborhood; + + /** + * 城市编码 + */ + private String citycode; + } + + /** + * 街道信息,包括街道名称、位置、方向等 + */ + @Data + public static class StreetNumber { + + /** + * 门牌号 + */ + private String number; + + /** + * 经纬度位置 + */ + private String location; + + /** + * 方向 + */ + private String direction; + + /** + * 距离 + */ + private String distance; + + /** + * 街道名称 + */ + private String street; + } + + /** + * 商圈信息 + */ + @Data + public static class BusinessArea { + + /** + * 商圈位置(经纬度) + */ + private String location; + + /** + * 商圈名称 + */ + private String name; + + /** + * 商圈ID + */ + private String id; + } + + /** + * 建筑信息,包括建筑名称和类型 + */ + @Data + public static class Building { + + /** + * 建筑名称 + */ + private String name; + + /** + * 建筑类型 + */ + private String type; + } + + /** + * 邻里信息 + */ + @Data + public static class Neighborhood { + + /** + * 邻里名称 + */ + private String name; + + /** + * 邻里类型 + */ + private String type; + } +} diff --git a/src/main/java/com/xjhs/findmemerchant/common/openapi/dto/CosPutObjectResult.java b/src/main/java/com/xjhs/findmemerchant/common/openapi/dto/CosPutObjectResult.java new file mode 100644 index 0000000..4ff570a --- /dev/null +++ b/src/main/java/com/xjhs/findmemerchant/common/openapi/dto/CosPutObjectResult.java @@ -0,0 +1,13 @@ +package com.xjhs.findmemerchant.common.openapi.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class CosPutObjectResult { + private String key; + private String eTag; +} diff --git a/src/main/java/com/xjhs/findmemerchant/config/FeignConfig.java b/src/main/java/com/xjhs/findmemerchant/config/FeignConfig.java new file mode 100644 index 0000000..d3790a1 --- /dev/null +++ b/src/main/java/com/xjhs/findmemerchant/config/FeignConfig.java @@ -0,0 +1,13 @@ +package com.xjhs.findmemerchant.config; + +import com.xjhs.findmemerchant.common.openapi.amap.AmapFeignClient; +import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.context.annotation.Configuration; + +@Configuration +@EnableFeignClients(basePackageClasses = { + AmapFeignClient.class, +}) +public class FeignConfig { + +} diff --git a/src/main/java/com/xjhs/findmemerchant/config/ThirdOpenApiConfig.java b/src/main/java/com/xjhs/findmemerchant/config/ThirdOpenApiConfig.java new file mode 100644 index 0000000..6c66601 --- /dev/null +++ b/src/main/java/com/xjhs/findmemerchant/config/ThirdOpenApiConfig.java @@ -0,0 +1,36 @@ +package com.xjhs.findmemerchant.config; + +import com.qcloud.cos.COSClient; +import com.qcloud.cos.ClientConfig; +import com.qcloud.cos.auth.BasicCOSCredentials; +import com.tencentcloudapi.common.Credential; +import com.tencentcloudapi.common.profile.ClientProfile; +import com.tencentcloudapi.common.profile.HttpProfile; +import com.tencentcloudapi.common.profile.Region; +import com.tencentcloudapi.sms.v20190711.SmsClient; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class ThirdOpenApiConfig { + + @Bean + public SmsClient tencentSmsClient(@Value("${appconfig.tencentSms.secretId}") String secretId, + @Value("${appconfig.tencentSms.secretKey}") String secretKey) { + var cred = new Credential(secretId, secretKey); + var httpProfile = new HttpProfile(); + httpProfile.setEndpoint("sms.tencentcloudapi.com"); + var clientProfile = new ClientProfile(); + clientProfile.setHttpProfile(httpProfile); + return new SmsClient(cred, "ap-guangzhou", clientProfile); + } + + @Bean + public COSClient tencentCosClient(@Value("${appconfig.tencentCos.secretId}") String secretId, + @Value("${appconfig.tencentCos.secretKey}") String secretKey){ + var cred = new BasicCOSCredentials(secretId,secretKey); + ClientConfig clientConfig = new ClientConfig(new com.qcloud.cos.region.Region("ap-guangzhou")); + return new COSClient(cred, clientConfig); + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 8c6d574..6242563 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -30,4 +30,21 @@ spring: port: ${REDIS_PORT:6379} password: ${REDIS_PASS:123456} database: 0 - timeout: 3s \ No newline at end of file + timeout: 3s + +appconfig: + saveFileRoot: ${SAVE_FILE_ROOT:./file-data} + amapKey: ${AMAP_KEY:c618de6e686c43095a8593db836c7de2} + tencentSms: + sdkAppId: ${SMS_SDKAPP_ID:1401031336} + templateId: ${SMS_TEMPLATE_ID:2512787} + signName: ${SMS_SIGN_NAME:新疆火烁智能科技} + secretId: ${SMS_SECRET_ID:AKIDWyGMFwQinhPFzXt54rTuAD5kEYheOyOd} + secretKey: ${SMS_SECRET_KEY:62mLjvwmQs9GAsQ5f6LQ7umgWCgy31sX} + tencentCos: + secretId: ${COS_SECRET_ID:AKIDWyGMFwQinhPFzXt54rTuAD5kEYheOyOd} + secretKey: ${COS_SECRET_KEY:62mLjvwmQs9GAsQ5f6LQ7umgWCgy31sX} + appId: ${COS_APP_ID:1375214531} + bucketName: ${COS_APP_BUCKET:merchant-1375214531} + +