From c2ec1e751371e11becb2d3360bd3548477b5fbde Mon Sep 17 00:00:00 2001 From: pppscn <35696959@qq.com> Date: Tue, 5 Jan 2021 19:38:48 +0800 Subject: [PATCH 1/5] =?UTF-8?q?=E5=B0=86=E6=97=A5=E5=BF=97=E5=90=8C?= =?UTF-8?q?=E6=97=B6=E8=BE=93=E5=87=BA=E5=88=B0=E6=8E=A7=E5=88=B6=E5=8F=B0?= =?UTF-8?q?=E5=92=8C=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/main.go b/main.go index 1dd52b9..097731c 100644 --- a/main.go +++ b/main.go @@ -5,12 +5,26 @@ import ( "github.com/unknwon/goconfig" "github.com/ztino/jd_seckill/cmd" "github.com/ztino/jd_seckill/common" + "io" "log" "os" "runtime" + "time" ) func init() { + //将日志同时输出到控制台和文件 + file := "./" + "jd_seckill_" + time.Now().Format("20060102") + ".log" + logFile, logErr := os.OpenFile(file, os.O_CREATE|os.O_APPEND|os.O_RDWR, 0666) + if logErr != nil { + panic(logErr) + } + defer logFile.Close() + mw := io.MultiWriter(os.Stdout, logFile) + log.SetOutput(mw) + log.SetPrefix("[jd_seckill]") + log.SetFlags(log.LstdFlags | log.Lshortfile | log.LUTC) + //客户端设置初始化 common.Client=httpc.NewHttpClient() common.CookieJar=httpc.NewCookieJar() @@ -26,6 +40,7 @@ func init() { //抢购状态管道 common.SeckillStatus=make(chan bool) + log.Println("jd_seckill 程序启动成功,祝您成功!") } func main() { From 1a5c4c7ad3eb9f4fadec47cc212b67f8a7ef4e19 Mon Sep 17 00:00:00 2001 From: pppscn <35696959@qq.com> Date: Tue, 5 Jan 2021 19:45:13 +0800 Subject: [PATCH 2/5] =?UTF-8?q?=E4=BC=98=E5=8C=96=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E8=8E=B7=E5=8F=96eid=E5=92=8Cfp=EF=BC=8C=E5=B9=B6=E5=86=99?= =?UTF-8?q?=E5=85=A5conf.ini?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 5 ++ README.md | 3 +- cmd/jdTdudfp.go | 126 +++++++++++++++++++++++++++--------------------- conf.ini | 3 ++ 4 files changed, 82 insertions(+), 55 deletions(-) diff --git a/.gitignore b/.gitignore index 95f82d7..0369067 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,8 @@ # Dependency directories (remove the comment below to include it) # vendor/ +/*.log +/.idea +/qr_code.png +/cookie.txt +*.bak diff --git a/README.md b/README.md index 7d15fb5..1a09cca 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,8 @@ jd_seckill login ### 自动获取eid,fp -> ⚠依赖谷歌浏览器,请安装谷歌浏览器,获取到的eid和fp请手动填入配置文件 +> ⚠依赖谷歌浏览器,请安装谷歌浏览器,windows下请将安全目录加入系统变量Path +> ⚠【重要提醒】自动获取eid和fp期间,建议鼠标跟随页面跳转,滑动到【加入购物车】【去购车结算】【去结算】按钮,但不要点击,可以提升获取成功率!(经验之谈!) 执行以下命令按照提示操作: ```shell diff --git a/cmd/jdTdudfp.go b/cmd/jdTdudfp.go index 329efd3..e55a494 100644 --- a/cmd/jdTdudfp.go +++ b/cmd/jdTdudfp.go @@ -2,16 +2,20 @@ package cmd import ( "context" + "fmt" "github.com/chromedp/cdproto/cdp" "github.com/chromedp/cdproto/network" "github.com/chromedp/cdproto/target" "github.com/chromedp/chromedp" "github.com/spf13/cobra" "github.com/tidwall/gjson" + "github.com/unknwon/goconfig" "github.com/ztino/jd_seckill/common" "github.com/ztino/jd_seckill/jd_seckill" "log" "net/url" + "os" + "strconv" "time" ) @@ -22,26 +26,26 @@ func init() { var jdTdudfpCmd = &cobra.Command{ Use: "jdTdudfp", Short: "auto get jd eid and fp", - Run: startJdTdudfp, + Run: startJdTdudfp, } -func startJdTdudfp(cmd *cobra.Command, args []string) { - session:=jd_seckill.NewSession(common.CookieJar) - err:=session.CheckLoginStatus() - if err!=nil { +func startJdTdudfp(cmd *cobra.Command, args []string) { + session := jd_seckill.NewSession(common.CookieJar) + err := session.CheckLoginStatus() + if err != nil { log.Println("自动获取eid和fp失败,请重新登录") - }else { - log.Println("开始自动获取eid和fp,如遇卡住请结束进程,重新启动") + } else { + retryTimes, _ := strconv.Atoi(common.Config.MustValue("config", "retry_times", "5")) + options := []chromedp.ExecAllocatorOption{ - chromedp.Flag("headless", false), - //chromedp.Flag("blink-settings", "imagesEnabled=false"), - chromedp.Flag("start-maximized", true), - chromedp.Flag("no-sandbox",true), - chromedp.Flag("disable-setuid-sandbox",true), - chromedp.Flag("no-default-browser-check",true), - chromedp.Flag("disable-plugins",true), - chromedp.WindowSize(1920,1080), - chromedp.UserAgent(common.Config.MustValue("config","default_user_agent","")), + chromedp.Flag("headless", false), //debug使用 + chromedp.Flag("blink-settings", "imagesEnabled=false"), //禁用图片加载 + chromedp.Flag("start-maximized", true), //最大化窗口 + chromedp.Flag("no-sandbox", true), //禁用沙盒, 性能优先 + chromedp.Flag("disable-setuid-sandbox", true), //禁用setuid沙盒, 性能优先 + chromedp.Flag("no-default-browser-check", true), //不检查默认浏览器 + chromedp.Flag("disable-plugins", true), //禁用扩展 + chromedp.UserAgent(common.Config.MustValue("config", "default_user_agent", "")), } options = append(chromedp.DefaultExecAllocatorOptions[:], options...) @@ -49,9 +53,10 @@ func startJdTdudfp(cmd *cobra.Command, args []string) { defer cc() ctx, cancel := chromedp.NewContext(c) - ch := addNewTabListener(ctx) + _ = addNewTabListener(ctx) defer cancel() + //设置cookie u, _ := url.Parse("http://jd.com") cookies := common.CookieJar.Cookies(u) err = chromedp.Run(ctx, @@ -67,61 +72,74 @@ func startJdTdudfp(cmd *cobra.Command, args []string) { } return nil }), - chromedp.Navigate("https://jd.com"), - chromedp.Sleep(2*time.Second), - chromedp.Click(".cate_menu_lk"), - chromedp.Sleep(2*time.Second), }, ) if err != nil { + log.Println("设置cookie出错了") log.Fatal(err) } - newCtx, cancel2 := chromedp.NewContext(ctx, chromedp.WithTargetID(<-ch)) - ch2 := addNewTabListener(newCtx) - defer cancel2() - - err = chromedp.Run(newCtx, - chromedp.Click(".goods_item_link"), - chromedp.Sleep(4*time.Second), + RETRY: + retryTimes-- + log.Println("【重要提醒】自动获取eid和fp期间,建议鼠标跟随页面跳转,滑动到【加入购物车】【去购车结算】【去结算】按钮,但不要点击,可以提升获取成功率!") + log.Println(fmt.Sprintf("开始自动获取eid和fp,如遇卡住请耐心等待,重试次数剩余: %v 次", retryTimes)) + var res []byte + testSkuId := common.Config.MustValue("config", "test_sku_id", "") + err = chromedp.Run(ctx, + chromedp.Tasks{ + chromedp.Navigate(fmt.Sprintf("http://item.jd.com/%s.html", testSkuId)), + chromedp.WaitVisible("#InitCartUrl"), //加入购物车 + chromedp.Sleep(2 * time.Second), + chromedp.Click("#InitCartUrl"), + chromedp.WaitVisible(".btn-addtocart"), //去购车结算 + chromedp.Sleep(2 * time.Second), + chromedp.Click(".btn-addtocart"), + chromedp.WaitVisible(".common-submit-btn"), //去结算 + chromedp.Sleep(2 * time.Second), + chromedp.Click(".common-submit-btn"), + chromedp.Sleep(3 * time.Second), + chromedp.Evaluate("_JdTdudfp", &res), + }, ) if err != nil { + log.Println("chromedp 出错了") log.Fatal(err) } - newCtx2, cancel3 := chromedp.NewContext(ctx, chromedp.WithTargetID(<-ch2)) - defer cancel3() + value := string(res) + if !gjson.Valid(value) || gjson.Get(value, "eid").String() == "" || gjson.Get(value, "fp").String() == "" { + log.Println("获取失败,请重新尝试,返回信息:" + value) + if retryTimes > 0 { + goto RETRY + } + } else { + eid := gjson.Get(value, "eid").String() + fp := gjson.Get(value, "fp").String() + log.Println("eid:" + eid) + log.Println("fp:" + fp) - var res []byte - err = chromedp.Run(newCtx2, - chromedp.Click("#InitCartUrl"), - chromedp.Sleep(2*time.Second), - chromedp.Click(".btn-addtocart"), - chromedp.Sleep(2*time.Second), - chromedp.Click(".common-submit-btn"), - chromedp.Sleep(3*time.Second), - chromedp.Evaluate("_JdTdudfp", &res), - ) - if err != nil { - log.Fatal(err) - } + //修改配置文件 + confFile := "./conf.ini" + cfg, err := goconfig.LoadConfigFile(confFile) + if err != nil { + log.Println("配置文件不存在,程序退出") + os.Exit(0) + } - value:=string(res) - if !gjson.Valid(value) || gjson.Get(value,"eid").String()=="" || gjson.Get(value,"fp").String()=="" { - log.Println("获取失败,请重新尝试,返回信息:"+value) - }else{ - log.Println("获取成功,请手动填入配置文件") - log.Println("eid:"+gjson.Get(value,"eid").String()) - log.Println("fp:"+gjson.Get(value,"fp").String()) + cfg.SetValue("config", "eid", eid) + cfg.SetValue("config", "fp", fp) + if err := goconfig.SaveConfigFile(cfg, confFile); err != nil { + log.Println("保存配置文件失败,请手动填入配置文件") + } + + log.Println("eid, fp参数已经自动填入配置文件") } + } } func addNewTabListener(ctx context.Context) <-chan target.ID { -/* mux := http.NewServeMux() - ts := httptest.NewServer(mux) - defer ts.Close()*/ return chromedp.WaitNewTarget(ctx, func(info *target.Info) bool { return true }) -} \ No newline at end of file +} diff --git a/conf.ini b/conf.ini index d258da9..3856915 100644 --- a/conf.ini +++ b/conf.ini @@ -4,6 +4,9 @@ # 随意填写可能导致订单无法提交等问题 eid = fp = +# 自动获取eid, fp的重试次数,测试商品sku_id +retry_times = 5 +test_sku_id = 100016034372 # 商品id # 已经是茅台的sku_id了 sku_id = 100012043978 From d6e44873493bfd4d0add20055e1d443bdef5bad0 Mon Sep 17 00:00:00 2001 From: pppscn <35696959@qq.com> Date: Tue, 5 Jan 2021 19:52:44 +0800 Subject: [PATCH 3/5] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E9=92=89=E9=92=89?= =?UTF-8?q?=E6=9C=BA=E5=99=A8=E4=BA=BA=E6=8E=A8=E9=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- conf.ini | 9 ++++++++- go.mod | 4 ++++ jd_seckill/seckill.go | 4 +++- service/dingtalk.go | 35 +++++++++++++++++++++++++++++++++++ service/service.go | 24 +++++++++++++++--------- 5 files changed, 65 insertions(+), 11 deletions(-) create mode 100644 service/dingtalk.go diff --git a/conf.ini b/conf.ini index 3856915..eba8fae 100644 --- a/conf.ini +++ b/conf.ini @@ -53,4 +53,11 @@ port = # 邮箱地址 xxxxxxxx@xx.com email_user = # 邮箱授权码(并不一定是邮箱密码) xxxxxxxxxxxxxxxx -email_pwd = \ No newline at end of file +email_pwd = + +#钉钉机器人配置 +[dingtalk] +# 机器人的Webhook地址(https://oapi.dingtalk.com/robot/send?access_token=XXXXXX)的access_token +access_token = +# 密钥,机器人安全设置页面,加签一栏下面显示的SEC开头的字符串 +secret = \ No newline at end of file diff --git a/go.mod b/go.mod index e87c3fc..2d16b81 100644 --- a/go.mod +++ b/go.mod @@ -5,14 +5,18 @@ go 1.15 require ( github.com/Albert-Zhan/httpc v0.0.0-20190712132051-aed72924b5e6 github.com/PuerkitoBio/goquery v1.6.0 + github.com/blinkbean/dingtalk v0.0.0-20201231030509-45a553a84503 github.com/chromedp/cdproto v0.0.0-20201204063249-be40c824ad18 github.com/chromedp/chromedp v0.5.4 github.com/gobwas/httphead v0.1.0 // indirect + github.com/json-iterator/go v1.1.10 // indirect github.com/spf13/cobra v1.1.1 github.com/tidwall/gjson v1.6.7 github.com/unknwon/goconfig v0.0.0-20200908083735-df7de6a44db8 + golang.org/x/net v0.0.0-20201224014010-6772e930b67b // indirect golang.org/x/sys v0.0.0-20201231184435-2d18734c6014 // indirect golang.org/x/text v0.3.4 + golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df ) diff --git a/jd_seckill/seckill.go b/jd_seckill/seckill.go index 8be8e04..96e5b0e 100644 --- a/jd_seckill/seckill.go +++ b/jd_seckill/seckill.go @@ -63,7 +63,9 @@ func (this *Seckill) MakeReserve() { reserveUrl := gjson.Get(body, "url").String() req = httpc.NewRequest(this.client) _, _, _ = req.SetUrl("https:" + reserveUrl).SetMethod("get").Send().End() - log.Println("预约成功,已获得抢购资格 / 您已成功预约过了,无需重复预约") + msg := "预约成功,已获得抢购资格 / 您已成功预约过了,无需重复预约![我的预约](https://yushou.jd.com/member/qualificationList.action)" + _ = service.SendMessage(this.conf, "茅台抢购通知", msg) + log.Println(msg) } } diff --git a/service/dingtalk.go b/service/dingtalk.go new file mode 100644 index 0000000..7de4c74 --- /dev/null +++ b/service/dingtalk.go @@ -0,0 +1,35 @@ +package service + +import ( + "github.com/blinkbean/dingtalk" + "github.com/unknwon/goconfig" + "log" +) + +type Dingtalk struct { + conf *goconfig.ConfigFile +} + +func NewDingtalk(conf *goconfig.ConfigFile) *Dingtalk { + return &Dingtalk{conf: conf} +} + +func (this *Dingtalk) Send(title, msg string) error { + cli := dingtalk.InitDingTalkWithSecret( + this.conf.MustValue("dingtalk", "access_token", ""), + this.conf.MustValue("dingtalk", "secret", ""), + ) + markdown := []string{ + "### " + title, + "---------", + msg, + } + err := cli.SendMarkDownMessageBySlice(title, markdown) + if err != nil { + log.Println(err) + } else { + log.Println("钉钉机器人推送成功") + } + + return nil +} diff --git a/service/service.go b/service/service.go index 83b17af..35c6d4c 100644 --- a/service/service.go +++ b/service/service.go @@ -2,20 +2,26 @@ package service import "github.com/unknwon/goconfig" -func SendMessage(conf *goconfig.ConfigFile,title,msg string) error { - if conf.MustValue("messenger","enable","false")=="true" { +func SendMessage(conf *goconfig.ConfigFile, title, msg string) error { + if conf.MustValue("messenger", "enable", "false") == "true" { + //钉钉机器人 + if conf.MustValue("messenger", "type", "none") == "dingtalk" { + dingtalk := NewDingtalk(conf) + err := dingtalk.Send(title, msg) + return err + } //邮件发送 - if conf.MustValue("messenger","type","none")=="smtp" { - email:=NewEmail(conf) - err:=email.Send([]string{conf.MustValue("messenger","email","")},title,msg) + if conf.MustValue("messenger", "type", "none") == "smtp" { + email := NewEmail(conf) + err := email.Send([]string{conf.MustValue("messenger", "email", "")}, title, msg) return err } //Server酱推送 - if conf.MustValue("messenger","type","none")=="wechat" { - wechat:=NewWechat(conf) - err:=wechat.Send(title,msg) + if conf.MustValue("messenger", "type", "none") == "wechat" { + wechat := NewWechat(conf) + err := wechat.Send(title, msg) return err } } return nil -} \ No newline at end of file +} From b783ba852494cc91368f03a88e0585b637441afb Mon Sep 17 00:00:00 2001 From: pppscn <35696959@qq.com> Date: Tue, 5 Jan 2021 20:00:07 +0800 Subject: [PATCH 4/5] =?UTF-8?q?=E8=87=AA=E5=8A=A8=E5=8C=96=E6=8A=A2?= =?UTF-8?q?=E8=B4=AD=E6=94=AF=E6=8C=81=EF=BC=8C=E6=97=A0=E9=9C=80=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE=E6=8A=A2=E8=B4=AD=E6=97=B6=E9=97=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 +--- cmd/seckill.go | 34 ++++++++++++++++++++++------------ common/lib.go | 6 +++++- common/var.go | 9 ++++++--- conf.ini | 4 ++-- 5 files changed, 36 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 1a09cca..79f70b6 100644 --- a/README.md +++ b/README.md @@ -26,8 +26,6 @@ go get github.com/ztino/jd_seckill ## 待办 -- 日志目前还未输出到本地日志文件保存 -- 自动化抢购支持,无需设置抢购时间 - 跨平台桌面端支持,打算使用:https://github.com/therecipe/qt ## 使用 @@ -114,7 +112,7 @@ $ zbarimg qr_code.png > qrcode.txt && qrencode -r qrcode.txt -o - -t UTF8 # 解 ## 抢购流程/抢购结果 -- 程序开始抢购总时间为两分钟,不管有无抢购成功,都会停止,抢购详情请查阅日志和自己配置的第三方推送服务。 +- 程序开始抢购总时间为两分钟,不管有无抢购成功,都会停止,抢购详情请查阅日志和自己配置的第三方推送服务(目前支持:钉钉机器人、SMTP邮件、Server酱) - 第二天抢购需要修改抢购时间和重新开始抢购任务。 diff --git a/cmd/seckill.go b/cmd/seckill.go index 8464061..df5b740 100644 --- a/cmd/seckill.go +++ b/cmd/seckill.go @@ -35,21 +35,31 @@ func startSeckill(cmd *cobra.Command, args []string) { user:=jd_seckill.NewUser(common.Client,common.Config) go KeepSession(user) //计算抢购时间 - nowLocalTime:=time.Now().UnixNano()/1e6 - jdTime,_:=GetJdTime() - buyDate:=common.Config.MustValue("config","buy_time","") - loc, _ := time.LoadLocation("Local") - t,_:=time.ParseInLocation("2006-01-02 15:04:05",buyDate,loc) - buyTime:=t.UnixNano()/1e6 - diffTime:=nowLocalTime-jdTime - log.Println(fmt.Sprintf("正在等待到达设定时间:%s,检测本地时间与京东服务器时间误差为【%d】毫秒",buyDate,diffTime)) - timerTime:=(buyTime+diffTime)-jdTime - if timerTime<=0 { - log.Println("请设置抢购时间") + startTime, err := common.Hour2Unix(common.Config.MustValue("config", "buy_time", "09:59:59")) + if err != nil { + log.Fatal("抢购开始时间初始化失败,请检查时间格式", err) + } + if startTime.Unix() < time.Now().Unix() { + startTime = startTime.AddDate(0, 0, 1) + } + log.Println("抢购开始时间:", startTime) + + localTime := time.Now().UnixNano() / 1e6 + log.Println("本地系统时间:", localTime) + jdTime, _ := GetJdTime() + log.Println("京东云端时间:", jdTime) + diffTime := localTime - jdTime + log.Println(fmt.Sprintf("本地时间与京东服务器时间误差为【%d】毫秒", diffTime)) + + buyTime := startTime.UnixNano() / 1e6 + timerTime := (buyTime + diffTime) - jdTime + if timerTime <= 0 { + log.Println("请设置抢购开始时间,或直接清空") os.Exit(0) } //等待抢购 - time.Sleep(time.Duration(timerTime)*time.Millisecond) + log.Println("等待抢购中……") + time.Sleep(time.Duration(timerTime) * time.Millisecond) //开始抢购 log.Println("时间到达,开始执行……") seckill:=jd_seckill.NewSeckill(common.Client,common.Config) diff --git a/common/lib.go b/common/lib.go index 6a6a2d0..58aa2fe 100644 --- a/common/lib.go +++ b/common/lib.go @@ -108,4 +108,8 @@ func OpenImage(file string) { _=cmd.Start() } } -} \ No newline at end of file +} + +func Hour2Unix(hour string) (time.Time, error) { + return time.ParseInLocation(DateTimeFormatStr, time.Now().Format(DateFormatStr) + " " + hour, time.Local) +} diff --git a/common/var.go b/common/var.go index 023779c..69a776f 100644 --- a/common/var.go +++ b/common/var.go @@ -5,9 +5,12 @@ import ( "github.com/unknwon/goconfig" ) -const SoftName = "jd_seckill" - -const Version = "0.1.7" +const ( + SoftName = "jd_seckill" + Version = "0.1.7" + DateTimeFormatStr = "2006-01-02 15:04:05" + DateFormatStr = "2006-01-02" +) var Client *httpc.HttpClient diff --git a/conf.ini b/conf.ini index eba8fae..f9e2541 100644 --- a/conf.ini +++ b/conf.ini @@ -12,8 +12,8 @@ test_sku_id = 100016034372 sku_id = 100012043978 # 抢购数量 seckill_num = 2 -# 抢购开始时间设定 2021-01-01 09:59:59 -buy_time = 2021-01-01 09:59:59 +# 抢购开始时间设定,格式:09:59:59,如果小于当前时间,则表示明天这个时间点开始;大于当前时间,则为今天。 +buy_time = 09:59:59 # 抢购总时间,单位:分钟,默认两分钟 seckill_time = # 抢购任务数量,默认5个 From 9091401d6e12dc04bf3b9225d827b96af1fd2a9e Mon Sep 17 00:00:00 2001 From: pppscn <35696959@qq.com> Date: Tue, 5 Jan 2021 20:00:49 +0800 Subject: [PATCH 5/5] =?UTF-8?q?=E6=8A=A2=E8=B4=AD=E5=BC=80=E5=A7=8B?= =?UTF-8?q?=E6=97=B6=E9=97=B4=E8=AE=BE=E5=AE=9A=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 79f70b6..10bca33 100644 --- a/README.md +++ b/README.md @@ -114,7 +114,7 @@ $ zbarimg qr_code.png > qrcode.txt && qrencode -r qrcode.txt -o - -t UTF8 # 解 - 程序开始抢购总时间为两分钟,不管有无抢购成功,都会停止,抢购详情请查阅日志和自己配置的第三方推送服务(目前支持:钉钉机器人、SMTP邮件、Server酱) -- 第二天抢购需要修改抢购时间和重新开始抢购任务。 +- 抢购开始时间设定,格式:09:59:59,如果小于当前时间,则表示明天这个时间点开始;大于当前时间,则为今天 - 先写这么多。。。