备份代码
This commit is contained in:
parent
27e398ac73
commit
15ddb83d2d
|
@ -33,16 +33,28 @@ dependencies {
|
||||||
implementation("io.ktor:ktor-serialization-gson-jvm")
|
implementation("io.ktor:ktor-serialization-gson-jvm")
|
||||||
implementation("io.ktor:ktor-server-freemarker-jvm")
|
implementation("io.ktor:ktor-server-freemarker-jvm")
|
||||||
implementation("io.ktor:ktor-server-netty-jvm")
|
implementation("io.ktor:ktor-server-netty-jvm")
|
||||||
|
implementation("io.insert-koin:koin-ktor:3.2.0")
|
||||||
|
implementation("io.insert-koin:koin-core:3.2.0")
|
||||||
implementation("ch.qos.logback:logback-classic:$logback_version")
|
implementation("ch.qos.logback:logback-classic:$logback_version")
|
||||||
|
|
||||||
implementation("io.ktor:ktor-server-config-yaml:2.3.10")
|
implementation("io.ktor:ktor-server-config-yaml:2.3.10")
|
||||||
// Exposed
|
// Exposed
|
||||||
implementation("org.jetbrains.exposed:exposed-core:$exposed_version")
|
implementation("org.jetbrains.exposed:exposed-core:$exposed_version")
|
||||||
implementation("org.jetbrains.exposed:exposed-dao:$exposed_version")
|
implementation("org.jetbrains.exposed:exposed-dao:$exposed_version")
|
||||||
|
implementation("org.jetbrains.exposed:exposed-crypt:$exposed_version")
|
||||||
implementation("org.jetbrains.exposed:exposed-jdbc:$exposed_version")
|
implementation("org.jetbrains.exposed:exposed-jdbc:$exposed_version")
|
||||||
implementation("org.jetbrains.exposed:exposed-java-time:$exposed_version")
|
implementation("org.jetbrains.exposed:exposed-java-time:$exposed_version")
|
||||||
|
implementation("org.jetbrains.exposed:exposed-json:$exposed_version")
|
||||||
|
implementation("org.jetbrains.exposed:exposed-money:$exposed_version")
|
||||||
implementation("com.h2database:h2:$h2_version")
|
implementation("com.h2database:h2:$h2_version")
|
||||||
|
|
||||||
|
//aliyunOss
|
||||||
|
implementation("com.aliyun.oss:aliyun-sdk-oss:3.17.4")
|
||||||
|
implementation("javax.xml.bind:jaxb-api:2.3.1")
|
||||||
|
implementation("javax.activation:activation:1.1.1")
|
||||||
|
implementation("org.glassfish.jaxb:jaxb-runtime:2.3.3")
|
||||||
|
|
||||||
|
|
||||||
//pool
|
//pool
|
||||||
implementation("com.zaxxer:HikariCP:$hikaricp_version")
|
implementation("com.zaxxer:HikariCP:$hikaricp_version")
|
||||||
implementation("org.ehcache:ehcache:$ehcache_version")
|
implementation("org.ehcache:ehcache:$ehcache_version")
|
||||||
|
|
|
@ -2,7 +2,7 @@ ktor_version=2.3.10
|
||||||
kotlin_version=1.9.23
|
kotlin_version=1.9.23
|
||||||
logback_version=1.4.14
|
logback_version=1.4.14
|
||||||
kotlin.code.style=official
|
kotlin.code.style=official
|
||||||
exposed_version = 0.41.1
|
exposed_version = 0.50.1
|
||||||
h2_version = 2.2.224
|
h2_version = 2.2.224
|
||||||
hikaricp_version = 5.1.0
|
hikaricp_version = 5.1.0
|
||||||
ehcache_version = 3.10.8
|
ehcache_version = 3.10.8
|
||||||
|
|
|
@ -1,34 +1,34 @@
|
||||||
package echo.org
|
package echo.org
|
||||||
|
|
||||||
import com.zaxxer.hikari.HikariDataSource
|
import echo.org.application.route.DocumentModule
|
||||||
|
import echo.org.instructure.AliyunOss
|
||||||
import echo.org.instructure.DatabaseSingleton
|
import echo.org.instructure.DatabaseSingleton
|
||||||
import echo.org.plugins.configureRouting
|
import echo.org.plugins.configureRouting
|
||||||
import echo.org.plugins.configureSecurity
|
import echo.org.plugins.configureSecurity
|
||||||
import echo.org.plugins.configureSerialization
|
import echo.org.plugins.configureSerialization
|
||||||
import echo.org.plugins.configureTemplating
|
import echo.org.plugins.configureTemplating
|
||||||
import io.ktor.server.application.*
|
import io.ktor.server.application.*
|
||||||
import org.jetbrains.exposed.sql.Database
|
import org.koin.ktor.ext.inject
|
||||||
|
import org.koin.ktor.plugin.Koin
|
||||||
|
|
||||||
fun main(args: Array<String>) {
|
fun main(args: Array<String>) {
|
||||||
io.ktor.server.netty.EngineMain.main(args)
|
io.ktor.server.netty.EngineMain.main(args)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Application.module() {
|
fun Application.module() {
|
||||||
|
// Initialize Koin
|
||||||
|
install(Koin) {
|
||||||
|
modules(DocumentModule)
|
||||||
|
}
|
||||||
|
AliyunOss.init()
|
||||||
configureSecurity()
|
configureSecurity()
|
||||||
configureSerialization()
|
configureSerialization()
|
||||||
configureTemplating()
|
|
||||||
configureRouting()
|
configureRouting()
|
||||||
|
configureTemplating()
|
||||||
initDatabase()
|
initDatabase()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Application.initDatabase() {
|
fun Application.initDatabase() {
|
||||||
val config = environment.config.config("storage")
|
DatabaseSingleton.init(environment.config)
|
||||||
val dateSourceConfig = DatabaseSingleton.createHikariDataSource(
|
|
||||||
url = config.property("jdbcURL").getString(),
|
|
||||||
driver = config.property("driverClassName").getString(),
|
|
||||||
usernameInput = config.property("username").getString(),
|
|
||||||
passwordInput = config.property("password").getString()
|
|
||||||
)
|
|
||||||
val dataSource = HikariDataSource(dateSourceConfig)
|
|
||||||
Database.connect(dataSource)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,100 @@
|
||||||
|
package echo.org.application.route
|
||||||
|
|
||||||
|
import echo.org.domain.*
|
||||||
|
import echo.org.instructure.AliyunOssFileServiceImpl
|
||||||
|
import echo.org.instructure.DocumentRepositoryImpl
|
||||||
|
import io.ktor.http.*
|
||||||
|
import io.ktor.http.content.*
|
||||||
|
import io.ktor.server.application.*
|
||||||
|
import io.ktor.server.freemarker.*
|
||||||
|
import io.ktor.server.request.*
|
||||||
|
import io.ktor.server.response.*
|
||||||
|
import io.ktor.server.routing.*
|
||||||
|
import org.koin.dsl.module
|
||||||
|
import org.koin.ktor.ext.inject
|
||||||
|
import java.io.File
|
||||||
|
import java.net.URLEncoder
|
||||||
|
import java.nio.charset.StandardCharsets
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
|
val DocumentModule = module {
|
||||||
|
single<DocumentRepository> { DocumentRepositoryImpl() }
|
||||||
|
single<FileOSS> { AliyunOssFileServiceImpl() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Route.handleDocuments() {
|
||||||
|
|
||||||
|
val documentRepository by inject<DocumentRepository>()
|
||||||
|
val fileOSS by inject<FileOSS>()
|
||||||
|
|
||||||
|
route("/documents") {
|
||||||
|
get("") {
|
||||||
|
val res = documentRepository.pageQuery(DocumentQueryParam())
|
||||||
|
call.respond(FreeMarkerContent("index.ftl", res.results, "e"))
|
||||||
|
}
|
||||||
|
post("/add") {
|
||||||
|
// 添加一个文件
|
||||||
|
val multipart = call.receiveMultipart()
|
||||||
|
var fileSize: String?
|
||||||
|
var fileType: String?
|
||||||
|
var uploadName: String?
|
||||||
|
multipart.forEachPart { part ->
|
||||||
|
when (part) {
|
||||||
|
is PartData.FileItem -> {
|
||||||
|
val fileName = part.originalFileName as String
|
||||||
|
fileType = fileName.split(".").last()
|
||||||
|
uploadName = System.currentTimeMillis().toString() + "." + fileType
|
||||||
|
val file = File(uploadName!!)
|
||||||
|
part.streamProvider()
|
||||||
|
.use { its ->
|
||||||
|
file.outputStream().buffered()
|
||||||
|
.use { its.copyTo(it) }
|
||||||
|
}
|
||||||
|
fileSize = file.length().toString()
|
||||||
|
if (fileOSS.upload(file)) {
|
||||||
|
file.delete()
|
||||||
|
documentRepository.create(
|
||||||
|
DocumentVO(
|
||||||
|
fileName, "someAuthorId",
|
||||||
|
LocalDateTime.now(), LocalDateTime.now(),
|
||||||
|
fileType ?: "", uploadName ?: "",
|
||||||
|
fileSize ?: "", ""
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
part.dispose()
|
||||||
|
}
|
||||||
|
call.respond(Result.success(null))
|
||||||
|
}
|
||||||
|
get("/download") {
|
||||||
|
// 获取下载地址
|
||||||
|
val documentQueryParam = DocumentQueryParam()
|
||||||
|
documentQueryParam.id = 1
|
||||||
|
documentQueryParam.id?.let {
|
||||||
|
documentRepository.findById(it)?.let { document ->
|
||||||
|
val file = fileOSS.download(document.uploadName)
|
||||||
|
if (file.exists()) {
|
||||||
|
val encodedFileName = URLEncoder.encode(document.title, StandardCharsets.UTF_8.toString())
|
||||||
|
call.response.header(
|
||||||
|
HttpHeaders.ContentDisposition,
|
||||||
|
"attachment; filename*=UTF-8''$encodedFileName"
|
||||||
|
)
|
||||||
|
call.response.header(HttpHeaders.ContentType, document.fileType)
|
||||||
|
call.respondFile(file)
|
||||||
|
} else {
|
||||||
|
call.respond(HttpStatusCode.NotFound, mapOf("error" to "File not found"))
|
||||||
|
}
|
||||||
|
} ?: call.respond(HttpStatusCode.NotFound, mapOf("error" to "Document not found"))
|
||||||
|
} ?: call.respond(HttpStatusCode.BadRequest, mapOf("error" to "Missing or invalid document ID"))
|
||||||
|
}
|
||||||
|
post("/query") {
|
||||||
|
val documentQueryParam = call.receive<DocumentQueryParam>()
|
||||||
|
val pageQuery = documentRepository.pageQuery(documentQueryParam)
|
||||||
|
call.respond(Result.success(pageQuery))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,23 +6,40 @@ import org.jetbrains.exposed.dao.LongEntityClass
|
||||||
import org.jetbrains.exposed.dao.id.EntityID
|
import org.jetbrains.exposed.dao.id.EntityID
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
class Document(id: EntityID<Long>) : LongEntity(id) {
|
|
||||||
companion object : LongEntityClass<Document>(Documents)
|
data class Document(
|
||||||
var title: String by Documents.title
|
val id: Long,
|
||||||
var content: String by Documents.content
|
val title: String,
|
||||||
var authorId: String by Documents.authorId
|
val uploadName: String,
|
||||||
var createdAt: LocalDateTime by Documents.createdAt
|
val fileType: String,
|
||||||
var updatedAt: LocalDateTime by Documents.updatedAt
|
val authorId: String,
|
||||||
var fileSize: String by Documents.fileSize
|
val createdAt: LocalDateTime,
|
||||||
}
|
val updatedAt: LocalDateTime,
|
||||||
|
val fileSize: String,
|
||||||
|
val filePath: String
|
||||||
|
)
|
||||||
|
|
||||||
|
data class DocumentVO(
|
||||||
|
val title: String, val authorId: String,
|
||||||
|
val updatedAt: LocalDateTime,
|
||||||
|
val createdAt: LocalDateTime,
|
||||||
|
val fileType: String,
|
||||||
|
val uploadName: String,
|
||||||
|
val fileSize: String,
|
||||||
|
val filePath: String
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
interface DocumentRepository {
|
interface DocumentRepository {
|
||||||
suspend fun save(document: Document): Document?
|
suspend fun create(documentVO: DocumentVO): Document?
|
||||||
|
suspend fun edit(document: Document): Document?
|
||||||
suspend fun findById(id: Long): Document?
|
suspend fun findById(id: Long): Document?
|
||||||
suspend fun deleteById(id: Long): Boolean
|
suspend fun deleteById(id: Long): Boolean
|
||||||
suspend fun pageQuery(documentQueryParam: DocumentQueryParam): PageResult<Document>
|
suspend fun pageQuery(documentQueryParam: DocumentQueryParam): PageResult<Document>
|
||||||
}
|
}
|
||||||
|
|
||||||
data class DocumentQueryParam(var title: String? = null,
|
data class DocumentQueryParam(
|
||||||
var authorId: String? = null) : BasePageQueryParm()
|
var id: Long? = null,
|
||||||
|
var title: String? = null,
|
||||||
|
var authorId: String? = null
|
||||||
|
) : BasePageQueryParm()
|
|
@ -0,0 +1,9 @@
|
||||||
|
package echo.org.domain
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
interface FileOSS {
|
||||||
|
fun upload(file: File): Boolean
|
||||||
|
fun download(fileName: String): File
|
||||||
|
fun delete(fileName: String): Boolean
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
package echo.org.instructure
|
||||||
|
|
||||||
|
import com.aliyun.oss.OSS
|
||||||
|
import com.aliyun.oss.OSSClientBuilder
|
||||||
|
import com.aliyun.oss.common.auth.CredentialsProvider
|
||||||
|
import com.aliyun.oss.common.auth.CredentialsProviderFactory
|
||||||
|
import com.aliyun.oss.common.auth.DefaultCredentialProvider
|
||||||
|
import com.aliyun.oss.common.auth.EnvironmentVariableCredentialsProvider
|
||||||
|
import com.aliyun.oss.model.Bucket
|
||||||
|
import echo.org.domain.FileOSS
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileOutputStream
|
||||||
|
|
||||||
|
|
||||||
|
//获取本地环境的oss访问凭证信息
|
||||||
|
fun credentialsProvider(): EnvironmentVariableCredentialsProvider =
|
||||||
|
CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider()
|
||||||
|
|
||||||
|
object AliyunOss {
|
||||||
|
|
||||||
|
private lateinit var ossClient: OSS
|
||||||
|
|
||||||
|
private const val ENDPOINT = "https://oss-cn-hangzhou.aliyuncs.com"
|
||||||
|
|
||||||
|
private const val BUCKET_NAME = "echolaw"
|
||||||
|
|
||||||
|
fun init(): OSS {
|
||||||
|
val accessKeyId = System.getenv("OSS_ACCESS_KEY_ID")
|
||||||
|
val accessKeySecret = System.getenv("OSS_ACCESS_KEY_SECRET")
|
||||||
|
val credentialsProvider: CredentialsProvider = DefaultCredentialProvider(accessKeyId, accessKeySecret)
|
||||||
|
ossClient = OSSClientBuilder().build(ENDPOINT, credentialsProvider)
|
||||||
|
return ossClient
|
||||||
|
}
|
||||||
|
|
||||||
|
fun shutdown() {
|
||||||
|
ossClient.shutdown()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun client(): OSS {
|
||||||
|
return ossClient
|
||||||
|
}
|
||||||
|
|
||||||
|
fun bucket(): Bucket {
|
||||||
|
return ossClient.createBucket(BUCKET_NAME)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun bucketName(): String {
|
||||||
|
return BUCKET_NAME
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AliyunOssFileServiceImpl : FileOSS {
|
||||||
|
override fun upload(file: File): Boolean {
|
||||||
|
try {
|
||||||
|
AliyunOss.client().putObject(
|
||||||
|
AliyunOss.bucketName(),
|
||||||
|
file.name,
|
||||||
|
file
|
||||||
|
)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun download(fileName: String): File {
|
||||||
|
val ossObject = AliyunOss.client().getObject(AliyunOss.bucketName(), fileName)
|
||||||
|
val file = File(fileName)
|
||||||
|
val outputStream = FileOutputStream(file)
|
||||||
|
ossObject.objectContent?.use { input ->
|
||||||
|
outputStream.use { output ->
|
||||||
|
input.copyTo(output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return file
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun delete(fileName: String): Boolean {
|
||||||
|
val client = AliyunOss.client()
|
||||||
|
try {
|
||||||
|
client.deleteObject(AliyunOss.bucketName(), fileName)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,18 +4,32 @@ import com.zaxxer.hikari.HikariConfig
|
||||||
import com.zaxxer.hikari.HikariDataSource
|
import com.zaxxer.hikari.HikariDataSource
|
||||||
import io.ktor.server.config.*
|
import io.ktor.server.config.*
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import org.jetbrains.exposed.sql.Database
|
||||||
|
import org.jetbrains.exposed.sql.SchemaUtils
|
||||||
import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction
|
import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction
|
||||||
|
import org.jetbrains.exposed.sql.transactions.transaction
|
||||||
|
|
||||||
object DatabaseSingleton {
|
object DatabaseSingleton {
|
||||||
|
|
||||||
fun init(config: ApplicationConfig) {
|
lateinit var dataSource: HikariDataSource
|
||||||
val driverClassName = config.property("storage.driverClassName").getString()
|
|
||||||
val jdbcURL = config.property("storage.jdbcURL").getString() +
|
|
||||||
config.propertyOrNull("storage.database")?.getString()
|
|
||||||
|
|
||||||
|
fun init(config: ApplicationConfig) {
|
||||||
|
val jdbcUrl = config.property("storage.jdbcURL").getString()
|
||||||
|
val database = config.property("storage.database").getString()
|
||||||
|
val dateSourceConfig = createHikariDataSource(
|
||||||
|
url = "$jdbcUrl/$database",
|
||||||
|
driver = config.property("storage.driverClassName").getString(),
|
||||||
|
usernameInput = config.property("storage.username").getString(),
|
||||||
|
passwordInput = config.property("storage.password").getString()
|
||||||
|
)
|
||||||
|
dataSource = HikariDataSource(dateSourceConfig)
|
||||||
|
Database.connect(dataSource)
|
||||||
|
transaction {
|
||||||
|
SchemaUtils.create(Documents)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createHikariDataSource(
|
private fun createHikariDataSource(
|
||||||
url: String,
|
url: String,
|
||||||
driver: String,
|
driver: String,
|
||||||
usernameInput: String,
|
usernameInput: String,
|
||||||
|
|
|
@ -1,66 +1,95 @@
|
||||||
package echo.org.instructure
|
package echo.org.instructure
|
||||||
|
|
||||||
import echo.org.domain.Document
|
|
||||||
import echo.org.domain.DocumentQueryParam
|
import echo.org.domain.*
|
||||||
import echo.org.domain.DocumentRepository
|
|
||||||
import echo.org.domain.PageResult
|
|
||||||
import echo.org.instructure.DatabaseSingleton.dbQuery
|
import echo.org.instructure.DatabaseSingleton.dbQuery
|
||||||
import org.jetbrains.exposed.dao.id.LongIdTable
|
import echo.org.instructure.Documents.id
|
||||||
import org.jetbrains.exposed.sql.Column
|
import org.jetbrains.exposed.sql.*
|
||||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||||
import org.jetbrains.exposed.sql.andWhere
|
|
||||||
import org.jetbrains.exposed.sql.deleteWhere
|
|
||||||
import org.jetbrains.exposed.sql.javatime.datetime
|
import org.jetbrains.exposed.sql.javatime.datetime
|
||||||
import org.jetbrains.exposed.sql.selectAll
|
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
|
|
||||||
object Documents : LongIdTable() {
|
object Documents : Table() {
|
||||||
val title: Column<String> = varchar("title", 255)
|
val id = long("id").autoIncrement().uniqueIndex()
|
||||||
val content: Column<String> = text("content")
|
val title = varchar("title", 255)
|
||||||
val authorId: Column<String> = varchar("author_id", 50)
|
val uploadName = varchar("upload_name", 255)
|
||||||
val createdAt: Column<LocalDateTime> = datetime("created_at")
|
val fileType = varchar("file_type", 50)
|
||||||
val updatedAt: Column<LocalDateTime> = datetime("updated_at")
|
val authorId = varchar("author_id", 255)
|
||||||
val fileSize: Column<String> = varchar("file_size", 255)
|
val createdAt = datetime("created_at")
|
||||||
|
val updatedAt = datetime("updated_at")
|
||||||
|
val fileSize = varchar("file_size", 100)
|
||||||
|
val filePath = varchar("file_path", 255)
|
||||||
|
override val primaryKey = PrimaryKey(id)
|
||||||
|
|
||||||
|
// Convert a ResultRow to a Document
|
||||||
|
fun toDocument(row: ResultRow): Document {
|
||||||
|
return Document(
|
||||||
|
id = row[id],
|
||||||
|
title = row[title],
|
||||||
|
authorId = row[authorId],
|
||||||
|
createdAt = row[createdAt],
|
||||||
|
updatedAt = row[updatedAt],
|
||||||
|
fileType = row[fileType],
|
||||||
|
uploadName = row[uploadName],
|
||||||
|
fileSize = row[fileSize],
|
||||||
|
filePath = row[filePath]
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class DocumentRepositoryImpl : DocumentRepository {
|
class DocumentRepositoryImpl : DocumentRepository {
|
||||||
|
|
||||||
override suspend fun save(document: Document): Document = dbQuery {
|
override suspend fun edit(document: Document): Document? = dbQuery {
|
||||||
// 检查文档是否有有效的ID
|
// 检查文档是否有有效的ID
|
||||||
val existingDocument = document.id.let {
|
val existingDocument = Documents.selectAll().where { id eq document.id }.singleOrNull()
|
||||||
Document.findById(it)
|
// 如果文档存在,则更新,否则返回 null
|
||||||
|
existingDocument?.let {
|
||||||
|
Documents.update({ id eq document.id }) {
|
||||||
|
it[title] = document.title
|
||||||
|
it[authorId] = document.authorId
|
||||||
|
it[createdAt] = document.createdAt
|
||||||
|
it[updatedAt] = LocalDateTime.now() // 更新时间应为当前时间
|
||||||
|
it[fileSize] = document.fileSize
|
||||||
}
|
}
|
||||||
// 如果文档存在,则更新,否则创建新文档
|
// 返回更新后的 Document 对象
|
||||||
existingDocument?.apply {
|
Documents.selectAll().where { id eq document.id }
|
||||||
title = document.title
|
.map { Documents.toDocument(it) }
|
||||||
content = document.content
|
.singleOrNull()
|
||||||
authorId = document.authorId
|
|
||||||
createdAt = document.createdAt
|
|
||||||
updatedAt = LocalDateTime.now() // 更新时间应为当前时间
|
|
||||||
fileSize = document.fileSize
|
|
||||||
} ?: Document.new {
|
|
||||||
title = document.title
|
|
||||||
content = document.content
|
|
||||||
authorId = document.authorId
|
|
||||||
createdAt = document.createdAt
|
|
||||||
updatedAt = document.updatedAt
|
|
||||||
fileSize = document.fileSize
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun create(
|
||||||
|
documentVO: DocumentVO
|
||||||
|
): Document = dbQuery {
|
||||||
|
Documents.insert {
|
||||||
|
it[title] = documentVO.title
|
||||||
|
it[authorId] = documentVO.authorId
|
||||||
|
it[updatedAt] = documentVO.updatedAt
|
||||||
|
it[createdAt] = documentVO.createdAt
|
||||||
|
it[fileType] = documentVO.fileType
|
||||||
|
it[uploadName] = documentVO.uploadName
|
||||||
|
it[fileSize] = documentVO.fileSize
|
||||||
|
it[filePath] = documentVO.filePath
|
||||||
|
}
|
||||||
|
Documents.selectAll().where { id eq id }
|
||||||
|
.map { Documents.toDocument(it) }
|
||||||
|
.single()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
override suspend fun findById(id: Long): Document? = dbQuery {
|
override suspend fun findById(id: Long): Document? = dbQuery {
|
||||||
Document.find { Documents.id eq id }.firstOrNull()
|
Documents.selectAll().where { Documents.id eq id }
|
||||||
|
.mapNotNull { Documents.toDocument(it) }
|
||||||
|
.singleOrNull()
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun deleteById(id: Long): Boolean {
|
override suspend fun deleteById(id: Long): Boolean = dbQuery {
|
||||||
Document.find { Documents.id eq id }
|
Documents.deleteWhere { Documents.id eq id } > 0
|
||||||
.firstOrNull()?.delete()
|
|
||||||
return Documents.deleteWhere { Documents.id eq id } > 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun pageQuery(documentQueryParam: DocumentQueryParam): PageResult<Document> {
|
override suspend fun pageQuery(documentQueryParam: DocumentQueryParam): PageResult<Document> = dbQuery {
|
||||||
// 构建查询条件
|
// 构建查询条件
|
||||||
val query = Documents.selectAll().apply {
|
val query = Documents.selectAll().apply {
|
||||||
documentQueryParam.authorId?.let {
|
documentQueryParam.authorId?.let {
|
||||||
|
@ -70,12 +99,13 @@ class DocumentRepositoryImpl : DocumentRepository {
|
||||||
andWhere { Documents.title like "%${documentQueryParam.title}%" }
|
andWhere { Documents.title like "%${documentQueryParam.title}%" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val pageSize = documentQueryParam.pageSize ?: 10 // 默认页面大小
|
val pageSize = documentQueryParam.pageSize // 默认页面大小
|
||||||
val pageIndex = documentQueryParam.pageNum ?: 0 // 默认页码
|
val pageIndex = documentQueryParam.pageNum - 1 // 默认页码
|
||||||
val offset = pageSize * pageIndex.toLong()
|
val offset = pageSize * pageIndex.toLong()
|
||||||
// 执行查询
|
// 执行查询
|
||||||
val count = query.count()
|
val count = query.count()
|
||||||
val documentList = query.limit(pageSize, offset).map { Document.wrapRow(it) }
|
val documents = query.limit(pageSize, offset)
|
||||||
return PageResult(documentList, count)
|
.map { Documents.toDocument(it) }
|
||||||
|
PageResult(documents, count)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,47 +1,15 @@
|
||||||
package echo.org.plugins
|
package echo.org.plugins
|
||||||
|
|
||||||
|
|
||||||
import echo.org.domainDocuments
|
import echo.org.application.route.handleDocuments
|
||||||
import echo.org.domain.Result
|
|
||||||
import io.ktor.http.content.*
|
|
||||||
import io.ktor.server.application.*
|
import io.ktor.server.application.*
|
||||||
import io.ktor.server.freemarker.*
|
|
||||||
import io.ktor.server.http.content.*
|
import io.ktor.server.http.content.*
|
||||||
import io.ktor.server.request.*
|
|
||||||
import io.ktor.server.response.*
|
|
||||||
import io.ktor.server.routing.*
|
import io.ktor.server.routing.*
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
fun Application.configureRouting() {
|
fun Application.configureRouting() {
|
||||||
routing {
|
routing {
|
||||||
staticResources("/files", "files")
|
staticResources("/files", "files")
|
||||||
route("/documents") {
|
handleDocuments()
|
||||||
get("") {
|
|
||||||
|
|
||||||
call.respond(FreeMarkerContent("index.ftl", model, "e"))
|
|
||||||
}
|
|
||||||
post("/add") {
|
|
||||||
// 添加一个文件
|
|
||||||
val multipart = call.receiveMultipart()
|
|
||||||
multipart.forEachPart { part ->
|
|
||||||
when (part) {
|
|
||||||
is PartData.FileItem -> {
|
|
||||||
val fileName = part.originalFileName as String
|
|
||||||
val file = File("upload-directory/$fileName")
|
|
||||||
part.streamProvider()
|
|
||||||
.use { its -> file.outputStream().buffered()
|
|
||||||
.use { its.copyTo(it) } }
|
|
||||||
}
|
|
||||||
else -> {}
|
|
||||||
}
|
|
||||||
part.dispose()
|
|
||||||
}
|
|
||||||
call.respond(Result.success(""))
|
|
||||||
}
|
|
||||||
post("/download"){
|
|
||||||
// 获取下载第
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,20 @@
|
||||||
package echo.org.plugins
|
package echo.org.plugins
|
||||||
|
|
||||||
|
import com.google.gson.TypeAdapter
|
||||||
|
import com.google.gson.stream.JsonReader
|
||||||
|
import com.google.gson.stream.JsonWriter
|
||||||
import io.ktor.serialization.gson.*
|
import io.ktor.serialization.gson.*
|
||||||
import io.ktor.server.application.*
|
import io.ktor.server.application.*
|
||||||
import io.ktor.server.plugins.contentnegotiation.*
|
import io.ktor.server.plugins.contentnegotiation.*
|
||||||
import io.ktor.server.response.*
|
import io.ktor.server.response.*
|
||||||
import io.ktor.server.routing.*
|
import io.ktor.server.routing.*
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
import java.time.format.DateTimeFormatter
|
||||||
|
|
||||||
fun Application.configureSerialization() {
|
fun Application.configureSerialization() {
|
||||||
install(ContentNegotiation) {
|
install(ContentNegotiation) {
|
||||||
gson {
|
gson {
|
||||||
|
registerTypeAdapter(LocalDateTime::class.java, LocalDateTimeAdapter())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
routing {
|
routing {
|
||||||
|
@ -17,3 +23,15 @@ fun Application.configureSerialization() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class LocalDateTimeAdapter : TypeAdapter<LocalDateTime>() {
|
||||||
|
private val formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME
|
||||||
|
|
||||||
|
override fun write(out: JsonWriter, value: LocalDateTime) {
|
||||||
|
out.value(value.format(formatter))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun read(input: JsonReader): LocalDateTime {
|
||||||
|
return LocalDateTime.parse(input.nextString(), formatter)
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,12 +3,12 @@ ktor:
|
||||||
modules:
|
modules:
|
||||||
- echo.org.ApplicationKt.module
|
- echo.org.ApplicationKt.module
|
||||||
deployment:
|
deployment:
|
||||||
port: 8080
|
port: 8012
|
||||||
|
|
||||||
|
|
||||||
storage:
|
storage:
|
||||||
driverClassName: "com.mysql.cj.jdbc.Driver"
|
driverClassName: "com.mysql.cj.jdbc.Driver"
|
||||||
jdbcURL: "jdbc:mysql://47.97.21.20:3306/"
|
jdbcURL: "jdbc:mysql://47.97.21.20:3306"
|
||||||
database: "document"
|
database: "document"
|
||||||
username: "documentAdmin"
|
username: "documentAdmin"
|
||||||
password: "123456"
|
password: "123456"
|
|
@ -4,7 +4,7 @@
|
||||||
<pattern>%d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
<pattern>%d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||||
</encoder>
|
</encoder>
|
||||||
</appender>
|
</appender>
|
||||||
<root level="trace">
|
<root level="INFO">
|
||||||
<appender-ref ref="STDOUT"/>
|
<appender-ref ref="STDOUT"/>
|
||||||
</root>
|
</root>
|
||||||
<logger name="org.eclipse.jetty" level="INFO"/>
|
<logger name="org.eclipse.jetty" level="INFO"/>
|
||||||
|
|
|
@ -1,10 +1,17 @@
|
||||||
package echo.org
|
package echo.org
|
||||||
|
|
||||||
|
import echo.org.application.route.DocumentModule
|
||||||
|
import echo.org.instructure.Documents
|
||||||
import echo.org.plugins.*
|
import echo.org.plugins.*
|
||||||
import io.ktor.client.request.*
|
import io.ktor.client.request.*
|
||||||
import io.ktor.client.statement.*
|
import io.ktor.client.statement.*
|
||||||
import io.ktor.http.*
|
import io.ktor.http.*
|
||||||
|
import io.ktor.server.application.*
|
||||||
import io.ktor.server.testing.*
|
import io.ktor.server.testing.*
|
||||||
|
import org.jetbrains.exposed.sql.SchemaUtils
|
||||||
|
import org.jetbrains.exposed.sql.transactions.transaction
|
||||||
|
import org.koin.dsl.module
|
||||||
|
import org.koin.ktor.plugin.Koin
|
||||||
import kotlin.test.*
|
import kotlin.test.*
|
||||||
|
|
||||||
class ApplicationTest {
|
class ApplicationTest {
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
package echo.org
|
||||||
|
|
||||||
|
import echo.org.instructure.AliyunOss
|
||||||
|
import io.ktor.server.testing.*
|
||||||
|
import java.io.File
|
||||||
|
import kotlin.test.Test
|
||||||
|
|
||||||
|
class ApplicationOSSTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testRoot() = testApplication {
|
||||||
|
AliyunOss.init()
|
||||||
|
|
||||||
|
AliyunOss.client().putObject(AliyunOss.bucketName(),
|
||||||
|
"测试.txt",
|
||||||
|
File("D:\\code\\blog\\upload-directory\\新建 文本文档 (3).txt"))
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue