init project
This commit is contained in:
commit
95d8f7de3b
|
|
@ -0,0 +1 @@
|
||||||
|
DATABASE_URL=mysql://root:Chenweijia113!@localhost/lottery
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
/target
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,16 @@
|
||||||
|
[package]
|
||||||
|
name = "sched_tasks_rs"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
chrono = "0.4.38"
|
||||||
|
clokwerk = "0.4.0"
|
||||||
|
log = "0.4.22"
|
||||||
|
env_logger = "0.9"
|
||||||
|
openssl = { version = "0.10.66", features = ["vendored"] }
|
||||||
|
regex = "1.10.5"
|
||||||
|
reqwest = { version = "0.12.5", features = ["gzip"] }
|
||||||
|
scraper = "0.19.1"
|
||||||
|
sea-orm = { version = "0.12", features = [ "sqlx-mysql", "runtime-tokio-rustls", "macros" ]}
|
||||||
|
tokio = { version = "1.39.2", features = ["full"] }
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
FROM hub.airpig.cn/library/alpine:latest
|
||||||
|
|
||||||
|
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories \
|
||||||
|
&& apk update && apk add tzdata \
|
||||||
|
&& cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
|
||||||
|
&& echo "Asia/Shanghai" > /etc/timezone \
|
||||||
|
&& apk del tzdata
|
||||||
|
|
||||||
|
ENV RUST_LOG=info
|
||||||
|
|
||||||
|
ENV RUST_ENV=prod
|
||||||
|
|
||||||
|
COPY ./target/x86_64-unknown-linux-musl/release/sched_tasks_rs /bin/sched_tasks_rs
|
||||||
|
|
||||||
|
|
||||||
|
ENTRYPOINT ["/bin/sched_tasks_rs"]
|
||||||
|
|
@ -0,0 +1,94 @@
|
||||||
|
use std::{thread, time::Duration};
|
||||||
|
|
||||||
|
use chrono::format;
|
||||||
|
use clokwerk::{Scheduler, TimeUnits, Job};
|
||||||
|
|
||||||
|
use log::{info, error};
|
||||||
|
|
||||||
|
mod tasks;
|
||||||
|
use tasks::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
env_logger::init();
|
||||||
|
let mut scheduler = Scheduler::new();
|
||||||
|
let fmt = "%Y年%m月%d日 %H:%M:%S";
|
||||||
|
let notify_url = "https://ding.airpig.cn/QfagvFa7u323xUS5yRbFR3";
|
||||||
|
scheduler.every(1.day()).at("00:01:00").run(move|| {
|
||||||
|
let rt = tokio::runtime::Runtime::new().unwrap();
|
||||||
|
rt.block_on(async {
|
||||||
|
let res = get_data_from_500_com("pls").await;
|
||||||
|
let msg;
|
||||||
|
match res {
|
||||||
|
Ok(_) => {
|
||||||
|
msg = format!("{}: 排列3数据采集完毕", chrono::Local::now().format(fmt));
|
||||||
|
info!("{}", msg);
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
msg = format!("{}: 排列3数据采集失败: {}", chrono::Local::now().format(fmt), e);
|
||||||
|
error!("{}", msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let notify = reqwest::get(format!("{}/{}", notify_url, msg)).await.unwrap();
|
||||||
|
if notify.status().is_success() {
|
||||||
|
info!("{}: 排列3数据采集通知发送成功", chrono::Local::now().format(fmt));
|
||||||
|
} else {
|
||||||
|
error!("{}: 排列3数据采集通知发送失败", chrono::Local::now().format(fmt));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
scheduler.every(1.day()).at("00:01:15").run(move|| {
|
||||||
|
let rt = tokio::runtime::Runtime::new().unwrap();
|
||||||
|
rt.block_on(async {
|
||||||
|
let res = get_data_from_500_com("plw").await;
|
||||||
|
let msg;
|
||||||
|
match res {
|
||||||
|
Ok(_) => {
|
||||||
|
msg = format!("{}: 排列5数据采集完毕", chrono::Local::now().format(fmt));
|
||||||
|
info!("{}", msg);
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
msg = format!("{}: 排列5数据采集失败: {}", chrono::Local::now().format(fmt), e);
|
||||||
|
error!("{}", msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let notify = reqwest::get(format!("{}/{}", notify_url, msg)).await.unwrap();
|
||||||
|
if notify.status().is_success() {
|
||||||
|
info!("{}: 排列5数据采集通知发送成功", chrono::Local::now().format(fmt));
|
||||||
|
} else {
|
||||||
|
error!("{}: 排列5数据采集通知发送失败", chrono::Local::now().format(fmt));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
scheduler.every(1.day()).at("00:01:30").run(move|| {
|
||||||
|
let rt = tokio::runtime::Runtime::new().unwrap();
|
||||||
|
rt.block_on(async {
|
||||||
|
let res = get_data_from_500_com("sd").await;
|
||||||
|
let msg;
|
||||||
|
match res {
|
||||||
|
Ok(_) => {
|
||||||
|
msg = format!("{}: 福彩3D数据采集完毕", chrono::Local::now().format(fmt));
|
||||||
|
info!("{}", msg);
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
msg = format!("{}: 福彩3D数据采集失败: {}", chrono::Local::now().format(fmt), e);
|
||||||
|
error!("{}", msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let notify = reqwest::get(format!("{}/{}", notify_url, msg)).await.unwrap();
|
||||||
|
if notify.status().is_success() {
|
||||||
|
info!("{}: 福彩3D数据采集通知发送成功", chrono::Local::now().format(fmt));
|
||||||
|
} else {
|
||||||
|
error!("{}: 福彩3D数据采集通知发送失败", chrono::Local::now().format(fmt));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
info!("{}: 开始运行定时任务", chrono::Local::now().format("%Y年%m月%d日 %H:%M:%S"));
|
||||||
|
loop {
|
||||||
|
scheduler.run_pending();
|
||||||
|
thread::sleep(Duration::from_millis(100));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,143 @@
|
||||||
|
|
||||||
|
use std::{env, time::Duration};
|
||||||
|
|
||||||
|
use chrono::{Local, NaiveDate};
|
||||||
|
use regex::Regex;
|
||||||
|
use scraper::{Html, Selector};
|
||||||
|
use sea_orm::{ActiveModelTrait, ActiveValue::Set, ConnectOptions, Database};
|
||||||
|
|
||||||
|
use crate::model::{sd, pls, plw};
|
||||||
|
|
||||||
|
pub async fn get_data_from_500_com(issue_type: &str) -> Result<(), String> {
|
||||||
|
|
||||||
|
// 数据库连接操作
|
||||||
|
let db_url;
|
||||||
|
if env::var("RUST_ENV").unwrap_or_else(|_|"dev".to_string()) == "dev" {
|
||||||
|
db_url = "mysql://root:Chenweijia113!@localhost/lottery";
|
||||||
|
} else {
|
||||||
|
db_url = "mysql://root:Chenweijia113!@mysql/lottery";
|
||||||
|
}
|
||||||
|
let mut opt = ConnectOptions::new(db_url);
|
||||||
|
opt.max_connections(100)
|
||||||
|
.min_connections(5)
|
||||||
|
.connect_timeout(Duration::from_secs(8))
|
||||||
|
.acquire_timeout(Duration::from_secs(8))
|
||||||
|
.idle_timeout(Duration::from_secs(8))
|
||||||
|
.max_lifetime(Duration::from_secs(8))
|
||||||
|
.sqlx_logging(true)
|
||||||
|
.sqlx_logging_level(log::LevelFilter::Info)
|
||||||
|
.set_schema_search_path("my_schema");
|
||||||
|
let db = Database::connect(opt).await.expect("Failed to connect to database");
|
||||||
|
|
||||||
|
let url = format!("https://kaijiang.500.com/{}.shtml", issue_type);
|
||||||
|
|
||||||
|
let client = reqwest::Client::new();
|
||||||
|
let res = client.get(&url).send().await.expect("Failed to send request");
|
||||||
|
println!("url: {}, status: {}", url, res.status());
|
||||||
|
|
||||||
|
if res.status().is_success() {
|
||||||
|
let document = res.text_with_charset("gb2312").await.unwrap();
|
||||||
|
let html = Html::parse_document(&document);
|
||||||
|
|
||||||
|
let issue_selector = Selector::parse(r#"td.td_title01 > span.span_left > a > font.cfont2 > strong"#).expect("selector error");
|
||||||
|
let issue_iter = html.select(&issue_selector).map(|x| {x.inner_html()});
|
||||||
|
let issue: String = issue_iter.collect();
|
||||||
|
|
||||||
|
let date_selector = Selector::parse(r#"td.td_title01 > span.span_right"#).expect("selector error");
|
||||||
|
let date_str = html.select(&date_selector).map(|x| {x.inner_html()}).collect::<String>();
|
||||||
|
let re = Regex::new(r"开奖日期:(.*) 兑奖截止日期:(.*)").unwrap();
|
||||||
|
let Some(draw_date) = re.captures(date_str.as_str()) else { return Err(String::from("日期解析错误")) };
|
||||||
|
let draw_date = draw_date.get(1).unwrap().as_str();
|
||||||
|
let date = NaiveDate::parse_from_str(draw_date, "%Y年%m月%d日").expect("Failed to parse date");
|
||||||
|
|
||||||
|
let ball_selector = Selector::parse(r#"li.ball_orange"#).expect("selector error");
|
||||||
|
let ball_iter = html.select(&ball_selector).map(|x| {x.inner_html()});
|
||||||
|
let ball: String = ball_iter.collect();
|
||||||
|
|
||||||
|
let numbers : Vec<i32> = ball.chars().map(|c| c.to_digit(10).unwrap() as i32).collect();
|
||||||
|
let mut seen = std::collections::HashSet::new();
|
||||||
|
let unique_numbers: Vec<_> = numbers.clone().into_iter().filter(|&x| seen.insert(x)).collect(); // 只有当元素被成功插入时(即它是唯一的),才包含它
|
||||||
|
// let max = numbers.iter().max().unwrap_or(&0);
|
||||||
|
// let min = numbers.iter().min().unwrap_or(&0);
|
||||||
|
let sum = numbers.iter().sum::<i32>();
|
||||||
|
let code_small = numbers.iter().filter(|&&x| x < 5).count() as i32;
|
||||||
|
let code_big = numbers.iter().filter(|&&x| x >= 5).count() as i32;
|
||||||
|
let code_single = numbers.iter().filter(|&&x| x % 2 == 1).count() as i32;
|
||||||
|
let code_double = numbers.iter().filter(|&&x| x % 2 == 0).count() as i32;
|
||||||
|
|
||||||
|
if issue_type == "sd" {
|
||||||
|
let group_type = if unique_numbers.len() == 3 { 6 } else if unique_numbers.len() == 2{ 3 } else {1};
|
||||||
|
let new_model = sd::ActiveModel {
|
||||||
|
draw_issue: Set(Some(issue)),
|
||||||
|
draw_code: Set(Some(ball)),
|
||||||
|
draw_date: Set(Some(date)),
|
||||||
|
created_at: Set(Some(Local::now().naive_local())),
|
||||||
|
sum_num: Set(Some(sum)),
|
||||||
|
code_small: Set(Some(code_small)),
|
||||||
|
code_big: Set(Some(code_big)),
|
||||||
|
code_single: Set(Some(code_single)),
|
||||||
|
code_double: Set(Some(code_double)),
|
||||||
|
hundred: Set(Some(numbers[0])),
|
||||||
|
ten: Set(Some(numbers[1])),
|
||||||
|
one: Set(Some(numbers[2])),
|
||||||
|
sum_hundred_one: Set(Some(numbers[0] + numbers[2])),
|
||||||
|
sum_hundred_ten: Set(Some(numbers[0] + numbers[1])),
|
||||||
|
sum_ten_one: Set(Some(numbers[1] + numbers[2])),
|
||||||
|
group_type: Set(Some(group_type)),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
new_model.insert(&db).await.expect("insert error");
|
||||||
|
} else if issue_type == "pls" {
|
||||||
|
let group_type = if unique_numbers.len() == 3 { 6 } else if unique_numbers.len() == 2{ 3 } else {1};
|
||||||
|
let new_model = pls::ActiveModel {
|
||||||
|
draw_issue: Set(Some(issue)),
|
||||||
|
draw_code: Set(Some(ball)),
|
||||||
|
draw_date: Set(Some(date)),
|
||||||
|
created_at: Set(Some(Local::now().naive_local())),
|
||||||
|
sum_num: Set(Some(sum)),
|
||||||
|
code_small: Set(Some(code_small)),
|
||||||
|
code_big: Set(Some(code_big)),
|
||||||
|
code_single: Set(Some(code_single)),
|
||||||
|
code_double: Set(Some(code_double)),
|
||||||
|
hundred: Set(Some(numbers[0])),
|
||||||
|
ten: Set(Some(numbers[1])),
|
||||||
|
one: Set(Some(numbers[2])),
|
||||||
|
sum_hundred_one: Set(Some(numbers[0] + numbers[2])),
|
||||||
|
sum_hundred_ten: Set(Some(numbers[0] + numbers[1])),
|
||||||
|
sum_ten_one: Set(Some(numbers[1] + numbers[2])),
|
||||||
|
group_type: Set(Some(group_type)),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
new_model.insert(&db).await.expect("insert error");
|
||||||
|
} else if issue_type == "plw" {
|
||||||
|
let new_model = plw::ActiveModel {
|
||||||
|
draw_issue: Set(Some(issue)),
|
||||||
|
draw_code: Set(Some(ball)),
|
||||||
|
draw_date: Set(Some(date)),
|
||||||
|
created_at: Set(Some(Local::now().naive_local())),
|
||||||
|
sum_num: Set(Some(sum)),
|
||||||
|
code_small: Set(Some(code_small)),
|
||||||
|
code_big: Set(Some(code_big)),
|
||||||
|
code_single: Set(Some(code_single)),
|
||||||
|
code_double: Set(Some(code_double)),
|
||||||
|
ten_thousand: Set(Some(numbers[0])),
|
||||||
|
thousand: Set(Some(numbers[1])),
|
||||||
|
hundred: Set(Some(numbers[2])),
|
||||||
|
ten: Set(Some(numbers[3])),
|
||||||
|
one: Set(Some(numbers[4])),
|
||||||
|
sum_hundred_one: Set(Some(numbers[2] + numbers[4])),
|
||||||
|
sum_hundred_ten: Set(Some(numbers[2] + numbers[3])),
|
||||||
|
sum_ten_one: Set(Some(numbers[3] + numbers[4])),
|
||||||
|
sum_first_3: Set(Some(numbers[0] + numbers[1] + numbers[2])),
|
||||||
|
sum_next_3: Set(Some(numbers[2] + numbers[3] + numbers[4])),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
new_model.insert(&db).await.expect("insert error");
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(String::from("获取数据失败"))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
pub mod get_data;
|
||||||
|
pub mod model;
|
||||||
|
|
||||||
|
pub use get_data::get_data_from_500_com;
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
|
||||||
|
|
||||||
|
pub mod prelude;
|
||||||
|
|
||||||
|
pub mod sd;
|
||||||
|
pub mod pls;
|
||||||
|
pub mod plw;
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
|
||||||
|
|
||||||
|
use sea_orm::entity::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
|
||||||
|
#[sea_orm(table_name = "pls")]
|
||||||
|
pub struct Model {
|
||||||
|
#[sea_orm(primary_key)]
|
||||||
|
pub id: i32,
|
||||||
|
pub draw_issue: Option<String>,
|
||||||
|
pub draw_date: Option<Date>,
|
||||||
|
pub draw_code: Option<String>,
|
||||||
|
pub hundred: Option<i32>,
|
||||||
|
pub ten: Option<i32>,
|
||||||
|
pub one: Option<i32>,
|
||||||
|
pub group_type: Option<i32>,
|
||||||
|
pub group_3_span: Option<i32>,
|
||||||
|
pub code_big: Option<i32>,
|
||||||
|
pub code_small: Option<i32>,
|
||||||
|
pub code_single: Option<i32>,
|
||||||
|
pub code_double: Option<i32>,
|
||||||
|
pub sum_num: Option<i32>,
|
||||||
|
pub sum_num_span: Option<i32>,
|
||||||
|
pub sum_ten_one: Option<i32>,
|
||||||
|
pub sum_hundred_ten: Option<i32>,
|
||||||
|
pub sum_hundred_one: Option<i32>,
|
||||||
|
pub sum_single_double: Option<i32>,
|
||||||
|
pub sum_big_small: Option<i32>,
|
||||||
|
pub created_at: Option<DateTime>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||||
|
pub enum Relation {}
|
||||||
|
|
||||||
|
impl ActiveModelBehavior for ActiveModel {}
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
|
||||||
|
|
||||||
|
use sea_orm::entity::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
|
||||||
|
#[sea_orm(table_name = "plw")]
|
||||||
|
pub struct Model {
|
||||||
|
#[sea_orm(primary_key)]
|
||||||
|
pub id: i32,
|
||||||
|
pub draw_issue: Option<String>,
|
||||||
|
pub draw_date: Option<Date>,
|
||||||
|
pub draw_code: Option<String>,
|
||||||
|
pub ten_thousand: Option<i32>,
|
||||||
|
pub thousand: Option<i32>,
|
||||||
|
pub hundred: Option<i32>,
|
||||||
|
pub ten: Option<i32>,
|
||||||
|
pub one: Option<i32>,
|
||||||
|
pub code_big: Option<i32>,
|
||||||
|
pub code_small: Option<i32>,
|
||||||
|
pub code_single: Option<i32>,
|
||||||
|
pub code_double: Option<i32>,
|
||||||
|
pub sum_num: Option<i32>,
|
||||||
|
pub sum_ten_one: Option<i32>,
|
||||||
|
pub sum_hundred_ten: Option<i32>,
|
||||||
|
pub sum_hundred_one: Option<i32>,
|
||||||
|
pub sum_first_3: Option<i32>,
|
||||||
|
pub sum_next_3: Option<i32>,
|
||||||
|
pub created_at: Option<DateTime>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||||
|
pub enum Relation {}
|
||||||
|
|
||||||
|
impl ActiveModelBehavior for ActiveModel {}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
|
||||||
|
pub use super::sd::Entity as Sd;
|
||||||
|
pub use super::pls::Entity as Pls;
|
||||||
|
pub use super::plw::Entity as Plw;
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
|
||||||
|
|
||||||
|
use sea_orm::entity::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
|
||||||
|
#[sea_orm(table_name = "sd")]
|
||||||
|
pub struct Model {
|
||||||
|
#[sea_orm(primary_key)]
|
||||||
|
pub id: i32,
|
||||||
|
pub draw_issue: Option<String>,
|
||||||
|
pub draw_date: Option<Date>,
|
||||||
|
pub draw_code: Option<String>,
|
||||||
|
pub hundred: Option<i32>,
|
||||||
|
pub ten: Option<i32>,
|
||||||
|
pub one: Option<i32>,
|
||||||
|
pub group_type: Option<i32>,
|
||||||
|
pub group_3_span: Option<i32>,
|
||||||
|
pub code_big: Option<i32>,
|
||||||
|
pub code_small: Option<i32>,
|
||||||
|
pub code_single: Option<i32>,
|
||||||
|
pub code_double: Option<i32>,
|
||||||
|
pub sum_num: Option<i32>,
|
||||||
|
pub sum_num_span: Option<i32>,
|
||||||
|
pub sum_ten_one: Option<i32>,
|
||||||
|
pub sum_hundred_ten: Option<i32>,
|
||||||
|
pub sum_hundred_one: Option<i32>,
|
||||||
|
pub sum_single_double: Option<i32>,
|
||||||
|
pub sum_big_small: Option<i32>,
|
||||||
|
pub created_at: Option<DateTime>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||||
|
pub enum Relation {}
|
||||||
|
|
||||||
|
impl ActiveModelBehavior for ActiveModel {}
|
||||||
Loading…
Reference in New Issue