Skip to content

Commit b80e1fc

Browse files
authored
Feat/v0.9.0 (#50)
1 parent 75d4006 commit b80e1fc

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+1523
-533
lines changed

backend/docs/docs.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4615,6 +4615,9 @@ const docTemplate = `{
46154615
},
46164616
"model.ForumInfo": {
46174617
"type": "object",
4618+
"required": [
4619+
"name"
4620+
],
46184621
"properties": {
46194622
"group_ids": {
46204623
"type": "array",
@@ -4630,6 +4633,9 @@ const docTemplate = `{
46304633
},
46314634
"name": {
46324635
"type": "string"
4636+
},
4637+
"route_name": {
4638+
"type": "string"
46334639
}
46344640
}
46354641
},

backend/intercept/trace.go

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package intercept
22

33
import (
4+
"time"
5+
46
"github.com/chaitin/koalaqa/pkg/context"
57
"github.com/chaitin/koalaqa/pkg/glog"
68
koalaTrace "github.com/chaitin/koalaqa/pkg/trace"
@@ -22,11 +24,19 @@ func (t *trace) Intercept(ctx *context.Context) {
2224
ctx.Context.Request = req.WithContext(koalaTrace.Context(req.Context()))
2325

2426
l := t.logger.WithContext(ctx).With("method", req.Method).With("path", req.URL.Path)
25-
l.Debug("receive request")
27+
start := time.Now()
28+
defer func() {
29+
// skip websocket request
30+
if ctx.Context.IsWebsocket() {
31+
return
32+
}
33+
duration := time.Since(start).Seconds()
34+
if duration > 1 {
35+
l.With("elapsed", duration).Warn("slow request")
36+
}
37+
}()
2638

2739
ctx.Next()
28-
29-
l.Debug("request finish")
3040
}
3141

3242
func (t *trace) Priority() int {
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package migration
2+
3+
import (
4+
"gorm.io/gorm"
5+
6+
"github.com/chaitin/koalaqa/migration/migrator"
7+
"github.com/chaitin/koalaqa/model"
8+
)
9+
10+
type updateHot struct{}
11+
12+
func (m *updateHot) Version() int64 {
13+
return 20251027121814
14+
}
15+
16+
func (m *updateHot) Migrate(tx *gorm.DB) error {
17+
hotFormula := `(
18+
0.3 * LN(GREATEST(view, 0) + 1) +
19+
0.4 * LN(GREATEST("like", 0) + 1) +
20+
0.3 * LN(GREATEST(comment, 0) + 1)
21+
) * EXP(-0.01 * EXTRACT(EPOCH FROM (NOW() - updated_at))/3600)
22+
* 10000
23+
* CASE WHEN resolved = true THEN 1.3 ELSE 1.0 END`
24+
25+
if err := tx.Model(&model.Discussion{}).Where("id > 0").
26+
Updates(map[string]any{
27+
"hot": gorm.Expr(hotFormula),
28+
"updated_at": gorm.Expr("updated_at"),
29+
}).Error; err != nil {
30+
return err
31+
}
32+
return nil
33+
}
34+
35+
func newUpdateHot() migrator.Migrator {
36+
return &updateHot{}
37+
}
38+
39+
func init() {
40+
registerDBMigrator(newUpdateHot)
41+
}

backend/model/forum.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,17 @@ type Forum struct {
55

66
Index uint `json:"index" gorm:"column:index"`
77
Name string `json:"name" gorm:"column:name;"`
8+
RouteName string `json:"route_name" gorm:"column:route_name;default:null;uniqueIndex"`
89
GroupIDs Int64Array `json:"group_ids" gorm:"column:group_ids;type:bigint[]"`
910
DatasetID string `json:"-" gorm:"column:dataset_id;type:text;uniqueIndex"`
1011
}
1112

1213
type ForumInfo struct {
13-
ID uint `json:"id"`
14-
Index uint `json:"index"`
15-
Name string `json:"name"`
16-
GroupIDs Int64Array `json:"group_ids" gorm:"type:bigint[]"`
14+
ID uint `json:"id"`
15+
Index uint `json:"index"`
16+
Name string `json:"name" binding:"required"`
17+
RouteName string `json:"route_name"`
18+
GroupIDs Int64Array `json:"group_ids" gorm:"type:bigint[]"`
1719
}
1820

1921
func init() {

backend/pkg/mq/mq.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,14 @@ package mq
33
import (
44
"context"
55
"time"
6+
7+
"github.com/nats-io/nats.go"
68
)
79

10+
type contextKey string
11+
12+
var keyMessageMetadata = contextKey("message_metadata")
13+
814
type Message interface{}
915

1016
type Topic interface {
@@ -34,3 +40,11 @@ type SubscriberWithHandler interface {
3440
type Publisher interface {
3541
Publish(ctx context.Context, topic Topic, data Message) error
3642
}
43+
44+
func MessageMetadata(ctx context.Context) *nats.MsgMetadata {
45+
metadata, ok := ctx.Value(keyMessageMetadata).(*nats.MsgMetadata)
46+
if !ok {
47+
return &nats.MsgMetadata{}
48+
}
49+
return metadata
50+
}

backend/pkg/mq/nats.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,9 @@ func (ns *natsSubscriber) Subscribe(ctx context.Context) error {
9090
case reflect.Ptr:
9191
param = val.Interface()
9292
}
93+
metadata, _ := msg.Metadata()
94+
ctx = context.WithValue(ctx, keyMessageMetadata, metadata)
9395
if err := h.Handle(ctx, param); err != nil {
94-
metadata, _ := msg.Metadata()
9596
ns.logger.WithContext(ctx).WithErr(err).With("topic", msg.Subject).With("metadata", metadata).Error("handle msg failed, wait retry")
9697
msg.NakWithDelay(time.Second * 10)
9798
return

backend/pkg/topic/doc_webhook.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package topic
2+
3+
import "github.com/chaitin/koalaqa/pkg/webhook/message"
4+
5+
var TopicDocWebhook = newTopic("koala.persistence.webhook.doc", true)
6+
7+
type MsgDocWebhook struct {
8+
MsgType message.Type `json:"action"`
9+
KBID uint `json:"kb_id"`
10+
DocID uint `json:"doc_id"`
11+
}

backend/pkg/webhook/message/common.go

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ type commonGetterIn struct {
2020
RepoDiscuss *repo.Discussion
2121
RepoGroupItems *repo.GroupItem
2222
RepoUserThird *repo.UserThird
23+
RepoDoc *repo.KBDocument
24+
RepoForum *repo.Forum
2325
}
2426

2527
type commonGetter struct {
@@ -30,7 +32,7 @@ func newCommonGetter(in commonGetterIn) *commonGetter {
3032
return &commonGetter{in: in}
3133
}
3234

33-
func (c *commonGetter) Message(ctx context.Context, dissID uint, userID uint) (*Common, error) {
35+
func (c *commonGetter) DiscussMessage(ctx context.Context, dissID uint, userID uint) (*Common, error) {
3436
var discuss struct {
3537
CreatedAt model.Timestamp `gorm:"type:timestamp with time zone"`
3638
ForumID uint
@@ -44,6 +46,12 @@ func (c *commonGetter) Message(ctx context.Context, dissID uint, userID uint) (*
4446
return nil, err
4547
}
4648

49+
var forum model.Forum
50+
err = c.in.RepoForum.GetByID(ctx, &forum, discuss.ForumID)
51+
if err != nil {
52+
return nil, err
53+
}
54+
4755
var groupItems []struct {
4856
Name string
4957
}
@@ -72,13 +80,9 @@ func (c *commonGetter) Message(ctx context.Context, dissID uint, userID uint) (*
7280
return nil, err
7381
}
7482

75-
discussURL := ""
76-
var publicAddress model.PublicAddress
77-
err = c.in.RepoSystem.GetValueByKey(ctx, &publicAddress, model.SystemKeyPublicAddress)
78-
if err != nil && !errors.Is(err, database.ErrRecordNotFound) {
83+
discussURL, err := c.publicAddress(ctx, path.Join(forum.RouteName, discuss.UUID))
84+
if err != nil {
7985
return nil, err
80-
} else if err == nil {
81-
discussURL = publicAddress.FullURL(path.Join("forum", strconv.FormatUint(uint64(discuss.ForumID), 10), "discuss", discuss.UUID))
8286
}
8387

8488
messageThirds := make([]commonUserThird, len(userThirds))
@@ -107,6 +111,42 @@ func (c *commonGetter) Message(ctx context.Context, dissID uint, userID uint) (*
107111
}, nil
108112
}
109113

114+
func (c *commonGetter) publicAddress(ctx context.Context, path string) (string, error) {
115+
var publicAddress model.PublicAddress
116+
err := c.in.RepoSystem.GetValueByKey(ctx, &publicAddress, model.SystemKeyPublicAddress)
117+
if err != nil {
118+
if !errors.Is(err, database.ErrRecordNotFound) {
119+
return "", err
120+
}
121+
122+
return "", nil
123+
}
124+
125+
return publicAddress.FullURL(path), nil
126+
}
127+
128+
func (c *commonGetter) DocMessage(ctx context.Context, kbID uint, docID uint) (*commonDoc, error) {
129+
var doc model.KBDocument
130+
err := c.in.RepoDoc.GetByID(ctx, &doc, kbID, docID)
131+
if err != nil {
132+
return nil, err
133+
}
134+
135+
docURL, err := c.publicAddress(ctx, "admin/ai/qa")
136+
if err != nil {
137+
return nil, err
138+
}
139+
if docURL != "" {
140+
docURL = docURL + "?id=" + strconv.FormatUint(uint64(kbID), 10)
141+
}
142+
143+
return &commonDoc{
144+
ID: doc.ID,
145+
Title: doc.Title,
146+
URL: docURL,
147+
}, nil
148+
}
149+
110150
func NewTestCommon() Common {
111151
return Common{
112152
User: commonUser{

backend/pkg/webhook/message/discuss.go

Lines changed: 36 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -29,31 +29,21 @@ func init() {
2929
}
3030

3131
type discussMsg struct {
32-
MsgType Type
33-
MsgTitle string
34-
HeadingPrefix string
32+
Header
3533

3634
Common
3735
}
3836

39-
type SendMsg struct {
37+
type sendDiscussMsg struct {
4038
*discussMsg
41-
webhookMsg
42-
}
43-
44-
func (d *discussMsg) Type() Type {
45-
return d.MsgType
46-
}
47-
48-
func (d *discussMsg) Title() string {
49-
return d.MsgTitle
39+
platformMsg
5040
}
5141

5242
func (d *discussMsg) Message(webhookType model.WebhookType) (string, error) {
5343
var buff bytes.Buffer
54-
err := discussTpl.Execute(&buff, SendMsg{
55-
discussMsg: d,
56-
webhookMsg: newWebhookMsg(webhookType),
44+
err := discussTpl.Execute(&buff, sendDiscussMsg{
45+
discussMsg: d,
46+
platformMsg: newPlatformMsg(webhookType),
5747
})
5848
if err != nil {
5949
return "", err
@@ -73,45 +63,55 @@ func (d *discussMsg) Data() Data {
7363

7464
func NewBotDislikeComment(body Common) Message {
7565
return &discussMsg{
76-
MsgType: TypeDislikeBotComment,
77-
MsgTitle: "不喜欢智能机器人的回答",
78-
HeadingPrefix: "问题",
79-
Common: body,
66+
Header: Header{
67+
MsgType: TypeDislikeBotComment,
68+
MsgTitle: "不喜欢智能机器人的回答",
69+
HeadingPrefix: "问题",
70+
},
71+
Common: body,
8072
}
8173
}
8274

8375
func NewBotUnknown(body Common) Message {
8476
return &discussMsg{
85-
MsgType: TypeBotUnknown,
86-
MsgTitle: "智能机器人无法解答问题",
87-
HeadingPrefix: "问题",
88-
Common: body,
77+
Header: Header{
78+
MsgType: TypeBotUnknown,
79+
MsgTitle: "智能机器人无法解答问题",
80+
HeadingPrefix: "问题",
81+
},
82+
Common: body,
8983
}
9084
}
9185

9286
func NewCreateQA(body Common) Message {
9387
return &discussMsg{
94-
MsgType: TypeNewQA,
95-
MsgTitle: "你有新的提问",
96-
HeadingPrefix: "提问",
97-
Common: body,
88+
Header: Header{
89+
MsgType: TypeNewQA,
90+
MsgTitle: "你有新的提问",
91+
HeadingPrefix: "提问",
92+
},
93+
Common: body,
9894
}
9995
}
10096

10197
func NewCreateFeedback(body Common) Message {
10298
return &discussMsg{
103-
MsgType: TypeNewFeedback,
104-
MsgTitle: "你有新的反馈",
105-
HeadingPrefix: "反馈",
106-
Common: body,
99+
Header: Header{
100+
MsgType: TypeNewFeedback,
101+
MsgTitle: "你有新的反馈",
102+
HeadingPrefix: "反馈",
103+
},
104+
Common: body,
107105
}
108106
}
109107

110108
func NewCreateBlog(body Common) Message {
111109
return &discussMsg{
112-
MsgType: TypeNewBlog,
113-
MsgTitle: "你有新的博客",
114-
HeadingPrefix: "博客",
115-
Common: body,
110+
Header: Header{
111+
MsgType: TypeNewBlog,
112+
MsgTitle: "你有新的博客",
113+
HeadingPrefix: "博客",
114+
},
115+
Common: body,
116116
}
117117
}

0 commit comments

Comments
 (0)