微信公众号开发——实现用户微信网页授权流程

news2024/11/25 9:33:16
😊 @ 作者: 一恍过去
💖 @ 主页: https://blog.csdn.net/zhuocailing3390
🎊 @ 社区: Java技术栈交流
🎉 @ 主题: 微信公众号开发——实现用户微信网页授权流程
⏱️ @ 创作时间: 2022年12月16日

目录

  • 准备工作
  • 授权说明
  • yaml配置
  • 接口常量定义
  • 定义工具类
  • 用户授权获取Code
  • 通过Code 换取授权access_token及用户信息
  • 获取用户信息
  • 流程测试

准备工作

1、在本地进行联调时,为让微信端能够访问到本地服务,需要进行内网穿透,参考《本地服务器内网穿透实现(NATAPP)》
2、配置网页授权获取用户基本信息,用于告诉微信发起授权的后端服务器地址

  • 正式公众号:在微信公众号请求用户网页授权之前,开发者需要先到公众平台官网中的“开发 - 接口权限 - 网页服务 - 网页帐号 - 网页授权获取用户基本信息”进行配置操作;
  • 测试沙箱环境:在 测试环境 中,进行配置网页授权
    在这里插入图片描述
    在这里插入图片描述

授权说明

微信授权时,分为snsapi_basesnsapi_userinfo两种授权方式

  • snsapi_base: 用来获取进入页面的用户的 openid 的,并且是静默授权并自动跳转到回调页的。用户感知的就是直接进入了回调页(往往是业务页面,不会有对于的认为操作);
  • snsapi_userinfo: 是用来获取用户的基本信息的。但这种授权需要用户手动同意,并且由于用户同意过,所以无须关注,就可在授权后获取该用户的基本信息。

网页授权流程最主要分三步:
1、引导用户进入授权页面同意授权,获取code
2、通过 code 换取网页授权access_token(与基础支持中的access_token不同)和openid
3、通过网页授权access_token和 openid 获取用户基本信息(支持 UnionID 机制)

后端接口流程说明:
当前Demo,后端只会对前端暴漏一个接口,微信授权回调地址直接重定向到后端地址,由后端进行后续授权以及获取用户信息操作;
暴漏给前端的接口只需要传入两个参数,分别为:socpe(授权类型)、baseUrl(授权成功后重定向到前端的页面地址)

比如:

1、前端调用`wechant/code`接口,传入`socpe=snsapi_userinfo、baseUrl=http://lhz.com/h5`
2、微信回调的授权地址,直接为后端地址
3、获取code、通过code获取access_token的流程由后端完成
4、后端获取到openid信息后,重定向到`http://lhz.com/h5`,比如`return "redirect:" + baseUrl + "?openid=" + openid;`

yaml配置

wx:
  # 来源于测试平台
  appid: wx79ec4331f29311b9
  secret: 1c79a199560f94096f26b8caa2a73a08
  apiUrl: https://api.weixin.qq.com/
  openApiUrl: https://open.weixin.qq.com/
  authRedirectUri: http://6uks3d.natappfree.cc/wechat/auth

接口常量定义

InterfaceConstant:

public interface InterfaceConstant {
    /**
     * 用户同意授权,获取code
     */
    String OAUTH2_AUTHORIZE = "connect/oauth2/authorize";

    /**
     * 通过 code 换取网页授权access_token
     */
    String OAUTH2_ACCESS_TOKEN = "sns/oauth2/access_token";

    /**
     * 获取用户信息
     */
    String OAUTH2_USERINFO = "sns/userinfo";
}

定义工具类

MapUtils:

public class MapUtils {

    /**
     * Map转换为 Entity
     *
     * @param params 包含参数的Map
     * @param t      需要赋值的实体
     * @param <T>    类型
     */
    public static <T> T mapToEntity(Map<String, Object> params, T t) {
        if (null == params) {
            return t;
        }
        Class<?> clazz = t.getClass();
        Field[] declaredFields = clazz.getDeclaredFields();
        try {
            for (Field declaredField : declaredFields) {
                declaredField.setAccessible(true);
                String name = declaredField.getName();
                if (null != params.get(name)) {
                    declaredField.set(t, params.get(name));
                }
            }
        } catch (Exception e) {
            throw new RuntimeException("属性设置失败!");
        }
        return t;
    }

    /**
     * 将对象转换为HashMap
     *
     * @param t   转换为Map的对象
     * @param <T> 转换为Map的类
     * @return Map
     */
    public static <T> Map<String, Object> entityToMap(T t) {
        Class<?> clazz = t.getClass();
        List<Field> allField = getAllField(clazz);
        Map<String, Object> hashMap = new LinkedHashMap<>(allField.size());
        try {
            for (Field declaredField : allField) {
                declaredField.setAccessible(true);
                Object o = declaredField.get(t);
                if (null != o) {
                    hashMap.put(declaredField.getName(), o);
                }
            }
        } catch (Exception e) {
            throw new RuntimeException("属性获取失败!");
        }
        return hashMap;
    }

    /**
     * 获取所有属性
     *
     * @param clazz class
     * @param <T>   泛型
     * @return List<Field>
     */
    public static <T> List<Field> getAllField(Class<T> clazz) {
        List<Field> fields = new ArrayList<>();
        Class<?> superClazz = clazz;
        while (null != superClazz) {
            fields.addAll(Arrays.asList(superClazz.getDeclaredFields()));
            superClazz = superClazz.getSuperclass();
        }
        return fields;
    }

    /**
     * 将Map参数转换为字符串
     *
     * @param map
     * @return
     */
    public static String mapToString(Map<String, Object> map) {
        StringBuffer sb = new StringBuffer();
        map.forEach((key, value) -> {
            sb.append(key).append("=").append(value.toString()).append("&");
        });
        String str = sb.toString();
        str = str.substring(0, str.length() - 1);
        return str;
    }

    /**
     * 将Bean对象转换Url请求的字符串
     *
     * @param t
     * @param <T>
     * @return
     */
    public static <T> String getUrlByBean(T t) {
        String pre = "?";
        Map<String, Object> map = entityToMap(t);
        return pre + mapToString(map);
    }

}

用户授权获取Code

接口说明:

由于授权操作安全等级较高,所以在发起授权请求时,微信会对授权链接做正则强匹配校验,如果链接的参数顺序不对,授权页面将无法正常访问;链接属性如下:https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect

重定向说明:

通过微信接口connect/oauth2/authorize获取授权code后,微信将进行一次重定向回调,回调地址就是请求设置的redirect_uri值;

需要注意的是,微信重定向回调地址,一定要在微信公众号平台进行配置

定义请求实体类 Oauth2AuthorizeRep:

@Data
public class Oauth2AuthorizeRep {
    /**
     * 公众号的唯一标识
     */
    private String appid;

    /**
     * 授权后重定向的回调链接地址, 请使用 urlEncode 对链接进行处理
     */
    private String redirect_uri;

    /**
     * 返回类型,请填写code
     */
    private String response_type = "code";

    /**
     * 应用授权作用域,snsapi_base (不弹出授权页面,直接跳转,只能获取用户openid)
     * snsapi_userinfo (弹出授权页面,可通过 openid 拿到昵称、性别、所在地。并且, 即使在未关注的情况下,只要用户授权,也能获取其信息 )
     */
    private String scope;

    /**
     * 重定向后会带上 state 参数,开发者可以填写a-zA-Z0-9的参数值,最多128字节
     */
    private String state;
}

Controller层示例:

@Slf4j
@Api(tags = "公众号/订阅号开发")
@Controller
public class WeChantController {
    /**
     * 由后端来进行授权操作(需要在微信页面打开)
     *
     * @param baseUrl 前端页面地址 用于授权完成后,后端重定向到前端页面
     * @param scope   应用授权作用域,此处为了模拟两种情况,进行传值:
     *                snsapi_base (不弹出授权页面,直接跳转,只能获取用户openid)
     *                snsapi_userinfo (弹出授权页面,可通过 openid 拿到昵称、性别、所在地。 即使在未关注的情况下,只要用户授权,也能获取其信息 )
     * @return
     */
    @GetMapping(value = "/code")
    @ApiOperation(value = "用户请求进行授权及获取信息", notes = "用户请求进行授权及获取信息")
    public String code(@RequestParam("baseUrl") String baseUrl, String scope) throws UnsupportedEncodingException {
        log.info("------ 用户请求进行授权及获取信息 ------");
        //通过code获取用户其信息
        String url = weChantService.getAuthCode(baseUrl, scope);
        return "redirect:" + url;
    }
}

Service层示例:

@Service
@Slf4j
public class WeChantService {
    /**
     * 获取用户授权码
     *
     * @param baseUrl
     * @param scope
     * @return
     */
    public String getAuthCode(String baseUrl, String scope) throws UnsupportedEncodingException {
        String appId = wxBean.getAppid();
        // 设置回调地址 http://6uks3d.natappfree.cc/wechat/auth,该地址为后端地址
        String redirectUri = wxBean.getAuthRedirectUri();
        // urlEncode处理
        redirectUri = URLEncoder.encode(redirectUri, "utf-8");
        // 组装url,在url中让state属性存方baseUrl的值
        String url = wxBean.getOpenApiUrl() + InterfaceConstant.OAUTH2_AUTHORIZE;
        // 封装url请求参数
        Oauth2AuthorizeRep rep = new Oauth2AuthorizeRep();
        rep.setAppid(appId);
        rep.setRedirect_uri(redirectUri);
        rep.setScope(scope);
        // 设置回调参数,需要进行urlEncode处理
        Map<String, String> stateMap = new HashMap<>(4);
        stateMap.put("baseUrl", baseUrl);
        stateMap.put("scope", scope);
        String stateMapStr = JSON.toJSONString(stateMap);
        stateMapStr = new String(Base64.getEncoder().encode(stateMapStr.getBytes(StandardCharsets.UTF_8)));
        rep.setState(stateMapStr);
        // 参数的顺序必须是:appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
        url = url + MapUtils.getUrlByBean(rep) + "#wechat_redirect";

        // 重定向url,微信会自动访问redirectUri,进行回调
        return url;
    }
}

请求示例:
需要在微信中打开以下链接(实际情况是前端在微信环境中进行调用),这里进行Demo演示,手动在微信中进行访问:http://6uks3d.natappfree.cc/wechat/code?baseUrl=https://www.baidu.com&scope=snsapi_base
其中http://6uks3d.natappfree.cc为通过NatApp配置的内网穿透域名,保证微信功能访问到本地项目;

通过Code 换取授权access_token及用户信息

接口说明:

由于公众号的 secret 和获取到的access_token安全级别都非常高,必须只保存在服务器,不允许传给客户端。
该接口就是用户授权获取Code中定义的redirectUri值,微信重定向时会进行调用

需要注意的是,微信重定向回调地址,一定要在微信公众号平台进行配置

定义请求实体类 Oauth2AccessTokenRep:

@Data
public class Oauth2AccessTokenRep {
    /**
     * 公众号的唯一标识
     */
    private String appid;

    /**
     * 公众号的appsecret
     */
    private String secret;

    /**
     * 填写第一步获取的 code 参数
     */
    private String code;

    /**
     * 填写为authorization_code
     */
    private String grant_type = "authorization_code";
}

定义响应实体类 Oauth2AccessTokenRes:

@Data
public class Oauth2AccessTokenRes {
    /**
     * 网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同
     */
    private String access_token;

    /**
     * access_token接口调用凭证超时时间,单位(秒)
     */
    private Integer expires_in;

    /**
     * 用户刷新access_token
     */
    private String refresh_token;
    /**
     * 用户唯一标识,请注意,在未关注公众号时,用户访问公众号的网页,也会产生一个用户和公众号唯一的OpenID
     */
    private String openid;

    /**
     * 用户授权的作用域,使用逗号(,)分隔
     */
    private String scope;

    /**
     * 是否为快照页模式虚拟账号,只有当用户是快照页模式虚拟账号时返回,值为1
     */
    private String is_snapshotuser;

    /**
     * 用户统一标识(针对一个微信开放平台帐号下的应用,同一用户的 unionid 是唯一的),只有当 scope 为"snsapi_userinfo"时返回
     * 并且公众号与微信开放平台进行了绑定才会返回
     */
    private String unionid;
}

Controller层示例:

该接口接收微信的授权回调,获取code及state值,并且在完成授权后,重定向到指定的前端页面

@Slf4j
@Controller
public class WeChantController {
    /**
     * @param code
     * @param state 存放的前端页面地址,授权后回调用
     * @return
     */
    @GetMapping(value = "/auth")
    @ApiOperation(value = "前端根据code获取信息", notes = "前端根据code获取信息")
    public String auth(@RequestParam(value = "code", required = false) String code, @RequestParam(value = "state", required = false) String state) throws UnsupportedEncodingException {
        log.info("------ 回显Code:{} ------", code);
        // 解析回传的 state值
        state = new String(Base64.getDecoder().decode(state.getBytes(StandardCharsets.UTF_8)));
        Map map = JSON.parseObject(state, Map.class);
        String baseUrl = map.get("baseUrl").toString();
        String scope = map.get("scope").toString();

        // 通过code获取用户openid
        String openid = weChantService.getUserAuth(code, scope);

        // 直接跳转到前端地址
        return "redirect:" + baseUrl + "?openid=" + openid;
    }
}

Service层示例:

@Service
@Slf4j
public class WeChantService {
    /**
     * 用户授权,并且返回openid返回给前端
     *
     * @param code
     * @param scope
     * @return
     */
    public String getUserAuth(String code, String scope) {

        String appId = wxBean.getAppid();
        String secret = wxBean.getSecret();
        String url = wxBean.getApiUrl() + InterfaceConstant.OAUTH2_ACCESS_TOKEN;
        // 封装url请求参数
        Oauth2AccessTokenRep rep = new Oauth2AccessTokenRep();
        rep.setAppid(appId);
        rep.setSecret(secret);
        rep.setCode(code);
        url = url + MapUtils.getUrlByBean(rep);
        Map map = restHttpRequest.doHttp(url, HttpMethod.GET, null);
        if (map == null) {
            throw new RuntimeException("授权失败!");
        }
        Oauth2AccessTokenRes res = new Oauth2AccessTokenRes();
        MapUtils.mapToEntity(map, res);
        log.info("Oauth2AccessTokenRes:" + JSON.toJSONString(res));

        // 获取access_token过期时间
        long expiresToken = res.getExpires_in() - 100;
        String access_token = res.getAccess_token();
        String openid = res.getOpenid();

        log.info("------ access_token:{} ------", access_token);
        log.info("------ openid:{} ------", openid);

        // 根据openid和access_token获取用户信息,如果"snsapi_userinfo"授权方式,再调用接口获取用户信息
        if (scope.equals(SNS_API_USERINFO)) {
            getAndInsertUserInfo(openid, access_token);
        }
        return openid;
    }
}

获取用户信息

定义请求实体类 Oauth2UserInfoRep:

@Data
public class Oauth2UserInfoRep {
    /**
     * 网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同
     */
    private String access_token;

    /**
     * 用户的唯一标识
     */
    private String openid;

    /**
     * 返回国家地区语言版本,zh_CN 简体,zh_TW 繁体,en 英语
     */
    private String lang = "zh_CN";

}

定义响应实体类 Oauth2UserInfoRes:

@Data
public class Oauth2UserInfoRes {
    /**
     * 用户昵称
     */
    private String nickname;

    /**
     * 用户的唯一标识
     */
    private String openid;

    /**
     * 用户的性别,值为1时是男性,值为2时是女性,值为0时是未知
     */
    private Integer sex;

    /**
     * 用户个人资料填写的省份
     */
    private String province;

    /**
     * 普通用户个人资料填写的城市
     */
    private String city;

    /**
     * 国家,如中国为CN
     */
    private String country;

    /**
     * 用户头像,最后一个数值代表正方形头像大小(有0、46、64、96、132数值可选,0代表640*640正方形头像),
     * 用户没有头像时该项为空。若用户更换头像,原有头像 URL 将失效。
     */
    private String headimgurl;

    /**
     * 用户特权信息,json 数组,如微信沃卡用户为(chinaunicom)
     */
    private List<String> privilege;

    /**
     * 只有在用户将公众号绑定到微信开放平台帐号后,才会出现该字段。
     */
    private String unionid;
}

Service层示例:

该接口在后端通过code获取accessTokenopenid后,直接调用该方法;

@Service
@Slf4j
public class WeChantService {
    private void getAndInsertUserInfo(String openid, String accessToken) {
        // 获取用户信息
        String url = wxBean.getApiUrl() + InterfaceConstant.OAUTH2_USERINFO;
        Oauth2UserInfoRep rep = new Oauth2UserInfoRep();
        rep.setAccess_token(accessToken);
        rep.setOpenid(openid);
        url = url + MapUtils.getUrlByBean(rep);
        Map userMap = restHttpRequest.doHttp(url, HttpMethod.GET, null);
        Oauth2UserInfoRes res = new Oauth2UserInfoRes();
        MapUtils.mapToEntity(userMap, res);
        // 打印信息
        log.info("UserInfo:" + JSON.toJSONString(res));
    }
}

流程测试

注意:由于使用了NatApp进行免费的内网穿透,可以会出现域名变更情况,如果域名发生了变更需要重新在公众号平台配置网页授权域名。

1、微信中访问后端接口,地址:http://6uks3d.natappfree.cc/wechat/code?baseUrl=https://www.baidu.com&scope=snsapi_userinfo

2、弹出授权页面如下
在这里插入图片描述
3、同意授权后,重定向到指定的页面(测试时使用了百度页面)
在这里插入图片描述
4、查看百度页面的链接信息,发现url后面带上了openid,就表示授权及重定向成功,比如:https://www.baidu.com/?openid=oTnaY6332ssfv4WiQBU0dES-WxJg

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/92910.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

阿里云效产品【代码管理Codeup】企业项目代码管理

文章目录前言一、Codeup是什么二、使用步骤1.首先登录阿里云2.进入云效3.进入云效4.代码分组5.新建代码库三、SSH 密钥总结前言 阿里云Code&#xff08;新版&#xff1a;代码托管Codeup&#xff09;阿里云代码管理 Codeup是基于 Git 的代码管理平台&#xff0c;10万企业正在使…

【头歌C语言程序与设计】顺序结构程序设计

目录 写在前面 正文 第1关&#xff1a;加法运算 第2关&#xff1a;不使用第3个变量&#xff0c;实现两个数的对调 第3关&#xff1a;用宏定义常量 第4关&#xff1a;数字分离 第5关&#xff1a;计算总成绩和平均成绩 第6关&#xff1a;求三角形的面积 第7关&#xff1…

黑客入门指南,学习黑客必须掌握的技术

黑客一词&#xff0c;原指热心于计算机技术&#xff0c;水平高超的电脑专家&#xff0c;尤其是程序设计人员。是一个喜欢用智力通过创造性方法来挑战脑力极限的人&#xff0c;特别是他们所感兴趣的领域&#xff0c;例如电脑编程等等。 提起黑客&#xff0c;总是那么神秘莫测。…

CentOS7安装MySQL

CentOS7安装MySQL 在CentOS中默认安装有MariaDB&#xff0c;这个是MySQL的分支&#xff0c;但为了需要&#xff0c;还是要在系统中安装MySQL&#xff0c;而且安装完成之后可以直接覆盖掉MariaDB。 下载并安装MySQL官方的 Yum Repository ​[rootlocalhost ~]# wget -i -c ht…

Sms开源短信及消息转发器,不仅只转发短信,备用机必备神器

Sms开源短信及消息转发器,不仅只转发短信,备用机必备神器。 短信转发器——不仅只转发短信&#xff0c;备用机必备神器&#xff01; 监控Android手机短信、来电、APP通知&#xff0c;并根据指定规则转发到其他手机&#xff1a;钉钉群自定义机器人、钉钉企业内机器人、企业微信…

c#入门-接口显式实现

接口显式实现 接口的显式实现主要解决两个问题 基类型隐式实现了一个接口成员。但是他的成员没有标记虚拟的&#xff0c;无法重写。接口可以多继承&#xff0c;那么重名了怎么办 显式继承语法 interface I回血 {public void 回血(); }显式继承时&#xff0c;不能写访问修饰…

团队新人多,稳定性经验不足,研发质量怎么保障?|TakinTalks论道

# 一分钟精华速览 #在研发和稳定性保障过程中&#xff0c;人与设备、程序、组织的交互是一个复杂的过程&#xff0c;虽然人们极少会恶意犯错&#xff0c;但由于受特定情景下的实际条件影响&#xff0c;人为失误也时有发生&#xff0c;那么&#xff0c;如何尽可能减少这些失误的…

Jenkins持续集成项目搭建与实践——基于Python Selenium自动化测试(自由风格)

&#x1f4cc; 博客主页&#xff1a; 程序员二黑 &#x1f4cc; 专注于软件测试领域相关技术实践和思考&#xff0c;持续分享自动化软件测试开发干货知识&#xff01; &#x1f4cc; 公号同名&#xff0c;欢迎加入我的测试交流群&#xff0c;我们一起交流学习&#xff01; 目录…

Linux无法访问本机mysql

问题描述&#xff1a;maven打包成jar包运行再linux中&#xff0c;数据库访问主机无法访问 可能问题&#xff1a; 目录 1.未修改mysql访问权限&#xff0c;修改user表&#xff0c;%表示任意ip 2.ip地址不对&#xff0c;需要找到ipV4的地址 3.导入jar包不对 4.打包成jar包插…

Mac 窗口管理分屏工具软件【Wins】

Wins&#xff0c;为 Mac 带来系统级的分屏功能&#xff0c;快速分屏&#xff0c;只需拖动窗口到屏幕边缘即可进行分屏操作。提供了分屏预览动画和触发分屏后的动画效果。 单设备永久使用&#xff0c;免费更新。仅需95 Wins 激活码&#xff1a;Wins - Mac 窗口管理分屏工具软…

HBM MM CDM Latch-Up

1、HBM&#xff08;Human Body model&#xff09;&#xff1a;正常2000V JEDEC document JEP155 states that 500-V HBM allows safe manufacturing with a standard ESD control process。 HBM VS System ESD 2、MM&#xff08;Machine Model&#xff09;&#xff0c;已经被J…

遥感测深方法综述(三)机载雷达测深系统关键参数

下面两张图分别是CZMIL Nova和SuperNova的技术参数&#xff0c;从图中可以看出&#xff0c;与最大探测深度有关的参数是Kd &#xff0c;且标称的最大深度4.X/Kd要求底质反射率大于15%。 1、海底底质对测量的影响 在进行激光测深的时候&#xff0c;海底底质的反射率对测量结果将…

【C++】继承(万字详解) —— 切片 | 隐藏 | 子类的默认成员函数 | 菱形继承

&#x1f308;欢迎来到C专栏~~继承 (꒪ꇴ꒪(꒪ꇴ꒪ )&#x1f423;,我是Scort目前状态&#xff1a;大三非科班啃C中&#x1f30d;博客主页&#xff1a;张小姐的猫~江湖背景快上车&#x1f698;&#xff0c;握好方向盘跟我有一起打天下嘞&#xff01;送给自己的一句鸡汤&#x1…

力扣 1945. 字符串转化后的各位数字之和

题目 给你一个由小写字母组成的字符串 s &#xff0c;以及一个整数 k 。 首先&#xff0c;用字母在字母表中的位置替换该字母&#xff0c;将 s 转化 为一个整数&#xff08;也就是&#xff0c;‘a’ 用 1 替换&#xff0c;‘b’ 用 2 替换&#xff0c;… ‘z’ 用 26 替换&am…

华为二面,原来是我对自动化测试的理解太肤浅了..

如何使用Python实现自动化测试 如果你入职一家新的公司&#xff0c;领导让你开展自动化测试&#xff0c;作为一个新人&#xff0c;你肯定会手忙脚乱&#xff0c;你会如何落地自动化测试呢&#xff1f;资深测试架构师沉醉将告诉你如何落地自动kan化测试&#xff0c;本次话题主要…

ChatGPT+Midjourney实现儿童绘本故事及其插图(数字1~10的故事)

一、背景 近期AI对话技术与AI绘画技术突飞猛进。其中1.&#xff09;AI对话技术是指用于模拟人类语言交流的人工智能技术。它可以让机器通过自然语言文本或语音与人类进行交流&#xff0c;并能理解人类的话语内容并做出相应的回应。对话技术在语音助手、智能客服、智能创作等领域…

Android核心基础总结——小菜爱教程

-0基础学习路线 Java基础 设计模式 Linux基础 数据库 网络协议 Android基础 服务器端开发 然后就是环境搭建、activity、intent、Android基础、基础应用开发。如阶梯图&#xff1a; 核心基础进阶 1、泛型、注解 2、动态编程技术 3、虚拟机 4、虚拟机垃圾回收 5、类与对象…

忘掉Xavier初始化吧!最强初始化方法ZerO来了

文 | 小戏谈及最典型的深度学习模型的训练&#xff0c;脑海里浮现的格式化的套路是什么&#xff1f;给定一个问题&#xff0c;给定一个数据集&#xff0c;弯弯绕绕确定好选择的神经网络的架构&#xff0c;然后上手调参&#xff0c;接下来的工作彷佛像是固定的重复工作&#xff…

AD20和立创EDA设计(3)微调原理图和原理图检查

&#xff08;1&#xff09;本文主要介绍如何将从立创EDA导出的原理图在AD20进行调整。 &#xff08;2&#xff09;需提前观看&#xff1a;AD20和立创EDA设计&#xff08;2&#xff09;提取立创EDA的原理图库和PCB库&#xff1b; &#xff08;3&#xff09;邀请加入嵌入式社区&a…

英语学习词根词缀总结

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站 点击跳转浏览。 目录 一.联想法 二.词根词缀法 索引 高频词根A-B系列 1.ab/abs- 表示远离,或否定 2.ac-/acr- 表示尖&#xff0c;酸&#x…