Initial commit of Descrybe v2 without local scratch artifacts.
Drop one-shot tmp/axe scripts and agent i18n scratch so the Gitea tree is deployable.
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
package woocommerce
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// SyncReviews pulls Woo product reviews in pages and upserts company-scoped rows.
|
||||
func (s *Service) SyncReviews(ctx context.Context, companyID uuid.UUID) (ReviewsSyncSummary, error) {
|
||||
client, _, opt, err := s.clientFor(ctx, companyID)
|
||||
if err != nil {
|
||||
return ReviewsSyncSummary{}, err
|
||||
}
|
||||
limit := opt.ReviewsSyncLimit
|
||||
if limit <= 0 || limit > maxReviewsSyncLimit {
|
||||
limit = defaultReviewsSyncLimit
|
||||
}
|
||||
pageSize := defaultPullPageSize
|
||||
summary := ReviewsSyncSummary{}
|
||||
|
||||
for page := 1; summary.Fetched < limit; page++ {
|
||||
remaining := limit - summary.Fetched
|
||||
perPage := pageSize
|
||||
if remaining < perPage {
|
||||
perPage = remaining
|
||||
}
|
||||
reviews, _, err := client.ListProductReviewsPage(ctx, page, perPage)
|
||||
if err != nil {
|
||||
opt.PendingReviewsSync = false
|
||||
opt.LastReviewsSyncStatus = "failed"
|
||||
opt.LastReviewsSyncError = truncateErr(err)
|
||||
_ = s.saveSyncOptions(ctx, companyID, opt)
|
||||
return summary, err
|
||||
}
|
||||
if len(reviews) == 0 {
|
||||
break
|
||||
}
|
||||
summary.Pages++
|
||||
summary.Fetched += len(reviews)
|
||||
for _, review := range reviews {
|
||||
if err := s.upsertReview(ctx, companyID, review); err != nil {
|
||||
summary.Failed++
|
||||
continue
|
||||
}
|
||||
summary.Upserted++
|
||||
}
|
||||
if len(reviews) < perPage {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
opt.PendingReviewsSync = false
|
||||
if summary.Failed > 0 && summary.Upserted == 0 {
|
||||
opt.LastReviewsSyncStatus = "failed"
|
||||
opt.LastReviewsSyncError = "all review upserts failed"
|
||||
} else if summary.Failed > 0 {
|
||||
opt.LastReviewsSyncStatus = "partial"
|
||||
opt.LastReviewsSyncError = "some review upserts failed"
|
||||
} else {
|
||||
opt.LastReviewsSyncStatus = "success"
|
||||
opt.LastReviewsSyncError = ""
|
||||
}
|
||||
now := time.Now().UTC()
|
||||
opt.LastReviewsSyncedAt = &now
|
||||
if err := s.saveSyncOptions(ctx, companyID, opt); err != nil {
|
||||
return summary, err
|
||||
}
|
||||
return summary, nil
|
||||
}
|
||||
|
||||
func (s *Service) upsertReview(ctx context.Context, companyID uuid.UUID, review ProductReview) error {
|
||||
payload := review.Raw
|
||||
if len(payload) == 0 {
|
||||
b, err := json.Marshal(review)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
payload = b
|
||||
}
|
||||
reviewedAt := parseWooTime(review.DateCreatedGMT, review.DateCreated)
|
||||
email := strings.TrimSpace(strings.ToLower(review.ReviewerEmail))
|
||||
_, err := s.Pool.Exec(ctx, `
|
||||
INSERT INTO product_reviews (
|
||||
company_id, external_id, product_id, product_name, status, reviewer, reviewer_email,
|
||||
rating, review, reviewed_at, payload, synced_at, updated_at
|
||||
) VALUES (
|
||||
$1, $2, NULLIF($3, 0), $4, $5, $6, $7,
|
||||
$8, $9, $10, $11::jsonb, now(), now()
|
||||
)
|
||||
ON CONFLICT (company_id, external_id) DO UPDATE SET
|
||||
product_id = EXCLUDED.product_id,
|
||||
product_name = EXCLUDED.product_name,
|
||||
status = EXCLUDED.status,
|
||||
reviewer = EXCLUDED.reviewer,
|
||||
reviewer_email = EXCLUDED.reviewer_email,
|
||||
rating = EXCLUDED.rating,
|
||||
review = EXCLUDED.review,
|
||||
reviewed_at = EXCLUDED.reviewed_at,
|
||||
payload = EXCLUDED.payload,
|
||||
synced_at = now(),
|
||||
updated_at = now()`,
|
||||
companyID, review.ID, review.ProductID, strings.TrimSpace(review.ProductName),
|
||||
strings.TrimSpace(review.Status), strings.TrimSpace(review.Reviewer), email,
|
||||
nullableInt(review.Rating), strings.TrimSpace(review.Review), reviewedAt, payload,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
func nullableInt(v int) *int {
|
||||
if v == 0 {
|
||||
return nil
|
||||
}
|
||||
return &v
|
||||
}
|
||||
Reference in New Issue
Block a user