协会导出

This commit is contained in:
=
2026-05-13 10:31:47 +08:00
parent c1ce122a70
commit f50c7eba89
8 changed files with 214 additions and 7 deletions
@@ -1,6 +1,7 @@
package io.v.nutz.sys.services;
import cn.wizzer.framework.base.service.BaseService;
import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
import io.v.nutz.web.controllers.platform.sys.club.ClubUserImportFeature;
import io.v.nutz.sys.models.Sys_club_user;
import org.nutz.lang.util.NutMap;
@@ -26,4 +27,27 @@ public interface SysClubUserService extends BaseService<Sys_club_user> {
* @return 错误记录列表
*/
List<ClubUserImportFeature.ErrorRow> getImportErrorList(String cacheKey);
/**
* 查询指定协会已审核通过的正常成员,用于协会成员导出。
*
* @param clubid 协会id
* @return 成员导出数据
*/
List<NutMap> listClubMembersForExport(String clubid, String searchName, String searchKeyword);
/**
* 查询所有协会已审核通过的正常成员,用于校工会全量导出。
*
* @return 成员导出数据
*/
List<NutMap> listAllClubMembersForExport();
/**
* 构建协会成员导出列,两个导出入口共用同一份字段定义。
*
* @param includeClubName 是否包含协会名称列
* @return Excel导出列
*/
List<ExcelExportEntity> buildClubMemberExportEntities(boolean includeClubName);
}
@@ -1,6 +1,8 @@
package io.v.nutz.sys.services.impl;
import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
import cn.hutool.core.convert.Convert;
import cn.wizzer.framework.base.service.BaseServiceImpl;
import io.v.nutz.base.utils.DateUtil;
import io.v.nutz.sys.models.Sys_club;
@@ -12,6 +14,7 @@ import io.v.nutz.sys.services.SysUserService;
import io.v.nutz.web.controllers.platform.sys.club.ClubUserImportFeature;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.sql.Sql;
import org.nutz.integration.jedis.RedisService;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.ioc.loader.annotation.Inject;
@@ -133,8 +136,9 @@ public class SysClubUserServiceImpl extends BaseServiceImpl<Sys_club_user> imple
continue;
}
if (!username.equals(Strings.sNull(user.getUsername()).trim())) {
errorRow.setErrorMessage("姓名与系统中的用户姓名不一致");
String systemUsername = Strings.sNull(user.getUsername()).trim();
if (!normalizeUsername(username).equals(normalizeUsername(systemUsername))) {
errorRow.setErrorMessage("姓名与系统中的用户姓名不一致,导入姓名:" + username + ",系统姓名:" + systemUsername);
errorRows.add(errorRow);
continue;
}
@@ -194,6 +198,98 @@ public class SysClubUserServiceImpl extends BaseServiceImpl<Sys_club_user> imple
return Json.fromJsonAsList(ClubUserImportFeature.ErrorRow.class, json);
}
/**
* 姓名导入比对时去除空白并统一常见全角半角、点号差异,避免人工看起来一致但字符不同导致误判。
*/
private String normalizeUsername(String username) {
return Convert.toDBC(Strings.sNull(username))
.replaceAll("\\s+", "")
.replace(" ", "")
.replace("•", "·")
.replace(".", "·")
.replace(".", "·")
.trim();
}
@Override
public List<NutMap> listClubMembersForExport(String clubid, String searchName, String searchKeyword) {
if (Strings.isBlank(clubid)) {
return Collections.emptyList();
}
Cnd cnd = Cnd.where("scu.clubid", "=", clubid);
if (Strings.isNotBlank(searchName) && Strings.isNotBlank(searchKeyword)) {
cnd.and(searchName, "like", "%" + searchKeyword + "%");
}
return listClubMembersForExportByCnd(cnd,
"ORDER BY FIELD(scu.sf, 1, 3, 4, 5, 6, 2), u.loginname");
}
@Override
public List<NutMap> listAllClubMembersForExport() {
return listClubMembersForExportByCnd(Cnd.NEW(),
"ORDER BY club.name ASC, FIELD(scu.sf, 1, 3, 4, 5, 6, 2), u.loginname");
}
@Override
public List<ExcelExportEntity> buildClubMemberExportEntities(boolean includeClubName) {
List<ExcelExportEntity> exportEntities = new ArrayList<>();
if (includeClubName) {
exportEntities.add(new ExcelExportEntity("协会名称", "clubname", 25));
}
exportEntities.add(new ExcelExportEntity("工号", "loginname", 18));
exportEntities.add(new ExcelExportEntity("姓名", "username", 18));
exportEntities.add(new ExcelExportEntity("性别", "sex", 12));
exportEntities.add(new ExcelExportEntity("电话", "mobile", 18));
exportEntities.add(new ExcelExportEntity("所属单位", "unitname", 25));
exportEntities.add(new ExcelExportEntity("身份", "sfName", 15));
exportEntities.add(new ExcelExportEntity("入会时间", "joinTime", 18));
exportEntities.add(new ExcelExportEntity("申请状态", "statusName", 18));
return exportEntities;
}
/**
* 按导出字段查询成员,并把身份、入会时间转换成页面和Excel需要的文本。
*/
private List<NutMap> listClubMembersForExportByCnd(Cnd cnd, String orderBy) {
Sql sql = org.nutz.dao.Sqls.create("SELECT\n" +
"\tclub.name clubname,\n" +
"\tu.loginname,\n" +
"\tu.username,\n" +
"\tu.sex,\n" +
"\tu.mobile,\n" +
"\tunit.name unitname,\n" +
"\tCASE scu.sf\n" +
"\t\tWHEN 1 THEN '会长'\n" +
"\t\tWHEN 3 THEN '副会长'\n" +
"\t\tWHEN 4 THEN '秘书长'\n" +
"\t\tWHEN 5 THEN '副秘书长'\n" +
"\t\tWHEN 6 THEN '经办人'\n" +
"\t\tELSE '会员'\n" +
"\tEND sfName,\n" +
"\tCASE WHEN scu.join_time IS NULL OR scu.join_time = '' THEN '' ELSE LEFT(scu.join_time, 10) END joinTime,\n" +
"\tCASE scu.status\n" +
"\t\tWHEN 1 THEN '待协会审核'\n" +
"\t\tWHEN 2 THEN '协会拒绝'\n" +
"\t\tWHEN 3 THEN '待校工会审核'\n" +
"\t\tWHEN 4 THEN '校工会拒绝'\n" +
"\t\tWHEN 5 THEN '审核通过'\n" +
"\t\tELSE ''\n" +
"\tEND statusName\n" +
"FROM sys_club_user scu\n" +
"LEFT JOIN sys_club club ON scu.clubid = club.id\n" +
"LEFT JOIN sys_user u ON scu.userid = u.id\n" +
"LEFT JOIN sys_unit unit ON u.unitid = unit.id\n" +
"$condition " + orderBy);
cnd.and("scu.isNormal", "=", true);
cnd.and("scu.status", "in", new int[]{1, 3, 5});
cnd.and("u.username", "is not", null);
sql.setCondition(cnd);
sql.setCallback(org.nutz.dao.Sqls.callback.maps());
this.dao().execute(sql);
List<NutMap> list = sql.getList(NutMap.class);
return list == null ? Collections.emptyList() : list;
}
/**
* Build one exportable error row from the original import row.
*/
@@ -23,6 +23,7 @@ import org.nutz.mvc.upload.UploadAdaptor;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
/**
@@ -85,4 +86,21 @@ public class ClubInController {
ViTool.excelResponse(response, "协会成员导入错误信息.xlsx");
workbook.write(response.getOutputStream());
}
/**
* 导出当前协会已通过成员,字段与成员列表保持一致但不包含申请状态。
*/
@At
@Ok("void")
@RequiresPermissions("sys.club.clubIn")
public void exportClubMembers(HttpServletResponse response, String clubid, String searchName, String searchKeyword) throws IOException {
ExportParams exportParams = new ExportParams();
exportParams.setType(ExcelType.XSSF);
Workbook workbook = ExcelExportUtil.exportExcel(exportParams,
sysClubUserService.buildClubMemberExportEntities(false),
new ArrayList<>(sysClubUserService.listClubMembersForExport(clubid, searchName, searchKeyword)));
ViTool.excelResponse(response, "协会成员信息.xlsx");
workbook.write(response.getOutputStream());
workbook.close();
}
}
@@ -1,11 +1,22 @@
package io.v.nutz.web.controllers.platform.sys.club;
import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import io.v.nutz.sys.services.SysClubUserService;
import io.v.nutz.util.ViTool;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.shiro.authz.annotation.RequiresAuthentication;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
/**
* @Author JyuHsin
* @Date 2022/4/14
@@ -17,9 +28,29 @@ import org.nutz.mvc.annotation.Ok;
@Ok("json")
public class SchoolInController {
@Inject
private SysClubUserService sysClubUserService;
@At("")
@Ok("beetl:/platform/sys/club/schoolIn.html")
@RequiresPermissions("sys.club.schoolIn")
public void index() {
}
/**
* 导出所有协会已通过成员,包含协会名称并按协会名称、身份排序。
*/
@At
@Ok("void")
@RequiresPermissions("sys.club.schoolIn")
public void exportAllClubMembers(HttpServletResponse response) throws IOException {
ExportParams exportParams = new ExportParams();
exportParams.setType(ExcelType.XSSF);
Workbook workbook = ExcelExportUtil.exportExcel(exportParams,
sysClubUserService.buildClubMemberExportEntities(true),
new ArrayList<>(sysClubUserService.listAllClubMembersForExport()));
ViTool.excelResponse(response, "协会成员信息汇总.xlsx");
workbook.write(response.getOutputStream());
workbook.close();
}
}
@@ -115,7 +115,7 @@ public class SysClubUserCon {
//查询理事机构
if (radioType != null && 1 == radioType) {
cnd.and("scu.sf", "=", "1");
cnd.and("scu.sf", "in", new int[]{1, 3, 4, 5, 6});
}
//查询社团成员
if (radioType != null && 2 == radioType) {
@@ -162,9 +162,11 @@ public class SysClubUserShCon {
auditList.add(NutMap.NEW().addv("auditUser", ShiroUtil.getPrincipalProperty("id")).addv("auditState", flag)
.addv("auditTime", DateUtil.getDateTime()).addv("auditType", type == 1 ? "club" : "school"));
if(applyType == 1) {
sysClubUserService.update(Chain.make("status", status)
.add("join_time", cn.hutool.core.date.DateUtil.now())
.add("auditList", auditList), cnd);
Chain statusChain = Chain.make("status", status).add("auditList", auditList);
if (flag) {
statusChain.add("join_time", DateUtil.getDate());
}
sysClubUserService.update(statusChain, cnd);
} else {
Chain exitStatus = Chain.make("exitStatus", status).add("exitAuditList", auditList);
if(status == 5) {
@@ -262,6 +262,7 @@ layout("/layouts/platform.html"){
<div class="pull-right offscreen-right mt5" v-if="userPageForm.radioType === '2'">
<el-button icon="el-icon-plus" @click="openUserAdd">录入成员</el-button>
<el-button icon="el-icon-download" @click="exportClubMembers">导出</el-button>
</div>
</el-card>
@@ -294,6 +295,13 @@ layout("/layouts/platform.html"){
<el-lable v-if="scope.row.sf==3">副会长</el-lable>
<el-lable v-if="scope.row.sf==4">秘书长</el-lable>
<el-lable v-if="scope.row.sf==5">副秘书长</el-lable>
<el-lable v-if="scope.row.sf==6">经办人</el-lable>
</template>
</el-table-column>
<el-table-column align="center" header-align="center" label="入会时间" prop="join_time">
<template slot-scope="scope">
<span>{{(scope.row.join_time || '').substring(0, 10)}}</span>
</template>
</el-table-column>
@@ -578,6 +586,7 @@ layout("/layouts/platform.html"){
<el-lable v-if="scope.row.sf==3">副会长</el-lable>
<el-lable v-if="scope.row.sf==4">秘书长</el-lable>
<el-lable v-if="scope.row.sf==5">副秘书长</el-lable>
<el-lable v-if="scope.row.sf==6">经办人</el-lable>
</template>
</el-table-column>
<el-table-column label="工号" width="150px" prop="loginname" header-align="center"
@@ -973,6 +982,19 @@ layout("/layouts/platform.html"){
}
window.location.href = base + '/platform/sys/club/clubIn/exportImportErrors?cacheKey=' + this.importResult.cacheKey
},
exportClubMembers() {
if (!this.userPageForm.clubid) {
this.$message({
message: '请先选择协会',
type: 'warning'
})
return
}
window.location.href = base + '/platform/sys/club/clubIn/exportClubMembers?clubid='
+ this.userPageForm.clubid
+ '&searchName=' + encodeURIComponent(this.userPageForm.searchName ? this.userPageForm.searchName : '')
+ '&searchKeyword=' + encodeURIComponent(this.userPageForm.searchKeyword ? this.userPageForm.searchKeyword : '')
},
doUserSearch() {
this.userTabKey = new Date().getTime()
this.userPageForm.pageNumber = 1
@@ -116,6 +116,9 @@ layout("/layouts/platform.html"){
<div class="btn-group tool-button mt5">
<el-button type="primary" icon="el-icon-search" @click="doSearch"></el-button>
</div>
<div class="pull-right offscreen-right mt5">
<el-button icon="el-icon-download" @click="exportAllClubMembers">导出</el-button>
</div>
<!--<div class="pull-right offscreen-right mt5">
&lt;!&ndash;#if(@shiro.hasRole('sysadmin')||@shiro.hasRole('A06')){#&ndash;&gt;
@@ -292,6 +295,13 @@ layout("/layouts/platform.html"){
<el-lable v-if="scope.row.sf==3">副会长</el-lable>
<el-lable v-if="scope.row.sf==4">秘书长</el-lable>
<el-lable v-if="scope.row.sf==5">副秘书长</el-lable>
<el-lable v-if="scope.row.sf==6">经办人</el-lable>
</template>
</el-table-column>
<el-table-column align="center" header-align="center" label="入会时间" prop="join_time">
<template slot-scope="scope">
<span>{{(scope.row.join_time || '').substring(0, 10)}}</span>
</template>
</el-table-column>
@@ -576,6 +586,7 @@ layout("/layouts/platform.html"){
<el-lable v-if="scope.row.sf==3">副会长</el-lable>
<el-lable v-if="scope.row.sf==4">秘书长</el-lable>
<el-lable v-if="scope.row.sf==5">副秘书长</el-lable>
<el-lable v-if="scope.row.sf==6">经办人</el-lable>
</template>
</el-table-column>
<el-table-column label="工号" width="150px" prop="loginname" header-align="center"
@@ -826,6 +837,9 @@ layout("/layouts/platform.html"){
})
},
exportAllClubMembers() {
window.location.href = base + '/platform/sys/club/schoolIn/exportAllClubMembers'
},
doUserSearch() {
this.userTabKey = new Date().getTime()
this.userPageForm.pageNumber = 1
@@ -1265,4 +1279,4 @@ layout("/layouts/platform.html"){
</script>
<!--#
}
#-->
#-->