This commit is contained in:
=
2026-08-26 15:41:02 +08:00
parent d5fe018988
commit d1e11363b1
12 changed files with 450 additions and 72 deletions
@@ -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<String> 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提醒待办。
@@ -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<String> 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) {
@@ -89,6 +89,7 @@ public class ClubRegistApplyController {
public Result submit(@Valid @Param("club") SysClub club,
@Param("::deleteIds") List<String> deleteIds,
@Param("::managePerson") List<NutMap> managePerson) {
sysClubService.validateRequiredFiles(club);
sysClubService.validateManagePerson(managePerson);
SysClub sysClub;
if (StrUtil.isBlank(club.getId())) {
@@ -119,6 +120,7 @@ public class ClubRegistApplyController {
@Param("::deleteIds") List<String> deleteIds,
@Param("::managePerson") List<NutMap> managePerson,
@Param("taskId") Long taskId) {
sysClubService.validateRequiredFiles(club);
sysClubService.validateManagePerson(managePerson);
if (StrUtil.isBlank(club.getId())) {
sysClubService.doAdd(club, managePerson);
@@ -45,6 +45,13 @@ public interface SysClubService extends BaseService<SysClub> {
*/
void validateManagePerson(List<NutMap> managePerson);
/**
* 校验协会正式提交时申请成立报告和章程草案是否已上传。
*
* @param club 协会申请信息
*/
void validateRequiredFiles(SysClub club);
/**
* 下载协会申请成立报告模板。
*
@@ -273,6 +273,17 @@ public class SysClubServiceImpl extends BaseServiceImpl<SysClub> 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<ClubRegisterPageVo> minePageData(ClubUserPageForm pageForm) {
Sql sql = Sqls.create("""
@@ -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);
}
@@ -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;
}
@@ -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<WelfareLis
void receiveXlsx(WelfareSelectionSituationPageForm pageForm, HttpServletResponse response);
/**
* 统计当前筛选范围内可接收提醒的人员数量
* 预览当前筛选范围内的提醒发送情况
*
* @param pageForm 当前页面筛选条件
* @param userIds 已勾选的人员ID;为空时按筛选条件查询全部人员
* @return 去重后的接收人数量
* @return 包含接收人数、预计新增OA待办数、已有OA待办数及OA开关状态的预览结果
*/
int countReminderRecipients(WelfareSelectionSituationPageForm pageForm, String[] userIds);
NutMap previewReminder(WelfareSelectionSituationPageForm pageForm, String[] userIds);
/**
* 向当前筛选范围内的人员发送福利选择提醒。
@@ -29,9 +30,9 @@ public interface WelfareSelectionSituationService extends BaseService<WelfareLis
* @param pageForm 当前页面筛选条件
* @param userIds 已勾选的人员ID;为空时按筛选条件查询全部人员
* @param content 自定义提醒内容
* @return 实际发送的去重后接收人数量
* @return 包含消息成功数、OA待办成功数、已有待办跳过数、OA待办失败数及提示文案的发送结果
*/
int sendReminder(WelfareSelectionSituationPageForm pageForm, String[] userIds, String content);
NutMap sendReminder(WelfareSelectionSituationPageForm pageForm, String[] userIds, String content);
/**
* 用户完成福利选择后将对应的学校OA提醒待办改为已办。
@@ -25,6 +25,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.WelfareReminderTodo;
import com.budwk.app.zhgh.welfare.param.WelfareSelectionSituationPageForm;
import com.budwk.app.zhgh.welfare.service.WelfareSelectionSituationService;
import lombok.extern.slf4j.Slf4j;
@@ -125,15 +126,23 @@ public class WelfareSelectionSituationServiceImpl extends BaseServiceImpl<Welfar
}
@Override
public int countReminderRecipients(WelfareSelectionSituationPageForm pageForm, String[] userIds) {
return getReminderRecipientIds(pageForm, userIds).size();
public NutMap previewReminder(WelfareSelectionSituationPageForm pageForm, String[] userIds) {
List<String> 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<String> 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<Welfar
if (Boolean.TRUE.equals(Globals.MyConfig.getBoolean("AppSms")) && !receiverUsers.isEmpty()) {
smsService.massSendByUsers(receiverUsers, title, content, pcUrl, appUrl);
}
// AppSchoolOa 控制学校OA福利提醒待办创建,待办与接收人按项目保持唯一。
if (Boolean.TRUE.equals(Globals.MyConfig.getBoolean("AppSchoolOa"))) {
schoolOaTodoService.createWelfareReminderTodos(pageForm.getProjectId(), title + content,
pcUrl, appUrl, SecurityUtil.getUserId(), recipientIds);
boolean todoEnabled = Boolean.TRUE.equals(Globals.MyConfig.getBoolean("AppSchoolOa"));
int todoSuccessCount = 0;
int existingTodoCount = 0;
int todoFailedCount = 0;
if (todoEnabled) {
Map<String, Sys_user> 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<String> getExistingReminderTodoUserIds(String projectId, List<String> 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;
}
/**
* 使用系统配置的绝对域名构造学校外部平台可访问的福利选择地址。
*/
@@ -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 {
@@ -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) {
@@ -126,7 +126,9 @@ layout("/layouts/platform.html"){
</el-dialog>
<el-dialog title="发送福利选择提醒" :visible.sync="reminderVisible" width="500px" :close-on-click-modal="false">
<el-alert :title="'发送模式:' + reminderModeText + ';本次将向' + reminderCount + '人发送消息'" type="warning" :closable="false"></el-alert>
<el-alert :title="'发送模式:' + reminderModeText + ';本次将向' + reminderCount + '人发送消息,预计新增OA待办' + reminderNewTodoCount + '条'" type="warning" :closable="false"></el-alert>
<el-alert v-if="reminderExistingTodoCount > 0" class="mt10" :title="'已有' + reminderExistingTodoCount + '人存在OA待办,本次仅发送消息,不重复创建待办'" type="info" :closable="false"></el-alert>
<el-alert v-if="!reminderTodoEnabled" class="mt10" title="学校OA待办开关未开启,本次只发送消息" type="info" :closable="false"></el-alert>
<el-form label-width="80px" class="mt20">
<el-form-item label="提醒内容">
<el-input v-model="reminderContent" type="textarea" :rows="5" maxlength="500" show-word-limit placeholder="请输入提醒内容"></el-input>
@@ -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)
},
// 管理员待选