feat: 添加 AI 记账功能,包括 API 处理器和核心服务逻辑。
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"accounting-app/internal/service"
|
||||
@@ -26,6 +27,11 @@ type ChatRequest struct {
|
||||
Message string `json:"message" binding:"required"`
|
||||
}
|
||||
|
||||
// InsightRequest represents an insight generation request
|
||||
type InsightRequest struct {
|
||||
ContextData map[string]interface{} `json:"context_data" binding:"required"`
|
||||
}
|
||||
|
||||
// TranscribeRequest represents a transcription request
|
||||
type TranscribeRequest struct {
|
||||
// Audio file is sent as multipart form data
|
||||
@@ -43,6 +49,7 @@ func (h *AIHandler) RegisterRoutes(rg *gin.RouterGroup) {
|
||||
ai.POST("/chat", h.Chat)
|
||||
ai.POST("/transcribe", h.Transcribe)
|
||||
ai.POST("/confirm", h.Confirm)
|
||||
ai.POST("/insight", h.Insight)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,11 +87,53 @@ func (h *AIHandler) Chat(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
// Insight handles daily insight generation
|
||||
// POST /api/v1/ai/insight
|
||||
func (h *AIHandler) Insight(c *gin.Context) {
|
||||
var req InsightRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"success": false,
|
||||
"error": "Invalid request: " + err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
userID := uint(1)
|
||||
if id, exists := c.Get("user_id"); exists {
|
||||
userID = id.(uint)
|
||||
}
|
||||
|
||||
insightJSON, err := h.aiService.GenerateDailyInsight(c.Request.Context(), userID, req.ContextData)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"success": false,
|
||||
"error": "Failed to generate insight: " + err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Try to parse the JSON string from LLM to ensure structure
|
||||
var result map[string]interface{}
|
||||
if err := json.Unmarshal([]byte(insightJSON), &result); err != nil {
|
||||
// Fallback if LLM output isn't valid JSON (rare with correct prompt)
|
||||
result = map[string]interface{}{
|
||||
"spending": insightJSON,
|
||||
"budget": "暂无建议",
|
||||
}
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"data": result,
|
||||
})
|
||||
}
|
||||
|
||||
// Transcribe handles audio transcription
|
||||
// POST /api/v1/ai/transcribe
|
||||
// Requirements: 12.2, 12.6
|
||||
func (h *AIHandler) Transcribe(c *gin.Context) {
|
||||
// Get audio file from form
|
||||
// ... existing body ...
|
||||
file, header, err := c.Request.FormFile("audio")
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
|
||||
Reference in New Issue
Block a user