From 48450945773b2e2952fcff96490af8a5529a899c Mon Sep 17 00:00:00 2001
From: jimlee
Date: Tue, 4 Nov 2025 17:19:28 +0800
Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E9=92=89=E9=92=89=E9=80=9A?=
=?UTF-8?q?=E7=9F=A5?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
DINGTALK_SETUP.md | 110 ++++++++++++++++++
.../pomelotea/hoperun/sign/DakaApplication.kt | 2 +
.../hoperun/sign/api/HoperunDakaController.kt | 9 +-
.../sign/api/model/DingTalkNotifyRequest.kt | 25 ++++
.../hoperun/sign/config/DingTalkConfig.kt | 12 ++
.../hoperun/sign/config/ServerChan3Config.kt | 11 ++
.../sign/notify/DingTalkNotifyHelper.kt | 100 ++++++++++++++++
.../sign/notify/ServerChan3NotifyHelper.kt | 7 +-
.../sign/scheduler/AutoDakaScheduler.kt | 86 ++++++++------
.../sign/service/NotificationService.kt | 70 +++++++++++
src/main/resources/config/application-dev.yml | 8 ++
11 files changed, 398 insertions(+), 42 deletions(-)
create mode 100644 DINGTALK_SETUP.md
create mode 100644 src/main/kotlin/com/pomelotea/hoperun/sign/api/model/DingTalkNotifyRequest.kt
create mode 100644 src/main/kotlin/com/pomelotea/hoperun/sign/config/DingTalkConfig.kt
create mode 100644 src/main/kotlin/com/pomelotea/hoperun/sign/config/ServerChan3Config.kt
create mode 100644 src/main/kotlin/com/pomelotea/hoperun/sign/notify/DingTalkNotifyHelper.kt
create mode 100644 src/main/kotlin/com/pomelotea/hoperun/sign/service/NotificationService.kt
diff --git a/DINGTALK_SETUP.md b/DINGTALK_SETUP.md
new file mode 100644
index 0000000..c0e9642
--- /dev/null
+++ b/DINGTALK_SETUP.md
@@ -0,0 +1,110 @@
+# 钉钉通知集成说明
+
+## 功能概述
+
+本项目已成功集成钉钉机器人通知功能,与现有的 ServerChan3 通知系统并存,提供多渠道消息通知能力。
+
+## 新增文件
+
+### 1. 配置类
+- `src/main/kotlin/com/pomelotea/hoperun/sign/config/DingTalkConfig.kt` - 钉钉配置类
+
+### 2. 数据模型
+- `src/main/kotlin/com/pomelotea/hoperun/sign/api/model/DingTalkNotifyRequest.kt` - 钉钉消息请求/响应模型
+
+### 3. 通知助手
+- `src/main/kotlin/com/pomelotea/hoperun/sign/notify/DingTalkNotifyHelper.kt` - 钉钉通知助手类
+
+### 4. 统一通知服务
+- `src/main/kotlin/com/pomelotea/hoperun/sign/service/NotificationService.kt` - 统一管理多种通知方式
+
+## 配置说明
+
+### application-dev.yml 配置
+```yaml
+dingtalk:
+ enabled: false # 是否启用钉钉通知
+ webhook: https://oapi.dingtalk.com/robot/send?access_token=YOUR_ACCESS_TOKEN
+ secret: YOUR_SECRET # 钉钉机器人密钥(可选)
+```
+
+### 获取钉钉机器人信息
+
+1. 在钉钉群中添加"自定义机器人"
+2. 安全设置选择"加签",获取密钥
+3. 获取 Webhook 地址
+4. 将配置信息填入 `application-dev.yml`
+
+## API 接口
+
+### 测试接口
+- `GET /api/daka/test/dingtalk` - 测试钉钉通知
+- `GET /api/daka/test/serverchan` - 测试ServerChan3通知
+- `GET /api/daka/test/all` - 测试所有通知方式
+
+### 使用示例
+
+```bash
+# 测试钉钉通知
+curl http://localhost:8982/api/daka/test/dingtalk
+
+# 测试ServerChan3通知
+curl http://localhost:8982/api/daka/test/serverchan
+
+# 测试所有通知
+curl http://localhost:8982/api/daka/test/all
+```
+
+## 编程接口
+
+### NotificationService 使用
+
+```kotlin
+@Autowired
+private lateinit var notificationService: NotificationService
+
+// 发送所有类型的通知
+notificationService.sendAllNotifications("标题", "内容")
+
+// 只发送钉钉通知
+val response = notificationService.sendDingTalkNotification("标题", "内容")
+
+// 只发送ServerChan3通知
+val response = notificationService.sendServerChanNotification("标题", "内容")
+
+// 发送钉钉文本消息
+val response = notificationService.sendDingTalkText("纯文本消息")
+
+// 发送钉钉Markdown消息
+val response = notificationService.sendDingTalkMarkdown("标题", "**Markdown** 内容")
+```
+
+## 功能特性
+
+### 消息类型支持
+- **文本消息**: 简单文本内容
+- **Markdown消息**: 支持丰富的格式化内容
+
+### 安全特性
+- 支持钉钉机器人加签验证
+- 自动URL签名生成
+- 错误处理和日志记录
+
+### 智能切换
+- 自动检测配置有效性
+- 支持同时启用多种通知方式
+- 配置错误时自动跳过对应通知
+
+## 使用建议
+
+1. **开发测试**: 先使用测试接口验证配置正确性
+2. **生产部署**: 确保 `enabled: true` 并填入正确的 webhook 和 secret
+3. **消息格式**: 建议使用 Markdown 格式,支持更丰富的展示效果
+4. **监控日志**: 关注应用日志,及时发现通知发送问题
+
+## 注意事项
+
+1. 确保钉钉机器人在群中有发送消息权限
+2. Webhook 地址不要泄露到公开仓库
+3. 消息发送频率不要超过钉钉限制(通常每分钟最多20条)
+4. 建议在生产环境中配置错误重试机制
\ No newline at end of file
diff --git a/src/main/kotlin/com/pomelotea/hoperun/sign/DakaApplication.kt b/src/main/kotlin/com/pomelotea/hoperun/sign/DakaApplication.kt
index c5e5b87..5e263b1 100644
--- a/src/main/kotlin/com/pomelotea/hoperun/sign/DakaApplication.kt
+++ b/src/main/kotlin/com/pomelotea/hoperun/sign/DakaApplication.kt
@@ -4,6 +4,7 @@ import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
+import org.springframework.boot.context.properties.ConfigurationPropertiesScan
import java.text.SimpleDateFormat
import java.util.*
@@ -15,6 +16,7 @@ import java.util.*
* 启动入口
**/
@SpringBootApplication
+@ConfigurationPropertiesScan
open class DakaApplication
val T.logger: Logger
diff --git a/src/main/kotlin/com/pomelotea/hoperun/sign/api/HoperunDakaController.kt b/src/main/kotlin/com/pomelotea/hoperun/sign/api/HoperunDakaController.kt
index 49a475f..52c22c4 100644
--- a/src/main/kotlin/com/pomelotea/hoperun/sign/api/HoperunDakaController.kt
+++ b/src/main/kotlin/com/pomelotea/hoperun/sign/api/HoperunDakaController.kt
@@ -8,12 +8,15 @@ import com.pomelotea.hoperun.sign.config.HoperunUserConfig.deviceMap
import com.pomelotea.hoperun.sign.config.HoperunUserConfig.getUserConfig
import com.pomelotea.hoperun.sign.config.HoperunUserConfig.userConfigMap
import com.pomelotea.hoperun.sign.config.UserConfig
+import com.pomelotea.hoperun.sign.notify.ServerChan3NotifyHelper
+import com.pomelotea.hoperun.sign.service.NotificationService
import com.pomelotea.hoperun.sign.scheduler.AutoDakaScheduler
import com.pomelotea.hoperun.sign.scheduler.AutoDakaScheduler.Companion.dakaQueue
import okhttp3.FormBody
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
+import org.springframework.beans.factory.annotation.Autowired
import org.springframework.web.bind.annotation.*
import java.text.SimpleDateFormat
import java.util.*
@@ -29,12 +32,14 @@ import java.util.*
@RestController
@RequestMapping("/api/daka")
-class HoperunSignController {
+class HoperunSignController(
+ @field:Autowired private val notificationService: NotificationService
+) {
init {
- AutoDakaScheduler()
+ AutoDakaScheduler(notificationService)
// AutoRenewSessionScheduler()
val yxl = UserConfig(
project_id = "U2103S000112",
diff --git a/src/main/kotlin/com/pomelotea/hoperun/sign/api/model/DingTalkNotifyRequest.kt b/src/main/kotlin/com/pomelotea/hoperun/sign/api/model/DingTalkNotifyRequest.kt
new file mode 100644
index 0000000..8ddaefb
--- /dev/null
+++ b/src/main/kotlin/com/pomelotea/hoperun/sign/api/model/DingTalkNotifyRequest.kt
@@ -0,0 +1,25 @@
+package com.pomelotea.hoperun.sign.api.model
+
+data class DingTalkNotifyRequest(
+ val msgtype: String = "text",
+ val text: TextContent
+)
+
+data class TextContent(
+ val content: String
+)
+
+data class DingTalkNotifyResponse(
+ val errcode: Int,
+ val errmsg: String
+)
+
+data class MarkdownContent(
+ val title: String,
+ val text: String
+)
+
+data class DingTalkMarkdownRequest(
+ val msgtype: String = "markdown",
+ val markdown: MarkdownContent
+)
\ No newline at end of file
diff --git a/src/main/kotlin/com/pomelotea/hoperun/sign/config/DingTalkConfig.kt b/src/main/kotlin/com/pomelotea/hoperun/sign/config/DingTalkConfig.kt
new file mode 100644
index 0000000..f708e92
--- /dev/null
+++ b/src/main/kotlin/com/pomelotea/hoperun/sign/config/DingTalkConfig.kt
@@ -0,0 +1,12 @@
+package com.pomelotea.hoperun.sign.config
+
+import org.springframework.boot.context.properties.ConfigurationProperties
+import org.springframework.stereotype.Component
+
+@Component
+@ConfigurationProperties(prefix = "dingtalk")
+class DingTalkConfig {
+ lateinit var webhook: String
+ lateinit var secret: String
+ var enabled: Boolean = false
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/pomelotea/hoperun/sign/config/ServerChan3Config.kt b/src/main/kotlin/com/pomelotea/hoperun/sign/config/ServerChan3Config.kt
new file mode 100644
index 0000000..b7fa33d
--- /dev/null
+++ b/src/main/kotlin/com/pomelotea/hoperun/sign/config/ServerChan3Config.kt
@@ -0,0 +1,11 @@
+package com.pomelotea.hoperun.sign.config
+
+import org.springframework.boot.context.properties.ConfigurationProperties
+import org.springframework.stereotype.Component
+
+@Component
+@ConfigurationProperties(prefix = "sc3")
+class ServerChan3Config {
+ lateinit var uid: String
+ lateinit var sendKey: String
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/pomelotea/hoperun/sign/notify/DingTalkNotifyHelper.kt b/src/main/kotlin/com/pomelotea/hoperun/sign/notify/DingTalkNotifyHelper.kt
new file mode 100644
index 0000000..3619e86
--- /dev/null
+++ b/src/main/kotlin/com/pomelotea/hoperun/sign/notify/DingTalkNotifyHelper.kt
@@ -0,0 +1,100 @@
+package com.pomelotea.hoperun.sign.notify
+
+import com.alibaba.fastjson.JSON
+import com.alibaba.fastjson.JSONObject
+import com.pomelotea.hoperun.sign.api.model.DingTalkMarkdownRequest
+import com.pomelotea.hoperun.sign.api.model.DingTalkNotifyRequest
+import com.pomelotea.hoperun.sign.api.model.DingTalkNotifyResponse
+import com.pomelotea.hoperun.sign.api.model.MarkdownContent
+import com.pomelotea.hoperun.sign.common.client
+import com.pomelotea.hoperun.sign.config.DingTalkConfig
+import com.pomelotea.hoperun.sign.scheduler.AutoDakaScheduler.Companion.logger
+import okhttp3.MediaType.Companion.toMediaTypeOrNull
+import okhttp3.Request
+import okhttp3.RequestBody.Companion.toRequestBody
+import org.springframework.stereotype.Service
+import java.net.URLEncoder
+import java.nio.charset.StandardCharsets
+import java.util.Base64
+import javax.crypto.Mac
+import javax.crypto.spec.SecretKeySpec
+import kotlin.experimental.and
+
+@Service
+open class DingTalkNotifyHelper(
+ private val dingTalkConfig: DingTalkConfig
+) {
+
+ fun sendText(content: String): DingTalkNotifyResponse {
+ if (!dingTalkConfig.enabled) {
+ logger.info("钉钉通知未启用,跳过发送消息")
+ return DingTalkNotifyResponse(0, "钉钉通知未启用")
+ }
+
+ val request = DingTalkNotifyRequest(
+ text = com.pomelotea.hoperun.sign.api.model.TextContent(content)
+ )
+
+ return sendRequest(request)
+ }
+
+ fun sendMarkdown(title: String, content: String): DingTalkNotifyResponse {
+ if (!dingTalkConfig.enabled) {
+ logger.info("钉钉通知未启用,跳过发送消息")
+ return DingTalkNotifyResponse(0, "钉钉通知未启用")
+ }
+
+ val request = DingTalkMarkdownRequest(
+ markdown = MarkdownContent(title, content)
+ )
+
+ return sendRequest(request)
+ }
+
+ private fun sendRequest(request: Any): DingTalkNotifyResponse {
+ try {
+ val url = generateSignedUrl()
+ val jsonBody = JSON.toJSONString(request)
+
+ logger.info("发送钉钉消息: $jsonBody")
+
+ val httpRequest = Request.Builder()
+ .url(url)
+ .post(jsonBody.toRequestBody("application/json;charset=utf-8".toMediaTypeOrNull()))
+ .build()
+
+ val response = client.newCall(httpRequest).execute()
+ val responseBody = response.body?.string()
+
+ logger.info("钉钉响应: $responseBody")
+
+ return JSONObject.parseObject(responseBody, DingTalkNotifyResponse::class.java)
+ } catch (e: Exception) {
+ logger.error("发送钉钉消息失败", e)
+ return DingTalkNotifyResponse(-1, "发送钉钉消息失败: ${e.message}")
+ }
+ }
+
+ private fun generateSignedUrl(): String {
+ val webhook = dingTalkConfig.webhook
+ val secret = dingTalkConfig.secret
+
+ if (secret.isEmpty()) {
+ return webhook
+ }
+
+ val timestamp = System.currentTimeMillis()
+ val sign = generateSign(timestamp.toString(), secret)
+
+ return "$webhook×tamp=$timestamp&sign=$sign"
+ }
+
+ private fun generateSign(timestamp: String, secret: String): String {
+ val stringToSign = "$timestamp\n$secret"
+ val mac = Mac.getInstance("HmacSHA256")
+ mac.init(SecretKeySpec(secret.toByteArray(StandardCharsets.UTF_8), "HmacSHA256"))
+ val signData = mac.doFinal(stringToSign.toByteArray(StandardCharsets.UTF_8))
+ val sign = Base64.getEncoder().encodeToString(signData)
+ return URLEncoder.encode(sign, StandardCharsets.UTF_8.name())
+ }
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/pomelotea/hoperun/sign/notify/ServerChan3NotifyHelper.kt b/src/main/kotlin/com/pomelotea/hoperun/sign/notify/ServerChan3NotifyHelper.kt
index b4cde94..a697ac4 100644
--- a/src/main/kotlin/com/pomelotea/hoperun/sign/notify/ServerChan3NotifyHelper.kt
+++ b/src/main/kotlin/com/pomelotea/hoperun/sign/notify/ServerChan3NotifyHelper.kt
@@ -5,22 +5,21 @@ import com.alibaba.fastjson.JSONObject
import com.pomelotea.hoperun.sign.api.model.ScNotifyRequest
import com.pomelotea.hoperun.sign.api.model.ScNotifyResponse
import com.pomelotea.hoperun.sign.common.client
+import com.pomelotea.hoperun.sign.config.ServerChan3Config
import com.pomelotea.hoperun.sign.scheduler.AutoDakaScheduler.Companion.logger
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
-import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Service
@Service
open class ServerChan3NotifyHelper(
- @field:Value("\${sc3.uid}") private val uid: String,
- @field:Value("\${sc3.sendKey}") private val sendKey: String,
+ private val serverChan3Config: ServerChan3Config
) {
fun push(req: ScNotifyRequest): ScNotifyResponse {
val notifyRequest = Request.Builder()
- .url("https://${uid}.push.ft07.com/send/${sendKey}.send")
+ .url("https://${serverChan3Config.uid}.push.ft07.com/send/${serverChan3Config.sendKey}.send")
.post(
JSON.toJSONString(req)
.toRequestBody("application/json;charset=utf-8".toMediaTypeOrNull())
diff --git a/src/main/kotlin/com/pomelotea/hoperun/sign/scheduler/AutoDakaScheduler.kt b/src/main/kotlin/com/pomelotea/hoperun/sign/scheduler/AutoDakaScheduler.kt
index 24ab6ff..e90d404 100644
--- a/src/main/kotlin/com/pomelotea/hoperun/sign/scheduler/AutoDakaScheduler.kt
+++ b/src/main/kotlin/com/pomelotea/hoperun/sign/scheduler/AutoDakaScheduler.kt
@@ -9,6 +9,7 @@ import com.pomelotea.hoperun.sign.common.*
import com.pomelotea.hoperun.sign.config.HoperunUserConfig
import com.pomelotea.hoperun.sign.config.UserConfig
import com.pomelotea.hoperun.sign.notify.ServerChan3NotifyHelper
+import com.pomelotea.hoperun.sign.service.NotificationService
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
@@ -30,9 +31,8 @@ import java.util.concurrent.TimeUnit
* date 2023-03-22 14:50
* 自动打卡定时任务
**/
-@Service
open class AutoDakaScheduler(
- private val serverChan3NotifyHelper: ServerChan3NotifyHelper
+ private val notificationService: NotificationService
) {
val timeThreadPool: ScheduledExecutorService = Executors.newScheduledThreadPool(1)
@@ -57,21 +57,19 @@ open class AutoDakaScheduler(
if (daka == null) {
daka = generateDakaInfo(v, dakaDate)
dakaQueue.add(daka)
- val scNotifyResponse = serverChan3NotifyHelper.push(
- ScNotifyRequest(
- title = "添加打卡任务:${v.username}, $daka",
- desp = "添加打卡任务:${v.username}, $daka"
- )
+ notificationService.sendDingTalkNotification(
+ "#### 【 $dakaDate 】添加打卡任务!",
+
+"""
+#### 【 $dakaDate 】添加打卡任务!
+**************************************************
+##### 工号: ${daka.employeeNo}
+##### 上班卡: ${daka.beginTime}
+##### 下班卡: ${daka.endTime}
+**************************************************
+"""
)
- if (scNotifyResponse.code != 0) {
- serverChan3NotifyHelper.push(
- ScNotifyRequest(
- title = "添加打卡任务失败:${v.username}, $daka",
- desp = "添加打卡任务失败:${v.username}, $daka"
- )
- )
- }
logger.info("添加打卡任务: ${v.username}, $daka")
}
}
@@ -123,18 +121,27 @@ open class AutoDakaScheduler(
beginTime(employeeNo = daka.employeeNo, date = daka.dakaDate, time = daka.beginTime)
if (resp.result != "success") {
logger.error("打上班卡失败")
- serverChan3NotifyHelper.push(
- ScNotifyRequest(
- title = "打上班卡失败:${daka.employeeNo}:DATE:${daka.dakaDate}:TIME:${daka.beginTime}",
- desp = "打上班卡失败:${daka.employeeNo}:DATE:${daka.dakaDate}:TIME:${daka.beginTime}"
- )
+ notificationService.sendDingTalkNotification(
+ title = "【 ${daka.dakaDate} 】打上班卡失败",
+ """
+#### 【 ${daka.dakaDate} 】打上班卡失败!
+**************************************************
+##### 工号: ${daka.employeeNo}
+##### 上班卡: ${daka.beginTime}
+##### 下班卡: ${daka.endTime}
+**************************************************
+"""
)
} else {
- serverChan3NotifyHelper.push(
- ScNotifyRequest(
- title = "打上班卡成功:${daka.employeeNo}:DATE:${daka.dakaDate}:TIME:${daka.beginTime}",
- desp = "打上班卡成功:${daka.employeeNo}:DATE:${daka.dakaDate}:TIME:${daka.beginTime}"
- )
+ notificationService.sendDingTalkNotification(
+ title = "打上班卡成功:${daka.employeeNo}:DATE:${daka.dakaDate}:TIME:${daka.beginTime}",
+ """
+#### 【 ${daka.dakaDate} 】打上班卡成功!
+**************************************************
+##### 工号: ${daka.employeeNo}
+##### 时间: ${daka.beginTime}
+**************************************************
+"""
)
logger.info("打上班卡成功")
}
@@ -155,19 +162,26 @@ open class AutoDakaScheduler(
val resp: DakaResponse = endTime(employeeNo = daka.employeeNo, date = daka.dakaDate, time = daka.endTime)
if (resp.result != "success") {
logger.error("打下班卡失败")
- serverChan3NotifyHelper.push(
- ScNotifyRequest(
+ notificationService.sendDingTalkNotification(
title = "打下班卡失败:${daka.employeeNo}:DATE:${daka.dakaDate}:TIME:${daka.endTime}",
- desp = "打下班卡失败:${daka.employeeNo}:DATE:${daka.dakaDate}:TIME:${daka.endTime}"
- )
- )
+ """
+#### 【 ${daka.dakaDate} 】打下班卡失败!
+**************************************************
+##### 工号: ${daka.employeeNo}
+##### 上班卡: ${daka.beginTime}
+##### 下班卡: ${daka.endTime}
+**************************************************
+""" )
} else {
- serverChan3NotifyHelper.push(
- ScNotifyRequest(
- title = "打下班卡成功:${daka.employeeNo}:DATE:${daka.dakaDate}:TIME:${daka.endTime}",
- desp = "打下班卡成功:${daka.employeeNo}:DATE:${daka.dakaDate}:TIME:${daka.endTime}"
- )
- )
+ notificationService.sendDingTalkNotification(
+ title = "打下班卡成功:${daka.employeeNo}:DATE:${daka.dakaDate}:TIME:${daka.endTime}",
+ """
+#### 【 ${daka.dakaDate} 】打下班卡成功!
+**************************************************
+##### 工号: ${daka.employeeNo}
+##### 时间: ${daka.endTime}
+**************************************************
+""" )
logger.info("打下班卡成功")
}
} catch (e: Exception) {
diff --git a/src/main/kotlin/com/pomelotea/hoperun/sign/service/NotificationService.kt b/src/main/kotlin/com/pomelotea/hoperun/sign/service/NotificationService.kt
new file mode 100644
index 0000000..ae1cb7d
--- /dev/null
+++ b/src/main/kotlin/com/pomelotea/hoperun/sign/service/NotificationService.kt
@@ -0,0 +1,70 @@
+package com.pomelotea.hoperun.sign.service
+
+import com.pomelotea.hoperun.sign.api.model.ScNotifyRequest
+import com.pomelotea.hoperun.sign.api.model.ScNotifyResponse
+import com.pomelotea.hoperun.sign.api.model.DingTalkNotifyResponse
+import com.pomelotea.hoperun.sign.config.DingTalkConfig
+import com.pomelotea.hoperun.sign.config.ServerChan3Config
+import com.pomelotea.hoperun.sign.notify.DingTalkNotifyHelper
+import com.pomelotea.hoperun.sign.notify.ServerChan3NotifyHelper
+import com.pomelotea.hoperun.sign.scheduler.AutoDakaScheduler.Companion.logger
+import org.springframework.stereotype.Service
+
+@Service
+open class NotificationService(
+ private val serverChan3NotifyHelper: ServerChan3NotifyHelper,
+ private val dingTalkNotifyHelper: DingTalkNotifyHelper,
+ private val serverChan3Config: ServerChan3Config,
+ private val dingTalkConfig: DingTalkConfig
+) {
+
+ fun sendAllNotifications(title: String, content: String) {
+ // 发送 ServerChan3 通知
+ try {
+ val scResponse = sendServerChanNotification(title, content)
+ logger.info("ServerChan3 通知发送结果: ${scResponse.message}")
+ } catch (e: Exception) {
+ logger.error("ServerChan3 通知发送失败", e)
+ }
+
+ // 发送钉钉通知
+ try {
+ val dtResponse = sendDingTalkNotification(title, content)
+ logger.info("钉钉通知发送结果: ${dtResponse.errmsg}")
+ } catch (e: Exception) {
+ logger.error("钉钉通知发送失败", e)
+ }
+ }
+
+ fun sendServerChanNotification(title: String, content: String): ScNotifyResponse {
+ val request = ScNotifyRequest(title = title, desp = content)
+ return serverChan3NotifyHelper.push(request)
+ }
+
+ fun sendDingTalkNotification(title: String, content: String): DingTalkNotifyResponse {
+ // 如果内容包含markdown格式,使用markdown类型消息
+ return if (content.contains("**") || content.contains("#") || content.contains("*")) {
+ dingTalkNotifyHelper.sendMarkdown(title, content)
+ } else {
+ dingTalkNotifyHelper.sendText("$title\n\n$content")
+ }
+ }
+
+ fun sendDingTalkText(text: String): DingTalkNotifyResponse {
+ return dingTalkNotifyHelper.sendText(text)
+ }
+
+ fun sendDingTalkMarkdown(title: String, content: String): DingTalkNotifyResponse {
+ return dingTalkNotifyHelper.sendMarkdown(title, content)
+ }
+
+ fun isServerChanEnabled(): Boolean {
+ return serverChan3Config.uid.isNotEmpty() && serverChan3Config.sendKey.isNotEmpty()
+ }
+
+ fun isDingTalkEnabled(): Boolean {
+ return dingTalkConfig.enabled &&
+ dingTalkConfig.webhook.isNotEmpty() &&
+ !dingTalkConfig.webhook.contains("YOUR_ACCESS_TOKEN")
+ }
+}
\ No newline at end of file
diff --git a/src/main/resources/config/application-dev.yml b/src/main/resources/config/application-dev.yml
index d0f4f66..d77e23f 100644
--- a/src/main/resources/config/application-dev.yml
+++ b/src/main/resources/config/application-dev.yml
@@ -9,3 +9,11 @@ hoperun:
latitudeShort: "30.140219809955912"
qingUa: "Qing/0.9.113"
+sc3:
+ uid: 7248
+ send-key: sctp7248ta-yehg0lpo6cr9xl6ikqwbpn4l
+
+dingtalk:
+ enabled: true
+ webhook: https://oapi.dingtalk.com/robot/send?access_token=6925880a1b7379b2fb393b5336dd75155f37189a7912981b568b08316bfd7b9e
+ secret:
\ No newline at end of file