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 6aad02ab..49b0fca3 100644 --- a/src/main/java/com/budwk/app/sys/controller/SysUnitController.java +++ b/src/main/java/com/budwk/app/sys/controller/SysUnitController.java @@ -285,21 +285,28 @@ public class SysUnitController { @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.andEX("id", "=", pid); cnd.asc("unitcode"); List list = sysUnitService.query(cnd); List> nodeList = CollUtil.newArrayList(); for (int i = 0; i < list.size(); i++) { - nodeList.add(new TreeNode<>(list.get(i).getId(), list.get(i).getParentId(), list.get(i).getName(), 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", list.get(i).getUnitTypeCode() + "unitTypeCode", unit.getUnitTypeCode() ) )); } - List> treeList = TreeUtil.build(nodeList, StrUtil.blankToDefault(pid, "0")); + List> treeList = TreeUtil.build(nodeList, virtualRootId); return Result.success(treeList); } catch (Exception e) { return Result.error(); diff --git a/src/main/java/com/budwk/app/zhgh/activity/culture/controller/ActivityCultureApplyActivityController.java b/src/main/java/com/budwk/app/zhgh/activity/culture/controller/ActivityCultureApplyActivityController.java index c405bc4d..b5101ffe 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/culture/controller/ActivityCultureApplyActivityController.java +++ b/src/main/java/com/budwk/app/zhgh/activity/culture/controller/ActivityCultureApplyActivityController.java @@ -15,7 +15,6 @@ import com.budwk.app.flow.entity.ProcessInstance; import com.budwk.app.flow.entity.ProcessTask; import com.budwk.app.flow.enums.ProcessSubmitTypeEnum; import com.budwk.app.flow.service.FlowCommonService; -import com.budwk.app.sys.models.Sys_home_activity; import com.budwk.app.web.commons.auth.utils.AuthUtil; import com.budwk.app.web.commons.auth.utils.SecurityUtil; import com.budwk.app.zhgh.activity.culture.models.ActivityTissue; @@ -151,8 +150,7 @@ public class ActivityCultureApplyActivityController { return Result.success(); } if (tissue.getIsEnrollSystem()) { - Sys_home_activity sysHomeActivity = tissue.covertToSysHomeActivity(); - activityCultureService.insertOrUpdate(sysHomeActivity); + activityCultureService.syncHomeActivity(tissue); } return Result.success(); } diff --git a/src/main/java/com/budwk/app/zhgh/activity/culture/controller/ActivityCultureAuditActivityController.java b/src/main/java/com/budwk/app/zhgh/activity/culture/controller/ActivityCultureAuditActivityController.java index f5828ba5..805e0366 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/culture/controller/ActivityCultureAuditActivityController.java +++ b/src/main/java/com/budwk/app/zhgh/activity/culture/controller/ActivityCultureAuditActivityController.java @@ -9,7 +9,6 @@ import com.budwk.app.base.param.PageForm; import com.budwk.app.base.result.Result; import com.budwk.app.flow.enums.ProcessTaskStateEnum; import com.budwk.app.flow.service.FlowCommonService; -import com.budwk.app.sys.models.Sys_home_activity; import com.budwk.app.web.commons.auth.utils.SecurityUtil; import com.budwk.app.zhgh.activity.culture.models.ActivityTissue; import com.budwk.app.zhgh.activity.culture.service.ActivityCultureService; @@ -144,8 +143,7 @@ public class ActivityCultureAuditActivityController { tissue.setIsUnseal(true); activityCultureService.update(tissue); if (args.getInt("submitType") == 1) { - Sys_home_activity sysHomeActivity = tissue.covertToSysHomeActivity(); - activityCultureService.insertOrUpdate(sysHomeActivity); + activityCultureService.syncHomeActivity(tissue); } flowCommonService.executeTask(args); return Result.success(); diff --git a/src/main/java/com/budwk/app/zhgh/activity/culture/controller/ActivityCultureInfoManageController.java b/src/main/java/com/budwk/app/zhgh/activity/culture/controller/ActivityCultureInfoManageController.java index b7f3ab57..1884735a 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/culture/controller/ActivityCultureInfoManageController.java +++ b/src/main/java/com/budwk/app/zhgh/activity/culture/controller/ActivityCultureInfoManageController.java @@ -74,9 +74,15 @@ public class ActivityCultureInfoManageController { @SaCheckPermission(value = {"activity.culture.infoManage.school", "activity.culture.infoManage.union", "activity.culture.infoManage.club"}, mode = SaMode.OR) public Result activityStatusChange(@Valid String id, @Valid Boolean isUnseal) { activityCultureService.update(Chain.make("isUnseal", isUnseal), Cnd.where("id", "=", id)); - activityCultureService.dao().update(Sys_home_activity.class, - Chain.make("enable", isUnseal), - Cnd.where("id", "=", id)); + ActivityTissue tissue = activityCultureService.fetch(id); + tissue.setIsUnseal(isUnseal); + if (Boolean.TRUE.equals(isUnseal)) { + activityCultureService.syncHomeActivity(tissue); + } else { + activityCultureService.dao().update(Sys_home_activity.class, + Chain.make("enable", false), + Cnd.where("id", "=", id)); + } return Result.success(); } diff --git a/src/main/java/com/budwk/app/zhgh/activity/culture/service/ActivityCultureService.java b/src/main/java/com/budwk/app/zhgh/activity/culture/service/ActivityCultureService.java index 8c38bed6..e5c86ab5 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/culture/service/ActivityCultureService.java +++ b/src/main/java/com/budwk/app/zhgh/activity/culture/service/ActivityCultureService.java @@ -24,4 +24,14 @@ public interface ActivityCultureService extends BaseService { Object pageData(PageForm page, String year, String name, String unionId, Integer activity_type); NutMap findOne(String id); + + /** + * 同步文化活动到首页活动表。 + * 1. 非报名类活动不推送首页,直接清理首页记录。 + * 2. 校工会活动没有审核流,提交后默认可推首页。 + * 3. 分工会、协会活动仅在已审核通过或已存在首页记录时更新首页信息。 + * + * @param tissue 文化活动 + */ + void syncHomeActivity(ActivityTissue tissue); } diff --git a/src/main/java/com/budwk/app/zhgh/activity/culture/service/impl/ActivityCultureServiceImpl.java b/src/main/java/com/budwk/app/zhgh/activity/culture/service/impl/ActivityCultureServiceImpl.java index cdd2aaf6..a1e06aa2 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/culture/service/impl/ActivityCultureServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/activity/culture/service/impl/ActivityCultureServiceImpl.java @@ -5,6 +5,7 @@ import com.budwk.app.base.constant.RoleConstant; 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.web.commons.auth.utils.AuthUtil; import com.budwk.app.web.commons.auth.utils.SecurityUtil; import com.budwk.app.zhgh.activity.basic.service.ActivityBasicScopeService; @@ -80,6 +81,7 @@ public class ActivityCultureServiceImpl extends BaseServiceImpl @Override public void doEditActivity(ActivityTissue tissue) { update(tissue); + syncHomeActivity(tissue); } @Override @@ -123,6 +125,7 @@ public class ActivityCultureServiceImpl extends BaseServiceImpl cnd.andEX("tissue.projectTypeCode", "!=", "50004"); cnd.andEX("tissue.unionId", "=", unionId); cnd.andEX("tissue.activity_type", "=", activity_type); + cnd.groupBy("tissue.id"); if (Strings.isNotBlank(name)) { cnd.where().andLike("tissue.name", name); @@ -188,4 +191,38 @@ public class ActivityCultureServiceImpl extends BaseServiceImpl return nutMap; } + + @Override + public void syncHomeActivity(ActivityTissue tissue) { + if (tissue == null || Strings.isBlank(tissue.getId())) { + return; + } + // 非报名活动不需要出现在首页,编辑成活动管理模式时同步移除首页记录。 + if (!Boolean.TRUE.equals(tissue.getIsEnrollSystem())) { + dao().delete(Sys_home_activity.class, tissue.getId()); + return; + } + + Sys_home_activity oldHomeActivity = dao().fetch(Sys_home_activity.class, tissue.getId()); + boolean canCreateHomeActivity = (tissue.getActivity_type() != null + && tissue.getActivity_type() == 40001) + || Boolean.TRUE.equals(tissue.getIsUnseal()) + || oldHomeActivity != null; + if (!canCreateHomeActivity) { + return; + } + + Sys_home_activity sysHomeActivity = tissue.covertToSysHomeActivity(); + // 校工会文化活动没有审核环节,前端未回传启用状态时默认直接上首页。 + if (tissue.getActivity_type() != null && tissue.getActivity_type() == 40001 && tissue.getIsUnseal() == null) { + sysHomeActivity.setEnable(true); + } + // 保留首页管理中手工设置的置顶、大图和排序,避免活动编辑时被覆盖。 + if (oldHomeActivity != null) { + sysHomeActivity.setTop(oldHomeActivity.getTop()); + sysHomeActivity.setPush(oldHomeActivity.getPush()); + sysHomeActivity.setSortNo(oldHomeActivity.getSortNo()); + } + dao().insertOrUpdate(sysHomeActivity); + } } diff --git a/src/main/java/com/budwk/app/zhgh/activity/workscollection/controller/h5/H5ActivityWorksUploadCollectionController.java b/src/main/java/com/budwk/app/zhgh/activity/workscollection/controller/h5/H5ActivityWorksUploadCollectionController.java index 73414a94..d87c4573 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/workscollection/controller/h5/H5ActivityWorksUploadCollectionController.java +++ b/src/main/java/com/budwk/app/zhgh/activity/workscollection/controller/h5/H5ActivityWorksUploadCollectionController.java @@ -60,6 +60,7 @@ public class H5ActivityWorksUploadCollectionController { awc.* FROM `activity_works_collection` awc + $condition """); Cnd cnd = Cnd.NEW(); cnd.andEX("YEAR(awc.startDateTime)", "=", year); @@ -68,9 +69,9 @@ public class H5ActivityWorksUploadCollectionController { cnd.and(new Static("now() > awc.startDateTime and now() < awc.endDateTime")); }//查询已结束的 else if (activityType == 3) { - cnd.and(new Static("now() > awc.startDateTime")); + cnd.and(new Static("now() >= awc.endDateTime")); } else if (activityType == 4) { - cnd.and(new Static("now() < awc.endDateTime")); + cnd.and(new Static("now() < awc.startDateTime")); } cnd.and("awc.enable", "=", 1); cnd.and("awc.type", "=", 1); diff --git a/src/main/java/com/budwk/app/zhgh/club/controller/apply/ClubUserJoinApplyController.java b/src/main/java/com/budwk/app/zhgh/club/controller/apply/ClubUserJoinApplyController.java index 1abcdc05..c412c035 100644 --- a/src/main/java/com/budwk/app/zhgh/club/controller/apply/ClubUserJoinApplyController.java +++ b/src/main/java/com/budwk/app/zhgh/club/controller/apply/ClubUserJoinApplyController.java @@ -28,6 +28,7 @@ import com.budwk.app.web.commons.auth.utils.SecurityUtil; import com.budwk.app.web.controllers.open.commons.service.CommonService; import com.budwk.app.zhgh.club.model.ClubUser; import com.budwk.app.zhgh.club.model.ClubUserApply; +import com.budwk.app.zhgh.club.service.ClubUserJoinService; import com.budwk.app.zhgh.staffbenefit.condolence.model.Condolence; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; @@ -64,6 +65,8 @@ public class ClubUserJoinApplyController { private FlowEngine flowEngine; @Inject private FlowCommonService flowCommonService; + @Inject + private ClubUserJoinService clubUserJoinService; @At("/") @SaCheckPermission("club.join.apply") @@ -112,6 +115,8 @@ public class ClubUserJoinApplyController { clubUserApply.setRoleCode(RoleConstant.CLUB_MEMBER.name()); clubUserApply.setMode(true); clubUserApply.setApplyDate(new Date()); + clubUserApply.setJoinTime(clubUserJoinService.resolveJoinTime(clubUserApply.getClubId(), clubUserApply.getUserId())); + clubUserApply.setExitTime(null); dao.insertOrUpdate(clubUserApply); return Result.success(); } @@ -124,12 +129,10 @@ public class ClubUserJoinApplyController { public Result submit(@Param("data") ClubUserApply clubUserApply, @Param("mode") Boolean mode) { if(mode == false) { - clubUserApply = dao.fetch( - ClubUserApply.class, - Cnd.where("userId", "=", SecurityUtil.getUserId()) - .and("clubId", "=", clubUserApply.getClubId()) - .desc("applyDate") - ); + clubUserApply = clubUserJoinService.buildExitApply(clubUserApply.getClubId(), SecurityUtil.getUserId()); + if (ObjectUtil.isEmpty(clubUserApply)) { + return Result.error("当前不是该社团成员,无法申请退出!"); + } } ClubUserApply userApply = dao.fetch(ClubUserApply.class, Cnd.where("userId", "=", clubUserApply.getUserId()).and("clubId", "=", clubUserApply.getClubId()).and("mode", "=", mode).desc(ClubUserApply::getApplyDate)); if (ObjectUtil.isNotEmpty(userApply) && StrUtil.isNotBlank(userApply.getId())) { @@ -142,9 +145,17 @@ public class ClubUserJoinApplyController { if(!mode) { clubUserApply.setId(null); } - clubUserApply.setRoleCode(RoleConstant.CLUB_MEMBER.name()); clubUserApply.setMode(mode); - clubUserApply.setApplyDate(new Date()); + Date applyDate = new Date(); + clubUserApply.setApplyDate(applyDate); + if (mode) { + clubUserApply.setRoleCode(RoleConstant.CLUB_MEMBER.name()); + clubUserApply.setJoinTime(clubUserJoinService.resolveJoinTime(clubUserApply.getClubId(), clubUserApply.getUserId())); + clubUserApply.setExitTime(null); + } else { + clubUserApply.setJoinTime(clubUserJoinService.resolveJoinTime(clubUserApply.getClubId(), clubUserApply.getUserId())); + clubUserApply.setExitTime(applyDate); + } dao.insertOrUpdate(clubUserApply); // 开启流程实例 diff --git a/src/main/java/com/budwk/app/zhgh/club/controller/infoManage/ClubChangeManagerController.java b/src/main/java/com/budwk/app/zhgh/club/controller/infoManage/ClubChangeManagerController.java index 1564057b..83fadd00 100644 --- a/src/main/java/com/budwk/app/zhgh/club/controller/infoManage/ClubChangeManagerController.java +++ b/src/main/java/com/budwk/app/zhgh/club/controller/infoManage/ClubChangeManagerController.java @@ -22,6 +22,7 @@ import com.budwk.app.web.commons.auth.utils.SecurityUtil; import com.budwk.app.zhgh.club.model.*; import com.budwk.app.zhgh.club.param.ClubUserPageForm; import com.budwk.app.zhgh.club.service.SysClubInfoManageService; +import com.budwk.app.zhgh.club.service.SysClubService; import com.budwk.app.zhgh.club.service.impl.SysClubUserServiceImpl; import io.swagger.annotations.ApiOperation; import org.nutz.aop.interceptor.ioc.TransAop; @@ -64,6 +65,8 @@ public class ClubChangeManagerController { private FlowCommonService flowCommonService; @Inject private SysClubInfoManageService infoManageService; + @Inject + private SysClubService sysClubService; @At("") @Ok("beetl:/platform/zhgh/club/infoManage/change/index.html") @@ -102,7 +105,10 @@ public class ClubChangeManagerController { $condition """); Cnd cnd = Cnd.NEW(); - cnd.and("info.userId", "=", SecurityUtil.getUserId()); + if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_CLUB_ADMIN.name())) { + List clubIdList = sysClubService.getMyManageClub().stream().map(SysClub::getId).toList(); + cnd.and("info.clubId", "in", clubIdList); + } cnd.andEX("year(info.creatTime)", "=", pageForm.getYear()); if (StrUtil.isNotBlank(pageForm.getClubName())) { cnd.and("club.clubName", "LIKE", "%" + pageForm.getClubName() + "%"); @@ -112,6 +118,7 @@ public class ClubChangeManagerController { } else { cnd.orderBy(pageForm.getPageOrderName(), PageUtil.getOrder(pageForm.getPageOrderBy())); } + cnd.groupBy("info.id"); sql.setCondition(cnd); Pagination pagination = infoManageService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql); List listMap = pagination.getList(NutMap.class); @@ -130,6 +137,17 @@ public class ClubChangeManagerController { @SaCheckPermission("club.infoManage.change") @SLog(tag = "社团管理系统-信息管理", msg = "提交变更理事机构") public Object submit(@Param("data") SysClubManager clubManager) { + Cnd cnd = Cnd.NEW(); + cnd.and(SysClubManager::getClubId, "=", clubManager.getClubId()); + if (StrUtil.isNotBlank(clubManager.getId())) { + cnd.and(SysClubManager::getId, "!=", clubManager.getId()); + } + List managerList = dao.query(SysClubManager.class, cnd); + List pendingProcessIds = managerList.stream().map(SysClubManager::getId).toList(); + int pendingCount = dao.count(ProcessInstance.class, Cnd.where(ProcessInstance::getBusinessNo, "in", pendingProcessIds).and(ProcessInstance::getState, "not in", List.of(ProcessInstanceStateEnum.FINISHED.getCode(), ProcessInstanceStateEnum.REJECT.getCode()))); + if (pendingCount > 0) { + return Result.error("您有该社团的申请记录尚未完成,请核对!"); + } if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name())) { String errMsg = "校工会审核" + clubManager.getPeriod() + "的换届报告通过后才能变更理事成员"; diff --git a/src/main/java/com/budwk/app/zhgh/club/controller/infoManage/ClubExitManageController.java b/src/main/java/com/budwk/app/zhgh/club/controller/infoManage/ClubExitManageController.java new file mode 100644 index 00000000..f0d47b69 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/club/controller/infoManage/ClubExitManageController.java @@ -0,0 +1,63 @@ +package com.budwk.app.zhgh.club.controller.infoManage; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import com.budwk.app.base.page.Pagination; +import com.budwk.app.base.result.Result; +import com.budwk.app.base.constant.RoleConstant; +import com.budwk.app.web.commons.auth.utils.AuthUtil; +import com.budwk.app.zhgh.club.model.SysClub; +import com.budwk.app.zhgh.club.param.ClubUserPageForm; +import com.budwk.app.zhgh.club.service.SysClubInfoManageService; +import com.budwk.app.zhgh.club.service.SysClubService; +import com.budwk.app.zhgh.club.vo.ClubUserCommonPageVo; +import com.budwk.app.zhgh.club.vo.ClubUserJoinVo; +import org.nutz.ioc.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.mvc.annotation.At; +import org.nutz.mvc.annotation.Ok; + +import javax.validation.Valid; +import java.util.List; + +@IocBean +@Ok("json") +@At("/platform/club/infoManage/exitManage") +public class ClubExitManageController { + + @Inject + private SysClubInfoManageService clubInfoManageService; + @Inject + private SysClubService sysClubService; + + @At("") + @Ok("beetl:/platform/zhgh/club/infoManage/exitManage/index.html") + @SaCheckPermission("club.infoManage.exitManage") + public void index() { + } + + @At + @SaCheckPermission("club.infoManage.exitManage") + public Result pageData(@Valid ClubUserPageForm pageForm) { + Pagination pagination = clubInfoManageService.exitManagePageData(pageForm); + return Result.success(pagination); + } + + @At + @SaCheckPermission("club.infoManage.exitManage") + public Result info(@Valid String id) { + ClubUserJoinVo clubUserJoinVo = clubInfoManageService.exitManageInfo(id); + return Result.success(clubUserJoinVo); + } + + @At + @SaCheckPermission("club.infoManage.exitManage") + public Result getClubList() { + List clubList; + if (AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name(), RoleConstant.SCHOOL_UNION_CLUB_ADMIN.name())) { + clubList = sysClubService.getMyManageClub(); + } else { + clubList = sysClubService.getMyManageClub(); + } + return Result.success(clubList); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/club/controller/infoManage/ClubRefreshReportController.java b/src/main/java/com/budwk/app/zhgh/club/controller/infoManage/ClubRefreshReportController.java index 5acde8f6..0f55c8c8 100644 --- a/src/main/java/com/budwk/app/zhgh/club/controller/infoManage/ClubRefreshReportController.java +++ b/src/main/java/com/budwk/app/zhgh/club/controller/infoManage/ClubRefreshReportController.java @@ -5,6 +5,7 @@ import cn.hutool.core.date.DateUtil; import cn.hutool.core.lang.Dict; 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.PageUtil; @@ -15,6 +16,7 @@ import com.budwk.app.flow.entity.ProcessTask; import com.budwk.app.flow.enums.ProcessInstanceStateEnum; import com.budwk.app.flow.enums.ProcessSubmitTypeEnum; import com.budwk.app.flow.service.FlowCommonService; +import com.budwk.app.web.commons.auth.utils.AuthUtil; import com.budwk.app.web.commons.auth.utils.SecurityUtil; import com.budwk.app.zhgh.club.model.SysClub; import com.budwk.app.zhgh.club.model.SysClubRefresh; @@ -58,6 +60,8 @@ public class ClubRefreshReportController { private FlowCommonService flowCommonService; @Inject private Dao dao; + @Inject + private SysClubService sysClubService; @At("") @Ok("beetl:/platform/zhgh/club/infoManage/refreshReport/index.html") @@ -96,7 +100,10 @@ public class ClubRefreshReportController { $condition """); Cnd cnd = Cnd.NEW(); - cnd.and("info.userId", "=", SecurityUtil.getUserId()); + if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_CLUB_ADMIN.name())) { + List clubIdList = sysClubService.getMyManageClub().stream().map(SysClub::getId).toList(); + cnd.and("info.clubId", "in", clubIdList); + } cnd.andEX("year(info.creatTime)", "=", pageForm.getYear()); if (StrUtil.isNotBlank(pageForm.getClubName())) { cnd.and("club.clubName", "LIKE", "%" + pageForm.getClubName() + "%"); @@ -119,6 +126,7 @@ public class ClubRefreshReportController { public Object submit(@Param("data") SysClubRefresh clubRefresh) { Cnd cnd = Cnd.NEW(); + cnd.and(SysClubRefresh::getClubId, "=", clubRefresh.getClubId()); if(StrUtil.isNotBlank(clubRefresh.getId())) { cnd.and(SysClubRefresh::getId, "!=", clubRefresh.getId()); } diff --git a/src/main/java/com/budwk/app/zhgh/club/controller/infoManage/ClubRuleUpdateController.java b/src/main/java/com/budwk/app/zhgh/club/controller/infoManage/ClubRuleUpdateController.java index 426fc0a2..b0a73954 100644 --- a/src/main/java/com/budwk/app/zhgh/club/controller/infoManage/ClubRuleUpdateController.java +++ b/src/main/java/com/budwk/app/zhgh/club/controller/infoManage/ClubRuleUpdateController.java @@ -4,6 +4,7 @@ import cn.dev33.satoken.annotation.SaCheckPermission; import cn.hutool.core.date.DateUtil; import cn.hutool.core.lang.Dict; import cn.hutool.core.util.StrUtil; +import com.budwk.app.base.constant.RoleConstant; import com.budwk.app.base.annotation.SLog; import com.budwk.app.base.page.Pagination; import com.budwk.app.base.result.Result; @@ -15,6 +16,7 @@ import com.budwk.app.flow.entity.ProcessTask; import com.budwk.app.flow.enums.ProcessInstanceStateEnum; import com.budwk.app.flow.enums.ProcessSubmitTypeEnum; import com.budwk.app.flow.service.FlowCommonService; +import com.budwk.app.web.commons.auth.utils.AuthUtil; import com.budwk.app.web.commons.auth.utils.SecurityUtil; import com.budwk.app.zhgh.club.model.SysClub; import com.budwk.app.zhgh.club.model.SysClubRefresh; @@ -59,6 +61,8 @@ public class ClubRuleUpdateController { private FlowCommonService flowCommonService; @Inject private Dao dao; + @Inject + private SysClubService sysClubService; @At("") @Ok("beetl:/platform/zhgh/club/infoManage/ruleUpdate/index.html") @@ -97,7 +101,10 @@ public class ClubRuleUpdateController { $condition """); Cnd cnd = Cnd.NEW(); - cnd.and("info.userId", "=", SecurityUtil.getUserId()); + if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_CLUB_ADMIN.name())) { + List clubIdList = sysClubService.getMyManageClub().stream().map(SysClub::getId).toList(); + cnd.and("info.clubId", "in", clubIdList); + } cnd.andEX("year(info.creatTime)", "=", pageForm.getYear()); if (StrUtil.isNotBlank(pageForm.getClubName())) { cnd.and("club.clubName", "LIKE", "%" + pageForm.getClubName() + "%"); @@ -120,6 +127,7 @@ public class ClubRuleUpdateController { public Object submit(@Param("data")SysClubRule clubRule) { Cnd cnd = Cnd.NEW(); + cnd.and(SysClubRule::getClubId, "=", clubRule.getClubId()); if(StrUtil.isNotBlank(clubRule.getId())) { cnd.and(SysClubRule::getId, "!=", clubRule.getId()); } diff --git a/src/main/java/com/budwk/app/zhgh/club/model/ClubUserApply.java b/src/main/java/com/budwk/app/zhgh/club/model/ClubUserApply.java index d755b33b..ff3a21d4 100644 --- a/src/main/java/com/budwk/app/zhgh/club/model/ClubUserApply.java +++ b/src/main/java/com/budwk/app/zhgh/club/model/ClubUserApply.java @@ -85,4 +85,14 @@ public class ClubUserApply extends BaseModel { @Comment("申请时间") @ColDefine(type = ColType.DATETIME) private Date applyDate; + + @Column + @Comment("入会时间") + @ColDefine(type = ColType.DATETIME) + private Date joinTime; + + @Column + @Comment("退会时间") + @ColDefine(type = ColType.DATETIME) + private Date exitTime; } diff --git a/src/main/java/com/budwk/app/zhgh/club/model/SysClubExamineRegister.java b/src/main/java/com/budwk/app/zhgh/club/model/SysClubExamineRegister.java index f0e6f294..3b41d961 100644 --- a/src/main/java/com/budwk/app/zhgh/club/model/SysClubExamineRegister.java +++ b/src/main/java/com/budwk/app/zhgh/club/model/SysClubExamineRegister.java @@ -47,7 +47,7 @@ public class SysClubExamineRegister extends BaseModel { @Column @Comment("社团名称") - @ColDefine(type = ColType.VARCHAR, width = 32) + @ColDefine(type = ColType.VARCHAR) private String clubName; @Column diff --git a/src/main/java/com/budwk/app/zhgh/club/service/ClubUserJoinService.java b/src/main/java/com/budwk/app/zhgh/club/service/ClubUserJoinService.java index e94a9d4b..1168e586 100644 --- a/src/main/java/com/budwk/app/zhgh/club/service/ClubUserJoinService.java +++ b/src/main/java/com/budwk/app/zhgh/club/service/ClubUserJoinService.java @@ -4,4 +4,25 @@ import com.budwk.app.base.service.BaseService; import com.budwk.app.zhgh.club.model.ClubUserApply; public interface ClubUserJoinService extends BaseService { + + /** + * 构造退会申请数据。 + * 当用户没有历史入会申请记录时,使用当前社团成员关系和人员基础信息兜底生成一份退会申请, + * 以保证会长等通过社团注册直接入会的人员也能正常发起退会流程。 + * + * @param clubId 社团ID + * @param userId 用户ID + * @return 退会申请对象,不存在当前成员关系时返回null + */ + ClubUserApply buildExitApply(String clubId, String userId); + + /** + * 获取社团成员的入会时间,优先取历史有效入会申请记录, + * 没有时兜底为社团创办时间。 + * + * @param clubId 社团ID + * @param userId 用户ID + * @return 入会时间 + */ + java.util.Date resolveJoinTime(String clubId, String userId); } diff --git a/src/main/java/com/budwk/app/zhgh/club/service/SysClubInfoManageService.java b/src/main/java/com/budwk/app/zhgh/club/service/SysClubInfoManageService.java index 924626a6..74ff1ba2 100644 --- a/src/main/java/com/budwk/app/zhgh/club/service/SysClubInfoManageService.java +++ b/src/main/java/com/budwk/app/zhgh/club/service/SysClubInfoManageService.java @@ -5,6 +5,7 @@ import com.budwk.app.base.service.BaseService; import com.budwk.app.zhgh.club.param.ClubUserPageForm; import com.budwk.app.zhgh.club.vo.ClubCommonPageVo; import com.budwk.app.zhgh.club.vo.ClubUserCommonPageVo; +import com.budwk.app.zhgh.club.vo.ClubUserJoinVo; import org.nutz.lang.util.NutMap; import javax.servlet.http.HttpServletResponse; @@ -23,8 +24,12 @@ public interface SysClubInfoManageService extends BaseService Pagination infoManageUserPageData(@Valid ClubUserPageForm pageForm); + Pagination exitManagePageData(@Valid ClubUserPageForm pageForm); + Pagination clubManagePersonAuditPageData(@Valid ClubUserPageForm pageForm); + ClubUserJoinVo exitManageInfo(String id); + List getClubTreeData(); /** diff --git a/src/main/java/com/budwk/app/zhgh/club/service/impl/ClubUserJoinServiceImpl.java b/src/main/java/com/budwk/app/zhgh/club/service/impl/ClubUserJoinServiceImpl.java index ad74026e..dc76f774 100644 --- a/src/main/java/com/budwk/app/zhgh/club/service/impl/ClubUserJoinServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/club/service/impl/ClubUserJoinServiceImpl.java @@ -1,14 +1,94 @@ -package com.budwk.app.zhgh.club.service.impl; - +import com.budwk.app.base.constant.RoleConstant; import com.budwk.app.base.service.impl.BaseServiceImpl; +import cn.hutool.core.date.DateUtil; +import com.budwk.app.sys.views.View_user; +import com.budwk.app.zhgh.club.model.ClubUser; import com.budwk.app.zhgh.club.model.ClubUserApply; +import com.budwk.app.zhgh.club.model.SysClub; import com.budwk.app.zhgh.club.service.ClubUserJoinService; +import org.nutz.dao.Cnd; import org.nutz.dao.Dao; import org.nutz.ioc.loader.annotation.IocBean; +import java.util.Date; +import java.util.List; + @IocBean(args = {"refer:dao"}) public class ClubUserJoinServiceImpl extends BaseServiceImpl implements ClubUserJoinService { public ClubUserJoinServiceImpl(Dao dao) { super(dao); } + + @Override + public ClubUserApply buildExitApply(String clubId, String userId) { + // 优先复用最近一次申请记录中的补充信息,避免用户重复填写基础资料。 + ClubUserApply latestApply = fetch( + Cnd.where("userId", "=", userId) + .and("clubId", "=", clubId) + .desc(ClubUserApply::getApplyDate) + ); + if (latestApply != null) { + latestApply.setId(null); + return latestApply; + } + + // 对于会长等注册社团时直接入会的人员,没有申请记录时从当前成员信息兜底生成退会申请。 + ClubUser clubUser = dao().fetch(ClubUser.class, Cnd.where("clubId", "=", clubId).and("userId", "=", userId)); + if (clubUser == null) { + return null; + } + View_user viewUser = dao().fetch(View_user.class, Cnd.where("id", "=", userId)); + + ClubUserApply clubUserApply = new ClubUserApply(); + clubUserApply.setClubId(clubId); + clubUserApply.setUserId(userId); + clubUserApply.setClubPosition(clubUser.getClubPosition()); + clubUserApply.setEmail(clubUser.getEmail()); + clubUserApply.setAvatar(clubUser.getAvatar()); + clubUserApply.setSameTimeJoinOtherClubSituation(clubUser.getSameTimeJoinOtherClubSituation()); + clubUserApply.setAwardsExperience(clubUser.getAwardsExperience()); + clubUserApply.setJoinTime(resolveJoinTime(clubId, userId)); + + List roleCodes = clubUser.getRoleCode(); + if (roleCodes != null && !roleCodes.isEmpty()) { + String exitRoleCode = roleCodes.stream() + .filter(roleCode -> !RoleConstant.CLUB_MEMBER.name().equals(roleCode)) + .findFirst() + .orElse(RoleConstant.CLUB_MEMBER.name()); + clubUserApply.setRoleCode(exitRoleCode); + } else { + clubUserApply.setRoleCode(RoleConstant.CLUB_MEMBER.name()); + } + + if (viewUser != null) { + clubUserApply.setBirthday(viewUser.getBirthday()); + clubUserApply.setMobile(viewUser.getMobile()); + if (clubUserApply.getEmail() == null) { + clubUserApply.setEmail(viewUser.getEmail()); + } + if (clubUserApply.getAvatar() == null) { + clubUserApply.setAvatar(viewUser.getAvatar()); + } + } + return clubUserApply; + } + + @Override + public Date resolveJoinTime(String clubId, String userId) { + ClubUserApply latestJoinApply = fetch( + Cnd.where("userId", "=", userId) + .and("clubId", "=", clubId) + .and("mode", "=", true) + .desc(ClubUserApply::getApplyDate) + ); + if (latestJoinApply != null && latestJoinApply.getJoinTime() != null) { + return latestJoinApply.getJoinTime(); + } + + SysClub club = dao().fetch(SysClub.class, clubId); + if (club != null && club.getFoundTime() != null) { + return DateUtil.parse(club.getFoundTime()); + } + return null; + } } diff --git a/src/main/java/com/budwk/app/zhgh/club/service/impl/SysClubInfoManageServiceImpl.java b/src/main/java/com/budwk/app/zhgh/club/service/impl/SysClubInfoManageServiceImpl.java index c2b5062f..9cc4424c 100644 --- a/src/main/java/com/budwk/app/zhgh/club/service/impl/SysClubInfoManageServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/club/service/impl/SysClubInfoManageServiceImpl.java @@ -20,6 +20,7 @@ import com.budwk.app.zhgh.club.service.SysClubInfoManageService; import com.budwk.app.zhgh.club.service.SysClubService; import com.budwk.app.zhgh.club.vo.ClubCommonPageVo; import com.budwk.app.zhgh.club.vo.ClubUserCommonPageVo; +import com.budwk.app.zhgh.club.vo.ClubUserJoinVo; import com.deepoove.poi.XWPFTemplate; import com.deepoove.poi.config.Configure; import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy; @@ -139,11 +140,27 @@ public class SysClubInfoManageServiceImpl extends BaseServiceImpl exitManagePageData(ClubUserPageForm pageForm) { + Sql sql = Sqls.create(""" + SELECT + info.id, + info.clubId, + info.userId, + info.roleCode AS applyRoleCode, + info.clubPosition, + info.email, + info.mobile, + info.birthday, + info.avatar, + info.sameTimeJoinOtherClubSituation, + info.awardsExperience, + info.signature, + info.applyDate, + u.username AS userName, + u.loginname AS loginName, + u.sex, + u.personType, + u.userState, + club.clubName, + u.unitname AS unitName, + DATE_FORMAT(IFNULL(info.joinTime, club.foundTime), '%Y-%m-%d %H:%i:%s') AS joinTime, + DATE_FORMAT(info.exitTime, '%Y-%m-%d %H:%i:%s') AS exitTime, + ins.id AS instanceId, + ins.businessNo, + ins.state instanceState, + ins.variable instanceVariable, + ins.processDefineId instanceProcessDefineId + FROM + club_user_apply info + LEFT JOIN sys_club club ON club.id = info.clubId + LEFT JOIN vw_user u ON u.id = info.userId + LEFT JOIN wf_process_instance ins ON ins.businessNo = info.id + $condition + """); + Cnd cnd = Cnd.NEW(); + cnd.and("info.mode", "=", false); + cnd.and("ins.state", "=", ProcessInstanceStateEnum.FINISHED.getCode()); + cnd.andEX("info.clubId", "=", pageForm.getClubId()); + cnd.andEX("u.personType", "=", pageForm.getPersonType()); + cnd.andEX("u.userState", "=", pageForm.getUserState()); + if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) { + cnd.and(Cnd.exps("u.loginname", "like", "%" + pageForm.getSearchKeyword() + "%").or("u.username", "like", "%" + pageForm.getSearchKeyword() + "%")); + } + if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name(), RoleConstant.SCHOOL_UNION_CLUB_ADMIN.name())) { + List 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 clubList = dao().query(Sys_user_role.class, Cnd.where("roleId", "in", clubIdList).and("userId", "=", SecurityUtil.getUserId()).and("clubId", "is not", null)); + cnd.and("info.clubId", "in", clubList.stream().map(Sys_user_role::getClubId).toList()); + } + cnd.groupBy("info.id"); + cnd.desc("info.applyDate"); + sql.setCondition(cnd); + Pagination pagination = listPageVO(pageForm, sql, ClubUserCommonPageVo.class); + List list = pagination.getList(ClubUserCommonPageVo.class); + for (ClubUserCommonPageVo vo : list) { + vo.setRoleName(SysClubUserServiceImpl.convertRoleName(List.of(vo.getApplyRoleCode()))); + } + pagination.setList(list); + return pagination; + } + + @Override + public ClubUserJoinVo exitManageInfo(String id) { + Sql sql = Sqls.create(""" + SELECT + cua.*, + 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 + club_user_apply cua + LEFT JOIN vw_user u ON u.id = cua.userId + LEFT JOIN sys_club club ON club.id = cua.clubId + WHERE cua.id = @id + """); + sql.setParam("id", id); + return fetchVO(sql, ClubUserJoinVo.class); + } + @Override public Pagination clubManagePersonAuditPageData(ClubUserPageForm pageForm) { Sql sql = Sqls.create(""" diff --git a/src/main/java/com/budwk/app/zhgh/club/vo/ClubUserCommonPageVo.java b/src/main/java/com/budwk/app/zhgh/club/vo/ClubUserCommonPageVo.java index 6411de53..fb421266 100644 --- a/src/main/java/com/budwk/app/zhgh/club/vo/ClubUserCommonPageVo.java +++ b/src/main/java/com/budwk/app/zhgh/club/vo/ClubUserCommonPageVo.java @@ -29,6 +29,9 @@ public class ClubUserCommonPageVo extends ClubUser { private String unionName; private String birthday; private String unionId; + private String joinTime; + private String exitTime; + private String applyRoleCode; private String roleName; } diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/edu/controller/EduCoursesController.java b/src/main/java/com/budwk/app/zhgh/dayofficework/edu/controller/EduCoursesController.java index 9e129ad5..232d826d 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/edu/controller/EduCoursesController.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/edu/controller/EduCoursesController.java @@ -9,12 +9,15 @@ import com.budwk.app.sys.models.Sys_file; import com.budwk.app.sys.utils.SysFileMinIoUtil; import com.budwk.app.zhgh.dayofficework.edu.models.EduChapters; import com.budwk.app.zhgh.dayofficework.edu.models.EduCourses; +import com.budwk.app.zhgh.dayofficework.edu.models.EduStudyRecords; import com.budwk.app.zhgh.dayofficework.edu.models.EduVideos; import com.budwk.app.zhgh.dayofficework.edu.service.EduCoursesService; +import com.budwk.app.zhgh.dayofficework.edu.service.EduStudyRecordsService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.nutz.dao.Cnd; import org.nutz.dao.Dao; +import org.nutz.dao.Sqls; import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.IocBean; import org.nutz.mvc.annotation.At; @@ -33,6 +36,8 @@ public class EduCoursesController { private Dao dao; @Inject private EduCoursesService eduCoursesService; + @Inject + private EduStudyRecordsService eduStudyRecordsService; @At("/courses") @Ok("beetl:/platform/zhgh/dayofficework/edu/courses/index.html") @@ -75,6 +80,14 @@ public class EduCoursesController { @ApiOperation("删除") @SLog(tag = "理论学习课程", msg = "删除课程,id:${args[0]}") public Result deleteCourses(@Param("id") String id) { + //理论学习学习记录表 + dao.execute(Sqls.create("delete from edu_study_records where courseId=\'" + id + "\'")); + //理论学习视频表 + dao.execute(Sqls.create("delete from edu_videos " + + "where chapterId in (select id from edu_chapters where courseId=\'" + id + "\')")); + //理论学习视频章节表 + dao.execute(Sqls.create("delete from edu_chapters where courseId=\'" + id + "\'")); + //理论学习课程表 dao.delete(EduCourses.class, id); return Result.success(); } @@ -161,6 +174,8 @@ public class EduCoursesController { @ApiOperation("删除") @SLog(tag = "理论学习课程", msg = "删除课程视频,id:${args[0]}") public Result deleteVideo(@Param("id") String id) { + // 删除视频时同步清理学习记录,避免已删除视频继续影响学习进度。 + eduStudyRecordsService.deleteStudyRecordsByVideoId(id); dao.delete(EduVideos.class, id); return Result.success(); } diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/edu/h5controller/H5EduController.java b/src/main/java/com/budwk/app/zhgh/dayofficework/edu/h5controller/H5EduController.java index 7087e89b..81ec6c97 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/edu/h5controller/H5EduController.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/edu/h5controller/H5EduController.java @@ -114,29 +114,8 @@ public class H5EduController { return Result.error("用户未登录"); } - // 获取课程总视频数 - List chapters = dao.query(EduChapters.class, Cnd.where(EduChapters::getCourseId, "=", courseId)); - List chapterIds = chapters.stream().map(EduChapters::getId).toList(); - int totalVideos = dao.count(EduVideos.class, Cnd.where(EduVideos::getChapterId, "in", chapterIds)); - - // 获取已完成的视频ID列表 - List completedRecords = dao.query(EduStudyRecords.class, - Cnd.where(EduStudyRecords::getUserId, "=", userId) - .and(EduStudyRecords::getCourseId, "=", courseId) - .and(EduStudyRecords::getIsCompleted, "=", 1)); - - List completedVideoIds = completedRecords.stream() - .map(EduStudyRecords::getVideoId) - .collect(java.util.stream.Collectors.toList()); - - int completedVideos = completedVideoIds.size(); - - java.util.Map progressData = new java.util.HashMap<>(); - progressData.put("totalVideos", totalVideos); - progressData.put("completedVideos", completedVideos); - progressData.put("completedVideoIds", completedVideoIds); - - return Result.success(progressData); + // 进度只按课程当前仍存在的视频统计,删除旧视频后要立即反映到进度条上。 + return Result.success(eduStudyRecordsService.getCurrentCourseProgress(userId, courseId)); } catch (Exception e) { log.error("获取课程进度失败", e); return Result.error("获取课程进度失败"); diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/edu/service/EduStudyRecordsService.java b/src/main/java/com/budwk/app/zhgh/dayofficework/edu/service/EduStudyRecordsService.java index f3ff042a..07c45bd0 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/edu/service/EduStudyRecordsService.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/edu/service/EduStudyRecordsService.java @@ -4,6 +4,7 @@ import com.budwk.app.base.service.BaseService; import com.budwk.app.zhgh.dayofficework.edu.models.EduStudyRecords; import java.util.List; +import java.util.Map; /** * @ClassName EduStudyRecordsService @@ -54,6 +55,20 @@ public interface EduStudyRecordsService extends BaseService { */ StudyProgressStats getCourseProgressStats(String userId, String courseId); + /** + * 按课程当前仍存在的视频重新计算学习进度,避免已删除视频的历史记录继续参与统计。 + * @param userId 用户ID + * @param courseId 课程ID + * @return 当前课程学习进度数据 + */ + Map getCurrentCourseProgress(String userId, String courseId); + + /** + * 删除视频时同步清理该视频的学习记录,避免历史脏数据影响后续进度统计。 + * @param videoId 视频ID + */ + void deleteStudyRecordsByVideoId(String videoId); + /** * 学习进度统计信息 */ diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/edu/service/impl/EduStudyRecordsServiceImpl.java b/src/main/java/com/budwk/app/zhgh/dayofficework/edu/service/impl/EduStudyRecordsServiceImpl.java index 1c86c7a7..6cf9e585 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/edu/service/impl/EduStudyRecordsServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/edu/service/impl/EduStudyRecordsServiceImpl.java @@ -2,6 +2,7 @@ package com.budwk.app.zhgh.dayofficework.edu.service.impl; import com.budwk.app.base.exception.BaseException; import com.budwk.app.base.service.impl.BaseServiceImpl; +import com.budwk.app.zhgh.dayofficework.edu.models.EduChapters; import com.budwk.app.zhgh.dayofficework.edu.models.EduStudyRecords; import com.budwk.app.zhgh.dayofficework.edu.models.EduVideos; import com.budwk.app.zhgh.dayofficework.edu.service.EduStudyRecordsService; @@ -12,8 +13,12 @@ import org.nutz.dao.Sqls; import org.nutz.dao.sql.Sql; import org.nutz.ioc.loader.annotation.IocBean; +import java.util.Collections; import java.util.Date; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; /** * @ClassName EduStudyRecordsServiceImpl @@ -195,4 +200,64 @@ public class EduStudyRecordsServiceImpl extends BaseServiceImpl return new StudyProgressStats(0, 0, 0, 0); } } + + @Override + public Map getCurrentCourseProgress(String userId, String courseId) { + try { + List chapters = dao().query(EduChapters.class, + Cnd.where(EduChapters::getCourseId, "=", courseId)); + List chapterIds = chapters.stream().map(EduChapters::getId).collect(Collectors.toList()); + Map progressData = new HashMap<>(); + + if (chapterIds.isEmpty()) { + progressData.put("totalVideos", 0); + progressData.put("completedVideos", 0); + progressData.put("completedVideoIds", Collections.emptyList()); + return progressData; + } + + List currentVideos = dao().query(EduVideos.class, + Cnd.where(EduVideos::getChapterId, "in", chapterIds)); + List currentVideoIds = currentVideos.stream().map(EduVideos::getId).collect(Collectors.toList()); + if (currentVideoIds.isEmpty()) { + progressData.put("totalVideos", 0); + progressData.put("completedVideos", 0); + progressData.put("completedVideoIds", Collections.emptyList()); + return progressData; + } + + // 只统计当前课程仍存在的视频,避免已删除视频的学习记录继续影响进度条。 + List completedRecords = dao().query(EduStudyRecords.class, + Cnd.where(EduStudyRecords::getUserId, "=", userId) + .and(EduStudyRecords::getCourseId, "=", courseId) + .and(EduStudyRecords::getIsCompleted, "=", 1) + .and(EduStudyRecords::getVideoId, "in", currentVideoIds)); + List completedVideoIds = completedRecords.stream() + .map(EduStudyRecords::getVideoId) + .distinct() + .collect(Collectors.toList()); + + progressData.put("totalVideos", currentVideoIds.size()); + progressData.put("completedVideos", completedVideoIds.size()); + progressData.put("completedVideoIds", completedVideoIds); + return progressData; + } catch (Exception e) { + log.error("按当前课程视频计算学习进度失败", e); + Map progressData = new HashMap<>(); + progressData.put("totalVideos", 0); + progressData.put("completedVideos", 0); + progressData.put("completedVideoIds", Collections.emptyList()); + return progressData; + } + } + + @Override + public void deleteStudyRecordsByVideoId(String videoId) { + try { + dao().clear(EduStudyRecords.class, Cnd.where(EduStudyRecords::getVideoId, "=", videoId)); + } catch (Exception e) { + log.error("删除视频学习记录失败, videoId={}", videoId, e); + throw new BaseException("删除视频学习记录失败", e); + } + } } diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/outlay/outlayManage/club/controller/OutlayManageClubQueryController.java b/src/main/java/com/budwk/app/zhgh/dayofficework/outlay/outlayManage/club/controller/OutlayManageClubQueryController.java index b32670f5..8c7743bb 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/outlay/outlayManage/club/controller/OutlayManageClubQueryController.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/outlay/outlayManage/club/controller/OutlayManageClubQueryController.java @@ -80,7 +80,9 @@ public class OutlayManageClubQueryController { SELECT * FROM `outlay_manage_club` $condition """); cnd.andEX("year", "=", year); - if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name())) { + // if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name())) { + // 现逻辑:系统管理员、校工会社团管理员可查看全部社团分页数据,会长仅查看自己负责的社团。 + if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_CLUB_ADMIN.name())) { List roleIds = sysRoleService.getRoleIdsByCode(List.of(RoleConstant.CLUB_PRESIDENT, RoleConstant.CLUB_SECRETARY)); List userRoles = clubQueryService.dao().query(Sys_user_role.class, Cnd.where("userId", "=", SecurityUtil.getUserId()) diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/outlay/outlayManage/club/model/OutlayManageClub.java b/src/main/java/com/budwk/app/zhgh/dayofficework/outlay/outlayManage/club/model/OutlayManageClub.java index 1bc1ef10..b2570efa 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/outlay/outlayManage/club/model/OutlayManageClub.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/outlay/outlayManage/club/model/OutlayManageClub.java @@ -75,7 +75,7 @@ public class OutlayManageClub extends BaseModel implements Serializable { @Column @Comment("社团名称") - @ColDefine(type = ColType.VARCHAR, width = 32) + @ColDefine(type = ColType.VARCHAR) private String clubName; @Column diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/qsv/h5controller/H5QsvQuizController.java b/src/main/java/com/budwk/app/zhgh/dayofficework/qsv/h5controller/H5QsvQuizController.java index ebe56109..b2e1635d 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/qsv/h5controller/H5QsvQuizController.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/qsv/h5controller/H5QsvQuizController.java @@ -1,6 +1,7 @@ package com.budwk.app.zhgh.dayofficework.qsv.h5controller; import cn.dev33.satoken.annotation.SaCheckLogin; +import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; @@ -102,7 +103,7 @@ public class H5QsvQuizController { List subjectIds = lastRecordOptional.get().getSubjectIds(); Cnd cnd = Cnd.where("id", "in", subjectIds); String dynamic = subjectIds.stream().map(id -> "'" + id + "'").collect(Collectors.joining(",")); - cnd.and(new Static("1=1 ORDER BY FIELD (id," + dynamic + ")")); + cnd.orderBy("sortNum","asc"); resultSubjects = dao.query(QsvSubject.class, cnd); answerRecordId = lastRecordOptional.get().getId(); } else { @@ -114,7 +115,7 @@ public class H5QsvQuizController { List subjectIds = answerRecord.getSubjectIds(); Cnd cnd = Cnd.where("id", "in", subjectIds); String dynamic = subjectIds.stream().map(id -> "'" + id + "'").collect(Collectors.joining(",")); - cnd.and(new Static("1=1 ORDER BY FIELD (id," + dynamic + ")")); + cnd.orderBy("sortNum","asc"); resultSubjects = dao.query(QsvSubject.class, cnd); } } else { @@ -143,7 +144,7 @@ public class H5QsvQuizController { List subjectIds = answerRecord.getSubjectIds(); Cnd cnd = Cnd.where("id", "in", subjectIds); String dynamic = subjectIds.stream().map(id -> "'" + id + "'").collect(Collectors.joining(",")); - cnd.and(new Static("1=1 ORDER BY FIELD (id," + dynamic + ")")); + cnd.orderBy("sortNum","asc"); resultSubjects = dao.query(QsvSubject.class, cnd); } } else { @@ -169,7 +170,7 @@ public class H5QsvQuizController { } Cnd cnd = Cnd.where("id", "in", subjectIds); String dynamic = subjectIds.stream().map(id -> "'" + id + "'").collect(Collectors.joining(",")); - cnd.and(new Static("1=1 ORDER BY FIELD (id," + dynamic + ")")); + cnd.orderBy("sortNum","asc"); resultSubjects = dao.query(QsvSubject.class, cnd); repeatTips = true; } else { @@ -179,7 +180,7 @@ public class H5QsvQuizController { List subjectIds = maxAnswerRecordOptional.get().getSubjectIds(); Cnd cnd = Cnd.where("id", "in", subjectIds); String dynamic = subjectIds.stream().map(id -> "'" + id + "'").collect(Collectors.joining(",")); - cnd.and(new Static("1=1 ORDER BY FIELD (id," + dynamic + ")")); + cnd.orderBy("sortNum","asc"); resultSubjects = dao.query(QsvSubject.class, cnd); answerRecordId = maxAnswerRecordOptional.get().getId(); } else { @@ -192,7 +193,7 @@ public class H5QsvQuizController { List subjectIds = maxAnswerRecordOptional.get().getSubjectIds(); Cnd cnd = Cnd.where("id", "in", subjectIds); String dynamic = subjectIds.stream().map(id -> "'" + id + "'").collect(Collectors.joining(",")); - cnd.and(new Static("1=1 ORDER BY FIELD (id," + dynamic + ")")); + cnd.orderBy("sortNum","asc"); resultSubjects = dao.query(QsvSubject.class, cnd); answerRecordId = maxAnswerRecordOptional.get().getId(); } else { @@ -206,7 +207,7 @@ public class H5QsvQuizController { List subjectIds = notFinishRecordOptional.get().getSubjectIds(); Cnd cnd = Cnd.where("id", "in", subjectIds); String dynamic = subjectIds.stream().map(id -> "'" + id + "'").collect(Collectors.joining(",")); - cnd.and(new Static("1=1 ORDER BY FIELD (id," + dynamic + ")")); + cnd.orderBy("sortNum","asc"); resultSubjects = dao.query(QsvSubject.class, cnd); answerRecordId = notFinishRecordOptional.get().getId(); } else { @@ -244,7 +245,7 @@ public class H5QsvQuizController { List subjectIds = maxAnswerRecordOptional.get().getSubjectIds(); Cnd cnd = Cnd.where("id", "in", subjectIds); String dynamic = subjectIds.stream().map(id -> "'" + id + "'").collect(Collectors.joining(",")); - cnd.and(new Static("1=1 ORDER BY FIELD (id," + dynamic + ")")); + cnd.orderBy("sortNum","asc"); resultSubjects = dao.query(QsvSubject.class, cnd); answerRecordId = maxAnswerRecordOptional.get().getId(); } else { @@ -280,7 +281,7 @@ public class H5QsvQuizController { List subjectIds = answerRecord.getSubjectIds(); Cnd cnd = Cnd.where("id", "in", subjectIds); String dynamic = subjectIds.stream().map(id -> "'" + id + "'").collect(Collectors.joining(",")); - cnd.and(new Static("1=1 ORDER BY FIELD (id," + dynamic + ")")); + cnd.orderBy("sortNum","asc"); resultSubjects = dao.query(QsvSubject.class, cnd); } else { //今天最大那次的记录 @@ -291,7 +292,7 @@ public class H5QsvQuizController { List subjectIds = maxTodayRecord.get().getSubjectIds(); Cnd cnd = Cnd.where("id", "in", subjectIds); String dynamic = subjectIds.stream().map(id -> "'" + id + "'").collect(Collectors.joining(",")); - cnd.and(new Static("1=1 ORDER BY FIELD (id," + dynamic + ")")); + cnd.orderBy("sortNum","asc"); resultSubjects = dao.query(QsvSubject.class, cnd); } else { throw new RuntimeException("业务异常"); @@ -305,9 +306,12 @@ public class H5QsvQuizController { answerRecordId = answerRecord.getId(); List subjectIds = answerRecord.getSubjectIds(); + if(CollectionUtil.isEmpty(subjectIds)){ + throw new RuntimeException("题目列表为空!请检查答题显示日期"); + } Cnd cnd = Cnd.where("id", "in", subjectIds); String dynamic = subjectIds.stream().map(id -> "'" + id + "'").collect(Collectors.joining(",")); - cnd.and(new Static("1=1 ORDER BY FIELD (id," + dynamic + ")")); + cnd.orderBy("sortNum","asc"); resultSubjects = dao.query(QsvSubject.class, cnd); } } @@ -340,6 +344,13 @@ public class H5QsvQuizController { float totalScore = 0; for (QsvAnswerParam.Subject subject : qsvAnswerParam.getSubjects()) { + QsvSubject dbSubject = dao.fetch(QsvSubject.class, subject.getId()); + if (ObjectUtil.isNotEmpty(dbSubject) && "checkbox".equals(dbSubject.getType()) + && ObjectUtil.isNotEmpty(dbSubject.getMaxMulti()) && dbSubject.getMaxMulti() > 0 + && CollectionUtil.size(subject.getUserSelectOptionIds()) > dbSubject.getMaxMulti()) { + // 后端兜底校验最大可选数,避免绕过前端直接提交超限答案。 + return Result.error("题目【" + dbSubject.getTitle() + "】最多只能选择" + dbSubject.getMaxMulti() + "项"); + } JSONObject entries = extJson.get(subject.getId(), JSONObject.class); if (ObjectUtil.isNotEmpty(entries)) { entries.set("optionIds", subject.getUserSelectOptionIds()); @@ -384,7 +395,7 @@ public class H5QsvQuizController { Cnd cnd = Cnd.where("id", "in", subjectIds); String dynamic = subjectIds.stream().map(id -> "'" + id + "'").collect(Collectors.joining(",")); - cnd.and(new Static("1=1 ORDER BY FIELD (id," + dynamic + ")")); + cnd.orderBy("sortNum","asc"); List subjects = dao.query(QsvSubject.class, cnd); dao.fetchLinks(subjects, "options", Cnd.NEW().asc("sortNum")); diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/controller/UnionReimburseApplyController.java b/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/controller/UnionReimburseApplyController.java index b10116f4..b53b50b6 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/controller/UnionReimburseApplyController.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/controller/UnionReimburseApplyController.java @@ -27,6 +27,7 @@ import com.budwk.app.zhgh.staffbenefit.condolence.service.CondolenceTypeService; import com.budwk.app.zhgh.dayofficework.unionReimburse.model.UnionReimburse; import com.budwk.app.zhgh.staffbenefit.condolence.model.CondolenceType; import com.budwk.app.zhgh.dayofficework.unionReimburse.service.UnionReimburseService; +import com.budwk.app.zhgh.dayofficework.unionReimburse.vo.UnionReimburseBankHistoryVO; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; @@ -85,19 +86,7 @@ public class UnionReimburseApplyController { @SaCheckPermission(value = {"unionReimburse.apply", "h5.unionReimburse.apply"}, mode = SaMode.OR) @SLog(type = "unionReimburse", tag = "工会报销-报销申请", msg = "保存报销申请") public Result save(@Param("data") UnionReimburse unionReimburse) { - if (StrUtil.isBlank(unionReimburse.getId())) { - unionReimburse.setCreateTime(new Date()); - String documentNo = generateDocumentNo(); - unionReimburse.setDocumentNo(documentNo); - } - unionReimburse.setStateId(1);//待提交 - if (("UNION_REIMBURSE_PROJECT_1").equals(unionReimburse.getReimburseProject())){ - unionReimburse.setRealMoney(unionReimburse.getCondolenceMoney()); - }else { - unionReimburse.setRealMoney(unionReimburse.getMoney()); - } - dao.insertOrUpdate(unionReimburse); - return Result.success(); + return unionReimburseService.saveApply(unionReimburse, 1); } @At @@ -105,18 +94,10 @@ public class UnionReimburseApplyController { @Aop(TransAop.READ_COMMITTED) @SaCheckPermission(value = {"unionReimburse.apply", "h5.unionReimburse.apply"}, mode = SaMode.OR) public Result submit(@Param("data") UnionReimburse unionReimburse) { - if (StrUtil.isBlank(unionReimburse.getId())) { - unionReimburse.setCreateTime(new Date()); - String documentNo = generateDocumentNo(); - unionReimburse.setDocumentNo(documentNo); + Result result = unionReimburseService.saveApply(unionReimburse, 2); + if (result.getCode() != 0) { + return result; } - unionReimburse.setStateId(2);//待审核 - if (("UNION_REIMBURSE_PROJECT_1").equals(unionReimburse.getReimburseProject())){ - unionReimburse.setRealMoney(unionReimburse.getCondolenceMoney()); - }else { - unionReimburse.setRealMoney(unionReimburse.getMoney()); - } - dao.insertOrUpdate(unionReimburse); // // 开启流程实例 // Dict args = Dict.create(); // args.set(FlowConst.SUBMIT_TYPE, ProcessSubmitTypeEnum.APPLY.getCode()); @@ -153,7 +134,7 @@ public class UnionReimburseApplyController { @SaCheckLogin @ApiOperation("获取当前登录人的申请信息") public Result info(@Param("id") String id) { - UnionReimburse unionReimburse = dao.fetch(UnionReimburse.class, id); + UnionReimburse unionReimburse = unionReimburseService.getApplyForm(id); return Result.success(unionReimburse); } @@ -190,14 +171,9 @@ public class UnionReimburseApplyController { cnd.and(seg); } cnd.and(View_user::getId, "!=", SecurityUtil.getUserId()); - /*if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name())) { - if (AuthUtil.hasRoleOr(RoleConstant.BRANCH_UNION_ADMIN.name(), RoleConstant.BRANCH_UNION_CHAIRMAN.name())) { - cnd.and(View_user::getUnionId, "=", SecurityUtil.getUnionId()); - } else { - cnd.and(View_user::getId, "=", SecurityUtil.getUserId()); - } - - }*/ + if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name())) { + cnd.and(View_user::getUnionId, "=", SecurityUtil.getUnionId()); + } sql.setCondition(cnd); Pagination pagination = unionReimburseService.listPageMap(1, 50, sql); return Result.success(pagination.getList()); @@ -215,20 +191,25 @@ public class UnionReimburseApplyController { @ApiOperation("查询厉害账号信息") @SaCheckPermission(value = {"unionReimburse.apply", "h5.unionReimburse.apply"}, mode = SaMode.OR) public Result findUserBankHistory(String payer) { - Sql sql = Sqls.create(""" - SELECT DISTINCT - bankCardNumber, - bankOfDeposit - FROM union_reimburse - $condition - """); - Cnd cnd = Cnd.NEW(); - cnd.andEX("payer", "=", payer); - sql.setCondition(cnd); - List result = unionReimburseService.listMap(sql); + List result = unionReimburseService.getUserBankHistory(payer); return Result.success(result); } + @At + @ApiOperation("校验发票号码是否重复") + @SaCheckPermission(value = {"unionReimburse.apply", "h5.unionReimburse.apply"}, mode = SaMode.OR) + public Result checkInvoiceDuplicate(String invoiceNo, String reimburseId) { + Result result = unionReimburseService.checkInvoiceDuplicate(invoiceNo, reimburseId); + return result == null ? Result.success() : result; + } + + @At + @ApiOperation("自动识别发票信息") + @SaCheckPermission(value = {"unionReimburse.apply", "h5.unionReimburse.apply"}, mode = SaMode.OR) + public Result recognizeInvoice(String fileId, @Param("verifySwitch") Boolean verifySwitch) { + return unionReimburseService.recognizeInvoice(fileId, verifySwitch); + } + @At @ApiOperation("查询校工会经费余额") @@ -281,39 +262,4 @@ public class UnionReimburseApplyController { // 如果没有找到该社团的经费记录,返回0 return Result.success(0.0); } - @At - @ApiOperation("生成文件编号") - private String generateDocumentNo() { - int currentYear = DateUtil.thisYear(); - String yearPrefix = String.valueOf(currentYear); - - org.nutz.dao.Cnd cnd = org.nutz.dao.Cnd.where("documentNo", "LIKE", yearPrefix + "%"); - cnd.desc("documentNo"); - cnd.limit(1); - - List list = dao.query(UnionReimburse.class, cnd); - - int nextSeq = 1; // 默认从01开始 - if (!list.isEmpty()) { - String maxNo = list.get(0).getDocumentNo(); - if (maxNo != null && maxNo.startsWith(yearPrefix)) { - // 提取序号部分(去掉年份前缀) - String seqPart = maxNo.substring(yearPrefix.length()); - try { - int currentMaxSeq = Integer.parseInt(seqPart); - nextSeq = currentMaxSeq + 1; - } catch (NumberFormatException e) { - // 如果解析失败,使用默认值1 - nextSeq = 1; - } - } - } - - // 格式化序号为两位数 - String seqStr = String.format("%02d", nextSeq); - return yearPrefix + seqStr; - } - - - } diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/controller/UnionReimburseCertifierUserSignController.java b/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/controller/UnionReimburseCertifierUserSignController.java index ce76cc30..c5524f81 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/controller/UnionReimburseCertifierUserSignController.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/controller/UnionReimburseCertifierUserSignController.java @@ -3,11 +3,13 @@ package com.budwk.app.zhgh.dayofficework.unionReimburse.controller; import cn.dev33.satoken.annotation.SaCheckPermission; import cn.dev33.satoken.annotation.SaMode; 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.param.PageForm; 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.web.commons.auth.utils.AuthUtil; import com.budwk.app.web.commons.auth.utils.SecurityUtil; import com.budwk.app.zhgh.dayofficework.unionReimburse.service.UnionReimburseService; import io.swagger.annotations.Api; @@ -105,6 +107,9 @@ public class UnionReimburseCertifierUserSignController { // 工会、单位名称查询条件 cnd.andEX("info.unionId", "=", unionId); cnd.andEX("info.unitId", "=", unitId); + if (!isAdmin()) { + cnd.andEX("info.unionId", "=", SecurityUtil.getUnionId()); + } // 报销项目查询条件 cnd.andEX("info.reimburseProject", "=", reimburseProject); @@ -116,10 +121,11 @@ public class UnionReimburseCertifierUserSignController { seg.orLike("info.loginName",pageForm.getSearchKeyword()); cnd.and(seg); } - if (StrUtil.isAllBlank(pageForm.getPageOrderName(), pageForm.getPageOrderBy())) { + String orderField = normalizeSortField(pageForm.getPageOrderName()); + if (StrUtil.isAllBlank(orderField, pageForm.getPageOrderBy())) { cnd.desc("info.createTime"); } else { - cnd.orderBy(pageForm.getPageOrderName(), PageUtil.getOrder(pageForm.getPageOrderBy())); + cnd.orderBy(orderField, PageUtil.getOrder(pageForm.getPageOrderBy())); } cnd.groupBy("t.id"); cnd.desc("t.createdAt"); @@ -128,4 +134,21 @@ public class UnionReimburseCertifierUserSignController { return Result.success(pagination); } + private boolean isAdmin() { + return AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name()); + } + + private String normalizeSortField(String pageOrderName) { + if (StrUtil.isBlank(pageOrderName)) { + return null; + } + return switch (pageOrderName) { + case "loginName" -> "info.loginName"; + case "unionName" -> "info.unionName"; + case "unitName" -> "info.unitName"; + case "createTime" -> "info.createTime"; + case "reimburseProject" -> "info.reimburseProject"; + default -> null; + }; + } } diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/controller/UnionReimburseCollectController.java b/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/controller/UnionReimburseCollectController.java index 4896f3d3..7c2dda77 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/controller/UnionReimburseCollectController.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/controller/UnionReimburseCollectController.java @@ -12,6 +12,7 @@ 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.utils.PageUtil; import com.budwk.app.base.utils.CommonDownloadUtil; import com.budwk.app.flow.engine.FlowEngine; import com.budwk.app.sys.models.Sys_dict; @@ -93,6 +94,9 @@ public class UnionReimburseCollectController { // 工会、单位名称查询条件 cnd.andEX("info.unionId", "=", unionId); cnd.andEX("info.unitId", "=", unitId); + if (!isAdmin()) { + cnd.andEX("info.unionId", "=", SecurityUtil.getUnionId()); + } // 报销项目查询条件 cnd.andEX("info.reimburseProject", "=", reimburseProject); @@ -106,7 +110,7 @@ public class UnionReimburseCollectController { cnd.and("info.stateId", "in", List.of(3, 2)); - cnd.desc("info.createTime"); + applyListSort(cnd, pageForm); sql.setCondition(cnd); Pagination pagination = unionReimburseService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql); return Result.success(pagination); @@ -149,7 +153,7 @@ public class UnionReimburseCollectController { cnd.andEX("YEAR(rei.createTime)", "=", year); cnd.andEX("u.unitid", "=", unitId); cnd.andEX("u.unionid", "=", unionId); - if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name())) { + if (!isAdmin()) { cnd.andEX("u.unionid", "=", SecurityUtil.getUnionId()); } cnd.desc("rei.createTime"); @@ -166,8 +170,10 @@ public class UnionReimburseCollectController { try{ response.setContentType("application/octet-stream"); - response.setHeader("Content-Disposition", "attachment;filename=" + new String(("收款人名册.xls").getBytes("utf-8"), "ISO8859-1")); - Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(), excelExportEntities, list); + response.setHeader("Content-Disposition", "attachment;filename=" + new String(("收款人名册.xlsx").getBytes("utf-8"), "ISO8859-1")); + ExportParams exportParams = new ExportParams(); + exportParams.setType(ExcelType.XSSF); + Workbook workbook = ExcelExportUtil.exportExcel(exportParams, excelExportEntities, list); workbook.write(response.getOutputStream()); }catch (Exception e){ e.printStackTrace(); @@ -197,6 +203,9 @@ public class UnionReimburseCollectController { Cnd cnd = buildCondition(year, unionId, unitId, reimburseProject, userName, "t1."); // 只查询流程实例状态为20的数据(已完成状态) cnd.and("ins.state", "=", 20); + if (!isAdmin()) { + cnd.andEX("t1.unionId", "=", SecurityUtil.getUnionId()); + } cnd.desc("t1.createTime"); sql.setCondition(cnd); @@ -275,4 +284,34 @@ public class UnionReimburseCollectController { return cnd; } + + private boolean isAdmin() { + return AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name()); + } + + /** + * 收集页列表只允许固定字段排序,前端传入原序时回退到默认申请时间倒序。 + */ + private void applyListSort(Cnd cnd, PageForm pageForm) { + String orderField = normalizeSortField(pageForm.getPageOrderName()); + if (StrUtil.isAllBlank(orderField, pageForm.getPageOrderBy())) { + cnd.desc("info.createTime"); + return; + } + cnd.orderBy(orderField, PageUtil.getOrder(pageForm.getPageOrderBy())); + } + + private String normalizeSortField(String pageOrderName) { + if (StrUtil.isBlank(pageOrderName)) { + return null; + } + return switch (pageOrderName) { + case "loginName" -> "info.loginName"; + case "unionName" -> "info.unionName"; + case "unitName" -> "info.unitName"; + case "createTime" -> "info.createTime"; + case "reimburseProject" -> "info.reimburseProject"; + default -> null; + }; + } } diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/controller/UnionReimburseMineController.java b/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/controller/UnionReimburseMineController.java index 99658c4f..779b5b30 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/controller/UnionReimburseMineController.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/controller/UnionReimburseMineController.java @@ -9,14 +9,17 @@ import cn.hutool.core.io.IoUtil; import cn.hutool.core.util.StrUtil; import com.budwk.app.base.annotation.SLog; import com.budwk.app.base.model.Audit; +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.utils.CommonDownloadUtil; import com.budwk.app.base.utils.MoneyUtil; import com.budwk.app.base.utils.OfficePlusUtil; +import com.budwk.app.base.utils.PageUtil; import com.budwk.app.base.utils.SysOfficeTemplateUtil; 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.unionReimburse.model.UnionReimburse; import com.budwk.app.zhgh.dayofficework.unionReimburse.service.UnionReimburseService; @@ -133,6 +136,9 @@ public class UnionReimburseMineController { // 工会、单位名称查询条件 cnd.andEX("info.unionId", "=", unionId); cnd.andEX("info.unitId", "=", unitId); + if (!isAdmin()) { + cnd.andEX("info.unionId", "=", SecurityUtil.getUnionId()); + } // 报销项目查询条件 cnd.andEX("info.reimburseProject", "=", reimburseProject); @@ -143,13 +149,40 @@ public class UnionReimburseMineController { seg.orLike("info.loginName", "%" + searchKeyword + "%"); cnd.and(seg); } - cnd.desc("info.createTime"); + applyListSort(cnd, pageForm); cnd.and("info.userId", "=", SecurityUtil.getUserId()); sql.setCondition(cnd); Pagination pagination = unionReimburseService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql); return Result.success(pagination); } + private boolean isAdmin() { + return AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name()); + } + + private void applyListSort(Cnd cnd, PageForm pageForm) { + String orderField = normalizeSortField(pageForm.getPageOrderName()); + if (StrUtil.isAllBlank(orderField, pageForm.getPageOrderBy())) { + cnd.desc("info.createTime"); + return; + } + cnd.orderBy(orderField, PageUtil.getOrder(pageForm.getPageOrderBy())); + } + + private String normalizeSortField(String pageOrderName) { + if (StrUtil.isBlank(pageOrderName)) { + return null; + } + return switch (pageOrderName) { + case "loginName" -> "info.loginName"; + case "unionName" -> "info.unionName"; + case "unitName" -> "info.unitName"; + case "createTime" -> "info.createTime"; + case "reimburseProject" -> "info.reimburseProject"; + default -> null; + }; + } + @At @ApiOperation("删除") @Aop(TransAop.READ_COMMITTED) diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/controller/UnionReimburseReviewController.java b/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/controller/UnionReimburseReviewController.java index 0df7de86..5f3fa5f9 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/controller/UnionReimburseReviewController.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/controller/UnionReimburseReviewController.java @@ -1,7 +1,5 @@ package com.budwk.app.zhgh.dayofficework.unionReimburse.controller; - -import cn.dev33.satoken.annotation.SaCheckLogin; import cn.dev33.satoken.annotation.SaCheckPermission; import cn.dev33.satoken.annotation.SaMode; import cn.hutool.core.util.StrUtil; @@ -10,15 +8,12 @@ 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.flow.enums.ProcessTaskStateEnum; -import com.budwk.app.web.commons.auth.utils.SecurityUtil; import com.budwk.app.zhgh.dayofficework.unionReimburse.model.UnionReimburse; import com.budwk.app.zhgh.dayofficework.unionReimburse.service.UnionReimburseService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; import org.nutz.aop.interceptor.ioc.TransAop; -import org.nutz.dao.Chain; import org.nutz.dao.Cnd; import org.nutz.dao.Dao; import org.nutz.dao.Sqls; @@ -35,8 +30,6 @@ import org.nutz.mvc.annotation.Param; import java.util.Date; import java.util.List; -import static org.openjdk.nashorn.internal.runtime.regexp.joni.Config.log; - @IocBean @At("/platform/unionReimburse/review") @Ok("json:full") @@ -49,12 +42,12 @@ public class UnionReimburseReviewController { @Inject private UnionReimburseService unionReimburseService; - @At("/index") @Ok("beetl:/platform/zhgh/dayofficework/unionReimburse/review/index.html") @SaCheckPermission("unionReimburse.review") public void index() { } + @At("/h5") @Ok("beetl:/platform/zhghh5/dayofficework/unionReimburse/review/index.html") @SaCheckPermission("h5.unionReimburse.review") @@ -71,37 +64,26 @@ public class UnionReimburseReviewController { String unitId, String reimburseProject) { Sql sql = Sqls.create(""" - SELECT + SELECT info.* FROM union_reimburse info - $condition + $condition """); Cnd cnd = Cnd.NEW(); - if (approval) { - // 已审核:查询 stateId 为 3(报销成功)、4(拒绝)、5(退回)的记录 cnd.and("info.stateId", "in", List.of(3, 4, 5)); } else { - // 未审核:只查询 stateId 为 2(待审核确认)的记录 cnd.and("info.stateId", "=", 2); } - // 年度查询条件 cnd.andEX("year(info.createTime)", "=", year); - - // 工会、单位名称查询条件 cnd.andEX("info.unionId", "=", unionId); cnd.andEX("info.unitId", "=", unitId); - - // 报销项目查询条件 cnd.andEX("info.reimburseProject", "=", reimburseProject); - - - // 姓名和工号查询条件 if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) { SqlExpressionGroup seg = new SqlExpressionGroup(); - seg.orLike("info.userName",pageForm.getSearchKeyword()); - seg.orLike("info.loginName",pageForm.getSearchKeyword()); + seg.orLike("info.userName", pageForm.getSearchKeyword()); + seg.orLike("info.loginName", pageForm.getSearchKeyword()); cnd.and(seg); } if (StrUtil.isAllBlank(pageForm.getPageOrderName(), pageForm.getPageOrderBy())) { @@ -119,21 +101,18 @@ public class UnionReimburseReviewController { @ApiOperation("审核") @Aop(TransAop.READ_COMMITTED) @SaCheckPermission(value = {"unionReimburse.review", "h5.unionReimburse.review"}, mode = SaMode.OR) - @SLog( tag = "审核工会报销", msg = "审核工会报销") - public Result reviewTask(@Param("data") UnionReimburse unionReimburse,String submitType) { + @SLog(tag = "审核工会报销", msg = "审核工会报销") + public Result reviewTask(@Param("data") UnionReimburse unionReimburse, String submitType) { if (submitType == null || !List.of("3", "4", "5").contains(submitType)) { return Result.error("无效的审核操作类型"); } - UnionReimburse dbRecord = dao.fetch(UnionReimburse.class, unionReimburse.getId()); if (dbRecord == null) { return Result.error("记录不存在"); } - dbRecord.setReviewTime(new Date()); dbRecord.setStateId(Integer.parseInt(submitType)); dbRecord.setReviewOpinion(unionReimburse.getReviewOpinion()); - dao.update(dbRecord); return Result.success(); } @@ -142,7 +121,7 @@ public class UnionReimburseReviewController { @ApiOperation("一键审核") @Aop(TransAop.READ_COMMITTED) @SaCheckPermission(value = {"unionReimburse.review", "h5.unionReimburse.review"}, mode = SaMode.OR) - @SLog( tag = "一键审核", msg = "一键审核") + @SLog(tag = "一键审核", msg = "一键审核") public Result allReview() { try { List reimbursements = unionReimburseService.query(Cnd.where("stateId", "=", 2)); @@ -158,6 +137,4 @@ public class UnionReimburseReviewController { return Result.error("一键审核失败: " + e.getMessage()); } } - - } diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/controller/UnionReimburseStatisticsController.java b/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/controller/UnionReimburseStatisticsController.java index b4c0dd22..315ad87b 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/controller/UnionReimburseStatisticsController.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/controller/UnionReimburseStatisticsController.java @@ -6,11 +6,14 @@ 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 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.flow.engine.FlowEngine; import com.budwk.app.sys.services.SysDictService; +import com.budwk.app.web.commons.auth.utils.AuthUtil; +import com.budwk.app.web.commons.auth.utils.SecurityUtil; import com.budwk.app.zhgh.dayofficework.unionReimburse.param.UnionReimbursePageForm; import com.budwk.app.zhgh.dayofficework.unionReimburse.service.UnionReimburseService; import io.swagger.annotations.Api; @@ -93,6 +96,8 @@ public class UnionReimburseStatisticsController { } if (Strings.isNotBlank(unionId)) { sql.setVar("unionIdCnd", String.format("AND us.unionId = '%s' ", unionId)); + } else if (!isAdmin()) { + sql.setVar("unionIdCnd", String.format("AND us.unionId = '%s' ", SecurityUtil.getUnionId())); } if (Strings.isNotBlank(condolenceTypeId)) { sql.setVar("typeCnd", String.format("AND con.condolenceTypeId = '%s' ", condolenceTypeId)); @@ -150,4 +155,7 @@ public class UnionReimburseStatisticsController { } + private boolean isAdmin() { + return AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name()); + } } diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/model/UnionReimburse.java b/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/model/UnionReimburse.java index c84b5c42..d4235db4 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/model/UnionReimburse.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/model/UnionReimburse.java @@ -415,4 +415,9 @@ public class UnionReimburse extends BaseModel { @ColDefine(type = ColType.DATETIME) private Date reviewTime; + /** + * 发票明细列表仅用于申请页表单回显和提交,不直接映射主表字段。 + */ + private List invoiceDetails; + } diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/model/UnionReimburseInvoiceDetail.java b/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/model/UnionReimburseInvoiceDetail.java new file mode 100644 index 00000000..a526f7b7 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/model/UnionReimburseInvoiceDetail.java @@ -0,0 +1,66 @@ +package com.budwk.app.zhgh.dayofficework.unionReimburse.model; + +import cn.hutool.json.JSONObject; +import com.budwk.app.base.model.BaseModel; +import lombok.Data; +import lombok.EqualsAndHashCode; +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.entity.annotation.Table; +import org.nutz.dao.interceptor.annotation.PrevInsert; + +import java.util.List; + +/** + * 工会报销发票明细。 + * 每条明细对应一张发票文件,保存发票号码、金额和销售方等信息。 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Table("union_reimburse_invoice_detail") +public class UnionReimburseInvoiceDetail extends BaseModel { + + @Name + @Comment("ID") + @ColDefine(type = ColType.VARCHAR, width = 32) + @PrevInsert(uu32 = true) + private String id; + + @Column + @Comment("报销主表ID") + @ColDefine(type = ColType.VARCHAR, width = 32) + private String reimburseId; + + @Column + @Comment("发票文件") + @ColDefine(type = ColType.MYSQL_JSON) + private List invoiceFiles; + + @Column + @Comment("发票号码") + @ColDefine(type = ColType.VARCHAR, width = 100) + private String invoiceNo; + + @Column + @Comment("发票金额") + @ColDefine(type = ColType.FLOAT, width = 10, precision = 2) + private Double invoiceAmount; + + @Column + @Comment("销售方信息名称") + @ColDefine(type = ColType.VARCHAR, width = 200) + private String sellerName; + + @Column + @Comment("项目名称") + @ColDefine(type = ColType.VARCHAR, width = 500) + private String itemName; + + @Column + @Comment("备注") + @ColDefine(type = ColType.VARCHAR, width = 500) + private String remark; +} diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/service/UnionReimburseService.java b/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/service/UnionReimburseService.java index cac64d45..546506e9 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/service/UnionReimburseService.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/service/UnionReimburseService.java @@ -1,11 +1,41 @@ package com.budwk.app.zhgh.dayofficework.unionReimburse.service; import com.budwk.app.base.service.BaseService; +import com.budwk.app.base.result.Result; import com.budwk.app.zhgh.dayofficework.unionReimburse.model.UnionReimburse; import com.budwk.app.zhgh.dayofficework.unionReimburse.param.UnionReimbursePageForm; +import com.budwk.app.zhgh.dayofficework.unionReimburse.vo.UnionReimburseBankHistoryVO; import org.nutz.dao.sql.Sql; +import java.util.List; + public interface UnionReimburseService extends BaseService { Sql getSql(UnionReimbursePageForm pageForm); + + /** + * 保存或提交报销申请,并同步保存发票明细。 + */ + Result saveApply(UnionReimburse unionReimburse, int stateId); + + /** + * 查询申请表单详情,补齐发票明细回显数据。 + */ + UnionReimburse getApplyForm(String id); + + /** + * 查询付款人的历史收款信息。 + */ + List getUserBankHistory(String payer); + + /** + * 校验发票号码是否与历史报销记录重复。 + */ + Result checkInvoiceDuplicate(String invoiceNo, String reimburseId); + + /** + * 识别单张发票文件,并在需要时调用腾讯云验真接口补齐票面信息。 + */ + Result recognizeInvoice(String fileId, Boolean verifySwitch); + } diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/service/impl/UnionReimburseServiceImpl.java b/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/service/impl/UnionReimburseServiceImpl.java index 55a91f56..32f2c050 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/service/impl/UnionReimburseServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/service/impl/UnionReimburseServiceImpl.java @@ -1,24 +1,69 @@ package com.budwk.app.zhgh.dayofficework.unionReimburse.service.impl; - +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; +import com.budwk.app.base.constant.RoleConstant; +import com.budwk.app.base.result.Result; import com.budwk.app.base.service.impl.BaseServiceImpl; +import com.budwk.app.sys.models.Sys_file; +import com.budwk.app.sys.services.SysFileService; import com.budwk.app.zhgh.dayofficework.unionReimburse.model.UnionReimburse; +import com.budwk.app.zhgh.dayofficework.unionReimburse.model.UnionReimburseInvoiceDetail; import com.budwk.app.zhgh.dayofficework.unionReimburse.param.UnionReimbursePageForm; import com.budwk.app.zhgh.dayofficework.unionReimburse.service.UnionReimburseService; +import com.budwk.app.zhgh.dayofficework.unionReimburse.vo.UnionReimburseBankHistoryVO; +import com.budwk.app.web.commons.auth.utils.AuthUtil; +import com.budwk.app.web.commons.auth.utils.SecurityUtil; import lombok.extern.slf4j.Slf4j; import org.nutz.dao.Cnd; import org.nutz.dao.Dao; import org.nutz.dao.Sqls; import org.nutz.dao.sql.Sql; import org.nutz.dao.util.cri.SqlExpressionGroup; +import org.nutz.ioc.impl.PropertiesProxy; +import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.lang.Lang; +import org.nutz.lang.util.NutMap; +import com.tencentcloudapi.common.Credential; +import com.tencentcloudapi.common.exception.TencentCloudSDKException; +import com.tencentcloudapi.common.profile.ClientProfile; +import com.tencentcloudapi.common.profile.HttpProfile; +import com.tencentcloudapi.ocr.v20181119.OcrClient; +import com.tencentcloudapi.ocr.v20181119.models.TextVatInvoice; +import com.tencentcloudapi.ocr.v20181119.models.VatInvoice; +import com.tencentcloudapi.ocr.v20181119.models.VatInvoiceItem; +import com.tencentcloudapi.ocr.v20181119.models.VatInvoiceOCRRequest; +import com.tencentcloudapi.ocr.v20181119.models.VatInvoiceOCRResponse; +import com.tencentcloudapi.ocr.v20181119.models.VatInvoiceVerifyRequest; +import com.tencentcloudapi.ocr.v20181119.models.VatInvoiceVerifyResponse; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Base64; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.Date; +import java.util.stream.Collectors; @Slf4j @IocBean(args = {"refer:dao"}) -public class UnionReimburseServiceImpl extends BaseServiceImpl implements UnionReimburseService { +public class UnionReimburseServiceImpl extends BaseServiceImpl implements UnionReimburseService { + @Inject + private SysFileService sysFileService; + @Inject + private PropertiesProxy conf; public UnionReimburseServiceImpl(Dao dao) { super(dao); } @@ -35,30 +80,609 @@ public class UnionReimburseServiceImpl extends BaseServiceImpl $condition """); Cnd cnd = Cnd.NEW(); - // 年度查询条件 cnd.andEX("year(info.createTime)", "=", pageForm.getYear()); - - // 工会、单位名称查询条件 cnd.andEX("info.condolenceUnionId", "=", pageForm.getUnionId()); cnd.andEX("info.unitId", "=", pageForm.getUnitId()); - cnd.andEX("info.condolenceTypeId", "=", pageForm.getCondolenceTypeId()); cnd.andEX("info.way", "=", pageForm.getWay()); - - // 报销项目查询条件 cnd.andEX("info.reimburseProject", "=", pageForm.getReimburseProject()); - // 姓名和工号查询条件 + if (!isAdmin()) { + cnd.andEX("info.condolenceUnionId", "=", SecurityUtil.getUnionId()); + } if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) { SqlExpressionGroup seg = new SqlExpressionGroup(); - seg.orLike("info.condolenceUserName",pageForm.getSearchKeyword()); - seg.orLike("info.condolenceLoginName",pageForm.getSearchKeyword()); + seg.orLike("info.condolenceUserName", pageForm.getSearchKeyword()); + seg.orLike("info.condolenceLoginName", pageForm.getSearchKeyword()); cnd.and(seg); } - cnd.and("info.stateId", "in", List.of(3, 2)); - - cnd.desc("info.createTime"); + applyStatisticsSort(cnd, pageForm); sql.setCondition(cnd); return sql; } + + @Override + public Result saveApply(UnionReimburse unionReimburse, int stateId) { + if (unionReimburse == null) { + return Result.error("报销数据不能为空"); + } + UnionReimburse oldRecord = null; + if (StrUtil.isNotBlank(unionReimburse.getId())) { + oldRecord = this.fetch(unionReimburse.getId()); + if (oldRecord == null) { + return Result.error("未找到对应的报销记录"); + } + } + + normalizeInvoiceDetails(unionReimburse); + if (needInvoiceDetails(unionReimburse)) { + fillInvoiceSummary(unionReimburse); + } + if (stateId == 2) { + Result validateResult = validateBeforeSubmit(unionReimburse); + if (validateResult != null) { + return validateResult; + } + Result duplicateResult = validateInvoiceDuplicate(unionReimburse); + if (duplicateResult != null) { + return duplicateResult; + } + } + + if (oldRecord == null) { + unionReimburse.setCreateTime(DateUtil.date()); + unionReimburse.setDocumentNo(generateDocumentNo()); + } else { + unionReimburse.setCreateTime(oldRecord.getCreateTime()); + unionReimburse.setDocumentNo(oldRecord.getDocumentNo()); + } + unionReimburse.setStateId(stateId); + if ("UNION_REIMBURSE_PROJECT_1".equals(unionReimburse.getReimburseProject())) { + unionReimburse.setRealMoney(unionReimburse.getCondolenceMoney()); + } else { + unionReimburse.setRealMoney(unionReimburse.getMoney()); + } + this.dao().insertOrUpdate(unionReimburse); + saveInvoiceDetails(unionReimburse.getId(), unionReimburse.getInvoiceDetails()); + return Result.success(stateId == 2 ? "提交成功" : "保存成功"); + } + + @Override + public UnionReimburse getApplyForm(String id) { + UnionReimburse unionReimburse = this.fetch(id); + if (unionReimburse == null) { + return null; + } + List invoiceDetails = this.dao().query( + UnionReimburseInvoiceDetail.class, + Cnd.where("reimburseId", "=", id).asc("createdAt") + ); + unionReimburse.setInvoiceDetails(invoiceDetails == null ? new ArrayList<>() : invoiceDetails); + return unionReimburse; + } + + @Override + public List getUserBankHistory(String payer) { + if (StrUtil.isBlank(payer)) { + return new ArrayList<>(); + } + Sql sql = Sqls.create(""" + SELECT + bankUserName, + bankCardNumber, + bankOfDeposit + FROM + union_reimburse + $condition + GROUP BY + bankUserName, + bankCardNumber, + bankOfDeposit + ORDER BY + MAX(createTime) DESC + """); + Cnd cnd = Cnd.NEW(); + cnd.and("payer", "=", payer); + cnd.and("bankUserName", "is not", null); + cnd.and("bankCardNumber", "is not", null); + cnd.and("bankOfDeposit", "is not", null); + sql.setCondition(cnd); + return this.listVO(sql, UnionReimburseBankHistoryVO.class); + } + + @Override + public Result checkInvoiceDuplicate(String invoiceNo, String reimburseId) { + String currentInvoiceNo = StrUtil.blankToDefault(StrUtil.trim(invoiceNo), ""); + if (StrUtil.isBlank(currentInvoiceNo)) { + return Result.error("发票号码不能为空"); + } + return queryHistoryInvoiceDuplicate(currentInvoiceNo, reimburseId); + } + + @Override + public Result recognizeInvoice(String fileId, Boolean verifySwitch) { + if (StrUtil.isBlank(fileId)) { + return Result.error("请先上传发票文件"); + } + Sys_file sysFile = sysFileService.fetch(fileId); + if (sysFile == null) { + return Result.error("发票文件不存在"); + } + Credential credential = getInvoiceCredential(); + if (credential == null) { + return Result.error("未配置腾讯云发票识别密钥,请先在 application-dev.yaml 中配置 invoice.tencent.secret-id 和 invoice.tencent.secret-key"); + } + try { + byte[] fileBytes = sysFileService.download(fileId); + VatInvoiceOCRResponse response = executeOcr(credential, sysFile, fileBytes); + NutMap result = buildOcrResult(response); + String msg = "发票识别成功"; + if (Boolean.TRUE.equals(verifySwitch)) { + String verifyMsg = enrichByVerify(credential, result); + msg = StrUtil.isBlank(verifyMsg) ? "发票识别并校验成功" : verifyMsg; + } + return Result.success(msg, result); + } catch (TencentCloudSDKException e) { + log.error("腾讯云发票识别失败,fileId:{}", fileId, e); + return Result.error("腾讯云发票识别失败:" + e.getMessage()); + } catch (Exception e) { + log.error("发票识别失败,fileId:{}", fileId, e); + return Result.error("发票识别失败:" + e.getMessage()); + } + } + + /** + * 统一规整发票明细中的文本和金额,避免空格、空数组等脏数据进入库表。 + */ + private void normalizeInvoiceDetails(UnionReimburse unionReimburse) { + if (unionReimburse.getInvoiceDetails() == null) { + unionReimburse.setInvoiceDetails(new ArrayList<>()); + return; + } + List invoiceDetails = unionReimburse.getInvoiceDetails().stream() + .filter(Objects::nonNull) + .collect(Collectors.toList()); + invoiceDetails.forEach(detail -> { + detail.setInvoiceNo(StrUtil.trim(detail.getInvoiceNo())); + detail.setSellerName(StrUtil.trim(detail.getSellerName())); + detail.setItemName(StrUtil.trim(detail.getItemName())); + detail.setRemark(StrUtil.trim(detail.getRemark())); + if (detail.getInvoiceFiles() == null) { + detail.setInvoiceFiles(new ArrayList<>()); + } + if (detail.getInvoiceAmount() != null) { + detail.setInvoiceAmount(BigDecimal.valueOf(detail.getInvoiceAmount()) + .setScale(2, RoundingMode.HALF_UP) + .doubleValue()); + } + }); + unionReimburse.setInvoiceDetails(invoiceDetails); + } + + /** + * 活动类报销由发票明细自动汇总金额和发票张数,减少手工输入出错。 + */ + private void fillInvoiceSummary(UnionReimburse unionReimburse) { + double totalMoney = 0D; + int invoiceFileCount = 0; + for (UnionReimburseInvoiceDetail detail : unionReimburse.getInvoiceDetails()) { + if (detail.getInvoiceAmount() != null) { + totalMoney = BigDecimal.valueOf(totalMoney) + .add(BigDecimal.valueOf(detail.getInvoiceAmount())) + .doubleValue(); + } + if (Lang.isNotEmpty(detail.getInvoiceFiles())) { + invoiceFileCount++; + } + } + unionReimburse.setMoney(BigDecimal.valueOf(totalMoney).setScale(2, RoundingMode.HALF_UP).doubleValue()); + unionReimburse.setInvoiceNumber(String.valueOf(invoiceFileCount)); + } + + /** + * 提交前补一层后端校验,防止前端绕过校验直接提交。 + */ + private Result validateBeforeSubmit(UnionReimburse unionReimburse) { + if (StrUtil.isBlank(unionReimburse.getMobile())) { + return Result.error("请填写联系方式"); + } + if (!isValidContact(unionReimburse.getMobile())) { + return Result.error("请输入正确的联系方式"); + } + if (StrUtil.isBlank(unionReimburse.getBankUserName())) { + return Result.error("请填写户名"); + } + if (StrUtil.isBlank(unionReimburse.getBankCardNumber())) { + return Result.error("请填写银行账号"); + } + if (StrUtil.isBlank(unionReimburse.getBankOfDeposit())) { + return Result.error("请填写开户行"); + } + if (StrUtil.isBlank(unionReimburse.getPaymentNotes())) { + return Result.error("请填写支付内容"); + } + if (!needInvoiceDetails(unionReimburse)) { + return null; + } + if (StrUtil.isBlank(unionReimburse.getActivityName())) { + return Result.error("请填写活动名称"); + } + if (StrUtil.isBlank(unionReimburse.getActivityPlace())) { + return Result.error("请填写活动地点"); + } + if (unionReimburse.getActivityTime() == null) { + return Result.error("请选择活动时间"); + } + if (Lang.isEmpty(unionReimburse.getFiles())) { + return Result.error("请上传附件"); + } + if (Lang.isEmpty(unionReimburse.getInvoiceDetails())) { + return Result.error("请至少维护一条发票明细"); + } + for (int i = 0; i < unionReimburse.getInvoiceDetails().size(); i++) { + UnionReimburseInvoiceDetail detail = unionReimburse.getInvoiceDetails().get(i); + int rowIndex = i + 1; + if (Lang.isEmpty(detail.getInvoiceFiles())) { + return Result.error("第" + rowIndex + "条发票明细请上传发票文件"); + } + if (StrUtil.isBlank(detail.getInvoiceNo())) { + return Result.error("第" + rowIndex + "条发票明细请填写发票号码"); + } + if (detail.getInvoiceAmount() == null || detail.getInvoiceAmount() <= 0) { + return Result.error("第" + rowIndex + "条发票明细请填写正确的发票金额"); + } + if (StrUtil.isBlank(detail.getSellerName())) { + return Result.error("第" + rowIndex + "条发票明细请填写销售方信息名称"); + } + if (StrUtil.isBlank(detail.getItemName())) { + return Result.error("第" + rowIndex + "条发票明细请填写项目名称"); + } + } + return null; + } + + /** + * 同次提交和历史记录都要拦截重复发票,避免重复报销。 + */ + private Result validateInvoiceDuplicate(UnionReimburse unionReimburse) { + Map> currentInvoiceMap = new LinkedHashMap<>(); + List details = unionReimburse.getInvoiceDetails(); + for (int i = 0; i < details.size(); i++) { + String invoiceNo = StrUtil.trim(details.get(i).getInvoiceNo()); + if (StrUtil.isBlank(invoiceNo)) { + continue; + } + currentInvoiceMap.computeIfAbsent(invoiceNo, key -> new ArrayList<>()).add(i + 1); + } + for (Map.Entry> entry : currentInvoiceMap.entrySet()) { + if (entry.getValue().size() > 1) { + String rowText = entry.getValue().stream().map(String::valueOf).collect(Collectors.joining("、")); + return Result.error("发票号码【" + entry.getKey() + "】与本次提交的第" + rowText + "条发票重复,不能提交"); + } + } + for (String invoiceNo : currentInvoiceMap.keySet()) { + Result duplicateResult = queryHistoryInvoiceDuplicate(invoiceNo, unionReimburse.getId()); + if (duplicateResult != null) { + return duplicateResult; + } + } + return null; + } + + /** + * 历史重复校验会排除当前单据自身,避免编辑回显时误判。 + */ + private Result queryHistoryInvoiceDuplicate(String invoiceNo, String reimburseId) { + Sql sql = Sqls.create(""" + SELECT + d.invoiceNo, + r.activityName + FROM + union_reimburse_invoice_detail d + LEFT JOIN union_reimburse r ON r.id = d.reimburseId + WHERE + d.invoiceNo = @invoiceNo + AND (@reimburseId = '' OR d.reimburseId <> @reimburseId) + LIMIT 1 + """); + sql.params().set("invoiceNo", invoiceNo); + sql.params().set("reimburseId", StrUtil.blankToDefault(StrUtil.trim(reimburseId), "")); + List repeatList = this.listMap(sql); + if (Lang.isEmpty(repeatList)) { + return null; + } + String activityName = repeatList.get(0).getString("activityName"); + if (StrUtil.isBlank(activityName)) { + activityName = "历史报销记录"; + } + return Result.error("发票号码【" + invoiceNo + "】与以往活动名称【" + activityName + "】中提交的发票重复,不能提交"); + } + + /** + * 每次按当前表单全量覆盖明细,避免旧明细残留。 + */ + /** + * 发票识别优先读取当前项目配置的 SecretId/SecretKey,没有时再回退读取用户提供的 csv 文件。 + */ + private Credential getInvoiceCredential() { + String secretId = StrUtil.trim(conf.get("invoice.tencent.secret-id")); + String secretKey = StrUtil.trim(conf.get("invoice.tencent.secret-key")); + if (StrUtil.isNotBlank(secretId) && StrUtil.isNotBlank(secretKey)) { + return new Credential(secretId, secretKey); + } + return loadCredentialFromSecretFile(); + } + + /** + * 兼容腾讯云控制台下载的密钥 csv,便于本地开发时不把明文密钥直接写进配置文件。 + */ + private Credential loadCredentialFromSecretFile() { + String secretFile = StrUtil.trim(conf.get("invoice.tencent.secret-file")); + if (StrUtil.isBlank(secretFile)) { + return null; + } + try { + Path path = Paths.get(secretFile); + if (!Files.exists(path)) { + return null; + } + List lines = Files.readAllLines(path); + if (lines.size() < 2) { + return null; + } + String[] values = lines.get(1).split(",", -1); + if (values.length < 2) { + return null; + } + String secretId = StrUtil.trim(values[0]); + String secretKey = StrUtil.trim(values[1]); + if (StrUtil.isBlank(secretId) || StrUtil.isBlank(secretKey)) { + return null; + } + return new Credential(secretId, secretKey); + } catch (Exception e) { + log.warn("读取腾讯云发票识别密钥文件失败,file:{}", secretFile, e); + return null; + } + } + + /** + * 统一创建腾讯云 OCR 客户端,避免控制器直接依赖第三方 SDK。 + */ + private OcrClient createOcrClient(Credential credential) { + HttpProfile httpProfile = new HttpProfile(); + httpProfile.setEndpoint("ocr.tencentcloudapi.com"); + ClientProfile clientProfile = new ClientProfile(); + clientProfile.setHttpProfile(httpProfile); + return new OcrClient(credential, "", clientProfile); + } + + private VatInvoiceOCRResponse executeOcr(Credential credential, Sys_file sysFile, byte[] fileBytes) throws TencentCloudSDKException { + OcrClient client = createOcrClient(credential); + VatInvoiceOCRRequest request = new VatInvoiceOCRRequest(); + request.setImageBase64(Base64.getEncoder().encodeToString(fileBytes)); + String fileName = StrUtil.blankToDefault(sysFile.getName(), "").toLowerCase(); + if (fileName.endsWith(".pdf")) { + request.setIsPdf(true); + request.setPdfPageNumber(1L); + } + return client.VatInvoiceOCR(request); + } + + /** + * 将腾讯云 OCR 返回的票面字段规整成页面可直接回填的键值结构。 + */ + private NutMap buildOcrResult(VatInvoiceOCRResponse response) { + NutMap result = NutMap.NEW(); + TextVatInvoice[] infos = response.getVatInvoiceInfos(); + VatInvoiceItem[] items = response.getItems(); + result.setv("invoiceNo", pickValue(infos, "发票号码", "号码", "No")); + result.setv("invoiceCode", pickValue(infos, "发票代码", "代码", "Code")); + result.setv("invoiceDate", normalizeDate(pickValue(infos, "开票日期", "日期", "Date"))); + result.setv("invoiceCheckCode", pickValue(infos, "校验码", "校验码后6位", "CheckCode")); + result.setv("invoiceAmount", parseMoney(pickValue(infos, "小写金额", "价税合计(小写)", "价税合计", "合计金额", "AmountWithTax"))); + result.setv("sellerName", pickValue(infos, "销售方名称", "销售方信息名称", "销售方", "SellerName")); + result.setv("itemName", joinItemNames(items)); + return result; + } + + /** + * 自动校验需要票面关键信息,缺字段时只提示人工核对,不阻塞用户继续录入。 + */ + private String enrichByVerify(Credential credential, NutMap result) { + String invoiceCode = result.getString("invoiceCode"); + String invoiceNo = result.getString("invoiceNo"); + String invoiceDate = result.getString("invoiceDate"); + String checkCode = lastDigits(result.getString("invoiceCheckCode"), 6); + if (StrUtil.isBlank(checkCode)) { + checkCode = moneyToVerifyValue(result.get("invoiceAmount")); + } + if (StrUtil.hasBlank(invoiceCode, invoiceNo, invoiceDate, checkCode)) { + return "发票识别成功,但自动校验信息不完整,请人工核对后提交"; + } + try { + OcrClient client = createOcrClient(credential); + VatInvoiceVerifyRequest request = new VatInvoiceVerifyRequest(); + request.setInvoiceCode(invoiceCode); + request.setInvoiceNo(invoiceNo); + request.setInvoiceDate(invoiceDate.replace("-", "")); + request.setAdditional(checkCode); + VatInvoiceVerifyResponse response = client.VatInvoiceVerify(request); + VatInvoice invoice = response.getInvoice(); + if (invoice == null) { + return "发票识别成功,但自动校验未返回有效结果,请人工核对后提交"; + } + result.setv("invoiceCode", invoice.getCode()); + result.setv("invoiceNo", invoice.getNumber()); + result.setv("invoiceDate", normalizeDate(invoice.getDate())); + result.setv("invoiceCheckCode", invoice.getCheckCode()); + result.setv("sellerName", invoice.getSellerName()); + result.setv("invoiceAmount", parseMoney(invoice.getAmountWithTax())); + result.setv("itemName", joinItemNames(invoice.getItems())); + return null; + } catch (Exception e) { + log.warn("腾讯云发票验真失败,invoiceNo:{}", invoiceNo, e); + return "发票识别成功,但自动校验失败,请人工核对后提交"; + } + } + + private String pickValue(TextVatInvoice[] infos, String... aliases) { + if (infos == null || aliases == null) { + return ""; + } + Set aliasSet = new LinkedHashSet<>(); + for (String alias : aliases) { + aliasSet.add(normalizeFieldName(alias)); + } + for (TextVatInvoice info : infos) { + if (info == null) { + continue; + } + if (aliasSet.contains(normalizeFieldName(info.getName()))) { + return StrUtil.blankToDefault(StrUtil.trim(info.getValue()), ""); + } + } + return ""; + } + + private String normalizeFieldName(String name) { + return StrUtil.blankToDefault(name, "") + .replace(":", "") + .replace(":", "") + .replace(" ", "") + .replace("\u00A0", "") + .trim(); + } + + private String normalizeDate(String dateText) { + String date = StrUtil.blankToDefault(StrUtil.trim(dateText), ""); + if (StrUtil.isBlank(date)) { + return ""; + } + String digits = date.replaceAll("[^0-9]", ""); + if (digits.length() == 8) { + return digits.substring(0, 4) + "-" + digits.substring(4, 6) + "-" + digits.substring(6); + } + return date.replace("年", "-").replace("月", "-").replace("日", ""); + } + + private Double parseMoney(Object moneyObj) { + String moneyText = StrUtil.blankToDefault(StrUtil.trim(ObjectUtil.defaultIfNull(moneyObj, "").toString()), ""); + if (StrUtil.isBlank(moneyText)) { + return null; + } + moneyText = moneyText.replace(",", "").replace("¥", "").replace("¥", ""); + try { + return BigDecimal.valueOf(Double.parseDouble(moneyText)).setScale(2, RoundingMode.HALF_UP).doubleValue(); + } catch (Exception e) { + return null; + } + } + + private String joinItemNames(VatInvoiceItem[] items) { + if (items == null || items.length == 0) { + return ""; + } + return Arrays.stream(items) + .map(VatInvoiceItem::getName) + .filter(StrUtil::isNotBlank) + .distinct() + .collect(Collectors.joining(";")); + } + + private String lastDigits(String text, int length) { + String digits = StrUtil.blankToDefault(text, "").replaceAll("[^0-9]", ""); + if (digits.length() <= length) { + return digits; + } + return digits.substring(digits.length() - length); + } + + private String moneyToVerifyValue(Object moneyObj) { + Double money = parseMoney(moneyObj); + if (money == null) { + return ""; + } + return BigDecimal.valueOf(money).setScale(2, RoundingMode.HALF_UP).toPlainString(); + } + + private void saveInvoiceDetails(String reimburseId, List invoiceDetails) { + this.dao().clear(UnionReimburseInvoiceDetail.class, Cnd.where("reimburseId", "=", reimburseId)); + if (Lang.isEmpty(invoiceDetails)) { + return; + } + invoiceDetails.forEach(detail -> { + detail.setId(null); + detail.setReimburseId(reimburseId); + this.dao().insert(detail); + }); + } + + /** + * 当前仅活动类报销维护发票明细,慰问类仍按原有逻辑处理金额。 + */ + private boolean needInvoiceDetails(UnionReimburse unionReimburse) { + return unionReimburse != null && !"UNION_REIMBURSE_PROJECT_1".equals(unionReimburse.getReimburseProject()); + } + + /** + * 生成年度流水号,保持与现有单据编号规则一致。 + */ + private String generateDocumentNo() { + int currentYear = DateUtil.thisYear(); + String yearPrefix = String.valueOf(currentYear); + Cnd cnd = Cnd.where("documentNo", "like", yearPrefix + "%"); + cnd.desc("documentNo"); + cnd.limit(1); + List list = this.query(cnd); + int nextSeq = 1; + if (Lang.isNotEmpty(list) && StrUtil.isNotBlank(list.get(0).getDocumentNo())) { + String maxNo = list.get(0).getDocumentNo(); + if (maxNo.startsWith(yearPrefix)) { + String seqPart = maxNo.substring(yearPrefix.length()); + if (StrUtil.isNumeric(seqPart)) { + nextSeq = Integer.parseInt(seqPart) + 1; + } + } + } + return yearPrefix + String.format("%02d", nextSeq); + } + + /** + * 统计页排序字段仅允许白名单列,避免传入非法字段。 + */ + private void applyStatisticsSort(Cnd cnd, UnionReimbursePageForm pageForm) { + String orderField = normalizeStatisticsSortField(pageForm.getPageOrderName()); + if (StrUtil.isBlank(orderField) || StrUtil.isBlank(pageForm.getPageOrderBy())) { + cnd.desc("info.createTime"); + return; + } + cnd.orderBy(orderField, "ascending".equals(pageForm.getPageOrderBy()) ? "asc" : "desc"); + } + + private String normalizeStatisticsSortField(String pageOrderName) { + if (StrUtil.isBlank(pageOrderName)) { + return null; + } + return switch (pageOrderName) { + case "condolenceLoginName" -> "info.condolenceLoginName"; + case "condolenceUnionName" -> "info.condolenceUnionName"; + case "unitName" -> "info.unitName"; + case "newCreateTime", "createTime" -> "info.createTime"; + case "newCondolenceTime" -> "info.condolenceTime"; + case "reimburseProject" -> "info.reimburseProject"; + default -> null; + }; + } + + private boolean isAdmin() { + return AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name()); + } + + /** + * 联系方式支持手机号和座机,和前端规则保持一致。 + */ + private boolean isValidContact(String mobile) { + return mobile.matches("^(1[3-9]\\d{9}|0\\d{2,3}-?\\d{7,8})$"); + } } diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/vo/UnionReimburseBankHistoryVO.java b/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/vo/UnionReimburseBankHistoryVO.java new file mode 100644 index 00000000..3baf62b9 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/vo/UnionReimburseBankHistoryVO.java @@ -0,0 +1,14 @@ +package com.budwk.app.zhgh.dayofficework.unionReimburse.vo; + +import lombok.Data; + +/** + * 付款人历史银行卡信息。 + * 用于申请页下次填写时快速回填户名、银行卡号和开户行。 + */ +@Data +public class UnionReimburseBankHistoryVO { + private String bankUserName; + private String bankCardNumber; + private String bankOfDeposit; +} diff --git a/src/main/java/com/budwk/app/zhgh/democratic/grassrootscongress/controller/material/GrassrootsCongressMaterialMineController.java b/src/main/java/com/budwk/app/zhgh/democratic/grassrootscongress/controller/material/GrassrootsCongressMaterialMineController.java index 1e02b91a..cdf04562 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/grassrootscongress/controller/material/GrassrootsCongressMaterialMineController.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/grassrootscongress/controller/material/GrassrootsCongressMaterialMineController.java @@ -79,7 +79,6 @@ public class GrassrootsCongressMaterialMineController { grassroots_congress_materials_info 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 $condition """); Cnd cnd = Cnd.NEW(); diff --git a/src/main/java/com/budwk/app/zhgh/democratic/grassrootscongress/controller/meeting/GrassrootsCongressMeetingMineController.java b/src/main/java/com/budwk/app/zhgh/democratic/grassrootscongress/controller/meeting/GrassrootsCongressMeetingMineController.java index bcef2b65..1fef85fe 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/grassrootscongress/controller/meeting/GrassrootsCongressMeetingMineController.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/grassrootscongress/controller/meeting/GrassrootsCongressMeetingMineController.java @@ -78,7 +78,7 @@ public class GrassrootsCongressMeetingMineController { grassroots_congress_meeting_info 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 + -- 任务参与人表一条任务会有多条参与人记录,这里不需要联查,否则同一业务会被展开成多行 $condition """); Cnd cnd = Cnd.NEW(); diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/query/ProposalQueryAnalysisController.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/query/ProposalQueryAnalysisController.java index 6bf7567c..f816b8aa 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/query/ProposalQueryAnalysisController.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/query/ProposalQueryAnalysisController.java @@ -54,29 +54,41 @@ public class ProposalQueryAnalysisController { @At @ApiOperation("统计数据") @SaCheckPermission("proposal.query.analysis") - public Result statisticsData(String sessionId) { + public Result statisticsData(String sessionId, String dimension) { + /* + * 查询参数说明: + * sessionId:教代会届次ID,用于限定统计提案范围; + * dimension:统计维度,可传“提案人单位、提案类型、代表团、立案结果、满意度”,为空时返回全部维度。 + * 返回值:Result.data 为统计行列表,包含 dimension、itemName、count、rate。 + */ if (StrUtil.isBlank(sessionId)) { return Result.error("请先选择教代会届次"); } - return Result.success(buildStatisticsRows(sessionId)); + return Result.success(buildStatisticsRows(sessionId, dimension)); } @At @ApiOperation("分析数据") @SaCheckPermission("proposal.query.analysis") - public Result analysisData(String sessionId) { + public Result analysisData(String sessionId, String dimension) { + /* + * 查询参数说明: + * sessionId:教代会届次ID,用于限定分析提案范围; + * dimension:统计维度,可传“提案人单位、提案类型、代表团、立案结果、满意度”,为空时返回全部维度。 + * 返回值:Result.data 为分析行列表,包含 dimension、total、categoryCount、topItem、topCount、topRate、analysis。 + */ if (StrUtil.isBlank(sessionId)) { return Result.error("请先选择教代会届次"); } - return Result.success(buildAnalysisRows(sessionId)); + return Result.success(buildAnalysisRows(sessionId, dimension)); } @At @Ok("void") @ApiOperation("导出统计数据") @SaCheckPermission("proposal.query.analysis") - public void exportStatisticsData(String sessionId, HttpServletResponse response) { - List list = buildStatisticsRows(sessionId); + public void exportStatisticsData(String sessionId, String dimension, HttpServletResponse response) { + List list = buildStatisticsRows(sessionId, dimension); List entities = new ArrayList<>(); entities.add(new ExcelExportEntity("统计维度", "dimension", 20)); entities.add(new ExcelExportEntity("分类项", "itemName", 30)); @@ -92,8 +104,8 @@ public class ProposalQueryAnalysisController { @Ok("void") @ApiOperation("导出分析数据") @SaCheckPermission("proposal.query.analysis") - public void exportAnalysisData(String sessionId, HttpServletResponse response) { - List list = buildAnalysisRows(sessionId); + public void exportAnalysisData(String sessionId, String dimension, HttpServletResponse response) { + List list = buildAnalysisRows(sessionId, dimension); List entities = new ArrayList<>(); entities.add(new ExcelExportEntity("统计维度", "dimension", 20)); entities.add(new ExcelExportEntity("总量", "total", 12)); @@ -108,15 +120,15 @@ public class ProposalQueryAnalysisController { CommonDownloadUtil.download("提案分析数据.xlsx", workbook, response); } - private List buildStatisticsRows(String sessionId) { - Map> dataMap = loadDimensionData(sessionId); + private List buildStatisticsRows(String sessionId, String dimension) { + Map> dataMap = loadDimensionData(sessionId, dimension); List result = new ArrayList<>(); - dataMap.forEach((dimension, items) -> { + dataMap.forEach((dimensionName, items) -> { long total = items.stream().mapToLong(v -> v.getLong("count", 0L)).sum(); for (NutMap item : items) { long count = item.getLong("count", 0L); result.add(NutMap.NEW() - .addv("dimension", dimension) + .addv("dimension", dimensionName) .addv("itemName", item.getString("itemName", "未维护")) .addv("count", count) .addv("rate", formatPercent(count, total))); @@ -125,13 +137,13 @@ public class ProposalQueryAnalysisController { return result; } - private List buildAnalysisRows(String sessionId) { - Map> dataMap = loadDimensionData(sessionId); + private List buildAnalysisRows(String sessionId, String dimension) { + Map> dataMap = loadDimensionData(sessionId, dimension); List result = new ArrayList<>(); - dataMap.forEach((dimension, items) -> { + dataMap.forEach((dimensionName, items) -> { if (items.isEmpty()) { result.add(NutMap.NEW() - .addv("dimension", dimension) + .addv("dimension", dimensionName) .addv("total", 0) .addv("categoryCount", 0) .addv("topItem", "-") @@ -150,10 +162,10 @@ public class ProposalQueryAnalysisController { long topCount = top.getLong("count", 0L); String topItem = top.getString("itemName", "未维护"); String topRate = formatPercent(topCount, total); - String analysis = String.format("%s主要集中在【%s】,数量为%d,占比%s。", dimension, topItem, topCount, topRate); + String analysis = String.format("%s主要集中在【%s】,数量为%d,占比%s。", dimensionName, topItem, topCount, topRate); result.add(NutMap.NEW() - .addv("dimension", dimension) + .addv("dimension", dimensionName) .addv("total", total) .addv("categoryCount", sorted.size()) .addv("topItem", topItem) @@ -164,13 +176,23 @@ public class ProposalQueryAnalysisController { return result; } - private Map> loadDimensionData(String sessionId) { + private Map> loadDimensionData(String sessionId, String dimension) { Map> map = new LinkedHashMap<>(); - map.put("提案人单位", queryUnitStat(sessionId)); - map.put("提案类型", queryTypeStat(sessionId)); - map.put("代表团", queryDelegationStat(sessionId)); - map.put("立案结果", queryCaseFilingResultStat(sessionId)); - map.put("满意度", querySatisfactionStat(sessionId)); + if (StrUtil.isBlank(dimension) || "提案人单位".equals(dimension)) { + map.put("提案人单位", queryUnitStat(sessionId)); + } + if (StrUtil.isBlank(dimension) || "提案类型".equals(dimension)) { + map.put("提案类型", queryTypeStat(sessionId)); + } + if (StrUtil.isBlank(dimension) || "代表团".equals(dimension)) { + map.put("代表团", queryDelegationStat(sessionId)); + } + if (StrUtil.isBlank(dimension) || "立案结果".equals(dimension)) { + map.put("立案结果", queryCaseFilingResultStat(sessionId)); + } + if (StrUtil.isBlank(dimension) || "满意度".equals(dimension)) { + map.put("满意度", querySatisfactionStat(sessionId)); + } return map; } diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/query/ProposalQueryComprehensiveController.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/query/ProposalQueryComprehensiveController.java index 4e8aaa1d..28be5a7f 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/query/ProposalQueryComprehensiveController.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/query/ProposalQueryComprehensiveController.java @@ -73,9 +73,10 @@ public class ProposalQueryComprehensiveController { 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 proposal_reply_unit pru ON pru.proposalId = info.id - LEFT JOIN wf_process_task replyTask on replyTask.processInstanceId = ins.id AND replyTask.taskState = 20 AND replyTask.taskName in ('master_reply','slave_reply','opinion_master_reply') + LEFT JOIN wf_process_task replyTask on replyTask.processInstanceId = ins.id $condition """); +// AND replyTask.taskState = 20 AND replyTask.taskName in ('master_reply','slave_reply','opinion_master_reply') Cnd cnd = Cnd.NEW(); if (ArrayUtil.isNotEmpty(pageForm.getOrigins()) && StrUtil.isNotBlank(pageForm.getCommonKeyword())) { SqlExpressionGroup seg = new SqlExpressionGroup(); diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/query/ProposalQueryUnitReplyController.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/query/ProposalQueryUnitReplyController.java index c4d7cc88..811f3976 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/query/ProposalQueryUnitReplyController.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/query/ProposalQueryUnitReplyController.java @@ -6,6 +6,7 @@ import com.budwk.app.flow.engine.util.FlowUtil; import com.budwk.app.flow.entity.ProcessTask; import com.budwk.app.flow.enums.ProcessTaskStateEnum; import com.budwk.app.zhgh.democratic.proposal.models.ProposalConfig; +import com.budwk.app.zhgh.democratic.proposal.models.ProposalInfo; import com.budwk.app.zhgh.democratic.proposal.models.ProposalReplyUnit; import com.budwk.app.zhgh.democratic.proposal.service.common.ProposalCommonService; import lombok.extern.slf4j.Slf4j; @@ -44,16 +45,25 @@ public class ProposalQueryUnitReplyController { @At @SaCheckPermission("proposal.query.unitReply") - public Result data(String sessionId, String undertakeUnitId) { + public Result data(String sessionId, String undertakeUnitId, String caseFilingResult) { + /* + * 查询参数说明: + * sessionId:教代会届次ID,用于限定统计的提案范围; + * undertakeUnitId:承办单位ID,用于只统计某个承办单位,为空时统计全部承办单位; + * caseFilingResult:立案结果字典值,对应 proposal_info.caseFilingResult,为空时不限制立案结果。 + * 返回值:Result.data 中包含 tableData 统计行列表,以及 slaveNeedReply 协办单位是否需要答复配置。 + */ // 提案配置 协办是否需要答复 ProposalConfig proposalConfig = dao.fetch(ProposalConfig.class, Cnd.NEW()); boolean slaveNeedReply = proposalConfig.getSlaveUnitNeedReply(); // 当前届次所有的提案ID - Sql sql = Sqls.create("select id from proposal_info where sessionId = @sessionId").setParam("sessionId", sessionId); - sql.setCallback(Sqls.callback.strList()); - dao.execute(sql); - List proposalIds = sql.getList(String.class); + Cnd proposalCnd = Cnd.where(ProposalInfo::getSessionId, "=", sessionId); + proposalCnd.andEX(ProposalInfo::getCaseFilingResult, "=", caseFilingResult); + List proposalIds = dao.query(ProposalInfo.class, proposalCnd) + .stream() + .map(ProposalInfo::getId) + .collect(Collectors.toList()); if(proposalIds.isEmpty()){ return Result.success().addData(Map.of("tableData", Collections.EMPTY_LIST, "slaveNeedReply", slaveNeedReply)); @@ -73,7 +83,7 @@ public class ProposalQueryUnitReplyController { List replyUnits = dao.query(ProposalReplyUnit.class, cnd); // 承办单位答复记录 - List replyTasks = dao.query(ProcessTask.class, Cnd.where(ProcessTask::getProcessInstanceId, "in", instIds).and(ProcessTask::getTaskName, "in", List.of("master_reply", "slave_reply", "opinion_master_reply")).and(ProcessTask::getTaskState, "in", List.of(ProcessTaskStateEnum.FINISHED.getCode(), ProcessTaskStateEnum.DOING.getCode()))); + List replyTasks = dao.query(ProcessTask.class, Cnd.where(ProcessTask::getProcessInstanceId, "in", instIds).and(ProcessTask::getTaskName, "in", List.of("two_unit_reply","unit_reply","master_reply", "slave_reply", "opinion_master_reply")).and(ProcessTask::getTaskState, "in", List.of(ProcessTaskStateEnum.FINISHED.getCode(), ProcessTaskStateEnum.DOING.getCode()))); // 承办单位 List tableData = new ArrayList<>(); diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalCaseCheckController.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalCaseCheckController.java index eb9f5661..069da813 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalCaseCheckController.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalCaseCheckController.java @@ -45,7 +45,7 @@ public class ProposalCaseCheckController { @At("") @Ok("beetl:/platform/zhgh/democratic/proposal/transact/caseCheck/index.html") - @SaCheckPermission("proposal.mine") + @SaCheckPermission("proposal.caseCheck") public void index() { } @@ -83,6 +83,7 @@ public class ProposalCaseCheckController { LEFT JOIN wf_process_task rt ON rt.processInstanceId = ins.id AND rt.taskParentId = t.id AND rt.taskState = 10 LEFT JOIN proposal_info info ON info.id = ins.businessNo LEFT JOIN proposal_type type on type.id = info.typeId + LEFT JOIN vw_user vu ON vu.id = info.createUserId LEFT JOIN teacher_congress_session tcs on tcs.id = info.sessionId LEFT JOIN teacher_congress_delegation tcd on tcd.id = info.delegationId $condition @@ -99,6 +100,8 @@ public class ProposalCaseCheckController { } else { cnd.and("t.taskState", "=", ProcessTaskStateEnum.DOING.getCode()); } + // 工会筛选按提案人所属工会口径处理,来源为 proposal_info.createUserId 关联 vw_user.unionId。 + cnd.andEX("vu.unionId", "=", pageForm.getUnionId()); ProposalSearchParam.buildSearch(cnd, pageForm); cnd.groupBy("t.id"); cnd.desc("t.createdAt"); diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalCommitteeFilingController.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalCommitteeFilingController.java index b8d2ae19..1da9b452 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalCommitteeFilingController.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalCommitteeFilingController.java @@ -83,6 +83,7 @@ public class ProposalCommitteeFilingController { LEFT JOIN proposal_info info ON info.id = ins.businessNo LEFT JOIN proposal_type type on type.id = info.typeId LEFT JOIN proposal_merge mer ON mer.proposalId = info.id + LEFT JOIN vw_user vu ON vu.id = info.createUserId LEFT JOIN teacher_congress_session tcs on tcs.id = info.sessionId LEFT JOIN teacher_congress_delegation tcd on tcd.id = info.delegationId $condition @@ -97,6 +98,8 @@ public class ProposalCommitteeFilingController { } else { cnd.and("t.taskState", "=", ProcessTaskStateEnum.DOING.getCode()); } + // 工会筛选按提案人所属工会口径处理,来源为 proposal_info.createUserId 关联 vw_user.unionId。 + cnd.andEX("vu.unionId", "=", pageForm.getUnionId()); ProposalSearchParam.buildSearch(cnd, pageForm); cnd.groupBy("t.id"); cnd.desc("t.createdAt"); diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalCommitteeFilingUnitController.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalCommitteeFilingUnitController.java index c95f3fd5..dae7c618 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalCommitteeFilingUnitController.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalCommitteeFilingUnitController.java @@ -99,6 +99,7 @@ public class ProposalCommitteeFilingUnitController { LEFT JOIN proposal_info info ON info.id = ins.businessNo LEFT JOIN proposal_type type on type.id = info.typeId LEFT JOIN proposal_merge mer ON mer.proposalId = info.id + LEFT JOIN vw_user vu ON vu.id = info.createUserId LEFT JOIN teacher_congress_session tcs on tcs.id = info.sessionId LEFT JOIN teacher_congress_delegation tcd on tcd.id = info.delegationId $condition @@ -113,6 +114,8 @@ public class ProposalCommitteeFilingUnitController { } else { cnd.and("t.taskState", "=", ProcessTaskStateEnum.DOING.getCode()); } + // 工会筛选按提案人所属工会口径处理,来源为 proposal_info.createUserId 关联 vw_user.unionId。 + cnd.andEX("vu.unionId", "=", pageForm.getUnionId()); ProposalSearchParam.buildSearch(cnd, pageForm); cnd.groupBy("t.id"); cnd.desc("t.createdAt"); diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalPreAuditController.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalPreAuditController.java index 1409bd6a..6a60c9e4 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalPreAuditController.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalPreAuditController.java @@ -42,7 +42,7 @@ public class ProposalPreAuditController { @At("") @Ok("beetl:/platform/zhgh/democratic/proposal/transact/preAudit/index.html") - @SaCheckPermission("proposal.mine") + @SaCheckPermission("proposal.preAudit") public void index() { } @@ -81,6 +81,7 @@ public class ProposalPreAuditController { LEFT JOIN wf_process_task rt ON rt.processInstanceId = ins.id AND rt.taskParentId = t.id AND rt.taskState = 10 LEFT JOIN proposal_info info ON info.id = ins.businessNo LEFT JOIN proposal_type type on type.id = info.typeId + LEFT JOIN vw_user vu ON vu.id = info.createUserId LEFT JOIN teacher_congress_session tcs on tcs.id = info.sessionId LEFT JOIN teacher_congress_delegation tcd on tcd.id = info.delegationId $condition @@ -97,6 +98,8 @@ public class ProposalPreAuditController { } else { cnd.and("t.taskState", "=", ProcessTaskStateEnum.DOING.getCode()); } + // 工会筛选按提案人所属工会口径处理,来源为 proposal_info.createUserId 关联 vw_user.unionId。 + cnd.andEX("vu.unionId", "=", pageForm.getUnionId()); ProposalSearchParam.buildSearch(cnd, pageForm); cnd.groupBy("t.id"); cnd.desc("t.createdAt"); diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalSchoolLeaderApprovalController.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalSchoolLeaderApprovalController.java index 637535aa..dff9ab32 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalSchoolLeaderApprovalController.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalSchoolLeaderApprovalController.java @@ -114,6 +114,7 @@ public class ProposalSchoolLeaderApprovalController { LEFT JOIN proposal_type type on type.id = info.typeId LEFT JOIN proposal_merge mer ON mer.proposalId = info.id LEFT JOIN proposal_reply_unit pru ON pru.proposalId = info.id + LEFT JOIN vw_user vu ON vu.id = info.createUserId LEFT JOIN teacher_congress_session tcs on tcs.id = info.sessionId LEFT JOIN teacher_congress_delegation tcd on tcd.id = info.delegationId $condition @@ -130,6 +131,8 @@ public class ProposalSchoolLeaderApprovalController { } else { cnd.and("t.taskState", "=", ProcessTaskStateEnum.DOING.getCode()); } + // 工会筛选按提案人所属工会口径处理,来源为 proposal_info.createUserId 关联 vw_user.unionId。 + cnd.andEX("vu.unionId", "=", pageForm.getUnionId()); ProposalSearchParam.buildSearch(cnd, pageForm); cnd.groupBy("t.id"); cnd.desc("t.createdAt"); diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalSecondedController.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalSecondedController.java index 5232dec5..cedcf17b 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalSecondedController.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalSecondedController.java @@ -2,24 +2,25 @@ package com.budwk.app.zhgh.democratic.proposal.controller.transact; import cn.dev33.satoken.annotation.SaCheckPermission; import cn.hutool.core.date.DateUtil; +import cn.hutool.core.lang.Dict; import cn.hutool.core.util.ObjectUtil; -import com.aspose.slides.internal.og.and; +import cn.hutool.json.JSONUtil; 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.service.BaseService; -import com.budwk.app.bpm.service.BpmService; -import com.budwk.app.flow.entity.ProcessInstance; +import com.budwk.app.flow.service.FlowCommonService; import com.budwk.app.flow.entity.ProcessTask; +import com.budwk.app.flow.entity.ProcessTaskActor; import com.budwk.app.flow.enums.ProcessSubmitTypeEnum; import com.budwk.app.flow.enums.ProcessTaskStateEnum; +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; import com.budwk.app.zhgh.democratic.proposal.models.ProposalSecond; import com.budwk.app.zhgh.democratic.proposal.param.ProposalSearchParam; import com.budwk.app.zhgh.democratic.proposal.service.ProposalSecondedService; -import com.budwk.app.zhgh.democratic.proposal.service.common.ProposalCommonService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; @@ -28,11 +29,12 @@ 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.Static; +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; -import org.nutz.lang.util.NutMap; +import org.nutz.json.Json; +import org.nutz.lang.Strings; import org.nutz.mvc.annotation.At; import org.nutz.mvc.annotation.Ok; import org.nutz.mvc.annotation.Param; @@ -55,11 +57,9 @@ public class ProposalSecondedController { @Inject private BaseService baseService; @Inject - private ProposalCommonService proposalCommonService; - @Inject - private BpmService bpmService; - @Inject private ProposalSecondedService proposalSecondedService; + @Inject + private FlowCommonService flowCommonService; @At("") @Ok("beetl:/platform/zhgh/democratic/proposal/transact/seconded/index.html") @@ -96,6 +96,7 @@ public class ProposalSecondedController { t.finishTime, t.taskParentId, t.variable taskVariable, + nt.taskName curTaskCode, if(t.taskName = 'second', ta.actorId, ps.seconderId) taskActorUserId, if(t.taskName = 'second', ta.actorName, ps.userName) taskActorName, IFNULL(GROUP_CONCAT(DISTINCT nt.displayName),'结束') curTaskName, @@ -108,36 +109,40 @@ public class ProposalSecondedController { LEFT JOIN wf_process_task nt ON nt.processInstanceId = ins.id AND nt.taskState = 10 LEFT JOIN wf_process_task rt ON rt.processInstanceId = ins.id AND rt.taskParentId = t.id AND rt.taskState = 10 LEFT JOIN proposal_info info ON info.id = ins.businessNo - LEFT JOIN proposal_second ps ON ps.proposalId = info.id + LEFT JOIN proposal_second ps ON ps.proposalId = info.id AND ps.seconderId = @seconderId LEFT JOIN proposal_type type on type.id = info.typeId LEFT JOIN teacher_congress_session tcs on tcs.id = info.sessionId LEFT JOIN teacher_congress_delegation tcd on tcd.id = info.delegationId $condition """); - sql.setParam("seconderId", SecurityUtil.getUserId()); + sql.setParam("seconderId", SecurityUtil.getUserId()); Cnd cnd = Cnd.NEW(); - cnd.and("t.taskName", "in", List.of("invite", "second", "delegation", "committee")); - cnd.and("ps.isAgree", "is", null); - if (!AuthUtil.hasRole(RoleConstant.SYSADMIN.name())) { - cnd.and("ta.actorId", "in", List.of(SecurityUtil.getUserId())); - } + cnd.and("ta.actorId", "in", List.of(SecurityUtil.getUserId())); if (approval) { + // 已附议页签只展示当前登录附议人已经处理过的记录。 + cnd.and("ps.isAgree", "is not", null); + cnd.and("t.taskName", "in", List.of("invite", "second", "delegation", "preAudit", "committee")); cnd.and("t.taskState", "in", List.of(ProcessTaskStateEnum.FINISHED.getCode(), ProcessTaskStateEnum.INTERRUPT.getCode())); } else { - cnd.and("t.taskState", "=", ProcessTaskStateEnum.DOING.getCode()); + // 未附议页签只展示当前登录附议人尚未处理的记录。 + cnd.and("ps.isAgree", "is", null); + // 待附议列表只展示两类任务: + // 1. invite / second 节点任务 + // 2. delegation / committee 节点,且任务状态必须是已废弃(99) + SqlExpressionGroup taskGroup = Cnd.exps("t.taskName", "in", List.of("invite", "second")); + SqlExpressionGroup delegationOrCommitteeGroup = Cnd.exps("t.taskName", "in", List.of("delegation", "preAudit", "committee")); + delegationOrCommitteeGroup.and("t.taskState", "=", ProcessTaskStateEnum.ABANDON.getCode()); + taskGroup.or(delegationOrCommitteeGroup); + cnd.and(taskGroup); } ProposalSearchParam.buildSearch(cnd, pageForm); cnd.groupBy("t.id"); cnd.desc("t.createdAt"); sql.setCondition(cnd); Pagination pagination = baseService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql); - List list = pagination.getList(NutMap.class); - for (NutMap row : list) { -// String instanceId = row.getString("instanceId"); - } return Result.success(pagination); } @@ -146,37 +151,146 @@ public class ProposalSecondedController { @Aop(TransAop.READ_COMMITTED) @ApiOperation("修改提案附议信息") @SLog(tag = "提案管理系统-附议提案", msg = "修改提案附议信息") + /** + * 仅更新附议结果和附议任务状态,不调用流程引擎推进流程。 + * 参数说明: + * 1. proposalId:提案主键,用于定位 proposal_second 附议记录。 + * 2. taskId:当前页面点击的任务主键,用于定位本次需要完成的 wf_process_task。 + * 3. taskActorUserId:附议人用户ID,用于校验附议记录和任务参与人是否匹配。 + * 4. opinion:附议意见,写入 proposal_second.opinion。 + * 5. submitType:附议结果,1 表示同意,20 表示不同意。 + * 返回值: + * Result.success() 表示 proposal_second 和 wf_process_task 更新成功; + * Result.error(...) 表示参数错误或未找到对应业务数据。 + */ public Result updateInfo(@Param("proposalId") String proposalId, + @Param("taskId") Long taskId, @Param("taskActorUserId") String taskActorUserId, @Param("opinion") String opinion, @Param("submitType") Integer submitType) { - // 查询附议信息表 + if (Strings.isBlank(proposalId) || Strings.isBlank(taskActorUserId) || taskId == null || submitType == null) { + return Result.error("参数错误"); + } + + // 先定位 wf_process_task,确保本次请求能准确命中需要完成的附议任务,再更新两张业务表。 + ProcessTask processTask = this.getSecondTask(taskId, taskActorUserId); + if (processTask == null) { + return Result.error("未找到附议任务"); + } + + // 更新 proposal_second,记录当前附议人的最终附议结果、附议意见和附议时间。 ProposalSecond proposalSecond = dao.fetch( ProposalSecond.class, Cnd.where(ProposalSecond::getProposalId, "=", proposalId) .and(ProposalSecond::getSeconderId, "=", taskActorUserId) ); + if (proposalSecond == null) { + return Result.error("未找到附议记录"); + } proposalSecond.setIsAgree(ObjectUtil.equals(submitType, ProcessSubmitTypeEnum.AGREE.getCode())); proposalSecond.setOpinion(opinion); + proposalSecond.setIsAgree(submitType == 1); proposalSecond.setSecondedTime(DateUtil.date()); dao.update(proposalSecond); - // 流程实例 - ProcessInstance processInstance = dao.fetch(ProcessInstance.class,Cnd.where(ProcessInstance::getBusinessNo, "=", proposalId)); - if(processInstance == null) return Result.success(); - - ProcessTask processTask = dao.fetch(ProcessTask.class,Cnd.where(ProcessTask::getProcessInstanceId, "=", processInstance.getId()) - .and(ProcessTask::getTaskName, "=", "second") - .and(ProcessTask::getTaskState, "=", ProcessTaskStateEnum.ABANDON.getCode()) - .and(new Static("id in (select processTaskId from wf_process_task_actor where actorId='" + taskActorUserId + "')")) - .limit(1,1)); - if(processTask == null) return Result.success(); - + // 仅更新本次附议对应的 wf_process_task,不推动实例、也不创建后续任务。 + View_user user = dao.fetch(View_user.class, Cnd.where(View_user::getId, "=", taskActorUserId)); + Dict taskArgs = Json.fromJson(Dict.class, processTask.getVariable()); + taskArgs.set("u_userId", user.getId()); + taskArgs.set("initiator", user.getId()); + taskArgs.set("initiatorName", user.getUsername()); + taskArgs.set("initiatorAccount", user.getLoginname()); + taskArgs.set("initiatorUnitId", user.getUnitId()); + taskArgs.set("initiatorUnitName", user.getUnitName()); + taskArgs.set("initiatorUnionId", user.getUnionId()); + taskArgs.set("initiatorUnionName", user.getUnionName()); + taskArgs.set("tf_loginName", user.getLoginname()); + taskArgs.set("tf_unionId", user.getUnionId()); + taskArgs.set("tf_unitId", user.getUnitId()); + taskArgs.set("tf_unitName", user.getUnitName()); + taskArgs.set("tf_userId", user.getId()); + taskArgs.set("tf_userName", user.getUsername()); + processTask.setVariable(JSONUtil.toJsonStr(taskArgs)); processTask.setTaskState(ProcessTaskStateEnum.FINISHED.getCode()); processTask.setFinishTime(DateUtil.date()); + processTask.setOperator(taskActorUserId); dao.update(processTask); return Result.success(); } + /** + * second 节点审核专用接口。 + * 参数说明: + * 1. data:前端提交的 JSON 字符串,必须包含 processTaskId、proposalId、taskActorUserId、tf_opinion、submitType。 + * 2. processTaskId:wf_process_task 主键,公共流程执行服务依赖该参数定位当前审核任务。 + * 3. proposalId:提案主键,附议业务表 proposal_second 通过该字段定位当前提案记录。 + * 4. taskActorUserId:附议人用户ID,用于定位当前登录人的附议记录。 + * 5. tf_opinion:审核意见,会同步写入流程表单变量和附议业务表 opinion 字段。 + * 6. submitType:审核结果,1 表示同意,20 表示不同意。 + * 返回值: + * Result.success() 表示公共流程任务执行成功且 proposal_second 已同步更新; + * Result.error(...) 表示请求参数不完整。 + */ + @At + @SaCheckPermission("proposal.seconded") + @Aop(TransAop.READ_COMMITTED) + @ApiOperation("执行附议审核任务") + @SLog(tag = "提案管理系统-附议提案", msg = "执行附议审核任务") + public Result executeTask(@Param("data") String data) { + if (Strings.isBlank(data)) { + return Result.error("参数错误"); + } + Dict args = Json.fromJson(Dict.class, data); + String proposalId = args.getStr("proposalId"); + String taskActorUserId = args.getStr("taskActorUserId"); + String opinion = args.getStr("tf_opinion"); + Integer submitType = args.getInt("submitType"); + if (args.getLong("processTaskId") == null || Strings.isBlank(proposalId) || Strings.isBlank(taskActorUserId) || submitType == null) { + return Result.error("参数错误"); + } + flowCommonService.executeTask(args); + proposalSecondedService.updateSecondRecord(proposalId, taskActorUserId, opinion, submitType); + return Result.success(); + } + + /** + * 根据当前点击的任务和附议人,定位实际需要完成的 second 节点任务。 + * 处理规则: + * 1. 如果页面点击的就是 second 节点任务,则直接更新该任务。 + * 2. 如果页面点击的是 invite、delegation、committee 等关联节点,则回查同实例下当前附议人的 second 任务。 + */ + private ProcessTask getSecondTask(Long taskId, String taskActorUserId) { + ProcessTask currentTask = dao.fetch(ProcessTask.class, taskId); + if (currentTask == null) { + return null; + } + if ("second".equals(currentTask.getTaskName()) + && List.of(ProcessTaskStateEnum.DOING.getCode(), ProcessTaskStateEnum.ABANDON.getCode()).contains(currentTask.getTaskState()) + && this.isTaskActorMatched(currentTask.getId(), taskActorUserId)) { + return currentTask; + } + + List secondTaskList = dao.query(ProcessTask.class, + Cnd.where(ProcessTask::getProcessInstanceId, "=", currentTask.getProcessInstanceId()) + .and(ProcessTask::getTaskName, "=", "second") + .and(ProcessTask::getTaskState, "in", List.of(ProcessTaskStateEnum.DOING.getCode(), ProcessTaskStateEnum.ABANDON.getCode())) + .desc(ProcessTask::getCreatedAt)); + for (ProcessTask secondTask : secondTaskList) { + if (this.isTaskActorMatched(secondTask.getId(), taskActorUserId)) { + return secondTask; + } + } + return null; + } + + /** + * 校验任务参与人是否为当前附议人,避免误更新其他代表的 second 任务。 + */ + private boolean isTaskActorMatched(Long processTaskId, String taskActorUserId) { + return dao.count(ProcessTaskActor.class, + Cnd.where(ProcessTaskActor::getProcessTaskId, "=", processTaskId) + .and(ProcessTaskActor::getActorId, "=", taskActorUserId)) > 0; + } + } diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalWriteController.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalWriteController.java index 4b8e8956..52f0da5e 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalWriteController.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalWriteController.java @@ -83,6 +83,7 @@ public class ProposalWriteController { @ApiOperation("保存提案") @SLog(tag = "提案管理系统-我的提案", msg = "保存提案") public Result save(@Param("info") ProposalInfo proposalInfo) { + proposalWriteService.checkCurrentUserDelegate(proposalInfo.getSessionId()); if (StrUtil.isBlank(proposalInfo.getCode())) { proposalInfo.setCode(proposalWriteService.generateProposalCode(proposalInfo.getSessionId(), proposalInfo.getDelegationId())); } @@ -98,6 +99,7 @@ public class ProposalWriteController { @ApiOperation("提交提案") @SLog(tag = "提案管理系统-我的提案", msg = "提交提案") public Result submit(@Param("info") ProposalInfo proposalInfo) { + proposalWriteService.checkCurrentUserDelegate(proposalInfo.getSessionId()); if (StrUtil.isBlank(proposalInfo.getCode())) { proposalInfo.setCode(proposalWriteService.generateProposalCode(proposalInfo.getSessionId(), proposalInfo.getDelegationId())); } diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/listenter/ProposalCommitteeFilingRevokeEventListener.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/listenter/ProposalCommitteeFilingRevokeEventListener.java index 39ddf560..b0a77181 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/listenter/ProposalCommitteeFilingRevokeEventListener.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/listenter/ProposalCommitteeFilingRevokeEventListener.java @@ -1,19 +1,9 @@ package com.budwk.app.zhgh.democratic.proposal.listenter; -import cn.hutool.json.JSONObject; -import cn.hutool.json.JSONUtil; import com.budwk.app.flow.engine.event.ProcessEvent; import com.budwk.app.flow.engine.event.ProcessEventListener; -import com.budwk.app.flow.entity.ProcessInstance; -import com.budwk.app.flow.entity.ProcessTask; import com.budwk.app.flow.enums.ProcessEventTypeEnum; -import com.budwk.app.flow.service.ProcessInstanceService; -import com.budwk.app.zhgh.democratic.proposal.models.ProposalMerge; -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 org.nutz.lang.Lang; /** * @version 1.0 @@ -25,27 +15,11 @@ import org.nutz.lang.Lang; @IocBean public class ProposalCommitteeFilingRevokeEventListener implements ProcessEventListener { - @Inject - private Dao dao; - @Inject - private ProcessInstanceService processInstanceService; - @Override public void onEvent(ProcessEvent event) { if (event.getEventType() == ProcessEventTypeEnum.PROCESS_TASK_REVOKE) { - Long taskId = event.getSourceId(); - ProcessTask task = dao.fetch(ProcessTask.class, taskId); - ProcessInstance processInstance = processInstanceService.getById(task.getProcessInstanceId()); - - // 处理并案信息撤回 - if ("committee".equals(task.getTaskName())) { - // 如果有并案,删掉并案数据(傻逼提的需求,nmsl) - ProposalMerge proposalMerge = dao.fetch(ProposalMerge.class, Cnd.where("proposalId", "=", processInstance.getBusinessNo())); - - if (Lang.isNotEmpty(proposalMerge)) { - dao.clear(ProposalMerge.class, Cnd.where(ProposalMerge::getProposalId, "=", proposalMerge.getProposalId())); - } - } + // proposal_merge 的删除逻辑已统一收口到 ProposalCommitteeFilingServiceImpl.revokeTask, + // 这里不再重复处理业务表,避免事件监听和 service 两处口径不一致。 } } } diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/param/ProposalQueryComprehensiveParam.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/param/ProposalQueryComprehensiveParam.java index da9ff171..5315291a 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/param/ProposalQueryComprehensiveParam.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/param/ProposalQueryComprehensiveParam.java @@ -77,7 +77,7 @@ public class ProposalQueryComprehensiveParam extends PageForm { if (Lang.isNotEmpty(searchParam.getSessionIds())) { cnd.and("info.sessionId", "in", searchParam.getSessionIds()); } else { - cnd.andEX("info.sessionId", "=", searchParam.getSessionId()); +// cnd.andEX("info.sessionId", "=", searchParam.getSessionId()); } //提案名称 diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/param/ProposalSearchParam.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/param/ProposalSearchParam.java index 2cca39d7..4694ac8b 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/param/ProposalSearchParam.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/param/ProposalSearchParam.java @@ -19,6 +19,7 @@ public class ProposalSearchParam extends PageForm { private String code; private String sessionId; private String delegationId; + private String unionId; private String createUserName; private String createUserLoginName; private String createUserKeyword; diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/ProposalSecondedService.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/ProposalSecondedService.java index 2bc7b9ed..fcef3ce8 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/ProposalSecondedService.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/ProposalSecondedService.java @@ -5,5 +5,13 @@ import com.budwk.app.zhgh.democratic.proposal.models.ProposalSecond; public interface ProposalSecondedService extends BaseService { - + /** + * 仅更新附议表 proposal_second 中当前附议人的附议结果。 + * 参数说明: + * 1. proposalId:提案ID,用于定位当前提案的附议记录。 + * 2. taskActorUserId:附议人用户ID,用于定位当前登录人的附议记录。 + * 3. opinion:附议意见,写入附议记录的 opinion 字段。 + * 4. submitType:附议结果,1 表示同意,20 表示不同意。 + */ + void updateSecondRecord(String proposalId, String taskActorUserId, String opinion, Integer submitType); } diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/ProposalWriteService.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/ProposalWriteService.java index 2dc013b2..10a932c4 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/ProposalWriteService.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/ProposalWriteService.java @@ -22,5 +22,15 @@ public interface ProposalWriteService extends BaseService { List listSourceByCode(@Valid String code); + /** + * 校验当前登录用户在指定教代会届次下是否具备代表身份。 + * 参数说明: + * 1. sessionId:教代会届次ID,保存和提交提案时通过该字段定位当前届次代表信息。 + * 返回值说明: + * 1. 校验通过时无返回值。 + * 2. 校验不通过时抛出业务异常,由上层统一返回提示信息。 + */ + void checkCurrentUserDelegate(String sessionId); + ProposalInfo importProposal(Map stringStringMap); } diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/common/ProposalCommonServiceImpl.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/common/ProposalCommonServiceImpl.java index 14b5fc0f..00dc0c2f 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/common/ProposalCommonServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/common/ProposalCommonServiceImpl.java @@ -234,10 +234,10 @@ public class ProposalCommonServiceImpl extends BaseServiceImpl imp // 提案附议 List secondInfos = new ArrayList<>(); List secondTaskVos = taskGroups.get("提案附议"); - if(secondTaskVos != null && secondTaskVos.size() > 0) { + if (secondTaskVos != null && secondTaskVos.size() > 0) { for (ProcessTaskVO secondTaskVO : secondTaskVos) { Dict taskFormData = secondTaskVO.getTaskFormData(); - if(taskFormData == null) { + if (taskFormData == null) { continue; } NutMap secondInfo = new NutMap(); @@ -248,7 +248,7 @@ public class ProposalCommonServiceImpl extends BaseServiceImpl imp taskUserSql.setCallback(Sqls.callback.map()); execute(taskUserSql); NutMap taskUserMap = (NutMap) taskUserSql.getResult(); - secondInfo.put("s_mobile", taskUserMap.getString("mobile")); + secondInfo.put("s_mobile", Objects.isNull(taskUserMap) ? "" : taskUserMap.getString("mobile")); secondInfos.add(secondInfo); } } @@ -256,10 +256,10 @@ public class ProposalCommonServiceImpl extends BaseServiceImpl imp // 代表团意见 List delegationAuditList = new ArrayList<>(); List delegationTaskVos = taskGroups.get("团长审核"); - if(delegationTaskVos != null && delegationTaskVos.size() > 0) { + if (delegationTaskVos != null && delegationTaskVos.size() > 0) { for (ProcessTaskVO delegationTaskVO : delegationTaskVos) { Dict taskFormData = delegationTaskVO.getTaskFormData(); - if(taskFormData == null) { + if (taskFormData == null) { continue; } @@ -601,10 +601,10 @@ public class ProposalCommonServiceImpl extends BaseServiceImpl imp List caseUnitList = listMap(caseUnitSql); for (NutMap v : caseUnitList) { String hostUnitName = v.getString("masterUnitName"); - if (StrUtil.isNotBlank(hostUnitName)){ + if (StrUtil.isNotBlank(hostUnitName)) { underTakeNames.add(hostUnitName); } - if (StrUtil.isNotBlank(v.getString("slaveUnitNames"))){ + if (StrUtil.isNotBlank(v.getString("slaveUnitNames"))) { List helpUnitNames = Arrays.asList(v.getString("slaveUnitNames").split("、")); underTakeNames.addAll(helpUnitNames); } @@ -725,73 +725,73 @@ public class ProposalCommonServiceImpl extends BaseServiceImpl imp String proposalMeasures = MapUtil.getStr(dataMap, "建议措施", ""); // 工号 - if(StrUtil.isBlank(loginName)) { + if (StrUtil.isBlank(loginName)) { throw new RuntimeException("工号不能为空"); } Sys_user user = sysUserService.getByLoginName(loginName); - if(user == null){ + if (user == null) { throw new RuntimeException("工号不存在"); } - if(StrUtil.isNotBlank(username) && !username.equals(user.getUsername())) { + if (StrUtil.isNotBlank(username) && !username.equals(user.getUsername())) { throw new RuntimeException("工号和提案人不一致"); } // 提案时间 - if(StrUtil.isBlank(proposalDate)) { + if (StrUtil.isBlank(proposalDate)) { throw new RuntimeException("提案时间不能为空"); } // 提案类型 - if(StrUtil.isBlank(proposalType)) { + if (StrUtil.isBlank(proposalType)) { throw new RuntimeException("提案类型不能为空"); } ProposalType proposalTypeObj = dao().fetch(ProposalType.class, Cnd.where(ProposalType::getName, "=", proposalType)); - if(proposalTypeObj == null) { + if (proposalTypeObj == null) { throw new RuntimeException("提案类型不存在"); } // 提案方式 - if(StrUtil.isBlank(proposalSource)) { + if (StrUtil.isBlank(proposalSource)) { throw new RuntimeException("提案方式不能为空"); } Sys_dict dict = sysDictService.fetch(Cnd.where("code", "=", "PROPOSAL_SOURCE")); - if(dict == null) { + if (dict == null) { throw new RuntimeException("提案方式字典配置不存在"); } List sysDicts = sysDictService.query(Cnd.where("parentId", "=", Strings.sNull(dict.getId())).asc("location")); - if(sysDicts == null || sysDicts.size() == 0) { + if (sysDicts == null || sysDicts.size() == 0) { throw new RuntimeException("提案方式字典配置不存在"); } Sys_dict proposalSourceDict = sysDicts.stream().filter(v -> v.getName().equals(proposalSource)).findFirst().orElse(null); - if(proposalSourceDict == null) { + if (proposalSourceDict == null) { throw new RuntimeException("提案方式不存在"); } // 提案名称 - if(StrUtil.isBlank(proposalName)) { + if (StrUtil.isBlank(proposalName)) { throw new RuntimeException("提案名称不能为空"); } // 案由 - if(StrUtil.isBlank(proposalContent)) { + if (StrUtil.isBlank(proposalContent)) { throw new RuntimeException("案由不能为空"); } // 建议措施 - if(StrUtil.isBlank(proposalMeasures)) { + if (StrUtil.isBlank(proposalMeasures)) { throw new RuntimeException("建议措施不能为空"); } // 判断当前导入用户是否是代表 Teacher_congress_delegate delegate = teacherCongressDelegateService.fetch(Cnd.where(Teacher_congress_delegate::getUserId, "=", SecurityUtil.getUserId()) .and(Teacher_congress_delegate::getDelFlag, "!=", 1).limit(1)); - if(delegate == null) { + if (delegate == null) { throw new RuntimeException("当前用户不是代表"); } // 教代会 List teacherCongressSessions = this.dao().query(Teacher_congress_session.class, Cnd.where("enable", "=", 1).desc("startDate")); - if(teacherCongressSessions == null || teacherCongressSessions.size() <= 0) { + if (teacherCongressSessions == null || teacherCongressSessions.size() <= 0) { throw new RuntimeException("没有开启的教代会"); } Teacher_congress_session teacherCongressSession = teacherCongressSessions.get(0); diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/impl/ProposalCommitteeFilingServiceImpl.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/impl/ProposalCommitteeFilingServiceImpl.java index 8adcdd63..34b0f4e5 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/impl/ProposalCommitteeFilingServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/impl/ProposalCommitteeFilingServiceImpl.java @@ -83,6 +83,9 @@ public class ProposalCommitteeFilingServiceImpl extends BaseServiceImpl implements ProposalSecondedService { - @Inject - private CommonService commonService; - public ProposalSecondedServiceImpl(Dao dao) { super(dao); } + @Override + public void updateSecondRecord(String proposalId, String taskActorUserId, String opinion, Integer submitType) { + if (Strings.isBlank(proposalId) || Strings.isBlank(taskActorUserId) || submitType == null) { + return; + } + // 仅更新当前提案、当前附议人的附议结果,不处理流程任务表。 + ProposalSecond proposalSecond = this.fetch(Cnd.where(ProposalSecond::getProposalId, "=", proposalId) + .and(ProposalSecond::getSeconderId, "=", taskActorUserId)); + if (proposalSecond == null) { + return; + } + proposalSecond.setIsAgree(ObjectUtil.equals(submitType, ProcessSubmitTypeEnum.AGREE.getCode())); + proposalSecond.setOpinion(opinion); + proposalSecond.setSecondedTime(DateUtil.date()); + this.update(proposalSecond); + } } diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/impl/ProposalWriteServiceImpl.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/impl/ProposalWriteServiceImpl.java index f28a1798..e559478f 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/impl/ProposalWriteServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/impl/ProposalWriteServiceImpl.java @@ -121,6 +121,23 @@ public class ProposalWriteServiceImpl extends BaseServiceImpl impl return dict == null ? new ArrayList<>() : sysDictService.query(Cnd.where("parentId", "=", Strings.sNull(dict.getId())).asc("location")); } + @Override + public void checkCurrentUserDelegate(String sessionId) { + if (StrUtil.isBlank(sessionId)) { + throw new RuntimeException("所属教代会不能为空"); + } + // 撰写提案必须绑定当前届次代表身份,避免非代表用户保存或提交提案。 + Teacher_congress_delegate delegate = teacherCongressDelegateService.fetch( + Cnd.where(Teacher_congress_delegate::getSessionId, "=", sessionId) + .and(Teacher_congress_delegate::getUserId, "=", SecurityUtil.getUserId()) + .and(Teacher_congress_delegate::getDelFlag, "!=", 1) + .limit(1) + ); + if (delegate == null) { + throw new RuntimeException("当前用户不是代表,无法撰写提案"); + } + } + @Override public ProposalInfo importProposal(Map dataMap) { String loginName = MapUtil.getStr(dataMap, "工号", ""); diff --git a/src/main/java/com/budwk/app/zhgh/democratic/teachercongress/delegate/controller/TeacherCongressDelegatePushController.java b/src/main/java/com/budwk/app/zhgh/democratic/teachercongress/delegate/controller/TeacherCongressDelegatePushController.java index 02dd6746..b597d30c 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/teachercongress/delegate/controller/TeacherCongressDelegatePushController.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/teachercongress/delegate/controller/TeacherCongressDelegatePushController.java @@ -150,7 +150,7 @@ public class TeacherCongressDelegatePushController { @Aop(TransAop.READ_COMMITTED) @SaCheckPermission("tc.delegate.push") // @SLog(tag = "民主管理-教代会代表推选", msg = "获取本分工会下的非代表用户") - public Result getUnionUser(String sessionId, String unionId) { + public Result getUnionUser(String sessionId, String unionId, String keyword) { // 查询预选中的代表 Cnd cndx = Cnd.where("sessionId", "=", sessionId); cndx.andEX("unionId", "=", SecurityUtil.getUnionId()); @@ -173,7 +173,7 @@ public class TeacherCongressDelegatePushController { `vw_user` u LEFT JOIN teacher_congress_delegate tcd ON tcd.userId = u.id AND tcd.sessionId = @sessionId - LEFT JOIN teacher_congress_delegate_push tcdp ON tcdp.userId = tcdp.id + LEFT JOIN teacher_congress_delegate_push tcdp ON tcdp.userId = u.id AND tcdp.sessionId = @sessionId $condition """); @@ -185,6 +185,14 @@ public class TeacherCongressDelegatePushController { cnd.and("tcd.userId", "is", null); cnd.and("tcdp.id", "is", null); cnd.and("u.unionId", "=", SecurityUtil.getUnionId()); + if (StrUtil.isNotBlank(keyword)) { + SqlExpressionGroup group = new SqlExpressionGroup(); + group.orLike("u.username", keyword); + group.orLike("u.loginname", keyword); + cnd.and(group); + } + // 搜索候选人时只返回少量命中数据,避免前端大列表筛选导致严重卡顿 + cnd.limit(1, 100); sql.setCondition(cnd); List userList = baseService.listMap(sql); diff --git a/src/main/java/com/budwk/app/zhgh/staffbenefit/difficulthelp/controller/DifficultHelpBranchUnionLeaderApprovalController.java b/src/main/java/com/budwk/app/zhgh/staffbenefit/difficulthelp/controller/DifficultHelpBranchUnionLeaderApprovalController.java index 6d8f320e..a3dd6746 100644 --- a/src/main/java/com/budwk/app/zhgh/staffbenefit/difficulthelp/controller/DifficultHelpBranchUnionLeaderApprovalController.java +++ b/src/main/java/com/budwk/app/zhgh/staffbenefit/difficulthelp/controller/DifficultHelpBranchUnionLeaderApprovalController.java @@ -14,6 +14,7 @@ import org.nutz.dao.Cnd; import org.nutz.dao.Dao; import org.nutz.dao.Sqls; import org.nutz.dao.sql.Sql; +import org.nutz.dao.util.cri.SqlExpressionGroup; import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.IocBean; import org.nutz.lang.util.NutMap; @@ -102,7 +103,16 @@ public class DifficultHelpBranchUnionLeaderApprovalController { $condition """); Cnd cnd = Cnd.NEW(); - pageParam.buildSearch(cnd, "info."); +// pageParam.buildSearchUnit(cnd, "info."); + + if (StrUtil.isNotBlank(pageParam.getSearchKeyword())) { + SqlExpressionGroup seg = new SqlExpressionGroup(); + seg.orLike("info.userName", pageParam.getSearchKeyword()); + seg.orLike("info.loginName", pageParam.getSearchKeyword()); + cnd.and(seg); + } + cnd.andEX("info.unionId", "=", pageParam.getUnionId()); + cnd.andEX("info.unitId", "=", pageParam.getUnitId()); cnd.and("t.taskName", "=", "36ebafda-44c2-4bd3-bd7f-10839a575a0d"); cnd.and("ta.actorId", "in", List.of(SecurityUtil.getUserId())); diff --git a/src/main/java/com/budwk/app/zhgh/staffbenefit/difficulthelp/controller/DifficultHelpMineController.java b/src/main/java/com/budwk/app/zhgh/staffbenefit/difficulthelp/controller/DifficultHelpMineController.java index 46471a69..6d9816e4 100644 --- a/src/main/java/com/budwk/app/zhgh/staffbenefit/difficulthelp/controller/DifficultHelpMineController.java +++ b/src/main/java/com/budwk/app/zhgh/staffbenefit/difficulthelp/controller/DifficultHelpMineController.java @@ -78,6 +78,7 @@ public class DifficultHelpMineController { LEFT(info.applyTime, 10) AS applyTime, info.applyCount, info.mobile, + info.money, ins.id AS instanceId, ins.businessNo, ins.state instanceState, diff --git a/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/aidFund/controller/AidFundApplyController.java b/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/aidFund/controller/AidFundApplyController.java index 308241b0..e258993a 100644 --- a/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/aidFund/controller/AidFundApplyController.java +++ b/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/aidFund/controller/AidFundApplyController.java @@ -129,7 +129,8 @@ public class AidFundApplyController { } //如果有两个工号并且有一个工号已经是会员就不让他申请了 if (userList.size() >= 2) { - int size = userList.stream().filter(Sys_user::getAidFundMember).toList().size(); + // 历史用户数据中 aidFundMember 可能为空,这里只统计明确为基金会员的工号,避免空值自动拆箱报错 + int size = userList.stream().filter(sysUser -> Boolean.TRUE.equals(sysUser.getAidFundMember())).toList().size(); if (size > 0) { return Result.success(Map.of("disabled", true, "label", "请用新工号登陆办理申请业务!")); } @@ -149,7 +150,7 @@ public class AidFundApplyController { aidFundMemberChangeRecord.setUserId(SecurityUtil.getUserId()); Sys_user user = dao.fetch(Sys_user.class, SecurityUtil.getUserId()); if (StrUtil.isBlank(aidFundMemberChangeRecord.getId())) aidFundMemberChangeRecord.setApplyTime(new Date()); - if (user.getAidFundMember() == AidFundMemberMode.NORMAL.getCode()) { + if (Boolean.TRUE.equals(user.getAidFundMember())) { aidFundMemberChangeRecord.setChangeType("AIDFUND_MEMBER_CHANGE_TYPE_FOUR"); } else { aidFundMemberChangeRecord.setChangeType("AIDFUND_MEMBER_CHANGE_TYPE_ONE"); @@ -159,7 +160,8 @@ public class AidFundApplyController { Dict args = Dict.create(); args.set(FlowConst.SUBMIT_TYPE, ProcessSubmitTypeEnum.APPLY.getCode()); args.set(FlowConst.FORM_DATA, aidFundMemberChangeRecord); - args.set("aidFundMemberUserType", user.getAidFundMemberUserType()); + // 流程变量中的基金会员类型以本次申请提交值为准,避免继续使用用户表中的旧值。 + args.set("aidFundMemberUserType", aidFundMemberChangeRecord.getAidFundMemberUserType()); ProcessInstance instance = flowEngine.startProcessInstanceByKey("JJHY", aidFundMemberChangeRecord.getId(), SecurityUtil.getUserId(), args); // 自动完成第一个申请任务 @@ -194,7 +196,7 @@ public class AidFundApplyController { us.username, us.loginname, us.mobile, - us.aidFundMemberUserType, + IFNULL(info.aidFundMemberUserType, us.aidFundMemberUserType) aidFundMemberUserType, us.arrivalAtSchoolDate, us.sex, us.unitName, diff --git a/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/aidFund/controller/AidFundApplyMineController.java b/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/aidFund/controller/AidFundApplyMineController.java index 5b6c73e2..c1ffc7ee 100644 --- a/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/aidFund/controller/AidFundApplyMineController.java +++ b/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/aidFund/controller/AidFundApplyMineController.java @@ -76,14 +76,17 @@ public class AidFundApplyMineController { @Param(value = "year") Integer year) { Sql sql = Sqls.create(""" SELECT - info.*, + info.id, + info.userId, + info.changeType, + info.applyTime, us.arrivalAtSchoolDate, us.mobile, us.loginname loginName, us.username userName, us.unitName, us.unionName, - us.aidFundMemberUserType, + IFNULL(info.aidFundMemberUserType, us.aidFundMemberUserType) aidFundMemberUserType, us.sex, ins.id AS instanceId, ins.businessNo, @@ -113,6 +116,8 @@ public class AidFundApplyMineController { cnd.andEX("year(info.applyTime)", "=", year); cnd.and("info.userId", "=", SecurityUtil.getUserId()); + // 管理端代操作产生的记录不属于“我的申请”,历史空值按普通申请处理。 + cnd.andEX("IFNULL(info.adminOperate, 0)", "=", 0); if (StrUtil.isNotBlank(pageForm.getPageOrderName()) && StrUtil.isNotBlank(pageForm.getPageOrderBy())) { cnd.orderBy(pageForm.getPageOrderName(), PageUtil.getOrder(pageForm.getPageOrderBy())); diff --git a/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/aidFund/controller/AidFundChangeRecordController.java b/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/aidFund/controller/AidFundChangeRecordController.java index 28e5ec28..b0f9a489 100644 --- a/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/aidFund/controller/AidFundChangeRecordController.java +++ b/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/aidFund/controller/AidFundChangeRecordController.java @@ -58,7 +58,6 @@ public class AidFundChangeRecordController { us.mobile, us.unitName, us.unionName, - us.aidFundMemberUserType, ins.id AS instanceId, ins.businessNo, ins.state instanceState, diff --git a/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/aidFund/controller/AidFundFoundationAuditController.java b/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/aidFund/controller/AidFundFoundationAuditController.java index e0f9599f..ec1d646f 100644 --- a/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/aidFund/controller/AidFundFoundationAuditController.java +++ b/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/aidFund/controller/AidFundFoundationAuditController.java @@ -1,24 +1,24 @@ package com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.controller; +import cn.afterturn.easypoi.excel.ExcelExportUtil; +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.StrUtil; 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.flow.enums.ProcessTaskStateEnum; -import com.budwk.app.web.commons.auth.utils.SecurityUtil; +import com.budwk.app.base.utils.CommonDownloadUtil; +import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.param.AidFundPageForm; import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.service.AidFundMemberChangeRecordService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; +import org.apache.poi.ss.usermodel.Workbook; 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; @@ -27,6 +27,8 @@ import org.nutz.mvc.annotation.At; import org.nutz.mvc.annotation.Ok; import org.nutz.mvc.annotation.Param; +import javax.servlet.http.HttpServletResponse; +import java.util.ArrayList; import java.util.List; /** @@ -44,8 +46,6 @@ public class AidFundFoundationAuditController { @Inject private AidFundMemberChangeRecordService changeRecordService; - - @At("") @Ok("beetl:/platform/zhgh/staffbenefit/medicalMutualAid/aidFund/foundationAudit/index.html") @SaCheckPermission("medicalMutualAid.aidFund.foundationAudit") @@ -61,79 +61,8 @@ public class AidFundFoundationAuditController { @At @ApiOperation("分页查询") @SaCheckPermission(value = {"medicalMutualAid.aidFund.foundationAudit", "h5.medicalMutualAid.aidFund.foundationAudit"}, mode = SaMode.OR) - public Result pageData(PageForm pageForm, - @Param(value = "year") Integer year, - @Param(value = "aidFundMemberUserType") String aidFundMemberUserType, - @Param(value = "unionId") String unionId, - @Param(value = "unitId") String unitId, - @Param(value = "changeType") String changeType, - @Param(value = "approval") Boolean approval) { - Sql sql = Sqls.create(""" - SELECT - info.*, - us.arrivalAtSchoolDate, - us.mobile, - us.loginname loginName, - us.username userName, - us.unitName, - us.unionName, - us.aidFundMemberUserType, - us.sex, - ins.id AS instanceId, - ins.businessNo, - ins.state instanceState, - ins.variable instanceVariable, - ins.processDefineId instanceProcessDefineId, - t.id taskId, - t.taskName AS taskKey, - t.displayName taskName, - t.taskType, - t.performType taskPerformType, - t.taskState, - t.finishTime, - t.taskParentId, - t.variable taskVariable, - IFNULL(GROUP_CONCAT(DISTINCT nt.displayName),'结束') curTaskName, - IF(rt.id IS NOT NULL, 1, 0) AS canRevoke - FROM - wf_process_task t - LEFT JOIN wf_process_instance ins ON ins.id = t.processInstanceId - LEFT JOIN aid_fund_member_change_record info ON info.id = ins.businessNo - LEFT JOIN vw_user us ON us.id = info.userId - LEFT JOIN wf_process_task_actor ta ON ta.processTaskId = t.id - LEFT JOIN wf_process_task nt ON nt.processInstanceId = ins.id AND nt.taskState = 10 - LEFT JOIN wf_process_task rt ON rt.processInstanceId = ins.id AND rt.taskParentId = t.id AND rt.taskState = 10 - $condition - """); - Cnd cnd = Cnd.NEW(); - if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) { - SqlExpressionGroup seg = new SqlExpressionGroup(); - seg.or("us.loginname", "like", "%" + pageForm.getSearchKeyword() + "%"); - seg.or("us.username", "like", "%" + pageForm.getSearchKeyword() + "%"); - cnd.and(seg); - } - cnd.andEX("YEAR(info.applyTime)", "=", year); - cnd.andEX("us.aidFundMemberUserType", "=", aidFundMemberUserType); - cnd.andEX("us.unionId", "=", unionId); - cnd.andEX("us.unitId", "=", unitId); - cnd.andEX("info.changeType", "=", changeType); - - cnd.and("t.taskName", "=", "85c7d148-44f5-4333-b854-ab225357fd3e"); - 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("t.taskState", "=", ProcessTaskStateEnum.DOING.getCode()); - } - - if (StrUtil.isAllBlank(pageForm.getPageOrderName(), pageForm.getPageOrderBy())) { - cnd.desc("info.applyTime"); - } else { - cnd.orderBy(pageForm.getPageOrderName(), PageUtil.getOrder(pageForm.getPageOrderBy())); - } - cnd.groupBy("t.id"); - sql.setCondition(cnd); + public Result pageData(AidFundPageForm pageForm) { + Sql sql = changeRecordService.getFoundationAuditSql(pageForm); Pagination pageVO = changeRecordService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql); List list = pageVO.getList(); @@ -144,6 +73,39 @@ public class AidFundFoundationAuditController { return Result.success(pageVO); } + /** + * 导出互管会审核页面申请记录。 + * + * @param pageForm 页面筛选参数,和列表页查询条件保持一致 + * @param response HTTP 响应对象,用于输出 Excel 文件流 + */ + @At + @Ok("void") + @ApiOperation("导出申请记录") + @SaCheckPermission(value = {"medicalMutualAid.aidFund.foundationAudit", "h5.medicalMutualAid.aidFund.foundationAudit"}, mode = SaMode.OR) + public void exportApplyRecord(AidFundPageForm pageForm, HttpServletResponse response) { + List listMap = changeRecordService.getFoundationAuditExportList(pageForm); + List entityList = new ArrayList<>(); + entityList.add(new ExcelExportEntity("工号", "loginName", 20)); + entityList.add(new ExcelExportEntity("姓名", "userName", 20)); + entityList.add(new ExcelExportEntity("性别", "sex", 15)); + entityList.add(new ExcelExportEntity("电话", "mobile", 20)); + entityList.add(new ExcelExportEntity("工会", "unionName", 20)); + entityList.add(new ExcelExportEntity("单位", "unitName", 20)); + entityList.add(new ExcelExportEntity("入校时间", "arrivalAtSchoolDate", 20)); + entityList.add(new ExcelExportEntity("基金会员类型", "aidFundMemberUserType", 20)); + entityList.add(new ExcelExportEntity("缴纳金额", "money", 20)); + entityList.add(new ExcelExportEntity("变更类型", "changeType", 20)); + entityList.add(new ExcelExportEntity("申请时间", "applyTime", 20)); + entityList.add(new ExcelExportEntity("审核状态", "approvalStatus", 20)); + + ExportParams exportParams = new ExportParams(); + exportParams.setType(ExcelType.XSSF); + Workbook workbook = ExcelExportUtil.exportExcel(exportParams, entityList, listMap); + String fileName = (pageForm.getYear() == null ? "" : pageForm.getYear()) + "基金会员审核记录.xlsx"; + CommonDownloadUtil.download(fileName, workbook, response); + } + @At @ApiOperation("查询需要缴费多少") @SaCheckPermission(value = {"medicalMutualAid.aidFund.foundationAudit", "h5.medicalMutualAid.aidFund.foundationAudit"}, mode = SaMode.OR) @@ -163,4 +125,23 @@ public class AidFundFoundationAuditController { return Result.success(); } + /** + * 批量审核互管会待办记录。 + * + * @param param 统一的审批参数,包含审批意见和审核动作 + * @param ids 勾选的申请记录 ID 数组 + * @return 批量审核结果统计 + */ + @At + @SLog(tag = "基金会员", msg = "互管会批量审核基金会员记录") + @Aop(TransAop.READ_COMMITTED) + @ApiOperation("批量审核") + @SaCheckPermission(value = {"medicalMutualAid.aidFund.foundationAudit", "h5.medicalMutualAid.aidFund.foundationAudit"}, mode = SaMode.OR) + public Result batchExecuteTask(@Param("data") String param, @Param("ids") String[] ids) { + if (ids == null || ids.length == 0 || StrUtil.isBlank(param)) { + return Result.error("参数错误"); + } + return Result.success(changeRecordService.batchExecuteTask(param, ids)); + } + } diff --git a/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/aidFund/controller/AidFundManageController.java b/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/aidFund/controller/AidFundManageController.java index c16b2101..f18bbc1d 100644 --- a/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/aidFund/controller/AidFundManageController.java +++ b/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/aidFund/controller/AidFundManageController.java @@ -10,18 +10,12 @@ import cn.hutool.core.date.DateUtil; 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.param.PageForm; import com.budwk.app.base.result.Result; import com.budwk.app.base.utils.CommonDownloadUtil; -import com.budwk.app.base.utils.PageUtil; import com.budwk.app.sys.models.Sys_user; -import com.budwk.app.sys.models.Sys_user_role; import com.budwk.app.sys.services.SysUserService; 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; import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.mode.AidFundMemberMode; import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.model.AidFundMemberChangeRecord; import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.param.AidFundPageForm; @@ -35,7 +29,6 @@ import org.nutz.dao.Chain; 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; @@ -79,55 +72,32 @@ public class AidFundManageController { @ApiOperation("分页查询") @SaCheckPermission("medicalMutualAid.aidFund.manage") public Result pageData(AidFundPageForm pageForm) { - Sql sql = Sqls.create(""" - SELECT - id, - loginname, - username, - sex, - arrivalAtSchoolDate, - unitName, - unionName, - aidFundMemberUserType, - aidFundMemberJoinTime - FROM - vw_user - $condition - """); - Cnd cnd = Cnd.NEW(); - if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name())) { - if (AuthUtil.hasRole(RoleConstant.RETIREMENT_WORKPLACE.name())) { - cnd.and(Sys_user::getAidFundMemberUserType, "=", "离退休人员"); - } else { - cnd.and(View_user::getUnionId, "=", SecurityUtil.getUnionId()); - } - } - - if (pageForm.getNotPayedCurrentYear()) { - cnd.and("id", "in", Sqls.create("SELECT userid FROM `aid_fund_member_pay` where `year` = YEAR(CURDATE()) and isPayed = 0")); - } - cnd.andEX(Sys_user::getAidFundMemberUserType, "=", pageForm.getAidFundMemberUserType()); - cnd.andEX(View_user::getUnionId, "=", pageForm.getUnionId()); - cnd.andEX(View_user::getUnitId, "=", pageForm.getUnitId()); - sql.setCondition(cnd); - cnd.and(Sys_user::getAidFundMember, "=", AidFundMemberMode.NORMAL.getCode()); - - if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) { - SqlExpressionGroup seg = new SqlExpressionGroup(); - seg.orLike("loginname", pageForm.getSearchKeyword()); - seg.orLike("username", pageForm.getSearchKeyword()); - cnd.and(seg); - } - if (StrUtil.isAllBlank(pageForm.getPageOrderName(), pageForm.getPageOrderBy())) { - cnd.desc("unitCode"); - } else { - cnd.orderBy(pageForm.getPageOrderName(), PageUtil.getOrder(pageForm.getPageOrderBy())); - } - sql.setCondition(cnd); - Pagination pagination = userService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql); + Sql sql = aidFundMemberService.getManageSql(pageForm); + Pagination pagination = aidFundMemberService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql); return Result.success(pagination); } + @At + @SaCheckPermission("medicalMutualAid.aidFund.manage") + @Ok("void") + @ApiOperation("导出会员名单") + public void exportMemberList(AidFundPageForm pageForm, HttpServletResponse response) { + List listMap = aidFundMemberService.listMap(aidFundMemberService.getManageSql(pageForm)); + + List entityList = new ArrayList<>(); + entityList.add(new ExcelExportEntity("姓名", "username", 20)); + entityList.add(new ExcelExportEntity("工号", "loginname", 20)); + entityList.add(new ExcelExportEntity("性别", "sex", 20)); + entityList.add(new ExcelExportEntity("会员类型", "aidFundMemberUserType", 20)); + entityList.add(new ExcelExportEntity("单位", "unitName", 20)); + entityList.add(new ExcelExportEntity("工会", "unionName", 20)); + + ExportParams exportParams = new ExportParams(); + exportParams.setType(ExcelType.XSSF); + Workbook workbook = ExcelExportUtil.exportExcel(exportParams, entityList, listMap); + CommonDownloadUtil.download("会员名单.xlsx", workbook, response); + } + @At @SaCheckPermission("medicalMutualAid.aidFund.manage") @@ -198,9 +168,12 @@ public class AidFundManageController { return Result.error("请选择要退会的人员"); } List recordArrayList = new ArrayList<>(); + List sysUserList = userService.query(Cnd.where("id", "in", ids)); for (String id : ids) { + Sys_user user = sysUserList.stream().filter(sysUser -> sysUser.getId().equals(id)).findFirst().orElse(null); AidFundMemberChangeRecord record = new AidFundMemberChangeRecord() .setUserId(id) + .setAidFundMemberUserType(user.getAidFundMemberUserType()) .setApplyTime(new Date()) .setChangeType("AIDFUND_MEMBER_CHANGE_TYPE_SEVEN"); recordArrayList.add(record); @@ -220,25 +193,7 @@ public class AidFundManageController { if (ObjectUtil.isEmpty(ids) || StrUtil.isBlank(changeType)) { return Result.error("参数错误"); } - List recordArrayList = new ArrayList<>(); - for (String id : ids) { - AidFundMemberChangeRecord record = new AidFundMemberChangeRecord() - .setUserId(id) - .setApplyTime(new Date()) - .setChangeType(changeType); - recordArrayList.add(record); - } - Chain add; - if (changeType.equals("AIDFUND_MEMBER_CHANGE_TYPE_EIGTH")) { - //会员退休 - add = Chain.make("aidFundMemberUserType", "离退休人员"); - } else { - add = Chain.make("aidFundMember", AidFundMemberMode.NONE.getCode()) - .add("aidFundMemberQuitTime", new Date()); - } - - userService.update(add, Cnd.where("id", "in", ids)); - userService.insert(recordArrayList); + aidFundMemberService.doBatchChangeTypeByAdmin(ids, changeType); return Result.success(); } @@ -258,21 +213,10 @@ public class AidFundManageController { @ApiOperation("变更基金会员") @Aop(TransAop.READ_COMMITTED) public Result doEditChangeType(String userId, String changeType) { - AidFundMemberChangeRecord record = new AidFundMemberChangeRecord() - .setUserId(userId) - .setApplyTime(new Date()) - .setChangeType(changeType); - aidFundMemberService.insert(record); - Chain add; - if (changeType.equals("AIDFUND_MEMBER_CHANGE_TYPE_EIGTH")) { - //会员退休 - add = Chain.make("aidFundMemberUserType", "离退休人员"); - } else { - add = Chain.make("aidFundMember", AidFundMemberMode.NONE.getCode()) - .add("aidFundMemberQuitTime", new Date()); + if (StrUtil.isBlank(userId) || StrUtil.isBlank(changeType)) { + return Result.error("参数错误"); } - - userService.update(add, Cnd.where("id", "=", userId)); + aidFundMemberService.doEditChangeTypeByAdmin(userId, changeType); return Result.success(); } diff --git a/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/aidFund/controller/AidFundRetirementAuditController.java b/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/aidFund/controller/AidFundRetirementAuditController.java index 8f445701..57a2dc6c 100644 --- a/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/aidFund/controller/AidFundRetirementAuditController.java +++ b/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/aidFund/controller/AidFundRetirementAuditController.java @@ -63,14 +63,17 @@ public class AidFundRetirementAuditController { @Param(value = "approval") Boolean approval) { Sql sql = Sqls.create(""" SELECT - info.*, + info.id, + info.userId, + info.changeType, + info.applyTime, us.arrivalAtSchoolDate, us.mobile, us.loginname loginName, us.username userName, us.unitName, us.unionName, - us.aidFundMemberUserType, + IFNULL(info.aidFundMemberUserType, us.aidFundMemberUserType) aidFundMemberUserType, us.sex, ins.id AS instanceId, ins.businessNo, @@ -106,7 +109,7 @@ public class AidFundRetirementAuditController { cnd.and(seg); } cnd.andEX("YEAR(info.applyTime)", "=", year); - cnd.andEX("us.aidFundMemberUserType", "=", aidFundMemberUserType); + cnd.andEX("IFNULL(info.aidFundMemberUserType, us.aidFundMemberUserType)", "=", aidFundMemberUserType); cnd.andEX("info.changeType", "=", changeType); cnd.and("t.taskName", "=", "f756c818-6bb7-4f50-b8fa-24390fc7b0b8"); diff --git a/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/aidFund/controller/AidFundUnionAuditController.java b/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/aidFund/controller/AidFundUnionAuditController.java index d051b2ff..b93013db 100644 --- a/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/aidFund/controller/AidFundUnionAuditController.java +++ b/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/aidFund/controller/AidFundUnionAuditController.java @@ -65,14 +65,17 @@ public class AidFundUnionAuditController { @Param(value = "approval") Boolean approval) { Sql sql = Sqls.create(""" SELECT - info.*, + info.id, + info.userId, + info.changeType, + info.applyTime, us.arrivalAtSchoolDate, us.mobile, us.loginname loginName, us.username userName, us.unitName, us.unionName, - us.aidFundMemberUserType, + IFNULL(info.aidFundMemberUserType, us.aidFundMemberUserType) aidFundMemberUserType, us.sex, ins.id AS instanceId, ins.businessNo, @@ -108,7 +111,7 @@ public class AidFundUnionAuditController { cnd.and(seg); } cnd.andEX("YEAR(info.applyTime)", "=", year); - cnd.andEX("us.aidFundMemberUserType", "=", aidFundMemberUserType); + cnd.andEX("IFNULL(info.aidFundMemberUserType, us.aidFundMemberUserType)", "=", aidFundMemberUserType); cnd.andEX("info.changeType", "=", changeType); cnd.and("t.taskName", "=", "06ed1d6d-5f96-485f-9150-ed1ef4a082d6"); diff --git a/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/aidFund/model/AidFundMemberChangeRecord.java b/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/aidFund/model/AidFundMemberChangeRecord.java index 50fa6d65..fe7b555d 100644 --- a/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/aidFund/model/AidFundMemberChangeRecord.java +++ b/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/aidFund/model/AidFundMemberChangeRecord.java @@ -38,10 +38,20 @@ public class AidFundMemberChangeRecord extends BaseModel { @ColDefine(type = ColType.VARCHAR, width = 50) private String changeType; + @Column + @Comment("申请时选择的基金会员类型(dict)") + @ColDefine(type = ColType.VARCHAR, width = 50) + private String aidFundMemberUserType; + @Column @Comment("申请时间") @ColDefine(type = ColType.DATETIME) private Date applyTime; + @Column + @Comment("是否系统管理员操作") + @ColDefine(type = ColType.BOOLEAN) + private Boolean adminOperate; + } diff --git a/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/aidFund/param/AidFundPageForm.java b/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/aidFund/param/AidFundPageForm.java index 97dc5ab8..a7c89000 100644 --- a/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/aidFund/param/AidFundPageForm.java +++ b/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/aidFund/param/AidFundPageForm.java @@ -22,6 +22,9 @@ public class AidFundPageForm extends PageForm { private String changeType; private Integer year; + //是否已审核 + private Boolean approval; + //是否扣款null全部、1已缴费、0未缴费 private Integer isPayed; } diff --git a/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/aidFund/service/AidFundMemberChangeRecordService.java b/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/aidFund/service/AidFundMemberChangeRecordService.java index c4a58e38..eb1160ce 100644 --- a/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/aidFund/service/AidFundMemberChangeRecordService.java +++ b/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/aidFund/service/AidFundMemberChangeRecordService.java @@ -1,6 +1,8 @@ package com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.service; import com.budwk.app.base.service.BaseService; +import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.param.AidFundPageForm; +import org.nutz.dao.sql.Sql; import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.model.AidFundMemberChangeRecord; import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.model.AidFundMemberPay; import org.nutz.lang.util.NutMap; @@ -9,10 +11,53 @@ import java.util.List; public interface AidFundMemberChangeRecordService extends BaseService { + /** + * 构建互管会审核页面查询 SQL。 + * + * @param pageForm 页面筛选参数,包含年度、姓名/工号、工会、单位、会员类型、变更类型和审核状态 + * @return 可直接用于分页或导出的查询 SQL + */ + Sql getFoundationAuditSql(AidFundPageForm pageForm); + /** + * 查询互管会审核页面导出数据。 + * + * @param pageForm 页面筛选参数,和列表页保持一致 + * @return 导出所需的列表数据,每条记录对应一条基金会员变更申请 + */ + List getFoundationAuditExportList(AidFundPageForm pageForm); + + /** + * 执行流程审核动作。 + * + * @param param 流程提交参数 JSON,包含提交流程所需的任务、意见和业务字段 + * @param id 基金会员变更记录 ID + */ void executeTask( String param, String id); + /** + * 批量执行互管会审核任务。 + * + * @param param 流程提交参数 JSON,包含统一的审批意见和提交类型 + * @param ids 选中的基金会员变更记录 ID 数组 + * @return 批量审核结果,包含成功数、失败数和失败原因 + */ + NutMap batchExecuteTask(String param, String[] ids); + /** + * 计算基金会员应补缴金额。 + * + * @param arrivalAtSchoolDate 入校时间,格式为 yyyy-MM-dd + * @param userId 用户 ID,空值时默认取当前登录人 + * @return 当前规则下计算出的应缴金额 + */ int getPayMoney(String arrivalAtSchoolDate,String userId); + + /** + * 查询用户历年缴费记录。 + * + * @param userId 用户 ID,空值时默认取当前登录人 + * @return 用户及其旧工号对应的缴费记录列表 + */ List getUserPayRecordList(String userId); } diff --git a/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/aidFund/service/AidFundMemberService.java b/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/aidFund/service/AidFundMemberService.java index 106c9699..1c96c372 100644 --- a/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/aidFund/service/AidFundMemberService.java +++ b/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/aidFund/service/AidFundMemberService.java @@ -2,6 +2,8 @@ package com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.service; import com.budwk.app.base.service.BaseService; import com.budwk.app.sys.models.Sys_user; +import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.param.AidFundPageForm; +import org.nutz.dao.sql.Sql; import org.nutz.lang.util.NutMap; import java.util.List; @@ -31,5 +33,30 @@ public interface AidFundMemberService extends BaseService { Double getPayMoney(Sys_user user, Integer year); + /** + * 获取基金会员台账列表查询SQL。 + * + * @param pageForm 页面查询条件,包含会员类型、工会、单位、关键字、排序及未缴费筛选参数 + * @return 与基金会员台账页面一致的查询SQL + */ + Sql getManageSql(AidFundPageForm pageForm); + + List findChangeRecordList(String userId); + + /** + * 管理端批量变更基金会员状态,并记录“系统管理员操作”来源。 + * + * @param ids 需要变更的用户ID数组,来自台账页面勾选行 + * @param changeType 变更类型字典值,对应 AIDFUND_MEMBER_CHANGE_TYPE + */ + void doBatchChangeTypeByAdmin(String[] ids, String changeType); + + /** + * 管理端对单个用户执行“变更”操作,并记录“系统管理员操作”来源。 + * + * @param userId 被变更的用户ID + * @param changeType 变更类型字典值,对应 AIDFUND_MEMBER_CHANGE_TYPE + */ + void doEditChangeTypeByAdmin(String userId, String changeType); } diff --git a/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/aidFund/service/impl/AidFundMemberChangeRecordServiceImpl.java b/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/aidFund/service/impl/AidFundMemberChangeRecordServiceImpl.java index 9e0fb920..f34337a8 100644 --- a/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/aidFund/service/impl/AidFundMemberChangeRecordServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/aidFund/service/impl/AidFundMemberChangeRecordServiceImpl.java @@ -3,20 +3,34 @@ package com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.service.impl; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.date.DateUtil; import cn.hutool.core.lang.Dict; +import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; +import com.budwk.app.base.constant.RoleConstant; import com.budwk.app.base.service.impl.BaseServiceImpl; +import com.budwk.app.base.utils.PageUtil; +import com.budwk.app.flow.constant.FlowConst; +import com.budwk.app.flow.entity.ProcessInstance; +import com.budwk.app.flow.entity.ProcessTask; +import com.budwk.app.flow.enums.ProcessTaskStateEnum; import com.budwk.app.flow.service.FlowCommonService; +import com.budwk.app.flow.service.ProcessTaskService; import com.budwk.app.sys.models.Sys_user; +import com.budwk.app.sys.services.SysDictService; +import com.budwk.app.web.commons.auth.utils.AuthUtil; import com.budwk.app.web.commons.auth.utils.SecurityUtil; import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.mode.AidFundMemberMode; import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.model.AidFundMemberChangeRecord; import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.model.AidFundMemberPay; +import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.param.AidFundPageForm; import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.service.AidFundMemberChangeRecordService; 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.dao.util.cri.SqlExpressionGroup; import org.nutz.ioc.aop.Aop; import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.IocBean; @@ -33,6 +47,9 @@ import java.util.*; */ @IocBean(args = {"refer:dao"}) public class AidFundMemberChangeRecordServiceImpl extends BaseServiceImpl implements AidFundMemberChangeRecordService { + private static final String FOUNDATION_AUDIT_TASK_NAME = "85c7d148-44f5-4333-b854-ab225357fd3e"; + private static final String CHANGE_TYPE_JOIN = "AIDFUND_MEMBER_CHANGE_TYPE_ONE"; + public AidFundMemberChangeRecordServiceImpl(Dao dao) { super(dao); } @@ -41,21 +58,228 @@ public class AidFundMemberChangeRecordServiceImpl extends BaseServiceImpl getFoundationAuditExportList(AidFundPageForm pageForm) { + List list = listMap(getFoundationAuditSql(pageForm)); + Map memberTypeMap = getDictNameMap("AIDFUND_MEMBER_USER_TYPE"); + Map changeTypeMap = getDictNameMap("AIDFUND_MEMBER_CHANGE_TYPE"); + list.forEach(map -> { + String arrivalAtSchoolDate = map.getString("arrivalAtSchoolDate"); + String changeType = map.getString("changeType"); + map.put("approvalStatus", ObjectUtil.equals(map.getInt("taskState"), ProcessTaskStateEnum.DOING.getCode()) ? "未审核" : "已审核"); + map.put("aidFundMemberUserType", memberTypeMap.getOrDefault(map.getString("aidFundMemberUserType"), map.getString("aidFundMemberUserType"))); + map.put("changeType", changeTypeMap.getOrDefault(changeType, changeType)); + map.put("arrivalAtSchoolDate", StrUtil.isBlank(arrivalAtSchoolDate) ? "" : DateUtil.format(DateUtil.parse(arrivalAtSchoolDate), "yyyy-MM-dd")); + map.put("applyTime", map.getTime("applyTime") == null ? "" : DateUtil.formatDateTime(map.getTime("applyTime"))); + // 仅加入基金时需要计算补缴金额,其他变更类型保持空白,避免误导为固定收费。 + map.put("money", ObjectUtil.equals(changeType, CHANGE_TYPE_JOIN) && StrUtil.isNotBlank(arrivalAtSchoolDate) + ? getPayMoney(arrivalAtSchoolDate, map.getString("userId")) : ""); + }); + return list; + } + + private Map getDictNameMap(String dictCode) { + return sysDictService.getSubListByCode(dictCode).stream().collect(HashMap::new, (map, dict) -> map.put(dict.getCode(), dict.getName()), HashMap::putAll); + } + @Override @Aop(TransAop.READ_COMMITTED) public void executeTask(String param, String id) { Dict args = Json.fromJson(Dict.class, param); + executeTask(args, id); + } + + @Override + @Aop(TransAop.READ_COMMITTED) + public NutMap batchExecuteTask(String param, String[] ids) { + NutMap result = NutMap.NEW(); + if (ArrayUtil.isEmpty(ids)) { + return result.setv("successCount", 0).setv("failCount", 0).setv("failMessages", List.of("请选择需要审核的记录")); + } + Dict baseArgs = Json.fromJson(Dict.class, param); + List doingTasks = processTaskService.getDoingTaskByBizIdTaskName(Arrays.asList(ids), FOUNDATION_AUDIT_TASK_NAME); + Map taskMap = new HashMap<>(); + List instanceIds = doingTasks.stream().map(ProcessTask::getProcessInstanceId).filter(Objects::nonNull).distinct().toList(); + Map businessNoMap = new HashMap<>(); + if (CollectionUtil.isNotEmpty(instanceIds)) { + List instances = dao().query(ProcessInstance.class, Cnd.where(ProcessInstance::getId, "in", instanceIds)); + instances.forEach(instance -> businessNoMap.put(instance.getId(), instance.getBusinessNo())); + } + doingTasks.forEach(task -> { + String businessNo = businessNoMap.get(task.getProcessInstanceId()); + if (StrUtil.isNotBlank(businessNo)) { + taskMap.put(businessNo, task); + } + }); + List failMessages = new ArrayList<>(); + int successCount = 0; + for (String id : ids) { + AidFundMemberChangeRecord changeRecord = fetch(id); + if (changeRecord == null) { + failMessages.add(id + ":申请记录不存在"); + continue; + } + ProcessTask processTask = taskMap.get(id); + if (processTask == null) { + failMessages.add(resolveRecordName(changeRecord) + ":审核任务不存在或已处理"); + continue; + } + if (!processTaskService.isAllowed(processTask, SecurityUtil.getUserId())) { + failMessages.add(resolveRecordName(changeRecord) + ":当前账号无权审核该记录"); + continue; + } + try { + Dict args = buildBatchTaskArgs(baseArgs, changeRecord, processTask); + executeTask(args, id); + successCount++; + } catch (Exception e) { + failMessages.add(resolveRecordName(changeRecord) + ":" + e.getMessage()); + } + } + result.setv("successCount", successCount); + result.setv("failCount", failMessages.size()); + result.setv("failMessages", failMessages); + return result; + } + + /** + * 批量审核沿用单条审核逻辑,但需要为每条记录补齐流程任务 ID 和加入基金时的入校时间。 + */ + private Dict buildBatchTaskArgs(Dict baseArgs, AidFundMemberChangeRecord changeRecord, ProcessTask processTask) { + Dict args = baseArgs.clone(); + args.set(FlowConst.PROCESS_TASK_ID_KEY, processTask.getId()); + args.set("changeType", changeRecord.getChangeType()); + if (ObjectUtil.equals(args.getInt("submitType"), 1) && ObjectUtil.equals(changeRecord.getChangeType(), CHANGE_TYPE_JOIN)) { + Sys_user user = dao().fetch(Sys_user.class, changeRecord.getUserId()); + if (user == null || StrUtil.isBlank(user.getArrivalAtSchoolDate())) { + throw new IllegalArgumentException("缺少入校时间,无法批量通过"); + } + args.set("arrivalAtSchoolDate", user.getArrivalAtSchoolDate()); + } + return args; + } + + private String resolveRecordName(AidFundMemberChangeRecord changeRecord) { + Sys_user user = dao().fetch(Sys_user.class, changeRecord.getUserId()); + if (user == null || StrUtil.isBlank(user.getUsername())) { + return changeRecord.getId(); + } + return user.getUsername(); + } + + /** + * 统一处理互管会审核通过后的用户状态同步,并继续推进流程任务。 + */ + private void executeTask(Dict args, String id) { AidFundMemberChangeRecord changeRecord = fetch(id); + if (changeRecord == null) { + throw new IllegalArgumentException("申请记录不存在"); + } if (args.getInt("submitType") == 1) { - //如果是通过 - if (ObjectUtil.equals(args.getStr("changeType"), "AIDFUND_MEMBER_CHANGE_TYPE_ONE")) { - //如果是加入基金 - dao().update(Sys_user.class, Chain.make("arrivalAtSchoolDate", args.getStr("arrivalAtSchoolDate")).add("aidFundMemberJoinTime", new Date()) - .add("aidFundMember", AidFundMemberMode.NORMAL.getCode()), + // 审核通过后需要把本次申请的基金会员类型同步回用户表,历史空值则保留用户原值不覆盖。 + String aidFundMemberUserType = changeRecord.getAidFundMemberUserType(); + // 如果是通过,加入基金需要回写入校时间和入会状态,其他类型按退会逻辑处理。 + if (ObjectUtil.equals(args.getStr("changeType"), CHANGE_TYPE_JOIN)) { + if (StrUtil.isBlank(args.getStr("arrivalAtSchoolDate"))) { + throw new IllegalArgumentException("缺少入校时间,无法通过审核"); + } + Chain chain = Chain.make("arrivalAtSchoolDate", args.getStr("arrivalAtSchoolDate")) + .add("aidFundMemberJoinTime", new Date()) + .add("aidFundMember", AidFundMemberMode.NORMAL.getCode()); + if (StrUtil.isNotBlank(aidFundMemberUserType)) { + chain.add("aidFundMemberUserType", aidFundMemberUserType); + } + dao().update(Sys_user.class, chain, Cnd.where(Sys_user::getId, "=", changeRecord.getUserId())); } else { - dao().update(Sys_user.class, Chain.make("aidFundMember", AidFundMemberMode.NONE.getCode()) - .add("aidFundMemberQuitTime", new Date()), + Chain chain = Chain.make("aidFundMember", AidFundMemberMode.NONE.getCode()) + .add("aidFundMemberQuitTime", new Date()); + if (StrUtil.isNotBlank(aidFundMemberUserType)) { + chain.add("aidFundMemberUserType", aidFundMemberUserType); + } + dao().update(Sys_user.class, chain, Cnd.where(Sys_user::getId, "=", changeRecord.getUserId())); } } diff --git a/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/aidFund/service/impl/AidFundMemberServiceImpl.java b/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/aidFund/service/impl/AidFundMemberServiceImpl.java index 618afc44..5ae7baf2 100644 --- a/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/aidFund/service/impl/AidFundMemberServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/aidFund/service/impl/AidFundMemberServiceImpl.java @@ -2,18 +2,28 @@ package com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.service.impl; import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import com.budwk.app.base.constant.RoleConstant; import com.budwk.app.base.service.impl.BaseServiceImpl; +import com.budwk.app.base.utils.PageUtil; import com.budwk.app.sys.models.Sys_user; 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; import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.mode.AidFundMemberMode; +import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.model.AidFundMemberChangeRecord; import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.model.AidFundMemberHistory; import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.model.AidFundMemberPay; +import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.param.AidFundPageForm; import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.service.AidFundMemberService; +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.dao.util.cri.SqlExpressionGroup; +import org.nutz.ioc.aop.Aop; import org.nutz.ioc.loader.annotation.IocBean; import org.nutz.lang.Strings; import org.nutz.lang.util.NutMap; @@ -121,6 +131,62 @@ public class AidFundMemberServiceImpl extends BaseServiceImpl implements AidFund return (year + 1 - beforeYear) * money + (year - beforeYear) * money * 0.1; } + /** + * 统一封装基金会员台账列表查询口径,分页查询和导出都复用这里,避免“所见”和“所得”不一致。 + * + * @param pageForm 页面查询条件,包含会员类型、工会、单位、关键字、排序及未缴费筛选参数 + * @return 基金会员台账查询SQL + */ + @Override + public Sql getManageSql(AidFundPageForm pageForm) { + Sql sql = Sqls.create(""" + SELECT + id, + loginname, + username, + sex, + arrivalAtSchoolDate, + unitName, + unionName, + aidFundMemberUserType, + aidFundMemberJoinTime + FROM + vw_user + $condition + """); + Cnd cnd = Cnd.NEW(); + // 非系统管理员按现有页面权限口径控制数据范围,确保导出结果与列表展示一致。 + if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name())) { + if (AuthUtil.hasRole(RoleConstant.RETIREMENT_WORKPLACE.name())) { + cnd.and(Sys_user::getAidFundMemberUserType, "=", "离退休人员"); + } else { + cnd.and(View_user::getUnionId, "=", SecurityUtil.getUnionId()); + } + } + + if (pageForm.getNotPayedCurrentYear()) { + cnd.and("id", "in", Sqls.create("SELECT userid FROM `aid_fund_member_pay` where `year` = YEAR(CURDATE()) and isPayed = 0")); + } + cnd.andEX(Sys_user::getAidFundMemberUserType, "=", pageForm.getAidFundMemberUserType()); + cnd.andEX(View_user::getUnionId, "=", pageForm.getUnionId()); + cnd.andEX(View_user::getUnitId, "=", pageForm.getUnitId()); + cnd.and(Sys_user::getAidFundMember, "=", AidFundMemberMode.NORMAL.getCode()); + + if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) { + SqlExpressionGroup seg = new SqlExpressionGroup(); + seg.orLike("loginname", pageForm.getSearchKeyword()); + seg.orLike("username", pageForm.getSearchKeyword()); + cnd.and(seg); + } + if (StrUtil.isAllBlank(pageForm.getPageOrderName(), pageForm.getPageOrderBy())) { + cnd.desc("unitCode"); + } else { + cnd.orderBy(pageForm.getPageOrderName(), PageUtil.getOrder(pageForm.getPageOrderBy())); + } + sql.setCondition(cnd); + return sql; + } + @Override public List findChangeRecordList(String userId) { Sql sql = Sqls.create(""" @@ -168,5 +234,93 @@ public class AidFundMemberServiceImpl extends BaseServiceImpl implements AidFund return listMap(sql); } + /** + * 管理端批量变更基金会员状态时,统一同步用户表并记录“系统管理员操作”的变更记录。 + * + * @param ids 需要变更的用户ID数组,来自台账页面勾选结果 + * @param changeType 变更类型字典值,对应 AIDFUND_MEMBER_CHANGE_TYPE + */ + @Override + @Aop(TransAop.READ_COMMITTED) + public void doBatchChangeTypeByAdmin(String[] ids, String changeType) { + List sysUserList = dao().query(Sys_user.class, Cnd.where("id", "in", ids)); + List recordArrayList = new ArrayList<>(); + for (String id : ids) { + Sys_user user = sysUserList.stream().filter(sysUser -> sysUser.getId().equals(id)).findFirst().orElse(null); + if (user == null) { + continue; + } + recordArrayList.add(buildAdminChangeRecord(user, changeType)); + } + updateUserChangeType(ids, changeType); + if (!recordArrayList.isEmpty()) { + dao().insert(recordArrayList); + } + } + + /** + * 管理端单个“变更”操作时,统一同步用户表并记录“系统管理员操作”的变更记录。 + * + * @param userId 被变更的用户ID + * @param changeType 变更类型字典值,对应 AIDFUND_MEMBER_CHANGE_TYPE + */ + @Override + @Aop(TransAop.READ_COMMITTED) + public void doEditChangeTypeByAdmin(String userId, String changeType) { + Sys_user user = dao().fetch(Sys_user.class, userId); + if (user == null) { + return; + } + dao().insert(buildAdminChangeRecord(user, changeType)); + updateUserChangeType(new String[]{userId}, changeType); + } + + /** + * 管理端变更落记录时,需要明确标记记录来源,避免被“我的申请”误识别为本人发起。 + * + * @param user 当前被操作的用户 + * @param changeType 变更类型字典值 + * @return 已补齐管理员操作标记和会员类型快照的变更记录 + */ + private AidFundMemberChangeRecord buildAdminChangeRecord(Sys_user user, String changeType) { + return new AidFundMemberChangeRecord() + .setUserId(user.getId()) + .setAidFundMemberUserType(resolveChangeRecordMemberType(user, changeType)) + .setApplyTime(new Date()) + .setChangeType(changeType) + .setAdminOperate(true); + } + + /** + * 退休变更需要把记录中的会员类型同步成退休类型,其余情况沿用当前用户档案中的会员类型。 + * + * @param user 当前被操作的用户 + * @param changeType 变更类型字典值 + * @return 变更记录中应保存的基金会员类型 + */ + private String resolveChangeRecordMemberType(Sys_user user, String changeType) { + if (ObjectUtil.equals(changeType, "AIDFUND_MEMBER_CHANGE_TYPE_EIGTH")) { + return "离退休人员"; + } + return user.getAidFundMemberUserType(); + } + + /** + * 管理端“变更”与“批量变更”共用同一套用户表更新规则,确保页面操作口径一致。 + * + * @param ids 需要更新的用户ID数组 + * @param changeType 变更类型字典值 + */ + private void updateUserChangeType(String[] ids, String changeType) { + Chain add; + if (ObjectUtil.equals(changeType, "AIDFUND_MEMBER_CHANGE_TYPE_EIGTH")) { + add = Chain.make("aidFundMemberUserType", "离退休人员"); + } else { + add = Chain.make("aidFundMember", AidFundMemberMode.NONE.getCode()) + .add("aidFundMemberQuitTime", new Date()); + } + dao().update(Sys_user.class, add, Cnd.where("id", "in", ids)); + } + } diff --git a/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/medical/controller/MedicalCalculateMoneyController.java b/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/medical/controller/MedicalCalculateMoneyController.java index 250b00de..909c53f3 100644 --- a/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/medical/controller/MedicalCalculateMoneyController.java +++ b/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/medical/controller/MedicalCalculateMoneyController.java @@ -73,6 +73,29 @@ public class MedicalCalculateMoneyController { return Result.success(applyData); } + @At + @ApiOperation("更新医疗互助补助金额明细") + @SaCheckPermission("medicalMutualAid.medical.calculateMoney") + public Object setGrantsOrUnionInto(String keys, double value) { + //获取申请最新年份 + Sql yearSql = Sqls.create("SELECT MAX(YEAR(applyTime)) AS currentYear FROM medical_apply"); + yearSql.setCallback(Sqls.callback.longs()); + medicalCalculateMoneyService.execute(yearSql); + long year = yearSql.getLong(); + MedicalMoneyDetail moneyDetail = medicalCalculateMoneyService.dao().fetch(MedicalMoneyDetail.class, Cnd.where("year", "=", year)); + if (Lang.isEmpty(moneyDetail)) { + moneyDetail = new MedicalMoneyDetail(); + moneyDetail.setYear((int) year); + } + if (keys.equals("grants")) { + moneyDetail.setSchoolGrants(new BigDecimal(value)); + } else if (keys.equals("into")) { + moneyDetail.setUnionInto(new BigDecimal(value)); + } + medicalCalculateMoneyService.insertOrUpdate(moneyDetail); + return null; + } + @At @ApiOperation("按比例修改补助金额") diff --git a/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/medical/controller/MedicalDashBoardController.java b/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/medical/controller/MedicalDashBoardController.java new file mode 100644 index 00000000..e6d4b996 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/medical/controller/MedicalDashBoardController.java @@ -0,0 +1,126 @@ +package com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.controller; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import com.budwk.app.base.result.Result; +import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.service.MedicalDashBoardService; +import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.vo.MedicalDashboardCategoryStatVO; +import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.vo.MedicalDashboardOverviewVO; +import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.vo.MedicalDashboardUnionResultVO; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.nutz.ioc.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.mvc.annotation.At; +import org.nutz.mvc.annotation.Ok; + +import java.util.List; + +/** + * @author Codex + * @description 医疗互助数据看板 + */ +@IocBean +@At("/platform/medicalMutualAid/medical/dashboard") +@Api("医疗互助数据看板") +@Ok("json:full") +public class MedicalDashBoardController { + + @Inject + private MedicalDashBoardService medicalDashBoardService; + + @At("") + @Ok("beetl:/platform/zhgh/staffbenefit/medicalMutualAid/medical/dashboard/index.html") + @SaCheckPermission("medicalMutualAid.medical.dashboard") + public void index() { + } + + /** + * 查询医疗互助总览统计。 + * 返回值字段说明: + * 1. fundMemberCount:当前基金会员总数 + * 2. currentYearApplyCount:本年度已办结申请数 + * 3. majorDiseaseApplyCount:重疾已办结申请数 + * 4. commonDiseaseApplyCount:普通疾病已办结申请数 + * 5. totalSubsidyMoney:补助金额合计 + * 6. totalLoveSubsidyMoney:爱心基金金额合计 + * + * @return 医疗互助总览统计 VO + */ + @At + @ApiOperation("查询医疗互助总览统计") + @SaCheckPermission("medicalMutualAid.medical.dashboard") + public Result overview() { + MedicalDashboardOverviewVO vo = medicalDashBoardService.getOverview(); + return Result.success(vo); + } + + /** + * 按工会统计医疗互助已办结申请数据。 + * 返回值字段说明: + * 1. tableData:表格使用的工会统计列表 + * 2. 列表元素中 applyCount 为已办结申请数,subsidyMoney 为补助金额合计 + * + * @return 工会统计结果 VO + */ + @At + @ApiOperation("按工会统计医疗互助数据") + @SaCheckPermission("medicalMutualAid.medical.dashboard") + public Result unionStat() { + MedicalDashboardUnionResultVO vo = medicalDashBoardService.getUnionStat(); + return Result.success(vo); + } + + /** + * 按在职状态统计医疗互助已办结申请数据。 + * 返回列表字段说明: + * 1. code:在职状态编码 + * 2. name:在职状态名称 + * 3. applyCount:该状态下已办结申请数 + * 4. subsidyMoney:该状态下补助金额合计 + * + * @return 在职状态统计 VO 列表 + */ + @At + @ApiOperation("按在职状态统计医疗互助数据") + @SaCheckPermission("medicalMutualAid.medical.dashboard") + public Result userStateStat() { + List list = medicalDashBoardService.getUserStateStat(); + return Result.success(list); + } + + /** + * 按人员类型统计医疗互助已办结申请数据。 + * 返回列表字段说明: + * 1. code:人员类型编码 + * 2. name:人员类型名称 + * 3. applyCount:该类型下已办结申请数 + * 4. subsidyMoney:该类型下补助金额合计 + * + * @return 人员类型统计 VO 列表 + */ + @At + @ApiOperation("按人员类型统计医疗互助数据") + @SaCheckPermission("medicalMutualAid.medical.dashboard") + public Result personTypeStat() { + List list = medicalDashBoardService.getPersonTypeStat(); + return Result.success(list); + } + + /** + * 按疾病类型统计医疗互助已办结申请数据。 + * 返回列表字段说明: + * 1. code:疾病类型 ID + * 2. name:疾病名称 + * 3. applyCount:该疾病下已办结申请数 + * 4. subsidyMoney:该疾病下补助金额合计 + * + * @return 疾病类型统计 VO 列表 + */ + @At + @ApiOperation("按疾病类型统计医疗互助数据") + @SaCheckPermission("medicalMutualAid.medical.dashboard") + public Result diseaseTypeStat() { + List list = medicalDashBoardService.getDiseaseTypeStat(); + return Result.success(list); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/medical/controller/MedicalUnionAuditController.java b/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/medical/controller/MedicalUnionAuditController.java index f8453d41..178a9491 100644 --- a/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/medical/controller/MedicalUnionAuditController.java +++ b/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/medical/controller/MedicalUnionAuditController.java @@ -55,6 +55,7 @@ public class MedicalUnionAuditController { @SaCheckPermission("medicalMutualAid.medical.unionAudit") public Result pageData(MedicalPageForm pageForm) { Cnd cnd = Cnd.NEW(); + cnd.and("t.taskName", "=", "a9bbf69c-5edf-4161-a364-47996d212472"); Sql sql = medicalAuditService.getAuditSql(pageForm, cnd); sql.setCondition(cnd); diff --git a/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/medical/service/MedicalDashBoardService.java b/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/medical/service/MedicalDashBoardService.java new file mode 100644 index 00000000..74c2e447 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/medical/service/MedicalDashBoardService.java @@ -0,0 +1,61 @@ +package com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.service; + +import com.budwk.app.base.service.BaseService; +import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.model.MedicalApply; +import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.vo.MedicalDashboardCategoryStatVO; +import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.vo.MedicalDashboardOverviewVO; +import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.vo.MedicalDashboardUnionResultVO; + +import java.util.List; + +/** + * @author Codex + * @description 医疗互助数据看板 service + */ +public interface MedicalDashBoardService extends BaseService { + + /** + * 查询医疗互助数据看板总览数据。 + * 返回值包含: + * 1. 已办结申请总数 + * 2. 本年度已办结申请数 + * 3. 重疾/普通疾病申请数 + * 4. 补助金额与爱心基金金额汇总 + * + * @return 医疗互助总览统计 VO + */ + MedicalDashboardOverviewVO getOverview(); + + /** + * 按工会统计医疗互助已办结申请数据。 + * 返回值中的 chartData 与 tableData 当前来源相同, + * 便于前端图表和表格按既有字段直接取值。 + * + * @return 工会统计结果 VO + */ + MedicalDashboardUnionResultVO getUnionStat(); + + /** + * 按人员在职状态统计医疗互助已办结申请数据。 + * code 为在职状态编码,name 为字典名称。 + * + * @return 在职状态统计列表 + */ + List getUserStateStat(); + + /** + * 按人员类型统计医疗互助已办结申请数据。 + * code 为人员类型编码,name 为字典名称。 + * + * @return 人员类型统计列表 + */ + List getPersonTypeStat(); + + /** + * 按疾病类型统计医疗互助已办结申请数据。 + * code 为疾病类型 ID,name 为疾病名称。 + * + * @return 疾病类型统计列表 + */ + List getDiseaseTypeStat(); +} diff --git a/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/medical/service/impl/MedicalApplyServiceImpl.java b/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/medical/service/impl/MedicalApplyServiceImpl.java index 56fd8e7c..db5256b0 100644 --- a/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/medical/service/impl/MedicalApplyServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/medical/service/impl/MedicalApplyServiceImpl.java @@ -186,6 +186,7 @@ public class MedicalApplyServiceImpl extends BaseServiceImpl imple // 查询申请成功的记录 List applyRecords = getApplyRecord(allUserIds); + applyRecords.removeIf(item -> applyId.equals(item.getString("id"))); //是否首次申请 爱心互助基金 重大疾病 boolean isFirstApply = applyRecords.stream().filter(v -> v.getBoolean("isMajorDiseases")).toList().isEmpty(); @@ -230,7 +231,7 @@ public class MedicalApplyServiceImpl extends BaseServiceImpl imple Boolean isBeyondAllMoney = allMoney.compareTo(medicalSetting.getPreviousYearMaxMoney()) > 0; - Sys_user user = dao().fetch(Sys_user.class, SecurityUtil.getUserId()); + Sys_user user = dao().fetch(Sys_user.class, applyInfo.getString("userId")); boolean fullTenYears = DateUtil.thisYear() - Integer.parseInt(user.getAidFundDeductTime()) >= 10; boolean fullFifYears = DateUtil.thisYear() - Integer.parseInt(user.getAidFundDeductTime()) >= 15; diff --git a/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/medical/service/impl/MedicalAuditServiceImpl.java b/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/medical/service/impl/MedicalAuditServiceImpl.java index bf577404..2dbf0673 100644 --- a/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/medical/service/impl/MedicalAuditServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/medical/service/impl/MedicalAuditServiceImpl.java @@ -72,7 +72,9 @@ public class MedicalAuditServiceImpl extends BaseServiceImpl imple seg.orLike("us.username", pageForm.getSearchKeyword()); cnd.and(seg); } - cnd.and("ta.actorId", "in", List.of(SecurityUtil.getUserId())); + if(!SecurityUtil.getUserLoginname().equals("superadmin")){ + cnd.and("ta.actorId", "in", List.of(SecurityUtil.getUserId())); + } cnd.andEX("year(info.applyTime)", "=", pageForm.getYear()); cnd.andEX("info.diseaseId", "=", pageForm.getDiseaseId()); cnd.andEX("us.unionid", "=", pageForm.getUnionId()); diff --git a/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/medical/service/impl/MedicalDashBoardServiceImpl.java b/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/medical/service/impl/MedicalDashBoardServiceImpl.java new file mode 100644 index 00000000..6bca8157 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/medical/service/impl/MedicalDashBoardServiceImpl.java @@ -0,0 +1,248 @@ +package com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.service.impl; + +import com.budwk.app.base.service.impl.BaseServiceImpl; +import com.budwk.app.flow.enums.ProcessInstanceStateEnum; +import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.model.MedicalApply; +import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.service.MedicalDashBoardService; +import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.vo.MedicalDashboardCategoryStatVO; +import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.vo.MedicalDashboardOverviewVO; +import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.vo.MedicalDashboardUnionResultVO; +import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.vo.MedicalDashboardUnionStatVO; +import org.nutz.dao.Dao; +import org.nutz.dao.Sqls; +import org.nutz.dao.sql.Sql; +import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.lang.util.NutMap; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; + +/** + * @author Codex + * @description 医疗互助数据看板 service 实现 + */ +@IocBean(args = {"refer:dao"}) +public class MedicalDashBoardServiceImpl extends BaseServiceImpl implements MedicalDashBoardService { + + public MedicalDashBoardServiceImpl(Dao dao) { + super(dao); + } + + @Override + public MedicalDashboardOverviewVO getOverview() { + Sql sql = Sqls.create(""" + SELECT + (SELECT COUNT(1) FROM sys_user WHERE aidFundMember = 1) AS fundMemberCount, + COUNT(DISTINCT CASE WHEN YEAR(info.applyTime) = YEAR(NOW()) THEN info.id END) AS currentYearApplyCount, + COUNT(DISTINCT CASE WHEN type.isMajorDiseases = 1 THEN info.id END) AS majorDiseaseApplyCount, + COUNT(DISTINCT CASE WHEN type.isMajorDiseases = 0 THEN info.id END) AS commonDiseaseApplyCount, + COALESCE(SUM(info.subsidyMoney), 0) AS totalSubsidyMoney, + COALESCE(SUM(info.loveSubsidyMoney), 0) AS totalLoveSubsidyMoney + FROM + medical_apply info + INNER JOIN wf_process_instance ins ON ins.businessNo = info.id + LEFT JOIN medical_disease_type type ON type.id = info.diseaseId + WHERE + ins.state = @state + """); + sql.setParam("state", ProcessInstanceStateEnum.FINISHED.getCode()); + NutMap data = fetchOne(sql); + + MedicalDashboardOverviewVO vo = new MedicalDashboardOverviewVO(); + vo.setFundMemberCount(data.getLong("fundMemberCount", 0L)); + vo.setCurrentYearApplyCount(data.getLong("currentYearApplyCount", 0L)); + vo.setMajorDiseaseApplyCount(data.getLong("majorDiseaseApplyCount", 0L)); + vo.setCommonDiseaseApplyCount(data.getLong("commonDiseaseApplyCount", 0L)); + vo.setTotalSubsidyMoney(getBigDecimalValue(data, "totalSubsidyMoney")); + vo.setTotalLoveSubsidyMoney(getBigDecimalValue(data, "totalLoveSubsidyMoney")); + return vo; + } + + @Override + public MedicalDashboardUnionResultVO getUnionStat() { + Sql sql = Sqls.create(""" + SELECT + un.id AS unionId, + un.`name` AS unionName, + un.unionCode AS unionCode, + COUNT(applyInfo.id) AS applyCount, + COALESCE(SUM(applyInfo.subsidyMoney), 0) AS subsidyMoney + FROM + sys_union un + LEFT JOIN vw_user us ON us.unionId = un.id + LEFT JOIN ( + SELECT + info.id, + info.userId, + info.subsidyMoney + FROM + medical_apply info + INNER JOIN wf_process_instance ins ON ins.businessNo = info.id + WHERE + ins.state = @state + ) applyInfo ON applyInfo.userId = us.id + GROUP BY + un.id, un.`name`, un.unionCode + ORDER BY + un.unionCode + """); + sql.setParam("state", ProcessInstanceStateEnum.FINISHED.getCode()); + List list = toUnionStatList(listMap(sql)); + + MedicalDashboardUnionResultVO resultVO = new MedicalDashboardUnionResultVO(); + resultVO.setTableData(new ArrayList<>(list)); + return resultVO; + } + + @Override + public List getUserStateStat() { + Sql sql = Sqls.create(""" + SELECT + dict.`code` AS code, + dict.`name` AS name, + COUNT(info.id) AS applyCount, + COALESCE(SUM(info.subsidyMoney), 0) AS subsidyMoney + FROM + sys_dict dict + LEFT JOIN sys_dict parent ON parent.id = dict.parentId + LEFT JOIN vw_user us ON us.userState = dict.`code` + LEFT JOIN medical_apply info ON info.userId = us.id + LEFT JOIN wf_process_instance ins ON ins.businessNo = info.id AND ins.state = @state + WHERE + parent.`code` = 'USER_STATE' + AND ins.id IS NOT NULL + GROUP BY + dict.`code`, dict.`name`, dict.location + ORDER BY + dict.location + """); + sql.setParam("state", ProcessInstanceStateEnum.FINISHED.getCode()); + return toCategoryStatList(listMap(sql)); + } + + @Override + public List getPersonTypeStat() { + Sql sql = Sqls.create(""" + SELECT + dict.`code` AS code, + dict.`name` AS name, + COUNT(info.id) AS applyCount, + COALESCE(SUM(info.subsidyMoney), 0) AS subsidyMoney + FROM + sys_dict dict + LEFT JOIN sys_dict parent ON parent.id = dict.parentId + LEFT JOIN vw_user us ON us.personType = dict.`code` + LEFT JOIN medical_apply info ON info.userId = us.id + LEFT JOIN wf_process_instance ins ON ins.businessNo = info.id AND ins.state = @state + WHERE + parent.`code` = 'USER_PERSON_TYPE' + AND ins.id IS NOT NULL + GROUP BY + dict.`code`, dict.`name`, dict.location + ORDER BY + dict.location + """); + sql.setParam("state", ProcessInstanceStateEnum.FINISHED.getCode()); + return toCategoryStatList(listMap(sql)); + } + + @Override + public List getDiseaseTypeStat() { + Sql sql = Sqls.create(""" + SELECT + type.id AS code, + type.diseaseName AS name, + COUNT(info.id) AS applyCount, + COALESCE(SUM(info.subsidyMoney), 0) AS subsidyMoney + FROM + medical_disease_type type + LEFT JOIN medical_apply info ON info.diseaseId = type.id + LEFT JOIN wf_process_instance ins ON ins.businessNo = info.id AND ins.state = @state + WHERE + ins.id IS NOT NULL + GROUP BY + type.id, type.diseaseName, type.diseaseCode + ORDER BY + type.diseaseCode + """); + sql.setParam("state", ProcessInstanceStateEnum.FINISHED.getCode()); + return toCategoryStatList(listMap(sql)); + } + + /** + * 执行单行统计 SQL。 + * 该方法用于处理总览类统计,返回结果仅一行。 + * + * @param sql 已设置好查询参数的 SQL 对象 + * @return 单行统计数据,字段不存在时返回空 NutMap,避免上层空指针 + */ + private NutMap fetchOne(Sql sql) { + sql.setCallback(Sqls.callback.map()); + dao().execute(sql); + Object result = sql.getResult(); + if (result instanceof NutMap) { + return (NutMap) result; + } + return NutMap.NEW(); + } + + /** + * 将工会统计查询结果转换为 VO。 + * + * @param dataList SQL 查询返回的 map 列表,字段需包含 unionId、unionName、unionCode、applyCount、subsidyMoney + * @return 工会统计 VO 列表 + */ + private List toUnionStatList(List dataList) { + List list = new ArrayList<>(); + for (NutMap data : dataList) { + MedicalDashboardUnionStatVO vo = new MedicalDashboardUnionStatVO(); + vo.setUnionId(data.getString("unionId")); + vo.setUnionName(data.getString("unionName")); + vo.setUnionCode(data.getString("unionCode")); + vo.setApplyCount(data.getLong("applyCount", 0L)); + vo.setSubsidyMoney(getBigDecimalValue(data, "subsidyMoney")); + list.add(vo); + } + return list; + } + + /** + * 将通用分类统计查询结果转换为 VO。 + * + * @param dataList SQL 查询返回的 map 列表,字段需包含 code、name、applyCount、subsidyMoney + * @return 分类统计 VO 列表 + */ + private List toCategoryStatList(List dataList) { + List list = new ArrayList<>(); + for (NutMap data : dataList) { + MedicalDashboardCategoryStatVO vo = new MedicalDashboardCategoryStatVO(); + vo.setCode(data.getString("code")); + vo.setName(data.getString("name")); + vo.setApplyCount(data.getLong("applyCount", 0L)); + vo.setSubsidyMoney(getBigDecimalValue(data, "subsidyMoney")); + list.add(vo); + } + return list; + } + + /** + * 从 NutMap 中安全读取金额字段。 + * SQL 聚合查询返回的金额字段有时会是 BigDecimal、Double、Long 或字符串, + * 因此这里统一做一次转换,避免 controller/service 层重复判空和类型判断。 + * + * @param data NutMap 查询结果对象 + * @param key 金额字段名 + * @return 对应金额,字段为空时返回 0 + */ + private BigDecimal getBigDecimalValue(NutMap data, String key) { + Object value = data.get(key); + if (value == null) { + return BigDecimal.ZERO; + } + if (value instanceof BigDecimal) { + return (BigDecimal) value; + } + return new BigDecimal(String.valueOf(value)); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/medical/vo/MedicalDashboardCategoryStatVO.java b/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/medical/vo/MedicalDashboardCategoryStatVO.java new file mode 100644 index 00000000..9f2cd8b8 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/medical/vo/MedicalDashboardCategoryStatVO.java @@ -0,0 +1,34 @@ +package com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.vo; + +import lombok.Data; + +import java.math.BigDecimal; + +/** + * @author Codex + * @description 医疗互助分类统计返回值 + */ +@Data +public class MedicalDashboardCategoryStatVO { + + /** + * 分类编码。 + * 不同接口场景下可能表示在职状态编码、人员类型编码、疾病ID 等。 + */ + private String code; + + /** + * 分类名称,用于前端直接展示图表/表格标题。 + */ + private String name; + + /** + * 当前分类下的已办结申请数 + */ + private Long applyCount; + + /** + * 当前分类下的已办结补助金额合计 + */ + private BigDecimal subsidyMoney; +} diff --git a/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/medical/vo/MedicalDashboardOverviewVO.java b/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/medical/vo/MedicalDashboardOverviewVO.java new file mode 100644 index 00000000..1e52ab2e --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/medical/vo/MedicalDashboardOverviewVO.java @@ -0,0 +1,43 @@ +package com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.vo; + +import lombok.Data; + +import java.math.BigDecimal; + +/** + * @author Codex + * @description 医疗互助数据看板总览统计返回值 + */ +@Data +public class MedicalDashboardOverviewVO { + + /** + * 当前基金会员总数 + */ + private Long fundMemberCount; + + /** + * 本年度已办结申请数 + */ + private Long currentYearApplyCount; + + /** + * 已办结重疾申请数 + */ + private Long majorDiseaseApplyCount; + + /** + * 已办结普通疾病申请数 + */ + private Long commonDiseaseApplyCount; + + /** + * 已办结补助金额合计 + */ + private BigDecimal totalSubsidyMoney; + + /** + * 已办结爱心基金金额合计 + */ + private BigDecimal totalLoveSubsidyMoney; +} diff --git a/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/medical/vo/MedicalDashboardUnionResultVO.java b/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/medical/vo/MedicalDashboardUnionResultVO.java new file mode 100644 index 00000000..2b96dbca --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/medical/vo/MedicalDashboardUnionResultVO.java @@ -0,0 +1,19 @@ +package com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.vo; + +import lombok.Data; + +import java.util.List; + +/** + * @author Codex + * @description 医疗互助按工会统计返回值 + */ +@Data +public class MedicalDashboardUnionResultVO { + + /** + * 表格数据。 + * 当前页面仅保留工会统计表格,因此只返回表格所需列表。 + */ + private List tableData; +} diff --git a/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/medical/vo/MedicalDashboardUnionStatVO.java b/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/medical/vo/MedicalDashboardUnionStatVO.java new file mode 100644 index 00000000..4f8621e4 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/staffbenefit/medicalMutualAid/medical/vo/MedicalDashboardUnionStatVO.java @@ -0,0 +1,38 @@ +package com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.vo; + +import lombok.Data; + +import java.math.BigDecimal; + +/** + * @author Codex + * @description 医疗互助按工会统计返回值 + */ +@Data +public class MedicalDashboardUnionStatVO { + + /** + * 工会ID + */ + private String unionId; + + /** + * 工会名称 + */ + private String unionName; + + /** + * 工会编码 + */ + private String unionCode; + + /** + * 已办结申请数 + */ + private Long applyCount; + + /** + * 已办结补助金额合计 + */ + private BigDecimal subsidyMoney; +} diff --git a/src/main/java/com/budwk/app/zhgh/staffmanage/member/controller/info/MemberInfoInputController.java b/src/main/java/com/budwk/app/zhgh/staffmanage/member/controller/info/MemberInfoInputController.java index c8fad38e..d856e161 100644 --- a/src/main/java/com/budwk/app/zhgh/staffmanage/member/controller/info/MemberInfoInputController.java +++ b/src/main/java/com/budwk/app/zhgh/staffmanage/member/controller/info/MemberInfoInputController.java @@ -1,20 +1,15 @@ package com.budwk.app.zhgh.staffmanage.member.controller.info; import cn.dev33.satoken.annotation.SaCheckPermission; -import cn.hutool.core.util.ObjectUtil; 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.sys.models.Sys_role; -import com.budwk.app.sys.models.Sys_union; import com.budwk.app.sys.models.Sys_user; -import com.budwk.app.sys.models.Sys_user_role; import com.budwk.app.zhgh.staffmanage.member.models.MemberChangeRecord; import com.budwk.app.zhgh.staffmanage.member.param.pageform.MemberInfoPageForm; import com.budwk.app.zhgh.staffmanage.member.service.MemberInfoService; -import org.apache.poi.ss.usermodel.Workbook; import org.nutz.aop.interceptor.ioc.TransAop; import org.nutz.dao.Cnd; import org.nutz.dao.Sqls; @@ -22,27 +17,13 @@ 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; -import org.nutz.lang.Strings; -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.nutz.mvc.annotation.POST; -import javax.servlet.http.HttpServletResponse; -import javax.validation.Valid; -import java.io.BufferedOutputStream; -import java.net.URLEncoder; -import java.util.List; -import java.util.stream.Collectors; -import java.util.zip.ZipEntry; -import java.util.zip.ZipOutputStream; - /** - * @version 1.0 - * @Author hqw - * @name:MemberInfoInputController - * @Date 2026/4/8 19:31 - * @注释 人员补录 + * 人员补录 */ @At("/platform/member/info/input") @Ok("json:full") @@ -58,7 +39,6 @@ public class MemberInfoInputController { public void index() { } - @At @SaCheckPermission("member.info.input") public Result pageData(MemberInfoPageForm pageForm) { @@ -74,30 +54,53 @@ public class MemberInfoInputController { @SLog(tag = "会员信息录入", type = "add", msg = "新增会员") public Result add(Sys_user user, String unionId, String unitId) { try { - memberInfoService.addMember(user,unionId,unitId); + memberInfoService.addMember(user, unionId, unitId); return Result.success("新增成功"); } catch (Exception e) { return Result.error(e.getMessage()); } } + @At + @SaCheckPermission("member.info.input") + public Result findOne(@Param("recordId") String recordId, @Param("userId") String userId) { + try { + return Result.success(memberInfoService.getMemberInputDetail(recordId, userId)); + } catch (Exception e) { + return Result.error(e.getMessage()); + } + } + @At @POST @Aop(TransAop.READ_COMMITTED) @SaCheckPermission("member.info.input") - @SLog(tag = "人员补录", msg = "删除会员") + @SLog(tag = "人员补录", type = "update", msg = "编辑补录人员") + public Result update(Sys_user user, String unionId, String unitId, String recordId) { + try { + memberInfoService.updateMemberInput(user, unionId, unitId, recordId); + return Result.success("编辑成功"); + } catch (Exception e) { + return Result.error(e.getMessage()); + } + } + + @At + @POST + @Aop(TransAop.READ_COMMITTED) + @SaCheckPermission("member.info.input") + @SLog(tag = "人员补录", msg = "删除会员") public Result doDelete(String id, String userId) { try { memberInfoService.dao().delete(MemberChangeRecord.class, id); Sys_role memberRole = memberInfoService.dao().fetch(Sys_role.class, Cnd.where("code", "=", RoleConstant.MEMBER.name())); Sys_role publicRole = memberInfoService.dao().fetch(Sys_role.class, Cnd.where("code", "=", RoleConstant.PUBLIC.name())); - memberInfoService.dao().execute(Sqls.create("delete from sys_user_role where userId = \'"+userId+"\' and roleId = \'"+publicRole.getId()+"\'")); - memberInfoService.dao().execute(Sqls.create("delete from sys_user_role where userId = \'"+userId+"\' and roleId = \'"+memberRole.getId()+"\'")); + memberInfoService.dao().execute(Sqls.create("delete from sys_user_role where userId = '" + userId + "' and roleId = '" + publicRole.getId() + "'")); + memberInfoService.dao().execute(Sqls.create("delete from sys_user_role where userId = '" + userId + "' and roleId = '" + memberRole.getId() + "'")); memberInfoService.dao().delete(Sys_user.class, userId); return Result.success("删除成功"); } catch (Exception e) { return Result.error(e.getMessage()); } } - } diff --git a/src/main/java/com/budwk/app/zhgh/staffmanage/member/models/MemberChangeRecord.java b/src/main/java/com/budwk/app/zhgh/staffmanage/member/models/MemberChangeRecord.java index e04a749f..61a1d8bb 100644 --- a/src/main/java/com/budwk/app/zhgh/staffmanage/member/models/MemberChangeRecord.java +++ b/src/main/java/com/budwk/app/zhgh/staffmanage/member/models/MemberChangeRecord.java @@ -237,5 +237,10 @@ public class MemberChangeRecord extends BaseModel { @ColDefine(type = ColType.VARCHAR,width = 10) private String aidFundMemberUserType; + @Column + @Comment("入校时间") + @ColDefine(type = ColType.DATE) + private String arrivalAtSchoolDate; + private Boolean edit; } diff --git a/src/main/java/com/budwk/app/zhgh/staffmanage/member/service/MemberInfoService.java b/src/main/java/com/budwk/app/zhgh/staffmanage/member/service/MemberInfoService.java index dfe8da2f..ca5de4b7 100644 --- a/src/main/java/com/budwk/app/zhgh/staffmanage/member/service/MemberInfoService.java +++ b/src/main/java/com/budwk/app/zhgh/staffmanage/member/service/MemberInfoService.java @@ -40,6 +40,23 @@ public interface MemberInfoService extends BaseService { */ void addMember(Sys_user user, String unionId, String unitId); + /** + * 获取人员补录编辑详情 + * @param recordId 补录记录ID + * @param userId 用户ID + * @return 编辑详情 + */ + NutMap getMemberInputDetail(String recordId, String userId); + + /** + * 更新人员补录信息 + * @param user 用户信息 + * @param unionId 工会ID + * @param unitId 单位ID + * @param recordId 补录记录ID + */ + void updateMemberInput(Sys_user user, String unionId, String unitId, String recordId); + /** * 获取会员历史 * @param pageForm diff --git a/src/main/java/com/budwk/app/zhgh/staffmanage/member/service/impl/MemberInfoServiceImpl.java b/src/main/java/com/budwk/app/zhgh/staffmanage/member/service/impl/MemberInfoServiceImpl.java index 3db77b6f..e1617358 100644 --- a/src/main/java/com/budwk/app/zhgh/staffmanage/member/service/impl/MemberInfoServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/staffmanage/member/service/impl/MemberInfoServiceImpl.java @@ -194,6 +194,7 @@ public class MemberInfoServiceImpl extends BaseServiceImpl implements user.setCreateAt(System.currentTimeMillis()); user.setLoginCount(0); user.setDisabled(false); + user.setMember(true); if (user.getMember() == null) { user.setMember(true); @@ -268,6 +269,99 @@ public class MemberInfoServiceImpl extends BaseServiceImpl implements } + @Override + public NutMap getMemberInputDetail(String recordId, String userId) { + MemberChangeRecord record = dao().fetch(MemberChangeRecord.class, recordId); + if (ObjectUtil.isEmpty(record)) { + throw new BaseException("补录记录不存在"); + } + Sys_user user = dao().fetch(Sys_user.class, userId); + if (ObjectUtil.isEmpty(user)) { + throw new BaseException("人员信息不存在"); + } + NutMap detail = NutMap.NEW(); + detail.putAll(BeanUtil.beanToMap(user)); + // 编辑页复用新增表单,需要把补录记录中的单位/工会及记录ID一起回填。 + detail.put("recordId", record.getId()); + detail.put("unitId", record.getUnitId()); + detail.put("unitName", record.getUnitName()); + detail.put("unionId", record.getUnionId()); + detail.put("unionName", record.getUnionName()); + return detail; + } + + @Override + @Aop(TransAop.READ_COMMITTED) + public void updateMemberInput(Sys_user user, String unionId, String unitId, String recordId) { + if (StrUtil.isBlank(user.getId())) { + throw new BaseException("用户ID不能为空"); + } + if (StrUtil.isBlank(recordId)) { + throw new BaseException("补录记录ID不能为空"); + } + if (StrUtil.isBlank(user.getLoginname())) { + throw new BaseException("工号不能为空"); + } + if (StrUtil.isBlank(user.getUsername())) { + throw new BaseException("姓名不能为空"); + } + Sys_user existUser = dao().fetch(Sys_user.class, Cnd.where("loginname", "=", user.getLoginname()).and("id", "!=", user.getId())); + if (ObjectUtil.isNotEmpty(existUser)) { + throw new BaseException("该工号已存在"); + } + Sys_user oldUser = dao().fetch(Sys_user.class, user.getId()); + if (ObjectUtil.isEmpty(oldUser)) { + throw new BaseException("人员信息不存在"); + } + user.setCreateAt(oldUser.getCreateAt()); + user.setLoginCount(oldUser.getLoginCount()); + user.setDisabled(false); + user.setMember(true); + if (user.getMember() == null) { + user.setMember(oldUser.getMember()); + } + if (user.getWelfareMember() == null) { + user.setWelfareMember(oldUser.getWelfareMember()); + } + dao().updateIgnoreNull(user); + + MemberChangeRecord record = dao().fetch(MemberChangeRecord.class, recordId); + if (ObjectUtil.isEmpty(record)) { + throw new BaseException("补录记录不存在"); + } + Sys_unit unit = dao().fetch(Sys_unit.class, Cnd.where("id", "=", unitId)); + Sys_union union = dao().fetch(Sys_union.class, Cnd.where("id", "=", unionId)); + // 补录列表展示来源于变更记录,编辑时同步更新记录内容,保证列表和详情一致。 + record.setLoginname(user.getLoginname()); + record.setUsername(user.getUsername()); + record.setUnitId(unitId); + record.setUnitName(ObjectUtil.isNotEmpty(unit) ? unit.getName() : ""); + record.setUnionId(unionId); + record.setUnionName(ObjectUtil.isNotEmpty(union) ? union.getName() : ""); + record.setSex(user.getSex()); + record.setBirthday(user.getBirthday()); + record.setIdCard(user.getIdCard()); + record.setNation(user.getNation()); + record.setPolitical(user.getPolitical()); + record.setEducation(user.getEducation()); + record.setAcademicDegree(user.getAcademicDegree()); + record.setPosition(user.getProfessionalTitle()); + record.setUserState(user.getUserState()); + record.setPreparedBy(user.getPreparedBy()); + record.setPersonType(user.getPersonType()); + record.setMobile(user.getMobile()); + record.setEmail(user.getEmail()); + record.setMember(user.getMember()); + record.setWelfareMember(user.getWelfareMember()); + record.setRetireDate(user.getRetireDate()); + record.setFamilies(user.getFamilies()); + record.setPersonalData(user.getPersonalData()); + record.setUserAttribute(user.getUserAttribute()); + record.setCampus(user.getCampus()); + record.setProfessionalTitle(user.getProfessionalTitle()); + record.setAidFundMemberUserType(user.getAidFundMemberUserType()); + dao().updateIgnoreNull(record); + } @Override public Sql getMemberHistorySql(MemberInfoPageForm pageForm) { diff --git a/src/main/java/com/budwk/app/zhgh/staffmanage/member/service/impl/MemberManageServiceImpl.java b/src/main/java/com/budwk/app/zhgh/staffmanage/member/service/impl/MemberManageServiceImpl.java index 302f4dbc..b83a7b43 100644 --- a/src/main/java/com/budwk/app/zhgh/staffmanage/member/service/impl/MemberManageServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/staffmanage/member/service/impl/MemberManageServiceImpl.java @@ -95,6 +95,7 @@ public class MemberManageServiceImpl extends BaseServiceImpl implement u.isModelWorker, u.userAttribute, u.aidFundMemberUserType, + u.arrivalAtSchoolDate, his.id hisId, his.changeTypes, his.changeTime @@ -220,10 +221,12 @@ public class MemberManageServiceImpl extends BaseServiceImpl implement allowChangeFieldNames.add("member"); allowChangeFieldNames.add("aidFundMemberUserType"); allowChangeFieldNames.add("userAttribute"); + allowChangeFieldNames.add("arrivalAtSchoolDate"); Map dictMap = dictList.stream().collect(Collectors.toMap(Sys_dict::getCode, Sys_dict::getName)); dictMap.put("member", "会员状态"); dictMap.put("aidFundMemberUserType", "人员分类"); dictMap.put("userAttribute", "人员属性"); + dictMap.put("arrivalAtSchoolDate", "入校时间"); return NutMap.NEW().addv("allowChangeFieldNames", allowChangeFieldNames).addv("dictMap", dictMap); } diff --git a/src/main/resources/static/components/plugins/sysUpload/index.vue b/src/main/resources/static/components/plugins/sysUpload/index.vue index e00ea1ea..a48773ac 100644 --- a/src/main/resources/static/components/plugins/sysUpload/index.vue +++ b/src/main/resources/static/components/plugins/sysUpload/index.vue @@ -8,6 +8,7 @@ :on-success="handleSuccess" :on-preview="handlePreview" :on-exceed="handleExceed" + :on-remove="handleRemove" :before-upload="beforeUpload" :limit="upload_number" :accept="fileAccept" diff --git a/src/main/resources/views/platform/sys/unit/index.html b/src/main/resources/views/platform/sys/unit/index.html index a186709e..22d5377d 100644 --- a/src/main/resources/views/platform/sys/unit/index.html +++ b/src/main/resources/views/platform/sys/unit/index.html @@ -82,6 +82,7 @@ layout("/layouts/platform.html"){ :expand-on-click-node="false" :props="{ children: 'children', label: 'name' }" node-key="id" + :default-expanded-keys="defaultExpandedKeys" @node-click="treeNodeClick" :filter-node-method="filterNode" highlight-current @@ -156,6 +157,7 @@ layout("/layouts/platform.html"){ filterText: null, currentTreeNode: null, currentTreeData: null, + defaultExpandedKeys: [], treeData: [] } }, @@ -175,6 +177,7 @@ layout("/layouts/platform.html"){ this.treeData = res.data if (this.treeData) { this.currentTreeData = this.treeData[0] + this.defaultExpandedKeys = this.treeData.length > 0 ? [this.treeData[0].id] : [] } }) }, diff --git a/src/main/resources/views/platform/zhgh/activity/culture/common/applyActivity.js b/src/main/resources/views/platform/zhgh/activity/culture/common/applyActivity.js index 855b1a6c..a75d39e6 100644 --- a/src/main/resources/views/platform/zhgh/activity/culture/common/applyActivity.js +++ b/src/main/resources/views/platform/zhgh/activity/culture/common/applyActivity.js @@ -456,6 +456,8 @@ const ACTIVITY_CULTURE_APPLY_ACTIVITY = { complete_result> + + 编辑 - 撤回 diff --git a/src/main/resources/views/platform/zhgh/activity/trainSignUp/type/index.html b/src/main/resources/views/platform/zhgh/activity/trainSignUp/type/index.html index b57eb1a3..b0038e99 100644 --- a/src/main/resources/views/platform/zhgh/activity/trainSignUp/type/index.html +++ b/src/main/resources/views/platform/zhgh/activity/trainSignUp/type/index.html @@ -80,7 +80,7 @@ layout("/layouts/platform.html"){ }) }, openAnnualLimit() { - window.location.href = '/platform/trainSignUp/annualLimit' + commonUtil.pjaxPush('/platform/trainSignUp/annualLimit') }, openAdd() { this.$refs.guava.edit(() => { diff --git a/src/main/resources/views/platform/zhgh/club/examine/apply/index.html b/src/main/resources/views/platform/zhgh/club/examine/apply/index.html index 4dde0117..ed4547fa 100644 --- a/src/main/resources/views/platform/zhgh/club/examine/apply/index.html +++ b/src/main/resources/views/platform/zhgh/club/examine/apply/index.html @@ -571,6 +571,11 @@ layout("/layouts/platform.html"){ {{formatNumber(row.support)}} + + +