增加活动详情接口

master
pan 3 years ago
parent 1fdce2ed58
commit 7e43a143a5
  1. 83
      module/src/main/kotlin/com/gyf/csams/module/ShareVo.kt
  2. 44
      src/main/kotlin/com/gyf/csams/Controller.kt
  3. 42
      src/main/kotlin/com/gyf/csams/Dao.kt
  4. 3
      src/main/kotlin/com/gyf/csams/MySQL.kt
  5. 165
      src/main/kotlin/com/gyf/csams/Service.kt
  6. 13
      test/ApplicationTest.kt
  7. 32
      test/ChannelTest.kt

@ -233,9 +233,34 @@ data class QueryAssociationMembers(
override val clientType: ClientType = ClientType.Foreground override val clientType: ClientType = ClientType.Foreground
) : ClientBaseVo() ) : 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() ) : Exam()
data class HistoryActVo(val name: String)
/** /**
* 开放题 * 开放题
@ -462,6 +485,7 @@ data class CheckVo(
data class ActivityVo( data class ActivityVo(
val activityId: Int?=null,
val activityName: String, val activityName: String,
val activityTime: Long, val activityTime: Long,
val activityAddress: String, val activityAddress: String,
@ -472,11 +496,9 @@ data class ActivityVo(
/** /**
* 前台活动申请书 * 前台活动申请书
* *
* @property activityId
* @property clientType * @property clientType
*/ */
data class ActivityApplyVo( data class ActivityApplyVo(
val activityId: Int?,
val associationId: Int, val associationId: Int,
val activityVo: ActivityVo, val activityVo: ActivityVo,
override val token: Token, override val token: Token,
@ -506,7 +528,7 @@ data class ActivityCheckVo(
) )
/** /**
* * 活动详情
*/ */
data class ActivityDetailVo( data class ActivityDetailVo(
val associationVo: AssociationVo, val associationVo: AssociationVo,
@ -514,23 +536,44 @@ data class ActivityDetailVo(
) )
/** /**
* 图片 * 活动条件搜索
* @property name 文件名 *
* @property size 文件大小 * @property associationId
* @property url 文件路径 * @property token
* @property md5 文件hash * @property clientType
* @property createTime 文件创建时间 */
* @property studentId 文件上传人 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( data class ActivityPhotoVo(
val name: String, val name: String,
val size: Long, val url: String
val url: String,
val md5: String,
val createTime: Date,
val studentId: String
) )
/**
* 搜索活动照片
*
* @property activityId
*/
data class SearchActivityPhotoVo(val activityId: Int)
const val MAX_SCORE = 5L const val MAX_SCORE = 5L
/** /**
@ -553,6 +596,7 @@ data class AllOfficerVo(
val publicRelationsDepartment: MutableList<ManagerInfoVo> val publicRelationsDepartment: MutableList<ManagerInfoVo>
) )
/** /**
* 换名申请表 * 换名申请表
* *
@ -566,4 +610,5 @@ data class RenameVo(
val oldName: String, val oldName: String,
val newName: String, val newName: String,
val reason: String val reason: String
) )

@ -403,6 +403,50 @@ fun Application.ActivityController(){
accept(ActivityService) accept(ActivityService)
check(ActivityService) check(ActivityService)
read(ActivityService,Activities) read(ActivityService,Activities)
post("/load"){
val searchActivityVo=call.receive<SearchActivityVo>()
call.respond(ApiResponse(message = "活动列表加载成功",body = ActivityService.load(vo=searchActivityVo)))
}
post("/show"){
val showActivityVo=call.receive<ShowActivityVo>()
call.respond(ApiResponse(message = "活动详情加载成功",body = ActivityService.show(vo=showActivityVo)))
}
route("/photo"){
post{
val searchActivityPhotoVo=call.receive<SearchActivityPhotoVo>()
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<SearchCommentVo>()
call.respond(ApiResponse(message = "评论加载成功",body=ActivityService.loadComment(searchCommentVo)))
}
post("/send"){
val sendBBSVo=call.receive<SendBBSVo>()
try {
ActivityService.sendComment(sendBBSVo)
call.respond(ApiResponse(message = "评论发送成功",body=true))
} catch (e: Exception) {
log.error(e)
call.respond(ApiResponse(message = "评论发送失败",body=true))
}
}
}
} }
} }
} }

@ -326,3 +326,45 @@ class Activity(id:EntityID<Int>):AbstractAudit(id){
return activityName return activityName
} }
} }
@TableComment("相册")
object PhotoAlbums:IntIdTable(){
@TableComment("所属活动")
val activityId:Column<EntityID<Int>> = reference("activity_id",Activities)
@TableComment("照片")
val photoId:Column<EntityID<Int>> = reference("photo_id",ImageFiles)
@TableComment("照片名")
val name:Column<String> = varchar("name",length = 10)
}
class PhotoAlbum(id:EntityID<Int>):IntEntity(id){
companion object:IntEntityClass<PhotoAlbum>(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<String> = varchar("content",length = 80)
@TableComment("评论时间")
val createTime:Column<LocalDateTime> = datetime("create_time").defaultExpression(CurrentDateTime())
@TableComment("评论用户")
val userId:Column<EntityID<Int>> = reference("user_id",Users)
@TableComment("评论活动")
val activityId:Column<EntityID<Int>> = reference("activity_id",Activities)
}
class ActivityComment(id:EntityID<Int>):IntEntity(id){
companion object:IntEntityClass<ActivityComment>(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
}

@ -40,7 +40,8 @@ fun Application.MySQL() {
fun initTable(){ fun initTable(){
transaction { 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) SchemaUtils.createMissingTablesAndColumns(*tableList)
updateComment(*tableList) updateComment(*tableList)

@ -1,6 +1,7 @@
package com.gyf.csams package com.gyf.csams
import com.gyf.csams.FileService.getFormParam
import com.gyf.csams.module.* import com.gyf.csams.module.*
import io.ktor.application.* import io.ktor.application.*
import io.ktor.http.content.* import io.ktor.http.content.*
@ -373,24 +374,26 @@ object FileService : AbstractService() {
throw IllegalArgumentException("找不到key:${key}") throw IllegalArgumentException("找不到key:${key}")
} }
fun List<PartData>.getFormParam(key:String):String{
forEach {
if(it.name==key && it is PartData.FormItem){
return it.value
}
}
throw IllegalArgumentException("没有找到[key=${key}]的表单参数")
}
fun storeFile(data: List<PartData>): List<Int>? { fun storeFile(data: List<PartData>): List<Int>? {
val map = data.associateBy { val userId = data.getFormParam(key="id")
it.name val token = data.getFormParam(key="token")
}.toMutableMap() val createTime = data.getFormParam(key="createTime")
log.info("map=${map}")
val userId = getPartData<PartData.FormItem>(map, "id").value
val token = getPartData<PartData.FormItem>(map, "token").value
val createTime = getPartData<PartData.FormItem>(map, "createTime").value
val tokenVo = Token(token = token, id = userId.toInt(), createTime = createTime.toLong()) val tokenVo = Token(token = token, id = userId.toInt(), createTime = createTime.toLong())
if (AccountService.validUserToken(tokenVo)) { if (AccountService.validUserToken(tokenVo)) {
map.remove("id")
map.remove("token")
val fileIds = mutableListOf<Int>() val fileIds = mutableListOf<Int>()
map.forEach { data.forEach {
val value = it.value if (it is PartData.FileItem) {
if (value is PartData.FileItem) { val fileBytes = it.streamProvider().readBytes()
val fileBytes = value.streamProvider().readBytes() val fileName = it.originalFileName ?: throw IllegalArgumentException("参数异常")
val fileName = value.originalFileName ?: throw IllegalArgumentException("参数异常")
val format = fileBytes.getFormat() val format = fileBytes.getFormat()
val fullFileName = "${fileName}.${format.format}" val fullFileName = "${fileName}.${format.format}"
log.info("fullFileName=$fullFileName") log.info("fullFileName=$fullFileName")
@ -573,7 +576,7 @@ abstract class AuditService<T, E, F> : AbstractService() {
foreground( foreground(
receiverId = matchUser.id.value, receiverId = matchUser.id.value,
content = "您提交的【${entity.notificationName()}${this@AuditService.dataType}初审不通过,可根据初审意见,重新申请\n" + 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 -> { entity.audit.nextAudit != null && vo.result -> {
@ -587,7 +590,7 @@ abstract class AuditService<T, E, F> : AbstractService() {
foreground( foreground(
receiverId = matchUser.id.value, receiverId = matchUser.id.value,
content = "您提交的【${entity.notificationName()}${this@AuditService.dataType}复审不通过,可根据复审意见,重新申请\n" + 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<AssociationRegVo, AuditAssociationVo, A
val nextAudit = AuditLeggings.alias("nextAudit") val nextAudit = AuditLeggings.alias("nextAudit")
return@transaction Associations.innerJoin(otherTable = AuditLeggings) return@transaction Associations.innerJoin(otherTable = AuditLeggings)
.innerJoin(nextAudit, { AuditLeggings.nextAudit }, { nextAudit[AuditLeggings.id] }) .innerJoin(nextAudit, { AuditLeggings.nextAudit }, { nextAudit[AuditLeggings.id] })
.slice(Associations.columns) .slice(listOf(Associations.id))
.select { .select {
nextAudit[AuditLeggings.result] eq true and (Associations.name like "%${vo.name}%") and (Associations.desc like "%${vo.desc}%") nextAudit[AuditLeggings.result] eq true and (Associations.name like "%${vo.name}%") and (Associations.desc like "%${vo.desc}%")
}.map { }.map {
val imageFile = val association=Association.findById(it[Associations.id])?:throw AssociationIdError(it[Associations.id].value)
ImageFile.findById(it[Associations.logo]) ?: throw FileIdError(it[Associations.logo].value) toAssociationVo(association)
AssociationVo(name = it[Associations.name],
associationId = it[Associations.id].value,
logo = imageFile.filepath,
desc = it[Associations.desc],
faculty = it[Associations.faculty].let { it1 ->
AssociationFaculty.valueOf(
it1
)
},
level = it[Associations.level]?.let { it1 -> AssociationLevel.valueOf(it1) })
} }
} }
} }
@ -893,6 +885,110 @@ object ActivityService : AuditService<ActivityApplyVo, AuditActVo,ActivityCheckV
} }
/**
* 上传照片
*
* @param data
*/
fun upload(data: List<PartData>){
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<BBSVo>{
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<ActivityPhotoVo>{
return transaction {
PhotoAlbum.find { PhotoAlbums.activityId eq vo.activityId }.map {
ActivityPhotoVo(name = it.name,url=it.photo.filepath)
}
}
}
/**
* 活动列表
*
*/
fun load(vo:SearchActivityVo):List<ActivityVo>{
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? { override fun read(vo: OnlyToken, table: IntIdTable): ActivityCheckVo? {
val nextAudit = AuditLeggings.alias("nextAudit") val nextAudit = AuditLeggings.alias("nextAudit")
return transaction { return transaction {
@ -916,10 +1012,10 @@ object ActivityService : AuditService<ActivityApplyVo, AuditActVo,ActivityCheckV
transaction { transaction {
val user = User.findById(vo.token.id) ?: throw UserIdError(vo.token.id) val user = User.findById(vo.token.id) ?: throw UserIdError(vo.token.id)
if (vo.activityId != null) { if (vo.activityVo.activityId != null) {
//再次提交 //再次提交
log.info("再次提交【${vo.activityVo.activityName}${dataType}") log.info("再次提交【${vo.activityVo.activityName}${dataType}")
val activity = Activity.findById(vo.activityId!!) ?: throw ActivityIdError(vo.activityId!!) val activity = Activity.findById(vo.activityVo.activityId!!) ?: throw ActivityIdError(vo.activityVo.activityId!!)
activity.apply { activity.apply {
activityName = vo.activityVo.activityName activityName = vo.activityVo.activityName
activityTime = vo.activityVo.activityTime.toLocalDateTime() activityTime = vo.activityVo.activityTime.toLocalDateTime()
@ -963,7 +1059,8 @@ object ActivityService : AuditService<ActivityApplyVo, AuditActVo,ActivityCheckV
return ActivityVo( return ActivityVo(
activityName = activity.activityName, activityTime = activity.activityTime.toLong(), activityName = activity.activityName, activityTime = activity.activityTime.toLong(),
activityAddress = activity.activityAddress, activityDesc = activity.activityDesc, activityAddress = activity.activityAddress, activityDesc = activity.activityDesc,
activitySize = activity.activitySize activitySize = activity.activitySize,
activityId = activity.id.value
) )
} }
@ -1059,7 +1156,7 @@ object NotificationService : AbstractService() {
return@transaction Notification.find { return@transaction Notification.find {
(Notifications.receiverId eq vo.receiverId) and (Notifications.receiverId eq vo.receiverId) and
(Notifications.receiverClient eq vo.receiverClient.name) (Notifications.receiverClient eq vo.receiverClient.name)
}.map { }.sortedByDescending { it.createTime }.map {
NotificationVo( NotificationVo(
title = it.title, title = it.title,
id = it.id.value, id = it.id.value,

@ -12,6 +12,7 @@ import com.gyf.csams.module.UserRegVo
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.and
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
import java.io.File import java.io.File
import java.time.LocalDateTime import java.time.LocalDateTime
@ -117,16 +118,8 @@ class ApplicationTest {
fun testAudit(){ fun testAudit(){
initApp { initApp {
transaction { transaction {
Association.all().sortedBy { Notification.all().sortedByDescending { it.createTime }.forEach {
when{ println(it.createTime)
it.audit.nextAudit==null&&it.audit.manager==null->1
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 {
} }
} }
} }

@ -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<Int> {
var x = 1
while (true) send(x++) // infinite stream of integers starting from 1
}
@OptIn(ExperimentalCoroutinesApi::class)
fun CoroutineScope.square(numbers: ReceiveChannel<Int>): ReceiveChannel<Int> = produce {
for (x in numbers) send(x * x)
}
Loading…
Cancel
Save