103 lines
3.3 KiB
Go
103 lines
3.3 KiB
Go
package store
|
|
|
|
import (
|
|
"database/sql"
|
|
"errors"
|
|
"time"
|
|
|
|
core "agent-system-core"
|
|
)
|
|
|
|
// Notice 是一条待投递的后台子代理完成通知(N2 WP5)。
|
|
type Notice struct {
|
|
ID string
|
|
ConversationID string
|
|
Owner string
|
|
Name string
|
|
Payload core.AgentResultPayload
|
|
TaskID string `json:"taskId,omitempty"`
|
|
AttemptID string `json:"attemptId,omitempty"`
|
|
CardID string `json:"cardId,omitempty"`
|
|
Material bool `json:"material,omitempty"`
|
|
CreatedAt time.Time
|
|
}
|
|
|
|
// AppendNotice 落一条待投递通知(重启后可重投)。
|
|
type noticePayload struct {
|
|
core.AgentResultPayload
|
|
TaskID string `json:"taskId,omitempty"`
|
|
AttemptID string `json:"attemptId,omitempty"`
|
|
CardID string `json:"cardId,omitempty"`
|
|
Material bool `json:"material,omitempty"`
|
|
}
|
|
|
|
func (s *Store) AppendNotice(n Notice) error {
|
|
_, err := s.db.Exec(s.q(`INSERT INTO pending_notices (id,conversation_id,owner,name,payload,created_at) VALUES(?,?,?,?,?,?) ON CONFLICT(id) DO NOTHING`),
|
|
n.ID, n.ConversationID, n.Owner, n.Name, marshalJSON(¬icePayload{n.Payload, n.TaskID, n.AttemptID, n.CardID, n.Material}), nowISO())
|
|
return err
|
|
}
|
|
|
|
// AppendNoticeAccountChecked stores a new result notice only while the owner
|
|
// is effective in the same transaction that creates the notice.
|
|
func (s *Store) AppendNoticeAccountChecked(n Notice, allowImplicitDefault bool) error {
|
|
return s.withEffectiveAccountWrite(n.Owner, allowImplicitDefault, func(tx *sql.Tx, _ Account) error {
|
|
var owner string
|
|
if err := tx.QueryRow(s.q(`SELECT owner FROM conversations WHERE id=?`), n.ConversationID).Scan(&owner); errors.Is(err, sql.ErrNoRows) {
|
|
return ErrNotFound
|
|
} else if err != nil {
|
|
return err
|
|
}
|
|
if owner != n.Owner {
|
|
return ErrNotFound
|
|
}
|
|
_, err := tx.Exec(s.q(`INSERT INTO pending_notices (id,conversation_id,owner,name,payload,created_at) VALUES(?,?,?,?,?,?) ON CONFLICT(id) DO NOTHING`),
|
|
n.ID, n.ConversationID, n.Owner, n.Name, marshalJSON(¬icePayload{n.Payload, n.TaskID, n.AttemptID, n.CardID, n.Material}), nowISO())
|
|
return err
|
|
})
|
|
}
|
|
|
|
// ListNotices 取回全部待投递通知(Manager 启动时装入内存队列)。
|
|
func (s *Store) ListNotices() ([]Notice, error) {
|
|
rows, err := s.db.Query(s.q(`SELECT id,conversation_id,owner,name,payload,created_at FROM pending_notices ORDER BY created_at`))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var out []Notice
|
|
for rows.Next() {
|
|
var n Notice
|
|
var payload, ca string
|
|
if err := rows.Scan(&n.ID, &n.ConversationID, &n.Owner, &n.Name, &payload, &ca); err != nil {
|
|
return nil, err
|
|
}
|
|
p, err := unmarshalJSON[noticePayload](payload)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
n.Payload = p.AgentResultPayload
|
|
n.TaskID = p.TaskID
|
|
n.AttemptID = p.AttemptID
|
|
n.CardID = p.CardID
|
|
n.Material = p.Material
|
|
n.CreatedAt, _ = parseTime(ca)
|
|
out = append(out, n)
|
|
}
|
|
return out, rows.Err()
|
|
}
|
|
|
|
// DeleteNotice 投递成功后删除(按 id 幂等)。
|
|
func (s *Store) DeleteNotice(id string) error {
|
|
_, err := s.db.Exec(s.q(`DELETE FROM pending_notices WHERE id=?`), id)
|
|
return err
|
|
}
|
|
|
|
// DeleteNotices 批量删除(投递成功批)。
|
|
func (s *Store) DeleteNotices(ids []string) error {
|
|
for _, id := range ids {
|
|
if err := s.DeleteNotice(id); err != nil && !errors.Is(err, sql.ErrNoRows) {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|