From 0d6465fb0f4ef534d61b4ee86704a9194966a5a0 Mon Sep 17 00:00:00 2001 From: zhouxy Date: Fri, 22 May 2026 14:28:45 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=A1=E7=94=A8=E7=9B=91=E7=AE=A1=E5=AF=B9?= =?UTF-8?q?=E6=8E=A5=E7=BB=9F=E4=B8=80=E7=9B=91=E7=AE=A1=E9=97=A8=E6=88=B7?= =?UTF-8?q?=E7=9A=84=E5=BE=85=E5=8A=9E=E4=BA=8B=E9=A1=B9=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/impl/TSTaskListServiceImpl.java | 21 + .../JgmhPortalTodoDebugController.java | 73 ++++ .../aiccs/door/dto/JgmhDebugPushResult.java | 42 ++ .../door/entity/PortalTodoPushRecord.java | 60 +++ .../mapper/PortalTodoPushRecordMapper.java | 8 + .../door/service/IJgmhPortalTodoService.java | 37 ++ .../service/PortalTodoPushRecordService.java | 8 + .../impl/JgmhPortalTodoServiceImpl.java | 404 ++++++++++++++++++ .../impl/PortalTodoPushRecordServiceImpl.java | 14 + .../aiccs/outside/api/JgmhDoorApi.java | 27 ++ .../aiccs/outside/dto/JgmhTodoItemDto.java | 40 ++ .../aiccs/outside/dto/JgmhTodoRequestDto.java | 15 + .../schedule/JgmhPortalTodoPushScheduled.java | 58 +++ src/main/resources/application-dev.yml | 2 + src/main/resources/application-prod129.yml | 2 + src/main/resources/application-prod138.yml | 2 + src/main/resources/application-prod171.yml | 2 + src/main/resources/application-prod172.yml | 2 + src/main/resources/application-prod73.yml | 4 +- src/main/resources/application.yml | 9 + .../aiccs/door/PortalTodoPushRecordMapper.xml | 5 + 21 files changed, 834 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/chinaweal/aiccs/door/controller/JgmhPortalTodoDebugController.java create mode 100644 src/main/java/com/chinaweal/aiccs/door/dto/JgmhDebugPushResult.java create mode 100644 src/main/java/com/chinaweal/aiccs/door/entity/PortalTodoPushRecord.java create mode 100644 src/main/java/com/chinaweal/aiccs/door/mapper/PortalTodoPushRecordMapper.java create mode 100644 src/main/java/com/chinaweal/aiccs/door/service/IJgmhPortalTodoService.java create mode 100644 src/main/java/com/chinaweal/aiccs/door/service/PortalTodoPushRecordService.java create mode 100644 src/main/java/com/chinaweal/aiccs/door/service/impl/JgmhPortalTodoServiceImpl.java create mode 100644 src/main/java/com/chinaweal/aiccs/door/service/impl/PortalTodoPushRecordServiceImpl.java create mode 100644 src/main/java/com/chinaweal/aiccs/outside/api/JgmhDoorApi.java create mode 100644 src/main/java/com/chinaweal/aiccs/outside/dto/JgmhTodoItemDto.java create mode 100644 src/main/java/com/chinaweal/aiccs/outside/dto/JgmhTodoRequestDto.java create mode 100644 src/main/java/com/chinaweal/aiccs/schedule/JgmhPortalTodoPushScheduled.java create mode 100644 src/main/resources/mybatis/mapper/aiccs/door/PortalTodoPushRecordMapper.xml diff --git a/src/main/java/com/chinaweal/aiccs/aiccs/business/service/impl/TSTaskListServiceImpl.java b/src/main/java/com/chinaweal/aiccs/aiccs/business/service/impl/TSTaskListServiceImpl.java index 8e0cc1e..3eb5d2c 100644 --- a/src/main/java/com/chinaweal/aiccs/aiccs/business/service/impl/TSTaskListServiceImpl.java +++ b/src/main/java/com/chinaweal/aiccs/aiccs/business/service/impl/TSTaskListServiceImpl.java @@ -36,6 +36,7 @@ import com.chinaweal.aiccs.common.util.StringUtils; import com.chinaweal.aiccs.common.util.UuidUtil; import com.chinaweal.aiccs.crgs.system.entity.TaskList; import com.chinaweal.aiccs.door.service.ITUserportalService; +import com.chinaweal.aiccs.door.service.IJgmhPortalTodoService; import com.chinaweal.aiccs.org.entity.OrgUnits; import com.chinaweal.aiccs.org.mapper.AicorgMapper; import com.chinaweal.aiccs.org.mapper.TUsersMapper; @@ -65,6 +66,15 @@ import java.util.stream.Collectors; @Service public class TSTaskListServiceImpl extends BaseServiceImpl implements TSTaskListService { + /** 信用修复业务类型 */ + private static final List CREDIT_REPAIR_BUSTYPES = Arrays.asList( + BaseDataConstant.EXPTLIST_TYPE_REMOVE, + BaseDataConstant.EXPTLIST_TYPE_INDIVIDUAL_RESTORE, + BaseDataConstant.EXPTLIST_TYPE_FARMER_REMOVE, + BaseDataConstant.CREDIT_REPAIR_ILLEGAL_BIZ, + BaseDataConstant.BUSINESS_TYPE_REM_PUNISH + ); + @Autowired private UuidUtil uuidUtil; @@ -89,6 +99,9 @@ public class TSTaskListServiceImpl extends BaseServiceImpl debugPushSingle( + @ApiParam(value = "tstasklist 主键", required = true) + @RequestParam("tasklistid") String tasklistid) { + log.info("【调试推送】收到请求,tasklistid={}", tasklistid); + + if (!debugEnabled) { + log.warn("【调试推送】调试接口未开启,请检查配置 jgmhDoor.debugEnabled"); + return RestResult.error(ResultCode.INTERFACE_FORBID_VISIT, + "调试接口未开启,请联系管理员开启 jgmhDoor.debugEnabled"); + } + + JgmhDebugPushResult result = jgmhPortalTodoService.debugPushSingle(tasklistid); + log.info("【调试推送】完成,tasklistid={}, success={}, durationMs={}", + tasklistid, result.getSuccess(), result.getDurationMs()); + return RestResult.ok(result); + } +} diff --git a/src/main/java/com/chinaweal/aiccs/door/dto/JgmhDebugPushResult.java b/src/main/java/com/chinaweal/aiccs/door/dto/JgmhDebugPushResult.java new file mode 100644 index 0000000..4984294 --- /dev/null +++ b/src/main/java/com/chinaweal/aiccs/door/dto/JgmhDebugPushResult.java @@ -0,0 +1,42 @@ +package com.chinaweal.aiccs.door.dto; + +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * 统一监管门户待办对接 — 调试推送结果 + * + * 用于 JgmhPortalTodoDebugController.debugPushSingle 返回详细的调用过程信息, + * 便于开发联调排查请求/响应、token、耗时等数据。 + */ +@Data +@Accessors(chain = true) +public class JgmhDebugPushResult { + + /** 是否成功(true=门户HTTP调用成功;门户业务返回需自行判断 responseBody) */ + private Boolean success; + + /** 业务说明(每个环节的概要描述) */ + private String message; + + /** 使用的 token(脱敏后前4位 + **** + 后4位) */ + private String token; + + /** 实际发送给门户接口的请求体 JSON */ + private String requestBody; + + /** 门户接口返回的原始响应 */ + private String responseBody; + + /** HTTP 状态码 */ + private Integer httpStatus; + + /** 接口调用耗时(毫秒) */ + private Long durationMs; + + /** 组装后的待办事项预览(JgmhTodoItemDto) */ + private Object todoItemPreview; + + /** tstasklist 关键字段预览 */ + private Object taskListPreview; +} diff --git a/src/main/java/com/chinaweal/aiccs/door/entity/PortalTodoPushRecord.java b/src/main/java/com/chinaweal/aiccs/door/entity/PortalTodoPushRecord.java new file mode 100644 index 0000000..ff7cf79 --- /dev/null +++ b/src/main/java/com/chinaweal/aiccs/door/entity/PortalTodoPushRecord.java @@ -0,0 +1,60 @@ +package com.chinaweal.aiccs.door.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.chinaweal.youfool.framework.springboot.json.LocalDateTimeStringSerializer; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.time.LocalDateTime; + +@Data +@Accessors(chain = true) +@TableName("PORTAL_TODO_PUSH_RECORD") +public class PortalTodoPushRecord { + + @TableId(value = "ID", type = IdType.ASSIGN_UUID) + private String id; + + @TableField("TASKLISTID") + private String tasklistid; + + @TableField("BUSTYPE") + private String bustype; + + @TableField("DBSXID") + private String dbsxid; + + @TableField("DBSXMC") + private String dbsxmc; + + @TableField("DBSXZT") + private Integer dbsxzt; + + @TableField("USERSID") + private String usersid; + + @TableField("PUSHSTATUS") + private String pushstatus; + + @TableField("PUSHMESSAGE") + private String pushmessage; + + @TableField("PUSHCOUNT") + private Integer pushcount; + + @TableField("LASTPUSHTIME") + @JsonSerialize(using = LocalDateTimeStringSerializer.class) + private LocalDateTime lastpushtime; + + @TableField("CREATETIME") + @JsonSerialize(using = LocalDateTimeStringSerializer.class) + private LocalDateTime createtime; + + @TableField("UPDATETIME") + @JsonSerialize(using = LocalDateTimeStringSerializer.class) + private LocalDateTime updatetime; +} diff --git a/src/main/java/com/chinaweal/aiccs/door/mapper/PortalTodoPushRecordMapper.java b/src/main/java/com/chinaweal/aiccs/door/mapper/PortalTodoPushRecordMapper.java new file mode 100644 index 0000000..7cd8976 --- /dev/null +++ b/src/main/java/com/chinaweal/aiccs/door/mapper/PortalTodoPushRecordMapper.java @@ -0,0 +1,8 @@ +package com.chinaweal.aiccs.door.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.chinaweal.aiccs.door.entity.PortalTodoPushRecord; + +public interface PortalTodoPushRecordMapper extends BaseMapper { + +} diff --git a/src/main/java/com/chinaweal/aiccs/door/service/IJgmhPortalTodoService.java b/src/main/java/com/chinaweal/aiccs/door/service/IJgmhPortalTodoService.java new file mode 100644 index 0000000..5668459 --- /dev/null +++ b/src/main/java/com/chinaweal/aiccs/door/service/IJgmhPortalTodoService.java @@ -0,0 +1,37 @@ +package com.chinaweal.aiccs.door.service; + +import com.chinaweal.aiccs.aiccs.business.entity.TSTaskList; +import com.chinaweal.aiccs.door.dto.JgmhDebugPushResult; + +public interface IJgmhPortalTodoService { + /** + * 创建待推送记录(工作流启动时调用) + * 仅限:外网申请 + 待受理 + 信用修复业务 + */ + void createPushRecord(TSTaskList taskListModel); + + /** + * 批量推送待办事项(定时任务调用) + */ + void pushPendingTodoItems(); + + /** + * 用户点击"办理"后调用,将待办标记为已办 + */ + void onHandleTodo(String tasklistid); + + /** + * 【调试用】单笔推送指定 tasklistid 对应的待办数据到统一监管门户 + * + * - 绕过三重过滤(不校验 bustype/origin/busstatus),可推送任意一笔待办 + * - 同步直接调用门户接口,不走定时任务 + * - 不写入推送记录表,不污染生产数据 + * - 返回详细的调试信息(请求体、响应体、耗时等) + * + * 仅用于开发联调和线上排障,不应在业务流程中使用。 + * + * @param tasklistid tstasklist 主键 + * @return 调试结果详情 + */ + JgmhDebugPushResult debugPushSingle(String tasklistid); +} diff --git a/src/main/java/com/chinaweal/aiccs/door/service/PortalTodoPushRecordService.java b/src/main/java/com/chinaweal/aiccs/door/service/PortalTodoPushRecordService.java new file mode 100644 index 0000000..c58ff90 --- /dev/null +++ b/src/main/java/com/chinaweal/aiccs/door/service/PortalTodoPushRecordService.java @@ -0,0 +1,8 @@ +package com.chinaweal.aiccs.door.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.chinaweal.aiccs.door.entity.PortalTodoPushRecord; + +public interface PortalTodoPushRecordService extends IService { + +} diff --git a/src/main/java/com/chinaweal/aiccs/door/service/impl/JgmhPortalTodoServiceImpl.java b/src/main/java/com/chinaweal/aiccs/door/service/impl/JgmhPortalTodoServiceImpl.java new file mode 100644 index 0000000..f4682ce --- /dev/null +++ b/src/main/java/com/chinaweal/aiccs/door/service/impl/JgmhPortalTodoServiceImpl.java @@ -0,0 +1,404 @@ +package com.chinaweal.aiccs.door.service.impl; + +import com.alibaba.fastjson.JSON; +import com.chinaweal.aiccs.aiccs.business.entity.TSTaskList; +import com.chinaweal.aiccs.aiccs.business.service.TSTaskListService; +import com.chinaweal.aiccs.common.constant.BaseDataConstant; +import com.chinaweal.aiccs.door.dto.JgmhDebugPushResult; +import com.chinaweal.aiccs.door.entity.PortalTodoPushRecord; +import com.chinaweal.aiccs.door.service.IJgmhPortalTodoService; +import com.chinaweal.aiccs.door.service.PortalTodoPushRecordService; +import com.chinaweal.aiccs.outside.api.JgmhDoorApi; +import com.chinaweal.aiccs.outside.dto.JgmhTodoItemDto; +import com.chinaweal.aiccs.outside.dto.JgmhTodoRequestDto; +import com.chinaweal.aiccs.org.entity.TUsers; +import com.chinaweal.aiccs.org.service.TUsersService; +import com.chinaweal.aiccs.redis.RedisService; +import com.chinaweal.youfool.framework.springboot.exception.custom.BusinessException; +import com.dtflys.forest.http.ForestResponse; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +@Service +@Slf4j +public class JgmhPortalTodoServiceImpl implements IJgmhPortalTodoService { + + @Value("${jgmhDoor.enableTask}") + private boolean enableTask; + @Value("${jgmhDoor.appId}") + private String appId; + @Value("${jgmhDoor.redirectUrl}") + private String redirectUrl; + @Value("${jgmhDoor.mobileRedirectUrl:}") + private String mobileRedirectUrl; + + @Resource + private JgmhDoorApi jgmhDoorApi; + @Resource + private PortalTodoPushRecordService pushRecordService; + @Resource + private TSTaskListService taskListService; + @Resource + private TUsersService tUsersService; + @Resource + private RedisService redisService; + + private static final String TOKEN_CACHE_KEY = "JGMH_DOOR_TOKEN"; + + /** + * 信用修复业务类型白名单 + * 与 TSTaskListMapper.xml 中 repairTaskQuery 的过滤条件保持一致 + */ + private static final List CREDIT_REPAIR_BUSTYPES = Arrays.asList( + BaseDataConstant.EXPTLIST_TYPE_REMOVE, // "7" 企业经营异常名录信用修复 + BaseDataConstant.EXPTLIST_TYPE_INDIVIDUAL_RESTORE, // "12" 个体户经营异常名录信用修复 + BaseDataConstant.EXPTLIST_TYPE_FARMER_REMOVE, // "14" 农专社经营异常名录信用修复 + BaseDataConstant.CREDIT_REPAIR_ILLEGAL_BIZ, // "33" 严重违法失信信用修复 + BaseDataConstant.BUSINESS_TYPE_REM_PUNISH // "27" 行政处罚信用修复 + ); + + /** 外网申请标识 */ + private static final String ORIGIN_OUTER = "1"; + + /** 待受理状态 */ + private static final String BUSSTATUS_TOSIGN = BaseDataConstant.BUSINESS_STATUS_TOSIGN; // "-1" + + /** + * 获取token(带Redis缓存,有效期2小时,提前刷新) + */ + public String getToken() { + String token = redisService.get(TOKEN_CACHE_KEY); + if (StringUtils.isNotBlank(token)) { + return token; + } + + ForestResponse response = jgmhDoorApi.getToken(); + if (response.isSuccess()) { + token = response.getResult().trim(); + redisService.set(TOKEN_CACHE_KEY, token, 7000); + return token; + } + throw new BusinessException("获取统一监管门户token失败: " + response.getResult()); + } + + /** + * 获取本地用户对应的统一监管门户用户ID + * 映射链路:signuserid → t_users.IMSUSERCODE → USERSID + */ + private String getPortalUserId(String localUserId) { + TUsers tUser = tUsersService.getById(localUserId); + if (tUser == null || StringUtils.isBlank(tUser.getImsusercode())) { + return null; + } + return tUser.getImsusercode(); + } + + /** + * 创建待推送记录(工作流启动时调用) + * + * 三重过滤条件: + * 1. BUSTYPE in ('7','12','14','33','27') — 信用修复业务 + * 2. ORIGIN = '1' — 外网申请 + * 3. BUSSTATUS = '-1' — 待受理状态 + * + * 三个条件必须同时满足,任一不满足则跳过 + */ + @Override + public void createPushRecord(TSTaskList taskListModel) { + if (!enableTask) return; + + // 过滤条件1:仅限信用修复业务 + if (!CREDIT_REPAIR_BUSTYPES.contains(taskListModel.getBustype())) { + log.debug("统一监管门户待办推送:非信用修复业务,跳过。tasklistid={}, bustype={}", + taskListModel.getTasklistid(), taskListModel.getBustype()); + return; + } + + // 过滤条件2:仅限外网申请 + if (!ORIGIN_OUTER.equals(taskListModel.getOrigin())) { + log.debug("统一监管门户待办推送:非外网申请,跳过。tasklistid={}, origin={}", + taskListModel.getTasklistid(), taskListModel.getOrigin()); + return; + } + + // 过滤条件3:仅限待受理状态 + if (!BUSSTATUS_TOSIGN.equals(taskListModel.getBusstatus())) { + log.debug("统一监管门户待办推送:非待受理状态,跳过。tasklistid={}, busstatus={}", + taskListModel.getTasklistid(), taskListModel.getBusstatus()); + return; + } + + // 获取门户用户ID + String portalUserId = getPortalUserId(taskListModel.getSignuserid()); + if (portalUserId == null) { + log.info("统一监管门户待办推送:用户未关联IMS账号,signuserid={}", taskListModel.getSignuserid()); + return; + } + + // 创建推送记录 + // DBSXID 使用 tasklistid(因为只有2步且无回退,不存在多人共享问题) + PortalTodoPushRecord record = new PortalTodoPushRecord(); + record.setTasklistid(taskListModel.getTasklistid()); + record.setBustype(taskListModel.getBustype()); + record.setDbsxid(taskListModel.getTasklistid()); + record.setDbsxmc(taskListModel.getBusname()); + record.setDbsxzt(1); // 待办 + record.setUsersid(portalUserId); + record.setPushstatus("0"); // 待推送 + record.setPushcount(0); + record.setCreatetime(LocalDateTime.now()); + record.setUpdatetime(LocalDateTime.now()); + pushRecordService.save(record); + + log.info("统一监管门户待办推送:创建推送记录,tasklistid={}, bustype={}, usersid={}", + taskListModel.getTasklistid(), taskListModel.getBustype(), portalUserId); + } + + /** + * 用户点击"办理"后调用,将待办标记为已办 + * + * 调用时机:TSTaskListServiceImpl.updateWorkFlow() 中 busstatus 从 "-1"(待受理)变为 "0"(在办)时 + * 不直接推送,只更新推送记录状态,重置为待推送,下次定时任务推送状态变更 + */ + @Override + public void onHandleTodo(String tasklistid) { + if (!enableTask) return; + + PortalTodoPushRecord record = pushRecordService.lambdaQuery() + .eq(PortalTodoPushRecord::getTasklistid, tasklistid) + .eq(PortalTodoPushRecord::getDbsxzt, 1) // 当前是待办状态 + .one(); + + if (record == null) return; + + record.setDbsxzt(2); // 已办 + record.setPushstatus("0"); // 重置为待推送 + record.setUpdatetime(LocalDateTime.now()); + pushRecordService.updateById(record); + + log.info("统一监管门户待办推送:用户点击办理,标记为已办,tasklistid={}", tasklistid); + } + + /** + * 批量推送待办事项(定时任务调用) + */ + @Override + public void pushPendingTodoItems() { + if (!enableTask) return; + + List pendingRecords = pushRecordService.lambdaQuery() + .in(PortalTodoPushRecord::getPushstatus, "0", "2") + .lt(PortalTodoPushRecord::getPushcount, 3) + .list(); + + if (pendingRecords.isEmpty()) { + log.info("统一监管门户待办推送:无待推送记录"); + return; + } + + log.info("统一监管门户待办推送:待推送记录数={}", pendingRecords.size()); + + String token = getToken(); + + List items = new ArrayList<>(); + for (PortalTodoPushRecord record : pendingRecords) { + JgmhTodoItemDto item = buildTodoItem(record); + if (item != null) { + items.add(item); + } + } + + if (items.isEmpty()) { + log.info("统一监管门户待办推送:无有效推送数据"); + return; + } + + JgmhTodoRequestDto requestDto = new JgmhTodoRequestDto(); + requestDto.setData(items); + + try { + ForestResponse response = jgmhDoorApi.pushTodoItems(token, requestDto); + if (response.isSuccess()) { + for (PortalTodoPushRecord record : pendingRecords) { + record.setPushstatus("1"); + record.setPushcount(record.getPushcount() + 1); + record.setLastpushtime(LocalDateTime.now()); + } + log.info("统一监管门户待办推送:推送成功,记录数={}", pendingRecords.size()); + } else { + markAsFailed(pendingRecords, response.getResult()); + log.error("统一监管门户待办推送:推送失败,响应={}", response.getResult()); + } + } catch (Exception e) { + markAsFailed(pendingRecords, e.getMessage()); + log.error("统一监管门户待办推送:推送异常", e); + } + + pushRecordService.updateBatchById(pendingRecords); + } + + /** + * 组装推送数据项 + */ + private JgmhTodoItemDto buildTodoItem(PortalTodoPushRecord record) { + TSTaskList taskList = taskListService.getById(record.getTasklistid()); + + JgmhTodoItemDto item = new JgmhTodoItemDto(); + item.setDBSXID(record.getDbsxid()); + item.setDBSXMC(record.getDbsxmc()); + + if (taskList != null) { + item.setXXSM(taskList.getLinkname() + " - " + taskList.getBusname()); + item.setCJSJ(formatDateTime(taskList.getSendertime())); + item.setJZSJ(formatDateTime(taskList.getDeadlineDate())); + } else { + record.setPushstatus("2"); + record.setPushcount(3); + record.setPushmessage("关联的tstasklist记录不存在"); + record.setLastpushtime(LocalDateTime.now()); + return null; + } + + item.setDBSXZT(record.getDbsxzt()); + item.setUSERSID(record.getUsersid()); + item.setDBDZURL(redirectUrl); + item.setYDDDBURL(mobileRedirectUrl); + item.setLYXTAPPID(appId); + + return item; + } + + private void markAsFailed(List records, String message) { + for (PortalTodoPushRecord record : records) { + record.setPushstatus("2"); + record.setPushcount(record.getPushcount() + 1); + record.setPushmessage(message != null && message.length() > 500 + ? message.substring(0, 500) : message); + record.setLastpushtime(LocalDateTime.now()); + } + } + + private String formatDateTime(LocalDateTime dateTime) { + if (dateTime == null) return ""; + return dateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + } + + /** + * 【调试用】单笔推送指定 tasklistid 对应的待办数据到统一监管门户 + * + * - 绕过三重过滤(不校验 bustype/origin/busstatus),可推送任意一笔待办 + * - 同步直接调用门户接口,不走定时任务 + * - 不写入推送记录表,不污染生产数据 + * - 返回详细的调试信息(请求体、响应体、耗时等) + */ + @Override + public JgmhDebugPushResult debugPushSingle(String tasklistid) { + JgmhDebugPushResult result = new JgmhDebugPushResult(); + long startTime = System.currentTimeMillis(); + + try { + // 1. 校验入参 + if (StringUtils.isBlank(tasklistid)) { + return result.setSuccess(false).setMessage("tasklistid 不能为空"); + } + + // 2. 查询 tstasklist 记录 + TSTaskList taskList = taskListService.getById(tasklistid); + if (taskList == null) { + return result.setSuccess(false) + .setMessage("tasklistid=" + tasklistid + " 对应的 tstasklist 记录不存在"); + } + result.setTaskListPreview(buildTaskListPreview(taskList)); + + // 3. 获取门户用户ID + String portalUserId = getPortalUserId(taskList.getSignuserid()); + if (portalUserId == null) { + return result.setSuccess(false) + .setMessage("当前 tasklistid 的签收人未关联 IMS 账号,无法推送。signuserid=" + + taskList.getSignuserid()); + } + + // 4. 查询或临时构造推送记录(不写库) + PortalTodoPushRecord record = pushRecordService.lambdaQuery() + .eq(PortalTodoPushRecord::getTasklistid, tasklistid) + .last("LIMIT 1") + .one(); + if (record == null) { + record = new PortalTodoPushRecord(); + record.setTasklistid(tasklistid); + record.setBustype(taskList.getBustype()); + record.setDbsxid(tasklistid); + record.setDbsxmc(taskList.getBusname()); + record.setDbsxzt(1); // 默认按"待办"推送 + record.setUsersid(portalUserId); + } + + // 5. 组装推送数据 + JgmhTodoItemDto item = buildTodoItem(record); + if (item == null) { + return result.setSuccess(false).setMessage("组装推送数据失败,请查看日志"); + } + result.setTodoItemPreview(item); + + // 6. 获取 token(脱敏) + String token = getToken(); + result.setToken(maskToken(token)); + + // 7. 调用门户接口 + JgmhTodoRequestDto requestDto = new JgmhTodoRequestDto(); + requestDto.setData(Collections.singletonList(item)); + result.setRequestBody(JSON.toJSONString(requestDto)); + + ForestResponse response = jgmhDoorApi.pushTodoItems(token, requestDto); + result.setHttpStatus(response.getStatusCode()); + result.setResponseBody(response.getResult()); + + if (response.isSuccess()) { + result.setSuccess(true) + .setMessage("HTTP 调用成功,请查看 responseBody 确认门户业务返回"); + } else { + result.setSuccess(false) + .setMessage("HTTP 调用失败,HTTP=" + response.getStatusCode()); + } + } catch (Exception e) { + log.error("调试推送异常,tasklistid={}", tasklistid, e); + result.setSuccess(false).setMessage("调试推送异常:" + e.getMessage()); + } finally { + result.setDurationMs(System.currentTimeMillis() - startTime); + } + return result; + } + + /** Token 脱敏:前4位 + **** + 后4位 */ + private String maskToken(String token) { + if (StringUtils.isBlank(token) || token.length() <= 8) return "****"; + return token.substring(0, 4) + "****" + token.substring(token.length() - 4); + } + + /** 提取 tstasklist 关键字段用于调试查看 */ + private Map buildTaskListPreview(TSTaskList t) { + Map m = new LinkedHashMap<>(); + m.put("tasklistid", t.getTasklistid()); + m.put("bustype", t.getBustype()); + m.put("busname", t.getBusname()); + m.put("origin", t.getOrigin()); + m.put("busstatus", t.getBusstatus()); + m.put("signuserid", t.getSignuserid()); + m.put("linkname", t.getLinkname()); + m.put("sendertime", t.getSendertime()); + m.put("deadlineDate", t.getDeadlineDate()); + return m; + } +} diff --git a/src/main/java/com/chinaweal/aiccs/door/service/impl/PortalTodoPushRecordServiceImpl.java b/src/main/java/com/chinaweal/aiccs/door/service/impl/PortalTodoPushRecordServiceImpl.java new file mode 100644 index 0000000..893b637 --- /dev/null +++ b/src/main/java/com/chinaweal/aiccs/door/service/impl/PortalTodoPushRecordServiceImpl.java @@ -0,0 +1,14 @@ +package com.chinaweal.aiccs.door.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.chinaweal.aiccs.door.entity.PortalTodoPushRecord; +import com.chinaweal.aiccs.door.mapper.PortalTodoPushRecordMapper; +import com.chinaweal.aiccs.door.service.PortalTodoPushRecordService; +import org.springframework.stereotype.Service; + +@Service +public class PortalTodoPushRecordServiceImpl + extends ServiceImpl + implements PortalTodoPushRecordService { + +} diff --git a/src/main/java/com/chinaweal/aiccs/outside/api/JgmhDoorApi.java b/src/main/java/com/chinaweal/aiccs/outside/api/JgmhDoorApi.java new file mode 100644 index 0000000..e22cb1f --- /dev/null +++ b/src/main/java/com/chinaweal/aiccs/outside/api/JgmhDoorApi.java @@ -0,0 +1,27 @@ +package com.chinaweal.aiccs.outside.api; + +import com.chinaweal.aiccs.outside.dto.JgmhTodoRequestDto; +import com.dtflys.forest.annotation.*; +import com.dtflys.forest.http.ForestResponse; + +/** + * 统一监管门户 Forest API接口 + */ +@BaseRequest(baseURL = "${jgmhDoorBaseUrl}") +public interface JgmhDoorApi { + + /** + * 获取token + * GET /zt/api/auth/token?key={appid} + * Header: DI-Authorization: {app_secret} + */ + @GetRequest(url = "/zt/api/auth/token?key={appId}", headers = "DI-Authorization:${jgmhDoor.appSecret}") + ForestResponse getToken(); + + /** + * 推送待办事项 + * POST /zt/api/dataservice/v1/tyjgmhXZdbsx?token={token} + */ + @PostRequest(url = "/zt/api/dataservice/v1/tyjgmhXZdbsx?token={token}", contentType = "application/json") + ForestResponse pushTodoItems(@Var("token") String token, @JSONBody JgmhTodoRequestDto requestDto); +} diff --git a/src/main/java/com/chinaweal/aiccs/outside/dto/JgmhTodoItemDto.java b/src/main/java/com/chinaweal/aiccs/outside/dto/JgmhTodoItemDto.java new file mode 100644 index 0000000..b81f205 --- /dev/null +++ b/src/main/java/com/chinaweal/aiccs/outside/dto/JgmhTodoItemDto.java @@ -0,0 +1,40 @@ +package com.chinaweal.aiccs.outside.dto; + +import lombok.Data; + +/** + * 统一监管门户待办事项推送DTO + */ +@Data +public class JgmhTodoItemDto { + + /** 待办事项ID */ + private String DBSXID; + + /** 待办事项名称 */ + private String DBSXMC; + + /** 详细说明 */ + private String XXSM; + + /** 待办事项状态(1待办 2已办) */ + private Integer DBSXZT; + + /** 门户用户ID */ + private String USERSID; + + /** PC端待办跳转地址 */ + private String DBDZURL; + + /** 移动端待办跳转地址 */ + private String YDDDBURL; + + /** 创建时间 格式: yyyy-MM-dd HH:mm:ss */ + private String CJSJ; + + /** 截止时间 格式: yyyy-MM-dd HH:mm:ss */ + private String JZSJ; + + /** 来源系统APPID */ + private String LYXTAPPID; +} diff --git a/src/main/java/com/chinaweal/aiccs/outside/dto/JgmhTodoRequestDto.java b/src/main/java/com/chinaweal/aiccs/outside/dto/JgmhTodoRequestDto.java new file mode 100644 index 0000000..58ac1f6 --- /dev/null +++ b/src/main/java/com/chinaweal/aiccs/outside/dto/JgmhTodoRequestDto.java @@ -0,0 +1,15 @@ +package com.chinaweal.aiccs.outside.dto; + +import lombok.Data; + +import java.util.List; + +/** + * 统一监管门户待办推送请求包装DTO + */ +@Data +public class JgmhTodoRequestDto { + + /** 待办事项列表 */ + private List data; +} diff --git a/src/main/java/com/chinaweal/aiccs/schedule/JgmhPortalTodoPushScheduled.java b/src/main/java/com/chinaweal/aiccs/schedule/JgmhPortalTodoPushScheduled.java new file mode 100644 index 0000000..dfa5fb4 --- /dev/null +++ b/src/main/java/com/chinaweal/aiccs/schedule/JgmhPortalTodoPushScheduled.java @@ -0,0 +1,58 @@ +package com.chinaweal.aiccs.schedule; + +import com.chinaweal.aiccs.door.service.IJgmhPortalTodoService; +import com.chinaweal.aiccs.redis.RedisService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.Scheduled; + +/** + * 统一监管门户待办推送定时任务 + * 每30分钟扫描推送记录表,将待推送的记录推送到统一监管门户 + */ +@Slf4j +@Configuration +@EnableScheduling +public class JgmhPortalTodoPushScheduled { + + private static final String REDIS_LOCK_KEY = "schedule:jgmhPortalTodoPush:lock"; + private static final int REDIS_LOCK_TIMEOUT = 30 * 60; // 30分钟 + + @Autowired + private IJgmhPortalTodoService jgmhPortalTodoService; + + @Autowired + private RedisService redisService; + + @Scheduled(cron = "${scheduling.cron.jgmhPortalTodoPush:-}") + public void pushTodoItems() { + if (!acquireLock()) { + log.warn("【统一监管门户待办推送任务】上一次执行尚未完成或锁已存在,本次跳过"); + return; + } + + try { + log.info("【统一监管门户待办推送任务】开始执行"); + jgmhPortalTodoService.pushPendingTodoItems(); + log.info("【统一监管门户待办推送任务】执行完成"); + } catch (Exception e) { + log.error("【统一监管门户待办推送任务】执行异常:{}", e.getMessage(), e); + } finally { + releaseLock(); + } + } + + private boolean acquireLock() { + if (redisService.exists(REDIS_LOCK_KEY)) { + return false; + } + redisService.set(REDIS_LOCK_KEY, "1", REDIS_LOCK_TIMEOUT); + return true; + } + + private void releaseLock() { + redisService.remove(REDIS_LOCK_KEY); + } +} diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 592b7f0..bc6aacb 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -64,6 +64,7 @@ forest: moveinDownload: /DownloadTransferApi doorBaseUrl: http://172.16.52.60 xcDoorBaseUrl: http://10.201.62.2:6888 + jgmhDoorBaseUrl: http://10.6.47.11:8088/tyjgmh # 统一监管门户 xcDoorCasUrl: http://10.201.62.2:6889 # 第三方文件上传配置 fileUpload: @@ -158,6 +159,7 @@ scheduling: staticHqlcusers: 0 30 00 * * ? #定时获取浪潮用户 forceDeregisterNoticeExpired: '-' #拟强制注销公告期满更新定时任务 syncRevokeData: '-' #吊销数据同步任务(同步REVOKELIST到E_REVOKE和E_GT_REVOKE表) + jgmhPortalTodoPush: '-' #统一监管门户待办推送 ot: bakCseDownloadPath: /ot/download/case bakPermitDownloadPath: /ot/download/permit diff --git a/src/main/resources/application-prod129.yml b/src/main/resources/application-prod129.yml index 4633411..fea642a 100644 --- a/src/main/resources/application-prod129.yml +++ b/src/main/resources/application-prod129.yml @@ -78,6 +78,7 @@ forest: moveinDownload: /DownloadTransferApi doorBaseUrl: http://172.16.52.60 xcDoorBaseUrl: http://10.201.62.2:6888 + jgmhDoorBaseUrl: http://10.6.47.11:8088/tyjgmh # 统一监管门户 # 第三方文件上传配置 fileUpload: testUrl: http://172.26.0.10:9987/test/moveservice/enterprise/uploadfile #测试地址 @@ -167,6 +168,7 @@ scheduling: cancelEntRemove: '0 0 1 * * ?' #注销企业自动移出 syncRevokeData: '0 0 2 * * ?' #吊销数据同步任务(同步REVOKELIST到E_REVOKE和E_GT_REVOKE表) anotherListPB: '0 0 22 * * ?' # 个体户移出名单标记另册管理定时任务 + jgmhPortalTodoPush: '0 */30 * * * ?' #统一监管门户待办推送(每30分钟) ot: bakCseDownloadPath: /ot/download/case diff --git a/src/main/resources/application-prod138.yml b/src/main/resources/application-prod138.yml index 63a930e..7a55160 100644 --- a/src/main/resources/application-prod138.yml +++ b/src/main/resources/application-prod138.yml @@ -68,6 +68,7 @@ forest: moveinDownload: /DownloadTransferApi doorBaseUrl: http://10.194.188.69 xcDoorBaseUrl: http://10.201.62.82 + jgmhDoorBaseUrl: http://10.6.47.11:8088/tyjgmh # 统一监管门户 xcDoorCasUrl: http://10.201.62.81 # 第三方文件上传配置 fileUpload: @@ -145,6 +146,7 @@ scheduling: moveOutData: '0 58 16 * * ?' #迁出省局数据定时任务 moveInData: '0 17 16 * * ?' #迁入省局数据定时任务 forceDeregisterNoticeExpired: '0 0 1 * * ?' #拟强制注销公告期满更新定时任务 + jgmhPortalTodoPush: '0 */30 * * * ?' #统一监管门户待办推送(每30分钟) ot: bakCseDownloadPath: /ot/download/case diff --git a/src/main/resources/application-prod171.yml b/src/main/resources/application-prod171.yml index d4cc1b1..7e9972a 100644 --- a/src/main/resources/application-prod171.yml +++ b/src/main/resources/application-prod171.yml @@ -84,6 +84,7 @@ forest: moveinDownload: /DownloadTransferApi doorBaseUrl: http://10.194.188.69 xcDoorBaseUrl: http://10.201.62.82 + jgmhDoorBaseUrl: http://10.6.47.11:8088/tyjgmh # 统一监管门户 xcDoorCasUrl: http://10.201.62.81 # 第三方文件上传配置 fileUpload: @@ -163,6 +164,7 @@ scheduling: #attachmentUpload: '0 14 20 * * ?' #附件上传定时任务 #moveOutData: '0 58 16 * * ?' #迁出省局数据定时任务 forceDeregisterNoticeExpired: '0 0 1 * * ?' #拟强制注销公告期满更新定时任务 + jgmhPortalTodoPush: '0 */30 * * * ?' #统一监管门户待办推送(每30分钟) ot: diff --git a/src/main/resources/application-prod172.yml b/src/main/resources/application-prod172.yml index f6bfd4e..36153f2 100644 --- a/src/main/resources/application-prod172.yml +++ b/src/main/resources/application-prod172.yml @@ -84,6 +84,7 @@ forest: moveinDownload: /DownloadTransferApi doorBaseUrl: http://10.194.188.69 xcDoorBaseUrl: http://10.201.62.82 + jgmhDoorBaseUrl: http://10.6.47.11:8088/tyjgmh # 统一监管门户 xcDoorCasUrl: http://10.201.62.81 # 第三方文件上传配置 fileUpload: @@ -163,6 +164,7 @@ scheduling: moveOutData: '0 30 22 * * ?' #迁出省局数据定时任务 #moveInData: '-' #迁入省局数据定时任务 forceDeregisterNoticeExpired: '0 0 1 * * ?' #拟强制注销公告期满更新定时任务 + jgmhPortalTodoPush: '0 */30 * * * ?' #统一监管门户待办推送(每30分钟) ot: bakCseDownloadPath: /ot/download/case diff --git a/src/main/resources/application-prod73.yml b/src/main/resources/application-prod73.yml index 1a184ad..7e32c85 100644 --- a/src/main/resources/application-prod73.yml +++ b/src/main/resources/application-prod73.yml @@ -31,7 +31,7 @@ spring: username: SYSDBA password: ChinaWeal2025 youfool: - url: jdbc:dm://172.22.80.73:15236?schema=YOUFOOL + url: jdbc:dm://172.22.80.73:15236?schema=SYSDBA username: SYSDBA password: ChinaWeal2025 aiceps: @@ -78,6 +78,7 @@ forest: moveinDownload: /DownloadTransferApi doorBaseUrl: http://172.16.52.60 xcDoorBaseUrl: http://10.201.62.2:6888 + jgmhDoorBaseUrl: http://10.6.47.11:8088/tyjgmh # 统一监管门户 # 第三方文件上传配置 fileUpload: testUrl: http://172.26.0.10:9987/test/moveservice/enterprise/uploadfile #测试地址 @@ -167,6 +168,7 @@ scheduling: cancelEntRemove: '0 0 1 * * ?' #注销企业自动移出 syncRevokeData: '0 0 2 * * ?' #吊销数据同步任务(同步REVOKELIST到E_REVOKE和E_GT_REVOKE表) anotherListPB: '-' # 个体户移出名单标记另册管理定时任务 + jgmhPortalTodoPush: '0 */30 * * * ?' #统一监管门户待办推送(每30分钟) ot: bakCseDownloadPath: /ot/download/case bakPermitDownloadPath: /ot/download/permit diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 24d96f5..b232c42 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -144,6 +144,15 @@ xcDoor: enableTask: false # 开启推送待办 redirectUrl: http://10.21.5.138/integration/#/toDoList +# 统一监管门户 +jgmhDoor: + enableTask: false # 开启推送待办 + appId: a823ccd6b4954ec897d0d035c1534b2b + appSecret: RGRNOTZrNHBEbERRU1F1SHpZMVIzRQ== + redirectUrl: ${JGMH_DOOR_REDIRECT_URL:} # PC端待办跳转地址,需根据环境配置 + mobileRedirectUrl: ${JGMH_DOOR_MOBILE_REDIRECT_URL:} # 移动端待办跳转地址 + debugEnabled: ${JGMH_DOOR_DEBUG_ENABLED:false} # 单笔调试推送接口开关,生产环境建议关闭 + punishment: api: auth: diff --git a/src/main/resources/mybatis/mapper/aiccs/door/PortalTodoPushRecordMapper.xml b/src/main/resources/mybatis/mapper/aiccs/door/PortalTodoPushRecordMapper.xml new file mode 100644 index 0000000..ae170d5 --- /dev/null +++ b/src/main/resources/mybatis/mapper/aiccs/door/PortalTodoPushRecordMapper.xml @@ -0,0 +1,5 @@ + + + + +