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

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



前言

      在上一期的博客中与大家分享了微信小程序开发会议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. 点击多账号调试

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

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

目录
相关文章
|
9天前
|
存储 传感器 小程序
跳绳计数小程序开发系统
首先,我们需要明确跳绳计数小程序的核心功能:为用户提供跳绳计数的便捷方式。这意味着小程序需要能够准确地记录用户的跳绳次数,并为用户提供清晰、直观的计数展示。
|
9天前
|
人工智能 编解码 小程序
【一步步开发AI运动小程序】四、小程序如何抽帧
随着AI技术的发展,阿里体育等公司推出的“乐动力”、“天天跳绳”等APP使云上运动会、线上健身等概念备受关注。本文将引导您从零开始开发一个AI运动小程序,利用“云智AI运动识别小程序插件”。文中详细介绍了微信小程序抽帧的相关API、设置及注意事项,帮助开发者更好地实现AI运动功能。下篇将介绍人体识别技术,敬请期待。
|
10天前
|
传感器 存储 小程序
跳绳计数小程序开发
跳绳计数小程序旨在通过智能设备(如手机或智能手表)记录用户在跳绳过程中的次数、时间、速度等关键数据,为用户提供便捷的运动记录和数据分析服务
|
8天前
|
存储 小程序 安全
微信的开发管理都需要配置什么?
【10月更文挑战第17天】微信的开发管理都需要配置什么?
21 0
|
9天前
|
小程序 搜索推荐 前端开发
小剧场短剧影视小程序开发
小剧场短剧影视小程序旨在为用户提供一个便捷、互动的平台,让用户能够随时随地观看、分享和评论各类小剧场短剧。通过小程序,用户可以浏览热门短剧、搜索感兴趣的内容、参与社区互动,以及享受个性化的推荐服务。
|
10天前
|
小程序 搜索推荐 前端开发
短剧小程序开发案例
首先,明确你的短剧平台的目标用户群体和他们的需求。比如,年轻用户可能更倾向于轻松、幽默的短剧内容,而家庭用户则可能更偏爱教育、亲子类的短剧。了解用户需求有助于你设计更符合他们口味的功能和界面
|
10天前
|
机器学习/深度学习 人工智能 小程序
【一步步开发AI运动小程序】三、运动识别处理流程
随着人工智能技术的发展,阿里体育等公司推出的“乐动力”、“天天跳绳”等AI运动APP备受关注。本文将引导您从零开始开发一个AI运动小程序,使用“云智AI运动识别小程序插件”。文章介绍了视频帧、帧率FPS、抽帧和人体识别等基本概念,并详细说明了处理流程,包括抽帧、人体识别检测、骨骼图绘制和运动分析等步骤。下篇将介绍如何在小程序中实现抽帧。
|
13天前
|
JSON 小程序 JavaScript
uni-app开发微信小程序的报错[渲染层错误]排查及解决
uni-app开发微信小程序的报错[渲染层错误]排查及解决
193 7
|
13天前
|
小程序 JavaScript 前端开发
uni-app开发微信小程序:四大解决方案,轻松应对主包与vendor.js过大打包难题
uni-app开发微信小程序:四大解决方案,轻松应对主包与vendor.js过大打包难题
222 1
|
27天前
|
小程序 前端开发 测试技术
微信小程序的开发完整流程是什么?
微信小程序的开发完整流程是什么?
79 7

热门文章

最新文章