【Lilishop商城】No4-3.业务逻辑的代码开发,涉及到:会员B端第三方登录的开发-微信小程序登录接口开发

news/2024/5/19 20:25:59/文章来源:https://blog.csdn.net/vaevaevae233/article/details/128450098

仅涉及后端,全部目录看顶部专栏,代码、文档、接口路径在: 

 

【Lilishop商城】记录一下B2B2C商城系统学习笔记~_清晨敲代码的博客-CSDN博客


全篇会结合业务介绍重点设计逻辑,其中重点包括接口类、业务类,具体的结合源代码分析,源码读起来也不复杂~

谨慎:源代码中有一些注释是错误的,有的注释意思完全相反,有的注释对不上号,我在阅读过程中就顺手更新了,并且在我不会的地方添加了新的注释,所以在读源代码过程中一定要谨慎啊! 

目录

A1.会员登录模块

B1.微信小程序登录接口开发

业务逻辑:

代码逻辑:

        1.实体类Connect   及  分别通过 openid 和 unionid 与账号进行绑定

        2.获取系统里的微信小程序配置信息

        ​​​​​​​3.header的uuid作为 key 从缓存中获取的微信用户 


A1.会员登录模块

B1.微信小程序登录接口开发

微信小程序登录就一个接口,需要传参:临时登录凭证code、手机号码的对称解密的目标密文encryptedData、手机号码的对称解密算法初始向量iv、微信头像、微信用户昵称。header 里面传递 uuid

header的uuid:此次用户登录的uuid,用来防止多次调用接口导致多次调用第三方接口。前端需要根据后端的返回值来判断是否更新 uuid。        

临时登录凭证code:用来获取用户唯一标识 OpenID 、 用户在微信开放平台帐号下的唯一标识UnionID(若当前小程序已绑定到微信开放平台帐号) 和 会话密钥 session_key。拿到后用于解密和绑定帐号见:小程序登录 | 微信开放文档

手机号码的对称解密的目标密文encryptedData、手机号码的对称解密算法初始向量iv:是用来获取用户手机号码的,拿到后用于获取账号货或注册。此处使用的是旧的方法,具体可见之前的 No4-1 C1.微信小程序 文档说明 ;

微信头像、微信用户昵称: 拿到后用于账号注册,wx.getUserProfile(Object object) | 微信开放文档

接口接受到参数后会判断是否能拿到账号,拿到这直接返回账号登陆的 Token ;若拿不到则注册账号并返回账号登录的Token。

业务逻辑:

在介绍业务逻辑时,会涉及到一些其他代码结构,有需要说明的就用绿色底纹标注,然后在后面的代码逻辑里面详细介绍。

此时需要新增一个实体类 Connect ,是联合登陆关联表 li_connect 的,用来将账号和第三方用户唯一标识绑定的。

ConnectServiceImpl 类:仅使用的 mybatis-plus 的,没有自定义mapper,针对实体类 li_connect

  1. 先以header的uuid作为 key 从缓存中获取的微信用户信息,若获取到只直接 2.,若获取不到则获取系统里的微信小程序配置信息,然后再结合前端传入的临时登录凭证code ,作为入参调用微信提供的获取接口,从响应中拿到微信小程序端用户基本信息openid、unionid、session_key。并将这些信息以header的uuid作为 key 存到缓存里面
  2. 根据session_key、手机号码对称解密的目标密文encryptedData、手机号码的对称解密算法初始向量iv进行解密,拿到用户手机号码。【解密逻辑看微信提供的~在No4-1里说明了】
  3. 查询手机号码绑定的账号,如果存在会员,则分别通过 openid 和 unionid 与账号进行绑定并存储到li_connect表,并且返回账号的登录 token;如果不存在账号则 4.
  4. 如果不存在会员,则根据手机号、微信头像、微信用户昵称注册会员,用户名也是手机号码拼接的,并且分别通过 openid 和 unionid 与账号进行绑定并存储到li_connect表,由于是注册账号所以也要处理会员注册的小事件【和平台注册的一样不再说明了】,并且最后返回账号的登录 token;

代码逻辑:

//cn.lili.controller.passport.connect.MiniProgramBuyerController
@RestController
@RequestMapping("/buyer/passport/connect/miniProgram")
@Api(tags = "买家端,小程序登录接口")
public class MiniProgramBuyerController {@Autowiredpublic ConnectService connectService;@GetMapping("/auto-login")@ApiOperation(value = "小程序登录/自动注册")public ResultMessage<Token> autoLogin(@RequestHeader String uuid, WechatMPLoginParams params) {params.setUuid(uuid);return ResultUtil.data(this.connectService.miniProgramAutoLogin(params));}
。。。
}
//cn.lili.modules.connect.serviceimpl.ConnectServiceImpl
@Slf4j
@Service
public class ConnectServiceImpl extends ServiceImpl<ConnectMapper, Connect> implements ConnectService {@Autowiredprivate SettingService settingService;@Autowiredprivate MemberService memberService;@Autowiredprivate MemberTokenGenerate memberTokenGenerate;@Autowiredprivate Cache cache;/*** RocketMQ 配置*/@Autowiredprivate RocketmqCustomProperties rocketmqCustomProperties;@Autowiredprivate ApplicationEventPublisher applicationEventPublisher;@Override@Transactionalpublic Token miniProgramAutoLogin(WechatMPLoginParams params) {Object cacheData = cache.get(CachePrefix.WECHAT_SESSION_PARAMS.getPrefix() + params.getUuid());Map<String, String> map = new HashMap<>(3);if (cacheData == null) {//通过前端传入的微信返回的登录凭证code ,去换取微信小程序端用户基本信息JSONObject json = this.getConnect(params.getCode());//存储session key 后续登录用得到String sessionKey = json.getStr("session_key");String unionId = json.getStr("unionid");String openId = json.getStr("openid");map.put("sessionKey", sessionKey);map.put("unionId", unionId);map.put("openId", openId);cache.put(CachePrefix.WECHAT_SESSION_PARAMS.getPrefix() + params.getUuid(), map, 900L);} else {map = (Map<String, String>) cacheData;}//手机号 绑定 且 自动登录return this.phoneMpBindAndLogin(map.get("sessionKey"), params, map.get("openId"), map.get("unionId"));}/*** 通过微信返回等code 获取openid 等信息** @param code 微信code* @return 微信返回的信息*/public JSONObject getConnect(String code) {//获取系统里的微信小程序配置WechatConnectSettingItem setting = this.getWechatMPSetting();String url = "https://api.weixin.qq.com/sns/jscode2session?" +"appid=" + setting.getAppId() + "&" +"secret=" + setting.getAppSecret() + "&" +"js_code=" + code + "&" +"grant_type=authorization_code";String content = HttpUtils.doGet(url, "UTF-8", 100, 1000);log.error(content);return JSONUtil.parseObj(content);}/*** 手机号 绑定 且 自动登录** @param sessionKey 微信sessionKey* @param params     微信小程序自动登录参数* @param openId     微信openid* @param unionId    微信unionid* @return token*/@Transactional(rollbackFor = Exception.class)public Token phoneMpBindAndLogin(String sessionKey, WechatMPLoginParams params, String openId, String unionId) {String encryptedData = params.getEncryptedData();String iv = params.getIv();//使用旧版本解密方式,获取微信信息JSONObject userInfo = this.getUserInfo(encryptedData, sessionKey, iv);log.info("联合登陆返回:{}", userInfo.toString());//拿到手机号码String phone = (String) userInfo.get("purePhoneNumber");//手机号登录LambdaQueryWrapper<Member> lambdaQueryWrapper = new LambdaQueryWrapper<>();lambdaQueryWrapper.eq(Member::getMobile, phone);//查询手机号码绑定的账号Member member = memberService.getOne(lambdaQueryWrapper);//如果存在会员,则进行绑定微信openid 和 unionid,并且登录if (member != null) {//会员绑定 绑定微信小程序this.bindMpMember(openId, unionId, member);return memberTokenGenerate.createToken(member, true);}//如果没有会员,则根据手机号注册会员Member newMember = new Member("m" + phone, "111111", phone, params.getNickName(), params.getImage());memberService.save(newMember);newMember = memberService.findByUsername(newMember.getUsername());//会员绑定 绑定微信小程序this.bindMpMember(openId, unionId, newMember);//发送会员注册信息applicationEventPublisher.publishEvent(new TransactionCommitSendMQEvent("new member register", rocketmqCustomProperties.getMemberTopic(), MemberTagsEnum.MEMBER_REGISTER.name(), newMember));return memberTokenGenerate.createToken(newMember, true);}/*** 解密,获取微信信息,此类的逻辑是根据微信的加密数据解密算法逻辑开发的,具体见:https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/signature.html*  此方法是旧版本~~~详见:https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/deprecatedGetPhoneNumber.html*  新版本是拿到code后调用HTTPS接口来获取手机号码~~~这个就很简单就是用 HttpUtils 调用接口获取响应就可以了~*  详见:*      获取手机号码组件:https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/getPhoneNumber.html*      获取手机号码接口:https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/user-info/phone-number/getPhoneNumber.html** @param encryptedData 加密信息* @param sessionKey    微信sessionKey* @param iv            微信揭秘参数* @return 用户信息*/public JSONObject getUserInfo(String encryptedData, String sessionKey, String iv) {log.info("encryptedData:{},sessionKey:{},iv:{}", encryptedData, sessionKey, iv);//被加密的数据byte[] dataByte = Base64.getDecoder().decode(encryptedData);//加密秘钥byte[] keyByte = Base64.getDecoder().decode(sessionKey);//偏移量byte[] ivByte = Base64.getDecoder().decode(iv);try {//如果密钥不足16位,那么就补足.  这个if 中的内容很重要int base = 16;if (keyByte.length % base != 0) {int groups = keyByte.length / base + (keyByte.length % base != 0 ? 1 : 0);byte[] temp = new byte[groups * base];Arrays.fill(temp, (byte) 0);System.arraycopy(keyByte, 0, temp, 0, keyByte.length);keyByte = temp;}//初始化  需要导入包Security.addProvider(new BouncyCastleProvider());Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", "BC");SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");parameters.init(new IvParameterSpec(ivByte));//初始化cipher.init(Cipher.DECRYPT_MODE, spec, parameters);byte[] resultByte = cipher.doFinal(dataByte);if (null != resultByte && resultByte.length > 0) {String result = new String(resultByte, StandardCharsets.UTF_8);return JSONUtil.parseObj(result);}} catch (Exception e) {log.error("解密,获取微信信息错误", e);}throw new ServiceException(ResultCode.USER_CONNECT_ERROR);}/*** 会员绑定 绑定微信小程序* <p>* 如果openid 已经绑定其他账号,则这里不作处理,如果未绑定,则绑定最新的会员* 这样,微信小程序注册之后,其他app 公众号页面,都可以实现绑定自动登录功能* </p>** @param openId  微信openid* @param unionId 微信unionid* @param member  会员*/private void bindMpMember(String openId, String unionId, Member member) {//如果 unionid 不为空  则为账号绑定unionidif (CharSequenceUtil.isNotEmpty(unionId)) {LambdaQueryWrapper<Connect> lambdaQueryWrapper = new LambdaQueryWrapper();lambdaQueryWrapper.eq(Connect::getUnionId, unionId);lambdaQueryWrapper.eq(Connect::getUnionType, ConnectEnum.WECHAT.name());List<Connect> connects = this.list(lambdaQueryWrapper);//只有为绑定过的才会绑定,已绑定过的不会再次绑定!!!!if (connects.isEmpty()) {Connect connect = new Connect();connect.setUnionId(unionId);connect.setUserId(member.getId());connect.setUnionType(ConnectEnum.WECHAT.name());this.save(connect);}}//如果 openid 不为空  则为账号绑定openidif (CharSequenceUtil.isNotEmpty(openId)) {LambdaQueryWrapper<Connect> lambdaQueryWrapper = new LambdaQueryWrapper<>();lambdaQueryWrapper.eq(Connect::getUnionId, openId);lambdaQueryWrapper.eq(Connect::getUnionType, ConnectEnum.WECHAT_MP_OPEN_ID.name());List<Connect> connects = this.list(lambdaQueryWrapper);//只有为绑定过的才会绑定,已绑定过的不会再次绑定!!!!if (connects.isEmpty()) {Connect connect = new Connect();connect.setUnionId(openId);connect.setUserId(member.getId());connect.setUnionType(ConnectEnum.WECHAT_MP_OPEN_ID.name());this.save(connect);}}}/*** 获取微信小程序配置** @return 微信小程序配置*/private WechatConnectSettingItem getWechatMPSetting() {//从数据库中拿到微信 联合登陆设置Setting setting = settingService.get(SettingEnum.WECHAT_CONNECT.name());//然后将设置转化成使用JavaBean对象。此对象里面是用list存放多种微信应用设置的WechatConnectSetting wechatConnectSetting = JSONUtil.toBean(setting.getSettingValue(), WechatConnectSetting.class);if (wechatConnectSetting == null) {throw new ServiceException(ResultCode.WECHAT_CONNECT_NOT_EXIST);}//寻找对应对微信小程序登录配置for (WechatConnectSettingItem wechatConnectSettingItem : wechatConnectSetting.getWechatConnectSettingItems()) {//拿到微信小程序应用的配置if (wechatConnectSettingItem.getClientType().equals(ClientTypeEnum.WECHAT_MP.name())) {return wechatConnectSettingItem;}}throw new ServiceException(ResultCode.WECHAT_CONNECT_NOT_EXIST);}。。。
}

1.实体类Connect   及  分别通过 openid 和 unionid 与账号进行绑定

先说实体类 Connect, 里面重要的就是下面这三个字段,每一个联合登录关联表都会关联一个账号id、联合(第三方)用户id、联合(第三方)类型。只要联合用户id未关联过账号就会添加一份关联。

联合类型包括:微信的各个产品下的不同openid(小程序、移动应用、网站应用)、微信unionid、QQ的各个产品、微博(不知道微博有没有多个应用,还没用过)、支付宝等等,需要了解第三方后细分哦。

同一个 userid 可能和多个联合用户id 绑定哦。 

但是要注意,通常来说一个现实用户在任何端使用任意第三方进行授权登录时应该都关联同一个账号,但是shop里面可不是,所以需要注意~~~~【为啥?请看完所有的会员登录方式,就明白了】

    @ApiModelProperty("用户id")private String userId;@ApiModelProperty("联合登录id")private String unionId;/*** @see cn.lili.modules.connect.entity.enums.ConnectEnum*/@ApiModelProperty(value = "联合登录类型")private String unionType;

所以  分别通过 openid 和 unionid 与账号进行绑定  这个逻辑也就不用说明了,就是将第三方的用户id与平台账号绑定。

绑定也是先判断此联合用户id是否已存在,也就是是否已绑定过账号,没有则会绑定。

2.获取系统里的微信小程序配置信息

 配置信息都包括appId、appSecret、clientType,这些都是必须向微信申请的,审核通过后微信下发的,每个产品都有自己唯一的,例如shop项目里面微信小程序的和微信网站应用的都是不一样的!

这些信息是属于平台的,所以可以交给运营端管理,由于这些都是小数据单开一个数据表不值得,所以可以使用运营M端的系统设置逻辑,根据 K:V保存起来,V里面保存为 json类型的。​​​​​​​

使用时根据 clientType 区分是哪个产品的。

//WECHAT_CONNECT 的
{"wechatConnectSettingItems":[{"clientType":"PC","appId":"XXXXXXX","appSecret":"XXXXXXX"},{"clientType":"H5","appId":"XXXXXXX","appSecret":"XXXXXXX"},{"clientType":"WECHAT_MP","appId":"XXXXXXX","appSecret":"XXXXXXX"}]
}

 

3.header的uuid作为 key 从缓存中获取的微信用户 

其实不缓存也不会有问题,这里添加缓存是避免多次调用 this.getConnect(params.getCode());方法,该方法最终是会调用微信提供的接口的。而微信的接口都是有使用频率的!!!是有限制的!!!避免由于其他错误导致次数过多后面用户无法进行调用。

 

另一方面也是如果小程序端调用次数频率过多时,能过提高性能,毕竟同一个用户拿到的信息是一样的,所以先存储起来,如果真的碰到多次频繁调用,就能够直接获取缓存中的信息使用了。

这就是要多注意,前端需要根据后端的响应情况来更新或清除 uuid 哦,否则该逻辑就没用~~~

如果此次登录授权的响应是未成功,那么就不用清除 uuid ,如果登录成功就能清除 uuid 了

055e0a05fddf48d1895e4522ced70e0e.png (1080×2408)

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

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

第十六讲:神州交换机访问控制列表的配置

访问控制列表ACL&#xff08;Access Control Lists&#xff09;数据定义工具&#xff0c;基于用户自行定义的数据的参数区分不同的数据流&#xff0c;是在交换机和路由器上经常采用的一种防火墙技术&#xff0c;它可以对经过网络设备的数据包根据一定规则进行过滤。它有以下一些…

#5文献学习总结--利用多级反馈排队的雾计算框架中的期限和优先级感知任务卸载

文献&#xff1a;DPTO: A Deadline and Priority-aware Task Offloading in Fog Computing Framework Leveraging Multi-level Feedback Queueing 延迟相关优先级感知卸载&#xff08;DPTO&#xff09;策略&#xff0c;基于任务的最后期限为每个任务分配优先级&#xff0c;并将…

C#,图像二值化(06)——全局阈值的大津OTSU算法及其源代码

1、大津OTSU算法 最大类间方差法是1979年由日本学者大津(Nobuyuki Otsu)提出的&#xff0c;是一种自适应阈值确定的方法&#xff0c;又叫大津法&#xff0c;简称OTSU&#xff0c;是一种基于全局的二值化算法&#xff0c;它是根据图像的灰度特性,将图像分为前景和背景两个部分。…

rocketmq 实战问题汇总

rocketmq 实战过程会遇到这样或者那样的问题&#xff0c;今天我们专门抽出一篇文章来分析一下汇总一下&#xff0c;避免以后踩同样的坑&#xff1a; 1、找不到JDK的问题&#xff1a; 综合分析&#xff0c;是因为JDK安装的目录有空格导致的&#xff1a;Program Files 两个单词之…

YOLO-V5 系列算法和代码解析(三)—— 训练数据加载

文章目录调试准备Debug 设置代码修改调试数据代码运行逻辑类初始化启动迭代器数据增强调试准备 为了便于阅读代码和打印中间变量&#xff0c;需进行调试模式下运行代码。配置平台&#xff1a;Ubuntu&#xff0c;VSCode。在上一篇博文中&#xff0c;我们简单探讨过调试的设置。在…

JavaScript手写响应式原理(详解)

响应式原理 首先我们有一个对象 const obj {name: zlk,age: 18}这个对象可能在别处被用到 比如是这样的 function foo() {const newValue obj.nameconsole.log(hello world);console.log(obj.name);}我们来改变obj对象中的name的值 obj.name zlk这时候foo()应该被重新执…

一文读懂bert结构。

最近承接了项目要复现tiny_Bert。所以在这里用文章记录自己学到的。这篇文章是前置&#xff0c;主要介绍bert原理。 下一篇文章介绍tinybert的原理和训练 模型介绍&#xff1a; BERT概述&#xff1a; 如果要介绍tinyBERT&#xff0c;首先我们需要了解BERT模型。&#xff08;了…

原神私服搭建教程 (最新版)

搭建教程 1.准备阶段 1.请先确保电脑内有这些安装环境&#xff0c;否则私服无法运行&#xff01;&#xff01;&#xff01; MongoDB Python3.8 java17 mitmproxy 没有请在群文件下载安装环境&#xff0c;安装即可。特别强调&#xff1a;java17直接放在C:\Program Files目录下即…

【Java编程进阶】方法初识

推荐学习专栏&#xff1a;Java 编程进阶之路【从入门到精通】 文章目录1. Java 方法初识2. 方法的创建与使用3. 方法的分类3.1 无参无返回值3.2 无参带返回值3.3 有参无返回值3.4 有参带返回值4. 递归方法5. 总结1. Java 方法初识 方法是组合在一起来执行操作语句的集合&#…

一体式无线阀控超声水表在西北某市大用户用水计量收费管理项目应用案例

多年来&#xff0c;西北某市的工业园区供水公司对工业企业用户的用水收费一直采取业务员手动抄表、上门收费的方式。不仅效率低、浪费人力资源&#xff0c;而且供水公司很难掌握地区用水情况&#xff0c;不便于统一调度和管理。 为此&#xff0c;该工业园区安装了平升电子一体…

2023年无线运动耳机排行榜最新公布、公认最好的运动耳机推荐

随着人们日益对健康的重视&#xff0c;”全民健身“正在全国&#xff0c;乃至全世界蔓延开来&#xff0c;其中跑步锻炼凭借着门槛低&#xff0c;益处多成为了大部分人的健身的首选。而随着跑步大军的壮大&#xff0c;国内蓝牙耳机市场也是一片火热。其中蓝牙无线运动耳机凭借着…

拆串后结构化,其中按行对齐

【问题】 I have a bit weired scenario where i need to fetch data i have following three products product1 product2 product3and each product has different ids(e.g. p1345,p3453,p2345) and then each froduct have different options which are having different…

go 库 Cobra 现代化的命令行框架

go 库 Cobra 现代化的命令行框架 文章目录go 库 Cobra 现代化的命令行框架1. 简介2. 主要功能3. 应用举例4. Cobra 安装5. 使用 Cobra 库创建命令5.1 创建 rootCmd5.2 创建 main.go5.3 添加命令5.4 编译并运行6. 特性6.1 使用标志6.2 非选项参数验证6.3 PreRun and PostRun Hoo…

申请大学用的是IB预估分?

IB课程体系以其独特的优越性成为越来越多国际高中生的选择。如今全球共有3300多所高校接受IB成绩申请&#xff0c;其中包括美国常春藤盟校、英国G5在内的多所名校。 但是&#xff0c;大家知道吗&#xff0c;国内学习IB课程的学生是需要用预估分来申请大学的。今天&#xff0c;小…

4.7W防削顶单声道D类音频功率放大器HT6872介绍

HT6872简介 HT6872是一款低EMI&#xff0c;防削顶失真&#xff0c;单声道免滤波D类音频功率放大器。在6.5V电源&#xff0c;10%THDN&#xff0c;4Ω负载条件下&#xff0c;输出4.71W功率&#xff0c;在各类音频终端应用中维持高效率并提供AB类放大器的性能。 HT6872的最大特点是…

【PAT甲级 - C++题解】1032 Sharing

✍个人博客&#xff1a;https://blog.csdn.net/Newin2020?spm1011.2415.3001.5343 &#x1f4da;专栏地址&#xff1a;PAT题解集合 &#x1f4dd;原题地址&#xff1a;题目详情 - 1032 Sharing (pintia.cn) &#x1f511;中文翻译&#xff1a;共享 &#x1f4e3;专栏定位&…

梯度下降算法、随机梯度下降算法、动量随机梯度下降算法、AdaGrad算法、RMSProp算法、Adam算法详细介绍及其原理详解

文章目录前言一、回归拟合问题二、损失函数三、梯度下降算法四、随机梯度下降算法五、动量随机梯度下降算法六、AdaGrad算法七、RMSProp算法八、Adam算法总结前言 本篇博文详细介绍了关于梯度下降算法的所有相关知识&#xff0c;具体包括&#xff1a;回归拟合问题、损失函数、梯…

【OpenCV-Python】教程:9-1 级联分类器训练

OpenCV Python 级联分类器训练 【介绍】 使用增强的弱分类器级联包括两个主要阶段: 训练和检测阶段。使用基于HAAR或LBP模型的检测,在object detection tutorial中进行了描述。本文档概述了训练您自己的增强弱分类器级联所需的功能。当前的手册将走过所有不同的阶段: 收集训练…

MySQL5.7 多主一从(多源复制)同步配置

主从复制有如下一些优势&#xff1a; 分担负载&#xff1a;对业务进行读写分离&#xff0c;减轻主库I/O负载&#xff0c;将部分压力分担到从库上&#xff0c;缩短客户查询响应时间。 增加健壮性&#xff1a;在主库出现问题时&#xff0c;可通过多种方案将从库设置为主库&#…

4.防止数据权限越权

涉及的修改 这次提交内容很简单&#xff0c;就是在这些类的操作上&#xff0c;添加了 checkXxxDataScope()方法校验&#xff0c;下面来看下这个方法的实现 /*** 校验用户是否有数据权限* * param userId 用户id*/ Override public void checkUserDataScope(Long userId) {if (…