diff --git a/module/src/main/kotlin/com/gyf/csams/module/ShareVo.kt b/module/src/main/kotlin/com/gyf/csams/module/ShareVo.kt index 16aa663..13a7c9e 100644 --- a/module/src/main/kotlin/com/gyf/csams/module/ShareVo.kt +++ b/module/src/main/kotlin/com/gyf/csams/module/ShareVo.kt @@ -233,9 +233,34 @@ data class QueryAssociationMembers( override val clientType: ClientType = ClientType.Foreground ) : ClientBaseVo() +/** + * 交流区 + * + * @property user + * @property createTime + * @property content + */ +data class BBSVo(val user: UserInfoVo, val createTime: Long, val content: String) -data class BBSVo(val studentId: String, val name: String, val createTime: Date, val content: String) +/** + * 发送评论 + * + * @property content + * @property token + * @property clientType + */ +data class SendBBSVo(val content:String, + val activityId: Int, + override val token: Token, + override val clientType: ClientType=ClientType.Foreground +):ClientBaseVo() +/** + * 搜索评论 + * + * @property activityId + */ +data class SearchCommentVo(val activityId: Int) /** * 题型 @@ -275,8 +300,6 @@ data class ChoiceQuestionVo( ) : Exam() -data class HistoryActVo(val name: String) - /** * 开放题 @@ -462,6 +485,7 @@ data class CheckVo( data class ActivityVo( + val activityId: Int?=null, val activityName: String, val activityTime: Long, val activityAddress: String, @@ -472,11 +496,9 @@ data class ActivityVo( /** * 前台活动申请书 * - * @property activityId * @property clientType */ data class ActivityApplyVo( - val activityId: Int?, val associationId: Int, val activityVo: ActivityVo, override val token: Token, @@ -506,7 +528,7 @@ data class ActivityCheckVo( ) /** - * + * 活动详情 */ data class ActivityDetailVo( val associationVo: AssociationVo, @@ -514,23 +536,44 @@ data class ActivityDetailVo( ) /** - * 图片 - * @property name 文件名 - * @property size 文件大小 - * @property url 文件路径 - * @property md5 文件hash - * @property createTime 文件创建时间 - * @property studentId 文件上传人 + * 活动条件搜索 + * + * @property associationId + * @property token + * @property clientType + */ +data class SearchActivityVo(val associationId: Int, override val token: Token, + override val clientType: ClientType=ClientType.Foreground):ClientBaseVo() + +/** + * 活动id搜索 + * + * @property activityId + * @property token + * @property clientType + */ +data class ShowActivityVo(val activityId:Int, override val token: Token, + override val clientType: ClientType=ClientType.Foreground):ClientBaseVo() + + +/** + * 活动照片 + * + * @property name + * @property url */ data class ActivityPhotoVo( val name: String, - val size: Long, - val url: String, - val md5: String, - val createTime: Date, - val studentId: String + val url: String ) +/** + * 搜索活动照片 + * + * @property activityId + */ +data class SearchActivityPhotoVo(val activityId: Int) + const val MAX_SCORE = 5L /** @@ -553,6 +596,7 @@ data class AllOfficerVo( val publicRelationsDepartment: MutableList ) + /** * 换名申请表 * @@ -566,4 +610,5 @@ data class RenameVo( val oldName: String, val newName: String, val reason: String -) \ No newline at end of file +) + diff --git a/src/main/kotlin/com/gyf/csams/Controller.kt b/src/main/kotlin/com/gyf/csams/Controller.kt index 79ddf6b..1d97641 100644 --- a/src/main/kotlin/com/gyf/csams/Controller.kt +++ b/src/main/kotlin/com/gyf/csams/Controller.kt @@ -403,6 +403,50 @@ fun Application.ActivityController(){ accept(ActivityService) check(ActivityService) read(ActivityService,Activities) + + post("/load"){ + val searchActivityVo=call.receive() + call.respond(ApiResponse(message = "活动列表加载成功",body = ActivityService.load(vo=searchActivityVo))) + } + + post("/show"){ + val showActivityVo=call.receive() + call.respond(ApiResponse(message = "活动详情加载成功",body = ActivityService.show(vo=showActivityVo))) + } + + route("/photo"){ + post{ + val searchActivityPhotoVo=call.receive() + call.respond(ApiResponse(message = "活动相册加载成功",body = ActivityService.load(searchActivityPhotoVo))) + } + post("upload"){ + log.info("开始上传活动照片") + val multipartData = call.receiveMultipart() + multipartData.readAllParts().apply { + log.info("part size=$size") + ActivityService.upload(this) + call.respond(ApiResponse(message = "照片上传成功",body = true)) + } + log.info("----end-----") + } + } + + route("/comment"){ + post{ + val searchCommentVo=call.receive() + call.respond(ApiResponse(message = "评论加载成功",body=ActivityService.loadComment(searchCommentVo))) + } + post("/send"){ + val sendBBSVo=call.receive() + try { + ActivityService.sendComment(sendBBSVo) + call.respond(ApiResponse(message = "评论发送成功",body=true)) + } catch (e: Exception) { + log.error(e) + call.respond(ApiResponse(message = "评论发送失败",body=true)) + } + } + } } } } diff --git a/src/main/kotlin/com/gyf/csams/Dao.kt b/src/main/kotlin/com/gyf/csams/Dao.kt index bcd08eb..f31b5b9 100644 --- a/src/main/kotlin/com/gyf/csams/Dao.kt +++ b/src/main/kotlin/com/gyf/csams/Dao.kt @@ -326,3 +326,45 @@ class Activity(id:EntityID):AbstractAudit(id){ return activityName } } + +@TableComment("相册") +object PhotoAlbums:IntIdTable(){ + @TableComment("所属活动") + val activityId:Column> = reference("activity_id",Activities) + + @TableComment("照片") + val photoId:Column> = reference("photo_id",ImageFiles) + + @TableComment("照片名") + val name:Column = varchar("name",length = 10) +} + +class PhotoAlbum(id:EntityID):IntEntity(id){ + companion object:IntEntityClass(PhotoAlbums) + var activity by Activity referencedOn PhotoAlbums.activityId + var photo by ImageFile referencedOn PhotoAlbums.photoId + var name by PhotoAlbums.name +} + +@TableComment("活动评论") +object ActivityComments:IntIdTable(){ + @TableComment("评论内容") + val content:Column = varchar("content",length = 80) + + @TableComment("评论时间") + val createTime:Column = datetime("create_time").defaultExpression(CurrentDateTime()) + + @TableComment("评论用户") + val userId:Column> = reference("user_id",Users) + + @TableComment("评论活动") + val activityId:Column> = reference("activity_id",Activities) +} + +class ActivityComment(id:EntityID):IntEntity(id){ + companion object:IntEntityClass(ActivityComments) + var content by ActivityComments.content + var createTime by ActivityComments.createTime + var user by User referencedOn ActivityComments.userId + var activity by Activity referencedOn ActivityComments.activityId +} \ No newline at end of file diff --git a/src/main/kotlin/com/gyf/csams/MySQL.kt b/src/main/kotlin/com/gyf/csams/MySQL.kt index 6298701..96f79f1 100644 --- a/src/main/kotlin/com/gyf/csams/MySQL.kt +++ b/src/main/kotlin/com/gyf/csams/MySQL.kt @@ -40,7 +40,8 @@ fun Application.MySQL() { fun initTable(){ transaction { - val tableList= arrayOf(Users,UserTokens,Managers,ManagerTokens,AuditLeggings,LeaveMessages,ImageFiles,Associations,AssociationMembers,Notifications,Activities) + val tableList= arrayOf(Users,UserTokens,Managers,ManagerTokens,AuditLeggings,LeaveMessages, + ImageFiles,Associations,AssociationMembers,Notifications,Activities,PhotoAlbums,ActivityComments) SchemaUtils.createMissingTablesAndColumns(*tableList) updateComment(*tableList) diff --git a/src/main/kotlin/com/gyf/csams/Service.kt b/src/main/kotlin/com/gyf/csams/Service.kt index 7aeb05a..010cb6d 100644 --- a/src/main/kotlin/com/gyf/csams/Service.kt +++ b/src/main/kotlin/com/gyf/csams/Service.kt @@ -1,6 +1,7 @@ package com.gyf.csams +import com.gyf.csams.FileService.getFormParam import com.gyf.csams.module.* import io.ktor.application.* import io.ktor.http.content.* @@ -373,24 +374,26 @@ object FileService : AbstractService() { throw IllegalArgumentException("找不到key:${key}") } + fun List.getFormParam(key:String):String{ + forEach { + if(it.name==key && it is PartData.FormItem){ + return it.value + } + } + 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 createTime = getPartData(map, "createTime").value + val userId = data.getFormParam(key="id") + val token = data.getFormParam(key="token") + val createTime = data.getFormParam(key="createTime") val tokenVo = Token(token = token, id = userId.toInt(), createTime = createTime.toLong()) if (AccountService.validUserToken(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("参数异常") + data.forEach { + if (it is PartData.FileItem) { + val fileBytes = it.streamProvider().readBytes() + val fileName = it.originalFileName ?: throw IllegalArgumentException("参数异常") val format = fileBytes.getFormat() val fullFileName = "${fileName}.${format.format}" log.info("fullFileName=$fullFileName") @@ -573,7 +576,7 @@ abstract class AuditService : AbstractService() { foreground( receiverId = matchUser.id.value, content = "您提交的【${entity.notificationName()}】${this@AuditService.dataType}初审不通过,可根据初审意见,重新申请\n" + - "【初审意见:${vo.cause},审核人:${entity.audit.manager?.desc ?: ""}】" + "【初审意见:${vo.cause},审核人:${entity.audit.manager?.duty ?: ""}】" ) } entity.audit.nextAudit != null && vo.result -> { @@ -587,7 +590,7 @@ abstract class AuditService : AbstractService() { foreground( receiverId = matchUser.id.value, content = "您提交的【${entity.notificationName()}】${this@AuditService.dataType}复审不通过,可根据复审意见,重新申请\n" + - "【复审意见:${vo.cause},审核人:${entity.audit.nextAudit?.manager?.desc ?: ""}】" + "【复审意见:${vo.cause},审核人:${entity.audit.nextAudit?.manager?.duty ?: ""}】" ) } } @@ -756,23 +759,12 @@ object AssociationService : AuditService - AssociationFaculty.valueOf( - it1 - ) - }, - level = it[Associations.level]?.let { it1 -> AssociationLevel.valueOf(it1) }) + val association=Association.findById(it[Associations.id])?:throw AssociationIdError(it[Associations.id].value) + toAssociationVo(association) } } } @@ -893,6 +885,110 @@ object ActivityService : AuditService){ + val activityId=data.getFormParam("activityId") + val name=data.getFormParam("name") + transaction { + (Activity.findById(activityId.toInt())?:throw IllegalArgumentException("活动id参数有误:${activityId}")).apply { + val fileId=FileService.storeFile(data) + //保存到相册 + fileId?.forEach { + PhotoAlbum.new { + activity=this@apply + photo=ImageFile.findById(it)?:throw FileIdError(it) + this.name=name + }.let { + log.info("保存照片:${it.name}") + } + } + } + } + } + + /** + * 发送评论 + * + * @param vo + */ + fun sendComment(vo:SendBBSVo){ + transaction { + ActivityComment.new { + content=vo.content + user=User.findById(vo.token.id)?:throw UserIdError(vo.token.id) + activity=Activity.findById(vo.activityId)?:throw ActivityIdError(vo.activityId) + } + } + } + + /** + * 加载评论 + * + * @param vo + * @return + */ + fun loadComment(vo: SearchCommentVo):List{ + return transaction { + ActivityComment.find { ActivityComments.activityId eq vo.activityId }.map { + BBSVo(user = UserInfoVo(name=it.user.name,headImg = it.user.headImg?.filepath,desc = it.user.desc), + createTime = it.createTime.toLong(),content = it.content) + } + } + } + + /** + * 加载活动照片 + * + * @param vo + * @return + */ + fun load(vo:SearchActivityPhotoVo):List{ + return transaction { + PhotoAlbum.find { PhotoAlbums.activityId eq vo.activityId }.map { + ActivityPhotoVo(name = it.name,url=it.photo.filepath) + } + } + } + + + + /** + * 活动列表 + * + */ + fun load(vo:SearchActivityVo):List{ + return transaction { + val nextAudit = AuditLeggings.alias("nextAudit") + return@transaction Activities.innerJoin(otherTable = AuditLeggings) + .innerJoin(nextAudit, { AuditLeggings.nextAudit }, { nextAudit[AuditLeggings.id] }) + .slice(listOf(Activities.id)) + .select { + nextAudit[AuditLeggings.result] eq true and (Activities.associationId eq vo.associationId) + }.map { + val activity=Activity.findById(it[Activities.id])?:throw ActivityIdError(it[Activities.id].value) + toActivityVo(activity = activity) + } + } + } + + /** + * 活动详情 + * + * @param vo + * @return + */ + fun show(vo:ShowActivityVo):ActivityDetailVo{ + return transaction { + (Activity.findById(vo.activityId)?:throw ActivityIdError(vo.activityId)).let { + ActivityDetailVo(activityVo = toActivityVo(it),associationVo = AssociationService.toAssociationVo(it.association)) + } + } + } + override fun read(vo: OnlyToken, table: IntIdTable): ActivityCheckVo? { val nextAudit = AuditLeggings.alias("nextAudit") return transaction { @@ -916,10 +1012,10 @@ object ActivityService : AuditService1 - it.audit.nextAudit == null && it.audit.manager != null -> 2 - it.audit.nextAudit != null && it.audit.nextAudit?.manager == null -> 3 - it.audit.nextAudit != null && it.audit.nextAudit?.result == null -> 4 - else -> 5 - } - }.forEach { - + Notification.all().sortedByDescending { it.createTime }.forEach { + println(it.createTime) } } } diff --git a/test/ChannelTest.kt b/test/ChannelTest.kt deleted file mode 100644 index 52c18ed..0000000 --- a/test/ChannelTest.kt +++ /dev/null @@ -1,32 +0,0 @@ -/* - * 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