diff --git a/build.gradle.kts b/build.gradle.kts index 3404b05..08bca36 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -37,8 +37,6 @@ repositories { } dependencies { - implementation("org.springframework.boot:spring-boot-starter-data-elasticsearch") - implementation("org.springframework.boot:spring-boot-starter-data-redis") implementation("org.springframework.boot:spring-boot-starter-validation") implementation("org.springframework.boot:spring-boot-starter-web") implementation("com.fasterxml.jackson.module:jackson-module-kotlin") @@ -50,17 +48,23 @@ dependencies { implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:$kotlinxCoroutinesVersion") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-jdk9:$kotlinxCoroutinesVersion") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor:$kotlinxCoroutinesVersion") + // kotlin 反射 implementation("org.jetbrains.kotlin:kotlin-reflect") // sa-token 安全认证框架 implementation("cn.dev33:sa-token-spring-boot3-starter:1.35.0.RC") // jimmer 持久层框架 implementation("org.babyfish.jimmer:jimmer-spring-boot-starter:0.7.104") ksp("org.babyfish.jimmer:jimmer-ksp:0.7.104") -// developmentOnly("org.springframework.boot:spring-boot-docker-compose") implementation("org.postgresql:postgresql") // Kafka implementation("org.springframework.kafka:spring-kafka") + // redis + implementation("org.springframework.boot:spring-boot-starter-data-redis") + // 腾讯云COS + implementation("com.qcloud:cos_api:5.6.133") + // configuration processor annotationProcessor("org.springframework.boot:spring-boot-configuration-processor") + // test testImplementation("org.springframework.boot:spring-boot-starter-test") } diff --git a/src/main/kotlin/cn/edu/hfut/auto/knowledge/AicsKnowledgeBaseBackendApplication.kt b/src/main/kotlin/cn/edu/hfut/auto/knowledge/AicsKnowledgeBaseBackendApplication.kt index 80446ef..12dbfe2 100644 --- a/src/main/kotlin/cn/edu/hfut/auto/knowledge/AicsKnowledgeBaseBackendApplication.kt +++ b/src/main/kotlin/cn/edu/hfut/auto/knowledge/AicsKnowledgeBaseBackendApplication.kt @@ -1,11 +1,13 @@ package cn.edu.hfut.auto.knowledge import org.springframework.boot.autoconfigure.SpringBootApplication +import org.springframework.boot.context.properties.ConfigurationPropertiesScan import org.springframework.boot.runApplication import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RestController @SpringBootApplication +@ConfigurationPropertiesScan @RestController class AicsKnowledgeBaseBackendApplication { @RequestMapping("/hello") diff --git a/src/main/kotlin/cn/edu/hfut/auto/knowledge/config/TencentAPIConfig.kt b/src/main/kotlin/cn/edu/hfut/auto/knowledge/config/TencentAPIConfig.kt new file mode 100644 index 0000000..0ccbe20 --- /dev/null +++ b/src/main/kotlin/cn/edu/hfut/auto/knowledge/config/TencentAPIConfig.kt @@ -0,0 +1,28 @@ +package cn.edu.hfut.auto.knowledge.config + +import com.qcloud.cos.COSClient +import com.qcloud.cos.ClientConfig +import com.qcloud.cos.auth.BasicCOSCredentials +import com.qcloud.cos.region.Region +import org.springframework.boot.context.properties.ConfigurationProperties +import org.springframework.context.annotation.Configuration +import org.springframework.stereotype.Component + +@Configuration +class TencentAPIConfig + +@Component +class COSClientWrapper(tencentProperties: TencentProperties) : COSClient( + BasicCOSCredentials(tencentProperties.secretId, tencentProperties.secretKey), + ClientConfig(Region(tencentProperties.cosRegion)) +) { + val bucketName = tencentProperties.bucketName +} + +@ConfigurationProperties(prefix = "tencent") +data class TencentProperties( + val secretId: String, + val secretKey: String, + val cosRegion: String, + val bucketName: String +) \ No newline at end of file diff --git a/src/main/kotlin/cn/edu/hfut/auto/knowledge/controller/KnowledgeController.kt b/src/main/kotlin/cn/edu/hfut/auto/knowledge/controller/KnowledgeController.kt index 10f2500..9d40e7a 100644 --- a/src/main/kotlin/cn/edu/hfut/auto/knowledge/controller/KnowledgeController.kt +++ b/src/main/kotlin/cn/edu/hfut/auto/knowledge/controller/KnowledgeController.kt @@ -1,10 +1,8 @@ package cn.edu.hfut.auto.knowledge.controller import cn.dev33.satoken.annotation.SaCheckRole -import cn.edu.hfut.auto.knowledge.entity.Knowledge -import cn.edu.hfut.auto.knowledge.entity.KnowledgeDraft -import cn.edu.hfut.auto.knowledge.entity.KnowledgeFileAttribute -import cn.edu.hfut.auto.knowledge.entity.by +import cn.edu.hfut.auto.knowledge.config.COSClientWrapper +import cn.edu.hfut.auto.knowledge.entity.* import cn.edu.hfut.auto.knowledge.entity.rpc.FileTicket import cn.edu.hfut.auto.knowledge.entity.vo.KnowledgeFileUploadVO import cn.edu.hfut.auto.knowledge.exception.BusinessError @@ -12,6 +10,7 @@ import cn.edu.hfut.auto.knowledge.exception.ErrorCode import cn.edu.hfut.auto.knowledge.repository.KnowledgeFileAttributeRepository import cn.edu.hfut.auto.knowledge.repository.KnowledgeRepository import cn.edu.hfut.auto.knowledge.util.fileSuffix +import com.qcloud.cos.model.ciModel.job.DocHtmlRequest import kotlinx.coroutines.future.await import org.apache.kafka.common.KafkaException import org.babyfish.jimmer.kt.new @@ -33,7 +32,8 @@ const val UPLOAD_FILE_TOPIC = "upload_file" class KnowledgeController( private val kafkaTemplate: KafkaTemplate, private val knowledgeRepository: KnowledgeRepository, - private val knowledgeFileAttributeRepository: KnowledgeFileAttributeRepository + private val knowledgeFileAttributeRepository: KnowledgeFileAttributeRepository, + private val cosClientWrapper: COSClientWrapper ) { @GetMapping("/{knowledgeId}") @@ -84,4 +84,20 @@ class KnowledgeController( return this } } + + @GetMapping("/{knowledgeId}/preview/external") + suspend fun getExternalPreviewKnowledgeUrl(@PathVariable knowledgeId: UUID): String { + knowledgeRepository.findNullable(knowledgeId, Knowledge.BRIEF_FETCHER) + ?.knowledgeFileAttribute + ?.takeIf { it.externalPreviewable } + ?.let { + val request = DocHtmlRequest().apply { + bucketName = cosClientWrapper.bucketName + dstType = DocHtmlRequest.DocType.html + objectKey = "${it.id}.${it.suffix}" + } + return cosClientWrapper.GenerateDocPreviewUrl(request) + } + ?: throw BusinessError(ErrorCode.RESOURCE_NOT_FOUND) + } } \ No newline at end of file diff --git a/src/main/kotlin/cn/edu/hfut/auto/knowledge/entity/KnowledgeFileAttribute.kt b/src/main/kotlin/cn/edu/hfut/auto/knowledge/entity/KnowledgeFileAttribute.kt index 5f00ff2..4d9073c 100644 --- a/src/main/kotlin/cn/edu/hfut/auto/knowledge/entity/KnowledgeFileAttribute.kt +++ b/src/main/kotlin/cn/edu/hfut/auto/knowledge/entity/KnowledgeFileAttribute.kt @@ -31,6 +31,7 @@ interface KnowledgeFileAttribute { val notes: List companion object { + val EXTERNAL_PREVIEWABLE_SUFFIXES = listOf("pdf", "ppt", "pptx", "doc", "docx", "xls", "xlsx") val BRIEF_FETCHER = newFetcher(KnowledgeFileAttribute::class).by { allScalarFields() tags { @@ -38,4 +39,7 @@ interface KnowledgeFileAttribute { } } } -} \ No newline at end of file +} + +inline val KnowledgeFileAttribute.externalPreviewable: Boolean + get() = suffix in KnowledgeFileAttribute.EXTERNAL_PREVIEWABLE_SUFFIXES \ No newline at end of file diff --git a/src/main/kotlin/cn/edu/hfut/auto/knowledge/entity/rpc/FileTicket.kt b/src/main/kotlin/cn/edu/hfut/auto/knowledge/entity/rpc/FileTicket.kt index 882bbe3..1f9b336 100644 --- a/src/main/kotlin/cn/edu/hfut/auto/knowledge/entity/rpc/FileTicket.kt +++ b/src/main/kotlin/cn/edu/hfut/auto/knowledge/entity/rpc/FileTicket.kt @@ -1,15 +1,18 @@ package cn.edu.hfut.auto.knowledge.entity.rpc import cn.edu.hfut.auto.knowledge.entity.KnowledgeFileAttribute +import cn.edu.hfut.auto.knowledge.entity.externalPreviewable import org.apache.commons.codec.digest.Sha2Crypt import java.time.LocalDateTime -import java.util.UUID +import java.util.* data class FileTicket( val ticket: String, val id: UUID, val md5: String, - val size: Long + val size: Long, + val externalPreviewable: Boolean, + val suffix: String ) { constructor(knowledgeFileAttribute: KnowledgeFileAttribute, md5: String): this( buildString { @@ -19,10 +22,12 @@ data class FileTicket( append(LocalDateTime.now()) } }.let { - Sha2Crypt.sha256Crypt(it.toByteArray()) + Sha2Crypt.sha256Crypt(it.toByteArray()) }, knowledgeFileAttribute.id, md5, - knowledgeFileAttribute.size + knowledgeFileAttribute.size, + knowledgeFileAttribute.externalPreviewable, + knowledgeFileAttribute.suffix ) } diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index a3438fd..5d717bf 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -30,4 +30,10 @@ logging: config: classpath:log4j2-dev.xml aics: - max-permission-level: 3 \ No newline at end of file + max-permission-level: 3 + +tencent: + secret-id: AKIDSlvvhEYfYBetvYzCBvhJrDLGwNbcR2B7 + secret-key: kHZigS3UkqlY8WiuypXM3ZwCHA0iHepp + cos-region: ap-nanjing + bucket-name: aics-1300085057 \ No newline at end of file diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 1624083..2c98006 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -29,4 +29,10 @@ logging: config: classpath:log4j2-prod.xml aics: - max-permission-level: 3 \ No newline at end of file + max-permission-level: 3 + +tencent: + secret-id: AKIDSlvvhEYfYBetvYzCBvhJrDLGwNbcR2B7 + secret-key: kHZigS3UkqlY8WiuypXM3ZwCHA0iHepp + cos-region: ap-nanjing + bucket-name: aics-1300085057 \ No newline at end of file