From 80468467b03c4640fe206c152b84bfc9fcabd2df Mon Sep 17 00:00:00 2001 From: pan <1029559041@qq.com> Date: Thu, 27 May 2021 00:46:44 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E6=96=87=E4=BB=B6=E6=9C=8D?= =?UTF-8?q?=E5=8A=A1=20=E7=A4=BE=E5=9B=A2=E6=B3=A8=E5=86=8C=E6=9C=8D?= =?UTF-8?q?=E5=8A=A1=20=E5=A2=9E=E5=8A=A0=E5=9B=BE=E7=89=87=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E8=A1=A8=E3=80=81=E7=A4=BE=E5=9B=A2=E8=A1=A8=E3=80=81?= =?UTF-8?q?=E5=90=8E=E5=8F=B0=E7=AE=A1=E7=90=86=E5=91=98=E8=A1=A8=E3=80=81?= =?UTF-8?q?=E5=AE=A1=E6=A0=B8=E8=AE=B0=E5=BD=95=E8=A1=A8=20=E5=AE=8C?= =?UTF-8?q?=E5=96=84=E6=9C=8D=E5=8A=A1=E6=8A=BD=E8=B1=A1=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle.kts | 1 + resources/application.conf | 1 + resources/logback.xml | 2 +- src/Application.kt | 21 ++- src/Controller.kt | 210 +++++++++++++++++++++------ src/Dao.kt | 108 +++++++++++++- src/MySQL.kt | 2 +- src/Service.kt | 281 ++++++++++++++++++++++++++++++++----- src/Util.kt | 25 ++++ src/Vo.kt | 20 +-- test/ApplicationTest.kt | 27 ++-- test/ChannelTest.kt | 32 +++++ 12 files changed, 612 insertions(+), 118 deletions(-) create mode 100644 test/ChannelTest.kt diff --git a/build.gradle.kts b/build.gradle.kts index a4711a1..916bcf3 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -29,6 +29,7 @@ dependencies { */ implementation("io.ktor:ktor-server-netty:$ktor_version") implementation("io.ktor:ktor-server-core:$ktor_version") + /** * https://github.com/google/gson */ diff --git a/resources/application.conf b/resources/application.conf index f68d123..e3afe83 100644 --- a/resources/application.conf +++ b/resources/application.conf @@ -11,6 +11,7 @@ ktor { leaveMessage { maxSize = 20 } + filePath = upload watch = [ classes ] } application { diff --git a/resources/logback.xml b/resources/logback.xml index bdbb64e..7aafaef 100644 --- a/resources/logback.xml +++ b/resources/logback.xml @@ -1,7 +1,7 @@ - %d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + %d{YYYY-MM-dd HH:mm:ss} [%thread] %-5level - %C:%L - %msg%n diff --git a/src/Application.kt b/src/Application.kt index 86e3e99..bbcf603 100644 --- a/src/Application.kt +++ b/src/Application.kt @@ -22,15 +22,26 @@ fun Application.module(testing: Boolean = false) { } install(ContentNegotiation) { - gson { - } + gson() } + install(CallLogging) } fun Application.Controller(testing: Boolean = false){ - this.AccountController() - this.TestController() - this.MainController() + AccountController() + TestController() + MainController() + AssociationController() + NotificationController() + + /** + * 初始化log + */ + listOf(MainService,AccountService,FileService,AssociationService,BackgroundService).forEach { + it.init(environment) + } } + + diff --git a/src/Controller.kt b/src/Controller.kt index 12e2041..828f4e7 100644 --- a/src/Controller.kt +++ b/src/Controller.kt @@ -1,26 +1,26 @@ package com.gyf.csams import io.ktor.application.* +import io.ktor.http.content.* import io.ktor.request.* import io.ktor.response.* import io.ktor.routing.* -import org.slf4j.LoggerFactory +import kotlinx.coroutines.channels.Channel -suspend inline fun withToken(call:ApplicationCall, callback:(vo:T)->Unit){ - val levelVo=call.receive() - if(AccountService.validToken(levelVo.token)){ +suspend inline fun withToken(call: ApplicationCall, callback: (vo: T) -> Unit) { + val levelVo = call.receive() + if (AccountService.validToken(levelVo.token)) { callback(levelVo) - }else{ - call.respond(ApiResponse(message = "token校验失败",body = null)) + } else { + call.respond(Simple.error("token校验失败")) } } -private val logger = LoggerFactory.getLogger(Application::class.java) fun Application.AccountController() { routing { - route(path = "$ApiPathPrefix/account"){ - route(path="/register") { + route(path = "$ApiPathPrefix/account") { + route(path = "/register") { /** * 检测学号是否已注册 */ @@ -41,37 +41,37 @@ fun Application.AccountController() { * 注册账号 */ post { - val userVo = call.receive() - val userResDto = AccountService.register(userVo) - if (userResDto != null) { - call.respond(ApiResponse(message = "注册成功", body = userResDto)) - } else { - call.respond(Simple.error("注册失败")) - } + val userVo = call.receive() + val userResDto = AccountService.register(userVo) + if (userResDto != null) { + call.respond(ApiResponse(message = "注册成功", body = userResDto)) + } else { + call.respond(Simple.error("注册失败")) + } } } - route(path = "/login"){ - post{ - val userLoginVo= call.receive() - logger.info("执行登陆") - val token=AccountService.login(userLoginVo,call.request.host()) - logger.info("登录请求处理完毕") - call.respond(ApiResponse(message = if(token!=null) "登陆成功" else "账号或密码错误!!!",body = token)) + route(path = "/login") { + post { + val userLoginVo = call.receive() + environment.log.info("执行登陆") + val token = AccountService.login(userLoginVo, call.request.host()) + environment.log.info("登录请求处理完毕") + call.respond(ApiResponse(message = if (token != null) "登陆成功" else "账号或密码错误!!!", body = token)) } - post(path = "/token"){ - val tokenVo=call.receive() - val isValid=AccountService.validToken(tokenVo) - call.respond(ApiResponse(message = if(isValid) "令牌合法" else "令牌不合法",body = isValid)) + post(path = "/token") { + val tokenVo = call.receive() + val isValid = AccountService.validToken(tokenVo) + call.respond(ApiResponse(message = if (isValid) "令牌合法" else "令牌不合法", body = isValid)) } } - post(path = "/logout"){ - logger.info("退出登录") - val userLogoutVo=call.receive() - logger.info("$userLogoutVo") - val flag=AccountService.logout(userLogoutVo.studentId) - call.respond(ApiResponse(message = if(flag) "退出成功" else "退出失败",body = flag)) + post(path = "/logout") { + environment.log.info("退出登录") + val userLogoutVo = call.receive() + environment.log.info("$userLogoutVo") + val flag = AccountService.logout(userLogoutVo.userId) + call.respond(ApiResponse(message = if (flag) "退出成功" else "退出失败", body = flag)) } } } @@ -81,33 +81,153 @@ fun Application.AccountController() { * 主界面接口 * */ -fun Application.MainController(){ - MainService.register(maxSize = environment.config.property("ktor.deployment.leaveMessage.maxSize").getString().toInt()) +fun Application.MainController() { routing { //发送留言 route("$ApiPathPrefix/main") { post("/leaveMessage") { - withToken(call){ - val flag=MainService.createMessage(leaveMessageVo = it) - call.respond(ApiResponse(message = if(flag) "留言创建成功" else "留言创建失败",body=flag)) + withToken(call) { + val flag = MainService.createMessage(leaveMessageVo = it) + call.respond(ApiResponse(message = if (flag) "留言创建成功" else "留言创建失败", body = flag)) } } - post("/getMessage"){ - withToken(call){ - val s=MainService.getAllLeaveMessage() - call.respond(ApiResponse(message = "留言获取成功",body = s)) + post("/getMessage") { + withToken(call) { + val s = MainService.getAllLeaveMessage() + call.respond(ApiResponse(message = "留言获取成功", body = s)) } } } } } -fun Application.TestController(){ +fun Application.TestController() { routing { - get("$ApiPathPrefix/test"){ + get("$ApiPathPrefix/test") { AccountService.test() - call.respond(ApiResponse(message = "成功连接服务端",body=true)) + call.respond(ApiResponse(message = "成功连接服务端", body = true)) + } + + get("$ApiPathPrefix/testR"){ + sendNotification(SimpleNotification()) + call.respond("建立通知成功") + } + } +} + + +fun Application.AssociationController() { + routing { + route("$ApiPathPrefix/association") { + post("/uploadLogo") { + val multipartData = call.receiveMultipart() + multipartData.readAllParts().apply { + environment.log.info("part size=$size") + if (size == 3) { + val result=FileService.storeFile(this) + call.respond(ApiResponse(message = if(result.isNullOrEmpty()) "文件上传失败" else + "成功上传${result.size}个文件",body = result)) + } else { + call.respond(Simple.error("参数异常")) + throw IllegalArgumentException("参数异常") + } + } + } + post("/register"){ + withToken(call = call){ + val flag=AssociationService.register(vo=it) + call.respond(ApiResponse(message = if(flag) "社团注册资料已保存,请等待受理" else "社团注册资料保存失败",body=flag)) + } + } } } +} + +/** + * TODO + * + */ +enum class NotificationType{ + System +} + +/** + * 通知接收客户端 + * + */ +enum class ReceiverType{ + //前台 + Foreground, + //后台 + Background +} + +/** + * 通知接收者 + * + */ +sealed class Receiver{ + protected abstract val id:Int + abstract val target:ReceiverType +} + +/** + * 后台客户端接收 + * + * @property managerId 管理员id + */ +data class BackgroundReceiver(val managerId:Int,override val id: Int=managerId, override val target: ReceiverType=ReceiverType.Background):Receiver() + +/** + * 前台客户端接收 + * + * @property userId 用户id + */ +data class ForegroundReceiver(val userId:Int, override val id:Int=userId, override val target: ReceiverType=ReceiverType.Foreground):Receiver() + +sealed class NotificationVo{ + abstract val type:NotificationType + abstract val title:String + abstract val receiver:Receiver +} + +/** + * 系统通知 + * + * @property type + * @property title + */ +sealed class SysNotificationVo:NotificationVo(){ + override val type: NotificationType=NotificationType.System + abstract val content:T +} + +/** + * 注册社团通知 + * + * @property title + */ +data class RegisterNotification(override val title: String="注册社团通知", override val content: RegAssociationDto, + override val receiver: ForegroundReceiver +):SysNotificationVo() + + +data class SimpleNotification( + override val type: NotificationType=NotificationType.System, + override val title: String="test", + override val receiver: Receiver=BackgroundReceiver(managerId = randomNum().toInt()) +):NotificationVo() + +private val channel = Channel() + +suspend fun sendNotification(vo:NotificationVo){ + channel.send(vo) +} + +fun Application.NotificationController(){ + + routing { + + } } \ No newline at end of file diff --git a/src/Dao.kt b/src/Dao.kt index dd864f9..bd0ee6c 100644 --- a/src/Dao.kt +++ b/src/Dao.kt @@ -33,8 +33,8 @@ class User(id:EntityID):IntEntity(id){ @TableComment("用户授权令牌") object UserTokens: IntIdTable(){ - @TableComment("授权学号") - val studentId:Column = reference("student_id",Users.studentId) + @TableComment("授权用户") + val userId:Column> = reference("user_id",Users) @TableComment("令牌") val token:Column = varchar(name="token",length = 32) @@ -51,7 +51,7 @@ object UserTokens: IntIdTable(){ class UserToken(id:EntityID):IntEntity(id){ companion object:IntEntityClass(UserTokens) - var studentId by UserTokens.studentId + var user by User referencedOn UserTokens.userId var token by UserTokens.token var ip by UserTokens.ip var createTime by UserTokens.createTime @@ -61,7 +61,7 @@ class UserToken(id:EntityID):IntEntity(id){ @TableComment("留言") object LeaveMessages:IntIdTable(){ @TableComment("留言用户") - val studentId:Column = reference("student_id",Users.studentId) + val userId:Column> = reference("user_id",Users) @TableComment("留言内容") val message:Column = varchar(name = "message",length = 20) @@ -72,8 +72,106 @@ object LeaveMessages:IntIdTable(){ class LeaveMessage(id:EntityID):IntEntity(id){ companion object:IntEntityClass(LeaveMessages) - var studentId by LeaveMessages.studentId + var user by User referencedOn LeaveMessages.userId var message by LeaveMessages.message var createTime by LeaveMessages.createTime } +@TableComment("图片文件信息") +object ImageFiles:IntIdTable(){ + @TableComment("上传用户") + val userId:Column = varchar(name = "user_id",length = 10) + + @TableComment("文件相对路径") + val filepath:Column = varchar(name="filepath",length = 50) + + @TableComment("文件hash") + val md5:Column = varchar(name="md5",length = 32) + + @TableComment("文件创建时间") + val createTime:Column = datetime("create_time").defaultExpression(CurrentDateTime()) +} + +class ImageFile(id:EntityID):IntEntity(id){ + companion object:IntEntityClass(ImageFiles) + var userId by ImageFiles.userId + var filepath by ImageFiles.filepath + var md5 by ImageFiles.md5 + var createTime by ImageFiles.createTime +} + +@TableComment("社团") +object Associations:IntIdTable(){ + @TableComment("社团名称") + val name:Column =varchar(name = "name",length = 10) + + @TableComment("社团介绍") + val desc:Column =varchar(name = "desc",length = 30) + + @TableComment("社团logo") + val logo:Column> = reference("logo",ImageFiles) + + @TableComment("社团审核状态") + val status:Column = bool(name="status").default(false) +} + +class Association(id:EntityID):IntEntity(id){ + companion object:IntEntityClass(Associations) + var name by Associations.name + var desc by Associations.desc + var logo by ImageFile referencedOn Associations.logo + var status by Associations.status +} + +@TableComment("后台管理员") +object Managers:IntIdTable(){ + @TableComment("帐号") + val account:Column = varchar(name="account",length=10) + + @TableComment("密码") + val password:Column = varchar(name="password",length = 32) + + @TableComment("职务") + val duty:Column = varchar(name="duty",length = 32) + + @TableComment("等级") + val level:Column = integer("level") +} + +class Manager(id:EntityID):IntEntity(id){ + companion object:IntEntityClass(Managers) + var account by Managers.account + var password by Managers.password + var duty by Managers.duty + var level by Managers.level +} + +@TableComment("审核记录") +object CheckForms:IntIdTable(){ + @TableComment("审核类型") + val type:Column = varchar(name="type",length = 10) + + @TableComment("审核人") + val managerId:Column> = reference("manager_id",Managers) + + @TableComment("审核理由") + val cause:Column = varchar("cause",length = 30) + + @TableComment("审核对象") + val target:Column = integer("target") + + @TableComment("审核时间") + val createTime:Column = datetime("create_time").defaultExpression(CurrentDateTime()) +} + +class CheckForm(id:EntityID):IntEntity(id){ + companion object:IntEntityClass(CheckForms) + val type by CheckForms.type + val manager by Manager referrersOn CheckForms.managerId + val cause by CheckForms.cause + val target by CheckForms.target +} + + + + diff --git a/src/MySQL.kt b/src/MySQL.kt index 5d9d8aa..28a595a 100644 --- a/src/MySQL.kt +++ b/src/MySQL.kt @@ -40,7 +40,7 @@ fun Application.MySQL(testing: Boolean = false){ fun initTable(){ transaction { - val tableList= arrayOf(Users,UserTokens,LeaveMessages) + val tableList= arrayOf(Users,UserTokens,LeaveMessages,ImageFiles,Associations,Managers,CheckForms) SchemaUtils.createMissingTablesAndColumns(*tableList) updateComment(*tableList) diff --git a/src/Service.kt b/src/Service.kt index d6a206f..8c53922 100644 --- a/src/Service.kt +++ b/src/Service.kt @@ -1,13 +1,30 @@ package com.gyf.csams +import io.ktor.application.* +import io.ktor.http.content.* import org.jetbrains.exposed.sql.deleteWhere import org.jetbrains.exposed.sql.transactions.transaction -import org.slf4j.LoggerFactory +import org.slf4j.Logger +import java.io.File import java.time.ZoneOffset +import kotlin.properties.Delegates +interface BaseService{ + fun init(environment:ApplicationEnvironment) +} + + +abstract class AbstractService:BaseService{ + protected lateinit var log:Logger + override fun init(environment: ApplicationEnvironment) { + this.log=environment.log + } +} -object AccountService { - private val logger = LoggerFactory.getLogger(AccountService::class.java) +/** + * 账号服务 + */ +object AccountService:AbstractService() { /** * 检查学号是否已注册,true=已注册 */ @@ -23,16 +40,16 @@ object AccountService { fun register(userVo: UserVo): UserResDto? { try { return transaction { - val _pwd = randomNum(8) + val originPassword = randomNum(8) User.new { studentId=userVo.studentId name=userVo.name - password=_pwd.md5() + password=originPassword.md5() } - return@transaction UserResDto(password=_pwd) + return@transaction UserResDto(password=originPassword) } } catch (e: Exception) { - logger.error("注册失败,发生异常:$e") + log.error("注册失败,发生异常:$e") return null } } @@ -44,35 +61,35 @@ object AccountService { */ fun login(userLoginVo: UserLoginVo,_ip:String):Token?{ return transaction { - val user=User.find { Users.studentId eq userLoginVo.studentId }.firstOrNull() + val matchUser=User.find { Users.studentId eq userLoginVo.studentId }.firstOrNull() when { - user==null -> { - logger.warn("学号:${userLoginVo.studentId}不存在") + matchUser==null -> { + log.warn("学号:${userLoginVo.studentId}不存在") return@transaction null } - userLoginVo.password.md5() != user.password -> { - logger.warn("密码:${userLoginVo.password}错误") + userLoginVo.password.md5() != matchUser.password -> { + log.warn("密码:${userLoginVo.password}错误") return@transaction null } else -> { val token=UserToken.new{ - studentId=userLoginVo.studentId + user=matchUser ip=_ip device=userLoginVo.device - token=listOf(studentId,ip,device).joinToString(separator = ('a' .. 'z').random().toString()).md5() + token=listOf(matchUser.id,ip,device).joinToString(separator = ('a' .. 'z').random().toString()).md5() } token.flush() - return@transaction Token(studentId = userLoginVo.studentId,token = token.token, + return@transaction Token(userId = matchUser.id.value,token = token.token, createTime = token.createTime.toEpochSecond( - ZoneOffset.of("+8")),name=user.name) + ZoneOffset.of("+8"))) } } } } fun validToken(token: Token):Boolean{ - return validToken(TokenVo(token = token.token,studentId = token.studentId)) + return validToken(TokenVo(token = token.token,userId = token.userId)) } /** @@ -84,37 +101,40 @@ object AccountService { fun validToken(tokenVo: TokenVo):Boolean{ return transaction { !UserToken.find { - UserTokens.studentId eq tokenVo.studentId + UserTokens.userId eq tokenVo.userId UserTokens.token eq tokenVo.token }.empty() } } - fun logout(studentId:String):Boolean{ + fun logout(userId:Int):Boolean{ return transaction { - UserTokens.deleteWhere { UserTokens.studentId eq studentId }>0 + UserTokens.deleteWhere { UserTokens.userId eq userId }>0 } } fun test(){ - logger.info("开始测试") + log.info("开始测试") transaction { - logger.info("查询到个${User.count()}用户") + log.info("查询到个${User.count()}用户") } - logger.info("结束测试") + log.info("结束测试") } } -object MainService{ - private val logger = LoggerFactory.getLogger(MainService::class.java) - - var maxSize:Int=20 +/** + * 主页服务 + */ +object MainService:AbstractService(){ + private var maxSize by Delegates.notNull() - fun register(maxSize:Int){ - this.maxSize=maxSize + override fun init(environment:ApplicationEnvironment){ + super.init(environment) + this.maxSize=environment.config.property("ktor.deployment.leaveMessage.maxSize").getString().toInt() } + /** * 创建留言信息 * @@ -125,32 +145,219 @@ object MainService{ return if(leaveMessageVo.message.isNotEmpty()){ return transaction { val count=LeaveMessage.count().toInt() - logger.info("系统留言数:$count,限制数:$maxSize") + log.info("系统留言数:$count,限制数:$maxSize") if(count>= maxSize){ LeaveMessage.all().sortedBy { it.createTime }.subList(0, count-maxSize).forEach { it.delete() } } - logger.info("保存留言:${leaveMessageVo.message}") + log.info("保存留言:${leaveMessageVo.message}") LeaveMessage.new { - studentId = leaveMessageVo.token.studentId + user= User.findById(leaveMessageVo.token.userId)?:throw IllegalArgumentException("非法id") message = leaveMessageVo.message } - logger.info("留言保存成功") + log.info("留言保存成功") return@transaction true } }else{ - logger.info("留言不能为空") + log.info("留言不能为空") false } } - fun getAllLeaveMessage():List{ + fun getAllLeaveMessage():List{ return transaction { - logger.info("获取所有留言") + log.info("获取所有留言") + return@transaction LeaveMessage.all().toList().map { - LeaveMessageFormatVo(message = "${User.find { Users.studentId eq it.studentId }.first().name}说:${it.message}",studentId = it.studentId) + LeaveMessageDto( + message = "${it.user.name}说:${it.message}", + user = UserVo(studentId = it.user.studentId, name = it.user.name) + ) + } + } + } +} + +/** + * 文件管理服务 + */ +object FileService:AbstractService(){ + private lateinit var uploadDir:String + private lateinit var filePath:String + + override fun init(environment:ApplicationEnvironment){ + super.init(environment) + this.uploadDir= environment.config.property("ktor.deployment.filePath").getString() + val resourcePath =this::class.java.classLoader.getResource("")?.path ?: throw IllegalArgumentException("初始化资源目录失败") + File(resourcePath, uploadDir).apply { + when{ + !exists()&&mkdir()-> { + log.info("图片上传路径[${absolutePath}]初始化成功") + this@FileService.filePath=absolutePath + } + exists()->{ + log.info("图片上传路径[${absolutePath}]已存在") + this@FileService.filePath=absolutePath + } + else->throw IllegalArgumentException("图片上传路径[${absolutePath}]初始化失败") + } + } + } + + private fun save(id: String, path:String, file: File):Int{ + return transaction { + return@transaction ImageFile.new { + userId = id + filepath = path + md5 = file.readBytes().md5() + } + }.id.value + } + + + private inline fun getPartData(map:Map, key:String):T{ + if(map.containsKey(key)){ + val obj= map[key] + if(obj is T){ + return obj + }else{ + throw IllegalArgumentException("类型错误") + } + } + throw IllegalArgumentException("找不到key:${key}") + } + + fun storeFile(data:List):List?{ + val map=data.associateBy { + it.name + }.toMutableMap() + log.info("map=${map}") + val userId=getPartData(map,"id").value + val token= getPartData(map,"token").value + val tokenVo=TokenVo(token=token,userId=userId.toInt()) + if(AccountService.validToken(tokenVo)){ + map.remove("id") + map.remove("token") + val fileIds= mutableListOf() + map.forEach{ + val value=it.value + if(value is PartData.FileItem){ + val fileBytes = value.streamProvider().readBytes() + val fileName=value.originalFileName?:throw IllegalArgumentException("参数异常") + val format = fileBytes.getFormat() + val fullFileName="${fileName}.${format.format}" + log.info("fullFileName=$fullFileName") + val file=File(filePath,fullFileName).apply { + writeBytes(fileBytes) + } + val fileId= save(id = userId,"${uploadDir}/${fullFileName}",file=file) + fileIds.add(fileId) + } + } + return fileIds + }else{ + return null + } + } +} + +/** + * 社团服务 + */ +object AssociationService: AbstractService() { + + fun register(vo:RegAssociationVo):Boolean{ + return try { + transaction { + Association.new { + name=vo.name + desc=vo.desc + logo=ImageFile.findById(vo.fileId) ?: throw IllegalArgumentException("文件id不存在!") + } + } + log.info("未审核社团创建成功") + true + } catch (e: Exception) { + log.error(e.stackTraceToString()) + false + } + } +} + +enum class ManagerType(val desc:String,val level:Int){ + Teacher("老师",1), + PamphaBhusal("总部长",2), + SecretaryOfTheMinister("秘书部部长",3), + PropagandaDepartment("宣传部部长",3), + LiaisonMinister("外联部部长",3), + SecretaryDepartmentOfficer("秘书部干事",4), + PublicityDepartmentOfficer("宣传部干事",4), + LiaisonOfficer("外联部干事",4) +} + +object NotificationService:AbstractService(){ + +} + +/** + * 后台服务 + */ +object BackgroundService:AbstractService(){ + + + + override fun init(environment: ApplicationEnvironment) { + super.init(environment) + initManager() + } + + private fun createManager(type:ManagerType, num:Int=1): MutableList { + val managerList= mutableListOf() + repeat(num){ + val originPassword=randomNum() + Manager.new { + account= randomNum() + password = originPassword.md5() + duty=type.name + level=type.level + }.apply { + managerList.add(InitManagerDto(account=account,originPassword=originPassword)) + } + } + return managerList + } + + //初始化管理员 + private fun initManager(){ + transaction { + val resourcePath =this::class.java.classLoader.getResource("")?.path ?: throw IllegalArgumentException("初始化资源目录失败") + val file=File(resourcePath,"管理员账号.txt") + if(!file.exists()) { + Manager.count().let { it -> + if (it.toInt() == 0) { + val allManager = mutableListOf() + allManager.addAll(createManager(ManagerType.Teacher, 1)) + allManager.addAll(createManager(ManagerType.PamphaBhusal, 1)) + allManager.addAll(createManager(ManagerType.SecretaryOfTheMinister, 1)) + allManager.addAll(createManager(ManagerType.PropagandaDepartment, 1)) + allManager.addAll(createManager(ManagerType.LiaisonMinister, 1)) + arrayOf( + ManagerType.SecretaryDepartmentOfficer, + ManagerType.PublicityDepartmentOfficer, + ManagerType.LiaisonOfficer + ).forEach { + allManager.addAll(createManager(it, 3)) + } + allManager.forEach { + file.appendText("${it.account}------${it.originPassword}\n") + } + log.info("共生成${allManager.size}个管理员账号") + } else { + log.info("不需要生成管理员") + } + } } } } diff --git a/src/Util.kt b/src/Util.kt index 19f538d..f383c64 100644 --- a/src/Util.kt +++ b/src/Util.kt @@ -5,6 +5,7 @@ import org.jetbrains.exposed.sql.Column import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.vendors.MysqlDialect import org.jetbrains.exposed.sql.vendors.currentDialect +import java.io.File import java.math.BigInteger import java.security.MessageDigest @@ -24,9 +25,33 @@ fun String.md5(): String { return BigInteger(1, md.digest(toByteArray())).toString(16).padStart(32, '0') } +fun ByteArray.md5():String{ + val md = MessageDigest.getInstance("MD5") + return BigInteger(1, md.digest(this)).toString(16).padStart(32, '0') +} + @Target(AnnotationTarget.CLASS,AnnotationTarget.FIELD) annotation class TableComment(val comment:String) +enum class FileFormat(val format:String,val head:String){ + JPEG("jpg","FFD8FF") +} + +fun ByteArray.getFormat(): FileFormat = format(this) ?: throw IllegalArgumentException("无法识别数据格式") + +fun File.getFormat(): FileFormat { + val bytes=readBytes() + return format(bytes) ?: throw IllegalArgumentException("无非识别文件[${absolutePath}]文件的格式") +} + +private fun format(bytes:ByteArray):FileFormat?{ + FileFormat.values().forEach { it -> + if(it.head==bytes.copyOf(it.head.length / 2).joinToString(separator = "") { String.format("%02X",it) }){ + return it + } + } + return null +} /** * 表、表字段添加注释 diff --git a/src/Vo.kt b/src/Vo.kt index c0a0bac..b780e7c 100644 --- a/src/Vo.kt +++ b/src/Vo.kt @@ -1,7 +1,5 @@ package com.gyf.csams -import kotlinx.serialization.Serializable - data class ApiResponse(val code:Int=200,val message:String,val body:T?=null) @@ -23,7 +21,6 @@ class Simple { * @property studentId 学号 * @property name 姓名 */ -@Serializable data class UserVo(val studentId:String,val name:String) /** @@ -35,14 +32,13 @@ data class UserVo(val studentId:String,val name:String) */ data class UserLoginVo(val studentId: String,val password: String,val device: String) -@Serializable -data class UserLogoutVo(val studentId:String) +data class UserLogoutVo(val userId:Int) data class UserResDto(val password:String) -data class Token(val token:String,val createTime:Long,val studentId:String,val name:String) +data class Token(val token:String, val createTime:Long, val userId:Int) -data class TokenVo(val token:String,val studentId:String) +data class TokenVo(val token:String,val userId:Int) sealed class BaseVo{ abstract val token:Token @@ -53,4 +49,12 @@ data class LeaveMessageVo(val message: String, override val token:Token):BaseVo( data class OnlyToken(override val token: Token):BaseVo() //data class PageVo(val pageSize:Int=10,val page:, override val token: Token):BaseVo() -data class LeaveMessageFormatVo(val message: String,val studentId: String) \ No newline at end of file +data class LeaveMessageDto(val message: String,val user: UserVo) + +data class RegAssociationVo(val name:String, val desc:String,val fileId:Int, override val token: Token):BaseVo() + +data class ImageFileDto(val filepath:String,val md5:String,val createTime: Long,val url:String) + +data class RegAssociationDto(val name:String,val desc:String,val logo:ImageFileDto) + +data class InitManagerDto(val account:String,val originPassword:String) \ No newline at end of file diff --git a/test/ApplicationTest.kt b/test/ApplicationTest.kt index 09ebb04..b1263d9 100644 --- a/test/ApplicationTest.kt +++ b/test/ApplicationTest.kt @@ -6,10 +6,12 @@ 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 com.google.gson.Gson import io.ktor.config.* import io.ktor.http.* import io.ktor.server.testing.* import org.jetbrains.exposed.sql.transactions.transaction +import java.io.File import kotlin.test.Test import kotlin.test.assertEquals @@ -47,6 +49,8 @@ class ApplicationTest { put("ktor.deployment.mysql.driverClassName", "com.mysql.cj.jdbc.Driver") put("ktor.deployment.mysql.username", "root") put("ktor.deployment.mysql.password", "123456") + put("ktor.deployment.leaveMessage.maxSize", "20") + put("ktor.deployment.filePath", "upload") } MySQL(testing = true) }, test) @@ -60,19 +64,13 @@ class ApplicationTest { } } - - @Test fun testUpdateComment(){ initApp { // updateComment(Users,UserTokens) transaction { - UserToken.new { - studentId = "6666" - token = "22" - ip = "sdf" - device = "hahha" - } + val s= MainService.getAllLeaveMessage() + Gson().toJson(s) } } } @@ -87,15 +85,12 @@ class ApplicationTest { } } - @Test - fun localTime(){ - initApp { - transaction { - - } - } + @Test + fun checkFileHead(){ + val format=File("E:\\JetBrains\\IdeaProjects\\CsamsServer\\build\\classes\\kotlin\\main\\upload\\1621964313158").getFormat() + println(format.format) } /** @@ -117,7 +112,7 @@ class ApplicationTest { //生成模板实现 .produceType(EngineTemplateType.freemarker) //自定义文件名称 - .fileName(fileName).build(); + .fileName(fileName).build() println("数据库文档输出路径${engineConfig.fileOutputDir}") //忽略表 // val ignoreTableName = ArrayList() diff --git a/test/ChannelTest.kt b/test/ChannelTest.kt new file mode 100644 index 0000000..52c18ed --- /dev/null +++ b/test/ChannelTest.kt @@ -0,0 +1,32 @@ +/* + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +// This file was automatically generated from channels.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleChannel04 + +import kotlinx.coroutines.* +import kotlinx.coroutines.channels.ReceiveChannel +import kotlinx.coroutines.channels.produce + +fun main() = runBlocking { + val numbers = produceNumbers() // produces integers from 1 and on + val squares = square(numbers) // squares integers + repeat(5) { + delay(1000) + println(squares.receive()) // print first five + } + println("Done!") // we are done + coroutineContext.cancelChildren() // cancel children coroutines +} + +@OptIn(ExperimentalCoroutinesApi::class) +fun CoroutineScope.produceNumbers() = produce { + var x = 1 + while (true) send(x++) // infinite stream of integers starting from 1 +} + +@OptIn(ExperimentalCoroutinesApi::class) +fun CoroutineScope.square(numbers: ReceiveChannel): ReceiveChannel = produce { + for (x in numbers) send(x * x) +} \ No newline at end of file