Merge branch 'main' into feature_活动电子档案

# Conflicts:
#	src/main/resources/views/platform/zhgh/archive/docType/index.html
#	src/main/resources/views/platform/zhgh/archive/filing/index.html
#	src/main/resources/views/platform/zhgh/archive/project/index.html
This commit is contained in:
2026-08-29 09:29:50 +08:00
655 changed files with 42947 additions and 11085 deletions
@@ -12,6 +12,8 @@ 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.dao.util.cri.Static;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.json.Json;
@@ -51,7 +53,8 @@ public class FlowTodoCenterController {
t.h5FormKey,
t.createdAt,
t.finishTime,
t.variable,
t.variable AS taskVariable,
ins.variable,
ins.state AS instanceState,
ins.id AS instanceId,
ins.businessNo,
@@ -66,7 +69,14 @@ public class FlowTodoCenterController {
$condition
""");
Cnd cnd = Cnd.NEW();
cnd.and("t.taskState", "=", ProcessTaskStateEnum.DOING.getCode());
// 普通待办仍按进行中任务查询;提案进入团长审核后,补充展示尚未附议的已废弃 second 任务。
SqlExpressionGroup todoTaskGroup = Cnd.exps("t.taskState", "=", ProcessTaskStateEnum.DOING.getCode());
SqlExpressionGroup proposalSecondGroup = Cnd.exps("t.taskState", "=", ProcessTaskStateEnum.ABANDON.getCode())
.and("t.taskName", "=", "second")
.and(new Static("EXISTS (SELECT 1 FROM proposal_second ps WHERE ps.proposalId = ins.businessNo AND ps.seconderId = ta.actorId AND ps.isAgree IS NULL)"))
.and(new Static("EXISTS (SELECT 1 FROM wf_process_task dt WHERE dt.processInstanceId = t.processInstanceId AND dt.taskName = 'delegation' AND dt.taskState = 10)"));
todoTaskGroup.or(proposalSecondGroup);
cnd.and(todoTaskGroup);
cnd.and("ta.actorId", "=", SecurityUtil.getUserId());
cnd.andEX("def.category", "=", category);
if (StrUtil.isNotBlank(searchKeyword)) {
@@ -96,7 +106,8 @@ public class FlowTodoCenterController {
t.h5FormKey,
t.createdAt,
t.finishTime,
t.variable,
t.variable AS taskVariable,
ins.variable,
ins.state AS instanceState,
ins.id AS instanceId,
ins.businessNo,
@@ -223,7 +234,27 @@ public class FlowTodoCenterController {
LEFT JOIN wf_process_task_actor ta ON ta.processTaskId = t.id
WHERE
ta.actorId = @userId
AND t.taskState = 10
AND (
t.taskState = 10
OR (
t.taskState = 99
AND t.taskName = 'second'
AND EXISTS (
SELECT 1
FROM proposal_second ps
WHERE ps.proposalId = (SELECT businessNo FROM wf_process_instance WHERE id = t.processInstanceId)
AND ps.seconderId = ta.actorId
AND ps.isAgree IS NULL
)
AND EXISTS (
SELECT 1
FROM wf_process_task dt
WHERE dt.processInstanceId = t.processInstanceId
AND dt.taskName = 'delegation'
AND dt.taskState = 10
)
)
)
""");
todoSql.setParam("userId", SecurityUtil.getUserId());
todoSql.setCallback(Sqls.callback.integer());
@@ -6,7 +6,9 @@ import lombok.Setter;
import org.nutz.dao.entity.annotation.Column;
import org.nutz.dao.entity.annotation.Comment;
import org.nutz.dao.entity.annotation.Id;
import org.nutz.dao.entity.annotation.Index;
import org.nutz.dao.entity.annotation.Table;
import org.nutz.dao.entity.annotation.TableIndexes;
import java.io.Serializable;
import java.util.Date;
@@ -22,6 +24,9 @@ import java.util.Date;
@Getter
@Setter
@Table("wf_process_instance")
@TableIndexes({
@Index(name = "INDEX_WF_PROCESS_INSTANCE_BUSINESS_NO", fields = {"businessNo"}, unique = false)
})
@Comment("流程实例")
public class ProcessInstance extends BaseModel {
@@ -6,7 +6,9 @@ import lombok.Setter;
import org.nutz.dao.entity.annotation.Column;
import org.nutz.dao.entity.annotation.Comment;
import org.nutz.dao.entity.annotation.Id;
import org.nutz.dao.entity.annotation.Index;
import org.nutz.dao.entity.annotation.Table;
import org.nutz.dao.entity.annotation.TableIndexes;
import java.io.Serializable;
import java.util.Date;
@@ -19,6 +21,9 @@ import java.util.Date;
@Getter
@Setter
@Table("wf_process_task")
@TableIndexes({
@Index(name = "INDEX_WF_PROCESS_TASK_INSTANCE_STATE", fields = {"processInstanceId", "taskState"}, unique = false)
})
@Comment("流程任务")
public class ProcessTask extends BaseModel {
@@ -11,6 +11,8 @@ import com.budwk.app.flow.engine.event.ProcessPublisher;
import com.budwk.app.flow.entity.ProcessInstance;
import com.budwk.app.flow.entity.ProcessTask;
import com.budwk.app.flow.enums.*;
import com.budwk.app.sys.models.Sys_unit;
import com.budwk.app.sys.views.View_user;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Chain;
@@ -22,6 +24,7 @@ import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.json.Json;
import java.util.List;
import java.util.Optional;
@IocBean
public class FlowCommonService {
@@ -61,11 +64,13 @@ public class FlowCommonService {
args.put(FlowConst.SUBMIT_TYPE, submitType);
Sys_unit sysUnit = dao.fetch(Sys_unit.class, Cnd.where("id", "=", SecurityUtil.getUnitId()));
// 设置办理人信息到表单参数
args.put(FlowConst.TASK_FORM_DATA_PREFIX + "userName", SecurityUtil.getUserUsername());
args.put(FlowConst.TASK_FORM_DATA_PREFIX + "loginName", SecurityUtil.getUserLoginname());
args.put(FlowConst.TASK_FORM_DATA_PREFIX + "unitId", SecurityUtil.getUnitId());
args.put(FlowConst.TASK_FORM_DATA_PREFIX + "unitName", SecurityUtil.getUnitId());
args.put(FlowConst.TASK_FORM_DATA_PREFIX + "unitName", Optional.of(sysUnit).map(Sys_unit::getName).orElse(""));
args.put(FlowConst.TASK_FORM_DATA_PREFIX + "unionId", SecurityUtil.getUnionId());
if (ObjectUtil.equals(submitType, ProcessSubmitTypeEnum.ROLLBACK.getCode())) {
@@ -4,7 +4,6 @@ import cn.dev33.satoken.annotation.SaCheckPermission;
import com.budwk.app.base.annotation.SLog;
import com.budwk.app.base.constant.RedisConstant;
import com.budwk.app.base.result.Result;
import com.budwk.app.base.utils.PageUtil;
import com.budwk.app.sys.models.Sys_config;
import com.budwk.app.sys.services.SysConfigService;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
@@ -139,17 +138,18 @@ public class SysConfController {
@At
@Ok("json:full")
@SaCheckPermission("sys.manager.conf")
public Object data(@Param("pageNumber") int pageNumber, @Param("pageSize") int pageSize, @Param("pageOrderName") String pageOrderName, @Param("pageOrderBy") String pageOrderBy) {
public Object data(@Param("pageNumber") int pageNumber,
@Param("pageSize") int pageSize,
@Param("pageOrderName") String pageOrderName,
@Param("pageOrderBy") String pageOrderBy,
@Param("configKey") String configKey) {
try {
ensureAppImageConfig("AppHomeImg", "PC首页轮播图");
ensureAppImageConfig("H5AppHomeImg", "移动端首页轮播图");
ensureAppImageConfig("AppFeaturedActivityImg", "精彩活动页顶部图片");
ensureAppImageConfig("AppFestivalBenefitImg", "节日福利页顶部图片");
Cnd cnd = Cnd.NEW();
if (Strings.isNotBlank(pageOrderName) && Strings.isNotBlank(pageOrderBy)) {
cnd.orderBy(pageOrderName, PageUtil.getOrder(pageOrderBy));
}
return Result.success().addData(sysConfigService.listPage(pageNumber, pageSize, cnd));
return Result.success().addData(sysConfigService.pageData(
pageNumber, pageSize, pageOrderName, pageOrderBy, configKey));
} catch (Exception e) {
return Result.error();
}
@@ -154,10 +154,15 @@ public class SysHomeController {
.and(Sys_menu::getPlatform, "=", platform)
);
List<Sys_menu> menus = sysUserService.getMenus(SecurityUtil.getUserId());
List<String> allMenuIds = menus.stream().map(Sys_menu::getId).toList();
Set<String> allMenuIds = menus.stream().map(Sys_menu::getId).collect(Collectors.toSet());
Set<String> allParentMenuIds = menus.stream()
.map(Sys_menu::getParentId)
.filter(StrUtil::isNotBlank)
.collect(Collectors.toSet());
List<Sys_menu> sysMenus = list.stream()
// 推荐项必须同时属于当前用户已授权菜单,避免首页展示无权限入口。
.filter(menu -> allMenuIds.contains(menu.getId()))
// 应用有已授权菜单,或根菜单自身有链接且已授权时,才允许在首页展示入口。
.filter(menu -> allParentMenuIds.contains(menu.getId())
|| (StrUtil.isNotBlank(menu.getHref()) && allMenuIds.contains(menu.getId())))
.sorted(Comparator.comparing(Sys_menu::getLocation, Comparator.nullsLast(Integer::compareTo))
.thenComparing(Sys_menu::getId))
.toList();
@@ -175,7 +180,7 @@ public class SysHomeController {
.and(Sys_menu::getPlatform, "=", platform)
);
List<Sys_menu> menus = sysUserService.getMenus(SecurityUtil.getUserId());
List<String> allMenuIds = menus.stream().map(Sys_menu::getId).toList();
Set<String> allMenuIds = menus.stream().map(Sys_menu::getId).collect(Collectors.toSet());
List<Sys_menu> sysMenus = list.stream()
// 推荐项必须同时属于当前用户已授权菜单,避免首页展示无权限入口。
.filter(menu -> allMenuIds.contains(menu.getId()))
@@ -8,6 +8,7 @@ import com.budwk.app.base.annotation.SLog;
import com.budwk.app.base.result.Result;
import com.budwk.app.base.utils.PageUtil;
import com.budwk.app.sys.models.Sys_menu;
import com.budwk.app.sys.models.Sys_module;
import com.budwk.app.sys.models.Sys_role;
import com.budwk.app.sys.models.Sys_unit;
import com.budwk.app.sys.models.Sys_user;
@@ -23,6 +24,8 @@ import org.nutz.dao.FieldFilter;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.dao.util.Daos;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Lang;
@@ -120,6 +123,7 @@ public class SysRoleController {
@Ok("json:full")
@SaCheckPermission("sys.manager.role")
public Object pageData(@Param("searchUnit") String searchUnit,
@Param("moduleId") String moduleId,
@Param("searchName") String searchName,
@Param("searchKeyword") String searchKeyword,
@Param("searchName2") String searchName2,
@@ -130,9 +134,10 @@ public class SysRoleController {
try {
Sql sql = Sqls.create("""
SELECT
role.*
role.*, module.name AS moduleName
FROM
sys_role role
LEFT JOIN sys_module module ON module.id = role.moduleId
LEFT JOIN sys_user_role ur ON role.id = ur.roleId
LEFT JOIN sys_user u ON u.id = ur.userId
$condition
@@ -163,6 +168,9 @@ public class SysRoleController {
if (StrUtil.isAllNotBlank(searchName2, searchKeyword2)) {
cnd.and("u." + searchName2, "like", "%" + searchKeyword2 + "%");
}
if (StrUtil.isNotBlank(moduleId)) {
cnd.and("role.moduleId", "=", moduleId);
}
if (Strings.isNotBlank(pageOrderName) && Strings.isNotBlank(pageOrderBy)) {
cnd.orderBy(pageOrderName, PageUtil.getOrder(pageOrderBy));
} else {
@@ -287,8 +295,13 @@ public class SysRoleController {
@Ok("json")
@SaCheckPermission("sys.manager.role.add")
@SLog(tag = "添加角色", msg = "角色名称:${args[1].name}")
@Aop(TransAop.READ_COMMITTED)
public Object addDo(@Param("..") Sys_role role, HttpServletRequest req) {
try {
// 所属模块只允许选择已配置的 PC 端模块,避免角色关联无效或 H5 模块。
if (!isPcModule(role.getModuleId())) {
return Result.error("请选择所属模块");
}
int num = sysRoleService.count(Cnd.where("code", "=", role.getCode().trim()));
if (num > 0) {
return Result.error("角色编码已存在");
@@ -360,8 +373,13 @@ public class SysRoleController {
@Ok("json")
@SaCheckPermission("sys.manager.role.edit")
@SLog(tag = "修改角色", msg = "角色名称:${args[0].name}")
@Aop(TransAop.READ_COMMITTED)
public Object editDo(@Param("..") Sys_role role, HttpServletRequest req) {
try {
// 编辑时同样校验模块,保证历史角色补录后始终关联有效 PC 模块。
if (!isPcModule(role.getModuleId())) {
return Result.error("请选择所属模块");
}
Sys_role oldRole = sysRoleService.fetch(role.getId());
if (oldRole != null && !Strings.sBlank(oldRole.getCode()).equalsIgnoreCase(role.getCode())) {
int num = sysRoleService.count(Cnd.where("code", "=", role.getCode().trim()));
@@ -401,14 +419,13 @@ public class SysRoleController {
public Object user(@Param("roleId") String roleId, @Param("searchName") String searchName, @Param("searchKeyword") String searchKeyword,
@Param("pageNumber") int pageNumber, @Param("pageSize") int pageSize, @Param("pageOrderName") String pageOrderName, @Param("pageOrderBy") String pageOrderBy) {
try {
Sql sql = Sqls.create("SELECT a.*,c.name as unitname FROM sys_user a,sys_user_role b,sys_unit c WHERE a.unitid=c.id and a.id=b.userId and b.roleId=@roleId $s $o");
Sql sql = Sqls.create("SELECT a.*,c.name as unitname FROM sys_user a,sys_user_role b,sys_unit c WHERE a.unitid=c.id and a.id=b.userId and enable='1' and b.roleId=@roleId $s $o");
sql.params().set("roleId", roleId);
if (Strings.isNotBlank(searchName) && Strings.isNotBlank(searchKeyword)) {
sql.vars().set("s", " and a." + searchName + " like '%" + searchKeyword + "%'");
}
if (Strings.isNotBlank(pageOrderName) && Strings.isNotBlank(pageOrderBy)) {
sql.vars().set("o", " order by a." + pageOrderName + " " + PageUtil.getOrder(pageOrderBy));
}
return Result.success().addData(sysUserService.listPage(pageNumber, pageSize, sql));
} catch (Exception e) {
@@ -503,24 +520,42 @@ public class SysRoleController {
cnd.and(Cnd.exps("parentId", "is", null).or("parentId", "=", ""));
return Result.success(Daos.ext(sysRoleService.dao(), fieldFilter).query(Sys_menu.class, cnd));
}
/**
* 查询角色可选择的 PC 端模块。
*
* @return PC 端模块列表,按模块排序编号升序返回,元素包含模块 ID 和模块名称
*/
@At
@Ok("json:full")
@SaCheckPermission("sys.manager.role")
public Result listModule() {
List<Sys_module> modules = sysRoleService.dao().query(Sys_module.class,
Cnd.where(Sys_module::getPlatform, "=", "PC").asc(Sys_module::getSortNum));
return Result.success(modules);
}
/**
* 校验所属模块是否存在且属于 PC 平台。
*
* @param moduleId 模块主键,必须来自 sys_module 的 PC 端模块记录
* @return 模块有效时返回 true,否则返回 false
*/
private boolean isPcModule(String moduleId) {
if (StrUtil.isBlank(moduleId)) {
return false;
}
Sys_module module = sysRoleService.dao().fetch(Sys_module.class, moduleId);
return module != null && "PC".equals(module.getPlatform());
}
@At
@Ok("json:full")
@SaCheckLogin
public Result getRoleNames(){
Sql sql = Sqls.create("""
SELECT
GROUP_CONCAT(DISTINCT sr.`name`) roleNames
FROM
`sys_user_role` sur
LEFT JOIN sys_role sr ON sr.id = sur.roleId
WHERE
sur.userId = @userId
ORDER BY
sr.sort DESC
""").setParam("userId", SecurityUtil.getUserId());
sql.setCallback(Sqls.callback.map());
sysRoleService.dao().execute(sql);
return Result.success(sql.getResult());
// 首页身份与菜单、接口权限使用同一启用角色集合。
String roleNames = sysUserService.getEnabledRoleNames(SecurityUtil.getUserId());
return Result.success(NutMap.NEW().addv("roleNames", roleNames));
}
@@ -131,7 +131,8 @@ public class SysSignatureController {
@Aop(TransAop.READ_COMMITTED)
public Result update(@Param("file") TempFile tempFile) {
String url = sysFileService.uploadReturnUrl(SysFileEngineTypeEnum.MINIO.getValue(), tempFile);
Sys_user_signature sysUserSignature = dao.fetch(Sys_user_signature.class);
// 签字记录必须按当前登录用户读取,避免后续用户更新时覆盖其他用户的签字。
Sys_user_signature sysUserSignature = dao.fetch(Sys_user_signature.class, Cnd.where(Sys_user_signature::getUserId, "=", SecurityUtil.getUserId()));
if (ObjectUtil.isNull(sysUserSignature)) {
sysUserSignature = new Sys_user_signature();
}
@@ -7,6 +7,8 @@ import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNode;
import cn.hutool.core.lang.tree.TreeUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.base.exception.BaseException;
import com.budwk.app.base.page.Pagination;
@@ -263,7 +265,7 @@ public class SysUnionController {
if (Lang.isEmpty(branchUnionRoles)) {
return Result.success();
}
List<String> branchUnionRoleCodes = branchUnionRoles.stream().map(Sys_dict::getCode).toList();
List<String> branchUnionRoleCodes = getBranchUnionRoleCodes(branchUnionRoles);
Sql sql = Sqls.create("""
SELECT
@@ -338,7 +340,7 @@ public class SysUnionController {
if (Lang.isEmpty(branchUnionRoles)) {
return Result.success(List.of());
}
List<String> branchUnionRoleCodes = branchUnionRoles.stream().map(Sys_dict::getCode).toList();
List<String> branchUnionRoleCodes = getBranchUnionRoleCodes(branchUnionRoles);
Sql sql = Sqls.create("""
SELECT DISTINCT
@@ -371,6 +373,41 @@ public class SysUnionController {
return Result.success(usedJCodes);
}
/**
* 查询分工会干部角色选项,并补充按单位授权的二级党委书记角色。
*
* @return 角色编码和名称
*/
@At
@SaCheckPermission("sys.manager.union.branchOfficer")
public Result branchUnionRoleOptions() {
List<NutMap> roleOptions = sysDictService.getSubListByCode("BRANCH_UNION_ROLES").stream()
.map(item -> NutMap.NEW().addv("code", item.getCode()).addv("name", item.getName()))
.collect(Collectors.toCollection(ArrayList::new));
if (sysRoleService.getByCode(RoleConstant.UNIT_PARTY_SECRETARY) != null
&& roleOptions.stream().noneMatch(item -> RoleConstant.UNIT_PARTY_SECRETARY.name().equals(item.getString("code")))) {
roleOptions.add(NutMap.NEW().addv("code", RoleConstant.UNIT_PARTY_SECRETARY.name()).addv("name", "二级党委书记"));
}
return Result.success(roleOptions);
}
/**
* 查询当前分工会可配置二级党委书记的组成单位。
*
* @param unionId 分工会ID
* @return 当前分工会的二级单位
*/
@At
@SaCheckPermission("sys.manager.union.branchOfficer")
public Result branchUnionPartySecretaryUnitOptions(String unionId) {
if (StrUtil.isBlank(unionId)) {
return Result.error("分工会参数不能为空");
}
List<Sys_unit> units = dao.query(Sys_unit.class, Cnd.where("unionId", "=", unionId)
.and("unitLevel", "=", 2).asc("unitcode"));
return Result.success(units);
}
@At
@SaCheckPermission("sys.manager.union.branchOfficer")
@ApiOperation("添加分工会人员角色")
@@ -419,10 +456,16 @@ public class SysUnionController {
unionCadre.setIsJoin(true);
dao.insert(unionCadre);
// 去走工作流
// 校级管理员新增普通干部时直接授权,不生成无实际审核意义的流程数据。
boolean isAdmin = AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name());
if (isAdmin) {
sysUnionService.assignBranchUnionRole(userId, role.getId(), unionId, List.of());
return Result.success();
}
// 非校级管理员仍按原业务发起基层干部授权审核流程。
Dict args = Dict.create();
args.set("submit", isAdmin ? "admin" : "branch");
args.set("submit", "branch");
args.set(FlowConst.SUBMIT_TYPE, ProcessSubmitTypeEnum.APPLY.getCode());
args.set(FlowConst.FORM_DATA, unionCadre);
@@ -433,14 +476,83 @@ public class SysUnionController {
flowEngine.executeProcessTask(task.getId(), SecurityUtil.getUserId(), args);
}
if (isAdmin) {
// 如果是管理员,直接加角色
sysRoleService.clear("sys_user_role", Cnd.where("roleId", "=", role.getId()).and("userId", "=", userId).and("unionId", "=", unionId));
sysRoleService.insert("sys_user_role", Chain.make("roleId", role.getId()).add("userId", userId).add("unionId", unionId));
sysRoleService.clearCache();
sysUserService.clearCache();
return Result.success();
}
/**
* 新增二级党委书记授权。校级管理员直接按单位授权,其他用户发起审批,
* 审核通过后由流程拦截器按单位写入角色。
*
* @param userId 人员ID
* @param unionId 分工会ID
* @param unitIds 组成单位ID集合
* @param j 届次
* @return 处理结果
*/
@At
@SaCheckPermission("sys.manager.union.branchOfficer")
@ApiOperation("添加二级党委书记角色")
@Aop(TransAop.READ_COMMITTED)
public Result insertBranchUnionPartySecretaryRole(String userId, String unionId, @Param("unitIds") String[] unitIds, String j) {
if (StrUtil.isBlank(userId) || StrUtil.isBlank(unionId) || Lang.isEmpty(unitIds)) {
return Result.error("人员、分工会和所属单位不能为空");
}
Sys_role role = sysRoleService.getByCode(RoleConstant.UNIT_PARTY_SECRETARY);
if (role == null) {
return Result.error("未配置二级党委书记角色");
}
View_user user = dao.fetch(View_user.class, Cnd.where("id", "=", userId));
if (user == null || !unionId.equals(user.getUnionId())) {
return Result.error("所选人员不属于当前分工会");
}
List<String> distinctUnitIds = java.util.Arrays.stream(unitIds)
.filter(StrUtil::isNotBlank)
.distinct()
.toList();
List<Sys_unit> units = dao.query(Sys_unit.class, Cnd.where("id", "in", distinctUnitIds)
.and("unionId", "=", unionId).and("unitLevel", "=", 2));
if (units.size() != distinctUnitIds.size()) {
return Result.error("所属单位包含非当前分工会的单位");
}
int roleCount = dao.count(Sys_user_role.class, Cnd.where("roleId", "=", role.getId())
.and("userId", "=", userId).and("unionId", "=", unionId));
if (roleCount > 0) {
return Result.error("该人员已设置二级党委书记,请先删除后再重新设置");
}
Sys_union_cadre unionCadre = new Sys_union_cadre();
unionCadre.setUserId(userId);
unionCadre.setUnionId(unionId);
unionCadre.setMobile(user.getMobile());
unionCadre.setUnionName(user.getUnionName());
unionCadre.setLoginName(user.getLoginname());
unionCadre.setUserName(user.getUsername());
unionCadre.setRoleCode(RoleConstant.UNIT_PARTY_SECRETARY.name());
unionCadre.setJ(j);
unionCadre.setApplyDate(new Date());
unionCadre.setIsServing(true);
unionCadre.setIsJoin(true);
dao.insert(unionCadre);
boolean isAdmin = AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name());
if (isAdmin) {
// 校级管理员直接按所选单位授权,不创建 JCGHWY 流程实例和审核任务。
sysUnionService.assignBranchUnionRole(userId, role.getId(), unionId, distinctUnitIds);
return Result.success();
}
// 非校级管理员保留原审批流程,并将所选单位随表单传给审核通过拦截器。
JSONObject formData = JSONUtil.parseObj(unionCadre);
formData.set("unitIds", distinctUnitIds);
Dict args = Dict.create();
args.set("submit", "branch");
args.set(FlowConst.SUBMIT_TYPE, ProcessSubmitTypeEnum.APPLY.getCode());
args.set(FlowConst.FORM_DATA, formData.toString());
ProcessInstance instance = flowEngine.startProcessInstanceByKey("JCGHWY", unionCadre.getId(), SecurityUtil.getUserId(), args);
List<ProcessTask> doingTaskList = flowEngine.processTaskService().getDoingTaskList(instance.getId(), null);
for (ProcessTask task : doingTaskList) {
flowEngine.executeProcessTask(task.getId(), SecurityUtil.getUserId(), args);
}
return Result.success();
}
@@ -504,23 +616,34 @@ public class SysUnionController {
return Result.success();
}
/**
* 将字典维护的分工会角色与单位级二级党委书记角色合并为列表查询范围。
*/
private List<String> getBranchUnionRoleCodes(List<Sys_dict> branchUnionRoles) {
List<String> roleCodes = new ArrayList<>(branchUnionRoles.stream().map(Sys_dict::getCode).toList());
if (sysRoleService.getByCode(RoleConstant.UNIT_PARTY_SECRETARY) != null
&& !roleCodes.contains(RoleConstant.UNIT_PARTY_SECRETARY.name())) {
roleCodes.add(RoleConstant.UNIT_PARTY_SECRETARY.name());
}
return roleCodes;
}
/**
* 分页查询当前分工会的组成单位。
*
* @param pageForm 分页和查询参数;pageNumber 为页码,pageSize 为每页条数,searchKeyword 为单位编码或名称
* @param unionId 分工会ID,不能为空
* @return Resultdata 为 Paginationlist 是当前页单位列表,totalCount 是符合条件的单位总数
*/
@At
@ApiOperation("分工会组成单位分页")
@SaCheckPermission("sys.manager.union")
public Result branchUnionPartUnitPageData(PageForm pageForm, String unionId) {
Cnd cnd = Cnd.NEW();
cnd.and("unionId", "=", unionId);
if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) {
SqlExpressionGroup seg = new SqlExpressionGroup();
seg.orLike("`name`", pageForm.getSearchKeyword());
seg.orLike("unitcode", pageForm.getSearchKeyword());
cnd.and(seg);
public Result branchUnionPartUnitPageData(@Valid PageForm pageForm, String unionId) {
if (StrUtil.isBlank(unionId)) {
return Result.error("分工会参数不能为空");
}
cnd.desc("unitcode");
List<Sys_unit> list = dao.query(Sys_unit.class, cnd);
return Result.success(list);
return Result.success(sysUnitService.pageBranchUnionPartUnits(pageForm, unionId));
}
@At
@@ -1,6 +1,7 @@
package com.budwk.app.sys.controller.v4;
import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.result.Result;
@@ -50,6 +51,7 @@ public class SysV4MsgController {
m.title,
m.content,
m.type,
m.sendTime,
r.isRead,
r.readTime
FROM
@@ -69,6 +71,10 @@ public class SysV4MsgController {
cnd.andEX("m.type", "=", type);
if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) {
String keyword = "%" + pageForm.getSearchKeyword() + "%";
cnd.and(Cnd.exps("m.title", "like", keyword).or("m.content", "like", keyword));
}
cnd.desc("m.sendTime");
sql.setCondition(cnd);
@@ -1,6 +1,10 @@
package com.budwk.app.sys.interceptor;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.flow.constant.FlowConst;
import com.budwk.app.flow.engine.FlowInterceptor;
import com.budwk.app.flow.engine.core.Execution;
@@ -13,6 +17,9 @@ import com.budwk.app.sys.services.SysUserService;
import org.nutz.dao.Chain;
import org.nutz.dao.Cnd;
import java.util.ArrayList;
import java.util.List;
/**
* @version 1.0
* @Author FKY
@@ -35,14 +42,37 @@ public class SysUnionSchoolAuditInterceptor implements FlowInterceptor {
SysUserService sysUserService = ServiceContext.find(SysUserService.class);
// 表单数据
Sys_union_cadre unionBean = JSONUtil.toBean(formDataStr, Sys_union_cadre.class);
JSONObject formData = JSONUtil.parseObj(formDataStr);
Sys_union_cadre unionBean = JSONUtil.toBean(formData, Sys_union_cadre.class);
// 清除对应角色然后再新增
Sys_role role = sysRoleService.getByCode(unionBean.getRoleCode());
sysRoleService.clear("sys_user_role", Cnd.where("roleId", "=", role.getId())
.and("userId", "=", unionBean.getUserId()).and("unionId", "=", unionBean.getUnionId()));
sysRoleService.insert("sys_user_role", Chain.make("roleId", role.getId())
.add("userId", unionBean.getUserId()).add("unionId", unionBean.getUnionId()));
if (RoleConstant.UNIT_PARTY_SECRETARY.name().equals(unionBean.getRoleCode())) {
// 二级党委书记按申请中选定的单位分别授权,保障流程可按申请人所属单位找到办理人。
JSONArray unitIdArray = formData.getJSONArray("unitIds");
List<String> unitIds = new ArrayList<>();
if (unitIdArray != null) {
for (Object unitId : unitIdArray) {
String value = StrUtil.toString(unitId);
if (StrUtil.isNotBlank(value) && !unitIds.contains(value)) {
unitIds.add(value);
}
}
}
if (unitIds.isEmpty()) {
throw new IllegalArgumentException("二级党委书记未选择所属单位");
}
for (String unitId : unitIds) {
sysRoleService.insert("sys_user_role", Chain.make("roleId", role.getId())
.add("userId", unionBean.getUserId()).add("unionId", unionBean.getUnionId()).add("unitId", unitId));
}
} else {
// 其他分工会角色保持原有按分工会单条授权的逻辑。
sysRoleService.insert("sys_user_role", Chain.make("roleId", role.getId())
.add("userId", unionBean.getUserId()).add("unionId", unionBean.getUnionId()));
}
// 清除缓存
sysRoleService.clearCache();
@@ -60,7 +60,7 @@ public class Sys_log extends BaseModel implements Serializable {
@Column
@Comment("请求结果")
@ColDefine(type = ColType.TEXT)
@ColDefine(customType = "MEDIUMTEXT")
private String param;
@Column
@@ -45,6 +45,11 @@ public class Sys_role extends BaseModel implements Serializable {
@ColDefine(type = ColType.VARCHAR, width = 32)
private String unitid;
@Column
@ColDefine(type = ColType.VARCHAR, width = 32)
@Comment("所属模块ID")
private String moduleId;
@Column
@ColDefine(type = ColType.VARCHAR, width = 255)
private String note;
@@ -211,6 +211,12 @@ public class Sys_user extends BaseModel implements Serializable {
@DataCenterColumn(name = "教职工类别码", key = "RYLX", dict = "USER_PERSON_TYPE")
private String personType;
@Column
@Comment("待确认状态:0不用确认、1待确认、2已确认")
@ColDefine(type = ColType.INT, width = 1)
@Default("0")
private Integer pendingConfirmStatus;
@Column
@Comment("编制类别码")
@ColDefine(type = ColType.VARCHAR, width = 30)
@@ -16,6 +16,15 @@ public class Sys_user_role {
@ColDefine(type = ColType.VARCHAR, width = 32)
private String userId;
/**
* 用户角色关系是否启用。普通角色默认启用,教代会届次切换时按届次统一刷新。
*/
@Column
@Comment("是否启用:1启用,0禁用")
@ColDefine(type = ColType.BOOLEAN)
@Default("1")
private Boolean enable = true;
@Column
@ColDefine(type = ColType.VARCHAR, width = 32)
private String unitId;
@@ -1,5 +1,6 @@
package com.budwk.app.sys.services;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.sys.models.Sys_config;
import com.budwk.app.base.service.BaseService;
@@ -16,4 +17,17 @@ public interface SysConfigService extends BaseService<Sys_config> {
List<Sys_config> getAllList();
Sys_config getValueByKey(String key);
/**
* 分页查询系统参数,支持按参数名模糊查询和安全排序。
*
* @param pageNumber 页码
* @param pageSize 每页条数
* @param pageOrderName 排序字段
* @param pageOrderBy 排序方向
* @param configKey 参数名关键字
* @return 系统参数分页数据
*/
Pagination<Sys_config> pageData(int pageNumber, int pageSize, String pageOrderName,
String pageOrderBy, String configKey);
}
@@ -3,5 +3,18 @@ package com.budwk.app.sys.services;
import com.budwk.app.base.service.BaseService;
import com.budwk.app.sys.models.Sys_union;
import java.util.List;
public interface SysUnionService extends BaseService<Sys_union> {
/**
* 直接分配分工会干部角色。普通干部传空单位集合时按分工会写入一条角色关系;
* 二级党委书记传单位集合时按单位分别写入角色关系。
*
* @param userId 用户ID
* @param roleId 角色ID
* @param unionId 分工会ID
* @param unitIds 授权单位ID集合,普通干部角色传空集合
*/
void assignBranchUnionRole(String userId, String roleId, String unionId, List<String> unitIds);
}
@@ -1,5 +1,7 @@
package com.budwk.app.sys.services;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.service.BaseService;
import com.budwk.app.sys.models.Sys_unit;
@@ -7,6 +9,15 @@ import com.budwk.app.sys.models.Sys_unit;
* Created by wizzer on 2016/12/22.
*/
public interface SysUnitService extends BaseService<Sys_unit> {
/**
* 分页查询指定分工会的组成单位。
*
* @param pageForm 分页和查询参数;pageNumber 为页码,pageSize 为每页条数,searchKeyword 为单位编码或名称
* @param unionId 分工会ID,用于限制只查询当前分工会的组成单位
* @return 分页结果;list 为当前页单位数据,totalCount 为符合条件的单位总数
*/
Pagination<Sys_unit> pageBranchUnionPartUnits(PageForm pageForm, String unionId);
/**
* 保存单位
*
@@ -6,6 +6,7 @@ import com.budwk.app.base.exception.BaseException;
import com.budwk.app.base.exception.UnknownAccountException;
import com.budwk.app.base.service.BaseService;
import com.budwk.app.sys.models.Sys_menu;
import com.budwk.app.sys.models.Sys_role;
import com.budwk.app.sys.models.Sys_user;
import javax.servlet.http.HttpServletRequest;
@@ -31,6 +32,21 @@ public interface SysUserService extends BaseService<Sys_user> {
*/
List<String> getRoleCodeList(Sys_user user);
/**
* 查询用户当前启用的角色关系对应角色,供菜单、接口权限和首页身份统一使用。
*
* @param userId 用户ID
* @return 当前启用且角色本身未禁用的角色列表
*/
List<Sys_role> getEnabledRoles(String userId);
/**
* 查询用户当前启用角色的显示名称。
*
* @param userId 用户ID
* @return 去重并按角色顺序排列的角色名称
*/
String getEnabledRoleNames(String userId);
/**
* 获取用户的菜单
@@ -1,15 +1,19 @@
package com.budwk.app.sys.services.impl;
import com.budwk.app.base.exception.BaseException;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.base.utils.PageUtil;
import com.budwk.app.sys.models.Sys_config;
import com.budwk.app.sys.services.SysConfigService;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Lang;
import org.nutz.lang.Strings;
import java.util.List;
import java.util.Set;
/**
* Created by wizzer on 2016/12/23.
@@ -32,4 +36,32 @@ public class SysConfigServiceImpl extends BaseServiceImpl<Sys_config> implements
}
return sys_config;
}
/**
* 分页查询系统参数。参数名使用模糊匹配,排序字段限定在列表可展示字段内,
* 防止未经校验的前端字段直接进入排序 SQL。
*
* @param pageNumber 页码
* @param pageSize 每页条数
* @param pageOrderName 排序字段
* @param pageOrderBy 排序方向
* @param configKey 参数名关键字
* @return 系统参数分页数据
*/
@Override
@SuppressWarnings("unchecked")
public Pagination<Sys_config> pageData(int pageNumber, int pageSize, String pageOrderName,
String pageOrderBy, String configKey) {
Cnd cnd = Cnd.NEW();
if (Strings.isNotBlank(configKey)) {
cnd.where().andLike("configKey", configKey.trim());
}
Set<String> sortableColumns = Set.of("configKey", "configValue", "note");
if (sortableColumns.contains(pageOrderName) && Strings.isNotBlank(pageOrderBy)) {
cnd.orderBy(pageOrderName, PageUtil.getOrder(pageOrderBy));
} else {
cnd.asc("configKey");
}
return listPage(pageNumber, pageSize, cnd);
}
}
@@ -23,9 +23,12 @@ import com.budwk.app.zhgh.democratic.proposal.models.ProposalConfig;
import com.budwk.app.zhgh.staffmanage.member.constant.MemberChangeOrigin;
import com.budwk.app.zhgh.staffmanage.member.constant.MemberChangeType;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
@@ -50,6 +53,10 @@ import java.util.stream.Collectors;
@IocBean
public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService {
private static final int BATCH_SIZE = 500;
/** 不需要人工确认。 */
private static final int PENDING_CONFIRM_STATUS_NONE = 0;
/** 数据源缺失,等待人工确认。 */
private static final int PENDING_CONFIRM_STATUS_PENDING = 1;
@Inject
private Dao dao;
@@ -154,6 +161,10 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService
for (Sys_user_source source : sources) {
Sys_user user = userMap.get(source.getLoginname());
source.setMember(null);
String unitId = source.getUnitId();
if (StringUtils.isNotBlank(unitId) && unitId.length() == 5) {
source.setUnitId(unitId.substring(0, 3));
}
// 创建用户对象
Sys_user u = new Sys_user();
@@ -174,7 +185,7 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService
* 全量更新已有用户时置空,避免 updateIgnoreNull 把已迁移的基金会员标识覆盖为非会员。
*/
u.setAidFundMember(null);
u.setPersonType(null);
needDoUpdateList.add(u);
}
@@ -346,6 +357,12 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService
RoleEventPublisher.broadcast(new RoleEventMsg(unitId, RoleConstant.PROPOSAL_BRANCH_SCHOOL_LEADER.name(), RoleEventMsg.RENEW_ROLE));
}
/*
* 所有异步用户更新完成后再校正待确认状态,避免主事务提前锁定 sys_user,
* 导致异步更新线程等待主事务释放行锁而形成相互等待。
*/
reconcilePendingConfirmStatus(updateParam.getPullTime());
long endTime = System.currentTimeMillis();
log.info("全量更新用户数据完成,耗时: {} 毫秒", (endTime - startTime));
@@ -356,6 +373,49 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService
+ "";
}
/**
* 根据指定批次的完整数据源校正系统用户待确认状态。
* 已人工确认且仍未重新出现的人员保持已确认状态,避免每次全量更新重复转为待确认。
*
* @param pullTime 本次全量数据的拉取时间
*/
private void reconcilePendingConfirmStatus(String pullTime) {
Sql resetStatusSql = Sqls.create("""
UPDATE sys_user userInfo
SET userInfo.pendingConfirmStatus = @noneStatus
WHERE userInfo.personType IN ('教职工', '劳务派遣人员')
AND userInfo.pendingConfirmStatus <> @noneStatus
AND EXISTS (
SELECT 1
FROM sys_user_source sourceInfo
WHERE sourceInfo.pullTime = @pullTime
AND sourceInfo.loginname = userInfo.loginname
AND sourceInfo.personType IN ('教职工', '劳务派遣人员')
)
""");
resetStatusSql.setParam("noneStatus", PENDING_CONFIRM_STATUS_NONE);
resetStatusSql.setParam("pullTime", pullTime);
dao.execute(resetStatusSql);
Sql pendingStatusSql = Sqls.create("""
UPDATE sys_user userInfo
SET userInfo.pendingConfirmStatus = @pendingStatus
WHERE userInfo.personType IN ('教职工', '劳务派遣人员')
AND IFNULL(userInfo.pendingConfirmStatus, @noneStatus) = @noneStatus
AND NOT EXISTS (
SELECT 1
FROM sys_user_source sourceInfo
WHERE sourceInfo.pullTime = @pullTime
AND sourceInfo.loginname = userInfo.loginname
AND sourceInfo.personType IN ('教职工', '劳务派遣人员')
)
""");
pendingStatusSql.setParam("pendingStatus", PENDING_CONFIRM_STATUS_PENDING);
pendingStatusSql.setParam("noneStatus", PENDING_CONFIRM_STATUS_NONE);
pendingStatusSql.setParam("pullTime", pullTime);
dao.execute(pendingStatusSql);
}
/**
* 构建变更的历史数据
*
@@ -148,6 +148,11 @@ public class SysRoleServiceImpl extends BaseServiceImpl<Sys_role> implements Sys
@Override
@Aop(TransAop.READ_COMMITTED)
public void saveMenu(String[] menuIds, String roleId, String platform) {
// 保存前确认权限均属于当前平台,防止PC端与H5端权限混写。
List<Sys_menu> menus = sysMenuService.query(Cnd.where("id", "in", menuIds).and("platform", "=", platform));
if (menus.size() != menuIds.length) {
throw new BaseException("存在不属于当前平台的权限,无法保存");
}
//只清除对应平台的即可
Sql sql = Sqls.queryString("""
SELECT
@@ -2,13 +2,65 @@ package com.budwk.app.sys.services.impl;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.sys.models.Sys_union;
import com.budwk.app.sys.models.Sys_user_role;
import com.budwk.app.sys.services.SysRoleService;
import com.budwk.app.sys.services.SysUnionService;
import com.budwk.app.sys.services.SysUserService;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import java.util.List;
@IocBean(args = {"refer:dao"})
public class SysUnionServiceImpl extends BaseServiceImpl<Sys_union> implements SysUnionService {
@Inject
private SysRoleService sysRoleService;
@Inject
private SysUserService sysUserService;
public SysUnionServiceImpl(Dao dao) {
super(dao);
}
/**
* 直接分配分工会干部角色,并在授权完成后统一清理权限缓存。
* 普通角色按分工会授权一次,包含单位的角色按每个单位分别授权。
*
* @param userId 用户ID
* @param roleId 角色ID
* @param unionId 分工会ID
* @param unitIds 授权单位ID集合,普通干部角色传空集合
*/
@Override
public void assignBranchUnionRole(String userId, String roleId, String unionId, List<String> unitIds) {
// 先清理同一用户在当前分工会下的相同角色,保证重新授权后不存在重复关系。
dao().clear(Sys_user_role.class, Cnd.where(Sys_user_role::getRoleId, "=", roleId)
.and(Sys_user_role::getUserId, "=", userId)
.and(Sys_user_role::getUnionId, "=", unionId));
if (unitIds == null || unitIds.isEmpty()) {
Sys_user_role userRole = new Sys_user_role();
userRole.setRoleId(roleId);
userRole.setUserId(userId);
userRole.setUnionId(unionId);
dao().insert(userRole);
} else {
// 二级党委书记按选中的单位分别生成角色关系,供后续单位级权限判断使用。
for (String unitId : unitIds) {
Sys_user_role userRole = new Sys_user_role();
userRole.setRoleId(roleId);
userRole.setUserId(userId);
userRole.setUnionId(unionId);
userRole.setUnitId(unitId);
dao().insert(userRole);
}
}
sysRoleService.clearCache();
sysUserService.clearCache();
}
}
@@ -2,6 +2,8 @@ package com.budwk.app.sys.services.impl;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.constant.RedisConstant;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.sys.models.Sys_unit;
import com.budwk.app.sys.services.SysUnitService;
@@ -10,6 +12,7 @@ import org.nutz.dao.Chain;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
import org.nutz.dao.util.cri.SqlExpressionGroup;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Strings;
@@ -26,6 +29,22 @@ public class SysUnitServiceImpl extends BaseServiceImpl<Sys_unit> implements Sys
super(dao);
}
/**
* 按单位编码或名称筛选当前分工会的组成单位,并按单位编码倒序分页。
*/
@Override
public Pagination<Sys_unit> pageBranchUnionPartUnits(PageForm pageForm, String unionId) {
Cnd cnd = Cnd.where("unionId", "=", unionId);
if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) {
SqlExpressionGroup keywordCondition = new SqlExpressionGroup();
keywordCondition.orLike("`name`", pageForm.getSearchKeyword());
keywordCondition.orLike("unitcode", pageForm.getSearchKeyword());
cnd.and(keywordCondition);
}
cnd.desc("unitcode");
return listPage(pageForm.getPageNumber(), pageForm.getPageSize(), Sys_unit.class, cnd);
}
/**
* 新增单位
*
@@ -67,19 +67,10 @@ public class SysUserServiceImpl extends BaseServiceImpl<Sys_user> implements Sys
@Override
@CacheResult(cacheKey = "${userId}_getPermissionList")
public List<String> getPermissionList(String userId) {
Sys_user user = this.fetch(userId);
if (user == null) {
return new ArrayList<>();
}
this.fetchLinks(user, "roles");
if (user.getRoles() == null) {
return new ArrayList<>();
}
List<String> permissionList = new ArrayList<String>();
for (Sys_role role : user.getRoles()) {
if (!role.isDisabled()) {
permissionList.addAll(sysRoleService.getPermissionList(role));
}
// Sa-Token接口权限只汇总启用的用户角色关系,历史届次角色不再参与鉴权。
for (Sys_role role : getEnabledRoles(userId)) {
permissionList.addAll(sysRoleService.getPermissionList(role));
}
// 追加public公共角色权限
permissionList.addAll(sysRoleService.getPermissionList(sysRoleService.fetch(Cnd.where("code", "=", "public"))));
@@ -94,12 +85,63 @@ public class SysUserServiceImpl extends BaseServiceImpl<Sys_user> implements Sys
*/
@CacheResult(cacheKey = "${user.id}_getRoleCodeList")
public List<String> getRoleCodeList(Sys_user user) {
dao().fetchLinks(user, "roles");
List<String> roleNameList = new ArrayList<String>();
for (Sys_role role : user.getRoles()) {
if (!role.isDisabled()) roleNameList.add(role.getCode());
if (user == null) {
return new ArrayList<>();
}
return roleNameList;
// AuthUtil.hasRole与Sa-Token角色判断共用启用角色集合。
return getEnabledRoles(user.getId()).stream()
.map(Sys_role::getCode)
.filter(StrUtil::isNotBlank)
.distinct()
.toList();
}
/**
* 查询用户当前启用角色。用户角色关系和角色本身必须同时处于启用状态。
*
* @param userId 用户ID
* @return 当前启用角色列表
*/
@Override
@CacheResult(cacheKey = "${userId}_getEnabledRoles")
public List<Sys_role> getEnabledRoles(String userId) {
if (StrUtil.isBlank(userId) || fetch(userId) == null) {
return new ArrayList<>();
}
Sql sql = Sqls.create("""
SELECT DISTINCT
role.*
FROM
sys_role role
INNER JOIN sys_user_role userRole ON userRole.roleId = role.id
WHERE
userRole.userId = @userId
AND userRole.enable = @t
AND role.disabled = @f
ORDER BY role.sort DESC, role.id
""");
sql.setParam("userId", userId);
sql.setParam("t", true);
sql.setParam("f", false);
sql.setCallback(Sqls.callback.entities());
sql.setEntity(dao().getEntity(Sys_role.class));
dao().execute(sql);
return sql.getList(Sys_role.class);
}
/**
* 汇总用户当前启用角色名称,首页展示与菜单及接口权限保持相同数据口径。
*
* @param userId 用户ID
* @return 逗号分隔的角色名称
*/
@Override
public String getEnabledRoleNames(String userId) {
return getEnabledRoles(userId).stream()
.map(Sys_role::getName)
.filter(StrUtil::isNotBlank)
.distinct()
.collect(Collectors.joining(","));
}
/**
@@ -161,7 +203,8 @@ public class SysUserServiceImpl extends BaseServiceImpl<Sys_user> implements Sys
*/
// @CacheResult(cacheKey = "${userId}_getMenus")
public List<Sys_menu> getMenus(String userId) {
Sql sql = Sqls.create("select distinct a.* from sys_menu a,sys_role_menu b where a.id=b.menuId and " + " b.roleId in(select c.roleId from sys_user_role c,sys_role d where c.roleId=d.id and c.userId=@userId and d.disabled=@f) and a.disabled=@f and a.showit=@t and a.type='menu' order by a.location ASC,a.path asc");
// 菜单仅使用启用的用户角色关系,普通角色与当前开启届次角色保持原有权限。
Sql sql = Sqls.create("select distinct a.* from sys_menu a,sys_role_menu b where a.id=b.menuId and " + " b.roleId in(select c.roleId from sys_user_role c,sys_role d where c.roleId=d.id and c.userId=@userId and c.enable=@t and d.disabled=@f) and a.disabled=@f and a.showit=@t and a.type='menu' order by a.location ASC,a.path asc");
sql.params().set("userId", userId);
sql.params().set("f", false);
sql.params().set("t", true);
@@ -176,9 +219,10 @@ public class SysUserServiceImpl extends BaseServiceImpl<Sys_user> implements Sys
*/
// @CacheResult(cacheKey = "${userId}_getMenusAndButtons")
public List<Sys_menu> getMenusAndButtons(String userId) {
Sql sql = Sqls.create("select distinct a.* from sys_menu a,sys_role_menu b where a.id=b.menuId and " + " b.roleId in(select c.roleId from sys_user_role c,sys_role d where c.roleId=d.id and c.userId=@userId and d.disabled=@f) and a.disabled=@f order by a.location ASC,a.path asc");
Sql sql = Sqls.create("select distinct a.* from sys_menu a,sys_role_menu b where a.id=b.menuId and " + " b.roleId in(select c.roleId from sys_user_role c,sys_role d where c.roleId=d.id and c.userId=@userId and c.enable=@t and d.disabled=@f) and a.disabled=@f order by a.location ASC,a.path asc");
sql.params().set("userId", userId);
sql.params().set("f", false);
sql.params().set("t", true);
return sysMenuService.listEntity(sql);
}
@@ -195,9 +239,10 @@ public class SysUserServiceImpl extends BaseServiceImpl<Sys_user> implements Sys
*/
@CacheResult(cacheKey = "${userId}_getDatas")
public List<Sys_menu> getDatas(String userId) {
Sql sql = Sqls.create("select distinct a.* from sys_menu a,sys_role_menu b where a.id=b.menuId and " + " b.roleId in(select c.roleId from sys_user_role c,sys_role d where c.roleId=d.id and c.userId=@userId and d.disabled=@f) and a.disabled=@f and a.type='data' order by a.location ASC,a.path asc");
Sql sql = Sqls.create("select distinct a.* from sys_menu a,sys_role_menu b where a.id=b.menuId and " + " b.roleId in(select c.roleId from sys_user_role c,sys_role d where c.roleId=d.id and c.userId=@userId and c.enable=@t and d.disabled=@f) and a.disabled=@f and a.type='data' order by a.location ASC,a.path asc");
sql.params().set("userId", userId);
sql.params().set("f", false);
sql.params().set("t", true);
return sysMenuService.listEntity(sql);
}
@@ -232,9 +277,10 @@ public class SysUserServiceImpl extends BaseServiceImpl<Sys_user> implements Sys
*/
@CacheResult(cacheKey = "${userId}_${pid}_getRoleMenus")
public List<Sys_menu> getRoleMenus(String userId, String pid) {
Sql sql = Sqls.create("select distinct a.* from sys_menu a,sys_role_menu b where a.id=b.menuId and " + "$m and b.roleId in(select c.roleId from sys_user_role c,sys_role d where c.roleId=d.id and c.userId=@userId and d.disabled=@f) and a.disabled=@f order by a.location ASC,a.path asc");
Sql sql = Sqls.create("select distinct a.* from sys_menu a,sys_role_menu b where a.id=b.menuId and " + "$m and b.roleId in(select c.roleId from sys_user_role c,sys_role d where c.roleId=d.id and c.userId=@userId and c.enable=@t and d.disabled=@f) and a.disabled=@f order by a.location ASC,a.path asc");
sql.params().set("userId", userId);
sql.params().set("f", false);
sql.params().set("t", true);
if (Strings.isNotBlank(pid)) {
sql.vars().set("m", "a.parentId='" + pid + "'");
} else {
@@ -251,9 +297,10 @@ public class SysUserServiceImpl extends BaseServiceImpl<Sys_user> implements Sys
*/
@CacheResult(cacheKey = "${userId}_${pid}_hasChildren")
public boolean hasChildren(String userId, String pid) {
Sql sql = Sqls.create("select count(*) from sys_menu a,sys_role_menu b where a.id=b.menuId and " + "$m and b.roleId in(select c.roleId from sys_user_role c,sys_role d where c.roleId=d.id and c.userId=@userId and d.disabled=@f) and a.disabled=@f order by a.location ASC,a.path asc");
Sql sql = Sqls.create("select count(*) from sys_menu a,sys_role_menu b where a.id=b.menuId and " + "$m and b.roleId in(select c.roleId from sys_user_role c,sys_role d where c.roleId=d.id and c.userId=@userId and c.enable=@t and d.disabled=@f) and a.disabled=@f order by a.location ASC,a.path asc");
sql.params().set("userId", userId);
sql.params().set("f", false);
sql.params().set("t", true);
if (Strings.isNotBlank(pid)) {
sql.vars().set("m", "a.parentId='" + pid + "'");
} else {
@@ -387,6 +434,8 @@ public class SysUserServiceImpl extends BaseServiceImpl<Sys_user> implements Sys
user.setUnion(union);
}
user = this.fillMenu(user);
// 登录用户返回给前端的角色同样只保留启用关系,避免历史届次角色继续触发页面 hasRole 判断。
user.setRoles(this.getEnabledRoles(userId));
user.setPermissions(this.getPermissionList(userId));
return user;
}
@@ -133,10 +133,20 @@ public class ActivityBasicScopeUserDataController {
/**
* 删除一条记录
* 删除活动分组人员。
* id 有值时删除单个人员;id 为空时按当前页面的姓名/工号、工会、单位、人员类别和在职状态筛选后批量删除。
* groupId 为待删除人员所在的活动分组;existsLoginNameRedisKey 为导入核对名单生成的工号筛选缓存键。
* 返回 Result,code 为 0 表示删除处理成功。
*
* @param id
* @return
* @param id 单个人员 ID;批量删除时传空
* @param groupId 当前活动分组 ID
* @param searchKeyword 姓名或工号关键字
* @param unionId 所属工会 ID
* @param unitId 所属单位 ID
* @param personType 教职工类别
* @param userState 在职状态
* @param existsLoginNameRedisKey 导入核对名单的 Redis 缓存键
* @return 删除结果
*/
@At
@SLog(tag = "活动人员查询", msg = "删除活动人员")
@@ -145,7 +155,6 @@ public class ActivityBasicScopeUserDataController {
public Result doDelete(@Param(value = "id") String id,
@Param(value = "groupId") Integer groupId,
@Param(value = "searchKeyword") String searchKeyword,
@Param(value = "searchName") String searchName,
@Param(value = "unionId") String unionId,
@Param(value = "unitId") String unitId,
@Param(value = "personType") String personType,
@@ -160,7 +169,7 @@ public class ActivityBasicScopeUserDataController {
}
}
List<String> targetUserIds = queryGroupUserIds(groupId, id, searchKeyword, searchName, unionId, unitId, personType, userState, existsLoginNames);
List<String> targetUserIds = queryGroupUserIds(groupId, id, searchKeyword, unionId, unitId, personType, userState, existsLoginNames);
if (Lang.isEmpty(targetUserIds)) {
return Result.success();
}
@@ -282,9 +291,10 @@ public class ActivityBasicScopeUserDataController {
}
/**
* 删除时需要先按页面当前筛选条件圈定目标人员,再根据分组类型删除对应存储表中的快照数据。
* 删除时需要先按页面当前筛选条件圈定目标人员。姓名或工号关键字必须与列表查询使用相同的匹配规则,
* 再根据分组类型删除对应存储表中的快照数据。
*/
private List<String> queryGroupUserIds(Integer groupId, String id, String searchKeyword, String searchName,
private List<String> queryGroupUserIds(Integer groupId, String id, String searchKeyword,
String unionId, String unitId, String personType, String userState,
List<String> existsLoginNames) {
Sql sql = Sqls.queryString("""
@@ -296,8 +306,11 @@ public class ActivityBasicScopeUserDataController {
""");
Cnd cnd = Cnd.NEW();
cnd.and("u.id", "in", activityBasicScopeService.buildGroupUserIdSubSql(groupId));
if (StrUtil.isNotBlank(searchName) && StrUtil.isNotBlank(searchKeyword)) {
cnd.and(Cnd.likeEX(searchName, searchKeyword));
if (StrUtil.isNotBlank(searchKeyword)) {
SqlExpressionGroup group = new SqlExpressionGroup();
group.orLike("u.username", searchKeyword);
group.orLike("u.loginname", searchKeyword);
cnd.and(group);
}
cnd.andEX("u.unionid", "=", unionId);
cnd.andEX("u.unitid", "=", unitId);
@@ -196,6 +196,8 @@ public class ActivityCultureApplyActivityController {
}
private void cleanNewActivityData(ActivityTissue tissue) {
// 从模板另存或直接新建的活动必须进入活动列表,不能继承来源活动的模板状态。
tissue.setTemplateStatus(ActivityTissue.TEMPLATE_STATUS_UNSET);
tissue.setId(null);
tissue.setAuditId(null);
tissue.setState(null);
@@ -4,6 +4,7 @@ import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaMode;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.annotation.SLog;
import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.base.param.PageForm;
@@ -69,8 +70,9 @@ public class ActivityCultureInfoManageController {
public Result pageData(PageForm page, String year,
String name,
String unionId,
@Valid Integer activity_type) {
return Result.success(activityCultureService.pageData(page, year, name, unionId, activity_type));
@Valid Integer activity_type,
Integer templateStatus) {
return Result.success(activityCultureService.pageData(page, year, name, unionId, activity_type, templateStatus));
}
@At
@@ -90,47 +92,25 @@ public class ActivityCultureInfoManageController {
}
@At
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("activity.culture.infoManage.school")
@SaCheckPermission(value = {"activity.culture.infoManage.school", "activity.culture.infoManage.union"}, mode = SaMode.OR)
public Result setTemplate(@Valid String id) {
if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name())) {
return Result.error("无权操作");
}
ActivityTissue tissue = activityCultureService.fetch(id);
if (tissue == null) {
return Result.error("活动不存在");
String errorMessage = activityCultureService.setTemplate(id);
if (StrUtil.isNotBlank(errorMessage)) {
return Result.error(errorMessage);
}
if (tissue.getActivity_type() == null || tissue.getActivity_type() != 40001) {
return Result.error("仅校工会文化活动可设为模板");
}
Sys_home_template oldHomeTemplate = activityCultureService.dao().fetch(Sys_home_template.class, id);
Sys_home_template sysHomeTemplate = tissue.covertToSysHomeTemplate();
if (oldHomeTemplate != null) {
sysHomeTemplate.setTop(oldHomeTemplate.getTop());
sysHomeTemplate.setPush(oldHomeTemplate.getPush());
sysHomeTemplate.setSortNo(oldHomeTemplate.getSortNo());
if (oldHomeTemplate.getTemplateName() != null) {
sysHomeTemplate.setTemplateName(oldHomeTemplate.getTemplateName());
}
if (oldHomeTemplate.getTemplateIcon() != null) {
sysHomeTemplate.setTemplateIcon(oldHomeTemplate.getTemplateIcon());
}
if (oldHomeTemplate.getTemplateFile() != null) {
sysHomeTemplate.setTemplateFile(oldHomeTemplate.getTemplateFile());
}
}
activityCultureService.dao().insertOrUpdate(sysHomeTemplate);
return Result.success();
}
@At
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("activity.culture.infoManage.school")
@SaCheckPermission(value = {"activity.culture.infoManage.school", "activity.culture.infoManage.union"}, mode = SaMode.OR)
public Result cancelTemplate(@Valid String id) {
if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name())) {
return Result.error("无权操作");
}
activityCultureService.dao().delete(Sys_home_template.class, id);
activityCultureService.cancelTemplate(id);
return Result.success();
}
@@ -28,6 +28,12 @@ import java.util.List;
@Table("activity_tissue")
public class ActivityTissue extends BaseModel implements Serializable, SysHomeConvert {
/** 未设置为活动模板。 */
public static final int TEMPLATE_STATUS_UNSET = 0;
/** 已设置为活动模板。 */
public static final int TEMPLATE_STATUS_SET = 1;
@Column
@Name
@Comment("ID")
@@ -236,6 +242,12 @@ public class ActivityTissue extends BaseModel implements Serializable, SysHomeCo
@ColDefine(type = ColType.BOOLEAN)
private Boolean isUnseal;
@Column
@Comment("模板设置状态(0:未设置,1:已设置)")
@ColDefine(type = ColType.INT, width = 1)
@Default("0")
private Integer templateStatus;
@Column
@Comment("物品")
@ColDefine(type = ColType.MYSQL_JSON)
@@ -21,7 +21,33 @@ public interface ActivityCultureService extends BaseService<ActivityTissue> {
void doEditActivity(ActivityTissue tissue);
Object pageData(PageForm page, String year, String name, String unionId, Integer activity_type);
/**
* 分页查询文化活动。
*
* @param page 分页参数
* @param year 年度
* @param name 活动名称
* @param unionId 工会ID
* @param activity_type 活动类型
* @param templateStatus 模板设置状态,为空时不限制
* @return 文化活动分页数据
*/
Object pageData(PageForm page, String year, String name, String unionId, Integer activity_type, Integer templateStatus);
/**
* 将校工会或分工会文化活动设置为工作模板,并同步模板状态。
*
* @param id 活动ID
* @return 空字符串表示成功,否则返回业务错误信息
*/
String setTemplate(String id);
/**
* 取消校工会或分工会文化活动模板,并同步模板状态。
*
* @param id 活动ID
*/
void cancelTemplate(String id);
NutMap findOne(String id);
@@ -6,6 +6,7 @@ import com.budwk.app.base.model.Audit;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.sys.models.Sys_home_activity;
import com.budwk.app.sys.models.Sys_home_template;
import com.budwk.app.web.commons.auth.utils.AuthUtil;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.activity.basic.service.ActivityBasicScopeService;
@@ -14,6 +15,8 @@ import com.budwk.app.zhgh.activity.culture.models.ActivityTissuePerson;
import com.budwk.app.zhgh.activity.culture.service.ActivityCultureService;
import com.budwk.app.zhgh.club.model.SysClub;
import com.budwk.app.zhgh.club.service.SysClubService;
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;
@@ -21,6 +24,7 @@ import org.nutz.dao.sql.Sql;
import org.nutz.dao.util.cri.SqlExpressionGroup;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.ioc.aop.Aop;
import org.nutz.lang.Strings;
import org.nutz.lang.util.NutMap;
@@ -85,7 +89,7 @@ public class ActivityCultureServiceImpl extends BaseServiceImpl<ActivityTissue>
}
@Override
public Object pageData(PageForm page, String year, String name, String unionId, Integer activity_type) {
public Object pageData(PageForm page, String year, String name, String unionId, Integer activity_type, Integer templateStatus) {
Cnd cnd = Cnd.NEW();
Sql sql = Sqls.create("""
@@ -110,7 +114,7 @@ public class ActivityCultureServiceImpl extends BaseServiceImpl<ActivityTissue>
t.variable taskVariable,
IF((SELECT taskName FROM wf_process_task tt WHERE tt.id = t.taskParentId) = 'startTask', 1, 0) canRevoke,
(select MAX(id) from wf_process_task where processInstanceId = ins.id and taskName = 'startTask') AS startTaskId,
IF(sht.id IS NULL, 0, 1) AS isTemplate
tissue.templateStatus AS isTemplate
FROM
activity_tissue tissue
LEFT JOIN sys_union uni ON uni.id = tissue.unionId
@@ -119,7 +123,6 @@ public class ActivityCultureServiceImpl extends BaseServiceImpl<ActivityTissue>
LEFT JOIN `wf_process_instance` ins ON ins.businessNo = tissue.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
LEFT JOIN sys_home_template sht ON sht.id = tissue.id
$condition
""");
@@ -127,6 +130,8 @@ public class ActivityCultureServiceImpl extends BaseServiceImpl<ActivityTissue>
cnd.andEX("tissue.projectTypeCode", "!=", "50004");
cnd.andEX("tissue.unionId", "=", unionId);
cnd.andEX("tissue.activity_type", "=", activity_type);
// 校工会管理页按页签传入模板状态;其他文化活动页面不传时保持原查询范围。
cnd.andEX("tissue.templateStatus", "=", templateStatus);
cnd.groupBy("tissue.id");
if (Strings.isNotBlank(name)) {
@@ -159,6 +164,61 @@ public class ActivityCultureServiceImpl extends BaseServiceImpl<ActivityTissue>
return this.listPageMap(page.getPageNumber(), page.getPageSize(), sql);
}
/**
* 将校工会或分工会文化活动转换为工作模板,并保留模板中心已维护的展示配置。
*
* @param id 活动ID
* @return 空字符串表示成功,否则返回业务错误信息
*/
@Override
@Aop(TransAop.READ_COMMITTED)
public String setTemplate(String id) {
ActivityTissue tissue = fetch(id);
if (tissue == null) {
return "活动不存在";
}
if (tissue.getActivity_type() == null || !List.of(40001, 40002).contains(tissue.getActivity_type())) {
return "仅校工会或分工会文化活动可设为模板";
}
Sys_home_template oldHomeTemplate = dao().fetch(Sys_home_template.class, id);
Sys_home_template sysHomeTemplate = tissue.covertToSysHomeTemplate();
if (oldHomeTemplate != null) {
// 重复设置时保留模板中心人工配置,避免覆盖置顶、推送、排序及模板素材。
sysHomeTemplate.setTop(oldHomeTemplate.getTop());
sysHomeTemplate.setPush(oldHomeTemplate.getPush());
sysHomeTemplate.setSortNo(oldHomeTemplate.getSortNo());
if (oldHomeTemplate.getTemplateName() != null) {
sysHomeTemplate.setTemplateName(oldHomeTemplate.getTemplateName());
}
if (oldHomeTemplate.getTemplateIcon() != null) {
sysHomeTemplate.setTemplateIcon(oldHomeTemplate.getTemplateIcon());
}
if (oldHomeTemplate.getTemplateFile() != null) {
sysHomeTemplate.setTemplateFile(oldHomeTemplate.getTemplateFile());
}
}
dao().insertOrUpdate(sysHomeTemplate);
dao().update(ActivityTissue.class,
Chain.make("templateStatus", ActivityTissue.TEMPLATE_STATUS_SET),
Cnd.where("id", "=", id));
return "";
}
/**
* 删除模板中心记录并将文化活动恢复为未设置模板状态。
*
* @param id 活动ID
*/
@Override
@Aop(TransAop.READ_COMMITTED)
public void cancelTemplate(String id) {
dao().delete(Sys_home_template.class, id);
dao().update(ActivityTissue.class,
Chain.make("templateStatus", ActivityTissue.TEMPLATE_STATUS_UNSET),
Cnd.where("id", "=", id));
}
@Override
public NutMap findOne(String id) {
Sql sql = Sqls.create("""
@@ -87,6 +87,9 @@ public class FamilyActivityApplyController {
Cnd cnd = Cnd.NEW();
cnd.andEX("id", "=", id);
cnd.andEX("`year`", "=", year);
if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) {
cnd.and("activityName", "like", "%" + pageForm.getSearchKeyword().trim() + "%");
}
//查询报名中
if (activityType == 2) {
cnd.and(new Static("now() < activitySignUpEndTime"));
@@ -117,6 +120,7 @@ public class FamilyActivityApplyController {
@Param(value = "courseTypeId") String courseTypeId,
@Param(value = "activityId") String activityId,
@Param(value = "assortTypes") String[] assortTypes,
@Param(value = "searchKeyword") String searchKeyword,
@Param(value = "dataType") String dataType) {
FamilyActivity activity = dao.fetch(FamilyActivity.class, activityId);
Sql sql = Sqls.create("""
@@ -131,6 +135,9 @@ public class FamilyActivityApplyController {
Cnd cnd = Cnd.NEW();
cnd.andEX("type.id", "=", courseTypeId);
cnd.and("tsuc.activityId", "=", activityId);
if (StrUtil.isNotBlank(searchKeyword)) {
cnd.and("tsuc.courseName", "like", "%" + searchKeyword.trim() + "%");
}
if(Lang.isNotEmpty(assortTypes)) {
cnd.and("tsuc.assort", "in", assortTypes);
}
@@ -6,7 +6,6 @@ import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.annotation.SLog;
import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.result.Result;
import com.budwk.app.sys.models.Sys_home_activity;
@@ -68,20 +67,15 @@ public class FamilyActivityController {
@SaCheckPermission("family.manage")
public Result pageData(PageForm pageForm,
@Param(value = "year") Integer year,
@Param(value = "activityName") String activityName) {
@Param(value = "activityName") String activityName,
@Param(value = "templateStatus") Integer templateStatus) {
Cnd cnd = Cnd.NEW();
cnd.andEX("year", "=", year);
cnd.and(Cnd.likeEX("activityName", activityName));
// 有模板管理角色时前端按页签传入状态;普通用户不传时保持原列表范围。
cnd.andEX("templateStatus", "=", templateStatus);
cnd.orderBy("createdAt", "desc");
Pagination<FamilyActivity> pagination = familyActivityManageService.pageData(pageForm, cnd);
List<FamilyActivity> list = pagination.getList(FamilyActivity.class);
if (list != null && !list.isEmpty()) {
List<String> ids = list.stream().map(FamilyActivity::getId).toList();
List<Sys_home_template> templateList = dao.query(Sys_home_template.class, Cnd.where("id", "in", ids));
List<String> templateIds = templateList.stream().map(Sys_home_template::getId).toList();
list.forEach(activity -> activity.setIsTemplate(templateIds.contains(activity.getId())));
}
return Result.success().addData(pagination);
return Result.success().addData(familyActivityManageService.pageData(pageForm, cnd));
}
@At
@@ -91,27 +85,9 @@ public class FamilyActivityController {
if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name())) {
return Result.error("无权操作");
}
FamilyActivity activity = familyActivityManageService.fetch(id);
if (activity == null) {
if (!familyActivityManageService.setTemplate(id)) {
return Result.error("活动不存在");
}
Sys_home_template oldHomeTemplate = dao.fetch(Sys_home_template.class, id);
Sys_home_template sysHomeTemplate = activity.covertToSysHomeTemplate();
if (oldHomeTemplate != null) {
sysHomeTemplate.setTop(oldHomeTemplate.getTop());
sysHomeTemplate.setPush(oldHomeTemplate.getPush());
sysHomeTemplate.setSortNo(oldHomeTemplate.getSortNo());
if (oldHomeTemplate.getTemplateName() != null) {
sysHomeTemplate.setTemplateName(oldHomeTemplate.getTemplateName());
}
if (oldHomeTemplate.getTemplateIcon() != null) {
sysHomeTemplate.setTemplateIcon(oldHomeTemplate.getTemplateIcon());
}
if (oldHomeTemplate.getTemplateFile() != null) {
sysHomeTemplate.setTemplateFile(oldHomeTemplate.getTemplateFile());
}
}
dao.insertOrUpdate(sysHomeTemplate);
return Result.success();
}
@@ -122,7 +98,7 @@ public class FamilyActivityController {
if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name())) {
return Result.error("无权操作");
}
dao.delete(Sys_home_template.class, id);
familyActivityManageService.cancelTemplate(id);
return Result.success();
}
@@ -224,6 +200,8 @@ public class FamilyActivityController {
if (activity == null) {
return;
}
// 直接新建或从模板另存时明确重置状态,防止前端历史字段被带回。
activity.setTemplateStatus(FamilyActivity.TEMPLATE_STATUS_UNSET);
activity.setId(null);
activity.setIsTemplate(false);
activity.setCreatedBy(null);
@@ -25,6 +25,12 @@ import java.util.List;
@EqualsAndHashCode(callSuper = false)
public class FamilyActivity extends BaseModel implements Serializable, SysHomeConvert {
/** 未设置为活动模板。 */
public static final int TEMPLATE_STATUS_UNSET = 0;
/** 已设置为活动模板。 */
public static final int TEMPLATE_STATUS_SET = 1;
@Name
@PrevInsert(uu32 = true)
private String id;
@@ -114,6 +120,12 @@ public class FamilyActivity extends BaseModel implements Serializable, SysHomeCo
private Boolean isTemplate;
@Column
@Comment("模板设置状态(0:未设置,1:已设置)")
@ColDefine(type = ColType.INT, width = 1)
@Default("0")
private Integer templateStatus;
@Column
@Comment("活动类型")
@ColDefine(type = ColType.VARCHAR, width = 20)
@@ -50,6 +50,21 @@ public interface FamilyActivityService extends BaseService<FamilyActivity> {
*/
Pagination pageData(PageForm pageForm, Cnd cnd);
/**
* 将亲子活动设置为工作模板,并同步模板状态。
*
* @param id 活动ID
* @return true 表示活动存在且设置成功,false 表示活动不存在
*/
boolean setTemplate(String id);
/**
* 取消亲子活动模板,并同步模板状态。
*
* @param id 活动ID
*/
void cancelTemplate(String id);
/**
* 手机端分页查询
@@ -6,6 +6,7 @@ import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.result.Result;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.sys.models.Sys_home_activity;
import com.budwk.app.sys.models.Sys_home_template;
import com.budwk.app.sys.views.View_user;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.activity.family.models.*;
@@ -48,7 +49,8 @@ public class FamilyActivityServiceImpl extends BaseServiceImpl<FamilyActivity> i
@Override
@Aop(TransAop.READ_COMMITTED)
public void add(FamilyActivity activity, FamilyCourse course) {
// 新建活动(包括从模板另存)统一归入活动列表,不能继承来源活动的模板状态。
activity.setTemplateStatus(FamilyActivity.TEMPLATE_STATUS_UNSET);
dao().insert(activity);
//插入类型限制
@@ -215,6 +217,57 @@ public class FamilyActivityServiceImpl extends BaseServiceImpl<FamilyActivity> i
return pagination;
}
/**
* 将亲子活动转换为工作模板,并保留模板中心已维护的展示配置。
*
* @param id 活动ID
* @return true 表示活动存在且设置成功,false 表示活动不存在
*/
@Override
@Aop(TransAop.READ_COMMITTED)
public boolean setTemplate(String id) {
FamilyActivity activity = fetch(id);
if (activity == null) {
return false;
}
Sys_home_template oldHomeTemplate = dao().fetch(Sys_home_template.class, id);
Sys_home_template sysHomeTemplate = activity.covertToSysHomeTemplate();
if (oldHomeTemplate != null) {
// 重复设置时保留模板中心人工配置,避免覆盖置顶、推送、排序及模板素材。
sysHomeTemplate.setTop(oldHomeTemplate.getTop());
sysHomeTemplate.setPush(oldHomeTemplate.getPush());
sysHomeTemplate.setSortNo(oldHomeTemplate.getSortNo());
if (oldHomeTemplate.getTemplateName() != null) {
sysHomeTemplate.setTemplateName(oldHomeTemplate.getTemplateName());
}
if (oldHomeTemplate.getTemplateIcon() != null) {
sysHomeTemplate.setTemplateIcon(oldHomeTemplate.getTemplateIcon());
}
if (oldHomeTemplate.getTemplateFile() != null) {
sysHomeTemplate.setTemplateFile(oldHomeTemplate.getTemplateFile());
}
}
dao().insertOrUpdate(sysHomeTemplate);
dao().update(FamilyActivity.class,
Chain.make("templateStatus", FamilyActivity.TEMPLATE_STATUS_SET),
Cnd.where("id", "=", id));
return true;
}
/**
* 删除模板中心记录并将亲子活动恢复为未设置模板状态。
*
* @param id 活动ID
*/
@Override
@Aop(TransAop.READ_COMMITTED)
public void cancelTemplate(String id) {
dao().delete(Sys_home_template.class, id);
dao().update(FamilyActivity.class,
Chain.make("templateStatus", FamilyActivity.TEMPLATE_STATUS_UNSET),
Cnd.where("id", "=", id));
}
@Override
public Pagination mPageData(PageForm pageForm, Integer year, int activityStatus, Integer activityType) {
Cnd cnd = Cnd.NEW();
@@ -65,7 +65,8 @@ public class ActivitySportsInfoManageController {
@Param(value = "gameStyle") String gameStyle,
@Param(value = "query") String query,
@Param(value = "activityId") String activityId,
@Param(value = "activityLevel") String activityLevel) {
@Param(value = "activityLevel") String activityLevel,
@Param(value = "templateStatus") Integer templateStatus) {
Cnd cnd = Cnd.NEW();
Sql sql = Sqls.create("""
SELECT
@@ -91,13 +92,12 @@ public class ActivitySportsInfoManageController {
school.activityCode,
school.foundDate,
school.applyType,
IF(sht.id IS NULL, 0, 1) isTemplate,
school.templateStatus isTemplate,
(SELECT COUNT(1) FROM activity_school_apply asa WHERE asa.activityId = school.id and status=2) applyNum
FROM
activity_school school
LEFT JOIN activity_basic_settings ba ON ba.`code` = school.activityLevel
LEFT JOIN sys_union un ON un.id = school.belongUnionId
LEFT JOIN sys_home_template sht ON sht.id = school.id
$condition
""");
@@ -121,6 +121,8 @@ public class ActivitySportsInfoManageController {
cnd.andEX("school.activityLevel", "=", activityLevel);
cnd.andEX("school.gameStyle", "=", gameStyle);
// 管理员页签传入模板状态;未展示页签的用户不传该参数,继续查询原完整列表。
cnd.andEX("school.templateStatus", "=", templateStatus);
cnd.andEX("YEAR(school.applyStartTime)", "=", year);
if (StrUtil.isNotBlank(name)) {
cnd.and(Cnd.likeEX("school.name", name));
@@ -141,27 +143,9 @@ public class ActivitySportsInfoManageController {
if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name())) {
return Result.error("无权操作");
}
ActivitySchool activitySchool = activitySportsService.fetch(id);
if (activitySchool == null) {
if (!activitySportsService.setTemplate(id)) {
return Result.error("活动不存在");
}
Sys_home_template oldHomeTemplate = dao.fetch(Sys_home_template.class, id);
Sys_home_template sysHomeTemplate = activitySchool.covertToSysHomeTemplate();
if (oldHomeTemplate != null) {
sysHomeTemplate.setTop(oldHomeTemplate.getTop());
sysHomeTemplate.setPush(oldHomeTemplate.getPush());
sysHomeTemplate.setSortNo(oldHomeTemplate.getSortNo());
if (oldHomeTemplate.getTemplateName() != null) {
sysHomeTemplate.setTemplateName(oldHomeTemplate.getTemplateName());
}
if (oldHomeTemplate.getTemplateIcon() != null) {
sysHomeTemplate.setTemplateIcon(oldHomeTemplate.getTemplateIcon());
}
if (oldHomeTemplate.getTemplateFile() != null) {
sysHomeTemplate.setTemplateFile(oldHomeTemplate.getTemplateFile());
}
}
dao.insertOrUpdate(sysHomeTemplate);
return Result.success();
}
@@ -172,7 +156,7 @@ public class ActivitySportsInfoManageController {
if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name())) {
return Result.error("无权操作");
}
dao.delete(Sys_home_template.class, id);
activitySportsService.cancelTemplate(id);
return Result.success();
}
@@ -17,7 +17,7 @@ import com.budwk.app.zhgh.activity.sports.service.ActivitySportsApplyUserService
import org.nutz.dao.Cnd;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.dao.util.cri.SqlExpressionGroup;
import org.nutz.dao.util.cri.Static;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.random.R;
@@ -26,8 +26,6 @@ import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
import org.nutz.trans.Trans;
import java.util.Arrays;
/**
* @author zhf
* @date 2021/5/19 14:58
@@ -57,7 +55,8 @@ public class ActivitySportsResultsController {
PageForm page,
String activityId,
String eventId,
String[] isMenWomen) {
Integer isMenWomen,
Integer projectType) {
Cnd cnd = Cnd.NEW();
Sql sql = Sqls.create("""
SELECT
@@ -79,22 +78,10 @@ public class ActivitySportsResultsController {
cnd.and("ase.activityId", "=", activityId);
cnd.andEX("abs.`name`", "=", groupName);
cnd.andEX("ae.`id`", "=", eventId);
SqlExpressionGroup sqlExpressionGroup = new SqlExpressionGroup();
if (Arrays.stream(isMenWomen).anyMatch(a -> a.equals("2"))) {
sqlExpressionGroup.and("ae.isMenWomen", "=", 1);
}
if (Arrays.stream(isMenWomen).anyMatch(a -> a.equals("3"))) {
sqlExpressionGroup.or("ae.isMenWomen", "=", 2);
}
if (Arrays.stream(isMenWomen).anyMatch(a -> a.equals("4"))) {
sqlExpressionGroup.and("ae.projectType", "=", 1);
}
if (Arrays.stream(isMenWomen).anyMatch(a -> a.equals("5"))) {
sqlExpressionGroup.or("ae.projectType", "=", 2);
}
if (sqlExpressionGroup.getExps().size() > 0) {
cnd.and(sqlExpressionGroup);
}
// 页面男子/女子选项值与活动项目性别字段保持一致:1 为男子,2 为女子。
cnd.andEX("ae.isMenWomen", "=", isMenWomen);
// 页面项目类型选项值与活动项目类型字段保持一致:1 为单项,2 为团体。
cnd.andEX("ae.projectType", "=", projectType);
cnd.desc("allName");
sql.setCondition(cnd);
return Result.success(baseService.listPageMap(page.getPageNumber(), page.getPageSize(), sql));
@@ -192,8 +179,9 @@ public class ActivitySportsResultsController {
cnd.and("asa.activityId", "=", activityId);
cnd.and("asa.eventId", "=", eventId);
cnd.and("asa.awardsMode", "=", 1);
if (isMenWomen != null && isMenWomen == 1) cnd.and("u.sex", "=", "男性");
if (isMenWomen != null && isMenWomen == 2) cnd.and("u.sex", "=", "女性");
// 兼容历史人员“男性/女性”和临时添加人员“男/女”的性别值。
if (isMenWomen != null && isMenWomen == 1) cnd.and(new Static("u.sex IN ('男', '男性')"));
if (isMenWomen != null && isMenWomen == 2) cnd.and(new Static("u.sex IN ('女', '女性')"));
sql.setCondition(cnd);
return Result.success(activitySchoolApplyViService.listMap(sql));
}
@@ -268,8 +256,9 @@ public class ActivitySportsResultsController {
""");
cnd.and("ar.activityId", "=", activityId);
cnd.and("ar.eventId", "=", eventId);
if (isMenWomen != null && isMenWomen == 1) cnd.and("u.sex", "=", "男性");
if (isMenWomen != null && isMenWomen == 2) cnd.and("u.sex", "=", "女性");
// 兼容历史人员“男性/女性”和临时添加人员“男/女”的性别值。
if (isMenWomen != null && isMenWomen == 1) cnd.and(new Static("u.sex IN ('男', '男性')"));
if (isMenWomen != null && isMenWomen == 2) cnd.and(new Static("u.sex IN ('女', '女性')"));
cnd.asc("ar.ranking");
sql.setCondition(cnd);
return Result.success(baseService.listMap(sql));
@@ -64,8 +64,8 @@ public class H5ActivitySportsApplyUserController {
@At
@SaCheckPermission("h5.activity.sports.applyUser")
public Result activityData(Integer year, Integer isActivity, Integer applyStatus) {
return Result.success(activitySportsApplyUserService.activityData(year, isActivity, applyStatus));
public Result activityData(Integer year, Integer isActivity, Integer applyStatus, String searchKeyword) {
return Result.success(activitySportsApplyUserService.activityData(year, isActivity, applyStatus, searchKeyword));
}
@@ -171,6 +171,18 @@ public class ActivitySchool extends BaseModel implements Serializable , SysHomeC
@ColDefine(type = ColType.BOOLEAN)
private Integer isSave;
/** 未设置为工作模板。 */
public static final int TEMPLATE_STATUS_UNSET = 0;
/** 已设置为工作模板。 */
public static final int TEMPLATE_STATUS_SET = 1;
@Column
@Comment("模板设置状态(0:未设置 1:已设置)")
@ColDefine(type = ColType.INT, width = 1)
@Default("0")
private Integer templateStatus;
@Column
@Comment("小程序活动封面")
@ColDefine(type = ColType.VARCHAR,width = 100)
@@ -18,6 +18,7 @@ public class ActivitySportsApplyUserPageParam extends PageForm {
private String applyType;
private String isAudit;
private String groupName;
private String searchKeyword;
private String activityId;
private String eventId;
private String year;
@@ -19,6 +19,17 @@ public interface ActivitySportsApplyUserService extends BaseService<ActivityScho
Pagination activityData(Integer year, Integer isActivity,Integer applyStatus);
/**
* 根据年份、活动状态、报名状态和活动名称查询体育活动。
*
* @param year 活动年份
* @param isActivity 活动状态
* @param applyStatus 当前用户报名状态
* @param searchKeyword 活动名称模糊查询关键词
* @return 体育活动分页数据
*/
Pagination activityData(Integer year, Integer isActivity, Integer applyStatus, String searchKeyword);
List<NutMap> getEvents(String activityId);
Object getUnionCoachLeaderHead(String activityId,String unionId);
@@ -18,5 +18,20 @@ public interface ActivitySportsService extends BaseService<ActivitySchool> {
void doDelete(String id);
/**
* 将体育活动设置为工作模板,并同步活动表中的模板设置状态。
*
* @param id 体育活动ID
* @return 活动存在且设置成功时返回 true,活动不存在时返回 false
*/
boolean setTemplate(String id);
/**
* 取消体育活动的工作模板,并同步活动表中的模板设置状态。
*
* @param id 体育活动ID
*/
void cancelTemplate(String id);
void exportXlsx(String id, String unionId, HttpServletResponse response);
}
@@ -122,6 +122,9 @@ public class ActivitySportsApplyUserServiceImpl extends BaseServiceImpl<Activity
cnd.and(new Static("(SELECT COUNT(1) FROM activity_school_apply WHERE eventId = ase.eventId AND activityId = ase.activityId AND userId='%s')%s0".formatted(SecurityUtil.getUserId(), (pageParam.getIsAudit().equals("true") ? ">" : "="))));
}
cnd.and("ase.activityId", "=", pageParam.getActivityId());
if (StrUtil.isNotBlank(pageParam.getSearchKeyword())) {
cnd.and("eve.allName", "like", "%" + pageParam.getSearchKeyword().trim() + "%");
}
cnd.andEX("ase.eventId", "=", pageParam.getEventId());
cnd.andEX("YEAR(school.applyStartTime)", "=", pageParam.getYear());
@@ -144,6 +147,20 @@ public class ActivitySportsApplyUserServiceImpl extends BaseServiceImpl<Activity
@Override
public Pagination activityData(Integer year, Integer isActivity, Integer applyStatus) {
return activityData(year, isActivity, applyStatus, null);
}
/**
* 根据筛选条件及活动名称查询当前用户可见的体育活动。
*
* @param year 活动年份
* @param isActivity 活动状态
* @param applyStatus 当前用户报名状态
* @param searchKeyword 活动名称模糊查询关键词
* @return 体育活动分页数据
*/
@Override
public Pagination activityData(Integer year, Integer isActivity, Integer applyStatus, String searchKeyword) {
Sql sql = Sqls.create("""
SELECT
@@ -183,6 +200,10 @@ public class ActivitySportsApplyUserServiceImpl extends BaseServiceImpl<Activity
cnd.and("school.isSave", "!=", true);
if (Lang.isNotEmpty(searchKeyword)) {
cnd.and("school.NAME", "like", "%" + searchKeyword.trim() + "%");
}
//查询报名中
if (isActivity == 2) {
cnd.and(new Static("now() >applyStartTime and now() < applyEndTime"));
@@ -8,6 +8,7 @@ import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.base.utils.CommonDownloadUtil;
import com.budwk.app.base.utils.DateUtil;
import com.budwk.app.sys.models.Sys_home_activity;
import com.budwk.app.sys.models.Sys_home_template;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.activity.basic.models.ActivityBasicUnit;
import com.budwk.app.zhgh.activity.basic.models.ActivityBasicUnion;
@@ -61,6 +62,8 @@ public class ActivitySportsServiceImpl extends BaseServiceImpl<ActivitySchool> i
}
activitySchool.setFoundDate(DateUtil.getDate());
activitySchool.setClose(false);
// 新建活动只能从未设置模板状态开始防止客户端提交模板状态造成数据不一致
activitySchool.setTemplateStatus(ActivitySchool.TEMPLATE_STATUS_UNSET);
ActivitySchool school = insert(activitySchool);
addTeam(school.getId(), events);
@@ -190,6 +193,59 @@ public class ActivitySportsServiceImpl extends BaseServiceImpl<ActivitySchool> i
dao().delete(Sys_home_activity.class, id);
}
/**
* 将活动内容转换为首页工作模板并在同一事务内更新活动模板状态
* 重复设置时保留管理员在工作模板管理中维护的名称图标文件及排序配置
*
* @param id 体育活动ID
* @return 活动存在且设置成功时返回 true否则返回 false
*/
@Override
@Aop(TransAop.READ_COMMITTED)
public boolean setTemplate(String id) {
ActivitySchool activitySchool = fetch(id);
if (activitySchool == null) {
return false;
}
Sys_home_template oldHomeTemplate = dao().fetch(Sys_home_template.class, id);
Sys_home_template sysHomeTemplate = activitySchool.covertToSysHomeTemplate();
if (oldHomeTemplate != null) {
// 模板业务数据随活动刷新模板管理页面维护的展示配置继续沿用
sysHomeTemplate.setTop(oldHomeTemplate.getTop());
sysHomeTemplate.setPush(oldHomeTemplate.getPush());
sysHomeTemplate.setSortNo(oldHomeTemplate.getSortNo());
if (oldHomeTemplate.getTemplateName() != null) {
sysHomeTemplate.setTemplateName(oldHomeTemplate.getTemplateName());
}
if (oldHomeTemplate.getTemplateIcon() != null) {
sysHomeTemplate.setTemplateIcon(oldHomeTemplate.getTemplateIcon());
}
if (oldHomeTemplate.getTemplateFile() != null) {
sysHomeTemplate.setTemplateFile(oldHomeTemplate.getTemplateFile());
}
}
dao().insertOrUpdate(sysHomeTemplate);
dao().update(ActivitySchool.class,
Chain.make("templateStatus", ActivitySchool.TEMPLATE_STATUS_SET),
Cnd.where("id", "=", id));
return true;
}
/**
* 删除首页工作模板并在同一事务内将活动恢复为未设置模板状态
*
* @param id 体育活动ID
*/
@Override
@Aop(TransAop.READ_COMMITTED)
public void cancelTemplate(String id) {
dao().delete(Sys_home_template.class, id);
dao().update(ActivitySchool.class,
Chain.make("templateStatus", ActivitySchool.TEMPLATE_STATUS_UNSET),
Cnd.where("id", "=", id));
}
@Override
public void exportXlsx(String id, String unionId, HttpServletResponse response) {
try {
@@ -93,6 +93,9 @@ public class TrainSignUpApplyController {
Cnd cnd = Cnd.NEW();
cnd.andEX("id", "=", id);
cnd.andEX("`year`", "=", year);
if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) {
cnd.and(Cnd.likeEX("activityName", pageForm.getSearchKeyword()));
}
//查询报名中
if (activityType == 2) {
cnd.and(new Static("now() < activitySignUpEndTime"));
@@ -123,6 +126,7 @@ public class TrainSignUpApplyController {
@Param(value = "courseTypeId") String courseTypeId,
@Param(value = "activityId") String activityId,
@Param(value = "assortTypes") String[] assortTypes,
@Param(value = "searchKeyword") String searchKeyword,
@Param(value = "dataType") String dataType) {
Sql sql = Sqls.create("""
SELECT
@@ -156,6 +160,9 @@ public class TrainSignUpApplyController {
Cnd cnd = Cnd.NEW();
cnd.andEX("type.id", "=", courseTypeId);
cnd.and("tsuc.activityId", "=", activityId);
if (StrUtil.isNotBlank(searchKeyword)) {
cnd.and("tsuc.courseName", "like", "%" + searchKeyword.trim() + "%");
}
if (Lang.isNotEmpty(assortTypes)) {
cnd.and("tsuc.assort", "in", assortTypes);
@@ -6,7 +6,6 @@ import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.annotation.SLog;
import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.result.Result;
import com.budwk.app.flow.entity.ProcessInstance;
@@ -72,20 +71,15 @@ public class TrainSignUpManageController {
@SaCheckPermission("trainSignUp.manage")
public Result pageData(PageForm pageForm,
@Param(value = "year") Integer year,
@Param(value = "activityName") String activityName) {
@Param(value = "activityName") String activityName,
@Param(value = "templateStatus") Integer templateStatus) {
Cnd cnd = Cnd.NEW();
cnd.andEX("year", "=", year);
cnd.and(Cnd.likeEX("activityName", activityName));
// 具备模板管理角色时前端按页签传入状态普通用户不传时仍保持原列表范围
cnd.andEX("templateStatus", "=", templateStatus);
cnd.orderBy("createdAt", "desc");
Pagination<TrainSignUpActivity> pagination = trainSignUpActivityManageService.pageData(pageForm, cnd);
List<TrainSignUpActivity> list = pagination.getList(TrainSignUpActivity.class);
if (list != null && !list.isEmpty()) {
List<String> ids = list.stream().map(TrainSignUpActivity::getId).toList();
List<Sys_home_template> templateList = dao.query(Sys_home_template.class, Cnd.where("id", "in", ids));
List<String> templateIds = templateList.stream().map(Sys_home_template::getId).toList();
list.forEach(activity -> activity.setIsTemplate(templateIds.contains(activity.getId())));
}
return Result.success().addData(pagination);
return Result.success().addData(trainSignUpActivityManageService.pageData(pageForm, cnd));
}
@At
@@ -95,27 +89,9 @@ public class TrainSignUpManageController {
if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name())) {
return Result.error("无权操作");
}
TrainSignUpActivity activity = trainSignUpActivityManageService.fetch(id);
if (activity == null) {
if (!trainSignUpActivityManageService.setTemplate(id)) {
return Result.error("活动不存在");
}
Sys_home_template oldHomeTemplate = dao.fetch(Sys_home_template.class, id);
Sys_home_template sysHomeTemplate = activity.covertToSysHomeTemplate();
if (oldHomeTemplate != null) {
sysHomeTemplate.setTop(oldHomeTemplate.getTop());
sysHomeTemplate.setPush(oldHomeTemplate.getPush());
sysHomeTemplate.setSortNo(oldHomeTemplate.getSortNo());
if (oldHomeTemplate.getTemplateName() != null) {
sysHomeTemplate.setTemplateName(oldHomeTemplate.getTemplateName());
}
if (oldHomeTemplate.getTemplateIcon() != null) {
sysHomeTemplate.setTemplateIcon(oldHomeTemplate.getTemplateIcon());
}
if (oldHomeTemplate.getTemplateFile() != null) {
sysHomeTemplate.setTemplateFile(oldHomeTemplate.getTemplateFile());
}
}
dao.insertOrUpdate(sysHomeTemplate);
return Result.success();
}
@@ -126,7 +102,7 @@ public class TrainSignUpManageController {
if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name())) {
return Result.error("无权操作");
}
dao.delete(Sys_home_template.class, id);
trainSignUpActivityManageService.cancelTemplate(id);
return Result.success();
}
@@ -22,6 +22,12 @@ import java.util.List;
@EqualsAndHashCode(callSuper = false)
public class TrainSignUpActivity extends BaseModel implements Serializable, SysHomeConvert {
/** 未设置为活动模板。 */
public static final int TEMPLATE_STATUS_UNSET = 0;
/** 已设置为活动模板。 */
public static final int TEMPLATE_STATUS_SET = 1;
@Name
@PrevInsert(uu32 = true)
private String id;
@@ -112,6 +118,12 @@ public class TrainSignUpActivity extends BaseModel implements Serializable, SysH
private Boolean isTemplate;
@Column
@Comment("模板设置状态(0:未设置,1:已设置)")
@ColDefine(type = ColType.INT, width = 1)
@Default("0")
private Integer templateStatus;
@Column
@Comment("活动类型")
@ColDefine(type = ColType.VARCHAR, width = 20)
@@ -59,6 +59,21 @@ public interface TrainSignUpActivityService extends BaseService<TrainSignUpActiv
*/
Pagination pageData(PageForm pageForm, Cnd cnd);
/**
* 将品牌活动设置为工作模板并同步活动表中的模板状态
*
* @param id 活动ID
* @return true 表示活动存在且设置成功false 表示活动不存在
*/
boolean setTemplate(String id);
/**
* 取消品牌活动工作模板并同步活动表中的模板状态
*
* @param id 活动ID
*/
void cancelTemplate(String id);
/**
* 手机端分页查询
@@ -5,6 +5,7 @@ import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.sys.models.Sys_home_activity;
import com.budwk.app.sys.models.Sys_home_template;
import com.budwk.app.sys.views.View_user;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.activity.family.models.FamilyActivity;
@@ -53,7 +54,8 @@ public class TrainSignUpActivityServiceImpl extends BaseServiceImpl<TrainSignUpA
@Override
@Aop(TransAop.READ_COMMITTED)
public void add(TrainSignUpActivity activity, TrainSignUpCourse course) {
// 新建活动包括从模板另存统一归入活动列表不能继承来源活动的模板状态
activity.setTemplateStatus(TrainSignUpActivity.TEMPLATE_STATUS_UNSET);
dao().insert(activity);
//插入类型限制
@@ -289,6 +291,58 @@ public class TrainSignUpActivityServiceImpl extends BaseServiceImpl<TrainSignUpA
return pagination;
}
/**
* 将品牌活动转换为工作模板并原样保留模板中心已维护的展示配置
*
* @param id 活动ID
* @return true 表示活动存在且设置成功false 表示活动不存在
*/
@Override
@Aop(TransAop.READ_COMMITTED)
public boolean setTemplate(String id) {
TrainSignUpActivity activity = fetch(id);
if (activity == null) {
return false;
}
Sys_home_template oldHomeTemplate = dao().fetch(Sys_home_template.class, id);
Sys_home_template sysHomeTemplate = activity.covertToSysHomeTemplate();
if (oldHomeTemplate != null) {
// 重复设置时保留模板中心人工配置避免覆盖置顶推送排序及模板素材
sysHomeTemplate.setTop(oldHomeTemplate.getTop());
sysHomeTemplate.setPush(oldHomeTemplate.getPush());
sysHomeTemplate.setSortNo(oldHomeTemplate.getSortNo());
if (oldHomeTemplate.getTemplateName() != null) {
sysHomeTemplate.setTemplateName(oldHomeTemplate.getTemplateName());
}
if (oldHomeTemplate.getTemplateIcon() != null) {
sysHomeTemplate.setTemplateIcon(oldHomeTemplate.getTemplateIcon());
}
if (oldHomeTemplate.getTemplateFile() != null) {
sysHomeTemplate.setTemplateFile(oldHomeTemplate.getTemplateFile());
}
}
dao().insertOrUpdate(sysHomeTemplate);
dao().update(TrainSignUpActivity.class,
Chain.make("templateStatus", TrainSignUpActivity.TEMPLATE_STATUS_SET),
Cnd.where("id", "=", id));
return true;
}
/**
* 删除模板中心记录并将活动恢复为未设置模板状态
*
* @param id 活动ID
*/
@Override
@Aop(TransAop.READ_COMMITTED)
public void cancelTemplate(String id) {
dao().delete(Sys_home_template.class, id);
dao().update(TrainSignUpActivity.class,
Chain.make("templateStatus", TrainSignUpActivity.TEMPLATE_STATUS_UNSET),
Cnd.where("id", "=", id));
}
@Override
public Pagination mPageData(PageForm pageForm, Integer year, int activityStatus, Integer activityType) {
Cnd cnd = Cnd.NEW();
@@ -7,10 +7,8 @@ import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HtmlUtil;
import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.result.Result;
import com.budwk.app.base.service.BaseService;
import com.budwk.app.sys.models.Sys_home_activity;
import com.budwk.app.sys.models.Sys_home_template;
import com.budwk.app.sys.models.Sys_user;
@@ -22,6 +20,7 @@ import com.budwk.app.zhgh.activity.workscollection.models.Activity_works_collect
import com.budwk.app.zhgh.activity.workscollection.models.Activity_works_collection_upload;
import com.budwk.app.zhgh.activity.workscollection.models.Activity_works_subjectType;
import com.budwk.app.zhgh.activity.workscollection.models.Activity_works_worksType;
import com.budwk.app.zhgh.activity.workscollection.service.ActivityWorksCollectionService;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Chain;
import org.nutz.dao.Cnd;
@@ -50,7 +49,7 @@ public class ActivityWorksCollectionManageController {
@Inject
private Dao dao;
@Inject
private BaseService baseService;
private ActivityWorksCollectionService activityWorksCollectionService;
@Inject
private SysMsgService sysMsgService;
@Inject
@@ -64,31 +63,8 @@ public class ActivityWorksCollectionManageController {
@At
@SaCheckPermission("activity.workscollection.manage")
public Result pageData(@Valid PageForm pageForm, Long year) {
Sql sql = Sqls.create("""
select
wc.id,
wc.name,
wc.createdAt,
wc.startDateTime,
wc.endDateTime,
wc.enable,
IF(sht.id IS NULL, 0, 1) isTemplate,
u.username as userName
from
activity_works_collection wc
LEFT JOIN vw_user u on u.id = wc.createdBy
LEFT JOIN sys_home_template sht ON sht.id = wc.id
$condition
""");
Cnd cnd = Cnd.NEW();
cnd.andEX("year(startDateTime)", "=", year);
if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) {
cnd.where().andLike("name", pageForm.getSearchKeyword());
}
sql.setCondition(cnd);
Pagination pagination = baseService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
return Result.success(pagination);
public Result pageData(@Valid PageForm pageForm, Long year, Integer templateStatus) {
return Result.success(activityWorksCollectionService.pageData(pageForm, year, templateStatus));
}
@At
@@ -97,27 +73,9 @@ public class ActivityWorksCollectionManageController {
if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name())) {
return Result.error("无权操作");
}
Activity_works_collection worksCollection = dao.fetch(Activity_works_collection.class, id);
if (worksCollection == null) {
if (!activityWorksCollectionService.setTemplate(id)) {
return Result.error("活动不存在");
}
Sys_home_template oldHomeTemplate = dao.fetch(Sys_home_template.class, id);
Sys_home_template sysHomeTemplate = worksCollection.covertToSysHomeTemplate();
if (oldHomeTemplate != null) {
sysHomeTemplate.setTop(oldHomeTemplate.getTop());
sysHomeTemplate.setPush(oldHomeTemplate.getPush());
sysHomeTemplate.setSortNo(oldHomeTemplate.getSortNo());
if (oldHomeTemplate.getTemplateName() != null) {
sysHomeTemplate.setTemplateName(oldHomeTemplate.getTemplateName());
}
if (oldHomeTemplate.getTemplateIcon() != null) {
sysHomeTemplate.setTemplateIcon(oldHomeTemplate.getTemplateIcon());
}
if (oldHomeTemplate.getTemplateFile() != null) {
sysHomeTemplate.setTemplateFile(oldHomeTemplate.getTemplateFile());
}
}
dao.insertOrUpdate(sysHomeTemplate);
return Result.success();
}
@@ -127,7 +85,7 @@ public class ActivityWorksCollectionManageController {
if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name())) {
return Result.error("无权操作");
}
dao.delete(Sys_home_template.class, id);
activityWorksCollectionService.cancelTemplate(id);
return Result.success();
}
@@ -162,6 +120,8 @@ public class ActivityWorksCollectionManageController {
if (worksCollection == null) {
return;
}
// 新建或从模板另存的活动必须进入活动列表不能继承来源活动的模板状态
worksCollection.setTemplateStatus(Activity_works_collection.TEMPLATE_STATUS_UNSET);
worksCollection.setId(null);
worksCollection.setCreatedBy(null);
worksCollection.setCreatedAt(null);
@@ -202,6 +162,10 @@ public class ActivityWorksCollectionManageController {
@SaCheckPermission(value = {"activity.workscollection.manage", "activity.workscollection.new"}, mode = SaMode.OR)
@Aop(TransAop.READ_COMMITTED)
public Result update(@Param("data") @Valid Activity_works_collection worksCollection) {
String validateMessage = activityWorksCollectionService.validateReferencedTypesBeforeUpdate(worksCollection);
if (StrUtil.isNotBlank(validateMessage)) {
return Result.error(validateMessage);
}
dao.update(worksCollection);
dao.updateLinks(worksCollection, "subjectTypes");
dao.insertLinks(worksCollection, "subjectTypes");
@@ -22,6 +22,12 @@ import java.util.List;
@Comment("作品征集活动")
public class Activity_works_collection extends BaseModel implements SysHomeConvert {
/** 未设置为活动模板。 */
public static final int TEMPLATE_STATUS_UNSET = 0;
/** 已设置为活动模板。 */
public static final int TEMPLATE_STATUS_SET = 1;
@Name
@Comment("ID")
@PrevInsert(uu32 = true)
@@ -110,6 +116,12 @@ public class Activity_works_collection extends BaseModel implements SysHomeConve
@Default(value = "1")
private Boolean isSubmit;
@Column
@Comment("模板设置状态(0:未设置,1:已设置)")
@ColDefine(type = ColType.INT, width = 1)
@Default("0")
private Integer templateStatus;
@Column
@Comment("主办单位")
@ColDefine(type = ColType.MYSQL_JSON)
@@ -0,0 +1,45 @@
package com.budwk.app.zhgh.activity.workscollection.service;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.service.BaseService;
import com.budwk.app.zhgh.activity.workscollection.models.Activity_works_collection;
/**
* 作品征集活动配置服务
*/
public interface ActivityWorksCollectionService extends BaseService<Activity_works_collection> {
/**
* 分页查询作品征集活动
*
* @param pageForm 分页及关键字参数
* @param year 活动年度
* @param templateStatus 模板设置状态为空时不限制
* @return 作品征集活动分页数据
*/
Pagination pageData(PageForm pageForm, Long year, Integer templateStatus);
/**
* 将作品征集活动设置为工作模板并同步模板状态
*
* @param id 活动ID
* @return true 表示设置成功false 表示活动不存在
*/
boolean setTemplate(String id);
/**
* 取消作品征集活动模板并同步模板状态
*
* @param id 活动ID
*/
void cancelTemplate(String id);
/**
* 校验编辑活动时删除的主题类型作品类型是否已被投稿引用
*
* @param worksCollection 前端提交的活动及其主题作品类型配置
* @return 校验通过返回 {@code null}校验不通过返回提示信息
*/
String validateReferencedTypesBeforeUpdate(Activity_works_collection worksCollection);
}
@@ -0,0 +1,213 @@
package com.budwk.app.zhgh.activity.workscollection.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.sys.models.Sys_home_template;
import com.budwk.app.zhgh.activity.workscollection.models.Activity_works_collection;
import com.budwk.app.zhgh.activity.workscollection.models.Activity_works_collection_upload;
import com.budwk.app.zhgh.activity.workscollection.models.Activity_works_subjectType;
import com.budwk.app.zhgh.activity.workscollection.models.Activity_works_worksType;
import com.budwk.app.zhgh.activity.workscollection.service.ActivityWorksCollectionService;
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;
import org.nutz.dao.sql.Sql;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.IocBean;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* 作品征集活动配置服务实现
*/
@IocBean(args = {"refer:dao"})
public class ActivityWorksCollectionServiceImpl extends BaseServiceImpl<Activity_works_collection> implements ActivityWorksCollectionService {
private final Dao dao;
public ActivityWorksCollectionServiceImpl(Dao dao) {
super(dao);
this.dao = dao;
}
/**
* 按年度关键字及模板状态分页查询作品征集活动
*/
@Override
public Pagination pageData(PageForm pageForm, Long year, Integer templateStatus) {
Sql sql = Sqls.create("""
select
wc.id,
wc.name,
wc.createdAt,
wc.startDateTime,
wc.endDateTime,
wc.enable,
wc.templateStatus,
wc.templateStatus isTemplate,
u.username as userName
from
activity_works_collection wc
LEFT JOIN vw_user u on u.id = wc.createdBy
$condition
""");
Cnd cnd = Cnd.NEW();
cnd.andEX("year(wc.startDateTime)", "=", year);
cnd.andEX("wc.templateStatus", "=", templateStatus);
if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) {
cnd.where().andLike("wc.name", pageForm.getSearchKeyword());
}
cnd.desc("wc.createdAt");
sql.setCondition(cnd);
return listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
}
/**
* 将作品征集活动转换为工作模板并保留模板中心已维护的展示配置
*/
@Override
@Aop(TransAop.READ_COMMITTED)
public boolean setTemplate(String id) {
Activity_works_collection worksCollection = dao.fetch(Activity_works_collection.class, id);
if (worksCollection == null) {
return false;
}
Sys_home_template oldHomeTemplate = dao.fetch(Sys_home_template.class, id);
Sys_home_template sysHomeTemplate = worksCollection.covertToSysHomeTemplate();
if (oldHomeTemplate != null) {
// 重复设置时保留模板中心人工配置避免覆盖置顶推送排序及模板素材
sysHomeTemplate.setTop(oldHomeTemplate.getTop());
sysHomeTemplate.setPush(oldHomeTemplate.getPush());
sysHomeTemplate.setSortNo(oldHomeTemplate.getSortNo());
if (oldHomeTemplate.getTemplateName() != null) {
sysHomeTemplate.setTemplateName(oldHomeTemplate.getTemplateName());
}
if (oldHomeTemplate.getTemplateIcon() != null) {
sysHomeTemplate.setTemplateIcon(oldHomeTemplate.getTemplateIcon());
}
if (oldHomeTemplate.getTemplateFile() != null) {
sysHomeTemplate.setTemplateFile(oldHomeTemplate.getTemplateFile());
}
}
dao.insertOrUpdate(sysHomeTemplate);
dao.update(Activity_works_collection.class,
Chain.make("templateStatus", Activity_works_collection.TEMPLATE_STATUS_SET),
Cnd.where("id", "=", id));
return true;
}
/**
* 删除模板中心记录并将作品征集活动恢复为未设置模板状态
*/
@Override
@Aop(TransAop.READ_COMMITTED)
public void cancelTemplate(String id) {
dao.delete(Sys_home_template.class, id);
dao.update(Activity_works_collection.class,
Chain.make("templateStatus", Activity_works_collection.TEMPLATE_STATUS_UNSET),
Cnd.where("id", "=", id));
}
@Override
public String validateReferencedTypesBeforeUpdate(Activity_works_collection worksCollection) {
if (worksCollection == null || StrUtil.isBlank(worksCollection.getId())) {
return null;
}
List<Activity_works_subjectType> persistedSubjectTypes = dao.query(Activity_works_subjectType.class,
Cnd.where(Activity_works_subjectType::getActivityId, "=", worksCollection.getId()));
List<Activity_works_subjectType> submittedSubjectTypes = CollUtil.defaultIfEmpty(worksCollection.getSubjectTypes(), Collections.emptyList());
Set<String> submittedSubjectIds = submittedSubjectTypes.stream()
.map(Activity_works_subjectType::getId)
.filter(StrUtil::isNotBlank)
.collect(Collectors.toSet());
List<Activity_works_subjectType> deletedSubjectTypes = persistedSubjectTypes.stream()
.filter(subjectType -> !submittedSubjectIds.contains(subjectType.getId()))
.toList();
String subjectError = getReferencedSubjectTypeError(worksCollection.getId(), deletedSubjectTypes);
if (subjectError != null) {
return subjectError;
}
for (Activity_works_subjectType submittedSubjectType : submittedSubjectTypes) {
if (submittedSubjectType == null || StrUtil.isBlank(submittedSubjectType.getId())) {
continue;
}
List<Activity_works_worksType> persistedWorksTypes = dao.query(Activity_works_worksType.class,
Cnd.where(Activity_works_worksType::getSubjectId, "=", submittedSubjectType.getId()));
List<Activity_works_worksType> submittedWorksTypes = CollUtil.defaultIfEmpty(submittedSubjectType.getWorksTypes(), Collections.emptyList());
Set<String> submittedWorksIds = submittedWorksTypes.stream()
.map(Activity_works_worksType::getId)
.filter(StrUtil::isNotBlank)
.collect(Collectors.toSet());
List<Activity_works_worksType> deletedWorksTypes = persistedWorksTypes.stream()
.filter(worksType -> !submittedWorksIds.contains(worksType.getId()))
.toList();
String worksError = getReferencedWorksTypeError(worksCollection.getId(), deletedWorksTypes);
if (worksError != null) {
return worksError;
}
}
return null;
}
/**
* 校验待删除主题类型是否已有投稿防止投稿的主题类型关联被清空
*/
private String getReferencedSubjectTypeError(String activityId, List<Activity_works_subjectType> deletedSubjectTypes) {
if (CollUtil.isEmpty(deletedSubjectTypes)) {
return null;
}
List<String> deletedSubjectIds = deletedSubjectTypes.stream()
.map(Activity_works_subjectType::getId)
.collect(Collectors.toList());
List<Activity_works_collection_upload> uploads = dao.query(Activity_works_collection_upload.class,
Cnd.where(Activity_works_collection_upload::getActivityId, "=", activityId)
.and(Activity_works_collection_upload::getSubjectId, "in", deletedSubjectIds));
if (CollUtil.isEmpty(uploads)) {
return null;
}
Set<String> referencedSubjectIds = uploads.stream()
.map(Activity_works_collection_upload::getSubjectId)
.collect(Collectors.toSet());
String typeNames = deletedSubjectTypes.stream()
.filter(subjectType -> referencedSubjectIds.contains(subjectType.getId()))
.map(Activity_works_subjectType::getTypeName)
.collect(Collectors.joining(""));
return "主题类型“" + typeNames + "”下已有投稿,不能删除";
}
/**
* 校验待删除作品类型是否已有投稿防止投稿的作品类型关联被清空
*/
private String getReferencedWorksTypeError(String activityId, List<Activity_works_worksType> deletedWorksTypes) {
if (CollUtil.isEmpty(deletedWorksTypes)) {
return null;
}
List<String> deletedWorksIds = deletedWorksTypes.stream()
.map(Activity_works_worksType::getId)
.collect(Collectors.toList());
List<Activity_works_collection_upload> uploads = dao.query(Activity_works_collection_upload.class,
Cnd.where(Activity_works_collection_upload::getActivityId, "=", activityId)
.and(Activity_works_collection_upload::getWorksId, "in", deletedWorksIds));
if (CollUtil.isEmpty(uploads)) {
return null;
}
Set<String> referencedWorksIds = uploads.stream()
.map(Activity_works_collection_upload::getWorksId)
.collect(Collectors.toSet());
String typeNames = deletedWorksTypes.stream()
.filter(worksType -> referencedWorksIds.contains(worksType.getId()))
.map(Activity_works_worksType::getWorksTypeName)
.collect(Collectors.joining(""));
return "作品类型“" + typeNames + "”下已有投稿,不能删除";
}
}
@@ -1,62 +0,0 @@
package com.budwk.app.zhgh.archive.constant;
import org.nutz.lang.Strings;
import java.util.List;
import java.util.Map;
/**
* 活动电子档案常量
*/
public final class ArchiveConstant {
public static final int PROJECT_DRAFT = 0;
public static final int PROJECT_FILING = 1;
public static final int PROJECT_COMPLETED = 2;
public static final int DOCUMENT_DRAFT = 0;
public static final int DOCUMENT_SUBMITTED = 1;
public static final String STAGE_PREPARE = "PREPARE";
public static final String STAGE_PROCESS = "PROCESS";
public static final String STAGE_SUMMARY = "SUMMARY";
public static final Map<String, String> STAGE_LABELS = Map.of(
STAGE_PREPARE, "筹备阶段",
STAGE_PROCESS, "实施阶段",
STAGE_SUMMARY, "总结阶段"
);
private ArchiveConstant() {
}
public static boolean validStage(String stage) {
return Strings.isNotBlank(stage) && STAGE_LABELS.containsKey(stage);
}
public static String stageLabel(String stage) {
return STAGE_LABELS.getOrDefault(stage, stage);
}
public static String projectStatusLabel(Integer status) {
if (status != null && status == PROJECT_COMPLETED) {
return "已完成";
}
if (status != null && status == PROJECT_FILING) {
return "归档中";
}
return "草稿";
}
public static String documentStatusLabel(Integer status) {
return status != null && status == DOCUMENT_SUBMITTED ? "已提交归档" : "草稿";
}
public static List<Map<String, String>> stageOptions() {
return List.of(
Map.of("value", STAGE_PREPARE, "label", STAGE_LABELS.get(STAGE_PREPARE)),
Map.of("value", STAGE_PROCESS, "label", STAGE_LABELS.get(STAGE_PROCESS)),
Map.of("value", STAGE_SUMMARY, "label", STAGE_LABELS.get(STAGE_SUMMARY))
);
}
}
@@ -1,221 +0,0 @@
package com.budwk.app.zhgh.archive.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.budwk.app.base.annotation.SLog;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.result.Result;
import com.budwk.app.zhgh.archive.constant.ArchiveConstant;
import com.budwk.app.zhgh.archive.model.ArchiveDocType;
import com.budwk.app.zhgh.archive.model.ArchiveDocument;
import com.budwk.app.zhgh.archive.model.ArchiveProject;
import com.budwk.app.zhgh.archive.service.ArchiveService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.nutz.dao.Dao;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Strings;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
/**
* 活动电子档案
*/
@IocBean
@Ok("json:full")
@At("/platform/archive")
@Api(tags = "活动电子档案")
public class ArchiveController {
@Inject
private ArchiveService archiveService;
@Inject
private Dao dao;
@At("/docType")
@Ok("beetl:/platform/zhgh/archive/docType/index.html")
@SaCheckPermission("archive.docType")
public void docTypeIndex() {
}
@At("/docType/pageData")
@SaCheckPermission("archive.docType")
public Result docTypePageData(PageForm pageForm, String name,
String projectStage, Boolean requiredFlag) {
return Result.success(archiveService.pageDocTypes(
pageForm, name, projectStage, requiredFlag));
}
@At("/docType/listData")
public Result docTypeListData(String projectStage) {
return Result.success(archiveService.listDocTypes(projectStage));
}
@At("/docType/fetchOne")
@SaCheckPermission("archive.docType")
public Result docTypeFetchOne(String id) {
return Result.success(dao.fetch(ArchiveDocType.class, id));
}
@At("/docType/onSubmit")
@SLog(tag = "活动电子档案-文档类型", msg = "新增或修改文档类型")
@SaCheckPermission("archive.docType")
public Result docTypeOnSubmit(ArchiveDocType docType) {
try {
archiveService.saveDocType(docType);
return Result.success();
} catch (IllegalArgumentException e) {
return Result.error(e.getMessage());
}
}
@At("/docType/onDelete")
@SLog(tag = "活动电子档案-文档类型", msg = "删除文档类型")
@SaCheckPermission("archive.docType")
public Result docTypeOnDelete(String id) {
try {
archiveService.deleteDocType(id);
return Result.success();
} catch (IllegalArgumentException e) {
return Result.error(e.getMessage());
}
}
@At("/project")
@Ok("beetl:/platform/zhgh/archive/project/index.html")
@SaCheckPermission("archive.project")
public void projectIndex() {
}
@At("/project/pageData")
@SaCheckPermission("archive.project")
public Result projectPageData(PageForm pageForm, Integer year,
String projectName, String unionId, Integer status) {
return Result.success(archiveService.pageProjects(
pageForm, year, projectName, unionId, status));
}
@At("/project/listData")
public Result projectListData() {
return Result.success(archiveService.listProjects());
}
@At("/project/fetchOne")
@SaCheckPermission("archive.project")
public Result projectFetchOne(String id) {
return Result.success(dao.fetch(ArchiveProject.class, id));
}
@At("/project/onSubmit")
@SLog(tag = "活动电子档案-归档项目", msg = "新增或修改归档项目")
@SaCheckPermission("archive.project")
public Result projectOnSubmit(ArchiveProject project) {
try {
archiveService.saveProject(project);
return Result.success();
} catch (IllegalArgumentException e) {
return Result.error(e.getMessage());
}
}
@At("/project/onDelete")
@SLog(tag = "活动电子档案-归档项目", msg = "删除归档项目")
@SaCheckPermission("archive.project")
public Result projectOnDelete(String id) {
try {
archiveService.deleteProject(id);
return Result.success();
} catch (IllegalArgumentException e) {
return Result.error(e.getMessage());
}
}
@At("/filing")
@Ok("beetl:/platform/zhgh/archive/filing/index.html")
@SaCheckPermission("archive.filing")
public void filingIndex() {
}
@At("/filing/pageData")
@SaCheckPermission("archive.filing")
public Result filingPageData(PageForm pageForm, String projectId, Integer year,
String projectStage, String docTypeId,
String documentName, Integer status) {
return Result.success(archiveService.pageDocuments(pageForm, projectId, year,
projectStage, docTypeId, documentName, status));
}
@At("/filing/fetchOne")
@SaCheckPermission("archive.filing")
public Result filingFetchOne(String id) {
return Result.success(archiveService.fetchDocument(id));
}
@At("/filing/saveDraft")
@ApiOperation("保存归档文档草稿")
@SLog(tag = "活动电子档案-资料归档", msg = "保存归档文档草稿")
@SaCheckPermission("archive.filing")
public Result filingSaveDraft(ArchiveDocument document) {
return saveDocument(document, false);
}
@At("/filing/submit")
@ApiOperation("提交归档文档")
@SLog(tag = "活动电子档案-资料归档", msg = "提交归档文档")
@SaCheckPermission("archive.filing")
public Result filingSubmit(ArchiveDocument document) {
return saveDocument(document, true);
}
@At("/filing/onDelete")
@SLog(tag = "活动电子档案-资料归档", msg = "删除归档文档")
@SaCheckPermission("archive.filing")
public Result filingOnDelete(String id) {
archiveService.deleteDocument(id);
return Result.success();
}
@At("/ledger")
@Ok("beetl:/platform/zhgh/archive/ledger/index.html")
@SaCheckPermission("archive.ledger")
public void ledgerIndex() {
}
@At("/ledger/pageData")
@SaCheckPermission("archive.ledger")
public Result ledgerPageData(PageForm pageForm, Integer year,
String projectName, String unionId, Integer status) {
return Result.success(archiveService.pageProjects(
pageForm, year, projectName, unionId, status));
}
@At("/ledger/detail")
@SaCheckPermission("archive.ledger")
public Result ledgerDetail(PageForm pageForm, String projectId,
String projectStage, String docTypeId,
String documentName, Integer status) {
return Result.success(archiveService.pageDocuments(pageForm, projectId,
null, projectStage, docTypeId, documentName, status));
}
@At("/common/stageOptions")
public Result stageOptions() {
return Result.success(ArchiveConstant.stageOptions());
}
@At("/common/unionOptions")
public Result unionOptions() {
return Result.success(archiveService.listUnions());
}
private Result saveDocument(ArchiveDocument document, boolean submit) {
try {
archiveService.saveDocument(document, submit);
return Result.success();
} catch (IllegalArgumentException e) {
return Result.error(e.getMessage());
}
}
}
@@ -1,55 +0,0 @@
package com.budwk.app.zhgh.archive.model;
import com.budwk.app.base.model.BaseModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import org.nutz.dao.entity.annotation.ColDefine;
import org.nutz.dao.entity.annotation.ColType;
import org.nutz.dao.entity.annotation.Column;
import org.nutz.dao.entity.annotation.Comment;
import org.nutz.dao.entity.annotation.Name;
import org.nutz.dao.interceptor.annotation.PrevInsert;
import org.nutz.dao.entity.annotation.Table;
import org.nutz.dao.entity.annotation.TableMeta;
/**
* 活动电子档案文档类型
*/
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
@Table("act_archive_doc_type")
@TableMeta("{'mysql-charset':'utf8mb4'}")
@Comment("活动电子档案文档类型")
public class ArchiveDocType extends BaseModel {
@Name
@PrevInsert(uu32 = true)
@ColDefine(type = ColType.VARCHAR, width = 32)
@Comment("主键")
private String id;
@Column
@ColDefine(type = ColType.VARCHAR, width = 150)
@Comment("类型名称")
private String name;
@Column
@ColDefine(type = ColType.VARCHAR, width = 32)
@Comment("项目阶段")
private String projectStage;
@Column
@Comment("排序编号")
private Integer sortNo;
@Column
@Comment("是否必选")
private Boolean requiredFlag;
@Column
@ColDefine(type = ColType.VARCHAR, width = 600)
@Comment("备注")
private String remark;
}
@@ -1,78 +0,0 @@
package com.budwk.app.zhgh.archive.model;
import cn.hutool.json.JSONObject;
import com.budwk.app.base.model.BaseModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import org.nutz.dao.entity.annotation.ColDefine;
import org.nutz.dao.entity.annotation.ColType;
import org.nutz.dao.entity.annotation.Column;
import org.nutz.dao.entity.annotation.Comment;
import org.nutz.dao.entity.annotation.Name;
import org.nutz.dao.interceptor.annotation.PrevInsert;
import org.nutz.dao.entity.annotation.Table;
import org.nutz.dao.entity.annotation.TableMeta;
import java.util.List;
/**
* 活动电子档案归档文档
*/
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
@Table("act_archive_document")
@TableMeta("{'mysql-charset':'utf8mb4'}")
@Comment("活动电子档案归档文档")
public class ArchiveDocument extends BaseModel {
@Name
@PrevInsert(uu32 = true)
@ColDefine(type = ColType.VARCHAR, width = 32)
@Comment("主键")
private String id;
@Column
@ColDefine(type = ColType.VARCHAR, width = 32)
@Comment("项目ID")
private String projectId;
@Column
@ColDefine(type = ColType.VARCHAR, width = 300)
@Comment("文档名称")
private String documentName;
@Column
@ColDefine(type = ColType.VARCHAR, width = 32)
@Comment("项目阶段")
private String projectStage;
@Column
@ColDefine(type = ColType.VARCHAR, width = 32)
@Comment("文档类型ID")
private String docTypeId;
@Column
@Comment("文档状态")
private Integer status;
@Column
@ColDefine(type = ColType.VARCHAR, width = 100)
@Comment("上传人")
private String uploader;
@Column
@Comment("上传时间")
private Long uploadTime;
@Column
@ColDefine(type = ColType.MYSQL_JSON)
@Comment("附件")
private List<JSONObject> files;
@Column
@ColDefine(type = ColType.VARCHAR, width = 600)
@Comment("备注")
private String remark;
}
@@ -1,55 +0,0 @@
package com.budwk.app.zhgh.archive.model;
import com.budwk.app.base.model.BaseModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import org.nutz.dao.entity.annotation.ColDefine;
import org.nutz.dao.entity.annotation.ColType;
import org.nutz.dao.entity.annotation.Column;
import org.nutz.dao.entity.annotation.Comment;
import org.nutz.dao.entity.annotation.Name;
import org.nutz.dao.interceptor.annotation.PrevInsert;
import org.nutz.dao.entity.annotation.Table;
import org.nutz.dao.entity.annotation.TableMeta;
/**
* 活动电子档案项目
*/
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
@Table("act_archive_project")
@TableMeta("{'mysql-charset':'utf8mb4'}")
@Comment("活动电子档案项目")
public class ArchiveProject extends BaseModel {
@Name
@PrevInsert(uu32 = true)
@ColDefine(type = ColType.VARCHAR, width = 32)
@Comment("主键")
private String id;
@Column
@Comment("年度")
private Integer year;
@Column
@ColDefine(type = ColType.VARCHAR, width = 300)
@Comment("项目名称")
private String projectName;
@Column
@Comment("项目状态")
private Integer status;
@Column
@ColDefine(type = ColType.VARCHAR, width = 32)
@Comment("所属工会ID")
private String unionId;
@Column
@ColDefine(type = ColType.VARCHAR, width = 150)
@Comment("所属工会名称")
private String unionName;
}
@@ -1,406 +0,0 @@
package com.budwk.app.zhgh.archive.service;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.sys.models.Sys_union;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.archive.constant.ArchiveConstant;
import com.budwk.app.zhgh.archive.model.ArchiveDocType;
import com.budwk.app.zhgh.archive.model.ArchiveDocument;
import com.budwk.app.zhgh.archive.model.ArchiveProject;
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;
import org.nutz.dao.sql.Sql;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Strings;
import org.nutz.lang.util.NutMap;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* 活动电子档案领域服务
*/
@IocBean(args = {"refer:dao"})
public class ArchiveService extends BaseServiceImpl<ArchiveProject> {
public ArchiveService(Dao dao) {
super(dao);
}
public Pagination<NutMap> pageDocTypes(PageForm pageForm, String name,
String projectStage, Boolean requiredFlag) {
Cnd cnd = Cnd.NEW();
cnd.and(Cnd.likeEX("adt.name", name));
cnd.andEX("adt.projectStage", "=", projectStage);
cnd.andEX("adt.requiredFlag", "=", requiredFlag);
cnd.and("adt.delFlag", "=", false);
cnd.asc("adt.projectStage").asc("adt.sortNo").asc("adt.createdAt");
Sql sql = Sqls.create("""
SELECT adt.*,
(SELECT COUNT(1)
FROM act_archive_document ad
WHERE ad.docTypeId = adt.id
AND ad.delFlag = 0) AS documentCount
FROM act_archive_doc_type adt
$condition
""");
sql.setCondition(cnd);
Pagination<NutMap> page = listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
page.getList().forEach(this::enrichDocType);
return page;
}
public List<NutMap> listDocTypes(String projectStage) {
Cnd cnd = Cnd.where("adt.delFlag", "=", false);
cnd.andEX("adt.projectStage", "=", projectStage);
cnd.asc("adt.projectStage").asc("adt.sortNo").asc("adt.createdAt");
Sql sql = Sqls.create("""
SELECT adt.*,
(SELECT COUNT(1)
FROM act_archive_document ad
WHERE ad.docTypeId = adt.id
AND ad.delFlag = 0) AS documentCount
FROM act_archive_doc_type adt
$condition
""");
sql.setCondition(cnd);
List<NutMap> list = listMap(sql);
list.forEach(this::enrichDocType);
return list;
}
@Aop(TransAop.READ_COMMITTED)
public void saveDocType(ArchiveDocType docType) {
if (docType == null || Strings.isBlank(docType.getName())) {
throw new IllegalArgumentException("类型名称不能为空");
}
if (!ArchiveConstant.validStage(docType.getProjectStage())) {
throw new IllegalArgumentException("项目阶段不正确");
}
docType.setName(docType.getName().trim());
if (docType.getSortNo() == null) {
docType.setSortNo(0);
}
if (docType.getRequiredFlag() == null) {
docType.setRequiredFlag(false);
}
Cnd duplicate = Cnd.where("projectStage", "=", docType.getProjectStage())
.and("name", "=", docType.getName())
.and("delFlag", "=", false);
if (Strings.isNotBlank(docType.getId())) {
duplicate.and("id", "<>", docType.getId());
}
if (dao().count(ArchiveDocType.class, duplicate) > 0) {
throw new IllegalArgumentException("同一项目阶段下类型名称不能重复");
}
if (Strings.isBlank(docType.getId())) {
dao().insert(docType);
} else {
ArchiveDocType old = dao().fetch(ArchiveDocType.class, docType.getId());
if (old == null) {
throw new IllegalArgumentException("文档类型不存在");
}
dao().updateIgnoreNull(docType);
}
}
@Aop(TransAop.READ_COMMITTED)
public void deleteDocType(String id) {
if (Strings.isBlank(id)) {
return;
}
if (dao().count(ArchiveDocument.class,
Cnd.where("docTypeId", "=", id).and("delFlag", "=", false)) > 0) {
throw new IllegalArgumentException("文档类型已被归档文档引用,不能删除");
}
dao().delete(ArchiveDocType.class, id);
}
public Pagination<NutMap> pageProjects(PageForm pageForm, Integer year,
String projectName, String unionId, Integer status) {
Cnd cnd = Cnd.NEW();
cnd.andEX("ap.year", "=", year);
cnd.and(Cnd.likeEX("ap.projectName", projectName));
cnd.andEX("ap.unionId", "=", unionId);
cnd.andEX("ap.status", "=", status);
cnd.and("ap.delFlag", "=", false);
cnd.desc("ap.year").desc("ap.createdAt");
Sql sql = Sqls.create("""
SELECT ap.*,
(SELECT COUNT(1)
FROM act_archive_document ad
WHERE ad.projectId = ap.id
AND ad.delFlag = 0) AS documentCount
FROM act_archive_project ap
$condition
""");
sql.setCondition(cnd);
Pagination<NutMap> page = listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
page.getList().forEach(this::enrichProject);
return page;
}
public List<NutMap> listProjects() {
Cnd cnd = Cnd.where("ap.delFlag", "=", false);
cnd.desc("ap.year").desc("ap.createdAt");
Sql sql = Sqls.create("""
SELECT ap.*,
(SELECT COUNT(1)
FROM act_archive_document ad
WHERE ad.projectId = ap.id
AND ad.delFlag = 0) AS documentCount
FROM act_archive_project ap
$condition
""");
sql.setCondition(cnd);
List<NutMap> list = listMap(sql);
list.forEach(this::enrichProject);
return list;
}
@Aop(TransAop.READ_COMMITTED)
public void saveProject(ArchiveProject project) {
if (project == null || project.getYear() == null) {
throw new IllegalArgumentException("年度不能为空");
}
if (Strings.isBlank(project.getProjectName())) {
throw new IllegalArgumentException("项目名称不能为空");
}
if (Strings.isBlank(project.getUnionId())) {
throw new IllegalArgumentException("所属工会不能为空");
}
Sys_union union = dao().fetch(Sys_union.class, project.getUnionId());
if (union == null) {
throw new IllegalArgumentException("所属工会不存在");
}
project.setProjectName(project.getProjectName().trim());
project.setUnionName(union.getName());
Cnd duplicate = Cnd.where("year", "=", project.getYear())
.and("projectName", "=", project.getProjectName())
.and("unionId", "=", project.getUnionId())
.and("delFlag", "=", false);
if (Strings.isNotBlank(project.getId())) {
duplicate.and("id", "<>", project.getId());
}
if (dao().count(ArchiveProject.class, duplicate) > 0) {
throw new IllegalArgumentException("同年度同工会下项目名称不能重复");
}
if (Strings.isBlank(project.getId())) {
project.setStatus(ArchiveConstant.PROJECT_DRAFT);
dao().insert(project);
} else {
ArchiveProject old = dao().fetch(ArchiveProject.class, project.getId());
if (old == null) {
throw new IllegalArgumentException("归档项目不存在");
}
project.setStatus(old.getStatus());
dao().updateIgnoreNull(project);
}
}
@Aop(TransAop.READ_COMMITTED)
public void deleteProject(String id) {
if (Strings.isBlank(id)) {
return;
}
if (dao().count(ArchiveDocument.class,
Cnd.where("projectId", "=", id).and("delFlag", "=", false)) > 0) {
throw new IllegalArgumentException("项目下已存在归档文档,不能删除");
}
dao().delete(ArchiveProject.class, id);
}
public Pagination<NutMap> pageDocuments(PageForm pageForm, String projectId, Integer year,
String projectStage, String docTypeId,
String documentName, Integer status) {
Cnd cnd = Cnd.NEW();
cnd.andEX("ad.projectId", "=", projectId);
cnd.andEX("ap.year", "=", year);
cnd.andEX("ad.projectStage", "=", projectStage);
cnd.andEX("ad.docTypeId", "=", docTypeId);
cnd.and(Cnd.likeEX("ad.documentName", documentName));
cnd.andEX("ad.status", "=", status);
cnd.and("ad.delFlag", "=", false);
cnd.and("ap.delFlag", "=", false);
cnd.desc("ap.year").desc("ad.uploadTime").desc("ad.createdAt");
Sql sql = Sqls.create("""
SELECT ad.*, ap.year, ap.projectName, ap.unionId, ap.unionName,
adt.name AS docTypeName, adt.requiredFlag AS docTypeRequiredFlag
FROM act_archive_document ad
JOIN act_archive_project ap
ON ap.id = ad.projectId
LEFT JOIN act_archive_doc_type adt
ON adt.id = ad.docTypeId
AND adt.delFlag = 0
$condition
""");
sql.setCondition(cnd);
Pagination<NutMap> page = listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
page.getList().forEach(this::enrichDocument);
return page;
}
public NutMap fetchDocument(String id) {
Sql sql = Sqls.create("""
SELECT ad.*, ap.year, ap.projectName, ap.unionId, ap.unionName,
adt.name AS docTypeName, adt.requiredFlag AS docTypeRequiredFlag
FROM act_archive_document ad
JOIN act_archive_project ap
ON ap.id = ad.projectId
LEFT JOIN act_archive_doc_type adt
ON adt.id = ad.docTypeId
AND adt.delFlag = 0
WHERE ad.id = @id
AND ad.delFlag = 0
AND ap.delFlag = 0
""");
sql.params().set("id", id);
NutMap map = fetchMap(sql);
if (map != null) {
enrichDocument(map);
}
return map;
}
@Aop(TransAop.READ_COMMITTED)
public void saveDocument(ArchiveDocument document, boolean submit) {
validateDocument(document, submit);
ArchiveDocument old = Strings.isBlank(document.getId())
? null : dao().fetch(ArchiveDocument.class, document.getId());
if (Strings.isNotBlank(document.getId()) && old == null) {
throw new IllegalArgumentException("归档文档不存在");
}
int targetStatus = submit ? ArchiveConstant.DOCUMENT_SUBMITTED : ArchiveConstant.DOCUMENT_DRAFT;
if (old != null && old.getStatus() != null
&& old.getStatus() == ArchiveConstant.DOCUMENT_SUBMITTED && !submit) {
targetStatus = ArchiveConstant.DOCUMENT_SUBMITTED;
}
document.setStatus(targetStatus);
if (targetStatus == ArchiveConstant.DOCUMENT_SUBMITTED) {
String uploader = SecurityUtil.getUserUsername();
if (Strings.isBlank(uploader)) {
uploader = SecurityUtil.getUserLoginname();
}
document.setUploader(uploader);
document.setUploadTime(System.currentTimeMillis());
} else if (old != null) {
document.setUploader(old.getUploader());
document.setUploadTime(old.getUploadTime());
}
String oldProjectId = old == null ? null : old.getProjectId();
if (old == null) {
dao().insert(document);
} else {
dao().updateIgnoreNull(document);
}
refreshProjectStatus(document.getProjectId());
if (Strings.isNotBlank(oldProjectId) && !oldProjectId.equals(document.getProjectId())) {
refreshProjectStatus(oldProjectId);
}
}
@Aop(TransAop.READ_COMMITTED)
public void deleteDocument(String id) {
ArchiveDocument document = dao().fetch(ArchiveDocument.class, id);
if (document == null) {
return;
}
dao().delete(ArchiveDocument.class, id);
refreshProjectStatus(document.getProjectId());
}
public List<Sys_union> listUnions() {
return dao().query(Sys_union.class, Cnd.where("delFlag", "=", false).asc("unionCode"));
}
private void validateDocument(ArchiveDocument document, boolean submit) {
if (document == null || Strings.isBlank(document.getProjectId())) {
throw new IllegalArgumentException("归档项目不能为空");
}
if (Strings.isBlank(document.getDocumentName())) {
throw new IllegalArgumentException("文档名称不能为空");
}
ArchiveProject project = dao().fetch(ArchiveProject.class,
Cnd.where("id", "=", document.getProjectId()).and("delFlag", "=", false));
if (project == null) {
throw new IllegalArgumentException("归档项目不存在");
}
if (!ArchiveConstant.validStage(document.getProjectStage())) {
throw new IllegalArgumentException("项目阶段不正确");
}
document.setDocumentName(document.getDocumentName().trim());
if (Strings.isNotBlank(document.getDocTypeId())) {
ArchiveDocType docType = dao().fetch(ArchiveDocType.class,
Cnd.where("id", "=", document.getDocTypeId()).and("delFlag", "=", false));
if (docType == null) {
throw new IllegalArgumentException("文档类型不存在");
}
if (!document.getProjectStage().equals(docType.getProjectStage())) {
throw new IllegalArgumentException("文档类型与项目阶段不匹配");
}
}
if (submit && (document.getFiles() == null || document.getFiles().isEmpty())) {
throw new IllegalArgumentException("提交归档前请先上传附件");
}
}
private void refreshProjectStatus(String projectId) {
if (Strings.isBlank(projectId)) {
return;
}
ArchiveProject project = dao().fetch(ArchiveProject.class, projectId);
if (project == null) {
return;
}
List<ArchiveDocument> submitted = dao().query(ArchiveDocument.class,
Cnd.where("projectId", "=", projectId)
.and("status", "=", ArchiveConstant.DOCUMENT_SUBMITTED)
.and("delFlag", "=", false));
int status = ArchiveConstant.PROJECT_DRAFT;
if (!submitted.isEmpty()) {
List<ArchiveDocType> requiredTypes = dao().query(ArchiveDocType.class,
Cnd.where("requiredFlag", "=", true).and("delFlag", "=", false));
if (requiredTypes.isEmpty()) {
status = ArchiveConstant.PROJECT_FILING;
} else {
Set<String> submittedKeys = new HashSet<>();
submitted.forEach(item -> {
if (Strings.isNotBlank(item.getDocTypeId())) {
submittedKeys.add(item.getProjectStage() + "_" + item.getDocTypeId());
}
});
boolean completed = requiredTypes.stream().allMatch(item ->
submittedKeys.contains(item.getProjectStage() + "_" + item.getId()));
status = completed ? ArchiveConstant.PROJECT_COMPLETED : ArchiveConstant.PROJECT_FILING;
}
}
dao().update(ArchiveProject.class, Chain.make("status", status),
Cnd.where("id", "=", projectId));
}
private void enrichDocType(NutMap map) {
map.put("projectStageLabel", ArchiveConstant.stageLabel(map.getString("projectStage")));
}
private void enrichProject(NutMap map) {
map.put("statusLabel", ArchiveConstant.projectStatusLabel(map.getInt("status")));
}
private void enrichDocument(NutMap map) {
map.put("projectStageLabel", ArchiveConstant.stageLabel(map.getString("projectStage")));
map.put("statusLabel", ArchiveConstant.documentStatusLabel(map.getInt("status")));
Object files = map.get("files");
if (files == null || StrUtil.isBlank(String.valueOf(files))) {
map.put("files", Collections.emptyList());
}
}
}
@@ -76,7 +76,8 @@ public class ClubExamineApplyController {
if(year == null) {
return Result.error("年份信息为空,请核查");
}
List<SysClubExamineRegister> list = dao.query(SysClubExamineRegister.class, Cnd.where("clubId", "=", clubId).and("year(registerDate)", "=", year).andEX("id", "!=", id));
// 同一社团的年度重复申请校验以登记年度为准不能按申请时间判断
List<SysClubExamineRegister> list = dao.query(SysClubExamineRegister.class, Cnd.where("clubId", "=", clubId).and("year", "=", year).andEX("id", "!=", id));
List<ProcessInstance> instanceList = dao.query(
ProcessInstance.class,
Cnd.where(ProcessInstance::getBusinessNo, "in", list.stream().map(SysClubExamineRegister::getId).toList())
@@ -146,8 +147,9 @@ public class ClubExamineApplyController {
if(StrUtil.isBlank(clubId)) {
return Result.error("社团信息为空,请核查");
}
// 上年度结余按登记年度查询避免申请时间跨年导致取错数据
SysClubExamineRegister register = dao.fetch(SysClubExamineRegister.class, Cnd.where("clubId", "=", clubId)
.and("YEAR(registerDate)", "=", DateUtil.thisYear() - 1));
.and("year", "=", DateUtil.thisYear() - 1));
if (Lang.isNotEmpty(register)) {
List<JSONObject> list = register.getIncomeCensus();
float surplus = list.get(0).getFloat("surplus");
@@ -87,7 +87,8 @@ public class ClubExamineMineController {
nutMap.setv("clubName", register.getClubName());
nutMap.setv("create_time", register.getFoundTime());
nutMap.setv("dues_standard", register.getDue());
nutMap.setv("year", register.getRegisterDate().substring(0, 4));
// 生成材料中的年度与列表展示的登记年度保持一致
nutMap.setv("year", register.getYear());
nutMap.setv("registerDate", register.getRegisterDate());
nutMap.setv("incomeCensus", incomeCensus);
nutMap.setv("jgUser", jgUser);
@@ -90,10 +90,12 @@ public class ClubAuditManagerController {
cnd.andEX("year(info.creatTime)", "=", pageForm.getYear());
cnd.and("t.taskName", "=", "ef81777f-22fb-4fe4-9800-909e6c681210");
cnd.and("ta.actorId", "in", List.of(SecurityUtil.getUserId()));
if (approval) {
// 已审核是全量历史不能因历史工作流参与人不同而遗漏参考库迁移记录
cnd.and("t.taskState", "in", List.of(ProcessTaskStateEnum.FINISHED.getCode(), ProcessTaskStateEnum.INTERRUPT.getCode()));
} else {
// 未审核只展示当前登录人实际可以处理的待办任务
cnd.and("ta.actorId", "in", List.of(SecurityUtil.getUserId()));
cnd.and("t.taskState", "=", ProcessTaskStateEnum.DOING.getCode());
}
@@ -200,6 +200,7 @@ public class ClubInfoManageController {
@SLog(tag = "社团管理系统-信息管理", msg = "修改身份")
public Result updateRoleCode(@Param("id") String id, @Valid String[] roleCodes, @Valid String clubId) {
List<String> roleCodeList = Arrays.asList(roleCodes);
ClubUser clubUser = clubInfoManageService.dao().fetch(ClubUser.class, id);
//查询社团是否存在会长或者秘书长
if(Arrays.asList(roleCodes).contains(RoleConstant.CLUB_PRESIDENT.name())) {
int count = dao.count(ClubUser.class, Cnd.where("clubId", "=", clubId)
@@ -210,14 +211,14 @@ public class ClubInfoManageController {
}
if(Arrays.asList(roleCodes).contains(RoleConstant.CLUB_SECRETARY.name())) {
int count = dao.count(ClubUser.class, Cnd.where("clubId", "=", clubId)
.and("id", "!=", id)
.and(new Static("JSON_CONTAINS(roleCode, '\"%s\"')".formatted(RoleConstant.CLUB_SECRETARY.name()))));
if (count > 0) {
// return Result.error("秘书长只能有一位");
return Result.error("秘书长只能有一位");
}
}
ClubUser clubUser = clubInfoManageService.dao().fetch(ClubUser.class, id);
// 先清除所有的角色
dao.clear(Sys_user_role.class, Cnd.where("userId", "=", clubUser.getUserId()).and("clubId", "=", clubUser.getClubId()));
// 再根据传过来的赋值
@@ -2,10 +2,14 @@ package com.budwk.app.zhgh.club.controller.infoManage;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.json.JSONObject;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.result.Result;
import com.budwk.app.base.utils.PageUtil;
import com.budwk.app.flow.enums.ProcessTaskStateEnum;
import com.budwk.app.sys.models.Sys_file;
import com.budwk.app.sys.utils.SysFileMinIoUtil;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.club.param.ClubUserPageForm;
import com.budwk.app.zhgh.club.service.SysClubInfoManageService;
@@ -14,12 +18,24 @@ import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.json.Json;
import org.nutz.lang.util.NutMap;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
/**
* @Author: JyuHsin
@@ -32,6 +48,8 @@ import java.util.List;
@At("/platform/club/infoManage/schoolAuditReport")
public class ClubSchoolAuditReportController {
private static final Logger log = LoggerFactory.getLogger(ClubSchoolAuditReportController.class);
@Inject
private SysClubInfoManageService clubInfoManageService;
@@ -44,6 +62,71 @@ public class ClubSchoolAuditReportController {
@SaCheckPermission("club.infoManage.schoolAuditReport")
public Result pageData(@Valid ClubUserPageForm pageForm,
@Param(value = "approval") Boolean approval) {
Sql sql = buildPageDataSql(pageForm, approval);
Pagination pagination = clubInfoManageService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
return Result.success(pagination);
}
/**
* 导出当前筛选范围内的换届报告附件压缩包
*
* @param pageForm 页面查询条件
* @param approval 审核状态与列表页保持一致
* @param response HTTP 下载响应
*/
@At
@Ok("void")
@SaCheckPermission("club.infoManage.schoolAuditReport")
public void exportRefreshReportZip(@Valid ClubUserPageForm pageForm,
@Param(value = "approval") Boolean approval,
HttpServletResponse response) throws IOException {
List<NutMap> reportList = clubInfoManageService.listMap(buildPageDataSql(pageForm, approval));
response.setContentType("application/zip");
response.setHeader("Content-Disposition", "attachment;filename=" + URLUtil.encode("换届报告.zip"));
// 显式指定 UTF-8确保压缩包内中文目录和文件名正常显示
try (ZipOutputStream zipOutputStream = new ZipOutputStream(new BufferedOutputStream(response.getOutputStream()), StandardCharsets.UTF_8)) {
Set<String> entryNames = new HashSet<>();
for (NutMap report : reportList) {
String filesJson = report.getString("files");
if (StrUtil.isBlank(filesJson)) {
continue;
}
List<JSONObject> files = Json.fromJsonAsList(JSONObject.class, filesJson);
String folderName = buildFolderName(report);
for (int index = 0; index < files.size(); index++) {
JSONObject fileInfo = files.get(index);
Sys_file sysFile = findSysFile(fileInfo);
if (sysFile == null) {
log.warn("换届报告附件不存在,报告ID:{},附件:{}", report.getString("id"), fileInfo);
continue;
}
byte[] fileBytes = SysFileMinIoUtil.getFileBytes(sysFile.getBucket(), sysFile.getStoragePath());
if (fileBytes == null || fileBytes.length == 0) {
log.warn("换届报告附件内容为空,报告ID:{},附件ID:{}", report.getString("id"), sysFile.getId());
continue;
}
String fileName = StrUtil.blankToDefault(fileInfo.getStr("name"), fileInfo.getStr("filename"));
if (StrUtil.isBlank(fileName)) {
fileName = sysFile.getName();
}
String entryName = buildUniqueEntryName(folderName + "/" + sanitizeFileName(fileName), entryNames);
zipOutputStream.putNextEntry(new ZipEntry(entryName));
zipOutputStream.write(fileBytes);
zipOutputStream.closeEntry();
}
}
}
}
/**
* 构建列表与导出共用的换届报告查询确保查什么导什么
*
* @param pageForm 页面查询条件
* @param approval 审核状态
* @return 已绑定筛选条件的查询对象
*/
private Sql buildPageDataSql(ClubUserPageForm pageForm, Boolean approval) {
Sql sql = Sqls.create("""
SELECT
info.*,
@@ -79,7 +162,7 @@ public class ClubSchoolAuditReportController {
cnd.and("t.taskName", "=", "d4323546-8d09-419e-88d4-7b15862ca29d");
cnd.and("ta.actorId", "in", List.of(SecurityUtil.getUserId()));
if (approval) {
if (Boolean.TRUE.equals(approval)) {
cnd.and("t.taskState", "in", List.of(ProcessTaskStateEnum.FINISHED.getCode(), ProcessTaskStateEnum.INTERRUPT.getCode()));
} else {
cnd.and("t.taskState", "=", ProcessTaskStateEnum.DOING.getCode());
@@ -96,8 +179,67 @@ public class ClubSchoolAuditReportController {
cnd.groupBy("t.id");
sql.setCondition(cnd);
return sql;
}
Pagination pagination = clubInfoManageService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
return Result.success(pagination);
/**
* 兼容新旧附件 JSON 结构按附件 ID 或下载地址查找文件元数据
*
* @param fileInfo 附件 JSON 信息
* @return 系统文件记录未找到时返回 null
*/
private Sys_file findSysFile(JSONObject fileInfo) {
String fileId = fileInfo.getStr("id");
if (StrUtil.isNotBlank(fileId)) {
return clubInfoManageService.dao().fetch(Sys_file.class, fileId);
}
String fileUrl = fileInfo.getStr("url");
if (StrUtil.isBlank(fileUrl)) {
return null;
}
return clubInfoManageService.dao().fetch(Sys_file.class, Cnd.where(Sys_file::getDownloadPath, "=", fileUrl));
}
/**
* 使用社团名称建立目录方便按换届报告来源查阅
*
* @param report 换届报告记录
* @return Zip 内目录名称
*/
private String buildFolderName(NutMap report) {
return sanitizeFileName(report.getString("clubName"));
}
/**
* 保留附件原始文件名同一社团目录存在同名文件时追加序号避免覆盖
*
* @param entryName 原始 Zip 条目名称
* @param entryNames 已写入的 Zip 条目名称
* @return 唯一的 Zip 条目名称
*/
private String buildUniqueEntryName(String entryName, Set<String> entryNames) {
if (entryNames.add(entryName)) {
return entryName;
}
int extensionIndex = entryName.lastIndexOf('.');
String name = extensionIndex > entryName.lastIndexOf('/') ? entryName.substring(0, extensionIndex) : entryName;
String extension = extensionIndex > entryName.lastIndexOf('/') ? entryName.substring(extensionIndex) : "";
int index = 2;
String uniqueEntryName;
do {
uniqueEntryName = name + "(" + index + ")" + extension;
index++;
} while (!entryNames.add(uniqueEntryName));
return uniqueEntryName;
}
/**
* 去除 Zip 条目路径分隔符避免附件名影响压缩包目录结构
*
* @param fileName 原始文件名
* @return 可安全写入 Zip 的文件名
*/
private String sanitizeFileName(String fileName) {
return StrUtil.blankToDefault(fileName, "未命名文件").replace("/", "_").replace("\\", "_");
}
}
@@ -95,6 +95,14 @@ public class SysClubExamineRegister extends BaseModel {
@ColDefine(type = ColType.MYSQL_JSON)
private List<JSONObject> yearActivityList;
/**
* 旧版年审表保存的年度活动明细保留该字段用于查看迁移历史数据
*/
@Column
@Comment("旧版年度活动明细")
@ColDefine(type = ColType.MYSQL_JSON)
private List<JSONObject> yearActivitys;
@Column
@Comment("财务收支情况统计json")
@ColDefine(type = ColType.MYSQL_JSON)
@@ -24,6 +24,10 @@ public class ClubUserPageForm extends PageForm {
private Integer auditType;
private Integer source;
private Integer year;
/** 社团评优申报开始年度筛选条件。 */
private String applyStartYear;
/** 社团评优申报结束年度筛选条件。 */
private String applyEndYear;
private Boolean giveMoney;
private Integer radioType;
}
@@ -71,7 +71,9 @@ public class SysClubEvaluateServiceImpl extends BaseServiceImpl<SysClubEvaluate>
Cnd cnd = Cnd.NEW();
cnd.andEX("info.clubId", "=", pageForm.getClubId());
cnd.andEX("year(info.applyTime)", "=", pageForm.getYear());
// 评优年度以申报填写的起止年度为准不能使用申请提交时间所在年份代替
cnd.andEX("info.applyStartYear", "=", pageForm.getApplyStartYear());
cnd.andEX("info.applyEndYear", "=", pageForm.getApplyEndYear());
cnd.and("info.userId", "=", SecurityUtil.getUserId());
cnd.groupBy("info.id");
@@ -144,7 +146,9 @@ public class SysClubEvaluateServiceImpl extends BaseServiceImpl<SysClubEvaluate>
""");
cnd.andEX("ce.clubId", "=", pageForm.getClubId());
cnd.andEX("year(ce.applyTime)", "=", pageForm.getYear());
// 审核列表与参考项目保持一致分别按申报开始年度和申报结束年度筛选
cnd.andEX("ce.applyStartYear", "=", pageForm.getApplyStartYear());
cnd.andEX("ce.applyEndYear", "=", pageForm.getApplyEndYear());
if (pageForm.getAudit()) {
cnd.and("t.taskState", "in", List.of(ProcessTaskStateEnum.FINISHED.getCode(), ProcessTaskStateEnum.INTERRUPT.getCode()));
@@ -8,6 +8,7 @@ import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.base.utils.PageUtil;
import com.budwk.app.flow.entity.ProcessInstance;
import com.budwk.app.flow.enums.ProcessInstanceStateEnum;
import com.budwk.app.flow.enums.ProcessTaskStateEnum;
import com.budwk.app.web.commons.auth.utils.AuthUtil;
@@ -33,8 +34,10 @@ import org.nutz.lang.util.NutMap;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
@IocBean(args = {"refer:dao"})
@@ -119,6 +122,7 @@ public class SysClubExamineServiceImpl extends BaseServiceImpl<SysClubExamineReg
Sql applySql = Sqls.create("""
SELECT
cu.userId,
cu.id,
cu.mode,
cu.applyDate,
cu.joinTime,
@@ -129,17 +133,25 @@ public class SysClubExamineServiceImpl extends BaseServiceImpl<SysClubExamineReg
LEFT JOIN sys_user u ON u.id = cu.userId
WHERE cu.clubId = @clubId
AND COALESCE(cu.delFlag, 0) = 0
AND EXISTS (
SELECT 1
FROM wf_process_instance ins
WHERE ins.businessNo = cu.id
AND ins.state = @finishedState
)
ORDER BY cu.applyDate, cu.mode
""")
.setParam("clubId", clubId)
.setParam("finishedState", ProcessInstanceStateEnum.FINISHED.getCode());
.setParam("clubId", clubId);
List<NutMap> applications = listMap(applySql);
if (!applications.isEmpty()) {
// 流程状态改为按当前社团申请批量查询避免相关子查询对流程实例表重复扫描
List<String> applicationIds = applications.stream()
.map(application -> application.getString("id"))
.filter(StrUtil::isNotBlank)
.collect(Collectors.toList());
List<ProcessInstance> finishedInstances = applicationIds.isEmpty() ? new ArrayList<>()
: dao().query(ProcessInstance.class, Cnd.where(ProcessInstance::getBusinessNo, "in", applicationIds)
.and(ProcessInstance::getState, "=", ProcessInstanceStateEnum.FINISHED.getCode()));
Set<String> finishedBusinessNos = finishedInstances.stream()
.map(ProcessInstance::getBusinessNo)
.filter(StrUtil::isNotBlank)
.collect(Collectors.toCollection(HashSet::new));
applications.removeIf(application -> !finishedBusinessNos.contains(application.getString("id")));
}
applications.sort(Comparator.comparing(row -> row.getTime("applyDate"), Comparator.nullsLast(Date::compareTo)));
for (NutMap application : applications) {
@@ -259,6 +271,10 @@ public class SysClubExamineServiceImpl extends BaseServiceImpl<SysClubExamineReg
}
private String normalizeUserState(String userState) {
// 历史成员数据可能未同步人员状态空值不参与在职退休人数统计
if (StrUtil.isBlank(userState)) {
return null;
}
if (List.of("在职", "在岗").contains(userState)) {
return "在职";
}
@@ -291,7 +307,7 @@ public class SysClubExamineServiceImpl extends BaseServiceImpl<SysClubExamineReg
@Override
public List<NutMap> getJgUser(String clubId) {
// 构建社团管理人员查询SQL排除普通成员按职务排序
// 构建社团员查询SQL理事机构成员优先展示普通成员随后展示
Sql sql = Sqls.create("""
SELECT
cl.*,
@@ -304,7 +320,6 @@ public class SysClubExamineServiceImpl extends BaseServiceImpl<SysClubExamineReg
LEFT JOIN `vw_user` u ON cl.userId = u.id
WHERE
cl.clubId = @clubId
AND NOT JSON_CONTAINS(cl.roleCode, '"CLUB_MEMBER"')
ORDER BY
CASE
WHEN JSON_CONTAINS(cl.roleCode, '"CLUB_PRESIDENT"') THEN 1
@@ -477,13 +492,154 @@ public class SysClubExamineServiceImpl extends BaseServiceImpl<SysClubExamineReg
// 查询主表数据并关联明细数据
ClubExamineVo clubExamineVo = fetchVO(sql, ClubExamineVo.class);
if (Lang.isNotEmpty(clubExamineVo)) {
// 详情页统一使用V4字段兼容尚未改写JSON内容的V3年审记录
normalizeActivityData(clubExamineVo);
List<SysClubExamineRegisterDetailed> detailedList = dao().query(SysClubExamineRegisterDetailed.class,
Cnd.where("registerId", "=", id).asc("location"));
clubExamineVo.setDetailedList(detailedList);
// V3年审审核记录已迁入audit表作为V4工作流历史为空时的详情回显数据
Sql legacyAuditSql = Sqls.create("""
SELECT
a.id,
a.username AS userName,
a.loginname AS loginName,
a.auditTime,
a.auditPass,
a.auditOpinion,
a.auditSign,
CASE
WHEN a.id = scer.clubAuditId THEN '社团审核'
WHEN a.id = scer.clubLeaderAuditId THEN '会长审核'
WHEN a.id = scer.schoolAuditId THEN '校工会审核'
END AS auditName
FROM
sys_club_examine_register scer
INNER JOIN audit a ON a.id IN (scer.clubAuditId, scer.clubLeaderAuditId, scer.schoolAuditId)
WHERE
scer.id = @id
ORDER BY
CASE
WHEN a.id = scer.clubAuditId THEN 1
WHEN a.id = scer.clubLeaderAuditId THEN 2
WHEN a.id = scer.schoolAuditId THEN 3
ELSE 99
END
""").setParam("id", id);
clubExamineVo.setLegacyAuditRecords(listMap(legacyAuditSql));
}
return clubExamineVo;
}
/**
* 将旧版年审活动和计划字段转换为查看页统一使用的V4字段避免历史数据在页面中显示为空
*
* @param examineVo 年审详情
*/
private void normalizeActivityData(ClubExamineVo examineVo) {
examineVo.setSummaryFiles(normalizeSummaryFiles(examineVo.getSummaryFiles()));
examineVo.setChangeUserNum(normalizeChangeUserNum(examineVo.getChangeUserNum()));
List<JSONObject> yearActivitySource = examineVo.getYearActivityList();
if (yearActivitySource == null || yearActivitySource.isEmpty()) {
yearActivitySource = examineVo.getYearActivitys();
}
examineVo.setYearActivityList(yearActivitySource == null ? new ArrayList<>()
: yearActivitySource.stream()
.map(item -> normalizeActivityItem(item, true))
.collect(Collectors.toList()));
List<JSONObject> planSource = examineVo.getPlans();
examineVo.setPlans(planSource == null ? new ArrayList<>()
: planSource.stream()
.map(item -> normalizeActivityItem(item, false))
.collect(Collectors.toList()));
}
/**
* 将V3年审记录中的年度减少字段转换为查看页统一使用的V4字段
* 历史年审必须展示申请时保存的数据不能按当前会员状态重新统计
*
* @param changeUserNum 年审记录保存的成员变化数据
* @return 兼容V3V4字段的成员变化数据
*/
private List<JSONObject> normalizeChangeUserNum(List<JSONObject> changeUserNum) {
if (changeUserNum == null || changeUserNum.isEmpty()) {
return new ArrayList<>();
}
return changeUserNum.stream()
.filter(Objects::nonNull)
.peek(item -> {
if (!item.containsKey("yearReduceNum") && item.containsKey("yearEditNum")) {
item.set("yearReduceNum", item.get("yearEditNum"));
}
})
.collect(Collectors.toList());
}
/**
* 将V3工作总结附件的文件主键包装为V4文件预览组件所需的response.data格式
*
* @param summaryFiles 原始工作总结附件
* @return 可供文件预览组件读取的附件数据
*/
private List<JSONObject> normalizeSummaryFiles(List<JSONObject> summaryFiles) {
if (summaryFiles == null || summaryFiles.isEmpty()) {
return new ArrayList<>();
}
return summaryFiles.stream()
.filter(Objects::nonNull)
.map(file -> {
JSONObject response = file.getJSONObject("response");
if (response != null && StrUtil.isNotBlank(response.getStr("data"))) {
return file;
}
String fileId = file.getStr("id");
if (StrUtil.isBlank(fileId)) {
return null;
}
JSONObject normalizedFile = new JSONObject();
normalizedFile.set("response", new JSONObject().set("data", fileId));
return normalizedFile;
})
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
/**
* 兼容V3活动字段hdsjmczbdwhdddcjrshjqk和V4活动字段
*
* @param source 原始活动或计划数据
* @param includeResult 是否包含本年度活动的参加人数和获奖情况
* @return 供查看页使用的统一字段数据
*/
private JSONObject normalizeActivityItem(JSONObject source, boolean includeResult) {
JSONObject item = new JSONObject();
item.set("activityName", getActivityValue(source, "activityName", "mc"));
item.set("activityUnit", getActivityValue(source, "activityUnit", "zbdw"));
item.set("activityAddress", getActivityValue(source, "activityAddress", "hddd"));
if (includeResult) {
item.set("activityTime", getActivityValue(source, "activityTime", "hdsj"));
item.set("joinNum", getActivityValue(source, "joinNum", "cjrs"));
item.set("prize", getActivityValue(source, "prize", "hjqk"));
}
return item;
}
/**
* 优先读取V4字段历史记录没有该字段时回退读取对应的V3字段
*
* @param source 原始JSON数据
* @param currentField V4字段名
* @param legacyField V3字段名
* @return 兼容后的字段值
*/
private String getActivityValue(JSONObject source, String currentField, String legacyField) {
if (source == null) {
return null;
}
String currentValue = source.getStr(currentField);
return StrUtil.isNotBlank(currentValue) ? currentValue : source.getStr(legacyField);
}
@Override
public List<NutMap> getYearActivityExportData(String id) {
ClubExamineVo examineVo = this.findOne(id);
@@ -591,7 +747,8 @@ public class SysClubExamineServiceImpl extends BaseServiceImpl<SysClubExamineReg
*/
private void buildPageBaseCondition(Cnd cnd, ClubUserPageForm pageForm, String tableAlias) {
if (pageForm.getYear() != null) {
cnd.andEX("YEAR(" + tableAlias + ".registerDate)", "=", pageForm.getYear());
// 年度查询应与列表展示的登记年度保持一致不能使用申请时间推算年度
cnd.andEX(tableAlias + ".year", "=", pageForm.getYear());
}
if (StrUtil.isNotBlank(pageForm.getClubId())) {
cnd.andEX(tableAlias + ".clubId", "=", pageForm.getClubId());
@@ -86,7 +86,7 @@ public class SysClubInfoManageServiceImpl extends BaseServiceImpl<ClubCommonPage
scu.clubId = c.id
) currentNum,
GROUP_CONCAT(DISTINCT presidentUser.username) AS clubLeader,
GROUP_CONCAT(secretaryUser.username) AS clubSecretary
GROUP_CONCAT(DISTINCT secretaryUser.username) AS clubSecretary
FROM
sys_club c
LEFT JOIN club_user presidentCu on presidentCu.clubId = c.id AND JSON_CONTAINS(presidentCu.roleCode, '"CLUB_PRESIDENT"')
@@ -229,23 +229,19 @@ public class SysClubInfoManageServiceImpl extends BaseServiceImpl<ClubCommonPage
public Pagination<ClubUserCommonPageVo> exitManagePageData(ClubUserPageForm pageForm) {
Sql sql = Sqls.create("""
SELECT
COALESCE(info.id, scu.id) AS id,
scu.clubId,
scu.userId,
COALESCE(info.roleCode, scu.roleCode) AS applyRoleCode,
COALESCE(info.clubPosition, scu.position) AS clubPosition,
COALESCE(info.email, scu.email, u.email) AS email,
COALESCE(info.mobile, u.mobile) AS mobile,
COALESCE(info.birthday, u.birthday) AS birthday,
COALESCE(info.avatar, scu.avatar, u.avatar) AS avatar,
COALESCE(info.sameTimeJoinOtherClubSituation, scu.sameTimeJoinOtherClubSituation) AS sameTimeJoinOtherClubSituation,
COALESCE(info.awardsExperience, scu.awardsExperience) AS awardsExperience,
info.signature,
COALESCE(
info.applyDate,
STR_TO_DATE(scu.changeTime, '%Y-%m-%d %H:%i:%s'),
STR_TO_DATE(JSON_UNQUOTE(JSON_EXTRACT(scu.exitAuditList, '$[0].auditTime')), '%Y-%m-%d %H:%i:%s')
) AS applyDate,
exitInfo.id,
exitInfo.clubId,
exitInfo.userId,
exitInfo.applyRoleCode,
exitInfo.clubPosition,
COALESCE(exitInfo.email, u.email) AS email,
COALESCE(exitInfo.mobile, u.mobile) AS mobile,
COALESCE(exitInfo.birthday, u.birthday) AS birthday,
COALESCE(exitInfo.avatar, u.avatar) AS avatar,
exitInfo.sameTimeJoinOtherClubSituation,
exitInfo.awardsExperience,
exitInfo.signature,
exitInfo.applyDate,
u.username AS userName,
u.loginname AS loginName,
u.sex,
@@ -253,32 +249,14 @@ public class SysClubInfoManageServiceImpl extends BaseServiceImpl<ClubCommonPage
u.userState,
club.clubName,
u.unitname AS unitName,
COALESCE(scu.joinTime, DATE_FORMAT(club.foundTime, '%Y-%m-%d %H:%i:%s')) AS joinTime,
COALESCE(
DATE_FORMAT(info.exitTime, '%Y-%m-%d %H:%i:%s'),
JSON_UNQUOTE(JSON_EXTRACT(scu.exitAuditList, '$[0].auditTime')),
scu.changeTime
) AS exitTime,
IF(info.id IS NULL, 'DIRECT_REMOVE', 'AUDIT_EXIT') AS exitType,
ins.id AS instanceId,
ins.businessNo,
ins.state instanceState,
ins.variable instanceVariable,
ins.processDefineId instanceProcessDefineId
FROM
sys_club_user scu
LEFT JOIN club_user_apply info ON info.clubId = scu.clubId
AND info.userId = scu.userId
AND info.mode = false
LEFT JOIN sys_club club ON club.id = scu.clubId
INNER JOIN vw_user u ON u.id = scu.userId
LEFT JOIN wf_process_instance ins ON ins.businessNo = info.id
AND ins.state = 20
DATE_FORMAT(COALESCE(exitInfo.joinTime, STR_TO_DATE(club.foundTime, '%Y-%m-%d')), '%Y-%m-%d %H:%i:%s') AS joinTime,
DATE_FORMAT(exitInfo.exitTime, '%Y-%m-%d %H:%i:%s') AS exitTime,
exitInfo.exitType
""" + getExitManageBaseSql() + """
$condition
""");
Cnd cnd = Cnd.NEW();
cnd.and("scu.isNormal", "=", false);
cnd.andEX("scu.clubId", "=", pageForm.getClubId());
cnd.andEX("exitInfo.clubId", "=", pageForm.getClubId());
cnd.andEX("u.personType", "=", pageForm.getPersonType());
cnd.andEX("u.userState", "=", pageForm.getUserState());
if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) {
@@ -287,9 +265,9 @@ public class SysClubInfoManageServiceImpl extends BaseServiceImpl<ClubCommonPage
if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name(), RoleConstant.SCHOOL_UNION_CLUB_ADMIN.name())) {
List<String> clubIdList = commonService.findUserRoleByRoleCode(List.of(RoleConstant.CLUB_PRESIDENT.name(), RoleConstant.CLUB_VICE_PRESIDENT.name(), RoleConstant.CLUB_SECRETARY.name(), RoleConstant.CLUB_VICE_SECRETARY.name(), RoleConstant.CLUB_OPERATOR.name()));
List<Sys_user_role> clubList = dao().query(Sys_user_role.class, Cnd.where("roleId", "in", clubIdList).and("userId", "=", SecurityUtil.getUserId()).and("clubId", "is not", null));
cnd.and("scu.clubId", "in", clubList.stream().map(Sys_user_role::getClubId).toList());
cnd.and("exitInfo.clubId", "in", clubList.stream().map(Sys_user_role::getClubId).toList());
}
cnd.groupBy("scu.id");
cnd.groupBy("exitInfo.id");
cnd.desc("exitTime");
sql.setCondition(cnd);
Pagination<ClubUserCommonPageVo> pagination = listPageVO(pageForm, sql, ClubUserCommonPageVo.class);
@@ -307,55 +285,110 @@ public class SysClubInfoManageServiceImpl extends BaseServiceImpl<ClubCommonPage
public ClubUserJoinVo exitManageInfo(String id) {
Sql sql = Sqls.create("""
SELECT
COALESCE(cua.id, scu.id) AS id,
scu.clubId,
scu.userId,
COALESCE(cua.roleCode, scu.roleCode) AS roleCode,
COALESCE(cua.clubPosition, scu.position) AS clubPosition,
COALESCE(cua.email, scu.email, u.email) AS email,
COALESCE(cua.mobile, u.mobile) AS mobile,
COALESCE(cua.birthday, u.birthday) AS birthday,
COALESCE(cua.avatar, scu.avatar, u.avatar) AS avatar,
COALESCE(cua.sameTimeJoinOtherClubSituation, scu.sameTimeJoinOtherClubSituation) AS sameTimeJoinOtherClubSituation,
COALESCE(cua.awardsExperience, scu.awardsExperience) AS awardsExperience,
cua.signature,
COALESCE(
cua.applyDate,
STR_TO_DATE(scu.changeTime, '%Y-%m-%d %H:%i:%s'),
STR_TO_DATE(JSON_UNQUOTE(JSON_EXTRACT(scu.exitAuditList, '$[0].auditTime')), '%Y-%m-%d %H:%i:%s')
) AS applyDate,
STR_TO_DATE(scu.joinTime, '%Y-%m-%d %H:%i:%s') AS joinTime,
COALESCE(
cua.exitTime,
STR_TO_DATE(JSON_UNQUOTE(JSON_EXTRACT(scu.exitAuditList, '$[0].auditTime')), '%Y-%m-%d %H:%i:%s'),
STR_TO_DATE(scu.changeTime, '%Y-%m-%d %H:%i:%s')
) AS exitTime,
IF(cua.id IS NULL, 'DIRECT_REMOVE', 'AUDIT_EXIT') AS exitType,
exitInfo.id,
exitInfo.clubId,
exitInfo.userId,
exitInfo.applyRoleCode AS roleCode,
exitInfo.clubPosition,
COALESCE(exitInfo.email, u.email) AS email,
COALESCE(exitInfo.mobile, u.mobile) AS mobile,
COALESCE(exitInfo.birthday, u.birthday) AS birthday,
COALESCE(exitInfo.avatar, u.avatar) AS avatar,
exitInfo.sameTimeJoinOtherClubSituation,
exitInfo.awardsExperience,
exitInfo.signature,
exitInfo.applyDate,
COALESCE(exitInfo.joinTime, STR_TO_DATE(club.foundTime, '%Y-%m-%d')) AS joinTime,
exitInfo.exitTime,
exitInfo.exitType,
club.clubName,
u.loginname AS loginName,
u.username AS userName,
u.sex,
u.mobile,
u.unitName,
u.unionName,
u.technicalTitle,
u.education,
u.academicDegree,
u.position
FROM
sys_club_user scu
LEFT JOIN club_user_apply cua ON cua.clubId = scu.clubId
AND cua.userId = scu.userId
AND cua.mode = false
LEFT JOIN vw_user u ON u.id = scu.userId
LEFT JOIN sys_club club ON club.id = scu.clubId
WHERE scu.isNormal = false
AND COALESCE(cua.id, scu.id) = @id
""" + getExitManageBaseSql() + """
WHERE exitInfo.id = @id
""");
sql.setParam("id", id);
return fetchVO(sql, ClubUserJoinVo.class);
}
/**
* 退会申请审批完成后会删除club_user成员关系因此退会管理以退会申请记录为主数据源
* 同时保留旧sys_club_user表中已退会的数据避免历史直接移出记录无法查询
*
* @return 退会管理列表和详情共用的数据来源SQL
*/
private String getExitManageBaseSql() {
return """
FROM (
SELECT
cua.id,
cua.clubId,
cua.userId,
cua.roleCode AS applyRoleCode,
cua.clubPosition,
cua.email,
cua.mobile,
cua.birthday,
cua.avatar,
cua.sameTimeJoinOtherClubSituation,
cua.awardsExperience,
cua.signature,
cua.applyDate,
cua.joinTime,
cua.exitTime,
'AUDIT_EXIT' AS exitType
FROM
club_user_apply cua
INNER JOIN wf_process_instance ins ON ins.businessNo = cua.id AND ins.state = 20
WHERE cua.mode = false
UNION ALL
SELECT
scu.id,
scu.clubId,
scu.userId,
scu.roleCode AS applyRoleCode,
scu.position AS clubPosition,
scu.email,
NULL AS mobile,
NULL AS birthday,
scu.avatar,
scu.sameTimeJoinOtherClubSituation,
scu.awardsExperience,
NULL AS signature,
COALESCE(
STR_TO_DATE(scu.changeTime, '%Y-%m-%d %H:%i:%s'),
STR_TO_DATE(JSON_UNQUOTE(JSON_EXTRACT(scu.exitAuditList, '$[0].auditTime')), '%Y-%m-%d %H:%i:%s')
) AS applyDate,
STR_TO_DATE(scu.joinTime, '%Y-%m-%d %H:%i:%s') AS joinTime,
COALESCE(
STR_TO_DATE(JSON_UNQUOTE(JSON_EXTRACT(scu.exitAuditList, '$[0].auditTime')), '%Y-%m-%d %H:%i:%s'),
STR_TO_DATE(scu.changeTime, '%Y-%m-%d %H:%i:%s')
) AS exitTime,
'DIRECT_REMOVE' AS exitType
FROM
sys_club_user scu
WHERE scu.isNormal = false
AND NOT EXISTS (
SELECT 1
FROM club_user_apply cua
INNER JOIN wf_process_instance ins ON ins.businessNo = cua.id AND ins.state = 20
WHERE cua.mode = false
AND cua.clubId = scu.clubId
AND cua.userId = scu.userId
)
) exitInfo
LEFT JOIN sys_club club ON club.id = exitInfo.clubId
INNER JOIN vw_user u ON u.id = exitInfo.userId
""";
}
@Override
public Pagination<ClubUserCommonPageVo> clubManagePersonAuditPageData(ClubUserPageForm pageForm) {
Sql sql = Sqls.create("""
@@ -17,6 +17,10 @@ public class ClubEvaluatePageVo {
private String foundTime;
private String typeName;
private Date applyTime;
/** 社团评优申报开始年度。 */
private String applyStartYear;
/** 社团评优申报结束年度。 */
private String applyEndYear;
private String instanceId;
private String businessNo;
@@ -6,6 +6,7 @@ import com.budwk.app.zhgh.club.model.SysClubExamineRegisterDetailed;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.nutz.lang.util.NutMap;
import java.util.List;
@@ -18,4 +19,9 @@ public class ClubExamineVo extends SysClubExamineRegister {
private String typeName;
@ApiModelProperty("节点审批记录")
private List<BpmTaskApprovalRecordVo> nodeTasks;
/**
* V3年审已迁入但未转换为V4工作流任务的历史审核记录
*/
private List<NutMap> legacyAuditRecords;
}
@@ -11,6 +11,7 @@ import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.nutz.dao.Cnd;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.util.NutMap;
@@ -43,22 +44,26 @@ public class ArticleAnalysisController {
@At
@SaCheckPermission("article.analysis")
@ApiOperation(value = "统计分析")
public Result overview() {
public Result overview(Date startDate, Date endDate, String unionId, String clubId) {
List<ArticleAnalysisOverviewVO> list = new ArrayList<>();
// 获取总投稿量
list.add(ArticleAnalysisOverviewVO.builder()
.label("总投稿量")
.value(articleService.count())
.value(articleService.count(buildStatisticsCondition(startDate, endDate, unionId, clubId)))
.icon("el-icon-document")
.bgColor("rgba(24, 103, 176, 0.1)")
.trend(null)
.build());
// 获取本月投稿量
long monthlyCount = articleService.count(Cnd.where("MONTH(submitTime)", "=", DateUtil.thisMonth() + 1));
Cnd monthlyCondition = buildStatisticsCondition(startDate, endDate, unionId, clubId);
monthlyCondition.and("MONTH(submitTime)", "=", DateUtil.thisMonth() + 1);
long monthlyCount = articleService.count(monthlyCondition);
//上月投稿量
long lastMonthCount = articleService.count(Cnd.where("MONTH(submitTime)", "=", DateUtil.offsetMonth(new Date(), -1)));
Cnd lastMonthCondition = buildStatisticsCondition(startDate, endDate, unionId, clubId);
lastMonthCondition.and("MONTH(submitTime)", "=", DateUtil.month(DateUtil.offsetMonth(new Date(), -1)) + 1);
long lastMonthCount = articleService.count(lastMonthCondition);
list.add(ArticleAnalysisOverviewVO.builder()
.label("本月投稿")
@@ -70,7 +75,9 @@ public class ArticleAnalysisController {
// 获取活跃投稿人数
DateTime dateTime = DateUtil.offsetMonth(new Date(), -1);
long activeUsers = Math.max(articleService.count(Cnd.where("submitTime", ">=", dateTime).groupBy("userId")), 0L);
Cnd activeUserCondition = buildStatisticsCondition(startDate, endDate, unionId, clubId);
activeUserCondition.and("submitTime", ">=", dateTime).groupBy("userId");
long activeUsers = Math.max(articleService.count(activeUserCondition), 0L);
list.add(ArticleAnalysisOverviewVO.builder()
.label("活跃投稿人")
.value(activeUsers)
@@ -81,7 +88,11 @@ public class ArticleAnalysisController {
// 获取参与分工会数量
long unionCount = articleService.count(Sqls.create("SELECT COUNT(distinct unionId) FROM article WHERE unionId IS NOT NULL"));
Sql unionCountSql = Sqls.create("SELECT COUNT(distinct unionId) FROM article $condition");
Cnd unionCountCondition = buildStatisticsCondition(startDate, endDate, unionId, clubId);
unionCountCondition.and("unionId", "IS NOT", null);
unionCountSql.setCondition(unionCountCondition);
long unionCount = articleService.count(unionCountSql);
list.add(ArticleAnalysisOverviewVO.builder()
.label("参与分工会")
.value(unionCount)
@@ -91,7 +102,11 @@ public class ArticleAnalysisController {
.build());
// 获取参与社团数量
long clubCount = articleService.count(Sqls.create("SELECT COUNT(distinct clubId) FROM article WHERE clubId IS NOT NULL"));
Sql clubCountSql = Sqls.create("SELECT COUNT(distinct clubId) FROM article $condition");
Cnd clubCountCondition = buildStatisticsCondition(startDate, endDate, unionId, clubId);
clubCountCondition.and("clubId", "IS NOT", null);
clubCountSql.setCondition(clubCountCondition);
long clubCount = articleService.count(clubCountSql);
list.add(ArticleAnalysisOverviewVO.builder()
.label("参与社团")
.value(clubCount)
@@ -102,22 +117,43 @@ public class ArticleAnalysisController {
return Result.success(list);
}
/**
* 构建概览统计的筛选条件与图表接口保持相同的时间范围分工会和社团口径
*/
private Cnd buildStatisticsCondition(Date startDate, Date endDate, String unionId, String clubId) {
Cnd cnd = Cnd.NEW();
if (startDate != null && endDate != null) {
cnd.and(Cnd.cri().where().andBetween("submitTime", startDate, endDate));
}
cnd.andEX("unionId", "=", unionId);
cnd.andEX("clubId", "=", clubId);
return cnd;
}
@At
@SaCheckPermission("article.analysis")
@ApiOperation(value = "投稿趋势")
public Result trend(@Valid String type, Date startDate, Date endDate) {
List<NutMap> submissionTrend = articleAnalysisService.getSubmissionTrend(type, startDate, endDate);
public Result trend(@Valid String type, Date startDate, Date endDate, String unionId, String clubId) {
List<NutMap> submissionTrend = articleAnalysisService.getSubmissionTrend(type, startDate, endDate, unionId, clubId);
return Result.success(submissionTrend);
}
@At
@SaCheckPermission("article.analysis")
@ApiOperation(value = "分工会投稿分布")
public Result distribution(Date startDate, Date endDate) {
List<NutMap> unionDistribution = articleAnalysisService.getUnionDistribution(startDate, endDate);
public Result distribution(Date startDate, Date endDate, String unionId, String clubId) {
List<NutMap> unionDistribution = articleAnalysisService.getUnionDistribution(startDate, endDate, unionId, clubId);
return Result.success(unionDistribution);
}
@At
@SaCheckPermission("article.analysis")
@ApiOperation(value = "社团投稿分布")
public Result clubDistribution(Date startDate, Date endDate, String unionId, String clubId) {
List<NutMap> clubDistribution = articleAnalysisService.getClubDistribution(startDate, endDate, unionId, clubId);
return Result.success(clubDistribution);
}
@At
@SaCheckPermission("article.analysis")
@ApiOperation(value = "投稿排名")
@@ -52,6 +52,7 @@ public class ArticleMineController {
@SaCheckPermission("article.mine")
@ApiOperation("分页查询")
public Result pageData(@Valid ArticleInfoPageParam pageForm) {
// 每篇投稿仅关联最新的一条待办任务避免多办理人或并行待办将同一投稿重复展示
Sql sql = Sqls.create("""
SELECT
info.id,
@@ -79,8 +80,11 @@ public class ArticleMineController {
FROM
article info
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
LEFT JOIN wf_process_task t ON t.id = (
SELECT MAX(currentTask.id)
FROM wf_process_task currentTask
WHERE currentTask.processInstanceId = ins.id AND currentTask.taskState = 10
)
$condition
""");
Cnd cnd = Cnd.NEW();
@@ -49,6 +49,7 @@ public class ArticleQueryController {
info.loginName,
info.unitName,
info.unionName,
info.clubName,
info.submitTime,
info.origin,
ins.id AS instanceId,
@@ -13,12 +13,17 @@ public interface ArticleAnalysisService extends BaseService<Article> {
/**
* 获取投稿趋势数据
*/
List<NutMap> getSubmissionTrend(String type, Date startDate, Date endDate);
List<NutMap> getSubmissionTrend(String type, Date startDate, Date endDate, String unionId, String clubId);
/**
* 获取分工会投稿分布
*/
List<NutMap> getUnionDistribution(Date startDate, Date endDate);
List<NutMap> getUnionDistribution(Date startDate, Date endDate, String unionId, String clubId);
/**
* 获取社团投稿分布
*/
List<NutMap> getClubDistribution(Date startDate, Date endDate, String unionId, String clubId);
/**
* 获取排行榜数据
@@ -22,7 +22,7 @@ public class ArticleAnalysisServiceImpl extends BaseServiceImpl<Article> impleme
}
@Override
public List<NutMap> getSubmissionTrend(String type, Date startDate, Date endDate) {
public List<NutMap> getSubmissionTrend(String type, Date startDate, Date endDate, String unionId, String clubId) {
String groupFormat = switch (type) {
case "week" -> "%Y-%u"; // 按周分组
case "month" -> "%Y-%m"; // 按月分组
@@ -40,10 +40,8 @@ public class ArticleAnalysisServiceImpl extends BaseServiceImpl<Article> impleme
""");
sql.setParam("groupFormat",groupFormat);
Cnd cnd = Cnd.NEW();
if(ObjectUtil.isAllNotEmpty(startDate,endDate)){
cnd.and(Cnd.cri().where().andBetween("submitTime", startDate, endDate));
}
Cnd cnd = buildStatisticsCondition(startDate, endDate, unionId, clubId);
cnd.groupBy("DATE_FORMAT(submitTime, @groupFormat)");
sql.setCondition(cnd);
return listMap(sql);
@@ -53,7 +51,7 @@ public class ArticleAnalysisServiceImpl extends BaseServiceImpl<Article> impleme
* 获取分工会投稿分布
*/
@Override
public List<NutMap> getUnionDistribution(Date startDate, Date endDate) {
public List<NutMap> getUnionDistribution(Date startDate, Date endDate, String unionId, String clubId) {
Sql sql = Sqls.create("""
SELECT
unionName AS type,
@@ -62,10 +60,7 @@ public class ArticleAnalysisServiceImpl extends BaseServiceImpl<Article> impleme
article
$condition
""");
Cnd cnd = Cnd.NEW();
if(ObjectUtil.isAllNotEmpty(startDate,endDate)){
cnd.and(Cnd.cri().where().andBetween("submitTime", startDate, endDate));
}
Cnd cnd = buildStatisticsCondition(startDate, endDate, unionId, clubId);
cnd.groupBy("unionId","unionName");
cnd.desc("value");
sql.setCondition(cnd);
@@ -73,6 +68,47 @@ public class ArticleAnalysisServiceImpl extends BaseServiceImpl<Article> impleme
return listMap(sql);
}
/**
* 获取社团投稿分布
*/
@Override
public List<NutMap> getClubDistribution(Date startDate, Date endDate, String unionId, String clubId) {
Sql sql = Sqls.create("""
SELECT
clubName AS type,
COUNT(*) AS value
FROM
article
$condition
""");
Cnd cnd = buildStatisticsCondition(startDate, endDate, unionId, clubId);
cnd.and("clubId", "IS NOT", null);
cnd.groupBy("clubId", "clubName");
cnd.desc("value");
sql.setCondition(cnd);
return listMap(sql);
}
/**
* 为统计图表统一拼接时间范围和分工会筛选条件保证各图表的查询口径一致
*
* @param startDate 查询开始日期为空时不限制开始时间
* @param endDate 查询结束日期为空时不限制结束时间
* @param unionId 分工会 ID为空时查询全部分工会
* @param clubId 社团 ID为空时查询全部社团
* @return 可直接设置到统计 SQL 的查询条件
*/
private Cnd buildStatisticsCondition(Date startDate, Date endDate, String unionId, String clubId) {
Cnd cnd = Cnd.NEW();
if (ObjectUtil.isAllNotEmpty(startDate, endDate)) {
cnd.and(Cnd.cri().where().andBetween("submitTime", startDate, endDate));
}
cnd.andEX("unionId", "=", unionId);
cnd.andEX("clubId", "=", clubId);
return cnd;
}
/**
* 获取排行榜数据
*/
@@ -5,12 +5,15 @@ import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaMode;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.annotation.SLog;
import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.result.Result;
import com.budwk.app.base.utils.CommonDownloadUtil;
import com.budwk.app.web.commons.auth.utils.AuthUtil;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.dayofficework.asset.model.Asset;
import com.budwk.app.zhgh.dayofficework.asset.model.AssetDepreciationRecord;
@@ -26,6 +29,7 @@ import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Cnd;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.dao.util.cri.SqlExpressionGroup;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
@@ -153,12 +157,19 @@ public class AssetManageController {
@At
@ApiOperation("查询责任人")
@SaCheckPermission("asset.manage")
@SaCheckPermission(value = {"asset.manage", "asset.stocktaking", "h5.asset.stocktaking"}, mode = SaMode.OR)
public Result searchAssetUseUser(String query) {
Cnd cnd = Cnd.NEW();
cnd.where().orLike("username", query);
cnd.where().orLike("loginname", query);
cnd.where().orLike("id", query);
// 三个关键词条件必须整体分组避免 OR 条件绕过分工会数据范围
SqlExpressionGroup searchGroup = new SqlExpressionGroup();
searchGroup.orLike("username", query);
searchGroup.orLike("loginname", query);
searchGroup.orLike("id", query);
cnd.and(searchGroup);
// 分工会侧盘点人员只能选择本分工会成员校工会和系统管理员保留全校查询范围
if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name())) {
cnd.and("unionId", "=", SecurityUtil.getUnionId());
}
Sql sql = Sqls.create("""
SELECT
id,
@@ -1,12 +1,8 @@
package com.budwk.app.zhgh.dayofficework.birthdayWishes.task;
import cn.hutool.core.date.DateUtil;
import com.budwk.app.sys.models.Sys_msg;
import com.budwk.app.sys.services.SysMsgService;
import com.budwk.app.zhgh.staffmanage.birthday.service.UserBirthdayService;
import lombok.extern.slf4j.Slf4j;
import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.json.Json;
@@ -15,16 +11,12 @@ import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import java.util.List;
@IocBean
@Slf4j
public class BirthdayWishesTask implements Job {
@Inject
protected SysMsgService sysMsgService;
@Inject
protected Dao dao;
private UserBirthdayService userBirthdayService;
@Override
@@ -34,28 +26,12 @@ public class BirthdayWishesTask implements Job {
log.info("=================================参数:{}", Json.toJson(dataMap));
String planName = dataMap.getString("name");
String today = DateUtil.today();
String title = planName + today;
String content = dataMap.getString("template");
Sys_msg msg = new Sys_msg();
msg.setTitle(planName + today);
msg.setType("user");
msg.setSendType("show");
msg.setSendAt(DateUtil.current());
msg.setNote(dataMap.getString("template"));
// 查询今天生日的会员用户
Sql sql = Sqls.create("SELECT loginname FROM sys_user WHERE member = 1 AND MONTH(birthday) = MONTH(CURRENT_DATE) AND DAY(birthday) = DAY(CURRENT_DATE)");
sql.setCallback(Sqls.callback.strList());
dao.execute(sql);
List<String> users = sql.getList(String.class);
// 转为数组
String[] userArray = users.toArray(String[]::new);
log.info("=================================查询今天生日会员用户:{}", users);
if(userArray.length > 0){
log.info("=================================发送消息:{}", msg);
sysMsgService.saveMsg(msg, userArray, true);
}
// 统一由生日服务查询当天生日会员生成移动端链接并记录系统推送日志
int receiverCount = userBirthdayService.sendTodayBirthdayMessages(title, content);
log.info("=================================当天生日通知进入发送队列人数:{}", receiverCount);
}
}
@@ -58,6 +58,10 @@ public class BuildHomeKpiWriteDynamicTableRenderPolicy extends DynamicTableRende
// 处理合并
if(ObjectUtil.isNotEmpty(dataList)){
for (int[] value : groupRanges.values()) {
// 单条明细无需合并POI 不支持对同一行进行纵向合并
if (value[0] == value[1]) {
continue;
}
TableTools.mergeCellsVertically(xwpfTable, 0, value[0] + 1, value[1] + 1);
TableTools.mergeCellsVertically(xwpfTable, 1, value[0] + 1, value[1] + 1);
TableTools.mergeCellsVertically(xwpfTable, 4, value[0] + 1, value[1] + 1);
@@ -94,7 +94,7 @@ public class BuildHomeKpiWriteController {
buildHomeKpiWriteService.exportDocx(id, os);
// 下载生成的文件
CommonDownloadUtil.download(
"南京邮电大学分工会教职工之家建设考核评议自评表.docx",
"中国地质大学分工会教职工之家建设考核评议自评表.docx",
os.toByteArray(),
response
);
@@ -136,7 +136,7 @@ public class BuildHomeLogBookController {
template.render(map).writeAndClose(os);
// 下载生成的文件
CommonDownloadUtil.download(
"南京邮电大学分工会建家记录册.docx",
"中国地质大学分工会建家记录册.docx",
os.toByteArray(),
response
);
@@ -2,6 +2,7 @@ package com.budwk.app.zhgh.dayofficework.buildHome.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.result.Result;
@@ -104,8 +105,13 @@ public class BuildHomeMaterialQueryController {
}
@At
@Ok("void")
@SaCheckPermission("buildHome.material.query")
public void exportZip(String actId, HttpServletResponse response) throws IOException {
BuildHomeAct act = buildHomeActService.fetch(actId);
if (act == null) {
throw new IllegalArgumentException("未找到评比事项");
}
Sql sql = Sqls.create("""
SELECT
t1.id,
@@ -123,7 +129,6 @@ public class BuildHomeMaterialQueryController {
AND t2.actId = @actId
LEFT JOIN build_home_act t3 ON t3.id = t2.actId
AND t3.id = @actId
WHERE t1.id = '3742a919fb6847f7a8dbe051a2ef0025'
GROUP BY
t1.id,
t2.unionId
@@ -135,7 +140,9 @@ public class BuildHomeMaterialQueryController {
// 2. 设置响应头
response.setContentType("application/zip");
response.setHeader("Content-Disposition", "attachment; filename=\"工会文件汇总.zip\"");
// 使用评比事项名称作为压缩包名称避免下载文件无法区分来源
String zipFileName = sanitizeFileName(act.getName()) + "汇总材料.zip";
response.setHeader("Content-Disposition", "attachment; filename=" + URLUtil.encode(zipFileName));
// 3. 使用try-with-resources创建ZIP输出流
try (ZipOutputStream zos = new ZipOutputStream(response.getOutputStream())) {
@@ -164,6 +171,18 @@ public class BuildHomeMaterialQueryController {
}
/**
* 清理评比事项名称中的 Windows 文件名非法字符确保浏览器可正常保存压缩包
*
* @param fileName 原始评比事项名称
* @return 可用于下载文件名的评比事项名称
*/
private String sanitizeFileName(String fileName) {
String sanitizedFileName = StrUtil.blankToDefault(fileName, "评比事项");
return sanitizedFileName.replaceAll("[\\\\/:*?\"<>|]", "_");
}
private void processFileList(ZipOutputStream zos, String unionName, String fileType, String filesJson) throws IOException {
if (StrUtil.isNotBlank(filesJson)) {
List<NutMap> files = Json.fromJsonAsList(NutMap.class, filesJson);
@@ -4,12 +4,15 @@ import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaMode;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.annotation.SLog;
import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.result.Result;
import com.budwk.app.flow.engine.FlowEngine;
import com.budwk.app.web.commons.auth.utils.AuthUtil;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.dayofficework.cadreTraining.model.CadreTrainingAct;
import com.budwk.app.zhgh.dayofficework.cadreTraining.model.CadreTrainingSignUp;
import com.budwk.app.zhgh.dayofficework.cadreTraining.service.CadreTrainingActService;
import com.budwk.app.zhgh.dayofficework.cadreTraining.service.CadreTrainingSignUpService;
import io.swagger.annotations.Api;
@@ -29,6 +32,7 @@ import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
import javax.validation.Valid;
import java.util.List;
/**
* @author : hongqiwei
@@ -106,12 +110,41 @@ public class CadreTrainingMineController {
cnd.where().andLike(CadreTrainingAct::getName, pageForm.getSearchKeyword());
}
cnd.desc("info.applyTime");
cnd.and("info.userId", "=", SecurityUtil.getUserId());
// 本人报名和本人代他人报名的记录都应在我的报名中展示
cnd.and(Cnd.exps("info.userId", "=", SecurityUtil.getUserId())
.or("info.applyUserId", "=", SecurityUtil.getUserId()));
sql.setCondition(cnd);
Pagination<NutMap> pagination = cadreTrainingSignUpService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
return Result.success(pagination);
}
/**
* 查询我的报名查看页的报名人员
* 管理角色按活动和分工会查看全部报名人员普通用户仅返回本人记录避免扩大人员信息可见范围
*
* @param activityId 培训活动 ID对应 cadre_training 表主键
* @param unionId 当前查看报名记录所属的分工会 ID
* @return 报名记录列表元素包含人员姓名工号单位分工会联系方式及备注
*/
@At
@ApiOperation("查询我的报名活动人员")
@SaCheckPermission(value = {"cadreTraining.mine", "h5.cadreTraining.mine"}, mode = SaMode.OR)
public Result listSignUsers(@Valid String activityId, String unionId) {
Cnd cnd = Cnd.where(CadreTrainingSignUp::getCadreTrainingActId, "=", activityId);
if (AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name(), RoleConstant.BRANCH_UNION_CHAIRMAN.name())) {
// 管理角色必须携带当前报名记录所属的分工会防止空参数绕过分工会范围
if (StrUtil.isBlank(unionId)) {
return Result.error("分工会信息不能为空");
}
cnd.and(CadreTrainingSignUp::getUnionId, "=", unionId);
} else {
cnd.and(CadreTrainingSignUp::getUserId, "=", SecurityUtil.getUserId());
}
cnd.asc(CadreTrainingSignUp::getApplyTime);
List<CadreTrainingSignUp> signUsers = dao.query(CadreTrainingSignUp.class, cnd);
return Result.success(signUsers);
}
@At
@ApiOperation("删除")
@Aop(TransAop.READ_COMMITTED)
@@ -67,8 +67,9 @@ public class CadreTrainingSignUpController {
@SaCheckPermission("cadreTraining.branchUnionSignUp")
@ApiOperation("分页查询")
public Result pageData(@Valid PageForm pageForm,Integer year, boolean isEnrolled) {
// 同一活动存在多条代报名记录时活动列表仅展示一次
Sql sql = Sqls.create("""
SELECT
SELECT DISTINCT
info.*,
CASE WHEN cts.id IS NOT NULL THEN 1 ELSE 0 END AS isEnrolled
FROM
@@ -101,6 +102,9 @@ public class CadreTrainingSignUpController {
List<ProcessInstance> instances = new ArrayList<>();
for (CadreTrainingSignUp signUp : cadreTrainingSignUps) {
// 代报名记录必须以当前登录人为报名操作人已报名我的报名按操作人查询
signUp.setApplyUserId(SecurityUtil.getUserId());
signUp.setApplyUserUserName(SecurityUtil.getUserUsername());
if (StrUtil.isBlank(signUp.getId())) {
signUp.setApplyTime(new Date());
}
@@ -84,6 +84,8 @@ public class EnrollmentRegistrationApplyListController {
Cnd cnd = Cnd.NEW();
cnd.andEX("YEAR(info.applyTime)", "=", year);
cnd.and("info.userId", "=", SecurityUtil.getUserId());
// 同一登记可能关联多个流程任务参与人按业务主表ID分组后仅展示一条申请记录
cnd.groupBy("info.id");
sql.setCondition(cnd);
Pagination pagination = baseService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
return Result.success(pagination);
@@ -74,6 +74,7 @@ public class EvaluateActivityController {
Cnd cnd = Cnd.NEW();
cnd.andEX("year", "=", year);
cnd.and(Cnd.likeEX("eva.name", searchKeyword));
cnd.groupBy("eva.id");
sql.setCondition(cnd);
return Result.success(baseService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql));
}
@@ -83,6 +83,7 @@ public class EvaluateApplyController {
Cnd cnd = Cnd.NEW();
cnd.and("enable", "=", 1);
cnd.andEX("year", "=", year);
cnd.groupBy("eva.id");
sql.setCondition(cnd);
Pagination pagination = evaluateActivityService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
return Result.success(pagination);
@@ -108,6 +109,10 @@ public class EvaluateApplyController {
@ApiOperation("保存")
@SLog(tag = "评优评先申请", msg = "保存申请")
public Result save(@Param("data") EvaluateApply evaluateApply) {
String validateMessage = evaluateService.validateCollectiveHonorApply(evaluateApply);
if (validateMessage != null) {
return Result.error(validateMessage);
}
evaluateApply.setApplyDateTime(new Date());
evaluateService.insertOrUpdate(evaluateApply);
return Result.success();
@@ -119,6 +124,10 @@ public class EvaluateApplyController {
@Aop(TransAop.READ_COMMITTED)
@SLog( tag = "评优评先申请", msg = "重新提交申请")
public Result submitAgain(@Param("data") EvaluateApply evaluateApply, @Param("taskId") Long taskId) {
String validateMessage = evaluateService.validateCollectiveHonorApply(evaluateApply);
if (validateMessage != null) {
return Result.error(validateMessage);
}
evaluateService.insertOrUpdate(evaluateApply);
Dict dict = Dict.create();
@@ -134,6 +143,10 @@ public class EvaluateApplyController {
@Aop(TransAop.READ_COMMITTED)
@SLog(tag = "评优评先申请", msg = "提交申请")
public Result submit(@Param("data") EvaluateApply evaluateApply) {
String validateMessage = evaluateService.validateCollectiveHonorApply(evaluateApply);
if (validateMessage != null) {
return Result.error(validateMessage);
}
evaluateApply.setApplyDateTime(new Date());
evaluateService.insertOrUpdate(evaluateApply);
@@ -80,6 +80,7 @@ public class EvaluateMineController {
Cnd cnd = Cnd.NEW();
cnd.andEX("YEAR(info.year)", "=", year);
cnd.and("info.loginName", "=", SecurityUtil.getUserLoginname());
cnd.groupBy("info.id");
cnd.desc("info.createdAt");
sql.setCondition(cnd);
Pagination pagination = evaluateService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
@@ -10,4 +10,12 @@ public interface EvaluateService extends BaseService<EvaluateApply> {
Sql getSummarySql(EvaluatePageForm pageForm);
/**
* 校验集体荣誉是否已由当前分工会申请
*
* @param evaluateApply 待保存或提交的申请
* @return 校验通过返回 {@code null}重复申请时返回提示信息
*/
String validateCollectiveHonorApply(EvaluateApply evaluateApply);
}
@@ -4,9 +4,12 @@ import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.base.utils.PageUtil;
import com.budwk.app.flow.enums.ProcessTaskStateEnum;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.dayofficework.evaluation.models.EvaluateActivity;
import com.budwk.app.zhgh.dayofficework.evaluation.models.EvaluateApply;
import com.budwk.app.zhgh.dayofficework.evaluation.service.EvaluateService;
import com.budwk.app.zhgh.dayofficework.evaluation.vo.EvaluatePageForm;
import com.budwk.app.zhgh.dayofficework.honor.models.HonorBasicSettings;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
@@ -67,6 +70,7 @@ public class EvaluateServiceImpl extends BaseServiceImpl<EvaluateApply> implemen
seg.orLike("info.loginName", pageForm.getSearchKeyword());
cnd.and(seg);
}
cnd.groupBy("info.id");
if (StrUtil.isAllBlank(pageForm.getPageOrderName(), pageForm.getPageOrderBy())) {
cnd.desc("t.createdAt").desc("info.applyDateTime");
} else {
@@ -76,4 +80,30 @@ public class EvaluateServiceImpl extends BaseServiceImpl<EvaluateApply> implemen
sql.setCondition(cnd);
return sql;
}
@Override
public String validateCollectiveHonorApply(EvaluateApply evaluateApply) {
if (evaluateApply == null || StrUtil.isBlank(evaluateApply.getEvaluateId())) {
return "评优评先活动不能为空";
}
EvaluateActivity activity = dao().fetch(EvaluateActivity.class, evaluateApply.getEvaluateId());
if (activity == null) {
return "评优评先活动不存在";
}
HonorBasicSettings honorType = dao().fetch(HonorBasicSettings.class, activity.getHonorTypeId());
if (honorType == null || !StrUtil.equals("集体荣誉", honorType.getName())) {
return null;
}
Cnd cnd = Cnd.where(EvaluateApply::getUnionId, "=", SecurityUtil.getUnionId())
.and(EvaluateApply::getHonorId, "=", activity.getHonorId())
.and("YEAR(applyDateTime)", "=", activity.getYear());
if (StrUtil.isNotBlank(evaluateApply.getId())) {
cnd.and(EvaluateApply::getId, "!=", evaluateApply.getId());
}
if (dao().count(EvaluateApply.class, cnd) > 0) {
return "本分工会已申请该集体荣誉,同一类型只能申请一次";
}
return null;
}
}
@@ -141,7 +141,7 @@ public class GhkhKhphController {
List<ExcelExportEntity> exportEntities = new ArrayList<>();
exportEntities.add(new ExcelExportEntity("序号", "index", 10));
exportEntities.add(new ExcelExportEntity("年度", "annual", 10));
exportEntities.add(new ExcelExportEntity("分工会名称", "unionName", 30));
exportEntities.add(new ExcelExportEntity("分工会名称", "unionname", 30));
exportEntities.add(new ExcelExportEntity("线上得分", "xgh_score", 20));
exportEntities.add(new ExcelExportEntity("线上最终得分" + (config.getOnLine() != null ? "" + config.getOnLine() + "%" : ""), "onLineFinalScore", 20));
exportEntities.add(new ExcelExportEntity("线下得分", "offLineScore", 20));
@@ -3,11 +3,13 @@ package com.budwk.app.zhgh.dayofficework.honor.controller;
import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.result.Result;
import com.budwk.app.sys.models.Sys_union;
import com.budwk.app.sys.services.SysUnionService;
import com.budwk.app.sys.services.SysUserService;
import com.budwk.app.web.commons.auth.utils.AuthUtil;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.dayofficework.honor.models.Honor;
import com.budwk.app.zhgh.dayofficework.honor.service.HonorCommonService;
@@ -90,7 +92,10 @@ public class HonorRegistrationController {
seg.orLike("t1.username",keyWord);
cnd.and(seg);
}
cnd.and("t1.unionId","=", SecurityUtil.getUnionId());
// 系统管理员可跨分工会查询人员其他角色仍限定为当前所属分工会
if (!AuthUtil.hasRole(RoleConstant.SYSADMIN.name())) {
cnd.and("t1.unionId", "=", SecurityUtil.getUnionId());
}
sql.setCondition(cnd);
Pagination pagination = sysUserService.listPageMap(1, 50, sql);
return Result.success(pagination.getList());
@@ -54,8 +54,8 @@ public class HonorSummaryController {
@SaCheckPermission("honor.honorLevel.Summary")
public Result unionData(HonorPageForm pageForm) {
List<HonorBasicSettings> query = dao.query(HonorBasicSettings.class, Cnd.where("queryTypeCode", "in", List.of(HonorTypeOrigin.HONOR_SINGLE.name(), HonorTypeOrigin.HONOR_LIST.name())));
String listHonorId = query.stream().filter(v->v.getQueryTypeCode().equals(HonorTypeOrigin.HONOR_LIST.name())).findFirst().orElse(new HonorBasicSettings()).getId();
String singleHonorId = query.stream().filter(v->v.getQueryTypeCode().equals(HonorTypeOrigin.HONOR_SINGLE.name())).findFirst().orElse(new HonorBasicSettings()).getId();
String personalHonorId = query.stream().filter(v->v.getQueryTypeCode().equals(HonorTypeOrigin.HONOR_SINGLE.name())).findFirst().orElse(new HonorBasicSettings()).getId();
String collectiveHonorId = query.stream().filter(v->v.getQueryTypeCode().equals(HonorTypeOrigin.HONOR_LIST.name())).findFirst().orElse(new HonorBasicSettings()).getId();
Cnd cnd = Cnd.NEW();
Sql sql = Sqls.create("""
SELECT
@@ -65,7 +65,7 @@ public class HonorSummaryController {
( SELECT COUNT( 1 ) FROM honor h WHERE h.honorType = @collectiveHonor AND h.unionId = un.id $cnd) collectiveHonorNum
FROM
sys_union un $condition
""").setParam("personalHonor", listHonorId).setParam("collectiveHonor", singleHonorId);
""").setParam("personalHonor", personalHonorId).setParam("collectiveHonor", collectiveHonorId);
cnd.andEX("id", "=", pageForm.getUnionId());
if (pageForm.getYear() != null) {
sql.setVar("cnd", "and YEAR(grantDate)=" + pageForm.getYear());
@@ -79,21 +79,27 @@ public class HonorSummaryController {
@At
@SaCheckPermission("honor.honorLevel.Summary")
public Result honorLevelData(HonorPageForm pageForm) {
List<HonorBasicSettings> query = dao.query(HonorBasicSettings.class, Cnd.where("queryTypeCode", "in",
List.of(HonorTypeOrigin.HONOR_SCHOOL_LEVEL.name(), HonorTypeOrigin.HONOR_PROVINCIAL_LEVEL.name(), HonorTypeOrigin.HONOR_NATIONAL_LEVEL.name())));
String schoolHonorId = query.stream().filter(v->v.getQueryTypeCode().equals(HonorTypeOrigin.HONOR_SCHOOL_LEVEL.name())).findFirst().orElse(new HonorBasicSettings()).getId();
String provincialHonorId = query.stream().filter(v->v.getQueryTypeCode().equals(HonorTypeOrigin.HONOR_PROVINCIAL_LEVEL.name())).findFirst().orElse(new HonorBasicSettings()).getId();
String nationalHonorId = query.stream().filter(v->v.getQueryTypeCode().equals(HonorTypeOrigin.HONOR_NATIONAL_LEVEL.name())).findFirst().orElse(new HonorBasicSettings()).getId();
HonorBasicSettings levelRoot = dao.fetch(HonorBasicSettings.class,
Cnd.where("queryTypeCode", "=", HonorTypeOrigin.HONOR_LEVEL.name()));
List<HonorBasicSettings> levelList = levelRoot == null ? List.of() : dao.query(HonorBasicSettings.class,
Cnd.where("parentId", "=", levelRoot.getId()));
String schoolHonorId = levelList.stream().filter(item -> "校内".equals(item.getName()))
.findFirst().orElse(new HonorBasicSettings()).getId();
String provincialHonorId = levelList.stream().filter(item -> "省部级".equals(item.getName()))
.findFirst().orElse(new HonorBasicSettings()).getId();
String nationalHonorId = levelList.stream().filter(item -> "国家级".equals(item.getName()))
.findFirst().orElse(new HonorBasicSettings()).getId();
Cnd cnd = Cnd.NEW();
// honor.honorLevel 直接保存荣誉等级项 ID荣誉等级下的具体等级项统计
// 历史荣誉记录可能未保存 unionId统计时仅在 unionId 为空时按 unionName 回退关联
Sql sql = Sqls.create("""
SELECT
id,
name as unionname,
( SELECT COUNT( 1 ) FROM honor h WHERE h.honorLevel = @nationalLevel AND h.unionId = un.id $cnd) nationalLevelNum,
( SELECT COUNT( 1 ) FROM honor h WHERE h.honorLevel = @provincial AND h.unionId = un.id $cnd) provincialNum ,
( SELECT COUNT( 1 ) FROM honor h WHERE h.honorLevel = @schoolLevel AND h.unionId = un.id $cnd) schoolLevelNum
( SELECT COUNT( 1 ) FROM honor h WHERE h.honorLevel = @nationalLevel AND (h.unionId = un.id OR ((h.unionId IS NULL OR h.unionId = '') AND h.unionName = un.name)) $cnd) nationalLevelNum,
( SELECT COUNT( 1 ) FROM honor h WHERE h.honorLevel = @provincial AND (h.unionId = un.id OR ((h.unionId IS NULL OR h.unionId = '') AND h.unionName = un.name)) $cnd) provincialNum ,
( SELECT COUNT( 1 ) FROM honor h WHERE h.honorLevel = @schoolLevel AND (h.unionId = un.id OR ((h.unionId IS NULL OR h.unionId = '') AND h.unionName = un.name)) $cnd) schoolLevelNum
FROM
sys_union un $condition
""").setParam("nationalLevel", nationalHonorId).setParam("provincial", provincialHonorId).setParam("schoolLevel", schoolHonorId);
@@ -3,11 +3,14 @@ package com.budwk.app.zhgh.dayofficework.meeting.controller;
import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.annotation.RepeatSubmit;
import com.budwk.app.base.annotation.SLog;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.result.Result;
import com.budwk.app.base.utils.PageUtil;
import com.budwk.app.sys.models.Sys_module;
import com.budwk.app.sys.models.Sys_role;
import com.budwk.app.zhgh.dayofficework.meeting.model.MeetingInfo;
import com.budwk.app.zhgh.dayofficework.meeting.model.MeetingTimePeriod;
import com.budwk.app.zhgh.dayofficework.meeting.model.MeetingTimePeriodUser;
@@ -101,6 +104,7 @@ public class MeetingManageController {
@At
@ApiOperation("新增/修改会议")
@Aop(TransAop.READ_COMMITTED)
@RepeatSubmit
@SaCheckPermission("meeting.manage")
@SLog(tag = "会议管理系统-会议管理", msg = "新增/修改会议")
public Object submit(@Param("data") MeetingInfo info,
@@ -159,13 +163,17 @@ public class MeetingManageController {
cnd.andEX("u.unitId", "=", unitId);
cnd.and(Cnd.likeEX("u.sex", sex));
if (StrUtil.isNotBlank(roleId)) {
cnd.and(new Static("u.id in (select userId from sys_user_role where roleId = '%s')".formatted(roleId)));
cnd.and(new Static("u.id in (select userId from sys_user_role where roleId = '%s' and enable='1')".formatted(roleId)));
}
// 选择届次但未选择角色时按届次筛选全部相关人员同时选择角色时限定为同一角色关联记录
if (StrUtil.isBlank(roleId) && StrUtil.isNotBlank(teacherCongressSessionId)) {
cnd.and(new Static("u.id in (select userId from sys_user_role where enable='1' and tcSessionId = '%s')".formatted(teacherCongressSessionId)));
}
if (StrUtil.isNotBlank(roleId) && StrUtil.isNotBlank(teacherCongressSessionId)) {
cnd.and(new Static("u.id in (select userId from sys_user_role where roleId = '%s' and tcSessionId = '%s')".formatted(roleId, teacherCongressSessionId)));
cnd.and(new Static("u.id in (select userId from sys_user_role where roleId = '%s' and enable='1' and tcSessionId = '%s')".formatted(roleId, teacherCongressSessionId)));
}
if (StrUtil.isNotBlank(roleId) && StrUtil.isNotBlank(workerCongressSessionId)) {
cnd.and(new Static("u.id in (select userId from sys_user_role where roleId = '%s' and wcSessionId = '%s')".formatted(roleId, workerCongressSessionId)));
cnd.and(new Static("u.id in (select userId from sys_user_role where roleId = '%s' and enable='1' and wcSessionId = '%s')".formatted(roleId, workerCongressSessionId)));
}
if (Strings.isNotBlank(pageForm.getSearchName()) && Strings.isNotBlank(pageForm.getSearchKeyword())) {
cnd.and(pageForm.getSearchName(), "like", "%" + pageForm.getSearchKeyword() + "%");
@@ -225,4 +233,40 @@ public class MeetingManageController {
List<Worker_congress_session> list = dao.query(Worker_congress_session.class, Cnd.where(Teacher_congress_session::getEnable, "=", true).desc("year"));
return Result.success(list);
}
/**
* 查询会议选择用户时可筛选的 PC 端模块
*
* @return 模块列表元素包含模块 ID名称和平台按排序编号升序返回
*/
@At
@ApiOperation("查询 PC 端模块")
@SaCheckPermission("meeting.manage")
public Result listModule() {
List<Sys_module> modules = dao.query(Sys_module.class,
Cnd.where(Sys_module::getPlatform, "=", "PC").asc(Sys_module::getSortNum));
return Result.success(modules);
}
/**
* 按模块查询会议选择用户时可筛选的启用角色
*
* @param moduleId PC 端模块 ID来源于 sys_module
* @return 角色列表元素包含角色 ID名称和角色标识未传模块时返回空列表
*/
@At
@ApiOperation("按模块查询角色")
@SaCheckPermission("meeting.manage")
public Result listRole(String moduleId) {
if (StrUtil.isBlank(moduleId)) {
return Result.success(List.of());
}
Sys_module module = dao.fetch(Sys_module.class, moduleId);
if (module == null || !"PC".equals(module.getPlatform())) {
return Result.success(List.of());
}
List<Sys_role> roles = dao.query(Sys_role.class,
Cnd.where(Sys_role::getModuleId, "=", moduleId).and(Sys_role::isDisabled, "=", false).asc(Sys_role::getSort));
return Result.success(roles);
}
}
@@ -93,6 +93,10 @@ public class MeetingMineController {
List<MeetingTimePeriodUser> periodUsers = dao.query(MeetingTimePeriodUser.class, Cnd.where(MeetingTimePeriodUser::getUserId, "=", SecurityUtil.getUserId()));
List<String> idList = periodUsers.stream().map(MeetingTimePeriodUser::getMeetingId).toList();
// 当前用户未被安排参会时直接返回空分页避免生成空 IN 条件导致数据库语法错误
if (idList.isEmpty()) {
return Result.success(new Pagination<>(pageForm.getPageNumber(), pageForm.getPageSize(), 0, List.of()));
}
cnd.and("id", "in", idList);
cnd.andEX("typeId", "=", type);
@@ -217,6 +221,14 @@ public class MeetingMineController {
@Aop(TransAop.READ_COMMITTED)
@SLog(tag = "会务管理-我的会议-签到", msg = "会议签到")
public Result sign(@Valid String periodId) {
MeetingTimePeriod timePeriod = dao.fetch(MeetingTimePeriod.class, periodId);
if (timePeriod == null) {
return Result.error("会议场次不存在!");
}
// 以服务端时间校验场次开始时间避免客户端时间或页面状态被绕过
if (timePeriod.getStartTime() == null || DateUtil.date().before(timePeriod.getStartTime())) {
return Result.error("会议尚未开始,暂不能签到!");
}
MeetingTimePeriodUser user = dao.fetch(
MeetingTimePeriodUser.class,
Cnd.where(MeetingTimePeriodUser::getTimePeriodId, "=", periodId)
@@ -115,8 +115,10 @@ public class MeetingOnlineController {
@At("/getRealTimeData/?")
@Ok("json:full")
@SaCheckLogin
public Result getRealTimeData(String timePeriodId) {
List<MeetingTimePeriodUser> signInUser = onlineService.getSignInUser(timePeriodId);
public Result getRealTimeData(String timePeriodId,
@Param(value = "signRankOrder") String signRankOrder) {
// 签到人员排序统一交由Service处理控制器只负责接收前端排序方向并返回实时数据
List<MeetingTimePeriodUser> signInUser = onlineService.getSignInUser(timePeriodId, signRankOrder);
List<Map<String, Object>> countData = onlineService.getRealTimeData(timePeriodId);
return Result.success(Map.of("countData", countData, "userData", signInUser));
}
@@ -18,6 +18,15 @@ import java.util.Map;
public interface MeetingOnlineService extends BaseService<MeetingInfo> {
List<MeetingTimePeriodUser> exportSignature(String timePeriodId);
List<MeetingTimePeriodUser> getSignInUser(String timePeriodId);
/**
* 查询指定会议时段的签到人员并根据签到名次要求排序
*
* @param timePeriodId 会议时段ID
* @param signRankOrder 签到名次排序方向ascending为升序其他值按降序处理
* @return 已签到人员列表
*/
List<MeetingTimePeriodUser> getSignInUser(String timePeriodId, String signRankOrder);
List<Map<String, Object>> getRealTimeData(String timePeriodId);
}
@@ -19,6 +19,7 @@ import org.nutz.lang.Lang;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@@ -44,11 +45,12 @@ public class MeetingInfoServiceImpl extends BaseServiceImpl<MeetingInfo> impleme
// 插入会议表和会议场次表
info.setCreateTime(DateUtil.now());
insertWith(info, "timePeriods");
List<MeetingTimePeriodUser> uniqueUsers = distinctUsers(users);
// 插入参与人员表
if (Lang.isNotEmpty(info.getTimePeriods()) && Lang.isNotEmpty(users)) {
if (Lang.isNotEmpty(info.getTimePeriods()) && Lang.isNotEmpty(uniqueUsers)) {
List<MeetingTimePeriodUser> relations = info.getTimePeriods()
.stream()
.flatMap(period -> Arrays.stream(users).map(user -> buildRelation(info.getId(), period.getId(), user)))
.flatMap(period -> uniqueUsers.stream().map(user -> buildRelation(info.getId(), period.getId(), user)))
.toList();
dao().insert(relations);
}
@@ -58,6 +60,7 @@ public class MeetingInfoServiceImpl extends BaseServiceImpl<MeetingInfo> impleme
@Aop(TransAop.READ_COMMITTED)
public void edit(MeetingInfo info, MeetingTimePeriodUser[] users) {
String meetingId = info.getId();
List<MeetingTimePeriodUser> uniqueUsers = distinctUsers(users);
// 设置外键并保存场次含新增和修改
info.getTimePeriods().forEach(p -> p.setMeetingId(meetingId));
@@ -79,7 +82,7 @@ public class MeetingInfoServiceImpl extends BaseServiceImpl<MeetingInfo> impleme
.collect(Collectors.toSet());
// 页面传过来的新人员
Set<String> newUserIds = Arrays.stream(users).map(MeetingTimePeriodUser::getUserId).collect(Collectors.toSet());
Set<String> newUserIds = uniqueUsers.stream().map(MeetingTimePeriodUser::getUserId).collect(Collectors.toSet());
// 删除废弃场次及其所有关联
Set<String> deletedPeriodIds = Sets.difference(oldPeriodIds, newPeriodIds);
@@ -99,7 +102,7 @@ public class MeetingInfoServiceImpl extends BaseServiceImpl<MeetingInfo> impleme
List<MeetingTimePeriodUser> newRelations = new ArrayList<>();
// 新增的用户 所有现存场次
List<MeetingTimePeriodUser> newUsers = Arrays.stream(users).filter(u -> StrUtil.isBlank(u.getId())).toList();
List<MeetingTimePeriodUser> newUsers = uniqueUsers.stream().filter(u -> StrUtil.isBlank(u.getId())).toList();
for (String periodId : newPeriodIds) {
for (MeetingTimePeriodUser user : newUsers) {
newRelations.add(buildRelation(meetingId, periodId, user));
@@ -109,7 +112,7 @@ public class MeetingInfoServiceImpl extends BaseServiceImpl<MeetingInfo> impleme
// 新增的场次 所有现存老用户避免重复插入新用户
Set<String> addedPeriodIds = Sets.difference(newPeriodIds, oldPeriodIds);
for (String periodId : addedPeriodIds) {
for (MeetingTimePeriodUser user : users) {
for (MeetingTimePeriodUser user : uniqueUsers) {
if (StrUtil.isBlank(user.getId())) continue;
newRelations.add(buildRelation(meetingId, periodId, user));
}
@@ -131,4 +134,21 @@ public class MeetingInfoServiceImpl extends BaseServiceImpl<MeetingInfo> impleme
rel.setJoinStatus(true);
return rel;
}
/**
* 按用户 ID 去重会议参会人员保留首次选择的人员信息
* 同一用户在一个会议的每个场次只允许生成一条参会关系防止重复提交或重复选择产生重复记录
*
* @param users 前端提交的参会人员数组每项必须包含 userId
* @return 去重后的参会人员列表userId 为空的无效项不会参与保存
*/
private List<MeetingTimePeriodUser> distinctUsers(MeetingTimePeriodUser[] users) {
if (users == null || users.length == 0) {
return List.of();
}
return new ArrayList<>(Arrays.stream(users)
.filter(user -> StrUtil.isNotBlank(user.getUserId()))
.collect(Collectors.toMap(MeetingTimePeriodUser::getUserId, user -> user, (first, duplicate) -> first, LinkedHashMap::new))
.values());
}
}
@@ -70,13 +70,16 @@ public class MeetingOnlineServiceImpl extends BaseServiceImpl<MeetingInfo> imple
}
@Override
public List<MeetingTimePeriodUser> getSignInUser(String timePeriodId) {
return dao().query(
MeetingTimePeriodUser.class,
Cnd.where(MeetingTimePeriodUser::getTimePeriodId, "=", timePeriodId)
.and(MeetingTimePeriodUser::getSignStatus, "=", true)
.desc("signTime")
);
public List<MeetingTimePeriodUser> getSignInUser(String timePeriodId, String signRankOrder) {
Cnd cnd = Cnd.where(MeetingTimePeriodUser::getTimePeriodId, "=", timePeriodId)
.and(MeetingTimePeriodUser::getSignStatus, "=", true);
// 签到名次由签到时间确定只识别固定方向参数禁止将前端排序内容直接拼接到SQL中
if ("ascending".equals(signRankOrder)) {
cnd.asc("signTime");
} else {
cnd.desc("signTime");
}
return dao().query(MeetingTimePeriodUser.class, cnd);
}
@Override
@@ -133,7 +136,8 @@ public class MeetingOnlineServiceImpl extends BaseServiceImpl<MeetingInfo> imple
Sys_role formalRole = roleService.getByCode(formalRoleCode);
Sys_role attendanceRole = roleService.getByCode(attendanceRoleCode);
List<String> signedInUserIds = getSignInUser(timePeriodId).stream()
// 代表签到统计只关心已签到人员集合使用默认降序查询即可
List<String> signedInUserIds = getSignInUser(timePeriodId, "descending").stream()
.map(MeetingTimePeriodUser::getUserId)
.filter(Objects::nonNull)
.toList();

Some files were not shown because too many files have changed in this diff Show More