diff --git a/src/main/java/com/budwk/app/flow/controller/FlowDesignController.java b/src/main/java/com/budwk/app/flow/controller/FlowDesignController.java index d0814f1f..8af02627 100644 --- a/src/main/java/com/budwk/app/flow/controller/FlowDesignController.java +++ b/src/main/java/com/budwk/app/flow/controller/FlowDesignController.java @@ -18,6 +18,7 @@ import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; import org.nutz.aop.interceptor.ioc.TransAop; +import org.nutz.dao.Chain; import org.nutz.dao.Cnd; import org.nutz.dao.Dao; import org.nutz.dao.Sqls; @@ -110,14 +111,39 @@ public class FlowDesignController { @ApiOperation("获取流程设计分页列表") @SaCheckPermission("flow.design") public Result pageData(PageForm pageForm, String displayName, String name, String category, Boolean deployed) { + Sql sql = Sqls.create(""" + SELECT + pd.* + FROM + wf_process_design pd + LEFT JOIN wf_process_category pc ON pc.id = pd.category + $condition + """); Cnd cnd = Cnd.NEW(); - cnd.and(Cnd.likeEX(ProcessDesign::getDisplayName, displayName)); - cnd.and(Cnd.likeEX(ProcessDesign::getName, name)); - cnd.andEX(ProcessDesign::getCategory, "=", category); - cnd.andEX(ProcessDesign::getIsDeployed, "=", deployed); + cnd.and(Cnd.likeEX("pd.displayName", displayName)); + cnd.and(Cnd.likeEX("pd.name", name)); + cnd.andEX("pd.category", "=", category); + cnd.andEX("pd.isDeployed", "=", deployed); + if ("sortNo".equals(pageForm.getPageOrderName())) { + if ("descending".equals(pageForm.getPageOrderBy())) { + cnd.desc("pd.sortNo"); + } else { + cnd.asc("pd.sortNo"); + } + } else if ("category".equals(pageForm.getPageOrderName())) { + if ("descending".equals(pageForm.getPageOrderBy())) { + cnd.desc("pc.name"); + } else { + cnd.asc("pc.name"); + } + } else { + cnd.asc("pd.sortNo"); + } + cnd.asc("pd.id"); - Pagination pagination = processDesignService.listPage(pageForm.getPageNumber(), pageForm.getPageSize(), cnd); + sql.setCondition(cnd); + Pagination pagination = processDesignService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql); return Result.success(pagination); } @@ -206,6 +232,17 @@ public class FlowDesignController { return Result.success(); } + @At + @ApiOperation("修改流程设计排序编码") + @SaCheckPermission("flow.design") + public Result updateSortNo(@Param("id") Long id, @Param("sortNo") Integer sortNo) { + if (id == null || sortNo == null || sortNo < 0) { + return Result.error("排序编码必须是非负整数"); + } + processDesignService.update(Chain.make("sortNo", sortNo), Cnd.where(ProcessDesign::getId, "=", id)); + return Result.success(); + } + @At @SaCheckLogin @ApiOperation("获取流程设计任务参与者处理类") diff --git a/src/main/java/com/budwk/app/flow/entity/ProcessDesign.java b/src/main/java/com/budwk/app/flow/entity/ProcessDesign.java index 065c04b2..1b63eb68 100644 --- a/src/main/java/com/budwk/app/flow/entity/ProcessDesign.java +++ b/src/main/java/com/budwk/app/flow/entity/ProcessDesign.java @@ -68,6 +68,11 @@ public class ProcessDesign extends BaseModel { @Column private Integer isDeployed; + @Comment("排序编码") + @Column + @Default("0") + private Integer sortNo; + @Comment("备注") @Column private String remark; diff --git a/src/main/java/com/budwk/app/flow/handler/FlowSchoolUnionActivityAdminHandler.java b/src/main/java/com/budwk/app/flow/handler/FlowSchoolUnionActivityAdminHandler.java new file mode 100644 index 00000000..62f22786 --- /dev/null +++ b/src/main/java/com/budwk/app/flow/handler/FlowSchoolUnionActivityAdminHandler.java @@ -0,0 +1,44 @@ +package com.budwk.app.flow.handler; + +import com.budwk.app.base.constant.RoleConstant; +import com.budwk.app.flow.engine.AssignmentHandler; +import com.budwk.app.flow.engine.core.Execution; +import com.budwk.app.flow.engine.core.ServiceContext; +import com.budwk.app.flow.engine.model.TaskModel; +import org.nutz.dao.Dao; +import org.nutz.dao.Sqls; +import org.nutz.dao.sql.Sql; + +import java.util.List; + +public class FlowSchoolUnionActivityAdminHandler implements AssignmentHandler { + + @Override + public List assign(TaskModel model, Execution execution) { + Dao dao = ServiceContext.find(Dao.class); + Sql sql = Sqls.create(""" + SELECT + userRole.userId + FROM + `sys_user_role` userRole + LEFT JOIN sys_role role ON role.id = userRole.roleId + WHERE + role.`code` = @roleCode + GROUP BY + userRole.userId + """).setParam("roleCode", RoleConstant.SCHOOL_UNION_ACTIVITY_ADMIN.name()); + sql.setCallback(Sqls.callback.strList()); + dao.execute(sql); + return sql.getList(String.class); + } + + @Override + public String getMessage() { + return "\u83b7\u53d6\u6821\u5de5\u4f1a\u6d3b\u52a8\u7ba1\u7406\u5458"; + } + + @Override + public int getOrder() { + return AssignmentHandler.super.getOrder(); + } +} diff --git a/src/main/java/com/budwk/app/sys/controller/SysDataToolController.java b/src/main/java/com/budwk/app/sys/controller/SysDataToolController.java new file mode 100644 index 00000000..3e9fa87c --- /dev/null +++ b/src/main/java/com/budwk/app/sys/controller/SysDataToolController.java @@ -0,0 +1,451 @@ +package com.budwk.app.sys.controller; + +import cn.afterturn.easypoi.excel.ExcelExportUtil; +import cn.afterturn.easypoi.excel.annotation.Excel; +import cn.afterturn.easypoi.excel.entity.ExportParams; +import cn.dev33.satoken.annotation.SaCheckPermission; +import cn.hutool.core.util.StrUtil; +import com.budwk.app.base.annotation.SLog; +import com.budwk.app.base.result.Result; +import com.budwk.app.base.utils.CommonDownloadUtil; +import com.budwk.app.sys.enums.SysDataImportPlugin; +import com.budwk.app.sys.services.SysRoleService; +import com.budwk.app.sys.services.SysUserService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellType; +import org.apache.poi.ss.usermodel.DateUtil; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.WorkbookFactory; +import org.nutz.dao.Chain; +import org.nutz.dao.Cnd; +import org.nutz.dao.Sqls; +import org.nutz.dao.entity.Record; +import org.nutz.dao.sql.Sql; +import org.nutz.integration.jedis.RedisService; +import org.nutz.ioc.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.json.Json; +import org.nutz.lang.random.R; +import org.nutz.lang.util.NutMap; +import org.nutz.mvc.annotation.AdaptBy; +import org.nutz.mvc.annotation.At; +import org.nutz.mvc.annotation.Ok; +import org.nutz.mvc.annotation.Param; +import org.nutz.mvc.upload.TempFile; +import org.nutz.mvc.upload.UploadAdaptor; + +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +@Slf4j +@IocBean +@Ok("json:full") +@At("/platform/sys/data/tool") +@Api("系统数据高级工具") +public class SysDataToolController { + + private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + + @Inject + private SysUserService sysUserService; + @Inject + private SysRoleService sysRoleService; + @Inject + private RedisService redisService; + + @At("") + @Ok("beetl:/platform/sys/data/tool/index.html") + @SaCheckPermission("sys.data.advanced.tool") + public void index() { + } + + @At + @ApiOperation("查询数据库中的表") + @SaCheckPermission("sys.data.advanced.tool") + public Result tableNames() { + Sql sql = Sqls.create(""" + SELECT + table_name, + table_rows, + table_comment + FROM + information_schema.TABLES + WHERE + table_schema = (SELECT DATABASE()) + ORDER BY + table_name + """); + return Result.success(sysUserService.listMap(sql)); + } + + @At + @ApiOperation("查询数据表字段") + @SaCheckPermission("sys.data.advanced.tool") + public Result columns(String tableName) { + if (StrUtil.isBlank(tableName)) { + return Result.error("请选择数据表"); + } + return Result.success(getColumns(tableName)); + } + + @At + @ApiOperation("执行高级导入") + @SLog(tag = "数据管理-高级工具", msg = "执行高级导入") + @SaCheckPermission("sys.data.advanced.tool") + @AdaptBy(type = UploadAdaptor.class, args = {"ioc:fileUpload"}) + public Result importData(@Param("file") TempFile file, + String tableName, + String relation, + Integer method, + String field, + String plugins) throws IOException { + if (file == null) { + return Result.error("请上传 Excel 文件"); + } + if (StrUtil.isBlank(tableName)) { + return Result.error("请选择数据表"); + } + if (method == null) { + return Result.error("请选择导入方式"); + } + if ((method == 1 || method == 3) && StrUtil.isBlank(field)) { + return Result.error("请选择关键字段"); + } + if (StrUtil.isBlank(relation)) { + return Result.error("请配置字段对应关系"); + } + + List columns = getColumns(tableName); + if (columns.isEmpty()) { + return Result.error("未获取到数据表字段"); + } + + NutMap[] relations = parseRelations(relation); + List dataList = getDataForExcel(file, relations); + List pluginList = parsePlugins(plugins); + pluginList.sort(Comparator.comparingInt(SysDataImportPlugin::getLocation)); + Record pkRecord = getPrimaryKey(columns).orElse(null); + Set tableColumns = new HashSet<>(); + columns.forEach(column -> tableColumns.add(column.getString("column_name"))); + + int total = 0; + int success = 0; + List errors = new ArrayList<>(); + + for (NutMap row : dataList) { + total++; + try { + if (StrUtil.isNotBlank(field)) { + row.setv(field, row.getString(field)); + } + + Chain chain = buildChain(row, tableColumns); + if (chain == null) { + continue; + } + + Object pk; + if (method == 1) { + Record currentRecord = sysUserService.dao().fetch(tableName, Cnd.where(field, "=", row.get(field))); + if (currentRecord == null) { + pk = prepareBefore(tableName, pkRecord, field, pluginList, true, chain, row, tableColumns); + sysUserService.dao().insert(tableName, chain); + } else { + pk = prepareBefore(tableName, pkRecord, field, pluginList, false, chain, row, tableColumns); + sysUserService.dao().update(tableName, chain, Cnd.where(field, "=", row.get(field))); + } + } else if (method == 2) { + pk = prepareBefore(tableName, pkRecord, field, pluginList, true, chain, row, tableColumns); + sysUserService.dao().insert(tableName, chain); + } else if (method == 3) { + pk = prepareBefore(tableName, pkRecord, field, pluginList, false, chain, row, tableColumns); + sysUserService.dao().update(tableName, chain, Cnd.where(field, "=", row.get(field))); + } else { + throw new IllegalArgumentException("导入方式不正确"); + } + + applyAfterPlugins(tableName, tableColumns, chain, pk, pluginList); + success++; + } catch (Exception e) { + log.error("高级导入失败, table={}, row={}", tableName, row, e); + errors.add(new ImportError(Json.toJson(row), e.getMessage())); + } + } + + String cacheKey = ""; + if (!errors.isEmpty()) { + cacheKey = R.UU32(); + redisService.setex(cacheKey, 60 * 60 * 5, Json.toJson(errors)); + } + + sysUserService.clearCache(); + sysRoleService.clearCache(); + return Result.success(NutMap.NEW().addv("total", total).addv("success", success).addv("cacheKey", cacheKey)); + } + + @At + @Ok("void") + @ApiOperation("导出错误记录") + @SaCheckPermission("sys.data.advanced.tool") + public void exportErrors(HttpServletResponse response, String cacheKey) { + if (StrUtil.isBlank(cacheKey)) { + CommonDownloadUtil.download("导入错误记录.xlsx", + ExcelExportUtil.exportExcel(new ExportParams(), ImportError.class, new ArrayList<>()), + response); + return; + } + String json = redisService.get(cacheKey); + List errors = StrUtil.isBlank(json) ? new ArrayList<>() : Json.fromJsonAsList(ImportError.class, json); + CommonDownloadUtil.download("导入错误记录.xlsx", + ExcelExportUtil.exportExcel(new ExportParams(), ImportError.class, errors), + response); + } + + private void applyAfterPlugins(String tableName, + Set tableColumns, + Chain chain, + Object pk, + List pluginList) { + if (pluginList.isEmpty()) { + return; + } + SysDataImportPlugin.PluginContext context = new SysDataImportPlugin.PluginContext( + tableName, + tableColumns, + chain, + pk, + sysUserService.dao(), + sysRoleService + ); + pluginList.forEach(plugin -> plugin.after(context)); + } + + private Object prepareBefore(String tableName, + Record pkRecord, + String field, + List pluginList, + boolean save, + Chain chain, + NutMap row, + Set tableColumns) { + Object pk = resolvePrimaryKeyValue(tableName, pkRecord, field, save, chain, row); + SysDataImportPlugin.PluginContext context = new SysDataImportPlugin.PluginContext( + tableName, + tableColumns, + chain, + pk, + sysUserService.dao(), + sysRoleService + ); + pluginList.forEach(plugin -> plugin.before(context)); + return pk; + } + + private Object resolvePrimaryKeyValue(String tableName, + Record pkRecord, + String field, + boolean save, + Chain chain, + NutMap row) { + if (pkRecord == null) { + return null; + } + String pkColumnName = pkRecord.getString("column_name"); + String pkColumnType = pkRecord.getString("column_type"); + + Object pk; + if (save) { + pk = row.get(pkColumnName); + if (pk == null) { + pk = generatePrimaryKey(pkColumnType); + chain.add(pkColumnName, pk); + } + } else { + Record record = sysUserService.dao().fetch(tableName, Cnd.where(field, "=", row.get(field))); + if (record == null) { + throw new IllegalArgumentException("未找到关键字段匹配的记录"); + } + pk = record.get(pkColumnName); + } + + if (pk == null) { + throw new IllegalArgumentException("主键生成失败"); + } + return pk; + } + + private Object generatePrimaryKey(String pkColumnType) { + if (StrUtil.isBlank(pkColumnType)) { + return R.UU32(); + } + return switch (pkColumnType.toLowerCase()) { + case "int", "integer" -> R.random(1000, 9999); + case "varchar(16)" -> R.UU16(); + case "varchar(64)" -> R.UU64(); + default -> R.UU32(); + }; + } + + private Chain buildChain(NutMap row, Set tableColumns) { + NutMap filtered = NutMap.NEW(); + row.forEach((key, value) -> { + if (key != null && tableColumns.contains(key)) { + filtered.addv(key, value); + } + }); + return filtered.isEmpty() ? null : Chain.from(filtered); + } + + private NutMap[] parseRelations(String relationJson) { + List relationList = Json.fromJsonAsList(NutMap.class, relationJson); + return relationList.toArray(new NutMap[0]); + } + + private List parsePlugins(String pluginsJson) { + if (StrUtil.isBlank(pluginsJson)) { + return new ArrayList<>(); + } + List pluginNames = Json.fromJsonAsList(String.class, pluginsJson); + List plugins = new ArrayList<>(); + for (String pluginName : pluginNames) { + if (StrUtil.isBlank(pluginName)) { + continue; + } + plugins.add(SysDataImportPlugin.valueOf(pluginName)); + } + return plugins; + } + + private Optional getPrimaryKey(List columns) { + return columns.stream().filter(column -> "PRI".equalsIgnoreCase(column.getString("column_key"))).findFirst(); + } + + private List getColumns(String tableName) { + Sql sql = Sqls.create(""" + SELECT + column_name, + is_nullable, + column_type, + column_key, + column_comment + FROM + information_schema.COLUMNS + WHERE + table_name = @tableName + AND table_schema = (SELECT DATABASE()) + ORDER BY + ordinal_position + """).setParam("tableName", tableName); + return sysUserService.list(sql); + } + + private List getDataForExcel(TempFile file, NutMap[] relation) throws IOException { + List result = new ArrayList<>(); + Map headerColumnMap = new HashMap<>(); + + Sheet sheet = WorkbookFactory.create(file.getInputStream()).getSheetAt(0); + Row headerRow = sheet.getRow(0); + if (headerRow == null) { + return result; + } + + int columnNum = headerRow.getPhysicalNumberOfCells(); + for (int i = 0; i < columnNum; i++) { + Cell cell = headerRow.getCell(i); + if (cell == null) { + continue; + } + String header = cell.getStringCellValue(); + for (NutMap item : relation) { + if (StrUtil.equals(item.getString("relation"), header)) { + headerColumnMap.put(i, item.getString("column_name")); + break; + } + } + } + + int rowNum = sheet.getPhysicalNumberOfRows(); + for (int i = 1; i < rowNum; i++) { + Row row = sheet.getRow(i); + if (row == null) { + continue; + } + NutMap data = NutMap.NEW(); + for (int j = 0; j < columnNum; j++) { + String columnName = headerColumnMap.get(j); + if (columnName == null) { + continue; + } + data.setv(columnName, getCellValue(row.getCell(j))); + } + if (!data.isEmpty()) { + result.add(data); + } + } + return result; + } + + private Object getCellValue(Cell cell) { + if (cell == null) { + return null; + } + CellType cellType = cell.getCellType(); + if (cellType == CellType.NUMERIC) { + if (DateUtil.isCellDateFormatted(cell)) { + Date date = cell.getDateCellValue(); + return DATE_TIME_FORMATTER.format(LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault())); + } + double numericValue = cell.getNumericCellValue(); + BigDecimal decimal = BigDecimal.valueOf(numericValue); + if (String.valueOf(numericValue).contains("E")) { + return decimal.toPlainString(); + } + if (numericValue == Math.rint(numericValue)) { + return decimal.toBigInteger(); + } + return numericValue; + } + if (cellType == CellType.STRING) { + return cell.getStringCellValue(); + } + if (cellType == CellType.BOOLEAN) { + return cell.getBooleanCellValue(); + } + if (cellType == CellType.ERROR) { + return cell.getErrorCellValue(); + } + return null; + } + + @Data + @NoArgsConstructor + @AllArgsConstructor + public static class ImportError { + @Excel(name = "行数据", width = 80) + private String row; + + @Excel(name = "错误原因", width = 40) + private String errorMsg; + } +} diff --git a/src/main/java/com/budwk/app/sys/controller/SysHomeController.java b/src/main/java/com/budwk/app/sys/controller/SysHomeController.java index 6f32f097..adccf473 100644 --- a/src/main/java/com/budwk/app/sys/controller/SysHomeController.java +++ b/src/main/java/com/budwk/app/sys/controller/SysHomeController.java @@ -150,8 +150,10 @@ public class SysHomeController { ); List menus = sysUserService.getMenus(SecurityUtil.getUserId()); List allMenuIds = menus.stream().map(Sys_menu::getId).toList(); - //List sysMenus = list.stream().filter(m -> allMenuIds.contains(m.getId())).sorted(Comparator.comparingInt(Sys_menu::getLocation)).toList(); - List sysMenus = list.stream().sorted(Comparator.comparingInt(Sys_menu::getLocation)).toList(); + List sysMenus = list.stream() + .sorted(Comparator.comparing(Sys_menu::getLocation, Comparator.nullsLast(Integer::compareTo)) + .thenComparing(Sys_menu::getId)) + .toList(); return Result.success(sysMenus); } diff --git a/src/main/java/com/budwk/app/sys/controller/SysMenuController.java b/src/main/java/com/budwk/app/sys/controller/SysMenuController.java index 811d9ca0..8c669d2a 100644 --- a/src/main/java/com/budwk/app/sys/controller/SysMenuController.java +++ b/src/main/java/com/budwk/app/sys/controller/SysMenuController.java @@ -411,6 +411,24 @@ public class SysMenuController { } } + @At + @Ok("json") + @SaCheckPermission("sys.manager.menu.edit") + public Object updateLocation(String id, Integer location) { + try { + if (StrUtil.isBlank(id) || location == null || location < 0) { + return Result.error("排序编码必须是非负整数"); + } + sysMenuService.update(Chain.make("location", location), Cnd.where("id", "=", id)); + sysMenuService.clearCache(); + sysUserService.clearCache(); + sysRoleService.clearCache(); + return Result.success(); + } catch (Exception e) { + return Result.error(); + } + } + @At @Ok("json") @SaCheckPermission("sys.manager.menu") diff --git a/src/main/java/com/budwk/app/sys/controller/SysUnionController.java b/src/main/java/com/budwk/app/sys/controller/SysUnionController.java index 066ea8c9..c058ff3d 100644 --- a/src/main/java/com/budwk/app/sys/controller/SysUnionController.java +++ b/src/main/java/com/budwk/app/sys/controller/SysUnionController.java @@ -257,7 +257,7 @@ public class SysUnionController { */ @At @SaCheckPermission("sys.manager.union") - public Result branchUnionUserPageData(PageForm pageForm, String unionId) { + public Result branchUnionUserPageData(PageForm pageForm, String unionId, @Param("j") String j) { List branchUnionRoles = sysDictService.getSubListByCode("BRANCH_UNION_ROLES"); if (Lang.isEmpty(branchUnionRoles)) { return Result.success(); @@ -267,12 +267,18 @@ public class SysUnionController { Sql sql = Sqls.create(""" SELECT info.*, + COALESCE(jDict.`name`, info.j, curJDict.`name`, curSession.j) AS jName, role.`name` AS roleName, ins.id AS instanceId, ins.businessNo, ins.state instanceState, ins.variable instanceVariable, ins.processDefineId instanceProcessDefineId, + CASE + WHEN info.isServing = 0 THEN '离任' + WHEN t.id IS NOT NULL THEN t.displayName + ELSE '在任' + END AS displayStatus, t.id taskId, t.taskName AS taskKey, t.displayName taskName, @@ -287,6 +293,16 @@ public class SysUnionController { FROM sys_union_cadre info LEFT JOIN sys_role role ON role.`code` = info.roleCode + LEFT JOIN sys_dict jParent ON jParent.`code` = 'TEACHER_CONGRESS_J' + LEFT JOIN sys_dict jDict ON jDict.parentId = jParent.id AND jDict.`code` = info.j + LEFT JOIN ( + SELECT j + FROM teacher_congress_session + WHERE enable = 1 + ORDER BY startDate DESC + LIMIT 1 + ) curSession ON info.j IS NULL OR info.j = '' + LEFT JOIN sys_dict curJDict ON curJDict.parentId = jParent.id AND curJDict.`code` = curSession.j LEFT JOIN `wf_process_instance` ins ON ins.businessNo = info.id LEFT JOIN wf_process_task t ON t.processInstanceId = ins.id AND t.taskState = 10 LEFT JOIN wf_process_task_actor ta ON ta.processTaskId = t.id @@ -303,6 +319,9 @@ public class SysUnionController { case "roleName" -> cnd.where().andLike("role.`name`", pageForm.getSearchKeyword()); } } + if (StrUtil.isNotBlank(j)) { + cnd.and("COALESCE(NULLIF(info.j, ''), curSession.j)", "=", j); + } sql.setVar("constructionSql", new Static(" group by info.id order by field(role.code," + branchUnionRoleCodes.stream().map(code -> "'" + code + "'").collect(Collectors.joining(",")) + ")") ); @@ -311,11 +330,51 @@ public class SysUnionController { return Result.success(list); } + @At + @SaCheckPermission("sys.manager.union") + public Result branchUnionUserUsedJData(String unionId) { + List branchUnionRoles = sysDictService.getSubListByCode("BRANCH_UNION_ROLES"); + if (Lang.isEmpty(branchUnionRoles)) { + return Result.success(List.of()); + } + List branchUnionRoleCodes = branchUnionRoles.stream().map(Sys_dict::getCode).toList(); + + Sql sql = Sqls.create(""" + SELECT DISTINCT + COALESCE(NULLIF(info.j, ''), curSession.j) AS j, + COALESCE(jDict.location, curJDict.location, 0) AS jLocation + FROM + sys_union_cadre info + LEFT JOIN sys_role role ON role.`code` = info.roleCode + LEFT JOIN sys_dict jParent ON jParent.`code` = 'TEACHER_CONGRESS_J' + LEFT JOIN sys_dict jDict ON jDict.parentId = jParent.id AND jDict.`code` = info.j + LEFT JOIN ( + SELECT j + FROM teacher_congress_session + WHERE enable = 1 + ORDER BY startDate DESC + LIMIT 1 + ) curSession ON info.j IS NULL OR info.j = '' + LEFT JOIN sys_dict curJDict ON curJDict.parentId = jParent.id AND curJDict.`code` = curSession.j + $condition + ORDER BY jLocation DESC + """); + Cnd cnd = Cnd.where("role.`code`", "in", branchUnionRoleCodes); + cnd.and("info.unionId", "=", unionId); + sql.setCondition(cnd); + List usedJCodes = sysUserService.listMap(sql).stream() + .map(item -> item.getString("j")) + .filter(StrUtil::isNotBlank) + .distinct() + .toList(); + return Result.success(usedJCodes); + } + @At @SaCheckPermission("sys.manager.union.branchOfficer") @ApiOperation("添加分工会人员角色") @Aop(TransAop.READ_COMMITTED) - public Result insertBranchUnionUserRole(String userId, String roleCode, String unionId) { + public Result insertBranchUnionUserRole(String userId, String roleCode, String unionId, String j) { Sys_role role = sysRoleService.fetch(Cnd.where("code", "=", roleCode)); if (Lang.isEmpty(role)) { return Result.success("无法找到" + roleCode + "对应编码的角色"); @@ -353,7 +412,9 @@ public class SysUnionController { unionCadre.setLoginName(user.getLoginname()); unionCadre.setUserName(user.getUsername()); unionCadre.setRoleCode(roleCode); + unionCadre.setJ(j); unionCadre.setApplyDate(new Date()); + unionCadre.setIsServing(true); unionCadre.setIsJoin(true); dao.insert(unionCadre); @@ -382,6 +443,39 @@ public class SysUnionController { return Result.success(); } + @At + @ApiOperation("分工会干部离任") + @SaCheckPermission("sys.manager.union.branchOfficer") + @Aop(TransAop.READ_COMMITTED) + public Result leaveBranchUnionUserRole(String id, String leaveDate) { + if (StrUtil.isBlank(id)) { + return Result.error("请选择离任人员"); + } + if (StrUtil.isBlank(leaveDate)) { + return Result.error("请选择离任时间"); + } + Sys_union_cadre unionCadre = dao.fetch(Sys_union_cadre.class, id); + if (Lang.isEmpty(unionCadre)) { + return Result.error("未找到对应干部记录"); + } + Sys_role role = sysRoleService.fetch(Cnd.where("code", "=", unionCadre.getRoleCode())); + if (Lang.isEmpty(role)) { + throw new BaseException("无法找到{}对应编码的角色", unionCadre.getRoleCode()); + } + + Date parsedLeaveDate = cn.hutool.core.date.DateUtil.parseDate(leaveDate); + dao.update(Sys_union_cadre.class, + Chain.make("isServing", false).add("leaveDate", parsedLeaveDate), + Cnd.where("id", "=", id)); + + sysRoleService.clear("sys_user_role", Cnd.where("roleId", "=", role.getId()) + .and("userId", "=", unionCadre.getUserId()) + .and("unionId", "=", unionCadre.getUnionId())); + sysRoleService.clearCache(); + sysUserService.clearCache(); + return Result.success(); + } + @At @ApiOperation("删除分工会人员角色") @SaCheckPermission("sys.manager.union.branchOfficer") diff --git a/src/main/java/com/budwk/app/sys/controller/v4/SysV4AppsController.java b/src/main/java/com/budwk/app/sys/controller/v4/SysV4AppsController.java index ea744372..aa84bd3d 100644 --- a/src/main/java/com/budwk/app/sys/controller/v4/SysV4AppsController.java +++ b/src/main/java/com/budwk/app/sys/controller/v4/SysV4AppsController.java @@ -72,6 +72,7 @@ public class SysV4AppsController { m.href, m.icon, m.picIcon, + m.location, sm.`name` AS moduleName, CASE WHEN f.userId IS NOT NULL THEN @@ -91,6 +92,7 @@ public class SysV4AppsController { cnd.and("m.id","in", menus.stream().map(Sys_menu::getId).toArray()); cnd.andEX("m.moduleId", "=", categoryId); cnd.asc("m.location"); + cnd.asc("m.id"); if (StrUtil.isNotBlank(keyword)) { cnd.where().andLike("m.name", keyword); } @@ -145,6 +147,7 @@ public class SysV4AppsController { m.href, m.icon, m.picIcon, + m.location, sm.`name` AS moduleName, 1 AS isFavorite FROM @@ -159,7 +162,8 @@ public class SysV4AppsController { AND m.platform = @platform AND m.disabled = 0 ORDER BY - m.location ASC; + m.location ASC, + m.id ASC; """); sql.setParam("userId", SecurityUtil.getUserId()); sql.setParam("platform", platform); diff --git a/src/main/java/com/budwk/app/sys/controller/v4/SysV4ServController.java b/src/main/java/com/budwk/app/sys/controller/v4/SysV4ServController.java index 2483fb79..7f3e3933 100644 --- a/src/main/java/com/budwk/app/sys/controller/v4/SysV4ServController.java +++ b/src/main/java/com/budwk/app/sys/controller/v4/SysV4ServController.java @@ -46,13 +46,14 @@ public class SysV4ServController { @ApiOperation("获取应用列表") public Result list(@Param("categoryId") String categoryId, @Param("letter") String letter, @Param("keyword") String keyword, HttpServletRequest req) { Sql sql = Sqls.create(""" - SELECT t.*,(select picIcon from wf_process_design where name = t.name) as picIcon + SELECT t.*, d.picIcon, d.sortNo FROM wf_process_define t INNER JOIN ( SELECT name, MAX(id) AS max_id FROM wf_process_define GROUP BY name ) sub ON t.name = sub.name AND t.id = sub.max_id + LEFT JOIN wf_process_design d ON d.name = t.name $condition """); Cnd cnd = Cnd.NEW(); @@ -61,6 +62,8 @@ public class SysV4ServController { } cnd.andEX("t.category", "=", categoryId); cnd.andEX("t.pinyinName", "=", letter); + cnd.asc("d.sortNo"); + cnd.asc("t.id"); sql.setCondition(cnd); List list = sysMenuService.listMap(sql); return Result.success(list); diff --git a/src/main/java/com/budwk/app/sys/enums/SysDataImportPlugin.java b/src/main/java/com/budwk/app/sys/enums/SysDataImportPlugin.java new file mode 100644 index 00000000..7bfe4df8 --- /dev/null +++ b/src/main/java/com/budwk/app/sys/enums/SysDataImportPlugin.java @@ -0,0 +1,123 @@ +package com.budwk.app.sys.enums; + +import com.budwk.app.base.annotation.DictEnum; +import com.budwk.app.base.constant.RoleConstant; +import com.budwk.app.base.utils.PwdUtil; +import com.budwk.app.sys.models.Sys_role; +import com.budwk.app.sys.models.Sys_user_role; +import com.budwk.app.sys.services.SysRoleService; +import lombok.Getter; +import org.nutz.dao.Chain; +import org.nutz.dao.Cnd; +import org.nutz.dao.Dao; +import org.nutz.lang.random.R; + +import java.util.Date; +import java.util.Set; + +/** + * 高级导入工具插件。 + */ +@Getter +@DictEnum(key = "sysDataImportPlugin", name = "系统数据高级工具插件") +public enum SysDataImportPlugin { + + USER_INIT(1, "初始化系统用户"), + MEMBER_INIT(2, "设置为会员"); + + private final int location; + private final String description; + + SysDataImportPlugin(int location, String description) { + this.location = location; + this.description = description; + } + + public void before(PluginContext context) { + if (!context.isSysUserTable()) { + return; + } + if (this == USER_INIT) { + String salt = R.UU32(); + context.addValue("salt", salt); + context.addValue("password", PwdUtil.getPassword(PwdUtil.generate(12), salt)); + context.addValue("loginCount", 0); + context.addValue("disabled", false); + return; + } + if (this == MEMBER_INIT) { + context.addValue("member", true); + context.addValue("memberTime", new Date()); + } + } + + public void after(PluginContext context) { + if (!context.isSysUserTable()) { + return; + } + if (this == USER_INIT) { + context.ensureRole(RoleConstant.PUBLIC); + return; + } + if (this == MEMBER_INIT) { + context.ensureRole(RoleConstant.MEMBER); + } + } + + @Getter + public static class PluginContext { + private final String tableName; + private final Set tableColumns; + private final Chain chain; + private final Object pk; + private final Dao dao; + private final SysRoleService sysRoleService; + + public PluginContext(String tableName, + Set tableColumns, + Chain chain, + Object pk, + Dao dao, + SysRoleService sysRoleService) { + this.tableName = tableName; + this.tableColumns = tableColumns; + this.chain = chain; + this.pk = pk; + this.dao = dao; + this.sysRoleService = sysRoleService; + } + + public boolean isSysUserTable() { + return "sys_user".equalsIgnoreCase(tableName); + } + + public boolean hasColumn(String columnName) { + return tableColumns.contains(columnName); + } + + public void addValue(String columnName, Object value) { + if (hasColumn(columnName)) { + chain.add(columnName, value); + } + } + + public void ensureRole(RoleConstant roleConstant) { + if (pk == null || !isSysUserTable()) { + return; + } + Sys_role sysRole = sysRoleService.getByCode(roleConstant); + if (sysRole == null) { + return; + } + int count = dao.count(Sys_user_role.class, Cnd.where("userId", "=", String.valueOf(pk)) + .and("roleId", "=", sysRole.getId())); + if (count > 0) { + return; + } + Sys_user_role userRole = new Sys_user_role(); + userRole.setUserId(String.valueOf(pk)); + userRole.setRoleId(sysRole.getId()); + dao.insert(userRole); + } + } +} diff --git a/src/main/java/com/budwk/app/sys/models/Sys_union_cadre.java b/src/main/java/com/budwk/app/sys/models/Sys_union_cadre.java index 63c18feb..63a9a059 100644 --- a/src/main/java/com/budwk/app/sys/models/Sys_union_cadre.java +++ b/src/main/java/com/budwk/app/sys/models/Sys_union_cadre.java @@ -61,11 +61,26 @@ public class Sys_union_cadre extends BaseModel { @ColDefine(type = ColType.VARCHAR, width = 100) private String roleCode; + @Column + @Comment("届数") + @ColDefine(type = ColType.VARCHAR, width = 8) + private String j; + @Column @Comment("添加时间") @ColDefine(type = ColType.DATETIME) private Date applyDate; + @Column + @Comment("离任时间") + @ColDefine(type = ColType.DATETIME) + private Date leaveDate; + + @Column + @Comment("是否在任") + @ColDefine(type = ColType.BOOLEAN) + private Boolean isServing; + @Column @Comment("加入还是退出") @ColDefine(type = ColType.BOOLEAN) diff --git a/src/main/java/com/budwk/app/zhgh/activity/basic/controller/ActivityBasicScopeController.java b/src/main/java/com/budwk/app/zhgh/activity/basic/controller/ActivityBasicScopeController.java index f3a513b6..0566b171 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/basic/controller/ActivityBasicScopeController.java +++ b/src/main/java/com/budwk/app/zhgh/activity/basic/controller/ActivityBasicScopeController.java @@ -317,10 +317,7 @@ public class ActivityBasicScopeController { cnd.orderBy(activityUserScopePageParam.getPageOrderName(), activityUserScopePageParam.getPageOrderBy().equalsIgnoreCase("ascending") ? "ASC" : "DESC"); } - if (activityUserScopePageParam.getActivityGroupId() != null) { - Sql sqlx = activityBasicScopeService.buildGroupUserIdSubSql(activityUserScopePageParam.getActivityGroupId()); - cnd.and("u.id", activityUserScopePageParam.getReverseSelection() ? "IN" : "NOT IN", sqlx); - } + appendActivityGroupCondition(cnd, activityUserScopePageParam); cnd.andEX("u.id", IN_OR_NIN_OP, activityUserScopePageParam.getUserId()); @@ -332,7 +329,7 @@ public class ActivityBasicScopeController { cnd.and("u.welfareMember", EQ_OR_NEQ_OP, 1); } if (ArrayUtils.contains(activityUserScopePageParam.getMemberTypes(), "基金会员")) { - cnd.and(new Static(" u.loginname " + IN_OR_NIN_OP + " (select loginname from sick_fund_member)")); + cnd.and("u.aidFundMember", EQ_OR_NEQ_OP, 1); } } @@ -378,8 +375,12 @@ public class ActivityBasicScopeController { cnd.andEX("u.unionid", "=", SecurityUtil.getUnionId()); } } - cnd.andEX("u.unionid", EQ_OR_NEQ_OP, activityUserScopePageParam.getUnionId()); - cnd.andEX("u.unitid", EQ_OR_NEQ_OP, activityUserScopePageParam.getUnitId()); + String[] unionIds = getUnionIds(activityUserScopePageParam); + if (Lang.isNotEmpty(unionIds)) { + cnd.andEX("u.unionid", IN_OR_NIN_OP, unionIds); + } else { + cnd.andEX("u.unionid", EQ_OR_NEQ_OP, activityUserScopePageParam.getUnionId()); + } cnd.andEX("u.personType", IN_OR_NIN_OP, activityUserScopePageParam.getPersonTypes()); cnd.andEX("u.userState", IN_OR_NIN_OP, activityUserScopePageParam.getUserStates()); cnd.andEX("u.sex", IN_OR_NIN_OP, activityUserScopePageParam.getSexTypes()); @@ -394,6 +395,34 @@ public class ActivityBasicScopeController { } + private void appendActivityGroupCondition(Cnd cnd, ActivityUserScopePageParam activityUserScopePageParam) { + Integer activityGroupId = activityUserScopePageParam.getActivityGroupId(); + if (activityGroupId == null) { + return; + } + boolean reverseSelection = Boolean.TRUE.equals(activityUserScopePageParam.getReverseSelection()); + Integer groupType = activityBasicScopeService.getGroupType(activityGroupId); + if (GROUP_TYPE_RESULT == groupType) { + String existsSql = """ + EXISTS ( + SELECT 1 + FROM activity_user_scope aus + WHERE aus.groupId = %d + AND aus.userId = u.id + ) + """.formatted(activityGroupId); + cnd.and(new Static(reverseSelection ? existsSql : "NOT " + existsSql)); + return; + } + + ActivityUserScope groupInfo = activityBasicScopeService.getGroupInfo(activityGroupId); + if (groupInfo == null || StrUtil.isBlank(groupInfo.getGroupSql())) { + return; + } + String groupSql = "(" + groupInfo.getGroupSql() + ")"; + cnd.and(new Static(reverseSelection ? groupSql : "NOT " + groupSql)); + } + /** * 结果分组会把当前筛选出的用户快照保存下来,因此只落 userId 明细数据。 */ @@ -577,7 +606,7 @@ public class ActivityBasicScopeController { conditionList.add("u.welfareMember " + eqOrNeq + " 1"); } if (ArrayUtils.contains(activityUserScopePageParam.getMemberTypes(), "基金会员")) { - conditionList.add("u.loginname " + inOrNotIn + " (select loginname from sick_fund_member)"); + conditionList.add("u.aidFundMember " + eqOrNeq + " 1"); } } @@ -601,8 +630,12 @@ public class ActivityBasicScopeController { conditionList.add("u.unionid = " + wrapSqlValue(SecurityUtil.getUnionId())); } - addSingleValueCondition(conditionList, "u.unionid", eqOrNeq, activityUserScopePageParam.getUnionId()); - addSingleValueCondition(conditionList, "u.unitid", eqOrNeq, activityUserScopePageParam.getUnitId()); + String[] unionIds = getUnionIds(activityUserScopePageParam); + if (Lang.isNotEmpty(unionIds)) { + addArrayCondition(conditionList, "u.unionid", inOrNotIn, unionIds); + } else { + addSingleValueCondition(conditionList, "u.unionid", eqOrNeq, activityUserScopePageParam.getUnionId()); + } addArrayCondition(conditionList, "u.personType", inOrNotIn, activityUserScopePageParam.getPersonTypes()); addArrayCondition(conditionList, "u.userState", inOrNotIn, activityUserScopePageParam.getUserStates()); addArrayCondition(conditionList, "u.sex", inOrNotIn, activityUserScopePageParam.getSexTypes()); @@ -637,6 +670,16 @@ public class ActivityBasicScopeController { } } + private String[] getUnionIds(ActivityUserScopePageParam activityUserScopePageParam) { + if (Lang.isNotEmpty(activityUserScopePageParam.getUnionIds())) { + return activityUserScopePageParam.getUnionIds(); + } + if (StrUtil.isNotBlank(activityUserScopePageParam.getUnionId())) { + return new String[]{activityUserScopePageParam.getUnionId()}; + } + return null; + } + private void addArrayCondition(List conditionList, String columnName, String operator, String[] values) { if (Lang.isNotEmpty(values)) { conditionList.add(columnName + " " + operator + " (" + buildSqlStringList(values) + ")"); diff --git a/src/main/java/com/budwk/app/zhgh/activity/basic/models/ActivityUserScope.java b/src/main/java/com/budwk/app/zhgh/activity/basic/models/ActivityUserScope.java index 509f6236..c71ff5bc 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/basic/models/ActivityUserScope.java +++ b/src/main/java/com/budwk/app/zhgh/activity/basic/models/ActivityUserScope.java @@ -20,7 +20,8 @@ import java.io.Serializable; @Comment("活动人员范围设置") @TableIndexes({ @Index(name = "INDEX_ACTIVITY_USER_SCOPE_GROUPID", fields = {"groupId"}, unique = false), - @Index(name = "INDEX_ACTIVITY_USER_SCOPE_USERID", fields = {"userId"}, unique = false) + @Index(name = "INDEX_ACTIVITY_USER_SCOPE_USERID", fields = {"userId"}, unique = false), + @Index(name = "INDEX_ACTIVITY_USER_SCOPE_GROUPID_USERID", fields = {"groupId", "userId"}, unique = false) }) public class ActivityUserScope extends BaseModel implements Serializable { diff --git a/src/main/java/com/budwk/app/zhgh/activity/basic/param/ActivityUserScopePageParam.java b/src/main/java/com/budwk/app/zhgh/activity/basic/param/ActivityUserScopePageParam.java index 133bc3e6..1f0138c5 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/basic/param/ActivityUserScopePageParam.java +++ b/src/main/java/com/budwk/app/zhgh/activity/basic/param/ActivityUserScopePageParam.java @@ -16,6 +16,7 @@ public class ActivityUserScopePageParam extends PageForm { // 工会id private String unionId; + private String[] unionIds; // 单位id private String unitId; // 人类型 diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/siteCug/controller/SiteCugApplyController.java b/src/main/java/com/budwk/app/zhgh/dayofficework/siteCug/controller/SiteCugApplyController.java index 13848bbb..a7923c08 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/siteCug/controller/SiteCugApplyController.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/siteCug/controller/SiteCugApplyController.java @@ -15,6 +15,7 @@ import com.budwk.app.flow.constant.FlowConst; import com.budwk.app.flow.engine.FlowEngine; import com.budwk.app.flow.entity.ProcessInstance; import com.budwk.app.flow.entity.ProcessTask; +import com.budwk.app.flow.enums.ProcessInstanceStateEnum; import com.budwk.app.flow.enums.ProcessSubmitTypeEnum; import com.budwk.app.sys.models.SysHoliday; import com.budwk.app.web.commons.auth.utils.SecurityUtil; @@ -29,6 +30,8 @@ import lombok.extern.slf4j.Slf4j; import org.nutz.aop.interceptor.ioc.TransAop; import org.nutz.dao.Cnd; import org.nutz.dao.Dao; +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; @@ -40,16 +43,19 @@ import org.nutz.mvc.annotation.Param; import java.time.Year; import java.util.ArrayList; +import java.util.Arrays; import java.util.Date; import java.util.List; -import java.util.UUID; import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; import java.util.stream.Collectors; @Slf4j @IocBean @Ok("json:full") -@Api(tags = "场地预约-场馆") +@Api(tags = "\u573a\u5730\u9884\u7ea6-\u573a\u9986") @At("/platform/siteCug/apply") public class SiteCugApplyController { @@ -67,20 +73,23 @@ public class SiteCugApplyController { @At("/") @SaCheckPermission("siteCug.apply") @Ok("beetl:/platform/zhgh/dayofficework/siteCug/apply/index.html") - public void index() {} + public void index() { + } @At("/h5") @SaCheckPermission(value = {"siteCug.apply", "h5.siteCug.apply"}, mode = SaMode.OR) @Ok("beetl:/platform/zhghh5/dayofficework/siteCug/apply/index.html") - public void h5Index() {} + public void h5Index() { + } @At("/form/h5") @SaCheckPermission(value = {"siteCug.apply", "h5.siteCug.apply"}, mode = SaMode.OR) @Ok("beetl:/platform/zhghh5/dayofficework/siteCug/apply/form/index.html") - public void h5Form() {} + public void h5Form() { + } @At - @ApiOperation("分页查询场地") + @ApiOperation("\u5206\u9875\u67e5\u8be2\u573a\u5730") @SaCheckPermission(value = {"siteCug.apply", "h5.siteCug.apply"}, mode = SaMode.OR) public Result pageData(PageForm pageForm, @Param("type") String type) { Cnd cnd = Cnd.NEW(); @@ -102,17 +111,530 @@ public class SiteCugApplyController { List typeList = dao.query(SiteCugFunctionType.class, Cnd.NEW()); Map typeMap = typeList.stream().collect(Collectors.toMap(SiteCugFunctionType::getId, SiteCugFunctionType::getName)); + List siteIds = listMap.stream().map(item -> item.getString("id")).filter(StrUtil::isNotBlank).toList(); + String today = DateUtil.today(); + String dayStart = today + " 00:00:00"; + String dayEnd = today + " 23:59:59"; + Map> applyMap = queryTodayApplyMap(siteIds, dayStart, dayEnd); + Set holidaySet = dao.query(SysHoliday.class, Cnd.NEW()).stream() + .map(SysHoliday::getDay) + .filter(StrUtil::isNotBlank) + .collect(Collectors.toSet()); for (NutMap map : listMap) { map.put("typeName", typeMap.get(map.getString("typeId"))); + map.put("reserveTimeTypeName", getReserveTimeTypeName(map.getInt("reserveTimeType"))); + map.put("timelineSegments", buildTimelineSegments(map, applyMap.getOrDefault(map.getString("id"), new ArrayList<>()), today, holidaySet)); } return Result.success(pagination); } @At - @ApiOperation("提交预约") + @ApiOperation("\u67e5\u8be2\u573a\u5730\u53ef\u9884\u7ea6\u65f6\u6bb5") @SaCheckPermission(value = {"siteCug.apply", "h5.siteCug.apply"}, mode = SaMode.OR) - @SLog(tag = "场馆功能管理-场地预约", msg = "提交场地预约") + public Result availability(@Param("siteId") String siteId, @Param("date") String date) { + if (StrUtil.isBlank(siteId)) { + return Result.error("\u573a\u5730\u4e0d\u80fd\u4e3a\u7a7a"); + } + String targetDate = StrUtil.isBlank(date) ? DateUtil.today() : date; + if (!targetDate.matches("^\\d{4}-\\d{2}-\\d{2}$")) { + return Result.error("\u9884\u7ea6\u65e5\u671f\u683c\u5f0f\u4e0d\u6b63\u786e"); + } + SiteCugInfo siteInfo = infoService.fetch(siteId); + if (siteInfo == null) { + return Result.error("\u573a\u5730\u4e0d\u5b58\u5728"); + } + Set holidaySet = queryHolidaySet(); + List applyList = queryDayApplyList(siteId, targetDate + " 00:00:00", targetDate + " 23:59:59"); + return Result.success(NutMap.NEW() + .addv("date", targetDate) + .addv("reserveTimeType", siteInfo.getReserveTimeType()) + .addv("reserveTimeTypeName", getReserveTimeTypeName(siteInfo.getReserveTimeType())) + .addv("blocks", buildAvailabilityBlocks(siteInfo, targetDate, applyList, holidaySet))); + } + + private Map> queryTodayApplyMap(List siteIds, String dayStart, String dayEnd) { + if (siteIds == null || siteIds.isEmpty()) { + return Map.of(); + } + Sql sql = Sqls.create(""" + select + sa.* + from + site_cug_apply sa + left join wf_process_instance ins on ins.businessNo = sa.id + $condition + """); + Cnd cnd = Cnd.NEW(); + cnd.and("sa.siteId", "in", siteIds); + cnd.and("ins.state", "in", List.of( + ProcessInstanceStateEnum.DOING.getCode(), + ProcessInstanceStateEnum.FINISHED.getCode(), + ProcessInstanceStateEnum.PENDING.getCode() + )); + cnd.and("sa.reserveEndTime", ">=", dayStart); + cnd.and("sa.reserveStartTime", "<=", dayEnd); + sql.setCondition(cnd); + sql.setEntity(dao.getEntity(SiteCugApply.class)); + sql.setCallback(Sqls.callback.entities()); + dao.execute(sql); + List applyList = sql.getList(SiteCugApply.class); + if (applyList == null || applyList.isEmpty()) { + return Map.of(); + } + return applyList.stream() + .filter(Objects::nonNull) + .collect(Collectors.groupingBy(SiteCugApply::getSiteId)); + } + + private List queryDayApplyList(String siteId, String dayStart, String dayEnd) { + if (StrUtil.isBlank(siteId)) { + return new ArrayList<>(); + } + Sql sql = Sqls.create(""" + select + sa.* + from + site_cug_apply sa + left join wf_process_instance ins on ins.businessNo = sa.id + $condition + """); + Cnd cnd = Cnd.NEW(); + cnd.and("sa.siteId", "=", siteId); + cnd.and("ins.state", "in", List.of( + ProcessInstanceStateEnum.DOING.getCode(), + ProcessInstanceStateEnum.FINISHED.getCode(), + ProcessInstanceStateEnum.PENDING.getCode() + )); + cnd.and("sa.reserveEndTime", ">=", dayStart); + cnd.and("sa.reserveStartTime", "<=", dayEnd); + sql.setCondition(cnd); + sql.setEntity(dao.getEntity(SiteCugApply.class)); + sql.setCallback(Sqls.callback.entities()); + dao.execute(sql); + List applyList = sql.getList(SiteCugApply.class); + return applyList == null ? new ArrayList<>() : applyList; + } + + private Set queryHolidaySet() { + return dao.query(SysHoliday.class, Cnd.NEW()).stream() + .map(SysHoliday::getDay) + .filter(StrUtil::isNotBlank) + .collect(Collectors.toSet()); + } + + private String getReserveTimeTypeName(Integer reserveTimeType) { + if (reserveTimeType != null && reserveTimeType == 2) { + return "\u5168\u5929\u5019\u9884\u7ea6"; + } + return "\u5206\u6bb5\u9884\u7ea6"; + } + + private List buildTimelineSegments(NutMap site, List applyList, String today, Set holidaySet) { + List segments = new ArrayList<>(); + boolean isWeekend = isWeekend(today); + boolean isHoliday = holidaySet.contains(today); + List openRanges = buildOpenRanges(site, isWeekend, isHoliday); + List disabledRanges = buildDisabledRanges(site, today); + List reservedRanges = buildReservedRanges(applyList, today); + int totalMinutes = 24 * 60; + int segmentMinutes = 30; + for (int start = 0; start < totalMinutes; start += segmentMinutes) { + int end = start + segmentMinutes; + String status = "closed"; + if (intersectsAny(start, end, openRanges)) { + status = "available"; + } + if (intersectsAny(start, end, disabledRanges)) { + status = "closed"; + } + if (!"closed".equals(status) && intersectsAny(start, end, reservedRanges)) { + status = "reserved"; + } + segments.add(NutMap.NEW() + .addv("status", status) + .addv("startMinute", start) + .addv("endMinute", end)); + } + return segments; + } + + private List buildOpenRanges(NutMap site, boolean isWeekend, boolean isHoliday) { + if (isWeekend) { + return new ArrayList<>(); + } + if (Boolean.TRUE.equals(site.getBoolean("filterHolidays")) && isHoliday) { + return new ArrayList<>(); + } + Integer reserveTimeType = site.getInt("reserveTimeType"); + if (reserveTimeType != null && reserveTimeType == 2) { + NutMap fullDayOpenHour = toNutMap(site.get("fullDayOpenHour")); + if (fullDayOpenHour.isEmpty()) { + List openHours = toNutMapList(site.get("openHours")); + fullDayOpenHour = openHours.isEmpty() ? NutMap.NEW() : openHours.get(0); + } + return parseTimeRanges(List.of(fullDayOpenHour)); + } + List segmentedOpenHours = toNutMapList(site.get("segmentedOpenHours")); + if (segmentedOpenHours.isEmpty()) { + segmentedOpenHours = toNutMapList(site.get("openHours")); + } + return parseTimeRanges(segmentedOpenHours); + } + + private List buildDisabledRanges(NutMap site, String today) { + List disabledList = toNutMapList(site.get("notApplyTimeList")); + List result = new ArrayList<>(); + for (NutMap item : disabledList) { + if (item == null || !StrUtil.equals(today, item.getString("date"))) { + continue; + } + Integer start = parseTimeToMinutes(item.getString("startTime")); + Integer end = parseTimeToMinutes(item.getString("endTime")); + if (start != null && end != null && end > start) { + result.add(new int[]{start, end}); + } + } + return result; + } + + private List buildReservedRanges(List applyList, String today) { + List result = new ArrayList<>(); + String dayStart = today + " 00:00:00"; + String dayEnd = today + " 23:59:59"; + DateTime startOfDay = DateUtil.parseDateTime(dayStart); + DateTime endOfDay = DateUtil.parseDateTime(dayEnd); + for (SiteCugApply item : applyList) { + if (item == null || StrUtil.hasBlank(item.getReserveStartTime(), item.getReserveEndTime())) { + continue; + } + DateTime reserveStart = DateUtil.parseDateTime(item.getReserveStartTime()); + DateTime reserveEnd = DateUtil.parseDateTime(item.getReserveEndTime()); + if (!reserveEnd.isAfter(reserveStart)) { + continue; + } + DateTime actualStart = reserveStart.isBefore(startOfDay) ? startOfDay : reserveStart; + DateTime actualEnd = reserveEnd.isAfter(endOfDay) ? endOfDay : reserveEnd; + int startMinute = actualStart.hour(true) * 60 + actualStart.minute(); + int endMinute = actualEnd.hour(true) * 60 + actualEnd.minute(); + if (actualEnd.second() > 0) { + endMinute = Math.min(24 * 60, endMinute + 1); + } + if (endMinute > startMinute) { + result.add(new int[]{startMinute, endMinute}); + } + } + return result; + } + + private List parseTimeRanges(List sourceList) { + List result = new ArrayList<>(); + for (NutMap item : sourceList) { + if (item == null) { + continue; + } + Integer start = parseTimeToMinutes(item.getString("startTime")); + Integer end = parseTimeToMinutes(item.getString("endTime")); + if (start != null && end != null && end > start) { + result.add(new int[]{start, end}); + } + } + return result; + } + + private NutMap toNutMap(Object value) { + if (value instanceof NutMap nutMap) { + return nutMap; + } + if (value instanceof Map map) { + NutMap nutMap = NutMap.NEW(); + map.forEach((key, val) -> nutMap.put(String.valueOf(key), val)); + return nutMap; + } + return NutMap.NEW(); + } + + private List toNutMapList(Object value) { + List result = new ArrayList<>(); + if (!(value instanceof List list)) { + return result; + } + for (Object item : list) { + result.add(toNutMap(item)); + } + return result; + } + + private Integer parseTimeToMinutes(String timeText) { + if (StrUtil.isBlank(timeText)) { + return null; + } + List parts = Arrays.stream(timeText.split(":")) + .filter(StrUtil::isNotBlank) + .toList(); + if (parts.size() < 2) { + return null; + } + try { + int hour = Integer.parseInt(parts.get(0)); + int minute = Integer.parseInt(parts.get(1)); + if (hour < 0 || hour > 24 || minute < 0 || minute > 59) { + return null; + } + if (hour == 24 && minute > 0) { + return null; + } + return hour * 60 + minute; + } catch (NumberFormatException e) { + return null; + } + } + + private boolean intersectsAny(int start, int end, List ranges) { + for (int[] range : ranges) { + if (range != null && start < range[1] && end > range[0]) { + return true; + } + } + return false; + } + + private boolean isWeekend(String dateText) { + DateTime date = DateUtil.parseDate(dateText); + int week = date.dayOfWeek() - 1; + return week == 0 || week == 6; + } + + private List buildAvailabilityBlocks(SiteCugInfo siteInfo, String day, List applyList, Set holidaySet) { + if (siteInfo.getReserveTimeType() != null && siteInfo.getReserveTimeType() == 2) { + return buildFullDayAvailabilityBlocks(siteInfo, day, applyList, holidaySet); + } + return buildSegmentedAvailabilityBlocks(siteInfo, day, applyList, holidaySet); + } + + private List buildSegmentedAvailabilityBlocks(SiteCugInfo siteInfo, String day, List applyList, Set holidaySet) { + List sourceList = siteInfo.getSegmentedOpenHours(); + if (sourceList == null || sourceList.isEmpty()) { + sourceList = siteInfo.getOpenHours(); + } + List result = new ArrayList<>(); + List reservedRanges = buildReservedRanges(applyList, day); + List disabledRanges = buildDisabledRanges(toSiteNutMap(siteInfo), day); + boolean siteClosed = isSiteClosed(siteInfo, day, holidaySet); + for (int i = 0; i < sourceList.size(); i++) { + NutMap item = toNutMap(sourceList.get(i)); + Integer slotStart = parseTimeToMinutes(item.getString("startTime")); + Integer slotEnd = parseTimeToMinutes(item.getString("endTime")); + int unitMinutes = parseTimeUnitMinutes(item.get("timeUnit")); + if (slotStart == null || slotEnd == null || slotEnd <= slotStart || unitMinutes <= 0) { + continue; + } + String groupLabel = "\u573a\u6b21" + (i + 1) + " " + formatMinutes(slotStart) + "-" + formatMinutes(slotEnd) + " / " + formatUnitText(unitMinutes); + for (int cursor = slotStart; cursor + unitMinutes <= slotEnd; cursor += unitMinutes) { + int blockEnd = cursor + unitMinutes; + result.add(buildAvailabilityBlock(day, cursor, blockEnd, reservedRanges, disabledRanges, siteClosed, i, groupLabel)); + } + } + return result; + } + + private List buildFullDayAvailabilityBlocks(SiteCugInfo siteInfo, String day, List applyList, Set holidaySet) { + NutMap source = getFullDayOpenHourConfig(siteInfo); + Integer slotStart = parseTimeToMinutes(source.getString("startTime")); + Integer slotEnd = parseTimeToMinutes(source.getString("endTime")); + int unitMinutes = parseTimeUnitMinutes(source.get("timeUnit")); + if (slotStart == null || slotEnd == null || slotEnd <= slotStart || unitMinutes <= 0) { + return new ArrayList<>(); + } + List result = new ArrayList<>(); + List reservedRanges = buildReservedRanges(applyList, day); + List disabledRanges = buildDisabledRanges(toSiteNutMap(siteInfo), day); + boolean siteClosed = isSiteClosed(siteInfo, day, holidaySet); + String groupLabel = "\u5168\u5929\u5019\u65f6\u6bb5 / " + formatUnitText(unitMinutes); + for (int cursor = slotStart; cursor + unitMinutes <= slotEnd; cursor += unitMinutes) { + int blockEnd = cursor + unitMinutes; + result.add(buildAvailabilityBlock(day, cursor, blockEnd, reservedRanges, disabledRanges, siteClosed, 0, groupLabel)); + } + return result; + } + + private NutMap buildAvailabilityBlock(String day, int startMinute, int endMinute, List reservedRanges, List disabledRanges, boolean siteClosed, int groupIndex, String groupLabel) { + String status = "available"; + if (siteClosed || intersectsAny(startMinute, endMinute, disabledRanges)) { + status = "closed"; + } else if (intersectsAny(startMinute, endMinute, reservedRanges)) { + status = "reserved"; + } + return NutMap.NEW() + .addv("key", day + "_" + startMinute + "_" + endMinute) + .addv("groupIndex", groupIndex) + .addv("groupLabel", groupLabel) + .addv("status", status) + .addv("startMinute", startMinute) + .addv("endMinute", endMinute) + .addv("startTime", formatMinutes(startMinute)) + .addv("endTime", formatMinutes(endMinute)) + .addv("label", formatMinutes(startMinute) + "-" + formatMinutes(endMinute)) + .addv("startDateTime", day + " " + formatMinutes(startMinute) + ":00") + .addv("endDateTime", day + " " + formatMinutes(endMinute) + ":00"); + } + + private boolean isSiteClosed(SiteCugInfo siteInfo, String day, Set holidaySet) { + if (isWeekend(day)) { + return true; + } + return Boolean.TRUE.equals(siteInfo.getFilterHolidays()) && holidaySet.contains(day); + } + + private NutMap toSiteNutMap(SiteCugInfo siteInfo) { + return NutMap.NEW() + .addv("notApplyTimeList", siteInfo.getNotApplyTimeList()) + .addv("filterHolidays", siteInfo.getFilterHolidays()) + .addv("reserveTimeType", siteInfo.getReserveTimeType()) + .addv("openHours", siteInfo.getOpenHours()) + .addv("segmentedOpenHours", siteInfo.getSegmentedOpenHours()) + .addv("fullDayOpenHour", siteInfo.getFullDayOpenHour()); + } + + private NutMap getFullDayOpenHourConfig(SiteCugInfo siteInfo) { + NutMap source = toNutMap(siteInfo.getFullDayOpenHour()); + if (!source.isEmpty()) { + return source; + } + List openHours = siteInfo.getOpenHours(); + return openHours == null || openHours.isEmpty() ? NutMap.NEW() : toNutMap(openHours.get(0)); + } + + private int parseTimeUnitMinutes(Object value) { + if (value == null) { + return 60; + } + try { + double unit = Double.parseDouble(String.valueOf(value)); + if (unit <= 0) { + return 60; + } + return (int) Math.round(unit); + } catch (NumberFormatException e) { + return 60; + } + } + + private String formatMinutes(int totalMinutes) { + int hour = totalMinutes / 60; + int minute = totalMinutes % 60; + return String.format("%02d:%02d", hour, minute); + } + + private String formatUnitText(int unitMinutes) { + return unitMinutes + "\u5206\u949f"; + } + + private Result validateApplySelectionBySchedule(SiteCugApply apply) { + if (apply == null || StrUtil.hasBlank(apply.getSiteId(), apply.getReserveStartTime(), apply.getReserveEndTime())) { + return Result.success(); + } + SiteCugInfo siteInfo = infoService.fetch(apply.getSiteId()); + if (siteInfo == null) { + return Result.error("\u573a\u5730\u4e0d\u5b58\u5728"); + } + DateTime start = DateUtil.parseDateTime(apply.getReserveStartTime()); + DateTime end = DateUtil.parseDateTime(apply.getReserveEndTime()); + String startDay = DateUtil.formatDate(start); + String endDay = DateUtil.formatDate(end); + if (!StrUtil.equals(startDay, endDay)) { + return Result.error("\u9884\u7ea6\u65f6\u95f4\u5fc5\u987b\u5728\u540c\u4e00\u5929\u5185"); + } + Set holidaySet = queryHolidaySet(); + List applyList = queryDayApplyList(apply.getSiteId(), startDay + " 00:00:00", startDay + " 23:59:59").stream() + .filter(item -> item != null && !StrUtil.equals(item.getId(), apply.getId())) + .collect(Collectors.toList()); + if (siteInfo.getReserveTimeType() != null && siteInfo.getReserveTimeType() == 2) { + if (!isWithinOpenRanges(siteInfo, startDay, start, end, holidaySet)) { + return Result.error("\u5168\u5929\u5019\u9884\u7ea6\u8bf7\u5728\u5f53\u5929\u5f00\u653e\u65f6\u95f4\u5185\u6309\u9884\u7ea6\u65f6\u95f4\u5355\u4f4d\u9009\u62e9\u4e00\u4e2a\u6216\u591a\u4e2a\u8fde\u7eed\u65f6\u6bb5"); + } + if (!isAlignedWithFullDayTimeUnit(siteInfo, start, end)) { + return Result.error("\u5168\u5929\u5019\u9884\u7ea6\u7684\u5f00\u59cb\u548c\u7ed3\u675f\u65f6\u95f4\u5fc5\u987b\u7b26\u5408\u9884\u7ea6\u65f6\u95f4\u5355\u4f4d"); + } + return Result.success(); + } + List blocks = buildSegmentedAvailabilityBlocks(siteInfo, startDay, applyList, holidaySet); + String startText = apply.getReserveStartTime(); + String endText = apply.getReserveEndTime(); + for (int i = 0; i < blocks.size(); i++) { + NutMap block = blocks.get(i); + if (!"available".equals(block.getString("status"))) { + continue; + } + if (!StrUtil.equals(startText, block.getString("startDateTime"))) { + continue; + } + String currentEnd = block.getString("endDateTime"); + if (StrUtil.equals(endText, currentEnd)) { + return Result.success(); + } + for (int j = i + 1; j < blocks.size(); j++) { + NutMap nextBlock = blocks.get(j); + NutMap prevBlock = blocks.get(j - 1); + if (!"available".equals(nextBlock.getString("status"))) { + break; + } + if (!StrUtil.equals(prevBlock.getString("endDateTime"), nextBlock.getString("startDateTime"))) { + break; + } + currentEnd = nextBlock.getString("endDateTime"); + if (StrUtil.equals(endText, currentEnd)) { + return Result.success(); + } + } + } + return Result.error("\u5206\u6bb5\u9884\u7ea6\u8bf7\u6309\u573a\u6b21\u65f6\u95f4\u5355\u4f4d\u9009\u62e9\u4e00\u4e2a\u6216\u591a\u4e2a\u8fde\u7eed\u65f6\u6bb5"); + } + + private boolean isWithinOpenRanges(SiteCugInfo siteInfo, String day, DateTime start, DateTime end, Set holidaySet) { + if (isSiteClosed(siteInfo, day, holidaySet)) { + return false; + } + List openRanges = buildOpenRanges(toSiteNutMap(siteInfo), false, false); + List disabledRanges = buildDisabledRanges(toSiteNutMap(siteInfo), day); + int startMinute = start.hour(true) * 60 + start.minute(); + int endMinute = end.hour(true) * 60 + end.minute(); + if (end.second() > 0) { + endMinute = Math.min(24 * 60, endMinute + 1); + } + final int finalEndMinute = endMinute; + boolean inOpenRange = openRanges.stream().anyMatch(range -> startMinute >= range[0] && finalEndMinute <= range[1]); + return inOpenRange && !intersectsAny(startMinute, finalEndMinute, disabledRanges); + } + + private boolean isAlignedWithFullDayTimeUnit(SiteCugInfo siteInfo, DateTime start, DateTime end) { + if (start == null || end == null || start.second() > 0 || end.second() > 0) { + return false; + } + NutMap source = getFullDayOpenHourConfig(siteInfo); + Integer slotStart = parseTimeToMinutes(source.getString("startTime")); + Integer slotEnd = parseTimeToMinutes(source.getString("endTime")); + int unitMinutes = parseTimeUnitMinutes(source.get("timeUnit")); + if (slotStart == null || slotEnd == null || slotEnd <= slotStart || unitMinutes <= 0) { + return false; + } + int startMinute = start.hour(true) * 60 + start.minute(); + int endMinute = end.hour(true) * 60 + end.minute(); + return startMinute >= slotStart + && endMinute <= slotEnd + && endMinute > startMinute + && (startMinute - slotStart) % unitMinutes == 0 + && (endMinute - slotStart) % unitMinutes == 0; + } + + @At + @ApiOperation("\u63d0\u4ea4\u9884\u7ea6") + @SaCheckPermission(value = {"siteCug.apply", "h5.siteCug.apply"}, mode = SaMode.OR) + @SLog(tag = "\u573a\u9986\u529f\u80fd\u7ba1\u7406-\u573a\u5730\u9884\u7ea6", msg = "\u63d0\u4ea4\u573a\u5730\u9884\u7ea6") public Result submit(@Param("data") SiteCugApply apply) { + Result scheduleValidate = validateApplySelectionBySchedule(apply); + if (scheduleValidate.getCode() != 0) { + return scheduleValidate; + } Map validate = applyService.validateApply(apply); if (validate.containsKey(false)) { return Result.error(validate.get(false)); @@ -122,19 +644,23 @@ public class SiteCugApplyController { } @At("/submitYearly") - @ApiOperation("按本年后续同星期同时间段批量提交预约") + @ApiOperation("\u6309\u672c\u5e74\u540e\u7eed\u540c\u661f\u671f\u540c\u65f6\u95f4\u6bb5\u6279\u91cf\u63d0\u4ea4\u9884\u7ea6") @Aop(TransAop.READ_COMMITTED) @SaCheckPermission(value = {"siteCug.apply", "h5.siteCug.apply"}, mode = SaMode.OR) - @SLog(tag = "场馆功能管理-场地预约", msg = "批量提交本年场地预约") + @SLog(tag = "\u573a\u9986\u529f\u80fd\u7ba1\u7406-\u573a\u5730\u9884\u7ea6", msg = "\u6279\u91cf\u63d0\u4ea4\u672c\u5e74\u573a\u5730\u9884\u7ea6") public Result submitYearly(@Param("data") SiteCugApply apply) { List applyList = buildYearlyApplyList(apply); if (applyList.isEmpty()) { - return Result.error("未生成可预约的日期"); + return Result.error("\u672a\u751f\u6210\u53ef\u9884\u7ea6\u7684\u65e5\u671f"); } for (SiteCugApply item : applyList) { + Result scheduleValidate = validateApplySelectionBySchedule(item); + if (scheduleValidate.getCode() != 0) { + return scheduleValidate; + } Map validate = applyService.validateApply(item); if (validate.containsKey(false)) { - return Result.error(String.format("%s 预约失败:%s", item.getReserveStartTime(), validate.get(false))); + return Result.error(String.format("%s \u9884\u7ea6\u5931\u8d25\uff1a%s", item.getReserveStartTime(), validate.get(false))); } } String yearlyBatchNo = YEARLY_BATCH_PREFIX + UUID.randomUUID(); @@ -142,11 +668,11 @@ public class SiteCugApplyController { item.setBackOption(yearlyBatchNo); submitSingleApply(item); } - return Result.success(String.format("已成功预约本年剩余%d个时间段", applyList.size())); + return Result.success(String.format("\u5df2\u6210\u529f\u9884\u7ea6\u622a\u81f3%s\u5171%d\u4e2a\u65f6\u95f4\u6bb5", apply.getYearlyReserveEndDate(), applyList.size())); } @At - @ApiOperation("查询预约限制配置") + @ApiOperation("\u67e5\u8be2\u9884\u7ea6\u9650\u5236\u914d\u7f6e") @SaCheckPermission(value = {"siteCug.apply", "h5.siteCug.apply"}, mode = SaMode.OR) public Result timeLimitConfig(@Param("siteId") String siteId) { if (StrUtil.isBlank(siteId)) { @@ -174,7 +700,7 @@ public class SiteCugApplyController { } private List buildYearlyApplyList(SiteCugApply apply) { - if (apply == null || StrUtil.hasBlank(apply.getReserveStartTime(), apply.getReserveEndTime())) { + if (apply == null || StrUtil.hasBlank(apply.getReserveStartTime(), apply.getReserveEndTime(), apply.getYearlyReserveEndDate())) { return new ArrayList<>(); } DateTime start = DateUtil.parseDateTime(apply.getReserveStartTime()); @@ -182,6 +708,10 @@ public class SiteCugApplyController { if (!end.isAfter(start)) { return new ArrayList<>(); } + DateTime reserveEndDate = parseReserveEndDate(apply.getYearlyReserveEndDate()); + if (reserveEndDate == null || reserveEndDate.isBefore(DateUtil.beginOfDay(start))) { + return new ArrayList<>(); + } SiteCugInfo siteInfo = infoService.fetch(apply.getSiteId()); if (siteInfo == null) { return new ArrayList<>(); @@ -205,9 +735,9 @@ public class SiteCugApplyController { int endMinute = end.minute(); int endSecond = end.second(); Date current = DateUtil.beginOfDay(start); - Date endOfYear = DateUtil.endOfYear(start); + Date batchEndDate = DateUtil.endOfDay(reserveEndDate); List result = new ArrayList<>(); - while (current.compareTo(endOfYear) <= 0) { + while (current.compareTo(batchEndDate) <= 0) { DateTime currentDate = DateUtil.date(current); String currentDay = DateUtil.formatDate(currentDate); if (currentDate.dayOfWeek() - 1 == targetWeek && !holidayList.contains(currentDay)) { @@ -222,6 +752,17 @@ public class SiteCugApplyController { return result; } + private DateTime parseReserveEndDate(String endDate) { + if (StrUtil.isBlank(endDate)) { + return null; + } + try { + return DateUtil.parseDate(endDate); + } catch (Exception e) { + return null; + } + } + private DateTime buildDateTime(DateTime date, int hour, int minute, int second) { return DateUtil.parseDateTime(String.format("%s %02d:%02d:%02d", DateUtil.formatDate(date), hour, minute, second)); } @@ -245,7 +786,8 @@ public class SiteCugApplyController { .setReserveEndTime(apply.getReserveEndTime()) .setJoinCount(apply.getJoinCount()) .setApplyCause(apply.getApplyCause()) - .setBackOption(apply.getBackOption()); + .setBackOption(apply.getBackOption()) + .setYearlyReserveEndDate(apply.getYearlyReserveEndDate()); } private void submitSingleApply(SiteCugApply apply) { @@ -261,4 +803,4 @@ public class SiteCugApplyController { flowEngine.executeProcessTask(task.getId(), SecurityUtil.getUserId(), args); } } -} \ No newline at end of file +} diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/siteCug/controller/SiteCugManageController.java b/src/main/java/com/budwk/app/zhgh/dayofficework/siteCug/controller/SiteCugManageController.java index 41dc9c90..d95de001 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/siteCug/controller/SiteCugManageController.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/siteCug/controller/SiteCugManageController.java @@ -27,12 +27,13 @@ import org.nutz.mvc.annotation.Param; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.stream.Collectors; @Slf4j @IocBean @Ok("json:full") -@Api(tags = "场地管理-场馆") +@Api(tags = "siteCug manage") @At("/platform/siteCug/manage") public class SiteCugManageController { @@ -47,7 +48,7 @@ public class SiteCugManageController { public void index() {} @At - @ApiOperation("分页查询") + @ApiOperation("page data") @SaCheckPermission("siteCug.manage") public Result pageData(PageForm pageForm, @Param("type") String type) { Cnd cnd = Cnd.NEW(); @@ -76,9 +77,9 @@ public class SiteCugManageController { } @At - @ApiOperation("新增/修改场地") + @ApiOperation("submit") @SaCheckPermission("siteCug.manage") - @SLog(tag = "场馆功能管理-场地管理", msg = "新增/修改场地") + @SLog(tag = "siteCug.manage", msg = "submit site") public Object submit(@Param("data") SiteCugInfo info) { List notApplyTimeList = info.getNotApplyTimeList(); if (notApplyTimeList != null) { @@ -89,27 +90,81 @@ public class SiteCugManageController { || StrUtil.isBlank(item.getString("endTime")) || item.getString("startTime").compareTo(item.getString("endTime")) >= 0); if (invalid) { - return Result.error("禁用时间设置有误"); + return Result.error("Invalid disabled time settings"); } } + + if (info.getReserveTimeType() == null) { + info.setReserveTimeType(1); + } + List segmentedOpenHours = info.getSegmentedOpenHours(); + NutMap fullDayOpenHour = info.getFullDayOpenHour(); + if ((segmentedOpenHours == null || segmentedOpenHours.isEmpty()) && info.getReserveTimeType() == 1) { + segmentedOpenHours = info.getOpenHours(); + } + if ((fullDayOpenHour == null || fullDayOpenHour.isEmpty()) && info.getReserveTimeType() == 2 && info.getOpenHours() != null && !info.getOpenHours().isEmpty()) { + fullDayOpenHour = info.getOpenHours().get(0); + } + if (info.getReserveTimeType() == 1) { + if (segmentedOpenHours == null || segmentedOpenHours.isEmpty()) { + return Result.error("Please add at least one booking slot"); + } + boolean invalid = segmentedOpenHours.stream().anyMatch(item -> + item == null + || StrUtil.isBlank(item.getString("startTime")) + || StrUtil.isBlank(item.getString("endTime")) + || item.getString("startTime").compareTo(item.getString("endTime")) >= 0 + || !isPositiveNumber(item.get("timeUnit"))); + if (invalid) { + return Result.error("Invalid segmented booking slot settings"); + } + if (hasOverlap(segmentedOpenHours)) { + return Result.error("Segmented booking slots cannot overlap"); + } + segmentedOpenHours = segmentedOpenHours.stream() + .filter(Objects::nonNull) + .map(this::normalizeOpenHourTimeUnit) + .collect(Collectors.toList()); + info.setSegmentedOpenHours(segmentedOpenHours); + info.setOpenHours(segmentedOpenHours); + } else if (info.getReserveTimeType() == 2) { + if (fullDayOpenHour == null || fullDayOpenHour.isEmpty()) { + return Result.error("Please set the full-day booking time range"); + } + NutMap item = fullDayOpenHour; + if (item == null + || StrUtil.isBlank(item.getString("startTime")) + || StrUtil.isBlank(item.getString("endTime")) + || item.getString("startTime").compareTo(item.getString("endTime")) >= 0) { + return Result.error("Invalid full-day booking time range"); + } + if (!isPositiveNumber(item.get("timeUnit"))) { + return Result.error("Invalid full-day booking time unit"); + } + fullDayOpenHour = normalizeOpenHourTimeUnit(fullDayOpenHour); + info.setFullDayOpenHour(fullDayOpenHour); + info.setOpenHours(List.of(fullDayOpenHour)); + } + if (info.getFilterHolidays() == null) { info.setFilterHolidays(false); } infoService.insertOrUpdate(info); - return Result.success(); + SiteCugInfo savedInfo = infoService.fetch(info.getId()); + return Result.success(savedInfo == null ? info : savedInfo); } @At - @ApiOperation("删除场地") + @ApiOperation("delete") @SaCheckPermission("siteCug.manage") - @SLog(tag = "场馆功能管理-场地管理", msg = "删除场地") + @SLog(tag = "siteCug.manage", msg = "delete site") public Object delete(String id) { infoService.delete(id); return Result.success(); } @At - @ApiOperation("查询场地") + @ApiOperation("query sites") @SaCheckLogin public Result querySites() { List list = infoService.query(Cnd.where(SiteCugInfo::getState, "=", true).desc(SiteCugInfo::getSortNum)); @@ -117,7 +172,7 @@ public class SiteCugManageController { } @At - @ApiOperation("查询单个场地") + @ApiOperation("query site info") @SaCheckLogin public Result info(String id) { SiteCugInfo info = infoService.fetch(id); @@ -129,4 +184,59 @@ public class SiteCugManageController { map.put("typeName", type == null ? "" : type.getName()); return Result.success(map); } + + private boolean isPositiveNumber(Object value) { + if (value == null) { + return false; + } + try { + return Double.parseDouble(String.valueOf(value)) > 0; + } catch (Exception e) { + return false; + } + } + + private NutMap normalizeOpenHourTimeUnit(NutMap item) { + if (item == null) { + return null; + } + item.put("timeUnit", normalizeTimeUnitMinutes(item.get("timeUnit"))); + return item; + } + + private int normalizeTimeUnitMinutes(Object value) { + if (value == null) { + return 60; + } + try { + double parsed = Double.parseDouble(String.valueOf(value)); + if (parsed <= 0) { + return 60; + } + return (int) Math.round(parsed); + } catch (Exception e) { + return 60; + } + } + + private boolean hasOverlap(List openHours) { + if (openHours == null || openHours.size() < 2) { + return false; + } + List validHours = openHours.stream() + .filter(item -> item != null + && StrUtil.isNotBlank(item.getString("startTime")) + && StrUtil.isNotBlank(item.getString("endTime")) + && item.getString("startTime").compareTo(item.getString("endTime")) < 0) + .sorted((a, b) -> a.getString("startTime").compareTo(b.getString("startTime"))) + .collect(Collectors.toList()); + for (int i = 1; i < validHours.size(); i++) { + NutMap prev = validHours.get(i - 1); + NutMap current = validHours.get(i); + if (current.getString("startTime").compareTo(prev.getString("endTime")) < 0) { + return true; + } + } + return false; + } } diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/siteCug/controller/SiteCugMineController.java b/src/main/java/com/budwk/app/zhgh/dayofficework/siteCug/controller/SiteCugMineController.java index c02f9a8d..3f33fc89 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/siteCug/controller/SiteCugMineController.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/siteCug/controller/SiteCugMineController.java @@ -81,6 +81,7 @@ public class SiteCugMineController { t.finishTime, t.taskParentId, t.variable taskVariable, + CASE WHEN info.reserveType = 'club' THEN info.clubName ELSE info.applyUnionName END AS reserveTargetName, CASE WHEN ins.state in (10, 20) AND (t.displayName LIKE '%校工会允许%' OR t.taskName = 'c8c03407-cf26-4e0b-82f1-b2afc9c79996') THEN 1 @@ -105,7 +106,6 @@ public class SiteCugMineController { SqlExpressionGroup seg = new SqlExpressionGroup(); seg.or(SiteCugApply::getApplyUserName, "like", "%" + pageForm.getSearchKeyword() + "%"); seg.or(SiteCugApply::getApplyLoginName, "like", "%" + pageForm.getSearchKeyword() + "%"); - seg.or("si.name", "like", "%" + pageForm.getSearchKeyword() + "%"); cnd.and(seg); } cnd.andEX("si.typeId", "=", siteType); diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/siteCug/controller/SiteCugSchoolUnionAuditController.java b/src/main/java/com/budwk/app/zhgh/dayofficework/siteCug/controller/SiteCugSchoolUnionAuditController.java index 1ebe3d28..b14d1353 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/siteCug/controller/SiteCugSchoolUnionAuditController.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/siteCug/controller/SiteCugSchoolUnionAuditController.java @@ -75,6 +75,7 @@ public class SiteCugSchoolUnionAuditController { SELECT info.*, si.name as siteName, + CASE WHEN info.reserveType = 'club' THEN info.clubName ELSE info.applyUnionName END AS reserveTargetName, ins.id AS instanceId, ins.businessNo, ins.state instanceState, @@ -113,7 +114,6 @@ public class SiteCugSchoolUnionAuditController { SqlExpressionGroup seg = new SqlExpressionGroup(); seg.or(SiteCugApply::getApplyUserName, "like", "%" + pageForm.getSearchKeyword() + "%"); seg.or(SiteCugApply::getApplyLoginName, "like", "%" + pageForm.getSearchKeyword() + "%"); - seg.or("si.name", "like", "%" + pageForm.getSearchKeyword() + "%"); cnd.and(seg); } cnd.andEX("si.typeId", "=", siteType); @@ -129,7 +129,7 @@ public class SiteCugSchoolUnionAuditController { } if (StrUtil.isNotBlank(pageForm.getPageOrderName()) && StrUtil.isNotBlank(pageForm.getPageOrderBy())) { - cnd.orderBy(pageForm.getPageOrderName(), PageUtil.getOrder(pageForm.getPageOrderBy())); + cnd.orderBy(resolveOrderColumn(pageForm.getPageOrderName()), PageUtil.getOrder(pageForm.getPageOrderBy())); } else { cnd.desc("info.createdAt"); } @@ -139,6 +139,36 @@ public class SiteCugSchoolUnionAuditController { return Result.success(pageVO); } + private String resolveOrderColumn(String pageOrderName) { + if (StrUtil.isBlank(pageOrderName)) { + return "info.createdAt"; + } + switch (pageOrderName) { + case "siteName": + return "si.name"; + case "applyUserName": + return "info.applyUserName"; + case "applyLoginName": + return "info.applyLoginName"; + case "yearlyBatch": + return "yearlyBatch"; + case "reserveTargetName": + return "reserveTargetName"; + case "reserveStartTime": + return "info.reserveStartTime"; + case "reserveEndTime": + return "info.reserveEndTime"; + case "applyMobile": + return "info.applyMobile"; + case "curTaskName": + return "curTaskName"; + case "instanceState": + return "ins.state"; + default: + return "info.createdAt"; + } + } + @At("/executeTask") @ApiOperation("执行审核任务") @Aop(TransAop.READ_COMMITTED) diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/siteCug/model/SiteCugApply.java b/src/main/java/com/budwk/app/zhgh/dayofficework/siteCug/model/SiteCugApply.java index d332d548..4857fa3d 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/siteCug/model/SiteCugApply.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/siteCug/model/SiteCugApply.java @@ -109,4 +109,6 @@ public class SiteCugApply extends BaseModel { @Comment("反馈意见") @ColDefine(type = ColType.VARCHAR, width = 200) private String backOption; + + private String yearlyReserveEndDate; } diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/siteCug/model/SiteCugInfo.java b/src/main/java/com/budwk/app/zhgh/dayofficework/siteCug/model/SiteCugInfo.java index 2e491ca9..96be2a49 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/siteCug/model/SiteCugInfo.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/siteCug/model/SiteCugInfo.java @@ -78,6 +78,27 @@ public class SiteCugInfo extends BaseModel { @ColDefine(type = ColType.MYSQL_JSON) private List notApplyTimeList; + @Column + @Comment("预约时间段类型(1分段预约,2全天候预约)") + @ColDefine(type = ColType.INT) + @Default("1") + private Integer reserveTimeType; + + @Column + @Comment("场次信息") + @ColDefine(type = ColType.MYSQL_JSON) + private List openHours; + + @Column + @Comment("分段预约场次信息") + @ColDefine(type = ColType.MYSQL_JSON) + private List segmentedOpenHours; + + @Column + @Comment("全天候预约时间段") + @ColDefine(type = ColType.MYSQL_JSON) + private NutMap fullDayOpenHour; + @Column @Comment("开启状态") @ColDefine(type = ColType.BOOLEAN) @@ -94,4 +115,9 @@ public class SiteCugInfo extends BaseModel { @Comment("场地介绍") @ColDefine(type = ColType.TEXT) private String introduce; -} \ No newline at end of file + + @Column + @Comment("场地照片") + @ColDefine(type = ColType.VARCHAR, width = 255) + private String sitePhoto; +} diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/siteCug/service/impl/SiteCugApplyServiceImpl.java b/src/main/java/com/budwk/app/zhgh/dayofficework/siteCug/service/impl/SiteCugApplyServiceImpl.java index a2357ac1..dba66f65 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/siteCug/service/impl/SiteCugApplyServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/siteCug/service/impl/SiteCugApplyServiceImpl.java @@ -136,6 +136,7 @@ public class SiteCugApplyServiceImpl extends BaseServiceImpl imple si.name AS siteName, ins.id AS instanceId, ins.processDefineId instanceProcessDefineId, + CASE WHEN info.backOption LIKE 'YEARLY_BATCH:%' THEN 1 ELSE 0 END AS yearlyBatch, CASE WHEN info.reserveType = 'club' THEN '协会预约' ELSE '分工会预约' END AS reserveTypeName, CASE WHEN info.reserveType = 'club' THEN info.clubName ELSE info.applyUnionName END AS reserveTargetName, DATE_FORMAT(info.createdAt, '%Y-%m-%d %H:%i:%s') AS applyTime @@ -155,7 +156,6 @@ public class SiteCugApplyServiceImpl extends BaseServiceImpl imple SqlExpressionGroup seg = new SqlExpressionGroup(); seg.or(SiteCugApply::getApplyUserName, "like", "%" + pageForm.getSearchKeyword() + "%"); seg.or(SiteCugApply::getApplyLoginName, "like", "%" + pageForm.getSearchKeyword() + "%"); - seg.or("si.name", "like", "%" + pageForm.getSearchKeyword() + "%"); cnd.and(seg); } if (StrUtil.isNotBlank(pageForm.getReserveTargetKeyword())) { diff --git a/src/main/java/com/budwk/app/zhgh/staffmanage/member/controller/statistics/MemberAnalysisController.java b/src/main/java/com/budwk/app/zhgh/staffmanage/member/controller/statistics/MemberAnalysisController.java index 5dbbfbfb..69190ccb 100644 --- a/src/main/java/com/budwk/app/zhgh/staffmanage/member/controller/statistics/MemberAnalysisController.java +++ b/src/main/java/com/budwk/app/zhgh/staffmanage/member/controller/statistics/MemberAnalysisController.java @@ -2,6 +2,7 @@ package com.budwk.app.zhgh.staffmanage.member.controller.statistics; import cn.dev33.satoken.annotation.SaCheckPermission; import com.budwk.app.base.result.Result; +import com.budwk.app.zhgh.staffmanage.member.param.pageform.MemberStatisticsPageForm; import com.budwk.app.zhgh.staffmanage.member.service.MemberStatisticsService; import org.apache.poi.ss.usermodel.Workbook; import org.nutz.ioc.loader.annotation.Inject; @@ -39,14 +40,12 @@ public class MemberAnalysisController { @At @SaCheckPermission("member.statistics.analysis") - public Result pageData(@Param(value = "queryId") String queryId, - @Param(value = "queryType") String queryType, - @Param(value = "currentYear") Integer currentYear) { + public Result pageData(@Param("..") MemberStatisticsPageForm pageForm) { List list = new ArrayList<>(); - if ("fgh".equals(queryType)) { - list = memberStatisticsService.getUnionAnalysisData(queryId, currentYear); + if ("fgh".equals(pageForm.getQueryType())) { + list = memberStatisticsService.getUnionAnalysisData(pageForm); } else { - list = memberStatisticsService.getUnitAnalysisData(queryId, currentYear); + list = memberStatisticsService.getUnitAnalysisData(pageForm); } return Result.success(list); } @@ -58,10 +57,13 @@ public class MemberAnalysisController { public void doExport(@Param("queryType") String queryType, @Param("currentYear") String currentYear, @Param("queryId") String queryId, HttpServletResponse response) { List list = new ArrayList<>(); + MemberStatisticsPageForm pageForm = new MemberStatisticsPageForm(); + pageForm.setQueryId(queryId); + pageForm.setCurrentYear(Integer.valueOf(currentYear)); if ("union".equals(queryType)) { - list = memberStatisticsService.getUnionAnalysisData(queryId, Integer.valueOf(currentYear)); + list = memberStatisticsService.getUnionAnalysisData(pageForm); } else { - list = memberStatisticsService.getUnitAnalysisData(queryId, Integer.valueOf(currentYear)); + list = memberStatisticsService.getUnitAnalysisData(pageForm); } NutMap sumMap = NutMap.NEW(); sumMap.put("union".equals(queryType) ? "unionName" : "unitName", "合计"); diff --git a/src/main/java/com/budwk/app/zhgh/staffmanage/member/service/MemberStatisticsService.java b/src/main/java/com/budwk/app/zhgh/staffmanage/member/service/MemberStatisticsService.java index 5e097e30..9acb34db 100644 --- a/src/main/java/com/budwk/app/zhgh/staffmanage/member/service/MemberStatisticsService.java +++ b/src/main/java/com/budwk/app/zhgh/staffmanage/member/service/MemberStatisticsService.java @@ -40,7 +40,14 @@ public interface MemberStatisticsService extends BaseService { * @param currentYear 所属年度 * @return */ - List getUnionAnalysisData(String unionId, Integer currentYear); + /** + * 获取工会分析数据。 + * pageForm 中除了机构和年份外,还会带上人员分类、人员属性等附加筛选条件。 + * + * @param pageForm 统计查询参数 + * @return 工会分析结果 + */ + List getUnionAnalysisData(MemberStatisticsPageForm pageForm); /** @@ -49,7 +56,14 @@ public interface MemberStatisticsService extends BaseService { * @param currentYear 所属年度 * @return */ - List getUnitAnalysisData(String unitId, Integer currentYear); + /** + * 获取单位分析数据。 + * pageForm 中除了机构和年份外,还会带上人员分类、人员属性等附加筛选条件。 + * + * @param pageForm 统计查询参数 + * @return 单位分析结果 + */ + List getUnitAnalysisData(MemberStatisticsPageForm pageForm); /** * 导出分析数据 diff --git a/src/main/java/com/budwk/app/zhgh/staffmanage/member/service/impl/MemberStatisticsServiceImpl.java b/src/main/java/com/budwk/app/zhgh/staffmanage/member/service/impl/MemberStatisticsServiceImpl.java index 82d5ab49..a6b65e1c 100644 --- a/src/main/java/com/budwk/app/zhgh/staffmanage/member/service/impl/MemberStatisticsServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/staffmanage/member/service/impl/MemberStatisticsServiceImpl.java @@ -29,6 +29,7 @@ import org.nutz.lang.util.NutMap; import java.util.ArrayList; import java.util.Calendar; import java.util.List; +import java.util.stream.Collectors; /** * @version 1.0 @@ -212,32 +213,79 @@ public class MemberStatisticsServiceImpl extends BaseServiceImpl imple @Override - public List getUnionAnalysisData(String queryId, Integer currentYear) { + public List getUnionAnalysisData(MemberStatisticsPageForm pageForm) { + String queryId = pageForm.getQueryId(); + Integer currentYear = pageForm.getCurrentYear(); + String lastYearPersonFilter = getAnalysisPersonFilterSql(pageForm, "his"); + String currentPersonFilter = getAnalysisPersonFilterSql(pageForm, "u"); + boolean useCurrentUserView = ((Integer) DateUtil.thisYear()).equals(currentYear); Sql sql = Sqls.create(""" SELECT gh.id, gh.unionCode, gh.`name` as unionName, - ( SELECT count( 1 ) FROM member_history his LEFT JOIN vw_user u on u.id = his.userId WHERE his.`year` = @lastYear AND u.unionId = gh.id ) AS lastYearMemberNum, - ( SELECT count( 1 ) FROM member_apply_record WHERE changeUnionId is not null AND unionId = gh.id AND unionId != changeUnionId AND YEAR ( applyDateTime ) = @currentYear ) AS memberUnitChangeNumIn, - ( SELECT count( 1 ) FROM member_apply_record WHERE changeUnionId is not null AND changeUnionId = gh.id AND unionId != changeUnionId AND YEAR ( applyDateTime ) = @currentYear ) AS memberUnitChangeNumOut, - ( SELECT count( 1 ) FROM sys_user_history his LEFT JOIN `vw_user` u ON u.loginname = his.loginname WHERE JSON_CONTAINS(his.changeTypes, '["NEW", "RESTORE"]', '$') AND u.unionId = gh.id AND YEAR ( his.changeTime ) = @currentYear ) AS newResetMemberNum, - ( SELECT count( 1 ) FROM sys_user_history his LEFT JOIN `vw_user` u ON u.loginname = his.loginname WHERE JSON_CONTAINS(his.changeTypes, '["WITHDRAWAL"]', '$') AND u.unionId = gh.id AND YEAR ( his.changeTime ) = @currentYear ) AS reduceOtherNum, - ( SELECT count( 1 ) FROM $table where member = 1 and unionId = gh.id) as currentYearMemberNum + IFNULL(lastYear.lastYearMemberNum, 0) AS lastYearMemberNum, + IFNULL(changeIn.memberUnitChangeNumIn, 0) AS memberUnitChangeNumIn, + IFNULL(changeOut.memberUnitChangeNumOut, 0) AS memberUnitChangeNumOut, + IFNULL(newReset.newResetMemberNum, 0) AS newResetMemberNum, + IFNULL(reduceOther.reduceOtherNum, 0) AS reduceOtherNum, + IFNULL(currentMember.currentYearMemberNum, 0) AS currentYearMemberNum FROM sys_union gh + LEFT JOIN ( + SELECT his.unionId, COUNT(1) AS lastYearMemberNum + FROM member_history his + WHERE his.`year` = @lastYear $lastYearPersonFilter + GROUP BY his.unionId + ) lastYear ON lastYear.unionId = gh.id + LEFT JOIN ( + SELECT unionId, COUNT(1) AS memberUnitChangeNumIn + FROM member_apply_record + WHERE changeUnionId is not null AND unionId != changeUnionId AND YEAR(applyDateTime) = @currentYear + GROUP BY unionId + ) changeIn ON changeIn.unionId = gh.id + LEFT JOIN ( + SELECT changeUnionId AS unionId, COUNT(1) AS memberUnitChangeNumOut + FROM member_apply_record + WHERE changeUnionId is not null AND unionId != changeUnionId AND YEAR(applyDateTime) = @currentYear + GROUP BY changeUnionId + ) changeOut ON changeOut.unionId = gh.id + LEFT JOIN ( + SELECT u.unionId, COUNT(1) AS newResetMemberNum + FROM sys_user_history his + JOIN vw_user u ON u.loginname = his.loginname + WHERE JSON_CONTAINS(his.changeTypes, '["NEW", "RESTORE"]', '$') + AND YEAR(his.changeTime) = @currentYear $currentPersonFilter + GROUP BY u.unionId + ) newReset ON newReset.unionId = gh.id + LEFT JOIN ( + SELECT u.unionId, COUNT(1) AS reduceOtherNum + FROM sys_user_history his + JOIN vw_user u ON u.loginname = his.loginname + WHERE JSON_CONTAINS(his.changeTypes, '["WITHDRAWAL"]', '$') + AND YEAR(his.changeTime) = @currentYear $currentPersonFilter + GROUP BY u.unionId + ) reduceOther ON reduceOther.unionId = gh.id + LEFT JOIN ( + SELECT u.unionId, COUNT(1) AS currentYearMemberNum + FROM $table u + WHERE u.member = 1 $currentPersonFilter $currentYearFilter + GROUP BY u.unionId + ) currentMember ON currentMember.unionId = gh.id $condition """); sql.setParam("lastYear", currentYear - 1); sql.setParam("currentYear", currentYear); - sql.setVar("table", ((Integer) DateUtil.thisYear()).equals(currentYear) ? "`vw_user`" : "member_history"); + sql.setVar("table", useCurrentUserView ? "`vw_user`" : "member_history"); + sql.setVar("lastYearPersonFilter", new Static(lastYearPersonFilter)); + sql.setVar("currentPersonFilter", new Static(currentPersonFilter)); + sql.setVar("currentYearFilter", new Static(getAnalysisYearFilterSql("u", useCurrentUserView, currentYear))); Cnd cnd = Cnd.NEW(); if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name())) { cnd.and("gh.id", "=", SecurityUtil.getUnionId()); } else { cnd.andEX("gh.id", "=", queryId); } - cnd.groupBy("gh.id, gh.unionCode, gh.name"); cnd.asc("gh.unionCode"); sql.setCondition(cnd); List list = listMap(sql); @@ -250,24 +298,73 @@ public class MemberStatisticsServiceImpl extends BaseServiceImpl imple @Override - public List getUnitAnalysisData(String queryId, Integer currentYear) { + public List getUnitAnalysisData(MemberStatisticsPageForm pageForm) { + String queryId = pageForm.getQueryId(); + Integer currentYear = pageForm.getCurrentYear(); + String lastYearPersonFilter = getAnalysisPersonFilterSql(pageForm, "his"); + String currentPersonFilter = getAnalysisPersonFilterSql(pageForm, "u"); + boolean useCurrentUserView = ((Integer) DateUtil.thisYear()).equals(currentYear); Sql sql = Sqls.create(""" SELECT dw.id, dw.unitcode as unitCode, dw.`name` as unitName, - ( SELECT count( 1 ) FROM member_history WHERE `year` = @lastYear AND unitId = dw.id ) AS lastYearMemberNum, - ( SELECT count( 1 ) FROM member_apply_record WHERE changeUnitId is not null AND unitId = dw.id AND unitId != changeUnitId AND YEAR ( applyDateTime ) = @currentYear ) AS memberUnitChangeNumIn, - ( SELECT count( 1 ) FROM member_apply_record WHERE changeUnitId is not null AND changeUnitId = dw.id AND unitId != changeUnitId AND YEAR ( applyDateTime ) = @currentYear ) AS memberUnitChangeNumOut, - ( SELECT count( 1 ) FROM sys_user_history his LEFT JOIN vw_user u ON u.loginname = his.loginname WHERE JSON_CONTAINS(his.changeTypes, '["NEW", "RESTORE"]', '$') AND u.unitId = dw.id AND YEAR ( his.changeTime ) = @currentYear ) AS newResetMemberNum, - ( SELECT count( 1 ) FROM sys_user_history his LEFT JOIN vw_user u ON u.loginname = his.loginname WHERE JSON_CONTAINS(his.changeTypes, '["WITHDRAWAL"]', '$') AND u.unitId = dw.id AND YEAR ( his.changeTime ) = @currentYear ) AS reduceOtherNum, - ( SELECT count( 1 ) from vw_user where member = 1 and unitId = dw.id) as currentYearMemberNum + IFNULL(lastYear.lastYearMemberNum, 0) AS lastYearMemberNum, + IFNULL(changeIn.memberUnitChangeNumIn, 0) AS memberUnitChangeNumIn, + IFNULL(changeOut.memberUnitChangeNumOut, 0) AS memberUnitChangeNumOut, + IFNULL(newReset.newResetMemberNum, 0) AS newResetMemberNum, + IFNULL(reduceOther.reduceOtherNum, 0) AS reduceOtherNum, + IFNULL(currentMember.currentYearMemberNum, 0) AS currentYearMemberNum FROM sys_unit dw + LEFT JOIN ( + SELECT his.unitId, COUNT(1) AS lastYearMemberNum + FROM member_history his + WHERE his.`year` = @lastYear $lastYearPersonFilter + GROUP BY his.unitId + ) lastYear ON lastYear.unitId = dw.id + LEFT JOIN ( + SELECT unitId, COUNT(1) AS memberUnitChangeNumIn + FROM member_apply_record + WHERE changeUnitId is not null AND unitId != changeUnitId AND YEAR(applyDateTime) = @currentYear + GROUP BY unitId + ) changeIn ON changeIn.unitId = dw.id + LEFT JOIN ( + SELECT changeUnitId AS unitId, COUNT(1) AS memberUnitChangeNumOut + FROM member_apply_record + WHERE changeUnitId is not null AND unitId != changeUnitId AND YEAR(applyDateTime) = @currentYear + GROUP BY changeUnitId + ) changeOut ON changeOut.unitId = dw.id + LEFT JOIN ( + SELECT u.unitId, COUNT(1) AS newResetMemberNum + FROM sys_user_history his + JOIN vw_user u ON u.loginname = his.loginname + WHERE JSON_CONTAINS(his.changeTypes, '["NEW", "RESTORE"]', '$') + AND YEAR(his.changeTime) = @currentYear $currentPersonFilter + GROUP BY u.unitId + ) newReset ON newReset.unitId = dw.id + LEFT JOIN ( + SELECT u.unitId, COUNT(1) AS reduceOtherNum + FROM sys_user_history his + JOIN vw_user u ON u.loginname = his.loginname + WHERE JSON_CONTAINS(his.changeTypes, '["WITHDRAWAL"]', '$') + AND YEAR(his.changeTime) = @currentYear $currentPersonFilter + GROUP BY u.unitId + ) reduceOther ON reduceOther.unitId = dw.id + LEFT JOIN ( + SELECT u.unitId, COUNT(1) AS currentYearMemberNum + FROM $table u + WHERE u.member = 1 $currentPersonFilter $currentYearFilter + GROUP BY u.unitId + ) currentMember ON currentMember.unitId = dw.id $condition """); sql.setParam("lastYear", currentYear - 1); sql.setParam("currentYear", currentYear); + sql.setVar("lastYearPersonFilter", new Static(lastYearPersonFilter)); + sql.setVar("currentPersonFilter", new Static(currentPersonFilter)); + sql.setVar("table", useCurrentUserView ? "`vw_user`" : "member_history"); + sql.setVar("currentYearFilter", new Static(getAnalysisYearFilterSql("u", useCurrentUserView, currentYear))); Cnd cnd = Cnd.NEW(); cnd.and("dw.unitLevel", "=", 3); if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name())) { @@ -275,7 +372,6 @@ public class MemberStatisticsServiceImpl extends BaseServiceImpl imple } else { cnd.andEX("dw.id", "=", queryId); } - cnd.groupBy("dw.id", "dw.unitcode", "dw.name"); cnd.asc("dw.unitcode"); sql.setCondition(cnd); List list = listMap(sql); @@ -286,6 +382,61 @@ public class MemberStatisticsServiceImpl extends BaseServiceImpl imple return list; } + /** + * 分析统计的 SQL 由多个子查询组成,外层机构条件不会自动作用到人员子查询。 + * 这里统一拼出人员分类、人员属性的过滤片段,分别注入到各个统计子查询里, + * 保证 analysis 页面和前端标签筛选口径一致。 + * + * @param pageForm 查询参数 + * @param alias 子查询里的人员表别名 + * @return 可直接拼接到子查询 WHERE 末尾的 SQL 片段 + */ + private String getAnalysisPersonFilterSql(MemberStatisticsPageForm pageForm, String alias) { + StringBuilder sqlBuilder = new StringBuilder(); + sqlBuilder.append(getAnalysisInSql(alias, "aidFundMemberUserType", pageForm.getAidFundMemberUserTypes())); + sqlBuilder.append(getAnalysisInSql(alias, "userAttribute", pageForm.getUserAttributes())); + return sqlBuilder.toString(); + } + + /** + * analysis 的当前数量在查历史年度时会切到 member_history, + * 这里统一补年度条件,避免历史年度把整张历史表都统计进去。 + * + * @param alias 子查询别名 + * @param useCurrentUserView 是否使用当前人员视图 + * @param currentYear 查询年度 + * @return 年度过滤 SQL 片段 + */ + private String getAnalysisYearFilterSql(String alias, boolean useCurrentUserView, Integer currentYear) { + if (useCurrentUserView) { + return ""; + } + return " AND " + alias + ".`year` = " + currentYear; + } + + /** + * 这里仅用于拼接 analysis 模块固定字段的 IN 条件, + * 会过滤空值并转义单引号,避免直接把前端原始值拼回 SQL。 + * + * @param alias 子查询别名 + * @param columnName 字段名 + * @param values 筛选值列表 + * @return IN 条件 SQL 片段 + */ + private String getAnalysisInSql(String alias, String columnName, List values) { + if (Lang.isEmpty(values)) { + return ""; + } + List validValues = values.stream().filter(Strings::isNotBlank).collect(Collectors.toList()); + if (Lang.isEmpty(validValues)) { + return ""; + } + String inValueSql = validValues.stream() + .map(value -> "'" + value.replace("'", "''") + "'") + .collect(Collectors.joining(", ")); + return " AND " + alias + "." + columnName + " IN (" + inValueSql + ")"; + } + @Override public Workbook exportAnalysisExcel(List list, String queryType) { @@ -348,6 +499,7 @@ public class MemberStatisticsServiceImpl extends BaseServiceImpl imple // sql.setVar("abbr", new Static("his")); } cnd.andEX("u.userAttribute", "in", pageForm.getUserAttributes()); + cnd.andEX("u.aidFundMemberUserType", "in", pageForm.getAidFundMemberUserTypes()); cnd.groupBy("un.id", "un.unionCode", "un.name"); cnd.asc("un.unionCode"); sql.setCondition(cnd); @@ -388,6 +540,7 @@ public class MemberStatisticsServiceImpl extends BaseServiceImpl imple sql.setVar("yearCnd", new Static(" AND u.`year` = %d".formatted(currentYear))); } cnd.andEX("u.userAttribute", "in", pageForm.getUserAttributes()); + cnd.andEX("u.aidFundMemberUserType", "in", pageForm.getAidFundMemberUserTypes()); cnd.and("un.unitLevel", "=", 2); cnd.groupBy("un.id", "un.unitcode", "un.name"); cnd.asc("un.unitcode"); diff --git a/src/main/java/com/budwk/app/zhgh/welfare/param/WelfareListPageForm.java b/src/main/java/com/budwk/app/zhgh/welfare/param/WelfareListPageForm.java index f16777e7..1ba712dc 100644 --- a/src/main/java/com/budwk/app/zhgh/welfare/param/WelfareListPageForm.java +++ b/src/main/java/com/budwk/app/zhgh/welfare/param/WelfareListPageForm.java @@ -55,4 +55,10 @@ public class WelfareListPageForm extends PageForm { @ApiModelProperty("在职状态") private String[] userStates; + @ApiModelProperty("人员分类") + private String aidFundMemberUserType; + + @ApiModelProperty("人员属性") + private String userAttribute; + } diff --git a/src/main/java/com/budwk/app/zhgh/welfare/param/WelfareSelectionSituationPageForm.java b/src/main/java/com/budwk/app/zhgh/welfare/param/WelfareSelectionSituationPageForm.java index 531a417a..6d17fbdf 100644 --- a/src/main/java/com/budwk/app/zhgh/welfare/param/WelfareSelectionSituationPageForm.java +++ b/src/main/java/com/budwk/app/zhgh/welfare/param/WelfareSelectionSituationPageForm.java @@ -31,4 +31,10 @@ public class WelfareSelectionSituationPageForm extends PageForm { @ApiModelProperty("是否已选择") private Boolean isSelect; + + @ApiModelProperty("人员分类") + private String aidFundMemberUserType; + + @ApiModelProperty("人员属性") + private String userAttribute; } diff --git a/src/main/java/com/budwk/app/zhgh/welfare/service/impl/WelfareListServiceImpl.java b/src/main/java/com/budwk/app/zhgh/welfare/service/impl/WelfareListServiceImpl.java index 6bbce8a1..95f04c04 100644 --- a/src/main/java/com/budwk/app/zhgh/welfare/service/impl/WelfareListServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/welfare/service/impl/WelfareListServiceImpl.java @@ -417,6 +417,8 @@ public class WelfareListServiceImpl extends BaseServiceImpl impleme cnd.andEX("t1.personType", "in", pageForm.getPersonTypes()); cnd.andEX("t1.preparedBy", "in", pageForm.getPreparedBys()); cnd.andEX("t1.userState", "in", pageForm.getUserStates()); + cnd.andEX("t2.aidFundMemberUserType", "=", pageForm.getAidFundMemberUserType()); + cnd.andEX("t2.userAttribute", "=", pageForm.getUserAttribute()); if (StrUtil.isAllBlank(pageForm.getPageOrderName(), pageForm.getPageOrderBy())) { cnd.asc("t1.welfareUnionName"); cnd.asc("t1.welfareUnitName"); diff --git a/src/main/java/com/budwk/app/zhgh/welfare/service/impl/WelfareSelectionSituationServiceImpl.java b/src/main/java/com/budwk/app/zhgh/welfare/service/impl/WelfareSelectionSituationServiceImpl.java index d1a4d944..1c5e0a2b 100644 --- a/src/main/java/com/budwk/app/zhgh/welfare/service/impl/WelfareSelectionSituationServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/welfare/service/impl/WelfareSelectionSituationServiceImpl.java @@ -83,6 +83,8 @@ public class WelfareSelectionSituationServiceImpl extends BaseServiceImpl - - + + + - + 添加至原有分组 添加到新分组 - + 保存人员结果 保存SQL条件 @@ -352,38 +350,47 @@ - + {{ formatGroupDisplayName(item) }} -
当前还没有同类型分组,请先创建新分组。
+
当前还没有同类型分组,请先创建新分组。
+ - +
- - 取 消 - 确 定 - + + 取消 + 确定 +
- - + + @@ -410,33 +417,23 @@ module.exports = { activityGroupList: [], activityGroupList2: [], memberTypeOptions: [ - { - code: "unionMember", - name: "工会会员" - }, - { - code: "welfareMember", - name: "福利会员" - }, - { - code: "sickFundMember", - name: "基金会员" - } + { code: "unionMember", name: "工会会员" }, + { code: "welfareMember", name: "福利会员" }, + { code: "sickFundMember", name: "基金会员" } ], sexTypeOptions: [ - {code: "男", name: "男"}, - {code: "女", name: "女"} + { code: "male", name: "男" }, + { code: "female", name: "女" } ], pageForm: { age: [0, 0], - unionId: "", - unitId: "", + unionIds: [], clubId: "", searchName: "u.username", personTypes: [], userStates: [], memberStatus: [], - memberTypes: [], + memberTypes: ["工会会员"], sexTypes: [], minAge: 0, maxAge: 0, @@ -449,36 +446,30 @@ module.exports = { activityUserCnd: "", reverseSelection: false }, - activityUnions: [], unions: [], - units: [], - activityUnits: [], - meetingOptions: [], roleList: [], tableHeight: "0px", tableColumns: [ - {prop: "loginName", label: "工号"}, - {prop: "userName", label: "姓名"}, - {prop: "sex", label: "性别"}, - {prop: "birthday", label: "出生年月", sortable: true}, - {prop: "age", label: "年龄", sortable: true}, - {prop: "mobile", label: "联系电话"}, - {prop: "personType", label: "教职工类别", sortable: true}, - {prop: "userState", label: "在职状态", sortable: true}, - {prop: "unitName", label: "所属单位", sortable: true}, - {prop: "unionName", label: "所属工会", sortable: true} - // {prop: 'activityUnionName', label: '活动工会', sortable: true}, + { prop: "loginName", label: "工号" }, + { prop: "userName", label: "姓名" }, + { prop: "sex", label: "性别" }, + { prop: "birthday", label: "出生年月", sortable: true }, + { prop: "age", label: "年龄", sortable: true }, + { prop: "mobile", label: "联系电话" }, + { prop: "personType", label: "教职工类别", sortable: true }, + { prop: "userState", label: "在职状态", sortable: true }, + { prop: "unitName", label: "所属单位", sortable: true }, + { prop: "unionName", label: "所属工会", sortable: true } ], rules: { - setGroupType: [{required: true, message: "请选择添加方式", trigger: ["blur", "change"]}], - groupType: [{required: true, message: "请选择保存方式", trigger: ["blur", "change"]}], - setGroupId: [{required: true, message: "请选择分组", trigger: ["blur", "change"]}], - setGroupName: [{required: true, message: "请输入分组名称", trigger: ["blur", "change"]}] + setGroupType: [{ required: true, message: "请选择添加方式", trigger: ["blur", "change"] }], + groupType: [{ required: true, message: "请选择保存方式", trigger: ["blur", "change"] }], + setGroupId: [{ required: true, message: "请选择分组", trigger: ["blur", "change"] }], + setGroupName: [{ required: true, message: "请输入分组名称", trigger: ["blur", "change"] }] }, - relatedSessionMenus: ["aca4d14498c145ceb5b24ed70776aae2", "733d7266652740a3aeac9da97ab5eeca", "fe2d0768e26d4a80beeb159306bb8d01"], roleData: {}, importDialogVisible: false, - sessionOptions: [], + sessionOptions: [] } }, computed: { @@ -494,9 +485,6 @@ module.exports = { is_H02() { return this.roleData.is_H02 }, - is_CLUB_PRESIDENT() { - return this.roleData.is_CLUB_PRESIDENT - }, is_sysadmin() { return this.roleData.is_sysadmin }, @@ -520,6 +508,26 @@ module.exports = { groupType: 1 } }, + getInitialUnionIds() { + if ((this.is_sysadmin || this.is_A06 || this.is_H02) === false && this.is_H04 === true && this.unionid) { + return [this.unionid] + } + return [] + }, + normalizePageForm(pageForm) { + const form = clone(pageForm) + form.personTypes = JSON.stringify(form.personTypes) + form.userStates = JSON.stringify(form.userStates) + form.memberStatus = JSON.stringify(form.memberStatus) + form.memberTypes = JSON.stringify(form.memberTypes) + form.sexTypes = JSON.stringify(form.sexTypes) + form.roleIds = JSON.stringify(form.roleIds) + form.userId = JSON.stringify(form.userId) + form.age = JSON.stringify(form.age) + form.unionIds = JSON.stringify(form.unionIds) + form.activityUserCnd = form.activityUserCnd ? JSON.stringify(form.activityUserCnd) : form.activityUserCnd + return form + }, openSetDialog() { this.formData = this.getDefaultFormData() this.setDialogVisible = true @@ -544,6 +552,10 @@ module.exports = { this.formData.setGroupId = null } }, + handleActivityGroupChange(val) { + this.pageForm.memberTypes = val ? [] : ["工会会员"] + this.doSearch() + }, formatGroupDisplayName(group) { if (Number(group.groupType) === 2) { return group.groupName + "(SQL条件)" @@ -557,41 +569,31 @@ module.exports = { } }) }, - flush(exists_login_name_redis_key) { - this.pageForm.existsLoginNameRedisKey = exists_login_name_redis_key + flush(existsLoginNameRedisKey) { + this.pageForm.existsLoginNameRedisKey = existsLoginNameRedisKey this.doSearch() }, clearImportDialog() { this.importDialogVisible = false }, doExportUser() { - let props = {} - this.tableColumns.forEach((v) => { - props[v.prop] = v.label + const props = {} + this.tableColumns.forEach((item) => { + props[item.prop] = item.label }) - const pageForm = clone(this.pageForm) - pageForm.personTypes = JSON.stringify(pageForm.personTypes) - pageForm.userStates = JSON.stringify(pageForm.userStates) - pageForm.memberStatus = JSON.stringify(pageForm.memberStatus) - pageForm.memberTypes = JSON.stringify(pageForm.memberTypes) - pageForm.sexTypes = JSON.stringify(pageForm.sexTypes) - pageForm.roleIds = JSON.stringify(pageForm.roleIds) - pageForm.userId = JSON.stringify(pageForm.userId) - pageForm.age = JSON.stringify(pageForm.age) + const pageForm = this.normalizePageForm(this.pageForm) pageForm.props = JSON.stringify(props) - pageForm.activityUserCnd = pageForm.activityUserCnd ? JSON.stringify(pageForm.activityUserCnd) : pageForm.activityUserCnd this.$downLoad("/platform/activity/basic/scope/doExportUser", { data: JSON.stringify(pageForm) }) }, doReset() { this.pageForm.userId = [] - this.pageForm.memberTypes = [] + this.pageForm.memberTypes = ["工会会员"] this.pageForm.sexTypes = [] this.pageForm.personTypes = [] this.pageForm.userStates = [] - this.pageForm.unionId = "" - this.pageForm.unitId = "" + this.pageForm.unionIds = this.getInitialUnionIds() this.pageForm.module = "" this.pageForm.teacherMeetingId = "" this.pageForm.roleIds = [] @@ -603,46 +605,52 @@ module.exports = { this.doSearch() }, async queryUser(val) { - const resp = await $.get("/open/common/userOptions", {query: val}) + const resp = await $.get("/open/common/userOptions", { query: val }) this.userList = resp.data }, - tagClick(key, val) { - let idx = this.pageForm[key].indexOf(val) - if (idx !== -1) { - this.pageForm[key].splice(idx, 1) + tagClick(key, value) { + const index = this.pageForm[key].indexOf(value) + if (index !== -1) { + this.pageForm[key].splice(index, 1) } else { - this.pageForm[key].push(val) + this.pageForm[key].push(value) } this.doSearch() }, - async flushUnits() { - this.$set(this.pageForm, "unitId", "") - if (this.is_sysadmin || this.is_A06) { - this.units = await this.$businessTool.listUnit(this.pageForm.unionId) - // this.activityUnits = await getActivityUnits(this.pageForm.activityUnionId) - } else { - this.units = await this.$businessTool.listUnit(this.unionId) - } + doSearch(showFullLoading = false) { + this.tableKey = new Date().getTime() + this.pageForm.pageNumber = 1 + this.pageData(showFullLoading === true) }, - async pageData() { - const pageForm = clone(this.pageForm) - pageForm.personTypes = JSON.stringify(pageForm.personTypes) - pageForm.userStates = JSON.stringify(pageForm.userStates) - pageForm.memberStatus = JSON.stringify(pageForm.memberStatus) - pageForm.memberTypes = JSON.stringify(pageForm.memberTypes) - pageForm.sexTypes = JSON.stringify(pageForm.sexTypes) - pageForm.roleIds = JSON.stringify(pageForm.roleIds) - pageForm.userId = JSON.stringify(pageForm.userId) - pageForm.age = JSON.stringify(pageForm.age) - pageForm.activityUserCnd = pageForm.activityUserCnd ? JSON.stringify(pageForm.activityUserCnd) : pageForm.activityUserCnd + async pageData(showFullLoading = false) { + this.tableLoading = true + const loading = showFullLoading && this.pageForm.activityGroupId + ? this.$loading({ + lock: true, + text: "正在查询,请稍候...", + spinner: "el-icon-loading", + background: "rgba(255, 255, 255, 0.65)" + }) + : null + try { + const resp = await $.post("/platform/activity/basic/scope/pageData", { + data: JSON.stringify(this.normalizePageForm(this.pageForm)) + }) - const resp = await $.post("/platform/activity/basic/scope/pageData", {data: JSON.stringify(pageForm)}) - - if (resp.code === 0) { - this.tableData = resp.data.list - this.pageForm.totalCount = resp.data.totalCount - } else { - this.notifyWarning(resp.msg) + if (resp.code === 0) { + this.tableData = (resp.data.list || []).map((item) => ({ + ...item, + birthday: item.birthday ? moment(item.birthday).format("YYYY-MM-DD") : item.birthday + })) + this.pageForm.totalCount = resp.data.totalCount + } else { + this.notifyWarning(resp.msg) + } + } finally { + this.tableLoading = false + if (loading) { + loading.close() + } } }, async doSetActivityUser() { @@ -655,29 +663,22 @@ module.exports = { this.$message.warning("请先指定筛选条件!") return } + const confirm = await this.$confirm("是否将符合搜索条件的用户设为活动人员?", "提示", { confirmButtonText: "确定", cancelButtonText: "取消", type: "warning" }) + if (confirm === "confirm") { this.settingLoading = true - const pageForm = clone(this.pageForm) - pageForm.personTypes = JSON.stringify(pageForm.personTypes) - pageForm.userStates = JSON.stringify(pageForm.userStates) - pageForm.memberStatus = JSON.stringify(pageForm.memberStatus) - pageForm.memberTypes = JSON.stringify(pageForm.memberTypes) - pageForm.sexTypes = JSON.stringify(pageForm.sexTypes) - pageForm.roleIds = JSON.stringify(pageForm.roleIds) - pageForm.userId = JSON.stringify(pageForm.userId) - pageForm.age = JSON.stringify(pageForm.age) - pageForm.activityUserCnd = pageForm.activityUserCnd ? JSON.stringify(pageForm.activityUserCnd) : pageForm.activityUserCnd - + const pageForm = this.normalizePageForm(this.pageForm) Object.assign(pageForm, this.formData) - const resp = await $.post("/platform/activity/basic/scope/doSetActivityUser", {data: JSON.stringify(pageForm)}) + const resp = await $.post("/platform/activity/basic/scope/doSetActivityUser", { + data: JSON.stringify(pageForm) + }) if (resp.code === 0) { this.setDialogVisible = false - // this.userScopeDialog = false await this.getActivityGroup() this.doSearch() this.$message.success(resp.msg) @@ -693,36 +694,31 @@ module.exports = { async getActivityGroup() { const resp = await $.get("/platform/activity/basic/scope/getActivityUserScopeGroup") this.activityGroupList = resp.data || [] - this.activityGroupList2 = [] - this.activityGroupList.forEach((v) => { - this.activityGroupList2.push({ - groupId: v.groupId, - groupName: this.formatGroupDisplayName(v) + "范围之外人员" - }) - }) + this.activityGroupList2 = this.activityGroupList.map((item) => ({ + groupId: item.groupId, + groupName: this.formatGroupDisplayName(item) + "范围之外人员" + })) }, async getRoleListByMenuId() { - const {data} = await $.post("/platform/activity/basic/scope/getRoleListByMenuId") + const { data } = await $.post("/platform/activity/basic/scope/getRoleListByMenuId") this.roleList = data }, async getRolesAndUnion() { - const {data} = await $.post("/platform/activity/basic/scope/getRolesAndUnion") + const { data } = await $.post("/platform/activity/basic/scope/getRolesAndUnion") this.roleData = data } }, async created() { this.listSession() await this.getRolesAndUnion() - this.clubOptions = await this.$businessTool.listManageClubByRole() - if ((this.is_sysadmin || this.is_A06 || this.is_H02) === false && this.is_H04 === true) { - this.unions = await this.$businessTool.listUnion(this.unionid) - if (this.unions.length > 0 && this.is_CLUB_PRESIDENT !== true) { - this.$set(this.pageForm, "unionId", this.unions[0].id) - } - } else { - this.unions = await this.$businessTool.listUnion() + this.clubOptions = await this.$businessTool.listClubByRole() + this.unions = (this.is_sysadmin || this.is_A06 || this.is_H02) + ? await this.$businessTool.listUnion() + : this.$businessTool.listUnion(this.unionid) + this.pageForm.unionIds = this.getInitialUnionIds() + if (!this.is_H04 && this.is_H02 && this.clubOptions.length > 0) { + this.$set(this.pageForm, "clubId", this.clubOptions[0].id) } - await this.flushUnits() await this.getActivityGroup() this.personTypeOptions = await this.$businessTool.getDictOptions("USER_PERSON_TYPE") this.userStateOptions = await this.$businessTool.getDictOptions("USER_STATE") @@ -753,6 +749,7 @@ module.exports = { .query-row > .query-content { min-width: 200px; overflow: hidden; + flex: 1; } .query-row > .query-content > .el-tag { @@ -760,11 +757,11 @@ module.exports = { margin-top: 5px; } -@media screen and (max-width: 992px) { - .query-row:nth-child(4) .query-content .el-col:not(:last-child) { - margin-bottom: 5px; - } +.query-actions { + justify-content: end; +} +@media screen and (max-width: 992px) { .query-title { display: none; } @@ -804,4 +801,24 @@ module.exports = { line-height: 20px; margin-top: 8px; } + +.existing-group-radio-list { + display: grid; + grid-template-columns: repeat(auto-fill, 190px); + column-gap: 24px; + row-gap: 5px; + align-items: start; +} + +.existing-group-radio-list /deep/ .existing-group-radio, +.existing-group-radio-list /deep/ .el-radio.is-bordered, +.existing-group-radio-list /deep/ .el-radio.is-bordered + .el-radio.is-bordered { + margin: 0 !important; + width: 190px; +} + +.existing-group-radio-list /deep/ .el-radio.is-bordered { + display: flex; + align-items: center; +} diff --git a/src/main/resources/views/platform/flow/design/index.html b/src/main/resources/views/platform/flow/design/index.html index d46055a2..44ce7cfb 100644 --- a/src/main/resources/views/platform/flow/design/index.html +++ b/src/main/resources/views/platform/flow/design/index.html @@ -43,7 +43,7 @@ layout("/layouts/platform.html"){ - + @@ -54,6 +54,19 @@ layout("/layouts/platform.html"){ + + +