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