From 32b288053a6242ce2590cb97160c6152613714e5 Mon Sep 17 00:00:00 2001 From: hongqiwei <1143662660@qq.com> Date: Wed, 2 Sep 2026 15:16:29 +0800 Subject: [PATCH] =?UTF-8?q?=E7=A6=8F=E5=88=A9=E5=8F=91=E6=B6=88=E6=81=AF?= =?UTF-8?q?=E6=95=B4=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/budwk/app/base/sms/SmsService.java | 12 +- .../base/sms/impl/ypi/SmsYpiServiceImpl.java | 9 +- .../impl/ypi/UnifiedMessageV4ServiceImpl.java | 66 ++- .../param/SchoolOaTodoBatchCreateParam.java | 24 + .../flow/param/SchoolOaTodoReceiverParam.java | 17 + .../app/flow/service/SchoolOaTodoService.java | 26 +- .../service/impl/SchoolOaTodoServiceImpl.java | 183 ++----- .../WelfareSelectionSituationController.java | 1 + .../welfare/model/WelfareReminderMessage.java | 121 +++++ .../WelfareSelectionSituationServiceImpl.java | 473 ++++++++++++++++-- .../staffbenefit/condolence/apply/index.html | 2 +- .../staffbenefit/condolence/common/info.js | 2 +- .../staffbenefit/condolence/apply/index.html | 2 +- .../staffbenefit/condolence/common/info.js | 2 +- 14 files changed, 742 insertions(+), 198 deletions(-) create mode 100644 src/main/java/com/budwk/app/flow/param/SchoolOaTodoBatchCreateParam.java create mode 100644 src/main/java/com/budwk/app/flow/param/SchoolOaTodoReceiverParam.java create mode 100644 src/main/java/com/budwk/app/zhgh/welfare/model/WelfareReminderMessage.java diff --git a/src/main/java/com/budwk/app/base/sms/SmsService.java b/src/main/java/com/budwk/app/base/sms/SmsService.java index 950a14d..64dbd8d 100644 --- a/src/main/java/com/budwk/app/base/sms/SmsService.java +++ b/src/main/java/com/budwk/app/base/sms/SmsService.java @@ -1,6 +1,7 @@ package com.budwk.app.base.sms; import com.budwk.app.sys.models.Sys_user; +import org.nutz.lang.util.NutMap; import java.util.List; @@ -47,6 +48,15 @@ public interface SmsService { * @param content 消息正文 * @param pcUrl PC端跳转链接 * @param mobileUrl 手机端跳转链接 + * @return 统一消息平台返回的消息标识集合;每个标识可用于查询该批消息的最终发送结果 */ - void massSendByUsers(List users, String title, String content, String pcUrl, String mobileUrl); + List massSendByUsers(List users, String title, String content, String pcUrl, String mobileUrl); + + /** + * 查询学校统一消息平台的批量发送结果。 + * + * @param messageId 批量发送接口返回的消息标识;同一批次的接收人使用该标识查询最终送达结果 + * @return 查询结果,包含消息总数、成功数、失败数及失败接收人数组;调用失败时抛出业务异常 + */ + NutMap getMessageResult(String messageId); } diff --git a/src/main/java/com/budwk/app/base/sms/impl/ypi/SmsYpiServiceImpl.java b/src/main/java/com/budwk/app/base/sms/impl/ypi/SmsYpiServiceImpl.java index 4e6974b..3199304 100644 --- a/src/main/java/com/budwk/app/base/sms/impl/ypi/SmsYpiServiceImpl.java +++ b/src/main/java/com/budwk/app/base/sms/impl/ypi/SmsYpiServiceImpl.java @@ -14,6 +14,7 @@ import org.nutz.json.Json; import org.nutz.lang.Lang; import org.nutz.lang.util.NutMap; +import java.util.Collections; import java.util.List; import java.util.Map; @@ -66,12 +67,18 @@ public class SmsYpiServiceImpl implements SmsService { * 兼容统一消息V4新增的批量发送接口;旧版实现不再注册为发送服务,仅保留历史调用能力。 */ @Override - public void massSendByUsers(List users, String title, String content, String pcUrl, String mobileUrl) { + public List massSendByUsers(List users, String title, String content, String pcUrl, String mobileUrl) { List loginNames = users.stream() .map(Sys_user::getLoginname) .filter(StrUtil::isNotBlank) .toList(); massSend(loginNames, title, content, StrUtil.blankToDefault(mobileUrl, pcUrl)); + return Collections.emptyList(); + } + + @Override + public NutMap getMessageResult(String messageId) { + throw new UnsupportedOperationException("旧版学校消息网关不支持统一消息V4发送结果查询"); } /** diff --git a/src/main/java/com/budwk/app/base/sms/impl/ypi/UnifiedMessageV4ServiceImpl.java b/src/main/java/com/budwk/app/base/sms/impl/ypi/UnifiedMessageV4ServiceImpl.java index f9732a6..884665d 100644 --- a/src/main/java/com/budwk/app/base/sms/impl/ypi/UnifiedMessageV4ServiceImpl.java +++ b/src/main/java/com/budwk/app/base/sms/impl/ypi/UnifiedMessageV4ServiceImpl.java @@ -13,6 +13,7 @@ import org.nutz.integration.jedis.RedisService; import org.nutz.ioc.impl.PropertiesProxy; import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.lang.util.NutMap; import java.util.List; @@ -63,9 +64,9 @@ public class UnifiedMessageV4ServiceImpl implements SmsService { } @Override - public void massSendByUsers(List users, String title, String content, String pcUrl, String mobileUrl) { + public List massSendByUsers(List users, String title, String content, String pcUrl, String mobileUrl) { if (users == null || users.isEmpty()) { - return; + return List.of(); } List receivers = users.stream() .filter(user -> StrUtil.isNotBlank(user.getLoginname()) && StrUtil.isNotBlank(user.getUsername())) @@ -74,13 +75,14 @@ public class UnifiedMessageV4ServiceImpl implements SmsService { if (receivers.isEmpty()) { throw new BaseException("学校统一消息发送失败:接收人工号或姓名不能为空"); } - sendMessage(title, content, createReceivers(receivers), pcUrl, mobileUrl); + return sendMessage(title, content, createReceivers(receivers), pcUrl, mobileUrl); } /** * 调用统一消息V4普通消息接口;有链接时按外链模式同时传递PC和手机端地址。 + * 返回值为平台入队后生成的消息标识集合,供业务侧随后查询最终逐人发送结果。 */ - private void sendMessage(String title, String content, JSONArray receivers, String pcUrl, String mobileUrl) { + private List sendMessage(String title, String content, JSONArray receivers, String pcUrl, String mobileUrl) { String token = getAccessToken(); boolean hasLink = StrUtil.isNotBlank(pcUrl) || StrUtil.isNotBlank(mobileUrl); JSONObject body = JSONUtil.createObj(); @@ -108,7 +110,20 @@ public class UnifiedMessageV4ServiceImpl implements SmsService { if (response.getInt("state", 0) != 200) { throw new BaseException("学校统一消息发送失败:{}", response.getStr("message", responseBody)); } - log.info("学校统一消息发送成功,接收人数量={},response={}", receivers.size(), responseBody); + JSONArray messageIdArray = response.getJSONArray("data"); + if (messageIdArray == null || messageIdArray.isEmpty()) { + throw new BaseException("学校统一消息发送失败:平台未返回消息标识"); + } + List messageIds = messageIdArray.stream() + .map(String::valueOf) + .filter(StrUtil::isNotBlank) + .toList(); + if (messageIds.isEmpty()) { + throw new BaseException("学校统一消息发送失败:平台返回的消息标识为空"); + } + log.info("学校统一消息发送已入队,接收人数量={},messageIds={},response={}", + receivers.size(), messageIds, responseBody); + return messageIds; } catch (BaseException e) { throw e; } catch (Exception e) { @@ -117,6 +132,47 @@ public class UnifiedMessageV4ServiceImpl implements SmsService { } } + /** + * 调用统一消息V4结果查询接口。 + * 参数为批量发送返回的messageId;返回NutMap中的messageTotal、successCount、failCount、resultState和failedUsers + * 分别表示平台统计的总人数、成功人数、失败人数、结果状态和失败用户明细。 + */ + @Override + public NutMap getMessageResult(String messageId) { + if (StrUtil.isBlank(messageId)) { + throw new BaseException("学校统一消息结果查询失败:消息标识不能为空"); + } + String resultUrl = getRequiredConfig("school-message.result-url").replace("{messageId}", messageId); + try { + HttpRequest request = HttpRequest.post(resultUrl) + .header("domain-name", getRequiredConfig("school-message.domain-name")) + .header("token", getAccessToken()); + addInterceptDomainHeader(request); + String responseBody = request.execute().body(); + JSONObject response = JSONUtil.parseObj(responseBody); + if (response.getInt("state", 0) != 200 || response.getJSONObject("data") == null) { + throw new BaseException("学校统一消息结果查询失败:{}", response.getStr("message", responseBody)); + } + JSONObject data = response.getJSONObject("data"); + NutMap result = NutMap.NEW() + .addv("messageId", messageId) + .addv("messageTotal", data.getInt("msgTotal", 0)) + .addv("resultState", data.getStr("state")) + .addv("successCount", data.getInt("successCount", 0)) + .addv("failCount", data.getInt("failCount", 0)) + .addv("failedUsers", data.getJSONArray("user")); + log.info("学校统一消息结果查询成功,messageId={},messageTotal={},successCount={},failCount={},resultState={}", + messageId, result.getInt("messageTotal", 0), result.getInt("successCount", 0), + result.getInt("failCount", 0), result.getString("resultState")); + return result; + } catch (BaseException e) { + throw e; + } catch (Exception e) { + log.error("学校统一消息结果查询异常,url={},messageId={}", resultUrl, messageId, e); + throw new BaseException("学校统一消息结果查询异常:{}", e.getMessage()); + } + } + /** * 获取学校统一消息V4令牌;文档有效期为300秒,缓存270秒避免临界过期。 */ diff --git a/src/main/java/com/budwk/app/flow/param/SchoolOaTodoBatchCreateParam.java b/src/main/java/com/budwk/app/flow/param/SchoolOaTodoBatchCreateParam.java new file mode 100644 index 0000000..21291b1 --- /dev/null +++ b/src/main/java/com/budwk/app/flow/param/SchoolOaTodoBatchCreateParam.java @@ -0,0 +1,24 @@ +package com.budwk.app.flow.param; + +import lombok.Data; + +import java.util.List; + +/** + * 学校OA批量待办创建参数。 + * 公共流程字段在本对象中传递,接收人差异字段放在userList中,对应学校OA的/handle/batch/insert接口。 + */ +@Data +public class SchoolOaTodoBatchCreateParam { + + private String flowTypeName; + private String flowId; + private String title; + private String stepName; + private String creatorCode; + private String creatorName; + private String creatorTime; + private String startPcPreviewUrl; + private String startAppPreviewUrl; + private List userList; +} diff --git a/src/main/java/com/budwk/app/flow/param/SchoolOaTodoReceiverParam.java b/src/main/java/com/budwk/app/flow/param/SchoolOaTodoReceiverParam.java new file mode 100644 index 0000000..ea3a7f4 --- /dev/null +++ b/src/main/java/com/budwk/app/flow/param/SchoolOaTodoReceiverParam.java @@ -0,0 +1,17 @@ +package com.budwk.app.flow.param; + +import lombok.Data; + +/** + * 学校OA批量待办中的单个接收人参数,对应批量接口userList数组的一个元素。 + */ +@Data +public class SchoolOaTodoReceiverParam { + + private String pcUrl; + private String appUrl; + private String uniqueId; + private String receiveCode; + private String receiveName; + private String receiveTime; +} 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 984bb82..5da6504 100644 --- a/src/main/java/com/budwk/app/flow/service/SchoolOaTodoService.java +++ b/src/main/java/com/budwk/app/flow/service/SchoolOaTodoService.java @@ -1,6 +1,7 @@ package com.budwk.app.flow.service; import com.budwk.app.flow.entity.ProcessTask; +import com.budwk.app.flow.param.SchoolOaTodoBatchCreateParam; /** * 学校 OA 待办同步服务。 @@ -43,30 +44,17 @@ public interface SchoolOaTodoService { void updateFlowFinishedStepName(Long processInstanceId); /** - * 创建福利选择提醒待办。 + * 创建学校OA批量待办。 * - * @param projectId 福利项目ID,用于构造学校OA业务标识 - * @param title 待办标题,学校OA接口没有独立正文时用于展示提醒内容 - * @param pcUrl PC端福利选择地址 - * @param appUrl 手机端福利选择地址 - * @param creatorId 待办创建人ID - * @param receiverId 待办接收人ID - * @return 学校OA待办唯一值 + * @param todoParam 批量待办公共字段及userList接收人数组 */ - String createWelfareReminderTodo(String projectId, String title, String pcUrl, String appUrl, String creatorId, String receiverId); + void createBatchTodo(SchoolOaTodoBatchCreateParam todoParam); /** - * 将指定的学校OA福利提醒待办改为已办。 + * 根据待办唯一值将学校OA待办改为已办。 * - * @param uniqueId 创建待办时记录的学校OA唯一值 + * @param uniqueId 创建待办时传入的唯一标识 */ - void completeWelfareReminderTodo(String uniqueId); + void completeTodoByUniqueId(String uniqueId); - /** - * 删除用户已完成福利选择对应的学校OA提醒待办。 - * - * @param projectId 福利项目ID - * @param userId 完成选择的用户ID - */ - void deleteWelfareReminderTodo(String projectId, String userId); } 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 c827fbd..f453107 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 @@ -14,6 +14,8 @@ import com.budwk.app.flow.entity.ProcessInstance; import com.budwk.app.flow.entity.ProcessTask; import com.budwk.app.flow.entity.ProcessTaskActor; import com.budwk.app.flow.enums.ProcessTaskStateEnum; +import com.budwk.app.flow.param.SchoolOaTodoBatchCreateParam; +import com.budwk.app.flow.param.SchoolOaTodoReceiverParam; import com.budwk.app.flow.service.SchoolOaTodoService; import com.budwk.app.sys.models.Sys_user; import com.budwk.app.web.commons.base.Globals; @@ -27,6 +29,7 @@ import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.IocBean; import java.util.Date; +import java.util.ArrayList; import java.util.List; /** @@ -201,48 +204,63 @@ public class SchoolOaTodoServiceImpl implements SchoolOaTodoService { } @Override - public String createWelfareReminderTodo(String projectId, String title, String pcUrl, String appUrl, String creatorId, String receiverId) { - if (!isSchoolOaEnabled("创建福利选择提醒待办", null, null)) { - return null; + @Aop(TransAop.READ_COMMITTED) + public void createBatchTodo(SchoolOaTodoBatchCreateParam todoParam) { + if (!isSchoolOaEnabled("创建批量待办", null, null)) { + throw new BaseException("学校OA待办开关状态已变化,本次待办未发送"); } - if (StrUtil.hasBlank(projectId, title, pcUrl, appUrl, creatorId, receiverId)) { - throw new BaseException("学校OA福利选择提醒待办参数不完整"); + if (todoParam == null || StrUtil.hasBlank(todoParam.getFlowTypeName(), todoParam.getFlowId(), todoParam.getTitle(), + todoParam.getStepName(), todoParam.getCreatorCode(), todoParam.getCreatorName(), todoParam.getCreatorTime(), + todoParam.getStartPcPreviewUrl(), todoParam.getStartAppPreviewUrl()) + || todoParam.getUserList() == null || todoParam.getUserList().isEmpty()) { + throw new BaseException("学校OA批量待办创建参数不完整"); } - Sys_user creator = dao.fetch(Sys_user.class, creatorId); - if (creator == null || StrUtil.hasBlank(creator.getLoginname(), creator.getUsername())) { - throw new BaseException("学校OA福利选择提醒待办创建人工号或姓名为空,userId={}", creatorId); + List userList = new ArrayList<>(); + for (SchoolOaTodoReceiverParam receiverParam : todoParam.getUserList()) { + if (receiverParam == null || StrUtil.hasBlank(receiverParam.getPcUrl(), receiverParam.getAppUrl(), + receiverParam.getUniqueId(), receiverParam.getReceiveCode(), receiverParam.getReceiveName(), + receiverParam.getReceiveTime())) { + throw new BaseException("学校OA批量待办接收人参数不完整"); + } + JSONObject receiverBody = new JSONObject(); + receiverBody.set("pcUrl", receiverParam.getPcUrl()); + receiverBody.set("appUrl", receiverParam.getAppUrl()); + receiverBody.set("uniqueId", receiverParam.getUniqueId()); + receiverBody.set("receiveCode", receiverParam.getReceiveCode()); + receiverBody.set("receiveName", receiverParam.getReceiveName()); + receiverBody.set("receiveTime", receiverParam.getReceiveTime()); + userList.add(receiverBody); } - 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())); + // 学校OA批量接口的公共流程字段在根节点,接收人差异字段仅放在userList中。 + JSONObject body = new JSONObject(); + body.set("flowTypeName", todoParam.getFlowTypeName()); + body.set("flowId", todoParam.getFlowId()); + body.set("title", todoParam.getTitle()); + body.set("stepName", todoParam.getStepName()); + body.set("creatorCode", todoParam.getCreatorCode()); + body.set("creatorName", todoParam.getCreatorName()); + body.set("creatorTime", todoParam.getCreatorTime()); + body.set("startPcPreviewUrl", todoParam.getStartPcPreviewUrl()); + body.set("startAppPreviewUrl", todoParam.getStartAppPreviewUrl()); + body.set("userList", userList); + + String batchInsertUrl = requireConfig("school-oa.todo.batch-insert-url"); + String responseBody = sendTodoRequest(batchInsertUrl, body, "学校OA批量待办创建失败", "学校OA批量待办创建异常"); + log.info("学校OA批量待办创建成功,flowId={},receiverCount={},response={}", + todoParam.getFlowId(), userList.size(), responseBody); } @Override - public void completeWelfareReminderTodo(String uniqueId) { - if (!isSchoolOaEnabled("完成福利选择提醒待办", null, null) || StrUtil.isBlank(uniqueId)) { + @Aop(TransAop.READ_COMMITTED) + public void completeTodoByUniqueId(String uniqueId) { + if (!isSchoolOaEnabled("完成待办", null, null) || StrUtil.isBlank(uniqueId)) { return; } 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 - public void deleteWelfareReminderTodo(String projectId, String userId) { - if (!isSchoolOaEnabled("删除福利选择提醒待办", null, null) || StrUtil.hasBlank(projectId, userId)) { - return; - } - // 兼容删除改造前按项目和用户生成的固定唯一值,新的重复提醒不再调用删除逻辑。 - String uniqueId = buildWelfareReminderFlowId(projectId, userId); - String deleteUrl = requireConfig("school-oa.todo.delete-url").replace("{uniqueId}", uniqueId); - sendTodoRequest(deleteUrl, "学校OA福利选择提醒待办删除失败", "学校OA福利选择提醒待办删除异常"); - log.info("学校OA福利选择提醒待办删除成功,projectId={},userId={},uniqueId={}", projectId, userId, uniqueId); + String responseBody = sendTodoRequest(updateUrl, "学校OA待办完成失败", "学校OA待办完成异常"); + log.info("学校OA待办完成成功,uniqueId={},response={}", uniqueId, responseBody); } /** @@ -273,29 +291,8 @@ public class SchoolOaTodoServiceImpl implements SchoolOaTodoService { body.set("receiveTime", receiveTime); String insertUrl = requireConfig("school-oa.todo.insert-url"); - String requestBody = JSONUtil.toJsonStr(body); - log.info("学校OA待办创建请求,url={},taskId={},processInstanceId={},receiveCode={},body={}", - insertUrl, task.getId(), instance.getId(), actor.getActorAccount(), requestBody); - try { - HttpRequest request = HttpUtil.createPost(insertUrl); - setTodoRequestHeaders(request); - request.body(requestBody); - - String responseBody = request.execute().body(); - log.info("学校OA待办创建响应,taskId={},receiveCode={},response={}", task.getId(), actor.getActorAccount(), responseBody); - JSONObject response = JSONUtil.parseObj(responseBody); - if (response.getInt("state", 0) != 200) { - log.warn("学校OA待办创建失败响应,taskId={},receiveCode={},response={}", task.getId(), actor.getActorAccount(), responseBody); - throw new BaseException("学校OA待办创建失败:{}", response.getStr("message", responseBody)); - } - log.info("学校OA待办创建成功,taskId={},receiveCode={},response={}", task.getId(), actor.getActorAccount(), responseBody); - } catch (BaseException e) { - throw e; - } catch (Exception e) { - log.error("学校OA待办创建异常,url={},taskId={},processInstanceId={},receiveCode={},body={}", - insertUrl, task.getId(), instance.getId(), actor.getActorAccount(), requestBody, e); - throw new BaseException("学校OA待办创建异常:{}", e.getMessage()); - } + String responseBody = sendTodoRequest(insertUrl, body, "学校OA待办创建失败", "学校OA待办创建异常"); + log.info("学校OA待办创建成功,taskId={},receiveCode={},response={}", task.getId(), actor.getActorAccount(), responseBody); } /** @@ -309,9 +306,8 @@ public class SchoolOaTodoServiceImpl implements SchoolOaTodoService { } String uniqueId = task.getId() + "_" + actor.getActorId(); - String updateUrl = requireConfig("school-oa.todo.update-url").replace("{uniqueId}", uniqueId); - String responseBody = sendTodoRequest(updateUrl, "学校OA待办完成失败", "学校OA待办完成异常"); - log.info("学校OA待办完成成功,taskId={},receiveCode={},uniqueId={},response={}", task.getId(), actor.getActorAccount(), uniqueId, responseBody); + completeTodoByUniqueId(uniqueId); + log.info("学校OA待办完成成功,taskId={},receiveCode={},uniqueId={}", task.getId(), actor.getActorAccount(), uniqueId); } /** @@ -399,79 +395,6 @@ public class SchoolOaTodoServiceImpl implements SchoolOaTodoService { return baseUrl + separator + "taskId=" + task.getId() + "&instanceId=" + task.getProcessInstanceId(); } - /** - * 按学校OA固定待办格式创建福利选择提醒,标题承载自定义提醒内容。 - */ - 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())); - body.set("title", title); - body.set("stepName", "福利选择提醒"); - body.set("creatorCode", creator.getLoginname()); - body.set("creatorName", creator.getUsername()); - body.set("creatorTime", creatorTime); - body.set("pcUrl", pcUrl); - body.set("appUrl", appUrl); - body.set("pcPreviewUrl", pcUrl); - body.set("appPreviewUrl", appUrl); - // 同一项目和用户固定使用一个唯一值,确保学校OA最多存在一条福利提醒待办。 - body.set("uniqueId", uniqueId); - body.set("receiveCode", receiver.getLoginname()); - body.set("receiveName", receiver.getUsername()); - body.set("receiveTime", creatorTime); - - String insertUrl = requireConfig("school-oa.todo.insert-url"); - String requestBody = JSONUtil.toJsonStr(body); - log.info("学校OA福利选择提醒待办创建请求,url={},projectId={},receiveCode={},body={}", - insertUrl, projectId, receiver.getLoginname(), requestBody); - 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; - } - - /** - * 调用学校OA插入待办接口并返回原始响应;网络和解析异常统一转换为业务异常。 - */ - private JSONObject executeWelfareReminderTodoRequest(String insertUrl, String requestBody, String projectId, String receiverLoginName) { - try { - HttpRequest request = HttpUtil.createPost(insertUrl); - setTodoRequestHeaders(request); - request.body(requestBody); - String responseBody = request.execute().body(); - log.info("学校OA福利选择提醒待办创建响应,projectId={},receiveCode={},response={}", - projectId, receiverLoginName, responseBody); - return JSONUtil.parseObj(responseBody); - } catch (Exception e) { - log.error("学校OA福利选择提醒待办创建异常,url={},projectId={},receiveCode={},body={}", - insertUrl, projectId, receiverLoginName, requestBody, e); - throw new BaseException("学校OA福利选择提醒待办创建异常:{}", e.getMessage()); - } - } - - /** 同一项目和用户共用固定流程标识。 */ - private String buildWelfareReminderFlowId(String projectId, String userId) { - return "welfare_reminder_" + projectId + "_" + userId; - } - - /** 同一项目和用户固定使用一个待办唯一值。 */ - private String buildWelfareReminderUniqueId(String projectId, String userId) { - return buildWelfareReminderFlowId(projectId, userId); - } - private String buildAppTaskUrl(String h5FormKey, ProcessTask task) { // 部分流程没有配置 H5 审核页,学校 OA 的移动端地址为空时按要求传固定域名兜底。 if (StrUtil.isBlank(h5FormKey)) { 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 10b691b..79bc1da 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 @@ -107,6 +107,7 @@ public class WelfareSelectionSituationController { } @At + @Aop(TransAop.READ_COMMITTED) @SLog(type = "welfare", tag = "选择情况", msg = "发送福利选择提醒") @SaCheckPermission("welfare.selection.situation") @ApiOperation("发送福利选择提醒") diff --git a/src/main/java/com/budwk/app/zhgh/welfare/model/WelfareReminderMessage.java b/src/main/java/com/budwk/app/zhgh/welfare/model/WelfareReminderMessage.java new file mode 100644 index 0000000..2396774 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/welfare/model/WelfareReminderMessage.java @@ -0,0 +1,121 @@ +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; + +/** + * 福利选择学校统一消息发送记录。 + * 每次提醒批次中的每位接收人各保存一条记录,用于回写统一消息平台的最终成功或失败结果。 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Table("welfare_reminder_message") +@Comment("福利选择学校统一消息发送记录") +@TableIndexes({ + @Index(name = "UK_WELFARE_REMINDER_MESSAGE_BATCH_USER", fields = {"messageBatchId", "userId"}, unique = true), + @Index(name = "IDX_WELFARE_REMINDER_MESSAGE_PROJECT_STATUS", fields = {"projectId", "messageStatus"}, unique = false) +}) +public class WelfareReminderMessage extends BaseModel { + + /** 已提交统一消息平台,等待查询最终结果。 */ + public static final int STATUS_WAITING_RESULT = 0; + /** 统一消息平台返回该接收人发送成功。 */ + public static final int STATUS_SUCCESS = 1; + /** 统一消息平台返回该接收人发送失败,或发送前参数校验失败。 */ + public static final int STATUS_FAILED = 2; + /** 统一消息结果查询异常,尚未能确认接收人的最终发送状态。 */ + public static final int STATUS_RESULT_QUERY_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 messageBatchId; + + @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("统一消息平台消息标识,多个标识以英文逗号分隔") + @ColDefine(type = ColType.VARCHAR, width = 500) + private String messageId; + + @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 = 100) + private String title; + + @Column + @Comment("消息内容") + @ColDefine(type = ColType.VARCHAR, width = 1000) + private String content; + + @Column + @Comment("状态:0等待结果、1成功、2失败、3结果查询异常") + @ColDefine(type = ColType.INT) + private Integer messageStatus; + + @Column + @Comment("提交到统一消息平台时间") + @ColDefine(type = ColType.DATETIME) + private Date sendTime; + + @Column + @Comment("查询到最终结果时间") + @ColDefine(type = ColType.DATETIME) + private Date resultTime; + + @Column + @Comment("发送或结果查询失败原因") + @ColDefine(type = ColType.VARCHAR, width = 1000) + private String failMessage; +} diff --git a/src/main/java/com/budwk/app/zhgh/welfare/service/impl/WelfareSelectionSituationServiceImpl.java b/src/main/java/com/budwk/app/zhgh/welfare/service/impl/WelfareSelectionSituationServiceImpl.java index 3f7f02f..4711484 100644 --- a/src/main/java/com/budwk/app/zhgh/welfare/service/impl/WelfareSelectionSituationServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/welfare/service/impl/WelfareSelectionSituationServiceImpl.java @@ -6,14 +6,21 @@ import cn.afterturn.easypoi.excel.entity.ExportParams; import cn.afterturn.easypoi.excel.entity.enmus.ExcelType; import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity; import cn.afterturn.easypoi.excel.export.ExcelExportService; +import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; import com.budwk.app.base.constant.RoleConstant; import com.budwk.app.base.page.Pagination; import com.budwk.app.base.service.impl.BaseServiceImpl; import com.budwk.app.base.utils.CommonDownloadUtil; +import com.budwk.app.base.utils.ManyAddOrRenewUtil; import com.budwk.app.base.utils.PageUtil; //import com.budwk.app.sys.models.Sys_file; import com.budwk.app.base.sms.SmsService; +import com.budwk.app.flow.param.SchoolOaTodoBatchCreateParam; +import com.budwk.app.flow.param.SchoolOaTodoReceiverParam; import com.budwk.app.flow.service.SchoolOaTodoService; import com.budwk.app.sys.models.Sys_user; import com.budwk.app.sys.services.SysFileService; @@ -25,6 +32,7 @@ import com.budwk.app.zhgh.dayofficework.message.strategy.impl.LocalMessageSendSt import com.budwk.app.zhgh.welfare.model.WelfareList; import com.budwk.app.zhgh.welfare.model.WelfareProject; import com.budwk.app.zhgh.welfare.model.WelfareProjectSubjectOption; +import com.budwk.app.zhgh.welfare.model.WelfareReminderMessage; import com.budwk.app.zhgh.welfare.model.WelfareReminderTodo; import com.budwk.app.zhgh.welfare.model.WelfareUserSelection; import com.budwk.app.zhgh.welfare.param.WelfareSelectionSituationPageForm; @@ -37,8 +45,11 @@ import org.nutz.dao.Cnd; import org.nutz.dao.Dao; import org.nutz.dao.Sqls; import org.nutz.dao.sql.Sql; +import org.nutz.aop.interceptor.ioc.TransAop; +import org.nutz.ioc.aop.Aop; import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.lang.random.R; import org.nutz.lang.util.NutMap; import javax.imageio.ImageIO; @@ -65,6 +76,8 @@ public class WelfareSelectionSituationServiceImpl extends BaseServiceImpl recipientIds = getReminderRecipientIds(pageForm, userIds); if (recipientIds.isEmpty()) { - return buildReminderResult(0, 0, 0, 0, 0, - Boolean.TRUE.equals(Globals.MyConfig.getBoolean("AppSchoolOa"))); + return buildReminderResult(0, 0, 0, 0, 0, 0, + Boolean.TRUE.equals(Globals.MyConfig.getBoolean("AppSms")), + Boolean.TRUE.equals(Globals.MyConfig.getBoolean("AppSchoolOa")), 0, 0); } // 各消息渠道统一使用福利业务标题,学校OA再拼接自定义正文作为待办标题。 String title = "智慧工会职工福利:"; @@ -183,20 +198,41 @@ public class WelfareSelectionSituationServiceImpl extends BaseServiceImpl receiverUsers = dao().query(Sys_user.class, Cnd.where(Sys_user::getId, "in", recipientIds)); + boolean messageEnabled = Boolean.TRUE.equals(Globals.MyConfig.getBoolean("AppSms")); + boolean todoEnabled = Boolean.TRUE.equals(Globals.MyConfig.getBoolean("AppSchoolOa")); + String senderId = SecurityUtil.getUserId(); + Sys_user sender = null; + if (messageEnabled || todoEnabled) { + sender = dao().fetch(Sys_user.class, senderId); + if (sender == null || StrUtil.hasBlank(sender.getId(), sender.getLoginname(), sender.getUsername())) { + throw new IllegalStateException("福利选择提醒创建人信息不完整"); + } + } + + int messageSuccessCount = 0; + int messageFailedCount = 0; + int messageResultQueryFailedCount = 0; // AppSms 控制学校消息平台发送,消息链接跳转到手机端福利选择列表。 - if (Boolean.TRUE.equals(Globals.MyConfig.getBoolean("AppSms")) && !receiverUsers.isEmpty()) { - smsService.massSendByUsers(receiverUsers, title, content, pcUrl, appUrl); + if (messageEnabled) { + NutMap messageResult = sendReminderMessageBatch(pageForm.getProjectId(), recipientIds, receiverUsers, + sender, title, content, pcUrl, appUrl); + messageSuccessCount = messageResult.getInt("successCount", 0); + messageFailedCount = messageResult.getInt("failedCount", 0); + messageResultQueryFailedCount = messageResult.getInt("resultQueryFailedCount", 0); } - boolean todoEnabled = Boolean.TRUE.equals(Globals.MyConfig.getBoolean("AppSchoolOa")); int todoSuccessCount = 0; int existingTodoCount = 0; int todoFailedCount = 0; if (todoEnabled) { Map receiverUserMap = receiverUsers.stream() .collect(Collectors.toMap(Sys_user::getId, user -> user, (left, right) -> left)); + Map reminderTodoMap = getReminderTodoMap(pageForm.getProjectId(), recipientIds); + List newSendingReminderTodos = new ArrayList<>(); + List retrySendingReminderTodos = new ArrayList<>(); + List sendingReceiverIds = new ArrayList<>(); for (String recipientId : recipientIds) { - WelfareReminderTodo reminderTodo = getReminderTodo(pageForm.getProjectId(), recipientId); + WelfareReminderTodo reminderTodo = reminderTodoMap.get(recipientId); if (isExistingReminderTodo(reminderTodo)) { existingTodoCount++; continue; @@ -204,21 +240,15 @@ public class WelfareSelectionSituationServiceImpl extends BaseServiceImpl recipientIds, List receiverUsers, + Sys_user sender, String title, String content, String pcUrl, String mobileUrl) { + if (StrUtil.hasBlank(projectId, title, content) || recipientIds.isEmpty() + || sender == null || StrUtil.hasBlank(sender.getId(), sender.getLoginname(), sender.getUsername())) { + throw new IllegalArgumentException("福利选择统一消息批量发送参数不完整"); + } + if (title.length() > 100) { + throw new IllegalArgumentException("福利选择统一消息标题长度不能超过100个字符"); + } + + String senderId = sender.getId(); + String messageBatchId = R.UU32(); + long now = System.currentTimeMillis(); + Map receiverUserMap = receiverUsers.stream() + .collect(Collectors.toMap(Sys_user::getId, user -> user, (left, right) -> left)); + List records = new ArrayList<>(); + List validRecords = new ArrayList<>(); + List validUsers = new ArrayList<>(); + for (String recipientId : recipientIds) { + Sys_user receiver = receiverUserMap.get(recipientId); + WelfareReminderMessage record = createReminderMessageRecord(messageBatchId, projectId, recipientId, + receiver, sender, title, content, now); + records.add(record); + if (receiver == null || StrUtil.hasBlank(receiver.getLoginname(), receiver.getUsername())) { + record.setMessageStatus(WelfareReminderMessage.STATUS_FAILED); + record.setFailMessage("学校统一消息发送失败:接收人工号或姓名为空"); + } else { + validRecords.add(record); + validUsers.add(receiver); + } + } + log.info("福利选择统一消息记录已在内存组装完成,projectId={},messageBatchId={},接收人总数={},可发送人数={},接收人信息失败人数={}", + projectId, messageBatchId, records.size(), validUsers.size(), records.size() - validUsers.size()); + + if (validUsers.isEmpty()) { + persistReminderMessageRecordsAsync(records, projectId, messageBatchId); + return NutMap.NEW().addv("successCount", 0).addv("failedCount", records.size()).addv("resultQueryFailedCount", 0); + } + + List messageIds; + String messageIdText; + try { + messageIds = smsService.massSendByUsers(validUsers, title, content, pcUrl, mobileUrl); + if (messageIds == null || messageIds.isEmpty()) { + throw new IllegalStateException("学校统一消息平台未返回消息标识"); + } + messageIdText = String.join(",", messageIds); + Date sendTime = new Date(); + validRecords.forEach(record -> { + record.setMessageId(messageIdText); + record.setMessageStatus(WelfareReminderMessage.STATUS_WAITING_RESULT); + record.setSendTime(sendTime); + record.setFailMessage(null); + record.setUpdatedBy(senderId); + record.setUpdatedAt(System.currentTimeMillis()); + }); + log.info("福利选择统一消息批量发送已入队,等待结果查询后异步写入记录,projectId={},messageBatchId={},messageIds={},接收人数={}", + projectId, messageBatchId, messageIds, validRecords.size()); + } catch (Exception e) { + markReminderMessageRecords(validRecords, senderId, WelfareReminderMessage.STATUS_FAILED, + null, null, limitFailureMessage(e.getMessage())); + persistReminderMessageRecordsAsync(records, projectId, messageBatchId); + log.error("福利选择统一消息批量发送失败,已提交异步入库记录,projectId={},messageBatchId={},接收人数={},失败原因={}", + projectId, messageBatchId, validRecords.size(), e.getMessage(), e); + return NutMap.NEW().addv("successCount", 0).addv("failedCount", records.size()).addv("resultQueryFailedCount", 0); + } + + try { + Map failedMessageByCode = queryFailedMessageByReceiverCode(messageIds); + Date resultTime = new Date(); + for (WelfareReminderMessage record : validRecords) { + String failMessage = failedMessageByCode.get(record.getReceiverCode()); + record.setMessageStatus(StrUtil.isBlank(failMessage) + ? WelfareReminderMessage.STATUS_SUCCESS : WelfareReminderMessage.STATUS_FAILED); + record.setResultTime(resultTime); + record.setFailMessage(failMessage); + record.setUpdatedBy(senderId); + record.setUpdatedAt(System.currentTimeMillis()); + } + int externalFailedCount = (int) validRecords.stream() + .filter(record -> failedMessageByCode.containsKey(record.getReceiverCode())) + .count(); + int failedCount = records.size() - validRecords.size() + externalFailedCount; + int successCount = validRecords.size() - externalFailedCount; + persistReminderMessageRecordsAsync(records, projectId, messageBatchId); + log.info("福利选择统一消息结果查询完成,已提交异步入库记录,projectId={},messageBatchId={},成功人数={},失败人数={},messageIds={}", + projectId, messageBatchId, successCount, failedCount, messageIds); + return NutMap.NEW().addv("successCount", successCount).addv("failedCount", failedCount).addv("resultQueryFailedCount", 0); + } catch (Exception e) { + markReminderMessageRecords(validRecords, senderId, WelfareReminderMessage.STATUS_RESULT_QUERY_FAILED, + messageIdText, new Date(), limitFailureMessage(e.getMessage())); + persistReminderMessageRecordsAsync(records, projectId, messageBatchId); + log.error("福利选择统一消息结果查询异常,已提交异步入库记录,projectId={},messageBatchId={},messageIds={},接收人数={},失败原因={}", + projectId, messageBatchId, messageIds, validRecords.size(), e.getMessage(), e); + return NutMap.NEW().addv("successCount", 0).addv("failedCount", records.size() - validRecords.size()) + .addv("resultQueryFailedCount", validRecords.size()); + } + } + + /** + * 创建统一消息接收人记录,并显式补齐fastInsert不会执行的主键和审计字段。 + */ + private WelfareReminderMessage createReminderMessageRecord(String messageBatchId, String projectId, String userId, + Sys_user receiver, Sys_user sender, String title, + String content, long now) { + WelfareReminderMessage record = new WelfareReminderMessage(); + record.setId(R.UU32()); + record.setMessageBatchId(messageBatchId); + record.setProjectId(projectId); + record.setUserId(userId); + if (receiver != null) { + record.setReceiverCode(receiver.getLoginname()); + record.setReceiverName(receiver.getUsername()); + } + record.setSenderId(sender.getId()); + record.setSenderCode(sender.getLoginname()); + record.setSenderName(sender.getUsername()); + record.setTitle(limitText(title, 100)); + record.setContent(limitText(content, 1000)); + record.setMessageStatus(WelfareReminderMessage.STATUS_WAITING_RESULT); + record.setCreatedBy(sender.getId()); + record.setCreatedAt(now); + record.setUpdatedBy(sender.getId()); + record.setUpdatedAt(now); + record.setDelFlag(false); + return record; + } + + /** + * 查询所有消息标识的结果并汇总失败用户工号与失败原因。 + * 当前配置仅使用一个发送渠道,通常只返回一个消息标识;多标识场景仍逐个查询并按工号合并失败结果。 + */ + private Map queryFailedMessageByReceiverCode(List messageIds) { + Map failedMessageByCode = new HashMap<>(); + for (String messageId : messageIds) { + NutMap messageResult = smsService.getMessageResult(messageId); + Object failedUsers = messageResult.get("failedUsers"); + if (failedUsers instanceof JSONArray failedUserArray) { + for (Object failedUserValue : failedUserArray) { + JSONObject failedUser = JSONUtil.parseObj(failedUserValue); + String userCode = failedUser.getStr("userCode"); + if (StrUtil.isNotBlank(userCode)) { + failedMessageByCode.put(userCode, limitFailureMessage(failedUser.getStr("errorInfo"))); + } + } + } + if ("0".equals(messageResult.getString("resultState")) && failedMessageByCode.isEmpty()) { + throw new IllegalStateException("学校统一消息平台返回发送失败,但未返回失败人员明细,messageId=" + messageId); + } + } + return failedMessageByCode; + } + + /** + * 在内存中更新一批统一消息记录状态;所有结果明确后再统一异步插入,避免异步插入和同步更新发生竞争。 + */ + private void markReminderMessageRecords(List records, String senderId, int messageStatus, + String messageId, Date resultTime, String failMessage) { + if (records.isEmpty()) { + return; + } + Date sendTime = messageId == null ? null : new Date(); + records.forEach(record -> { + if (messageId != null) { + record.setMessageId(messageId); + record.setSendTime(sendTime); + } + record.setMessageStatus(messageStatus); + record.setResultTime(resultTime); + record.setFailMessage(failMessage); + record.setUpdatedBy(senderId); + record.setUpdatedAt(System.currentTimeMillis()); + }); + } + + /** + * 使用项目线程池异步分批插入最终消息记录。记录已显式设置主键和审计字段, + * 因此可安全使用fastInsert;本方法只提交任务,不等待数据库写入完成。 + */ + private void persistReminderMessageRecordsAsync(List records, String projectId, String messageBatchId) { + if (records.isEmpty()) { + return; + } + manyAddOrRenewUtil.asyncExecuteFastInsert(records, 200); + log.info("福利选择统一消息记录已提交异步批量入库,projectId={},messageBatchId={},记录数={},batchSize={}", + projectId, messageBatchId, records.size(), 200); + } + /** * 查询项目和用户对应的唯一提醒待办记录。 */ @@ -264,6 +527,24 @@ public class WelfareSelectionSituationServiceImpl extends BaseServiceImpl reminderTodo, + (left, right) -> left)); + } + /** * 发送中、待办和已办记录均表示该项目已创建过OA待办,重复提醒时不再创建第二条。 * 发送失败记录允许沿用原唯一值重试。 @@ -288,24 +569,31 @@ public class WelfareSelectionSituationServiceImpl extends BaseServiceImpl newRecords, + List retryRecords, String senderId) { + if (!newRecords.isEmpty()) { + dao().fastInsert(newRecords); + log.info("福利选择提醒待办批量新增记录完成,projectId={},新增数量={},状态={}", + projectId, newRecords.size(), WelfareReminderTodo.STATUS_SENDING); + } + if (!retryRecords.isEmpty()) { + dao().update(retryRecords, + "oaFlowId|oaUniqueId|receiverCode|receiverName|senderId|senderCode|senderName|title|content|todoStatus|sendTime|finishTime|failMessage|updatedBy|updatedAt"); + log.info("福利选择提醒待办批量重试记录更新完成,projectId={},重试数量={},状态={},操作人={}", + projectId, retryRecords.size(), WelfareReminderTodo.STATUS_SENDING, senderId); + } + } + + /** + * 按本批用户统一更新待办状态。成功时写入发送时间,失败时写入统一失败原因。 + */ + private int updateReminderTodoBatchStatus(String projectId, List userIds, String senderId, + int todoStatus, Date sendTime, String failMessage) { + if (StrUtil.isBlank(projectId) || userIds.isEmpty()) { + return 0; + } + Chain chain = Chain.make("todoStatus", todoStatus) + .add("sendTime", sendTime) + .add("finishTime", null) + .add("failMessage", failMessage) + .add("updatedBy", senderId) + .add("updatedAt", System.currentTimeMillis()); + return dao().update(WelfareReminderTodo.class, chain, + Cnd.where(WelfareReminderTodo::getProjectId, "=", projectId) + .and(WelfareReminderTodo::getUserId, "in", userIds)); + } + /** * 保存OA待办发送失败原因;若发送前校验失败导致记录尚未创建,则补建一条失败记录。 */ @@ -336,7 +658,7 @@ public class WelfareSelectionSituationServiceImpl extends BaseServiceImpl 0) { + summary.append(",学校统一消息发送失败").append(messageFailedCount).append("人"); + } + if (messageResultQueryFailedCount > 0) { + summary.append(",学校统一消息结果查询异常").append(messageResultQueryFailedCount).append("人"); + } + } else { + summary.append(",学校统一消息开关未开启"); + } if (todoEnabled) { summary.append(",OA待办发送成功").append(todoSuccessCount).append("人"); if (existingTodoCount > 0) { @@ -379,7 +713,11 @@ public class WelfareSelectionSituationServiceImpl extends BaseServiceImpl maxLength ? safeText.substring(0, maxLength) : safeText; } + /** + * 组装福利选择的学校OA批量待办参数。 + * 公共OA服务只负责校验参数和调用接口;福利项目标识、标题、链接及接收人信息均在此处按福利业务规则生成。 + * + * @param projectId 福利项目ID,用于生成学校OA批次流程标识和每个接收人的唯一值 + * @param title 学校OA待办标题,包含福利提醒正文 + * @param pcUrl 福利选择PC端跳转地址 + * @param appUrl 福利选择移动端跳转地址 + * @param sender 发起提醒的系统用户,提供学校OA创建人工号和姓名 + * @param receiverIds 本次实际发送的接收人ID集合 + * @param receiverUserMap 接收人ID与系统用户信息的映射 + * @return 可直接传给学校OA批量待办接口的参数对象 + */ + private SchoolOaTodoBatchCreateParam buildWelfareReminderBatchTodoParam(String projectId, String title, + String pcUrl, String appUrl, Sys_user sender, + List receiverIds, + Map receiverUserMap) { + if (StrUtil.hasBlank(projectId, title, pcUrl, appUrl) + || sender == null || StrUtil.hasBlank(sender.getLoginname(), sender.getUsername()) + || receiverIds == null || receiverIds.isEmpty()) { + throw new IllegalArgumentException("学校OA福利选择提醒批量待办参数不完整"); + } + + String sendTime = DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss"); + List userList = new ArrayList<>(); + for (String receiverId : receiverIds) { + Sys_user receiver = receiverUserMap.get(receiverId); + if (receiver == null || StrUtil.hasBlank(receiver.getLoginname(), receiver.getUsername())) { + throw new IllegalArgumentException("学校OA福利选择提醒接收人信息不完整,userId=" + receiverId); + } + SchoolOaTodoReceiverParam receiverParam = new SchoolOaTodoReceiverParam(); + receiverParam.setPcUrl(pcUrl); + receiverParam.setAppUrl(appUrl); + receiverParam.setUniqueId(buildWelfareReminderUniqueId(projectId, receiverId)); + receiverParam.setReceiveCode(receiver.getLoginname()); + receiverParam.setReceiveName(receiver.getUsername()); + receiverParam.setReceiveTime(sendTime); + userList.add(receiverParam); + } + + SchoolOaTodoBatchCreateParam todoParam = new SchoolOaTodoBatchCreateParam(); + todoParam.setFlowTypeName("福利选择"); + todoParam.setFlowId(buildWelfareReminderBatchFlowId(projectId)); + todoParam.setTitle(title); + todoParam.setStepName("福利选择提醒"); + todoParam.setCreatorCode(sender.getLoginname()); + todoParam.setCreatorName(sender.getUsername()); + todoParam.setCreatorTime(sendTime); + todoParam.setStartPcPreviewUrl(pcUrl); + todoParam.setStartAppPreviewUrl(appUrl); + todoParam.setUserList(userList); + return todoParam; + } + + /** 同一项目的一批福利提醒共用学校OA流程标识,接收人通过uniqueId区分。 */ + private String buildWelfareReminderBatchFlowId(String projectId) { + return "welfare_reminder_" + projectId; + } + /** 同一项目和用户固定使用一个OA待办唯一值。 */ private String buildWelfareReminderUniqueId(String projectId, String userId) { return "welfare_reminder_" + projectId + "_" + userId; diff --git a/src/main/resources/views/platform/zhgh/staffbenefit/condolence/apply/index.html b/src/main/resources/views/platform/zhgh/staffbenefit/condolence/apply/index.html index fbd20a8..8924295 100644 --- a/src/main/resources/views/platform/zhgh/staffbenefit/condolence/apply/index.html +++ b/src/main/resources/views/platform/zhgh/staffbenefit/condolence/apply/index.html @@ -167,7 +167,7 @@ layout("/layouts/platform.html"){ -