diff --git a/src/main/java/com/budwk/app/zhgh/staffmanage/member/controller/apply/MemberApplyCommonController.java b/src/main/java/com/budwk/app/zhgh/staffmanage/member/controller/apply/MemberApplyCommonController.java index a9c8b46..d7d9934 100644 --- a/src/main/java/com/budwk/app/zhgh/staffmanage/member/controller/apply/MemberApplyCommonController.java +++ b/src/main/java/com/budwk/app/zhgh/staffmanage/member/controller/apply/MemberApplyCommonController.java @@ -1,6 +1,8 @@ package com.budwk.app.zhgh.staffmanage.member.controller.apply; import cn.dev33.satoken.annotation.SaCheckLogin; +import cn.dev33.satoken.annotation.SaCheckPermission; +import cn.dev33.satoken.annotation.SaMode; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import com.budwk.app.base.constant.RoleConstant; @@ -24,6 +26,49 @@ public class MemberApplyCommonController { @Inject private MemberApplyRecordService memberApplyRecordService; + /** + * PC、手机端按申请记录查看当次填写的资料。 + * + * @param recordId member_apply_record 的记录 ID,不是会员 userId 或流程任务 ID + * @return Result,data 为申请快照,包含申请人资料及 applyDateTime 申请时间 + */ + @At + @SaCheckLogin + @SaCheckPermission(value = {"member.apply.mine", "member.apply.branchUnionAudit", "member.apply.schoolUnionAudit", + "h5.member.apply.mine", "h5.member.apply.branch", "h5.member.apply.school"}, mode = SaMode.OR) + public Result findApplyRecord(@Valid String recordId) { + return Result.success(memberApplyRecordService.findApplyRecordForView(recordId)); + } + + /** + * 查询当前申请的提交和审核历史。 + * + * @param recordId 申请记录 ID,不是会员 ID + * @return Result.data 为记录列表,每项含 nodeCode、nodeName、operatorName、operatorLoginName、 + * handledAt、result、originalResult、opinion、signature;撤回后的原审核仍保留办理内容 + */ + @At + @SaCheckLogin + @SaCheckPermission(value = {"member.apply.mine", "member.apply.branchUnionAudit", "member.apply.schoolUnionAudit", + "h5.member.apply.mine", "h5.member.apply.branch", "h5.member.apply.school"}, mode = SaMode.OR) + public Result approvalRecords(@Valid String recordId) { + return Result.success(memberApplyRecordService.listApplyApprovalRecords(recordId)); + } + + /** + * 为两端入会查看页面提供同一份资料变更信息。 + * + * @param recordId 申请记录 ID + * @return Result.data 为变更项列表,name 为字段标题,value 为原值,newValue 为申请值 + */ + @At + @SaCheckLogin + @SaCheckPermission(value = {"member.apply.mine", "member.apply.branchUnionAudit", "member.apply.schoolUnionAudit", + "h5.member.apply.mine", "h5.member.apply.branch", "h5.member.apply.school"}, mode = SaMode.OR) + public Result findApplyChanges(@Valid String recordId) { + return Result.success(memberApplyRecordService.findApplyChangesForView(recordId)); + } + @At @SaCheckLogin public Result findOne(@Valid String id) { diff --git a/src/main/java/com/budwk/app/zhgh/staffmanage/member/controller/apply/MemberApplyController.java b/src/main/java/com/budwk/app/zhgh/staffmanage/member/controller/apply/MemberApplyController.java index 3879db9..b65b44d 100644 --- a/src/main/java/com/budwk/app/zhgh/staffmanage/member/controller/apply/MemberApplyController.java +++ b/src/main/java/com/budwk/app/zhgh/staffmanage/member/controller/apply/MemberApplyController.java @@ -58,6 +58,17 @@ public class MemberApplyController { public void index() { } + /** + * 查询当前用户能否新建入会申请,供 PC 入口和 H5 申请页共用。 + * + * @return Result.data 为 Boolean;true 表示可新建,false 表示已有进行中的申请 + */ + @At + @SaCheckPermission(value = {"member.apply.submit", "h5.member.apply.submit"}, mode = SaMode.OR) + public Result canStartNewApply() { + return Result.success(memberApplyRecordService.canStartNewApply()); + } + @At @SaCheckPermission(value = {"member.apply.submit","member.branchUnion.record","member.schoolUnion.record"}, mode = SaMode.OR) @@ -77,6 +88,7 @@ public class MemberApplyController { @SaCheckPermission("member.apply.submit") @SLog(tag = "会员管理-入会申请-保存申请", msg = "保存申请") public Result doSave(MemberApplyRecord record){ + memberApplyRecordService.checkCanSaveOrSubmit(record.getId()); record.setMember(true); record.setUserId(SecurityUtil.getUserId()); record.setChangeOrigin(MemberChangeOrigin.PERSONAL.name()); @@ -98,6 +110,7 @@ public class MemberApplyController { @SaCheckPermission("member.apply.submit") @SLog(tag = "会员管理-入会申请-提交申请", msg = "提交申请") public Object doSubmit(MemberApplyRecord record) { + memberApplyRecordService.checkCanSaveOrSubmit(record.getId()); record.setMember(true); record.setUserId(SecurityUtil.getUserId()); record.setChangeType(MemberChangeType.RESTORE.name()); diff --git a/src/main/java/com/budwk/app/zhgh/staffmanage/member/controller/apply/MemberApplyMineController.java b/src/main/java/com/budwk/app/zhgh/staffmanage/member/controller/apply/MemberApplyMineController.java index 1155f12..cde3c90 100644 --- a/src/main/java/com/budwk/app/zhgh/staffmanage/member/controller/apply/MemberApplyMineController.java +++ b/src/main/java/com/budwk/app/zhgh/staffmanage/member/controller/apply/MemberApplyMineController.java @@ -1,6 +1,7 @@ package com.budwk.app.zhgh.staffmanage.member.controller.apply; import cn.dev33.satoken.annotation.SaCheckPermission; +import cn.dev33.satoken.annotation.SaMode; import cn.hutool.core.util.StrUtil; import com.budwk.app.base.annotation.SLog; import com.budwk.app.base.constant.BpmProcessConstant; @@ -51,7 +52,7 @@ public class MemberApplyMineController { @At - @SaCheckPermission("member.apply.mine") + @SaCheckPermission(value = {"member.apply.mine", "h5.member.apply.mine"}, mode = SaMode.OR) public Result pageData(MemberApplyPageForm pageForm) { Sql sql = Sqls.create(""" SELECT @@ -101,12 +102,9 @@ public class MemberApplyMineController { MemberApplyPageForm.buildSearch(cnd, pageForm); cnd.and("nd.nodeCode", "=", 10); cnd.and("define.`code`", "=", BpmProcessConstant.MEMBER_SINGLE_APPLY.name()); - if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name())) { - if (AuthUtil.hasRole(RoleConstant.BRANCH_UNION_CHAIRMAN.name())) { - cnd.andEX("vu.unionId","=", SecurityUtil.getUnionId()); - } else { - cnd.and("vu.id", "=", SecurityUtil.getUserId()); - } + // “我的申请”仅系统管理员可查看全部,其他角色固定按申请记录归属查询。 + if (!AuthUtil.hasRole(RoleConstant.SYSADMIN.name())) { + cnd.and("record.userId", "=", SecurityUtil.getUserId()); } cnd.desc("record.applyDateTime"); sql.setCondition(cnd); @@ -117,9 +115,10 @@ public class MemberApplyMineController { @At @ApiOperation("删除会员申请记录") @Aop(TransAop.READ_COMMITTED) - @SaCheckPermission("member.apply.mine") + @SaCheckPermission(value = {"member.apply.mine", "h5.member.apply.mine"}, mode = SaMode.OR) @SLog(tag = "会员管理-入会申请-我的申请记录", msg = "删除申请") public Result delete(@Valid String id) { + memberApplyRecordService.checkMineRecordAccess(id); memberApplyRecordService.delete(id); bpmService.deleteInstance(id); return Result.success(); @@ -129,9 +128,10 @@ public class MemberApplyMineController { @At @ApiOperation("个人申请撤回") @Aop(TransAop.READ_COMMITTED) - @SaCheckPermission("member.apply.mine") + @SaCheckPermission(value = {"member.apply.mine", "h5.member.apply.mine"}, mode = SaMode.OR) @SLog(tag = "会员管理-入会申请-个人申请撤回", msg = "个人申请撤回") public Result revokeApply(@Valid String id) { + memberApplyRecordService.checkMineRecordAccess(id); bpmService.revokeApply(id); return Result.success(); } diff --git a/src/main/java/com/budwk/app/zhgh/staffmanage/member/controller/apply/MemberApplySchoolUnionAuditController.java b/src/main/java/com/budwk/app/zhgh/staffmanage/member/controller/apply/MemberApplySchoolUnionAuditController.java index 4915edb..942e501 100644 --- a/src/main/java/com/budwk/app/zhgh/staffmanage/member/controller/apply/MemberApplySchoolUnionAuditController.java +++ b/src/main/java/com/budwk/app/zhgh/staffmanage/member/controller/apply/MemberApplySchoolUnionAuditController.java @@ -1,7 +1,7 @@ package com.budwk.app.zhgh.staffmanage.member.controller.apply; import cn.dev33.satoken.annotation.SaCheckPermission; -import cn.hutool.core.bean.BeanUtil; +import cn.dev33.satoken.annotation.SaMode; import cn.hutool.core.util.ObjectUtil; import com.budwk.app.base.annotation.SLog; import com.budwk.app.base.constant.BpmProcessConstant; @@ -9,7 +9,6 @@ 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.bpm.enums.BpmProcessTaskStatusEnum; -import com.budwk.app.bpm.enums.BpmTaskApprovalTypeEnum; import com.budwk.app.bpm.param.BpmTaskApprovalParam; import com.budwk.app.bpm.service.BpmService; import com.budwk.app.sys.models.Sys_user_history; @@ -64,7 +63,7 @@ public class MemberApplySchoolUnionAuditController { } @At - @SaCheckPermission("member.apply.schoolUnionAudit") + @SaCheckPermission(value = {"member.apply.schoolUnionAudit", "h5.member.apply.school"}, mode = SaMode.OR) public Result pageData(MemberApplyPageForm pageForm) { Sql sql = Sqls.create(""" SELECT @@ -123,17 +122,19 @@ public class MemberApplySchoolUnionAuditController { } + /** + * 校工会审核入会申请,PC、H5 共用同一保存入口。 + * + * @param approvalParam approval JSON 中的实例、申请、任务 ID,审核类型、意见及签名图片 URL + * @return Result,code 为 0 表示审核及签名保存成功,msg 为结果提示 + */ @At @Aop(TransAop.READ_COMMITTED) @ApiOperation("会员入会校工会审核") - @SaCheckPermission("member.apply.branchUnionAudit") + @SaCheckPermission(value = {"member.apply.schoolUnionAudit", "h5.member.apply.school"}, mode = SaMode.OR) @SLog(tag = "会员管理-会员入会申请-校工会审核", msg = "会员入会审核(校工会)") public Result approval(@Param("approval") @Valid BpmTaskApprovalParam approvalParam) { - if (approvalParam.getBpmTaskApprovalTypeEnum().equals(BpmTaskApprovalTypeEnum.PASS)) { - MemberApplyRecord record = memberManageService.dao().fetch(MemberApplyRecord.class, Cnd.where("id", "=", approvalParam.getProcessInstanceBusinessId())); - memberManageService.compareMemberChangeInfoAndUpdateUser(record); - } - bpmService.completeTask(approvalParam.getProcessInstanceTaskId(), approvalParam.getBpmTaskApprovalTypeEnum(), null, null); + memberApplyRecordService.approveSchoolApply(approvalParam); return Result.success(); } @@ -141,7 +142,7 @@ public class MemberApplySchoolUnionAuditController { @At @ApiOperation("校工会审核撤回") @Aop(TransAop.READ_COMMITTED) - @SaCheckPermission("member.apply.branchUnionAudit") + @SaCheckPermission("member.apply.schoolUnionAudit") @SLog(tag = "会员管理-入会申请-校工会撤回", msg = "校工会入会审核撤回") public Result revoke(@Valid String taskId) { bpmService.revokeTask(taskId); @@ -155,7 +156,7 @@ public class MemberApplySchoolUnionAuditController { * @return */ @At - @SaCheckPermission(value = "member.apply.branchUnionAudit") + @SaCheckPermission(value = "member.apply.schoolUnionAudit") public Result findAuditChangeInfo(@Valid String recordId){ Sys_user_history history = memberApplyRecordService.dao().fetch(Sys_user_history.class, Cnd.where("recordId", "=", recordId).or("id","=",recordId)); MemberApplyRecord record = memberApplyRecordService.dao().fetch(MemberApplyRecord.class, recordId); diff --git a/src/main/java/com/budwk/app/zhgh/staffmanage/member/service/MemberApplyRecordService.java b/src/main/java/com/budwk/app/zhgh/staffmanage/member/service/MemberApplyRecordService.java index 7b81676..2d086ad 100644 --- a/src/main/java/com/budwk/app/zhgh/staffmanage/member/service/MemberApplyRecordService.java +++ b/src/main/java/com/budwk/app/zhgh/staffmanage/member/service/MemberApplyRecordService.java @@ -1,13 +1,71 @@ package com.budwk.app.zhgh.staffmanage.member.service; import com.budwk.app.base.service.BaseService; +import com.budwk.app.bpm.param.BpmTaskApprovalParam; import com.budwk.app.zhgh.staffmanage.member.models.MemberApplyRecord; +import org.nutz.lang.util.NutMap; + +import java.util.List; /** * 会员申请记录 */ public interface MemberApplyRecordService extends BaseService { + /** + * 判断当前登录人是否可以新建入会申请。 + * + * @return 没有进行中的入会流程时返回 true,否则返回 false + */ + boolean canStartNewApply(); + + /** + * 校验当前登录人保存或提交的入会申请,已有进行中申请时仅允许操作该申请本身。 + * + * @param recordId 本次申请记录 ID;新建申请时为空,编辑已有申请时传该记录 ID + * @throws com.budwk.app.base.exception.BaseException 记录不属于当前用户或存在另一条进行中申请时抛出 + */ + void checkCanSaveOrSubmit(String recordId); + + /** + * 校验“我的申请”记录访问范围,只有系统管理员可操作他人的申请。 + * + * @param recordId member_apply_record 的记录 ID,不能为空 + * @throws com.budwk.app.base.exception.BaseException 记录不存在或非系统管理员访问他人记录时抛出 + */ + void checkMineRecordAccess(String recordId); + + /** + * 查询当前登录人有权查看的申请快照,不使用会员当前资料替代申请内容。 + * + * @param recordId member_apply_record 的申请记录 ID + * @return 该次申请保存的 MemberApplyRecord,包含申请人资料及 applyDateTime 申请时间 + */ + MemberApplyRecord findApplyRecordForView(String recordId); + + /** + * 查询同一次入会申请的提交、重新提交及审核任务,包含待办和撤回状态。 + * + * @param recordId 申请记录 ID,按申请归属校验访问权限 + * @return 每项包含节点、办理人、时间、结果、撤回前原结果、意见和签名图片 URL; + * 未办理的撤回任务不返回,无流程时返回空列表 + */ + List listApplyApprovalRecords(String recordId); + + /** + * 查询有权查看的申请所涉及的资料变更。 + * + * @param recordId 申请记录 ID + * @return 变更项列表,每项含字段名、原值和新值;已审核记录优先使用保存的变更历史 + */ + List findApplyChangesForView(String recordId); + + /** + * 校工会办理入会申请,同一事务内更新会员资料并保存审核意见、签名和结果。 + * + * @param approvalParam 当前实例、业务和任务标识,审核类型、意见及签名图片 URL + */ + void approveSchoolApply(BpmTaskApprovalParam approvalParam); void startProcess(MemberApplyRecord memberApplyRecord, Long processDefineId); } diff --git a/src/main/java/com/budwk/app/zhgh/staffmanage/member/service/impl/MemberApplyRecordServiceImpl.java b/src/main/java/com/budwk/app/zhgh/staffmanage/member/service/impl/MemberApplyRecordServiceImpl.java index 2119ac1..4197d89 100644 --- a/src/main/java/com/budwk/app/zhgh/staffmanage/member/service/impl/MemberApplyRecordServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/staffmanage/member/service/impl/MemberApplyRecordServiceImpl.java @@ -1,15 +1,44 @@ package com.budwk.app.zhgh.staffmanage.member.service.impl; +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.date.DateUtil; import cn.hutool.core.lang.Dict; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONObject; +import com.budwk.app.base.constant.BpmProcessConstant; +import com.budwk.app.base.constant.RoleConstant; +import com.budwk.app.base.exception.BaseException; import com.budwk.app.base.service.impl.BaseServiceImpl; +import com.budwk.app.bpm.enums.BpmProcessInstanceStatusEnum; +import com.budwk.app.bpm.enums.BpmProcessNodeTypeEnum; +import com.budwk.app.bpm.enums.BpmProcessTaskStatusEnum; +import com.budwk.app.bpm.enums.BpmTaskApprovalTypeEnum; +import com.budwk.app.bpm.models.BpmProcessDefine; +import com.budwk.app.bpm.models.BpmProcessInstance; +import com.budwk.app.bpm.models.BpmProcessNodeDefine; +import com.budwk.app.bpm.models.BpmProcessTask; +import com.budwk.app.bpm.param.BpmTaskApprovalParam; +import com.budwk.app.bpm.service.BpmService; +import com.budwk.app.sys.models.Sys_user_history; +import com.budwk.app.web.commons.auth.utils.AuthUtil; import com.budwk.app.web.commons.auth.utils.SecurityUtil; import com.budwk.app.wf2.enums.FlowConst; import com.budwk.app.wf2.service.ProcessInstanceService; import com.budwk.app.zhgh.staffmanage.member.models.MemberApplyRecord; import com.budwk.app.zhgh.staffmanage.member.service.MemberApplyRecordService; +import com.budwk.app.zhgh.staffmanage.member.service.MemberManageService; +import org.nutz.aop.interceptor.ioc.TransAop; +import org.nutz.dao.Cnd; import org.nutz.dao.Dao; +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 java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; @IocBean(args = {"refer:dao"}) public class MemberApplyRecordServiceImpl extends BaseServiceImpl implements MemberApplyRecordService { @@ -20,6 +49,237 @@ public class MemberApplyRecordServiceImpl extends BaseServiceImpl recordIds = query(Cnd.where("userId", "=", SecurityUtil.getUserId()) + .and("delFlag", "=", false)).stream() + .map(MemberApplyRecord::getId) + .filter(id -> !StrUtil.equals(id, excludedRecordId)) + .toList(); + if (recordIds.isEmpty()) { + return false; + } + BpmProcessDefine define = dao().fetch(BpmProcessDefine.class, + Cnd.where("code", "=", BpmProcessConstant.MEMBER_SINGLE_APPLY.name())); + if (define == null) { + throw new BaseException("入会申请流程定义不存在"); + } + return dao().count(BpmProcessInstance.class, + Cnd.where("processDefineId", "=", define.getId()) + .and("processInstanceBusinessId", "in", recordIds) + .and("delFlag", "=", false) + .and("processInstanceStatus", "=", BpmProcessInstanceStatusEnum.RUNNING.name())) > 0; + } + + /** + * 按申请记录归属校验查看范围,保证会员资料后来发生变化时仍展示原申请内容。 + * + * @param recordId 申请记录 ID,不能为空 + * @return 申请资料及申请时间;记录不存在或无权查看时抛出业务异常 + */ + @Override + public MemberApplyRecord findApplyRecordForView(String recordId) { + if (StrUtil.isBlank(recordId)) { + throw new BaseException("申请记录不能为空"); + } + MemberApplyRecord record = fetch(recordId); + if (record == null) { + throw new BaseException("申请记录不存在"); + } + + // 校级管理员可查看全校申请,申请人可查看本人保存的申请。 + if (AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), + RoleConstant.SCHOOL_UNION_MEMBER_ADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name()) + || StrUtil.equals(SecurityUtil.getUserId(), record.getUserId())) { + return record; + } + + // 分工会主席仅能查看申请所属分工会的记录,空工会 ID 不作为授权依据。 + String unionId = SecurityUtil.getUnionId(); + if (AuthUtil.hasRole(RoleConstant.BRANCH_UNION_CHAIRMAN.name()) + && StrUtil.isNotBlank(unionId) && StrUtil.equals(unionId, record.getUnionId())) { + return record; + } + throw new BaseException("没有权限查看该申请记录"); + } + + @Override + public List listApplyApprovalRecords(String recordId) { + // 先校验申请访问范围,不能仅凭客户端传入的业务 ID 查询他人的审核历史。 + findApplyRecordForView(recordId); + List instances = dao().query(BpmProcessInstance.class, + Cnd.where("processInstanceBusinessId", "=", recordId).and("delFlag", "=", false)); + if (instances.isEmpty()) { + return new ArrayList<>(); + } + List instanceIds = instances.stream().map(BpmProcessInstance::getId).toList(); + List tasks = dao().query(BpmProcessTask.class, + Cnd.where("processInstanceId", "in", instanceIds).and("delFlag", "=", false) + .asc("createdOn").asc("createdAt").asc("id")); + if (tasks.isEmpty()) { + return new ArrayList<>(); + } + + // START、BACK 节点保留申请及重新提交记录,不能像通用审核查询那样只读取 TASK 节点。 + List nodeIds = tasks.stream().map(BpmProcessTask::getProcessTaskNodeId).distinct().toList(); + Set applyNodeIds = dao().query(BpmProcessNodeDefine.class, Cnd.where("id", "in", nodeIds)).stream() + .filter(node -> BpmProcessNodeTypeEnum.START.name().equals(node.getNodeType()) + || BpmProcessNodeTypeEnum.BACK.name().equals(node.getNodeType())) + .map(BpmProcessNodeDefine::getId).collect(Collectors.toSet()); + List records = new ArrayList<>(); + for (BpmProcessTask task : tasks) { + boolean complete = BpmProcessTaskStatusEnum.COMPLETE.name().equals(task.getTaskStatus()) + || BpmProcessTaskStatusEnum.AUTO_COMPLETE.name().equals(task.getTaskStatus()) + || BpmProcessTaskStatusEnum.TRANSFER.name().equals(task.getTaskStatus()); + JSONObject savedVariable = task.getExtVariable() == null ? new JSONObject() : task.getExtVariable(); + String approvalType = StrUtil.blankToDefault(savedVariable.getStr("bpmTaskApprovalType"), ""); + // 撤回会把原已办任务恢复为 ACTIVE,但其完成时间和审核变量仍在;这些字段属于原审核记录。 + boolean withdrawnApproval = !complete && task.getEndOn() != null + && List.of("PASS", "REJECT", "BACK", "BACK_OTHER_NODE").contains(approvalType) + && !applyNodeIds.contains(task.getProcessTaskNodeId()); + // 后续自动生成的 REVOKE 任务未经审核,不应显示为一条只有“已撤回”的审核记录。 + if (BpmProcessTaskStatusEnum.REVOKE.name().equals(task.getTaskStatus()) && !withdrawnApproval) { + continue; + } + boolean handled = complete || withdrawnApproval; + JSONObject variable = handled ? savedVariable : new JSONObject(); + String originalResult = switch (approvalType) { + case "PASS" -> "同意"; + case "REJECT" -> "建议撤销"; + case "BACK", "BACK_OTHER_NODE" -> "退回"; + default -> null; + }; + String result = switch (StrUtil.blankToDefault(task.getTaskStatus(), "")) { + case "ACTIVE" -> withdrawnApproval ? "已撤回" : "待处理"; + case "REVOKE" -> "已撤回"; + case "TRANSFER" -> "已转办"; + case "AUTO_COMPLETE" -> "自动完成"; + case "COMPLETE" -> switch (approvalType) { + case "PASS" -> "同意"; + case "REJECT" -> "建议撤销"; + case "BACK", "BACK_OTHER_NODE" -> "退回"; + case "APPLY" -> "已提交"; + default -> applyNodeIds.contains(task.getProcessTaskNodeId()) ? "已提交" : "已办理"; + }; + default -> "暂无"; + }; + records.add(NutMap.NEW() + .addv("id", task.getId()).addv("nodeCode", task.getProcessTaskNodeCode()) + .addv("nodeName", task.getProcessTaskNodeName()) + .addv("operatorName", handled ? task.getActualOwnerUserName() : null) + .addv("operatorLoginName", handled ? task.getActualOwnerLoginName() : null) + .addv("handledAt", handled && task.getEndOn() != null ? DateUtil.formatDateTime(task.getEndOn()) : null) + .addv("result", result) + .addv("originalResult", withdrawnApproval ? originalResult : null) + .addv("opinion", variable.getStr("approvalOpinion")) + .addv("signature", variable.getStr("approvalSignature"))); + } + return records; + } + + @Override + public List findApplyChangesForView(String recordId) { + MemberApplyRecord record = findApplyRecordForView(recordId); + // 审核后会员当前资料已经更新,应优先使用当时保存的变更信息。 + Sys_user_history history = dao().fetch(Sys_user_history.class, + Cnd.where("recordId", "=", recordId).or("id", "=", recordId)); + return history == null ? memberManageService.compareMemberChangeInfo(record) + : memberManageService.compareMemberChangeInfo(history); + } + + @Override + @Aop(TransAop.READ_COMMITTED) + public void approveSchoolApply(BpmTaskApprovalParam approvalParam) { + BpmTaskApprovalTypeEnum approvalType = approvalParam.getBpmTaskApprovalTypeEnum(); + if (!List.of(BpmTaskApprovalTypeEnum.PASS, BpmTaskApprovalTypeEnum.BACK, BpmTaskApprovalTypeEnum.REJECT).contains(approvalType)) { + throw new BaseException("校工会审核类型不正确"); + } + BpmProcessTask task = dao().fetch(BpmProcessTask.class, approvalParam.getProcessInstanceTaskId()); + if (task == null || Boolean.TRUE.equals(task.getDelFlag()) + || !BpmProcessTaskStatusEnum.ACTIVE.name().equals(task.getTaskStatus())) { + throw new BaseException("审核任务不存在或已处理,请刷新后重试"); + } + BpmProcessInstance instance = dao().fetch(BpmProcessInstance.class, task.getProcessInstanceId()); + // 任务、实例和申请必须属于同一次审核,避免用另一条申请的参数更新会员资料。 + if (instance == null || !StrUtil.equals(instance.getId(), approvalParam.getProcessInstanceId()) + || !StrUtil.equals(instance.getProcessInstanceBusinessId(), approvalParam.getProcessInstanceBusinessId()) + || !Integer.valueOf(50).equals(task.getProcessTaskNodeCode())) { + throw new BaseException("审核任务与申请不匹配"); + } + if (!AuthUtil.hasRole(RoleConstant.SYSADMIN.name()) + && (task.getAssignments() == null || !task.getAssignments().contains(SecurityUtil.getUserLoginname()))) { + throw new BaseException("当前用户不是该任务的审核人"); + } + MemberApplyRecord record = fetch(approvalParam.getProcessInstanceBusinessId()); + if (record == null) { + throw new BaseException("申请记录不存在"); + } + if (approvalType == BpmTaskApprovalTypeEnum.PASS) { + memberManageService.compareMemberChangeInfoAndUpdateUser(record); + } + // 完成任务时将意见、签名 URL、审核类型一并写入任务扩展变量,供 PC、手机端回显。 + bpmService.completeTask(task.getId(), approvalType, BeanUtil.beanToMap(approvalParam), null); + } + @Override public void startProcess(MemberApplyRecord record, Long processDefineId) { Dict dict = Dict.create(); diff --git a/src/main/resources/static/components/plugins/sysSignature/h5Index.vue b/src/main/resources/static/components/plugins/sysSignature/h5Index.vue index b63d1a1..4017a80 100644 --- a/src/main/resources/static/components/plugins/sysSignature/h5Index.vue +++ b/src/main/resources/static/components/plugins/sysSignature/h5Index.vue @@ -8,7 +8,9 @@ - + + 返回 @@ -42,7 +44,12 @@ module.exports = { }, methods: { openSignature() { - this.showSignaturePanel = true + // 签字弹框独立占一层历史;返回键先关闭签字板,再返回审核详情。 + h5PopupHistory.open(this._uid) + this.$set(this, "showSignaturePanel", true) + }, + closeSignature() { + h5PopupHistory.close(this._uid) }, save(signature) { @@ -61,8 +68,8 @@ module.exports = { .then((res) => { if (res.code === 0) { alert("保存成功") - this.showSignaturePanel = false this.$emit("input", res.data) + this.closeSignature() } else { alert("保存失败") } @@ -87,7 +94,14 @@ module.exports = { return new File([blob], filename, { type: contentType }) } }, - created() {} + mounted() { + h5PopupHistory.register(this._uid, () => { + this.$set(this, "showSignaturePanel", false) + }) + }, + beforeDestroy() { + h5PopupHistory.unregister(this._uid) + } } @@ -101,4 +115,11 @@ module.exports = { .main-panel .open-button { } + +.signature-close { + position: fixed; + top: 12px; + right: 12px; + z-index: 1; +} diff --git a/src/main/resources/static/components/plugins/sysSignature/index.vue b/src/main/resources/static/components/plugins/sysSignature/index.vue index 487ce2a..1e505ae 100644 --- a/src/main/resources/static/components/plugins/sysSignature/index.vue +++ b/src/main/resources/static/components/plugins/sysSignature/index.vue @@ -1,6 +1,6 @@ diff --git a/src/main/resources/views/platform/zhgh/staffmanage/member/common/audit/memberAuditChangeInfo.js b/src/main/resources/views/platform/zhgh/staffmanage/member/common/audit/memberAuditChangeInfo.js index 6841335..89234dd 100644 --- a/src/main/resources/views/platform/zhgh/staffmanage/member/common/audit/memberAuditChangeInfo.js +++ b/src/main/resources/views/platform/zhgh/staffmanage/member/common/audit/memberAuditChangeInfo.js @@ -56,6 +56,7 @@ const MEMBER_AUDIT_CHANGE_INFO = { + `, props: { diff --git a/src/main/resources/views/platform/zhgh/staffmanage/member/common/info/memberApplyAuditInfo.js b/src/main/resources/views/platform/zhgh/staffmanage/member/common/info/memberApplyAuditInfo.js new file mode 100644 index 0000000..e14d9e9 --- /dev/null +++ b/src/main/resources/views/platform/zhgh/staffmanage/member/common/info/memberApplyAuditInfo.js @@ -0,0 +1,134 @@ +const MEMBER_APPLY_AUDIT_INFO = { + template: /*language=HTML*/ ` +
+ + + + {{ viewData.applyDateTime || '暂无' }} + {{ viewData.loginname }} + {{ viewData.username }} + {{ viewData.sex }} + {{ viewData.nation }} + {{ viewData.userState }} + {{ viewData.preparedBy }} + {{ viewData.personType }} + {{ viewData.education }} + {{ viewData.political }} + {{ viewData.idCard }} + {{ viewData.mobile }} + {{ viewData.email }} + {{ viewData.unionName }} + {{ viewData.unitName }} + {{ viewData.governmentPosition }} + {{ viewData.wantJoinLoveMutualAidAssociation ? '是' : '否' }} + + + + + + + + 暂无 + + +
+ 暂无 +
+ {{ viewData.notes || '暂无' }} +
+
+ +
分工会审核信息
+ + {{ record.operatorName || '暂无' }} + {{ record.handledAt || '暂无' }} + {{ record.result }} + {{ record.originalResult }} + {{ record.opinion || '暂无' }} + + + 暂无 + + +
+ +
校工会审核信息
+ + {{ record.operatorName || '暂无' }} + {{ record.handledAt || '暂无' }} + {{ record.result }} + {{ record.originalResult }} + {{ record.opinion || '暂无' }} + + + 暂无 + + +
+
+ +
+ `, + data() { + return { + activeName: "apply", + viewData: {}, + approvalRecords: [], + viewLoading: false, + requestId: 0 + } + }, + computed: { + families() { + const value = this.viewData.families + if (Array.isArray(value)) { + return value + } + if (typeof value === "string" && value) { + try { + const parsed = JSON.parse(value) + return Array.isArray(parsed) ? parsed : [] + } catch (error) { + return [] + } + } + return [] + }, + // 申请人任务不展示;20、50 节点分别为分工会和校工会审核记录。 + branchRecords() { + return this.approvalRecords.filter((record) => Number(record.nodeCode) === 20 && record.handledAt) + }, + schoolRecords() { + return this.approvalRecords.filter((record) => Number(record.nodeCode) === 50 && record.handledAt) + } + }, + methods: { + /** + * recordId 为 member_apply_record.id。两个接口的 Result.data 分别为当次申请快照和流程记录列表; + * 记录包含 nodeCode、办理人、时间、结果、原审核结果、意见与签字 URL;只展示审核节点。 + */ + onOpen(recordId) { + this.$set(this, "activeName", "apply") + this.$set(this, "viewData", {}) + this.$set(this, "approvalRecords", []) + this.$set(this, "requestId", this.requestId + 1) + const requestId = this.requestId + this.$set(this, "viewLoading", true) + return Promise.all([ + this.$axios.post("/platform/member/apply/common/findApplyRecord", { recordId }), + this.$axios.post("/platform/member/apply/common/approvalRecords", { recordId }) + ]) + .then(([applyRes, recordsRes]) => { + if (requestId === this.requestId && applyRes.code === 0) { + this.$set(this, "viewData", applyRes.data || {}) + this.$set(this, "approvalRecords", recordsRes.code === 0 ? recordsRes.data || [] : []) + } + }) + .finally(() => { + if (requestId === this.requestId) { + this.$set(this, "viewLoading", false) + } + }) + } + } +} diff --git a/src/main/resources/views/platform/zhgh/staffmanage/member/common/info/memberInfo.js b/src/main/resources/views/platform/zhgh/staffmanage/member/common/info/memberInfo.js index fdd19e6..b26f93e 100644 --- a/src/main/resources/views/platform/zhgh/staffmanage/member/common/info/memberInfo.js +++ b/src/main/resources/views/platform/zhgh/staffmanage/member/common/info/memberInfo.js @@ -1,7 +1,9 @@ const MEMBER_INFO = { - template: ` -
+ template: /*language=HTML*/ ` +
+
本次申请
+ {{ viewData.applyDateTime || '暂无' }} {{ viewData.username + '(' + viewData.loginname + ')' }} {{ viewData.sex }} {{ viewData.nation }} @@ -45,20 +47,75 @@ 无数据 +
+
申请及审核记录
+ + + + + + + + + + + + + + + + +
+
`, data() { return { - viewData: {} + viewData: {}, + approvalRecords: [], + applyRecordId: "", + viewLoading: false, + viewRequestId: 0 } }, methods: { - onOpen(id) { - this.$axios.post("/platform/member/apply/common/findOne", { id }).then((res) => { - if (res.code === 0) { - this.viewData = res.data - } - }) + /** + * id 为会员 userId,recordId 可选,传入时查看该次申请快照。 + * 接口返回 Result,data 为会员资料或申请资料;申请资料含 applyDateTime。 + */ + onOpen(id, recordId = "") { + this.$set(this, "applyRecordId", recordId) + this.$set(this, "viewData", {}) + this.$set(this, "approvalRecords", []) + this.$set(this, "viewRequestId", this.viewRequestId + 1) + const requestId = this.viewRequestId + const url = recordId ? "/platform/member/apply/common/findApplyRecord" : "/platform/member/apply/common/findOne" + const params = recordId ? { recordId } : { id } + this.$set(this, "viewLoading", true) + // 入会查看同时读取申请快照和任务历史;普通会员资料查看仍只查询会员信息。 + const requests = [this.$axios.post(url, params)] + if (recordId) { + requests.push(this.$axios.post("/platform/member/apply/common/approvalRecords", { recordId })) + } + return Promise.all(requests) + .then(([res, recordsRes]) => { + // 快速切换查看对象时,仅接收最后一次请求,避免显示上一条申请。 + if (res.code === 0 && requestId === this.viewRequestId) { + this.$set(this, "viewData", res.data || {}) + this.$set(this, "approvalRecords", recordsRes && recordsRes.code === 0 ? recordsRes.data || [] : []) + } + }) + .finally(() => { + if (requestId === this.viewRequestId) { + this.$set(this, "viewLoading", false) + } + }) } } } diff --git a/src/main/resources/views/platform/zhghh5/staffmanage/member/apply/branch/index.html b/src/main/resources/views/platform/zhghh5/staffmanage/member/apply/branch/index.html index c849fb7..805af31 100644 --- a/src/main/resources/views/platform/zhghh5/staffmanage/member/apply/branch/index.html +++ b/src/main/resources/views/platform/zhghh5/staffmanage/member/apply/branch/index.html @@ -127,6 +127,10 @@ layout("/layouts/platform_h5.html"){ height: calc(100vh - 46px); overflow: auto; } + .approval-popup .van-tabs { + height: auto; + overflow: visible; + } .van-cell-group__title { padding: 6px 0; } @@ -211,30 +215,32 @@ layout("/layouts/platform_h5.html"){
- + - - +
{{formData.processInstanceNodeName}}
+ + - +
- 退回 - 建议撤销 - 同意 + 退回 + 拒绝 + 同意
diff --git a/src/main/resources/views/platform/zhghh5/staffmanage/member/common/mobileMemberApplyAuditInfo.js b/src/main/resources/views/platform/zhghh5/staffmanage/member/common/mobileMemberApplyAuditInfo.js new file mode 100644 index 0000000..cd050e0 --- /dev/null +++ b/src/main/resources/views/platform/zhghh5/staffmanage/member/common/mobileMemberApplyAuditInfo.js @@ -0,0 +1,132 @@ +const MOBILE_MEMBER_APPLY_AUDIT_INFO = { + template: /*language=HTML*/ ` +
+ + + 正在加载申请信息 + + {{ viewData.applyDateTime || '暂无' }} + {{ viewData.loginname }} + {{ viewData.username }} + {{ viewData.sex }} + {{ viewData.nation }} + {{ viewData.userState }} + {{ viewData.preparedBy }} + {{ viewData.personType }} + {{ viewData.education }} + {{ viewData.political }} + {{ viewData.idCard }} + {{ viewData.mobile }} + {{ viewData.email }} + {{ viewData.unionName }} + {{ viewData.unitName }} + {{ viewData.governmentPosition }} + {{ viewData.wantJoinLoveMutualAidAssociation ? '是' : '否' }} + +
+
+ {{ family.relation }} {{ family.name }} {{ family.unit }} {{ family.remark }} +
+
+ 暂无 +
+ +
+
+ {{ viewData.notes || '暂无' }} +
+ +
+ + + {{ record.result }} + {{ record.originalResult }} + {{ record.operatorName || '暂无' }} + {{ record.handledAt || '暂无' }} + {{ record.opinion || '暂无' }} + + + 暂无 + + + + + + {{ record.result }} + {{ record.originalResult }} + {{ record.operatorName || '暂无' }} + {{ record.handledAt || '暂无' }} + {{ record.opinion || '暂无' }} + + + 暂无 + + + +
+ +
+ `, + data() { + return { + activeName: "apply", + viewData: {}, + approvalRecords: [], + viewLoading: false, + requestId: 0 + } + }, + computed: { + families() { + const value = this.viewData.families + if (Array.isArray(value)) { + return value + } + if (typeof value === "string" && value) { + try { + const parsed = JSON.parse(value) + return Array.isArray(parsed) ? parsed : [] + } catch (error) { + return [] + } + } + return [] + }, + // 申请人任务不展示;20、50 节点分别为分工会和校工会审核记录。 + branchRecords() { + return this.approvalRecords.filter((record) => Number(record.nodeCode) === 20 && record.handledAt) + }, + schoolRecords() { + return this.approvalRecords.filter((record) => Number(record.nodeCode) === 50 && record.handledAt) + } + }, + methods: { + /** + * recordId 为申请记录 ID。接口返回 Result;data 分别为申请快照和流程记录列表, + * 按 nodeCode 将审核结果、意见与签字 URL 分到分工会和校工会页签。 + */ + onOpen(recordId) { + this.$set(this, "activeName", "apply") + this.$set(this, "viewData", {}) + this.$set(this, "approvalRecords", []) + this.$set(this, "requestId", this.requestId + 1) + const requestId = this.requestId + this.$set(this, "viewLoading", true) + return Promise.all([ + this.$axios.post("/platform/member/apply/common/findApplyRecord", { recordId }), + this.$axios.post("/platform/member/apply/common/approvalRecords", { recordId }) + ]) + .then(([applyRes, recordsRes]) => { + if (requestId === this.requestId && applyRes.code === 0) { + this.$set(this, "viewData", applyRes.data || {}) + this.$set(this, "approvalRecords", recordsRes.code === 0 ? recordsRes.data || [] : []) + } + }) + .finally(() => { + if (requestId === this.requestId) { + this.$set(this, "viewLoading", false) + } + }) + } + } +} diff --git a/src/main/resources/views/platform/zhghh5/staffmanage/member/common/mobileMemberChangeInfo.js b/src/main/resources/views/platform/zhghh5/staffmanage/member/common/mobileMemberChangeInfo.js index eaa62c8..7584563 100644 --- a/src/main/resources/views/platform/zhghh5/staffmanage/member/common/mobileMemberChangeInfo.js +++ b/src/main/resources/views/platform/zhghh5/staffmanage/member/common/mobileMemberChangeInfo.js @@ -1,7 +1,61 @@ const MOBILE_MEMBER_CHANGE_INFO = { - template: ` + template: /*language=HTML*/ `
+ + 正在加载申请记录 + + {{ applyData.applyDateTime || '暂无' }} + {{ applyData.username }} + {{ applyData.loginname }} + {{ applyData.unionName }} + {{ applyData.unitName }} + {{ applyData.governmentPosition }} + {{ applyData.sex }} + {{ applyData.nation }} + {{ applyData.idCard }} + {{ applyData.mobile }} + {{ applyData.email }} + {{ applyData.userState }} + {{ applyData.preparedBy }} + {{ applyData.personType }} + {{ applyData.education }} + {{ applyData.political }} + {{ applyData.wantJoinLoveMutualAidAssociation ? '是' : '否' }} + +
+
+ {{ family.relation }} {{ family.name }} {{ family.unit }} {{ family.remark }} +
+
+ 暂无 +
+ +
+
+ {{ applyData.notes || '暂无' }} +
+ +
+ + 正在加载审核记录 + + +