【微信小程序开发】之微信授权登陆

简介: 【微信小程序开发】之微信授权登陆



前言

      在上一期的博客中与大家分享了微信小程序开发会议OA项目首页数据的后台交互,以及在其中使用到工具文件来实现便捷的去调用请求访问后台的接口方法,还涉及了.wxs文件的介绍和使用。今天给大家带来的是微信小程序开发之微信授权登陆,让我们一起来了解了解吧。

一、微信授权登陆介绍

1. 基本概念

       微信授权登录是指用户通过微信账号登录第三方应用或网站的一种身份验证方式。当用户在第三方应用或网站上选择使用微信登录时,该应用或网站会跳转到微信登录页面,用户在此页面输入微信账号和密码进行登录。一旦用户成功登录并授权,第三方应用或网站就能够获得用户在微信上的基本信息,如昵称、头像等,并用于用户在应用或网站上的个性化服务。这种授权登录的方式方便用户快速登录第三方平台,同时减少了用户填写注册信息的麻烦。

2. 微信小程序开发实现微信授权登陆原理流程

实现微信授权登录的主要原理流程如下:

  1. 在小程序中创建一个“登录”按钮或其他触发授权登录的交互元素。
  2. 用户点击登录按钮后,小程序调用wx.login接口获取临时登录凭证code
  3. 小程序调用wx.getUserInfo接口获取用户的基本信息,例如昵称、头像等。
  4. 小程序将获取到的临时登录凭证code和用户基本信息发送给开发者自己的后台服务器。
  5. 开发者的后台服务器收到请求后,通过code调用微信的登录凭证校验接口,验证凭证的有效性,并获取到openidsession_key
  6. 开发者的后台服务器根据获取的openidsession_key生成一个自定义的用户标识 token,并返回给小程序。
  7. 小程序将自定义的用户标识 token 存储起来,作为用户登录的凭证。
  8. 用户在之后的小程序访问中,可以携带这个自定义的用户标识 token 请求开发者的后台服务器,进行用户身份的验证和数据操作。
  9. 开发者可以在后台服务器中根据用户的openid进行用户识别和管理,例如创建新用户、更新用户信息、绑定其他第三方账号等。
  10. 如果开发者需要获得用户的更多个人信息,例如手机号码、地理位置等,可以引导用户进行相应的授权,通过调用相应的接口获取这些信息。
  11. 如果小程序需要将用户的登录状态同步到其他终端(如PC端、移动App等),开发者可以通过在后台服务器中记录登录状态,实现多终端的登录一致性。
  12. 对于用户敏感操作或需要保护的数据,开发者可以在后台服务器进行权限验证,确保用户有足够的权限进行相应的操作。

注意:

       为了确保用户信息的安全性,开发者在传输用户信息时,应使用加密算法进行加密,以保护用户的隐私。此外,开发者的后台服务器需要与微信服务器进行密切的协作,以保证登录凭证和用户信息的有效性和安全性。

       为了确保用户信息的安全性,开发者在传输用户信息时,应使用加密算法进行加密,以保护用户的隐私。此外,开发者的后台服务器需要与微信服务器进行密切的协作,以保证登录凭证和用户信息的有效性和安全性。

3. 小程序中运用微信授权登陆的好处

使用微信授权登陆的优势有以下几点:

  1. 用户体验优化:相比传统的网页注册登录,微信授权登陆省去了繁琐的注册流程,用户只需授权手机用于绑定身份,即可完成登录,这大大提高了用户体验。
  2. 安全性:微信授权登陆采用了安全、可靠的授权机制,确保用户的个人信息和数据安全,同时避免了恶意注册和滥用账号的风险。
  3. 数据分析与优化:通过微信授权登陆,品牌企业可以获取到用户的手机信息,进而进行用户行为分析和数据挖掘,以便更好地了解用户需求,优化产品和服务。
  4. 提高安全性:微信授权登陆采用了安全、可靠的授权机制,确保用户的个人信息和数据安全,防止了恶意注册和滥用账号的风险。
  5. 简化开发流程:微信官方提供了丰富的API和开发工具,使得开发者可以轻松地实现微信授权登陆功能,简化了开发流程,提高了开发效率。

除此之外,还有跨平台整合、增加用户粘性、社交功能拓展、避免重复注册等等的好处。

二、微信授权登陆接口演示

       资源文件介绍

1. 在微信开发工具中导入小程序授权微信登陆前端项目

2. 微信授权登陆的方式

2.1 wx.login

       该方式演示的效果是(微信直接登陆2),效果如下

2.2 wx.getUserProfile

  该方式演示的效果是(微信直接登陆1),效果如下。将index.js文件中的canIUseGetUserProfile的属性值改为true。效果演示如下

2.3 二者的区别

  1. wx.login: 这个接口用于获取用户的登录凭证(code),开发者可以通过这个凭证向服务器换取用户的唯一标识和会话密钥等信息。一般情况下,账号登录或者授权登录都会使用wx.login接口获取用户的code。
  2. wx.getUserProfile: 这个接口用于获取用户的个人信息,包括昵称、头像等。在用户授权的情况下,开发者可以通过wx.getUserProfile获取用户的个人信息。

       因此,两者的区别在于功能和用途上。wx.login用于获取用户登录凭证,而wx.getUserProfile用于获取用户的个人信息。需要注意的是,为了保护用户隐私,获取用户个人信息需要用户授权,而且在小程序中需要提前向用户展示授权弹窗,让用户确认是否授权。

       但是微信官方推荐使用wx.getUserProfile的这种授权方式,这种方式安全性高。

三、 小程序微信授权登陆流程

1. 流程图

2. 代码调试

2.1 导入调试后端代码

       在我们的开发工具中导入资源中的ssm-oa项目文件

       我们还要看Maven文件是否一致,以及依赖下载网址是否是阿里云的下载路径

配置我们的数据库连接接口文件以及appID和app密钥,在application.yml中配置

2.2 导入调试前端代码

       导入我们前端的测试代码

2.3 效果演示

        我们授权登陆后会在数据库中显示增加一条关于该用户的数据

3. 代码

小程序前端代码
login.wxml
<!--pages/auth/login/login.wxml-->
<view class="container">
  <view class="login-box">
    <button wx:if="{{canIUseGetUserProfile}}" type="primary" class="wx-login-btn" bindtap="getUserProfile">微信直接登录</button>
    <button wx:else open-type="getUserInfo" type="primary" class="wx-login-btn" bindgetuserinfo="wxLogin">微信直接登录</button>
    <button type="primary" class="account-login-btn" bindtap="accountLogin">账号登录</button>
  </view>
</view>

       这编辑的是登陆功能的页面演示

login.js
// pages/auth/login/login.js
var util = require('../../../utils/util.js');
var user = require('../../../utils/user.js');
const app = getApp();
Page({
    /**
     * 页面的初始数据
     */
    data: {
        canIUseGetUserProfile: false, // 用于向前兼容
        lock:false
    },
    onLoad: function(options) {
        // 页面初始化 options为页面跳转所带来的参数
        // 页面渲染完成
        if (wx.getUserProfile) {
          this.setData({
            canIUseGetUserProfile: true
          })
        }
        //console.log('login.onLoad.canIUseGetUserProfile='+this.data.canIUseGetUserProfile)
    },
    /**
     * 生命周期函数--监听页面初次渲染完成
     */
    onReady() {
    },
    /**
     * 生命周期函数--监听页面显示
     */
    onShow() {
    },
    getUserProfile(e) {
        // 推荐使用wx.getUserProfile获取用户信息,开发者每次通过该接口获取用户个人信息均需用户确认
        // 开发者妥善保管用户快速填写的头像昵称,避免重复弹窗
        wx.getUserProfile({
            desc: '用于完善会员资料', // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写
            success: (res) => {
                //console.log(res);
                debugger
                user.checkLogin().catch(() => {
                    user.loginByWeixin(res.userInfo).then(res => {
                      app.globalData.hasLogin = true;
                      debugger
                      wx.navigateBack({
                        delta: 1
                      })
                    }).catch((err) => {
                      app.globalData.hasLogin = false;
                      if(err.errMsg=="request:fail timeout"){
                        util.showErrorToast('微信登录超时');
                      }else{
                        util.showErrorToast('微信登录失败');
                      }
                      this.setData({
                        lock:false
                      })
                    });
                  });
            },
            fail: (res) => {
                app.globalData.hasLogin = false;
                console.log(res);
                util.showErrorToast('微信登录失败');
            }
        });
    },
    wxLogin: function(e) {
        if (e.detail.userInfo == undefined) {
          app.globalData.hasLogin = false;
          util.showErrorToast('微信登录失败');
          return;
        }
        user.checkLogin().catch(() => {
            user.loginByWeixin(e.detail.userInfo).then(res => {
              app.globalData.hasLogin = true;
              wx.navigateBack({
                delta: 1
              })
            }).catch((err) => {
              app.globalData.hasLogin = false;
              if(err.errMsg=="request:fail timeout"){
                util.showErrorToast('微信登录超时');
              }else{
                util.showErrorToast('微信登录失败');
              }
            });
          });
    },
    accountLogin() {
        console.log('开发中....')
    }
})

       login.js文件中定义的是授权登陆所要涉及到的方法函数

后端代码
IpUtil.java
package com.zking.ssm.wxcontroller;
import java.net.InetAddress;
import java.net.UnknownHostException;
import javax.servlet.http.HttpServletRequest;
/**
 * IP地址相关工具类
 */
public class IpUtil {
  public static String client(HttpServletRequest request) {
    String xff = request.getHeader("x-forwarded-for");
    if (xff == null) {
      xff = request.getRemoteAddr();
    }
    return xff;
  }
  public static String getIpAddr(HttpServletRequest request) {
    String ipAddress = null;
    try {
      ipAddress = request.getHeader("x-forwarded-for");
      if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
        ipAddress = request.getHeader("Proxy-Client-IP");
      }
      if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
        ipAddress = request.getHeader("WL-Proxy-Client-IP");
      }
      if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
        ipAddress = request.getRemoteAddr();
        if (ipAddress.equals("127.0.0.1")) {
          // 根据网卡取本机配置的IP
          InetAddress inet = null;
          try {
            inet = InetAddress.getLocalHost();
          } catch (UnknownHostException e) {
            e.printStackTrace();
          }
          ipAddress = inet.getHostAddress();
        }
      }
      // 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
      if (ipAddress != null && ipAddress.length() > 15) { // "***.***.***.***".length()
        // = 15
        if (ipAddress.indexOf(",") > 0) {
          ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
        }
      }
    } catch (Exception e) {
      ipAddress = "";
    }
    // ipAddress = this.getRequest().getRemoteAddr();
    return ipAddress;
  }
}
WxAuthController.java
package com.zking.ssm.wxcontroller;
/**
 * @Autho donkee
 * @Since 2022/6/27
 */
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
import com.alibaba.fastjson.JSONObject;
import com.zking.ssm.annotation.LoginUser;
import com.zking.ssm.model.UserInfo;
import com.zking.ssm.model.WxLoginInfo;
import com.zking.ssm.model.WxUser;
import com.zking.ssm.service.UserToken;
import com.zking.ssm.service.UserTokenManager;
import com.zking.ssm.service.WxUserService;
import com.zking.ssm.util.JacksonUtil;
import com.zking.ssm.util.ResponseUtil;
import com.zking.ssm.util.UserTypeEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import javax.servlet.http.HttpServletRequest;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
 * 鉴权服务
 */
@Slf4j
@RestController
@RequestMapping("/wx/auth")
public class WxAuthController {
    @Autowired
    private WxMaService wxService;
    @Autowired
    private WxUserService userService;
    /**
     * 微信登录
     *
     * @param wxLoginInfo
     *            请求内容,{ code: xxx, userInfo: xxx }
     * @param request
     *            请求对象
     * @return 登录结果
     */
    @PostMapping("login_by_weixin")
    public Object loginByWeixin(@RequestBody WxLoginInfo wxLoginInfo, HttpServletRequest request) {
        //客户端需携带code与userInfo信息
        String code = wxLoginInfo.getCode();
        UserInfo userInfo = wxLoginInfo.getUserInfo();
        if (code == null || userInfo == null) {
            return ResponseUtil.badArgument();
        }
        //调用微信sdk获取openId及sessionKey
        String sessionKey = null;
        String openId = null;
        try {
            long beginTime = System.currentTimeMillis();
            //
            WxMaJscode2SessionResult result = this.wxService.getUserService().getSessionInfo(code);
//            Thread.sleep(6000);
            long endTime = System.currentTimeMillis();
            log.info("响应时间:{}",(endTime-beginTime));
            sessionKey = result.getSessionKey();//session id
            openId = result.getOpenid();//用户唯一标识 OpenID
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (sessionKey == null || openId == null) {
            log.error("微信登录,调用官方接口失败:{}", code);
            return ResponseUtil.fail();
        }else{
            log.info("openId={},sessionKey={}",openId,sessionKey);
        }
        //根据openId查询wx_user表
        //如果不存在,初始化wx_user,并保存到数据库中
        //如果存在,更新最后登录时间
        WxUser user = userService.queryByOid(openId);
        if (user == null) {
            user = new WxUser();
            user.setUsername(openId);
            user.setPassword(openId);
            user.setWeixinOpenid(openId);
            user.setAvatar(userInfo.getAvatarUrl());
            user.setNickname(userInfo.getNickName());
            user.setGender(userInfo.getGender());
            user.setUserLevel((byte) 0);
            user.setStatus((byte) 0);
            user.setLastLoginTime(new Date());
            user.setLastLoginIp(IpUtil.client(request));
            user.setShareUserId(1);
            userService.add(user);
        } else {
            user.setLastLoginTime(new Date());
            user.setLastLoginIp(IpUtil.client(request));
            if (userService.updateById(user) == 0) {
                log.error("修改失败:{}", user);
                return ResponseUtil.updatedDataFailed();
            }
        }
        // token
        UserToken userToken = null;
        try {
            userToken = UserTokenManager.generateToken(user.getId());
        } catch (Exception e) {
            log.error("微信登录失败,生成token失败:{}", user.getId());
            e.printStackTrace();
            return ResponseUtil.fail();
        }
        userToken.setSessionKey(sessionKey);
        log.info("SessionKey={}",UserTokenManager.getSessionKey(user.getId()));
        Map<Object, Object> result = new HashMap<Object, Object>();
        result.put("token", userToken.getToken());
        result.put("tokenExpire", userToken.getExpireTime().toString());
        userInfo.setUserId(user.getId());
        if (!StringUtils.isEmpty(user.getMobile())) {// 手机号存在则设置
            userInfo.setPhone(user.getMobile());
        }
        try {
            DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
            String registerDate = df.format(user.getAddTime() != null ? user.getAddTime() : new Date());
            userInfo.setRegisterDate(registerDate);
            userInfo.setStatus(user.getStatus());
            userInfo.setUserLevel(user.getUserLevel());// 用户层级
            userInfo.setUserLevelDesc(UserTypeEnum.getInstance(user.getUserLevel()).getDesc());// 用户层级描述
        } catch (Exception e) {
            log.error("微信登录:设置用户指定信息出错:"+e.getMessage());
            e.printStackTrace();
        }
        result.put("userInfo", userInfo);
        log.info("【请求结束】微信登录,响应结果:{}", JSONObject.toJSONString(result));
        return ResponseUtil.ok(result);
    }
    /**
     * 绑定手机号码
     *
     * @param userId
     * @param body
     * @return
     */
    @PostMapping("bindPhone")
    public Object bindPhone(@LoginUser Integer userId, @RequestBody String body) {
        log.info("【请求开始】绑定手机号码,请求参数,body:{}", body);
        String sessionKey = UserTokenManager.getSessionKey(userId);
        String encryptedData = JacksonUtil.parseString(body, "encryptedData");
        String iv = JacksonUtil.parseString(body, "iv");
        WxMaPhoneNumberInfo phoneNumberInfo = null;
        try {
            phoneNumberInfo = this.wxService.getUserService().getPhoneNoInfo(sessionKey, encryptedData, iv);
        } catch (Exception e) {
            log.error("绑定手机号码失败,获取微信绑定的手机号码出错:{}", body);
            e.printStackTrace();
            return ResponseUtil.fail();
        }
        String phone = phoneNumberInfo.getPhoneNumber();
        WxUser user = userService.selectByPrimaryKey(userId);
        user.setMobile(phone);
        if (userService.updateById(user) == 0) {
            log.error("绑定手机号码,更新用户信息出错,id:{}", user.getId());
            return ResponseUtil.updatedDataFailed();
        }
        Map<Object, Object> data = new HashMap<Object, Object>();
        data.put("phone", phone);
        log.info("【请求结束】绑定手机号码,响应结果:{}", JSONObject.toJSONString(data));
        return ResponseUtil.ok(data);
    }
    /**
     * 注销登录
     */
    @PostMapping("logout")
    public Object logout(@LoginUser Integer userId) {
        log.info("【请求开始】注销登录,请求参数,userId:{}", userId);
        if (userId == null) {
            return ResponseUtil.unlogin();
        }
        try {
            UserTokenManager.removeToken(userId);
        } catch (Exception e) {
            log.error("注销登录出错:userId:{}", userId);
            e.printStackTrace();
            return ResponseUtil.fail();
        }
        log.info("【请求结束】注销登录成功!");
        return ResponseUtil.ok();
    }
}
WxHomeController.java
package com.zking.ssm.wxcontroller;
import com.zking.ssm.mapper.InfoMapper;
import com.zking.ssm.model.Info;
import com.zking.ssm.util.ResponseUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
 * @Autho donkee
 * @Since 2022/6/29
 */
@RestController
@RequestMapping("/wx/home")
public class WxHomeController {
    @Autowired
    private InfoMapper infoMapper;
    @RequestMapping("/index")
    public Object index(Info info) {
        List<Info> infoList = infoMapper.list(info);
        Map<Object, Object> data = new HashMap<Object, Object>();
        data.put("infoList",infoList);
        return ResponseUtil.ok(data);
    }
}
WxInfoController.java
package com.zking.ssm.wxcontroller;
import com.zking.ssm.mapper.InfoMapper;
import com.zking.ssm.model.Info;
import com.zking.ssm.util.ResponseUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
 * @Autho donkee
 * @Since 2022/7/29
 */
@SuppressWarnings("all")
@RestController
@RequestMapping("/wx/info")
public class WxInfoController {
    @Autowired
    private InfoMapper infoMapper;
    @RequestMapping("/list")
    public Object list (Info info){
        List<Info> list = infoMapper.list(info);
        Map<Object, Object> data = new HashMap<Object, Object>();
        data.put("infoList",list);
        return ResponseUtil.ok(data);
    }
}
WxUserController.java
package com.zking.ssm.wxcontroller;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import com.zking.ssm.annotation.LoginUser;
import com.zking.ssm.model.WxUser;
import com.zking.ssm.service.UserTokenManager;
import com.zking.ssm.service.WxUserService;
import com.zking.ssm.util.ResponseUtil;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import com.alibaba.fastjson.JSONObject;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
/**
 * 用户服务
 */
@Slf4j
@RestController
@RequestMapping("/wx/user")
@Validated
public class WxUserController {
  @Autowired
  private WxUserService userService;
  /**
   * 用户个人页面数据
   * <p>
   * @param userId
   *            用户ID
   * @return 用户个人页面数据
   */
  @GetMapping("index")
  public Object list(@LoginUser Integer userId, @RequestHeader("X-OA-token") String token) {
    log.info("【请求开始】用户个人页面数据,请求参数,userId:{}", userId);
    log.info("【请求开始】用户个人页面数据,请求参数,token:{}", token);
    if (userId == null) {
      log.error("用户个人页面数据查询失败:用户未登录!!!");
      return ResponseUtil.unlogin();
    }
    WxUser wxUser = userService.selectByPrimaryKey(userId);
    Map<Object, Object> data = new HashMap<Object, Object>();
    data.put("metting_pubs", wxUser.getUserLevel());
    data.put("metting_joins",wxUser.getUserLevel());
    return ResponseUtil.ok(data);
  }
}

       这就是微信授权登陆小程序所涉及的后台代码。

四、解决微信名称含特殊标签符号的存储问题

       我们的一些微信名称有时会包含一些特殊符号的名称,当我们存储到数据库中时会出现异常,接下来就来解决一下这个问题。

1. 配置my.ini文件

       找到我们安装mysql的数据库下的my.ini文件,在文件中修改default-character-set和character-set-server的属性值修改为utf8mb4

2. 重启我们的数据库服务

       在我们的服务中找到mysql服务重新启动即可。

3. 点击多账号调试

       由此我们可以看到在数据库中我们成功将特殊字符存储到数据库中了。

       本期的博客分享到此结束,谢谢到家

目录
相关文章
|
12天前
|
人工智能 小程序
【一步步开发AI运动小程序】十五、AI运动识别中,如何判断人体站位的远近?
【云智AI运动识别小程序插件】提供人体、运动及姿态检测的AI能力,无需后台支持,具有快速、体验好、易集成等特点。本文介绍如何利用插件判断人体与摄像头的远近,确保人体图像在帧内的比例适中,以优化识别效果。通过`whole`检测规则,分别实现人体过近和过远的判断,并给出相应示例代码。
|
6天前
|
人工智能 小程序 API
【一步步开发AI运动小程序】十七、如何识别用户上传视频中的人体、运动、动作、姿态?
【云智AI运动识别小程序插件】提供人体、运动、姿态检测的AI能力,支持本地原生识别,无需后台服务,具有速度快、体验好、易集成等优点。本文介绍如何使用该插件实现用户上传视频的运动识别,包括视频解码抽帧和人体识别的实现方法。
|
11天前
|
人工智能 小程序 UED
【一步步开发AI运动小程序】十六、AI运动识别中,如何判断人体站位?
【云智AI运动识别小程序插件】提供人体、运动及姿态检测的AI能力,本地引擎无需后台支持,具备快速、体验好、易集成等优势。本文介绍如何利用插件的`camera-view`功能,通过检测人体站位视角(前、后、左、右),确保运动时的最佳识别率和用户体验。代码示例展示了如何实现视角检查,确保用户正或背对摄像头,为后续运动检测打下基础。
|
11天前
|
移动开发 小程序 PHP
校园圈子论坛系统采取的PHP语音和uni账号开发的小程序APP公众号H5是否只需要4800元?是的,就是只需要4800元
关于校园圈子论坛系统采用PHP语言和uni-app开发的小程序、APP、公众号和H5是否仅需4800元这个问题,实际上很难给出一个确定的答案。这个价格可能受到多种因素的影响
47 8
|
7天前
|
人工智能 小程序 数据处理
uni-app开发AI康复锻炼小程序,帮助肢体受伤患者康复!
近期,多家康复机构咨询AI运动识别插件是否适用于肢力运动受限患者的康复锻炼。本文介绍该插件在康复锻炼中的应用场景,包括康复运动指导、运动记录、恢复程度记录及过程监测。插件集成了人体检测、姿态识别等功能,支持微信小程序平台,使用便捷,安全可靠,帮助康复治疗更加高效精准。
|
2月前
|
JSON 小程序 JavaScript
uni-app开发微信小程序的报错[渲染层错误]排查及解决
uni-app开发微信小程序的报错[渲染层错误]排查及解决
695 7
|
2月前
|
小程序 JavaScript 前端开发
uni-app开发微信小程序:四大解决方案,轻松应对主包与vendor.js过大打包难题
uni-app开发微信小程序:四大解决方案,轻松应对主包与vendor.js过大打包难题
749 1
|
2月前
|
小程序 前端开发 测试技术
微信小程序的开发完整流程是什么?
微信小程序的开发完整流程是什么?
146 7
ly~
|
3月前
|
存储 供应链 小程序
除了微信小程序,PHP 还可以用于开发哪些类型的小程序?
除了微信小程序,PHP 还可用于开发多种类型的小程序,包括支付宝小程序、百度智能小程序、抖音小程序、企业内部小程序及行业特定小程序。在电商、生活服务、资讯、工具、娱乐、营销等领域,PHP 能有效管理商品信息、订单处理、支付接口、内容抓取、复杂计算、游戏数据、活动规则等多种业务。同时,在企业内部,PHP 可提升工作效率,实现审批流程、文件共享、生产计划等功能;在医疗和教育等行业,PHP 能管理患者信息、在线问诊、课程资源、成绩查询等重要数据。
ly~
86 6
|
2月前
|
缓存 小程序 索引
uni-app开发微信小程序时vant组件van-tabs的使用陷阱及解决方案
uni-app开发微信小程序时vant组件van-tabs的使用陷阱及解决方案
250 1