手机接口
diff --git a/src/main/java/com/supwisdom/dlpay/api/dao/PersondtlDao.java b/src/main/java/com/supwisdom/dlpay/api/dao/PersondtlDao.java
index 467ac47..f516373 100644
--- a/src/main/java/com/supwisdom/dlpay/api/dao/PersondtlDao.java
+++ b/src/main/java/com/supwisdom/dlpay/api/dao/PersondtlDao.java
@@ -1,10 +1,13 @@
 package com.supwisdom.dlpay.api.dao;
 
 import com.supwisdom.dlpay.api.domain.TPersondtl;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
 import org.springframework.stereotype.Repository;
 
 @Repository
 public interface PersondtlDao extends JpaRepository<TPersondtl, String>,JpaSpecificationExecutor<TPersondtl> {
+    Page<TPersondtl> findByUseridAndStatus(String userid,String status, Pageable pageable);
 }
diff --git a/src/main/java/com/supwisdom/dlpay/framework/domain/JwtRedis.java b/src/main/java/com/supwisdom/dlpay/framework/domain/JwtRedis.java
index 47ece23..d32ff8e 100644
--- a/src/main/java/com/supwisdom/dlpay/framework/domain/JwtRedis.java
+++ b/src/main/java/com/supwisdom/dlpay/framework/domain/JwtRedis.java
@@ -2,9 +2,10 @@
 
 import org.springframework.data.annotation.Id;
 import org.springframework.data.redis.core.RedisHash;
+import org.springframework.data.redis.core.TimeToLive;
 
 
-@RedisHash(value = "api_jwt", timeToLive = 3600L)
+@RedisHash(value = "api_jwt")
 public class JwtRedis {
   @Id
   String jti;
@@ -13,6 +14,7 @@
 
   String uid;
 
+  @TimeToLive
   Long expiration;
 
   public String getJti() {
diff --git a/src/main/java/com/supwisdom/dlpay/framework/util/DateUtil.java b/src/main/java/com/supwisdom/dlpay/framework/util/DateUtil.java
index 8da1f6c..fe39ac8 100644
--- a/src/main/java/com/supwisdom/dlpay/framework/util/DateUtil.java
+++ b/src/main/java/com/supwisdom/dlpay/framework/util/DateUtil.java
@@ -33,6 +33,22 @@
     java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat(partten);
     return sdf.format(new Date());
   }
+  /*
+  *
+  * */
+  public static String getNowInterDay(int intervalday) {
+    try {
+      java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyyMMdd");
+      Date d = new Date();
+      Calendar calendar = Calendar.getInstance();
+      calendar.setTimeInMillis(d.getTime());
+      calendar.add(Calendar.DATE, intervalday);
+      return sdf.format(calendar.getTime());
+    } catch (Exception e) {
+      e.printStackTrace();
+      return null;
+    }
+  }
 
   /**
    * Description: 得到一个特殊的时间 @param startTime String 格式:yyyyMMddHHmmss @param
diff --git a/src/main/java/com/supwisdom/dlpay/framework/util/StringUtil.java b/src/main/java/com/supwisdom/dlpay/framework/util/StringUtil.java
index 9d44ea9..20756cc 100644
--- a/src/main/java/com/supwisdom/dlpay/framework/util/StringUtil.java
+++ b/src/main/java/com/supwisdom/dlpay/framework/util/StringUtil.java
@@ -131,6 +131,24 @@
     }*/
     return true;
   }
+  /**
+   * 手机号遮掩中间4位
+   * */
+  public static String phoneReplace(String phone){
+    return phone.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");
+  }
 
+  /**
+   * 邮箱只显示@前面的首位和末位
+   * */
+  public static String emailReplace(String s){
+    return  s.replaceAll("(\\w?)(\\w+)(\\w)(@\\w+\\.[a-z]+(\\.[a-z]+)?)", "$1****$3$4");
+  }
 
+  /**
+   * 名字显示姓
+   * */
+  public static String nameReplace(String s){
+    return  s.replaceAll("([\\d\\D]{1})(.*)", "$1**");
+  }
 }
diff --git a/src/main/java/com/supwisdom/dlpay/framework/util/TradeDict.java b/src/main/java/com/supwisdom/dlpay/framework/util/TradeDict.java
index d5c3eda..d193d3a 100644
--- a/src/main/java/com/supwisdom/dlpay/framework/util/TradeDict.java
+++ b/src/main/java/com/supwisdom/dlpay/framework/util/TradeDict.java
@@ -11,6 +11,8 @@
   public static final String STATUS_CLOSED = "closed";
   public static final String STATUS_LOCKED = "locked";
 
+  public static final String STATUS_YES = "yes";
+  public static final String STATUS_NO = "no";
   /**
    * JWT 状态
    */
diff --git a/src/main/java/com/supwisdom/dlpay/util/ConstantUtil.java b/src/main/java/com/supwisdom/dlpay/util/ConstantUtil.java
index 3d93fc1..6f56ce7 100644
--- a/src/main/java/com/supwisdom/dlpay/util/ConstantUtil.java
+++ b/src/main/java/com/supwisdom/dlpay/util/ConstantUtil.java
@@ -39,4 +39,5 @@
   * */
 
   public static final String PAGE_USERXIEYI = "xieyi";//用户协议页面
+  public static final String PAGE_BANKXIEYI = "bankxieyi";//银行协议页面
 }
diff --git a/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/user_service_impl.kt b/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/user_service_impl.kt
index 7d24186..6a17565 100644
--- a/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/user_service_impl.kt
+++ b/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/user_service_impl.kt
@@ -2,19 +2,16 @@
 
 import com.supwisdom.dlpay.api.bean.ModifyUserParam
 import com.supwisdom.dlpay.api.bean.OpenUserParam
-import com.supwisdom.dlpay.api.dao.AccountDao
-import com.supwisdom.dlpay.api.dao.PersonDao
-import com.supwisdom.dlpay.api.dao.PersonIdentityDao
-import com.supwisdom.dlpay.api.dao.PointsAccountDao
-import com.supwisdom.dlpay.api.domain.TAccount
-import com.supwisdom.dlpay.api.domain.TPerson
-import com.supwisdom.dlpay.api.domain.TPersonIdentity
-import com.supwisdom.dlpay.api.domain.TPointsAccount
+import com.supwisdom.dlpay.api.dao.*
+import com.supwisdom.dlpay.api.domain.*
 import com.supwisdom.dlpay.framework.service.SystemUtilService
 import com.supwisdom.dlpay.api.service.UserService
 import com.supwisdom.dlpay.exception.TransactionProcessException
+import com.supwisdom.dlpay.framework.domain.TOperator
 import com.supwisdom.dlpay.framework.util.*
 import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.data.domain.PageRequest
+import org.springframework.data.domain.Sort
 import org.springframework.stereotype.Service
 
 /**
@@ -32,6 +29,9 @@
     private lateinit var pointsAccountDao: PointsAccountDao
     @Autowired
     private lateinit var systemUtilService: SystemUtilService
+    @Autowired
+    private lateinit var persondtlDao: PersondtlDao
+
 
     override fun registerUser(param: OpenUserParam): TPerson {
         var person = personDao.findByIdentity(param.idtype, param.idno)
@@ -172,4 +172,20 @@
                 ?: throw TransactionProcessException(TradeErrorCode.ACCOUNT_NOT_EXISTS, "用户<$userid>不存在")
     }
 
+    override fun findPersondtlByUserid(userid: String,pageno:Int): PageResult<TPersondtl> {
+        var pageable = PageRequest.of(pageno - 1, 10, Sort.Direction.DESC, "transdate","transtime")
+        return PageResult<TPersondtl>(persondtlDao.findByUseridAndStatus(userid,TradeDict.DTL_STATUS_SUCCESS,pageable))
+    }
+
+    override fun findPersondtlDetailByUserid(userid: String, billno: String): TPersondtl? {
+        var dtl = persondtlDao.findById(billno)
+        if(dtl.isPresent){
+            if(userid!=dtl.get().userid){
+                return null
+            }
+            return dtl.get()
+        }else{
+            return null
+        }
+    }
 }
\ No newline at end of file
diff --git a/src/main/kotlin/com/supwisdom/dlpay/api/service/user_service.kt b/src/main/kotlin/com/supwisdom/dlpay/api/service/user_service.kt
index 6ac023e..f758859 100644
--- a/src/main/kotlin/com/supwisdom/dlpay/api/service/user_service.kt
+++ b/src/main/kotlin/com/supwisdom/dlpay/api/service/user_service.kt
@@ -2,10 +2,8 @@
 
 import com.supwisdom.dlpay.api.bean.ModifyUserParam
 import com.supwisdom.dlpay.api.bean.OpenUserParam
-import com.supwisdom.dlpay.api.domain.TAccount
-import com.supwisdom.dlpay.api.domain.TPerson
-import com.supwisdom.dlpay.api.domain.TPersonIdentity
-import com.supwisdom.dlpay.api.domain.TPointsAccount
+import com.supwisdom.dlpay.api.domain.*
+import com.supwisdom.dlpay.framework.util.PageResult
 import org.springframework.transaction.annotation.Propagation
 import org.springframework.transaction.annotation.Transactional
 
@@ -43,4 +41,10 @@
     @Transactional(propagation = Propagation.REQUIRED, rollbackFor = arrayOf(Exception::class), readOnly = true)
     fun findOnePersonByUserid(userid: String): TPerson
 
+    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = arrayOf(Exception::class), readOnly = true)
+    fun findPersondtlByUserid(userid:String, pageno :Int) : PageResult<TPersondtl>
+
+    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = arrayOf(Exception::class), readOnly = true)
+    fun findPersondtlDetailByUserid(userid:String, billno :String) : TPersondtl?
+
 }
\ No newline at end of file
diff --git a/src/main/kotlin/com/supwisdom/dlpay/mobile/AuthLoginHandler.kt b/src/main/kotlin/com/supwisdom/dlpay/mobile/AuthLoginHandler.kt
index 60efda7..ca0ba0a 100644
--- a/src/main/kotlin/com/supwisdom/dlpay/mobile/AuthLoginHandler.kt
+++ b/src/main/kotlin/com/supwisdom/dlpay/mobile/AuthLoginHandler.kt
@@ -2,6 +2,7 @@
 
 import com.fasterxml.jackson.databind.ObjectMapper
 import com.supwisdom.dlpay.api.bean.JsonResult
+import com.supwisdom.dlpay.api.service.UserService
 import com.supwisdom.dlpay.exception.ValidateCodeException
 import com.supwisdom.dlpay.framework.core.JwtConfig
 import com.supwisdom.dlpay.framework.core.JwtTokenUtil
@@ -9,10 +10,7 @@
 import com.supwisdom.dlpay.framework.redisrepo.ApiClientRepository
 import com.supwisdom.dlpay.framework.redisrepo.ApiJwtRepository
 import com.supwisdom.dlpay.framework.service.SystemUtilService
-import com.supwisdom.dlpay.framework.util.Constants
-import com.supwisdom.dlpay.framework.util.DateUtil
-import com.supwisdom.dlpay.framework.util.SysparaUtil
-import com.supwisdom.dlpay.framework.util.TradeDict
+import com.supwisdom.dlpay.framework.util.*
 import com.supwisdom.dlpay.mobile.dao.MobileUserDao
 import com.supwisdom.dlpay.mobile.domain.TBMobileUser
 import com.supwisdom.dlpay.mobile.exception.UserLoginFailException
@@ -42,6 +40,9 @@
     lateinit var apiJwtRepository: ApiJwtRepository
     @Autowired
     lateinit var systemUtilService: SystemUtilService
+    @Autowired
+    lateinit var userService: UserService
+
 
     override fun onAuthenticationSuccess(request: HttpServletRequest, response: HttpServletResponse, authentication: Authentication) {
         logger.error(request?.getParameter("platform"))
@@ -49,12 +50,12 @@
         var user = mobileUserDao.findByPhone(temp.phone)
         if(user!=null) {
             var exp = systemUtilService.getSysparaValueAsInt(SysparaUtil.SYSPARAID_NO3,60*60*24*3)
-            jwtConfig.expiration = exp as Long
+            jwtConfig.expiration = exp.toLong()
             val token = JwtTokenUtil(jwtConfig).generateToken(
                     mapOf("uid" to user.uid, "issuer" to "payapi",
                             "audience" to temp.phone,
                             Constants.JWT_CLAIM_TENANTID to "mobile",
-                            "authorities" to temp.authorities))
+                            Constants.JWT_CLAIM_AUTHORITIES to temp.authorities))
             var jwt = JwtRedis().apply {
                 jti = token.jti
                 uid = temp.phone
@@ -74,13 +75,30 @@
             user.lastlogin = DateUtil.getNow()
             user.jti = jwt.jti
             mobileUserDao.save(user)
+            var payseted = false
+            if(!user!!.paypwd.isNullOrEmpty()){
+                payseted = true
+            }
+            var name = ""
+            if (!user.userid.isNullOrEmpty()) {
+                var person = userService.findOnePersonByUserid(user.userid!!)
+                if (person != null) {
+                    name = person.name
+                }
+            }
+
             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("tenantid", "mobile")))
+                    ?.put("tenantid", "mobile")
+                    ?.put("name", name)
+                    ?.put("phone", StringUtil.phoneReplace(user.phone))
+                    ?.put("paypwdset",payseted)
+                    ?.put("signed", if (user.issigned.isNullOrEmpty()) "" else user.issigned)
+                    ?.put("userid",if(user.userid.isNullOrEmpty()) "" else user.userid)))
         }else{
             throw UserLoginFailException("登录错误")
         }
diff --git a/src/main/kotlin/com/supwisdom/dlpay/mobile/MobileApi.kt b/src/main/kotlin/com/supwisdom/dlpay/mobile/MobileApi.kt
index df5d623..2f02141 100644
--- a/src/main/kotlin/com/supwisdom/dlpay/mobile/MobileApi.kt
+++ b/src/main/kotlin/com/supwisdom/dlpay/mobile/MobileApi.kt
@@ -1,6 +1,10 @@
 package com.supwisdom.dlpay.mobile
 
 import com.supwisdom.dlpay.api.bean.JsonResult
+import com.supwisdom.dlpay.api.dao.PersonDao
+import com.supwisdom.dlpay.api.dao.PersonIdentityDao
+import com.supwisdom.dlpay.api.domain.TPersonIdentity
+import com.supwisdom.dlpay.api.service.UserService
 import com.supwisdom.dlpay.framework.core.JwtConfig
 import com.supwisdom.dlpay.framework.core.JwtTokenUtil
 import com.supwisdom.dlpay.framework.domain.JwtRedis
@@ -9,22 +13,20 @@
 import com.supwisdom.dlpay.framework.util.*
 import com.supwisdom.dlpay.mobile.domain.TBMobileUser
 import com.supwisdom.dlpay.mobile.service.MobileApiService
-import com.supwisdom.dlpay.mobile.service.MobileUserService
 import com.supwisdom.dlpay.util.ConstantUtil
-import com.supwisdom.dlpay.util.DlpayUtil
+import org.apache.commons.lang.StringUtils
 import org.springframework.beans.factory.annotation.Autowired
 import org.springframework.data.redis.core.RedisTemplate
+import org.springframework.security.core.GrantedAuthority
+import org.springframework.security.core.authority.AuthorityUtils
 import org.springframework.security.core.context.SecurityContextHolder
 import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
-import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler
 import org.springframework.web.bind.annotation.RequestMapping
 import org.springframework.web.bind.annotation.RestController
-import javax.servlet.http.HttpServletRequest
-import javax.servlet.http.HttpServletResponse
-import org.springframework.web.bind.annotation.RequestMethod
 import org.springframework.web.bind.annotation.RequestParam
-import java.security.Principal
 import java.time.Duration
+import java.util.Calendar
+
 
 @RestController
 @RequestMapping("/mobileapi/i")
@@ -40,9 +42,6 @@
     @Autowired
     lateinit var systemUtilService: SystemUtilService
 
-    /*
-    * TODO 防止重复调用发验证码
-    * */
     @RequestMapping("/time")
     fun time(): JsonResult {
         return JsonResult.ok("OK").put("now", System.currentTimeMillis())!!
@@ -56,7 +55,9 @@
         }
         return JsonResult.error("页面未配置")
     }
-
+    /**
+     * 注册或找回时使用的验证码生成接口
+     * */
     @RequestMapping("/code")
     fun code(@RequestParam phone: String): JsonResult {
         if (phone.isNullOrEmpty() || phone.length != 11 || !StringUtil.isMobile(phone)) {
@@ -73,10 +74,12 @@
             System.out.println(code)
             redisTemplate.opsForValue().set(phone, code, Duration.ofMinutes(5))
         }
-        //TODO code
+        //TODO call send code sdk
         return JsonResult.ok("验证码已发送")
     }
-
+    /**
+     * 注册或找回时使用的验证码校验接口
+     * */
     @RequestMapping("/checkcode")
     fun check(@RequestParam phone: String,
               @RequestParam code: String,
@@ -87,7 +90,6 @@
             if (temp != code) {
                 return JsonResult.error("验证码错误")
             }
-            //TODO general user
             var user = mobileApiService.findUserByPhone(phone)
             if (user == null) {
                 user = TBMobileUser()
@@ -100,20 +102,26 @@
                 user.loginpwderror = 0
                 user = mobileApiService.saveUser(user)
             }
+            user.registerplatform = platform
+            user.devuid = uuid
+            user = mobileApiService.saveUser(user)
             var code = RandomUtils.getRandomString(30)
-            redisTemplate.opsForValue().set(user.uid, code, Duration.ofDays(1))
-            return JsonResult.ok("OK").put("uid", user.uid)?.put("code",code)!!
+            redisTemplate.opsForValue().set(user.uid, code, Duration.ofHours(1))
+            redisTemplate.delete(phone)
+            return JsonResult.ok("OK").put("uid", user.uid)?.put("randcode", code)!!
         } else {
-            return JsonResult.error("验证码已过期,请重新发送")
+            return JsonResult.error(-1, "验证码无效或已过期,请重新获取")
         }
     }
-
+    /**
+     * 注册
+     * */
     @RequestMapping("/register")
     fun register(@RequestParam id: String,
                  @RequestParam pwd: String,
                  @RequestParam repwd: String,
                  @RequestParam random: String): JsonResult {
-        if(random.isNullOrEmpty()){
+        if (random.isNullOrEmpty()) {
             return JsonResult.error("注册信息有误,请重新注册")
         }
         if (pwd.isNullOrEmpty() || repwd.isNullOrEmpty() || pwd.length < 6) {
@@ -123,19 +131,21 @@
             return JsonResult.error("两次密码不一致")
         }
         var user: TBMobileUser? = mobileApiService.findUserById(id) ?: return JsonResult.error("用户不存在,请注册")
-        var code =   redisTemplate.opsForValue().get(id)
-        if(code!=random){
-            return JsonResult.error("注册信息有误,请重新注册")
+        var code = redisTemplate.opsForValue().get(id)
+        if (random != code) {
+            return JsonResult.error("注册信息有误,请返回上一步,并重新发送验证码")
         }
         val encoder = BCryptPasswordEncoder()
         user!!.loginpwd = encoder.encode(pwd)
-        var exp = systemUtilService.getSysparaValueAsInt(SysparaUtil.SYSPARAID_NO3,60*60*24*3)
-        jwtConfig.expiration = exp as Long
+        var exp = systemUtilService.getSysparaValueAsInt(SysparaUtil.SYSPARAID_NO3, 60 * 60 * 24 * 3)
+        jwtConfig.expiration = exp.toLong()
+        var authorities: Collection<GrantedAuthority> = AuthorityUtils.createAuthorityList("ROLE_USER")
+        user.auths = authorities
         val token = JwtTokenUtil(jwtConfig).generateToken(
                 mapOf("uid" to user.uid, "issuer" to "payapi",
                         "audience" to user.phone,
                         Constants.JWT_CLAIM_TENANTID to "mobile",
-                        "authorities" to user.authorities))
+                        Constants.JWT_CLAIM_AUTHORITIES to user.authorities))
         var jwt = JwtRedis().apply {
             jti = token.jti
             uid = user.phone
@@ -143,7 +153,7 @@
             expiration = token.expiration.valueInMillis
         }.apply {
             //删除之前的token
-            if(!user.jti.isNullOrEmpty()){
+            if (!user.jti.isNullOrEmpty()) {
                 apiJwtRepository.deleteById(user.jti!!)
             }
             apiJwtRepository.save(this)
@@ -155,10 +165,18 @@
         user.lastlogin = DateUtil.getNow()
         user.jti = jwt.jti
         mobileApiService.saveUser(user)
-
+        redisTemplate.delete(user.uid)
+        var payseted = false
+        if (!user!!.paypwd.isNullOrEmpty()) {
+            payseted = true
+        }
         return JsonResult.ok("OK").put("token", token.jwtToken)
-                ?.put("expire",token.expiration.valueInMillis)
-                ?.put("now",System.currentTimeMillis())
+                ?.put("userid", if (user.userid.isNullOrEmpty()) "" else user.userid)
+                ?.put("expire", token.expiration.valueInMillis)
+                ?.put("now", System.currentTimeMillis())
+                ?.put("phone", StringUtil.phoneReplace(user.phone))
+                ?.put("paypwdset", payseted)
+                ?.put("signed", if (user.issigned.isNullOrEmpty()) "" else user.issigned)
                 ?.put("tenantid", "mobile")!!
     }
 }
@@ -169,19 +187,290 @@
 class ApiV1 {
     @Autowired
     lateinit var mobileApiService: MobileApiService
-
+    @Autowired
+    lateinit var userService: UserService
     @Autowired
     lateinit var redisTemplate: RedisTemplate<String, String>
+    @Autowired
+    lateinit var personDao: PersonDao
 
+    /**
+     * 用户信息
+     * */
     @RequestMapping("/infor")
     fun getUserInfor(): JsonResult {
         val p = SecurityContextHolder.getContext().authentication
-        return JsonResult.ok("OK").put("name", p.name)?.put("now", System.currentTimeMillis())!!
+        var user: TBMobileUser? = mobileApiService.findUserById(p.name) ?: return JsonResult.error("用户不存在,请注册")
+        var payseted = false
+        if (!user!!.paypwd.isNullOrEmpty()) {
+            payseted = true
+        }
+        var name = ""
+        if (!user.userid.isNullOrEmpty()) {
+            var person = userService.findOnePersonByUserid(user.userid!!)
+            if (person != null) {
+                name = person.name
+            }
+        }
+
+        return JsonResult.ok("OK").put("now", System.currentTimeMillis())
+                ?.put("paypwdset", payseted)
+                ?.put("name", name)
+                ?.put("signed", if (user.issigned.isNullOrEmpty()) "" else user.issigned)
+                ?.put("userid", if (user?.userid.isNullOrEmpty()) "" else user?.userid)!!
+
+    }
+    /**
+     * 验证码生成,内部校验
+     * */
+    @RequestMapping("/code")
+    fun code(): JsonResult {
+        val p = SecurityContextHolder.getContext().authentication
+        var user: TBMobileUser? = mobileApiService.findUserById(p.name) ?: return JsonResult.error("用户不存在,请注册")
+        if (user!!.phone.isNullOrEmpty()) {
+            return JsonResult.error("用户不存在,请注册")
+        }
+        var temp = redisTemplate.opsForValue().get(user!!.phone)
+        if (temp.isNullOrEmpty()) {
+            var code = RandomUtils.randomNumber(6)
+            System.out.println(code)
+            redisTemplate.opsForValue().set(user!!.phone, code, Duration.ofMinutes(5))
+        }
+        //TODO call send code sdk
+        return JsonResult.ok("验证码已发送")
     }
 
-    @RequestMapping("/register")
-    fun register(): JsonResult {
+    @RequestMapping("/checkcode")
+    fun check(@RequestParam code: String
+    ): JsonResult {
         val p = SecurityContextHolder.getContext().authentication
+        var user: TBMobileUser? = mobileApiService.findUserById(p.name) ?: return JsonResult.error("用户不存在,请注册")
+        var temp = redisTemplate.opsForValue().get(user!!.phone)
+        if (!temp.isNullOrEmpty()) {
+            if (temp != code) {
+                return JsonResult.error("验证码错误")
+            }
+            var code = RandomUtils.getRandomString(30)
+            redisTemplate.opsForValue().set(user.uid, code, Duration.ofHours(1))
+            redisTemplate.delete(user!!.phone)
+            return JsonResult.ok("OK").put("randcode", code)!!
+        } else {
+            return JsonResult.error(-1, "验证码无效或已过期,请重新获取")
+        }
+    }
+
+    /**
+     * 绑卡
+     * */
+    @RequestMapping("/bindcard")
+    fun bindcard(card: String, name: String, code: String): JsonResult {
+        val p = SecurityContextHolder.getContext().authentication
+        var user: TBMobileUser? = mobileApiService.findUserById(p.name) ?: return JsonResult.error("用户不存在,请注册")
+        var phone = user!!.phone
+        var temp = redisTemplate.opsForValue().get(phone)
+        if (!temp.isNullOrEmpty()) {
+            if (temp != code) {
+                return JsonResult.error("验证码错误")
+            }
+            if (user!!.userid.isNullOrEmpty()) {
+                var identy: TPersonIdentity? = userService.findPersonIdentity(card) ?: return JsonResult.error("银行卡号有误")
+                if (identy!!.person == null || identy.status != TradeDict.STATUS_NORMAL) {
+                    return JsonResult.error("银行卡号信息有误")
+                }
+                if (identy!!.person.name != name) {
+                    return JsonResult.error("姓名有误")
+                }
+                var temp: TBMobileUser? = mobileApiService.findUserById(identy!!.person.userid)
+                if (temp != null) {
+                    return JsonResult.error("该银行卡号已被绑定,如有疑问,请联系客服")
+                }
+                user.bindtime = DateUtil.getNow()
+                user.userid = identy.person.userid
+                mobileApiService.saveUser(user)
+                redisTemplate.delete(phone)
+                var payseted = false
+                if (!user.paypwd.isNullOrEmpty()) {
+                    payseted = true
+                }
+                return JsonResult.ok("OK").put("userid", user.userid)
+                        ?.put("paypwdset", payseted)
+                        ?.put("signed", if (user.issigned.isNullOrEmpty()) "" else user.issigned)!!
+            } else {
+                return JsonResult.error(-1, "用户已绑定银行卡")
+                        .put("userid", if (user.userid.isNullOrEmpty()) "" else user.userid)!!
+            }
+        } else {
+            return JsonResult.error(-1, "验证码无效或已过期,请重新获取")
+        }
+    }
+
+    /**
+     * 支付密码
+     * */
+    @RequestMapping("/paypwd")
+    fun paypwd(pwd: String, repwd: String, oldpwd: String?, type: String, randcode: String?): JsonResult {
+        val p = SecurityContextHolder.getContext().authentication
+        var user: TBMobileUser? = mobileApiService.findUserById(p.name) ?: return JsonResult.error("用户不存在,请注册")
+        if (pwd != repwd) {
+            return JsonResult.error("两次密码不一致,请确认")
+        }
+        if (pwd.length != 6) {
+            return JsonResult.error("支付密码为6位数字")
+        }
+        if (!StringUtils.isNumeric(pwd)) {
+            return JsonResult.error("支付密码为6位数字")
+        }
+        val encoder = BCryptPasswordEncoder()
+        if (user!!.paypwd.isNullOrEmpty()) {
+            user!!.paypwd = encoder.encode(pwd)
+            mobileApiService.saveUser(user)
+            return JsonResult.ok("OK")
+                    ?.put("paypwdset", true)!!
+        } else {
+            when (type) {
+                "new" -> return JsonResult.error("支付密码已设置")
+                "renew" -> {
+                    if (oldpwd.isNullOrEmpty()) {
+                        return JsonResult.error("原支付密码错误")
+                    }
+                    val encoder = BCryptPasswordEncoder()
+                    var b = encoder.encode(oldpwd)
+                    if (b != user.paypwd) {
+                        return JsonResult.error("原支付密码错误")
+                    }
+                    user!!.paypwd = encoder.encode(pwd)
+                    mobileApiService.saveUser(user)
+                    return JsonResult.ok("OK")
+                            ?.put("paypwdset", true)!!
+                }
+                "find" -> {
+                    if (randcode.isNullOrEmpty()) {
+                        return JsonResult.error("信息有误,请返回并重新设置")
+                    }
+                    var code = redisTemplate.opsForValue().get(user.uid)
+                    if (randcode != code) {
+                        return JsonResult.error(-1, "长时间未操作,请返回上一步,并重新发送验证码")
+                    }
+                    user!!.paypwd = encoder.encode(pwd)
+                    mobileApiService.saveUser(user)
+                    redisTemplate.delete(user.uid)
+                    return JsonResult.ok("OK")
+                            ?.put("paypwdset", true)!!
+                }
+                else -> return JsonResult.error("请求错误")
+            }
+        }
+    }
+
+    /**
+     *
+     * 银行协议
+     * */
+    @RequestMapping("/bxy")
+    fun xieyi(): JsonResult {
+        var page = mobileApiService.findPageById(ConstantUtil.PAGE_BANKXIEYI)
+        if (page != null) {
+            return JsonResult.ok("OK").put("page", page.pageContent)!!
+        }
+        return JsonResult.error("页面未配置")
+    }
+
+    /**
+     *
+     * 签约银行协议
+     * */
+    @RequestMapping("/signbxy")
+    fun signbxy(agree: String): JsonResult {
+        val p = SecurityContextHolder.getContext().authentication
+        var user: TBMobileUser? = mobileApiService.findUserById(p.name) ?: return JsonResult.error("用户不存在,请注册")
+        user!!.issigned = TradeDict.STATUS_YES
+        user!!.signedtime = DateUtil.getNow()
+        mobileApiService.saveUser(user)
+        //TODO 调用第三方接口签约
+        return JsonResult.ok("ok")
+                .put("signed", if (user.issigned.isNullOrEmpty()) "" else user.issigned)!!
+    }
+
+    /**
+     * 查询账单
+     * */
+    @RequestMapping("/bills")
+    fun bills(pageno: Int): JsonResult {
+        val p = SecurityContextHolder.getContext().authentication
+        var user: TBMobileUser? = mobileApiService.findUserById(p.name) ?: return JsonResult.error("用户不存在,请注册")
+        val c = Calendar.getInstance()
+        val timeOfDay = c.get(Calendar.HOUR_OF_DAY)
+        var t = ""
+        when (timeOfDay) {
+            in 0..7 -> t = "早上好"
+            in 8..12 -> t = "上午好"
+            in 13..17 -> t = "下午好"
+            in 18..23 -> t = "晚上好"
+        }
+        if (user!!.userid.isNullOrEmpty()) {
+            return JsonResult.ok("OK").put("t",t)!!
+        }
+        var no = if (pageno <= 0) {
+            1
+        } else {
+            pageno
+        }
+        var today = DateUtil.getNow("yyyyMMdd")
+        var yester = DateUtil.getNowInterDay(-1)
+
+        var page = userService.findPersondtlByUserid(user!!.userid!!, no)
+        return JsonResult.ok("OK").put("page", page)
+                ?.put("today",today)
+                ?.put("yesterday",yester)
+                ?.put("t",t)!!
+    }
+
+    /**
+     * 账单明细
+     * */
+    @RequestMapping("/billdetail")
+    fun billdetail(billid: String): JsonResult {
+        val p = SecurityContextHolder.getContext().authentication
+        var user: TBMobileUser? = mobileApiService.findUserById(p.name) ?: return JsonResult.error("用户不存在,请注册")
+        if (user!!.userid.isNullOrEmpty()) {
+            return JsonResult.ok("OK")
+        }
+        var dtl = userService.findPersondtlDetailByUserid(user?.userid!!, billid)
+        return JsonResult.ok("OK").put("dtl", dtl)!!
+    }
+
+    /**
+     * 密码修改
+     * */
+    @RequestMapping("/pwdset")
+    fun pwdset(pwd: String, newpwd: String, renewpwd: String): JsonResult {
+        val p = SecurityContextHolder.getContext().authentication
+        var user: TBMobileUser? = mobileApiService.findUserById(p.name) ?: return JsonResult.error("用户不存在,请注册")
         return JsonResult.ok("OK")
     }
+
+    /**
+     *
+     * 市民卡挂失
+     * */
+    @RequestMapping("/cardlost")
+    fun cardlost(paypwd: String): JsonResult {
+        val p = SecurityContextHolder.getContext().authentication
+        var user: TBMobileUser? = mobileApiService.findUserById(p.name) ?: return JsonResult.error("用户不存在,请注册")
+        //TODO cardlost
+        return JsonResult.ok("ok")
+    }
+    /**
+     *
+     * 二维码在线生成
+     * */
+    @RequestMapping("/qrcode")
+    fun qrcode(): JsonResult {
+        val p = SecurityContextHolder.getContext().authentication
+        var user: TBMobileUser? = mobileApiService.findUserById(p.name) ?: return JsonResult.error("用户不存在,请注册")
+        //TODO cardlost
+        return JsonResult.ok("ok")
+    }
+
+
 }
\ No newline at end of file
diff --git a/src/main/kotlin/com/supwisdom/dlpay/mobile/domain/TBMobileUser.kt b/src/main/kotlin/com/supwisdom/dlpay/mobile/domain/TBMobileUser.kt
index d0fbc7f..285546a 100644
--- a/src/main/kotlin/com/supwisdom/dlpay/mobile/domain/TBMobileUser.kt
+++ b/src/main/kotlin/com/supwisdom/dlpay/mobile/domain/TBMobileUser.kt
@@ -70,6 +70,12 @@
     var userid: String? = null
 
     /**
+     * 银行卡绑定时间
+     * */
+    @Column(name = "bindtime", length = 14)
+    var bindtime: String? = null
+
+    /**
      * 注册手机类型
      * */
     @Column(name = "registerplatform", length = 100)
@@ -136,4 +142,20 @@
     @Column(name = "jti", length = 64)
     var jti: String? = null
 
+    /**
+     * 签约
+     * */
+    @Column(name = "issigned", length = 20)
+    var issigned: String? = null
+    /**
+     * 签约时间
+     * */
+    @Column(name = "signedtime", length = 20)
+    var signedtime: String? = null
+
+    /**
+     * 头像
+     * */
+    @Column(name = "ulogo", length = 100)
+    var ulogo: String? = null
 }
\ No newline at end of file
diff --git a/src/main/kotlin/com/supwisdom/dlpay/mobile/service/impl/MobileUserServiceImpl.kt b/src/main/kotlin/com/supwisdom/dlpay/mobile/service/impl/MobileUserServiceImpl.kt
index 9c96ad6..197da10 100644
--- a/src/main/kotlin/com/supwisdom/dlpay/mobile/service/impl/MobileUserServiceImpl.kt
+++ b/src/main/kotlin/com/supwisdom/dlpay/mobile/service/impl/MobileUserServiceImpl.kt
@@ -27,11 +27,11 @@
         var temp = mobileUserDao.findByPhone(username!!)
         if(temp!=null) {
             if(temp.loginpwd.isNullOrEmpty()){
-                throw UserLoginFailException("用户注册后未设置登录密码,请重新注册")
+                throw UserLoginFailException("用户注册后未设置登录密码,请找回密码或重新注册")
             }
-            if (temp.loginpwderror != null && temp.loginpwderror!! >= 3 && (System.currentTimeMillis() - temp.loginpwderrortime!!) < 1000 * 60 * 30) {
-                throw UserLoginFailException("密码错误次数过多,请稍后再试")
-            } else if (temp.loginpwderror != null && temp.loginpwderror!! >= 3 && (System.currentTimeMillis() - temp.loginpwderrortime!!) > 1000 * 60 * 30) {
+            if (temp.loginpwderror != null && temp.loginpwderror!! >= 5 && (System.currentTimeMillis() - temp.loginpwderrortime!!) < 1000 * 60 * 30) {
+                throw UserLoginFailException("密码错误次数过多,请30分钟后再试")
+            } else if (temp.loginpwderror != null && temp.loginpwderror!! >= 5 && (System.currentTimeMillis() - temp.loginpwderrortime!!) > 1000 * 60 * 30) {
                 //更新时间
                 temp.loginpwderror = 0
                 temp.loginpwderrortime = null
diff --git a/src/main/kotlin/com/supwisdom/dlpay/security.kt b/src/main/kotlin/com/supwisdom/dlpay/security.kt
index cff3a80..dfe454a 100644
--- a/src/main/kotlin/com/supwisdom/dlpay/security.kt
+++ b/src/main/kotlin/com/supwisdom/dlpay/security.kt
@@ -145,6 +145,12 @@
                 response.status=HttpStatus.UNAUTHORIZED.value()
                 response.contentType = "application/json;charset=UTF-8"
                 return
+            } catch (e:Exception){
+                SecurityContextHolder.clearContext()
+                // jwt 失效后返回 401
+                response.status=HttpStatus.UNAUTHORIZED.value()
+                response.contentType = "application/json;charset=UTF-8"
+                return
             }
         }
         filterChain.doFilter(request, response)