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

news/2024/11/18 3:49:51/

文章目录

  • 一. 登录系统
    • 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.ppmy.cn/news/1028493.html

相关文章

input 设置type=“number“,鼠标悬停关闭提示语

一、问题 最近刚发现input 设置type"number"之后&#xff0c;鼠标悬停会出现提示语&#xff1a;请输入有效值。两个最接近的有效值分别为xx和xx。想要输入的值确实为number格式&#xff0c;又可以输入小数&#xff0c;不限制小数位&#xff0c;所以要把这讨厌的提示去…

LeetCode 周赛上分之旅 #39 结合中心扩展的单调栈贪心问题

⭐️ 本文已收录到 AndroidFamily&#xff0c;技术和职场问题&#xff0c;请关注公众号 [彭旭锐] 和 BaguTree Pro 知识星球提问。 学习数据结构与算法的关键在于掌握问题背后的算法思维框架&#xff0c;你的思考越抽象&#xff0c;它能覆盖的问题域就越广&#xff0c;理解难度…

基于grpc从零开始搭建一个准生产分布式应用(3) - GRPC实现

本章开始会进入GRPC子专题&#xff0c;先实现前面章节中提到的例子。然后就使用的知识点展开全面的描述。本章代码任务&#xff1a;1、实现一个简单的GRPC服务&#xff1b;2、实现GRPC拦截器。 本章的代码承接上一章的代码进行迭代。因模块间存在相互依赖关系&#xff0c;读者一…

MySQL表的增删查改(基础)

目录 一&#xff0c;新增数据 &#xff08;1&#xff09;全列插入 &#xff08;2&#xff09;指定列插入 &#xff08;3&#xff09;一次性插入多条数据 二&#xff0c;查询数据 &#xff08;1&#xff09;全列查询 &#xff08;2&#xff09;指定列查询 &#xff08;3&…

接口自动化测试-Requests模块实战详解,一篇打通...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 什么是requests&a…

SpringBoot入职学习

一、前言 公司入职&#xff0c;第一个事是把公司项目运行起来。然后在经过几天的颠沛流离&#xff0c;遇到一个事情。在创建yml文件的时候&#xff0c;需要设置自己的配置文件。当然还是先跑起来项目&#xff0c;就使用别人的yml文件。但是&#xff0c;到springboot配置那里卡…

传统图像算法 - 运动目标检测之KNN运动背景分割算法

以下代码用OpenCV实现了视频中背景消除和提取的建模&#xff0c;涉及到KNN&#xff08;K近邻算法&#xff09;&#xff0c;整体效果比较好&#xff0c;可以用来进行运动状态分析。 原理如下&#xff1a; 背景建模&#xff1a;在背景分割的开始阶段&#xff0c;建立背景模型。 …

【教女朋友 从 0 到 1 学编程系列】三、2048 前端游戏实战

目录 程序思想基本样式实现JavaScript 游戏脚本课后作业从本章节起,内容将首发于 CSDN 付费专栏。同时,视频教程也在筹备中。 程序思想 自定义游戏规则: 自适应全屏,4x4 格子操作只能:上下左右 4 个动作空白处(非碰撞)随机出现一个 2 或 4操作一个方向进行相同数字合并…