Files
kakeibo/backend/src/routes/note.rs
T
suphicakir 7f3fde074c feat(plan): sabit gelir/gider kalemleri ve dönem notları (backend)
- /api/periods/{id}/plan-items: listele, ekle; /api/plan-items/{id}: düzenle, sil
- Önceki dönemden kopyala: göreli gün korunur, dönem sonuna kırpılır, aynı kalem tekrar eklenmez
- /api/periods/{id}/notes (planning | review), /api/notes/{id}: Markdown notlar
- Kapalı dönemde değişiklik yok; silme geri alınabilir şekilde işaretlenir
- Entegrasyon testleri (5): kurallar, kopyalama, salt okunur dönem, hane izolasyonu

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 03:34:35 +03:00

138 lines
3.3 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! Dönem notları:
//!
//! | Yöntem | Yol | İş |
//! |---|---|---|
//! | GET | `/api/periods/{id}/notes?context=planning` | notlar (sayfa filtresi isteğe bağlı) |
//! | POST | `/api/periods/{id}/notes` | not ekle |
//! | PATCH | `/api/notes/{id}` | düzenle |
//! | DELETE | `/api/notes/{id}` | sil |
use axum::{
Json,
extract::{Path, Query, State},
http::StatusCode,
};
use mongodb::bson::Uuid;
use serde::{Deserialize, Serialize};
use ts_rs::TS;
use validator::Validate;
use crate::{
auth::AuthUser,
error::{AppError, AppResult},
extract::ValidJson,
models::note::{Note, NoteContext},
services::note,
state::AppState,
};
#[derive(Deserialize)]
pub struct ListQuery {
context: Option<NoteContext>,
}
#[derive(Deserialize, Validate, TS)]
#[ts(export, rename = "NoteCreateRequest")]
pub struct CreateRequest {
context: NoteContext,
title: String,
/// Markdown
#[serde(default)]
body_md: String,
}
#[derive(Deserialize, Validate, TS)]
#[ts(export, rename = "NoteUpdateRequest", optional_fields)]
pub struct UpdateRequest {
title: Option<String>,
body_md: Option<String>,
version: Option<i32>,
}
#[derive(Serialize, TS)]
#[ts(export, rename = "Note")]
pub struct NoteDto {
id: String,
context: NoteContext,
title: String,
body_md: String,
created_by: String,
/// ISO 8601
updated_at: String,
version: i32,
}
impl From<Note> for NoteDto {
fn from(n: Note) -> Self {
Self {
id: n.id.to_string(),
context: n.context,
title: n.title,
body_md: n.body_md,
created_by: n.created_by.to_string(),
updated_at: n.updated_at.try_to_rfc3339_string().unwrap_or_default(),
version: n.version,
}
}
}
fn parse_id(id: &str) -> AppResult<Uuid> {
Uuid::parse_str(id).map_err(|_| AppError::NotFound)
}
pub async fn list(
State(s): State<AppState>,
auth: AuthUser,
Path(period_id): Path<String>,
Query(q): Query<ListQuery>,
) -> AppResult<Json<Vec<NoteDto>>> {
let notes = note::list(&s.db, auth.household_id, parse_id(&period_id)?, q.context).await?;
Ok(Json(notes.into_iter().map(NoteDto::from).collect()))
}
pub async fn create(
State(s): State<AppState>,
auth: AuthUser,
Path(period_id): Path<String>,
ValidJson(req): ValidJson<CreateRequest>,
) -> AppResult<(StatusCode, Json<NoteDto>)> {
let n = note::create(
&s.db,
auth.household_id,
auth.user_id,
parse_id(&period_id)?,
req.context,
&req.title,
&req.body_md,
)
.await?;
Ok((StatusCode::CREATED, Json(n.into())))
}
pub async fn update(
State(s): State<AppState>,
auth: AuthUser,
Path(id): Path<String>,
ValidJson(req): ValidJson<UpdateRequest>,
) -> AppResult<Json<NoteDto>> {
let n = note::update(
&s.db,
auth.household_id,
parse_id(&id)?,
req.title.as_deref(),
req.body_md.as_deref(),
req.version,
)
.await?;
Ok(Json(n.into()))
}
pub async fn delete(
State(s): State<AppState>,
auth: AuthUser,
Path(id): Path<String>,
) -> AppResult<StatusCode> {
note::delete(&s.db, auth.household_id, parse_id(&id)?).await?;
Ok(StatusCode::NO_CONTENT)
}