From f9a31eed5b3209ea5ed5c8057046ddc8a782f474 Mon Sep 17 00:00:00 2001 From: pan <1029559041@qq.com> Date: Thu, 27 May 2021 10:44:07 +0800 Subject: [PATCH] =?UTF-8?q?=E9=80=9A=E7=9F=A5=E6=8E=A5=E5=8F=A3=E5=AF=B9?= =?UTF-8?q?=E6=8E=A5=E5=AE=A2=E6=88=B7=E7=AB=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Application.kt | 2 +- src/Controller.kt | 46 +++++++++++++++--------- src/Dao.kt | 37 +++++++++++++++++-- src/MySQL.kt | 2 +- src/Service.kt | 90 ++++++++++++++++++++++++++++++++++++++-------- src/Vo.kt | 13 ++++--- 6 files changed, 149 insertions(+), 41 deletions(-) diff --git a/src/Application.kt b/src/Application.kt index bbcf603..546e5d8 100644 --- a/src/Application.kt +++ b/src/Application.kt @@ -39,7 +39,7 @@ fun Application.Controller(testing: Boolean = false){ /** * 初始化log */ - listOf(MainService,AccountService,FileService,AssociationService,BackgroundService).forEach { + listOf(MainService,AccountService,FileService,AssociationService,BackgroundService,NotificationService).forEach { it.init(environment) } } diff --git a/src/Controller.kt b/src/Controller.kt index 828f4e7..91bd4f3 100644 --- a/src/Controller.kt +++ b/src/Controller.kt @@ -5,7 +5,6 @@ import io.ktor.http.content.* import io.ktor.request.* import io.ktor.response.* import io.ktor.routing.* -import kotlinx.coroutines.channels.Channel suspend inline fun withToken(call: ApplicationCall, callback: (vo: T) -> Unit) { val levelVo = call.receive() @@ -60,7 +59,7 @@ fun Application.AccountController() { call.respond(ApiResponse(message = if (token != null) "登陆成功" else "账号或密码错误!!!", body = token)) } post(path = "/token") { - val tokenVo = call.receive() + val tokenVo = call.receive() val isValid = AccountService.validToken(tokenVo) call.respond(ApiResponse(message = if (isValid) "令牌合法" else "令牌不合法", body = isValid)) } @@ -108,11 +107,6 @@ fun Application.TestController() { AccountService.test() call.respond(ApiResponse(message = "成功连接服务端", body = true)) } - - get("$ApiPathPrefix/testR"){ - sendNotification(SimpleNotification()) - call.respond("建立通知成功") - } } } @@ -124,7 +118,7 @@ fun Application.AssociationController() { val multipartData = call.receiveMultipart() multipartData.readAllParts().apply { environment.log.info("part size=$size") - if (size == 3) { + if (size == 4) { val result=FileService.storeFile(this) call.respond(ApiResponse(message = if(result.isNullOrEmpty()) "文件上传失败" else "成功上传${result.size}个文件",body = result)) @@ -186,7 +180,7 @@ data class BackgroundReceiver(val managerId:Int,override val id: Int=managerId, */ data class ForegroundReceiver(val userId:Int, override val id:Int=userId, override val target: ReceiverType=ReceiverType.Foreground):Receiver() -sealed class NotificationVo{ +sealed class BaseNotification{ abstract val type:NotificationType abstract val title:String abstract val receiver:Receiver @@ -198,7 +192,7 @@ sealed class NotificationVo{ * @property type * @property title */ -sealed class SysNotificationVo:NotificationVo(){ +sealed class SysNotificationVo:BaseNotification(){ override val type: NotificationType=NotificationType.System abstract val content:T } @@ -217,17 +211,35 @@ data class SimpleNotification( override val type: NotificationType=NotificationType.System, override val title: String="test", override val receiver: Receiver=BackgroundReceiver(managerId = randomNum().toInt()) -):NotificationVo() +):BaseNotification() -private val channel = Channel() - -suspend fun sendNotification(vo:NotificationVo){ - channel.send(vo) -} fun Application.NotificationController(){ - routing { + route("${ApiPathPrefix}/notification") { + post("/pull") { + withToken(call) { + val notificationList = NotificationService.pull(vo = it) + call.respond( + ApiResponse( + message = if (notificationList.isEmpty()) "没有最新通知" else "获取${notificationList.size}条最新通知", + body = notificationList + ) + ) + } + } + post("/count"){ + withToken(call) { + call.respond(ApiResponse(message = "统计未读通知",body = NotificationService.count(it))) + } + } + + post("/list"){ + withToken(call){ + call.respond(ApiResponse(message = "获取通知列表",body=NotificationService.list(it))) + } + } + } } } \ No newline at end of file diff --git a/src/Dao.kt b/src/Dao.kt index bd0ee6c..183e3e1 100644 --- a/src/Dao.kt +++ b/src/Dao.kt @@ -166,12 +166,43 @@ object CheckForms:IntIdTable(){ class CheckForm(id:EntityID):IntEntity(id){ companion object:IntEntityClass(CheckForms) - val type by CheckForms.type + var type by CheckForms.type val manager by Manager referrersOn CheckForms.managerId - val cause by CheckForms.cause - val target by CheckForms.target + var cause by CheckForms.cause + var target by CheckForms.target } +@TableComment("通知记录") +object Notifications:IntIdTable(){ + @TableComment("通知标题") + val title:Column = varchar(name="title",length = 10) + @TableComment("通知内容") + val content:Column = varchar(name="content",length = 30) + @TableComment("接收者") + val receiverId:Column = integer(name="receiver_id") + @TableComment("接收客户端") + val receiverClient:Column = varchar(name="receiver_client",length = 10) + + @TableComment("阅读状态") + val read:Column = bool("read").default(false) + + @TableComment("拉取状态") + val pull:Column = bool("pull").default(false) + + @TableComment("通知创建时间") + val createTime:Column = datetime("create_time").defaultExpression(CurrentDateTime()) +} + +class Notification(id:EntityID):IntEntity(id){ + companion object:IntEntityClass(Notifications) + var title by Notifications.title + var content by Notifications.content + var receiverId by Notifications.receiverId + var receiverClient by Notifications.receiverClient + var read by Notifications.read + var pull by Notifications.pull + var createTime by Notifications.createTime +} \ No newline at end of file diff --git a/src/MySQL.kt b/src/MySQL.kt index 28a595a..f3e341c 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,ImageFiles,Associations,Managers,CheckForms) + val tableList= arrayOf(Users,UserTokens,LeaveMessages,ImageFiles,Associations,Managers,CheckForms,Notifications) SchemaUtils.createMissingTablesAndColumns(*tableList) updateComment(*tableList) diff --git a/src/Service.kt b/src/Service.kt index 8c53922..fbe77dc 100644 --- a/src/Service.kt +++ b/src/Service.kt @@ -2,6 +2,7 @@ package com.gyf.csams import io.ktor.application.* import io.ktor.http.content.* +import org.jetbrains.exposed.sql.and import org.jetbrains.exposed.sql.deleteWhere import org.jetbrains.exposed.sql.transactions.transaction import org.slf4j.Logger @@ -80,7 +81,7 @@ object AccountService:AbstractService() { token=listOf(matchUser.id,ip,device).joinToString(separator = ('a' .. 'z').random().toString()).md5() } token.flush() - return@transaction Token(userId = matchUser.id.value,token = token.token, + return@transaction Token(id = matchUser.id.value,token = token.token, createTime = token.createTime.toEpochSecond( ZoneOffset.of("+8"))) } @@ -89,20 +90,10 @@ object AccountService:AbstractService() { } fun validToken(token: Token):Boolean{ - return validToken(TokenVo(token = token.token,userId = token.userId)) - } - - /** - * 令牌校验 - * - * @param tokenVo - * @return - */ - fun validToken(tokenVo: TokenVo):Boolean{ return transaction { !UserToken.find { - UserTokens.userId eq tokenVo.userId - UserTokens.token eq tokenVo.token + UserTokens.userId eq token.id + UserTokens.token eq token.token }.empty() } } @@ -154,7 +145,7 @@ object MainService:AbstractService(){ } log.info("保存留言:${leaveMessageVo.message}") LeaveMessage.new { - user= User.findById(leaveMessageVo.token.userId)?:throw IllegalArgumentException("非法id") + user= User.findById(leaveMessageVo.token.id)?:throw IllegalArgumentException("非法id") message = leaveMessageVo.message } log.info("留言保存成功") @@ -236,7 +227,8 @@ object FileService:AbstractService(){ log.info("map=${map}") val userId=getPartData(map,"id").value val token= getPartData(map,"token").value - val tokenVo=TokenVo(token=token,userId=userId.toInt()) + val createTime= getPartData(map,"createTime").value + val tokenVo=Token(token=token,id=userId.toInt(),createTime=createTime.toLong()) if(AccountService.validToken(tokenVo)){ map.remove("id") map.remove("token") @@ -252,6 +244,7 @@ object FileService:AbstractService(){ val file=File(filePath,fullFileName).apply { writeBytes(fileBytes) } + log.info("文件成功保存到${file.absolutePath}") val fileId= save(id = userId,"${uploadDir}/${fullFileName}",file=file) fileIds.add(fileId) } @@ -276,6 +269,12 @@ object AssociationService: AbstractService() { desc=vo.desc logo=ImageFile.findById(vo.fileId) ?: throw IllegalArgumentException("文件id不存在!") } + Notification.new { + title="注册社团" + content="您成功提交了一份社团注册资料,请耐心等待后台受理" + receiverId=vo.token.id + receiverClient=ReceiverType.Foreground.name + } } log.info("未审核社团创建成功") true @@ -297,8 +296,69 @@ enum class ManagerType(val desc:String,val level:Int){ LiaisonOfficer("外联部干事",4) } +/** + * 通知服务 + */ object NotificationService:AbstractService(){ + /** + * 拉取最新通知 + * + * @param vo + * @return + */ + fun pull(vo:NotificationDto):List{ + return transaction { + val notifications=Notification.find { Notifications.pull eq false and + (Notifications.receiverId eq vo.receiverId) and + (Notifications.receiverClient eq vo.receiverClient.name) } + log.info("获取${notifications.count()}条最新通知") + return@transaction notifications.map { + it.pull=true + NotificationVo(title=it.title,id=it.id.value,content = it.content,createTime = it.createTime.toEpochSecond( + ZoneOffset.of("+8"))) + } + } + } + + /** + * 未读通知计数 + * + * @param vo + * @return + */ + fun count(vo:NotificationDto):Long{ + return transaction { + return@transaction Notification.find{ Notifications.read eq false and + (Notifications.receiverId eq vo.receiverId) and + (Notifications.receiverClient eq vo.receiverClient.name) }.count().apply { + log.info("未读通知${this}条") + } + } + } + + /** + * TODO + * + * @param vo + * @return + */ + fun list(vo:NotificationDto): List? { + return vo.page?.let { + transaction { + log.info("page:${it}") + return@transaction Notification.find{ + (Notifications.receiverId eq vo.receiverId) and + (Notifications.receiverClient eq vo.receiverClient.name) }. + limit(n=it.pageSize,offset = (it.currentPage-1)*it.pageSize).map { + NotificationVo(title=it.title,id=it.id.value,content = it.content,createTime = it.createTime.toEpochSecond( + ZoneOffset.of("+8"))*1000) + } + } + } + + } + } /** diff --git a/src/Vo.kt b/src/Vo.kt index b780e7c..86bab83 100644 --- a/src/Vo.kt +++ b/src/Vo.kt @@ -36,9 +36,7 @@ data class UserLogoutVo(val userId:Int) data class UserResDto(val password:String) -data class Token(val token:String, val createTime:Long, val userId:Int) - -data class TokenVo(val token:String,val userId:Int) +data class Token(val token:String, val createTime:Long, val id:Int) sealed class BaseVo{ abstract val token:Token @@ -57,4 +55,11 @@ data class ImageFileDto(val filepath:String,val md5:String,val createTime: Long, 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 +data class InitManagerDto(val account:String,val originPassword:String) + +data class PageDto(val currentPage:Long,val pageSize:Int=10) + +data class NotificationDto(val receiverId:Int, val receiverClient:ReceiverType, override val token: Token, +val page:PageDto?):BaseVo() + +data class NotificationVo(val title:String,val content:String,val id:Int,val createTime: Long) \ No newline at end of file