feat(ws-org): add org_id scoping across ws entities

Add org_id fields and migrations for course, section, lianxi, piyue, and jihua/daka data paths, and enforce organization-scoped filtering in ws service queries.

Made-with: Cursor
This commit is contained in:
2026-04-27 23:06:58 +08:00
parent 83f657388c
commit 07ee6e7cc6
13 changed files with 675 additions and 59 deletions
+73 -17
View File
@@ -14,6 +14,7 @@ use htycommons::common::{
HtyErrCode, HtyResponse,
};
use htycommons::db::{exec_read_write_task, extract_conn, fetch_db_conn, DbState};
use htycommons::jwt::jwt_decode_token;
use htycommons::uuid;
use htycommons::web::{
wrap_json_anyhow_err, wrap_json_ok_resp, AuthorizationHeader, HtyHostHeader,
@@ -30,6 +31,12 @@ use std::ops::DerefMut;
use std::sync::Arc;
use tracing::{debug, error};
fn required_current_org_id_from_sudoer_token_str(token_str: &String) -> anyhow::Result<String> {
jwt_decode_token(token_str)?
.current_org_id
.ok_or_else(|| anyhow!("current_org_id is required"))
}
pub async fn create_daka(
root: HtySudoerTokenHeader,
host: HtyHostHeader,
@@ -54,6 +61,9 @@ pub async fn raw_create_daka(
db_pool: Arc<DbState>,
in_req_daka: &ReqDakaWithCourseSectionIds,
) -> anyhow::Result<String> {
let current_org_id = jwt_decode_token(&(*_token).clone())?
.current_org_id
.ok_or_else(|| anyhow!("current_org_id is required"))?;
let req_daka = in_req_daka.clone();
if req_daka.start_date.is_none()
|| req_daka.duration_days.is_none()
@@ -120,6 +130,7 @@ pub async fn raw_create_daka(
students: req_daka.students.clone(),
course_sections: req_daka.course_sections.clone(),
is_yanqi: req_daka.is_yanqi.clone(),
org_id: Some(current_org_id),
};
let mut params = HashMap::new();
@@ -168,6 +179,7 @@ pub fn raw_create_daka_tx(
course_section_id: in_section_id,
meta: Some(meta_data.clone()),
is_delete: false,
org_id: result_daka.org_id.clone(),
};
debug!("START CREATE DAKA QUMU SECTION -> {:?}", &section);
let created_section = DakaCourseSection::create(&section, conn)?;
@@ -220,8 +232,12 @@ pub async fn raw_find_daka_by_id2(
db_pool: Arc<DbState>,
) -> anyhow::Result<ReqDakaWithCourseSections2> {
debug!("raw_find_daka_by_id2 -> {:?}", id);
let daka = Daka::find_by_id(&id, extract_conn(fetch_db_conn(&db_pool)?).deref_mut())?;
let current_org_id = required_current_org_id_from_sudoer_token_str(&root.0)?;
let daka = Daka::find_by_id_in_org(
&id,
&current_org_id,
extract_conn(fetch_db_conn(&db_pool)?).deref_mut(),
)?;
debug!("raw_find_daka_by_id2 -> daka: {:?}", id);
@@ -262,11 +278,17 @@ pub async fn raw_find_daka_by_id2(
let any_relations: Vec<anyhow::Result<ReqDakaCourseSection>> = the_course_sections
.iter()
.map(|course_section| {
let in_jihua_course_section = DakaCourseSection::find_by_daka_id_and_course_section_id(
&daka.id,
&course_section.id,
extract_conn(fetch_db_conn(&db_pool)?).deref_mut(),
)?;
let daka_org_id = daka
.org_id
.as_ref()
.ok_or_else(|| anyhow!("daka.org_id is required"))?;
let in_jihua_course_section =
DakaCourseSection::find_by_daka_id_and_course_section_id_in_org(
&daka.id,
&course_section.id,
daka_org_id,
extract_conn(fetch_db_conn(&db_pool)?).deref_mut(),
)?;
Ok(ReqDakaCourseSection {
id: Some(in_jihua_course_section.id.clone()),
daka_id: None,
@@ -348,7 +370,12 @@ pub async fn raw_find_daka_by_id(
id: &String,
db_pool: Arc<DbState>,
) -> anyhow::Result<ReqDakaWithCourseSections> {
let daka = Daka::find_by_id(&id, extract_conn(fetch_db_conn(&db_pool)?).deref_mut())?;
let current_org_id = required_current_org_id_from_sudoer_token_str(&root.0)?;
let daka = Daka::find_by_id_in_org(
&id,
&current_org_id,
extract_conn(fetch_db_conn(&db_pool)?).deref_mut(),
)?;
let all_lianxis =
daka.find_active_belonging_lianxis(extract_conn(fetch_db_conn(&db_pool)?).deref_mut())?;
@@ -368,11 +395,17 @@ pub async fn raw_find_daka_by_id(
let any_relations: Vec<anyhow::Result<ReqDakaCourseSection>> = the_course_sections
.iter()
.map(|course_section| {
let in_jihua_course_section = DakaCourseSection::find_by_daka_id_and_course_section_id(
&daka.id,
&course_section.id,
extract_conn(fetch_db_conn(&db_pool)?).deref_mut(),
)?;
let daka_org_id = daka
.org_id
.as_ref()
.ok_or_else(|| anyhow!("daka.org_id is required"))?;
let in_jihua_course_section =
DakaCourseSection::find_by_daka_id_and_course_section_id_in_org(
&daka.id,
&course_section.id,
daka_org_id,
extract_conn(fetch_db_conn(&db_pool)?).deref_mut(),
)?;
Ok(ReqDakaCourseSection {
id: Some(in_jihua_course_section.id.clone()),
daka_id: None,
@@ -471,6 +504,9 @@ pub async fn raw_update_daka(
db_pool: Arc<DbState>,
in_req_daka: &ReqDakaWithCourseSectionIds,
) -> anyhow::Result<String> {
let current_org_id = jwt_decode_token(&(*_token).clone())?
.current_org_id
.ok_or_else(|| anyhow!("current_org_id is required"))?;
let the_req_daka = in_req_daka.clone();
let id_daka = the_req_daka
.clone()
@@ -534,6 +570,7 @@ pub async fn raw_update_daka(
db_daka.updated_at = c_req_daka.updated_at.clone();
db_daka.course_sections = c_req_daka.course_sections.clone();
db_daka.is_yanqi = c_req_daka.is_yanqi.clone();
db_daka.org_id = Some(current_org_id.clone());
debug!("raw_update_daka -> update_daka -> {:?}", &db_daka);
@@ -596,9 +633,14 @@ pub fn raw_update_daka_course_section_relations(
// 首先做第一步
// todo: 改为逻辑删除后,这里会重复查出来`已删除`关系,后续优化。
let current_org_id = in_daka
.org_id
.clone()
.ok_or_else(|| anyhow!("daka.org_id is required"))?;
let mut to_delete_relation_ids: Vec<String> = Vec::new();
// 查找这个daka的所有course_section_ids,然后跟传入的做比对,把不存在于传入数据的ids放入`to_delete_relation_ids`,然后逻辑删除.
let existing_daka_course_sections = DakaCourseSection::find_by_daka_id(daka_id, conn)?;
let existing_daka_course_sections =
DakaCourseSection::find_by_daka_id_in_org(daka_id, &current_org_id, conn)?;
debug!(
"raw_update_daka_course_section_relations -> existing_daka_course_sections -> {:?}",
&existing_daka_course_sections
@@ -627,9 +669,10 @@ pub fn raw_update_daka_course_section_relations(
// 然后做第二步
for in_course_section_id in in_course_section_ids.clone() {
match DakaCourseSection::find_by_daka_id_and_course_section_id(
match DakaCourseSection::find_by_daka_id_and_course_section_id_in_org(
&in_daka.id,
&in_course_section_id,
&current_org_id,
conn,
) {
Ok(rel) => {
@@ -657,6 +700,7 @@ pub fn raw_update_daka_course_section_relations(
course_section_id: in_course_section_id.clone(),
meta: None,
is_delete: false,
org_id: in_daka.org_id.clone(),
};
debug!("raw_update_daka_course_section_relations -> ADDING THIS RELATION -> {:?}", & to_add_rel);
let _ = DakaCourseSection::create(&to_add_rel, conn)?;
@@ -674,12 +718,12 @@ pub fn raw_update_daka_course_section_relations(
}
pub async fn delete_daka_by_id(
_root: HtySudoerTokenHeader,
root: HtySudoerTokenHeader,
Path(id_delete): Path<String>,
State(db_pool): State<Arc<DbState>>,
) -> Json<HtyResponse<()>> {
debug!("delete_daka_by_id -> start here");
match raw_delete_daka_by_id(&id_delete, db_pool).await {
match raw_delete_daka_by_id(&root, &id_delete, db_pool).await {
Ok(ok) => wrap_json_ok_resp(ok),
Err(e) => {
error!("delete_daka_by_id -> failed to delete daka, e: {}", e);
@@ -689,9 +733,16 @@ pub async fn delete_daka_by_id(
}
pub async fn raw_delete_daka_by_id(
root: &HtySudoerTokenHeader,
id_delete: &String,
db_pool: Arc<DbState>,
) -> anyhow::Result<()> {
let current_org_id = required_current_org_id_from_sudoer_token_str(&root.0)?;
let _ = Daka::find_by_id_in_org(
id_delete,
&current_org_id,
extract_conn(fetch_db_conn(&db_pool)?).deref_mut(),
)?;
// 逻辑删除 daka
let _ = Daka::logic_delete_by_id(
id_delete,
@@ -778,6 +829,7 @@ pub async fn raw_find_dakas_with_sections_by_user_id(
) -> anyhow::Result<(Vec<ReqDakaWithCourseSections>, i64, i64)> {
debug!("raw_find_dakas_with_sections_by_user_id -> START");
debug!("raw_find_dakas_with_sections_by_user_id -> teacher_id: {:?} / student_id: {:?} / scope: {:?}", teacher_id, student_id, scope);
let current_org_id = required_current_org_id_from_sudoer_token_str(&_root.0)?;
if (teacher_id.is_some() && student_id.is_some())
|| (teacher_id.is_none() && student_id.is_none())
@@ -799,6 +851,7 @@ pub async fn raw_find_dakas_with_sections_by_user_id(
teacher_id
.as_ref()
.ok_or_else(|| anyhow!("teacher_id is required"))?,
&current_org_id,
page,
page_size,
extract_conn(fetch_db_conn(&db_pool)?).deref_mut(),
@@ -809,6 +862,7 @@ pub async fn raw_find_dakas_with_sections_by_user_id(
teacher_id
.as_ref()
.ok_or_else(|| anyhow!("teacher_id is required"))?,
&current_org_id,
page,
page_size,
start_from,
@@ -821,6 +875,7 @@ pub async fn raw_find_dakas_with_sections_by_user_id(
teacher_id
.as_ref()
.ok_or_else(|| anyhow!("teacher_id is required"))?,
&current_org_id,
page,
page_size,
extract_conn(fetch_db_conn(&db_pool)?).deref_mut(),
@@ -850,6 +905,7 @@ pub async fn raw_find_dakas_with_sections_by_user_id(
dakas_with_pages = Daka::find_active_dakas_by_student_id_with_pagination(
&id_student,
&current_org_id,
page,
page_size,
extract_conn(fetch_db_conn(&db_pool)?).deref_mut(),