first commit
This commit is contained in:
commit
788302b850
113
pom.xml
Normal file
113
pom.xml
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-parent</artifactId>
|
||||||
|
<version>3.2.5</version>
|
||||||
|
<relativePath /> <!-- lookup parent from repository -->
|
||||||
|
</parent>
|
||||||
|
<groupId>tech.riemann</groupId>
|
||||||
|
<artifactId>bidding</artifactId>
|
||||||
|
<version>1.0.0</version>
|
||||||
|
<name>bidding</name>
|
||||||
|
<description>招标信息爬虫</description>
|
||||||
|
<properties>
|
||||||
|
<java.version>17</java.version>
|
||||||
|
</properties>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-context</artifactId>
|
||||||
|
<version>6.1.6</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.nutz</groupId>
|
||||||
|
<artifactId>nutz-spring-boot-starter</artifactId>
|
||||||
|
<version>3.3.1</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.ibeetl</groupId>
|
||||||
|
<artifactId>beetl</artifactId>
|
||||||
|
<version>3.16.1.RELEASE</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba</groupId>
|
||||||
|
<artifactId>druid-spring-boot-3-starter</artifactId>
|
||||||
|
<version>1.2.22</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.oceanbase</groupId>
|
||||||
|
<artifactId>oceanbase-client</artifactId>
|
||||||
|
<version>2.4.9</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>club.zhcs</groupId>
|
||||||
|
<artifactId>open-api-spring-boot-starter</artifactId>
|
||||||
|
<version>3.2.5</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jsoup</groupId>
|
||||||
|
<artifactId>jsoup</artifactId>
|
||||||
|
<version>1.17.2</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.mysql</groupId>
|
||||||
|
<artifactId>mysql-connector-j</artifactId>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- added on 20250214 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.seleniumhq.selenium</groupId>
|
||||||
|
<artifactId>selenium-java</artifactId>
|
||||||
|
<version>4.11.0</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.seleniumhq.selenium</groupId>
|
||||||
|
<artifactId>selenium-chrome-driver</artifactId>
|
||||||
|
<version>4.11.0</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<finalName>${project.artifactId}</finalName>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<excludes>
|
||||||
|
<exclude>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
</exclude>
|
||||||
|
</excludes>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
26
src/main/java/tech/riemann/bidding/BiddingApplication.java
Normal file
26
src/main/java/tech/riemann/bidding/BiddingApplication.java
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package tech.riemann.bidding;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.scheduling.TaskScheduler;
|
||||||
|
import org.springframework.scheduling.annotation.EnableAsync;
|
||||||
|
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||||
|
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
@EnableScheduling
|
||||||
|
@EnableAsync
|
||||||
|
public class BiddingApplication {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(BiddingApplication.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
TaskScheduler taskScheduler() {
|
||||||
|
ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
|
||||||
|
taskScheduler.setPoolSize(50);
|
||||||
|
return taskScheduler;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,273 @@
|
|||||||
|
package tech.riemann.bidding.component;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import org.nutz.dao.Cnd;
|
||||||
|
import org.nutz.http.Header;
|
||||||
|
import org.nutz.http.Http;
|
||||||
|
import org.nutz.json.Json;
|
||||||
|
import org.nutz.lang.Lang;
|
||||||
|
import org.nutz.lang.util.NutMap;
|
||||||
|
import org.nutz.log.Logs;
|
||||||
|
import org.nutz.spring.boot.service.ExtService;
|
||||||
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
|
import org.springframework.scheduling.annotation.Async;
|
||||||
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
|
|
||||||
|
import tech.riemann.bidding.entity.Notice;
|
||||||
|
import tech.riemann.bidding.entity.Notice.Channel;
|
||||||
|
import tech.riemann.bidding.entity.ScheduledLog;
|
||||||
|
import tech.riemann.bidding.repository.NoticeRepository;
|
||||||
|
import tech.riemann.bidding.repository.ScheduledLogRepository;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 通用方法,非请勿动!!!
|
||||||
|
*/
|
||||||
|
public abstract class NoticeCollector implements InitializingBean {
|
||||||
|
/**
|
||||||
|
* 机器人地址
|
||||||
|
*/
|
||||||
|
public static final String ROBOT_URL = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=7251bcc2-7158-4175-a5ef-bc529e432ee6";
|
||||||
|
/**
|
||||||
|
* 机器人地址(三人群)
|
||||||
|
*/
|
||||||
|
public static final String ROBOT_URL_GX = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=cc1e8fe7-3244-4ffe-80ac-b1528b202a01";
|
||||||
|
/**
|
||||||
|
* 关键词
|
||||||
|
*/
|
||||||
|
public static final String KEY_WORDS = "人力外包、驻场、资源池、软件开发、技术服务、开发服务、人员外包、技术开发、现场、外协";
|
||||||
|
/**
|
||||||
|
* 行业限定
|
||||||
|
*/
|
||||||
|
public static final String INDUSTRY_KEY_WORDS = "证券、基金、保险、信托、资管、银行、养老金、中国、国家、烟草、电力、电信、移动";
|
||||||
|
/**
|
||||||
|
* 重点关注
|
||||||
|
*/
|
||||||
|
public static final String ATTENTION_KEY_WORDS = "资源池、外包";
|
||||||
|
/**
|
||||||
|
* 黑名單
|
||||||
|
*/
|
||||||
|
public static final String BLOCKED_KEY_WORDS = "单一、竞争、中标、结果、询价、公示、终止、成交、延期、磋商、失败";
|
||||||
|
|
||||||
|
public static final Pattern DATE_PATTERN = Pattern.compile("\\d{4}[-]\\d{2}[-]\\d{2}");
|
||||||
|
public static final Pattern NUMBER_PATTERN = Pattern.compile("\\d+");
|
||||||
|
/**
|
||||||
|
* 待发送队列
|
||||||
|
*/
|
||||||
|
public static final LinkedBlockingQueue<Notice> NOTICES_QUEUE = new LinkedBlockingQueue<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提取日期
|
||||||
|
*
|
||||||
|
* @param info
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public LocalDate date(String info) {
|
||||||
|
Matcher matcher = DATE_PATTERN.matcher(info);
|
||||||
|
if (matcher.find()) {
|
||||||
|
String date = matcher.group();
|
||||||
|
return LocalDate.parse(date, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
|
||||||
|
}
|
||||||
|
return LocalDate.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提取数字
|
||||||
|
*
|
||||||
|
* @param info
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static long number(String info) {
|
||||||
|
Matcher matcher = NUMBER_PATTERN.matcher(info);
|
||||||
|
if (matcher.find()) {
|
||||||
|
String data = matcher.group();
|
||||||
|
return Long.parseLong(data);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 采集渠道
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public abstract Channel channel();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 采集方法,对应渠道自行实现
|
||||||
|
*/
|
||||||
|
public abstract void collect();
|
||||||
|
|
||||||
|
protected static final NutMap DUPLICATE_STATUS = NutMap.NEW();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 开始采集,由采集渠道自行决定定时频率等
|
||||||
|
*/
|
||||||
|
@Async
|
||||||
|
@Scheduled(cron = "0 */2 * * * ?")
|
||||||
|
public void start() {
|
||||||
|
DUPLICATE_STATUS.setv(channel().name(), 0);// 新一轮调度,重置当前渠道的重复数据
|
||||||
|
Logs.get().debugf("渠道:%s 开始进行数据采集!", channel().getDescription());
|
||||||
|
ScheduledLog log = startLog();
|
||||||
|
collect();
|
||||||
|
stopLog(log);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 推送数据
|
||||||
|
*
|
||||||
|
* @param notices
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public boolean pushNotices(List<Notice> notices) {
|
||||||
|
return notices.stream().map(this::pushNotice).allMatch(item -> item); // 全部都OK,不然就终止
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 推送数据
|
||||||
|
*
|
||||||
|
* @param notice
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public boolean pushNotice(Notice notice) {
|
||||||
|
if (notice.getPublishDate().isBefore(LocalDate.now().minusDays(2))) { // 两天之前的数据,直接不要了
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (noticeRepository().count(Cnd.where(Notice::getChannel, ExtService.EQ, notice.getChannel())
|
||||||
|
.and(Notice::getKey, ExtService.EQ, notice.getKey())) == 0) {
|
||||||
|
// 没有,插入数据,发送通知
|
||||||
|
noticeRepository().insert(notice);
|
||||||
|
sendMessage(notice);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
int currentDuplicate = DUPLICATE_STATUS.getInt(channel().name());
|
||||||
|
if (currentDuplicate >= 20) {// 超过3次重复,终止采集
|
||||||
|
DUPLICATE_STATUS.setv(channel().name(), 0);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
DUPLICATE_STATUS.setv(channel().name(), currentDuplicate + 1); // 重复的次数
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 看门狗,单独线程扫描待发送渠道进行消息发送,需要渠道实现通过
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void afterPropertiesSet() {
|
||||||
|
String messageTemplate = """
|
||||||
|
<font>%s</font>
|
||||||
|
>发布时间:<font color='comment'>%s</font>
|
||||||
|
>来源:<font color='comment'>%s</font>
|
||||||
|
>详情:<font color='comment'>[点击查看详情](%s)</font>
|
||||||
|
""";
|
||||||
|
new Thread(() -> {
|
||||||
|
while (true) {
|
||||||
|
Notice notice = NOTICES_QUEUE.poll();
|
||||||
|
if (notice != null) {
|
||||||
|
// 发送消息通知
|
||||||
|
Http.post3(ROBOT_URL,
|
||||||
|
Json.toJson(NutMap.NEW().addv("msgtype", "markdown").addv("markdown",
|
||||||
|
NutMap.NEW().addv("content",
|
||||||
|
String.format(messageTemplate, notice.getTitle(), notice.getPublishDate(),
|
||||||
|
notice.getChannel().getDescription(), notice.getUrl())))),
|
||||||
|
Header.create().asJsonContentType(), 500);
|
||||||
|
Http.post3(ROBOT_URL_GX,
|
||||||
|
Json.toJson(NutMap.NEW().addv("msgtype", "markdown").addv("markdown",
|
||||||
|
NutMap.NEW().addv("content",
|
||||||
|
String.format(messageTemplate, notice.getTitle(), notice.getPublishDate(),
|
||||||
|
notice.getChannel().getDescription(), notice.getUrl())))),
|
||||||
|
Header.create().asJsonContentType(), 500);
|
||||||
|
if (attention(notice)) {
|
||||||
|
// 发送AT助理消息
|
||||||
|
Http.post3(ROBOT_URL,
|
||||||
|
Json.toJson(NutMap.NEW().addv("msgtype", "text").addv("text",
|
||||||
|
NutMap.NEW().addv("mentioned_mobile_list", Lang.list("13811608471","13018059968","18343641000","18996359755","13673518683"))
|
||||||
|
.addv("content", "检测到【资源池、外包】项目,请立即确认!"+ notice.getTitle() +": "+ notice.getUrl()))),
|
||||||
|
Header.create().asJsonContentType(), 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Thread.sleep(notice == null ? 100 : 5000);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
throw Lang.wrapThrow(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean attention(Notice notice) {
|
||||||
|
List<String> keyWords = Lang.list(ATTENTION_KEY_WORDS.split("、"));
|
||||||
|
return keyWords.stream().anyMatch(key -> notice.getTitle().contains(key))
|
||||||
|
|| keyWords.stream().anyMatch(key -> notice.getContent().contains(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送消息
|
||||||
|
*
|
||||||
|
* @param notice
|
||||||
|
*/
|
||||||
|
public void sendMessage(Notice notice) {
|
||||||
|
/**
|
||||||
|
* 1. 关键词匹配放入队列<br>
|
||||||
|
* 2. 队列数据单独线程定时消费
|
||||||
|
*/
|
||||||
|
if (match(notice)) {
|
||||||
|
NOTICES_QUEUE.add(notice);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送消息的匹配规则
|
||||||
|
*
|
||||||
|
* @param notice 公告
|
||||||
|
* @return 是否匹配
|
||||||
|
*/
|
||||||
|
public boolean match(Notice notice) {
|
||||||
|
List<String> blacks = Lang.list(BLOCKED_KEY_WORDS.split("、"));
|
||||||
|
List<String> keyWords = Lang.list(KEY_WORDS.split("、"));
|
||||||
|
if (blacks.stream().noneMatch(key -> notice.getTitle().contains(key))
|
||||||
|
&& blacks.stream().noneMatch(key -> notice.getContent().contains(key))) {
|
||||||
|
return keyWords.stream().anyMatch(key -> notice.getTitle().contains(key))
|
||||||
|
|| keyWords.stream().anyMatch(key -> notice.getContent().contains(key));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract ScheduledLogRepository scheduledLogRepository();
|
||||||
|
|
||||||
|
public abstract NoticeRepository noticeRepository();
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 开始日志
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public ScheduledLog startLog() {
|
||||||
|
return ScheduledLog.builder().channel(channel()).threadId(Thread.currentThread().getId()).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 停止日志
|
||||||
|
*
|
||||||
|
* @param log
|
||||||
|
*/
|
||||||
|
public void stopLog(ScheduledLog log) {
|
||||||
|
log.setEnd(LocalDateTime.now());
|
||||||
|
scheduledLogRepository().insert(log);
|
||||||
|
}
|
||||||
|
public static void main(String[] args) {
|
||||||
|
Http.post3(ROBOT_URL,
|
||||||
|
Json.toJson(NutMap.NEW().addv("msgtype", "text").addv("text",
|
||||||
|
NutMap.NEW().addv("mentioned_mobile_list", Lang.list("13811608471","13018059968","18343641000","18996359755","13673518683"))
|
||||||
|
.addv("content", "本条消息是测试,检测到【资源池、人力外包】项目,请立即确认!"))),
|
||||||
|
Header.create().asJsonContentType(), 500);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,95 @@
|
|||||||
|
package tech.riemann.bidding.component.impl;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
import org.jsoup.nodes.Element;
|
||||||
|
import org.jsoup.select.Elements;
|
||||||
|
import org.nutz.lang.Lang;
|
||||||
|
import org.nutz.log.Log;
|
||||||
|
import org.nutz.log.Logs;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import tech.riemann.bidding.component.NoticeCollector;
|
||||||
|
import tech.riemann.bidding.entity.Notice;
|
||||||
|
import tech.riemann.bidding.entity.Notice.Channel;
|
||||||
|
import tech.riemann.bidding.repository.NoticeRepository;
|
||||||
|
import tech.riemann.bidding.repository.ScheduledLogRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class BOCNoticeCollector extends NoticeCollector {
|
||||||
|
|
||||||
|
private final NoticeRepository noticeRepository;
|
||||||
|
|
||||||
|
private static final Log logger = Logs.get();
|
||||||
|
private final ScheduledLogRepository scheduledLogRepository;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ScheduledLogRepository scheduledLogRepository() {
|
||||||
|
return scheduledLogRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NoticeRepository noticeRepository() {
|
||||||
|
return noticeRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Channel channel() {
|
||||||
|
return Channel.BOC;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void collect() {
|
||||||
|
for (int page = 1; page <= 20; page++) {
|
||||||
|
String indexPageName = page == 1 ? "" : "_" + page;
|
||||||
|
String url = String.format(channel().getUrl(), indexPageName);
|
||||||
|
logger.debugf("获取页面内容,Url:%s", url);
|
||||||
|
try {
|
||||||
|
Document document = Jsoup.connect(url).get();
|
||||||
|
Elements elements = document.select(".news ul li");
|
||||||
|
if (elements.isEmpty()) {
|
||||||
|
logger.debugf("没有内容,退出:%s", url);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
List<Notice> notices = Lang.list();
|
||||||
|
for (Element element : elements) {
|
||||||
|
String link = element.select("a").attr("href");
|
||||||
|
Notice notice = Notice.builder()
|
||||||
|
.channel(channel())
|
||||||
|
.key(link.replace("/","").replace("_", "").replace(".html", "").replace(".", ""))
|
||||||
|
.title(element.select("a").text())
|
||||||
|
.content("")
|
||||||
|
.url("https://www.bankofchina.com/aboutboc/bi6"+link.substring(1))
|
||||||
|
.publishDate(date(element.select("span").text().substring(2,12)))
|
||||||
|
.build();
|
||||||
|
notices.add(notice);
|
||||||
|
}
|
||||||
|
if (!pushNotices(notices)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.debug(e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean match(Notice notice) {
|
||||||
|
List<String> blacks = Lang.list(BLOCKED_KEY_WORDS.split("、"));
|
||||||
|
List<String> keyWords = Lang.list(KEY_WORDS.split("、"));
|
||||||
|
if (blacks.stream().noneMatch(key -> notice.getTitle().contains(key))
|
||||||
|
&& blacks.stream().noneMatch(key -> notice.getContent().contains(key))) {
|
||||||
|
return keyWords.stream().anyMatch(key -> notice.getTitle().contains(key));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,133 @@
|
|||||||
|
package tech.riemann.bidding.component.impl;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
import org.jsoup.nodes.Element;
|
||||||
|
import org.jsoup.select.Elements;
|
||||||
|
import org.nutz.lang.Lang;
|
||||||
|
import org.nutz.log.Log;
|
||||||
|
import org.nutz.log.Logs;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import tech.riemann.bidding.component.NoticeCollector;
|
||||||
|
import tech.riemann.bidding.entity.Notice;
|
||||||
|
import tech.riemann.bidding.entity.Notice.Channel;
|
||||||
|
import tech.riemann.bidding.repository.NoticeRepository;
|
||||||
|
import tech.riemann.bidding.repository.ScheduledLogRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class BOCQNoticeCollector extends NoticeCollector {
|
||||||
|
|
||||||
|
private final NoticeRepository noticeRepository;
|
||||||
|
|
||||||
|
private static final Log logger = Logs.get();
|
||||||
|
private final ScheduledLogRepository scheduledLogRepository;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ScheduledLogRepository scheduledLogRepository() {
|
||||||
|
return scheduledLogRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NoticeRepository noticeRepository() {
|
||||||
|
return noticeRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Channel channel() {
|
||||||
|
return Channel.BOCQ;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void collect() {
|
||||||
|
for (int page = 1; page <= 20; page++) {
|
||||||
|
String indexPageName = page == 1 ? "" : "_" + page;
|
||||||
|
String url = String.format(channel().getUrl(), indexPageName);
|
||||||
|
logger.debugf("获取页面内容,Url:%s", url);
|
||||||
|
try {
|
||||||
|
Document document = Jsoup.connect(url).get();
|
||||||
|
Elements elements = document.select(".dhy_b li");
|
||||||
|
if (elements.isEmpty()) {
|
||||||
|
logger.debugf("没有内容,退出:%s", url);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
List<Notice> notices = Lang.list();
|
||||||
|
for (Element element : elements) {
|
||||||
|
String link = element.select("a").attr("href");
|
||||||
|
Notice notice = Notice.builder()
|
||||||
|
.channel(channel())
|
||||||
|
.key(link.replace("/","").replace("_", "").replace(".html", "").replace(".", ""))
|
||||||
|
.title(element.select("a").text())
|
||||||
|
.content("")
|
||||||
|
.url("http://www.cqcbank.com.cn"+link)
|
||||||
|
.publishDate(date(element.select("span").text()))
|
||||||
|
.build();
|
||||||
|
notices.add(notice);
|
||||||
|
}
|
||||||
|
if (!pushNotices(notices)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.debug(e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean match(Notice notice) {
|
||||||
|
List<String> blacks = Lang.list(BLOCKED_KEY_WORDS.split("、"));
|
||||||
|
List<String> keyWords = Lang.list(KEY_WORDS.split("、"));
|
||||||
|
if (blacks.stream().noneMatch(key -> notice.getTitle().contains(key))
|
||||||
|
&& blacks.stream().noneMatch(key -> notice.getContent().contains(key))) {
|
||||||
|
return keyWords.stream().anyMatch(key -> notice.getTitle().contains(key));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
for (int page = 1; page <= 20; page++) {
|
||||||
|
String indexPageName = page == 1 ? "" : "_" + page;
|
||||||
|
String url = String.format(Channel.BOCQ.getUrl(), indexPageName);
|
||||||
|
System.out.println("获取页面内容,Url:"+url);
|
||||||
|
try {
|
||||||
|
Document document = Jsoup.connect(url).get();
|
||||||
|
Elements elements = document.select(".dhy_b li");
|
||||||
|
if (elements.isEmpty()) {
|
||||||
|
System.out.println("没有内容,退出:"+url);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
List<Notice> notices = Lang.list();
|
||||||
|
for (Element element : elements) {
|
||||||
|
System.out.println("element:" + element);
|
||||||
|
String link = element.select("a").attr("href");
|
||||||
|
LocalDate pDate = LocalDate.parse(element.select("span").text());
|
||||||
|
Notice notice = Notice.builder()
|
||||||
|
.channel(Channel.BOCQ)
|
||||||
|
.key(link.replace("/","").replace("_", "").replace(".html", "").replace(".", ""))
|
||||||
|
.title(element.select("a").text())
|
||||||
|
.content("")
|
||||||
|
.url("http://www.cqcbank.com.cn"+link)
|
||||||
|
.publishDate(pDate)
|
||||||
|
.build();
|
||||||
|
System.out.println("notice:\t"+notice.toString());
|
||||||
|
notices.add(notice);
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
// logger.debug(e);
|
||||||
|
System.out.println("IOException:" + e.getMessage());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// });
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,92 @@
|
|||||||
|
package tech.riemann.bidding.component.impl;
|
||||||
|
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
import org.jsoup.nodes.Element;
|
||||||
|
import org.jsoup.select.Elements;
|
||||||
|
import org.nutz.dao.Cnd;
|
||||||
|
import org.nutz.http.Header;
|
||||||
|
import org.nutz.http.Http;
|
||||||
|
import org.nutz.http.Response;
|
||||||
|
import org.nutz.lang.Strings;
|
||||||
|
import org.nutz.log.Logs;
|
||||||
|
import org.nutz.spring.boot.service.ExtService;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import tech.riemann.bidding.component.NoticeCollector;
|
||||||
|
import tech.riemann.bidding.entity.Notice;
|
||||||
|
import tech.riemann.bidding.entity.Notice.Channel;
|
||||||
|
import tech.riemann.bidding.repository.NoticeRepository;
|
||||||
|
import tech.riemann.bidding.repository.ScheduledLogRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上海宝华国际渠道采集器
|
||||||
|
*/
|
||||||
|
// @Component
|
||||||
|
@Deprecated
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class BaoSteelNoticeCollector extends NoticeCollector {
|
||||||
|
|
||||||
|
private final NoticeRepository noticeRepository;
|
||||||
|
private final ScheduledLogRepository scheduledLogRepository;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NoticeRepository noticeRepository() {
|
||||||
|
return noticeRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ScheduledLogRepository scheduledLogRepository() {
|
||||||
|
return scheduledLogRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Channel channel() {
|
||||||
|
return Channel.BAO_STEEL;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void collect() {
|
||||||
|
// 没有cookie会报521,目前cookie值写死,后续需要研究是不是在请求首页的时候自动下发了cookie信息
|
||||||
|
Response response = Http.get(channel().getUrl(),
|
||||||
|
Header.create()
|
||||||
|
.set("Host", "baosteelbidding.zbytb.com")
|
||||||
|
.set("Referer", "https://baosteelbidding.zbytb.com/")
|
||||||
|
.set("Cookie",
|
||||||
|
"__jsluid_s=ac68c37cdc617959a40cef00227811e1; Du4_city=132%7Chttps%3A%2F%2Fbaosteelbidding.zbytb.com%2F; __jsl_clearance_s=1712113445.458|0|%2F%2BzxEoTkUCTMNxnyj9Nu2c1jodI%3D; Hm_lvt_47b9a4b804f6b4f81affae66cb8a57e9=1712023024,1712113449; Hm_lpvt_47b9a4b804f6b4f81affae66cb8a57e9=1712113475; Du4_vistor_st=3")
|
||||||
|
.set("User-Agent",
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0"),
|
||||||
|
5000);
|
||||||
|
Logs.get().debugf("上海宝华国际渠道采集,网页访问状态码: %d", response.getStatus());
|
||||||
|
if (response.isOK()) {
|
||||||
|
// dom 解析
|
||||||
|
Document document = Jsoup.parse(response.getContent());
|
||||||
|
// .li_dot
|
||||||
|
Elements elements = document.select("ul.li_dot li");
|
||||||
|
Notice notice = Notice.builder().build();
|
||||||
|
for (Element element : elements) {
|
||||||
|
if (Strings.equals(element.attr("class"), "kws")) { // 第二行
|
||||||
|
notice.setPublishDate(date(element.text()));
|
||||||
|
if (noticeRepository.count(Cnd.where(Notice::getChannel, ExtService.EQ, notice.getChannel())
|
||||||
|
.and(Notice::getKey, ExtService.EQ, notice.getKey())) == 0) {
|
||||||
|
// 没有,插入数据,发送通知
|
||||||
|
noticeRepository.insert(notice);
|
||||||
|
sendMessage(notice);
|
||||||
|
}
|
||||||
|
} else { // 第一行
|
||||||
|
Elements link = element.select("a");
|
||||||
|
String url = link.first().attr("href");
|
||||||
|
notice = Notice.builder()
|
||||||
|
.channel(channel())
|
||||||
|
.key(number(url) + "")
|
||||||
|
.title(link.first().text())
|
||||||
|
.content("")
|
||||||
|
.url(url)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,135 @@
|
|||||||
|
package tech.riemann.bidding.component.impl;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.temporal.ChronoUnit;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.nutz.http.Http;
|
||||||
|
import org.nutz.http.Response;
|
||||||
|
import org.nutz.lang.Lang;
|
||||||
|
import org.nutz.lang.util.NutMap;
|
||||||
|
import org.nutz.log.Logs;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import tech.riemann.bidding.component.NoticeCollector;
|
||||||
|
import tech.riemann.bidding.entity.Notice;
|
||||||
|
import tech.riemann.bidding.entity.Notice.Channel;
|
||||||
|
import tech.riemann.bidding.repository.NoticeRepository;
|
||||||
|
import tech.riemann.bidding.repository.ScheduledLogRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class CFCPNNoticeCollector extends NoticeCollector {
|
||||||
|
|
||||||
|
private final NoticeRepository noticeRepository;
|
||||||
|
private final ScheduledLogRepository scheduledLogRepository;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ScheduledLogRepository scheduledLogRepository() {
|
||||||
|
return scheduledLogRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NoticeRepository noticeRepository() {
|
||||||
|
return noticeRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Channel channel() {
|
||||||
|
return Channel.CFCPN;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void collect() {
|
||||||
|
for (int page = 1;; page++) {
|
||||||
|
Response response = Http.post2(channel().getUrl(), NutMap.NEW().addv("noticeType", 1).addv("noticeState", 1)
|
||||||
|
.addv("pageNo", page).addv("isValid", 1).addv("orderBy", "publish_time desc"), 5000, 5000);
|
||||||
|
if (response.isOK()) {
|
||||||
|
List<NutMap> records = Lang.map(response.getContent()).getList("rows", NutMap.class);
|
||||||
|
if (records.isEmpty()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
List<Notice> notices = records.stream().map(item -> {
|
||||||
|
String content = "";
|
||||||
|
return Notice.builder().key(item.getString("id")).channel(channel())
|
||||||
|
.title(item.getString("noticeTitle")).content(content)
|
||||||
|
.url(String.format(
|
||||||
|
"http://www.cfcpn.com/jcw/sys/index/goUrl?url=modules/sys/login/detail&column=undefined&searchVal=%s",
|
||||||
|
item.getString("id")))
|
||||||
|
.publishDate(item.getAs("publishTime", LocalDateTime.class).toLocalDate()).build();
|
||||||
|
}).collect(Collectors.toList());
|
||||||
|
|
||||||
|
if (!pushNotices(notices)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Thread.sleep(5000);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Logs.get().debug(e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (notices.stream()
|
||||||
|
.anyMatch(item -> item.getPublishDate().isBefore(LocalDate.now().minus(2, ChronoUnit.DAYS)))) {// 早于两天的数据了,不再翻页
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Logs.get().debugf("请求发生错误,状态码为:%d", response.getStatus());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
for (int page = 1;; page++) {
|
||||||
|
Response response = Http.post2(Channel.CFCPN.getUrl(),
|
||||||
|
NutMap.NEW().addv("noticeType", 1).addv("noticeState", 1).addv("pageNo", page).addv("isValid", 1)
|
||||||
|
.addv("orderBy", "publish_time desc"),
|
||||||
|
5000, 5000);
|
||||||
|
if (response.isOK()) {
|
||||||
|
List<NutMap> records = Lang.map(response.getContent()).getList("rows", NutMap.class);
|
||||||
|
if (records.isEmpty()) {
|
||||||
|
Logs.get().info("records is empty!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
List<Notice> notices = records.stream().map(item -> {
|
||||||
|
String content = "";
|
||||||
|
return Notice.builder().key(item.getString("id")).channel(Channel.CFCPN)
|
||||||
|
.title(item.getString("noticeTitle")).content(content)
|
||||||
|
.url(String.format(
|
||||||
|
"http://www.cfcpn.com/jcw/sys/index/goUrl?url=modules/sys/login/detail&column=undefined&searchVal=%s",
|
||||||
|
item.getString("id")))
|
||||||
|
.publishDate(item.getAs("publishTime", LocalDateTime.class).toLocalDate()).build();
|
||||||
|
}).collect(Collectors.toList());
|
||||||
|
|
||||||
|
// if (!pushNotices(notices)) {
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
try {
|
||||||
|
Thread.sleep(5000);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Logs.get().info("Exception happened:" + e.toString());
|
||||||
|
Logs.get().debug(e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
notices.stream().forEach(System.out::println);
|
||||||
|
System.out.println("page=" + page + "" + "now():" + LocalDate.now() + "now()-5days:"
|
||||||
|
+ LocalDate.now().minus(5, ChronoUnit.DAYS));
|
||||||
|
if (notices.stream()
|
||||||
|
.anyMatch(item -> item.getPublishDate().isBefore(LocalDate.now().minus(5, ChronoUnit.DAYS)))) {// 早于两天的数据了,不再翻页
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Logs.get().info("请求发生错误,状态码为:%d" + response.getStatus());
|
||||||
|
Logs.get().debugf("请求发生错误,状态码为:%d", response.getStatus());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,150 @@
|
|||||||
|
package tech.riemann.bidding.component.impl;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
import org.jsoup.nodes.Element;
|
||||||
|
import org.jsoup.select.Elements;
|
||||||
|
import org.nutz.lang.Lang;
|
||||||
|
import org.nutz.log.Log;
|
||||||
|
import org.nutz.log.Logs;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import tech.riemann.bidding.component.NoticeCollector;
|
||||||
|
import tech.riemann.bidding.entity.Notice;
|
||||||
|
import tech.riemann.bidding.entity.Notice.Channel;
|
||||||
|
import tech.riemann.bidding.repository.NoticeRepository;
|
||||||
|
import tech.riemann.bidding.repository.ScheduledLogRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class CQRCBNoticeCollector extends NoticeCollector {
|
||||||
|
|
||||||
|
private final NoticeRepository noticeRepository;
|
||||||
|
|
||||||
|
private static final Log logger = Logs.get();
|
||||||
|
private final ScheduledLogRepository scheduledLogRepository;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ScheduledLogRepository scheduledLogRepository() {
|
||||||
|
return scheduledLogRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NoticeRepository noticeRepository() {
|
||||||
|
return noticeRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Channel channel() {
|
||||||
|
return Channel.CQRCB;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void collect() {
|
||||||
|
int totalPages = getTotalPages();
|
||||||
|
totalPages = totalPages > 20 ? 20 : totalPages;
|
||||||
|
for (int page = 1; page <= totalPages; page++) {
|
||||||
|
String urlSuffixStr = page == 1 ? "index" : "index_" + page + ".html";
|
||||||
|
String url = String.format(channel().getUrl(), urlSuffixStr);
|
||||||
|
logger.debugf("获取页面内容,Url:%s", url);
|
||||||
|
try {
|
||||||
|
Document document = Jsoup.connect(url).get();
|
||||||
|
Elements elements = document.select(".sideCont ul li");
|
||||||
|
if (elements.isEmpty()) {
|
||||||
|
logger.debugf("没有内容,退出:%s", url);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
List<Notice> notices = Lang.list();
|
||||||
|
for (Element element : elements) {
|
||||||
|
String link = element.select("a").attr("href");
|
||||||
|
Notice notice = Notice.builder()
|
||||||
|
.channel(channel())
|
||||||
|
.key(link.replace("/","").replace(".html", ""))
|
||||||
|
.title(element.select("a").text().replace("·",""))
|
||||||
|
.content("")
|
||||||
|
.url("https://www.cqrcb.com"+link)
|
||||||
|
.publishDate(date(element.select("span").text().substring(1,11)))
|
||||||
|
.build();
|
||||||
|
notices.add(notice);
|
||||||
|
}
|
||||||
|
if (!pushNotices(notices)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.debug(e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getTotalPages() {
|
||||||
|
String tempUrlStr = channel().getUrl() + "/index.html";
|
||||||
|
int totalPages;
|
||||||
|
try {
|
||||||
|
Document document = Jsoup.connect(tempUrlStr).get();
|
||||||
|
Elements elements = document.select(".pages em");
|
||||||
|
String pageRelativeStr = elements.first().select("span").text();
|
||||||
|
int lastSlashPos = pageRelativeStr.lastIndexOf("/");
|
||||||
|
totalPages = Integer.parseInt(pageRelativeStr.substring(lastSlashPos+1));
|
||||||
|
} catch (Exception e) {
|
||||||
|
// TODO: handle exception
|
||||||
|
logger.debug(e);
|
||||||
|
totalPages = 0;
|
||||||
|
}
|
||||||
|
return totalPages;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean match(Notice notice) {
|
||||||
|
List<String> blacks = Lang.list(BLOCKED_KEY_WORDS.split("、"));
|
||||||
|
List<String> keyWords = Lang.list(KEY_WORDS.split("、"));
|
||||||
|
if (blacks.stream().noneMatch(key -> notice.getTitle().contains(key))
|
||||||
|
&& blacks.stream().noneMatch(key -> notice.getContent().contains(key))) {
|
||||||
|
return keyWords.stream().anyMatch(key -> notice.getTitle().contains(key));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
for (int page = 1; page <= 20; page++) {
|
||||||
|
String urlSuffixStr = page == 1 ? "index" : "index_" + page + ".html";
|
||||||
|
String url = String.format(Channel.CQRCB.getUrl(), urlSuffixStr);
|
||||||
|
System.out.println("获取页面内容,Url:"+url);
|
||||||
|
try {
|
||||||
|
Document document = Jsoup.connect(url).get();
|
||||||
|
Elements elements = document.select(".sideCont ul li");
|
||||||
|
if (elements.isEmpty()) {
|
||||||
|
System.out.println("没有内容,退出:"+url);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
List<Notice> notices = Lang.list();
|
||||||
|
for (Element element : elements) {
|
||||||
|
String link = element.select("a").attr("href");
|
||||||
|
LocalDate pDate = LocalDate.parse(element.select("span").text().substring(1,11));
|
||||||
|
Notice notice = Notice.builder()
|
||||||
|
.channel(Channel.CQRCB)
|
||||||
|
.key(link.replace("/","").replace(".html", ""))
|
||||||
|
.title(element.select("a").text().replace("·",""))
|
||||||
|
.content("")
|
||||||
|
.url("https://www.cqrcb.com"+link)
|
||||||
|
.publishDate(pDate)
|
||||||
|
.build();
|
||||||
|
System.out.println("notice:\t"+notice.toString());
|
||||||
|
notices.add(notice);
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.debug(e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// });
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,82 @@
|
|||||||
|
package tech.riemann.bidding.component.impl;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
import org.jsoup.nodes.Element;
|
||||||
|
import org.jsoup.select.Elements;
|
||||||
|
import org.nutz.lang.Lang;
|
||||||
|
import org.nutz.log.Logs;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import tech.riemann.bidding.component.NoticeCollector;
|
||||||
|
import tech.riemann.bidding.entity.Notice;
|
||||||
|
import tech.riemann.bidding.entity.Notice.Channel;
|
||||||
|
import tech.riemann.bidding.repository.NoticeRepository;
|
||||||
|
import tech.riemann.bidding.repository.ScheduledLogRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class ChinaCcsscmNoticeCollector extends NoticeCollector {
|
||||||
|
|
||||||
|
private final NoticeRepository noticeRepository;
|
||||||
|
|
||||||
|
private final ScheduledLogRepository scheduledLogRepository;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ScheduledLogRepository scheduledLogRepository() {
|
||||||
|
return scheduledLogRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NoticeRepository noticeRepository() {
|
||||||
|
return noticeRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Channel channel() {
|
||||||
|
return Channel.CHINA_CCSSCM;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void collect() {
|
||||||
|
for (int page = 1;; page++) {
|
||||||
|
String url = String.format(channel().getUrl(), page);
|
||||||
|
try {
|
||||||
|
Document document = Jsoup.connect(url).get();
|
||||||
|
Elements elements = document.select(".Top5 ul li");
|
||||||
|
// 发起
|
||||||
|
if (elements.isEmpty()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
List<Notice> notices = Lang.list();
|
||||||
|
for (Element element : elements) {
|
||||||
|
String link = element.select("a").attr("href");
|
||||||
|
Notice notice = Notice.builder()
|
||||||
|
.channel(channel())
|
||||||
|
.key(number(link) + "")
|
||||||
|
.title(element.select("a").text())
|
||||||
|
.content("")
|
||||||
|
.url(link.startsWith("http") ? link : "https://zb.chinaccsscm.cn" + link)
|
||||||
|
.publishDate(date(element.select(".Right").text()))
|
||||||
|
.build();
|
||||||
|
notices.add(notice);
|
||||||
|
}
|
||||||
|
if (!pushNotices(notices)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
Logs.get().debug(e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,88 @@
|
|||||||
|
package tech.riemann.bidding.component.impl;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
import org.jsoup.nodes.Element;
|
||||||
|
import org.jsoup.select.Elements;
|
||||||
|
import org.nutz.lang.Lang;
|
||||||
|
import org.nutz.log.Log;
|
||||||
|
import org.nutz.log.Logs;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import tech.riemann.bidding.component.NoticeCollector;
|
||||||
|
import tech.riemann.bidding.entity.Notice;
|
||||||
|
import tech.riemann.bidding.entity.Notice.Channel;
|
||||||
|
import tech.riemann.bidding.repository.NoticeRepository;
|
||||||
|
import tech.riemann.bidding.repository.ScheduledLogRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class EBIDDINGNoticeCollector extends NoticeCollector {
|
||||||
|
|
||||||
|
private final NoticeRepository noticeRepository;
|
||||||
|
|
||||||
|
private static final Log logger = Logs.get();
|
||||||
|
private final ScheduledLogRepository scheduledLogRepository;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ScheduledLogRepository scheduledLogRepository() {
|
||||||
|
return scheduledLogRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NoticeRepository noticeRepository() {
|
||||||
|
return noticeRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Channel channel() {
|
||||||
|
return Channel.E_BIDDING;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void collect() {
|
||||||
|
Arrays.stream(KEY_WORDS.split("、")).forEach(keyword -> {
|
||||||
|
logger.debugf("爬取关键字,Url:%s", keyword);
|
||||||
|
for (int page = 1; page <= 20; page++) {
|
||||||
|
String url = String.format(channel().getUrl(), keyword, page);
|
||||||
|
logger.debugf("获取页面内容,Url:%s", url);
|
||||||
|
try {
|
||||||
|
Document document = Jsoup.connect(url).get();
|
||||||
|
Elements elements = document.select(".newslist>li");
|
||||||
|
if (elements.isEmpty()) {
|
||||||
|
logger.debugf("没有内容,退出:%s", url);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
List<Notice> notices = Lang.list();
|
||||||
|
for (Element element : elements) {
|
||||||
|
String link = element.select("a").attr("href");
|
||||||
|
Notice notice = Notice.builder()
|
||||||
|
.channel(channel())
|
||||||
|
.key(number(link.substring(link.lastIndexOf("/"))) + "")
|
||||||
|
.title(element.select("a").attr("title"))
|
||||||
|
.content("")
|
||||||
|
.url(link)
|
||||||
|
.publishDate(date(element.select(".newsDate div").text()))
|
||||||
|
.build();
|
||||||
|
notices.add(notice);
|
||||||
|
}
|
||||||
|
if (!pushNotices(notices)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
logger.debug(e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,99 @@
|
|||||||
|
package tech.riemann.bidding.component.impl;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
import org.jsoup.nodes.Element;
|
||||||
|
import org.jsoup.select.Elements;
|
||||||
|
import org.nutz.lang.Lang;
|
||||||
|
import org.nutz.log.Log;
|
||||||
|
import org.nutz.log.Logs;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import tech.riemann.bidding.component.NoticeCollector;
|
||||||
|
import tech.riemann.bidding.entity.Notice;
|
||||||
|
import tech.riemann.bidding.entity.Notice.Channel;
|
||||||
|
import tech.riemann.bidding.repository.NoticeRepository;
|
||||||
|
import tech.riemann.bidding.repository.ScheduledLogRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class GCZBNoticeCollector extends NoticeCollector {
|
||||||
|
|
||||||
|
private final NoticeRepository noticeRepository;
|
||||||
|
|
||||||
|
private static final Log logger = Logs.get();
|
||||||
|
private final ScheduledLogRepository scheduledLogRepository;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ScheduledLogRepository scheduledLogRepository() {
|
||||||
|
return scheduledLogRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NoticeRepository noticeRepository() {
|
||||||
|
return noticeRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Channel channel() {
|
||||||
|
return Channel.GCZB;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void collect() {
|
||||||
|
Arrays.stream(KEY_WORDS.split("、")).forEach(keyword -> {
|
||||||
|
logger.debugf("爬取关键字,Url:%s", keyword);
|
||||||
|
for (int page = 1; page <= 20; page++) {
|
||||||
|
String url = String.format(channel().getUrl(), page, keyword);
|
||||||
|
logger.debugf("获取页面内容,Url:%s", url);
|
||||||
|
try {
|
||||||
|
Document document = Jsoup.connect(url).get();
|
||||||
|
Elements elements = document.select(".lists_center ul li");
|
||||||
|
if (elements.isEmpty()) {
|
||||||
|
logger.debugf("没有内容,退出:%s", url);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
List<Notice> notices = Lang.list();
|
||||||
|
for (Element element : elements) {
|
||||||
|
String link = element.select("a").attr("href");
|
||||||
|
Notice notice = Notice.builder()
|
||||||
|
.channel(channel())
|
||||||
|
.key(number(link) + "")
|
||||||
|
.title(element.select("a").text())
|
||||||
|
.content("")
|
||||||
|
.url(link)
|
||||||
|
.publishDate(date(element.select("b").text()))
|
||||||
|
.build();
|
||||||
|
notices.add(notice);
|
||||||
|
}
|
||||||
|
if (!pushNotices(notices)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
logger.debug(e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean match(Notice notice) {
|
||||||
|
List<String> blacks = Lang.list(BLOCKED_KEY_WORDS.split("、"));
|
||||||
|
List<String> keyWords = Lang.list(KEY_WORDS.split("、"));
|
||||||
|
List<String> industrys = Lang.list(INDUSTRY_KEY_WORDS.split("、"));
|
||||||
|
if (blacks.stream().noneMatch(key -> notice.getTitle().contains(key)) && blacks.stream().noneMatch(key -> notice.getContent().contains(key))) {
|
||||||
|
return keyWords.stream().anyMatch(key -> notice.getTitle().contains(key)) && industrys.stream().anyMatch(key -> notice.getTitle().contains(key));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,87 @@
|
|||||||
|
package tech.riemann.bidding.component.impl;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
import org.jsoup.nodes.Element;
|
||||||
|
import org.jsoup.select.Elements;
|
||||||
|
import org.nutz.lang.Lang;
|
||||||
|
import org.nutz.log.Log;
|
||||||
|
import org.nutz.log.Logs;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import tech.riemann.bidding.component.NoticeCollector;
|
||||||
|
import tech.riemann.bidding.entity.Notice;
|
||||||
|
import tech.riemann.bidding.entity.Notice.Channel;
|
||||||
|
import tech.riemann.bidding.repository.NoticeRepository;
|
||||||
|
import tech.riemann.bidding.repository.ScheduledLogRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class GXZBNoticeCollector extends NoticeCollector {
|
||||||
|
|
||||||
|
private final NoticeRepository noticeRepository;
|
||||||
|
private final ScheduledLogRepository scheduledLogRepository;
|
||||||
|
private static final Log logger = Logs.get();
|
||||||
|
@Override
|
||||||
|
public ScheduledLogRepository scheduledLogRepository() {
|
||||||
|
return scheduledLogRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NoticeRepository noticeRepository() {
|
||||||
|
return noticeRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Channel channel() {
|
||||||
|
return Channel.GXZB;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void collect() {
|
||||||
|
for (int page = 1; page <= 20; page++) {
|
||||||
|
String url = String.format(channel().getUrl(), page);
|
||||||
|
logger.debugf("获取页面内容,Url:%s", url);
|
||||||
|
try {
|
||||||
|
Document document = Jsoup.connect(url).get();
|
||||||
|
Elements elements = document.select(".newslist>li");
|
||||||
|
if (elements.isEmpty()) {
|
||||||
|
logger.debugf("没有内容,退出:%s", url);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
List<Notice> notices = Lang.list();
|
||||||
|
for (Element element : elements) {
|
||||||
|
String link = element.select("a").attr("href");
|
||||||
|
Notice notice = Notice.builder()
|
||||||
|
.channel(channel())
|
||||||
|
.key(number(link.substring(link.lastIndexOf("/"))) + "")
|
||||||
|
.title(element.select("a").attr("title"))
|
||||||
|
.content("")
|
||||||
|
.url(link)
|
||||||
|
.publishDate(date(element.select(".newsDate div").text()))
|
||||||
|
.build();
|
||||||
|
notices.add(notice);
|
||||||
|
}
|
||||||
|
if (!pushNotices(notices)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
logger.debug(e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static void main(String[] args) {
|
||||||
|
String link = "https://ebid.gxzb.com.cn/biddingBulletin/2024-04-25/46236.html";
|
||||||
|
String number = NoticeCollector.number(link.substring(link.lastIndexOf("/")))+"";
|
||||||
|
System.out.println(number);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,111 @@
|
|||||||
|
package tech.riemann.bidding.component.impl;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.time.temporal.ChronoUnit;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.nutz.http.Header;
|
||||||
|
import org.nutz.http.Http;
|
||||||
|
import org.nutz.http.Response;
|
||||||
|
import org.nutz.json.Json;
|
||||||
|
import org.nutz.lang.Lang;
|
||||||
|
import org.nutz.lang.util.NutMap;
|
||||||
|
import org.nutz.log.Log;
|
||||||
|
import org.nutz.log.Logs;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import tech.riemann.bidding.component.NoticeCollector;
|
||||||
|
import tech.riemann.bidding.entity.Notice;
|
||||||
|
import tech.riemann.bidding.entity.Notice.Channel;
|
||||||
|
import tech.riemann.bidding.repository.NoticeRepository;
|
||||||
|
import tech.riemann.bidding.repository.ScheduledLogRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class PICCECNoticeCollector extends NoticeCollector {
|
||||||
|
|
||||||
|
private final NoticeRepository noticeRepository;
|
||||||
|
private final ScheduledLogRepository scheduledLogRepository;
|
||||||
|
private static final Log logger = Logs.get();
|
||||||
|
@Override
|
||||||
|
public ScheduledLogRepository scheduledLogRepository() {
|
||||||
|
return scheduledLogRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NoticeRepository noticeRepository() {
|
||||||
|
return noticeRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Channel channel() {
|
||||||
|
return Channel.PICCEC;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void collect() {
|
||||||
|
//供应商征集
|
||||||
|
//"siteId": "725", "categoryId": "211", "city": "", "county": "", "purchaseMode": ""
|
||||||
|
//
|
||||||
|
for (int page = 1;; page++) {
|
||||||
|
Response response = Http.post3(channel().getUrl(),
|
||||||
|
Json.toJson(NutMap.NEW()
|
||||||
|
.addv("dto", NutMap.NEW().addv("siteId", "725")
|
||||||
|
.addv("categoryId", "211,213,214,215,216,217")
|
||||||
|
.addv("city", "")
|
||||||
|
.addv("county", "")
|
||||||
|
.addv("purchaseMode", "")
|
||||||
|
)
|
||||||
|
.addv("pageNo", page)
|
||||||
|
.addv("pageSize", 10)),
|
||||||
|
Header.create().asJsonContentType(),
|
||||||
|
5000);
|
||||||
|
if (response.isOK()) {
|
||||||
|
List<NutMap> records = Lang.map(response.getContent()).getAs("res",NutMap.class).getList("rows", NutMap.class);
|
||||||
|
if (records.isEmpty()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
List<Notice> notices = records.stream()
|
||||||
|
.map(item -> {
|
||||||
|
String content = "";
|
||||||
|
String url=item.getString("url");
|
||||||
|
logger.debugf("获取详情页面,Url:%s", url);
|
||||||
|
return Notice.builder()
|
||||||
|
.key(Arrays.stream(url.split("/")).map(NoticeCollector::number).filter(urlitem->urlitem != 0).map(urlitem->urlitem+"").collect(Collectors.joining("-")))
|
||||||
|
.channel(channel())
|
||||||
|
.title(item.getString("title"))
|
||||||
|
.content(content)
|
||||||
|
.url("https://ec.picc.com/cms/default/webfile"+url)
|
||||||
|
.publishDate( LocalDate.parse(item.getString("publishDate"), DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'+0800'", Locale.CHINESE)))
|
||||||
|
.build();
|
||||||
|
})
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
if (!pushNotices(notices)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Thread.sleep(5000);
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
Logs.get().debug(e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (notices.stream().anyMatch(item -> item.getPublishDate().isBefore(LocalDate.now().minus(2, ChronoUnit.DAYS)))) {// 早于两天的数据了,不再翻页
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Logs.get().debugf("请求发生错误,状态码为:%d", response.getStatus());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,136 @@
|
|||||||
|
package tech.riemann.bidding.component.impl;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
import org.jsoup.nodes.Element;
|
||||||
|
import org.jsoup.select.Elements;
|
||||||
|
import org.nutz.lang.Lang;
|
||||||
|
import org.nutz.log.Log;
|
||||||
|
import org.nutz.log.Logs;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import tech.riemann.bidding.component.NoticeCollector;
|
||||||
|
import tech.riemann.bidding.entity.Notice;
|
||||||
|
import tech.riemann.bidding.entity.Notice.Channel;
|
||||||
|
import tech.riemann.bidding.repository.NoticeRepository;
|
||||||
|
import tech.riemann.bidding.repository.ScheduledLogRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class SWSCNoticeCollector extends NoticeCollector {
|
||||||
|
|
||||||
|
private final NoticeRepository noticeRepository;
|
||||||
|
|
||||||
|
private static final Log logger = Logs.get();
|
||||||
|
private final ScheduledLogRepository scheduledLogRepository;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ScheduledLogRepository scheduledLogRepository() {
|
||||||
|
return scheduledLogRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NoticeRepository noticeRepository() {
|
||||||
|
return noticeRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Channel channel() {
|
||||||
|
return Channel.SWSC;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void collect() {
|
||||||
|
for (int page = 1; page <= 20; page++) {
|
||||||
|
String urlSuffixStr = page == 1 ? "" : "index_" + page + ".html";
|
||||||
|
String url = String.format(channel().getUrl(), urlSuffixStr);
|
||||||
|
logger.debugf("获取页面内容,Url:%s", url);
|
||||||
|
try {
|
||||||
|
Document document = Jsoup.connect(url).get();
|
||||||
|
Elements elements = document.select(".m-list ul li");
|
||||||
|
if (elements.isEmpty()) {
|
||||||
|
logger.debugf("没有内容,退出:%s", url);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
List<Notice> notices = Lang.list();
|
||||||
|
for (Element element : elements) {
|
||||||
|
String link = element.select("a").attr("href");
|
||||||
|
Notice notice = Notice.builder()
|
||||||
|
.channel(channel())
|
||||||
|
.key(link.replace("/","").replace(".html", ""))
|
||||||
|
.title(element.select("a").attr("title"))
|
||||||
|
.content("")
|
||||||
|
.url("https://www.swsc.com.cn"+link)
|
||||||
|
.publishDate(date(element.select(".li-right").text()))
|
||||||
|
.build();
|
||||||
|
notices.add(notice);
|
||||||
|
}
|
||||||
|
if (!pushNotices(notices)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.debug(e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean match(Notice notice) {
|
||||||
|
List<String> blacks = Lang.list(BLOCKED_KEY_WORDS.split("、"));
|
||||||
|
List<String> keyWords = Lang.list(KEY_WORDS.split("、"));
|
||||||
|
if (blacks.stream().noneMatch(key -> notice.getTitle().contains(key))
|
||||||
|
&& blacks.stream().noneMatch(key -> notice.getContent().contains(key))) {
|
||||||
|
return keyWords.stream().anyMatch(key -> notice.getTitle().contains(key));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
for (int page = 1; page <= 20; page++) {
|
||||||
|
String urlSuffixStr = page == 1 ? "" : "index_" + page + ".html";
|
||||||
|
String url = String.format(Channel.SWSC.getUrl(), urlSuffixStr);
|
||||||
|
System.out.println("获取页面内容,Url:"+url);
|
||||||
|
try {
|
||||||
|
Document document = Jsoup.connect(url).get();
|
||||||
|
Elements elements = document.select(".m-list ul li");
|
||||||
|
if (elements.isEmpty()) {
|
||||||
|
System.out.println("没有内容,退出:"+url);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
List<Notice> notices = Lang.list();
|
||||||
|
for (Element element : elements) {
|
||||||
|
System.out.println("element:" + element);
|
||||||
|
String link = element.select("a").attr("href");
|
||||||
|
LocalDate pDate = LocalDate.parse(element.select(".li-right").text());
|
||||||
|
Notice notice = Notice.builder()
|
||||||
|
.channel(Channel.SWSC)
|
||||||
|
.key(link.replace("/","").replace(".html", ""))
|
||||||
|
.title(element.select("a").attr("title"))
|
||||||
|
.content(element.select("a").text())
|
||||||
|
.url("https://www.swsc.com.cn"+link)
|
||||||
|
.publishDate(pDate)
|
||||||
|
.build();
|
||||||
|
System.out.println("notice:\t"+notice.toString());
|
||||||
|
notices.add(notice);
|
||||||
|
}
|
||||||
|
// if (!pushNotices(notices)) {
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
} catch (IOException e) {
|
||||||
|
// logger.debug(e);
|
||||||
|
System.out.println("IOException:" + e.getMessage());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// });
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,114 @@
|
|||||||
|
package tech.riemann.bidding.component.impl;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.time.temporal.ChronoUnit;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.nutz.http.Http;
|
||||||
|
import org.nutz.http.Response;
|
||||||
|
import org.nutz.lang.Lang;
|
||||||
|
import org.nutz.lang.util.NutMap;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import tech.riemann.bidding.component.NoticeCollector;
|
||||||
|
import tech.riemann.bidding.entity.Notice;
|
||||||
|
import tech.riemann.bidding.entity.Notice.Channel;
|
||||||
|
import tech.riemann.bidding.repository.NoticeRepository;
|
||||||
|
import tech.riemann.bidding.repository.ScheduledLogRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 中化商务采集器
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class SinoChemitcNoticeCollector extends NoticeCollector {
|
||||||
|
|
||||||
|
private final NoticeRepository noticeRepository;
|
||||||
|
private final ScheduledLogRepository scheduledLogRepository;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ScheduledLogRepository scheduledLogRepository() {
|
||||||
|
return scheduledLogRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NoticeRepository noticeRepository() {
|
||||||
|
return noticeRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum Type {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
ZB_YS_BG(1, "招标/预审/变更"),
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
FZB(3, "非招标"),
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
PB_ZB_JG(2, "评标/中标结果");
|
||||||
|
|
||||||
|
int value;
|
||||||
|
String description;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String url(int page, String start, String end) {
|
||||||
|
return String.format(channel().getUrl(), page, 50, Type.ZB_YS_BG.getValue(), start, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Channel channel() {
|
||||||
|
return Channel.SINOCHEMITC;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void collect() {
|
||||||
|
for (int page = 1;; page++) {
|
||||||
|
Response response = Http.get(url(page, LocalDate.now().minus(1, ChronoUnit.DAYS).format(DateTimeFormatter.ofPattern("yyyy-MM-dd")), ""));
|
||||||
|
if (response.isOK()) {
|
||||||
|
String content = response.getContent();
|
||||||
|
List<NutMap> records = Lang.map(content).getAs("data", NutMap.class).getList("records", NutMap.class);
|
||||||
|
if (records.isEmpty()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
List<Notice> notices = records.stream()
|
||||||
|
.map(d -> Notice.builder()
|
||||||
|
.channel(channel())
|
||||||
|
.key(d.getString("id"))
|
||||||
|
.title(d.getString("title"))
|
||||||
|
.content(d.getString("content"))
|
||||||
|
.url(String
|
||||||
|
.format("https://d.sinochemitc.com/#/zcnotice/detale/xq?id=%s", d.getString("id")))
|
||||||
|
.publishDate(d.getAs("publishDate", LocalDate.class))
|
||||||
|
.build())
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
if (!pushNotices(notices)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean match(Notice notice) {
|
||||||
|
List<String> blacks = Lang.list(BLOCKED_KEY_WORDS.split("、"));
|
||||||
|
List<String> keyWords = Lang.list(KEY_WORDS.split("、"));
|
||||||
|
if (blacks.stream().noneMatch(key -> notice.getTitle().contains(key)) && blacks.stream().noneMatch(key -> notice.getContent().contains(key))) {
|
||||||
|
return keyWords.stream().anyMatch(key -> notice.getTitle().contains(key));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
49
src/main/java/tech/riemann/bidding/entity/IdBaseEntity.java
Normal file
49
src/main/java/tech/riemann/bidding/entity/IdBaseEntity.java
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
package tech.riemann.bidding.entity;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
import org.nutz.dao.entity.annotation.Column;
|
||||||
|
import org.nutz.dao.entity.annotation.Comment;
|
||||||
|
import org.nutz.spring.boot.service.entity.IdEntity;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat.Shape;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema.RequiredMode;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder.Default;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
import lombok.experimental.SuperBuilder;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@SuperBuilder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Accessors(chain = true)
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class IdBaseEntity extends IdEntity {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Schema(description = "创建时间", requiredMode = RequiredMode.NOT_REQUIRED)
|
||||||
|
@Column("created_time")
|
||||||
|
@Comment("创建时间")
|
||||||
|
@Default
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", locale = "GMT+8", shape = Shape.STRING)
|
||||||
|
protected LocalDateTime createdTime = LocalDateTime.now();
|
||||||
|
|
||||||
|
@Schema(description = "最后更新时间", requiredMode = RequiredMode.NOT_REQUIRED)
|
||||||
|
@Column("updated_time")
|
||||||
|
@Comment("最后更新时间")
|
||||||
|
@Default
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", locale = "GMT+8", shape = Shape.STRING)
|
||||||
|
protected LocalDateTime updatedTime = LocalDateTime.now();
|
||||||
|
|
||||||
|
}
|
190
src/main/java/tech/riemann/bidding/entity/Notice.java
Normal file
190
src/main/java/tech/riemann/bidding/entity/Notice.java
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
package tech.riemann.bidding.entity;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
|
||||||
|
import org.nutz.dao.entity.annotation.ColDefine;
|
||||||
|
import org.nutz.dao.entity.annotation.ColType;
|
||||||
|
import org.nutz.dao.entity.annotation.Column;
|
||||||
|
import org.nutz.dao.entity.annotation.Comment;
|
||||||
|
import org.nutz.dao.entity.annotation.Table;
|
||||||
|
import org.nutz.json.JsonField;
|
||||||
|
import org.nutz.lang.util.NutMap;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonGetter;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema.RequiredMode;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
import lombok.experimental.FieldNameConstants;
|
||||||
|
import lombok.experimental.SuperBuilder;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@SuperBuilder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@FieldNameConstants
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@Accessors(chain = true)
|
||||||
|
@Table("t_notice")
|
||||||
|
@Comment("招标公告")
|
||||||
|
@Schema(name = "Notice", description = "招标公告")
|
||||||
|
public class Notice extends IdBaseEntity {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Schema(description = "公告唯一标识(渠道内唯一)", requiredMode = RequiredMode.REQUIRED)
|
||||||
|
@Column("n_key")
|
||||||
|
@Comment("公告唯一标识(渠道内唯一)")
|
||||||
|
@ColDefine(notNull = true, width = 100)
|
||||||
|
String key;
|
||||||
|
|
||||||
|
@Schema(description = "公告标题", requiredMode = RequiredMode.REQUIRED)
|
||||||
|
@Column("n_title")
|
||||||
|
@Comment("公告标题")
|
||||||
|
@ColDefine(notNull = true, width = 200)
|
||||||
|
String title;
|
||||||
|
|
||||||
|
@Schema(description = "公告链接", requiredMode = RequiredMode.REQUIRED)
|
||||||
|
@Column("n_url")
|
||||||
|
@Comment("公告链接")
|
||||||
|
@ColDefine(notNull = true, width = 500)
|
||||||
|
String url;
|
||||||
|
|
||||||
|
@Schema(description = "公告渠道", requiredMode = RequiredMode.REQUIRED)
|
||||||
|
@Column("n_channel")
|
||||||
|
@Comment("公告渠道")
|
||||||
|
@ColDefine(notNull = true, width = 50)
|
||||||
|
Channel channel;
|
||||||
|
|
||||||
|
@Schema(description = "公告内容", requiredMode = RequiredMode.AUTO)
|
||||||
|
@Column("n_content")
|
||||||
|
@Comment("公告内容")
|
||||||
|
@ColDefine(notNull = false, type = ColType.TEXT)
|
||||||
|
String content;
|
||||||
|
|
||||||
|
@Schema(description = "招标金额", requiredMode = RequiredMode.AUTO)
|
||||||
|
@Column("n_amount")
|
||||||
|
@Comment("招标金额")
|
||||||
|
@ColDefine(notNull = false)
|
||||||
|
double amount;
|
||||||
|
|
||||||
|
@Schema(description = "发布时间", requiredMode = RequiredMode.AUTO)
|
||||||
|
@Column("n_publish_date")
|
||||||
|
@Comment("发布时间")
|
||||||
|
@ColDefine(notNull = false)
|
||||||
|
LocalDate publishDate;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum Channel {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
SINOCHEMITC("sinochemitc", "中化商务",
|
||||||
|
"https://d.sinochemitc.com/api/management/bidding/hy-bulletin-list?current=%d&size=%d&bulletinType=%d&bidType=&keyword=&startTime=%s&endTime=%s"),
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
CHINA_CCSSCM(
|
||||||
|
"chinaccsscm", "中通服总部",
|
||||||
|
"https://zb.chinaccsscm.cn/zbgg/index_%d.jhtml"),
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
CFCPN(
|
||||||
|
"cfcpn", "金采网",
|
||||||
|
"http://www.cfcpn.com/jcw/noticeinfo/noticeInfo/dataNoticeList"),
|
||||||
|
/**
|
||||||
|
* 1天内,北京、上海、重庆,只匹配标题
|
||||||
|
*/
|
||||||
|
GCZB(
|
||||||
|
"gczb", "招标与采购网",
|
||||||
|
"https://www.gc-zb.com/search/index.html?page=%d&keyword=%s&h_lx=&h_province=19,43,47&vague=0&date=1&search_field=1"),
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
BOC(
|
||||||
|
"boc", "中国银行采购公告",
|
||||||
|
"https://www.bankofchina.com/aboutboc/bi6/index%s.html"),
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
PICCEC(
|
||||||
|
"piccec", "人保E采",
|
||||||
|
"https://ec.picc.com/cms/api/dynamicData/queryContentPage"),
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
GXZB(
|
||||||
|
"gxzb", "国信招标",
|
||||||
|
"https://ebid.gxzb.com.cn/cms/category/bulletinList.html?searchDate=1999-04-25&dates=300&word=&categoryId=88&exactSearch=&industryName=&status=&tabName=招标投标&page=%d"),
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
BAO_STEEL(
|
||||||
|
"baosteel", "上海宝华国际",
|
||||||
|
"https://baosteelbidding.zbytb.com/fuwu/"),
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
BOCQ(
|
||||||
|
"bocq", "重庆银行采购供应商征集公告",
|
||||||
|
"http://www.cqcbank.com.cn/cn/jrch/cgxx/hjjkh/ddfa/index%s.html"),
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
SWSC(
|
||||||
|
"swsc", "西南证券",
|
||||||
|
"https://www.swsc.com.cn/html/goSwsc/cgxxgg/cgxmgs/"),
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
CQRCB(
|
||||||
|
"cqrcb", "重庆农村商业银行",
|
||||||
|
"https://www.cqrcb.com/cqrcb/aboutus/cgxx"),
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
CEBPUBSERVICE(
|
||||||
|
"cebpubservice", "中国招标投标公共服务平台",
|
||||||
|
"http://www.cebpubservice.com/"),
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
E_BIDDING(
|
||||||
|
"ebidding", "国信e采",
|
||||||
|
"https://www.e-bidding.org/cms/category/bulletinList.html?searchDate=1999-06-21&dates=300&word=%s&categoryId=88&exactSearch=&industryName=&status=&tabName=招标投标&page=%d"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
GTJA(
|
||||||
|
"gtja", "国泰君安",
|
||||||
|
"https://www.gtja.com/content/info-open/supplier/purchase-info.html?year=&keyword=%s");
|
||||||
|
|
||||||
|
String code;
|
||||||
|
String description;
|
||||||
|
String url;
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonGetter
|
||||||
|
@JsonField
|
||||||
|
public NutMap getChannelInfo() {
|
||||||
|
return channel == null ? null
|
||||||
|
: NutMap.NEW()
|
||||||
|
.addv("name", channel.name())
|
||||||
|
.addv("code", channel.getCode())
|
||||||
|
.addv("description",
|
||||||
|
channel.getDescription());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setChannelInfo(NutMap typeInfo) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
62
src/main/java/tech/riemann/bidding/entity/ScheduledLog.java
Normal file
62
src/main/java/tech/riemann/bidding/entity/ScheduledLog.java
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
package tech.riemann.bidding.entity;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
import org.nutz.dao.entity.annotation.ColDefine;
|
||||||
|
import org.nutz.dao.entity.annotation.Column;
|
||||||
|
import org.nutz.dao.entity.annotation.Comment;
|
||||||
|
import org.nutz.dao.entity.annotation.Table;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema.RequiredMode;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder.Default;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
import lombok.experimental.FieldNameConstants;
|
||||||
|
import lombok.experimental.SuperBuilder;
|
||||||
|
import tech.riemann.bidding.entity.Notice.Channel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@SuperBuilder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@FieldNameConstants
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@Accessors(chain = true)
|
||||||
|
@Table("t_scheduled_log")
|
||||||
|
@Comment("调度日志")
|
||||||
|
@Schema(name = "ScheduledLog", description = "调度日志")
|
||||||
|
public class ScheduledLog extends IdBaseEntity {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Schema(description = "公告渠道", requiredMode = RequiredMode.REQUIRED)
|
||||||
|
@Column("l_channel")
|
||||||
|
@Comment("公告渠道")
|
||||||
|
@ColDefine(notNull = true, width = 50)
|
||||||
|
Channel channel;
|
||||||
|
|
||||||
|
@Schema(description = "开始时间", requiredMode = RequiredMode.REQUIRED)
|
||||||
|
@Column("l_start")
|
||||||
|
@Comment("开始时间")
|
||||||
|
@Default
|
||||||
|
LocalDateTime start = LocalDateTime.now();
|
||||||
|
|
||||||
|
@Schema(description = "结束时间", requiredMode = RequiredMode.REQUIRED)
|
||||||
|
@Column("l_end")
|
||||||
|
@Comment("结束时间")
|
||||||
|
@Default
|
||||||
|
LocalDateTime end = LocalDateTime.now();
|
||||||
|
|
||||||
|
@Schema(description = "线程id", requiredMode = RequiredMode.REQUIRED)
|
||||||
|
@Column("l_thread_id")
|
||||||
|
@Comment("线程id")
|
||||||
|
long threadId;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
package tech.riemann.bidding.repository;
|
||||||
|
|
||||||
|
import org.nutz.dao.Dao;
|
||||||
|
import org.nutz.spring.boot.service.interfaces.IdEntityService;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import tech.riemann.bidding.entity.Notice;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class NoticeRepository implements IdEntityService<Notice> {
|
||||||
|
|
||||||
|
private final Dao dao;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Dao dao() {
|
||||||
|
return dao;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
package tech.riemann.bidding.repository;
|
||||||
|
|
||||||
|
import org.nutz.dao.Dao;
|
||||||
|
import org.nutz.spring.boot.service.interfaces.IdEntityService;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import tech.riemann.bidding.entity.ScheduledLog;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class ScheduledLogRepository implements IdEntityService<ScheduledLog> {
|
||||||
|
|
||||||
|
private final Dao dao;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Dao dao() {
|
||||||
|
return dao;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
1
src/main/resources/application.properties
Normal file
1
src/main/resources/application.properties
Normal file
@ -0,0 +1 @@
|
|||||||
|
spring.application.name=bidding
|
62
src/main/resources/application.yml
Normal file
62
src/main/resources/application.yml
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
spring:
|
||||||
|
application:
|
||||||
|
name: bidding
|
||||||
|
jackson:
|
||||||
|
date-format: yyyy-MM-dd HH:mm:ss
|
||||||
|
datasource:
|
||||||
|
type: com.alibaba.druid.pool.DruidDataSource
|
||||||
|
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||||
|
url: jdbc:mysql://localhost:3306/bidding
|
||||||
|
username: root
|
||||||
|
password: 123456
|
||||||
|
druid:
|
||||||
|
db-type: mysql
|
||||||
|
filters: stat,wall,log4j2
|
||||||
|
initial-size: 10
|
||||||
|
min-idle: 1
|
||||||
|
max-active: 50
|
||||||
|
max-wait: 60000
|
||||||
|
time-between-eviction-runs-millis: 60000
|
||||||
|
min-evictable-idle-time-millis: 300000
|
||||||
|
validation-query: SELECT 'ezalor'
|
||||||
|
test-while-idle: true
|
||||||
|
test-on-borrow: true
|
||||||
|
test-on-return: false
|
||||||
|
pool-prepared-statements: true
|
||||||
|
max-pool-prepared-statement-per-connection-size: 20
|
||||||
|
web-stat-filter:
|
||||||
|
enabled: true
|
||||||
|
url-pattern: /*
|
||||||
|
exclusions: /druid/*,*.js,*.gif,*.jpg,*.png,*.css,*.ico
|
||||||
|
stat-view-servlet:
|
||||||
|
enabled: true
|
||||||
|
url-pattern: /druid/*
|
||||||
|
reset-enable: true
|
||||||
|
nutz:
|
||||||
|
dao:
|
||||||
|
runtime:
|
||||||
|
basepackage:
|
||||||
|
- tech.riemann.bidding.entity
|
||||||
|
- BOOT-INF.classes.tech.riemann.bidding.entity
|
||||||
|
# - tech.riemann.bidding.component
|
||||||
|
check-index: true
|
||||||
|
create: true
|
||||||
|
delete-column: false
|
||||||
|
migration: true
|
||||||
|
sql-template:
|
||||||
|
enable: true
|
||||||
|
type: beetl
|
||||||
|
sql-manager:
|
||||||
|
paths:
|
||||||
|
- sqls/mysql
|
||||||
|
- BOOT-INF/classes/sqls/mysql
|
||||||
|
logging:
|
||||||
|
file:
|
||||||
|
name: ${user.home}/logs/${spring.application.name}.log
|
||||||
|
path: ${user.home}/logs
|
||||||
|
level:
|
||||||
|
"[org.nutz]": debug
|
||||||
|
"[tech.riemann]": debug
|
||||||
|
"[org.apache.logging]": off
|
||||||
|
springfox: off
|
||||||
|
"[io.swagger]": off
|
Loading…
x
Reference in New Issue
Block a user