commit
This commit is contained in:
+145
-3
@@ -2,10 +2,14 @@ package com.budwk.app.zhgh.club.controller.infoManage;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.util.URLUtil;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import com.budwk.app.base.page.Pagination;
|
||||
import com.budwk.app.base.result.Result;
|
||||
import com.budwk.app.base.utils.PageUtil;
|
||||
import com.budwk.app.flow.enums.ProcessTaskStateEnum;
|
||||
import com.budwk.app.sys.models.Sys_file;
|
||||
import com.budwk.app.sys.utils.SysFileMinIoUtil;
|
||||
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
|
||||
import com.budwk.app.zhgh.club.param.ClubUserPageForm;
|
||||
import com.budwk.app.zhgh.club.service.SysClubInfoManageService;
|
||||
@@ -14,12 +18,24 @@ import org.nutz.dao.Sqls;
|
||||
import org.nutz.dao.sql.Sql;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.json.Json;
|
||||
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 org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.validation.Valid;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
/**
|
||||
* @Author: JyuHsin
|
||||
@@ -32,6 +48,8 @@ import java.util.List;
|
||||
@At("/platform/club/infoManage/schoolAuditReport")
|
||||
public class ClubSchoolAuditReportController {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(ClubSchoolAuditReportController.class);
|
||||
|
||||
@Inject
|
||||
private SysClubInfoManageService clubInfoManageService;
|
||||
|
||||
@@ -44,6 +62,71 @@ public class ClubSchoolAuditReportController {
|
||||
@SaCheckPermission("club.infoManage.schoolAuditReport")
|
||||
public Result pageData(@Valid ClubUserPageForm pageForm,
|
||||
@Param(value = "approval") Boolean approval) {
|
||||
Sql sql = buildPageDataSql(pageForm, approval);
|
||||
Pagination pagination = clubInfoManageService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
|
||||
return Result.success(pagination);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出当前筛选范围内的换届报告附件压缩包。
|
||||
*
|
||||
* @param pageForm 页面查询条件
|
||||
* @param approval 审核状态,与列表页保持一致
|
||||
* @param response HTTP 下载响应
|
||||
*/
|
||||
@At
|
||||
@Ok("void")
|
||||
@SaCheckPermission("club.infoManage.schoolAuditReport")
|
||||
public void exportRefreshReportZip(@Valid ClubUserPageForm pageForm,
|
||||
@Param(value = "approval") Boolean approval,
|
||||
HttpServletResponse response) throws IOException {
|
||||
List<NutMap> reportList = clubInfoManageService.listMap(buildPageDataSql(pageForm, approval));
|
||||
response.setContentType("application/zip");
|
||||
response.setHeader("Content-Disposition", "attachment;filename=" + URLUtil.encode("换届报告.zip"));
|
||||
|
||||
// 显式指定 UTF-8,确保压缩包内中文目录和文件名正常显示。
|
||||
try (ZipOutputStream zipOutputStream = new ZipOutputStream(new BufferedOutputStream(response.getOutputStream()), StandardCharsets.UTF_8)) {
|
||||
Set<String> entryNames = new HashSet<>();
|
||||
for (NutMap report : reportList) {
|
||||
String filesJson = report.getString("files");
|
||||
if (StrUtil.isBlank(filesJson)) {
|
||||
continue;
|
||||
}
|
||||
List<JSONObject> files = Json.fromJsonAsList(JSONObject.class, filesJson);
|
||||
String folderName = buildFolderName(report);
|
||||
for (int index = 0; index < files.size(); index++) {
|
||||
JSONObject fileInfo = files.get(index);
|
||||
Sys_file sysFile = findSysFile(fileInfo);
|
||||
if (sysFile == null) {
|
||||
log.warn("换届报告附件不存在,报告ID:{},附件:{}", report.getString("id"), fileInfo);
|
||||
continue;
|
||||
}
|
||||
byte[] fileBytes = SysFileMinIoUtil.getFileBytes(sysFile.getBucket(), sysFile.getStoragePath());
|
||||
if (fileBytes == null || fileBytes.length == 0) {
|
||||
log.warn("换届报告附件内容为空,报告ID:{},附件ID:{}", report.getString("id"), sysFile.getId());
|
||||
continue;
|
||||
}
|
||||
String fileName = StrUtil.blankToDefault(fileInfo.getStr("name"), fileInfo.getStr("filename"));
|
||||
if (StrUtil.isBlank(fileName)) {
|
||||
fileName = sysFile.getName();
|
||||
}
|
||||
String entryName = buildUniqueEntryName(folderName + "/" + sanitizeFileName(fileName), entryNames);
|
||||
zipOutputStream.putNextEntry(new ZipEntry(entryName));
|
||||
zipOutputStream.write(fileBytes);
|
||||
zipOutputStream.closeEntry();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建列表与导出共用的换届报告查询,确保“查什么导什么”。
|
||||
*
|
||||
* @param pageForm 页面查询条件
|
||||
* @param approval 审核状态
|
||||
* @return 已绑定筛选条件的查询对象
|
||||
*/
|
||||
private Sql buildPageDataSql(ClubUserPageForm pageForm, Boolean approval) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
info.*,
|
||||
@@ -79,7 +162,7 @@ public class ClubSchoolAuditReportController {
|
||||
|
||||
cnd.and("t.taskName", "=", "d4323546-8d09-419e-88d4-7b15862ca29d");
|
||||
cnd.and("ta.actorId", "in", List.of(SecurityUtil.getUserId()));
|
||||
if (approval) {
|
||||
if (Boolean.TRUE.equals(approval)) {
|
||||
cnd.and("t.taskState", "in", List.of(ProcessTaskStateEnum.FINISHED.getCode(), ProcessTaskStateEnum.INTERRUPT.getCode()));
|
||||
} else {
|
||||
cnd.and("t.taskState", "=", ProcessTaskStateEnum.DOING.getCode());
|
||||
@@ -96,8 +179,67 @@ public class ClubSchoolAuditReportController {
|
||||
|
||||
cnd.groupBy("t.id");
|
||||
sql.setCondition(cnd);
|
||||
return sql;
|
||||
}
|
||||
|
||||
Pagination pagination = clubInfoManageService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
|
||||
return Result.success(pagination);
|
||||
/**
|
||||
* 兼容新旧附件 JSON 结构,按附件 ID 或下载地址查找文件元数据。
|
||||
*
|
||||
* @param fileInfo 附件 JSON 信息
|
||||
* @return 系统文件记录;未找到时返回 null
|
||||
*/
|
||||
private Sys_file findSysFile(JSONObject fileInfo) {
|
||||
String fileId = fileInfo.getStr("id");
|
||||
if (StrUtil.isNotBlank(fileId)) {
|
||||
return clubInfoManageService.dao().fetch(Sys_file.class, fileId);
|
||||
}
|
||||
String fileUrl = fileInfo.getStr("url");
|
||||
if (StrUtil.isBlank(fileUrl)) {
|
||||
return null;
|
||||
}
|
||||
return clubInfoManageService.dao().fetch(Sys_file.class, Cnd.where(Sys_file::getDownloadPath, "=", fileUrl));
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用社团名称建立目录,方便按换届报告来源查阅。
|
||||
*
|
||||
* @param report 换届报告记录
|
||||
* @return Zip 内目录名称
|
||||
*/
|
||||
private String buildFolderName(NutMap report) {
|
||||
return sanitizeFileName(report.getString("clubName"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 保留附件原始文件名;同一社团目录存在同名文件时追加序号,避免覆盖。
|
||||
*
|
||||
* @param entryName 原始 Zip 条目名称
|
||||
* @param entryNames 已写入的 Zip 条目名称
|
||||
* @return 唯一的 Zip 条目名称
|
||||
*/
|
||||
private String buildUniqueEntryName(String entryName, Set<String> entryNames) {
|
||||
if (entryNames.add(entryName)) {
|
||||
return entryName;
|
||||
}
|
||||
int extensionIndex = entryName.lastIndexOf('.');
|
||||
String name = extensionIndex > entryName.lastIndexOf('/') ? entryName.substring(0, extensionIndex) : entryName;
|
||||
String extension = extensionIndex > entryName.lastIndexOf('/') ? entryName.substring(extensionIndex) : "";
|
||||
int index = 2;
|
||||
String uniqueEntryName;
|
||||
do {
|
||||
uniqueEntryName = name + "(" + index + ")" + extension;
|
||||
index++;
|
||||
} while (!entryNames.add(uniqueEntryName));
|
||||
return uniqueEntryName;
|
||||
}
|
||||
|
||||
/**
|
||||
* 去除 Zip 条目路径分隔符,避免附件名影响压缩包目录结构。
|
||||
*
|
||||
* @param fileName 原始文件名
|
||||
* @return 可安全写入 Zip 的文件名
|
||||
*/
|
||||
private String sanitizeFileName(String fileName) {
|
||||
return StrUtil.blankToDefault(fileName, "未命名文件").replace("/", "_").replace("\\", "_");
|
||||
}
|
||||
}
|
||||
|
||||
+13
-3
@@ -9,9 +9,11 @@ import cn.dev33.satoken.annotation.SaMode;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.budwk.app.base.annotation.SLog;
|
||||
import com.budwk.app.base.constant.RoleConstant;
|
||||
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.web.commons.auth.utils.AuthUtil;
|
||||
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
|
||||
import com.budwk.app.zhgh.dayofficework.asset.model.Asset;
|
||||
import com.budwk.app.zhgh.dayofficework.asset.model.AssetDepreciationRecord;
|
||||
@@ -27,6 +29,7 @@ import org.nutz.aop.interceptor.ioc.TransAop;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Sqls;
|
||||
import org.nutz.dao.sql.Sql;
|
||||
import org.nutz.dao.util.cri.SqlExpressionGroup;
|
||||
import org.nutz.ioc.aop.Aop;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
@@ -157,9 +160,16 @@ public class AssetManageController {
|
||||
@SaCheckPermission(value = {"asset.manage", "asset.stocktaking", "h5.asset.stocktaking"}, mode = SaMode.OR)
|
||||
public Result searchAssetUseUser(String query) {
|
||||
Cnd cnd = Cnd.NEW();
|
||||
cnd.where().orLike("username", query);
|
||||
cnd.where().orLike("loginname", query);
|
||||
cnd.where().orLike("id", query);
|
||||
// 三个关键词条件必须整体分组,避免 OR 条件绕过分工会数据范围。
|
||||
SqlExpressionGroup searchGroup = new SqlExpressionGroup();
|
||||
searchGroup.orLike("username", query);
|
||||
searchGroup.orLike("loginname", query);
|
||||
searchGroup.orLike("id", query);
|
||||
cnd.and(searchGroup);
|
||||
// 分工会侧盘点人员只能选择本分工会成员,校工会和系统管理员保留全校查询范围。
|
||||
if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name())) {
|
||||
cnd.and("unionId", "=", SecurityUtil.getUnionId());
|
||||
}
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
id,
|
||||
|
||||
Reference in New Issue
Block a user