feat(industry): Phase 1 行业配置基础 — 数据模型 + 四行业内置配置 + ButlerRouter 动态关键词
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
- 新增 SaaS industry 模块 (types/service/handlers/mod/builtin) - 4 行业内置配置: healthcare/education/garment/ecommerce - 数据库迁移: industries + account_industries 表 - 8 个 API 端点 (CRUD + 用户行业关联) - ButlerRouter 改造: 支持 IndustryKeywordConfig 动态注入 - 12 个测试全通过 (含动态行业分类测试)
This commit is contained in:
111
crates/zclaw-saas/src/industry/handlers.rs
Normal file
111
crates/zclaw-saas/src/industry/handlers.rs
Normal file
@@ -0,0 +1,111 @@
|
||||
//! 行业配置 API handlers
|
||||
|
||||
use axum::extract::{Path, Query, State};
|
||||
use axum::Extension;
|
||||
use axum::Json;
|
||||
use crate::error::SaasResult;
|
||||
use crate::state::AppState;
|
||||
use crate::auth::types::AuthContext;
|
||||
use super::types::*;
|
||||
use super::service;
|
||||
|
||||
/// GET /api/v1/industries — 行业列表(公开,已认证用户可访问)
|
||||
pub async fn list_industries(
|
||||
State(state): State<AppState>,
|
||||
Query(query): Query<ListIndustriesQuery>,
|
||||
) -> SaasResult<Json<crate::common::PaginatedResponse<IndustryListItem>>> {
|
||||
let result = service::list_industries(&state.db, &query).await?;
|
||||
Ok(Json(result))
|
||||
}
|
||||
|
||||
/// GET /api/v1/industries/:id — 行业详情(公开)
|
||||
pub async fn get_industry(
|
||||
State(state): State<AppState>,
|
||||
Path(id): Path<String>,
|
||||
) -> SaasResult<Json<Industry>> {
|
||||
let industry = service::get_industry(&state.db, &id).await?;
|
||||
Ok(Json(industry))
|
||||
}
|
||||
|
||||
/// POST /api/v1/industries — 创建行业 (admin: config:write)
|
||||
pub async fn create_industry(
|
||||
State(state): State<AppState>,
|
||||
Extension(ctx): Extension<AuthContext>,
|
||||
Json(body): Json<CreateIndustryRequest>,
|
||||
) -> SaasResult<Json<Industry>> {
|
||||
require_config_write(&ctx)?;
|
||||
let industry = service::create_industry(&state.db, &body).await?;
|
||||
Ok(Json(industry))
|
||||
}
|
||||
|
||||
/// PATCH /api/v1/industries/:id — 更新行业 (admin: config:write)
|
||||
pub async fn update_industry(
|
||||
State(state): State<AppState>,
|
||||
Extension(ctx): Extension<AuthContext>,
|
||||
Path(id): Path<String>,
|
||||
Json(body): Json<UpdateIndustryRequest>,
|
||||
) -> SaasResult<Json<Industry>> {
|
||||
require_config_write(&ctx)?;
|
||||
let industry = service::update_industry(&state.db, &id, &body).await?;
|
||||
Ok(Json(industry))
|
||||
}
|
||||
|
||||
/// GET /api/v1/industries/:id/full-config — 完整配置(含关键词、prompt等)
|
||||
pub async fn get_industry_full_config(
|
||||
State(state): State<AppState>,
|
||||
Path(id): Path<String>,
|
||||
) -> SaasResult<Json<IndustryFullConfig>> {
|
||||
let config = service::get_industry_full_config(&state.db, &id).await?;
|
||||
Ok(Json(config))
|
||||
}
|
||||
|
||||
/// GET /api/v1/accounts/:id/industries — 用户授权行业列表
|
||||
pub async fn list_account_industries(
|
||||
State(state): State<AppState>,
|
||||
Path(account_id): Path<String>,
|
||||
) -> SaasResult<Json<Vec<AccountIndustryItem>>> {
|
||||
let items = service::list_account_industries(&state.db, &account_id).await?;
|
||||
Ok(Json(items))
|
||||
}
|
||||
|
||||
/// PUT /api/v1/accounts/:id/industries — 设置用户行业 (admin: account:admin)
|
||||
pub async fn set_account_industries(
|
||||
State(state): State<AppState>,
|
||||
Extension(ctx): Extension<AuthContext>,
|
||||
Path(account_id): Path<String>,
|
||||
Json(body): Json<SetAccountIndustriesRequest>,
|
||||
) -> SaasResult<Json<Vec<AccountIndustryItem>>> {
|
||||
require_account_admin(&ctx)?;
|
||||
let items = service::set_account_industries(&state.db, &account_id, &body).await?;
|
||||
Ok(Json(items))
|
||||
}
|
||||
|
||||
/// GET /api/v1/accounts/me/industries — 当前用户行业
|
||||
pub async fn list_my_industries(
|
||||
State(state): State<AppState>,
|
||||
Extension(ctx): Extension<AuthContext>,
|
||||
) -> SaasResult<Json<Vec<AccountIndustryItem>>> {
|
||||
let account_id = &ctx.account_id;
|
||||
let items = service::list_account_industries(&state.db, account_id).await?;
|
||||
Ok(Json(items))
|
||||
}
|
||||
|
||||
// ============ Helpers ============
|
||||
|
||||
fn require_config_write(ctx: &AuthContext) -> SaasResult<()> {
|
||||
if !ctx.permissions.contains(&"config:write".to_string())
|
||||
&& !ctx.permissions.contains(&"admin:full".to_string())
|
||||
{
|
||||
return Err(crate::error::SaasError::Forbidden("需要 config:write 权限".to_string()));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn require_account_admin(ctx: &AuthContext) -> SaasResult<()> {
|
||||
if !ctx.permissions.contains(&"account:admin".to_string())
|
||||
&& !ctx.permissions.contains(&"admin:full".to_string())
|
||||
{
|
||||
return Err(crate::error::SaasError::Forbidden("需要 account:admin 权限".to_string()));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user