package product import ( "context" "fmt" "gitea.timerzz.com/kedaya_haitao/common/model/product" coach_client "gitea.timerzz.com/kedaya_haitao/common/pkg/coach-client" "gitea.timerzz.com/kedaya_haitao/pusher/kitex_gen/push" "gitea.timerzz.com/kedaya_haitao/pusher/rpc/pusher" "github.com/golang/glog" "github.com/samber/lo" "gorm.io/gorm" "gorm.io/gorm/clause" "log/slog" "time" ) type Controller struct { ctx context.Context client *coach_client.US updateTime time.Time // 上次抓取时间 db *gorm.DB calculates []productv1.CalculateProcess Option } func NewController(client *coach_client.US, db *gorm.DB) *Controller { ctl := &Controller{ client: client, db: db, } ctl.AutoMigrate() ctl.LoadOption() ctl.LoadCalculateProcess() return ctl } func (c *Controller) AutoMigrate() { if err := c.db.AutoMigrate(&productv1.Product{}, &productv1.HistoryPrice{}, &Option{}, &productv1.CalculateProcess{}); err != nil { panic(err) } } func (c *Controller) Run(ctx context.Context) { c.ctx = ctx ticker := time.NewTicker(c.Interval) if err := c.Crawl(); err != nil { slog.Error(err.Error()) } else { slog.Info("抓取信息成功") c.updateTime = time.Now() } for { select { case <-ctx.Done(): return case <-ticker.C: if err := c.Crawl(); err != nil { slog.Error(err.Error()) } else { slog.Info("抓取信息成功") c.updateTime = time.Now() } } } } func (c *Controller) Crawl() error { slog.Info("开始抓取信息") for page, totalPage := 1, -1; page <= totalPage || totalPage == -1; page++ { resp, err := c.client.ViewAllBags(c.ctx, page) if err != nil { return fmt.Errorf("访问coach第%d页失败: %w", page, err) } totalPage = resp.PageData.TotalPages if err = c.saveRespData(resp.PageData.Products); err != nil { return fmt.Errorf("保存第%d页数据失败: %w", page, err) } glog.Infof("第%d页数据保存成功", page) } return nil } func (c *Controller) saveRespData(list []coach_client.Product) error { var products = make([]productv1.Product, 0, len(list)) for _, resp := range list { for _, color := range resp.Colors { price, _ := lo.Find(resp.VariantsOnSale, func(item coach_client.Variant) bool { return item.Id == color.VgId }) // 获取已经存的运费和得物价格 var savedProduct productv1.Product c.db.Model(&savedProduct).Where("pid = ?", color.VgId).Select("freight", "dw_price").Scan(&savedProduct) if savedProduct.Freight == 0 { savedProduct.Freight = c.Freight } p := productv1.Product{ UpdatedAt: time.Now(), Name: resp.Name, Pid: color.VgId, Color: color.Text, Link: fmt.Sprintf("%s/%s", "https://www.coachoutlet.com", color.Url), Image: color.Media.Thumbnail.Src, Orderable: color.Orderable, DiscPercent: price.Price.MarkdownDiscPercent, OriginalPrice: price.Price.Sales.Value, Freight: savedProduct.Freight, ExchangeRate: c.ExchangeRate, DWPrice: savedProduct.DWPrice, } var calculate []productv1.CalculateProcess c.db.Model(&productv1.CalculateProcess{}).Find(&calculate, "pid = ? AND website = ?", color.VgId, productv1.WebSite_US_Coach_Outlet) p.CalCNY(append(calculate, c.calculates...)) products = append(products, p) } } // 去重 products = lo.UniqBy(products, func(p productv1.Product) string { return p.Pid }) if len(products) == 0 { return nil } lo.ForEach(products, func(p productv1.Product, _ int) { p.CalRate() if p.Rate >= 15 && p.Orderable { c.Push("美国coach outlet有收益大于15%的商品", fmt.Sprintf("coach outlet 商品 %s 收益率达到:%.2f%% \n商品名:%s\n链接:%s", p.Pid, p.Rate, p.Name, p.Link)) return } var historyPrice []productv1.HistoryPrice c.db.Model(&productv1.HistoryPrice{}).Find(&historyPrice, "pid = ?", p.Pid) if len(historyPrice) > 0 { lowestPrice := lo.MinBy(historyPrice, func(a productv1.HistoryPrice, b productv1.HistoryPrice) bool { return a.OriginalPrice < b.OriginalPrice }) if p.OriginalPrice < lowestPrice.OriginalPrice { c.Push("美国coach outlet有商品价格史低", fmt.Sprintf("coach outlet 商品 %s 价格为%.2f之前最低价为:%.2f \n商品名:%s\n链接:%s", p.Pid, p.OriginalPrice, lowestPrice.OriginalPrice, p.Name, p.Link)) } } }) return c.db.Clauses(clause.OnConflict{ Columns: []clause.Column{{Name: "pid"}}, DoUpdates: clause.AssignmentColumns([]string{"name", "color", "link", "orderable", "original_price", "cny_price", "cal_mark", "rate", "price_status", "disc_percent", "updated_at"}), }).Create(products).Error } func (c *Controller) Push(title, content string) { resp, err := pusher.Push(c.ctx, &push.PushReq{ Ids: []int64{2}, Title: title, Content: content, }) if err != nil { glog.Errorf("消息推送失败:%v", err) } if resp.Code != 0 { glog.Errorf("消息推送失败:%s", resp.Msg) } }