mirror of
https://github.com/serty2005/rmser.git
synced 2026-02-04 19:02:33 -06:00
добавил ручки для фронта
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,2 +1,3 @@
|
||||
# Python virtual environment
|
||||
.venv
|
||||
*.py
|
||||
24
cmd/main.go
24
cmd/main.go
@@ -5,6 +5,7 @@ import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/gin-contrib/cors"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
"github.com/shopspring/decimal"
|
||||
@@ -71,7 +72,11 @@ func main() {
|
||||
recService := recServicePkg.NewService(recRepo)
|
||||
ocrService := ocrServicePkg.NewService(ocrRepo, catalogRepo, pyClient)
|
||||
invoiceService := invServicePkg.NewService(rmsClient)
|
||||
|
||||
// --- Инициализация Handler'ов ---
|
||||
invoiceHandler := handlers.NewInvoiceHandler(invoiceService)
|
||||
ocrHandler := handlers.NewOCRHandler(ocrService)
|
||||
recommendHandler := handlers.NewRecommendationsHandler(recService)
|
||||
|
||||
// --- БЛОК ПРОВЕРКИ СИНХРОНИЗАЦИИ (Run-once on start) ---
|
||||
go func() {
|
||||
@@ -129,16 +134,33 @@ func main() {
|
||||
} else {
|
||||
logger.Log.Warn("Telegram token не задан, бот не запущен")
|
||||
}
|
||||
|
||||
// 5. Запуск HTTP сервера (Gin)
|
||||
if cfg.App.Mode == "release" {
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
}
|
||||
r := gin.Default()
|
||||
|
||||
// --- Настройка CORS ---
|
||||
// Разрешаем запросы с любых источников для разработки Frontend
|
||||
corsConfig := cors.DefaultConfig()
|
||||
corsConfig.AllowAllOrigins = true // В продакшене заменить на AllowOrigins: []string{"http://domain.com"}
|
||||
corsConfig.AllowMethods = []string{"GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"}
|
||||
corsConfig.AllowHeaders = []string{"Origin", "Content-Length", "Content-Type", "Authorization"}
|
||||
r.Use(cors.New(corsConfig))
|
||||
|
||||
api := r.Group("/api")
|
||||
{
|
||||
// ... другие роуты ...
|
||||
// Invoices
|
||||
api.POST("/invoices/send", invoiceHandler.SendInvoice)
|
||||
|
||||
// Recommendations
|
||||
api.GET("/recommendations", recommendHandler.GetRecommendations)
|
||||
|
||||
// OCR
|
||||
api.GET("/ocr/catalog", ocrHandler.GetCatalog)
|
||||
api.GET("/ocr/matches", ocrHandler.GetMatches)
|
||||
api.POST("/ocr/match", ocrHandler.SaveMatch)
|
||||
}
|
||||
|
||||
// Простой хелсчек
|
||||
|
||||
5
go.mod
5
go.mod
@@ -3,6 +3,7 @@ module rmser
|
||||
go 1.25.1
|
||||
|
||||
require (
|
||||
github.com/gin-contrib/cors v1.7.6
|
||||
github.com/gin-gonic/gin v1.11.0
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/jackc/pgx/v5 v5.6.0
|
||||
@@ -22,13 +23,13 @@ require (
|
||||
github.com/cloudwego/base64x v0.1.6 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.9 // indirect
|
||||
github.com/gin-contrib/sse v1.1.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.27.0 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
|
||||
github.com/goccy/go-json v0.10.2 // indirect
|
||||
github.com/goccy/go-json v0.10.5 // indirect
|
||||
github.com/goccy/go-yaml v1.18.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||
|
||||
10
go.sum
10
go.sum
@@ -132,9 +132,11 @@ github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7z
|
||||
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
|
||||
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
|
||||
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
|
||||
github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY=
|
||||
github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/gin-contrib/cors v1.7.6 h1:3gQ8GMzs1Ylpf70y8bMw4fVpycXIeX1ZemuSQIsnQQY=
|
||||
github.com/gin-contrib/cors v1.7.6/go.mod h1:Ulcl+xN4jel9t1Ry8vqph23a60FwH9xVLd+3ykmTjOk=
|
||||
github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
|
||||
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
|
||||
github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk=
|
||||
@@ -163,8 +165,8 @@ github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAu
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
||||
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/goccy/go-yaml v1.9.5/go.mod h1:U/jl18uSupI5rdI2jmuCswEA2htH9eXfferR3KfscvA=
|
||||
github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
|
||||
github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
||||
|
||||
@@ -28,4 +28,7 @@ type Repository interface {
|
||||
|
||||
// FindMatch ищет товар по точному совпадению названия
|
||||
FindMatch(rawName string) (*uuid.UUID, error)
|
||||
|
||||
// GetAllMatches возвращает все существующие привязки
|
||||
GetAllMatches() ([]ProductMatch, error)
|
||||
}
|
||||
|
||||
@@ -47,3 +47,11 @@ func (r *pgRepository) FindMatch(rawName string) (*uuid.UUID, error) {
|
||||
|
||||
return &match.ProductID, nil
|
||||
}
|
||||
|
||||
func (r *pgRepository) GetAllMatches() ([]ocr.ProductMatch, error) {
|
||||
var matches []ocr.ProductMatch
|
||||
// Preload("Product") загружает связанную сущность товара,
|
||||
// чтобы мы видели не только ID, но и название товара из каталога.
|
||||
err := r.db.Preload("Product").Order("updated_at DESC").Find(&matches).Error
|
||||
return matches, err
|
||||
}
|
||||
|
||||
@@ -67,7 +67,6 @@ func (s *Service) ProcessReceiptImage(ctx context.Context, imgData []byte) ([]Pr
|
||||
} else {
|
||||
// Если не нашли, пробуем найти точное совпадение по имени в каталоге (на всякий случай)
|
||||
// (В реальном проекте тут может быть нечеткий поиск, но пока точный)
|
||||
// TODO: Добавить метод FindByName в репозиторий каталога, если нужно
|
||||
}
|
||||
|
||||
processed = append(processed, item)
|
||||
@@ -118,6 +117,11 @@ func (s *Service) SaveMapping(rawName string, productID uuid.UUID) error {
|
||||
return s.ocrRepo.SaveMatch(rawName, productID)
|
||||
}
|
||||
|
||||
// GetKnownMatches возвращает список всех обученных связей
|
||||
func (s *Service) GetKnownMatches() ([]ocr.ProductMatch, error) {
|
||||
return s.ocrRepo.GetAllMatches()
|
||||
}
|
||||
|
||||
// FindKnownMatch ищет, знаем ли мы уже этот товар
|
||||
func (s *Service) FindKnownMatch(rawName string) (*uuid.UUID, error) {
|
||||
return s.ocrRepo.FindMatch(rawName)
|
||||
|
||||
@@ -57,3 +57,15 @@ func (h *OCRHandler) SaveMatch(c *gin.Context) {
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"status": "saved"})
|
||||
}
|
||||
|
||||
// GetMatches возвращает список всех обученных связей
|
||||
func (h *OCRHandler) GetMatches(c *gin.Context) {
|
||||
matches, err := h.service.GetKnownMatches()
|
||||
if err != nil {
|
||||
logger.Log.Error("Ошибка получения списка матчей", zap.Error(err))
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, matches)
|
||||
}
|
||||
|
||||
37
internal/transport/http/handlers/recommendations.go
Normal file
37
internal/transport/http/handlers/recommendations.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
|
||||
"rmser/internal/services/recommend"
|
||||
"rmser/pkg/logger"
|
||||
)
|
||||
|
||||
type RecommendationsHandler struct {
|
||||
service *recommend.Service
|
||||
}
|
||||
|
||||
func NewRecommendationsHandler(service *recommend.Service) *RecommendationsHandler {
|
||||
return &RecommendationsHandler{service: service}
|
||||
}
|
||||
|
||||
// GetRecommendations godoc
|
||||
// @Summary Получить список рекомендаций
|
||||
// @Description Возвращает сгенерированные рекомендации (проблемные зоны учета)
|
||||
// @Tags recommendations
|
||||
// @Produce json
|
||||
// @Success 200 {array} recommendations.Recommendation
|
||||
// @Failure 500 {object} map[string]string
|
||||
func (h *RecommendationsHandler) GetRecommendations(c *gin.Context) {
|
||||
recs, err := h.service.GetRecommendations()
|
||||
if err != nil {
|
||||
logger.Log.Error("Ошибка получения рекомендаций", zap.Error(err))
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, recs)
|
||||
}
|
||||
Reference in New Issue
Block a user