1.操作日志增加删除、恢复,发布等操作 2.资讯精选列表返回最后送审者姓名

This commit is contained in:
sunflower2014 2025-07-31 15:32:14 +08:00
parent 4f59c48c1c
commit 3ad3b9a39e
10 changed files with 91 additions and 27 deletions

View File

@ -55,13 +55,6 @@
<artifactId>pdfbox</artifactId> <artifactId>pdfbox</artifactId>
<version>3.0.5</version> <!-- 或使用最新版本 --> <version>3.0.5</version> <!-- 或使用最新版本 -->
</dependency> </dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext7-core</artifactId>
<version>9.2.0</version> <!-- 或使用最新版本 -->
<type>pom</type>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@ -14,7 +14,7 @@ import org.springframework.scheduling.annotation.EnableScheduling;
"com.jinrui.reference.admin.mapper", "com.jinrui.reference.admin.mapper",
"com.jinrui.reference.core.mapper"}) "com.jinrui.reference.core.mapper"})
@EnableScheduling @EnableScheduling
@EnableAspectJAutoProxy @EnableAspectJAutoProxy(proxyTargetClass = true)
public class AdminApplication { public class AdminApplication {
public static void main(String[] args) { public static void main(String[] args) {

View File

@ -16,7 +16,10 @@ import org.springframework.stereotype.Component;
import com.jinrui.reference.admin.annotation.OperationInfo; import com.jinrui.reference.admin.annotation.OperationInfo;
import com.jinrui.reference.admin.model.dto.news.PublishNewsDTO; import com.jinrui.reference.admin.model.dto.news.PublishNewsDTO;
import com.jinrui.reference.admin.model.entity.AdminUser;
import com.jinrui.reference.admin.service.AdminJwtService;
import com.jinrui.reference.admin.service.UserOperationLogService; import com.jinrui.reference.admin.service.UserOperationLogService;
import com.jinrui.reference.core.model.dto.news.SaveNewsDTO;
import com.jinrui.reference.core.model.vo.ResultObject; import com.jinrui.reference.core.model.vo.ResultObject;
@Component @Component
@ -50,7 +53,16 @@ public class UserOperationLogAspect {
} }
} }
if ("news".equals(type)) { if ("news".equals(type)) {
userOperationLogService.logUserOperation(type, behavior, (String)args[0], ((PublishNewsDTO)args[1]).getId()); if (args[1] instanceof PublishNewsDTO) {
userOperationLogService.logUserOperation(type, behavior, (String)args[0], ((PublishNewsDTO)args[1]).getId());
} else if (args[1] instanceof SaveNewsDTO) {
String token = (String)args[0];
AdminUser adminUser = AdminJwtService.parseToken(token);
behavior = (adminUser.isReviewer() ? "发布": "送审");
userOperationLogService.logUserOperation(type, behavior, token, ((SaveNewsDTO)args[1]).getId());
} else {
userOperationLogService.logUserOperation(type, behavior, (String)args[0], (Long)args[1]);
}
} }

View File

@ -83,7 +83,7 @@ public class NewsController {
if (!isSuccessed) { if (!isSuccessed) {
return ResultObject.failed("该资讯正在审核中,请勿重复操作!"); return ResultObject.failed("该资讯正在审核中,请勿重复操作!");
} }
log.info("path: /news/publish, method: POST, request user id: {}, news id: {}", adminUserId, id); log.info("path: /news/submit, method: POST, request user id: {}, news id: {}", adminUserId, id);
return newsService.submit(id, adminUserId); return newsService.submit(id, adminUserId);
} catch (Exception e) { } catch (Exception e) {
log.error("解析登陆Token出错!", e); log.error("解析登陆Token出错!", e);
@ -94,6 +94,7 @@ public class NewsController {
} }
@PostMapping("/revoke") @PostMapping("/revoke")
@OperationInfo(behavior = "撤销", type = "news")
public ResultObject<Void> revoke(@RequestHeader("auth-token") String token, public ResultObject<Void> revoke(@RequestHeader("auth-token") String token,
@RequestBody PublishNewsDTO publishNewsDTO) { @RequestBody PublishNewsDTO publishNewsDTO) {
if (!StringUtils.hasText(token)) { if (!StringUtils.hasText(token)) {
@ -121,7 +122,7 @@ public class NewsController {
if (!isSuccessed) { if (!isSuccessed) {
return ResultObject.failed("该资讯正在审核中,请勿重复操作!"); return ResultObject.failed("该资讯正在审核中,请勿重复操作!");
} }
log.info("path: /news/publish, method: POST, request user id: {}, news id: {}", adminUserId, id); log.info("path: /news/revoke, method: POST, request user id: {}, news id: {}", adminUserId, id);
return newsService.revoke(id, adminUserId, adminUser.isReviewer()); return newsService.revoke(id, adminUserId, adminUser.isReviewer());
} catch (Exception e) { } catch (Exception e) {
log.error("解析登陆Token出错!", e); log.error("解析登陆Token出错!", e);
@ -197,6 +198,7 @@ public class NewsController {
} }
@DeleteMapping @DeleteMapping
@OperationInfo(behavior = "删除", type = "news")
public ResultObject<Void> deleteNews(@RequestHeader("auth-token") String token, public ResultObject<Void> deleteNews(@RequestHeader("auth-token") String token,
@RequestParam("id") Long id) { @RequestParam("id") Long id) {
if (!StringUtils.hasText(token)) { if (!StringUtils.hasText(token)) {
@ -230,6 +232,7 @@ public class NewsController {
} }
@PostMapping("/{id}/recover") @PostMapping("/{id}/recover")
@OperationInfo(behavior = "恢复", type = "news")
public ResultObject<Void> recoverNews(@RequestHeader("auth-token") String token, public ResultObject<Void> recoverNews(@RequestHeader("auth-token") String token,
@PathVariable( name = "id", required = true) Long id) { @PathVariable( name = "id", required = true) Long id) {
if (!StringUtils.hasText(token)) { if (!StringUtils.hasText(token)) {
@ -258,6 +261,7 @@ public class NewsController {
} }
@PostMapping("/create/publish") @PostMapping("/create/publish")
@OperationInfo(behavior = "发布", type = "news")
public ResultObject<Void> createPublish(@RequestHeader("auth-token") String token, public ResultObject<Void> createPublish(@RequestHeader("auth-token") String token,
@RequestBody SaveNewsDTO saveNewsDTO) { @RequestBody SaveNewsDTO saveNewsDTO) {
if (!StringUtils.hasText(token)) { if (!StringUtils.hasText(token)) {

View File

@ -1,6 +1,7 @@
package com.jinrui.reference.admin.job; package com.jinrui.reference.admin.job;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -22,18 +23,22 @@ public class NewsDeduplicationJob {
private NewsMapper newsMapper; private NewsMapper newsMapper;
// @Scheduled(fixedDelay=15, initialDelay=0, timeUnit = TimeUnit.MINUTES) @Scheduled(fixedDelay=15, initialDelay=0, timeUnit = TimeUnit.MINUTES)
public void cleanDuplicatedData() { public void cleanDuplicatedData() {
List<String> clusterIds = newsMapper.getDuplicatedCluster(); List<String> clusterIds = newsMapper.getDuplicatedCluster();
for (String clusterId: clusterIds) { for (String clusterId: clusterIds) {
List<News> duplicatedNews = newsMapper.getDuplicatedNews(clusterId); List<News> duplicatedNews = newsMapper.getDuplicatedNews(clusterId);
List<News> toBeDeletedNews = duplicatedNews.stream().filter(e -> e.getDeleted() && e.getEditorId() == null).collect(Collectors.toList()); List<News> toBeDeletedNews = duplicatedNews.stream().filter(e -> e.getDeleted() && e.getEditorId() == null).collect(Collectors.toList());
List<News> modifedNews = duplicatedNews.stream().filter(e -> e.getDeleted() && !Objects.equals(e.getStatus(), 1)).collect(Collectors.toList());
if (duplicatedNews.size() > toBeDeletedNews.size()) { if (duplicatedNews.size() > toBeDeletedNews.size()) {
newsService.backupDuplicatedNews(toBeDeletedNews, clusterId); newsService.backupDuplicatedNews(toBeDeletedNews, clusterId);
newsService.deletDuplicatedNews(toBeDeletedNews); newsService.deletDuplicatedNews(toBeDeletedNews);
} }
if (duplicatedNews.size() > modifedNews.size()) {
for(News news: modifedNews) {
newsMapper.recoverNews(news.getId(), news.getEditorId());
}
}
} }
} }
} }

View File

@ -251,7 +251,7 @@ public interface NewsMapper {
@Select("SELECT cluster_id FROM news WHERE newsinfo_id IS NOT NULL AND cluster_id IS NOT NULL GROUP BY cluster_id HAVING COUNT(*) > 1") @Select("SELECT cluster_id FROM news WHERE newsinfo_id IS NOT NULL AND cluster_id IS NOT NULL GROUP BY cluster_id HAVING COUNT(*) > 1")
List<String> getDuplicatedCluster(); List<String> getDuplicatedCluster();
@Select("select id, draft_id as draftId, newsinfo_id as newsinfoId, is_delete as deleted, editor_id as editorId from news where cluster_id = #{clusterId} and newsinfo_id IS NOT NULL") @Select("select id, draft_id as draftId, newsinfo_id as newsinfoId, is_delete as deleted, editor_id as editorId, status from news where cluster_id = #{clusterId} and newsinfo_id IS NOT NULL")
List<News> getDuplicatedNews(@Param("clusterId") String clusterId); List<News> getDuplicatedNews(@Param("clusterId") String clusterId);
@Delete("delete from news where id = #{id}") @Delete("delete from news where id = #{id}")
@ -294,16 +294,16 @@ public interface NewsMapper {
"news_tags.source_impact," + "news_tags.source_impact," +
"news_tags.China_factor," + "news_tags.China_factor," +
"news_tags.public_opinion_score" + "news_tags.public_opinion_score" +
"from news " + " from news " +
" left join news_tags on news.newsinfo_id = news_tags.newsinfo_id " + " left join news_tags on news.newsinfo_id = news_tags.newsinfo_id " +
"<where>" + "<where>" +
" news.status = 2 and news.is_delete = 0 and news.publish_time &gt;= adddate(date(now()), -2) " + " news.status in (1, 2, 3) and news.is_delete = 0 and news.publish_time &gt;= adddate(date(now()), -2) " +
"<if test=\"last != null\">" + "<if test=\"last != null\">" +
"and news.id &gt; #{last}" + " and news.id &gt; #{last}" +
"</if>" + "</if>" +
"</where>" + "</where>" +
"order by id desc " + " order by id asc " +
"limit ${limit}" + " limit ${limit}" +
"</script>") "</script>")
List<NewsApiVO> queryNewsByApi(@Param("last") Long last, @Param("limit") int limit); List<NewsApiVO> queryNewsByApi(@Param("last") Long last, @Param("limit") int limit);
} }

View File

@ -32,4 +32,17 @@ public interface UserOperationLogMapper {
@Select("select count(*) from user_operation_log where data_id = #{dataId} and type = #{type}") @Select("select count(*) from user_operation_log where data_id = #{dataId} and type = #{type}")
int queryTotal(@Param("dataId") Long dataId, @Param("type") String type); int queryTotal(@Param("dataId") Long dataId, @Param("type") String type);
@Results({
@Result(column = "id", property = "id", id = true),
@Result(column = "data_id", property = "dataId"),
@Result(column = "user_id", property = "userId"),
@Result(column = "username", property = "username"),
@Result(column = "user_type", property = "userType"),
@Result(column = "type", property = "type"),
@Result(column = "behavior", property = "behavior"),
@Result(column = "create_time", property = "createTime", javaType = Date.class, jdbcType = JdbcType.TIMESTAMP),
})
@Select("select * from user_operation_log where data_id = #{dataId} and type = #{type} and behavior = #{behavior} order by create_time desc limit 1")
UserOperationLog selectLastOperation(@Param("dataId") Long dataId, @Param("type") String type, @Param("behavior") String behavior);
} }

View File

@ -7,6 +7,7 @@ import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
import com.jinrui.reference.core.model.entity.NewsInfo; import com.jinrui.reference.core.model.entity.NewsInfo;
import com.jinrui.reference.core.model.entity.NewsTags;
import com.jinrui.reference.core.model.vo.news.NewsDetailVO; import com.jinrui.reference.core.model.vo.news.NewsDetailVO;
@SuppressWarnings("unused") @SuppressWarnings("unused")
@ -139,13 +140,19 @@ public class SaveNewsDTO {
public NewsInfo toNewsInfo() { public NewsInfo toNewsInfo() {
NewsInfo newsInfo = new NewsInfo(); NewsInfo newsInfo = new NewsInfo();
newsInfo.setTitle(this.getTitle()); // newsInfo.setTitle(this.getTitle());
newsInfo.setSummary(this.getSummary()); newsInfo.setSummary(this.getSummary());
newsInfo.setStatus(this.getStatus()); newsInfo.setStatus(this.getStatus());
newsInfo.setContent(this.getContent()); // newsInfo.setContent(this.getContent());
newsInfo.setCreateTime(new Date()); newsInfo.setCreateTime(new Date());
newsInfo.setUpdateTime(newsInfo.getCreateTime()); newsInfo.setUpdateTime(newsInfo.getCreateTime());
newsInfo.setInputDate(newsInfo.getCreateTime()); newsInfo.setInputDate(newsInfo.getCreateTime());
NewsTags newsTags = new NewsTags();
newsTags.setTitle(this.getTitle());
newsTags.setRewriteContent(this.getContent());
newsInfo.setNewsTags(newsTags);
return newsInfo; return newsInfo;
} }

View File

@ -54,6 +54,8 @@ public class NewsVO {
private Double score; private Double score;
private String submitter;
public Boolean getDeleted() { public Boolean getDeleted() {
return deleted; return deleted;
} }
@ -145,4 +147,12 @@ public class NewsVO {
public void setRating(Byte rating) { public void setRating(Byte rating) {
this.rating = rating; this.rating = rating;
} }
public String getSubmitter() {
return submitter;
}
public void setSubmitter(String submitter) {
this.submitter = submitter;
}
} }

View File

@ -115,6 +115,9 @@ public class NewsService {
log.warn("找不到ID为{}的新闻!", id); log.warn("找不到ID为{}的新闻!", id);
return ResultObject.failed("找不到ID为" + id + "的新闻!"); return ResultObject.failed("找不到ID为" + id + "的新闻!");
} }
if (news.getDeleted()) {
return ResultObject.failed("该资讯正在审核中,请勿重复操作!");
}
Integer oldStatus = news.getStatus(); Integer oldStatus = news.getStatus();
if (oldStatus == 1) { if (oldStatus == 1) {
return ResultObject.failed("资讯已被撤稿,请刷新列表页面!"); return ResultObject.failed("资讯已被撤稿,请刷新列表页面!");
@ -476,9 +479,11 @@ public class NewsService {
return ResultObject.failed(500, "服务器错误,请联系系统管理员!"); return ResultObject.failed(500, "服务器错误,请联系系统管理员!");
} }
// 同步更新对应的全量资讯逻辑 // 发布时同步更新对应的全量资讯逻辑
try { try {
syncUpdateNewsInfo(saveNewsDTO); if (newStatus == 2) {
syncUpdateNewsInfo(saveNewsDTO);
}
} catch(IOException e) { } catch(IOException e) {
log.error("同步更新对应的全量资讯出错!", e); log.error("同步更新对应的全量资讯出错!", e);
return ResultObject.failed(500, "服务器错误,请联系系统管理员!"); return ResultObject.failed(500, "服务器错误,请联系系统管理员!");
@ -546,6 +551,10 @@ public class NewsService {
return ResultObject.failed("请先手动下架新闻然后进行删除!"); return ResultObject.failed("请先手动下架新闻然后进行删除!");
} }
if (status == 3) {
return ResultObject.failed("该资讯正在审核中,请勿重复操作");
}
try { try {
int count = newsMapper.deleteNews(newsId, editorId); int count = newsMapper.deleteNews(newsId, editorId);
if (count == 0) { if (count == 0) {
@ -898,6 +907,9 @@ public class NewsService {
newsMap.put(newsVO.getId(), newsVO); newsMap.put(newsVO.getId(), newsVO);
} }
resultList.add(newsVO); resultList.add(newsVO);
if (isReviewer) {
newsVO.setSubmitter(this.getNewsLastSubmitter(news.getId()));
}
} }
List<Column> columns = columnMapper.queryAll(); List<Column> columns = columnMapper.queryAll();
@ -937,6 +949,14 @@ public class NewsService {
return pageObject; return pageObject;
} }
private String getNewsLastSubmitter(Long newsId) {
UserOperationLog userOperationLog = userOperationLogMapper.selectLastOperation(newsId, "news", "送审");
if (userOperationLog == null) {
return null;
}
return userOperationLog.getUsername();
}
public ResultObject<NewsScoreVO> getScore(Long id) { public ResultObject<NewsScoreVO> getScore(Long id) {
News news = newsMapper.getById(id); News news = newsMapper.getById(id);
String newsinfoId = news.getNewsinfoId(); String newsinfoId = news.getNewsinfoId();
@ -1087,7 +1107,7 @@ public class NewsService {
return ResultObject.failed("找不到ID为" + id + "的新闻!"); return ResultObject.failed("找不到ID为" + id + "的新闻!");
} }
Integer oldStatus = news.getStatus(); Integer oldStatus = news.getStatus();
if (oldStatus != 1) { if (oldStatus != 1||news.getDeleted()) {
return ResultObject.failed("该资讯正在审核中,请勿重复操作"); return ResultObject.failed("该资讯正在审核中,请勿重复操作");
} }
@ -1103,14 +1123,14 @@ public class NewsService {
} }
Integer oldStatus = news.getStatus(); Integer oldStatus = news.getStatus();
if (isReviewer) { if (isReviewer) {
if (oldStatus != 2) { if (oldStatus != 2||news.getDeleted()) {
return ResultObject.failed("该资讯正在审核中,请勿重复操作"); return ResultObject.failed("该资讯正在审核中,请勿重复操作");
} }
newsMapper.changeFrom(id, oldStatus, 3, editorId); newsMapper.changeFrom(id, oldStatus, 3, editorId);
return ResultObject.success(); return ResultObject.success();
} }
if (oldStatus != 3) { if (oldStatus != 3||news.getDeleted()) {
return ResultObject.failed("该资讯正在审核中,请勿重复操作"); return ResultObject.failed("该资讯正在审核中,请勿重复操作");
} }