feat(course-package): add org_visible to course_group and course_package_item table

Migrations:
- htyws: add org_id, org_visible columns to course_group
- htykc: create course_package_item table

Backend:
- CourseGroup: add org_visible, org_id fields; find_org_visible_by_org_id query
- CoursePackageItem: new model with sync/list API
- SUPERVISOR role check for package item management
- New endpoint: GET /api/v1/ws/find_org_visible_course_groups
- New endpoints: POST /api/v1/clazz/course-package/item/sync,
  GET /api/v1/clazz/course-package/item/list/{package_id}

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-04-30 14:26:27 +08:00
parent 542fb2461a
commit db08280f6e
12 changed files with 296 additions and 2 deletions
+4
View File
@@ -191,6 +191,10 @@ pub fn ws_rocket(db_url: &str) -> Router {
"/api/v1/ws/delete_course_group/{id_delete}",
post(delete_course_group),
)
.route(
"/api/v1/ws/find_org_visible_course_groups",
get(find_org_visible_course_groups),
)
.route("/api/v1/ws/update_piyue", post(update_piyue))
.route("/api/v1/ws/create_piyue2", post(create_piyue2))
.route("/api/v1/ws/create_course", post(create_course))
+40
View File
@@ -464,6 +464,7 @@ pub fn raw_create_course_group(
in_section_group.updated_at = Some(current_local_datetime());
in_section_group.created_by = Some(user_id.clone());
in_section_group.updated_by = Some(user_id);
in_section_group.org_id = token.current_org_id.clone();
let res = CourseGroup::create(
&in_section_group,
@@ -642,6 +643,45 @@ pub fn raw_delete_course_group(
Ok(())
}
pub async fn find_org_visible_course_groups(
_sudoer: HtySudoerTokenHeader,
auth: AuthorizationHeader,
State(db_pool): State<Arc<DbState>>,
) -> Json<HtyResponse<Vec<ReqCourseGroup>>> {
debug!("find_org_visible_course_groups -> starts");
match raw_find_org_visible_course_groups(&auth, db_pool) {
Ok(ok) => {
debug!(
"find_org_visible_course_groups -> success: {:?}!",
ok.len()
);
wrap_json_ok_resp(ok)
}
Err(e) => {
error!(
"find_org_visible_course_groups -> failed, e: {}",
e
);
wrap_json_anyhow_err(e)
}
}
}
pub fn raw_find_org_visible_course_groups(
auth: &AuthorizationHeader,
db_pool: Arc<DbState>,
) -> anyhow::Result<Vec<ReqCourseGroup>> {
let token = HtyToken::from_jwt(auth.clone().deref())?;
let org = token.current_org_id.ok_or_else(|| anyhow!("org_id is required"))?;
let groups = CourseGroup::find_org_visible_by_org_id(
&org,
extract_conn(fetch_db_conn(&db_pool)?).deref_mut(),
)?;
Ok(groups.iter().map(|g| g.to_req()).collect())
}
pub async fn create_course_section(
root: HtySudoerTokenHeader,
auth: AuthorizationHeader,