修改TMS用户同步只增量同步用户数据
This commit is contained in:
+5
-4
@@ -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())));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+9
@@ -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>
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
Reference in New Issue
Block a user