feat: 增加首页过滤和搜索
This commit is contained in:
@@ -41,6 +41,7 @@ dependencies {
|
|||||||
implementation("cn.bigmodel.openapi:oapi-java-sdk:release-V4-2.3.0")
|
implementation("cn.bigmodel.openapi:oapi-java-sdk:release-V4-2.3.0")
|
||||||
implementation("com.baidubce:qianfan:0.1.1")
|
implementation("com.baidubce:qianfan:0.1.1")
|
||||||
implementation("org.jsoup:jsoup:1.18.1")
|
implementation("org.jsoup:jsoup:1.18.1")
|
||||||
|
implementation("com.blinkfox:fenix-spring-boot-starter:3.0.0")
|
||||||
|
|
||||||
val hutoolVersion = "5.8.32"
|
val hutoolVersion = "5.8.32"
|
||||||
implementation("cn.hutool:hutool-core:$hutoolVersion")
|
implementation("cn.hutool:hutool-core:$hutoolVersion")
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.lanyuanxiaoyao.digtal.market
|
package com.lanyuanxiaoyao.digtal.market
|
||||||
|
|
||||||
|
import com.blinkfox.fenix.EnableFenix
|
||||||
import com.lanyuanxiaoyao.digtal.market.runner.NewsRunner
|
import com.lanyuanxiaoyao.digtal.market.runner.NewsRunner
|
||||||
import com.lanyuanxiaoyao.digtal.market.runner.PushRunner
|
import com.lanyuanxiaoyao.digtal.market.runner.PushRunner
|
||||||
import com.lanyuanxiaoyao.squirrel.core.common.Management
|
import com.lanyuanxiaoyao.squirrel.core.common.Management
|
||||||
@@ -20,7 +21,6 @@ import org.springframework.context.ApplicationListener
|
|||||||
import org.springframework.context.annotation.Bean
|
import org.springframework.context.annotation.Bean
|
||||||
import org.springframework.context.annotation.Configuration
|
import org.springframework.context.annotation.Configuration
|
||||||
import org.springframework.context.event.ContextClosedEvent
|
import org.springframework.context.event.ContextClosedEvent
|
||||||
import org.springframework.scheduling.annotation.EnableScheduling
|
|
||||||
import org.springframework.web.servlet.config.annotation.CorsRegistry
|
import org.springframework.web.servlet.config.annotation.CorsRegistry
|
||||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
|
||||||
|
|
||||||
@@ -41,7 +41,8 @@ data class MailProperties @ConstructorBinding constructor(
|
|||||||
val targets: List<String>,
|
val targets: List<String>,
|
||||||
)
|
)
|
||||||
|
|
||||||
@EnableScheduling
|
// @EnableScheduling
|
||||||
|
@EnableFenix
|
||||||
@ConfigurationPropertiesScan
|
@ConfigurationPropertiesScan
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
class Application : ApplicationRunner, ApplicationListener<ContextClosedEvent> {
|
class Application : ApplicationRunner, ApplicationListener<ContextClosedEvent> {
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
package com.lanyuanxiaoyao.digtal.market
|
package com.lanyuanxiaoyao.digtal.market
|
||||||
|
|
||||||
|
import com.blinkfox.fenix.specification.FenixJpaSpecificationExecutor
|
||||||
import jakarta.persistence.Column
|
import jakarta.persistence.Column
|
||||||
import jakarta.persistence.Entity
|
import jakarta.persistence.Entity
|
||||||
import jakarta.persistence.Id
|
import jakarta.persistence.Id
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
import org.springframework.data.jpa.repository.JpaRepository
|
import org.springframework.data.jpa.repository.JpaRepository
|
||||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor
|
|
||||||
import org.springframework.data.jpa.repository.Modifying
|
import org.springframework.data.jpa.repository.Modifying
|
||||||
import org.springframework.data.jpa.repository.Query
|
import org.springframework.data.jpa.repository.Query
|
||||||
import org.springframework.data.repository.query.Param
|
import org.springframework.data.repository.query.Param
|
||||||
@@ -30,12 +30,18 @@ class Article(
|
|||||||
)
|
)
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
interface ArticleRepository : JpaRepository<Article, String>, JpaSpecificationExecutor<Article> {
|
interface ArticleRepository : JpaRepository<Article, String>, FenixJpaSpecificationExecutor<Article> {
|
||||||
fun findAllByDescriptionIsNullAndTextIsNotNull(): List<Article>
|
fun findAllByDescriptionIsNullAndTextIsNotNull(): List<Article>
|
||||||
fun findAllByHtmlIsNotNull(): List<Article>
|
fun findAllByHtmlIsNotNull(): List<Article>
|
||||||
|
|
||||||
fun existsByCode(code: String): Boolean
|
fun existsByCode(code: String): Boolean
|
||||||
|
|
||||||
|
@Query("select distinct article.category from Article article where article.category is not null and article.category <> ''")
|
||||||
|
fun findAllCategory(): List<String>
|
||||||
|
|
||||||
|
@Query("select distinct article.author from Article article where article.author is not null and article.author <> ''")
|
||||||
|
fun findAllAuthor(): List<String>
|
||||||
|
|
||||||
@Modifying
|
@Modifying
|
||||||
@Transactional
|
@Transactional
|
||||||
@Query("update Article article set article.pushed = :pushed where article.id = :id")
|
@Query("update Article article set article.pushed = :pushed where article.id = :id")
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
package com.lanyuanxiaoyao.digtal.market.controller
|
package com.lanyuanxiaoyao.digtal.market.controller
|
||||||
|
|
||||||
|
import cn.hutool.core.date.LocalDateTimeUtil
|
||||||
import com.lanyuanxiaoyao.digtal.market.ArticleRepository
|
import com.lanyuanxiaoyao.digtal.market.ArticleRepository
|
||||||
import com.lanyuanxiaoyao.digtal.market.sites
|
import com.lanyuanxiaoyao.digtal.market.sites
|
||||||
import com.lanyuanxiaoyao.squirrel.core.common.Site
|
import com.lanyuanxiaoyao.squirrel.core.common.Site
|
||||||
import jakarta.annotation.Resource
|
import jakarta.annotation.Resource
|
||||||
|
import jakarta.persistence.criteria.Predicate
|
||||||
|
import java.sql.Date
|
||||||
|
import java.time.Instant
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import org.springframework.data.domain.PageRequest
|
import org.springframework.data.domain.PageRequest
|
||||||
import org.springframework.data.domain.Sort
|
import org.springframework.data.domain.Sort
|
||||||
@@ -23,11 +27,66 @@ class OverviewController {
|
|||||||
@GetMapping("news")
|
@GetMapping("news")
|
||||||
fun news(
|
fun news(
|
||||||
@RequestParam("code", required = false) code: String?,
|
@RequestParam("code", required = false) code: String?,
|
||||||
|
@RequestParam("filter_keyword", required = false) filterKeyword: String?,
|
||||||
|
@RequestParam("filter_date", required = false) filterDate: Long?,
|
||||||
|
@RequestParam("filter_date_start", required = false) filterDateStart: Long?,
|
||||||
|
@RequestParam("filter_date_end", required = false) filterDateEnd: Long?,
|
||||||
|
@RequestParam("filter_source", required = false) filterSource: String?,
|
||||||
|
@RequestParam("filter_author", required = false) filterAuthor: String?,
|
||||||
|
@RequestParam("filter_category", required = false) filterCategory: String?,
|
||||||
@RequestParam("page", defaultValue = "1") page: Int,
|
@RequestParam("page", defaultValue = "1") page: Int,
|
||||||
@RequestParam("count", defaultValue = "10") count: Int,
|
@RequestParam("count", defaultValue = "10") count: Int,
|
||||||
): Map<String, Any> {
|
): Map<String, Any> {
|
||||||
val request = PageRequest.of(page - 1, count, Sort.by(Sort.Direction.DESC, "createTime"))
|
val pageRequest = PageRequest.of(page - 1, count, Sort.by(Sort.Direction.DESC, "createTime"))
|
||||||
val result = articleRepository.findAll(request)
|
val result = articleRepository.findAll({ root, _, builder ->
|
||||||
|
val predictions = mutableListOf<Predicate>()
|
||||||
|
filterKeyword?.let {
|
||||||
|
predictions.add(
|
||||||
|
builder.or(
|
||||||
|
builder.like(root.get("title"), "%${it}%"),
|
||||||
|
builder.like(root.get("subtitle"), "%${it}%"),
|
||||||
|
builder.like(root.get("text"), "%${it}%"),
|
||||||
|
builder.like(root.get("description"), "%${it}%"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (filterDate != null) {
|
||||||
|
val datetime = LocalDateTimeUtil.of(filterDate * 1000)
|
||||||
|
predictions.add(
|
||||||
|
builder.between(
|
||||||
|
root.get<Date>("createTime"),
|
||||||
|
Date.from(Instant.ofEpochMilli(LocalDateTimeUtil.toEpochMilli(LocalDateTimeUtil.beginOfDay(datetime)))),
|
||||||
|
Date.from(Instant.ofEpochMilli(LocalDateTimeUtil.toEpochMilli(LocalDateTimeUtil.endOfDay(datetime))))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} else if (filterDateStart != null && filterDateEnd != null) {
|
||||||
|
predictions.add(
|
||||||
|
builder.between(root.get<Date>("createTime"), Date.from(Instant.ofEpochSecond(filterDateStart)), Date.from(Instant.ofEpochSecond(filterDateEnd)))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
filterSource?.let {
|
||||||
|
val site = sites.firstOrNull { site -> site.name == it }
|
||||||
|
if (site != null) {
|
||||||
|
predictions.add(
|
||||||
|
builder.equal(root.get<String>("code"), site.code)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
filterAuthor?.let {
|
||||||
|
predictions.add(
|
||||||
|
builder.equal(root.get<String>("author"), it)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
filterCategory?.let {
|
||||||
|
predictions.add(
|
||||||
|
builder.equal(root.get<String>("category"), it)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
builder.and(*predictions.toTypedArray())
|
||||||
|
}, pageRequest)
|
||||||
|
articleRepository.findAll({ builder ->
|
||||||
|
builder.build()
|
||||||
|
}, pageRequest)
|
||||||
return mapOf(
|
return mapOf(
|
||||||
"items" to result.content.map {
|
"items" to result.content.map {
|
||||||
val site = sites.find { site -> site.code == it.code }!!
|
val site = sites.find { site -> site.code == it.code }!!
|
||||||
@@ -49,4 +108,13 @@ class OverviewController {
|
|||||||
"total" to result.totalElements,
|
"total" to result.totalElements,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("all_source")
|
||||||
|
fun allSource() = sites.map { it.name }
|
||||||
|
|
||||||
|
@GetMapping("all_category")
|
||||||
|
fun allCategory() = articleRepository.findAllCategory()
|
||||||
|
|
||||||
|
@GetMapping("all_author")
|
||||||
|
fun allAuthor() = articleRepository.findAllAuthor()
|
||||||
}
|
}
|
||||||
@@ -44,3 +44,6 @@ messenger:
|
|||||||
database:
|
database:
|
||||||
h2-path: ./database/database.db
|
h2-path: ./database/database.db
|
||||||
json-path: ./database/database.json
|
json-path: ./database/database.json
|
||||||
|
fenix:
|
||||||
|
print-banner: false
|
||||||
|
print-sql: false
|
||||||
@@ -30,32 +30,138 @@ function overviewTab() {
|
|||||||
return {
|
return {
|
||||||
title: '总览',
|
title: '总览',
|
||||||
tab: {
|
tab: {
|
||||||
id: 'news_list',
|
type: 'crud',
|
||||||
columnClassName: 'px-2 pt-2',
|
syncLocation: false,
|
||||||
type: 'service',
|
|
||||||
api: {
|
api: {
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: '${base}/overview/news',
|
url: '${base}/overview/news',
|
||||||
data: {
|
data: {
|
||||||
page: '${page|default:1}',
|
page: '${page|default:1}',
|
||||||
count: '${count|default:10}',
|
count: '${count|default:10}',
|
||||||
|
filter_keyword: '${filter_keyword|default:undefined}',
|
||||||
|
filter_date: '${filter_date|default:undefined}',
|
||||||
|
filter_date_start: '${filter_date_start|default:undefined}',
|
||||||
|
filter_date_end: '${filter_date_end|default:undefined}',
|
||||||
|
filter_source: '${filter_source|default:undefined}',
|
||||||
|
filter_author: '${filter_author|default:undefined}',
|
||||||
|
filter_category: '${filter_category|default:undefined}',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
filterTogglable: true,
|
||||||
|
filterDefaultVisible: false,
|
||||||
|
mode: 'list',
|
||||||
|
headerToolbar: [
|
||||||
|
'reload',
|
||||||
|
'filter-toggler',
|
||||||
|
'pagination',
|
||||||
|
],
|
||||||
|
footerToolbar: [],
|
||||||
|
filter: {
|
||||||
|
title: '搜索',
|
||||||
|
mode: 'horizontal',
|
||||||
|
horizontal: {
|
||||||
|
leftFixed: 'sm',
|
||||||
|
},
|
||||||
body: [
|
body: [
|
||||||
{
|
{
|
||||||
type: 'action',
|
type: 'group',
|
||||||
icon: 'fa fa-refresh',
|
body: [
|
||||||
label: '刷新',
|
{
|
||||||
className: 'mb-2',
|
type: 'input-text',
|
||||||
actionType: 'reload',
|
label: '关键字',
|
||||||
target: 'news_list',
|
name: 'filter_keyword',
|
||||||
|
placeholder: '关键字搜索',
|
||||||
|
},
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
...pagination(),
|
type: 'group',
|
||||||
className: 'float-right',
|
body: [
|
||||||
|
{
|
||||||
|
type: 'input-date',
|
||||||
|
label: '日期',
|
||||||
|
name: 'filter_date',
|
||||||
|
columnRatio: 4,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'list',
|
type: 'input-date-range',
|
||||||
|
label: '日期范围',
|
||||||
|
name: 'filter_date_start',
|
||||||
|
extraName: 'filter_date_end',
|
||||||
|
columnRatio: 8,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'group',
|
||||||
|
body: [
|
||||||
|
{
|
||||||
|
type: 'select',
|
||||||
|
label: '来源',
|
||||||
|
name: 'filter_source',
|
||||||
|
multiple: true,
|
||||||
|
searchable: true,
|
||||||
|
source: {
|
||||||
|
method: 'get',
|
||||||
|
url: '${base}/overview/all_source',
|
||||||
|
adaptor: (payload, response, api, context) => {
|
||||||
|
console.log(payload, response, api, context)
|
||||||
|
return payload.map(i => {
|
||||||
|
return {label: i, value: i}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'select',
|
||||||
|
label: '作者',
|
||||||
|
name: 'filter_author',
|
||||||
|
multiple: true,
|
||||||
|
searchable: true,
|
||||||
|
source: {
|
||||||
|
method: 'get',
|
||||||
|
url: '${base}/overview/all_author',
|
||||||
|
adaptor: (payload, response, api, context) => {
|
||||||
|
console.log(payload, response, api, context)
|
||||||
|
return payload.map(i => {
|
||||||
|
return {label: i, value: i}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'select',
|
||||||
|
label: '关键词',
|
||||||
|
name: 'filter_category',
|
||||||
|
multiple: true,
|
||||||
|
searchable: true,
|
||||||
|
source: {
|
||||||
|
method: 'get',
|
||||||
|
url: '${base}/overview/all_category',
|
||||||
|
adaptor: (payload, response, api, context) => {
|
||||||
|
console.log(payload, response, api, context)
|
||||||
|
return payload.map(i => {
|
||||||
|
return {label: i, value: i}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
type: 'reset',
|
||||||
|
label: '清空'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'submit',
|
||||||
|
label: '搜索',
|
||||||
|
level: 'primary',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
canAccessSuperData: false,
|
||||||
listItem: {
|
listItem: {
|
||||||
body: [
|
body: [
|
||||||
{
|
{
|
||||||
@@ -183,8 +289,5 @@ function overviewTab() {
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
pagination(),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user