曹旭辉 发表于 2023-6-17 10:36:21

尚医通-day12【token续期和就诊人管理】(内附源码)

页面预览

就诊人管理

就诊人列表

https://s2.loli.net/2023/06/17/3Wjum49GgbELkqF.png
添加就诊人

https://s2.loli.net/2023/06/17/jA8d3riRapCvSXE.png
https://s2.loli.net/2023/06/17/W86vYlO2fnrFhRT.png
https://s2.loli.net/2023/06/17/6IWQMJzoNGFna3u.png
查看就诊人

https://s2.loli.net/2023/06/17/U3MWNL4Az7y1Rat.png![image-20230225060710
https://s2.loli.net/2023/06/17/YiOjURIMvts9HbV.png
管理员系统用户管理

前面我们完成了用户登录、用户认证与就诊人管理,现在我们需要把这些信息在我们的平台管理系统中进行统一管理
用户列表

https://s2.loli.net/2023/06/17/Rew9mu86AoG5JSZ.png用户详情

https://s2.loli.net/2023/06/17/LpEKoqYNkMg1P3l.png
第01章-token刷新

简单的设置redis和cookie的过期时间,会导致用户在操作的过程中掉线,为了解决这个问题,我们可以使用token续期的方案,具体的做法是生成一个刷新token。
1、存储token

设置token与refreshToken,都包含用户信息(userId、name、headimgurl),可以一样可以不一样,refreshToken比token时间更长,如token30分钟,refreshToken60分钟
在ApiWxController的callback方法的return "redirect:"前面,将之前的生成token和存储token的相关代码封装到saveToken方法中。
将如下代码进行封装:
https://s2.loli.net/2023/06/17/mhr4oRQfLWs1j7q.png
封装成如下代码:
@Resource
private AuthContextHolder authContextHolder;UserVo userVo = new UserVo();
userVo.setName(name);
userVo.setUserId(userInfo.getId());
userVo.setHeadimgurl(userInfo.getHeadImgUrl());

authContextHolder.saveToken(userVo, response);

return "redirect:" + constantProperties.getSytBaseUrl();service-util中引入依赖
<dependency>
    <groupId>com.atguigu</groupId>
    <artifactId>model</artifactId>
    <version>1.0</version>
</dependency>在AuthContextHolder中添加如下方法:
/**
   * 将token和refreshToken保存在redis和cookie中的通用方法
   * @param userVo
   * @param response
   */
public void saveToken(UserVo userVo, HttpServletResponse response) {

    int redisMaxTime = 30;
    //生成token/refreshToken
    String token = getToken();
    String refreshToken = getToken();

    //将生成token/refreshToken存入redis:token做键,userVo做值
    redisTemplate.opsForValue()//30分钟
      .set("user:token:" + token, userVo, redisMaxTime, TimeUnit.MINUTES);
    redisTemplate.opsForValue()//60分钟
      .set("user:refreshToken:" + refreshToken, userVo, redisMaxTime * 2, TimeUnit.MINUTES);

    //将token、refreshToken和name存入cookie
    int cookieMaxTime = 60 * 30;//30分钟
    CookieUtils.setCookie(response, "token", token, cookieMaxTime);
    CookieUtils.setCookie(response, "refreshToken", refreshToken, cookieMaxTime * 2);
    CookieUtils.setCookie(response, "name", URLEncoder.encode(userVo.getName()), cookieMaxTime * 2);
    CookieUtils.setCookie(response, "headimgurl", URLEncoder.encode(userVo.getHeadimgurl()), cookieMaxTime * 2);
}private String getToken(){
    return UUID.randomUUID().toString().replaceAll("-", "");
}3、续期token

修改checkAuth方法,当token不存在时,续期token
/**
   * 检查授权状态并续期
   * @param request
   * @return
   */
public Long checkAuth(HttpServletRequest request, HttpServletResponse response){
    //从http请求头中获取token
    String token = request.getHeader("token");
    if(StringUtils.isEmpty(token)) {
      //throw new GuiguException(ResultCodeEnum.LOGIN_AUTH);
      return refreshToken(request, response);//刷新token
    }

    Object userVoObj = redisTemplate.opsForValue().get("user:token:" + token);
    if(userVoObj == null){
      //throw new GuiguException(ResultCodeEnum.LOGIN_AUTH);
      return refreshToken(request, response);//刷新token
    }

    UserVo userVo = (UserVo)userVoObj;

    return userVo.getUserId();
}/**
   * 刷新token
   * @param request
   * @param response
   * @return
   */
public Long refreshToken(HttpServletRequest request, HttpServletResponse response) {

    //从cookie中获取刷新token
    String refreshToken = CookieUtils.getCookie(request, "refreshToken");

    //从redis中根据刷新token获取用户信息
    Object userVoObj = redisTemplate.opsForValue().get("user:refreshToken:" + refreshToken);
    if(userVoObj == null) {
      //LOGIN_AURH(214, "登录过期"),
      throw new GuiguException(ResultCodeEnum.LOGIN_TIMEOUT);//登录过期
    }

    UserVo userVo = (UserVo) userVoObj;
    saveToken(userVo, response);

    return userVo.getUserId();
}4、修改checkAuth方法的调用

FrontUserInfoController和FrontFileController中的方法,添加response参数
5、cookie跨域

跨域ajax访问时,session_id不会被存储,cookie不会被传递到后端,因此需要解决cookie跨域存储的问题
解决方案:
(1)网关配置文件CorsConfig中添加如下设置:
config.setAllowCredentials(true); //避免跨域访问时session_id每次不一致(2)前端axios初始化文件request.js添加如下设置:
withCredentials: true //避免跨域访问时session_id每次不一致6、前端request.js拦截处理

如果接口返回214状态(登录超时),说明用户超时未操作,直接退出。
// http response 拦截器
service.interceptors.response.use(
        response => {

                //如果响应码是214,则需要登录超时
                if (response.data.code === 214) {
                        // to re-login
                        MessageBox.confirm('登录超时, 请重新登录', '确认退出', {
                                confirmButtonText: '重新登录',
                                cancelButtonText: '回到首页',
                                type: 'warning'
                        }).then(() => {
                                window.location.href = process.env.BASE_API + '/front/user/wx/login'
                        }).catch(()=>{
                                window.location.href = '/'
                        })
                       
                }else if (response.data.code !== 200) {
                        Message({
                                message: response.data.message,
                                type: 'error',
                                duration: 5 * 1000
                        })

                        return Promise.reject(response.data)
                } else {
                        return response.data
                }
        },
        error => {
                return Promise.reject(error.response)
        })7、myheader.vue

myheader.vue中showInfo方法的修改,保证token过期后,用户名信息依然显示
showInfo() {
    let refreshToken = cookie.get('refreshToken')
    if (refreshToken) {
      this.name = cookie.get('name')
      this.headimgurl = cookie.get('headimgurl')
    }
},myheader.vue中退出登录方法的修改,需要清空refreshToken
cookie.set('name', '', {domain: 'localhost'})
cookie.set('token', '', {domain: 'localhost'})
cookie.set('refreshToken', '', {domain: 'localhost'}) //清空refreshToken
cookie.set('headimgurl', '', {domain: 'localhost'})补充:由于我们解决了cookie的ajax传递跨域问题,因此之前通过header传输的token其实可以不必要了,我们可以直接在后端从cookie中获取token的值,如下:
String token = request.getHeader("token");

//以上可以替换为如下:

String token = CookieUtils.getCookie(request, "token");第02章-就诊人管理

1、后端接口

1.1、添加就诊人

FrontPatientController:
package com.atguigu.syt.user.controller.front;

@Api(tags = "就诊人管理")
@RestController
@RequestMapping("/front/user/patient")
public class FrontPatientController {

    @Resource
    private PatientService patientService;

    @Resource
    private AuthContextHolder authContextHolder;

    @ApiOperation("添加就诊人")
    @ApiImplicitParam(name = "patient",value = "就诊人对象", required = true)
    @PostMapping("/auth/save")
    public Result savePatient(@RequestBody Patient patient, HttpServletRequest request, HttpServletResponse response) {

      Long userId = authContextHolder.checkAuth(request, response);
      patient.setUserId(userId);
      patientService.save(patient);

      return Result.ok().message("保存成功");
    }
}1.2、修改

FrontPatientController:
@ApiOperation("修改就诊人")
@ApiImplicitParam(name = "patient",value = "就诊人对象", required = true)
@PutMapping("/auth/update")
public Result updatePatient(@RequestBody Patient patient, HttpServletRequest request, HttpServletResponse response) {

    authContextHolder.checkAuth(request, response);
    patientService.updateById(patient);
    return Result.ok().message("修改成功");
}1.3、根据id获取就诊人

FrontPatientController:
@ApiOperation("根据id获取就诊人信息")
@ApiImplicitParam(name = "id",value = "就诊人id", required = true)
@GetMapping("/auth/get/{id}")
public Result<Patient> getPatient(@PathVariable Long id, HttpServletRequest request, HttpServletResponse response) {

    Long userId = authContextHolder.checkAuth(request, response);
    //加上userId参数,只可以获取自己名下的就诊人信息
    Patient patient = patientService.getPatientById(id, userId);
    return Result.ok(patient);
}接口:PatientService
/**
   * 根据id获取本人名下的就诊人信息
   * @param id
   * @param userId
   * @return
   */
Patient getPatientById(Long id, Long userId);实现:PatientServiceImpl
@Override
public Patient getPatientById(Long id, Long userId) {

    LambdaQueryWrapper<Patient> queryWrapper = new LambdaQueryWrapper<>();
    queryWrapper.eq(Patient::getUserId, userId).eq(Patient::getId, id);
    Patient patient = baseMapper.selectOne(queryWrapper);
    //封装数据
    return this.packPatient(patient);
}辅助方法
@Resource
private DictFeignClient dictFeignClient;

@Resource
private RegionFeignClient regionFeignClient;/**
   * 封装Patient对象里面其他参数
   * @param patient
   * @return
   */
private Patient packPatient(Patient patient) {

    String certificatesTypeString = dictFeignClient.getName(DictTypeEnum.CERTIFICATES_TYPE.getDictTypeId(),patient.getCertificatesType());
    String contactsCertificatesTypeString = dictFeignClient.getName(
      DictTypeEnum.CERTIFICATES_TYPE.getDictTypeId(),patient.getContactsCertificatesType());
    String provinceString = regionFeignClient.getName(patient.getProvinceCode());
    String cityString = regionFeignClient.getName(patient.getCityCode());
    String districtString = regionFeignClient.getName(patient.getDistrictCode());

    patient.getParam().put("certificatesTypeString", certificatesTypeString);
    patient.getParam().put("contactsCertificatesTypeString", contactsCertificatesTypeString);
    patient.getParam().put("provinceString", provinceString);
    patient.getParam().put("cityString", cityString);
    patient.getParam().put("districtString", districtString);
    patient.getParam().put("fullAddress",
                        provinceString + cityString + districtString + patient.getAddress());
    return patient;
}1.4、获取就诊人列表

FrontPatientController
@ApiOperation("获取就诊人列表")
@GetMapping("/auth/findAll")
public Result<List<Patient>> findAll(HttpServletRequest request, HttpServletResponse response) {

    Long userId = authContextHolder.checkAuth(request, response);
    List<Patient> list = patientService.findByUserId(userId);
    return Result.ok(list);
}接口:PatientService
/**
   * 根据userId获取就诊人列表
   * @param userId
   * @return
   */
List<Patient> findByUserId(Long userId);实现:PatientServiceImpl
@Override
public List<Patient> findByUserId(Long userId) {

    LambdaQueryWrapper<Patient> queryWrapper = new LambdaQueryWrapper<>();
    queryWrapper.eq(Patient::getUserId, userId);

    List<Patient> patientList = baseMapper.selectList(queryWrapper);
    patientList.forEach(patient -> {
      patient.getParam().put("expenseMethod", patient.getIsInsure()==0?"自费":"医保");
    });
    return patientList;
}1.5、删除就诊人

FrontPatientController:
@ApiOperation("删除就诊人")
@DeleteMapping("/auth/remove/{id}")
public Result removePatient(@PathVariable Long id, HttpServletRequest request, HttpServletResponse response) {

    Long userId = authContextHolder.checkAuth(request, response);
    //加上userId参数,只可以删除自己名下的就诊人信息
    patientService.removeById(id, userId);
    return Result.ok();
}接口:PatientService
/**
* 根据id删除自己名下的就诊人信息
* @param id
* @param userId
*/
void removeById(Long id, Long userId);实现:PatientServiceImpl
@Override
public void removeById(Long id, Long userId) {

    LambdaQueryWrapper<Patient> queryWrapper = new LambdaQueryWrapper<>();
    queryWrapper.eq(Patient::getUserId, userId).eq(Patient::getId, id);
    baseMapper.delete(queryWrapper);
}2、前端整合

2.1、api

创建api/patient.js
import request from '~/utils/request'

export default {

save(patient) {
    return request({
      url: `/front/user/patient/auth/save`,
      method: 'post',
      data: patient
    })
},

updateById(patient) {
    return request({
      url: `/front/user/patient/auth/update`,
      method: 'put',
      data: patient
    })
},

getById(id) {
    return request({
      url: `/front/user/patient/auth/get/${id}`,
      method: 'get'
    })
},

findList() {
    return request({
      url: `/front/user/patient/auth/findAll`,
      method: `get`
    })
},

removeById(id) {
    return request({
      url: `/front/user/patient/auth/remove/${id}`,
      method: 'delete'
    })
}
}2.2、页面渲染

资料:资料>就诊人管理>
就诊人列表:pages/patient/index.vue
就诊人添加与修改 :pages/patient/add.vue
就诊人详情与删除:pages/patient/show.vue
第03章-管理系统用户管理(作业)

1、后端接口

1.1、用户列表

AdminUserInfoController:
package com.atguigu.syt.user.controller.admin;

@Api(tags = "用户管理接口")
@RestController
@RequestMapping("/admin/user/userInfo")
public class AdminUserInfoController {

    @Resource
    private UserInfoService userInfoService;

    @ApiOperation("分页条件查询")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "page",value = "页码",required = true),
            @ApiImplicitParam(name = "limit",value = "每页记录数",required = true),
            @ApiImplicitParam(name = "userInfoQueryVo",value = "查询对象",required = false)
    })
    @GetMapping("/{page}/{limit}")
    public Result<IPage<UserInfo>> list(
            @PathVariable Long page,
            @PathVariable Long limit,
            UserInfoQueryVo userInfoQueryVo
    ) {
      Page<UserInfo> pageParam = new Page<>(page,limit);
      IPage<UserInfo> pageModel = userInfoService.selectPage(pageParam, userInfoQueryVo);
      return Result.ok(pageModel);
    }
}Service接口:UserInfoService
/**
   * 查询用户分页列表
   * @param pageParam
   * @param userInfoQueryVo
   * @return
   */
IPage<UserInfo> selectPage(Page<UserInfo> pageParam, UserInfoQueryVo userInfoQueryVo);Service实现:UserInfoServiceImpl
@Override
public IPage<UserInfo> selectPage(Page<UserInfo> pageParam, UserInfoQueryVo userInfoQueryVo) {
    //UserInfoQueryVo获取条件值
    String keyword = userInfoQueryVo.getKeyword(); //用户名称
    String createTimeBegin = userInfoQueryVo.getCreateTimeBegin(); //开始时间
    String createTimeEnd = userInfoQueryVo.getCreateTimeEnd(); //结束时间
    //对条件值进行非空判断
    LambdaQueryWrapper<UserInfo> wrapper = new LambdaQueryWrapper<>();
    wrapper.and(!StringUtils.isEmpty(keyword),
                i -> i.like(UserInfo::getName, keyword).or().like(UserInfo::getPhone, keyword))
      .ge(!StringUtils.isEmpty(createTimeBegin), UserInfo::getCreateTime, createTimeBegin)
      .le(!StringUtils.isEmpty(createTimeEnd), UserInfo::getCreateTime, createTimeEnd);
    //调用mapper的方法
    IPage<UserInfo> pages = baseMapper.selectPage(pageParam, wrapper);
    //编号变成对应值封装
    pages.getRecords().forEach(this::packUserInfoForList);
    return pages;
}辅助方法:
/**
   * 封装用户状态、认证状态、证件类型信息
   * @param userInfo
   * @return
   */
private UserInfo packUserInfoForList(UserInfo userInfo) {

    //判断用户是否已经提交实名认证
    if(userInfo.getAuthStatus().intValue() != AuthStatusEnum.NO_AUTH.getStatus().intValue()){
      String certificatesTypeString = dictFeignClient.getName(
            DictTypeEnum.CERTIFICATES_TYPE.getDictTypeId(),
            userInfo.getCertificatesType()
      );
      userInfo.getParam().put("certificatesTypeString", certificatesTypeString);
    }
   
    userInfo.getParam().put("authStatusString", AuthStatusEnum.getStatusNameByStatus(userInfo.getAuthStatus()));
    userInfo.getParam().put("statusString", UserStatusEnum.getStatusNameByStatus(userInfo.getStatus()));

    return userInfo;
}1.2、锁定和解锁

Controller:在AdminUserInfoController中添加方法
@ApiOperation("锁定和解锁")
@ApiImplicitParams({
    @ApiImplicitParam(name = "userId",value = "用户id",required = true),
    @ApiImplicitParam(name = "status",value = "用户状态",required = true)
})
@PutMapping("lock/{userId}/{status}")
public Result lock(
    @PathVariable("userId") Long userId,
    @PathVariable("status") Integer status){
    boolean result = userInfoService.lock(userId, status);
    if(result){
      return Result.ok().message("设置成功");
    }else{
      return Result.ok().message("设置失败");
    }
}Service接口:UserInfoService
/**
   * 锁定和解锁用户
   * @param userId
   * @param status
   * @return
   */
boolean lock(Long userId, Integer status);Service实现:UserInfoServiceImpl
@Override
public boolean lock(Long userId, Integer status) {
    if(status == 0 || status == 1){
      UserInfo userInfo = new UserInfo();
      userInfo.setId(userId);
      userInfo.setStatus(status);
      return this.updateById(userInfo);
    }
    return false;
}1.3、用户详情

Controller:在AdminUserInfoController中添加方法
@ApiOperation("用户详情")
@ApiImplicitParam(name = "userId",value = "用户id",required = true)
@GetMapping("show/{userId}")
public Result<Map<String, Object>> show(@PathVariable Long userId) {
    return Result.ok(userInfoService.show(userId));
}Service接口:UserInfoService
/**
   * 根据用户id获取用户详情
   * @param userId
   * @return
   */
Map<String, Object> show(Long userId);Service实现:UserInfoServiceImpl
@Resource
private PatientService patientService;@Override
public Map<String, Object> show(Long userId) {
    Map<String,Object> map = new HashMap<>();
    //根据userid查询用户信息
    UserInfo userInfo = this.packUserInfo(baseMapper.selectById(userId));
    map.put("userInfo",userInfo);

    //根据userid查询就诊人信息
    List<Patient> patientList = patientService.findByUserId(userId);
    map.put("patientList",patientList);
    return map;
}1.4、用户认证审批

Controller:在AdminUserInfoController中添加方法
@ApiOperation("认证审批")
@ApiImplicitParams({
    @ApiImplicitParam(name = "userId",value = "用户id",required = true),
    @ApiImplicitParam(name = "authStatus",value = "用户认证审批状态",required = true)
})
@PutMapping("approval/{userId}/{authStatus}")
public Result approval(@PathVariable Long userId, @PathVariable Integer authStatus) {
    boolean result = userInfoService.approval(userId,authStatus);
    if(result){
      return Result.ok().message("审批成功");
    }else{
      return Result.ok().message("审批失败");
    }
}Service接口:UserInfoService
/**
   * 审核用户
   * @param userId
   * @param authStatus
   * @return
   */
boolean approval(Long userId, Integer authStatus);Service实现:UserInfoServiceImpl
/**
   * 认证审批2通过-1不通过
   * @param userId
   * @param authStatus
   * @return
   */
@Override
public boolean approval(Long userId, Integer authStatus) {
    if(authStatus == AuthStatusEnum.AUTH_SUCCESS.getStatus()
       || authStatus == AuthStatusEnum.AUTH_FAIL.getStatus()){
      UserInfo userInfo = new UserInfo();
      userInfo.setId(userId);
      userInfo.setAuthStatus(authStatus);
      return this.updateById(userInfo);
    }
    return false;
}2、前端整合

2.1、页面

在后台管理系统前端项目中添加如下页面,资料:资料>用户管理
用户列表:src/views/syt/userInfo/list.vue
用户详情,详情页面展示用户信息、用户就诊人信息:src/views/syt/userInfo/show.vue
2.2、路由

静态路由:
{
    path: '/userInfo',
    component: Layout,
    redirect: '/userInfo/list',
    name: 'userInfo',
    meta: { title: '用户管理', icon: 'user' },
    alwaysShow: true,
    children: [
      {
      path: 'list',
      name: 'userInfoList',
      component: () => import('@/views/syt/userInfo/list'),
      meta: { title: '用户列表', icon: 'el-icon-s-unfold' }
      },
      {
      path: 'show/:id',
      name: 'userInfoShow',
      component: () => import('@/views/syt/userInfo/show'),
      meta: { title: '查看用户' },
      hidden: true
      }
    ]
},动态路由:
用户管理:
https://s2.loli.net/2023/06/17/4FnKfGXRiopxkOc.png
用户列表:
https://s2.loli.net/2023/06/17/6D8eh7mfqKy2zl3.png
查看用户:
https://s2.loli.net/2023/06/17/k9BCDJOUzGp5RVe.png
2.3、api

创建src/api/syt/userInfo.js
import request from '@/utils/request'

const apiName = '/admin/user/userInfo'
export default {

//用户列表
getPageList(page, limit, searchObj) {
    return request({
      url: `${apiName}/${page}/${limit}`,
      method: 'get',
      params: searchObj
    })
},

//锁定与解锁
lock(id, status) {
    return request({
      url: `${apiName}/lock/${id}/${status}`,
      method: 'put'
    })
},

//用户详情
show(id) {
    return request({
      url: `${apiName}/show/${id}`,
      method: 'get'
    })
},

//认证审批
approval(id, authStatus) {
    return request({
      url: `${apiName}/approval/${id}/${authStatus}`,
      method: 'put'
    })
}
}源码:https://gitee.com/dengyaojava/guigu-syt-parent

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: 尚医通-day12【token续期和就诊人管理】(内附源码)