尚医通-day12【token续期和就诊人管理】(内附源码)
阅读原文时间:2023年08月18日阅读:4

页面预览

就诊人列表

添加就诊人

查看就诊人

![image-20230225060710

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

用户列表

用户详情

第01章-token刷新

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

设置token与refreshToken,都包含用户信息(userId、name、headimgurl),可以一样可以不一样,refreshToken比token时间更长,如token30分钟,refreshToken60分钟

ApiWxController的callback方法的return "redirect:"前面,将之前的生成token和存储token的相关代码封装到saveToken方法中。

将如下代码进行封装:

封装成如下代码:

@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("-", "");
}

修改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();
}

FrontUserInfoController和FrontFileController中的方法,添加response参数

跨域ajax访问时,session_id不会被存储,cookie不会被传递到后端,因此需要解决cookie跨域存储的问题

解决方案:

(1)网关配置文件CorsConfig中添加如下设置:

config.setAllowCredentials(true); //避免跨域访问时session_id每次不一致

(2)前端axios初始化文件request.js添加如下设置:

withCredentials: true //避免跨域访问时session_id每次不一致

如果接口返回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)
    })

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、添加就诊人

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.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、用户列表

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.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
      }
    ]
  },

动态路由:

用户管理:

用户列表:

查看用户:

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