批量吊销相关推送逻辑

This commit is contained in:
黎润豪 2026-03-27 17:37:44 +08:00
parent 9e0538db20
commit 5d13e94804
15 changed files with 637 additions and 0 deletions

View File

@ -0,0 +1,77 @@
package com.chinaweal.aiccs.aiccs.revoke.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import com.chinaweal.aiccs.common.util.DateUtils;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.time.LocalDate;
import java.time.LocalDateTime;
/**
* <p>
* 个体吊销信息表
* </p>
*
* @author system
* @since 2026-03-27
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("E_GT_REVOKE")
@ApiModel(value = "EGtRevoke", description = "个体吊销信息表")
public class EGtRevoke extends Model<EGtRevoke> {
private static final long serialVersionUID = 1L;
/**
* 主体身份代码
*/
@TableId(value = "PRIPID")
@ApiModelProperty(value = "主体身份代码")
private String pripid;
/**
* 吊销原因
*/
@TableField("REVREA")
@ApiModelProperty(value = "吊销原因")
private String revrea;
/**
* 吊销日期
*/
@TableField("REVDATE")
@JsonFormat(pattern = DateUtils.DATE_DEFAULT_FORMAT, timezone = "GMT+8")
@ApiModelProperty(value = "吊销日期")
private LocalDate revdate;
/**
* 数据汇总单位
*/
@TableField("S_EXT_FROMNODE")
@ApiModelProperty(value = "数据汇总单位")
private String sExtFromnode;
/**
* 数据汇总时间
*/
@TableField("S_EXT_DATATIME")
@JsonFormat(pattern = DateUtils.DATETIME_DEFAULT_FORMAT, timezone = "GMT+8")
@ApiModelProperty(value = "数据汇总时间")
private LocalDateTime sExtDatatime;
@Override
protected Serializable pkVal() {
return this.pripid;
}
}

View File

@ -0,0 +1,91 @@
package com.chinaweal.aiccs.aiccs.revoke.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import com.chinaweal.aiccs.common.util.DateUtils;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.time.LocalDate;
import java.time.LocalDateTime;
/**
* <p>
* 吊销信息表
* </p>
*
* @author system
* @since 2026-03-27
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("E_REVOKE")
@ApiModel(value = "ERevoke", description = "吊销信息表")
public class ERevoke extends Model<ERevoke> {
private static final long serialVersionUID = 1L;
/**
* 主体身份代码
*/
@TableId(value = "PRIPID")
@ApiModelProperty(value = "主体身份代码", position = 1, required = true)
private String pripid;
/**
* 吊销日期
*/
@TableField("REVDATE")
@JsonFormat(pattern = DateUtils.DATE_DEFAULT_FORMAT, timezone = "GMT+8")
@ApiModelProperty(value = "吊销日期", position = 2)
private LocalDate revdate;
/**
* 违法依据
*/
@TableField("ILLEGACT")
@ApiModelProperty(value = "违法依据", position = 3)
private String illegact;
/**
* 吊销机关
*/
@TableField("REVAUTH")
@ApiModelProperty(value = "吊销机关", position = 4)
private String revauth;
/**
* 吊销依据
*/
@TableField("REVBASIS")
@ApiModelProperty(value = "吊销依据", position = 5)
private String revbasis;
/**
* 数据汇总单位
*/
@TableField("S_EXT_FROMNODE")
@ApiModelProperty(value = "数据汇总单位", position = 6)
private String sExtFromnode;
/**
* 数据汇总时间
*/
@TableField("S_EXT_DATATIME")
@JsonFormat(pattern = DateUtils.DATETIME_DEFAULT_FORMAT, timezone = "GMT+8")
@ApiModelProperty(value = "数据汇总时间", position = 7)
private LocalDateTime sExtDatatime;
@Override
protected Serializable pkVal() {
return this.pripid;
}
}

View File

@ -0,0 +1,25 @@
package com.chinaweal.aiccs.aiccs.revoke.mapper;
import com.chinaweal.aiccs.aiccs.revoke.entity.EGtRevoke;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* <p>
* 个体吊销信息表 Mapper 接口
* </p>
*
* @author system
* @since 2026-03-27
*/
public interface EGtRevokeMapper extends BaseMapper<EGtRevoke> {
/**
* 根据主体身份代码查询
* @param pripid 主体身份代码
* @return 个体吊销信息列表
*/
List<EGtRevoke> selectByPripid(@Param("pripid") String pripid);
}

View File

@ -0,0 +1,25 @@
package com.chinaweal.aiccs.aiccs.revoke.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.chinaweal.aiccs.aiccs.revoke.entity.ERevoke;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* <p>
* 吊销信息表 Mapper 接口
* </p>
*
* @author system
* @since 2026-03-27
*/
public interface ERevokeMapper extends BaseMapper<ERevoke> {
/**
* 根据主体身份代码查询
* @param pripid 主体身份代码
* @return 吊销信息列表
*/
List<ERevoke> selectByPripid(@Param("pripid") String pripid);
}

View File

@ -7,6 +7,7 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.chinaweal.aiccs.aiccs.revoke.entity.dto.RevokeListDto;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
/**
@ -21,4 +22,14 @@ public interface RevokelistMapper extends BaseMapper<Revokelist> {
IPage<RevokeListDto> selectRevokeListPage(IPage<RevokeListDto> page, @Param("customParamMap") Map<String, Object> customParamMap);
/**
* 根据主键切片查询未同步的REVOKELIST记录
* 使用EXISTS语句判断E_REVOKE和E_GT_REVOKE表是否已存在该PRIPID
*
* @param lastRevlistId 上一次查询的最后一条主键ID初始为null或空字符串
* @param pageSize 每页记录数
* @return 未同步的REVOKELIST记录列表
*/
List<Revokelist> selectUnsyncedRevokelistBySlice(@Param("lastRevlistId") String lastRevlistId, @Param("pageSize") int pageSize);
}

View File

@ -0,0 +1,237 @@
package com.chinaweal.aiccs.aiccs.revoke.schedule;
import com.chinaweal.aiccs.aiccs.entbase.entity.TREntBase;
import com.chinaweal.aiccs.aiccs.entbase.service.TREntBaseService;
import com.chinaweal.aiccs.aiccs.revoke.entity.EGtRevoke;
import com.chinaweal.aiccs.aiccs.revoke.entity.ERevoke;
import com.chinaweal.aiccs.aiccs.revoke.entity.Revokelist;
import com.chinaweal.aiccs.aiccs.revoke.mapper.RevokelistMapper;
import com.chinaweal.aiccs.aiccs.revoke.service.IEGtRevokeService;
import com.chinaweal.aiccs.aiccs.revoke.service.IERevokeService;
import com.chinaweal.aiccs.redis.RedisService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.List;
/**
* 吊销数据同步定时任务
* <p>
* 扫描REVOKELIST表将未同步的数据根据市场主体类型分别写入E_REVOKE表或E_GT_REVOKE表
* - 个体户enttype为9500或9910写入E_GT_REVOKE表
* - 其他市场主体写入E_REVOKE表
*
* @author system
* @since 2026-03-27
*/
@Slf4j
@Configuration
@EnableScheduling
public class RevokeSyncScheduled {
/**
* Redis锁key
*/
private static final String REDIS_LOCK_KEY = "schedule:syncRevokeData:lock";
/**
* Redis锁超时时间
*/
private static final int REDIS_LOCK_TIMEOUT = 30 * 60; // 30分钟
/**
* 个体户类型编码
*/
private static final String ENTTYPE_INDIVIDUAL_9500 = "9500";
private static final String ENTTYPE_INDIVIDUAL_9910 = "9910";
@Autowired
private RevokelistMapper revokelistMapper;
@Autowired
private IERevokeService eRevokeService;
@Autowired
private IEGtRevokeService eGtRevokeService;
@Autowired
private TREntBaseService trEntBaseService;
@Autowired
private RedisService redisService;
/**
* 定时同步吊销数据
* 将REVOKELIST表中未同步的数据根据市场主体类型分别写入E_REVOKE或E_GT_REVOKE表
*/
@Scheduled(cron = "${scheduling.cron.syncRevokeData:-}")
@Transactional(rollbackFor = Exception.class)
public void syncRevokeData() {
// 检查Redis并发锁
if (!acquireLock()) {
log.warn("【吊销数据同步任务】上一次执行尚未完成或锁已存在,本次跳过");
return;
}
try {
doSync();
} catch (Exception e) {
log.error("【吊销数据同步任务】执行异常:{}", e.getMessage(), e);
throw e;
} finally {
releaseLock();
}
}
/**
* 执行数据同步
*/
private void doSync() {
log.info("【吊销数据同步任务】开始执行");
int pageSize = 100;
int successCount = 0;
int failCount = 0;
int batchNum = 0;
String lastRevlistId = null;
while (true) {
// 切片查询未同步的记录
List<Revokelist> unsyncedList = getUnsynchronizedRevokelist(lastRevlistId, pageSize);
if (unsyncedList.isEmpty()) {
log.info("【吊销数据同步任务】没有更多需要同步的数据,任务结束");
break;
}
batchNum++;
log.info("【吊销数据同步任务】正在处理第{}批,记录数:{}", batchNum, unsyncedList.size());
// 更新lastRevlistId为当前批次的最后一条记录主键
lastRevlistId = unsyncedList.get(unsyncedList.size() - 1).getRevlistid();
for (Revokelist revokelist : unsyncedList) {
try {
// 根据PRIPID查询市场主体详细信息
TREntBase entBase = trEntBaseService.getById(revokelist.getPripid());
if (entBase == null) {
log.warn("【吊销数据同步任务】市场主体信息不存在PRIPID{}", revokelist.getPripid());
failCount++;
continue;
}
String enttype = entBase.getEnttype();
LocalDateTime now = LocalDateTime.now();
// 根据市场主体类型判断写入目标表
if (isIndividual(enttype)) {
// 个体户写入E_GT_REVOKE表
EGtRevoke eGtRevoke = buildEGtRevoke(revokelist, now);
eGtRevokeService.save(eGtRevoke);
log.debug("【吊销数据同步任务】个体户数据已写入PRIPID{}", revokelist.getPripid());
} else {
// 其他市场主体写入E_REVOKE表
ERevoke eRevoke = buildERevoke(revokelist, now);
eRevokeService.save(eRevoke);
log.debug("【吊销数据同步任务】企业数据已写入PRIPID{}", revokelist.getPripid());
}
successCount++;
} catch (Exception e) {
log.error("【吊销数据同步任务】数据同步失败PRIPID{},错误信息:{}", revokelist.getPripid(), e.getMessage(), e);
failCount++;
}
}
}
log.info("【吊销数据同步任务】执行完成,共处理{}批,成功:{},失败:{}", batchNum, successCount, failCount);
}
/**
* 获取分布式锁
*
* @return true-获取锁成功false-锁已存在
*/
private boolean acquireLock() {
// 检查锁是否存在
if (redisService.exists(REDIS_LOCK_KEY)) {
return false;
}
// 设置锁30分钟超时
redisService.set(REDIS_LOCK_KEY, "1", REDIS_LOCK_TIMEOUT);
return true;
}
/**
* 释放分布式锁
*/
private void releaseLock() {
redisService.remove(REDIS_LOCK_KEY);
log.debug("【吊销数据同步任务】Redis锁已释放");
}
/**
* 切片查询未同步的REVOKELIST记录
* 使用EXISTS语句判断E_REVOKE和E_GT_REVOKE表是否已存在该PRIPID
*
* @param lastRevlistId 上一次查询的最后一条主键ID初始为null
* @param pageSize 每页记录数
* @return 未同步的REVOKELIST记录列表
*/
private List<Revokelist> getUnsynchronizedRevokelist(String lastRevlistId, int pageSize) {
return revokelistMapper.selectUnsyncedRevokelistBySlice(lastRevlistId, pageSize);
}
/**
* 判断是否为个体户
*
* @param enttype 市场主体类型编码
* @return true-是个体户false-不是个体户
*/
private boolean isIndividual(String enttype) {
return ENTTYPE_INDIVIDUAL_9500.equals(enttype) || ENTTYPE_INDIVIDUAL_9910.equals(enttype);
}
/**
* 构建E_REVOKE实体
*/
private ERevoke buildERevoke(Revokelist revokelist, LocalDateTime now) {
ERevoke eRevoke = new ERevoke();
eRevoke.setPripid(revokelist.getPripid());
// REVDATE字段映射revokelist的REVOKEDATE转换为LocalDate
if (revokelist.getRevokedate() != null) {
eRevoke.setRevdate(revokelist.getRevokedate().toLocalDate());
}
// ILLEGACT字段映射revokelist的REVOKEREA存储吊销原因
eRevoke.setIllegact(revokelist.getRevokeRea());
// REVAUTH字段映射revokelist的PUNISHORGCN存储处罚机关
eRevoke.setRevauth(revokelist.getPunishOrgCn());
// REVBASIS字段可以从CASENO或WRITSID获取此处暂时留空
eRevoke.setRevbasis(null);
// S_EXT_FROMNODE字段数据汇总单位从LAUPTIME或其他字段获取
eRevoke.setSExtFromnode("150000");
eRevoke.setSExtDatatime(now);
return eRevoke;
}
/**
* 构建EGtRevoke实体
*/
private EGtRevoke buildEGtRevoke(Revokelist revokelist, LocalDateTime now) {
EGtRevoke eGtRevoke = new EGtRevoke();
eGtRevoke.setPripid(revokelist.getPripid());
// REVREA字段映射revokelist的REVOKEREA存储吊销原因
eGtRevoke.setRevrea(revokelist.getRevokeRea());
// REVDATE字段映射revokelist的REVOKEDATE转换为LocalDate
if (revokelist.getRevokedate() != null) {
eGtRevoke.setRevdate(revokelist.getRevokedate().toLocalDate());
}
// S_EXT_FROMNODE字段数据汇总单位
eGtRevoke.setSExtFromnode("150000");
eGtRevoke.setSExtDatatime(now);
return eGtRevoke;
}
}

View File

@ -0,0 +1,25 @@
package com.chinaweal.aiccs.aiccs.revoke.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.chinaweal.aiccs.aiccs.revoke.entity.EGtRevoke;
import java.util.List;
/**
* <p>
* 个体吊销信息表 服务类
* </p>
*
* @author system
* @since 2026-03-27
*/
public interface IEGtRevokeService extends IService<EGtRevoke> {
/**
* 根据主体身份代码查询
*
* @param pripid 主体身份代码
* @return 个体吊销信息列表
*/
List<EGtRevoke> selectByPripid(String pripid);
}

View File

@ -0,0 +1,25 @@
package com.chinaweal.aiccs.aiccs.revoke.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.chinaweal.aiccs.aiccs.revoke.entity.ERevoke;
import java.util.List;
/**
* <p>
* 吊销信息表 服务类
* </p>
*
* @author system
* @since 2026-03-27
*/
public interface IERevokeService extends IService<ERevoke> {
/**
* 根据主体身份代码查询
*
* @param pripid 主体身份代码
* @return 吊销信息列表
*/
List<ERevoke> selectByPripid(String pripid);
}

View File

@ -0,0 +1,26 @@
package com.chinaweal.aiccs.aiccs.revoke.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.chinaweal.aiccs.aiccs.revoke.entity.EGtRevoke;
import com.chinaweal.aiccs.aiccs.revoke.mapper.EGtRevokeMapper;
import com.chinaweal.aiccs.aiccs.revoke.service.IEGtRevokeService;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* <p>
* 个体吊销信息表 服务实现类
* </p>
*
* @author system
* @since 2026-03-27
*/
@Service
public class EGtRevokeServiceImpl extends ServiceImpl<EGtRevokeMapper, EGtRevoke> implements IEGtRevokeService {
@Override
public List<EGtRevoke> selectByPripid(String pripid) {
return baseMapper.selectByPripid(pripid);
}
}

View File

@ -0,0 +1,26 @@
package com.chinaweal.aiccs.aiccs.revoke.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.chinaweal.aiccs.aiccs.revoke.entity.ERevoke;
import com.chinaweal.aiccs.aiccs.revoke.mapper.ERevokeMapper;
import com.chinaweal.aiccs.aiccs.revoke.service.IERevokeService;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* <p>
* 吊销信息表 服务实现类
* </p>
*
* @author system
* @since 2026-03-27
*/
@Service
public class ERevokeServiceImpl extends ServiceImpl<ERevokeMapper, ERevoke> implements IERevokeService {
@Override
public List<ERevoke> selectByPripid(String pripid) {
return baseMapper.selectByPripid(pripid);
}
}

View File

@ -30,6 +30,7 @@ import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.servlet.ServletOutputStream;
import java.io.File;
@ -355,6 +356,7 @@ public class TsbizrevlistServiceImpl extends BaseServiceImpl<TsbizrevlistMapper,
* 直接将市场主体吊销状态写入吊销结果表并推送至外部系统
*/
@Override
@Transactional
public DirectRevokeResultDto directRevoke(DirectRevokeDto dto, AICUser user) {
List<String> pripids = dto.getPripids();

View File

@ -156,6 +156,7 @@ scheduling:
staticHqlcusers: 0 30 00 * * ? #定时获取浪潮用户
forceDeregisterNoticeExpired: '-' #拟强制注销公告期满更新定时任务
syncRevokeData: '-' #吊销数据同步任务同步REVOKELIST到E_REVOKE和E_GT_REVOKE表
ot:
bakCseDownloadPath: /ot/download/case
bakPermitDownloadPath: /ot/download/permit

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.chinaweal.aiccs.aiccs.revoke.mapper.EGtRevokeMapper">
<!-- 通用查询映射结果 -->
<resultMap id="BaseResultMap" type="com.chinaweal.aiccs.aiccs.revoke.entity.EGtRevoke">
<id column="PRIPID" property="pripid" />
<result column="REVREA" property="revrea" />
<result column="REVDATE" property="revdate" />
<result column="S_EXT_FROMNODE" property="sExtFromnode" />
<result column="S_EXT_DATATIME" property="sExtDatatime" />
</resultMap>
<!-- 通用查询结果列 -->
<sql id="Base_Column_List">
PRIPID, REVREA, REVDATE, S_EXT_FROMNODE, S_EXT_DATATIME
</sql>
<select id="selectByPripid" resultMap="BaseResultMap">
SELECT <include refid="Base_Column_List" />
FROM E_GT_REVOKE
WHERE PRIPID = #{pripid}
</select>
</mapper>

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.chinaweal.aiccs.aiccs.revoke.mapper.ERevokeMapper">
<!-- 通用查询映射结果 -->
<resultMap id="BaseResultMap" type="com.chinaweal.aiccs.aiccs.revoke.entity.ERevoke">
<id column="PRIPID" property="pripid" />
<result column="REVDATE" property="revdate" />
<result column="ILLEGACT" property="illegact" />
<result column="REVAUTH" property="revauth" />
<result column="REVBASIS" property="revbasis" />
<result column="S_EXT_FROMNODE" property="sExtFromnode" />
<result column="S_EXT_DATATIME" property="sExtDatatime" />
</resultMap>
<!-- 通用查询结果列 -->
<sql id="Base_Column_List">
PRIPID, REVDATE, ILLEGACT, REVAUTH, REVBASIS, S_EXT_FROMNODE, S_EXT_DATATIME
</sql>
<select id="selectByPripid" resultMap="BaseResultMap">
SELECT <include refid="Base_Column_List" />
FROM E_REVOKE
WHERE PRIPID = #{pripid}
</select>
</mapper>

View File

@ -65,5 +65,19 @@
order by REVOKEDATE desc
</select>
<select id="selectUnsyncedRevokelistBySlice" resultMap="BaseResultMap">
SELECT <include refid="Base_Column_List" />
FROM revokelist r
WHERE r.STATE = '1'
AND r.PRIPID IS NOT NULL
AND NOT EXISTS (SELECT 1 FROM E_REVOKE e WHERE e.PRIPID = r.PRIPID)
AND NOT EXISTS (SELECT 1 FROM E_GT_REVOKE g WHERE g.PRIPID = r.PRIPID)
<if test="lastRevlistId != null and lastRevlistId != ''">
AND r.REVLISTID &gt; #{lastRevlistId}
</if>
ORDER BY r.REVLISTID ASC
FETCH FIRST #{pageSize} ROWS ONLY
</select>
</mapper>