Skip to content

Commit 1f7c144

Browse files
authored
Merge pull request #410 from lazzyfu/dev
Dev
2 parents ce9a07f + 7492bdb commit 1f7c144

14 files changed

Lines changed: 1528 additions & 594 deletions

File tree

.codex/skills/ant-design-vue/SKILL.md

Lines changed: 552 additions & 111 deletions
Large diffs are not rendered by default.
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
interface:
2-
display_name: "Ant Design Vue 统一规范"
3-
short_description: "统一 Ant Design Vue 页面布局、间距、圆角与按钮规范"
4-
default_prompt: "请基于 Vue3 + Ant Design Vue 4.x 先输出规范化 UI 方案(布局、组件映射、一致性检查),再给实现建议;严格遵守全局间距/圆角 token 和按钮统一 middle 规则。"
2+
display_name: "Ant Design Vue 统一规范"
3+
short_description: "统一 Ant Design Vue 页面布局、间距、圆角与按钮规范"
4+
default_prompt: "请基于 Vue3 + Ant Design Vue 4.x 先输出规范化 UI 方案(布局、组件映射、一致性检查),再给实现建议;严格遵守全局间距/圆角 token 和按钮统一 middle 规则。"

backend/internal/common/services/notify.go

Lines changed: 67 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package services
33
import (
44
"errors"
55
"fmt"
6+
netmail "net/mail"
67
"net/url"
78
"strings"
89
"time"
@@ -113,15 +114,15 @@ func (s *AdminTestNotifyConfigService) Run() error {
113114
if err != nil {
114115
return fmt.Errorf("加载通知配置失败: %w", err)
115116
}
117+
if err := validateRuntimeChannelConfig(s.Channel, runtimeCfg); err != nil {
118+
return err
119+
}
116120

117121
now := time.Now().Format("2006-01-02 15:04:05")
118122
baseMsg := fmt.Sprintf("您好,这是一条 GoInsight 消息通知测试。\n时间:%s", now)
119123

120124
switch s.Channel {
121125
case "wechat":
122-
if !runtimeCfg.Wechat.Enable {
123-
return errors.New("请先启用企业微信通知后再测试")
124-
}
125126
wechatNotifier := notifier.NewWechatNotifier(notifier.WechatNotifierConfig{
126127
Webhook: runtimeCfg.Wechat.Webhook,
127128
})
@@ -131,9 +132,6 @@ func (s *AdminTestNotifyConfigService) Run() error {
131132
return nil
132133

133134
case "dingtalk":
134-
if !runtimeCfg.DingTalk.Enable {
135-
return errors.New("请先启用钉钉通知后再测试")
136-
}
137135
dingtalkNotifier := notifier.NewDingTalkNotifier(notifier.DingTalkNotifierConfig{
138136
Webhook: runtimeCfg.DingTalk.Webhook,
139137
})
@@ -147,10 +145,6 @@ func (s *AdminTestNotifyConfigService) Run() error {
147145
return nil
148146

149147
case "mail":
150-
if !runtimeCfg.Mail.Enable {
151-
return errors.New("请先启用邮件通知后再测试")
152-
}
153-
154148
var user userModels.InsightUsers
155149
if err := global.App.DB.Table("insight_users").Where("username = ?", username).Take(&user).Error; err != nil {
156150
return fmt.Errorf("邮件测试发送失败: 查询用户邮箱失败(%w)", err)
@@ -175,6 +169,60 @@ func (s *AdminTestNotifyConfigService) Run() error {
175169
}
176170
}
177171

172+
func validateRuntimeChannelConfig(channel string, runtimeCfg *notifier.RuntimeNotifyConfig) error {
173+
if runtimeCfg == nil {
174+
return errors.New("通知配置不存在")
175+
}
176+
177+
switch channel {
178+
case "wechat":
179+
if !runtimeCfg.Wechat.Enable {
180+
return errors.New("请先启用企业微信通知后再测试")
181+
}
182+
if !isValidHTTPURL(runtimeCfg.Wechat.Webhook) {
183+
return errors.New("企业微信 Webhook 配置不正确,请先保存合法地址")
184+
}
185+
return nil
186+
187+
case "dingtalk":
188+
if !runtimeCfg.DingTalk.Enable {
189+
return errors.New("请先启用钉钉通知后再测试")
190+
}
191+
if !isValidHTTPURL(runtimeCfg.DingTalk.Webhook) {
192+
return errors.New("钉钉 Webhook 配置不正确,请先保存合法地址")
193+
}
194+
if strings.TrimSpace(runtimeCfg.DingTalk.Keywords) == "" {
195+
return errors.New("钉钉关键字不能为空,请先完善后再测试")
196+
}
197+
return nil
198+
199+
case "mail":
200+
if !runtimeCfg.Mail.Enable {
201+
return errors.New("请先启用邮件通知后再测试")
202+
}
203+
mailUsername := strings.TrimSpace(runtimeCfg.Mail.Username)
204+
if mailUsername == "" {
205+
return errors.New("邮件发件账号不能为空,请先完善后再测试")
206+
}
207+
if _, err := netmail.ParseAddress(mailUsername); err != nil {
208+
return errors.New("邮件发件账号格式不正确,请先完善后再测试")
209+
}
210+
if strings.TrimSpace(runtimeCfg.Mail.Host) == "" {
211+
return errors.New("邮件 SMTP 主机不能为空,请先完善后再测试")
212+
}
213+
if runtimeCfg.Mail.Port < 1 || runtimeCfg.Mail.Port > 65535 {
214+
return errors.New("邮件 SMTP 端口范围必须为 1-65535")
215+
}
216+
if strings.TrimSpace(runtimeCfg.Mail.Password) == "" {
217+
return errors.New("邮件 SMTP 密码不能为空,请先完善后再测试")
218+
}
219+
return nil
220+
221+
default:
222+
return errors.New("不支持的测试渠道")
223+
}
224+
}
225+
178226
func normalizeNoticeURL(raw string) string {
179227
return strings.TrimRight(strings.TrimSpace(raw), "/")
180228
}
@@ -202,10 +250,16 @@ func validateNotifyConfigForSave(form *forms.AdminUpdateNotifyConfigForm, hasSto
202250
if form.Wechat.Enable && form.Wechat.Webhook == "" {
203251
return errors.New("启用企业微信通知时,Webhook 不能为空")
204252
}
253+
if form.Wechat.Enable && !isValidHTTPURL(form.Wechat.Webhook) {
254+
return errors.New("启用企业微信通知时,Webhook 地址格式不正确")
255+
}
205256

206257
if form.DingTalk.Enable && form.DingTalk.Webhook == "" {
207258
return errors.New("启用钉钉通知时,Webhook 不能为空")
208259
}
260+
if form.DingTalk.Enable && !isValidHTTPURL(form.DingTalk.Webhook) {
261+
return errors.New("启用钉钉通知时,Webhook 地址格式不正确")
262+
}
209263
if form.DingTalk.Enable && form.DingTalk.Keywords == "" {
210264
return errors.New("启用钉钉通知时,关键字不能为空")
211265
}
@@ -214,6 +268,9 @@ func validateNotifyConfigForSave(form *forms.AdminUpdateNotifyConfigForm, hasSto
214268
if form.Mail.Username == "" {
215269
return errors.New("启用邮件通知时,用户名不能为空")
216270
}
271+
if _, err := netmail.ParseAddress(form.Mail.Username); err != nil {
272+
return errors.New("启用邮件通知时,用户名必须是合法邮箱")
273+
}
217274
if form.Mail.Host == "" {
218275
return errors.New("启用邮件通知时,SMTP 主机不能为空")
219276
}

backend/internal/common/services/notify_test.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"testing"
55

66
"github.com/lazzyfu/goinsight/internal/common/forms"
7+
"github.com/lazzyfu/goinsight/pkg/notifier"
78
)
89

910
func buildValidNotifyForm() *forms.AdminUpdateNotifyConfigForm {
@@ -59,6 +60,15 @@ func TestValidateNotifyConfigForSave(t *testing.T) {
5960
}
6061
})
6162

63+
t.Run("wechat enabled with invalid webhook", func(t *testing.T) {
64+
form := buildValidNotifyForm()
65+
form.Wechat.Enable = true
66+
form.Wechat.Webhook = "not-a-url"
67+
if err := validateNotifyConfigForSave(form, false); err == nil {
68+
t.Fatal("expected validation error")
69+
}
70+
})
71+
6272
t.Run("dingtalk enabled without webhook", func(t *testing.T) {
6373
form := buildValidNotifyForm()
6474
form.DingTalk.Enable = true
@@ -67,6 +77,16 @@ func TestValidateNotifyConfigForSave(t *testing.T) {
6777
}
6878
})
6979

80+
t.Run("dingtalk enabled with invalid webhook", func(t *testing.T) {
81+
form := buildValidNotifyForm()
82+
form.DingTalk.Enable = true
83+
form.DingTalk.Webhook = "invalid-webhook"
84+
form.DingTalk.Keywords = "GoInsight"
85+
if err := validateNotifyConfigForSave(form, false); err == nil {
86+
t.Fatal("expected validation error")
87+
}
88+
})
89+
7090
t.Run("dingtalk enabled without keywords", func(t *testing.T) {
7191
form := buildValidNotifyForm()
7292
form.DingTalk.Enable = true
@@ -95,6 +115,18 @@ func TestValidateNotifyConfigForSave(t *testing.T) {
95115
}
96116
})
97117

118+
t.Run("mail enabled with invalid username email", func(t *testing.T) {
119+
form := buildValidNotifyForm()
120+
form.Mail.Enable = true
121+
form.Mail.Username = "not-an-email"
122+
form.Mail.Host = "smtp.example.com"
123+
form.Mail.Password = "secret"
124+
form.Mail.Port = 465
125+
if err := validateNotifyConfigForSave(form, false); err == nil {
126+
t.Fatal("expected validation error")
127+
}
128+
})
129+
98130
t.Run("mail enabled with invalid port", func(t *testing.T) {
99131
form := buildValidNotifyForm()
100132
form.Mail.Enable = true
@@ -131,3 +163,51 @@ func TestValidateNotifyConfigForSave(t *testing.T) {
131163
}
132164
})
133165
}
166+
167+
func buildValidRuntimeNotifyConfig() *notifier.RuntimeNotifyConfig {
168+
cfg := &notifier.RuntimeNotifyConfig{}
169+
cfg.Wechat.Enable = true
170+
cfg.Wechat.Webhook = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=demo"
171+
cfg.DingTalk.Enable = true
172+
cfg.DingTalk.Webhook = "https://oapi.dingtalk.com/robot/send?access_token=demo"
173+
cfg.DingTalk.Keywords = "GoInsight"
174+
cfg.Mail.Enable = true
175+
cfg.Mail.Username = "ops@example.com"
176+
cfg.Mail.Password = "secret"
177+
cfg.Mail.Host = "smtp.example.com"
178+
cfg.Mail.Port = 465
179+
return cfg
180+
}
181+
182+
func TestValidateRuntimeChannelConfig(t *testing.T) {
183+
t.Run("valid wechat config", func(t *testing.T) {
184+
cfg := buildValidRuntimeNotifyConfig()
185+
if err := validateRuntimeChannelConfig("wechat", cfg); err != nil {
186+
t.Fatalf("unexpected error: %v", err)
187+
}
188+
})
189+
190+
t.Run("wechat enabled but webhook empty", func(t *testing.T) {
191+
cfg := buildValidRuntimeNotifyConfig()
192+
cfg.Wechat.Webhook = ""
193+
if err := validateRuntimeChannelConfig("wechat", cfg); err == nil {
194+
t.Fatal("expected validation error")
195+
}
196+
})
197+
198+
t.Run("dingtalk enabled but keywords empty", func(t *testing.T) {
199+
cfg := buildValidRuntimeNotifyConfig()
200+
cfg.DingTalk.Keywords = ""
201+
if err := validateRuntimeChannelConfig("dingtalk", cfg); err == nil {
202+
t.Fatal("expected validation error")
203+
}
204+
})
205+
206+
t.Run("mail enabled but password empty", func(t *testing.T) {
207+
cfg := buildValidRuntimeNotifyConfig()
208+
cfg.Mail.Password = ""
209+
if err := validateRuntimeChannelConfig("mail", cfg); err == nil {
210+
t.Fatal("expected validation error")
211+
}
212+
})
213+
}

backend/web/dist/index.html

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1-
<!doctype html>
2-
<html lang="en">
3-
<head>
4-
<meta charset="utf-8">
5-
<meta name="viewport" content="width=device-width,initial-scale=1">
6-
<title>GoInsight</title>
7-
</head>
8-
<body>
9-
<p>Frontend assets are not built yet. Run `cd www && npm install && npm run build` first.</p>
10-
</body>
1+
<!DOCTYPE html>
2+
<html lang="">
3+
<head>
4+
<meta charset="UTF-8">
5+
<link rel="icon" href="/favicon.ico">
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
7+
<title>数据库工单平台</title>
8+
<script type="module" crossorigin src="/assets/index-KL7fv5kM.js"></script>
9+
<link rel="stylesheet" crossorigin href="/assets/index-QwWTY4TQ.css">
10+
</head>
11+
<body>
12+
<div id="app"></div>
13+
</body>
1114
</html>

0 commit comments

Comments
 (0)