diff --git a/.gitignore b/.gitignore index 6f161d7..d102438 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,6 @@ yarn-debug.log* yarn-error.log* pnpm-debug.log* - /target/ !.mvn/wrapper/maven-wrapper.jar @@ -43,4 +42,5 @@ yus-water/logs/ *.vm **/target/** -/img \ No newline at end of file +/img +runtime-lib/ \ No newline at end of file diff --git a/pom.xml b/pom.xml index 35b33b5..aec9730 100644 --- a/pom.xml +++ b/pom.xml @@ -223,23 +223,51 @@ 4.6.0 + + com.squareup.okhttp3 + okhttp + 4.12.0 + + + + + + + + + + + + + + + + + - org.springframework.boot - spring-boot-maven-plugin - 2.7.5 + org.apache.maven.plugins + maven-dependency-plugin + 3.5.0 + copy-dependencies - repackage + copy-dependencies + + true + ${project.basedir}/runtime-lib + false + false + true + - \ No newline at end of file diff --git a/src/main/java/com/whdc/FxkhTxlApiApplication.java b/src/main/java/com/whdc/FxkhTxlApiApplication.java index 9d3ffe9..0e10d52 100644 --- a/src/main/java/com/whdc/FxkhTxlApiApplication.java +++ b/src/main/java/com/whdc/FxkhTxlApiApplication.java @@ -8,8 +8,13 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCaching; import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.web.servlet.config.annotation.EnableWebMvc; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; +import javax.script.ScriptException; + /** * @author 李赛 * @date 2022-06-26 0:17 @@ -21,6 +26,7 @@ import org.springframework.web.servlet.config.annotation.EnableWebMvc; @EnableScheduling @SpringBootApplication @MapperScan("com.whdc.mapper") +@EnableTransactionManagement public class FxkhTxlApiApplication { public static void main(String[] args) { diff --git a/src/main/java/com/whdc/component/AutoCallScheduled.java b/src/main/java/com/whdc/component/AutoCallScheduled.java new file mode 100644 index 0000000..4d30481 --- /dev/null +++ b/src/main/java/com/whdc/component/AutoCallScheduled.java @@ -0,0 +1,59 @@ +package com.whdc.component; + +import com.whdc.service.AutoCallTaskService; +import com.whdc.utils.AutoCallHelper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Profile; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; + +/** + * @author lyf + * @since 2025-06-20 + */ +@Component +@Slf4j +@Profile("deprecated") +public class AutoCallScheduled { + + @Autowired + private AutoCallTaskService autoCallTaskService; + @Autowired + private AutoCallHelper autoCallHelper; + + @PostConstruct + public void getToken() throws Exception { + autoCallHelper.getToken(); + } + + @Scheduled(cron = "*/3 * * * * ?") + public void step1() { + if (autoCallTaskService.isScheduled()) { + autoCallTaskService.step1GenerateWarnCallMap(); + } + } + + @Scheduled(cron = "*/3 * * * * ?") + public void step2() { + if (autoCallTaskService.isScheduled()) { + autoCallTaskService.step2GenerateAutoCallByWcm(); + } + } + + @Scheduled(cron = "*/10 * * * * ?") + public void step3() { + if (autoCallTaskService.isScheduled()) { + autoCallTaskService.step3UploadCallTask(); + } + } + + @Scheduled(cron = "*/10 * * * * ?") + public void step4() { + if (autoCallTaskService.isScheduled()) { + autoCallTaskService.step4QueryTaskResult(); + } + } +} diff --git a/src/main/java/com/whdc/component/AutoCallTaskScheduled.java b/src/main/java/com/whdc/component/AutoCallTaskScheduled.java new file mode 100644 index 0000000..801814d --- /dev/null +++ b/src/main/java/com/whdc/component/AutoCallTaskScheduled.java @@ -0,0 +1,95 @@ +package com.whdc.component; + +import com.whdc.mapper.AutoCallConfigMapper; +import com.whdc.model.entity.AutoCallPerson; +import com.whdc.service.AutoCallTaskService2; +import com.whdc.utils.AutoCallHelper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.annotation.Profile; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.TaskScheduler; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.stream.Collectors; + +/** + * @author lyf + * @since 2025-06-20 + */ +@Component +@Slf4j +@Profile("default") +public class AutoCallTaskScheduled { + + @Autowired + private AutoCallTaskService2 autoCallTaskService; + @Autowired + private AutoCallConfigMapper configMapper; + @Autowired + private AutoCallHelper autoCallHelper; + @Autowired + private TaskScheduler taskScheduler; + + private AtomicBoolean initialized = new AtomicBoolean(false); + + @EventListener(ApplicationReadyEvent.class) + public void initialize() throws Exception { + autoCallHelper.getToken(); + initialized.set(true); + log.info("AutoCallTaskScheduled初始化完成"); + } + + @Scheduled(cron = "*/3 * * * * ?") + public void generateLoop() { + if (configMapper.isScheduled()) { + autoCallTaskService.step1GenerateTask(); + } + } + + @Scheduled(cron = "*/3 * * * * ?") + public void callLoop() { + if (!initialized.get()) { + return; + } + if (!configMapper.isScheduled()) { + return; + } + log.info("AutoCallTaskScheduled callLoop"); + List personList = autoCallTaskService.step2GetOneUnUploadedPerson(); + log.info("AutoCallTaskScheduled {}个外呼人, {}", personList.size(), personList.stream().map(AutoCallPerson::getUploadCustName).collect(Collectors.toList())); + try { + for (AutoCallPerson person : personList) { + autoCallTaskService.step3UploadAICCTask(person); + + int pendingDuration = 60 * 1000 * 2; + int loopGap = 1000; + boolean success = false; + while (pendingDuration > 0) { + try { + Thread.sleep(loopGap); + } catch (InterruptedException ignore) { + Thread.currentThread().interrupt(); + return; + } + pendingDuration -= loopGap; + + success = autoCallTaskService.step4QueryAICCTaskResult(person); + if (success) break; + } + + if (!success) { + autoCallTaskService.markPersonDetailQueryTimeout(person); + } + } + } catch (Exception e) { + log.error("AutoCallTaskScheduled callLoop error", e); + } + } + +} diff --git a/src/main/java/com/whdc/component/MyPostConstruct.java b/src/main/java/com/whdc/component/MyPostConstruct.java index 9422c67..f98b8b6 100644 --- a/src/main/java/com/whdc/component/MyPostConstruct.java +++ b/src/main/java/com/whdc/component/MyPostConstruct.java @@ -19,6 +19,7 @@ import org.apache.commons.lang3.StringUtils; import org.apache.http.HttpStatus; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Profile; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.Scheduled; @@ -40,6 +41,7 @@ import static com.whdc.controller.QXWarnController.QX_TEMP_REDIS_KEY; */ @Component @Slf4j +@Profile("default") public class MyPostConstruct { @Autowired diff --git a/src/main/java/com/whdc/config/InterceptorConfig.java b/src/main/java/com/whdc/config/InterceptorConfig.java index d3a9ff7..2c03bc8 100644 --- a/src/main/java/com/whdc/config/InterceptorConfig.java +++ b/src/main/java/com/whdc/config/InterceptorConfig.java @@ -1,5 +1,7 @@ package com.whdc.config; +import okhttp3.OkHttpClient; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.EnableWebMvc; @@ -13,7 +15,11 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @EnableWebMvc @Configuration public class InterceptorConfig implements WebMvcConfigurer { - + @Bean + public OkHttpClient okHttpClient() { + return new OkHttpClient.Builder() + .build(); + } @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/**").addResourceLocations("classpath:/static/"); diff --git a/src/main/java/com/whdc/config/MybatisInterceptor.java b/src/main/java/com/whdc/config/MybatisInterceptor.java index d345fda..07c3981 100644 --- a/src/main/java/com/whdc/config/MybatisInterceptor.java +++ b/src/main/java/com/whdc/config/MybatisInterceptor.java @@ -42,15 +42,15 @@ public class MybatisInterceptor implements Interceptor { continue; } - if (field.getType().equals(String.class)) { - field.setAccessible(true); - Object o = field.get(parameter); - field.setAccessible(false); - String newVal = o == null ? "" : String.valueOf(o).trim(); - field.setAccessible(true); - field.set(parameter, newVal); - field.setAccessible(false); - } +// if (field.getType().equals(String.class)) { +// field.setAccessible(true); +// Object o = field.get(parameter); +// field.setAccessible(false); +// String newVal = o == null ? "" : String.valueOf(o).trim(); +// field.setAccessible(true); +// field.set(parameter, newVal); +// field.setAccessible(false); +// } // 注入创建时间 if ("createtime".equals(field.getName()) || ("createTime".equals(field.getName()))) { diff --git a/src/main/java/com/whdc/controller/AutoCallController.java b/src/main/java/com/whdc/controller/AutoCallController.java new file mode 100644 index 0000000..64e1f1e --- /dev/null +++ b/src/main/java/com/whdc/controller/AutoCallController.java @@ -0,0 +1,91 @@ +package com.whdc.controller; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.whdc.model.dto.AutoCallDto; +import com.whdc.model.entity.AutoCallPerson; +import com.whdc.model.entity.AutoCallTask; +import com.whdc.model.entity.WarnCallMap; +import com.whdc.service.AutoCallApiService; +import com.whdc.service.AutoCallTaskService; +import com.whdc.service.AutoCallTaskService2; +import com.whdc.utils.ResultJson; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.text.ParseException; +import java.util.List; + +/** + * @author lyf + * @since 2025-06-17 + */ +@RestController +@RequestMapping("/autocall") +public class AutoCallController { + @Autowired + private AutoCallApiService autoCallApiService; + @Autowired + private AutoCallTaskService autoCallTaskService; + @Autowired + private AutoCallTaskService2 autoCallTaskService2; + + @GetMapping("/doCallTest") + public ResultJson doCallTest() throws ParseException { + autoCallTaskService.generateFakeCall(); + return ResultJson.ok("resp"); + } + + @GetMapping("/doGenerateTest2") + public ResultJson doGenerateTest2() throws ParseException { + autoCallTaskService2.generateFakeCall(); + return ResultJson.ok("resp"); + } + + @GetMapping("/doCallTest2") + public ResultJson> doCallTest2() throws ParseException { + List personList = autoCallTaskService2.doCallTest(); + return ResultJson.ok(personList); + } + + @GetMapping("/getToken") + public ResultJson getToken() { + return ResultJson.ok(autoCallTaskService.getToken()); + } + + @GetMapping("/queryTaskResult") + public ResultJson queryTaskResult(@RequestParam("requestId") String requestId, @RequestParam("custId") String custId) { + return ResultJson.ok(autoCallTaskService.queryTaskResult(requestId, custId)); + } + + @PostMapping("/page") + public ResultJson> newList(@RequestBody AutoCallDto dto) { + return ResultJson.ok(autoCallApiService.page(dto)); + } + + @PostMapping("/page2") + public ResultJson> page2(@RequestBody AutoCallDto dto) { + return ResultJson.ok(autoCallApiService.page2(dto)); + } + + @GetMapping("/listCallIsNotPut") + public ResultJson> listCallIsNotPut() { + return ResultJson.ok(autoCallApiService.listCallIsNotPut()); + } + + @GetMapping("/isEnable") + public ResultJson isEnable() { + return ResultJson.ok(autoCallTaskService.isEnable()); + } + + @GetMapping("/setEnable") + public ResultJson setEnable(@RequestParam("enable") Boolean enable) { + autoCallTaskService.setEnable(enable); + return ResultJson.ok(true); + } + + @GetMapping("/setCallIsPut") + public ResultJson setCallIsPut(@RequestParam("taskId") Integer taskId) { + autoCallTaskService.setCallIsPut(taskId); + return ResultJson.ok(true); + } +} diff --git a/src/main/java/com/whdc/controller/QXWarnController.java b/src/main/java/com/whdc/controller/QXWarnController.java index 3eb6302..063fe58 100644 --- a/src/main/java/com/whdc/controller/QXWarnController.java +++ b/src/main/java/com/whdc/controller/QXWarnController.java @@ -7,7 +7,7 @@ import com.whdc.exception.MyException; import com.whdc.model.dto.ApiDto; import com.whdc.model.dto.GroupWarningDto; import com.whdc.model.dto.WarnDppleDto; -import com.whdc.model.entity.AddressBookOld; +import com.whdc.model.entity.WarningResponder; import com.whdc.model.entity.QXWarning; import com.whdc.model.entity.WarnMsgFB; import com.whdc.model.vo.*; @@ -54,7 +54,7 @@ public class QXWarnController { @Autowired private IAddressBookService addressBookService; @Autowired - private IAddressBookOldService addressBookOldService; + private IWarningResponderService warningResponderService; @Autowired private IAdinfoService adinfoService; @Autowired @@ -259,9 +259,9 @@ public class QXWarnController { // String adcd = adinfoService.getAdcdByAdnm2(cnnm2);//根据区县的名字找到对应的adcd // System.out.println(adcd); if (StringUtils.isNotBlank(cnnm2)) { - List dpples = addressBookOldService.getListByAdnm2(cnnm2); + List dpples = warningResponderService.getListByAdnm2(cnnm2); System.out.println("dpple个数:" + dpples.size()); - for (AddressBookOld dpple : dpples) { + for (WarningResponder dpple : dpples) { dpple.decryptPhone(); WarnDppleVO warndpple = new WarnDppleVO(); warndpple.setCtnm(dto.getCtnm()); @@ -269,6 +269,7 @@ public class QXWarnController { warndpple.setName(dpple.getName()); warndpple.setPosition(dpple.getPosition()); warndpple.setPhone(dpple.getPhone()); + warndpple.setLevel(dpple.getLevel()); warnDpples.add(warndpple); } } else { diff --git a/src/main/java/com/whdc/controller/WarnMsgFBController.java b/src/main/java/com/whdc/controller/WarnMsgFBController.java index c37aa40..07d28a9 100644 --- a/src/main/java/com/whdc/controller/WarnMsgFBController.java +++ b/src/main/java/com/whdc/controller/WarnMsgFBController.java @@ -1,11 +1,11 @@ package com.whdc.controller; import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; -import com.whdc.model.entity.AddressBookOld; +import com.whdc.model.entity.WarningResponder; import com.whdc.model.entity.QXWarning; import com.whdc.model.entity.WarnMsgFB; import com.whdc.model.vo.ExcelOldDataVo; -import com.whdc.service.IAddressBookOldService; +import com.whdc.service.IWarningResponderService; import com.whdc.service.IQXWarningService; import com.whdc.service.IWarnMsgFBService; import com.whdc.utils.ExcelCommon; @@ -37,7 +37,7 @@ public class WarnMsgFBController { private IQXWarningService qxService; @Autowired - private IAddressBookOldService oldService; + private IWarningResponderService oldService; //增 @ApiOperation(value = "新增") @PostMapping(value = "/add") @@ -76,8 +76,8 @@ public class WarnMsgFBController { List qxWarns = ExcelCommon.importExcel(file,1, 0, 1, QXWarning.class); Map> qxByID = qxWarns.stream().collect(Collectors.groupingBy(QXWarning::getEffectId, Collectors.toList())); - List olds = oldService.list(); - Map> txlByName = olds.stream().collect(Collectors.groupingBy(AddressBookOld::getName, Collectors.toList())); + List olds = oldService.list(); + Map> txlByName = olds.stream().collect(Collectors.groupingBy(WarningResponder::getName, Collectors.toList())); warnMsgs.forEach(o -> { List qxWarnings = qxByID.get(o.getEffectId()); @@ -87,7 +87,7 @@ public class WarnMsgFBController { .setPublishTime(qxWarning.getPublishTime()) .setWarnid(qxWarning.getWarnid()); String name = o.getCalledPerson(); - List txl = txlByName.get(name); + List txl = txlByName.get(name); if (CollectionUtils.isNotEmpty(txl)){ o.setCalledPhone(txl.get(0).getPhone()); }else{ diff --git a/src/main/java/com/whdc/controller/AddressBookOldController.java b/src/main/java/com/whdc/controller/WarningResponderController.java similarity index 66% rename from src/main/java/com/whdc/controller/AddressBookOldController.java rename to src/main/java/com/whdc/controller/WarningResponderController.java index 55124d0..0e67cf6 100644 --- a/src/main/java/com/whdc/controller/AddressBookOldController.java +++ b/src/main/java/com/whdc/controller/WarningResponderController.java @@ -4,11 +4,11 @@ import com.alibaba.fastjson.JSON; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; import com.whdc.exception.MyException; -import com.whdc.model.dto.AddressBookOldDto; -import com.whdc.model.entity.AddressBookOld; +import com.whdc.model.dto.WarningResponderDto; +import com.whdc.model.entity.WarningResponder; import com.whdc.model.vo.ExcelDataVo; import com.whdc.model.vo.ExcelOldDataVo; -import com.whdc.service.IAddressBookOldService; +import com.whdc.service.IWarningResponderService; import com.whdc.utils.ExcelCommon; import com.whdc.utils.ResultJson; import io.swagger.annotations.Api; @@ -44,12 +44,12 @@ import static com.whdc.model.MyConstant.REDIS_KEY; @Slf4j @Api(tags = "web通讯录 - Controller") @RestController -@RequestMapping("/addressbookOld") +@RequestMapping({"/addressbookOld", "/warningResponder"}) @Transactional -public class AddressBookOldController { +public class WarningResponderController { @Autowired - private IAddressBookOldService service; + private IWarningResponderService service; public static final String ADDRESS_BOOK_REDIS_KEY = REDIS_KEY + "addressbookOld"; public static final String ADDRESS_BOOK_TEMP_REDIS_KEY = ADDRESS_BOOK_REDIS_KEY + "temp:"; @@ -62,23 +62,23 @@ public class AddressBookOldController { @ApiOperation(value = "查询所有联系人") @PostMapping(value = "find") @Cacheable(value = ADDRESS_BOOK_REDIS_KEY, key = "#root.method.name+':'+#dto.toString()") - public ResultJson> find(@RequestBody AddressBookOldDto dto) { + public ResultJson> find(@RequestBody WarningResponderDto dto) { - LambdaQueryChainWrapper query = service.lambdaQuery(); + LambdaQueryChainWrapper query = service.lambdaQuery(); String name = dto.getName(); if (StringUtils.isNotBlank(name)){ - query.like(AddressBookOld::getName, name); + query.like(WarningResponder::getName, name); } String phone = dto.getPhone(); if (StringUtils.isNotBlank(phone)){ - query.like(AddressBookOld::getPhone, phone); + query.like(WarningResponder::getPhone, phone); } - List data = query.list(); + List data = query.list(); - data = data.stream().map(AddressBookOld::decryptPhone).collect(Collectors.toList()); + data = data.stream().map(WarningResponder::decryptPhone).collect(Collectors.toList()); return ResultJson.ok(data); @@ -89,21 +89,22 @@ public class AddressBookOldController { @GetMapping(value = "downloadExcel") public void downloadExcel( HttpServletResponse response) { - List list = service.lambdaQuery() + List list = service.lambdaQuery() .list(); - for (AddressBookOld o : list) { + for (WarningResponder o : list) { o.decryptPhone(); } + list.stream().forEach(vo -> vo.setLevelLabel(dictMappingReverse(vo.getLevel()))); ExcelCommon.exportExcel(list, - null, "防汛抗旱通讯录", AddressBookOld.class, "湖北省防汛抗旱通信录模版" + LocalDateTime.now() + ".xlsx", - response); + null, "防汛抗旱通讯录", WarningResponder.class, "湖北省防汛抗旱通信录模版" + LocalDateTime.now() + ".xlsx", + response); } @ApiOperation(value = "解析文件数据") @PostMapping(value = "getExcelData") public ResultJson getExcelData(MultipartFile file) { - List appends = ExcelCommon.importExcel(file, 0, 1, AddressBookOld.class); + List appends = ExcelCommon.importExcel(file, 0, 1, WarningResponder.class); ExcelOldDataVo excelDataVo = new ExcelOldDataVo(); if (CollectionUtils.isNotEmpty(appends)){ @@ -129,7 +130,7 @@ public class AddressBookOldController { if (StringUtils.isBlank(s)){ throw new MyException("文件已过期"); } - List appends = JSON.parseArray(s, AddressBookOld.class); + List appends = JSON.parseArray(s, WarningResponder.class); if (CollectionUtils.isEmpty(appends)) { throw new MyException("文件数据为空"); @@ -148,7 +149,7 @@ public class AddressBookOldController { // 新增创建时间 Date date = new Date(); - appends = appends.stream().map(vo -> vo.setCreateTime(date).encryptPhone()) + appends = appends.stream().map(vo -> vo.setCreateTime(date).setLevel(dictMapping(vo.getLevelLabel())).encryptPhone()) .collect(Collectors.toList()); if (!service.saveBatch(appends)) { @@ -158,4 +159,39 @@ public class AddressBookOldController { return ResultJson.ok("数据新增成功"); } + private Integer dictMapping(String label) { + if (label == null) { + return null; + } + switch (label) { + case "分管副市(县)长": + return 1; + case "常务副市(县)长": + return 2; + case "市(县)长": + return 3; + case "书记": + return 4; + default: + return null; + } + } + + private String dictMappingReverse(Integer level) { + if (level == null) { + return null; + } + switch (level) { + case 1: + return "分管副市(县)长"; + case 2: + return "常务副市(县)长"; + case 3: + return "市(县)长"; + case 4: + return "书记"; + default: + return null; + } + } } diff --git a/src/main/java/com/whdc/mapper/AddressBookOldMapper.java b/src/main/java/com/whdc/mapper/AddressBookOldMapper.java deleted file mode 100644 index 1ff2c26..0000000 --- a/src/main/java/com/whdc/mapper/AddressBookOldMapper.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.whdc.mapper; - -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import com.whdc.model.entity.AddressBookOld; -import org.apache.ibatis.annotations.Param; - -import java.util.List; - -/** - * @author xusan - * @date 2024-05-11 - */ -public interface AddressBookOldMapper extends BaseMapper { - - - List getListByAdnm2(@Param("adnm")String adnm); -// List getListByAdnm(@Param("adnm") String adnm); -} \ No newline at end of file diff --git a/src/main/java/com/whdc/mapper/AutoCallConfigMapper.java b/src/main/java/com/whdc/mapper/AutoCallConfigMapper.java new file mode 100644 index 0000000..e13dcf1 --- /dev/null +++ b/src/main/java/com/whdc/mapper/AutoCallConfigMapper.java @@ -0,0 +1,29 @@ +package com.whdc.mapper; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.whdc.model.entity.AutoCallConfig; + +/** + * @author lyf + * @since 2025-06-19 + */ +public interface AutoCallConfigMapper extends BaseMapper { + default boolean isEnable() { + AutoCallConfig config = selectOne( + new QueryWrapper() + .eq("key", "enable") + .last("limit 1") + ); + return config != null && "1".equals(config.getValue()); + } + + default boolean isScheduled() { + AutoCallConfig config = selectOne( + new QueryWrapper() + .eq("key", "schedule") + .last("limit 1") + ); + return config != null && "1".equals(config.getValue()); + } +} diff --git a/src/main/java/com/whdc/mapper/AutoCallMapper.java b/src/main/java/com/whdc/mapper/AutoCallMapper.java new file mode 100644 index 0000000..5519b45 --- /dev/null +++ b/src/main/java/com/whdc/mapper/AutoCallMapper.java @@ -0,0 +1,25 @@ +package com.whdc.mapper; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.whdc.model.entity.AutoCall; +import org.apache.ibatis.annotations.Param; + +import java.util.Date; +import java.util.List; + +/** + * @author lyf + * @since 2025-06-14 + */ +public interface AutoCallMapper extends BaseMapper { + List getListByLevelAndStatus(@Param("level") Integer level, @Param("status") String status, @Param("called") Boolean called, @Param("stm") Date stm, @Param("etm") Date etm); + + default List listByWcmIdAndCnnmOrderByLevelAsc(Integer wcmId, String cnnm) { + return selectList( + new QueryWrapper() + .eq("wcm_id", wcmId) + .orderByAsc("level") + ); + } +} diff --git a/src/main/java/com/whdc/mapper/AutoCallPersonMapper.java b/src/main/java/com/whdc/mapper/AutoCallPersonMapper.java new file mode 100644 index 0000000..dd766fb --- /dev/null +++ b/src/main/java/com/whdc/mapper/AutoCallPersonMapper.java @@ -0,0 +1,42 @@ +package com.whdc.mapper; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.whdc.model.entity.AutoCallPerson; + +import java.util.List; + +/** + * @author lyf + * @since 2025-07-08 + */ +public interface AutoCallPersonMapper extends BaseMapper { + default List listUnUploaded() { + return selectList( + new QueryWrapper() + .eq("status", AutoCallPerson.STATUS_DEFAULT) + .orderByAsc("id") + ); + } + + default boolean isAnySuccess(Integer taskId) { + return selectCount( + new QueryWrapper() + .eq("task_id", taskId) + .eq("status", AutoCallPerson.STATUS_PUT) + ) > 0; + } + + default boolean isAllFail(Integer taskId) { + long personCnt = selectCount( + new QueryWrapper() + .eq("task_id", taskId) + ); + long failCnt = selectCount( + new QueryWrapper() + .eq("task_id", taskId) + .in("status", AutoCallPerson.STATUS_CALLED, AutoCallPerson.STATUS_CANCELLED) + ); + return personCnt == failCnt; + } +} diff --git a/src/main/java/com/whdc/mapper/AutoCallTaskMapper.java b/src/main/java/com/whdc/mapper/AutoCallTaskMapper.java new file mode 100644 index 0000000..f078317 --- /dev/null +++ b/src/main/java/com/whdc/mapper/AutoCallTaskMapper.java @@ -0,0 +1,28 @@ +package com.whdc.mapper; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.whdc.model.entity.AutoCallTask; +import com.whdc.model.entity.QXWarning; +import org.apache.ibatis.annotations.Select; + +import java.util.List; + +/** + * @author lyf + * @since 2025-07-08 + */ +public interface AutoCallTaskMapper extends BaseMapper { + List listWarnsThatNotGeneratedTask(); + + default List listShouldGenerate() { + return selectList( + new QueryWrapper() + .eq("status", AutoCallTask.STATUS_SHOULD_GENERATE) + .orderByAsc("id") + ); + } + + @Select("update auto_call_task set status = #{status} where id = #{taskId}") + void setStatus(Integer taskId, int status); +} diff --git a/src/main/java/com/whdc/mapper/QXWarningMapper.java b/src/main/java/com/whdc/mapper/QXWarningMapper.java index 4d1a250..eeadff5 100644 --- a/src/main/java/com/whdc/mapper/QXWarningMapper.java +++ b/src/main/java/com/whdc/mapper/QXWarningMapper.java @@ -12,6 +12,7 @@ import java.util.List; public interface QXWarningMapper extends BaseMapper { List find(@Param("dto")QXWarning dto); List findByMsgIsNull(); + List findByAutoMsgIsNull(); IPage page(@Param("page") IPage page, @Param("dto") GroupWarningDto dto); diff --git a/src/main/java/com/whdc/mapper/WarnCallMapMapper.java b/src/main/java/com/whdc/mapper/WarnCallMapMapper.java new file mode 100644 index 0000000..c217b9d --- /dev/null +++ b/src/main/java/com/whdc/mapper/WarnCallMapMapper.java @@ -0,0 +1,57 @@ +package com.whdc.mapper; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.whdc.model.entity.QXWarning; +import com.whdc.model.entity.WarnCallMap; + +import java.util.List; + +/** + * @author lyf + * @since 2025-06-19 + */ +public interface WarnCallMapMapper extends BaseMapper { + List listWarnsThatNotGeneratedWcm(); + + default List listUnGeneratedWcm() { + return selectList( + new QueryWrapper() + .eq("should_generate", 1) + .eq("generated", 0) + .eq("err_step", 0) + .orderByAsc("id") + ); + } + + default List listUnUploadedWcm() { + return selectList( + new QueryWrapper() + .eq("generated", 1) + .eq("uploaded", 0) + .eq("err_step", 0) + .orderByAsc("id") + ); + } + + default List listUnCalledWcm() { + return selectList( + new QueryWrapper() + .eq("uploaded", 1) + .eq("called", 0) + .eq("err_step", 0) + .orderByAsc("id") + ); + } + + default List listCallIsNotPutWcm() { + return selectList( + new QueryWrapper() + .eq("uploaded", 1) + .eq("called", 1) + .eq("call_is_put", 0) + .eq("err_step", 0) + .orderByDesc("id") + ); + } +} diff --git a/src/main/java/com/whdc/mapper/WarningResponderMapper.java b/src/main/java/com/whdc/mapper/WarningResponderMapper.java new file mode 100644 index 0000000..ce80cfc --- /dev/null +++ b/src/main/java/com/whdc/mapper/WarningResponderMapper.java @@ -0,0 +1,20 @@ +package com.whdc.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.whdc.model.entity.WarningResponder; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * @author xusan + * @date 2024-05-11 + */ +public interface WarningResponderMapper extends BaseMapper { + + + List getListByAdnm2(@Param("adnm") String adnm); + + List listByCnnmAndLevelOrderByLevelAsc(@Param("cnnm") String cnnm, @Param("levels") List levels); + +} \ No newline at end of file diff --git a/src/main/java/com/whdc/model/dto/AutoCallDto.java b/src/main/java/com/whdc/model/dto/AutoCallDto.java new file mode 100644 index 0000000..2b40e0b --- /dev/null +++ b/src/main/java/com/whdc/model/dto/AutoCallDto.java @@ -0,0 +1,23 @@ +package com.whdc.model.dto; + +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * Description: + * Created by XuSan on 2024/7/31. + * + * @author XuSan + * @version 1.0 + */ +@Data +@Accessors(chain = true) +public class AutoCallDto { + + private String stm; + private String etm; + + private Boolean callIsPut; + + private FindPageDto page; +} diff --git a/src/main/java/com/whdc/model/dto/AddressBookOldDto.java b/src/main/java/com/whdc/model/dto/WarningResponderDto.java similarity index 89% rename from src/main/java/com/whdc/model/dto/AddressBookOldDto.java rename to src/main/java/com/whdc/model/dto/WarningResponderDto.java index 17a38aa..03bfd3c 100644 --- a/src/main/java/com/whdc/model/dto/AddressBookOldDto.java +++ b/src/main/java/com/whdc/model/dto/WarningResponderDto.java @@ -8,7 +8,7 @@ import lombok.Data; * @date 2023年3月20日08:57:22 */ @Data -public class AddressBookOldDto{ +public class WarningResponderDto { @ApiModelProperty(value = "名字") private String name; diff --git a/src/main/java/com/whdc/model/entity/AddressBook.java b/src/main/java/com/whdc/model/entity/AddressBook.java index 53366b1..4c73557 100644 --- a/src/main/java/com/whdc/model/entity/AddressBook.java +++ b/src/main/java/com/whdc/model/entity/AddressBook.java @@ -9,6 +9,7 @@ import com.whdc.model.group.Insert; import com.whdc.model.group.Update; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.experimental.Accessors; @@ -110,4 +111,10 @@ public class AddressBook extends Model implements Serializable { @ApiModelProperty(value = "openid") private String openid; + /** + * 责任人级别 + */ + @TableField(value = "level") + @Schema(description = "1,2,3,4") + private Integer level; } \ No newline at end of file diff --git a/src/main/java/com/whdc/model/entity/AutoCall.java b/src/main/java/com/whdc/model/entity/AutoCall.java new file mode 100644 index 0000000..1a0e5f3 --- /dev/null +++ b/src/main/java/com/whdc/model/entity/AutoCall.java @@ -0,0 +1,77 @@ +package com.whdc.model.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.annotations.ApiModel; +import lombok.Data; +import lombok.ToString; +import lombok.experimental.Accessors; + +import java.util.Date; + +/** + * @author lyf + * @since 2025-06-14 + */ +@Data +@Accessors(chain = true) +@ApiModel(description = "自动拨号") +@TableName("FXKH_TXL.AUTOCALL") +@ToString +public class AutoCall { + /* + new的时候必填responderId,responderName,wcmId,createTm + query后必填__开头的 + */ + @TableId(value = "ID", type = IdType.AUTO) + private Integer id; + @TableField(value = "responder_id") + @JsonProperty("addressBookOldId") + private Integer responderId; + @JsonProperty("addressBookOldName") + @TableField(value = "responder_name") + private String responderName; + @TableField(value = "status") + private String status;//接通,空号,停机,关机,未接,拒接,占线,呼叫失败,null(空代表未呼叫) + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + @TableField(value = "_create_tm") + private Date createTm; + @TableField(value = "wcm_id") + private Integer wcmId; + @TableField(value = "level") + private Integer level; + + @TableField(value = "__talk_times") + private Integer talkTimes;//通话时长,单位秒 + @TableField(value = "__sip_term_cause") + private String sipTermCause; //例如”对方挂机“ + @TableField(value = "__caller") + private String caller; + @TableField(value = "__number") + private String number; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + @TableField(value = "__startring_at") + private Date startringAt; //-3 + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + @TableField(value = "__connected_at") + private Date connectedAt; //-3 + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + @TableField(value = "__started_at") + private Date startedAt; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + @TableField(value = "__endring_at") + private Date endringAt; //-3 + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + @TableField(value = "__disconnected_at") + private Date disconnectedAt; //-3 + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + @TableField(value = "__stopped_at") + private Date stopedAt; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + @TableField(value = "__last_modify") + private Date lastModify; +} diff --git a/src/main/java/com/whdc/model/entity/AutoCallConfig.java b/src/main/java/com/whdc/model/entity/AutoCallConfig.java new file mode 100644 index 0000000..0733e06 --- /dev/null +++ b/src/main/java/com/whdc/model/entity/AutoCallConfig.java @@ -0,0 +1,15 @@ +package com.whdc.model.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +/** + * @author lyf + * @since 2025-06-19 + */ +@Data +@TableName("FXKH_TXL.AUTOCALL_CONFIG") +public class AutoCallConfig { + private String key; + private String value; +} diff --git a/src/main/java/com/whdc/model/entity/AutoCallPerson.java b/src/main/java/com/whdc/model/entity/AutoCallPerson.java new file mode 100644 index 0000000..e2e6eda --- /dev/null +++ b/src/main/java/com/whdc/model/entity/AutoCallPerson.java @@ -0,0 +1,79 @@ +package com.whdc.model.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.util.Date; + +/** + * @author lyf + * @since 2025-07-08 + */ +@Data +public class AutoCallPerson { + public static final int STATUS_DEFAULT = 0; + public static final int STATUS_UPLOADED = 1; + public static final int STATUS_CALLED = 2; + public static final int STATUS_PUT = 3; + public static final int STATUS_CANCELLED = 4; + public static final int ERRCODE_ENCODE = 1; + public static final int ERRCODE_UPLOAD_FAIL = 2; + + @TableId(value = "ID", type = IdType.AUTO) + private Integer id; + @TableField(value = "task_id") + private Integer taskId; + @TableField(value = "status") + private Integer status; //0:未上传 default 0 + @TableField(value = "level") + private Integer level; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + @TableField(value = "_create_tm") + private Date createTm; + @TableField(value = "error_code") + private Integer errorCode; //default 0 + + @TableField(value = "__request_id") + private String uploadRequestId; //任务名,同时作为参数的taskName,用custId + @TableField(value = "__cust_id") + private String uploadCustId; //联系人id,用warningResponderId加时间戳毫秒 + @TableField(value = "__cust_name") + private String uploadCustName; //联系人 + @TableField(value = "__content") + private String uploadContent; //联系人 + @TableField(value = "__number") + private String uploadNumber; + @TableField(value = "__upload_resp_msg") + private String uploadRespMsg; //上传结果 + @TableField(value = "__remark") + private String detailRemark;//接通,空号,停机,关机,未接,拒接,占线,呼叫失败,null(空代表未呼叫) + @TableField(value = "__talk_times") + private Integer detailTalkTimes;//通话时长,单位秒 + @TableField(value = "__sip_term_cause") + private String detailSipTermCause; //例如”对方挂机“ + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + @TableField(value = "__startring_at") + private Date detailStartringAt; //-3 + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + @TableField(value = "__connected_at") + private Date detailConnectedAt; //-3 + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + @TableField(value = "__started_at") + private Date detailStartedAt; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + @TableField(value = "__endring_at") + private Date detailEndringAt; //-3 + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + @TableField(value = "__disconnected_at") + private Date detailDisconnectedAt; //-3 + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + @TableField(value = "__stopped_at") + private Date detailStopedAt; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + @TableField(value = "__last_modify") + private Date detailLastModify; +} diff --git a/src/main/java/com/whdc/model/entity/AutoCallTask.java b/src/main/java/com/whdc/model/entity/AutoCallTask.java new file mode 100644 index 0000000..e1a6c22 --- /dev/null +++ b/src/main/java/com/whdc/model/entity/AutoCallTask.java @@ -0,0 +1,57 @@ +package com.whdc.model.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.util.Date; +import java.util.List; + +/** + * @author lyf + * @since 2025-07-08 + */ +@Data +public class AutoCallTask { + public static final int STATUS_DEFAULT = 0; // 不生成 + public static final int STATUS_SHOULD_GENERATE = 1; + public static final int STATUS_GENERATED = 2; + public static final int STATUS_ANY_SUCCESS = 3; + public static final int STATUS_ALL_FAIL = 4; + public static final int STATUS_CANCELLED = 5; + public static final int ERRCODE_NO_PERSON = 1; + public static final int ERRCODE_DB_ERROR = 2; + + @TableId(value = "ID", type = IdType.AUTO) + private Integer id; + @TableField(value = "warn_id") + private Integer warnId; //not null + @TableField(value = "status") + private Integer status; //0:不拨打 default 0 + @TableField(value = "error_code") + private Integer errorCode; //default 0 + @TableField(value = "warn_name") + private String warnName; + @TableField(value = "warn_tm") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8") + private Date warnTm; + @TableField(value = "warn_level") + private String warnLevel; + @TableField(value = "WARN_CTNM") + private String warnCtnm; + @TableField(value = "warn_cnnm") + private String warnCnnm; + @TableField(value = "warn_content") + private String warnContent; + + @TableField(value = "_create_tm") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8") + private Date createTm; + @TableField(value = "_remark") + private String remark; + + @TableField(exist = false) + private List callList; +} diff --git a/src/main/java/com/whdc/model/entity/ShAddressBook.java b/src/main/java/com/whdc/model/entity/ShAddressBook.java index c0f8772..1deea54 100644 --- a/src/main/java/com/whdc/model/entity/ShAddressBook.java +++ b/src/main/java/com/whdc/model/entity/ShAddressBook.java @@ -160,4 +160,10 @@ public class ShAddressBook implements Serializable { @TableField(exist = false) List list; + /** + * 责任人级别 + */ + @TableField(value = "level") + @Schema(description = "1,2,3,4") + private Integer level; } diff --git a/src/main/java/com/whdc/model/entity/WarnCallMap.java b/src/main/java/com/whdc/model/entity/WarnCallMap.java new file mode 100644 index 0000000..2f478ee --- /dev/null +++ b/src/main/java/com/whdc/model/entity/WarnCallMap.java @@ -0,0 +1,73 @@ +package com.whdc.model.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.util.Date; +import java.util.List; + +/** + * @author lyf + * @since 2025-06-19 + */ +@Data +@TableName("FXKH_TXL.WARN_CALL_MAP") +public class WarnCallMap { + /* + new的时候必填warnId,shouldGenerate,cnnm,requestId,custId,custName,warnName,warnTm,warnLevel,callContent,createTm + 生成autoCall后必填generated + upload后必填uploadRespMsg,uploaded + query后必填callIsPut,called + */ + @TableId(value = "ID", type = IdType.AUTO) + private Integer id; + @TableField(value = "warn_id") + private Integer warnId; //not null + @TableField(value = "should_generate") + private Integer shouldGenerate; //0:不生成,1:生成, not null + @TableField(value = "generated") + private Integer generated; //0:未生成,1:已生成, default 0 + @TableField(value = "uploaded") + private Integer uploaded; //0:未上传,1:已上传, default 0 + @TableField(value = "cnnm") + private String cnnm; + @TableField(value = "upload_resp_msg") + private String uploadRespMsg; + @TableField(value = "request_id") + private String requestId; //任务名,作为参数的taskName + @TableField(value = "cust_id") + private String custId; //联系人id + @TableField(value = "cust_name") + private String custName; //联系人 + @TableField(value = "call_is_put") + private Integer callIsPut; //0:接通,1:已接通, default 0 + @TableField(value = "called") + private Integer called; //0:未拨打,1:已拨打, default 0 + @TableField(value = "err_step") + private Integer errStep; //0:初始化,1:step1出错, 以此类推,default 0 + + @TableField(value = "warn_name") + private String warnName; + @TableField(value = "warn_tm") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8") + private Date warnTm; + @TableField(value = "warn_level") + private String warnLevel; + @TableField(value = "call_content") + private String warnContent; + + @TableField(value = "_create_tm") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8") + private Date createTm; + @TableField(value = "_remark") + private String remark; + + + @TableField(exist = false) + private List callList; + +} diff --git a/src/main/java/com/whdc/model/entity/AddressBookOld.java b/src/main/java/com/whdc/model/entity/WarningResponder.java similarity index 81% rename from src/main/java/com/whdc/model/entity/AddressBookOld.java rename to src/main/java/com/whdc/model/entity/WarningResponder.java index 5a7898c..77f00b2 100644 --- a/src/main/java/com/whdc/model/entity/AddressBookOld.java +++ b/src/main/java/com/whdc/model/entity/WarningResponder.java @@ -33,10 +33,10 @@ import java.util.UUID; @EqualsAndHashCode(callSuper = true) @Data @Accessors(chain = true) -@ApiModel(description = "通讯录") +@ApiModel(description = "预警责任人") @TableName("FXKH_TXL.ADDRESS_BOOK_OLD") @Slf4j -public class AddressBookOld extends Model implements Serializable { +public class WarningResponder extends Model implements Serializable { @TableId(value = "ID",type = IdType.AUTO) @@ -79,7 +79,7 @@ public class AddressBookOld extends Model implements Serializabl @ApiModelProperty(value = "创建时间") private Date createTime; - public AddressBookOld encryptPhone(){ + public WarningResponder encryptPhone(){ if (StringUtils.isBlank(this.salt)){ this.salt = UUID.randomUUID().toString().replaceAll("-", ""); } @@ -94,7 +94,7 @@ public class AddressBookOld extends Model implements Serializabl return this; } - public AddressBookOld decryptPhone(){ + public WarningResponder decryptPhone(){ try { if (StringUtils.isNotBlank(this.phone) && StringUtils.isNotBlank(this.salt)){ this.phone = SymmetricEncryption.decrypt(this.phone,this.salt); @@ -105,4 +105,25 @@ public class AddressBookOld extends Model implements Serializabl } return this; } + + public static void main(String[] args) { + WarningResponder a = new WarningResponder(); + a.setPhone("15623775829"); + a.encryptPhone(); + System.out.println(a.salt); + System.out.println(a.phone); + } + + /** + * 责任人级别 + */ + @TableField(value = "LEVEL") + private Integer level; + + /** + * 责任人级别 + */ + @TableField(exist = false) + @Excel(name = "预警呼叫等级", width = 20) + private String levelLabel; } diff --git a/src/main/java/com/whdc/model/entity/autocall/AICCCallRespDetail.java b/src/main/java/com/whdc/model/entity/autocall/AICCCallRespDetail.java new file mode 100644 index 0000000..2a45ba8 --- /dev/null +++ b/src/main/java/com/whdc/model/entity/autocall/AICCCallRespDetail.java @@ -0,0 +1,64 @@ +package com.whdc.model.entity.autocall; + + +import com.alibaba.fastjson.annotation.JSONField; +import lombok.ToString; + +import java.util.List; + +/** + * 联通查询任务状态接口返回对象 + * + * @author lyf + * @since 2025-06-17 + */ +@lombok.Data +@ToString +public class AICCCallRespDetail { + private Data data; + + @lombok.Data + @ToString + public static class Data { + List records; + } + + @lombok.Data + @ToString + public static class Record { + private String caller; //外呼号码 + private Long dataTime; + private String processId; //机器人id + private String sessionDetailId; //联系人id,等同返回值的custId + private Integer talkTimes;//通话时长,单位秒 + private String remark; //中文状态,接通,空号,停机,关机,未接,拒接,占线,呼叫失败,""代表未呼叫 + private String taskName; //任务名 + private Integer status; //0进行中 2结束 4暂停 + private Integer totalCount; + private Integer sendCount; + + private RawVarListMap rawVarListMap; + } + + @lombok.Data + @ToString + public static class RawVarListMap { + private String taskName; //任务名,作为参数的taskName + private String dialTaskMainSn; //任务编号,作为参数的requestId + private String custId; //联系人id + private String sipTermCause; //例如”对方挂机“ + private String sipTermStatus; //接通是200 + private String caller; + @JSONField(name = "@NUMBER") + private String number; + private String startringAt; + private String connectedAt; + private String startedAt; + private String endringAt; + private String disconnectedAt; + private String stopedAt; + private String lastModify; + } + + +} diff --git a/src/main/java/com/whdc/model/entity/autocall/AICCCallRespTask.java b/src/main/java/com/whdc/model/entity/autocall/AICCCallRespTask.java new file mode 100644 index 0000000..2a2b674 --- /dev/null +++ b/src/main/java/com/whdc/model/entity/autocall/AICCCallRespTask.java @@ -0,0 +1,38 @@ +package com.whdc.model.entity.autocall; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * uploadCallData接口的返回值 + * + * @author lyf + * @since 2025-06-20 + */ +@EqualsAndHashCode(callSuper = true) +@Data +public class AICCCallRespTask extends AICCCallRespWrapper { + /* + { + "msg": "操作成功!", + "result": { + "msg": "任务名称重复" | "导入成功", + "count": "0", + "status": "-1" + }, + "code": 0, + "redirected": false, + "success": true, + "errorType": 0, + "errorCode": "", + "timestamp": 1750383985979 + } + */ + //code 0 success true + public static final String MSG_SUCCESS = "导入成功"; + public static final String MSG_REPEAT = "任务名称重复"; + + private String msg; + private String count; + private String status; +} diff --git a/src/main/java/com/whdc/model/entity/autocall/AICCCallRespWrapper.java b/src/main/java/com/whdc/model/entity/autocall/AICCCallRespWrapper.java new file mode 100644 index 0000000..b5fc715 --- /dev/null +++ b/src/main/java/com/whdc/model/entity/autocall/AICCCallRespWrapper.java @@ -0,0 +1,37 @@ +package com.whdc.model.entity.autocall; + +import lombok.Data; + +/** + * @author lyf + * @since 2025-06-20 + */ +@Data +public class AICCCallRespWrapper { + /* + { + "code": 406, + "errorCode": "", + "errorType": 0, + "msg": "Token失效,请重新登录!", + "redirected": true, + "success": false, + "timestamp": 1750384243314 + } + */ + //code 406 success false + private String msg; + private int code; + private boolean redirected; + private boolean success; + private int errorType; + private String errorCode; + private long timestamp; + + //token失效没有result + private T result; + + public boolean isTokenInvalid() { + return code == 406 && !success; + } +} diff --git a/src/main/java/com/whdc/model/entity/autocall/AICCLogin.java b/src/main/java/com/whdc/model/entity/autocall/AICCLogin.java new file mode 100644 index 0000000..4926dca --- /dev/null +++ b/src/main/java/com/whdc/model/entity/autocall/AICCLogin.java @@ -0,0 +1,19 @@ +package com.whdc.model.entity.autocall; + +import lombok.Data; + +/** + * @author lyf + * @since 2025-06-20 + */ +@Data +public class AICCLogin { + /* + {"msg":"操作成功!","result":{"msg":"操作成功","tenantId":"963936517","status":"0","token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6IkhCU0wwMSIsInRlbmFudElkIjoiOTYzOTM2NTE3IiwiZXhwIjoxNzUwNjg4NjE4fQ.INUcA-keg7T3HrZXH87K2ZqOT2trxLF38kmA9Wu301w"},"code":0,"redirected":false,"success":true,"errorType":0,"errorCode":"","timestamp":1750386218853} + */ + //code 0 success true + private String msg; + private String tenantId; + private String status; + private String token; +} diff --git a/src/main/java/com/whdc/model/entity/autocall/AICCUploadTask.java b/src/main/java/com/whdc/model/entity/autocall/AICCUploadTask.java new file mode 100644 index 0000000..ff21ac1 --- /dev/null +++ b/src/main/java/com/whdc/model/entity/autocall/AICCUploadTask.java @@ -0,0 +1,120 @@ +package com.whdc.model.entity.autocall; + +import com.alibaba.fastjson.annotation.JSONField; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; + +/** + * @author lyf + * @since 2025-06-20 + */ +@Data +public class AICCUploadTask { + /* + JSONObject data = new JSONObject(); + data.put("taskName", taskName); + data.put("processId", processId); + data.put("callerGroup", callerGroup); + data.put("requestId", requestId); + data.put("calleeType", 2); + data.put("repeatTimes", 2); + data.put("autoCall", "0"); + data.put("spanSeconds", 60); + data.put("processType", "3"); + data.put("mutiTimeRange", timeRange); + JSONArray params = new JSONArray(); + JSONObject param = new JSONObject(); + param.put("@common_user_name", custName); + param.put("@NUMBER", number); + if (bakNumbers != null && bakNumbers.length > 0) { + for (int i = 0; i < bakNumbers.length; i++) { + if (i == 3) break; + param.put("备用号码" + (i + 1), bakNumbers[i]); + } + } + param.put("custId", custId); + param.put("content", "我是省防办智能外呼系统," + content + "如需咨询请拨打02787221781"); + params.add(param); + data.put("param", params); + */ + private String taskName; + private String processId; + private String callerGroup; + private String requestId; + private int calleeType = 1; //默认0 重乎1 顺乎2 默认为0 + private int repeatTimes = 1;//呼叫次数,CallType=0不传; CalleeType=1重乎必传; CalleeType=2顺乎并且autoCall为0必传 + private String autoCall = "0";//顺乎未接通是否重乎,开启=0; 关闭=1; CalleeType=2顺乎必传 + private int spanSeconds = 3;//重乎间隔时间(秒),CallType=0不传; CalleeType=1重乎必传; CalleeType=2顺乎并且autoCall为0必传 + private String processType = "3"; + private String mutiTimeRange; + private List param; +// private String smsSend = "1"; //1不发短信,0发短信 +// private String smsSendType = "0"; //挂机短信0,未接通短信1,smsSend为0必传 +// private String templateId; //短信模板id,smsSend为0必传 + + @Data + public static class Cust { + @JSONField(name = "@common_user_name") + private String custName; + @JSONField(name = "@NUMBER") + private String number; + @JSONField(name = "备用号码1") + private String backNumber1; + @JSONField(name = "备用号码2") + private String backNumber2; + @JSONField(name = "备用号码3") + private String backNumber3; + private String custId; + private String content; + @JSONField(serialize = false, deserialize = false) + private List _numbers; + + public static CustBuilder builder() { + return new CustBuilder(); + } + + @Data + @Accessors(chain = true) + public static class CustBuilder { + private String custName; + private String custId; + private String content; + @JSONField(serialize = false, deserialize = false) + private List _numbers; + + public Cust build() { + Cust cust = new Cust(); + cust.setCustName(custName); + cust.setCustId(custId); + cust.setContent(content); + for (int i = 0; i < _numbers.size(); i++) { + switch (i) { + case 0: + cust.setNumber(_numbers.get(i)); + break; + case 1: + cust.setBackNumber1(_numbers.get(i)); + break; + case 2: + cust.setBackNumber2(_numbers.get(i)); + break; + case 3: + cust.setBackNumber3(_numbers.get(i)); + break; + } + } + return cust; + } + } + + } + + public void genMutiTimeRange() { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + this.mutiTimeRange = sdf.format(new Date()) + "_" + sdf.format(new Date(System.currentTimeMillis() + 1000 * 60 * 60)); + } +} diff --git a/src/main/java/com/whdc/model/vo/ExcelOldDataVo.java b/src/main/java/com/whdc/model/vo/ExcelOldDataVo.java index 107fb2a..5961d84 100644 --- a/src/main/java/com/whdc/model/vo/ExcelOldDataVo.java +++ b/src/main/java/com/whdc/model/vo/ExcelOldDataVo.java @@ -1,6 +1,6 @@ package com.whdc.model.vo; -import com.whdc.model.entity.AddressBookOld; +import com.whdc.model.entity.WarningResponder; import io.swagger.annotations.ApiModelProperty; import lombok.AllArgsConstructor; import lombok.Data; @@ -21,7 +21,7 @@ import java.util.List; public class ExcelOldDataVo { @ApiModelProperty(value = "数据") - private List list; + private List list; @ApiModelProperty(value = "键") private String key; diff --git a/src/main/java/com/whdc/model/vo/WarnDppleVO.java b/src/main/java/com/whdc/model/vo/WarnDppleVO.java index a1d147e..cc2c15b 100644 --- a/src/main/java/com/whdc/model/vo/WarnDppleVO.java +++ b/src/main/java/com/whdc/model/vo/WarnDppleVO.java @@ -25,4 +25,8 @@ public class WarnDppleVO { @ApiParam("手机号") @ApiModelProperty(value = "手机号") private String phone; + + @ApiParam("责任级别") + @ApiModelProperty(value = "责任级别") + private Integer level; } diff --git a/src/main/java/com/whdc/service/AutoCallApiService.java b/src/main/java/com/whdc/service/AutoCallApiService.java new file mode 100644 index 0000000..1d7fa96 --- /dev/null +++ b/src/main/java/com/whdc/service/AutoCallApiService.java @@ -0,0 +1,152 @@ +package com.whdc.service; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.whdc.mapper.*; +import com.whdc.model.dto.AutoCallDto; +import com.whdc.model.entity.AutoCall; +import com.whdc.model.entity.AutoCallPerson; +import com.whdc.model.entity.AutoCallTask; +import com.whdc.model.entity.WarnCallMap; +import com.whdc.utils.DateUtils; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.text.ParseException; +import java.util.Date; +import java.util.List; + +/** + * @author lyf + * @since 2025-06-14 + */ +@Service +@Slf4j +public class AutoCallApiService { + @Autowired + private AutoCallMapper autoCallMapper; + @Autowired + private WarnCallMapMapper warnCallMapMapper; + + public Page page2(AutoCallDto dto) { + Date stm = null; + Date etm = null; + try { + if (dto.getStm() != null && dto.getEtm() != null) { + stm = DateUtils.formatD(dto.getStm(), DateUtils.DATE_PATTERN1); + etm = DateUtils.formatD(dto.getEtm(), DateUtils.DATE_PATTERN1); + } + } catch (ParseException e) { + throw new RuntimeException(e); + } + if (stm != null) stm = DateUtils.standardize(stm, true); + if (etm != null) etm = DateUtils.standardize(etm, false); + + QueryWrapper query = new QueryWrapper() + .orderByDesc("id") + .between("warn_tm", stm, etm); + if (dto.getCallIsPut() != null) { + if (dto.getCallIsPut()) { + query.eq("status", + AutoCallTask.STATUS_ANY_SUCCESS + ); + } else { + query.eq("status", + AutoCallTask.STATUS_ALL_FAIL + ); + } + } else { + query.in("status", + AutoCallTask.STATUS_SHOULD_GENERATE, + AutoCallTask.STATUS_GENERATED, + AutoCallTask.STATUS_ANY_SUCCESS, + AutoCallTask.STATUS_ALL_FAIL, + AutoCallTask.STATUS_CANCELLED + ); + } + Page pageParam = dto.getPage().getPage(); + Page pageResult = taskMapper.selectPage( + pageParam, + query + ); + + List records = pageResult.getRecords(); + if (records.size() > 0) { + for (AutoCallTask task : records) { + Integer taskId = task.getId(); + List autoCallList = personMapper.selectList( + new QueryWrapper() + .eq("task_id", taskId) + ); + task.setCallList(autoCallList); + } + } + return pageResult; + } + + public Page page(AutoCallDto dto) { + Date stm = null; + Date etm = null; + try { + if (dto.getStm() != null && dto.getEtm() != null) { + stm = DateUtils.formatD(dto.getStm(), DateUtils.DATE_PATTERN1); + etm = DateUtils.formatD(dto.getEtm(), DateUtils.DATE_PATTERN1); + } + } catch (ParseException e) { + throw new RuntimeException(e); + } + if (stm != null) stm = DateUtils.standardize(stm, true); + if (etm != null) etm = DateUtils.standardize(etm, false); + + QueryWrapper query = new QueryWrapper() + .eq("should_generate", 1) + .orderByDesc("id") + .between("warn_tm", stm, etm); + if (dto.getCallIsPut() != null) { + if (dto.getCallIsPut()) { + query.eq("call_is_put", 1); + } else { + query.eq("call_is_put", 0); + } + } + Page pageParam = dto.getPage().getPage(); + Page pageResult = warnCallMapMapper.selectPage( + pageParam, + query + ); + List records = pageResult.getRecords(); + if (records.size() > 0) { + for (WarnCallMap record : records) { + Integer wcmId = record.getId(); + List autoCallList = autoCallMapper.selectList( + new QueryWrapper() + .eq("wcm_id", wcmId) + ); + record.setCallList(autoCallList); + } + } + return pageResult; + } + + public List listCallIsNotPut() { + List wcmList = warnCallMapMapper.listCallIsNotPutWcm(); + for (WarnCallMap record : wcmList) { + Integer wcmId = record.getId(); + List autoCallList = autoCallMapper.selectList( + new QueryWrapper() + .eq("wcm_id", wcmId) + ); + record.setCallList(autoCallList); + } + return wcmList; + } + + @Autowired + private AutoCallTaskMapper taskMapper; + @Autowired + private AutoCallPersonMapper personMapper; + @Autowired + private QXWarningMapper warningMapper; + +} diff --git a/src/main/java/com/whdc/service/AutoCallTaskService.java b/src/main/java/com/whdc/service/AutoCallTaskService.java new file mode 100644 index 0000000..3209006 --- /dev/null +++ b/src/main/java/com/whdc/service/AutoCallTaskService.java @@ -0,0 +1,494 @@ +package com.whdc.service; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.whdc.mapper.*; +import com.whdc.model.entity.*; +import com.whdc.model.entity.autocall.AICCCallRespDetail; +import com.whdc.model.entity.autocall.AICCCallRespWrapper; +import com.whdc.model.entity.autocall.AICCUploadTask; +import com.whdc.model.entity.autocall.AICCCallRespTask; +import com.whdc.utils.AutoCallHelper; +import com.whdc.utils.SmsHelper; +import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.NotNull; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.stream.Collectors; + +/** + * @author lyf + * @since 2025-06-14 + */ +@Service +@Slf4j +public class AutoCallTaskService { + private static final String[] excludeNames = new String[]{"高新", "开发", "自治", "技术", "旅游", "管理", "工业", "产业", "示范"}; + private static final AtomicBoolean isCalling = new AtomicBoolean(false); + @Autowired + private WarningResponderMapper warningResponderMapper; + @Autowired + private QXWarningMapper qxWarningMapper; + @Autowired + private AutoCallMapper autoCallMapper; + @Autowired + private AutoCallConfigMapper configMapper; + @Autowired + private WarnCallMapMapper warnCallMapMapper; + @Autowired + private AutoCallHelper autoCallHelper; + @Autowired + private SmsHelper smsHelper; + @Value("${autocall.processId}") + private String processId; + @Value("${autocall.callerGroup}") + private String callerGroup; + @Value("${autocall.secret}") + private String secret; + @Value("${autocall.sysUserId}") + private String sysUserId; + + @Transactional + public void generateFakeCall() throws ParseException { + QXWarning warn = qxWarningMapper.selectOne( + new QueryWrapper() + .orderByDesc("WARNID") + .last("limit 1") + ); + warn.setCtnm("恩施州"); + warn.setCnnm("咸丰,"); + + List wcmList = newWcmByWarn(warn); + WarnCallMap wcm = wcmList.get(0); + wcm.setCreateTm(new Date()); + warnCallMapMapper.insert(wcm); + } + + public void step1GenerateWarnCallMap() { + List warnList = warnCallMapMapper.listWarnsThatNotGeneratedWcm(); + for (QXWarning warn : warnList) { + try { + List wcmList = newWcmByWarn(warn); + for (WarnCallMap wcm : wcmList) { + if (wcm.getCnnm() == null) { + wcm.setErrStep(1); + wcm.setRemark("未找到县区"); + } + wcm.setCreateTm(new Date()); + warnCallMapMapper.insert(wcm); + } + } catch (Exception e) { + log.error("根据预警生成呼叫失败", e); + } + } + } + + public String getToken() { + return autoCallHelper.getToken(); + } + + private @NotNull List newWcmByWarn(QXWarning warn) throws ParseException { + String cnnmStr = warn.getCnnm(); + Integer warnId = warn.getWarnid(); + String warnSignalLevel = warn.getWarnSignalLevel(); + String content = warn.getContent(); + String ctnm = warn.getCtnm(); + Date publishTime = warn.getPublishTime(); + + List wcmList = new ArrayList<>(); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日HH时mm分"); +// content = "我是省防办智能外呼系统," + content + "如需咨询请拨打02787221781"; + content = "我是省防办智能外呼系统," +warn.getCtnm()+sdf.format(warn.getPublishTime()) +"发布暴雨"+warn.getWarnSignalLevel()+"预警信号:"+ content + "如需咨询请拨打02787221781,相关信息以短信发送给您。"; + if (cnnmStr == null || cnnmStr.trim().isEmpty()) { + WarnCallMap wcm = newWcm(null, warnId, publishTime, ctnm, warnSignalLevel, content); + wcmList.add(wcm); + return wcmList; + } + String[] cnnms = cnnmStr.trim().split(","); + for (String cnnm : cnnms) { + if (cnnm.isEmpty()) continue; + if (publishTime == null) continue; + + WarnCallMap wcm = newWcm(cnnm, warnId, publishTime, ctnm, warnSignalLevel, content); + wcmList.add(wcm); + } + return wcmList; + } + + private @NotNull WarnCallMap newWcm(String cnnm, Integer warnId, Date publishTime, String ctnm, String warnSignalLevel, String content) throws ParseException { + WarnCallMap wcm = new WarnCallMap(); + wcm.setWarnId(warnId); + wcm.setCnnm(cnnm); + + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + wcm.setWarnTm(publishTime); + + wcm.setWarnName(String.format("%s%s%s%s预警", sdf.format(publishTime), ctnm, cnnm, warnSignalLevel)); + wcm.setWarnLevel(warnSignalLevel); + wcm.setWarnContent(content); + wcm.setRequestId("hbsl" + new Date().getTime() + cnnm); + + if (isEnable()) { + wcm.setShouldGenerate(1); + } else { + wcm.setShouldGenerate(0); + } + return wcm; + } + + //为什么wcm和autocall的生成要分开:wcm是根据qxwarning生成的,自动呼叫可能会暂停,避免之后开启会根据早时的qxwarning生成wcm进而呼叫 + public void step2GenerateAutoCallByWcm() { + List wcmList = warnCallMapMapper.listUnGeneratedWcm(); + for (WarnCallMap wcm : wcmList) { + List autoCallList = newAutoCall(wcm); + if (autoCallList.size() == 0) { + wcm.setErrStep(2); + wcm.setRemark("未找到责任人"); + warnCallMapMapper.updateById(wcm); + return; + } + + StringBuilder sb = new StringBuilder(); + //将responderId合并为custId + for (AutoCall autoCall : autoCallList) { + sb.append(autoCall.getResponderId()).append("-"); + } + sb.deleteCharAt(sb.length() - 1); + String custId = System.currentTimeMillis() + "-" + sb; + //将responderName合并为custName + sb = new StringBuilder(); + for (AutoCall autoCall : autoCallList) { + sb.append(autoCall.getResponderName()).append(","); + } + sb.deleteCharAt(sb.length() - 1); + String custName = sb.toString(); + wcm.setCustId(custId); + wcm.setCustName(custName); + wcm.setGenerated(1); + + Date now = new Date(); + autoCallList.forEach(a -> { + a.setCreateTm(now); + autoCallMapper.insert(a); + }); + + warnCallMapMapper.updateById(wcm); + } + } + + private @NotNull List newAutoCall(WarnCallMap wcm) { + Integer wcmId = wcm.getId(); + String cnnm = wcm.getCnnm(); + List existList = autoCallMapper.listByWcmIdAndCnnmOrderByLevelAsc(wcmId, cnnm); + if (existList != null && existList.size() > 0) { + return Collections.emptyList(); + } + + return newAutoCall(wcmId, cnnm, wcm.getWarnLevel()); + } + + private @NotNull List newAutoCall(Integer wcmId, String cnnm, String warnLevelStr) { + //处理行政区划和预警级别,橙色预警拨打1,2level的,红色预警只拨打3level的 + List warnLevel = mapWarnSignalLevel(warnLevelStr); + List wrList = listWarningResponderByCnnmAndLevelOrderByLevelAsc(cnnm, warnLevel); + if (wrList.isEmpty()) return Collections.emptyList(); + //处理县区包含高新区、开发区等情况 + doSomethingWith(cnnm, wrList); + //按level从小到大排序 + wrList.sort(Comparator.comparingInt(WarningResponder::getLevel)); + List autoCallList = new ArrayList<>(); + for (WarningResponder wr : wrList) { + Integer wrLevel = wr.getLevel(); + wr.decryptPhone(); + AutoCall autoCall = new AutoCall(); + autoCall.setResponderId(wr.getId()) + .setResponderName(wr.getName()) + .setWcmId(wcmId) + .setLevel(wrLevel) + .setNumber(wr.getPhone()); + autoCallList.add(autoCall); + } + + return autoCallList; + } + + public void step3UploadCallTask() { + if (!isCalling.compareAndSet(false, true)) { + return; + } + + try { + //已提交,提交返回内容为“导入成功”,未拨打 + List wcmList = warnCallMapMapper.listUnUploadedWcm(); + if (wcmList == null || wcmList.isEmpty()) return; + + for (WarnCallMap wcm : wcmList) { + Integer wcmId = wcm.getId(); + List autoCallList = autoCallMapper.listByWcmIdAndCnnmOrderByLevelAsc(wcmId, wcm.getCnnm()); + if (autoCallList == null || autoCallList.isEmpty()) { + wcm.setErrStep(3); + wcm.setRemark("未找到呼叫列表"); + warnCallMapMapper.updateById(wcm); + continue; + } + + List numbers = autoCallList.stream() + .map(AutoCall::getNumber) + .collect(Collectors.toList()); + + AICCUploadTask task = autoCallHelper.newTask( + wcm.getRequestId(), + wcm.getCustId(), + wcm.getCustName(), + wcm.getWarnContent(), + numbers + ); + AICCCallRespWrapper AICCCallRespWrapper = autoCallHelper.apiUploadCallData(task); + + String msg = AICCCallRespWrapper.getResult().getMsg(); + wcm.setUploaded(1); + wcm.setUploadRespMsg(msg); + warnCallMapMapper.updateById(wcm); + + smsHelper.send(numbers, wcm.getWarnContent()); + } + } finally { + isCalling.set(false); + } + } + + public void step4QueryTaskResult() { + List wcmList = warnCallMapMapper.listUnCalledWcm(); + if (wcmList.isEmpty()) return; + + for (WarnCallMap wcm : wcmList) { + Integer wcmId = wcm.getId(); + String requestId = wcm.getRequestId(); + String custId = wcm.getCustId(); + + List autoCallList = autoCallMapper.listByWcmIdAndCnnmOrderByLevelAsc(wcmId, wcm.getCnnm()); + if (autoCallList == null || autoCallList.isEmpty()) { + wcm.setErrStep(4); + wcm.setRemark("未找到呼叫列表"); + warnCallMapMapper.updateById(wcm); + continue; + } + + AICCCallRespWrapper AICCCallRespWrapper = autoCallHelper.apiGetTaskCallDetail(requestId, custId); + if (AICCCallRespWrapper == null || !AICCCallRespWrapper.isSuccess()) { + wcm.setErrStep(4); + wcm.setRemark("请求外呼平台失败"); + warnCallMapMapper.updateById(wcm); + continue; + } + + //任务不存在 + if (AICCCallRespWrapper.getResult() == null + || AICCCallRespWrapper.getResult().getData() == null + || AICCCallRespWrapper.getResult().getData().getRecords() == null + || AICCCallRespWrapper.getResult().getData().getRecords().isEmpty()) { + wcm.setErrStep(4); + wcm.setRemark("外呼平台查询不到任务"); + warnCallMapMapper.updateById(wcm); + continue; + } + + List records = AICCCallRespWrapper.getResult().getData().getRecords(); + boolean anyCalled = true; + boolean anyCallIsPut = false; + for (AICCCallRespDetail.Record record : records) { + if (record.getRemark() == null || record.getRemark().isEmpty()) { + anyCalled = false; + continue; + } + if ("接通".equals(record.getRemark())) anyCallIsPut = true; + AICCCallRespDetail.RawVarListMap rawVarListMap = record.getRawVarListMap(); + String number = rawVarListMap.getNumber(); + AutoCall call = null; + for (AutoCall a : autoCallList) { + if (a.getNumber().equals(number)) { + call = a; + break; + } + } + copyDataFromDetailToAutoCall(record, call, rawVarListMap); + } + if (!records.isEmpty() && anyCalled) { + wcm.setCalled(1); + warnCallMapMapper.updateById(wcm); + } + + for (AutoCall a : autoCallList) { + if (a.getStatus() == null && anyCallIsPut) { + a.setStatus("已呼通其他责任人"); + } + autoCallMapper.updateById(a); + } + if (anyCallIsPut) { + wcm.setCallIsPut(1); + warnCallMapMapper.updateById(wcm); + } + } + } + + public AICCCallRespWrapper queryTaskResult(String requestId, String custId) { + return autoCallHelper.apiGetTaskCallDetail(requestId, custId); + } + + private void copyDataFromDetailToAutoCall(AICCCallRespDetail.Record record, AutoCall call, AICCCallRespDetail.RawVarListMap rawVarListMap) { + if (call != null) { + call.setStatus(record.getRemark()); + call.setTalkTimes(record.getTalkTimes()); + call.setSipTermCause(rawVarListMap.getSipTermCause()); + call.setCaller(rawVarListMap.getCaller()); + + Date d; + long l; + + if (rawVarListMap.getStartringAt() != null) { + d = new Date(); + l = Long.parseLong(rawVarListMap.getStartringAt().substring(0, 13)); + d.setTime(l); + call.setStartringAt(d); + } + + if (rawVarListMap.getConnectedAt() != null) { + d = new Date(); + l = Long.parseLong(rawVarListMap.getConnectedAt().substring(0, 13)); + d.setTime(l); + call.setConnectedAt(d); + } + + if (rawVarListMap.getStartedAt() != null) { + d = new Date(); + l = Long.parseLong(rawVarListMap.getStartedAt()); + d.setTime(l); + call.setStartedAt(d); + } + + if (rawVarListMap.getEndringAt() != null) { + d = new Date(); + l = Long.parseLong(rawVarListMap.getEndringAt().substring(0, 13)); + d.setTime(l); + call.setEndringAt(d); + } + + if (rawVarListMap.getDisconnectedAt() != null) { + d = new Date(); + l = Long.parseLong(rawVarListMap.getDisconnectedAt().substring(0, 13)); + d.setTime(l); + call.setDisconnectedAt(d); + } + + if (rawVarListMap.getStopedAt() != null) { + d = new Date(); + l = Long.parseLong(rawVarListMap.getStopedAt()); + d.setTime(l); + call.setStopedAt(d); + } + + if (rawVarListMap.getLastModify() != null) { + d = new Date(); + l = Long.parseLong(rawVarListMap.getLastModify()); + d.setTime(l); + call.setLastModify(d); + } + } + } + + + private List mapWarnSignalLevel(String warnSignalLevel) { + List ret = new ArrayList<>(); + if ("红色".equals(warnSignalLevel)) { + ret.add(3); + } else { + ret.add(1); + ret.add(2); + } + return ret; + } + + /* + 更新字典,是否开启自动呼叫 + */ + public void setEnable(Boolean enable) { + AutoCallConfig config = new AutoCallConfig(); + config.setKey("enable"); + config.setValue(enable ? "1" : "0"); + configMapper.update( + config, + new QueryWrapper() + .eq("key", "enable") + ); + } + + /* + 查询字典,是否开启自动呼叫 + */ + public boolean isEnable() { + AutoCallConfig config = configMapper.selectOne( + new QueryWrapper() + .eq("key", "enable") + .last("limit 1") + ); + return config != null && "1".equals(config.getValue()); + } + + public void setCallIsPut(Integer wcmId) { + WarnCallMap wcm = warnCallMapMapper.selectById(wcmId); + wcm.setCalled(1); + wcm.setCallIsPut(1); + warnCallMapMapper.updateById(wcm); + } + + /* + 查询字典,是否开启定时任务 + */ + public boolean isScheduled() { + AutoCallConfig config = configMapper.selectOne( + new QueryWrapper() + .eq("key", "schedule") + .last("limit 1") + ); + return config != null && "1".equals(config.getValue()); + } + + private static void doSomethingWith(String cnmm, List wrList) { + //预警包含高新等字符,可跳出 + boolean skip = false; + for (String excludeName : excludeNames) { + if (cnmm.contains(excludeName)) { + skip = true; + break; + } + } + if (skip) { + return; + } + //预警不包含高新等字符,检查人员,保留不包含高新等字符的人员 + Iterator iter = wrList.iterator(); + while (iter.hasNext()) { + WarningResponder autoCall = iter.next(); + boolean anyMatch = false; + for (String excludeName : excludeNames) { + if (autoCall.getCnnm().contains(excludeName)) { + anyMatch = true; + break; + } + } + if (anyMatch) { + iter.remove(); + } + } + } + + private List listWarningResponderByCnnmAndLevelOrderByLevelAsc(String cnnm, List levels) { + return warningResponderMapper.listByCnnmAndLevelOrderByLevelAsc(cnnm, levels); + } +} diff --git a/src/main/java/com/whdc/service/AutoCallTaskService2.java b/src/main/java/com/whdc/service/AutoCallTaskService2.java new file mode 100644 index 0000000..bb21562 --- /dev/null +++ b/src/main/java/com/whdc/service/AutoCallTaskService2.java @@ -0,0 +1,449 @@ +package com.whdc.service; + +import com.alibaba.fastjson.JSON; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.whdc.mapper.*; +import com.whdc.model.entity.*; +import com.whdc.model.entity.autocall.AICCCallRespDetail; +import com.whdc.model.entity.autocall.AICCCallRespTask; +import com.whdc.model.entity.autocall.AICCCallRespWrapper; +import com.whdc.model.entity.autocall.AICCUploadTask; +import com.whdc.utils.AICCHelper; +import com.whdc.utils.SmsHelper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.TransactionStatus; +import org.springframework.transaction.support.DefaultTransactionDefinition; + +import java.io.UnsupportedEncodingException; +import java.security.GeneralSecurityException; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.stream.Collectors; + +/** + * @author lyf + * @since 2025-07-08 + */ +@Service +@Slf4j +public class AutoCallTaskService2 { + @Autowired + private AutoCallTaskMapper taskMapper; + @Autowired + private AutoCallPersonMapper personMapper; + @Autowired + private AutoCallConfigMapper configMapper; + @Autowired + private PlatformTransactionManager transactionManager; + @Autowired + private WarningResponderMapper warningResponderMapper; + @Autowired + private QXWarningMapper qxWarningMapper; + @Autowired + private AICCHelper aiccHelper; + @Autowired + private SmsHelper smsHelper; + + public void generateFakeCall() { + QXWarning warn = qxWarningMapper.selectOne( + new QueryWrapper() + .orderByDesc("WARNID") + .last("limit 1") + ); + warn.setCtnm("恩施州"); + warn.setCnnm("咸丰 "); + + List taskList = newTask(warn); + AutoCallTask task = taskList.get(0); + task.setStatus(0); + task.setCreateTm(new Date()); + task.setWarnCnnm("咸丰 "); + taskMapper.insert(task); + generatePerson(task); + } + + public List doCallTest() { + List personList = step2GetOneUnUploadedPerson(); + + for (AutoCallPerson person : personList) { + step3UploadAICCTask(person); + + int pendingDuration = 60 * 1000 * 2; + int loopGap = 1000; + while (true) { + try { + Thread.sleep(loopGap); + } catch (InterruptedException ignore) { + } + pendingDuration -= loopGap; + if (pendingDuration <= 0) break; + + boolean f = step4QueryAICCTaskResult(person); + if (f) { + break; + } + } + if (pendingDuration <= 0) { + markPersonDetailQueryTimeout(person); + } + } + return personList; + } + + public void markPersonDetailQueryTimeout(AutoCallPerson person) { + person.setDetailRemark("超时"); + person.setStatus(AutoCallPerson.STATUS_CANCELLED); + person.setDetailSipTermCause("超时"); + personMapper.updateById(person); + } + + public void step1GenerateTask() { + //切记要设置task的status + List warnList = taskMapper.listWarnsThatNotGeneratedTask(); + boolean enable = configMapper.isEnable(); + for (QXWarning warn : warnList) { + try { + List taskList = newTask(warn); + Date now = new Date(); + for (AutoCallTask task : taskList) { + if (task.getWarnCnnm() == null) { + task.setRemark("未找到县区"); + } + task.setCreateTm(now); + if (enable && task.getWarnCnnm() != null) { + task.setStatus(AutoCallTask.STATUS_SHOULD_GENERATE); + } + taskMapper.insert(task); + if (enable && task.getWarnCnnm() != null) { + generatePerson(task); + } + } + } catch (Exception e) { + log.error("根据预警生成呼叫失败", e); + } + } + } + + private List newTask(QXWarning warn) { + String cnnmStr = warn.getCnnm(); + Integer warnId = warn.getWarnid(); + String warnSignalLevel = warn.getWarnSignalLevel(); + String content = warn.getContent(); + String ctnm = warn.getCtnm(); + Date publishTime = warn.getPublishTime(); + + List ret = new ArrayList<>(); + if (cnnmStr == null || cnnmStr.isEmpty()) { + AutoCallTask task = newTask(null, warnId, publishTime, ctnm, warnSignalLevel, content); + ret.add(task); + return ret; + } + String[] cnnms = cnnmStr.split(","); + for (String cnnm : cnnms) { + if (cnnm.isEmpty()) continue; + if (publishTime == null) continue; + if ("test".equals(cnnm)) { + cnnm = "咸丰 "; + ctnm = "恩施州"; + } + AutoCallTask task = newTask(cnnm, warnId, publishTime, ctnm, warnSignalLevel, content); + ret.add(task); + } + return ret; + } + + private AutoCallTask newTask(String cnnm, Integer warnId, Date publishTime, String ctnm, String warnSignalLevel, String content) { + AutoCallTask task = new AutoCallTask(); + task.setWarnId(warnId); + task.setWarnCtnm(ctnm); + task.setWarnCnnm(cnnm); + task.setWarnContent(content); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + task.setWarnTm(publishTime); + task.setWarnName(String.format("%s%s%s%s预警", sdf.format(publishTime), ctnm, cnnm, warnSignalLevel)); + task.setWarnLevel(warnSignalLevel); + return task; + } + + public void generatePerson(AutoCallTask task) { + //切记要设置task的status + List personList = newPerson(task); + if (personList.size() == 0) { + task.setRemark("未找到责任人"); + task.setStatus(AutoCallTask.STATUS_CANCELLED); + task.setErrorCode(AutoCallTask.ERRCODE_NO_PERSON); + taskMapper.updateById(task); + return; + } + TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition()); + try { + Date now = new Date(); + personList.forEach(a -> { + a.setCreateTm(now); + personMapper.insert(a); + }); + task.setStatus(AutoCallTask.STATUS_GENERATED); + taskMapper.updateById(task); + transactionManager.commit(status); + } catch (Exception e) { + transactionManager.rollback(status); + task.setRemark("数据库异常"); + task.setStatus(AutoCallTask.STATUS_CANCELLED); + task.setErrorCode(AutoCallTask.ERRCODE_DB_ERROR); + taskMapper.updateById(task); + log.error("插入外呼责任人异常", e); + log.error("{}", JSON.toJSONString(personList)); + } + } + + private List mapWarnSignalLevel(String warnSignalLevel) { + List ret = new ArrayList<>(); + if ("红色".equals(warnSignalLevel)) { + ret.add(3); + } else { + ret.add(1); + ret.add(2); + } + return ret; + } + + private static final String[] excludeNames = new String[]{"高新", "开发", "自治", "技术", "旅游", "管理", "工业", "产业", "示范"}; + + private static void doSomethingWith(String cnmm, List wrList) { + //预警包含高新等字符,可跳出 + boolean skip = false; + for (String excludeName : excludeNames) { + if (cnmm.contains(excludeName)) { + skip = true; + break; + } + } + if (skip) { + return; + } + //预警不包含高新等字符,检查人员,保留不包含高新等字符的人员 + Iterator iter = wrList.iterator(); + while (iter.hasNext()) { + WarningResponder autoCall = iter.next(); + boolean anyMatch = false; + for (String excludeName : excludeNames) { + if (autoCall.getCnnm().contains(excludeName)) { + anyMatch = true; + break; + } + } + if (anyMatch) { + iter.remove(); + } + } + } + + private List newPerson(AutoCallTask task) { + String cnnm = task.getWarnCnnm(); + long millis = task.getCreateTm().getTime(); + List warnLevels = mapWarnSignalLevel(task.getWarnLevel()); + List wrList = warningResponderMapper.listByCnnmAndLevelOrderByLevelAsc(cnnm, warnLevels); + if (wrList.isEmpty()) return Collections.emptyList(); + //处理县区包含高新区、开发区等情况 + doSomethingWith(cnnm, wrList); + //按level从小到大排序 + wrList.sort(Comparator.comparingInt(WarningResponder::getLevel)); + + List personList = new ArrayList<>(); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + for (WarningResponder wr : wrList) { + Integer wrLevel = wr.getLevel(); + wr.decryptPhone(); + AutoCallPerson person = new AutoCallPerson(); + String uniqueId = millis + "-" + wr.getId(); + person.setTaskId(task.getId()); + person.setUploadRequestId(uniqueId); + person.setUploadCustId(uniqueId); + person.setUploadCustName(wr.getName()); + person.setLevel(wrLevel); + person.setUploadNumber(wr.getPhone()); + person.setUploadContent( + "我是省防办智能外呼系统," + + wr.getName() + "同志您好," + + task.getWarnCtnm() + + sdf.format(task.getWarnTm()) + + "发布暴雨" + + task.getWarnLevel() + + "预警信号:" + + task.getWarnContent() + + "如需咨询请拨打02787221781,相关信息以短信发送给您。" + ); + person.setCreateTm(task.getCreateTm()); + personList.add(person); + } + return personList; + } + + public List step2GetOneUnUploadedPerson() { + List personList = personMapper.listUnUploaded(); + if (personList == null || personList.isEmpty()) return Collections.emptyList(); + + Map> personGroup = personList.stream().collect(Collectors.groupingBy(AutoCallPerson::getTaskId)); + + List ret = new ArrayList<>(); + personGroup.forEach((taskId, list) -> { + //按level从小到大排序 + list.sort(Comparator.comparingInt(AutoCallPerson::getLevel)); + ret.add(list.get(0)); + }); + + return ret; + } + + private static final AtomicBoolean isCalling = new AtomicBoolean(false); + + public void step3UploadAICCTask(AutoCallPerson person) { + //切记要设置person的status + if (!isCalling.compareAndSet(false, true)) { + return; + } + try { + List numbers = Collections.singletonList(person.getUploadNumber()); + AICCUploadTask uploadTask = aiccHelper.newTask( + person.getUploadRequestId(), + person.getUploadCustId(), + person.getUploadCustName(), + person.getUploadContent(), + numbers + ); + AICCCallRespWrapper AICCCallRespWrapper = null; + try { + AICCCallRespWrapper = aiccHelper.apiUploadCallData(uploadTask); + } catch (GeneralSecurityException | UnsupportedEncodingException e) { + person.setStatus(AutoCallPerson.STATUS_CANCELLED); + person.setErrorCode(AutoCallPerson.ERRCODE_ENCODE); + personMapper.updateById(person); + log.error("外呼任务编码异常", e); + } + if (AICCCallRespWrapper == null) { + person.setStatus(AutoCallPerson.STATUS_CANCELLED); + person.setErrorCode(AutoCallPerson.ERRCODE_UPLOAD_FAIL); + personMapper.updateById(person); + } else { + String msg = AICCCallRespWrapper.getResult().getMsg(); + person.setStatus(AutoCallPerson.STATUS_UPLOADED); + person.setUploadRespMsg(msg); + personMapper.updateById(person); + smsHelper.send(numbers, person.getUploadContent()); + } + } finally { + isCalling.set(false); + } + } + + public boolean step4QueryAICCTaskResult(AutoCallPerson person) { + //切记要设置person的status和task的status + AICCCallRespWrapper detail = aiccHelper.apiGetTaskCallDetail(person.getUploadRequestId(), person.getUploadCustId()); + List records = detail.getResult().getData().getRecords(); + if (records.isEmpty()) return false; //这个false是平台还未有拨打,应继续刷接口 + AICCCallRespDetail.Record record = records.get(0); + String dialRemark = record.getRemark(); + boolean done = false; + if ("接通".equals(dialRemark)) { + person.setStatus(AutoCallPerson.STATUS_PUT); + done = true; + } else if (records.size() > 1) { + person.setStatus(AutoCallPerson.STATUS_CALLED); + done = true; + } + if (done) { + person.setDetailRemark(dialRemark); + person.setDetailTalkTimes(record.getTalkTimes()); + AICCCallRespDetail.RawVarListMap rawVarListMap = record.getRawVarListMap(); + person.setDetailSipTermCause(rawVarListMap.getSipTermCause()); + + { + Date d; + long l; + if (rawVarListMap.getStartringAt() != null) { + d = new Date(); + l = Long.parseLong(rawVarListMap.getStartringAt().substring(0, 13)); + d.setTime(l); + person.setDetailStartringAt(d); + } + + if (rawVarListMap.getConnectedAt() != null) { + d = new Date(); + l = Long.parseLong(rawVarListMap.getConnectedAt().substring(0, 13)); + d.setTime(l); + person.setDetailConnectedAt(d); + } + + if (rawVarListMap.getStartedAt() != null) { + d = new Date(); + l = Long.parseLong(rawVarListMap.getStartedAt()); + d.setTime(l); + person.setDetailStartedAt(d); + } + + if (rawVarListMap.getEndringAt() != null) { + d = new Date(); + l = Long.parseLong(rawVarListMap.getEndringAt().substring(0, 13)); + d.setTime(l); + person.setDetailEndringAt(d); + } + + if (rawVarListMap.getDisconnectedAt() != null) { + d = new Date(); + l = Long.parseLong(rawVarListMap.getDisconnectedAt().substring(0, 13)); + d.setTime(l); + person.setDetailDisconnectedAt(d); + } + + if (rawVarListMap.getStopedAt() != null) { + d = new Date(); + l = Long.parseLong(rawVarListMap.getStopedAt()); + d.setTime(l); + person.setDetailStopedAt(d); + } + + if (rawVarListMap.getLastModify() != null) { + d = new Date(); + l = Long.parseLong(rawVarListMap.getLastModify()); + d.setTime(l); + person.setDetailLastModify(d); + } + } + personMapper.updateById(person); + boolean anySuccess = markTaskStatus(person.getTaskId()); + if (anySuccess) { + markRemanentPersonStatus(person.getTaskId()); + } + return true; + } + return false; //这个false是平台还未完成重呼,应继续刷接口 + } + + private void markRemanentPersonStatus(Integer taskId) { + personMapper.update(new AutoCallPerson() {{ + setStatus(AutoCallPerson.STATUS_CANCELLED); + setDetailRemark("已呼通其他联系人"); + }}, new QueryWrapper() {{ + eq("task_id", taskId); + eq("status", AutoCallPerson.STATUS_DEFAULT); + }}); + + } + + private boolean markTaskStatus(Integer taskId) { + boolean anySuccess = personMapper.isAnySuccess(taskId); + if (anySuccess) { + taskMapper.setStatus(taskId, AutoCallTask.STATUS_ANY_SUCCESS); + return true; + } + boolean allFail = personMapper.isAllFail(taskId); + if (allFail) { + taskMapper.setStatus(taskId, AutoCallTask.STATUS_ALL_FAIL); + } + return false; + } +} diff --git a/src/main/java/com/whdc/service/IAddressBookOldService.java b/src/main/java/com/whdc/service/IWarningResponderService.java similarity index 50% rename from src/main/java/com/whdc/service/IAddressBookOldService.java rename to src/main/java/com/whdc/service/IWarningResponderService.java index 16538b4..37be020 100644 --- a/src/main/java/com/whdc/service/IAddressBookOldService.java +++ b/src/main/java/com/whdc/service/IWarningResponderService.java @@ -1,7 +1,7 @@ package com.whdc.service; import com.baomidou.mybatisplus.extension.service.IService; -import com.whdc.model.entity.AddressBookOld; +import com.whdc.model.entity.WarningResponder; import java.util.List; @@ -12,6 +12,6 @@ import java.util.List; * @author xusan * @date 2024-05-11 */ -public interface IAddressBookOldService extends IService { - List getListByAdnm2(String cnnm2); +public interface IWarningResponderService extends IService { + List getListByAdnm2(String cnnm2); } \ No newline at end of file diff --git a/src/main/java/com/whdc/service/impl/AddressBookOldServiceImpl.java b/src/main/java/com/whdc/service/impl/AddressBookOldServiceImpl.java deleted file mode 100644 index bd90199..0000000 --- a/src/main/java/com/whdc/service/impl/AddressBookOldServiceImpl.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.whdc.service.impl; - -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.baomidou.mybatisplus.core.metadata.IPage; -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.whdc.mapper.AddressBookOldMapper; -import com.whdc.model.dto.AddressBookOldDto; -import com.whdc.model.entity.AddressBookOld; -import com.whdc.service.IAddressBookOldService; -import org.springframework.stereotype.Service; - -import java.util.List; - -/** - *

- * 服务实现类 - *

- * - * @author xusan - * @date 2024-05-11 - */ -@Service -public class AddressBookOldServiceImpl extends ServiceImpl implements IAddressBookOldService { - - @Override - public List getListByAdnm2(String adnm) { - return baseMapper.getListByAdnm2(adnm); - } -} diff --git a/src/main/java/com/whdc/service/impl/WarningResponderServiceImpl.java b/src/main/java/com/whdc/service/impl/WarningResponderServiceImpl.java new file mode 100644 index 0000000..0dbaaa5 --- /dev/null +++ b/src/main/java/com/whdc/service/impl/WarningResponderServiceImpl.java @@ -0,0 +1,26 @@ +package com.whdc.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.whdc.mapper.WarningResponderMapper; +import com.whdc.model.entity.WarningResponder; +import com.whdc.service.IWarningResponderService; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + *

+ * 服务实现类 + *

+ * + * @author xusan + * @date 2024-05-11 + */ +@Service +public class WarningResponderServiceImpl extends ServiceImpl implements IWarningResponderService { + + @Override + public List getListByAdnm2(String adnm) { + return baseMapper.getListByAdnm2(adnm); + } +} diff --git a/src/main/java/com/whdc/utils/AESpkcs7paddingUtil.java b/src/main/java/com/whdc/utils/AESpkcs7paddingUtil.java new file mode 100644 index 0000000..fcdabba --- /dev/null +++ b/src/main/java/com/whdc/utils/AESpkcs7paddingUtil.java @@ -0,0 +1,181 @@ +package com.whdc.utils; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import org.apache.commons.codec.binary.Base64; +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +import javax.crypto.*; +import javax.crypto.spec.SecretKeySpec; +import java.io.UnsupportedEncodingException; +import java.security.*; + + +public class AESpkcs7paddingUtil { + + /** + * 密钥算法 + */ + private static final String KEY_ALGORITHM = "AES"; + + /** + * 加密/解密算法 / 工作模式 / 填充方式 + * Java 6支持PKCS5Padding填充方式 + * Bouncy Castle支持PKCS7Padding填充方式 + */ + private static final String CIPHER_ALGORITHM = "AES/ECB/PKCS7Padding"; + + static { + //如果是PKCS7Padding填充方式,则必须加上下面这行 + Security.addProvider(new BouncyCastleProvider()); + } + + /** + * 生成密钥 + * + * @return 密钥 + * @throws Exception + */ + public static String generateKey() throws Exception { + //实例化密钥生成器 + KeyGenerator kg = KeyGenerator.getInstance(KEY_ALGORITHM); + /** + * 设置AES密钥长度 + * AES要求密钥长度为128位或192位或256位,java默认限制AES密钥长度最多128位 + * 如需192位或256位,则需要到oracle官网找到对应版本的jdk下载页,在"Additional Resources"中找到 + * "Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files",点击[DOWNLOAD]下载 + * 将下载后的local_policy.jar和US_export_policy.jar放到jdk安装目录下的jre/lib/security/目录下,替换该目录下的同名文件 + */ + kg.init(128); + //生成密钥 + SecretKey secretKey = kg.generateKey(); + //获得密钥的字符串形式 + return Base64.encodeBase64String(secretKey.getEncoded()); + } + + /** + * AES加密 + * + * @param source 源字符串 + * @param key 密钥 + * @return 加密后的密文 + * @throws Exception + */ + public static String encrypt(String source, String key) throws GeneralSecurityException, UnsupportedEncodingException { + + byte[] sourceBytes = source.getBytes("UTF-8"); + byte[] keyBytes = key.getBytes("UTF-8"); + Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM, "BC"); + cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(keyBytes, KEY_ALGORITHM)); + byte[] decrypted = cipher.doFinal(sourceBytes); + return Base64.encodeBase64String(decrypted); + + } + + /** + * AES解密 + * + * @param encryptStr 加密后的密文 + * @param key 密钥 + * @return 源字符串 + * @throws Exception + */ + public static String decrypt(String encryptStr, String key) { + try { + byte[] sourceBytes = Base64.decodeBase64(encryptStr); + final byte[] keyBytes = key.getBytes("UTF-8"); + Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM, "BC"); + cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(keyBytes, KEY_ALGORITHM)); + byte[] decoded = cipher.doFinal(sourceBytes); + return new String(decoded, "UTF-8"); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + public static void main(String[] args) throws Exception { + String data = "{\"sysUserId\":\"HBSL01\",\"expire\":1000000}"; + String encrypt = encrypt(data, "yxt@2024-1234567"); + System.out.println(encrypt); + String request = "{\"request\":\"" + encrypt + "\"}"; + String resp = HttpUtil.sendPost("https://aicc.cuopen.net:9801/aicc-api/ssb/callout/thirdParty/login", request); + System.out.println(resp); + + /* + 挂断是无人接听 + */ + +// genTask(); +// genQueryTask(); +// String request = "{\n" + +// " \"requestId\": \"hbsl1750130919921\",\n" + +// " \"custId\": \"hbsl-zj\"\n" + +// "}"; +// JSONObject header = new JSONObject(); +// header.put("X-Access-Token", "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Ik5TQkRKSzIwMjUiLCJ0ZW5hbnRJZCI6IjE5MTY1NDI5IiwiZXhwIjoxNzUwNDI5OTAzfQ.RvsTL5L8jPl6GqiQq344jBP5i-v6vuQt-kARsvUvuyY"); +// String resp = HttpUtil.sendPost("https://aicc.cuopen.net:9801/aicc-api/ssb/callout/thirdParty/task/getTaskCallDetail", request, header); +// AICCCallRespDetail autoCallDetail = JSON.parseObject(resp, AICCCallRespDetail.class); +// autoCallDetail.getResult().getData().getRecords().forEach(System.out::println); + + +// AutoCallHelper helper = new AutoCallHelper(); +// helper.initToken(); +// String token = helper.getToken(); +// genTask(); + } + + private static void genQueryTask() throws Exception { + String requestId = "hbsl1750130186416"; + String custId = "detail2c9ab2f08709s438018722de2ebf001f"; + String data = "{\"requestId\":\"" + requestId + "\",\"custId\":\"" + custId + "\"}"; + System.out.println(data); + String e = encrypt(data, "yxt@2024-1234567"); + System.out.println(e); + } + + private static void genTask() throws Exception { + String taskName = "hbsl" + System.currentTimeMillis(); + String processId = "1934801506242949122"; +// String processId = "1934876091344777218"; + String requestId = taskName; + String callerGroup = "02160980358"; +// String callerGroup = "01080945234"; + String content = "这里是湖北省防汛抗旱指挥部办公室,咸宁市2025年06月15日02时26分58秒发布暴雨橙色预警信号:过去6小时最大降水出现在青山镇(青山水库)为54.6毫米。预计今天夜间,崇阳南部将出现暴雨到大暴雨,累计降雨量将达100毫米以上,并伴有雷暴大风,山区山洪、地质灾害、城乡积涝、中小河流洪水风险高,请加强防范。"; +// String content = "咸宁市2025年06月15日02时26分58秒发布暴雨橙色预警信号"; + JSONObject data = new JSONObject(); + data.put("taskName", taskName); + data.put("processId", processId); + data.put("callerGroup", callerGroup); + data.put("requestId", requestId); + data.put("calleeType", 2); + data.put("repeatTimes", 2); + data.put("autoCall", "0"); + data.put("spanSeconds", 60); + data.put("processType", "3"); + data.put("mutiTimeRange", "2025-06-17 10:00:00_2025-06-17 21:00:00"); + JSONArray params = new JSONArray(); + JSONObject param = new JSONObject(); + param.put("@common_user_name", "李"); +// param.put("@NUMBER", "15671545233"); + param.put("@NUMBER", "15671545233"); + param.put("备用号码1", "18162570850"); +// param.put("备用号码1", "13933930962"); +// param.put("备用号码2", "15697168180"); + param.put("custId", "abo70652-abo70653-abo70654"); +// param.put("address", "{}"); + param.put("content", content); + params.add(param); + data.put("param", params); + System.out.println(data); + + String e = encrypt(data.toJSONString(), "yxt@2024-1234567"); + + JSONObject request = new JSONObject(); + request.put("request", e); + JSONObject header = new JSONObject(); + header.put("X-Access-Token", "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Ik5TQkRKSzIwMjUiLCJ0ZW5hbnRJZCI6IjE5MTY1NDI5IiwiZXhwIjoxNzUwNDI5OTAzfQ.RvsTL5L8jPl6GqiQq344jBP5i-v6vuQt-kARsvUvuyY"); + HttpUtil.sendPost("https://aicc.cuopen.net:9801/aicc-api/ssb/callout/thirdParty/task/uploadCallData", request.toJSONString(), header); + + } +} \ No newline at end of file diff --git a/src/main/java/com/whdc/utils/AICCHelper.java b/src/main/java/com/whdc/utils/AICCHelper.java new file mode 100644 index 0000000..8c55819 --- /dev/null +++ b/src/main/java/com/whdc/utils/AICCHelper.java @@ -0,0 +1,243 @@ +package com.whdc.utils; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.TypeReference; +import com.whdc.model.entity.autocall.*; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.io.UnsupportedEncodingException; +import java.security.GeneralSecurityException; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; + +/** + * @author lyf + * @since 2025-06-19 + */ +@Component +@Slf4j +public class AICCHelper { + @Setter + @Value("${autocall.processId}") + private String processId; + @Setter + @Value("${autocall.callerGroup}") + private String callerGroup; + @Setter + @Value("${autocall.secret}") + private String secret; + @Setter + @Value("${autocall.sysUserId}") + private String sysUserId; + @Autowired + private HttpHelper httpHelper = new HttpHelper(); + + private static final AtomicBoolean isAcquiringToken = new AtomicBoolean(false); + private static volatile CountDownLatch latch = new CountDownLatch(0); + private static final AtomicReference globalToken = new AtomicReference<>(); + + public String getToken() { + if (globalToken.get() == null) { + initToken(); + } + return globalToken.get(); + } + + public void initToken() throws RuntimeException { + if (isAcquiringToken.compareAndSet(false, true)) { + globalToken.set(null); + try { + String data = "{\"sysUserId\":\"" + sysUserId + "\",\"expire\":1000000}"; + String encrypt; + try { + encrypt = AESpkcs7paddingUtil.encrypt(data, secret); + } catch (Exception e) { + throw new RuntimeException(e); + } + JSONObject request = new JSONObject(); + request.put("request", encrypt); + Map headers = new HashMap<>(); + headers.put("X-Access-Token", getToken()); + String resp = httpHelper.postJsonString("https://aicc.cuopen.net:9801/aicc-api/ssb/callout/thirdParty/login", request.toJSONString(), headers); + + TypeReference> type = new TypeReference>() { + }; + AICCCallRespWrapper AICCCallRespWrapper = JSON.parseObject(resp, type); + if (AICCCallRespWrapper == null || !AICCCallRespWrapper.isSuccess() || AICCCallRespWrapper.getResult() == null) { + log.warn("获取外呼系统token失败"); + return; + } + + AICCLogin result = AICCCallRespWrapper.getResult(); + String token = result.getToken(); + if (token != null && !token.isEmpty()) { + globalToken.set(token); + } + } finally { + isAcquiringToken.set(false); + } + } else { + try { + latch.await(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + } + + public AICCCallRespWrapper apiUploadCallData(AICCUploadTask data) throws GeneralSecurityException, UnsupportedEncodingException { + return apiUploadCallData(data, getToken()); + } + + public AICCCallRespWrapper apiUploadCallData(AICCUploadTask data, String token) throws GeneralSecurityException, UnsupportedEncodingException { + String e; + try { + e = AESpkcs7paddingUtil.encrypt(JSON.toJSONString(data), secret); + } catch (GeneralSecurityException | UnsupportedEncodingException ex) { + throw ex; + } catch (Exception ex) { + throw new RuntimeException(ex); + } + JSONObject request = new JSONObject(); + request.put("request", e); + Map headers = new HashMap<>(); + headers.put("X-Access-Token", token); + String resp = httpHelper.postJsonString("https://aicc.cuopen.net:9801/aicc-api/ssb/callout/thirdParty/task/uploadCallData", request.toJSONString(), headers); + log.info("apiUploadCallData: {}", resp); + TypeReference> type = new TypeReference>() { + }; + AICCCallRespWrapper AICCCallRespWrapper = JSON.parseObject(resp, type); + if (AICCCallRespWrapper == null || !AICCCallRespWrapper.isSuccess()) { + initToken(); + headers.put("X-Access-Token", getToken()); + resp = httpHelper.postJsonString("https://aicc.cuopen.net:9801/aicc-api/ssb/callout/thirdParty/task/uploadCallData", request.toJSONString(), headers); + AICCCallRespWrapper = JSON.parseObject(resp, type); + } + + if (AICCCallRespWrapper == null || !AICCCallRespWrapper.isSuccess()) { + return null; + } + return AICCCallRespWrapper; + } + + public AICCCallRespWrapper apiGetTaskCallDetail(String requestId, String custId) { + JSONObject request = new JSONObject(); + request.put("requestId", requestId); + request.put("custId", custId); + Map headers = new HashMap<>(); + headers.put("X-Access-Token", getToken()); + String resp = httpHelper.postJsonString("https://aicc.cuopen.net:9801/aicc-api/ssb/callout/thirdParty/task/getTaskCallDetail", request.toJSONString(),headers); + + TypeReference> type = new TypeReference>() { + }; + AICCCallRespWrapper AICCCallRespWrapper = JSON.parseObject(resp, type); + if (AICCCallRespWrapper == null || !AICCCallRespWrapper.isSuccess()) { + initToken(); + headers.put("X-Access-Token", getToken()); + resp = httpHelper.postJsonString("https://aicc.cuopen.net:9801/aicc-api/ssb/callout/thirdParty/task/getTaskCallDetail", request.toJSONString(), headers); + AICCCallRespWrapper = JSON.parseObject(resp, type); + } + + if (AICCCallRespWrapper == null || !AICCCallRespWrapper.isSuccess()) { + return null; + } + return AICCCallRespWrapper; + } + + public AICCUploadTask newTask(String requestId, String custId, String custName, String content, List numbers) { + AICCUploadTask task = new AICCUploadTask(); + task.setTaskName(requestId); + task.setProcessId(processId); + task.setCallerGroup(callerGroup); + task.setRequestId(requestId); + task.genMutiTimeRange(); + AICCUploadTask.Cust param = AICCUploadTask.Cust.builder() + .setCustName(custName) + .setCustId(custId) + .setContent(content) + .set_numbers(numbers) + .build(); + task.setParam(Collections.singletonList(param)); + + return task; + } + +// private String sendPost(String url, JSONObject jsonData) { +// String resp = sendPost(url, jsonData.toJSONString()); +// if (resp == null) { +// resp = sendPost(url, jsonData.toJSONString()); +// } +// return resp; +// } +// +// private String sendPost(String url, JSONObject jsonData, String token) { +// String resp = sendPost(url, jsonData.toJSONString(), token); +// if (resp == null) { +// resp = sendPost(url, jsonData.toJSONString(), token); +// } +// return resp; +// } +// +// private String sendPost(String url, String jsonData) { +// return sendPost(url, jsonData, getToken()); +// } +// +// private String sendPost(String url, String jsonData, String token) { +// CloseableHttpResponse response = null; +// CloseableHttpClient httpClient = null; +// String responseContent = null; +// try { +// httpClient = HttpClients.createDefault(); +// HttpPost httpPost = new HttpPost(url); +// httpPost.addHeader("Content-Type", "application/json"); +// httpPost.addHeader("X-Access-Token", token); +// +// if (StringUtils.isNotBlank(jsonData)) { +// httpPost.setEntity(new StringEntity(jsonData, "UTF-8")); +// } +// +// log.info("请求地址: " + url); +// log.info("token: " + getToken()); +// log.info("请求参数: " + jsonData); +// +// response = httpClient.execute(httpPost); +// HttpEntity entity = response.getEntity(); +// responseContent = EntityUtils.toString(entity, "UTF-8"); +// responseContent = jsonFormat(responseContent); +// if (responseContent.length() < 200) { +// log.info("响应参数: " + responseContent); +// } +// } catch (Exception e) { +// log.error("发送请求异常", e); +// return null; +// } finally { +// try { +// if (null != response) { +// response.close(); +// } +// httpClient.close(); +// } catch (IOException e) { +// e.printStackTrace(); +// } +// } +// return responseContent; +// } + + private static String jsonFormat(String str) { + if (JSON.isValidObject(str)) { + str = JSON.parseObject(str).toJSONString(); + } + + return str; + } +} diff --git a/src/main/java/com/whdc/utils/AutoCallHelper.java b/src/main/java/com/whdc/utils/AutoCallHelper.java new file mode 100644 index 0000000..d9922ab --- /dev/null +++ b/src/main/java/com/whdc/utils/AutoCallHelper.java @@ -0,0 +1,235 @@ +package com.whdc.utils; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.TypeReference; +import com.whdc.model.entity.autocall.*; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.apache.http.HttpEntity; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.util.EntityUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; + +/** + * @author lyf + * @since 2025-06-19 + */ +@Component +@Slf4j +public class AutoCallHelper { + @Setter + @Value("${autocall.processId}") + private String processId; + @Setter + @Value("${autocall.callerGroup}") + private String callerGroup; + @Setter + @Value("${autocall.secret}") + private String secret; + @Setter + @Value("${autocall.sysUserId}") + private String sysUserId; + + private static final AtomicBoolean isAcquiringToken = new AtomicBoolean(false); + private static volatile CountDownLatch latch = new CountDownLatch(0); + private static final AtomicReference globalToken = new AtomicReference<>(); + + public String getToken() { + if (globalToken.get() == null) { + initToken(); + } + return globalToken.get(); + } + + public void initToken() throws RuntimeException { + if (isAcquiringToken.compareAndSet(false, true)) { + globalToken.set(null); + try { + String data = "{\"sysUserId\":\"" + sysUserId + "\",\"expire\":1000000}"; + String encrypt; + try { + encrypt = AESpkcs7paddingUtil.encrypt(data, secret); + } catch (Exception e) { + throw new RuntimeException(e); + } + JSONObject request = new JSONObject(); + request.put("request", encrypt); + String resp = sendPost("https://aicc.cuopen.net:9801/aicc-api/ssb/callout/thirdParty/login", request); + + TypeReference> type = new TypeReference>() { + }; + AICCCallRespWrapper AICCCallRespWrapper = JSON.parseObject(resp, type); + if (AICCCallRespWrapper == null || !AICCCallRespWrapper.isSuccess() || AICCCallRespWrapper.getResult() == null) { + log.warn("获取外呼系统token失败"); + return; + } + + AICCLogin result = AICCCallRespWrapper.getResult(); + String token = result.getToken(); + if (token != null && !token.isEmpty()) { + globalToken.set(token); + } + } finally { + isAcquiringToken.set(false); + } + } else { + try { + latch.await(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + } + + public AICCCallRespWrapper apiUploadCallData(AICCUploadTask data) { + return apiUploadCallData(data, getToken()); + } + + public AICCCallRespWrapper apiUploadCallData(AICCUploadTask data, String token) { + String e; + try { + e = AESpkcs7paddingUtil.encrypt(JSON.toJSONString(data), secret); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + JSONObject request = new JSONObject(); + request.put("request", e); + String resp = sendPost("https://aicc.cuopen.net:9801/aicc-api/ssb/callout/thirdParty/task/uploadCallData", request, token); + + TypeReference> type = new TypeReference>() { + }; + AICCCallRespWrapper AICCCallRespWrapper = JSON.parseObject(resp, type); + if (AICCCallRespWrapper == null || !AICCCallRespWrapper.isSuccess()) { + initToken(); + resp = sendPost("https://aicc.cuopen.net:9801/aicc-api/ssb/callout/thirdParty/task/uploadCallData", request, token); + AICCCallRespWrapper = JSON.parseObject(resp, type); + } + + if (AICCCallRespWrapper == null || !AICCCallRespWrapper.isSuccess()) { + return null; + } + return AICCCallRespWrapper; + } + + public AICCCallRespWrapper apiGetTaskCallDetail(String requestId, String custId) { + JSONObject request = new JSONObject(); + request.put("requestId", requestId); + request.put("custId", custId); + String resp = sendPost("https://aicc.cuopen.net:9801/aicc-api/ssb/callout/thirdParty/task/getTaskCallDetail", request); + + TypeReference> type = new TypeReference>() { + }; + AICCCallRespWrapper AICCCallRespWrapper = JSON.parseObject(resp, type); + if (AICCCallRespWrapper == null || !AICCCallRespWrapper.isSuccess()) { + initToken(); + resp = sendPost("https://aicc.cuopen.net:9801/aicc-api/ssb/callout/thirdParty/task/getTaskCallDetail", request); + AICCCallRespWrapper = JSON.parseObject(resp, type); + } + + if (AICCCallRespWrapper == null || !AICCCallRespWrapper.isSuccess()) { + return null; + } + return AICCCallRespWrapper; + } + + public AICCUploadTask newTask(String requestId, String custId, String custName, String content, List numbers) { + AICCUploadTask task = new AICCUploadTask(); + task.setTaskName(requestId); + task.setProcessId(processId); + task.setCallerGroup(callerGroup); + task.setRequestId(requestId); + task.genMutiTimeRange(); + AICCUploadTask.Cust param = AICCUploadTask.Cust.builder() + .setCustName(custName) + .setCustId(custId) + .setContent(content) + .set_numbers(numbers) + .build(); + task.setParam(Collections.singletonList(param)); + + return task; + } + + private String sendPost(String url, JSONObject jsonData) { + String resp = sendPost(url, jsonData.toJSONString()); + if (resp == null) { + resp = sendPost(url, jsonData.toJSONString()); + } + return resp; + } + + private String sendPost(String url, JSONObject jsonData, String token) { + String resp = sendPost(url, jsonData.toJSONString(), token); + if (resp == null) { + resp = sendPost(url, jsonData.toJSONString(), token); + } + return resp; + } + + private String sendPost(String url, String jsonData) { + return sendPost(url, jsonData, getToken()); + } + + private String sendPost(String url, String jsonData, String token) { + CloseableHttpResponse response = null; + CloseableHttpClient httpClient = null; + String responseContent = null; + try { + httpClient = HttpClients.createDefault(); + HttpPost httpPost = new HttpPost(url); + httpPost.addHeader("Content-Type", "application/json"); + httpPost.addHeader("X-Access-Token", token); + + if (StringUtils.isNotBlank(jsonData)) { + httpPost.setEntity(new StringEntity(jsonData, "UTF-8")); + } + + log.info("请求地址: " + url); + log.info("token: " + getToken()); + log.info("请求参数: " + jsonData); + + response = httpClient.execute(httpPost); + HttpEntity entity = response.getEntity(); + responseContent = EntityUtils.toString(entity, "UTF-8"); + responseContent = jsonFormat(responseContent); + if (responseContent.length() < 200) { + log.info("响应参数: " + responseContent); + } + } catch (Exception e) { + log.error("发送请求异常", e); + return null; + } finally { + try { + if (null != response) { + response.close(); + } + httpClient.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + return responseContent; + } + + private static String jsonFormat(String str) { + if (JSON.isValidObject(str)) { + str = JSON.parseObject(str).toJSONString(); + } + + return str; + } +} diff --git a/src/main/java/com/whdc/utils/DateUtils.java b/src/main/java/com/whdc/utils/DateUtils.java index a445b7c..6508a7a 100644 --- a/src/main/java/com/whdc/utils/DateUtils.java +++ b/src/main/java/com/whdc/utils/DateUtils.java @@ -4,10 +4,7 @@ import java.text.ParseException; import java.text.SimpleDateFormat; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.Objects; -import java.util.SimpleTimeZone; +import java.util.*; /** * @author 李赛 @@ -118,6 +115,23 @@ public class DateUtils { return simpleDateFormat.parse(dateStr); } + public static Date standardize(Date date, boolean stm) { + Calendar c = Calendar.getInstance(); + c.setTime(date); + if (stm) { + c.set(Calendar.HOUR_OF_DAY, 0); + c.set(Calendar.MINUTE, 0); + c.set(Calendar.SECOND, 0); + c.set(Calendar.MILLISECOND, 0); + } else { + c.set(Calendar.HOUR_OF_DAY, 23); + c.set(Calendar.MINUTE, 59); + c.set(Calendar.SECOND, 59); + c.set(Calendar.MILLISECOND, 999); + } + return c.getTime(); + } + public static void main(String[] args) throws ParseException { diff --git a/src/main/java/com/whdc/utils/HttpHelper.java b/src/main/java/com/whdc/utils/HttpHelper.java new file mode 100644 index 0000000..0c1b919 --- /dev/null +++ b/src/main/java/com/whdc/utils/HttpHelper.java @@ -0,0 +1,216 @@ +package com.whdc.utils; + +import lombok.extern.slf4j.Slf4j; +import okhttp3.*; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.io.File; +import java.io.IOException; +import java.net.URLEncoder; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.Map; + +/** + * @author lyf + * @since 2025-06-25 + */ +@Slf4j +@Component +public class HttpHelper { + @Autowired + private OkHttpClient okHttpClient = new OkHttpClient.Builder().build(); + + public @NotNull String get(@NotNull String url, @Nullable Map params, @Nullable Map headers) { + if (url.isEmpty()) { + return "url为空"; + } + + Request.Builder requestBuilder = new Request.Builder() + .url(url) + .get(); + + //header + if (headers != null) { + for (Map.Entry entry : headers.entrySet()) { + String key = entry.getKey(); + String value = entry.getValue(); + if (key != null && value != null) { + requestBuilder.header(key, value); + } + } + } + //doGet + Request request = requestBuilder.build(); + + return doGet(request); + } + + private @NotNull String doGet(Request request) { + try (Response response = okHttpClient.newCall(request).execute()) { + if (response.isSuccessful() && response.body() != null) { + return response.body().string(); + } else { + return "请求失败:" + response; + } + } catch (IOException e) { + return "网络异常:" + e.getMessage(); + } + } + + public @NotNull String postJsonString(@NotNull String url, @Nullable String json, @Nullable Map headers) { + return postJsonString(url, json, headers, StandardCharsets.UTF_8); + } + + public @NotNull String postJsonString(@NotNull String url, @Nullable String json, @Nullable Map headers, @NotNull Charset charset) { + if (url.isEmpty()) { + return "url为空"; + } + if (json == null) { + json = "{}"; + } + + //mediatype + MediaType mediaType = MediaType.get("application/json"); + //payload + RequestBody requestBody = RequestBody.create(json, mediaType); + //builder + Request.Builder requestBuilder = new Request.Builder() + .url(url) + .header("Content-Type", mediaType + ";charset=" + charset.name()) + .post(requestBody); + //header + if (headers != null) { + for (Map.Entry entry : headers.entrySet()) { + String key = entry.getKey(); + String value = entry.getValue(); + if (key != null && value != null) { + requestBuilder.header(key, value); + } + } + } + Request request = requestBuilder.build(); + //post + return doPost(request); + } + + public @NotNull String postFormData(@NotNull String url, @Nullable Map params, @Nullable Map headers) { + return postFormData(url, params, headers, StandardCharsets.UTF_8); + } + + public @NotNull String postFormData(@NotNull String url, @Nullable Map params, @Nullable Map headers, @NotNull Charset charset) { + if (url.isEmpty()) { + return "url为空"; + } + + //mediatype + MediaType mediaType = MultipartBody.FORM; + //payload + MultipartBody.Builder bodyBuilder = new MultipartBody.Builder() + .setType(MultipartBody.FORM); + if (params != null) { + for (Map.Entry entry : params.entrySet()) { + String key = entry.getKey(); + Object value = entry.getValue(); + if (key == null || value == null) { + continue; + } + if (value instanceof String) { + bodyBuilder.addFormDataPart(key, (String) value); + } else if (value instanceof File) { + File file = (File) value; + RequestBody fileBody = RequestBody.create(file, MediaType.get("application/octet-stream")); + bodyBuilder.addFormDataPart(key, file.getName(), fileBody); + } else { + return "不支持的参数类型"; + } + } + } + RequestBody requestBody = bodyBuilder.build(); + //builder + Request.Builder requestBuilder = new Request.Builder() + .url(url) + .header("Content-Type", mediaType + ";charset=" + charset.name()) + .post(requestBody); + //header + if (headers != null) { + for (Map.Entry entry : headers.entrySet()) { + String key = entry.getKey(); + String value = entry.getValue(); + if (key != null && value != null) { + requestBuilder.header(key, value); + } + } + } + //post + Request request = requestBuilder.build(); + + return doPost(request); + } + + public @NotNull String postFormUrlEncoded(@NotNull String url, @Nullable Map params, @Nullable Map headers) { + return postFormUrlEncoded(url, params, headers, StandardCharsets.UTF_8); + } + + public @NotNull String postFormUrlEncoded(@NotNull String url, @Nullable Map params, @Nullable Map headers, @NotNull Charset charset) { + if (url.isEmpty()) { + return "url为空"; + } + + //mediatype + MediaType mediaType = MediaType.get("application/x-www-form-urlencoded"); + //payload + FormBody.Builder bodyBuilder = new FormBody.Builder(charset); + if (params != null) { + for (Map.Entry entry : params.entrySet()) { + String key = entry.getKey(); + Object value = entry.getValue(); + if (key != null && value != null) { + try { + // Manually encode using GBK + String encodedValue = URLEncoder.encode(value.toString(), "GBK"); + bodyBuilder.addEncoded(key, encodedValue); + } catch (Exception e) { + return "参数encode错误"; + } + } + } + } + RequestBody formBody = bodyBuilder.build(); + //builder + Request.Builder requestBuilder = new Request.Builder() + .url(url) + .header("Content-Type", mediaType + ";charset=" + charset.name()) + .post(formBody); + //header + if (headers != null) { + for (Map.Entry entry : headers.entrySet()) { + String key = entry.getKey(); + String value = entry.getValue(); + if (key != null && value != null) { + requestBuilder.header(key, value); + } + } + } + //post + Request request = requestBuilder.build(); + + return doPost(request); + } + + private @NotNull String doPost(Request request) { + try (Response response = okHttpClient.newCall(request).execute()) { + if (response.isSuccessful() && response.body() != null) { + return response.body().string(); + } else { + return "请求失败:" + response; + } + } catch (IOException e) { + return "网络异常:" + e.getMessage(); + } + } + +} diff --git a/src/main/java/com/whdc/utils/HttpUtil.java b/src/main/java/com/whdc/utils/HttpUtil.java index d4fea85..d11a718 100644 --- a/src/main/java/com/whdc/utils/HttpUtil.java +++ b/src/main/java/com/whdc/utils/HttpUtil.java @@ -393,6 +393,7 @@ public class HttpUtil { } log.info("请求地址: " + url); + log.info("请求header: " + header); log.info("请求参数: " + jsonData); @@ -402,7 +403,9 @@ public class HttpUtil { responseContent = jsonFormat(responseContent); - log.info("响应参数: " + responseContent); + if (responseContent.length() < 200) { + log.info("响应参数: " + responseContent); + } } catch (Exception e) { log.error("发送请求异常", e); diff --git a/src/main/java/com/whdc/utils/SmsHelper.java b/src/main/java/com/whdc/utils/SmsHelper.java new file mode 100644 index 0000000..ebd6c69 --- /dev/null +++ b/src/main/java/com/whdc/utils/SmsHelper.java @@ -0,0 +1,174 @@ +package com.whdc.utils; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.nio.charset.Charset; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.text.SimpleDateFormat; +import java.util.*; + +/** + * @author lyf + * @since 2025-06-25 + */ +@Component +@Slf4j +public class SmsHelper { + @Value("${sms.url}") + private String url; + @Value("${sms.userName}") + private String userName; + @Value("${sms.serviceCode}") + private String serviceCode; + @Value("${sms.userPassword}") + private String userPassword; + @Autowired + private HttpHelper httpHelper; + + public String send(List phones, String content) { + return send(phones, content, "201"); + } + + private String send(List phones, String content, String msgType) { + if (phones == null || phones.size() == 0) { + return "未选择手机号"; + } + if (content == null || content.length() == 0) { + return "短信内容为空"; + } + + String[] msgIds = new String[phones.size()]; + for (int i = 0; i < phones.size(); i++) { + msgIds[i] = UUID.randomUUID().toString().replace("-", ""); + } + String strPhones = String.join(",", phones); + String strMsgIds = String.join(",", msgIds); + String timestamp = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()); + String sign = md5(userPassword + timestamp).toUpperCase(); + + Map params = new HashMap<>(); + params.put("serviceCode", serviceCode); + params.put("userName", userName); + params.put("userPassword", userPassword); + params.put("phones", strPhones); + params.put("msgContent", encodeGBK(content)); + params.put("timestamp", timestamp); + params.put("sign", sign); + params.put("mhtMsgIds", strMsgIds); + params.put("sendTime", ""); + params.put("priority", "5"); + params.put("orgCode", "42"); + params.put("msgType", msgType); + params.put("reportFlag", "0"); + + String strResult = httpHelper.postFormUrlEncoded(url, params, null, Charset.forName("GBK")); + if (strResult == null) { + return "发送失败"; + } + +// Map result = new ObjectMapper().readValue(strResult, Map.class); + JSONObject result = JSON.parseObject(strResult); + if (result == null) { + return "发送结果解析失败"; + } + int errorCode = (Integer) result.get("result"); + if (errorCode != 0) { + return SmtpErrorCode.getErrorString(errorCode); + } + + return "发送成功"; + } + + private String md5(String str) { + MessageDigest m; + try { + m = MessageDigest.getInstance("MD5"); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + m.update(str.getBytes()); + byte[] s = m.digest(); + return parseByte2HexStr(s); + } + + private static String encodeGBK(String content) { + try { + return URLDecoder.decode(content, "GBK"); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } + + //将二进制数组转换成16进制字符串 + private String parseByte2HexStr(byte[] buf) { + StringBuilder sb = new StringBuilder(); + for (byte b : buf) { + String hex = Integer.toHexString(b & 0xFF); + if (hex.length() == 1) { + hex = '0' + hex; + } + sb.append(hex); + } + return sb.toString(); + } + + public static class SmtpErrorCode { + private static Map map; + + static { + map = new HashMap<>(); + + map.put(100, "非法登录,如登录名、口令出错、登录名与口令不符,md5 值不匹配等。"); + map.put(101, "连接过多,指单个商户要求同时建立的连接数过多。"); + map.put(102, "登陆类型错。"); + map.put(103, "协议版本号错"); + map.put(104, "ip 不合法,请联系管理员绑定 ip 后才可正常使用"); + map.put(105, "Smtp 的 tcp 协议只支持后付费类型"); + + map.put(201, "非法手机号码。"); + map.put(202, "reportFlag 值不合法,值必须为 0 或 1"); + map.put(203, "信息长度错"); + map.put(204, "短信内容中有非法字符"); + map.put(205, "短信内容太长"); + map.put(206, "不存在的优先级"); + map.put(207, "SUBMIT 命令中的 serviceCode 不合法或者过期"); + map.put(208, "手机号和商户消息 id 个数不匹配"); + map.put(209, "orgCode 字段超过规定长度"); + map.put(210, "msgType 字段超过规定长度"); + map.put(211, "http 提交短信时的 url 不正确"); + map.put(212, "被代理的商户与代理商户之间的关系不合法"); + map.put(213, "暂未开通此商户通道,或者商户通道名称配置错误"); + map.put(214, "请求参数有误,比如传递了不在接口文档中定义的参数"); + map.put(215, "被代理的商户 id 不存在"); + map.put(216, "商户平台的消息 id 的长度超过最大长度"); + map.put(217, "sendTime 参数时间戳格式不正确"); + map.put(218, "商户服务代码与用户名密码不匹配"); + map.put(219, "接口只支持 HTTP POST 方式提交,而您的请求为 HTTP GET 方式"); + map.put(220, "批量提交一次最多只支持 50 个"); + map.put(221, "手机号不能为空"); + map.put(222, "短信内容含有乱码"); + + map.put(500, "系统内部失败"); + + map.put(900, "余额不足"); + map.put(901, "发送速度太快"); + } + + public static String getErrorString(int errorcode) { + if (!map.containsKey(errorcode)) { + return "errorcode:" + errorcode + "未定义"; + } + + return map.get(errorcode); + } + } + +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 61fdd86..574c25a 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -6,6 +6,10 @@ server: session: # 配置会话超时 timeout: 120s + encoding: + charset: UTF-8 + enabled: true + force: true spring: #数据库配置 datasource: @@ -85,4 +89,17 @@ wx: secret: 99a1b89ac30e28bcc9bba8be973027f4 #getGroupWarning: http://223.75.53.141:8000/shzh/met/zyqxfw/api/warning/getGroupWarning -getGroupWarning: http://127.0.0.1:20000/shzh/met/zyqxfw/api/warning/getGroupWarning \ No newline at end of file +getGroupWarning: http://127.0.0.1:20000/shzh/met/zyqxfw/api/warning/getGroupWarning + +autocall: + sysUserId: HBSL01 # 平台网页https://aicc.cuopen.net:9801/login 密码Gswl@2025 + processId: 1935233125932101634 +# processId: 1934801506242949122 +# callerGroup: 02160980358 + callerGroup: 02759325005 + secret: yxt@2024-1234567 +sms: + url: http://120.55.193.51:8098/smtp/http/submit + userName: hbfx15 + serviceCode: hbfx15 + userPassword: hbfx15 \ No newline at end of file diff --git a/src/main/resources/log4j.properties b/src/main/resources/log4j.properties new file mode 100644 index 0000000..a6233d5 --- /dev/null +++ b/src/main/resources/log4j.properties @@ -0,0 +1,2 @@ +log4j.rootLogger=ERROR +log4j.logger.org.apache.http=ERROR \ No newline at end of file diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml index a7f2a35..09f1447 100644 --- a/src/main/resources/logback-spring.xml +++ b/src/main/resources/logback-spring.xml @@ -34,7 +34,7 @@ - debug + info ${CONSOLE_LOG_PATTERN} @@ -199,15 +199,16 @@ + - + - + diff --git a/src/main/resources/mapper/AutoCallMapper.xml b/src/main/resources/mapper/AutoCallMapper.xml new file mode 100644 index 0000000..e53f442 --- /dev/null +++ b/src/main/resources/mapper/AutoCallMapper.xml @@ -0,0 +1,23 @@ + + + + + \ No newline at end of file diff --git a/src/main/resources/mapper/AutoCallTaskMapper.xml b/src/main/resources/mapper/AutoCallTaskMapper.xml new file mode 100644 index 0000000..5f684bd --- /dev/null +++ b/src/main/resources/mapper/AutoCallTaskMapper.xml @@ -0,0 +1,31 @@ + + + + + + \ No newline at end of file diff --git a/src/main/resources/mapper/QXWarningMapper.xml b/src/main/resources/mapper/QXWarningMapper.xml index 47a4938..8f10aa7 100644 --- a/src/main/resources/mapper/QXWarningMapper.xml +++ b/src/main/resources/mapper/QXWarningMapper.xml @@ -29,6 +29,17 @@ WF.ID IS NULL ORDER BY Q.WARNID DESC + + 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; + + + \ No newline at end of file diff --git a/src/main/resources/mapper/AddressBookOldMapper.xml b/src/main/resources/mapper/WarningResponderMapper.xml similarity index 86% rename from src/main/resources/mapper/AddressBookOldMapper.xml rename to src/main/resources/mapper/WarningResponderMapper.xml index d377491..80041f8 100644 --- a/src/main/resources/mapper/AddressBookOldMapper.xml +++ b/src/main/resources/mapper/WarningResponderMapper.xml @@ -1,6 +1,6 @@ - + - select * from ADDRESS_BOOK_OLD A where 1=1 and @@ -97,5 +97,18 @@ + \ No newline at end of file