This commit is contained in:
2026-07-08 15:28:09 +08:00
parent f7f239c68b
commit 783e2b1fb0
54 changed files with 135 additions and 1950 deletions
@@ -5,6 +5,7 @@ import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HtmlUtil;
import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.base.event.role.RoleEventMsg;
@@ -50,6 +51,8 @@ import java.util.stream.Collectors;
@Slf4j
@IocBean
public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService {
private static final int BATCH_SIZE = 500;
private static final int ROLE_DELETE_BATCH_SIZE = 50;
@Inject
private Dao dao;
@@ -180,6 +183,21 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService
List<Sys_user_history> histories = new CopyOnWriteArrayList<>();
List<String> addMemberUserIds = Collections.synchronizedList(new ArrayList<>());
List<String> removeMemberUserIds = Collections.synchronizedList(new ArrayList<>());
Set<String> sourceLoginNames = sources.stream()
.map(Sys_user_source::getLoginname)
.filter(StrUtil::isNotBlank)
.collect(Collectors.toSet());
List<String> leaveUserIds = sysUsers.stream()
.filter(user -> StrUtil.isNotBlank(user.getLoginname()))
.filter(user -> !sourceLoginNames.contains(user.getLoginname()))
.map(Sys_user::getId)
.toList();
List<String> leaveMemberUserIds = sysUsers.stream()
.filter(user -> StrUtil.isNotBlank(user.getLoginname()))
.filter(user -> !sourceLoginNames.contains(user.getLoginname()))
.filter(user -> Boolean.TRUE.equals(user.getMember()))
.map(Sys_user::getId)
.toList();
// 处理每条数据
for (Sys_user_source source : sources) {
@@ -235,7 +253,7 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService
CompletableFuture<Void> insertTask = CompletableFuture.runAsync(() -> {
log.info("新增用户: {} 个", needInitUserList.size());
// 分批处理,每批200条
List<List<Sys_user>> batches = ListUtil.split(needInitUserList, 500);
List<List<Sys_user>> batches = ListUtil.split(needInitUserList, BATCH_SIZE);
batches.forEach(batch -> {
try {
dao.fastInsert(batch);
@@ -260,7 +278,7 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService
if (!roleList.isEmpty()) {
// 分批处理角色分配
List<List<Sys_user_role>> roleBatches = ListUtil.split(roleList, 500);
List<List<Sys_user_role>> roleBatches = ListUtil.split(roleList, BATCH_SIZE);
roleBatches.forEach(batch -> {
try {
dao.fastInsert(batch);
@@ -282,7 +300,7 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService
CompletableFuture<Void> updateTask = CompletableFuture.runAsync(() -> {
log.info("更新用户: {} 个", needDoUpdateList.size());
// 分批处理,每批200条
List<List<Sys_user>> batches = ListUtil.split(needDoUpdateList, 500);
List<List<Sys_user>> batches = ListUtil.split(needDoUpdateList, BATCH_SIZE);
batches.forEach(batch -> {
try {
dao.updateIgnoreNull(batch);
@@ -294,6 +312,65 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService
updateTasks.add(updateTask);
}
if (Lang.isNotEmpty(leaveUserIds)) {
CompletableFuture<Void> leaveTask = CompletableFuture.runAsync(() -> {
log.info("数据源缺失用户转为不在岗并取消会员: {} 个", leaveUserIds.size());
List<List<String>> batches = ListUtil.split(leaveUserIds, BATCH_SIZE);
batches.forEach(batch -> {
try {
/*
* 本次拉取批次中没有出现、但系统用户表仍存在的人员,按离岗处理:
* 1. userState 写为“不在岗”,用于后续人员状态筛选和业务判断;
* 2. member 写为 false,避免仍按会员身份参与后续业务判断。
*/
dao.update(Sys_user.class, Chain.make("userState", "不在岗").add("member", false), Cnd.where("id", "in", batch));
} catch (Exception e) {
log.error("批量更新数据源缺失用户状态异常", e);
}
});
}, executorService);
updateTasks.add(leaveTask);
}
if (Lang.isNotEmpty(leaveMemberUserIds)) {
CompletableFuture<Void> leaveMemberRoleTask = CompletableFuture.runAsync(() -> {
log.info("数据源缺失会员移除会员角色: {} 个", leaveMemberUserIds.size());
List<List<String>> batches = ListUtil.split(leaveMemberUserIds, ROLE_DELETE_BATCH_SIZE);
batches.forEach(batch -> {
try {
/*
* 只对原本是会员的缺失人员移除会员角色,并缩小删除批次,
* 降低 sys_user_role 大批量 DELETE 时的锁等待概率。
*/
dao.clear(Sys_user_role.class, Cnd.where("userId", "in", batch).and("roleId", "=", memberRole.getId()));
} catch (Exception e) {
log.error("批量移除数据源缺失会员角色异常", e);
}
});
}, executorService);
updateTasks.add(leaveMemberRoleTask);
}
if (Lang.isNotEmpty(histories)) {
CompletableFuture<Void> historyTask = CompletableFuture.runAsync(() -> {
log.info("添加历史记录: {} 条", histories.size());
/*
* 历史记录基于更新前的 sys_user 与 sys_user_source 生成,必须纳入本次更新等待范围。
* 继续使用线程池和批量插入,避免主线程逐条写入拖慢更新接口。
*/
List<List<Sys_user_history>> batches = ListUtil.split(histories, 500);
batches.forEach(batch -> {
try {
dao.fastInsert(batch);
} catch (Exception e) {
log.error("批量添加历史记录异常", e);
throw new RuntimeException(e);
}
});
}, executorService);
updateTasks.add(historyTask);
}
// 等待用户数据更新完成
try {
// 设置超时时间,避免无限等待
@@ -307,22 +384,6 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService
return "更新失败: " + e.getMessage();
}
// 3. 异步添加历史记录 - 不等待完成
if (Lang.isNotEmpty(histories)) {
executorService.execute(() -> {
log.info("添加历史记录: {} 条", histories.size());
// 分批处理历史记录
List<List<Sys_user_history>> batches = ListUtil.split(histories, 500);
batches.forEach(batch -> {
try {
dao.fastInsert(batch);
} catch (Exception e) {
log.error("批量添加历史记录异常", e);
}
});
});
}
/* // 4. 异步添加会员角色 - 不等待完成
if (Lang.isNotEmpty(addMemberUserIds)) {
executorService.execute(() -> {
@@ -384,7 +445,7 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService
log.info("全量更新用户数据完成,耗时: {} 毫秒", (endTime - startTime));
return "更新完成: 新增用户 " + needInitUserList.size() + " 个, 更新用户 " + needDoUpdateList.size()
+ " 个, 待添加会员 " + addMemberUserIds.size() + " 个, 待移除会员 " + removeMemberUserIds.size() + "";
+ " 个, 数据源缺失转不在岗 " + leaveUserIds.size() + " 个, 待添加会员 " + addMemberUserIds.size() + " 个, 待移除会员 " + removeMemberUserIds.size() + "";
}
/**
@@ -453,8 +514,8 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService
NutMap change = NutMap.NEW();
change.put("fieldName", "单位");
change.put("field", "unitId");
change.put("sourceValue", unitIdNameMap.get(user.getUnitId()));
change.put("newValue", unitIdNameMap.get(user.getUnitId()));
change.put("sourceValue", getUnitChangeValue(user.getUnitId()));
change.put("newValue", getUnitChangeValue(source.getUnitId()));
changeList.add(change);
// 单位异动发送订阅消息,清空原单位的所有角色
@@ -470,8 +531,8 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService
// 生成变更信息描述
String changeInfos = changeList.stream()
.map(v -> v.getString("fieldName") + "" +
HtmlUtil.cleanHtmlTag(v.getString("sourceValue")) + "" +
HtmlUtil.cleanHtmlTag(v.getString("newValue")))
HtmlUtil.cleanHtmlTag(StrUtil.blankToDefault(v.getString("sourceValue"), "")) + "" +
HtmlUtil.cleanHtmlTag(StrUtil.blankToDefault(v.getString("newValue"), "")))
.collect(Collectors.joining(""));
// 设置历史记录信息
@@ -481,4 +542,15 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService
return history;
}
/**
* 获取单位变更记录展示值。
* 入参 unitId 为系统用户旧单位或数据源新单位ID;返回值优先使用单位名称,查不到名称时保留单位ID,避免变更记录为空导致更新中断。
*/
private String getUnitChangeValue(String unitId) {
if (StrUtil.isBlank(unitId)) {
return "";
}
return StrUtil.blankToDefault(unitIdNameMap.get(unitId), unitId);
}
}
@@ -159,6 +159,8 @@ public class SysDataUserPullServiceImpl extends BaseServiceImpl<Sys_user_source>
} catch (Exception e) {
sysUser.setBirthday(null);
}
} else if ("LXNY".equals(mapping.key)) {
mapping.field.set(sysUser, normalizeArrivalAtSchoolDate(raw.getStr(mapping.key)));
} else {
mapping.field.set(sysUser, raw.getStr(mapping.key));
}
@@ -203,6 +205,27 @@ public class SysDataUserPullServiceImpl extends BaseServiceImpl<Sys_user_source>
}
}
/**
* 规范化信息中心返回的来校时间。
* 入参 arrivalAtSchoolDate 对应信息中心 LXNY 字段,允许 yyyy-MM-dd 或 yyyy-MM
* 返回值用于 sys_user_source.arrivalAtSchoolDate 和后续 sys_user.arrivalAtSchoolDate,年月格式统一补为当月 01 日。
*/
private String normalizeArrivalAtSchoolDate(String arrivalAtSchoolDate) {
String dateText = StrUtil.trim(arrivalAtSchoolDate);
if (StrUtil.isBlank(dateText)) {
return dateText;
}
if (dateText.matches("\\d{4}-\\d{1,2}-\\d{1,2}")) {
String[] parts = dateText.split("-");
return parts[0] + "-" + StrUtil.padPre(parts[1], 2, '0') + "-" + StrUtil.padPre(parts[2], 2, '0');
}
if (dateText.matches("\\d{4}-\\d{1,2}")) {
String[] parts = dateText.split("-");
return parts[0] + "-" + StrUtil.padPre(parts[1], 2, '0') + "-01";
}
return dateText;
}
/**
* 获取信息中心开放平台访问令牌。
* 返回值为接口后续调用使用的 access_token 字符串。
@@ -63,6 +63,11 @@ public class AidFundMemberHistory extends BaseModel {
@ColDefine(type = ColType.VARCHAR, width = 20)
private String personType;
@Column
@Comment("人员属性")
@ColDefine(type = ColType.VARCHAR, width = 20)
private String userAttribute;
@Column
@Comment("在职状态")
@ColDefine(type = ColType.VARCHAR, width = 10)
@@ -64,4 +64,9 @@ public class AidFundMemberPay extends BaseModel {
@ColDefine(type = ColType.VARCHAR, width = 10)
private String userState;
@Column
@Comment("人员属性")
@ColDefine(type = ColType.VARCHAR, width = 20)
private String userAttribute;
}