feat: account

This commit is contained in:
acx
2024-07-28 15:04:53 +00:00
parent b3ee37fbe3
commit e25c1b5ceb
10 changed files with 382 additions and 8 deletions

View File

@@ -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

150
src/ledger/account.rs Normal file
View File

@@ -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<crate::AppState> {
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<crate::AppState>,
claims: Claims,
Json(payload): Json<CreateAccountRequest>,
) -> Result<Json<db_model::Account>, (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<i64>,
State(app_state): State<crate::AppState>,
claims: Claims,
Json(payload): Json<CreateAccountRequest>,
) -> Result<Json<CommonResp>, (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<i64>,
State(app_state): State<crate::AppState>,
claims: Claims,
) -> Result<Json<db_model::Account>, (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<crate::AppState>,
claims: Claims,
) -> Result<Json<Vec<db_model::Account>>, (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))
}

145
src/ledger/book.rs Normal file
View File

@@ -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<crate::AppState> {
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<crate::AppState>,
claims: Claims,
Json(payload): Json<CreateBookRequest>,
) -> Result<Json<db_model::Book>, (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<i64>,
State(app_state): State<crate::AppState>,
claims: Claims,
Json(payload): Json<CreateBookRequest>,
) -> Result<Json<CommonResp>, (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<i64>,
State(app_state): State<crate::AppState>,
claims: Claims,
) -> Result<Json<db_model::Book>, (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<crate::AppState>,
claims: Claims,
) -> Result<Json<Vec<db_model::Book>>, (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))
}

View File

@@ -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<crate::AppState> {
@@ -35,6 +37,8 @@ pub fn get_nest_handlers() -> Router<crate::AppState> {
#[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)

View File

@@ -1,2 +1,4 @@
pub mod category;
pub mod tag;
pub mod book;
pub mod account;

View File

@@ -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<crate::AppState> {
@@ -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)

View File

@@ -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);

View File

@@ -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)]

View File

@@ -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,

View File

@@ -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,