修改TMS用户同步只增量同步用户数据

This commit is contained in:
luojiaquan
2026-10-19 09:59:01 +08:00
parent a48b40c649
commit 92fc2f1ad0
6 changed files with 271 additions and 9 deletions
@@ -72,15 +72,16 @@ public class SysUserController extends BaseController
} }
/** /**
* 从老系统(Oracle)同步部门/角色/用户。 * 从 TMS(老系统 Oracle)增量同步用户。
* 覆盖式迁移:除内置超级管理员外,现有演示部门、角色、用户将被替换。 * 不变更组织机构、不同步角色:已存在用户(优先工号,其次姓名+机构匹配)保持不变,
* 仅新增不存在的用户;机构匹配不到的新用户挂在“威狮国际”下。
*/ */
@PreAuthorize("@ss.hasPermi('system:user:add')") @PreAuthorize("@ss.hasPermi('system:user:add')")
@Log(title = "用户管理-老系统组织同步", businessType = BusinessType.IMPORT) @Log(title = "用户管理-TMS同步用户", businessType = BusinessType.IMPORT)
@PostMapping("/syncFromOracle") @PostMapping("/syncFromOracle")
public AjaxResult syncFromOracle() public AjaxResult syncFromOracle()
{ {
Map<String, Object> result = orgSyncService.syncFromOracle(); Map<String, Object> result = orgSyncService.syncUsersOnly();
return AjaxResult.success(String.valueOf(result.get("summary")), result); return AjaxResult.success(String.valueOf(result.get("summary")), result);
} }
@@ -40,6 +40,9 @@ public class OrgSyncService
/** 写库人标记 */ /** 写库人标记 */
public static final String SYNC_MARKER = "oracle-sync"; public static final String SYNC_MARKER = "oracle-sync";
/** TMS 用户增量同步写库人标记 */
public static final String USER_SYNC_MARKER = "tms-user-sync";
/** 新用户默认密码(BCrypt,明文 123456) */ /** 新用户默认密码(BCrypt,明文 123456) */
private static final String DEFAULT_PASSWORD = private static final String DEFAULT_PASSWORD =
"$2a$10$gAOzrNJaYLM2m85.9ykkLO0IPvQktxqee8RVpAu1e3.J0Ujiv4Z0G"; "$2a$10$gAOzrNJaYLM2m85.9ykkLO0IPvQktxqee8RVpAu1e3.J0Ujiv4Z0G";
@@ -101,6 +104,194 @@ public class OrgSyncService
return result; return result;
} }
/**
* TMS 用户增量同步(按钮「TMS同步用户」)。
* 规则:
* 1) 不变动组织机构(sys_dept)、不同步角色/岗位(sys_role、sys_user_role、sys_user_post);
* 2) 只同步用户:已存在的用户一律保持不变,仅新增系统中不存在的用户;
* 3) 匹配优先级:先按工号(EMPLOYEE_CODE = sys_user.user_name)匹配,
* 再按姓名(EMPLOYEE_NAME = sys_user.nick_name)+ 所在机构匹配;
* 4) 新用户部门:按 TMS 部门名称匹配现有机构(优先“威狮国际”直属部门),
* 机构匹配不到的统一挂到根部门“威狮国际”(dept_id=200);
* 5) 新用户 user_id 自增、默认密码 123456、不分配角色。
*/
public Map<String, Object> syncUsersOnly()
{
long t0 = System.currentTimeMillis();
List<Map<String, Object>> deptRows = oracleOrgReader.readAllDept();
List<Map<String, Object>> empRows = oracleOrgReader.readAllEmployee();
// TMS 部门编码 -> 部门名称(TAB_DEPT 中 DEPT_NAME 即机构名)
Map<String, String> tmsDeptNameByCode = new HashMap<String, String>();
for (Map<String, Object> row : deptRows)
{
String code = clean(str(row, "DEPT_CODE"));
String name = clean(str(row, "DEPT_NAME"));
if (code != null && name != null && !tmsDeptNameByCode.containsKey(code))
{
tmsDeptNameByCode.put(code, name);
}
}
// 现有机构:部门名称 -> deptId(重名时优先“威狮国际”直属部门)
List<SysDept> bmsDepts = orgSyncWriter.readSyncDepts();
Map<String, Long> bmsDeptIdByName = new HashMap<String, Long>();
for (SysDept d : bmsDepts)
{
String name = clean(d.getDeptName());
if (name == null || d.getDeptId() == null)
{
continue;
}
Long exist = bmsDeptIdByName.get(name);
if (exist == null
|| (d.getParentId() != null && d.getParentId().longValue() == ROOT_DEPT_ID
&& exist.longValue() != ROOT_DEPT_ID))
{
bmsDeptIdByName.put(name, d.getDeptId());
}
}
// 现有用户匹配索引:工号(小写);姓名 + 机构
List<SysUser> bmsUsers = orgSyncWriter.readSyncUsers();
Map<String, SysUser> userByLoginName = new HashMap<String, SysUser>();
Map<String, SysUser> userByNameDept = new HashMap<String, SysUser>();
for (SysUser u : bmsUsers)
{
if (u == null || u.getUserId() == null)
{
continue;
}
if (clean(u.getUserName()) != null
&& !userByLoginName.containsKey(u.getUserName().trim().toLowerCase()))
{
userByLoginName.put(u.getUserName().trim().toLowerCase(), u);
}
if (clean(u.getNickName()) != null && u.getDeptId() != null)
{
String key = nameDeptKey(u.getDeptId(), u.getNickName());
if (!userByNameDept.containsKey(key))
{
userByNameDept.put(key, u);
}
}
}
List<SysUser> toInsert = new ArrayList<SysUser>();
Set<String> batchLoginNames = new HashSet<String>();
long matchedByCode = 0;
long matchedByName = 0;
long orgFallback = 0;
long skipped = 0;
for (Map<String, Object> row : empRows)
{
String code = clean(str(row, "EMPLOYEE_CODE"));
String name = clean(str(row, "EMPLOYEE_NAME"));
if (code == null && name == null)
{
skipped++;
continue;
}
if (code != null && BUILTIN_ADMIN.equalsIgnoreCase(code))
{
// 不动 BMS 内置超级管理员
skipped++;
continue;
}
// 按机构匹配部门:TMS DEPT_CODE -> TMS 部门名称 -> 现有 sys_dept;匹配不到挂威狮国际
Long targetDeptId = ROOT_DEPT_ID;
String deptCode = clean(str(row, "DEPT_CODE"));
if (deptCode != null)
{
String tmsDeptName = tmsDeptNameByCode.get(deptCode);
if (tmsDeptName != null)
{
Long matched = bmsDeptIdByName.get(tmsDeptName);
if (matched != null)
{
targetDeptId = matched;
}
}
}
boolean underRoot = targetDeptId == ROOT_DEPT_ID;
// 1) 优先工号匹配
if (code != null && userByLoginName.containsKey(code.toLowerCase()))
{
matchedByCode++;
continue;
}
// 2) 再按姓名 + 所在机构匹配
if (name != null && userByNameDept.containsKey(nameDeptKey(targetDeptId, name)))
{
matchedByName++;
continue;
}
// 3) 不存在 -> 新增(工号是登录账号,缺失无法建号)
if (code == null)
{
log.warn("TMS 用户同步跳过:姓名={} 缺少工号(EMPLOYEE_CODE),无法创建登录账号", name);
skipped++;
continue;
}
if (!batchLoginNames.add(code.toLowerCase()))
{
log.warn("TMS 用户同步跳过:工号 {}({}) 在 TMS 数据中重复", code, name);
skipped++;
continue;
}
if (underRoot)
{
orgFallback++;
}
SysUser u = new SysUser();
u.setDeptId(targetDeptId);
u.setUserName(code);
u.setNickName(name != null ? truncate(name, 30) : code);
u.setPassword(DEFAULT_PASSWORD);
u.setStatus("1".equals(clean(str(row, "BL_OPEN"))) ? "0" : "1");
u.setSex(mapSex(clean(str(row, "SEX"))));
u.setEmail(cleanEmail(str(row, "EMAIL")));
u.setPhonenumber(cleanPhone(str(row, "MOBILE_PHONE")));
u.setRemark(truncate(clean(str(row, "REMARK")), 500));
Object createDate = row.get("CREATE_DATE");
u.setCreateTime(createDate instanceof Date ? (Date) createDate : new Date());
u.setCreateBy(USER_SYNC_MARKER);
toInsert.add(u);
}
orgSyncWriter.insertNewUsers(toInsert);
long cost = System.currentTimeMillis() - t0;
String summary = String.format(
"TMS 用户同步完成:读取 TMS 员工 %d 人,已存在保持不变 %d 人(工号匹配 %d / 姓名+机构匹配 %d),新增 %d 人(其中机构未匹配挂“%s”%d 人),跳过 %d 条,耗时 %dms。新用户默认密码 123456,未分配角色。",
empRows.size(), matchedByCode + matchedByName, matchedByCode, matchedByName,
toInsert.size(), ROOT_DEPT_NAME, orgFallback, skipped, cost);
log.info(summary);
Map<String, Object> result = new LinkedHashMap<String, Object>();
result.put("total", empRows.size());
result.put("existCount", matchedByCode + matchedByName);
result.put("matchedByCode", matchedByCode);
result.put("matchedByName", matchedByName);
result.put("insertCount", toInsert.size());
result.put("orgFallbackCount", orgFallback);
result.put("skipCount", skipped);
result.put("costMs", cost);
result.put("summary", summary);
return result;
}
private static String nameDeptKey(Long deptId, String name)
{
return deptId + "|" + name.trim();
}
private List<SysDept> buildDepts(List<Map<String, Object>> rows) private List<SysDept> buildDepts(List<Map<String, Object>> rows)
{ {
List<SysDept> list = new ArrayList<>(); List<SysDept> list = new ArrayList<>();
@@ -59,4 +59,26 @@ public class OrgSyncWriter
// 6. 超级管理员挂到根部门 // 6. 超级管理员挂到根部门
writerMapper.updateUserDept(1L, adminDeptId); writerMapper.updateUserDept(1L, adminDeptId);
} }
/** TMS 用户增量同步:读取现有全部未删除部门 */
public List<SysDept> readSyncDepts()
{
return writerMapper.selectSyncDepts();
}
/** TMS 用户增量同步:读取现有全部未删除用户(仅匹配字段) */
public List<SysUser> readSyncUsers()
{
return writerMapper.selectSyncUsers();
}
/** TMS 用户增量同步:仅新增用户(user_id 自增,不触碰部门/角色/岗位),单事务 */
@Transactional(rollbackFor = Exception.class)
public void insertNewUsers(List<SysUser> users)
{
for (int i = 0; i < users.size(); i += USER_BATCH)
{
writerMapper.batchInsertUserAutoId(users.subList(i, Math.min(i + USER_BATCH, users.size())));
}
}
} }
@@ -43,4 +43,13 @@ public interface SysOrgSyncWriterMapper
/** 将指定用户挂到根部门 */ /** 将指定用户挂到根部门 */
int updateUserDept(@Param("userId") Long userId, @Param("deptId") Long deptId); int updateUserDept(@Param("userId") Long userId, @Param("deptId") Long deptId);
/** 查询全部未删除部门(TMS 用户增量同步:按部门名称匹配机构) */
List<SysDept> selectSyncDepts();
/** 查询全部未删除用户的匹配字段(TMS 用户增量同步:工号/姓名+机构匹配) */
List<SysUser> selectSyncUsers();
/** 批量新增用户(user_id 自增,不指定 ID,不写角色/岗位) */
int batchInsertUserAutoId(@Param("list") List<SysUser> list);
} }
@@ -4,6 +4,21 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bms.system.orgsync.mapper.SysOrgSyncWriterMapper"> <mapper namespace="com.bms.system.orgsync.mapper.SysOrgSyncWriterMapper">
<!-- 全局未开启下划线转驼峰,TMS 用户增量同步查询使用显式映射 -->
<resultMap id="syncDeptResult" type="com.bms.common.core.domain.entity.SysDept">
<id property="deptId" column="dept_id"/>
<result property="parentId" column="parent_id"/>
<result property="deptName" column="dept_name"/>
<result property="status" column="status"/>
</resultMap>
<resultMap id="syncUserResult" type="com.bms.common.core.domain.entity.SysUser">
<id property="userId" column="user_id"/>
<result property="deptId" column="dept_id"/>
<result property="userName" column="user_name"/>
<result property="nickName" column="nick_name"/>
</resultMap>
<delete id="deleteAllRoleDept"> <delete id="deleteAllRoleDept">
delete from sys_role_dept delete from sys_role_dept
</delete> </delete>
@@ -62,4 +77,28 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<update id="updateUserDept"> <update id="updateUserDept">
update sys_user set dept_id = #{deptId} where user_id = #{userId} update sys_user set dept_id = #{deptId} where user_id = #{userId}
</update> </update>
<select id="selectSyncDepts" resultMap="syncDeptResult">
select dept_id, parent_id, dept_name, status
from sys_dept
where del_flag = '0'
</select>
<select id="selectSyncUsers" resultMap="syncUserResult">
select user_id, dept_id, user_name, nick_name
from sys_user
where del_flag = '0'
</select>
<insert id="batchInsertUserAutoId" parameterType="java.util.List">
insert into sys_user(dept_id, user_name, nick_name, user_type, email, phonenumber, sex,
password, status, del_flag, create_by, create_time, remark)
values
<foreach collection="list" item="u" separator=",">
(#{u.deptId}, #{u.userName}, #{u.nickName}, '00', #{u.email}, #{u.phonenumber},
#{u.sex}, #{u.password}, #{u.status}, '0', #{u.createBy},
<choose><when test="u.createTime != null">#{u.createTime}</when><otherwise>sysdate()</otherwise></choose>,
#{u.remark})
</foreach>
</insert>
</mapper> </mapper>
+5 -5
View File
@@ -28,9 +28,9 @@
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['system:user:export']">导出</el-button> <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['system:user:export']">导出</el-button>
</el-col> </el-col>
<!-- <el-col :span="1.5">--> <el-col :span="1.5">
<!-- <el-button type="primary" plain icon="Refresh" :loading="syncLoading" @click="handleSyncFromOracle" v-hasPermi="['system:user:add']">TMS系统同步用户数据</el-button>--> <el-button type="primary" plain icon="Refresh" :loading="syncLoading" @click="handleSyncFromOracle" v-hasPermi="['system:user:add']">TMS同步用户</el-button>
<!-- </el-col>--> </el-col>
</template> </template>
<template #col-userName="{ row }"> <template #col-userName="{ row }">
@@ -262,10 +262,10 @@ function handleDelete(userIds) {
}).catch(() => {}) }).catch(() => {})
} }
/** 从老系统(Oracle)同步部门/角色/用户 */ /** 从 TMS(老系统 Oracle)增量同步用户:不动组织机构/角色,已有用户不变,仅新增 */
function handleSyncFromOracle() { function handleSyncFromOracle() {
proxy.$modal.confirm( proxy.$modal.confirm(
'将从老系统同步部门(威狮国际及其下级部门)、角色、用户数据。注意:同步为覆盖式迁移,除超级管理员外,系统现有部门、角色、用户将被老系统数据替换(角色权限需另行配置,新用户默认密码 123456)。是否继续?' '将从 TMS 增量同步用户:不改动组织机构和角色,已存在的用户保持不变(优先按工号匹配,其次按姓名+所在机构匹配),仅新增系统中不存在的用户;机构匹配不到的新用户统一挂到“威狮国际”下,初始密码 123456。是否继续?'
).then(() => { ).then(() => {
syncLoading.value = true syncLoading.value = true
return syncUsersFromOracle() return syncUsersFromOracle()