资讯专栏INFORMATION COLUMN

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

BDEEFE / 707人阅读

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

描述

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

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

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


通过连接邀请

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

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

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

class InviteUserView(View):
    """邀请注册"""

    @method_decorator(login_required)
    def get(self, request):
        return HttpResponseRedirect(reverse("myauth:personal-center"))
        
        

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

def invite_user(backend, user, response, *args, **kwargs):
    is_new = kwargs["is_new"]
    if not is_new or not user:
        return
    # 二维码扫描
    ...
    # 点击邀请链接
    next_url = backend.strategy.session_get("next")
    if next_url:
        params = parse_url(next_url)["params"]
        inviter_id = params.get("inviter_id")
        if inviter_id and user:
            try:
                inviter = User.objects.get(id=inviter_id)
                UserInvite.invite_user(inviter_id, user, only_allow_invited_by_one_user=True)
            except:
                return
            user._inviter = inviter
        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这段代码才是这篇博客里面难度最大的东西

def weixin_handler_event_scan(self, request, parsed_wechat, *args, **kwargs):
    key = parsed_wechat.message.key # 对应生成二维码的key
    ticket = parsed_wechat.message.ticket
    if ticket:
        response = self.handle_invite_scan(request, parsed_wechat, key)
        if response:
            return response
    return self.weixin_handler_event(
            request, parsed_wechat, *args, **kwargs)
            
def handle_invite_scan(self, request, parsed_wechat, scene_id):
    try:
        qrcode = QRCode.objects.get(scene_id=scene_id, action_type="invite_user")
    except QRCode.DoesNotExist:
        return
    openid = parsed_wechat.message.source
    user_info = parsed_wechat.get_user_info(openid)
    strategy = load_strategy(request)
    backend = WeixinOAuth2APP()
    backend.strategy = strategy
    idx, backend, xargs, xkwargs = strategy.partial_from_session(
        {
            "next":0,
            "backend": backend,
            "args":[],
            "kwargs":{"qrcode": qrcode},
        }
    )
    xkwargs.update({"response": user_info})
    user = backend.continue_pipeline(pipeline_index=idx, *xargs, **xkwargs)
            if not user:
        return
    if user.is_new and hasattr(user, "_inviter"):
        content = u"感谢您的加入,邀请者是 {}".format(user._inviter.username)
        response_xml = parsed_wechat.response_text(content=content)
        return HttpResponse(response_xml, content_type="application/xml")
        

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

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

def invite_user(backend, user, response, *args, **kwargs):
    is_new = kwargs["is_new"]
    if not is_new or not user:
        return
    # 二维码扫描
    qrcode = kwargs.get("qrcode")
    if qrcode and qrcode.userprofile_set.all().exists():
        inviter = qrcode.userprofile_set.all()[0].user
        try:
            UserInvite.invite_user(inviter.id, user, only_allow_invited_by_one_user=True)
        except:
            return
        user._inviter = inviter
        return {"inviter": inviter}
    ....
微信二维码

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

class QRCode(models.Model):

    TEMP_QRCODE_UPDATE_DAYS = 7

    QR_SCENE = "QR_SCENE"
    QR_LIMIT_SCENE = "QR_LIMIT_SCENE"
    QR_LIMIT_STR_SCENE = "QR_LIMIT_STR_SCENE"
    ACTION_NAME_CHOICES = (
        (QR_SCENE, QR_SCENE),
        (QR_LIMIT_SCENE, QR_LIMIT_SCENE),
        (QR_LIMIT_STR_SCENE, QR_LIMIT_STR_SCENE),
    )
    url = models.URLField(blank=True, max_length=255, default="")
    # QR_SCENE时上限为2**32
    scene_id = models.CharField(blank=True, max_length=255, db_index=True, default="")
    update_time = models.DateTimeField(blank=True, null=True)
    action_name = models.CharField(max_length=30,
            choices=ACTION_NAME_CHOICES, default=QR_SCENE, db_index=True)
    action_type = models.CharField(max_length=255, default="", db_index=True)

    @classmethod
    def get_qrcode(cls, action_name, scene_id, action_type=None):
        now = timezone.now()
        qrcode = None
        try:
            qrcode = cls.objects.get(action_name=action_name, scene_id=scene_id)
            # 临时二维码判断是否过期
            if qrcode.action_name == cls.QR_SCENE:
                if qrcode.update_time and qrcode.url:
                    _delta = now - qrcode.update_time
                    if _delta.days < qrcode.TEMP_QRCODE_UPDATE_DAYS:
                        return qrcode
            else:
                return qrcode
        except cls.DoesNotExist:
            pass
        if not qrcode:
            qrcode = cls(
                    action_name=action_name,
                    scene_id=scene_id,
                    action_type=action_type)
        qrcode.update_time = now
        if action_name == cls.QR_SCENE:
            qrcode.url = create_temp_qrcode(scene_id)
        else:
            qrcode.url = create_permanent_qrcode(scene_id)
        qrcode.save()
        return qrcode

    @classmethod
    def generate_temp_scene_id(cls, obj_id):
        """max id: 2 ** 32 = 4294967296"""
        return int("{}{}{}".format(randint(1, 3), obj_id, uuid4().int)[:9])

    @property
    def qrcode_url(self):
        if not self.action_name or not self.scene_id:
            raise Exception(u"qrcode object must have action_name and scene_id value")
        now = timezone.now()
        # 永久化的二维码不必更新
        if self.action_name != self.QR_SCENE:
            if not self.url:
                self.update_time = now
                self.url = create_permanent_qrcode(self.scene_id)
                self.save()
            return self.url
        # 临时二维码判断是否过期
        if self.update_time and self.url:
            _delta = now - self.update_time
            if _delta.days < self.TEMP_QRCODE_UPDATE_DAYS:
                return self.url
        self.update_time = now
        self.url = create_temp_qrcode(self.scene_id)
        self.save()
        return self.url

       

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

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

相关文章

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

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

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

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

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

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

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

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

    codeGoogle 评论0 收藏0
  • 捋一捋这些年一起薅过羊毛 &amp;#8211; 包含永久免费vps、域名等

    摘要:特点,永久免费,数据中心多达个,之前好用的首尔和东京因为众多的涌入导致线路拥堵不堪此外机型可自由分配个,内存,最大空间容量和的带宽。是乌克兰官方为个人提供的免费二级域名后缀,任何人都可以申请。由乌克兰官方运行,稳定性有保障。作为一个mjj,最喜欢做的事情就是薅羊毛,这篇文章就来整理一些特别优质的,不容错过的羊毛!算是薅羊毛的一个指南!! 一:永久VPS篇 这里整理一些永久...

    mrli2016 评论0 收藏1

发表评论

0条评论

BDEEFE

|高级讲师

TA的文章

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