//! 同步载荷应用:按业务键 upsert,仅当载荷新于本地时更新(last-write-wins)。 // Logan.Li package sync import ( "fmt" "strings" "time" "backend/internal/dto" "backend/internal/model" ) // upsertProduct 按产品 code 定位 // Logan.Li func (s *Service) upsertProduct(p dto.SyncProductPayload) bool { if strings.TrimSpace(p.Code) == "" { return false } var local model.Product err := s.db.Where("code = ?", p.Code).First(&local).Error if err != nil { local = model.Product{ Code: p.Code, Name: p.Name, Version: p.Version, Description: p.Description, Features: p.Features, Price: p.Price, PriceModel: p.PriceModel, Status: p.Status, CreatedAt: p.UpdatedAt, UpdatedAt: p.UpdatedAt, } return s.db.Create(&local).Error == nil } if !p.UpdatedAt.After(local.UpdatedAt) { return false // 本地已是最新的,跳过 // Logan.Li } local.Name = p.Name local.Version = p.Version local.Description = p.Description local.Features = p.Features local.Price = p.Price local.PriceModel = p.PriceModel local.Status = p.Status return s.db.Save(&local).Error == nil } // upsertPlan 按 产品code+plan_type+duration 定位 // Logan.Li func (s *Service) upsertPlan(pl dto.SyncPlanPayload) bool { if pl.ProductCode == "" { return false } var product model.Product if err := s.db.Where("code = ?", pl.ProductCode).First(&product).Error; err != nil { return false // 产品依赖未就绪,等下一轮 // Logan.Li } var local model.Plan err := s.db.Where("product_id = ? AND plan_type = ? AND duration = ?", product.ID, pl.PlanType, pl.Duration).First(&local).Error if err != nil { local = model.Plan{ ProductID: product.ID, Name: pl.Name, PlanType: pl.PlanType, Price: pl.Price, Currency: pl.Currency, Duration: pl.Duration, MaxDevices: pl.MaxDevices, Features: pl.Features, SortOrder: pl.SortOrder, Status: pl.Status, CreatedAt: pl.UpdatedAt, UpdatedAt: pl.UpdatedAt, } return s.db.Create(&local).Error == nil } if !pl.UpdatedAt.After(local.UpdatedAt) { return false } local.Name = pl.Name local.Price = pl.Price local.Currency = pl.Currency local.MaxDevices = pl.MaxDevices local.Features = pl.Features local.SortOrder = pl.SortOrder local.Status = pl.Status return s.db.Save(&local).Error == nil } // upsertCustomer 按 email 定位(空/占位邮箱回退 name),合规最小字段 // Logan.Li func (s *Service) upsertCustomer(c dto.SyncCustomerPayload) bool { email := strings.TrimSpace(c.Email) name := strings.TrimSpace(c.Name) var local model.Customer var err error if email != "" && !strings.HasSuffix(email, "@local.invalid") { err = s.db.Where("email = ?", email).First(&local).Error } else { if name == "" { return false } err = s.db.Where("name = ?", name).Order("id ASC").First(&local).Error } if err != nil { if email == "" { email = fmt.Sprintf("sync+%d@local.invalid", time.Now().UnixNano()) } local = model.Customer{ Name: name, Company: c.Company, Email: email, Status: c.Status, CreatedAt: c.UpdatedAt, UpdatedAt: c.UpdatedAt, } return s.db.Create(&local).Error == nil } if !c.UpdatedAt.After(local.UpdatedAt) { return false } local.Name = name local.Company = c.Company if email != "" && !strings.HasSuffix(email, "@local.invalid") { local.Email = email } local.Status = c.Status return s.db.Save(&local).Error == nil } // upsertLicense 按 license_key 定位,跨表解析产品与客户 // Logan.Li func (s *Service) upsertLicense(l dto.SyncLicensePayload) bool { if strings.TrimSpace(l.LicenseKey) == "" || l.ProductCode == "" { return false } var product model.Product if err := s.db.Where("code = ?", l.ProductCode).First(&product).Error; err != nil { return false } customerID, err := s.resolveCustomer(l.CustomerEmail, l.CustomerName) if err != nil { return false } var local model.License err = s.db.Where("license_key = ?", l.LicenseKey).First(&local).Error if err != nil { local = model.License{ LicenseKey: l.LicenseKey, ProductID: product.ID, CustomerID: customerID, Status: l.Status, StartDate: l.StartDate, EndDate: l.EndDate, Duration: l.Duration, MaxActivations: l.MaxActivations, CurrentActivations: l.CurrentActivations, IsTrial: l.IsTrial, TrialDays: l.TrialDays, CreatedAt: l.UpdatedAt, UpdatedAt: l.UpdatedAt, } return s.db.Create(&local).Error == nil } if !l.UpdatedAt.After(local.UpdatedAt) { return false } local.ProductID = product.ID local.CustomerID = customerID local.Status = l.Status local.StartDate = l.StartDate local.EndDate = l.EndDate local.Duration = l.Duration local.MaxActivations = l.MaxActivations local.CurrentActivations = l.CurrentActivations local.IsTrial = l.IsTrial local.TrialDays = l.TrialDays return s.db.Save(&local).Error == nil } // resolveCustomer 按邮箱解析或创建客户(离线占位客户按名字复用) // Logan.Li func (s *Service) resolveCustomer(email, name string) (uint, error) { email = strings.TrimSpace(email) name = strings.TrimSpace(name) if email == "" { email = fmt.Sprintf("sync+%d@local.invalid", time.Now().UnixNano()) } var customer model.Customer if err := s.db.Where("email = ?", email).First(&customer).Error; err == nil { return customer.ID, nil } customer = model.Customer{Name: name, Email: email, Status: 1} if err := s.db.Create(&customer).Error; err != nil { // 并发下可能已存在,再查一次 // Logan.Li if err2 := s.db.Where("email = ?", email).First(&customer).Error; err2 == nil { return customer.ID, nil } return 0, err } return customer.ID, nil }