Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

很难抢到菜,逻辑似乎有问题 #41

Closed
cris8259 opened this issue Apr 15, 2022 · 16 comments
Closed

很难抢到菜,逻辑似乎有问题 #41

cris8259 opened this issue Apr 15, 2022 · 16 comments

Comments

@cris8259
Copy link

提交订单失败后就又重新跑一边获取购物车,配送时间 ,应该是 循环获取购物车,成功后循环获取配送时间段,成功后循环提交订单,提交订单失败又分几种,1. 购物车商品全部是无效的->退出 2. 购物车商品部分无效->更新购物车 3. 无配送时间段->循环获取配送时间段 4. 网络繁忙->继续提交订单。 就几种状态,建议用一个状态机,for循环

for _, reserveTime := range multiReserveTime {
		sess := session.Clone()
		sess.UpdatePackageOrder(reserveTime)
		wg.Go(func() error {
			startTime := time.Unix(int64(sess.PackageOrder.PaymentOrder.ReservedTimeStart), 0).Format("2006/01/02 15:04:05")
			endTime := time.Unix(int64(sess.PackageOrder.PaymentOrder.ReservedTimeEnd), 0).Format("2006/01/02 15:04:05")
			timeRange := startTime + "——" + endTime
			logrus.Infof(">>> 提交订单中, 预约时间段(%s)", timeRange)
			if err := sess.CreateOrder(context.Background()); err != nil {
				logrus.Warningf("提交订单(%s)失败: %v", timeRange, err)
				return err
			}

			successCh <- struct{}{}
			return nil
		})
	}
@zc2638
Copy link
Owner

zc2638 commented Apr 15, 2022

可以提供下对应的 code 吗
如果可以的话,也可以提交 PR

@cris8259
Copy link
Author

我只会c语言,不懂go ,只是抢菜几次发现成功率对比手动抢没有优势。然后看log输出每次都有获取购物车的信息,所以就这么猜测了,然后看了一下你的代码,好像印证了我的猜测。我会go的话就改掉测试,然后pr了。所以还需要你检查一下

@cris8259
Copy link
Author

类似下面这种,在几种状态之间切换,当然还要考虑延时的问题。我记得你其中有一个延时使用 3+rand() 秒来延时的,太长了,以设置的ms级别来,关于封号的问题,我把延时设置为1ms 5ms都不见有封号的现象,没有长时间运行,15分钟以内,几个号跑的,都不见有封号
while(1)
{
if(state == getCart)
{
if(getCart() == success )
{
state = getTime;
}

}
else if(state == getTime)
{

}
else if(state == sendOrder)
{

}

}

@zc2638
Copy link
Owner

zc2638 commented Apr 15, 2022

对应的 状态码 哈

@cris8259
Copy link
Author

是的哈,等你的更新,哈哈

@chenxuuu
Copy link

应该改一下这里的代码?

https://github.com/zc2638/ddshop/blob/main/core/ddmc/session.go#L124-L176=

@chenxuuu
Copy link

chenxuuu commented Apr 15, 2022

大概这样?没学过go,编译环境都没有,瞎改的不知道能不能用

func (s *Session) run(ctx context.Context) error {
	logrus.Info("=====> 获取购物车中有效商品")

	if err := s.CartAllCheck(ctx); err != nil {
		return fmt.Errorf("全选购物车商品失败: %v", err)
	}
	cartData, err := s.GetCart(ctx)
	if err != nil {
		return err
	}

	products := cartData["products"].([]map[string]interface{})
	for k, v := range products {
		logrus.Infof("[%v] %s 数量:%v 总价:%s", k, v["product_name"], v["count"], v["total_price"])
	}

	for {
		for {
			logrus.Info("=====> 获取可预约时间")
			multiReserveTime, err := s.GetMultiReserveTime(ctx, products)
			if err != nil {
				logrus.Errorf("获取可预约时间失败: %v", err)
			} else {
				break
			}
			if len(multiReserveTime) == 0 {
				return ErrorNoReserveTime
			} else {
				break
			}
			time.Sleep(time.Duration(s.cfg.DDMC.Interval) * time.Millisecond)
		}

		logrus.Infof("发现可用的配送时段!")

		logrus.Info("=====> 生成订单信息")
		for {
			checkOrderData, err := s.CheckOrder(ctx, cartData, multiReserveTime)
			if err != nil {
				logrus.Errorf("检查订单失败: %v", err)
			} else {
				logrus.Infof("订单总金额:%v\n", checkOrderData["price"])
				break
			}
			time.Sleep(time.Duration(s.cfg.DDMC.Interval) * time.Millisecond)
		}

		for {
			sess := s.Clone()
			sess.SetReserve(multiReserveTime[0])
	
			startTime := time.Unix(int64(sess.Reserve.StartTimestamp), 0).Format("2006/01/02 15:04:05")
			endTime := time.Unix(int64(sess.Reserve.EndTimestamp), 0).Format("2006/01/02 15:04:05")
			timeRange := startTime + "——" + endTime
			logrus.Infof("=====> 提交订单中, 预约时间段(%s)", timeRange)
			if err := sess.CreateOrder(context.Background(), cartData, checkOrderData); err != nil {
				logrus.Errorf("提交订单(%s)失败: %v", timeRange, err)
			} else {
				break
			}
			time.Sleep(time.Duration(s.cfg.DDMC.Interval) * time.Millisecond)
		}

		if err := s.noticeIns.Notice("抢菜成功", "叮咚买菜 抢菜成功,请尽快支付!"); err != nil {
			logrus.Warningf("通知失败: %v", err)
		}
		return nil
	}
}

@zc2638
Copy link
Owner

zc2638 commented Apr 16, 2022

https://github.com/zc2638/ddshop/blob/main/core/ddmc/session.go#L195-L230

func (s *Session) GetAddress() (map[string]AddressItem, error) {
...
	resp, err := s.execute(context.Background(), req, http.MethodGet, urlPath, maxRetryCount)
	if err != nil {
		return nil, err
	}
...

在请求层面直接就重试了哦,可以避免再次构建参数带来的时间和性能损耗

@chenxuuu
Copy link

今天测试了,刷新购物车+获取订单+提交订单全部连续成功的概率基本为0,还是要在每一步逐个尝试比较好

@cris8259
Copy link
Author

@chenxuuu 是的,现实中比手动抢菜成功率低,哈哈

@SoberY
Copy link

SoberY commented Apr 17, 2022

在6:00和8:30 提交订单页面好像是会自动获取可配送时间的(对应的可能是post请求不需要填写 reserved_time_start ),
另外好像也会自动剔除缺货的菜(不缺定是在请求里提前剔除 还是参数中带有多余的货品也可以 我猜大概率是后者),

所以比较好的逻辑或许是前面的逻辑成功以后 不在进行重试 而是不停的发送addNewOrder请求? (对应的手动不停点击提交订单), 对于使用者而言应该添加更多的菜品(因为会自动剔除缺货的菜品)

https://github.com/zc2638/ddshop/blob/main/core/ddmc/session.go#L195-L230

func (s *Session) GetAddress() (map[string]AddressItem, error) {
...
	resp, err := s.execute(context.Background(), req, http.MethodGet, urlPath, maxRetryCount)
	if err != nil {
		return nil, err
	}
...

在请求层面直接就重试了哦,可以避免再次构建参数带来的时间和性能损耗

在6:00和8:30 提交订单页面好像是会自动获取可配送时间的(对应的可能是post请求不需要填写 reserved_time_start ),
另外好像也会自动剔除缺货的菜(不缺定是在请求里提前剔除 还是参数中带有多余的货品也可以 我猜大概率是后者),

所以比较好的逻辑或许是前面的逻辑成功以后 不在进行重试 而是不停的发送addNewOrder请求? (对应的手动不停点击提交订单), 对于使用者而言应该添加更多的菜品(因为会自动剔除缺货的菜品)

@zc2638
Copy link
Owner

zc2638 commented Apr 17, 2022

https://github.com/zc2638/ddshop/blob/main/core/ddmc/session.go#L127-L183

增加了购物车商品 及 可配送时间缓存,可以看看新逻辑

@yanjinbin
Copy link

https://github.com/zc2638/ddshop/blob/main/core/ddmc/session.go#L195-L230

func (s *Session) GetAddress() (map[string]AddressItem, error) {
...
	resp, err := s.execute(context.Background(), req, http.MethodGet, urlPath, maxRetryCount)
	if err != nil {
		return nil, err
	}
...

在请求层面直接就重试了哦,可以避免再次构建参数带来的时间和性能损耗

关于重试 是不是可以直接用框架 处理好了 是否重试的业务逻辑 可以写在外面 类似 https://pkg.go.dev/github.com/avast/retry-go 😄

@zc2638
Copy link
Owner

zc2638 commented Apr 17, 2022

关于重试 是不是可以直接用框架 处理好了 是否重试的业务逻辑 可以写在外面 类似 https://pkg.go.dev/github.com/avast/retry-go 😄

func (s *Session) execute(ctx context.Context, request *resty.Request, method, url string, count int) (*resty.Response, error) {
	...
	resp, err := request.Execute(method, url)
	if err != nil {
		return nil, fmt.Errorf("request failed: %v", err)
	}
	...
	count--
	return s.execute(nil, request, method, url, count)
}

在请求层面已经retry了

@cris8259
Copy link
Author

没有可预约时间的情况下,使用3+rand() 秒延时,这个太大了,应该用ms级延时,或者放到配置文件里。有时候没有预约时间,刷新又能有

@yanjinbin
Copy link

我发现 sku spu 不一样 但是都是同种商品可能性挺大的

@zc2638 zc2638 closed this as completed Apr 18, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants