You've already forked agentic-coding-workflow
feat(chat): TemplateService for prompt templates with system/user scope auth
This commit is contained in:
@@ -0,0 +1,109 @@
|
|||||||
|
package chat
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"log/slog"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
|
||||||
|
"github.com/yan1h/agent-coding-workflow/internal/audit"
|
||||||
|
"github.com/yan1h/agent-coding-workflow/internal/infra/errs"
|
||||||
|
)
|
||||||
|
|
||||||
|
type templateService struct {
|
||||||
|
repo Repository
|
||||||
|
auditor audit.Recorder
|
||||||
|
log *slog.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ TemplateService = (*templateService)(nil)
|
||||||
|
|
||||||
|
// NewTemplateService 构造 prompt_templates CRUD 服务。
|
||||||
|
func NewTemplateService(repo Repository, a audit.Recorder, log *slog.Logger) TemplateService {
|
||||||
|
return &templateService{repo: repo, auditor: a, log: log}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *templateService) List(ctx context.Context, userID uuid.UUID) ([]PromptTemplate, error) {
|
||||||
|
return s.repo.ListTemplatesForUser(ctx, userID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *templateService) Create(ctx context.Context, userID uuid.UUID, name, content string, scope TemplateScope) (*PromptTemplate, error) {
|
||||||
|
if scope != TemplateScopeSystem && scope != TemplateScopeUser {
|
||||||
|
return nil, errs.New(errs.CodeInvalidInput, "invalid scope")
|
||||||
|
}
|
||||||
|
t := &PromptTemplate{
|
||||||
|
ID: uuid.Must(uuid.NewV7()),
|
||||||
|
Name: name,
|
||||||
|
Content: content,
|
||||||
|
Scope: scope,
|
||||||
|
CreatedAt: time.Now(),
|
||||||
|
UpdatedAt: time.Now(),
|
||||||
|
}
|
||||||
|
if scope == TemplateScopeUser {
|
||||||
|
uid := userID
|
||||||
|
t.OwnerID = &uid
|
||||||
|
}
|
||||||
|
if err := s.repo.InsertTemplate(ctx, t); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if scope == TemplateScopeSystem {
|
||||||
|
s.tryAudit(ctx, audit.Entry{
|
||||||
|
UserID: &userID, Action: "prompt_template.created",
|
||||||
|
TargetType: "prompt_template", TargetID: t.ID.String(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return t, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *templateService) Update(ctx context.Context, userID, id uuid.UUID, name, content string) error {
|
||||||
|
t, err := s.repo.GetTemplate(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if t == nil {
|
||||||
|
return errs.New(errs.CodeChatTemplateNotFound, "template not found")
|
||||||
|
}
|
||||||
|
if t.Scope == TemplateScopeUser && (t.OwnerID == nil || *t.OwnerID != userID) {
|
||||||
|
return errs.New(errs.CodeForbidden, "not template owner")
|
||||||
|
}
|
||||||
|
if err := s.repo.UpdateTemplate(ctx, id, name, content); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if t.Scope == TemplateScopeSystem {
|
||||||
|
s.tryAudit(ctx, audit.Entry{
|
||||||
|
UserID: &userID, Action: "prompt_template.updated",
|
||||||
|
TargetType: "prompt_template", TargetID: id.String(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *templateService) Delete(ctx context.Context, userID, id uuid.UUID) error {
|
||||||
|
t, err := s.repo.GetTemplate(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if t == nil {
|
||||||
|
return errs.New(errs.CodeChatTemplateNotFound, "template not found")
|
||||||
|
}
|
||||||
|
if t.Scope == TemplateScopeUser && (t.OwnerID == nil || *t.OwnerID != userID) {
|
||||||
|
return errs.New(errs.CodeForbidden, "not template owner")
|
||||||
|
}
|
||||||
|
if err := s.repo.DeleteTemplate(ctx, id); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if t.Scope == TemplateScopeSystem {
|
||||||
|
s.tryAudit(ctx, audit.Entry{
|
||||||
|
UserID: &userID, Action: "prompt_template.deleted",
|
||||||
|
TargetType: "prompt_template", TargetID: id.String(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *templateService) tryAudit(ctx context.Context, e audit.Entry) {
|
||||||
|
if err := s.auditor.Record(ctx, e); err != nil {
|
||||||
|
s.log.Warn("audit record failed", "action", e.Action, "err", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user