集成钉钉
This commit is contained in:
110
src/main/java/com/rj/service/impl/DingTalkRobotServiceImpl.java
Normal file
110
src/main/java/com/rj/service/impl/DingTalkRobotServiceImpl.java
Normal file
@@ -0,0 +1,110 @@
|
||||
package com.rj.service.impl;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.rj.config.LbAssessmentDingTalkProperties;
|
||||
import com.rj.service.DingTalkRobotService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class DingTalkRobotServiceImpl implements DingTalkRobotService {
|
||||
|
||||
private static final String SEND_URL = "https://oapi.dingtalk.com/robot/send";
|
||||
|
||||
private final RestTemplate restTemplate;
|
||||
private final ObjectMapper objectMapper;
|
||||
private final LbAssessmentDingTalkProperties properties;
|
||||
|
||||
public DingTalkRobotServiceImpl(RestTemplate restTemplate,
|
||||
ObjectMapper objectMapper,
|
||||
LbAssessmentDingTalkProperties properties) {
|
||||
this.restTemplate = restTemplate;
|
||||
this.objectMapper = objectMapper;
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendText(String content) {
|
||||
if (!properties.isEnabled()) {
|
||||
throw new IllegalStateException("钉钉机器人发送未启用,请在配置中设置 lb.assessment-apply.dingtalk.enabled=true");
|
||||
}
|
||||
if (!StringUtils.hasText(properties.getAccessToken())) {
|
||||
throw new IllegalStateException("未配置钉钉机器人 access-token(lb.assessment-apply.dingtalk.access-token)");
|
||||
}
|
||||
if (!StringUtils.hasText(content)) {
|
||||
throw new IllegalArgumentException("发送内容不能为空");
|
||||
}
|
||||
|
||||
String url = buildWebhookUrl();
|
||||
Map<String, Object> body = new HashMap<>();
|
||||
body.put("msgtype", "text");
|
||||
Map<String, String> text = new HashMap<>();
|
||||
text.put("content", content);
|
||||
body.put("text", text);
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
HttpEntity<Map<String, Object>> entity = new HttpEntity<>(body, headers);
|
||||
|
||||
ResponseEntity<String> response = restTemplate.postForEntity(url, entity, String.class);
|
||||
parseResponse(response.getBody());
|
||||
}
|
||||
|
||||
private String buildWebhookUrl() {
|
||||
StringBuilder url = new StringBuilder(SEND_URL)
|
||||
.append("?access_token=")
|
||||
.append(properties.getAccessToken().trim());
|
||||
if (StringUtils.hasText(properties.getSecret())) {
|
||||
long timestamp = System.currentTimeMillis();
|
||||
String sign = sign(timestamp, properties.getSecret().trim());
|
||||
url.append("×tamp=").append(timestamp).append("&sign=").append(sign);
|
||||
}
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
private static String sign(long timestamp, String secret) {
|
||||
try {
|
||||
String stringToSign = timestamp + "\n" + secret;
|
||||
Mac mac = Mac.getInstance("HmacSHA256");
|
||||
mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
|
||||
byte[] signData = mac.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8));
|
||||
return URLEncoder.encode(Base64.getEncoder().encodeToString(signData), StandardCharsets.UTF_8);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("钉钉加签失败:" + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private void parseResponse(String body) {
|
||||
if (!StringUtils.hasText(body)) {
|
||||
throw new IllegalStateException("钉钉接口返回为空");
|
||||
}
|
||||
try {
|
||||
JsonNode root = objectMapper.readTree(body);
|
||||
int errcode = root.path("errcode").asInt(-1);
|
||||
if (errcode != 0) {
|
||||
String errmsg = root.path("errmsg").asText("未知错误");
|
||||
throw new IllegalStateException("钉钉发送失败:" + errmsg + "(errcode=" + errcode + ")");
|
||||
}
|
||||
} catch (IllegalStateException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("解析钉钉响应失败:" + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.rj.entity.LbAssessmentApply;
|
||||
import com.rj.mapper.LbAssessmentApplyMapper;
|
||||
import com.rj.service.DingTalkRobotService;
|
||||
import com.rj.service.ILbAssessmentApplyService;
|
||||
import jakarta.servlet.ServletOutputStream;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
@@ -47,6 +48,9 @@ public class LbAssessmentApplyServiceImpl
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
@Autowired
|
||||
private DingTalkRobotService dingTalkRobotService;
|
||||
|
||||
@Value("${langchain4j.open-ai.chat-model.model-name:qwen-plus}")
|
||||
private String dashScopeChatModel;
|
||||
|
||||
@@ -67,7 +71,7 @@ public class LbAssessmentApplyServiceImpl
|
||||
moderatorName 主持人姓名;
|
||||
meetingNumber 会议号;
|
||||
applicationDatetime 申请日期时间,优先 ISO-8601(如 2026-05-15T14:30:00),也可 yyyy-MM-dd HH:mm:ss;若仅有日期可设为当天 00:00:00。
|
||||
不要输出 id、tenantId、createdAt、updatedAt。
|
||||
不要输出 id、tenantId、createdAt、updatedAt、notificationText。
|
||||
""";
|
||||
|
||||
@Override
|
||||
@@ -190,6 +194,7 @@ public class LbAssessmentApplyServiceImpl
|
||||
entity.setCreatedAt(now);
|
||||
}
|
||||
entity.setUpdatedAt(now);
|
||||
refreshNotificationText(entity);
|
||||
|
||||
boolean ok = this.save(entity);
|
||||
result.put("success", ok);
|
||||
@@ -216,6 +221,9 @@ public class LbAssessmentApplyServiceImpl
|
||||
}
|
||||
entity.setUpdatedAt(LocalDateTime.now());
|
||||
boolean ok = this.updateById(entity);
|
||||
if (ok) {
|
||||
persistNotificationText(entity.getId());
|
||||
}
|
||||
result.put("success", ok);
|
||||
result.put("message", ok ? "编辑成功" : "编辑失败");
|
||||
if (ok) {
|
||||
@@ -308,17 +316,14 @@ public class LbAssessmentApplyServiceImpl
|
||||
return result;
|
||||
}
|
||||
|
||||
LambdaQueryWrapper<LbAssessmentApply> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(LbAssessmentApply::getTenantId, tenantId.trim())
|
||||
.eq(LbAssessmentApply::getId, id.trim());
|
||||
LbAssessmentApply record = this.getOne(queryWrapper);
|
||||
LbAssessmentApply record = getByTenantIdAndId(tenantId, id);
|
||||
if (record == null) {
|
||||
result.put("success", false);
|
||||
result.put("message", "未找到对应的申请评估记录");
|
||||
return result;
|
||||
}
|
||||
|
||||
String text = formatAssessmentNotificationText(record);
|
||||
String text = ensureAndSaveNotificationText(record);
|
||||
result.put("success", true);
|
||||
result.put("message", "生成成功");
|
||||
result.put("data", text);
|
||||
@@ -331,6 +336,91 @@ public class LbAssessmentApplyServiceImpl
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> sendNotificationTextToDingTalk(String tenantId, String id) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
try {
|
||||
if (tenantId == null || tenantId.trim().isEmpty()) {
|
||||
result.put("success", false);
|
||||
result.put("message", "tenantId不能为空");
|
||||
return result;
|
||||
}
|
||||
if (id == null || id.trim().isEmpty()) {
|
||||
result.put("success", false);
|
||||
result.put("message", "id不能为空");
|
||||
return result;
|
||||
}
|
||||
|
||||
LbAssessmentApply record = getByTenantIdAndId(tenantId, id);
|
||||
if (record == null) {
|
||||
result.put("success", false);
|
||||
result.put("message", "未找到对应的申请评估记录");
|
||||
return result;
|
||||
}
|
||||
|
||||
String text = ensureAndSaveNotificationText(record);
|
||||
dingTalkRobotService.sendText(text);
|
||||
|
||||
result.put("success", true);
|
||||
result.put("message", "发送成功");
|
||||
result.put("data", text);
|
||||
return result;
|
||||
} catch (IllegalArgumentException | IllegalStateException e) {
|
||||
log.warn("发送评估会议通知到钉钉失败, tenantId={}, id={}, reason={}", tenantId, id, e.getMessage());
|
||||
result.put("success", false);
|
||||
result.put("message", e.getMessage());
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
log.error("发送评估会议通知到钉钉异常, tenantId={}, id={}", tenantId, id, e);
|
||||
result.put("success", false);
|
||||
result.put("message", "发送失败:" + e.getMessage());
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
private LbAssessmentApply getByTenantIdAndId(String tenantId, String id) {
|
||||
LambdaQueryWrapper<LbAssessmentApply> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(LbAssessmentApply::getTenantId, tenantId.trim())
|
||||
.eq(LbAssessmentApply::getId, id.trim());
|
||||
return this.getOne(queryWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回可用的通知文案:库中已有则直接返回,否则生成并落库。
|
||||
*/
|
||||
private String ensureAndSaveNotificationText(LbAssessmentApply record) {
|
||||
String text = record.getNotificationText();
|
||||
if (text != null && !text.trim().isEmpty()) {
|
||||
return text;
|
||||
}
|
||||
text = formatAssessmentNotificationText(record);
|
||||
LbAssessmentApply update = new LbAssessmentApply();
|
||||
update.setId(record.getId());
|
||||
update.setNotificationText(text);
|
||||
update.setUpdatedAt(LocalDateTime.now());
|
||||
if (!this.updateById(update)) {
|
||||
throw new IllegalStateException("通知文案保存失败");
|
||||
}
|
||||
record.setNotificationText(text);
|
||||
return text;
|
||||
}
|
||||
|
||||
private void refreshNotificationText(LbAssessmentApply entity) {
|
||||
entity.setNotificationText(formatAssessmentNotificationText(entity));
|
||||
}
|
||||
|
||||
private void persistNotificationText(String id) {
|
||||
LbAssessmentApply record = this.getById(id);
|
||||
if (record == null) {
|
||||
return;
|
||||
}
|
||||
LbAssessmentApply update = new LbAssessmentApply();
|
||||
update.setId(id);
|
||||
update.setNotificationText(formatAssessmentNotificationText(record));
|
||||
update.setUpdatedAt(LocalDateTime.now());
|
||||
this.updateById(update);
|
||||
}
|
||||
|
||||
private static String formatAssessmentNotificationText(LbAssessmentApply record) {
|
||||
String teacherName = nullToEmpty(record.getAssessmentTeacherName());
|
||||
String applicantName = nullToEmpty(record.getApplicantName());
|
||||
|
||||
Reference in New Issue
Block a user