调整数据库结构,ID不再自增,使用UUID,修改后端逻辑
This commit is contained in:
@@ -1,16 +1,22 @@
|
||||
package ink.snowflake.server
|
||||
|
||||
import com.google.gson.Gson
|
||||
import ink.snowflake.server.plugins.*
|
||||
import ink.snowflake.server.route.User
|
||||
import ink.snowflake.server.route.chat
|
||||
import ink.snowflake.server.plugins.configureSockets
|
||||
import ink.snowflake.server.route.AI
|
||||
import ink.snowflake.server.route.ImageAnalytics
|
||||
import ink.snowflake.server.route.RemoteDebug
|
||||
import ink.snowflake.server.route.VideoAnalytics
|
||||
import ink.snowflake.server.route.VideoAnalyticsJetson
|
||||
import ink.snowflake.server.controller.User
|
||||
import ink.snowflake.server.controller.chat
|
||||
import ink.snowflake.server.utils.plugins.configureSockets
|
||||
import ink.snowflake.server.controller.AI
|
||||
import ink.snowflake.server.controller.ImageAnalytics
|
||||
import ink.snowflake.server.controller.RemoteDebug
|
||||
import ink.snowflake.server.controller.VideoAnalytics
|
||||
import ink.snowflake.server.controller.VideoAnalyticsJetson
|
||||
import ink.snowflake.server.utils.AppConfig
|
||||
import ink.snowflake.server.utils.plugins.configureCORS
|
||||
import ink.snowflake.server.utils.plugins.configureDatabases
|
||||
import ink.snowflake.server.utils.plugins.configureSecurity
|
||||
import ink.snowflake.server.utils.plugins.configureSerialization
|
||||
import ink.snowflake.server.utils.plugins.configureStaticPath
|
||||
import ink.snowflake.server.utils.plugins.configureStatusPages
|
||||
import ink.snowflake.server.utils.plugins.configureTemplating
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.tomcat.jakarta.*
|
||||
|
||||
|
||||
+1
-8
@@ -1,13 +1,7 @@
|
||||
package ink.snowflake.server.route
|
||||
package ink.snowflake.server.controller
|
||||
|
||||
import ai.koog.agents.core.agent.AIAgent
|
||||
import ai.koog.agents.core.tools.ToolRegistry
|
||||
import ai.koog.agents.ext.tool.SayToUser
|
||||
import ai.koog.prompt.executor.clients.openai.OpenAIModels
|
||||
import ai.koog.prompt.executor.llms.all.simpleOllamaAIExecutor
|
||||
import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor
|
||||
import ai.koog.prompt.executor.ollama.client.OllamaClient
|
||||
import ai.koog.prompt.executor.ollama.client.OllamaModelCard
|
||||
import ai.koog.prompt.llm.LLMCapability
|
||||
import ai.koog.prompt.llm.LLMProvider
|
||||
import ai.koog.prompt.llm.LLModel
|
||||
@@ -17,7 +11,6 @@ import io.ktor.server.response.respond
|
||||
import io.ktor.server.routing.get
|
||||
import io.ktor.server.routing.route
|
||||
import io.ktor.server.routing.routing
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
fun Application.AI() {
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
package ink.snowflake.server.route
|
||||
package ink.snowflake.server.controller
|
||||
|
||||
import com.cyberecho.mdoel.database.WSChatRecords
|
||||
import ink.snowflake.server.gson
|
||||
@@ -6,7 +6,7 @@ import ink.snowflake.server.model.request.ChatRequest
|
||||
import ink.snowflake.server.model.response.ChatResponse
|
||||
import ink.snowflake.server.model.response.BaseResponse
|
||||
import ink.snowflake.server.utils.WebSocketManager
|
||||
import ink.snowflake.server.database.ChatRecordsDao
|
||||
import ink.snowflake.server.utils.dao.ChatRecordsDao
|
||||
import ink.snowflake.server.utils.getUserIdByToken
|
||||
import io.ktor.client.*
|
||||
import io.ktor.client.engine.cio.*
|
||||
+2
-3
@@ -1,14 +1,13 @@
|
||||
package ink.snowflake.server.route
|
||||
package ink.snowflake.server.controller
|
||||
|
||||
import ink.snowflake.server.model.request.ImageAnalyticsRequest
|
||||
import ink.snowflake.server.model.response.*
|
||||
import ink.snowflake.server.database.ImageDao
|
||||
import ink.snowflake.server.utils.dao.ImageDao
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.auth.*
|
||||
import io.ktor.server.routing.*
|
||||
import io.ktor.server.request.*
|
||||
import io.ktor.server.response.*
|
||||
import kotlin.text.isNullOrEmpty
|
||||
|
||||
fun Application.ImageAnalytics() {
|
||||
routing {
|
||||
+6
-6
@@ -1,8 +1,8 @@
|
||||
package ink.snowflake.server.route
|
||||
package ink.snowflake.server.controller
|
||||
|
||||
import com.google.gson.Gson
|
||||
import ink.snowflake.server.SERVER_PATH_FRP
|
||||
import ink.snowflake.server.model.request.DevicesInfo
|
||||
import ink.snowflake.server.model.request.DevicesInfoRequest
|
||||
import ink.snowflake.server.model.response.*
|
||||
import ink.snowflake.server.utils.runCommand
|
||||
import io.ktor.client.HttpClient
|
||||
@@ -79,13 +79,13 @@ fun Application.RemoteDebug() {
|
||||
val name = call.parameters["name"]
|
||||
val response: HttpResponse = client.get("http://171.212.101.201:65534/api/proxy/tcp")
|
||||
val responseBody: String = response.bodyAsText()
|
||||
val devicesInfo: DevicesInfo = Gson().fromJson(responseBody, DevicesInfo::class.java)
|
||||
val onlineDevices = devicesInfo.proxies.stream()
|
||||
val devicesInfoRequest: DevicesInfoRequest = Gson().fromJson(responseBody, DevicesInfoRequest::class.java)
|
||||
val onlineDevices = devicesInfoRequest.proxies.stream()
|
||||
.filter { it.status == "online" && it.conf != null && it.conf.remotePort >= 10000 && it.conf.remotePort <= 20000 }
|
||||
val devices: MutableList<DeviceItem> = mutableListOf()
|
||||
val devices: MutableList<DeviceItemResponse> = mutableListOf()
|
||||
for (data in onlineDevices) {
|
||||
if (name == "null" || name == null || data.name.contains(name) && data.conf != null) {
|
||||
devices.add(DeviceItem(data.name, data.conf!!.remotePort))
|
||||
devices.add(DeviceItemResponse(data.name, data.conf!!.remotePort))
|
||||
}
|
||||
}
|
||||
call.respond(BaseResponse(data = devices))
|
||||
+81
-86
@@ -1,4 +1,4 @@
|
||||
package ink.snowflake.server.route
|
||||
package ink.snowflake.server.controller
|
||||
|
||||
import com.auth0.jwt.JWT
|
||||
import com.auth0.jwt.algorithms.Algorithm
|
||||
@@ -8,7 +8,8 @@ import ink.snowflake.server.model.request.RefreshTokenRequest
|
||||
import ink.snowflake.server.model.request.RegisterRequest
|
||||
import ink.snowflake.server.model.response.*
|
||||
import ink.snowflake.server.utils.AppConfig
|
||||
import ink.snowflake.server.database.UserDAO
|
||||
import ink.snowflake.server.utils.dao.AIDao
|
||||
import ink.snowflake.server.utils.dao.UserDAO
|
||||
import ink.snowflake.server.utils.getUserIdByToken
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.auth.*
|
||||
@@ -49,31 +50,26 @@ fun Application.User(config: AppConfig) {
|
||||
val password = loginRequest.password
|
||||
// 查找用户
|
||||
val userId = UserDAO.getUserIdByEmail(email)
|
||||
val res = if(userId == null){
|
||||
// 账号不存在
|
||||
-1
|
||||
}else{
|
||||
val userPassword = UserDAO.getPasswordById(userId)
|
||||
// 验证密码
|
||||
if (password == userPassword) {
|
||||
// 登录成功
|
||||
userId
|
||||
} else {
|
||||
// 账号密码不匹配
|
||||
-2
|
||||
}
|
||||
}
|
||||
call.respond(
|
||||
when (res) {
|
||||
-1 -> BaseResponse(status = false, message = "尚未注册", data = null)
|
||||
-2 -> BaseResponse(status = false, message = "账号密码不匹配,请重新登录", data = null)
|
||||
else -> BaseResponse(
|
||||
status = true, data = LoginResponse(
|
||||
userId.toString(),
|
||||
generateAccessToken(config, res),
|
||||
generateRefreshToken(config, res)
|
||||
if (userId == null) {
|
||||
// 账号不存在
|
||||
BaseResponse(status = false, message = "尚未注册", data = null)
|
||||
} else {
|
||||
val userPassword = UserDAO.getPasswordById(userId)
|
||||
// 验证密码
|
||||
if (password == userPassword) {
|
||||
// 登录成功
|
||||
BaseResponse (
|
||||
status = true, data = LoginResponse(
|
||||
userId,
|
||||
generateAccessToken(config, userId),
|
||||
generateRefreshToken(config, userId)
|
||||
)
|
||||
)
|
||||
)
|
||||
} else {
|
||||
// 账号密码不匹配
|
||||
BaseResponse(status = false, message = "账号密码不匹配,请重新登录", data = null)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -98,77 +94,76 @@ fun Application.User(config: AppConfig) {
|
||||
// 从 Redis 获取保存的验证码
|
||||
val bucket: RBucket<String> = redisClient.getBucket("verificationCode:$account")
|
||||
val storedCode = bucket.get()
|
||||
if (storedCode == null) {
|
||||
call.respond(BaseResponse(status = false, message = "验证码过期", data = null))
|
||||
} else if (storedCode != register.code) {
|
||||
call.respond(BaseResponse(status = false, message = "验证码错误", data = null))
|
||||
} else {
|
||||
// 查找用户
|
||||
val userId = UserDAO.getUserIdByEmail(register.account)
|
||||
val res = if(userId != null){
|
||||
// 如果用户已存在,返回 0
|
||||
0
|
||||
}else{
|
||||
// 用户不存在,插入新用户
|
||||
UserDAO.registerByEmailAndGetId(register.account,register.password)
|
||||
}
|
||||
if (res > 0) {
|
||||
call.respond(
|
||||
BaseResponse(
|
||||
status = true, message = "注册成功", data =
|
||||
BaseResponse(
|
||||
status = true, data = LoginResponse(
|
||||
account,
|
||||
generateAccessToken(config, res),
|
||||
generateRefreshToken(config, res)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
call.respond(
|
||||
if (storedCode == null) {
|
||||
BaseResponse(status = false, message = "验证码过期,请重新获取", data = null)
|
||||
} else if (storedCode != register.code) {
|
||||
BaseResponse(status = false, message = "验证码错误,请检查或重新获取", data = null)
|
||||
} else {
|
||||
call.respond(BaseResponse(status = false, message = "账户已存在", data = null))
|
||||
// 查找用户
|
||||
val userId = UserDAO.getUserIdByEmail(register.account)
|
||||
if (userId == null) {
|
||||
// 用户不存在,插入新用户
|
||||
val id = UserDAO.registerByEmailAndGetId(register.account, register.password)
|
||||
if (id == null) {
|
||||
BaseResponse(status = true, message = "注册失败,请联系管理员", data = null)
|
||||
} else {
|
||||
BaseResponse(
|
||||
status = true, message = "注册成功", data = LoginResponse(
|
||||
id,
|
||||
generateAccessToken(config, id),
|
||||
generateRefreshToken(config, id)
|
||||
)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
// 如果用户已存在,返回 0
|
||||
BaseResponse(status = false, message = "账户已存在", data = null)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
post("/refreshToken") {
|
||||
try {
|
||||
val token = call.receive<RefreshTokenRequest>()
|
||||
val verifier = JWT.require(Algorithm.HMAC256(config.jwtSecret))
|
||||
.withAudience(config.jwtAudience)
|
||||
.withIssuer(config.jwtDomain)
|
||||
.build()
|
||||
val decodedJWT = verifier.verify(token.refreshToken)
|
||||
val tokenType = decodedJWT.getClaim("token_type").asString()
|
||||
if (tokenType != "refresh_token") {
|
||||
call.respond(
|
||||
val token = call.receive<RefreshTokenRequest>()
|
||||
val verifier = JWT.require(Algorithm.HMAC256(config.jwtSecret))
|
||||
.withAudience(config.jwtAudience)
|
||||
.withIssuer(config.jwtDomain)
|
||||
.build()
|
||||
val decodedJWT = verifier.verify(token.refreshToken)
|
||||
val tokenType = decodedJWT.getClaim("token_type").asString()
|
||||
call.respond(
|
||||
try {
|
||||
if (tokenType != "refresh_token") {
|
||||
BaseResponse(
|
||||
status = false,
|
||||
message = "拿什么乱七八糟的东西跟我换Access Token呢,???",
|
||||
data = null
|
||||
)
|
||||
)
|
||||
}
|
||||
val userId = decodedJWT.getClaim("user_id").asInt()
|
||||
// 生成新的access token和refresh token
|
||||
call.respond(
|
||||
}
|
||||
val userId = UUID.fromString(decodedJWT.getClaim("user_id").asString())
|
||||
// 生成新的access token和refresh token
|
||||
BaseResponse(
|
||||
status = true, data = RefreshTokenResponse(
|
||||
generateAccessToken(config, userId),
|
||||
generateRefreshToken(config, userId)
|
||||
)
|
||||
)
|
||||
)
|
||||
call.respond(BaseResponse(data = generateAccessToken(config, userId)))
|
||||
} catch (ex: Exception) {
|
||||
call.respond(BaseResponse(status = false, message = "token解析错误", data = null))
|
||||
}
|
||||
BaseResponse(data = generateAccessToken(config, userId))
|
||||
} catch (ex: Exception) {
|
||||
BaseResponse(status = false, message = "token解析错误", data = null)
|
||||
}
|
||||
)
|
||||
}
|
||||
authenticate {
|
||||
get("/getAiList") {
|
||||
// val allAIProfile = AIDao.getAllAIProfiles().stream().map {
|
||||
// AiListForUser(aiId = it.id, aiName = it.name, aiAvatar = it.avatarUrl ?: "")
|
||||
// }.toList()
|
||||
// call.respond(BaseResponse(data = allAIProfile))
|
||||
val allAIProfile = AIDao.getAllAIProfiles().map {
|
||||
AiListForUserResponse(
|
||||
aiId = it.id,
|
||||
aiName = it.name,
|
||||
aiAvatar = it.avatarUrl ?: ""
|
||||
)
|
||||
}
|
||||
call.respond(BaseResponse(data = allAIProfile))
|
||||
}
|
||||
get("/getUserInfo") {
|
||||
val userId = getUserIdByToken(call)
|
||||
@@ -190,22 +185,22 @@ fun Application.User(config: AppConfig) {
|
||||
}
|
||||
}
|
||||
|
||||
fun generateAccessToken(config: AppConfig, userId: Int): Token {
|
||||
fun generateAccessToken(config: AppConfig, userId: UUID): Token {
|
||||
// return generateToken(config, userId, 120, "access_token")
|
||||
return generateToken(config, userId, 2 * 60 * 60, "access_token")
|
||||
}
|
||||
|
||||
fun generateRefreshToken(config: AppConfig, userId: Int): Token {
|
||||
fun generateRefreshToken(config: AppConfig, userId: UUID): Token {
|
||||
return generateToken(config, userId, 10 * 24 * 60 * 60, "refresh_token")
|
||||
}
|
||||
|
||||
|
||||
fun generateToken(config: AppConfig, userId: Int, second: Int, tokenType: String): Token {
|
||||
fun generateToken(config: AppConfig, userId: UUID, second: Int, tokenType: String): Token {
|
||||
val expiresAt = Date(System.currentTimeMillis() + second * 1000) //
|
||||
val token = JWT.create()
|
||||
.withAudience(config.jwtAudience)
|
||||
.withIssuer(config.jwtDomain)
|
||||
.withClaim("user_id", userId)
|
||||
.withClaim("user_id", userId.toString())
|
||||
.withClaim("token_type", tokenType)
|
||||
.withExpiresAt(expiresAt)
|
||||
.sign(Algorithm.HMAC256(config.jwtSecret))
|
||||
@@ -238,13 +233,13 @@ fun sendVerificationEmail(config: AppConfig, recipientEmail: String, verificatio
|
||||
val message = MimeMessage(session).apply {
|
||||
setFrom(InternetAddress(config.smtpUser))
|
||||
setRecipient(Message.RecipientType.TO, InternetAddress(recipientEmail))
|
||||
subject = "Welcome to CyberEcho! Verification Code:$verificationCode"
|
||||
subject = "Welcome to BBIT-Lab! Verification Code:$verificationCode"
|
||||
val htmlContent = """
|
||||
<html>
|
||||
<body style="font-family: Arial, sans-serif; color: #333;">
|
||||
<div style="max-width: 600px; margin: 0 auto; padding: 20px; border: 1px solid #ddd; border-radius: 10px; background-color: #f9f9f9;">
|
||||
<h2 style="color: #4CAF50;">Welcome to CyberEcho!</h2>
|
||||
<p style="font-size: 16px;">Thank you for signing up with <strong>CyberEcho</strong>! We are excited to have you on board.</p>
|
||||
<h2 style="color: #4CAF50;">Welcome to BBIT-Lab!</h2>
|
||||
<p style="font-size: 16px;">Thank you for signing up with <strong>BBIT-Lab</strong>! We are excited to have you on board.</p>
|
||||
|
||||
<p style="font-size: 16px;">To complete your registration, please use the verification code below:</p>
|
||||
|
||||
@@ -258,7 +253,7 @@ fun sendVerificationEmail(config: AppConfig, recipientEmail: String, verificatio
|
||||
<hr style="border-top: 1px solid #ddd; margin-top: 30px;">
|
||||
<p style="font-size: 14px; color: #888; text-align: center;">
|
||||
Best regards<br>
|
||||
<strong>The CyberEcho Account Team</strong>
|
||||
<strong>The BBIT-Lab Account Team</strong>
|
||||
</p>
|
||||
</div>
|
||||
</body>
|
||||
+22
-24
@@ -1,15 +1,15 @@
|
||||
package ink.snowflake.server.route
|
||||
package ink.snowflake.server.controller
|
||||
|
||||
import ink.snowflake.server.SERVER_PATH_OSS
|
||||
import ink.snowflake.server.VIDEO_INPUT_PATH
|
||||
import ink.snowflake.server.database.VideoDao
|
||||
import ink.snowflake.server.database.table.VideoTable
|
||||
import ink.snowflake.server.database.table.VideoTable.vAAverageMaskedRatio
|
||||
import ink.snowflake.server.database.table.VideoTable.vACountPeople
|
||||
import ink.snowflake.server.database.table.VideoTable.vAMaxAction
|
||||
import ink.snowflake.server.database.table.VideoTable.vAMaxStayTime
|
||||
import ink.snowflake.server.database.table.VideoTable.vATotalPeople
|
||||
import ink.snowflake.server.database.table.VideoTable.vStartDateTime
|
||||
import ink.snowflake.server.utils.dao.VideoDao
|
||||
import ink.snowflake.server.model.database.VideosTable
|
||||
import ink.snowflake.server.model.database.VideosTable.vAAverageMaskedRatio
|
||||
import ink.snowflake.server.model.database.VideosTable.vACountPeople
|
||||
import ink.snowflake.server.model.database.VideosTable.vAMaxAction
|
||||
import ink.snowflake.server.model.database.VideosTable.vAMaxStayTime
|
||||
import ink.snowflake.server.model.database.VideosTable.vATotalPeople
|
||||
import ink.snowflake.server.model.database.VideosTable.vStartDateTime
|
||||
import ink.snowflake.server.model.request.VideoAnalyticsRequest
|
||||
import ink.snowflake.server.model.response.*
|
||||
import ink.snowflake.server.utils.WebSocketManager.broadcastMessage
|
||||
@@ -23,8 +23,6 @@ import io.ktor.server.routing.*
|
||||
import io.ktor.server.websocket.*
|
||||
import io.ktor.websocket.*
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.channels.consumeEach
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.datetime.LocalDateTime
|
||||
import kotlinx.datetime.toJavaLocalDateTime
|
||||
@@ -114,12 +112,12 @@ fun Application.VideoAnalytics() {
|
||||
get("/getAnalyticsDetailByVideoId") {
|
||||
// 获取 vId 参数
|
||||
val vIdParam = call.parameters["vId"]
|
||||
val vId = vIdParam?.toIntOrNull() // 将 vId 转换为 Int,确保安全
|
||||
if (vId == null) {
|
||||
call.respond(BaseResponse(status = true, message = "Invalid vId", data = null))
|
||||
if (vIdParam == null) {
|
||||
call.respond(BaseResponse(status = true, message = "缺少参数vId", data = null))
|
||||
return@get
|
||||
}
|
||||
// 查询视频信息
|
||||
val vId = UUID.fromString(vIdParam)
|
||||
val video = VideoDao.getAnalyticsDetailByVideoId(vId)
|
||||
if (video == null) {
|
||||
call.respond(BaseResponse(status = false, message = "不存在该视频", data = null))
|
||||
@@ -216,17 +214,17 @@ fun Application.VideoAnalytics() {
|
||||
BaseResponse(
|
||||
data = VideoAnalyticsDetail(
|
||||
v_id = vId,
|
||||
v_name = video[VideoTable.vName],
|
||||
v_video_play_path = "http://${SERVER_PATH_OSS}:9000/video/" + video[VideoTable.vObjectName],
|
||||
v_file_name = video[VideoTable.vFileName],
|
||||
v_duration = video[VideoTable.vDuration],
|
||||
v_size = video[VideoTable.vSize],
|
||||
v_name = video[VideosTable.vName],
|
||||
v_video_play_path = "http://${SERVER_PATH_OSS}:9000/video/" + video[VideosTable.vObjectName],
|
||||
v_file_name = video[VideosTable.vFileName],
|
||||
v_duration = video[VideosTable.vDuration],
|
||||
v_size = video[VideosTable.vSize],
|
||||
v_start_datetime = video[vStartDateTime]?.toString() ?: "",
|
||||
v_video_codec = video[VideoTable.vVideoCodec],
|
||||
v_audio_codec = video[VideoTable.vAudioCodec],
|
||||
v_overall_bit_rate = video[VideoTable.vOverallBitRate],
|
||||
v_resolution = video[VideoTable.vResolution],
|
||||
v_a_time = video[VideoTable.vATime].toString(),
|
||||
v_video_codec = video[VideosTable.vVideoCodec],
|
||||
v_audio_codec = video[VideosTable.vAudioCodec],
|
||||
v_overall_bit_rate = video[VideosTable.vOverallBitRate],
|
||||
v_resolution = video[VideosTable.vResolution],
|
||||
v_a_time = video[VideosTable.vATime].toString(),
|
||||
v_a_total_people = video[vATotalPeople], // 总人数
|
||||
v_a_count_people = video[vACountPeople], // 佩戴口罩人数
|
||||
v_a_max_stay_time = video[vAMaxStayTime], // 最大停留时间
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package ink.snowflake.server.route
|
||||
package ink.snowflake.server.controller
|
||||
|
||||
import ink.snowflake.server.model.response.*
|
||||
import io.ktor.server.application.*
|
||||
@@ -1,51 +0,0 @@
|
||||
package ink.snowflake.server.database
|
||||
|
||||
import ink.snowflake.server.database.table.ChatRecord
|
||||
import ink.snowflake.server.database.table.ChatRecordsTable
|
||||
import ink.snowflake.server.database.table.toChatRecord
|
||||
import kotlinx.datetime.Clock
|
||||
import org.jetbrains.exposed.v1.core.SortOrder
|
||||
import org.jetbrains.exposed.v1.datetime.timestampLiteral
|
||||
import org.jetbrains.exposed.v1.jdbc.insertAndGetId
|
||||
import org.jetbrains.exposed.v1.jdbc.selectAll
|
||||
import org.jetbrains.exposed.v1.jdbc.transactions.transaction
|
||||
|
||||
object ChatRecordsDao {
|
||||
/**
|
||||
* 插入聊天记录
|
||||
*/
|
||||
fun insertChatRecord(
|
||||
userId: Int,
|
||||
message: String,
|
||||
messageType: String,
|
||||
sendAt: Long,
|
||||
from: String,
|
||||
emotion: String?
|
||||
): Boolean {
|
||||
return transaction {
|
||||
ChatRecordsTable
|
||||
.insertAndGetId {
|
||||
it[ChatRecordsTable.userId] = userId
|
||||
it[ChatRecordsTable.message] = message
|
||||
it[ChatRecordsTable.messageType] = messageType
|
||||
it[ChatRecordsTable.sentAt] = timestampLiteral(Clock.System.now())
|
||||
it[ChatRecordsTable.from] = from
|
||||
it[ChatRecordsTable.emotion] = emotion
|
||||
}.value > 0
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 取最近五条聊天记录
|
||||
*/
|
||||
fun getRecentChatRecords(userId: Int): List<ChatRecord> {
|
||||
return transaction {
|
||||
ChatRecordsTable
|
||||
.selectAll()
|
||||
.where { ChatRecordsTable.userId eq userId }
|
||||
.orderBy(ChatRecordsTable.sentAt to SortOrder.DESC)
|
||||
.limit(5)
|
||||
.toList().map { it.toChatRecord() }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
package ink.snowflake.server.database
|
||||
|
||||
import ink.snowflake.server.database.table.UserTable
|
||||
import ink.snowflake.server.model.response.UserInfo
|
||||
import kotlinx.datetime.Clock
|
||||
import org.jetbrains.exposed.v1.core.SqlExpressionBuilder.eq
|
||||
import org.jetbrains.exposed.v1.datetime.timestampLiteral
|
||||
import org.jetbrains.exposed.v1.jdbc.insertAndGetId
|
||||
import org.jetbrains.exposed.v1.jdbc.selectAll
|
||||
import org.jetbrains.exposed.v1.jdbc.transactions.transaction
|
||||
|
||||
|
||||
object UserDAO {
|
||||
/**
|
||||
* 根据 email 获取密码
|
||||
* @return 密码的 SHA-256 哈希值
|
||||
*/
|
||||
fun getPasswordById(id: Int): String? {
|
||||
return transaction {
|
||||
// 查询指定 email 的用户并返回 passwordHash
|
||||
UserTable
|
||||
.selectAll().where { UserTable.id eq id }
|
||||
.map { it[UserTable.passwordHash] }
|
||||
.singleOrNull() // 如果没有找到用户,返回 null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 email 获取用户 ID
|
||||
* @return 用户的 ID
|
||||
*/
|
||||
fun getUserIdByEmail(email: String): Int? {
|
||||
return transaction {
|
||||
// 查询指定 email 的用户并返回 id
|
||||
UserTable
|
||||
.selectAll().where { UserTable.email eq email }
|
||||
.map { it[UserTable.id].value }
|
||||
.singleOrNull() // 如果没有找到用户,返回 null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加用户
|
||||
* @return 如果添加成功,返回 1;如果用户已存在,返回 0;如果添加失败,返回 -1
|
||||
*/
|
||||
fun registerByEmailAndGetId(account: String, hashPassword: String): Int {
|
||||
return transaction {
|
||||
try {
|
||||
val newUserId = UserTable
|
||||
.insertAndGetId {
|
||||
it[email] = account
|
||||
it[passwordHash] = hashPassword
|
||||
it[createdAt] = timestampLiteral(Clock.System.now())
|
||||
it[updatedAt] = timestampLiteral(Clock.System.now())
|
||||
}
|
||||
// 如果插入成功,返回 userId
|
||||
if (newUserId.value > 0) {
|
||||
newUserId.value
|
||||
} else {
|
||||
// 插入失败,返回 -1
|
||||
-1
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
// 如果发生异常(例如数据库连接问题),返回 -1
|
||||
e.printStackTrace() // 打印异常堆栈
|
||||
-1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getUserInfoByUserId(userId: Int?): UserInfo? {
|
||||
if (userId == null) return null
|
||||
|
||||
return transaction {
|
||||
UserTable
|
||||
.selectAll().where { UserTable.id eq userId }
|
||||
.map {
|
||||
UserInfo(
|
||||
username = it[UserTable.username],
|
||||
email = it[UserTable.email],
|
||||
phone = it[UserTable.phone],
|
||||
roles = it[UserTable.roles]?.removePrefix("{")!!.removeSuffix("}").split(","),
|
||||
)
|
||||
}
|
||||
.singleOrNull()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package ink.snowflake.server.database.table
|
||||
|
||||
import org.jetbrains.exposed.v1.core.dao.id.IntIdTable
|
||||
|
||||
|
||||
object VideoAnalyticsDetailTable : IntIdTable("video_analytics_detail") {
|
||||
val vId = integer("v_id").references(VideoTable.id)
|
||||
val aTimeStamp = integer("a_time_stamp")
|
||||
val aTotalPeople = integer("a_total_people")
|
||||
val aTotalPeopleMasked = integer("a_total_people_masked")
|
||||
val aAction = varchar("a_action", 50)
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package ink.snowflake.server.database.table
|
||||
|
||||
import org.jetbrains.exposed.v1.core.dao.id.IntIdTable
|
||||
import org.jetbrains.exposed.v1.datetime.datetime
|
||||
|
||||
|
||||
object VideoTable : IntIdTable("video") {
|
||||
val vName = varchar("v_name", 255)
|
||||
val vObjectName = varchar("v_object_name", 255)
|
||||
val vFileName = varchar("v_file_name", 255)
|
||||
val vStartDateTime = datetime("v_start_datetime").nullable()
|
||||
val vDuration = float("v_duration")
|
||||
val vSize = float("v_size")
|
||||
val vVideoCodec = varchar("v_video_codec", 50)
|
||||
val vAudioCodec = varchar("v_audio_codec", 50)
|
||||
val vOverallBitRate = integer("v_overall_bit_rate")
|
||||
val vResolution = varchar("v_resolution", 20)
|
||||
val vATime = datetime("v_a_time")
|
||||
val vATotalPeople = integer("v_a_total_people")
|
||||
val vACountPeople = integer("v_a_count_people")
|
||||
val vAMaxStayTime = float("v_a_max_stay_time")
|
||||
val vAMaxAction = varchar("v_a_max_action", 50)
|
||||
val vAAverageMaskedRatio = float("v_a_average_masked_ratio")
|
||||
}
|
||||
+7
-3
@@ -1,15 +1,18 @@
|
||||
package ink.snowflake.server.database.table
|
||||
package ink.snowflake.server.model.database
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import ink.snowflake.server.gson
|
||||
import kotlinx.serialization.Contextual
|
||||
import org.jetbrains.exposed.v1.core.ResultRow
|
||||
import org.jetbrains.exposed.v1.core.dao.id.IntIdTable
|
||||
import org.jetbrains.exposed.v1.core.dao.id.UUIDTable
|
||||
import org.jetbrains.exposed.v1.datetime.timestamp
|
||||
import org.jetbrains.exposed.v1.json.json
|
||||
import java.util.UUID
|
||||
|
||||
// 定义 AI 表
|
||||
object AIProfilesTable : IntIdTable("ai_profiles") {
|
||||
object AIProfilesTable : UUIDTable("ai_profiles") {
|
||||
val name = varchar("name", 100) // AI 名称
|
||||
val avatarUrl = varchar("avatar_url", 255).nullable() // AI 头像 URL
|
||||
val aiPersonality = json("ai_personality", serialize = { gson.toJson(it) }, deserialize = {
|
||||
@@ -22,7 +25,8 @@ object AIProfilesTable : IntIdTable("ai_profiles") {
|
||||
|
||||
@Serializable
|
||||
data class AIProfile(
|
||||
val id: Int,
|
||||
@Contextual
|
||||
val id: UUID,
|
||||
val name: String,
|
||||
val avatarUrl: String?,
|
||||
val aiPersonality: Map<String, String>,
|
||||
+8
-6
@@ -1,13 +1,15 @@
|
||||
package ink.snowflake.server.database.table
|
||||
package ink.snowflake.server.model.database
|
||||
|
||||
import org.jetbrains.exposed.v1.core.Column
|
||||
import org.jetbrains.exposed.v1.core.ResultRow
|
||||
import org.jetbrains.exposed.v1.core.dao.id.IntIdTable
|
||||
import org.jetbrains.exposed.v1.core.dao.id.UUIDTable
|
||||
import org.jetbrains.exposed.v1.datetime.timestamp
|
||||
import java.util.UUID
|
||||
import kotlin.uuid.Uuid
|
||||
|
||||
object ChatRecordsTable : IntIdTable("chat_records") {
|
||||
object ChatRecordsTable : UUIDTable("chat_records") {
|
||||
// 用户 ID(外键)
|
||||
val userId: Column<Int> = integer("user_id").references(UserTable.id)
|
||||
val userId: Column<UUID> = uuid("user_id").references(UsersTable.id)
|
||||
|
||||
// 消息内容
|
||||
val message: Column<String> = text("message")
|
||||
@@ -26,8 +28,8 @@ object ChatRecordsTable : IntIdTable("chat_records") {
|
||||
}
|
||||
|
||||
data class ChatRecord(
|
||||
val id: Int,
|
||||
val userId: Int,
|
||||
val id: UUID,
|
||||
val userId: UUID,
|
||||
val message: String,
|
||||
val messageType: String,
|
||||
val sentAt: String,// eg. "2024-12-10T06:19:04.433006Z"
|
||||
+3
-3
@@ -1,12 +1,12 @@
|
||||
package ink.snowflake.server.database.table
|
||||
package ink.snowflake.server.model.database
|
||||
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import ink.snowflake.server.gson
|
||||
import org.jetbrains.exposed.v1.core.dao.id.IntIdTable
|
||||
import org.jetbrains.exposed.v1.core.dao.id.UUIDTable
|
||||
import org.jetbrains.exposed.v1.datetime.datetime
|
||||
import org.jetbrains.exposed.v1.json.json
|
||||
|
||||
object ImageTable : IntIdTable("image_sca") {
|
||||
object ScaImagesTable : UUIDTable("sca_images") {
|
||||
val name = varchar("name", 255)
|
||||
val upload_datetime = datetime("upload_datetime")
|
||||
val file_name = varchar("file_name", 255)
|
||||
+3
-3
@@ -1,9 +1,9 @@
|
||||
package ink.snowflake.server.database.table
|
||||
package ink.snowflake.server.model.database
|
||||
|
||||
import org.jetbrains.exposed.v1.core.dao.id.IntIdTable
|
||||
import org.jetbrains.exposed.v1.core.dao.id.UUIDTable
|
||||
import org.jetbrains.exposed.v1.datetime.timestamp
|
||||
|
||||
object UserTable : IntIdTable("users") {
|
||||
object UsersTable : UUIDTable("users") {
|
||||
// 用户名
|
||||
val username = varchar("username", length = 50).nullable()
|
||||
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package ink.snowflake.server.model.database
|
||||
|
||||
import org.jetbrains.exposed.v1.core.dao.id.IntIdTable
|
||||
import org.jetbrains.exposed.v1.core.dao.id.UUIDTable
|
||||
|
||||
|
||||
object VideoAnalyticsDetailsTable : UUIDTable("video_analytics_details") {
|
||||
val vId = uuid("v_id").references(VideosTable.id)
|
||||
val aTimeStamp = integer("time_stamp")
|
||||
val aTotalPeople = integer("total_people")
|
||||
val aTotalPeopleMasked = integer("total_people_masked")
|
||||
val aAction = varchar("action", 50)
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package ink.snowflake.server.model.database
|
||||
|
||||
import org.jetbrains.exposed.v1.core.dao.id.IntIdTable
|
||||
import org.jetbrains.exposed.v1.core.dao.id.UUIDTable
|
||||
import org.jetbrains.exposed.v1.datetime.datetime
|
||||
|
||||
|
||||
object VideosTable : UUIDTable("videos") {
|
||||
val vName = varchar("name", 255)
|
||||
val vObjectName = varchar("object_name", 255)
|
||||
val vFileName = varchar("file_name", 255)
|
||||
val vStartDateTime = datetime("start_datetime").nullable()
|
||||
val vDuration = float("duration")
|
||||
val vSize = float("size")
|
||||
val vVideoCodec = varchar("video_codec", 50)
|
||||
val vAudioCodec = varchar("audio_codec", 50)
|
||||
val vOverallBitRate = integer("overall_bit_rate")
|
||||
val vResolution = varchar("resolution", 20)
|
||||
val vATime = datetime("a_time")
|
||||
val vATotalPeople = integer("a_total_people")
|
||||
val vACountPeople = integer("a_count_people")
|
||||
val vAMaxStayTime = float("a_max_stay_time")
|
||||
val vAMaxAction = varchar("a_max_action", 50)
|
||||
val vAAverageMaskedRatio = float("a_average_masked_ratio")
|
||||
}
|
||||
+1
-1
@@ -2,7 +2,7 @@ package ink.snowflake.server.model.request
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class DevicesInfo(
|
||||
data class DevicesInfoRequest(
|
||||
@SerializedName("proxies")
|
||||
val proxies: List<Proxy> = listOf()
|
||||
) {
|
||||
@@ -1,10 +1,13 @@
|
||||
package ink.snowflake.server.model.request
|
||||
|
||||
import kotlinx.serialization.Contextual
|
||||
import kotlinx.serialization.Serializable
|
||||
import java.util.UUID
|
||||
|
||||
@Serializable
|
||||
data class ImageAnalyticsRequest(
|
||||
val id : Int,
|
||||
@Contextual
|
||||
val id : UUID,
|
||||
val name : String,
|
||||
val upload_datetime: String, // 上传时间
|
||||
val file_name: String, // 文件名
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package ink.snowflake.server.model.request
|
||||
|
||||
import ink.snowflake.server.model.response.Token
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
|
||||
+5
-2
@@ -1,10 +1,13 @@
|
||||
package ink.snowflake.server.model.response
|
||||
|
||||
import kotlinx.serialization.Contextual
|
||||
import kotlinx.serialization.Serializable
|
||||
import java.util.UUID
|
||||
|
||||
@Serializable
|
||||
data class AiListForUser(
|
||||
val aiId :Int,
|
||||
data class AiListForUserResponse(
|
||||
@Contextual
|
||||
val aiId: UUID,
|
||||
val aiName :String,
|
||||
val aiAvatar : String
|
||||
)
|
||||
+1
-1
@@ -3,7 +3,7 @@ package ink.snowflake.server.model.response
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class DeviceItem (
|
||||
data class DeviceItemResponse (
|
||||
val deviceName:String,
|
||||
val devicePort:Int,
|
||||
)
|
||||
@@ -1,8 +1,12 @@
|
||||
package ink.snowflake.server.model.response
|
||||
|
||||
import kotlinx.serialization.Contextual
|
||||
import kotlinx.serialization.Serializable
|
||||
import java.util.UUID
|
||||
|
||||
|
||||
@Serializable
|
||||
data class LoginResponse(val userId: String, val accessToken: Token, val refreshToken: Token)
|
||||
data class LoginResponse(
|
||||
@Contextual
|
||||
val userId: UUID, val accessToken: Token, val refreshToken: Token)
|
||||
|
||||
|
||||
@@ -3,4 +3,4 @@ package ink.snowflake.server.model.response
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class Token(val token: String, val expiresAt: Long = 0,val expiresStr:String)
|
||||
data class Token(val token: String, val expiresAt: Long = 0, val expiresStr:String)
|
||||
+1
-2
@@ -1,10 +1,9 @@
|
||||
package ink.snowflake.server.model.response
|
||||
|
||||
import kotlinx.datetime.LocalDateTime
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class UserInfo(
|
||||
data class UserInfoResponse(
|
||||
val username: String?,
|
||||
val email: String?,
|
||||
val phone: String?,
|
||||
+4
-1
@@ -1,10 +1,13 @@
|
||||
package ink.snowflake.server.model.response
|
||||
|
||||
import kotlinx.serialization.Contextual
|
||||
import kotlinx.serialization.Serializable
|
||||
import java.util.UUID
|
||||
|
||||
@Serializable
|
||||
data class VideoAnalyticsDetail(
|
||||
val v_id: Int,
|
||||
@Contextual
|
||||
val v_id: UUID,
|
||||
val v_name: String,
|
||||
val v_video_play_path: String,
|
||||
val v_file_name: String,
|
||||
@@ -1,10 +1,13 @@
|
||||
package ink.snowflake.server.model.response
|
||||
|
||||
import kotlinx.serialization.Contextual
|
||||
import kotlinx.serialization.Serializable
|
||||
import java.util.UUID
|
||||
|
||||
@Serializable
|
||||
data class VideoListResponse(
|
||||
val v_id: Int,
|
||||
@Contextual
|
||||
val v_id: UUID,
|
||||
val v_name: String,
|
||||
val v_a_time: String?
|
||||
)
|
||||
+3
-1
@@ -1,9 +1,11 @@
|
||||
package com.cyberecho.mdoel.database
|
||||
|
||||
import java.util.UUID
|
||||
|
||||
|
||||
data class WSChatRecords(
|
||||
val id: Long = 0, // 消息的唯一标识符,自增
|
||||
val aiId: Int, // 交流的对象AI_ID
|
||||
val aiId: UUID, // 交流的对象AI_ID
|
||||
val messageType: String,// 消息类型(文本、图片、语音等)
|
||||
val content: String, // 消息内容
|
||||
val timestamp: Long, // 发送时间(时间戳,单位为毫秒)
|
||||
@@ -1,20 +0,0 @@
|
||||
package ink.snowflake.server.plugins
|
||||
|
||||
import io.ktor.http.*
|
||||
import io.ktor.serialization.*
|
||||
import io.ktor.serialization.gson.*
|
||||
import io.ktor.serialization.kotlinx.json.*
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.plugins.contentnegotiation.*
|
||||
import io.ktor.server.request.*
|
||||
import io.ktor.server.response.*
|
||||
import io.ktor.server.routing.*
|
||||
|
||||
fun Application.configureSerialization() {
|
||||
install(ContentNegotiation) {
|
||||
json()
|
||||
gson{
|
||||
setPrettyPrinting()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
package ink.snowflake.server.utils
|
||||
|
||||
import ink.snowflake.server.model.response.UserInfo
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.auth.*
|
||||
import io.ktor.server.auth.jwt.*
|
||||
@@ -8,9 +7,7 @@ import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.datetime.LocalDateTime
|
||||
import kotlinx.datetime.format
|
||||
import kotlinx.datetime.toJavaLocalDateTime
|
||||
import org.jetbrains.exposed.v1.jdbc.transactions.experimental.newSuspendedTransaction
|
||||
import java.io.BufferedReader
|
||||
import java.io.InputStreamReader
|
||||
import java.text.SimpleDateFormat
|
||||
@@ -18,10 +15,9 @@ import java.time.format.DateTimeFormatter
|
||||
import java.util.*
|
||||
|
||||
|
||||
fun getUserIdByToken(call: ApplicationCall) : Int?{
|
||||
fun getUserIdByToken(call: ApplicationCall) : UUID?{
|
||||
// 通过token获取user_id
|
||||
return call.principal<JWTPrincipal>()?.payload?.getClaim("user_id")?.asInt()
|
||||
|
||||
return UUID.fromString(call.principal<JWTPrincipal>()?.payload?.getClaim("user_id")?.asString())
|
||||
}
|
||||
|
||||
fun formatDateToTargetString(date: Date, targetFormat: String): String {
|
||||
|
||||
@@ -7,6 +7,7 @@ import io.ktor.websocket.*
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.UUID
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
object WebSocketManager {
|
||||
@@ -14,17 +15,17 @@ object WebSocketManager {
|
||||
val scope = CoroutineScope(Dispatchers.IO)
|
||||
|
||||
// 存储所有 WebSocket 连接的 Map,key 为 user_id,value 为 WebSocketSession
|
||||
private val connections = ConcurrentHashMap<Int, WebSocketSession>()
|
||||
private val connections = ConcurrentHashMap<UUID, WebSocketSession>()
|
||||
|
||||
|
||||
// 添加 WebSocket 连接
|
||||
fun addConnection(userId: Int, session: WebSocketSession) {
|
||||
fun addConnection(userId: UUID, session: WebSocketSession) {
|
||||
connections[userId] = session
|
||||
println("新连接:$userId")
|
||||
}
|
||||
|
||||
// 移除 WebSocket 连接
|
||||
fun removeConnection(userId: Int) {
|
||||
fun removeConnection(userId: UUID) {
|
||||
connections.remove(userId)
|
||||
println("断开连接:$userId")
|
||||
}
|
||||
@@ -39,7 +40,7 @@ object WebSocketManager {
|
||||
}
|
||||
|
||||
// 向特定用户发送消息
|
||||
suspend fun sendMessageToUser(userId: Int, message: WSChatRecords) {
|
||||
suspend fun sendMessageToUser(userId: UUID, message: WSChatRecords) {
|
||||
connections[userId]?.send(gson.toJson(message))
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -1,8 +1,8 @@
|
||||
package ink.snowflake.server.database
|
||||
package ink.snowflake.server.utils.dao
|
||||
|
||||
import ink.snowflake.server.database.table.AIProfile
|
||||
import ink.snowflake.server.database.table.AIProfilesTable
|
||||
import ink.snowflake.server.database.table.toAIProfile
|
||||
import ink.snowflake.server.model.database.AIProfile
|
||||
import ink.snowflake.server.model.database.AIProfilesTable
|
||||
import ink.snowflake.server.model.database.toAIProfile
|
||||
import org.jetbrains.exposed.v1.jdbc.selectAll
|
||||
import org.jetbrains.exposed.v1.jdbc.transactions.transaction
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
package ink.snowflake.server.utils.dao
|
||||
|
||||
import ink.snowflake.server.model.database.ChatRecord
|
||||
import ink.snowflake.server.model.database.ChatRecordsTable
|
||||
import ink.snowflake.server.model.database.toChatRecord
|
||||
import kotlinx.datetime.Clock
|
||||
import org.jetbrains.exposed.v1.core.SortOrder
|
||||
import org.jetbrains.exposed.v1.datetime.timestampLiteral
|
||||
import org.jetbrains.exposed.v1.exceptions.ExposedSQLException
|
||||
import org.jetbrains.exposed.v1.jdbc.insert
|
||||
import org.jetbrains.exposed.v1.jdbc.insertAndGetId
|
||||
import org.jetbrains.exposed.v1.jdbc.selectAll
|
||||
import org.jetbrains.exposed.v1.jdbc.transactions.transaction
|
||||
import java.util.UUID
|
||||
import kotlin.uuid.Uuid
|
||||
|
||||
object ChatRecordsDao {
|
||||
/**
|
||||
* 插入聊天记录
|
||||
*/
|
||||
fun insertChatRecord(
|
||||
userId: UUID,
|
||||
message: String,
|
||||
messageType: String,
|
||||
sendAt: Long,
|
||||
from: String,
|
||||
emotion: String?
|
||||
): Boolean {
|
||||
return transaction {
|
||||
try {
|
||||
ChatRecordsTable
|
||||
.insert {
|
||||
it[ChatRecordsTable.userId] = userId
|
||||
it[ChatRecordsTable.message] = message
|
||||
it[ChatRecordsTable.messageType] = messageType
|
||||
it[ChatRecordsTable.sentAt] = timestampLiteral(Clock.System.now())
|
||||
it[ChatRecordsTable.from] = from
|
||||
it[ChatRecordsTable.emotion] = emotion
|
||||
}
|
||||
true
|
||||
} catch (e: ExposedSQLException) {
|
||||
e.printStackTrace()
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 取最近五条聊天记录
|
||||
*/
|
||||
fun getRecentChatRecords(userId: UUID): List<ChatRecord> {
|
||||
return transaction {
|
||||
ChatRecordsTable
|
||||
.selectAll()
|
||||
.where { ChatRecordsTable.userId eq userId }
|
||||
.orderBy(ChatRecordsTable.sentAt to SortOrder.DESC)
|
||||
.limit(5)
|
||||
.toList().map { it.toChatRecord() }
|
||||
}
|
||||
}
|
||||
}
|
||||
+20
-20
@@ -1,8 +1,8 @@
|
||||
package ink.snowflake.server.database
|
||||
package ink.snowflake.server.utils.dao
|
||||
|
||||
import ink.snowflake.server.SERVER_PATH_OSS
|
||||
import ink.snowflake.server.model.request.ImageAnalyticsRequest
|
||||
import ink.snowflake.server.database.table.ImageTable
|
||||
import ink.snowflake.server.model.database.ScaImagesTable
|
||||
import ink.snowflake.server.utils.formatLocalDateTimeToString
|
||||
import kotlinx.datetime.toKotlinLocalDateTime
|
||||
import org.jetbrains.exposed.v1.core.SortOrder
|
||||
@@ -15,7 +15,7 @@ object ImageDao {
|
||||
|
||||
fun insertImageAnalyticsData(request: ImageAnalyticsRequest) {
|
||||
return transaction {
|
||||
ImageTable.insert {
|
||||
ScaImagesTable.insert {
|
||||
it[upload_datetime] = Timestamp.valueOf(request.upload_datetime)
|
||||
.toLocalDateTime().toKotlinLocalDateTime()
|
||||
it[file_name] = request.file_name
|
||||
@@ -34,25 +34,25 @@ object ImageDao {
|
||||
|
||||
fun getImageList(name: String): List<ImageAnalyticsRequest> {
|
||||
return transaction {
|
||||
ImageTable.selectAll()
|
||||
.where { ImageTable.name like "%$name%" }
|
||||
.orderBy(ImageTable.upload_datetime, SortOrder.DESC)
|
||||
ScaImagesTable.selectAll()
|
||||
.where { ScaImagesTable.name like "%$name%" }
|
||||
.orderBy(ScaImagesTable.upload_datetime, SortOrder.DESC)
|
||||
.map {
|
||||
ImageAnalyticsRequest(
|
||||
id = it[ImageTable.id].value,
|
||||
name = it[ImageTable.name],
|
||||
upload_datetime = formatLocalDateTimeToString(it[ImageTable.upload_datetime]),
|
||||
file_name = it[ImageTable.file_name],
|
||||
image_pre = "http://${SERVER_PATH_OSS}:9000/image-sca/" + it[ImageTable.image_pre],
|
||||
image_after = "http://${SERVER_PATH_OSS}:9000/image-sca/" + it[ImageTable.image_after],
|
||||
resolution = it[ImageTable.resolution],
|
||||
size = it[ImageTable.size],
|
||||
cocoon_count = it[ImageTable.cocoon_count],
|
||||
max_confidence = it[ImageTable.max_confidence],
|
||||
min_confidence = it[ImageTable.min_confidence],
|
||||
average_confidence = it[ImageTable.average_confidence],
|
||||
other_info = it[ImageTable.other_info] as Map<String, String>,
|
||||
processing_time = formatLocalDateTimeToString(it[ImageTable.processing_time])
|
||||
id = it[ScaImagesTable.id].value,
|
||||
name = it[ScaImagesTable.name],
|
||||
upload_datetime = formatLocalDateTimeToString(it[ScaImagesTable.upload_datetime]),
|
||||
file_name = it[ScaImagesTable.file_name],
|
||||
image_pre = "http://${SERVER_PATH_OSS}:9000/image-sca/" + it[ScaImagesTable.image_pre],
|
||||
image_after = "http://${SERVER_PATH_OSS}:9000/image-sca/" + it[ScaImagesTable.image_after],
|
||||
resolution = it[ScaImagesTable.resolution],
|
||||
size = it[ScaImagesTable.size],
|
||||
cocoon_count = it[ScaImagesTable.cocoon_count],
|
||||
max_confidence = it[ScaImagesTable.max_confidence],
|
||||
min_confidence = it[ScaImagesTable.min_confidence],
|
||||
average_confidence = it[ScaImagesTable.average_confidence],
|
||||
other_info = it[ScaImagesTable.other_info] as Map<String, String>,
|
||||
processing_time = formatLocalDateTimeToString(it[ScaImagesTable.processing_time])
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package ink.snowflake.server.utils.dao
|
||||
|
||||
import ink.snowflake.server.model.database.UsersTable
|
||||
import ink.snowflake.server.model.response.UserInfoResponse
|
||||
import kotlinx.datetime.Clock
|
||||
import org.jetbrains.exposed.v1.datetime.timestampLiteral
|
||||
import org.jetbrains.exposed.v1.jdbc.insertAndGetId
|
||||
import org.jetbrains.exposed.v1.jdbc.selectAll
|
||||
import org.jetbrains.exposed.v1.jdbc.transactions.transaction
|
||||
import java.util.UUID
|
||||
|
||||
|
||||
object UserDAO {
|
||||
/**
|
||||
* 根据 email 获取密码
|
||||
* @return 密码的 SHA-256 哈希值
|
||||
*/
|
||||
fun getPasswordById(id: UUID): String? {
|
||||
return transaction {
|
||||
// 查询指定 email 的用户并返回 passwordHash
|
||||
UsersTable
|
||||
.selectAll().where { UsersTable.id eq id }
|
||||
.map { it[UsersTable.passwordHash] }
|
||||
.singleOrNull() // 如果没有找到用户,返回 null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 email 获取用户 ID
|
||||
* @return 用户的 ID
|
||||
*/
|
||||
fun getUserIdByEmail(email: String): UUID? {
|
||||
return transaction {
|
||||
// 查询指定 email 的用户并返回 id
|
||||
UsersTable
|
||||
.selectAll().where { UsersTable.email eq email }
|
||||
.map { it[UsersTable.id].value }
|
||||
.singleOrNull() // 如果没有找到用户,返回 null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加用户
|
||||
* @return 如果添加成功,返回 1;如果用户已存在,返回 0;如果添加失败,返回 -1
|
||||
*/
|
||||
fun registerByEmailAndGetId(account: String, hashPassword: String): UUID? {
|
||||
return transaction {
|
||||
try {
|
||||
val newUserId = UsersTable
|
||||
.insertAndGetId {
|
||||
it[username] = account
|
||||
it[email] = account
|
||||
it[passwordHash] = hashPassword
|
||||
it[createdAt] = timestampLiteral(Clock.System.now())
|
||||
it[updatedAt] = timestampLiteral(Clock.System.now())
|
||||
}
|
||||
// 如果插入成功,返回 userId
|
||||
newUserId.value
|
||||
} catch (e: Exception) {
|
||||
// 如果发生异常(例如数据库连接问题),返回 -1
|
||||
e.printStackTrace()
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getUserInfoByUserId(userId: UUID?): UserInfoResponse? {
|
||||
if (userId == null) return null
|
||||
return transaction {
|
||||
try {
|
||||
UsersTable
|
||||
.selectAll().where { UsersTable.id eq userId }
|
||||
.map {
|
||||
UserInfoResponse(
|
||||
username = it[UsersTable.username],
|
||||
email = it[UsersTable.email],
|
||||
phone = it[UsersTable.phone],
|
||||
roles = it[UsersTable.roles]?.removePrefix("{")!!.removeSuffix("}").split(","),
|
||||
)
|
||||
}
|
||||
.singleOrNull()
|
||||
} catch (e: Exception) {
|
||||
// 如果发生异常(例如数据库连接问题),返回 -1
|
||||
e.printStackTrace()
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+23
-22
@@ -1,9 +1,9 @@
|
||||
package ink.snowflake.server.database
|
||||
package ink.snowflake.server.utils.dao
|
||||
|
||||
import ink.snowflake.server.database.table.VideoAnalyticsDetailTable
|
||||
import ink.snowflake.server.database.table.VideoTable
|
||||
import ink.snowflake.server.database.table.VideoTable.vATime
|
||||
import ink.snowflake.server.database.table.VideoTable.vName
|
||||
import ink.snowflake.server.model.database.VideoAnalyticsDetailsTable
|
||||
import ink.snowflake.server.model.database.VideosTable
|
||||
import ink.snowflake.server.model.database.VideosTable.vATime
|
||||
import ink.snowflake.server.model.database.VideosTable.vName
|
||||
import ink.snowflake.server.model.request.VideoAnalyticsRequest
|
||||
import ink.snowflake.server.model.request.VideoDetail
|
||||
import ink.snowflake.server.model.response.VideoListResponse
|
||||
@@ -16,13 +16,14 @@ import org.jetbrains.exposed.v1.jdbc.insertAndGetId
|
||||
import org.jetbrains.exposed.v1.jdbc.selectAll
|
||||
import org.jetbrains.exposed.v1.jdbc.transactions.transaction
|
||||
import java.sql.Timestamp
|
||||
import java.util.UUID
|
||||
|
||||
object VideoDao {
|
||||
|
||||
fun insertVideoAnalyticsData(request: VideoAnalyticsRequest) {
|
||||
return transaction {
|
||||
// 插入视频信息
|
||||
val videoId = VideoTable.insertAndGetId {
|
||||
val videoId = VideosTable.insertAndGetId {
|
||||
it[vName] = request.v_name
|
||||
it[vObjectName] = request.v_object_name
|
||||
it[vFileName] = request.v_file_name
|
||||
@@ -43,7 +44,7 @@ object VideoDao {
|
||||
}
|
||||
// 插入视频分析详情
|
||||
request.v_a_details.forEach { detail ->
|
||||
VideoAnalyticsDetailTable.insert {
|
||||
VideoAnalyticsDetailsTable.insert {
|
||||
it[vId] = videoId.value
|
||||
it[aTimeStamp] = detail.a_time_stamp
|
||||
it[aTotalPeople] = detail.a_total_people
|
||||
@@ -56,39 +57,39 @@ object VideoDao {
|
||||
|
||||
fun getVideoList(name: String?): List<VideoListResponse> {
|
||||
return transaction {
|
||||
val query = VideoTable.selectAll()
|
||||
val query = VideosTable.selectAll()
|
||||
// 动态加 where 条件
|
||||
if (!name.isNullOrBlank()) {
|
||||
query.where { VideoTable.vName like "%${name}%" }
|
||||
query.where { vName like "%${name}%" }
|
||||
}
|
||||
query.orderBy(VideoTable.vATime, SortOrder.DESC)
|
||||
query.orderBy(vATime, SortOrder.DESC)
|
||||
.map {
|
||||
VideoListResponse(
|
||||
v_id = it[VideoTable.id].value,
|
||||
v_name = it[VideoTable.vName],
|
||||
v_a_time = formatLocalDateTimeToString(it[VideoTable.vATime])
|
||||
v_id = it[VideosTable.id].value,
|
||||
v_name = it[vName],
|
||||
v_a_time = formatLocalDateTimeToString(it[vATime])
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getAnalyticsDetailByVideoId(vId: Int): ResultRow? {
|
||||
fun getAnalyticsDetailByVideoId(vId: UUID): ResultRow? {
|
||||
return transaction {
|
||||
VideoTable.selectAll().where { VideoTable.id eq vId }.singleOrNull()
|
||||
VideosTable.selectAll().where { VideosTable.id eq vId }.singleOrNull()
|
||||
}
|
||||
}
|
||||
|
||||
fun selectVideoDetailByVid(vId: Int): List<VideoDetail> {
|
||||
fun selectVideoDetailByVid(vId: UUID): List<VideoDetail> {
|
||||
return transaction {
|
||||
VideoAnalyticsDetailTable
|
||||
VideoAnalyticsDetailsTable
|
||||
.selectAll()
|
||||
.where { VideoAnalyticsDetailTable.vId eq vId }
|
||||
.where { VideoAnalyticsDetailsTable.vId eq vId }
|
||||
.map {
|
||||
VideoDetail(
|
||||
a_time_stamp = it[VideoAnalyticsDetailTable.aTimeStamp],
|
||||
a_total_people = it[VideoAnalyticsDetailTable.aTotalPeople],
|
||||
a_total_people_masked = it[VideoAnalyticsDetailTable.aTotalPeopleMasked],
|
||||
a_action = it[VideoAnalyticsDetailTable.aAction]
|
||||
a_time_stamp = it[VideoAnalyticsDetailsTable.aTimeStamp],
|
||||
a_total_people = it[VideoAnalyticsDetailsTable.aTotalPeople],
|
||||
a_total_people_masked = it[VideoAnalyticsDetailsTable.aTotalPeopleMasked],
|
||||
a_action = it[VideoAnalyticsDetailsTable.aAction]
|
||||
)
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
package ink.snowflake.server.plugins
|
||||
package ink.snowflake.server.utils.plugins
|
||||
|
||||
import ink.snowflake.server.utils.AppConfig
|
||||
import io.ktor.http.*
|
||||
@@ -14,7 +14,7 @@ fun Application.configureCORS() {
|
||||
allowHost("localhost:8089")
|
||||
allowHost("127.0.0.1:8089")
|
||||
allowHost("171.212.101.199:8089")
|
||||
allowHost("debug.scaitcn.com:8089")
|
||||
allowHost("s1.ronsunny.cn:8089")
|
||||
|
||||
// 进一步配置 CORS
|
||||
allowMethod(HttpMethod.Get)
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package ink.snowflake.server.plugins
|
||||
package ink.snowflake.server.utils.plugins
|
||||
|
||||
import ink.snowflake.server.utils.AppConfig
|
||||
import org.jetbrains.exposed.v1.jdbc.Database
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package ink.snowflake.server.plugins
|
||||
package ink.snowflake.server.utils.plugins
|
||||
|
||||
import com.auth0.jwt.JWT
|
||||
import com.auth0.jwt.algorithms.Algorithm
|
||||
@@ -0,0 +1,28 @@
|
||||
package ink.snowflake.server.utils.plugins
|
||||
|
||||
import ink.snowflake.server.model.UUIDSerializer
|
||||
import io.ktor.serialization.gson.*
|
||||
import io.ktor.serialization.kotlinx.json.*
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.plugins.contentnegotiation.*
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.modules.SerializersModule
|
||||
import java.util.UUID
|
||||
|
||||
fun Application.configureSerialization() {
|
||||
install(ContentNegotiation) {
|
||||
json(
|
||||
Json {
|
||||
prettyPrint = true
|
||||
serializersModule = module
|
||||
encodeDefaults = true
|
||||
}
|
||||
)
|
||||
gson{
|
||||
setPrettyPrinting()
|
||||
}
|
||||
}
|
||||
}
|
||||
val module = SerializersModule {
|
||||
contextual(UUID::class, UUIDSerializer)
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package ink.snowflake.server.plugins
|
||||
package ink.snowflake.server.utils.plugins
|
||||
|
||||
import ink.snowflake.server.utils.AppConfig
|
||||
import io.ktor.server.application.*
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
package ink.snowflake.server.plugins
|
||||
package ink.snowflake.server.utils.plugins
|
||||
|
||||
import ink.snowflake.server.model.response.BaseResponse
|
||||
import io.ktor.http.*
|
||||
@@ -22,7 +22,7 @@ fun Application.configureStatusPages() {
|
||||
call.respond(
|
||||
HttpStatusCode.InternalServerError, BaseResponse<Nothing>(
|
||||
status = false,
|
||||
message = cause.localizedMessage
|
||||
message = cause.localizedMessage?:"Unknown error"
|
||||
)
|
||||
)
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package ink.snowflake.server.plugins
|
||||
package ink.snowflake.server.utils.plugins
|
||||
|
||||
import io.ktor.http.*
|
||||
import io.ktor.server.application.*
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package ink.snowflake.server.plugins
|
||||
package ink.snowflake.server.utils.plugins
|
||||
|
||||
import io.ktor.serialization.gson.*
|
||||
import io.ktor.server.application.*
|
||||
Reference in New Issue
Block a user