diff --git a/admin/pom.xml b/admin/pom.xml
index 28d264e..fdb215c 100644
--- a/admin/pom.xml
+++ b/admin/pom.xml
@@ -15,6 +15,8 @@
8
8
UTF-8
+ 8.12.2
+ 2.0.1
@@ -22,12 +24,32 @@
com.jinrui
core
0.0.1-SNAPSHOT
+
+
+ org.elasticsearch.client
+ elasticsearch-rest-client
+
+
+ jakarta.json-api
+ jakarta.json
+
+
redis.clients
jedis
${jedis.version}
+
+ org.elasticsearch.client
+ elasticsearch-rest-client
+ ${elasticsearch.version}
+
+
+ jakarta.json
+ jakarta.json-api
+ ${jakartajson.version}
+
diff --git a/admin/src/main/java/com/jinrui/reference/admin/controller/NewsInfoController.java b/admin/src/main/java/com/jinrui/reference/admin/controller/NewsInfoController.java
new file mode 100644
index 0000000..e53b8b3
--- /dev/null
+++ b/admin/src/main/java/com/jinrui/reference/admin/controller/NewsInfoController.java
@@ -0,0 +1,201 @@
+package com.jinrui.reference.admin.controller;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.util.ObjectUtils;
+import org.springframework.util.StringUtils;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestHeader;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.jinrui.reference.admin.model.dto.newsinfo.PublishNewsInfoDTO;
+import com.jinrui.reference.admin.model.entity.AdminUser;
+import com.jinrui.reference.admin.service.AdminJwtService;
+import com.jinrui.reference.core.model.vo.PageObject;
+import com.jinrui.reference.core.model.vo.ResultObject;
+import com.jinrui.reference.core.model.vo.newsinfo.NewsInfoDetailVO;
+import com.jinrui.reference.core.model.vo.newsinfo.NewsInfoVO;
+import com.jinrui.reference.core.model.vo.newsinfo.SaveNewsInfoDTO;
+import com.jinrui.reference.core.service.NewsInfoService;
+
+@RestController
+@RequestMapping("/newsinfo")
+public class NewsInfoController {
+ private static final Logger log = LoggerFactory.getLogger(NewsInfoController.class);
+
+ private final NewsInfoService newsInfoService;
+ private final ObjectMapper objectMapper;
+
+ public NewsInfoController(NewsInfoService newsInfoService,
+ ObjectMapper objectMapper) {
+ this.newsInfoService = newsInfoService;
+ this.objectMapper = objectMapper;;
+ }
+
+ @PostMapping("/publish")
+ public ResultObject publish(@RequestHeader("auth-token") String token,
+ @RequestBody PublishNewsInfoDTO publishNewsDTO) {
+ if (!StringUtils.hasText(token)) {
+ return ResultObject.failed("登陆Token为空!");
+ }
+
+ try {
+ AdminUser adminUser = AdminJwtService.parseToken(token);
+ if (adminUser == null) {
+ log.warn("解析token {}拿不到AdminUser对象!", token);
+ return ResultObject.failed("登陆Token有误,请联系系统管理员!");
+ }
+
+ Long adminUserId = adminUser.getId();
+ if (!adminUser.isActive()) {
+ log.warn("当前用户已被封禁! id = {}", adminUserId);
+ return ResultObject.failed("当前用户已被封禁!请联系系统管理员!");
+ }
+
+ String id = publishNewsDTO.getId();
+ if (ObjectUtils.isEmpty(id)) {
+ return ResultObject.failed("要发布/下架的新闻ID不可为空!");
+ }
+
+ log.info("path: /newsinfo/publish, method: POST, request user id: {}, news id: {}", adminUserId, id);
+ return newsInfoService.publish(id, adminUserId);
+ } catch (Exception e) {
+ log.error("解析登陆Token出错!", e);
+ return ResultObject.failed(500, "服务端错误,请联系系统管理员!");
+ }
+ }
+
+ @GetMapping("/detail")
+ public ResultObject detail(@RequestHeader("auth-token") String token,
+ @RequestParam("id") String id) {
+ if (!StringUtils.hasText(token)) {
+ return ResultObject.failed("登陆Token为空!");
+ }
+
+ try {
+ AdminUser adminUser = AdminJwtService.parseToken(token);
+ if (adminUser == null) {
+ log.warn("解析token {}拿不到AdminUser对象!", token);
+ return ResultObject.failed("登陆Token有误,请联系系统管理员!");
+ }
+
+ Long adminUserId = adminUser.getId();
+ if (!adminUser.isActive()) {
+ log.warn("当前用户已被封禁! id = {}", adminUserId);
+ return ResultObject.failed("当前用户已被封禁!请联系系统管理员!");
+ }
+
+ log.info("path: /newsinfo/detail, method: GET, request user id: {}, news id: {}", adminUserId, id);
+ return newsInfoService.detail(id);
+ } catch (Exception e) {
+ log.error("解析登陆Tokn出错!", e);
+ return ResultObject.failed(500, "服务端错误,请联系系统管理员!");
+ }
+ }
+
+ @PostMapping("/delete")
+ public ResultObject deleteNews(@RequestHeader("auth-token") String token,
+ @RequestParam("id") String id) {
+ if (!StringUtils.hasText(token)) {
+ return ResultObject.failed("登陆Token为空!");
+ }
+
+ try {
+ AdminUser adminUser = AdminJwtService.parseToken(token);
+ if (adminUser == null) {
+ log.warn("解析token {}拿不到AdminUser对象!", token);
+ return ResultObject.failed("登陆Token有误,请联系系统管理员!");
+ }
+
+ Long adminUserId = adminUser.getId();
+ if (!adminUser.isActive()) {
+ log.warn("当前用户已被封禁! id = {}", adminUserId);
+ return ResultObject.failed("当前用户已被封禁!请联系系统管理员!");
+ }
+
+ log.info("path: /newsinfo, method: DELETE, request user id: {}, news id: {}", adminUserId, id);
+ return newsInfoService.deleteNewsInfo(id);
+ } catch (Exception e) {
+ log.error("解析登陆Token出错!", e);
+ return ResultObject.failed(500, "服务端错误,请联系系统管理员!");
+ }
+ }
+
+ @PostMapping("/save")
+ public ResultObject saveDraft(@RequestHeader("auth-token") String token,
+ @RequestBody SaveNewsInfoDTO saveNewsDTO) {
+ if (!StringUtils.hasText(token)) {
+ return ResultObject.failed("登陆Token为空!");
+ }
+
+ try {
+ AdminUser adminUser = AdminJwtService.parseToken(token);
+ if (adminUser == null) {
+ log.warn("解析token {}拿不到AdminUser对象!", token);
+ return ResultObject.failed("登陆Token有误,请联系系统管理员!");
+ }
+
+ if (!adminUser.isActive()) {
+ log.warn("当前用户已被封禁! id = {}", adminUser.getId());
+ return ResultObject.failed("当前用户已被封禁!请联系系统管理员!");
+ }
+
+ log.info("path: /news/save, method: POST, request user id: {}, param: {}",
+ adminUser.getId(), objectMapper.writeValueAsString(saveNewsDTO));
+ } catch (Exception e) {
+ log.error("解析登陆Token出错!", e);
+ return ResultObject.failed(500, "服务端错误,请联系系统管理员!");
+ }
+
+ // 这个接口是保存和编辑,那status应该是2
+ saveNewsDTO.setStatus(2);
+ return newsInfoService.save(saveNewsDTO);
+ }
+
+ @GetMapping
+ public PageObject queryNewsInfo(@RequestHeader("auth-token") String token,
+ @RequestParam(value = "title", required = false) String title,
+ @RequestParam(value = "content", required = false) String content,
+ @RequestParam(value = "stockcode", required = false) String stockcode,
+ @RequestParam(value = "sourcename", required = false) Long sourcename,
+ @RequestParam(value = "page", required = false, defaultValue = "1") int page,
+ @RequestParam(value = "size", required = false, defaultValue = "10") int size,
+ @RequestParam(value = "last", required = false) Integer last,
+ @RequestParam(value = "current", required = false) Integer current,
+ @RequestParam(value = "orderBy", required = false, defaultValue = "createTime") String orderBy,
+ @RequestParam(value = "direction", required = false, defaultValue = "asc") String direction
+ ) {
+ if (!StringUtils.hasText(token)) {
+ return PageObject.failedPage("登陆Token为空!");
+ }
+
+ try {
+ AdminUser adminUser = AdminJwtService.parseToken(token);
+ if (adminUser == null) {
+ log.warn("解析token {}拿不到AdminUser对象!", token);
+ return PageObject.failedPage("登陆Token有误,请联系系统管理员!");
+ }
+
+ if (!adminUser.isActive()) {
+ log.warn("当前用户已被封禁! id = {}", adminUser.getId());
+ return PageObject.failedPage("当前用户已被封禁!请联系系统管理员!");
+ }
+
+ log.info("path: /newsinfo, method: GET, request user id: {}, title: {}, content: {}, stockcode: {}, sourcename: {}, " +
+ "page: {}, size: {}, last: {}, current: {}, orderBy: {}, direction: {}",
+ adminUser.getId(), title, content, stockcode,sourcename, page, size, last, current, orderBy, direction);
+ } catch (Exception e) {
+ log.error("解析登陆Token出错!", e);
+ return PageObject.failedPage(500, "服务端错误,请联系系统管理员!");
+ }
+
+ return newsInfoService.queryNewsInfo(title, content, stockcode, sourcename, page, size, last, current, orderBy, direction);
+ }
+
+
+}
diff --git a/admin/src/main/java/com/jinrui/reference/admin/model/dto/newsinfo/PublishNewsInfoDTO.java b/admin/src/main/java/com/jinrui/reference/admin/model/dto/newsinfo/PublishNewsInfoDTO.java
new file mode 100644
index 0000000..c93820d
--- /dev/null
+++ b/admin/src/main/java/com/jinrui/reference/admin/model/dto/newsinfo/PublishNewsInfoDTO.java
@@ -0,0 +1,13 @@
+package com.jinrui.reference.admin.model.dto.newsinfo;
+
+public class PublishNewsInfoDTO {
+ private String id;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+}
diff --git a/admin/src/main/resources/application.yml b/admin/src/main/resources/application.yml
index c2ab94d..f08fca3 100644
--- a/admin/src/main/resources/application.yml
+++ b/admin/src/main/resources/application.yml
@@ -32,3 +32,10 @@ redis:
host: 123.60.153.169
port: 6379
password: Xgf_redis
+elasticsearch:
+ scheme: http
+ host: 10.127.2.194
+ port: 9200
+ enable: true
+ username: elastic
+ password: Aa123456
\ No newline at end of file
diff --git a/core/pom.xml b/core/pom.xml
index 64ae0ba..43e2235 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -17,6 +17,8 @@
UTF-8
1.70
4.4.0
+ 8.12.2
+ 2.0.1
@@ -59,6 +61,31 @@
bcutil-jdk15on
${bouncycastle.version}
+
+ co.elastic.clients
+ elasticsearch-java
+ ${elasticsearch.version}
+
+
+ org.elasticsearch.client
+ elasticsearch-rest-client
+
+
+ jakarta.json-api
+ jakarta.json
+
+
+
+
+ org.elasticsearch.client
+ elasticsearch-rest-client
+ ${elasticsearch.version}
+
+
+ jakarta.json
+ jakarta.json-api
+ ${jakartajson.version}
+
diff --git a/core/src/main/java/com/jinrui/reference/core/ElasticSearchConfig.java b/core/src/main/java/com/jinrui/reference/core/ElasticSearchConfig.java
new file mode 100644
index 0000000..746795c
--- /dev/null
+++ b/core/src/main/java/com/jinrui/reference/core/ElasticSearchConfig.java
@@ -0,0 +1,90 @@
+package com.jinrui.reference.core;
+
+import org.apache.http.HttpHost;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.impl.client.BasicCredentialsProvider;
+import org.apache.http.message.BasicHeader;
+import org.apache.http.client.CredentialsProvider;
+import org.apache.http.Header;
+import org.elasticsearch.client.RestClient;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import co.elastic.clients.elasticsearch.ElasticsearchAsyncClient;
+import co.elastic.clients.elasticsearch.ElasticsearchClient;
+import co.elastic.clients.json.jackson.JacksonJsonpMapper;
+import co.elastic.clients.transport.ElasticsearchTransport;
+import co.elastic.clients.transport.rest_client.RestClientTransport;
+
+@Configuration
+public class ElasticSearchConfig {
+
+ @Value("${elasticsearch.scheme}")
+ private String scheme;
+
+ @Value("${elasticsearch.host}")
+ private String host;
+
+ @Value("${elasticsearch.port}")
+ private int port;
+
+ @Value("${elasticsearch.enable}")
+ private boolean enable;
+
+ @Value("${elasticsearch.username}")
+ private String username;
+
+ @Value("${elasticsearch.password}")
+ private String password;
+
+
+ @Bean
+ public ElasticsearchClient elasticsearchClient(){
+ ElasticsearchClient client = new ElasticsearchClient(null);
+ if (enable){
+ final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
+ if (username != null && !username.isEmpty() && password != null && !password.isEmpty()) {
+
+ //设置账号密码
+ credentialsProvider.setCredentials(
+ AuthScope.ANY, new UsernamePasswordCredentials(username, password));
+ }
+
+ Header[] defaultHeaders = new Header[]{new BasicHeader("header", "value")};
+ RestClient restClient = RestClient.builder(new HttpHost(host, port, scheme))
+ .setHttpClientConfigCallback(httpClientBuilder -> httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider))
+ .setDefaultHeaders(defaultHeaders)
+ .build();
+ ElasticsearchTransport transport = new RestClientTransport(restClient,new JacksonJsonpMapper());
+ client = new ElasticsearchClient(transport);
+ }
+ return client;
+
+ }
+
+ @Bean
+ public ElasticsearchAsyncClient elasticsearchAsyncClient(){
+ ElasticsearchAsyncClient syncClient = new ElasticsearchAsyncClient(null);
+ if (enable){
+ final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
+ if (username != null && !username.isEmpty() && password != null && !password.isEmpty()) {
+
+ //设置账号密码
+ credentialsProvider.setCredentials(
+ AuthScope.ANY, new UsernamePasswordCredentials(username, password));
+ }
+
+ Header[] defaultHeaders = new Header[]{new BasicHeader("header", "value")};
+ RestClient restClient = RestClient.builder(new HttpHost(host, port, scheme))
+ .setHttpClientConfigCallback(httpClientBuilder -> httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider))
+ .setDefaultHeaders(defaultHeaders)
+ .build();
+ ElasticsearchTransport transport = new RestClientTransport(restClient,new JacksonJsonpMapper());
+ syncClient = new ElasticsearchAsyncClient(transport);
+ }
+ return syncClient;
+
+ }
+}
diff --git a/core/src/main/java/com/jinrui/reference/core/mapper/NewsMapper.java b/core/src/main/java/com/jinrui/reference/core/mapper/NewsMapper.java
index e9a7d32..87f60e2 100644
--- a/core/src/main/java/com/jinrui/reference/core/mapper/NewsMapper.java
+++ b/core/src/main/java/com/jinrui/reference/core/mapper/NewsMapper.java
@@ -4,6 +4,7 @@ import com.jinrui.reference.core.model.entity.News;
import com.jinrui.reference.core.model.entity.NewsDraft;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
+import org.apache.ibatis.annotations.Options;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
@@ -35,7 +36,7 @@ public interface NewsMapper {
@Update("update news set status = 2, publish_time = now(), editor_id = #{editorId} where id = #{id}")
void simplePublish(@Param("id") long id, @Param("editorId") long editorId);
-
+
@Update("update news " +
"set draft_id = #{draftId}," +
"editor_id = #{editorId}," +
@@ -45,6 +46,7 @@ public interface NewsMapper {
"content = #{content}," +
"content_text = #{contentText}," +
"status = #{status}," +
+ "newsinfo_id = #{newsinfoId}," +
"update_time = now() " +
"where id = #{id}")
void updateNews(News news);
@@ -63,7 +65,7 @@ public interface NewsMapper {
"where id = #{id}")
void publishNews(News news);
- @Select("select id, draft_id as draftId, status from news where id = #{id}")
+ @Select("select id, draft_id as draftId, status, newsinfo_id as newsinfoId from news where id = #{id}")
News getById(@Param("id") Long id);
@Select("select last_insert_id()")
@@ -73,8 +75,9 @@ public interface NewsMapper {
"values (#{title}, #{summary}, #{picture}, #{type}, #{content}, now(), now())")
void saveDraft(NewsDraft newsDraft);
- @Insert("insert into news(draft_id, title, summary, picture, type, content, create_time, update_time, status, publish_time, content_text)" +
- "values (#{draftId}, #{title}, #{summary}, #{picture}, #{type}, #{content}, now(), now(), #{status}, #{publishTime}, #{contentText})")
+ @Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id")
+ @Insert("insert into news(draft_id, title, summary, picture, type, content, create_time, update_time, status, publish_time, content_text, newsinfo_id)" +
+ "values (#{draftId}, #{title}, #{summary}, #{picture}, #{type}, #{content}, now(), now(), #{status}, #{publishTime}, #{contentText}, #{newsinfoId})")
void saveNews(News news);
diff --git a/core/src/main/java/com/jinrui/reference/core/mapper/TagMapper.java b/core/src/main/java/com/jinrui/reference/core/mapper/TagMapper.java
index 4ee0b2d..753c270 100644
--- a/core/src/main/java/com/jinrui/reference/core/mapper/TagMapper.java
+++ b/core/src/main/java/com/jinrui/reference/core/mapper/TagMapper.java
@@ -25,6 +25,10 @@ public interface TagMapper {
})
@Select("select * from tag")
List queryAll();
+
+
+ @Select("select id, parent_id as parentId, name from tag where id = #{id}")
+ Tag queryById(@Param("id") Long id);
@Select("select id, draft_id as draftId, tag_id as tagId " +
"from draft_tag_rel where draft_id = #{draftId}")
diff --git a/core/src/main/java/com/jinrui/reference/core/model/dto/news/SaveNewsDTO.java b/core/src/main/java/com/jinrui/reference/core/model/dto/news/SaveNewsDTO.java
index 17e69b9..60b4ca7 100644
--- a/core/src/main/java/com/jinrui/reference/core/model/dto/news/SaveNewsDTO.java
+++ b/core/src/main/java/com/jinrui/reference/core/model/dto/news/SaveNewsDTO.java
@@ -3,6 +3,10 @@ package com.jinrui.reference.core.model.dto.news;
import java.util.Date;
import java.util.List;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.ObjectUtils;
+
+import com.jinrui.reference.core.model.entity.NewsInfo;
import com.jinrui.reference.core.model.vo.news.NewsDetailVO;
@SuppressWarnings("unused")
@@ -128,4 +132,16 @@ public class SaveNewsDTO {
public void setIndustries(List industries) {
this.industries = industries;
}
+
+ public NewsInfo toNewsInfo() {
+ NewsInfo newsInfo = new NewsInfo();
+ newsInfo.setTitle(this.getTitle());
+ newsInfo.setSummary(this.getSummary());
+ newsInfo.setStatus(this.getStatus());
+ newsInfo.setContent(this.getContent());
+ newsInfo.setCreateTime(new Date());
+ newsInfo.setUpdateTime(newsInfo.getCreateTime());
+ newsInfo.setInputDate(newsInfo.getCreateTime());
+ return newsInfo;
+ }
}
diff --git a/core/src/main/java/com/jinrui/reference/core/model/entity/Industry.java b/core/src/main/java/com/jinrui/reference/core/model/entity/Industry.java
index d5fde6a..1f43db0 100644
--- a/core/src/main/java/com/jinrui/reference/core/model/entity/Industry.java
+++ b/core/src/main/java/com/jinrui/reference/core/model/entity/Industry.java
@@ -3,6 +3,7 @@ package com.jinrui.reference.core.model.entity;
import java.util.Date;
import org.apache.ibatis.annotations.Param;
+import org.springframework.util.ObjectUtils;
/**
* 行业分类
@@ -86,4 +87,11 @@ public class Industry {
public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
+
+ public String getDisplayName() {
+ if (ObjectUtils.isEmpty(this.getSecondaryName())) {
+ return this.getPrimaryName();
+ }
+ return this.getSecondaryName();
+ }
}
diff --git a/core/src/main/java/com/jinrui/reference/core/model/entity/News.java b/core/src/main/java/com/jinrui/reference/core/model/entity/News.java
index dfdcf37..0c3a291 100644
--- a/core/src/main/java/com/jinrui/reference/core/model/entity/News.java
+++ b/core/src/main/java/com/jinrui/reference/core/model/entity/News.java
@@ -70,8 +70,13 @@ public class News {
* 修改时间
*/
private Date updateTime;
+
+ /**
+ * 关联的全量资讯es文档ID
+ */
+ private String newsinfoId;
- public News() {}
+ public News() {}
public News(SaveNewsDTO saveNewsDTO) {
this.id = saveNewsDTO.getId();
@@ -191,4 +196,12 @@ public class News {
public void setContentText(String contentText) {
this.contentText = contentText;
}
+
+ public String getNewsinfoId() {
+ return newsinfoId;
+ }
+
+ public void setNewsinfoId(String newsinfoId) {
+ this.newsinfoId = newsinfoId;
+ }
}
diff --git a/core/src/main/java/com/jinrui/reference/core/model/entity/NewsInfo.java b/core/src/main/java/com/jinrui/reference/core/model/entity/NewsInfo.java
new file mode 100644
index 0000000..acc5036
--- /dev/null
+++ b/core/src/main/java/com/jinrui/reference/core/model/entity/NewsInfo.java
@@ -0,0 +1,323 @@
+package com.jinrui.reference.core.model.entity;
+
+import java.util.Date;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * 全量资讯
+ */
+public class NewsInfo {
+
+ public static final String INDEX_NAME = "news_info";
+ public static final String TITLE_ES_NAME = "title_txt";
+ public static final String SUMMARY_ES_NAME = "abstract";
+ public static final String CONTENT_ES_NAME = "CN_content";
+ public static final String SOURCE_ES_NAME = "sourcename.keyword";
+ public static final String DELETED_ES_NAME = "deleted.keyword";
+
+ /**
+ * 全量资讯ID
+ */
+ private String id;
+
+ /**
+ * 资讯精选ID
+ */
+ @JsonProperty("news_id")
+ private Long newsId;
+
+ /**
+ * 数据输入时间
+ */
+ @JsonProperty("input_date")
+ private Date inputDate;
+
+ private Integer words;
+
+ /**
+ * 关键词列表(分号分割)
+ */
+ @JsonProperty("key_word")
+ private String keyword;
+
+ /**
+ * 标题
+ */
+ @JsonProperty(TITLE_ES_NAME)
+ private String title;
+
+ @JsonProperty("title_EN")
+ private String englishTitle;
+
+ /**
+ * 摘要
+ */
+ @JsonProperty(SUMMARY_ES_NAME)
+ private String summary;
+
+ /**
+ * 原文链接
+ */
+ @JsonProperty("URL")
+ private String url;
+
+ /**
+ * 数据来源名称或标识
+ */
+ private String sourcename;
+
+ /**
+ * 语言
+ */
+ private String lang;
+
+ /**
+ * H5富文本
+ */
+ @JsonProperty(CONTENT_ES_NAME)
+ private String content;
+
+ @JsonProperty("EN_content")
+ private String englishContent;
+
+ /**
+ * 分类信息json格式
+ */
+ @JsonProperty("category")
+ private String industry;
+
+ /**
+ * 是否删除 0 正常, 1 已删除
+ */
+ private Integer deleted = 0;
+
+ /**
+ * 文件日期(取自目录结构)
+ */
+ @JsonProperty("file_date")
+ private Date fileDate;
+
+ /**
+ * 完整文件名(含扩展名)
+ */
+ @JsonProperty("file_name")
+ private String fileName;
+
+ /**
+ * 新闻状态 0-草稿 | 1-未发布 | 2-已发布
+ */
+ private Integer status;
+
+ /**
+ * 创建时间
+ */
+ @JsonProperty("create_time")
+ private Date createTime;
+
+ /**
+ * 发布时间
+ */
+ private Date publishTime;
+
+ /**
+ * 修改时间
+ */
+ @JsonProperty("update_time")
+ private Date updateTime;
+
+ /**
+ * 编辑ID - 最后一个点击发布的人的ID
+ */
+ private Long editorId;
+
+ public NewsInfo() {}
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public Long getEditorId() {
+ return editorId;
+ }
+
+ public void setEditorId(Long editorId) {
+ this.editorId = editorId;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public String getSummary() {
+ return summary;
+ }
+
+ public void setSummary(String summary) {
+ this.summary = summary;
+ }
+
+ public String getContent() {
+ return content;
+ }
+
+ public void setContent(String content) {
+ this.content = content;
+ }
+
+ public Integer getStatus() {
+ return status;
+ }
+
+ public void setStatus(Integer status) {
+ this.status = status;
+ }
+
+ public Date getCreateTime() {
+ return createTime;
+ }
+
+ public void setCreateTime(Date createTime) {
+ this.createTime = createTime;
+ }
+
+ public Date getPublishTime() {
+ return publishTime;
+ }
+
+ public void setPublishTime(Date publishTime) {
+ this.publishTime = publishTime;
+ }
+
+ public Date getUpdateTime() {
+ return updateTime;
+ }
+
+ public void setUpdateTime(Date updateTime) {
+ this.updateTime = updateTime;
+ }
+
+ public String getUrl() {
+ return url;
+ }
+
+ public void setUrl(String url) {
+ this.url = url;
+ }
+
+ public String getSourcename() {
+ return sourcename;
+ }
+
+ public void setSourcename(String sourcename) {
+ this.sourcename = sourcename;
+ }
+
+ public String getLang() {
+ return lang;
+ }
+
+ public void setLang(String lang) {
+ this.lang = lang;
+ }
+
+ public Integer getDeleted() {
+ return deleted;
+ }
+
+ public void setDeleted(Integer deleted) {
+ this.deleted = deleted;
+ }
+
+ public Long getNewsId() {
+ return newsId;
+ }
+
+ public void setNewsId(Long newsId) {
+ this.newsId = newsId;
+ }
+
+ public Date getInputDate() {
+ return inputDate;
+ }
+
+ public void setInputDate(Date inputDate) {
+ this.inputDate = inputDate;
+ }
+
+ public String getKeyword() {
+ return keyword;
+ }
+
+ public void setKeyword(String keyword) {
+ this.keyword = keyword;
+ }
+
+ public String getEnglishTitle() {
+ return englishTitle;
+ }
+
+ public void setEnglishTitle(String englishTitle) {
+ this.englishTitle = englishTitle;
+ }
+
+ public String getEnglishContent() {
+ return englishContent;
+ }
+
+ public void setEnglishContent(String englishContent) {
+ this.englishContent = englishContent;
+ }
+
+ public String getIndustry() {
+ return industry;
+ }
+
+ public void setIndustry(String industry) {
+ this.industry = industry;
+ }
+
+ public Date getFileDate() {
+ return fileDate;
+ }
+
+ public void setFileDate(Date fileDate) {
+ this.fileDate = fileDate;
+ }
+
+ public String getFileName() {
+ return fileName;
+ }
+
+ public void setFileName(String fileName) {
+ this.fileName = fileName;
+ }
+
+ public Integer getWords() {
+ return words;
+ }
+
+ public void setWords(Integer words) {
+ this.words = words;
+ }
+
+ public News toNews() {
+ News news = new News();
+ news.setTitle(this.getTitle());
+ news.setContent(this.getContent());
+ news.setStatus(this.getStatus());
+ news.setSummary(this.getSummary());
+ news.setPublishTime(new Date());
+ news.setType(1);
+ news.setNewsinfoId(this.getId());
+ return news;
+ }
+}
diff --git a/core/src/main/java/com/jinrui/reference/core/model/vo/newsinfo/NewsInfoDetailVO.java b/core/src/main/java/com/jinrui/reference/core/model/vo/newsinfo/NewsInfoDetailVO.java
new file mode 100644
index 0000000..1e4f559
--- /dev/null
+++ b/core/src/main/java/com/jinrui/reference/core/model/vo/newsinfo/NewsInfoDetailVO.java
@@ -0,0 +1,222 @@
+package com.jinrui.reference.core.model.vo.newsinfo;
+
+import java.util.Date;
+import java.util.List;
+
+import com.jinrui.reference.core.model.entity.NewsInfo;
+import com.jinrui.reference.core.model.vo.news.NewsDetailIndustry;
+import com.jinrui.reference.core.model.vo.news.NewsDetailTag;
+
+public class NewsInfoDetailVO {
+ /**
+ * 全量资讯ID
+ */
+ private String id;
+
+ /**
+ * 资讯精选ID
+ */
+ private Long newsId;
+
+ /**
+ * 数据输入时间
+ */
+ private Date inputDate;
+
+ /**
+ * 关键词列表(分号分割)
+ */
+ private String keyword;
+
+ /**
+ * 标题
+ */
+ private String title;
+
+
+ /**
+ * 摘要
+ */
+ private String summary;
+
+ /**
+ * 数据来源名称或标识
+ */
+ private NewsDetailTag sourcename;
+
+ /**
+ * H5富文本
+ */
+ private String content;
+
+ /**
+ * 分类信息json格式
+ */
+ private List industry;
+
+ /**
+ * 文件日期(取自目录结构)
+ */
+ private Date fileDate;
+
+ /**
+ * 新闻状态 0-草稿 | 1-未发布 | 2-已发布
+ */
+ private Integer status;
+
+ /**
+ * 创建时间
+ */
+ private Date createTime;
+
+ /**
+ * 发布时间
+ */
+ private Date publishTime;
+
+ /**
+ * 修改时间
+ */
+ private Date updateTime;
+
+ /**
+ * 编辑ID - 最后一个点击发布的人的ID
+ */
+ private Long editorId;
+
+
+
+ public NewsInfoDetailVO() {}
+
+ public NewsInfoDetailVO(NewsInfo newsInfo) {
+ this.id = newsInfo.getId();
+ this.newsId = newsInfo.getNewsId();
+ this.title = newsInfo.getTitle();
+ this.summary = newsInfo.getSummary();
+ this.status = (newsInfo.getStatus() == null ? 1: newsInfo.getStatus());
+ this.content = newsInfo.getContent();
+ this.publishTime = newsInfo.getPublishTime();
+ this.createTime = newsInfo.getCreateTime();
+ this.updateTime = newsInfo.getUpdateTime();
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public Long getEditorId() {
+ return editorId;
+ }
+
+ public void setEditorId(Long editorId) {
+ this.editorId = editorId;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public String getSummary() {
+ return summary;
+ }
+
+ public void setSummary(String summary) {
+ this.summary = summary;
+ }
+
+ public String getContent() {
+ return content;
+ }
+
+ public void setContent(String content) {
+ this.content = content;
+ }
+
+ public Integer getStatus() {
+ return status;
+ }
+
+ public void setStatus(Integer status) {
+ this.status = status;
+ }
+
+ public Date getCreateTime() {
+ return createTime;
+ }
+
+ public void setCreateTime(Date createTime) {
+ this.createTime = createTime;
+ }
+
+ public Date getPublishTime() {
+ return publishTime;
+ }
+
+ public void setPublishTime(Date publishTime) {
+ this.publishTime = publishTime;
+ }
+
+ public Date getUpdateTime() {
+ return updateTime;
+ }
+
+ public void setUpdateTime(Date updateTime) {
+ this.updateTime = updateTime;
+ }
+
+ public NewsDetailTag getSourcename() {
+ return sourcename;
+ }
+
+ public void setSourcename(NewsDetailTag sourcename) {
+ this.sourcename = sourcename;
+ }
+
+ public Long getNewsId() {
+ return newsId;
+ }
+
+ public void setNewsId(Long newsId) {
+ this.newsId = newsId;
+ }
+
+ public Date getInputDate() {
+ return inputDate;
+ }
+
+ public void setInputDate(Date inputDate) {
+ this.inputDate = inputDate;
+ }
+
+ public String getKeyword() {
+ return keyword;
+ }
+
+ public void setKeyword(String keyword) {
+ this.keyword = keyword;
+ }
+
+ public List getIndustry() {
+ return industry;
+ }
+
+ public void setIndustry(List industry) {
+ this.industry = industry;
+ }
+
+ public Date getFileDate() {
+ return fileDate;
+ }
+
+ public void setFileDate(Date fileDate) {
+ this.fileDate = fileDate;
+ }
+}
diff --git a/core/src/main/java/com/jinrui/reference/core/model/vo/newsinfo/NewsInfoVO.java b/core/src/main/java/com/jinrui/reference/core/model/vo/newsinfo/NewsInfoVO.java
new file mode 100644
index 0000000..d4902ce
--- /dev/null
+++ b/core/src/main/java/com/jinrui/reference/core/model/vo/newsinfo/NewsInfoVO.java
@@ -0,0 +1,194 @@
+package com.jinrui.reference.core.model.vo.newsinfo;
+
+import java.util.Date;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.jinrui.reference.core.model.entity.NewsInfo;
+
+public class NewsInfoVO {
+ /**
+ * 全量资讯ID
+ */
+ private String id;
+
+ /**
+ * 资讯精选ID
+ */
+ private Long newsId;
+
+ /**
+ * 数据输入时间
+ */
+ private Date inputDate;
+
+ /**
+ * 标题
+ */
+ private String title;
+
+ /**
+ * 摘要
+ */
+ private String summary;
+
+ /**
+ * 正文
+ */
+ private String content;
+
+ /**
+ * 数据来源名称或标识
+ */
+ private String sourcename;
+
+ /**
+ * 文件日期(取自目录结构)
+ */
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
+ private Date fileDate;
+
+ /**
+ * 新闻状态 0-草稿 | 1-未发布 | 2-已发布
+ */
+ private Integer status;
+
+ /**
+ * 创建时间
+ */
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
+ private Date createTime;
+
+ /**
+ * 发布时间
+ */
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
+ private Date publishTime;
+
+ /**
+ * 修改时间
+ */
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
+ private Date updateTime;
+
+ /**
+ * 编辑ID - 最后一个点击发布的人的ID
+ */
+ private Long editorId;
+
+
+ public NewsInfoVO(NewsInfo newsInfo) {
+ this.id = newsInfo.getId();
+ this.title = newsInfo.getTitle();
+ this.summary = newsInfo.getSummary();
+ this.status = newsInfo.getStatus();
+ this.sourcename = newsInfo.getSourcename();
+ this.content = newsInfo.getContent();
+ this.publishTime = newsInfo.getPublishTime();
+ this.createTime = newsInfo.getCreateTime();
+ this.updateTime = newsInfo.getUpdateTime();
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public Long getEditorId() {
+ return editorId;
+ }
+
+ public void setEditorId(Long editorId) {
+ this.editorId = editorId;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public String getSummary() {
+ return summary;
+ }
+
+ public void setSummary(String summary) {
+ this.summary = summary;
+ }
+
+ public Integer getStatus() {
+ return status;
+ }
+
+ public void setStatus(Integer status) {
+ this.status = status;
+ }
+
+ public Date getCreateTime() {
+ return createTime;
+ }
+
+ public void setCreateTime(Date createTime) {
+ this.createTime = createTime;
+ }
+
+ public Date getPublishTime() {
+ return publishTime;
+ }
+
+ public void setPublishTime(Date publishTime) {
+ this.publishTime = publishTime;
+ }
+
+ public Date getUpdateTime() {
+ return updateTime;
+ }
+
+ public void setUpdateTime(Date updateTime) {
+ this.updateTime = updateTime;
+ }
+
+ public String getSourcename() {
+ return sourcename;
+ }
+
+ public void setSourcename(String sourcename) {
+ this.sourcename = sourcename;
+ }
+
+ public Long getNewsId() {
+ return newsId;
+ }
+
+ public void setNewsId(Long newsId) {
+ this.newsId = newsId;
+ }
+
+ public Date getInputDate() {
+ return inputDate;
+ }
+
+ public void setInputDate(Date inputDate) {
+ this.inputDate = inputDate;
+ }
+
+ public Date getFileDate() {
+ return fileDate;
+ }
+
+ public void setFileDate(Date fileDate) {
+ this.fileDate = fileDate;
+ }
+
+ public String getContent() {
+ return content;
+ }
+
+ public void setContent(String content) {
+ this.content = content;
+ }
+}
diff --git a/core/src/main/java/com/jinrui/reference/core/model/vo/newsinfo/SaveNewsInfoDTO.java b/core/src/main/java/com/jinrui/reference/core/model/vo/newsinfo/SaveNewsInfoDTO.java
new file mode 100644
index 0000000..d0f3da0
--- /dev/null
+++ b/core/src/main/java/com/jinrui/reference/core/model/vo/newsinfo/SaveNewsInfoDTO.java
@@ -0,0 +1,151 @@
+package com.jinrui.reference.core.model.vo.newsinfo;
+
+import java.util.Date;
+import java.util.List;
+
+import com.jinrui.reference.core.model.dto.news.SaveDraftColumn;
+import com.jinrui.reference.core.model.dto.news.SaveDraftTag;
+import com.jinrui.reference.core.model.entity.News;
+import com.jinrui.reference.core.model.entity.NewsInfo;
+import com.jinrui.reference.core.model.vo.news.NewsDetailVO;
+
+@SuppressWarnings("unused")
+public class SaveNewsInfoDTO {
+
+ private String id;
+
+ private String title;
+
+ private String summary;
+
+ private String picture;
+
+ private SaveDraftTag tag;
+
+ private SaveDraftColumn column;
+
+ private List industries;
+
+
+ private String content;
+ // 不含html标签的纯文本
+ private String contentText;
+ private Integer status;
+ private Date publishTime;
+
+ public SaveNewsInfoDTO() {}
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public String getSummary() {
+ return summary;
+ }
+
+ public void setSummary(String summary) {
+ this.summary = summary;
+ }
+
+ public String getPicture() {
+ return picture;
+ }
+
+ public void setPicture(String picture) {
+ this.picture = picture;
+ }
+
+ public SaveDraftTag getTag() {
+ return tag;
+ }
+
+ public void setTag(SaveDraftTag tag) {
+ this.tag = tag;
+ }
+
+ public SaveDraftColumn getColumn() {
+ return column;
+ }
+
+ public void setColumn(SaveDraftColumn column) {
+ this.column = column;
+ }
+
+ public String getContent() {
+ return content;
+ }
+
+ public void setContent(String content) {
+ this.content = content;
+ }
+
+ public Integer getStatus() {
+ return status;
+ }
+
+ public void setStatus(Integer status) {
+ this.status = status;
+ }
+
+ public Date getPublishTime() {
+ return publishTime;
+ }
+
+ public void setPublishTime(Date publishTime) {
+ this.publishTime = publishTime;
+ }
+
+ public String getContentText() {
+ return contentText;
+ }
+
+ public void setContentText(String contentText) {
+ this.contentText = contentText;
+ }
+
+ public List getIndustries() {
+ return industries;
+ }
+
+ public void setIndustries(List industries) {
+ this.industries = industries;
+ }
+
+ public NewsInfo toNewsInfo() {
+ NewsInfo newsInfo = new NewsInfo();
+ newsInfo.setId(this.getId());
+ newsInfo.setTitle(this.getTitle());
+ newsInfo.setSummary(this.getSummary());
+ newsInfo.setStatus(this.getStatus());
+ newsInfo.setContent(this.getContent());
+ newsInfo.setCreateTime(new Date());
+ newsInfo.setUpdateTime(newsInfo.getCreateTime());
+ newsInfo.setInputDate(newsInfo.getCreateTime());
+ return newsInfo;
+ }
+
+ public News toNews() {
+ News news = new News();
+ news.setTitle(this.getTitle());
+ news.setSummary(this.getSummary());
+ news.setStatus(this.getStatus());
+ news.setContent(this.getContent());
+ news.setCreateTime(new Date());
+ news.setUpdateTime(news.getCreateTime());
+ news.setType(1);
+ news.setNewsinfoId(this.getId());
+ return news;
+ }
+}
diff --git a/core/src/main/java/com/jinrui/reference/core/service/NewsInfoService.java b/core/src/main/java/com/jinrui/reference/core/service/NewsInfoService.java
new file mode 100644
index 0000000..0020490
--- /dev/null
+++ b/core/src/main/java/com/jinrui/reference/core/service/NewsInfoService.java
@@ -0,0 +1,513 @@
+package com.jinrui.reference.core.service;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.ObjectUtils;
+import org.springframework.util.StringUtils;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.jinrui.reference.core.mapper.IndustryMapper;
+import com.jinrui.reference.core.mapper.NewsMapper;
+import com.jinrui.reference.core.mapper.TagMapper;
+import com.jinrui.reference.core.model.dto.news.SaveDraftTag;
+import com.jinrui.reference.core.model.entity.Industry;
+import com.jinrui.reference.core.model.entity.News;
+import com.jinrui.reference.core.model.entity.NewsIndustryRel;
+import com.jinrui.reference.core.model.entity.NewsInfo;
+import com.jinrui.reference.core.model.entity.NewsTagRel;
+import com.jinrui.reference.core.model.entity.Tag;
+import com.jinrui.reference.core.model.vo.PageObject;
+import com.jinrui.reference.core.model.vo.ResultObject;
+import com.jinrui.reference.core.model.vo.news.NewsDetailIndustry;
+import com.jinrui.reference.core.model.vo.news.NewsDetailTag;
+import com.jinrui.reference.core.model.vo.news.NewsDetailTagItem;
+import com.jinrui.reference.core.model.vo.newsinfo.NewsInfoDetailVO;
+import com.jinrui.reference.core.model.vo.newsinfo.NewsInfoVO;
+import com.jinrui.reference.core.model.vo.newsinfo.SaveNewsInfoDTO;
+
+import co.elastic.clients.elasticsearch.ElasticsearchClient;
+import co.elastic.clients.elasticsearch._types.ElasticsearchException;
+import co.elastic.clients.elasticsearch._types.Refresh;
+import co.elastic.clients.elasticsearch._types.SortOrder;
+import co.elastic.clients.elasticsearch._types.query_dsl.MatchQuery;
+import co.elastic.clients.elasticsearch._types.query_dsl.MultiMatchQuery;
+import co.elastic.clients.elasticsearch._types.query_dsl.Operator;
+import co.elastic.clients.elasticsearch._types.query_dsl.Query;
+import co.elastic.clients.elasticsearch._types.query_dsl.QueryBuilders;
+import co.elastic.clients.elasticsearch._types.query_dsl.TermQuery;
+import co.elastic.clients.elasticsearch.core.GetResponse;
+import co.elastic.clients.elasticsearch.core.IndexResponse;
+import co.elastic.clients.elasticsearch.core.SearchResponse;
+import co.elastic.clients.elasticsearch.core.search.Hit;
+import co.elastic.clients.elasticsearch.core.search.HitsMetadata;
+
+@Service
+public class NewsInfoService {
+
+ private static final Logger log = LoggerFactory.getLogger(NewsInfoService.class);
+
+ private final ElasticsearchClient elasticsearchClient;
+ private final ObjectMapper objectMapper;
+ private final TagMapper tagMapper;
+ private final IndustryMapper industryMapper;
+ private final NewsMapper newsMapper;
+
+ public NewsInfoService(ElasticsearchClient elasticsearchClient,
+ ObjectMapper objectMapper,
+ TagMapper tagMapper,
+ IndustryMapper industryMapper,
+ NewsMapper newsMapper) {
+ this.elasticsearchClient = elasticsearchClient;
+ this.objectMapper = objectMapper;
+ this.tagMapper = tagMapper;
+ this.industryMapper = industryMapper;
+ this.newsMapper = newsMapper;
+ }
+
+ public ResultObject publish(String id, long editorId) {
+ try {
+ GetResponse newsInfoResp = elasticsearchClient.get(g -> g.index(NewsInfo.INDEX_NAME).id(String.valueOf(id)), NewsInfo.class);
+ if (!newsInfoResp.found()) {
+ return ResultObject.success();
+ }
+
+ NewsInfo newsInfo = newsInfoResp.source();
+ newsInfo.setId(id);
+ if (newsInfo.getStatus() == null ||newsInfo.getStatus().intValue() == 1) {
+ newsInfo.setStatus(2);
+ } else {
+ newsInfo.setStatus(1);
+ }
+
+ News relateNews = newsMapper.getNewsDetail(newsInfo.getNewsId());
+
+ if (relateNews == null||!Objects.equals(relateNews.getTitle(), newsInfo.getTitle())) {
+ // 插入数据到News表中
+ News relNews = newsInfo.toNews();
+ relNews.setEditorId(editorId);
+ newsMapper.saveNews(relNews);
+ newsInfo.setNewsId(relNews.getId());
+
+
+ String industry = newsInfo.getIndustry();
+ if (!ObjectUtils.isEmpty(industry)) {
+ List industries = industryMapper.queryAll();
+ for (Industry industryItem: industries) {
+ if (industry.equals(industryItem.getPrimaryName()) || industryItem.equals(industryItem.getSecondaryName())) {
+ industryMapper.saveNewsIndustryRel(newsInfo.getNewsId(), industryItem.getId());
+ }
+ }
+ }
+
+ String sourcename = newsInfo.getSourcename();
+ if (!ObjectUtils.isEmpty(sourcename)) {
+ List matchedTags = tagMapper.queryTag(1L, sourcename, null, "create_time", null);
+ for (Tag tag: matchedTags) {
+ if (tag.getName().equals(sourcename)) {
+ tagMapper.saveNewsTagRel(newsInfo.getNewsId(), tag.getId());
+ }
+ }
+ }
+ } else {
+ if (newsInfo.getStatus().intValue() == 2) {
+ newsMapper.simplePublish(newsInfo.getNewsId(), editorId);
+ } else {
+ newsMapper.simpleUnpublish(newsInfo.getNewsId());
+ }
+ }
+
+ NewsInfo publishedNewsInfo = new NewsInfo();
+ publishedNewsInfo.setStatus(newsInfo.getStatus());
+ publishedNewsInfo.setNewsId(newsInfo.getNewsId());
+
+ elasticsearchClient.update(e -> e.index(NewsInfo.INDEX_NAME).refresh(Refresh.True).id(String.valueOf(id)).doc(publishedNewsInfo), NewsInfo.class);
+
+ } catch(IOException e) {
+ log.error("全量资讯发布失败!", e);
+ return ResultObject.failed(500, "服务器错误,请联系系统管理员!");
+ }
+
+ return ResultObject.success();
+ }
+
+ public ResultObject detail(String id) {
+
+ try {
+ GetResponse newsInfoResp = elasticsearchClient.get(g -> g.index(NewsInfo.INDEX_NAME).id(String.valueOf(id)), NewsInfo.class);
+ if (!newsInfoResp.found()) {
+ return ResultObject.success();
+ }
+ NewsInfo newsInfo = newsInfoResp.source();
+ newsInfo.setId(id);
+
+ NewsInfoDetailVO newsInfoDetailVO = new NewsInfoDetailVO(newsInfo);
+
+ List allTags = tagMapper.queryAll();
+ Map tagMap = allTags.stream().collect(Collectors.toMap(Tag::getId, Function.identity()));
+ List allIndustrys = industryMapper.queryAll();
+
+
+ if (newsInfo.getStatus() == null ||newsInfo.getStatus() == 1) {
+ for (Tag tag: allTags) {
+ if (tag.getName().equals(newsInfo.getSourcename())) {
+ NewsDetailTag newsDetailTag = new NewsDetailTag();
+
+ NewsDetailTagItem sourceTagItem = new NewsDetailTagItem();
+ newsDetailTag.setSource(sourceTagItem);
+ sourceTagItem.setId(tag.getId());
+ sourceTagItem.setName(tag.getName());
+ newsInfoDetailVO.setSourcename(newsDetailTag);
+ }
+ }
+ String industryStr = newsInfo.getIndustry();
+ for (Industry industry: allIndustrys) {
+ if (Objects.equals(industry.getDisplayName(), industryStr)) {
+ NewsDetailIndustry newsDetailIndustry = new NewsDetailIndustry();
+ newsDetailIndustry.setId(newsDetailIndustry.getId());
+ newsDetailIndustry.setPrimaryName(newsDetailIndustry.getPrimaryName());
+ newsDetailIndustry.setSecondaryName(newsDetailIndustry.getSecondaryName());
+ newsInfoDetailVO.setIndustry(Arrays.asList(newsDetailIndustry));
+ }
+ }
+
+ } else {
+ Map industryMap = allIndustrys.stream().collect(Collectors.toMap(Industry::getId, Function.identity()));
+
+ List tagRelList = tagMapper.getNewsTagRelList(newsInfo.getNewsId());
+ NewsDetailTag newsDetailTag = new NewsDetailTag();
+ newsInfoDetailVO.setSourcename(newsDetailTag);
+
+ if (!CollectionUtils.isEmpty(tagRelList)) {
+ List arr = new ArrayList<>();
+ for (NewsTagRel rel : tagRelList) {
+ Long tagId = rel.getTagId();
+ Tag tag = tagMap.get(tagId);
+ Long parentId = tag.getParentId();
+ NewsDetailTagItem tagItem = new NewsDetailTagItem();
+ tagItem.setId(tagId);
+ tagItem.setName(tag.getName());
+ if (parentId != null && parentId == 1) {
+ newsDetailTag.setSource(tagItem);
+ } else {
+ newsDetailTag.setField(tagItem);
+ arr.add(tagItem);
+ }
+ }
+ newsDetailTag.setFieldArr(arr);
+ }
+
+ List industryRelList = industryMapper.getNewsIndustryRelList(newsInfo.getNewsId());
+ List newsIndustryList = new ArrayList<>();
+ newsInfoDetailVO.setIndustry(newsIndustryList);
+ if (!CollectionUtils.isEmpty(industryRelList)) {
+ for (NewsIndustryRel rel: industryRelList) {
+ Long industryId = rel.getIndustryId();
+ Industry industry = industryMap.get(industryId);
+ NewsDetailIndustry newsDetailIndustry = new NewsDetailIndustry();
+ newsDetailIndustry.setId(industryId);
+ newsDetailIndustry.setPrimaryName(industry.getPrimaryName());
+ newsDetailIndustry.setSecondaryName(industry.getSecondaryName());
+ newsIndustryList.add(newsDetailIndustry);
+ }
+ }
+
+ }
+ return ResultObject.success(newsInfoDetailVO);
+ } catch(IOException e) {
+ log.error("获取全量资讯详情异常!", e);
+ return ResultObject.failed(500, "服务器错误,请联系系统管理员!");
+ }
+ }
+
+ public ResultObject save(SaveNewsInfoDTO saveNewsDTO) {
+ String id = saveNewsDTO.getId();
+ if (ObjectUtils.isEmpty(id)) {
+ return saveNewsInfo(saveNewsDTO);
+ }
+ return updateNewsInfo(saveNewsDTO);
+ }
+
+
+ private ResultObject updateNewsInfo(SaveNewsInfoDTO saveNewsInfoDTO) {
+ String id = saveNewsInfoDTO.getId();
+
+ try {
+ GetResponse getResp = elasticsearchClient.get(e -> e.index(NewsInfo.INDEX_NAME).id(String.valueOf(id)), NewsInfo.class);
+ if (getResp.found()) {
+ NewsInfo newsInfo = getResp.source();
+ newsInfo.setId(id);
+ Long newsId = newsInfo.getNewsId();
+ News relateNews = newsMapper.getNewsDetail(newsId);
+
+ if (relateNews == null ||!Objects.equals(newsInfo.getTitle(), relateNews.getTitle())) {
+ News news = createRelateNews(saveNewsInfoDTO);
+ newsId = news.getId();
+ } else {
+ updateRelateNews(newsId, saveNewsInfoDTO);
+ }
+ NewsInfo updatedNewsInfo = toNewsInfo(newsId, saveNewsInfoDTO);
+ elasticsearchClient.update(e -> e.index(NewsInfo.INDEX_NAME).id(updatedNewsInfo.getId()).doc(updatedNewsInfo).refresh(Refresh.True), NewsInfo.class);
+ }
+ } catch(IOException e) {
+ log.error("全量资讯更新异常!", e);
+ return ResultObject.failed(500, "服务器错误,请联系系统管理员!");
+ }
+ return ResultObject.success();
+ }
+
+ public ResultObject deleteNewsInfo(String newsInfoId) {
+ try {
+ GetResponse getResp = elasticsearchClient.get(e -> e.index(NewsInfo.INDEX_NAME).id(String.valueOf(newsInfoId)), NewsInfo.class);
+ if (getResp.found()) {
+ NewsInfo newsInfo = getResp.source();
+ Long newsId = newsInfo.getNewsId();
+ // 删除资讯精选中关联的数据
+ deleteNewsRel(newsId);
+ newsMapper.deleteNews(newsId);
+
+ NewsInfo deletedNewsInfo = new NewsInfo();
+ deletedNewsInfo.setDeleted(1);
+ elasticsearchClient.update(e -> e.index(NewsInfo.INDEX_NAME).refresh(Refresh.True).id(newsInfoId).doc(deletedNewsInfo), NewsInfo.class);
+ }
+
+ } catch(IOException e) {
+ log.error("删除全量资讯异常!", e);
+ return ResultObject.failed(500, "服务器错误,请联系系统管理员!");
+ }
+ return ResultObject.success();
+ }
+
+ private void deleteNewsRel(Long newsId) {
+ tagMapper.deleteNews(newsId);
+ industryMapper.deleteNews(newsId);
+ }
+
+ private ResultObject saveNewsInfo(SaveNewsInfoDTO saveNewsDTO) {
+ // 创建关联的资讯精选
+ News news = createRelateNews(saveNewsDTO);
+ NewsInfo newsInfo = toNewsInfo(news.getId(), saveNewsDTO);
+ try {
+ IndexResponse resp = elasticsearchClient.index(c -> c.index(NewsInfo.INDEX_NAME).document(newsInfo).refresh(Refresh.True));
+ news.setNewsinfoId(resp.id());
+ newsMapper.updateNews(news);
+ } catch(IOException|ElasticsearchException e) {
+ log.error("新建全量资讯出错!", e);
+ return ResultObject.failed(500, "服务器错误,请联系系统管理员!");
+ }
+ return ResultObject.success();
+ }
+
+ private NewsInfo toNewsInfo(Long newsId, SaveNewsInfoDTO saveNewsDTO) {
+ NewsInfo newsInfo = saveNewsDTO.toNewsInfo();
+ newsInfo.setNewsId(newsId);
+ List industries = saveNewsDTO.getIndustries();
+ if (!ObjectUtils.isEmpty(industries)) {
+ List allIndustry = industryMapper.queryAll();
+ Map industryMap = allIndustry.stream().collect(Collectors.toMap(Industry::getId, Function.identity()));
+ String industry = industries.stream().map(e -> industryMap.get(e)).map(Industry::getDisplayName).collect(Collectors.joining(";"));
+ newsInfo.setIndustry(industry);
+ }
+
+ SaveDraftTag tag = saveNewsDTO.getTag();
+ if (tag != null) {
+ if (tag.getSource() != null) {
+ Tag sourceTag = tagMapper.queryById(tag.getSource());
+ if (sourceTag != null) {
+ newsInfo.setSourcename(sourceTag.getName());
+ }
+ }
+ }
+ return newsInfo;
+ }
+
+ private void updateRelateNews(Long newsId, SaveNewsInfoDTO saveNewsDTO) {
+ News news = saveNewsDTO.toNews();
+ news.setId(newsId);
+ newsMapper.updateNews(news);
+
+ tagMapper.deleteNews(newsId);
+ industryMapper.deleteNews(newsId);
+
+ SaveDraftTag saveDraftTag = saveNewsDTO.getTag();
+ if (saveDraftTag != null) {
+ Long source = saveDraftTag.getSource();
+ if (source != null) {
+ tagMapper.saveNewsTagRel(newsId, source);
+ }
+ List fieldArr = saveDraftTag.getFieldArr();
+ if (fieldArr != null) {
+ for (Long field: fieldArr) {
+ tagMapper.saveNewsTagRel(newsId, field);
+ }
+ }
+ }
+
+ List industries = saveNewsDTO.getIndustries();
+ if (industries != null) {
+ for (Long industry: industries) {
+ tagMapper.saveNewsTagRel(newsId, industry);
+ }
+ }
+ }
+
+
+ private News createRelateNews(SaveNewsInfoDTO saveNewsInfoDTO) {
+ News news = saveNewsInfoDTO.toNews();
+ newsMapper.saveNews(news);
+
+ // 保存标签关系
+ try {
+ SaveDraftTag saveDraftTag = saveNewsInfoDTO.getTag();
+ if (saveDraftTag != null) {
+ Long source = saveDraftTag.getSource();
+ if (source != null) {
+ tagMapper.saveNewsTagRel(news.getId(), source);
+ }
+
+ // 频道标签多个,批量插
+ List fieldArr = saveDraftTag.getFieldArr();
+ if (fieldArr != null) {
+ for (Long item : fieldArr) {
+ tagMapper.saveNewsTagRel(news.getId(), item);
+ }
+ }
+ }
+ } catch (Exception e) {
+ log.error("保存发布全量资讯关联标签出错!", e);
+ }
+
+
+ // 保存新闻行业分类关系
+ try {
+ List saveDraftIndustries = saveNewsInfoDTO.getIndustries();
+ if (!CollectionUtils.isEmpty(saveDraftIndustries)) {
+ for (Long industryId : saveDraftIndustries) {
+ industryMapper.saveNewsIndustryRel(news.getId(), industryId);
+ }
+ }
+ } catch (Exception e) {
+ log.error("保存发布新闻行业分类出错!", e);
+ }
+ return news;
+ }
+
+ public PageObject queryNewsInfo(String title, String content, String stockcode, Long sourcename, int page, int size,
+ Integer last, Integer current, String orderBy, String direction) {
+
+ if (StringUtils.hasText(orderBy)) {
+ switch (orderBy) {
+ case "publishTime": {
+ orderBy = "publish_time";
+ break;
+ }
+ case "updateTime": {
+ orderBy = "update_time";
+ break;
+ }
+ case "createTime": {
+ orderBy = "create_time";
+ break;
+ }
+ }
+ }
+
+ final String orderByField = orderBy;
+ int offset = 0;
+ if (current != null) {
+ offset = (Math.max(0, current - 1)) * size;
+ }
+ int from = offset;
+ long total = 0;
+ List newsInfoList = new ArrayList<>();
+
+ try {
+ Query deletedQuery = TermQuery.of(t -> t.field(NewsInfo.DELETED_ES_NAME).value(0L))._toQuery();
+// BoolQuery boolQuery = BoolQuery.of(e -> e.filter(deletedQuery)); // QueryBuilders.bool().build()
+
+ List conditions = new ArrayList<>();
+ List filters = new ArrayList<>();
+ filters.add(deletedQuery);
+
+ if (!ObjectUtils.isEmpty(title)) {
+ Query titleQuery = MatchQuery.of(m -> m.field(NewsInfo.TITLE_ES_NAME).query(title))._toQuery(); // .analyzer("ik_max_word")
+ conditions.add(titleQuery);
+ }
+ if (!ObjectUtils.isEmpty(content)) {
+ Query summaryContentQuery = MultiMatchQuery.of(m -> m.fields(NewsInfo.CONTENT_ES_NAME, NewsInfo.SUMMARY_ES_NAME).query(content).operator(Operator.Or))._toQuery(); // .analyzer("ik_max_word")
+ conditions.add(summaryContentQuery);
+ }
+ if (sourcename != null) {
+ Tag sourcenameTag = tagMapper.queryById(sourcename);
+ if (sourcenameTag != null) {
+ Query sourcenameQuery = TermQuery.of(t -> t.field(NewsInfo.SOURCE_ES_NAME).value(sourcenameTag.getName()))._toQuery();
+ filters.add(sourcenameQuery);
+ }
+ }
+
+ if (conditions.size() == 0) {
+ conditions.add(QueryBuilders.matchAll().build()._toQuery());
+ }
+ Query resultQuery = new Query.Builder().bool(b -> b.must(conditions).filter(filters)).build();
+ SortOrder sortOrder = (Objects.equals(direction, SortOrder.Asc.jsonValue()) ?SortOrder.Asc:SortOrder.Desc);
+ SearchResponse searchResp = elasticsearchClient.search(s -> s.index(NewsInfo.INDEX_NAME)
+ .from(from) // 分页参数
+ .size(size) // 分页参数
+ .sort(so -> so.field(f -> f.field("_score").order(SortOrder.Desc).field(orderByField).order(sortOrder))) // 排序字段
+ .query(resultQuery)
+ .highlight(h -> h.preTags("")
+ .postTags("")
+ .requireFieldMatch(true)
+ .fields(NewsInfo.SUMMARY_ES_NAME, hf -> hf.numberOfFragments(0))
+ .fields(NewsInfo.CONTENT_ES_NAME, hf -> hf.numberOfFragments(0))
+ .fields(NewsInfo.TITLE_ES_NAME, hf -> hf.numberOfFragments(0))), NewsInfo.class);
+
+ HitsMetadata hits = searchResp.hits();
+ total = hits.total().value();
+ for (Hit hit: hits.hits()) {
+ NewsInfo newsInfo = hit.source();
+ Map> highlight = hit.highlight();
+
+ newsInfo.setTitle(highlight.get(NewsInfo.TITLE_ES_NAME) == null ? newsInfo.getTitle():highlight.get(NewsInfo.TITLE_ES_NAME).get(0));
+ newsInfo.setSummary(highlight.get(NewsInfo.SUMMARY_ES_NAME) == null ? newsInfo.getSummary():highlight.get(NewsInfo.SUMMARY_ES_NAME).get(0));
+ newsInfo.setContent(highlight.get(NewsInfo.CONTENT_ES_NAME) == null ? newsInfo.getContent():highlight.get(NewsInfo.CONTENT_ES_NAME).get(0));
+
+ newsInfo.setId(hit.id());
+ newsInfoList.add(newsInfo);
+ }
+ } catch (Exception e) {
+ log.error("搜索新闻异常!", e);
+ return PageObject.failedPage(500, "服务器错误,请联系系统管理员!");
+ }
+
+ PageObject pageObject = new PageObject<>();
+ pageObject.setTotal(Long.valueOf(total).intValue());
+ pageObject.setCode(200);
+ pageObject.setCurrent(page);
+ pageObject.setSize(Math.min(newsInfoList.size(), size));
+ if (CollectionUtils.isEmpty(newsInfoList)) {
+ log.info("搜索结果为空!");
+ pageObject.setData(new ArrayList<>());
+ return pageObject;
+ }
+
+ List resultList = new ArrayList<>();
+ for (NewsInfo newsInfo : newsInfoList) {
+ NewsInfoVO newsInfoVO = new NewsInfoVO(newsInfo);
+ resultList.add(newsInfoVO);
+ }
+
+ pageObject.setData(resultList);
+ return pageObject;
+ }
+}
diff --git a/core/src/main/java/com/jinrui/reference/core/service/NewsService.java b/core/src/main/java/com/jinrui/reference/core/service/NewsService.java
index a21b3aa..3f3bd33 100644
--- a/core/src/main/java/com/jinrui/reference/core/service/NewsService.java
+++ b/core/src/main/java/com/jinrui/reference/core/service/NewsService.java
@@ -1,5 +1,6 @@
package com.jinrui.reference.core.service;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
@@ -14,6 +15,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
+import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import com.fasterxml.jackson.core.JsonProcessingException;
@@ -36,6 +38,7 @@ import com.jinrui.reference.core.model.entity.News;
import com.jinrui.reference.core.model.entity.NewsColumnRel;
import com.jinrui.reference.core.model.entity.NewsDraft;
import com.jinrui.reference.core.model.entity.NewsIndustryRel;
+import com.jinrui.reference.core.model.entity.NewsInfo;
import com.jinrui.reference.core.model.entity.NewsTagRel;
import com.jinrui.reference.core.model.entity.Tag;
import com.jinrui.reference.core.model.vo.PageObject;
@@ -49,6 +52,9 @@ import com.jinrui.reference.core.model.vo.news.NewsDetailTagItem;
import com.jinrui.reference.core.model.vo.news.NewsDetailVO;
import com.jinrui.reference.core.model.vo.news.NewsVO;
+import co.elastic.clients.elasticsearch.ElasticsearchClient;
+import co.elastic.clients.elasticsearch._types.Refresh;
+
@Service
public class NewsService {
@@ -59,17 +65,20 @@ public class NewsService {
private final ObjectMapper objectMapper;
private final TagMapper tagMapper;
private final IndustryMapper industryMapper;
+ private final ElasticsearchClient elasticsearchClient;
public NewsService(NewsMapper newsMapper,
ColumnMapper columnMapper,
ObjectMapper objectMapper,
TagMapper tagMapper,
- IndustryMapper industryMapper) {
+ IndustryMapper industryMapper,
+ ElasticsearchClient elasticsearchClient) {
this.newsMapper = newsMapper;
this.columnMapper = columnMapper;
this.objectMapper = objectMapper;
this.tagMapper = tagMapper;
this.industryMapper = industryMapper;
+ this.elasticsearchClient = elasticsearchClient;
}
public ResultObject publish(long id, long editorId) {
@@ -81,12 +90,14 @@ public class NewsService {
Integer status = news.getStatus();
if (status == 2) {
newsMapper.simpleUnpublish(id);
+ syncStatusToNewsInfo(news.getNewsinfoId(), 1);
return ResultObject.success();
}
Long draftId = news.getDraftId();
if (draftId == null) {
newsMapper.simplePublish(id, editorId);
+ syncStatusToNewsInfo(news.getNewsinfoId(), 2);
return ResultObject.success();
}
@@ -99,6 +110,16 @@ public class NewsService {
SaveNewsDTO saveNewsDTO = new SaveNewsDTO(newsDetailVO);
return createPublish(editorId, saveNewsDTO);
}
+
+ private void syncStatusToNewsInfo(String newsInfoId, Integer status) {
+ NewsInfo publishedNewsInfo = new NewsInfo();
+ publishedNewsInfo.setStatus(status);
+ try {
+ elasticsearchClient.update(e -> e.index(NewsInfo.INDEX_NAME).refresh(Refresh.True).id(newsInfoId).doc(publishedNewsInfo), NewsInfo.class);
+ } catch(IOException e) {
+ log.error("资讯精选状态同步至全量资讯出错!", e);
+ }
+ }
public ResultObject detail(Long id) {
News news = newsMapper.getById(id);
@@ -415,8 +436,48 @@ public class NewsService {
log.error("保存发布新闻标签出错!", e);
return ResultObject.failed(500, "服务器错误,请联系系统管理员!");
}
+
+ // 同步更新对应的全量资讯逻辑
+ try {
+ syncUpdateNewsInfo(saveNewsDTO);
+ } catch(IOException e) {
+ log.error("同步更新对应的全量资讯出错!", e);
+ return ResultObject.failed(500, "服务器错误,请联系系统管理员!");
+ }
+
return ResultObject.success();
}
+
+ private void syncUpdateNewsInfo(SaveNewsDTO saveNewsDTO) throws IOException {
+ Long newsId = saveNewsDTO.getId();
+ if (newsId == null) {
+ return;
+ }
+ News news = newsMapper.getById(newsId);
+ String newsInfoId = news.getNewsinfoId();
+ if (ObjectUtils.isEmpty(newsInfoId)) {
+ return;
+ }
+
+ NewsInfo newsInfo = saveNewsDTO.toNewsInfo();
+ newsInfo.setStatus(2);
+ SaveDraftTag saveDraftTag = saveNewsDTO.getTag();
+ if (saveDraftTag != null && saveDraftTag.getSource() != null) {
+ Tag sourceTag = tagMapper.queryById(saveDraftTag.getSource());
+ if (sourceTag != null) {
+ newsInfo.setSourcename(sourceTag.getName());
+ }
+ }
+ List industries = saveNewsDTO.getIndustries();
+ if (!ObjectUtils.isEmpty(industries)) {
+ List allIndustry = industryMapper.queryAll();
+ Map industryMap = allIndustry.stream().collect(Collectors.toMap(Industry::getId, Function.identity()));
+ String industry = industries.stream().map(e -> industryMap.get(e)).map(Industry::getDisplayName).collect(Collectors.joining(";"));
+ newsInfo.setIndustry(industry);
+ }
+
+ elasticsearchClient.update(e -> e.index(NewsInfo.INDEX_NAME).refresh(Refresh.True).id(newsInfoId).doc(newsInfo), NewsInfo.class);
+ }
public ResultObject saveDraft(SaveNewsDTO saveNewsDTO) {
Long id = saveNewsDTO.getId();
@@ -461,6 +522,15 @@ public class NewsService {
}
try {
newsMapper.deleteNews(newsId);
+ String newsInfoId = news.getNewsinfoId();
+ if (!ObjectUtils.isEmpty(newsInfoId)) {
+ NewsInfo deletedNewsInfo = new NewsInfo();
+ deletedNewsInfo.setDeleted(1);
+ elasticsearchClient.update(e -> e.index(NewsInfo.INDEX_NAME)
+ .refresh(Refresh.True)
+ .id(newsInfoId)
+ .doc(deletedNewsInfo), NewsInfo.class);
+ }
} catch (Exception e) {
log.error("删除新闻异常!", e);
return ResultObject.failed(500, "服务器错误,请联系系统管理员!");
diff --git a/mini/pom.xml b/mini/pom.xml
index b1a4945..ae18a82 100644
--- a/mini/pom.xml
+++ b/mini/pom.xml
@@ -15,6 +15,8 @@
8
8
UTF-8
+ 8.12.2
+ 2.0.1
@@ -22,12 +24,32 @@
com.jinrui
core
0.0.1-SNAPSHOT
+
+
+ org.elasticsearch.client
+ elasticsearch-rest-client
+
+
+ jakarta.json-api
+ jakarta.json
+
+
redis.clients
jedis
${jedis.version}
+
+ org.elasticsearch.client
+ elasticsearch-rest-client
+ ${elasticsearch.version}
+
+
+ jakarta.json
+ jakarta.json-api
+ ${jakartajson.version}
+