添加feature数据库访问功能及实现 添加feature几个api接口及实现 调整数据库模型枚举字段使用枚举取代i32 添加设计文档
This commit is contained in:
parent
176d9b09e4
commit
3798572b0c
@ -5,7 +5,7 @@ use base64::Engine;
|
||||
use chrono::NaiveDateTime;
|
||||
use rtss_db::DraftDataAccessor;
|
||||
use rtss_db::RtssDbAccessor;
|
||||
use rtss_dto::common::DataType;
|
||||
use rtss_dto::common::{DataType, Role};
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::apis::{PageDto, PageQueryDto};
|
||||
@ -15,7 +15,7 @@ use super::common::{DataOptions, IscsDataOptions};
|
||||
use super::release_data::ReleaseDataId;
|
||||
use super::user::UserId;
|
||||
|
||||
use crate::user_auth::{Role, RoleGuard, Token, UserAuthCache};
|
||||
use crate::user_auth::{RoleGuard, Token, UserAuthCache};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct DraftDataQuery;
|
||||
@ -35,7 +35,7 @@ impl DraftDataQuery {
|
||||
) -> async_graphql::Result<PageDto<DraftDataDto>> {
|
||||
let db_accessor = ctx.data::<RtssDbAccessor>()?;
|
||||
let paging_result = db_accessor
|
||||
.query_draft_data(query.into(), paging.into())
|
||||
.paging_query_draft_data(query.into(), paging.into())
|
||||
.await?;
|
||||
Ok(paging_result.into())
|
||||
}
|
||||
@ -55,7 +55,7 @@ impl DraftDataQuery {
|
||||
query.data_type = Some(DataType::Iscs);
|
||||
let db_accessor = ctx.data::<RtssDbAccessor>()?;
|
||||
let paging_result = db_accessor
|
||||
.query_draft_data(query.into(), paging.into())
|
||||
.paging_query_draft_data(query.into(), paging.into())
|
||||
.await?;
|
||||
Ok(paging_result.into())
|
||||
}
|
||||
@ -70,7 +70,7 @@ impl DraftDataQuery {
|
||||
let db_accessor = ctx.data::<RtssDbAccessor>()?;
|
||||
query.data_type = Some(DataType::Iscs);
|
||||
let paging_result = db_accessor
|
||||
.query_draft_data(query.into(), paging.into())
|
||||
.paging_query_draft_data(query.into(), paging.into())
|
||||
.await?;
|
||||
Ok(paging_result.into())
|
||||
}
|
||||
@ -86,6 +86,7 @@ impl DraftDataQuery {
|
||||
async fn draft_data_exist(
|
||||
&self,
|
||||
ctx: &Context<'_>,
|
||||
data_type: DataType,
|
||||
name: String,
|
||||
) -> async_graphql::Result<bool> {
|
||||
let user = ctx
|
||||
@ -94,7 +95,9 @@ impl DraftDataQuery {
|
||||
.await?;
|
||||
let user_id = user.id_i32();
|
||||
let db_accessor = ctx.data::<RtssDbAccessor>()?;
|
||||
let exist = db_accessor.is_draft_data_exist(user_id, &name).await?;
|
||||
let exist = db_accessor
|
||||
.is_draft_data_exist(user_id, &data_type, &name)
|
||||
.await?;
|
||||
Ok(exist)
|
||||
}
|
||||
}
|
||||
@ -328,7 +331,7 @@ impl From<rtss_db::model::DraftDataModel> for DraftDataDto {
|
||||
Self {
|
||||
id: value.id,
|
||||
name: value.name,
|
||||
data_type: DataType::try_from(value.data_type).unwrap(),
|
||||
data_type: value.data_type,
|
||||
options: value.options,
|
||||
data: value
|
||||
.data
|
||||
|
136
crates/rtss_api/src/apis/feature.rs
Normal file
136
crates/rtss_api/src/apis/feature.rs
Normal file
@ -0,0 +1,136 @@
|
||||
use async_graphql::{
|
||||
dataloader::DataLoader, ComplexObject, Context, InputObject, Object, SimpleObject,
|
||||
};
|
||||
use chrono::NaiveDateTime;
|
||||
use rtss_db::{FeatureAccessor, RtssDbAccessor};
|
||||
use rtss_dto::common::FeatureType;
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::{
|
||||
apis::{PageDto, PageQueryDto},
|
||||
loader::RtssDbLoader,
|
||||
};
|
||||
|
||||
use super::user::UserId;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct FeatureQuery;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct FeatureMutation;
|
||||
|
||||
#[Object]
|
||||
impl FeatureQuery {
|
||||
/// 分页查询特征(系统管理)
|
||||
async fn feature_paging(
|
||||
&self,
|
||||
ctx: &Context<'_>,
|
||||
page: PageQueryDto,
|
||||
query: FeatureQueryDto,
|
||||
) -> async_graphql::Result<PageDto<FeatureDto>> {
|
||||
let dba = ctx.data::<RtssDbAccessor>()?;
|
||||
let paging = dba
|
||||
.paging_query_features(page.into(), &query.into())
|
||||
.await?;
|
||||
Ok(paging.into())
|
||||
}
|
||||
|
||||
/// id获取特征
|
||||
async fn feature(&self, ctx: &Context<'_>, id: i32) -> async_graphql::Result<FeatureDto> {
|
||||
let dba = ctx.data::<RtssDbAccessor>()?;
|
||||
let feature = dba.get_feature(id).await?;
|
||||
Ok(feature.into())
|
||||
}
|
||||
|
||||
/// id列表获取特征
|
||||
async fn features(
|
||||
&self,
|
||||
ctx: &Context<'_>,
|
||||
ids: Vec<i32>,
|
||||
) -> async_graphql::Result<Vec<FeatureDto>> {
|
||||
let dba = ctx.data::<RtssDbAccessor>()?;
|
||||
let features = dba.get_features(ids.as_slice()).await?;
|
||||
Ok(features.into_iter().map(|f| f.into()).collect())
|
||||
}
|
||||
}
|
||||
|
||||
#[Object]
|
||||
impl FeatureMutation {
|
||||
/// 上下架特征
|
||||
async fn publish_feature(
|
||||
&self,
|
||||
ctx: &Context<'_>,
|
||||
id: i32,
|
||||
is_published: bool,
|
||||
) -> async_graphql::Result<FeatureDto> {
|
||||
let dba = ctx.data::<RtssDbAccessor>()?;
|
||||
let feature = dba.set_feature_published(id, is_published).await?;
|
||||
Ok(feature.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, InputObject)]
|
||||
pub struct FeatureQueryDto {
|
||||
pub name: Option<String>,
|
||||
pub feature_type: Option<i32>,
|
||||
pub is_published: Option<bool>,
|
||||
}
|
||||
|
||||
impl From<FeatureQueryDto> for rtss_db::FeaturePagingFilter {
|
||||
fn from(value: FeatureQueryDto) -> Self {
|
||||
Self {
|
||||
name: value.name,
|
||||
feature_type: value.feature_type,
|
||||
is_published: value.is_published,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, SimpleObject)]
|
||||
#[graphql(complex)]
|
||||
pub struct FeatureDto {
|
||||
pub id: i32,
|
||||
pub feature_type: FeatureType,
|
||||
pub name: String,
|
||||
pub description: String,
|
||||
pub config: Value,
|
||||
pub is_published: bool,
|
||||
pub creator_id: i32,
|
||||
pub updater_id: i32,
|
||||
pub created_at: NaiveDateTime,
|
||||
pub updated_at: NaiveDateTime,
|
||||
}
|
||||
|
||||
#[ComplexObject]
|
||||
impl FeatureDto {
|
||||
/// 创建用户name
|
||||
async fn creator_name(&self, ctx: &Context<'_>) -> async_graphql::Result<Option<String>> {
|
||||
let loader = ctx.data_unchecked::<DataLoader<RtssDbLoader>>();
|
||||
let name = loader.load_one(UserId::new(self.creator_id)).await?;
|
||||
Ok(name)
|
||||
}
|
||||
|
||||
/// 更新用户name
|
||||
async fn updater_name(&self, ctx: &Context<'_>) -> async_graphql::Result<Option<String>> {
|
||||
let loader = ctx.data_unchecked::<DataLoader<RtssDbLoader>>();
|
||||
let name = loader.load_one(UserId::new(self.updater_id)).await?;
|
||||
Ok(name)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<rtss_db::model::FeatureModel> for FeatureDto {
|
||||
fn from(value: rtss_db::model::FeatureModel) -> Self {
|
||||
Self {
|
||||
id: value.id,
|
||||
feature_type: value.feature_type,
|
||||
name: value.name,
|
||||
description: value.description,
|
||||
config: value.config,
|
||||
is_published: value.is_published,
|
||||
creator_id: value.creator_id,
|
||||
updater_id: value.updater_id,
|
||||
created_at: value.created_at.naive_local(),
|
||||
updated_at: value.updated_at.naive_local(),
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
use async_graphql::{Enum, InputObject, MergedObject, OutputType, SimpleObject};
|
||||
use draft_data::{DraftDataMutation, DraftDataQuery};
|
||||
use feature::{FeatureMutation, FeatureQuery};
|
||||
use release_data::{ReleaseDataMutation, ReleaseDataQuery};
|
||||
|
||||
mod simulation_definition;
|
||||
@ -9,15 +10,21 @@ use user::{UserMutation, UserQuery};
|
||||
|
||||
mod common;
|
||||
mod draft_data;
|
||||
mod feature;
|
||||
mod release_data;
|
||||
mod simulation;
|
||||
mod user;
|
||||
|
||||
#[derive(Default, MergedObject)]
|
||||
pub struct Query(UserQuery, DraftDataQuery, ReleaseDataQuery);
|
||||
pub struct Query(UserQuery, DraftDataQuery, ReleaseDataQuery, FeatureQuery);
|
||||
|
||||
#[derive(Default, MergedObject)]
|
||||
pub struct Mutation(UserMutation, DraftDataMutation, ReleaseDataMutation);
|
||||
pub struct Mutation(
|
||||
UserMutation,
|
||||
DraftDataMutation,
|
||||
ReleaseDataMutation,
|
||||
FeatureMutation,
|
||||
);
|
||||
|
||||
#[derive(Enum, Copy, Clone, Default, Eq, PartialEq, Debug)]
|
||||
#[graphql(remote = "rtss_db::common::SortOrder")]
|
||||
@ -57,6 +64,7 @@ impl From<PageQueryDto> for rtss_db::common::PageQuery {
|
||||
name = "ReleaseIscsDataPageDto",
|
||||
params(release_data::ReleaseIscsDataWithoutVersionDto)
|
||||
))]
|
||||
#[graphql(concrete(name = "FeaturePageDto", params(feature::FeatureDto)))]
|
||||
pub struct PageDto<T: OutputType> {
|
||||
pub total: i64,
|
||||
pub items: Vec<T>,
|
||||
|
@ -8,7 +8,7 @@ use chrono::NaiveDateTime;
|
||||
use rtss_db::model::*;
|
||||
use rtss_db::prelude::*;
|
||||
use rtss_db::{model::ReleaseDataModel, ReleaseDataAccessor, RtssDbAccessor};
|
||||
use rtss_dto::common::DataType;
|
||||
use rtss_dto::common::{DataType, Role};
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::apis::draft_data::DraftDataDto;
|
||||
@ -18,7 +18,7 @@ use super::common::{DataOptions, IscsDataOptions};
|
||||
use super::user::UserId;
|
||||
use super::{PageDto, PageQueryDto};
|
||||
|
||||
use crate::user_auth::{Role, RoleGuard, Token, UserAuthCache};
|
||||
use crate::user_auth::{RoleGuard, Token, UserAuthCache};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ReleaseDataQuery;
|
||||
@ -38,7 +38,7 @@ impl ReleaseDataQuery {
|
||||
) -> async_graphql::Result<PageDto<ReleaseDataDto>> {
|
||||
let db_accessor = ctx.data::<RtssDbAccessor>()?;
|
||||
let paging = db_accessor
|
||||
.query_release_data_list(query.into(), page.into())
|
||||
.paging_query_release_data_list(query.into(), page.into())
|
||||
.await?;
|
||||
Ok(paging.into())
|
||||
}
|
||||
@ -54,7 +54,7 @@ impl ReleaseDataQuery {
|
||||
let db_accessor = ctx.data::<RtssDbAccessor>()?;
|
||||
query.data_type = Some(DataType::Iscs);
|
||||
let paging = db_accessor
|
||||
.query_release_data_list(query.into(), page.into())
|
||||
.paging_query_release_data_list(query.into(), page.into())
|
||||
.await?;
|
||||
Ok(paging.into())
|
||||
}
|
||||
@ -96,7 +96,7 @@ impl ReleaseDataQuery {
|
||||
) -> async_graphql::Result<PageDto<ReleaseDataVersionDto>> {
|
||||
let db_accessor = ctx.data::<RtssDbAccessor>()?;
|
||||
let paging = db_accessor
|
||||
.query_release_data_version_list(data_id, page.into())
|
||||
.paging_query_release_data_version_list(data_id, page.into())
|
||||
.await?;
|
||||
Ok(paging.into())
|
||||
}
|
||||
@ -422,7 +422,7 @@ impl From<ReleaseDataModel> for ReleaseDataDto {
|
||||
Self {
|
||||
id: model.id,
|
||||
name: model.name,
|
||||
data_type: DataType::try_from(model.data_type).unwrap(),
|
||||
data_type: model.data_type,
|
||||
options: model.options,
|
||||
used_version_id: model.used_version_id,
|
||||
user_id: model.user_id,
|
||||
|
@ -6,9 +6,10 @@ use rtss_db::{DbAccessError, RtssDbAccessor, UserAccessor};
|
||||
|
||||
use crate::{
|
||||
loader::RtssDbLoader,
|
||||
user_auth::{build_jwt, Claims, Role, RoleGuard, Token, UserAuthCache, UserInfoDto},
|
||||
user_auth::{build_jwt, Claims, RoleGuard, Token, UserAuthCache, UserInfoDto},
|
||||
UserAuthClient,
|
||||
};
|
||||
use rtss_dto::common::Role;
|
||||
|
||||
use super::{PageDto, PageQueryDto};
|
||||
|
||||
@ -128,7 +129,7 @@ impl From<rtss_db::model::UserModel> for UserDto {
|
||||
name: value.username,
|
||||
mobile: value.mobile,
|
||||
email: value.email,
|
||||
roles: serde_json::from_value(value.roles).unwrap(),
|
||||
roles: value.roles.0,
|
||||
created_at: value.created_at.naive_local(),
|
||||
updated_at: value.updated_at.naive_local(),
|
||||
}
|
||||
|
@ -3,21 +3,16 @@ use std::{
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
use async_graphql::{Enum, Guard};
|
||||
use async_graphql::Guard;
|
||||
use axum::http::HeaderMap;
|
||||
use chrono::{DateTime, Local};
|
||||
use rtss_dto::common::Role;
|
||||
use rtss_log::tracing::error;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
mod jwt_auth;
|
||||
pub use jwt_auth::*;
|
||||
|
||||
#[derive(Eq, PartialEq, Clone, Copy, Debug, Hash, Enum, Serialize, Deserialize)]
|
||||
pub enum Role {
|
||||
Admin,
|
||||
User,
|
||||
}
|
||||
|
||||
pub struct RoleGuard {
|
||||
role: Role,
|
||||
}
|
||||
|
@ -16,15 +16,20 @@ use super::RtssDbAccessor;
|
||||
#[allow(async_fn_in_trait)]
|
||||
pub trait DraftDataAccessor {
|
||||
/// 查询所有草稿数据
|
||||
async fn query_draft_data(
|
||||
async fn paging_query_draft_data(
|
||||
&self,
|
||||
query: DraftDataQuery,
|
||||
page: PageQuery,
|
||||
) -> Result<PageResult<DraftDataModel>, DbAccessError>;
|
||||
/// 根据id查询草稿数据
|
||||
async fn query_draft_data_by_id(&self, id: i32) -> Result<DraftDataModel, DbAccessError>;
|
||||
/// 是否user_id+name的数据已存在
|
||||
async fn is_draft_data_exist(&self, user_id: i32, name: &str) -> Result<bool, DbAccessError>;
|
||||
/// 是否user_id+data_type+name的数据已存在
|
||||
async fn is_draft_data_exist(
|
||||
&self,
|
||||
user_id: i32,
|
||||
data_type: &DataType,
|
||||
name: &str,
|
||||
) -> Result<bool, DbAccessError>;
|
||||
/// 创建草稿数据基本信息
|
||||
async fn create_draft_data(
|
||||
&self,
|
||||
@ -201,7 +206,7 @@ impl CreateDraftData {
|
||||
}
|
||||
|
||||
impl DraftDataAccessor for RtssDbAccessor {
|
||||
async fn query_draft_data(
|
||||
async fn paging_query_draft_data(
|
||||
&self,
|
||||
query: DraftDataQuery,
|
||||
page: PageQuery,
|
||||
@ -251,19 +256,25 @@ impl DraftDataAccessor for RtssDbAccessor {
|
||||
Ok(draft_data)
|
||||
}
|
||||
|
||||
async fn is_draft_data_exist(&self, user_id: i32, name: &str) -> Result<bool, DbAccessError> {
|
||||
async fn is_draft_data_exist(
|
||||
&self,
|
||||
user_id: i32,
|
||||
data_type: &DataType,
|
||||
name: &str,
|
||||
) -> Result<bool, DbAccessError> {
|
||||
let table = DraftDataColumn::Table.name();
|
||||
let filter = format!(
|
||||
"WHERE {} = '{}' AND {} = {}",
|
||||
DraftDataColumn::Name.name(),
|
||||
name,
|
||||
DraftDataColumn::UserId.name(),
|
||||
user_id
|
||||
);
|
||||
let sql = format!("SELECT COUNT(*) FROM {table} {filter}");
|
||||
let user_id_column = DraftDataColumn::UserId.name();
|
||||
let data_type_column = DraftDataColumn::DataType.name();
|
||||
let name_column = DraftDataColumn::Name.name();
|
||||
let sql = format!("SELECT COUNT(*) FROM {table} WHERE {user_id_column} = $1 AND {data_type_column} = $2 AND {name_column} = $3",);
|
||||
// log sql
|
||||
debug!("draft data exist check sql: {}", sql);
|
||||
let count: i64 = sqlx::query_scalar(&sql).fetch_one(&self.pool).await?;
|
||||
let count: i64 = sqlx::query_scalar(&sql)
|
||||
.bind(user_id)
|
||||
.bind(data_type)
|
||||
.bind(name)
|
||||
.fetch_one(&self.pool)
|
||||
.await?;
|
||||
|
||||
Ok(count > 0)
|
||||
}
|
||||
@ -274,7 +285,7 @@ impl DraftDataAccessor for RtssDbAccessor {
|
||||
) -> Result<DraftDataModel, DbAccessError> {
|
||||
// 检查是否已存在
|
||||
let exist = self
|
||||
.is_draft_data_exist(create.user_id, &create.name)
|
||||
.is_draft_data_exist(create.user_id, &create.data_type, &create.name)
|
||||
.await?;
|
||||
if exist {
|
||||
return Err(DbAccessError::RowExist);
|
||||
@ -297,7 +308,7 @@ impl DraftDataAccessor for RtssDbAccessor {
|
||||
// 插入数据
|
||||
let draft_data: DraftDataModel = sqlx::query_as(&sql)
|
||||
.bind(create.name)
|
||||
.bind(create.data_type as i32)
|
||||
.bind(create.data_type)
|
||||
.bind(create.options)
|
||||
.bind(create.user_id)
|
||||
.bind(create.data)
|
||||
@ -411,14 +422,10 @@ impl DraftDataAccessor for RtssDbAccessor {
|
||||
user_id: i32,
|
||||
) -> Result<DraftDataModel, DbAccessError> {
|
||||
let draft_data = self.query_draft_data_by_id(draft_id).await?;
|
||||
let create = CreateDraftData::new(
|
||||
name,
|
||||
DataType::try_from(draft_data.data_type).unwrap(),
|
||||
user_id,
|
||||
)
|
||||
.with_option_options(draft_data.options)
|
||||
.with_data(draft_data.data.as_ref().unwrap())
|
||||
.with_option_default_release_data_id(draft_data.default_release_data_id);
|
||||
let create = CreateDraftData::new(name, draft_data.data_type, user_id)
|
||||
.with_option_options(draft_data.options)
|
||||
.with_data(draft_data.data.as_ref().unwrap())
|
||||
.with_option_default_release_data_id(draft_data.default_release_data_id);
|
||||
|
||||
self.create_draft_data(create).await
|
||||
}
|
||||
@ -429,7 +436,7 @@ mod tests {
|
||||
use crate::{SyncUserInfo, UserAccessor};
|
||||
|
||||
use super::*;
|
||||
use rtss_dto::common::IscsStyle;
|
||||
use rtss_dto::common::{IscsStyle, Role};
|
||||
use rtss_log::tracing::Level;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::{types::chrono::Local, PgPool};
|
||||
@ -439,12 +446,6 @@ mod tests {
|
||||
pub style: IscsStyle,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
enum Role {
|
||||
User,
|
||||
Admin,
|
||||
}
|
||||
|
||||
// You could also do `use foo_crate::MIGRATOR` and just refer to it as `MIGRATOR` here.
|
||||
#[sqlx::test(migrator = "crate::MIGRATOR")]
|
||||
async fn basic_use_test(pool: PgPool) -> Result<(), DbAccessError> {
|
||||
@ -539,7 +540,7 @@ mod tests {
|
||||
|
||||
// 查询确认当前数据已删除
|
||||
let page = accessor
|
||||
.query_draft_data(DraftDataQuery::new(), PageQuery::new(1, 100))
|
||||
.paging_query_draft_data(DraftDataQuery::new(), PageQuery::new(1, 100))
|
||||
.await?;
|
||||
assert_eq!(page.total, 0);
|
||||
|
||||
@ -574,7 +575,7 @@ mod tests {
|
||||
}
|
||||
|
||||
let page = accessor
|
||||
.query_draft_data(
|
||||
.paging_query_draft_data(
|
||||
DraftDataQuery::new()
|
||||
.with_user_id(2)
|
||||
.with_name("test".to_string())
|
||||
@ -585,13 +586,13 @@ mod tests {
|
||||
assert_eq!(page.total, 5);
|
||||
|
||||
let page = accessor
|
||||
.query_draft_data(DraftDataQuery::new(), PageQuery::new(1, 100))
|
||||
.paging_query_draft_data(DraftDataQuery::new(), PageQuery::new(1, 100))
|
||||
.await?;
|
||||
assert_eq!(page.total, 20);
|
||||
|
||||
// 查询共享数据
|
||||
let page = accessor
|
||||
.query_draft_data(
|
||||
.paging_query_draft_data(
|
||||
DraftDataQuery::new().with_is_shared(true),
|
||||
PageQuery::new(1, 10),
|
||||
)
|
||||
@ -600,7 +601,7 @@ mod tests {
|
||||
|
||||
// 查询options
|
||||
let page = accessor
|
||||
.query_draft_data(
|
||||
.paging_query_draft_data(
|
||||
DraftDataQuery::new().with_options(
|
||||
serde_json::to_value(IscsDataOptions {
|
||||
style: IscsStyle::DaShiZhiNeng,
|
||||
|
351
crates/rtss_db/src/db_access/feature.rs
Normal file
351
crates/rtss_db/src/db_access/feature.rs
Normal file
@ -0,0 +1,351 @@
|
||||
use rtss_dto::common::FeatureType;
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::{
|
||||
common::{PageQuery, PageResult, TableColumn},
|
||||
model::{FeatureColumn, FeatureModel},
|
||||
DbAccessError,
|
||||
};
|
||||
|
||||
use super::RtssDbAccessor;
|
||||
|
||||
/// 功能特性管理
|
||||
#[allow(async_fn_in_trait)]
|
||||
pub trait FeatureAccessor {
|
||||
/// 创建功能特性
|
||||
async fn create_feature(&self, feature: &CreateFeature) -> Result<FeatureModel, DbAccessError>;
|
||||
/// 更新功能特性
|
||||
async fn update_feature(&self, feature: &UpdateFeature) -> Result<FeatureModel, DbAccessError>;
|
||||
/// 上下架功能特性
|
||||
async fn set_feature_published(
|
||||
&self,
|
||||
feature_id: i32,
|
||||
is_published: bool,
|
||||
) -> Result<FeatureModel, DbAccessError>;
|
||||
/// 分页查询功能特性
|
||||
async fn paging_query_features(
|
||||
&self,
|
||||
page: PageQuery,
|
||||
filter: &FeaturePagingFilter,
|
||||
) -> Result<PageResult<FeatureModel>, DbAccessError>;
|
||||
/// 获取id对应的功能特性
|
||||
async fn get_feature(&self, id: i32) -> Result<FeatureModel, DbAccessError>;
|
||||
/// 根据id列表获取功能特性基本信息
|
||||
async fn get_features(&self, ids: &[i32]) -> Result<Vec<FeatureModel>, DbAccessError>;
|
||||
}
|
||||
|
||||
impl FeatureAccessor for RtssDbAccessor {
|
||||
async fn create_feature(&self, feature: &CreateFeature) -> Result<FeatureModel, DbAccessError> {
|
||||
let table = FeatureColumn::Table.name();
|
||||
let feature_type_column = FeatureColumn::FeatureType.name();
|
||||
let name_column = FeatureColumn::Name.name();
|
||||
let description_column = FeatureColumn::Description.name();
|
||||
let config_column = FeatureColumn::Config.name();
|
||||
let creator_id_column = FeatureColumn::CreatorId.name();
|
||||
let updater_id_column = FeatureColumn::UpdaterId.name();
|
||||
let sql = format!(
|
||||
"INSERT INTO {table} ({feature_type_column}, {name_column}, {description_column}, {config_column}, {creator_id_column}, {updater_id_column}) VALUES ($1, $2, $3, $4, $5, $6) RETURNING *",
|
||||
);
|
||||
let row = sqlx::query_as::<_, FeatureModel>(&sql)
|
||||
.bind(feature.feature_type)
|
||||
.bind(&feature.name)
|
||||
.bind(&feature.description)
|
||||
.bind(&feature.config)
|
||||
.bind(feature.creator_id)
|
||||
.bind(feature.creator_id)
|
||||
.fetch_one(&self.pool)
|
||||
.await?;
|
||||
Ok(row)
|
||||
}
|
||||
|
||||
async fn update_feature(&self, feature: &UpdateFeature) -> Result<FeatureModel, DbAccessError> {
|
||||
let table = FeatureColumn::Table.name();
|
||||
let name_column = FeatureColumn::Name.name();
|
||||
let description_column = FeatureColumn::Description.name();
|
||||
let config_column = FeatureColumn::Config.name();
|
||||
let updater_id_column = FeatureColumn::UpdaterId.name();
|
||||
let update_at_column = FeatureColumn::UpdatedAt.name();
|
||||
let sql = format!(
|
||||
"UPDATE {table} SET {name_column} = $1, {description_column} = $2, {config_column} = $3, {updater_id_column} = $4, {update_at_column} = 'now()' WHERE id = $5 RETURNING *",
|
||||
);
|
||||
let row = sqlx::query_as::<_, FeatureModel>(&sql)
|
||||
.bind(&feature.name)
|
||||
.bind(&feature.description)
|
||||
.bind(&feature.config)
|
||||
.bind(feature.updater_id)
|
||||
.bind(feature.id)
|
||||
.fetch_one(&self.pool)
|
||||
.await?;
|
||||
Ok(row)
|
||||
}
|
||||
|
||||
async fn set_feature_published(
|
||||
&self,
|
||||
feature_id: i32,
|
||||
is_published: bool,
|
||||
) -> Result<FeatureModel, DbAccessError> {
|
||||
let table = FeatureColumn::Table.name();
|
||||
let is_published_column = FeatureColumn::IsPublished.name();
|
||||
let update_at_column = FeatureColumn::UpdatedAt.name();
|
||||
let sql = format!(
|
||||
"UPDATE {table} SET {is_published_column} = $1, {update_at_column} = 'now()' WHERE id = $2 RETURNING *",
|
||||
);
|
||||
let row = sqlx::query_as::<_, FeatureModel>(&sql)
|
||||
.bind(is_published)
|
||||
.bind(feature_id)
|
||||
.fetch_one(&self.pool)
|
||||
.await?;
|
||||
Ok(row)
|
||||
}
|
||||
|
||||
async fn paging_query_features(
|
||||
&self,
|
||||
page: PageQuery,
|
||||
filter: &FeaturePagingFilter,
|
||||
) -> Result<PageResult<FeatureModel>, DbAccessError> {
|
||||
let table = FeatureColumn::Table.name();
|
||||
let where_clause = filter.to_where_clause();
|
||||
let total =
|
||||
sqlx::query_scalar::<_, i64>(&format!("SELECT COUNT(*) FROM {table} {where_clause}",))
|
||||
.fetch_one(&self.pool)
|
||||
.await?;
|
||||
if total == 0 {
|
||||
return Ok(PageResult::new(total, vec![]));
|
||||
}
|
||||
let update_at_column = FeatureColumn::UpdatedAt.name();
|
||||
let select_columns = [
|
||||
FeatureColumn::Id.name(),
|
||||
FeatureColumn::FeatureType.name(),
|
||||
FeatureColumn::Name.name(),
|
||||
FeatureColumn::Description.name(),
|
||||
FeatureColumn::IsPublished.name(),
|
||||
FeatureColumn::CreatorId.name(),
|
||||
FeatureColumn::UpdaterId.name(),
|
||||
FeatureColumn::CreatedAt.name(),
|
||||
FeatureColumn::UpdatedAt.name(),
|
||||
];
|
||||
let select_columns = select_columns.join(", ");
|
||||
let limit_clause = page.to_limit_clause();
|
||||
let sql = format!(
|
||||
"SELECT {select_columns} FROM {table} {where_clause} ORDER BY {update_at_column} DESC {limit_clause}",
|
||||
);
|
||||
let rows = sqlx::query_as::<_, FeatureModel>(&sql)
|
||||
.fetch_all(&self.pool)
|
||||
.await?;
|
||||
Ok(PageResult::new(total, rows))
|
||||
}
|
||||
|
||||
async fn get_feature(&self, id: i32) -> Result<FeatureModel, DbAccessError> {
|
||||
let table = FeatureColumn::Table.name();
|
||||
let id_column = FeatureColumn::Id.name();
|
||||
let sql = format!("SELECT * FROM {table} WHERE {id_column} = $1",);
|
||||
let row = sqlx::query_as::<_, FeatureModel>(&sql)
|
||||
.bind(id)
|
||||
.fetch_one(&self.pool)
|
||||
.await?;
|
||||
Ok(row)
|
||||
}
|
||||
|
||||
async fn get_features(&self, ids: &[i32]) -> Result<Vec<FeatureModel>, DbAccessError> {
|
||||
let table = FeatureColumn::Table.name();
|
||||
let id_column = FeatureColumn::Id.name();
|
||||
let select_columns = [
|
||||
FeatureColumn::Id.name(),
|
||||
FeatureColumn::FeatureType.name(),
|
||||
FeatureColumn::Name.name(),
|
||||
FeatureColumn::Description.name(),
|
||||
FeatureColumn::IsPublished.name(),
|
||||
FeatureColumn::CreatorId.name(),
|
||||
FeatureColumn::UpdaterId.name(),
|
||||
FeatureColumn::CreatedAt.name(),
|
||||
FeatureColumn::UpdatedAt.name(),
|
||||
];
|
||||
let select_columns = select_columns.join(", ");
|
||||
let sql = format!("SELECT {select_columns} FROM {table} WHERE {id_column} = ANY($1)",);
|
||||
let rows = sqlx::query_as::<_, FeatureModel>(&sql)
|
||||
.bind(ids)
|
||||
.fetch_all(&self.pool)
|
||||
.await?;
|
||||
Ok(rows)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CreateFeature {
|
||||
pub feature_type: FeatureType,
|
||||
pub name: String,
|
||||
pub description: String,
|
||||
pub config: Value,
|
||||
pub creator_id: i32,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct UpdateFeature {
|
||||
pub id: i32,
|
||||
pub name: String,
|
||||
pub description: String,
|
||||
pub config: Value,
|
||||
pub updater_id: i32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct FeaturePagingFilter {
|
||||
pub name: Option<String>,
|
||||
pub feature_type: Option<i32>,
|
||||
pub is_published: Option<bool>,
|
||||
}
|
||||
|
||||
impl FeaturePagingFilter {
|
||||
fn to_where_clause(&self) -> String {
|
||||
let mut clauses = vec![];
|
||||
if let Some(name) = &self.name {
|
||||
clauses.push(format!("name like '%{}%'", name));
|
||||
}
|
||||
if let Some(feature_type) = self.feature_type {
|
||||
clauses.push(format!("feature_type = {}", feature_type));
|
||||
}
|
||||
if let Some(is_published) = self.is_published {
|
||||
clauses.push(format!("is_published = {}", is_published));
|
||||
}
|
||||
if clauses.is_empty() {
|
||||
"".to_string()
|
||||
} else {
|
||||
format!("WHERE {}", clauses.join(" AND "))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use rtss_dto::common::Role;
|
||||
use rtss_log::tracing::Level;
|
||||
use sqlx::{types::chrono::Local, PgPool};
|
||||
|
||||
use crate::{SyncUserInfo, UserAccessor};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[sqlx::test(migrator = "crate::MIGRATOR")]
|
||||
async fn test_basic_use(pool: PgPool) -> Result<(), DbAccessError> {
|
||||
rtss_log::Logging::default().with_level(Level::DEBUG).init();
|
||||
let accessor = crate::db_access::RtssDbAccessor::new(pool);
|
||||
// 同步10个用户
|
||||
let mut users = vec![];
|
||||
for i in 0..10 {
|
||||
let user = SyncUserInfo {
|
||||
id: i + 1,
|
||||
name: format!("user{}", i + 1),
|
||||
password: "password".to_string(),
|
||||
roles: serde_json::to_value(&vec![Role::User]).unwrap(),
|
||||
email: None,
|
||||
mobile: None,
|
||||
created_at: Local::now(),
|
||||
updated_at: None,
|
||||
};
|
||||
users.push(user);
|
||||
}
|
||||
accessor.sync_user(users.as_slice()).await?;
|
||||
|
||||
// 创建测试
|
||||
let creator_id = 1;
|
||||
let create_config = Value::String("create".to_string());
|
||||
let feature = CreateFeature {
|
||||
feature_type: FeatureType::Ur,
|
||||
name: "test".to_string(),
|
||||
description: "test".to_string(),
|
||||
config: create_config.clone(),
|
||||
creator_id,
|
||||
};
|
||||
let feature = accessor.create_feature(&feature).await?;
|
||||
println!("create feature: {:?}", feature);
|
||||
assert_eq!(feature.creator_id, creator_id);
|
||||
assert_eq!(feature.updater_id, creator_id);
|
||||
assert_eq!(feature.is_published, true);
|
||||
assert_eq!(feature.config, create_config);
|
||||
let feature_id = feature.id;
|
||||
// 获取功能特性测试
|
||||
let feature = accessor.get_feature(feature_id).await?;
|
||||
println!("get feature: {:?}", feature);
|
||||
assert_eq!(feature.id, feature_id);
|
||||
assert_eq!(feature.creator_id, creator_id);
|
||||
assert_eq!(feature.updater_id, creator_id);
|
||||
assert_eq!(feature.is_published, true);
|
||||
assert_eq!(feature.config, create_config);
|
||||
// 更新测试
|
||||
let updater_id = 2;
|
||||
let config = Value::String("config".to_string());
|
||||
let feature = UpdateFeature {
|
||||
id: feature_id,
|
||||
name: "test update".to_string(),
|
||||
description: "test update".to_string(),
|
||||
config: config.clone(),
|
||||
updater_id: updater_id,
|
||||
};
|
||||
let feature = accessor.update_feature(&feature).await?;
|
||||
println!("update feature: {:?}", feature);
|
||||
assert_eq!(feature.updater_id, updater_id);
|
||||
assert_eq!(feature.config, config);
|
||||
assert!(feature.updated_at > feature.created_at);
|
||||
// 上下架测试
|
||||
let feature = accessor.set_feature_published(feature_id, false).await?;
|
||||
println!("set feature published: {:?}", feature);
|
||||
assert_eq!(feature.is_published, false);
|
||||
// 分页查询测试
|
||||
// 创建10个功能特性,5个发布的,5个未发布的
|
||||
for i in 0..10 {
|
||||
let feature = CreateFeature {
|
||||
feature_type: FeatureType::Ur,
|
||||
name: format!("test{}", i),
|
||||
description: format!("test{}", i),
|
||||
config: Value::Null,
|
||||
creator_id,
|
||||
};
|
||||
let feature = accessor.create_feature(&feature).await?;
|
||||
if i < 5 {
|
||||
accessor.set_feature_published(feature.id, false).await?;
|
||||
}
|
||||
}
|
||||
// 无过滤条件分页查询测试
|
||||
let filter = FeaturePagingFilter::default();
|
||||
let item_per_page = 10;
|
||||
let page_result = accessor
|
||||
.paging_query_features(PageQuery::new(1, item_per_page), &filter)
|
||||
.await?;
|
||||
assert_eq!(page_result.total, 11);
|
||||
assert_eq!(page_result.data.len(), item_per_page as usize);
|
||||
// 上架过滤条件分页查询测试
|
||||
let filter = FeaturePagingFilter {
|
||||
is_published: Some(true),
|
||||
..Default::default()
|
||||
};
|
||||
let page_result = accessor
|
||||
.paging_query_features(PageQuery::new(1, item_per_page), &filter)
|
||||
.await?;
|
||||
println!("published features: {:?}", page_result);
|
||||
assert_eq!(page_result.total, 5);
|
||||
assert_eq!(page_result.data.len(), 5);
|
||||
// 名称过滤条件分页查询测试
|
||||
let filter = FeaturePagingFilter {
|
||||
name: Some("test".to_string()),
|
||||
..Default::default()
|
||||
};
|
||||
let page_result = accessor
|
||||
.paging_query_features(PageQuery::new(1, item_per_page), &filter)
|
||||
.await?;
|
||||
println!("name filter features: {:?}", page_result);
|
||||
assert_eq!(page_result.total, 11);
|
||||
assert_eq!(page_result.data.len(), item_per_page as usize);
|
||||
|
||||
// 根据id列表获取功能特性测试
|
||||
let ids = page_result
|
||||
.data
|
||||
.iter()
|
||||
.map(|f| f.id)
|
||||
.skip(5)
|
||||
.collect::<Vec<_>>();
|
||||
let features = accessor.get_features(&ids).await?;
|
||||
println!("get features: {:?}", features);
|
||||
assert_eq!(features.len(), ids.len());
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -4,6 +4,8 @@ mod release_data;
|
||||
pub use release_data::*;
|
||||
mod user;
|
||||
pub use user::*;
|
||||
mod feature;
|
||||
pub use feature::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RtssDbAccessor {
|
||||
|
@ -31,7 +31,7 @@ pub trait ReleaseDataAccessor {
|
||||
user_id: Option<i32>,
|
||||
) -> Result<(ReleaseDataModel, ReleaseDataVersionModel), DbAccessError>;
|
||||
/// 分页查询发布数据列表
|
||||
async fn query_release_data_list(
|
||||
async fn paging_query_release_data_list(
|
||||
&self,
|
||||
query: ReleaseDataQuery,
|
||||
page: PageQuery,
|
||||
@ -53,7 +53,7 @@ pub trait ReleaseDataAccessor {
|
||||
release_ids: &[i32],
|
||||
) -> Result<Vec<(i32, String)>, DbAccessError>;
|
||||
/// 查询发布数据所有版本信息
|
||||
async fn query_release_data_version_list(
|
||||
async fn paging_query_release_data_version_list(
|
||||
&self,
|
||||
release_id: i32,
|
||||
page: PageQuery,
|
||||
@ -252,7 +252,7 @@ impl ReleaseDataAccessor for RtssDbAccessor {
|
||||
}
|
||||
// 判断发布数据名称是否已存在
|
||||
if self
|
||||
.is_release_data_name_exist(DataType::try_from(draft.data_type).unwrap(), name)
|
||||
.is_release_data_name_exist(draft.data_type, name)
|
||||
.await?
|
||||
{
|
||||
return Err(DbAccessError::DataError("发布数据名称已存在".to_string()));
|
||||
@ -364,7 +364,7 @@ impl ReleaseDataAccessor for RtssDbAccessor {
|
||||
Ok((rd, rdv))
|
||||
}
|
||||
|
||||
async fn query_release_data_list(
|
||||
async fn paging_query_release_data_list(
|
||||
&self,
|
||||
query: ReleaseDataQuery,
|
||||
page: PageQuery,
|
||||
@ -449,7 +449,7 @@ impl ReleaseDataAccessor for RtssDbAccessor {
|
||||
Ok(rd)
|
||||
}
|
||||
|
||||
async fn query_release_data_version_list(
|
||||
async fn paging_query_release_data_version_list(
|
||||
&self,
|
||||
release_id: i32,
|
||||
page: PageQuery,
|
||||
@ -633,7 +633,7 @@ impl ReleaseDataAccessor for RtssDbAccessor {
|
||||
// 创建草稿数据
|
||||
let draft = self
|
||||
.create_draft_data(
|
||||
CreateDraftData::new(&name, DataType::try_from(rd.data_type).unwrap(), user_id)
|
||||
CreateDraftData::new(&name, rd.data_type, user_id)
|
||||
.with_option_options(rdv.options)
|
||||
.with_data(&rdv.data)
|
||||
.with_default_release_data_id(rd.id),
|
||||
@ -649,7 +649,7 @@ mod tests {
|
||||
|
||||
use super::*;
|
||||
use chrono::Local;
|
||||
use rtss_dto::common::IscsStyle;
|
||||
use rtss_dto::common::{IscsStyle, Role};
|
||||
use rtss_log::tracing::Level;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::PgPool;
|
||||
@ -692,12 +692,6 @@ mod tests {
|
||||
pub style: IscsStyle,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
enum Role {
|
||||
User,
|
||||
Admin,
|
||||
}
|
||||
|
||||
// You could also do `use foo_crate::MIGRATOR` and just refer to it as `MIGRATOR` here.
|
||||
#[sqlx::test(migrator = "crate::MIGRATOR")]
|
||||
async fn test_basic_use(pool: PgPool) -> Result<(), DbAccessError> {
|
||||
@ -818,7 +812,7 @@ mod tests {
|
||||
|
||||
// 查询发布数据所有版本
|
||||
let versions = accessor
|
||||
.query_release_data_version_list(
|
||||
.paging_query_release_data_version_list(
|
||||
release_data.id,
|
||||
PageQuery {
|
||||
page: 1,
|
||||
@ -871,27 +865,27 @@ mod tests {
|
||||
// 分页查询发布数据,无条件
|
||||
let query = ReleaseDataQuery::new();
|
||||
let page = PageQuery::new(1, 100);
|
||||
let page_result = accessor.query_release_data_list(query, page).await?;
|
||||
let page_result = accessor.paging_query_release_data_list(query, page).await?;
|
||||
assert_eq!(page_result.total, 9);
|
||||
// 分页查询发布数据,按是否上架过滤
|
||||
let query = ReleaseDataQuery::new().with_is_published(true);
|
||||
let page = PageQuery::new(1, 100);
|
||||
let page_result = accessor.query_release_data_list(query, page).await?;
|
||||
let page_result = accessor.paging_query_release_data_list(query, page).await?;
|
||||
assert_eq!(page_result.total, 8);
|
||||
// 分页查询发布数据,按名称过滤
|
||||
let query = ReleaseDataQuery::new().with_name("test_2".to_string());
|
||||
let page = PageQuery::new(1, 10);
|
||||
let page_result = accessor.query_release_data_list(query, page).await?;
|
||||
let page_result = accessor.paging_query_release_data_list(query, page).await?;
|
||||
assert_eq!(page_result.total, 2);
|
||||
// 分页查询发布数据,按用户id过滤
|
||||
let query = ReleaseDataQuery::new().with_user_id(2);
|
||||
let page = PageQuery::new(1, 10);
|
||||
let page_result = accessor.query_release_data_list(query, page).await?;
|
||||
let page_result = accessor.paging_query_release_data_list(query, page).await?;
|
||||
assert_eq!(page_result.total, 2);
|
||||
// 分页查询发布数据,按数据类型过滤
|
||||
let query = ReleaseDataQuery::new().with_data_type(rtss_dto::common::DataType::Em);
|
||||
let page = PageQuery::new(1, 10);
|
||||
let page_result = accessor.query_release_data_list(query, page).await?;
|
||||
let page_result = accessor.paging_query_release_data_list(query, page).await?;
|
||||
assert_eq!(page_result.total, 8);
|
||||
println!("分页查询发布数据测试成功");
|
||||
|
||||
|
@ -1,5 +1,9 @@
|
||||
use rtss_dto::common::{DataType, FeatureType, Role};
|
||||
use serde_json::Value;
|
||||
use sqlx::types::chrono::{DateTime, Local};
|
||||
use sqlx::types::{
|
||||
chrono::{DateTime, Local},
|
||||
Json,
|
||||
};
|
||||
|
||||
use crate::common::TableColumn;
|
||||
|
||||
@ -25,7 +29,7 @@ pub struct UserModel {
|
||||
pub email: Option<String>,
|
||||
#[sqlx(default)]
|
||||
pub mobile: Option<String>,
|
||||
pub roles: Value,
|
||||
pub roles: Json<Vec<Role>>,
|
||||
pub created_at: DateTime<Local>,
|
||||
pub updated_at: DateTime<Local>,
|
||||
}
|
||||
@ -50,7 +54,7 @@ pub enum DraftDataColumn {
|
||||
pub struct DraftDataModel {
|
||||
pub id: i32,
|
||||
pub name: String,
|
||||
pub data_type: i32,
|
||||
pub data_type: DataType,
|
||||
#[sqlx(default)]
|
||||
pub options: Option<Value>,
|
||||
#[sqlx(default)]
|
||||
@ -84,7 +88,7 @@ pub enum ReleaseDataColumn {
|
||||
pub struct ReleaseDataModel {
|
||||
pub id: i32,
|
||||
pub name: String,
|
||||
pub data_type: i32,
|
||||
pub data_type: DataType,
|
||||
/// 从发布版本复制的选项,主要用于查询
|
||||
#[sqlx(default)]
|
||||
pub options: Option<Value>,
|
||||
@ -130,6 +134,7 @@ pub(crate) enum FeatureColumn {
|
||||
FeatureType,
|
||||
Name,
|
||||
Description,
|
||||
Config,
|
||||
IsPublished,
|
||||
CreatorId,
|
||||
UpdaterId,
|
||||
@ -140,9 +145,11 @@ pub(crate) enum FeatureColumn {
|
||||
#[derive(Debug, sqlx::FromRow)]
|
||||
pub struct FeatureModel {
|
||||
pub id: i32,
|
||||
pub feature_type: i32,
|
||||
pub feature_type: FeatureType,
|
||||
pub name: String,
|
||||
pub description: String,
|
||||
#[sqlx(default)]
|
||||
pub config: Value,
|
||||
pub is_published: bool,
|
||||
pub creator_id: i32,
|
||||
pub updater_id: i32,
|
||||
@ -150,82 +157,25 @@ pub struct FeatureModel {
|
||||
pub updated_at: DateTime<Local>,
|
||||
}
|
||||
|
||||
/// 数据库表 rtss.feature_release_data 列映射
|
||||
/// 数据库表 rtss.user_config 列映射
|
||||
#[derive(Debug)]
|
||||
#[allow(dead_code)]
|
||||
pub(crate) enum FeatureReleaseDataColumn {
|
||||
Table,
|
||||
FeatureId,
|
||||
ReleaseDataId,
|
||||
}
|
||||
|
||||
#[derive(Debug, sqlx::FromRow)]
|
||||
pub struct FeatureReleaseDataModel {
|
||||
pub feature_id: i32,
|
||||
pub release_data_id: i32,
|
||||
}
|
||||
|
||||
/// 数据库表 rtss.feature_group 列映射
|
||||
#[derive(Debug)]
|
||||
#[allow(dead_code)]
|
||||
pub(crate) enum FeatureGroupColumn {
|
||||
Table,
|
||||
Id,
|
||||
Name,
|
||||
Description,
|
||||
IsPublished,
|
||||
CreatorId,
|
||||
UpdaterId,
|
||||
CreatedAt,
|
||||
UpdatedAt,
|
||||
}
|
||||
|
||||
#[derive(Debug, sqlx::FromRow)]
|
||||
pub struct FeatureGroupModel {
|
||||
pub id: i32,
|
||||
pub name: String,
|
||||
pub description: String,
|
||||
pub is_published: bool,
|
||||
pub creator_id: i32,
|
||||
pub updater_id: i32,
|
||||
pub created_at: DateTime<Local>,
|
||||
pub updated_at: DateTime<Local>,
|
||||
}
|
||||
|
||||
/// 数据库表 rtss.feature_group_feature 列映射
|
||||
#[derive(Debug)]
|
||||
#[allow(dead_code)]
|
||||
pub(crate) enum FeatureGroupFeatureColumn {
|
||||
Table,
|
||||
FeatureGroupId,
|
||||
FeatureId,
|
||||
}
|
||||
|
||||
#[derive(Debug, sqlx::FromRow)]
|
||||
pub struct FeatureGroupFeatureModel {
|
||||
pub feature_group_id: i32,
|
||||
pub feature_id: i32,
|
||||
}
|
||||
|
||||
/// 数据库表 rtss.feature_config 列映射
|
||||
#[derive(Debug)]
|
||||
#[allow(dead_code)]
|
||||
pub(crate) enum FeatureConfigColumn {
|
||||
pub(crate) enum UserConfigColumn {
|
||||
Table,
|
||||
Id,
|
||||
UserId,
|
||||
FeatureId,
|
||||
ConfigType,
|
||||
Config,
|
||||
CreatedAt,
|
||||
UpdatedAt,
|
||||
}
|
||||
|
||||
#[derive(Debug, sqlx::FromRow)]
|
||||
pub struct FeatureConfigModel {
|
||||
pub struct UserConfigModel {
|
||||
pub id: i32,
|
||||
pub user_id: i32,
|
||||
pub feature_id: i32,
|
||||
pub config: Vec<u8>,
|
||||
pub config_type: i32,
|
||||
pub config: Value,
|
||||
pub created_at: DateTime<Local>,
|
||||
pub updated_at: DateTime<Local>,
|
||||
}
|
||||
@ -304,6 +254,7 @@ impl TableColumn for FeatureColumn {
|
||||
FeatureColumn::FeatureType => "feature_type",
|
||||
FeatureColumn::Name => "name",
|
||||
FeatureColumn::Description => "description",
|
||||
FeatureColumn::Config => "config",
|
||||
FeatureColumn::IsPublished => "is_published",
|
||||
FeatureColumn::CreatorId => "creator_id",
|
||||
FeatureColumn::UpdaterId => "updater_id",
|
||||
@ -313,52 +264,16 @@ impl TableColumn for FeatureColumn {
|
||||
}
|
||||
}
|
||||
|
||||
impl TableColumn for FeatureReleaseDataColumn {
|
||||
impl TableColumn for UserConfigColumn {
|
||||
fn name(&self) -> &str {
|
||||
match self {
|
||||
FeatureReleaseDataColumn::Table => "rtss.feature_release_data",
|
||||
FeatureReleaseDataColumn::FeatureId => "feature_id",
|
||||
FeatureReleaseDataColumn::ReleaseDataId => "release_data_id",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TableColumn for FeatureGroupColumn {
|
||||
fn name(&self) -> &str {
|
||||
match self {
|
||||
FeatureGroupColumn::Table => "rtss.feature_group",
|
||||
FeatureGroupColumn::Id => "id",
|
||||
FeatureGroupColumn::Name => "name",
|
||||
FeatureGroupColumn::Description => "description",
|
||||
FeatureGroupColumn::IsPublished => "is_published",
|
||||
FeatureGroupColumn::CreatorId => "creator_id",
|
||||
FeatureGroupColumn::UpdaterId => "updater_id",
|
||||
FeatureGroupColumn::CreatedAt => "created_at",
|
||||
FeatureGroupColumn::UpdatedAt => "updated_at",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TableColumn for FeatureGroupFeatureColumn {
|
||||
fn name(&self) -> &str {
|
||||
match self {
|
||||
FeatureGroupFeatureColumn::Table => "rtss.feature_group_feature",
|
||||
FeatureGroupFeatureColumn::FeatureGroupId => "feature_group_id",
|
||||
FeatureGroupFeatureColumn::FeatureId => "feature_id",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TableColumn for FeatureConfigColumn {
|
||||
fn name(&self) -> &str {
|
||||
match self {
|
||||
FeatureConfigColumn::Table => "rtss.feature_config",
|
||||
FeatureConfigColumn::Id => "id",
|
||||
FeatureConfigColumn::UserId => "user_id",
|
||||
FeatureConfigColumn::FeatureId => "feature_id",
|
||||
FeatureConfigColumn::Config => "config",
|
||||
FeatureConfigColumn::CreatedAt => "created_at",
|
||||
FeatureConfigColumn::UpdatedAt => "updated_at",
|
||||
UserConfigColumn::Table => "rtss.user_config",
|
||||
UserConfigColumn::Id => "id",
|
||||
UserConfigColumn::UserId => "user_id",
|
||||
UserConfigColumn::ConfigType => "config_type",
|
||||
UserConfigColumn::Config => "config",
|
||||
UserConfigColumn::CreatedAt => "created_at",
|
||||
UserConfigColumn::UpdatedAt => "updated_at",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,14 +21,22 @@ fn main() {
|
||||
}
|
||||
Config::new()
|
||||
.out_dir("src/pb")
|
||||
.type_attribute("common.DataType", "#[derive(sqlx::Type)]")
|
||||
.type_attribute("common.DataType", "#[derive(async_graphql::Enum)]")
|
||||
.type_attribute("common.IscsStyle", "#[derive(async_graphql::Enum)]")
|
||||
.type_attribute(
|
||||
"common.Role",
|
||||
"#[derive(sqlx::Type, async_graphql::Enum, serde::Serialize, serde::Deserialize)]",
|
||||
)
|
||||
.type_attribute(
|
||||
"common.DataType",
|
||||
"#[derive(sqlx::Type, async_graphql::Enum, serde::Serialize, serde::Deserialize)]",
|
||||
)
|
||||
.type_attribute(
|
||||
"common.IscsStyle",
|
||||
"#[derive(serde::Serialize, serde::Deserialize)]",
|
||||
"#[derive(async_graphql::Enum, serde::Serialize, serde::Deserialize)]",
|
||||
)
|
||||
.type_attribute(
|
||||
"common.FeatureType",
|
||||
"#[derive(sqlx::Type, async_graphql::Enum, serde::Serialize, serde::Deserialize)]",
|
||||
)
|
||||
.type_attribute("common.FeatureType", "#[derive(sqlx::Type)]")
|
||||
.compile_protos(
|
||||
&[
|
||||
"../../rtss-proto-msg/src/em_data.proto",
|
||||
|
@ -88,10 +88,59 @@ pub struct CommonInfo {
|
||||
#[prost(message, repeated, tag = "4")]
|
||||
pub child_transforms: ::prost::alloc::vec::Vec<ChildTransform>,
|
||||
}
|
||||
/// 用户角色
|
||||
#[derive(
|
||||
sqlx::Type,
|
||||
async_graphql::Enum,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
Clone,
|
||||
Copy,
|
||||
Debug,
|
||||
PartialEq,
|
||||
Eq,
|
||||
Hash,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
::prost::Enumeration,
|
||||
)]
|
||||
#[repr(i32)]
|
||||
pub enum Role {
|
||||
/// 未知
|
||||
Unknown = 0,
|
||||
/// 系统管理员
|
||||
Admin = 1,
|
||||
/// 普通用户
|
||||
User = 2,
|
||||
}
|
||||
impl Role {
|
||||
/// 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 {
|
||||
Role::Unknown => "Role_Unknown",
|
||||
Role::Admin => "Role_Admin",
|
||||
Role::User => "Role_User",
|
||||
}
|
||||
}
|
||||
/// Creates an enum from field names used in the ProtoBuf definition.
|
||||
pub fn from_str_name(value: &str) -> ::core::option::Option<Self> {
|
||||
match value {
|
||||
"Role_Unknown" => Some(Self::Unknown),
|
||||
"Role_Admin" => Some(Self::Admin),
|
||||
"Role_User" => Some(Self::User),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
/// 数据类型
|
||||
#[derive(
|
||||
sqlx::Type,
|
||||
async_graphql::Enum,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
Clone,
|
||||
Copy,
|
||||
Debug,
|
||||
@ -108,10 +157,6 @@ pub enum DataType {
|
||||
Unknown = 0,
|
||||
/// 电子地图数据
|
||||
Em = 1,
|
||||
/// IBP数据
|
||||
Ibp = 2,
|
||||
/// PSL数据
|
||||
Psl = 3,
|
||||
/// ISCS数据
|
||||
Iscs = 4,
|
||||
}
|
||||
@ -124,8 +169,6 @@ impl DataType {
|
||||
match self {
|
||||
DataType::Unknown => "DataType_Unknown",
|
||||
DataType::Em => "DataType_Em",
|
||||
DataType::Ibp => "DataType_Ibp",
|
||||
DataType::Psl => "DataType_Psl",
|
||||
DataType::Iscs => "DataType_Iscs",
|
||||
}
|
||||
}
|
||||
@ -134,8 +177,6 @@ impl DataType {
|
||||
match value {
|
||||
"DataType_Unknown" => Some(Self::Unknown),
|
||||
"DataType_Em" => Some(Self::Em),
|
||||
"DataType_Ibp" => Some(Self::Ibp),
|
||||
"DataType_Psl" => Some(Self::Psl),
|
||||
"DataType_Iscs" => Some(Self::Iscs),
|
||||
_ => None,
|
||||
}
|
||||
@ -170,28 +211,40 @@ impl IscsStyle {
|
||||
pub fn as_str_name(&self) -> &'static str {
|
||||
match self {
|
||||
IscsStyle::Unknown => "IscsStyle_Unknown",
|
||||
IscsStyle::DaShiZhiNeng => "DaShiZhiNeng",
|
||||
IscsStyle::DaShiZhiNeng => "IscsStyle_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),
|
||||
"IscsStyle_DaShiZhiNeng" => Some(Self::DaShiZhiNeng),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
/// 功能特性类型
|
||||
#[derive(
|
||||
sqlx::Type, Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration,
|
||||
sqlx::Type,
|
||||
async_graphql::Enum,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
Clone,
|
||||
Copy,
|
||||
Debug,
|
||||
PartialEq,
|
||||
Eq,
|
||||
Hash,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
::prost::Enumeration,
|
||||
)]
|
||||
#[repr(i32)]
|
||||
pub enum FeatureType {
|
||||
/// 未知
|
||||
Unknown = 0,
|
||||
/// 仿真
|
||||
Simulation = 1,
|
||||
/// 城轨信号系统仿真(Urban Rail)
|
||||
Ur = 1,
|
||||
/// 运行图编制
|
||||
RunPlan = 2,
|
||||
}
|
||||
@ -203,7 +256,7 @@ impl FeatureType {
|
||||
pub fn as_str_name(&self) -> &'static str {
|
||||
match self {
|
||||
FeatureType::Unknown => "FeatureType_Unknown",
|
||||
FeatureType::Simulation => "FeatureType_Simulation",
|
||||
FeatureType::Ur => "FeatureType_Ur",
|
||||
FeatureType::RunPlan => "FeatureType_RunPlan",
|
||||
}
|
||||
}
|
||||
@ -211,7 +264,7 @@ impl FeatureType {
|
||||
pub fn from_str_name(value: &str) -> ::core::option::Option<Self> {
|
||||
match value {
|
||||
"FeatureType_Unknown" => Some(Self::Unknown),
|
||||
"FeatureType_Simulation" => Some(Self::Simulation),
|
||||
"FeatureType_Ur" => Some(Self::Ur),
|
||||
"FeatureType_RunPlan" => Some(Self::RunPlan),
|
||||
_ => None,
|
||||
}
|
||||
|
@ -2,16 +2,12 @@
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct IscsGraphicStorage {
|
||||
#[prost(message, optional, tag = "1")]
|
||||
pub canvas: ::core::option::Option<super::common::Canvas>,
|
||||
#[prost(message, repeated, tag = "1")]
|
||||
pub cctv_of_station_control_storages: ::prost::alloc::vec::Vec<
|
||||
CctvOfStationControlStorage,
|
||||
>,
|
||||
#[prost(message, repeated, tag = "2")]
|
||||
pub arrows: ::prost::alloc::vec::Vec<Arrow>,
|
||||
#[prost(message, repeated, tag = "3")]
|
||||
pub iscs_texts: ::prost::alloc::vec::Vec<IscsText>,
|
||||
#[prost(message, repeated, tag = "4")]
|
||||
pub rects: ::prost::alloc::vec::Vec<Rect>,
|
||||
#[prost(message, repeated, tag = "5")]
|
||||
pub cctv_buttons: ::prost::alloc::vec::Vec<CctvButton>,
|
||||
pub fas_platform_alarm_storages: ::prost::alloc::vec::Vec<FasPlatformAlarmStorage>,
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
@ -177,3 +173,33 @@ pub struct TemperatureDetector {
|
||||
#[prost(string, tag = "2")]
|
||||
pub code: ::prost::alloc::string::String,
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct CctvOfStationControlStorage {
|
||||
#[prost(string, tag = "1")]
|
||||
pub station_name: ::prost::alloc::string::String,
|
||||
#[prost(message, optional, tag = "2")]
|
||||
pub canvas: ::core::option::Option<super::common::Canvas>,
|
||||
#[prost(message, repeated, tag = "3")]
|
||||
pub arrows: ::prost::alloc::vec::Vec<Arrow>,
|
||||
#[prost(message, repeated, tag = "4")]
|
||||
pub iscs_texts: ::prost::alloc::vec::Vec<IscsText>,
|
||||
#[prost(message, repeated, tag = "5")]
|
||||
pub rects: ::prost::alloc::vec::Vec<Rect>,
|
||||
#[prost(message, repeated, tag = "6")]
|
||||
pub cctv_buttons: ::prost::alloc::vec::Vec<CctvButton>,
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct FasPlatformAlarmStorage {
|
||||
#[prost(string, tag = "1")]
|
||||
pub station_name: ::prost::alloc::string::String,
|
||||
#[prost(message, optional, tag = "2")]
|
||||
pub canvas: ::core::option::Option<super::common::Canvas>,
|
||||
#[prost(message, repeated, tag = "3")]
|
||||
pub arrows: ::prost::alloc::vec::Vec<Arrow>,
|
||||
#[prost(message, repeated, tag = "4")]
|
||||
pub iscs_texts: ::prost::alloc::vec::Vec<IscsText>,
|
||||
#[prost(message, repeated, tag = "5")]
|
||||
pub rects: ::prost::alloc::vec::Vec<Rect>,
|
||||
}
|
||||
|
8
docs/DESIGN.md
Normal file
8
docs/DESIGN.md
Normal file
@ -0,0 +1,8 @@
|
||||
# 系统设计
|
||||
|
||||
## 数据库设计
|
||||
- 核心数据如下图所示
|
||||
|
||||
![alt text](image-1.png)
|
||||
|
||||
- 所有数据从草稿数据开始,草稿数据发布为发布数据,功能定义引用发布数据,同时功能还可以引用其他功能实现功能目录树/功能组/功能包
|
BIN
docs/image-1.png
Normal file
BIN
docs/image-1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
@ -14,6 +14,38 @@ CREATE TABLE
|
||||
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP -- 更新时间
|
||||
);
|
||||
|
||||
-- 创建用户名称索引
|
||||
CREATE INDEX ON rtss.user (username);
|
||||
|
||||
-- 创建用户邮箱索引
|
||||
CREATE INDEX ON rtss.user (email);
|
||||
|
||||
-- 创建用户手机号索引
|
||||
CREATE INDEX ON rtss.user (mobile);
|
||||
|
||||
-- 创建用户角色索引
|
||||
CREATE INDEX ON rtss.user USING GIN (roles);
|
||||
|
||||
-- 注释用户表
|
||||
COMMENT ON TABLE rtss.user IS '用户表';
|
||||
|
||||
-- 注释用户表字段
|
||||
COMMENT ON COLUMN rtss.user.id IS 'id 自增主键';
|
||||
|
||||
COMMENT ON COLUMN rtss.user.username IS '用户名';
|
||||
|
||||
COMMENT ON COLUMN rtss.user.password IS '密码';
|
||||
|
||||
COMMENT ON COLUMN rtss.user.email IS '邮箱';
|
||||
|
||||
COMMENT ON COLUMN rtss.user.mobile IS '手机号';
|
||||
|
||||
COMMENT ON COLUMN rtss.user.roles IS '角色列表';
|
||||
|
||||
COMMENT ON COLUMN rtss.user.created_at IS '创建时间';
|
||||
|
||||
COMMENT ON COLUMN rtss.user.updated_at IS '更新时间';
|
||||
|
||||
-- 创建草稿数据表
|
||||
CREATE TABLE
|
||||
rtss.draft_data (
|
||||
@ -28,7 +60,7 @@ CREATE TABLE
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP, -- 创建时间
|
||||
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP, -- 更新时间
|
||||
FOREIGN KEY (user_id) REFERENCES rtss.user (id) ON DELETE CASCADE, -- 用户外键
|
||||
UNIQUE (name, user_id) -- 一个用户的草稿名称唯一
|
||||
UNIQUE (name, data_type, user_id) -- 一个用户的某个类型的草稿名称唯一
|
||||
);
|
||||
|
||||
-- 创建草稿数据用户索引
|
||||
@ -78,6 +110,18 @@ CREATE TABLE
|
||||
UNIQUE(data_type, name) -- 数据类型和名称唯一
|
||||
);
|
||||
|
||||
-- 创建发布数据名称索引
|
||||
CREATE INDEX ON rtss.release_data (name);
|
||||
|
||||
-- 创建发布数据用户索引
|
||||
CREATE INDEX ON rtss.release_data (user_id);
|
||||
|
||||
-- 创建发布数据类型索引
|
||||
CREATE INDEX ON rtss.release_data (data_type);
|
||||
|
||||
-- 创建发布数据配置项索引
|
||||
CREATE INDEX ON rtss.release_data USING GIN (options);
|
||||
|
||||
-- 注释发布数据表
|
||||
COMMENT ON TABLE rtss.release_data IS '发布数据表';
|
||||
|
||||
@ -114,6 +158,15 @@ CREATE TABLE
|
||||
FOREIGN KEY (release_data_id) REFERENCES rtss.release_data (id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- 创建发布数据版本发布数据索引
|
||||
CREATE INDEX ON rtss.release_data_version (release_data_id);
|
||||
|
||||
-- 创建发布数据版本用户索引
|
||||
CREATE INDEX ON rtss.release_data_version (user_id);
|
||||
|
||||
-- 创建发布数据版本配置项索引
|
||||
CREATE INDEX ON rtss.release_data_version USING GIN (options);
|
||||
|
||||
-- 创建发布数据当前版本外键
|
||||
ALTER TABLE rtss.release_data ADD FOREIGN KEY (used_version_id) REFERENCES rtss.release_data_version (id) ON DELETE SET NULL;
|
||||
|
||||
@ -145,6 +198,7 @@ CREATE TABLE
|
||||
feature_type INT NOT NULL, -- feature类型
|
||||
name VARCHAR(128) NOT NULL UNIQUE, -- feature名称
|
||||
description TEXT NOT NULL, -- feature描述
|
||||
config JSONB NOT NULL, -- feature配置
|
||||
is_published BOOLEAN NOT NULL DEFAULT TRUE, -- 是否上架
|
||||
creator_id INT NOT NULL, -- 创建用户id
|
||||
updater_id INT NOT NULL, -- 更新用户id
|
||||
@ -154,6 +208,12 @@ CREATE TABLE
|
||||
FOREIGN KEY (updater_id) REFERENCES rtss.user (id) ON DELETE CASCADE -- 用户外键
|
||||
);
|
||||
|
||||
-- 创建feature类型索引
|
||||
CREATE INDEX ON rtss.feature (feature_type);
|
||||
|
||||
-- 创建feature名称索引
|
||||
CREATE INDEX ON rtss.feature (name);
|
||||
|
||||
-- 注释仿真feature表
|
||||
COMMENT ON TABLE rtss.feature IS 'feature表';
|
||||
|
||||
@ -166,6 +226,8 @@ COMMENT ON COLUMN rtss.feature.name IS 'feature名称';
|
||||
|
||||
COMMENT ON COLUMN rtss.feature.description IS 'feature描述';
|
||||
|
||||
COMMENT ON COLUMN rtss.feature.config IS 'feature配置';
|
||||
|
||||
COMMENT ON COLUMN rtss.feature.is_published IS '是否上架';
|
||||
|
||||
COMMENT ON COLUMN rtss.feature.creator_id IS '创建用户id';
|
||||
@ -174,95 +236,36 @@ COMMENT ON COLUMN rtss.feature.created_at IS '创建时间';
|
||||
|
||||
COMMENT ON COLUMN rtss.feature.updated_at IS '更新时间';
|
||||
|
||||
-- 创建仿真feature和发布数据关联表
|
||||
-- 创建用户配置表
|
||||
CREATE TABLE
|
||||
rtss.feature_release_data (
|
||||
feature_id INT NOT NULL, -- 仿真feature id
|
||||
release_data_id INT NOT NULL, -- 发布数据id
|
||||
PRIMARY KEY (feature_id, release_data_id),
|
||||
FOREIGN KEY (feature_id) REFERENCES rtss.feature (id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (release_data_id) REFERENCES rtss.release_data (id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- 注释仿真feature和发布数据关联表
|
||||
COMMENT ON TABLE rtss.feature_release_data IS '仿真feature和发布数据关联表';
|
||||
|
||||
-- 注释仿真feature和发布数据关联表字段
|
||||
COMMENT ON COLUMN rtss.feature_release_data.feature_id IS '仿真feature id';
|
||||
|
||||
COMMENT ON COLUMN rtss.feature_release_data.release_data_id IS '发布数据id';
|
||||
|
||||
-- 创建feature group表
|
||||
CREATE TABLE
|
||||
rtss.feature_group (
|
||||
id SERIAL PRIMARY KEY, -- id 自增主键
|
||||
name VARCHAR(128) NOT NULL UNIQUE, -- feature group名称
|
||||
description TEXT NOT NULL, -- feature group描述
|
||||
is_published BOOLEAN NOT NULL DEFAULT TRUE, -- 是否上架
|
||||
creator_id INT NOT NULL, -- 创建用户id
|
||||
updater_id INT NOT NULL, -- 更新用户id
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP, -- 创建时间
|
||||
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP, -- 更新时间
|
||||
FOREIGN KEY (creator_id) REFERENCES rtss.user (id) ON DELETE CASCADE, -- 用户外键
|
||||
FOREIGN KEY (updater_id) REFERENCES rtss.user (id) ON DELETE CASCADE -- 用户外键
|
||||
);
|
||||
|
||||
-- 注释仿真feature group表
|
||||
COMMENT ON TABLE rtss.feature_group IS 'feature group表';
|
||||
|
||||
-- 注释仿真feature group表字段
|
||||
COMMENT ON COLUMN rtss.feature_group.id IS 'id 自增主键';
|
||||
|
||||
COMMENT ON COLUMN rtss.feature_group.name IS 'feature group名称';
|
||||
|
||||
COMMENT ON COLUMN rtss.feature_group.description IS 'feature group描述';
|
||||
|
||||
COMMENT ON COLUMN rtss.feature_group.is_published IS '是否上架';
|
||||
|
||||
COMMENT ON COLUMN rtss.feature_group.creator_id IS '创建用户id';
|
||||
|
||||
COMMENT ON COLUMN rtss.feature_group.created_at IS '创建时间';
|
||||
|
||||
COMMENT ON COLUMN rtss.feature_group.updated_at IS '更新时间';
|
||||
|
||||
-- 创建feature group和feature关联表
|
||||
CREATE TABLE
|
||||
rtss.feature_group_feature (
|
||||
feature_group_id INT NOT NULL, -- feature group id
|
||||
feature_id INT NOT NULL, -- feature id
|
||||
PRIMARY KEY (feature_id, feature_group_id),
|
||||
FOREIGN KEY (feature_id) REFERENCES rtss.feature (id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (feature_group_id) REFERENCES rtss.feature_group (id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- 注释仿真feature group和feature关联表
|
||||
COMMENT ON TABLE rtss.feature_group_feature IS '仿真feature group和feature关联表';
|
||||
|
||||
-- 创建用户feature配置表
|
||||
CREATE TABLE
|
||||
rtss.feature_config (
|
||||
rtss.user_config (
|
||||
id SERIAL PRIMARY KEY, -- id 自增主键
|
||||
user_id INT NOT NULL, -- 用户id
|
||||
feature_id INT NOT NULL, -- 仿真feature id
|
||||
config_type INT NOT NULL, -- 配置类型
|
||||
config BYTEA NOT NULL, -- 配置
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP, -- 创建时间
|
||||
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP, -- 更新时间
|
||||
FOREIGN KEY (user_id) REFERENCES rtss.user (id) ON DELETE CASCADE, -- 用户外键
|
||||
FOREIGN KEY (feature_id) REFERENCES rtss.feature (id) ON DELETE CASCADE
|
||||
FOREIGN KEY (user_id) REFERENCES rtss.user (id) ON DELETE CASCADE -- 用户外键
|
||||
);
|
||||
|
||||
-- 创建用户配置用户索引
|
||||
CREATE INDEX ON rtss.user_config (user_id);
|
||||
|
||||
-- 创建用户配置类型索引
|
||||
CREATE INDEX ON rtss.user_config (config_type);
|
||||
|
||||
-- 注释用户feature配置表
|
||||
COMMENT ON TABLE rtss.feature_config IS '用户feature配置表';
|
||||
COMMENT ON TABLE rtss.user_config IS '用户feature配置表';
|
||||
|
||||
-- 注释用户feature配置表字段
|
||||
COMMENT ON COLUMN rtss.feature_config.id IS 'id 自增主键';
|
||||
COMMENT ON COLUMN rtss.user_config.id IS 'id 自增主键';
|
||||
|
||||
COMMENT ON COLUMN rtss.feature_config.user_id IS '用户id';
|
||||
COMMENT ON COLUMN rtss.user_config.user_id IS '用户id';
|
||||
|
||||
COMMENT ON COLUMN rtss.feature_config.feature_id IS '仿真feature id';
|
||||
COMMENT ON COLUMN rtss.user_config.config_type IS '配置类型';
|
||||
|
||||
COMMENT ON COLUMN rtss.feature_config.config IS '配置';
|
||||
COMMENT ON COLUMN rtss.user_config.config IS '配置';
|
||||
|
||||
COMMENT ON COLUMN rtss.feature_config.created_at IS '创建时间';
|
||||
COMMENT ON COLUMN rtss.user_config.created_at IS '创建时间';
|
||||
|
||||
COMMENT ON COLUMN rtss.feature_config.updated_at IS '更新时间';
|
||||
COMMENT ON COLUMN rtss.user_config.updated_at IS '更新时间';
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 1672a8c0e2b41c4076c1dc9ec852f425dcba21c3
|
||||
Subproject commit 02d9ad3b44876fc470e460bb6975c3d08f698b1b
|
Loading…
Reference in New Issue
Block a user