Перевел на multi-tenant

Добавил поставщиков
Накладные успешно создаются из фронта
This commit is contained in:
2025-12-18 03:56:21 +03:00
parent 47ec8094e5
commit 542beafe0e
38 changed files with 1942 additions and 977 deletions

View File

@@ -22,9 +22,19 @@ func NewOCRHandler(service *ocrService.Service) *OCRHandler {
// GetCatalog возвращает список товаров для OCR сервиса
func (h *OCRHandler) GetCatalog(c *gin.Context) {
items, err := h.service.GetCatalogForIndexing()
// Если этот эндпоинт дергает Python-скрипт без токена пользователя - это проблема безопасности.
// Либо Python скрипт должен передавать токен админа/системы и ID сервера в query.
// ПОКА: Предполагаем, что запрос идет от фронта или с заголовком X-Telegram-User-ID.
// Если заголовка нет (вызов от скрипта), пробуем взять server_id из query (небезопасно, но для MVP)
// Или лучше так: этот метод вызывается Фронтендом для поиска? Нет, название GetCatalogForIndexing намекает на OCR.
// Оставим пока требование UserID.
userID := c.MustGet("userID").(uuid.UUID)
items, err := h.service.GetCatalogForIndexing(userID)
if err != nil {
logger.Log.Error("Ошибка получения каталога для OCR", zap.Error(err))
logger.Log.Error("Ошибка получения каталога", zap.Error(err))
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
@@ -38,8 +48,9 @@ type MatchRequest struct {
ContainerID *string `json:"container_id"`
}
// SaveMatch сохраняет привязку (обучение)
func (h *OCRHandler) SaveMatch(c *gin.Context) {
userID := c.MustGet("userID").(uuid.UUID)
var req MatchRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
@@ -64,7 +75,7 @@ func (h *OCRHandler) SaveMatch(c *gin.Context) {
}
}
if err := h.service.SaveMapping(req.RawName, pID, qty, contID); err != nil {
if err := h.service.SaveMapping(userID, req.RawName, pID, qty, contID); err != nil {
logger.Log.Error("Ошибка сохранения матчинга", zap.Error(err))
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
@@ -73,18 +84,16 @@ func (h *OCRHandler) SaveMatch(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"status": "saved"})
}
// DeleteMatch удаляет связь
func (h *OCRHandler) DeleteMatch(c *gin.Context) {
// Получаем raw_name из query параметров, так как в URL path могут быть спецсимволы
// Пример: DELETE /api/ocr/match?raw_name=Хлеб%20Бородинский
userID := c.MustGet("userID").(uuid.UUID)
rawName := c.Query("raw_name")
if rawName == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "raw_name is required"})
return
}
if err := h.service.DeleteMatch(rawName); err != nil {
logger.Log.Error("Ошибка удаления матча", zap.Error(err))
if err := h.service.DeleteMatch(userID, rawName); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
@@ -92,43 +101,32 @@ func (h *OCRHandler) DeleteMatch(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"status": "deleted"})
}
// SearchProducts ищет товары (для автокомплита)
func (h *OCRHandler) SearchProducts(c *gin.Context) {
query := c.Query("q") // ?q=молоко
if query == "" {
c.JSON(http.StatusOK, []interface{}{})
return
}
userID := c.MustGet("userID").(uuid.UUID)
query := c.Query("q")
products, err := h.service.SearchProducts(query)
products, err := h.service.SearchProducts(userID, query)
if err != nil {
logger.Log.Error("Search error", zap.Error(err))
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
// Отдаем на фронт упрощенную структуру или полную, в зависимости от нужд.
// Product entity уже содержит JSON теги, так что можно отдать напрямую.
c.JSON(http.StatusOK, products)
}
// GetMatches возвращает список всех обученных связей
func (h *OCRHandler) GetMatches(c *gin.Context) {
matches, err := h.service.GetKnownMatches()
userID := c.MustGet("userID").(uuid.UUID)
matches, err := h.service.GetKnownMatches(userID)
if err != nil {
logger.Log.Error("Ошибка получения списка матчей", zap.Error(err))
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, matches)
}
// GetUnmatched возвращает список нераспознанных позиций для подсказок
func (h *OCRHandler) GetUnmatched(c *gin.Context) {
items, err := h.service.GetUnmatchedItems()
userID := c.MustGet("userID").(uuid.UUID)
items, err := h.service.GetUnmatchedItems(userID)
if err != nil {
logger.Log.Error("Ошибка получения списка unmatched", zap.Error(err))
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}