增加门户登录错误次数限制及未操作登出
diff --git a/backend/src/main/java/com/supwisdom/dlpay/framework/domain/TOperator.java b/backend/src/main/java/com/supwisdom/dlpay/framework/domain/TOperator.java
index ba7e07e..9f93636 100644
--- a/backend/src/main/java/com/supwisdom/dlpay/framework/domain/TOperator.java
+++ b/backend/src/main/java/com/supwisdom/dlpay/framework/domain/TOperator.java
@@ -74,6 +74,19 @@
   @Column(name = "jti", length = 64)
   private String jti;
 
+  /**
+   * 登录密码错误次数
+   * */
+  @Column(name = "loginpwderror")
+  private Integer loginpwderror;
+
+  /**
+   * 登录密码错误时间
+   * */
+  @Column(name = "loginpwderrortime")
+  private Long loginpwderrortime;
+
+
   @Transient
   private String rolename;
 
@@ -211,6 +224,22 @@
     this.rolename = rolename;
   }
 
+  public Integer getLoginpwderror() {
+    return loginpwderror;
+  }
+
+  public void setLoginpwderror(Integer loginpwderror) {
+    this.loginpwderror = loginpwderror;
+  }
+
+  public Long getLoginpwderrortime() {
+    return loginpwderrortime;
+  }
+
+  public void setLoginpwderrortime(Long loginpwderrortime) {
+    this.loginpwderrortime = loginpwderrortime;
+  }
+
   @Override
   public Collection<? extends GrantedAuthority> getAuthorities() {
     return this.authorities;
diff --git a/backend/src/main/java/com/supwisdom/dlpay/framework/service/impl/OperatorDetailServiceImpl.java b/backend/src/main/java/com/supwisdom/dlpay/framework/service/impl/OperatorDetailServiceImpl.java
index e1b9eec..9f82e35 100644
--- a/backend/src/main/java/com/supwisdom/dlpay/framework/service/impl/OperatorDetailServiceImpl.java
+++ b/backend/src/main/java/com/supwisdom/dlpay/framework/service/impl/OperatorDetailServiceImpl.java
@@ -9,6 +9,7 @@
 import com.supwisdom.dlpay.framework.util.StringUtil;
 import com.supwisdom.dlpay.framework.util.TradeDict;
 import com.supwisdom.dlpay.mobile.exception.PortalBusinessException;
+import com.supwisdom.dlpay.mobile.exception.UserLoginFailException;
 import com.supwisdom.dlpay.portal.bean.OperatorSearchBean;
 import com.supwisdom.dlpay.portal.bean.RoleSearchBean;
 import com.supwisdom.dlpay.portal.dao.ResourceDao;
@@ -37,10 +38,28 @@
   @Autowired
   private RoleDao roleDao;
 
+  private int MAX_PWDERROR = 5;
+
+  private int TIME_INTERVAL = 1000 * 60 * 10;
+
   @Override
   public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
     TOperator oper = operatorDao.findByOpercode(username);
-    if (null == oper) {
+    if (null != oper) {
+      long currentTime = System.currentTimeMillis();
+      if (oper.getLoginpwderror() != null &&
+          oper.getLoginpwderror() >= MAX_PWDERROR &&
+          (currentTime - oper.getLoginpwderrortime()) < TIME_INTERVAL) {
+        long minutes = (TIME_INTERVAL - (currentTime - oper.getLoginpwderrortime())) / 1000 / 60 + 1;
+        throw new UserLoginFailException("密码错误次数过多,请" + minutes + "分钟后重试");
+      } else if (oper.getLoginpwderror() != null &&
+          oper.getLoginpwderror() >= MAX_PWDERROR &&
+          (currentTime - oper.getLoginpwderrortime()) >= TIME_INTERVAL) {
+        oper.setLoginpwderror(0);
+        oper.setLoginpwderrortime(null);
+        operatorDao.save(oper);
+      }
+    } else {
       throw new UsernameNotFoundException("管理员不存在");
     }
     Collection<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>() {
@@ -93,7 +112,7 @@
 
   @Override
   public TOperator saveOrUpdateOper(TOperator operator) {
-    if (StringUtil.isEmpty(operator.getOperid())){
+    if (StringUtil.isEmpty(operator.getOperid())) {
       operator.setOpendate(DateUtil.getNow("yyyyMMdd"));
       operator.setStatus(TradeDict.STATUS_NORMAL);
       operator.setOpertype(PortalConstant.OPERTYPE_DEFAULT);
@@ -101,7 +120,7 @@
       BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
       operator.setOperpwd(encoder.encode(PortalConstant.OPERPWD_DEFAULT));
       return operatorDao.save(operator);
-    }else {
+    } else {
       TOperator op = operatorDao.findByOperid(operator.getOperid());
       if (op == null) {
         throw new PortalBusinessException("未找到待修改的管理员信息");
diff --git a/backend/src/main/java/com/supwisdom/dlpay/medical/util/MedicalConstant.java b/backend/src/main/java/com/supwisdom/dlpay/medical/util/MedicalConstant.java
index 541ba26..0132e81 100644
--- a/backend/src/main/java/com/supwisdom/dlpay/medical/util/MedicalConstant.java
+++ b/backend/src/main/java/com/supwisdom/dlpay/medical/util/MedicalConstant.java
@@ -6,6 +6,10 @@
    */
   public final static String CARD_TYPE_IDCAED = "1";//身份证
   /**
+   * 患者就诊卡类型
+   */
+  public final static Integer MEDICALCARD_HOSPITAL = 2;
+  /**
    * 患者信息认证模式
    */
   public final static String PATIENTINFO_TYPE_QUERY = "1";//查询
@@ -38,7 +42,6 @@
   public final static int MEDICAL_RESPONSE_SUCCESS = 200;//成功
 
 
-
   /**
    * 本地流水状态
    */
diff --git a/backend/src/main/kotlin/com/supwisdom/dlpay/medical/MedicalApi.kt b/backend/src/main/kotlin/com/supwisdom/dlpay/medical/MedicalApi.kt
index 81eaa06..c5660ac 100644
--- a/backend/src/main/kotlin/com/supwisdom/dlpay/medical/MedicalApi.kt
+++ b/backend/src/main/kotlin/com/supwisdom/dlpay/medical/MedicalApi.kt
@@ -161,7 +161,7 @@
      * 获取待支付列表
      */
     @PostMapping("/unpayed/list")
-    fun getUnPayedList(hospitalcode: String): JsonResult? {
+    fun getUnPayedList(hospitalcode: String, cardno: String?): JsonResult? {
         try {
             val p = SecurityContextHolder.getContext().authentication
             val user = mobileApiService.findUserById(p.name)
@@ -169,15 +169,22 @@
             if (user.userid.isNullOrEmpty()) {
                 return JsonResult.error("请绑定银行卡")
             }
-            val userInfo = userProxy.querybycardno(QueryUserParam().apply {
-                this.userid = user.userid
-            })
-            if (userInfo.retcode != 0) {
-                logger.error { "获取userid为${user.userid}的用户信息失败,错误信息:${userInfo.retmsg}" }
-                return JsonResult.error("获取用户信息失败")
+            if (!cardno.isNullOrEmpty()) {
+                val data = medicalService.getUnPayedList(
+                        user.uid, hospitalcode, cardno, MedicalConstant.ORDER_QUERYTYPE_MEDICALCARD)
+                return JsonResult.ok().put("data", data)
+            } else {
+                val userInfo = userProxy.querybycardno(QueryUserParam().apply {
+                    this.userid = user.userid
+                })
+                if (userInfo.retcode != 0) {
+                    logger.error { "获取userid为${user.userid}的用户信息失败,错误信息:${userInfo.retmsg}" }
+                    return JsonResult.error("获取用户信息失败")
+                }
+                val data = medicalService.getUnPayedList(
+                        user.uid, hospitalcode, userInfo.idno, MedicalConstant.ORDER_QUERYTYPE_IDENTITYCARD)
+                return JsonResult.ok().put("data", data)
             }
-            val data = medicalService.getUnPayedList(user.uid, userInfo.idno, hospitalcode)
-            return JsonResult.ok().put("data", data)
         } catch (e: Exception) {
             logger.error("系统异常", e)
             if (e is MedicineException) {
diff --git a/backend/src/main/kotlin/com/supwisdom/dlpay/medical/bean/UnPayedBean.kt b/backend/src/main/kotlin/com/supwisdom/dlpay/medical/bean/UnPayedBean.kt
index 1b0bd14..f455de6 100644
--- a/backend/src/main/kotlin/com/supwisdom/dlpay/medical/bean/UnPayedBean.kt
+++ b/backend/src/main/kotlin/com/supwisdom/dlpay/medical/bean/UnPayedBean.kt
@@ -6,6 +6,8 @@
     var organizationId = ""
     var queryType = 0
     var patientIdentityCardList = ArrayList<IdentityCardBean>()
+    var patientMedicalCardList = ArrayList<PatientMedicalCardBean>()
+    var patientIdList = ArrayList<String>()
 }
 
 class IdentityCardBean {
@@ -13,6 +15,11 @@
     var cardNumber: String? = ""
 }
 
+class PatientMedicalCardBean {
+    var patientMedicalCardType: Int? = 0
+    var patientMedicalCardNumber: String? = ""
+}
+
 class UnPayedResponse {
     var patientId: String? = ""
     var patientMedicalCardType: Int? = 0
diff --git a/backend/src/main/kotlin/com/supwisdom/dlpay/medical/service/MedicalService.kt b/backend/src/main/kotlin/com/supwisdom/dlpay/medical/service/MedicalService.kt
index eb6419b..512377e 100644
--- a/backend/src/main/kotlin/com/supwisdom/dlpay/medical/service/MedicalService.kt
+++ b/backend/src/main/kotlin/com/supwisdom/dlpay/medical/service/MedicalService.kt
@@ -32,7 +32,7 @@
     fun deleteAppointment(appointmentId: String, uid: String)
 
     @Transactional
-    fun getUnPayedList(uid: String, idno: String, organizationId: String): List<UnPayedDTO>
+    fun getUnPayedList(uid: String, organizationId: String, data: String, queryType: Int): List<UnPayedDTO>
 
     /**
      * 从HIS查询已支付列表
diff --git a/backend/src/main/kotlin/com/supwisdom/dlpay/medical/service/impl/MedicalServiceImpl.kt b/backend/src/main/kotlin/com/supwisdom/dlpay/medical/service/impl/MedicalServiceImpl.kt
index 58d02cc..eb8da0d 100644
--- a/backend/src/main/kotlin/com/supwisdom/dlpay/medical/service/impl/MedicalServiceImpl.kt
+++ b/backend/src/main/kotlin/com/supwisdom/dlpay/medical/service/impl/MedicalServiceImpl.kt
@@ -164,24 +164,46 @@
         appointmentDtlDao.save(appointmentDtl)
     }
 
-    fun getUnPayedResponse(organizationId: String, idno: String): UnPayedResponse {
-        val unPayedIdentityCardList = ArrayList<IdentityCardBean>()
-        unPayedIdentityCardList.add(IdentityCardBean().apply {
-            this.cardType = MedicalConstant.CARD_TYPE_IDCAED
-            this.cardNumber = idno
-        })
+    fun getUnPayedResponse(organizationId: String, data: String, queryType: Int): UnPayedResponse {
         val upPayedRequest = UnPayedRequest().apply {
             this.organizationId = organizationId
-            this.queryType = MedicalConstant.ORDER_QUERYTYPE_IDENTITYCARD
-            this.patientIdentityCardList = unPayedIdentityCardList
+            this.queryType = queryType
         }
-        //根据患者id查询未支付账单
+        when (queryType) {
+            MedicalConstant.ORDER_QUERYTYPE_IDENTITYCARD -> {
+                //  按身份类型查询
+                val unPayedIdentityCardList = ArrayList<IdentityCardBean>()
+                unPayedIdentityCardList.add(IdentityCardBean().apply {
+                    this.cardType = MedicalConstant.CARD_TYPE_IDCAED
+                    this.cardNumber = data
+                })
+                upPayedRequest.patientIdentityCardList = unPayedIdentityCardList
+            }
+            MedicalConstant.ORDER_QUERYTYPE_PATIENTID -> {
+                //  根据患者id查询
+                val unPayedPatientList = ArrayList<String>()
+                unPayedPatientList.add(data)
+                upPayedRequest.patientIdList = unPayedPatientList
+            }
+            MedicalConstant.ORDER_QUERYTYPE_MEDICALCARD -> {
+                //  按医疗就诊卡查询
+                val unPayedMedicalCardList = ArrayList<PatientMedicalCardBean>()
+                unPayedMedicalCardList.add(PatientMedicalCardBean().apply {
+                    patientMedicalCardType = MedicalConstant.MEDICALCARD_HOSPITAL
+                    patientMedicalCardNumber = data
+                })
+                upPayedRequest.patientMedicalCardList = unPayedMedicalCardList
+            }
+            else -> {
+                throw MedicineException("未知的待付账单查询模式")
+            }
+        }
         return medicalClient.getUnPayedList(upPayedRequest)
     }
 
-    override fun getUnPayedList(uid: String, idno: String, organizationId: String): List<UnPayedDTO> {
-        //根据患者id查询未支付账单
-        val unPayedResponse = getUnPayedResponse(organizationId, idno)
+    override fun getUnPayedList(uid: String, organizationId: String, data: String, queryType: Int): List<UnPayedDTO> {
+        //根据证件号或医疗卡查询未支付账单
+        val unPayedResponse = getUnPayedResponse(organizationId, data, queryType)
         val unPayedDTOList = ArrayList<UnPayedDTO>()
         //拆分每次就诊下的每个项目为一条展示数据
         unPayedResponse.medicalInformation.forEach { medicalInformation ->
@@ -293,8 +315,8 @@
             throw MedicineException("该流水状态异常,请查询后再试")
         }
         //1.从HIS中找到待支付流水
-        //1.1 根据患者id查询未支付账单
-        val unPayedResponse = getUnPayedResponse(medicalDtl.organizationid, bean.idno)
+        val unPayedResponse = getUnPayedResponse(
+                medicalDtl.organizationid, medicalDtl.patientid, MedicalConstant.ORDER_QUERYTYPE_PATIENTID)
         var mergingItems: MergingItems? = null
         unPayedResponse.medicalInformation.forEach { medicalInformation ->
             if (medicalInformation.emergencyNumber == medicalDtl.emergency) {
diff --git a/backend/src/main/kotlin/com/supwisdom/dlpay/portal/OperLoginHandler.kt b/backend/src/main/kotlin/com/supwisdom/dlpay/portal/OperLoginHandler.kt
index 7ac0a95..4861ead 100644
--- a/backend/src/main/kotlin/com/supwisdom/dlpay/portal/OperLoginHandler.kt
+++ b/backend/src/main/kotlin/com/supwisdom/dlpay/portal/OperLoginHandler.kt
@@ -4,6 +4,7 @@
 import com.supwisdom.dlpay.api.bean.JsonResult
 import com.supwisdom.dlpay.framework.core.JwtConfig
 import com.supwisdom.dlpay.framework.core.JwtTokenUtil
+import com.supwisdom.dlpay.framework.dao.OperatorDao
 import com.supwisdom.dlpay.framework.domain.JwtRedis
 import com.supwisdom.dlpay.framework.domain.TOperator
 import com.supwisdom.dlpay.framework.redisrepo.ApiJwtRepository
@@ -41,11 +42,12 @@
     @Autowired
     lateinit var systemUtilService: SystemUtilService
 
+    private val PORTAL_LOGIN_EXPIRE_IN_SECONDS = 60 * 30
+
     override fun onAuthenticationSuccess(request: HttpServletRequest, response: HttpServletResponse, authentication: Authentication) {
         val temp = authentication.principal as TOperator
         val operator = operatorDetailService.findByOperid(temp.operid)
-        val exp = systemUtilService.getSysparaValueAsInt(SysparaUtil.MOBILE_LOGIN_EXPIRE_IN_SECONDS,60*60*24*3)
-        jwtConfig.expiration = exp.toLong()
+        jwtConfig.expiration = PORTAL_LOGIN_EXPIRE_IN_SECONDS.toLong()
         if (operator != null) {
             //TODO 从数据取jwtConfig.expiration
             val token = JwtTokenUtil(jwtConfig).generateToken(
@@ -65,14 +67,18 @@
                 }
                 apiJwtRepository.save(this)
             }
+            if (operator.loginpwderror != null && operator.loginpwderror > 0) {
+                operator.loginpwderror = 0
+                operator.loginpwderrortime = null
+            }
             operator.jti = jwt.jti
             operatorDetailService.saveOper(operator)
             response.status = HttpStatus.OK.value()
             response.contentType = "application/json;charset=UTF-8"
             response.writer.write(objectMapper.writeValueAsString(JsonResult.ok()
                     .put("token", token.jwtToken)
-                    ?.put("expire",token.expiration.valueInMillis)
-                    ?.put("now",System.currentTimeMillis())
+                    ?.put("expire", token.expiration.valueInMillis)
+                    ?.put("now", System.currentTimeMillis())
                     ?.put("tenantid", "mobile")
                     ?.put("uid", operator.operid)))
         } else {
@@ -86,6 +92,8 @@
 class OperLoginFailHandler : SimpleUrlAuthenticationFailureHandler() {
     @Autowired
     lateinit var objectMapper: ObjectMapper
+    @Autowired
+    lateinit var operatorDao: OperatorDao
 
     @Throws(IOException::class, ServletException::class)
     override fun onAuthenticationFailure(request: HttpServletRequest,
@@ -97,6 +105,17 @@
             is DisabledException -> "账户已被注销,请联系管理员"
             else -> exception.message!!
         }
+        val temp = request.getParameter("username")
+        if (!temp.isNullOrEmpty()) {
+            operatorDao.findByOpercode(temp)?.let {
+                if (it.loginpwderror == null || it.loginpwderror == 0) {
+                    it.loginpwderror = 0
+                    it.loginpwderrortime = System.currentTimeMillis()
+                }
+                it.loginpwderror += 1
+                operatorDao.save(it)
+            }
+        }
         response.status = HttpStatus.OK.value()
         response.contentType = "application/json;charset=UTF-8"
         response.writer.write(objectMapper.writeValueAsString(JsonResult.error(errmsg)))
diff --git a/config/application-devel-pg.properties b/config/application-devel-pg.properties
index 2237bf3..3967e95 100644
--- a/config/application-devel-pg.properties
+++ b/config/application-devel-pg.properties
@@ -13,10 +13,11 @@
 spring.datasource.initialization-mode=always
 # Redis settings
 #spring.redis.host=ykt.supwisdom.com
-spring.redis.host=localhost
+#spring.redis.host=localhost
+spring.redis.host=172.28.201.70
 spring.redis.port=6379
-spring.redis.password=system
-spring.redis.database=2
+spring.redis.password=
+spring.redis.database=3
 #port
 server.port=8089
 # jwt settings