Преглед на файлове

登录增加验证码校验

chengyao преди 3 години
родител
ревизия
e66aad0cf5

+ 15 - 0
pom.xml

@@ -36,6 +36,8 @@
         <aggregator.version>1.1.0</aggregator.version>
         <okhttp.version>4.2.2</okhttp.version>
         <easypoi.version>4.2.0</easypoi.version>
+        <patchca.version>1.1.2</patchca.version>
+        <hutool.version>5.0.7</hutool.version>
         <docker-maven-plugin.version>1.2.1</docker-maven-plugin.version>
         <docker.image.prefix>192.168.2.121:5000/diagbotcloud</docker.image.prefix>
         <registryUrl>http://192.168.2.121:5000/repository/diagbotcloud/</registryUrl>
@@ -108,6 +110,19 @@
             <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
         </dependency>
 
+        <dependency>
+            <groupId>com.bladejava</groupId>
+            <artifactId>blade-patchca</artifactId>
+            <version>${patchca.version}</version>
+
+        </dependency>
+
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-all</artifactId>
+            <version>${hutool.version}</version>
+        </dependency>
+
         <!--swagger-->
         <dependency>
             <groupId>io.springfox</groupId>

+ 1 - 0
src/main/java/com/diagbot/config/ResourceServerConfigurer.java

@@ -38,6 +38,7 @@ public class ResourceServerConfigurer extends ResourceServerConfigurerAdapter {
                 .authorizeRequests()
                 .regexMatchers(".*swagger.*", ".*v2.*", ".*webjars.*", "/druid.*", "/actuator.*", "/hystrix.*").permitAll()
                 .antMatchers("/sys/user/getJwt").permitAll()
+                .antMatchers("/sys/user/captcha").permitAll()
                 .antMatchers("/sys/user/getHospitalMark").permitAll()
                 .antMatchers("/sys/user/getJwtNoPass").permitAll()
                 .antMatchers("/sys/user/refreshJwt").permitAll()

+ 1 - 0
src/main/java/com/diagbot/config/security/UrlAccessDecisionManager.java

@@ -81,6 +81,7 @@ public class UrlAccessDecisionManager implements AccessDecisionManager {
                 || matchers("/actuator/**", request)
                 || matchers("/hystrix/**", request)
                 || matchers("/sys/user/getJwt", request)
+                || matchers("/sys/user/captcha", request)
                 || matchers("/sys/user/getHospitalMark", request)
                 || matchers("/sys/user/getJwtNoPass", request)
                 || matchers("/sys/user/refreshJwt", request)

+ 25 - 0
src/main/java/com/diagbot/dto/ImageCaptchaDTO.java

@@ -0,0 +1,25 @@
+package com.diagbot.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+/**
+ * @Author chengyao
+ * @Date 2021/10/14 20:01
+ * @Version 1.0
+ */
+
+@Data
+@AllArgsConstructor
+public class ImageCaptchaDTO {
+    /**
+     * 图片验证码文字内容
+     */
+    private String words;
+    /**
+     * 返回内容,例如正常的Base64或以Web的image标签识别的base64格式
+     */
+    private String returnContent;
+    //图片唯一id
+    private String captchaId;
+}

+ 61 - 0
src/main/java/com/diagbot/entity/ImageCaptchaParams.java

@@ -0,0 +1,61 @@
+package com.diagbot.entity;
+import lombok.Data;
+
+
+/**
+ * @Author chengyao
+ * @Date 2021/10/14 19:57
+ * @Version 1.0
+ */
+
+@Data
+public class ImageCaptchaParams {
+    /**
+     * 默认图片验证码的文字内容
+     */
+    public static final String DEFAULT_WORDS = "123456789abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ";
+    public static final String DEFAULT_FORMAT = "png";
+    /**
+     * 图片验证码宽度
+     */
+    private int width;
+    /**
+     * 图片验证码高度
+     */
+    private int height;
+    /**
+     * 最多生成几个文字
+     */
+    private int maxWordAmount;
+    /**
+     * 最小生成几个文字
+     */
+    private int minWordAmount;
+    /**
+     * 字体最大尺寸
+     */
+    private int maxFontSize;
+    /**
+     * 文字最小尺寸
+     */
+    private int minFontSize;
+    /**
+     * 生成图片验证码内容的字体
+     */
+    private String words;
+    /**
+     * 图片类型
+     */
+    private String format;
+
+    public ImageCaptchaParams(){
+        this.width = 105;
+        this.height = 35;
+        this.maxWordAmount = 4;
+        this.minWordAmount = 4;
+        this.minFontSize = 24;
+        this.maxFontSize = 25;
+        this.words = DEFAULT_WORDS;
+        this.format = DEFAULT_FORMAT;
+    }
+}

+ 18 - 0
src/main/java/com/diagbot/facade/BasDoctorInfoFacade.java

@@ -15,6 +15,7 @@ import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
+import java.util.stream.Collectors;
 
 /**
  * @Description:
@@ -28,8 +29,25 @@ public class BasDoctorInfoFacade extends BasDoctorInfoServiceImpl {
     @Autowired
     BasDeptInfoFacade basDeptInfoFacade;
     @Autowired
+    BasDoctorInfoFacade basDoctorInfoFacade;
+    @Autowired
     BehospitalInfoFacade behospitalInfoFacade;
 
+    /**
+     * 获取医院医生下拉列表信息
+     *
+     * @param basDoctorInfoVO
+     * @return 医院医生下拉列表信息
+     */
+    public List<String> getDoctorProfessorList(BasDoctorInfoVO basDoctorInfoVO) {
+        basDoctorInfoVO.setHospitalId(Long.valueOf(SysUserUtils.getCurrentHospitalID()));
+       return basDoctorInfoFacade.lambdaQuery()
+                .eq(BasDoctorInfo::getHospitalId, basDoctorInfoVO.getHospitalId())
+                .eq(BasDoctorInfo::getIsDeleted, IsDeleteEnum.N.getKey())
+                .orderByAsc(BasDoctorInfo::getProfessor)
+                .list().stream().filter(obj->!"NULL".equals(obj.getProfessor()) && StringUtils.isNotBlank(obj.getProfessor())).map(BasDoctorInfo::getProfessor).distinct().collect(Collectors.toList());
+    }
+
     /**
      * 获取医院医生下拉列表信息
      *

+ 28 - 2
src/main/java/com/diagbot/facade/SysUserFacade.java

@@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.diagbot.client.AuthServiceClient;
 import com.diagbot.dto.*;
 import com.diagbot.entity.BasHospitalInfo;
+import com.diagbot.entity.ImageCaptchaParams;
 import com.diagbot.entity.JWT;
 import com.diagbot.entity.JwtStore;
 import com.diagbot.entity.SysHospitalSet;
@@ -26,7 +27,9 @@ import com.diagbot.service.impl.SysUserServiceImpl;
 import com.diagbot.util.BeanUtil;
 import com.diagbot.util.DateUtil;
 import com.diagbot.util.EntityUtil;
+import com.diagbot.util.ImageCaptchaUtil;
 import com.diagbot.util.ListUtil;
+import com.diagbot.util.RedisUtils;
 import com.diagbot.util.StringUtil;
 import com.diagbot.util.SysUserUtils;
 import com.diagbot.vo.BasDeptInfoVO;
@@ -41,7 +44,9 @@ import org.springframework.security.crypto.password.PasswordEncoder;
 import org.springframework.security.oauth2.common.OAuth2AccessToken;
 import org.springframework.stereotype.Component;
 import org.springframework.util.DigestUtils;
+import org.springframework.util.StringUtils;
 
+import java.io.IOException;
 import java.util.*;
 import java.util.stream.Collectors;
 
@@ -87,7 +92,8 @@ public class SysUserFacade extends SysUserServiceImpl {
     private SysDictionaryFacade sysDictionaryFacade;
     @Autowired
     private SysHospitalSetFacade sysHospitalSetFacade;
-
+    @Autowired
+    private RedisUtils redisUtils;
 
 
 
@@ -109,13 +115,22 @@ public class SysUserFacade extends SysUserServiceImpl {
         }
         return mark;
     }
+
+    public ImageCaptchaDTO getCaptcha() throws IOException {
+        ImageCaptchaDTO base64ForWeb = ImageCaptchaUtil.createBase64ForWeb(new ImageCaptchaParams());
+        String key = UUID.randomUUID().toString();
+        base64ForWeb.setCaptchaId(key);
+        redisUtils.set(key, base64ForWeb.getWords(), 60*3);
+        return base64ForWeb;
+    };
+
     /**
      * 获取jwt
      * @param username 用户名
      * @param password 密码
      * @return jwt
      */
-    public JwtDTO getJwt(String username, String password) {
+    public JwtDTO getJwt(String username, String password, String captcha, String captchaId) {
         JwtDTO data = new JwtDTO();
         if (StringUtil.isBlank(username)) {
             throw new CommonException(CommonErrorCode.PARAM_IS_NULL,
@@ -125,6 +140,17 @@ public class SysUserFacade extends SysUserServiceImpl {
             throw new CommonException(CommonErrorCode.PARAM_IS_NULL,
                     "请输入密码");
         }
+        if (StringUtils.isEmpty(captcha)) {
+            throw new CommonException(CommonErrorCode.PARAM_IS_NULL,
+                    "请输入验证码");
+        }
+        // 验证码校验
+        Object captchaObject= redisUtils.get(captchaId);
+        redisUtils.del(captchaId);
+        if(null == captchaObject || StringUtils.isEmpty(captchaObject.toString()) || !captchaObject.toString().trim().equalsIgnoreCase(captcha)){
+                throw new CommonException(CommonErrorCode.PARAM_IS_ERROR,"验证码错误");
+            }
+
         //使用MD5对密码进行加密
         String MD5Password = DigestUtils.md5DigestAsHex(password.getBytes());
         QueryWrapper<SysUser> userQueryWrapper = new QueryWrapper<>();

+ 77 - 0
src/main/java/com/diagbot/util/ImageCaptchaUtil.java

@@ -0,0 +1,77 @@
+package com.diagbot.util;
+
+import cn.hutool.core.codec.Base64;
+import com.diagbot.dto.ImageCaptchaDTO;
+import com.diagbot.entity.ImageCaptchaParams;
+import org.patchca.color.SingleColorFactory;
+import org.patchca.filter.predefined.CurvesRippleFilterFactory;
+import org.patchca.font.RandomFontFactory;
+import org.patchca.service.ConfigurableCaptchaService;
+import org.patchca.utils.encoder.EncoderHelper;
+import org.patchca.word.RandomWordFactory;
+
+import java.awt.*;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * @author huangrusheng
+ * @version 1.0
+ * @date 2021/5/20 9:54
+ */
+public final class ImageCaptchaUtil {
+
+    private ImageCaptchaUtil(){}
+    /**
+     * 流方式输出
+     * @param params
+     * @param outputStream
+     * @return 图片文字内容
+     * @throws IOException
+     */
+    public static String create(ImageCaptchaParams params, OutputStream outputStream) throws IOException{
+        ConfigurableCaptchaService  captchaService = new ConfigurableCaptchaService();
+        captchaService.setColorFactory(new SingleColorFactory(Color.black));
+        CurvesRippleFilterFactory curvesRippleFilterFactory = new CurvesRippleFilterFactory();
+        curvesRippleFilterFactory.setColorFactory(captchaService.getColorFactory());
+        curvesRippleFilterFactory.setStrokeMin(1);
+        curvesRippleFilterFactory.setStrokeMax(1.5f);
+        captchaService.setFilterFactory(curvesRippleFilterFactory);
+        captchaService.setHeight(params.getHeight());
+        captchaService.setWidth(params.getWidth());
+        RandomWordFactory wordFactory = new RandomWordFactory();
+        wordFactory.setCharacters(params.getWords());
+        wordFactory.setMaxLength(params.getMaxWordAmount());
+        wordFactory.setMinLength(params.getMinWordAmount());
+        captchaService.setWordFactory(wordFactory);
+        RandomFontFactory fontFactory = new RandomFontFactory();
+        fontFactory.setMaxSize(params.getMaxFontSize());
+        fontFactory.setMinSize(params.getMinFontSize());
+        captchaService.setFontFactory(fontFactory);
+        return EncoderHelper.getChallangeAndWriteImage(captchaService,params.getFormat(),outputStream);
+    }
+    /**
+     * 以base64方式返回
+     * @param params
+     * @return 图片文字内容+base64格式的图片
+     * @throws IOException
+     */
+    public static ImageCaptchaDTO createBase64(ImageCaptchaParams params) throws IOException{
+        ByteArrayOutputStream outputStream = new ByteArrayOutputStream(1024 * 3);
+        String words = create(params,outputStream);
+        return new ImageCaptchaDTO(words, Base64.encode(outputStream.toByteArray()),"");
+    }
+    /**
+     * 以Web的img识别的base64返回
+     * @param params
+     * @return 图片文字内容+base64格式的img标签识别的图片
+     * @throws IOException
+     */
+    public static ImageCaptchaDTO createBase64ForWeb(ImageCaptchaParams params) throws IOException{
+        ByteArrayOutputStream outputStream = new ByteArrayOutputStream(1024 * 3);
+        String words = create(params,outputStream);
+        String encode = Base64.encode(outputStream.toByteArray());
+        return new ImageCaptchaDTO(words,encode,"");
+    }
+}

+ 611 - 0
src/main/java/com/diagbot/util/RedisUtils.java

@@ -0,0 +1,611 @@
+
+package com.diagbot.util;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.CollectionUtils;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+@Component
+public class RedisUtils {
+@Autowired
+    private RedisTemplate<String, Object> redisTemplate;
+
+    public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
+        this.redisTemplate = redisTemplate;
+    }
+    // =============================common============================
+
+    /**
+     * 普通缓存获取
+     *
+     * @param
+     * @return 值
+     */
+    @Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW)
+    public Set<String> keys(String pattern) {
+        return redisTemplate.keys(pattern);
+    }
+
+    /**
+     * 指定缓存失效时间
+     *
+     * @param key  键
+     * @param time 时间(秒)
+     * @return
+     */
+    public boolean expire(String key, long time) {
+        try {
+            if (time > 0) {
+                redisTemplate.expire(key, time, TimeUnit.SECONDS);
+            }
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 根据key 获取过期时间
+     *
+     * @param key 键 不能为null
+     * @return 时间(秒) 返回0代表为永久有效
+     */
+    @Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW)
+    public long getExpire(String key) {
+        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
+    }
+
+    /**
+     * 判断key是否存在
+     *
+     * @param key 键
+     * @return true 存在 false不存在
+     */
+    @Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW)
+    public boolean hasKey(String key) {
+        try {
+            return redisTemplate.hasKey(key);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 删除缓存
+     *
+     * @param key 可以传一个值 或多个
+     */
+    @SuppressWarnings("unchecked")
+    public void del(String... key) {
+        if (key != null && key.length > 0) {
+            if (key.length == 1) {
+                redisTemplate.delete(key[0]);
+            } else {
+                redisTemplate.delete(CollectionUtils.arrayToList(key));
+            }
+        }
+    }
+
+    // ============================String=============================
+
+    /**
+     * 普通缓存获取
+     *
+     * @param key 键
+     * @return 值
+     */
+    @Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW)
+    public Object get(String key) {
+        return key == null ? null : redisTemplate.opsForValue().get(key);
+    }
+
+    /**
+     * 普通缓存放入
+     *
+     * @param key   键
+     * @param value 值
+     * @return true成功 false失败
+     */
+    public boolean set(String key, Object value) {
+        try {
+            redisTemplate.opsForValue().set(key, value);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+
+    }
+
+    /**
+     * 普通缓存放入并设置时间
+     *
+     * @param key   键
+     * @param value 值
+     * @param time  时间(秒) time要大于0 如果time小于等于0 将设置无限期
+     * @return true成功 false 失败
+     */
+    public boolean set(String key, Object value, long time) {
+        try {
+            if (time > 0) {
+                redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
+            } else {
+                set(key, value);
+            }
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 递增
+     *
+     * @param key   键
+     * @param delta 要增加几(大于0)
+     * @return
+     */
+    public long incr(String key, long delta) {
+        if (delta < 0) {
+            throw new RuntimeException("递增因子必须大于0");
+        }
+        return redisTemplate.opsForValue().increment(key, delta);
+    }
+
+    /**
+     * 递减
+     *
+     * @param key   键
+     * @param delta 要减少几(小于0)
+     * @return
+     */
+    public long decr(String key, long delta) {
+        if (delta < 0) {
+            throw new RuntimeException("递减因子必须大于0");
+        }
+        return redisTemplate.opsForValue().increment(key, -delta);
+    }
+
+    // ================================Map=================================
+
+    /**
+     * HashGet
+     *
+     * @param key  键 不能为null
+     * @param item 项 不能为null
+     * @return 值
+     */
+    @Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW)
+    public Object hget(String key, String item) {
+        return redisTemplate.opsForHash().get(key, item);
+    }
+
+    /**
+     * 获取hashKey对应的所有键值
+     *
+     * @param key 键
+     * @return 对应的多个键值
+     */
+    @Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW)
+    public Map<Object, Object> hmget(String key) {
+        return redisTemplate.opsForHash().entries(key);
+    }
+
+    /**
+     * HashSet
+     *
+     * @param key 键
+     * @param map 对应多个键值
+     * @return true 成功 false 失败
+     */
+    public boolean hmset(String key, Map<String, Object> map) {
+        try {
+            redisTemplate.opsForHash().putAll(key, map);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * HashSet 并设置时间
+     *
+     * @param key  键
+     * @param map  对应多个键值
+     * @param time 时间(秒)
+     * @return true成功 false失败
+     */
+    public boolean hmset(String key, Map<String, Object> map, long time) {
+        try {
+            redisTemplate.opsForHash().putAll(key, map);
+            if (time > 0) {
+                expire(key, time);
+            }
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 向一张hash表中放入数据,如果不存在将创建
+     *
+     * @param key   键
+     * @param item  项
+     * @param value 值
+     * @return true 成功 false失败
+     */
+    public boolean hset(String key, String item, Object value) {
+        try {
+            redisTemplate.opsForHash().put(key, item, value);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 向一张hash表中放入数据,如果不存在将创建
+     *
+     * @param key   键
+     * @param item  项
+     * @param value 值
+     * @param time  时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
+     * @return true 成功 false失败
+     */
+    public boolean hset(String key, String item, Object value, long time) {
+        try {
+            redisTemplate.opsForHash().put(key, item, value);
+            if (time > 0) {
+                expire(key, time);
+            }
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 删除hash表中的值
+     *
+     * @param key  键 不能为null
+     * @param item 项 可以使多个 不能为null
+     */
+    public void hdel(String key, Object... item) {
+        redisTemplate.opsForHash().delete(key, item);
+    }
+
+    /**
+     * 判断hash表中是否有该项的值
+     *
+     * @param key  键 不能为null
+     * @param item 项 不能为null
+     * @return true 存在 false不存在
+     */
+    @Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW)
+    public boolean hHasKey(String key, String item) {
+        return redisTemplate.opsForHash().hasKey(key, item);
+    }
+
+    /**
+     * hash递增 如果不存在,就会创建一个 并把新增后的值返回
+     *
+     * @param key  键
+     * @param item 项
+     * @param by   要增加几(大于0)
+     * @return
+     */
+    public double hincr(String key, String item, double by) {
+        return redisTemplate.opsForHash().increment(key, item, by);
+    }
+
+    /**
+     * hash递减
+     *
+     * @param key  键
+     * @param item 项
+     * @param by   要减少记(小于0)
+     * @return
+     */
+    public double hdecr(String key, String item, double by) {
+        return redisTemplate.opsForHash().increment(key, item, -by);
+    }
+
+    // ============================set=============================
+
+    /**
+     * 根据key获取Set中的所有值
+     *
+     * @param key 键
+     * @return
+     */
+    @Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW)
+    public Set<Object> sGet(String key) {
+        try {
+            return redisTemplate.opsForSet().members(key);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    /**
+     * 根据value从一个set中查询,是否存在
+     *
+     * @param key   键
+     * @param value 值
+     * @return true 存在 false不存在
+     */
+    @Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW)
+    public boolean sHasKey(String key, Object value) {
+        try {
+            return redisTemplate.opsForSet().isMember(key, value);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 将数据放入set缓存
+     *
+     * @param key    键
+     * @param values 值 可以是多个
+     * @return 成功个数
+     */
+    public long sSet(String key, Object... values) {
+        try {
+            return redisTemplate.opsForSet().add(key, values);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return 0;
+        }
+    }
+
+    /**
+     * 将set数据放入缓存
+     *
+     * @param key    键
+     * @param time   时间(秒)
+     * @param values 值 可以是多个
+     * @return 成功个数
+     */
+    public long sSetAndTime(String key, long time, Object... values) {
+        try {
+            Long count = redisTemplate.opsForSet().add(key, values);
+            if (time > 0) {
+                expire(key, time);
+            }
+            return count;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return 0;
+        }
+    }
+
+    /**
+     * 获取set缓存的长度
+     *
+     * @param key 键
+     * @return
+     */
+    @Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW)
+    public long sGetSetSize(String key) {
+        try {
+            return redisTemplate.opsForSet().size(key);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return 0;
+        }
+    }
+
+    /**
+     * 移除值为value的
+     *
+     * @param key    键
+     * @param values 值 可以是多个
+     * @return 移除的个数
+     */
+    public long setRemove(String key, Object... values) {
+        try {
+            Long count = redisTemplate.opsForSet().remove(key, values);
+            return count;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return 0;
+        }
+    }
+    // ===============================list=================================
+
+    /**
+     * 获取list缓存的内容
+     *
+     * @param key   键
+     * @param start 开始
+     * @param end   结束 0 到 -1代表所有值
+     * @return
+     */
+    @Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW)
+    public List<Object> lGet(String key, long start, long end) {
+        try {
+            return redisTemplate.opsForList().range(key, start, end);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    /**
+     * 获取list缓存的长度
+     *
+     * @param key 键
+     * @return
+     */
+    @Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW)
+    public long lGetListSize(String key) {
+        try {
+            return redisTemplate.opsForList().size(key);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return 0;
+        }
+    }
+
+    /**
+     * 通过索引 获取list中的值
+     *
+     * @param key   键
+     * @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
+     * @return
+     */
+    @Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW)
+    public Object lGetIndex(String key, long index) {
+        try {
+            return redisTemplate.opsForList().index(key, index);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    /**
+     * 将list放入缓存
+     *
+     * @param key   键
+     * @param value 值
+     * @param time  时间(秒)
+     * @return
+     */
+    public boolean lSet(String key, Object value) {
+        try {
+            redisTemplate.opsForList().rightPush(key, value);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 将list放入缓存
+     *
+     * @param key   键
+     * @param value 值
+     * @param time  时间(秒)
+     * @return
+     */
+    public boolean lSet(String key, Object value, long time) {
+        try {
+            redisTemplate.opsForList().rightPush(key, value);
+            if (time > 0) {
+                expire(key, time);
+            }
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 将list放入缓存
+     *
+     * @param key   键
+     * @param value 值
+     * @param time  时间(秒)
+     * @return
+     */
+    public boolean lSet(String key, List<Object> value) {
+        try {
+            redisTemplate.opsForList().rightPushAll(key, value);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 将list放入缓存
+     *
+     * @param key   键
+     * @param value 值
+     * @param time  时间(秒)
+     * @return
+     */
+    public boolean lSet(String key, List<Object> value, long time) {
+        try {
+            redisTemplate.opsForList().rightPushAll(key, value);
+            if (time > 0) {
+                expire(key, time);
+            }
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 根据索引修改list中的某条数据
+     *
+     * @param key   键
+     * @param index 索引
+     * @param value 值
+     * @return
+     */
+    public boolean lUpdateIndex(String key, long index, Object value) {
+        try {
+            redisTemplate.opsForList().set(key, index, value);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 移除N个值为value
+     *
+     * @param key   键
+     * @param count 移除多少个
+     * @param value 值
+     * @return 移除的个数
+     */
+    public long lRemove(String key, long count, Object value) {
+        try {
+            Long remove = redisTemplate.opsForList().remove(key, count, value);
+            return remove;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return 0;
+        }
+    }
+
+    /**
+     * 按键的排序获取对应的值
+     *
+     * @param keys 多个键
+     * @return List<Object>
+     */
+    @Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW)
+    public List<Object> mget(Collection<String> keys) {
+        return redisTemplate.opsForValue().multiGet(keys);
+    }
+
+}

+ 3 - 0
src/main/java/com/diagbot/vo/UserLoginVO.java

@@ -18,5 +18,8 @@ public class UserLoginVO {
     private String username;
     @NotBlank(message = "请输入密码!")
     private String password;
+    @NotBlank(message = "请输入验证码!")
+    private String captcha;
+    private String captchaId;
 
 }

+ 0 - 1
src/main/java/com/diagbot/vo/data/BasDoctorInfoVO.java

@@ -17,7 +17,6 @@ public class BasDoctorInfoVO {
      */
     private String inputStr;
     private String deptName;
-    @ApiModelProperty(hidden = true)
     private String deptId;
     @ApiModelProperty(hidden = true)
     private Long hospitalId;

+ 8 - 0
src/main/java/com/diagbot/web/BasDoctorInfoController.java

@@ -39,4 +39,12 @@ public class BasDoctorInfoController {
     public RespDTO<List<BasDoctorInfo>> getList(@RequestBody BasDoctorInfoVO basDoctorInfoVO) {
         return RespDTO.onSuc(basDoctorInfoFacade.getDoctorByDept(basDoctorInfoVO));
     }
+
+    @ApiOperation(value = "获取病历质控一览下医院医生职称[by:cy]",
+            notes = "inputStr: 搜索参数")
+    @PostMapping("/getDoctorProfessorList")
+    @SysLogger("getDoctorProfessorList")
+    public RespDTO<List<String>> getDoctorProfessorList(@RequestBody BasDoctorInfoVO basDoctorInfoVO) {
+        return RespDTO.onSuc(basDoctorInfoFacade.getDoctorProfessorList(basDoctorInfoVO));
+    }
 }

+ 23 - 0
src/main/java/com/diagbot/web/ConsoleByDeptController.java

@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.diagbot.annotation.SysLogger;
 import com.diagbot.dto.*;
 import com.diagbot.facade.ConsoleByDeptFacade;
+import com.diagbot.facade.ConsoleFacade;
 import com.diagbot.vo.*;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
@@ -30,6 +31,8 @@ import java.util.Map;
 public class ConsoleByDeptController {
     @Autowired
     private ConsoleByDeptFacade consoleByDeptFacade;
+    @Autowired
+    private ConsoleFacade consoleFacade;
 
     //region --------------------------首页接口开始------------------------------
     @ApiOperation(value = "出院病人统计(科室)-首页[by:zhaops]",
@@ -230,6 +233,26 @@ public class ConsoleByDeptController {
     public RespDTO<List<HomePageByDeptDTO>> homePageLevelStatisticsByDept(@RequestBody @Valid FilterOrderByDeptVO filterOrderByDeptVO) {
         return RespDTO.onSuc(consoleByDeptFacade.homePageLevelStatisticsByDept(filterOrderByDeptVO));
     }
+
+    /**
+     * 医师病案首页合格率占比科室 湘雅定制
+     *
+     * @param filterOrderPageVO
+     * @return
+     */
+    @ApiOperation(value = "病案首页合格率占比[by:zhaops]",
+            notes = "type: 统计维度 1-本月,2-本年(必填)<br>" +
+                    "level: 病历等级 <br>" +
+                    "deptName: 科室名称 <br>" +
+                    "asc: 排序(升序) <br>" +
+                    "desc: 排序(降序) <br>" +
+                    "isPlacefile: 是否归档(0:未归档,1:已归档) <br>")
+    @PostMapping("/homePageLevelStatisticsXYByDept")
+    @SysLogger("homePageLevelStatisticsXYByDept")
+    public RespDTO<IPage<HomePageNumXYDTO>> homePageLevelStatisticsXY(@RequestBody @Valid FilterOrderPageVO filterOrderPageVO) {
+        return RespDTO.onSuc(consoleFacade.homePageLevelStatisticsXY(filterOrderPageVO));
+    }
+
     /**
      * 病案首页合格/不合格数
      *

+ 22 - 0
src/main/java/com/diagbot/web/ConsoleByDeptExportController.java

@@ -6,6 +6,7 @@ import com.diagbot.dto.DeptEntryNumDTO;
 import com.diagbot.dto.EntryStasByDeptDTO;
 import com.diagbot.dto.RespDTO;
 import com.diagbot.facade.ConsoleByDeptExportFacade;
+import com.diagbot.facade.ConsoleExportFacade;
 import com.diagbot.vo.*;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
@@ -33,6 +34,8 @@ public class ConsoleByDeptExportController {
 
     @Autowired
     private ConsoleByDeptExportFacade consoleByDeptExportFacade;
+    @Autowired
+    private ConsoleExportFacade consoleExportFacade;
 
 /**
      * 病案首页合格率占比科室-导出
@@ -55,6 +58,25 @@ public class ConsoleByDeptExportController {
         consoleByDeptExportFacade.homePageLevelExportByDept(response, filterOrderByDeptVO);
     }
 
+    /**
+     * 病案首页合格率占比导出-湘雅-科室
+     *
+     * @param filterOrderPageVO
+     * @return
+     */
+    @ApiOperation(value = "病案首页合格率占比导出[by:gaodm]",
+            notes = "type: 统计维度 1-本月,2-本年(必填)<br>" +
+                    "level: 病历等级 <br>" +
+                    "name: 科室名称 <br>" +
+                    "asc: 排序(升序) <br>" +
+                    "desc: 排序(降序) <br>" +
+                    "isPlacefile: 是否归档(0:未归档,1:已归档) <br>")
+    @PostMapping("/homePageLevelXYExportByDept")
+    @SysLogger("homePageLevelXYExportByDept")
+    public void homePageLevelXYExport(HttpServletResponse response, @RequestBody @Valid FilterOrderPageVO filterOrderPageVO) {
+        consoleExportFacade.homePageLevelXYExport(response, filterOrderPageVO);
+    }
+
     /**
      * 病案首页不合格/合格数病历详情页导出-科室
      *

+ 12 - 3
src/main/java/com/diagbot/web/SysUserController.java

@@ -2,6 +2,7 @@ package com.diagbot.web;
 
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.diagbot.annotation.SysLogger;
+import com.diagbot.dto.ImageCaptchaDTO;
 import com.diagbot.dto.JwtDTO;
 import com.diagbot.dto.LoginDTO;
 import com.diagbot.dto.ModifyPasswordVO;
@@ -29,8 +30,9 @@ import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 import springfox.documentation.annotations.ApiIgnore;
-
 import javax.validation.Valid;
+import java.io.IOException;
+
 
 /**
  * @Description: 用户账号API
@@ -64,11 +66,18 @@ public class SysUserController {
                     "password:密码, 必填, 默认密码:a123456<br> ")
     @PostMapping("/getJwt")
     @SysLogger("getJwt")
-    public RespDTO<JwtDTO> getJwt(@RequestBody UserLoginVO userLoginVO) {
-        JwtDTO data = userFacade.getJwt(userLoginVO.getUsername(), userLoginVO.getPassword());
+    public RespDTO<JwtDTO> getJwt(@RequestBody @Valid UserLoginVO userLoginVO) {
+        JwtDTO data = userFacade.getJwt(userLoginVO.getUsername(), userLoginVO.getPassword(),userLoginVO.getCaptcha(),userLoginVO.getCaptchaId());
         return RespDTO.onSuc(data);
     }
 
+    @ApiOperation(value = "获取验证码[by:cy]")
+    @PostMapping("/captcha")
+    @SysLogger("captcha")
+    public RespDTO<ImageCaptchaDTO> getCaptcha() throws IOException {
+        return  RespDTO.onSuc(userFacade.getCaptcha());
+    }
+
     @ApiOperation(value = "获取标识--选择登录页面[by:cy]")
     @PostMapping("/getHospitalMark")
     @SysLogger("getHospitalMark")

+ 1 - 1
src/main/resources/application-local.yml

@@ -59,7 +59,7 @@ spring:
     druid:
       driver-class-name: com.mysql.cj.jdbc.Driver
       platform: mysql
-      url: jdbc:mysql://192.168.2.236:3306/qc?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&characterSetResults=utf8&useSSL=false&allowMultiQueries=true
+      url: jdbc:mysql://192.168.2.237:3306/qc?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&characterSetResults=utf8&useSSL=false&allowMultiQueries=true
       username: root
       password: lantone
       # 连接池的配置信息

+ 1 - 1
src/main/resources/bootstrap.yml

@@ -2,7 +2,7 @@ spring:
   application:
     name: mrqc-sys
   profiles:
-    active: test
+    active: local
   main:
     allow-bean-definition-overriding: true
 

+ 3 - 0
src/main/resources/mapper/QcresultInfoMapper.xml

@@ -1748,6 +1748,9 @@
                 <![CDATA[ AND a.leave_hospital_date <= #{filterPageVO.endDate}]]>
             </if>
         </if>
+        <if test="filterPageVO.doctorName != null and filterPageVO.doctorName != ''">
+            AND a.doctor_name = #{filterPageVO.doctorName}
+        </if>
         <if test="filterPageVO.deptName != null and filterPageVO.deptName != ''">
             AND a.beh_dept_name  =  #{filterPageVO.deptName}
         </if>