feat: connect inbox confirmation workflow
This commit is contained in:
@@ -1,17 +1,33 @@
|
||||
package inbox
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
"senlinai-agent/backend/internal/httpx"
|
||||
"senlinai-agent/backend/internal/logic/auth"
|
||||
"senlinai-agent/backend/internal/models"
|
||||
)
|
||||
|
||||
type Handler struct {
|
||||
service *Service
|
||||
}
|
||||
|
||||
// InboxItemDTO 隔离 Gorm 自增主键,只通过公开 identity 关联项目和条目。
|
||||
type InboxItemDTO struct {
|
||||
ID string `json:"id"`
|
||||
ProjectID string `json:"projectId"`
|
||||
SourceType string `json:"sourceType"`
|
||||
Title string `json:"title"`
|
||||
Body string `json:"body"`
|
||||
Status string `json:"status"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
}
|
||||
|
||||
func NewHandler(service *Service) *Handler {
|
||||
return &Handler{service: service}
|
||||
}
|
||||
@@ -23,68 +39,101 @@ func (h *Handler) Register(router gin.IRouter) {
|
||||
}
|
||||
|
||||
func (h *Handler) capture(c *gin.Context) {
|
||||
userID, ok := auth.CurrentUserID(c)
|
||||
userID, ok := currentInboxUser(c)
|
||||
if !ok {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "missing current user"})
|
||||
return
|
||||
}
|
||||
projectID, err := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid project id"})
|
||||
projectIdentity, ok := httpx.IdentityParam(c, "id")
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
var input struct {
|
||||
SourceType string `json:"source_type"`
|
||||
SourceType string `json:"sourceType"`
|
||||
Title string `json:"title"`
|
||||
Body string `json:"body"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&input); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request"})
|
||||
httpx.Error(c, http.StatusBadRequest, "invalid_request", "请求参数无效")
|
||||
return
|
||||
}
|
||||
item, err := h.service.Capture(CaptureInput{ProjectID: uint(projectID), UserID: userID, SourceType: input.SourceType, Title: input.Title, Body: input.Body})
|
||||
item, err := h.service.Capture(CaptureInput{
|
||||
ProjectIdentity: projectIdentity, UserID: userID, SourceType: input.SourceType, Title: input.Title, Body: input.Body,
|
||||
})
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
writeInboxError(c, err)
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusCreated, item)
|
||||
c.JSON(http.StatusCreated, inboxItemDTO(*item))
|
||||
}
|
||||
|
||||
func (h *Handler) analyze(c *gin.Context) {
|
||||
userID, ok := auth.CurrentUserID(c)
|
||||
userID, ok := currentInboxUser(c)
|
||||
if !ok {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "missing current user"})
|
||||
return
|
||||
}
|
||||
itemID, err := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid inbox id"})
|
||||
itemIdentity, ok := httpx.IdentityParam(c, "id")
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
suggestions, err := h.service.Analyze(uint(itemID), userID)
|
||||
suggestions, err := h.service.Analyze(itemIdentity, userID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
writeInboxError(c, err)
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"suggestions": suggestions})
|
||||
}
|
||||
|
||||
func (h *Handler) confirm(c *gin.Context) {
|
||||
itemID, err := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid inbox id"})
|
||||
userID, ok := currentInboxUser(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
itemIdentity, ok := httpx.IdentityParam(c, "id")
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
var input struct {
|
||||
Suggestions []Suggestion `json:"suggestions"`
|
||||
SuggestionIDs []string `json:"suggestionIds"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&input); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request"})
|
||||
httpx.Error(c, http.StatusBadRequest, "invalid_request", "请求参数无效")
|
||||
return
|
||||
}
|
||||
if err := h.service.Confirm(uint(itemID), input.Suggestions); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
result, err := h.service.Confirm(itemIdentity, userID, input.SuggestionIDs)
|
||||
if err != nil {
|
||||
writeInboxError(c, err)
|
||||
return
|
||||
}
|
||||
c.Status(http.StatusNoContent)
|
||||
c.JSON(http.StatusOK, result)
|
||||
}
|
||||
|
||||
func currentInboxUser(c *gin.Context) (uint, bool) {
|
||||
userID, ok := auth.CurrentUserID(c)
|
||||
if !ok {
|
||||
httpx.Error(c, http.StatusUnauthorized, "unauthorized", "未登录或登录已失效")
|
||||
return 0, false
|
||||
}
|
||||
return userID, true
|
||||
}
|
||||
|
||||
func inboxItemDTO(item models.SenlinAgentInboxItem) InboxItemDTO {
|
||||
return InboxItemDTO{
|
||||
ID: item.Identity, ProjectID: item.ProjectIdentity, SourceType: item.SourceType,
|
||||
Title: item.Title, Body: item.Body, Status: item.Status,
|
||||
CreatedAt: item.CreatedAt.UTC(), UpdatedAt: item.UpdatedAt.UTC(),
|
||||
}
|
||||
}
|
||||
|
||||
func writeInboxError(c *gin.Context, err error) {
|
||||
switch {
|
||||
case errors.Is(err, gorm.ErrRecordNotFound):
|
||||
httpx.Error(c, http.StatusNotFound, "not_found", "Inbox 条目或项目不存在")
|
||||
case errors.Is(err, ErrInboxAlreadyConfirmed):
|
||||
httpx.Error(c, http.StatusConflict, "conflict", "该 Inbox 条目已经确认处理")
|
||||
case errors.Is(err, ErrProjectAndUserRequired), errors.Is(err, ErrSourceTypeRequired),
|
||||
errors.Is(err, ErrSuggestionSelectionRequired), errors.Is(err, ErrSuggestionNotFound):
|
||||
httpx.Error(c, http.StatusBadRequest, "invalid_request", "请求参数或建议选择无效")
|
||||
default:
|
||||
httpx.Error(c, http.StatusInternalServerError, "internal_error", "Inbox 操作失败,请稍后重试")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user