package main import ( "context" "errors" "flag" "fmt" "os" "time" "bindbox-game/configs" "bindbox-game/internal/pkg/env" "bindbox-game/internal/pkg/logger" "bindbox-game/internal/repository/mysql" "bindbox-game/internal/repository/mysql/model" douyin "bindbox-game/internal/service/douyin" ) // staticSyscfg implements sysconfig.Service with fixed cookie type staticSyscfg struct { cookie string } func (s *staticSyscfg) GetByKey(ctx context.Context, key string) (*model.SystemConfigs, error) { switch key { case douyin.ConfigKeyDouyinCookie: if s.cookie == "" { return nil, errors.New("douyin cookie 未设置") } return &model.SystemConfigs{ConfigKey: key, ConfigValue: s.cookie}, nil case douyin.ConfigKeyDouyinInterval: return &model.SystemConfigs{ConfigKey: key, ConfigValue: "5"}, nil default: return nil, errors.New("暂不支持的配置 key: " + key) } } func (s *staticSyscfg) UpsertByKey(ctx context.Context, key string, value string, remark string) (*model.SystemConfigs, error) { return nil, errors.New("UpsertByKey 未实现") } func (s *staticSyscfg) ModifyByID(ctx context.Context, id int64, value *string, remark *string) error { return errors.New("ModifyByID 未实现") } func (s *staticSyscfg) DeleteByID(ctx context.Context, id int64) error { return errors.New("DeleteByID 未实现") } func (s *staticSyscfg) List(ctx context.Context, page int, pageSize int, keyword string) (items []*model.SystemConfigs, total int64, err error) { return nil, 0, errors.New("List 未实现") } func main() { minutes := flag.Int("minutes", 10, "同步最近多少分钟的订单") useProxy := flag.Bool("proxy", false, "是否使用服务内置代理") printLimit := flag.Int("print", 10, "同步后打印多少条订单 (0 表示不打印)") mode := flag.String("mode", "sync-all", "同步模式: sync-all(默认增量)/fetch(按绑定用户)") grantMinesweeper := flag.Bool("grant-minesweeper", false, "同步后执行 GrantMinesweeperQualifications") fetchOnlyUnmatched := flag.Bool("fetch-only-unmatched", true, "按用户同步时是否仅同步未匹配订单的用户") fetchMaxUsers := flag.Int("fetch-max-users", 200, "按用户同步时最多处理的用户数量 (50-1000)") fetchBatchSize := flag.Int("fetch-batch-size", 20, "按用户同步时的单批次用户数量 (5-50)") fetchConcurrency := flag.Int("fetch-concurrency", 5, "按用户同步时的并发抓取数 (<=批次大小)") fetchDelay := flag.Int("fetch-delay-ms", 200, "批次之间的停顿时间 (毫秒)") flag.Parse() env.Active() // 初始化 env flag(依赖已有的全局 -env/ACTIVE_ENV 配置) configs.Init() cookie := "passport_csrf_token=40ba4a1be914a9f167320ed28b8c93d7; passport_csrf_token_default=40ba4a1be914a9f167320ed28b8c93d7; is_staff_user=false; s_v_web_id=verify_mkf83bbo_zfQ3q1Gp_5irf_4OOI_9y4N_C253269yUIJy; SHOP_ID=156231010; PIGEON_CID=4339134776748827; __security_mc_1_s_sdk_crypt_sdk=db47f387-4d0b-bf21; bd_ticket_guard_client_web_domain=2; bd_ticket_guard_client_data=eyJiZC10aWNrZXQtZ3VhcmQtdmVyc2lvbiI6MiwiYmQtdGlja2V0LWd1YXJkLWl0ZXJhdGlvbi12ZXJzaW9uIjoxLCJiZC10aWNrZXQtZ3VhcmQtcmVlLXB1YmxpYy1rZXkiOiJCTHVTREdkVFRHWUdNMVY3ZDZKS2M4V2FwWGJ1K3JVYmVqRThONTZoeTI4SUJXdmVxZjBLMS9GczE0dWx5RTVRd2d4cjdnaDd6SXdMZjlsWDkwOFZQQWs9IiwiYmQtdGlja2V0LWd1YXJkLXdlYi12ZXJzaW9uIjoyfQ%3D%3D; bd_ticket_guard_web_domain=3; zsgw_business_data=%7B%22uuid%22%3A%226756720f-c380-4bda-ab81-3dd27ca08a2d%22%2C%22platform%22%3A%22pc%22%2C%22source%22%3A%22seo.fxg.jinritemai.com%22%7D; gfkadpd=4272,23756; ecom_gray_shop_id=156231010; csrf_session_id=5f00eba89758e4dec6fcb81867a8bdb5; Hm_lvt_b6520b076191ab4b36812da4c90f7a5e=1769876902,1770569311,1771350555,1772107597; HMACCOUNT=9C6B7571794A6624; Hm_lpvt_b6520b076191ab4b36812da4c90f7a5e=1772107601; ttwid=1%7CNnXcElGkMBE8UTpDOFYR5OfCUYkFjQaLyn1EagPBZgM%7C1772107539%7C23b2036059d82f195be2cc6b908c05b330cdb997234bf3f905c0bc13590d9a40; tt_scid=zEkoBrglfkrRTI4eZLkaSJXnjYM1LLpi9u.Llrfk6aQR5C3CVkjUGS20663cJtx-8cc3; odin_tt=6aea70f28ec501b3733a05a9ceda2cc9f6821ac8477dc66bd2901e299b4a704093d7918c0b6313913e6aa947ff023152c414dd30955f1fa9b96e2aa5828503ce; passport_auth_status=7dd7c4f1d18367e48c305613e3b56d2b%2Ceae9153b20c76f1d76ce32f5abfd7ad2; passport_auth_status_ss=7dd7c4f1d18367e48c305613e3b56d2b%2Ceae9153b20c76f1d76ce32f5abfd7ad2; bd_ticket_guard_server_data=eyJ0aWNrZXQiOiJoYXNoLk9mY291aFhPRllGWTFlSjE0UFFVckltR2JxVFBmQ1NjRG03S3BOeXZBZ009IiwidHNfc2lnbiI6InRzLjIuZDRkMmU1ZGJiZjkxMGMxYzM2ZDhjNTIwZjI3MzVhMjBmYjZhODk5ZDhmNDE0NDUzYzgyMmI5MTgyMTU5ZWJjOWM0ZmJlODdkMjMxOWNmMDUzMTg2MjRjZWRhMTQ5MTFjYTQwNmRlZGJlYmVkZGIyZTMwZmNlOGQ0ZmEwMjU3NWQiLCJjbGllbnRfY2VydCI6InB1Yi5CTHVTREdkVFRHWUdNMVY3ZDZKS2M4V2FwWGJ1K3JVYmVqRThONTZoeTI4SUJXdmVxZjBLMS9GczE0dWx5RTVRd2d4cjdnaDd6SXdMZjlsWDkwOFZQQWs9IiwibG9nX2lkIjoiMjAyNjAyMjYyMDA1NDg3QTBGMjlBRDMzODc4RDMxOUU3QyIsImNyZWF0ZV90aW1lIjoxNzcyMTA3NTQ4fQ%3D%3D; uid_tt=f02800c52b2bb3676614350efaed9630; uid_tt_ss=f02800c52b2bb3676614350efaed9630; sid_tt=c1e5f1ad8bdb3ad22bbd7a10b45e5273; sessionid=c1e5f1ad8bdb3ad22bbd7a10b45e5273; sessionid_ss=c1e5f1ad8bdb3ad22bbd7a10b45e5273; PHPSESSID=e246627f02d38ca5ad58d19df52647d2; PHPSESSID_SS=e246627f02d38ca5ad58d19df52647d2; ucas_c0=CkEKBTEuMC4wEJqIkMTi4o3QaRjmJiD61rDnqc2DBCiwITCb1oDYuM3aB0Cd7oDNBkidor3PBlC_vL6Ekt3t1GdYbhIUJvXy9UDSp90OViBiv17GMnQoNPQ; ucas_c0_ss=CkEKBTEuMC4wEJqIkMTi4o3QaRjmJiD61rDnqc2DBCiwITCb1oDYuM3aB0Cd7oDNBkidor3PBlC_vL6Ekt3t1GdYbhIUJvXy9UDSp90OViBiv17GMnQoNPQ; gd_random=eyJtYXRjaCI6dHJ1ZSwicGVyY2VudCI6MC4yNjk1MzQ2NjQzODMwNjUzfQ==.2Y8PvKxWpRpeQAxCqhA2WtHb2gI9V7vfrLpYjxq4jzM=; source=seo.fxg.jinritemai.com; sid_guard=c1e5f1ad8bdb3ad22bbd7a10b45e5273%7C1772107554%7C5184000%7CMon%2C+27-Apr-2026+12%3A05%3A54+GMT; session_tlb_tag=sttt%7C6%7CweXxrYvbOtIrvXoQtF5Sc__________-gxQYaEjeIZwmKtrmw7H3GC7-rTXLZAYpDxwTHQAiXDQ%3D; sid_ucp_v1=1.0.0-KDI3MGY5YTIzODY2NmQ0Njg5MjJiMDhkMzVlNGI4ZGIyM2IxNjE2YzMKGwib1oDYuM3aBxCi7oDNBhiwISAMOAZA9AdIBBoCbHEiIGMxZTVmMWFkOGJkYjNhZDIyYmJkN2ExMGI0NWU1Mjcz; ssid_ucp_v1=1.0.0-KDI3MGY5YTIzODY2NmQ0Njg5MjJiMDhkMzVlNGI4ZGIyM2IxNjE2YzMKGwib1oDYuM3aBxCi7oDNBhiwISAMOAZA9AdIBBoCbHEiIGMxZTVmMWFkOGJkYjNhZDIyYmJkN2ExMGI0NWU1Mjcz; COMPASS_LUOPAN_DT=session_7611143309406634249; BUYIN_SASID=SID2_7611142212729061666" if cookie == "" { fmt.Println("请通过环境变量 DOUYIN_COOKIE 提供抖店 Cookie") os.Exit(1) } log, err := logger.NewCustomLogger(logger.WithDebugLevel(), logger.WithOutputInConsole()) if err != nil { panic(err) } repo, err := mysql.New() if err != nil { panic(err) } defer repo.DbRClose() defer repo.DbWClose() svc := douyin.New(log, repo, &staticSyscfg{cookie: cookie}, nil, nil, nil) ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) defer cancel() switch *mode { case "fetch": fmt.Println("开始 FetchAndSyncOrders(按绑定用户同步)...") result, err := svc.FetchAndSyncOrders(ctx, &douyin.FetchOptions{ OnlyUnmatched: *fetchOnlyUnmatched, MaxUsers: *fetchMaxUsers, BatchSize: *fetchBatchSize, Concurrency: *fetchConcurrency, InterBatchDelay: time.Duration(*fetchDelay) * time.Millisecond, }) if err != nil { panic(err) } fmt.Printf("完成:抓取 %d,新订单 %d,匹配 %d,处理用户 %d/%d,跳过 %d,用时 %.2fs。\n", result.TotalFetched, result.NewOrders, result.MatchedUsers, result.ProcessedUsers, result.TotalUsers, result.SkippedUsers, float64(result.ElapsedMS)/1000.0) case "sync-all": fallthrough default: duration := time.Duration(*minutes) * time.Minute fmt.Printf("开始 SyncAllOrders,duration=%s proxy=%v ...\n", duration, *useProxy) result, err := svc.SyncAllOrders(ctx, duration, *useProxy) if err != nil { panic(err) } fmt.Printf("完成:抓取 %d,新订单 %d,匹配 %d。\n", result.TotalFetched, result.NewOrders, result.MatchedUsers) } if *grantMinesweeper { fmt.Println("执行 GrantMinesweeperQualifications ...") if err := svc.GrantMinesweeperQualifications(ctx); err != nil { fmt.Printf("GrantMinesweeperQualifications 失败: %v\n", err) } else { fmt.Println("GrantMinesweeperQualifications 完成。") } } if *printLimit > 0 { var orders []model.DouyinOrders if err := repo.GetDbR().Order("id DESC").Limit(*printLimit).Find(&orders).Error; err != nil { fmt.Printf("读取订单列表失败: %v\n", err) return } fmt.Println("shop_order_id\torder_status\tdouyin_user_id\tlocal_user_id") for _, o := range orders { fmt.Printf("%s\t%d\t%s\t%s\n", o.ShopOrderID, o.OrderStatus, o.DouyinUserID, o.LocalUserID) } } }