diff --git a/src/main/java/com/budwk/app/sys/controller/SysHomeController.java b/src/main/java/com/budwk/app/sys/controller/SysHomeController.java index 9395da6..46a5075 100644 --- a/src/main/java/com/budwk/app/sys/controller/SysHomeController.java +++ b/src/main/java/com/budwk/app/sys/controller/SysHomeController.java @@ -1,34 +1,29 @@ package com.budwk.app.sys.controller; import cn.dev33.satoken.annotation.SaCheckLogin; -import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.http.useragent.UserAgent; import cn.hutool.http.useragent.UserAgentUtil; import cn.hutool.json.JSONObject; import com.budwk.app.base.constant.RoleConstant; import com.budwk.app.base.result.Result; -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_menu; +import com.budwk.app.sys.services.SysHomeActivityService; import com.budwk.app.sys.services.SysHomeNewsService; import com.budwk.app.sys.services.SysMenuService; 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.activity.basic.service.ActivityBasicScopeService; import io.swagger.annotations.ApiOperation; import org.nutz.dao.Cnd; import org.nutz.dao.Dao; 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.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.IocBean; -import org.nutz.lang.Lang; import org.nutz.lang.Strings; -import org.nutz.lang.util.NutMap; import org.nutz.log.Log; import org.nutz.log.Logs; import org.nutz.mvc.annotation.At; @@ -52,7 +47,7 @@ public class SysHomeController { @Inject private SysUserService sysUserService; @Inject - private ActivityBasicScopeService activityBasicScopeService; + private SysHomeActivityService sysHomeActivityService; @Inject private SysHomeNewsService sysHomeNewsService; @@ -201,71 +196,16 @@ public class SysHomeController { } + /** + * includeEnded可选:true供PC首页展示已结束活动,缺省false保持既有调用行为。 + * 返回Result,data为当前登录用户可见的活动实体列表,含名称、封面、链接及起止时间,不含内部SQL和类路径。 + */ @At @SaCheckLogin @ApiOperation("首页推送活动") @Ok("json") - public Result listHomeActivity() { - FieldFilter fieldFilter = FieldFilter.locked(Sys_home_activity.class, "classPath"); - List list = Daos.ext(dao, fieldFilter).query(Sys_home_activity.class, Cnd.where("enable", "=", 1).desc("top")); - String userId = SecurityUtil.getUserId(); - List allowActivityList = new ArrayList<>(); - // 今天的时间 - Date today = DateUtil.date(); - for (Sys_home_activity activity : list) { - // 该活动的时间范围 - Date startDate = activity.getStartDate(); - Date endDate = activity.getEndDate(); - // 判断当前时间是否在活动范围内,不在就过滤掉 -// if (!DateUtil.isIn(today, startDate, endDate)) { -// continue; -// } - // 只过滤结束的 - if (DateUtil.compare(today, endDate) > 0) { - continue; - } - Integer allowUserGroupId = activity.getAllowUserGroupId(); - String allowUserSql = activity.getAllowUserSql(); - activity.setAllowUserSql(null); - if (allowUserGroupId == null && StrUtil.isBlank(allowUserSql)) { - allowActivityList.add(activity); - continue; - } - - if (allowUserGroupId != null) { - if (activityBasicScopeService.isUserInGroup(allowUserGroupId, userId)) { - allowActivityList.add(activity); - continue; - } - } - - if (StrUtil.isNotBlank(allowUserSql)) { - Sql sql = Sqls.create(allowUserSql).setParam("userId", userId); - sql.setCallback(Sqls.callback.map()); - dao.execute(sql); - NutMap nutMap = (NutMap) sql.getResult(); - if (Lang.isNotEmpty(nutMap) && StrUtil.isNotBlank(nutMap.getString("userId"))) { - allowActivityList.add(activity); - } - } - } - return Result.success(allowActivityList); -// -// -// -// -// FieldFilter fieldFilter = FieldFilter.locked(Sys_home_activity.class, "allowUserSql|classPath"); -// List list = Daos.ext(dao, fieldFilter).query(Sys_home_activity.class, Cnd.where("enable", "=", 1).desc("top")); -// -// List menus = sysUserService.getMenus(SecurityUtil.getUserId()); -// Set hrefs = menus.stream().map(Sys_menu::getHref).collect(Collectors.toSet()); -// -// List hasPermisiionList = list.stream().filter(activity -> { -// String url = activity.getUrl(); -// String h5Url = activity.getH5Url(); -// return hrefs.contains(url) || hrefs.contains(h5Url); -// }).toList().stream().toList(); -// return Result.success(hasPermisiionList); + public Result listHomeActivity(@Param(value = "includeEnded", df = "false") boolean includeEnded) { + return Result.success(sysHomeActivityService.listHomeActivity(SecurityUtil.getUserId(), includeEnded)); } @At diff --git a/src/main/java/com/budwk/app/sys/controller/SysUnitController.java b/src/main/java/com/budwk/app/sys/controller/SysUnitController.java index b731785..d0972e4 100644 --- a/src/main/java/com/budwk/app/sys/controller/SysUnitController.java +++ b/src/main/java/com/budwk/app/sys/controller/SysUnitController.java @@ -22,7 +22,6 @@ import com.budwk.app.sys.services.SysDictService; import com.budwk.app.sys.services.SysRoleService; import com.budwk.app.sys.services.SysUnitService; import com.budwk.app.sys.services.SysUserService; -import com.budwk.app.sys.utils.DataCenterUtil; import com.budwk.app.sys.views.View_user; import com.budwk.app.web.commons.auth.utils.AuthUtil; import com.budwk.app.web.commons.auth.utils.SecurityUtil; @@ -215,9 +214,9 @@ public class SysUnitController { if (AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name())) { Cnd cnd = Cnd.NEW(); if (Strings.isBlank(pid)) { - cnd.and("id", "=", DataCenterUtil.findRootUnit(sysUnitService.query(Cnd.NEW())).getId()); + cnd.and("parentId", "=", "").or("parentId", "is", null); } else { - cnd.and("parentId", "=", pid).and("id", "<>", pid); + cnd.and("parentId", "=", pid); } cnd.asc("location").asc("path"); list = sysUnitService.query(cnd); @@ -228,9 +227,9 @@ public class SysUnitController { } else { Cnd cnd = Cnd.NEW(); if (Strings.isBlank(pid)) { - cnd.and("id", "=", DataCenterUtil.findRootUnit(sysUnitService.query(Cnd.NEW())).getId()); + cnd.and("parentId", "=", "").or("parentId", "is", null); } else { - cnd.and("parentId", "=", pid).and("id", "<>", pid); + cnd.and("parentId", "=", pid); } cnd.asc("location").asc("path"); list = sysUnitService.query(cnd); @@ -259,9 +258,9 @@ public class SysUnitController { if (StpUtil.hasRole("SYSADMIN")) { Cnd cnd = Cnd.NEW(); if (Strings.isBlank(pid)) { - cnd.and("id", "=", DataCenterUtil.findRootUnit(sysUnitService.query(Cnd.NEW())).getId()); + cnd.and("parentId", "=", "").or("parentId", "is", null); } else { - cnd.and("parentId", "=", pid).and("id", "<>", pid); + cnd.and("parentId", "=", pid); } cnd.asc("location").asc("path"); list = sysUnitService.query(cnd); @@ -272,9 +271,9 @@ public class SysUnitController { } else { Cnd cnd = Cnd.NEW(); if (Strings.isBlank(pid)) { - cnd.and("id", "=", DataCenterUtil.findRootUnit(sysUnitService.query(Cnd.NEW())).getId()); + cnd.and("parentId", "=", "").or("parentId", "is", null); } else { - cnd.and("parentId", "=", pid).and("id", "<>", pid); + cnd.and("parentId", "=", pid); } cnd.asc("location").asc("path"); list = sysUnitService.query(cnd); @@ -296,27 +295,37 @@ public class SysUnitController { } } + /** + * 获取以指定单位为根的单位树。 + * + * @param pid 根单位 ID;未传时使用系统根单位 0 + * @param req 当前 HTTP 请求 + * @return Result 对象;code 为 0 时 data 为树数组,节点包含 id、parentId、name、children、unitTypeCode; + * unitTypeCode 为 0 表示其他类、1 表示部门类,历史空值仅在返回时按 0 展示 + */ @At("/tree") @Ok("json") @SaCheckLogin public Object tree(@Param("pid") String pid, HttpServletRequest req) { try { + String rootId = StrUtil.blankToDefault(pid, "0"); String virtualRootId = "root"; Cnd cnd = Cnd.NEW(); cnd.asc("unitcode"); List list = sysUnitService.query(cnd); - String rootId = StrUtil.isBlank(pid) ? DataCenterUtil.findRootUnit(list).getId() : pid; List> nodeList = CollUtil.newArrayList(); for (int i = 0; i < list.size(); i++) { Sys_unit unit = list.get(i); /* - * 动态查询根挂到虚拟根下,兼容根节点父级为空或指向自身的数据。 + * 单位根节点存在 id 与 parentId 都为 0 的自引用数据。 + * 构建树时把当前查询根挂到虚拟根下,避免“中国地质大学”和 parentId=0 的学院被构造成同级。 */ String parentId = rootId.equals(unit.getId()) ? virtualRootId : unit.getParentId(); nodeList.add(new TreeNode<>(unit.getId(), parentId, unit.getName(), i) .setExtra( Map.of( + // 历史手工新建单位可能未设置类型,展示时按其他类处理,避免空值使整棵树加载失败。 "unitTypeCode", unit.getUnitTypeCode() == null ? 0 : unit.getUnitTypeCode() ) )); @@ -324,23 +333,37 @@ public class SysUnitController { List> treeList = TreeUtil.build(nodeList, virtualRootId); return Result.success(treeList); } catch (Exception e) { + log.error("加载单位树失败", e); return Result.error(); } } + /** + * 新建当前单位的下设单位。 + * + * @param unit 表单数据;name 为单位名称,unitcode 为唯一编码,unitLevel 为单位等级,unionId 为所属工会; + * unitTypeCode 为部门类别,0 表示其他类、1 表示部门类,未传时由 service 按其他类保存 + * @param parentId 当前选中上级单位的 ID,必须对应已存在的单位 + * @param req 当前 HTTP 请求 + * @return Result 对象;code 为 0 表示保存成功,非 0 表示失败,msg 为提示信息,data 为空 + */ @At @Ok("json") + @Aop(TransAop.READ_COMMITTED) @SaCheckPermission("sys.manager.unit.add") @SLog(tag = "新建单位", msg = "单位名称:${args[0].name}") public Object addDo(@Param("..") Sys_unit unit, @Param("parentId") String parentId, HttpServletRequest req) { try { - if ("root".equals(parentId)) { - parentId = ""; + if (Strings.isBlank(unit.getName()) || Strings.isBlank(unit.getUnitcode()) || Strings.isBlank(parentId) || "root".equals(parentId)) { + return Result.error("单位名称、单位编码和上级单位不能为空"); } unit.setCreatedBy(SecurityUtil.getUserId()); sysUnitService.save(unit, parentId); return Result.success(); + } catch (BaseException e) { + return Result.error(e.getMessage()); } catch (Exception e) { + log.error("新建单位失败", e); return Result.error(); } } diff --git a/src/main/java/com/budwk/app/zhgh/activity/basic/service/ActivityBasicUnionService.java b/src/main/java/com/budwk/app/zhgh/activity/basic/service/ActivityBasicUnionService.java new file mode 100644 index 0000000..c5b7682 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/activity/basic/service/ActivityBasicUnionService.java @@ -0,0 +1,35 @@ +package com.budwk.app.zhgh.activity.basic.service; + +import com.budwk.app.base.service.BaseService; +import com.budwk.app.zhgh.activity.basic.models.ActivityBasicUnion; +import org.nutz.lang.util.NutMap; + +/** + * 活动工会资料同步及组成单位维护,保留活动模块独立维护的临时分工会。 + */ +public interface ActivityBasicUnionService extends BaseService { + + /** + * 无请求参数,按 sys_union.id 新增缺少的活动工会,仅更新已有非临时工会的名称,不删除记录。 + * + * @return NutMap;insertCount 为新增工会数,updateCount 为名称实际变更的工会数 + */ + NutMap syncUnions(); + + /** + * 获取全部二级活动单位,不按现有分工会归属过滤候选项。 + * + * @param unionId 当前活动分工会ID,用于回显已选组成单位 + * @return NutMap;allUnits 为二级单位列表,selectUnitIds 为当前分工会已选二级单位ID列表 + */ + NutMap getPartUnitTransferData(String unionId); + + /** + * 设置当前活动分工会的组成单位,选中单位允许从其他活动分工会转入。 + * + * @param unionId 当前活动分工会ID + * @param unitIds 二级活动单位ID数组,传空数组或 null 表示清空当前工会组成单位 + * @return 无返回值;包含不存在或非二级单位的ID时抛出异常,不修改归属 + */ + void setPartUnits(String unionId, String[] unitIds); +} diff --git a/src/main/java/com/budwk/app/zhgh/activity/basic/service/ActivityBasicUnitService.java b/src/main/java/com/budwk/app/zhgh/activity/basic/service/ActivityBasicUnitService.java new file mode 100644 index 0000000..cde2556 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/activity/basic/service/ActivityBasicUnitService.java @@ -0,0 +1,19 @@ +package com.budwk.app.zhgh.activity.basic.service; + +import com.budwk.app.base.service.BaseService; +import com.budwk.app.zhgh.activity.basic.models.ActivityBasicUnit; +import org.nutz.lang.util.NutMap; + +/** + * 活动单位基础资料同步,活动工会归属仍由活动模块独立维护。 + */ +public interface ActivityBasicUnitService extends BaseService { + + /** + * 将系统单位同步到活动单位,不删除活动单位,不覆盖已有活动单位的工会归属。 + * + * @return NutMap;insertCount 为新增单位数,updateCount 为更新基础资料的已有单位数, + * pendingUnionCount 为新增单位中未能匹配活动工会、需要配置归属的数量 + */ + NutMap syncUnits(); +} diff --git a/src/main/java/com/budwk/app/zhgh/activity/basic/service/impl/ActivityBasicUnionServiceImpl.java b/src/main/java/com/budwk/app/zhgh/activity/basic/service/impl/ActivityBasicUnionServiceImpl.java new file mode 100644 index 0000000..65a6f9a --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/activity/basic/service/impl/ActivityBasicUnionServiceImpl.java @@ -0,0 +1,115 @@ +package com.budwk.app.zhgh.activity.basic.service.impl; + +import cn.hutool.core.util.StrUtil; +import com.budwk.app.base.exception.BaseException; +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.activity.basic.models.ActivityBasicUnion; +import com.budwk.app.zhgh.activity.basic.models.ActivityBasicUnit; +import com.budwk.app.zhgh.activity.basic.service.ActivityBasicUnionService; +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.ioc.aop.Aop; +import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.lang.util.NutMap; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +@IocBean(args = {"refer:dao"}) +public class ActivityBasicUnionServiceImpl extends BaseServiceImpl implements ActivityBasicUnionService { + + public ActivityBasicUnionServiceImpl(Dao dao) { + super(dao); + } + + /** + * 无请求参数,按来源工会ID同步全部工会组织;返回新增数和名称实际变更数。 + * 已有工会只更新名称及修改审计信息,不覆盖编码、联系方式、校区、组成单位和管理员关系。 + */ + @Override + @Aop(TransAop.READ_COMMITTED) + public NutMap syncUnions() { + List sourceUnions = dao().query(Sys_union.class, Cnd.NEW()); + Map existingUnions = dao().query(ActivityBasicUnion.class, Cnd.NEW()).stream() + .collect(Collectors.toMap(ActivityBasicUnion::getId, union -> union)); + String operatorId = SecurityUtil.getUserId(); + long updateTime = System.currentTimeMillis(); + int insertCount = 0; + int updateCount = 0; + + for (Sys_union sourceUnion : sourceUnions) { + if (StrUtil.isBlank(sourceUnion.getId())) { + throw new BaseException("工会组织存在空ID,本次同步已取消,请先检查工会组织资料"); + } + ActivityBasicUnion existingUnion = existingUnions.get(sourceUnion.getId()); + if (existingUnion == null) { + // 按列插入以避开实体UUID生成拦截器,保留来源ID,保证重复同步不会重复新增。 + dao().insert(ActivityBasicUnion.class, Chain.make("id", sourceUnion.getId()) + .add("name", sourceUnion.getName()).add("unionCode", sourceUnion.getUnionCode()) + .add("telephone", sourceUnion.getTelephone()).add("campus", sourceUnion.getCampus()) + .add("source", "sys").add("createdBy", operatorId).add("createdAt", updateTime) + .add("updatedBy", operatorId).add("updatedAt", updateTime).add("delFlag", false)); + insertCount++; + } else if (!"activity".equals(existingUnion.getSource()) + && !Objects.equals(existingUnion.getName(), sourceUnion.getName())) { + // 临时分工会即使与来源ID意外相同也不覆盖;来源中已不存在的工会自然保留。 + dao().update(ActivityBasicUnion.class, Chain.make("name", sourceUnion.getName()) + .add("updatedBy", operatorId).add("updatedAt", updateTime), + Cnd.where("id", "=", sourceUnion.getId())); + updateCount++; + } + } + return NutMap.NEW().addv("insertCount", insertCount).addv("updateCount", updateCount); + } + + /** + * 按单位层级查询全部二级活动单位,单位类别和现有工会归属不影响候选范围。 + * + * @param unionId 当前活动分工会ID,仅用于计算回显的选中项 + * @return NutMap;allUnits 为候选单位实体列表,selectUnitIds 为当前工会已选二级单位ID列表 + */ + @Override + public NutMap getPartUnitTransferData(String unionId) { + List units = dao().query(ActivityBasicUnit.class, + Cnd.where("unitLevel", "=", 2).asc("unitcode")); + List selectUnitIds = units.stream() + .filter(unit -> Objects.equals(unit.getUnionId(), unionId)) + .map(ActivityBasicUnit::getId).toList(); + return NutMap.NEW().addv("selectUnitIds", selectUnitIds).addv("allUnits", units); + } + + /** + * 校验全部选中单位均为二级单位后,替换当前活动分工会的组成单位关系。 + * + * @param unionId 当前活动分工会ID + * @param unitIds 二级活动单位ID数组,空数组或 null 表示清空组成单位 + * @return 无返回值;无效单位参数在修改归属之前抛出异常 + */ + @Override + @Aop(TransAop.READ_COMMITTED) + public void setPartUnits(String unionId, String[] unitIds) { + List selectedIds = unitIds == null ? List.of() : Arrays.stream(unitIds).distinct().toList(); + if (!selectedIds.isEmpty()) { + // 必须先完整校验再解除原归属,防止提交三级、空ID或不存在的单位时丢失已有组成单位。 + if (selectedIds.stream().anyMatch(StrUtil::isBlank) + || dao().count(ActivityBasicUnit.class, + Cnd.where("id", "in", selectedIds).and("unitLevel", "=", 2)) != selectedIds.size()) { + throw new BaseException("组成单位只能选择有效的二级单位,请刷新后重新选择"); + } + } + // 保存时替换当前组成单位,历史误选的三级单位也随本次重新设置解除关联。 + dao().update(ActivityBasicUnit.class, Chain.make("unionId", null), Cnd.where("unionId", "=", unionId)); + if (!selectedIds.isEmpty()) { + // 允许将已属于其他活动分工会的二级单位转入,不增加原工会归属限制。 + dao().update(ActivityBasicUnit.class, Chain.make("unionId", unionId), + Cnd.where("id", "in", selectedIds).and("unitLevel", "=", 2)); + } + } +} diff --git a/src/main/java/com/budwk/app/zhgh/activity/basic/service/impl/ActivityBasicUnitServiceImpl.java b/src/main/java/com/budwk/app/zhgh/activity/basic/service/impl/ActivityBasicUnitServiceImpl.java new file mode 100644 index 0000000..8b44385 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/activity/basic/service/impl/ActivityBasicUnitServiceImpl.java @@ -0,0 +1,93 @@ +package com.budwk.app.zhgh.activity.basic.service.impl; + +import cn.hutool.core.util.StrUtil; +import com.budwk.app.base.exception.BaseException; +import com.budwk.app.base.service.impl.BaseServiceImpl; +import com.budwk.app.sys.models.Sys_unit; +import com.budwk.app.web.commons.auth.utils.SecurityUtil; +import com.budwk.app.zhgh.activity.basic.models.ActivityBasicUnion; +import com.budwk.app.zhgh.activity.basic.models.ActivityBasicUnit; +import com.budwk.app.zhgh.activity.basic.service.ActivityBasicUnitService; +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.ioc.aop.Aop; +import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.lang.util.NutMap; + +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +@IocBean(args = {"refer:dao"}) +public class ActivityBasicUnitServiceImpl extends BaseServiceImpl implements ActivityBasicUnitService { + + public ActivityBasicUnitServiceImpl(Dao dao) { + super(dao); + } + + /** + * 无请求参数,按 sys_unit.id 同步全部系统单位;返回新增数、更新数和新增单位待配置活动工会数。 + * 已有记录只更新基础字段,连原有的空工会归属也保留,避免覆盖活动模块人工维护的组成单位关系。 + */ + @Override + @Aop(TransAop.READ_COMMITTED) + public NutMap syncUnits() { + List sourceUnits = dao().query(Sys_unit.class, Cnd.NEW()); + Set existingIds = dao().query(ActivityBasicUnit.class, Cnd.NEW()).stream() + .map(ActivityBasicUnit::getId).collect(Collectors.toSet()); + Set activityUnionIds = dao().query(ActivityBasicUnion.class, Cnd.NEW()).stream() + .map(ActivityBasicUnion::getId).collect(Collectors.toSet()); + int insertCount = 0; + int updateCount = 0; + int pendingUnionCount = 0; + String operatorId = SecurityUtil.getUserId(); + long updateTime = System.currentTimeMillis(); + + for (Sys_unit sourceUnit : sourceUnits) { + if (StrUtil.isBlank(sourceUnit.getId())) { + throw new BaseException("系统单位存在空ID,本次同步已取消,请先检查系统单位资料"); + } + Chain fields = buildBasicFields(sourceUnit) + .add("updatedBy", operatorId).add("updatedAt", updateTime); + if (existingIds.contains(sourceUnit.getId())) { + // 更新列中不包含 unionId、unionGroupId 和 id,保留现有归属及报名记录使用的单位ID。 + dao().update(ActivityBasicUnit.class, fields, Cnd.where("id", "=", sourceUnit.getId())); + updateCount++; + } else { + // 新单位仅继承已存在于活动工会表的归属,不能将普通工会ID直接当作有效活动工会。 + String unionId = StrUtil.isNotBlank(sourceUnit.getUnionId()) && activityUnionIds.contains(sourceUnit.getUnionId()) + ? sourceUnit.getUnionId() : null; + if (unionId == null) { + pendingUnionCount++; + } + fields.add("id", sourceUnit.getId()).add("unionId", unionId) + .add("createdBy", operatorId).add("createdAt", updateTime).add("delFlag", false); + // 按列插入避开实体的 UUID 生成拦截器,确保活动单位ID始终等于系统单位ID。 + dao().insert(ActivityBasicUnit.class, fields); + existingIds.add(sourceUnit.getId()); + insertCount++; + } + } + return NutMap.NEW().addv("insertCount", insertCount).addv("updateCount", updateCount) + .addv("pendingUnionCount", pendingUnionCount); + } + + /** + * 只列出允许从系统单位更新的基础资料;工会、小组归属和审计创建信息不属于基础同步字段。 + * 父级、路径与层级沿用源值,树展示需要的根节点适配在接口内存中完成,不改写单位关系。 + * + * @param unit sys_unit 中的完整单位记录 + * @return 可供 DAO 按列新增或更新的 Chain,不含单位ID和工会归属 + */ + private Chain buildBasicFields(Sys_unit unit) { + return Chain.make("parentId", unit.getParentId()).add("path", unit.getPath()) + .add("name", unit.getName()).add("aliasName", unit.getAliasName()).add("unitcode", unit.getUnitcode()) + .add("note", unit.getNote()).add("address", unit.getAddress()).add("telephone", unit.getTelephone()) + .add("email", unit.getEmail()).add("website", unit.getWebsite()).add("location", unit.getLocation()) + .add("hasChildren", unit.isHasChildren()).add("logo", unit.getLogo()).add("unitLevel", unit.getUnitLevel()) + .add("divisionCollegeCode", unit.getDivisionCollegeCode()).add("institutionType", unit.getInstitutionType()) + .add("unitType", unit.getUnitType()).add("unitTypeCode", unit.getUnitTypeCode()); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/activity/sports/models/ActivityResultsPrize.java b/src/main/java/com/budwk/app/zhgh/activity/sports/models/ActivityResultsPrize.java new file mode 100644 index 0000000..a892b85 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/activity/sports/models/ActivityResultsPrize.java @@ -0,0 +1,40 @@ +package com.budwk.app.zhgh.activity.sports.models; + +import com.budwk.app.base.model.BaseModel; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.nutz.dao.entity.annotation.*; +import java.math.BigDecimal; + +/** 一条获奖成绩的奖品人数归属,与积分分配独立;一人一次获奖对应一份奖品。 */ +@Data +@EqualsAndHashCode(callSuper = true) +@Table("activity_results_prize") +@Comment("活动成绩奖品人数分配") +@TableIndexes({@Index(name = "ARP_RESULT_UNION", fields = {"resultId", "prizeUnionId"}, unique = true)}) +public class ActivityResultsPrize extends BaseModel { + @Name + @ColDefine(type = ColType.VARCHAR, width = 32) + private String id; + + @Column + @Comment("成绩主记录ID") + @ColDefine(type = ColType.VARCHAR, width = 32) + private String resultId; + + @Column + @Comment("领取奖品的正式分工会ID,对应sys_union") + @ColDefine(type = ColType.VARCHAR, width = 32) + private String prizeUnionId; + + @Column + @Comment("奖品领取工会名称快照") + @ColDefine(type = ColType.VARCHAR, width = 100) + private String prizeUnionName; + + @Column + @Comment("实际获奖人数,个人成绩合计为1,团体成绩合计等于获奖总人数") + @ColDefine(type = ColType.INT) + // 接收时保留原始数值精度,避免JSON直接绑定Integer时把3.5截断为3;保存前严格转为int范围整数。 + private BigDecimal numberOfPeople; +} diff --git a/src/main/java/com/budwk/app/zhgh/activity/sports/models/ActivityResultsScore.java b/src/main/java/com/budwk/app/zhgh/activity/sports/models/ActivityResultsScore.java new file mode 100644 index 0000000..895e364 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/activity/sports/models/ActivityResultsScore.java @@ -0,0 +1,40 @@ +package com.budwk.app.zhgh.activity.sports.models; + +import com.budwk.app.base.model.BaseModel; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.nutz.dao.entity.annotation.*; + +import java.math.BigDecimal; + +/** 一次团体获奖在正式分工会之间的积分分配,不代表新增获奖或人数分配。 */ +@Data +@EqualsAndHashCode(callSuper = true) +@Table("activity_results_score") +@Comment("活动成绩积分分配") +@TableIndexes({@Index(name = "ARS_RESULT_UNION", fields = {"resultId", "scoreUnionId"}, unique = true)}) +public class ActivityResultsScore extends BaseModel { + @Name + @ColDefine(type = ColType.VARCHAR, width = 32) + private String id; + + @Column + @Comment("成绩主记录ID") + @ColDefine(type = ColType.VARCHAR, width = 32) + private String resultId; + + @Column + @Comment("正式计分工会ID,对应sys_union") + @ColDefine(type = ColType.VARCHAR, width = 32) + private String scoreUnionId; + + @Column + @Comment("计分工会名称快照") + @ColDefine(type = ColType.VARCHAR, width = 100) + private String scoreUnionName; + + @Column + @Comment("分配积分,合计必须等于主成绩总积分") + @ColDefine(customType = "decimal(10,2)") + private BigDecimal integral; +} diff --git a/src/main/java/com/budwk/app/zhgh/club/controller/statistics/ClubMemberStatisticsController.java b/src/main/java/com/budwk/app/zhgh/club/controller/statistics/ClubMemberStatisticsController.java new file mode 100644 index 0000000..513dd26 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/club/controller/statistics/ClubMemberStatisticsController.java @@ -0,0 +1,104 @@ +package com.budwk.app.zhgh.club.controller.statistics; + +import cn.dev33.satoken.annotation.SaCheckPermission; +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; +import com.budwk.app.flow.enums.ProcessInstanceStateEnum; +import com.budwk.app.zhgh.club.service.SysClubService; +import com.budwk.app.zhgh.club.service.impl.SysClubUserServiceImpl; +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.json.Json; +import org.nutz.lang.util.NutMap; +import org.nutz.mvc.annotation.At; +import org.nutz.mvc.annotation.Ok; + +import javax.validation.Valid; +import java.util.List; + +@IocBean +@Ok("json") +@At("/platform/club/statistics/member") +public class ClubMemberStatisticsController { + + @Inject + private SysClubService sysClubService; + + @At("") + @Ok("beetl:/platform/zhgh/club/statistics/member.html") + @SaCheckPermission("club.statistics.member") + public void index() { + } + + @At + @SaCheckPermission("club.statistics.member") + public Result pageData(@Valid PageForm pageForm, String clubId, String userId, String keyWord, String unitName, String userState, String sex) { + Sql sql = generateSql(false, clubId, userId, keyWord, unitName, userState, sex); + Pagination pagination = sysClubService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql); + List members = pagination.getList(NutMap.class); + for (NutMap member : members) { + List roles = StrUtil.isBlank(member.getString("roleCode")) + ? List.of() : Json.fromJsonAsList(String.class, member.getString("roleCode")); + member.put("roleName", SysClubUserServiceImpl.convertRoleName(roles)); + member.put("roleCode", roles); + } + pagination.setList(members); + return Result.success(pagination); + } + + @At + @SaCheckPermission("club.statistics.member") + public Result clubOptions() { + return Result.success(sysClubService.listMap(generateSql(true, null, null, null, null, null, null))); + } + + private Sql generateSql(boolean options, String clubId, String userId, String keyWord, String unitName, String userState, String sex) { + String fields = options ? "DISTINCT club.id, club.clubCode, club.clubName" : """ + c.id, c.userId, club.clubCode, club.clubName, + COALESCE(NULLIF(u.username, ''), c.userName) AS userName, + COALESCE(NULLIF(u.loginname, ''), c.loginName) AS loginName, + u.sex, COALESCE(NULLIF(u.mobile, ''), c.mobile) AS mobile, + u.unitname AS unitName, u.userState, c.roleCode, c.joinTime + """; + Sql sql = Sqls.create("SELECT " + fields + "\n" + """ + FROM sys_club club + INNER JOIN club_user c ON c.clubId = club.id + LEFT JOIN vw_user u ON u.id = c.userId + $condition + AND EXISTS ( + SELECT 1 FROM wf_process_instance registerInstance + INNER JOIN wf_process_define registerDefine + ON registerDefine.id = registerInstance.processDefineId + WHERE registerInstance.businessNo = club.id + AND registerInstance.state = @finishedState + AND registerInstance.delFlag = 0 + AND registerDefine.name = @registerProcessKey + AND registerDefine.delFlag = 0 + ) + ORDER BY club.clubCode, club.id + """ + (options ? "" : ", c.id")); + Cnd cnd = Cnd.where("club.dismiss", "=", false); + cnd.and("club.delFlag", "=", false); + cnd.and("c.delFlag", "=", false); + cnd.andEX("club.id", "=", clubId); + cnd.andEX("c.userId", "=", userId); + cnd.andEX("u.userState", "=", userState); + cnd.andEX("u.sex", "=", sex); + if (StrUtil.isNotBlank(unitName)) { + cnd.and("u.unitname", "=", unitName); + } + if (StrUtil.isNotBlank(keyWord)) { + cnd.and(Cnd.exps("COALESCE(NULLIF(u.username, ''), c.userName)", "like", "%" + keyWord + "%") + .or("COALESCE(NULLIF(u.loginname, ''), c.loginName)", "like", "%" + keyWord + "%")); + } + sql.setCondition(cnd); + sql.setParam("finishedState", ProcessInstanceStateEnum.FINISHED.getCode()); + sql.setParam("registerProcessKey", "XHZC"); + return sql; + } +} diff --git a/src/main/java/com/budwk/app/zhgh/club/controller/statistics/ClubStatisticsController.java b/src/main/java/com/budwk/app/zhgh/club/controller/statistics/ClubStatisticsController.java index acd6b01..2d5822e 100644 --- a/src/main/java/com/budwk/app/zhgh/club/controller/statistics/ClubStatisticsController.java +++ b/src/main/java/com/budwk/app/zhgh/club/controller/statistics/ClubStatisticsController.java @@ -129,7 +129,21 @@ public class ClubStatisticsController { u.mobile, u.unitname as unitName, u.userState, - c.* + c.id, + c.clubId, + c.userId, + c.roleCode, + c.clubPosition, + c.email, + c.avatar, + c.sameTimeJoinOtherClubSituation, + c.awardsExperience, + c.joinTime, + c.createdBy, + c.createdAt, + c.updatedBy, + c.updatedAt, + c.delFlag FROM club_user c LEFT JOIN `vw_user` u ON u.id = c.userId diff --git a/src/main/resources/db/club/init_club_member_statistics_menu.sql b/src/main/resources/db/club/init_club_member_statistics_menu.sql new file mode 100644 index 0000000..201a666 --- /dev/null +++ b/src/main/resources/db/club/init_club_member_statistics_menu.sql @@ -0,0 +1,58 @@ +-- 1. 将现有“社团统计”移到新父级下面,保留原权限和角色关联。 +UPDATE sys_menu +SET parentId = 'f2b61f72eab34e139bc150a2d907c385', + path = '001100060001', + updatedAt = UNIX_TIMESTAMP(NOW()) * 1000, + permission = 'club.statistics.group' +WHERE id = 'b8817cce3ae34d49844a7ea8d8b15f3f' + AND permission = 'club.statistics'; + +-- 2. 新增“社团统计”父级菜单。 +INSERT INTO sys_menu ( + id, parentId, path, name, aliasName, type, href, target, + icon, showit, disabled, permission, location, hasChildren, + createdBy, createdAt, updatedBy, updatedAt, delFlag, + platform, moduleId, initialPinyinName, isRecommendApp, isRecommendService +) VALUES ( + 'f2b61f72eab34e139bc150a2d907c385', + '', + '00110006', + '社团统计', '', 'menu', + '', '', + '', 1, 0, 'club.statistics', 411, 1, + '', UNIX_TIMESTAMP(NOW()) * 1000, + '', UNIX_TIMESTAMP(NOW()) * 1000, 0, + 'PC', '4cfc8977507c40b3acee8443c7c5a8a3', 's', 0, 0 + ); + +-- 3. 新增“成员统计”子菜单。 +INSERT INTO sys_menu ( + id, parentId, path, name, aliasName, type, href, target, + icon, showit, disabled, permission, location, hasChildren, + createdBy, createdAt, updatedBy, updatedAt, delFlag, + platform, moduleId, initialPinyinName, isRecommendApp, isRecommendService +) VALUES ( + 'a914ce82f0764e9c8f1b35a6d2270b41', + 'f2b61f72eab34e139bc150a2d907c385', + '001100060002', + '成员统计', '', 'menu', + '/platform/club/statistics/member', 'data-pjax', + '', 1, 0, 'club.statistics.member', 412, 0, + '', UNIX_TIMESTAMP(NOW()) * 1000, + '', UNIX_TIMESTAMP(NOW()) * 1000, 0, + 'PC', '4cfc8977507c40b3acee8443c7c5a8a3', 'c', 0, 0 + ); + +-- 成员统计可查询所有社团,仅给系统管理员和校工会管理员授权。 +-- 社团统计父级 +INSERT INTO sys_role_menu (roleId, menuId) VALUES + ('419844ad90f34ca58807e84224905f27', 'f2b61f72eab34e139bc150a2d907c385'), + ('6aece46b946e4fb9ac4cb15c0cd8c3e6', 'f2b61f72eab34e139bc150a2d907c385'), + ('722d454171ac49f1bba60b7672e500ff', 'f2b61f72eab34e139bc150a2d907c385'), + ('72e17abb91cb48b4820474904e189182', 'f2b61f72eab34e139bc150a2d907c385'), + ('7f4a820d5a394bb8a0a92d6699afe7c8', 'f2b61f72eab34e139bc150a2d907c385'); + +-- 成员统计 +INSERT INTO sys_role_menu (roleId, menuId) VALUES + ('722d454171ac49f1bba60b7672e500ff', 'a914ce82f0764e9c8f1b35a6d2270b41'), + ('7f4a820d5a394bb8a0a92d6699afe7c8', 'a914ce82f0764e9c8f1b35a6d2270b41'); \ No newline at end of file diff --git a/src/main/resources/static/assets/platform/js/main.js b/src/main/resources/static/assets/platform/js/main.js deleted file mode 100644 index fdfea10..0000000 --- a/src/main/resources/static/assets/platform/js/main.js +++ /dev/null @@ -1,58 +0,0 @@ -;(function (a) { - ;(jQuery.browser = jQuery.browser || {}).mobile = - /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test( - a - ) || - /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test( - a.substr(0, 4) - ) -})(navigator.userAgent || navigator.vendor || window.opera) - -$.ajaxSetup({ - contentType: "application/x-www-form-urlencoded;charset=utf-8", - success: function (response, textStatus, jqXHR) { - if (response.code !== 0) { - // AJAX全局回调没有Vue实例上下文,使用Element全局消息组件展示业务错误。 - if (window.ELEMENT && ELEMENT.Message) { - ELEMENT.Message.error(response.msg) - } - } else { - return response - } - }, - error: function (v1) { - this.$message.error(v1.status + " " + v1.statusText) - }, - complete: function (XMLHttpRequest, textStatus) { - var sessionstatus = XMLHttpRequest.getResponseHeader("loginStatus") //通过XMLHttpRequest取得响应头,sessionstatus, - if (sessionstatus == "accessDenied") { - this.$message({ - message: "\u767b\u5f55\u5931\u6548\uff0c\u8bf7\u5237\u65b0\u9875\u9762\u91cd\u65b0\u767b\u5f55", - type: "error" - }) - } - if (sessionstatus == "unauthorized") { - this.$message({ - message: "\u6ca1\u6709\u6743\u9650", - type: "error" - }) - } - } -}) - -function pjaxReplace(url, data = {}) { - const { pathname, search } = location - if (url === pathname + search) { - return - } - $.pjax({ - url: url, - container: "#sub-app-container-main-content-body", - maxCacheLength: 0, - push: false, - replace: true, - fragment: "#sub-app-container-main-content-body", - timeout: 8000, - data - }) -} diff --git a/src/main/resources/static/components/module/activity/score/scoreTopEight.vue b/src/main/resources/static/components/module/activity/score/scoreTopEight.vue index aff0847..d8f0a51 100644 --- a/src/main/resources/static/components/module/activity/score/scoreTopEight.vue +++ b/src/main/resources/static/components/module/activity/score/scoreTopEight.vue @@ -11,7 +11,7 @@ - @@ -47,7 +47,21 @@ module.exports = { this.tableData = [] const {data} = await this.$axios.post(loc() + "/isScoreTopEight", this.Form) const table = data.score.sort(this.compare("totalScore")) - this.tableData = table.slice(0, 8) + // 按竞赛名次规则取前八:同分并列同名次,并列后名次不跳号(dense rank); + // 名次大于 8 的舍弃。不能直接 slice(0, 8),否则同分并列的工会会被切掉。 + const ranked = [] + let rank = 0 + let prevScore = null + table.forEach(v => { + // 总分带 0.5 折半小数,比较用容差,避免浮点误差误判并列 + if (prevScore === null || Math.abs(v.totalScore - prevScore) > 0.000001) { + // 每遇到一个新的分值,名次 +1;同分者沿用同一名次 + rank += 1 + prevScore = v.totalScore + } + ranked.push(Object.assign({}, v, {rank: rank})) + }) + this.tableData = ranked.filter(v => v.rank <= 8) } catch (e) { this.$message.error(e.msg) } finally { diff --git a/src/main/resources/views/layouts/v4/home/act.js b/src/main/resources/views/layouts/v4/home/act.js index 6ae7fcb..cf7882a 100644 --- a/src/main/resources/views/layouts/v4/home/act.js +++ b/src/main/resources/views/layouts/v4/home/act.js @@ -15,7 +15,7 @@ const act = {
暂无图片
- + {{getStatusText(activity)}} @@ -76,9 +76,9 @@ const act = { if (!this.swiper) return this.swiper.isEnd ? this.swiper.slideTo(0) : this.swiper.slideNext() }, - /*查询活动*/ + // PC首页包含已结束活动;接口data为按未结束在前、已结束在后排列的活动实体数组。 listAct() { - this.$axios.post("/platform/home/listHomeActivity").then((res) => { + this.$axios.post("/platform/home/listHomeActivity", {includeEnded: true}).then((res) => { if (res.code === 0) { // for (let i = 0; i < 10; i++) { // this.actList = this.actList.concat(res.data) @@ -149,7 +149,7 @@ const act = { // 活动状态 getStatusText(item) { if (this.$moment().unix() > this.$moment(item.endDate).unix()) { - return "已结束" + return "报名已结束" } else if (this.$moment().unix() < this.$moment(item.startDate).unix()) { return "未开始" } else { diff --git a/src/main/resources/views/platform/sys/union/branchUnionGroupManage.js b/src/main/resources/views/platform/sys/union/branchUnionGroupManage.js index 1e28984..a9353d5 100644 --- a/src/main/resources/views/platform/sys/union/branchUnionGroupManage.js +++ b/src/main/resources/views/platform/sys/union/branchUnionGroupManage.js @@ -40,7 +40,7 @@ const branchUnionGroupManage = { + :close-on-click-modal="false" @close="clearUserOptions"> { - if (res.code === 0) { + if (res.code === 0 && this.dialogFormVisible && requestId === this.userOptionsRequestId) { this.userOptions = res.data } }) diff --git a/src/main/resources/views/platform/sys/unit/index.html b/src/main/resources/views/platform/sys/unit/index.html index 22d5377..4bd3a4c 100644 --- a/src/main/resources/views/platform/sys/unit/index.html +++ b/src/main/resources/views/platform/sys/unit/index.html @@ -77,6 +77,7 @@ layout("/layouts/platform.html"){
new Vue({ el: "#app", - data: function () { + data() { return { unitTabActive: "threeUnitManage", filterText: null, currentTreeNode: null, currentTreeData: null, defaultExpandedKeys: [], + treeLoading: false, treeData: [] } }, @@ -173,26 +175,45 @@ layout("/layouts/platform.html"){ }, methods: { getTreeData() { - this.$axios.post(loc() + "/tree").then((res) => { - this.treeData = res.data - if (this.treeData) { - this.currentTreeData = this.treeData[0] - this.defaultExpandedKeys = this.treeData.length > 0 ? [this.treeData[0].id] : [] + this.$set(this, "treeLoading", true) + // tree 返回单位树数组;失败时保留现有内容,成功后按 ID 恢复当前单位。 + return this.$axios.post(loc() + "/tree").then((res) => { + if (res.code !== 0 || !Array.isArray(res.data)) return + const selectedId = this.currentTreeData?.id + this.$set(this, "treeData", res.data) + this.$set(this, "defaultExpandedKeys", res.data.length > 0 ? [res.data[0].id] : []) + if (res.data.length === 0) { + this.$set(this, "currentTreeData", null) + this.$set(this, "currentTreeNode", null) + return } + this.$nextTick(() => { + const tree = this.$refs.treeRef + const node = (selectedId && tree.getNode(selectedId)) || tree.getNode(res.data[0].id) + // 将选中节点的祖先展开,避免刷新后当前单位隐藏在折叠的分支中。 + let parent = node.parent + while (parent && parent.level > 0) { + parent.expanded = true + parent = parent.parent + } + tree.setCurrentKey(node.data.id) + tree.filter(this.filterText) + this.treeNodeClick(node.data, node) + }) + }).finally(() => { + this.$set(this, "treeLoading", false) }) }, treeNodeClick(data, node) { - this.currentTreeData = data - this.currentTreeNode = node - this.$nextTick(()=>{ - if (data.unitTypeCode==1) { - this.$refs.bmlRef.pageData() - this.$refs.userRef.pageData() - this.$refs.leaderRef.pageData() - } else { - this.$refs.bzlRef.pageData() - } - }) + this.$set(this, "currentTreeData", data) + this.$set(this, "currentTreeNode", node) + // 单位列表由 currentData 监听刷新,部门类另外更新人员和干部页签。 + this.$nextTick(() => { + if (data.unitTypeCode == 1) { + this.$refs.userRef.pageData() + this.$refs.leaderRef.pageData() + } + }) }, filterNode(value, data, node) { if (!value) return true diff --git a/src/main/resources/views/platform/sys/unit/unitManage.js b/src/main/resources/views/platform/sys/unit/unitManage.js index 956d398..b09ebb5 100644 --- a/src/main/resources/views/platform/sys/unit/unitManage.js +++ b/src/main/resources/views/platform/sys/unit/unitManage.js @@ -11,7 +11,7 @@ const UNIT_MANAGE_TEMPLATE = { 新建单位 - + @@ -75,8 +75,8 @@ const UNIT_MANAGE_TEMPLATE = { - 取 消 - 确 定 + 取 消 + 确 定
@@ -110,6 +110,8 @@ const UNIT_MANAGE_TEMPLATE = { audit: false }, tableData: [], + tableLoading: false, + formLoading: false, dialogVisible: false, formData: {}, formRules: { @@ -128,96 +130,130 @@ const UNIT_MANAGE_TEMPLATE = { } }, computed: {}, + watch: { + // 首次进入、切换节点以及刷新树后,均按最新上级单位从第一页重新查询。 + currentData: { + immediate: true, + handler() { + this.doSearch() + } + } + }, methods: { doSearch() { - this.pageForm.pageNumber = 1 - this.pageData() + this.$set(this.pageForm, "pageNumber", 1) + return this.pageData() }, openAdd() { - this.dialogVisible = true - this.formData = {} //打开新增窗口,表单先清空 - this.parentUnit = [] - this.options = [] + // 上级关系取当前单位 ID,与用户是否点击过树节点无关;手工新增默认其他类。 + this.$set(this, "formData", { + parentId: this.currentData.id, + unitLevel: this.unitLevel, + unitTypeCode: 0 + }) + this.$set(this, "parentUnit", []) + this.$set(this, "options", []) + this.$set(this, "dialogVisible", true) + this.$nextTick(() => { + this.$refs.form.clearValidate() + }) + }, + closeDialog() { + // 提交过程中保留表单,等待请求完成后再允许取消。 + if (!this.formLoading) { + this.$set(this, "dialogVisible", false) + } }, doHandle() { - this.$confirm("确定要提交吗?", "提示", { + if (this.formLoading) return + this.$refs.form.validate((valid) => { + if (!valid) return + return this.$confirm("确定要提交吗?", "提示", { + confirmButtonText: "确定", + cancelButtonText: "取消", + type: "warning" + }).then(() => { + if (this.formLoading) return + const url = this.formData.id ? "/platform/sys/unit/editDo" : "/platform/sys/unit/addDo" + // 新建始终携带当前上级;编辑沿用回显的原上级,不在保存时意外变更层级。 + if (!this.formData.id) { + this.$set(this.formData, "parentId", this.currentData.id) + } + this.$set(this, "formLoading", true) + return this.$axios.post(url, this.formData).then((resp) => { + if (resp.code === 0) { + this.$message.success(resp.msg) + this.$set(this, "dialogVisible", false) + // 父页面刷新并恢复当前树节点,currentData 监听统一刷新列表,避免重复查询。 + this.$emit("refresh", null) + } else { + this.$message.warning(resp.msg) + } + }).finally(() => { + this.$set(this, "formLoading", false) + }) + }).catch(() => { + // 取消确认直接结束;请求错误由全局 axios 拦截器提示,保留表单供重试。 + }) + }) + }, + // id 为待编辑单位 ID,接口 data 为单位实体;保留已有部门类别及原上级关系。 + openEdit(id) { + return this.$axios.post("/platform/sys/unit/edit/" + id).then((resp) => { + if (resp.code === 0) { + this.$set(this, "formData", resp.data) + this.$set(this, "dialogVisible", true) + this.$nextTick(() => { + this.$refs.form.clearValidate() + }) + } else { + this.$message.warning(resp.msg) + } + }) + }, + doDelete(id) { + return this.$confirm("此操作将永久删除, 是否继续?", "提示", { confirmButtonText: "确定", cancelButtonText: "取消", type: "warning" }).then(() => { - const self = this - const url = this.formData.id ? "/platform/sys/unit/editDo" : "/platform/sys/unit/addDo" - this.$refs["form"].validate(async function (valid) { - if (valid) { - if (self.currentNode) { - self.formData.parentId = self.currentData.id - } - const resp = await self.$axios.post(url, self.formData) - if (resp.code === 0) { - self.$message.success(resp.msg) - self.doSearch() - self.dialogVisible = false - self.$emit("refresh", null) - } else { - self.$message.warning(resp.msg) - } + return this.$axios.post("/platform/sys/unit/delete/" + id).then((resp) => { + if (resp.code === 0) { + this.$message.success(resp.msg) + this.$emit("refresh", null) + } else { + this.$message.warning(resp.msg) } }) }) }, - async openEdit(id) { - console.log(this.currentData) - const resp = await this.$axios.post("/platform/sys/unit/edit/" + id) - if (resp.code === 0) { - this.formData = resp.data - this.dialogVisible = true - } else { - this.$message.warning(resp.msg) - } - }, - async doDelete(id) { - const confirm = await this.$confirm("此操作将永久删除, 是否继续?", "提示", { - confirmButtonText: "确定", - cancelButtonText: "取消", - type: "warning" - }) - if (confirm === "confirm") { - const resp = await this.$axios.post("/platform/sys/unit/delete/" + id) - if (resp.code === 0) { - this.$message.success(resp.msg) - this.doSearch() - this.$emit("refresh", null) - } else { - this.$message.warning(resp.msg) - } - } - }, pageNumberChange(val) { - this.pageForm.pageNumber = val + this.$set(this.pageForm, "pageNumber", val) this.pageData() }, pageSizeChange(val) { - this.pageForm.pageSize = val + this.$set(this.pageForm, "pageSize", val) this.pageData() }, pageData() { - this.pageForm.unitId = this.currentData.id - // this.pageForm.unitLevel = this.unitLevel - this.$axios.post(loc() + "/pageData", this.pageForm).then((res) => { + if (!this.currentData || !this.currentData.id) return + // unitId 限定当前上级,返回 data.list 为下设单位,data.totalCount 为分页总数。 + this.$set(this.pageForm, "unitId", this.currentData.id) + this.$set(this, "tableLoading", true) + return this.$axios.post(loc() + "/pageData", this.pageForm).then((res) => { if (res.code === 0) { - this.tableData = res.data.list - this.pageForm.totalCount = res.data.totalCount + this.$set(this, "tableData", res.data.list) + this.$set(this.pageForm, "totalCount", res.data.totalCount) } + }).finally(() => { + this.$set(this, "tableLoading", false) }) } }, - async created() { - this.pageData() - - if (this.$auth.hasRoleOr('SYSADMIN, SCHOOL_UNION_ADMIN')) { - this.unions = await this.$businessTool.listUnion() - } else { - this.unions = await this.$businessTool.listUnion(this.store.state.user.union.id) - } + created() { + const unionId = this.$auth.hasRoleOr('SYSADMIN, SCHOOL_UNION_ADMIN') ? null : this.store.state.user.union.id + this.$businessTool.listUnion(unionId).then((unions) => { + this.$set(this, "unions", unions) + }) } } diff --git a/src/main/resources/views/platform/zhgh/activity/basic/union/activityBasicBranchUnionManage.js b/src/main/resources/views/platform/zhgh/activity/basic/union/activityBasicBranchUnionManage.js index 583717a..2152d8b 100644 --- a/src/main/resources/views/platform/zhgh/activity/basic/union/activityBasicBranchUnionManage.js +++ b/src/main/resources/views/platform/zhgh/activity/basic/union/activityBasicBranchUnionManage.js @@ -14,6 +14,7 @@ const ACTIVITY_BASIC_BRANCH_UNION_MANAGE = { 新建临时分工会 + 更新工会 @@ -79,6 +80,7 @@ const ACTIVITY_BASIC_BRANCH_UNION_MANAGE = { mixins: [initTableMixins], data() { return { + pageDataUrl: "/platform/activity/basic/union/pageData", dialogFormVisible: false, dialogGlyFormVisible: false, formData: {}, @@ -88,10 +90,25 @@ const ACTIVITY_BASIC_BRANCH_UNION_MANAGE = { } }, methods: { - pageData() { - this.$axios.post("/platform/activity/basic/union/pageData", this.pageForm).then((res) => { - this.tableData = res.data.list - this.pageForm.totalCount = res.data.totalCount + // 同步新增工会和已有工会名称;复用表格查询及父页面刷新事件,保留现有查询和分页逻辑。 + updateUnions() { + if (this.formLoading) { + return + } + this.$set(this, "formLoading", true) + return this.$axios.post("/platform/activity/basic/union/updateUnions").then((res) => { + if (res.code !== 0) { + this.notifyWarning(res.msg) + return + } + const result = res.data + this.notifySuccess("工会更新完成:新增 " + result.insertCount + " 个,更新名称 " + result.updateCount + " 个。") + this.$emit("refresh", null) + return this.pageData() + }).catch(() => { + this.notifyError("工会更新或刷新失败,请刷新页面后核对结果") + }).finally(() => { + this.$set(this, "formLoading", false) }) }, openAdd() { diff --git a/src/main/resources/views/platform/zhgh/activity/basic/unit/index.html b/src/main/resources/views/platform/zhgh/activity/basic/unit/index.html index 40ea9a9..1559b67 100644 --- a/src/main/resources/views/platform/zhgh/activity/basic/unit/index.html +++ b/src/main/resources/views/platform/zhgh/activity/basic/unit/index.html @@ -9,10 +9,10 @@ layout("/layouts/platform.html"){
搜索 + 更新单位
@@ -104,6 +105,36 @@ layout("/layouts/platform.html"){ }, components: {}, methods: { + // 点击后同步一次系统单位;已有活动工会归属由后端保留,完成后刷新树和当前单位列表。 + updateUnits() { + if (this.formLoading) { + return + } + this.$set(this, "formLoading", true) + this.$axios.post(loc() + "/updateUnits").then((res) => { + if (res.code !== 0) { + this.notifyWarning(res.msg) + return + } + const result = res.data + let message = "单位更新完成:新增 " + result.insertCount + " 个,更新 " + result.updateCount + " 个。" + if (result.pendingUnionCount > 0) { + message += "新增单位中有 " + result.pendingUnionCount + " 个待配置活动工会,请到活动工会的组成单位中维护。" + this.notifyWarning(message) + } else { + this.notifySuccess(message) + } + return this.getTreeData().then((loaded) => { + if (loaded) { + this.doSearch() + } + }) + }).catch(() => { + this.notifyError("单位更新或刷新失败,请刷新页面后核对结果") + }).finally(() => { + this.$set(this, "formLoading", false) + }) + }, async doDelete(id) { const confirm = await this.$confirm("确定要删除该单位吗?", "提示", { confirmButtonText: "确定", @@ -122,24 +153,42 @@ layout("/layouts/platform.html"){ } }, treeNodeClick(data, node) { - this.currentTreeData = data - this.currentTreeNode = node - this.pageForm.parentId = data.id + this.$set(this, "currentTreeData", data) + this.$set(this, "currentTreeNode", node) + this.$set(this.pageForm, "parentId", data.id) this.doSearch() }, getTreeData() { - this.$axios.post(loc() + "/tree").then((res) => { - this.treeData = res.data - if (this.treeData) { - this.currentTreeData = this.treeData[0] + return this.$axios.post(loc() + "/tree").then((res) => { + if (res.code !== 0) { + this.notifyWarning(res.msg) + return false } + this.$set(this, "treeData", res.data || []) + // 同步后沿用当前选择,首次进入按真实树根查询,不再依赖旧的根单位ID常量。 + if (!this.pageForm.parentId && this.treeData.length > 0) { + this.$set(this.pageForm, "parentId", this.treeData[0].id) + } + this.$nextTick(() => { + const tree = this.$refs.treeRef + const node = tree.getNode(this.pageForm.parentId) + this.$set(this, "currentTreeNode", node || null) + this.$set(this, "currentTreeData", node ? node.data : null) + tree.setCurrentKey(this.pageForm.parentId) + }) + return true }) }, openUnit() {} }, created() { - this.pageData() - this.getTreeData() + this.getTreeData().then((loaded) => { + if (loaded) { + this.doSearch() + } + }).catch(() => { + this.notifyError("加载单位失败,请稍后重试") + }) } }) diff --git a/src/main/resources/views/platform/zhgh/activity/sports/ActivityPrize/index.html b/src/main/resources/views/platform/zhgh/activity/sports/ActivityPrize/index.html index cffbe63..eeef32f 100644 --- a/src/main/resources/views/platform/zhgh/activity/sports/ActivityPrize/index.html +++ b/src/main/resources/views/platform/zhgh/activity/sports/ActivityPrize/index.html @@ -6,18 +6,19 @@ layout("/layouts/platform.html"){