package repository import ( "context" "encoding/json" "errors" "git.techease.ru/Smart-search/smart-search-back/internal/model" errs "git.techease.ru/Smart-search/smart-search-back/pkg/errors" sq "github.com/Masterminds/squirrel" "github.com/google/uuid" "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgxpool" ) type requestRepository struct { pool *pgxpool.Pool qb sq.StatementBuilderType } func NewRequestRepository(pool *pgxpool.Pool) RequestRepository { return &requestRepository{ pool: pool, qb: sq.StatementBuilder.PlaceholderFormat(sq.Dollar), } } func (r *requestRepository) Create(ctx context.Context, req *model.Request) error { query := r.qb.Insert("requests_for_suppliers").Columns( "user_id", "request_txt", ).Values( req.UserID, req.RequestTxt, ).Suffix("RETURNING id, created_at") sqlQuery, args, err := query.ToSql() if err != nil { return errs.NewInternalError(errs.DatabaseError, "failed to build query", err) } err = r.pool.QueryRow(ctx, sqlQuery, args...).Scan(&req.ID, &req.CreatedAt) if err != nil { return errs.NewInternalError(errs.DatabaseError, "failed to create request", err) } return nil } func (r *requestRepository) UpdateWithTZ(ctx context.Context, id uuid.UUID, tz string, generated bool) error { return r.updateWithTZExecutor(ctx, r.pool, id, tz, generated) } func (r *requestRepository) UpdateWithTZTx(ctx context.Context, tx pgx.Tx, id uuid.UUID, tz string, generated bool) error { return r.updateWithTZExecutor(ctx, tx, id, tz, generated) } func (r *requestRepository) updateWithTZExecutor(ctx context.Context, exec DBTX, id uuid.UUID, tz string, generated bool) error { query := r.qb.Update("requests_for_suppliers"). Set("final_tz", tz). Set("generated_tz", generated). Set("generated_final_tz", generated). Where(sq.Eq{"id": id}) sqlQuery, args, err := query.ToSql() if err != nil { return errs.NewInternalError(errs.DatabaseError, "failed to build query", err) } _, err = exec.Exec(ctx, sqlQuery, args...) if err != nil { return errs.NewInternalError(errs.DatabaseError, "failed to update request", err) } return nil } func (r *requestRepository) UpdateFinalTZ(ctx context.Context, id uuid.UUID, finalTZ string) error { query := r.qb.Update("requests_for_suppliers"). Set("final_update_tz", finalTZ). Where(sq.Eq{"id": id}) sqlQuery, args, err := query.ToSql() if err != nil { return errs.NewInternalError(errs.DatabaseError, "failed to build query", err) } _, err = r.pool.Exec(ctx, sqlQuery, args...) if err != nil { return errs.NewInternalError(errs.DatabaseError, "failed to update final TZ", err) } return nil } func (r *requestRepository) GetByUserID(ctx context.Context, userID int) ([]*model.Request, error) { query := r.qb.Select( "r.id", "r.request_txt", "ms.status_name as mailing_status", ).From("requests_for_suppliers r"). Join("mailling_status ms ON r.mailling_status_id = ms.id"). Where(sq.Eq{"r.user_id": userID}). OrderBy("r.created_at DESC") sqlQuery, args, err := query.ToSql() if err != nil { return nil, errs.NewInternalError(errs.DatabaseError, "failed to build query", err) } rows, err := r.pool.Query(ctx, sqlQuery, args...) if err != nil { return nil, errs.NewInternalError(errs.DatabaseError, "failed to get requests", err) } defer rows.Close() var requests []*model.Request for rows.Next() { req := &model.Request{} var statusName string err := rows.Scan(&req.ID, &req.RequestTxt, &statusName) if err != nil { return nil, errs.NewInternalError(errs.DatabaseError, "failed to scan request", err) } requests = append(requests, req) } return requests, nil } func (r *requestRepository) GetByID(ctx context.Context, id uuid.UUID) (*model.Request, error) { query := r.qb.Select( "id", "user_id", "request_txt", "generated_tz", "final_tz", "generated_final_tz", "final_update_tz", "mailling_status_id", "created_at", ).From("requests_for_suppliers").Where(sq.Eq{"id": id}) sqlQuery, args, err := query.ToSql() if err != nil { return nil, errs.NewInternalError(errs.DatabaseError, "failed to build query", err) } req := &model.Request{} err = r.pool.QueryRow(ctx, sqlQuery, args...).Scan( &req.ID, &req.UserID, &req.RequestTxt, &req.GeneratedTZ, &req.FinalTZ, &req.GeneratedFinalTZ, &req.FinalUpdateTZ, &req.MailingStatusID, &req.CreatedAt, ) if errors.Is(err, pgx.ErrNoRows) { return nil, errs.NewBusinessError(errs.RequestNotFound, "request not found") } if err != nil { return nil, errs.NewInternalError(errs.DatabaseError, "failed to get request", err) } return req, nil } func (r *requestRepository) GetDetailByID(ctx context.Context, id uuid.UUID) (*model.RequestDetail, error) { sqlQuery := ` SELECT r.id AS request_id, r.request_txt AS title, r.final_update_tz AS mail_text, COALESCE(json_agg( json_build_object( 'email', COALESCE(s.email, ''), 'phone', COALESCE(s.phone, ''), 'company_name', COALESCE(s.name, ''), 'company_id', s.id, 'url', COALESCE(s.url, '') ) ) FILTER (WHERE s.id IS NOT NULL), '[]') AS suppliers FROM requests_for_suppliers r LEFT JOIN suppliers s ON s.request_id = r.id WHERE r.id = $1 GROUP BY r.id, r.request_txt, r.final_update_tz ` detail := &model.RequestDetail{} var suppliersJSON []byte err := r.pool.QueryRow(ctx, sqlQuery, id).Scan( &detail.RequestID, &detail.Title, &detail.MailText, &suppliersJSON, ) if err == pgx.ErrNoRows { return nil, errs.NewBusinessError(errs.RequestNotFound, "request not found") } if err != nil { return nil, errs.NewInternalError(errs.DatabaseError, "failed to get request detail", err) } if len(suppliersJSON) > 0 && string(suppliersJSON) != "[]" { if err := json.Unmarshal(suppliersJSON, &detail.Suppliers); err != nil { return nil, errs.NewInternalError(errs.DatabaseError, "failed to parse suppliers", err) } } return detail, nil } func (r *requestRepository) GetUserStatistics(ctx context.Context, userID int) (requestsCount, suppliersCount, createdTZ int, err error) { sqlQuery := ` SELECT COUNT(DISTINCT r.id) AS requests_count, COUNT(s.id) AS suppliers_count, COUNT(r.request_txt) AS created_tz FROM requests_for_suppliers r LEFT JOIN suppliers s ON s.request_id = r.id WHERE r.user_id = $1 ` err = r.pool.QueryRow(ctx, sqlQuery, userID).Scan(&requestsCount, &suppliersCount, &createdTZ) if err != nil { return 0, 0, 0, errs.NewInternalError(errs.DatabaseError, "failed to get statistics", err) } return requestsCount, suppliersCount, createdTZ, nil }