更新注册登录接口

master
pan 4 years ago
parent 4667bce9a9
commit 1139824098
  1. 21
      build.gradle.kts
  2. 26
      src/AccountController.kt
  3. 91
      src/AccountService.kt
  4. 68
      src/Dao.kt
  5. 20
      src/MySQL.kt
  6. 46
      src/Service.kt
  7. 31
      src/Vo.kt
  8. 100
      test/ApplicationTest.kt

@ -24,16 +24,35 @@ repositories {
dependencies { dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version") implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version")
/**
* ktor
*/
implementation("io.ktor:ktor-server-netty:$ktor_version") implementation("io.ktor:ktor-server-netty:$ktor_version")
implementation("ch.qos.logback:logback-classic:$logback_version")
implementation("io.ktor:ktor-server-core:$ktor_version") implementation("io.ktor:ktor-server-core:$ktor_version")
/**
* https://github.com/google/gson
*/
implementation("io.ktor:ktor-gson:$ktor_version") implementation("io.ktor:ktor-gson:$ktor_version")
/**
* https://github.com/qos-ch/logback
*/
implementation("ch.qos.logback:logback-classic:$logback_version")
/**
* https://github.com/Kotlin/kotlinx.serialization
*/
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.1.0") implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.1.0")
/**
* https://github.com/JetBrains/Exposed
*/
implementation("org.jetbrains.exposed:exposed-core:$exposedVersion") implementation("org.jetbrains.exposed:exposed-core:$exposedVersion")
implementation("org.jetbrains.exposed:exposed-dao:$exposedVersion") implementation("org.jetbrains.exposed:exposed-dao:$exposedVersion")
implementation("org.jetbrains.exposed:exposed-jdbc:$exposedVersion") implementation("org.jetbrains.exposed:exposed-jdbc:$exposedVersion")
implementation("org.jetbrains.exposed:exposed-java-time:$exposedVersion")
implementation("mysql:mysql-connector-java:8.0.19") implementation("mysql:mysql-connector-java:8.0.19")
implementation("com.zaxxer:HikariCP:3.4.2") implementation("com.zaxxer:HikariCP:3.4.2")
implementation("cn.smallbun.screw:screw-core:1.0.5")
testImplementation("io.ktor:ktor-server-tests:$ktor_version") testImplementation("io.ktor:ktor-server-tests:$ktor_version")
} }

@ -1,6 +1,7 @@
package com.gyf.csams package com.gyf.csams
import io.ktor.application.* import io.ktor.application.*
import io.ktor.features.*
import io.ktor.request.* import io.ktor.request.*
import io.ktor.response.* import io.ktor.response.*
import io.ktor.routing.* import io.ktor.routing.*
@ -17,10 +18,10 @@ fun Application.AccountController() {
get(path = "/checkId") { get(path = "/checkId") {
val studentId = call.request.queryParameters["studentId"] val studentId = call.request.queryParameters["studentId"]
if (studentId?.isNotEmpty() == true) { if (studentId?.isNotEmpty() == true) {
if (Service.registered(studentId)) { if (AccountService.registered(studentId)) {
call.respond(ApiResponse(code = 200, message = "学号已注册", body = true)) call.respond(ApiResponse(message = "学号已注册", body = true))
} else { } else {
call.respond(ApiResponse(code = 200, message = "学号可注册", body = false)) call.respond(ApiResponse(message = "学号可注册", body = false))
} }
} else { } else {
call.respond(Simple.error("学号检测失败,请联系管理员")) call.respond(Simple.error("学号检测失败,请联系管理员"))
@ -32,14 +33,27 @@ fun Application.AccountController() {
*/ */
post { post {
val userVo = call.receive<UserVo>() val userVo = call.receive<UserVo>()
val userResDto = Service.register(userVo) val userResDto = AccountService.register(userVo)
if (userResDto != null) { if (userResDto != null) {
call.respond(ApiResponse(code = 200, message = "注册成功", userResDto)) call.respond(ApiResponse(message = "注册成功", body = userResDto))
} else { } else {
call.respond(ApiResponse(code = 400, message = "注册失败", body = null)) call.respond(Simple.error("注册失败"))
} }
} }
} }
route(path = "/login"){
post{
val userLoginVo= call.receive<UserLoginVo>()
val tokenResDto:TokenResDto=AccountService.login(userLoginVo,call.request.origin.remoteHost)
call.respond(ApiResponse(message = if(tokenResDto.token!=null) "登陆成功" else "账号或密码错误!!!",body = tokenResDto))
}
post(path = "/token"){
val tokenVo=call.receive<TokenVo>()
val isValid=AccountService.validToken(tokenVo)
call.respond(ApiResponse(message = if(isValid) "令牌合法" else "令牌不合法",body = isValid))
}
}
} }
} }
} }

@ -0,0 +1,91 @@
package com.gyf.csams
import org.jetbrains.exposed.sql.transactions.transaction
import org.slf4j.LoggerFactory
import java.time.ZoneOffset
class AccountService {
companion object {
private val logger = LoggerFactory.getLogger(AccountService::class.java)
/**
* 检查学号是否已注册,true=已注册
*/
fun registered(selectId: String): Boolean {
return transaction {
return@transaction !User.find { Users.studentId eq selectId }.empty()
}
}
/**
* 注册
*/
fun register(userVo: UserVo): UserResDto? {
try {
return transaction {
val _pwd = randomNum(8)
User.new {
studentId=userVo.studentId
name=userVo.name
password=_pwd.md5()
}
return@transaction UserResDto(password=_pwd)
}
} catch (e: Exception) {
logger.error("注册失败,发生异常:$e")
return null
}
}
/**
* 登录
*
* @param userLoginVo 登陆表单
*/
fun login(userLoginVo: UserLoginVo,_ip:String):TokenResDto{
return transaction {
val user=User.find { Users.studentId eq userLoginVo.studentId }.firstOrNull()
when {
user==null -> {
logger.warn("学号:${userLoginVo.studentId}不存在")
return@transaction TokenResDto(isValid = false)
}
userLoginVo.password.md5() != user.password -> {
logger.warn("密码:${userLoginVo.password}错误")
return@transaction TokenResDto(isValid = false)
}
else -> {
val token=UserToken.new{
studentId=userLoginVo.studentId
ip=_ip
device=userLoginVo.device
token=listOf(studentId,ip,device).joinToString(separator = ('a' .. 'z').random().toString()).md5()
}
return@transaction TokenResDto(isValid = true,token = Token(token = token.token,createTime = token.createTime.toEpochSecond(
ZoneOffset.of("+8")),studentId = token.studentId))
}
}
}
}
/**
* 令牌校验
*
* @param tokenVo
* @return
*/
fun validToken(tokenVo: TokenVo):Boolean{
return transaction {
return@transaction !UserToken.find {
UserTokens.studentId eq tokenVo.studentId
UserTokens.token eq tokenVo.token
}.empty()
}
}
}
}

@ -1,12 +1,70 @@
package com.gyf.csams package com.gyf.csams
import org.jetbrains.exposed.dao.IntEntity
import org.jetbrains.exposed.dao.IntEntityClass
import org.jetbrains.exposed.dao.id.EntityID
import org.jetbrains.exposed.dao.id.IntIdTable
import org.jetbrains.exposed.sql.Column import org.jetbrains.exposed.sql.Column
import org.jetbrains.exposed.sql.Table import org.jetbrains.exposed.sql.`java-time`.datetime
import java.time.LocalDateTime
object User: Table(){ /**
val studentId:Column<String> = varchar(name="student_id",length = 8) * 用户
*/
object Users: IntIdTable(){
/**
* 学号
*/
val studentId:Column<String> = varchar(name="student_id",length = 8).uniqueIndex()
/**
* 姓名
*/
val name:Column<String> = varchar(name="name",length = 10) val name:Column<String> = varchar(name="name",length = 10)
/**
* 密码,hash加密
*/
val password:Column<String> = varchar(name="password",length = 32) val password:Column<String> = varchar(name="password",length = 32)
override val primaryKey: PrimaryKey }
get() = PrimaryKey(studentId)
class User(id:EntityID<Int>):IntEntity(id){
companion object : IntEntityClass<User>(Users)
var studentId by Users.studentId
var name by Users.name
var password by Users.password
}
/**
* 用户授权令牌
*/
object UserTokens: IntIdTable(){
/**
* 授权学号
*/
val studentId:Column<String> = reference("student_id",Users.studentId)
/**
* 令牌
*/
val token:Column<String> = varchar(name="token",length = 32)
/**
* 授权ip地址
*/
val ip:Column<String> = varchar(name="ip",length = 32)
/**
* 令牌创建时间
*/
val createTime:Column<LocalDateTime> = datetime("create_time").default(LocalDateTime.now())
/**
* 授权设备
*/
val device:Column<String> = varchar(name="device",length = 256)
}
class UserToken(id:EntityID<Int>):IntEntity(id){
companion object:IntEntityClass<UserToken>(UserTokens)
var studentId by UserTokens.studentId
var token by UserTokens.token
var ip by UserTokens.ip
var createTime by UserTokens.createTime
var device by UserTokens.device
} }

@ -6,6 +6,22 @@ import io.ktor.application.*
import org.jetbrains.exposed.sql.Database import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.SchemaUtils import org.jetbrains.exposed.sql.SchemaUtils
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
import javax.sql.DataSource
class MySQL private constructor(val dataSource: DataSource? = null) {
companion object {
@Volatile
private var instance: MySQL? = null
fun getInstance(dataSource: DataSource?) =
instance ?: synchronized(this) {
instance ?: MySQL(dataSource).also { instance = it }
}
}
}
fun Application.MySQL(testing: Boolean = false){ fun Application.MySQL(testing: Boolean = false){
val config = HikariConfig().apply { val config = HikariConfig().apply {
@ -17,12 +33,14 @@ fun Application.MySQL(testing: Boolean = false){
} }
val dataSource = HikariDataSource(config) val dataSource = HikariDataSource(config)
Database.connect(dataSource) Database.connect(dataSource)
MySQL.getInstance(dataSource)
initTable() initTable()
} }
fun initTable(){ fun initTable(){
transaction { transaction {
SchemaUtils.createMissingTablesAndColumns(User) SchemaUtils.createMissingTablesAndColumns(Users)
SchemaUtils.createMissingTablesAndColumns(UserTokens)
} }
} }

@ -1,46 +0,0 @@
package com.gyf.csams
import org.jetbrains.exposed.sql.insert
import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction
import org.slf4j.LoggerFactory
class Service {
companion object {
private val logger = LoggerFactory.getLogger(Service::class.java)
/**
* 检查学号是否已注册
*/
fun registered(selectId: String): Boolean {
return transaction {
return@transaction User.select { User.studentId eq selectId }.count().toInt() > 0
}
}
/**
* 注册
*/
fun register(userVo: UserVo): UserResDto? {
var userResDto: UserResDto? = null
try {
transaction {
val _pwd = randomNum(8)
val result = User.insert {
it[studentId] = userVo.studentId
it[name] = userVo.name
it[password] = _pwd.md5()
}.resultedValues
if (result?.isNotEmpty() == true) {
userResDto = UserResDto(password = _pwd)
}
}
} catch (e: Exception) {
logger.error("注册失败,发生异常:$e")
}
return userResDto
}
}
}

@ -3,7 +3,7 @@ package com.gyf.csams
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
data class ApiResponse<T>(val code:Int,val message:String,val body:T?=null) data class ApiResponse<T>(val code:Int=200,val message:String,val body:T?=null)
class Simple { class Simple {
companion object { companion object {
@ -17,8 +17,33 @@ class Simple {
} }
} }
/**
* 用户注册表单
*
* @property studentId 学号
* @property name 姓名
*/
@Serializable @Serializable
data class UserVo(val studentId:String,val name:String) data class UserVo(val studentId:String,val name:String)
data class UserResDto(val password:String) /**
* 用户登陆表单
*
* @property studentId 学号
* @property password 密码
* @property device 设备型号
*/
data class UserLoginVo(val studentId: String,val password: String,val device: String)
data class UserResDto(val password:String)
data class Token(val token:String,val createTime:Long,val studentId:String)
/**
* 令牌传输
*
* @property isValid
* @property token
*/
data class TokenResDto(val isValid:Boolean,val token: Token?=null)
data class TokenVo(val token:String,val studentId:String)

@ -1,11 +1,20 @@
package com.gyf.csams package com.gyf.csams
import cn.smallbun.screw.core.Configuration
import cn.smallbun.screw.core.engine.EngineConfig
import cn.smallbun.screw.core.engine.EngineFileType
import cn.smallbun.screw.core.engine.EngineTemplateType
import cn.smallbun.screw.core.execute.DocumentationExecute
import cn.smallbun.screw.core.process.ProcessConfig
import io.ktor.config.* import io.ktor.config.*
import io.ktor.http.* import io.ktor.http.*
import io.ktor.server.testing.* import io.ktor.server.testing.*
import org.jetbrains.exposed.sql.transactions.transaction
import java.time.LocalDateTime
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
class ApplicationTest { class ApplicationTest {
@Test @Test
fun testRoot() { fun testRoot() {
@ -30,21 +39,100 @@ class ApplicationTest {
println("admin".md5()) println("admin".md5())
} }
@Test fun <R> initApp(test: TestApplicationEngine.() -> R):R{
fun testInsertUser(){ //<R> withTestApplication(test: TestApplicationEngine.() -> R): R
withTestApplication({ return withTestApplication({
(environment.config as MapApplicationConfig).apply { (environment.config as MapApplicationConfig).apply {
// Set here the properties // Set here the properties
put("ktor.deployment.mysql.jdbcUrl", "jdbc:mysql://localhost:3306/csams") put("ktor.deployment.mysql.jdbcUrl", "jdbc:mysql://localhost:3306/csams?serverTimezone=Asia/Shanghai")
put("ktor.deployment.mysql.driverClassName", "com.mysql.cj.jdbc.Driver") put("ktor.deployment.mysql.driverClassName", "com.mysql.cj.jdbc.Driver")
put("ktor.deployment.mysql.username", "root") put("ktor.deployment.mysql.username", "root")
put("ktor.deployment.mysql.password", "123456") put("ktor.deployment.mysql.password", "123456")
} }
MySQL(testing = true) MySQL(testing = true)
}) { }, test)
val c=Service.registered(selectId = "77889") }
@Test
fun testInsertUser(){
initApp{
val c=AccountService.register(UserVo(studentId = "6666",name = "hahaha"))
println(c) println(c)
} }
} }
@Test
fun testEntryId() {
initApp {
transaction {
// User.new {
// studentId="20210101"
// name="222"
// password="12345678".md5()
// }
// alter table Users comment '123';
exec("alter table Users comment '6666'")
}
}
}
@Test
fun localTime(){
println(LocalDateTime.now())
}
/**
* 文档生成
*/
@Test
fun documentGeneration() {
initApp {
//生成配置
val engineConfig = EngineConfig.builder()
//生成文件路径
.fileOutputDir("f:\\Desktop")
//打开目录
.openOutputDir(true)
//文件类型
.fileType(EngineFileType.HTML)
//生成模板实现
.produceType(EngineTemplateType.freemarker)
//自定义文件名称
.fileName("777").build();
//忽略表
// val ignoreTableName = ArrayList<String>()
// ignoreTableName.add("test_user")
// ignoreTableName.add("test_group")
//忽略表前缀
// val ignorePrefix = ArrayList<String>()
// ignorePrefix.add("test_")
//忽略表后缀
// val ignoreSuffix = ArrayList<String>()
// ignoreSuffix.add("_test")
val processConfig = ProcessConfig.builder() //指定生成逻辑、当存在指定表、指定表前缀、指定表后缀时,将生成指定表,其余表不生成、并跳过忽略表配置
//根据名称指定表生成
.designatedTableName(ArrayList()) //根据表前缀生成
.designatedTablePrefix(ArrayList()) //根据表后缀生成
.designatedTableSuffix(ArrayList()) //忽略表名
// .ignoreTableName(ignoreTableName) //忽略表前缀
// .ignoreTablePrefix(ignorePrefix) //忽略表后缀
// .ignoreTableSuffix(ignoreSuffix)
.build()
//配置
val config: Configuration = Configuration.builder() //版本
.version("1.0.0") //描述
.description("数据库设计文档生成") //数据源
.dataSource(MySQL.getInstance(null).dataSource) //生成配置
.engineConfig(engineConfig) //生成配置
.produceConfig(processConfig)
.build()
//执行生成
DocumentationExecute(config).execute()
}
}
} }

Loading…
Cancel
Save