完成以下第三方接口对接:

1.地图功能-地理/逆地理编码
2.发送短信验证码
3.COS云对象存储对接
其他:
完成文件上传/预览公共接口
This commit is contained in:
guotao 2026-01-12 18:50:22 +08:00
parent 0edffa145b
commit 39953cca84
11 changed files with 622 additions and 1 deletions

34
pom.xml
View file

@ -17,7 +17,25 @@
<properties> <properties>
<java.version>25</java.version> <java.version>25</java.version>
</properties> </properties>
<dependencyManagement>
<dependencies> <dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>2025.0.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId> <artifactId>spring-boot-starter-web</artifactId>
@ -76,6 +94,22 @@
<artifactId>commons-lang3</artifactId> <artifactId>commons-lang3</artifactId>
<version>3.18.0</version> <version>3.18.0</version>
</dependency> </dependency>
<!--腾讯云SDK-->
<dependency>
<groupId>com.tencentcloudapi</groupId>
<artifactId>tencentcloud-sdk-java</artifactId>
<version>3.1.1396</version>
</dependency>
<dependency>
<groupId>com.qcloud</groupId>
<artifactId>cos_api</artifactId>
<version>5.6.246</version>
</dependency>
<dependency>
<groupId>org.apache.tika</groupId>
<artifactId>tika-core</artifactId>
<version>3.2.3</version>
</dependency>
</dependencies> </dependencies>
<build> <build>

View file

@ -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("文件上传到对象存储失败");
}
}
}

View file

@ -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("系统错误,验证码发送失败");
}
}
}

View file

@ -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);
}

View file

@ -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);
}
}

View file

@ -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<GeocodeDetails> 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;
}
}

View file

@ -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<String> 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<BusinessArea> 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;
}
}

View file

@ -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;
}

View file

@ -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 {
}

View file

@ -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);
}
}

View file

@ -31,3 +31,20 @@ spring:
password: ${REDIS_PASS:123456} password: ${REDIS_PASS:123456}
database: 0 database: 0
timeout: 3s 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}