草稿和发布数据添加options配置项
All checks were successful
build / build-rust (push) Successful in 3m0s

实现配置项相关的CRUD
调整草稿数据管理接口
This commit is contained in:
soul-walker 2024-09-19 00:07:53 +08:00
parent 875788a71d
commit 2a7a2ffc99
13 changed files with 289 additions and 52 deletions

View File

@ -11,6 +11,7 @@
"Joylink", "Joylink",
"jsonwebtoken", "jsonwebtoken",
"mplj", "mplj",
"Neng",
"plpgsql", "plpgsql",
"prost", "prost",
"proto", "proto",

3
Cargo.lock generated
View File

@ -2199,6 +2199,8 @@ dependencies = [
"anyhow", "anyhow",
"rtss_dto", "rtss_dto",
"rtss_log", "rtss_log",
"serde",
"serde_json",
"sqlx", "sqlx",
"thiserror", "thiserror",
] ]
@ -2210,6 +2212,7 @@ dependencies = [
"async-graphql", "async-graphql",
"prost", "prost",
"prost-build", "prost-build",
"serde",
"sqlx", "sqlx",
] ]

View File

@ -23,6 +23,7 @@ sqlx = { version = "0.8", features = [
"chrono", "chrono",
] } ] }
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0.125"
anyhow = "1.0" anyhow = "1.0"
[dependencies] [dependencies]

View File

@ -6,8 +6,8 @@ edition = "2021"
[dependencies] [dependencies]
anyhow = { workspace = true } anyhow = { workspace = true }
tokio = { workspace = true, features = ["macros", "rt-multi-thread"] } tokio = { workspace = true, features = ["macros", "rt-multi-thread"] }
serde = { version = "1.0.208", features = ["derive"] } serde = { workspace = true }
serde_json = "1.0.125" serde_json = { workspace = true }
chrono = { version = "0.4.38", features = ["serde"] } chrono = { version = "0.4.38", features = ["serde"] }
axum = "0.7.5" axum = "0.7.5"
axum-extra = { version = "0.9.3", features = ["typed-header"] } axum-extra = { version = "0.9.3", features = ["typed-header"] }

View File

@ -1,8 +1,13 @@
use async_graphql::{Context, InputObject, Object, SimpleObject}; use async_graphql::{Context, InputObject, Object, SimpleObject};
use async_graphql::{InputType, OutputType};
use chrono::{DateTime, Local}; use chrono::{DateTime, Local};
use rtss_db::common::TableColumn;
use rtss_db::model::DraftDataColumn;
use rtss_db::DraftDataAccessor; use rtss_db::DraftDataAccessor;
use rtss_db::RtssDbAccessor; use rtss_db::RtssDbAccessor;
use rtss_dto::common::DataType; use rtss_dto::common::{DataType, IscsStyle};
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use crate::pagination::PageQueryDto; use crate::pagination::PageQueryDto;
@ -28,12 +33,12 @@ impl DraftDataQuery {
Ok(paging_result.into()) Ok(paging_result.into())
} }
/// 分页查询用户的草稿数据 /// 分页查询用户的草稿数据
async fn user_draft_data_paging<'ctx>( async fn user_draft_iscs_data_paging<'ctx>(
&self, &self,
ctx: &Context<'ctx>, ctx: &Context<'ctx>,
paging: PageQueryDto, paging: PageQueryDto,
query: UserDraftDataFilterDto, query: UserDraftDataFilterDto<IscsDataOptions>,
) -> async_graphql::Result<DraftDataPage> { ) -> async_graphql::Result<DraftIscsDataPage> {
let db_accessor = ctx.data::<RtssDbAccessor>()?; let db_accessor = ctx.data::<RtssDbAccessor>()?;
let paging_result = db_accessor let paging_result = db_accessor
.query_draft_data(query.into(), paging.into()) .query_draft_data(query.into(), paging.into())
@ -41,12 +46,12 @@ impl DraftDataQuery {
Ok(paging_result.into()) Ok(paging_result.into())
} }
/// 分页查询共享的草稿数据 /// 分页查询共享的草稿数据
async fn shared_draft_data_paging<'ctx>( async fn shared_draft_iscs_data_paging<'ctx>(
&self, &self,
ctx: &Context<'ctx>, ctx: &Context<'ctx>,
paging: PageQueryDto, paging: PageQueryDto,
query: SharedDraftDataFilterDto, query: SharedDraftDataFilterDto<IscsDataOptions>,
) -> async_graphql::Result<DraftDataPage> { ) -> async_graphql::Result<DraftIscsDataPage> {
let db_accessor = ctx.data::<RtssDbAccessor>()?; let db_accessor = ctx.data::<RtssDbAccessor>()?;
let paging_result = db_accessor let paging_result = db_accessor
.query_draft_data(query.into(), paging.into()) .query_draft_data(query.into(), paging.into())
@ -54,7 +59,11 @@ impl DraftDataQuery {
Ok(paging_result.into()) Ok(paging_result.into())
} }
/// 根据id获取草稿数据 /// 根据id获取草稿数据
async fn draft_data(&self, ctx: &Context<'_>, id: i32) -> async_graphql::Result<DraftData> { async fn draft_data(
&self,
ctx: &Context<'_>,
id: i32,
) -> async_graphql::Result<DraftDataWithStringOptions> {
let db_accessor = ctx.data::<RtssDbAccessor>()?; let db_accessor = ctx.data::<RtssDbAccessor>()?;
let draft_data = db_accessor.query_draft_data_by_id(id).await?; let draft_data = db_accessor.query_draft_data_by_id(id).await?;
Ok(draft_data.into()) Ok(draft_data.into())
@ -74,12 +83,12 @@ impl DraftDataQuery {
#[Object] #[Object]
impl DraftDataMutation { impl DraftDataMutation {
/// 创建草稿数据 /// 创建草稿ISCS数据
async fn create_draft_data( async fn create_draft_iscs_data(
&self, &self,
ctx: &Context<'_>, ctx: &Context<'_>,
input: CreateDraftDataDto, input: CreateDraftDataDto<IscsDataOptions>,
) -> async_graphql::Result<DraftData> { ) -> async_graphql::Result<DraftData<IscsDataOptions>> {
let db_accessor = ctx.data::<RtssDbAccessor>()?; let db_accessor = ctx.data::<RtssDbAccessor>()?;
let draft_data = db_accessor.create_draft_data(input.into()).await?; let draft_data = db_accessor.create_draft_data(input.into()).await?;
Ok(draft_data.into()) Ok(draft_data.into())
@ -90,7 +99,7 @@ impl DraftDataMutation {
ctx: &Context<'_>, ctx: &Context<'_>,
id: i32, id: i32,
name: String, name: String,
) -> async_graphql::Result<DraftData> { ) -> async_graphql::Result<DraftDataWithStringOptions> {
let db_accessor = ctx.data::<RtssDbAccessor>()?; let db_accessor = ctx.data::<RtssDbAccessor>()?;
let draft_data = db_accessor.update_draft_data_name(id, &name).await?; let draft_data = db_accessor.update_draft_data_name(id, &name).await?;
Ok(draft_data.into()) Ok(draft_data.into())
@ -101,7 +110,7 @@ impl DraftDataMutation {
ctx: &Context<'_>, ctx: &Context<'_>,
id: i32, id: i32,
data: Vec<u8>, data: Vec<u8>,
) -> async_graphql::Result<DraftData> { ) -> async_graphql::Result<DraftDataWithStringOptions> {
let db_accessor = ctx.data::<RtssDbAccessor>()?; let db_accessor = ctx.data::<RtssDbAccessor>()?;
let draft_data = db_accessor let draft_data = db_accessor
.update_draft_data_data(id, data.as_slice()) .update_draft_data_data(id, data.as_slice())
@ -114,7 +123,7 @@ impl DraftDataMutation {
ctx: &Context<'_>, ctx: &Context<'_>,
id: i32, id: i32,
is_shared: bool, is_shared: bool,
) -> async_graphql::Result<DraftData> { ) -> async_graphql::Result<DraftDataWithStringOptions> {
let db_accessor = ctx.data::<RtssDbAccessor>()?; let db_accessor = ctx.data::<RtssDbAccessor>()?;
let draft_data = db_accessor.set_draft_data_shared(id, is_shared).await?; let draft_data = db_accessor.set_draft_data_shared(id, is_shared).await?;
Ok(draft_data.into()) Ok(draft_data.into())
@ -135,7 +144,7 @@ impl DraftDataMutation {
ctx: &Context<'_>, ctx: &Context<'_>,
id: i32, id: i32,
release_data_id: i32, release_data_id: i32,
) -> async_graphql::Result<DraftData> { ) -> async_graphql::Result<DraftDataWithStringOptions> {
let db_accessor = ctx.data::<RtssDbAccessor>()?; let db_accessor = ctx.data::<RtssDbAccessor>()?;
let draft_data = db_accessor let draft_data = db_accessor
.set_default_release_data_id(id, release_data_id) .set_default_release_data_id(id, release_data_id)
@ -149,7 +158,7 @@ impl DraftDataMutation {
id: i32, id: i32,
name: String, name: String,
user_id: i32, user_id: i32,
) -> async_graphql::Result<DraftData> { ) -> async_graphql::Result<DraftDataWithStringOptions> {
let db_accessor = ctx.data::<RtssDbAccessor>()?; let db_accessor = ctx.data::<RtssDbAccessor>()?;
let draft_data = db_accessor.save_as_new_draft(id, &name, user_id).await?; let draft_data = db_accessor.save_as_new_draft(id, &name, user_id).await?;
Ok(draft_data.into()) Ok(draft_data.into())
@ -157,54 +166,87 @@ impl DraftDataMutation {
} }
#[derive(Debug, InputObject)] #[derive(Debug, InputObject)]
pub struct CreateDraftDataDto { #[graphql(concrete(name = "CreateDraftIscsDto", params(IscsDataOptions)))]
pub struct CreateDraftDataDto<T: DataOptions> {
pub name: String, pub name: String,
pub data_type: rtss_dto::common::DataType, pub options: Option<T>,
pub data: Vec<u8>,
pub user_id: i32, pub user_id: i32,
} }
impl From<CreateDraftDataDto> for rtss_db::CreateDraftData { impl<T: DataOptions> From<CreateDraftDataDto<T>> for rtss_db::CreateDraftData {
fn from(value: CreateDraftDataDto) -> Self { fn from(value: CreateDraftDataDto<T>) -> Self {
Self::new(&value.name, value.data_type, value.user_id).with_data(value.data.as_slice()) let cdd = Self::new(&value.name, DataType::Iscs, value.user_id);
if value.options.is_some() {
cdd.with_options(serde_json::to_value(value.options).unwrap())
} else {
cdd
}
} }
} }
/// 用户的草稿数据查询条件 /// 用户的草稿数据查询条件
#[derive(Debug, InputObject)] #[derive(Debug, InputObject)]
pub struct UserDraftDataFilterDto { #[graphql(concrete(name = "UserDraftIscsDataFilterDto", params(IscsDataOptions)))]
pub struct UserDraftDataFilterDto<T: DataOptions> {
pub user_id: i32, pub user_id: i32,
pub name: Option<String>, pub name: Option<String>,
pub data_type: Option<rtss_dto::common::DataType>, pub data_type: Option<rtss_dto::common::DataType>,
pub options: Option<T>,
pub is_shared: Option<bool>, pub is_shared: Option<bool>,
} }
impl From<UserDraftDataFilterDto> for rtss_db::DraftDataQuery { impl<T: DataOptions> From<UserDraftDataFilterDto<T>> for rtss_db::DraftDataQuery {
fn from(value: UserDraftDataFilterDto) -> Self { fn from(value: UserDraftDataFilterDto<T>) -> Self {
Self { Self {
user_id: Some(value.user_id), user_id: Some(value.user_id),
name: value.name, name: value.name,
data_type: value.data_type, data_type: value.data_type,
options_filter: value.options.map(|o| o.to_data_options_filter_clause()),
is_shared: value.is_shared, is_shared: value.is_shared,
} }
} }
} }
pub trait DataOptions: InputType + OutputType + Serialize + DeserializeOwned {
fn to_data_options_filter_clause(&self) -> String;
}
#[derive(Debug, InputObject, SimpleObject, Serialize, Deserialize)]
#[graphql(input_name = "IscsDataOptionsInput")]
pub struct IscsDataOptions {
pub style: IscsStyle,
}
impl DataOptions for IscsDataOptions {
fn to_data_options_filter_clause(&self) -> String {
let options_column = DraftDataColumn::Options.name();
// format!("{options_column}['style'] = '\"{}\"'", self.style.as_str_name())
format!(
"{options_column} @> '{}'",
serde_json::to_string(self).unwrap()
)
}
}
/// 共享的草稿数据查询条件 /// 共享的草稿数据查询条件
#[derive(Debug, InputObject)] #[derive(Debug, InputObject)]
pub struct SharedDraftDataFilterDto { #[graphql(concrete(name = "SharedDraftIscsDataFilterDto", params(IscsDataOptions)))]
pub struct SharedDraftDataFilterDto<T: DataOptions> {
pub user_id: Option<i32>, pub user_id: Option<i32>,
pub name: Option<String>, pub name: Option<String>,
pub data_type: Option<rtss_dto::common::DataType>, pub data_type: Option<rtss_dto::common::DataType>,
pub options: Option<T>,
} }
impl From<SharedDraftDataFilterDto> for rtss_db::DraftDataQuery { impl<T: DataOptions> From<SharedDraftDataFilterDto<T>> for rtss_db::DraftDataQuery {
fn from(value: SharedDraftDataFilterDto) -> Self { fn from(value: SharedDraftDataFilterDto<T>) -> Self {
Self { Self {
user_id: value.user_id, user_id: value.user_id,
name: value.name, name: value.name,
data_type: value.data_type, data_type: value.data_type,
is_shared: Some(true), is_shared: Some(true),
options_filter: value.options.map(|o| o.to_data_options_filter_clause()),
} }
} }
} }
@ -225,15 +267,17 @@ impl From<DraftDataFilterDto> for rtss_db::DraftDataQuery {
name: value.name, name: value.name,
data_type: value.data_type, data_type: value.data_type,
is_shared: value.is_shared, is_shared: value.is_shared,
options_filter: None,
} }
} }
} }
#[derive(Debug, SimpleObject)] #[derive(Debug, SimpleObject)]
pub struct DraftData { pub struct DraftDataWithStringOptions {
pub id: i32, pub id: i32,
pub name: String, pub name: String,
pub data_type: rtss_dto::common::DataType, pub data_type: rtss_dto::common::DataType,
pub options: Option<String>,
pub data: Option<Vec<u8>>, pub data: Option<Vec<u8>>,
pub user_id: i32, pub user_id: i32,
pub is_shared: bool, pub is_shared: bool,
@ -241,12 +285,43 @@ pub struct DraftData {
pub updated_at: DateTime<Local>, pub updated_at: DateTime<Local>,
} }
impl From<rtss_db::model::DraftDataModel> for DraftData { impl From<rtss_db::model::DraftDataModel> for DraftDataWithStringOptions {
fn from(value: rtss_db::model::DraftDataModel) -> Self { fn from(value: rtss_db::model::DraftDataModel) -> Self {
Self { Self {
id: value.id, id: value.id,
name: value.name, name: value.name,
data_type: DataType::try_from(value.data_type).unwrap(), data_type: DataType::try_from(value.data_type).unwrap(),
options: value.options.map(|o| serde_json::to_string(&o).unwrap()),
data: value.data,
user_id: value.user_id,
is_shared: value.is_shared,
created_at: value.created_at,
updated_at: value.updated_at,
}
}
}
#[derive(Debug, SimpleObject)]
#[graphql(concrete(name = "DraftIscsData", params(IscsDataOptions)))]
pub struct DraftData<T: OutputType> {
pub id: i32,
pub name: String,
pub data_type: rtss_dto::common::DataType,
pub options: Option<T>,
pub data: Option<Vec<u8>>,
pub user_id: i32,
pub is_shared: bool,
pub created_at: DateTime<Local>,
pub updated_at: DateTime<Local>,
}
impl<T: DataOptions> From<rtss_db::model::DraftDataModel> for DraftData<T> {
fn from(value: rtss_db::model::DraftDataModel) -> Self {
Self {
id: value.id,
name: value.name,
data_type: DataType::try_from(value.data_type).unwrap(),
options: value.options.map(|o| serde_json::from_value(o).unwrap()),
data: value.data, data: value.data,
user_id: value.user_id, user_id: value.user_id,
is_shared: value.is_shared, is_shared: value.is_shared,
@ -259,7 +334,7 @@ impl From<rtss_db::model::DraftDataModel> for DraftData {
#[derive(Debug, SimpleObject)] #[derive(Debug, SimpleObject)]
pub struct DraftDataPage { pub struct DraftDataPage {
pub total: i64, pub total: i64,
pub data: Vec<DraftData>, pub data: Vec<DraftDataWithStringOptions>,
} }
impl From<rtss_db::common::PageResult<rtss_db::model::DraftDataModel>> for DraftDataPage { impl From<rtss_db::common::PageResult<rtss_db::model::DraftDataModel>> for DraftDataPage {
@ -270,3 +345,47 @@ impl From<rtss_db::common::PageResult<rtss_db::model::DraftDataModel>> for Draft
} }
} }
} }
#[derive(Debug, SimpleObject)]
pub struct DraftIscsDataPage {
pub total: i64,
pub data: Vec<DraftData<IscsDataOptions>>,
}
impl From<rtss_db::common::PageResult<rtss_db::model::DraftDataModel>> for DraftIscsDataPage {
fn from(value: rtss_db::common::PageResult<rtss_db::model::DraftDataModel>) -> Self {
Self {
total: value.total,
data: value.data.into_iter().map(|m| m.into()).collect(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_iscs_data_options_serialize() {
rtss_log::Logging::default().init();
let options = IscsDataOptions {
style: IscsStyle::DaShiZhiNeng,
};
let json = serde_json::to_string(&options).unwrap();
println!("{}", json);
println!("{}", options.style.as_str_name());
assert_eq!(json, r#"{"style":"DaShiZhiNeng"}"#);
let options: IscsDataOptions = serde_json::from_str(&json).unwrap();
assert_eq!(options.style, IscsStyle::DaShiZhiNeng);
}
#[test]
fn test_iscs_data_options_into_data_options_filter_clause() {
let options = IscsDataOptions {
style: IscsStyle::DaShiZhiNeng,
};
let clause = options.to_data_options_filter_clause();
println!("{clause}");
assert_eq!(clause, r#"options @> '{"style":"DaShiZhiNeng"}'"#);
}
}

View File

@ -5,6 +5,8 @@ edition = "2021"
[dependencies] [dependencies]
anyhow = { workspace = true } anyhow = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
sqlx = { workspace = true, features = [ sqlx = { workspace = true, features = [
"runtime-tokio", "runtime-tokio",
"macros", "macros",

View File

@ -2,6 +2,7 @@ use std::vec;
use rtss_dto::common::DataType; use rtss_dto::common::DataType;
use rtss_log::tracing::debug; use rtss_log::tracing::debug;
use serde_json::Value;
use crate::{ use crate::{
common::{PageQuery, PageResult, Sort, SortOrder, TableColumn}, common::{PageQuery, PageResult, Sort, SortOrder, TableColumn},
@ -69,6 +70,8 @@ pub struct DraftDataQuery {
pub user_id: Option<i32>, pub user_id: Option<i32>,
pub name: Option<String>, pub name: Option<String>,
pub data_type: Option<DataType>, pub data_type: Option<DataType>,
/// 选项过滤条件,由业务层构建并确保合法性
pub options_filter: Option<String>,
pub is_shared: Option<bool>, pub is_shared: Option<bool>,
} }
@ -97,6 +100,11 @@ impl DraftDataQuery {
self self
} }
pub fn with_options_filter(mut self, options_filter: &str) -> Self {
self.options_filter = Some(options_filter.to_string());
self
}
fn build_filter(&self) -> String { fn build_filter(&self) -> String {
let mut filters = vec![]; let mut filters = vec![];
if let Some(user_id) = self.user_id { if let Some(user_id) = self.user_id {
@ -123,6 +131,9 @@ impl DraftDataQuery {
is_shared is_shared
)); ));
} }
if let Some(options_filter) = &self.options_filter {
filters.push(options_filter.clone());
}
if filters.is_empty() { if filters.is_empty() {
"".to_string() "".to_string()
} else { } else {
@ -134,6 +145,7 @@ impl DraftDataQuery {
pub struct CreateDraftData { pub struct CreateDraftData {
name: String, name: String,
data_type: DataType, data_type: DataType,
options: Option<Value>,
data: Option<Vec<u8>>, data: Option<Vec<u8>>,
default_release_data_id: Option<i32>, default_release_data_id: Option<i32>,
user_id: i32, user_id: i32,
@ -144,12 +156,18 @@ impl CreateDraftData {
CreateDraftData { CreateDraftData {
name: name.to_string(), name: name.to_string(),
data_type, data_type,
options: None,
data: None, data: None,
default_release_data_id: None, default_release_data_id: None,
user_id, user_id,
} }
} }
pub fn with_options(mut self, options: Value) -> Self {
self.options = Some(options);
self
}
pub fn with_data(mut self, data: &[u8]) -> Self { pub fn with_data(mut self, data: &[u8]) -> Self {
self.data = Some(data.to_vec()); self.data = Some(data.to_vec());
self self
@ -240,21 +258,23 @@ impl DraftDataAccessor for RtssDbAccessor {
// 插入数据 // 插入数据
let table = DraftDataColumn::Table.name(); let table = DraftDataColumn::Table.name();
let columns = format!( let columns = format!(
"{}, {}, {}, {}, {}", "{}, {}, {}, {}, {}, {}",
DraftDataColumn::Name.name(), DraftDataColumn::Name.name(),
DraftDataColumn::DataType.name(), DraftDataColumn::DataType.name(),
DraftDataColumn::Options.name(),
DraftDataColumn::UserId.name(), DraftDataColumn::UserId.name(),
DraftDataColumn::Data.name(), DraftDataColumn::Data.name(),
DraftDataColumn::DefaultReleaseDataId.name(), DraftDataColumn::DefaultReleaseDataId.name(),
); );
let sql = let sql =
format!("INSERT INTO {table} ({columns}) VALUES ($1, $2, $3, $4, $5) RETURNING *",); format!("INSERT INTO {table} ({columns}) VALUES ($1, $2, $3, $4, $5, $6) RETURNING *",);
// log sql // log sql
debug!("create sql: {}", sql); debug!("create sql: {}", sql);
// 插入数据 // 插入数据
let draft_data: DraftDataModel = sqlx::query_as(&sql) let draft_data: DraftDataModel = sqlx::query_as(&sql)
.bind(create.name) .bind(create.name)
.bind(create.data_type as i32) .bind(create.data_type as i32)
.bind(create.options)
.bind(create.user_id) .bind(create.user_id)
.bind(create.data) .bind(create.data)
.bind(create.default_release_data_id) .bind(create.default_release_data_id)
@ -380,9 +400,16 @@ impl DraftDataAccessor for RtssDbAccessor {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use rtss_dto::common::IscsStyle;
use rtss_log::tracing::Level; use rtss_log::tracing::Level;
use serde::{Deserialize, Serialize};
use sqlx::PgPool; use sqlx::PgPool;
#[derive(Debug, Serialize, Deserialize)]
pub struct IscsDataOptions {
pub style: IscsStyle,
}
// You could also do `use foo_crate::MIGRATOR` and just refer to it as `MIGRATOR` here. // You could also do `use foo_crate::MIGRATOR` and just refer to it as `MIGRATOR` here.
#[sqlx::test(migrator = "crate::MIGRATOR")] #[sqlx::test(migrator = "crate::MIGRATOR")]
async fn basic_use_test(pool: PgPool) -> Result<(), DbAccessError> { async fn basic_use_test(pool: PgPool) -> Result<(), DbAccessError> {
@ -455,15 +482,7 @@ mod tests {
// 查询确认当前数据已删除 // 查询确认当前数据已删除
let page = accessor let page = accessor
.query_draft_data( .query_draft_data(DraftDataQuery::new(), PageQuery::new(1, 100))
DraftDataQuery {
user_id: None,
name: None,
data_type: None,
is_shared: None,
},
PageQuery::new(1, 100),
)
.await?; .await?;
assert_eq!(page.total, 0); assert_eq!(page.total, 0);
@ -471,11 +490,28 @@ mod tests {
// 分四个user_id各插入5条数据 // 分四个user_id各插入5条数据
for i in 1..5 { for i in 1..5 {
for j in 1..6 { for j in 1..6 {
let draft = accessor
.create_draft_data(CreateDraftData::new(&format!("test{}", j), DataType::Em, i))
.await?;
if i == 1 { if i == 1 {
let draft = accessor
.create_draft_data(
CreateDraftData::new(&format!("test{}", j), DataType::Iscs, i)
.with_options(
serde_json::to_value(IscsDataOptions {
style: IscsStyle::DaShiZhiNeng,
})
.unwrap(),
),
)
.await?;
println!("{:?}", draft);
accessor.set_draft_data_shared(draft.id, true).await?; accessor.set_draft_data_shared(draft.id, true).await?;
} else {
accessor
.create_draft_data(CreateDraftData::new(
&format!("test{}", j),
DataType::Em,
i,
))
.await?;
} }
} }
} }
@ -483,7 +519,7 @@ mod tests {
let page = accessor let page = accessor
.query_draft_data( .query_draft_data(
DraftDataQuery::new() DraftDataQuery::new()
.with_user_id(1) .with_user_id(2)
.with_name("test".to_string()) .with_name("test".to_string())
.with_data_type(DataType::Em), .with_data_type(DataType::Em),
PageQuery::new(1, 10), PageQuery::new(1, 10),
@ -505,6 +541,16 @@ mod tests {
.await?; .await?;
assert_eq!(page.total, 5); assert_eq!(page.total, 5);
// 查询options
let page = accessor
.query_draft_data(
DraftDataQuery::new()
.with_options_filter(r#"options @> '{"style":"DaShiZhiNeng"}'"#),
PageQuery::new(1, 10),
)
.await?;
assert_eq!(page.total, 5);
Ok(()) Ok(())
} }
} }

View File

@ -1,14 +1,16 @@
use serde_json::Value;
use sqlx::types::chrono::{DateTime, Local}; use sqlx::types::chrono::{DateTime, Local};
use crate::common::TableColumn; use crate::common::TableColumn;
/// 数据库表 rtss.draft_data 列映射 /// 数据库表 rtss.draft_data 列映射
#[derive(Debug)] #[derive(Debug)]
pub(crate) enum DraftDataColumn { pub enum DraftDataColumn {
Table, Table,
Id, Id,
Name, Name,
DataType, DataType,
Options,
Data, Data,
DefaultReleaseDataId, DefaultReleaseDataId,
UserId, UserId,
@ -23,6 +25,8 @@ pub struct DraftDataModel {
pub name: String, pub name: String,
pub data_type: i32, pub data_type: i32,
#[sqlx(default)] #[sqlx(default)]
pub options: Option<Value>,
#[sqlx(default)]
pub data: Option<Vec<u8>>, pub data: Option<Vec<u8>>,
#[sqlx(default)] #[sqlx(default)]
pub default_release_data_id: Option<i32>, pub default_release_data_id: Option<i32>,
@ -35,11 +39,12 @@ pub struct DraftDataModel {
/// 数据库表 rtss.release_data 列映射 /// 数据库表 rtss.release_data 列映射
#[derive(Debug)] #[derive(Debug)]
pub(crate) enum ReleaseDataColumn { pub enum ReleaseDataColumn {
Table, Table,
Id, Id,
Name, Name,
DataType, DataType,
Options,
UsedVersionId, UsedVersionId,
UserId, UserId,
IsPublished, IsPublished,
@ -53,6 +58,8 @@ pub struct ReleaseDataModel {
pub id: i32, pub id: i32,
pub name: String, pub name: String,
pub data_type: i32, pub data_type: i32,
#[sqlx(default)]
pub options: Option<Value>,
pub used_version_id: Option<i32>, pub used_version_id: Option<i32>,
pub user_id: i32, pub user_id: i32,
pub is_published: bool, pub is_published: bool,
@ -201,6 +208,7 @@ impl TableColumn for DraftDataColumn {
DraftDataColumn::Id => "id", DraftDataColumn::Id => "id",
DraftDataColumn::Name => "name", DraftDataColumn::Name => "name",
DraftDataColumn::DataType => "data_type", DraftDataColumn::DataType => "data_type",
DraftDataColumn::Options => "options",
DraftDataColumn::Data => "data", DraftDataColumn::Data => "data",
DraftDataColumn::DefaultReleaseDataId => "default_release_data_id", DraftDataColumn::DefaultReleaseDataId => "default_release_data_id",
DraftDataColumn::UserId => "user_id", DraftDataColumn::UserId => "user_id",
@ -218,6 +226,7 @@ impl TableColumn for ReleaseDataColumn {
ReleaseDataColumn::Id => "id", ReleaseDataColumn::Id => "id",
ReleaseDataColumn::Name => "name", ReleaseDataColumn::Name => "name",
ReleaseDataColumn::DataType => "data_type", ReleaseDataColumn::DataType => "data_type",
ReleaseDataColumn::Options => "options",
ReleaseDataColumn::UsedVersionId => "used_version_id", ReleaseDataColumn::UsedVersionId => "used_version_id",
ReleaseDataColumn::UserId => "user_id", ReleaseDataColumn::UserId => "user_id",
ReleaseDataColumn::IsPublished => "is_published", ReleaseDataColumn::IsPublished => "is_published",

View File

@ -7,6 +7,7 @@ edition = "2021"
prost = "0.13" prost = "0.13"
async-graphql = { version = "7.0.7", features = ["chrono"] } async-graphql = { version = "7.0.7", features = ["chrono"] }
sqlx = { workspace = true } sqlx = { workspace = true }
serde = { workspace = true }
[build-dependencies] [build-dependencies]
prost-build = "0.13" prost-build = "0.13"

View File

@ -23,6 +23,11 @@ fn main() {
.out_dir("src/pb") .out_dir("src/pb")
.type_attribute("common.DataType", "#[derive(sqlx::Type)]") .type_attribute("common.DataType", "#[derive(sqlx::Type)]")
.type_attribute("common.DataType", "#[derive(async_graphql::Enum)]") .type_attribute("common.DataType", "#[derive(async_graphql::Enum)]")
.type_attribute("common.IscsStyle", "#[derive(async_graphql::Enum)]")
.type_attribute(
"common.IscsStyle",
"#[derive(serde::Serialize, serde::Deserialize)]",
)
.type_attribute("common.FeatureType", "#[derive(sqlx::Type)]") .type_attribute("common.FeatureType", "#[derive(sqlx::Type)]")
.compile_protos( .compile_protos(
&[ &[

View File

@ -141,6 +141,47 @@ impl DataType {
} }
} }
} }
#[derive(
async_graphql::Enum,
serde::Serialize,
serde::Deserialize,
Clone,
Copy,
Debug,
PartialEq,
Eq,
Hash,
PartialOrd,
Ord,
::prost::Enumeration,
)]
#[repr(i32)]
pub enum IscsStyle {
/// 未知
Unknown = 0,
/// 达实智能(福州一号线)
DaShiZhiNeng = 1,
}
impl IscsStyle {
/// String value of the enum field names used in the ProtoBuf definition.
///
/// The values are not transformed in any way and thus are considered stable
/// (if the ProtoBuf definition does not change) and safe for programmatic use.
pub fn as_str_name(&self) -> &'static str {
match self {
IscsStyle::Unknown => "IscsStyle_Unknown",
IscsStyle::DaShiZhiNeng => "DaShiZhiNeng",
}
}
/// Creates an enum from field names used in the ProtoBuf definition.
pub fn from_str_name(value: &str) -> ::core::option::Option<Self> {
match value {
"IscsStyle_Unknown" => Some(Self::Unknown),
"DaShiZhiNeng" => Some(Self::DaShiZhiNeng),
_ => None,
}
}
}
/// 功能特性类型 /// 功能特性类型
#[derive( #[derive(
sqlx::Type, Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration, sqlx::Type, Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration,

View File

@ -7,6 +7,7 @@ CREATE TABLE
id SERIAL PRIMARY KEY, -- id 自增主键 id SERIAL PRIMARY KEY, -- id 自增主键
name VARCHAR(128) NOT NULL, -- 草稿名称 name VARCHAR(128) NOT NULL, -- 草稿名称
data_type INT NOT NULL, -- 数据类型 data_type INT NOT NULL, -- 数据类型
options JSONB NULL, -- 数据相关的参数项或配置项
data BYTEA, -- 草稿数据 data BYTEA, -- 草稿数据
default_release_data_id INT NULL, -- 默认发布数据id default_release_data_id INT NULL, -- 默认发布数据id
user_id INT NOT NULL, -- 创建用户id user_id INT NOT NULL, -- 创建用户id
@ -22,6 +23,9 @@ CREATE INDEX ON rtss.draft_data (user_id);
-- 创建草稿数据类型索引 -- 创建草稿数据类型索引
CREATE INDEX ON rtss.draft_data (data_type); CREATE INDEX ON rtss.draft_data (data_type);
-- 创建草稿数据配置项索引
CREATE INDEX ON rtss.draft_data USING GIN (options);
-- 注释草稿数据表 -- 注释草稿数据表
COMMENT ON TABLE rtss.draft_data IS '草稿数据表'; COMMENT ON TABLE rtss.draft_data IS '草稿数据表';
@ -32,6 +36,8 @@ COMMENT ON COLUMN rtss.draft_data.name IS '草稿名称';
COMMENT ON COLUMN rtss.draft_data.data_type IS '数据类型'; COMMENT ON COLUMN rtss.draft_data.data_type IS '数据类型';
COMMENT ON COLUMN rtss.draft_data.options IS '数据相关的参数项或配置项';
COMMENT ON COLUMN rtss.draft_data.data IS '草稿数据'; COMMENT ON COLUMN rtss.draft_data.data IS '草稿数据';
COMMENT ON COLUMN rtss.draft_data.user_id IS '创建用户id'; COMMENT ON COLUMN rtss.draft_data.user_id IS '创建用户id';
@ -48,6 +54,7 @@ CREATE TABLE
id SERIAL PRIMARY KEY, -- id 自增主键 id SERIAL PRIMARY KEY, -- id 自增主键
name VARCHAR(128) NOT NULL UNIQUE, -- 发布数据名称(数据唯一标识) name VARCHAR(128) NOT NULL UNIQUE, -- 发布数据名称(数据唯一标识)
data_type INT NOT NULL, -- 数据类型 data_type INT NOT NULL, -- 数据类型
options JSONB NULL, -- 数据相关的参数项或配置项
used_version_id INT NULL, -- 使用的版本数据id used_version_id INT NULL, -- 使用的版本数据id
user_id INT NOT NULL, -- 发布/更新用户id user_id INT NOT NULL, -- 发布/更新用户id
is_published BOOLEAN NOT NULL DEFAULT TRUE, -- 是否上架 is_published BOOLEAN NOT NULL DEFAULT TRUE, -- 是否上架
@ -65,6 +72,8 @@ COMMENT ON COLUMN rtss.release_data.name IS '发布数据名称(数据唯一标
COMMENT ON COLUMN rtss.release_data.data_type IS '数据类型'; COMMENT ON COLUMN rtss.release_data.data_type IS '数据类型';
COMMENT ON COLUMN rtss.release_data.options IS '数据相关的参数项或配置项';
COMMENT ON COLUMN rtss.release_data.used_version_id IS '使用的版本数据id'; COMMENT ON COLUMN rtss.release_data.used_version_id IS '使用的版本数据id';
COMMENT ON COLUMN rtss.release_data.user_id IS '发布/更新用户id'; COMMENT ON COLUMN rtss.release_data.user_id IS '发布/更新用户id';

@ -1 +1 @@
Subproject commit 4e447beffa31c94b30bcee57a7cf229dcf01351f Subproject commit a1622410919b58683bc0ec059b4f5de575583c13