From e25c1b5ceb3167d52e5d8ee3f4815f08b37d09b4 Mon Sep 17 00:00:00 2001 From: acx Date: Sun, 28 Jul 2024 15:04:53 +0000 Subject: [PATCH] feat: account --- .../2024-07-07-151037_base_schema/up.sql | 6 +- src/ledger/account.rs | 150 ++++++++++++++++++ src/ledger/book.rs | 145 +++++++++++++++++ src/ledger/category.rs | 10 +- src/ledger/mod.rs | 4 +- src/ledger/tag.rs | 10 +- src/main.rs | 2 + src/model/db_model.rs | 49 ++++++ src/model/schema.rs | 7 +- src/schema.rs | 7 +- 10 files changed, 382 insertions(+), 8 deletions(-) create mode 100644 src/ledger/account.rs create mode 100644 src/ledger/book.rs diff --git a/migrations/2024-07-07-151037_base_schema/up.sql b/migrations/2024-07-07-151037_base_schema/up.sql index d5ba287..58907dd 100644 --- a/migrations/2024-07-07-151037_base_schema/up.sql +++ b/migrations/2024-07-07-151037_base_schema/up.sql @@ -4,6 +4,8 @@ CREATE TABLE "categories" ( "id" BIGSERIAL PRIMARY KEY, "uid" BIGINT NOT NULL, "name" TEXT NOT NULL, + "level" INT NOT NULL DEFAULT 0, + "parent_category_id" BIGINT NOT NULL DEFAULT 0, "is_delete" BOOLEAN NOT NULL DEFAULT FALSE, "create_at" TIMESTAMP NOT NULL DEFAULT current_timestamp, "update_at" TIMESTAMP NOT NULL DEFAULT current_timestamp @@ -13,6 +15,8 @@ CREATE TABLE "tags" ( "id" BIGSERIAL PRIMARY KEY, "uid" BIGINT NOT NULL, "name" TEXT NOT NULL, + "level" INT NOT NULL DEFAULT 0, + "parent_tag_id" BIGINT NOT NULL DEFAULT 0, "is_delete" BOOLEAN NOT NULL DEFAULT FALSE, "create_at" TIMESTAMP NOT NULL DEFAULT current_timestamp, "update_at" TIMESTAMP NOT NULL DEFAULT current_timestamp @@ -53,7 +57,7 @@ CREATE TABLE "accounts" ( "id" BIGSERIAL PRIMARY KEY, "uid" BIGINT NOT NULL, "name" TEXT NOT NULL, - "type" BIGINT NOT NULL DEFAULT 0, + "account_type" BIGINT NOT NULL DEFAULT 0, "is_delete" BOOLEAN NOT NULL DEFAULT FALSE, "create_at" TIMESTAMP NOT NULL DEFAULT current_timestamp, "update_at" TIMESTAMP NOT NULL DEFAULT current_timestamp diff --git a/src/ledger/account.rs b/src/ledger/account.rs new file mode 100644 index 0000000..a595d91 --- /dev/null +++ b/src/ledger/account.rs @@ -0,0 +1,150 @@ +// use std::sync::Arc; +use axum::routing::{get, post}; +use axum::{ + extract::{Path, State}, + http::StatusCode, + Json, Router, +}; +use axum_macros::debug_handler; +use diesel::prelude::*; +// use diesel::update; +use serde::{Deserialize, Serialize}; +// use serde_json::to_string; +use crate::model::db_model; +use crate::model::schema; +use crate::util; +use crate::util::req::CommonResp; +use chrono::prelude::*; +use tracing::info; +use crate::middleware::auth; +use crate::middleware::auth::Claims; + +#[derive(Deserialize)] +pub struct CreateAccountRequest { + name: String, + account_type: i64, +} + +#[derive(Serialize)] +pub struct CreateAccountResponse { + id: i64, + name: String, + account_type: i64, +} + +pub fn get_nest_handlers() -> Router { + Router::new() + .route("/", post(create_account).get(get_all_accounts)) + .route("/:id", post(update_account).get(get_account)) +} + +#[debug_handler] +pub async fn create_account( + State(app_state): State, + claims: Claims, + Json(payload): Json, +) -> Result, (StatusCode, String)> { + let uid: i64 = claims.uid.clone(); + let conn = app_state + .db + .get() + .await + .map_err(util::req::internal_error)?; + let new_account = db_model::AccountForm { + name: payload.name, + account_type: payload.account_type, + uid: uid, + }; + let res = conn + .interact(move |conn| { + diesel::insert_into(schema::accounts::table) + .values(&new_account) + .returning(db_model::Account::as_returning()) + .get_result(conn) + }) + .await + .map_err(util::req::internal_error)? + .map_err(util::req::internal_error)?; + Ok(Json(res)) +} + +pub async fn update_account( + Path(id): Path, + State(app_state): State, + claims: Claims, + Json(payload): Json, +) -> Result, (StatusCode, String)> { + let uid: i64 = claims.uid.clone(); + let conn = app_state + .db + .get() + .await + .map_err(util::req::internal_error)?; + let now = Utc::now().naive_utc(); + let res = conn + .interact(move |conn| { + diesel::update(schema::accounts::table) + .filter(schema::accounts::id.eq(id)) + .filter(schema::accounts::uid.eq(uid)) + .set(( + schema::accounts::name.eq(payload.name), + schema::accounts::account_type.eq(payload.account_type), + schema::accounts::update_at.eq(now), + )) + .execute(conn) + }) + .await + .map_err(util::req::internal_error)? + .map_err(util::req::internal_error)?; + let resp = util::req::CommonResp { code: 0 }; + Ok(Json(resp)) +} + +pub async fn get_account( + Path(id): Path, + State(app_state): State, + claims: Claims, +) -> Result, (StatusCode, String)> { + let uid: i64 = claims.uid.clone(); + let conn = app_state + .db + .get() + .await + .map_err(util::req::internal_error)?; + let res = conn + .interact(move |conn| { + schema::accounts::table + .filter(schema::accounts::id.eq(id)) + .filter(schema::accounts::uid.eq(uid)) + .select(db_model::Account::as_select()) + .limit(1) + .get_result(conn) + }) + .await + .map_err(util::req::internal_error)? + .map_err(util::req::internal_error)?; + Ok(Json(res)) +} + +pub async fn get_all_accounts( + State(app_state): State, + claims: Claims, +) -> Result>, (StatusCode, String)> { + let uid: i64 = claims.uid.clone(); + let conn = app_state + .db + .get() + .await + .map_err(util::req::internal_error)?; + let res = conn + .interact(move |conn| { + schema::accounts::table + .filter(schema::accounts::uid.eq(uid)) + .select(db_model::Account::as_select()) + .load(conn) + }) + .await + .map_err(util::req::internal_error)? + .map_err(util::req::internal_error)?; + Ok(Json(res)) +} diff --git a/src/ledger/book.rs b/src/ledger/book.rs new file mode 100644 index 0000000..17e7548 --- /dev/null +++ b/src/ledger/book.rs @@ -0,0 +1,145 @@ +use axum::routing::{get, post}; +use axum::{ + extract::{Path, State}, + http::StatusCode, + Json, Router, +}; +use axum_macros::debug_handler; +use diesel::prelude::*; +// use diesel::update; +use serde::{Deserialize, Serialize}; +// use serde_json::to_string; +use crate::model::db_model; +use crate::model::schema; +use crate::util; +use crate::util::req::CommonResp; +use chrono::prelude::*; +use tracing::info; +use crate::middleware::auth; +use crate::middleware::auth::Claims; + +#[derive(Deserialize)] +pub struct CreateBookRequest { + name: String, +} + +#[derive(Serialize)] +pub struct CreateBookResponse { + id: i64, + name: String, +} + +pub fn get_nest_handlers() -> Router { + Router::new() + .route("/", post(create_book).get(get_all_books)) + .route("/:id", post(update_book).get(get_book)) +} + +#[debug_handler] +pub async fn create_book( + State(app_state): State, + claims: Claims, + Json(payload): Json, +) -> Result, (StatusCode, String)> { + let uid: i64 = claims.uid.clone(); + let conn = app_state + .db + .get() + .await + .map_err(util::req::internal_error)?; + let new_book = db_model::BookForm { + name: payload.name, + uid, + }; + let res = conn + .interact(move |conn| { + diesel::insert_into(schema::books::table) + .values(&new_book) + .returning(db_model::Book::as_returning()) + .get_result(conn) + }) + .await + .map_err(util::req::internal_error)? + .map_err(util::req::internal_error)?; + Ok(Json(res)) +} + +pub async fn update_book( + Path(id): Path, + State(app_state): State, + claims: Claims, + Json(payload): Json, +) -> Result, (StatusCode, String)> { + let uid: i64 = claims.uid.clone(); + let conn = app_state + .db + .get() + .await + .map_err(util::req::internal_error)?; + let now = Utc::now().naive_utc(); + let res = conn + .interact(move |conn| { + diesel::update(schema::books::table) + .filter(schema::books::id.eq(id)) + .filter(schema::books::uid.eq(uid)) + .set(( + schema::books::name.eq(payload.name), + schema::books::update_at.eq(now), + )) + .execute(conn) + }) + .await + .map_err(util::req::internal_error)? + .map_err(util::req::internal_error)?; + let resp = util::req::CommonResp { code: 0 }; + Ok(Json(resp)) +} + +pub async fn get_book( + Path(id): Path, + State(app_state): State, + claims: Claims, +) -> Result, (StatusCode, String)> { + let uid: i64 = claims.uid.clone(); + let conn = app_state + .db + .get() + .await + .map_err(util::req::internal_error)?; + let res = conn + .interact(move |conn| { + schema::books::table + .filter(schema::books::id.eq(id)) + .filter(schema::books::uid.eq(uid)) + .select(db_model::Book::as_select()) + .limit(1) + .get_result(conn) + }) + .await + .map_err(util::req::internal_error)? + .map_err(util::req::internal_error)?; + Ok(Json(res)) +} + +pub async fn get_all_books( + State(app_state): State, + claims: Claims, +) -> Result>, (StatusCode, String)> { + let uid: i64 = claims.uid.clone(); + let conn = app_state + .db + .get() + .await + .map_err(util::req::internal_error)?; + let res = conn + .interact(move |conn| { + schema::books::table + .filter(schema::books::uid.eq(uid)) + .select(db_model::Book::as_select()) + .load(conn) + }) + .await + .map_err(util::req::internal_error)? + .map_err(util::req::internal_error)?; + Ok(Json(res)) +} diff --git a/src/ledger/category.rs b/src/ledger/category.rs index 65af9ed..079d1ab 100644 --- a/src/ledger/category.rs +++ b/src/ledger/category.rs @@ -24,6 +24,8 @@ use crate::middleware::auth::Claims; pub struct CreateCategoryResponse { id: i64, name: String, + level: i32, + parent_category_id: i64, } pub fn get_nest_handlers() -> Router { @@ -35,6 +37,8 @@ pub fn get_nest_handlers() -> Router { #[derive(Deserialize)] pub struct CreateCategoryRequest { name: String, + level: i32, + parent_category_id: i64, } #[debug_handler] @@ -52,7 +56,9 @@ pub async fn create_category( .map_err(util::req::internal_error)?; let new_category = db_model::CategoryForm { name: payload.name, - uid, + uid: uid, + level: payload.level, + parent_category_id: payload.parent_category_id, }; let res = conn .interact(move |conn| { @@ -89,6 +95,8 @@ pub async fn update_category( .filter(schema::categories::uid.eq(uid)) .set(( schema::categories::name.eq(payload.name), + schema::categories::level.eq(payload.level), + schema::categories::parent_category_id.eq(payload.parent_category_id), schema::categories::update_at.eq(now), )) .execute(conn) diff --git a/src/ledger/mod.rs b/src/ledger/mod.rs index 4e864ca..7a0d918 100644 --- a/src/ledger/mod.rs +++ b/src/ledger/mod.rs @@ -1,2 +1,4 @@ pub mod category; -pub mod tag; \ No newline at end of file +pub mod tag; +pub mod book; +pub mod account; \ No newline at end of file diff --git a/src/ledger/tag.rs b/src/ledger/tag.rs index e44f736..8c1b081 100644 --- a/src/ledger/tag.rs +++ b/src/ledger/tag.rs @@ -22,12 +22,16 @@ use crate::middleware::auth::Claims; #[derive(Deserialize)] pub struct CreateTagRequest { name: String, + level: i32, + parent_tag_id: i64, } #[derive(Serialize)] pub struct CreateTagResponse { id: i64, name: String, + level: i32, + parent_tag_id: i64, } pub fn get_nest_handlers() -> Router { @@ -50,7 +54,9 @@ pub async fn create_tag( .map_err(util::req::internal_error)?; let new_tag = db_model::TagForm { name: payload.name, - uid, + uid: uid, + level: payload.level, + parent_tag_id: payload.parent_tag_id, }; let res = conn .interact(move |conn| { @@ -85,6 +91,8 @@ pub async fn update_tag( .filter(schema::tags::uid.eq(uid)) .set(( schema::tags::name.eq(payload.name), + schema::tags::level.eq(payload.level), + schema::tags::parent_tag_id.eq(payload.parent_tag_id), schema::tags::update_at.eq(now), )) .execute(conn) diff --git a/src/main.rs b/src/main.rs index 1f3f696..21e4cd5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -93,6 +93,8 @@ async fn main() { // V1 apis .nest("/api/v1/category", ledger::category::get_nest_handlers()) .nest("/api/v1/tag", ledger::tag::get_nest_handlers()) + .nest("/api/v1/book", ledger::book::get_nest_handlers()) + .nest("/api/v1/account", ledger::account::get_nest_handlers()) .nest("/api/v1/user", user::handler::get_nest_handlers()) .with_state(shared_state) .layer(global_layer); diff --git a/src/model/db_model.rs b/src/model/db_model.rs index c298749..15db6c6 100644 --- a/src/model/db_model.rs +++ b/src/model/db_model.rs @@ -8,6 +8,8 @@ pub struct Category { id: i64, uid: i64, name: String, + level: i32, + parent_category_id: i64, is_delete: bool, create_at: chrono::NaiveDateTime, update_at: chrono::NaiveDateTime, @@ -18,6 +20,8 @@ pub struct Category { pub struct CategoryForm { pub uid: i64, pub name: String, + pub level: i32, + pub parent_category_id: i64, } #[derive(Queryable, Selectable, serde::Serialize, serde::Deserialize)] @@ -27,6 +31,8 @@ pub struct Tag { id: i64, uid: i64, name: String, + level: i32, + parent_tag_id: i64, is_delete: bool, create_at: chrono::NaiveDateTime, update_at: chrono::NaiveDateTime, @@ -37,6 +43,49 @@ pub struct Tag { pub struct TagForm { pub uid: i64, pub name: String, + pub level: i32, + pub parent_tag_id: i64, +} + +#[derive(Queryable, Selectable, serde::Serialize, serde::Deserialize)] +#[diesel(table_name = schema::books)] +#[diesel(check_for_backend(diesel::pg::Pg))] +pub struct Book { + id: i64, + uid: i64, + name: String, + is_delete: bool, + create_at: chrono::NaiveDateTime, + update_at: chrono::NaiveDateTime, +} + +#[derive(serde::Deserialize, Insertable)] +#[diesel(table_name = schema::books)] +pub struct BookForm { + pub uid: i64, + pub name: String, +} + + +#[derive(Queryable, Selectable, serde::Serialize, serde::Deserialize)] +#[diesel(table_name = schema::accounts)] +#[diesel(check_for_backend(diesel::pg::Pg))] +pub struct Account { + id: i64, + uid: i64, + name: String, + account_type: i64, + is_delete: bool, + create_at: chrono::NaiveDateTime, + update_at: chrono::NaiveDateTime, +} + +#[derive(serde::Deserialize, Insertable)] +#[diesel(table_name = schema::accounts)] +pub struct AccountForm { + pub uid: i64, + pub name: String, + pub account_type: i64, } #[derive(Queryable, Selectable, serde::Serialize)] diff --git a/src/model/schema.rs b/src/model/schema.rs index f64c28b..4e7ce31 100644 --- a/src/model/schema.rs +++ b/src/model/schema.rs @@ -5,8 +5,7 @@ diesel::table! { id -> Int8, uid -> Int8, name -> Text, - #[sql_name = "type"] - type_ -> Int8, + account_type -> Int8, is_delete -> Bool, create_at -> Timestamp, update_at -> Timestamp, @@ -42,6 +41,8 @@ diesel::table! { id -> Int8, uid -> Int8, name -> Text, + level -> Int4, + parent_category_id -> Int8, is_delete -> Bool, create_at -> Timestamp, update_at -> Timestamp, @@ -53,6 +54,8 @@ diesel::table! { id -> Int8, uid -> Int8, name -> Text, + level -> Int4, + parent_tag_id -> Int8, is_delete -> Bool, create_at -> Timestamp, update_at -> Timestamp, diff --git a/src/schema.rs b/src/schema.rs index f64c28b..4e7ce31 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -5,8 +5,7 @@ diesel::table! { id -> Int8, uid -> Int8, name -> Text, - #[sql_name = "type"] - type_ -> Int8, + account_type -> Int8, is_delete -> Bool, create_at -> Timestamp, update_at -> Timestamp, @@ -42,6 +41,8 @@ diesel::table! { id -> Int8, uid -> Int8, name -> Text, + level -> Int4, + parent_category_id -> Int8, is_delete -> Bool, create_at -> Timestamp, update_at -> Timestamp, @@ -53,6 +54,8 @@ diesel::table! { id -> Int8, uid -> Int8, name -> Text, + level -> Int4, + parent_tag_id -> Int8, is_delete -> Bool, create_at -> Timestamp, update_at -> Timestamp,