存储新增:腾讯云、华为云

短信新增:腾讯云、华为云
物流新增:快递100
物流查询方式新增:快递查询地图轨迹
This commit is contained in:
pikachu1995@126.com 2023-02-16 18:16:37 +08:00
parent f92756c103
commit 98907b33aa
38 changed files with 2058 additions and 709 deletions

View File

@ -116,6 +116,16 @@ public class OrderBuyerController {
return ResultUtil.data(orderService.getTraces(orderSn)); return ResultUtil.data(orderService.getTraces(orderSn));
} }
@ApiOperation(value = "查询地图版物流踪迹")
@ApiImplicitParams({
@ApiImplicitParam(name = "orderSn", value = "订单编号", required = true, dataType = "String", paramType = "path")
})
@PostMapping(value = "/getMapTraces/{orderSn}")
public ResultMessage<Object> getMapTraces(@NotBlank(message = "订单编号不能为空") @PathVariable String orderSn) {
OperationalJudgment.judgment(orderService.getBySn(orderSn));
return ResultUtil.data(orderService.getMapTraces(orderSn));
}
@PreventDuplicateSubmissions @PreventDuplicateSubmissions
@ApiOperation(value = "开票") @ApiOperation(value = "开票")

View File

@ -437,8 +437,26 @@
<artifactId>minio</artifactId> <artifactId>minio</artifactId>
<version>${minio.version}</version> <version>${minio.version}</version>
</dependency> </dependency>
<dependency>
<groupId>com.huaweicloud</groupId>
<artifactId>esdk-obs-java</artifactId>
<version>${huaweicloud.version}</version>
</dependency>
<dependency>
<groupId>com.qcloud</groupId>
<artifactId>cos_api</artifactId>
<version>${cos.version}</version>
</dependency>
<dependency>
<groupId>com.tencentcloudapi</groupId>
<artifactId>tencentcloud-sdk-java</artifactId>
<version>${tencentcloud.version}</version>
</dependency>
<dependency>
<groupId>com.github.kuaidi100-api</groupId>
<artifactId>sdk</artifactId>
<version>${kuaidi100-api.version}</version>
</dependency>
</dependencies> </dependencies>

View File

@ -193,7 +193,7 @@ public enum ResultCode {
ORDER_NOT_USER(31007, "非当前会员的订单"), ORDER_NOT_USER(31007, "非当前会员的订单"),
ORDER_TAKE_ERROR(31008, "当前订单无法核销"), ORDER_TAKE_ERROR(31008, "当前订单无法核销"),
MEMBER_ADDRESS_NOT_EXIST(31009, "订单无收货地址,请先配置收货地址"), MEMBER_ADDRESS_NOT_EXIST(31009, "订单无收货地址,请先配置收货地址"),
STORE_ADDRESS_NOT_EXIST(31009,"订单没有自提地址,请先选择自提地址"), STORE_ADDRESS_NOT_EXIST(31009, "订单没有自提地址,请先选择自提地址"),
ORDER_DELIVER_NUM_ERROR(31010, "没有待发货的订单"), ORDER_DELIVER_NUM_ERROR(31010, "没有待发货的订单"),
ORDER_NOT_SUPPORT_DISTRIBUTION(31011, "购物车中包含不支持配送的商品,请重新选择收货地址,或者重新选择商品"), ORDER_NOT_SUPPORT_DISTRIBUTION(31011, "购物车中包含不支持配送的商品,请重新选择收货地址,或者重新选择商品"),
ORDER_NOT_EXIST_VALID(31041, "购物车中无有效商品,请检查购物车内商品,或者重新选择商品"), ORDER_NOT_EXIST_VALID(31041, "购物车中无有效商品,请检查购物车内商品,或者重新选择商品"),
@ -202,6 +202,8 @@ public enum ResultCode {
ORDER_ITEM_NOT_EXIST(31014, "当前订单项不存在!"), ORDER_ITEM_NOT_EXIST(31014, "当前订单项不存在!"),
POINT_NOT_ENOUGH(31015, "当前会员积分不足购买当前积分商品!"), POINT_NOT_ENOUGH(31015, "当前会员积分不足购买当前积分商品!"),
ORDER_LABEL_ORDER_ERROR(31016, "订单不能打印电子面单"),
/** /**
* 支付 * 支付

View File

@ -1,7 +1,5 @@
package cn.lili.modules.file.entity.enums; package cn.lili.modules.file.entity.enums;
import com.aliyun.oss.OSS;
/** /**
* OssEnum * OssEnum
* *
@ -11,7 +9,7 @@ import com.aliyun.oss.OSS;
*/ */
public enum OssEnum { public enum OssEnum {
/** /**
* * 上传渠道
*/ */
ALI_OSS, MINIO; ALI_OSS, MINIO, HUAWEI_OBS, TENCENT_COS;
} }

View File

@ -4,7 +4,9 @@ import cn.hutool.json.JSONUtil;
import cn.lili.common.exception.ServiceException; import cn.lili.common.exception.ServiceException;
import cn.lili.modules.file.entity.enums.OssEnum; import cn.lili.modules.file.entity.enums.OssEnum;
import cn.lili.modules.file.plugin.impl.AliFilePlugin; import cn.lili.modules.file.plugin.impl.AliFilePlugin;
import cn.lili.modules.file.plugin.impl.HuaweiFilePlugin;
import cn.lili.modules.file.plugin.impl.MinioFilePlugin; import cn.lili.modules.file.plugin.impl.MinioFilePlugin;
import cn.lili.modules.file.plugin.impl.TencentFilePlugin;
import cn.lili.modules.system.entity.dos.Setting; import cn.lili.modules.system.entity.dos.Setting;
import cn.lili.modules.system.entity.dto.OssSetting; import cn.lili.modules.system.entity.dto.OssSetting;
import cn.lili.modules.system.entity.enums.SettingEnum; import cn.lili.modules.system.entity.enums.SettingEnum;
@ -47,6 +49,10 @@ public class FilePluginFactory {
return new MinioFilePlugin(ossSetting); return new MinioFilePlugin(ossSetting);
case ALI_OSS: case ALI_OSS:
return new AliFilePlugin(ossSetting); return new AliFilePlugin(ossSetting);
case HUAWEI_OBS:
return new HuaweiFilePlugin(ossSetting);
case TENCENT_COS:
return new TencentFilePlugin(ossSetting);
default: default:
throw new ServiceException(); throw new ServiceException();
} }

View File

@ -44,9 +44,9 @@ public class AliFilePlugin implements FilePlugin {
*/ */
private OSS getOssClient() { private OSS getOssClient() {
return new OSSClientBuilder().build( return new OSSClientBuilder().build(
ossSetting.getEndPoint(), ossSetting.getAliyunOSSEndPoint(),
ossSetting.getAccessKeyId(), ossSetting.getAliyunOSSAccessKeyId(),
ossSetting.getAccessKeySecret()); ossSetting.getAliyunOSSAccessKeySecret());
} }
@ -56,14 +56,14 @@ public class AliFilePlugin implements FilePlugin {
* @return * @return
*/ */
private String getUrlPrefix() { private String getUrlPrefix() {
return "https://" + ossSetting.getBucketName() + "." + ossSetting.getEndPoint() + "/"; return "https://" + ossSetting.getAliyunOSSBucketName() + "." + ossSetting.getAliyunOSSEndPoint() + "/";
} }
@Override @Override
public String pathUpload(String filePath, String key) { public String pathUpload(String filePath, String key) {
OSS ossClient = getOssClient(); OSS ossClient = getOssClient();
try { try {
ossClient.putObject(ossSetting.getBucketName(), key, new File(filePath)); ossClient.putObject(ossSetting.getAliyunOSSBucketName(), key, new File(filePath));
} catch (OSSException oe) { } catch (OSSException oe) {
log.error("Caught an OSSException, which means your request made it to OSS, " log.error("Caught an OSSException, which means your request made it to OSS, "
+ "but was rejected with an error response for some reason."); + "but was rejected with an error response for some reason.");
@ -94,7 +94,7 @@ public class AliFilePlugin implements FilePlugin {
try { try {
ObjectMetadata meta = new ObjectMetadata(); ObjectMetadata meta = new ObjectMetadata();
meta.setContentType("image/jpg"); meta.setContentType("image/jpg");
ossClient.putObject(ossSetting.getBucketName(), key, inputStream, meta); ossClient.putObject(ossSetting.getAliyunOSSBucketName(), key, inputStream, meta);
} catch (OSSException oe) { } catch (OSSException oe) {
log.error("Caught an OSSException, which means your request made it to OSS, " log.error("Caught an OSSException, which means your request made it to OSS, "
+ "but was rejected with an error response for some reason."); + "but was rejected with an error response for some reason.");
@ -125,7 +125,7 @@ public class AliFilePlugin implements FilePlugin {
try { try {
ossClient.deleteObjects( ossClient.deleteObjects(
new DeleteObjectsRequest(ossSetting.getBucketName()).withKeys(key)); new DeleteObjectsRequest(ossSetting.getAliyunOSSBucketName()).withKeys(key));
} catch (OSSException oe) { } catch (OSSException oe) {
log.error("Caught an OSSException, which means your request made it to OSS, " log.error("Caught an OSSException, which means your request made it to OSS, "
+ "but was rejected with an error response for some reason."); + "but was rejected with an error response for some reason.");

View File

@ -0,0 +1,128 @@
package cn.lili.modules.file.plugin.impl;
import cn.lili.common.enums.ResultCode;
import cn.lili.common.exception.ServiceException;
import cn.lili.modules.file.entity.enums.OssEnum;
import cn.lili.modules.file.plugin.FilePlugin;
import cn.lili.modules.system.entity.dto.OssSetting;
import com.obs.services.ObsClient;
import com.obs.services.exception.ObsException;
import com.obs.services.model.*;
import lombok.extern.slf4j.Slf4j;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
/**
* 华为obs 文件操作
*
* @author Bulbasaur
*
*
*/
@Slf4j
public class HuaweiFilePlugin implements FilePlugin {
private OssSetting ossSetting;
public HuaweiFilePlugin(OssSetting ossSetting) {
this.ossSetting = ossSetting;
}
@Override
public OssEnum pluginName() {
return OssEnum.HUAWEI_OBS;
}
/**
* 获取oss client
*
* @return
*/
private ObsClient getObsClient() {
return new ObsClient(ossSetting.getHuaweicloudOBSAccessKey(), ossSetting.getHuaweicloudOBSSecretKey(), ossSetting.getAliyunOSSEndPoint());
}
@Override
public String pathUpload(String filePath, String key) {
ObsClient obsClient = getObsClient();
try {
obsClient.putObject(ossSetting.getHuaweicloudOBSBucketName(), key, new File(filePath));
} catch (ObsException ce) {
log.error("Error Message: " + ce.getMessage());
throw new ServiceException(ResultCode.OSS_EXCEPTION_ERROR);
} finally {
try {
// 关闭OBS连接
obsClient.close();
} catch (IOException e) {
log.error("OBS关闭连接报错" + e.getMessage());
throw new ServiceException(ResultCode.OSS_EXCEPTION_ERROR);
}
}
return getUrlPrefix() + key;
}
@Override
public String inputStreamUpload(InputStream inputStream, String key) {
ObsClient obsClient = getObsClient();
try {
obsClient.putObject(new PutObjectRequest(ossSetting.getHuaweicloudOBSBucketName(), key, inputStream));
} catch (ObsException obsException) {
throw new ServiceException(ResultCode.OSS_EXCEPTION_ERROR);
} finally {
try {
// 关闭OBS连接
obsClient.close();
} catch (IOException e) {
log.error("OBS关闭连接报错" + e.getMessage());
throw new ServiceException(ResultCode.OSS_EXCEPTION_ERROR);
}
}
return getUrlPrefix() + key;
}
@Override
public void deleteFile(List<String> keys) {
ObsClient obsClient = getObsClient();
ListVersionsResult result;
try {
DeleteObjectsRequest deleteRequest = new DeleteObjectsRequest(ossSetting.getHuaweicloudOBSBucketName());
//deleteRequest.setQuiet(true); // 注意此demo默认是详细模式如果要使用简单模式请添加本行代码
for (String key : keys) {
deleteRequest.addKeyAndVersion(key);
}
DeleteObjectsResult deleteResult = obsClient.deleteObjects(deleteRequest);
// 获取删除成功的对象
log.info("删除成功:" + deleteResult.getDeletedObjectResults());
// 获取删除失败的对象
log.info("删除失败:" + deleteResult.getErrorResults());
} catch (ObsException obsException) {
throw new ServiceException(ResultCode.OSS_DELETE_ERROR);
} finally {
try {
// 关闭OBS连接
obsClient.close();
} catch (IOException e) {
log.error("OBS关闭连接报错" + e.getMessage());
throw new ServiceException(ResultCode.OSS_EXCEPTION_ERROR);
}
}
}
/**
* 获取配置前缀
*
* @return
*/
private String getUrlPrefix() {
return "https://" + ossSetting.getHuaweicloudOBSBucketName() + "." + ossSetting.getHuaweicloudOBSEndPoint() + "/";
}
}

View File

@ -0,0 +1,122 @@
package cn.lili.modules.file.plugin.impl;
import cn.lili.common.enums.ResultCode;
import cn.lili.common.exception.ServiceException;
import cn.lili.modules.file.entity.enums.OssEnum;
import cn.lili.modules.file.plugin.FilePlugin;
import cn.lili.modules.system.entity.dto.OssSetting;
import com.qcloud.cos.COSClient;
import com.qcloud.cos.ClientConfig;
import com.qcloud.cos.auth.BasicCOSCredentials;
import com.qcloud.cos.auth.COSCredentials;
import com.qcloud.cos.exception.CosClientException;
import com.qcloud.cos.exception.CosServiceException;
import com.qcloud.cos.http.HttpProtocol;
import com.qcloud.cos.model.DeleteObjectsRequest;
import com.qcloud.cos.model.ObjectMetadata;
import com.qcloud.cos.model.PutObjectRequest;
import com.qcloud.cos.region.Region;
import lombok.extern.slf4j.Slf4j;
import java.io.File;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
/**
* 腾讯cos 文件操作
*
* @author Bulbasaur
*/
@Slf4j
public class TencentFilePlugin implements FilePlugin {
private OssSetting ossSetting;
public TencentFilePlugin(OssSetting ossSetting) {
this.ossSetting = ossSetting;
}
@Override
public OssEnum pluginName() {
return OssEnum.TENCENT_COS;
}
/**
* 获取oss client
*
* @return
*/
private COSClient getCOSClient() {
// 1 初始化用户身份信息secretId, secretKey
COSCredentials cred = new BasicCOSCredentials(ossSetting.getTencentCOSSecretId(), ossSetting.getTencentCOSSecretKey());
// 2 设置 bucket 的地域, COS 地域的简称请参见 https://cloud.tencent.com/document/product/436/6224
ClientConfig clientConfig = new ClientConfig(new Region("COS_REGION"));
// 这里建议设置使用 https 协议
clientConfig.setHttpProtocol(HttpProtocol.https);
// 3 生成 cos 客户端
return new COSClient(cred, clientConfig);
}
/**
* 获取配置前缀
*
* @return
*/
private String getUrlPrefix() {
return "https://" + ossSetting.getTencentCOSBucket() + "." + ossSetting.getTencentCOSEndPoint() + "/";
}
@Override
public String pathUpload(String filePath, String key) {
COSClient cosClient = getCOSClient();
try {
cosClient.putObject(ossSetting.getTencentCOSBucket(), key, new File(filePath));
} catch (CosServiceException oe) {
throw new ServiceException(ResultCode.OSS_EXCEPTION_ERROR);
} catch (CosClientException ce) {
throw new ServiceException(ResultCode.OSS_EXCEPTION_ERROR);
} finally {
cosClient.shutdown();
}
return getUrlPrefix() + key;
}
@Override
public String inputStreamUpload(InputStream inputStream, String key) {
COSClient cosClient = getCOSClient();
try {
ObjectMetadata meta = new ObjectMetadata();
meta.setContentType("image/jpg");
cosClient.putObject(ossSetting.getTencentCOSBucket(), key, inputStream, meta);
} catch (CosServiceException oe) {
throw new ServiceException(ResultCode.OSS_EXCEPTION_ERROR);
} catch (CosClientException ce) {
throw new ServiceException(ResultCode.OSS_EXCEPTION_ERROR);
} finally {
cosClient.shutdown();
}
return getUrlPrefix() + key;
}
@Override
public void deleteFile(List<String> keys) {
COSClient cosClient = getCOSClient();
try {
List<DeleteObjectsRequest.KeyVersion> delObjects = new ArrayList<>();
for (String key:keys) {
delObjects.add(new DeleteObjectsRequest.KeyVersion(key));
}
cosClient.deleteObjects(new DeleteObjectsRequest(ossSetting.getTencentCOSBucket()).withKeys(delObjects));
} catch (CosServiceException oe) {
throw new ServiceException(ResultCode.OSS_EXCEPTION_ERROR);
} catch (CosClientException ce) {
throw new ServiceException(ResultCode.OSS_EXCEPTION_ERROR);
} finally {
cosClient.shutdown();
}
}
}

View File

@ -1,17 +0,0 @@
package cn.lili.modules.kdBrid.service;
/**
* 快递鸟电子面单业务层实现
*
* @author chc
* @since 2022-4-12 10:12:43
*/
public interface KdNiaoService {
/**
* 生成电子面单
* @param orderSn 订单编号
* @param logisticsId 物流公司
* @return 电子面单模板
*/
String createElectronicsFaceSheet(String orderSn, String logisticsId) throws Exception;
}

View File

@ -0,0 +1,53 @@
package cn.lili.modules.logistics;
import cn.lili.modules.logistics.entity.dto.LabelOrderDTO;
import cn.lili.modules.logistics.entity.enums.LogisticsEnum;
import cn.lili.modules.system.entity.dos.Logistics;
import cn.lili.modules.system.entity.vo.Traces;
/**
* 物流插件接口
*
* @author Bulbasaur
* @author Bulbasaur
* @since 2023-02-16
*/
public interface LogisticsPlugin {
/**
* 插件名称
*/
LogisticsEnum pluginName();
/**
* 实时查询快递
*
* @param logistics 物流公司
* @param expNo
* @param phone
* @return 物流信息
*/
Traces pollQuery(Logistics logistics, String expNo, String phone);
/**
* 实时查询地图轨迹
*
* @param logistics 物流公司
* @param expNo 单号
* @param phone 收件人手机号
* @param from 出发地信息最小颗粒到市级例如广东省深圳市
* @param to 目的地信息最小颗粒到市级例如广东省深圳市
* @return 物流信息
*/
Traces pollMapTrack(Logistics logistics, String expNo, String phone, String from, String to);
/**
* 电子面单打印
*
* @param labelOrderDTO 电子面单DTO
* @return
*/
String labelOrder(LabelOrderDTO labelOrderDTO);
}

View File

@ -0,0 +1,56 @@
package cn.lili.modules.logistics;
import cn.hutool.json.JSONUtil;
import cn.lili.common.exception.ServiceException;
import cn.lili.modules.logistics.entity.enums.LogisticsEnum;
import cn.lili.modules.logistics.plugin.kdniao.KdniaoPlugin;
import cn.lili.modules.logistics.plugin.kuaidi100.Kuaidi100Plugin;
import cn.lili.modules.system.entity.dos.Setting;
import cn.lili.modules.system.entity.dto.LogisticsSetting;
import cn.lili.modules.system.entity.enums.SettingEnum;
import cn.lili.modules.system.service.SettingService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* 文件服务抽象工厂 直接返回操作类
*
* @author Bulbasaur
* @version v1.0
* 2022-06-06 11:35
*/
@Component
public class LogisticsPluginFactory {
@Autowired
private SettingService settingService;
/**
* 获取logistics client
*
* @return
*/
public LogisticsPlugin filePlugin() {
LogisticsSetting logisticsSetting = null;
try {
Setting setting = settingService.get(SettingEnum.LOGISTICS_SETTING.name());
logisticsSetting = JSONUtil.toBean(setting.getSettingValue(), LogisticsSetting.class);
switch (LogisticsEnum.valueOf(logisticsSetting.getType())) {
case KDNIAO:
return new KdniaoPlugin(logisticsSetting);
case KUAIDI100:
return new Kuaidi100Plugin(logisticsSetting);
default:
throw new ServiceException();
}
} catch (Exception e) {
throw new ServiceException();
}
}
}

View File

@ -0,0 +1,31 @@
package cn.lili.modules.logistics.entity.dto;
import cn.lili.modules.order.order.entity.dos.Order;
import cn.lili.modules.order.order.entity.dos.OrderItem;
import cn.lili.modules.store.entity.dos.StoreLogistics;
import cn.lili.modules.store.entity.dto.StoreDeliverGoodsAddressDTO;
import cn.lili.modules.system.entity.dos.Logistics;
import lombok.Data;
import java.util.List;
/**
* 电子面单DTO
*
* @author Bulbasaur
* @since 2023-02-16
*/
@Data
public class LabelOrderDTO {
//订单
Order order;
//订单货物
List<OrderItem> orderItems;
//物流公司
Logistics logistics;
//店铺物流公司配置
StoreLogistics storeLogistics;
//店铺发件地址
StoreDeliverGoodsAddressDTO storeDeliverGoodsAddressDTO;
}

View File

@ -0,0 +1,15 @@
package cn.lili.modules.logistics.entity.enums;
/**
* LogisticsEnum
*
* @author Bulbasaur
* @version v1.0
* 2022-06-06 11:23
*/
public enum LogisticsEnum {
/**
* 快递查询渠道
*/
KDNIAO, KUAIDI100;
}

View File

@ -1,35 +1,22 @@
package cn.lili.modules.kdBrid.serviceImpl; package cn.lili.modules.logistics.plugin.kdniao;
import cn.hutool.core.text.CharSequenceUtil; import cn.hutool.core.text.CharSequenceUtil;
import cn.lili.common.enums.ResultCode; import cn.lili.common.enums.ResultCode;
import cn.lili.common.exception.ServiceException; import cn.lili.common.exception.ServiceException;
import cn.lili.common.security.OperationalJudgment; import cn.lili.modules.logistics.LogisticsPlugin;
import cn.lili.modules.kdBrid.service.KdNiaoService; import cn.lili.modules.logistics.entity.dto.LabelOrderDTO;
import cn.lili.modules.member.service.StoreLogisticsService; import cn.lili.modules.logistics.entity.enums.LogisticsEnum;
import cn.lili.modules.order.order.aop.OrderLogPoint;
import cn.lili.modules.order.order.entity.dos.Order; import cn.lili.modules.order.order.entity.dos.Order;
import cn.lili.modules.order.order.entity.dos.OrderItem; import cn.lili.modules.order.order.entity.dos.OrderItem;
import cn.lili.modules.order.order.entity.enums.DeliverStatusEnum;
import cn.lili.modules.order.order.entity.enums.OrderStatusEnum;
import cn.lili.modules.order.order.service.OrderItemService;
import cn.lili.modules.order.order.service.OrderService;
import cn.lili.modules.store.entity.dos.StoreLogistics; import cn.lili.modules.store.entity.dos.StoreLogistics;
import cn.lili.modules.store.entity.dto.StoreDeliverGoodsAddressDTO; import cn.lili.modules.store.entity.dto.StoreDeliverGoodsAddressDTO;
import cn.lili.modules.store.service.StoreDetailService;
import cn.lili.modules.system.entity.dos.Logistics; import cn.lili.modules.system.entity.dos.Logistics;
import cn.lili.modules.system.entity.dos.Setting; import cn.lili.modules.system.entity.dto.LogisticsSetting;
import cn.lili.modules.system.entity.dto.KuaidiSetting; import cn.lili.modules.system.entity.vo.Traces;
import cn.lili.modules.system.entity.enums.SettingEnum; import com.alibaba.fastjson.JSON;
import cn.lili.modules.system.service.LogisticsService;
import cn.lili.modules.system.service.SettingService;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.io.*; import java.io.*;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
@ -42,111 +29,116 @@ import java.util.List;
import java.util.Map; import java.util.Map;
/** /**
* 快递鸟电子面单业务层实现 * 快递鸟插件
* *
* @author chc * @author Bulbasaur
* @since 2022-4-12 10:12:43
*/ */
@Service
@Slf4j @Slf4j
public class KdNiaoServiceImpl implements KdNiaoService { public class KdniaoPlugin implements LogisticsPlugin {
/**
* 订单货物
*/
@Autowired
OrderItemService orderItemService;
/**
* 订单
*/
@Autowired @Autowired
OrderService orderService; private LogisticsSetting logisticsSetting;
/**
* 物流公司
*/
@Autowired
LogisticsService logisticsService;
/**
* 商家店铺
*/
@Autowired
StoreDetailService storeDetailService;
/**
* 配置
*/
@Autowired
SettingService settingService;
/**
* 店铺-物流
*/
@Autowired
StoreLogisticsService storeLogisticsService;
public KdniaoPlugin(LogisticsSetting logisticsSetting) {
this.logisticsSetting = logisticsSetting;
}
@Override @Override
@OrderLogPoint(description = "'订单['+#orderSn+']发货,打印电子面单'", orderSn = "#orderSn") public LogisticsEnum pluginName() {
@Transactional(rollbackFor = Exception.class) return LogisticsEnum.KDNIAO;
public String createElectronicsFaceSheet(String orderSn, String logisticsId) throws Exception { }
//电子面单模板
String printTemplate = null;
//获取订单及子订单
Order order = OperationalJudgment.judgment(orderService.getBySn(orderSn));
List<OrderItem> orderItems = orderItemService.getByOrderSn(orderSn);
Setting setting = settingService.get(SettingEnum.KUAIDI_SETTING.name()); @Override
if (CharSequenceUtil.isBlank(setting.getSettingValue())) { public Traces pollQuery(Logistics logistics, String expNo, String phone) {
throw new ServiceException(ResultCode.LOGISTICS_NOT_SETTING); try {
String requestData = "{'OrderCode':'','ShipperCode':'" + logistics.getCode() +
"','LogisticCode':'" + expNo + "'" +
",'CustomerName':'" + phone.substring(phone.length() - 4) + "'" +
"}";
//请求地址-测试地址
String testReqURL = "http://sandboxapi.kdniao.com:8080/kdniaosandbox/gateway/exterfaceInvoke.json";
//请求地址-正式地址
String reqURL = "https://api.kdniao.com/Ebusiness/EbusinessOrderHandle.aspx";
Map<String, String> params = new HashMap<>(8);
params.put("RequestData", urlEncoder(requestData, "UTF-8"));
params.put("EBusinessID", logisticsSetting.getKdniaoEbusinessID());
params.put("RequestType", "1002");
String dataSign = encrypt(requestData, logisticsSetting.getKdniaoAppKey(), "UTF-8");
params.put("DataSign", urlEncoder(dataSign, "UTF-8"));
params.put("DataType", "2");
String result = sendPost(reqURL, params);
Map map = (Map) JSON.parse(result);
return new Traces(logistics.getName(), expNo, (List<Map>) map.get("Traces"));
} catch (Exception e) {
e.printStackTrace();
} }
KuaidiSetting kuaidiSetting = new Gson().fromJson(setting.getSettingValue(), KuaidiSetting.class); return null;
}
//ID @Override
String EBusinessID = kuaidiSetting.getEbusinessID(); public Traces pollMapTrack(Logistics logistics, String expNo, String phone, String from, String to) {
try {
//KEY //请求地址-测试地址
String AppKey = kuaidiSetting.getAppKey(); String testReqURL = "http://sandboxapi.kdniao.com:8080/kdniaosandbox/gateway/exterfaceInvoke.json";
//请求地址-正式地址
//请求url String reqURL = "https://api.kdniao.com/Ebusiness/EbusinessOrderHandle.aspx";
String ReqURL = kuaidiSetting.getSheetReqURL(); String RequestData = "{" +
"'OrderCode': ''," +
//如果订单未发货并且订单状态值等于待发货 "'CustomerName': '" + phone.substring(phone.length() - 4) + "'," +
if (order.getDeliverStatus().equals(DeliverStatusEnum.UNDELIVERED.name()) && order.getOrderStatus().equals(OrderStatusEnum.UNDELIVERED.name())) { "'ShipperCode': '" + logistics.getCode() + "'," +
"'LogisticCode': '" + expNo + "'," +
"'SenderCityName': '" + from + "'," +
"'ReceiverCityName': '" + to + "'," +
"'IsReturnCoordinates': 1," +
"'IsReturnRouteMap': 1," +
"}";
// 组装系统级参数
Map<String, String> params = new HashMap<String, String>();
params.put("RequestData", urlEncoder(RequestData, "UTF-8"));
params.put("EBusinessID", logisticsSetting.getKdniaoEbusinessID());
params.put("RequestType", "8003");
String dataSign = encrypt(RequestData, logisticsSetting.getKdniaoAppKey(), "UTF-8");
params.put("DataSign", urlEncoder(dataSign, "UTF-8"));
// params.put("DataType", "2");
// 以form表单形式提交post请求post请求体中包含了应用级参数和系统级参数
String result = sendPost(reqURL, params);
log.error(result);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@Override
public String labelOrder(LabelOrderDTO labelOrderDTO) {
try {
//订单
Order order = labelOrderDTO.getOrder();
//订单货物
List<OrderItem> orderItems = labelOrderDTO.getOrderItems();
//获取对应物流 //获取对应物流
Logistics logistics = logisticsService.getById(logisticsId); Logistics logistics = labelOrderDTO.getLogistics();
//物流为空,抛出异常
if (logistics == null) {
throw new ServiceException(ResultCode.ORDER_LOGISTICS_ERROR);
}
//获取店家的物流信息
LambdaQueryWrapper<StoreLogistics> lambdaQueryWrapper = Wrappers.lambdaQuery();
lambdaQueryWrapper.eq(StoreLogistics::getLogisticsId, logisticsId);
lambdaQueryWrapper.eq(StoreLogistics::getStoreId, order.getStoreId());
StoreLogistics storeLogistics = storeLogisticsService.getOne(lambdaQueryWrapper);
//获取店家信息
StoreDeliverGoodsAddressDTO storeDeliverGoodsAddressDTO = storeDetailService.getStoreDeliverGoodsAddressDto(order.getStoreId());
//收件人地址 //收件人地址
String[] ConsigneeAddress = order.getConsigneeAddressPath().split(","); String[] ConsigneeAddress = order.getConsigneeAddressPath().split(",");
//获取店家信息
StoreDeliverGoodsAddressDTO storeDeliverGoodsAddressDTO = labelOrderDTO.getStoreDeliverGoodsAddressDTO();
//发件人地址 //发件人地址
String[] consignorAddress = storeDeliverGoodsAddressDTO.getSalesConsignorAddressPath().split(","); String[] consignorAddress = storeDeliverGoodsAddressDTO.getSalesConsignorAddressPath().split(",");
//店铺-物流公司设置
StoreLogistics storeLogistics = labelOrderDTO.getStoreLogistics();
//组装快递鸟应用级参数 //组装快递鸟应用级参数
String resultDate = "{" + String resultDate = "{" +
"'OrderCode': '" + orderSn + "'," + //订单编码 "'OrderCode': '" + order.getSn() + "'," + //订单编码
"'ShipperCode': '" + logistics.getCode() + "'," + //快递公司编码 "'ShipperCode': '" + logistics.getCode() + "'," + //快递公司编码
"'CustomerName': '"+storeLogistics.getCustomerName()+"'," +//客户编码 "'CustomerName': '" + storeLogistics.getCustomerName() + "'," +//客户编码
"'CustomerPwd': '"+storeLogistics.getCustomerPwd()+"'," + //客户密码 "'CustomerPwd': '" + storeLogistics.getCustomerPwd() + "'," + //客户密码
"'MonthCode': '"+storeLogistics.getMonthCode()+"'," + //密钥 "'MonthCode': '" + storeLogistics.getMonthCode() + "'," + //密钥
"'SendSite': '"+storeLogistics.getSendSite()+"'," + //归属网点 "'SendSite': '" + storeLogistics.getSendSite() + "'," + //归属网点
"'SendStaff': '"+storeLogistics.getSendStaff()+"'," + //收件快递员 "'SendStaff': '" + storeLogistics.getSendStaff() + "'," + //收件快递员
"'PayType': "+storeLogistics.getPayType()+"," + "'PayType': " + storeLogistics.getPayType() + "," +
"'ExpType': "+storeLogistics.getExpType()+"," + "'ExpType': " + storeLogistics.getExpType() + "," +
//发件人信息 //发件人信息
"'Sender': {" + "'Sender': {" +
"'Name': '" + storeDeliverGoodsAddressDTO.getSalesConsignorName() + "'," + "'Name': '" + storeDeliverGoodsAddressDTO.getSalesConsignorName() + "'," +
@ -176,45 +168,49 @@ public class KdNiaoServiceImpl implements KdNiaoService {
"},"; "},";
} }
resultDate = resultDate + "]," + resultDate = resultDate + "]," +
"'Quantity': "+orderItems.size()+"," + //包裹数 "'Quantity': " + orderItems.size() + "," + //包裹数
"'IsReturnPrintTemplate':1,"+ //生成电子面单模板 "'IsReturnPrintTemplate':1," + //生成电子面单模板
"'Remark': '" + order.getRemark() + "'"+//商家备注 "'Remark': '" + order.getRemark() + "'" +//商家备注
"}"; "}";
//组织系统级参数 //组织系统级参数
Map<String, String> params = new HashMap<>(); Map<String, String> params = new HashMap<>();
//请求地址-测试地址
String testReqURL = "http://sandboxapi.kdniao.com:8080/kdniaosandbox/gateway/exterfaceInvoke.json";
//请求地址-正式地址
String reqURL = "https://api.kdniao.com/api/EOrderService";
//进行格式加密 //进行格式加密
params.put("RequestData", urlEncoder(resultDate, "UTF-8")); params.put("RequestData", urlEncoder(resultDate, "UTF-8"));
params.put("EBusinessID", EBusinessID); params.put("EBusinessID", logisticsSetting.getKdniaoEbusinessID());
params.put("RequestType", "1007"); params.put("RequestType", "1007");
String dataSign = encrypt(resultDate, AppKey, "UTF-8"); String dataSign = encrypt(resultDate, logisticsSetting.getKdniaoAppKey(), "UTF-8");
params.put("DataSign", dataSign); params.put("DataSign", dataSign);
params.put("DataType", "2"); params.put("DataType", "2");
// 以form表单形式提交post请求post请求体中包含了应用级参数和系统级参数 // 以form表单形式提交post请求post请求体中包含了应用级参数和系统级参数
String result = sendPost(ReqURL, params); String result = sendPost(reqURL, params);
if(CharSequenceUtil.isEmpty(result) || CharSequenceUtil.isBlank(result)){ if (CharSequenceUtil.isEmpty(result) || CharSequenceUtil.isBlank(result)) {
throw new ServiceException(ResultCode.LOGISTICS_CHECK_SETTING); throw new ServiceException(ResultCode.LOGISTICS_CHECK_SETTING);
} }
//根据公司业务处理返回的信息...... //根据公司业务处理返回的信息......
JSONObject obj = JSONObject.parseObject(result); JSONObject obj = JSONObject.parseObject(result);
log.info("电子面单响应:{}",result); log.info("电子面单响应:{}", result);
if(!"100".equals(obj.getString("ResultCode"))){ if (!"100".equals(obj.getString("ResultCode"))) {
return obj.getString("Reason"); return obj.getString("Reason");
} }
JSONObject orderJson = JSONObject.parseObject(obj.getString("Order")); JSONObject orderJson = JSONObject.parseObject(obj.getString("Order"));
//电子面单模板 return obj.getString("PrintTemplate");
printTemplate = obj.getString("PrintTemplate"); } catch (Exception e) {
e.printStackTrace();
//进行发货
orderService.delivery(orderSn, orderJson.getString("LogisticCode"), logisticsId);
} }
return printTemplate; return null;
} }
/** /**
* MD5加密 * MD5加密
* *
@ -323,7 +319,7 @@ public class KdNiaoServiceImpl implements KdNiaoService {
result.append(line); result.append(line);
} }
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); log.error("向指定 URL 发送POST方法的请求错误", e);
} }
//使用finally块来关闭输出流输入流 //使用finally块来关闭输出流输入流
finally { finally {
@ -381,5 +377,4 @@ public class KdNiaoServiceImpl implements KdNiaoService {
} }
return sb.toString(); return sb.toString();
} }
} }

View File

@ -0,0 +1,184 @@
package cn.lili.modules.logistics.plugin.kuaidi100;
import cn.lili.modules.logistics.LogisticsPlugin;
import cn.lili.modules.logistics.entity.dto.LabelOrderDTO;
import cn.lili.modules.logistics.entity.enums.LogisticsEnum;
import cn.lili.modules.logistics.plugin.kuaidi100.utils.Kuaidi100SignUtils;
import cn.lili.modules.order.order.entity.dos.Order;
import cn.lili.modules.order.order.entity.dos.OrderItem;
import cn.lili.modules.store.entity.dos.StoreLogistics;
import cn.lili.modules.store.entity.dto.StoreDeliverGoodsAddressDTO;
import cn.lili.modules.system.entity.dos.Logistics;
import cn.lili.modules.system.entity.dto.LogisticsSetting;
import cn.lili.modules.system.entity.vo.Traces;
import com.google.gson.Gson;
import com.kuaidi100.sdk.api.LabelV2;
import com.kuaidi100.sdk.api.QueryTrack;
import com.kuaidi100.sdk.api.QueryTrackMap;
import com.kuaidi100.sdk.contant.ApiInfoConstant;
import com.kuaidi100.sdk.contant.PrintType;
import com.kuaidi100.sdk.core.IBaseClient;
import com.kuaidi100.sdk.pojo.HttpResult;
import com.kuaidi100.sdk.request.ManInfo;
import com.kuaidi100.sdk.request.PrintReq;
import com.kuaidi100.sdk.request.QueryTrackParam;
import com.kuaidi100.sdk.request.QueryTrackReq;
import com.kuaidi100.sdk.request.labelV2.OrderReq;
import com.kuaidi100.sdk.response.QueryTrackData;
import com.kuaidi100.sdk.response.QueryTrackMapResp;
import com.kuaidi100.sdk.response.QueryTrackResp;
import com.kuaidi100.sdk.response.samecity.OrderResp;
import com.kuaidi100.sdk.utils.SignUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 快递100插件
*
* @author Bulbasaur
*/
@Slf4j
public class Kuaidi100Plugin implements LogisticsPlugin {
@Autowired
private LogisticsSetting logisticsSetting;
public Kuaidi100Plugin(LogisticsSetting logisticsSetting) {
this.logisticsSetting = logisticsSetting;
}
@Override
public LogisticsEnum pluginName() {
return LogisticsEnum.KUAIDI100;
}
@Override
public Traces pollQuery(Logistics logistics, String expNo, String phone) {
try {
QueryTrackReq queryTrackReq = new QueryTrackReq();
QueryTrackParam queryTrackParam = new QueryTrackParam();
queryTrackParam.setCom(logistics.getCode());
queryTrackParam.setNum(expNo);
queryTrackParam.setPhone(phone);
String param = new Gson().toJson(queryTrackParam);
queryTrackReq.setParam(param);
queryTrackReq.setCustomer(logisticsSetting.getKuaidi100Customer());
queryTrackReq.setSign(Kuaidi100SignUtils.querySign(param, logisticsSetting.getKuaidi100Key(), logisticsSetting.getKuaidi100Customer()));
IBaseClient baseClient = new QueryTrack();
HttpResult httpResult = baseClient.execute(queryTrackReq);
QueryTrackResp queryTrackResp = new Gson().fromJson(httpResult.getBody(), QueryTrackResp.class);
log.info(String.valueOf(queryTrackResp.getData()));
List<Map> traces = new ArrayList<>();
for (QueryTrackData queryTrackData : queryTrackResp.getData()) {
Map map = new HashMap<String, String>();
map.put("AcceptTime", queryTrackData.getTime());
map.put("AcceptStation", queryTrackData.getContext());
map.put("Remark", null);
traces.add(map);
}
return new Traces(logistics.getName(), expNo, traces);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@Override
public Traces pollMapTrack(Logistics logistics, String expNo, String phone, String from, String to) {
try {
QueryTrackReq queryTrackReq = new QueryTrackReq();
QueryTrackParam queryTrackParam = new QueryTrackParam();
queryTrackParam.setCom(logistics.getCode());
queryTrackParam.setNum(expNo);
queryTrackParam.setPhone(phone);
queryTrackParam.setFrom(from);
queryTrackParam.setTo(to);
queryTrackParam.setResultv2("5");
String param = new Gson().toJson(queryTrackParam);
queryTrackReq.setParam(param);
queryTrackReq.setCustomer(logisticsSetting.getKuaidi100Customer());
queryTrackReq.setSign(SignUtils.querySign(param, logisticsSetting.getKuaidi100Key(), logisticsSetting.getKuaidi100Customer()));
IBaseClient baseClient = new QueryTrackMap();
HttpResult result = baseClient.execute(queryTrackReq);
QueryTrackMapResp queryTrackMapResp = new Gson().fromJson(result.getBody(), QueryTrackMapResp.class);
System.out.println(queryTrackMapResp);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@Override
public String labelOrder(LabelOrderDTO labelOrderDTO) {
try {
//订单
Order order = labelOrderDTO.getOrder();
//订单货物
List<OrderItem> orderItems = labelOrderDTO.getOrderItems();
//获取对应物流
Logistics logistics = labelOrderDTO.getLogistics();
//收件人地址
String[] consigneeAddress = order.getConsigneeAddressPath().split(",");
//获取店家信息
StoreDeliverGoodsAddressDTO storeDeliverGoodsAddressDTO = labelOrderDTO.getStoreDeliverGoodsAddressDTO();
//发件人地址
String[] consignorAddress = storeDeliverGoodsAddressDTO.getSalesConsignorAddressPath().split(",");
//店铺-物流公司设置
StoreLogistics storeLogistics = labelOrderDTO.getStoreLogistics();
ManInfo recManInfo = new ManInfo();
recManInfo.setName(order.getConsigneeName());
recManInfo.setMobile(order.getConsigneeMobile());
recManInfo.setPrintAddr(consigneeAddress[0] + consigneeAddress[1] + consigneeAddress[2] + consigneeAddress[3] + order.getConsigneeDetail());
ManInfo sendManInfo = new ManInfo();
sendManInfo.setName(storeDeliverGoodsAddressDTO.getSalesConsignorName());
sendManInfo.setMobile(storeDeliverGoodsAddressDTO.getSalesConsignorMobile());
sendManInfo.setPrintAddr(consignorAddress[0] + consignorAddress[1] + consignorAddress[2] + consignorAddress[3] + storeDeliverGoodsAddressDTO.getSalesConsignorDetail());
OrderReq orderReq = new OrderReq();
orderReq.setKuaidicom(logistics.getCode());
orderReq.setCount(1);
// orderReq.setSiid(siid);
//orderReq.setTempId("60f6c17c7c223700131d8bc3");
orderReq.setSendMan(sendManInfo);
orderReq.setRecMan(recManInfo);
orderReq.setPrintType(PrintType.CLOUD);
String param = new Gson().toJson(orderReq);
String t = System.currentTimeMillis() + "";
PrintReq printReq = new PrintReq();
printReq.setT(t);
printReq.setKey(logisticsSetting.getKuaidi100Key());
printReq.setSign(SignUtils.printSign(param, t, logisticsSetting.getKuaidi100Key(), logisticsSetting.getKuaidi100Customer()));
printReq.setMethod(ApiInfoConstant.ORDER);
printReq.setParam(param);
IBaseClient baseClient = new LabelV2();
HttpResult result = baseClient.execute(printReq);
System.out.println(result.getBody());
QueryTrackMapResp queryTrackMapResp = new Gson().fromJson(result.getBody(), QueryTrackMapResp.class);
OrderResp orderResp = new Gson().fromJson(result.getBody(), OrderResp.class);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}

View File

@ -0,0 +1,73 @@
package cn.lili.modules.logistics.plugin.kuaidi100.utils;
import org.apache.commons.codec.digest.DigestUtils;
/**
* @Author: api.kuaidi100.com
* @Date: 2020-07-14 16:54
*/
public class Kuaidi100SignUtils {
/**
* 快递100加密方式统一为MD5后转大写
*
* @param msg
* @return
*/
public static String sign(String msg) {
return DigestUtils.md5Hex(msg).toUpperCase();
}
/**
* 查询加密
*
* @param param
* @param key
* @param customer
* @return
*/
public static String querySign(String param, String key, String customer) {
return sign(param + key + customer);
}
/**
* 打印/下单 加密
*
* @param param
* @param t
* @param key
* @param secret
* @return: java.lang.String
* @author: api.kuaidi100.com
* @time: 2020/12/3 9:23
*/
public static String printSign(String param, String t, String key, String secret) {
return sign(param + t + key + secret);
}
/**
* 云平台 加密
*
* @param key
* @param secret
* @return: java.lang.String
* @author: api.kuaidi100.com
* @time: 2020/12/3 9:23
*/
public static String cloudSign(String key, String secret) {
return sign(key + secret);
}
/**
* 短信加密
*
* @param key
* @param userId
* @return: java.lang.String
* @author: api.kuaidi100.com
* @time: 2020/12/3 9:32
*/
public static String smsSign(String key, String userId) {
return sign(key + userId);
}
}

View File

@ -269,8 +269,7 @@ public class AfterSaleServiceImpl extends ServiceImpl<AfterSaleMapper, AfterSale
//根据售后单号获取售后单 //根据售后单号获取售后单
AfterSale afterSale = OperationalJudgment.judgment(this.getBySn(afterSaleSn)); AfterSale afterSale = OperationalJudgment.judgment(this.getBySn(afterSaleSn));
String str=storeDetailService.getStoreDetail(afterSale.getStoreId()).getSalesConsigneeMobile(); return logisticsService.getLogisticTrack(afterSale.getMLogisticsCode(), afterSale.getMLogisticsNo(), storeDetailService.getStoreDetail(afterSale.getStoreId()).getSalesConsigneeMobile());
return logisticsService.getLogistic(afterSale.getMLogisticsCode(), afterSale.getMLogisticsNo(), str.substring(str.length()-4));
} }
@Override @Override

View File

@ -64,9 +64,9 @@ public interface OrderService extends IService<Order> {
* 根据促销查询订单 * 根据促销查询订单
* *
* @param orderPromotionType 订单类型 * @param orderPromotionType 订单类型
* @param payStatus 支付状态 * @param payStatus 支付状态
* @param parentOrderSn 依赖订单编号 * @param parentOrderSn 依赖订单编号
* @param orderSn 订单编号 * @param orderSn 订单编号
* @return 订单信息 * @return 订单信息
*/ */
List<Order> queryListByPromotion(String orderPromotionType, String payStatus, String parentOrderSn, String orderSn); List<Order> queryListByPromotion(String orderPromotionType, String payStatus, String parentOrderSn, String orderSn);
@ -75,9 +75,9 @@ public interface OrderService extends IService<Order> {
* 根据促销查询订单 * 根据促销查询订单
* *
* @param orderPromotionType 订单类型 * @param orderPromotionType 订单类型
* @param payStatus 支付状态 * @param payStatus 支付状态
* @param parentOrderSn 依赖订单编号 * @param parentOrderSn 依赖订单编号
* @param orderSn 订单编号 * @param orderSn 订单编号
* @return 订单信息 * @return 订单信息
*/ */
long queryCountByPromotion(String orderPromotionType, String payStatus, String parentOrderSn, String orderSn); long queryCountByPromotion(String orderPromotionType, String payStatus, String parentOrderSn, String orderSn);
@ -91,7 +91,6 @@ public interface OrderService extends IService<Order> {
List<Order> queryListByPromotion(String pintuanId); List<Order> queryListByPromotion(String pintuanId);
/** /**
* 查询导出订单列表 * 查询导出订单列表
* *
@ -174,6 +173,14 @@ public interface OrderService extends IService<Order> {
*/ */
Traces getTraces(String orderSn); Traces getTraces(String orderSn);
/**
* 获取地图版 物流踪迹
*
* @param orderSn 订单编号
* @return 物流踪迹
*/
Traces getMapTraces(String orderSn);
/** /**
* 订单核验 * 订单核验
* *
@ -288,9 +295,9 @@ public interface OrderService extends IService<Order> {
/** /**
* 检查是否开始虚拟成团 * 检查是否开始虚拟成团
* *
* @param pintuanId 拼团活动id * @param pintuanId 拼团活动id
* @param requiredNum 成团人数 * @param requiredNum 成团人数
* @param fictitious 是否开启成团 * @param fictitious 是否开启成团
* @return 是否成功 * @return 是否成功
*/ */
boolean checkFictitiousOrder(String pintuanId, Integer requiredNum, Boolean fictitious); boolean checkFictitiousOrder(String pintuanId, Integer requiredNum, Boolean fictitious);

View File

@ -16,7 +16,6 @@ import cn.lili.common.properties.RocketmqCustomProperties;
import cn.lili.common.security.OperationalJudgment; import cn.lili.common.security.OperationalJudgment;
import cn.lili.common.security.context.UserContext; import cn.lili.common.security.context.UserContext;
import cn.lili.common.security.enums.UserEnums; import cn.lili.common.security.enums.UserEnums;
import cn.lili.common.utils.ObjectUtil;
import cn.lili.common.utils.SnowFlake; import cn.lili.common.utils.SnowFlake;
import cn.lili.modules.goods.entity.dto.GoodsCompleteMessage; import cn.lili.modules.goods.entity.dto.GoodsCompleteMessage;
import cn.lili.modules.member.entity.dto.MemberAddressDTO; import cn.lili.modules.member.entity.dto.MemberAddressDTO;
@ -41,6 +40,8 @@ import cn.lili.modules.order.trade.service.OrderLogService;
import cn.lili.modules.payment.entity.enums.PaymentMethodEnum; import cn.lili.modules.payment.entity.enums.PaymentMethodEnum;
import cn.lili.modules.promotion.entity.dos.Pintuan; import cn.lili.modules.promotion.entity.dos.Pintuan;
import cn.lili.modules.promotion.service.PintuanService; import cn.lili.modules.promotion.service.PintuanService;
import cn.lili.modules.store.entity.dto.StoreDeliverGoodsAddressDTO;
import cn.lili.modules.store.service.StoreDetailService;
import cn.lili.modules.system.aspect.annotation.SystemLogPoint; import cn.lili.modules.system.aspect.annotation.SystemLogPoint;
import cn.lili.modules.system.entity.dos.Logistics; import cn.lili.modules.system.entity.dos.Logistics;
import cn.lili.modules.system.entity.vo.Traces; import cn.lili.modules.system.entity.vo.Traces;
@ -149,6 +150,9 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
@Autowired @Autowired
private ApplicationEventPublisher applicationEventPublisher; private ApplicationEventPublisher applicationEventPublisher;
@Autowired
private StoreDetailService storeDetailService;
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public void intoDB(TradeDTO tradeDTO) { public void intoDB(TradeDTO tradeDTO) {
@ -297,7 +301,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
Order order = OperationalJudgment.judgment(this.getBySn(orderSn)); Order order = OperationalJudgment.judgment(this.getBySn(orderSn));
//如果订单促销类型不为空&&订单是拼团订单并且订单未成团则抛出异常 //如果订单促销类型不为空&&订单是拼团订单并且订单未成团则抛出异常
if (OrderPromotionTypeEnum.PINTUAN.name().equals(order.getOrderPromotionType()) if (OrderPromotionTypeEnum.PINTUAN.name().equals(order.getOrderPromotionType())
&& !CharSequenceUtil.equalsAny(order.getOrderStatus(),OrderStatusEnum.UNDELIVERED.name(),OrderStatusEnum.STAY_PICKED_UP.name())) { && !CharSequenceUtil.equalsAny(order.getOrderStatus(), OrderStatusEnum.UNDELIVERED.name(), OrderStatusEnum.STAY_PICKED_UP.name())) {
throw new ServiceException(ResultCode.ORDER_CAN_NOT_CANCEL); throw new ServiceException(ResultCode.ORDER_CAN_NOT_CANCEL);
} }
if (CharSequenceUtil.equalsAny(order.getOrderStatus(), if (CharSequenceUtil.equalsAny(order.getOrderStatus(),
@ -461,8 +465,19 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
//获取订单信息 //获取订单信息
Order order = this.getBySn(orderSn); Order order = this.getBySn(orderSn);
//获取踪迹信息 //获取踪迹信息
String str = order.getConsigneeMobile(); return logisticsService.getLogisticTrack(order.getLogisticsCode(), order.getLogisticsNo(), order.getConsigneeMobile());
return logisticsService.getLogistic(order.getLogisticsCode(), order.getLogisticsNo(), str.substring(str.length() - 4)); }
@Override
public Traces getMapTraces(String orderSn) {
//获取订单信息
Order order = this.getBySn(orderSn);
//获取店家信息
StoreDeliverGoodsAddressDTO storeDeliverGoodsAddressDTO = storeDetailService.getStoreDeliverGoodsAddressDto(order.getStoreId());
String from = storeDeliverGoodsAddressDTO.getSalesConsignorAddressPath().substring(0, storeDeliverGoodsAddressDTO.getSalesConsignorAddressPath().indexOf(",") - 1);
String to = order.getConsigneeAddressPath().substring(0, order.getConsigneeAddressPath().indexOf(",") - 1);
//获取踪迹信息
return logisticsService.getLogisticMapTrack(order.getLogisticsCode(), order.getLogisticsNo(), order.getConsigneeMobile(), from, to);
} }
@Override @Override
@ -484,7 +499,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
public Order take(String verificationCode) { public Order take(String verificationCode) {
String storeId = OperationalJudgment.judgment(UserContext.getCurrentUser()).getStoreId(); String storeId = OperationalJudgment.judgment(UserContext.getCurrentUser()).getStoreId();
Order order = this.getOne(new LambdaQueryWrapper<Order>().eq(Order::getVerificationCode, verificationCode).eq(Order::getStoreId, storeId)); Order order = this.getOne(new LambdaQueryWrapper<Order>().eq(Order::getVerificationCode, verificationCode).eq(Order::getStoreId, storeId));
if(order == null){ if (order == null) {
throw new ServiceException(ResultCode.ORDER_NOT_EXIST); throw new ServiceException(ResultCode.ORDER_NOT_EXIST);
} }
order.setOrderStatus(OrderStatusEnum.COMPLETED.name()); order.setOrderStatus(OrderStatusEnum.COMPLETED.name());
@ -497,7 +512,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
public Order getOrderByVerificationCode(String verificationCode) { public Order getOrderByVerificationCode(String verificationCode) {
String storeId = Objects.requireNonNull(UserContext.getCurrentUser()).getStoreId(); String storeId = Objects.requireNonNull(UserContext.getCurrentUser()).getStoreId();
return this.getOne(new LambdaQueryWrapper<Order>() return this.getOne(new LambdaQueryWrapper<Order>()
.in(Order::getOrderStatus, OrderStatusEnum.TAKE.name(),OrderStatusEnum.STAY_PICKED_UP.name()) .in(Order::getOrderStatus, OrderStatusEnum.TAKE.name(), OrderStatusEnum.STAY_PICKED_UP.name())
.eq(Order::getStoreId, storeId) .eq(Order::getStoreId, storeId)
.eq(Order::getVerificationCode, verificationCode)); .eq(Order::getVerificationCode, verificationCode));
} }
@ -961,9 +976,9 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
public void normalOrderConfirm(String orderSn) { public void normalOrderConfirm(String orderSn) {
OrderStatusEnum orderStatusEnum = null; OrderStatusEnum orderStatusEnum = null;
Order order = this.getBySn(orderSn); Order order = this.getBySn(orderSn);
if(DeliveryMethodEnum.SELF_PICK_UP.name().equals(order.getDeliveryMethod())){ if (DeliveryMethodEnum.SELF_PICK_UP.name().equals(order.getDeliveryMethod())) {
orderStatusEnum = OrderStatusEnum.STAY_PICKED_UP; orderStatusEnum = OrderStatusEnum.STAY_PICKED_UP;
}else if (DeliveryMethodEnum.LOGISTICS.name().equals(order.getDeliveryMethod())){ } else if (DeliveryMethodEnum.LOGISTICS.name().equals(order.getDeliveryMethod())) {
orderStatusEnum = OrderStatusEnum.UNDELIVERED; orderStatusEnum = OrderStatusEnum.UNDELIVERED;
} }
//修改订单 //修改订单

View File

@ -3,7 +3,6 @@ package cn.lili.modules.sms;
import cn.lili.modules.verification.entity.enums.VerificationEnums; import cn.lili.modules.verification.entity.enums.VerificationEnums;
import java.util.List; import java.util.List;
import java.util.Map;
/** /**
* 短信接口 * 短信接口
@ -35,15 +34,6 @@ public interface SmsUtil {
*/ */
boolean verifyCode(String mobile, VerificationEnums verificationEnums, String uuid, String code); boolean verifyCode(String mobile, VerificationEnums verificationEnums, String uuid, String code);
/**
* 短信发送
*
* @param mobile 接收手机号
* @param param 参数
* @param templateCode 模版code
* @param signName 签名名称
*/
void sendSmsCode(String signName, String mobile, Map<String, String> param, String templateCode);
/** /**
* 短信批量发送 * 短信批量发送

View File

@ -0,0 +1,15 @@
package cn.lili.modules.sms.entity.enums;
/**
* 短信通道枚举
*
* @author Bulbasaur
* @since 2023-02-16
*/
public enum SmsEnum {
/**
* 短信渠道
*/
ALI, HUAWEI, TENCENT;
}

View File

@ -1,7 +1,6 @@
package cn.lili.modules.sms.impl; package cn.lili.modules.sms.impl;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import cn.lili.cache.Cache; import cn.lili.cache.Cache;
import cn.lili.cache.CachePrefix; import cn.lili.cache.CachePrefix;
import cn.lili.common.enums.ResultCode; import cn.lili.common.enums.ResultCode;
@ -9,28 +8,21 @@ import cn.lili.common.exception.ServiceException;
import cn.lili.common.properties.SmsTemplateProperties; import cn.lili.common.properties.SmsTemplateProperties;
import cn.lili.common.properties.SystemSettingProperties; import cn.lili.common.properties.SystemSettingProperties;
import cn.lili.common.security.context.UserContext; import cn.lili.common.security.context.UserContext;
import cn.lili.common.utils.Base64Utils;
import cn.lili.common.utils.CommonUtil; import cn.lili.common.utils.CommonUtil;
import cn.lili.modules.member.entity.dos.Member; import cn.lili.modules.member.entity.dos.Member;
import cn.lili.modules.member.service.MemberService; import cn.lili.modules.member.service.MemberService;
import cn.lili.modules.sms.AliSmsUtil;
import cn.lili.modules.sms.SmsUtil; import cn.lili.modules.sms.SmsUtil;
import cn.lili.modules.sms.entity.dos.SmsSign;
import cn.lili.modules.sms.entity.dos.SmsTemplate;
import cn.lili.modules.system.entity.dos.Setting; import cn.lili.modules.system.entity.dos.Setting;
import cn.lili.modules.system.entity.dto.SmsSetting; import cn.lili.modules.system.entity.dto.SmsSetting;
import cn.lili.modules.system.entity.enums.SettingEnum; import cn.lili.modules.system.entity.enums.SettingEnum;
import cn.lili.modules.system.service.SettingService; import cn.lili.modules.system.service.SettingService;
import cn.lili.modules.verification.entity.enums.VerificationEnums; import cn.lili.modules.verification.entity.enums.VerificationEnums;
import com.aliyun.dysmsapi20170525.models.*;
import com.aliyun.teaopenapi.models.Config;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.xkcoding.http.util.StringUtil; import com.xkcoding.http.util.StringUtil;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -44,7 +36,7 @@ import java.util.Map;
*/ */
@Component @Component
@Slf4j @Slf4j
public class SmsUtilAliImplService implements SmsUtil, AliSmsUtil { public class SmsUtilAliImplService implements SmsUtil {
@Autowired @Autowired
private Cache cache; private Cache cache;
@ -122,9 +114,9 @@ public class SmsUtilAliImplService implements SmsUtil, AliSmsUtil {
//如果是测试模式 默认验证码 6个1 //如果是测试模式 默认验证码 6个1
if (systemSettingProperties.getIsTestModel()) { if (systemSettingProperties.getIsTestModel()) {
code = "111111"; code = "111111";
log.info("测试模式 - 接收手机:{},验证码:{}",mobile,code); log.info("测试模式 - 接收手机:{},验证码:{}", mobile, code);
} else { } else {
log.info("接收手机:{},验证码:{}",mobile,code); log.info("接收手机:{},验证码:{}", mobile, code);
//发送短信 //发送短信
this.sendSmsCode(smsSetting.getSignName(), mobile, params, templateCode); this.sendSmsCode(smsSetting.getSignName(), mobile, params, templateCode);
} }
@ -145,232 +137,14 @@ public class SmsUtilAliImplService implements SmsUtil, AliSmsUtil {
} }
@Override
public void sendSmsCode(String signName, String mobile, Map<String, String> param, String templateCode) {
com.aliyun.dysmsapi20170525.Client client = this.createClient();
SendSmsRequest sendSmsRequest = new SendSmsRequest()
.setSignName(signName)
.setPhoneNumbers(mobile)
.setTemplateCode(templateCode)
.setTemplateParam(JSONUtil.toJsonStr(param));
try {
SendSmsResponse response = client.sendSms(sendSmsRequest);
if (!("OK").equals(response.getBody().getCode())) {
throw new ServiceException(response.getBody().getMessage());
}
} catch (Exception e) {
log.error("发送短信错误", e);
}
}
@Override @Override
public void sendBatchSms(String signName, List<String> mobile, String templateCode) { public void sendBatchSms(String signName, List<String> mobile, String templateCode) {
com.aliyun.dysmsapi20170525.Client client = this.createClient();
List<String> sign = new ArrayList<String>();
sign.addAll(mobile);
sign.replaceAll(e -> signName);
//手机号拆成多个小组进行发送
List<List<String>> mobileList = new ArrayList<>();
//签名名称多个小组
List<List<String>> signNameList = new ArrayList<>();
//循环分组
for (int i = 0; i < (mobile.size() / 100 + (mobile.size() % 100 == 0 ? 0 : 1)); i++) {
int endPoint = Math.min((100 + (i * 100)), mobile.size());
mobileList.add(mobile.subList((i * 100), endPoint));
signNameList.add(sign.subList((i * 100), endPoint));
}
// //发送短信
for (int i = 0; i < mobileList.size(); i++) {
SendBatchSmsRequest sendBatchSmsRequest = new SendBatchSmsRequest()
.setPhoneNumberJson(JSONUtil.toJsonStr(mobileList.get(i)))
.setSignNameJson(JSONUtil.toJsonStr(signNameList.get(i)))
.setTemplateCode(templateCode);
try {
client.sendBatchSms(sendBatchSmsRequest);
} catch (Exception e) {
log.error("批量发送短信错误", e);
}
}
} }
private void sendSmsCode(String signName, String mobile, Map<String, String> param, String templateCode) {
@Override
public void addSmsSign(SmsSign smsSign) throws Exception {
//设置参数添加短信签名
com.aliyun.dysmsapi20170525.Client client = this.createClient();
//营业执照
AddSmsSignRequest.AddSmsSignRequestSignFileList signFileList0 = new AddSmsSignRequest.AddSmsSignRequestSignFileList()
.setFileContents(Base64Utils.encode(smsSign.getBusinessLicense()))
.setFileSuffix(smsSign.getBusinessLicense().substring(smsSign.getBusinessLicense().lastIndexOf(".") + 1));
//授权委托书
AddSmsSignRequest.AddSmsSignRequestSignFileList signFileList1 = new AddSmsSignRequest.AddSmsSignRequestSignFileList()
.setFileContents(Base64Utils.encode(smsSign.getLicense()))
.setFileSuffix(smsSign.getLicense().substring(smsSign.getLicense().lastIndexOf(".")) + 1);
//添加短信签名
AddSmsSignRequest addSmsSignRequest = new AddSmsSignRequest()
.setSignName(smsSign.getSignName())
.setSignSource(smsSign.getSignSource())
.setRemark(smsSign.getRemark())
.setSignFileList(java.util.Arrays.asList(
signFileList0,
signFileList1
));
AddSmsSignResponse response = client.addSmsSign(addSmsSignRequest);
if (!("OK").equals(response.getBody().getCode())) {
throw new ServiceException(response.getBody().getMessage());
}
}
@Override
public void deleteSmsSign(String signName) throws Exception {
com.aliyun.dysmsapi20170525.Client client = this.createClient();
DeleteSmsSignRequest deleteSmsSignRequest = new DeleteSmsSignRequest()
.setSignName(signName);
DeleteSmsSignResponse response = client.deleteSmsSign(deleteSmsSignRequest);
if (!("OK").equals(response.getBody().getCode())) {
throw new ServiceException(response.getBody().getMessage());
}
}
@Override
public Map<String, Object> querySmsSign(String signName) throws Exception {
//设置参数查看短信签名
com.aliyun.dysmsapi20170525.Client client = this.createClient();
QuerySmsSignRequest querySmsSignRequest = new QuerySmsSignRequest().setSignName(signName);
QuerySmsSignResponse response = client.querySmsSign(querySmsSignRequest);
if (!("OK").equals(response.getBody().getCode())) {
throw new ServiceException(response.getBody().getMessage());
}
Map<String, Object> map = new HashMap<>(2);
map.put("SignStatus", response.getBody().getSignStatus());
map.put("Reason", response.getBody().getReason());
return map;
}
@Override
public void modifySmsSign(SmsSign smsSign) throws Exception {
//设置参数添加短信签名
com.aliyun.dysmsapi20170525.Client client = this.createClient();
ModifySmsSignRequest.ModifySmsSignRequestSignFileList signFileList0 = new ModifySmsSignRequest.ModifySmsSignRequestSignFileList()
.setFileContents(Base64Utils.encode(smsSign.getBusinessLicense()))
.setFileSuffix(smsSign.getBusinessLicense().substring(smsSign.getBusinessLicense().lastIndexOf(".") + 1));
ModifySmsSignRequest.ModifySmsSignRequestSignFileList signFileList1 = new ModifySmsSignRequest.ModifySmsSignRequestSignFileList()
.setFileContents(Base64Utils.encode(smsSign.getLicense()))
.setFileSuffix(smsSign.getLicense().substring(smsSign.getBusinessLicense().lastIndexOf(".") + 1));
ModifySmsSignRequest modifySmsSign = new ModifySmsSignRequest()
.setSignName(smsSign.getSignName())
.setSignSource(smsSign.getSignSource())
.setRemark(smsSign.getRemark())
.setSignFileList(java.util.Arrays.asList(
signFileList0,
signFileList1
));
ModifySmsSignResponse response = client.modifySmsSign(modifySmsSign);
if (!("OK").equals(response.getBody().getCode())) {
throw new ServiceException(response.getBody().getMessage());
}
}
@Override
public void modifySmsTemplate(SmsTemplate smsTemplate) throws Exception {
com.aliyun.dysmsapi20170525.Client client = this.createClient();
ModifySmsTemplateRequest modifySmsTemplateRequest = new ModifySmsTemplateRequest()
.setTemplateType(smsTemplate.getTemplateType())
.setTemplateName(smsTemplate.getTemplateName())
.setTemplateContent(smsTemplate.getTemplateContent())
.setRemark(smsTemplate.getRemark())
.setTemplateCode(smsTemplate.getTemplateCode());
ModifySmsTemplateResponse response = client.modifySmsTemplate(modifySmsTemplateRequest);
if (!("OK").equals(response.getBody().getCode())) {
throw new ServiceException(response.getBody().getMessage());
}
}
@Override
public Map<String, Object> querySmsTemplate(String templateCode) throws Exception {
com.aliyun.dysmsapi20170525.Client client = this.createClient();
QuerySmsTemplateRequest querySmsTemplateRequest = new QuerySmsTemplateRequest()
.setTemplateCode(templateCode);
QuerySmsTemplateResponse response = client.querySmsTemplate(querySmsTemplateRequest);
if (!("OK").equals(response.getBody().getCode())) {
throw new ServiceException(response.getBody().getMessage());
}
Map<String, Object> map = new HashMap<>(4);
map.put("TemplateStatus", response.getBody().getTemplateStatus());
map.put("Reason", response.getBody().getReason());
map.put("TemplateCode", response.getBody().getTemplateCode());
return map;
}
@Override
public String addSmsTemplate(SmsTemplate smsTemplate) throws Exception {
com.aliyun.dysmsapi20170525.Client client = this.createClient();
AddSmsTemplateRequest addSmsTemplateRequest = new AddSmsTemplateRequest()
.setTemplateType(1)
.setTemplateName(smsTemplate.getTemplateName())
.setTemplateContent(smsTemplate.getTemplateContent())
.setRemark(smsTemplate.getRemark());
AddSmsTemplateResponse response = client.addSmsTemplate(addSmsTemplateRequest);
if (!("OK").equals(response.getBody().getCode())) {
throw new ServiceException(response.getBody().getMessage());
}
return response.getBody().getTemplateCode();
}
@Override
public void deleteSmsTemplate(String templateCode) throws Exception {
com.aliyun.dysmsapi20170525.Client client = this.createClient();
DeleteSmsTemplateRequest deleteSmsTemplateRequest = new DeleteSmsTemplateRequest()
.setTemplateCode(templateCode);
DeleteSmsTemplateResponse response = client.deleteSmsTemplate(deleteSmsTemplateRequest);
if (!("OK").equals(response.getBody().getCode())) {
throw new ServiceException(response.getBody().getMessage());
}
}
/**
* 初始化账号Client
*
* @return Client 短信操作util
*/
public com.aliyun.dysmsapi20170525.Client createClient() {
try {
Setting setting = settingService.get(SettingEnum.SMS_SETTING.name());
if (StrUtil.isBlank(setting.getSettingValue())) {
throw new ServiceException(ResultCode.ALI_SMS_SETTING_ERROR);
}
SmsSetting smsSetting = new Gson().fromJson(setting.getSettingValue(), SmsSetting.class);
Config config = new Config();
//您的AccessKey ID
config.accessKeyId = smsSetting.getAccessKeyId();
//您的AccessKey Secret
config.accessKeySecret = smsSetting.getAccessSecret();
//访问的域名
config.endpoint = "dysmsapi.aliyuncs.com";
return new com.aliyun.dysmsapi20170525.Client(config);
} catch (Exception e) {
log.error("短信初始化错误", e);
}
return null;
} }
/** /**

View File

@ -1,16 +1,46 @@
package cn.lili.modules.sms; package cn.lili.modules.sms.plugin;
import cn.lili.modules.sms.entity.dos.SmsSign; import cn.lili.modules.sms.entity.dos.SmsSign;
import cn.lili.modules.sms.entity.dos.SmsTemplate; import cn.lili.modules.sms.entity.dos.SmsTemplate;
import cn.lili.modules.sms.entity.enums.SmsEnum;
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
import java.util.List;
import java.util.Map; import java.util.Map;
/** /**
* @author Chopper * 短信插件接口
* @version v4.1 *
* @since 2021/2/1 6:05 下午 * @author Bulbasaur
* @since 2023-02-16
*/ */
public interface AliSmsUtil { public interface SmsPlugin {
/**
* 插件名称
*/
SmsEnum pluginName();
/**
* 短信发送
*
* @param mobile 接收手机号
* @param param 参数
* @param templateCode 模版code
* @param signName 签名名称
*/
void sendSmsCode(String signName, String mobile, Map<String, String> param, String templateCode) throws TencentCloudSDKException;
/**
* 短信批量发送
*
* @param mobile 接收手机号
* @param signName 签名
* @param templateCode 模版code
*/
void sendBatchSms(String signName, List<String> mobile, String templateCode);
/** /**
* 申请短信签名 * 申请短信签名
* *

View File

@ -0,0 +1,59 @@
package cn.lili.modules.sms.plugin;
import cn.hutool.json.JSONUtil;
import cn.lili.common.exception.ServiceException;
import cn.lili.modules.sms.entity.enums.SmsEnum;
import cn.lili.modules.sms.plugin.impl.AliSmsPlugin;
import cn.lili.modules.sms.plugin.impl.HuaweiSmsPlugin;
import cn.lili.modules.sms.plugin.impl.TencentSmsPlugin;
import cn.lili.modules.system.entity.dos.Setting;
import cn.lili.modules.system.entity.dto.SmsSetting;
import cn.lili.modules.system.entity.enums.SettingEnum;
import cn.lili.modules.system.service.SettingService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* 短信服务抽象工厂 直接返回操作类
*
* @author Bulbasaur
* @version v1.0
* @since 2023-02-16
*/
@Component
public class SmsPluginFactory {
@Autowired
private SettingService settingService;
/**
* 获取oss client
*
* @return
*/
public SmsPlugin smsPlugin() {
SmsSetting smsSetting = null;
try {
Setting setting = settingService.get(SettingEnum.SMS_SETTING.name());
smsSetting = JSONUtil.toBean(setting.getSettingValue(), SmsSetting.class);
switch (SmsEnum.valueOf(smsSetting.getType())) {
case ALI:
return new AliSmsPlugin(smsSetting);
case TENCENT:
return new TencentSmsPlugin(smsSetting);
case HUAWEI:
return new HuaweiSmsPlugin(smsSetting);
default:
throw new ServiceException();
}
} catch (Exception e) {
throw new ServiceException();
}
}
}

View File

@ -0,0 +1,265 @@
package cn.lili.modules.sms.plugin.impl;
import cn.hutool.json.JSONUtil;
import cn.lili.common.enums.ResultCode;
import cn.lili.common.exception.ServiceException;
import cn.lili.common.utils.Base64Utils;
import cn.lili.modules.sms.entity.dos.SmsSign;
import cn.lili.modules.sms.entity.dos.SmsTemplate;
import cn.lili.modules.sms.entity.enums.SmsEnum;
import cn.lili.modules.sms.plugin.SmsPlugin;
import cn.lili.modules.system.entity.dto.SmsSetting;
import com.aliyun.dysmsapi20170525.models.*;
import com.aliyun.teaopenapi.models.Config;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 阿里云短信插件
*
* @author Bulbasaur
* @since 2023-02-16
*/
@Slf4j
public class AliSmsPlugin implements SmsPlugin {
private SmsSetting smsSetting;
public AliSmsPlugin(SmsSetting smsSetting) {
this.smsSetting = smsSetting;
}
@Override
public SmsEnum pluginName() {
return SmsEnum.ALI;
}
@Override
public void sendSmsCode(String signName, String mobile, Map<String, String> param, String templateCode) {
com.aliyun.dysmsapi20170525.Client client = this.createClient();
SendSmsRequest sendSmsRequest = new SendSmsRequest()
.setSignName(signName)
.setPhoneNumbers(mobile)
.setTemplateCode(templateCode)
.setTemplateParam(JSONUtil.toJsonStr(param));
try {
SendSmsResponse response = client.sendSms(sendSmsRequest);
if (!("OK").equals(response.getBody().getCode())) {
throw new ServiceException(response.getBody().getMessage());
}
} catch (Exception e) {
log.error("发送短信错误", e);
}
}
@Override
public void sendBatchSms(String signName, List<String> mobile, String templateCode) {
com.aliyun.dysmsapi20170525.Client client = this.createClient();
List<String> sign = new ArrayList<String>();
sign.addAll(mobile);
sign.replaceAll(e -> signName);
//手机号拆成多个小组进行发送
List<List<String>> mobileList = new ArrayList<>();
//签名名称多个小组
List<List<String>> signNameList = new ArrayList<>();
//循环分组
for (int i = 0; i < (mobile.size() / 100 + (mobile.size() % 100 == 0 ? 0 : 1)); i++) {
int endPoint = Math.min((100 + (i * 100)), mobile.size());
mobileList.add(mobile.subList((i * 100), endPoint));
signNameList.add(sign.subList((i * 100), endPoint));
}
// //发送短信
for (int i = 0; i < mobileList.size(); i++) {
SendBatchSmsRequest sendBatchSmsRequest = new SendBatchSmsRequest()
.setPhoneNumberJson(JSONUtil.toJsonStr(mobileList.get(i)))
.setSignNameJson(JSONUtil.toJsonStr(signNameList.get(i)))
.setTemplateCode(templateCode);
try {
client.sendBatchSms(sendBatchSmsRequest);
} catch (Exception e) {
log.error("批量发送短信错误", e);
}
}
}
@Override
public void addSmsSign(SmsSign smsSign) throws Exception {
//设置参数添加短信签名
com.aliyun.dysmsapi20170525.Client client = this.createClient();
//营业执照
AddSmsSignRequest.AddSmsSignRequestSignFileList signFileList0 = new AddSmsSignRequest.AddSmsSignRequestSignFileList()
.setFileContents(Base64Utils.encode(smsSign.getBusinessLicense()))
.setFileSuffix(smsSign.getBusinessLicense().substring(smsSign.getBusinessLicense().lastIndexOf(".") + 1));
//授权委托书
AddSmsSignRequest.AddSmsSignRequestSignFileList signFileList1 = new AddSmsSignRequest.AddSmsSignRequestSignFileList()
.setFileContents(Base64Utils.encode(smsSign.getLicense()))
.setFileSuffix(smsSign.getLicense().substring(smsSign.getLicense().lastIndexOf(".")) + 1);
//添加短信签名
AddSmsSignRequest addSmsSignRequest = new AddSmsSignRequest()
.setSignName(smsSign.getSignName())
.setSignSource(smsSign.getSignSource())
.setRemark(smsSign.getRemark())
.setSignFileList(java.util.Arrays.asList(
signFileList0,
signFileList1
));
AddSmsSignResponse response = client.addSmsSign(addSmsSignRequest);
if (!("OK").equals(response.getBody().getCode())) {
throw new ServiceException(response.getBody().getMessage());
}
}
@Override
public void deleteSmsSign(String signName) throws Exception {
com.aliyun.dysmsapi20170525.Client client = this.createClient();
DeleteSmsSignRequest deleteSmsSignRequest = new DeleteSmsSignRequest()
.setSignName(signName);
DeleteSmsSignResponse response = client.deleteSmsSign(deleteSmsSignRequest);
if (!("OK").equals(response.getBody().getCode())) {
throw new ServiceException(response.getBody().getMessage());
}
}
@Override
public Map<String, Object> querySmsSign(String signName) throws Exception {
//设置参数查看短信签名
com.aliyun.dysmsapi20170525.Client client = this.createClient();
QuerySmsSignRequest querySmsSignRequest = new QuerySmsSignRequest().setSignName(signName);
QuerySmsSignResponse response = client.querySmsSign(querySmsSignRequest);
if (!("OK").equals(response.getBody().getCode())) {
throw new ServiceException(response.getBody().getMessage());
}
Map<String, Object> map = new HashMap<>(2);
map.put("SignStatus", response.getBody().getSignStatus());
map.put("Reason", response.getBody().getReason());
return map;
}
@Override
public void modifySmsSign(SmsSign smsSign) throws Exception {
//设置参数添加短信签名
com.aliyun.dysmsapi20170525.Client client = this.createClient();
ModifySmsSignRequest.ModifySmsSignRequestSignFileList signFileList0 = new ModifySmsSignRequest.ModifySmsSignRequestSignFileList()
.setFileContents(Base64Utils.encode(smsSign.getBusinessLicense()))
.setFileSuffix(smsSign.getBusinessLicense().substring(smsSign.getBusinessLicense().lastIndexOf(".") + 1));
ModifySmsSignRequest.ModifySmsSignRequestSignFileList signFileList1 = new ModifySmsSignRequest.ModifySmsSignRequestSignFileList()
.setFileContents(Base64Utils.encode(smsSign.getLicense()))
.setFileSuffix(smsSign.getLicense().substring(smsSign.getBusinessLicense().lastIndexOf(".") + 1));
ModifySmsSignRequest modifySmsSign = new ModifySmsSignRequest()
.setSignName(smsSign.getSignName())
.setSignSource(smsSign.getSignSource())
.setRemark(smsSign.getRemark())
.setSignFileList(java.util.Arrays.asList(
signFileList0,
signFileList1
));
ModifySmsSignResponse response = client.modifySmsSign(modifySmsSign);
if (!("OK").equals(response.getBody().getCode())) {
throw new ServiceException(response.getBody().getMessage());
}
}
@Override
public void modifySmsTemplate(SmsTemplate smsTemplate) throws Exception {
com.aliyun.dysmsapi20170525.Client client = this.createClient();
ModifySmsTemplateRequest modifySmsTemplateRequest = new ModifySmsTemplateRequest()
.setTemplateType(smsTemplate.getTemplateType())
.setTemplateName(smsTemplate.getTemplateName())
.setTemplateContent(smsTemplate.getTemplateContent())
.setRemark(smsTemplate.getRemark())
.setTemplateCode(smsTemplate.getTemplateCode());
ModifySmsTemplateResponse response = client.modifySmsTemplate(modifySmsTemplateRequest);
if (!("OK").equals(response.getBody().getCode())) {
throw new ServiceException(response.getBody().getMessage());
}
}
@Override
public Map<String, Object> querySmsTemplate(String templateCode) throws Exception {
com.aliyun.dysmsapi20170525.Client client = this.createClient();
QuerySmsTemplateRequest querySmsTemplateRequest = new QuerySmsTemplateRequest()
.setTemplateCode(templateCode);
QuerySmsTemplateResponse response = client.querySmsTemplate(querySmsTemplateRequest);
if (!("OK").equals(response.getBody().getCode())) {
throw new ServiceException(response.getBody().getMessage());
}
Map<String, Object> map = new HashMap<>(4);
map.put("TemplateStatus", response.getBody().getTemplateStatus());
map.put("Reason", response.getBody().getReason());
map.put("TemplateCode", response.getBody().getTemplateCode());
return map;
}
@Override
public String addSmsTemplate(SmsTemplate smsTemplate) throws Exception {
com.aliyun.dysmsapi20170525.Client client = this.createClient();
AddSmsTemplateRequest addSmsTemplateRequest = new AddSmsTemplateRequest()
.setTemplateType(1)
.setTemplateName(smsTemplate.getTemplateName())
.setTemplateContent(smsTemplate.getTemplateContent())
.setRemark(smsTemplate.getRemark());
AddSmsTemplateResponse response = client.addSmsTemplate(addSmsTemplateRequest);
if (!("OK").equals(response.getBody().getCode())) {
throw new ServiceException(response.getBody().getMessage());
}
return response.getBody().getTemplateCode();
}
@Override
public void deleteSmsTemplate(String templateCode) throws Exception {
com.aliyun.dysmsapi20170525.Client client = this.createClient();
DeleteSmsTemplateRequest deleteSmsTemplateRequest = new DeleteSmsTemplateRequest()
.setTemplateCode(templateCode);
DeleteSmsTemplateResponse response = client.deleteSmsTemplate(deleteSmsTemplateRequest);
if (!("OK").equals(response.getBody().getCode())) {
throw new ServiceException(response.getBody().getMessage());
}
}
/**
* 初始化账号Client
*
* @return Client 短信操作
*/
public com.aliyun.dysmsapi20170525.Client createClient() {
try {
if (smsSetting == null) {
throw new ServiceException(ResultCode.ALI_SMS_SETTING_ERROR);
}
Config config = new Config();
//您的AccessKey ID
config.accessKeyId = smsSetting.getAccessKeyId();
//您的AccessKey Secret
config.accessKeySecret = smsSetting.getAccessSecret();
//访问的域名
config.endpoint = "dysmsapi.aliyuncs.com";
return new com.aliyun.dysmsapi20170525.Client(config);
} catch (Exception e) {
log.error("短信初始化错误", e);
}
return null;
}
}

View File

@ -0,0 +1,323 @@
package cn.lili.modules.sms.plugin.impl;
import cn.lili.modules.sms.entity.dos.SmsSign;
import cn.lili.modules.sms.entity.dos.SmsTemplate;
import cn.lili.modules.sms.entity.enums.SmsEnum;
import cn.lili.modules.sms.plugin.SmsPlugin;
import cn.lili.modules.system.entity.dto.SmsSetting;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import javax.net.ssl.*;
import java.io.*;
import java.net.URL;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* 腾讯云短信插件
*
* @author Bulbasaur
* @since 2023-02-16
*/
@Slf4j
public class HuaweiSmsPlugin implements SmsPlugin {
private SmsSetting smsSetting;
public HuaweiSmsPlugin(SmsSetting smsSetting) {
this.smsSetting = smsSetting;
}
@Override
public SmsEnum pluginName() {
return SmsEnum.HUAWEI;
}
//无需修改,用于格式化鉴权头域,"X-WSSE"参数赋值
private static final String WSSE_HEADER_FORMAT = "UsernameToken Username=\"%s\",PasswordDigest=\"%s\",Nonce=\"%s\",Created=\"%s\"";
//无需修改,用于格式化鉴权头域,"Authorization"参数赋值
private static final String AUTH_HEADER_VALUE = "WSSE realm=\"SDP\",profile=\"UsernameToken\",type=\"Appkey\"";
@Override
public void sendSmsCode(String signName, String mobile, Map<String, String> param, String templateCode) {
try {
this.sendSms(signName, mobile, "[" + param.values() + "]", templateCode);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void sendBatchSms(String signName, List<String> mobile, String templateCode) {
try {
this.sendSms(signName, StringUtils.join(mobile, ","), null, templateCode);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void addSmsSign(SmsSign smsSign) throws Exception {
}
@Override
public void deleteSmsSign(String signName) throws Exception {
}
@Override
public Map<String, Object> querySmsSign(String signName) throws Exception {
return null;
}
@Override
public void modifySmsSign(SmsSign smsSign) throws Exception {
}
@Override
public void modifySmsTemplate(SmsTemplate smsTemplate) throws Exception {
}
@Override
public Map<String, Object> querySmsTemplate(String templateCode) throws Exception {
return null;
}
@Override
public String addSmsTemplate(SmsTemplate smsTemplate) throws Exception {
return null;
}
@Override
public void deleteSmsTemplate(String templateCode) throws Exception {
}
private void sendSms(String signName, String mobile, String param, String templateCode) throws Exception {
//必填,请参考"开发准备"获取如下数据,替换为实际值
String url = "https://smsapi.cn-north-4.myhuaweicloud.com:443/sms/batchSendSms/v1"; //APP接入地址(在控制台"应用管理"页面获取)+接口访问URI
String appKey = smsSetting.getHuaweiAppKey(); //APP_Key
String appSecret = smsSetting.getHuaweiAppSecret(); //APP_Secret
String sender = smsSetting.getHuaweiSender(); //国内短信签名通道号或国际/港澳台短信通道号
String templateId = templateCode; //模板ID
//条件必填,国内短信关注,当templateId指定的模板类型为通用模板时生效且必填,必须是已审核通过的,与模板类型一致的签名名称
//国际/港澳台短信不用关注该参数
String signature = signName; //签名名称
//必填,全局号码格式(包含国家码),示例:+8615123456789,多个号码之间用英文逗号分隔
String receiver = mobile; //短信接收人号码
//选填,短信状态报告接收地址,推荐使用域名,为空或者不填表示不接收状态报告
String statusCallBack = "";
/**
* 选填,使用无变量模板时请赋空值 String templateParas = "";
* 单变量模板示例:模板内容为"您的验证码是${1}",templateParas可填写为"[\"369751\"]"
* 双变量模板示例:模板内容为"您有${1}件快递请到${2}领取",templateParas可填写为"[\"3\",\"人民公园正门\"]"
* 模板中的每个变量都必须赋值且取值不能为空
* 查看更多模板和变量规范:产品介绍>模板和变量规范
*/
String templateParas = param; //模板变量此处以单变量验证码短信为例请客户自行生成6位验证码并定义为字符串类型以杜绝首位0丢失的问题例如002569变成了2569
//请求Body,不携带签名名称时,signature请填null
String body = buildRequestBody(sender, receiver, templateId, templateParas, statusCallBack, signature);
if (null == body || body.isEmpty()) {
System.out.println("body is null.");
return;
}
//请求Headers中的X-WSSE参数值
String wsseHeader = buildWsseHeader(appKey, appSecret);
if (null == wsseHeader || wsseHeader.isEmpty()) {
System.out.println("wsse header is null.");
return;
}
Writer out = null;
BufferedReader in = null;
StringBuffer result = new StringBuffer();
HttpsURLConnection connection = null;
InputStream is = null;
HostnameVerifier hv = new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
trustAllHttpsCertificates();
try {
URL realUrl = new URL(url);
connection = (HttpsURLConnection) realUrl.openConnection();
connection.setHostnameVerifier(hv);
connection.setDoOutput(true);
connection.setDoInput(true);
connection.setUseCaches(true);
//请求方法
connection.setRequestMethod("POST");
//请求Headers参数
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
connection.setRequestProperty("Authorization", AUTH_HEADER_VALUE);
connection.setRequestProperty("X-WSSE", wsseHeader);
connection.connect();
out = new OutputStreamWriter(connection.getOutputStream());
out.write(body); //发送请求Body参数
out.flush();
out.close();
int status = connection.getResponseCode();
if (200 == status) { //200
is = connection.getInputStream();
} else { //400/401
is = connection.getErrorStream();
}
in = new BufferedReader(new InputStreamReader(is, "UTF-8"));
String line = "";
while ((line = in.readLine()) != null) {
result.append(line);
}
System.out.println(result.toString()); //打印响应消息实体
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (null != out) {
out.close();
}
if (null != is) {
is.close();
}
if (null != in) {
in.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 构造请求Body体
*
* @param sender
* @param receiver
* @param templateId
* @param templateParas
* @param statusCallBack
* @param signature | 签名名称,使用国内短信通用模板时填写
* @return
*/
static String buildRequestBody(String sender, String receiver, String templateId, String templateParas,
String statusCallBack, String signature) {
if (null == sender || null == receiver || null == templateId || sender.isEmpty() || receiver.isEmpty()
|| templateId.isEmpty()) {
System.out.println("buildRequestBody(): sender, receiver or templateId is null.");
return null;
}
Map<String, String> map = new HashMap<String, String>();
map.put("from", sender);
map.put("to", receiver);
map.put("templateId", templateId);
if (null != templateParas && !templateParas.isEmpty()) {
map.put("templateParas", templateParas);
}
if (null != statusCallBack && !statusCallBack.isEmpty()) {
map.put("statusCallback", statusCallBack);
}
if (null != signature && !signature.isEmpty()) {
map.put("signature", signature);
}
StringBuilder sb = new StringBuilder();
String temp = "";
for (String s : map.keySet()) {
try {
temp = URLEncoder.encode(map.get(s), "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
sb.append(s).append("=").append(temp).append("&");
}
return sb.deleteCharAt(sb.length() - 1).toString();
}
/**
* 构造X-WSSE参数值
*
* @param appKey
* @param appSecret
* @return
*/
static String buildWsseHeader(String appKey, String appSecret) {
if (null == appKey || null == appSecret || appKey.isEmpty() || appSecret.isEmpty()) {
System.out.println("buildWsseHeader(): appKey or appSecret is null.");
return null;
}
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
String time = sdf.format(new Date()); //Created
String nonce = UUID.randomUUID().toString().replace("-", ""); //Nonce
MessageDigest md;
byte[] passwordDigest = null;
try {
md = MessageDigest.getInstance("SHA-256");
md.update((nonce + time + appSecret).getBytes());
passwordDigest = md.digest();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
//如果JDK版本是1.8,请加载原生Base64类,并使用如下代码
String passwordDigestBase64Str = Base64.getEncoder().encodeToString(passwordDigest); //PasswordDigest
//如果JDK版本低于1.8,请加载三方库提供Base64类,并使用如下代码
//String passwordDigestBase64Str = Base64.encodeBase64String(passwordDigest); //PasswordDigest
//若passwordDigestBase64Str中包含换行符,请执行如下代码进行修正
//passwordDigestBase64Str = passwordDigestBase64Str.replaceAll("[\\s*\t\n\r]", "");
return String.format(WSSE_HEADER_FORMAT, appKey, passwordDigestBase64Str, nonce, time);
}
/*** @throws Exception
*/
static void trustAllHttpsCertificates() throws Exception {
TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
return;
}
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
return;
}
public X509Certificate[] getAcceptedIssuers() {
return null;
}
}
};
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, null);
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
}
}

View File

@ -0,0 +1,254 @@
package cn.lili.modules.sms.plugin.impl;
import cn.hutool.core.convert.Convert;
import cn.lili.modules.sms.entity.dos.SmsSign;
import cn.lili.modules.sms.entity.dos.SmsTemplate;
import cn.lili.modules.sms.entity.enums.SmsEnum;
import cn.lili.modules.sms.plugin.SmsPlugin;
import cn.lili.modules.system.entity.dto.SmsSetting;
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
import com.tencentcloudapi.common.profile.HttpProfile;
import com.tencentcloudapi.sms.v20210111.SmsClient;
import com.tencentcloudapi.sms.v20210111.models.SendSmsRequest;
import com.tencentcloudapi.sms.v20210111.models.SendSmsResponse;
import lombok.extern.slf4j.Slf4j;
import java.util.List;
import java.util.Map;
/**
* 腾讯云短信插件
*
* @author Bulbasaur
* @since 2023-02-16
*/
@Slf4j
public class TencentSmsPlugin implements SmsPlugin {
private SmsSetting smsSetting;
public TencentSmsPlugin(SmsSetting smsSetting) {
this.smsSetting = smsSetting;
}
@Override
public SmsEnum pluginName() {
return SmsEnum.TENCENT;
}
@Override
public void sendSmsCode(String signName, String mobile, Map<String, String> param, String templateCode) {
try {
/* 实例化一个请求对象根据调用的接口和实际情况可以进一步设置请求参数
* 你可以直接查询SDK源码确定接口有哪些属性可以设置
* 属性可能是基本类型也可能引用了另一个数据结构
* 推荐使用IDE进行开发可以方便的跳转查阅各个接口和数据结构的文档说明 */
SendSmsRequest req = new SendSmsRequest();
/* 填充请求参数,这里request对象的成员变量即对应接口的入参
* 你可以通过官网接口文档或跳转到request对象的定义处查看请求参数的定义
* 基本类型的设置:
* 帮助链接
* 短信控制台: https://console.cloud.tencent.com/smsv2
* 腾讯云短信小助手: https://cloud.tencent.com/document/product/382/3773#.E6.8A.80.E6.9C.AF.E4.BA.A4.E6.B5.81 */
/* 短信应用ID: 短信SdkAppId在 [短信控制台] 添加应用后生成的实际SdkAppId示例如1400006666 */
// 应用 ID 可前往 [短信控制台](https://console.cloud.tencent.com/smsv2/app-manage) 查看
req.setSmsSdkAppId(smsSetting.getTencentSdkAppId());
/* 短信签名内容: 使用 UTF-8 编码,必须填写已审核通过的签名 */
// 签名信息可前往 [国内短信](https://console.cloud.tencent.com/smsv2/csms-sign) [国际/港澳台短信](https://console.cloud.tencent.com/smsv2/isms-sign) 的签名管理查看
req.setSignName(smsSetting.getTencentSignName());
/* 模板 ID: 必须填写已审核通过的模板 ID */
// 模板 ID 可前往 [国内短信](https://console.cloud.tencent.com/smsv2/csms-template) [国际/港澳台短信](https://console.cloud.tencent.com/smsv2/isms-template) 的正文模板管理查看
req.setTemplateId(templateCode);
/* 模板参数: 模板参数的个数需要与 TemplateId 对应模板的变量个数保持一致,若无模板参数,则设置为空 */
req.setTemplateParamSet(param.values().toArray(new String[0]));
/* 下发手机号码采用 E.164 标准+[国家或地区码][手机号]
* 示例如+8613711112222 其中前面有一个+ 86为国家码13711112222为手机号最多不要超过200个手机号 */
String[] phoneNumberSet = {"+86" + mobile};
req.setPhoneNumberSet(phoneNumberSet);
/* 用户的 session 内容(无需要可忽略): 可以携带用户侧 ID 等上下文信息server 会原样返回 */
String sessionContext = "";
req.setSessionContext(sessionContext);
/* 短信码号扩展号(无需要可忽略): 默认未开通,如需开通请联系 [腾讯云短信小助手] */
String extendCode = "";
req.setExtendCode(extendCode);
/* 国际/港澳台短信 SenderId无需要可忽略: 国内短信填空,默认未开通,如需开通请联系 [腾讯云短信小助手] */
String senderid = "";
req.setSenderId(senderid);
/* 通过 client 对象调用 SendSms 方法发起请求注意请求方法名与请求对象是对应的
* 返回的 res 是一个 SendSmsResponse 类的实例与请求对象对应 */
SendSmsResponse res = getClient().SendSms(req);
// 输出json格式的字符串回包
System.out.println(SendSmsResponse.toJsonString(res));
// 也可以取出单个值你可以通过官网接口文档或跳转到response对象的定义处查看返回字段的定义
// System.out.println(res.getRequestId());
/* 当出现以下错误码时快速解决方案参考
* [FailedOperation.SignatureIncorrectOrUnapproved](https://cloud.tencent.com/document/product/382/9558#.E7.9F.AD.E4.BF.A1.E5.8F.91.E9.80.81.E6.8F.90.E7.A4.BA.EF.BC.9Afailedoperation.signatureincorrectorunapproved-.E5.A6.82.E4.BD.95.E5.A4.84.E7.90.86.EF.BC.9F)
* [FailedOperation.TemplateIncorrectOrUnapproved](https://cloud.tencent.com/document/product/382/9558#.E7.9F.AD.E4.BF.A1.E5.8F.91.E9.80.81.E6.8F.90.E7.A4.BA.EF.BC.9Afailedoperation.templateincorrectorunapproved-.E5.A6.82.E4.BD.95.E5.A4.84.E7.90.86.EF.BC.9F)
* [UnauthorizedOperation.SmsSdkAppIdVerifyFail](https://cloud.tencent.com/document/product/382/9558#.E7.9F.AD.E4.BF.A1.E5.8F.91.E9.80.81.E6.8F.90.E7.A4.BA.EF.BC.9Aunauthorizedoperation.smssdkappidverifyfail-.E5.A6.82.E4.BD.95.E5.A4.84.E7.90.86.EF.BC.9F)
* [UnsupportedOperation.ContainDomesticAndInternationalPhoneNumber](https://cloud.tencent.com/document/product/382/9558#.E7.9F.AD.E4.BF.A1.E5.8F.91.E9.80.81.E6.8F.90.E7.A4.BA.EF.BC.9Aunsupportedoperation.containdomesticandinternationalphonenumber-.E5.A6.82.E4.BD.95.E5.A4.84.E7.90.86.EF.BC.9F)
* 更多错误可咨询[腾讯云助手](https://tccc.qcloud.com/web/im/index.html#/chat?webAppId=8fa15978f85cb41f7e2ea36920cb3ae1&title=Sms)
*/
} catch (TencentCloudSDKException tencentCloudSDKException) {
tencentCloudSDKException.printStackTrace();
}
}
@Override
public void sendBatchSms(String signName, List<String> mobile, String templateCode) {
try {
/* 实例化一个请求对象根据调用的接口和实际情况可以进一步设置请求参数
* 你可以直接查询SDK源码确定接口有哪些属性可以设置
* 属性可能是基本类型也可能引用了另一个数据结构
* 推荐使用IDE进行开发可以方便的跳转查阅各个接口和数据结构的文档说明 */
SendSmsRequest req = new SendSmsRequest();
/* 填充请求参数,这里request对象的成员变量即对应接口的入参
* 你可以通过官网接口文档或跳转到request对象的定义处查看请求参数的定义
* 基本类型的设置:
* 帮助链接
* 短信控制台: https://console.cloud.tencent.com/smsv2
* 腾讯云短信小助手: https://cloud.tencent.com/document/product/382/3773#.E6.8A.80.E6.9C.AF.E4.BA.A4.E6.B5.81 */
/* 短信应用ID: 短信SdkAppId在 [短信控制台] 添加应用后生成的实际SdkAppId示例如1400006666 */
// 应用 ID 可前往 [短信控制台](https://console.cloud.tencent.com/smsv2/app-manage) 查看
req.setSmsSdkAppId(smsSetting.getTencentSdkAppId());
/* 短信签名内容: 使用 UTF-8 编码,必须填写已审核通过的签名 */
// 签名信息可前往 [国内短信](https://console.cloud.tencent.com/smsv2/csms-sign) [国际/港澳台短信](https://console.cloud.tencent.com/smsv2/isms-sign) 的签名管理查看
req.setSignName(smsSetting.getTencentSignName());
/* 模板 ID: 必须填写已审核通过的模板 ID */
// 模板 ID 可前往 [国内短信](https://console.cloud.tencent.com/smsv2/csms-template) [国际/港澳台短信](https://console.cloud.tencent.com/smsv2/isms-template) 的正文模板管理查看
req.setTemplateId(templateCode);
/* 模板参数: 模板参数的个数需要与 TemplateId 对应模板的变量个数保持一致,若无模板参数,则设置为空 */
req.setTemplateParamSet(null);
/* 下发手机号码采用 E.164 标准+[国家或地区码][手机号]
* 示例如+8613711112222 其中前面有一个+ 86为国家码13711112222为手机号最多不要超过200个手机号 */
req.setPhoneNumberSet(Convert.toStrArray(mobile));
/* 用户的 session 内容(无需要可忽略): 可以携带用户侧 ID 等上下文信息server 会原样返回 */
String sessionContext = "";
req.setSessionContext(sessionContext);
/* 短信码号扩展号(无需要可忽略): 默认未开通,如需开通请联系 [腾讯云短信小助手] */
String extendCode = "";
req.setExtendCode(extendCode);
/* 国际/港澳台短信 SenderId无需要可忽略: 国内短信填空,默认未开通,如需开通请联系 [腾讯云短信小助手] */
String senderid = "";
req.setSenderId(senderid);
/* 通过 client 对象调用 SendSms 方法发起请求注意请求方法名与请求对象是对应的
* 返回的 res 是一个 SendSmsResponse 类的实例与请求对象对应 */
SendSmsResponse res = getClient().SendSms(req);
// 输出json格式的字符串回包
System.out.println(SendSmsResponse.toJsonString(res));
// 也可以取出单个值你可以通过官网接口文档或跳转到response对象的定义处查看返回字段的定义
// System.out.println(res.getRequestId());
/* 当出现以下错误码时快速解决方案参考
* [FailedOperation.SignatureIncorrectOrUnapproved](https://cloud.tencent.com/document/product/382/9558#.E7.9F.AD.E4.BF.A1.E5.8F.91.E9.80.81.E6.8F.90.E7.A4.BA.EF.BC.9Afailedoperation.signatureincorrectorunapproved-.E5.A6.82.E4.BD.95.E5.A4.84.E7.90.86.EF.BC.9F)
* [FailedOperation.TemplateIncorrectOrUnapproved](https://cloud.tencent.com/document/product/382/9558#.E7.9F.AD.E4.BF.A1.E5.8F.91.E9.80.81.E6.8F.90.E7.A4.BA.EF.BC.9Afailedoperation.templateincorrectorunapproved-.E5.A6.82.E4.BD.95.E5.A4.84.E7.90.86.EF.BC.9F)
* [UnauthorizedOperation.SmsSdkAppIdVerifyFail](https://cloud.tencent.com/document/product/382/9558#.E7.9F.AD.E4.BF.A1.E5.8F.91.E9.80.81.E6.8F.90.E7.A4.BA.EF.BC.9Aunauthorizedoperation.smssdkappidverifyfail-.E5.A6.82.E4.BD.95.E5.A4.84.E7.90.86.EF.BC.9F)
* [UnsupportedOperation.ContainDomesticAndInternationalPhoneNumber](https://cloud.tencent.com/document/product/382/9558#.E7.9F.AD.E4.BF.A1.E5.8F.91.E9.80.81.E6.8F.90.E7.A4.BA.EF.BC.9Aunsupportedoperation.containdomesticandinternationalphonenumber-.E5.A6.82.E4.BD.95.E5.A4.84.E7.90.86.EF.BC.9F)
* 更多错误可咨询[腾讯云助手](https://tccc.qcloud.com/web/im/index.html#/chat?webAppId=8fa15978f85cb41f7e2ea36920cb3ae1&title=Sms)
*/
} catch (TencentCloudSDKException tencentCloudSDKException) {
tencentCloudSDKException.printStackTrace();
}
}
@Override
public void addSmsSign(SmsSign smsSign) throws Exception {
}
@Override
public void deleteSmsSign(String signName) throws Exception {
}
@Override
public Map<String, Object> querySmsSign(String signName) throws Exception {
return null;
}
@Override
public void modifySmsSign(SmsSign smsSign) throws Exception {
}
@Override
public void modifySmsTemplate(SmsTemplate smsTemplate) throws Exception {
}
@Override
public Map<String, Object> querySmsTemplate(String templateCode) throws Exception {
return null;
}
@Override
public String addSmsTemplate(SmsTemplate smsTemplate) throws Exception {
return null;
}
@Override
public void deleteSmsTemplate(String templateCode) throws Exception {
}
/**
* 获取smsclient
*
* @return
*/
private SmsClient getClient() {
/* 必要步骤
* 实例化一个认证对象入参需要传入腾讯云账户密钥对secretIdsecretKey
* 这里采用的是从环境变量读取的方式需要在环境变量中先设置这两个值
* 你也可以直接在代码中写死密钥对但是小心不要将代码复制上传或者分享给他人
* 以免泄露密钥对危及你的财产安全
* SecretIdSecretKey 查询: https://console.cloud.tencent.com/cam/capi */
Credential cred = new Credential(smsSetting.getTencentSecretId(), smsSetting.getTencentSecretKey());
// 实例化一个http选项可选没有特殊需求可以跳过
HttpProfile httpProfile = new HttpProfile();
// 设置代理无需要直接忽略
// httpProfile.setProxyHost("真实代理ip");
// httpProfile.setProxyPort(真实代理端口);
/* SDK默认使用POST方法
* 如果你一定要使用GET方法可以在这里设置GET方法无法处理一些较大的请求 */
httpProfile.setReqMethod("POST");
/* SDK有默认的超时时间非必要请不要进行调整
* 如有需要请在代码中查阅以获取最新的默认值 */
httpProfile.setConnTimeout(60);
/* 指定接入地域域名,默认就近地域接入域名为 sms.tencentcloudapi.com ,也支持指定地域域名访问,例如广州地域的域名为 sms.ap-guangzhou.tencentcloudapi.com */
httpProfile.setEndpoint("sms.tencentcloudapi.com");
/* 实例化要请求产品(以sms为例)的client对象
* 第二个参数是地域信息可以直接填写字符串ap-guangzhou支持的地域列表参考 https://cloud.tencent.com/document/api/382/52071#.E5.9C.B0.E5.9F.9F.E5.88.97.E8.A1.A8 */
return new SmsClient(cred, "ap-guangzhou");
}
}

View File

@ -3,9 +3,9 @@ package cn.lili.modules.sms.serviceimpl;
import cn.lili.common.enums.ResultCode; import cn.lili.common.enums.ResultCode;
import cn.lili.common.exception.ServiceException; import cn.lili.common.exception.ServiceException;
import cn.lili.common.vo.PageVO; import cn.lili.common.vo.PageVO;
import cn.lili.modules.sms.AliSmsUtil;
import cn.lili.modules.sms.entity.dos.SmsSign; import cn.lili.modules.sms.entity.dos.SmsSign;
import cn.lili.modules.sms.mapper.SmsSignMapper; import cn.lili.modules.sms.mapper.SmsSignMapper;
import cn.lili.modules.sms.plugin.SmsPluginFactory;
import cn.lili.modules.sms.service.SmsSignService; import cn.lili.modules.sms.service.SmsSignService;
import cn.lili.mybatis.util.PageUtil; import cn.lili.mybatis.util.PageUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
@ -23,6 +23,7 @@ import java.util.Map;
/** /**
* 短信签名业务层实现 * 短信签名业务层实现
*
* @author Chopper * @author Chopper
* @since 2021/1/30 4:27 下午 * @since 2021/1/30 4:27 下午
*/ */
@ -30,7 +31,7 @@ import java.util.Map;
@Service @Service
public class SmsSignServiceImpl extends ServiceImpl<SmsSignMapper, SmsSign> implements SmsSignService { public class SmsSignServiceImpl extends ServiceImpl<SmsSignMapper, SmsSign> implements SmsSignService {
@Autowired @Autowired
private AliSmsUtil aliSmsUtil; private SmsPluginFactory smsPluginFactory;
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
@ -40,11 +41,11 @@ public class SmsSignServiceImpl extends ServiceImpl<SmsSignMapper, SmsSign> impl
if (this.getOne(new QueryWrapper<SmsSign>().eq("sign_name", smsSign.getSignName())) != null) { if (this.getOne(new QueryWrapper<SmsSign>().eq("sign_name", smsSign.getSignName())) != null) {
throw new ServiceException(ResultCode.SMS_SIGN_EXIST_ERROR); throw new ServiceException(ResultCode.SMS_SIGN_EXIST_ERROR);
} }
aliSmsUtil.addSmsSign(smsSign); smsPluginFactory.smsPlugin().addSmsSign(smsSign);
smsSign.setSignStatus(0); smsSign.setSignStatus(0);
this.save(smsSign); this.save(smsSign);
} catch (Exception e) { } catch (Exception e) {
log.error("添加短信签名错误",e); log.error("添加短信签名错误", e);
} }
} }
@ -54,11 +55,11 @@ public class SmsSignServiceImpl extends ServiceImpl<SmsSignMapper, SmsSign> impl
try { try {
SmsSign smsSign = this.getById(id); SmsSign smsSign = this.getById(id);
if (smsSign != null) { if (smsSign != null) {
aliSmsUtil.deleteSmsSign(smsSign.getSignName()); smsPluginFactory.smsPlugin().deleteSmsSign(smsSign.getSignName());
this.removeById(id); this.removeById(id);
} }
} catch (Exception e) { } catch (Exception e) {
log.error("删除短信签名错误",e); log.error("删除短信签名错误", e);
} }
} }
@ -72,13 +73,13 @@ public class SmsSignServiceImpl extends ServiceImpl<SmsSignMapper, SmsSign> impl
List<SmsSign> list = list(new LambdaQueryWrapper<SmsSign>().ne(SmsSign::getSignStatus, 1)); List<SmsSign> list = list(new LambdaQueryWrapper<SmsSign>().ne(SmsSign::getSignStatus, 1));
//查询签名状态 //查询签名状态
for (SmsSign smsSign : list) { for (SmsSign smsSign : list) {
map = aliSmsUtil.querySmsSign(smsSign.getSignName()); map = smsPluginFactory.smsPlugin().querySmsSign(smsSign.getSignName());
smsSign.setSignStatus((Integer) map.get("SignStatus")); smsSign.setSignStatus((Integer) map.get("SignStatus"));
smsSign.setReason(map.get("Reason").toString()); smsSign.setReason(map.get("Reason").toString());
this.updateById(smsSign); this.updateById(smsSign);
} }
} catch (Exception e) { } catch (Exception e) {
log.error("查询短信签名错误",e); log.error("查询短信签名错误", e);
} }
} }
@ -86,10 +87,10 @@ public class SmsSignServiceImpl extends ServiceImpl<SmsSignMapper, SmsSign> impl
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public void modifySmsSign(SmsSign smsSign) { public void modifySmsSign(SmsSign smsSign) {
try { try {
aliSmsUtil.modifySmsSign(smsSign); smsPluginFactory.smsPlugin().modifySmsSign(smsSign);
this.updateById(smsSign); this.updateById(smsSign);
} catch (Exception e) { } catch (Exception e) {
log.error("更新短信签名错误",e); log.error("更新短信签名错误", e);
} }
} }

View File

@ -1,9 +1,9 @@
package cn.lili.modules.sms.serviceimpl; package cn.lili.modules.sms.serviceimpl;
import cn.lili.common.vo.PageVO; import cn.lili.common.vo.PageVO;
import cn.lili.modules.sms.AliSmsUtil;
import cn.lili.modules.sms.entity.dos.SmsTemplate; import cn.lili.modules.sms.entity.dos.SmsTemplate;
import cn.lili.modules.sms.mapper.SmsTemplateMapper; import cn.lili.modules.sms.mapper.SmsTemplateMapper;
import cn.lili.modules.sms.plugin.SmsPluginFactory;
import cn.lili.modules.sms.service.SmsTemplateService; import cn.lili.modules.sms.service.SmsTemplateService;
import cn.lili.mybatis.util.PageUtil; import cn.lili.mybatis.util.PageUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
@ -19,25 +19,27 @@ import java.util.Map;
/** /**
* 短信模板业务层实现 * 短信模板业务层实现
*
* @author Chopper * @author Chopper
* @since 2021/1/30 4:27 下午 * @since 2021/1/30 4:27 下午
*/ */
@Slf4j @Slf4j
@Service @Service
public class SmsTemplateServiceImpl extends ServiceImpl<SmsTemplateMapper, SmsTemplate> implements SmsTemplateService { public class SmsTemplateServiceImpl extends ServiceImpl<SmsTemplateMapper, SmsTemplate> implements SmsTemplateService {
@Autowired @Autowired
private AliSmsUtil aliSmsUtil; private SmsPluginFactory smsPluginFactory;
@Override @Override
public void addSmsTemplate(SmsTemplate smsTemplate) { public void addSmsTemplate(SmsTemplate smsTemplate) {
try { try {
smsTemplate.setTemplateCode(aliSmsUtil.addSmsTemplate(smsTemplate)); smsTemplate.setTemplateCode(smsPluginFactory.smsPlugin().addSmsTemplate(smsTemplate));
smsTemplate.setTemplateStatus(0); smsTemplate.setTemplateStatus(0);
smsTemplate.setTemplateType(1); smsTemplate.setTemplateType(1);
this.save(smsTemplate); this.save(smsTemplate);
} catch (Exception e) { } catch (Exception e) {
log.error("添加短信模板错误",e); log.error("添加短信模板错误", e);
} }
} }
@ -46,11 +48,11 @@ public class SmsTemplateServiceImpl extends ServiceImpl<SmsTemplateMapper, SmsTe
try { try {
SmsTemplate smsTemplate = this.getById(id); SmsTemplate smsTemplate = this.getById(id);
if (smsTemplate.getTemplateCode() != null) { if (smsTemplate.getTemplateCode() != null) {
aliSmsUtil.deleteSmsTemplate(smsTemplate.getTemplateCode()); smsPluginFactory.smsPlugin().deleteSmsTemplate(smsTemplate.getTemplateCode());
} }
this.removeById(id); this.removeById(id);
} catch (Exception e) { } catch (Exception e) {
log.error("删除短信模板错误",e); log.error("删除短信模板错误", e);
} }
} }
@ -63,25 +65,25 @@ public class SmsTemplateServiceImpl extends ServiceImpl<SmsTemplateMapper, SmsTe
List<SmsTemplate> list = list(new LambdaQueryWrapper<SmsTemplate>().eq(SmsTemplate::getTemplateStatus, 0)); List<SmsTemplate> list = list(new LambdaQueryWrapper<SmsTemplate>().eq(SmsTemplate::getTemplateStatus, 0));
//查询签名状态 //查询签名状态
for (SmsTemplate smsTemplate : list) { for (SmsTemplate smsTemplate : list) {
map = aliSmsUtil.querySmsTemplate(smsTemplate.getTemplateCode()); map = smsPluginFactory.smsPlugin().querySmsTemplate(smsTemplate.getTemplateCode());
smsTemplate.setTemplateStatus((Integer) map.get("TemplateStatus")); smsTemplate.setTemplateStatus((Integer) map.get("TemplateStatus"));
smsTemplate.setReason(map.get("Reason").toString()); smsTemplate.setReason(map.get("Reason").toString());
smsTemplate.setTemplateCode(map.get("TemplateCode").toString()); smsTemplate.setTemplateCode(map.get("TemplateCode").toString());
this.updateById(smsTemplate); this.updateById(smsTemplate);
} }
} catch (Exception e) { } catch (Exception e) {
log.error("查询短信模板错误",e); log.error("查询短信模板错误", e);
} }
} }
@Override @Override
public void modifySmsTemplate(SmsTemplate smsTemplate) { public void modifySmsTemplate(SmsTemplate smsTemplate) {
try { try {
aliSmsUtil.modifySmsTemplate(smsTemplate); smsPluginFactory.smsPlugin().modifySmsTemplate(smsTemplate);
smsTemplate.setTemplateStatus(0); smsTemplate.setTemplateStatus(0);
this.updateById(smsTemplate); this.updateById(smsTemplate);
} catch (Exception e) { } catch (Exception e) {
log.error("重新提交短信模板错误",e); log.error("重新提交短信模板错误", e);
} }
} }

View File

@ -11,23 +11,29 @@ import java.io.Serializable;
* @since 2020-03-10 10:04 上午 * @since 2020-03-10 10:04 上午
*/ */
@Data @Data
public class KuaidiSetting implements Serializable { public class LogisticsSetting implements Serializable {
private static final long serialVersionUID = 3520379500723173689L; private static final long serialVersionUID = 3520379500723173689L;
/**
* 快递查询类型
*/
private String type;
/** /**
* 企业id * 企业id
*/ */
private String ebusinessID; private String kdniaoEbusinessID;
/** /**
* 密钥 * 密钥
*/ */
private String appKey; private String kdniaoAppKey;
/**
* api地址
*/
private String reqURL;
/** /**
* 电子面单api地址 * 快递100 授权码请申请企业版获取
*/ */
private String sheetReqURL; private String kuaidi100Customer;
/**
* 快递100 Key
*/
private String kuaidi100Key;
} }

View File

@ -24,25 +24,25 @@ public class OssSetting implements Serializable {
private String type; private String type;
/** /**
* 域名 * 阿里云-域名
*/ */
private String endPoint = ""; private String aliyunOSSEndPoint = "";
/** /**
* 储存空间 * 阿里云-储存空间
*/ */
private String bucketName = ""; private String aliyunOSSBucketName = "";
/** /**
* 存放路径路径 * 阿里云-存放路径路径
*/ */
private String picLocation = ""; private String aliyunOSSPicLocation = "";
/** /**
* 密钥id * 阿里云-密钥id
*/ */
private String accessKeyId = ""; private String aliyunOSSAccessKeyId = "";
/** /**
* 密钥 * 阿里云-密钥
*/ */
private String accessKeySecret = ""; private String aliyunOSSAccessKeySecret = "";
/** /**
@ -71,6 +71,48 @@ public class OssSetting implements Serializable {
private String m_bucketName; private String m_bucketName;
/**
* 华为云-发起者的Access Key
*
* @return
*/
String huaweicloudOBSAccessKey;
/**
* 华为云-密钥
*/
String huaweicloudOBSSecretKey;
/**
* 华为云OBS-节点
*/
String huaweicloudOBSEndPoint;
/**
* 华为云OBS-
*/
private String huaweicloudOBSBucketName = "";
/**
* 腾讯云 用户的 SecretId
*/
String tencentCOSSecretId;
/**
* 腾讯云 用户的 SecretKey
*/
String tencentCOSSecretKey;
/**
* 腾讯云 bucket 的地域
*/
String tencentCOSRegion;
/**
* 腾讯云 bucket
*/
String tencentCOSBucket;
/**
* 腾讯云-域名
*/
private String tencentCOSEndPoint = "";
public String getType() { public String getType() {
//默认给阿里云oss存储类型 //默认给阿里云oss存储类型
if (StringUtils.isEmpty(type)) { if (StringUtils.isEmpty(type)) {

View File

@ -7,23 +7,60 @@ import java.io.Serializable;
/** /**
* 短信配置 * 短信配置
* 这里在前台不做调整方便客户直接把服务商的内容配置在我们平台 * 这里在前台不做调整方便客户直接把服务商的内容配置在我们平台
*
* @author Chopper * @author Chopper
* @since 2020/11/30 15:23 * @since 2020/11/30 15:23
*/ */
@Data @Data
public class SmsSetting implements Serializable { public class SmsSetting implements Serializable {
/**
* 类型
*/
private String type;
/** /**
* 从上到下yi依次是
* 节点地址
* key * key
*/
private String accessKeyId;
/**
* 密钥 * 密钥
*/
private String accessSecret;
/**
* 签名 * 签名
*/ */
private String regionId;
private String accessKeyId;
private String accessSecret;
private String signName; private String signName;
/**
* 腾讯云 用户的 SecretId
*/
String tencentSecretId;
/**
* 腾讯云 用户的 SecretKey
*/
String tencentSecretKey;
/* 短信应用ID: 短信SdkAppId在 [短信控制台] 添加应用后生成的实际SdkAppId示例如1400006666 */
String tencentSdkAppId;
/* 短信签名内容: 使用 UTF-8 编码,必须填写已审核通过的签名 */
String tencentSignName;
/**
* 华为 APP_Key
*/
String huaweiAppKey;
/**
* 华为 APP_Secret
*/
String huaweiAppSecret;
/**
* 国内短信签名通道号或国际/港澳台短信通道号
*/
String huaweiSender;
/**
* 签名名称
*/
String huaweiSignature;
} }

View File

@ -17,8 +17,8 @@ public enum SettingEnum {
EMAIL_SETTING, EMAIL_SETTING,
//商品设置 //商品设置
GOODS_SETTING, GOODS_SETTING,
//快递设置 //快递设置
KUAIDI_SETTING, LOGISTICS_SETTING,
//订单配置 //订单配置
ORDER_SETTING, ORDER_SETTING,
//阿里OSS配置 //阿里OSS配置
@ -45,6 +45,9 @@ public enum SettingEnum {
ALIPAY_PAYMENT, ALIPAY_PAYMENT,
//微信支付设置 //微信支付设置
WECHAT_PAYMENT, WECHAT_PAYMENT,
//银联支付设置
UNION_PAYMENT,
//热词设置 //热词设置
HOT_WORDS HOT_WORDS
} }

View File

@ -19,10 +19,15 @@ public interface LogisticsService extends IService<Logistics> {
* *
* @param logisticsId 物流公司ID * @param logisticsId 物流公司ID
* @param logisticsNo 单号 * @param logisticsNo 单号
* @param customerName 手机号后四位 * @param phone 手机号
* @return * @return
*/ */
Traces getLogistic(String logisticsId, String logisticsNo, String customerName); Traces getLogisticTrack(String logisticsId, String logisticsNo, String phone);
Traces getLogisticMapTrack(String logisticsId, String logisticsNo, String phone, String from, String to);
String labelOrder(String orderSn, String logisticsId);
/** /**
* 获取已开启的物流公司列表 * 获取已开启的物流公司列表

View File

@ -1,34 +1,33 @@
package cn.lili.modules.system.serviceimpl; package cn.lili.modules.system.serviceimpl;
import cn.hutool.core.text.CharSequenceUtil;
import cn.lili.common.enums.ResultCode; import cn.lili.common.enums.ResultCode;
import cn.lili.common.enums.SwitchEnum; import cn.lili.common.enums.SwitchEnum;
import cn.lili.common.exception.ServiceException; import cn.lili.common.exception.ServiceException;
import cn.lili.common.security.OperationalJudgment;
import cn.lili.modules.logistics.LogisticsPluginFactory;
import cn.lili.modules.logistics.entity.dto.LabelOrderDTO;
import cn.lili.modules.member.service.StoreLogisticsService;
import cn.lili.modules.order.order.entity.dos.Order;
import cn.lili.modules.order.order.entity.dos.OrderItem;
import cn.lili.modules.order.order.entity.enums.DeliverStatusEnum;
import cn.lili.modules.order.order.entity.enums.OrderStatusEnum;
import cn.lili.modules.order.order.service.OrderItemService;
import cn.lili.modules.order.order.service.OrderService;
import cn.lili.modules.store.entity.dos.StoreLogistics;
import cn.lili.modules.store.entity.dto.StoreDeliverGoodsAddressDTO;
import cn.lili.modules.store.service.StoreDetailService;
import cn.lili.modules.system.entity.dos.Logistics; import cn.lili.modules.system.entity.dos.Logistics;
import cn.lili.modules.system.entity.dos.Setting;
import cn.lili.modules.system.entity.dto.KuaidiSetting;
import cn.lili.modules.system.entity.enums.SettingEnum;
import cn.lili.modules.system.entity.vo.Traces; import cn.lili.modules.system.entity.vo.Traces;
import cn.lili.modules.system.mapper.LogisticsMapper; import cn.lili.modules.system.mapper.LogisticsMapper;
import cn.lili.modules.system.service.LogisticsService; import cn.lili.modules.system.service.LogisticsService;
import cn.lili.modules.system.service.SettingService;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.gson.Gson;
import groovy.util.logging.Slf4j; import groovy.util.logging.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
/** /**
* 物流公司业务层实现 * 物流公司业务层实现
@ -40,19 +39,72 @@ import java.util.Map;
@Service @Service
public class LogisticsServiceImpl extends ServiceImpl<LogisticsMapper, Logistics> implements LogisticsService { public class LogisticsServiceImpl extends ServiceImpl<LogisticsMapper, Logistics> implements LogisticsService {
@Autowired @Autowired
private SettingService settingService; private LogisticsPluginFactory logisticsPluginFactory;
@Autowired
private OrderService orderService;
@Autowired
private OrderItemService orderItemService;
@Autowired
private StoreLogisticsService storeLogisticsService;
@Autowired
private StoreDetailService storeDetailService;
@Override @Override
public Traces getLogistic(String logisticsId, String logisticsNo, String customerName) { public Traces getLogisticTrack(String logisticsId, String logisticsNo, String phone) {
try { try {
return getOrderTracesByJson(logisticsId, logisticsNo,customerName); return logisticsPluginFactory.filePlugin().pollQuery(this.getById(logisticsId), logisticsNo, phone);
} catch (Exception e) { } catch (Exception e) {
log.error("获取物流公司错误",e); log.error("获取物流公司错误", e);
} }
return null; return null;
} }
@Override
public Traces getLogisticMapTrack(String logisticsId, String logisticsNo, String phone, String from, String to) {
try {
return logisticsPluginFactory.filePlugin().pollMapTrack(this.getById(logisticsId), logisticsNo, phone, from, to);
} catch (Exception e) {
log.error("获取物流公司错误", e);
}
return null;
}
@Override
public String labelOrder(String orderSn, String logisticsId) {
//获取订单及子订单
Order order = OperationalJudgment.judgment(orderService.getBySn(orderSn));
if (order.getDeliverStatus().equals(DeliverStatusEnum.UNDELIVERED.name())
&& order.getOrderStatus().equals(OrderStatusEnum.UNDELIVERED.name())) {
//订单货物
List<OrderItem> orderItems = orderItemService.getByOrderSn(orderSn);
//获取对应物流
Logistics logistics = this.getById(logisticsId);
// 店铺-物流公司设置
LambdaQueryWrapper<StoreLogistics> lambdaQueryWrapper = Wrappers.lambdaQuery();
lambdaQueryWrapper.eq(StoreLogistics::getLogisticsId, logisticsId);
lambdaQueryWrapper.eq(StoreLogistics::getStoreId, order.getStoreId());
StoreLogistics storeLogistics = storeLogisticsService.getOne(lambdaQueryWrapper);
//获取店家信息
StoreDeliverGoodsAddressDTO storeDeliverGoodsAddressDTO = storeDetailService.getStoreDeliverGoodsAddressDto(order.getStoreId());
LabelOrderDTO labelOrderDTO = new LabelOrderDTO();
labelOrderDTO.setOrder(order);
labelOrderDTO.setOrderItems(orderItems);
labelOrderDTO.setLogistics(logistics);
labelOrderDTO.setStoreLogistics(storeLogistics);
labelOrderDTO.setStoreDeliverGoodsAddressDTO(storeDeliverGoodsAddressDTO);
//触发电子面单
return logisticsPluginFactory.filePlugin().labelOrder(labelOrderDTO);
} else {
throw new ServiceException(ResultCode.ORDER_LABEL_ORDER_ERROR);
}
}
@Override @Override
public List<Logistics> getOpenLogistics() { public List<Logistics> getOpenLogistics() {
LambdaQueryWrapper<Logistics> queryWrapper = new LambdaQueryWrapper<>(); LambdaQueryWrapper<Logistics> queryWrapper = new LambdaQueryWrapper<>();
@ -60,218 +112,4 @@ public class LogisticsServiceImpl extends ServiceImpl<LogisticsMapper, Logistics
return this.list(queryWrapper); return this.list(queryWrapper);
} }
/**
* 获取物流信息
*
* @param logisticsId 物流公司ID
* @param expNo 物流单号
* @param customerName 手机号后四位
* @return 物流信息
* @throws Exception
*/
private Traces getOrderTracesByJson(String logisticsId, String expNo, String customerName) throws Exception {
Setting setting = settingService.get(SettingEnum.KUAIDI_SETTING.name());
if (CharSequenceUtil.isBlank(setting.getSettingValue())) {
throw new ServiceException(ResultCode.LOGISTICS_NOT_SETTING);
}
KuaidiSetting kuaidiSetting = new Gson().fromJson(setting.getSettingValue(), KuaidiSetting.class);
//ID
String EBusinessID = kuaidiSetting.getEbusinessID();
//KEY
String AppKey = kuaidiSetting.getAppKey();
//请求url
String ReqURL = kuaidiSetting.getReqURL();
Logistics logistics = this.getById(logisticsId);
if (logistics != null) {
String requestData = "{'OrderCode':'','ShipperCode':'" + logistics.getCode() +
"','LogisticCode':'" + expNo + "'" +
",'CustomerName':'" + customerName + "'"+
"}";
Map<String, String> params = new HashMap<>(8);
params.put("RequestData", urlEncoder(requestData, "UTF-8"));
params.put("EBusinessID", EBusinessID);
params.put("RequestType", "1002");
String dataSign = encrypt(requestData, AppKey, "UTF-8");
params.put("DataSign", urlEncoder(dataSign, "UTF-8"));
params.put("DataType", "2");
String result = sendPost(ReqURL, params);
Map map = (Map) JSON.parse(result);
return new Traces(logistics.getName(), expNo, (List<Map>) map.get("Traces"));
}
return null;
}
/**
* MD5加密
*
* @param str 内容
* @param charset 编码方式
* @throws Exception
*/
@SuppressWarnings("unused")
private String MD5(String str, String charset) throws Exception {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(str.getBytes(charset));
byte[] result = md.digest();
StringBuffer sb = new StringBuffer(32);
for (int i = 0; i < result.length; i++) {
int val = result[i] & 0xff;
if (val <= 0xf) {
sb.append("0");
}
sb.append(Integer.toHexString(val));
}
return sb.toString().toLowerCase();
}
/**
* base64编码
*
* @param str 内容
* @param charset 编码方式di
* @throws UnsupportedEncodingException
*/
private String base64(String str, String charset) throws UnsupportedEncodingException {
return base64Encode(str.getBytes(charset));
}
@SuppressWarnings("unused")
private String urlEncoder(String str, String charset) throws UnsupportedEncodingException {
return URLEncoder.encode(str, charset);
}
/**
* 电商Sign签名生成
*
* @param content 内容
* @param keyValue Appkey
* @param charset 编码方式
* @return DataSign签名
* @throws UnsupportedEncodingException ,Exception
*/
@SuppressWarnings("unused")
private String encrypt(String content, String keyValue, String charset) throws Exception {
if (keyValue != null) {
return base64(MD5(content + keyValue, charset), charset);
}
return base64(MD5(content, charset), charset);
}
/**
* 向指定 URL 发送POST方法的请求
*
* @param url 发送请求的 URL
* @param params 请求的参数集合
* @return 远程资源的响应结果
*/
@SuppressWarnings("unused")
private String sendPost(String url, Map<String, String> params) {
OutputStreamWriter out = null;
BufferedReader in = null;
StringBuilder result = new StringBuilder();
try {
URL realUrl = new URL(url);
HttpURLConnection conn = (HttpURLConnection) realUrl.openConnection();
//发送POST请求必须设置如下两行
conn.setDoOutput(true);
conn.setDoInput(true);
//POST方法
conn.setRequestMethod("POST");
//设置通用的请求属性
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");
conn.setRequestProperty("user-agent",
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
conn.connect();
//获取URLConnection对象对应的输出流
out = new OutputStreamWriter(conn.getOutputStream(), StandardCharsets.UTF_8);
//发送请求参数
if (params != null) {
StringBuilder param = new StringBuilder();
for (Map.Entry<String, String> entry : params.entrySet()) {
if (param.length() > 0) {
param.append("&");
}
param.append(entry.getKey());
param.append("=");
param.append(entry.getValue());
}
out.write(param.toString());
}
//flush输出流的缓冲
out.flush();
//定义BufferedReader输入流来读取URL的响应
in = new BufferedReader(
new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8));
String line;
while ((line = in.readLine()) != null) {
result.append(line);
}
} catch (Exception e) {
log.error("向指定 URL 发送POST方法的请求错误",e);
}
//使用finally块来关闭输出流输入流
finally {
try {
if (out != null) {
out.close();
}
if (in != null) {
in.close();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
return result.toString();
}
private static final char[] BASE64_ENCODE_CHARS = new char[]{
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
'w', 'x', 'y', 'z', '0', '1', '2', '3',
'4', '5', '6', '7', '8', '9', '+', '/'};
public static String base64Encode(byte[] data) {
StringBuffer sb = new StringBuffer();
int len = data.length;
int i = 0;
int b1, b2, b3;
while (i < len) {
b1 = data[i++] & 0xff;
if (i == len) {
sb.append(BASE64_ENCODE_CHARS[b1 >>> 2]);
sb.append(BASE64_ENCODE_CHARS[(b1 & 0x3) << 4]);
sb.append("==");
break;
}
b2 = data[i++] & 0xff;
if (i == len) {
sb.append(BASE64_ENCODE_CHARS[b1 >>> 2]);
sb.append(BASE64_ENCODE_CHARS[((b1 & 0x03) << 4) | ((b2 & 0xf0) >>> 4)]);
sb.append(BASE64_ENCODE_CHARS[(b2 & 0x0f) << 2]);
sb.append("=");
break;
}
b3 = data[i++] & 0xff;
sb.append(BASE64_ENCODE_CHARS[b1 >>> 2]);
sb.append(BASE64_ENCODE_CHARS[((b1 & 0x03) << 4) | ((b2 & 0xf0) >>> 4)]);
sb.append(BASE64_ENCODE_CHARS[((b2 & 0x0f) << 2) | ((b3 & 0xc0) >>> 6)]);
sb.append(BASE64_ENCODE_CHARS[b3 & 0x3f]);
}
return sb.toString();
}
} }

View File

@ -1,6 +1,7 @@
package cn.lili.controller.setting; package cn.lili.controller.setting;
import cn.hutool.json.JSONUtil; import cn.hutool.json.JSONUtil;
import cn.lili.cache.Cache;
import cn.lili.common.aop.annotation.DemoSite; import cn.lili.common.aop.annotation.DemoSite;
import cn.lili.common.enums.ResultCode; import cn.lili.common.enums.ResultCode;
import cn.lili.common.enums.ResultUtil; import cn.lili.common.enums.ResultUtil;
@ -36,6 +37,11 @@ import java.util.Collections;
public class SettingManagerController { public class SettingManagerController {
@Autowired @Autowired
private SettingService settingService; private SettingService settingService;
/**
* 缓存
*/
@Autowired
private Cache<String> cache;
@DemoSite @DemoSite
@ -79,7 +85,6 @@ public class SettingManagerController {
} }
/** /**
* 对配置进行过滤 * 对配置进行过滤
* *
@ -111,6 +116,7 @@ public class SettingManagerController {
*/ */
private ResultMessage createSetting(String key) { private ResultMessage createSetting(String key) {
SettingEnum settingEnum = SettingEnum.valueOf(key); SettingEnum settingEnum = SettingEnum.valueOf(key);
cache.remove(key);
Setting setting = settingService.get(key); Setting setting = settingService.get(key);
switch (settingEnum) { switch (settingEnum) {
case BASE_SETTING: case BASE_SETTING:
@ -133,10 +139,10 @@ public class SettingManagerController {
return setting == null ? return setting == null ?
ResultUtil.data(new GoodsSetting()) : ResultUtil.data(new GoodsSetting()) :
ResultUtil.data(JSONUtil.toBean(setting.getSettingValue(), GoodsSetting.class)); ResultUtil.data(JSONUtil.toBean(setting.getSettingValue(), GoodsSetting.class));
case KUAIDI_SETTING: case LOGISTICS_SETTING:
return setting == null ? return setting == null ?
ResultUtil.data(new KuaidiSetting()) : ResultUtil.data(new LogisticsSetting()) :
ResultUtil.data(JSONUtil.toBean(setting.getSettingValue(), KuaidiSetting.class)); ResultUtil.data(JSONUtil.toBean(setting.getSettingValue(), LogisticsSetting.class));
case ORDER_SETTING: case ORDER_SETTING:
return setting == null ? return setting == null ?
ResultUtil.data(new OrderSetting()) : ResultUtil.data(new OrderSetting()) :

View File

@ -61,6 +61,10 @@
<spring-boot-admin>2.3.1</spring-boot-admin> <spring-boot-admin>2.3.1</spring-boot-admin>
<owasp-java-html-sanitizer>20211018.2</owasp-java-html-sanitizer> <owasp-java-html-sanitizer>20211018.2</owasp-java-html-sanitizer>
<minio.version>8.0.3</minio.version> <minio.version>8.0.3</minio.version>
<huaweicloud.version>3.21.8</huaweicloud.version>
<cos.version>5.6.97</cos.version>
<tencentcloud.version>3.1.693</tencentcloud.version>
<kuaidi100-api.version>1.0.11</kuaidi100-api.version>
</properties> </properties>
<modules> <modules>

View File

@ -7,7 +7,6 @@ import cn.lili.common.enums.ResultUtil;
import cn.lili.common.security.OperationalJudgment; import cn.lili.common.security.OperationalJudgment;
import cn.lili.common.security.context.UserContext; import cn.lili.common.security.context.UserContext;
import cn.lili.common.vo.ResultMessage; import cn.lili.common.vo.ResultMessage;
import cn.lili.modules.kdBrid.service.KdNiaoService;
import cn.lili.modules.member.entity.dto.MemberAddressDTO; import cn.lili.modules.member.entity.dto.MemberAddressDTO;
import cn.lili.modules.member.service.StoreLogisticsService; import cn.lili.modules.member.service.StoreLogisticsService;
import cn.lili.modules.order.order.entity.dto.OrderExportDTO; import cn.lili.modules.order.order.entity.dto.OrderExportDTO;
@ -16,6 +15,7 @@ import cn.lili.modules.order.order.entity.vo.OrderDetailVO;
import cn.lili.modules.order.order.entity.vo.OrderSimpleVO; import cn.lili.modules.order.order.entity.vo.OrderSimpleVO;
import cn.lili.modules.order.order.service.OrderPriceService; import cn.lili.modules.order.order.service.OrderPriceService;
import cn.lili.modules.order.order.service.OrderService; import cn.lili.modules.order.order.service.OrderService;
import cn.lili.modules.system.service.LogisticsService;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiImplicitParam;
@ -63,10 +63,10 @@ public class OrderStoreController {
private StoreLogisticsService storeLogisticsService; private StoreLogisticsService storeLogisticsService;
/** /**
* 快递鸟电子面单 * 快递
*/ */
@Autowired @Autowired
private KdNiaoService kdNiaoService; private LogisticsService logisticsService;
@ApiOperation(value = "查询订单列表") @ApiOperation(value = "查询订单列表")
@ -200,7 +200,7 @@ public class OrderStoreController {
@ApiImplicitParam(name = "logisticsId", value = "物流公司", required = true, dataType = "String", paramType = "query") @ApiImplicitParam(name = "logisticsId", value = "物流公司", required = true, dataType = "String", paramType = "query")
}) })
public ResultMessage<Object> createElectronicsFaceSheet(@NotNull(message = "参数非法") @PathVariable String orderSn, public ResultMessage<Object> createElectronicsFaceSheet(@NotNull(message = "参数非法") @PathVariable String orderSn,
@NotNull(message = "请选择物流公司") String logisticsId) throws Exception{ @NotNull(message = "请选择物流公司") String logisticsId) throws Exception {
return ResultUtil.data(kdNiaoService.createElectronicsFaceSheet(orderSn,logisticsId)); return ResultUtil.data(logisticsService.labelOrder(orderSn, logisticsId));
} }
} }