社团接口更新

完善注册接口
增加社团查询,社团注册资料受理、审核接口
更新用户表、管理员表、审核表、通知表、社团表
增加社团成员表
增加Error自定义异常类
服务类更新token校验逻辑,登录获取用户信息,详看类
工具类增加LocalDateTime->Long转换
实体类更新用户管理员实体,增加社团相关实体
master
pan 4 years ago
parent 9fe8fcad7b
commit 1bdf4a1ae2
  1. 2
      resources/application.conf
  2. 1
      src/Application.kt
  3. 145
      src/Controller.kt
  4. 145
      src/Dao.kt
  5. 19
      src/Error.kt
  6. 2
      src/MySQL.kt
  7. 830
      src/Service.kt
  8. 29
      src/Util.kt
  9. 241
      src/Vo.kt
  10. 31
      test/ApplicationTest.kt

@ -11,7 +11,7 @@ ktor {
leaveMessage { leaveMessage {
maxSize = 20 maxSize = 20
} }
filePath = upload filePath = static/image
watch = [ classes ] watch = [ classes ]
} }
application { application {

@ -30,6 +30,7 @@ fun Application.module(testing: Boolean = false) {
fun Application.Controller(testing: Boolean = false){ fun Application.Controller(testing: Boolean = false){
StaticController()
AccountController() AccountController()
TestController() TestController()
MainController() MainController()

@ -5,6 +5,7 @@ import io.ktor.http.content.*
import io.ktor.request.* import io.ktor.request.*
import io.ktor.response.* import io.ktor.response.*
import io.ktor.routing.* import io.ktor.routing.*
import io.ktor.util.*
suspend inline fun <reified T : ClientBaseVo> withToken(call: ApplicationCall, callback: (vo: T) -> Unit) { suspend inline fun <reified T : ClientBaseVo> withToken(call: ApplicationCall, callback: (vo: T) -> Unit) {
val levelVo = call.receive<T>() val levelVo = call.receive<T>()
@ -15,6 +16,25 @@ suspend inline fun <reified T : ClientBaseVo> 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() { fun Application.AccountController() {
routing { routing {
@ -40,7 +60,7 @@ fun Application.AccountController() {
* 注册账号 * 注册账号
*/ */
post { post {
val userVo = call.receive<UserVo>() val userVo = call.receive<UserRegVo>()
val userResDto = AccountService.register(userVo) val userResDto = AccountService.register(userVo)
if (userResDto != null) { if (userResDto != null) {
call.respond(ApiResponse(message = "注册成功", body = userResDto)) call.respond(ApiResponse(message = "注册成功", body = userResDto))
@ -55,26 +75,36 @@ fun Application.AccountController() {
route(path = ClientType.Foreground.name.toLowerCase()){ route(path = ClientType.Foreground.name.toLowerCase()){
post { post {
val userLoginVo = call.receive<UserLoginVo>() val userLoginVo = call.receive<UserLoginVo>()
val token = AccountService.login(userLoginVo, call.request.host()) loginRes(call=call,body = AccountService.login(userLoginVo, call.request.host()))
call.respond(ApiResponse(message = if (token != null) "登陆成功" else "账号或密码错误!!!", body = token))
} }
post("/token"){ post("/token"){
val tokenVo = call.receive<Token>() val tokenVo = call.receive<Token>()
val isValid = AccountService.validUserToken(tokenVo) try {
call.respond(ApiResponse(message = if (isValid) "令牌合法" else "令牌不合法", body = isValid)) 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()){ route(path = ClientType.Background.name.toLowerCase()){
post{ post{
val managerLoginVo = call.receive<ManagerLoginVo>() val managerLoginVo = call.receive<ManagerLoginVo>()
val token = AccountService.login(managerLoginVo, call.request.host()) loginRes(call=call,body = AccountService.login(managerLoginVo, call.request.host()))
call.respond(ApiResponse(message = if (token != null) "登陆成功" else "账号或密码错误!!!", body = token))
} }
post("/token"){ post("/token"){
val tokenVo = call.receive<Token>() val tokenVo = call.receive<Token>()
val isValid = AccountService.validManagerToken(tokenVo) try {
call.respond(ApiResponse(message = if (isValid) "令牌合法" else "令牌不合法", body = isValid)) 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") { post(path = "/logout") {
environment.log.info("退出登录") log.info("退出登录")
val onlyToken = call.receive<OnlyToken>() val onlyToken = call.receive<OnlyToken>()
environment.log.info("$onlyToken") log.info("$onlyToken")
val flag = AccountService.logout(onlyToken) val flag = AccountService.logout(onlyToken)
call.respond(ApiResponse(message = if (flag) "退出成功" else "退出失败", body = flag)) call.respond(ApiResponse(message = if (flag) "退出成功" else "退出失败", body = flag))
} }
@ -131,9 +161,12 @@ fun Application.AssociationController() {
routing { routing {
route("$ApiPathPrefix/association") { route("$ApiPathPrefix/association") {
post("/uploadLogo") { post("/uploadLogo") {
log.info("开始上传logo")
val multipartData = call.receiveMultipart() val multipartData = call.receiveMultipart()
multipartData.readAllParts().apply { multipartData.readAllParts().apply {
environment.log.info("part size=$size") log.info("part size=$size")
if (size == 4) { if (size == 4) {
val result=FileService.storeFile(this) val result=FileService.storeFile(this)
call.respond(ApiResponse(message = if(result.isNullOrEmpty()) "文件上传失败" else call.respond(ApiResponse(message = if(result.isNullOrEmpty()) "文件上传失败" else
@ -143,20 +176,90 @@ fun Application.AssociationController() {
throw IllegalArgumentException("参数异常") throw IllegalArgumentException("参数异常")
} }
} }
log.info("----end-----")
} }
post("/register"){ post("/register"){
withToken<RegAssociationVo>(call = call){ log.info("开始提交注册资料")
withToken<AssociationRegVo>(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<SearchAssociationVo>(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<AcceptRegAssociation>(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<OnlyToken>(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<CheckRegVo>(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<OnlyToken>(call = call){
try { try {
val flag=AssociationService.register(vo=it) call.respond(ApiResponse(message = "用户社团查询成功",body=AssociationService.read(vo=it)))
call.respond(ApiResponse(message = if(flag) "社团注册资料已提交,请等待受理" else "社团注册资料提交失败",body=flag)) } catch (e: Exception) {
} catch (e: UserIdError) { log.error(e)
call.respond(ApiResponse(message = "社团资料提交失败,请联系管理员",body = false)) call.respond(Simple.error("用户社团查询失败"))
} catch (e:FileIdError){
call.respond(ApiResponse(message = "社团资料提交失败,请联系管理员",body = false))
}catch (e:HasAssociationError){
call.respond(ApiResponse(message = e.message?:"发生未知错误,请联系管理员",body = false))
} }
} }
log.info("----end-----")
} }
} }
} }

@ -10,17 +10,28 @@ import org.jetbrains.exposed.sql.`java-time`.CurrentTimestamp
import org.jetbrains.exposed.sql.`java-time`.datetime import org.jetbrains.exposed.sql.`java-time`.datetime
import java.time.LocalDateTime import java.time.LocalDateTime
open class Person:IntIdTable(){
@TableComment("用户")
object Users: IntIdTable(){
@TableComment("学号")
val studentId:Column<String> = varchar(name="student_id",length = 8).uniqueIndex()
@TableComment("姓名") @TableComment("姓名")
val name:Column<String> = varchar(name="name",length = 10) val name:Column<String> = varchar(name="name",length = 10)
@TableComment("密码") @TableComment("密码")
val password:Column<String> = varchar(name="password",length = 32) val password:Column<String> = varchar(name="password",length = 32)
@TableComment("个人简介")
val desc:Column<String> = varchar(name="desc",length = 20).default("")
@TableComment("头像")
val imgId:Column<EntityID<Int>?> = reference("img_id", ImageFiles).nullable()
}
@TableComment("用户")
object Users: Person(){
@TableComment("学号")
val studentId:Column<String> = varchar(name="student_id",length = 8).uniqueIndex()
@TableComment("社团成员id")
val associationMemberId:Column<EntityID<Int>?> = reference("association_member_id", AssociationMembers).nullable()
} }
class User(id:EntityID<Int>):IntEntity(id){ class User(id:EntityID<Int>):IntEntity(id){
@ -28,8 +39,33 @@ class User(id:EntityID<Int>):IntEntity(id){
var studentId by Users.studentId var studentId by Users.studentId
var name by Users.name var name by Users.name
var password by Users.password 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<String> = varchar(name="account",length=10)
@TableComment("职务")
val duty:Column<String> = varchar(name="duty",length = 32)
}
class Manager(id:EntityID<Int>):IntEntity(id){
companion object:IntEntityClass<Manager>(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(){ open class BaseTokens:IntIdTable(){
@TableComment("令牌") @TableComment("令牌")
val token:Column<String> = varchar(name="token",length = 32) val token:Column<String> = varchar(name="token",length = 32)
@ -118,22 +154,25 @@ class ImageFile(id:EntityID<Int>):IntEntity(id){
var createTime by ImageFiles.createTime var createTime by ImageFiles.createTime
} }
@TableComment("社团") @TableComment("社团信息")
object Associations:IntIdTable(){ object Associations:IntIdTable(){
@TableComment("社团名称") @TableComment("社团名称")
val name:Column<String> =varchar(name = "name",length = 10) val name:Column<String> = varchar(name = "name", length = 10)
@TableComment("社团介绍") @TableComment("社团介绍")
val desc:Column<String> =varchar(name = "desc",length = 30) val desc:Column<String> = varchar(name = "desc", length = 30)
@TableComment("社团logo") @TableComment("社团logo")
val logo:Column<EntityID<Int>> = reference("logo",ImageFiles) val logo:Column<EntityID<Int>> = reference("logo", ImageFiles)
@TableComment("社团审核状态") @TableComment("审核信息")
val status:Column<Boolean> = bool(name="status").default(false) val logId:Column<EntityID<Int>> = reference("log_id",AuditLeggings)
@TableComment("创建人") @TableComment("社团级别")
val user:Column<EntityID<Int>> = reference("user_id",Users) val level:Column<String?> = varchar(name="level",length = 1).nullable()
@TableComment("所属院系")
val faculty:Column<String> = varchar(name="faculty",length = 20)
} }
class Association(id:EntityID<Int>):IntEntity(id){ class Association(id:EntityID<Int>):IntEntity(id){
@ -141,57 +180,64 @@ class Association(id:EntityID<Int>):IntEntity(id){
var name by Associations.name var name by Associations.name
var desc by Associations.desc var desc by Associations.desc
var logo by ImageFile referencedOn Associations.logo var logo by ImageFile referencedOn Associations.logo
var status by Associations.status var log by AuditLogging referencedOn Associations.logId
var user by User referencedOn Associations.user val level by Associations.level
var faculty by Associations.faculty
} }
@TableComment("后台管理员") @TableComment("社团成员信息")
object Managers:IntIdTable(){ object AssociationMembers:IntIdTable(){
@TableComment("帐号") @TableComment("社团Id")
val account:Column<String> = varchar(name="account",length=10) val associationId:Column<EntityID<Int>> = reference("association_id",Associations)
@TableComment("密码")
val password:Column<String> = varchar(name="password",length = 32)
@TableComment("职务")
val duty:Column<String> = varchar(name="duty",length = 32)
@TableComment("等级") @TableComment("是否团长")
val level:Column<Int> = integer("level") val isHead:Column<Boolean> = bool("is_head")
} }
class Manager(id:EntityID<Int>):IntEntity(id){ class AssociationMember(id:EntityID<Int>):IntEntity(id) {
companion object:IntEntityClass<Manager>(Managers) companion object : IntEntityClass<AssociationMember>(AssociationMembers)
var account by Managers.account var association by Association referencedOn AssociationMembers.associationId
var password by Managers.password var isHead by AssociationMembers.isHead
var duty by Managers.duty
var level by Managers.level
} }
@TableComment("审核记录") @TableComment("审核记录")
object CheckForms:IntIdTable(){ object AuditLeggings:IntIdTable(){
@TableComment("审核类型") @TableComment("申请人")
val type:Column<String> = varchar(name="type",length = 10) val userId: Column<EntityID<Int>> = reference("user_id",Users)
@TableComment("审核人") @TableComment("申请时间")
val managerId:Column<EntityID<Int>> = reference("manager_id",Managers) val applyTime:Column<LocalDateTime> = datetime("apply_time").defaultExpression(CurrentDateTime())
@TableComment("负责人")
val managerId: Column<EntityID<Int>?> = reference("manager_id",Managers).nullable()
@TableComment("受理时间")
val acceptTime:Column<LocalDateTime?> = datetime("accept_time").nullable()
@TableComment("审核理由") @TableComment("审核理由")
val cause:Column<String> = varchar("cause",length = 30) val cause:Column<String?> = varchar("cause",length = 30).nullable()
@TableComment("审核对象") @TableComment("审核结果")
val target:Column<Int> = integer("target") val result:Column<Boolean?> = bool("result").nullable()
@TableComment("审核时间") @TableComment("审核时间")
val createTime:Column<LocalDateTime> = datetime("create_time").defaultExpression(CurrentDateTime()) val auditTime:Column<LocalDateTime?> = datetime("audit_time").nullable()
@TableComment("复审记录")
val nextAudit:Column<EntityID<Int>?> = reference("next_audit",AuditLeggings).nullable()
} }
class CheckForm(id:EntityID<Int>):IntEntity(id){ class AuditLogging(id:EntityID<Int>):IntEntity(id){
companion object:IntEntityClass<CheckForm>(CheckForms) companion object:IntEntityClass<AuditLogging>(AuditLeggings)
var type by CheckForms.type var user by User referencedOn AuditLeggings.userId
val manager by Manager referrersOn CheckForms.managerId var applyTime by AuditLeggings.applyTime
var cause by CheckForms.cause var manager by Manager optionalReferencedOn AuditLeggings.managerId
var target by CheckForms.target 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("通知记录") @TableComment("通知记录")
@ -200,7 +246,7 @@ object Notifications:IntIdTable(){
val title:Column<String> = varchar(name="title",length = 10) val title:Column<String> = varchar(name="title",length = 10)
@TableComment("通知内容") @TableComment("通知内容")
val content:Column<String> = varchar(name="content",length = 30) val content:Column<String> = varchar(name="content",length = 256)
@TableComment("接收者") @TableComment("接收者")
val receiverId:Column<Int> = integer(name="receiver_id") val receiverId:Column<Int> = integer(name="receiver_id")
@ -228,3 +274,4 @@ class Notification(id:EntityID<Int>):IntEntity(id){
var pull by Notifications.pull var pull by Notifications.pull
var createTime by Notifications.createTime var createTime by Notifications.createTime
} }

@ -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}]不存在")

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

File diff suppressed because it is too large Load Diff

@ -8,6 +8,8 @@ import org.jetbrains.exposed.sql.vendors.currentDialect
import java.io.File import java.io.File
import java.math.BigInteger import java.math.BigInteger
import java.security.MessageDigest import java.security.MessageDigest
import java.time.LocalDateTime
import java.time.ZoneOffset
const val ApiPathPrefix="/api" const val ApiPathPrefix="/api"
/** /**
@ -33,12 +35,39 @@ fun ByteArray.md5():String{
@Target(AnnotationTarget.CLASS,AnnotationTarget.FIELD) @Target(AnnotationTarget.CLASS,AnnotationTarget.FIELD)
annotation class TableComment(val comment:String) 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){ enum class FileFormat(val format:String,val head:String){
JPEG("jpg","FFD8FF") JPEG("jpg","FFD8FF")
} }
/**
* 获取文件格式
*
* @return
*/
fun ByteArray.getFormat(): FileFormat = format(this) ?: throw IllegalArgumentException("无法识别数据格式") fun ByteArray.getFormat(): FileFormat = format(this) ?: throw IllegalArgumentException("无法识别数据格式")
/**
* 获取文件格式
*
* @return
*/
fun File.getFormat(): FileFormat { fun File.getFormat(): FileFormat {
val bytes=readBytes() val bytes=readBytes()
return format(bytes) ?: throw IllegalArgumentException("无非识别文件[${absolutePath}]文件的格式") return format(bytes) ?: throw IllegalArgumentException("无非识别文件[${absolutePath}]文件的格式")

@ -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{ sealed class BaseLoginVo{
abstract val password: String 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 Token(val token:String, val createTime:Long, val id:Int)
data class LeaveMessageVo(val message: String, override val token:Token, data class LeaveMessageVo(val message: String, override val token:Token,
override val clientType: ClientType=ClientType.Foreground):ClientBaseVo() override val clientType: ClientType=ClientType.Foreground):ClientBaseVo()
data class OnlyToken(override val token: Token, override val clientType: ClientType):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() ):ClientBaseVo()
data class ImageFileDto(val filepath:String,val md5:String,val createTime: Long,val url:String) data class ImageFileDto(val filepath:String,val md5:String,val createTime: Long,val url:String)
@ -69,3 +153,152 @@ data class NotificationDto(val receiverId:Int, val receiverClient:ClientType, ov
):ClientBaseVo() ):ClientBaseVo()
data class NotificationVo(val title:String,val content:String,val id:Int,val createTime: Long) 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()

@ -10,6 +10,9 @@ import com.google.gson.Gson
import io.ktor.config.* import io.ktor.config.*
import io.ktor.http.* import io.ktor.http.*
import io.ktor.server.testing.* import io.ktor.server.testing.*
import org.jetbrains.exposed.sql.alias
import org.jetbrains.exposed.sql.innerJoin
import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
import java.io.File import java.io.File
import kotlin.test.Test import kotlin.test.Test
@ -50,7 +53,7 @@ class ApplicationTest {
put("ktor.deployment.mysql.username", "root") put("ktor.deployment.mysql.username", "root")
put("ktor.deployment.mysql.password", "123456") put("ktor.deployment.mysql.password", "123456")
put("ktor.deployment.leaveMessage.maxSize", "20") put("ktor.deployment.leaveMessage.maxSize", "20")
put("ktor.deployment.filePath", "upload") put("ktor.deployment.filePath", "assets")
} }
MySQL(testing = true) MySQL(testing = true)
}, test) }, test)
@ -59,7 +62,7 @@ class ApplicationTest {
@Test @Test
fun testInsertUser(){ fun testInsertUser(){
initApp{ initApp{
val c=AccountService.register(UserVo(studentId = "6666",name = "hahaha")) val c=AccountService.register(UserRegVo(studentId = "6666",name = "hahaha"))
println(c) println(c)
} }
} }
@ -98,6 +101,30 @@ class ApplicationTest {
println(ClientType.Foreground.name.toLowerCase()) 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()
}
}
}
/** /**
* 文档生成 * 文档生成
*/ */

Loading…
Cancel
Save