资讯专栏INFORMATION COLUMN

微信公众号开发小记——4.两种邀请用户的方式 扫码&&链接

BDEEFE / 1200人阅读

摘要:借着这个需求体会了下微信开发的两种不同类型非端口的两种开发,以及的一些正确姿势。关于用户微信登录的事情我们通过已经解决了参考我的上一篇博客微信公众号开发小记接入三方登录,所以可以直接用的装饰器完成这种事情。

描述

假设的我们的服务号有这么一些功能,比如底部有按钮,点击会有一些复杂的功能,这时候可能就需要一个用户系统,有用户系统就经常想要做什么分享邀请新用户之类的,这时候就又有几种方式,1.直接一个连接,让其他用户点;2.有一个二维码,让离得近的朋友扫。

借着这个需求体会了下微信开发的两种不同类型(80非80端口的两种开发),以及python-social-auth的一些正确姿势。

而这个需求其实就对应了两种开发模式,比如有个需求可以在公众号内直接回复,或者在一个页面里面让用户提交表单等等。


通过连接邀请

首先,理解需求:用户在微信点击我们的邀请连接后,会引导用户做一个有绿色按钮的微信登录,用户登陆后成为我们的用户,并且跳转到某个页面。

关于用户微信登录的事情我们通过python-social-auth已经解决了(参考我的上一篇博客微信公众号开发小记——3.接入三方登录),所以可以直接用django的login_required装饰器完成这种事情。

由于微信号的登录只有微信,所以LOGIN_URL = "/login/weixinapp/"

</>复制代码

  1. class InviteUserView(View):
  2. """邀请注册"""
  3. @method_decorator(login_required)
  4. def get(self, request):
  5. return HttpResponseRedirect(reverse("myauth:personal-center"))

上面的代码只是保证用户点解邀请链接会成为我们的用户,但是没有记录对应的邀请者信息等等,由于邀请这个事情其实是一个登录的流程,所以可以写在pipeline里面

</>复制代码

  1. def invite_user(backend, user, response, *args, **kwargs):
  2. is_new = kwargs["is_new"]
  3. if not is_new or not user:
  4. return
  5. # 二维码扫描
  6. ...
  7. # 点击邀请链接
  8. next_url = backend.strategy.session_get("next")
  9. if next_url:
  10. params = parse_url(next_url)["params"]
  11. inviter_id = params.get("inviter_id")
  12. if inviter_id and user:
  13. try:
  14. inviter = User.objects.get(id=inviter_id)
  15. UserInvite.invite_user(inviter_id, user, only_allow_invited_by_one_user=True)
  16. except:
  17. return
  18. user._inviter = inviter
  19. return {"inviter": inviter}
通过扫码邀请

首先,扫码是一个服务号80端口的事件,所以代码添加在weixin_server/views.py 微信公众号开发小记——2.80端口上的服务

难点在于这里,微信扫码后是直接进入公众号的,如果你想要让用户进入公众号之后就变成我们的用户而不是让他在点一个东西这里是比较蛋疼的,因为你的服务器在这时候做302微信是不认得。这就导致了几个问题:

由于不引导用户登录,我这里没办法直接用python-social-auth里面的do_complete方法(因为拿不到用户的access_token),不过好处是使用微信服务器的access_token以及用户的openid我可以直接拿到这个用户的用户信息。这个问题就变成了python-social-auth的do_complete有用户response后执行pipeline的逻辑了。

然后我扒了下代码,用了几个小时从单测里面找到了这个逻辑,具体见handle_invite_scan这段代码才是这篇博客里面难度最大的东西

</>复制代码

  1. def weixin_handler_event_scan(self, request, parsed_wechat, *args, **kwargs):
  2. key = parsed_wechat.message.key # 对应生成二维码的key
  3. ticket = parsed_wechat.message.ticket
  4. if ticket:
  5. response = self.handle_invite_scan(request, parsed_wechat, key)
  6. if response:
  7. return response
  8. return self.weixin_handler_event(
  9. request, parsed_wechat, *args, **kwargs)
  10. def handle_invite_scan(self, request, parsed_wechat, scene_id):
  11. try:
  12. qrcode = QRCode.objects.get(scene_id=scene_id, action_type="invite_user")
  13. except QRCode.DoesNotExist:
  14. return
  15. openid = parsed_wechat.message.source
  16. user_info = parsed_wechat.get_user_info(openid)
  17. strategy = load_strategy(request)
  18. backend = WeixinOAuth2APP()
  19. backend.strategy = strategy
  20. idx, backend, xargs, xkwargs = strategy.partial_from_session(
  21. {
  22. "next":0,
  23. "backend": backend,
  24. "args":[],
  25. "kwargs":{"qrcode": qrcode},
  26. }
  27. )
  28. xkwargs.update({"response": user_info})
  29. user = backend.continue_pipeline(pipeline_index=idx, *xargs, **xkwargs)
  30. if not user:
  31. return
  32. if user.is_new and hasattr(user, "_inviter"):
  33. content = u"感谢您的加入,邀请者是 {}".format(user._inviter.username)
  34. response_xml = parsed_wechat.response_text(content=content)
  35. return HttpResponse(response_xml, content_type="application/xml")

然后就可以正常的执行了,由于二维码的机制跟url不同,所以需要多带带的二维码处理逻辑

下面先把pipeline的那段代码贴过来,这里没什么特殊的

</>复制代码

  1. def invite_user(backend, user, response, *args, **kwargs):
  2. is_new = kwargs["is_new"]
  3. if not is_new or not user:
  4. return
  5. # 二维码扫描
  6. qrcode = kwargs.get("qrcode")
  7. if qrcode and qrcode.userprofile_set.all().exists():
  8. inviter = qrcode.userprofile_set.all()[0].user
  9. try:
  10. UserInvite.invite_user(inviter.id, user, only_allow_invited_by_one_user=True)
  11. except:
  12. return
  13. user._inviter = inviter
  14. return {"inviter": inviter}
  15. ....
微信二维码

二维码有两种大的类型,永久二维码、临时二维码,永久上线10万张,scenen_id为1~10万,然而他又有scenen_str这种字符串的形式,那肯定选第二种字符串了;临时二维码则scenen_id为1~2^10,这点需要注意,超过这个限制secen_id都是2^10-1,而且蛋疼的是,临时二维码会有过期时间需要维护这个二维码。为了方便我们的业务逻辑查询,我添加了一个action_type的字段,来做业务上的区别,方便查询。

</>复制代码

  1. class QRCode(models.Model):
  2. TEMP_QRCODE_UPDATE_DAYS = 7
  3. QR_SCENE = "QR_SCENE"
  4. QR_LIMIT_SCENE = "QR_LIMIT_SCENE"
  5. QR_LIMIT_STR_SCENE = "QR_LIMIT_STR_SCENE"
  6. ACTION_NAME_CHOICES = (
  7. (QR_SCENE, QR_SCENE),
  8. (QR_LIMIT_SCENE, QR_LIMIT_SCENE),
  9. (QR_LIMIT_STR_SCENE, QR_LIMIT_STR_SCENE),
  10. )
  11. url = models.URLField(blank=True, max_length=255, default="")
  12. # QR_SCENE时上限为2**32
  13. scene_id = models.CharField(blank=True, max_length=255, db_index=True, default="")
  14. update_time = models.DateTimeField(blank=True, null=True)
  15. action_name = models.CharField(max_length=30,
  16. choices=ACTION_NAME_CHOICES, default=QR_SCENE, db_index=True)
  17. action_type = models.CharField(max_length=255, default="", db_index=True)
  18. @classmethod
  19. def get_qrcode(cls, action_name, scene_id, action_type=None):
  20. now = timezone.now()
  21. qrcode = None
  22. try:
  23. qrcode = cls.objects.get(action_name=action_name, scene_id=scene_id)
  24. # 临时二维码判断是否过期
  25. if qrcode.action_name == cls.QR_SCENE:
  26. if qrcode.update_time and qrcode.url:
  27. _delta = now - qrcode.update_time
  28. if _delta.days < qrcode.TEMP_QRCODE_UPDATE_DAYS:
  29. return qrcode
  30. else:
  31. return qrcode
  32. except cls.DoesNotExist:
  33. pass
  34. if not qrcode:
  35. qrcode = cls(
  36. action_name=action_name,
  37. scene_id=scene_id,
  38. action_type=action_type)
  39. qrcode.update_time = now
  40. if action_name == cls.QR_SCENE:
  41. qrcode.url = create_temp_qrcode(scene_id)
  42. else:
  43. qrcode.url = create_permanent_qrcode(scene_id)
  44. qrcode.save()
  45. return qrcode
  46. @classmethod
  47. def generate_temp_scene_id(cls, obj_id):
  48. """max id: 2 ** 32 = 4294967296"""
  49. return int("{}{}{}".format(randint(1, 3), obj_id, uuid4().int)[:9])
  50. @property
  51. def qrcode_url(self):
  52. if not self.action_name or not self.scene_id:
  53. raise Exception(u"qrcode object must have action_name and scene_id value")
  54. now = timezone.now()
  55. # 永久化的二维码不必更新
  56. if self.action_name != self.QR_SCENE:
  57. if not self.url:
  58. self.update_time = now
  59. self.url = create_permanent_qrcode(self.scene_id)
  60. self.save()
  61. return self.url
  62. # 临时二维码判断是否过期
  63. if self.update_time and self.url:
  64. _delta = now - self.update_time
  65. if _delta.days < self.TEMP_QRCODE_UPDATE_DAYS:
  66. return self.url
  67. self.update_time = now
  68. self.url = create_temp_qrcode(self.scene_id)
  69. self.save()
  70. return self.url

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/45431.html

相关文章

  • 微信公众开发小记——2.80端口上服务

    摘要:微信在做一些操作是需要用到生成二维码等而每天接口的调用上限为,需要自己做缓存文档讲了几种方式,我觉得放在中拿比较妥当。微信菜单会缓存分钟,你可以取消关注,然后在关注查看菜单变化效果。 描述 微信公众号开发基本分为2大种类型 1.用户直接做了某些操作(回复信息、订阅、扫码、发语音、点按钮等),此时这些信息微信会发送到微信服务器的80端口,这是一种开发类型;2.通过连接(按钮、文章)引导用...

    frank_fun 评论0 收藏0
  • U大使必看!!新手指引来啦

    第一步 注册加入U大使点击立即加入:U大使注册入口 根据指引填写好注册信息,注册成功会收到短信通知。登录U大使推广管理平台方式登录UCloud控制台后,可在产品总览页面找到U大使推广管理平台第二步 获取专属邀请链接进行推广获取方式一:注册成功后会收到一封邮件,内附专属推广链接获取方式二:登录U大使推广管理平台,在推广素材中不仅可以找到UCloud注册页面的邀请链接,还可以找到更多热门活动的邀请链接...

    UCloud小助手 评论1 收藏0
  • 【沙龙报名中】与微信&amp;云开发官方团队零距离互动,揭秘爆款微信小游戏背后技术!

    摘要:在现如今的游戏市场寒冬中,拥有微信庞大的用户量以及更好兼容性的小程序游戏,优势就显得格外明显。扫描二维码即可报名您在现场将有这些体验来自腾讯云云开发团队与微信团队联合打造干货分享,内容包括微信小游戏首发经验分享。 有人说微信小程序游戏的百花齐放 活像十几年前的4399小游戏称霸互联网的景象 歪,斗地主吗,三缺二, 不用下app,小程序就能玩,我保证不抢地主让你抢! ...... ‘...

    zhonghanwen 评论0 收藏0
  • 浅析微信支付:开通免充值产品功能及如何进行接口升级指引

    摘要:本文是浅析微信支付系列文章的第十五篇,主要讲解如何开通免充值产品功能流程和其中的注意事项,对于接口升级会重要讲解,避免爬坑。 本文是【浅析微信支付】系列文章的第十五篇,主要讲解如何开通免充值产品功能流程和其中的注意事项,对于接口升级会重要讲解,避免爬坑。 浅析微信支付系列已经更新十五篇了哟~,没有看过的朋友们可以看一下哦。 浅析微信支付:商户平台代金券或立减优惠开通、指定用户代金券发...

    NicolasHe 评论0 收藏0
  • JavaScript &amp; jQuery 常用方法小记

    摘要:函数节流用途如调整浏览器大小,或者用户输入信息,导致反复提交接口调用方法判断手机端访问获取地址栏参数返回顶部当滚动条的位置处于距顶部像素以下时,跳转链接出现,否则消失当点击跳转链接后,回到页面顶部位置正则检测手机号邮箱 1.JavaScript 函数节流用途:如调整浏览器大小,或者用户输入信息,导致反复提交接口 function throttle(method,context) { ...

    codeGoogle 评论0 收藏0

发表评论

0条评论

BDEEFE

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<