Compare commits
No commits in common. '3ce8e1c59f3cf5aaadef5cc24cf98ff22206c502' and 'e4842142a613d6e6ae0175a34347861f267d1284' have entirely different histories.
3ce8e1c59f
...
e4842142a6
48 changed files with 1144 additions and 3683 deletions
@ -1,58 +0,0 @@ |
|||||||
package com.biutag.supervision.job; |
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor; |
|
||||||
import lombok.extern.slf4j.Slf4j; |
|
||||||
import org.springframework.scheduling.annotation.Scheduled; |
|
||||||
import org.springframework.stereotype.Component; |
|
||||||
|
|
||||||
import java.time.LocalDate; |
|
||||||
import java.time.LocalDateTime; |
|
||||||
|
|
||||||
import com.biutag.supervision.service.MailBoxCaptureService; |
|
||||||
|
|
||||||
/** |
|
||||||
* @ClassName MailBoxCaptureJob |
|
||||||
* @Description 局长信箱数据抓取定时任务 |
|
||||||
* - 阶段1(每天2点):抓取来信,创建 Negative + ComplaintCollection |
|
||||||
* - 阶段2(每天3点):同步涉及人和核查附件 |
|
||||||
* @Author shihao |
|
||||||
* @Date 2026/4/28 |
|
||||||
*/ |
|
||||||
@Slf4j |
|
||||||
@RequiredArgsConstructor |
|
||||||
@Component |
|
||||||
public class MailBoxCaptureJob { |
|
||||||
|
|
||||||
private final MailBoxCaptureService mailBoxCaptureService; |
|
||||||
|
|
||||||
/** |
|
||||||
* 阶段1:抓取来信 |
|
||||||
* 每天凌晨2点执行 |
|
||||||
*/ |
|
||||||
@Scheduled(cron = "0 0 2 * * ?") |
|
||||||
public void mailBoxCaptureCompletedToNegative() { |
|
||||||
LocalDateTime start = LocalDate.now().minusDays(1).atStartOfDay(); |
|
||||||
LocalDateTime end = LocalDate.now().atStartOfDay(); |
|
||||||
mailBoxCaptureService.captureMayorMailbox(start, end); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 阶段2:同步涉及人和核查附件 |
|
||||||
* 每天凌晨3点执行 |
|
||||||
*/ |
|
||||||
@Scheduled(cron = "0 0 4 * * ?") |
|
||||||
public void syncBlameAndFilesForCompletedMails() { |
|
||||||
LocalDateTime start = LocalDate.now().minusDays(1).atStartOfDay(); |
|
||||||
LocalDateTime end = LocalDate.now().atStartOfDay(); |
|
||||||
mailBoxCaptureService.syncBlameAndFiles(start, end); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 阶段3:同步延期信息(初核工作开展情况、初核附件) |
|
||||||
* 每天凌晨4点执行 |
|
||||||
*/ |
|
||||||
@Scheduled(cron = "0 0 3 * * ?") |
|
||||||
public void syncExtension() { |
|
||||||
mailBoxCaptureService.syncExtension(); |
|
||||||
} |
|
||||||
} |
|
||||||
@ -1,16 +0,0 @@ |
|||||||
package com.biutag.supervision.mapper; |
|
||||||
|
|
||||||
import com.baomidou.dynamic.datasource.annotation.DS; |
|
||||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
|
||||||
import com.biutag.supervision.pojo.entity.mailbox.MailExtension; |
|
||||||
|
|
||||||
/** |
|
||||||
* @ClassName MailExtensionMapper |
|
||||||
* @Description 局长信箱延期表Mapper |
|
||||||
* @Author auto-generate |
|
||||||
* @Date 2026/4/29 |
|
||||||
*/ |
|
||||||
@DS("mailbox") |
|
||||||
public interface MailExtensionMapper extends BaseMapper<MailExtension> { |
|
||||||
|
|
||||||
} |
|
||||||
@ -1,29 +0,0 @@ |
|||||||
package com.biutag.supervision.pojo.dto; |
|
||||||
|
|
||||||
import com.biutag.supervision.pojo.entity.ComplaintCollection; |
|
||||||
import com.biutag.supervision.pojo.entity.mailbox.Mail; |
|
||||||
import com.biutag.supervision.pojo.entity.mailbox.MailBlame; |
|
||||||
import io.swagger.v3.oas.annotations.media.Schema; |
|
||||||
import lombok.Getter; |
|
||||||
import lombok.Setter; |
|
||||||
|
|
||||||
import java.util.List; |
|
||||||
|
|
||||||
/** |
|
||||||
* 局长信箱阶段2同步数据DTO |
|
||||||
* 用于在循环内传递同步所需的数据 |
|
||||||
*/ |
|
||||||
@Getter |
|
||||||
@Setter |
|
||||||
@Schema(description = "局长信箱阶段2同步数据DTO") |
|
||||||
public class MailBoxSyncDto { |
|
||||||
|
|
||||||
@Schema(description = "投诉举报收集表记录") |
|
||||||
private ComplaintCollection cc; |
|
||||||
|
|
||||||
@Schema(description = "局长信箱邮件数据") |
|
||||||
private Mail mail; |
|
||||||
|
|
||||||
@Schema(description = "涉及人列表") |
|
||||||
private List<MailBlame> mailBlames; |
|
||||||
} |
|
||||||
@ -1,361 +0,0 @@ |
|||||||
package com.biutag.supervision.pojo.dto; |
|
||||||
|
|
||||||
import com.biutag.supervision.constants.enums.ProblemSourcesEnum; |
|
||||||
import com.biutag.supervision.pojo.dto.flow.VerifyData; |
|
||||||
import com.biutag.supervision.pojo.entity.NegativeBlame; |
|
||||||
import com.biutag.supervision.pojo.entity.NegativeFile; |
|
||||||
import com.biutag.supervision.pojo.entity.NegativeThingFile; |
|
||||||
import com.fasterxml.jackson.annotation.JsonFormat; |
|
||||||
import io.swagger.v3.oas.annotations.media.Schema; |
|
||||||
import lombok.Getter; |
|
||||||
import lombok.Setter; |
|
||||||
|
|
||||||
import java.time.LocalDateTime; |
|
||||||
import java.util.ArrayList; |
|
||||||
import java.util.List; |
|
||||||
|
|
||||||
/** |
|
||||||
* Negative纯数据保存DTO - 所有字段通过传参,不写死任何值 |
|
||||||
*/ |
|
||||||
@Setter |
|
||||||
@Getter |
|
||||||
@Schema(description = "Negative纯数据保存DTO") |
|
||||||
public class NegativeDataOnlyDto { |
|
||||||
|
|
||||||
// ========== Negative主表所有字段 ==========
|
|
||||||
|
|
||||||
@Schema(description = "主键(可传可不传,不传则自动生成)") |
|
||||||
private String id; |
|
||||||
|
|
||||||
@Schema(description = "样本来源编号") |
|
||||||
private String originId; |
|
||||||
|
|
||||||
@Schema(description = "编号") |
|
||||||
private String serialNumber; |
|
||||||
|
|
||||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm") |
|
||||||
@Schema(description = "问题发生时间") |
|
||||||
private LocalDateTime happenTime; |
|
||||||
|
|
||||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm") |
|
||||||
@Schema(description = "问题发现时间") |
|
||||||
private LocalDateTime discoveryTime; |
|
||||||
|
|
||||||
/** |
|
||||||
* @see ProblemSourcesEnum |
|
||||||
*/ |
|
||||||
@Schema(description = "问题来源code") |
|
||||||
private String problemSourcesCode; |
|
||||||
/** |
|
||||||
* @see ProblemSourcesEnum |
|
||||||
*/ |
|
||||||
|
|
||||||
@Schema(description = "问题来源") |
|
||||||
private String problemSources; |
|
||||||
|
|
||||||
@Schema(description = "业务类型code") |
|
||||||
private String businessTypeCode; |
|
||||||
|
|
||||||
@Schema(description = "业务类别名称") |
|
||||||
private String businessTypeName; |
|
||||||
|
|
||||||
@Schema(description = "涉嫌问题(列表,会转成逗号分隔字符串)") |
|
||||||
private List<String> involveProblem = new ArrayList<>(); |
|
||||||
|
|
||||||
@Schema(description = "涉及警种名称") |
|
||||||
private String policeTypeName; |
|
||||||
|
|
||||||
@Schema(description = "涉及警种") |
|
||||||
private String policeType; |
|
||||||
|
|
||||||
@Schema(description = "涉及单位名称") |
|
||||||
private String involveDepartName; |
|
||||||
|
|
||||||
@Schema(description = "涉及单位id") |
|
||||||
private String involveDepartId; |
|
||||||
|
|
||||||
@Schema(description = "联系电话") |
|
||||||
private String contactPhone; |
|
||||||
|
|
||||||
@Schema(description = "反映人姓名") |
|
||||||
private String responderName; |
|
||||||
|
|
||||||
@Schema(description = "反映人身份证") |
|
||||||
private String responderIdCard; |
|
||||||
|
|
||||||
@Schema(description = "简要描述") |
|
||||||
private String thingDesc; |
|
||||||
|
|
||||||
@Schema(description = "填写人姓名") |
|
||||||
private String fillName; |
|
||||||
|
|
||||||
@Schema(description = "填写人部门id") |
|
||||||
private String fillDepartId; |
|
||||||
|
|
||||||
@Schema(description = "填写人部门名称") |
|
||||||
private String fillDeaprtName; |
|
||||||
|
|
||||||
@Schema(description = "审核人姓名") |
|
||||||
private String checkName; |
|
||||||
|
|
||||||
@Schema(description = "审核人身份证") |
|
||||||
private String checkIdCode; |
|
||||||
|
|
||||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm") |
|
||||||
@Schema(description = "创建时间(不传则自动填充)") |
|
||||||
private LocalDateTime crtTime; |
|
||||||
|
|
||||||
@Schema(description = "更新人姓名") |
|
||||||
private String updName; |
|
||||||
|
|
||||||
@Schema(description = "更新人") |
|
||||||
private String updUser; |
|
||||||
|
|
||||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm") |
|
||||||
@Schema(description = "更新时间(不传则自动填充)") |
|
||||||
private LocalDateTime updTime; |
|
||||||
|
|
||||||
@Schema(description = "状态") |
|
||||||
private String status; |
|
||||||
|
|
||||||
@Schema(description = "备注") |
|
||||||
private String remark; |
|
||||||
|
|
||||||
@Schema(description = "预留字段") |
|
||||||
private String reserve; |
|
||||||
|
|
||||||
@Schema(description = "核查情况") |
|
||||||
private String checkStatus; |
|
||||||
|
|
||||||
@Schema(description = "核查情况名称") |
|
||||||
private String checkStatusName; |
|
||||||
|
|
||||||
@Schema(description = "核查结论code") |
|
||||||
private String checkStatusCode; |
|
||||||
|
|
||||||
@Schema(description = "完善状态") |
|
||||||
private String completeStatus; |
|
||||||
|
|
||||||
@Schema(description = "最后编辑人员") |
|
||||||
private String lastEditName; |
|
||||||
|
|
||||||
@Schema(description = "任务ID") |
|
||||||
private String taskId; |
|
||||||
|
|
||||||
@Schema(description = "督察主题code") |
|
||||||
private String supervisionSubjectCode; |
|
||||||
|
|
||||||
@Schema(description = "督察主题名称") |
|
||||||
private String supervisionSubjectName; |
|
||||||
|
|
||||||
@Schema(description = "是否整改code") |
|
||||||
private String isRectifyCode; |
|
||||||
|
|
||||||
@Schema(description = "是否整改名称") |
|
||||||
private String isRectifyName; |
|
||||||
|
|
||||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm") |
|
||||||
@Schema(description = "办结时间") |
|
||||||
private LocalDateTime completeDate; |
|
||||||
|
|
||||||
@Schema(description = "督察报告id") |
|
||||||
private String reportId; |
|
||||||
|
|
||||||
@Schema(description = "流程key") |
|
||||||
private String flowKey; |
|
||||||
|
|
||||||
@Schema(description = "主办层级") |
|
||||||
private String hostLevel; |
|
||||||
|
|
||||||
@Schema(description = "办理时限") |
|
||||||
private String timeLimit; |
|
||||||
|
|
||||||
@Schema(description = "最大签收时长(天)") |
|
||||||
private Integer maxSignDuration; |
|
||||||
|
|
||||||
@Schema(description = "最大办理时长(天)") |
|
||||||
private Integer maxHandleDuration; |
|
||||||
|
|
||||||
@Schema(description = "最大延期时长(天)") |
|
||||||
private Integer maxExtensionDuration; |
|
||||||
|
|
||||||
@Schema(description = "审批流程") |
|
||||||
private String approvalFlow; |
|
||||||
|
|
||||||
@Schema(description = "核查情况描述") |
|
||||||
private String checkStatusDesc; |
|
||||||
|
|
||||||
@Schema(description = "整改情况描述") |
|
||||||
private String rectifyDesc; |
|
||||||
|
|
||||||
@Schema(description = "整改限制天数") |
|
||||||
private Integer rectifyRestrictionDays; |
|
||||||
|
|
||||||
@Schema(description = "追责对象") |
|
||||||
private String accountabilityTarget; |
|
||||||
|
|
||||||
@Schema(description = "办理状态") |
|
||||||
private String processingStatus; |
|
||||||
|
|
||||||
@Schema(description = "延期申请ID") |
|
||||||
private Integer negativeExtensionApplyId; |
|
||||||
|
|
||||||
@Schema(description = "是否能申请延期") |
|
||||||
private Boolean extensionApplyFlag; |
|
||||||
|
|
||||||
@Schema(description = "延期天数") |
|
||||||
private Integer extensionDays; |
|
||||||
|
|
||||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm") |
|
||||||
@Schema(description = "市局下发时间") |
|
||||||
private LocalDateTime firstDistributeTime; |
|
||||||
|
|
||||||
@Schema(description = "是否二级机构办理") |
|
||||||
private Boolean isSecondHandle; |
|
||||||
|
|
||||||
@Schema(description = "二级办理单位id") |
|
||||||
private String handleSecondDepartId; |
|
||||||
|
|
||||||
@Schema(description = "二级办理单位名称") |
|
||||||
private String handleSecondDepartName; |
|
||||||
|
|
||||||
@Schema(description = "三级办理单位id") |
|
||||||
private String handleThreeDepartId; |
|
||||||
|
|
||||||
@Schema(description = "三级办理单位名称") |
|
||||||
private String handleThreeDepartName; |
|
||||||
|
|
||||||
@Schema(description = "涉及案件/警情编号") |
|
||||||
private String caseNumber; |
|
||||||
|
|
||||||
@Schema(description = "办理超时(秒)") |
|
||||||
private Long handleTimeout; |
|
||||||
|
|
||||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm") |
|
||||||
@Schema(description = "申请办结时间") |
|
||||||
private LocalDateTime handleTime; |
|
||||||
|
|
||||||
@Schema(description = "当前处理对象") |
|
||||||
private String currentProcessingObject; |
|
||||||
|
|
||||||
@Schema(description = "专项督察") |
|
||||||
private String specialSupervision; |
|
||||||
|
|
||||||
@Schema(description = "通报期数") |
|
||||||
private String reportNumber; |
|
||||||
|
|
||||||
@Schema(description = "核查办理情况") |
|
||||||
private String verifySituation; |
|
||||||
|
|
||||||
@Schema(description = "佐证材料情况") |
|
||||||
private String verifyFileSituation; |
|
||||||
|
|
||||||
@Schema(description = "单位会签ID") |
|
||||||
private Integer countersignApplyId; |
|
||||||
|
|
||||||
@Schema(description = "创建单位层级") |
|
||||||
private Integer crtDepartLevel; |
|
||||||
|
|
||||||
@Schema(description = "市局下发意见") |
|
||||||
private String firstDistributeComments; |
|
||||||
|
|
||||||
@Schema(description = "是否抽检") |
|
||||||
private Boolean spotCheckFlag; |
|
||||||
|
|
||||||
@Schema(description = "抽检结果") |
|
||||||
private String spotCheckResult; |
|
||||||
|
|
||||||
@Schema(description = "抽检情况") |
|
||||||
private String spotCheckDesc; |
|
||||||
|
|
||||||
@Schema(description = "未整改原因") |
|
||||||
private String unrectifyReason; |
|
||||||
|
|
||||||
@Schema(description = "二级部门id") |
|
||||||
private String secondInvolveDepartId; |
|
||||||
|
|
||||||
@Schema(description = "三级部门id") |
|
||||||
private String threeInvolveDepartId; |
|
||||||
|
|
||||||
@Schema(description = "经办人") |
|
||||||
private String handlePolices; |
|
||||||
|
|
||||||
@Schema(description = "剩余办理时间") |
|
||||||
private Long handleRemainingTime; |
|
||||||
|
|
||||||
@Schema(description = "样本ID") |
|
||||||
private Integer sampleId; |
|
||||||
|
|
||||||
@Schema(description = "下发问题(JSON)") |
|
||||||
private List<VerifyData.Problem> problems; |
|
||||||
|
|
||||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm") |
|
||||||
@Schema(description = "核查时间") |
|
||||||
private LocalDateTime verifyTime; |
|
||||||
|
|
||||||
@Schema(description = "项目名称") |
|
||||||
private String projectName; |
|
||||||
|
|
||||||
@Schema(description = "涉及问题金额") |
|
||||||
private Double involveMoney; |
|
||||||
|
|
||||||
@Schema(description = "化解情况") |
|
||||||
private String resolveSituation; |
|
||||||
|
|
||||||
@Schema(description = "当前状态") |
|
||||||
private String resolveStatus; |
|
||||||
|
|
||||||
@Schema(description = "接访领导姓名") |
|
||||||
private String visitingLeaderName; |
|
||||||
|
|
||||||
@Schema(description = "接访领导工号") |
|
||||||
private String visitingLeaderEmpNo; |
|
||||||
|
|
||||||
@Schema(description = "12337办理结果") |
|
||||||
private String handleResult12337; |
|
||||||
|
|
||||||
@Schema(description = "12337办理结果分组") |
|
||||||
private String handleResult12337Group; |
|
||||||
|
|
||||||
@Schema(description = "涉及人员是否领导班子") |
|
||||||
private String verifiedIsLeader; |
|
||||||
|
|
||||||
@Schema(description = "办理结果") |
|
||||||
private String processResult; |
|
||||||
|
|
||||||
@Schema(description = "处分处理情况") |
|
||||||
private String disciplinaryActionDesc; |
|
||||||
|
|
||||||
@Schema(description = "来源类型") |
|
||||||
private String sourceType; |
|
||||||
|
|
||||||
@Schema(description = "来源类型名称") |
|
||||||
private String sourceTypeDesc; |
|
||||||
|
|
||||||
@Schema(description = "下发单位id") |
|
||||||
private String issuingDepartId; |
|
||||||
|
|
||||||
@Schema(description = "下发单位名称") |
|
||||||
private String issuingDepartName; |
|
||||||
|
|
||||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm") |
|
||||||
@Schema(description = "最后一次审批节点操作时间") |
|
||||||
private LocalDateTime latestProcessTime; |
|
||||||
|
|
||||||
@Schema(description = "二级审批累计时长(秒)") |
|
||||||
private Long secondApprovalTime; |
|
||||||
|
|
||||||
@Schema(description = "市局审批累计时长(秒)") |
|
||||||
private Long firstApproveTime; |
|
||||||
|
|
||||||
// ========== 关联表 ==========
|
|
||||||
|
|
||||||
@Schema(description = "涉及人员列表") |
|
||||||
private List<NegativeBlame> blames = new ArrayList<>(); |
|
||||||
|
|
||||||
@Schema(description = "事件附件列表") |
|
||||||
private List<NegativeThingFile> thingFiles = new ArrayList<>(); |
|
||||||
|
|
||||||
@Schema(description = "核查附件列表") |
|
||||||
private List<NegativeFile> files = new ArrayList<>(); |
|
||||||
} |
|
||||||
@ -1,81 +0,0 @@ |
|||||||
package com.biutag.supervision.pojo.dto.complaintCollection; |
|
||||||
|
|
||||||
import com.alibaba.excel.annotation.ExcelProperty; |
|
||||||
import com.fasterxml.jackson.annotation.JsonFormat; |
|
||||||
import lombok.Getter; |
|
||||||
import lombok.Setter; |
|
||||||
|
|
||||||
import java.time.LocalDateTime; |
|
||||||
|
|
||||||
@Getter |
|
||||||
@Setter |
|
||||||
public class ExportComplaintCollectionBlameVo { |
|
||||||
|
|
||||||
@ExcelProperty("关联负面清单ID") |
|
||||||
private String negativeId; |
|
||||||
|
|
||||||
@ExcelProperty("信件编号") |
|
||||||
private String originId; |
|
||||||
|
|
||||||
@ExcelProperty(value = "发现/受理时间", format = "yyyy-MM-dd HH:mm") |
|
||||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm") |
|
||||||
private LocalDateTime discoveryTime; |
|
||||||
|
|
||||||
@ExcelProperty("来源") |
|
||||||
private String sourceTablePath; |
|
||||||
|
|
||||||
@ExcelProperty("业务类别") |
|
||||||
private String businessTypeName; |
|
||||||
|
|
||||||
@ExcelProperty("涉嫌问题") |
|
||||||
private String involveProblemStr; |
|
||||||
|
|
||||||
@ExcelProperty("简要描述") |
|
||||||
private String thingDesc; |
|
||||||
|
|
||||||
@ExcelProperty("涉及单位(二级)") |
|
||||||
private String secondDepartName; |
|
||||||
|
|
||||||
@ExcelProperty("涉及单位(三级)") |
|
||||||
private String thirdDepartName; |
|
||||||
|
|
||||||
@ExcelProperty("核查结论") |
|
||||||
private String checkStatusName; |
|
||||||
|
|
||||||
@ExcelProperty("追责对象") |
|
||||||
private String accountabilityTarget; |
|
||||||
|
|
||||||
@ExcelProperty("涉及人员姓名") |
|
||||||
private String blameName; |
|
||||||
|
|
||||||
@ExcelProperty("警号") |
|
||||||
private String blameEmpNo; |
|
||||||
|
|
||||||
@ExcelProperty("身份证") |
|
||||||
private String blameIdCode; |
|
||||||
|
|
||||||
@ExcelProperty("人员属性") |
|
||||||
private String ivPersonType; |
|
||||||
|
|
||||||
@ExcelProperty("责任追责") |
|
||||||
private String handleResultName; |
|
||||||
|
|
||||||
@ExcelProperty("责任领导姓名") |
|
||||||
private String leadName; |
|
||||||
|
|
||||||
@ExcelProperty("责任领导警号") |
|
||||||
private String leadEmpNo; |
|
||||||
|
|
||||||
@ExcelProperty("责任领导身份证") |
|
||||||
private String leadIdCode; |
|
||||||
|
|
||||||
@ExcelProperty("责任领导责任追责") |
|
||||||
private String leadHandleResultName; |
|
||||||
|
|
||||||
@ExcelProperty("问题类型") |
|
||||||
private String problemType; |
|
||||||
|
|
||||||
@ExcelProperty("督察措施") |
|
||||||
private String superviseMeasuresName; |
|
||||||
|
|
||||||
} |
|
||||||
@ -1,93 +0,0 @@ |
|||||||
package com.biutag.supervision.pojo.dto.complaintCollection; |
|
||||||
|
|
||||||
import com.alibaba.excel.annotation.ExcelProperty; |
|
||||||
import com.fasterxml.jackson.annotation.JsonFormat; |
|
||||||
import lombok.Getter; |
|
||||||
import lombok.Setter; |
|
||||||
|
|
||||||
import java.time.LocalDateTime; |
|
||||||
|
|
||||||
@Getter |
|
||||||
@Setter |
|
||||||
public class ExportComplaintCollectionExcelVo { |
|
||||||
|
|
||||||
@ExcelProperty("关联负面清单ID") |
|
||||||
private String negativeId; |
|
||||||
|
|
||||||
@ExcelProperty("信件编号") |
|
||||||
private String originId; |
|
||||||
|
|
||||||
@ExcelProperty("来源") |
|
||||||
private String sourceTablePath; |
|
||||||
|
|
||||||
@ExcelProperty(value = "发现/受理时间", format = "yyyy-MM-dd HH:mm") |
|
||||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm") |
|
||||||
private LocalDateTime discoveryTime; |
|
||||||
|
|
||||||
@ExcelProperty("业务类别") |
|
||||||
private String businessTypeName; |
|
||||||
|
|
||||||
@ExcelProperty("涉嫌问题") |
|
||||||
private String involveProblemStr; |
|
||||||
|
|
||||||
@ExcelProperty("简要描述") |
|
||||||
private String thingDesc; |
|
||||||
|
|
||||||
@ExcelProperty("投诉反映人") |
|
||||||
private String responderName; |
|
||||||
|
|
||||||
@ExcelProperty("联系电话") |
|
||||||
private String responderPhone; |
|
||||||
|
|
||||||
@ExcelProperty("涉及警种") |
|
||||||
private String policeTypeName; |
|
||||||
|
|
||||||
@ExcelProperty("办理单位(二级)") |
|
||||||
private String secondDepartName; |
|
||||||
|
|
||||||
@ExcelProperty("办理单位(三级)") |
|
||||||
private String thirdDepartName; |
|
||||||
|
|
||||||
@ExcelProperty("涉及单位") |
|
||||||
private String involveDepartName; |
|
||||||
|
|
||||||
@ExcelProperty("核查结论") |
|
||||||
private String checkStatusName; |
|
||||||
|
|
||||||
// @ExcelProperty("追责对象")
|
|
||||||
// private String accountabilityTarget;
|
|
||||||
|
|
||||||
// @ExcelProperty("涉及人数")
|
|
||||||
// private Integer blameNumber;
|
|
||||||
|
|
||||||
@ExcelProperty("涉及人员") |
|
||||||
private String blameStr; |
|
||||||
|
|
||||||
// @ExcelProperty("责任追责")
|
|
||||||
// private String handleResultName;
|
|
||||||
|
|
||||||
// @ExcelProperty("是否追责")
|
|
||||||
// private String isHoldAccountable;
|
|
||||||
|
|
||||||
// @ExcelProperty("问题类型")
|
|
||||||
// private String problemTypeList;
|
|
||||||
|
|
||||||
@ExcelProperty("办理状态") |
|
||||||
private String processingStatus; |
|
||||||
|
|
||||||
@ExcelProperty("办结情况") |
|
||||||
private String completionStatus; |
|
||||||
|
|
||||||
@ExcelProperty("群众认可") |
|
||||||
private String publicRecognition; |
|
||||||
|
|
||||||
@ExcelProperty("办理方式") |
|
||||||
private String handleMethod; |
|
||||||
|
|
||||||
@ExcelProperty("是否重复件") |
|
||||||
private String repeatt; |
|
||||||
|
|
||||||
@ExcelProperty("标签") |
|
||||||
private String tag; |
|
||||||
|
|
||||||
} |
|
||||||
@ -1,41 +0,0 @@ |
|||||||
package com.biutag.supervision.pojo.entity.mailbox; |
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.annotation.TableId; |
|
||||||
import com.baomidou.mybatisplus.annotation.TableName; |
|
||||||
import io.swagger.v3.oas.annotations.media.Schema; |
|
||||||
import lombok.Getter; |
|
||||||
import lombok.Setter; |
|
||||||
|
|
||||||
import java.time.LocalDateTime; |
|
||||||
|
|
||||||
/** |
|
||||||
* @ClassName MailExtension |
|
||||||
* @Description 局长信箱延期表 |
|
||||||
* @Author auto-generate |
|
||||||
* @Date 2026/4/29 |
|
||||||
*/ |
|
||||||
@Getter |
|
||||||
@Setter |
|
||||||
@TableName("mail_extension") |
|
||||||
@Schema(description = "局长信箱延期表") |
|
||||||
public class MailExtension { |
|
||||||
|
|
||||||
@Schema(description = "主键ID") |
|
||||||
@TableId |
|
||||||
private String id; |
|
||||||
|
|
||||||
@Schema(description = "信件ID") |
|
||||||
private String mailId; |
|
||||||
|
|
||||||
@Schema(description = "延期天数") |
|
||||||
private Integer days; |
|
||||||
|
|
||||||
@Schema(description = "延期理由") |
|
||||||
private String extensionReason; |
|
||||||
|
|
||||||
@Schema(description = "创建时间") |
|
||||||
private LocalDateTime createTime; |
|
||||||
|
|
||||||
@Schema(description = "初核材料(附件)") |
|
||||||
private String initialVerifyAttachments; |
|
||||||
} |
|
||||||
@ -1,32 +0,0 @@ |
|||||||
package com.biutag.supervision.pojo.request.complaintCollection; |
|
||||||
|
|
||||||
import cn.hutool.core.util.StrUtil; |
|
||||||
import com.biutag.supervision.aop.ParamChecked; |
|
||||||
import io.swagger.v3.oas.annotations.media.Schema; |
|
||||||
import lombok.Getter; |
|
||||||
import lombok.Setter; |
|
||||||
|
|
||||||
/** |
|
||||||
* @ClassName ComplaintCollectionDetailRequest |
|
||||||
* @Description 涉访涉诉详情查询请求 |
|
||||||
* @Author system |
|
||||||
* @Date 2026/04/08 |
|
||||||
*/ |
|
||||||
@Setter |
|
||||||
@Getter |
|
||||||
@Schema(description = "涉访涉诉详情查询请求") |
|
||||||
public class ComplaintCollectionDetailRequest implements ParamChecked { |
|
||||||
|
|
||||||
@Schema(description = "投诉举报主键ID") |
|
||||||
private String id; |
|
||||||
|
|
||||||
@Schema(description = "问题id") |
|
||||||
private String negativeId; |
|
||||||
|
|
||||||
@Override |
|
||||||
public void check() { |
|
||||||
if (StrUtil.isBlank(id) && StrUtil.isBlank(negativeId)) { |
|
||||||
throw new IllegalArgumentException("投诉举报主键ID和问题ID不能同时空"); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
@ -1,38 +0,0 @@ |
|||||||
package com.biutag.supervision.pojo.request.complaintCollection; |
|
||||||
|
|
||||||
|
|
||||||
import com.biutag.supervision.aop.ParamChecked; |
|
||||||
import com.biutag.supervision.util.CheckUtil; |
|
||||||
import io.swagger.v3.oas.annotations.media.Schema; |
|
||||||
import lombok.Getter; |
|
||||||
import lombok.Setter; |
|
||||||
|
|
||||||
@Setter |
|
||||||
@Getter |
|
||||||
@Schema(description = "初核请求") |
|
||||||
public class ComplaintCollectionInitialReviewRequest implements ParamChecked { |
|
||||||
|
|
||||||
@Schema(description = "涉访涉诉编号id") |
|
||||||
private String complaintId; |
|
||||||
|
|
||||||
@Schema(description = "初核工作开展情况") |
|
||||||
private String initWorkDes; |
|
||||||
|
|
||||||
@Schema(description = "初核发现的问题及下步工作计划") |
|
||||||
private String initProblemPlan; |
|
||||||
|
|
||||||
@Schema(description = "初核结论") |
|
||||||
private String initVerdict; |
|
||||||
|
|
||||||
@Schema(description = "初核附件") |
|
||||||
private String initFile; |
|
||||||
|
|
||||||
|
|
||||||
@Override |
|
||||||
public void check() { |
|
||||||
CheckUtil.checkNotBlank(complaintId, "涉访涉诉id不能为空"); |
|
||||||
CheckUtil.checkNotBlank(initWorkDes, "初核工作开展情况不能为空"); |
|
||||||
CheckUtil.checkNotBlank(initProblemPlan, "初核发现的问题及下步工作计划不能为空"); |
|
||||||
CheckUtil.checkNotBlank(initVerdict, "初核结论不能为空"); |
|
||||||
} |
|
||||||
} |
|
||||||
@ -1,31 +0,0 @@ |
|||||||
package com.biutag.supervision.pojo.request.complaintCollection; |
|
||||||
|
|
||||||
|
|
||||||
import com.biutag.supervision.aop.ParamChecked; |
|
||||||
import com.biutag.supervision.util.CheckUtil; |
|
||||||
import io.swagger.v3.oas.annotations.media.Schema; |
|
||||||
import lombok.Getter; |
|
||||||
import lombok.Setter; |
|
||||||
|
|
||||||
@Setter |
|
||||||
@Getter |
|
||||||
@Schema(description = "初核请求") |
|
||||||
public class ComplaintCollectionInitialReviewSaveRequest implements ParamChecked { |
|
||||||
|
|
||||||
@Schema(description = "涉访涉诉编号id") |
|
||||||
private String complaintId; |
|
||||||
|
|
||||||
@Schema(description = "初核工作开展情况") |
|
||||||
private String initWorkDes; |
|
||||||
|
|
||||||
@Schema(description = "初核发现的问题及下步工作计划") |
|
||||||
private String initProblemPlan; |
|
||||||
|
|
||||||
@Schema(description = "初核结论") |
|
||||||
private String initVerdict; |
|
||||||
|
|
||||||
@Override |
|
||||||
public void check() { |
|
||||||
CheckUtil.checkNotNull(complaintId, "投诉编号不能为空!"); |
|
||||||
} |
|
||||||
} |
|
||||||
@ -1,85 +0,0 @@ |
|||||||
package com.biutag.supervision.pojo.vo.complaintCollection; |
|
||||||
|
|
||||||
import com.biutag.supervision.pojo.vo.FileVo; |
|
||||||
import io.swagger.v3.oas.annotations.media.Schema; |
|
||||||
import lombok.Data; |
|
||||||
|
|
||||||
import java.time.LocalDateTime; |
|
||||||
import java.util.List; |
|
||||||
|
|
||||||
/** |
|
||||||
* @ClassName ComplaintCollectionDetailVo |
|
||||||
* @Description 涉访涉诉详情查询响应VO(用于修改弹窗数据回显) |
|
||||||
* @Author system |
|
||||||
* @Date 2026/04/08 |
|
||||||
*/ |
|
||||||
@Data |
|
||||||
@Schema(description = "涉访涉诉详情查询响应VO(用于修改弹窗数据回显)") |
|
||||||
public class ComplaintCollectionDetailVo { |
|
||||||
|
|
||||||
@Schema(description = "主键ID") |
|
||||||
private String id; |
|
||||||
|
|
||||||
@Schema(description = "来源表类型") |
|
||||||
private String sourceTable; |
|
||||||
|
|
||||||
@Schema(description = "来源子类型") |
|
||||||
private String sourceTableSubOne; |
|
||||||
|
|
||||||
@Schema(description = "编号") |
|
||||||
private String originId; |
|
||||||
|
|
||||||
@Schema(description = "登记/受理时间") |
|
||||||
private LocalDateTime discoveryTime; |
|
||||||
|
|
||||||
@Schema(description = "来件人姓名") |
|
||||||
private String responderName; |
|
||||||
|
|
||||||
@Schema(description = "身份证号") |
|
||||||
private String responderIdCode; |
|
||||||
|
|
||||||
@Schema(description = "联系电话") |
|
||||||
private String responderPhone; |
|
||||||
|
|
||||||
@Schema(description = "被投诉二级机构ID") |
|
||||||
private String secondDepartId; |
|
||||||
|
|
||||||
@Schema(description = "被投诉二级机构名称") |
|
||||||
private String secondDepartName; |
|
||||||
|
|
||||||
@Schema(description = "来件内容") |
|
||||||
private String thingDesc; |
|
||||||
|
|
||||||
@Schema(description = "涉嫌问题(逗号分隔)") |
|
||||||
private String involveProblem; |
|
||||||
|
|
||||||
@Schema(description = "涉嫌问题ID列表") |
|
||||||
private List<String> involveProblemIdList; |
|
||||||
|
|
||||||
@Schema(description = "是否重复件") |
|
||||||
private String repeatt; |
|
||||||
|
|
||||||
@Schema(description = "是否领导审批") |
|
||||||
private String leadApproval; |
|
||||||
|
|
||||||
@Schema(description = "标签(逗号分隔)") |
|
||||||
private String tag; |
|
||||||
|
|
||||||
@Schema(description = "标签列表") |
|
||||||
private List<String> tags; |
|
||||||
|
|
||||||
@Schema(description = "办理方式") |
|
||||||
private String handleMethod; |
|
||||||
|
|
||||||
@Schema(description = "业务类型code") |
|
||||||
private String businessTypeCode; |
|
||||||
|
|
||||||
@Schema(description = "业务类型name") |
|
||||||
private String businessTypeName; |
|
||||||
|
|
||||||
@Schema(description = "附件列表") |
|
||||||
private List<FileVo> thingFiles; |
|
||||||
|
|
||||||
@Schema(description = "问题ID(negative表)") |
|
||||||
private String negativeId; |
|
||||||
} |
|
||||||
@ -1,927 +0,0 @@ |
|||||||
package com.biutag.supervision.service; |
|
||||||
|
|
||||||
import cn.hutool.core.collection.CollectionUtil; |
|
||||||
import cn.hutool.core.util.IdUtil; |
|
||||||
import cn.hutool.core.util.StrUtil; |
|
||||||
import com.alibaba.fastjson2.JSON; |
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
|
||||||
import com.biutag.supervision.constants.enums.AccountabilityTargetEnum; |
|
||||||
import com.biutag.supervision.constants.enums.BusinessTypeEnum; |
|
||||||
import com.biutag.supervision.constants.enums.ProblemSourcesEnum; |
|
||||||
import com.biutag.supervision.constants.enums.ProcessingStatusEnum; |
|
||||||
import com.biutag.supervision.mapper.ComplaintCollectionMapper; |
|
||||||
import com.biutag.supervision.mapper.MailBlameMapper; |
|
||||||
import com.biutag.supervision.mapper.MailExtensionMapper; |
|
||||||
import com.biutag.supervision.pojo.dto.MailBoxSyncDto; |
|
||||||
import com.biutag.supervision.pojo.dto.NegativeDataOnlyDto; |
|
||||||
import com.biutag.supervision.pojo.dto.mail.MailAttachmentDTO; |
|
||||||
import com.biutag.supervision.pojo.entity.*; |
|
||||||
import com.biutag.supervision.pojo.entity.mailbox.Mail; |
|
||||||
import com.biutag.supervision.pojo.entity.mailbox.MailBlame; |
|
||||||
import com.biutag.supervision.pojo.entity.mailbox.MailExtension; |
|
||||||
import com.biutag.supervision.pojo.enums.complaintCollection.ComplaintCollectionInitialEnum; |
|
||||||
import com.biutag.supervision.pojo.enums.complaintCollection.ComplaintCollectionSourceTableEnum; |
|
||||||
import com.biutag.supervision.pojo.enums.negative.NegativeSourceTypeEnum; |
|
||||||
import com.biutag.supervision.pojo.param.ComplaintCollection.ComplaintCollectionQueryParam; |
|
||||||
import com.biutag.supervision.pojo.param.ComplaintCollection.ComplaintCollectionUpdateParam; |
|
||||||
import com.biutag.supervision.pojo.param.MailQueryParam; |
|
||||||
import com.biutag.supervision.pojo.param.SupDepartQueryParam; |
|
||||||
import com.biutag.supervision.pojo.param.SupExternalDepartQueryParam; |
|
||||||
import com.biutag.supervision.repository.complaintCollection.ComplaintCollectionResourceService; |
|
||||||
import com.biutag.supervision.repository.mail.MailResourceService; |
|
||||||
import com.biutag.supervision.repository.supExternalDepart.SupExternalDepartResourceService; |
|
||||||
import com.biutag.supervision.repository.supdepart.SupDepartResourceService; |
|
||||||
import com.biutag.supervision.service.complaintCollection.ComplaintCollectionService; |
|
||||||
import com.biutag.supervision.util.TimeUtil; |
|
||||||
import lombok.RequiredArgsConstructor; |
|
||||||
import lombok.extern.slf4j.Slf4j; |
|
||||||
import org.springframework.stereotype.Service; |
|
||||||
import org.springframework.transaction.support.TransactionTemplate; |
|
||||||
|
|
||||||
import java.time.LocalDate; |
|
||||||
import java.time.LocalDateTime; |
|
||||||
import java.time.format.DateTimeFormatter; |
|
||||||
import java.util.ArrayList; |
|
||||||
import java.util.Collections; |
|
||||||
import java.util.List; |
|
||||||
import java.util.Objects; |
|
||||||
import java.util.stream.Collectors; |
|
||||||
|
|
||||||
/** |
|
||||||
* @ClassName MailBoxCaptureService |
|
||||||
* @Description 局长信箱数据抓取业务逻辑 |
|
||||||
* - 阶段1:抓取来信,创建 Negative 主表 + 来信附件 + ComplaintCollection |
|
||||||
* - 阶段2:同步办结后的涉及人和核查附件 |
|
||||||
* @Author shihao |
|
||||||
* @Date 2026/4/28 |
|
||||||
*/ |
|
||||||
@Slf4j |
|
||||||
@Service |
|
||||||
@RequiredArgsConstructor |
|
||||||
public class MailBoxCaptureService { |
|
||||||
|
|
||||||
private final NegativeService negativeService; |
|
||||||
private final NegativeBlameService blameService; |
|
||||||
private final NegativeProblemRelationService negativeProblemRelationService; |
|
||||||
private final NegativeFileService fileService; |
|
||||||
private final MailService mailService; |
|
||||||
private final MailResourceService mailResourceService; |
|
||||||
private final SupDepartResourceService supDepartResourceService; |
|
||||||
private final SupExternalDepartResourceService supExternalDepartResourceService; |
|
||||||
private final ComplaintCollectionResourceService complaintCollectionResourceService; |
|
||||||
private final MailBlameMapper mailBlameMapper; |
|
||||||
private final MailExtensionMapper mailExtensionMapper; |
|
||||||
private final SupPoliceService supPoliceService; |
|
||||||
private final SupDictHandleResultMapingService dictHandleResultMapingService; |
|
||||||
private final SupDictProblemTypeMapingService dictProblemTypeMapingService; |
|
||||||
private final SupDictProblemTypeService problemTypeService; |
|
||||||
private final TransactionTemplate transactionTemplate; |
|
||||||
private final ComplaintCollectionMapper complaintCollectionMapper; |
|
||||||
private final ComplaintCollectionService complaintCollectionService; |
|
||||||
private static final DateTimeFormatter INITIAL_REVIEW_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); |
|
||||||
private static final int CHECK_LIMIT_DAYS = 4; |
|
||||||
|
|
||||||
// ==================== 阶段1:抓取来信 ====================
|
|
||||||
|
|
||||||
/** |
|
||||||
* 抓取来信并创建 ComplaintCollection 和 Negative |
|
||||||
*/ |
|
||||||
public void captureMayorMailbox(LocalDateTime start, LocalDateTime end) { |
|
||||||
log.info("【局长信箱抓取新信件】时间范围 {} ~ {}", start, end); |
|
||||||
long startTimeMillis = System.currentTimeMillis(); |
|
||||||
|
|
||||||
try { |
|
||||||
// 1. 查询来信的信件
|
|
||||||
List<Mail> mailList = queryNewMails(start, end); |
|
||||||
if (CollectionUtil.isEmpty(mailList)) { |
|
||||||
log.warn("【局长信箱已办结抓取】未查询到任何投诉举报信件数据,任务结束"); |
|
||||||
return; |
|
||||||
} |
|
||||||
log.info("【局长信箱抓取新信件】查询到投诉举报信件信件数量:{}", mailList.size()); |
|
||||||
|
|
||||||
// 3. 遍历处理
|
|
||||||
int successCount = 0; |
|
||||||
int skipCount = 0; |
|
||||||
int failCount = 0; |
|
||||||
|
|
||||||
for (Mail mail : mailList) { |
|
||||||
try { |
|
||||||
// 每条记录在独立事务中执行
|
|
||||||
boolean success = Boolean.TRUE.equals(transactionTemplate.execute(status -> { |
|
||||||
return doCaptureSingleMail(mail); |
|
||||||
})); |
|
||||||
if (success) { |
|
||||||
successCount++; |
|
||||||
log.debug("【局长信箱抓取新信件】处理成功: mailId={}", mail.getId()); |
|
||||||
} else { |
|
||||||
skipCount++; |
|
||||||
log.debug("【局长信箱抓取新信件】处理跳过: mailId={}", mail.getId()); |
|
||||||
} |
|
||||||
} catch (Exception e) { |
|
||||||
failCount++; |
|
||||||
log.error("【局长信箱抓取新信件】处理失败: mailId={}, error={}", mail.getId(), e.getMessage(), e); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// 4. 输出统计
|
|
||||||
long cost = System.currentTimeMillis() - startTimeMillis; |
|
||||||
log.info("【局长信箱抓取新信件】完成,总数:{},成功:{},跳过:{},失败:{},耗时:{}ms", |
|
||||||
mailList.size(), successCount, skipCount, failCount, cost); |
|
||||||
|
|
||||||
} catch (Exception e) { |
|
||||||
log.error("【局长信箱抓取新信件】任务执行过程中发生严重异常,任务中断", e); |
|
||||||
throw e; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// ==================== 阶段2:同步涉及人、核查附件、核查情况 ====================
|
|
||||||
|
|
||||||
/** |
|
||||||
* 同步办结后的涉及人和核查附件 |
|
||||||
*/ |
|
||||||
public void syncBlameAndFiles(LocalDateTime start, LocalDateTime end) { |
|
||||||
log.info("【阶段2同步】时间范围 {} ~ {}", start, end); |
|
||||||
long startTimeMillis = System.currentTimeMillis(); |
|
||||||
|
|
||||||
try { |
|
||||||
// 1. 从 ComplaintCollection 查询局长信箱记录
|
|
||||||
List<ComplaintCollection> completedList = queryCompletedMailbox(); |
|
||||||
if (CollectionUtil.isEmpty(completedList)) { |
|
||||||
log.warn("【阶段2同步】未查询到任何需要同步的数据,任务结束"); |
|
||||||
return; |
|
||||||
} |
|
||||||
log.info("【阶段2同步】查询到需要同步数量:{}", completedList.size()); |
|
||||||
|
|
||||||
// 2. 遍历处理(每条记录独立事务)
|
|
||||||
int successCount = 0; |
|
||||||
int skipCount = 0; |
|
||||||
int failCount = 0; |
|
||||||
|
|
||||||
for (ComplaintCollection cc : completedList) { |
|
||||||
try { |
|
||||||
String originId = cc.getOriginId(); |
|
||||||
// 查询信件
|
|
||||||
MailQueryParam param = new MailQueryParam(); |
|
||||||
param.setId(originId); |
|
||||||
param.setMailState("completion"); |
|
||||||
List<Mail> mailList = mailResourceService.query(param); |
|
||||||
if (CollectionUtil.isEmpty(mailList)) { |
|
||||||
log.info("【阶段2同步】无对应办结Mail:originId=" + originId); |
|
||||||
continue; |
|
||||||
} |
|
||||||
Mail mail = mailList.get(0); |
|
||||||
|
|
||||||
// 查询涉及人
|
|
||||||
LambdaQueryWrapper<MailBlame> mailBlameLambdaQueryWrapper = new LambdaQueryWrapper<>(); |
|
||||||
mailBlameLambdaQueryWrapper.eq(MailBlame::getMailId, mail.getId()); |
|
||||||
List<MailBlame> mailBlames = mailBlameMapper.selectList(mailBlameLambdaQueryWrapper); |
|
||||||
|
|
||||||
// 构建 DTO
|
|
||||||
MailBoxSyncDto dto = new MailBoxSyncDto(); |
|
||||||
dto.setCc(cc); |
|
||||||
dto.setMail(mail); |
|
||||||
dto.setMailBlames(mailBlames); |
|
||||||
|
|
||||||
// 每条记录在独立事务中执行
|
|
||||||
boolean success = Boolean.TRUE.equals(transactionTemplate.execute(status -> { |
|
||||||
return doSyncSingleRecord(dto); |
|
||||||
})); |
|
||||||
if (success) { |
|
||||||
successCount++; |
|
||||||
log.debug("【阶段2同步】处理成功: ccId={}", cc.getId()); |
|
||||||
} else { |
|
||||||
skipCount++; |
|
||||||
log.debug("【阶段2同步】处理跳过: ccId={}", cc.getId()); |
|
||||||
} |
|
||||||
} catch (Exception e) { |
|
||||||
failCount++; |
|
||||||
log.error("【阶段2同步】处理失败: ccId={}, error={}", cc.getId(), e.getMessage(), e); |
|
||||||
} |
|
||||||
} |
|
||||||
// 3. 输出统计
|
|
||||||
long cost = System.currentTimeMillis() - startTimeMillis; |
|
||||||
log.info("【阶段2同步】完成,总数:{},成功:{},跳过:{},失败:{},耗时:{}ms", |
|
||||||
completedList.size(), successCount, skipCount, failCount, cost); |
|
||||||
} catch (Exception e) { |
|
||||||
log.error("【阶段2同步】任务执行过程中发生严重异常,任务中断", e); |
|
||||||
throw e; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// ==================== 查询相关 ====================
|
|
||||||
|
|
||||||
/** |
|
||||||
* 从 ComplaintCollection 查询局长信箱记录 |
|
||||||
*/ |
|
||||||
private List<ComplaintCollection> queryCompletedMailbox() { |
|
||||||
ComplaintCollectionQueryParam param = new ComplaintCollectionQueryParam(); |
|
||||||
param.setSourceTable(ComplaintCollectionSourceTableEnum.MAYOR_MAILBOX.getCode()); |
|
||||||
param.setBlameSyncStatus("0"); // 未同步
|
|
||||||
return complaintCollectionResourceService.query(param); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 查询来信时间范围内的信件(按来信时间查询) |
|
||||||
*/ |
|
||||||
private List<Mail> queryNewMails(LocalDateTime start, LocalDateTime end) { |
|
||||||
LambdaQueryWrapper<Mail> queryWrapper = new LambdaQueryWrapper<>(); |
|
||||||
queryWrapper.in(Mail::getMailLevel, "涉警投诉"); |
|
||||||
queryWrapper.between(Mail::getMailTime, start, end); |
|
||||||
return mailService.list(queryWrapper); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 判重检查:检查是否已处理 |
|
||||||
*/ |
|
||||||
private boolean isAlreadyProcessed(Mail mail) { |
|
||||||
boolean exists = negativeService.exists(mail.getId()); |
|
||||||
if (exists) { |
|
||||||
log.debug("【局长信箱已办结抓取】跳过(已处理): mailId={}", mail.getId()); |
|
||||||
} |
|
||||||
return exists; |
|
||||||
} |
|
||||||
|
|
||||||
// ==================== 单位查询 ====================
|
|
||||||
|
|
||||||
/** |
|
||||||
* 根据外部单位ID查询单位实体(外部ID -> 内部ID -> 部门实体) |
|
||||||
* |
|
||||||
* @return 部门实体,找不到时返回 null(由调用方决定如何处理) |
|
||||||
*/ |
|
||||||
private SupDepart getDepartByExternalId(Integer externalId) { |
|
||||||
if (externalId == null) { |
|
||||||
log.warn("【局长信箱已办结抓取】外部单位ID为空"); |
|
||||||
return null; |
|
||||||
} |
|
||||||
// 1. 通过外部ID查询映射表,获取内部ID
|
|
||||||
SupExternalDepart externalDepart = findExternalDepart(externalId); |
|
||||||
if (externalDepart == null) { |
|
||||||
log.warn("【局长信箱已办结抓取】未找到SupExternalDepart映射,externalId={}", externalId); |
|
||||||
return null; |
|
||||||
} |
|
||||||
String internalId = externalDepart.getInternalId(); |
|
||||||
if (StrUtil.isBlank(internalId)) { |
|
||||||
log.warn("【局长信箱已办结抓取】SupExternalDepart.internalId为空,externalId={}", externalId); |
|
||||||
return null; |
|
||||||
} |
|
||||||
// 2. 通过内部ID查询部门实体
|
|
||||||
SupDepart depart = findDepartById(internalId); |
|
||||||
if (depart == null) { |
|
||||||
log.warn("【局长信箱已办结抓取】未找到单位,internalId={}", internalId); |
|
||||||
return null; |
|
||||||
} |
|
||||||
return depart; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 根据外部ID查询单位映射 |
|
||||||
* |
|
||||||
* @return 单位映射,找不到时返回 null |
|
||||||
*/ |
|
||||||
private SupExternalDepart findExternalDepart(Integer externalId) { |
|
||||||
SupExternalDepartQueryParam queryParam = new SupExternalDepartQueryParam(); |
|
||||||
queryParam.setSource("局长信箱"); |
|
||||||
queryParam.setExternalIds(Collections.singleton(String.valueOf(externalId))); |
|
||||||
List<SupExternalDepart> list = supExternalDepartResourceService.query(queryParam); |
|
||||||
if (CollectionUtil.isEmpty(list)) { |
|
||||||
return null; |
|
||||||
} |
|
||||||
return list.get(0); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 根据内部ID查询部门 |
|
||||||
*/ |
|
||||||
private SupDepart findDepartById(String id) { |
|
||||||
SupDepartQueryParam queryParam = new SupDepartQueryParam(); |
|
||||||
queryParam.setId(id); |
|
||||||
List<SupDepart> list = supDepartResourceService.query(queryParam); |
|
||||||
if (CollectionUtil.isEmpty(list)) { |
|
||||||
return null; |
|
||||||
} |
|
||||||
return list.get(0); |
|
||||||
} |
|
||||||
|
|
||||||
// ==================== 阶段1 抓取方法 ====================
|
|
||||||
|
|
||||||
/** |
|
||||||
* 单条记录的抓取逻辑(独立事务执行) |
|
||||||
*/ |
|
||||||
public boolean doCaptureSingleMail(Mail mail) { |
|
||||||
// 1. 判重检查
|
|
||||||
if (isAlreadyProcessed(mail)) { |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
// 2. 查询单位信息
|
|
||||||
SupDepart secondDepart = getDepartByExternalId(mail.getSecondDeptId()); |
|
||||||
SupDepart thirdDepart = getDepartByExternalId(mail.getThreeDeptId()); |
|
||||||
|
|
||||||
// 2.1 如果单位信息获取失败,抛出异常触发回滚
|
|
||||||
if (secondDepart == null || thirdDepart == null) { |
|
||||||
throw new RuntimeException("单位映射缺失: mailId=" + mail.getId()); |
|
||||||
} |
|
||||||
|
|
||||||
// 3. 构建并保存 Negative(阶段1:主表 + 来信附件)
|
|
||||||
NegativeDataOnlyDto dto = buildNegativeDto(mail, secondDepart, thirdDepart); |
|
||||||
Negative negative = negativeService.saveNegativeMain(dto); |
|
||||||
negativeService.saveThingFiles(dto, negative.getId()); |
|
||||||
|
|
||||||
// 4. 保存 ComplaintCollection
|
|
||||||
ComplaintCollection cc = buildComplaintCollection(mail, negative.getId(), secondDepart, thirdDepart); |
|
||||||
complaintCollectionResourceService.saveOrUpdateComplaintCollection(Collections.singletonList(cc)); |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
// ==================== 阶段2 同步方法 ====================
|
|
||||||
|
|
||||||
/** |
|
||||||
* 单条记录的同步逻辑(独立事务执行) |
|
||||||
*/ |
|
||||||
private boolean doSyncSingleRecord(MailBoxSyncDto dto) { |
|
||||||
ComplaintCollection cc = dto.getCc(); |
|
||||||
Mail mail = dto.getMail(); |
|
||||||
List<MailBlame> mailBlames = dto.getMailBlames(); |
|
||||||
|
|
||||||
// 1. 通过 originId 找到对应的 Negative(只有阶段1创建过的才同步)
|
|
||||||
Negative negative = negativeService.getByOriginId(cc.getOriginId()); |
|
||||||
if (negative == null) { |
|
||||||
log.warn("【阶段2同步】无对应Negative,跳过: originId={}", cc.getOriginId()); |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
// 2. 组装数据并保存
|
|
||||||
syncBlame(mail, negative.getId(), mailBlames); |
|
||||||
syncFiles(mail, negative.getId()); |
|
||||||
|
|
||||||
// 3. 同步核查情况到 Negative
|
|
||||||
syncCheckStatus(mail, negative); |
|
||||||
|
|
||||||
// 4. 更新同步标记(所有同步操作成功后)
|
|
||||||
updateSyncTag(cc); |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 同步涉及人 |
|
||||||
*/ |
|
||||||
private List<NegativeBlame> syncBlame(Mail mail, String negativeId, List<MailBlame> mailBlames) { |
|
||||||
// 幂等性检查:已存在则跳过
|
|
||||||
LambdaQueryWrapper<NegativeBlame> existWrapper = new LambdaQueryWrapper<>(); |
|
||||||
existWrapper.eq(NegativeBlame::getNegativeId, negativeId); |
|
||||||
if (blameService.count(existWrapper) > 0) { |
|
||||||
log.info("【阶段2同步】涉及人已存在,跳过: negativeId={}", negativeId); |
|
||||||
return Collections.emptyList(); |
|
||||||
} |
|
||||||
|
|
||||||
// mailBlames 已由调用方在事务外查询好
|
|
||||||
if (CollectionUtil.isEmpty(mailBlames)) { |
|
||||||
return Collections.emptyList(); |
|
||||||
} |
|
||||||
|
|
||||||
List<NegativeBlame> result = new ArrayList<>(); |
|
||||||
for (MailBlame blame : mailBlames) { |
|
||||||
// 通过姓名+警号查询 SupPolice 获取完整信息
|
|
||||||
LambdaQueryWrapper<SupPolice> supPoliceLambdaQueryWrapper = new LambdaQueryWrapper<>(); |
|
||||||
supPoliceLambdaQueryWrapper.eq(SupPolice::getIdCode, blame.getBlameIdCode()); |
|
||||||
supPoliceLambdaQueryWrapper.last("limit 1"); |
|
||||||
SupPolice police = supPoliceService.getOne(supPoliceLambdaQueryWrapper); |
|
||||||
if (Objects.isNull(police)) { |
|
||||||
log.warn("【阶段2同步】未找到该警员的数据! blameName={}, blameEmpNo={}", blame.getBlameName(), blame.getBlameEmpNo()); |
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
NegativeBlame negativeBlame = new NegativeBlame(); |
|
||||||
negativeBlame.setBlameId(IdUtil.getSnowflakeNextIdStr()); |
|
||||||
negativeBlame.setNegativeId(negativeId); |
|
||||||
negativeBlame.setType("personal"); |
|
||||||
negativeBlame.setBlameEmpNo(police.getEmpNo()); |
|
||||||
negativeBlame.setBlameIdCode(police.getIdCode()); |
|
||||||
negativeBlame.setBlameName(police.getName()); |
|
||||||
negativeBlame.setCrtTime(LocalDateTime.now()); |
|
||||||
negativeBlame.setUpdTime(LocalDateTime.now()); |
|
||||||
negativeBlame.setLeadEmpNo(blame.getLeaderEmpNo()); |
|
||||||
negativeBlame.setLeadName(blame.getLeaderName()); |
|
||||||
negativeBlame.setLeadIdCode(blame.getLeaderIdCode()); |
|
||||||
negativeBlame.setIvPersonType(police.getPersonType()); |
|
||||||
|
|
||||||
// 责任追究映射
|
|
||||||
if (StrUtil.isNotBlank(blame.getVerifyPunish())) { |
|
||||||
try { |
|
||||||
List<String> externalNames = JSON.parseArray(blame.getVerifyPunish(), String.class); |
|
||||||
List<SupDictHandleResultMaping> dictHandleResultMapings = dictHandleResultMapingService.list(externalNames); |
|
||||||
negativeBlame.setHandleResultCode( |
|
||||||
dictHandleResultMapings.stream() |
|
||||||
.map(SupDictHandleResultMaping::getInternalId) |
|
||||||
.collect(Collectors.joining(",")) |
|
||||||
); |
|
||||||
negativeBlame.setHandleResultName( |
|
||||||
dictHandleResultMapings.stream() |
|
||||||
.map(SupDictHandleResultMaping::getInternalName) |
|
||||||
.collect(Collectors.joining("、")) |
|
||||||
); |
|
||||||
} catch (Exception e) { |
|
||||||
log.warn("【阶段2同步】责任追究映射失败: verifyPunish={}", blame.getVerifyPunish(), e); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// 查证属实问题映射 - 创建 NegativeProblemRelation
|
|
||||||
if (StrUtil.isNotBlank(blame.getVerifyProblem())) { |
|
||||||
try { |
|
||||||
List<String> externalNames = JSON.parseArray(blame.getVerifyProblem(), String.class); |
|
||||||
List<SupDictProblemTypeMaping> problemTypeMapings = dictProblemTypeMapingService.list(externalNames); |
|
||||||
for (SupDictProblemTypeMaping problemTypeMaping : problemTypeMapings) { |
|
||||||
SupDictProblemType threeProblem = problemTypeService.getById(problemTypeMaping.getInternalId()); |
|
||||||
if (threeProblem == null) { |
|
||||||
log.warn("【阶段2同步】未找到三级问题类型: internalId={}", problemTypeMaping.getInternalId()); |
|
||||||
continue; |
|
||||||
} |
|
||||||
NegativeProblemRelation problemRelation = new NegativeProblemRelation(); |
|
||||||
problemRelation.setBlameId(negativeBlame.getBlameId()); |
|
||||||
problemRelation.setNegativeId(negativeId); |
|
||||||
problemRelation.setThreeLevelCode(threeProblem.getId()); |
|
||||||
problemRelation.setThreeLevelContent(threeProblem.getName()); |
|
||||||
// 二级
|
|
||||||
String parentCode = threeProblem.getParentCode(); |
|
||||||
SupDictProblemType twoProblem = null; |
|
||||||
if (StrUtil.isNotBlank(parentCode)) { |
|
||||||
twoProblem = problemTypeService.getById(parentCode); |
|
||||||
if (twoProblem != null) { |
|
||||||
problemRelation.setTwoLevelCode(twoProblem.getId()); |
|
||||||
problemRelation.setTwoLevelContent(twoProblem.getName()); |
|
||||||
} |
|
||||||
} |
|
||||||
// 一级
|
|
||||||
if (twoProblem != null) { |
|
||||||
String twoParentCode = twoProblem.getParentCode(); |
|
||||||
if (StrUtil.isNotBlank(twoParentCode)) { |
|
||||||
SupDictProblemType oneProblem = problemTypeService.getById(twoParentCode); |
|
||||||
if (oneProblem != null) { |
|
||||||
problemRelation.setOneLevelCode(oneProblem.getId()); |
|
||||||
problemRelation.setOneLevelContent(oneProblem.getName()); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
negativeProblemRelationService.save(problemRelation); |
|
||||||
} |
|
||||||
} catch (Exception e) { |
|
||||||
log.warn("【阶段2同步】查证属实问题映射失败: verifyProblem={}", blame.getVerifyProblem(), e); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// 保存 NegativeBlame
|
|
||||||
blameService.save(negativeBlame); |
|
||||||
result.add(negativeBlame); |
|
||||||
log.debug("【阶段2同步】保存涉及人: negativeId={}, blameName={}", negativeId, negativeBlame.getBlameName()); |
|
||||||
} |
|
||||||
return result; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 同步核查情况:Mail 表的核查字段 -> Negative 表 |
|
||||||
*/ |
|
||||||
private void syncCheckStatus(Mail mail, Negative negative) { |
|
||||||
String checkStatus = null; |
|
||||||
String checkStatusName = null; |
|
||||||
String checkStatusCode = null; |
|
||||||
|
|
||||||
// 核查情况映射
|
|
||||||
String verifyIsTrue = mail.getVerifyIsTrue(); |
|
||||||
if ("属实".equals(verifyIsTrue)) { |
|
||||||
checkStatus = "1"; |
|
||||||
checkStatusName = "属实"; |
|
||||||
checkStatusCode = "1"; |
|
||||||
} else if ("基本属实".equals(verifyIsTrue)) { |
|
||||||
checkStatus = "2"; |
|
||||||
checkStatusName = "部分属实"; |
|
||||||
checkStatusCode = "2"; |
|
||||||
} else if ("不属实".equals(verifyIsTrue)) { |
|
||||||
checkStatus = "3"; |
|
||||||
checkStatusName = "不属实"; |
|
||||||
checkStatusCode = "5"; |
|
||||||
} |
|
||||||
|
|
||||||
// 核查结论(优先 verifyDetails,其次 completionComment)
|
|
||||||
String checkStatusDesc = null; |
|
||||||
if (StrUtil.isNotBlank(mail.getVerifyDetails())) { |
|
||||||
checkStatusDesc = mail.getVerifyDetails(); |
|
||||||
} else if (StrUtil.isNotBlank(mail.getCompletionComment())) { |
|
||||||
checkStatusDesc = mail.getCompletionComment(); |
|
||||||
} |
|
||||||
|
|
||||||
// 只有存在核查情况时才更新
|
|
||||||
if (checkStatus != null) { |
|
||||||
// 防御编程:使用 updateById 确保只更新单条记录
|
|
||||||
negative.setCheckStatus(checkStatus); |
|
||||||
negative.setCheckStatusName(checkStatusName); |
|
||||||
negative.setCheckStatusDesc(checkStatusDesc); |
|
||||||
negative.setCheckStatusCode(checkStatusCode); |
|
||||||
negative.setProcessingStatus(ProcessingStatusEnum.completed.name()); |
|
||||||
boolean updated = negativeService.updateById(negative); |
|
||||||
if (updated) { |
|
||||||
log.debug("【阶段2同步】更新核查情况: negativeId={}, checkStatus={}, checkStatusDesc={}", |
|
||||||
negative.getId(), checkStatus, checkStatusDesc); |
|
||||||
} else { |
|
||||||
log.warn("【阶段2同步】更新核查情况失败,Negative不存在或已被删除: negativeId={}", negative.getId()); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 同步涉及人:MailBlame -> NegativeBlame(组装并保存) |
|
||||||
* 参照 MailService#saveMailbox 的 for 循环逻辑 |
|
||||||
*/ |
|
||||||
|
|
||||||
/** |
|
||||||
* 同步核查附件:Mail.verifyAttachments -> NegativeFile(组装并保存) |
|
||||||
*/ |
|
||||||
private void syncFiles(Mail mail, String negativeId) { |
|
||||||
if (StrUtil.isBlank(mail.getVerifyAttachments())) { |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
List<MailAttachmentDTO> attachments = JSON.parseArray(mail.getVerifyAttachments(), MailAttachmentDTO.class); |
|
||||||
if (CollectionUtil.isEmpty(attachments)) { |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
// 幂等性检查:如果该 Negative 已有附件,直接跳过
|
|
||||||
long existingCount = fileService.count( |
|
||||||
new LambdaQueryWrapper<NegativeFile>().eq(NegativeFile::getNegtiveId, negativeId) |
|
||||||
); |
|
||||||
if (existingCount > 0) { |
|
||||||
log.info("【阶段2同步】附件已存在,跳过: negativeId={}", negativeId); |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
for (MailAttachmentDTO att : attachments) { |
|
||||||
NegativeFile file = new NegativeFile(); |
|
||||||
file.setFileId(IdUtil.getSnowflakeNextIdStr()); |
|
||||||
file.setNegtiveId(negativeId); |
|
||||||
file.setFileName(StrUtil.isNotBlank(att.getOriginFilename()) ? att.getOriginFilename() : getFileName(att.getFilepath())); |
|
||||||
file.setFilePath(buildFileUrl(att)); |
|
||||||
file.setCrtTime(LocalDateTime.now()); |
|
||||||
fileService.save(file); |
|
||||||
log.debug("【阶段2同步】保存核查附件: negativeId={}, fileName={}", negativeId, file.getFileName()); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 更新同步标记(所有同步操作成功后) |
|
||||||
* |
|
||||||
* @param cc |
|
||||||
*/ |
|
||||||
private void updateSyncTag(ComplaintCollection cc) { |
|
||||||
ComplaintCollectionUpdateParam complaintCollectionUpdateParam = new ComplaintCollectionUpdateParam(); |
|
||||||
complaintCollectionUpdateParam.setId(cc.getId()); |
|
||||||
complaintCollectionUpdateParam.setBlameSyncStatus("1"); |
|
||||||
boolean updated = complaintCollectionResourceService.updateSelectiveById(complaintCollectionUpdateParam); |
|
||||||
if (!updated) { |
|
||||||
throw new RuntimeException("更新blameSyncStatus失败: ccId=" + cc.getId()); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 构建文件完整URL |
|
||||||
*/ |
|
||||||
private String buildFileUrl(MailAttachmentDTO att) { |
|
||||||
String filepath = null; |
|
||||||
if (StrUtil.isNotBlank(att.getDocxFilepath())) { |
|
||||||
filepath = "http://65.47.60.145/lan-api/api/file/stream/" + att.getDocxFilepath(); |
|
||||||
} else if (StrUtil.isNotBlank(att.getFilepath())) { |
|
||||||
filepath = "http://65.47.60.145/lan-api/api/file/stream/" + att.getFilepath(); |
|
||||||
} |
|
||||||
return filepath; |
|
||||||
} |
|
||||||
|
|
||||||
// ==================== 组装 ====================
|
|
||||||
|
|
||||||
/** |
|
||||||
* 构建 NegativeDataOnlyDto |
|
||||||
*/ |
|
||||||
private NegativeDataOnlyDto buildNegativeDto(Mail mail, SupDepart secondDepart, SupDepart thirdDepart) { |
|
||||||
NegativeDataOnlyDto dto = new NegativeDataOnlyDto(); |
|
||||||
// 基础字段
|
|
||||||
dto.setOriginId(mail.getId()); |
|
||||||
dto.setDiscoveryTime(mail.getMailTime()); |
|
||||||
// 创建时间以来信时间为准
|
|
||||||
dto.setCrtTime(mail.getMailTime()); |
|
||||||
dto.setProblemSourcesCode(ProblemSourcesEnum.JZXX.getValue()); |
|
||||||
dto.setProblemSources(ProblemSourcesEnum.JZXX.getLabel()); |
|
||||||
// 业务类型(其他)
|
|
||||||
dto.setBusinessTypeCode(BusinessTypeEnum.QT.getValue()); |
|
||||||
dto.setBusinessTypeName(BusinessTypeEnum.QT.getLabel()); |
|
||||||
// 追责对象(涉及个人)
|
|
||||||
dto.setAccountabilityTarget(AccountabilityTargetEnum.PERSONAL.getValue()); |
|
||||||
// 反映人信息
|
|
||||||
dto.setResponderName(mail.getContactName()); |
|
||||||
dto.setContactPhone(mail.getContactPhone()); |
|
||||||
// 办理状态(已完成)
|
|
||||||
dto.setProcessingStatus("completed"); |
|
||||||
// 来信内容
|
|
||||||
dto.setThingDesc(mail.getContent()); |
|
||||||
|
|
||||||
// 涉及单位设置
|
|
||||||
dto.setInvolveDepartId(thirdDepart.getId()); |
|
||||||
dto.setInvolveDepartName(thirdDepart.getName()); |
|
||||||
// 办理单位设置
|
|
||||||
dto.setHandleSecondDepartId(secondDepart.getId()); |
|
||||||
dto.setHandleSecondDepartName(secondDepart.getName()); |
|
||||||
dto.setHandleThreeDepartId(thirdDepart.getId()); |
|
||||||
dto.setHandleThreeDepartName(thirdDepart.getName()); |
|
||||||
|
|
||||||
// 来信附件
|
|
||||||
dto.setThingFiles(buildThingFiles(mail.getAttachments())); |
|
||||||
|
|
||||||
// 来源字段
|
|
||||||
dto.setSourceType(NegativeSourceTypeEnum.COMPLAINT_REPORT.getCode()); |
|
||||||
dto.setSourceTypeDesc(NegativeSourceTypeEnum.COMPLAINT_REPORT.getDesc()); |
|
||||||
// dto.setIssuingDepartId("-1");
|
|
||||||
// dto.setIssuingDepartName("自定抓取");
|
|
||||||
return dto; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 构建问题附件列表 |
|
||||||
*/ |
|
||||||
private List<NegativeThingFile> buildThingFiles(String attachmentsJson) { |
|
||||||
List<MailAttachmentDTO> attachments = parseAttachments(attachmentsJson); |
|
||||||
if (CollectionUtil.isEmpty(attachments)) { |
|
||||||
return Collections.emptyList(); |
|
||||||
} |
|
||||||
List<NegativeThingFile> list = new ArrayList<>(); |
|
||||||
for (MailAttachmentDTO att : attachments) { |
|
||||||
NegativeThingFile file = new NegativeThingFile(); |
|
||||||
file.setFileName(StrUtil.isNotBlank(att.getOriginFilename()) ? att.getOriginFilename() : getFileName(att.getFilepath())); |
|
||||||
file.setFilePath(att.getFilepath()); |
|
||||||
file.setCreateTime(LocalDateTime.now()); |
|
||||||
list.add(file); |
|
||||||
} |
|
||||||
return list; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 解析附件 JSON |
|
||||||
*/ |
|
||||||
private List<MailAttachmentDTO> parseAttachments(String json) { |
|
||||||
if (StrUtil.isBlank(json)) { |
|
||||||
return Collections.emptyList(); |
|
||||||
} |
|
||||||
try { |
|
||||||
List<MailAttachmentDTO> list = JSON.parseArray(json, MailAttachmentDTO.class); |
|
||||||
list.forEach(item -> { |
|
||||||
String filepath = null; |
|
||||||
if (StrUtil.isNotBlank(item.getDocxFilepath())) { |
|
||||||
filepath = "http://65.47.60.145/lan-api/api/file/stream/" + item.getDocxFilepath(); |
|
||||||
} else { |
|
||||||
filepath = "http://65.47.60.145/lan-api/api/file/stream/" + item.getFilepath(); |
|
||||||
} |
|
||||||
item.setFilepath(filepath); |
|
||||||
}); |
|
||||||
return list; |
|
||||||
} catch (Exception e) { |
|
||||||
log.warn("【局长信箱已办结抓取】附件JSON解析失败,json={}", json, e); |
|
||||||
return Collections.emptyList(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 从路径获取文件名 |
|
||||||
*/ |
|
||||||
private String getFileName(String path) { |
|
||||||
if (path == null) return null; |
|
||||||
int index = Math.max(path.lastIndexOf('/'), path.lastIndexOf('\\')); |
|
||||||
return index == -1 ? path : path.substring(index + 1); |
|
||||||
} |
|
||||||
|
|
||||||
// ==================== ComplaintCollection 组装 ====================
|
|
||||||
|
|
||||||
/** |
|
||||||
* 构建 ComplaintCollection |
|
||||||
*/ |
|
||||||
private ComplaintCollection buildComplaintCollection(Mail mail, String negativeId, SupDepart secondDepart, SupDepart thirdDepart) { |
|
||||||
ComplaintCollection cc = new ComplaintCollection(); |
|
||||||
// 设置问题编号
|
|
||||||
cc.setNegativeId(negativeId); |
|
||||||
cc.setOriginId(mail.getId()); |
|
||||||
// 问题来源信息
|
|
||||||
String sfssSourceTableSubOne = getSfssSourceTableSubOne(mail.getSource()); |
|
||||||
cc.setSourceTable(ComplaintCollectionSourceTableEnum.MAYOR_MAILBOX.getCode()); |
|
||||||
cc.setSourceTableSubOne(sfssSourceTableSubOne); |
|
||||||
// 单位信息
|
|
||||||
cc.setSecondDepartId(secondDepart.getId()); |
|
||||||
cc.setSecondDepartName(secondDepart.getShortName()); |
|
||||||
cc.setThirdDepartId(thirdDepart.getId()); |
|
||||||
cc.setThirdDepartName(thirdDepart.getShortName()); |
|
||||||
// 创建时间用来信时间
|
|
||||||
cc.setCreateTime(mail.getMailTime()); |
|
||||||
cc.setCreateBy("自动抓取"); |
|
||||||
|
|
||||||
// 身份证
|
|
||||||
cc.setResponderIdCode(mail.getContactIdCard()); |
|
||||||
|
|
||||||
// 初核信息
|
|
||||||
cc.setGwf3(ComplaintCollectionInitialEnum.UN_UPLOADED.getCode()); |
|
||||||
return cc; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
private String getSfssSourceTableSubOne(String str) { |
|
||||||
if ("厅长信箱".equals(str)) { |
|
||||||
return "23_tz"; |
|
||||||
} |
|
||||||
if ("mailbox".equals(str)) { |
|
||||||
return "23_jz"; |
|
||||||
} |
|
||||||
if ("110_report_complaints".equals(str)) { |
|
||||||
return "23_jb"; |
|
||||||
} |
|
||||||
return ""; |
|
||||||
} |
|
||||||
|
|
||||||
// ==================== 延期同步(初核信息) ====================
|
|
||||||
|
|
||||||
/** |
|
||||||
* 同步延期信息(初核工作开展情况、初核附件) |
|
||||||
* 延期数据来源于 mailbox.mail_extension 表 |
|
||||||
* - initWorkDes: 初核工作开展情况 <- extensionReason |
|
||||||
* - initFile: 初核附件 <- initialVerifyAttachments |
|
||||||
*/ |
|
||||||
public void syncExtension() { |
|
||||||
log.info("【延期同步】开始执行延期信息同步任务"); |
|
||||||
long startTimeMillis = System.currentTimeMillis(); |
|
||||||
|
|
||||||
try { |
|
||||||
// 1. 查询所有局长信箱来源没有初核的记录
|
|
||||||
List<ComplaintCollection> mailBoxList = queryAllMailboxRecords(); |
|
||||||
if (CollectionUtil.isEmpty(mailBoxList)) { |
|
||||||
log.info("【延期同步】无局长信箱记录"); |
|
||||||
return; |
|
||||||
} |
|
||||||
log.info("【延期同步】查询到局长信箱数量:{}", mailBoxList.size()); |
|
||||||
|
|
||||||
// 2. 遍历处理
|
|
||||||
int successCount = 0; |
|
||||||
int skipCount = 0; |
|
||||||
int failCount = 0; |
|
||||||
|
|
||||||
for (ComplaintCollection cc : mailBoxList) { |
|
||||||
try { |
|
||||||
String originId = cc.getOriginId(); |
|
||||||
// 查询延期信息(可扩展:后续可能来自多个来源)
|
|
||||||
MailExtension extension = fetchExtensionInfo(originId); |
|
||||||
if (extension == null) { |
|
||||||
// 无延期记录(直接办结),只计算初核状态
|
|
||||||
log.debug("【延期同步】无延期数据,直接计算初核状态:originId=" + originId); |
|
||||||
syncInitialStatusForDirectCompletion(cc.getOriginId(), cc.getId()); |
|
||||||
skipCount++; |
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
// 找到信件
|
|
||||||
Mail mail = getMailByOriginId(originId); |
|
||||||
ComplaintCollection complaintCollection = getComplaintCollectionByOriginId(originId); |
|
||||||
// 初核计算
|
|
||||||
syncInitialStatusWithExtension(mail, complaintCollection, extension); |
|
||||||
successCount++; |
|
||||||
} catch (Exception e) { |
|
||||||
failCount++; |
|
||||||
log.error("【延期同步】处理失败: ccId={}, error={}", cc.getId(), e.getMessage(), e); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
long cost = System.currentTimeMillis() - startTimeMillis; |
|
||||||
log.info("【延期同步】完成,总数:{},成功:{},跳过:{},失败:{},耗时:{}ms", |
|
||||||
mailBoxList.size(), successCount, skipCount, failCount, cost); |
|
||||||
} catch (Exception e) { |
|
||||||
log.error("【延期同步】任务执行过程中发生严重异常", e); |
|
||||||
throw e; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 查询所有局长信箱来源并初核情况是空的记录 |
|
||||||
*/ |
|
||||||
private List<ComplaintCollection> queryAllMailboxRecords() { |
|
||||||
LambdaQueryWrapper<ComplaintCollection> queryWrapper = new LambdaQueryWrapper<>(); |
|
||||||
queryWrapper.isNotNull(ComplaintCollection::getGwf3); |
|
||||||
queryWrapper.eq(ComplaintCollection::getProblemSourcesCode, ComplaintCollectionSourceTableEnum.MAYOR_MAILBOX.getCode()); |
|
||||||
return complaintCollectionMapper.selectList(queryWrapper); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 根据 originId 获取 Mail |
|
||||||
* |
|
||||||
* @param originId 原始信件ID |
|
||||||
* @return Mail 实体 |
|
||||||
*/ |
|
||||||
private Mail getMailByOriginId(String originId) { |
|
||||||
LambdaQueryWrapper<Mail> queryWrapper = new LambdaQueryWrapper<>(); |
|
||||||
queryWrapper.eq(Mail::getId, originId); |
|
||||||
Mail mail = mailService.getOne(queryWrapper); |
|
||||||
if (mail == null) { |
|
||||||
log.warn("【延期同步】未找到 Mail:originId={}", originId); |
|
||||||
} |
|
||||||
return mail; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 根据 originId 获取 ComplaintCollection |
|
||||||
* |
|
||||||
* @param originId 原始信件ID |
|
||||||
* @return ComplaintCollection 实体 |
|
||||||
*/ |
|
||||||
private ComplaintCollection getComplaintCollectionByOriginId(String originId) { |
|
||||||
ComplaintCollectionQueryParam queryParam = new ComplaintCollectionQueryParam(); |
|
||||||
queryParam.setOriginId(originId); |
|
||||||
List<ComplaintCollection> list = complaintCollectionResourceService.query(queryParam); |
|
||||||
if (CollectionUtil.isEmpty(list)) { |
|
||||||
throw new IllegalArgumentException(String.format("未找到对应投诉举报数据,编号%s", originId)); |
|
||||||
} |
|
||||||
return list.get(0); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 有延期记录时,同步初核状态 |
|
||||||
* |
|
||||||
* @param mail 邮件实体 |
|
||||||
* @param cc 归集记录 |
|
||||||
* @param extension 延期信息 |
|
||||||
*/ |
|
||||||
private void syncInitialStatusWithExtension(Mail mail, ComplaintCollection cc, MailExtension extension) { |
|
||||||
long maxSeconds = CHECK_LIMIT_DAYS * TimeUtil.SECONDS_OF_A_DAY; |
|
||||||
long remainingAtInitial = TimeUtil.getRemainingDuration(mail.getMailTime(), extension.getCreateTime(), maxSeconds); |
|
||||||
String initialReviewStatus = (remainingAtInitial < 0) ? |
|
||||||
ComplaintCollectionInitialEnum.TIMEOUT_UPLOAD.getCode() : ComplaintCollectionInitialEnum.UPLOADED.getCode(); |
|
||||||
|
|
||||||
ComplaintCollectionUpdateParam updateParam = new ComplaintCollectionUpdateParam(); |
|
||||||
updateParam.setId(cc.getId()); |
|
||||||
updateParam.setGwf2(String.valueOf(extension.getCreateTime())); |
|
||||||
updateParam.setGwf3(initialReviewStatus); |
|
||||||
updateParam.setInitWorkDes(extension.getExtensionReason()); |
|
||||||
updateParam.setInitFile(extension.getInitialVerifyAttachments()); |
|
||||||
complaintCollectionResourceService.updateSelectiveById(updateParam); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 获取延期信息(扩展点:后续可支持多个来源) |
|
||||||
* 拿最早的那次记录 |
|
||||||
* @param originId 原始信件ID |
|
||||||
* @return 延期信息,未找到返回 null |
|
||||||
*/ |
|
||||||
private MailExtension fetchExtensionInfo(String originId) { |
|
||||||
// ========== 扩展点:后续可在此添加其他初核信息来源 ==========
|
|
||||||
// 目前仅从 mail_extension 表获取延期信息,取创建时间最早的那条
|
|
||||||
List<MailExtension> extensions = mailExtensionMapper.selectList( |
|
||||||
new LambdaQueryWrapper<MailExtension>() |
|
||||||
.eq(MailExtension::getMailId, originId) |
|
||||||
.orderByAsc(MailExtension::getCreateTime) |
|
||||||
.last("LIMIT 1") |
|
||||||
); |
|
||||||
return CollectionUtil.isEmpty(extensions) ? null : extensions.get(0); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 无延期记录时(直接办结),计算初核状态 |
|
||||||
* - 初核时间(gwf2)= submit_completion_time(提交办理时间) |
|
||||||
* - 若 submit_completion_time 为空,则跳过(说明未办结也未申请延期) |
|
||||||
* |
|
||||||
* @param originId 原始信件ID |
|
||||||
* @param ccId 归集记录ID |
|
||||||
*/ |
|
||||||
private void syncInitialStatusForDirectCompletion(String originId, String ccId) { |
|
||||||
// 1. 查询 Mail
|
|
||||||
Mail mail = getMailByOriginId(originId); |
|
||||||
if (mail == null) { |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
// 2. 检查是否有 submit_completion_time
|
|
||||||
LocalDateTime submitCompletionTime = mail.getSubmitCompletionTime(); |
|
||||||
if (submitCompletionTime == null) { |
|
||||||
// 未办结也未申请延期,跳过本次计算
|
|
||||||
log.debug("【延期同步】无延期且未提交办理时间,跳过:originId={}", originId); |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
// 3. 用 submit_completion_time 计算初核状态
|
|
||||||
LocalDateTime mailTime = mail.getMailTime(); |
|
||||||
long maxSeconds = CHECK_LIMIT_DAYS * TimeUtil.SECONDS_OF_A_DAY; |
|
||||||
long remainingAtInitial = TimeUtil.getRemainingDuration(mailTime, submitCompletionTime, maxSeconds); |
|
||||||
String initialReviewStatus = (remainingAtInitial < 0) ? |
|
||||||
ComplaintCollectionInitialEnum.TIMEOUT_UPLOAD.getCode() : |
|
||||||
ComplaintCollectionInitialEnum.UPLOADED.getCode(); |
|
||||||
|
|
||||||
// 4. 更新
|
|
||||||
ComplaintCollectionUpdateParam updateParam = new ComplaintCollectionUpdateParam(); |
|
||||||
updateParam.setId(ccId); |
|
||||||
updateParam.setGwf2(String.valueOf(submitCompletionTime)); |
|
||||||
updateParam.setGwf3(initialReviewStatus); |
|
||||||
updateParam.setInitWorkDes("直接办结"); |
|
||||||
complaintCollectionResourceService.updateSelectiveById(updateParam); |
|
||||||
|
|
||||||
log.debug("【延期同步】无延期记录,初核状态计算完成:originId={}, status={}", |
|
||||||
originId, initialReviewStatus); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
File diff suppressed because it is too large
Load Diff
@ -1,104 +0,0 @@ |
|||||||
package com.biutag.supervision.util; |
|
||||||
|
|
||||||
import cn.hutool.core.util.StrUtil; |
|
||||||
|
|
||||||
/** |
|
||||||
* 参数校验工具类 |
|
||||||
* 用于统一校验请求参数中的字符串,校验失败抛出 IllegalArgumentException |
|
||||||
*/ |
|
||||||
public class CheckUtil { |
|
||||||
|
|
||||||
/** |
|
||||||
* 校验字符串不能为 null 或纯空白字符 |
|
||||||
* |
|
||||||
* @param str 待校验字符串 |
|
||||||
* @param message 异常信息 |
|
||||||
* @throws IllegalArgumentException 如果字符串为 null 或空白 |
|
||||||
*/ |
|
||||||
public static void checkNotBlank(String str, String message) { |
|
||||||
if (StrUtil.isBlank(str)) { |
|
||||||
throw new IllegalArgumentException(message); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 校验字符串不能为 null(允许空字符串) |
|
||||||
* |
|
||||||
* @param str 待校验字符串 |
|
||||||
* @param message 异常信息 |
|
||||||
* @throws IllegalArgumentException 如果字符串为 null |
|
||||||
*/ |
|
||||||
public static void checkNotNull(String str, String message) { |
|
||||||
if (str == null) { |
|
||||||
throw new IllegalArgumentException(message); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 校验字符串长度不能超过指定最大值 |
|
||||||
* |
|
||||||
* @param str 待校验字符串 |
|
||||||
* @param max 最大长度 |
|
||||||
* @param message 异常信息 |
|
||||||
* @throws IllegalArgumentException 如果字符串长度超过 max |
|
||||||
*/ |
|
||||||
public static void checkMaxLength(String str, int max, String message) { |
|
||||||
if (str != null && str.length() > max) { |
|
||||||
throw new IllegalArgumentException(message); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 校验字符串长度不能小于指定最小值 |
|
||||||
* |
|
||||||
* @param str 待校验字符串 |
|
||||||
* @param min 最小长度 |
|
||||||
* @param message 异常信息 |
|
||||||
* @throws IllegalArgumentException 如果字符串长度小于 min |
|
||||||
*/ |
|
||||||
public static void checkMinLength(String str, int min, String message) { |
|
||||||
if (str != null && str.length() < min) { |
|
||||||
throw new IllegalArgumentException(message); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 校验字符串长度必须在指定范围内 |
|
||||||
* |
|
||||||
* @param str 待校验字符串 |
|
||||||
* @param min 最小长度(包含) |
|
||||||
* @param max 最大长度(包含) |
|
||||||
* @param message 异常信息 |
|
||||||
* @throws IllegalArgumentException 如果字符串长度不在 [min, max] 范围内 |
|
||||||
*/ |
|
||||||
public static void checkLengthRange(String str, int min, int max, String message) { |
|
||||||
if (str != null && (str.length() < min || str.length() > max)) { |
|
||||||
throw new IllegalArgumentException(message); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
/** |
|
||||||
* 校验多个字符串是否全部为 null 或纯空白字符(即全部为空) |
|
||||||
* 如果全部为空,则抛出 IllegalArgumentException |
|
||||||
* |
|
||||||
* @param message 异常信息 |
|
||||||
* @param strings 待校验的字符串数组 |
|
||||||
* @throws IllegalArgumentException 如果所有字符串均为 null 或空白 |
|
||||||
*/ |
|
||||||
public static void checkNotAllBlank(String message, String... strings) { |
|
||||||
if (strings == null || strings.length == 0) { |
|
||||||
throw new IllegalArgumentException(message); |
|
||||||
} |
|
||||||
boolean allBlank = true; |
|
||||||
for (String str : strings) { |
|
||||||
if (StrUtil.isNotBlank(str)) { |
|
||||||
allBlank = false; |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
if (allBlank) { |
|
||||||
throw new IllegalArgumentException(message); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
Loading…
Reference in new issue