feat: 优化导入问题和单位调整

This commit is contained in:
2026-09-29 16:19:17 +08:00
parent 09832d6346
commit a3c77b4f85
3 changed files with 279 additions and 43 deletions
@@ -4,6 +4,8 @@ import cn.hutool.core.util.StrUtil;
import cn.wizzer.framework.base.Result;
import com.google.common.base.Joiner;
import io.v.nutz.annontation.ViReturn;
import io.v.nutz.base.event.user.UserChangeMsg;
import io.v.nutz.base.event.user.UserChangePublisher;
import io.v.nutz.base.utils.RCSCloudAPI;
import io.v.nutz.base.utils.Vi;
import io.v.nutz.dao.CndPlus;
@@ -27,6 +29,7 @@ import io.v.nutz.sys.models.Sys_unit;
import io.v.nutz.sys.models.Sys_user;
import io.v.nutz.sys.models.User;
import io.v.nutz.sys.services.SysDictService;
import io.v.nutz.sys.services.SysRoleService;
import io.v.nutz.sys.services.SysUnitService;
import io.v.nutz.sys.services.SysUserService;
import io.v.nutz.util.ShiroUtil;
@@ -43,6 +46,7 @@ import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Strings;
import org.nutz.lang.random.R;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
@@ -84,6 +88,9 @@ public class MemberChangeManageController {
@Inject
private SysUnitService sysUnitService;
@Inject
private SysRoleService sysRoleService;
@At
@ViReturn
@RequiresPermissions("member.change.mange")
@@ -126,6 +133,48 @@ public class MemberChangeManageController {
.setParam("success", MemberChangeApplyState.SUCCESS)
.setVar("failStates", Joiner.on(",").join(List.of(MemberChangeApplyState.UNION_FAIL, MemberChangeApplyState.SCHOOL_UNION_FAIL)));
sql.setCondition(memberSelectionCondition(unionId, unitId, threeUnitId, unionGroupId,
changeDateBefore, changeDateEnd, pageForm, campus, sex, personType, userState,
changeType, changed, isMember));
return memberService.list(pageForm, sql);
}
@At
@ViReturn
@RequiresPermissions("member.change.mange")
public Object selectAllUsers(@Param(value = "unionId", required = false) String unionId,
@Param(value = "unitId", required = false) String unitId,
@Param(value = "threeUnitId", required = false) String threeUnitId,
@Param(value = "unionGroupId", required = false) String unionGroupId,
@Param(value = "changeDateBefore", required = false) String changeDateBefore,
@Param(value = "changeDateEnd", required = false) String changeDateEnd,
PageForm pageForm,
@Param(value = "campus", required = false) String campus,
@Param(value = "sex", required = false) String sex,
@Param(value = "personType", required = false) String personType,
@Param(value = "userState", required = false) String userState,
@Param(value = "changeType", required = false) String changeType,
@Param(value = "changed", required = false) Integer changed,
@Param(value = "isMember", required = false) Integer isMember) {
Sql sql = Sqls.create("""
SELECT u.id, u.loginname, u.username, u.unitname, u.unionname,
u.threeUnitName, u.campusId
FROM `user` u
LEFT JOIN user_history_latest his ON his.loginname = u.loginname
LEFT JOIN member_change_record_latest mcr ON mcr.userId = u.id
LEFT JOIN audit_state state ON mcr.stateId = state.stateId
$condition
""");
sql.setCondition(memberSelectionCondition(unionId, unitId, threeUnitId, unionGroupId,
changeDateBefore, changeDateEnd, pageForm, campus, sex, personType, userState,
changeType, changed, isMember));
return memberService.listMap(sql);
}
private CndPlus memberSelectionCondition(String unionId, String unitId, String threeUnitId,
String unionGroupId, String changeDateBefore, String changeDateEnd,
PageForm pageForm, String campus, String sex, String personType,
String userState, String changeType, Integer changed, Integer isMember) {
CndPlus cnd = CndPlus.create();
SqlExpressionGroup group = new SqlExpressionGroup();
@@ -181,8 +230,7 @@ public class MemberChangeManageController {
}
}
cnd.desc("his.changeTime");
sql.setCondition(cnd);
return memberService.list(pageForm, sql);
return cnd;
}
@At
@@ -424,35 +472,75 @@ public class MemberChangeManageController {
@RequiresPermissions("member.change.mange")
public Object userUnitMove(String threeUnitId, String unitId, @Param("userId[]") String[] userId) {
if (userId == null || userId.length == 0) {
return Result.error("请选择需要调整的用户");
}
Sys_unit unit = memberService.dao().fetch(Sys_unit.class, unitId);
if (unit == null) {
return Result.error("所选单位不存在");
}
Trans.exec(() -> {
Chain updateUnitChain = Chain.make("unitid", unit.getId())
.add("unionid", unit.getUnionid())
.add("threeUnitId", threeUnitId);
memberService.update("sys_user", updateUnitChain, Cnd.where("id", "in", userId));
List<Sys_user> users = dao.query(Sys_user.class, Cnd.where("id", "in", userId));
if (users.size() != new HashSet<>(Arrays.asList(userId)).size()) {
throw new IllegalArgumentException("部分用户不存在,请重新选择");
}
List<Audit> audits = new ArrayList<>();
List<MemberChangeRecord> records = new ArrayList<>();
List<UserHistory> histories = new ArrayList<>();
Map<String, List<String>> usersByOriginUnit = new LinkedHashMap<>();
Date applyTime = new Date();
for (Sys_user user : users) {
UserHistory history = new UserHistory();
BeanUtils.copyProperties(user, history);
history.setId(R.UU32());
history.setChangeTime(applyTime);
history.setChangeType(UserChangeType.UNIT_CHANGE.getCode());
histories.add(history);
for (String uid : userId) {
MemberChangeRecord record = new MemberChangeRecord();
record.setRecordMode(MemberChangeRecordMode.MEMBER.getCode());
record.setType(UserChangeType.UNIT_CHANGE.getCode());
record.setUserId(uid);
record.setApplyTime(new Date());
record.setUserId(user.getId());
record.setOriginUnitId(user.getUnitid());
record.setOriginUnionId(user.getUnionid());
record.setOriginThreeUnitId(user.getThreeUnitId());
record.setCurrentUnitId(unit.getId());
record.setCurrentUnionId(unit.getUnionid());
record.setCurrentThreeUnitId(threeUnitId);
record.setApplyTime(applyTime);
record.setApplyOrigin(MemberChangeOrigin.SCHOOL.getCode());
record.setRecordMode(MemberChangeRecordMode.MEMBER.getCode());
record.setStateId(MemberChangeApplyState.SUCCESS);
Audit audit = new Audit();
audit.setAuditPass(true);
audit.setAuditOpinion(MemberOrigin.BATCH.getDescription());
String id = auditService.insert(audit).getId();
record.setSchoolAuditId(id);
memberService.insert(record);
audits.add(audit);
records.add(record);
usersByOriginUnit.computeIfAbsent(user.getUnitid(), key -> new ArrayList<>()).add(user.getId());
}
dao.insert(audits);
for (int i = 0; i < records.size(); i++) {
records.get(i).setSchoolAuditId(audits.get(i).getId());
}
dao.insert(records);
dao.fastInsert(histories);
usersByOriginUnit.forEach((originUnitId, ids) ->
UserChangePublisher.broadcast(new UserChangeMsg(ids,
UserChangeMsg.UNIT_CHANGE_OPERATION, originUnitId, unit.getId())));
Chain updateUnitChain = Chain.make("unitid", unit.getId())
.add("unionid", unit.getUnionid())
.add("threeUnitId", threeUnitId);
memberService.update("sys_user", updateUnitChain, Cnd.where("id", "in", userId));
});
userService.clearCache();
sysRoleService.clearCache();
return null;
}
@@ -48,6 +48,9 @@ import org.springframework.util.CollectionUtils;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -310,13 +313,51 @@ public class MemberGroupInfoController {
List<Sys_user> userList = new ArrayList<>();
List<MemberImportTemp> errorInfos = new ArrayList<>();
List<NutMap> errorInfos = new ArrayList<>();
memberImportList.forEach(v -> {
Sys_user user = sysUserService.fetch(Cnd.where("loginname", "=", v.getLoginName()));
user = user == null ? new Sys_user() : user;
Sys_unit unit = unitMap.get(v.getUnitName());
List<String> errors = new ArrayList<>();
if (!isfg && user.getId() != null) {
errors.add("一卡通号已存在,仅追加模式不能重复导入");
}
if (unit == null) {
errorInfos.add(v);
errors.add("请核对该人员的单位信息");
}
String schoolTime = StrUtil.trim(v.getSchoolTime());
LocalDate schoolDate = null;
if (StrUtil.isNotBlank(schoolTime)) {
if (!schoolTime.matches("[0-9]{8}|[0-9]{4}-[0-9]{2}-[0-9]{2}")) {
errors.add("入校时间【" + schoolTime + "】格式错误,请填写yyyy-MM-dd或yyyyMMdd");
} else {
try {
schoolDate = LocalDate.parse(schoolTime, schoolTime.length() == 8
? DateTimeFormatter.BASIC_ISO_DATE : DateTimeFormatter.ISO_LOCAL_DATE);
} catch (DateTimeParseException e) {
errors.add("入校时间【" + schoolTime + "】不是有效日期,请核对年月日");
}
}
}
String memberJoinTime = StrUtil.trim(v.getMemberJoinTime());
LocalDate memberJoinDate = null;
if (StrUtil.isNotBlank(memberJoinTime)) {
if (!memberJoinTime.matches("[0-9]{8}|[0-9]{4}-[0-9]{2}-[0-9]{2}")) {
errors.add("入会时间【" + memberJoinTime + "】格式错误,请填写yyyy-MM-dd或yyyyMMdd");
} else {
try {
memberJoinDate = LocalDate.parse(memberJoinTime, memberJoinTime.length() == 8
? DateTimeFormatter.BASIC_ISO_DATE : DateTimeFormatter.ISO_LOCAL_DATE);
} catch (DateTimeParseException e) {
errors.add("入会时间【" + memberJoinTime + "】不是有效日期,请核对年月日");
}
}
}
if (!errors.isEmpty()) {
errorInfos.add(NutMap.NEW().addv("工号", v.getLoginName()).addv("姓名", v.getUserName())
.addv("性别", v.getSex()).addv("所属工会", v.getUnionName()).addv("所属单位", v.getUnitName())
.addv("入校时间", v.getSchoolTime()).addv("入会时间", v.getMemberJoinTime())
.addv("错误原因", String.join(";", errors)));
} else {
user.setId(user.getId() == null ? R.UU32() : user.getId());
user.setLoginname(v.getLoginName());
@@ -330,8 +371,8 @@ public class MemberGroupInfoController {
user.setPolitical(v.getPolitical());
user.setEducation(v.getEducation());
user.setJobTitle(v.getJobTitle());
user.setSchoolTime(v.getSchoolTime());
user.setMemberJoinTime(v.getMemberJoinTime() != null ? DateUtil.parseDate(v.getMemberJoinTime()) : null);
user.setSchoolTime(schoolDate == null ? null : schoolDate.toString());
user.setMemberJoinTime(memberJoinDate == null ? null : DateUtil.parseDate(memberJoinDate.toString()));
user.setPersonType(v.getPersonType());
user.setPreparedBy(v.getPreparedBy());
user.setRemarks(v.getRemarks());
@@ -350,16 +391,12 @@ public class MemberGroupInfoController {
}
}
String errMsg = "请核对该人员的单位信息";
if (!CollectionUtils.isEmpty(errorInfos)) {
NutMap nutMap = NutMap.NEW();
nutMap.setv("totalCount", memberImportList.size());
nutMap.setv("successCount", successUserList.size());
nutMap.setv("errorCount", errorInfos.size());
nutMap.setv("errorList", errorInfos.stream().map(v -> {
return NutMap.NEW().addv("工号", v.getLoginName()).addv("姓名", v.getUserName()).addv("性别", v.getSex()).addv("所属工会", v.getUnionName()).addv("所属单位", v.getUnitName()).addv("错误原因", errMsg);
}).collect(Collectors.toList()));
nutMap.setv("errorList", errorInfos);
return Result.success(nutMap);
}
return Result.success();
@@ -2,10 +2,42 @@
layout("/layouts/platform.html"){
#-->
<style>
.select-all-users.is-bordered.is-checked {
border-color: #dcdfe6;
}
.select-all-users .el-checkbox__input.is-checked + .el-checkbox__label {
color: inherit;
}
.unit-move-drawer .el-drawer__body {
display: flex;
flex-direction: column;
overflow: hidden;
min-height: 0;
}
.unit-move-drawer .el-drawer__body > .el-row {
flex-shrink: 0;
}
.unit-move-drawer .unit-move-fields {
flex-wrap: wrap;
}
.unit-move-drawer .unit-move-fields > .el-col {
margin-bottom: 10px;
}
.unit-move-drawer .el-drawer__body > .unit-move-list {
flex: 1;
min-height: 0;
padding: 0 20px;
}
.unit-move-drawer .unit-move-footer {
flex-shrink: 0;
padding: 20px;
text-align: right;
}
</style>
<div id="app" v-cloak>
<div id="app" v-cloak v-loading.fullscreen.lock="selectAllLoading"
element-loading-text="正在全选,请稍候"
:inert="selectAllLoading ? '' : null" :aria-busy="selectAllLoading ? 'true' : 'false'">
<guava ref="guava">
<template>
<el-card shadow="never">
@@ -248,13 +280,13 @@ layout("/layouts/platform.html"){
</el-dropdown-menu>
</el-dropdown>
<el-button :disabled="!checkUsers.length" type="primary" size="small"
<el-button :disabled="selectAllLoading || !checkUsers.length" type="primary" size="small"
icon="el-icon-edit"
@click="openBatchEdit" class="ml5">
设置校区
</el-button>
<el-button :disabled="!checkUsers.length" type="primary" size="small"
<el-button :disabled="selectAllLoading || !checkUsers.length" type="primary" size="small"
icon="el-icon-edit"
@click="openUnitMove" class="ml5">
单位调整
@@ -263,6 +295,16 @@ layout("/layouts/platform.html"){
</template>
</table-tool>
<div style="display: flex; align-items: center; margin-bottom: 10px;">
<el-checkbox :value="allUsersSelected" :indeterminate="someUsersSelected"
:disabled="tableLoading || selectAllLoading" size="small" border
@change="changeAllUsersSelection" class="select-all-users">
<i v-if="selectAllLoading" class="el-icon-loading"></i>
全选({{allUsersSelected ? '全部选中' : (someUsersSelected ? '部分选中' : '未选择')}})
</el-checkbox>
<span style="margin-left: 10px;">已选中 {{checkUsers.length}} 人</span>
</div>
<el-table ref="table" :data="tableData" style="width: 100%" row-key="id"
@sort-change="pageOrder"
v-loading="tableLoading" :size="tableSize" class="vi-table"
@@ -463,12 +505,12 @@ layout("/layouts/platform.html"){
<el-drawer
title="单位调整"
:visible.sync="unitMoveVisibile"
:before-close="handleClose" size="70%">
:before-close="handleClose" size="70%" custom-class="unit-move-drawer">
<el-row class="text-primary p20">
选择新单位
</el-row>
<el-row type="flex" :gutter="20" class="pl20 pr20">
<el-row type="flex" :gutter="20" class="pl20 pr20 unit-move-fields">
<el-col :span="12">
<el-select placeholder="请选择新工会" v-model="unitMoveFormData.unionId"
style="width: 100%;"
@@ -510,10 +552,10 @@ layout("/layouts/platform.html"){
</el-col>
</el-row>
<el-row class="mt10">
<el-table :data="checkUsers" style="width: 100%;padding: 20px" class="vi-table"
<el-row class="mt10 unit-move-list">
<el-table :data="checkUsers" style="width: 100%" class="vi-table"
border
height="70vh">
height="100%">
<el-table-column show-overflow-tooltip align="center" header-align="center"
prop="loginname"
width="120"
@@ -552,10 +594,12 @@ layout("/layouts/platform.html"){
</el-table>
</el-row>
<span style="position: absolute;bottom: 20px;right: 20px">
<div class="unit-move-footer">
<el-button @click="unitMoveVisibile = false">取 消</el-button>
<el-button type="primary" @click="doUnitMove">确定</el-button>
</span>
<el-button type="primary" :loading="unitMoveLoading" :disabled="unitMoveLoading" @click="doUnitMove">
{{unitMoveLoading ? '迁移中...' : '确定'}}
</el-button>
</div>
</el-drawer>
@@ -612,11 +656,13 @@ layout("/layouts/platform.html"){
}]
},
checkUsers: [],
selectAllLoading: false,
batchEditVisible: false,
batchEditLoading: false,
batchFormData: {},
unitMoveVisibile: false,
unitMoveLoading: false,
unitMoveUnions: [],
unitMoveUnits: [],
unitMoveFormData: {
@@ -670,6 +716,25 @@ layout("/layouts/platform.html"){
selectUserLoading: false,
}
},
computed: {
allUsersSelected() {
return this.pageForm.totalCount > 0 && this.checkUsers.length === this.pageForm.totalCount
},
someUsersSelected() {
return this.checkUsers.length > 0 && !this.allUsersSelected
},
selectionFilters() {
const {pageNumber, pageSize, totalCount, pageOrderName, pageOrderBy, ...filters} = this.pageForm
return JSON.stringify(filters)
}
},
watch: {
selectionFilters() {
if (this.$refs.table) {
this.$refs.table.clearSelection()
}
}
},
components: {
'guava': httpVueLoader('/components/plugins/Guava.vue'),
'member': httpVueLoader('/components/member/MemberInfo.vue?v=1.0.3'),
@@ -772,6 +837,9 @@ layout("/layouts/platform.html"){
}
},
async doUnitMove() {
if (this.unitMoveLoading) {
return
}
if (this.unitMoveFormData.unitId === '') {
this.notifyWarning('请选择新单位!')
return
@@ -787,14 +855,22 @@ layout("/layouts/platform.html"){
userId: this.checkUsers.map(v => v.id)
}
const resp = await $.post(loc() + '/userUnitMove', params)
if (resp.code === 0) {
this.pageData()
this.getNumData()
this.$refs.table.clearSelection();
this.unitMoveVisibile = false
} else {
this.notifyWarning(resp.msg)
this.unitMoveLoading = true
try {
const resp = await $.post(loc() + '/userUnitMove', params)
if (resp.code === 0) {
this.pageData()
this.getNumData()
this.$refs.table.clearSelection();
this.unitMoveVisibile = false
this.notifySuccess('迁移完成')
} else {
this.notifyWarning(resp.msg)
}
} catch (e) {
this.notifyWarning('请求异常,无法确认迁移结果')
} finally {
this.unitMoveLoading = false
}
},
changeDateRangeChange(val) {
@@ -834,6 +910,41 @@ layout("/layouts/platform.html"){
this.batchFormData = {}
this.batchEditVisible = true
},
changeAllUsersSelection(checked) {
if (checked) {
this.selectAllUsers()
} else {
this.$refs.table.clearSelection()
}
},
async selectAllUsers() {
if (this.selectAllLoading) {
return
}
this.selectAllLoading = true
const snapshot = this.selectionFilters
try {
const resp = await $.post(loc() + '/selectAllUsers', JSON.parse(snapshot))
if (resp.code !== 0) {
this.notifyWarning(resp.msg || '全部勾选失败')
return
}
if (snapshot !== this.selectionFilters) {
this.notifyWarning('查询条件已变化,请重新全选')
return
}
const currentRows = new Map(this.tableData.map(v => [v.id, v]))
const selectedUsers = new Map(resp.data.map(v => [v.id, currentRows.get(v.id) || v]))
this.pageForm.totalCount = selectedUsers.size
this.$refs.table.clearSelection()
selectedUsers.forEach(v => this.$refs.table.toggleRowSelection(v, true))
this.notifySuccess('已勾选 ' + selectedUsers.size + ' 人')
} catch (e) {
this.notifyWarning('全部勾选失败,请稍后重试')
} finally {
this.selectAllLoading = false
}
},
tableHandleSelectionChange(val) {
this.checkUsers = val;
},