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:
2026-04-23 17:16:10 +08:00
commit c843fecbce
19 changed files with 17353 additions and 0 deletions
+693
View File
@@ -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>,
}
+44
View File
@@ -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,);