社团接口更新

完善注册接口
增加社团查询,社团注册资料受理、审核接口
更新用户表、管理员表、审核表、通知表、社团表
增加社团成员表
增加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. 648
      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)

@ -2,30 +2,31 @@ package com.gyf.csams
import io.ktor.application.* import io.ktor.application.*
import io.ktor.http.content.* import io.ktor.http.content.*
import org.jetbrains.exposed.sql.and import io.ktor.util.*
import org.jetbrains.exposed.sql.deleteWhere import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
import org.slf4j.Logger import org.slf4j.Logger
import java.io.File import java.io.File
import java.time.ZoneOffset import java.time.LocalDateTime
import kotlin.properties.Delegates import kotlin.properties.Delegates
import kotlin.system.exitProcess
interface BaseService{ interface BaseService {
fun init(environment:ApplicationEnvironment) fun init(environment: ApplicationEnvironment)
} }
abstract class AbstractService:BaseService{ abstract class AbstractService : BaseService {
protected lateinit var log:Logger protected lateinit var log: Logger
override fun init(environment: ApplicationEnvironment) { override fun init(environment: ApplicationEnvironment) {
this.log=environment.log this.log = environment.log
} }
} }
/** /**
* 账号服务 * 账号服务
*/ */
object AccountService:AbstractService() { object AccountService : AbstractService() {
/** /**
* 检查学号是否已注册,true=已注册 * 检查学号是否已注册,true=已注册
*/ */
@ -38,16 +39,16 @@ object AccountService:AbstractService() {
/** /**
* 注册 * 注册
*/ */
fun register(userVo: UserVo): UserResDto? { fun register(userVo: UserRegVo): UserResDto? {
try { try {
return transaction { return transaction {
val originPassword = randomNum(8) val originPassword = randomNum(8)
User.new { User.new {
studentId=userVo.studentId studentId = userVo.studentId
name=userVo.name name = userVo.name
password=originPassword.md5() password = originPassword.md5()
} }
return@transaction UserResDto(password=originPassword) return@transaction UserResDto(password = originPassword)
} }
} catch (e: Exception) { } catch (e: Exception) {
log.error("注册失败,发生异常:$e") log.error("注册失败,发生异常:$e")
@ -59,58 +60,133 @@ object AccountService:AbstractService() {
* 前台登录 * 前台登录
* *
*/ */
fun login(userLoginVo: UserLoginVo,_ip:String):Token?{ fun login(userLoginVo: UserLoginVo, _ip: String): UserVo {
return transaction { return transaction {
val matchUser=User.find { Users.studentId eq userLoginVo.studentId }.firstOrNull() val matchUser = User.find { Users.studentId eq userLoginVo.studentId }.firstOrNull()
when { when {
matchUser==null -> { matchUser == null -> {
log.warn("学号:${userLoginVo.studentId}不存在") log.warn("学号:${userLoginVo.studentId}不存在")
return@transaction null throw StudentIdError(userLoginVo.studentId)
} }
userLoginVo.password.md5() != matchUser.password -> { userLoginVo.password.md5() != matchUser.password -> {
log.warn("密码:${userLoginVo.password}错误") log.warn("密码:${userLoginVo.password}错误")
return@transaction null throw PasswordError(id = userLoginVo.studentId, password = userLoginVo.password)
} }
else -> { else -> {
val token=UserToken.new{ val token = UserToken.new {
user=matchUser user = matchUser
ip=_ip ip = _ip
device=userLoginVo.device device = userLoginVo.device
token=listOf(matchUser.id,ip,device).joinToString(separator = ('a' .. 'z').random().toString()).md5() token =
listOf(matchUser.id, ip, device).joinToString(separator = ('a'..'z').random().toString())
.md5()
} }
token.flush() token.flush()
return@transaction Token(id = matchUser.id.value,token = token.token,
createTime = token.createTime.toEpochSecond( return@transaction UserVo(
ZoneOffset.of("+8"))) 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) }
)
}
}
} }
} }
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
)
} }
fun login(managerLoginVo: ManagerLoginVo, _ip:String):Token?{ 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 { return transaction {
val matchUser=Manager.find { Managers.account eq managerLoginVo.account }.firstOrNull() val matchManager = Manager.find { Managers.account eq managerLoginVo.account }.firstOrNull()
when { when {
matchUser==null -> { matchManager == null -> {
log.warn("学号:${managerLoginVo.account}不存在") log.warn("号:${managerLoginVo.account}不存在")
return@transaction null throw AccountError(managerLoginVo.account)
} }
managerLoginVo.password.md5() != matchUser.password -> { managerLoginVo.password.md5() != matchManager.password -> {
log.warn("密码:${managerLoginVo.password}错误") log.warn("密码:${managerLoginVo.password}错误")
return@transaction null throw PasswordError(id = managerLoginVo.account, password = managerLoginVo.password)
} }
else -> { else -> {
val token=ManagerToken.new{ val token = ManagerToken.new {
manager=matchUser manager = matchManager
ip=_ip ip = _ip
device=managerLoginVo.device device = managerLoginVo.device
token=listOf(matchUser.id,ip,device).joinToString(separator = ('a' .. 'z').random().toString()).md5() token =
listOf(matchManager.id, ip, device).joinToString(separator = ('a'..'z').random().toString())
.md5()
} }
token.flush() token.flush()
return@transaction Token(id = matchUser.id.value,token = token.token,
createTime = token.createTime.toEpochSecond( return@transaction tokenRes(token, matchManager)
ZoneOffset.of("+8"))) }
}
}
}
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校验失败")
} }
} }
} }
@ -133,24 +209,24 @@ object AccountService:AbstractService() {
} }
} }
fun <T : ClientBaseVo> validToken(vo:T):Boolean{ fun <T : ClientBaseVo> validToken(vo: T): Boolean {
return if(vo.clientType==ClientType.Foreground){ return if (vo.clientType == ClientType.Foreground) {
validUserToken(vo.token) validUserToken(vo.token)
}else{ } else {
validManagerToken(vo.token) validManagerToken(vo.token)
} }
} }
fun logout(vo:OnlyToken):Boolean{ fun logout(vo: OnlyToken): Boolean {
return transaction { return transaction {
if(vo.clientType==ClientType.Foreground) if (vo.clientType == ClientType.Foreground)
UserTokens.deleteWhere { UserTokens.userId eq vo.token.id }>0 UserTokens.deleteWhere { UserTokens.userId eq vo.token.id } > 0
else else
ManagerTokens.deleteWhere { ManagerTokens.managerId eq vo.token.id }>0 ManagerTokens.deleteWhere { ManagerTokens.managerId eq vo.token.id } > 0
} }
} }
fun test(){ fun test() {
log.info("开始测试") log.info("开始测试")
transaction { transaction {
log.info("查询到个${User.count()}用户") log.info("查询到个${User.count()}用户")
@ -163,12 +239,12 @@ object AccountService:AbstractService() {
/** /**
* 主页服务 * 主页服务
*/ */
object MainService:AbstractService(){ object MainService : AbstractService() {
private var maxSize by Delegates.notNull<Int>() private var maxSize by Delegates.notNull<Int>()
override fun init(environment:ApplicationEnvironment){ override fun init(environment: ApplicationEnvironment) {
super.init(environment) 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 * @param leaveMessageVo
* @return * @return
*/ */
fun createMessage(leaveMessageVo: LeaveMessageVo):Boolean{ fun createMessage(leaveMessageVo: LeaveMessageVo): Boolean {
return if(leaveMessageVo.message.isNotEmpty()){ return if (leaveMessageVo.message.isNotEmpty()) {
return transaction { return transaction {
val count=LeaveMessage.count().toInt() val count = LeaveMessage.count().toInt()
log.info("系统留言数:$count,限制数:$maxSize") log.info("系统留言数:$count,限制数:$maxSize")
if(count>= maxSize){ if (count >= maxSize) {
LeaveMessage.all().sortedBy { it.createTime }.subList(0, count-maxSize).forEach { LeaveMessage.all().sortedBy { it.createTime }.subList(0, count - maxSize).forEach {
it.delete() it.delete()
} }
} }
log.info("保存留言:${leaveMessageVo.message}") log.info("保存留言:${leaveMessageVo.message}")
LeaveMessage.new { LeaveMessage.new {
user= User.findById(leaveMessageVo.token.id)?:throw IllegalArgumentException("非法id") user = User.findById(leaveMessageVo.token.id) ?: throw IllegalArgumentException("非法id")
message = leaveMessageVo.message message = leaveMessageVo.message
} }
log.info("留言保存成功") log.info("留言保存成功")
return@transaction true return@transaction true
} }
}else{ } else {
log.info("留言不能为空") log.info("留言不能为空")
false false
} }
} }
fun getAllLeaveMessage():List<LeaveMessageDto>{ fun getAllLeaveMessage(): List<LeaveMessageDto> {
return transaction { return transaction {
log.info("获取所有留言") log.info("获取所有留言")
return@transaction LeaveMessage.all().toList().map { return@transaction LeaveMessage.all().toList().map {
LeaveMessageDto( LeaveMessageDto(
message = "${it.user.name}说:${it.message}", 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(){ object FileService : AbstractService() {
private lateinit var uploadDir:String private lateinit var uploadDir: String
private lateinit var filePath:String private lateinit var filePath: String
override fun init(environment:ApplicationEnvironment){ override fun init(environment: ApplicationEnvironment) {
super.init(environment) super.init(environment)
this.uploadDir= environment.config.property("ktor.deployment.filePath").getString() this.uploadDir = environment.config.property("ktor.deployment.filePath").getString()
val resourcePath =this::class.java.classLoader.getResource("")?.path ?: throw IllegalArgumentException("初始化资源目录失败") filePath =
File(resourcePath, uploadDir).apply { this::class.java.classLoader.getResource(uploadDir)?.path ?: throw IllegalArgumentException("初始化资源目录失败")
when{ log.info("上传路径[${filePath}]")
!exists()&&mkdir()-> {
log.info("图片上传路径[${absolutePath}]初始化成功")
this@FileService.filePath=absolutePath
}
exists()->{
log.info("图片上传路径[${absolutePath}]已存在")
this@FileService.filePath=absolutePath
}
else->throw IllegalArgumentException("图片上传路径[${absolutePath}]初始化失败")
}
}
} }
private fun save(id: String, path:String, file: File):Int{ private fun save(id: String, path: String, file: File): Int {
return transaction { return transaction {
return@transaction ImageFile.new { return@transaction ImageFile.new {
userId = id userId = id
@ -254,122 +319,334 @@ object FileService:AbstractService(){
} }
private inline fun <reified T:PartData> getPartData(map:Map<String?, PartData>, key:String):T{ private inline fun <reified T : PartData> getPartData(map: Map<String?, PartData>, key: String): T {
if(map.containsKey(key)){ if (map.containsKey(key)) {
val obj= map[key] val obj = map[key]
if(obj is T){ if (obj is T) {
return obj return obj
}else{ } else {
throw IllegalArgumentException("类型错误") throw IllegalArgumentException("类型错误")
} }
} }
throw IllegalArgumentException("找不到key:${key}") throw IllegalArgumentException("找不到key:${key}")
} }
fun storeFile(data:List<PartData>):List<Int>?{ fun storeFile(data: List<PartData>): List<Int>? {
val map=data.associateBy { val map = data.associateBy {
it.name it.name
}.toMutableMap() }.toMutableMap()
log.info("map=${map}") log.info("map=${map}")
val userId=getPartData<PartData.FormItem>(map,"id").value val userId = getPartData<PartData.FormItem>(map, "id").value
val token= getPartData<PartData.FormItem>(map,"token").value val token = getPartData<PartData.FormItem>(map, "token").value
val createTime= getPartData<PartData.FormItem>(map,"createTime").value val createTime = getPartData<PartData.FormItem>(map, "createTime").value
val tokenVo=Token(token=token,id=userId.toInt(),createTime=createTime.toLong()) val tokenVo = Token(token = token, id = userId.toInt(), createTime = createTime.toLong())
if(AccountService.validUserToken(tokenVo)){ if (AccountService.validUserToken(tokenVo)) {
map.remove("id") map.remove("id")
map.remove("token") map.remove("token")
val fileIds= mutableListOf<Int>() val fileIds = mutableListOf<Int>()
map.forEach{ map.forEach {
val value=it.value val value = it.value
if(value is PartData.FileItem){ if (value is PartData.FileItem) {
val fileBytes = value.streamProvider().readBytes() val fileBytes = value.streamProvider().readBytes()
val fileName=value.originalFileName?:throw IllegalArgumentException("参数异常") val fileName = value.originalFileName ?: throw IllegalArgumentException("参数异常")
val format = fileBytes.getFormat() val format = fileBytes.getFormat()
val fullFileName="${fileName}.${format.format}" val fullFileName = "${fileName}.${format.format}"
log.info("fullFileName=$fullFileName") log.info("fullFileName=$fullFileName")
val file=File(filePath,fullFileName).apply { val file = File(filePath, fullFileName).apply {
writeBytes(fileBytes) writeBytes(fileBytes)
} }
log.info("文件成功保存到${file.absolutePath}") 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) fileIds.add(fileId)
} }
} }
return fileIds return fileIds
}else{ } else {
return null return null
} }
} }
} }
/**
* 社团服务
*/
object AssociationService : AbstractService() {
class UserIdError(id: Int): IllegalArgumentException("用户id${id}不存在") /**
* 注册社团
*
* @param regVo
* @return
*/
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
}
class FileIdError(id:Int): IllegalArgumentException("文件id${id}不存在!") }
}
}
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}创建成功")
}
}
class HasAssociationError(s: String) : IllegalArgumentException(s) /**
* 前台读取社团注册资料
*
*/
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
*/ */
object AssociationService: AbstractService() { 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
}
}
}
}
}
/** /**
* 注册社团 * 社团列表加载
* *
* @param vo * @param vo
* @return * @return
*/ */
fun register(vo:RegAssociationVo):Boolean{ fun load(vo: SearchAssociationVo): List<AssociationVo> {
return try { 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) })
}
}
}
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<DisposeRegInfoVo> {
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}份社团注册资料")
}
}
}
/**
* 审核记录
*
* @param vo
*/
fun check(vo:CheckRegVo){
transaction { transaction {
val user=User.findById(vo.token.id)?:throw UserIdError(vo.token.id) try {
Association.find { Associations.user eq vo.token.id }.apply { 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{ when{
count()!=0L && first().status ->throw HasAssociationError("您是社团团长不能再创建其他社团") association.log.nextAudit==null&&vo.result->{
count()!=0L->throw HasAssociationError("您已经提交过社团注册资料,请耐心等待后台管理员处理") 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.new {
name=vo.name
desc=vo.desc
logo=ImageFile.findById(vo.fileId) ?: throw FileIdError(vo.fileId)
this.user=user
} }
association.log.nextAudit==null&&!vo.result->{
Notification.new { Notification.new {
title="注册社团" title = "注册社团"
content="您成功提交了一份社团注册资料,请耐心等待后台受理" content = "您提交的【${association.name}】社团注册资料初审不通过,可根据初审意见,重新申请\n" +
receiverId=vo.token.id "【初审意见:${vo.cause},审核人:${association.log.manager?.desc?:""}"
receiverClient=ClientType.Foreground.name receiverId = matchUser.id.value
receiverClient = ClientType.Foreground.name
}.apply {
this@AssociationService.log.info("通知前台用户审核结果")
} }
BackgroundService.createBackgroundNotification(title = "审核注册社团",content = "用户${user.name}提交了一份社团资料需要您进行受理",
duty = Duty.PamphaBhusal)
return@transaction true
} }
log.info("未审核社团创建成功") association.log.nextAudit!=null&&vo.result->{
true AssociationMember.new {
} catch (e: Exception) { this.association=association
log.error(e.stackTraceToString()) isHead=true
false }.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("通知前台用户审核结果")
}
} }
} }
}
enum class Duty(val desc:String, val level:Int){ this@AssociationService.log.info("${association.name}】社团注册资料审核完成")
Teacher("老师",1), } catch (e: Exception) {
PamphaBhusal("总部长",2), rollback()
SecretaryOfTheMinister("秘书部部长",3), throw e
PropagandaDepartment("宣传部部长",3), }
LiaisonMinister("外联部部长",3), }
SecretaryDepartmentOfficer("秘书部干事",4), }
PublicityDepartmentOfficer("宣传部干事",4),
LiaisonOfficer("外联部干事",4)
} }
/** /**
* 通知服务 * 通知服务
*/ */
object NotificationService:AbstractService(){ object NotificationService : AbstractService() {
/** /**
* 拉取最新通知 * 拉取最新通知
@ -377,17 +654,21 @@ object NotificationService:AbstractService(){
* @param vo * @param vo
* @return * @return
*/ */
fun pull(vo:NotificationDto):List<NotificationVo>{ fun pull(vo: NotificationDto): List<NotificationVo> {
return transaction { return transaction {
log.info("通知查询条件[receiverId=${vo.receiverId},receiverClient=${vo.receiverClient.name},pull=false]") log.info("通知查询条件[receiverId=${vo.receiverId},receiverClient=${vo.receiverClient.name},pull=false]")
val notifications=Notification.find { Notifications.pull eq false and val notifications = Notification.find {
Notifications.pull eq false and
(Notifications.receiverId eq vo.receiverId) and (Notifications.receiverId eq vo.receiverId) and
(Notifications.receiverClient eq vo.receiverClient.name) } (Notifications.receiverClient eq vo.receiverClient.name)
}
log.info("获取${notifications.count()}条最新通知") log.info("获取${notifications.count()}条最新通知")
return@transaction notifications.map { return@transaction notifications.map {
it.pull=true it.pull = true
NotificationVo(title=it.title,id=it.id.value,content = it.content,createTime = it.createTime.toEpochSecond( NotificationVo(
ZoneOffset.of("+8"))) title = it.title, id = it.id.value, content = it.content,
createTime = it.createTime.format()
)
} }
} }
} }
@ -398,11 +679,13 @@ object NotificationService:AbstractService(){
* @param vo * @param vo
* @return * @return
*/ */
fun count(vo:NotificationDto):Long{ fun count(vo: NotificationDto): Long {
return transaction { return transaction {
return@transaction Notification.find{ Notifications.read eq false and return@transaction Notification.find {
Notifications.read eq false and
(Notifications.receiverId eq vo.receiverId) and (Notifications.receiverId eq vo.receiverId) and
(Notifications.receiverClient eq vo.receiverClient.name) }.count().apply { (Notifications.receiverClient eq vo.receiverClient.name)
}.count().apply {
log.info("未读通知${this}") log.info("未读通知${this}")
} }
} }
@ -413,16 +696,20 @@ object NotificationService:AbstractService(){
* @param vo * @param vo
* @return * @return
*/ */
fun list(vo:NotificationDto): List<NotificationVo>? { fun list(vo: NotificationDto): List<NotificationVo>? {
return vo.page?.let { return vo.page?.let {
transaction { transaction {
log.info("page:${it}") log.info("page:${it}")
return@transaction Notification.find{ return@transaction Notification.find {
(Notifications.receiverId eq vo.receiverId) and (Notifications.receiverId eq vo.receiverId) and
(Notifications.receiverClient eq vo.receiverClient.name) }. (Notifications.receiverClient eq vo.receiverClient.name)
limit(n=it.pageSize,offset = (it.currentPage-1)*it.pageSize).map { }.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( NotificationVo(
ZoneOffset.of("+8"))*1000) 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) { override fun init(environment: ApplicationEnvironment) {
@ -447,45 +733,47 @@ 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 { Manager.find { Managers.duty eq duty.name }.apply {
if(count()==0L){ if (count() == 0L) {
log.warn("找不到适当的${duty.desc}处理此任务") log.warn("找不到适当的${duty.desc}处理此任务")
}else{ } else {
forEach { forEach {
Notification.new { Notification.new {
this.title = title this.title = title
this.content = content this.content = content
receiverId= it.id.value receiverId = it.id.value
receiverClient=ClientType.Background.name receiverClient = ClientType.Background.name
} }
} }
} }
} }
} }
private fun createManager(duty:Duty, num:Int=1): MutableList<InitManagerDto> { private fun createManager(duty: Duty, num: Int = 1): MutableList<InitManagerDto> {
val managerList= mutableListOf<InitManagerDto>() val managerList = mutableListOf<InitManagerDto>()
repeat(num){ repeat(num) {
val originPassword=randomNum() val originPassword = randomNum()
Manager.new { Manager.new {
account= randomNum() account = randomNum()
password = originPassword.md5() password = originPassword.md5()
this.duty =duty.name this.duty = duty.name
level=duty.level this.name = duty.desc
}.apply { }.apply {
managerList.add(InitManagerDto(account=account,originPassword=originPassword,duty=duty)) managerList.add(InitManagerDto(account = account, originPassword = originPassword, duty = duty))
} }
} }
return managerList return managerList
} }
//初始化管理员 //初始化管理员
private fun initManager(){ private fun initManager() {
transaction { transaction {
val resourcePath =this::class.java.classLoader.getResource("")?.path ?: throw IllegalArgumentException("初始化资源目录失败") val resourcePath =
val file=File(resourcePath,"管理员账号.txt") this::class.java.classLoader.getResource("")?.path ?: throw IllegalArgumentException("初始化资源目录失败")
if(!file.exists()) { val file = File(resourcePath, "管理员账号.txt")
try {
if (!file.exists()) {
Manager.count().let { it -> Manager.count().let { it ->
if (it.toInt() == 0) { if (it.toInt() == 0) {
val allManager = mutableListOf<InitManagerDto>() val allManager = mutableListOf<InitManagerDto>()
@ -510,6 +798,10 @@ object BackgroundService:AbstractService(){
} }
} }
} }
} catch (e: Exception) {
log.error(e)
exitProcess(0)
}
} }
} }
} }

@ -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