1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
package com.smartor.service.impl;
 
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson;
import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.core.service.IConfigService;
import com.ruoyi.common.dx.MessageSend;
import com.ruoyi.common.exception.base.BaseException;
import com.ruoyi.common.utils.*;
import com.ruoyi.common.utils.http.HttpUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.system.domain.SysConfig;
import com.ruoyi.system.mapper.SysConfigMapper;
import com.ruoyi.system.service.ISysConfigService;
import com.smartor.common.FtpService;
import com.smartor.common.MtSubmitSmUtil;
import com.smartor.common.ScheduleSubtaskBuilder;
import com.smartor.config.PhoneUtils;
import com.smartor.domain.DTO.ServiceSubtaskDetailDTO;
import com.smartor.domain.*;
import com.smartor.domain.VO.HeLibraryCountVO;
import com.smartor.domain.VO.ServiceSubtaskCotinueCountVO;
import com.smartor.domain.VO.TransferDeptVO;
import com.smartor.domain.entity.ServiceSubtaskEntity;
import com.smartor.mapper.*;
import com.smartor.service.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestBody;
 
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
 
/**
 * 单一任务(随访)Service业务层处理
 *
 * @author ruoyi
 * @date 2024-02-02
 */
@Slf4j
@Service
public class ServicPhoneServiceImpl implements IServicePhoneService {
    @Value("${shurl}")
    private String shurl;
 
    @Value("${shversion}")
    private String shversion;
 
    /**
     * 新增外呼实例配置
     *
     * @param request 外呼实例配置请求对象
     * @return 外呼实例配置结果
     * @throws BaseException 接口调用失败或数据解析异常时抛出
     */
    @Override
    public OutboundInstanceResVO addOutboundInstance(OutboundConfigAddReqVO request) {
        // 1. 参数校验
        validateRequest(request);
 
        // 2. 发送 HTTP 请求
        String responseJson = sendHttpPostRequest(shurl + shversion + "/config/addOutboundInstance", request);
 
        // 3. 解析响应并返回结果
        return parseResponse(responseJson);
    }
 
    /**
     * 参数校验
     */
    private void validateRequest(OutboundConfigAddReqVO request) {
        if (request == null) {
            throw new BaseException("请求参数不能为空");
        }
 
        // 必传参数校验
        assertNotBlank(request.getName(), "项目名称不能为空");
        assertNotBlank(request.getAgent(), "坐席号不能为空");
        assertNotBlank(request.getTenantId(), "资源组号不能为空");
        assertNotBlank(request.getCallType(), "呼叫类型不能为空");
        assertNotBlank(request.getActionID(), "actionID 不能为空");
 
        // 条件必填校验:isVgc=0 时,queueId 和 ratio 为必填
        if ("0".equals(request.getIsVgc())) {
            assertNotBlank(request.getQueueId(), "isVgc=0 时,执行坐席组 ID 不能为空");
            assertNotBlank(request.getRatio(), "isVgc=0 时,外呼速率不能为空");
        }
        // 条件必填校验:isVgc=1 时,ivrId 为必填
        else if ("1".equals(request.getIsVgc())) {
            assertNotBlank(request.getIvrId(), "isVgc=1 时,IVR 流程 ID 不能为空");
        }
    }
 
    /**
     * 发送 POST HTTP 请求
     *
     * @param url     API 地址
     * @param request 请求对象
     * @return 服务器响应 JSON 字符串
     */
    private String sendHttpPostRequest(String url, Object request) {
        log.info("开始调用外呼实例配置接口,URL: {}", url);
 
        Map<String, String> headers = buildHeaders();
        String requestBody = new Gson().toJson(request);
 
        String response = HttpUtils.sendPostByHeader(url, requestBody, headers);
 
        if (StringUtils.isEmpty(response)) {
            log.error("外呼实例配置接口调用失败,未返回有效数据,URL: {}", url);
            throw new BaseException("接口调用失败,未返回有效数据");
        }
 
        log.info("外呼实例配置接口调用成功,响应:{}", response);
        return response;
    }
 
    /**
     * 构建 HTTP 请求头
     */
    private Map<String, String> buildHeaders() {
        Map<String, String> headers = new HashMap<>(1);
        headers.put("Content-Type", "application/json; charset=UTF-8");
        return headers;
    }
 
    /**
     * 解析 HTTP 响应并转换为 OutboundInstanceResVO
     *
     * @param responseJson 响应 JSON 字符串
     * @return 外呼实例配置响应对象
     * @throws BaseException 当响应码不为 200 或响应数据解析失败时抛出
     */
    private OutboundInstanceResVO parseResponse(String responseJson) {
        try {
            JSONObject response = JSON.parseObject(responseJson);
 
            // 检查响应码
            Integer code = response.getInteger("code");
            if (!"200".equals(code.toString())) {
                String message = response.getString("message");
                log.error("外呼实例配置接口返回失败,code: {}, message: {}", code, message);
                throw new BaseException("新增外呼实例配置失败:" + message);
            }
 
            // 解析 data 字段
            JSONObject data = response.getJSONObject("data");
            if (data == null || data.isEmpty()) {
                throw new BaseException("接口返回数据为空");
            }
 
            return data.toJavaObject(OutboundInstanceResVO.class);
 
        } catch (BaseException e) {
            throw e;
        } catch (Exception e) {
            log.error("解析外呼实例配置响应异常,response: {}", responseJson, e);
            throw new BaseException("解析响应数据异常:" + e.getMessage());
        }
    }
 
    /**
     * 断言字符串非空工具方法
     *
     * @param value   待检查的值
     * @param message 断言失败时的错误消息
     * @throws BaseException 当值为空时抛出
     */
    private void assertNotBlank(String value, String message) {
        if (StringUtils.isBlank(value)) {
            throw new BaseException(message);
        }
    }
}