尚医通(医院预约挂号系统)笔记

文章目录

  • 一. 登录系统
    • 1. 手机登录
      • 1.1 业务流程
      • 1.2 代码
      • 1.3 JWT
    • 2. 微信登陆
      • 2.1 业务流程
      • 2.2 代码
      • 2.3 OAthu2
    • 3. 用户认证与网关整合
  • 二. 预约挂号
  • 1. 业务流程及模块设计

一. 登录系统

1. 手机登录

1.1 业务流程

  • 传入手机号和验证码
  • 校验手机号和验证码是否为空
  • 校验手机验证码和输入的验证码是否一致
    (在点击发送验证码按钮时,调用短信模块,使用阿里云短信实现发送验证码,且保存到redis中)
  • 绑定手机号 (微信登录相关)
  • 判断是否为第一次登录,若是第一次登录,保存信息到数据库
  • 返回登录信息,用户名和token(用JWT生成)

1.2 代码

    public Map<String, Object> loginUser(LoginVo loginVo) {//从loginVo获取输入的手机号,和验证码String phone = loginVo.getPhone();String code = loginVo.getCode();//判断手机号和验证码是否为空if(StringUtils.isEmpty(phone) || StringUtils.isEmpty(code)) {throw new YyghException(ResultCodeEnum.PARAM_ERROR);}//判断手机验证码和输入的验证码是否一致String redisCode = redisTemplate.opsForValue().get(phone);if(!code.equals(redisCode)) {throw new YyghException(ResultCodeEnum.CODE_ERROR);}//绑定手机号码UserInfo userInfo = null;if(!StringUtils.isEmpty(loginVo.getOpenid())) {userInfo = this.selectWxInfoOpenId(loginVo.getOpenid());if(null != userInfo) {userInfo.setPhone(loginVo.getPhone());this.updateById(userInfo);} else {throw new YyghException(ResultCodeEnum.DATA_ERROR);}}//如果userinfo为空,进行正常手机登录if(userInfo == null) {//判断是否第一次登录:根据手机号查询数据库,如果不存在相同手机号就是第一次登录QueryWrapper<UserInfo> wrapper = new QueryWrapper<>();wrapper.eq("phone",phone);userInfo = baseMapper.selectOne(wrapper);if(userInfo == null) { //第一次使用这个手机号登录//添加信息到数据库userInfo = new UserInfo();userInfo.setName("");userInfo.setPhone(phone);userInfo.setStatus(1);baseMapper.insert(userInfo);}}//校验是否被禁用if(userInfo.getStatus() == 0) {throw new YyghException(ResultCodeEnum.LOGIN_DISABLED_ERROR);}//不是第一次,直接登录//返回登录信息//返回登录用户名//返回token信息Map<String, Object> map = new HashMap<>();String name = userInfo.getName();if(StringUtils.isEmpty(name)) {name = userInfo.getNickName();}if(StringUtils.isEmpty(name)) {name = userInfo.getPhone();}map.put("name",name);//jwt生成token字符串String token = JwtHelper.createToken(userInfo.getId(), name);map.put("token",token);return map;}

1.3 JWT

在这里插入图片描述
base64编码,并不是加密,只是把明文信息变成了不可见的字符串。但是其实只要用一些工具就可以把base64编码解成明文,所以不要在JWT中放入涉及私密的信息。
由id+name得到token,且能由token反向得到name和id

    private static long tokenExpiration = 24*60*60*1000;//签名秘钥private static String tokenSignKey = "123456";//根据参数生成tokenpublic static String createToken(Long userId, String userName) {String token = Jwts.builder().setSubject("YYGH-USER").setExpiration(new Date(System.currentTimeMillis() + tokenExpiration)).claim("userId", userId).claim("userName", userName).signWith(SignatureAlgorithm.HS512, tokenSignKey).compressWith(CompressionCodecs.GZIP).compact();return token;}

2. 微信登陆

2.1 业务流程

  • 打开页面后,出现二维码
    申请二维码过程略,得到id,密钥,域名放到配置文件中;
    写一个接口,返回生成二维码需要的参数
wx.open.app_id=wxed9954c01bb89b47
wx.open.app_secret=a7482517235173ddb4083788de60b90e
wx.open.redirect_url=http://guli.shop/api/ucenter/wx/callback
yygh.baseUrl=http://localhost:3000
  • 扫码,完成后绑定手机号

2.2 代码

生成扫描二维码 (参考文档)

    //1 生成微信扫描二维码//返回生成二维码需要参数@GetMapping("getLoginParam")@ResponseBodypublic Result genQrConnect() {try {Map<String, Object> map = new HashMap<>();map.put("appid", ConstantWxPropertiesUtils.WX_OPEN_APP_ID);map.put("scope","snsapi_login");String wxOpenRedirectUrl = ConstantWxPropertiesUtils.WX_OPEN_REDIRECT_URL;wxOpenRedirectUrl = URLEncoder.encode(wxOpenRedirectUrl, "utf-8");map.put("redirect_uri",wxOpenRedirectUrl);map.put("state",System.currentTimeMillis()+"");return Result.ok(map);} catch (UnsupportedEncodingException e) {e.printStackTrace();return null;}}

回调函数,扫码后的返回内容
扫码后的过程:
在这里插入图片描述
回调方法的逻辑:
在这里插入图片描述
access_token token凭证
openid 微信的唯一id,判断用户是否存在
在第三步完成后,还需要绑定手机号,返回token,跳转到前端页面
跳转前端页面:携带token,openid重定向到前端页面

    //微信扫描后回调的方法@GetMapping("callback")public String callback(String code,String state) {//第一步 获取临时票据 codeSystem.out.println("code:"+code);//第二步 拿着code和微信id和秘钥,请求微信固定地址 ,得到两个值//使用code和appid以及appscrect换取access_token//  %s   占位符StringBuffer baseAccessTokenUrl = new StringBuffer().append("https://api.weixin.qq.com/sns/oauth2/access_token").append("?appid=%s").append("&secret=%s").append("&code=%s").append("&grant_type=authorization_code");String accessTokenUrl = String.format(baseAccessTokenUrl.toString(),ConstantWxPropertiesUtils.WX_OPEN_APP_ID,ConstantWxPropertiesUtils.WX_OPEN_APP_SECRET,code);//使用httpclient请求这个地址try {String accesstokenInfo = HttpClientUtils.get(accessTokenUrl);System.out.println("accesstokenInfo:"+accesstokenInfo);//从返回字符串获取两个值 openid  和  access_tokenJSONObject jsonObject = JSONObject.parseObject(accesstokenInfo);String access_token = jsonObject.getString("access_token");String openid = jsonObject.getString("openid");//判断数据库是否存在微信的扫描人信息//根据openid判断UserInfo userInfo = userInfoService.selectWxInfoOpenId(openid);if(userInfo == null) { //数据库不存在微信信息//第三步 拿着openid  和  access_token请求微信地址,得到扫描人信息String baseUserInfoUrl = "https://api.weixin.qq.com/sns/userinfo" +"?access_token=%s" +"&openid=%s";String userInfoUrl = String.format(baseUserInfoUrl, access_token, openid);String resultInfo = HttpClientUtils.get(userInfoUrl);System.out.println("resultInfo:"+resultInfo);JSONObject resultUserInfoJson = JSONObject.parseObject(resultInfo);//解析用户信息//用户昵称String nickname = resultUserInfoJson.getString("nickname");//用户头像String headimgurl = resultUserInfoJson.getString("headimgurl");//获取扫描人信息添加数据库userInfo = new UserInfo();userInfo.setNickName(nickname);userInfo.setOpenid(openid);userInfo.setStatus(1);userInfoService.save(userInfo);}//返回name和token字符串Map<String,String> map = new HashMap<>();String name = userInfo.getName();if(StringUtils.isEmpty(name)) {name = userInfo.getNickName();}if(StringUtils.isEmpty(name)) {name = userInfo.getPhone();}map.put("name", name);//判断userInfo是否有手机号,如果手机号为空,返回openid//如果手机号不为空,返回openid值是空字符串//前端判断:如果openid不为空,绑定手机号,如果openid为空,不需要绑定手机号if(StringUtils.isEmpty(userInfo.getPhone())) {map.put("openid", userInfo.getOpenid());} else {map.put("openid", "");}//使用jwt生成token字符串String token = JwtHelper.createToken(userInfo.getId(), name);map.put("token", token);//跳转到前端页面return "redirect:" + ConstantWxPropertiesUtils.YYGH_BASE_URL + "/weixin/callback?token="+map.get("token")+ "&openid="+map.get("openid")+"&name="+URLEncoder.encode(map.get("name"),"utf-8");} catch (Exception e) {e.printStackTrace();return null;}}

2.3 OAthu2

  • (1) 开放系统间的授权

在这里插入图片描述
在这里插入图片描述
资源拥有者可以访问资源,但是客户应用不能。但是此时客户应用要访问资源,因此拥有者需要给客户应用授权。
那么如何授权呢?
采用颁发令牌的方式。接近OAuth2方式,需要考虑如何管理令牌、颁发令牌、吊销令牌,需要统一的协议,因此就有了OAuth2协议。
在这里插入图片描述

  • (2) 单点登录问题
    什么是单点登录问题?
    一个模块登录后,其他模块也可以访问,不需要再次登录。
    在这里插入图片描述
    解决方法:
    在这里插入图片描述

3. 用户认证与网关整合


把登录校验放到网关里面去。

  • Filter判断哪些需要登录校验,哪些不需要
  • 利用JWT工具从token中拿出id,若不为空,说明已经登录
package com.atguigu.yygh.gateway.filter;import com.alibaba.fastjson.JSONObject;
import com.atguigu.yygh.common.helper.JwtHelper;
import com.atguigu.yygh.common.result.Result;
import com.atguigu.yygh.common.result.ResultCodeEnum;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;import java.nio.charset.StandardCharsets;
import java.util.List;/*** <p>* 全局Filter,统一处理会员登录与外部不允许访问的服务* </p>*  * @author qy* @since 2019-11-21*/
@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {private AntPathMatcher antPathMatcher = new AntPathMatcher();@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {ServerHttpRequest request = exchange.getRequest();String path = request.getURI().getPath();System.out.println("==="+path);//内部服务接口,不允许外部访问if(antPathMatcher.match("/**/inner/**", path)) {ServerHttpResponse response = exchange.getResponse();return out(response, ResultCodeEnum.PERMISSION);}//api接口,异步请求,校验用户必须登录if(antPathMatcher.match("/api/**/auth/**", path)) {Long userId = this.getUserId(request);if(StringUtils.isEmpty(userId)) {ServerHttpResponse response = exchange.getResponse();return out(response, ResultCodeEnum.LOGIN_AUTH);}}return chain.filter(exchange);}@Overridepublic int getOrder() {return 0;}/*** api接口鉴权失败返回数据* @param response* @return*/private Mono<Void> out(ServerHttpResponse response, ResultCodeEnum resultCodeEnum) {Result result = Result.build(null, resultCodeEnum);byte[] bits = JSONObject.toJSONString(result).getBytes(StandardCharsets.UTF_8);DataBuffer buffer = response.bufferFactory().wrap(bits);//指定编码,否则在浏览器中会中文乱码response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");return response.writeWith(Mono.just(buffer));}/*** 获取当前登录用户id* @param request* @return*/private Long getUserId(ServerHttpRequest request) {String token = "";List<String> tokenList = request.getHeaders().get("token");if(null  != tokenList) {token = tokenList.get(0);}if(!StringUtils.isEmpty(token)) {return JwtHelper.getUserId(token);}return null;}
}

二. 预约挂号

1. 业务流程及模块设计

  • 展示科室信息页面:Feign调用获取就诊人接口,排班下单信息接口

  • 挂号功能,预约成功后,消息队列发送短信和更新排班数量信息(在短信和排班模块中,添加一个监听器,监听消息队列,有消息就调用方法)
    其他一些模块:用户自己的订单管理,系统

  • 支付功能

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

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

相关文章

【用unity实现100个游戏之6】制作一个战旗自走棋类游戏(附源码)

文章目录 前言导入素材开始1. 设置瓦片间隙2. 放置全图瓦片3. 美化瓦片地图4. 添加树木障碍物5. 设定不同的排序图层6. 瓦片交互6. 瓦片交互优化6. 瓦片是否允许角色7. 添加角色8. 新增游戏管理脚本9. 角色移动范围逻辑10. 角色移动范围可视化11. 角色移动12. 重置瓦片颜色12. …

C语言必会题目(2)

W...Y的主页 &#x1f60a; 代码仓库分享&#x1f495; 今天继续分享C语言必会的题目&#xff0c;上一篇文章主要是一些选择题&#xff0c;而今天我们主要内容为编程题的推荐与讲解 准备好迎接下面的题了吗&#xff1f;开始发车了&#xff01;&#xff01;&#xff01; 输入…

Linux源码剖析匿名共享内存shmem原理

如下问题如果都清楚了就不用看本文了&#xff1a; 1. shmem ram文件系统的初始化流程是怎样的 2. shmem思想上想复用基于文件的操作流程&#xff0c;实现上shmem也引入了一个文件&#xff0c;那么类似文件open会生成struct file&#xff0c;shmem的struct file怎么生成的 3.…

Ansible Playbook快速部署一主多从MySQL集群

部署目标&#xff1a; 1、快速部署一套一主两从的mysql集群 2、部署过程中支持交互式定义安装目录及监听端口号 部署清单目录结构&#xff1a; rootmaster:/opt/mysql# tree . . ├── group_vars │ └── all.yml ├── hosts ├── mysql.yml └── roles└── mys…

【Spring Cloud +Vue+UniApp】智慧建筑工地平台源码

智慧工地源码 、智慧工地云平台源码、 智慧建筑源码支持私有化部署&#xff0c;提供SaaS硬件设备运维全套服务。 前言&#xff1a;互联网建筑工地&#xff0c;是将互联网的理念和技术引入建筑工地&#xff0c;从施工现场源头抓起&#xff0c;最大程度的收集人员、安全、环境、材…

C++的六大“天选之子“拷贝构造与与运算符重载

&#x1f388;个人主页:&#x1f388; :✨✨✨初阶牛✨✨✨ &#x1f43b;推荐专栏1: &#x1f354;&#x1f35f;&#x1f32f;C语言初阶 &#x1f43b;推荐专栏2: &#x1f354;&#x1f35f;&#x1f32f;C语言进阶 &#x1f511;个人信条: &#x1f335;知行合一 &#x1f…

MySql(干货)

写这篇博客的目的不是为了将介绍原理&#xff0c;而是为了Sql中的代码操作属实太多了&#xff0c;在这里进行一个汇总&#xff0c;方便查阅&#xff01;&#xff01;&#xff01; Sql分类 分类全称说明 DDL Data Definintion Language数据定义语言&#xff0c;用来定义数据库对…

Docker知识(详细笔记)

概览图 文章目录 概览图docker 知识速查1. 初识 Docker1.1 概念1.2 特点1.3 架构1.4 应用场景1.5 安装 Docker1.6 配置 Docker 镜像 2. Docker 命令2.1 Docker 进程相关命令2.2 Docker 镜像相关命令2.3 Docker 容器相关命令 3. Docker 容器的数据卷3.1 数据卷概念及作用3.1.1 概…

jvm里的内存溢出

目录 堆溢出 虚拟机栈和本地方法栈溢出&#xff08;栈溢出很少出现&#xff09; 方法区和运行时常量池溢出 本机内存直接溢出&#xff08;实际中很少出现、了解即可&#xff09; 堆溢出 堆溢出&#xff1a;最常见的是大list&#xff0c;list里面有很多元素 堆溢出该怎么解决…

修改IDEA的idea.vmoptions参数导致IDEA无法打开(ReservedCodeCacheSize)

事发原因 Maven导依赖的时候OOM&#xff0c;因此怀疑是内存太小&#xff0c;尝试修改idea.vmoptions的参数&#xff0c;然后发现IDEA重启后打不开了&#xff0c;卸载重装后也无法打开。。。 实际上如果导包爆出OOM的话应该调整下图参数&#xff0c;不过这都是后话了 解决思路…

52.Linux学习day02 基础命令详解2

目录 Linux常见的基础命令 1.cp 2.mv 3.rm 4.find 5.grep 6.管道 | 7.wc 8.su 9.关机与重启 10.runleve Linux常见的基础命令 1.cp 用于复制文件或目录 使用 cp 命令的基本格式如下&#xff1a; cp [选项] 源文件 目标文件或目录选项&#xff1a;cp 命令支持一些选…

Spring5新功能

文章目录 前言一、整合日志功能二、Nullable注解三、函数式风格编程四、JUnit5单元测试框架总结 前言 整合日志、Nullable注解、函数式风格编程、整合JUnit5、Webflux 一、整合日志功能 Spring5移除了Log4jConfigListener&#xff0c;官方建议使用Log4j2. 依赖&#xff1a; &…

k8s 滚动更新控制(一)

在传统的应用升级时&#xff0c;通常采用的方式是先停止服务&#xff0c;然后升级部署&#xff0c;最后将新应用启动。这个过程面临一个问题&#xff0c;就是在某段时间内&#xff0c;服务是不可用的&#xff0c;对于用户来说是非常不友好的。而kubernetes滚动更新&#xff0c;…

研发工程师玩转Kubernetes——PVC使用Label和storage选择PV

在《研发工程师玩转Kubernetes——local型PV和PVC绑定过程中的状态变化》和《研发工程师玩转Kubernetes——使用local型PV在不同Pod上共享数据》中&#xff0c;我们介绍了指定VPC的spec.volumeName为PV名称来绑定它们的方法。本文将介绍PVC在创建时&#xff0c;系统自动选择绑定…

什么是DNS欺骗及如何进行DNS欺骗

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、什么是 DNS 欺骗&#xff1f;二、开始1.配置2.Ettercap启动3.操作 总结 前言 我已经离开了一段时间&#xff0c;我现在回来了&#xff0c;我终于在做一个教…

[vscode]vscode运行cmake时候exe不执行而且前面多一些字符

遇到一个奇怪问题,你单独打开cmd去执行vscode编译过程序没问题&#xff0c;但是你在vscode确不会执行&#xff0c;这是因为vscode没有读取到电脑环境变量导致加载DLL失败&#xff0c;但是在vscode终端不会给你提示少DLL&#xff0c;需要你自己把DLL复制到exe目录即可解决问题。…

Vue.js 生命周期详解

Vue.js 是一款流行的 JavaScript 框架&#xff0c;它采用了组件化的开发方式&#xff0c;使得前端开发更加简单和高效。在 Vue.js 的开发过程中&#xff0c;了解和理解 Vue 的生命周期非常重要。本文将详细介绍 Vue 生命周期的四个阶段&#xff1a;创建、挂载、更新和销毁。 …

计算机视觉的应用9-视觉领域中的61个经典数据集【大集合】的应用与实战

大家好,我是微学AI,今天给大家介绍一下计算机视觉的应用9-视觉领域中的61个经典数据集【大集合】的应用与实战,我们都知道计算机视觉是一门研究如何使计算机能够理解和解释数字图像或视频的技术和方法。在计算机视觉领域中,数据集是非常重要的资源,它们可以用于训练和评估…

从源码Debug深入spring事件机制,基于观察者模式仿写spring事件监听骨架

文章目录 1.测试案例2.DEBUG源码分析3. 异步监听4.ApplicationListener子接口5. 注解支持6. 基于观察者模式高仿spring事件监听6.1 先定义自定义一个事件6.2 定义两个监听器6.3 定义一个持有所有监听器的对象&#xff0c;类似spring的SimpleApplicationEventMulticaster6.4 事件…

什么是响应式设计?列举几种实现响应式设计的方法。

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 什么是响应式设计&#xff1f;⭐ 实现响应式设计的方法⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅&#xff01;这个专栏…