You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
516 lines
18 KiB
516 lines
18 KiB
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 org.jetbrains.exposed.sql.transactions.transaction
|
|
import org.slf4j.Logger
|
|
import java.io.File
|
|
import java.time.ZoneOffset
|
|
import kotlin.properties.Delegates
|
|
|
|
interface BaseService{
|
|
fun init(environment:ApplicationEnvironment)
|
|
}
|
|
|
|
|
|
abstract class AbstractService:BaseService{
|
|
protected lateinit var log:Logger
|
|
override fun init(environment: ApplicationEnvironment) {
|
|
this.log=environment.log
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 账号服务
|
|
*/
|
|
object AccountService:AbstractService() {
|
|
/**
|
|
* 检查学号是否已注册,true=已注册
|
|
*/
|
|
fun registered(selectId: String): Boolean {
|
|
return transaction {
|
|
return@transaction !User.find { Users.studentId eq selectId }.empty()
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 注册
|
|
*/
|
|
fun 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)
|
|
}
|
|
} 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(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")))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fun validManagerToken(token:Token):Boolean{
|
|
return transaction {
|
|
!ManagerToken.find {
|
|
ManagerTokens.managerId eq token.id
|
|
ManagerTokens.token eq token.token
|
|
}.empty()
|
|
}
|
|
}
|
|
|
|
fun validUserToken(token: Token):Boolean{
|
|
return transaction {
|
|
!UserToken.find {
|
|
UserTokens.userId eq token.id
|
|
UserTokens.token eq token.token
|
|
}.empty()
|
|
}
|
|
}
|
|
|
|
fun <T : ClientBaseVo> 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(){
|
|
private var maxSize by Delegates.notNull<Int>()
|
|
|
|
override fun init(environment:ApplicationEnvironment){
|
|
super.init(environment)
|
|
this.maxSize=environment.config.property("ktor.deployment.leaveMessage.maxSize").getString().toInt()
|
|
}
|
|
|
|
|
|
/**
|
|
* 创建留言信息
|
|
*
|
|
* @param leaveMessageVo
|
|
* @return
|
|
*/
|
|
fun createMessage(leaveMessageVo: LeaveMessageVo):Boolean{
|
|
return if(leaveMessageVo.message.isNotEmpty()){
|
|
return transaction {
|
|
val count=LeaveMessage.count().toInt()
|
|
log.info("系统留言数:$count,限制数:$maxSize")
|
|
if(count>= maxSize){
|
|
LeaveMessage.all().sortedBy { it.createTime }.subList(0, count-maxSize).forEach {
|
|
it.delete()
|
|
}
|
|
|
|
}
|
|
log.info("保存留言:${leaveMessageVo.message}")
|
|
LeaveMessage.new {
|
|
user= User.findById(leaveMessageVo.token.id)?:throw IllegalArgumentException("非法id")
|
|
message = leaveMessageVo.message
|
|
}
|
|
log.info("留言保存成功")
|
|
return@transaction true
|
|
}
|
|
}else{
|
|
log.info("留言不能为空")
|
|
false
|
|
}
|
|
}
|
|
|
|
fun getAllLeaveMessage():List<LeaveMessageDto>{
|
|
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)
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 文件管理服务
|
|
*/
|
|
object FileService:AbstractService(){
|
|
private lateinit var uploadDir:String
|
|
private lateinit var filePath:String
|
|
|
|
override fun init(environment:ApplicationEnvironment){
|
|
super.init(environment)
|
|
this.uploadDir= environment.config.property("ktor.deployment.filePath").getString()
|
|
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}]初始化失败")
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun save(id: String, path:String, file: File):Int{
|
|
return transaction {
|
|
return@transaction ImageFile.new {
|
|
userId = id
|
|
filepath = path
|
|
md5 = file.readBytes().md5()
|
|
}
|
|
}.id.value
|
|
}
|
|
|
|
|
|
private inline fun <reified T:PartData> getPartData(map:Map<String?, PartData>, key:String):T{
|
|
if(map.containsKey(key)){
|
|
val obj= map[key]
|
|
if(obj is T){
|
|
return obj
|
|
}else{
|
|
throw IllegalArgumentException("类型错误")
|
|
}
|
|
}
|
|
throw IllegalArgumentException("找不到key:${key}")
|
|
}
|
|
|
|
fun storeFile(data:List<PartData>):List<Int>?{
|
|
val map=data.associateBy {
|
|
it.name
|
|
}.toMutableMap()
|
|
log.info("map=${map}")
|
|
val userId=getPartData<PartData.FormItem>(map,"id").value
|
|
val token= getPartData<PartData.FormItem>(map,"token").value
|
|
val createTime= getPartData<PartData.FormItem>(map,"createTime").value
|
|
val tokenVo=Token(token=token,id=userId.toInt(),createTime=createTime.toLong())
|
|
if(AccountService.validUserToken(tokenVo)){
|
|
map.remove("id")
|
|
map.remove("token")
|
|
val fileIds= mutableListOf<Int>()
|
|
map.forEach{
|
|
val value=it.value
|
|
if(value is PartData.FileItem){
|
|
val fileBytes = value.streamProvider().readBytes()
|
|
val fileName=value.originalFileName?:throw IllegalArgumentException("参数异常")
|
|
val format = fileBytes.getFormat()
|
|
val fullFileName="${fileName}.${format.format}"
|
|
log.info("fullFileName=$fullFileName")
|
|
val file=File(filePath,fullFileName).apply {
|
|
writeBytes(fileBytes)
|
|
}
|
|
log.info("文件成功保存到${file.absolutePath}")
|
|
val fileId= save(id = userId,"${uploadDir}/${fullFileName}",file=file)
|
|
fileIds.add(fileId)
|
|
}
|
|
}
|
|
return fileIds
|
|
}else{
|
|
return null
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
class UserIdError(id: Int): IllegalArgumentException("用户id${id}不存在")
|
|
|
|
class FileIdError(id:Int): IllegalArgumentException("文件id${id}不存在!")
|
|
|
|
class HasAssociationError(s: String) : IllegalArgumentException(s)
|
|
|
|
/**
|
|
* 社团服务
|
|
*/
|
|
object AssociationService: AbstractService() {
|
|
|
|
/**
|
|
* 注册社团
|
|
*
|
|
* @param vo
|
|
* @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("您已经提交过社团注册资料,请耐心等待后台管理员处理")
|
|
}
|
|
}
|
|
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=vo.token.id
|
|
receiverClient=ClientType.Foreground.name
|
|
}
|
|
BackgroundService.createBackgroundNotification(title = "审核注册社团",content = "用户${user.name}提交了一份社团资料需要您进行受理",
|
|
duty = Duty.PamphaBhusal)
|
|
return@transaction true
|
|
}
|
|
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)
|
|
}
|
|
|
|
/**
|
|
* 通知服务
|
|
*/
|
|
object NotificationService:AbstractService(){
|
|
|
|
/**
|
|
* 拉取最新通知
|
|
*
|
|
* @param vo
|
|
* @return
|
|
*/
|
|
fun pull(vo:NotificationDto):List<NotificationVo>{
|
|
return transaction {
|
|
log.info("通知查询条件[receiverId=${vo.receiverId},receiverClient=${vo.receiverClient.name},pull=false]")
|
|
val notifications=Notification.find { Notifications.pull eq false and
|
|
(Notifications.receiverId eq vo.receiverId) and
|
|
(Notifications.receiverClient eq vo.receiverClient.name) }
|
|
log.info("获取${notifications.count()}条最新通知")
|
|
return@transaction notifications.map {
|
|
it.pull=true
|
|
NotificationVo(title=it.title,id=it.id.value,content = it.content,createTime = it.createTime.toEpochSecond(
|
|
ZoneOffset.of("+8")))
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 未读通知计数
|
|
*
|
|
* @param vo
|
|
* @return
|
|
*/
|
|
fun count(vo:NotificationDto):Long{
|
|
return transaction {
|
|
return@transaction Notification.find{ Notifications.read eq false and
|
|
(Notifications.receiverId eq vo.receiverId) and
|
|
(Notifications.receiverClient eq vo.receiverClient.name) }.count().apply {
|
|
log.info("未读通知${this}条")
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param vo
|
|
* @return
|
|
*/
|
|
fun list(vo:NotificationDto): List<NotificationVo>? {
|
|
return vo.page?.let {
|
|
transaction {
|
|
log.info("page:${it}")
|
|
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)
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* 后台服务
|
|
*/
|
|
object BackgroundService:AbstractService(){
|
|
|
|
|
|
|
|
override fun init(environment: ApplicationEnvironment) {
|
|
super.init(environment)
|
|
initManager()
|
|
}
|
|
|
|
/**
|
|
* 前台任务通知管理员处理
|
|
*
|
|
*/
|
|
fun createBackgroundNotification(content:String,title:String,duty: Duty){
|
|
Manager.find { Managers.duty eq duty.name }.apply {
|
|
if(count()==0L){
|
|
log.warn("找不到适当的${duty.desc}处理此任务")
|
|
}else{
|
|
forEach {
|
|
Notification.new {
|
|
this.title = title
|
|
this.content = content
|
|
receiverId= it.id.value
|
|
receiverClient=ClientType.Background.name
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun createManager(duty:Duty, num:Int=1): MutableList<InitManagerDto> {
|
|
val managerList= mutableListOf<InitManagerDto>()
|
|
repeat(num){
|
|
val originPassword=randomNum()
|
|
Manager.new {
|
|
account= randomNum()
|
|
password = originPassword.md5()
|
|
this.duty =duty.name
|
|
level=duty.level
|
|
}.apply {
|
|
managerList.add(InitManagerDto(account=account,originPassword=originPassword,duty=duty))
|
|
}
|
|
}
|
|
return managerList
|
|
}
|
|
|
|
//初始化管理员
|
|
private fun initManager(){
|
|
transaction {
|
|
val resourcePath =this::class.java.classLoader.getResource("")?.path ?: throw IllegalArgumentException("初始化资源目录失败")
|
|
val file=File(resourcePath,"管理员账号.txt")
|
|
if(!file.exists()) {
|
|
Manager.count().let { it ->
|
|
if (it.toInt() == 0) {
|
|
val allManager = mutableListOf<InitManagerDto>()
|
|
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("不需要生成管理员")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|