From 3207e0a5944124a62dc8ed318aff6a0edaa4f0ac Mon Sep 17 00:00:00 2001 From: = <=> Date: Fri, 24 Apr 2026 14:27:55 +0800 Subject: [PATCH] commit --- ...ctivityCultureApplyActivityController.java | 4 +- ...ctivityCultureAuditActivityController.java | 4 +- .../ActivityCultureInfoManageController.java | 12 +- .../service/ActivityCultureService.java | 10 + .../impl/ActivityCultureServiceImpl.java | 37 + ...tivityWorksUploadCollectionController.java | 5 +- .../apply/ClubUserJoinApplyController.java | 27 +- .../ClubChangeManagerController.java | 20 +- .../ClubRefreshReportController.java | 10 +- .../infoManage/ClubRuleUpdateController.java | 10 +- .../app/zhgh/club/model/ClubUserApply.java | 10 + .../club/model/SysClubExamineRegister.java | 2 +- .../club/service/ClubUserJoinService.java | 21 + .../service/SysClubInfoManageService.java | 5 + .../service/impl/ClubUserJoinServiceImpl.java | 84 ++- .../impl/SysClubInfoManageServiceImpl.java | 109 ++- .../zhgh/club/vo/ClubUserCommonPageVo.java | 3 + .../OutlayManageClubQueryController.java | 4 +- .../club/model/OutlayManageClub.java | 2 +- .../UnionReimburseApplyController.java | 104 +-- ...nReimburseCertifierUserSignController.java | 27 +- .../UnionReimburseCollectController.java | 47 +- .../UnionReimburseMineController.java | 35 +- .../UnionReimburseReviewController.java | 39 +- .../UnionReimburseStatisticsController.java | 8 + .../unionReimburse/model/UnionReimburse.java | 5 + .../service/UnionReimburseService.java | 30 + .../impl/UnionReimburseServiceImpl.java | 652 +++++++++++++++++- ...TeacherCongressDelegatePushController.java | 12 +- .../activity/culture/common/applyActivity.js | 2 + .../zhgh/activity/culture/common/infoMange.js | 2 +- .../zhgh/activity/trainSignUp/type/index.html | 2 +- .../zhgh/club/examine/apply/index.html | 41 +- .../zhgh/club/examine/common/examineInfo.js | 17 + .../club/infoManage/manage/clubInfoManage.js | 1 + .../register/apply/clubRegisterSponsor.js | 7 +- .../zhgh/club/register/apply/index.html | 25 +- .../outlay/outlayManage/club/query/index.html | 14 +- .../unionReimburse/apply/index.html | 611 +++++++++++++++- .../certifierUserSign/index.html | 27 +- .../unionReimburse/collect/index.html | 59 +- .../unionReimburse/mine/index.html | 27 +- .../unionReimburse/review/index.html | 53 +- .../unionReimburse/statistics/index.html | 23 +- .../teachercongress/delegate/push/write.js | 6 +- .../unionReimburse/review/index.html | 63 +- 46 files changed, 1993 insertions(+), 325 deletions(-) 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/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/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/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/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/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/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)}} + + +