This commit is contained in:
2026-08-21 14:58:15 +08:00
parent b556fdacc5
commit 06893cf3e9
8 changed files with 526 additions and 0 deletions
@@ -23,6 +23,7 @@ import com.budwk.app.zhgh.democratic.proposal.models.ProposalConfig;
import com.budwk.app.zhgh.staffmanage.member.constant.MemberChangeOrigin;
import com.budwk.app.zhgh.staffmanage.member.constant.MemberChangeType;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
@@ -160,6 +161,10 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService
for (Sys_user_source source : sources) {
Sys_user user = userMap.get(source.getLoginname());
source.setMember(null);
String unitId = source.getUnitId();
if (StringUtils.isNotBlank(unitId) && unitId.length() == 5) {
source.setUnitId(unitId.substring(0, 3));
}
// 创建用户对象
Sys_user u = new Sys_user();
@@ -0,0 +1,49 @@
package com.budwk.app.zhgh.democratic.teachercongress.delegate.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.result.Result;
import com.budwk.app.zhgh.democratic.teachercongress.delegate.param.RepresentativeChangeRecordPageParam;
import com.budwk.app.zhgh.democratic.teachercongress.delegate.service.RepresentativeChangeRecordService;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.util.NutMap;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
import javax.validation.Valid;
/**
* 教代会代表变动记录页面及查询接口。
*/
@IocBean
@At("/platform/jdh/db/changeRecord")
@Ok("json:full")
public class RepresentativeChangeRecordController {
@Inject
private RepresentativeChangeRecordService representativeChangeRecordService;
/**
* 打开代表变动记录页面。
*/
@At("")
@Ok("beetl:/platform/zhgh/democratic/teachercongress/delegate/changeRecord/index.html")
@SaCheckPermission("sys.jdh.db.changeRecord")
public void index() {
}
/**
* 分页查询代表变动记录。
*
* @param pageForm 分页、关键字及组织筛选参数
* @return 代表变动记录分页结果
*/
@At
@SaCheckPermission("sys.jdh.db.changeRecord")
public Result pageData(@Valid @Param("pageForm") RepresentativeChangeRecordPageParam pageForm) {
Pagination<NutMap> pagination = representativeChangeRecordService.pageData(pageForm);
return Result.success(pagination);
}
}
@@ -0,0 +1,106 @@
package com.budwk.app.zhgh.democratic.teachercongress.delegate.models;
import com.budwk.app.base.model.BaseModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.nutz.dao.entity.annotation.ColDefine;
import org.nutz.dao.entity.annotation.ColType;
import org.nutz.dao.entity.annotation.Column;
import org.nutz.dao.entity.annotation.Comment;
import org.nutz.dao.entity.annotation.Name;
import org.nutz.dao.entity.annotation.Table;
import org.nutz.dao.entity.annotation.TableMeta;
import org.nutz.dao.interceptor.annotation.PrevInsert;
/**
* 教代会届次延用时产生的代表异动记录。
* 记录代表在上一届与新一届之间单位、分工会及代表团的对应关系,供后续异动查询使用。
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Table("representative_change_record")
@TableMeta("{'mysql-charset':'utf8mb4'}")
@Comment("教代会代表异动记录")
public class RepresentativeChangeRecord extends BaseModel {
@Name
@Comment("ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
@PrevInsert(uu32 = true)
private String id;
@Column
@Comment("用户ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String userId;
@Column
@Comment("原教代会届次")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String oldJdhSession;
@Column
@Comment("新教代会届次")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String newJdhSession;
@Column
@Comment("原单位ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String oldUnitId;
@Column
@Comment("原单位名称")
@ColDefine(type = ColType.VARCHAR, width = 500)
private String oldUnitName;
@Column
@Comment("原工会ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String oldUnionId;
@Column
@Comment("原工会名称")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String oldUnionName;
@Column
@Comment("原代表团ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String oldDbtId;
@Column
@Comment("原代表团名称")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String oldDbtName;
@Column
@Comment("新单位ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String newUnitId;
@Column
@Comment("新单位名称")
@ColDefine(type = ColType.VARCHAR, width = 500)
private String newUnitName;
@Column
@Comment("新工会ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String newUnionId;
@Column
@Comment("新工会名称")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String newUnionName;
@Column
@Comment("新代表团ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String newDbtId;
@Column
@Comment("新代表团名称")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String newDbtName;
}
@@ -0,0 +1,22 @@
package com.budwk.app.zhgh.democratic.teachercongress.delegate.param;
import com.budwk.app.base.param.PageForm;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 代表变动记录分页查询参数。
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class RepresentativeChangeRecordPageParam extends PageForm {
/** 新教代会届次ID。 */
private String sessionId;
/** 代表变动后的所属单位ID。 */
private String unitId;
/** 代表变动后的所属分工会ID。 */
private String unionId;
}
@@ -0,0 +1,21 @@
package com.budwk.app.zhgh.democratic.teachercongress.delegate.service;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.service.BaseService;
import com.budwk.app.zhgh.democratic.teachercongress.delegate.models.RepresentativeChangeRecord;
import com.budwk.app.zhgh.democratic.teachercongress.delegate.param.RepresentativeChangeRecordPageParam;
import org.nutz.lang.util.NutMap;
/**
* 教代会代表变动记录服务。
*/
public interface RepresentativeChangeRecordService extends BaseService<RepresentativeChangeRecord> {
/**
* 分页查询代表变动记录,并关联人员及新旧届次名称。
*
* @param pageForm 分页、关键字及组织筛选条件
* @return 代表变动记录分页数据
*/
Pagination<NutMap> pageData(RepresentativeChangeRecordPageParam pageForm);
}
@@ -0,0 +1,96 @@
package com.budwk.app.zhgh.democratic.teachercongress.delegate.service.impl;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.zhgh.democratic.teachercongress.delegate.models.RepresentativeChangeRecord;
import com.budwk.app.zhgh.democratic.teachercongress.delegate.param.RepresentativeChangeRecordPageParam;
import com.budwk.app.zhgh.democratic.teachercongress.delegate.service.RepresentativeChangeRecordService;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.util.NutMap;
import java.util.Map;
/**
* 教代会代表变动记录服务实现。
*/
@IocBean(args = {"refer:dao"})
public class RepresentativeChangeRecordServiceImpl extends BaseServiceImpl<RepresentativeChangeRecord>
implements RepresentativeChangeRecordService {
/** 前端排序字段与查询字段映射,避免将未校验字段直接拼入 SQL。 */
private static final Map<String, String> SORT_COLUMNS = Map.of(
"username", "u.username",
"loginname", "u.loginname",
"oldJdhName", "oldSession.fullName",
"newJdhName", "newSession.fullName",
"oldUnitName", "c.oldUnitName",
"newUnitName", "c.newUnitName",
"oldDbtName", "c.oldDbtName",
"newDbtName", "c.newDbtName"
);
public RepresentativeChangeRecordServiceImpl(Dao dao) {
super(dao);
}
/**
* 分页查询代表变动记录,并关联当前人员姓名、工号及新旧届次名称。
*
* @param pageForm 分页、关键字及组织筛选条件
* @return 可直接供列表展示的分页 Map 数据
*/
@Override
@SuppressWarnings("unchecked")
public Pagination<NutMap> pageData(RepresentativeChangeRecordPageParam pageForm) {
Sql sql = Sqls.create("""
SELECT
c.*,
u.username,
u.loginname,
oldSession.fullName AS oldJdhName,
newSession.fullName AS newJdhName
FROM representative_change_record c
LEFT JOIN sys_user u ON c.userId = u.id
LEFT JOIN teacher_congress_session oldSession ON oldSession.id = c.oldJdhSession
LEFT JOIN teacher_congress_session newSession ON newSession.id = c.newJdhSession
$condition
""");
sql.setCondition(buildCondition(pageForm));
return listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
}
/**
* 组装代表变动记录查询条件。单位和分工会均按变动后的组织信息筛选,保持与原系统口径一致。
*
* @param pageForm 页面查询参数
* @return SQL 查询条件及安全排序规则
*/
private Cnd buildCondition(RepresentativeChangeRecordPageParam pageForm) {
Cnd cnd = Cnd.NEW();
cnd.andEX("c.newJdhSession", "=", pageForm.getSessionId());
cnd.andEX("c.newUnionId", "=", pageForm.getUnionId());
cnd.andEX("c.newUnitId", "=", pageForm.getUnitId());
if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) {
String keyword = "%" + pageForm.getSearchKeyword().trim() + "%";
cnd.and(Cnd.exps("u.username", "like", keyword)
.or("u.loginname", "like", keyword));
}
String sortColumn = SORT_COLUMNS.get(pageForm.getPageOrderName());
if (StrUtil.isNotBlank(sortColumn)) {
if ("ascending".equalsIgnoreCase(pageForm.getPageOrderBy())) {
cnd.asc(sortColumn);
} else {
cnd.desc(sortColumn);
}
} else {
cnd.desc("c.createdAt");
}
return cnd;
}
}
@@ -17,6 +17,7 @@ import com.budwk.app.sys.services.SysRoleService;
import com.budwk.app.sys.services.SysUserService;
import com.budwk.app.sys.views.View_user;
import com.budwk.app.zhgh.democratic.proposal.models.ProposalType;
import com.budwk.app.zhgh.democratic.teachercongress.delegate.models.RepresentativeChangeRecord;
import com.budwk.app.zhgh.democratic.teachercongress.delegate.models.Teacher_congress_delegate;
import com.budwk.app.zhgh.democratic.teachercongress.delegation.models.Teacher_congress_delegation;
import com.budwk.app.zhgh.democratic.teachercongress.delegation.models.Teacher_congress_delegation_union;
@@ -46,6 +47,8 @@ import java.util.Date;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
@IocBean(args = {"refer:dao"})
public class TeacherCongressSessionServiceImpl extends BaseServiceImpl<Teacher_congress_session> implements TeacherCongressSessionService {
@@ -131,6 +134,9 @@ public class TeacherCongressSessionServiceImpl extends BaseServiceImpl<Teacher_c
//从代表团入手
List<Teacher_congress_delegation> lastDelegations = dao().query(Teacher_congress_delegation.class, Cnd.where("sessionId", "=", lastSession.getId()));
// 保留上一届代表团ID与实体的对应关系,用于代表延用完成后判断代表团是否发生异动。
Map<String, Teacher_congress_delegation> lastDelegationsMap = lastDelegations.stream()
.collect(Collectors.toMap(Teacher_congress_delegation::getId, delegation -> delegation));
for (Teacher_congress_delegation lastDelegation : lastDelegations) {
//代表团继承
Teacher_congress_delegation newDelegation = BeanUtil.copyProperties(lastDelegation, Teacher_congress_delegation.class);
@@ -201,10 +207,17 @@ public class TeacherCongressSessionServiceImpl extends BaseServiceImpl<Teacher_c
dao().fastInsert(newDelegationUnits);
}
// 新代表团已经全部生成,此时建立ID索引,后续可按新代表的 delegationId 获取新代表团编码。
List<Teacher_congress_delegation> newSessionDelegations = dao().query(Teacher_congress_delegation.class,
Cnd.where(Teacher_congress_delegation::getSessionId, "=", session.getId()));
Map<String, Teacher_congress_delegation> newDelegationsMap = newSessionDelegations.stream()
.collect(Collectors.toMap(Teacher_congress_delegation::getId, delegation -> delegation));
//代表数据 单位 分工会 要根据最新的设置来调整
//代表数据
List<Teacher_congress_delegate> lastDelegates = dao().query(Teacher_congress_delegate.class, Cnd.where(Teacher_congress_delegate::getSessionId, "=", lastSession.getId()));
List<RepresentativeChangeRecord> changeRecords = new ArrayList<>();
List<Teacher_congress_delegate> newDelegates = lastDelegates.stream().map(lastDelegate -> {
Teacher_congress_delegate newDelegate = BeanUtil.copyProperties(lastDelegate, Teacher_congress_delegate.class);
newDelegate.setId(R.UU32());
@@ -241,9 +254,20 @@ public class TeacherCongressSessionServiceImpl extends BaseServiceImpl<Teacher_c
}
}
}
// 以代表团编码判断是否发生代表团异动,并完整保存延用前后的组织信息。
RepresentativeChangeRecord changeRecord = buildRepresentativeChangeRecord(
lastSession.getId(), session.getId(), lastDelegate, newDelegate,
lastDelegationsMap, newDelegationsMap);
if (changeRecord != null) {
changeRecords.add(changeRecord);
}
return newDelegate;
}).toList();
dao().insert(newDelegates);
if (!changeRecords.isEmpty()) {
dao().fastInsert(changeRecords);
}
//代表权限数据
for (Teacher_congress_delegate newDelegate : newDelegates) {
@@ -282,6 +306,51 @@ public class TeacherCongressSessionServiceImpl extends BaseServiceImpl<Teacher_c
finishInsertSessionRoleStatus(session);
}
/**
* 根据延用前后的代表团编码生成代表异动记录。
* 旧数据缺少代表团或新届次无法匹配代表团时不生成记录,避免不完整历史数据导致新增届次失败。
*
* @param oldSessionId 原届次ID
* @param newSessionId 新届次ID
* @param oldDelegate 原届次代表数据
* @param newDelegate 延用后代表数据
* @param oldDelegationsMap 原届次代表团映射
* @param newDelegationsMap 新届次代表团映射
* @return 代表团编码发生变化时返回异动记录,否则返回 null
*/
private RepresentativeChangeRecord buildRepresentativeChangeRecord(
String oldSessionId,
String newSessionId,
Teacher_congress_delegate oldDelegate,
Teacher_congress_delegate newDelegate,
Map<String, Teacher_congress_delegation> oldDelegationsMap,
Map<String, Teacher_congress_delegation> newDelegationsMap) {
Teacher_congress_delegation oldDelegation = oldDelegationsMap.get(oldDelegate.getDelegationId());
Teacher_congress_delegation newDelegation = newDelegationsMap.get(newDelegate.getDelegationId());
if (oldDelegation == null || newDelegation == null
|| Objects.equals(oldDelegation.getCode(), newDelegation.getCode())) {
return null;
}
RepresentativeChangeRecord record = new RepresentativeChangeRecord();
record.setOldJdhSession(oldSessionId);
record.setNewJdhSession(newSessionId);
record.setUserId(oldDelegate.getUserId());
record.setOldUnitId(oldDelegate.getUnitId());
record.setOldUnitName(oldDelegate.getUnitName());
record.setOldUnionId(oldDelegate.getUnionId());
record.setOldUnionName(oldDelegate.getUnionName());
record.setOldDbtId(oldDelegate.getDelegationId());
record.setOldDbtName(oldDelegation.getName());
record.setNewUnitId(newDelegate.getUnitId());
record.setNewUnitName(newDelegate.getUnitName());
record.setNewUnionId(newDelegate.getUnionId());
record.setNewUnionName(newDelegate.getUnionName());
record.setNewDbtId(newDelegate.getDelegationId());
record.setNewDbtName(newDelegation.getName());
return record;
}
/**
* 修改届次基础信息。提案类型在保存前统一完成 JSON 解析、去重和长度校验,
* 避免新增与编辑使用不同的数据格式。
@@ -492,6 +561,9 @@ public class TeacherCongressSessionServiceImpl extends BaseServiceImpl<Teacher_c
dao().clear(Teacher_congress_prepare_file.class, Cnd.where("sessionId", "=", id));
//代表
dao().clear(Teacher_congress_delegate.class, Cnd.where("sessionId", "=", id));
//删除以当前届次为新届次的代表异动记录,保持与届次延用数据一致。
dao().clear(RepresentativeChangeRecord.class,
Cnd.where(RepresentativeChangeRecord::getNewJdhSession, "=", id));
//删除相关权限
dao().clear(Sys_user_role.class, Cnd.where(Sys_user_role::getTcSessionId, "=", id));
@@ -0,0 +1,155 @@
<!--#
layout("/layouts/platform.html"){
#-->
<div id="app" v-cloak>
<guava ref="guava">
<template>
<el-card shadow="never">
<search @search="doSearch">
<search-item label="姓名/工号">
<el-input v-model="pageForm.searchKeyword" clearable
placeholder="请输入姓名或工号"
@keyup.enter.native="doSearch"></el-input>
</search-item>
<search-item label="教代会">
<el-select v-model="pageForm.sessionId" clearable filterable
placeholder="请选择教代会" style="width: 100%">
<el-option v-for="item in sessionOptions" :key="item.id"
:label="item.fullName" :value="item.id"></el-option>
</el-select>
</search-item>
<search-item label="所属工会">
<el-select v-model="pageForm.unionId" clearable filterable
placeholder="请选择所属工会" style="width: 100%"
@change="handleUnionChange">
<el-option v-for="item in unionOptions" :key="item.id"
:label="item.name" :value="item.id"></el-option>
</el-select>
</search-item>
<search-item label="所属单位">
<el-select v-model="pageForm.unitId" clearable filterable
placeholder="请选择所属单位" style="width: 100%">
<el-option v-for="item in unitOptions" :key="item.id"
:label="item.name" :value="item.id"></el-option>
</el-select>
</search-item>
</search>
</el-card>
<el-card class="mt10" shadow="never">
<table-tool label="代表变动记录"></table-tool>
<el-table v-loading="tableLoading" :data="tableData"
@sort-change="pageOrder" row-key="id" ref="tableRef"
style="width: 100%">
<el-table-column type="index" :index="indexMethod" label="序号"
align="center" header-align="center" width="70"></el-table-column>
<el-table-column label="姓名" prop="username" sortable="custom"
align="center" header-align="center" min-width="110"></el-table-column>
<el-table-column label="工号" prop="loginname" sortable="custom"
align="center" header-align="center" min-width="110"></el-table-column>
<el-table-column label="原教代会" prop="oldJdhName" sortable="custom"
align="center" header-align="center" min-width="130"
show-overflow-tooltip></el-table-column>
<el-table-column label="新教代会" prop="newJdhName" sortable="custom"
align="center" header-align="center" min-width="130"
show-overflow-tooltip></el-table-column>
<el-table-column label="原单位" prop="oldUnitName" sortable="custom"
align="center" header-align="center" min-width="180"
show-overflow-tooltip></el-table-column>
<el-table-column label="新单位" prop="newUnitName" sortable="custom"
align="center" header-align="center" min-width="180"
show-overflow-tooltip></el-table-column>
<el-table-column label="原代表团" prop="oldDbtName" sortable="custom"
align="center" header-align="center" min-width="140"
show-overflow-tooltip></el-table-column>
<el-table-column label="新代表团" prop="newDbtName" sortable="custom"
align="center" header-align="center" min-width="140"
show-overflow-tooltip></el-table-column>
</el-table>
<!--#include("/layouts/pagination.html"){}#-->
</el-card>
</template>
</guava>
</div>
<script nonce="${cspNonce!}">
new Vue({
el: "#app",
mixins: [initTableMixins],
data() {
return {
pageForm: {
searchKeyword: "",
sessionId: "",
unionId: "",
unitId: ""
},
sessionOptions: [],
unionOptions: [],
unitOptions: [],
pageRequestSequence: 0
}
},
methods: {
/** 查询分页数据;快速连续查询时只接收最后一次请求结果。 */
pageData() {
const requestSequence = ++this.pageRequestSequence
this.tableData = []
this.tableLoading = true
this.$axios.post(loc() + "/pageData", {
pageForm: JSON.stringify(this.pageForm)
}).then((res) => {
if (requestSequence !== this.pageRequestSequence) {
return
}
if (res.code === 0) {
this.tableData = res.data.list || []
this.pageForm.totalCount = res.data.totalCount || 0
} else {
this.$message.error(res.msg)
}
}).finally(() => {
if (requestSequence === this.pageRequestSequence) {
this.tableLoading = false
}
})
},
/** 切换工会时清空原单位条件,并重新加载该工会下的单位。 */
handleUnionChange() {
this.pageForm.unitId = ""
this.listUnit()
},
/** 加载教代会届次选项。 */
listSession() {
this.$axios.post("/platform/teacherCongress/common/listSession").then((res) => {
if (res.code === 0) {
this.sessionOptions = res.data || []
}
})
},
/** 加载当前账号可见的工会选项。 */
listUnion() {
this.$businessTool.listUnion().then((data) => {
this.unionOptions = data || []
})
},
/** 按当前工会条件加载单位选项。 */
listUnit() {
this.$businessTool.listUnit(this.pageForm.unionId).then((data) => {
this.unitOptions = data || []
})
}
},
created() {
this.listSession()
this.listUnion()
this.listUnit()
this.pageData()
}
})
</script>
<!--#
}
#-->