refactor teaching domain naming to course/course_section/course_group
Add diesel migrations and synchronize backend/frontend model fields plus jsonb key migration to support generic teaching subjects beyond piano-specific naming. Made-with: Cursor
This commit is contained in:
@@ -0,0 +1,693 @@
|
||||
use anyhow::anyhow;
|
||||
use chrono::NaiveDateTime;
|
||||
use diesel::{
|
||||
insert_into, sql_query, update, ExpressionMethods, OptionalExtension, PgConnection, QueryDsl,
|
||||
RunQueryDsl,
|
||||
};
|
||||
use log::debug;
|
||||
use string_builder::Builder;
|
||||
|
||||
use crate::schema::*;
|
||||
use htycommons::common::{date_to_string, HtyErr, HtyErrCode};
|
||||
use htycommons::db::SingleVal;
|
||||
use htycommons::models::*;
|
||||
use htyuc_models::models::ReqHtyUserGroup;
|
||||
use htyws_models::models::{CourseSectionJsonData, ReqDakaWithCourseSectionIds2, ReqJihua};
|
||||
|
||||
#[derive(
|
||||
AsChangeset,
|
||||
Identifiable,
|
||||
PartialEq,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
Queryable,
|
||||
Debug,
|
||||
Insertable,
|
||||
Clone,
|
||||
QueryableByName,
|
||||
)]
|
||||
#[diesel(table_name = kecheng)]
|
||||
pub struct Kecheng {
|
||||
pub id: String,
|
||||
pub kecheng_name: String,
|
||||
pub kecheng_status: String,
|
||||
pub kecheng_desc: Option<String>,
|
||||
pub start_from: NaiveDateTime,
|
||||
pub end_by: NaiveDateTime,
|
||||
pub duration: Option<i32>,
|
||||
pub root_id: String, // 是对应root kecheng
|
||||
pub kecheng_type: Option<String>,
|
||||
pub parent_id: String,
|
||||
pub created_by: Option<String>,
|
||||
pub created_at: Option<NaiveDateTime>,
|
||||
pub students: Option<SingleVal<ReqHtyUserGroup>>,
|
||||
pub teachers: Option<SingleVal<ReqHtyUserGroup>>,
|
||||
pub jihuas: Option<MultiVals<ReqJihua>>,
|
||||
pub dakas: Option<MultiVals<ReqDakaWithCourseSectionIds2>>,
|
||||
pub is_delete: Option<bool>,
|
||||
pub is_repeat: Option<bool>,
|
||||
pub course_sections: Option<MultiVals<CourseSectionJsonData>>,
|
||||
pub is_notified: Option<bool>,
|
||||
}
|
||||
|
||||
// https://weinan.io/2024/02/23/rust-diesel.html
|
||||
#[derive(QueryableByName, Default, Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
pub struct ReqKechengWithRepeat {
|
||||
// kecheng
|
||||
#[diesel(sql_type = diesel::sql_types::Varchar)]
|
||||
pub kc_id: String,
|
||||
#[diesel(sql_type = diesel::sql_types::Varchar)]
|
||||
pub kc_kecheng_name: String,
|
||||
#[diesel(sql_type = diesel::sql_types::Varchar)]
|
||||
pub kc_kecheng_status: String,
|
||||
#[diesel(sql_type = diesel::sql_types::Nullable < diesel::sql_types::Varchar >)]
|
||||
pub kc_kecheng_desc: Option<String>,
|
||||
#[diesel(sql_type = diesel::sql_types::Timestamp)]
|
||||
pub kc_start_from: NaiveDateTime,
|
||||
#[diesel(sql_type = diesel::sql_types::Timestamp)]
|
||||
pub kc_end_by: NaiveDateTime,
|
||||
#[diesel(sql_type = diesel::sql_types::Nullable < diesel::sql_types::Int4 >)]
|
||||
pub kc_duration: Option<i32>,
|
||||
#[diesel(sql_type = diesel::sql_types::Varchar)]
|
||||
pub kc_root_id: String,
|
||||
#[diesel(sql_type = diesel::sql_types::Nullable < diesel::sql_types::Varchar >)]
|
||||
pub kc_kecheng_type: Option<String>,
|
||||
#[diesel(sql_type = diesel::sql_types::Varchar)]
|
||||
pub kc_parent_id: String,
|
||||
#[diesel(sql_type = diesel::sql_types::Nullable < diesel::sql_types::Varchar >)]
|
||||
pub kc_created_by: Option<String>,
|
||||
#[diesel(sql_type = diesel::sql_types::Nullable < diesel::sql_types::Timestamp >)]
|
||||
pub kc_created_at: Option<NaiveDateTime>,
|
||||
#[diesel(sql_type = diesel::sql_types::Nullable < diesel::sql_types::Jsonb >)]
|
||||
pub kc_students: Option<SingleVal<ReqHtyUserGroup>>,
|
||||
#[diesel(sql_type = diesel::sql_types::Nullable < diesel::sql_types::Jsonb >)]
|
||||
pub kc_teachers: Option<SingleVal<ReqHtyUserGroup>>,
|
||||
#[diesel(sql_type = diesel::sql_types::Nullable < diesel::sql_types::Jsonb >)]
|
||||
pub kc_jihuas: Option<MultiVals<ReqJihua>>,
|
||||
#[diesel(sql_type = diesel::sql_types::Nullable < diesel::sql_types::Jsonb >)]
|
||||
pub kc_dakas: Option<MultiVals<ReqDakaWithCourseSectionIds2>>,
|
||||
#[diesel(sql_type = diesel::sql_types::Nullable < diesel::sql_types::Bool >)]
|
||||
pub kc_is_delete: Option<bool>,
|
||||
#[diesel(sql_type = diesel::sql_types::Nullable < diesel::sql_types::Bool >)]
|
||||
pub kc_is_repeat: Option<bool>,
|
||||
#[diesel(sql_type = diesel::sql_types::Nullable < diesel::sql_types::Jsonb >)]
|
||||
pub kc_course_sections: Option<MultiVals<CourseSectionJsonData>>,
|
||||
// // kc_repeat
|
||||
#[diesel(sql_type = diesel::sql_types::Varchar)]
|
||||
pub re_id: String,
|
||||
#[diesel(sql_type = diesel::sql_types::Nullable < diesel::sql_types::Varchar >)]
|
||||
pub re_kecheng_id: Option<String>,
|
||||
#[diesel(sql_type = diesel::sql_types::Nullable < diesel::sql_types::Timestamp >)]
|
||||
pub re_start_from: Option<NaiveDateTime>,
|
||||
#[diesel(sql_type = diesel::sql_types::Nullable < diesel::sql_types::Timestamp >)]
|
||||
pub re_end_by: Option<NaiveDateTime>,
|
||||
#[diesel(sql_type = diesel::sql_types::Nullable < diesel::sql_types::Timestamp >)]
|
||||
pub re_repeat_start: Option<NaiveDateTime>,
|
||||
#[diesel(sql_type = diesel::sql_types::Nullable < diesel::sql_types::Int4 >)]
|
||||
pub re_repeat_cycle_days: Option<i32>,
|
||||
#[diesel(sql_type = diesel::sql_types::Nullable < diesel::sql_types::Timestamp >)]
|
||||
pub re_repeat_end: Option<NaiveDateTime>,
|
||||
#[diesel(sql_type = diesel::sql_types::Nullable < diesel::sql_types::Varchar >)]
|
||||
pub re_repeat_status: Option<String>,
|
||||
#[diesel(sql_type = diesel::sql_types::Nullable < diesel::sql_types::Timestamp >)]
|
||||
pub re_latest_kc_created_at: Option<NaiveDateTime>,
|
||||
}
|
||||
|
||||
impl ReqKechengWithRepeat {
|
||||
pub fn from_kecheng_and_repeat(
|
||||
in_kecheng: &Option<Kecheng>,
|
||||
in_kecheng_repeat: &Option<KechengRepeat>,
|
||||
) -> ReqKechengWithRepeat {
|
||||
let the_kecheng = if let Some(k) = in_kecheng {
|
||||
k.clone()
|
||||
} else {
|
||||
Kecheng {
|
||||
id: "".to_string(),
|
||||
kecheng_name: "".to_string(),
|
||||
kecheng_status: "".to_string(),
|
||||
kecheng_desc: None,
|
||||
start_from: Default::default(),
|
||||
end_by: Default::default(),
|
||||
duration: None,
|
||||
root_id: "".to_string(),
|
||||
kecheng_type: None,
|
||||
parent_id: "".to_string(),
|
||||
created_by: None,
|
||||
created_at: None,
|
||||
students: None,
|
||||
teachers: None,
|
||||
jihuas: None,
|
||||
dakas: None,
|
||||
is_delete: None,
|
||||
is_repeat: None,
|
||||
course_sections: None,
|
||||
is_notified: None,
|
||||
}
|
||||
};
|
||||
|
||||
let the_kecheng_repeat = if let Some(kr) = in_kecheng_repeat {
|
||||
kr.clone()
|
||||
} else {
|
||||
KechengRepeat {
|
||||
id: "".to_string(),
|
||||
kecheng_id: None,
|
||||
start_from: None,
|
||||
end_by: None,
|
||||
repeat_start: None,
|
||||
repeat_cycle_days: None,
|
||||
repeat_end: None,
|
||||
repeat_status: None,
|
||||
latest_kc_created_at: None,
|
||||
}
|
||||
};
|
||||
|
||||
ReqKechengWithRepeat {
|
||||
kc_id: the_kecheng.id.clone(),
|
||||
kc_kecheng_name: the_kecheng.kecheng_name.clone(),
|
||||
kc_kecheng_status: the_kecheng.kecheng_status.clone(),
|
||||
kc_kecheng_desc: the_kecheng.kecheng_desc.clone(),
|
||||
kc_start_from: the_kecheng.start_from.clone(),
|
||||
kc_end_by: the_kecheng.end_by.clone(),
|
||||
kc_duration: the_kecheng.duration.clone(),
|
||||
kc_root_id: the_kecheng.root_id.clone(),
|
||||
kc_kecheng_type: the_kecheng.kecheng_type.clone(),
|
||||
kc_parent_id: the_kecheng.parent_id.clone(),
|
||||
kc_created_by: the_kecheng.created_by.clone(),
|
||||
kc_created_at: the_kecheng.created_at.clone(),
|
||||
kc_students: the_kecheng.students.clone(),
|
||||
kc_teachers: the_kecheng.teachers.clone(),
|
||||
kc_jihuas: the_kecheng.jihuas.clone(),
|
||||
kc_dakas: the_kecheng.dakas.clone(),
|
||||
kc_is_delete: the_kecheng.is_delete.clone(),
|
||||
kc_is_repeat: the_kecheng.is_repeat.clone(),
|
||||
kc_course_sections: the_kecheng.course_sections.clone(),
|
||||
re_id: the_kecheng.id.clone(),
|
||||
re_kecheng_id: the_kecheng_repeat.kecheng_id.clone(),
|
||||
re_start_from: the_kecheng_repeat.start_from.clone(),
|
||||
re_end_by: the_kecheng_repeat.end_by.clone(),
|
||||
re_repeat_start: the_kecheng_repeat.repeat_start.clone(),
|
||||
re_repeat_cycle_days: the_kecheng_repeat.repeat_cycle_days.clone(),
|
||||
re_repeat_end: the_kecheng_repeat.repeat_end.clone(),
|
||||
re_repeat_status: the_kecheng_repeat.repeat_status.clone(),
|
||||
re_latest_kc_created_at: the_kecheng_repeat.latest_kc_created_at.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Kecheng {
|
||||
pub fn to_req(&self) -> ReqKecheng {
|
||||
let req_res = ReqKecheng {
|
||||
created_at: self.created_at.clone(),
|
||||
created_by: self.created_by.clone(),
|
||||
dakas: self.dakas.clone(),
|
||||
duration: self.duration.clone(),
|
||||
end_by: Some(self.end_by.clone()),
|
||||
id: Some(self.id.clone()),
|
||||
is_delete: self.is_delete.clone(),
|
||||
is_repeat: self.is_repeat.clone(),
|
||||
jihuas: self.jihuas.clone(),
|
||||
kecheng_desc: self.kecheng_desc.clone(),
|
||||
kecheng_name: Some(self.kecheng_name.clone()),
|
||||
kecheng_repeat: None, // todo: fill in?
|
||||
kecheng_status: Some(self.kecheng_status.clone()),
|
||||
kecheng_type: self.kecheng_type.clone(),
|
||||
parent_id: Some(self.parent_id.clone()),
|
||||
root_id: Some(self.root_id.clone()),
|
||||
start_from: Some(self.start_from.clone()),
|
||||
students: self.students.clone(),
|
||||
teachers: self.teachers.clone(),
|
||||
course_sections: self.course_sections.clone(),
|
||||
is_notified: self.is_notified.clone(),
|
||||
};
|
||||
req_res
|
||||
}
|
||||
|
||||
pub fn find_by_id(id_kecheng: &String, conn: &mut PgConnection) -> anyhow::Result<Kecheng> {
|
||||
// use crate::schema::kecheng::dsl::*;
|
||||
match kecheng::table
|
||||
.filter(kecheng::id.eq(id_kecheng))
|
||||
.first::<Kecheng>(conn)
|
||||
{
|
||||
Ok(q) => Ok(q),
|
||||
Err(e) => Err(anyhow!(HtyErr {
|
||||
code: HtyErrCode::DbErr,
|
||||
reason: Some(e.to_string()),
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create(in_kecheng: &Kecheng, conn: &mut PgConnection) -> anyhow::Result<Kecheng> {
|
||||
use crate::schema::kecheng::dsl::*;
|
||||
match insert_into(kecheng).values(in_kecheng).get_result(conn) {
|
||||
Ok(res) => Ok(res),
|
||||
Err(e) => Err(anyhow!(HtyErr {
|
||||
code: HtyErrCode::DbErr,
|
||||
reason: Some(e.to_string()),
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(in_kecheng: &Kecheng, conn: &mut PgConnection) -> anyhow::Result<Kecheng> {
|
||||
let result = update(kecheng::table)
|
||||
.filter(kecheng::id.eq(in_kecheng.clone().id))
|
||||
.set(in_kecheng)
|
||||
.get_result(conn)
|
||||
.map_err(|e| {
|
||||
anyhow!(HtyErr {
|
||||
code: HtyErrCode::DbErr,
|
||||
reason: Some(e.to_string()),
|
||||
})
|
||||
});
|
||||
result
|
||||
}
|
||||
|
||||
pub fn find_kecheng_by_user_id(
|
||||
id_user: &String,
|
||||
date_start: &Option<NaiveDateTime>,
|
||||
date_end: &Option<NaiveDateTime>,
|
||||
conn: &mut PgConnection,
|
||||
) -> anyhow::Result<Vec<Kecheng>> {
|
||||
use diesel::prelude::*;
|
||||
|
||||
let q;
|
||||
if date_start.is_some() && date_end.is_some() {
|
||||
q = format!("SELECT * FROM kecheng WHERE (jsonb_path_exists(students, '$.val.users.vals[*].user_id ? (@ == \"{}\")') OR jsonb_path_exists(teachers, '$.val.users.vals[*].user_id ? (@ == \"{}\")')) AND (start_from <= '{}' AND end_by >= '{}' );", id_user.clone(), id_user.clone(), &date_end.as_ref().ok_or_else(|| anyhow!("date_end is required"))?, &date_start.as_ref().ok_or_else(|| anyhow!("date_start is required"))?);
|
||||
} else {
|
||||
q = format!("SELECT * FROM kecheng WHERE (jsonb_path_exists(students, '$.val.users.vals[*].user_id ? (@ == \"{}\")') OR jsonb_path_exists(teachers, '$.val.users.vals[*].user_id ? (@ == \"{}\")'));", id_user.clone(), id_user.clone());
|
||||
}
|
||||
|
||||
debug!("find_kecheng_by_user_id -> q: {:?}", q);
|
||||
let res = sql_query(q.clone()).load(conn)?;
|
||||
debug!("find_kecheng_by_user_id -> res: {:?}", res);
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
pub fn find_kecheng_by_daka_id(
|
||||
id_daka: &String,
|
||||
conn: &mut PgConnection,
|
||||
) -> anyhow::Result<Kecheng> {
|
||||
use diesel::prelude::*;
|
||||
|
||||
let q = format!("SELECT * FROM kecheng WHERE (jsonb_path_exists(dakas, '$.vals[*].id ? (@ == \"{}\")'));", id_daka.clone());
|
||||
|
||||
let query_res: Vec<Kecheng> = sql_query(q.clone()).load(conn)?;
|
||||
let res = query_res
|
||||
.first()
|
||||
.ok_or_else(|| anyhow!("No kecheng found"))?
|
||||
.clone();
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
pub fn find_kechengs_by_daka_ids(
|
||||
id_dakas: &Vec<String>,
|
||||
conn: &mut PgConnection,
|
||||
) -> anyhow::Result<Vec<Kecheng>> {
|
||||
use diesel::prelude::*;
|
||||
|
||||
let mut query_seg_builder = Builder::default();
|
||||
|
||||
for id_daka in id_dakas {
|
||||
query_seg_builder.append(format!("@ ==\"{}\" || ", id_daka))
|
||||
}
|
||||
// placeholder
|
||||
query_seg_builder.append("@ == false");
|
||||
|
||||
let q = format!(
|
||||
"SELECT * FROM kecheng WHERE (jsonb_path_exists(dakas, '$.vals[*].id ? ({})'));",
|
||||
query_seg_builder.string()?
|
||||
);
|
||||
debug!("find_kechengs_by_daka_ids -> q: {:?}", q);
|
||||
|
||||
let query_res = sql_query(q.clone()).load(conn)?;
|
||||
Ok(query_res)
|
||||
}
|
||||
|
||||
// 不可能找出来已经实体化的课程,只会找出来虚拟课程
|
||||
// 因为查询`is_repeat=TRUE`的课程,所以实际上只会查出来重复课程的 root kecheng
|
||||
// 而root kecheng不会显示在日历上,所以不会是虚拟课程或者实体化的重复课程。
|
||||
pub fn find_all_repeatable_by_date_range(
|
||||
start_from: &NaiveDateTime,
|
||||
end_by: &NaiveDateTime,
|
||||
conn: &mut PgConnection,
|
||||
) -> anyhow::Result<Option<Vec<ReqKechengWithRepeat>>> {
|
||||
let q = format!("SELECT kecheng.id AS kc_id, kecheng.kecheng_name AS kc_kecheng_name, kecheng.kecheng_status AS kc_kecheng_status, kecheng.kecheng_desc AS kc_kecheng_desc, kecheng.start_from AS kc_start_from, kecheng.end_by AS kc_end_by, kecheng.duration AS kc_duration, kecheng.root_id AS kc_root_id, kecheng.kecheng_type AS kc_kecheng_type, kecheng.parent_id AS kc_parent_id, kecheng.created_by AS kc_created_by, kecheng.created_at AS kc_created_at, kecheng.students AS kc_students, kecheng.teachers AS kc_teachers, kecheng.jihuas AS kc_jihuas, kecheng.dakas AS kc_dakas, kecheng.is_delete AS kc_is_delete, kecheng.is_repeat AS kc_is_repeat, kecheng.course_sections AS kc_course_sections, kecheng_repeat.id AS re_id, kecheng_repeat.kecheng_id AS re_kecheng_id, kecheng_repeat.start_from AS re_start_from, kecheng_repeat.end_by AS re_end_by, kecheng_repeat.repeat_start AS re_repeat_start, kecheng_repeat.repeat_cycle_days AS re_repeat_cycle_days, kecheng_repeat.repeat_end AS re_repeat_end, kecheng_repeat.repeat_status AS re_repeat_status, kecheng_repeat.latest_kc_created_at AS re_latest_kc_created_at FROM kecheng JOIN kecheng_repeat ON kecheng.id = kecheng_repeat.kecheng_id WHERE (kecheng.is_repeat IS NOT NULL AND kecheng.is_repeat IS TRUE) AND ((kecheng_repeat.repeat_start <= '{}' AND kecheng_repeat.repeat_end >= '{}') OR (kecheng.start_from <= '{}' AND kecheng.end_by >= '{}'));", date_to_string(end_by), date_to_string(start_from), date_to_string(end_by), date_to_string(start_from)).to_string();
|
||||
|
||||
debug!("find_all_repeatable_by_date_range -> q: {:?}", q);
|
||||
|
||||
let some_kechengs = sql_query(q.clone()).load(conn).optional()?;
|
||||
|
||||
debug!(
|
||||
"find_all_repeatable_by_date_range -> kechengs_with_repeat: {:?}",
|
||||
some_kechengs
|
||||
);
|
||||
|
||||
Ok(some_kechengs)
|
||||
}
|
||||
|
||||
// select kecheng.is_repeat from kecheng join kecheng_repeat on kecheng.id = kecheng_repeat.kecheng_id where kecheng.is_repeat is not null;
|
||||
pub fn find_all_repeatable_by_date_range_and_user_id(
|
||||
id_user: &String,
|
||||
start_from: &NaiveDateTime,
|
||||
end_by: &NaiveDateTime,
|
||||
conn: &mut PgConnection,
|
||||
) -> anyhow::Result<Option<Vec<ReqKechengWithRepeat>>> {
|
||||
let q = format!("SELECT kecheng.id AS kc_id, kecheng.kecheng_name AS kc_kecheng_name, kecheng.kecheng_status AS kc_kecheng_status, kecheng.kecheng_desc AS kc_kecheng_desc, kecheng.start_from AS kc_start_from, kecheng.end_by AS kc_end_by, kecheng.duration AS kc_duration, kecheng.root_id AS kc_root_id, kecheng.kecheng_type AS kc_kecheng_type, kecheng.parent_id AS kc_parent_id, kecheng.created_by AS kc_created_by, kecheng.created_at AS kc_created_at, kecheng.students AS kc_students, kecheng.teachers AS kc_teachers, kecheng.jihuas AS kc_jihuas, kecheng.dakas AS kc_dakas, kecheng.is_delete AS kc_is_delete, kecheng.is_repeat AS kc_is_repeat, kecheng.course_sections AS kc_course_sections, kecheng_repeat.id AS re_id, kecheng_repeat.kecheng_id AS re_kecheng_id, kecheng_repeat.start_from AS re_start_from, kecheng_repeat.end_by AS re_end_by, kecheng_repeat.repeat_start AS re_repeat_start, kecheng_repeat.repeat_cycle_days AS re_repeat_cycle_days, kecheng_repeat.repeat_end AS re_repeat_end, kecheng_repeat.repeat_status AS re_repeat_status, kecheng_repeat.latest_kc_created_at AS re_latest_kc_created_at FROM kecheng JOIN kecheng_repeat ON kecheng.id = kecheng_repeat.kecheng_id WHERE (jsonb_path_exists(kecheng.students, '$.val.users.vals[*].user_id ? (@ == \"{}\")') OR jsonb_path_exists(kecheng.teachers, '$.val.users.vals[*].user_id ? (@ == \"{}\")')) AND (kecheng.is_repeat IS NOT NULL AND kecheng.is_repeat IS TRUE) AND ((kecheng_repeat.repeat_start <= '{}' AND kecheng_repeat.repeat_end >= '{}') OR (kecheng.start_from <= '{}' AND kecheng.end_by >= '{}'));", id_user.clone(), id_user.clone(), date_to_string(end_by), date_to_string(start_from), date_to_string(end_by), date_to_string(start_from)).to_string();
|
||||
|
||||
debug!(
|
||||
"find_all_repeatable_by_date_range_and_user_id -> q: {:?}",
|
||||
q
|
||||
);
|
||||
|
||||
let some_kechengs = sql_query(q.clone()).load(conn).optional()?;
|
||||
|
||||
debug!(
|
||||
"find_all_repeatable_by_date_range_and_user_id -> kechengs_with_repeat: {:?}",
|
||||
some_kechengs
|
||||
);
|
||||
|
||||
Ok(some_kechengs)
|
||||
}
|
||||
|
||||
// is_repeatable=true的课程之可能是root kecheng
|
||||
// root kcheng不可能显示在日历上
|
||||
// 重复课程从第一节课就已经是虚拟课程(在未被实例化以前)
|
||||
pub fn find_all_non_repeatable_by_date_range(
|
||||
start_from: &NaiveDateTime,
|
||||
end_by: &NaiveDateTime,
|
||||
conn: &mut PgConnection,
|
||||
) -> anyhow::Result<Option<Vec<Kecheng>>> {
|
||||
let q = format!("SELECT * FROM kecheng WHERE (is_repeat IS NULL OR is_repeat IS FALSE) AND (start_from <= '{}' AND end_by >= '{}');", date_to_string(end_by), date_to_string(start_from)).to_string();
|
||||
|
||||
debug!("find_all_non_repeatable_by_date_range -> q: {:?}", q);
|
||||
|
||||
let some_kechengs = sql_query(q.clone()).load(conn).optional()?;
|
||||
|
||||
debug!(
|
||||
"find_all_non_repeatable_by_date_range -> kechengs: {:?}",
|
||||
some_kechengs
|
||||
);
|
||||
|
||||
Ok(some_kechengs)
|
||||
}
|
||||
|
||||
pub fn find_all_non_repeatable_by_date_range_and_user_id(
|
||||
id_user: &String,
|
||||
start_from: &NaiveDateTime,
|
||||
end_by: &NaiveDateTime,
|
||||
conn: &mut PgConnection,
|
||||
) -> anyhow::Result<Option<Vec<Kecheng>>> {
|
||||
let q = format!("SELECT * FROM kecheng WHERE (is_repeat IS NULL OR is_repeat IS FALSE) AND (start_from <= '{}' AND end_by >= '{}') AND (jsonb_path_exists(kecheng.students, '$.val.users.vals[*].user_id ? (@ == \"{}\")') OR jsonb_path_exists(kecheng.teachers, '$.val.users.vals[*].user_id ? (@ == \"{}\")'));", date_to_string(end_by), date_to_string(start_from), id_user.clone(), id_user.clone()).to_string();
|
||||
|
||||
debug!(
|
||||
"find_all_non_repeatable_by_date_range_and_user_id -> q: {:?}",
|
||||
q
|
||||
);
|
||||
|
||||
let some_kechengs = sql_query(q.clone()).load(conn).optional()?;
|
||||
|
||||
debug!(
|
||||
"find_all_non_repeatable_by_date_range_and_user_id -> kechengs: {:?}",
|
||||
some_kechengs
|
||||
);
|
||||
|
||||
Ok(some_kechengs)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(
|
||||
AsChangeset,
|
||||
Associations,
|
||||
Identifiable,
|
||||
PartialEq,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
Queryable,
|
||||
Debug,
|
||||
Insertable,
|
||||
Clone,
|
||||
)]
|
||||
#[diesel(table_name = kecheng_repeat)]
|
||||
#[diesel(belongs_to(Kecheng, foreign_key = kecheng_id))]
|
||||
pub struct KechengRepeat {
|
||||
pub id: String,
|
||||
pub kecheng_id: Option<String>,
|
||||
pub start_from: Option<NaiveDateTime>,
|
||||
pub end_by: Option<NaiveDateTime>,
|
||||
pub repeat_start: Option<NaiveDateTime>,
|
||||
pub repeat_cycle_days: Option<i32>,
|
||||
pub repeat_end: Option<NaiveDateTime>,
|
||||
pub repeat_status: Option<String>,
|
||||
pub latest_kc_created_at: Option<NaiveDateTime>,
|
||||
}
|
||||
|
||||
impl KechengRepeat {
|
||||
pub fn update(
|
||||
in_kecheng_repeat: &KechengRepeat,
|
||||
conn: &mut PgConnection,
|
||||
) -> anyhow::Result<KechengRepeat> {
|
||||
let result = update(kecheng_repeat::table)
|
||||
.filter(kecheng_repeat::id.eq(in_kecheng_repeat.clone().id))
|
||||
.set(in_kecheng_repeat)
|
||||
.get_result(conn)
|
||||
.map_err(|e| {
|
||||
anyhow!(HtyErr {
|
||||
code: HtyErrCode::DbErr,
|
||||
reason: Some(e.to_string()),
|
||||
})
|
||||
})?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn to_req(&self) -> ReqKechengRepeat {
|
||||
let req_repeat = ReqKechengRepeat {
|
||||
id: Some(self.id.clone()),
|
||||
kecheng_id: self.kecheng_id.clone(),
|
||||
start_from: self.end_by.clone(),
|
||||
end_by: self.end_by.clone(),
|
||||
repeat_start: self.repeat_start.clone(),
|
||||
repeat_cycle_days: self.repeat_cycle_days.clone(),
|
||||
repeat_end: self.repeat_end.clone(),
|
||||
repeat_status: self.repeat_status.clone(),
|
||||
latest_kc_created_at: self.latest_kc_created_at.clone(),
|
||||
};
|
||||
req_repeat
|
||||
}
|
||||
|
||||
pub fn create(
|
||||
in_kecheng_repeat: &KechengRepeat,
|
||||
conn: &mut PgConnection,
|
||||
) -> anyhow::Result<KechengRepeat> {
|
||||
use crate::schema::kecheng_repeat::dsl::*;
|
||||
match insert_into(kecheng_repeat)
|
||||
.values(in_kecheng_repeat)
|
||||
.get_result(conn)
|
||||
{
|
||||
Ok(res) => Ok(res),
|
||||
Err(e) => Err(anyhow!(HtyErr {
|
||||
code: HtyErrCode::DbErr,
|
||||
reason: Some(e.to_string()),
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn find_by_kecheng_id(
|
||||
id_kecheng: &String,
|
||||
conn: &mut PgConnection,
|
||||
) -> anyhow::Result<Option<KechengRepeat>> {
|
||||
use crate::schema::kecheng_repeat::dsl::*;
|
||||
match kecheng_repeat
|
||||
.filter(kecheng_id.eq(id_kecheng))
|
||||
.first::<KechengRepeat>(conn)
|
||||
.optional()
|
||||
{
|
||||
Ok(q) => Ok(q),
|
||||
Err(e) => Err(anyhow!(HtyErr {
|
||||
code: HtyErrCode::DbErr,
|
||||
reason: Some(e.to_string()),
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn find_by_id(
|
||||
id_kecheng_repeat: &String,
|
||||
conn: &mut PgConnection,
|
||||
) -> anyhow::Result<Option<KechengRepeat>> {
|
||||
match kecheng_repeat::table
|
||||
.filter(kecheng_repeat::id.eq(id_kecheng_repeat))
|
||||
.first::<KechengRepeat>(conn)
|
||||
.optional()
|
||||
{
|
||||
Ok(q) => Ok(q),
|
||||
Err(e) => Err(anyhow!(HtyErr {
|
||||
code: HtyErrCode::DbErr,
|
||||
reason: Some(e.to_string()),
|
||||
})),
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
// #[derive(
|
||||
// AsChangeset,
|
||||
// Associations,
|
||||
// Identifiable,
|
||||
// PartialEq,
|
||||
// Serialize,
|
||||
// Deserialize,
|
||||
// Queryable,
|
||||
// Debug,
|
||||
// Insertable,
|
||||
// Clone,
|
||||
// )]
|
||||
// #[diesel(table_name = kecheng_users)]
|
||||
// #[diesel(belongs_to(Kecheng, foreign_key = kecheng_id))]
|
||||
// pub struct KechengUser {
|
||||
// pub id: String,
|
||||
// pub user_id: String,
|
||||
// pub kecheng_id: String,
|
||||
// pub kecheng_status: String,
|
||||
// pub user_type: String,
|
||||
// }
|
||||
//
|
||||
// impl KechengUser {
|
||||
// pub fn create(in_kecheng_user: &KechengUser, conn: &mut PgConnection) -> anyhow::Result<KechengUser> {
|
||||
// use crate::schema::kecheng_users::dsl::*;
|
||||
// match insert_into(kecheng_users).values(in_kecheng_user).get_result(conn) {
|
||||
// Ok(res) => Ok(res),
|
||||
// Err(e) => Err(anyhow!(HtyErr {
|
||||
// code: HtyErrCode::DbErr,
|
||||
// reason: Some(e.to_string()),
|
||||
// })),
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// pub fn find_linked_kecheng_by_user_id(id_user: &String, conn: &mut PgConnection) -> anyhow::Result<Vec<Kecheng>> {
|
||||
// use crate::schema::kecheng_users::dsl::*;
|
||||
// match kecheng_users
|
||||
// .filter(user_id.eq(id_user))
|
||||
// .inner_join(kecheng::table)
|
||||
// .select(kecheng::all_columns)
|
||||
// .load::<Kecheng>(conn)
|
||||
// {
|
||||
// Ok(res) => Ok(res),
|
||||
// Err(e) => Err(anyhow!(HtyErr {
|
||||
// code: HtyErrCode::DbErr,
|
||||
// reason: Some(e.to_string()),
|
||||
// })),
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct ReqKecheng {
|
||||
pub created_at: Option<NaiveDateTime>,
|
||||
pub created_by: Option<String>,
|
||||
pub dakas: Option<MultiVals<ReqDakaWithCourseSectionIds2>>,
|
||||
pub duration: Option<i32>,
|
||||
pub end_by: Option<NaiveDateTime>,
|
||||
pub id: Option<String>,
|
||||
pub is_delete: Option<bool>,
|
||||
pub is_repeat: Option<bool>,
|
||||
pub jihuas: Option<MultiVals<ReqJihua>>,
|
||||
pub kecheng_desc: Option<String>,
|
||||
pub kecheng_name: Option<String>,
|
||||
pub kecheng_repeat: Option<ReqKechengRepeat>,
|
||||
// 如果包含本数据, 则可以使用
|
||||
pub kecheng_status: Option<String>,
|
||||
pub kecheng_type: Option<String>,
|
||||
pub parent_id: Option<String>,
|
||||
pub root_id: Option<String>,
|
||||
pub start_from: Option<NaiveDateTime>,
|
||||
pub students: Option<SingleVal<ReqHtyUserGroup>>,
|
||||
pub teachers: Option<SingleVal<ReqHtyUserGroup>>,
|
||||
pub course_sections: Option<MultiVals<CourseSectionJsonData>>,
|
||||
pub is_notified: Option<bool>,
|
||||
}
|
||||
|
||||
impl ReqKecheng {
|
||||
pub fn from_kecheng_and_repeat(
|
||||
in_kecheng: &Option<Kecheng>,
|
||||
in_kecheng_repeat: &Option<KechengRepeat>,
|
||||
) -> ReqKecheng {
|
||||
let the_kecheng = if let Some(k) = in_kecheng {
|
||||
k.clone()
|
||||
} else {
|
||||
Kecheng {
|
||||
id: "".to_string(),
|
||||
kecheng_name: "".to_string(),
|
||||
kecheng_status: "".to_string(),
|
||||
kecheng_desc: None,
|
||||
start_from: Default::default(),
|
||||
end_by: Default::default(),
|
||||
duration: None,
|
||||
root_id: "".to_string(),
|
||||
kecheng_type: None,
|
||||
parent_id: "".to_string(),
|
||||
created_by: None,
|
||||
created_at: None,
|
||||
students: None,
|
||||
teachers: None,
|
||||
jihuas: None,
|
||||
dakas: None,
|
||||
is_delete: None,
|
||||
is_repeat: None,
|
||||
course_sections: None,
|
||||
is_notified: None,
|
||||
}
|
||||
};
|
||||
|
||||
debug!("the_kecheng -> {:?}", the_kecheng);
|
||||
|
||||
let some_kecheng_repeat = if let Some(kr) = in_kecheng_repeat {
|
||||
Some(kr.to_req())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
debug!("some_kecheng_repeat -> {:?}", some_kecheng_repeat);
|
||||
|
||||
ReqKecheng {
|
||||
created_at: the_kecheng.created_at,
|
||||
created_by: the_kecheng.created_by,
|
||||
dakas: the_kecheng.dakas,
|
||||
duration: the_kecheng.duration,
|
||||
end_by: Some(the_kecheng.end_by),
|
||||
id: Some(the_kecheng.id),
|
||||
is_delete: the_kecheng.is_delete,
|
||||
is_repeat: the_kecheng.is_repeat,
|
||||
jihuas: the_kecheng.jihuas,
|
||||
kecheng_desc: the_kecheng.kecheng_desc,
|
||||
kecheng_name: Some(the_kecheng.kecheng_name),
|
||||
kecheng_repeat: some_kecheng_repeat,
|
||||
kecheng_status: Some(the_kecheng.kecheng_status),
|
||||
kecheng_type: the_kecheng.kecheng_type,
|
||||
parent_id: Some(the_kecheng.parent_id),
|
||||
root_id: Some(the_kecheng.root_id),
|
||||
start_from: Some(the_kecheng.start_from),
|
||||
students: the_kecheng.students,
|
||||
teachers: the_kecheng.teachers,
|
||||
course_sections: the_kecheng.course_sections,
|
||||
is_notified: the_kecheng.is_notified,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct ReqKechengRepeat {
|
||||
pub id: Option<String>,
|
||||
pub kecheng_id: Option<String>,
|
||||
pub start_from: Option<NaiveDateTime>,
|
||||
pub end_by: Option<NaiveDateTime>,
|
||||
pub repeat_start: Option<NaiveDateTime>,
|
||||
pub repeat_cycle_days: Option<i32>,
|
||||
pub repeat_end: Option<NaiveDateTime>,
|
||||
pub repeat_status: Option<String>,
|
||||
pub latest_kc_created_at: Option<NaiveDateTime>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct ReqKechengUser {
|
||||
pub id: Option<String>,
|
||||
pub user_id: Option<String>,
|
||||
pub kecheng_id: Option<String>,
|
||||
pub kecheng_status: Option<String>,
|
||||
pub user_type: Option<String>,
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// @generated automatically by Diesel CLI.
|
||||
|
||||
diesel::table! {
|
||||
kecheng (id) {
|
||||
id -> Varchar,
|
||||
kecheng_name -> Varchar,
|
||||
kecheng_status -> Varchar,
|
||||
kecheng_desc -> Nullable<Varchar>,
|
||||
start_from -> Timestamp,
|
||||
end_by -> Timestamp,
|
||||
duration -> Nullable<Int4>,
|
||||
root_id -> Varchar,
|
||||
kecheng_type -> Nullable<Varchar>,
|
||||
parent_id -> Varchar,
|
||||
created_by -> Nullable<Varchar>,
|
||||
created_at -> Nullable<Timestamp>,
|
||||
students -> Nullable<Jsonb>,
|
||||
teachers -> Nullable<Jsonb>,
|
||||
jihuas -> Nullable<Jsonb>,
|
||||
dakas -> Nullable<Jsonb>,
|
||||
is_delete -> Nullable<Bool>,
|
||||
is_repeat -> Nullable<Bool>,
|
||||
course_sections -> Nullable<Jsonb>,
|
||||
is_notified -> Nullable<Bool>,
|
||||
}
|
||||
}
|
||||
|
||||
diesel::table! {
|
||||
kecheng_repeat (id) {
|
||||
id -> Varchar,
|
||||
kecheng_id -> Nullable<Varchar>,
|
||||
start_from -> Nullable<Timestamp>,
|
||||
end_by -> Nullable<Timestamp>,
|
||||
repeat_start -> Nullable<Timestamp>,
|
||||
repeat_cycle_days -> Nullable<Int4>,
|
||||
repeat_end -> Nullable<Timestamp>,
|
||||
repeat_status -> Nullable<Varchar>,
|
||||
latest_kc_created_at -> Nullable<Timestamp>,
|
||||
}
|
||||
}
|
||||
|
||||
diesel::joinable!(kecheng_repeat -> kecheng (kecheng_id));
|
||||
|
||||
diesel::allow_tables_to_appear_in_same_query!(kecheng, kecheng_repeat,);
|
||||
Reference in New Issue
Block a user