diff --git a/.gitignore b/.gitignore
index 6f161d7..d102438 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,7 +4,6 @@ yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
-
/target/
!.mvn/wrapper/maven-wrapper.jar
@@ -43,4 +42,5 @@ yus-water/logs/
*.vm
**/target/**
-/img
\ No newline at end of file
+/img
+runtime-lib/
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 35b33b5..aec9730 100644
--- a/pom.xml
+++ b/pom.xml
@@ -223,23 +223,51 @@
4.6.0
+
+ com.squareup.okhttp3
+ okhttp
+ 4.12.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
- org.springframework.boot
- spring-boot-maven-plugin
- 2.7.5
+ org.apache.maven.plugins
+ maven-dependency-plugin
+ 3.5.0
+ copy-dependencies
- repackage
+ copy-dependencies
+
+ true
+ ${project.basedir}/runtime-lib
+ false
+ false
+ true
+
-
\ No newline at end of file
diff --git a/src/main/java/com/whdc/FxkhTxlApiApplication.java b/src/main/java/com/whdc/FxkhTxlApiApplication.java
index 9d3ffe9..0e10d52 100644
--- a/src/main/java/com/whdc/FxkhTxlApiApplication.java
+++ b/src/main/java/com/whdc/FxkhTxlApiApplication.java
@@ -8,8 +8,13 @@ import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.scheduling.annotation.EnableScheduling;
+import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+import javax.script.ScriptException;
+
/**
* @author 李赛
* @date 2022-06-26 0:17
@@ -21,6 +26,7 @@ import org.springframework.web.servlet.config.annotation.EnableWebMvc;
@EnableScheduling
@SpringBootApplication
@MapperScan("com.whdc.mapper")
+@EnableTransactionManagement
public class FxkhTxlApiApplication {
public static void main(String[] args) {
diff --git a/src/main/java/com/whdc/component/AutoCallScheduled.java b/src/main/java/com/whdc/component/AutoCallScheduled.java
new file mode 100644
index 0000000..4d30481
--- /dev/null
+++ b/src/main/java/com/whdc/component/AutoCallScheduled.java
@@ -0,0 +1,59 @@
+package com.whdc.component;
+
+import com.whdc.service.AutoCallTaskService;
+import com.whdc.utils.AutoCallHelper;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Profile;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.PostConstruct;
+
+/**
+ * @author lyf
+ * @since 2025-06-20
+ */
+@Component
+@Slf4j
+@Profile("deprecated")
+public class AutoCallScheduled {
+
+ @Autowired
+ private AutoCallTaskService autoCallTaskService;
+ @Autowired
+ private AutoCallHelper autoCallHelper;
+
+ @PostConstruct
+ public void getToken() throws Exception {
+ autoCallHelper.getToken();
+ }
+
+ @Scheduled(cron = "*/3 * * * * ?")
+ public void step1() {
+ if (autoCallTaskService.isScheduled()) {
+ autoCallTaskService.step1GenerateWarnCallMap();
+ }
+ }
+
+ @Scheduled(cron = "*/3 * * * * ?")
+ public void step2() {
+ if (autoCallTaskService.isScheduled()) {
+ autoCallTaskService.step2GenerateAutoCallByWcm();
+ }
+ }
+
+ @Scheduled(cron = "*/10 * * * * ?")
+ public void step3() {
+ if (autoCallTaskService.isScheduled()) {
+ autoCallTaskService.step3UploadCallTask();
+ }
+ }
+
+ @Scheduled(cron = "*/10 * * * * ?")
+ public void step4() {
+ if (autoCallTaskService.isScheduled()) {
+ autoCallTaskService.step4QueryTaskResult();
+ }
+ }
+}
diff --git a/src/main/java/com/whdc/component/AutoCallTaskScheduled.java b/src/main/java/com/whdc/component/AutoCallTaskScheduled.java
new file mode 100644
index 0000000..801814d
--- /dev/null
+++ b/src/main/java/com/whdc/component/AutoCallTaskScheduled.java
@@ -0,0 +1,95 @@
+package com.whdc.component;
+
+import com.whdc.mapper.AutoCallConfigMapper;
+import com.whdc.model.entity.AutoCallPerson;
+import com.whdc.service.AutoCallTaskService2;
+import com.whdc.utils.AutoCallHelper;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.context.event.ApplicationReadyEvent;
+import org.springframework.context.annotation.Profile;
+import org.springframework.context.event.EventListener;
+import org.springframework.scheduling.TaskScheduler;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.stream.Collectors;
+
+/**
+ * @author lyf
+ * @since 2025-06-20
+ */
+@Component
+@Slf4j
+@Profile("default")
+public class AutoCallTaskScheduled {
+
+ @Autowired
+ private AutoCallTaskService2 autoCallTaskService;
+ @Autowired
+ private AutoCallConfigMapper configMapper;
+ @Autowired
+ private AutoCallHelper autoCallHelper;
+ @Autowired
+ private TaskScheduler taskScheduler;
+
+ private AtomicBoolean initialized = new AtomicBoolean(false);
+
+ @EventListener(ApplicationReadyEvent.class)
+ public void initialize() throws Exception {
+ autoCallHelper.getToken();
+ initialized.set(true);
+ log.info("AutoCallTaskScheduled初始化完成");
+ }
+
+ @Scheduled(cron = "*/3 * * * * ?")
+ public void generateLoop() {
+ if (configMapper.isScheduled()) {
+ autoCallTaskService.step1GenerateTask();
+ }
+ }
+
+ @Scheduled(cron = "*/3 * * * * ?")
+ public void callLoop() {
+ if (!initialized.get()) {
+ return;
+ }
+ if (!configMapper.isScheduled()) {
+ return;
+ }
+ log.info("AutoCallTaskScheduled callLoop");
+ List personList = autoCallTaskService.step2GetOneUnUploadedPerson();
+ log.info("AutoCallTaskScheduled {}个外呼人, {}", personList.size(), personList.stream().map(AutoCallPerson::getUploadCustName).collect(Collectors.toList()));
+ try {
+ for (AutoCallPerson person : personList) {
+ autoCallTaskService.step3UploadAICCTask(person);
+
+ int pendingDuration = 60 * 1000 * 2;
+ int loopGap = 1000;
+ boolean success = false;
+ while (pendingDuration > 0) {
+ try {
+ Thread.sleep(loopGap);
+ } catch (InterruptedException ignore) {
+ Thread.currentThread().interrupt();
+ return;
+ }
+ pendingDuration -= loopGap;
+
+ success = autoCallTaskService.step4QueryAICCTaskResult(person);
+ if (success) break;
+ }
+
+ if (!success) {
+ autoCallTaskService.markPersonDetailQueryTimeout(person);
+ }
+ }
+ } catch (Exception e) {
+ log.error("AutoCallTaskScheduled callLoop error", e);
+ }
+ }
+
+}
diff --git a/src/main/java/com/whdc/component/MyPostConstruct.java b/src/main/java/com/whdc/component/MyPostConstruct.java
index 9422c67..f98b8b6 100644
--- a/src/main/java/com/whdc/component/MyPostConstruct.java
+++ b/src/main/java/com/whdc/component/MyPostConstruct.java
@@ -19,6 +19,7 @@ import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Profile;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
@@ -40,6 +41,7 @@ import static com.whdc.controller.QXWarnController.QX_TEMP_REDIS_KEY;
*/
@Component
@Slf4j
+@Profile("default")
public class MyPostConstruct {
@Autowired
diff --git a/src/main/java/com/whdc/config/InterceptorConfig.java b/src/main/java/com/whdc/config/InterceptorConfig.java
index d3a9ff7..2c03bc8 100644
--- a/src/main/java/com/whdc/config/InterceptorConfig.java
+++ b/src/main/java/com/whdc/config/InterceptorConfig.java
@@ -1,5 +1,7 @@
package com.whdc.config;
+import okhttp3.OkHttpClient;
+import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
@@ -13,7 +15,11 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@EnableWebMvc
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
-
+ @Bean
+ public OkHttpClient okHttpClient() {
+ return new OkHttpClient.Builder()
+ .build();
+ }
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
diff --git a/src/main/java/com/whdc/config/MybatisInterceptor.java b/src/main/java/com/whdc/config/MybatisInterceptor.java
index d345fda..07c3981 100644
--- a/src/main/java/com/whdc/config/MybatisInterceptor.java
+++ b/src/main/java/com/whdc/config/MybatisInterceptor.java
@@ -42,15 +42,15 @@ public class MybatisInterceptor implements Interceptor {
continue;
}
- if (field.getType().equals(String.class)) {
- field.setAccessible(true);
- Object o = field.get(parameter);
- field.setAccessible(false);
- String newVal = o == null ? "" : String.valueOf(o).trim();
- field.setAccessible(true);
- field.set(parameter, newVal);
- field.setAccessible(false);
- }
+// if (field.getType().equals(String.class)) {
+// field.setAccessible(true);
+// Object o = field.get(parameter);
+// field.setAccessible(false);
+// String newVal = o == null ? "" : String.valueOf(o).trim();
+// field.setAccessible(true);
+// field.set(parameter, newVal);
+// field.setAccessible(false);
+// }
// 注入创建时间
if ("createtime".equals(field.getName()) || ("createTime".equals(field.getName()))) {
diff --git a/src/main/java/com/whdc/controller/AutoCallController.java b/src/main/java/com/whdc/controller/AutoCallController.java
new file mode 100644
index 0000000..64e1f1e
--- /dev/null
+++ b/src/main/java/com/whdc/controller/AutoCallController.java
@@ -0,0 +1,91 @@
+package com.whdc.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.whdc.model.dto.AutoCallDto;
+import com.whdc.model.entity.AutoCallPerson;
+import com.whdc.model.entity.AutoCallTask;
+import com.whdc.model.entity.WarnCallMap;
+import com.whdc.service.AutoCallApiService;
+import com.whdc.service.AutoCallTaskService;
+import com.whdc.service.AutoCallTaskService2;
+import com.whdc.utils.ResultJson;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.text.ParseException;
+import java.util.List;
+
+/**
+ * @author lyf
+ * @since 2025-06-17
+ */
+@RestController
+@RequestMapping("/autocall")
+public class AutoCallController {
+ @Autowired
+ private AutoCallApiService autoCallApiService;
+ @Autowired
+ private AutoCallTaskService autoCallTaskService;
+ @Autowired
+ private AutoCallTaskService2 autoCallTaskService2;
+
+ @GetMapping("/doCallTest")
+ public ResultJson doCallTest() throws ParseException {
+ autoCallTaskService.generateFakeCall();
+ return ResultJson.ok("resp");
+ }
+
+ @GetMapping("/doGenerateTest2")
+ public ResultJson doGenerateTest2() throws ParseException {
+ autoCallTaskService2.generateFakeCall();
+ return ResultJson.ok("resp");
+ }
+
+ @GetMapping("/doCallTest2")
+ public ResultJson> doCallTest2() throws ParseException {
+ List personList = autoCallTaskService2.doCallTest();
+ return ResultJson.ok(personList);
+ }
+
+ @GetMapping("/getToken")
+ public ResultJson getToken() {
+ return ResultJson.ok(autoCallTaskService.getToken());
+ }
+
+ @GetMapping("/queryTaskResult")
+ public ResultJson queryTaskResult(@RequestParam("requestId") String requestId, @RequestParam("custId") String custId) {
+ return ResultJson.ok(autoCallTaskService.queryTaskResult(requestId, custId));
+ }
+
+ @PostMapping("/page")
+ public ResultJson> newList(@RequestBody AutoCallDto dto) {
+ return ResultJson.ok(autoCallApiService.page(dto));
+ }
+
+ @PostMapping("/page2")
+ public ResultJson> page2(@RequestBody AutoCallDto dto) {
+ return ResultJson.ok(autoCallApiService.page2(dto));
+ }
+
+ @GetMapping("/listCallIsNotPut")
+ public ResultJson> listCallIsNotPut() {
+ return ResultJson.ok(autoCallApiService.listCallIsNotPut());
+ }
+
+ @GetMapping("/isEnable")
+ public ResultJson isEnable() {
+ return ResultJson.ok(autoCallTaskService.isEnable());
+ }
+
+ @GetMapping("/setEnable")
+ public ResultJson setEnable(@RequestParam("enable") Boolean enable) {
+ autoCallTaskService.setEnable(enable);
+ return ResultJson.ok(true);
+ }
+
+ @GetMapping("/setCallIsPut")
+ public ResultJson setCallIsPut(@RequestParam("taskId") Integer taskId) {
+ autoCallTaskService.setCallIsPut(taskId);
+ return ResultJson.ok(true);
+ }
+}
diff --git a/src/main/java/com/whdc/controller/QXWarnController.java b/src/main/java/com/whdc/controller/QXWarnController.java
index 3eb6302..063fe58 100644
--- a/src/main/java/com/whdc/controller/QXWarnController.java
+++ b/src/main/java/com/whdc/controller/QXWarnController.java
@@ -7,7 +7,7 @@ import com.whdc.exception.MyException;
import com.whdc.model.dto.ApiDto;
import com.whdc.model.dto.GroupWarningDto;
import com.whdc.model.dto.WarnDppleDto;
-import com.whdc.model.entity.AddressBookOld;
+import com.whdc.model.entity.WarningResponder;
import com.whdc.model.entity.QXWarning;
import com.whdc.model.entity.WarnMsgFB;
import com.whdc.model.vo.*;
@@ -54,7 +54,7 @@ public class QXWarnController {
@Autowired
private IAddressBookService addressBookService;
@Autowired
- private IAddressBookOldService addressBookOldService;
+ private IWarningResponderService warningResponderService;
@Autowired
private IAdinfoService adinfoService;
@Autowired
@@ -259,9 +259,9 @@ public class QXWarnController {
// String adcd = adinfoService.getAdcdByAdnm2(cnnm2);//根据区县的名字找到对应的adcd
// System.out.println(adcd);
if (StringUtils.isNotBlank(cnnm2)) {
- List dpples = addressBookOldService.getListByAdnm2(cnnm2);
+ List dpples = warningResponderService.getListByAdnm2(cnnm2);
System.out.println("dpple个数:" + dpples.size());
- for (AddressBookOld dpple : dpples) {
+ for (WarningResponder dpple : dpples) {
dpple.decryptPhone();
WarnDppleVO warndpple = new WarnDppleVO();
warndpple.setCtnm(dto.getCtnm());
@@ -269,6 +269,7 @@ public class QXWarnController {
warndpple.setName(dpple.getName());
warndpple.setPosition(dpple.getPosition());
warndpple.setPhone(dpple.getPhone());
+ warndpple.setLevel(dpple.getLevel());
warnDpples.add(warndpple);
}
} else {
diff --git a/src/main/java/com/whdc/controller/WarnMsgFBController.java b/src/main/java/com/whdc/controller/WarnMsgFBController.java
index c37aa40..07d28a9 100644
--- a/src/main/java/com/whdc/controller/WarnMsgFBController.java
+++ b/src/main/java/com/whdc/controller/WarnMsgFBController.java
@@ -1,11 +1,11 @@
package com.whdc.controller;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
-import com.whdc.model.entity.AddressBookOld;
+import com.whdc.model.entity.WarningResponder;
import com.whdc.model.entity.QXWarning;
import com.whdc.model.entity.WarnMsgFB;
import com.whdc.model.vo.ExcelOldDataVo;
-import com.whdc.service.IAddressBookOldService;
+import com.whdc.service.IWarningResponderService;
import com.whdc.service.IQXWarningService;
import com.whdc.service.IWarnMsgFBService;
import com.whdc.utils.ExcelCommon;
@@ -37,7 +37,7 @@ public class WarnMsgFBController {
private IQXWarningService qxService;
@Autowired
- private IAddressBookOldService oldService;
+ private IWarningResponderService oldService;
//增
@ApiOperation(value = "新增")
@PostMapping(value = "/add")
@@ -76,8 +76,8 @@ public class WarnMsgFBController {
List qxWarns = ExcelCommon.importExcel(file,1, 0, 1, QXWarning.class);
Map> qxByID = qxWarns.stream().collect(Collectors.groupingBy(QXWarning::getEffectId, Collectors.toList()));
- List olds = oldService.list();
- Map> txlByName = olds.stream().collect(Collectors.groupingBy(AddressBookOld::getName, Collectors.toList()));
+ List olds = oldService.list();
+ Map> txlByName = olds.stream().collect(Collectors.groupingBy(WarningResponder::getName, Collectors.toList()));
warnMsgs.forEach(o -> {
List qxWarnings = qxByID.get(o.getEffectId());
@@ -87,7 +87,7 @@ public class WarnMsgFBController {
.setPublishTime(qxWarning.getPublishTime())
.setWarnid(qxWarning.getWarnid());
String name = o.getCalledPerson();
- List txl = txlByName.get(name);
+ List txl = txlByName.get(name);
if (CollectionUtils.isNotEmpty(txl)){
o.setCalledPhone(txl.get(0).getPhone());
}else{
diff --git a/src/main/java/com/whdc/controller/AddressBookOldController.java b/src/main/java/com/whdc/controller/WarningResponderController.java
similarity index 66%
rename from src/main/java/com/whdc/controller/AddressBookOldController.java
rename to src/main/java/com/whdc/controller/WarningResponderController.java
index 55124d0..0e67cf6 100644
--- a/src/main/java/com/whdc/controller/AddressBookOldController.java
+++ b/src/main/java/com/whdc/controller/WarningResponderController.java
@@ -4,11 +4,11 @@ import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.whdc.exception.MyException;
-import com.whdc.model.dto.AddressBookOldDto;
-import com.whdc.model.entity.AddressBookOld;
+import com.whdc.model.dto.WarningResponderDto;
+import com.whdc.model.entity.WarningResponder;
import com.whdc.model.vo.ExcelDataVo;
import com.whdc.model.vo.ExcelOldDataVo;
-import com.whdc.service.IAddressBookOldService;
+import com.whdc.service.IWarningResponderService;
import com.whdc.utils.ExcelCommon;
import com.whdc.utils.ResultJson;
import io.swagger.annotations.Api;
@@ -44,12 +44,12 @@ import static com.whdc.model.MyConstant.REDIS_KEY;
@Slf4j
@Api(tags = "web通讯录 - Controller")
@RestController
-@RequestMapping("/addressbookOld")
+@RequestMapping({"/addressbookOld", "/warningResponder"})
@Transactional
-public class AddressBookOldController {
+public class WarningResponderController {
@Autowired
- private IAddressBookOldService service;
+ private IWarningResponderService service;
public static final String ADDRESS_BOOK_REDIS_KEY = REDIS_KEY + "addressbookOld";
public static final String ADDRESS_BOOK_TEMP_REDIS_KEY = ADDRESS_BOOK_REDIS_KEY + "temp:";
@@ -62,23 +62,23 @@ public class AddressBookOldController {
@ApiOperation(value = "查询所有联系人")
@PostMapping(value = "find")
@Cacheable(value = ADDRESS_BOOK_REDIS_KEY, key = "#root.method.name+':'+#dto.toString()")
- public ResultJson> find(@RequestBody AddressBookOldDto dto) {
+ public ResultJson> find(@RequestBody WarningResponderDto dto) {
- LambdaQueryChainWrapper query = service.lambdaQuery();
+ LambdaQueryChainWrapper query = service.lambdaQuery();
String name = dto.getName();
if (StringUtils.isNotBlank(name)){
- query.like(AddressBookOld::getName, name);
+ query.like(WarningResponder::getName, name);
}
String phone = dto.getPhone();
if (StringUtils.isNotBlank(phone)){
- query.like(AddressBookOld::getPhone, phone);
+ query.like(WarningResponder::getPhone, phone);
}
- List data = query.list();
+ List data = query.list();
- data = data.stream().map(AddressBookOld::decryptPhone).collect(Collectors.toList());
+ data = data.stream().map(WarningResponder::decryptPhone).collect(Collectors.toList());
return ResultJson.ok(data);
@@ -89,21 +89,22 @@ public class AddressBookOldController {
@GetMapping(value = "downloadExcel")
public void downloadExcel( HttpServletResponse response) {
- List list = service.lambdaQuery()
+ List list = service.lambdaQuery()
.list();
- for (AddressBookOld o : list) {
+ for (WarningResponder o : list) {
o.decryptPhone();
}
+ list.stream().forEach(vo -> vo.setLevelLabel(dictMappingReverse(vo.getLevel())));
ExcelCommon.exportExcel(list,
- null, "防汛抗旱通讯录", AddressBookOld.class, "湖北省防汛抗旱通信录模版" + LocalDateTime.now() + ".xlsx",
- response);
+ null, "防汛抗旱通讯录", WarningResponder.class, "湖北省防汛抗旱通信录模版" + LocalDateTime.now() + ".xlsx",
+ response);
}
@ApiOperation(value = "解析文件数据")
@PostMapping(value = "getExcelData")
public ResultJson getExcelData(MultipartFile file) {
- List appends = ExcelCommon.importExcel(file, 0, 1, AddressBookOld.class);
+ List appends = ExcelCommon.importExcel(file, 0, 1, WarningResponder.class);
ExcelOldDataVo excelDataVo = new ExcelOldDataVo();
if (CollectionUtils.isNotEmpty(appends)){
@@ -129,7 +130,7 @@ public class AddressBookOldController {
if (StringUtils.isBlank(s)){
throw new MyException("文件已过期");
}
- List appends = JSON.parseArray(s, AddressBookOld.class);
+ List appends = JSON.parseArray(s, WarningResponder.class);
if (CollectionUtils.isEmpty(appends)) {
throw new MyException("文件数据为空");
@@ -148,7 +149,7 @@ public class AddressBookOldController {
// 新增创建时间
Date date = new Date();
- appends = appends.stream().map(vo -> vo.setCreateTime(date).encryptPhone())
+ appends = appends.stream().map(vo -> vo.setCreateTime(date).setLevel(dictMapping(vo.getLevelLabel())).encryptPhone())
.collect(Collectors.toList());
if (!service.saveBatch(appends)) {
@@ -158,4 +159,39 @@ public class AddressBookOldController {
return ResultJson.ok("数据新增成功");
}
+ private Integer dictMapping(String label) {
+ if (label == null) {
+ return null;
+ }
+ switch (label) {
+ case "分管副市(县)长":
+ return 1;
+ case "常务副市(县)长":
+ return 2;
+ case "市(县)长":
+ return 3;
+ case "书记":
+ return 4;
+ default:
+ return null;
+ }
+ }
+
+ private String dictMappingReverse(Integer level) {
+ if (level == null) {
+ return null;
+ }
+ switch (level) {
+ case 1:
+ return "分管副市(县)长";
+ case 2:
+ return "常务副市(县)长";
+ case 3:
+ return "市(县)长";
+ case 4:
+ return "书记";
+ default:
+ return null;
+ }
+ }
}
diff --git a/src/main/java/com/whdc/mapper/AddressBookOldMapper.java b/src/main/java/com/whdc/mapper/AddressBookOldMapper.java
deleted file mode 100644
index 1ff2c26..0000000
--- a/src/main/java/com/whdc/mapper/AddressBookOldMapper.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.whdc.mapper;
-
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import com.whdc.model.entity.AddressBookOld;
-import org.apache.ibatis.annotations.Param;
-
-import java.util.List;
-
-/**
- * @author xusan
- * @date 2024-05-11
- */
-public interface AddressBookOldMapper extends BaseMapper {
-
-
- List getListByAdnm2(@Param("adnm")String adnm);
-// List getListByAdnm(@Param("adnm") String adnm);
-}
\ No newline at end of file
diff --git a/src/main/java/com/whdc/mapper/AutoCallConfigMapper.java b/src/main/java/com/whdc/mapper/AutoCallConfigMapper.java
new file mode 100644
index 0000000..e13dcf1
--- /dev/null
+++ b/src/main/java/com/whdc/mapper/AutoCallConfigMapper.java
@@ -0,0 +1,29 @@
+package com.whdc.mapper;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.whdc.model.entity.AutoCallConfig;
+
+/**
+ * @author lyf
+ * @since 2025-06-19
+ */
+public interface AutoCallConfigMapper extends BaseMapper {
+ default boolean isEnable() {
+ AutoCallConfig config = selectOne(
+ new QueryWrapper()
+ .eq("key", "enable")
+ .last("limit 1")
+ );
+ return config != null && "1".equals(config.getValue());
+ }
+
+ default boolean isScheduled() {
+ AutoCallConfig config = selectOne(
+ new QueryWrapper()
+ .eq("key", "schedule")
+ .last("limit 1")
+ );
+ return config != null && "1".equals(config.getValue());
+ }
+}
diff --git a/src/main/java/com/whdc/mapper/AutoCallMapper.java b/src/main/java/com/whdc/mapper/AutoCallMapper.java
new file mode 100644
index 0000000..5519b45
--- /dev/null
+++ b/src/main/java/com/whdc/mapper/AutoCallMapper.java
@@ -0,0 +1,25 @@
+package com.whdc.mapper;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.whdc.model.entity.AutoCall;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @author lyf
+ * @since 2025-06-14
+ */
+public interface AutoCallMapper extends BaseMapper {
+ List getListByLevelAndStatus(@Param("level") Integer level, @Param("status") String status, @Param("called") Boolean called, @Param("stm") Date stm, @Param("etm") Date etm);
+
+ default List listByWcmIdAndCnnmOrderByLevelAsc(Integer wcmId, String cnnm) {
+ return selectList(
+ new QueryWrapper()
+ .eq("wcm_id", wcmId)
+ .orderByAsc("level")
+ );
+ }
+}
diff --git a/src/main/java/com/whdc/mapper/AutoCallPersonMapper.java b/src/main/java/com/whdc/mapper/AutoCallPersonMapper.java
new file mode 100644
index 0000000..dd766fb
--- /dev/null
+++ b/src/main/java/com/whdc/mapper/AutoCallPersonMapper.java
@@ -0,0 +1,42 @@
+package com.whdc.mapper;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.whdc.model.entity.AutoCallPerson;
+
+import java.util.List;
+
+/**
+ * @author lyf
+ * @since 2025-07-08
+ */
+public interface AutoCallPersonMapper extends BaseMapper {
+ default List listUnUploaded() {
+ return selectList(
+ new QueryWrapper()
+ .eq("status", AutoCallPerson.STATUS_DEFAULT)
+ .orderByAsc("id")
+ );
+ }
+
+ default boolean isAnySuccess(Integer taskId) {
+ return selectCount(
+ new QueryWrapper()
+ .eq("task_id", taskId)
+ .eq("status", AutoCallPerson.STATUS_PUT)
+ ) > 0;
+ }
+
+ default boolean isAllFail(Integer taskId) {
+ long personCnt = selectCount(
+ new QueryWrapper()
+ .eq("task_id", taskId)
+ );
+ long failCnt = selectCount(
+ new QueryWrapper()
+ .eq("task_id", taskId)
+ .in("status", AutoCallPerson.STATUS_CALLED, AutoCallPerson.STATUS_CANCELLED)
+ );
+ return personCnt == failCnt;
+ }
+}
diff --git a/src/main/java/com/whdc/mapper/AutoCallTaskMapper.java b/src/main/java/com/whdc/mapper/AutoCallTaskMapper.java
new file mode 100644
index 0000000..f078317
--- /dev/null
+++ b/src/main/java/com/whdc/mapper/AutoCallTaskMapper.java
@@ -0,0 +1,28 @@
+package com.whdc.mapper;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.whdc.model.entity.AutoCallTask;
+import com.whdc.model.entity.QXWarning;
+import org.apache.ibatis.annotations.Select;
+
+import java.util.List;
+
+/**
+ * @author lyf
+ * @since 2025-07-08
+ */
+public interface AutoCallTaskMapper extends BaseMapper {
+ List listWarnsThatNotGeneratedTask();
+
+ default List listShouldGenerate() {
+ return selectList(
+ new QueryWrapper()
+ .eq("status", AutoCallTask.STATUS_SHOULD_GENERATE)
+ .orderByAsc("id")
+ );
+ }
+
+ @Select("update auto_call_task set status = #{status} where id = #{taskId}")
+ void setStatus(Integer taskId, int status);
+}
diff --git a/src/main/java/com/whdc/mapper/QXWarningMapper.java b/src/main/java/com/whdc/mapper/QXWarningMapper.java
index 4d1a250..eeadff5 100644
--- a/src/main/java/com/whdc/mapper/QXWarningMapper.java
+++ b/src/main/java/com/whdc/mapper/QXWarningMapper.java
@@ -12,6 +12,7 @@ import java.util.List;
public interface QXWarningMapper extends BaseMapper {
List find(@Param("dto")QXWarning dto);
List findByMsgIsNull();
+ List findByAutoMsgIsNull();
IPage page(@Param("page") IPage page, @Param("dto") GroupWarningDto dto);
diff --git a/src/main/java/com/whdc/mapper/WarnCallMapMapper.java b/src/main/java/com/whdc/mapper/WarnCallMapMapper.java
new file mode 100644
index 0000000..c217b9d
--- /dev/null
+++ b/src/main/java/com/whdc/mapper/WarnCallMapMapper.java
@@ -0,0 +1,57 @@
+package com.whdc.mapper;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.whdc.model.entity.QXWarning;
+import com.whdc.model.entity.WarnCallMap;
+
+import java.util.List;
+
+/**
+ * @author lyf
+ * @since 2025-06-19
+ */
+public interface WarnCallMapMapper extends BaseMapper {
+ List listWarnsThatNotGeneratedWcm();
+
+ default List listUnGeneratedWcm() {
+ return selectList(
+ new QueryWrapper()
+ .eq("should_generate", 1)
+ .eq("generated", 0)
+ .eq("err_step", 0)
+ .orderByAsc("id")
+ );
+ }
+
+ default List listUnUploadedWcm() {
+ return selectList(
+ new QueryWrapper()
+ .eq("generated", 1)
+ .eq("uploaded", 0)
+ .eq("err_step", 0)
+ .orderByAsc("id")
+ );
+ }
+
+ default List listUnCalledWcm() {
+ return selectList(
+ new QueryWrapper()
+ .eq("uploaded", 1)
+ .eq("called", 0)
+ .eq("err_step", 0)
+ .orderByAsc("id")
+ );
+ }
+
+ default List listCallIsNotPutWcm() {
+ return selectList(
+ new QueryWrapper()
+ .eq("uploaded", 1)
+ .eq("called", 1)
+ .eq("call_is_put", 0)
+ .eq("err_step", 0)
+ .orderByDesc("id")
+ );
+ }
+}
diff --git a/src/main/java/com/whdc/mapper/WarningResponderMapper.java b/src/main/java/com/whdc/mapper/WarningResponderMapper.java
new file mode 100644
index 0000000..ce80cfc
--- /dev/null
+++ b/src/main/java/com/whdc/mapper/WarningResponderMapper.java
@@ -0,0 +1,20 @@
+package com.whdc.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.whdc.model.entity.WarningResponder;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * @author xusan
+ * @date 2024-05-11
+ */
+public interface WarningResponderMapper extends BaseMapper {
+
+
+ List getListByAdnm2(@Param("adnm") String adnm);
+
+ List listByCnnmAndLevelOrderByLevelAsc(@Param("cnnm") String cnnm, @Param("levels") List levels);
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/whdc/model/dto/AutoCallDto.java b/src/main/java/com/whdc/model/dto/AutoCallDto.java
new file mode 100644
index 0000000..2b40e0b
--- /dev/null
+++ b/src/main/java/com/whdc/model/dto/AutoCallDto.java
@@ -0,0 +1,23 @@
+package com.whdc.model.dto;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+/**
+ * Description:
+ * Created by XuSan on 2024/7/31.
+ *
+ * @author XuSan
+ * @version 1.0
+ */
+@Data
+@Accessors(chain = true)
+public class AutoCallDto {
+
+ private String stm;
+ private String etm;
+
+ private Boolean callIsPut;
+
+ private FindPageDto page;
+}
diff --git a/src/main/java/com/whdc/model/dto/AddressBookOldDto.java b/src/main/java/com/whdc/model/dto/WarningResponderDto.java
similarity index 89%
rename from src/main/java/com/whdc/model/dto/AddressBookOldDto.java
rename to src/main/java/com/whdc/model/dto/WarningResponderDto.java
index 17a38aa..03bfd3c 100644
--- a/src/main/java/com/whdc/model/dto/AddressBookOldDto.java
+++ b/src/main/java/com/whdc/model/dto/WarningResponderDto.java
@@ -8,7 +8,7 @@ import lombok.Data;
* @date 2023年3月20日08:57:22
*/
@Data
-public class AddressBookOldDto{
+public class WarningResponderDto {
@ApiModelProperty(value = "名字")
private String name;
diff --git a/src/main/java/com/whdc/model/entity/AddressBook.java b/src/main/java/com/whdc/model/entity/AddressBook.java
index 53366b1..4c73557 100644
--- a/src/main/java/com/whdc/model/entity/AddressBook.java
+++ b/src/main/java/com/whdc/model/entity/AddressBook.java
@@ -9,6 +9,7 @@ import com.whdc.model.group.Insert;
import com.whdc.model.group.Update;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
@@ -110,4 +111,10 @@ public class AddressBook extends Model implements Serializable {
@ApiModelProperty(value = "openid")
private String openid;
+ /**
+ * 责任人级别
+ */
+ @TableField(value = "level")
+ @Schema(description = "1,2,3,4")
+ private Integer level;
}
\ No newline at end of file
diff --git a/src/main/java/com/whdc/model/entity/AutoCall.java b/src/main/java/com/whdc/model/entity/AutoCall.java
new file mode 100644
index 0000000..1a0e5f3
--- /dev/null
+++ b/src/main/java/com/whdc/model/entity/AutoCall.java
@@ -0,0 +1,77 @@
+package com.whdc.model.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 com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.annotations.ApiModel;
+import lombok.Data;
+import lombok.ToString;
+import lombok.experimental.Accessors;
+
+import java.util.Date;
+
+/**
+ * @author lyf
+ * @since 2025-06-14
+ */
+@Data
+@Accessors(chain = true)
+@ApiModel(description = "自动拨号")
+@TableName("FXKH_TXL.AUTOCALL")
+@ToString
+public class AutoCall {
+ /*
+ new的时候必填responderId,responderName,wcmId,createTm
+ query后必填__开头的
+ */
+ @TableId(value = "ID", type = IdType.AUTO)
+ private Integer id;
+ @TableField(value = "responder_id")
+ @JsonProperty("addressBookOldId")
+ private Integer responderId;
+ @JsonProperty("addressBookOldName")
+ @TableField(value = "responder_name")
+ private String responderName;
+ @TableField(value = "status")
+ private String status;//接通,空号,停机,关机,未接,拒接,占线,呼叫失败,null(空代表未呼叫)
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+ @TableField(value = "_create_tm")
+ private Date createTm;
+ @TableField(value = "wcm_id")
+ private Integer wcmId;
+ @TableField(value = "level")
+ private Integer level;
+
+ @TableField(value = "__talk_times")
+ private Integer talkTimes;//通话时长,单位秒
+ @TableField(value = "__sip_term_cause")
+ private String sipTermCause; //例如”对方挂机“
+ @TableField(value = "__caller")
+ private String caller;
+ @TableField(value = "__number")
+ private String number;
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+ @TableField(value = "__startring_at")
+ private Date startringAt; //-3
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+ @TableField(value = "__connected_at")
+ private Date connectedAt; //-3
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+ @TableField(value = "__started_at")
+ private Date startedAt;
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+ @TableField(value = "__endring_at")
+ private Date endringAt; //-3
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+ @TableField(value = "__disconnected_at")
+ private Date disconnectedAt; //-3
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+ @TableField(value = "__stopped_at")
+ private Date stopedAt;
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+ @TableField(value = "__last_modify")
+ private Date lastModify;
+}
diff --git a/src/main/java/com/whdc/model/entity/AutoCallConfig.java b/src/main/java/com/whdc/model/entity/AutoCallConfig.java
new file mode 100644
index 0000000..0733e06
--- /dev/null
+++ b/src/main/java/com/whdc/model/entity/AutoCallConfig.java
@@ -0,0 +1,15 @@
+package com.whdc.model.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+/**
+ * @author lyf
+ * @since 2025-06-19
+ */
+@Data
+@TableName("FXKH_TXL.AUTOCALL_CONFIG")
+public class AutoCallConfig {
+ private String key;
+ private String value;
+}
diff --git a/src/main/java/com/whdc/model/entity/AutoCallPerson.java b/src/main/java/com/whdc/model/entity/AutoCallPerson.java
new file mode 100644
index 0000000..e2e6eda
--- /dev/null
+++ b/src/main/java/com/whdc/model/entity/AutoCallPerson.java
@@ -0,0 +1,79 @@
+package com.whdc.model.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import java.util.Date;
+
+/**
+ * @author lyf
+ * @since 2025-07-08
+ */
+@Data
+public class AutoCallPerson {
+ public static final int STATUS_DEFAULT = 0;
+ public static final int STATUS_UPLOADED = 1;
+ public static final int STATUS_CALLED = 2;
+ public static final int STATUS_PUT = 3;
+ public static final int STATUS_CANCELLED = 4;
+ public static final int ERRCODE_ENCODE = 1;
+ public static final int ERRCODE_UPLOAD_FAIL = 2;
+
+ @TableId(value = "ID", type = IdType.AUTO)
+ private Integer id;
+ @TableField(value = "task_id")
+ private Integer taskId;
+ @TableField(value = "status")
+ private Integer status; //0:未上传 default 0
+ @TableField(value = "level")
+ private Integer level;
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+ @TableField(value = "_create_tm")
+ private Date createTm;
+ @TableField(value = "error_code")
+ private Integer errorCode; //default 0
+
+ @TableField(value = "__request_id")
+ private String uploadRequestId; //任务名,同时作为参数的taskName,用custId
+ @TableField(value = "__cust_id")
+ private String uploadCustId; //联系人id,用warningResponderId加时间戳毫秒
+ @TableField(value = "__cust_name")
+ private String uploadCustName; //联系人
+ @TableField(value = "__content")
+ private String uploadContent; //联系人
+ @TableField(value = "__number")
+ private String uploadNumber;
+ @TableField(value = "__upload_resp_msg")
+ private String uploadRespMsg; //上传结果
+ @TableField(value = "__remark")
+ private String detailRemark;//接通,空号,停机,关机,未接,拒接,占线,呼叫失败,null(空代表未呼叫)
+ @TableField(value = "__talk_times")
+ private Integer detailTalkTimes;//通话时长,单位秒
+ @TableField(value = "__sip_term_cause")
+ private String detailSipTermCause; //例如”对方挂机“
+
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+ @TableField(value = "__startring_at")
+ private Date detailStartringAt; //-3
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+ @TableField(value = "__connected_at")
+ private Date detailConnectedAt; //-3
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+ @TableField(value = "__started_at")
+ private Date detailStartedAt;
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+ @TableField(value = "__endring_at")
+ private Date detailEndringAt; //-3
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+ @TableField(value = "__disconnected_at")
+ private Date detailDisconnectedAt; //-3
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+ @TableField(value = "__stopped_at")
+ private Date detailStopedAt;
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+ @TableField(value = "__last_modify")
+ private Date detailLastModify;
+}
diff --git a/src/main/java/com/whdc/model/entity/AutoCallTask.java b/src/main/java/com/whdc/model/entity/AutoCallTask.java
new file mode 100644
index 0000000..e1a6c22
--- /dev/null
+++ b/src/main/java/com/whdc/model/entity/AutoCallTask.java
@@ -0,0 +1,57 @@
+package com.whdc.model.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @author lyf
+ * @since 2025-07-08
+ */
+@Data
+public class AutoCallTask {
+ public static final int STATUS_DEFAULT = 0; // 不生成
+ public static final int STATUS_SHOULD_GENERATE = 1;
+ public static final int STATUS_GENERATED = 2;
+ public static final int STATUS_ANY_SUCCESS = 3;
+ public static final int STATUS_ALL_FAIL = 4;
+ public static final int STATUS_CANCELLED = 5;
+ public static final int ERRCODE_NO_PERSON = 1;
+ public static final int ERRCODE_DB_ERROR = 2;
+
+ @TableId(value = "ID", type = IdType.AUTO)
+ private Integer id;
+ @TableField(value = "warn_id")
+ private Integer warnId; //not null
+ @TableField(value = "status")
+ private Integer status; //0:不拨打 default 0
+ @TableField(value = "error_code")
+ private Integer errorCode; //default 0
+ @TableField(value = "warn_name")
+ private String warnName;
+ @TableField(value = "warn_tm")
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
+ private Date warnTm;
+ @TableField(value = "warn_level")
+ private String warnLevel;
+ @TableField(value = "WARN_CTNM")
+ private String warnCtnm;
+ @TableField(value = "warn_cnnm")
+ private String warnCnnm;
+ @TableField(value = "warn_content")
+ private String warnContent;
+
+ @TableField(value = "_create_tm")
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
+ private Date createTm;
+ @TableField(value = "_remark")
+ private String remark;
+
+ @TableField(exist = false)
+ private List callList;
+}
diff --git a/src/main/java/com/whdc/model/entity/ShAddressBook.java b/src/main/java/com/whdc/model/entity/ShAddressBook.java
index c0f8772..1deea54 100644
--- a/src/main/java/com/whdc/model/entity/ShAddressBook.java
+++ b/src/main/java/com/whdc/model/entity/ShAddressBook.java
@@ -160,4 +160,10 @@ public class ShAddressBook implements Serializable {
@TableField(exist = false)
List list;
+ /**
+ * 责任人级别
+ */
+ @TableField(value = "level")
+ @Schema(description = "1,2,3,4")
+ private Integer level;
}
diff --git a/src/main/java/com/whdc/model/entity/WarnCallMap.java b/src/main/java/com/whdc/model/entity/WarnCallMap.java
new file mode 100644
index 0000000..2f478ee
--- /dev/null
+++ b/src/main/java/com/whdc/model/entity/WarnCallMap.java
@@ -0,0 +1,73 @@
+package com.whdc.model.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 com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @author lyf
+ * @since 2025-06-19
+ */
+@Data
+@TableName("FXKH_TXL.WARN_CALL_MAP")
+public class WarnCallMap {
+ /*
+ new的时候必填warnId,shouldGenerate,cnnm,requestId,custId,custName,warnName,warnTm,warnLevel,callContent,createTm
+ 生成autoCall后必填generated
+ upload后必填uploadRespMsg,uploaded
+ query后必填callIsPut,called
+ */
+ @TableId(value = "ID", type = IdType.AUTO)
+ private Integer id;
+ @TableField(value = "warn_id")
+ private Integer warnId; //not null
+ @TableField(value = "should_generate")
+ private Integer shouldGenerate; //0:不生成,1:生成, not null
+ @TableField(value = "generated")
+ private Integer generated; //0:未生成,1:已生成, default 0
+ @TableField(value = "uploaded")
+ private Integer uploaded; //0:未上传,1:已上传, default 0
+ @TableField(value = "cnnm")
+ private String cnnm;
+ @TableField(value = "upload_resp_msg")
+ private String uploadRespMsg;
+ @TableField(value = "request_id")
+ private String requestId; //任务名,作为参数的taskName
+ @TableField(value = "cust_id")
+ private String custId; //联系人id
+ @TableField(value = "cust_name")
+ private String custName; //联系人
+ @TableField(value = "call_is_put")
+ private Integer callIsPut; //0:接通,1:已接通, default 0
+ @TableField(value = "called")
+ private Integer called; //0:未拨打,1:已拨打, default 0
+ @TableField(value = "err_step")
+ private Integer errStep; //0:初始化,1:step1出错, 以此类推,default 0
+
+ @TableField(value = "warn_name")
+ private String warnName;
+ @TableField(value = "warn_tm")
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
+ private Date warnTm;
+ @TableField(value = "warn_level")
+ private String warnLevel;
+ @TableField(value = "call_content")
+ private String warnContent;
+
+ @TableField(value = "_create_tm")
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
+ private Date createTm;
+ @TableField(value = "_remark")
+ private String remark;
+
+
+ @TableField(exist = false)
+ private List callList;
+
+}
diff --git a/src/main/java/com/whdc/model/entity/AddressBookOld.java b/src/main/java/com/whdc/model/entity/WarningResponder.java
similarity index 81%
rename from src/main/java/com/whdc/model/entity/AddressBookOld.java
rename to src/main/java/com/whdc/model/entity/WarningResponder.java
index 5a7898c..77f00b2 100644
--- a/src/main/java/com/whdc/model/entity/AddressBookOld.java
+++ b/src/main/java/com/whdc/model/entity/WarningResponder.java
@@ -33,10 +33,10 @@ import java.util.UUID;
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
-@ApiModel(description = "通讯录")
+@ApiModel(description = "预警责任人")
@TableName("FXKH_TXL.ADDRESS_BOOK_OLD")
@Slf4j
-public class AddressBookOld extends Model implements Serializable {
+public class WarningResponder extends Model implements Serializable {
@TableId(value = "ID",type = IdType.AUTO)
@@ -79,7 +79,7 @@ public class AddressBookOld extends Model implements Serializabl
@ApiModelProperty(value = "创建时间")
private Date createTime;
- public AddressBookOld encryptPhone(){
+ public WarningResponder encryptPhone(){
if (StringUtils.isBlank(this.salt)){
this.salt = UUID.randomUUID().toString().replaceAll("-", "");
}
@@ -94,7 +94,7 @@ public class AddressBookOld extends Model implements Serializabl
return this;
}
- public AddressBookOld decryptPhone(){
+ public WarningResponder decryptPhone(){
try {
if (StringUtils.isNotBlank(this.phone) && StringUtils.isNotBlank(this.salt)){
this.phone = SymmetricEncryption.decrypt(this.phone,this.salt);
@@ -105,4 +105,25 @@ public class AddressBookOld extends Model implements Serializabl
}
return this;
}
+
+ public static void main(String[] args) {
+ WarningResponder a = new WarningResponder();
+ a.setPhone("15623775829");
+ a.encryptPhone();
+ System.out.println(a.salt);
+ System.out.println(a.phone);
+ }
+
+ /**
+ * 责任人级别
+ */
+ @TableField(value = "LEVEL")
+ private Integer level;
+
+ /**
+ * 责任人级别
+ */
+ @TableField(exist = false)
+ @Excel(name = "预警呼叫等级", width = 20)
+ private String levelLabel;
}
diff --git a/src/main/java/com/whdc/model/entity/autocall/AICCCallRespDetail.java b/src/main/java/com/whdc/model/entity/autocall/AICCCallRespDetail.java
new file mode 100644
index 0000000..2a45ba8
--- /dev/null
+++ b/src/main/java/com/whdc/model/entity/autocall/AICCCallRespDetail.java
@@ -0,0 +1,64 @@
+package com.whdc.model.entity.autocall;
+
+
+import com.alibaba.fastjson.annotation.JSONField;
+import lombok.ToString;
+
+import java.util.List;
+
+/**
+ * 联通查询任务状态接口返回对象
+ *
+ * @author lyf
+ * @since 2025-06-17
+ */
+@lombok.Data
+@ToString
+public class AICCCallRespDetail {
+ private Data data;
+
+ @lombok.Data
+ @ToString
+ public static class Data {
+ List records;
+ }
+
+ @lombok.Data
+ @ToString
+ public static class Record {
+ private String caller; //外呼号码
+ private Long dataTime;
+ private String processId; //机器人id
+ private String sessionDetailId; //联系人id,等同返回值的custId
+ private Integer talkTimes;//通话时长,单位秒
+ private String remark; //中文状态,接通,空号,停机,关机,未接,拒接,占线,呼叫失败,""代表未呼叫
+ private String taskName; //任务名
+ private Integer status; //0进行中 2结束 4暂停
+ private Integer totalCount;
+ private Integer sendCount;
+
+ private RawVarListMap rawVarListMap;
+ }
+
+ @lombok.Data
+ @ToString
+ public static class RawVarListMap {
+ private String taskName; //任务名,作为参数的taskName
+ private String dialTaskMainSn; //任务编号,作为参数的requestId
+ private String custId; //联系人id
+ private String sipTermCause; //例如”对方挂机“
+ private String sipTermStatus; //接通是200
+ private String caller;
+ @JSONField(name = "@NUMBER")
+ private String number;
+ private String startringAt;
+ private String connectedAt;
+ private String startedAt;
+ private String endringAt;
+ private String disconnectedAt;
+ private String stopedAt;
+ private String lastModify;
+ }
+
+
+}
diff --git a/src/main/java/com/whdc/model/entity/autocall/AICCCallRespTask.java b/src/main/java/com/whdc/model/entity/autocall/AICCCallRespTask.java
new file mode 100644
index 0000000..2a2b674
--- /dev/null
+++ b/src/main/java/com/whdc/model/entity/autocall/AICCCallRespTask.java
@@ -0,0 +1,38 @@
+package com.whdc.model.entity.autocall;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * uploadCallData接口的返回值
+ *
+ * @author lyf
+ * @since 2025-06-20
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class AICCCallRespTask extends AICCCallRespWrapper {
+ /*
+ {
+ "msg": "操作成功!",
+ "result": {
+ "msg": "任务名称重复" | "导入成功",
+ "count": "0",
+ "status": "-1"
+ },
+ "code": 0,
+ "redirected": false,
+ "success": true,
+ "errorType": 0,
+ "errorCode": "",
+ "timestamp": 1750383985979
+ }
+ */
+ //code 0 success true
+ public static final String MSG_SUCCESS = "导入成功";
+ public static final String MSG_REPEAT = "任务名称重复";
+
+ private String msg;
+ private String count;
+ private String status;
+}
diff --git a/src/main/java/com/whdc/model/entity/autocall/AICCCallRespWrapper.java b/src/main/java/com/whdc/model/entity/autocall/AICCCallRespWrapper.java
new file mode 100644
index 0000000..b5fc715
--- /dev/null
+++ b/src/main/java/com/whdc/model/entity/autocall/AICCCallRespWrapper.java
@@ -0,0 +1,37 @@
+package com.whdc.model.entity.autocall;
+
+import lombok.Data;
+
+/**
+ * @author lyf
+ * @since 2025-06-20
+ */
+@Data
+public class AICCCallRespWrapper {
+ /*
+ {
+ "code": 406,
+ "errorCode": "",
+ "errorType": 0,
+ "msg": "Token失效,请重新登录!",
+ "redirected": true,
+ "success": false,
+ "timestamp": 1750384243314
+ }
+ */
+ //code 406 success false
+ private String msg;
+ private int code;
+ private boolean redirected;
+ private boolean success;
+ private int errorType;
+ private String errorCode;
+ private long timestamp;
+
+ //token失效没有result
+ private T result;
+
+ public boolean isTokenInvalid() {
+ return code == 406 && !success;
+ }
+}
diff --git a/src/main/java/com/whdc/model/entity/autocall/AICCLogin.java b/src/main/java/com/whdc/model/entity/autocall/AICCLogin.java
new file mode 100644
index 0000000..4926dca
--- /dev/null
+++ b/src/main/java/com/whdc/model/entity/autocall/AICCLogin.java
@@ -0,0 +1,19 @@
+package com.whdc.model.entity.autocall;
+
+import lombok.Data;
+
+/**
+ * @author lyf
+ * @since 2025-06-20
+ */
+@Data
+public class AICCLogin {
+ /*
+ {"msg":"操作成功!","result":{"msg":"操作成功","tenantId":"963936517","status":"0","token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6IkhCU0wwMSIsInRlbmFudElkIjoiOTYzOTM2NTE3IiwiZXhwIjoxNzUwNjg4NjE4fQ.INUcA-keg7T3HrZXH87K2ZqOT2trxLF38kmA9Wu301w"},"code":0,"redirected":false,"success":true,"errorType":0,"errorCode":"","timestamp":1750386218853}
+ */
+ //code 0 success true
+ private String msg;
+ private String tenantId;
+ private String status;
+ private String token;
+}
diff --git a/src/main/java/com/whdc/model/entity/autocall/AICCUploadTask.java b/src/main/java/com/whdc/model/entity/autocall/AICCUploadTask.java
new file mode 100644
index 0000000..ff21ac1
--- /dev/null
+++ b/src/main/java/com/whdc/model/entity/autocall/AICCUploadTask.java
@@ -0,0 +1,120 @@
+package com.whdc.model.entity.autocall;
+
+import com.alibaba.fastjson.annotation.JSONField;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @author lyf
+ * @since 2025-06-20
+ */
+@Data
+public class AICCUploadTask {
+ /*
+ JSONObject data = new JSONObject();
+ data.put("taskName", taskName);
+ data.put("processId", processId);
+ data.put("callerGroup", callerGroup);
+ data.put("requestId", requestId);
+ data.put("calleeType", 2);
+ data.put("repeatTimes", 2);
+ data.put("autoCall", "0");
+ data.put("spanSeconds", 60);
+ data.put("processType", "3");
+ data.put("mutiTimeRange", timeRange);
+ JSONArray params = new JSONArray();
+ JSONObject param = new JSONObject();
+ param.put("@common_user_name", custName);
+ param.put("@NUMBER", number);
+ if (bakNumbers != null && bakNumbers.length > 0) {
+ for (int i = 0; i < bakNumbers.length; i++) {
+ if (i == 3) break;
+ param.put("备用号码" + (i + 1), bakNumbers[i]);
+ }
+ }
+ param.put("custId", custId);
+ param.put("content", "我是省防办智能外呼系统," + content + "如需咨询请拨打02787221781");
+ params.add(param);
+ data.put("param", params);
+ */
+ private String taskName;
+ private String processId;
+ private String callerGroup;
+ private String requestId;
+ private int calleeType = 1; //默认0 重乎1 顺乎2 默认为0
+ private int repeatTimes = 1;//呼叫次数,CallType=0不传; CalleeType=1重乎必传; CalleeType=2顺乎并且autoCall为0必传
+ private String autoCall = "0";//顺乎未接通是否重乎,开启=0; 关闭=1; CalleeType=2顺乎必传
+ private int spanSeconds = 3;//重乎间隔时间(秒),CallType=0不传; CalleeType=1重乎必传; CalleeType=2顺乎并且autoCall为0必传
+ private String processType = "3";
+ private String mutiTimeRange;
+ private List param;
+// private String smsSend = "1"; //1不发短信,0发短信
+// private String smsSendType = "0"; //挂机短信0,未接通短信1,smsSend为0必传
+// private String templateId; //短信模板id,smsSend为0必传
+
+ @Data
+ public static class Cust {
+ @JSONField(name = "@common_user_name")
+ private String custName;
+ @JSONField(name = "@NUMBER")
+ private String number;
+ @JSONField(name = "备用号码1")
+ private String backNumber1;
+ @JSONField(name = "备用号码2")
+ private String backNumber2;
+ @JSONField(name = "备用号码3")
+ private String backNumber3;
+ private String custId;
+ private String content;
+ @JSONField(serialize = false, deserialize = false)
+ private List _numbers;
+
+ public static CustBuilder builder() {
+ return new CustBuilder();
+ }
+
+ @Data
+ @Accessors(chain = true)
+ public static class CustBuilder {
+ private String custName;
+ private String custId;
+ private String content;
+ @JSONField(serialize = false, deserialize = false)
+ private List _numbers;
+
+ public Cust build() {
+ Cust cust = new Cust();
+ cust.setCustName(custName);
+ cust.setCustId(custId);
+ cust.setContent(content);
+ for (int i = 0; i < _numbers.size(); i++) {
+ switch (i) {
+ case 0:
+ cust.setNumber(_numbers.get(i));
+ break;
+ case 1:
+ cust.setBackNumber1(_numbers.get(i));
+ break;
+ case 2:
+ cust.setBackNumber2(_numbers.get(i));
+ break;
+ case 3:
+ cust.setBackNumber3(_numbers.get(i));
+ break;
+ }
+ }
+ return cust;
+ }
+ }
+
+ }
+
+ public void genMutiTimeRange() {
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ this.mutiTimeRange = sdf.format(new Date()) + "_" + sdf.format(new Date(System.currentTimeMillis() + 1000 * 60 * 60));
+ }
+}
diff --git a/src/main/java/com/whdc/model/vo/ExcelOldDataVo.java b/src/main/java/com/whdc/model/vo/ExcelOldDataVo.java
index 107fb2a..5961d84 100644
--- a/src/main/java/com/whdc/model/vo/ExcelOldDataVo.java
+++ b/src/main/java/com/whdc/model/vo/ExcelOldDataVo.java
@@ -1,6 +1,6 @@
package com.whdc.model.vo;
-import com.whdc.model.entity.AddressBookOld;
+import com.whdc.model.entity.WarningResponder;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
@@ -21,7 +21,7 @@ import java.util.List;
public class ExcelOldDataVo {
@ApiModelProperty(value = "数据")
- private List list;
+ private List list;
@ApiModelProperty(value = "键")
private String key;
diff --git a/src/main/java/com/whdc/model/vo/WarnDppleVO.java b/src/main/java/com/whdc/model/vo/WarnDppleVO.java
index a1d147e..cc2c15b 100644
--- a/src/main/java/com/whdc/model/vo/WarnDppleVO.java
+++ b/src/main/java/com/whdc/model/vo/WarnDppleVO.java
@@ -25,4 +25,8 @@ public class WarnDppleVO {
@ApiParam("手机号")
@ApiModelProperty(value = "手机号")
private String phone;
+
+ @ApiParam("责任级别")
+ @ApiModelProperty(value = "责任级别")
+ private Integer level;
}
diff --git a/src/main/java/com/whdc/service/AutoCallApiService.java b/src/main/java/com/whdc/service/AutoCallApiService.java
new file mode 100644
index 0000000..1d7fa96
--- /dev/null
+++ b/src/main/java/com/whdc/service/AutoCallApiService.java
@@ -0,0 +1,152 @@
+package com.whdc.service;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.whdc.mapper.*;
+import com.whdc.model.dto.AutoCallDto;
+import com.whdc.model.entity.AutoCall;
+import com.whdc.model.entity.AutoCallPerson;
+import com.whdc.model.entity.AutoCallTask;
+import com.whdc.model.entity.WarnCallMap;
+import com.whdc.utils.DateUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.text.ParseException;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @author lyf
+ * @since 2025-06-14
+ */
+@Service
+@Slf4j
+public class AutoCallApiService {
+ @Autowired
+ private AutoCallMapper autoCallMapper;
+ @Autowired
+ private WarnCallMapMapper warnCallMapMapper;
+
+ public Page page2(AutoCallDto dto) {
+ Date stm = null;
+ Date etm = null;
+ try {
+ if (dto.getStm() != null && dto.getEtm() != null) {
+ stm = DateUtils.formatD(dto.getStm(), DateUtils.DATE_PATTERN1);
+ etm = DateUtils.formatD(dto.getEtm(), DateUtils.DATE_PATTERN1);
+ }
+ } catch (ParseException e) {
+ throw new RuntimeException(e);
+ }
+ if (stm != null) stm = DateUtils.standardize(stm, true);
+ if (etm != null) etm = DateUtils.standardize(etm, false);
+
+ QueryWrapper query = new QueryWrapper()
+ .orderByDesc("id")
+ .between("warn_tm", stm, etm);
+ if (dto.getCallIsPut() != null) {
+ if (dto.getCallIsPut()) {
+ query.eq("status",
+ AutoCallTask.STATUS_ANY_SUCCESS
+ );
+ } else {
+ query.eq("status",
+ AutoCallTask.STATUS_ALL_FAIL
+ );
+ }
+ } else {
+ query.in("status",
+ AutoCallTask.STATUS_SHOULD_GENERATE,
+ AutoCallTask.STATUS_GENERATED,
+ AutoCallTask.STATUS_ANY_SUCCESS,
+ AutoCallTask.STATUS_ALL_FAIL,
+ AutoCallTask.STATUS_CANCELLED
+ );
+ }
+ Page pageParam = dto.getPage().getPage();
+ Page pageResult = taskMapper.selectPage(
+ pageParam,
+ query
+ );
+
+ List records = pageResult.getRecords();
+ if (records.size() > 0) {
+ for (AutoCallTask task : records) {
+ Integer taskId = task.getId();
+ List autoCallList = personMapper.selectList(
+ new QueryWrapper()
+ .eq("task_id", taskId)
+ );
+ task.setCallList(autoCallList);
+ }
+ }
+ return pageResult;
+ }
+
+ public Page page(AutoCallDto dto) {
+ Date stm = null;
+ Date etm = null;
+ try {
+ if (dto.getStm() != null && dto.getEtm() != null) {
+ stm = DateUtils.formatD(dto.getStm(), DateUtils.DATE_PATTERN1);
+ etm = DateUtils.formatD(dto.getEtm(), DateUtils.DATE_PATTERN1);
+ }
+ } catch (ParseException e) {
+ throw new RuntimeException(e);
+ }
+ if (stm != null) stm = DateUtils.standardize(stm, true);
+ if (etm != null) etm = DateUtils.standardize(etm, false);
+
+ QueryWrapper query = new QueryWrapper()
+ .eq("should_generate", 1)
+ .orderByDesc("id")
+ .between("warn_tm", stm, etm);
+ if (dto.getCallIsPut() != null) {
+ if (dto.getCallIsPut()) {
+ query.eq("call_is_put", 1);
+ } else {
+ query.eq("call_is_put", 0);
+ }
+ }
+ Page pageParam = dto.getPage().getPage();
+ Page pageResult = warnCallMapMapper.selectPage(
+ pageParam,
+ query
+ );
+ List records = pageResult.getRecords();
+ if (records.size() > 0) {
+ for (WarnCallMap record : records) {
+ Integer wcmId = record.getId();
+ List autoCallList = autoCallMapper.selectList(
+ new QueryWrapper()
+ .eq("wcm_id", wcmId)
+ );
+ record.setCallList(autoCallList);
+ }
+ }
+ return pageResult;
+ }
+
+ public List listCallIsNotPut() {
+ List wcmList = warnCallMapMapper.listCallIsNotPutWcm();
+ for (WarnCallMap record : wcmList) {
+ Integer wcmId = record.getId();
+ List autoCallList = autoCallMapper.selectList(
+ new QueryWrapper()
+ .eq("wcm_id", wcmId)
+ );
+ record.setCallList(autoCallList);
+ }
+ return wcmList;
+ }
+
+ @Autowired
+ private AutoCallTaskMapper taskMapper;
+ @Autowired
+ private AutoCallPersonMapper personMapper;
+ @Autowired
+ private QXWarningMapper warningMapper;
+
+}
diff --git a/src/main/java/com/whdc/service/AutoCallTaskService.java b/src/main/java/com/whdc/service/AutoCallTaskService.java
new file mode 100644
index 0000000..3209006
--- /dev/null
+++ b/src/main/java/com/whdc/service/AutoCallTaskService.java
@@ -0,0 +1,494 @@
+package com.whdc.service;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.whdc.mapper.*;
+import com.whdc.model.entity.*;
+import com.whdc.model.entity.autocall.AICCCallRespDetail;
+import com.whdc.model.entity.autocall.AICCCallRespWrapper;
+import com.whdc.model.entity.autocall.AICCUploadTask;
+import com.whdc.model.entity.autocall.AICCCallRespTask;
+import com.whdc.utils.AutoCallHelper;
+import com.whdc.utils.SmsHelper;
+import lombok.extern.slf4j.Slf4j;
+import org.jetbrains.annotations.NotNull;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.*;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.stream.Collectors;
+
+/**
+ * @author lyf
+ * @since 2025-06-14
+ */
+@Service
+@Slf4j
+public class AutoCallTaskService {
+ private static final String[] excludeNames = new String[]{"高新", "开发", "自治", "技术", "旅游", "管理", "工业", "产业", "示范"};
+ private static final AtomicBoolean isCalling = new AtomicBoolean(false);
+ @Autowired
+ private WarningResponderMapper warningResponderMapper;
+ @Autowired
+ private QXWarningMapper qxWarningMapper;
+ @Autowired
+ private AutoCallMapper autoCallMapper;
+ @Autowired
+ private AutoCallConfigMapper configMapper;
+ @Autowired
+ private WarnCallMapMapper warnCallMapMapper;
+ @Autowired
+ private AutoCallHelper autoCallHelper;
+ @Autowired
+ private SmsHelper smsHelper;
+ @Value("${autocall.processId}")
+ private String processId;
+ @Value("${autocall.callerGroup}")
+ private String callerGroup;
+ @Value("${autocall.secret}")
+ private String secret;
+ @Value("${autocall.sysUserId}")
+ private String sysUserId;
+
+ @Transactional
+ public void generateFakeCall() throws ParseException {
+ QXWarning warn = qxWarningMapper.selectOne(
+ new QueryWrapper()
+ .orderByDesc("WARNID")
+ .last("limit 1")
+ );
+ warn.setCtnm("恩施州");
+ warn.setCnnm("咸丰,");
+
+ List wcmList = newWcmByWarn(warn);
+ WarnCallMap wcm = wcmList.get(0);
+ wcm.setCreateTm(new Date());
+ warnCallMapMapper.insert(wcm);
+ }
+
+ public void step1GenerateWarnCallMap() {
+ List warnList = warnCallMapMapper.listWarnsThatNotGeneratedWcm();
+ for (QXWarning warn : warnList) {
+ try {
+ List wcmList = newWcmByWarn(warn);
+ for (WarnCallMap wcm : wcmList) {
+ if (wcm.getCnnm() == null) {
+ wcm.setErrStep(1);
+ wcm.setRemark("未找到县区");
+ }
+ wcm.setCreateTm(new Date());
+ warnCallMapMapper.insert(wcm);
+ }
+ } catch (Exception e) {
+ log.error("根据预警生成呼叫失败", e);
+ }
+ }
+ }
+
+ public String getToken() {
+ return autoCallHelper.getToken();
+ }
+
+ private @NotNull List newWcmByWarn(QXWarning warn) throws ParseException {
+ String cnnmStr = warn.getCnnm();
+ Integer warnId = warn.getWarnid();
+ String warnSignalLevel = warn.getWarnSignalLevel();
+ String content = warn.getContent();
+ String ctnm = warn.getCtnm();
+ Date publishTime = warn.getPublishTime();
+
+ List wcmList = new ArrayList<>();
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日HH时mm分");
+// content = "我是省防办智能外呼系统," + content + "如需咨询请拨打02787221781";
+ content = "我是省防办智能外呼系统," +warn.getCtnm()+sdf.format(warn.getPublishTime()) +"发布暴雨"+warn.getWarnSignalLevel()+"预警信号:"+ content + "如需咨询请拨打02787221781,相关信息以短信发送给您。";
+ if (cnnmStr == null || cnnmStr.trim().isEmpty()) {
+ WarnCallMap wcm = newWcm(null, warnId, publishTime, ctnm, warnSignalLevel, content);
+ wcmList.add(wcm);
+ return wcmList;
+ }
+ String[] cnnms = cnnmStr.trim().split(",");
+ for (String cnnm : cnnms) {
+ if (cnnm.isEmpty()) continue;
+ if (publishTime == null) continue;
+
+ WarnCallMap wcm = newWcm(cnnm, warnId, publishTime, ctnm, warnSignalLevel, content);
+ wcmList.add(wcm);
+ }
+ return wcmList;
+ }
+
+ private @NotNull WarnCallMap newWcm(String cnnm, Integer warnId, Date publishTime, String ctnm, String warnSignalLevel, String content) throws ParseException {
+ WarnCallMap wcm = new WarnCallMap();
+ wcm.setWarnId(warnId);
+ wcm.setCnnm(cnnm);
+
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ wcm.setWarnTm(publishTime);
+
+ wcm.setWarnName(String.format("%s%s%s%s预警", sdf.format(publishTime), ctnm, cnnm, warnSignalLevel));
+ wcm.setWarnLevel(warnSignalLevel);
+ wcm.setWarnContent(content);
+ wcm.setRequestId("hbsl" + new Date().getTime() + cnnm);
+
+ if (isEnable()) {
+ wcm.setShouldGenerate(1);
+ } else {
+ wcm.setShouldGenerate(0);
+ }
+ return wcm;
+ }
+
+ //为什么wcm和autocall的生成要分开:wcm是根据qxwarning生成的,自动呼叫可能会暂停,避免之后开启会根据早时的qxwarning生成wcm进而呼叫
+ public void step2GenerateAutoCallByWcm() {
+ List wcmList = warnCallMapMapper.listUnGeneratedWcm();
+ for (WarnCallMap wcm : wcmList) {
+ List autoCallList = newAutoCall(wcm);
+ if (autoCallList.size() == 0) {
+ wcm.setErrStep(2);
+ wcm.setRemark("未找到责任人");
+ warnCallMapMapper.updateById(wcm);
+ return;
+ }
+
+ StringBuilder sb = new StringBuilder();
+ //将responderId合并为custId
+ for (AutoCall autoCall : autoCallList) {
+ sb.append(autoCall.getResponderId()).append("-");
+ }
+ sb.deleteCharAt(sb.length() - 1);
+ String custId = System.currentTimeMillis() + "-" + sb;
+ //将responderName合并为custName
+ sb = new StringBuilder();
+ for (AutoCall autoCall : autoCallList) {
+ sb.append(autoCall.getResponderName()).append(",");
+ }
+ sb.deleteCharAt(sb.length() - 1);
+ String custName = sb.toString();
+ wcm.setCustId(custId);
+ wcm.setCustName(custName);
+ wcm.setGenerated(1);
+
+ Date now = new Date();
+ autoCallList.forEach(a -> {
+ a.setCreateTm(now);
+ autoCallMapper.insert(a);
+ });
+
+ warnCallMapMapper.updateById(wcm);
+ }
+ }
+
+ private @NotNull List newAutoCall(WarnCallMap wcm) {
+ Integer wcmId = wcm.getId();
+ String cnnm = wcm.getCnnm();
+ List existList = autoCallMapper.listByWcmIdAndCnnmOrderByLevelAsc(wcmId, cnnm);
+ if (existList != null && existList.size() > 0) {
+ return Collections.emptyList();
+ }
+
+ return newAutoCall(wcmId, cnnm, wcm.getWarnLevel());
+ }
+
+ private @NotNull List newAutoCall(Integer wcmId, String cnnm, String warnLevelStr) {
+ //处理行政区划和预警级别,橙色预警拨打1,2level的,红色预警只拨打3level的
+ List warnLevel = mapWarnSignalLevel(warnLevelStr);
+ List wrList = listWarningResponderByCnnmAndLevelOrderByLevelAsc(cnnm, warnLevel);
+ if (wrList.isEmpty()) return Collections.emptyList();
+ //处理县区包含高新区、开发区等情况
+ doSomethingWith(cnnm, wrList);
+ //按level从小到大排序
+ wrList.sort(Comparator.comparingInt(WarningResponder::getLevel));
+ List autoCallList = new ArrayList<>();
+ for (WarningResponder wr : wrList) {
+ Integer wrLevel = wr.getLevel();
+ wr.decryptPhone();
+ AutoCall autoCall = new AutoCall();
+ autoCall.setResponderId(wr.getId())
+ .setResponderName(wr.getName())
+ .setWcmId(wcmId)
+ .setLevel(wrLevel)
+ .setNumber(wr.getPhone());
+ autoCallList.add(autoCall);
+ }
+
+ return autoCallList;
+ }
+
+ public void step3UploadCallTask() {
+ if (!isCalling.compareAndSet(false, true)) {
+ return;
+ }
+
+ try {
+ //已提交,提交返回内容为“导入成功”,未拨打
+ List wcmList = warnCallMapMapper.listUnUploadedWcm();
+ if (wcmList == null || wcmList.isEmpty()) return;
+
+ for (WarnCallMap wcm : wcmList) {
+ Integer wcmId = wcm.getId();
+ List autoCallList = autoCallMapper.listByWcmIdAndCnnmOrderByLevelAsc(wcmId, wcm.getCnnm());
+ if (autoCallList == null || autoCallList.isEmpty()) {
+ wcm.setErrStep(3);
+ wcm.setRemark("未找到呼叫列表");
+ warnCallMapMapper.updateById(wcm);
+ continue;
+ }
+
+ List numbers = autoCallList.stream()
+ .map(AutoCall::getNumber)
+ .collect(Collectors.toList());
+
+ AICCUploadTask task = autoCallHelper.newTask(
+ wcm.getRequestId(),
+ wcm.getCustId(),
+ wcm.getCustName(),
+ wcm.getWarnContent(),
+ numbers
+ );
+ AICCCallRespWrapper AICCCallRespWrapper = autoCallHelper.apiUploadCallData(task);
+
+ String msg = AICCCallRespWrapper.getResult().getMsg();
+ wcm.setUploaded(1);
+ wcm.setUploadRespMsg(msg);
+ warnCallMapMapper.updateById(wcm);
+
+ smsHelper.send(numbers, wcm.getWarnContent());
+ }
+ } finally {
+ isCalling.set(false);
+ }
+ }
+
+ public void step4QueryTaskResult() {
+ List wcmList = warnCallMapMapper.listUnCalledWcm();
+ if (wcmList.isEmpty()) return;
+
+ for (WarnCallMap wcm : wcmList) {
+ Integer wcmId = wcm.getId();
+ String requestId = wcm.getRequestId();
+ String custId = wcm.getCustId();
+
+ List autoCallList = autoCallMapper.listByWcmIdAndCnnmOrderByLevelAsc(wcmId, wcm.getCnnm());
+ if (autoCallList == null || autoCallList.isEmpty()) {
+ wcm.setErrStep(4);
+ wcm.setRemark("未找到呼叫列表");
+ warnCallMapMapper.updateById(wcm);
+ continue;
+ }
+
+ AICCCallRespWrapper AICCCallRespWrapper = autoCallHelper.apiGetTaskCallDetail(requestId, custId);
+ if (AICCCallRespWrapper == null || !AICCCallRespWrapper.isSuccess()) {
+ wcm.setErrStep(4);
+ wcm.setRemark("请求外呼平台失败");
+ warnCallMapMapper.updateById(wcm);
+ continue;
+ }
+
+ //任务不存在
+ if (AICCCallRespWrapper.getResult() == null
+ || AICCCallRespWrapper.getResult().getData() == null
+ || AICCCallRespWrapper.getResult().getData().getRecords() == null
+ || AICCCallRespWrapper.getResult().getData().getRecords().isEmpty()) {
+ wcm.setErrStep(4);
+ wcm.setRemark("外呼平台查询不到任务");
+ warnCallMapMapper.updateById(wcm);
+ continue;
+ }
+
+ List records = AICCCallRespWrapper.getResult().getData().getRecords();
+ boolean anyCalled = true;
+ boolean anyCallIsPut = false;
+ for (AICCCallRespDetail.Record record : records) {
+ if (record.getRemark() == null || record.getRemark().isEmpty()) {
+ anyCalled = false;
+ continue;
+ }
+ if ("接通".equals(record.getRemark())) anyCallIsPut = true;
+ AICCCallRespDetail.RawVarListMap rawVarListMap = record.getRawVarListMap();
+ String number = rawVarListMap.getNumber();
+ AutoCall call = null;
+ for (AutoCall a : autoCallList) {
+ if (a.getNumber().equals(number)) {
+ call = a;
+ break;
+ }
+ }
+ copyDataFromDetailToAutoCall(record, call, rawVarListMap);
+ }
+ if (!records.isEmpty() && anyCalled) {
+ wcm.setCalled(1);
+ warnCallMapMapper.updateById(wcm);
+ }
+
+ for (AutoCall a : autoCallList) {
+ if (a.getStatus() == null && anyCallIsPut) {
+ a.setStatus("已呼通其他责任人");
+ }
+ autoCallMapper.updateById(a);
+ }
+ if (anyCallIsPut) {
+ wcm.setCallIsPut(1);
+ warnCallMapMapper.updateById(wcm);
+ }
+ }
+ }
+
+ public AICCCallRespWrapper queryTaskResult(String requestId, String custId) {
+ return autoCallHelper.apiGetTaskCallDetail(requestId, custId);
+ }
+
+ private void copyDataFromDetailToAutoCall(AICCCallRespDetail.Record record, AutoCall call, AICCCallRespDetail.RawVarListMap rawVarListMap) {
+ if (call != null) {
+ call.setStatus(record.getRemark());
+ call.setTalkTimes(record.getTalkTimes());
+ call.setSipTermCause(rawVarListMap.getSipTermCause());
+ call.setCaller(rawVarListMap.getCaller());
+
+ Date d;
+ long l;
+
+ if (rawVarListMap.getStartringAt() != null) {
+ d = new Date();
+ l = Long.parseLong(rawVarListMap.getStartringAt().substring(0, 13));
+ d.setTime(l);
+ call.setStartringAt(d);
+ }
+
+ if (rawVarListMap.getConnectedAt() != null) {
+ d = new Date();
+ l = Long.parseLong(rawVarListMap.getConnectedAt().substring(0, 13));
+ d.setTime(l);
+ call.setConnectedAt(d);
+ }
+
+ if (rawVarListMap.getStartedAt() != null) {
+ d = new Date();
+ l = Long.parseLong(rawVarListMap.getStartedAt());
+ d.setTime(l);
+ call.setStartedAt(d);
+ }
+
+ if (rawVarListMap.getEndringAt() != null) {
+ d = new Date();
+ l = Long.parseLong(rawVarListMap.getEndringAt().substring(0, 13));
+ d.setTime(l);
+ call.setEndringAt(d);
+ }
+
+ if (rawVarListMap.getDisconnectedAt() != null) {
+ d = new Date();
+ l = Long.parseLong(rawVarListMap.getDisconnectedAt().substring(0, 13));
+ d.setTime(l);
+ call.setDisconnectedAt(d);
+ }
+
+ if (rawVarListMap.getStopedAt() != null) {
+ d = new Date();
+ l = Long.parseLong(rawVarListMap.getStopedAt());
+ d.setTime(l);
+ call.setStopedAt(d);
+ }
+
+ if (rawVarListMap.getLastModify() != null) {
+ d = new Date();
+ l = Long.parseLong(rawVarListMap.getLastModify());
+ d.setTime(l);
+ call.setLastModify(d);
+ }
+ }
+ }
+
+
+ private List mapWarnSignalLevel(String warnSignalLevel) {
+ List ret = new ArrayList<>();
+ if ("红色".equals(warnSignalLevel)) {
+ ret.add(3);
+ } else {
+ ret.add(1);
+ ret.add(2);
+ }
+ return ret;
+ }
+
+ /*
+ 更新字典,是否开启自动呼叫
+ */
+ public void setEnable(Boolean enable) {
+ AutoCallConfig config = new AutoCallConfig();
+ config.setKey("enable");
+ config.setValue(enable ? "1" : "0");
+ configMapper.update(
+ config,
+ new QueryWrapper()
+ .eq("key", "enable")
+ );
+ }
+
+ /*
+ 查询字典,是否开启自动呼叫
+ */
+ public boolean isEnable() {
+ AutoCallConfig config = configMapper.selectOne(
+ new QueryWrapper()
+ .eq("key", "enable")
+ .last("limit 1")
+ );
+ return config != null && "1".equals(config.getValue());
+ }
+
+ public void setCallIsPut(Integer wcmId) {
+ WarnCallMap wcm = warnCallMapMapper.selectById(wcmId);
+ wcm.setCalled(1);
+ wcm.setCallIsPut(1);
+ warnCallMapMapper.updateById(wcm);
+ }
+
+ /*
+ 查询字典,是否开启定时任务
+ */
+ public boolean isScheduled() {
+ AutoCallConfig config = configMapper.selectOne(
+ new QueryWrapper()
+ .eq("key", "schedule")
+ .last("limit 1")
+ );
+ return config != null && "1".equals(config.getValue());
+ }
+
+ private static void doSomethingWith(String cnmm, List wrList) {
+ //预警包含高新等字符,可跳出
+ boolean skip = false;
+ for (String excludeName : excludeNames) {
+ if (cnmm.contains(excludeName)) {
+ skip = true;
+ break;
+ }
+ }
+ if (skip) {
+ return;
+ }
+ //预警不包含高新等字符,检查人员,保留不包含高新等字符的人员
+ Iterator iter = wrList.iterator();
+ while (iter.hasNext()) {
+ WarningResponder autoCall = iter.next();
+ boolean anyMatch = false;
+ for (String excludeName : excludeNames) {
+ if (autoCall.getCnnm().contains(excludeName)) {
+ anyMatch = true;
+ break;
+ }
+ }
+ if (anyMatch) {
+ iter.remove();
+ }
+ }
+ }
+
+ private List listWarningResponderByCnnmAndLevelOrderByLevelAsc(String cnnm, List levels) {
+ return warningResponderMapper.listByCnnmAndLevelOrderByLevelAsc(cnnm, levels);
+ }
+}
diff --git a/src/main/java/com/whdc/service/AutoCallTaskService2.java b/src/main/java/com/whdc/service/AutoCallTaskService2.java
new file mode 100644
index 0000000..bb21562
--- /dev/null
+++ b/src/main/java/com/whdc/service/AutoCallTaskService2.java
@@ -0,0 +1,449 @@
+package com.whdc.service;
+
+import com.alibaba.fastjson.JSON;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.whdc.mapper.*;
+import com.whdc.model.entity.*;
+import com.whdc.model.entity.autocall.AICCCallRespDetail;
+import com.whdc.model.entity.autocall.AICCCallRespTask;
+import com.whdc.model.entity.autocall.AICCCallRespWrapper;
+import com.whdc.model.entity.autocall.AICCUploadTask;
+import com.whdc.utils.AICCHelper;
+import com.whdc.utils.SmsHelper;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.PlatformTransactionManager;
+import org.springframework.transaction.TransactionStatus;
+import org.springframework.transaction.support.DefaultTransactionDefinition;
+
+import java.io.UnsupportedEncodingException;
+import java.security.GeneralSecurityException;
+import java.text.SimpleDateFormat;
+import java.util.*;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.stream.Collectors;
+
+/**
+ * @author lyf
+ * @since 2025-07-08
+ */
+@Service
+@Slf4j
+public class AutoCallTaskService2 {
+ @Autowired
+ private AutoCallTaskMapper taskMapper;
+ @Autowired
+ private AutoCallPersonMapper personMapper;
+ @Autowired
+ private AutoCallConfigMapper configMapper;
+ @Autowired
+ private PlatformTransactionManager transactionManager;
+ @Autowired
+ private WarningResponderMapper warningResponderMapper;
+ @Autowired
+ private QXWarningMapper qxWarningMapper;
+ @Autowired
+ private AICCHelper aiccHelper;
+ @Autowired
+ private SmsHelper smsHelper;
+
+ public void generateFakeCall() {
+ QXWarning warn = qxWarningMapper.selectOne(
+ new QueryWrapper()
+ .orderByDesc("WARNID")
+ .last("limit 1")
+ );
+ warn.setCtnm("恩施州");
+ warn.setCnnm("咸丰 ");
+
+ List taskList = newTask(warn);
+ AutoCallTask task = taskList.get(0);
+ task.setStatus(0);
+ task.setCreateTm(new Date());
+ task.setWarnCnnm("咸丰 ");
+ taskMapper.insert(task);
+ generatePerson(task);
+ }
+
+ public List doCallTest() {
+ List personList = step2GetOneUnUploadedPerson();
+
+ for (AutoCallPerson person : personList) {
+ step3UploadAICCTask(person);
+
+ int pendingDuration = 60 * 1000 * 2;
+ int loopGap = 1000;
+ while (true) {
+ try {
+ Thread.sleep(loopGap);
+ } catch (InterruptedException ignore) {
+ }
+ pendingDuration -= loopGap;
+ if (pendingDuration <= 0) break;
+
+ boolean f = step4QueryAICCTaskResult(person);
+ if (f) {
+ break;
+ }
+ }
+ if (pendingDuration <= 0) {
+ markPersonDetailQueryTimeout(person);
+ }
+ }
+ return personList;
+ }
+
+ public void markPersonDetailQueryTimeout(AutoCallPerson person) {
+ person.setDetailRemark("超时");
+ person.setStatus(AutoCallPerson.STATUS_CANCELLED);
+ person.setDetailSipTermCause("超时");
+ personMapper.updateById(person);
+ }
+
+ public void step1GenerateTask() {
+ //切记要设置task的status
+ List warnList = taskMapper.listWarnsThatNotGeneratedTask();
+ boolean enable = configMapper.isEnable();
+ for (QXWarning warn : warnList) {
+ try {
+ List taskList = newTask(warn);
+ Date now = new Date();
+ for (AutoCallTask task : taskList) {
+ if (task.getWarnCnnm() == null) {
+ task.setRemark("未找到县区");
+ }
+ task.setCreateTm(now);
+ if (enable && task.getWarnCnnm() != null) {
+ task.setStatus(AutoCallTask.STATUS_SHOULD_GENERATE);
+ }
+ taskMapper.insert(task);
+ if (enable && task.getWarnCnnm() != null) {
+ generatePerson(task);
+ }
+ }
+ } catch (Exception e) {
+ log.error("根据预警生成呼叫失败", e);
+ }
+ }
+ }
+
+ private List newTask(QXWarning warn) {
+ String cnnmStr = warn.getCnnm();
+ Integer warnId = warn.getWarnid();
+ String warnSignalLevel = warn.getWarnSignalLevel();
+ String content = warn.getContent();
+ String ctnm = warn.getCtnm();
+ Date publishTime = warn.getPublishTime();
+
+ List ret = new ArrayList<>();
+ if (cnnmStr == null || cnnmStr.isEmpty()) {
+ AutoCallTask task = newTask(null, warnId, publishTime, ctnm, warnSignalLevel, content);
+ ret.add(task);
+ return ret;
+ }
+ String[] cnnms = cnnmStr.split(",");
+ for (String cnnm : cnnms) {
+ if (cnnm.isEmpty()) continue;
+ if (publishTime == null) continue;
+ if ("test".equals(cnnm)) {
+ cnnm = "咸丰 ";
+ ctnm = "恩施州";
+ }
+ AutoCallTask task = newTask(cnnm, warnId, publishTime, ctnm, warnSignalLevel, content);
+ ret.add(task);
+ }
+ return ret;
+ }
+
+ private AutoCallTask newTask(String cnnm, Integer warnId, Date publishTime, String ctnm, String warnSignalLevel, String content) {
+ AutoCallTask task = new AutoCallTask();
+ task.setWarnId(warnId);
+ task.setWarnCtnm(ctnm);
+ task.setWarnCnnm(cnnm);
+ task.setWarnContent(content);
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ task.setWarnTm(publishTime);
+ task.setWarnName(String.format("%s%s%s%s预警", sdf.format(publishTime), ctnm, cnnm, warnSignalLevel));
+ task.setWarnLevel(warnSignalLevel);
+ return task;
+ }
+
+ public void generatePerson(AutoCallTask task) {
+ //切记要设置task的status
+ List personList = newPerson(task);
+ if (personList.size() == 0) {
+ task.setRemark("未找到责任人");
+ task.setStatus(AutoCallTask.STATUS_CANCELLED);
+ task.setErrorCode(AutoCallTask.ERRCODE_NO_PERSON);
+ taskMapper.updateById(task);
+ return;
+ }
+ TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
+ try {
+ Date now = new Date();
+ personList.forEach(a -> {
+ a.setCreateTm(now);
+ personMapper.insert(a);
+ });
+ task.setStatus(AutoCallTask.STATUS_GENERATED);
+ taskMapper.updateById(task);
+ transactionManager.commit(status);
+ } catch (Exception e) {
+ transactionManager.rollback(status);
+ task.setRemark("数据库异常");
+ task.setStatus(AutoCallTask.STATUS_CANCELLED);
+ task.setErrorCode(AutoCallTask.ERRCODE_DB_ERROR);
+ taskMapper.updateById(task);
+ log.error("插入外呼责任人异常", e);
+ log.error("{}", JSON.toJSONString(personList));
+ }
+ }
+
+ private List mapWarnSignalLevel(String warnSignalLevel) {
+ List ret = new ArrayList<>();
+ if ("红色".equals(warnSignalLevel)) {
+ ret.add(3);
+ } else {
+ ret.add(1);
+ ret.add(2);
+ }
+ return ret;
+ }
+
+ private static final String[] excludeNames = new String[]{"高新", "开发", "自治", "技术", "旅游", "管理", "工业", "产业", "示范"};
+
+ private static void doSomethingWith(String cnmm, List wrList) {
+ //预警包含高新等字符,可跳出
+ boolean skip = false;
+ for (String excludeName : excludeNames) {
+ if (cnmm.contains(excludeName)) {
+ skip = true;
+ break;
+ }
+ }
+ if (skip) {
+ return;
+ }
+ //预警不包含高新等字符,检查人员,保留不包含高新等字符的人员
+ Iterator iter = wrList.iterator();
+ while (iter.hasNext()) {
+ WarningResponder autoCall = iter.next();
+ boolean anyMatch = false;
+ for (String excludeName : excludeNames) {
+ if (autoCall.getCnnm().contains(excludeName)) {
+ anyMatch = true;
+ break;
+ }
+ }
+ if (anyMatch) {
+ iter.remove();
+ }
+ }
+ }
+
+ private List newPerson(AutoCallTask task) {
+ String cnnm = task.getWarnCnnm();
+ long millis = task.getCreateTm().getTime();
+ List warnLevels = mapWarnSignalLevel(task.getWarnLevel());
+ List wrList = warningResponderMapper.listByCnnmAndLevelOrderByLevelAsc(cnnm, warnLevels);
+ if (wrList.isEmpty()) return Collections.emptyList();
+ //处理县区包含高新区、开发区等情况
+ doSomethingWith(cnnm, wrList);
+ //按level从小到大排序
+ wrList.sort(Comparator.comparingInt(WarningResponder::getLevel));
+
+ List personList = new ArrayList<>();
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ for (WarningResponder wr : wrList) {
+ Integer wrLevel = wr.getLevel();
+ wr.decryptPhone();
+ AutoCallPerson person = new AutoCallPerson();
+ String uniqueId = millis + "-" + wr.getId();
+ person.setTaskId(task.getId());
+ person.setUploadRequestId(uniqueId);
+ person.setUploadCustId(uniqueId);
+ person.setUploadCustName(wr.getName());
+ person.setLevel(wrLevel);
+ person.setUploadNumber(wr.getPhone());
+ person.setUploadContent(
+ "我是省防办智能外呼系统," +
+ wr.getName() + "同志您好," +
+ task.getWarnCtnm() +
+ sdf.format(task.getWarnTm()) +
+ "发布暴雨" +
+ task.getWarnLevel() +
+ "预警信号:" +
+ task.getWarnContent() +
+ "如需咨询请拨打02787221781,相关信息以短信发送给您。"
+ );
+ person.setCreateTm(task.getCreateTm());
+ personList.add(person);
+ }
+ return personList;
+ }
+
+ public List step2GetOneUnUploadedPerson() {
+ List personList = personMapper.listUnUploaded();
+ if (personList == null || personList.isEmpty()) return Collections.emptyList();
+
+ Map> personGroup = personList.stream().collect(Collectors.groupingBy(AutoCallPerson::getTaskId));
+
+ List ret = new ArrayList<>();
+ personGroup.forEach((taskId, list) -> {
+ //按level从小到大排序
+ list.sort(Comparator.comparingInt(AutoCallPerson::getLevel));
+ ret.add(list.get(0));
+ });
+
+ return ret;
+ }
+
+ private static final AtomicBoolean isCalling = new AtomicBoolean(false);
+
+ public void step3UploadAICCTask(AutoCallPerson person) {
+ //切记要设置person的status
+ if (!isCalling.compareAndSet(false, true)) {
+ return;
+ }
+ try {
+ List numbers = Collections.singletonList(person.getUploadNumber());
+ AICCUploadTask uploadTask = aiccHelper.newTask(
+ person.getUploadRequestId(),
+ person.getUploadCustId(),
+ person.getUploadCustName(),
+ person.getUploadContent(),
+ numbers
+ );
+ AICCCallRespWrapper AICCCallRespWrapper = null;
+ try {
+ AICCCallRespWrapper = aiccHelper.apiUploadCallData(uploadTask);
+ } catch (GeneralSecurityException | UnsupportedEncodingException e) {
+ person.setStatus(AutoCallPerson.STATUS_CANCELLED);
+ person.setErrorCode(AutoCallPerson.ERRCODE_ENCODE);
+ personMapper.updateById(person);
+ log.error("外呼任务编码异常", e);
+ }
+ if (AICCCallRespWrapper == null) {
+ person.setStatus(AutoCallPerson.STATUS_CANCELLED);
+ person.setErrorCode(AutoCallPerson.ERRCODE_UPLOAD_FAIL);
+ personMapper.updateById(person);
+ } else {
+ String msg = AICCCallRespWrapper.getResult().getMsg();
+ person.setStatus(AutoCallPerson.STATUS_UPLOADED);
+ person.setUploadRespMsg(msg);
+ personMapper.updateById(person);
+ smsHelper.send(numbers, person.getUploadContent());
+ }
+ } finally {
+ isCalling.set(false);
+ }
+ }
+
+ public boolean step4QueryAICCTaskResult(AutoCallPerson person) {
+ //切记要设置person的status和task的status
+ AICCCallRespWrapper detail = aiccHelper.apiGetTaskCallDetail(person.getUploadRequestId(), person.getUploadCustId());
+ List records = detail.getResult().getData().getRecords();
+ if (records.isEmpty()) return false; //这个false是平台还未有拨打,应继续刷接口
+ AICCCallRespDetail.Record record = records.get(0);
+ String dialRemark = record.getRemark();
+ boolean done = false;
+ if ("接通".equals(dialRemark)) {
+ person.setStatus(AutoCallPerson.STATUS_PUT);
+ done = true;
+ } else if (records.size() > 1) {
+ person.setStatus(AutoCallPerson.STATUS_CALLED);
+ done = true;
+ }
+ if (done) {
+ person.setDetailRemark(dialRemark);
+ person.setDetailTalkTimes(record.getTalkTimes());
+ AICCCallRespDetail.RawVarListMap rawVarListMap = record.getRawVarListMap();
+ person.setDetailSipTermCause(rawVarListMap.getSipTermCause());
+
+ {
+ Date d;
+ long l;
+ if (rawVarListMap.getStartringAt() != null) {
+ d = new Date();
+ l = Long.parseLong(rawVarListMap.getStartringAt().substring(0, 13));
+ d.setTime(l);
+ person.setDetailStartringAt(d);
+ }
+
+ if (rawVarListMap.getConnectedAt() != null) {
+ d = new Date();
+ l = Long.parseLong(rawVarListMap.getConnectedAt().substring(0, 13));
+ d.setTime(l);
+ person.setDetailConnectedAt(d);
+ }
+
+ if (rawVarListMap.getStartedAt() != null) {
+ d = new Date();
+ l = Long.parseLong(rawVarListMap.getStartedAt());
+ d.setTime(l);
+ person.setDetailStartedAt(d);
+ }
+
+ if (rawVarListMap.getEndringAt() != null) {
+ d = new Date();
+ l = Long.parseLong(rawVarListMap.getEndringAt().substring(0, 13));
+ d.setTime(l);
+ person.setDetailEndringAt(d);
+ }
+
+ if (rawVarListMap.getDisconnectedAt() != null) {
+ d = new Date();
+ l = Long.parseLong(rawVarListMap.getDisconnectedAt().substring(0, 13));
+ d.setTime(l);
+ person.setDetailDisconnectedAt(d);
+ }
+
+ if (rawVarListMap.getStopedAt() != null) {
+ d = new Date();
+ l = Long.parseLong(rawVarListMap.getStopedAt());
+ d.setTime(l);
+ person.setDetailStopedAt(d);
+ }
+
+ if (rawVarListMap.getLastModify() != null) {
+ d = new Date();
+ l = Long.parseLong(rawVarListMap.getLastModify());
+ d.setTime(l);
+ person.setDetailLastModify(d);
+ }
+ }
+ personMapper.updateById(person);
+ boolean anySuccess = markTaskStatus(person.getTaskId());
+ if (anySuccess) {
+ markRemanentPersonStatus(person.getTaskId());
+ }
+ return true;
+ }
+ return false; //这个false是平台还未完成重呼,应继续刷接口
+ }
+
+ private void markRemanentPersonStatus(Integer taskId) {
+ personMapper.update(new AutoCallPerson() {{
+ setStatus(AutoCallPerson.STATUS_CANCELLED);
+ setDetailRemark("已呼通其他联系人");
+ }}, new QueryWrapper() {{
+ eq("task_id", taskId);
+ eq("status", AutoCallPerson.STATUS_DEFAULT);
+ }});
+
+ }
+
+ private boolean markTaskStatus(Integer taskId) {
+ boolean anySuccess = personMapper.isAnySuccess(taskId);
+ if (anySuccess) {
+ taskMapper.setStatus(taskId, AutoCallTask.STATUS_ANY_SUCCESS);
+ return true;
+ }
+ boolean allFail = personMapper.isAllFail(taskId);
+ if (allFail) {
+ taskMapper.setStatus(taskId, AutoCallTask.STATUS_ALL_FAIL);
+ }
+ return false;
+ }
+}
diff --git a/src/main/java/com/whdc/service/IAddressBookOldService.java b/src/main/java/com/whdc/service/IWarningResponderService.java
similarity index 50%
rename from src/main/java/com/whdc/service/IAddressBookOldService.java
rename to src/main/java/com/whdc/service/IWarningResponderService.java
index 16538b4..37be020 100644
--- a/src/main/java/com/whdc/service/IAddressBookOldService.java
+++ b/src/main/java/com/whdc/service/IWarningResponderService.java
@@ -1,7 +1,7 @@
package com.whdc.service;
import com.baomidou.mybatisplus.extension.service.IService;
-import com.whdc.model.entity.AddressBookOld;
+import com.whdc.model.entity.WarningResponder;
import java.util.List;
@@ -12,6 +12,6 @@ import java.util.List;
* @author xusan
* @date 2024-05-11
*/
-public interface IAddressBookOldService extends IService {
- List getListByAdnm2(String cnnm2);
+public interface IWarningResponderService extends IService {
+ List getListByAdnm2(String cnnm2);
}
\ No newline at end of file
diff --git a/src/main/java/com/whdc/service/impl/AddressBookOldServiceImpl.java b/src/main/java/com/whdc/service/impl/AddressBookOldServiceImpl.java
deleted file mode 100644
index bd90199..0000000
--- a/src/main/java/com/whdc/service/impl/AddressBookOldServiceImpl.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package com.whdc.service.impl;
-
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import com.whdc.mapper.AddressBookOldMapper;
-import com.whdc.model.dto.AddressBookOldDto;
-import com.whdc.model.entity.AddressBookOld;
-import com.whdc.service.IAddressBookOldService;
-import org.springframework.stereotype.Service;
-
-import java.util.List;
-
-/**
- *
- * 服务实现类
- *
- *
- * @author xusan
- * @date 2024-05-11
- */
-@Service
-public class AddressBookOldServiceImpl extends ServiceImpl implements IAddressBookOldService {
-
- @Override
- public List getListByAdnm2(String adnm) {
- return baseMapper.getListByAdnm2(adnm);
- }
-}
diff --git a/src/main/java/com/whdc/service/impl/WarningResponderServiceImpl.java b/src/main/java/com/whdc/service/impl/WarningResponderServiceImpl.java
new file mode 100644
index 0000000..0dbaaa5
--- /dev/null
+++ b/src/main/java/com/whdc/service/impl/WarningResponderServiceImpl.java
@@ -0,0 +1,26 @@
+package com.whdc.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.whdc.mapper.WarningResponderMapper;
+import com.whdc.model.entity.WarningResponder;
+import com.whdc.service.IWarningResponderService;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ *
+ * 服务实现类
+ *
+ *
+ * @author xusan
+ * @date 2024-05-11
+ */
+@Service
+public class WarningResponderServiceImpl extends ServiceImpl implements IWarningResponderService {
+
+ @Override
+ public List getListByAdnm2(String adnm) {
+ return baseMapper.getListByAdnm2(adnm);
+ }
+}
diff --git a/src/main/java/com/whdc/utils/AESpkcs7paddingUtil.java b/src/main/java/com/whdc/utils/AESpkcs7paddingUtil.java
new file mode 100644
index 0000000..fcdabba
--- /dev/null
+++ b/src/main/java/com/whdc/utils/AESpkcs7paddingUtil.java
@@ -0,0 +1,181 @@
+package com.whdc.utils;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import org.apache.commons.codec.binary.Base64;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+
+import javax.crypto.*;
+import javax.crypto.spec.SecretKeySpec;
+import java.io.UnsupportedEncodingException;
+import java.security.*;
+
+
+public class AESpkcs7paddingUtil {
+
+ /**
+ * 密钥算法
+ */
+ private static final String KEY_ALGORITHM = "AES";
+
+ /**
+ * 加密/解密算法 / 工作模式 / 填充方式
+ * Java 6支持PKCS5Padding填充方式
+ * Bouncy Castle支持PKCS7Padding填充方式
+ */
+ private static final String CIPHER_ALGORITHM = "AES/ECB/PKCS7Padding";
+
+ static {
+ //如果是PKCS7Padding填充方式,则必须加上下面这行
+ Security.addProvider(new BouncyCastleProvider());
+ }
+
+ /**
+ * 生成密钥
+ *
+ * @return 密钥
+ * @throws Exception
+ */
+ public static String generateKey() throws Exception {
+ //实例化密钥生成器
+ KeyGenerator kg = KeyGenerator.getInstance(KEY_ALGORITHM);
+ /**
+ * 设置AES密钥长度
+ * AES要求密钥长度为128位或192位或256位,java默认限制AES密钥长度最多128位
+ * 如需192位或256位,则需要到oracle官网找到对应版本的jdk下载页,在"Additional Resources"中找到
+ * "Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files",点击[DOWNLOAD]下载
+ * 将下载后的local_policy.jar和US_export_policy.jar放到jdk安装目录下的jre/lib/security/目录下,替换该目录下的同名文件
+ */
+ kg.init(128);
+ //生成密钥
+ SecretKey secretKey = kg.generateKey();
+ //获得密钥的字符串形式
+ return Base64.encodeBase64String(secretKey.getEncoded());
+ }
+
+ /**
+ * AES加密
+ *
+ * @param source 源字符串
+ * @param key 密钥
+ * @return 加密后的密文
+ * @throws Exception
+ */
+ public static String encrypt(String source, String key) throws GeneralSecurityException, UnsupportedEncodingException {
+
+ byte[] sourceBytes = source.getBytes("UTF-8");
+ byte[] keyBytes = key.getBytes("UTF-8");
+ Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM, "BC");
+ cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(keyBytes, KEY_ALGORITHM));
+ byte[] decrypted = cipher.doFinal(sourceBytes);
+ return Base64.encodeBase64String(decrypted);
+
+ }
+
+ /**
+ * AES解密
+ *
+ * @param encryptStr 加密后的密文
+ * @param key 密钥
+ * @return 源字符串
+ * @throws Exception
+ */
+ public static String decrypt(String encryptStr, String key) {
+ try {
+ byte[] sourceBytes = Base64.decodeBase64(encryptStr);
+ final byte[] keyBytes = key.getBytes("UTF-8");
+ Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM, "BC");
+ cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(keyBytes, KEY_ALGORITHM));
+ byte[] decoded = cipher.doFinal(sourceBytes);
+ return new String(decoded, "UTF-8");
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ String data = "{\"sysUserId\":\"HBSL01\",\"expire\":1000000}";
+ String encrypt = encrypt(data, "yxt@2024-1234567");
+ System.out.println(encrypt);
+ String request = "{\"request\":\"" + encrypt + "\"}";
+ String resp = HttpUtil.sendPost("https://aicc.cuopen.net:9801/aicc-api/ssb/callout/thirdParty/login", request);
+ System.out.println(resp);
+
+ /*
+ 挂断是无人接听
+ */
+
+// genTask();
+// genQueryTask();
+// String request = "{\n" +
+// " \"requestId\": \"hbsl1750130919921\",\n" +
+// " \"custId\": \"hbsl-zj\"\n" +
+// "}";
+// JSONObject header = new JSONObject();
+// header.put("X-Access-Token", "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Ik5TQkRKSzIwMjUiLCJ0ZW5hbnRJZCI6IjE5MTY1NDI5IiwiZXhwIjoxNzUwNDI5OTAzfQ.RvsTL5L8jPl6GqiQq344jBP5i-v6vuQt-kARsvUvuyY");
+// String resp = HttpUtil.sendPost("https://aicc.cuopen.net:9801/aicc-api/ssb/callout/thirdParty/task/getTaskCallDetail", request, header);
+// AICCCallRespDetail autoCallDetail = JSON.parseObject(resp, AICCCallRespDetail.class);
+// autoCallDetail.getResult().getData().getRecords().forEach(System.out::println);
+
+
+// AutoCallHelper helper = new AutoCallHelper();
+// helper.initToken();
+// String token = helper.getToken();
+// genTask();
+ }
+
+ private static void genQueryTask() throws Exception {
+ String requestId = "hbsl1750130186416";
+ String custId = "detail2c9ab2f08709s438018722de2ebf001f";
+ String data = "{\"requestId\":\"" + requestId + "\",\"custId\":\"" + custId + "\"}";
+ System.out.println(data);
+ String e = encrypt(data, "yxt@2024-1234567");
+ System.out.println(e);
+ }
+
+ private static void genTask() throws Exception {
+ String taskName = "hbsl" + System.currentTimeMillis();
+ String processId = "1934801506242949122";
+// String processId = "1934876091344777218";
+ String requestId = taskName;
+ String callerGroup = "02160980358";
+// String callerGroup = "01080945234";
+ String content = "这里是湖北省防汛抗旱指挥部办公室,咸宁市2025年06月15日02时26分58秒发布暴雨橙色预警信号:过去6小时最大降水出现在青山镇(青山水库)为54.6毫米。预计今天夜间,崇阳南部将出现暴雨到大暴雨,累计降雨量将达100毫米以上,并伴有雷暴大风,山区山洪、地质灾害、城乡积涝、中小河流洪水风险高,请加强防范。";
+// String content = "咸宁市2025年06月15日02时26分58秒发布暴雨橙色预警信号";
+ JSONObject data = new JSONObject();
+ data.put("taskName", taskName);
+ data.put("processId", processId);
+ data.put("callerGroup", callerGroup);
+ data.put("requestId", requestId);
+ data.put("calleeType", 2);
+ data.put("repeatTimes", 2);
+ data.put("autoCall", "0");
+ data.put("spanSeconds", 60);
+ data.put("processType", "3");
+ data.put("mutiTimeRange", "2025-06-17 10:00:00_2025-06-17 21:00:00");
+ JSONArray params = new JSONArray();
+ JSONObject param = new JSONObject();
+ param.put("@common_user_name", "李");
+// param.put("@NUMBER", "15671545233");
+ param.put("@NUMBER", "15671545233");
+ param.put("备用号码1", "18162570850");
+// param.put("备用号码1", "13933930962");
+// param.put("备用号码2", "15697168180");
+ param.put("custId", "abo70652-abo70653-abo70654");
+// param.put("address", "{}");
+ param.put("content", content);
+ params.add(param);
+ data.put("param", params);
+ System.out.println(data);
+
+ String e = encrypt(data.toJSONString(), "yxt@2024-1234567");
+
+ JSONObject request = new JSONObject();
+ request.put("request", e);
+ JSONObject header = new JSONObject();
+ header.put("X-Access-Token", "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Ik5TQkRKSzIwMjUiLCJ0ZW5hbnRJZCI6IjE5MTY1NDI5IiwiZXhwIjoxNzUwNDI5OTAzfQ.RvsTL5L8jPl6GqiQq344jBP5i-v6vuQt-kARsvUvuyY");
+ HttpUtil.sendPost("https://aicc.cuopen.net:9801/aicc-api/ssb/callout/thirdParty/task/uploadCallData", request.toJSONString(), header);
+
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/whdc/utils/AICCHelper.java b/src/main/java/com/whdc/utils/AICCHelper.java
new file mode 100644
index 0000000..8c55819
--- /dev/null
+++ b/src/main/java/com/whdc/utils/AICCHelper.java
@@ -0,0 +1,243 @@
+package com.whdc.utils;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson.TypeReference;
+import com.whdc.model.entity.autocall.*;
+import lombok.Setter;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import java.io.UnsupportedEncodingException;
+import java.security.GeneralSecurityException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * @author lyf
+ * @since 2025-06-19
+ */
+@Component
+@Slf4j
+public class AICCHelper {
+ @Setter
+ @Value("${autocall.processId}")
+ private String processId;
+ @Setter
+ @Value("${autocall.callerGroup}")
+ private String callerGroup;
+ @Setter
+ @Value("${autocall.secret}")
+ private String secret;
+ @Setter
+ @Value("${autocall.sysUserId}")
+ private String sysUserId;
+ @Autowired
+ private HttpHelper httpHelper = new HttpHelper();
+
+ private static final AtomicBoolean isAcquiringToken = new AtomicBoolean(false);
+ private static volatile CountDownLatch latch = new CountDownLatch(0);
+ private static final AtomicReference globalToken = new AtomicReference<>();
+
+ public String getToken() {
+ if (globalToken.get() == null) {
+ initToken();
+ }
+ return globalToken.get();
+ }
+
+ public void initToken() throws RuntimeException {
+ if (isAcquiringToken.compareAndSet(false, true)) {
+ globalToken.set(null);
+ try {
+ String data = "{\"sysUserId\":\"" + sysUserId + "\",\"expire\":1000000}";
+ String encrypt;
+ try {
+ encrypt = AESpkcs7paddingUtil.encrypt(data, secret);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ JSONObject request = new JSONObject();
+ request.put("request", encrypt);
+ Map headers = new HashMap<>();
+ headers.put("X-Access-Token", getToken());
+ String resp = httpHelper.postJsonString("https://aicc.cuopen.net:9801/aicc-api/ssb/callout/thirdParty/login", request.toJSONString(), headers);
+
+ TypeReference> type = new TypeReference>() {
+ };
+ AICCCallRespWrapper AICCCallRespWrapper = JSON.parseObject(resp, type);
+ if (AICCCallRespWrapper == null || !AICCCallRespWrapper.isSuccess() || AICCCallRespWrapper.getResult() == null) {
+ log.warn("获取外呼系统token失败");
+ return;
+ }
+
+ AICCLogin result = AICCCallRespWrapper.getResult();
+ String token = result.getToken();
+ if (token != null && !token.isEmpty()) {
+ globalToken.set(token);
+ }
+ } finally {
+ isAcquiringToken.set(false);
+ }
+ } else {
+ try {
+ latch.await();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+
+ public AICCCallRespWrapper apiUploadCallData(AICCUploadTask data) throws GeneralSecurityException, UnsupportedEncodingException {
+ return apiUploadCallData(data, getToken());
+ }
+
+ public AICCCallRespWrapper apiUploadCallData(AICCUploadTask data, String token) throws GeneralSecurityException, UnsupportedEncodingException {
+ String e;
+ try {
+ e = AESpkcs7paddingUtil.encrypt(JSON.toJSONString(data), secret);
+ } catch (GeneralSecurityException | UnsupportedEncodingException ex) {
+ throw ex;
+ } catch (Exception ex) {
+ throw new RuntimeException(ex);
+ }
+ JSONObject request = new JSONObject();
+ request.put("request", e);
+ Map headers = new HashMap<>();
+ headers.put("X-Access-Token", token);
+ String resp = httpHelper.postJsonString("https://aicc.cuopen.net:9801/aicc-api/ssb/callout/thirdParty/task/uploadCallData", request.toJSONString(), headers);
+ log.info("apiUploadCallData: {}", resp);
+ TypeReference> type = new TypeReference>() {
+ };
+ AICCCallRespWrapper AICCCallRespWrapper = JSON.parseObject(resp, type);
+ if (AICCCallRespWrapper == null || !AICCCallRespWrapper.isSuccess()) {
+ initToken();
+ headers.put("X-Access-Token", getToken());
+ resp = httpHelper.postJsonString("https://aicc.cuopen.net:9801/aicc-api/ssb/callout/thirdParty/task/uploadCallData", request.toJSONString(), headers);
+ AICCCallRespWrapper = JSON.parseObject(resp, type);
+ }
+
+ if (AICCCallRespWrapper == null || !AICCCallRespWrapper.isSuccess()) {
+ return null;
+ }
+ return AICCCallRespWrapper;
+ }
+
+ public AICCCallRespWrapper apiGetTaskCallDetail(String requestId, String custId) {
+ JSONObject request = new JSONObject();
+ request.put("requestId", requestId);
+ request.put("custId", custId);
+ Map headers = new HashMap<>();
+ headers.put("X-Access-Token", getToken());
+ String resp = httpHelper.postJsonString("https://aicc.cuopen.net:9801/aicc-api/ssb/callout/thirdParty/task/getTaskCallDetail", request.toJSONString(),headers);
+
+ TypeReference> type = new TypeReference>() {
+ };
+ AICCCallRespWrapper AICCCallRespWrapper = JSON.parseObject(resp, type);
+ if (AICCCallRespWrapper == null || !AICCCallRespWrapper.isSuccess()) {
+ initToken();
+ headers.put("X-Access-Token", getToken());
+ resp = httpHelper.postJsonString("https://aicc.cuopen.net:9801/aicc-api/ssb/callout/thirdParty/task/getTaskCallDetail", request.toJSONString(), headers);
+ AICCCallRespWrapper = JSON.parseObject(resp, type);
+ }
+
+ if (AICCCallRespWrapper == null || !AICCCallRespWrapper.isSuccess()) {
+ return null;
+ }
+ return AICCCallRespWrapper;
+ }
+
+ public AICCUploadTask newTask(String requestId, String custId, String custName, String content, List numbers) {
+ AICCUploadTask task = new AICCUploadTask();
+ task.setTaskName(requestId);
+ task.setProcessId(processId);
+ task.setCallerGroup(callerGroup);
+ task.setRequestId(requestId);
+ task.genMutiTimeRange();
+ AICCUploadTask.Cust param = AICCUploadTask.Cust.builder()
+ .setCustName(custName)
+ .setCustId(custId)
+ .setContent(content)
+ .set_numbers(numbers)
+ .build();
+ task.setParam(Collections.singletonList(param));
+
+ return task;
+ }
+
+// private String sendPost(String url, JSONObject jsonData) {
+// String resp = sendPost(url, jsonData.toJSONString());
+// if (resp == null) {
+// resp = sendPost(url, jsonData.toJSONString());
+// }
+// return resp;
+// }
+//
+// private String sendPost(String url, JSONObject jsonData, String token) {
+// String resp = sendPost(url, jsonData.toJSONString(), token);
+// if (resp == null) {
+// resp = sendPost(url, jsonData.toJSONString(), token);
+// }
+// return resp;
+// }
+//
+// private String sendPost(String url, String jsonData) {
+// return sendPost(url, jsonData, getToken());
+// }
+//
+// private String sendPost(String url, String jsonData, String token) {
+// CloseableHttpResponse response = null;
+// CloseableHttpClient httpClient = null;
+// String responseContent = null;
+// try {
+// httpClient = HttpClients.createDefault();
+// HttpPost httpPost = new HttpPost(url);
+// httpPost.addHeader("Content-Type", "application/json");
+// httpPost.addHeader("X-Access-Token", token);
+//
+// if (StringUtils.isNotBlank(jsonData)) {
+// httpPost.setEntity(new StringEntity(jsonData, "UTF-8"));
+// }
+//
+// log.info("请求地址: " + url);
+// log.info("token: " + getToken());
+// log.info("请求参数: " + jsonData);
+//
+// response = httpClient.execute(httpPost);
+// HttpEntity entity = response.getEntity();
+// responseContent = EntityUtils.toString(entity, "UTF-8");
+// responseContent = jsonFormat(responseContent);
+// if (responseContent.length() < 200) {
+// log.info("响应参数: " + responseContent);
+// }
+// } catch (Exception e) {
+// log.error("发送请求异常", e);
+// return null;
+// } finally {
+// try {
+// if (null != response) {
+// response.close();
+// }
+// httpClient.close();
+// } catch (IOException e) {
+// e.printStackTrace();
+// }
+// }
+// return responseContent;
+// }
+
+ private static String jsonFormat(String str) {
+ if (JSON.isValidObject(str)) {
+ str = JSON.parseObject(str).toJSONString();
+ }
+
+ return str;
+ }
+}
diff --git a/src/main/java/com/whdc/utils/AutoCallHelper.java b/src/main/java/com/whdc/utils/AutoCallHelper.java
new file mode 100644
index 0000000..d9922ab
--- /dev/null
+++ b/src/main/java/com/whdc/utils/AutoCallHelper.java
@@ -0,0 +1,235 @@
+package com.whdc.utils;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson.TypeReference;
+import com.whdc.model.entity.autocall.*;
+import lombok.Setter;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.http.HttpEntity;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.util.EntityUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * @author lyf
+ * @since 2025-06-19
+ */
+@Component
+@Slf4j
+public class AutoCallHelper {
+ @Setter
+ @Value("${autocall.processId}")
+ private String processId;
+ @Setter
+ @Value("${autocall.callerGroup}")
+ private String callerGroup;
+ @Setter
+ @Value("${autocall.secret}")
+ private String secret;
+ @Setter
+ @Value("${autocall.sysUserId}")
+ private String sysUserId;
+
+ private static final AtomicBoolean isAcquiringToken = new AtomicBoolean(false);
+ private static volatile CountDownLatch latch = new CountDownLatch(0);
+ private static final AtomicReference globalToken = new AtomicReference<>();
+
+ public String getToken() {
+ if (globalToken.get() == null) {
+ initToken();
+ }
+ return globalToken.get();
+ }
+
+ public void initToken() throws RuntimeException {
+ if (isAcquiringToken.compareAndSet(false, true)) {
+ globalToken.set(null);
+ try {
+ String data = "{\"sysUserId\":\"" + sysUserId + "\",\"expire\":1000000}";
+ String encrypt;
+ try {
+ encrypt = AESpkcs7paddingUtil.encrypt(data, secret);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ JSONObject request = new JSONObject();
+ request.put("request", encrypt);
+ String resp = sendPost("https://aicc.cuopen.net:9801/aicc-api/ssb/callout/thirdParty/login", request);
+
+ TypeReference> type = new TypeReference>() {
+ };
+ AICCCallRespWrapper AICCCallRespWrapper = JSON.parseObject(resp, type);
+ if (AICCCallRespWrapper == null || !AICCCallRespWrapper.isSuccess() || AICCCallRespWrapper.getResult() == null) {
+ log.warn("获取外呼系统token失败");
+ return;
+ }
+
+ AICCLogin result = AICCCallRespWrapper.getResult();
+ String token = result.getToken();
+ if (token != null && !token.isEmpty()) {
+ globalToken.set(token);
+ }
+ } finally {
+ isAcquiringToken.set(false);
+ }
+ } else {
+ try {
+ latch.await();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+
+ public AICCCallRespWrapper apiUploadCallData(AICCUploadTask data) {
+ return apiUploadCallData(data, getToken());
+ }
+
+ public AICCCallRespWrapper apiUploadCallData(AICCUploadTask data, String token) {
+ String e;
+ try {
+ e = AESpkcs7paddingUtil.encrypt(JSON.toJSONString(data), secret);
+ } catch (Exception ex) {
+ throw new RuntimeException(ex);
+ }
+ JSONObject request = new JSONObject();
+ request.put("request", e);
+ String resp = sendPost("https://aicc.cuopen.net:9801/aicc-api/ssb/callout/thirdParty/task/uploadCallData", request, token);
+
+ TypeReference> type = new TypeReference>() {
+ };
+ AICCCallRespWrapper AICCCallRespWrapper = JSON.parseObject(resp, type);
+ if (AICCCallRespWrapper == null || !AICCCallRespWrapper.isSuccess()) {
+ initToken();
+ resp = sendPost("https://aicc.cuopen.net:9801/aicc-api/ssb/callout/thirdParty/task/uploadCallData", request, token);
+ AICCCallRespWrapper = JSON.parseObject(resp, type);
+ }
+
+ if (AICCCallRespWrapper == null || !AICCCallRespWrapper.isSuccess()) {
+ return null;
+ }
+ return AICCCallRespWrapper;
+ }
+
+ public AICCCallRespWrapper apiGetTaskCallDetail(String requestId, String custId) {
+ JSONObject request = new JSONObject();
+ request.put("requestId", requestId);
+ request.put("custId", custId);
+ String resp = sendPost("https://aicc.cuopen.net:9801/aicc-api/ssb/callout/thirdParty/task/getTaskCallDetail", request);
+
+ TypeReference> type = new TypeReference>() {
+ };
+ AICCCallRespWrapper AICCCallRespWrapper = JSON.parseObject(resp, type);
+ if (AICCCallRespWrapper == null || !AICCCallRespWrapper.isSuccess()) {
+ initToken();
+ resp = sendPost("https://aicc.cuopen.net:9801/aicc-api/ssb/callout/thirdParty/task/getTaskCallDetail", request);
+ AICCCallRespWrapper = JSON.parseObject(resp, type);
+ }
+
+ if (AICCCallRespWrapper == null || !AICCCallRespWrapper.isSuccess()) {
+ return null;
+ }
+ return AICCCallRespWrapper;
+ }
+
+ public AICCUploadTask newTask(String requestId, String custId, String custName, String content, List numbers) {
+ AICCUploadTask task = new AICCUploadTask();
+ task.setTaskName(requestId);
+ task.setProcessId(processId);
+ task.setCallerGroup(callerGroup);
+ task.setRequestId(requestId);
+ task.genMutiTimeRange();
+ AICCUploadTask.Cust param = AICCUploadTask.Cust.builder()
+ .setCustName(custName)
+ .setCustId(custId)
+ .setContent(content)
+ .set_numbers(numbers)
+ .build();
+ task.setParam(Collections.singletonList(param));
+
+ return task;
+ }
+
+ private String sendPost(String url, JSONObject jsonData) {
+ String resp = sendPost(url, jsonData.toJSONString());
+ if (resp == null) {
+ resp = sendPost(url, jsonData.toJSONString());
+ }
+ return resp;
+ }
+
+ private String sendPost(String url, JSONObject jsonData, String token) {
+ String resp = sendPost(url, jsonData.toJSONString(), token);
+ if (resp == null) {
+ resp = sendPost(url, jsonData.toJSONString(), token);
+ }
+ return resp;
+ }
+
+ private String sendPost(String url, String jsonData) {
+ return sendPost(url, jsonData, getToken());
+ }
+
+ private String sendPost(String url, String jsonData, String token) {
+ CloseableHttpResponse response = null;
+ CloseableHttpClient httpClient = null;
+ String responseContent = null;
+ try {
+ httpClient = HttpClients.createDefault();
+ HttpPost httpPost = new HttpPost(url);
+ httpPost.addHeader("Content-Type", "application/json");
+ httpPost.addHeader("X-Access-Token", token);
+
+ if (StringUtils.isNotBlank(jsonData)) {
+ httpPost.setEntity(new StringEntity(jsonData, "UTF-8"));
+ }
+
+ log.info("请求地址: " + url);
+ log.info("token: " + getToken());
+ log.info("请求参数: " + jsonData);
+
+ response = httpClient.execute(httpPost);
+ HttpEntity entity = response.getEntity();
+ responseContent = EntityUtils.toString(entity, "UTF-8");
+ responseContent = jsonFormat(responseContent);
+ if (responseContent.length() < 200) {
+ log.info("响应参数: " + responseContent);
+ }
+ } catch (Exception e) {
+ log.error("发送请求异常", e);
+ return null;
+ } finally {
+ try {
+ if (null != response) {
+ response.close();
+ }
+ httpClient.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ return responseContent;
+ }
+
+ private static String jsonFormat(String str) {
+ if (JSON.isValidObject(str)) {
+ str = JSON.parseObject(str).toJSONString();
+ }
+
+ return str;
+ }
+}
diff --git a/src/main/java/com/whdc/utils/DateUtils.java b/src/main/java/com/whdc/utils/DateUtils.java
index a445b7c..6508a7a 100644
--- a/src/main/java/com/whdc/utils/DateUtils.java
+++ b/src/main/java/com/whdc/utils/DateUtils.java
@@ -4,10 +4,7 @@ import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
-import java.util.Date;
-import java.util.GregorianCalendar;
-import java.util.Objects;
-import java.util.SimpleTimeZone;
+import java.util.*;
/**
* @author 李赛
@@ -118,6 +115,23 @@ public class DateUtils {
return simpleDateFormat.parse(dateStr);
}
+ public static Date standardize(Date date, boolean stm) {
+ Calendar c = Calendar.getInstance();
+ c.setTime(date);
+ if (stm) {
+ c.set(Calendar.HOUR_OF_DAY, 0);
+ c.set(Calendar.MINUTE, 0);
+ c.set(Calendar.SECOND, 0);
+ c.set(Calendar.MILLISECOND, 0);
+ } else {
+ c.set(Calendar.HOUR_OF_DAY, 23);
+ c.set(Calendar.MINUTE, 59);
+ c.set(Calendar.SECOND, 59);
+ c.set(Calendar.MILLISECOND, 999);
+ }
+ return c.getTime();
+ }
+
public static void main(String[] args) throws ParseException {
diff --git a/src/main/java/com/whdc/utils/HttpHelper.java b/src/main/java/com/whdc/utils/HttpHelper.java
new file mode 100644
index 0000000..0c1b919
--- /dev/null
+++ b/src/main/java/com/whdc/utils/HttpHelper.java
@@ -0,0 +1,216 @@
+package com.whdc.utils;
+
+import lombok.extern.slf4j.Slf4j;
+import okhttp3.*;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URLEncoder;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.Map;
+
+/**
+ * @author lyf
+ * @since 2025-06-25
+ */
+@Slf4j
+@Component
+public class HttpHelper {
+ @Autowired
+ private OkHttpClient okHttpClient = new OkHttpClient.Builder().build();
+
+ public @NotNull String get(@NotNull String url, @Nullable Map params, @Nullable Map headers) {
+ if (url.isEmpty()) {
+ return "url为空";
+ }
+
+ Request.Builder requestBuilder = new Request.Builder()
+ .url(url)
+ .get();
+
+ //header
+ if (headers != null) {
+ for (Map.Entry entry : headers.entrySet()) {
+ String key = entry.getKey();
+ String value = entry.getValue();
+ if (key != null && value != null) {
+ requestBuilder.header(key, value);
+ }
+ }
+ }
+ //doGet
+ Request request = requestBuilder.build();
+
+ return doGet(request);
+ }
+
+ private @NotNull String doGet(Request request) {
+ try (Response response = okHttpClient.newCall(request).execute()) {
+ if (response.isSuccessful() && response.body() != null) {
+ return response.body().string();
+ } else {
+ return "请求失败:" + response;
+ }
+ } catch (IOException e) {
+ return "网络异常:" + e.getMessage();
+ }
+ }
+
+ public @NotNull String postJsonString(@NotNull String url, @Nullable String json, @Nullable Map headers) {
+ return postJsonString(url, json, headers, StandardCharsets.UTF_8);
+ }
+
+ public @NotNull String postJsonString(@NotNull String url, @Nullable String json, @Nullable Map headers, @NotNull Charset charset) {
+ if (url.isEmpty()) {
+ return "url为空";
+ }
+ if (json == null) {
+ json = "{}";
+ }
+
+ //mediatype
+ MediaType mediaType = MediaType.get("application/json");
+ //payload
+ RequestBody requestBody = RequestBody.create(json, mediaType);
+ //builder
+ Request.Builder requestBuilder = new Request.Builder()
+ .url(url)
+ .header("Content-Type", mediaType + ";charset=" + charset.name())
+ .post(requestBody);
+ //header
+ if (headers != null) {
+ for (Map.Entry entry : headers.entrySet()) {
+ String key = entry.getKey();
+ String value = entry.getValue();
+ if (key != null && value != null) {
+ requestBuilder.header(key, value);
+ }
+ }
+ }
+ Request request = requestBuilder.build();
+ //post
+ return doPost(request);
+ }
+
+ public @NotNull String postFormData(@NotNull String url, @Nullable Map params, @Nullable Map headers) {
+ return postFormData(url, params, headers, StandardCharsets.UTF_8);
+ }
+
+ public @NotNull String postFormData(@NotNull String url, @Nullable Map params, @Nullable Map headers, @NotNull Charset charset) {
+ if (url.isEmpty()) {
+ return "url为空";
+ }
+
+ //mediatype
+ MediaType mediaType = MultipartBody.FORM;
+ //payload
+ MultipartBody.Builder bodyBuilder = new MultipartBody.Builder()
+ .setType(MultipartBody.FORM);
+ if (params != null) {
+ for (Map.Entry entry : params.entrySet()) {
+ String key = entry.getKey();
+ Object value = entry.getValue();
+ if (key == null || value == null) {
+ continue;
+ }
+ if (value instanceof String) {
+ bodyBuilder.addFormDataPart(key, (String) value);
+ } else if (value instanceof File) {
+ File file = (File) value;
+ RequestBody fileBody = RequestBody.create(file, MediaType.get("application/octet-stream"));
+ bodyBuilder.addFormDataPart(key, file.getName(), fileBody);
+ } else {
+ return "不支持的参数类型";
+ }
+ }
+ }
+ RequestBody requestBody = bodyBuilder.build();
+ //builder
+ Request.Builder requestBuilder = new Request.Builder()
+ .url(url)
+ .header("Content-Type", mediaType + ";charset=" + charset.name())
+ .post(requestBody);
+ //header
+ if (headers != null) {
+ for (Map.Entry entry : headers.entrySet()) {
+ String key = entry.getKey();
+ String value = entry.getValue();
+ if (key != null && value != null) {
+ requestBuilder.header(key, value);
+ }
+ }
+ }
+ //post
+ Request request = requestBuilder.build();
+
+ return doPost(request);
+ }
+
+ public @NotNull String postFormUrlEncoded(@NotNull String url, @Nullable Map params, @Nullable Map headers) {
+ return postFormUrlEncoded(url, params, headers, StandardCharsets.UTF_8);
+ }
+
+ public @NotNull String postFormUrlEncoded(@NotNull String url, @Nullable Map params, @Nullable Map headers, @NotNull Charset charset) {
+ if (url.isEmpty()) {
+ return "url为空";
+ }
+
+ //mediatype
+ MediaType mediaType = MediaType.get("application/x-www-form-urlencoded");
+ //payload
+ FormBody.Builder bodyBuilder = new FormBody.Builder(charset);
+ if (params != null) {
+ for (Map.Entry entry : params.entrySet()) {
+ String key = entry.getKey();
+ Object value = entry.getValue();
+ if (key != null && value != null) {
+ try {
+ // Manually encode using GBK
+ String encodedValue = URLEncoder.encode(value.toString(), "GBK");
+ bodyBuilder.addEncoded(key, encodedValue);
+ } catch (Exception e) {
+ return "参数encode错误";
+ }
+ }
+ }
+ }
+ RequestBody formBody = bodyBuilder.build();
+ //builder
+ Request.Builder requestBuilder = new Request.Builder()
+ .url(url)
+ .header("Content-Type", mediaType + ";charset=" + charset.name())
+ .post(formBody);
+ //header
+ if (headers != null) {
+ for (Map.Entry entry : headers.entrySet()) {
+ String key = entry.getKey();
+ String value = entry.getValue();
+ if (key != null && value != null) {
+ requestBuilder.header(key, value);
+ }
+ }
+ }
+ //post
+ Request request = requestBuilder.build();
+
+ return doPost(request);
+ }
+
+ private @NotNull String doPost(Request request) {
+ try (Response response = okHttpClient.newCall(request).execute()) {
+ if (response.isSuccessful() && response.body() != null) {
+ return response.body().string();
+ } else {
+ return "请求失败:" + response;
+ }
+ } catch (IOException e) {
+ return "网络异常:" + e.getMessage();
+ }
+ }
+
+}
diff --git a/src/main/java/com/whdc/utils/HttpUtil.java b/src/main/java/com/whdc/utils/HttpUtil.java
index d4fea85..d11a718 100644
--- a/src/main/java/com/whdc/utils/HttpUtil.java
+++ b/src/main/java/com/whdc/utils/HttpUtil.java
@@ -393,6 +393,7 @@ public class HttpUtil {
}
log.info("请求地址: " + url);
+ log.info("请求header: " + header);
log.info("请求参数: " + jsonData);
@@ -402,7 +403,9 @@ public class HttpUtil {
responseContent = jsonFormat(responseContent);
- log.info("响应参数: " + responseContent);
+ if (responseContent.length() < 200) {
+ log.info("响应参数: " + responseContent);
+ }
} catch (Exception e) {
log.error("发送请求异常", e);
diff --git a/src/main/java/com/whdc/utils/SmsHelper.java b/src/main/java/com/whdc/utils/SmsHelper.java
new file mode 100644
index 0000000..ebd6c69
--- /dev/null
+++ b/src/main/java/com/whdc/utils/SmsHelper.java
@@ -0,0 +1,174 @@
+package com.whdc.utils;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.nio.charset.Charset;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+/**
+ * @author lyf
+ * @since 2025-06-25
+ */
+@Component
+@Slf4j
+public class SmsHelper {
+ @Value("${sms.url}")
+ private String url;
+ @Value("${sms.userName}")
+ private String userName;
+ @Value("${sms.serviceCode}")
+ private String serviceCode;
+ @Value("${sms.userPassword}")
+ private String userPassword;
+ @Autowired
+ private HttpHelper httpHelper;
+
+ public String send(List phones, String content) {
+ return send(phones, content, "201");
+ }
+
+ private String send(List phones, String content, String msgType) {
+ if (phones == null || phones.size() == 0) {
+ return "未选择手机号";
+ }
+ if (content == null || content.length() == 0) {
+ return "短信内容为空";
+ }
+
+ String[] msgIds = new String[phones.size()];
+ for (int i = 0; i < phones.size(); i++) {
+ msgIds[i] = UUID.randomUUID().toString().replace("-", "");
+ }
+ String strPhones = String.join(",", phones);
+ String strMsgIds = String.join(",", msgIds);
+ String timestamp = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
+ String sign = md5(userPassword + timestamp).toUpperCase();
+
+ Map params = new HashMap<>();
+ params.put("serviceCode", serviceCode);
+ params.put("userName", userName);
+ params.put("userPassword", userPassword);
+ params.put("phones", strPhones);
+ params.put("msgContent", encodeGBK(content));
+ params.put("timestamp", timestamp);
+ params.put("sign", sign);
+ params.put("mhtMsgIds", strMsgIds);
+ params.put("sendTime", "");
+ params.put("priority", "5");
+ params.put("orgCode", "42");
+ params.put("msgType", msgType);
+ params.put("reportFlag", "0");
+
+ String strResult = httpHelper.postFormUrlEncoded(url, params, null, Charset.forName("GBK"));
+ if (strResult == null) {
+ return "发送失败";
+ }
+
+// Map result = new ObjectMapper().readValue(strResult, Map.class);
+ JSONObject result = JSON.parseObject(strResult);
+ if (result == null) {
+ return "发送结果解析失败";
+ }
+ int errorCode = (Integer) result.get("result");
+ if (errorCode != 0) {
+ return SmtpErrorCode.getErrorString(errorCode);
+ }
+
+ return "发送成功";
+ }
+
+ private String md5(String str) {
+ MessageDigest m;
+ try {
+ m = MessageDigest.getInstance("MD5");
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException(e);
+ }
+ m.update(str.getBytes());
+ byte[] s = m.digest();
+ return parseByte2HexStr(s);
+ }
+
+ private static String encodeGBK(String content) {
+ try {
+ return URLDecoder.decode(content, "GBK");
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ //将二进制数组转换成16进制字符串
+ private String parseByte2HexStr(byte[] buf) {
+ StringBuilder sb = new StringBuilder();
+ for (byte b : buf) {
+ String hex = Integer.toHexString(b & 0xFF);
+ if (hex.length() == 1) {
+ hex = '0' + hex;
+ }
+ sb.append(hex);
+ }
+ return sb.toString();
+ }
+
+ public static class SmtpErrorCode {
+ private static Map map;
+
+ static {
+ map = new HashMap<>();
+
+ map.put(100, "非法登录,如登录名、口令出错、登录名与口令不符,md5 值不匹配等。");
+ map.put(101, "连接过多,指单个商户要求同时建立的连接数过多。");
+ map.put(102, "登陆类型错。");
+ map.put(103, "协议版本号错");
+ map.put(104, "ip 不合法,请联系管理员绑定 ip 后才可正常使用");
+ map.put(105, "Smtp 的 tcp 协议只支持后付费类型");
+
+ map.put(201, "非法手机号码。");
+ map.put(202, "reportFlag 值不合法,值必须为 0 或 1");
+ map.put(203, "信息长度错");
+ map.put(204, "短信内容中有非法字符");
+ map.put(205, "短信内容太长");
+ map.put(206, "不存在的优先级");
+ map.put(207, "SUBMIT 命令中的 serviceCode 不合法或者过期");
+ map.put(208, "手机号和商户消息 id 个数不匹配");
+ map.put(209, "orgCode 字段超过规定长度");
+ map.put(210, "msgType 字段超过规定长度");
+ map.put(211, "http 提交短信时的 url 不正确");
+ map.put(212, "被代理的商户与代理商户之间的关系不合法");
+ map.put(213, "暂未开通此商户通道,或者商户通道名称配置错误");
+ map.put(214, "请求参数有误,比如传递了不在接口文档中定义的参数");
+ map.put(215, "被代理的商户 id 不存在");
+ map.put(216, "商户平台的消息 id 的长度超过最大长度");
+ map.put(217, "sendTime 参数时间戳格式不正确");
+ map.put(218, "商户服务代码与用户名密码不匹配");
+ map.put(219, "接口只支持 HTTP POST 方式提交,而您的请求为 HTTP GET 方式");
+ map.put(220, "批量提交一次最多只支持 50 个");
+ map.put(221, "手机号不能为空");
+ map.put(222, "短信内容含有乱码");
+
+ map.put(500, "系统内部失败");
+
+ map.put(900, "余额不足");
+ map.put(901, "发送速度太快");
+ }
+
+ public static String getErrorString(int errorcode) {
+ if (!map.containsKey(errorcode)) {
+ return "errorcode:" + errorcode + "未定义";
+ }
+
+ return map.get(errorcode);
+ }
+ }
+
+}
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index 61fdd86..574c25a 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -6,6 +6,10 @@ server:
session:
# 配置会话超时
timeout: 120s
+ encoding:
+ charset: UTF-8
+ enabled: true
+ force: true
spring:
#数据库配置
datasource:
@@ -85,4 +89,17 @@ wx:
secret: 99a1b89ac30e28bcc9bba8be973027f4
#getGroupWarning: http://223.75.53.141:8000/shzh/met/zyqxfw/api/warning/getGroupWarning
-getGroupWarning: http://127.0.0.1:20000/shzh/met/zyqxfw/api/warning/getGroupWarning
\ No newline at end of file
+getGroupWarning: http://127.0.0.1:20000/shzh/met/zyqxfw/api/warning/getGroupWarning
+
+autocall:
+ sysUserId: HBSL01 # 平台网页https://aicc.cuopen.net:9801/login 密码Gswl@2025
+ processId: 1935233125932101634
+# processId: 1934801506242949122
+# callerGroup: 02160980358
+ callerGroup: 02759325005
+ secret: yxt@2024-1234567
+sms:
+ url: http://120.55.193.51:8098/smtp/http/submit
+ userName: hbfx15
+ serviceCode: hbfx15
+ userPassword: hbfx15
\ No newline at end of file
diff --git a/src/main/resources/log4j.properties b/src/main/resources/log4j.properties
new file mode 100644
index 0000000..a6233d5
--- /dev/null
+++ b/src/main/resources/log4j.properties
@@ -0,0 +1,2 @@
+log4j.rootLogger=ERROR
+log4j.logger.org.apache.http=ERROR
\ No newline at end of file
diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml
index a7f2a35..09f1447 100644
--- a/src/main/resources/logback-spring.xml
+++ b/src/main/resources/logback-spring.xml
@@ -34,7 +34,7 @@
- debug
+ info
${CONSOLE_LOG_PATTERN}
@@ -199,15 +199,16 @@
+
-
+
-
+
diff --git a/src/main/resources/mapper/AutoCallMapper.xml b/src/main/resources/mapper/AutoCallMapper.xml
new file mode 100644
index 0000000..e53f442
--- /dev/null
+++ b/src/main/resources/mapper/AutoCallMapper.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/resources/mapper/AutoCallTaskMapper.xml b/src/main/resources/mapper/AutoCallTaskMapper.xml
new file mode 100644
index 0000000..5f684bd
--- /dev/null
+++ b/src/main/resources/mapper/AutoCallTaskMapper.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/resources/mapper/QXWarningMapper.xml b/src/main/resources/mapper/QXWarningMapper.xml
index 47a4938..8f10aa7 100644
--- a/src/main/resources/mapper/QXWarningMapper.xml
+++ b/src/main/resources/mapper/QXWarningMapper.xml
@@ -29,6 +29,17 @@
WF.ID IS NULL
ORDER BY Q.WARNID DESC
+