个人案例
- 基于多端身份管理框架构建跨小程序、IOS、Andriod 的用户管理体系
文章前的一些声明。1、我本人其实没啥编程基础,唯一会的代码是print 'hello world'2、为什么会涉及到这个用户管理体系其实是因为我在尝试用AI 进行应用前后端开发,目前产品的前后端已经基本跑通,只涉及到一些小地方的调整,但考虑到后面使用多端框架开发的纯小白可能会越来越多,所以把自己的实现逻辑发出来作为记录,也是抛砖引玉,希望大佬们能帮我看下目前的实现逻辑有没有错漏的地方。 业务是个多端项目。实际上针对不同平台会有不同的登录方式处理。目前规划会有两个平台: 微信小程序(下称小程序)IOS(下称APP)这里面有个大前提,我们的整套用户管理体系都是基于微信小程序多端框架提供的身份管理逻辑来的,根据微信官方的文档,多端身份管理基于微信身份管理体系提供的user_id,但这里有个问题。多端身份管理并不提供小程序内的登录方式,只在多端app里生效。在这种前提下,我们需要基于多端框架的身份管理体系重新构建一套完整的用户管理体系。 这套体系在小程序与app端登录方式不同,但我们尝试通过users表中的不同字段将其关联起来,由此构建一个完整的身份管理体系。但无论是哪种登录方式,我们都会在前置认证环节走完后用jwt方式生成token(包括refresh token),并返回给客户端。由此达到构建用户登录态的目的。 > 注意:用户表中存在两个字面意思的userid。一个是用户表中的自增id(列名为`id`),一个是多端用户管理体系中的user_id(列名为`user_id`)。当我们使用 **用户id** 表述时指的是自增id,当我们使用 **user_id** 表述时指的是多端用户管理体系中的user_id。 **用户id**用来满足服务业务中的关联查询等需求,**user_id** 用来满足多端用户管理体系中的用户管理需求。 小程序端 在小程序端,我们提供两种方式来实现登录 `wx.login` 登录 前端通过wx.login获取微信的登录code并发给后端,后端服务走 `code2Session` 用code换取 openid、unionid,然后在此基础上生成token返回给前端构建登录态。 手机号登录+`wx.login`登录 需要重点说明下,这里前端其实是两个步骤 步骤1:前端调用手机号快速验证组件也就是 `bindgetphonenumber` 获取手机号code 步骤2:前端调用 `wx.login` 获取微信登录code 步骤3:前端将手机号的code和微信的登录code发给后端,后端服务先走`code2Session` 用wx.login传回来的code换取 openid、unionid,然后再走 `getPhoneNumber` 用手机号的code换取手机号信息,然后用 `code2Session` 用手机号的code换取手机号。 步骤3中,先检查手机号是否在users表中存在,如果存在则开始构建登录态。如果手机号在users表中不存在,则使用传回来的 `unionid` 创建新用户,同时给这个账户绑定当前手机号,然后开始构建登录态。APP端 APP端和小程序端实现稍有不同,重点在于APP端登陆时,服务端使用的是 `code2Verifyinfo` 来换取用户标识信息,这个标识信息在不同登录接口下将返回不同的登录信息,且针对绑定情况,返回信息也会有所不同(见 code2Verifyinfo 板块) 在app端,我们提供3种登录方式 微信APP登录苹果登录手机号登录微信APP登录:`wx.weixinAppLogin` 前端通过 `wx.weixinAppLogin` 获取微信的登录code,然后后端服务走 `code2Verifyinfo` 用code换取用户标识信息,然后在此基础上生成token返回给前端构建登录态。 1. 获取标识信息后,检查`unionid`是否在users表中存在,如果存在则检查`user_id`是否在users表中存在: 如果`user_id`存在,则开始构建登录态。如果`user_id`不存在,则使用`unionid`创建新用户,同时将获取到的其他用户标识信息写入到用户表中(`user_id`、`logintype`、`login_time`)2. 如果`unionid`在users表中不存在,则使用`unionid`创建新用户,同时将获取到的其他用户标识信息写入到用户表中(`user_id`、`logintype`、`login_time`) 苹果登录:`wx.appleLogin` 前端通过 `wx.appleLogin` 获取苹果的登录code,然后后端服务走 `code2Verifyinfo` 用code换取用户标识信息,然后在此基础上生成token返回给前端构建登录态。 1. 获取标识信息后,检查`user_id`是否在users表中存在,如果存在则开始构建登录态。 2. 如果`user_id`在users表中不存在,则使用`user_id`创建新用户。 手机号登录 手机号登录前端会有两种方式 本机号码一键登录:`wx.phoneOneClickLogin`手机号验证码登录:`wx.phoneSmsLogin`两种方式只是前端在获取code的方式不同,需要为两种code设计不同接口即可,走 `code2Verifyinfo` 用code换取的用户标识信息是一样的,下方只讲获取标识信息后的处理逻辑 1. 获取标识信息后,要判断当前是否返回了其他登录方式(`openapp_info`、`apple_info`)的标识信息,如果没返回其他登录方式的标识信息,则检查手机号是否在users表中存在: 如果手机号在users表中存在,则开始构建登录态,并返回「该手机号此前绑定过小程序,已使用绑定账号登录」同时将获取到的其他用户标识信息写入到用户表中(`user_id`、`logintype`、`login_time`)如果手机号在users表中不存在,则使用`user_id`创建新用户。2. 如果返回了其他登录方式(`openapp_info`、`apple_info`)的标识信息,则直接使用返回的信息构建用户登录态即可。 最后附上完整的用户登录流程图 [图片]
04-03 - Skyline|小程序手势:让半屏弹窗更顺滑
在小程序页面开发中,我们经常用半屏弹窗来进来内容展示,例如:微信开放社区切换主页、加入购物车的选项页、文章留言区等等。 [图片] 常见的半屏弹窗展示逻辑是这样的: 打开弹窗:点击 “打开弹窗” 按钮展示弹窗关闭弹窗:点击“关闭按钮” or 遮罩层 关闭弹窗当我们想在半屏弹窗加一些交互动画时,可以监听节点的 touch 事件来做一些手势判断,进而处理拖拽事件。但是这种方式实现的滚动动画容易卡顿,出现延迟的情况,效果并不理想。 为了丰富小程序的交互体验,我们内置了一批手势组件,可以帮助开发者更好的实现交互动画的效果。 下图演示使用手势的半屏弹窗下拉效果与普通半屏下拉的对比。 当内部评论列表往下拉到顶部时,变为半屏的下拉,可直接下拉关闭弹窗。 [图片] 我们来看下这种操作是怎么实现的 在上面评论列表的半屏弹窗中会有一个 scroll-view 滚动组件,在 scroll-view 中会有滚动事件,当滚动到顶部时,我们希望有整个半屏的下拉事件。 所以我们需要在半屏的最外层放置一个拖动手势组件 pan-gesture-handler 由于拖动组件内部的 scroll-view 也是可以滚动的,所以这里需要进行一个手势协商的处理,就是什么条件下由哪个组件来响应手势。 当手势往下 ⬇️ 滚动时,此时判断内部 scroll-view 滚动条的位置 滚动条处于顶部:外层 pan-gesture-handler 响应滚动,此时半屏往下拖动至关闭半屏滚动条不处于顶部:内层 scroll-view 响应滚动,此时内部列表往上滚[图片] 当手势往上 ⬆️ 滚动时,此时判断半屏的位置 半屏不完全打开时:外层 pan-gesture-handler 响应滚动,此时半屏往上拖动至完全打开半屏半屏完全打开时:内层 scroll-view 响应滚动,此时内部列表往下滚[图片] 我们来看一下代码的实现,这里用到的手势组件 pan-gesture-handler(拖动时触发)和 vertical-drag-gesture-handler(纵向滑动时触发),手势组件有以下属性 on-gesture-event:手势回调事件should-response-on-move:是否响应当前手势的 move 阶段simultaneous-handlers:指定需要协商的手势是哪几个,下面演示表示 pan 和 scroll 协同触发。native-view:代理的原生节点,这里 scroll-view(scroll-y) 内有个 vertical-drag 手势,scroll-view 自身无法处理,需要被代理出来 ... 接着,我们看看在页面 js 中怎么处理手势。 在手势处理的回调中因为会改变半屏的状态值,所以这里的回调函数采用 worklet 函数,worklet 函数运行在 UI 线程,使得小程序可以做到类原生动画般的体验。 // page.js // shared 创建的变量为共享变量,可在 UI 线程和 JS 线程间同步 this.transY = wx.worklet.shared(1000) this.scrollTop = wx.worklet.shared(0) this.startPan = wx.worklet.shared(true) // shouldPanResponse 和 shouldScrollViewResponse 用于 pan 手势和 scroll-view 滚动手势的协商 shouldPanResponse() { 'worklet' return this.startPan.value }, shouldScrollViewResponse(pointerEvent) { 'worklet' // transY > 0 说明 pan 手势在移动半屏,此时 scroll-view 滚动不应生效 if (this.transY.value > 0) return false const scrollTop = this.scrollTop.value const { deltaY } = pointerEvent // deltaY > 0 是往上滚动,scrollTop <= 0 是滚动到顶部边界,此时 pan 开始生效,scroll-view 滚动不生效 const result = scrollTop <= 0 && deltaY > 0 this.startPan.value = result return !result }, // pan 手势处理 handlePan(gestureEvent) { 'worklet' if (gestureEvent.state === GestureState.ACTIVE) { const curPosition = this.transY.value const destination = Math.max(0, curPosition + gestureEvent.deltaY) // 改变半屏的位置 this.transY.value = destination } // 其他手势状态的处理,如滚动结束时计算半屏处于打开还是关闭的状态 } 目前,同程旅行 已经上线了手势结合半屏的效果 体验路径:酒店查询 - 选择酒店 - 选择入住人 - 新增入住人 [图片] 普通半屏结合手势代码片段:https://developers.weixin.qq.com/s/lx0RH1mD7rGj 手势除了在普通半屏的应用之外,也可以实现分段式半屏。下面演示的分段式半屏比普通半屏的判断条件更多一些。 判断条件同普通半屏类似,根据手势方向 和 分段式半屏当前的位置来判断是响应分段式半屏还是内部列表,响应分段式半屏是改变到哪一个位置。 [图片] 这里与普通半屏不同的是我们还改变了地图的缩放级别(scale) 因为 worklet 函数是在 UI 线程运行的,当要改变 data 值时,需要通过 wx.worklet.runOnJS 调回 JS 线程。 // page.js // 设置 map scale // 运行在 JS 线程 setMapScale(scale) { this.setData({ scale }) }, // worklet 函数,运行在 UI 线程 scrollTo(toValue) { 'worklet' let scale = 18 if (toValue > screenHeight / 2) { scale = 16 } // 从 UI 线程调回 JS 线程 wx.worklet.runOnJS(this.setMapScale.bind(this))(scale) this.transY.value = timing(toValue, { duration: 200 }) }, // 处理拖动半屏的手势 handlePan(gestureEvent) { 'worklet' // 滚动半屏的位置 if (gestureEvent.state === GestureState.ACTIVE) { // deltaY < 0,往上滑动 this.upward.value = gestureEvent.deltaY < 0 // 当前半屏位置 const curPosition = this.transY.value // 只能在 [statusBarHeight, screenHeight] 之间移动 const destination = clamp(curPosition + gestureEvent.deltaY, statusBarHeight, screenHeight) if (curPosition === destination) return // 改变 transY,来改变半屏的位置 this.transY.value = destination } if (gestureEvent.state === GestureState.END || gestureEvent.state === GestureState.CANCELLED) { if (this.transY.value <= screenHeight / 2) { // 在上面的位置 if (this.upward.value) { this.scrollTo(statusBarHeight) } else { this.scrollTo(screenHeight / 2) } } else if (this.transY.value > screenHeight / 2 && this.transY.value <= this.initTransY.value) { // 在中间位置的时候 if (this.upward.value) { this.scrollTo(screenHeight / 2) } else { this.scrollTo(this.initTransY.value) } } else { // 在最下面的位置 this.scrollTo(this.initTransY.value) } } }, 分段式页面代码片段:https://developers.weixin.qq.com/s/fw0U31mI7bGf 半屏的交互除了在页面内实现,也能跨页面实现,如常见的下沉式半屏交互。其中,半屏效果与上述实现类似,而前一页面的下沉实现需要结合自定义路由 后面的文章中我们会介绍自定义路由结合手势怎么去实现下沉式半屏效果,不仅如此,还有很多类原生的页面切换效果都能通过自定义路由实现 [图片]
2023-08-03 - 云托管Cos能否在腾讯云cos列表里展示,使得方便使用腾讯云的服务,比如缩略图功能?
云托管Cos能否在腾讯云cos列表里展示,使得方便使用腾讯云的服务,比如缩略图功能?
2024-03-21 - 01-21
- wxml2canvas-2d:简单易用的小程序海报、战绩等分享图片生成方案
Github 地址:https://github.com/ChrisChan13/wxml2canvas-2d 介绍 当前,众多小程序的多处场景都需要能够生成分享图便于用户进行二次传播,从而提升小程序的传播率以及加强品牌效应。 比较简单的分享图,如寥寥几行文字和一张小程序码,可以通过微信的 Canvas API 绘制。旧版 Canvas API 绘制过程繁琐,且每次绘制都需要调用 draw 方法,一不小心代码就写了上百行。 新版 Canvas API 基本与 Web Canvas 对齐,使得开发效率提高、性能得到优化。虽然免去了很多繁琐操作,但面对拥有元素众多、结构复杂的分享图片,依然解决不了代码冗长的问题。 目前开源的一些小程序图片生成方案,有的年久失修、有的依然使用旧版 Canvas API、有的使用方式不够简便,于是便有了开发 wxml2canvas-2d 的想法。 wxml2canvas-2d 的图片生成方式简单直观:首先在 wxml 页面上编写元素结构,其次在 wxss 中编写元素样式,最后调用 wxml2canvas-2d 的相关方法即可生成所需的分享图片。 wxml2canvas-2d 会通过 class 类名查询元素节点的 computedStyle 和节点属性,从而将元素节点绘制到画布上。这样做的好处是在编写 wxml 结构以及样式时,可以直观的看见样式的变化,方便调整。当然也有坏处,这个用来生成图片的“wxml 模板”,必须存在于页面上。若需要隐藏这个“模板”,只可用定位将其移至屏幕外,不可以使用 wx:if 或 hidden 隐藏。 wxml2canvas-2d 已经支持大部分常用的 CSS 属性,等你来测~ 示例 克隆此 Github 仓库,运行 [代码]npm i & npm run dev[代码],将 miniprogram_dev 文件夹导入微信开发者工具 效果预览 小程序内容: [图片] 生成的图片: [图片] 安装 npm 使用 npm 构建前,请先阅读微信官方的 npm 支持 [代码]# 通过 npm 安装 npm i wxml2canvas-2d -S --production [代码] 构建 npm 包 打开微信开发者工具,点击 工具 -> 构建 npm,并勾选 使用 npm 模块 选项,构建完成后,即可引入组件。 使用 在页面配置中引入 [代码]wxml2canvas-2d[代码] ; [代码]{ "usingComponents": { "wxml2canvas": "wxml2canvas-2d" } } [代码] 在页面中编写 wxml 结构,将要生成画布内容的根节点用名为 [代码]wxml2canvas-container[代码] 的样式类名称标记,将该根节点内部需要生成画布内容的节点用名为 [代码]wxml2canvas-item[代码] 的样式类名称标记(文字类节点需在对应节点声明 [代码]data-text[代码] 属性,并传入文字内容)。上述两个样式类名称可以自定义,只需将对应名称传入 [代码]wxml2canvas[代码] 组件的对应属性参数即可; [代码]<!-- pages/index/index.wxml --> <view class="wxml2canvas-container box"> <view class="wxml2canvas-item title" data-text="测试标题">测试标题</view> <image class="wxml2canvas-item image" src="/your-image-path.png" /> <view class="wxml2canvas-item content" data-text="测试内容,长文本。。">测试内容,长文本。。</view> </view> <button catchtap="generateSharingCard">生成画布内容</button> <wxml2canvas id="wxml2canvas" /> [代码] 补充各个节点样式; [代码]/* pages/index/index.wxss */ .box { /* 根节点(容器)的样式 */ } .title { /* 标题的样式 */ } .image { /* 图片的样式 */ } .content { /* 内容的样式 */ } [代码] 依据 wxml 结构以及 css 样式,生成画布内容,并将生成结果导出。 [代码]// pages/index/index.js Page({ async generateSharingCard() { const canvas = this.selectComponent('#wxml2canvas'); await canvas.draw(); const filePath = await canvas.toTempFilePath(); wx.previewImage({ urls: [filePath], }); }, }); [代码] 更多内容及文档 点击此处 前往查看!如果有好的建议或者想法,也欢迎提交 Issue 或 PR~
2024-11-21 - 微信云托管FAQ
Q1:云托管的时间相差8个小时? A:容器系统时间默认为 UTC 协调世界时间 (Universal Time Coordinated),与本地所属时区 CST (上海时间)相差 8 个小时: 在构建基础镜像或在基础镜像的基础上制作自定义镜像时,在 Dockerfile 中创建时区文件即可解决单一容器内时区不一致问题,且后续使用该镜像时,将不再受时区问题困扰。 1.打开 Dockerfile 文件。 2.写入以下内容,配置时区文件 FROM centos as centos COPY --from=centos /usr/share/zoneinfo/Asia/Shanghai /etc/localtime RUN echo "Asia/Shanghai" > /etc/timezone 3.重新构建容器镜像,使用新的镜像重新部署。或直接上传含新的 Dockerfile 的代码包重新部署。 Q2:云托管的作用是什么? A:代替服务器部署小程序/公众号后端。 Q3:云托管能托管后端服务么? A:云托管就是为开发者提供的云原生全托管的容器后端云服务,支持托管任意语言及框架的容器化应用。 Q4:腾讯云和微信云托管有关系吗?云开发的云托管和微信云托管有什么区别? A:微信云托管是整合了腾讯云底层资源和微信生态链路的综合解决方案。原云开发中的云托管独立出来,升级为微信云托管,补充数据库、ci/cd、灰度发布等更多完整后端功能和企业级devops能力。 Q5:无法登录云托管控制台? A:小程序测试号/试用号不支持,第三方快速创建的小程序需在小程序mp后台绑定手机号码后才可登录,快速创建的小程序可先前往“小程序助手”小程序设置登录邮箱和密码,设置完成后即可前往微信公众平台登录使用,具体可参考指引:https://kf.qq.com/touch/sappfaq/200617VbQzaa200617aq67ru.html Q6:部署失败,提示端口异常? A:端口异常有两种可能:1. 程序启动正常,但填错端口,导致无法连接;2. 端口没填错,但是程序启动异常,导致无法连接。 Q7:没有配置过扩缩容条件,为什么一直显示服务扩容中呢? A:系统默认配置cup使用率>=60%的扩缩容条件,扩容中是触发了扩缩容条件,如需调整可前往「控制台-服务管理-服务设置-基础设置」中进行更改。 Q8:云托管是否支持多个端口监听? A:目前仅支持设置一个监听端口。为了更优的体验和性能,一个服务只建议包含一个进程,专注完成一件事。如果是多个进程,就拆成多个服务,这样每个服务应该都只需要一个端口,这样就使得服务可以更聚焦,且独立根据用量扩缩容。 Q9:云托管的实例多久会释放? A:实例最小值设置为0才会缩容到0,30min内无请求就会缩容到0。 Q10:如何让服务常驻运行? A:将最小实例副本数设置为1即可,路径:服务管理-服务列表-服务设置-基础信息。 Q11:云托管可接入腾讯云的消息队列 RabbitMQ 版吗? A:可以的,保证在同一个vpc即可。 Q12:开放接口服务的本地调试需要cloudbase_access_token吗? A:本地调试请使用 VSCode 调试插件,会自动生成 cloudbase_access_token,详情。 Q13:小程序本地调试如何使用微信令牌? A:小程序可参考文档使用 VSCode 插件进行本地调试,可以使用微信令牌和免鉴权能力:开放接口服务 | 微信开放文档 Q14:云托管除了MySQL能否支持其他数据库? A:使用其他数据库可以自行到腾讯云上购买,搭配云托管使用,买在和云托管同一个VPC下即可。(云托管VPC路径:控制台-设置-环境设置-网络) Q15:云托管的数据库密码在哪里查看? A:微信云托管不会存储数据库密码,请妥善保管,如忘记重置密码即可。(如通过一键部署创建的数据库,账密也可前往微信服务通知查看) Q16:云托管怎么使用云开发的数据库? A:云托管不可以直接使用云开发数据库,建议使用云托管自带的MySQL数据库;如果一定要使用云开发数据库,只有node.js语言,才可以使用官方node-sdk调用;其他语言的其他类型数据库暂无SDK支持;其他在腾讯云的云数据库必须和云托管环境在同一VPC下才可以使用。(云托管VPC路径:控制台-设置-环境设置-网络) Q17:云托管的数据库为何会自动暂停? A:数据库默认10min内无读写自动暂停,如需取消暂停可前往数据库中关闭自动暂停。 Q18:云托管数据库的表结构是否会初始化? A:系统不会对数据库做任何操作。 Q19:云托管的数据库多久不用会被注销? A:不会主动注销,除非是用户自己主动注销,或者是账号欠费7天后停服。 Q20:callContainer 对于回包的大小有限制? A:返回包大小限制 1000k。 Q21:调用云托管服务失败,提示服务名不存在? A:服务名不存在,可能有如下几种情况: 服务名填写错误,环境中不存在此服务;环境id填写错误,填错的环境中不存在此服务;在没有和对应的云托管环境绑定的小程序中调用了服务。环境绑定小程序A但是在小程序B中调用;服务已被删除。 Q22:公众号的事件推送到云托管,为什么没有unionid? A:需要公众号绑定open账号,请求的headers会带上 x-wx-unionid。 Q23:配置云托管消息推送-path值怎样填? A:服务下哪个接口接收即写该接口在服务内的路径即可,比如根路径填写 /。 Q24:使用云托管,公众号怎么绑定服务配置?可以直接访问云托管给的外网网址来访问服务? A:不需要再填写服务器配置,可直接使用微信开放能力 -「开放接口服务」,另,公众号内更推荐用callcontainer调用而不是直接公网访问服务。 Q25:云托管是否是固定出口IP? A:不是,出口入口IP都不固定,固定IP后续作为付费能力开放。 Q26:云托管如果被DDoS会不会扣钱? A:这里会存在两种情况: 1.通过调用callcontainer+关闭公网访问的模式,可以彻底防止被DDoS; 2.如果用户自己开放了公网访问引来了DDoS,还是会产生费用的。 Q27:免费额度到期或者使用完了如何购买资源? A:免费额度到期或使用完后,将按实际用量计费,如需购买资源包,可前往「云托管控制台-资源监控-资源使用-资源包」选购合适的资源包。 Q28:资源包能否增加时长? A:时长不能叠加,只能叠加用量。 Q29:不小心开通了云托管环境,没有创建服务,会扣费么? A:不会。没有实例运行不会产生费用。
2022-02-08