Added structured logging across services and repositories. Updated SQL queries to use parameterized placeholders for better readability and security. Enhanced error handling for external service communication.

This commit is contained in:
2026-05-15 22:07:03 +03:00
parent c3f90b57c2
commit 8462b16305
11 changed files with 296 additions and 63 deletions
+2
View File
@@ -0,0 +1,2 @@
Portainer
admin - 4c#;=H36$s^J
+3 -2
View File
@@ -7,6 +7,7 @@ import (
"FamilyHub/src/domain" "FamilyHub/src/domain"
"errors" "errors"
"io" "io"
"log"
"net/http" "net/http"
"strconv" "strconv"
"strings" "strings"
@@ -73,13 +74,13 @@ func (router *TransactionsRouter) Create(c *gin.Context) {
c.JSON(http.StatusBadRequest, dto.ErrorResponse{Message: err.Error()}) c.JSON(http.StatusBadRequest, dto.ErrorResponse{Message: err.Error()})
return return
} }
log.Printf("%+v\n", input)
transaction, err := router.creationService.Create(c.Request.Context(), input) transaction, err := router.creationService.Create(c.Request.Context(), input)
if err != nil { if err != nil {
handleTransactionError(c, err) handleTransactionError(c, err)
return return
} }
log.Printf("%+v\n", transaction)
c.JSON(http.StatusCreated, dto.TransactionToResponse(transaction)) c.JSON(http.StatusCreated, dto.TransactionToResponse(transaction))
} }
+12 -1
View File
@@ -4,8 +4,10 @@ import (
"FamilyHub/src/domain" "FamilyHub/src/domain"
"FamilyHub/src/integrations/receiptProvider" "FamilyHub/src/integrations/receiptProvider"
"FamilyHub/src/repositories" "FamilyHub/src/repositories"
"FamilyHub/src/utils"
"context" "context"
"fmt" "fmt"
"log"
"strings" "strings"
) )
@@ -35,18 +37,23 @@ func (s *receiptService) AddReceipt(
ctx context.Context, ctx context.Context,
req domain.AddReceiptRequest, req domain.AddReceiptRequest,
) (*domain.Receipt, error) { ) (*domain.Receipt, error) {
log.Printf("receipt add request: payload=%s", utils.ToLogJSON(req))
receipt, err := s.provider.GetReceipt(ctx, req.Date, req.Number) receipt, err := s.provider.GetReceipt(ctx, req.Date, req.Number)
if err != nil { if err != nil {
log.Printf("receipt add failed: err=%v payload=%s", err, utils.ToLogJSON(req))
return nil, err return nil, err
} }
receiptID, err := s.repo.Create(ctx, receipt) receiptID, err := s.repo.Create(ctx, receipt)
if err != nil { if err != nil {
log.Printf("receipt persist failed: err=%v receipt=%s", err, utils.ToLogJSON(receipt))
return nil, err return nil, err
} }
receipt.ID = int(receiptID) receipt.ID = int(receiptID)
if !s.shouldCreateTransaction(req) { if !s.shouldCreateTransaction(req) {
log.Printf("receipt add response: payload=%s", utils.ToLogJSON(receipt))
return receipt, nil return receipt, nil
} }
@@ -59,6 +66,7 @@ func (s *receiptService) AddReceipt(
} }
receipt.TransactionID = &transaction.ID receipt.TransactionID = &transaction.ID
log.Printf("receipt add response: payload=%s", utils.ToLogJSON(receipt))
return receipt, nil return receipt, nil
} }
@@ -94,11 +102,14 @@ func (s *receiptService) createTransactionForReceipt(
CreatedBy: *req.CreatedBy, CreatedBy: *req.CreatedBy,
ReceiptID: &receiptID, ReceiptID: &receiptID,
} }
log.Printf("%+v\n", transaction)
log.Printf("receipt transaction create request: payload=%s", utils.ToLogJSON(transaction))
if err := s.transactionRepo.Create(ctx, transaction); err != nil { if err := s.transactionRepo.Create(ctx, transaction); err != nil {
log.Printf("receipt transaction create failed: err=%v payload=%s", err, utils.ToLogJSON(transaction))
return nil, err return nil, err
} }
log.Printf("receipt transaction create response: payload=%s", utils.ToLogJSON(transaction))
return transaction, nil return transaction, nil
} }
+8
View File
@@ -3,10 +3,12 @@ package services
import ( import (
"FamilyHub/src/domain" "FamilyHub/src/domain"
"FamilyHub/src/repositories" "FamilyHub/src/repositories"
"FamilyHub/src/utils"
"context" "context"
"database/sql" "database/sql"
"errors" "errors"
"fmt" "fmt"
"log"
"strings" "strings"
) )
@@ -38,7 +40,10 @@ var (
) )
func (s *transactionService) Create(ctx context.Context, req domain.CreateTransactionRequest) (*domain.Transaction, error) { func (s *transactionService) Create(ctx context.Context, req domain.CreateTransactionRequest) (*domain.Transaction, error) {
log.Printf("transaction create request: payload=%s", utils.ToLogJSON(req))
if strings.TrimSpace(req.Type) == "" || strings.TrimSpace(req.Category) == "" { if strings.TrimSpace(req.Type) == "" || strings.TrimSpace(req.Category) == "" {
log.Printf("transaction create failed: err=%v payload=%s", ErrInvalidTransaction, utils.ToLogJSON(req))
return nil, ErrInvalidTransaction return nil, ErrInvalidTransaction
} }
@@ -55,8 +60,10 @@ func (s *transactionService) Create(ctx context.Context, req domain.CreateTransa
if err := s.repo.Create(ctx, transaction); err != nil { if err := s.repo.Create(ctx, transaction); err != nil {
if errors.Is(err, repositories.ErrReceiptNotFound) { if errors.Is(err, repositories.ErrReceiptNotFound) {
log.Printf("transaction create failed: err=%v payload=%s", ErrReceiptNotFound, utils.ToLogJSON(req))
return nil, ErrReceiptNotFound return nil, ErrReceiptNotFound
} }
log.Printf("transaction create failed: err=%v payload=%s", err, utils.ToLogJSON(req))
return nil, err return nil, err
} }
@@ -72,6 +79,7 @@ func (s *transactionService) Create(ctx context.Context, req domain.CreateTransa
}) })
} }
log.Printf("transaction create response: payload=%s", utils.ToLogJSON(transaction))
return transaction, nil return transaction, nil
} }
+51 -18
View File
@@ -3,11 +3,14 @@ package familyHub
import ( import (
"FamilyHub/src/config" "FamilyHub/src/config"
"FamilyHub/src/domain" "FamilyHub/src/domain"
"FamilyHub/src/utils"
"bytes" "bytes"
"context" "context"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"io"
"log"
"net/http" "net/http"
"strconv" "strconv"
"time" "time"
@@ -63,14 +66,13 @@ func (c *HTTPClient) SendReceipt(ctx context.Context, payload domain.AddReceiptR
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
resp, err := c.client.Do(req) responseBody, statusCode, err := c.doRequest(req, "familyhub_api.transactions.create", body)
if err != nil { if err != nil {
return err return err
} }
defer resp.Body.Close()
if resp.StatusCode >= 300 { if statusCode >= 300 {
return fmt.Errorf("api error: status %d", resp.StatusCode) return fmt.Errorf("api error: status %d body %s", statusCode, utils.TruncateForLog(string(responseBody), 512))
} }
return nil return nil
@@ -120,14 +122,13 @@ func (c *HTTPClient) RegisterUser(ctx context.Context, payload domain.CreateUser
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
resp, err := c.client.Do(req) responseBody, statusCode, err := c.doRequest(req, "familyhub_api.users.create", body)
if err != nil { if err != nil {
return err return err
} }
defer resp.Body.Close()
if resp.StatusCode >= 300 { if statusCode >= 300 {
return fmt.Errorf("api error: status %d", resp.StatusCode) return fmt.Errorf("api error: status %d body %s", statusCode, utils.TruncateForLog(string(responseBody), 512))
} }
return nil return nil
@@ -144,22 +145,21 @@ func (c *HTTPClient) GetUserByTelegramID(ctx context.Context, telegramID int64)
return nil, err return nil, err
} }
resp, err := c.client.Do(req) responseBody, statusCode, err := c.doRequest(req, "familyhub_api.users.by_telegram", nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer resp.Body.Close()
if resp.StatusCode == http.StatusNotFound { if statusCode == http.StatusNotFound {
return nil, errUserNotFound return nil, errUserNotFound
} }
if resp.StatusCode >= 300 { if statusCode >= 300 {
return nil, fmt.Errorf("api error: status %d", resp.StatusCode) return nil, fmt.Errorf("api error: status %d body %s", statusCode, utils.TruncateForLog(string(responseBody), 512))
} }
var user domain.UserResponse var user domain.UserResponse
if err := json.NewDecoder(resp.Body).Decode(&user); err != nil { if err := json.Unmarshal(responseBody, &user); err != nil {
return nil, err return nil, err
} }
@@ -184,15 +184,48 @@ func (c *HTTPClient) CreateFamily(ctx context.Context, payload domain.CreateFami
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
resp, err := c.client.Do(req) responseBody, statusCode, err := c.doRequest(req, "familyhub_api.families.create", body)
if err != nil { if err != nil {
return err return err
} }
defer resp.Body.Close()
if resp.StatusCode >= 300 { if statusCode >= 300 {
return fmt.Errorf("api error: status %d", resp.StatusCode) return fmt.Errorf("api error: status %d body %s", statusCode, utils.TruncateForLog(string(responseBody), 512))
} }
return nil return nil
} }
func (c *HTTPClient) doRequest(req *http.Request, service string, requestBody []byte) ([]byte, int, error) {
log.Printf(
"external request: service=%s method=%s url=%s body=%q",
service,
req.Method,
req.URL.String(),
utils.TruncateForLog(string(requestBody), utils.DefaultLogValueLimit),
)
resp, err := c.client.Do(req)
if err != nil {
log.Printf("external response: service=%s method=%s url=%s err=%v", service, req.Method, req.URL.String(), err)
return nil, 0, err
}
defer resp.Body.Close()
responseBody, readErr := io.ReadAll(resp.Body)
if readErr != nil {
log.Printf("external response: service=%s method=%s url=%s status=%d read_err=%v", service, req.Method, req.URL.String(), resp.StatusCode, readErr)
return nil, resp.StatusCode, readErr
}
log.Printf(
"external response: service=%s method=%s url=%s status=%d body=%q",
service,
req.Method,
req.URL.String(),
resp.StatusCode,
utils.TruncateForLog(string(responseBody), utils.DefaultLogValueLimit),
)
return responseBody, resp.StatusCode, nil
}
@@ -2,9 +2,14 @@ package familyHub
import ( import (
"FamilyHub/src/config" "FamilyHub/src/config"
"FamilyHub/src/utils"
"context" "context"
"fmt"
"io"
"log"
"net/http" "net/http"
"strconv" "strconv"
"strings"
"time" "time"
) )
@@ -18,19 +23,42 @@ func NewBotClient(config config.Config) (*HTTPClient, error) {
} }
func (c *HTTPClient) SendMessage(ctx context.Context, chatId int64, message string) error { func (c *HTTPClient) SendMessage(ctx context.Context, chatId int64, message string) error {
url := c.config.TelegramApi + "/bot" + c.config.BotToken + "/sendMessage?chat_id=" + strconv.FormatInt(chatId, 10) + "&text=" + message
req, err := http.NewRequestWithContext( req, err := http.NewRequestWithContext(
ctx, ctx,
http.MethodGet, http.MethodGet,
c.config.TelegramApi+"/bot"+c.config.BotToken+"/sendMessage?chat_id="+strconv.FormatInt(chatId, 10)+"&text="+message, url,
nil, nil,
) )
if err != nil { if err != nil {
return err return err
} }
logURL := strings.ReplaceAll(req.URL.String(), c.config.BotToken, "***")
log.Printf(
"external request: service=telegram_bot.send_message method=%s url=%s body=%q",
http.MethodGet,
logURL,
utils.TruncateForLog(fmt.Sprintf("chat_id=%d&text=%s", chatId, message), utils.DefaultLogValueLimit),
)
resp, err := c.client.Do(req) resp, err := c.client.Do(req)
if err != nil { if err != nil {
log.Printf("external response: service=telegram_bot.send_message method=%s url=%s err=%v", http.MethodGet, logURL, err)
return err return err
} }
defer resp.Body.Close() defer resp.Body.Close()
responseBody, readErr := io.ReadAll(resp.Body)
if readErr != nil {
log.Printf("external response: service=telegram_bot.send_message method=%s url=%s status=%d read_err=%v", http.MethodGet, logURL, resp.StatusCode, readErr)
return readErr
}
log.Printf(
"external response: service=telegram_bot.send_message method=%s url=%s status=%d body=%q",
http.MethodGet,
logURL,
resp.StatusCode,
utils.TruncateForLog(string(responseBody), utils.DefaultLogValueLimit),
)
return nil return nil
} }
+12
View File
@@ -1,9 +1,11 @@
package ocr package ocr
import ( import (
"FamilyHub/src/utils"
"bytes" "bytes"
"context" "context"
"fmt" "fmt"
"log"
vision "cloud.google.com/go/vision/apiv1" vision "cloud.google.com/go/vision/apiv1"
) )
@@ -30,19 +32,29 @@ func (g *GoogleOCR) Close() error {
} }
func (g *GoogleOCR) Recognize(ctx context.Context, image []byte) (string, error) { func (g *GoogleOCR) Recognize(ctx context.Context, image []byte) (string, error) {
log.Printf("external request: service=google_ocr.detect_text image_size_bytes=%d", len(image))
img, err := vision.NewImageFromReader(bytes.NewReader(image)) img, err := vision.NewImageFromReader(bytes.NewReader(image))
if err != nil { if err != nil {
log.Printf("external response: service=google_ocr.detect_text err=%v", err)
return "", fmt.Errorf("load image: %w", err) return "", fmt.Errorf("load image: %w", err)
} }
annotations, err := g.client.DetectTexts(ctx, img, nil, 1) annotations, err := g.client.DetectTexts(ctx, img, nil, 1)
if err != nil { if err != nil {
log.Printf("external response: service=google_ocr.detect_text err=%v", err)
return "", fmt.Errorf("detect text: %w", err) return "", fmt.Errorf("detect text: %w", err)
} }
if len(annotations) == 0 { if len(annotations) == 0 {
log.Printf("external response: service=google_ocr.detect_text result=%q", "")
return "", nil return "", nil
} }
log.Printf(
"external response: service=google_ocr.detect_text result=%q annotations=%d",
utils.TruncateForLog(annotations[0].Description, utils.DefaultLogValueLimit),
len(annotations),
)
return annotations[0].Description, nil return annotations[0].Description, nil
} }
@@ -64,6 +64,14 @@ func (s *receiptProvider) GetReceipt(
var receipt domain.Receipt var receipt domain.Receipt
body, contentType := buildMultipartBody(date, number) body, contentType := buildMultipartBody(date, number)
requestBody := body.String()
log.Printf(
"external request: service=receipt_provider method=%s url=%s content_type=%s body=%q",
http.MethodPost,
url,
contentType,
utils.TruncateForLog(requestBody, utils.DefaultLogValueLimit),
)
httpReq, err := http.NewRequestWithContext( httpReq, err := http.NewRequestWithContext(
ctx, ctx,
http.MethodPost, http.MethodPost,
@@ -79,18 +87,27 @@ func (s *receiptProvider) GetReceipt(
resp, err := s.client.Do(httpReq) resp, err := s.client.Do(httpReq)
if err != nil { if err != nil {
log.Println(err.Error()) log.Printf("external response: service=receipt_provider method=%s url=%s err=%v", http.MethodPost, url, err)
return nil, err return nil, err
} }
defer resp.Body.Close() defer resp.Body.Close()
if resp.StatusCode != http.StatusOK { responseBody, readErr := io.ReadAll(resp.Body)
responseBody, readErr := io.ReadAll(io.LimitReader(resp.Body, 4096))
if readErr != nil { if readErr != nil {
log.Printf("failed to read external service error body: %v", readErr) log.Printf("failed to read external service response body: %v", readErr)
return nil, readErr
} }
bodyText := strings.TrimSpace(string(responseBody)) bodyText := strings.TrimSpace(string(responseBody))
log.Printf("external service returned %d body=%q", resp.StatusCode, bodyText) log.Printf(
"external response: service=receipt_provider method=%s url=%s status=%d body=%q",
http.MethodPost,
url,
resp.StatusCode,
utils.TruncateForLog(bodyText, utils.DefaultLogValueLimit),
)
if resp.StatusCode != http.StatusOK {
return nil, &ExternalServiceError{ return nil, &ExternalServiceError{
StatusCode: resp.StatusCode, StatusCode: resp.StatusCode,
Body: bodyText, Body: bodyText,
@@ -100,8 +117,8 @@ func (s *receiptProvider) GetReceipt(
var raw struct { var raw struct {
Message map[string]interface{} `json:"message"` Message map[string]interface{} `json:"message"`
} }
if err := json.NewDecoder(resp.Body).Decode(&raw); err != nil { if err := json.Unmarshal(responseBody, &raw); err != nil {
log.Printf("external service returned %s\n", err.Error()) log.Printf("external service returned invalid json: %v", err)
return nil, err return nil, err
} }
@@ -112,6 +129,7 @@ func (s *receiptProvider) GetReceipt(
} }
if receipt.IssuedAtRaw == "" { if receipt.IssuedAtRaw == "" {
log.Printf("external response parse failed: service=receipt_provider err=%v date=%s number=%s", ErrReceiptNotFound, date, number)
return nil, ErrReceiptNotFound return nil, ErrReceiptNotFound
} }
+91 -32
View File
@@ -4,6 +4,7 @@ import (
"context" "context"
"database/sql" "database/sql"
"errors" "errors"
"log"
"FamilyHub/src/domain" "FamilyHub/src/domain"
) )
@@ -25,29 +26,63 @@ func NewReceiptsSQLRepository(db *sql.DB) *ReceiptsSQLRepository {
} }
func (r *ReceiptsSQLRepository) Create(ctx context.Context, receipt *domain.Receipt) (int64, error) { func (r *ReceiptsSQLRepository) Create(ctx context.Context, receipt *domain.Receipt) (int64, error) {
log.Printf("%+v\n", receipt)
tx, err := r.db.BeginTx(ctx, nil) tx, err := r.db.BeginTx(ctx, nil)
if err != nil { if err != nil {
return 0, err return 0, err
} }
defer tx.Rollback() defer tx.Rollback()
if receipt.ReceiptNumber != receipt.UI { if receipt.ReceiptNumber != receipt.UI {
receipt.ReceiptNumber = receipt.UI receipt.ReceiptNumber = receipt.UI
} }
res, err := tx.ExecContext(ctx, `
log.Println("First query")
query := `
INSERT INTO receipts ( INSERT INTO receipts (
transaction_id, receipt_number, ui, status, issued_at, transaction_id,
total_amount, payment_amount, cash_amount, receipt_number,
another_amount, clearing_amount, margin, ui,
currency, payment_type, status,
cashbox_number, cashier, issued_at,
name_spd, name_to, name_np, type_np, total_amount,
street_to, house_to, payment_amount,
kod_soato, oblast_soato, rayon_soato, selsovet_soato, cash_amount,
doc_num, skno_number, unp, another_amount,
clearing_amount,
margin,
currency,
payment_type,
cashbox_number,
cashier,
name_spd,
name_to,
name_np,
type_np,
street_to,
house_to,
kod_soato,
oblast_soato,
rayon_soato,
selsovet_soato,
doc_num,
skno_number,
unp,
success success
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) )
`, VALUES (
$1, $2, $3, $4, $5,
$6, $7, $8, $9, $10,
$11, $12, $13, $14, $15,
$16, $17, $18, $19, $20,
$21, $22, $23, $24, $25,
$26, $27, $28, $29
)
RETURNING id;
`
args := []any{
receipt.TransactionID, receipt.TransactionID,
receipt.ReceiptNumber, receipt.ReceiptNumber,
receipt.UI, receipt.UI,
@@ -85,16 +120,19 @@ func (r *ReceiptsSQLRepository) Create(ctx context.Context, receipt *domain.Rece
receipt.UNP, receipt.UNP,
receipt.Success, receipt.Success,
) }
log.Printf("SQL: %s", query)
log.Printf("ARGS: %+v", args)
var receiptID int64
err = tx.QueryRowContext(ctx, query, args...).Scan(&receiptID)
if err != nil { if err != nil {
return 0, err return 0, err
} }
receiptID, err := res.LastInsertId() log.Println("Second query")
if err != nil {
return 0, err
}
stmt, err := tx.PrepareContext(ctx, ` stmt, err := tx.PrepareContext(ctx, `
INSERT INTO positions ( INSERT INTO positions (
@@ -109,7 +147,11 @@ func (r *ReceiptsSQLRepository) Create(ctx context.Context, receipt *domain.Rece
tag, tag,
marking_code, marking_code,
ukz_code ukz_code
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) )
VALUES (
$1, $2, $3, $4, $5,
$6, $7, $8, $9, $10, $11
)
`) `)
if err != nil { if err != nil {
return 0, err return 0, err
@@ -117,7 +159,8 @@ func (r *ReceiptsSQLRepository) Create(ctx context.Context, receipt *domain.Rece
defer stmt.Close() defer stmt.Close()
for _, p := range receipt.Positions { for _, p := range receipt.Positions {
_, err = stmt.ExecContext(ctx, _, err = stmt.ExecContext(
ctx,
receiptID, receiptID,
p.SectionNumber, p.SectionNumber,
p.GTINCode, p.GTINCode,
@@ -135,7 +178,11 @@ func (r *ReceiptsSQLRepository) Create(ctx context.Context, receipt *domain.Rece
} }
} }
return receiptID, tx.Commit() if err = tx.Commit(); err != nil {
return 0, err
}
return receiptID, nil
} }
func (r *ReceiptsSQLRepository) GetByID(ctx context.Context, id int64) (*domain.Receipt, error) { func (r *ReceiptsSQLRepository) GetByID(ctx context.Context, id int64) (*domain.Receipt, error) {
@@ -157,7 +204,7 @@ func (r *ReceiptsSQLRepository) GetByID(ctx context.Context, id int64) (*domain.
doc_num, skno_number, unp, doc_num, skno_number, unp,
success success
FROM receipts FROM receipts
WHERE id = ? WHERE id = $1
`, id).Scan( `, id).Scan(
&receipt.ID, &receipt.ID,
&receipt.TransactionID, &receipt.TransactionID,
@@ -213,7 +260,7 @@ func (r *ReceiptsSQLRepository) GetByID(ctx context.Context, id int64) (*domain.
product_count, amount, product_count, amount,
discount, surcharge, discount, surcharge,
tag, marking_code, ukz_code tag, marking_code, ukz_code
FROM positions WHERE receipt_id = ? FROM positions WHERE receipt_id = $1
`, id) `, id)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -247,10 +294,16 @@ func (r *ReceiptsSQLRepository) GetByID(ctx context.Context, id int64) (*domain.
func (r *ReceiptsSQLRepository) GetAll(ctx context.Context, limit, offset int) ([]*domain.Receipt, error) { func (r *ReceiptsSQLRepository) GetAll(ctx context.Context, limit, offset int) ([]*domain.Receipt, error) {
rows, err := r.db.QueryContext(ctx, ` rows, err := r.db.QueryContext(ctx, `
SELECT id, transaction_id, receipt_number, issued_at, total_amount, currency SELECT
id,
transaction_id,
receipt_number,
issued_at,
total_amount,
currency
FROM receipts FROM receipts
ORDER BY issued_at DESC ORDER BY issued_at DESC
LIMIT ? OFFSET ? LIMIT $1 OFFSET $2
`, limit, offset) `, limit, offset)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -261,6 +314,7 @@ func (r *ReceiptsSQLRepository) GetAll(ctx context.Context, limit, offset int) (
for rows.Next() { for rows.Next() {
var rct domain.Receipt var rct domain.Receipt
if err := rows.Scan( if err := rows.Scan(
&rct.ID, &rct.ID,
&rct.TransactionID, &rct.TransactionID,
@@ -271,9 +325,14 @@ func (r *ReceiptsSQLRepository) GetAll(ctx context.Context, limit, offset int) (
); err != nil { ); err != nil {
return nil, err return nil, err
} }
receipts = append(receipts, &rct) receipts = append(receipts, &rct)
} }
if err := rows.Err(); err != nil {
return nil, err
}
return receipts, nil return receipts, nil
} }
@@ -287,11 +346,11 @@ func (r *ReceiptsSQLRepository) Update(ctx context.Context, receipt *domain.Rece
_, err = tx.ExecContext(ctx, ` _, err = tx.ExecContext(ctx, `
UPDATE receipts SET UPDATE receipts SET
transaction_id = ?, transaction_id = $1,
issued_at = ?, issued_at = $2,
total_amount = ?, total_amount = $3,
currency = ? currency = $4
WHERE id = ? WHERE id = $5
`, `,
receipt.TransactionID, receipt.TransactionID,
receipt.IssuedAt, receipt.IssuedAt,
@@ -303,7 +362,7 @@ func (r *ReceiptsSQLRepository) Update(ctx context.Context, receipt *domain.Rece
return err return err
} }
_, err = tx.ExecContext(ctx, `DELETE FROM positions WHERE receipt_id = ?`, receipt.ID) _, err = tx.ExecContext(ctx, `DELETE FROM positions WHERE receipt_id = $1`, receipt.ID)
if err != nil { if err != nil {
return err return err
} }
@@ -312,7 +371,7 @@ func (r *ReceiptsSQLRepository) Update(ctx context.Context, receipt *domain.Rece
_, err = tx.ExecContext(ctx, ` _, err = tx.ExecContext(ctx, `
INSERT INTO positions ( INSERT INTO positions (
receipt_id, product_name, product_count, amount receipt_id, product_name, product_count, amount
) VALUES (?, ?, ?, ?) ) VALUES ($1, $2, $3, $4)
`, receipt.ID, p.ProductName, p.ProductCount, p.Amount) `, receipt.ID, p.ProductName, p.ProductCount, p.Amount)
if err != nil { if err != nil {
return err return err
@@ -324,7 +383,7 @@ func (r *ReceiptsSQLRepository) Update(ctx context.Context, receipt *domain.Rece
func (r *ReceiptsSQLRepository) Delete(ctx context.Context, id int64) error { func (r *ReceiptsSQLRepository) Delete(ctx context.Context, id int64) error {
_, err := r.db.ExecContext(ctx, _, err := r.db.ExecContext(ctx,
`DELETE FROM receipts WHERE id = ?`, `DELETE FROM receipts WHERE id = $1`,
id, id,
) )
return err return err
+29
View File
@@ -0,0 +1,29 @@
package utils
import (
"encoding/json"
"fmt"
)
const DefaultLogValueLimit = 4096
func ToLogJSON(value any) string {
if value == nil {
return "null"
}
data, err := json.Marshal(value)
if err != nil {
return TruncateForLog(fmt.Sprintf("%+v", value), DefaultLogValueLimit)
}
return TruncateForLog(string(data), DefaultLogValueLimit)
}
func TruncateForLog(value string, limit int) string {
if limit <= 0 || len(value) <= limit {
return value
}
return value[:limit] + "...(truncated)"
}
+32
View File
@@ -0,0 +1,32 @@
{
"id": 0,
"transaction_id": null,
"STATUS": 1,
"another_amount": 0,
"cash_amount": 0,
"cashbox_number": 119091676,
"cashier": "Старший кассир КСО",
"clearing_amount": 190.06,
"currency": "BYN",
"doc_num": "39972",
"house_to": "11",
"kod_soato": "5000000000",
"margin": 0,
"name_np": "Минск",
"name_spd": "Общество с ограниченной ответственностью \"ГРИНрозница\"",
"name_to": "Магазин \"ГРИН-5\"",
"oblast_soato": null,
"rayon_soato": null,
"selsovet_soato": null,
"payment_amount": 190.06,
"payment_type": 1,
"receipt_number": "CBB580D6268681CB071931DC",
"skno_number": "AVQ24170126963",
"street_to": "УЛ. ПЕТРА МСТИСЛАВЦА",
"success": "A check is correct",
"total_amount": 190.06,
"type_np": "г.",
"ui": "CBB580D6268681CB071931DC",
"unp": "191634233",
"issued_at": "09/11/2025, 18:08:31"
}