mirror of
https://github.com/SECTL/SecScore.git
synced 2026-07-19 21:14:21 +08:00
实现实时双写同步并接入核心写操作
This commit is contained in:
+161
-121
@@ -547,15 +547,15 @@ async fn ensure_student_tag_pair(
|
|||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn current_remote_and_local(
|
async fn current_remote_and_local_from_state(
|
||||||
app_handle: &AppHandle,
|
app_handle: &AppHandle,
|
||||||
state: &State<'_, Arc<RwLock<AppState>>>,
|
app_state: &Arc<RwLock<AppState>>,
|
||||||
) -> Result<
|
) -> Result<
|
||||||
Option<(sea_orm::DatabaseConnection, sea_orm::DatabaseConnection)>,
|
Option<(sea_orm::DatabaseConnection, sea_orm::DatabaseConnection)>,
|
||||||
String,
|
String,
|
||||||
> {
|
> {
|
||||||
let (current_conn, db_type) = {
|
let (current_conn, db_type) = {
|
||||||
let state_guard = state.read();
|
let state_guard = app_state.read();
|
||||||
let db = state_guard.db.read().clone();
|
let db = state_guard.db.read().clone();
|
||||||
let settings = state_guard.settings.read();
|
let settings = state_guard.settings.read();
|
||||||
let status_json = settings.get_value(SettingsKey::PgConnectionStatus);
|
let status_json = settings.get_value(SettingsKey::PgConnectionStatus);
|
||||||
@@ -588,6 +588,158 @@ async fn current_remote_and_local(
|
|||||||
Ok(Some((local_conn, remote_conn)))
|
Ok(Some((local_conn, remote_conn)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn realtime_dual_write_sync(app_state: &Arc<RwLock<AppState>>) -> Result<(), String> {
|
||||||
|
let app_handle = {
|
||||||
|
let state_guard = app_state.read();
|
||||||
|
state_guard.app_handle.clone()
|
||||||
|
};
|
||||||
|
|
||||||
|
let can_sync = current_remote_and_local_from_state(&app_handle, app_state)
|
||||||
|
.await?
|
||||||
|
.is_some();
|
||||||
|
if !can_sync {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = db_sync_apply_internal(
|
||||||
|
ConflictStrategy::KeepRemote,
|
||||||
|
app_handle,
|
||||||
|
app_state.clone(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
if !result.success {
|
||||||
|
return Err(result
|
||||||
|
.message
|
||||||
|
.unwrap_or_else(|| "实时双写同步失败".to_string()));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn db_sync_apply_internal(
|
||||||
|
strategy: ConflictStrategy,
|
||||||
|
app_handle: AppHandle,
|
||||||
|
app_state: Arc<RwLock<AppState>>,
|
||||||
|
) -> Result<DbSyncApplyResult, String> {
|
||||||
|
let Some((local_conn, remote_conn)) =
|
||||||
|
current_remote_and_local_from_state(&app_handle, &app_state).await?
|
||||||
|
else {
|
||||||
|
return Ok(DbSyncApplyResult {
|
||||||
|
success: false,
|
||||||
|
synced_records: 0,
|
||||||
|
resolved_conflicts: 0,
|
||||||
|
message: Some("当前不在 PostgreSQL 远程模式,无法执行同步".to_string()),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
let local_students = load_students(&local_conn).await?;
|
||||||
|
let remote_students = load_students(&remote_conn).await?;
|
||||||
|
let local_reasons = load_reasons(&local_conn).await?;
|
||||||
|
let remote_reasons = load_reasons(&remote_conn).await?;
|
||||||
|
let local_tags = load_tags(&local_conn).await?;
|
||||||
|
let remote_tags = load_tags(&remote_conn).await?;
|
||||||
|
let local_events = load_events(&local_conn).await?;
|
||||||
|
let remote_events = load_events(&remote_conn).await?;
|
||||||
|
let local_pairs = load_student_tag_pairs(&local_conn).await?;
|
||||||
|
let remote_pairs = load_student_tag_pairs(&remote_conn).await?;
|
||||||
|
|
||||||
|
let (preferred, target) = if strategy == ConflictStrategy::KeepLocal {
|
||||||
|
(&local_conn, &remote_conn)
|
||||||
|
} else {
|
||||||
|
(&remote_conn, &local_conn)
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut synced_records = 0usize;
|
||||||
|
let mut resolved_conflicts = 0usize;
|
||||||
|
|
||||||
|
for student in local_students.values() {
|
||||||
|
if upsert_student(&remote_conn, student).await? {
|
||||||
|
synced_records += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for student in remote_students.values() {
|
||||||
|
if upsert_student(&local_conn, student).await? {
|
||||||
|
synced_records += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for reason in local_reasons.values() {
|
||||||
|
if upsert_reason(&remote_conn, reason).await? {
|
||||||
|
synced_records += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for reason in remote_reasons.values() {
|
||||||
|
if upsert_reason(&local_conn, reason).await? {
|
||||||
|
synced_records += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for tag in local_tags.values() {
|
||||||
|
if upsert_tag(&remote_conn, tag).await? {
|
||||||
|
synced_records += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for tag in remote_tags.values() {
|
||||||
|
if upsert_tag(&local_conn, tag).await? {
|
||||||
|
synced_records += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for event in local_events.values() {
|
||||||
|
if upsert_event(&remote_conn, event).await? {
|
||||||
|
synced_records += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for event in remote_events.values() {
|
||||||
|
if upsert_event(&local_conn, event).await? {
|
||||||
|
synced_records += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for pair in local_pairs.union(&remote_pairs) {
|
||||||
|
if ensure_student_tag_pair(&local_conn, pair).await? {
|
||||||
|
synced_records += 1;
|
||||||
|
}
|
||||||
|
if ensure_student_tag_pair(&remote_conn, pair).await? {
|
||||||
|
synced_records += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let preferred_students = load_students(preferred).await?;
|
||||||
|
for student in preferred_students.values() {
|
||||||
|
if upsert_student(target, student).await? {
|
||||||
|
resolved_conflicts += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let preferred_reasons = load_reasons(preferred).await?;
|
||||||
|
for reason in preferred_reasons.values() {
|
||||||
|
if upsert_reason(target, reason).await? {
|
||||||
|
resolved_conflicts += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let preferred_tags = load_tags(preferred).await?;
|
||||||
|
for tag in preferred_tags.values() {
|
||||||
|
if upsert_tag(target, tag).await? {
|
||||||
|
resolved_conflicts += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let preferred_events = load_events(preferred).await?;
|
||||||
|
for event in preferred_events.values() {
|
||||||
|
if upsert_event(target, event).await? {
|
||||||
|
resolved_conflicts += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let preferred_pairs = load_student_tag_pairs(preferred).await?;
|
||||||
|
for pair in &preferred_pairs {
|
||||||
|
if ensure_student_tag_pair(target, pair).await? {
|
||||||
|
resolved_conflicts += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(DbSyncApplyResult {
|
||||||
|
success: true,
|
||||||
|
synced_records,
|
||||||
|
resolved_conflicts,
|
||||||
|
message: Some("本地与远程同步完成".to_string()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn check_admin_permission(state: &Arc<RwLock<AppState>>) -> Result<(), String> {
|
fn check_admin_permission(state: &Arc<RwLock<AppState>>) -> Result<(), String> {
|
||||||
let state_guard = state.read();
|
let state_guard = state.read();
|
||||||
let mut permissions = state_guard.permissions.write();
|
let mut permissions = state_guard.permissions.write();
|
||||||
@@ -795,7 +947,10 @@ pub async fn db_sync_preview(
|
|||||||
) -> Result<IpcResponse<DbSyncPreviewResult>, String> {
|
) -> Result<IpcResponse<DbSyncPreviewResult>, String> {
|
||||||
check_admin_permission(&state)?;
|
check_admin_permission(&state)?;
|
||||||
|
|
||||||
let Some((local_conn, remote_conn)) = current_remote_and_local(&app_handle, &state).await? else {
|
let app_state = state.inner().clone();
|
||||||
|
let Some((local_conn, remote_conn)) =
|
||||||
|
current_remote_and_local_from_state(&app_handle, &app_state).await?
|
||||||
|
else {
|
||||||
return Ok(IpcResponse::success(DbSyncPreviewResult {
|
return Ok(IpcResponse::success(DbSyncPreviewResult {
|
||||||
can_sync: false,
|
can_sync: false,
|
||||||
need_sync: false,
|
need_sync: false,
|
||||||
@@ -906,123 +1061,8 @@ pub async fn db_sync_apply(
|
|||||||
state: State<'_, Arc<RwLock<AppState>>>,
|
state: State<'_, Arc<RwLock<AppState>>>,
|
||||||
) -> Result<IpcResponse<DbSyncApplyResult>, String> {
|
) -> Result<IpcResponse<DbSyncApplyResult>, String> {
|
||||||
check_admin_permission(&state)?;
|
check_admin_permission(&state)?;
|
||||||
|
let result = db_sync_apply_internal(strategy, app_handle, state.inner().clone()).await?;
|
||||||
let Some((local_conn, remote_conn)) = current_remote_and_local(&app_handle, &state).await? else {
|
Ok(IpcResponse::success(result))
|
||||||
return Ok(IpcResponse::success(DbSyncApplyResult {
|
|
||||||
success: false,
|
|
||||||
synced_records: 0,
|
|
||||||
resolved_conflicts: 0,
|
|
||||||
message: Some("当前不在 PostgreSQL 远程模式,无法执行同步".to_string()),
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
let local_students = load_students(&local_conn).await?;
|
|
||||||
let remote_students = load_students(&remote_conn).await?;
|
|
||||||
let local_reasons = load_reasons(&local_conn).await?;
|
|
||||||
let remote_reasons = load_reasons(&remote_conn).await?;
|
|
||||||
let local_tags = load_tags(&local_conn).await?;
|
|
||||||
let remote_tags = load_tags(&remote_conn).await?;
|
|
||||||
let local_events = load_events(&local_conn).await?;
|
|
||||||
let remote_events = load_events(&remote_conn).await?;
|
|
||||||
let local_pairs = load_student_tag_pairs(&local_conn).await?;
|
|
||||||
let remote_pairs = load_student_tag_pairs(&remote_conn).await?;
|
|
||||||
|
|
||||||
let (preferred, target) = if strategy == ConflictStrategy::KeepLocal {
|
|
||||||
(&local_conn, &remote_conn)
|
|
||||||
} else {
|
|
||||||
(&remote_conn, &local_conn)
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut synced_records = 0usize;
|
|
||||||
let mut resolved_conflicts = 0usize;
|
|
||||||
|
|
||||||
for student in local_students.values() {
|
|
||||||
if upsert_student(&remote_conn, student).await? {
|
|
||||||
synced_records += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for student in remote_students.values() {
|
|
||||||
if upsert_student(&local_conn, student).await? {
|
|
||||||
synced_records += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for reason in local_reasons.values() {
|
|
||||||
if upsert_reason(&remote_conn, reason).await? {
|
|
||||||
synced_records += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for reason in remote_reasons.values() {
|
|
||||||
if upsert_reason(&local_conn, reason).await? {
|
|
||||||
synced_records += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for tag in local_tags.values() {
|
|
||||||
if upsert_tag(&remote_conn, tag).await? {
|
|
||||||
synced_records += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for tag in remote_tags.values() {
|
|
||||||
if upsert_tag(&local_conn, tag).await? {
|
|
||||||
synced_records += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for event in local_events.values() {
|
|
||||||
if upsert_event(&remote_conn, event).await? {
|
|
||||||
synced_records += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for event in remote_events.values() {
|
|
||||||
if upsert_event(&local_conn, event).await? {
|
|
||||||
synced_records += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for pair in local_pairs.union(&remote_pairs) {
|
|
||||||
if ensure_student_tag_pair(&local_conn, pair).await? {
|
|
||||||
synced_records += 1;
|
|
||||||
}
|
|
||||||
if ensure_student_tag_pair(&remote_conn, pair).await? {
|
|
||||||
synced_records += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let preferred_students = load_students(preferred).await?;
|
|
||||||
for student in preferred_students.values() {
|
|
||||||
if upsert_student(target, student).await? {
|
|
||||||
resolved_conflicts += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let preferred_reasons = load_reasons(preferred).await?;
|
|
||||||
for reason in preferred_reasons.values() {
|
|
||||||
if upsert_reason(target, reason).await? {
|
|
||||||
resolved_conflicts += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let preferred_tags = load_tags(preferred).await?;
|
|
||||||
for tag in preferred_tags.values() {
|
|
||||||
if upsert_tag(target, tag).await? {
|
|
||||||
resolved_conflicts += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let preferred_events = load_events(preferred).await?;
|
|
||||||
for event in preferred_events.values() {
|
|
||||||
if upsert_event(target, event).await? {
|
|
||||||
resolved_conflicts += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let preferred_pairs = load_student_tag_pairs(preferred).await?;
|
|
||||||
for pair in &preferred_pairs {
|
|
||||||
if ensure_student_tag_pair(target, pair).await? {
|
|
||||||
resolved_conflicts += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(IpcResponse::success(DbSyncApplyResult {
|
|
||||||
success: true,
|
|
||||||
synced_records,
|
|
||||||
resolved_conflicts,
|
|
||||||
message: Some("本地与远程同步完成".to_string()),
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ use crate::db::entities::{score_events, students};
|
|||||||
use crate::services::PermissionLevel;
|
use crate::services::PermissionLevel;
|
||||||
use crate::state::AppState;
|
use crate::state::AppState;
|
||||||
|
|
||||||
|
use super::database::realtime_dual_write_sync;
|
||||||
use super::response::IpcResponse;
|
use super::response::IpcResponse;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
@@ -172,7 +173,7 @@ pub async fn event_create(
|
|||||||
active.update(&txn).await.map_err(|e| e.to_string())?;
|
active.update(&txn).await.map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
txn.commit().await.map_err(|e| e.to_string())?;
|
txn.commit().await.map_err(|e| e.to_string())?;
|
||||||
|
realtime_dual_write_sync(state.inner()).await?;
|
||||||
Ok(IpcResponse::success(inserted.id))
|
Ok(IpcResponse::success(inserted.id))
|
||||||
}
|
}
|
||||||
Ok(None) => Ok(IpcResponse::error("Student not found")),
|
Ok(None) => Ok(IpcResponse::error("Student not found")),
|
||||||
@@ -233,7 +234,7 @@ pub async fn event_delete(
|
|||||||
.map_err(|e| e.to_string())?;
|
.map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
txn.commit().await.map_err(|e| e.to_string())?;
|
txn.commit().await.map_err(|e| e.to_string())?;
|
||||||
|
realtime_dual_write_sync(state.inner()).await?;
|
||||||
Ok(IpcResponse::success_empty())
|
Ok(IpcResponse::success_empty())
|
||||||
}
|
}
|
||||||
Ok(None) => Ok(IpcResponse::error("Event not found")),
|
Ok(None) => Ok(IpcResponse::error("Event not found")),
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ use crate::db::entities::reasons;
|
|||||||
use crate::services::PermissionLevel;
|
use crate::services::PermissionLevel;
|
||||||
use crate::state::AppState;
|
use crate::state::AppState;
|
||||||
|
|
||||||
|
use super::database::realtime_dual_write_sync;
|
||||||
use super::response::IpcResponse;
|
use super::response::IpcResponse;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
@@ -116,11 +117,14 @@ pub async fn reason_create(
|
|||||||
updated_at: Set(now),
|
updated_at: Set(now),
|
||||||
};
|
};
|
||||||
|
|
||||||
match new_reason.insert(conn).await {
|
match new_reason.insert(conn).await {
|
||||||
Ok(inserted) => Ok(IpcResponse::success(inserted.id)),
|
Ok(inserted) => {
|
||||||
Err(e) => Ok(IpcResponse::error(&format!(
|
realtime_dual_write_sync(state.inner()).await?;
|
||||||
"Failed to create reason: {}",
|
Ok(IpcResponse::success(inserted.id))
|
||||||
e
|
}
|
||||||
|
Err(e) => Ok(IpcResponse::error(&format!(
|
||||||
|
"Failed to create reason: {}",
|
||||||
|
e
|
||||||
))),
|
))),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -165,7 +169,10 @@ pub async fn reason_update(
|
|||||||
}
|
}
|
||||||
|
|
||||||
match active.update(conn).await {
|
match active.update(conn).await {
|
||||||
Ok(_) => Ok(IpcResponse::success_empty()),
|
Ok(_) => {
|
||||||
|
realtime_dual_write_sync(state.inner()).await?;
|
||||||
|
Ok(IpcResponse::success_empty())
|
||||||
|
}
|
||||||
Err(e) => Ok(IpcResponse::error(&format!(
|
Err(e) => Ok(IpcResponse::error(&format!(
|
||||||
"Failed to update reason: {}",
|
"Failed to update reason: {}",
|
||||||
e
|
e
|
||||||
@@ -205,6 +212,7 @@ pub async fn reason_delete(
|
|||||||
if result.rows_affected == 0 {
|
if result.rows_affected == 0 {
|
||||||
Ok(IpcResponse::error("记录不存在"))
|
Ok(IpcResponse::error("记录不存在"))
|
||||||
} else {
|
} else {
|
||||||
|
realtime_dual_write_sync(state.inner()).await?;
|
||||||
Ok(IpcResponse::success(DeleteResult {
|
Ok(IpcResponse::success(DeleteResult {
|
||||||
changes: result.rows_affected as i32,
|
changes: result.rows_affected as i32,
|
||||||
}))
|
}))
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ use crate::models::{StudentUpdate, StudentWithTags};
|
|||||||
use crate::services::PermissionLevel;
|
use crate::services::PermissionLevel;
|
||||||
use crate::state::AppState;
|
use crate::state::AppState;
|
||||||
|
|
||||||
|
use super::database::realtime_dual_write_sync;
|
||||||
use super::response::{ImportResult, IpcResponse};
|
use super::response::{ImportResult, IpcResponse};
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
@@ -126,7 +127,10 @@ pub async fn student_create(
|
|||||||
};
|
};
|
||||||
|
|
||||||
match new_student.insert(conn).await {
|
match new_student.insert(conn).await {
|
||||||
Ok(inserted) => Ok(IpcResponse::success(inserted.id)),
|
Ok(inserted) => {
|
||||||
|
realtime_dual_write_sync(state.inner()).await?;
|
||||||
|
Ok(IpcResponse::success(inserted.id))
|
||||||
|
}
|
||||||
Err(e) => Ok(IpcResponse::error(&format!(
|
Err(e) => Ok(IpcResponse::error(&format!(
|
||||||
"Failed to create student: {}",
|
"Failed to create student: {}",
|
||||||
e
|
e
|
||||||
@@ -181,7 +185,10 @@ pub async fn student_update(
|
|||||||
}
|
}
|
||||||
|
|
||||||
match active.update(conn).await {
|
match active.update(conn).await {
|
||||||
Ok(_) => Ok(IpcResponse::success_empty()),
|
Ok(_) => {
|
||||||
|
realtime_dual_write_sync(state.inner()).await?;
|
||||||
|
Ok(IpcResponse::success_empty())
|
||||||
|
}
|
||||||
Err(e) => Ok(IpcResponse::error(&format!(
|
Err(e) => Ok(IpcResponse::error(&format!(
|
||||||
"Failed to update student: {}",
|
"Failed to update student: {}",
|
||||||
e
|
e
|
||||||
@@ -230,7 +237,7 @@ pub async fn student_delete(
|
|||||||
.map_err(|e| e.to_string())?;
|
.map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
txn.commit().await.map_err(|e| e.to_string())?;
|
txn.commit().await.map_err(|e| e.to_string())?;
|
||||||
|
realtime_dual_write_sync(state.inner()).await?;
|
||||||
Ok(IpcResponse::success_empty())
|
Ok(IpcResponse::success_empty())
|
||||||
}
|
}
|
||||||
Ok(None) => Ok(IpcResponse::error("Student not found")),
|
Ok(None) => Ok(IpcResponse::error("Student not found")),
|
||||||
@@ -314,6 +321,7 @@ pub async fn student_import_from_xlsx(
|
|||||||
}
|
}
|
||||||
|
|
||||||
txn.commit().await.map_err(|e| e.to_string())?;
|
txn.commit().await.map_err(|e| e.to_string())?;
|
||||||
|
realtime_dual_write_sync(state.inner()).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(IpcResponse::success(ImportResult {
|
Ok(IpcResponse::success(ImportResult {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ use crate::db::entities::{student_tags, tags};
|
|||||||
use crate::services::PermissionLevel;
|
use crate::services::PermissionLevel;
|
||||||
use crate::state::AppState;
|
use crate::state::AppState;
|
||||||
|
|
||||||
|
use super::database::realtime_dual_write_sync;
|
||||||
use super::response::{IpcResponse, TagResponse};
|
use super::response::{IpcResponse, TagResponse};
|
||||||
|
|
||||||
fn check_admin_permission(state: &Arc<RwLock<AppState>>, sender_id: Option<u32>) -> bool {
|
fn check_admin_permission(state: &Arc<RwLock<AppState>>, sender_id: Option<u32>) -> bool {
|
||||||
@@ -159,10 +160,13 @@ pub async fn tags_create(
|
|||||||
};
|
};
|
||||||
|
|
||||||
match new_tag.insert(conn).await {
|
match new_tag.insert(conn).await {
|
||||||
Ok(inserted) => Ok(IpcResponse::success(TagResponse {
|
Ok(inserted) => {
|
||||||
id: inserted.id,
|
realtime_dual_write_sync(state.inner()).await?;
|
||||||
name: inserted.name,
|
Ok(IpcResponse::success(TagResponse {
|
||||||
})),
|
id: inserted.id,
|
||||||
|
name: inserted.name,
|
||||||
|
}))
|
||||||
|
}
|
||||||
Err(e) => Ok(IpcResponse::error(&format!("Failed to create tag: {}", e))),
|
Err(e) => Ok(IpcResponse::error(&format!("Failed to create tag: {}", e))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -210,7 +214,7 @@ pub async fn tags_delete(
|
|||||||
.map_err(|e| e.to_string())?;
|
.map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
txn.commit().await.map_err(|e| e.to_string())?;
|
txn.commit().await.map_err(|e| e.to_string())?;
|
||||||
|
realtime_dual_write_sync(state.inner()).await?;
|
||||||
Ok(IpcResponse::success_empty())
|
Ok(IpcResponse::success_empty())
|
||||||
}
|
}
|
||||||
Ok(None) => Ok(IpcResponse::error("Tag not found")),
|
Ok(None) => Ok(IpcResponse::error("Tag not found")),
|
||||||
@@ -264,7 +268,7 @@ pub async fn tags_update_student_tags(
|
|||||||
}
|
}
|
||||||
|
|
||||||
txn.commit().await.map_err(|e| e.to_string())?;
|
txn.commit().await.map_err(|e| e.to_string())?;
|
||||||
|
realtime_dual_write_sync(state.inner()).await?;
|
||||||
Ok(IpcResponse::success_empty())
|
Ok(IpcResponse::success_empty())
|
||||||
} else {
|
} else {
|
||||||
Ok(IpcResponse::error("Database not connected"))
|
Ok(IpcResponse::error("Database not connected"))
|
||||||
|
|||||||
Reference in New Issue
Block a user