diff --git a/src/main/java/com/budwk/app/flow/service/SchoolOaTodoService.java b/src/main/java/com/budwk/app/flow/service/SchoolOaTodoService.java index d403cca..984bb82 100644 --- a/src/main/java/com/budwk/app/flow/service/SchoolOaTodoService.java +++ b/src/main/java/com/budwk/app/flow/service/SchoolOaTodoService.java @@ -2,8 +2,6 @@ package com.budwk.app.flow.service; import com.budwk.app.flow.entity.ProcessTask; -import java.util.List; - /** * 学校 OA 待办同步服务。 */ @@ -52,19 +50,17 @@ public interface SchoolOaTodoService { * @param pcUrl PC端福利选择地址 * @param appUrl 手机端福利选择地址 * @param creatorId 待办创建人ID - * @param receiverIds 待办接收人ID集合 + * @param receiverId 待办接收人ID + * @return 学校OA待办唯一值 */ - void createWelfareReminderTodos(String projectId, String title, String pcUrl, String appUrl, String creatorId, List receiverIds); + String createWelfareReminderTodo(String projectId, String title, String pcUrl, String appUrl, String creatorId, String receiverId); /** - * 将用户当前福利项目下的全部学校OA提醒待办统一办结。 + * 将指定的学校OA福利提醒待办改为已办。 * - * @param projectId 福利项目ID - * @param userId 完成选择的用户ID - * @param pcUrl PC端福利选择地址 - * @param appUrl 手机端福利选择地址 + * @param uniqueId 创建待办时记录的学校OA唯一值 */ - void completeWelfareReminderTodos(String projectId, String userId, String pcUrl, String appUrl); + void completeWelfareReminderTodo(String uniqueId); /** * 删除用户已完成福利选择对应的学校OA提醒待办。 diff --git a/src/main/java/com/budwk/app/flow/service/impl/SchoolOaTodoServiceImpl.java b/src/main/java/com/budwk/app/flow/service/impl/SchoolOaTodoServiceImpl.java index 6ebc5ef..c827fbd 100644 --- a/src/main/java/com/budwk/app/flow/service/impl/SchoolOaTodoServiceImpl.java +++ b/src/main/java/com/budwk/app/flow/service/impl/SchoolOaTodoServiceImpl.java @@ -28,7 +28,6 @@ import org.nutz.ioc.loader.annotation.IocBean; import java.util.Date; import java.util.List; -import java.util.UUID; /** * 学校 OA 待办同步服务实现。 @@ -202,11 +201,11 @@ public class SchoolOaTodoServiceImpl implements SchoolOaTodoService { } @Override - public void createWelfareReminderTodos(String projectId, String title, String pcUrl, String appUrl, String creatorId, List receiverIds) { + public String createWelfareReminderTodo(String projectId, String title, String pcUrl, String appUrl, String creatorId, String receiverId) { if (!isSchoolOaEnabled("创建福利选择提醒待办", null, null)) { - return; + return null; } - if (StrUtil.hasBlank(projectId, title, pcUrl, appUrl, creatorId) || receiverIds == null || receiverIds.isEmpty()) { + if (StrUtil.hasBlank(projectId, title, pcUrl, appUrl, creatorId, receiverId)) { throw new BaseException("学校OA福利选择提醒待办参数不完整"); } @@ -215,33 +214,23 @@ public class SchoolOaTodoServiceImpl implements SchoolOaTodoService { throw new BaseException("学校OA福利选择提醒待办创建人工号或姓名为空,userId={}", creatorId); } - String creatorTime = formatTime(System.currentTimeMillis()); - for (String receiverId : receiverIds) { - Sys_user receiver = dao.fetch(Sys_user.class, receiverId); - if (receiver == null || StrUtil.hasBlank(receiver.getLoginname(), receiver.getUsername())) { - throw new BaseException("学校OA福利选择提醒待办接收人工号或姓名为空,userId={}", receiverId); - } - sendWelfareReminderTodo(projectId, title, pcUrl, appUrl, creator, receiver, creatorTime); + Sys_user receiver = dao.fetch(Sys_user.class, receiverId); + if (receiver == null || StrUtil.hasBlank(receiver.getLoginname(), receiver.getUsername())) { + throw new BaseException("学校OA福利选择提醒待办接收人工号或姓名为空,userId={}", receiverId); } + return sendWelfareReminderTodo(projectId, title, pcUrl, appUrl, creator, receiver, + formatTime(System.currentTimeMillis())); } @Override - public void completeWelfareReminderTodos(String projectId, String userId, String pcUrl, String appUrl) { - if (!isSchoolOaEnabled("办结福利选择提醒流程", null, null) - || StrUtil.hasBlank(projectId, userId, pcUrl, appUrl)) { + public void completeWelfareReminderTodo(String uniqueId) { + if (!isSchoolOaEnabled("完成福利选择提醒待办", null, null) || StrUtil.isBlank(uniqueId)) { return; } - String flowId = buildWelfareReminderFlowId(projectId, userId); - JSONObject body = JSONUtil.createObj(); - body.set("flowId", flowId); - body.set("stepName", "福利选择完成"); - body.set("pcUrl", pcUrl); - body.set("appUrl", appUrl); - String updateFlowUrl = requireConfig("school-oa.todo.update-flow-url"); - String responseBody = sendTodoRequest(updateFlowUrl, body, - "学校OA福利选择提醒流程办结失败", "学校OA福利选择提醒流程办结异常"); - log.info("学校OA福利选择提醒流程办结成功,projectId={},userId={},flowId={},response={}", - projectId, userId, flowId, responseBody); + String updateUrl = requireConfig("school-oa.todo.update-url").replace("{uniqueId}", uniqueId); + String responseBody = sendTodoRequest(updateUrl, + "学校OA福利选择提醒待办完成失败", "学校OA福利选择提醒待办完成异常"); + log.info("学校OA福利选择提醒待办完成成功,uniqueId={},response={}", uniqueId, responseBody); } @Override @@ -413,8 +402,9 @@ public class SchoolOaTodoServiceImpl implements SchoolOaTodoService { /** * 按学校OA固定待办格式创建福利选择提醒,标题承载自定义提醒内容。 */ - private void sendWelfareReminderTodo(String projectId, String title, String pcUrl, String appUrl, - Sys_user creator, Sys_user receiver, String creatorTime) { + private String sendWelfareReminderTodo(String projectId, String title, String pcUrl, String appUrl, + Sys_user creator, Sys_user receiver, String creatorTime) { + String uniqueId = buildWelfareReminderUniqueId(projectId, receiver.getId()); JSONObject body = new JSONObject(); body.set("flowTypeName", "福利选择"); body.set("flowId", buildWelfareReminderFlowId(projectId, receiver.getId())); @@ -427,8 +417,8 @@ public class SchoolOaTodoServiceImpl implements SchoolOaTodoService { body.set("appUrl", appUrl); body.set("pcPreviewUrl", pcUrl); body.set("appPreviewUrl", appUrl); - // 每次提醒使用新的唯一值,重复提醒直接新增待办,不删除或覆盖历史提醒。 - body.set("uniqueId", buildWelfareReminderUniqueId(projectId, receiver.getId())); + // 同一项目和用户固定使用一个唯一值,确保学校OA最多存在一条福利提醒待办。 + body.set("uniqueId", uniqueId); body.set("receiveCode", receiver.getLoginname()); body.set("receiveName", receiver.getUsername()); body.set("receiveTime", creatorTime); @@ -440,10 +430,17 @@ public class SchoolOaTodoServiceImpl implements SchoolOaTodoService { JSONObject response = executeWelfareReminderTodoRequest(insertUrl, requestBody, projectId, receiver.getLoginname()); String responseMessage = response.getStr("message", ""); if (response.getInt("state", 0) != 200) { + // 历史待办已存在时视为创建成功,由本系统补记待办记录并阻止后续重复创建。 + if (StrUtil.contains(responseMessage, "已存在")) { + log.info("学校OA福利选择提醒待办已存在,按发送成功处理,projectId={},receiveCode={},uniqueId={}", + projectId, receiver.getLoginname(), uniqueId); + return uniqueId; + } throw new BaseException("学校OA福利选择提醒待办创建失败:{}", StrUtil.blankToDefault(responseMessage, JSONUtil.toJsonStr(response))); } log.info("学校OA福利选择提醒待办创建成功,projectId={},receiveCode={},response={}", projectId, receiver.getLoginname(), JSONUtil.toJsonStr(response)); + return uniqueId; } /** @@ -465,18 +462,14 @@ public class SchoolOaTodoServiceImpl implements SchoolOaTodoService { } } - /** - * 同一项目和用户共用流程标识,用户完成选择后可一次办结该流程下的全部提醒待办。 - */ + /** 同一项目和用户共用固定流程标识。 */ private String buildWelfareReminderFlowId(String projectId, String userId) { return "welfare_reminder_" + projectId + "_" + userId; } - /** - * 每次福利提醒生成新的待办唯一值,允许对同一用户重复发送多条学校OA待办。 - */ + /** 同一项目和用户固定使用一个待办唯一值。 */ private String buildWelfareReminderUniqueId(String projectId, String userId) { - return buildWelfareReminderFlowId(projectId, userId) + "_" + UUID.randomUUID().toString().replace("-", ""); + return buildWelfareReminderFlowId(projectId, userId); } private String buildAppTaskUrl(String h5FormKey, ProcessTask task) { diff --git a/src/main/java/com/budwk/app/zhgh/club/controller/register/ClubRegistApplyController.java b/src/main/java/com/budwk/app/zhgh/club/controller/register/ClubRegistApplyController.java index 50b72a1..3914279 100644 --- a/src/main/java/com/budwk/app/zhgh/club/controller/register/ClubRegistApplyController.java +++ b/src/main/java/com/budwk/app/zhgh/club/controller/register/ClubRegistApplyController.java @@ -89,6 +89,7 @@ public class ClubRegistApplyController { public Result submit(@Valid @Param("club") SysClub club, @Param("::deleteIds") List deleteIds, @Param("::managePerson") List managePerson) { + sysClubService.validateRequiredFiles(club); sysClubService.validateManagePerson(managePerson); SysClub sysClub; if (StrUtil.isBlank(club.getId())) { @@ -119,6 +120,7 @@ public class ClubRegistApplyController { @Param("::deleteIds") List deleteIds, @Param("::managePerson") List managePerson, @Param("taskId") Long taskId) { + sysClubService.validateRequiredFiles(club); sysClubService.validateManagePerson(managePerson); if (StrUtil.isBlank(club.getId())) { sysClubService.doAdd(club, managePerson); diff --git a/src/main/java/com/budwk/app/zhgh/club/service/SysClubService.java b/src/main/java/com/budwk/app/zhgh/club/service/SysClubService.java index 8616e13..2c8ee07 100644 --- a/src/main/java/com/budwk/app/zhgh/club/service/SysClubService.java +++ b/src/main/java/com/budwk/app/zhgh/club/service/SysClubService.java @@ -45,6 +45,13 @@ public interface SysClubService extends BaseService { */ void validateManagePerson(List managePerson); + /** + * 校验协会正式提交时申请成立报告和章程草案是否已上传。 + * + * @param club 协会申请信息 + */ + void validateRequiredFiles(SysClub club); + /** * 下载协会申请成立报告模板。 * diff --git a/src/main/java/com/budwk/app/zhgh/club/service/impl/SysClubServiceImpl.java b/src/main/java/com/budwk/app/zhgh/club/service/impl/SysClubServiceImpl.java index 5fda237..07b27be 100644 --- a/src/main/java/com/budwk/app/zhgh/club/service/impl/SysClubServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/club/service/impl/SysClubServiceImpl.java @@ -273,6 +273,17 @@ public class SysClubServiceImpl extends BaseServiceImpl implements SysC } } + @Override + public void validateRequiredFiles(SysClub club) { + // 文件上传组件提交空数组时,不能仅依赖 Bean Validation 的 @NotEmpty,需要在正式提交前显式校验。 + if (club == null || ObjectUtil.isEmpty(club.getEstablishReport())) { + throw new BaseException("请上传申请成立报告"); + } + if (ObjectUtil.isEmpty(club.getRulesFile())) { + throw new BaseException("请上传章程草案"); + } + } + @Override public Pagination minePageData(ClubUserPageForm pageForm) { Sql sql = Sqls.create(""" diff --git a/src/main/java/com/budwk/app/zhgh/welfare/controller/WelfareSelectionSituationController.java b/src/main/java/com/budwk/app/zhgh/welfare/controller/WelfareSelectionSituationController.java index ac185c5..1399a80 100644 --- a/src/main/java/com/budwk/app/zhgh/welfare/controller/WelfareSelectionSituationController.java +++ b/src/main/java/com/budwk/app/zhgh/welfare/controller/WelfareSelectionSituationController.java @@ -22,6 +22,7 @@ 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 org.nutz.mvc.annotation.At; import org.nutz.mvc.annotation.Ok; import org.nutz.mvc.annotation.Param; @@ -102,7 +103,7 @@ public class WelfareSelectionSituationController { @SaCheckPermission("welfare.selection.situation") @ApiOperation("预览福利选择提醒接收人数") public Result previewReminder(@Valid @Param("pageForm") WelfareSelectionSituationPageForm pageForm, @Param("userIds") String[] userIds) { - return Result.success(situationService.countReminderRecipients(pageForm, userIds)); + return Result.success(situationService.previewReminder(pageForm, userIds)); } @At @@ -113,11 +114,11 @@ public class WelfareSelectionSituationController { if (StrUtil.isBlank(content)) { return Result.error("提醒内容不能为空"); } - int recipientCount = situationService.sendReminder(pageForm, userIds, content.trim()); - if (recipientCount == 0) { + NutMap sendResult = situationService.sendReminder(pageForm, userIds, content.trim()); + if (sendResult.getInt("recipientCount", 0) == 0) { return Result.error("当前筛选条件下没有可提醒的人员"); } - return Result.success("提醒已发送给" + recipientCount + "人"); + return Result.success(sendResult.getString("summary"), sendResult); } diff --git a/src/main/java/com/budwk/app/zhgh/welfare/model/WelfareReminderTodo.java b/src/main/java/com/budwk/app/zhgh/welfare/model/WelfareReminderTodo.java new file mode 100644 index 0000000..f118236 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/welfare/model/WelfareReminderTodo.java @@ -0,0 +1,120 @@ +package com.budwk.app.zhgh.welfare.model; + +import com.budwk.app.base.model.BaseModel; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.nutz.dao.entity.annotation.ColDefine; +import org.nutz.dao.entity.annotation.ColType; +import org.nutz.dao.entity.annotation.Column; +import org.nutz.dao.entity.annotation.Comment; +import org.nutz.dao.entity.annotation.Index; +import org.nutz.dao.entity.annotation.Name; +import org.nutz.dao.entity.annotation.Table; +import org.nutz.dao.entity.annotation.TableIndexes; +import org.nutz.dao.interceptor.annotation.PrevInsert; + +import java.util.Date; + +/** + * 福利选择学校OA提醒待办记录。 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Table("welfare_reminder_todo") +@Comment("福利选择学校OA提醒待办记录") +@TableIndexes({ + @Index(name = "UK_WELFARE_REMINDER_TODO_PROJECT_USER", fields = {"projectId", "userId"}, unique = true), + @Index(name = "IDX_WELFARE_REMINDER_TODO_STATUS", fields = {"todoStatus"}, unique = false) +}) +public class WelfareReminderTodo extends BaseModel { + + /** 待办正在发送。 */ + public static final int STATUS_SENDING = 0; + /** 学校OA待办已创建,等待用户处理。 */ + public static final int STATUS_PENDING = 1; + /** 用户已完成福利选择,学校OA待办已办结。 */ + public static final int STATUS_COMPLETED = 2; + /** 学校OA待办发送失败,可使用原唯一值重试。 */ + public static final int STATUS_FAILED = 3; + + @Name + @Comment("ID") + @ColDefine(type = ColType.VARCHAR, width = 32) + @PrevInsert(uu32 = true) + private String id; + + @Column + @Comment("福利项目ID") + @ColDefine(type = ColType.VARCHAR, width = 32) + private String projectId; + + @Column + @Comment("接收用户ID") + @ColDefine(type = ColType.VARCHAR, width = 32) + private String userId; + + @Column + @Comment("学校OA流程ID") + @ColDefine(type = ColType.VARCHAR, width = 100) + private String oaFlowId; + + @Column + @Comment("学校OA待办唯一值") + @ColDefine(type = ColType.VARCHAR, width = 100) + private String oaUniqueId; + + @Column + @Comment("接收人工号") + @ColDefine(type = ColType.VARCHAR, width = 50) + private String receiverCode; + + @Column + @Comment("接收人姓名") + @ColDefine(type = ColType.VARCHAR, width = 100) + private String receiverName; + + @Column + @Comment("发送人ID") + @ColDefine(type = ColType.VARCHAR, width = 32) + private String senderId; + + @Column + @Comment("发送人工号") + @ColDefine(type = ColType.VARCHAR, width = 50) + private String senderCode; + + @Column + @Comment("发送人姓名") + @ColDefine(type = ColType.VARCHAR, width = 100) + private String senderName; + + @Column + @Comment("待办标题") + @ColDefine(type = ColType.VARCHAR, width = 500) + private String title; + + @Column + @Comment("提醒内容") + @ColDefine(type = ColType.VARCHAR, width = 500) + private String content; + + @Column + @Comment("状态:0发送中、1待办、2已办、3发送失败") + @ColDefine(type = ColType.INT) + private Integer todoStatus; + + @Column + @Comment("发送成功时间") + @ColDefine(type = ColType.DATETIME) + private Date sendTime; + + @Column + @Comment("办结时间") + @ColDefine(type = ColType.DATETIME) + private Date finishTime; + + @Column + @Comment("发送失败原因") + @ColDefine(type = ColType.VARCHAR, width = 1000) + private String failMessage; +} diff --git a/src/main/java/com/budwk/app/zhgh/welfare/service/WelfareSelectionSituationService.java b/src/main/java/com/budwk/app/zhgh/welfare/service/WelfareSelectionSituationService.java index 9577813..076dd36 100644 --- a/src/main/java/com/budwk/app/zhgh/welfare/service/WelfareSelectionSituationService.java +++ b/src/main/java/com/budwk/app/zhgh/welfare/service/WelfareSelectionSituationService.java @@ -4,6 +4,7 @@ import com.budwk.app.base.page.Pagination; import com.budwk.app.base.service.BaseService; import com.budwk.app.zhgh.welfare.model.WelfareList; import com.budwk.app.zhgh.welfare.param.WelfareSelectionSituationPageForm; +import org.nutz.lang.util.NutMap; import javax.servlet.http.HttpServletResponse; @@ -15,13 +16,13 @@ public interface WelfareSelectionSituationService extends BaseService recipientIds = getReminderRecipientIds(pageForm, userIds); + boolean todoEnabled = Boolean.TRUE.equals(Globals.MyConfig.getBoolean("AppSchoolOa")); + int existingTodoCount = todoEnabled ? getExistingReminderTodoUserIds(pageForm.getProjectId(), recipientIds).size() : 0; + return NutMap.NEW() + .addv("recipientCount", recipientIds.size()) + .addv("newTodoCount", todoEnabled ? recipientIds.size() - existingTodoCount : 0) + .addv("existingTodoCount", existingTodoCount) + .addv("todoEnabled", todoEnabled); } @Override - public int sendReminder(WelfareSelectionSituationPageForm pageForm, String[] userIds, String content) { + public NutMap sendReminder(WelfareSelectionSituationPageForm pageForm, String[] userIds, String content) { List recipientIds = getReminderRecipientIds(pageForm, userIds); if (recipientIds.isEmpty()) { - return 0; + return buildReminderResult(0, 0, 0, 0, 0, + Boolean.TRUE.equals(Globals.MyConfig.getBoolean("AppSchoolOa"))); } // 各消息渠道统一使用福利业务标题,学校OA再拼接自定义正文作为待办标题。 String title = "智慧工会职工福利:"; @@ -148,26 +157,225 @@ public class WelfareSelectionSituationServiceImpl extends BaseServiceImpl receiverUserMap = receiverUsers.stream() + .collect(Collectors.toMap(Sys_user::getId, user -> user, (left, right) -> left)); + for (String recipientId : recipientIds) { + WelfareReminderTodo reminderTodo = getReminderTodo(pageForm.getProjectId(), recipientId); + if (isExistingReminderTodo(reminderTodo)) { + existingTodoCount++; + continue; + } + + Sys_user receiver = receiverUserMap.get(recipientId); + try { + // 发送前先落库为“发送中”,确保每一次实际发起的OA待办都可追溯。 + reminderTodo = saveSendingReminderTodo(reminderTodo, pageForm.getProjectId(), receiver, + SecurityUtil.getUserId(), title + content, content); + String uniqueId = schoolOaTodoService.createWelfareReminderTodo(pageForm.getProjectId(), title + content, + pcUrl, appUrl, SecurityUtil.getUserId(), recipientId); + if (StrUtil.isBlank(uniqueId)) { + throw new IllegalStateException("学校OA待办开关状态已变化,本次待办未发送"); + } + reminderTodo.setOaUniqueId(uniqueId); + reminderTodo.setTodoStatus(WelfareReminderTodo.STATUS_PENDING); + reminderTodo.setSendTime(new Date()); + reminderTodo.setFinishTime(null); + reminderTodo.setFailMessage(null); + dao().update(reminderTodo); + todoSuccessCount++; + } catch (Exception e) { + todoFailedCount++; + saveReminderTodoFailure(reminderTodo, pageForm.getProjectId(), recipientId, receiver, + title + content, content, e); + log.error("学校OA福利选择提醒待办发送失败,projectId={},userId={}", + pageForm.getProjectId(), recipientId, e); + } + } } - return recipientIds.size(); + return buildReminderResult(recipientIds.size(), recipientIds.size(), todoSuccessCount, + existingTodoCount, todoFailedCount, todoEnabled); } @Override public void completeReminderTodo(String projectId, String userId) { + WelfareReminderTodo reminderTodo = getReminderTodo(projectId, userId); + if (reminderTodo == null || !Objects.equals(reminderTodo.getTodoStatus(), WelfareReminderTodo.STATUS_PENDING) + || StrUtil.isBlank(reminderTodo.getOaUniqueId())) { + return; + } try { - String pcUrl = buildWelfareSelectionUrl("/platform/welfare/userSelect"); - String appUrl = buildWelfareSelectionUrl("/platform/h5/welfare/userSelect/list"); - schoolOaTodoService.completeWelfareReminderTodos(projectId, userId, pcUrl, appUrl); + schoolOaTodoService.completeWelfareReminderTodo(reminderTodo.getOaUniqueId()); + reminderTodo.setTodoStatus(WelfareReminderTodo.STATUS_COMPLETED); + reminderTodo.setFinishTime(new Date()); + reminderTodo.setFailMessage(null); + dao().update(reminderTodo); } catch (Exception e) { // 学校OA流程办结失败不能影响用户完成福利选择,后续可根据日志人工处理。 + reminderTodo.setFailMessage(limitFailureMessage(e.getMessage())); + dao().update(reminderTodo); log.error("学校OA福利选择提醒流程办结失败,projectId={},userId={}", projectId, userId, e); } } + /** + * 查询项目和用户对应的唯一提醒待办记录。 + */ + private WelfareReminderTodo getReminderTodo(String projectId, String userId) { + if (StrUtil.hasBlank(projectId, userId)) { + return null; + } + return dao().fetch(WelfareReminderTodo.class, Cnd.where(WelfareReminderTodo::getProjectId, "=", projectId) + .and(WelfareReminderTodo::getUserId, "=", userId)); + } + + /** + * 发送中、待办和已办记录均表示该项目已创建过OA待办,重复提醒时不再创建第二条。 + * 发送失败记录允许沿用原唯一值重试。 + */ + private boolean isExistingReminderTodo(WelfareReminderTodo reminderTodo) { + return reminderTodo != null && !Objects.equals(reminderTodo.getTodoStatus(), WelfareReminderTodo.STATUS_FAILED); + } + + /** + * 批量统计已存在的OA待办接收人,供发送预览显示重复跳过数量。 + */ + private Set getExistingReminderTodoUserIds(String projectId, List userIds) { + if (StrUtil.isBlank(projectId) || userIds.isEmpty()) { + return Collections.emptySet(); + } + return dao().query(WelfareReminderTodo.class, Cnd.where(WelfareReminderTodo::getProjectId, "=", projectId) + .and(WelfareReminderTodo::getUserId, "in", userIds) + .and(WelfareReminderTodo::getTodoStatus, "!=", WelfareReminderTodo.STATUS_FAILED)) + .stream() + .map(WelfareReminderTodo::getUserId) + .collect(Collectors.toSet()); + } + + /** + * 新建或重置待办发送记录。固定的流程ID和唯一值保证同一项目、同一用户最多一条OA待办。 + */ + private WelfareReminderTodo saveSendingReminderTodo(WelfareReminderTodo reminderTodo, String projectId, + Sys_user receiver, String senderId, String title, String content) { + if (receiver == null || StrUtil.hasBlank(receiver.getId(), receiver.getLoginname(), receiver.getUsername())) { + throw new IllegalArgumentException("学校OA福利选择提醒接收人信息不完整"); + } + Sys_user sender = dao().fetch(Sys_user.class, senderId); + if (sender == null || StrUtil.hasBlank(sender.getLoginname(), sender.getUsername())) { + throw new IllegalArgumentException("学校OA福利选择提醒创建人信息不完整"); + } + + boolean newRecord = reminderTodo == null; + WelfareReminderTodo record = newRecord ? new WelfareReminderTodo() : reminderTodo; + String uniqueId = buildWelfareReminderUniqueId(projectId, receiver.getId()); + record.setProjectId(projectId); + record.setUserId(receiver.getId()); + record.setOaFlowId(uniqueId); + record.setOaUniqueId(uniqueId); + record.setReceiverCode(receiver.getLoginname()); + record.setReceiverName(receiver.getUsername()); + record.setSenderId(sender.getId()); + record.setSenderCode(sender.getLoginname()); + record.setSenderName(sender.getUsername()); + record.setTitle(limitText(title, 500)); + record.setContent(limitText(content, 500)); + record.setTodoStatus(WelfareReminderTodo.STATUS_SENDING); + record.setSendTime(null); + record.setFinishTime(null); + record.setFailMessage(null); + if (newRecord) { + dao().insert(record); + } else { + dao().update(record); + } + return record; + } + + /** + * 保存OA待办发送失败原因;若发送前校验失败导致记录尚未创建,则补建一条失败记录。 + */ + private void saveReminderTodoFailure(WelfareReminderTodo reminderTodo, String projectId, String userId, + Sys_user receiver, String title, String content, Exception exception) { + WelfareReminderTodo record = reminderTodo == null ? getReminderTodo(projectId, userId) : reminderTodo; + if (record == null) { + record = new WelfareReminderTodo(); + record.setProjectId(projectId); + record.setUserId(userId); + record.setOaFlowId(buildWelfareReminderUniqueId(projectId, userId)); + record.setOaUniqueId(buildWelfareReminderUniqueId(projectId, userId)); + record.setSenderId(SecurityUtil.getUserId()); + if (receiver != null) { + record.setReceiverCode(receiver.getLoginname()); + record.setReceiverName(receiver.getUsername()); + } + Sys_user sender = dao().fetch(Sys_user.class, SecurityUtil.getUserId()); + if (sender != null) { + record.setSenderCode(sender.getLoginname()); + record.setSenderName(sender.getUsername()); + } + record.setTitle(limitText(title, 500)); + record.setContent(limitText(content, 500)); + record.setTodoStatus(WelfareReminderTodo.STATUS_FAILED); + record.setFailMessage(limitFailureMessage(exception.getMessage())); + dao().insert(record); + return; + } + record.setTodoStatus(WelfareReminderTodo.STATUS_FAILED); + record.setFailMessage(limitFailureMessage(exception.getMessage())); + dao().update(record); + } + + /** + * 组装发送统计和前端提示,消息与OA待办分别计数,避免把重复跳过误报为发送成功。 + */ + private NutMap buildReminderResult(int recipientCount, int messageSuccessCount, int todoSuccessCount, + int existingTodoCount, int todoFailedCount, boolean todoEnabled) { + StringBuilder summary = new StringBuilder("消息发送成功").append(messageSuccessCount).append("人"); + if (todoEnabled) { + summary.append(",OA待办发送成功").append(todoSuccessCount).append("人"); + if (existingTodoCount > 0) { + summary.append(",").append(existingTodoCount).append("人已有OA待办未重复发送"); + } + if (todoFailedCount > 0) { + summary.append(",OA待办发送失败").append(todoFailedCount).append("人"); + } + } else { + summary.append(",学校OA待办开关未开启"); + } + return NutMap.NEW() + .addv("recipientCount", recipientCount) + .addv("messageSuccessCount", messageSuccessCount) + .addv("todoSuccessCount", todoSuccessCount) + .addv("existingTodoCount", existingTodoCount) + .addv("todoFailedCount", todoFailedCount) + .addv("todoEnabled", todoEnabled) + .addv("summary", summary.append("。").toString()); + } + + /** + * 数据库失败原因字段最多保存1000个字符,避免外部接口返回超长文本导致二次写库失败。 + */ + private String limitFailureMessage(String message) { + String safeMessage = StrUtil.blankToDefault(message, "未知异常"); + return safeMessage.length() > 1000 ? safeMessage.substring(0, 1000) : safeMessage; + } + + /** 按数据库字段长度截取发送内容,避免超长文本影响待办记录落库。 */ + private String limitText(String text, int maxLength) { + String safeText = StrUtil.nullToEmpty(text); + return safeText.length() > maxLength ? safeText.substring(0, maxLength) : safeText; + } + + /** 同一项目和用户固定使用一个OA待办唯一值。 */ + private String buildWelfareReminderUniqueId(String projectId, String userId) { + return "welfare_reminder_" + projectId + "_" + userId; + } + /** * 使用系统配置的绝对域名构造学校外部平台可访问的福利选择地址。 */ diff --git a/src/main/resources/views/platform/zhgh/club/register/apply/index.html b/src/main/resources/views/platform/zhgh/club/register/apply/index.html index eed9cd8..6465a25 100644 --- a/src/main/resources/views/platform/zhgh/club/register/apply/index.html +++ b/src/main/resources/views/platform/zhgh/club/register/apply/index.html @@ -85,6 +85,7 @@ layout("/layouts/platform.html"){ await this.doHandle("onSave") }, onSubmit() { + if (!this.validateRequiredFiles()) return this.$refs.clubFormRef.$refs.form.validate().then(() => { this.doHandle("onSubmit") }).catch(() => { @@ -92,12 +93,26 @@ layout("/layouts/platform.html"){ }) }, onFinishTask() { + if (!this.validateRequiredFiles()) return this.$refs.clubFormRef.$refs.form.validate().then(() => { this.doHandle("onFinishTask") }).catch(() => { this.$message.warning({ title: "警告", message: "存在必填项未填写!" }) }) }, + // 文件上传组件使用数组承载文件,提交前显式检查空数组,避免表单校验误判为已填写。 + validateRequiredFiles() { + const formData = this.$refs.clubFormRef.formData + if (!formData.establishReport || formData.establishReport.length === 0) { + this.$message.warning({ title: "警告", message: "请上传申请成立报告" }) + return false + } + if (!formData.rulesFile || formData.rulesFile.length === 0) { + this.$message.warning({ title: "警告", message: "请上传章程草案" }) + return false + } + return true + }, async doHandle(type) { let formData = {} try { diff --git a/src/main/resources/views/platform/zhgh/welfare/select/optionSelect.js b/src/main/resources/views/platform/zhgh/welfare/select/optionSelect.js index 08cbf6b..3ae47b4 100644 --- a/src/main/resources/views/platform/zhgh/welfare/select/optionSelect.js +++ b/src/main/resources/views/platform/zhgh/welfare/select/optionSelect.js @@ -391,6 +391,10 @@ const optionSelect = { userName: "", userSign: "" } + // PC端本人选择时默认当前登录人作为收货人;管理员代选仍由管理员填写目标用户收货人。 + if (!this.isProxySelect && this.$store.state.user && this.$store.state.user.username) { + this.$set(this.contactForm, "userName", this.$store.state.user.username) + } this.getProjectInfo() }, @@ -440,7 +444,11 @@ const optionSelect = { this.hasSubmittedBefore = this.userSelection.length > 0 if (this.userSelection && this.userSelection.length > 0) { - this.contactForm.mobile = this.userSelection[0]?.mobile + this.$set(this.contactForm, "mobile", this.userSelection[0]?.mobile || "") + // 已有选择记录时优先回显历史收货人;历史记录为空时保留本人默认值。 + if (this.userSelection[0]?.userName) { + this.$set(this.contactForm, "userName", this.userSelection[0].userName) + } this.$set(this.contactForm, "userSign", this.userSelection[0]?.userSign || "") } else { if (this.isProxySelect) { diff --git a/src/main/resources/views/platform/zhgh/welfare/selectionSituation/index.html b/src/main/resources/views/platform/zhgh/welfare/selectionSituation/index.html index 5f11e2c..2a563b7 100644 --- a/src/main/resources/views/platform/zhgh/welfare/selectionSituation/index.html +++ b/src/main/resources/views/platform/zhgh/welfare/selectionSituation/index.html @@ -126,7 +126,9 @@ layout("/layouts/platform.html"){ - + + + @@ -174,6 +176,9 @@ layout("/layouts/platform.html"){ reminderVisible: false, reminderContent: "", reminderCount: 0, + reminderNewTodoCount: 0, + reminderExistingTodoCount: 0, + reminderTodoEnabled: false, reminderSending: false } }, @@ -270,11 +275,14 @@ layout("/layouts/platform.html"){ this.$message.error(response.msg) return } - if (response.data === 0) { + if (!response.data || response.data.recipientCount === 0) { this.$message.warning("当前筛选条件下没有可提醒的人员") return } - this.$set(this, "reminderCount", response.data) + this.$set(this, "reminderCount", response.data.recipientCount) + this.$set(this, "reminderNewTodoCount", response.data.newTodoCount) + this.$set(this, "reminderExistingTodoCount", response.data.existingTodoCount) + this.$set(this, "reminderTodoEnabled", response.data.todoEnabled) this.$set(this, "reminderContent", "请及时完成本次福利选择。") this.$set(this, "reminderVisible", true) }, @@ -294,7 +302,11 @@ layout("/layouts/platform.html"){ { headers: { "Content-Type": "application/x-www-form-urlencoded" } } ) if (response.code === 0) { - this.$message.success(response.msg) + if (response.data && response.data.todoFailedCount > 0) { + this.$message.warning(response.msg) + } else { + this.$message.success(response.msg) + } this.closeReminder() } else { this.$message.error(response.msg) @@ -308,6 +320,10 @@ layout("/layouts/platform.html"){ closeReminder() { this.$set(this, "reminderVisible", false) this.$set(this, "reminderContent", "") + this.$set(this, "reminderCount", 0) + this.$set(this, "reminderNewTodoCount", 0) + this.$set(this, "reminderExistingTodoCount", 0) + this.$set(this, "reminderTodoEnabled", false) }, // 管理员待选