From 1bdf4a1ae2ce84244c952992390ebc30ee874285 Mon Sep 17 00:00:00 2001 From: pan <1029559041@qq.com> Date: Tue, 1 Jun 2021 00:42:58 +0800 Subject: [PATCH] =?UTF-8?q?=E7=A4=BE=E5=9B=A2=E6=8E=A5=E5=8F=A3=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=20=E5=AE=8C=E5=96=84=E6=B3=A8=E5=86=8C=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=20=E5=A2=9E=E5=8A=A0=E7=A4=BE=E5=9B=A2=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2=EF=BC=8C=E7=A4=BE=E5=9B=A2=E6=B3=A8=E5=86=8C=E8=B5=84?= =?UTF-8?q?=E6=96=99=E5=8F=97=E7=90=86=E3=80=81=E5=AE=A1=E6=A0=B8=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=20=E6=9B=B4=E6=96=B0=E7=94=A8=E6=88=B7=E8=A1=A8?= =?UTF-8?q?=E3=80=81=E7=AE=A1=E7=90=86=E5=91=98=E8=A1=A8=E3=80=81=E5=AE=A1?= =?UTF-8?q?=E6=A0=B8=E8=A1=A8=E3=80=81=E9=80=9A=E7=9F=A5=E8=A1=A8=E3=80=81?= =?UTF-8?q?=E7=A4=BE=E5=9B=A2=E8=A1=A8=20=E5=A2=9E=E5=8A=A0=E7=A4=BE?= =?UTF-8?q?=E5=9B=A2=E6=88=90=E5=91=98=E8=A1=A8=20=E5=A2=9E=E5=8A=A0Error?= =?UTF-8?q?=E8=87=AA=E5=AE=9A=E4=B9=89=E5=BC=82=E5=B8=B8=E7=B1=BB=20?= =?UTF-8?q?=E6=9C=8D=E5=8A=A1=E7=B1=BB=E6=9B=B4=E6=96=B0token=E6=A0=A1?= =?UTF-8?q?=E9=AA=8C=E9=80=BB=E8=BE=91=EF=BC=8C=E7=99=BB=E5=BD=95=E8=8E=B7?= =?UTF-8?q?=E5=8F=96=E7=94=A8=E6=88=B7=E4=BF=A1=E6=81=AF=EF=BC=8C=E8=AF=A6?= =?UTF-8?q?=E7=9C=8B=E7=B1=BB=20=E5=B7=A5=E5=85=B7=E7=B1=BB=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0LocalDateTime->Long=E8=BD=AC=E6=8D=A2=20=E5=AE=9E?= =?UTF-8?q?=E4=BD=93=E7=B1=BB=E6=9B=B4=E6=96=B0=E7=94=A8=E6=88=B7=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E5=91=98=E5=AE=9E=E4=BD=93=EF=BC=8C=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E7=A4=BE=E5=9B=A2=E7=9B=B8=E5=85=B3=E5=AE=9E=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- resources/application.conf | 2 +- src/Application.kt | 1 + src/Controller.kt | 145 ++++++- src/Dao.kt | 147 ++++--- src/Error.kt | 19 + src/MySQL.kt | 2 +- src/Service.kt | 830 +++++++++++++++++++++++++------------ src/Util.kt | 29 ++ src/Vo.kt | 243 ++++++++++- test/ApplicationTest.kt | 31 +- 10 files changed, 1100 insertions(+), 349 deletions(-) create mode 100644 src/Error.kt diff --git a/resources/application.conf b/resources/application.conf index e3afe83..548a945 100644 --- a/resources/application.conf +++ b/resources/application.conf @@ -11,7 +11,7 @@ ktor { leaveMessage { maxSize = 20 } - filePath = upload + filePath = static/image watch = [ classes ] } application { diff --git a/src/Application.kt b/src/Application.kt index 546e5d8..8d115e5 100644 --- a/src/Application.kt +++ b/src/Application.kt @@ -30,6 +30,7 @@ fun Application.module(testing: Boolean = false) { fun Application.Controller(testing: Boolean = false){ + StaticController() AccountController() TestController() MainController() diff --git a/src/Controller.kt b/src/Controller.kt index 0656f60..49fe27e 100644 --- a/src/Controller.kt +++ b/src/Controller.kt @@ -5,6 +5,7 @@ import io.ktor.http.content.* import io.ktor.request.* import io.ktor.response.* import io.ktor.routing.* +import io.ktor.util.* suspend inline fun withToken(call: ApplicationCall, callback: (vo: T) -> Unit) { val levelVo = call.receive() @@ -15,6 +16,25 @@ suspend inline fun withToken(call: ApplicationCall, c } } +fun Application.StaticController(){ + routing { + val uploadDir=environment.config.property("ktor.deployment.filePath").getString() + static(uploadDir) { + resources(uploadDir) + } + } +} + +suspend fun loginRes(call: ApplicationCall, body:OwnInfoVo){ + try { + call.respond(ApiResponse(message = "登陆成功", body = body)) + } catch (e: IllegalArgumentException) { + call.respond(ApiResponse(message = "${e.message}",body=null)) + }catch (e:Exception){ + call.respond(ApiResponse(message = "发生未知错误,请联系管理员",body=null)) + } +} + fun Application.AccountController() { routing { @@ -40,7 +60,7 @@ fun Application.AccountController() { * 注册账号 */ post { - val userVo = call.receive() + val userVo = call.receive() val userResDto = AccountService.register(userVo) if (userResDto != null) { call.respond(ApiResponse(message = "注册成功", body = userResDto)) @@ -55,26 +75,36 @@ fun Application.AccountController() { route(path = ClientType.Foreground.name.toLowerCase()){ post { val userLoginVo = call.receive() - val token = AccountService.login(userLoginVo, call.request.host()) - call.respond(ApiResponse(message = if (token != null) "登陆成功" else "账号或密码错误!!!", body = token)) + loginRes(call=call,body = AccountService.login(userLoginVo, call.request.host())) } post("/token"){ val tokenVo = call.receive() - val isValid = AccountService.validUserToken(tokenVo) - call.respond(ApiResponse(message = if (isValid) "令牌合法" else "令牌不合法", body = isValid)) + try { + val userinfo = AccountService.getUserVo(tokenVo) + log.info("$userinfo") + call.respond(ApiResponse(message = "令牌合法", body = userinfo)) + } catch (e: Exception) { + log.error(e) + call.respond(ApiResponse(message = "令牌不合法", body = null)) + } } } route(path = ClientType.Background.name.toLowerCase()){ post{ val managerLoginVo = call.receive() - val token = AccountService.login(managerLoginVo, call.request.host()) - call.respond(ApiResponse(message = if (token != null) "登陆成功" else "账号或密码错误!!!", body = token)) + loginRes(call=call,body = AccountService.login(managerLoginVo, call.request.host())) } post("/token"){ val tokenVo = call.receive() - val isValid = AccountService.validManagerToken(tokenVo) - call.respond(ApiResponse(message = if (isValid) "令牌合法" else "令牌不合法", body = isValid)) + try { + val managerInfo = AccountService.getManagerVo(tokenVo) + log.info("$managerInfo") + call.respond(ApiResponse(message = "令牌合法", body = managerInfo)) + } catch (e: Exception) { + log.error(e) + call.respond(ApiResponse(message = "令牌不合法", body = null)) + } } } } @@ -82,9 +112,9 @@ fun Application.AccountController() { post(path = "/logout") { - environment.log.info("退出登录") + log.info("退出登录") val onlyToken = call.receive() - environment.log.info("$onlyToken") + log.info("$onlyToken") val flag = AccountService.logout(onlyToken) call.respond(ApiResponse(message = if (flag) "退出成功" else "退出失败", body = flag)) } @@ -131,9 +161,12 @@ fun Application.AssociationController() { routing { route("$ApiPathPrefix/association") { post("/uploadLogo") { + + log.info("开始上传logo") val multipartData = call.receiveMultipart() + multipartData.readAllParts().apply { - environment.log.info("part size=$size") + log.info("part size=$size") if (size == 4) { val result=FileService.storeFile(this) call.respond(ApiResponse(message = if(result.isNullOrEmpty()) "文件上传失败" else @@ -143,20 +176,90 @@ fun Application.AssociationController() { throw IllegalArgumentException("参数异常") } } + log.info("----end-----") } post("/register"){ - withToken(call = call){ + log.info("开始提交注册资料") + withToken(call = call){ + try { + AssociationService.register(regVo=it) + call.respond(ApiResponse(message = "社团注册资料已提交,请等待受理",body = true)) + } catch (e: IllegalArgumentException) { + call.respond(ApiResponse(message = "社团资料提交失败,请联系系统管理员",body = false)) + } catch (e:Exception){ + call.respond(ApiResponse(message = e.message?:"发生未知错误,请联系系统管理员",body = false)) + } + } + log.info("----end-----") + } + + post("/list"){ + log.info("开始查询社团") + withToken(call = call){ + try { + call.respond(ApiResponse(message = "社团列表检索完成",body=AssociationService.load(vo=it))) + } catch (e: Exception) { + log.error(e) + call.respond(ApiResponse(message = "社团列表检索失败",body=null)) + } + } + log.info("----end-----") + } + + post("/accept"){ + log.info("开始受理") + withToken(call = call){ + try { + AssociationService.accept(vo=it) + call.respond(ApiResponse(message = "社团注册资料受理成功",body=true)) + }catch (e:IllegalArgumentException){ + call.respond(ApiResponse(message = "社团资料受理失败,请联系系统管理员",body = false)) + }catch (e:Exception){ + call.respond(Simple.error("发生未知错误,请联系管理员")) + } + } + log.info("----end-----") + } + + post("/audit"){ + log.info("审核列表") + withToken(call = call){ + try { + call.respond(ApiResponse(message = "社团注册资料获取成功",body=AssociationService.loadAudit(it))) + }catch (e:IllegalArgumentException){ + call.respond(Simple.error("社团注册资料获取失败")) + }catch (e:Exception){ + call.respond(Simple.error("发生未知错误,请联系管理员")) + } + } + log.info("----end-----") + } + + post("/check"){ + log.info("审核社团注册资料") + withToken(call=call){ + try { + AssociationService.check(it) + call.respond(ApiResponse(message = "审核结果已保存",body = true)) + } catch (e: Exception) { + log.error(e) + call.respond(ApiResponse(message = "审核结果保存失败",body = false)) + } + } + log.info("----end-----") + } + + post("/read"){ + log.info("查询用户社团") + withToken(call = call){ try { - val flag=AssociationService.register(vo=it) - call.respond(ApiResponse(message = if(flag) "社团注册资料已提交,请等待受理" else "社团注册资料提交失败",body=flag)) - } catch (e: UserIdError) { - call.respond(ApiResponse(message = "社团资料提交失败,请联系管理员",body = false)) - } catch (e:FileIdError){ - call.respond(ApiResponse(message = "社团资料提交失败,请联系管理员",body = false)) - }catch (e:HasAssociationError){ - call.respond(ApiResponse(message = e.message?:"发生未知错误,请联系管理员",body = false)) + call.respond(ApiResponse(message = "用户社团查询成功",body=AssociationService.read(vo=it))) + } catch (e: Exception) { + log.error(e) + call.respond(Simple.error("用户社团查询失败")) } } + log.info("----end-----") } } } diff --git a/src/Dao.kt b/src/Dao.kt index 5563b60..9a9cd94 100644 --- a/src/Dao.kt +++ b/src/Dao.kt @@ -10,17 +10,28 @@ import org.jetbrains.exposed.sql.`java-time`.CurrentTimestamp import org.jetbrains.exposed.sql.`java-time`.datetime import java.time.LocalDateTime - -@TableComment("用户") -object Users: IntIdTable(){ - @TableComment("学号") - val studentId:Column = varchar(name="student_id",length = 8).uniqueIndex() +open class Person:IntIdTable(){ @TableComment("姓名") val name:Column = varchar(name="name",length = 10) @TableComment("密码") val password:Column = varchar(name="password",length = 32) + + @TableComment("个人简介") + val desc:Column = varchar(name="desc",length = 20).default("") + + @TableComment("头像") + val imgId:Column?> = reference("img_id", ImageFiles).nullable() +} + +@TableComment("用户") +object Users: Person(){ + @TableComment("学号") + val studentId:Column = varchar(name="student_id",length = 8).uniqueIndex() + + @TableComment("社团成员id") + val associationMemberId:Column?> = reference("association_member_id", AssociationMembers).nullable() } class User(id:EntityID):IntEntity(id){ @@ -28,8 +39,33 @@ class User(id:EntityID):IntEntity(id){ var studentId by Users.studentId var name by Users.name var password by Users.password + var desc by Users.desc + val headImg by ImageFile optionalReferencedOn Users.imgId + var associationMember by AssociationMember optionalReferencedOn Users.associationMemberId } + +@TableComment("后台管理员") +object Managers:Person(){ + @TableComment("帐号") + val account:Column = varchar(name="account",length=10) + + @TableComment("职务") + val duty:Column = varchar(name="duty",length = 32) +} + +class Manager(id:EntityID):IntEntity(id){ + companion object:IntEntityClass(Managers) + var account by Managers.account + var password by Managers.password + var duty by Managers.duty + var desc by Managers.desc + var name by Managers.name + val headImg by ImageFile optionalReferencedOn Managers.imgId +} + + + open class BaseTokens:IntIdTable(){ @TableComment("令牌") val token:Column = varchar(name="token",length = 32) @@ -118,22 +154,25 @@ class ImageFile(id:EntityID):IntEntity(id){ var createTime by ImageFiles.createTime } -@TableComment("社团") +@TableComment("社团信息") object Associations:IntIdTable(){ @TableComment("社团名称") - val name:Column =varchar(name = "name",length = 10) + val name:Column = varchar(name = "name", length = 10) @TableComment("社团介绍") - val desc:Column =varchar(name = "desc",length = 30) + val desc:Column = varchar(name = "desc", length = 30) @TableComment("社团logo") - val logo:Column> = reference("logo",ImageFiles) + val logo:Column> = reference("logo", ImageFiles) + + @TableComment("审核信息") + val logId:Column> = reference("log_id",AuditLeggings) - @TableComment("社团审核状态") - val status:Column = bool(name="status").default(false) + @TableComment("社团级别") + val level:Column = varchar(name="level",length = 1).nullable() - @TableComment("创建人") - val user:Column> = reference("user_id",Users) + @TableComment("所属院系") + val faculty:Column = varchar(name="faculty",length = 20) } class Association(id:EntityID):IntEntity(id){ @@ -141,57 +180,64 @@ class Association(id:EntityID):IntEntity(id){ var name by Associations.name var desc by Associations.desc var logo by ImageFile referencedOn Associations.logo - var status by Associations.status - var user by User referencedOn Associations.user + var log by AuditLogging referencedOn Associations.logId + val level by Associations.level + var faculty by Associations.faculty } -@TableComment("后台管理员") -object Managers:IntIdTable(){ - @TableComment("帐号") - val account:Column = varchar(name="account",length=10) - - @TableComment("密码") - val password:Column = varchar(name="password",length = 32) - - @TableComment("职务") - val duty:Column = varchar(name="duty",length = 32) +@TableComment("社团成员信息") +object AssociationMembers:IntIdTable(){ + @TableComment("社团Id") + val associationId:Column> = reference("association_id",Associations) - @TableComment("等级") - val level:Column = integer("level") + @TableComment("是否团长") + val isHead:Column = bool("is_head") } -class Manager(id:EntityID):IntEntity(id){ - companion object:IntEntityClass(Managers) - var account by Managers.account - var password by Managers.password - var duty by Managers.duty - var level by Managers.level +class AssociationMember(id:EntityID):IntEntity(id) { + companion object : IntEntityClass(AssociationMembers) + var association by Association referencedOn AssociationMembers.associationId + var isHead by AssociationMembers.isHead } + @TableComment("审核记录") -object CheckForms:IntIdTable(){ - @TableComment("审核类型") - val type:Column = varchar(name="type",length = 10) +object AuditLeggings:IntIdTable(){ + @TableComment("申请人") + val userId: Column> = reference("user_id",Users) - @TableComment("审核人") - val managerId:Column> = reference("manager_id",Managers) + @TableComment("申请时间") + val applyTime:Column = datetime("apply_time").defaultExpression(CurrentDateTime()) + + @TableComment("负责人") + val managerId: Column?> = reference("manager_id",Managers).nullable() + + @TableComment("受理时间") + val acceptTime:Column = datetime("accept_time").nullable() @TableComment("审核理由") - val cause:Column = varchar("cause",length = 30) + val cause:Column = varchar("cause",length = 30).nullable() - @TableComment("审核对象") - val target:Column = integer("target") + @TableComment("审核结果") + val result:Column = bool("result").nullable() @TableComment("审核时间") - val createTime:Column = datetime("create_time").defaultExpression(CurrentDateTime()) + val auditTime:Column = datetime("audit_time").nullable() + + @TableComment("复审记录") + val nextAudit:Column?> = reference("next_audit",AuditLeggings).nullable() } -class CheckForm(id:EntityID):IntEntity(id){ - companion object:IntEntityClass(CheckForms) - var type by CheckForms.type - val manager by Manager referrersOn CheckForms.managerId - var cause by CheckForms.cause - var target by CheckForms.target +class AuditLogging(id:EntityID):IntEntity(id){ + companion object:IntEntityClass(AuditLeggings) + var user by User referencedOn AuditLeggings.userId + var applyTime by AuditLeggings.applyTime + var manager by Manager optionalReferencedOn AuditLeggings.managerId + var acceptTime by AuditLeggings.acceptTime + var cause by AuditLeggings.cause + var result by AuditLeggings.result + var auditTime by AuditLeggings.auditTime + var nextAudit by AuditLogging optionalReferencedOn AuditLeggings.nextAudit } @TableComment("通知记录") @@ -200,7 +246,7 @@ object Notifications:IntIdTable(){ val title:Column = varchar(name="title",length = 10) @TableComment("通知内容") - val content:Column = varchar(name="content",length = 30) + val content:Column = varchar(name="content",length = 256) @TableComment("接收者") val receiverId:Column = integer(name="receiver_id") @@ -227,4 +273,5 @@ class Notification(id:EntityID):IntEntity(id){ 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/Error.kt b/src/Error.kt new file mode 100644 index 0000000..8762344 --- /dev/null +++ b/src/Error.kt @@ -0,0 +1,19 @@ +package com.gyf.csams + + + +class UserIdError(id:Int):IllegalArgumentException("用户Id[${id}]不存在") + +class StudentIdError(id:String):IllegalArgumentException("学号[${id}]不存在") + +class FileIdError(id:Int):IllegalArgumentException("文件Id[${id}]不存在") + +class RegIdError(id:Int):IllegalArgumentException("注册资料Id[${id}]不存在") + +class ManagerIdError(id:Int):IllegalArgumentException("管理员Id[${id}]不存在") + +class AccountError(id:String):IllegalArgumentException("账号[${id}]不存在") + +class PasswordError(id:String,password:String):IllegalArgumentException("[id=${id}]密码[${password}]错误") + +class AssociationIdError(id:Int):IllegalArgumentException("社团Id[${id}]不存在") diff --git a/src/MySQL.kt b/src/MySQL.kt index 377b3e2..119b99b 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,Notifications,ManagerTokens) + val tableList= arrayOf(Users,UserTokens,Managers,ManagerTokens,AuditLeggings,LeaveMessages,ImageFiles,Associations,Associations,Notifications) SchemaUtils.createMissingTablesAndColumns(*tableList) updateComment(*tableList) diff --git a/src/Service.kt b/src/Service.kt index b895aaa..749fead 100644 --- a/src/Service.kt +++ b/src/Service.kt @@ -2,173 +2,249 @@ 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 io.ktor.util.* +import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.transactions.transaction import org.slf4j.Logger import java.io.File -import java.time.ZoneOffset +import java.time.LocalDateTime import kotlin.properties.Delegates +import kotlin.system.exitProcess -interface BaseService{ - fun init(environment:ApplicationEnvironment) +interface BaseService { + fun init(environment: ApplicationEnvironment) } -abstract class AbstractService:BaseService{ - protected lateinit var log:Logger +abstract class AbstractService : BaseService { + protected lateinit var log: Logger override fun init(environment: ApplicationEnvironment) { - this.log=environment.log + this.log = environment.log } } /** * 账号服务 */ -object AccountService:AbstractService() { - /** - * 检查学号是否已注册,true=已注册 - */ - fun registered(selectId: String): Boolean { - return transaction { - return@transaction !User.find { Users.studentId eq selectId }.empty() - } +object AccountService : AbstractService() { + /** + * 检查学号是否已注册,true=已注册 + */ + fun registered(selectId: String): Boolean { + return transaction { + return@transaction !User.find { Users.studentId eq selectId }.empty() } + } - /** - * 注册 - */ - fun register(userVo: UserVo): UserResDto? { - try { - return transaction { - val originPassword = randomNum(8) - User.new { - studentId=userVo.studentId - name=userVo.name - password=originPassword.md5() - } - return@transaction UserResDto(password=originPassword) + /** + * 注册 + */ + fun register(userVo: UserRegVo): UserResDto? { + try { + return transaction { + val originPassword = randomNum(8) + User.new { + studentId = userVo.studentId + name = userVo.name + password = originPassword.md5() } - } catch (e: Exception) { - log.error("注册失败,发生异常:$e") - return null + return@transaction UserResDto(password = originPassword) } + } catch (e: Exception) { + log.error("注册失败,发生异常:$e") + return null } + } - /** - * 前台登录 - * - */ - fun login(userLoginVo: UserLoginVo,_ip:String):Token?{ - return transaction { - val matchUser=User.find { Users.studentId eq userLoginVo.studentId }.firstOrNull() - when { - matchUser==null -> { - log.warn("学号:${userLoginVo.studentId}不存在") - return@transaction null - } - userLoginVo.password.md5() != matchUser.password -> { - log.warn("密码:${userLoginVo.password}错误") - return@transaction null - } - 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() - return@transaction Token(id = matchUser.id.value,token = token.token, - createTime = token.createTime.toEpochSecond( - ZoneOffset.of("+8"))) + /** + * 前台登录 + * + */ + 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() + + return@transaction UserVo( + studentId = matchUser.studentId, token = + Token( + id = matchUser.id.value, token = token.token, + createTime = token.createTime.format() + ), name = matchUser.name, + headImg = matchUser.headImg?.filepath, + desc = matchUser.desc, + associationMemberVo = matchUser.associationMember?.let { + AssociationMemberVo(association = toAssociationVo(it.association),isHead = it.isHead) } + ) } } } + } - fun login(managerLoginVo: ManagerLoginVo, _ip:String):Token?{ - return transaction { - val matchUser=Manager.find { Managers.account eq managerLoginVo.account }.firstOrNull() - when { - matchUser==null -> { - log.warn("学号:${managerLoginVo.account}不存在") - return@transaction null - } - managerLoginVo.password.md5() != matchUser.password -> { - log.warn("密码:${managerLoginVo.password}错误") - return@transaction null - } - else -> { - val token=ManagerToken.new{ - manager=matchUser - ip=_ip - device=managerLoginVo.device - token=listOf(matchUser.id,ip,device).joinToString(separator = ('a' .. 'z').random().toString()).md5() - } - token.flush() - return@transaction Token(id = matchUser.id.value,token = token.token, - createTime = token.createTime.toEpochSecond( - ZoneOffset.of("+8"))) + private fun toAssociationVo(vo:Association):AssociationVo{ + return AssociationVo(id=vo.id.value,name = vo.name,desc = vo.desc,logo = vo.logo.filepath, + faculty = AssociationFaculty.valueOf(vo.faculty),level = vo.level?.let { AssociationLevel.valueOf(it) }) + } + + 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.format() + ), + desc = matchManager.desc, + duty = Duty.valueOf(matchManager.duty), + headImg = matchManager.headImg?.filepath, name = matchManager.name + ) + } + + private fun tokenRes(token:UserToken,matchUser:User):UserVo{ + return UserVo( + studentId = matchUser.studentId, token = + Token( + id = matchUser.id.value, token = token.token, + createTime = token.createTime.format() + ), name = matchUser.name, + headImg = matchUser.headImg?.filepath, desc = matchUser.desc, + associationMemberVo = matchUser.associationMember?.let { + AssociationMemberVo(association = toAssociationVo(it.association),isHead = it.isHead) } + ) + } + + + 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 validManagerToken(token:Token):Boolean{ - return transaction { - !ManagerToken.find { - ManagerTokens.managerId eq token.id - ManagerTokens.token eq token.token - }.empty() + + 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 validUserToken(token: Token):Boolean{ - return transaction { - !UserToken.find { - UserTokens.userId eq token.id - UserTokens.token eq token.token - }.empty() + 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校验失败") } } + } - fun validToken(vo:T):Boolean{ - return if(vo.clientType==ClientType.Foreground){ - validUserToken(vo.token) - }else{ - validManagerToken(vo.token) - } + fun validManagerToken(token:Token):Boolean{ + return transaction { + !ManagerToken.find { + ManagerTokens.managerId eq token.id + ManagerTokens.token eq token.token + }.empty() } + } - 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 validUserToken(token: Token):Boolean{ + return transaction { + !UserToken.find { + UserTokens.userId eq token.id + UserTokens.token eq token.token + }.empty() } + } - fun test(){ - log.info("开始测试") - transaction { - log.info("查询到个${User.count()}用户") - } - log.info("结束测试") + fun validToken(vo: T): Boolean { + return if (vo.clientType == ClientType.Foreground) { + validUserToken(vo.token) + } else { + validManagerToken(vo.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(){ +object MainService : AbstractService() { private var maxSize by Delegates.notNull() - override fun init(environment:ApplicationEnvironment){ + override fun init(environment: ApplicationEnvironment) { super.init(environment) - this.maxSize=environment.config.property("ktor.deployment.leaveMessage.maxSize").getString().toInt() + this.maxSize = environment.config.property("ktor.deployment.leaveMessage.maxSize").getString().toInt() } @@ -178,39 +254,39 @@ object MainService:AbstractService(){ * @param leaveMessageVo * @return */ - fun createMessage(leaveMessageVo: LeaveMessageVo):Boolean{ - return if(leaveMessageVo.message.isNotEmpty()){ + fun createMessage(leaveMessageVo: LeaveMessageVo): Boolean { + return if (leaveMessageVo.message.isNotEmpty()) { return transaction { - val count=LeaveMessage.count().toInt() + val count = LeaveMessage.count().toInt() log.info("系统留言数:$count,限制数:$maxSize") - if(count>= maxSize){ - LeaveMessage.all().sortedBy { it.createTime }.subList(0, count-maxSize).forEach { + 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") + user = User.findById(leaveMessageVo.token.id) ?: throw IllegalArgumentException("非法id") message = leaveMessageVo.message } log.info("留言保存成功") return@transaction true } - }else{ + } else { log.info("留言不能为空") false } } - fun getAllLeaveMessage():List{ + fun getAllLeaveMessage(): List { return transaction { log.info("获取所有留言") return@transaction LeaveMessage.all().toList().map { LeaveMessageDto( message = "${it.user.name}说:${it.message}", - user = UserVo(studentId = it.user.studentId, name = it.user.name) + user = UserInfoVo(name = it.user.name, headImg = it.user.headImg?.filepath, desc = it.user.desc) ) } } @@ -220,30 +296,19 @@ object MainService:AbstractService(){ /** * 文件管理服务 */ -object FileService:AbstractService(){ - private lateinit var uploadDir:String - private lateinit var filePath:String +object FileService : AbstractService() { + private lateinit var uploadDir: String + private lateinit var filePath: String - override fun init(environment:ApplicationEnvironment){ + override fun init(environment: ApplicationEnvironment) { super.init(environment) - this.uploadDir= environment.config.property("ktor.deployment.filePath").getString() - val resourcePath =this::class.java.classLoader.getResource("")?.path ?: throw IllegalArgumentException("初始化资源目录失败") - File(resourcePath, uploadDir).apply { - when{ - !exists()&&mkdir()-> { - log.info("图片上传路径[${absolutePath}]初始化成功") - this@FileService.filePath=absolutePath - } - exists()->{ - log.info("图片上传路径[${absolutePath}]已存在") - this@FileService.filePath=absolutePath - } - else->throw IllegalArgumentException("图片上传路径[${absolutePath}]初始化失败") - } - } + this.uploadDir = environment.config.property("ktor.deployment.filePath").getString() + filePath = + this::class.java.classLoader.getResource(uploadDir)?.path ?: throw IllegalArgumentException("初始化资源目录失败") + log.info("上传路径[${filePath}]") } - private fun save(id: String, path:String, file: File):Int{ + private fun save(id: String, path: String, file: File): Int { return transaction { return@transaction ImageFile.new { userId = id @@ -254,122 +319,334 @@ object FileService:AbstractService(){ } - private inline fun getPartData(map:Map, key:String):T{ - if(map.containsKey(key)){ - val obj= map[key] - if(obj is T){ + private inline fun getPartData(map: Map, key: String): T { + if (map.containsKey(key)) { + val obj = map[key] + if (obj is T) { return obj - }else{ + } else { throw IllegalArgumentException("类型错误") } } throw IllegalArgumentException("找不到key:${key}") } - fun storeFile(data:List):List?{ - val map=data.associateBy { + 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 tokenVo=Token(token=token,id=userId.toInt(),createTime=createTime.toLong()) - if(AccountService.validUserToken(tokenVo)){ + val userId = getPartData(map, "id").value + val token = getPartData(map, "token").value + val createTime = getPartData(map, "createTime").value + 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 fileIds = mutableListOf() + map.forEach { + val value = it.value + if (value is PartData.FileItem) { val fileBytes = value.streamProvider().readBytes() - val fileName=value.originalFileName?:throw IllegalArgumentException("参数异常") + val fileName = value.originalFileName ?: throw IllegalArgumentException("参数异常") val format = fileBytes.getFormat() - val fullFileName="${fileName}.${format.format}" + val fullFileName = "${fileName}.${format.format}" log.info("fullFileName=$fullFileName") - val file=File(filePath,fullFileName).apply { + val file = File(filePath, fullFileName).apply { writeBytes(fileBytes) } log.info("文件成功保存到${file.absolutePath}") - val fileId= save(id = userId,"${uploadDir}/${fullFileName}",file=file) + val fileId = save(id = userId, "/${uploadDir}/${fullFileName}", file = file) fileIds.add(fileId) } } return fileIds - }else{ + } else { return null } } } - -class UserIdError(id: Int): IllegalArgumentException("用户id${id}不存在") - -class FileIdError(id:Int): IllegalArgumentException("文件id${id}不存在!") - -class HasAssociationError(s: String) : IllegalArgumentException(s) - /** * 社团服务 */ -object AssociationService: AbstractService() { +object AssociationService : AbstractService() { /** * 注册社团 * - * @param vo + * @param regVo * @return */ - fun register(vo:RegAssociationVo):Boolean{ - return try { - transaction { - val user=User.findById(vo.token.id)?:throw UserIdError(vo.token.id) - Association.find { Associations.user eq vo.token.id }.apply { - when{ - count()!=0L && first().status ->throw HasAssociationError("您是社团团长不能再创建其他社团") - count()!=0L->throw HasAssociationError("您已经提交过社团注册资料,请耐心等待后台管理员处理") + fun register(regVo: AssociationRegVo) { + return transaction { + //再次申请 + val user = User.findById(regVo.token.id) ?: throw UserIdError(regVo.token.id) + if(regVo.id!=null){ + log.info("再次提交【${regVo.name}】注册资料") + val association= Association.findById(regVo.id)?:throw RegIdError(regVo.id) + val log = AuditLogging.new { + this.user = user + } + + association.apply { + name = regVo.name + desc = regVo.desc + logo = ImageFile.findById(regVo.fileId) ?: throw FileIdError(regVo.fileId) + this.log = log + } + }else { + val association:Association?= AuditLogging.find { AuditLeggings.userId eq user.id }.firstOrNull()?.user?.associationMember?.association + when { + association != null && association.log.result == true -> throw IllegalArgumentException("您是社团团长不能再创建其他社团") + association != null && association.log.result == null -> throw IllegalArgumentException("您已经提交过社团注册资料,请耐心等待后台管理员处理") + association == null -> { + val log = AuditLogging.new { + this.user = user + } + Association.new { + name = regVo.name + desc = regVo.desc + logo = ImageFile.findById(regVo.fileId) ?: throw FileIdError(regVo.fileId) + this.log = log + faculty = user.faculty().name + } + } } - Association.new { - name=vo.name - desc=vo.desc - logo=ImageFile.findById(vo.fileId) ?: throw FileIdError(vo.fileId) - this.user=user + } + Notification.new { + title = "注册社团" + content = "您成功提交了一份社团注册资料,请耐心等待后台受理" + receiverId = regVo.token.id + receiverClient = ClientType.Foreground.name + } + BackgroundService.createBackgroundNotification( + title = "审核注册社团", content = "用户${user.name}提交了一份社团资料需要您进行受理", + duty = Duty.PamphaBhusal + ) + this@AssociationService.log.info("未审核社团:${regVo.name}创建成功") + } + } + + /** + * 前台读取社团注册资料 + * + */ + fun read(vo:OnlyToken):AssociationCheckVo?{ + return transaction { + val association:Association?= AuditLogging.find { AuditLeggings.userId eq vo.token.id }.firstOrNull()?.user?.associationMember?.association + return@transaction association?.let { + AssociationCheckVo(id=it.id.value,name=it.name,desc=it.desc,logo = it.logo.filepath, + faculty = AssociationFaculty.valueOf(it.faculty),level = it.level?.let { it1 -> + AssociationLevel.valueOf( + it1 + ) + },checkStatus = when{ + it.log.nextAudit == null && it.log.manager == null->CheckStatus.WaitFirst + it.log.nextAudit == null && it.log.manager != null->CheckStatus.AcceptFirst + it.log.nextAudit != null && it.log.nextAudit?.manager==null ->CheckStatus.WaitLast + it.log.nextAudit != null && it.log.nextAudit?.result==null ->CheckStatus.AcceptLast + else->CheckStatus.Finish + },applyTime = it.log.applyTime.format(), + firstCause = it.log.cause?:"",lastCause = it.log.nextAudit?.cause, + fileId = it.logo.id.value) + } + } + } + + /** + * 注册资料受理 + * + * @param vo + */ + fun accept(vo: AcceptRegAssociation) { + transaction { + val association= Association.find { Associations.logId eq vo.regId }.firstOrNull()?:throw RegIdError(vo.regId) + when (val manager = Manager.findById(vo.token.id)) { + null -> throw ManagerIdError(vo.token.id) + else -> { + association.apply { + if(vo.isFirstAccept) { + log.manager = manager + log.acceptTime = LocalDateTime.now() + }else{ + log.nextAudit?.manager=manager + log.nextAudit?.acceptTime=LocalDateTime.now() + } + } + log.info("[${association.name}]社团注册资料已受理") + Notification.new { + title = "注册社团" + content = "您提交的[${association.name}]社团注册资料已受理" + receiverId = vo.token.id + receiverClient = ClientType.Foreground.name + } } - Notification.new { - title="注册社团" - content="您成功提交了一份社团注册资料,请耐心等待后台受理" - receiverId=vo.token.id - receiverClient=ClientType.Foreground.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(Associations.columns) + .select { + nextAudit[AuditLeggings.result] eq true and (Associations.name like "%${vo.name}%") and (Associations.desc like "%${vo.desc}%") + }.map { + val imageFile=ImageFile.findById(it[Associations.logo])?:throw FileIdError(it[Associations.logo].value) + + AssociationVo(name = it[Associations.name], + id = 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) }) } - BackgroundService.createBackgroundNotification(title = "审核注册社团",content = "用户${user.name}提交了一份社团资料需要您进行受理", - duty = Duty.PamphaBhusal) - return@transaction true + } + } + + private fun toAuditLoggingVo(it:AuditLogging?):AuditLoggingVo?{ + return it?.let { + val auditLogging=AuditLoggingVo(id=it.id.value,user = + UserInfoVo(name = it.user.name,headImg = it.user.headImg?.filepath,desc = it.user.desc),applyTime = it.applyTime.format(),manager = it.manager?.let { + ManagerInfoVo(duty = Duty.valueOf(it.duty),name = it.name,headImg = it.headImg?.filepath,desc = it.desc) }, + acceptTime = it.acceptTime?.format(),cause = it.cause,result = it.result,auditTime = it.auditTime?.format(), + nextAudit = toAuditLoggingVo(it.nextAudit)) + auditLogging + } + } + + fun loadAudit(vo: OnlyToken): List { + return transaction { + return@transaction Association.all().map { + val log=toAuditLoggingVo(it.log)?:throw IllegalArgumentException("转换审核记录出错!!!!") + DisposeRegInfoVo(name = it.name,desc = it.desc,logo = it.logo.filepath,log=log) + }.apply { + log.info("找到${this.size}份社团注册资料") } - log.info("未审核社团创建成功") - true - } catch (e: Exception) { - log.error(e.stackTraceToString()) - false + } + } -} -enum class Duty(val desc:String, val level:Int){ - Teacher("老师",1), - PamphaBhusal("总部长",2), - SecretaryOfTheMinister("秘书部部长",3), - PropagandaDepartment("宣传部部长",3), - LiaisonMinister("外联部部长",3), - SecretaryDepartmentOfficer("秘书部干事",4), - PublicityDepartmentOfficer("宣传部干事",4), - LiaisonOfficer("外联部干事",4) + /** + * 审核记录 + * + * @param vo + */ + fun check(vo:CheckRegVo){ + transaction { + try { + val association=Association.find { Associations.logId eq vo.regId }.firstOrNull()?:throw RegIdError(vo.regId) + val matchUser=User.findById(vo.token.id)?:throw UserIdError(vo.token.id) + association.log.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 { + this@AssociationService.log.info("更新审核结果") + } + when{ + association.log.nextAudit==null&&vo.result->{ + val log = AuditLogging.new { + this.user = matchUser + }.apply { + this@AssociationService.log.info("构造复审记录") + } + association.log.nextAudit=log + BackgroundService.createBackgroundNotification( + title = "审核注册社团", + content = "总部长上报了一份社团注册资料需要您进行受理", + duty = Duty.Teacher + ).apply { + this@AssociationService.log.info("通知老师复审社团注册资料") + } + Notification.new { + title = "注册社团" + content = "您提交的【${association.name}】社团注册资料初审通过,请耐心等待复审" + receiverId = matchUser.id.value + receiverClient = ClientType.Foreground.name + }.apply { + this@AssociationService.log.info("通知前台用户审核结果") + } + } + association.log.nextAudit==null&&!vo.result->{ + Notification.new { + title = "注册社团" + content = "您提交的【${association.name}】社团注册资料初审不通过,可根据初审意见,重新申请\n" + + "【初审意见:${vo.cause},审核人:${association.log.manager?.desc?:""}】" + receiverId = matchUser.id.value + receiverClient = ClientType.Foreground.name + }.apply { + this@AssociationService.log.info("通知前台用户审核结果") + } + } + association.log.nextAudit!=null&&vo.result->{ + AssociationMember.new { + this.association=association + isHead=true + }.apply { + matchUser.associationMember=this + this@AssociationService.log.info("初始化社团团长") + } + Notification.new { + title = "注册社团" + content = "您提交的【${association.name}】社团注册资料复审通过" + receiverId = matchUser.id.value + receiverClient = ClientType.Foreground.name + }.apply { + this@AssociationService.log.info("通知前台用户审核结果") + } + } + else->{ + Notification.new { + title = "注册社团" + content = "您提交的【${association.name}】社团注册资料复审不通过,可根据复审意见,重新申请\n" + + "【复审意见:${vo.cause},审核人:${association.log.nextAudit?.manager?.desc?:""}】" + receiverId = matchUser.id.value + receiverClient = ClientType.Foreground.name + }.apply { + this@AssociationService.log.info("通知前台用户审核结果") + } + } + } + + this@AssociationService.log.info("【${association.name}】社团注册资料审核完成") + } catch (e: Exception) { + rollback() + throw e + } + } + } } /** * 通知服务 */ -object NotificationService:AbstractService(){ +object NotificationService : AbstractService() { /** * 拉取最新通知 @@ -377,17 +654,21 @@ object NotificationService:AbstractService(){ * @param vo * @return */ - fun pull(vo:NotificationDto):List{ + 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) } + 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"))) + it.pull = true + NotificationVo( + title = it.title, id = it.id.value, content = it.content, + createTime = it.createTime.format() + ) } } } @@ -398,12 +679,14 @@ object NotificationService:AbstractService(){ * @param vo * @return */ - fun count(vo:NotificationDto):Long{ + 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}条") + 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}条") } } } @@ -413,16 +696,20 @@ object NotificationService:AbstractService(){ * @param vo * @return */ - fun list(vo:NotificationDto): List? { + fun list(vo: NotificationDto): List? { return vo.page?.let { transaction { log.info("page:${it}") - return@transaction Notification.find{ + 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) + (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.format() + ) } } } @@ -434,8 +721,7 @@ object NotificationService:AbstractService(){ /** * 后台服务 */ -object BackgroundService:AbstractService(){ - +object BackgroundService : AbstractService() { override fun init(environment: ApplicationEnvironment) { @@ -447,68 +733,74 @@ object BackgroundService:AbstractService(){ * 前台任务通知管理员处理 * */ - fun createBackgroundNotification(content:String,title:String,duty: Duty){ + fun createBackgroundNotification(content: String, title: String, duty: Duty) { Manager.find { Managers.duty eq duty.name }.apply { - if(count()==0L){ + if (count() == 0L) { log.warn("找不到适当的${duty.desc}处理此任务") - }else{ + } else { forEach { Notification.new { this.title = title this.content = content - receiverId= it.id.value - receiverClient=ClientType.Background.name + receiverId = it.id.value + receiverClient = ClientType.Background.name } } } } } - private fun createManager(duty:Duty, num:Int=1): MutableList { - val managerList= mutableListOf() - repeat(num){ - val originPassword=randomNum() + private fun createManager(duty: Duty, num: Int = 1): MutableList { + val managerList = mutableListOf() + repeat(num) { + val originPassword = randomNum() Manager.new { - account= randomNum() + account = randomNum() password = originPassword.md5() - this.duty =duty.name - level=duty.level + this.duty = duty.name + this.name = duty.desc }.apply { - managerList.add(InitManagerDto(account=account,originPassword=originPassword,duty=duty)) + managerList.add(InitManagerDto(account = account, originPassword = originPassword, duty = duty)) } } return managerList } //初始化管理员 - private fun initManager(){ + private fun initManager() { transaction { - val resourcePath =this::class.java.classLoader.getResource("")?.path ?: throw IllegalArgumentException("初始化资源目录失败") - val file=File(resourcePath,"管理员账号.txt") - 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") + 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("不需要生成管理员") } - log.info("共生成${allManager.size}个管理员账号") - } else { - log.info("不需要生成管理员") } } + } catch (e: Exception) { + log.error(e) + exitProcess(0) } } } diff --git a/src/Util.kt b/src/Util.kt index f383c64..de67223 100644 --- a/src/Util.kt +++ b/src/Util.kt @@ -8,6 +8,8 @@ import org.jetbrains.exposed.sql.vendors.currentDialect import java.io.File import java.math.BigInteger import java.security.MessageDigest +import java.time.LocalDateTime +import java.time.ZoneOffset const val ApiPathPrefix="/api" /** @@ -33,12 +35,39 @@ fun ByteArray.md5():String{ @Target(AnnotationTarget.CLASS,AnnotationTarget.FIELD) annotation class TableComment(val comment:String) +/** + * 转换成时间戳 + *LocalDateTime->Long + * @return + */ +fun LocalDateTime.format(): Long { + return this.toEpochSecond( + ZoneOffset.of("+8") + ) * 1000 +} + +/** + * 根据文件头识别格式 + * + * @property format + * @property head + */ enum class FileFormat(val format:String,val head:String){ JPEG("jpg","FFD8FF") } +/** + * 获取文件格式 + * + * @return + */ fun ByteArray.getFormat(): FileFormat = format(this) ?: throw IllegalArgumentException("无法识别数据格式") +/** + * 获取文件格式 + * + * @return + */ fun File.getFormat(): FileFormat { val bytes=readBytes() return format(bytes) ?: throw IllegalArgumentException("无非识别文件[${absolutePath}]文件的格式") diff --git a/src/Vo.kt b/src/Vo.kt index fbcefd0..df941a2 100644 --- a/src/Vo.kt +++ b/src/Vo.kt @@ -16,7 +16,82 @@ class Simple { } -data class UserVo(val studentId:String,val name:String) +/** + * 一般信息 + * + */ +abstract class PersonInfoVo{ + abstract val name:String + abstract val headImg: String? + abstract val desc: String +} + +enum class Duty(val desc:String, val level:Int){ + Teacher("老师",1), + PamphaBhusal("总部长",2), + SecretaryOfTheMinister("秘书部部长",3), + PropagandaDepartment("宣传部部长",3), + LiaisonMinister("外联部部长",3), + SecretaryDepartmentOfficer("秘书部干事",4), + PublicityDepartmentOfficer("宣传部干事",4), + LiaisonOfficer("外联部干事",4) +} + +/** + * 个人信息 + * + */ +abstract class OwnInfoVo :PersonInfoVo(){ + abstract val token:Token +} + +data class ManagerInfoVo(val duty: Duty, + override val name: String, + override val headImg: String?, + override val desc: String +):PersonInfoVo() + +data class UserInfoVo(override val name: String, override val headImg: String?, override val desc: String):PersonInfoVo() + + +/** + * 管理员个人信息 + * + * @property account 管理员账号 + * @property name 姓名 + * @property duty 职务 + * @property headImg 头像 + * @property desc 个人简介 + */ +data class ManagerVo( + val account: String, + val duty: Duty, + override val token: Token, + override val name: String, + override val headImg: String?, + override val desc: String +) : OwnInfoVo() + +data class UserRegVo(val studentId: String, val name: String) + +/** + * 用户个人信息 + * + * @property studentId 学号 + * @property name 姓名 + * @property headImg 头像 + * @property desc 个人简介 + */ +data class UserVo( + val studentId: String, + val manager:ManagerVo?=null, + override val token: Token, + override val name: String, + override val headImg: String?, + override val desc: String, + val associationMemberVo: AssociationMemberVo? +) : OwnInfoVo() + sealed class BaseLoginVo{ abstract val password: String @@ -45,15 +120,24 @@ data class ManagerLoginVo(val account:String, data class Token(val token:String, val createTime:Long, val id:Int) + data class LeaveMessageVo(val message: String, override val token:Token, override val clientType: ClientType=ClientType.Foreground):ClientBaseVo() data class OnlyToken(override val token: Token, override val clientType: ClientType):ClientBaseVo() -data class LeaveMessageDto(val message: String,val user: UserVo) +data class LeaveMessageDto(val message: String,val user: UserInfoVo) -data class RegAssociationVo(val name:String, val desc:String, val fileId:Int, override val token: Token, - override val clientType: ClientType=ClientType.Foreground +/** + * 社团注册资料表单 + * + * @property name + * @property desc + * @property fileId + */ +data class AssociationRegVo(val id:Int?, val name: String, val desc: String, val fileId: Int, + override val clientType: ClientType=ClientType.Foreground, + override val token: Token ):ClientBaseVo() data class ImageFileDto(val filepath:String,val md5:String,val createTime: Long,val url:String) @@ -68,4 +152,153 @@ data class NotificationDto(val receiverId:Int, val receiverClient:ClientType, ov val page:PageDto?, override val clientType: ClientType=receiverClient ):ClientBaseVo() -data class NotificationVo(val title:String,val content:String,val id:Int,val createTime: Long) \ No newline at end of file +data class NotificationVo(val title:String,val content:String,val id:Int,val createTime: Long) + +enum class ApplyStatus{ + //已受理 + Accept, + //已审核 + Check +} + +data class SearchAssociationVo(val name:String, val desc:String, + override val clientType: ClientType=ClientType.Foreground, override val token: Token +):ClientBaseVo() + +/** + * 社团级别 + * + */ +enum class AssociationLevel { + A, + B, + C, + D +} + +/** + * 所属院系 + * + */ +enum class AssociationFaculty(val desc: String, val range: IntRange) { + ForeignLanguageDept("外语系", 0..0), + CivilEngineeringDept("土木工程", 1..10), + SEM("经理管理学院", 11..20), + MechanicalEngineeringDept("机械工程", 21..30), + TransportationDept("交通运输", 31..40), + ArchitectureAndArts("建筑与艺术", 41..50), + ElectricalDept("电气", 51..60), + MaterialsDept("材料", 61..70), + MessageDept("信息", 71..80), + MathematicsDept("数理", 81..90), + GraduateStudent("研究生", 91..99) +} + +abstract class BaseAssociationVo{ + abstract val id: Int + abstract val name: String + abstract val desc: String + abstract val logo:String + abstract val faculty: AssociationFaculty + abstract val level: AssociationLevel? +} + +/** + * 社团列表 + * + */ +class AssociationVo( + override val id: Int, + override val name: String, + override val desc: String, + override val logo: String, + override val faculty: AssociationFaculty, + override val level: AssociationLevel? +) :BaseAssociationVo() + +//审核状态 +enum class CheckStatus(val desc: String){ + WaitFirst("等待初审"), + AcceptFirst("初审受理"), + WaitLast("等待复审"), + AcceptLast("复审受理"), + Finish("审核完成") +} + +data class AssociationMemberVo( + val association:AssociationVo, + val isHead:Boolean +) + +//用户社团 +data class AssociationCheckVo( + override val id: Int, + override val name: String, + override val desc: String, + override val logo: String, + override val faculty: AssociationFaculty, + override val level: AssociationLevel?, + val checkStatus:CheckStatus, + val applyTime: Long, + val firstCause:String, + val lastCause:String?, + val fileId:Int +):BaseAssociationVo() + +val facultyRange=IntRange(4,5) +fun User.faculty():AssociationFaculty{ + val num=studentId.substring(facultyRange).toInt() + AssociationFaculty.values().forEach { + if(num in it.range){ + return it + } + } + throw IllegalArgumentException("无法根据${facultyRange}对应的num[${num}]找到所属院系") +} + + +/** + * 通用审核记录 + * + * @property id + * @property user + * @property applyTime + * @property manager + * @property acceptTime + * @property cause + * @property result + * @property auditTime + * @property nextAudit + */ +data class AuditLoggingVo(val id:Int, val user:UserInfoVo, val applyTime: Long, val manager:ManagerInfoVo?, + val acceptTime:Long?, val cause:String?, val result:Boolean?, + val auditTime:Long?, val nextAudit:AuditLoggingVo?) + +/** + * 社团注册资料受理 + * + * @property regId + * @property clientType + */ +data class AcceptRegAssociation(val regId:Int, + val isFirstAccept:Boolean, + override val clientType: ClientType=ClientType.Background, + override val token: Token +):ClientBaseVo() + +/** + * 社团注册审核记录 + * + */ +data class DisposeRegInfoVo(val name:String,val desc:String,val logo:String,val log:AuditLoggingVo) +/** + * 社团注册资料审核 + * + * @property regId + * @property result + * @property cause + * @property clientType + */ +data class CheckRegVo(val regId:Int, val result:Boolean, val cause:String, + override val clientType: ClientType=ClientType.Background, override val token: Token +):ClientBaseVo() \ No newline at end of file diff --git a/test/ApplicationTest.kt b/test/ApplicationTest.kt index 34ce8fa..d882c05 100644 --- a/test/ApplicationTest.kt +++ b/test/ApplicationTest.kt @@ -10,6 +10,9 @@ import com.google.gson.Gson import io.ktor.config.* import io.ktor.http.* import io.ktor.server.testing.* +import org.jetbrains.exposed.sql.alias +import org.jetbrains.exposed.sql.innerJoin +import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.transactions.transaction import java.io.File import kotlin.test.Test @@ -50,7 +53,7 @@ class ApplicationTest { put("ktor.deployment.mysql.username", "root") put("ktor.deployment.mysql.password", "123456") put("ktor.deployment.leaveMessage.maxSize", "20") - put("ktor.deployment.filePath", "upload") + put("ktor.deployment.filePath", "assets") } MySQL(testing = true) }, test) @@ -59,7 +62,7 @@ class ApplicationTest { @Test fun testInsertUser(){ initApp{ - val c=AccountService.register(UserVo(studentId = "6666",name = "hahaha")) + val c=AccountService.register(UserRegVo(studentId = "6666",name = "hahaha")) println(c) } } @@ -98,6 +101,30 @@ class ApplicationTest { println(ClientType.Foreground.name.toLowerCase()) } + @Test + fun test(){ + println("12345678".substring(IntRange(4,5))) + } + + @Test + fun testMap(){ + println(arrayOf(1,2,3).mapNotNull { if(it==2) it*2 else null }) + } + + @Test + fun testAudit(){ + initApp { + transaction { + val nextAudit=AuditLeggings.alias("nextAudit") + Associations.innerJoin(otherTable = AuditLeggings).innerJoin(nextAudit,{AuditLeggings.nextAudit},{nextAudit[AuditLeggings.id]}) + .slice(Associations.columns) + .select { + nextAudit[AuditLeggings.result] eq true + }.toList() + } + } + } + /** * 文档生成 */