Files
zhenyi 6205a6de0a refactor(models): replace hardcoded strings with typed enums
- Add ReviewState enum (pending, approved, changes_requested, etc.)
- Add DEFAULT_REVISION constant for git HEAD references
- service/pr/reviews.rs: use ReviewState for review creation and
  submission state validation
- service/pr/core.rs: use MergeStrategyKind for merge strategy
  selection
- service/im/stages.rs: use StagePrivacyLevel for stage creation
- service/im/invitations.rs: use Role enum for invitation role
  defaults
2026-06-10 18:49:06 +08:00

120 lines
3.7 KiB
Rust

use serde::{Deserialize, Serialize};
use uuid::Uuid;
use crate::error::AppError;
use crate::models::channels::Stage;
use crate::models::common::StagePrivacyLevel;
use crate::service::ImService;
use super::session::ImSession;
#[derive(Deserialize, Serialize, Clone, Debug, utoipa::ToSchema)]
pub struct CreateStageParams {
pub topic: String,
pub privacy_level: Option<String>,
pub discoverable: Option<bool>,
}
#[derive(Deserialize, Serialize, Clone, Debug, utoipa::ToSchema)]
pub struct UpdateStageParams {
pub topic: Option<String>,
pub privacy_level: Option<String>,
pub discoverable: Option<bool>,
}
impl ImService {
pub async fn stage_get(
&self,
_ctx: &ImSession,
channel_id: Uuid,
) -> Result<Option<Stage>, AppError> {
sqlx::query_as::<_, Stage>(
"SELECT id, channel_id, topic, privacy_level, discoverable, \
started_by, started_at, ended_at, created_at, updated_at \
FROM stage WHERE channel_id = $1 AND ended_at IS NULL \
ORDER BY started_at DESC LIMIT 1",
)
.bind(channel_id)
.fetch_optional(self.ctx.db.reader())
.await
.map_err(AppError::Database)
}
pub async fn stage_create(
&self,
ctx: &ImSession,
channel_id: Uuid,
params: CreateStageParams,
) -> Result<Stage, AppError> {
let now = chrono::Utc::now();
let privacy = params
.privacy_level
.as_deref()
.and_then(|s| s.parse::<StagePrivacyLevel>().ok())
.filter(|s| *s != StagePrivacyLevel::Unknown)
.unwrap_or(StagePrivacyLevel::GuildOnly);
sqlx::query_as::<_, Stage>(
"INSERT INTO stage \
(id, channel_id, topic, privacy_level, discoverable, \
started_by, started_at, created_at, updated_at) \
VALUES ($1, $2, $3, $4::stage_privacy_level, $5, $6, $7, $7, $7) \
RETURNING id, channel_id, topic, privacy_level, discoverable, \
started_by, started_at, ended_at, created_at, updated_at",
)
.bind(Uuid::now_v7())
.bind(channel_id)
.bind(&params.topic)
.bind(privacy)
.bind(params.discoverable.unwrap_or(false))
.bind(ctx.user)
.bind(now)
.fetch_one(self.ctx.db.writer())
.await
.map_err(AppError::Database)
}
pub async fn stage_update(
&self,
_ctx: &ImSession,
stage_id: Uuid,
params: UpdateStageParams,
) -> Result<Stage, AppError> {
let now = chrono::Utc::now();
sqlx::query_as::<_, Stage>(
"UPDATE stage SET \
topic = COALESCE($1, topic), \
privacy_level = COALESCE($2::stage_privacy_level, privacy_level), \
discoverable = COALESCE($3, discoverable), \
updated_at = $4 \
WHERE id = $5 \
RETURNING id, channel_id, topic, privacy_level, discoverable, \
started_by, started_at, ended_at, created_at, updated_at",
)
.bind(params.topic.as_deref())
.bind(params.privacy_level.as_deref())
.bind(params.discoverable)
.bind(now)
.bind(stage_id)
.fetch_one(self.ctx.db.writer())
.await
.map_err(AppError::Database)
}
pub async fn stage_delete(
&self,
_ctx: &ImSession,
stage_id: Uuid,
) -> Result<(), AppError> {
let now = chrono::Utc::now();
sqlx::query(
"UPDATE stage SET ended_at = $1, updated_at = $1 WHERE id = $2 AND ended_at IS NULL",
)
.bind(now)
.bind(stage_id)
.execute(self.ctx.db.writer())
.await
.map_err(AppError::Database)?;
Ok(())
}
}