作者 钟来

水产舆情采集

  1 +package com.ruoyi.system.domain.fish;
  2 +
  3 +
  4 +import java.util.Date;
  5 +import com.fasterxml.jackson.annotation.JsonFormat;
  6 +import com.ruoyi.common.annotation.PublicSQLConfig;
  7 +import org.apache.commons.lang3.builder.ToStringBuilder;
  8 +import org.apache.commons.lang3.builder.ToStringStyle;
  9 +import io.swagger.annotations.ApiModel;
  10 +import io.swagger.annotations.ApiModelProperty;
  11 +import com.ruoyi.common.tool.BaseEntity;
  12 +
  13 +/**
  14 + * 水产舆情对象 fish_aquatic_public_opinion
  15 + *
  16 + * @author zhonglai
  17 + * @date 2025-07-03
  18 + */
  19 +@ApiModel("水产舆情")
  20 +public class FishAquaticPublicOpinion extends BaseEntity
  21 +{
  22 + @PublicSQLConfig(isSelect=false)
  23 + private static final long serialVersionUID = 1L;
  24 +
  25 + /** 主键 */
  26 + private Integer id;
  27 +
  28 + /** 发布时间 */
  29 + @JsonFormat(pattern = "yyyy-MM-dd")
  30 + @ApiModelProperty(value="发布时间")
  31 + private Date releaseTime;
  32 +
  33 + /** 标题 */
  34 + @ApiModelProperty(value="标题")
  35 + private String title;
  36 +
  37 + /** 详情地址 */
  38 + @ApiModelProperty(value="详情地址")
  39 + private String infoUrl;
  40 +
  41 + /** 文件地址 */
  42 + @ApiModelProperty(value="文件地址")
  43 + private String filePath;
  44 +
  45 + public void setId(Integer id)
  46 + {
  47 + this.id = id;
  48 + }
  49 +
  50 + public Integer getId()
  51 + {
  52 + return id;
  53 + }
  54 + public void setReleaseTime(Date releaseTime)
  55 + {
  56 + this.releaseTime = releaseTime;
  57 + }
  58 +
  59 + public Date getReleaseTime()
  60 + {
  61 + return releaseTime;
  62 + }
  63 + public void setTitle(String title)
  64 + {
  65 + this.title = title;
  66 + }
  67 +
  68 + public String getTitle()
  69 + {
  70 + return title;
  71 + }
  72 + public void setInfoUrl(String infoUrl)
  73 + {
  74 + this.infoUrl = infoUrl;
  75 + }
  76 +
  77 + public String getInfoUrl()
  78 + {
  79 + return infoUrl;
  80 + }
  81 + public void setFilePath(String filePath)
  82 + {
  83 + this.filePath = filePath;
  84 + }
  85 +
  86 + public String getFilePath()
  87 + {
  88 + return filePath;
  89 + }
  90 +
  91 + @Override
  92 + public String toString() {
  93 + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
  94 + .append("id", getId())
  95 + .append("releaseTime", getReleaseTime())
  96 + .append("title", getTitle())
  97 + .append("infoUrl", getInfoUrl())
  98 + .append("createTime", getCreateTime())
  99 + .append("filePath", getFilePath())
  100 + .toString();
  101 + }
  102 +}
@@ -46,6 +46,10 @@ @@ -46,6 +46,10 @@
46 <groupId>com.jcraft</groupId> 46 <groupId>com.jcraft</groupId>
47 <artifactId>jsch</artifactId> 47 <artifactId>jsch</artifactId>
48 </dependency> 48 </dependency>
  49 + <dependency>
  50 + <groupId>org.jsoup</groupId>
  51 + <artifactId>jsoup</artifactId>
  52 + </dependency>
49 </dependencies> 53 </dependencies>
50 54
51 </project> 55 </project>
  1 +package com.ruoyi.quartz.task;
  2 +
  3 +import cn.hutool.http.HttpUtil;
  4 +import com.alibaba.fastjson.JSONObject;
  5 +import com.ruoyi.common.constant.Constants;
  6 +import com.ruoyi.common.tool.SysLogininforType;
  7 +import com.ruoyi.common.utils.DateUtils;
  8 +import com.ruoyi.common.utils.MessageUtils;
  9 +import com.ruoyi.quartz.task.aquatic.AquaticPublicOpinionService;
  10 +import com.zhonglai.luhui.dao.service.PublicService;
  11 +import com.zhonglai.luhui.sys.manager.AsyncManager;
  12 +import com.zhonglai.luhui.sys.manager.factory.AsyncFactory;
  13 +import org.slf4j.Logger;
  14 +import org.slf4j.LoggerFactory;
  15 +import org.springframework.beans.factory.annotation.Autowired;
  16 +import org.springframework.stereotype.Component;
  17 +
  18 +import java.util.List;
  19 +import java.util.Map;
  20 +import java.util.TimerTask;
  21 +
  22 +/**
  23 + * 水产舆情
  24 + */
  25 +@Component("aquaticPublicOpinionTask")
  26 +public class AquaticPublicOpinionTask {
  27 + private final Logger logger = LoggerFactory.getLogger(this.getClass());
  28 + @Autowired
  29 + private List<AquaticPublicOpinionService> aquaticPublicOpinionServices;
  30 + @Autowired
  31 + protected PublicService publicService;
  32 + public void run(String day)
  33 + {
  34 + for (AquaticPublicOpinionService service : aquaticPublicOpinionServices) {
  35 + AsyncManager.me().execute(new TimerTask() {
  36 + @Override
  37 + public void run() {
  38 + try {
  39 + logger.info("异步执行服务:{},参数:{}", service.getClass().getSimpleName(), day);
  40 + service.run(day);
  41 + logger.info("服务执行完成:{}", service.getClass().getSimpleName());
  42 + } catch (Exception e) {
  43 + logger.error("服务 {} 执行出错", service.getClass().getSimpleName(), e);
  44 + }
  45 + }
  46 + });
  47 + }
  48 + }
  49 +
  50 + public void pushPublicOpinion()
  51 + {
  52 + List<Map<String,Object>> lsit = publicService.getObjectListBySQL("SELECT * FROM `fish_aquatic_public_opinion` WHERE release_time='"+DateUtils.getDate()+" 00:00:00'");
  53 +
  54 + if (null != lsit && lsit.size()!=0)
  55 + {
  56 + for (Map<String,Object> map:lsit)
  57 + {
  58 + StringBuffer content = new StringBuffer();
  59 + content.append("【水产新闻】");
  60 + content.append("\n");
  61 + content.append(map.get("title"));
  62 + content.append("\n");
  63 + content.append("https://lh.admin.yu2le.com/api/profile/aquaticPublicOpinion/"+map.get("file_path"));
  64 + content.append("\n");
  65 + content.append("来源:"+map.get("info_url"));
  66 + content.append("\n");
  67 + JSONObject jsonObject = new JSONObject();
  68 + jsonObject.put("msgtype","text");
  69 + JSONObject text = new JSONObject();
  70 + text.put("content",content);
  71 + text.put("mentioned_mobile_list",new String[]{"@all"});
  72 + jsonObject.put("text",text);
  73 + HttpUtil.post("https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=6d6b4790-d20f-4692-bc4a-e8ceb1e8cfda",jsonObject.toJSONString());
  74 +
  75 + }
  76 + }
  77 + }
  78 +}
  1 +package com.ruoyi.quartz.task.aquatic;
  2 +
  3 +import com.ruoyi.common.utils.DateUtils;
  4 +import com.ruoyi.common.utils.StringUtils;
  5 +import com.ruoyi.system.domain.fish.FishAquaticPublicOpinion;
  6 +import com.zhonglai.luhui.dao.service.PublicService;
  7 +import org.jsoup.Jsoup;
  8 +import org.jsoup.nodes.Document;
  9 +import org.slf4j.Logger;
  10 +import org.slf4j.LoggerFactory;
  11 +import org.springframework.beans.factory.annotation.Autowired;
  12 +
  13 +import java.io.File;
  14 +import java.io.IOException;
  15 +import java.nio.charset.StandardCharsets;
  16 +import java.nio.file.Files;
  17 +import java.nio.file.Path;
  18 +import java.nio.file.Paths;
  19 +import java.util.List;
  20 +import java.util.Map;
  21 +
  22 +public abstract class AquaticPublicOpinionBase implements AquaticPublicOpinionService{
  23 + protected final Logger logger = LoggerFactory.getLogger(this.getClass());
  24 + @Autowired
  25 + protected PublicService publicService;
  26 + public void updateSaveInfo(String text,Integer id)
  27 + {
  28 + if(StringUtils.isNotEmpty(text))
  29 + {
  30 + Path path = Paths.get("uploadPath/aquaticPublicOpinion/"+id+".txt");
  31 + String filepath = saveFile(text, path);
  32 + if(StringUtils.isNotEmpty(filepath))
  33 + {
  34 + FishAquaticPublicOpinion upaquaticPublicOpinion = new FishAquaticPublicOpinion();
  35 + upaquaticPublicOpinion.setId(id);
  36 + upaquaticPublicOpinion.setFilePath(filepath);
  37 + publicService.updateObject(upaquaticPublicOpinion,"id");
  38 + }
  39 + }
  40 + }
  41 +
  42 + public String saveFile(String text,Path path) {
  43 + File file = path.getParent().toFile();
  44 + if(!file.exists())
  45 + {
  46 + file.mkdirs();
  47 + }
  48 + System.out.println(path.toAbsolutePath());
  49 + try {
  50 + return Files.write(path, text.toString().getBytes(StandardCharsets.UTF_8)).getFileName().toString();
  51 + } catch (IOException e) {
  52 + logger.error("文件"+path+"保存失败",e);
  53 + }
  54 + return null;
  55 + }
  56 +
  57 + protected void insertFishAquaticPublicOpinions(List<FishAquaticPublicOpinion> fishAquaticPublicOpinions)
  58 + {
  59 + if(null != fishAquaticPublicOpinions && fishAquaticPublicOpinions.size() !=0)
  60 + {
  61 + publicService.insertAll( fishAquaticPublicOpinions);
  62 + }
  63 + }
  64 +
  65 + abstract List<FishAquaticPublicOpinion> collect(String day);
  66 + abstract String getInfo(String info_url);
  67 + @Override
  68 + public void run(String day) {
  69 + if(StringUtils.isEmpty(day))
  70 + {
  71 + day = DateUtils.getDate();
  72 + }
  73 + List<FishAquaticPublicOpinion> list = collect(day);
  74 + if (null != list && list.size() != 0)
  75 + {
  76 + //持久
  77 + insertFishAquaticPublicOpinions(list);
  78 + //保存文件
  79 + for (FishAquaticPublicOpinion aquaticPublicOpinion:list)
  80 + {
  81 + Map<String,Object> map = publicService.getObjectList(aquaticPublicOpinion,"id,info_url",null,null,0,0).get(0);
  82 + String text = getInfo((String) map.get("info_url"));
  83 + updateSaveInfo(text, (Integer) map.get("id"));
  84 + }
  85 + }
  86 + }
  87 +
  88 + protected Document createDocument(String url) throws IOException {
  89 + return Jsoup.connect(url).timeout(10000).get();
  90 + }
  91 +}
  1 +package com.ruoyi.quartz.task.aquatic;
  2 +
  3 +public interface AquaticPublicOpinionService {
  4 + public void run(String day);
  5 +}
  1 +package com.ruoyi.quartz.task.aquatic;
  2 +
  3 +import com.ruoyi.common.utils.DateUtils;
  4 +import com.ruoyi.common.utils.StringUtils;
  5 +import com.ruoyi.system.domain.fish.FishAquaticPublicOpinion;
  6 +import org.jsoup.nodes.Document;
  7 +import org.jsoup.nodes.Element;
  8 +import org.jsoup.select.Elements;
  9 +import org.springframework.stereotype.Service;
  10 +
  11 +import java.text.ParseException;
  12 +import java.util.ArrayList;
  13 +import java.util.Date;
  14 +import java.util.List;
  15 +
  16 +@Service
  17 +public class WwwCafsAcCn extends AquaticPublicOpinionBase{
  18 + @Override
  19 + List<FishAquaticPublicOpinion> collect(String day) {
  20 +
  21 + String[] urls = {"https://www.cafs.ac.cn/kxyj/kyjz.htm","https://www.cafs.ac.cn/gjhz/gjhz.htm","https://www.cafs.ac.cn/djwh/djdt.htm","https://www.cafs.ac.cn/xwxx/tpxw.htm"};
  22 + List<FishAquaticPublicOpinion> list = new ArrayList<>();
  23 + for (String domain :urls)
  24 + {
  25 + try {
  26 + // 解析页面
  27 + Document doc = createDocument(domain);
  28 + // 选中 div.m_list 下的 ul 中的所有 li
  29 + Elements liElements = doc.select("div.list_con ul li");
  30 + // 打印所有 li 的 HTML
  31 + for (Element li : liElements) {
  32 + Elements a = li.select("a");
  33 +
  34 + String url = a.attr("abs:href");
  35 +
  36 + String title = a.attr("title");
  37 +
  38 + String time = li.select("span").text();
  39 + if(StringUtils.isNotEmpty(day) && DateUtils.parseDate(time, "yyyy年MM月dd日").equals(DateUtils.parseDate(day,DateUtils.YYYY_MM_DD)))
  40 + {
  41 + FishAquaticPublicOpinion aquaticPublicOpinion = new FishAquaticPublicOpinion();
  42 + aquaticPublicOpinion.setTitle(title);
  43 + aquaticPublicOpinion.setInfoUrl(url);
  44 + aquaticPublicOpinion.setReleaseTime(DateUtils.parseDate(time,"yyyy年MM月dd日"));
  45 + aquaticPublicOpinion.setCreateTime(new Date());
  46 + list.add(aquaticPublicOpinion);
  47 + }else{
  48 + return list;
  49 + }
  50 +
  51 + }
  52 + } catch (Exception e) {
  53 + logger.error("数据解析错误:"+domain,e);
  54 + }
  55 + }
  56 + return list;
  57 + }
  58 +
  59 + @Override
  60 + String getInfo(String info_url) {
  61 + try {
  62 + // 访问网页
  63 + Document doc = createDocument(info_url);
  64 +
  65 + // 查找正文区域,常见class为 TRS_Editor 或 article
  66 + Element content = doc.selectFirst("div.v_news_content");
  67 +
  68 + if (content != null) {
  69 + StringBuilder text = new StringBuilder();
  70 +
  71 + // 遍历段落,保留换行
  72 + for (Element p : content.select("p")) {
  73 + String line = p.text().trim();
  74 + if (!line.isEmpty()) {
  75 + text.append(line).append("\n\n");
  76 + }
  77 + }
  78 +
  79 + return text.toString();
  80 + } else {
  81 + logger.error("未找到正文内容区域:"+info_url);
  82 + }
  83 +
  84 + } catch (Exception e) {
  85 + logger.error("解析详情错误:"+info_url,e);
  86 + }
  87 + return null;
  88 + }
  89 +
  90 + public static void main(String[] args) {
  91 + String day = DateUtils.getDate();
  92 + String time = "2025年7月8日";
  93 + try {
  94 + System.out.println(StringUtils.isNotEmpty(day) && (
  95 + (DateUtils.parseDate(time, "yyyy年MM月dd日").equals(DateUtils.parseDate(day,DateUtils.YYYY_MM_DD)) ||
  96 + (DateUtils.parseDate(time, "yyyy年MM月dd日").after(DateUtils.parseDate(day,DateUtils.YYYY_MM_DD)))
  97 + )));
  98 + } catch (ParseException e) {
  99 + throw new RuntimeException(e);
  100 + }
  101 + }
  102 +}
  1 +package com.ruoyi.quartz.task.aquatic;
  2 +
  3 +import com.ruoyi.common.utils.DateUtils;
  4 +import com.ruoyi.common.utils.StringUtils;
  5 +import com.ruoyi.system.domain.fish.FishAquaticPublicOpinion;
  6 +import org.jsoup.nodes.Document;
  7 +import org.jsoup.nodes.Element;
  8 +import org.jsoup.select.Elements;
  9 +import org.springframework.stereotype.Service;
  10 +
  11 +import java.util.ArrayList;
  12 +import java.util.Date;
  13 +import java.util.List;
  14 +
  15 +@Service
  16 +public class WwwChinaCfaOrg extends AquaticPublicOpinionBase{
  17 + public static void main(String[] args) {
  18 + WwwChinaCfaOrg wwwCsfishOrgCn = new WwwChinaCfaOrg();
  19 + wwwCsfishOrgCn.collect("");
  20 + }
  21 + /**
  22 + * 中国渔业协会
  23 + * @param day
  24 + * @return
  25 + */
  26 + @Override
  27 + public List<FishAquaticPublicOpinion> collect(String day)
  28 + {
  29 + String[] urls = {"http://www.china-cfa.org/xwzx/","http://www.china-cfa.org/tzgg/","http://www.china-cfa.org/tzgg/","http://www.china-cfa.org/hzjl/","http://www.china-cfa.org/hzzx/","http://www.china-cfa.org/jypx/","http://www.china-cfa.org/bzgz/"};
  30 + List<FishAquaticPublicOpinion> list = new ArrayList<>();
  31 + for (String domain :urls)
  32 + {
  33 + try {
  34 + // 解析页面
  35 + Document doc = createDocument(domain);
  36 + // 选中 div.m_list 下的 ul 中的所有 li
  37 + Elements liElements = doc.select("div.list ul li");
  38 + // 打印所有 li 的 HTML
  39 + for (Element li : liElements) {
  40 + Elements a = li.select("a");
  41 +
  42 + String url = a.attr("abs:href");
  43 +
  44 + String title = a.attr("title");
  45 +
  46 + String time = li.select("span").text();
  47 + if(StringUtils.isNotEmpty(day) && DateUtils.parseDate(time,DateUtils.YYYY_MM_DD).equals(DateUtils.parseDate(day,DateUtils.YYYY_MM_DD)))
  48 + {
  49 + FishAquaticPublicOpinion aquaticPublicOpinion = new FishAquaticPublicOpinion();
  50 + aquaticPublicOpinion.setTitle(title);
  51 + aquaticPublicOpinion.setInfoUrl(url);
  52 + aquaticPublicOpinion.setReleaseTime(DateUtils.parseDate(time,DateUtils.YYYY_MM_DD));
  53 + aquaticPublicOpinion.setCreateTime(new Date());
  54 + list.add(aquaticPublicOpinion);
  55 + }else{
  56 + return list;
  57 + }
  58 + }
  59 + } catch (Exception e) {
  60 + logger.error("数据解析错误:"+domain,e);
  61 + }
  62 + }
  63 + return list;
  64 + }
  65 +
  66 + @Override
  67 + public String getInfo(String info_url)
  68 + {
  69 + try {
  70 + // 访问网页
  71 + Document doc = createDocument(info_url);
  72 +
  73 + // 查找正文区域,常见class为 TRS_Editor 或 article
  74 + Element content = doc.selectFirst("div.TRS_Editor");
  75 +
  76 + if (content != null) {
  77 + StringBuilder text = new StringBuilder();
  78 +
  79 + // 遍历段落,保留换行
  80 + for (Element p : content.select("p")) {
  81 + String line = p.text().trim();
  82 + if (!line.isEmpty()) {
  83 + text.append(line).append("\n\n");
  84 + }
  85 + }
  86 +
  87 + return text.toString();
  88 + } else {
  89 + logger.error("未找到正文内容区域:"+info_url);
  90 + }
  91 +
  92 + } catch (Exception e) {
  93 + logger.error("解析详情错误:"+info_url,e);
  94 + }
  95 + return null;
  96 + }
  97 +}
  1 +package com.ruoyi.quartz.task.aquatic;
  2 +
  3 +import com.ruoyi.common.utils.DateUtils;
  4 +import com.ruoyi.common.utils.StringUtils;
  5 +import com.ruoyi.system.domain.fish.FishAquaticPublicOpinion;
  6 +import org.jsoup.nodes.Document;
  7 +import org.jsoup.nodes.Element;
  8 +import org.jsoup.select.Elements;
  9 +import org.springframework.stereotype.Service;
  10 +
  11 +import java.util.ArrayList;
  12 +import java.util.Date;
  13 +import java.util.List;
  14 +
  15 +@Service
  16 +public class WwwCsfishOrgCn extends AquaticPublicOpinionBase{
  17 +
  18 + @Override
  19 + List<FishAquaticPublicOpinion> collect(String day) {
  20 + String domain = "http://www.csfish.org.cn/catalog/197";
  21 + List<FishAquaticPublicOpinion> list = new ArrayList<>();
  22 + try {
  23 + // 解析页面
  24 + Document doc = createDocument(domain);
  25 + // 选中 div.m_list 下的 ul 中的所有 li
  26 + Elements liElements = doc.select("div.zjjg-panel-bd ul li.zjjg-eli");
  27 + // 打印所有 li 的 HTML
  28 + for (Element li : liElements) {
  29 + Elements a = li.select("a");
  30 +
  31 + String url = a.attr("abs:href");
  32 +
  33 + String title = a.text();
  34 +
  35 + String time = li.select("span").text();
  36 + if(StringUtils.isNotEmpty(day) && DateUtils.parseDate(time,DateUtils.YYYY_MM_DD).equals(DateUtils.parseDate(day,DateUtils.YYYY_MM_DD)))
  37 + {
  38 + System.out.println("url:"+url+" title:"+title+" time:"+time);
  39 + FishAquaticPublicOpinion aquaticPublicOpinion = new FishAquaticPublicOpinion();
  40 + aquaticPublicOpinion.setTitle(title);
  41 + aquaticPublicOpinion.setInfoUrl(url);
  42 + aquaticPublicOpinion.setReleaseTime(DateUtils.parseDate(time,DateUtils.YYYY_MM_DD));
  43 + aquaticPublicOpinion.setCreateTime(new Date());
  44 + list.add(aquaticPublicOpinion);
  45 + }else {
  46 + return list;
  47 + }
  48 + }
  49 + } catch (Exception e) {
  50 + logger.error("数据解析错误:"+domain,e);
  51 + }
  52 + return list;
  53 + }
  54 +
  55 + @Override
  56 + String getInfo(String info_url) {
  57 + try {
  58 + // 访问网页
  59 + Document doc = createDocument(info_url);
  60 +
  61 + // 查找正文区域,常见class为 TRS_Editor 或 article
  62 + Element content = doc.selectFirst("div.solution-inform");
  63 +
  64 + if (content != null) {
  65 + StringBuilder text = new StringBuilder();
  66 + String title = doc.selectFirst("div.words-title span").text();
  67 + text.append( title);
  68 + text.append("\n\n");
  69 + // 遍历段落,保留换行
  70 + for (Element p : content.select("p")) {
  71 + String line = p.text().trim();
  72 + if (!line.isEmpty()) {
  73 + text.append(line).append("\n\n");
  74 + }
  75 + }
  76 +
  77 + return text.toString();
  78 + } else {
  79 + logger.error("未找到正文内容区域:"+info_url);
  80 + }
  81 +
  82 + } catch (Exception e) {
  83 + logger.error("解析详情错误:"+info_url,e);
  84 + }
  85 + return null;
  86 + }
  87 +}
  1 +package com.ruoyi.quartz.task.aquatic;
  2 +
  3 +import com.ruoyi.common.utils.DateUtils;
  4 +import com.ruoyi.common.utils.StringUtils;
  5 +import com.ruoyi.system.domain.fish.FishAquaticPublicOpinion;
  6 +import org.jsoup.Jsoup;
  7 +import org.jsoup.nodes.Document;
  8 +import org.jsoup.nodes.Element;
  9 +import org.jsoup.select.Elements;
  10 +import org.springframework.stereotype.Service;
  11 +
  12 +import java.io.File;
  13 +import java.io.IOException;
  14 +import java.nio.file.Path;
  15 +import java.nio.file.Paths;
  16 +import java.util.*;
  17 +
  18 +@Service
  19 +public class WwwMoaGovCn extends AquaticPublicOpinionBase{
  20 + /**
  21 + * 中华人民共和国农业农村部网站
  22 + * @param day
  23 + * @return
  24 + */
  25 + @Override
  26 + public List<FishAquaticPublicOpinion> collect(String day)
  27 + {
  28 + //先下载规章
  29 + nyncbgzk();
  30 + List<FishAquaticPublicOpinion> list = govpublic(day);
  31 + return list;
  32 + }
  33 +
  34 + @Override
  35 + String getInfo(String info_url) {
  36 + try {
  37 + // 访问网页
  38 + Document doc = createDocument(info_url);
  39 +
  40 + // 查找正文区域,常见class为 TRS_Editor 或 article
  41 + Element content = doc.selectFirst("div.gsj_htmlcon");
  42 +
  43 + if (content != null) {
  44 + StringBuilder text = new StringBuilder();
  45 +
  46 + // 遍历段落,保留换行
  47 + for (Element p : content.select("p")) {
  48 + String line = p.text().trim();
  49 + if (!line.isEmpty()) {
  50 + text.append(line).append("\n\n");
  51 + }
  52 + }
  53 +
  54 + return text.toString();
  55 + } else {
  56 + logger.error("未找到正文内容区域:"+info_url);
  57 + }
  58 +
  59 + } catch (Exception e) {
  60 + logger.error("解析详情错误:"+info_url,e);
  61 + }
  62 + return null;
  63 + }
  64 +
  65 + public static void main(String[] args) {
  66 + WwwMoaGovCn wwwMoaGovCn = new WwwMoaGovCn();
  67 + wwwMoaGovCn.nyncbgzk();
  68 + }
  69 +
  70 + private List<FishAquaticPublicOpinion> govpublic(String day)
  71 + {
  72 + String domain = "https://www.moa.gov.cn/govpublic/";
  73 + List<FishAquaticPublicOpinion> list = new ArrayList<>();
  74 + try {
  75 + // 解析页面
  76 + Document doc = createDocument(domain);
  77 + // 选中 div.m_list 下的 ul 中的所有 li
  78 + Elements liElements = doc.select("ul.commonlist li");
  79 + // 打印所有 li 的 HTML
  80 + for (Element li : liElements) {
  81 + Elements a = li.select("a");
  82 +
  83 + String url = a.attr("abs:href");
  84 +
  85 + String title = a.attr("title");
  86 +
  87 + String time = li.select("span").text();
  88 + if(StringUtils.isNotEmpty(day) && DateUtils.parseDate(time,DateUtils.YYYY_MM_DD).equals(DateUtils.parseDate(day,DateUtils.YYYY_MM_DD)))
  89 + {
  90 + FishAquaticPublicOpinion aquaticPublicOpinion = new FishAquaticPublicOpinion();
  91 + aquaticPublicOpinion.setTitle(title);
  92 + aquaticPublicOpinion.setInfoUrl(url);
  93 + aquaticPublicOpinion.setReleaseTime(DateUtils.parseDate(time,DateUtils.YYYY_MM_DD));
  94 + aquaticPublicOpinion.setCreateTime(new Date());
  95 + list.add(aquaticPublicOpinion);
  96 + }else {
  97 + return list;
  98 + }
  99 +
  100 + }
  101 + } catch (Exception e) {
  102 + logger.error("数据解析错误:"+domain,e);
  103 + }
  104 + return list;
  105 + }
  106 +
  107 + /**
  108 + * 农业农村部
  109 + * 规章
  110 + * @return
  111 + */
  112 + private void nyncbgzk()
  113 + {
  114 + String firstUrl = "https://www.moa.gov.cn/gk/nyncbgzk/";
  115 + try {
  116 + // 解析页面
  117 + Document doc = createDocument(firstUrl);
  118 + // 选中 div.m_list 下的 ul 中的所有 li
  119 + Elements liElements = doc.select("div.gz_list ul li div.title");
  120 + File[] files = getDirFils(Paths.get("uploadPath/gz/").toAbsolutePath().toString());
  121 + for (Element li : liElements)
  122 + {
  123 + Elements a = li.select("a");
  124 + String url = a.attr("abs:href");
  125 + String title = a.text()+".txt";
  126 + if(isStringInTop20Files(files, title))
  127 + {
  128 + return;
  129 + }
  130 +
  131 + Document info_doc = createDocument(url);
  132 + Elements content = info_doc.select("div.gz_content");
  133 + Path path = Paths.get("uploadPath/gz/"+title);
  134 + saveFile(content.text(),path);
  135 + }
  136 + } catch (IOException e) {
  137 + logger.error("数据解析错误:"+firstUrl,e);
  138 + }
  139 +
  140 + }
  141 +
  142 + /**
  143 + * 判断指定字符串str是否存在于指定目录中按修改时间排序的前20个文件名中
  144 + * @param files 指定目录路径
  145 + * @param str 要匹配的字符串
  146 + * @return 如果存在则返回true,否则返回false
  147 + */
  148 + public static boolean isStringInTop20Files( File[] files , String str) {
  149 +
  150 + // 取前20个文件名判断是否包含指定字符串
  151 + for (int i = 0; i < Math.min(20, files.length); i++) {
  152 + String fileName = files[i].getName();
  153 + if (fileName.contains(str)) {
  154 + return true;
  155 + }
  156 + }
  157 +
  158 + return false;
  159 + }
  160 +
  161 + private static File[] getDirFils(String dirPath)
  162 + {
  163 + File dir = new File(dirPath);
  164 + if (!dir.exists() || !dir.isDirectory()) {
  165 + System.err.println("目录不存在或不是一个目录: " + dirPath);
  166 + return null;
  167 + }
  168 +
  169 + File[] files = dir.listFiles();
  170 + if (files == null || files.length == 0) {
  171 + return null;
  172 + }
  173 +
  174 + // 按照文件最后修改时间降序排序
  175 + Arrays.sort(files, Comparator.comparingLong(File::lastModified).reversed());
  176 + return files;
  177 + }
  178 +}
  1 +package com.ruoyi.quartz.task.aquatic;
  2 +
  3 +import com.ruoyi.common.utils.DateUtils;
  4 +import com.ruoyi.common.utils.StringUtils;
  5 +import com.ruoyi.system.domain.fish.FishAquaticPublicOpinion;
  6 +import com.zhonglai.luhui.dao.service.PublicService;
  7 +import org.jsoup.Jsoup;
  8 +import org.jsoup.nodes.Document;
  9 +import org.jsoup.nodes.Element;
  10 +import org.jsoup.select.Elements;
  11 +import org.slf4j.Logger;
  12 +import org.slf4j.LoggerFactory;
  13 +import org.springframework.beans.factory.annotation.Autowired;
  14 +import org.springframework.stereotype.Service;
  15 +
  16 +import java.util.ArrayList;
  17 +import java.util.Date;
  18 +import java.util.List;
  19 +
  20 +/**
  21 + * 全国水产技术推广总站、中国水产学会
  22 + */
  23 +@Service
  24 +public class WwwNftecAgriCn extends AquaticPublicOpinionBase{
  25 +
  26 + /**
  27 + * 全国水产技术推广总站、中国水产学会
  28 + * @param day
  29 + * @return
  30 + */
  31 + @Override
  32 + public List<FishAquaticPublicOpinion> collect(String day)
  33 + {
  34 + String[] urls = {"http://www.nftec.agri.cn/tzgg/","http://www.nftec.agri.cn/bwzc/","http://www.nftec.agri.cn/zzxhdt/","http://www.nftec.agri.cn/dfgz/","http://www.nftec.agri.cn/zcfg/"};
  35 + List<FishAquaticPublicOpinion> list = new ArrayList<>();
  36 + for (String domain :urls)
  37 + {
  38 + try {
  39 + // 解析页面
  40 + Document doc = createDocument(domain);
  41 + // 选中 div.m_list 下的 ul 中的所有 li
  42 + Elements liElements = doc.select("div.m_list ul li");
  43 + // 打印所有 li 的 HTML
  44 + for (Element li : liElements) {
  45 + Elements a = li.select("a");
  46 +
  47 + String url = a.attr("abs:href");
  48 +
  49 + String title = a.attr("title");
  50 +
  51 + String time = li.select("span").text().replace("(","").replace(")","");
  52 + if(StringUtils.isNotEmpty(day) && DateUtils.parseDate(time,DateUtils.YYYY_MM_DD).equals(DateUtils.parseDate(day,DateUtils.YYYY_MM_DD)))
  53 + {
  54 + FishAquaticPublicOpinion aquaticPublicOpinion = new FishAquaticPublicOpinion();
  55 + aquaticPublicOpinion.setTitle(title);
  56 + aquaticPublicOpinion.setInfoUrl(url);
  57 + aquaticPublicOpinion.setReleaseTime(DateUtils.parseDate(time,DateUtils.YYYY_MM_DD));
  58 + aquaticPublicOpinion.setCreateTime(new Date());
  59 + list.add(aquaticPublicOpinion);
  60 + }else {
  61 + return list;
  62 + }
  63 +
  64 + }
  65 + } catch (Exception e) {
  66 + logger.error("数据解析错误:"+domain,e);
  67 + }
  68 + }
  69 +
  70 + return list;
  71 + }
  72 +
  73 +
  74 +
  75 + @Override
  76 + public String getInfo(String info_url)
  77 + {
  78 + try {
  79 + // 访问网页
  80 + Document doc = createDocument(info_url);
  81 +
  82 + // 查找正文区域,常见class为 TRS_Editor 或 article
  83 + Element content = null;
  84 + if (info_url.startsWith("http://www.nftec.agri.cn/zcfg"))
  85 + {
  86 + content = doc.selectFirst("div#TRS_AUTOADD");
  87 + }else{
  88 + content = doc.selectFirst("div.TRS_Editor");
  89 + }
  90 + if (content != null) {
  91 + StringBuilder text = new StringBuilder();
  92 +
  93 + // 遍历段落,保留换行
  94 + for (Element p : content.select("p")) {
  95 + String line = p.text().trim();
  96 + if (!line.isEmpty()) {
  97 + text.append(line).append("\n\n");
  98 + }
  99 + }
  100 +
  101 + return text.toString();
  102 + } else {
  103 + logger.error("未找到正文内容区域:"+info_url);
  104 + }
  105 +
  106 + } catch (Exception e) {
  107 + logger.error("解析详情错误:"+info_url,e);
  108 + }
  109 + return null;
  110 + }
  111 +
  112 +}
  1 +package com.zhonglai.luhui.admin.config;
  2 +
  3 +import org.springframework.context.annotation.Configuration;
  4 +import org.springframework.stereotype.Component;
  5 +
  6 +import javax.servlet.*;
  7 +import javax.servlet.http.HttpServletRequest;
  8 +import javax.servlet.http.HttpServletResponse;
  9 +import java.io.IOException;
  10 +@Component
  11 +public class TxtFileEncodingFilter implements Filter {
  12 +
  13 + @Override
  14 + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
  15 + throws IOException, ServletException {
  16 +
  17 + // 如果是.txt文件,设置Content-Type为 text/plain; charset=UTF-8
  18 + if (request instanceof HttpServletRequest) {
  19 + HttpServletRequest req = (HttpServletRequest) request;
  20 + String uri = req.getRequestURI();
  21 + if (uri != null && uri.endsWith(".txt")) {
  22 + HttpServletResponse resp = (HttpServletResponse) response;
  23 + resp.setContentType("text/plain; charset=UTF-8");
  24 + }
  25 + }
  26 +
  27 + chain.doFilter(request, response);
  28 + }
  29 +}
@@ -173,6 +173,15 @@ public class ServerController extends BaseController @@ -173,6 +173,15 @@ public class ServerController extends BaseController
173 HttpUtil.post("https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=6d6b4790-d20f-4692-bc4a-e8ceb1e8cfda",jsonObject.toJSONString()); 173 HttpUtil.post("https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=6d6b4790-d20f-4692-bc4a-e8ceb1e8cfda",jsonObject.toJSONString());
174 return AjaxResult.success(); 174 return AjaxResult.success();
175 } 175 }
  176 +
  177 + @ApiOperation("获取当天水产新闻")
  178 + @GetMapping("/getNowAquaticPublicOpinion")
  179 + public AjaxResult getNowAquaticPublicOpinion()
  180 + {
  181 + List<Map<String,Object>> lsit = publicService.getObjectListBySQL("SELECT * FROM `fish_aquatic_public_opinion` WHERE release_time='"+DateUtils.getDate()+" 00:00:00'");
  182 + return AjaxResult.success(lsit);
  183 + }
  184 +
176 @ApiOperation("获取服务器应用列表") 185 @ApiOperation("获取服务器应用列表")
177 @GetMapping("/getServiceAoolicationList") 186 @GetMapping("/getServiceAoolicationList")
178 public Map<String,Object> getServiceAoolicationList() 187 public Map<String,Object> getServiceAoolicationList()
@@ -307,7 +316,15 @@ public class ServerController extends BaseController @@ -307,7 +316,15 @@ public class ServerController extends BaseController
307 { 316 {
308 startPage(); 317 startPage();
309 startOrderBy(); 318 startOrderBy();
310 - List<Map<String,Object>> list = publicService.getObjectList(sysMonitorApplicationLog,"*",null,null,0,0); 319 + Map<String, Object> map = new HashMap<>();
  320 + Map<String, Object> params = sysMonitorApplicationLog.getParams();
  321 + if(params.containsKey("s_timestamp") && params.containsKey("e_timestamp"))
  322 + {
  323 + map.put("end_timestamp",params.get("e_timestamp"));
  324 + sysMonitorApplicationLog.setTimestamp((String) params.get("s_timestamp"));
  325 + map.put("timestamp","time=");
  326 + }
  327 + List<Map<String,Object>> list = publicService.getObjectList(sysMonitorApplicationLog,"*",map,null,0,0);
311 return getDataTable(list); 328 return getDataTable(list);
312 } 329 }
313 330
1 -# 项目相关配置 jhlt: # 名称 name: zhonglai # 版本 version: 3.8.2 # 版权年份 copyrightYear: 2022 # 实例演示开关 demoEnabled: true # 文件路径 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath) profile: D:/ruoyi/uploadPath # 获取ip地址开关 addressEnabled: false # 验证码类型 math 数组计算 char 字符验证 captchaType: math # 开发环境配置 server: # 服务器的HTTP端口,默认为8080 port: 8080 servlet: # 应用的访问路径 context-path: / tomcat: # tomcat的URI编码 uri-encoding: UTF-8 # 连接数满后的排队数,默认为100 accept-count: 1000 threads: # tomcat最大线程数,默认为200 max: 800 # Tomcat启动初始化的线程数,默认值10 min-spare: 100 # 日志配置 logging: level: com.ruoyi: debug org.springframework: warn com.zhonglai.luhui: debug # Spring配置 spring: # 资源信息 messages: # 国际化资源文件路径 basename: i18n/messages profiles: active: druid # 文件上传 servlet: multipart: # 单个文件大小 max-file-size: 10MB # 设置总上传的文件大小 max-request-size: 20MB # 服务模块 devtools: restart: # 热部署开关 enabled: true # redis 配置 redis: # 地址 host: 119.23.218.181 # 端口,默认为6379 port: 6379 # 数据库索引 database: 1 # 密码 password: # 连接超时时间 timeout: 10s lettuce: pool: # 连接池中的最小空闲连接 min-idle: 0 # 连接池中的最大空闲连接 max-idle: 8 # 连接池的最大数据库连接数 max-active: 8 # #连接池最大阻塞等待时间(使用负值表示没有限制) max-wait: -1ms # token配置 token: # 令牌自定义标识 header: Authorization # 令牌密钥 secret: abcdefghijklmnopqrstuvwxyz # 令牌有效期(默认30分钟) expireTime: 1440 rediskey: lh-admin # MyBatis配置 mybatis: # 搜索指定包别名 typeAliasesPackage: com.ruoyi.**.domain,com.zhonglai.**.domain # 配置mapper的扫描,找到所有的mapper.xml映射文件 mapperLocations: classpath*:mapper/**/*Mapper.xml # 加载全局的配置文件 configLocation: classpath:mybatis/mybatis-config.xml # PageHelper分页插件 pagehelper: helperDialect: mysql supportMethodsArguments: true params: count=countSql # Swagger配置 swagger: # 是否开启swagger enabled: true # 请求前缀 pathMapping: /dev-api # 防止XSS攻击 xss: # 过滤开关 enabled: true # 排除链接(多个用逗号分隔) excludes: /system/notice # 匹配链接 urlPatterns: /system/*,/monitor/*,/tool/* mqtt: client: device_life: 180 sys: ## // 对于登录login 注册register 验证码captchaImage 允许匿名访问 antMatchers: /login,/register,/captchaImage,/getCacheObject,/v2/api-docs,/tool/gen/generatorCodeFromDb,/data/**,/monitor/server/upload,/monitor/server/getSysMonitorServerList,/monitor/server/getSysMonitorServerLogList # NameServer地址 rocketmq: name-server: 8.129.224.117:9876 # 默认的消息组 producer: group: deviceCommand send-message-timeout: 30000 send-topic: lh-mqtt-service-deviceCommand-test send-tags: 1  
  1 +# 项目相关配置 jhlt: # 名称 name: zhonglai # 版本 version: 3.8.2 # 版权年份 copyrightYear: 2022 # 实例演示开关 demoEnabled: true # 文件路径 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath) profile: E:/work/idea/Luhui/uploadPath # 获取ip地址开关 addressEnabled: false # 验证码类型 math 数组计算 char 字符验证 captchaType: math # 开发环境配置 server: # 服务器的HTTP端口,默认为8080 port: 8080 servlet: # 应用的访问路径 context-path: / tomcat: # tomcat的URI编码 uri-encoding: UTF-8 # 连接数满后的排队数,默认为100 accept-count: 1000 threads: # tomcat最大线程数,默认为200 max: 800 # Tomcat启动初始化的线程数,默认值10 min-spare: 100 # 日志配置 logging: level: com.ruoyi: debug org.springframework: warn com.zhonglai.luhui: debug # Spring配置 spring: # 资源信息 messages: # 国际化资源文件路径 basename: i18n/messages profiles: active: druid # 文件上传 servlet: multipart: # 单个文件大小 max-file-size: 10MB # 设置总上传的文件大小 max-request-size: 20MB # 服务模块 devtools: restart: # 热部署开关 enabled: true # redis 配置 redis: # 地址 host: 119.23.218.181 # 端口,默认为6379 port: 6379 # 数据库索引 database: 1 # 密码 password: # 连接超时时间 timeout: 10s lettuce: pool: # 连接池中的最小空闲连接 min-idle: 0 # 连接池中的最大空闲连接 max-idle: 8 # 连接池的最大数据库连接数 max-active: 8 # #连接池最大阻塞等待时间(使用负值表示没有限制) max-wait: -1ms # token配置 token: # 令牌自定义标识 header: Authorization # 令牌密钥 secret: abcdefghijklmnopqrstuvwxyz # 令牌有效期(默认30分钟) expireTime: 1440 rediskey: lh-admin # MyBatis配置 mybatis: # 搜索指定包别名 typeAliasesPackage: com.ruoyi.**.domain,com.zhonglai.**.domain # 配置mapper的扫描,找到所有的mapper.xml映射文件 mapperLocations: classpath*:mapper/**/*Mapper.xml # 加载全局的配置文件 configLocation: classpath:mybatis/mybatis-config.xml # PageHelper分页插件 pagehelper: helperDialect: mysql supportMethodsArguments: true params: count=countSql # Swagger配置 swagger: # 是否开启swagger enabled: true # 请求前缀 pathMapping: /dev-api # 防止XSS攻击 xss: # 过滤开关 enabled: true # 排除链接(多个用逗号分隔) excludes: /system/notice # 匹配链接 urlPatterns: /system/*,/monitor/*,/tool/* mqtt: client: device_life: 180 sys: ## // 对于登录login 注册register 验证码captchaImage 允许匿名访问 antMatchers: /login,/register,/captchaImage,/getCacheObject,/v2/api-docs,/tool/gen/generatorCodeFromDb,/data/**,/monitor/server/upload,/monitor/server/getSysMonitorServerList,/monitor/server/getSysMonitorServerLogList # NameServer地址 rocketmq: name-server: 8.129.224.117:9876 # 默认的消息组 producer: group: deviceCommand send-message-timeout: 30000 send-topic: lh-mqtt-service-deviceCommand-test send-tags: 1
@@ -615,6 +615,11 @@ @@ -615,6 +615,11 @@
615 <version>1.18</version> 615 <version>1.18</version>
616 </dependency> 616 </dependency>
617 617
  618 + <dependency>
  619 + <groupId>org.jsoup</groupId>
  620 + <artifactId>jsoup</artifactId>
  621 + <version>1.17.2</version>
  622 + </dependency>
618 </dependencies> 623 </dependencies>
619 624
620 625