This commit is contained in:
2026-07-17 16:40:35 +08:00
parent 6facd195c4
commit 6af2967fd5
14 changed files with 230 additions and 4 deletions
@@ -391,4 +391,12 @@ public class Sys_user extends BaseModel implements Serializable {
@Comment("openId")
@ColDefine(type = ColType.VARCHAR, width = 50)
private String openId;
@Column
@Comment("编制信息")
@ColDefine(type = ColType.VARCHAR, width = 64)
@DataCenterColumn(name = "编制信息", key = "bzxx")
private String compilationInformation;
}
@@ -26,6 +26,7 @@ import com.budwk.app.sys.services.SysDataUnitPullService;
import com.budwk.app.sys.services.SysDataUserPullService;
import com.budwk.app.sys.services.SysDictService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.nutz.aop.interceptor.async.Async;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Chain;
@@ -228,6 +229,8 @@ public class SysDataUserPullServiceImpl extends BaseServiceImpl<Sys_user_source>
// 插入到数据库
dao().insert(latestSourceList);
updateDictCompilationInformation(latestSourceList);
// 人员的单位数据和数据库的单位数据比较,如果人员里面有单位不存在,去更新单位数据
List<String> sourceUnitIds = latestSourceList.stream().map(Sys_user_source::getUnitId).distinct().toList();
Sql sql = Sqls.create("select id from sys_unit group by id");
@@ -245,6 +248,36 @@ public class SysDataUserPullServiceImpl extends BaseServiceImpl<Sys_user_source>
}
}
/**
* 更新编制信息
* @param userSources
*/
private void updateDictCompilationInformation(List<Sys_user_source> userSources){
if(CollUtil.isNotEmpty(userSources)){
List<String> compilationInformationList = userSources.stream()
.map(Sys_user_source::getCompilationInformation)
.filter(StringUtils::isNotBlank)
.distinct()
.collect(Collectors.toList());
//系统在职状态字典
List<Sys_dict> sysUserStates = sysDictService.getSubListByCode("USER_COMPILATION_INFORMATION");
//系统中不存在的就插入
List<Sys_dict> insertUserStates = compilationInformationList.stream()
.filter(compilationInformation -> sysUserStates.stream().noneMatch(dict -> dict.getCode().equals(compilationInformation)))
.map(compilationInformation -> {
Sys_dict dict = new Sys_dict();
dict.setCode(compilationInformation);
dict.setName(compilationInformation);
return dict;
}).toList();
if(CollUtil.isNotEmpty(insertUserStates)){
for (Sys_dict dict : insertUserStates) {
sysDictService.saveByParentCode(dict, "USER_COMPILATION_INFORMATION");
}
}
}
}
@Async
private void updateDict(List<Sys_user_source> userSources) {
//判断是否要更新在职状态字典
@@ -96,6 +96,9 @@ public class View_user {
@Column
private String preparedBy;
@Column
private String compilationInformation;
@Column
private String identityType;
@@ -1,17 +1,24 @@
package com.budwk.app.zhgh.staffmanage.member.controller.change;
import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.result.Result;
import com.budwk.app.base.utils.CommonDownloadUtil;
import com.budwk.app.zhgh.staffmanage.member.param.pageform.MemberManagePageForm;
import com.budwk.app.zhgh.staffmanage.member.service.MemberCommonService;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Workbook;
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 javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.util.List;
@@ -44,6 +51,29 @@ public class MemberChangeRecordController {
return Result.success(pagination);
}
/**
* 导出变更记录列表,导出的字段和数据范围与当前页面查询结果保持一致。
*
* @param pageForm 当前页面查询条件和显示字段
* @param response 文件下载响应
*/
@At
@Ok("void")
@SaCheckPermission("member.change.record")
public void doExport(MemberManagePageForm pageForm, HttpServletResponse response) {
try {
// 导出列由前端当前表格列配置决定,额外补充页面固定展示的异动信息列。
List<ExcelExportEntity> exportEntities = memberCommonService.buildMemberHistoryRecordExportEntities(pageForm.getColumns());
List<NutMap> list = memberCommonService.memberHistoryRecordExportData(pageForm);
ExportParams exportParams = new ExportParams();
exportParams.setType(ExcelType.XSSF);
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, exportEntities, list);
CommonDownloadUtil.download("会员变更记录.xlsx", workbook, response);
} catch (Exception e) {
log.error("导出会员变更记录失败", e);
}
}
@At
@SaCheckPermission("member.change.record")
@@ -14,6 +14,7 @@ import org.nutz.dao.util.cri.Static;
import org.nutz.lang.Lang;
import java.util.List;
import java.util.Map;
/**
* @version 1.0
@@ -59,6 +60,11 @@ public class MemberManagePageForm extends PageForm {
//变更来源
private List<Integer> changeOrigins;
/**
* 页面当前显示的列表字段,导出时按照该字段顺序动态生成表头。
*/
private List<Map<String, String>> columns;
public static void buildSearch(Cnd cnd, MemberManagePageForm pageForm) {
if (cnd == null) {
cnd = Cnd.NEW();
@@ -44,6 +44,9 @@ public class MemberStatisticsPageForm extends PageForm {
private List<String> preparedBys;
@ApiModelProperty("编制信息")
private List<String> compilationInformations;
private List<String> userStates;
private List<String> memberStatus;
@@ -1,5 +1,6 @@
package com.budwk.app.zhgh.staffmanage.member.service;
import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.service.BaseService;
import com.budwk.app.sys.models.Sys_user;
@@ -43,6 +44,22 @@ public interface MemberCommonService extends BaseService<Sys_user> {
*/
Pagination memberHistoryRecords(MemberManagePageForm pageForm);
/**
* 根据变更记录页面查询条件获取全部导出数据,导出数据与列表查询口径保持一致。
*
* @param pageForm 变更记录查询参数
* @return 变更记录导出数据
*/
List<NutMap> memberHistoryRecordExportData(MemberManagePageForm pageForm);
/**
* 根据前端当前列表列配置生成导出列,确保页面展示什么字段就导出什么字段。
*
* @param columns 页面列配置
* @return Excel 导出列配置
*/
List<ExcelExportEntity> buildMemberHistoryRecordExportEntities(List<Map<String, String>> columns);
/**
* 获取可变更字段
@@ -1,11 +1,13 @@
package com.budwk.app.zhgh.staffmanage.member.service.impl;
import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HtmlUtil;
import cn.hutool.json.JSONUtil;
import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.base.exception.BaseException;
import com.budwk.app.base.page.Pagination;
@@ -139,6 +141,54 @@ public class MemberManageServiceImpl extends BaseServiceImpl<Sys_user> implement
@Override
public Pagination memberHistoryRecords(MemberManagePageForm pageForm) {
Sql sql = getMemberHistoryRecordSql(pageForm);
return listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
}
/**
* 根据变更记录查询条件获取全部数据,用于页面导出,保证导出数据与列表查询口径一致。
*
* @param pageForm 变更记录查询参数
* @return 已转换异动类型显示文本的导出数据
*/
@Override
public List<NutMap> memberHistoryRecordExportData(MemberManagePageForm pageForm) {
List<NutMap> list = listMap(getMemberHistoryRecordSql(pageForm));
Map<String, String> changeTypeNameMap = Arrays.stream(MemberChangeType.values())
.collect(Collectors.toMap(MemberChangeType::getCode, MemberChangeType::getChangeTypeName));
int[] no = {1};
list.forEach(row -> {
row.put("no", no[0]++);
row.put("changeTypesText", getChangeTypesText(row.get("changeTypes"), changeTypeNameMap));
});
return list;
}
/**
* 根据页面表格列生成导出列,并补充页面分组展示的更新时间、异动类型两列。
*
* @param columns 页面当前显示的基础列
* @return Excel 导出列配置
*/
@Override
public List<ExcelExportEntity> buildMemberHistoryRecordExportEntities(List<Map<String, String>> columns) {
List<ExcelExportEntity> exportEntities = new ArrayList<>();
exportEntities.add(new ExcelExportEntity("序号", "no", 10));
if (Lang.isNotEmpty(columns)) {
columns.forEach(column -> exportEntities.add(new ExcelExportEntity(column.get("label"), column.get("prop"), 20)));
}
exportEntities.add(new ExcelExportEntity("更新时间", "changeTime", 25));
exportEntities.add(new ExcelExportEntity("异动类型", "changeTypesText", 30));
return exportEntities;
}
/**
* 构建变更记录列表 SQL,分页查询和导出查询共用该方法,避免筛选条件不一致。
*
* @param pageForm 变更记录查询参数
* @return 变更记录查询 SQL
*/
private Sql getMemberHistoryRecordSql(MemberManagePageForm pageForm) {
Sql sql = Sqls.create("""
SELECT
his.id,
@@ -206,7 +256,31 @@ public class MemberManageServiceImpl extends BaseServiceImpl<Sys_user> implement
cnd.andEX("his.unionId", "=", SecurityUtil.getUnionId());
}
sql.setCondition(cnd);
return listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
return sql;
}
/**
* 将数据库保存的异动类型 JSON 数组转换为页面展示的中文名称,供导出 Excel 使用。
*
* @param changeTypes 数据库中的异动类型值
* @param changeTypeNameMap 异动类型枚举名称映射
* @return 逗号分隔的异动类型名称
*/
private String getChangeTypesText(Object changeTypes, Map<String, String> changeTypeNameMap) {
if (changeTypes == null) {
return null;
}
List<String> changeTypeList;
if (changeTypes instanceof Collection<?> collection) {
changeTypeList = collection.stream().map(String::valueOf).toList();
} else if (StrUtil.isNotBlank(String.valueOf(changeTypes))) {
changeTypeList = JSONUtil.parseArray(String.valueOf(changeTypes)).toList(String.class);
} else {
return null;
}
return changeTypeList.stream()
.map(type -> changeTypeNameMap.getOrDefault(type, type))
.collect(Collectors.joining(","));
}
@@ -90,6 +90,8 @@ public class MemberStatisticsServiceImpl extends BaseServiceImpl<Sys_user> imple
handleField("unitId", pageForm.getUnitId(), reverseSelection, IN_OR_NIN_OP, cnd);
handleField("personType", pageForm.getPersonTypes(), reverseSelection, IN_OR_NIN_OP, cnd);
handleField("preparedBy", pageForm.getPreparedBys(), reverseSelection, IN_OR_NIN_OP, cnd);
// 编制信息前端使用独立字典传值,实际对应用户表/视图中的 compilationInformation 字段。
handleField("compilationInformation", pageForm.getCompilationInformations(), reverseSelection, IN_OR_NIN_OP, cnd);
handleField("userState", pageForm.getUserStates(), reverseSelection, IN_OR_NIN_OP, cnd);
handleField("sex", pageForm.getSexTypes(), reverseSelection, IN_OR_NIN_OP, cnd);
cnd.andEX("sur.roleId", IN_OR_NIN_OP, pageForm.getRoleIds());
@@ -145,6 +147,7 @@ public class MemberStatisticsServiceImpl extends BaseServiceImpl<Sys_user> imple
u.arrivalAtSchoolDate,
u.personType,
u.preparedBy,
u.compilationInformation,
u.identityType,
u.userState,
u.unionName,
@@ -41,7 +41,6 @@ const basicForm = {
<el-col :span="12">
<el-form-item :rules="{required:true,message: '请选择活动时间', trigger: 'blur'}" label="活动时间" prop="activityTime">
<el-date-picker
:picker-options="pickerOptions"
end-placeholder="请选择活动结束日期"
range-separator="至"
start-placeholder="请选择活动开始日期"
@@ -41,7 +41,6 @@ const basicForm = {
<el-col :span="12">
<el-form-item :rules="{required:true,message: '请选择活动时间', trigger: 'blur'}" label="活动时间" prop="activityTime">
<el-date-picker
:picker-options="pickerOptions"
end-placeholder="请选择活动结束日期"
range-separator="至"
start-placeholder="请选择活动开始日期"
@@ -39,7 +39,9 @@ layout("/layouts/platform.html"){
</el-card>
<el-card shadow="never" class="mt10">
<table-tool label="变更列表" :app="this"></table-tool>
<table-tool label="变更列表" :app="this">
<el-button icon="el-icon-printer" type="primary" size="small" @click="doExport">导出</el-button>
</table-tool>
<el-table :data="tableData" style="width: 100%" row-key="id"
@sort-change="pageOrder"
resizable
@@ -189,6 +191,19 @@ layout("/layouts/platform.html"){
})
},
doExport() {
const pageForm = clone(this.pageForm)
pageForm.userStates = JSON.stringify(pageForm.userStates)
pageForm.personTypes = JSON.stringify(pageForm.personTypes)
pageForm.changeTypes = JSON.stringify(pageForm.changeTypes)
pageForm.changeOrigins = JSON.stringify(pageForm.changeOrigins)
// 导出字段取当前页面列表列,并补充后端固定处理的异动信息列。
pageForm.columns = JSON.stringify(this.tableColumns.map((item) => {
return {prop: item.prop, label: item.label}
}))
this.$downLoad(loc() + "/doExport", pageForm)
},
},
async created() {
@@ -160,6 +160,7 @@ layout("/layouts/platform.html"){
{ prop: "userState", label: "在职状态", width: 120, sortable: true },
{ prop: "personType", label: "教职工类别", width: 120, sortable: true },
{ prop: "preparedBy", label: "编制类别", width: 120, sortable: true },
{ prop: "compilationInformation", label: "编制信息", width: 120, sortable: true },
{ prop: "postDoctoralJoinDate", label: "进站时间", sortable: true, checked: 0 },
{ prop: "unionName", label: "所属工会", width: 120, sortable: true },
{ prop: "unitName", label: "所属单位", width: 120, sortable: true },
@@ -201,6 +202,8 @@ layout("/layouts/platform.html"){
pageForm.sexTypes = JSON.stringify(this.pageForm.sexTypes)
pageForm.memberTypes = JSON.stringify(this.pageForm.memberTypes)
pageForm.personTypes = JSON.stringify(this.pageForm.personTypes)
pageForm.preparedBys = JSON.stringify(this.pageForm.preparedBys)
pageForm.compilationInformations = JSON.stringify(this.pageForm.compilationInformations)
pageForm.userStates = JSON.stringify(this.pageForm.userStates)
const data = this.tableColumns.filter(item => this.checkedFields.includes(item.prop)).map(item => {
return {prop: item.prop, label: item.label}
@@ -225,6 +228,7 @@ layout("/layouts/platform.html"){
pageForm.memberTypes = JSON.stringify(this.pageForm.memberTypes)
pageForm.personTypes = JSON.stringify(this.pageForm.personTypes)
pageForm.preparedBys = JSON.stringify(this.pageForm.preparedBys)
pageForm.compilationInformations = JSON.stringify(this.pageForm.compilationInformations)
pageForm.memberStatus = JSON.stringify(this.pageForm.memberStatus)
pageForm.userStates = JSON.stringify(this.pageForm.userStates)
if (pageForm.birthdayRange && pageForm.birthdayRange.length > 0) {
@@ -112,6 +112,31 @@ const MEMBER_STATISTICS_COMPREHENSIVE_QUERY_FORM = {
</el-col>
</el-row>
<el-row type="flex" align="middle" class="query-row query-row-tag">
<el-col class="query-row-title">编制信息:</el-col>
<el-col class="query-row-content query-row-content-tag">
<el-tag
style="margin-right: 10px;cursor: pointer"
v-for="item in compilationInformationOptions"
:key="item.code"
:type="item.name"
:effect="pageForm.compilationInformations.includes(item.name)?'dark':'plain'"
@click="tagClick('compilationInformations',item.name)">
{{ item.name }}
</el-tag>
<el-link type="danger"
v-if="compilationInformationOptions.length&&pageForm.compilationInformations.length"
:underline="false"
@click="pageForm.compilationInformations=[];doSearch()">清空
</el-link>
<el-link :underline="false"
@click="pageForm.compilationInformations=compilationInformationOptions.map(p=>p.code);doSearch()"
type="success">全部
</el-link>
</el-col>
</el-row>
<el-row type="flex" align="middle" class="query-row">
<el-col class="query-row-content">
<el-row>
@@ -208,6 +233,7 @@ const MEMBER_STATISTICS_COMPREHENSIVE_QUERY_FORM = {
searchName: "username",
personTypes: [],
preparedBys: [],
compilationInformations: [],
userStates: [],
memberStatus: [],
year: moment().format("YYYY"),
@@ -224,6 +250,7 @@ const MEMBER_STATISTICS_COMPREHENSIVE_QUERY_FORM = {
},
personTypeOptions: [],
preparedByOptions: [],
compilationInformationOptions: [],
userStateOptions: [],
memberStatusOptions: [],
unions: [],
@@ -315,6 +342,7 @@ const MEMBER_STATISTICS_COMPREHENSIVE_QUERY_FORM = {
this.pageForm.sexTypes = []
this.pageForm.personTypes = []
this.pageForm.preparedBys = []
this.pageForm.compilationInformations = []
this.pageForm.memberStatus = []
this.pageForm.userStates = []
this.pageForm.unionId = []
@@ -352,6 +380,10 @@ const MEMBER_STATISTICS_COMPREHENSIVE_QUERY_FORM = {
this.$businessTool.getDictOptions("USER_PREPARED_BY_TYPE").then((data) => {
this.preparedByOptions = data
})
// 编制信息使用独立字典,后端按 sys_user.compilationInformation 字段进行筛选。
this.$businessTool.getDictOptions("USER_COMPILATION_INFORMATION").then((data) => {
this.compilationInformationOptions = data
})
this.$businessTool.getDictOptions("USER_STATE").then((data) => {
this.userStateOptions = data
})