Browse Source

工作通知接入

master
luoweijian 1 month ago
parent
commit
63ad4621e6
  1. 9
      src/main/java/com/project/exam/domain/service/NotifyExamRecordDomainService.java
  2. 61
      src/main/java/com/project/exam/domain/service/impl/NotifyExamRecordDomainServiceImpl.java
  3. 8
      src/main/java/com/project/exam/domain/service/impl/SubmitPaperDomainServiceImpl.java
  4. 46
      src/main/java/com/project/task/domain/job/TaskNotifyJob.java
  5. 8
      src/main/java/com/project/task/domain/service/NotifyTaskDomainService.java
  6. 159
      src/main/java/com/project/task/domain/service/impl/NotifyTaskDomainServiceImpl.java
  7. 54
      src/main/resources/logback-spring.xml

9
src/main/java/com/project/exam/domain/service/NotifyExamRecordDomainService.java

@ -0,0 +1,9 @@
package com.project.exam.domain.service;
import com.project.exam.domain.dto.ExamRecordDTO;
public interface NotifyExamRecordDomainService {
void notifyExamRecord(ExamRecordDTO dto);
}

61
src/main/java/com/project/exam/domain/service/impl/NotifyExamRecordDomainServiceImpl.java

@ -0,0 +1,61 @@
package com.project.exam.domain.service.impl;
import com.github.tingyugetc520.ali.dingtalk.api.DtService;
import com.github.tingyugetc520.ali.dingtalk.bean.message.DtCorpConversationMessage;
import com.github.tingyugetc520.ali.dingtalk.bean.message.DtCorpConversationMsgSendResult;
import com.github.tingyugetc520.ali.dingtalk.bean.message.DtMessage;
import com.project.ding.utils.SecurityUtils;
import com.project.exam.domain.dto.ExamRecordDTO;
import com.project.exam.domain.service.NotifyExamRecordDomainService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Collections;
@Service
@Slf4j
public class NotifyExamRecordDomainServiceImpl implements NotifyExamRecordDomainService {
@Autowired
private DtService dtService;
@Override
public void notifyExamRecord(ExamRecordDTO dto) {
String userId = SecurityUtils.getUserId();
// 不阻碍正常业务
try {
String markdown = String.format("### 【%s】考试成绩已公布,请留意查收\n\n", dto.getTaskDTO().getName()) +
String.format("考试成绩:%.2f\n\n", dto.getScore()) +
String.format("考试结果:%s\n\n", dto.getPass() ? "考试通过" : "考试不通过") +
String.format("%s\n\n", dto.getPass() ? "恭喜考试通过,请继续保持!可以自行查看考试结果" :
"遗憾考试不通过,请再接再厉!可以查看考试结果或尝试重新补考");
DtCorpConversationMessage message = DtCorpConversationMessage.builder()
.agentId(dtService.getDtConfigStorage().getAgentId())
.userIds(Collections.singletonList(userId))
.msg(DtMessage.ACTIONCARD()
.title("产品知识考核")
.markdown(markdown)
.singleTitle("查看详情")
// todo 修改为对应页面
.singleUrl("https://www.baidu.com/").build())
.build();
DtCorpConversationMsgSendResult result = dtService.getCorpConversationMsgService().send(message);
if (result.getErrCode() == 0) {
log.info(">>> [成绩公布通知] 成功发送通知 ,考试任务名:{} ,用户:{}, ExamRecord:{}", dto.getTaskDTO().getName() , userId , dto.getId()
);
} else {
log.error(">>> [成绩公布通知] 发送失败.考试任务名:{} ,用户:{}, ExamRecord:{}, 错误码: {}, 错误信息: {}", dto.getTaskDTO().getName() , userId , dto.getId() ,
result.getErrCode(), result.getErrMsg());
}
} catch (Exception e) {
log.error(">>> [成绩公布通知] 钉钉接口调用异常, 考试任务名:{} ,用户:{}, ExamRecord:{}, 原因: {}", dto.getTaskDTO().getName() , userId ,
dto.getId() , e.getMessage());
}
}
}

8
src/main/java/com/project/exam/domain/service/impl/SubmitPaperDomainServiceImpl.java

@ -5,6 +5,7 @@ import com.project.base.domain.exception.BusinessErrorException;
import com.project.exam.domain.dto.ExamRecordDTO;
import com.project.exam.domain.entity.ExamRecordEntity;
import com.project.exam.domain.service.BuildExamRecordDomainService;
import com.project.exam.domain.service.NotifyExamRecordDomainService;
import com.project.exam.domain.service.SubmitPaperDomainService;
import com.project.exam.mapper.ExamRecordMapper;
import com.project.task.domain.dto.TaskDTO;
@ -40,6 +41,9 @@ public class SubmitPaperDomainServiceImpl implements SubmitPaperDomainService {
@Autowired
private BuildExamRecordDomainService buildExamRecordDomainService;
@Autowired
private NotifyExamRecordDomainService notifyExamRecordDomainService;
@Override
@Transactional(rollbackFor = Exception.class)
public ExamRecordDTO submitPaper(Long recordId) throws Exception {
@ -83,9 +87,13 @@ public class SubmitPaperDomainServiceImpl implements SubmitPaperDomainService {
// 更新 TaskUser 冗余字段
updateTaskUserStatus(taskUser , isPassed , recordId);
// 构建dto
ExamRecordDTO dto = record.toDTO(ExamRecordDTO::new);
// 后面需要用到这个taskDTO
dto.setTaskDTO(task.toDTO(TaskDTO::new));
// 发送通知
notifyExamRecordDomainService.notifyExamRecord(dto);
return buildExamRecordDomainService.buildDTO(dto);
}

46
src/main/java/com/project/task/domain/job/TaskNotifyJob.java

@ -0,0 +1,46 @@
package com.project.task.domain.job;
import com.project.base.config.ScheduledTaskProperties;
import com.project.task.domain.service.NotifyTaskDomainService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import java.util.Date;
@Service
@Slf4j
public class TaskNotifyJob {
@Autowired
private ScheduledTaskProperties taskProps;
@Autowired
private NotifyTaskDomainService notifyTaskDomainService;
/**
* 每天上午 9:00 执行通知
*/
@Scheduled(cron = "0 0 9 * * ?")
public void run() {
if ("dev".equalsIgnoreCase(taskProps.getOwner())) {
return;
}
log.info(">>> [每日通知任务] 启动执行...");
Date today = new Date();
try {
// 处理新开始的任务
notifyTaskDomainService.notifyNewTasks(today);
// 处理7天截止的任务
notifyTaskDomainService.notifyDeadlineTasks(today);
} catch (Exception e) {
log.error(">>> [每日通知任务] 执行异常", e);
}
log.info(">>> [每日通知任务] 执行完毕。");
}
}

8
src/main/java/com/project/task/domain/service/NotifyTaskDomainService.java

@ -0,0 +1,8 @@
package com.project.task.domain.service;
import java.util.Date;
public interface NotifyTaskDomainService {
void notifyNewTasks(Date today);
void notifyDeadlineTasks(Date today);
}

159
src/main/java/com/project/task/domain/service/impl/NotifyTaskDomainServiceImpl.java

@ -0,0 +1,159 @@
package com.project.task.domain.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.github.tingyugetc520.ali.dingtalk.api.DtService;
import com.github.tingyugetc520.ali.dingtalk.bean.message.DtCorpConversationMessage;
import com.github.tingyugetc520.ali.dingtalk.bean.message.DtCorpConversationMsgSendResult;
import com.github.tingyugetc520.ali.dingtalk.bean.message.DtMessage;
import com.github.tingyugetc520.ali.dingtalk.error.DtErrorException;
import com.project.task.domain.dto.TaskDTO;
import com.project.task.domain.entity.TaskEntity;
import com.project.task.domain.entity.TaskUserEntity;
import com.project.task.domain.enums.TaskUserStatusEnum;
import com.project.task.domain.service.NotifyTaskDomainService;
import com.project.task.mapper.TaskMapper;
import com.project.task.mapper.TaskUserMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
@Service
@Slf4j
public class NotifyTaskDomainServiceImpl implements NotifyTaskDomainService {
@Autowired
private TaskMapper taskMapper;
@Autowired
private TaskUserMapper taskUserMapper;
@Autowired
private DtService dtService;
private final SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日");
@Override
public void notifyNewTasks(Date today) {
// 1. 查找今天 00:00:00 到 23:59:59 之间开始的任务
Date startOfDay = DateUtil.beginOfDay(today);
Date endOfDay = DateUtil.endOfDay(today);
List<TaskDTO> startTasks = taskMapper.selectList(new LambdaQueryWrapper<TaskEntity>()
.between(TaskEntity::getStartTime, startOfDay, endOfDay))
.stream().map(entity -> entity.toDTO(TaskDTO::new)).toList();
for (TaskDTO task : startTasks) {
// 获取该任务下所有考生
List<String> userIds = taskUserMapper.selectList(new LambdaQueryWrapper<TaskUserEntity>()
.select(TaskUserEntity::getUserId)
.eq(TaskUserEntity::getTaskId, task.getId()))
.stream().map(TaskUserEntity::getUserId).toList();
sendBatchNewTasks(userIds , task);
}
}
@Override
public void notifyDeadlineTasks(Date today) {
Date startOfDay = DateUtil.beginOfDay(DateUtil.offsetDay(today , -6));
Date endOfDay = DateUtil.endOfDay(DateUtil.offsetDay(today , -6));
List<TaskDTO> startTasks = taskMapper.selectList(new LambdaQueryWrapper<TaskEntity>()
.between(TaskEntity::getStartTime, startOfDay, endOfDay))
.stream().map(entity -> entity.toDTO(TaskDTO::new)).toList();
for (TaskDTO task : startTasks) {
// 获取该任务下所有未通过考生
List<String> userIds = taskUserMapper.selectList(new LambdaQueryWrapper<TaskUserEntity>()
.select(TaskUserEntity::getUserId)
.in(TaskUserEntity::getStatus , Arrays.asList(TaskUserStatusEnum.Not_Start.getValue() , TaskUserStatusEnum.Fail.getValue()))
.eq(TaskUserEntity::getTaskId, task.getId()))
.stream().map(TaskUserEntity::getUserId).toList();
sendBatchDeadlineTasks(userIds , task);
}
}
private void sendBatchNewTasks(List<String> userIds , TaskDTO taskDTO) {
if (CollUtil.isEmpty(userIds)) {
return;
}
StringBuilder markdown = new StringBuilder();
markdown.append(String.format("### 【%s】已经<font color=#00b500>开始</font>,请按时完成\n\n" , taskDTO.getName()))
.append(String.format("产品线:%s\n\n" , taskDTO.getLineName()))
.append(String.format("开考时间:%s\n\n" ,sdf.format(taskDTO.getStartTime())))
.append(String.format("截止时间:%s\n\n" , sdf.format(taskDTO.getEndTime())))
.append(String.format("注意事项:%s" , taskDTO.getNote()));
ListUtil.partition(userIds, 100).forEach(batch -> {
DtCorpConversationMessage message = DtCorpConversationMessage.builder()
.agentId(dtService.getDtConfigStorage().getAgentId())
.userIds(batch)
.msg(DtMessage.ACTIONCARD()
.title("产品知识考核")
.markdown(markdown.toString())
.singleTitle("查看详情")
// todo 修改为对应页面
.singleUrl("https://www.baidu.com/").build())
.build();
try {
DtCorpConversationMsgSendResult result = dtService.getCorpConversationMsgService().send(message);
if (result.getErrCode() == 0) {
log.info(">>> [考试任务开始通知] 成功发送新任务通知. 任务名: {}, 批次人数: {}, 任务ID: {}",
taskDTO.getName(), batch.size(), result.getTaskId());
} else {
log.warn(">>> [考试任务开始通知] 发送失败. 错误码: {}, 错误信息: {}", result.getErrCode(), result.getErrMsg());
}
} catch (DtErrorException e) {
log.error(">>> [考试任务开始通知] 钉钉接口调用异常, 任务名: {}, 原因: {}", taskDTO.getName(), e.getMessage());
}
});
}
private void sendBatchDeadlineTasks(List<String> userIds , TaskDTO taskDTO) {
if (CollUtil.isEmpty(userIds)) {
return;
}
StringBuilder markdown = new StringBuilder();
markdown.append(String.format("### 【%s】即将<font color=#FF0000>结束</font>,请按时完成\n\n" , taskDTO.getName()))
.append(String.format("产品线:%s\n\n" , taskDTO.getLineName()))
.append(String.format("开考时间:%s\n\n" ,sdf.format(taskDTO.getStartTime())))
.append(String.format("截止时间:%s\n\n" , sdf.format(taskDTO.getEndTime())))
.append(String.format("注意事项:%s" , taskDTO.getNote()));
ListUtil.partition(userIds, 100).forEach(batch -> {
DtCorpConversationMessage message = DtCorpConversationMessage.builder()
.agentId(dtService.getDtConfigStorage().getAgentId())
.userIds(batch)
.msg(DtMessage.ACTIONCARD()
.title("产品知识考核")
.markdown(markdown.toString())
.singleTitle("查看详情")
// todo 修改为对应页面
.singleUrl("https://www.baidu.com/").build())
.build();
try {
DtCorpConversationMsgSendResult result = dtService.getCorpConversationMsgService().send(message);
if (result.getErrCode() == 0) {
log.info(">>> [考试任务即将结束通知] 成功发送新任务通知. 任务名: {}, 批次人数: {}, 任务ID: {}",
taskDTO.getName(), batch.size(), result.getTaskId());
} else {
log.warn(">>> [考试任务即将结束通知] 发送失败. 错误码: {}, 错误信息: {}", result.getErrCode(), result.getErrMsg());
}
} catch (DtErrorException e) {
log.error(">>> [考试任务即将结束通知] 钉钉接口调用异常, 任务名: {}, 原因: {}", taskDTO.getName(), e.getMessage());
}
});
}
}

54
src/main/resources/logback-spring.xml

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 引入 Spring Boot 默认的日志配置(保留控制台输出等) -->
<include resource="org/springframework/boot/logging/logback/defaults.xml" />
<include resource="org/springframework/boot/logging/logback/console-appender.xml" />
<!-- 定义定时任务专用的 Appender -->
<appender name="REPLENISH_JOB_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 日志文件路径 -->
<file>logs/replenish-inventory.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- 每天产生一个日志文件,并进行 GZ 压缩 -->
<fileNamePattern>logs/archive/replenish-%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<!-- 单个文件最大 10MB -->
<maxFileSize>10MB</maxFileSize>
<!-- 只保留最近 3 天的日志-->
<maxHistory>3</maxHistory>
<!-- 该类日志总大小不得超过 100MB -->
<totalSizeCap>100MB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- 将特定的类绑定到上述 Appender -->
<logger name="com.project.question.domain.job.InventoryCheckJob" level="INFO" additivity="false">
<appender-ref ref="REPLENISH_JOB_FILE" />
</logger>
<appender name="TASK_NOTICE_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/exam-notice.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>logs/archive/exam-notice-%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<maxFileSize>5MB</maxFileSize>
<maxHistory>3</maxHistory>
<totalSizeCap>50MB</totalSizeCap>
</rollingPolicy>
<encoder><pattern>${FILE_LOG_PATTERN}</pattern></encoder>
</appender>
<logger name="com.project.task.domain.job.TaskNotifyJob" level="INFO" additivity="false">
<appender-ref ref="TASK_NOTICE_FILE" />
</logger>
<!-- 根日志配置(保持原有逻辑) -->
<root level="INFO">
<appender-ref ref="CONSOLE" />
</root>
</configuration>
Loading…
Cancel
Save