use sea_orm::ActiveModelTrait; use sea_orm::QuerySelect; use sea_orm::Set; use sea_orm::{ColumnTrait, EntityTrait, PaginatorTrait, QueryFilter, QueryOrder}; use uuid::Uuid; use crate::entity::ai_prompt; use crate::error::{AiError, AiResult}; use erp_core::types::Pagination; pub struct PromptService { pub db: sea_orm::DatabaseConnection, } impl PromptService { pub fn new(db: sea_orm::DatabaseConnection) -> Self { Self { db } } /// 获取当前激活的 Prompt 模板 pub async fn get_active_prompt( &self, tenant_id: Uuid, name: &str, ) -> AiResult { ai_prompt::Entity::find() .filter(ai_prompt::Column::TenantId.eq(tenant_id)) .filter(ai_prompt::Column::Name.eq(name)) .filter(ai_prompt::Column::IsActive.eq(true)) .filter(ai_prompt::Column::DeletedAt.is_null()) .one(&self.db) .await? .ok_or_else(|| AiError::PromptNotFound(name.into())) } /// 新建 Prompt pub async fn create_prompt( &self, tenant_id: Uuid, user_id: Uuid, name: String, system_prompt: String, user_prompt_template: String, model_config: serde_json::Value, category: String, ) -> AiResult { let id = Uuid::now_v7(); let now = chrono::Utc::now(); let active = ai_prompt::ActiveModel { id: Set(id), tenant_id: Set(tenant_id), name: Set(name), description: Set(String::new()), system_prompt: Set(system_prompt), user_prompt_template: Set(user_prompt_template), variables_schema: Set(None), model_config: Set(model_config), version: Set(1), is_active: Set(true), category: Set(category), tags: Set(None), created_at: Set(now), updated_at: Set(now), created_by: Set(Some(user_id)), updated_by: Set(Some(user_id)), deleted_at: Set(None), version_lock: Set(1), }; Ok(active.insert(&self.db).await?) } /// 分页列出 Prompt 模板 pub async fn list_prompts( &self, tenant_id: Uuid, category: Option, pagination: &Pagination, ) -> AiResult<(Vec, u64)> { let mut query = ai_prompt::Entity::find() .filter(ai_prompt::Column::TenantId.eq(tenant_id)) .filter(ai_prompt::Column::DeletedAt.is_null()); if let Some(cat) = &category { query = query.filter(ai_prompt::Column::Category.eq(cat.as_str())); } let total = query.clone().count(&self.db).await?; let items = query .order_by_desc(ai_prompt::Column::UpdatedAt) .offset(pagination.offset()) .limit(pagination.limit()) .all(&self.db) .await?; Ok((items, total)) } /// 更新 Prompt(创建新版本) pub async fn update_prompt( &self, id: Uuid, tenant_id: Uuid, user_id: Uuid, system_prompt: Option, user_prompt_template: Option, model_config: Option, description: Option, ) -> AiResult { let entity = ai_prompt::Entity::find_by_id(id) .one(&self.db) .await? .ok_or_else(|| AiError::PromptNotFound(id.to_string()))?; if entity.tenant_id != tenant_id { return Err(AiError::Validation("跨租户操作".into())); } let new_id = Uuid::now_v7(); let now = chrono::Utc::now(); let active = ai_prompt::ActiveModel { id: Set(new_id), tenant_id: Set(tenant_id), name: Set(entity.name.clone()), description: Set(description.unwrap_or(entity.description.clone())), system_prompt: Set(system_prompt.unwrap_or(entity.system_prompt.clone())), user_prompt_template: Set(user_prompt_template.unwrap_or(entity.user_prompt_template.clone())), variables_schema: Set(entity.variables_schema.clone()), model_config: Set(model_config.unwrap_or(entity.model_config.clone())), version: Set(entity.version + 1), is_active: Set(entity.is_active), category: Set(entity.category.clone()), tags: Set(entity.tags.clone()), created_at: Set(now), updated_at: Set(now), created_by: Set(Some(user_id)), updated_by: Set(Some(user_id)), deleted_at: Set(None), version_lock: Set(1), }; Ok(active.insert(&self.db).await?) } /// 激活指定 Prompt(停用同 name+category 的其他版本) pub async fn activate_prompt( &self, id: Uuid, tenant_id: Uuid, ) -> AiResult { let entity = ai_prompt::Entity::find_by_id(id) .one(&self.db) .await? .ok_or_else(|| AiError::PromptNotFound(id.to_string()))?; if entity.tenant_id != tenant_id { return Err(AiError::Validation("跨租户操作".into())); } // 停用同 name + category 的其他激活版本 let siblings = ai_prompt::Entity::find() .filter(ai_prompt::Column::TenantId.eq(tenant_id)) .filter(ai_prompt::Column::Name.eq(&entity.name)) .filter(ai_prompt::Column::Category.eq(&entity.category)) .filter(ai_prompt::Column::IsActive.eq(true)) .filter(ai_prompt::Column::DeletedAt.is_null()) .all(&self.db) .await?; for sibling in siblings { let mut active: ai_prompt::ActiveModel = sibling.into(); active.is_active = Set(false); active.updated_at = Set(chrono::Utc::now()); active.update(&self.db).await?; } // 激活目标 let mut active: ai_prompt::ActiveModel = entity.into(); active.is_active = Set(true); active.updated_at = Set(chrono::Utc::now()); Ok(active.update(&self.db).await?) } /// 回滚(= 激活指定旧版本) pub async fn rollback_prompt( &self, id: Uuid, tenant_id: Uuid, ) -> AiResult { self.activate_prompt(id, tenant_id).await } }