From 640d93c464c65a0ef128f7f357a3e9abe44fbd2c Mon Sep 17 00:00:00 2001
From: xuhy <3313886187@qq.com>
Date: 星期二, 21 十月 2025 15:10:12 +0800
Subject: [PATCH] 虚拟号码通话
---
ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/entity/Order.java | 16
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/voice/service/IAXBInterfaceDemo.java | 57 +
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/utils/PrivateNumberUtil.java | 336 ++++++++
ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/factory/OrderFallbackFactory.java | 12
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/voice/util/AXBUtil.java | 60 +
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/voice/service/impl/AXBInterfaceDemoImpl.java | 208 +++++
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/OrderController.java | 36
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/entity/IamConfig.java | 48 +
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/voice/service/impl/AXBInterfaceDemoImpl.java | 208 +++++
ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/feignClient/OrderClient.java | 25
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/voice/util/HttpUtilClient.java | 360 ++++++++
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/utils/UrlToMultipartFileUtil.java | 60 +
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/controller/OrderController.java | 126 +-
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/entity/Order.java | 15
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/voice/service/IAXBInterfaceDemo.java | 57 +
ruoyi-service/ruoyi-admin/pom.xml | 5
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/controller/OrderController.java | 38
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/voice/util/AXBUtil.java | 61 +
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/voice/util/HttpUtil.java | 360 ++++++++
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/voice/util/StringUtil.java | 53 +
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/entity/Order.java | 16
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/mapper/IamConfigMapper.java | 18
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/service/IamConfigService.java | 16
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/entity/IamConfig.java | 48 +
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/service/IamConfigService.java | 16
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/service/impl/IamConfigServiceImpl.java | 20
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/service/impl/IamConfigServiceImpl.java | 20
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/controller/PrivateNumberCallBackController.java | 61 +
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/voice/util/StringUtil.java | 53 +
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/mapper/IamConfigMapper.java | 18
30 files changed, 2,351 insertions(+), 76 deletions(-)
diff --git a/ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/entity/Order.java b/ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/entity/Order.java
index 785b0b5..a50b94b 100644
--- a/ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/entity/Order.java
+++ b/ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/entity/Order.java
@@ -199,4 +199,20 @@
private String paperPic;
@ApiModelProperty("拉起确认收款页面参数")
private String packageInfo;
+
+ @ApiModelProperty("绑定关系id")
+ @TableField("subscription_id")
+ private String subscriptionId;
+ @ApiModelProperty("虚拟号码")
+ @TableField("virtual_number")
+ private String virtualNumber;
+ @ApiModelProperty("电话录音")
+ @TableField("phone_recording")
+ private String phoneRecording;
+ @ApiModelProperty("录音文件存储的服务器域名")
+ @TableField("record_domain")
+ private String recordDomain;
+ @ApiModelProperty("录音文件名")
+ @TableField("record_object_name")
+ private String recordObjectName;
}
diff --git a/ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/factory/OrderFallbackFactory.java b/ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/factory/OrderFallbackFactory.java
index 56b9e4d..0560e45 100644
--- a/ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/factory/OrderFallbackFactory.java
+++ b/ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/factory/OrderFallbackFactory.java
@@ -187,7 +187,7 @@
}
@Override
- public R<Boolean> orderSubmit(OrderSubmitRequest orderSubmitRequest) {
+ public R<String> orderSubmit(OrderSubmitRequest orderSubmitRequest) {
return R.fail(cause.getMessage());
}
@@ -273,6 +273,16 @@
}
@Override
+ public R<String> updateSubscriptionId(String orderId, String subscriptionId, String virtualNumber) {
+ return R.fail(cause.getMessage());
+ }
+
+ @Override
+ public R<String> updatePhoneRecording(String orderId, String audioUrl) {
+ return R.fail(cause.getMessage());
+ }
+
+ @Override
public R<Boolean> changeOrderState(String orderId, Integer state) {
return R.fail(cause.getMessage());
}
diff --git a/ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/feignClient/OrderClient.java b/ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/feignClient/OrderClient.java
index e639dba..0ef6ae6 100644
--- a/ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/feignClient/OrderClient.java
+++ b/ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/feignClient/OrderClient.java
@@ -227,8 +227,6 @@
/**
* 根据类型获取交易额
*
- * @param cityList 城市列表
- * @param type 查询类型
* @return 交易额
*/
@GetMapping(value = "/order/tradeMoney")
@@ -296,7 +294,7 @@
* @return 提交结果
*/
@PostMapping(value = "/order/orderSubmit")
- R<Boolean> orderSubmit(@RequestBody OrderSubmitRequest orderSubmitRequest);
+ R<String> orderSubmit(@RequestBody OrderSubmitRequest orderSubmitRequest);
/**
* 师傅端-定时调度记录师傅所走路线经纬度
@@ -460,4 +458,25 @@
*/
@GetMapping(value = "/order/getTodayOrderData")
R<List<Order>> getTodayOrderData();
+
+ /**
+ * 设置订单的绑定id
+ * @param orderId
+ * @param subscriptionId
+ * @return
+ */
+ @GetMapping(value = "/order/updateSubscriptionId")
+ R<String> updateSubscriptionId(@RequestParam("orderId")String orderId,
+ @RequestParam("subscriptionId")String subscriptionId,
+ @RequestParam("virtualNumber")String virtualNumber);
+ /**
+ * 设置订单的通话录音
+ * @param orderId 订单id
+ * @param audioUrl 音频地址
+ * @return
+ */
+ @GetMapping(value = "/order/updatePhoneRecording")
+ R<String> updatePhoneRecording(@RequestParam("orderId")String orderId,
+ @RequestParam("audioUrl")String audioUrl);
+
}
diff --git a/ruoyi-service/ruoyi-admin/pom.xml b/ruoyi-service/ruoyi-admin/pom.xml
index f2eb0bd..488dec6 100644
--- a/ruoyi-service/ruoyi-admin/pom.xml
+++ b/ruoyi-service/ruoyi-admin/pom.xml
@@ -239,6 +239,11 @@
<version>0.2.12</version>
<scope>compile</scope>
</dependency>
+ <!-- 转MultipartFile才需要导这个maven包 -->
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-test</artifactId>
+ </dependency>
</dependencies>
<build>
diff --git a/ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/controller/OrderController.java b/ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/controller/OrderController.java
index 24c280a..2d5a75d 100644
--- a/ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/controller/OrderController.java
+++ b/ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/controller/OrderController.java
@@ -14,13 +14,16 @@
import com.ruoyi.admin.service.*;
import com.ruoyi.admin.utils.DescribeInstances;
import com.ruoyi.admin.utils.HttpUtil;
+import com.ruoyi.admin.utils.PrivateNumberUtil;
import com.ruoyi.admin.vo.OrderByServeRecordVO;
import com.ruoyi.admin.vo.OrderDetailVO;
import com.ruoyi.admin.vo.OrderReasinDto;
import com.ruoyi.admin.vo.ReassinDto;
+import com.ruoyi.admin.voice.util.AXBUtil;
import com.ruoyi.common.core.constant.Constants;
import com.ruoyi.common.core.domain.BaseEntity;
import com.ruoyi.common.core.domain.R;
+import com.ruoyi.common.core.exception.GlobalException;
import com.ruoyi.common.core.exception.ServiceException;
import com.ruoyi.common.core.utils.DateUtils;
import com.ruoyi.common.core.utils.GaoDeMapUtil;
@@ -61,6 +64,7 @@
import javax.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
@@ -100,7 +104,15 @@
private ExchangeDispatchClient dispatchClient;
@Resource
private TokenService tokenService;
+ @Resource
+ private RecoveryClassifyService recoveryClassifyService;
+ @Resource
+ private FranchiseeService franchiseeService;
+ @Resource
+ private PrivateNumberUtil privateNumberUtil;
+ @Autowired
+ private IamConfigService iamConfigService;
/**
* 雪花算法类
*/
@@ -144,11 +156,6 @@
}
return R.ok(orderDetailVO);
}
- @Resource
- private RecoveryClassifyService recoveryClassifyService;
-
- @Resource
- private FranchiseeService franchiseeService;
/**
* 订单列表
*
@@ -429,70 +436,65 @@
@RequiresPermissions("order_reassignment")
@ApiOperation(value = "订单列表-订单派单/改派", tags = {"后台-订单管理"})
@PostMapping(value = "/reassignment")
-
@Transactional(rollbackFor = Exception.class)
public R<String> reassignment(@RequestBody OrderReasinDto orderReasinDto) {
-// String[] split = orderIds.split(",");
for (ReassinDto orderId : orderReasinDto.getReassinDtos()) {
-
- MasterWorker masterWorker = masterWorkerService.lambdaQuery()
- .eq(MasterWorker::getId, orderReasinDto.getWorkerId())
- .eq(MasterWorker::getIsDelete, 0).one();
- Order item = orderClient.detail(orderId.getOrderId()).getData();
- Order order = orderClient.exchangeOrder(orderId.getType(), orderId.getOrderId(), orderReasinDto.getWorkerId(),
- masterWorker.getRealName(), masterWorker.getPhone()).getData();
- // 订单派单
- boolean result = true;
- if (Constants.TWO.equals(orderId.getType())) {
- orderClient.updateArrivalTime(order.getId(),orderReasinDto.getArriveTime());
-
- if (order.getState().equals(Constants.SIX) || order.getState().equals(Constants.THREE)) {
- orderClient.updateState(order.getId(), 7);
-
- //如果是待改派,将上门时间设置为最新的,并且更新再投原因
- if (order.getState().equals(Constants.SIX)){
- dispatchClient.changeReason(order.getId(), "");
+ MasterWorker masterWorker = masterWorkerService.lambdaQuery()
+ .eq(MasterWorker::getId, orderReasinDto.getWorkerId())
+ .eq(MasterWorker::getIsDelete, 0).one();
+ Order order = orderClient.exchangeOrder(orderId.getType(), orderId.getOrderId(), orderReasinDto.getWorkerId(),
+ masterWorker.getRealName(), masterWorker.getPhone()).getData();
+ try{
+ // 虚拟号码 师傅号码备案
+ String fileName = privateNumberUtil.uploadNumberFile(masterWorker.getProfilePicture()).getData();
+ R r = privateNumberUtil.addANumber(masterWorker.getPhone(), masterWorker.getRealName(), masterWorker.getIdNumber(), fileName);
+ }catch (Exception e){
+ throw new GlobalException("虚拟号码绑定异常!");
+ }
+ // 虚拟号码配置
+ IamConfig iamConfig = iamConfigService.getById(1);
+ // 订单派单
+ if (Constants.TWO.equals(orderId.getType())) {
+ orderClient.updateArrivalTime(order.getId(),orderReasinDto.getArriveTime());
+ if (order.getState().equals(Constants.SIX) || order.getState().equals(Constants.THREE)) {
+ orderClient.updateState(order.getId(), 7);
+ //如果是待改派,将上门时间设置为最新的,并且更新再投原因
+ if (order.getState().equals(Constants.SIX)){
+ dispatchClient.changeReason(order.getId(), "");
+ }
+ }
+ // 订单状态为 待完工时,需要更改状态 待上门且清空师傅到达预约点时间
+ if (order.getState().equals(Constants.TWO)) {
+ orderClient.updateStateAndArrivalTime(orderId.getOrderId(), Constants.ONE);
+ }
+ if(StringUtils.hasLength(order.getSubscriptionId())){
+ // 虚拟号码更换
+ AXBUtil.axbModifyNumber(iamConfig.getAppKey(),iamConfig.getAppSecret(),order.getSubscriptionId(), order.getReservationPhone(), masterWorker.getPhone());
+ }else {
+ // 虚拟号码绑定
+ String result = AXBUtil.axbBindNumber(iamConfig.getAppKey(), iamConfig.getAppSecret(), order.getOrderNumber(), order.getReservationPhone(), masterWorker.getPhone());
+ // 存储绑定后的唯一标识
+ JSONObject jsonObject = JSONObject.parseObject(result);
+ String subscriptionId = jsonObject.getString("subscriptionId");
+ orderClient.updateSubscriptionId(order.getId(), subscriptionId,iamConfig.getVirtualNumber());
}
+ }else {
+ orderClient.updateArrivalTime(order.getId(),orderReasinDto.getArriveTime());
+ orderClient.updateState(order.getId(), 7);
+ // 虚拟号码绑定
+ String result = AXBUtil.axbBindNumber(iamConfig.getAppKey(), iamConfig.getAppSecret(), order.getOrderNumber(), order.getReservationPhone(), masterWorker.getPhone());
+ // 存储绑定后的唯一标识
+ JSONObject jsonObject = JSONObject.parseObject(result);
+ String subscriptionId = jsonObject.getString("subscriptionId");
+ orderClient.updateSubscriptionId(order.getId(), subscriptionId,iamConfig.getVirtualNumber());
+
}
- // 订单状态为 待完工时,需要更改状态 待上门且清空师傅到达预约点时间
- if (order.getState().equals(Constants.TWO)) {
- orderClient.updateStateAndArrivalTime(orderId.getOrderId(), Constants.ONE);
+ ChannelHandlerContext context = NettyChannelMap.getData(String.valueOf(orderReasinDto.getWorkerId()));
+ if (null != context) {
+ NettyWebSocketController.sendMsgToClient(context, "您有一条新的订单,请注意查收!");
}
-
-
-
-
- // 生成改派信息
-// ChangeDispatch changeDispatch = new ChangeDispatch();
-// changeDispatch.setWorkerId(item.getServerId());
-// changeDispatch.setWorkerName(item.getServerName());
-// changeDispatch.setApplyReason(orderReasinDto.getApplyReason());
-// changeDispatch.setApplyTime(new Date());
-// changeDispatch.setState(Constants.ONE);
-// changeDispatch.setOrderId(orderId.getOrderId());
-// changeDispatch.setOrderNumber(item.getOrderNumber());
-// if (null != item.getUserId()) {
-// changeDispatch.setUserId(item.getUserId());
-// }
-// changeDispatch.setUserName(item.getReservationName());
-// changeDispatch.setIsDelete(Constants.ZERO);
-// result = dispatchClient.saveRecord(changeDispatch).getData();
- }else {
- orderClient.updateArrivalTime(order.getId(),orderReasinDto.getArriveTime());
- orderClient.updateState(order.getId(), 7);
}
- ChannelHandlerContext context = NettyChannelMap.getData(String.valueOf(orderReasinDto.getWorkerId()));
- if (null != context) {
- NettyWebSocketController.sendMsgToClient(context, "您有一条新的订单,请注意查收!");
- }
-// try {
-// WebSocketServer.sendInfo("您有一条新的订单,请注意查收!", String.valueOf(workerId));
-// } catch (IOException e) {
-// return R.fail("订单推送失败!");
-// }
- }
-
return R.ok() ;
}
@@ -623,8 +625,6 @@
/**
* 订单列表-excel导出
*
- * @param name 师傅姓名
- * @param phone 师傅电话
*/
@RequiresPermissions("order_count")
@ApiOperation(value = "订单统计", tags = {"后台-订单管理"})
diff --git a/ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/controller/PrivateNumberCallBackController.java b/ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/controller/PrivateNumberCallBackController.java
new file mode 100644
index 0000000..10fff40
--- /dev/null
+++ b/ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/controller/PrivateNumberCallBackController.java
@@ -0,0 +1,61 @@
+package com.ruoyi.admin.controller;
+
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.ruoyi.admin.entity.Order;
+import com.ruoyi.admin.service.OrderService;
+import com.ruoyi.common.core.domain.R;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+import java.util.Objects;
+
+/**
+ * <p>
+ * 协议政策、司机操作指导 前端控制器
+ * </p>
+ *
+ * @author hjl
+ * @since 2024-05-29
+ */
+@Slf4j
+@RestController
+@RequestMapping("/privateNumber")
+public class PrivateNumberCallBackController {
+
+
+ @Resource
+ private OrderService orderService;
+
+ /**
+ * 隐私号码回调
+ */
+ @PostMapping(value = "/callBack")
+ public R<String> callBack(@RequestBody JSONObject jsonObject) {
+ JSONArray feeLst = jsonObject.getJSONArray("feeLst");
+ for (int i = 0; i < feeLst.size(); i++) {
+ JSONObject feeLstJson = feeLst.getJSONObject(i);
+ String subscriptionId = feeLstJson.getString("subscriptionId");
+ String recordDomain = feeLstJson.getString("recordDomain");
+ String recordObjectName = feeLstJson.getString("recordObjectName");
+ String recordBucketName = feeLstJson.getString("recordBucketName");
+ log.info("隐私号码回调:{},录音文件名:{},录音服务器名:{}", subscriptionId, recordObjectName, recordDomain);
+ Order order = orderService.getOne(Wrappers.lambdaQuery(Order.class)
+ .eq(Order::getSubscriptionId, subscriptionId)
+ .last("LIMIT 1"));
+ if(Objects.nonNull(order)){
+ order.setRecordDomain(recordDomain);
+ order.setRecordObjectName(recordObjectName);
+ orderService.updateById(order);
+ }
+ }
+ return R.ok("OK");
+ }
+
+}
diff --git a/ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/entity/IamConfig.java b/ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/entity/IamConfig.java
new file mode 100644
index 0000000..0a62306
--- /dev/null
+++ b/ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/entity/IamConfig.java
@@ -0,0 +1,48 @@
+package com.ruoyi.admin.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.io.Serializable;
+
+/**
+ * <p>
+ * 虚拟号码配置
+ * </p>
+ *
+ * @author hjl
+ * @since 2024-07-08
+ */
+@Getter
+@Setter
+@TableName("t_iam_config")
+@ApiModel(value = "t_iam_config对象", description = "虚拟号码配置")
+public class IamConfig implements Serializable {
+
+ @TableId(value = "id", type = IdType.AUTO)
+ private Integer id;
+ @ApiModelProperty("用户")
+ @TableField("userName")
+ private String userName;
+ @ApiModelProperty("子账号")
+ @TableField("account")
+ private String account;
+ @ApiModelProperty("子账号密码")
+ @TableField("password")
+ private String password;
+ @ApiModelProperty("虚拟号码APP_Key")
+ @TableField("appKey")
+ private String appKey;
+ @ApiModelProperty("虚拟号码APP_Secret")
+ @TableField("appSecret")
+ private String appSecret;
+ @ApiModelProperty("虚拟号码")
+ @TableField("virtualNumber")
+ private String virtualNumber;
+}
diff --git a/ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/entity/Order.java b/ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/entity/Order.java
index 4f82687..729eaa2 100644
--- a/ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/entity/Order.java
+++ b/ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/entity/Order.java
@@ -148,4 +148,20 @@
@TableField(exist = false)
private String applyReason;
+ @ApiModelProperty("绑定关系id")
+ @TableField("subscription_id")
+ private String subscriptionId;
+ @ApiModelProperty("虚拟号码")
+ @TableField("virtual_number")
+ private String virtualNumber;
+ @ApiModelProperty("电话录音")
+ @TableField("phone_recording")
+ private String phoneRecording;
+ @ApiModelProperty("录音文件存储的服务器域名")
+ @TableField("record_domain")
+ private String recordDomain;
+ @ApiModelProperty("录音文件名")
+ @TableField("record_object_name")
+ private String recordObjectName;
+
}
diff --git a/ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/mapper/IamConfigMapper.java b/ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/mapper/IamConfigMapper.java
new file mode 100644
index 0000000..6c36b82
--- /dev/null
+++ b/ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/mapper/IamConfigMapper.java
@@ -0,0 +1,18 @@
+package com.ruoyi.admin.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.admin.entity.IamConfig;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * <p>
+ * 虚拟号码管理 Mapper 接口
+ * </p>
+ *
+ * @author hjl
+ * @since 2024-07-08
+ */
+@Mapper
+public interface IamConfigMapper extends BaseMapper<IamConfig> {
+
+}
diff --git a/ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/service/IamConfigService.java b/ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/service/IamConfigService.java
new file mode 100644
index 0000000..0866cb0
--- /dev/null
+++ b/ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/service/IamConfigService.java
@@ -0,0 +1,16 @@
+package com.ruoyi.admin.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.admin.entity.IamConfig;
+
+/**
+ * <p>
+ * 改派管理 服务类
+ * </p>
+ *
+ * @author hjl
+ * @since 2024-07-08
+ */
+public interface IamConfigService extends IService<IamConfig> {
+
+}
diff --git a/ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/service/impl/IamConfigServiceImpl.java b/ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/service/impl/IamConfigServiceImpl.java
new file mode 100644
index 0000000..75e4561
--- /dev/null
+++ b/ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/service/impl/IamConfigServiceImpl.java
@@ -0,0 +1,20 @@
+package com.ruoyi.admin.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.admin.entity.IamConfig;
+import com.ruoyi.admin.mapper.IamConfigMapper;
+import com.ruoyi.admin.service.IamConfigService;
+import org.springframework.stereotype.Service;
+
+/**
+ * <p>
+ * 改派管理 服务实现类
+ * </p>
+ *
+ * @author hjl
+ * @since 2024-07-08
+ */
+@Service
+public class IamConfigServiceImpl extends ServiceImpl<IamConfigMapper, IamConfig> implements IamConfigService {
+
+}
diff --git a/ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/utils/PrivateNumberUtil.java b/ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/utils/PrivateNumberUtil.java
new file mode 100644
index 0000000..1707b65
--- /dev/null
+++ b/ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/utils/PrivateNumberUtil.java
@@ -0,0 +1,336 @@
+package com.ruoyi.admin.utils;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.ruoyi.admin.entity.IamConfig;
+import com.ruoyi.admin.service.IamConfigService;
+import com.ruoyi.common.core.domain.R;
+import com.ruoyi.common.core.exception.ServiceException;
+import com.ruoyi.common.redis.service.RedisService;
+import lombok.extern.slf4j.Slf4j;
+import okhttp3.*;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.IOException;
+import java.util.Date;
+import java.util.concurrent.TimeUnit;
+
+@Slf4j
+@Component
+public class PrivateNumberUtil {
+
+ @Autowired
+ private RedisService redisService;
+ @Autowired
+ private IamConfigService iamConfigService;
+
+ private final String TOKEN_URL = "https://iam.cn-south-1.myhuaweicloud.com/v3/auth/tokens";
+ private final String UPLOAD_NUMBER_FILE_URL = "https://rtc.cn-north-1.myhuaweicloud.com/v1/privatenumber/a-number/files?agree_authorization_statement=true";
+ private final String ADD_A_NUMBER_URL = "https://rtc.cn-north-1.myhuaweicloud.com/v1.0/privatenumber/a-number?agree_authorization_statement=true";
+
+ /**
+ * 获取token
+ * @return
+ */
+ public String getToken() {
+ String token = redisService.getCacheObject("PRIVATE_NUMBER:");
+ if(StringUtils.hasLength(token)){
+ return token;
+ }
+ IamConfig iamConfig = iamConfigService.getById(1);
+ String userName = iamConfig.getUserName();
+ String account = iamConfig.getAccount();
+ String password = iamConfig.getPassword();
+ OkHttpClient client = new OkHttpClient().newBuilder()
+ .build();
+ MediaType mediaType = MediaType.parse("text/plain");
+ RequestBody body = RequestBody.create(mediaType, "{\"auth\":{\"identity\":{\"methods\":[\"password\"],\"password\":{\"user\":{\"domain\":{\"name\":\""+userName+"\"},\"name\":\""+account+"\",\"password\":\""+password+"\"}}},\"scope\":{\"domain\":{},\"project\":{\"name\":\"cn-north-1\"}}}}");
+ Request request = new Request.Builder()
+ .url(TOKEN_URL)
+ .method("POST", body)
+ .build();
+ Response response = null;
+ try {
+ response = client.newCall(request).execute();
+ } catch (IOException e) {
+ throw new ServiceException(e.getMessage());
+ }
+ // 获取响应头 X-Subject-Token
+ token = response.header("X-Subject-Token");
+ if(StringUtils.hasLength(token)){
+ ResponseBody responseBody = response.body();
+ if(responseBody != null) {
+ try {
+ String string = responseBody.string();
+ log.info("获取token:{}",string);
+ JSONObject jsonObject = JSONObject.parseObject(string);
+ JSONObject tokenJson = jsonObject.getJSONObject("token");
+ Date expiresAt = tokenJson.getDate("expires_at");
+ redisService.setCacheObject("PRIVATE_NUMBER:",token,expiresAt.getTime()-System.currentTimeMillis(), TimeUnit.MILLISECONDS);
+ } catch (IOException e) {
+ throw new ServiceException(e.getMessage());
+ }
+ }
+ return token;
+ }else {
+ return null;
+ }
+ }
+ /**
+ * 获取token
+ * @return
+ */
+ public static String getTokenTest() {
+ String account = "hsg13437173440";
+ String userName = "denglu";
+ String password = "6!JR8Fn0";
+ OkHttpClient client = new OkHttpClient().newBuilder()
+ .build();
+ MediaType mediaType = MediaType.parse("text/plain");
+ RequestBody body = RequestBody.create(mediaType, "{\"auth\":{\"identity\":{\"methods\":[\"password\"],\"password\":{\"user\":{\"domain\":{\"name\":\""+account+"\"},\"name\":\""+userName+"\",\"password\":\""+password+"\"}}},\"scope\":{\"domain\":{},\"project\":{\"name\":\"cn-north-1\"}}}}");
+ Request request = new Request.Builder()
+ .url("https://iam.cn-south-1.myhuaweicloud.com/v3/auth/tokens")
+ .method("POST", body)
+ .build();
+ Response response = null;
+ try {
+ response = client.newCall(request).execute();
+ } catch (IOException e) {
+ throw new ServiceException(e.getMessage());
+ }
+ // 获取响应头 X-Subject-Token
+ String token = response.header("X-Subject-Token");
+ if(StringUtils.hasLength(token)){
+ return token;
+ }else {
+ return null;
+ }
+ }
+
+
+ /**
+ * 上传号码文件
+ * @param filePath
+ */
+ public R<String> uploadNumberFile(String filePath) {
+ MultipartFile file = UrlToMultipartFileUtil.urlTransferMultipartFile(filePath);
+ MultipartFile[] files = new MultipartFile[]{file};
+ OkHttpClient client = new OkHttpClient().newBuilder()
+ .build();
+ // 构建multipart/form-data请求体
+ MultipartBody.Builder builder = new MultipartBody.Builder();
+ builder.setType(MultipartBody.FORM);
+ // 添加文件到请求体
+ try {
+ for (MultipartFile multipartFile : files) {
+ if (multipartFile != null && !multipartFile.isEmpty()) {
+ RequestBody fileBody = RequestBody.create(
+ MediaType.parse(multipartFile.getContentType()),
+ multipartFile.getBytes()
+ );
+ builder.addFormDataPart("files", multipartFile.getOriginalFilename(), fileBody);
+ }
+ }
+ } catch (IOException e) {
+ System.err.println("Error reading multipart file: " + e.getMessage());
+ e.printStackTrace();
+ return R.fail("文件读取失败: " + e.getMessage());
+ }
+ RequestBody body = builder.build();
+ Request request = new Request.Builder()
+ .url(UPLOAD_NUMBER_FILE_URL)
+ .method("POST", body)
+ .addHeader("X-Auth-Token", getToken())
+ .build();
+ Response response = null;
+ try {
+ response = client.newCall(request).execute();
+ ResponseBody responseBody = response.body();
+ if(responseBody != null) {
+ try {
+ String string = responseBody.string();
+ log.info("上传号码文件:{}",string);
+ JSONObject jsonObject = JSONObject.parseObject(string);
+ String result = jsonObject.getString("result");
+ if("Upload Successful.".equals(result)){
+ return R.ok(file.getOriginalFilename(),"");
+ }else {
+ String failReason = jsonObject.getString("failReason");
+ return R.fail(failReason);
+ }
+ } catch (IOException e) {
+ throw new ServiceException(e.getMessage());
+ }
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ return R.fail();
+ }
+ public static R uploadNumberFileTest(String filePath,String accessToken) {
+ MultipartFile file = UrlToMultipartFileUtil.urlTransferMultipartFile(filePath);
+ MultipartFile[] files = new MultipartFile[]{file};
+ OkHttpClient client = new OkHttpClient().newBuilder()
+ .build();
+ // 构建multipart/form-data请求体
+ MultipartBody.Builder builder = new MultipartBody.Builder();
+ builder.setType(MultipartBody.FORM);
+ // 添加文件到请求体
+ try {
+ for (MultipartFile multipartFile : files) {
+ if (multipartFile != null && !multipartFile.isEmpty()) {
+ RequestBody fileBody = RequestBody.create(
+ MediaType.parse(multipartFile.getContentType()),
+ multipartFile.getBytes()
+ );
+ builder.addFormDataPart("files", multipartFile.getOriginalFilename(), fileBody);
+ }
+ }
+ } catch (IOException e) {
+ System.err.println("Error reading multipart file: " + e.getMessage());
+ e.printStackTrace();
+ return R.fail("文件读取失败: " + e.getMessage());
+ }
+ RequestBody body = builder.build();
+ Request request = new Request.Builder()
+ .url("https://rtc.cn-north-1.myhuaweicloud.com/v1/privatenumber/a-number/files?agree_authorization_statement=true")
+ .method("POST", body)
+ .addHeader("X-Auth-Token", getTokenTest())
+ .build();
+ Response response = null;
+ try {
+ response = client.newCall(request).execute();
+ ResponseBody responseBody = response.body();
+ if(responseBody != null) {
+ try {
+ String string = responseBody.string();
+ System.err.println("body:::::::::::::::::"+string);
+ JSONObject jsonObject = JSONObject.parseObject(string);
+ String result = jsonObject.getString("result");
+ if("Upload Successful.".equals(result)){
+ return R.ok();
+ }else {
+ String failReason = jsonObject.getString("failReason");
+ return R.fail(failReason);
+ }
+ } catch (IOException e) {
+ throw new ServiceException(e.getMessage());
+ }
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ return R.fail();
+ }
+
+ /**
+ * 新增租户级 A 号码报备接口
+ * @param fileName
+ */
+ public R addANumber(String number, String realName, String idNumber, String fileName) {
+ JSONObject requestBody = new JSONObject();
+ JSONArray numbers = new JSONArray();
+ JSONObject jsonObject = new JSONObject();
+ jsonObject.put("number", "+86"+number); // 手机号码
+ jsonObject.put("numberType", 1); // 号码类型 1=手机号 2=固话
+ jsonObject.put("name", realName); // 真实姓名
+ jsonObject.put("identityCard",idNumber); // 身份证号
+ jsonObject.put("liveFaceImage", fileName); // 号码所属人现场照片文件名
+ numbers.add(jsonObject);
+ requestBody.put("numbers", numbers);
+ OkHttpClient client = new OkHttpClient().newBuilder()
+ .build();
+ // 构建json请求体
+ MediaType mediaType = MediaType.parse("application/json");
+ String requestBodyJson = requestBody.toJSONString();
+ RequestBody body = RequestBody.create(mediaType, requestBodyJson);
+ Request request = new Request.Builder()
+ .url(ADD_A_NUMBER_URL)
+ .method("POST", body)
+ .addHeader("X-Auth-Token", getToken())
+ .addHeader("Content-Type", "application/json")
+ .addHeader("Accept", "application/json")
+ .build();
+ Response response = null;
+ try {
+ response = client.newCall(request).execute();
+ ResponseBody responseBody = response.body();
+ if(responseBody != null) {
+ String string = responseBody.string();
+ log.info("新增租户级 A 号码报备接口:{}",string);
+ // 解析响应JSON
+ JSONObject responseJson = JSONObject.parseObject(string);
+ String result = responseJson.getString("result");
+ if("Add Successful.".equals(result)){
+ return R.ok();
+ }else {
+ String errorMsg = responseJson.getString("error_msg");
+ return R.fail(errorMsg);
+ }
+ }
+ } catch (IOException e) {
+ throw new ServiceException(e.getMessage());
+ }
+ return R.fail();
+ }
+ public static R addANumberTest(String number, String realName, String idNumber, String fileName) {
+ JSONObject requestBody = new JSONObject();
+ JSONArray numbers = new JSONArray();
+ JSONObject jsonObject = new JSONObject();
+ jsonObject.put("number", "+86"+number); // 手机号码
+ jsonObject.put("numberType", 1); // 号码类型 1=手机号 2=固话
+ jsonObject.put("name", realName); // 真实姓名
+ jsonObject.put("identityCard",idNumber); // 身份证号
+ jsonObject.put("liveFaceImage", fileName); // 号码所属人现场照片文件名
+ numbers.add(jsonObject);
+ requestBody.put("numbers", numbers);
+ OkHttpClient client = new OkHttpClient().newBuilder()
+ .build();
+ // 构建json请求体
+ MediaType mediaType = MediaType.parse("application/json");
+ String requestBodyJson = requestBody.toJSONString();
+ RequestBody body = RequestBody.create(mediaType, requestBodyJson);
+ Request request = new Request.Builder()
+ .url("https://rtc.cn-north-1.myhuaweicloud.com/v1.0/privatenumber/a-number?agree_authorization_statement=true")
+ .method("POST", body)
+ .addHeader("X-Auth-Token", getTokenTest())
+ .addHeader("Content-Type", "application/json")
+ .addHeader("Accept", "application/json")
+ .build();
+ Response response = null;
+ try {
+ response = client.newCall(request).execute();
+ ResponseBody responseBody = response.body();
+ if(responseBody != null) {
+ String string = responseBody.string();
+ log.info("新增租户级 A 号码报备接口:{}",string);
+ System.err.println("body:::::::::::::::::+++++++++++"+string);
+ // 解析响应JSON
+ JSONObject responseJson = JSONObject.parseObject(string);
+ String result = responseJson.getString("result");
+ if("Add Successful.".equals(result)){
+ return R.ok();
+ }else {
+ String errorMsg = responseJson.getString("error_msg");
+ return R.fail(errorMsg);
+ }
+ }
+ } catch (IOException e) {
+ throw new ServiceException(e.getMessage());
+ }
+ return R.fail();
+ }
+
+ public static void main(String[] args) {
+// String token = "MIINawYJKoZIhvcNAQcCoIINXDCCDVgCAQExDTALBglghkgBZQMEAgEwggt9BgkqhkiG9w0BBwGgggtuBIILansidG9rZW4iOnsiZXhwaXJlc19hdCI6IjIwMjUtMTAtMjFUMDI6Mzc6NTAuODE0MDAwWiIsIm1ldGhvZHMiOlsicGFzc3dvcmQiXSwiY2F0YWxvZyI6W10sInJvbGVzIjpbeyJuYW1lIjoib3BfZ2F0ZWRfY3Nic19yZXBfYWNjZWxlcmF0aW9uIiwiaWQiOiIwIn0seyJuYW1lIjoib3BfZ2F0ZWRfZWNzX2Rpc2tBY2MiLCJpZCI6IjAifSx7Im5hbWUiOiJvcF9nYXRlZF9kc3NfbW9udGgiLCJpZCI6IjAifSx7Im5hbWUiOiJvcF9nYXRlZF9vYnNfZGVlcF9hcmNoaXZlIiwiaWQiOiIwIn0seyJuYW1lIjoib3BfZ2F0ZWRfYV9jbi1zb3V0aC00YyIsImlkIjoiMCJ9LHsibmFtZSI6Im9wX2dhdGVkX2RlY19tb250aF91c2VyIiwiaWQiOiIwIn0seyJuYW1lIjoib3BfZ2F0ZWRfY2JyX3NlbGxvdXQiLCJpZCI6IjAifSx7Im5hbWUiOiJvcF9nYXRlZF9lY3Nfb2xkX3Jlb3VyY2UiLCJpZCI6IjAifSx7Im5hbWUiOiJvcF9nYXRlZF9ldnNfUm95YWx0eSIsImlkIjoiMCJ9LHsibmFtZSI6Im9wX2dhdGVkX3dlbGlua2JyaWRnZV9lbmRwb2ludF9idXkiLCJpZCI6IjAifSx7Im5hbWUiOiJvcF9nYXRlZF9jYnJfZmlsZSIsImlkIjoiMCJ9LHsibmFtZSI6Im9wX2dhdGVkX2Rtcy1yb2NrZXRtcTUtYmFzaWMiLCJpZCI6IjAifSx7Im5hbWUiOiJvcF9nYXRlZF9ldnNfRVNpbmdsZV9jb3B5U1NEIiwiaWQiOiIwIn0seyJuYW1lIjoib3BfZ2F0ZWRfZG1zLWthZmthMyIsImlkIjoiMCJ9LHsibmFtZSI6Im9wX2dhdGVkX29ic19kZWNfbW9udGgiLCJpZCI6IjAifSx7Im5hbWUiOiJvcF9nYXRlZF9jc2JzX3Jlc3RvcmUiLCJpZCI6IjAifSx7Im5hbWUiOiJvcF9nYXRlZF9jYnJfdm13YXJlIiwiaWQiOiIwIn0seyJuYW1lIjoib3BfZ2F0ZWRfaWRtZV9tYm1fZm91bmRhdGlvbiIsImlkIjoiMCJ9LHsibmFtZSI6Im9wX2dhdGVkX211bHRpX2JpbmQiLCJpZCI6IjAifSx7Im5hbWUiOiJvcF9nYXRlZF9zbW5fY2FsbG5vdGlmeSIsImlkIjoiMCJ9LHsibmFtZSI6Im9wX2dhdGVkX2FfYXAtc291dGhlYXN0LTNkIiwiaWQiOiIwIn0seyJuYW1lIjoib3BfZ2F0ZWRfY3Nic19wcm9ncmVzc2JhciIsImlkIjoiMCJ9LHsibmFtZSI6Im9wX2dhdGVkX2Nlc19yZXNvdXJjZWdyb3VwX3RhZyIsImlkIjoiMCJ9LHsibmFtZSI6Im9wX2dhdGVkX2Vjc19vZmZsaW5lX2FjNyIsImlkIjoiMCJ9LHsibmFtZSI6Im9wX2dhdGVkX2V2c19yZXR5cGUiLCJpZCI6IjAifSx7Im5hbWUiOiJvcF9nYXRlZF9rb29tYXAiLCJpZCI6IjAifSx7Im5hbWUiOiJvcF9nYXRlZF9ldnNfZXNzZDIiLCJpZCI6IjAifSx7Im5hbWUiOiJvcF9nYXRlZF9kbXMtYW1xcC1iYXNpYyIsImlkIjoiMCJ9LHsibmFtZSI6Im9wX2dhdGVkX2V2c19wb29sX2NhIiwiaWQiOiIwIn0seyJuYW1lIjoib3BfZ2F0ZWRfYV9jbi1zb3V0aHdlc3QtMmIiLCJpZCI6IjAifSx7Im5hbWUiOiJvcF9nYXRlZF9od2NwaCIsImlkIjoiMCJ9LHsibmFtZSI6Im9wX2dhdGVkX2Vjc19vZmZsaW5lX2Rpc2tfNCIsImlkIjoiMCJ9LHsibmFtZSI6Im9wX2dhdGVkX3Ntbl93ZWxpbmtyZWQiLCJpZCI6IjAifSx7Im5hbWUiOiJvcF9nYXRlZF9odl92ZW5kb3IiLCJpZCI6IjAifSx7Im5hbWUiOiJvcF9nYXRlZF9hX2NuLW5vcnRoLTRlIiwiaWQiOiIwIn0seyJuYW1lIjoib3BfZ2F0ZWRfYV9jbi1ub3J0aC00ZCIsImlkIjoiMCJ9LHsibmFtZSI6Im9wX2dhdGVkX2Vjc19oZWNzX3giLCJpZCI6IjAifSx7Im5hbWUiOiJvcF9nYXRlZF9jYnJfZmlsZXNfYmFja3VwIiwiaWQiOiIwIn0seyJuYW1lIjoib3BfZ2F0ZWRfZWNzX2FjNyIsImlkIjoiMCJ9LHsibmFtZSI6Im9wX2dhdGVkX2VwcyIsImlkIjoiMCJ9LHsibmFtZSI6Im9wX2dhdGVkX2NzYnNfcmVzdG9yZV9hbGwiLCJpZCI6IjAifSx7Im5hbWUiOiJvcF9nYXRlZF9hX2NuLW5vcnRoLTRmIiwiaWQiOiIwIn0seyJuYW1lIjoib3BfZ2F0ZWRfb3BfZ2F0ZWRfcm91bmR0YWJsZSIsImlkIjoiMCJ9LHsibmFtZSI6Im9wX2dhdGVkX2V2c19leHQiLCJpZCI6IjAifSx7Im5hbWUiOiJvcF9nYXRlZF9wZnNfZGVlcF9hcmNoaXZlIiwiaWQiOiIwIn0seyJuYW1lIjoib3BfZ2F0ZWRfYV9hcC1zb3V0aGVhc3QtMWUiLCJpZCI6IjAifSx7Im5hbWUiOiJvcF9nYXRlZF9hX3J1LW1vc2Nvdy0xYiIsImlkIjoiMCJ9LHsibmFtZSI6Im9wX2dhdGVkX2FfYXAtc291dGhlYXN0LTFkIiwiaWQiOiIwIn0seyJuYW1lIjoib3BfZ2F0ZWRfYXBwc3RhZ2UiLCJpZCI6IjAifSx7Im5hbWUiOiJvcF9nYXRlZF9hX2FwLXNvdXRoZWFzdC0xZiIsImlkIjoiMCJ9LHsibmFtZSI6Im9wX2dhdGVkX3Ntbl9hcHBsaWNhdGlvbiIsImlkIjoiMCJ9LHsibmFtZSI6Im9wX2dhdGVkX2V2c19jb2xkIiwiaWQiOiIwIn0seyJuYW1lIjoib3BfZ2F0ZWRfZWNzX2dwdV9nNXIiLCJpZCI6IjAifSx7Im5hbWUiOiJvcF9nYXRlZF9vcF9nYXRlZF9tZXNzYWdlb3ZlcjVnIiwiaWQiOiIwIn0seyJuYW1lIjoib3BfZ2F0ZWRfZWNzX3JpIiwiaWQiOiIwIn0seyJuYW1lIjoib3BfZ2F0ZWRfYV9ydS1ub3J0aHdlc3QtMmMiLCJpZCI6IjAifSx7Im5hbWUiOiJvcF9nYXRlZF9pZWZfcGxhdGludW0iLCJpZCI6IjAifV0sInByb2plY3QiOnsiZG9tYWluIjp7Im5hbWUiOiJoc2cxMzQzNzE3MzQ0MCIsImlkIjoiNDY5ZWU1MzAzMjdlNGYzOTkxMTgyNjg0MmRiY2E3ZTcifSwibmFtZSI6ImNuLW5vcnRoLTEiLCJpZCI6IjY3MDc2YWVjOTAyODQ4Zjc4NGI3YWMxMzk0NzY1NmYzIn0sImlzc3VlZF9hdCI6IjIwMjUtMTAtMjBUMDI6Mzc6NTAuODE0MDAwWiIsInVzZXIiOnsiZG9tYWluIjp7Im5hbWUiOiJoc2cxMzQzNzE3MzQ0MCIsImlkIjoiNDY5ZWU1MzAzMjdlNGYzOTkxMTgyNjg0MmRiY2E3ZTcifSwibmFtZSI6ImRlbmdsdSIsInBhc3N3b3JkX2V4cGlyZXNfYXQiOiIiLCJpZCI6IjMyN2YwNjQ0N2Y1OTRkYzc5M2JjOTg4NTkwOGUwZWRiIn19fTGCAcEwggG9AgEBMIGXMIGJMQswCQYDVQQGEwJDTjESMBAGA1UECAwJR3VhbmdEb25nMREwDwYDVQQHDAhTaGVuWmhlbjEuMCwGA1UECgwlSHVhd2VpIFNvZnR3YXJlIFRlY2hub2xvZ2llcyBDby4sIEx0ZDEOMAwGA1UECwwFQ2xvdWQxEzARBgNVBAMMCmNhLmlhbS5wa2kCCQDcsytdEGFqEDALBglghkgBZQMEAgEwDQYJKoZIhvcNAQEBBQAEggEAbtVzZi6mqzlm1pyXLoQK2jNhfm5hqnjO8LrWK5FK1rtWpQU8ouEFJBfUDor6kcJpetMnf8fnpJF9Lmd+rT8N5-39Cpdr831ooUJufWIzBSt8Y+N+IoFUpQd-Ut5PRl0YpGWf90015EHcY-hhJk6rW-URTPXwC57f8CGkd3gOF7PPlv9TzIYtyjpCCjJah2kGjqfogYIQ9l1EBsZQplYd5OaYaH-dvxFBA8tlNteaEWGRAfhqsu6bbMVXlFG0VwlnQdUFvR6N7dUytmgKxaaHjyH2KUuGwgh6ELevfnYyqoYk-UAarnr3MbrnpmfcL7z3D-4aBOSTmXIOIHFkFRiUCQ==";
+ R r = uploadNumberFileTest("https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/38878509-00b7-4fb2-9c3a-b53d8161d84a.jpg","");
+// System.err.println(r.getMsg());
+// String tokenTest = getTokenTest();
+// System.err.println(tokenTest);
+// R r = addANumberTest("17828262728", "刘杰", "511028200002150816", "avatar6278474701910805124b53d8161d84a.jpg");
+// System.err.println(r.getMsg());
+ }
+}
diff --git a/ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/utils/UrlToMultipartFileUtil.java b/ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/utils/UrlToMultipartFileUtil.java
new file mode 100644
index 0000000..1f23055
--- /dev/null
+++ b/ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/utils/UrlToMultipartFileUtil.java
@@ -0,0 +1,60 @@
+package com.ruoyi.admin.utils;
+
+import org.apache.http.entity.ContentType;
+import org.springframework.mock.web.MockMultipartFile;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.*;
+import java.net.URL;
+
+public class UrlToMultipartFileUtil {
+
+ // url: 链接,可访问的图片,视频,或其他文件
+ public static MultipartFile urlTransferMultipartFile(String url){
+ //对本地文件命名,可以从链接截取,可以自己手写,看需求 https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/98c0800f-3178-4db5-bf21-d53de2ed7849.jpg
+ // 截取出文件名,不包含尾缀
+ String fileName = url.substring(url.lastIndexOf("-") + 1);
+ File file = null;
+ URL urlfile;
+ InputStream inStream = null;
+ OutputStream os = null;
+ MultipartFile multipartFile = null;
+ try {
+ file = File.createTempFile("avatar", fileName);
+ //下载
+ urlfile = new URL(url);
+ inStream = urlfile.openStream();
+ os = new FileOutputStream(file);
+
+ int bytesRead = 0;
+ byte[] buffer = new byte[8192];
+ while ((bytesRead = inStream.read(buffer, 0, 8192)) != -1) {
+ os.write(buffer, 0, bytesRead);
+ }
+ // file转multipartFile,如果只需要转File就不用加下面这两行代码,直接返回File即可
+ FileInputStream inputStream = new FileInputStream(file);
+ multipartFile = new MockMultipartFile(file.getName(), file.getName(),
+ ContentType.APPLICATION_OCTET_STREAM.toString(), inputStream);
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ if (null != os) {
+ os.close();
+ }
+ if (null != inStream) {
+ inStream.close();
+ }
+ // 用完删除
+ if (null != file){
+ file.delete();
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ // 按需返回file还是multipartFile
+ return multipartFile;
+ }
+
+}
diff --git a/ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/voice/service/IAXBInterfaceDemo.java b/ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/voice/service/IAXBInterfaceDemo.java
new file mode 100644
index 0000000..5788760
--- /dev/null
+++ b/ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/voice/service/IAXBInterfaceDemo.java
@@ -0,0 +1,57 @@
+package com.ruoyi.admin.voice.service;
+
+/**
+ * AXB模式接口
+ */
+public interface IAXBInterfaceDemo {
+ /**
+ * Set X number to be the privacy number between number a and number b |
+ * 隐私号码AXB绑定
+ *
+ * @param relationNum 关系号码
+ * @param callerNum 主叫号码
+ * @param calleeNum 被叫号码
+ */
+ String axbBindNumber(String relationNum, String callerNum, String calleeNum);
+
+ /**
+ * Modify number a/b of the privacy relationship assigned by subscriptionId |
+ * 隐私号码AXB绑定信息修改
+ *
+ * @param subscriptionId 绑定关系ID
+ * @param callerNum 主叫号码
+ * @param calleeNum 被叫号码
+ */
+ void axbModifyNumber(String subscriptionId, String callerNum, String calleeNum);
+
+ /**
+ * Unbind the privacy relationship between number a and number b | 隐私号码AXB解绑
+ *
+ * @param subscriptionId 绑定关系ID
+ * @param relationNum 关系号码 都传时以subscriptionId优先
+ */
+ void axbUnbindNumber(String subscriptionId, String relationNum);
+
+ /**
+ * Query the privacy binding relationship on the X number | 查询AXB绑定信息
+ *
+ * @param subscriptionId 绑定关系ID
+ * @param relationNum 关系号码 都传时以subscriptionId优先
+ */
+ void axbQueryBindRelation(String subscriptionId, String relationNum);
+
+ /**
+ * Get download link of the record file created in call | 获取录音文件下载地址
+ *
+ * @param recordDomain 录音文件存储的服务器域名
+ * @param fileName 录音文件名
+ */
+ String axbGetRecordDownloadLink(String recordDomain, String fileName);
+
+ /**
+ * Stop the call on the X number assigned by sessionid | 终止呼叫
+ *
+ * @param sessionid 呼叫会话ID 通过"呼叫事件通知接口"获取
+ */
+ void axbStopCall(String sessionid);
+}
\ No newline at end of file
diff --git a/ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/voice/service/impl/AXBInterfaceDemoImpl.java b/ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/voice/service/impl/AXBInterfaceDemoImpl.java
new file mode 100644
index 0000000..cc908c6
--- /dev/null
+++ b/ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/voice/service/impl/AXBInterfaceDemoImpl.java
@@ -0,0 +1,208 @@
+package com.ruoyi.admin.voice.service.impl;
+
+import com.alibaba.fastjson.JSONObject;
+import com.ruoyi.admin.voice.service.IAXBInterfaceDemo;
+import com.ruoyi.admin.voice.util.HttpUtilClient;
+import com.ruoyi.common.core.exception.ServiceException;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * AXB模式接口测试
+ */
+@Slf4j
+public class AXBInterfaceDemoImpl implements IAXBInterfaceDemo {
+
+ private String appKey; // APP_Key
+ private String appSecret; // APP_Secret
+ private String ompDomainName; // APP接入地址
+
+ public AXBInterfaceDemoImpl(String appKey, String appSecret, String ompDomainName) {
+ this.appKey = appKey;
+ this.appSecret = appSecret;
+ this.ompDomainName = ompDomainName;
+ }
+
+ /**
+ * Build the real url of https request | 构建隐私保护通话平台请求路径
+ *
+ * @param path 接口访问URI
+ * @return
+ */
+ private String buildOmpUrl(String path) {
+ return ompDomainName + path;
+ }
+
+ @Override
+ public String axbBindNumber(String relationNum, String callerNum, String calleeNum) {
+ if (StringUtils.isBlank(relationNum) || StringUtils.isBlank(callerNum) || StringUtils.isBlank(calleeNum)) {
+ log.info("axbBindNumber set params error");
+ throw new ServiceException("axbBindNumber set params error");
+ }
+
+ // 必填,AXB模式绑定接口访问URI
+ String url = "/rest/caas/relationnumber/partners/v1.0";
+ String realUrl = buildOmpUrl(url);
+
+ // 封装JOSN请求
+ JSONObject json = new JSONObject();
+ json.put("relationNum", relationNum); // X号码(关系号码)
+ json.put("callerNum", callerNum); // A方真实号码(手机或固话)
+ json.put("calleeNum", calleeNum); // B方真实号码(手机或固话)
+
+ /**
+ * 选填,各参数要求请参考"AXB模式绑定接口"
+ */
+// json.put("areaCode", "0755"); //城市码
+// json.put("areaMatchMode", "1"); //号码筛选方式
+// json.put("callDirection", 0); //允许呼叫的方向
+// json.put("duration", 86400); //绑定关系保持时间
+// json.put("recordFlag", false); //是否通话录音
+// json.put("recordHintTone", "recordHintTone.wav"); //录音提示音
+// json.put("maxDuration", 60); //单次通话最长时间
+// json.put("lastMinVoice", "lastMinVoice.wav"); //通话最后一分钟提示音
+// json.put("privateSms", true); //是否支持短信功能
+// JSONObject preVoice = new JSONObject();
+// preVoice.put("callerHintTone", "callerHintTone.wav"); //设置A拨打X号码时的通话前等待音
+// preVoice.put("calleeHintTone", "calleeHintTone.wav"); //设置B拨打X号码时的通话前等待音
+// json.put("preVoice", preVoice); //个性化通话前等待音
+
+ String result = HttpUtilClient.sendPost(appKey, appSecret, realUrl, json.toString());
+ log.info("Response is :" + result);
+ return result;
+ }
+
+ @Override
+ public void axbModifyNumber(String subscriptionId, String callerNum, String calleeNum) {
+ if (StringUtils.isBlank(subscriptionId)) {
+ log.info("axbModifyNumber set params error");
+ return;
+ }
+
+ // 必填,AXB模式绑定信息修改接口访问URI
+ String url = "/rest/caas/relationnumber/partners/v1.0";
+ String realUrl = buildOmpUrl(url);
+
+ // 封装JOSN请求
+ JSONObject json = new JSONObject();
+ json.put("subscriptionId", subscriptionId); // 绑定关系ID
+ if (StringUtils.isNotBlank(callerNum)) {
+ json.put("callerNum", callerNum); // 将A方修改为新的号码(手机或固话)
+ }
+ if (StringUtils.isNotBlank(calleeNum)) {
+ json.put("calleeNum", calleeNum); // 将B方修改为新的号码(手机或固话)
+ }
+
+ /**
+ * 选填,各参数要求请参考"AXB模式绑定信息修改接口"
+ */
+// json.put("callDirection", 0); //允许呼叫的方向
+// json.put("duration", 86400); //绑定关系保持时间
+// json.put("maxDuration", 90); //单次通话最长时间
+// json.put("lastMinVoice", "lastMinVoice.wav"); //通话最后一分钟提示音
+// json.put("privateSms", true); //是否支持短信功能
+// json.put("recordFlag", false); //是否通话录音
+// JSONObject preVoice = new JSONObject();
+// preVoice.put("callerHintTone", "callerHintTone.wav"); //设置A拨打X号码时的通话前等待音
+// preVoice.put("calleeHintTone", "calleeHintTone.wav"); //设置B拨打X号码时的通话前等待音
+// json.put("preVoice", preVoice); //个性化通话前等待音
+
+ String result = HttpUtilClient.sendPut(appKey, appSecret, realUrl, json.toString());
+ log.info("Response is :" + result);
+ }
+
+ @Override
+ public void axbUnbindNumber(String subscriptionId, String relationNum) {
+ if (StringUtils.isBlank(subscriptionId) && StringUtils.isBlank(relationNum)) {
+ log.info("axbUnbindNumber set params error");
+ return;
+ }
+
+ // 必填,AXB模式解绑接口访问URI
+ String url = "/rest/caas/relationnumber/partners/v1.0";
+ String realUrl = buildOmpUrl(url);
+
+ // 申明对象
+ Map<String, Object> map = new HashMap<String, Object>();
+ if (StringUtils.isNotBlank(subscriptionId)) {
+ map.put("subscriptionId", subscriptionId); // 绑定关系ID
+ } else {
+ map.put("relationNum", relationNum); // X号码(关系号码)
+ }
+
+ String result = HttpUtilClient.sendDelete(appKey, appSecret, realUrl, HttpUtilClient.map2UrlEncodeString(map));
+ log.info("Response is :" + result);
+ }
+
+ @Override
+ public void axbQueryBindRelation(String subscriptionId, String relationNum) {
+ if (StringUtils.isBlank(subscriptionId) && StringUtils.isBlank(relationNum)) {
+ log.info("axbQueryBindRelation set params error");
+ return;
+ }
+
+ // 必填,AXB模式绑定信息查询接口访问URI
+ String url = "/rest/caas/relationnumber/partners/v1.0";
+ String realUrl = buildOmpUrl(url);
+
+ // 申明对象
+ Map<String, Object> map = new HashMap<String, Object>();
+ if (StringUtils.isNotBlank(subscriptionId)) {
+ map.put("subscriptionId", subscriptionId); // 绑定关系ID
+ } else {
+ map.put("relationNum", relationNum); // X号码(关系号码)
+
+ /**
+ * 选填,各参数要求请参考"AXB模式绑定信息查询接口"
+ */
+// map.put("pageIndex", 1); //查询的分页索引,从1开始编号
+// map.put("pageSize", 20); //查询的分页大小,即每次查询返回多少条数据
+ }
+
+ String result = HttpUtilClient.sendGet(appKey, appSecret, realUrl, HttpUtilClient.map2UrlEncodeString(map));
+ log.info("Response is :" + result);
+ }
+
+ @Override
+ public String axbGetRecordDownloadLink(String recordDomain, String fileName) {
+ if (StringUtils.isBlank(recordDomain) || StringUtils.isBlank(fileName)) {
+ log.info("axbGetRecordDownloadLink set params error");
+ throw new ServiceException("axbGetRecordDownloadLink set params error");
+ }
+ // 必填,AXB模式获取录音文件下载地址接口访问URI
+ String url = "/rest/provision/voice/record/v1.0";
+ String realUrl = buildOmpUrl(url);
+
+ // 申明对象
+ Map<String, Object> map = new HashMap<String, Object>();
+ map.put("recordDomain", recordDomain); // 录音文件存储的服务器域名
+ map.put("fileName", fileName); // 录音文件名
+
+ String result = HttpUtilClient.sendGet(appKey, appSecret, realUrl, HttpUtilClient.map2UrlEncodeString(map));
+ log.info("Response is :" + result);
+ return result;
+ }
+
+ @Override
+ public void axbStopCall(String sessionid) {
+ if (StringUtils.isBlank(sessionid)) {
+ log.info("axbStopCall set params error");
+ return;
+ }
+
+ // 必填,AXB模式终止呼叫接口访问URI
+ String url = "/rest/httpsessions/callStop/v2.0";
+ String realUrl = buildOmpUrl(url);
+
+ // 封装JOSN请求
+ JSONObject json = new JSONObject();
+ json.put("sessionid", sessionid); // 呼叫会话ID
+ json.put("signal", "call_stop"); // 取值固定为"call_stop"
+
+ String result = HttpUtilClient.sendPost(appKey, appSecret, realUrl, json.toString());
+ log.info("Response is :" + result);
+ }
+}
\ No newline at end of file
diff --git a/ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/voice/util/AXBUtil.java b/ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/voice/util/AXBUtil.java
new file mode 100644
index 0000000..bb643ad
--- /dev/null
+++ b/ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/voice/util/AXBUtil.java
@@ -0,0 +1,60 @@
+package com.ruoyi.admin.voice.util;
+
+
+import com.ruoyi.admin.voice.service.IAXBInterfaceDemo;
+import com.ruoyi.admin.voice.service.impl.AXBInterfaceDemoImpl;
+
+public class AXBUtil {
+
+ /**
+ * 必填,请登录管理控制台,从"应用管理"页获取
+ */
+ private final static String OMPDOMAINNAME = "https://rtcpns.cn-north-1.myhuaweicloud.com"; // APP接入地址
+
+ /**
+ * AXB模式 绑定号码
+ * @param relationNum X 隐私号码 13154610294
+ * @param callerNum A
+ * @param calleeNum B
+ */
+ public static String axbBindNumber(String appKey,String appSecret,String relationNum, String callerNum, String calleeNum) {
+ IAXBInterfaceDemo axb = new AXBInterfaceDemoImpl(appKey, appSecret, OMPDOMAINNAME);
+ // 第一步: 号码绑定,即调用AXB模式绑定接口 93f4474b-3e0b-490d-bd1d-b6fd31b63c0b
+ return axb.axbBindNumber("+86"+relationNum, "+86"+callerNum, "+86"+calleeNum);
+ }
+
+ /**
+ * Get download link of the record file created in call | 获取录音文件下载地址
+ * @param recordDomain 录音文件存储的服务器域名
+ * @param fileName 录音文件名
+ */
+ public static String axbGetRecordDownloadLink(String appKey,String appSecret,String recordDomain, String fileName) {
+ IAXBInterfaceDemo axb = new AXBInterfaceDemoImpl(appKey, appSecret, OMPDOMAINNAME);
+ // 第三步: 用户通话结束,若设置录音,则商户可以获取录音文件下载地址,即调用获取录音文件下载地址接口
+ return axb.axbGetRecordDownloadLink(recordDomain, fileName);
+ }
+ /**
+ * Modify number a/b of the privacy relationship assigned by subscriptionId |
+ * 隐私号码AXB绑定信息修改
+ * @param subscriptionId 绑定关系ID
+ * @param callerNum 主叫号码
+ * @param calleeNum 被叫号码
+ */
+ public static void axbModifyNumber(String appKey,String appSecret,String subscriptionId, String callerNum, String calleeNum) {
+ IAXBInterfaceDemo axb = new AXBInterfaceDemoImpl(appKey, appSecret, OMPDOMAINNAME);
+ // 第四步: 根据业务需求,可更改绑定关系,即调用AXB模式绑定信息修改接口
+ axb.axbModifyNumber(subscriptionId, callerNum, calleeNum);
+ }
+
+ /**
+ * AXB模式 解绑号码
+ * @param subscriptionId 绑定号码后的唯一标识
+ * @param relationNum X 隐私号码 13154610294
+ */
+ public static void axbUnbindNumber(String appKey,String appSecret,String subscriptionId, String relationNum) {
+ IAXBInterfaceDemo axb = new AXBInterfaceDemoImpl(appKey, appSecret, OMPDOMAINNAME);
+ // 第五步: 隐私号码循环使用,商户可将绑定关系解绑,即调用AXB模式解绑接口
+ axb.axbUnbindNumber(subscriptionId, "+86"+relationNum);
+ }
+
+}
diff --git a/ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/voice/util/HttpUtilClient.java b/ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/voice/util/HttpUtilClient.java
new file mode 100644
index 0000000..d8a80b6
--- /dev/null
+++ b/ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/voice/util/HttpUtilClient.java
@@ -0,0 +1,360 @@
+package com.ruoyi.admin.voice.util;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.StringUtils;
+
+import javax.net.ssl.*;
+import java.io.*;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.Map;
+
+/**
+ * HTTP协议传输工具类
+ */
+@Slf4j
+public class HttpUtilClient {
+
+ private static final int HTTP_STATUS_OK = 200;
+
+ /**
+ * 向指定 URL发送POST方法的请求
+ *
+ * @param appKey
+ * @param appSecret
+ * @param url
+ * @param jsonBody
+ * @return
+ */
+ public static String sendPost(String appKey, String appSecret, String url, String jsonBody) {
+ DataOutputStream 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;
+ }
+ };
+ try {
+ trustAllHttpsCertificates();
+ } catch (Exception e1) {
+ e1.printStackTrace();
+ }
+ try {
+ URL realUrl = new URL(url);
+ connection = (HttpsURLConnection) realUrl.openConnection();
+
+ connection.setHostnameVerifier(hv);
+ connection.setDoOutput(true);
+ connection.setDoInput(true);
+ connection.setRequestMethod("POST");
+ connection.setRequestProperty("Accept", "application/json");
+ connection.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
+ connection.setRequestProperty("Authorization",
+ "AKSK realm=\"SDP\",profile=\"UsernameToken\",type=\"Appkey\"");
+ connection.setRequestProperty("X-AKSK", StringUtil.buildAKSKHeader(appKey, appSecret));
+
+ log.info("RequestBody is : " + jsonBody);
+
+ connection.connect();
+ out = new DataOutputStream(connection.getOutputStream());
+ out.writeBytes(jsonBody);
+ out.flush();
+ out.close();
+
+ int status = connection.getResponseCode();
+ if (HTTP_STATUS_OK == status) {
+ is = connection.getInputStream();
+ } else {
+ is = connection.getErrorStream();
+ }
+ in = new BufferedReader(new InputStreamReader(is, "UTF-8"));
+ String line = "";
+ while ((line = in.readLine()) != null) {
+ result.append(line);
+ }
+ } catch (Exception e) {
+ log.info("Send Post request catch exception: " + e.toString());
+ }
+ finally {
+ IOUtils.closeQuietly(out);
+ IOUtils.closeQuietly(is);
+ IOUtils.closeQuietly(in);
+ if (null != connection) {
+ IOUtils.close(connection);
+ }
+ }
+ return result.toString();
+ }
+
+ /**
+ * 向指定 URL发送PUT方法的请求
+ *
+ * @param appKey
+ * @param appSecret
+ * @param url
+ * @param jsonBody
+ * @return
+ */
+ public static String sendPut(String appKey, String appSecret, String url, String jsonBody) {
+ DataOutputStream 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;
+ }
+ };
+ try {
+ trustAllHttpsCertificates();
+ } catch (Exception e1) {
+ e1.printStackTrace();
+ }
+ try {
+ URL realUrl = new URL(url);
+ connection = (HttpsURLConnection) realUrl.openConnection();
+
+ connection.setHostnameVerifier(hv);
+ connection.setDoOutput(true);
+ connection.setDoInput(true);
+ connection.setRequestMethod("PUT");
+ connection.setRequestProperty("Accept", "application/json");
+ connection.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
+ connection.setRequestProperty("Authorization",
+ "AKSK realm=\"SDP\",profile=\"UsernameToken\",type=\"Appkey\"");
+ connection.setRequestProperty("X-AKSK", StringUtil.buildAKSKHeader(appKey, appSecret));
+
+ log.info("RequestBody is : " + jsonBody);
+
+ connection.connect();
+ out = new DataOutputStream(connection.getOutputStream());
+ out.writeBytes(jsonBody);
+ out.flush();
+ out.close();
+
+ int status = connection.getResponseCode();
+ if (HTTP_STATUS_OK == status) {
+ is = connection.getInputStream();
+ } else {
+ is = connection.getErrorStream();
+ }
+ in = new BufferedReader(new InputStreamReader(is));
+ String line;
+ while ((line = in.readLine()) != null) {
+ result.append(line);
+ }
+ } catch (Exception e) {
+ log.info("Send Put request catch exception: " + e.toString());
+ e.printStackTrace();
+ }
+ finally {
+ IOUtils.closeQuietly(out);
+ IOUtils.closeQuietly(is);
+ IOUtils.closeQuietly(in);
+ if (null != connection) {
+ IOUtils.close(connection);
+ }
+ }
+ return result.toString();
+ }
+
+ /**
+ * 向指定 URL发送DELETE方法的请求
+ *
+ * @param appKey
+ * @param appSecret
+ * @param url
+ * @param params
+ * @return
+ */
+ public static String sendDelete(String appKey, String appSecret, String url, String params) {
+ 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;
+ }
+ };
+ try {
+ trustAllHttpsCertificates();
+ } catch (Exception e1) {
+ e1.printStackTrace();
+ }
+ try {
+ String realPath = url + (StringUtils.isEmpty(params) ? "" : "?" + params);
+ URL realUrl = new URL(realPath);
+ connection = (HttpsURLConnection) realUrl.openConnection();
+
+ connection.setHostnameVerifier(hv);
+ connection.setDoInput(true);
+ connection.setRequestMethod("DELETE");
+ connection.setRequestProperty("Accept", "application/json");
+ connection.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
+ connection.setRequestProperty("Authorization",
+ "AKSK realm=\"SDP\",profile=\"UsernameToken\",type=\"Appkey\"");
+ connection.setRequestProperty("X-AKSK", StringUtil.buildAKSKHeader(appKey, appSecret));
+
+ log.info("RequestBody is : " + params);
+
+ connection.connect();
+
+ int status = connection.getResponseCode();
+ if (HTTP_STATUS_OK == status) {
+ is = connection.getInputStream();
+ } else {
+ is = connection.getErrorStream();
+ }
+ in = new BufferedReader(new InputStreamReader(is));
+ String line;
+ while ((line = in.readLine()) != null) {
+ result.append(line);
+ }
+ } catch (Exception e) {
+ log.info("Send DELETE request catch exception: " + e.toString());
+ }
+ finally {
+ IOUtils.closeQuietly(is);
+ IOUtils.closeQuietly(in);
+ if (null != connection) {
+ IOUtils.close(connection);
+ }
+ }
+ return result.toString();
+ }
+
+ /**
+ * 向指定 URL发送GET方法的请求
+ *
+ * @param appKey
+ * @param appSecret
+ * @param url
+ * @param params
+ * @return
+ */
+ public static String sendGet(String appKey, String appSecret, String url, String params) {
+ 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;
+ }
+ };
+ try {
+ trustAllHttpsCertificates();
+ } catch (Exception e1) {
+ e1.printStackTrace();
+ }
+ try {
+ String realPath = url + (StringUtils.isEmpty(params) ? "" : "?" + params);
+ URL realUrl = new URL(realPath);
+ connection = (HttpsURLConnection) realUrl.openConnection();
+
+ connection.setHostnameVerifier(hv);
+ connection.setDoInput(true);
+ connection.setRequestMethod("GET");
+ connection.setRequestProperty("Accept", "application/json");
+ connection.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
+ connection.setRequestProperty("Authorization",
+ "AKSK realm=\"SDP\",profile=\"UsernameToken\",type=\"Appkey\"");
+ connection.setRequestProperty("X-AKSK", StringUtil.buildAKSKHeader(appKey, appSecret));
+ connection.setInstanceFollowRedirects(false); //设置本次连接不自动处理重定向
+
+ log.info("RequestBody is : " + params);
+
+ connection.connect();
+
+ int status = connection.getResponseCode();
+ if (301 == status) { //获取录音文件下载地址
+ return connection.getHeaderField("Location");
+ }else if (HTTP_STATUS_OK == status) { //查询绑定信息
+ is = connection.getInputStream();
+ } else { //获取错误码
+ is = connection.getErrorStream();
+ }
+ in = new BufferedReader(new InputStreamReader(is));
+ String line;
+ while ((line = in.readLine()) != null) {
+ result.append(line);
+ }
+ } catch (Exception e) {
+ log.info("Send GET request catch exception: " + e.toString());
+ }
+ finally {
+ IOUtils.closeQuietly(is);
+ IOUtils.closeQuietly(in);
+ if (null != connection) {
+ IOUtils.close(connection);
+ }
+ }
+ return result.toString();
+ }
+
+ /**
+ * 键值对转查询url
+ *
+ * @param map
+ * @return
+ */
+ public static String map2UrlEncodeString(Map<String, Object> map) {
+ if(null == map || map.isEmpty()) {
+ return "";
+ }
+ StringBuilder sb = new StringBuilder();
+ String temp = "";
+
+ for (String s : map.keySet()) {
+ try {
+ temp = URLEncoder.encode(String.valueOf(map.get(s)), "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ e.printStackTrace();
+ }
+ sb.append(s).append("=").append(temp).append("&");
+ }
+ return sb.deleteCharAt(sb.length() - 1).toString();
+ }
+
+ /**
+ * 忽略SSL证书校验
+ *
+ * @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());
+ }
+}
\ No newline at end of file
diff --git a/ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/voice/util/StringUtil.java b/ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/voice/util/StringUtil.java
new file mode 100644
index 0000000..d8d70e7
--- /dev/null
+++ b/ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/voice/util/StringUtil.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright Notice:
+ * Copyright 1998-2008, Huawei Technologies Co., Ltd. ALL Rights Reserved.
+ *
+ * Warning: This computer software sourcecode is protected by copyright law
+ * and international treaties. Unauthorized reproduction or distribution
+ * of this sourcecode, or any portion of it, may result in severe civil and
+ * criminal penalties, and will be prosecuted to the maximum extent
+ * possible under the law.
+ */
+package com.ruoyi.admin.voice.util;
+
+//import java.nio.charset.Charset;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+import java.nio.charset.StandardCharsets;
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+public class StringUtil {
+ public static final String AKSK_HEADER_FORMAT = "UsernameToken Username=\"%s\",PasswordDigest=\"%s\",Nonce=\"%s\",Created=\"%s\"";
+
+ public static boolean strIsNullOrEmpty(String s) {
+ return (null == s || s.trim().length() < 1);
+ }
+
+ public static String buildAKSKHeader(String appKey, String appSecret) throws Exception {
+ if (StringUtil.strIsNullOrEmpty(appKey) || StringUtil.strIsNullOrEmpty(appSecret)) {
+ return null;
+ }
+
+ SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
+ format.setTimeZone(TimeZone.getTimeZone("UTC"));
+ Calendar calendar = Calendar.getInstance();
+ String time = format.format(calendar.getTime());
+ String stNonce = UUID.randomUUID().toString().replace("-", "").toUpperCase(Locale.ROOT);
+ String str = stNonce + time;
+ Mac mac = Mac.getInstance("HmacSHA256");
+ mac.init(new SecretKeySpec(appSecret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
+ byte[] authBytes = mac.doFinal(str.getBytes(StandardCharsets.UTF_8));
+ String passwordDigestBase64Str = encodeBase64(authBytes);
+ return String.format(AKSK_HEADER_FORMAT, appKey, passwordDigestBase64Str, stNonce, time);
+ }
+
+ private static String encodeBase64(byte[] bytes) {
+ if (bytes.length == 0) {
+ return null;
+ } else {
+ return new String(Base64.getEncoder().encode(bytes), StandardCharsets.UTF_8);
+ }
+ }
+}
\ No newline at end of file
diff --git a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/OrderController.java b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/OrderController.java
index 32f12f1..df666e7 100644
--- a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/OrderController.java
+++ b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/OrderController.java
@@ -132,6 +132,35 @@
.eq(Order::getId, orderId).set(Order::getState, state).update());
}
+ /**
+ * 更改订单虚拟童话绑定id
+ */
+ @ApiOperation(value = "订单列表-设置虚拟号码绑定id", tags = {"后台-订单管理"})
+ @GetMapping(value = "/updateSubscriptionId")
+ public R<Boolean> updateSubscriptionId(@RequestParam("orderId") String orderId,
+ @RequestParam("subscriptionId")String subscriptionId,
+ @RequestParam("virtualNumber")String virtualNumber) {
+ // 修改订单设置绑定id
+ orderService.lambdaUpdate().eq(Order::getId, orderId)
+ .set(Order::getSubscriptionId, subscriptionId)
+ .set(Order::getVirtualNumber, virtualNumber)
+ .update();
+ return R.ok();
+ }
+ /**
+ * 更改订单虚拟童话录音
+ */
+ @ApiOperation(value = "订单列表-设置虚拟号码绑定id", tags = {"后台-订单管理"})
+ @GetMapping(value = "/updatePhoneRecording")
+ public R<Boolean> updatePhoneRecording(@RequestParam("orderId") String orderId,
+ @RequestParam("audioUrl")String audioUrl) {
+ // 修改订单设置绑定id
+ orderService.lambdaUpdate().eq(Order::getId, orderId)
+ .set(Order::getPhoneRecording, audioUrl)
+ .update();
+ return R.ok();
+ }
+
@ApiOperation(value = "订单列表-增加打印次数", tags = {"后台-订单管理"})
@PostMapping(value = "/count")
@@ -744,7 +773,7 @@
@ApiOperation(value = "订单完工-提交订单", tags = {"师傅端-订单列表"})
@PostMapping(value = "/orderSubmit")
@Transactional(rollbackFor = Exception.class)
- public R<Boolean> orderSubmit(@RequestBody OrderSubmitRequest orderSubmitRequest) {
+ public R<String> orderSubmit(@RequestBody OrderSubmitRequest orderSubmitRequest) {
// 订单信息
Order order = orderService.lambdaQuery().eq(Order::getId, orderSubmitRequest.getOrderId())
.eq(Order::getIsDelete, 0).one();
@@ -765,7 +794,10 @@
serveRecord.setCardPic(orderSubmitRequest.getCardPic());
serveRecord.setMachinePic(orderSubmitRequest.getMachinePic());
boolean save = serveRecordService.save(serveRecord);
- return R.ok(update && save);
+ if(update && save){
+ return R.ok(order.getSubscriptionId(), "订单提交成功!");
+ }
+ return R.ok();
}
/**
diff --git a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/entity/Order.java b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/entity/Order.java
index c1821f5..8f9615c 100644
--- a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/entity/Order.java
+++ b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/entity/Order.java
@@ -216,4 +216,19 @@
@TableField("msg_count")
private Integer msgCount;
+ @ApiModelProperty("绑定关系id")
+ @TableField("subscription_id")
+ private String subscriptionId;
+ @ApiModelProperty("虚拟号码")
+ @TableField("virtual_number")
+ private String virtualNumber;
+ @ApiModelProperty("电话录音")
+ @TableField("phone_recording")
+ private String phoneRecording;
+ @ApiModelProperty("录音文件存储的服务器域名")
+ @TableField("record_domain")
+ private String recordDomain;
+ @ApiModelProperty("录音文件名")
+ @TableField("record_object_name")
+ private String recordObjectName;
}
diff --git a/ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/controller/OrderController.java b/ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/controller/OrderController.java
index cf1d75f..3eb83a2 100644
--- a/ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/controller/OrderController.java
+++ b/ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/controller/OrderController.java
@@ -4,6 +4,7 @@
import cn.afterturn.easypoi.cache.manager.IFileLoader;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.admin.api.feignClient.AdminClient;
import com.ruoyi.common.core.constant.Constants;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.domain.Result;
@@ -19,11 +20,14 @@
import com.ruoyi.order.api.feignClient.ExchangeDispatchClient;
import com.ruoyi.order.api.feignClient.OrderClient;
import com.ruoyi.system.api.model.LoginUserInfo;
+import com.ruoyi.worker.entity.IamConfig;
import com.ruoyi.worker.entity.MasterWorker;
import com.ruoyi.worker.entity.RecoveryServe;
+import com.ruoyi.worker.service.IamConfigService;
import com.ruoyi.worker.service.MasterWorkerService;
import com.ruoyi.worker.service.RecoveryServeService;
import com.ruoyi.worker.vo.ServeCoordinate;
+import com.ruoyi.worker.voice.util.AXBUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
@@ -36,18 +40,14 @@
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
+import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import lombok.Synchronized;
import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
@@ -81,6 +81,8 @@
@Resource
private RedisService redisService;
+ @Resource
+ private IamConfigService iamConfigService;
/**
* linux服务器保存订单轨迹文件夹
@@ -288,7 +290,29 @@
if (null == loginWorker) {
return R.loginExpire("登录失效!");
}
- return orderClient.orderSubmit(orderSubmitRequest);
+ R<String> result = orderClient.orderSubmit(orderSubmitRequest);
+ String subscriptionId = result.getData();
+ boolean flag = false;
+ if (StringUtils.hasLength(subscriptionId)) {
+ IamConfig iamConfig = iamConfigService.getById(1);
+ // 查询音频链接
+ OrderDetailVO data = orderClient.orderDetail(orderSubmitRequest.getOrderId()).getData();
+ if(Objects.nonNull(data)){
+ Order orderInfo = data.getOrderInfo();
+ if (Objects.nonNull(orderInfo)){
+ String recordDomain = orderInfo.getRecordDomain();
+ String recordObjectName = orderInfo.getRecordObjectName();
+ String audioUrl = AXBUtil.axbGetRecordDownloadLink(iamConfig.getAppKey(), iamConfig.getAppSecret(),recordDomain, recordObjectName);
+ orderInfo.setPhoneRecording(audioUrl);
+ // 设置音频
+ orderClient.updatePhoneRecording(orderInfo.getId(), audioUrl);
+ }
+ }
+ // 虚拟号码解绑
+ AXBUtil.axbUnbindNumber(iamConfig.getAppKey(), iamConfig.getAppSecret(),subscriptionId, iamConfig.getVirtualNumber());
+ flag = true;
+ }
+ return R.ok(flag);
}
private static final double EARTH_RADIUS_METERS = 6371000.0;
/**
diff --git a/ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/entity/IamConfig.java b/ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/entity/IamConfig.java
new file mode 100644
index 0000000..bbf6309
--- /dev/null
+++ b/ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/entity/IamConfig.java
@@ -0,0 +1,48 @@
+package com.ruoyi.worker.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.io.Serializable;
+
+/**
+ * <p>
+ * 虚拟号码配置
+ * </p>
+ *
+ * @author hjl
+ * @since 2024-07-08
+ */
+@Getter
+@Setter
+@TableName("t_iam_config")
+@ApiModel(value = "t_iam_config对象", description = "虚拟号码配置")
+public class IamConfig implements Serializable {
+
+ @TableId(value = "id", type = IdType.AUTO)
+ private Integer id;
+ @ApiModelProperty("用户")
+ @TableField("userName")
+ private String userName;
+ @ApiModelProperty("子账号")
+ @TableField("account")
+ private String account;
+ @ApiModelProperty("子账号密码")
+ @TableField("password")
+ private String password;
+ @ApiModelProperty("虚拟号码APP_Key")
+ @TableField("appKey")
+ private String appKey;
+ @ApiModelProperty("虚拟号码APP_Secret")
+ @TableField("appSecret")
+ private String appSecret;
+ @ApiModelProperty("虚拟号码")
+ @TableField("virtualNumber")
+ private String virtualNumber;
+}
diff --git a/ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/mapper/IamConfigMapper.java b/ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/mapper/IamConfigMapper.java
new file mode 100644
index 0000000..91624df
--- /dev/null
+++ b/ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/mapper/IamConfigMapper.java
@@ -0,0 +1,18 @@
+package com.ruoyi.worker.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.worker.entity.IamConfig;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * <p>
+ * 虚拟号码管理 Mapper 接口
+ * </p>
+ *
+ * @author hjl
+ * @since 2024-07-08
+ */
+@Mapper
+public interface IamConfigMapper extends BaseMapper<IamConfig> {
+
+}
diff --git a/ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/service/IamConfigService.java b/ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/service/IamConfigService.java
new file mode 100644
index 0000000..dccf60f
--- /dev/null
+++ b/ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/service/IamConfigService.java
@@ -0,0 +1,16 @@
+package com.ruoyi.worker.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.worker.entity.IamConfig;
+
+/**
+ * <p>
+ * 改派管理 服务类
+ * </p>
+ *
+ * @author hjl
+ * @since 2024-07-08
+ */
+public interface IamConfigService extends IService<IamConfig> {
+
+}
diff --git a/ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/service/impl/IamConfigServiceImpl.java b/ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/service/impl/IamConfigServiceImpl.java
new file mode 100644
index 0000000..053a932
--- /dev/null
+++ b/ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/service/impl/IamConfigServiceImpl.java
@@ -0,0 +1,20 @@
+package com.ruoyi.worker.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.worker.entity.IamConfig;
+import com.ruoyi.worker.mapper.IamConfigMapper;
+import com.ruoyi.worker.service.IamConfigService;
+import org.springframework.stereotype.Service;
+
+/**
+ * <p>
+ * 改派管理 服务实现类
+ * </p>
+ *
+ * @author hjl
+ * @since 2024-07-08
+ */
+@Service
+public class IamConfigServiceImpl extends ServiceImpl<IamConfigMapper, IamConfig> implements IamConfigService {
+
+}
diff --git a/ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/voice/service/IAXBInterfaceDemo.java b/ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/voice/service/IAXBInterfaceDemo.java
new file mode 100644
index 0000000..041a537
--- /dev/null
+++ b/ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/voice/service/IAXBInterfaceDemo.java
@@ -0,0 +1,57 @@
+package com.ruoyi.worker.voice.service;
+
+/**
+ * AXB模式接口
+ */
+public interface IAXBInterfaceDemo {
+ /**
+ * Set X number to be the privacy number between number a and number b |
+ * 隐私号码AXB绑定
+ *
+ * @param relationNum 关系号码
+ * @param callerNum 主叫号码
+ * @param calleeNum 被叫号码
+ */
+ String axbBindNumber(String relationNum, String callerNum, String calleeNum);
+
+ /**
+ * Modify number a/b of the privacy relationship assigned by subscriptionId |
+ * 隐私号码AXB绑定信息修改
+ *
+ * @param subscriptionId 绑定关系ID
+ * @param callerNum 主叫号码
+ * @param calleeNum 被叫号码
+ */
+ void axbModifyNumber(String subscriptionId, String callerNum, String calleeNum);
+
+ /**
+ * Unbind the privacy relationship between number a and number b | 隐私号码AXB解绑
+ *
+ * @param subscriptionId 绑定关系ID
+ * @param relationNum 关系号码 都传时以subscriptionId优先
+ */
+ void axbUnbindNumber(String subscriptionId, String relationNum);
+
+ /**
+ * Query the privacy binding relationship on the X number | 查询AXB绑定信息
+ *
+ * @param subscriptionId 绑定关系ID
+ * @param relationNum 关系号码 都传时以subscriptionId优先
+ */
+ void axbQueryBindRelation(String subscriptionId, String relationNum);
+
+ /**
+ * Get download link of the record file created in call | 获取录音文件下载地址
+ *
+ * @param recordDomain 录音文件存储的服务器域名
+ * @param fileName 录音文件名
+ */
+ String axbGetRecordDownloadLink(String recordDomain, String fileName);
+
+ /**
+ * Stop the call on the X number assigned by sessionid | 终止呼叫
+ *
+ * @param sessionid 呼叫会话ID 通过"呼叫事件通知接口"获取
+ */
+ void axbStopCall(String sessionid);
+}
\ No newline at end of file
diff --git a/ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/voice/service/impl/AXBInterfaceDemoImpl.java b/ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/voice/service/impl/AXBInterfaceDemoImpl.java
new file mode 100644
index 0000000..c06f028
--- /dev/null
+++ b/ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/voice/service/impl/AXBInterfaceDemoImpl.java
@@ -0,0 +1,208 @@
+package com.ruoyi.worker.voice.service.impl;
+
+import com.alibaba.fastjson.JSONObject;
+import com.ruoyi.common.core.exception.ServiceException;
+import com.ruoyi.worker.voice.service.IAXBInterfaceDemo;
+import com.ruoyi.worker.voice.util.HttpUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * AXB模式接口测试
+ */
+@Slf4j
+public class AXBInterfaceDemoImpl implements IAXBInterfaceDemo {
+
+ private String appKey; // APP_Key
+ private String appSecret; // APP_Secret
+ private String ompDomainName; // APP接入地址
+
+ public AXBInterfaceDemoImpl(String appKey, String appSecret, String ompDomainName) {
+ this.appKey = appKey;
+ this.appSecret = appSecret;
+ this.ompDomainName = ompDomainName;
+ }
+
+ /**
+ * Build the real url of https request | 构建隐私保护通话平台请求路径
+ *
+ * @param path 接口访问URI
+ * @return
+ */
+ private String buildOmpUrl(String path) {
+ return ompDomainName + path;
+ }
+
+ @Override
+ public String axbBindNumber(String relationNum, String callerNum, String calleeNum) {
+ if (StringUtils.isBlank(relationNum) || StringUtils.isBlank(callerNum) || StringUtils.isBlank(calleeNum)) {
+ log.info("axbBindNumber set params error");
+ throw new ServiceException("axbBindNumber set params error");
+ }
+
+ // 必填,AXB模式绑定接口访问URI
+ String url = "/rest/caas/relationnumber/partners/v1.0";
+ String realUrl = buildOmpUrl(url);
+
+ // 封装JOSN请求
+ JSONObject json = new JSONObject();
+ json.put("relationNum", relationNum); // X号码(关系号码)
+ json.put("callerNum", callerNum); // A方真实号码(手机或固话)
+ json.put("calleeNum", calleeNum); // B方真实号码(手机或固话)
+
+ /**
+ * 选填,各参数要求请参考"AXB模式绑定接口"
+ */
+// json.put("areaCode", "0755"); //城市码
+// json.put("areaMatchMode", "1"); //号码筛选方式
+// json.put("callDirection", 0); //允许呼叫的方向
+// json.put("duration", 86400); //绑定关系保持时间
+// json.put("recordFlag", false); //是否通话录音
+// json.put("recordHintTone", "recordHintTone.wav"); //录音提示音
+// json.put("maxDuration", 60); //单次通话最长时间
+// json.put("lastMinVoice", "lastMinVoice.wav"); //通话最后一分钟提示音
+// json.put("privateSms", true); //是否支持短信功能
+// JSONObject preVoice = new JSONObject();
+// preVoice.put("callerHintTone", "callerHintTone.wav"); //设置A拨打X号码时的通话前等待音
+// preVoice.put("calleeHintTone", "calleeHintTone.wav"); //设置B拨打X号码时的通话前等待音
+// json.put("preVoice", preVoice); //个性化通话前等待音
+
+ String result = HttpUtil.sendPost(appKey, appSecret, realUrl, json.toString());
+ log.info("Response is :" + result);
+ return result;
+ }
+
+ @Override
+ public void axbModifyNumber(String subscriptionId, String callerNum, String calleeNum) {
+ if (StringUtils.isBlank(subscriptionId)) {
+ log.info("axbModifyNumber set params error");
+ return;
+ }
+
+ // 必填,AXB模式绑定信息修改接口访问URI
+ String url = "/rest/caas/relationnumber/partners/v1.0";
+ String realUrl = buildOmpUrl(url);
+
+ // 封装JOSN请求
+ JSONObject json = new JSONObject();
+ json.put("subscriptionId", subscriptionId); // 绑定关系ID
+ if (StringUtils.isNotBlank(callerNum)) {
+ json.put("callerNum", callerNum); // 将A方修改为新的号码(手机或固话)
+ }
+ if (StringUtils.isNotBlank(calleeNum)) {
+ json.put("calleeNum", calleeNum); // 将B方修改为新的号码(手机或固话)
+ }
+
+ /**
+ * 选填,各参数要求请参考"AXB模式绑定信息修改接口"
+ */
+// json.put("callDirection", 0); //允许呼叫的方向
+// json.put("duration", 86400); //绑定关系保持时间
+// json.put("maxDuration", 90); //单次通话最长时间
+// json.put("lastMinVoice", "lastMinVoice.wav"); //通话最后一分钟提示音
+// json.put("privateSms", true); //是否支持短信功能
+// json.put("recordFlag", false); //是否通话录音
+// JSONObject preVoice = new JSONObject();
+// preVoice.put("callerHintTone", "callerHintTone.wav"); //设置A拨打X号码时的通话前等待音
+// preVoice.put("calleeHintTone", "calleeHintTone.wav"); //设置B拨打X号码时的通话前等待音
+// json.put("preVoice", preVoice); //个性化通话前等待音
+
+ String result = HttpUtil.sendPut(appKey, appSecret, realUrl, json.toString());
+ log.info("Response is :" + result);
+ }
+
+ @Override
+ public void axbUnbindNumber(String subscriptionId, String relationNum) {
+ if (StringUtils.isBlank(subscriptionId) && StringUtils.isBlank(relationNum)) {
+ log.info("axbUnbindNumber set params error");
+ return;
+ }
+
+ // 必填,AXB模式解绑接口访问URI
+ String url = "/rest/caas/relationnumber/partners/v1.0";
+ String realUrl = buildOmpUrl(url);
+
+ // 申明对象
+ Map<String, Object> map = new HashMap<String, Object>();
+ if (StringUtils.isNotBlank(subscriptionId)) {
+ map.put("subscriptionId", subscriptionId); // 绑定关系ID
+ } else {
+ map.put("relationNum", relationNum); // X号码(关系号码)
+ }
+
+ String result = HttpUtil.sendDelete(appKey, appSecret, realUrl, HttpUtil.map2UrlEncodeString(map));
+ log.info("Response is :" + result);
+ }
+
+ @Override
+ public void axbQueryBindRelation(String subscriptionId, String relationNum) {
+ if (StringUtils.isBlank(subscriptionId) && StringUtils.isBlank(relationNum)) {
+ log.info("axbQueryBindRelation set params error");
+ return;
+ }
+
+ // 必填,AXB模式绑定信息查询接口访问URI
+ String url = "/rest/caas/relationnumber/partners/v1.0";
+ String realUrl = buildOmpUrl(url);
+
+ // 申明对象
+ Map<String, Object> map = new HashMap<String, Object>();
+ if (StringUtils.isNotBlank(subscriptionId)) {
+ map.put("subscriptionId", subscriptionId); // 绑定关系ID
+ } else {
+ map.put("relationNum", relationNum); // X号码(关系号码)
+
+ /**
+ * 选填,各参数要求请参考"AXB模式绑定信息查询接口"
+ */
+// map.put("pageIndex", 1); //查询的分页索引,从1开始编号
+// map.put("pageSize", 20); //查询的分页大小,即每次查询返回多少条数据
+ }
+
+ String result = HttpUtil.sendGet(appKey, appSecret, realUrl, HttpUtil.map2UrlEncodeString(map));
+ log.info("Response is :" + result);
+ }
+
+ @Override
+ public String axbGetRecordDownloadLink(String recordDomain, String fileName) {
+ if (StringUtils.isBlank(recordDomain) || StringUtils.isBlank(fileName)) {
+ log.info("axbGetRecordDownloadLink set params error");
+ throw new ServiceException("axbGetRecordDownloadLink set params error");
+ }
+ // 必填,AXB模式获取录音文件下载地址接口访问URI
+ String url = "/rest/provision/voice/record/v1.0";
+ String realUrl = buildOmpUrl(url);
+
+ // 申明对象
+ Map<String, Object> map = new HashMap<String, Object>();
+ map.put("recordDomain", recordDomain); // 录音文件存储的服务器域名
+ map.put("fileName", fileName); // 录音文件名
+
+ String result = HttpUtil.sendGet(appKey, appSecret, realUrl, HttpUtil.map2UrlEncodeString(map));
+ log.info("Response is :" + result);
+ return result;
+ }
+
+ @Override
+ public void axbStopCall(String sessionid) {
+ if (StringUtils.isBlank(sessionid)) {
+ log.info("axbStopCall set params error");
+ return;
+ }
+
+ // 必填,AXB模式终止呼叫接口访问URI
+ String url = "/rest/httpsessions/callStop/v2.0";
+ String realUrl = buildOmpUrl(url);
+
+ // 封装JOSN请求
+ JSONObject json = new JSONObject();
+ json.put("sessionid", sessionid); // 呼叫会话ID
+ json.put("signal", "call_stop"); // 取值固定为"call_stop"
+
+ String result = HttpUtil.sendPost(appKey, appSecret, realUrl, json.toString());
+ log.info("Response is :" + result);
+ }
+}
\ No newline at end of file
diff --git a/ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/voice/util/AXBUtil.java b/ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/voice/util/AXBUtil.java
new file mode 100644
index 0000000..e8d79db
--- /dev/null
+++ b/ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/voice/util/AXBUtil.java
@@ -0,0 +1,61 @@
+package com.ruoyi.worker.voice.util;
+
+
+import com.ruoyi.worker.voice.service.IAXBInterfaceDemo;
+import com.ruoyi.worker.voice.service.impl.AXBInterfaceDemoImpl;
+
+public class AXBUtil {
+
+ /**
+ * 必填,请登录管理控制台,从"应用管理"页获取
+ */
+ private final static String OMPDOMAINNAME = "https://rtcpns.cn-north-1.myhuaweicloud.com"; // APP接入地址
+
+ /**
+ * AXB模式 绑定号码
+ * @param relationNum X 隐私号码 13154610294
+ * @param callerNum A
+ * @param calleeNum B
+ */
+ public static String axbBindNumber(String appKey,String appSecret,String relationNum, String callerNum, String calleeNum) {
+ IAXBInterfaceDemo axb = new AXBInterfaceDemoImpl(appKey, appSecret, OMPDOMAINNAME);
+ // 第一步: 号码绑定,即调用AXB模式绑定接口 93f4474b-3e0b-490d-bd1d-b6fd31b63c0b
+ return axb.axbBindNumber("+86"+relationNum, "+86"+callerNum, "+86"+calleeNum);
+ }
+
+ /**
+ * Get download link of the record file created in call | 获取录音文件下载地址
+ * @param recordDomain 录音文件存储的服务器域名
+ * @param fileName 录音文件名
+ */
+ public static String axbGetRecordDownloadLink(String appKey,String appSecret,String recordDomain, String fileName) {
+ IAXBInterfaceDemo axb = new AXBInterfaceDemoImpl(appKey, appSecret, OMPDOMAINNAME);
+ // 第三步: 用户通话结束,若设置录音,则商户可以获取录音文件下载地址,即调用获取录音文件下载地址接口
+ return axb.axbGetRecordDownloadLink(recordDomain, fileName);
+ }
+
+ /**
+ * Modify number a/b of the privacy relationship assigned by subscriptionId |
+ * 隐私号码AXB绑定信息修改
+ * @param subscriptionId 绑定关系ID
+ * @param callerNum 主叫号码
+ * @param calleeNum 被叫号码
+ */
+ public static void axbModifyNumber(String appKey,String appSecret,String subscriptionId, String callerNum, String calleeNum) {
+ IAXBInterfaceDemo axb = new AXBInterfaceDemoImpl(appKey, appSecret, OMPDOMAINNAME);
+ // 第四步: 根据业务需求,可更改绑定关系,即调用AXB模式绑定信息修改接口
+ axb.axbModifyNumber(subscriptionId, callerNum, calleeNum);
+ }
+
+ /**
+ * AXB模式 解绑号码
+ * @param subscriptionId 绑定号码后的唯一标识
+ * @param relationNum X 隐私号码 13154610294
+ */
+ public static void axbUnbindNumber(String appKey,String appSecret,String subscriptionId, String relationNum) {
+ IAXBInterfaceDemo axb = new AXBInterfaceDemoImpl(appKey, appSecret, OMPDOMAINNAME);
+ // 第五步: 隐私号码循环使用,商户可将绑定关系解绑,即调用AXB模式解绑接口
+ axb.axbUnbindNumber(subscriptionId, "+86"+relationNum);
+ }
+
+}
diff --git a/ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/voice/util/HttpUtil.java b/ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/voice/util/HttpUtil.java
new file mode 100644
index 0000000..b57fc43
--- /dev/null
+++ b/ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/voice/util/HttpUtil.java
@@ -0,0 +1,360 @@
+package com.ruoyi.worker.voice.util;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.StringUtils;
+
+import javax.net.ssl.*;
+import java.io.*;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.Map;
+
+/**
+ * HTTP协议传输工具类
+ */
+@Slf4j
+public class HttpUtil {
+
+ private static final int HTTP_STATUS_OK = 200;
+
+ /**
+ * 向指定 URL发送POST方法的请求
+ *
+ * @param appKey
+ * @param appSecret
+ * @param url
+ * @param jsonBody
+ * @return
+ */
+ public static String sendPost(String appKey, String appSecret, String url, String jsonBody) {
+ DataOutputStream 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;
+ }
+ };
+ try {
+ trustAllHttpsCertificates();
+ } catch (Exception e1) {
+ e1.printStackTrace();
+ }
+ try {
+ URL realUrl = new URL(url);
+ connection = (HttpsURLConnection) realUrl.openConnection();
+
+ connection.setHostnameVerifier(hv);
+ connection.setDoOutput(true);
+ connection.setDoInput(true);
+ connection.setRequestMethod("POST");
+ connection.setRequestProperty("Accept", "application/json");
+ connection.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
+ connection.setRequestProperty("Authorization",
+ "AKSK realm=\"SDP\",profile=\"UsernameToken\",type=\"Appkey\"");
+ connection.setRequestProperty("X-AKSK", StringUtil.buildAKSKHeader(appKey, appSecret));
+
+ log.info("RequestBody is : " + jsonBody);
+
+ connection.connect();
+ out = new DataOutputStream(connection.getOutputStream());
+ out.writeBytes(jsonBody);
+ out.flush();
+ out.close();
+
+ int status = connection.getResponseCode();
+ if (HTTP_STATUS_OK == status) {
+ is = connection.getInputStream();
+ } else {
+ is = connection.getErrorStream();
+ }
+ in = new BufferedReader(new InputStreamReader(is, "UTF-8"));
+ String line = "";
+ while ((line = in.readLine()) != null) {
+ result.append(line);
+ }
+ } catch (Exception e) {
+ log.info("Send Post request catch exception: " + e.toString());
+ }
+ finally {
+ IOUtils.closeQuietly(out);
+ IOUtils.closeQuietly(is);
+ IOUtils.closeQuietly(in);
+ if (null != connection) {
+ IOUtils.close(connection);
+ }
+ }
+ return result.toString();
+ }
+
+ /**
+ * 向指定 URL发送PUT方法的请求
+ *
+ * @param appKey
+ * @param appSecret
+ * @param url
+ * @param jsonBody
+ * @return
+ */
+ public static String sendPut(String appKey, String appSecret, String url, String jsonBody) {
+ DataOutputStream 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;
+ }
+ };
+ try {
+ trustAllHttpsCertificates();
+ } catch (Exception e1) {
+ e1.printStackTrace();
+ }
+ try {
+ URL realUrl = new URL(url);
+ connection = (HttpsURLConnection) realUrl.openConnection();
+
+ connection.setHostnameVerifier(hv);
+ connection.setDoOutput(true);
+ connection.setDoInput(true);
+ connection.setRequestMethod("PUT");
+ connection.setRequestProperty("Accept", "application/json");
+ connection.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
+ connection.setRequestProperty("Authorization",
+ "AKSK realm=\"SDP\",profile=\"UsernameToken\",type=\"Appkey\"");
+ connection.setRequestProperty("X-AKSK", StringUtil.buildAKSKHeader(appKey, appSecret));
+
+ log.info("RequestBody is : " + jsonBody);
+
+ connection.connect();
+ out = new DataOutputStream(connection.getOutputStream());
+ out.writeBytes(jsonBody);
+ out.flush();
+ out.close();
+
+ int status = connection.getResponseCode();
+ if (HTTP_STATUS_OK == status) {
+ is = connection.getInputStream();
+ } else {
+ is = connection.getErrorStream();
+ }
+ in = new BufferedReader(new InputStreamReader(is));
+ String line;
+ while ((line = in.readLine()) != null) {
+ result.append(line);
+ }
+ } catch (Exception e) {
+ log.info("Send Put request catch exception: " + e.toString());
+ e.printStackTrace();
+ }
+ finally {
+ IOUtils.closeQuietly(out);
+ IOUtils.closeQuietly(is);
+ IOUtils.closeQuietly(in);
+ if (null != connection) {
+ IOUtils.close(connection);
+ }
+ }
+ return result.toString();
+ }
+
+ /**
+ * 向指定 URL发送DELETE方法的请求
+ *
+ * @param appKey
+ * @param appSecret
+ * @param url
+ * @param params
+ * @return
+ */
+ public static String sendDelete(String appKey, String appSecret, String url, String params) {
+ 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;
+ }
+ };
+ try {
+ trustAllHttpsCertificates();
+ } catch (Exception e1) {
+ e1.printStackTrace();
+ }
+ try {
+ String realPath = url + (StringUtils.isEmpty(params) ? "" : "?" + params);
+ URL realUrl = new URL(realPath);
+ connection = (HttpsURLConnection) realUrl.openConnection();
+
+ connection.setHostnameVerifier(hv);
+ connection.setDoInput(true);
+ connection.setRequestMethod("DELETE");
+ connection.setRequestProperty("Accept", "application/json");
+ connection.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
+ connection.setRequestProperty("Authorization",
+ "AKSK realm=\"SDP\",profile=\"UsernameToken\",type=\"Appkey\"");
+ connection.setRequestProperty("X-AKSK", StringUtil.buildAKSKHeader(appKey, appSecret));
+
+ log.info("RequestBody is : " + params);
+
+ connection.connect();
+
+ int status = connection.getResponseCode();
+ if (HTTP_STATUS_OK == status) {
+ is = connection.getInputStream();
+ } else {
+ is = connection.getErrorStream();
+ }
+ in = new BufferedReader(new InputStreamReader(is));
+ String line;
+ while ((line = in.readLine()) != null) {
+ result.append(line);
+ }
+ } catch (Exception e) {
+ log.info("Send DELETE request catch exception: " + e.toString());
+ }
+ finally {
+ IOUtils.closeQuietly(is);
+ IOUtils.closeQuietly(in);
+ if (null != connection) {
+ IOUtils.close(connection);
+ }
+ }
+ return result.toString();
+ }
+
+ /**
+ * 向指定 URL发送GET方法的请求
+ *
+ * @param appKey
+ * @param appSecret
+ * @param url
+ * @param params
+ * @return
+ */
+ public static String sendGet(String appKey, String appSecret, String url, String params) {
+ 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;
+ }
+ };
+ try {
+ trustAllHttpsCertificates();
+ } catch (Exception e1) {
+ e1.printStackTrace();
+ }
+ try {
+ String realPath = url + (StringUtils.isEmpty(params) ? "" : "?" + params);
+ URL realUrl = new URL(realPath);
+ connection = (HttpsURLConnection) realUrl.openConnection();
+
+ connection.setHostnameVerifier(hv);
+ connection.setDoInput(true);
+ connection.setRequestMethod("GET");
+ connection.setRequestProperty("Accept", "application/json");
+ connection.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
+ connection.setRequestProperty("Authorization",
+ "AKSK realm=\"SDP\",profile=\"UsernameToken\",type=\"Appkey\"");
+ connection.setRequestProperty("X-AKSK", StringUtil.buildAKSKHeader(appKey, appSecret));
+ connection.setInstanceFollowRedirects(false); //设置本次连接不自动处理重定向
+
+ log.info("RequestBody is : " + params);
+
+ connection.connect();
+
+ int status = connection.getResponseCode();
+ if (301 == status) { //获取录音文件下载地址
+ return connection.getHeaderField("Location");
+ }else if (HTTP_STATUS_OK == status) { //查询绑定信息
+ is = connection.getInputStream();
+ } else { //获取错误码
+ is = connection.getErrorStream();
+ }
+ in = new BufferedReader(new InputStreamReader(is));
+ String line;
+ while ((line = in.readLine()) != null) {
+ result.append(line);
+ }
+ } catch (Exception e) {
+ log.info("Send GET request catch exception: " + e.toString());
+ }
+ finally {
+ IOUtils.closeQuietly(is);
+ IOUtils.closeQuietly(in);
+ if (null != connection) {
+ IOUtils.close(connection);
+ }
+ }
+ return result.toString();
+ }
+
+ /**
+ * 键值对转查询url
+ *
+ * @param map
+ * @return
+ */
+ public static String map2UrlEncodeString(Map<String, Object> map) {
+ if(null == map || map.isEmpty()) {
+ return "";
+ }
+ StringBuilder sb = new StringBuilder();
+ String temp = "";
+
+ for (String s : map.keySet()) {
+ try {
+ temp = URLEncoder.encode(String.valueOf(map.get(s)), "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ e.printStackTrace();
+ }
+ sb.append(s).append("=").append(temp).append("&");
+ }
+ return sb.deleteCharAt(sb.length() - 1).toString();
+ }
+
+ /**
+ * 忽略SSL证书校验
+ *
+ * @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());
+ }
+}
\ No newline at end of file
diff --git a/ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/voice/util/StringUtil.java b/ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/voice/util/StringUtil.java
new file mode 100644
index 0000000..2ef1991
--- /dev/null
+++ b/ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/voice/util/StringUtil.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright Notice:
+ * Copyright 1998-2008, Huawei Technologies Co., Ltd. ALL Rights Reserved.
+ *
+ * Warning: This computer software sourcecode is protected by copyright law
+ * and international treaties. Unauthorized reproduction or distribution
+ * of this sourcecode, or any portion of it, may result in severe civil and
+ * criminal penalties, and will be prosecuted to the maximum extent
+ * possible under the law.
+ */
+package com.ruoyi.worker.voice.util;
+
+//import java.nio.charset.Charset;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+import java.nio.charset.StandardCharsets;
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+public class StringUtil {
+ public static final String AKSK_HEADER_FORMAT = "UsernameToken Username=\"%s\",PasswordDigest=\"%s\",Nonce=\"%s\",Created=\"%s\"";
+
+ public static boolean strIsNullOrEmpty(String s) {
+ return (null == s || s.trim().length() < 1);
+ }
+
+ public static String buildAKSKHeader(String appKey, String appSecret) throws Exception {
+ if (StringUtil.strIsNullOrEmpty(appKey) || StringUtil.strIsNullOrEmpty(appSecret)) {
+ return null;
+ }
+
+ SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
+ format.setTimeZone(TimeZone.getTimeZone("UTC"));
+ Calendar calendar = Calendar.getInstance();
+ String time = format.format(calendar.getTime());
+ String stNonce = UUID.randomUUID().toString().replace("-", "").toUpperCase(Locale.ROOT);
+ String str = stNonce + time;
+ Mac mac = Mac.getInstance("HmacSHA256");
+ mac.init(new SecretKeySpec(appSecret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
+ byte[] authBytes = mac.doFinal(str.getBytes(StandardCharsets.UTF_8));
+ String passwordDigestBase64Str = encodeBase64(authBytes);
+ return String.format(AKSK_HEADER_FORMAT, appKey, passwordDigestBase64Str, stNonce, time);
+ }
+
+ private static String encodeBase64(byte[] bytes) {
+ if (bytes.length == 0) {
+ return null;
+ } else {
+ return new String(Base64.getEncoder().encode(bytes), StandardCharsets.UTF_8);
+ }
+ }
+}
\ No newline at end of file
--
Gitblit v1.7.1