通知接口对接客户端

master
pan 3 years ago
parent 80468467b0
commit f9a31eed5b
  1. 2
      src/Application.kt
  2. 46
      src/Controller.kt
  3. 37
      src/Dao.kt
  4. 2
      src/MySQL.kt
  5. 90
      src/Service.kt
  6. 13
      src/Vo.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)
}
}

@ -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 <reified T : BaseVo> withToken(call: ApplicationCall, callback: (vo: T) -> Unit) {
val levelVo = call.receive<T>()
@ -60,7 +59,7 @@ fun Application.AccountController() {
call.respond(ApiResponse(message = if (token != null) "登陆成功" else "账号或密码错误!!!", body = token))
}
post(path = "/token") {
val tokenVo = call.receive<TokenVo>()
val tokenVo = call.receive<Token>()
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<T>:NotificationVo(){
sealed class SysNotificationVo<T>: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<NotificationVo>()
suspend fun sendNotification(vo:NotificationVo){
channel.send(vo)
}
fun Application.NotificationController(){
routing {
route("${ApiPathPrefix}/notification") {
post("/pull") {
withToken<NotificationDto>(call) {
val notificationList = NotificationService.pull(vo = it)
call.respond(
ApiResponse(
message = if (notificationList.isEmpty()) "没有最新通知" else "获取${notificationList.size}条最新通知",
body = notificationList
)
)
}
}
post("/count"){
withToken<NotificationDto>(call) {
call.respond(ApiResponse(message = "统计未读通知",body = NotificationService.count(it)))
}
}
post("/list"){
withToken<NotificationDto>(call){
call.respond(ApiResponse(message = "获取通知列表",body=NotificationService.list(it)))
}
}
}
}
}

@ -166,12 +166,43 @@ object CheckForms:IntIdTable(){
class CheckForm(id:EntityID<Int>):IntEntity(id){
companion object:IntEntityClass<CheckForm>(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<String> = varchar(name="title",length = 10)
@TableComment("通知内容")
val content:Column<String> = varchar(name="content",length = 30)
@TableComment("接收者")
val receiverId:Column<Int> = integer(name="receiver_id")
@TableComment("接收客户端")
val receiverClient:Column<String> = varchar(name="receiver_client",length = 10)
@TableComment("阅读状态")
val read:Column<Boolean> = bool("read").default(false)
@TableComment("拉取状态")
val pull:Column<Boolean> = bool("pull").default(false)
@TableComment("通知创建时间")
val createTime:Column<LocalDateTime> = datetime("create_time").defaultExpression(CurrentDateTime())
}
class Notification(id:EntityID<Int>):IntEntity(id){
companion object:IntEntityClass<Notification>(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
}

@ -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)

@ -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<PartData.FormItem>(map,"id").value
val token= getPartData<PartData.FormItem>(map,"token").value
val tokenVo=TokenVo(token=token,userId=userId.toInt())
val createTime= getPartData<PartData.FormItem>(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<NotificationVo>{
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<NotificationVo>? {
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)
}
}
}
}
}
/**

@ -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)
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)
Loading…
Cancel
Save