This commit is contained in:
zhouhefeng
2026-09-29 10:06:50 +08:00
parent d1f38603b4
commit 8c431ff665
8 changed files with 217 additions and 32 deletions
@@ -17,6 +17,8 @@ public class AidFundPageForm extends PageForm {
private String unitId;
//是否当年未缴费
private Boolean notPayedCurrentYear;
//是否筛选人员类型与基金会员类型不一致的基金会员
private Boolean personTypeChanged;
//基金会员变更类型
private String changeType;
@@ -22,6 +22,7 @@ import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.dao.util.cri.Static;
import org.nutz.dao.util.cri.SqlExpressionGroup;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.IocBean;
@@ -167,6 +168,17 @@ public class AidFundMemberServiceImpl extends BaseServiceImpl implements AidFund
if (pageForm.getNotPayedCurrentYear()) {
cnd.and("id", "in", Sqls.create("SELECT userid FROM `aid_fund_member_pay` where `year` = YEAR(CURDATE()) and isPayed = 0"));
}
if (ObjectUtil.equals(pageForm.getPersonTypeChanged(), true)) {
// 按工号比对当前用户表人员类型与基金会员类型,筛出需要人工核实变更的会员。
cnd.and(new Static("""
EXISTS (
SELECT 1
FROM sys_user su
WHERE su.loginname = vw_user.loginname
AND IFNULL(su.personType, '') <> IFNULL(vw_user.aidFundMemberUserType, '')
)
"""));
}
cnd.andEX(Sys_user::getAidFundMemberUserType, "=", pageForm.getAidFundMemberUserType());
cnd.andEX(View_user::getUnionId, "=", pageForm.getUnionId());
cnd.andEX(View_user::getUnitId, "=", pageForm.getUnitId());
@@ -17,4 +17,24 @@ public class PendingConfirmUserBatchChangeParam {
* 批量设置的人员类型。
*/
private String personType;
/**
* 批量设置的在职状态,空值不参与批量变更。
*/
private String userState;
/**
* 批量设置的人员属性,空值不参与批量变更。
*/
private String userAttribute;
/**
* 批量设置的会员状态,null不参与批量变更,false表示设置为非会员。
*/
private Boolean member;
/**
* 批量设置的福利会员状态,null不参与批量变更,false表示设置为非福利会员。
*/
private Boolean welfareMember;
}
@@ -29,9 +29,9 @@ public interface PendingConfirmUserService extends BaseService<Sys_user> {
boolean submitChange(MemberChangeRecord record);
/**
* 批量修改待确认人员的人员类型,并将处理成功的人员标记为已确认。
* 批量修改待确认人员允许整改的字段,并将处理成功的人员标记为已确认。
*
* @param changeParam 批量勾选的用户ID及目标人员类型
* @param changeParam 批量勾选的用户ID及待整改字段
* @return 实际完成确认的人员数量
*/
int batchChangePersonType(PendingConfirmUserBatchChangeParam changeParam);
@@ -71,6 +71,7 @@ public class PendingConfirmUserServiceImpl extends BaseServiceImpl<Sys_user> imp
userInfo.sex,
userInfo.userState,
userInfo.personType,
userInfo.userAttribute,
userInfo.unitId,
unitInfo.name AS unitName,
unionInfo.id unionId,
@@ -123,14 +124,14 @@ public class PendingConfirmUserServiceImpl extends BaseServiceImpl<Sys_user> imp
throw new BaseException("请选择人员类型");
}
validatePersonType(record.getPersonType());
// 单人页面虽然提交完整表单,服务层只采用用户ID和人员类型,防止请求篡改其他只读字段。
return changePersonType(record.getUserId(), record.getPersonType());
// 单人页面虽然提交完整表单,服务层只采纳待确认业务允许编辑的字段,防止请求篡改其他只读字段。
return changePendingUserFields(record);
}
/**
* 在同一事务内批量处理选中人员;任意人员状态异常时整体回滚,避免出现部分成功。
*
* @param changeParam 批量用户ID及目标人员类型
* @param changeParam 批量用户ID及待确认字段变更内容
* @return 完成确认的人员数量
*/
@Override
@@ -139,10 +140,12 @@ public class PendingConfirmUserServiceImpl extends BaseServiceImpl<Sys_user> imp
if (changeParam == null || StrUtil.isBlank(changeParam.getUserIds())) {
throw new BaseException("请选择需要变更的人员");
}
if (StrUtil.isBlank(changeParam.getPersonType())) {
throw new BaseException("请选择人员类型");
if (!hasBatchChangeField(changeParam)) {
throw new BaseException("请选择需要变更的内容");
}
if (StrUtil.isNotBlank(changeParam.getPersonType())) {
validatePersonType(changeParam.getPersonType());
}
validatePersonType(changeParam.getPersonType());
List<String> userIds;
try {
@@ -162,11 +165,42 @@ public class PendingConfirmUserServiceImpl extends BaseServiceImpl<Sys_user> imp
}
for (String userId : new ArrayList<>(normalizedUserIds)) {
changePersonType(userId, changeParam.getPersonType());
MemberChangeRecord record = new MemberChangeRecord();
record.setUserId(userId);
if (StrUtil.isNotBlank(changeParam.getPersonType())) {
record.setPersonType(changeParam.getPersonType());
}
if (StrUtil.isNotBlank(changeParam.getUserState())) {
record.setUserState(changeParam.getUserState());
}
if (StrUtil.isNotBlank(changeParam.getUserAttribute())) {
record.setUserAttribute(changeParam.getUserAttribute());
}
if (changeParam.getMember() != null) {
record.setMember(changeParam.getMember());
}
if (changeParam.getWelfareMember() != null) {
record.setWelfareMember(changeParam.getWelfareMember());
}
changePendingUserFields(record);
}
return normalizedUserIds.size();
}
/**
* 判断批量请求是否至少包含一个待整改字段;字符串空值不参与,Boolean false 是有效值。
*
* @param changeParam 批量变更参数
* @return true 表示存在至少一个待变更字段
*/
private boolean hasBatchChangeField(PendingConfirmUserBatchChangeParam changeParam) {
return StrUtil.isNotBlank(changeParam.getPersonType())
|| StrUtil.isNotBlank(changeParam.getUserState())
|| StrUtil.isNotBlank(changeParam.getUserAttribute())
|| changeParam.getMember() != null
|| changeParam.getWelfareMember() != null;
}
/**
* 仅修改指定待确认人员的人员类型,并复用会员公共服务生成用户历史记录。
*
@@ -175,6 +209,20 @@ public class PendingConfirmUserServiceImpl extends BaseServiceImpl<Sys_user> imp
* @return true 表示人员类型发生变化,false 表示类型未变化但已完成确认
*/
private boolean changePersonType(String userId, String personType) {
MemberChangeRecord record = new MemberChangeRecord();
record.setUserId(userId);
record.setPersonType(personType);
return changePendingUserFields(record);
}
/**
* 仅采纳待确认页面允许编辑的人员字段,并复用会员公共服务生成用户历史记录。
*
* @param record 前端提交的待确认人员变更内容
* @return true 表示人员字段发生变化,false 表示字段未变化但已完成确认
*/
private boolean changePendingUserFields(MemberChangeRecord record) {
String userId = record.getUserId();
Sys_user user = dao().fetch(Sys_user.class, userId);
if (user == null || !Integer.valueOf(STATUS_PENDING).equals(user.getPendingConfirmStatus())) {
throw new BaseException("当前人员不属于待确认人员记录");
@@ -185,13 +233,15 @@ public class PendingConfirmUserServiceImpl extends BaseServiceImpl<Sys_user> imp
if (userView == null) {
throw new BaseException("未获取到人员基础信息");
}
// 从数据库当前值重建完整变更对象,除人员类型外不信任前端提交的任何人员字段。
// 从数据库当前值重建完整变更对象,仅覆盖待确认页面允许编辑的字段。
MemberChangeRecord safeRecord = new MemberChangeRecord();
BeanUtil.copyProperties(userView, safeRecord);
safeRecord.setUserId(userId);
safeRecord.setPersonType(StrUtil.trim(personType));
safeRecord.setMember(Integer.valueOf(1).equals(userView.getMember()));
safeRecord.setWelfareMember(Integer.valueOf(1).equals(userView.getWelfareMember()));
safeRecord.setPersonType(record.getPersonType() == null ? userView.getPersonType() : StrUtil.trim(record.getPersonType()));
safeRecord.setUserState(record.getUserState() == null ? userView.getUserState() : record.getUserState());
safeRecord.setUserAttribute(record.getUserAttribute() == null ? userView.getUserAttribute() : record.getUserAttribute());
safeRecord.setMember(record.getMember() == null ? Integer.valueOf(1).equals(userView.getMember()) : record.getMember());
safeRecord.setWelfareMember(record.getWelfareMember() == null ? Integer.valueOf(1).equals(userView.getWelfareMember()) : record.getWelfareMember());
safeRecord.setArrivalAtSchoolDate(userView.getArrivalAtSchoolDate() == null
? null : DateUtil.formatDate(userView.getArrivalAtSchoolDate()));
safeRecord.setWelfareStopDate(userView.getWelfareStopDate() == null
@@ -201,8 +251,12 @@ public class PendingConfirmUserServiceImpl extends BaseServiceImpl<Sys_user> imp
// 复用公共校验和历史记录生成口径,确保单人、批量变更结果保持一致。
memberCommonService.validateChangeAndSetBasicData(safeRecord, MemberChangeOrigin.SYSTEM);
} catch (BaseException e) {
// 人员类型与当前值相同时允许直接完成确认;其他业务异常必须继续抛出并触发事务回滚。
// 公共允许变更字段暂不包含福利会员状态,单独修改该字段时由待确认入口专用逻辑直接更新。
if ("未获取到异动数据".equals(e.getMessage())) {
if (updateWelfareMemberIfChanged(record, userView)) {
markConfirmed(userId);
return true;
}
markConfirmed(userId);
return false;
}
@@ -214,6 +268,34 @@ public class PendingConfirmUserServiceImpl extends BaseServiceImpl<Sys_user> imp
return true;
}
/**
* 仅在本次请求明确传入福利会员状态且与当前值不一致时更新;未传入时不参与整改。
*
* @param record 待确认页面提交的变更内容
* @param userView 当前人员视图数据
* @return true 表示已更新福利会员状态
*/
private boolean updateWelfareMemberIfChanged(MemberChangeRecord record, View_user userView) {
if (record.getWelfareMember() == null) {
return false;
}
boolean currentWelfareMember = Integer.valueOf(1).equals(userView.getWelfareMember());
if (record.getWelfareMember().equals(currentWelfareMember)) {
return false;
}
Sql sql = Sqls.create("""
UPDATE sys_user
SET welfareMember = @welfareMember
WHERE id = @userId
AND pendingConfirmStatus = @pendingStatus
""");
sql.setParam("welfareMember", record.getWelfareMember());
sql.setParam("userId", userView.getId());
sql.setParam("pendingStatus", STATUS_PENDING);
dao().execute(sql);
return true;
}
/**
* 校验待确认人员允许变更的目标人员类型。
*
@@ -265,6 +347,7 @@ public class PendingConfirmUserServiceImpl extends BaseServiceImpl<Sys_user> imp
"unionName", "unionInfo.name",
"unitName", "unitInfo.name",
"personType", "userInfo.personType",
"userAttribute", "userInfo.userAttribute",
"userState", "userInfo.userState"
);
return orderColumns.getOrDefault(StrUtil.nullToEmpty(pageOrderName), "");
@@ -43,10 +43,15 @@ layout("/layouts/platform.html"){
v-if="$auth.hasPermission('medicalMutualAid.aidFund.manage.createPayList')" class="mr10">
当年未缴费【基金会员】名单
</el-tag>
<el-tag :effect="pageForm.personTypeChanged?'dark':'plain'"
@click="pageForm.personTypeChanged=!pageForm.personTypeChanged;doSearch();"
v-if="$auth.hasPermission('medicalMutualAid.aidFund.manage.createPayList')" class="mr10">
人员类型变化名单
</el-tag>
<el-button @click="batchExit" size="small"
type="danger" v-if="pageForm.notPayedCurrentYear"
v-if="$auth.hasPermission('medicalMutualAid.aidFund.manage.createPayList')">
type="danger"
v-if="$auth.hasPermission('medicalMutualAid.aidFund.manage.createPayList') && (pageForm.notPayedCurrentYear || pageForm.personTypeChanged)">
批量设置为退会
</el-button>
@@ -258,7 +263,8 @@ layout("/layouts/platform.html"){
pageForm: {
unitId: '',
unionId: '',
notPayedCurrentYear: false
notPayedCurrentYear: false,
personTypeChanged: false
},
tableColumns: [
{prop: "loginname", label: "工号"},
@@ -153,6 +153,8 @@ layout("/layouts/platform.html"){
prop="unitName" show-overflow-tooltip sortable></el-table-column>
<el-table-column align="center" header-align="center" label="人员类型" min-width="120"
prop="personType" show-overflow-tooltip sortable></el-table-column>
<el-table-column align="center" header-align="center" label="人员属性" min-width="120"
prop="userAttribute" show-overflow-tooltip sortable></el-table-column>
<el-table-column align="center" header-align="center" label="在职状态" min-width="100"
prop="userState" show-overflow-tooltip sortable></el-table-column>
<el-table-column align="center" header-align="center" label="确认状态" min-width="100"
@@ -184,18 +186,40 @@ layout("/layouts/platform.html"){
</template>
</guava>
<el-dialog title="批量变更人员类型" :visible.sync="batchDialogVisible"
<el-dialog title="批量变更" :visible.sync="batchDialogVisible"
append-to-body width="480px" custom-class="pending-batch-change-dialog"
@closed="resetBatchChange">
<el-form :model="batchChangeForm" :rules="batchChangeRules"
ref="batchChangeFormRef" label-width="100px">
<el-form-item label="人员类型" prop="personType">
<el-select v-model="batchChangeForm.personType" filterable
<el-form-item label="人员类型">
<el-select v-model="batchChangeForm.personType" clearable filterable
placeholder="请选择人员类型" style="width: 100%">
<el-option v-for="item in pendingPersonTypeOptions" :key="item.code"
:label="item.name" :value="item.code"></el-option>
</el-select>
</el-form-item>
<el-form-item label="在职状态">
<dict-select v-model="batchChangeForm.userState" clearable code="USER_STATE"
placeholder="请选择在职状态" style="width: 100%"></dict-select>
</el-form-item>
<el-form-item label="人员属性">
<dict-select v-model="batchChangeForm.userAttribute" clearable code="USER_ATTRIBUTE"
placeholder="请选择人员属性" style="width: 100%"></dict-select>
</el-form-item>
<el-form-item label="会员状态">
<el-select v-model="batchChangeForm.member" clearable
placeholder="请选择会员状态" style="width: 100%">
<el-option label="会员" :value="true"></el-option>
<el-option label="非会员" :value="false"></el-option>
</el-select>
</el-form-item>
<el-form-item label="福利会员状态">
<el-select v-model="batchChangeForm.welfareMember" clearable
placeholder="请选择福利会员状态" style="width: 100%">
<el-option label="福利会员" :value="true"></el-option>
<el-option label="非福利会员" :value="false"></el-option>
</el-select>
</el-form-item>
</el-form>
<template slot="footer">
<el-button @click="batchDialogVisible = false">取消</el-button>
@@ -228,11 +252,13 @@ layout("/layouts/platform.html"){
batchDialogVisible: false,
batchSubmitting: false,
batchChangeForm: {
personType: ""
},
batchChangeRules: {
personType: [{required: true, message: "请选择人员类型", trigger: "change"}]
personType: "",
userState: "",
userAttribute: "",
member: null,
welfareMember: null
},
batchChangeRules: {},
pendingTableHeight: 300,
pendingTableResizeObserver: null
}
@@ -306,11 +332,23 @@ layout("/layouts/platform.html"){
this.$message.warning("请先勾选需要变更的人员")
return
}
this.batchChangeForm.personType = ""
this.resetBatchChangeForm()
this.batchDialogVisible = true
},
/** 判断批量表单是否至少填写一个需要变更的字段,布尔 false 也是有效值。 */
hasBatchChangeField() {
return !!this.batchChangeForm.personType
|| !!this.batchChangeForm.userState
|| !!this.batchChangeForm.userAttribute
|| this.batchChangeForm.member !== null && this.batchChangeForm.member !== undefined && this.batchChangeForm.member !== ""
|| this.batchChangeForm.welfareMember !== null && this.batchChangeForm.welfareMember !== undefined && this.batchChangeForm.welfareMember !== ""
},
/** 二次确认批量操作,防止误改多名人员。 */
confirmBatchChange() {
if (!this.hasBatchChangeField()) {
this.$message.warning("请至少选择一个需要批量变更的字段")
return
}
this.$refs.batchChangeFormRef.validate((valid) => {
if (!valid) {
return
@@ -326,11 +364,26 @@ layout("/layouts/platform.html"){
/** 调用批量接口,在一个事务中修改人员类型并完成确认。 */
submitBatchChange() {
const userIds = this.selectedPendingUsers.map((item) => item.id)
const params = {
userIds: JSON.stringify(userIds)
}
if (this.batchChangeForm.personType) {
params.personType = this.batchChangeForm.personType
}
if (this.batchChangeForm.userState) {
params.userState = this.batchChangeForm.userState
}
if (this.batchChangeForm.userAttribute) {
params.userAttribute = this.batchChangeForm.userAttribute
}
if (this.batchChangeForm.member !== null && this.batchChangeForm.member !== undefined && this.batchChangeForm.member !== "") {
params.member = this.batchChangeForm.member
}
if (this.batchChangeForm.welfareMember !== null && this.batchChangeForm.welfareMember !== undefined && this.batchChangeForm.welfareMember !== "") {
params.welfareMember = this.batchChangeForm.welfareMember
}
this.batchSubmitting = true
this.$axios.post("/platform/sourcechange/pending/batchChange", {
userIds: JSON.stringify(userIds),
personType: this.batchChangeForm.personType
}).then((resp) => {
this.$axios.post("/platform/sourcechange/pending/batchChange", params).then((resp) => {
if (resp.code === 0) {
this.$message.success(resp.msg)
this.batchDialogVisible = false
@@ -344,10 +397,18 @@ layout("/layouts/platform.html"){
this.batchSubmitting = false
})
},
/** 重置批量表单,空值字段不会参与批量整改。 */
resetBatchChangeForm() {
this.batchChangeForm.personType = ""
this.batchChangeForm.userState = ""
this.batchChangeForm.userAttribute = ""
this.batchChangeForm.member = null
this.batchChangeForm.welfareMember = null
},
/** 关闭弹窗后清理表单,避免保留上次选择的人员类型。 */
resetBatchChange() {
this.batchSubmitting = false
this.batchChangeForm.personType = ""
this.resetBatchChangeForm()
if (this.$refs.batchChangeFormRef) {
this.$refs.batchChangeFormRef.clearValidate()
}
@@ -21,9 +21,10 @@ const PENDING_MEMBER_CHANGE = {
}
},
methods: {
/** 编辑模式仅允许修改人员类型;查看模式下所有字段均为只读。 */
/** 编辑模式仅允许修改待确认业务确认所需字段;查看模式下所有字段均为只读。 */
pendingAllowFields(prop) {
return this.pendingChangeReadonly || prop !== "personType"
const editableFields = ["personType", "userState", "userAttribute", "member", "welfareMember"]
return this.pendingChangeReadonly || !editableFields.includes(prop)
},
/** 从人员类型字典中仅保留待确认业务允许变更的两种类型。 */