9 changed files with 1326 additions and 1 deletions
@ -0,0 +1,49 @@ |
|||||||
|
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 3 * * ?") |
||||||
|
public void syncBlameAndFilesForCompletedMails() { |
||||||
|
LocalDateTime start = LocalDate.now().minusDays(1).atStartOfDay(); |
||||||
|
LocalDateTime end = LocalDate.now().atStartOfDay(); |
||||||
|
mailBoxCaptureService.syncBlameAndFiles(start, end); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,29 @@ |
|||||||
|
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; |
||||||
|
} |
||||||
@ -0,0 +1,361 @@ |
|||||||
|
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<>(); |
||||||
|
} |
||||||
@ -0,0 +1,723 @@ |
|||||||
|
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.mapper.MailBlameMapper; |
||||||
|
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.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 lombok.RequiredArgsConstructor; |
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
import org.springframework.stereotype.Service; |
||||||
|
import org.springframework.transaction.support.TransactionTemplate; |
||||||
|
|
||||||
|
import java.time.LocalDateTime; |
||||||
|
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 SupPoliceService supPoliceService; |
||||||
|
private final SupDictHandleResultMapingService dictHandleResultMapingService; |
||||||
|
private final SupDictProblemTypeMapingService dictProblemTypeMapingService; |
||||||
|
private final SupDictProblemTypeService problemTypeService; |
||||||
|
private final TransactionTemplate transactionTemplate; |
||||||
|
|
||||||
|
// ==================== 阶段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 || StrUtil.isNotBlank(checkStatusDesc)) { |
||||||
|
// 防御编程:使用 updateById 确保只更新单条记录
|
||||||
|
negative.setCheckStatus(checkStatus); |
||||||
|
negative.setCheckStatusName(checkStatusName); |
||||||
|
negative.setCheckStatusDesc(checkStatusDesc); |
||||||
|
negative.setCheckStatusCode(checkStatusCode); |
||||||
|
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()); |
||||||
|
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 ""; |
||||||
|
} |
||||||
|
} |
||||||
Loading…
Reference in new issue