package com.gyf.csams import com.gyf.csams.FileService.getFormParam import com.gyf.csams.module.* import io.ktor.application.* import io.ktor.http.content.* import io.ktor.util.* import org.jetbrains.exposed.dao.IntEntityClass import org.jetbrains.exposed.dao.id.EntityID import org.jetbrains.exposed.dao.id.IntIdTable import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.transactions.transaction import org.slf4j.Logger import java.io.File import java.time.LocalDateTime import kotlin.properties.Delegates import kotlin.system.exitProcess interface BaseService { fun init(environment: ApplicationEnvironment) } abstract class AbstractService : BaseService { protected lateinit var log: Logger protected lateinit var environment: ApplicationEnvironment override fun init(environment: ApplicationEnvironment) { this.environment = environment this.log = environment.log } } /** * 账号服务 */ object AccountService : AbstractService() { /** * 检查学号是否已注册,true=已注册 */ fun registered(selectId: String): Boolean { return transaction { return@transaction !User.find { Users.studentId eq selectId }.empty() } } fun toUserInfo(user: User): UserInfoVo { return UserInfoVo(name = user.name, headImg = user.headImg?.filepath, desc = user.desc) } /** * 注册 */ fun register(userVo: UserRegVo): String? { try { return transaction { val originPassword = if (environment.developmentMode) (1..8).joinToString("") else randomNum(8) log.info("密码:${originPassword}") User.new { studentId = userVo.studentId name = userVo.name password = originPassword.md5() } return@transaction originPassword } } catch (e: Exception) { log.error("注册失败,发生异常:$e") return null } } fun login(vo: BaseLoginVo, ip: String): OwnInfoVo { return when (vo) { is UserLoginVo -> login(vo, ip) is ManagerLoginVo -> login(vo, ip) else -> throw IllegalArgumentException("vo:${vo::class.java}类型不合法") } } /** * 前台登录 * */ private fun login(userLoginVo: UserLoginVo, _ip: String): UserVo { return transaction { val matchUser = User.find { Users.studentId eq userLoginVo.studentId }.firstOrNull() when { matchUser == null -> { log.warn("学号:${userLoginVo.studentId}不存在") throw StudentIdError(userLoginVo.studentId) } userLoginVo.password.md5() != matchUser.password -> { log.warn("密码:${userLoginVo.password}错误") throw PasswordError(id = userLoginVo.studentId, password = userLoginVo.password) } else -> { val token = UserToken.new { user = matchUser ip = _ip device = userLoginVo.device token = listOf(matchUser.id, ip, device).joinToString(separator = ('a'..'z').random().toString()) .md5() } token.flush() val associationMember = AssociationMember.find { AssociationMembers.userId eq matchUser.id }.firstOrNull() return@transaction UserVo( studentId = matchUser.studentId, token = Token( id = matchUser.id.value, token = token.token, createTime = token.createTime.toLong() ), name = matchUser.name, headImg = matchUser.headImg?.filepath, desc = matchUser.desc, associationVo = associationMember?.let { AssociationService.toAssociationVo(it.association) }, isHead = associationMember?.isHead ) } } } } /** * 后台登陆 * * @return */ private fun login(managerLoginVo: ManagerLoginVo, _ip: String): ManagerVo { return transaction { val matchManager = Manager.find { Managers.account eq managerLoginVo.account }.firstOrNull() when { matchManager == null -> { log.warn("账号:${managerLoginVo.account}不存在") throw AccountError(managerLoginVo.account) } managerLoginVo.password.md5() != matchManager.password -> { log.warn("密码:${managerLoginVo.password}错误") throw PasswordError(id = managerLoginVo.account, password = managerLoginVo.password) } else -> { val token = ManagerToken.new { manager = matchManager ip = _ip device = managerLoginVo.device token = listOf(matchManager.id, ip, device).joinToString(separator = ('a'..'z').random().toString()) .md5() } token.flush() return@transaction tokenRes(token, matchManager) } } } } fun uploadHeadImg(data: List) { val userId = data.getFormParam("id").toInt() transaction { val fileIds = FileService.storeFile(data) if (fileIds?.isNotEmpty() == true) { val fileId = fileIds.first() val user = User.findById(userId) ?: throw UserIdError(userId) user.apply { headImg = ImageFile.findById(fileId) ?: throw FileIdError(fileId) } } } } fun refreshInfo(vo: InfoUpdateVo): PersonInfoVo { return transaction { when (vo.clientType) { ClientType.Foreground -> { val matchUser = User.findById(vo.token.id) ?: throw UserIdError(vo.token.id) matchUser.apply { vo.name?.let { matchUser.name = it } vo.desc?.let { matchUser.desc = it } } val token = UserToken.find { UserTokens.userId eq matchUser.id }.firstOrNull() ?: throw IllegalArgumentException("无法根据[userId=${matchUser.id}]找到token") tokenRes(token = token, matchUser = matchUser) } ClientType.Background -> { val matchManager = Manager.findById(vo.token.id) ?: throw ManagerIdError(vo.token.id) matchManager.apply { vo.name?.let { matchManager.name = it } vo.desc?.let { matchManager.desc = it } } val token = ManagerToken.find { ManagerTokens.managerId eq matchManager.id }.firstOrNull() ?: throw IllegalArgumentException("无法根据[matchId=${matchManager.id}]找到token") tokenRes(token = token, matchManager = matchManager) } } } } private fun tokenRes(token: ManagerToken, matchManager: Manager): ManagerVo { return ManagerVo( account = matchManager.account, token = Token( id = matchManager.id.value, token = token.token, createTime = token.createTime.toLong() ), desc = matchManager.desc, duty = Duty.valueOf(matchManager.duty), headImg = matchManager.headImg?.filepath, name = matchManager.name ) } private fun tokenRes(token: UserToken, matchUser: User): UserVo { val associationMember = AssociationMember.find { AssociationMembers.userId eq matchUser.id }.firstOrNull() return UserVo( studentId = matchUser.studentId, token = Token( id = matchUser.id.value, token = token.token, createTime = token.createTime.toLong() ), name = matchUser.name, headImg = matchUser.headImg?.filepath, desc = matchUser.desc, associationVo = associationMember?.let { AssociationService.toAssociationVo(it.association) }, isHead = associationMember?.isHead ) } fun getManagerVo(token: Token): ManagerVo { return transaction { val c = ManagerToken.find { ManagerTokens.managerId eq token.id ManagerTokens.token eq token.token } if (!c.empty()) { val manager = Manager.findById(token.id) return@transaction tokenRes(token = c.first(), matchManager = manager!!) } else { throw IllegalArgumentException("token校验失败") } } } fun getUserVo(token: Token): UserVo { return transaction { val c = UserToken.find { UserTokens.userId eq token.id UserTokens.token eq token.token } if (!c.empty()) { val user = User.findById(token.id) return@transaction tokenRes(token = c.first(), matchUser = user!!) } else { throw IllegalArgumentException("token校验失败") } } } private fun validManagerToken(token: Token): Boolean { return transaction { !ManagerToken.find { ManagerTokens.managerId eq token.id ManagerTokens.token eq token.token }.empty() } } fun validUserToken(token: Token): Boolean { return transaction { !UserToken.find { UserTokens.userId eq token.id UserTokens.token eq token.token }.empty() } } fun validToken(vo: T): Boolean { return when (vo.clientType) { ClientType.Foreground -> validUserToken(vo.token) ClientType.Background -> validManagerToken(vo.token) else -> throw IllegalArgumentException("不清楚是来自哪个平台的token信息") } } fun logout(vo: OnlyToken): Boolean { return transaction { if (vo.clientType == ClientType.Foreground) UserTokens.deleteWhere { UserTokens.userId eq vo.token.id } > 0 else ManagerTokens.deleteWhere { ManagerTokens.managerId eq vo.token.id } > 0 } } fun test() { log.info("开始测试") transaction { log.info("查询到个${User.count()}用户") } log.info("结束测试") } } /** * 主页服务 */ object MainService : AbstractService() { private var maxSize by Delegates.notNull() override fun init(environment: ApplicationEnvironment) { super.init(environment) this.maxSize = environment.config.property("ktor.deployment.leaveMessage.maxSize").getString().toInt() } /** * 创建留言信息 * * @param leaveMessageVo * @return */ fun createMessage(leaveMessageVo: LeaveMessageVo): Boolean { return if (leaveMessageVo.message.isNotEmpty()) { return transaction { val count = LeaveMessage.count().toInt() log.info("系统留言数:$count,限制数:$maxSize") if (count >= maxSize) { LeaveMessage.all().sortedBy { it.createTime }.subList(0, count - maxSize).forEach { it.delete() } } log.info("保存留言:${leaveMessageVo.message}") LeaveMessage.new { user = User.findById(leaveMessageVo.token.id) ?: throw IllegalArgumentException("非法id") message = leaveMessageVo.message } log.info("留言保存成功") return@transaction true } } else { log.info("留言不能为空") false } } fun getAllLeaveMessage(): List { return transaction { log.info("获取所有留言") return@transaction LeaveMessage.all().toList().map { LeaveMessageDto( message = "${it.user.name}说:${it.message}", user = AccountService.toUserInfo(user = it.user) ) } } } } /** * 文件管理服务 */ 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() log.info(this::class.java.classLoader.getResource("")?.path) filePath = environment.classLoader.getResource(uploadDir)?.path ?: throw IllegalArgumentException("初始化资源目录失败") File(filePath).let { when { it.exists() -> log.info("上传路径[${filePath}]已存在") !it.mkdirs() -> throw IllegalArgumentException("上传路径[${filePath}]创建失败") } } } 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 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 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)) { val fileIds = mutableListOf() 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") val file = File(filePath, fullFileName).apply { writeBytes(fileBytes) } log.info("文件成功保存到${file.absolutePath}") val fileId = save(id = userId, "/${uploadDir}/${fullFileName}", file = file) fileIds.add(fileId) } } return fileIds } else { return null } } } /** * 审核资料 * * @param T 注册实体类型 * @param E 后台注册实体类型 * @param F 前台审核进度类型 */ abstract class AuditService : AbstractService() { abstract val dataType: String /** * 注册资料 * * @param vo */ abstract fun register(vo: T) // protected abstract fun load /** * 通知用户审核进度 * * @param receiverId * @param content */ private fun foreground(receiverId: Int, content: String) { Notification.new { this.title = dataType this.content = content this.receiverId = receiverId receiverClient = ClientType.Foreground.name }.apply { log.info("通知前台用户${dataType}处理进度") } } /** * 前台任务通知管理员处理 * */ private fun background(content: String, duty: Duty) { Manager.find { Managers.duty eq duty.name }.apply { if (count() == 0L) { log.warn("找不到适当的${duty.desc}处理此任务") } else { forEach { Notification.new { this.title = dataType this.content = content receiverId = it.id.value receiverClient = ClientType.Background.name }.apply { log.info("通知${duty.desc}处理${dataType}") } } } } } /** * 查找审核资料 * * @param auditId * @return */ abstract fun findEntity(auditId: Int): AbstractAudit protected inline fun , reified Table : AbstractAudits> testFind( table: Table, row: Row, auditId: Int ): E { return row.find { table.auditId eq auditId }.firstOrNull() ?: throw AuditIdError(auditId) } /** * 更新审核结果 * * @param vo */ private fun AuditLogging.update(vo: CheckVo) { apply { if (nextAudit != null) { nextAudit?.apply { cause = vo.cause result = vo.result auditTime = LocalDateTime.now() } } else { cause = vo.cause result = vo.result auditTime = LocalDateTime.now() } }.apply { log.info("更新审核结果") } } /** * 通过审核后 * */ protected open fun afterAudit(entity: AbstractAudit) {} /** * 资料受理 * * @param vo */ fun accept(vo: AcceptVo) { transaction { val association = findEntity(vo.auditId) when (val manager = Manager.findById(vo.token.id)) { null -> throw ManagerIdError(vo.token.id) else -> { association.apply { val nextAudit = audit.nextAudit if (nextAudit == null) { audit.manager = manager audit.acceptTime = LocalDateTime.now() } else { nextAudit.manager = manager nextAudit.acceptTime = LocalDateTime.now() } } log.info("[${association.notificationName()}]${this@AuditService.dataType}已受理") Notification.new { title = this@AuditService.dataType content = "您提交的[${association.notificationName()}]${this@AuditService.dataType}已受理" receiverId = vo.token.id receiverClient = ClientType.Foreground.name } } } } } /** * 资料审核 * * @param vo */ fun check(vo: CheckVo) { transaction { try { val entity = findEntity(vo.auditId) val matchUser = entity.audit.user entity.audit.update(vo) when { entity.audit.nextAudit == null && vo.result -> { entity.audit.nextAudit = AuditLogging.new { this.user = matchUser }.apply { log.info("构造${this@AuditService.dataType}复审记录") } background( content = "总部长上报了一份${this@AuditService.dataType}需要您进行受理", duty = Duty.Teacher ) foreground( receiverId = matchUser.id.value, content = "您提交的【${entity.notificationName()}】${this@AuditService.dataType}初审通过,请耐心等待复审" ) } entity.audit.nextAudit == null && !vo.result -> { foreground( receiverId = matchUser.id.value, content = "您提交的【${entity.notificationName()}】${this@AuditService.dataType}初审不通过,可根据初审意见,重新申请\n" + "【初审意见:${vo.cause},审核人:${entity.audit.manager?.duty ?: ""}】" ) } entity.audit.nextAudit != null && vo.result -> { afterAudit(entity) foreground( receiverId = matchUser.id.value, content = "您提交的【${entity.notificationName()}】${this@AuditService.dataType}复审通过" ) } else -> { foreground( receiverId = matchUser.id.value, content = "您提交的【${entity.notificationName()}】${this@AuditService.dataType}复审不通过,可根据复审意见,重新申请\n" + "【复审意见:${vo.cause},审核人:${entity.audit.nextAudit?.manager?.duty ?: ""}】" ) } } log.info("【${entity.notificationName()}】${this@AuditService.dataType}审核完成") } catch (e: Exception) { rollback() throw e } } } //过滤完成的审核记录?(可多次提交的资料) open val isFilter: Boolean = true /** * TODO 审核进度查询 * */ fun read(vo: OnlyToken, table: IntIdTable): F? { return transaction { if (isFilter) { // val nextAudit = AuditLeggings.alias("nextAudit") val c = table.innerJoin(AuditLeggings) // .innerJoin(nextAudit, { AuditLeggings.nextAudit }, { nextAudit[AuditLeggings.id] }) .innerJoin(Users) .slice(listOf(table.id, AuditLeggings.result, AuditLeggings.nextAudit)) .select { Users.id eq vo.token.id } .filter { log.info("it[AuditLeggings.result]=${it[AuditLeggings.result]},it[AuditLeggings.nextAudit]=${it[AuditLeggings.nextAudit]}") it[AuditLeggings.result] == null || it[AuditLeggings.nextAudit]?.let { it1 -> AuditLogging.findById( it1 )?.result } == null } log.info("size:${c.size}") if (c.size == 1) { getCheckVo(c.first()[table.id]) } else { null } } else { table.innerJoin(AuditLeggings).innerJoin(Users) .slice(listOf(table.id)) .select { Users.id eq vo.token.id } .firstOrNull() ?.let { it -> getCheckVo(id = it[table.id]) } } } } abstract fun getCheckVo(id: EntityID): F protected fun toCheckVo(audit: AuditLogging): AuditCheckVo { return AuditCheckVo( checkStatus = when { audit.nextAudit == null && audit.manager == null -> CheckStatus.WaitFirst audit.nextAudit == null && audit.manager != null && audit.result == null -> CheckStatus.AcceptFirst audit.nextAudit != null && audit.nextAudit?.manager == null -> CheckStatus.WaitLast audit.nextAudit != null && audit.nextAudit?.result == null -> CheckStatus.AcceptLast else -> CheckStatus.Finish }, applyTime = audit.applyTime.toLong(), firstCause = audit.cause ?: "", lastCause = audit.nextAudit?.cause, result = audit.nextAudit?.result == true ) } /** * 查看审核记录 * * @param vo * @return */ abstract fun audit(vo: OnlyToken): List protected fun createSortedBy(table: IntEntityClass): List { return table.all().sortedBy { //按初审受理>等待复审>复审受理>完成审核 排序 when { 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 } } } protected fun toAuditLoggingVo(it: AuditLogging?): AuditLoggingVo? { return it?.let { logging -> val auditLogging = AuditLoggingVo( id = logging.id.value, user = AccountService.toUserInfo(user = logging.user), applyTime = logging.applyTime.toLong(), manager = logging.manager?.let { ManagerInfoVo( duty = Duty.valueOf(it.duty), name = it.name, headImg = it.headImg?.filepath, desc = it.desc ) }, acceptTime = logging.acceptTime?.toLong(), cause = logging.cause, result = logging.result, auditTime = logging.auditTime?.toLong(), nextAudit = toAuditLoggingVo(logging.nextAudit) ) auditLogging } } protected fun createNotifications(vo: ClientBaseVo, user: User) { foreground( receiverId = vo.token.id, content = "您成功提交了一份${dataType},请耐心等待后台受理" ) background( content = "用户${user.name}提交了一份${dataType}需要您进行受理", duty = Duty.PamphaBhusal ) } } /** * 社团服务 */ object AssociationService : AuditService() { override val dataType: String = "社团注册资料" override fun findEntity(auditId: Int): AbstractAudit { return testFind(Associations, Association, auditId) } override val isFilter: Boolean = false override fun afterAudit(entity: AbstractAudit) { AssociationMember.new { user = entity.audit.user this.association = Association.find { Associations.auditId eq entity.audit.id }.firstOrNull() ?: throw IllegalArgumentException("无法根据[audit=${entity.audit.id}]找到社团信息") isHead = true }.apply { this@AssociationService.log.info("初始化社团团长") } } fun Association.set(vo: AssociationRegVo, user: User) { name = vo.name desc = vo.desc logo = ImageFile.findById(vo.fileId) ?: throw FileIdError(vo.fileId) this.audit = AuditLogging.new { this.user = user } } /** * 加载所有社团 * * @return */ fun loadAll(): List { return transaction { Association.all().map { toAssociationVo(it) } } } private fun toExamVo(question: Question): ExamVo { return ExamVo( questionId = question.id.value, question = question.question, optionsA = question.optionsA, optionsB = question.optionsB, optionsC = question.optionsC, optionsD = question.optionsD, rightOption = question.answer ) } /** * 加载题库 * * @param vo * @return */ fun loadExam(vo: SearchExamVo): List { return transaction { Question.find { Questions.associationId eq vo.associationId }.map { toExamVo(question = it) } } } /** * 提交答卷 * */ fun applyAnswer(vo: AddAnswerVo) { transaction { val user = User.findById(vo.token.id) ?: throw UserIdError(vo.token.id) val association = Association.findById(vo.associationId) ?: throw AssociationIdError(vo.associationId) val join = createJoin(association = association, user = user) vo.answers.forEach { val id = it.examVo.questionId ?: throw IllegalArgumentException("问题id不存在") val question = Question.findById(id) ?: throw IllegalArgumentException("不存在[id=${id}]的问题") Answer.new { this.question = question answer = it.answer this.join = join } } } } private fun toJoinVo(join: JoinAssociation): JoinAssociationVo { val hasPaper = Answer.find { Answers.joinId eq join.id.value }.count() > 0 return JoinAssociationVo( id = join.id.value, user = AccountService.toUserInfo(join.user), associationVo = toAssociationVo(join.association), hasPaper = hasPaper, result = join.result, applyTime = join.applyTime.toLong(), auditTime = join.auditTime?.toLong() ) } /** * 显示答卷 * * @param vo * @return */ fun showAnswer(vo: ShowAnswerVo): List { return transaction { val join = JoinAssociation.findById(vo.joinId) ?: throw IllegalArgumentException("找不到[id=${vo.joinId}]申请记录") Answer.find { Answers.joinId eq join.id }.map { ApplyAnswerVo(examVo = toExamVo(it.question), answer = it.answer) } } } /** * 创建申请记录 * * @param association * @param user */ private fun createJoin(association: Association, user: User): JoinAssociation { return JoinAssociation.new { this.association = association this.user = user } } /** * 生成试卷 * * @param vo * @return */ fun createPaper(vo: SearchExamVo): List { return transaction { val association = Association.findById(vo.associationId) ?: throw AssociationIdError(vo.associationId) Question.find { Questions.associationId eq association.id }.let { it -> it.shuffled().map { toExamVo(question = it) } } } } private fun createJoinNotification(user: User, association: Association) { AssociationMember.find { AssociationMembers.associationId eq association.id and (AssociationMembers.isHead eq true) }.firstOrNull()?.apply { Notification.new { this.title = "入团申请通知" this.content = "用户${user.name}申请加入社团" this.receiverId = user.id.value receiverClient = ClientType.Foreground.name } } } /** * 申请入团 * */ fun applyAssociation(vo: SearchExamVo): ApplyAssociationResultVo { return transaction { val user = User.findById(vo.token.id) ?: throw UserIdError(vo.token.id) val association = Association.findById(vo.associationId) ?: throw AssociationIdError(vo.associationId) JoinAssociation.find { JoinAssociations.userId eq vo.token.id and (JoinAssociations.result eq null) } .firstOrNull().let { if (it != null) { return@transaction ApplyAssociationResultVo(associationVo = toAssociationVo(it.association)) } } Question.find { Questions.associationId eq association.id }.let { it -> //题库需要满足最少题目数才能生成试卷 if (it.count() >= QUESTION_MIN_SIZE) { ApplyAssociationResultVo(result = true, hasPaper = true) } //免试入团申请 else { createJoin(association = association, user = user) createJoinNotification(association = association, user = user) ApplyAssociationResultVo(result = true, hasPaper = false) } } } } /** * 入团申请记录 * */ fun joinAssociation(vo: SearchExamVo): List { return transaction { val association = Association.findById(vo.associationId) ?: throw AssociationIdError(vo.associationId) JoinAssociation.find { JoinAssociations.associationId eq association.id } .sortedByDescending { it.applyTime }.map { toJoinVo(join = it) } } } /** * 审核入团申请 * @param vo */ fun auditJoin(vo: AuditJoinVo) { transaction { val join = JoinAssociation.findById(vo.joinId) ?: throw IllegalArgumentException("审核记录不存在") join.result = vo.result join.auditTime = LocalDateTime.now() if (vo.result) { AssociationMember.new { user = join.user association = join.association isHead = false } //入团申请通过,通知社团其他人 AssociationMember.find { AssociationMembers.associationId eq join.association.id }.forEach { Notification.new { this.title = "入团通知" this.content = "欢迎新人${join.user.name}加入社团" this.receiverId = it.user.id.value receiverClient = ClientType.Foreground.name }.apply { log.info("通知前台用户${dataType}处理进度") } } } //把审核结果发送给申请人 Notification.new { this.title = "入团审核结果" this.content = "您申请加入${join.association.name}社团审核${if (vo.result) "通过" else "不通过"}" this.receiverId = join.user.id.value receiverClient = ClientType.Foreground.name } } } /** * 更新题库 * * @param vo */ fun updateExam(vo: AddExamVo) { transaction { val association = Association.findById(vo.associationId) ?: throw IllegalArgumentException("找不到社团信息") vo.deleteIds?.let { //删除社团题目 Question.find { Questions.associationId eq association.id and (Questions.id inList it) }.forEach { log.info("删除[id=${it.id}]题目") it.delete() } } vo.questions.forEach { exam -> exam.questionId.let { if (it == null) { Question.new { question = exam.question optionsA = exam.optionsA optionsB = exam.optionsB optionsC = exam.optionsC optionsD = exam.optionsD this.answer = exam.rightOption this.association = association } } else { log.info("更新[id=${it}]的题目") Question.findById(it)?.apply { question = exam.question optionsA = exam.optionsA optionsB = exam.optionsB optionsC = exam.optionsC optionsD = exam.optionsD this.answer = exam.rightOption } } } } } } /** * 更新社团信息 * * @param vo */ fun update(vo: AssociationVo) { transaction { Association.findById(vo.associationId).let { if (it == null) { throw AssociationIdError(vo.associationId) } else { it.level = vo.level?.name } } } } /** * 注册社团 * * @param vo * @return */ override fun register(vo: AssociationRegVo) { return transaction { //再次申请 val user = User.findById(vo.token.id) ?: throw UserIdError(vo.token.id) if (vo.associationId != null) { log.info("再次提交【${vo.name}】注册资料") (Association.findById(vo.associationId!!) ?: throw AuditIdError(vo.associationId!!)) .set(vo = vo, user = user) } else { val associationMember = AssociationMember.find { AssociationMembers.userId eq user.id }.firstOrNull() val association: Association? = associationMember?.association when { association != null && association.audit.result == true -> throw IllegalArgumentException("您是社团团长不能再创建其他社团") association != null && association.audit.result == null -> throw IllegalArgumentException("您已经提交过${dataType},请耐心等待后台管理员处理") association == null -> { Association.new { set(vo = vo, user = user) faculty = faculty(user.studentId).name } } } } createNotifications(vo = vo, user = user) log.info("未审核社团:${vo.name}创建成功") } } /** * 社团列表加载 * * @param vo * @return */ fun load(vo: SearchAssociationVo): List { return transaction { log.info("社团搜索条件[name=${vo.name},desc=${vo.desc}]") val nextAudit = AuditLeggings.alias("nextAudit") return@transaction Associations.innerJoin(otherTable = AuditLeggings) .innerJoin(nextAudit, { AuditLeggings.nextAudit }, { nextAudit[AuditLeggings.id] }) .slice(listOf(Associations.id)) .select { nextAudit[AuditLeggings.result] eq true and (Associations.name like "%${vo.name}%") and (Associations.desc like "%${vo.desc}%") }.map { val association = Association.findById(it[Associations.id]) ?: throw AssociationIdError(it[Associations.id].value) toAssociationVo(association) } } } /** * 前台读取社团注册资料 * */ override fun getCheckVo(id: EntityID): AssociationCheckVo { return (Association.findById(id) ?: throw AssociationIdError(id.value)).let { AssociationCheckVo( associationVo = toAssociationVo(it), auditCheckVo = toCheckVo(it.audit), fileId = it.logo.id.value ) } } /** * 后台读取社团注册资料 * * @param vo * @return */ override fun audit(vo: OnlyToken): List { return transaction { return@transaction createSortedBy(Association).map { val audit = toAuditLoggingVo(it.audit) ?: throw IllegalArgumentException("转换审核记录出错!!!!") AuditAssociationVo(name = it.name, desc = it.desc, logo = it.logo.filepath, audit = audit) }.apply { log.info("找到${this.size}份${dataType}") } } } fun toAssociationVo(it: Association): AssociationVo { return AssociationVo(associationId = it.id.value, name = it.name, desc = it.desc, logo = it.logo.filepath, faculty = AssociationFaculty.valueOf(it.faculty), level = it.level?.let { AssociationLevel.valueOf(it) }) } /** * * 查询社团详情 * @param vo * @return */ fun load(vo: ShowAssociationVo): AssociationMainVo { return transaction { val associationVo = (Association.findById(vo.id) ?: throw AssociationIdError(vo.id)).let { it -> AssociationVo(associationId = it.id.value, name = it.name, desc = it.desc, logo = it.logo.filepath, faculty = AssociationFaculty.valueOf(it.faculty), level = it.level?.let { AssociationLevel.valueOf(it) }) } val head = Users.innerJoin(AssociationMembers) .slice(Users.columns) .select { AssociationMembers.isHead eq true and (AssociationMembers.associationId eq vo.id) } .firstOrNull()?.let { it -> UserInfoVo( name = it[Users.name], headImg = it[Users.imgId]?.let { ImageFile.findById(it) ?: throw FileIdError(it.value) }?.filepath, desc = it[Users.desc] ) } ?: throw IllegalArgumentException("无法找到社团[${vo.id}]") AssociationMainVo(associationVo = associationVo, head = head) } } /** * 加载社团成员 * * @param vo * @return */ fun load(vo: QueryAssociationMembers): List { return transaction { Users.innerJoin(AssociationMembers).innerJoin(Associations) .slice(Users.columns) .select { vo.name.let { if (it == null || it.isEmpty()) Associations.id eq vo.id else Associations.id eq vo.id and (Users.name like "%${vo.name}%") } }.map { it -> UserInfoVo( name = it[Users.name], headImg = it[Users.imgId]?.let { ImageFile.findById(it) }?.filepath, desc = it[Users.desc] ) } } } } /** * 活动服务 */ object ActivityService : AuditService() { override val dataType: String = "活动申请书" override fun findEntity(auditId: Int): AbstractAudit { return testFind(Activities, Activity, auditId) } private fun checkTendency(userId:Int, activityId:Int, type:TendencyType): SizedIterable { val user=User.findById(userId)?:throw UserIdError(userId) val activity=Activity.findById(activityId)?:throw ActivityIdError(activityId) return Tendency.find { Tendencies.userId eq user.id and (Tendencies.activityId eq activity.id) and (Tendencies.type eq type.ordinal) } } fun checkTendency(vo: CheckTendencyVo):CheckTendencyResVo{ return transaction { val hasLike= checkTendency(userId = vo.token.id,activityId = vo.activityId,type = TendencyType.Like).count()==1L val hasCollect=checkTendency(userId = vo.token.id,activityId = vo.activityId,type = TendencyType.Collect).count()==1L CheckTendencyResVo(hasLike=hasLike,hasCollect=hasCollect) } } fun findTendency(vo:FindTendencyVo):List{ return transaction { val user=User.findById(vo.token.id)?:throw UserIdError(vo.token.id) Tendency.find { Tendencies.userId eq user.id and (Tendencies.type eq vo.type.ordinal) }.map { toActivityVo(activity = it.activity) } } } /** * 活动倾向 * */ fun tendency(vo: TendencyVo) { transaction { checkTendency(userId = vo.token.id,activityId = vo.activityId,type = vo.type).apply { if (count() == 1L) { first().delete() } else { Tendency.new { this.user= User[vo.token.id] this.activity= Activity[vo.activityId] this.type=vo.type.ordinal } } } } } /** * 查看活动信息 * */ fun listAll(): List { return transaction { Activity.all().map { ManagerActVo( association = AssociationService.toAssociationVo(it.association), score = (1..5).random(), activityVo = toActivityVo(it) ) } } } /** * 主页活动图片 * * @return */ fun random(): List { return transaction { PhotoAlbum.all().shuffled().map { MainActivityPhotoVo( url = it.photo.filepath, activityVo = toActivityVo(it.activity), associationVo = AssociationService.toAssociationVo(it.activity.association) ) } } } /** * 上传照片 * * @param data */ fun upload(data: List) { 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 = AccountService.toUserInfo(it.user), 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) ) } } } fun Activity.set(activityVo: ActivityVo, user: User) { activityName = activityVo.activityName activityTime = activityVo.activityTime.toLocalDateTime() activityAddress = activityVo.activityAddress activityDesc = activityVo.activityDesc activitySize = activityVo.activitySize audit = AuditLogging.new { this.user = user } } /** * 申请活动 * */ override fun register(vo: ActivityApplyVo) { transaction { val user = User.findById(vo.token.id) ?: throw UserIdError(vo.token.id) if (vo.activityVo.activityId != null) { //再次提交 log.info("再次提交【${vo.activityVo.activityName}】${dataType}") (Activity.findById(vo.activityVo.activityId!!) ?: throw ActivityIdError(vo.activityVo.activityId!!)) .set(activityVo = vo.activityVo, user = user) } else { //首次提交 //申请活动的限制 val activityMember = AssociationMember.find { AssociationMembers.userId eq user.id }.firstOrNull() when { activityMember == null -> throw IllegalArgumentException("非社团成员无法申请活动") !activityMember.isHead -> throw IllegalArgumentException("不是社团团长无法申请活动") else -> { Activity.new { set(vo.activityVo, user = user) association = Association.findById(vo.associationId) ?: throw AssociationIdError(vo.associationId) } } } } createNotifications(vo = vo, user = user) } } private fun toActivityVo(activity: Activity): ActivityVo { return ActivityVo( activityName = activity.activityName, activityTime = activity.activityTime.toLong(), activityAddress = activity.activityAddress, activityDesc = activity.activityDesc, activitySize = activity.activitySize, activityId = activity.id.value ) } override fun audit(vo: OnlyToken): List { return transaction { return@transaction createSortedBy(Activity).map { val audit = toAuditLoggingVo(it.audit) ?: throw IllegalArgumentException("转换审核记录出错!!!!") AuditActVo(activityVo = toActivityVo(activity = it), audit = audit) }.apply { log.info("找到${this.size}份${dataType}") } } } override fun getCheckVo(id: EntityID): ActivityCheckVo { return (Activity.findById(id) ?: throw ActivityIdError(id.value)).let { ActivityCheckVo( activityVo = toActivityVo(activity = it), auditCheckVo = toCheckVo(it.audit) ) } } } /** * 重命名服务 */ object RenameService : AuditService() { override val dataType: String = "换名申请表" fun Rename.set(vo: RenameVo, user: User) { newName = vo.newName cause = vo.cause audit = AuditLogging.new { this.user = user } } //TODO 类型自动转换 override fun afterAudit(entity: AbstractAudit) { if (entity is Rename) { entity.association.name = entity.newName } } override fun register(vo: RenameApplyVo) { transaction { vo.rename.renameId.also { val user = User.findById(vo.token.id) ?: throw UserIdError(vo.token.id) if (it != null) { //再次提交 (Rename.findById(it) ?: throw IllegalArgumentException("无法根据[id=${it}]找到${dataType}")).set( vo = vo.rename, user = user ) } else { //重新提交 val activityMember = AssociationMember.find { AssociationMembers.userId eq user.id }.firstOrNull() when { activityMember == null -> throw IllegalArgumentException("非社团成员无法申请社团换名") !activityMember.isHead -> throw IllegalArgumentException("非社团团长无法申请社团换名") else -> { Rename.new { set(vo = vo.rename, user = user) association = Association.findById(vo.associationId) ?: throw AssociationIdError(vo.associationId) } } } } createNotifications(vo = vo, user = user) } } } override fun findEntity(auditId: Int): AbstractAudit { return testFind(Renames, Rename, auditId) } private fun toRenameVo(rename: Rename): RenameVo { return RenameVo(renameId = rename.id.value, newName = rename.newName, cause = rename.cause) } override fun getCheckVo(id: EntityID): RenameCheckVo { return (Rename.findById(id) ?: throw IllegalArgumentException("找不到[id=${id}]${dataType}")).let { RenameCheckVo(renameVo = toRenameVo(it), auditCheckVo = toCheckVo(it.audit)) } } override fun audit(vo: OnlyToken): List { return transaction { return@transaction createSortedBy(Rename).map { val audit = toAuditLoggingVo(it.audit) ?: throw IllegalArgumentException("转换审核记录出错!!!!") AuditRenameVo( renameVo = toRenameVo(rename = it), audit = audit, associationVo = AssociationService.toAssociationVo(it.association) ) }.apply { log.info("找到${this.size}份${ActivityService.dataType}") } } } } /** * 通知服务 */ object NotificationService : AbstractService() { /** * 拉取最新通知 * * @param vo * @return */ fun pull(vo: NotificationDto): List { return transaction { log.info("通知查询条件[receiverId=${vo.receiverId},receiverClient=${vo.receiverClient.name},pull=false]") 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.toLong() ) } } } /** * 未读通知计数 * * @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}条") } } } /** * * @param vo * @return */ fun list(vo: NotificationDto): List { return transaction { return@transaction Notification.find { (Notifications.receiverId eq vo.receiverId) and (Notifications.receiverClient eq vo.receiverClient.name) }.sortedByDescending { it.createTime }.map { NotificationVo( title = it.title, id = it.id.value, content = it.content, createTime = it.createTime.toLong() ) } } } } /** * 后台服务 */ object BackgroundService : AbstractService() { override fun init(environment: ApplicationEnvironment) { super.init(environment) initManager() } fun toManagerVo(manager: Manager): ManagerInfoVo { return ManagerInfoVo( duty = manager.duty.let { Duty.valueOf(it) }, name = manager.name, headImg = manager.headImg?.filepath, desc = manager.desc ) } /** * 加载部门部长,和总人数 * */ fun load(): ManagerDutySumVo { return transaction { val secretariat = Manager.find { Managers.duty eq Duty.SecretaryOfTheMinister.name }.first() val secretariatCount = Manager.find { Managers.duty eq Duty.SecretaryDepartmentOfficer.name }.count() val propaganda = Manager.find { Managers.duty eq Duty.PropagandaDepartment.name }.first() val propagandaCount = Manager.find { Managers.duty eq Duty.PublicityDepartmentOfficer.name }.count() val publicRelationsDepartment = Manager.find { Managers.duty eq Duty.LiaisonMinister.name }.first() val publicRelationsDepartmentCount = Manager.find { Managers.duty eq Duty.LiaisonOfficer.name }.count() ManagerDutySumVo( secretariat = ManagerDutyVo(manager = toManagerVo(secretariat), people = secretariatCount.toInt()), propaganda = ManagerDutyVo(manager = toManagerVo(propaganda), people = propagandaCount.toInt()), publicRelationsDepartment = ManagerDutyVo( manager = toManagerVo(publicRelationsDepartment), people = publicRelationsDepartmentCount.toInt() ) ) } } /** * 加载部门详细信息 * * @return */ fun loadDetail(): AllOfficerVo { return transaction { val secretariat = Manager.find { Managers.duty eq Duty.SecretaryDepartmentOfficer.name }.map { toManagerVo(it) } val propaganda = Manager.find { Managers.duty eq Duty.PublicityDepartmentOfficer.name }.map { toManagerVo(it) } val publicRelationsDepartment = Manager.find { Managers.duty eq Duty.LiaisonOfficer.name }.map { toManagerVo(it) } AllOfficerVo( secretariat = secretariat, propaganda = propaganda, publicRelationsDepartment = publicRelationsDepartment ) } } private fun createManager(duty: Duty, num: Int = 1): MutableList { val managerList = mutableListOf() repeat(num) { val originPassword = randomNum() Manager.new { account = randomNum() password = originPassword.md5() this.duty = duty.name this.name = duty.desc }.apply { managerList.add(InitManagerDto(account = account, originPassword = originPassword, duty = duty)) } } return managerList } //初始化管理员 private fun initManager() { transaction { val resourcePath = this::class.java.classLoader.getResource("")?.path ?: throw IllegalArgumentException("初始化资源目录失败") val file = File(resourcePath, "管理员账号.txt") try { if (!file.exists()) { Manager.count().let { it -> if (it.toInt() == 0) { val allManager = mutableListOf() allManager.addAll(createManager(Duty.Teacher, 1)) allManager.addAll(createManager(Duty.PamphaBhusal, 1)) allManager.addAll(createManager(Duty.SecretaryOfTheMinister, 1)) allManager.addAll(createManager(Duty.PropagandaDepartment, 1)) allManager.addAll(createManager(Duty.LiaisonMinister, 1)) arrayOf( Duty.SecretaryDepartmentOfficer, Duty.PublicityDepartmentOfficer, Duty.LiaisonOfficer ).forEach { allManager.addAll(createManager(it, 3)) } allManager.forEach { file.appendText("${it.account}------${it.originPassword}------${it.duty.desc}\n") } log.info("共生成${allManager.size}个管理员账号") } else { log.info("不需要生成管理员") } } } } catch (e: Exception) { log.error(e) exitProcess(0) } } } }