feat: 新增了笔记相关的接口
- getNoteDetailed - uploadNote - updateNote - deleteNote - starNote - likeNotemaster
parent
d50715f58c
commit
e9b6521c89
|
@ -39,6 +39,7 @@ repositories {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("org.springframework.boot:spring-boot-starter-validation")
|
implementation("org.springframework.boot:spring-boot-starter-validation")
|
||||||
implementation("org.springframework.boot:spring-boot-starter-web")
|
implementation("org.springframework.boot:spring-boot-starter-web")
|
||||||
|
implementation("org.springframework.boot:spring-boot-starter-webflux")
|
||||||
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
|
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
|
||||||
// 日志
|
// 日志
|
||||||
implementation("org.springframework.boot:spring-boot-starter-log4j2")
|
implementation("org.springframework.boot:spring-boot-starter-log4j2")
|
||||||
|
|
|
@ -10,5 +10,5 @@ echo 'saving...'
|
||||||
docker save -o ..\docker\aics_main.tar auto/aics_main:latest
|
docker save -o ..\docker\aics_main.tar auto/aics_main:latest
|
||||||
|
|
||||||
echo 'compressing...'
|
echo 'compressing...'
|
||||||
Compress-Archive -Path ..\docker\aics_main.tar -DestinationPath ..\docker\aics_main.zip
|
Compress-Archive -Path ..\docker\aics_main.tar -DestinationPath ..\docker\aics_main.zip -Update
|
||||||
pause
|
pause
|
|
@ -1,5 +1,11 @@
|
||||||
package cn.edu.hfut.auto.knowledge.config
|
package cn.edu.hfut.auto.knowledge.config
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonParser
|
||||||
|
import com.fasterxml.jackson.databind.DeserializationContext
|
||||||
|
import com.fasterxml.jackson.databind.JsonDeserializer
|
||||||
|
import com.fasterxml.jackson.databind.Module
|
||||||
|
import com.fasterxml.jackson.databind.module.SimpleModule
|
||||||
|
import org.springframework.context.annotation.Bean
|
||||||
import org.springframework.context.annotation.Configuration
|
import org.springframework.context.annotation.Configuration
|
||||||
import org.springframework.core.convert.converter.Converter
|
import org.springframework.core.convert.converter.Converter
|
||||||
import org.springframework.stereotype.Component
|
import org.springframework.stereotype.Component
|
||||||
|
@ -16,4 +22,18 @@ class SerializeConfig {
|
||||||
return UUID.fromString(source)
|
return UUID.fromString(source)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
fun nullableUUIDModule(): Module {
|
||||||
|
return SimpleModule().apply {
|
||||||
|
addDeserializer(UUID::class.java, NullableUUIDJsonDeserializer())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class NullableUUIDJsonDeserializer : JsonDeserializer<UUID?>() {
|
||||||
|
override fun deserialize(p: JsonParser, ctxt: DeserializationContext): UUID? {
|
||||||
|
return p.valueAsString?.takeIf { it != "null" }?.let { UUID.fromString(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
package cn.edu.hfut.auto.knowledge.config
|
||||||
|
|
||||||
|
import cn.edu.hfut.auto.knowledge.service.QueryService
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
|
import org.springframework.beans.factory.config.ConfigurableBeanFactory
|
||||||
|
import org.springframework.context.annotation.Bean
|
||||||
|
import org.springframework.context.annotation.Configuration
|
||||||
|
import org.springframework.web.reactive.function.client.WebClient
|
||||||
|
import org.springframework.web.reactive.function.client.support.WebClientAdapter
|
||||||
|
import org.springframework.web.service.invoker.HttpServiceProxyFactory
|
||||||
|
import org.springframework.web.service.invoker.createClient
|
||||||
|
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
class WebClientConfig {
|
||||||
|
@Bean
|
||||||
|
fun webClient(objectMapper: ObjectMapper): WebClient {
|
||||||
|
return WebClient.builder()
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
fun queryService(webClient: WebClient, configurableBeanFactory: ConfigurableBeanFactory): QueryService {
|
||||||
|
val httpServiceProxyFactory = HttpServiceProxyFactory.builder(WebClientAdapter.forClient(webClient))
|
||||||
|
.embeddedValueResolver(configurableBeanFactory::resolveEmbeddedValue)
|
||||||
|
.build()
|
||||||
|
return httpServiceProxyFactory.createClient<QueryService>()
|
||||||
|
}
|
||||||
|
}
|
|
@ -157,6 +157,7 @@ class KnowledgeController(
|
||||||
?.knowledgeFileAttribute
|
?.knowledgeFileAttribute
|
||||||
?.let { attr ->
|
?.let { attr ->
|
||||||
knowledgeFileAttributeRepository.update(new(KnowledgeFileAttribute::class).by {
|
knowledgeFileAttributeRepository.update(new(KnowledgeFileAttribute::class).by {
|
||||||
|
id = knowledgeId
|
||||||
starers = attr.starers.filterNot { it.id == loginUserId }
|
starers = attr.starers.filterNot { it.id == loginUserId }
|
||||||
if (active) {
|
if (active) {
|
||||||
starers().addBy { id = loginUserId }
|
starers().addBy { id = loginUserId }
|
||||||
|
|
|
@ -0,0 +1,106 @@
|
||||||
|
package cn.edu.hfut.auto.knowledge.controller
|
||||||
|
|
||||||
|
import cn.edu.hfut.auto.knowledge.entity.*
|
||||||
|
import cn.edu.hfut.auto.knowledge.entity.vo.NotePutQueryVO
|
||||||
|
import cn.edu.hfut.auto.knowledge.entity.vo.NoteVO
|
||||||
|
import cn.edu.hfut.auto.knowledge.exception.BusinessError
|
||||||
|
import cn.edu.hfut.auto.knowledge.exception.ErrorCode
|
||||||
|
import cn.edu.hfut.auto.knowledge.repository.NoteRepository
|
||||||
|
import cn.edu.hfut.auto.knowledge.service.QueryService
|
||||||
|
import cn.edu.hfut.auto.knowledge.util.getLoginUser
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
|
import org.babyfish.jimmer.kt.new
|
||||||
|
import org.babyfish.jimmer.sql.kt.fetcher.newFetcher
|
||||||
|
import org.springframework.transaction.annotation.Transactional
|
||||||
|
import org.springframework.web.bind.annotation.*
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/note")
|
||||||
|
class NoteController(
|
||||||
|
private val noteRepository: NoteRepository,
|
||||||
|
private val queryService: QueryService,
|
||||||
|
private val objectMapper: ObjectMapper
|
||||||
|
) {
|
||||||
|
@GetMapping("/{noteId}")
|
||||||
|
suspend fun getNoteDetailed(@PathVariable noteId: UUID): Note =
|
||||||
|
noteRepository.findNullable(noteId, Note.DETAILED_FETCHER) ?: throw BusinessError(ErrorCode.RESOURCE_NOT_FOUND)
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
@Transactional(rollbackFor = [Exception::class])
|
||||||
|
suspend fun uploadNote(@RequestBody vo: NoteVO): Note {
|
||||||
|
val result = noteRepository.insert(new(Note::class).by {
|
||||||
|
author().id = getLoginUser(objectMapper).id
|
||||||
|
title = vo.title
|
||||||
|
createTime = LocalDateTime.now()
|
||||||
|
updateTime = createTime
|
||||||
|
pageView = 0
|
||||||
|
vo.tags.forEach {
|
||||||
|
tags().addBy { id = it }
|
||||||
|
}
|
||||||
|
vo.linkingKnowledgeFiles.forEach {
|
||||||
|
knowledgeFiles().addBy { id = it }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
queryService.putNote(result.id, NotePutQueryVO(result.title, vo.content, vo.tags))
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/{noteId}")
|
||||||
|
@Transactional(rollbackFor = [Exception::class])
|
||||||
|
suspend fun updateNote(@PathVariable noteId: UUID, @RequestBody vo: NoteVO) {
|
||||||
|
val result = noteRepository.update(new(Note::class).by {
|
||||||
|
id = noteId
|
||||||
|
title = vo.title
|
||||||
|
createTime = LocalDateTime.now()
|
||||||
|
pageView = 0
|
||||||
|
tags()
|
||||||
|
vo.tags.forEach {
|
||||||
|
tags().addBy { id = it }
|
||||||
|
}
|
||||||
|
knowledgeFiles()
|
||||||
|
vo.linkingKnowledgeFiles.forEach {
|
||||||
|
knowledgeFiles().addBy { id = it }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
queryService.putNote(result.id, NotePutQueryVO(result.title, vo.content, vo.tags))
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{noteId}")
|
||||||
|
suspend fun deleteNote(@PathVariable noteId: UUID) {
|
||||||
|
noteRepository.deleteById(noteId)
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/{noteId}/star")
|
||||||
|
suspend fun starNote(@PathVariable noteId: UUID, active: Boolean) {
|
||||||
|
val loginUserId = getLoginUser(objectMapper).id
|
||||||
|
noteRepository.findNullable(noteId, newFetcher(Note::class).by {
|
||||||
|
starers()
|
||||||
|
})?.let { attr ->
|
||||||
|
noteRepository.update(new(Note::class).by {
|
||||||
|
id = noteId
|
||||||
|
starers = attr.starers.filterNot { it.id == loginUserId }
|
||||||
|
if (active) {
|
||||||
|
starers().addBy { id = loginUserId }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} ?: throw BusinessError(ErrorCode.RESOURCE_NOT_FOUND)
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/{noteId}/like")
|
||||||
|
suspend fun likeNote(@PathVariable noteId: UUID, active: Boolean) {
|
||||||
|
val loginUserId = getLoginUser(objectMapper).id
|
||||||
|
noteRepository.findNullable(noteId, newFetcher(Note::class).by {
|
||||||
|
likers()
|
||||||
|
})?.let { attr ->
|
||||||
|
noteRepository.update(new(Note::class).by {
|
||||||
|
id = noteId
|
||||||
|
likers = attr.likers.filterNot { it.id == loginUserId }
|
||||||
|
if (active) {
|
||||||
|
likers().addBy { id = loginUserId }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} ?: throw BusinessError(ErrorCode.RESOURCE_NOT_FOUND)
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,20 +26,24 @@ interface Knowledge {
|
||||||
val knowledgeFileAttribute: KnowledgeFileAttribute?
|
val knowledgeFileAttribute: KnowledgeFileAttribute?
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@JvmField
|
||||||
val BRIEF_FETCHER = newFetcher(Knowledge::class).by {
|
val BRIEF_FETCHER = newFetcher(Knowledge::class).by {
|
||||||
allScalarFields()
|
allScalarFields()
|
||||||
knowledgeFileAttribute(KnowledgeFileAttribute.BRIEF_FETCHER)
|
knowledgeFileAttribute(KnowledgeFileAttribute.BRIEF_FETCHER)
|
||||||
}
|
}
|
||||||
|
@JvmField
|
||||||
val DETAILED_FILE_FETCHER = newFetcher(Knowledge::class).by {
|
val DETAILED_FILE_FETCHER = newFetcher(Knowledge::class).by {
|
||||||
allScalarFields()
|
allScalarFields()
|
||||||
knowledgeFileAttribute(KnowledgeFileAttribute.DETAILED_FETCHER)
|
knowledgeFileAttribute(KnowledgeFileAttribute.DETAILED_FETCHER)
|
||||||
}
|
}
|
||||||
|
@JvmField
|
||||||
val AS_PARENT_FETCHER = newFetcher(Knowledge::class).by {
|
val AS_PARENT_FETCHER = newFetcher(Knowledge::class).by {
|
||||||
allScalarFields()
|
allScalarFields()
|
||||||
parent()
|
parent()
|
||||||
children(BRIEF_FETCHER)
|
children(BRIEF_FETCHER)
|
||||||
knowledgeFileAttribute(KnowledgeFileAttribute.BRIEF_FETCHER)
|
knowledgeFileAttribute(KnowledgeFileAttribute.BRIEF_FETCHER)
|
||||||
}
|
}
|
||||||
|
@JvmField
|
||||||
val AS_CHILD_FETCHER = newFetcher(Knowledge::class).by {
|
val AS_CHILD_FETCHER = newFetcher(Knowledge::class).by {
|
||||||
allScalarFields()
|
allScalarFields()
|
||||||
parent(BRIEF_FETCHER)
|
parent(BRIEF_FETCHER)
|
||||||
|
|
|
@ -31,11 +31,14 @@ interface KnowledgeFileAttribute {
|
||||||
val notes: List<Note>
|
val notes: List<Note>
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@JvmField
|
||||||
val EXTERNAL_PREVIEWABLE_SUFFIXES = listOf("pdf", "ppt", "pptx", "doc", "docx", "xls", "xlsx")
|
val EXTERNAL_PREVIEWABLE_SUFFIXES = listOf("pdf", "ppt", "pptx", "doc", "docx", "xls", "xlsx")
|
||||||
|
@JvmField
|
||||||
val BRIEF_FETCHER = newFetcher(KnowledgeFileAttribute::class).by {
|
val BRIEF_FETCHER = newFetcher(KnowledgeFileAttribute::class).by {
|
||||||
allScalarFields()
|
allScalarFields()
|
||||||
tags(Tag.ALL_FETCHER)
|
tags(Tag.ALL_FETCHER)
|
||||||
}
|
}
|
||||||
|
@JvmField
|
||||||
val DETAILED_FETCHER = newFetcher(KnowledgeFileAttribute::class).by {
|
val DETAILED_FETCHER = newFetcher(KnowledgeFileAttribute::class).by {
|
||||||
allScalarFields()
|
allScalarFields()
|
||||||
starers()
|
starers()
|
||||||
|
|
|
@ -11,9 +11,11 @@ interface Note {
|
||||||
@Id
|
@Id
|
||||||
@GeneratedValue(generatorType = UUIDIdGenerator::class)
|
@GeneratedValue(generatorType = UUIDIdGenerator::class)
|
||||||
val id: UUID
|
val id: UUID
|
||||||
|
@Key
|
||||||
val title: String
|
val title: String
|
||||||
|
|
||||||
@ManyToOne
|
@ManyToOne
|
||||||
|
@Key
|
||||||
val author: User
|
val author: User
|
||||||
val createTime: LocalDateTime
|
val createTime: LocalDateTime
|
||||||
val updateTime: LocalDateTime
|
val updateTime: LocalDateTime
|
||||||
|
@ -39,6 +41,7 @@ interface Note {
|
||||||
val likers: List<User>
|
val likers: List<User>
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@JvmField
|
||||||
val BRIEF_FETCHER = newFetcher(Note::class).by {
|
val BRIEF_FETCHER = newFetcher(Note::class).by {
|
||||||
allScalarFields()
|
allScalarFields()
|
||||||
author(User.BRIEF_FETCHER)
|
author(User.BRIEF_FETCHER)
|
||||||
|
@ -46,5 +49,19 @@ interface Note {
|
||||||
starers()
|
starers()
|
||||||
likers()
|
likers()
|
||||||
}
|
}
|
||||||
|
@JvmField
|
||||||
|
val DETAILED_FETCHER = newFetcher(Note::class).by {
|
||||||
|
allScalarFields()
|
||||||
|
author(User.BRIEF_FETCHER)
|
||||||
|
tags(Tag.ALL_FETCHER)
|
||||||
|
knowledgeFiles {
|
||||||
|
allScalarFields()
|
||||||
|
starers()
|
||||||
|
tags(Tag.ALL_FETCHER)
|
||||||
|
knowledge { allScalarFields() }
|
||||||
|
}
|
||||||
|
starers()
|
||||||
|
likers()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -27,6 +27,7 @@ interface Notice {
|
||||||
val targetUser: User
|
val targetUser: User
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@JvmField
|
||||||
val BRIEF_FETCHER = newFetcher(Notice::class).by {
|
val BRIEF_FETCHER = newFetcher(Notice::class).by {
|
||||||
allScalarFields()
|
allScalarFields()
|
||||||
note { allScalarFields() }
|
note { allScalarFields() }
|
||||||
|
|
|
@ -4,6 +4,7 @@ import org.babyfish.jimmer.sql.Entity
|
||||||
import org.babyfish.jimmer.sql.GeneratedValue
|
import org.babyfish.jimmer.sql.GeneratedValue
|
||||||
import org.babyfish.jimmer.sql.GenerationType
|
import org.babyfish.jimmer.sql.GenerationType
|
||||||
import org.babyfish.jimmer.sql.Id
|
import org.babyfish.jimmer.sql.Id
|
||||||
|
import org.babyfish.jimmer.sql.Key
|
||||||
import org.babyfish.jimmer.sql.kt.fetcher.newFetcher
|
import org.babyfish.jimmer.sql.kt.fetcher.newFetcher
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
|
@ -11,9 +12,11 @@ interface Tag {
|
||||||
@Id
|
@Id
|
||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
val id: Long
|
val id: Long
|
||||||
|
@Key
|
||||||
val name: String
|
val name: String
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@JvmField
|
||||||
val ALL_FETCHER = newFetcher(Tag::class).by {
|
val ALL_FETCHER = newFetcher(Tag::class).by {
|
||||||
allScalarFields()
|
allScalarFields()
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@ interface User {
|
||||||
val starredNotes: List<Note>
|
val starredNotes: List<Note>
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@JvmField
|
||||||
val BRIEF_FETCHER = newFetcher(User::class).by {
|
val BRIEF_FETCHER = newFetcher(User::class).by {
|
||||||
allScalarFields()
|
allScalarFields()
|
||||||
password(false)
|
password(false)
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
package cn.edu.hfut.auto.knowledge.entity.vo
|
||||||
|
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
data class NotePutQueryVO(
|
||||||
|
val title: String,
|
||||||
|
val content: String,
|
||||||
|
val tags: List<Long>
|
||||||
|
)
|
||||||
|
|
||||||
|
data class NoteVO(
|
||||||
|
val title: String,
|
||||||
|
val content: String,
|
||||||
|
val tags: List<Long>,
|
||||||
|
val linkingKnowledgeFiles: List<UUID>
|
||||||
|
)
|
|
@ -0,0 +1,7 @@
|
||||||
|
package cn.edu.hfut.auto.knowledge.repository
|
||||||
|
|
||||||
|
import cn.edu.hfut.auto.knowledge.entity.Note
|
||||||
|
import org.babyfish.jimmer.spring.repository.KRepository
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
interface NoteRepository : KRepository<Note, UUID>
|
|
@ -0,0 +1,15 @@
|
||||||
|
package cn.edu.hfut.auto.knowledge.service
|
||||||
|
|
||||||
|
import cn.edu.hfut.auto.knowledge.entity.vo.NotePutQueryVO
|
||||||
|
import org.springframework.http.MediaType
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody
|
||||||
|
import org.springframework.web.service.annotation.HttpExchange
|
||||||
|
import org.springframework.web.service.annotation.PutExchange
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
@HttpExchange(url = "\${aics.services-url.query}/search", contentType = MediaType.APPLICATION_JSON_VALUE)
|
||||||
|
interface QueryService {
|
||||||
|
@PutExchange("/note/{noteId}")
|
||||||
|
suspend fun putNote(@PathVariable noteId: UUID, @RequestBody vo: NotePutQueryVO)
|
||||||
|
}
|
|
@ -31,6 +31,8 @@ logging:
|
||||||
|
|
||||||
aics:
|
aics:
|
||||||
max-permission-level: 3
|
max-permission-level: 3
|
||||||
|
services-url:
|
||||||
|
query: http://localhost:8082
|
||||||
|
|
||||||
tencent:
|
tencent:
|
||||||
secret-id: AKIDSlvvhEYfYBetvYzCBvhJrDLGwNbcR2B7
|
secret-id: AKIDSlvvhEYfYBetvYzCBvhJrDLGwNbcR2B7
|
||||||
|
|
|
@ -30,6 +30,8 @@ logging:
|
||||||
|
|
||||||
aics:
|
aics:
|
||||||
max-permission-level: 3
|
max-permission-level: 3
|
||||||
|
services-url:
|
||||||
|
query: http://aics_query:8082
|
||||||
|
|
||||||
tencent:
|
tencent:
|
||||||
secret-id: AKIDSlvvhEYfYBetvYzCBvhJrDLGwNbcR2B7
|
secret-id: AKIDSlvvhEYfYBetvYzCBvhJrDLGwNbcR2B7
|
||||||
|
|
Loading…
Reference in New Issue