This commit is contained in:
=
2026-05-29 11:37:02 +08:00
parent 6b2fd1047f
commit 6e4771282d
14 changed files with 281 additions and 58 deletions
@@ -408,16 +408,30 @@ public class SysUnionController {
}
if (isAdmin) {
// 如果是管理员,直接加角色
sysRoleService.clear("sys_user_role", Cnd.where("roleId", "=", role.getId()).and("userId", "=", userId).and("unionId", "=", unionId));
sysRoleService.insert("sys_user_role", Chain.make("roleId", role.getId()).add("userId", userId).add("unionId", unionId));
sysRoleService.clearCache();
sysUserService.clearCache();
// 如果是管理员,直接同步分工会干部角色;非管理员等待校工会审核通过后同步。
// sysRoleService.clear("sys_user_role", Cnd.where("roleId", "=", role.getId()).and("userId", "=", userId).and("unionId", "=", unionId));
// sysRoleService.insert("sys_user_role", Chain.make("roleId", role.getId()).add("userId", userId).add("unionId", unionId));
// sysRoleService.clearCache();
// sysUserService.clearCache();
sysUnionService.syncBranchUnionCadreRole(unionCadre);
}
return Result.success();
}
@At
@ApiOperation("同步分工会干部角色")
@SaCheckPermission("sys.manager.union")
@Aop(TransAop.READ_COMMITTED)
public Result syncBranchUnionCadreRole(String id) {
Sys_union_cadre unionCadre = dao.fetch(Sys_union_cadre.class, id);
if (Lang.isEmpty(unionCadre)) {
return Result.error("未找到分工会干部记录");
}
sysUnionService.syncBranchUnionCadreRole(unionCadre);
return Result.success();
}
@At
@ApiOperation("删除分工会人员角色")
@SaCheckPermission("sys.manager.union.branchOfficer")
@@ -6,12 +6,8 @@ import com.budwk.app.flow.engine.FlowInterceptor;
import com.budwk.app.flow.engine.core.Execution;
import com.budwk.app.flow.engine.core.ServiceContext;
import com.budwk.app.flow.enums.ProcessSubmitTypeEnum;
import com.budwk.app.sys.models.Sys_role;
import com.budwk.app.sys.models.Sys_union_cadre;
import com.budwk.app.sys.services.SysRoleService;
import com.budwk.app.sys.services.SysUserService;
import org.nutz.dao.Chain;
import org.nutz.dao.Cnd;
import com.budwk.app.sys.services.SysUnionService;
/**
* @version 1.0
@@ -26,27 +22,25 @@ public class SysUnionSchoolAuditInterceptor implements FlowInterceptor {
@Override
public void intercept(Execution execution) {
// 是否审核通过
boolean submitType = execution.getArgs().getInt("submitType").equals(ProcessSubmitTypeEnum.AGREE.getCode());
Integer submitTypeCode = execution.getArgs().getInt(FlowConst.SUBMIT_TYPE);
boolean submitType = ProcessSubmitTypeEnum.AGREE.getCode().equals(submitTypeCode);
if (submitType) {
// 审核通过,添加相应的角色
String formDataStr = execution.getArgs().getStr(FlowConst.FORM_DATA);
SysRoleService sysRoleService = ServiceContext.find(SysRoleService.class);
SysUserService sysUserService = ServiceContext.find(SysUserService.class);
SysUnionService sysUnionService = ServiceContext.find(SysUnionService.class);
// 表单数据
Sys_union_cadre unionBean = JSONUtil.toBean(formDataStr, Sys_union_cadre.class);
// 清除对应角色然后再新增
Sys_role role = sysRoleService.getByCode(unionBean.getRoleCode());
sysRoleService.clear("sys_user_role", Cnd.where("roleId", "=", role.getId())
.and("userId", "=", unionBean.getUserId()).and("unionId", "=", unionBean.getUnionId()));
sysRoleService.insert("sys_user_role", Chain.make("roleId", role.getId())
.add("userId", unionBean.getUserId()).add("unionId", unionBean.getUnionId()));
// 清除缓存
sysRoleService.clearCache();
sysUserService.clearCache();
// 原直接写角色逻辑保留,统一改由 service 同步干部角色。
// Sys_role role = sysRoleService.getByCode(unionBean.getRoleCode());
// sysRoleService.clear("sys_user_role", Cnd.where("roleId", "=", role.getId())
// .and("userId", "=", unionBean.getUserId()).and("unionId", "=", unionBean.getUnionId()));
// sysRoleService.insert("sys_user_role", Chain.make("roleId", role.getId())
// .add("userId", unionBean.getUserId()).add("unionId", unionBean.getUnionId()));
// 校工会审核通过后,同步干部对应的系统角色,供根目录主席列和权限判断读取。
sysUnionService.syncBranchUnionCadreRole(unionBean);
}
// 未审核通过,不作操作
}
@@ -279,6 +279,12 @@ public class Sys_user extends BaseModel implements Serializable {
@Default(value = "0")
private Boolean member;
@Column
@Comment("是否缴费")
@ColDefine(type = ColType.BOOLEAN)
@Default(value = "0")
private Boolean memberFeePaid;
@Column
@Comment("会员加入时间")
@ColDefine(type = ColType.DATETIME)
@@ -2,6 +2,12 @@ package com.budwk.app.sys.services;
import com.budwk.app.base.service.BaseService;
import com.budwk.app.sys.models.Sys_union;
import com.budwk.app.sys.models.Sys_union_cadre;
public interface SysUnionService extends BaseService<Sys_union> {
/**
* 根据分工会干部记录同步系统角色,保证干部表和用户角色表保持一致。
*/
void syncBranchUnionCadreRole(Sys_union_cadre unionCadre);
}
@@ -153,39 +153,41 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService
}
/**
* 批量补充会员角色,已存在会员角色的用户不重复插入。
* 批量补充指定角色,已存在该角色的用户不重复插入。
*/
private void addMemberRolesIfAbsent(List<String> userIds, Sys_role memberRole) {
if (Lang.isEmpty(userIds) || memberRole == null) {
return;
private int addRoleIfAbsent(List<String> userIds, Sys_role role, String roleName) {
if (Lang.isEmpty(userIds) || role == null) {
return 0;
}
List<String> distinctUserIds = userIds.stream().filter(Objects::nonNull).distinct().toList();
if (distinctUserIds.isEmpty()) {
return;
return 0;
}
List<Sys_user_role> existingRoles = dao.query(Sys_user_role.class,
Cnd.where("roleId", "=", memberRole.getId()).and("userId", "in", distinctUserIds));
Set<String> existingUserIds = existingRoles.stream().map(Sys_user_role::getUserId).collect(Collectors.toSet());
List<Sys_user_role> roleList = distinctUserIds.stream()
.filter(id -> !existingUserIds.contains(id))
.map(id -> {
Sys_user_role userRole = new Sys_user_role();
userRole.setRoleId(memberRole.getId());
userRole.setUserId(id);
return userRole;
})
.collect(Collectors.toList());
if (roleList.isEmpty()) {
return;
}
List<List<Sys_user_role>> batches = ListUtil.split(roleList, 500);
batches.forEach(batch -> {
try {
dao.fastInsert(batch);
} catch (Exception e) {
log.error("批量添加会员角色异常", e);
int insertCount = 0;
for (List<String> userIdBatch : ListUtil.split(distinctUserIds, 500)) {
List<Sys_user_role> existingRoles = dao.query(Sys_user_role.class,
Cnd.where("roleId", "=", role.getId()).and("userId", "in", userIdBatch));
Set<String> existingUserIds = existingRoles.stream().map(Sys_user_role::getUserId).collect(Collectors.toSet());
List<Sys_user_role> roleList = userIdBatch.stream()
.filter(id -> !existingUserIds.contains(id))
.map(id -> {
Sys_user_role userRole = new Sys_user_role();
userRole.setRoleId(role.getId());
userRole.setUserId(id);
return userRole;
})
.collect(Collectors.toList());
if (roleList.isEmpty()) {
continue;
}
});
try {
dao.fastInsert(roleList);
insertCount += roleList.size();
} catch (Exception e) {
log.error("批量添加{}角色异常", roleName, e);
}
}
return insertCount;
}
/**
@@ -297,6 +299,7 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService
}, executorService);
updateTasks.add(insertTask);
/*
// 异步处理公共角色分配
if (!needInitUserList.isEmpty()) {
CompletableFuture<Void> roleTask = CompletableFuture.runAsync(() -> {
@@ -326,6 +329,7 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService
}, executorService);
// 不等待角色分配完成
}
*/
}
// 2. 更新现有用户 - 使用批量处理
@@ -358,6 +362,18 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService
return "更新失败: " + e.getMessage();
}
// 全量更新后统一补齐角色:所有人补公共角色,所有会员补会员角色;仅新增缺失角色,不删除、不重复插入。
List<Sys_user> latestUsers = dao.query(Sys_user.class, Cnd.NEW());
List<String> allUserIds = latestUsers.stream().map(Sys_user::getId).filter(Objects::nonNull).toList();
List<String> memberUserIds = latestUsers.stream()
.filter(user -> Boolean.TRUE.equals(user.getMember()))
.map(Sys_user::getId)
.filter(Objects::nonNull)
.toList();
int publicRoleAddCount = addRoleIfAbsent(allUserIds, publicRole, "公共");
int memberRoleAddCount = addRoleIfAbsent(memberUserIds, memberRole, "会员");
log.info("全量更新补齐角色完成,新增公共角色{}个,新增会员角色{}个", publicRoleAddCount, memberRoleAddCount);
// 3. 异步添加历史记录 - 不等待完成
if (Lang.isNotEmpty(histories)) {
executorService.execute(() -> {
@@ -374,19 +390,19 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService
});
}
/*
// 4. 异步添加会员角色 - 不等待完成
if (Lang.isNotEmpty(addMemberUserIds)) {
executorService.execute(() -> {
try {
log.info("添加会员角色: {} 个", addMemberUserIds.size());
addMemberRolesIfAbsent(addMemberUserIds, memberRole);
addRoleIfAbsent(addMemberUserIds, memberRole, "会员");
} catch (Exception e) {
log.error("添加会员角色异常", e);
}
});
}
/*
// 5. 异步移除会员角色 - 不等待完成
if (Lang.isNotEmpty(removeMemberUserIds)) {
executorService.execute(() -> {
@@ -419,7 +435,7 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService
log.info("全量更新用户数据完成,耗时: {} 毫秒", (endTime - startTime));
return "更新完成: 新增用户 " + needInitUserList.size() + " 个, 更新用户 " + needDoUpdateList.size()
+ " 个, 待添加会员 " + addMemberUserIds.size() + " 个, 待移除会员 " + removeMemberUserIds.size() + " 个";
+ " 个, 新增公共角色 " + publicRoleAddCount + " 个, 新增会员角色 " + memberRoleAddCount + " 个";
}
/**
@@ -1,14 +1,64 @@
package com.budwk.app.sys.services.impl;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.exception.BaseException;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.sys.models.Sys_role;
import com.budwk.app.sys.models.Sys_union;
import com.budwk.app.sys.models.Sys_union_cadre;
import com.budwk.app.sys.models.Sys_user_role;
import com.budwk.app.sys.services.SysRoleService;
import com.budwk.app.sys.services.SysUnionService;
import com.budwk.app.sys.services.SysUserService;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Lang;
@IocBean(args = {"refer:dao"})
public class SysUnionServiceImpl extends BaseServiceImpl<Sys_union> implements SysUnionService {
@Inject
private SysRoleService sysRoleService;
@Inject
private SysUserService sysUserService;
public SysUnionServiceImpl(Dao dao) {
super(dao);
}
/**
* 分工会干部审核通过后,需要同时维护 sys_user_role,否则根目录分工会主席列和实际权限都会取不到角色。
*/
@Override
@Aop(TransAop.READ_COMMITTED)
public void syncBranchUnionCadreRole(Sys_union_cadre unionCadre) {
if (unionCadre == null || StrUtil.hasBlank(unionCadre.getUserId(), unionCadre.getUnionId(), unionCadre.getRoleCode())) {
throw new BaseException("分工会干部角色同步失败,干部记录缺少用户、工会或角色信息");
}
Sys_role role = sysRoleService.fetch(Cnd.where(Sys_role::getCode, "=", unionCadre.getRoleCode()));
if (Lang.isEmpty(role)) {
throw new BaseException("分工会干部角色同步失败,未找到{}对应的系统角色", unionCadre.getRoleCode());
}
Cnd roleCnd = Cnd.where(Sys_user_role::getRoleId, "=", role.getId())
.and(Sys_user_role::getUserId, "=", unionCadre.getUserId())
.and(Sys_user_role::getUnionId, "=", unionCadre.getUnionId());
dao().clear(Sys_user_role.class, roleCnd);
if (Boolean.TRUE.equals(unionCadre.getIsJoin())) {
Sys_user_role userRole = new Sys_user_role();
userRole.setRoleId(role.getId());
userRole.setUserId(unionCadre.getUserId());
userRole.setUnionId(unionCadre.getUnionId());
dao().insert(userRole);
}
sysRoleService.clearCache();
sysUserService.clearCache();
}
}
@@ -108,6 +108,9 @@ public class View_user {
@Column
private Integer member;
@Column
private Boolean memberFeePaid;
@Column
private Integer memberOrigin;
@@ -102,6 +102,28 @@ public class MemberInfoGroupController {
memberInfoService.exportMember(pageForm, response);
}
@At
@SaCheckPermission("member.info.group")
public Result toggleMemberFeePaid(String userId) {
try {
return Result.success(memberInfoService.toggleMemberFeePaid(userId));
} catch (Exception e) {
e.printStackTrace();
return Result.error(e.getMessage());
}
}
@At
@SaCheckPermission("member.info.group")
public Result batchSetMemberFeePaid(MemberInfoPageForm pageForm) {
try {
return Result.success(memberInfoService.batchSetMemberFeePaid(pageForm));
} catch (Exception e) {
e.printStackTrace();
return Result.error(e.getMessage());
}
}
@At
@Ok("void")
@@ -74,6 +74,9 @@ public class MemberInfoPageForm extends PageForm {
@ApiModelProperty("会员状态")
private Integer memberStatus;
@ApiModelProperty("是否缴费")
private Boolean memberFeePaid;
@ApiModelProperty("职位")
private String position;
@@ -118,9 +121,9 @@ public class MemberInfoPageForm extends PageForm {
// 权限判断
if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name())) {
// 分工会主席只能查看本人所在单位会员,避免无分工会人员被 unionId 空值兜底查出。
// 分工会主席查看本人所在分工会下的会员,覆盖该分工会包含的全部单位。
if (AuthUtil.hasRole(RoleConstant.BRANCH_UNION_CHAIRMAN.name())) {
cnd.and(prefix + "unitId", "=", SecurityUtil.getUnitId());
cnd.and(prefix + "unionId", "=", SecurityUtil.getUnionId());
} else {
cnd.and(prefix + "unionId", "=", SecurityUtil.getUnionId());
}
@@ -61,6 +61,22 @@ public interface MemberInfoService extends BaseService<Sys_user> {
*/
void exportMember(MemberInfoPageForm pageForm, HttpServletResponse response);
/**
* 切换指定会员的缴费状态
*
* @param userId 会员用户ID
* @return 切换后的缴费状态,true 表示已缴费,false 表示未缴费
*/
Boolean toggleMemberFeePaid(String userId);
/**
* 按当前会员列表筛选条件批量设置为已缴费
*
* @param pageForm 会员列表筛选条件
* @return 本次设置为已缴费的会员数量
*/
int batchSetMemberFeePaid(MemberInfoPageForm pageForm);
/**
* 导入会员数据处理
@@ -101,14 +101,18 @@ public class MemberInfoServiceImpl extends BaseServiceImpl<Sys_user> implements
DATE_FORMAT(u.teachingTime, '%Y-%m-%d') AS teachingTime,
DATE_FORMAT(u.expectedLeaveSchoolDate, '%Y-%m-%d') AS expectedLeaveSchoolDate,
u.member,
IFNULL(su.memberFeePaid, 0) AS memberFeePaid,
DATE_FORMAT(u.postDoctoralJoinDate, '%Y-%m-%d') AS postDoctoralJoinDate
FROM
vw_user u
LEFT JOIN sys_user su ON su.id = u.id
$condition
""");
Cnd cnd = Cnd.NEW();
cnd.and("member", "=", true);
cnd.and("u.member", "=", true);
pageForm.buildSearch(cnd, "u.");
cnd.andEX("su.memberFeePaid", "=", pageForm.getMemberFeePaid());
cnd.asc("u.id");
sql.setCondition(cnd);
return sql;
}
@@ -266,6 +270,37 @@ public class MemberInfoServiceImpl extends BaseServiceImpl<Sys_user> implements
CommonDownloadUtil.download("会员档案表.xlsx", workbook, response);
}
@Override
@Aop(TransAop.READ_COMMITTED)
public Boolean toggleMemberFeePaid(String userId) {
if (StrUtil.isBlank(userId)) {
throw new RuntimeException("会员用户ID不能为空");
}
Sys_user user = dao().fetch(Sys_user.class, userId);
if (user == null) {
throw new RuntimeException("会员用户不存在");
}
Boolean memberFeePaid = !Boolean.TRUE.equals(user.getMemberFeePaid());
dao().update(Sys_user.class, Chain.make("memberFeePaid", memberFeePaid), Cnd.where("id", "=", userId));
return memberFeePaid;
}
@Override
@Aop(TransAop.READ_COMMITTED)
public int batchSetMemberFeePaid(MemberInfoPageForm pageForm) {
Sql sql = getSql(pageForm);
List<NutMap> list = listMap(sql);
List<String> userIds = list.stream()
.map(row -> row.getString("id"))
.filter(StrUtil::isNotBlank)
.toList();
if (Lang.isEmpty(userIds)) {
return 0;
}
dao().update(Sys_user.class, Chain.make("memberFeePaid", true), Cnd.where("id", "in", userIds));
return userIds.size();
}
@Aop(TransAop.READ_COMMITTED)
@Override
@@ -127,10 +127,22 @@ const branchUnionCadreAudit = {
...this.formData,
submitType: val
})
}).then((res) => {
// 审核通过后同步分工会角色,避免干部表有记录但根目录主席列和权限角色为空。
if (res.code === 0 && val === 1) {
return this.$axios.post("/platform/sys/union/syncBranchUnionCadreRole", {
id: row.id
}).then((syncRes) => {
return syncRes.code === 0 ? res : syncRes
})
}
return res
}).then((res) => {
if (res.code === 0) {
this.$message.success(res.msg)
this.doSearch()
} else {
this.$message.error(res.msg)
}
}).finally(() => {
loading.close()
@@ -45,6 +45,12 @@ const COMMON_QUERY = {
<dict-select v-model="pageForm.preparedBys" placeholder="请选择编制类别" @change="doSearch"
code="USER_PREPARED_BY_TYPE" multiple collapse-tags></dict-select>
</search-item>
<search-item label="是否缴费">
<el-select clearable placeholder="请选择是否缴费" style="width: 100%" v-model="pageForm.memberFeePaid" @change="doSearch">
<el-option label="已缴费" :value="true"></el-option>
<el-option label="未缴费" :value="false"></el-option>
</el-select>
</search-item>
</search>
`,
store,
@@ -68,6 +74,7 @@ const COMMON_QUERY = {
userStates: [],
personTypes: [],
preparedBys: [],
memberFeePaid: null,
}
}
},
@@ -10,6 +10,7 @@ layout("/layouts/platform.html"){
<el-card shadow="never" class="mt10">
<table-tool label="会员列表">
<el-button type="primary" size="small" icon="el-icon-check" @click="batchSetMemberFeePaid">一键设置已缴费</el-button>
<el-button type="primary" size="small" icon="el-icon-upload2" @click="openImport">导入会员</el-button>
<el-button
type="primary"
@@ -78,9 +79,14 @@ layout("/layouts/platform.html"){
min-width="50"
></el-table-column>
<el-table-column prop="userOnline" align="center" header-align="center" label="操作" fixed="right" width="100px">
<el-table-column prop="userOnline" align="center" header-align="center" label="操作" fixed="right" width="200px">
<template scope="scope">
<el-button size="mini" type="primary" @click="openView(scope.row.id)">查看</el-button>
<el-button
size="mini"
:type="isMemberFeePaid(scope.row) ? 'success' : 'info'"
@click="toggleMemberFeePaid(scope.row)"
>{{ isMemberFeePaid(scope.row) ? "已缴费" : "未缴费" }}</el-button>
</template>
</el-table-column>
</el-table>
@@ -127,7 +133,7 @@ layout("/layouts/platform.html"){
{ prop: "political", label: "政治面貌", width: 120, sortable: true},
{ prop: "userState", label: "在职状态", width: 120, sortable: true },
{ prop: "personType", label: "教职工类别", width: 120, sortable: true },
{ prop: "preparedBy", label: "编制类别", width: 120, sortable: true },
{ prop: "preparedBy", label: "编制类别", width: 120, sortable: true, checked: 0 },
{ prop: "postDoctoralJoinDate", label: "进站时间", sortable: true, checked: 0 },
{ prop: "unionName", label: "所属工会", width: 120, sortable: true },
{ prop: "unitName", label: "所属单位", width: 120, sortable: true },
@@ -168,6 +174,39 @@ layout("/layouts/platform.html"){
}
this.pageData()
},
isMemberFeePaid(row) {
return row.memberFeePaid === true || row.memberFeePaid === 1 || row.memberFeePaid === "1"
},
toggleMemberFeePaid(row) {
this.$axios.post(loc() + "/toggleMemberFeePaid", { userId: row.id })
.then((data) => {
if (data.code === 0) {
this.$set(row, "memberFeePaid", data.data)
this.$message.success(data.data ? "已设置为已缴费" : "已设置为未缴费")
} else {
this.$message.error(data.msg)
}
})
},
batchSetMemberFeePaid() {
this.$confirm("确认将当前筛选条件下的会员设置为已缴费吗?", "提示", { type: "warning" })
.then(() => {
const pageForm = clone(this.pageForm)
pageForm.userStates = JSON.stringify(this.pageForm.userStates)
pageForm.personTypes = JSON.stringify(this.pageForm.personTypes)
pageForm.preparedBys = JSON.stringify(this.pageForm.preparedBys)
return this.$axios.post(loc() + "/batchSetMemberFeePaid", pageForm)
})
.then((data) => {
if (data.code === 0) {
this.$message.success("已设置" + data.data + "人")
this.pageData()
} else {
this.$message.error(data.msg)
}
})
.catch(() => {})
},
exportMember() {
const pageForm = clone(this.pageForm)
pageForm.userStates = JSON.stringify(this.pageForm.userStates)