mirror of
https://github.com/serty2005/rmser.git
synced 2026-02-04 19:02:33 -06:00
added front - react+ts
ocr improved
This commit is contained in:
@@ -42,8 +42,6 @@ func (s *Service) ProcessReceiptImage(ctx context.Context, imgData []byte) ([]Pr
|
||||
}
|
||||
|
||||
var processed []ProcessedItem
|
||||
|
||||
// 2. Обрабатываем каждую строку
|
||||
for _, rawItem := range rawResult.Items {
|
||||
item := ProcessedItem{
|
||||
RawName: rawItem.RawName,
|
||||
@@ -52,26 +50,24 @@ func (s *Service) ProcessReceiptImage(ctx context.Context, imgData []byte) ([]Pr
|
||||
Sum: decimal.NewFromFloat(rawItem.Sum),
|
||||
}
|
||||
|
||||
// 3. Ищем соответствие
|
||||
// Сначала проверяем таблицу ручного обучения (product_matches)
|
||||
matchID, err := s.ocrRepo.FindMatch(rawItem.RawName)
|
||||
match, err := s.ocrRepo.FindMatch(rawItem.RawName)
|
||||
if err != nil {
|
||||
logger.Log.Error("db error finding match", zap.Error(err))
|
||||
}
|
||||
|
||||
if matchID != nil {
|
||||
// Нашли в обучении
|
||||
item.ProductID = matchID
|
||||
if match != nil {
|
||||
item.ProductID = &match.ProductID
|
||||
item.IsMatched = true
|
||||
item.MatchSource = "learned"
|
||||
// Здесь мы могли бы подтянуть quantity/container из матча,
|
||||
// но пока фронт сам это сделает, запросив /ocr/matches или получив подсказку.
|
||||
} else {
|
||||
// Если не нашли, пробуем найти точное совпадение по имени в каталоге (на всякий случай)
|
||||
// (В реальном проекте тут может быть нечеткий поиск, но пока точный)
|
||||
if err := s.ocrRepo.UpsertUnmatched(rawItem.RawName); err != nil {
|
||||
logger.Log.Warn("failed to save unmatched", zap.Error(err))
|
||||
}
|
||||
}
|
||||
|
||||
processed = append(processed, item)
|
||||
}
|
||||
|
||||
return processed, nil
|
||||
}
|
||||
|
||||
@@ -87,14 +83,21 @@ type ProcessedItem struct {
|
||||
MatchSource string // "learned", "auto", "manual"
|
||||
}
|
||||
|
||||
// ProductForIndex DTO для внешнего сервиса
|
||||
type ProductForIndex struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Code string `json:"code"`
|
||||
type ContainerForIndex struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Count float64 `json:"count"`
|
||||
}
|
||||
|
||||
// GetCatalogForIndexing возвращает список товаров для построения индекса
|
||||
type ProductForIndex struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Code string `json:"code"`
|
||||
MeasureUnit string `json:"measure_unit"`
|
||||
Containers []ContainerForIndex `json:"containers"`
|
||||
}
|
||||
|
||||
// GetCatalogForIndexing - возвращает облегченный каталог
|
||||
func (s *Service) GetCatalogForIndexing() ([]ProductForIndex, error) {
|
||||
products, err := s.catalogRepo.GetActiveGoods()
|
||||
if err != nil {
|
||||
@@ -103,18 +106,35 @@ func (s *Service) GetCatalogForIndexing() ([]ProductForIndex, error) {
|
||||
|
||||
result := make([]ProductForIndex, 0, len(products))
|
||||
for _, p := range products {
|
||||
uom := ""
|
||||
if p.MainUnit != nil {
|
||||
uom = p.MainUnit.Name
|
||||
}
|
||||
|
||||
var conts []ContainerForIndex
|
||||
for _, c := range p.Containers {
|
||||
cnt, _ := c.Count.Float64()
|
||||
conts = append(conts, ContainerForIndex{
|
||||
ID: c.ID.String(),
|
||||
Name: c.Name,
|
||||
Count: cnt,
|
||||
})
|
||||
}
|
||||
|
||||
result = append(result, ProductForIndex{
|
||||
ID: p.ID.String(),
|
||||
Name: p.Name,
|
||||
Code: p.Code,
|
||||
ID: p.ID.String(),
|
||||
Name: p.Name,
|
||||
Code: p.Code,
|
||||
MeasureUnit: uom,
|
||||
Containers: conts,
|
||||
})
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// SaveMapping сохраняет связь "Текст из чека" -> "Наш товар"
|
||||
func (s *Service) SaveMapping(rawName string, productID uuid.UUID) error {
|
||||
return s.ocrRepo.SaveMatch(rawName, productID)
|
||||
// SaveMapping сохраняет привязку с количеством и фасовкой
|
||||
func (s *Service) SaveMapping(rawName string, productID uuid.UUID, quantity decimal.Decimal, containerID *uuid.UUID) error {
|
||||
return s.ocrRepo.SaveMatch(rawName, productID, quantity, containerID)
|
||||
}
|
||||
|
||||
// GetKnownMatches возвращает список всех обученных связей
|
||||
@@ -122,8 +142,14 @@ func (s *Service) GetKnownMatches() ([]ocr.ProductMatch, error) {
|
||||
return s.ocrRepo.GetAllMatches()
|
||||
}
|
||||
|
||||
// GetUnmatchedItems возвращает список частых нераспознанных строк
|
||||
func (s *Service) GetUnmatchedItems() ([]ocr.UnmatchedItem, error) {
|
||||
// Берем топ 50 нераспознанных
|
||||
return s.ocrRepo.GetTopUnmatched(50)
|
||||
}
|
||||
|
||||
// FindKnownMatch ищет, знаем ли мы уже этот товар
|
||||
func (s *Service) FindKnownMatch(rawName string) (*uuid.UUID, error) {
|
||||
func (s *Service) FindKnownMatch(rawName string) (*ocr.ProductMatch, error) {
|
||||
return s.ocrRepo.FindMatch(rawName)
|
||||
}
|
||||
|
||||
|
||||
@@ -49,8 +49,15 @@ func NewService(
|
||||
|
||||
// SyncCatalog загружает номенклатуру и сохраняет в БД
|
||||
func (s *Service) SyncCatalog() error {
|
||||
logger.Log.Info("Начало синхронизации номенклатуры")
|
||||
logger.Log.Info("Начало синхронизации каталога...")
|
||||
|
||||
// 1. Сначала Единицы измерения (чтобы FK не ругался)
|
||||
if err := s.syncMeasureUnits(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 2. Товары
|
||||
logger.Log.Info("Запрос товаров из RMS...")
|
||||
products, err := s.rmsClient.FetchCatalog()
|
||||
if err != nil {
|
||||
return fmt.Errorf("ошибка получения каталога из RMS: %w", err)
|
||||
@@ -64,6 +71,19 @@ func (s *Service) SyncCatalog() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) syncMeasureUnits() error {
|
||||
logger.Log.Info("Синхронизация единиц измерения...")
|
||||
units, err := s.rmsClient.FetchMeasureUnits()
|
||||
if err != nil {
|
||||
return fmt.Errorf("ошибка получения ед.изм: %w", err)
|
||||
}
|
||||
if err := s.catalogRepo.SaveMeasureUnits(units); err != nil {
|
||||
return fmt.Errorf("ошибка сохранения ед.изм: %w", err)
|
||||
}
|
||||
logger.Log.Info("Единицы измерения обновлены", zap.Int("count", len(units)))
|
||||
return nil
|
||||
}
|
||||
|
||||
// SyncRecipes загружает техкарты за указанный период (или за последние 30 дней по умолчанию)
|
||||
func (s *Service) SyncRecipes() error {
|
||||
logger.Log.Info("Начало синхронизации техкарт")
|
||||
|
||||
Reference in New Issue
Block a user