Skip to main content

tibba_model_builtin/
detector_group.rs

1// Copyright 2026 Tree xie.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use serde::{Deserialize, Serialize};
16use snafu::ResultExt;
17use sqlx::FromRow;
18use sqlx::{Pool, Postgres, QueryBuilder};
19use std::collections::HashMap;
20use tibba_model::{
21    Error, JsonSnafu, Model, ModelListParams, Schema, SchemaAllowCreate, SchemaAllowEdit,
22    SchemaType, SchemaView, SqlxSnafu, format_datetime,
23};
24use time::PrimitiveDateTime;
25
26type Result<T> = std::result::Result<T, Error>;
27
28#[derive(FromRow)]
29struct DetectorGroupSchema {
30    id: i64,
31    name: String,
32    code: String,
33    owner_id: i64,
34    status: i16,
35    remark: String,
36    created_by: i64,
37    created: PrimitiveDateTime,
38    modified: PrimitiveDateTime,
39}
40
41#[derive(Deserialize, Serialize, Clone, Debug)]
42pub struct DetectorGroup {
43    pub id: i64,
44    pub name: String,
45    pub code: String,
46    pub owner_id: i64,
47    pub status: i16,
48    pub created_by: i64,
49    pub remark: String,
50    pub created: String,
51    pub modified: String,
52}
53
54impl From<DetectorGroupSchema> for DetectorGroup {
55    fn from(schema: DetectorGroupSchema) -> Self {
56        Self {
57            id: schema.id,
58            name: schema.name,
59            code: schema.code,
60            owner_id: schema.owner_id,
61            status: schema.status,
62            created_by: schema.created_by,
63            remark: schema.remark,
64            created: format_datetime(schema.created),
65            modified: format_datetime(schema.modified),
66        }
67    }
68}
69
70#[derive(Debug, Clone, Deserialize, Default)]
71pub struct DetectorGroupInsertParams {
72    pub name: String,
73    pub code: String,
74    pub owner_id: u64,
75    pub created_by: u64,
76    pub status: i16,
77    pub remark: String,
78}
79
80#[derive(Debug, Clone, Deserialize, Default)]
81pub struct DetectorGroupUpdateParams {
82    pub name: Option<String>,
83    pub owner_id: Option<u64>,
84    pub status: Option<i16>,
85    pub remark: Option<String>,
86}
87
88pub struct DetectorGroupModel {}
89
90impl DetectorGroupModel {
91    pub async fn list_enabled(&self, pool: &Pool<Postgres>) -> Result<Vec<DetectorGroup>> {
92        let groups = sqlx::query_as::<_, DetectorGroupSchema>(
93            r#"SELECT * FROM detector_groups WHERE deleted_at IS NULL AND status = 1"#,
94        )
95        .fetch_all(pool)
96        .await
97        .context(SqlxSnafu)?;
98
99        Ok(groups.into_iter().map(|schema| schema.into()).collect())
100    }
101}
102
103impl Model for DetectorGroupModel {
104    type Output = DetectorGroup;
105    fn new() -> Self {
106        Self {}
107    }
108    async fn schema_view(&self, _pool: &Pool<Postgres>) -> SchemaView {
109        SchemaView {
110            schemas: vec![
111                Schema::new_id(),
112                Schema {
113                    name: "code".to_string(),
114                    category: SchemaType::String,
115                    required: true,
116                    fixed: true,
117                    ..Default::default()
118                },
119                Schema {
120                    name: "name".to_string(),
121                    category: SchemaType::String,
122                    required: true,
123                    ..Default::default()
124                },
125                Schema {
126                    name: "owner_id".to_string(),
127                    category: SchemaType::Number,
128                    read_only: true,
129                    auto_create: true,
130                    ..Default::default()
131                },
132                Schema::new_status(),
133                Schema::new_remark(),
134                Schema::new_created(),
135                Schema::new_modified(),
136            ],
137            allow_edit: SchemaAllowEdit {
138                owner: true,
139                roles: vec!["*".to_string()],
140                ..Default::default()
141            },
142            allow_create: SchemaAllowCreate {
143                roles: vec!["*".to_string()],
144                ..Default::default()
145            },
146        }
147    }
148
149    fn push_filter_conditions<'args>(
150        &self,
151        qb: &mut QueryBuilder<'args, Postgres>,
152        filters: &HashMap<String, String>,
153    ) -> Result<()> {
154        if let Some(status) = filters.get("status").and_then(|s| s.parse::<i16>().ok()) {
155            qb.push(" AND status = ");
156            qb.push_bind(status);
157        }
158        Ok(())
159    }
160
161    async fn insert(&self, pool: &Pool<Postgres>, mut params: serde_json::Value) -> Result<u64> {
162        // owner_id 未指定时默认与 created_by 相同(创建者即为拥有者)
163        if let Some(obj) = params.as_object_mut() {
164            if !obj.contains_key("owner_id") {
165                if let Some(created_by) = obj.get("created_by").cloned() {
166                    obj.insert("owner_id".to_string(), created_by);
167                }
168            }
169        }
170        let params: DetectorGroupInsertParams =
171            serde_json::from_value(params).context(JsonSnafu)?;
172        let row: (i64,) = sqlx::query_as(
173            r#"INSERT INTO detector_groups (name, code, owner_id, created_by, status, remark) VALUES ($1, $2, $3, $4, $5, $6) RETURNING id"#,
174        )
175        .bind(params.name)
176        .bind(params.code)
177        .bind(params.owner_id as i64)
178        .bind(params.created_by as i64)
179        .bind(params.status)
180        .bind(params.remark)
181        .fetch_one(pool)
182        .await
183        .context(SqlxSnafu)?;
184
185        Ok(row.0 as u64)
186    }
187
188    async fn get_by_id(&self, pool: &Pool<Postgres>, id: u64) -> Result<Option<Self::Output>> {
189        let result = sqlx::query_as::<_, DetectorGroupSchema>(
190            r#"SELECT * FROM detector_groups WHERE id = $1 AND deleted_at IS NULL"#,
191        )
192        .bind(id as i64)
193        .fetch_optional(pool)
194        .await
195        .context(SqlxSnafu)?;
196
197        Ok(result.map(|schema| schema.into()))
198    }
199
200    async fn delete_by_id(&self, pool: &Pool<Postgres>, id: u64) -> Result<()> {
201        sqlx::query(
202            r#"UPDATE detector_groups SET deleted_at = NOW(), modified = NOW() WHERE id = $1"#,
203        )
204        .bind(id as i64)
205        .execute(pool)
206        .await
207        .context(SqlxSnafu)?;
208
209        Ok(())
210    }
211
212    async fn update_by_id(
213        &self,
214        pool: &Pool<Postgres>,
215        id: u64,
216        params: serde_json::Value,
217    ) -> Result<()> {
218        let params: DetectorGroupUpdateParams =
219            serde_json::from_value(params).context(JsonSnafu)?;
220
221        let _ = sqlx::query(
222            r#"UPDATE detector_groups SET name = COALESCE($1, name), owner_id = COALESCE($2, owner_id), status = COALESCE($3, status), remark = COALESCE($4, remark), modified = NOW() WHERE id = $5 AND deleted_at IS NULL"#,
223        )
224        .bind(params.name)
225        .bind(params.owner_id.map(|v| v as i64))
226        .bind(params.status)
227        .bind(params.remark)
228        .bind(id as i64)
229        .execute(pool)
230        .await
231        .context(SqlxSnafu)?;
232
233        Ok(())
234    }
235
236    async fn count(&self, pool: &Pool<Postgres>, params: &ModelListParams) -> Result<i64> {
237        let mut qb = QueryBuilder::new("SELECT COUNT(*) FROM detector_groups");
238        self.push_conditions(&mut qb, params)?;
239        let count = qb
240            .build_query_scalar::<i64>()
241            .fetch_one(pool)
242            .await
243            .context(SqlxSnafu)?;
244        Ok(count)
245    }
246
247    async fn list(
248        &self,
249        pool: &Pool<Postgres>,
250        params: &ModelListParams,
251    ) -> Result<Vec<Self::Output>> {
252        let mut qb = QueryBuilder::new("SELECT * FROM detector_groups");
253        self.push_conditions(&mut qb, params)?;
254        params.push_pagination(&mut qb);
255        let groups = qb
256            .build_query_as::<DetectorGroupSchema>()
257            .fetch_all(pool)
258            .await
259            .context(SqlxSnafu)?;
260        Ok(groups.into_iter().map(|s| s.into()).collect())
261    }
262}