智能外呼

master
李一帆 2025-07-14 11:23:11 +08:00
parent da00fc2b1a
commit c20442eae6
58 changed files with 3549 additions and 114 deletions

4
.gitignore vendored
View File

@ -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
/img
runtime-lib/

38
pom.xml
View File

@ -223,23 +223,51 @@
<version>4.6.0</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.12.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<!-- <plugin>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-maven-plugin</artifactId>-->
<!-- <version>2.7.5</version>-->
<!-- <executions>-->
<!-- <execution>-->
<!-- <id>repackage-executable-jar</id>-->
<!-- <goals>-->
<!-- <goal>repackage</goal>-->
<!-- </goals>-->
<!-- <configuration>-->
<!-- <skip>true</skip>-->
<!-- </configuration>-->
<!-- </execution>-->
<!-- </executions>-->
<!-- </plugin>-->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.7.5</version>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.5.0</version>
<executions>
<execution>
<id>copy-dependencies</id>
<goals>
<goal>repackage</goal>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<skip>true</skip>
<outputDirectory>${project.basedir}/runtime-lib</outputDirectory>
<overWriteReleases>false</overWriteReleases>
<overWriteSnapshots>false</overWriteSnapshots>
<overWriteIfNewer>true</overWriteIfNewer>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -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) {

View File

@ -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();
}
}
}

View File

@ -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<AutoCallPerson> 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);
}
}
}

View File

@ -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

View File

@ -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/");

View File

@ -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()))) {

View File

@ -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<String> doCallTest() throws ParseException {
autoCallTaskService.generateFakeCall();
return ResultJson.ok("resp");
}
@GetMapping("/doGenerateTest2")
public ResultJson<String> doGenerateTest2() throws ParseException {
autoCallTaskService2.generateFakeCall();
return ResultJson.ok("resp");
}
@GetMapping("/doCallTest2")
public ResultJson<List<AutoCallPerson>> doCallTest2() throws ParseException {
List<AutoCallPerson> personList = autoCallTaskService2.doCallTest();
return ResultJson.ok(personList);
}
@GetMapping("/getToken")
public ResultJson<String> getToken() {
return ResultJson.ok(autoCallTaskService.getToken());
}
@GetMapping("/queryTaskResult")
public ResultJson<String> queryTaskResult(@RequestParam("requestId") String requestId, @RequestParam("custId") String custId) {
return ResultJson.ok(autoCallTaskService.queryTaskResult(requestId, custId));
}
@PostMapping("/page")
public ResultJson<Page<WarnCallMap>> newList(@RequestBody AutoCallDto dto) {
return ResultJson.ok(autoCallApiService.page(dto));
}
@PostMapping("/page2")
public ResultJson<Page<AutoCallTask>> page2(@RequestBody AutoCallDto dto) {
return ResultJson.ok(autoCallApiService.page2(dto));
}
@GetMapping("/listCallIsNotPut")
public ResultJson<List<WarnCallMap>> listCallIsNotPut() {
return ResultJson.ok(autoCallApiService.listCallIsNotPut());
}
@GetMapping("/isEnable")
public ResultJson<Boolean> isEnable() {
return ResultJson.ok(autoCallTaskService.isEnable());
}
@GetMapping("/setEnable")
public ResultJson<Boolean> setEnable(@RequestParam("enable") Boolean enable) {
autoCallTaskService.setEnable(enable);
return ResultJson.ok(true);
}
@GetMapping("/setCallIsPut")
public ResultJson<Boolean> setCallIsPut(@RequestParam("taskId") Integer taskId) {
autoCallTaskService.setCallIsPut(taskId);
return ResultJson.ok(true);
}
}

View File

@ -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<AddressBookOld> dpples = addressBookOldService.getListByAdnm2(cnnm2);
List<WarningResponder> 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 {

View File

@ -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<QXWarning> qxWarns = ExcelCommon.importExcel(file,1, 0, 1, QXWarning.class);
Map<String, List<QXWarning>> qxByID = qxWarns.stream().collect(Collectors.groupingBy(QXWarning::getEffectId, Collectors.toList()));
List<AddressBookOld> olds = oldService.list();
Map<String, List<AddressBookOld>> txlByName = olds.stream().collect(Collectors.groupingBy(AddressBookOld::getName, Collectors.toList()));
List<WarningResponder> olds = oldService.list();
Map<String, List<WarningResponder>> txlByName = olds.stream().collect(Collectors.groupingBy(WarningResponder::getName, Collectors.toList()));
warnMsgs.forEach(o -> {
List<QXWarning> qxWarnings = qxByID.get(o.getEffectId());
@ -87,7 +87,7 @@ public class WarnMsgFBController {
.setPublishTime(qxWarning.getPublishTime())
.setWarnid(qxWarning.getWarnid());
String name = o.getCalledPerson();
List<AddressBookOld> txl = txlByName.get(name);
List<WarningResponder> txl = txlByName.get(name);
if (CollectionUtils.isNotEmpty(txl)){
o.setCalledPhone(txl.get(0).getPhone());
}else{

View File

@ -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<List<AddressBookOld>> find(@RequestBody AddressBookOldDto dto) {
public ResultJson<List<WarningResponder>> find(@RequestBody WarningResponderDto dto) {
LambdaQueryChainWrapper<AddressBookOld> query = service.lambdaQuery();
LambdaQueryChainWrapper<WarningResponder> 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<AddressBookOld> data = query.list();
List<WarningResponder> 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<AddressBookOld> list = service.lambdaQuery()
List<WarningResponder> 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<ExcelOldDataVo> getExcelData(MultipartFile file) {
List<AddressBookOld> appends = ExcelCommon.importExcel(file, 0, 1, AddressBookOld.class);
List<WarningResponder> 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<AddressBookOld> appends = JSON.parseArray(s, AddressBookOld.class);
List<WarningResponder> 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;
}
}
}

View File

@ -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<AddressBookOld> {
List<AddressBookOld> getListByAdnm2(@Param("adnm")String adnm);
// List<AddressBookOld> getListByAdnm(@Param("adnm") String adnm);
}

View File

@ -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<AutoCallConfig> {
default boolean isEnable() {
AutoCallConfig config = selectOne(
new QueryWrapper<AutoCallConfig>()
.eq("key", "enable")
.last("limit 1")
);
return config != null && "1".equals(config.getValue());
}
default boolean isScheduled() {
AutoCallConfig config = selectOne(
new QueryWrapper<AutoCallConfig>()
.eq("key", "schedule")
.last("limit 1")
);
return config != null && "1".equals(config.getValue());
}
}

View File

@ -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<AutoCall> {
List<AutoCall> getListByLevelAndStatus(@Param("level") Integer level, @Param("status") String status, @Param("called") Boolean called, @Param("stm") Date stm, @Param("etm") Date etm);
default List<AutoCall> listByWcmIdAndCnnmOrderByLevelAsc(Integer wcmId, String cnnm) {
return selectList(
new QueryWrapper<AutoCall>()
.eq("wcm_id", wcmId)
.orderByAsc("level")
);
}
}

View File

@ -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<AutoCallPerson> {
default List<AutoCallPerson> listUnUploaded() {
return selectList(
new QueryWrapper<AutoCallPerson>()
.eq("status", AutoCallPerson.STATUS_DEFAULT)
.orderByAsc("id")
);
}
default boolean isAnySuccess(Integer taskId) {
return selectCount(
new QueryWrapper<AutoCallPerson>()
.eq("task_id", taskId)
.eq("status", AutoCallPerson.STATUS_PUT)
) > 0;
}
default boolean isAllFail(Integer taskId) {
long personCnt = selectCount(
new QueryWrapper<AutoCallPerson>()
.eq("task_id", taskId)
);
long failCnt = selectCount(
new QueryWrapper<AutoCallPerson>()
.eq("task_id", taskId)
.in("status", AutoCallPerson.STATUS_CALLED, AutoCallPerson.STATUS_CANCELLED)
);
return personCnt == failCnt;
}
}

View File

@ -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<AutoCallTask> {
List<QXWarning> listWarnsThatNotGeneratedTask();
default List<AutoCallTask> listShouldGenerate() {
return selectList(
new QueryWrapper<AutoCallTask>()
.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);
}

View File

@ -12,6 +12,7 @@ import java.util.List;
public interface QXWarningMapper extends BaseMapper<QXWarning> {
List<QXWarning> find(@Param("dto")QXWarning dto);
List<QXWarningVO> findByMsgIsNull();
List<QXWarningVO> findByAutoMsgIsNull();
IPage<QXWarning> page(@Param("page") IPage<QXWarning> page, @Param("dto") GroupWarningDto dto);

View File

@ -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<WarnCallMap> {
List<QXWarning> listWarnsThatNotGeneratedWcm();
default List<WarnCallMap> listUnGeneratedWcm() {
return selectList(
new QueryWrapper<WarnCallMap>()
.eq("should_generate", 1)
.eq("generated", 0)
.eq("err_step", 0)
.orderByAsc("id")
);
}
default List<WarnCallMap> listUnUploadedWcm() {
return selectList(
new QueryWrapper<WarnCallMap>()
.eq("generated", 1)
.eq("uploaded", 0)
.eq("err_step", 0)
.orderByAsc("id")
);
}
default List<WarnCallMap> listUnCalledWcm() {
return selectList(
new QueryWrapper<WarnCallMap>()
.eq("uploaded", 1)
.eq("called", 0)
.eq("err_step", 0)
.orderByAsc("id")
);
}
default List<WarnCallMap> listCallIsNotPutWcm() {
return selectList(
new QueryWrapper<WarnCallMap>()
.eq("uploaded", 1)
.eq("called", 1)
.eq("call_is_put", 0)
.eq("err_step", 0)
.orderByDesc("id")
);
}
}

View File

@ -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<WarningResponder> {
List<WarningResponder> getListByAdnm2(@Param("adnm") String adnm);
List<WarningResponder> listByCnnmAndLevelOrderByLevelAsc(@Param("cnnm") String cnnm, @Param("levels") List<Integer> levels);
}

View File

@ -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;
}

View File

@ -8,7 +8,7 @@ import lombok.Data;
* @date 202332008:57:22
*/
@Data
public class AddressBookOldDto{
public class WarningResponderDto {
@ApiModelProperty(value = "名字")
private String name;

View File

@ -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<AddressBook> implements Serializable {
@ApiModelProperty(value = "openid")
private String openid;
/**
*
*/
@TableField(value = "level")
@Schema(description = "1,2,3,4")
private Integer level;
}

View File

@ -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 {
/*
newresponderId,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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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<AutoCallPerson> callList;
}

View File

@ -160,4 +160,10 @@ public class ShAddressBook implements Serializable {
@TableField(exist = false)
List<ShCalls> list;
/**
*
*/
@TableField(value = "level")
@Schema(description = "1,2,3,4")
private Integer level;
}

View File

@ -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 {
/*
newwarnId,shouldGenerate,cnnm,requestId,custId,custName,warnName,warnTm,warnLevel,callContent,createTm
autoCallgenerated
uploaduploadRespMsg,uploaded
querycallIsPut,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<AutoCall> callList;
}

View File

@ -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<AddressBookOld> implements Serializable {
public class WarningResponder extends Model<WarningResponder> implements Serializable {
@TableId(value = "ID",type = IdType.AUTO)
@ -79,7 +79,7 @@ public class AddressBookOld extends Model<AddressBookOld> 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<AddressBookOld> 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<AddressBookOld> 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;
}

View File

@ -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<Record> 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;
}
}

View File

@ -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;
}

View File

@ -0,0 +1,37 @@
package com.whdc.model.entity.autocall;
import lombok.Data;
/**
* @author lyf
* @since 2025-06-20
*/
@Data
public class AICCCallRespWrapper<T> {
/*
{
"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;
}
}

View File

@ -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;
}

View File

@ -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<Cust> 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<String> _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<String> _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));
}
}

View File

@ -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<AddressBookOld> list;
private List<WarningResponder> list;
@ApiModelProperty(value = "键")
private String key;

View File

@ -25,4 +25,8 @@ public class WarnDppleVO {
@ApiParam("手机号")
@ApiModelProperty(value = "手机号")
private String phone;
@ApiParam("责任级别")
@ApiModelProperty(value = "责任级别")
private Integer level;
}

View File

@ -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<AutoCallTask> 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<AutoCallTask> query = new QueryWrapper<AutoCallTask>()
.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<AutoCallTask> pageResult = taskMapper.selectPage(
pageParam,
query
);
List<AutoCallTask> records = pageResult.getRecords();
if (records.size() > 0) {
for (AutoCallTask task : records) {
Integer taskId = task.getId();
List<AutoCallPerson> autoCallList = personMapper.selectList(
new QueryWrapper<AutoCallPerson>()
.eq("task_id", taskId)
);
task.setCallList(autoCallList);
}
}
return pageResult;
}
public Page<WarnCallMap> 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<WarnCallMap> query = new QueryWrapper<WarnCallMap>()
.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<WarnCallMap> pageResult = warnCallMapMapper.selectPage(
pageParam,
query
);
List<WarnCallMap> records = pageResult.getRecords();
if (records.size() > 0) {
for (WarnCallMap record : records) {
Integer wcmId = record.getId();
List<AutoCall> autoCallList = autoCallMapper.selectList(
new QueryWrapper<AutoCall>()
.eq("wcm_id", wcmId)
);
record.setCallList(autoCallList);
}
}
return pageResult;
}
public List<WarnCallMap> listCallIsNotPut() {
List<WarnCallMap> wcmList = warnCallMapMapper.listCallIsNotPutWcm();
for (WarnCallMap record : wcmList) {
Integer wcmId = record.getId();
List<AutoCall> autoCallList = autoCallMapper.selectList(
new QueryWrapper<AutoCall>()
.eq("wcm_id", wcmId)
);
record.setCallList(autoCallList);
}
return wcmList;
}
@Autowired
private AutoCallTaskMapper taskMapper;
@Autowired
private AutoCallPersonMapper personMapper;
@Autowired
private QXWarningMapper warningMapper;
}

View File

@ -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<QXWarning>()
.orderByDesc("WARNID")
.last("limit 1")
);
warn.setCtnm("恩施州");
warn.setCnnm("咸丰,");
List<WarnCallMap> wcmList = newWcmByWarn(warn);
WarnCallMap wcm = wcmList.get(0);
wcm.setCreateTm(new Date());
warnCallMapMapper.insert(wcm);
}
public void step1GenerateWarnCallMap() {
List<QXWarning> warnList = warnCallMapMapper.listWarnsThatNotGeneratedWcm();
for (QXWarning warn : warnList) {
try {
List<WarnCallMap> 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<WarnCallMap> 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<WarnCallMap> 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<WarnCallMap> wcmList = warnCallMapMapper.listUnGeneratedWcm();
for (WarnCallMap wcm : wcmList) {
List<AutoCall> 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<AutoCall> newAutoCall(WarnCallMap wcm) {
Integer wcmId = wcm.getId();
String cnnm = wcm.getCnnm();
List<AutoCall> existList = autoCallMapper.listByWcmIdAndCnnmOrderByLevelAsc(wcmId, cnnm);
if (existList != null && existList.size() > 0) {
return Collections.emptyList();
}
return newAutoCall(wcmId, cnnm, wcm.getWarnLevel());
}
private @NotNull List<AutoCall> newAutoCall(Integer wcmId, String cnnm, String warnLevelStr) {
//处理行政区划和预警级别橙色预警拨打12level的红色预警只拨打3level的
List<Integer> warnLevel = mapWarnSignalLevel(warnLevelStr);
List<WarningResponder> wrList = listWarningResponderByCnnmAndLevelOrderByLevelAsc(cnnm, warnLevel);
if (wrList.isEmpty()) return Collections.emptyList();
//处理县区包含高新区、开发区等情况
doSomethingWith(cnnm, wrList);
//按level从小到大排序
wrList.sort(Comparator.comparingInt(WarningResponder::getLevel));
List<AutoCall> 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<WarnCallMap> wcmList = warnCallMapMapper.listUnUploadedWcm();
if (wcmList == null || wcmList.isEmpty()) return;
for (WarnCallMap wcm : wcmList) {
Integer wcmId = wcm.getId();
List<AutoCall> autoCallList = autoCallMapper.listByWcmIdAndCnnmOrderByLevelAsc(wcmId, wcm.getCnnm());
if (autoCallList == null || autoCallList.isEmpty()) {
wcm.setErrStep(3);
wcm.setRemark("未找到呼叫列表");
warnCallMapMapper.updateById(wcm);
continue;
}
List<String> numbers = autoCallList.stream()
.map(AutoCall::getNumber)
.collect(Collectors.toList());
AICCUploadTask task = autoCallHelper.newTask(
wcm.getRequestId(),
wcm.getCustId(),
wcm.getCustName(),
wcm.getWarnContent(),
numbers
);
AICCCallRespWrapper<AICCCallRespTask> 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<WarnCallMap> wcmList = warnCallMapMapper.listUnCalledWcm();
if (wcmList.isEmpty()) return;
for (WarnCallMap wcm : wcmList) {
Integer wcmId = wcm.getId();
String requestId = wcm.getRequestId();
String custId = wcm.getCustId();
List<AutoCall> autoCallList = autoCallMapper.listByWcmIdAndCnnmOrderByLevelAsc(wcmId, wcm.getCnnm());
if (autoCallList == null || autoCallList.isEmpty()) {
wcm.setErrStep(4);
wcm.setRemark("未找到呼叫列表");
warnCallMapMapper.updateById(wcm);
continue;
}
AICCCallRespWrapper<AICCCallRespDetail> 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<AICCCallRespDetail.Record> 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<AICCCallRespDetail> 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<Integer> mapWarnSignalLevel(String warnSignalLevel) {
List<Integer> 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<AutoCallConfig>()
.eq("key", "enable")
);
}
/*
*/
public boolean isEnable() {
AutoCallConfig config = configMapper.selectOne(
new QueryWrapper<AutoCallConfig>()
.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<AutoCallConfig>()
.eq("key", "schedule")
.last("limit 1")
);
return config != null && "1".equals(config.getValue());
}
private static void doSomethingWith(String cnmm, List<WarningResponder> wrList) {
//预警包含高新等字符,可跳出
boolean skip = false;
for (String excludeName : excludeNames) {
if (cnmm.contains(excludeName)) {
skip = true;
break;
}
}
if (skip) {
return;
}
//预警不包含高新等字符,检查人员,保留不包含高新等字符的人员
Iterator<WarningResponder> 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<WarningResponder> listWarningResponderByCnnmAndLevelOrderByLevelAsc(String cnnm, List<Integer> levels) {
return warningResponderMapper.listByCnnmAndLevelOrderByLevelAsc(cnnm, levels);
}
}

View File

@ -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<QXWarning>()
.orderByDesc("WARNID")
.last("limit 1")
);
warn.setCtnm("恩施州");
warn.setCnnm("咸丰 ");
List<AutoCallTask> taskList = newTask(warn);
AutoCallTask task = taskList.get(0);
task.setStatus(0);
task.setCreateTm(new Date());
task.setWarnCnnm("咸丰 ");
taskMapper.insert(task);
generatePerson(task);
}
public List<AutoCallPerson> doCallTest() {
List<AutoCallPerson> 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<QXWarning> warnList = taskMapper.listWarnsThatNotGeneratedTask();
boolean enable = configMapper.isEnable();
for (QXWarning warn : warnList) {
try {
List<AutoCallTask> 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<AutoCallTask> 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<AutoCallTask> 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<AutoCallPerson> 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<Integer> mapWarnSignalLevel(String warnSignalLevel) {
List<Integer> 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<WarningResponder> wrList) {
//预警包含高新等字符,可跳出
boolean skip = false;
for (String excludeName : excludeNames) {
if (cnmm.contains(excludeName)) {
skip = true;
break;
}
}
if (skip) {
return;
}
//预警不包含高新等字符,检查人员,保留不包含高新等字符的人员
Iterator<WarningResponder> 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<AutoCallPerson> newPerson(AutoCallTask task) {
String cnnm = task.getWarnCnnm();
long millis = task.getCreateTm().getTime();
List<Integer> warnLevels = mapWarnSignalLevel(task.getWarnLevel());
List<WarningResponder> wrList = warningResponderMapper.listByCnnmAndLevelOrderByLevelAsc(cnnm, warnLevels);
if (wrList.isEmpty()) return Collections.emptyList();
//处理县区包含高新区、开发区等情况
doSomethingWith(cnnm, wrList);
//按level从小到大排序
wrList.sort(Comparator.comparingInt(WarningResponder::getLevel));
List<AutoCallPerson> 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<AutoCallPerson> step2GetOneUnUploadedPerson() {
List<AutoCallPerson> personList = personMapper.listUnUploaded();
if (personList == null || personList.isEmpty()) return Collections.emptyList();
Map<Integer, List<AutoCallPerson>> personGroup = personList.stream().collect(Collectors.groupingBy(AutoCallPerson::getTaskId));
List<AutoCallPerson> 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<String> numbers = Collections.singletonList(person.getUploadNumber());
AICCUploadTask uploadTask = aiccHelper.newTask(
person.getUploadRequestId(),
person.getUploadCustId(),
person.getUploadCustName(),
person.getUploadContent(),
numbers
);
AICCCallRespWrapper<AICCCallRespTask> 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<AICCCallRespDetail> detail = aiccHelper.apiGetTaskCallDetail(person.getUploadRequestId(), person.getUploadCustId());
List<AICCCallRespDetail.Record> 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<AutoCallPerson>() {{
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;
}
}

View File

@ -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<AddressBookOld> {
List<AddressBookOld> getListByAdnm2(String cnnm2);
public interface IWarningResponderService extends IService<WarningResponder> {
List<WarningResponder> getListByAdnm2(String cnnm2);
}

View File

@ -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;
/**
* <p>
*
* </p>
*
* @author xusan
* @date 2024-05-11
*/
@Service
public class AddressBookOldServiceImpl extends ServiceImpl<AddressBookOldMapper, AddressBookOld> implements IAddressBookOldService {
@Override
public List<AddressBookOld> getListByAdnm2(String adnm) {
return baseMapper.getListByAdnm2(adnm);
}
}

View File

@ -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;
/**
* <p>
*
* </p>
*
* @author xusan
* @date 2024-05-11
*/
@Service
public class WarningResponderServiceImpl extends ServiceImpl<WarningResponderMapper, WarningResponder> implements IWarningResponderService {
@Override
public List<WarningResponder> getListByAdnm2(String adnm) {
return baseMapper.getListByAdnm2(adnm);
}
}

View File

@ -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 6PKCS5Padding
* Bouncy CastlePKCS7Padding
*/
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
* AES128192256javaAES128
* 192256oraclejdk"Additional Resources"
* "Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files",[DOWNLOAD]
* local_policy.jarUS_export_policy.jarjdkjre/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);
}
}

View File

@ -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<String> 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<String, String> 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<AICCCallRespWrapper<AICCLogin>> type = new TypeReference<AICCCallRespWrapper<AICCLogin>>() {
};
AICCCallRespWrapper<AICCLogin> 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<AICCCallRespTask> apiUploadCallData(AICCUploadTask data) throws GeneralSecurityException, UnsupportedEncodingException {
return apiUploadCallData(data, getToken());
}
public AICCCallRespWrapper<AICCCallRespTask> 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<String, String> 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<AICCCallRespWrapper<AICCCallRespTask>> type = new TypeReference<AICCCallRespWrapper<AICCCallRespTask>>() {
};
AICCCallRespWrapper<AICCCallRespTask> 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<AICCCallRespDetail> apiGetTaskCallDetail(String requestId, String custId) {
JSONObject request = new JSONObject();
request.put("requestId", requestId);
request.put("custId", custId);
Map<String, String> 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<AICCCallRespWrapper<AICCCallRespDetail>> type = new TypeReference<AICCCallRespWrapper<AICCCallRespDetail>>() {
};
AICCCallRespWrapper<AICCCallRespDetail> 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<String> 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;
}
}

View File

@ -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<String> 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<AICCCallRespWrapper<AICCLogin>> type = new TypeReference<AICCCallRespWrapper<AICCLogin>>() {
};
AICCCallRespWrapper<AICCLogin> 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<AICCCallRespTask> apiUploadCallData(AICCUploadTask data) {
return apiUploadCallData(data, getToken());
}
public AICCCallRespWrapper<AICCCallRespTask> 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<AICCCallRespWrapper<AICCCallRespTask>> type = new TypeReference<AICCCallRespWrapper<AICCCallRespTask>>() {
};
AICCCallRespWrapper<AICCCallRespTask> 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<AICCCallRespDetail> 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<AICCCallRespWrapper<AICCCallRespDetail>> type = new TypeReference<AICCCallRespWrapper<AICCCallRespDetail>>() {
};
AICCCallRespWrapper<AICCCallRespDetail> 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<String> 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;
}
}

View File

@ -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 {

View File

@ -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<String, String> params, @Nullable Map<String, String> headers) {
if (url.isEmpty()) {
return "url为空";
}
Request.Builder requestBuilder = new Request.Builder()
.url(url)
.get();
//header
if (headers != null) {
for (Map.Entry<String, String> 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<String, String> headers) {
return postJsonString(url, json, headers, StandardCharsets.UTF_8);
}
public @NotNull String postJsonString(@NotNull String url, @Nullable String json, @Nullable Map<String, String> 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<String, String> 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<String, Object> params, @Nullable Map<String, String> headers) {
return postFormData(url, params, headers, StandardCharsets.UTF_8);
}
public @NotNull String postFormData(@NotNull String url, @Nullable Map<String, Object> params, @Nullable Map<String, String> 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<String, Object> 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<String, String> 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<String, Object> params, @Nullable Map<String, String> headers) {
return postFormUrlEncoded(url, params, headers, StandardCharsets.UTF_8);
}
public @NotNull String postFormUrlEncoded(@NotNull String url, @Nullable Map<String, Object> params, @Nullable Map<String, String> 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<String, Object> 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<String, String> 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();
}
}
}

View File

@ -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);

View File

@ -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<String> phones, String content) {
return send(phones, content, "201");
}
private String send(List<String> 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<String, Object> 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<Integer, String> 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);
}
}
}

View File

@ -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
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

View File

@ -0,0 +1,2 @@
log4j.rootLogger=ERROR
log4j.logger.org.apache.http=ERROR

View File

@ -34,7 +34,7 @@
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<!--此日志appender是为开发使用只配置最底级别控制台输出的日志级别是大于或等于此级别的日志信息-->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>debug</level>
<level>info</level>
</filter>
<encoder>
<Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
@ -199,15 +199,16 @@
<logger name="o.a.http.impl.execchain.MainClientExec" level="error" additivity="false"/>
<logger name=".i.c.DefaultHttpClientConnectionOperator" level="error" additivity="false"/>
<logger name=".a.http.impl.execchain.MainClientExec" level="error" additivity="false"/>
<logger name="org.apache.http" level="error" additivity="false"/>
<!--
ERROR [ModelSpecification.spec] At least one type of specification is required
https://github.com/jhipster/generator-jhipster/pull/15004/files
-->
<logger name="Validator" level="INFO"/>
<root level="debug">
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="DEBUG_FILE"/>
<!-- <appender-ref ref="DEBUG_FILE"/>-->
<appender-ref ref="INFO_FILE"/>
<appender-ref ref="WARN_FILE"/>
<appender-ref ref="ERROR_FILE"/>

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.whdc.mapper.AutoCallMapper">
<select id="getListByLevelAndStatus" resultType="com.whdc.model.entity.AutoCall">
select * from AUTOCALL A
where 1=1
<if test="level != null">
and A.LEVEL = #{adnm}
</if>
<if test="status != null and called == null">
and A.STATUS = #{status}
</if>
<if test="called != null and called == true">
and A.STATUS is not null
</if>
<if test="called != null and called == false">
and A.STATUS is null
</if>
<if test="stm != null and etm != null">
AND A._create_tm BETWEEN #{stm} AND #{etm}
</if>
</select>
</mapper>

View File

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.whdc.mapper.AutoCallTaskMapper">
<select id="listWarnsThatNotGeneratedTask" resultType="com.whdc.model.entity.QXWarning">
SELECT
Q.*
FROM
FXKH_TXL.QXWARNING Q
WHERE
NOT EXISTS (
SELECT 1
FROM FXKH_TXL.AUTOCALL_TASK TASK
WHERE TASK.WARN_ID = Q.WARNID
)
AND Q.PUBLISH_TIME > (
SELECT MAX(TASK_LATEST.warn_tm)
FROM FXKH_TXL.AUTOCALL_TASK TASK_LATEST
)
ORDER BY Q.WARNID ASC;
</select>
<select id="getList" resultType="com.whdc.model.entity.AutoCallTask">
SELECT
*
FROM
FXKH_TXL.AUTOCALL_TASK TASK
WHERE
TASK.AUTOCALL_ID IS NULL
AND TASK.SHOULD_GENERATE = 1
AND TASK.GENERATED = 0
</select>
</mapper>

View File

@ -29,6 +29,17 @@
WF.ID IS NULL
ORDER BY Q.WARNID DESC
</select>
<select id="findByAutoMsgIsNull" resultType="com.whdc.model.vo.QXWarningVO">
SELECT
Q.*, 0 STATUS
FROM
FXKH_TXL.QXWARNING Q
LEFT JOIN FXKH_TXL.AUTOCALL WF ON WF.WARN_ID = Q.WARNID
WHERE
WF.ID IS NULL
AND Q.PUBLISH_TIME >= '2025-06-19 09:00:00'
ORDER BY Q.WARNID DESC
</select>
<select id="page" resultType="com.whdc.model.vo.QXWarningVO">
SELECT
Q.*,IF( WF.ID IS NOT NULL,1,0) STATUS

View File

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.whdc.mapper.WarnCallMapMapper">
<select id="listWarnsThatNotGeneratedWcm" resultType="com.whdc.model.entity.QXWarning">
SELECT
Q.*
FROM
FXKH_TXL.QXWARNING Q
WHERE
NOT EXISTS (
SELECT 1
FROM FXKH_TXL.WARN_CALL_MAP WCM
WHERE WCM.WARN_ID = Q.WARNID
)
AND Q.PUBLISH_TIME > (
SELECT MAX(WCM_LATEST.warn_tm)
FROM FXKH_TXL.WARN_CALL_MAP WCM_LATEST
)
ORDER BY Q.WARNID ASC;
</select>
<select id="getWCMList" resultType="com.whdc.model.entity.AutoCall">
SELECT
*
FROM
FXKH_TXL.WARN_CALL_MAP WCM
WHERE
WCM.AUTOCALL_ID IS NULL
AND WCM.SHOULD_GENERATE = 1
AND WCM.GENERATED = 0
</select>
</mapper>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.whdc.mapper.AddressBookOldMapper">
<mapper namespace="com.whdc.mapper.WarningResponderMapper">
<!-- AND AB.ADCD ${dto.v} #{dto.adcd}-->
<select id="page" resultType="com.whdc.model.vo.AddressBookVo">
@ -89,7 +89,7 @@
LEFT JOIN ADINFO A ON AB.ADCD = A.ADCD
WHERE AB.OPENID = #{id}
</select>
<select id="getListByAdnm2" resultType="com.whdc.model.entity.AddressBookOld">
<select id="getListByAdnm2" resultType="com.whdc.model.entity.WarningResponder">
select * from ADDRESS_BOOK_OLD A
where 1=1 and
<if test="adnm != null and adnm != '' ">
@ -97,5 +97,18 @@
</if>
</select>
<select id="listByCnnmAndLevelOrderByLevelAsc" resultType="com.whdc.model.entity.WarningResponder">
select * from ADDRESS_BOOK_OLD A
where 1=1
<if test="cnnm != null and cnnm != '' ">
<!-- and A.CNNM like CONCAT('%',#{cnnm},'%')-->
and A.CNNM = #{cnnm}
</if>
and level in
<foreach collection="levels" item="item" index="index" open="(" separator="," close=")">
#{item}
</foreach>
order by level asc
</select>
</mapper>