微信公众号开发—通过网页授权实现业务系统登录及用户绑定(微信网页授权自动登录业务系统)

news2024/11/24 16:21:36
😊 @ 作者: 一恍过去
💖 @ 主页: https://blog.csdn.net/zhuocailing3390
🎊 @ 社区: Java技术栈交流
🎉 @ 主题: 微信公众号开发—通过网页授权实现业务系统登录及用户绑定(微信网页授权自动登录业务系统)
⏱️ @ 创作时间: 2022年12月19日

目录

  • 1、准备工作
  • 2、登录授权绑定说明
  • 3、登录授权绑定流程
  • 4、基础代码实现
    • 4.1 定义工具类
    • 4.2 模拟前端获取Code
    • 4.3 授权绑定操作接口

1、准备工作

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

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

2、登录授权绑定说明

业务系统在外部浏览器默认情况通过手机号进行登录;在微信环境中,除了可以使用手机号登录外,也可以使用微信授权实现业务系统的登录;为了实现微信网页授权登录业务系统与手机号登录业务系统是统一的用户,需要在第一次使用微信授权登录时进行绑定操作。为了简单的记录是否存在绑定关系,此Demo为了简便演示操作,在后台数据库中通过表中的openId是否存在已表示是否存在绑定,表结构如下:

CREATE TABLE `tb_user_info` (
  `id` bigint NOT NULL COMMENT '主键',
  `mobile` varchar(12) DEFAULT NULL COMMENT '手机号',
  `opend_id` varchar(100) DEFAULT NULL COMMENT '公众号openId',
  `gender` int DEFAULT NULL COMMENT '性别',
  `nick_name` varchar(32) DEFAULT NULL COMMENT '昵称',
  `head_image` varchar(256) DEFAULT NULL COMMENT '头像链接',
  PRIMARY KEY (`id`)
)

3、登录授权绑定流程

  • 用户点击微信登录
    • 前端请求微信网页授权接口,询问用户是否同意授权;
    • 用户同意授权后,通过获取code;
    • 前端通过code调用后端wxLogin接口获取openId等微信用户信息;
    • 判断openId是否存在绑定关系(是否在数据表中)
      • 存在绑定关系,表示可以直接登录,返回用户信息及登录验证的token信息;
      • 不存在绑定关系,则判断前端调用wxLogin接口时,用户是否已经登录(token是否存在或有效),如果已登录则直接绑定openId并且返回绑定成功标识;如果没有登录,将通过openId获取的微信信息存入redis中,将redisKey返回到前端;
  • 前端检测是否存在redisKey(是否引导授权绑定)
    • 不存在redisKey,并且授权绑定成功,直接渲染当前用户信息;
    • 存在redisKey,进入授权绑定页面,引导用户输入手机号及验证码;
      • 前端将用户手机号、验证码、redisKey传入后端;
      • 后端判断redisKey是否过期(默认1小时过期),如果过期则要求用户重新点击微信登录;
      • 验证手机号及验证码是否正确,如果存在则要求用户重新填写;
      • 如果redisKey、手机号、验证码都正确,则判断该手机号是否存在于业务系统中;
      • 如果手机号存在于系统中,并且已经绑定了openId,则提示用户无法绑定;如果存在于系统中,但是没有绑定openId,则更新数据库,实现openId绑定;
      • 如果手机号不存在于系统中,则将手机号、openId、redisKey中的基本信息插入到数据库中,实现用户的注册已经openId绑定;

基础流程图:
在这里插入图片描述

4、基础代码实现

注意:Demo只实现,用户发起微信登录接口、授权绑定接口两个接口;获取验证码、验证码校验等流程只做伪代码描述,当前Demo没有做前端代码,由后端来模拟网页授权及授权回调后,获取code的接口;

4.1 定义工具类

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);
    }

}

4.2 模拟前端获取Code

定义请求实体类 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;
}

定义代码:

@Slf4j
@Controller
public class AuthController {

    /**
     * 模拟前端发起微信网页授权,再用户点击"微信登录"时,进行调用
     *
     * @return
     * @throws UnsupportedEncodingException
     */
    @GetMapping(value = "/pullCode")
    @ApiOperation(value = "用户请求进行授权及获取信息", notes = "用户请求进行授权及获取信息")
    public String code() throws UnsupportedEncodingException {
        log.info("------ 用户请求进行授权及获取信息 ------");

        // 设置回调地址 http://qh3wg7.natappfree.cc/wechat/getCode
        String redirectUri = "http://qh3wg7.natappfree.cc/wechat/getCode";
        // urlEncode处理
        redirectUri = URLEncoder.encode(redirectUri, "utf-8");

        String url = "https://open.weixin.qq.com/connect/oauth2/authorize";
        // 封装url请求参数
        Oauth2AuthorizeRep rep = new Oauth2AuthorizeRep();
        rep.setAppid("wx79ec4331f29311b9");
        rep.setRedirect_uri(redirectUri);
        rep.setScope("snsapi_userinfo");
        rep.setState("STATE");
        // 参数的顺序必须是: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 "redirect:" + url;
    }

    /**
     * 模拟前端接收微信网页授权后回调,并且获取code;
     * 获取code后,由前端将code传入到后端的wxLogin接口中
     *
     * @param code
     */
    @GetMapping(value = "/getCode")
    @ApiOperation(value = "前端根据code获取信息", notes = "前端根据code获取信息")
    @ResponseBody
    public void auth(@RequestParam(value = "code") String code) {
        log.info("------ 回显Code:{} ------", code);
    }
}

4.3 授权绑定操作接口

定义获取用户信息请求实体类 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;
}

定义用户基本信息表实现类:

@Table(name = "tb_user_info")
@Data
public class UserInfo implements Serializable {
    /**
     * 主键
     */
    @Id
    private Long id;

    /**
     * 登录手机号
     */
    private String mobile;

    /**
     * 微信公众号号,唯一ID
     */
    @Column(name = "openId")
    private String openId;

    /**
     * 性别
     */
    private Integer gender;

    /**
     * 昵称
     */
    @Column(name = "nickName")
    private String nickName;

    /**
     * 头像
     */
    @Column(name = "headImage")
    private String headImage;
}

Controller实现:

    /**
     * 用户请求微信登录(由前端获取到)
     *
     * @return
     * @throws UnsupportedEncodingException
     */
    @GetMapping(value = "/wxLogin")
    @ApiOperation(value = "用户请求微信登录", notes = "用户请求微信登录")
    @ResponseBody
    public PageResult wxLogin(@RequestParam(value = "code") String code) {
        return authService.wxLogin(code);
    }


    /**
     * 绑定微信openId
     *
     * @param mobile
     * @param smsCode
     * @param redisKey
     * @return
     */
    @GetMapping(value = "/wxBind")
    @ApiOperation(value = "绑定微信openId", notes = "绑定微信openId")
    @ResponseBody
    public PageResult wxBind(@RequestParam("mobile") String mobile, @RequestParam("smsCode") String smsCode, @RequestParam("redisKey") String redisKey) {
        log.info("------ 绑定微信openId ------");
        //通过code获取用户及天气实时位置等信息
        return authService.wxBind(mobile, smsCode, redisKey);
    }

Service实现:


@Slf4j
@Service
public class AuthService {

    @Resource
    private RestHttpRequest restHttpRequest;

    @Resource
    private UserInfoMapper userInfoMapper;

    @Resource
    private WxBean wxBean;

    public PageResult wxLogin(@RequestParam(value = "code") String code) {
        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);
        // 注意一个code只能使用一次,使用后需要重新模拟前端获取
        rep.setCode(code);
        url = url + MapUtils.getUrlByBean(rep);
        Map map = restHttpRequest.doHttp(url, HttpMethod.GET, null);
        if (map == null || map.get("errcode") != null) {
            throw new RuntimeException("获取授权信息失败!");
        }
        Oauth2AccessTokenRes res = new Oauth2AccessTokenRes();
        MapUtils.mapToEntity(map, res);
        log.info("Oauth2AccessTokenRes:" + JSON.toJSONString(res));

        // 获取accessToken及openId过期时间
        String accessToken = res.getAccess_token();
        String openid = res.getOpenid();

        // 判断openId是否存在绑定关系
        UserInfo userInfo = userInfoMapper.selectByOpenId(openid);
        if (userInfo != null) {
            // 存在绑定关系,表示可以直接登录,返回用户信息及登录验证的token信息
            return ResultUtils.success(userInfo);
        }

        // 判断当前用户是否已经登录了,模拟登录,当System.currentTimeMillis()偶数表示已经登录
        if (System.currentTimeMillis() % 2 == 0) {
            // 如果已登录则直接绑定openId并且返回绑定成功标识
            userInfo = new UserInfo();
            // TODO 如果已经登录了,业务系统直接根据token获取用户Id值
            userInfo.setId(1L);
            userInfo.setOpenId(openid);
            userInfoMapper.updateByPrimaryKeySelective(userInfo);

            return ResultUtils.success(null);
        } else {
            // 如果没有登录,将通过openId获取的微信信息存入redis中,将redisKey返回到前端
            String redisKey = UUIDUtils.getUuId();
            Oauth2UserInfoRes oauth2UserInfoRes = getAndInsertUserInfo(openid, accessToken);
            RedisUtils.setEx(redisKey, JSON.toJSONString(oauth2UserInfoRes), 60L, TimeUnit.MINUTES);

            return ResultUtils.success(redisKey);
        }
    }

    /**
     * 绑定微信openId
     *
     * @param mobile
     * @param smsCode
     * @param redisKey
     * @return
     */
    public PageResult wxBind(@RequestParam("mobile") String mobile, @RequestParam("smsCode") String smsCode, @RequestParam("redisKey") String redisKey) {

        if (!RedisUtils.hasKey(redisKey)) {
            return ResultUtils.fail("长时间未操作,请重新授权登录!");
        }
        Object o = RedisUtils.get(redisKey);
        Oauth2UserInfoRes oauth2UserInfoRes = JSON.parseObject(o.toString(), Oauth2UserInfoRes.class);

        // 验证手机号及验证码是否正确,如果System.currentTimeMillis()为偶数则表示不正确
        if (System.currentTimeMillis() % 2 == 0) {
            return ResultUtils.fail("验证码错误!");
        }

        // 删除key
        RedisUtils.del(redisKey);

        // 通过手机号查询
        UserInfo userInfo = userInfoMapper.selectByMobile(mobile);

        // 如果手机号不存在于系统中,则将手机号、openId、redisKey中的基本信息插入到数据库中,实现用户的注册已经openId绑定;
        if (userInfo == null) {
            userInfo = new UserInfo();
            userInfo.setId(System.currentTimeMillis());
            userInfo.setGender(oauth2UserInfoRes.getSex());
            userInfo.setMobile(mobile);
            userInfo.setNickName(oauth2UserInfoRes.getNickname());
            userInfo.setOpenId(oauth2UserInfoRes.getOpenid());
            userInfo.setHeadImage(oauth2UserInfoRes.getHeadimgurl());
            userInfoMapper.insert(userInfo);
            // 返回成功
            return ResultUtils.success(userInfo);
        }

        if (userInfo.getOpenId() != null) {
            return ResultUtils.fail("手机号已绑定其他微信号,无法再次绑定!");
        } else {
            // 进行绑定操作
            userInfo.setOpenId(oauth2UserInfoRes.getOpenid());
            userInfoMapper.updateByPrimaryKeySelective(userInfo);
            return ResultUtils.success(userInfo);
        }
    }

    private Oauth2UserInfoRes 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));
        return res;
    }
}

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

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

相关文章

产品经理快速入门指南之常见问题篇

产品经理作为近几年互联网最炙手可热的岗位&#xff0c;可谓是赚足了眼球。其实呢&#xff0c;产品经理是很难定义的一个角色&#xff0c;如果非要一句话定义&#xff0c;那么产品经理是为终端用户服务&#xff0c;负责产品整个生命周期的人。今天这篇文章小编会带大家了解一下…

交通部1078-2016版中的音视频协议的一点想法

交通部1078的音视频中的格式有点像TS流, 交通部中1078的音视频的解析用的走的HTTP的协议,内容是流媒体,之前用的是ffmpeg解析的内容流之前是把payload中的数据流区分出音频,视频直接塞给ffmpeg,由ffmpeg推两路流到rtmp协议上去,这种方法有点粗暴, 最近分析ffmpeg源码发现交通…

谷歌通过Chrome简化登录安卓密码支持

谷歌宣布&#xff0c;它正在为其 Chrome 网络浏览器和 Android 操作系统引入密码支持&#xff0c;以简化跨应用程序、网站和设备的登录。 谷歌今天表示&#xff1a; “密码是密码和其他可钓鱼身份验证因素的一种更安全的替代品。它们不能重复使用&#xff0c;不会在服务器漏洞…

AngularJS 2.0 稳定版真的发布了!

导读之前我们还哀叹&#xff0c;谷歌的 AngularJS 2.0 的稳定版看起来年底也未必能见到&#xff0c;然而&#xff0c;在 9 月 14 日谷歌总部召开的一个会议上&#xff0c;突然就宣布最终的稳定版发布了——而这距离前一个版本 RC7 的发布才过去了一天。 AngularJS 2.0 的开发始…

详解入门安全测试最难懂的概念 —— CSRF

对于刚刚入门安全的同学来说&#xff0c;csrf是最难理解的概念之一&#xff0c;本文会用最简单的方式对csrf进行讲解&#xff0c;包括csrf的定义&#xff0c;csrf典型的攻击流程以及如何对其进行防范&#xff0c;希望本文能够帮到大家&#xff01; CSRF定义 CSRF&#xff08;…

Nacos学习笔记 (1)Nacos的简介与安装

1. Nacos 介绍与发展前景 1.1 官网概览&#xff1a; Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service的首字母简称&#xff0c;一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。 Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一…

性能测试(一)—— 概述、策略、指标、流程

目录 一、性能测试概述 1、为什么要进行性能测试&#xff1f; 2、性能测试的概念 2.1 什么是性能&#xff1f; 2.2 什么是性能测试&#xff1f; 2.3 性能测试目的 3、性能测试与功能测试 3.1 焦点不一样 3.2 关系 二、性能测试策略 1、性能测试策略 1.1 基准测试 …

PageObject(PO)设计模式在 UI 自动化中的实践总结(以 QQ 邮箱登陆为例)

1080608 28.8 KB PO的思想最早是2013年由IT大佬Martin Flower提出的&#xff1a; martinfowler.com bliki: PageObject A page object wraps an HTML page, or fragment, with an application-specific API, allowing you to manipulate page elements for testing without d…

MVC操作方法如何绑定Stream类型的参数

1、我需要读取HTTP消息的整个 body 来填充 MVC 方法参数&#xff1b; 2、HTTP消息的 body 不是 form-data&#xff0c;而是完全的二进制内容。 最简单的方法就是不使用模型绑定&#xff0c;即在MVC方法中直接访问 HttpContext.Request.Body。 var request HttpContext.Requ…

[附源码]计算机毕业设计Python的物品交换平台(程序+源码+LW文档)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程 项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等…

使用设备树给DM9000网卡_触摸屏指定中断

目录 1 在设备树中描述网卡中断 2 dm9dev9000c.c 3 在设备树中描述触摸屏中断 1 在设备树中描述网卡中断 srom-cs420000000 {compatible "simple-bus";#address-cells <1>;#size-cells <1>;reg <0x20000000 0x8000000>;ranges;ethernet20000…

ESP 常用的低功耗配置选项解析

此篇博客介绍 ESP 常用的低功耗配置选项。 1.常用功耗优化配置选项 1.1.动态调频 CPU 工作的频率越高&#xff0c;功耗消耗也越大。通过 DFS&#xff08;dynamic frequency scaling&#xff0c;动态调频&#xff09;可以让系统自动切换工作频率&#xff0c;达到功耗和性能间…

Transformers学习笔记1. 一些基本概念和编码器、字典

Transformers学习笔记1. 一些基本概念和编码器、字典一、基本概念1. Hugging Face简介2. Transformers&#xff08;1&#xff09;简介&#xff08;1&#xff09;预定义模型&#xff08;2&#xff09;使用方法3. Datasets查看有哪些公开数据集方法1&#xff1a; 使用datasets包的…

深入分析JVM执行引擎

程序和机器沟通的桥梁 一、闲聊 相信很多朋友在出国旅游&#xff0c;或者与外国友人沟通的过程中&#xff0c;都会遇到语言不通的烦恼。这时候我们就需要掌握对应的外语或者拥有一部翻译机。而笔者只会中文&#xff0c;所以需要借助一部翻译器才能与不懂中文的外国友人交流。咱…

Android入门第51天-使用Android的SharedPreference存取信息

简介 上一篇我们介绍了在android里如何读写本地文件。我们有一种场景&#xff0c;类似网页的cookie&#xff0c;要把用户的一些储如上一次登录、使用的痕迹等信息保存下来以便于每次不需要做重复“填表单”的操作&#xff0c;当在这种场景下我们如果也使用本地文件读写的话显然…

关于Unity使用Aspose.Words创建表格单元格垂直合并不生效情况说明

文章目录&#x1f449;一、前言&#x1f449;二、问题重现1、首先看一下我用下面两段代码创建的表格&#xff1a;2、被这个问题折磨的心路历程&#x1f449;三、分析原因&#x1f449;四、解决方法&#x1f449;一、前言 最近在使用Aspose.Words.dll实现创建表格功能时&#x…

Google Earth Engine APP(GEE)——用一个选择器选择不同城市的应用

我们很多时候在进行应用制作的时候,都会用到选择器用于添加不同的城市,从而进一步选择不同的区域进行分析,本文就将准备一个包含有城市的矢量数据,按照名字进行筛选,最终展示不同城市的所在范围,从而实现简单的select选择器的调用。本文最主要的就是这个回调函数。 具体…

C语言基础—指针(地址引用、指针数组、二次指针)

本章主要讲解指针的基本定义和指针的传递、偏移。后面继续讲解指针数组和多维指针、二级指针等 知识点&#xff1a; 指针的定义和指针分类各类指针的字节长度取决于系统位数指针的传递&#xff08;值传递和引用(地址传递)&#xff09;指针的偏移&#xff08;自增自减号&#x…

动态优化解决方案空间中的最小支持(Matlab代码实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 &#x1f468;‍&#x1f4bb;4 Matlab代码 &#x1f4a5;1 概述 相对于求解函数极值这类静态问题&#xff0c;许多存在于真实世界的优化问题都是在动态变化的&#xff0c;这一类问题被称为动态…

201732-35-6,H2N-AFP-pNA

AFP-pNA&#xff0c;来自牙周病原体牙龈卟啉单胞菌和黑普氏菌的脯氨酸三肽基氨基肽酶的底物。 编号: 189876中文名称: 三肽Xaa-Xaa-Pro tripeptidylpeptidase substrateCAS号: 201732-35-6单字母: H2N-AFP-pNA三字母: H2N-Ala-Phe-Pro-pNA氨基酸个数: 3分子式: C23H27N5O5平均分…