4f3f0f24ff
新增后台 AGC 模型目录、别名、启停和默认项管理 客户端设置页恢复原状,对话框右下角按别名选择模型 服务端按稳定模型标识映射并校验实际模型白名单 修复 AGC 配套后端端口漂移、启动等待和 SpacetimeDB 版本检查 补充迁移、文档、启动与模型选择测试
1273 lines
44 KiB
Rust
1273 lines
44 KiB
Rust
use std::collections::BTreeMap;
|
||
|
||
use serde::{Deserialize, Serialize};
|
||
use serde_json::Value;
|
||
|
||
#[cfg(any())]
|
||
use crate::creation_entry_config::{
|
||
CreationEntryEventBannerResponse, PublicWorkInteractionConfigResponse,
|
||
UnifiedCreationSpecResponse,
|
||
};
|
||
|
||
/// 后台 member 可被授予的一级 Tab 权限;账号管理仅 owner 可见,不进入该集合。
|
||
pub const ADMIN_TAB_PERMISSIONS: [&str; 16] = [
|
||
"dashboard",
|
||
"overview",
|
||
"tables",
|
||
"debug",
|
||
"tracking",
|
||
"gray-release",
|
||
"redeem",
|
||
"invite",
|
||
"profile-wallet",
|
||
"tasks",
|
||
"recharge-products",
|
||
"recharge-orders",
|
||
"editor-generation-pricing",
|
||
"editor-showcase",
|
||
"editor-assets",
|
||
"error-reports",
|
||
];
|
||
|
||
/// 不随一级 Tab 自动授予的后台高风险操作权限。
|
||
pub const ADMIN_ACTION_PROFILE_WALLET_CONSUMPTION_RECONCILE: &str =
|
||
"profile-wallet-consumption-reconcile";
|
||
pub const ADMIN_ACTION_PERMISSIONS: [&str; 1] = [ADMIN_ACTION_PROFILE_WALLET_CONSUMPTION_RECONCILE];
|
||
|
||
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub enum AdminAccountRole {
|
||
Owner,
|
||
Member,
|
||
}
|
||
|
||
// 管理后台协议统一收口在 shared-contracts,避免页面脚本和 Rust handler 各自手拼字段。
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminLoginRequest {
|
||
pub username: String,
|
||
pub password: String,
|
||
}
|
||
|
||
// 登录成功后返回管理员访问令牌与基础会话信息。
|
||
|
||
/// 后台创作入口开关列表响应。
|
||
#[cfg(any())]
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminCreationEntryConfigResponse {
|
||
pub entries: Vec<AdminCreationEntryTypeConfigPayload>,
|
||
/// 底部加号创作入口页的后台公告列表。
|
||
pub event_banners: Vec<CreationEntryEventBannerResponse>,
|
||
/// 公开作品详情页点赞 / 改造能力配置。
|
||
pub public_work_interactions: Vec<PublicWorkInteractionConfigResponse>,
|
||
}
|
||
|
||
/// 后台单个创作入口开关配置。
|
||
#[cfg(any())]
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminCreationEntryTypeConfigPayload {
|
||
pub id: String,
|
||
pub title: String,
|
||
pub subtitle: String,
|
||
pub badge: String,
|
||
pub image_src: String,
|
||
pub visible: bool,
|
||
pub open: bool,
|
||
pub sort_order: i32,
|
||
pub category_id: String,
|
||
pub category_label: String,
|
||
pub category_sort_order: i32,
|
||
pub updated_at_micros: i64,
|
||
#[serde(skip_serializing_if = "Option::is_none")]
|
||
pub unified_creation_spec: Option<UnifiedCreationSpecResponse>,
|
||
}
|
||
|
||
/// 后台保存创作入口开关配置请求。
|
||
#[cfg(any())]
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminUpsertCreationEntryTypeConfigRequest {
|
||
pub id: String,
|
||
pub title: String,
|
||
pub subtitle: String,
|
||
pub badge: String,
|
||
pub image_src: String,
|
||
pub visible: bool,
|
||
pub open: bool,
|
||
pub sort_order: i32,
|
||
pub category_id: String,
|
||
pub category_label: String,
|
||
pub category_sort_order: i32,
|
||
#[serde(skip_serializing_if = "Option::is_none")]
|
||
pub unified_creation_spec: Option<UnifiedCreationSpecResponse>,
|
||
}
|
||
|
||
/// 后台保存创作入口公告表单序列化结果请求。
|
||
#[cfg(any())]
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminUpsertCreationEntryEventBannersRequest {
|
||
/// 传输字段沿用既有契约,内容由后台标题 / HTML 表单生成。
|
||
pub event_banners_json: String,
|
||
}
|
||
|
||
/// 后台保存公开作品点赞 / 改造能力配置请求。
|
||
#[cfg(any())]
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminUpsertPublicWorkInteractionConfigRequest {
|
||
pub public_work_interactions: Vec<PublicWorkInteractionConfigResponse>,
|
||
}
|
||
|
||
/// 后台灰度配置列表响应。
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminFeatureGateConfigResponse {
|
||
pub gates: Vec<AdminFeatureGateConfigPayload>,
|
||
}
|
||
|
||
/// 后台单个灰度 gate 配置。
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminFeatureGateConfigPayload {
|
||
pub gate_key: String,
|
||
pub enabled: bool,
|
||
pub rollout_percent: u32,
|
||
pub allow_user_ids: Vec<String>,
|
||
pub allow_user_tags: Vec<String>,
|
||
pub deny_user_ids: Vec<String>,
|
||
pub description: String,
|
||
pub updated_at: String,
|
||
}
|
||
|
||
/// 后台保存灰度 gate 配置请求。
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminUpsertFeatureGateConfigRequest {
|
||
pub gate_key: String,
|
||
pub enabled: bool,
|
||
pub rollout_percent: u32,
|
||
pub allow_user_ids: Vec<String>,
|
||
pub allow_user_tags: Vec<String>,
|
||
pub deny_user_ids: Vec<String>,
|
||
pub description: String,
|
||
}
|
||
|
||
/// 后台作品可见性列表项。
|
||
#[cfg(any())]
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminWorkVisibilityEntryPayload {
|
||
pub source_type: String,
|
||
pub work_id: String,
|
||
pub profile_id: String,
|
||
pub source_session_id: Option<String>,
|
||
pub public_work_code: String,
|
||
pub owner_user_id: String,
|
||
pub author_display_name: String,
|
||
pub title: String,
|
||
pub subtitle: String,
|
||
pub cover_image_src: Option<String>,
|
||
pub visible: bool,
|
||
pub published_at_micros: Option<i64>,
|
||
pub updated_at_micros: i64,
|
||
}
|
||
|
||
/// 后台作品可见性列表响应。
|
||
#[cfg(any())]
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminWorkVisibilityListResponse {
|
||
pub entries: Vec<AdminWorkVisibilityEntryPayload>,
|
||
}
|
||
|
||
/// 后台修改作品可见性请求。
|
||
#[cfg(any())]
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminUpdateWorkVisibilityRequest {
|
||
pub source_type: String,
|
||
pub profile_id: String,
|
||
pub visible: bool,
|
||
}
|
||
|
||
/// 后台修改作品可见性响应。
|
||
#[cfg(any())]
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminUpdateWorkVisibilityResponse {
|
||
pub entry: AdminWorkVisibilityEntryPayload,
|
||
}
|
||
|
||
#[derive(Clone, Debug, Serialize, Deserialize, Default, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminEditorAssetListQuery {
|
||
pub cursor: Option<String>,
|
||
pub owner_user_id: Option<String>,
|
||
pub keyword: Option<String>,
|
||
pub created_after: Option<String>,
|
||
pub created_before: Option<String>,
|
||
pub limit: Option<u32>,
|
||
}
|
||
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminEditorImageSequenceFramePayload {
|
||
pub image_src: String,
|
||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||
pub object_key: Option<String>,
|
||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||
pub asset_object_id: Option<String>,
|
||
pub width: u32,
|
||
pub height: u32,
|
||
}
|
||
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminEditorAssetPayload {
|
||
pub asset_id: String,
|
||
pub owner_user_id: String,
|
||
pub author_display_name: Option<String>,
|
||
pub author_public_user_code: Option<String>,
|
||
pub folder_id: String,
|
||
pub label: String,
|
||
pub asset_object_id: Option<String>,
|
||
pub image_src: String,
|
||
pub object_key: Option<String>,
|
||
pub width: u32,
|
||
pub height: u32,
|
||
pub source_type: String,
|
||
pub prompt: Option<String>,
|
||
pub actual_prompt: Option<String>,
|
||
pub model: Option<String>,
|
||
pub provider: Option<String>,
|
||
pub task_id: Option<String>,
|
||
pub group_task_id: Option<String>,
|
||
pub asset_kind: Option<String>,
|
||
pub generation_inputs: Option<Value>,
|
||
pub source_resource_id: Option<String>,
|
||
pub source_image_src: Option<String>,
|
||
pub source_object_key: Option<String>,
|
||
pub source_asset_object_id: Option<String>,
|
||
pub source_label: Option<String>,
|
||
pub thumbnail_src: Option<String>,
|
||
pub generation_cost_mud_points: u64,
|
||
pub created_at: String,
|
||
pub updated_at: String,
|
||
pub generator: String,
|
||
pub task_generator: String,
|
||
pub task_cost_mud_points: u64,
|
||
pub children: Vec<AdminEditorAssetPayload>,
|
||
pub image_sequence_frames: Option<Vec<AdminEditorImageSequenceFramePayload>>,
|
||
pub image_sequence_duration_ms: Option<u64>,
|
||
}
|
||
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminEditorAssetListResponse {
|
||
pub entries: Vec<AdminEditorAssetPayload>,
|
||
pub next_cursor: Option<String>,
|
||
}
|
||
|
||
#[derive(Clone, Debug, Serialize, Deserialize, Default, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminEditorShowcaseListQuery {
|
||
pub cursor: Option<String>,
|
||
pub owner_user_id: Option<String>,
|
||
pub review_status: Option<String>,
|
||
pub submitted_after: Option<String>,
|
||
pub submitted_before: Option<String>,
|
||
pub limit: Option<u32>,
|
||
}
|
||
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminEditorShowcaseAssetPayload {
|
||
pub showcase_id: String,
|
||
pub asset_id: String,
|
||
pub owner_user_id: String,
|
||
pub author_display_name: Option<String>,
|
||
pub author_public_user_code: Option<String>,
|
||
pub label: String,
|
||
pub image_src: String,
|
||
pub object_key: Option<String>,
|
||
pub width: u32,
|
||
pub height: u32,
|
||
pub prompt: Option<String>,
|
||
pub actual_prompt: Option<String>,
|
||
pub model: Option<String>,
|
||
pub provider: Option<String>,
|
||
pub task_id: Option<String>,
|
||
pub source_resource_id: Option<String>,
|
||
pub source_image_src: Option<String>,
|
||
pub source_object_key: Option<String>,
|
||
pub source_asset_object_id: Option<String>,
|
||
pub source_label: Option<String>,
|
||
pub asset_kind: Option<String>,
|
||
pub generation_inputs: Option<Value>,
|
||
pub thumbnail_src: Option<String>,
|
||
pub generation_cost_mud_points: u64,
|
||
pub refund_mud_points: u64,
|
||
pub review_status: String,
|
||
pub display_enabled: bool,
|
||
pub like_count: u64,
|
||
pub asset_deleted_while_pending: bool,
|
||
pub reviewed_by_admin_user_id: Option<String>,
|
||
pub review_note: Option<String>,
|
||
pub refund_ledger_id: Option<String>,
|
||
pub refund_completed_at: Option<String>,
|
||
pub submitted_at: String,
|
||
pub reviewed_at: Option<String>,
|
||
pub approved_at: Option<String>,
|
||
pub rejected_at: Option<String>,
|
||
pub updated_at: String,
|
||
pub showcase_category: Option<String>,
|
||
pub image_sequence_frames: Option<Vec<AdminEditorImageSequenceFramePayload>>,
|
||
pub image_sequence_duration_ms: Option<u64>,
|
||
}
|
||
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminEditorShowcaseListResponse {
|
||
pub entries: Vec<AdminEditorShowcaseAssetPayload>,
|
||
pub next_cursor: Option<String>,
|
||
}
|
||
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminEditorShowcaseReviewRequest {
|
||
pub showcase_id: String,
|
||
pub review_status: String,
|
||
pub review_note: Option<String>,
|
||
}
|
||
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminEditorShowcaseDisplayRequest {
|
||
pub showcase_id: String,
|
||
pub display_enabled: bool,
|
||
pub showcase_category: Option<String>,
|
||
}
|
||
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminEditorShowcaseAssetResponse {
|
||
pub entry: AdminEditorShowcaseAssetPayload,
|
||
}
|
||
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminEditorShowcaseCampaignPayload {
|
||
pub enabled: bool,
|
||
pub title: String,
|
||
pub image_src: String,
|
||
pub image_object_key: Option<String>,
|
||
pub image_width: Option<u32>,
|
||
pub image_height: Option<u32>,
|
||
pub prompt: String,
|
||
pub author: String,
|
||
pub cost_text: String,
|
||
pub updated_at: String,
|
||
}
|
||
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminEditorShowcaseCampaignResponse {
|
||
pub campaign: Option<AdminEditorShowcaseCampaignPayload>,
|
||
}
|
||
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminUpsertEditorShowcaseCampaignRequest {
|
||
pub enabled: bool,
|
||
pub title: String,
|
||
pub image_src: String,
|
||
pub image_object_key: Option<String>,
|
||
pub image_width: Option<u32>,
|
||
pub image_height: Option<u32>,
|
||
pub prompt: String,
|
||
pub author: String,
|
||
pub cost_text: String,
|
||
}
|
||
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminCreateEditorShowcaseCampaignImageUploadTicketRequest {
|
||
pub file_name: String,
|
||
pub content_type: String,
|
||
pub content_length: u64,
|
||
}
|
||
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminCreateEditorShowcaseCampaignImageUploadTicketResponse {
|
||
pub upload: AdminDirectUploadTicketPayload,
|
||
}
|
||
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminConfirmEditorShowcaseCampaignImageUploadRequest {
|
||
pub bucket: String,
|
||
pub object_key: String,
|
||
pub content_type: String,
|
||
pub content_length: u64,
|
||
}
|
||
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminDirectUploadTicketPayload {
|
||
pub bucket: String,
|
||
pub host: String,
|
||
pub object_key: String,
|
||
pub legacy_public_path: String,
|
||
pub form_fields: BTreeMap<String, String>,
|
||
}
|
||
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminLoginResponse {
|
||
pub token: String,
|
||
pub admin: AdminSessionPayload,
|
||
}
|
||
|
||
// 管理员会话只暴露页面展示和鉴权调试所需的最小字段。
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminSessionPayload {
|
||
pub subject: String,
|
||
pub username: String,
|
||
pub display_name: String,
|
||
pub roles: Vec<String>,
|
||
pub account_role: AdminAccountRole,
|
||
pub tab_permissions: Vec<String>,
|
||
pub action_permissions: Vec<String>,
|
||
pub issued_at: String,
|
||
pub expires_at: String,
|
||
}
|
||
|
||
/// 后台账号列表和写操作响应均复用该安全视图,不返回密码摘要。
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminAccountPayload {
|
||
pub account_id: String,
|
||
pub username: String,
|
||
pub display_name: String,
|
||
pub account_role: AdminAccountRole,
|
||
pub tab_permissions: Vec<String>,
|
||
pub action_permissions: Vec<String>,
|
||
pub enabled: bool,
|
||
pub token_version: u64,
|
||
pub created_by: String,
|
||
pub updated_by: String,
|
||
pub created_at: String,
|
||
pub updated_at: String,
|
||
}
|
||
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminAccountListResponse {
|
||
pub accounts: Vec<AdminAccountPayload>,
|
||
}
|
||
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminCreateAccountRequest {
|
||
pub username: String,
|
||
pub display_name: String,
|
||
pub password: String,
|
||
pub tab_permissions: Vec<String>,
|
||
#[serde(default)]
|
||
pub action_permissions: Vec<String>,
|
||
pub enabled: bool,
|
||
}
|
||
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminCreateAccountResponse {
|
||
pub account: AdminAccountPayload,
|
||
}
|
||
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminUpdateAccountRequest {
|
||
pub display_name: String,
|
||
#[serde(skip_serializing_if = "Option::is_none")]
|
||
pub password: Option<String>,
|
||
pub tab_permissions: Vec<String>,
|
||
pub action_permissions: Vec<String>,
|
||
pub enabled: bool,
|
||
}
|
||
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminUpdateAccountResponse {
|
||
pub account: AdminAccountPayload,
|
||
}
|
||
|
||
// 页面恢复登录态时读取当前管理员会话。
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminMeResponse {
|
||
pub admin: AdminSessionPayload,
|
||
}
|
||
|
||
// 后台概览统一返回服务信息与数据库信息两块,前端不再额外拼装。
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminOverviewResponse {
|
||
pub service: AdminServiceOverviewPayload,
|
||
pub database: AdminDatabaseOverviewPayload,
|
||
}
|
||
|
||
/// 后台 dashboard 查询参数;日期均使用北京时间日历日期 YYYY-MM-DD。
|
||
#[derive(Clone, Debug, Serialize, Deserialize, Default, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminDashboardQuery {
|
||
pub granularity: Option<String>,
|
||
/// day / week / month 兼容旧查询口径,表示被选中的日期。
|
||
pub anchor: Option<String>,
|
||
/// period 自定义时段起始日期,闭区间。
|
||
pub start_date: Option<String>,
|
||
/// period 自定义时段终止日期,闭区间。
|
||
pub end_date: Option<String>,
|
||
}
|
||
|
||
/// 后台 dashboard 返回运营指标、趋势图和读取告警。
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminDashboardResponse {
|
||
pub range: AdminDashboardRangePayload,
|
||
pub metrics: AdminDashboardMetricsPayload,
|
||
pub charts: Vec<AdminDashboardChartPayload>,
|
||
pub operations: AdminDashboardOperationsPayload,
|
||
pub warnings: Vec<String>,
|
||
pub generated_at: String,
|
||
}
|
||
|
||
/// dashboard 当前筛选范围,所有日/周/月图表共用同一时间窗。
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminDashboardRangePayload {
|
||
pub granularity: String,
|
||
pub anchor_date: String,
|
||
pub period_start_date: String,
|
||
pub period_end_date: String,
|
||
pub period_label: String,
|
||
}
|
||
|
||
/// dashboard 首屏核心指标。
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminDashboardMetricsPayload {
|
||
pub generated_assets: u64,
|
||
pub consumed_mud_points: u64,
|
||
pub total_registered_users: u64,
|
||
pub new_registered_users: u64,
|
||
pub day1_retention: AdminDashboardRetentionPayload,
|
||
pub day7_retention: AdminDashboardRetentionPayload,
|
||
pub visit_users: u64,
|
||
pub total_visit_users: u64,
|
||
pub visit_count: u64,
|
||
pub total_visit_count: u64,
|
||
pub current_users: u64,
|
||
pub new_user_payment_conversion: AdminDashboardPaymentConversionPayload,
|
||
}
|
||
|
||
/// dashboard 新增用户付费转化;rate_basis_points 取值 0..=10000,1 表示 0.01%。
|
||
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminDashboardPaymentConversionPayload {
|
||
pub paid_users: u64,
|
||
pub new_registered_users: u64,
|
||
pub rate_basis_points: u32,
|
||
}
|
||
|
||
/// dashboard 新增用户留存;rate_basis_points 取值 0..=10000,1 表示 0.01%。
|
||
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminDashboardRetentionPayload {
|
||
pub eligible_users: u64,
|
||
pub retained_users: u64,
|
||
pub rate_basis_points: u32,
|
||
}
|
||
|
||
/// dashboard 图表定义,前端只渲染后端给出的 bucket。
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminDashboardChartPayload {
|
||
pub id: String,
|
||
pub title: String,
|
||
pub unit: String,
|
||
pub total: u64,
|
||
pub buckets: Vec<AdminDashboardChartBucketPayload>,
|
||
}
|
||
|
||
/// dashboard 单个趋势桶。
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminDashboardChartBucketPayload {
|
||
pub key: String,
|
||
pub label: String,
|
||
pub value: u64,
|
||
}
|
||
|
||
/// dashboard 运营页签的常用运营看板。
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminDashboardOperationsPayload {
|
||
pub cards: Vec<AdminDashboardOperationMetricPayload>,
|
||
pub asset_kind_breakdown: Vec<AdminDashboardBreakdownRowPayload>,
|
||
pub module_visit_breakdown: Vec<AdminDashboardBreakdownRowPayload>,
|
||
}
|
||
|
||
/// dashboard 运营指标卡。
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminDashboardOperationMetricPayload {
|
||
pub id: String,
|
||
pub label: String,
|
||
pub value: u64,
|
||
pub unit: String,
|
||
}
|
||
|
||
/// dashboard 常用排行 / 分布行。
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminDashboardBreakdownRowPayload {
|
||
pub key: String,
|
||
pub label: String,
|
||
pub value: u64,
|
||
}
|
||
|
||
// 服务概览描述当前 api-server 与 SpacetimeDB 连接配置。
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminServiceOverviewPayload {
|
||
pub bind_host: String,
|
||
pub bind_port: u16,
|
||
pub jwt_issuer: String,
|
||
pub admin_enabled: bool,
|
||
pub spacetime_server_url: String,
|
||
pub spacetime_database: String,
|
||
}
|
||
|
||
// 数据库概览返回真实数据库元信息、表清单与统计错误。
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminDatabaseOverviewPayload {
|
||
pub database_identity: Option<String>,
|
||
pub owner_identity: Option<String>,
|
||
pub host_type: Option<String>,
|
||
pub schema_table_names: Vec<String>,
|
||
pub table_stats: Vec<AdminDatabaseTableStatPayload>,
|
||
pub fetch_errors: Vec<String>,
|
||
}
|
||
|
||
// 单表统计允许成功和失败并存,避免某张表失败导致整页概览不可用。
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminDatabaseTableStatPayload {
|
||
pub table_name: String,
|
||
pub row_count: Option<u64>,
|
||
pub error_message: Option<String>,
|
||
}
|
||
|
||
// 后台表清单独立用于“表查询”页,避免页面必须先拉完整总览。
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminDatabaseTableListResponse {
|
||
pub tables: Vec<String>,
|
||
pub fetch_errors: Vec<String>,
|
||
}
|
||
|
||
// 后台通用表查询参数,用户输入不进入 SQL,只在 API Server 内存中过滤。
|
||
// filters 支持两种 JSON 形式:object(列名到等值,如 {"user_id":"u1"},兼容旧入口)
|
||
// 与条件数组(如 [{"column":"points","op":"gt","value":"5"}])。条件数组运算符包含
|
||
// eq、ne、gt、gte、lt、lte、contains、notContains、startsWith、endsWith、in、notIn、
|
||
// isEmpty、isNotEmpty;允许同列多条件,全部条件之间为 AND。
|
||
#[derive(Clone, Debug, Serialize, Deserialize, Default, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminDatabaseTableRowsQuery {
|
||
pub limit: Option<u32>,
|
||
pub page: Option<u32>,
|
||
pub search: Option<String>,
|
||
pub filters: Option<String>,
|
||
pub sort_column: Option<String>,
|
||
pub sort_direction: Option<String>,
|
||
}
|
||
|
||
// 后台通用表查询响应,cells 使用列名映射,raw 保留原始行便于详情排障。
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminDatabaseTableRowsResponse {
|
||
pub table_name: String,
|
||
pub columns: Vec<String>,
|
||
pub rows: Vec<AdminDatabaseTableRowPayload>,
|
||
pub total_returned: usize,
|
||
pub total_matched: usize,
|
||
pub limit: u32,
|
||
pub page: u32,
|
||
pub scanned_count: usize,
|
||
pub scan_limit: u32,
|
||
pub scan_limit_reached: bool,
|
||
}
|
||
|
||
// 单行查询结果,值统一用 JSON 承载以兼容不同表字段类型。
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminDatabaseTableRowPayload {
|
||
pub cells: Value,
|
||
pub raw: Value,
|
||
}
|
||
|
||
/// API Key 专用查询参数。该接口只返回安全元数据,不复用通用表查询的
|
||
/// 原始 JSON 行,以避免后台详情页意外暴露 key_hash 或密文。
|
||
#[derive(Clone, Debug, Serialize, Deserialize, Default, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminExternalApiKeyListQuery {
|
||
pub owner_user_id: Option<String>,
|
||
pub public_user_code: Option<String>,
|
||
pub key_id: Option<String>,
|
||
pub name: Option<String>,
|
||
pub key_prefix: Option<String>,
|
||
pub created_after: Option<String>,
|
||
pub created_before: Option<String>,
|
||
pub status: Option<String>,
|
||
pub limit: Option<u32>,
|
||
pub offset: Option<u32>,
|
||
pub sort_column: Option<String>,
|
||
pub sort_direction: Option<String>,
|
||
}
|
||
|
||
/// API Key 后台安全元数据。永不包含 key_hash、明文 Key 或密文。
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminExternalApiKeyPayload {
|
||
pub key_id: String,
|
||
pub owner_user_id: String,
|
||
pub name: String,
|
||
pub key_prefix: String,
|
||
pub scopes: Vec<String>,
|
||
pub created_at: String,
|
||
pub last_used_at: Option<String>,
|
||
pub revoked_at: Option<String>,
|
||
pub updated_at: String,
|
||
pub status: String,
|
||
}
|
||
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminExternalApiKeyListResponse {
|
||
pub keys: Vec<AdminExternalApiKeyPayload>,
|
||
pub total: usize,
|
||
pub limit: u32,
|
||
pub offset: u32,
|
||
pub scanned_count: usize,
|
||
pub scan_limit: u32,
|
||
pub scan_limit_reached: bool,
|
||
}
|
||
|
||
// 调试请求只允许同源路径、受控请求头和有限请求体。
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminDebugHttpRequest {
|
||
pub method: String,
|
||
pub path: String,
|
||
pub headers: Option<Vec<AdminDebugHeaderInput>>,
|
||
pub body: Option<String>,
|
||
}
|
||
|
||
// 调试请求头使用显式结构,避免页面直接塞任意对象。
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminDebugHeaderInput {
|
||
pub name: String,
|
||
pub value: String,
|
||
}
|
||
|
||
// 调试响应回显状态、响应头与文本/JSON 预览,便于后台排查接口问题。
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminDebugHttpResponse {
|
||
pub status: u16,
|
||
pub status_text: String,
|
||
pub headers: Vec<AdminDebugHeaderInput>,
|
||
pub body_text: String,
|
||
pub body_json: Option<Value>,
|
||
}
|
||
|
||
// 后台埋点明细查询参数只保留运营筛选需要的只读字段。
|
||
#[derive(Clone, Debug, Serialize, Deserialize, Default, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminTrackingEventListQuery {
|
||
pub event_key: Option<String>,
|
||
pub user_id: Option<String>,
|
||
pub scope_kind: Option<String>,
|
||
pub scope_id: Option<String>,
|
||
/// 北京时间日历日期 YYYY-MM-DD,按 tracking_event.day_key 闭区间过滤。
|
||
pub start_date: Option<String>,
|
||
/// 北京时间日历日期 YYYY-MM-DD,按 tracking_event.day_key 闭区间过滤。
|
||
pub end_date: Option<String>,
|
||
pub limit: Option<u32>,
|
||
/// 后台导出专用开关;列表查询仍使用较小上限,避免日常误刷拖慢数据库。
|
||
pub export_all: Option<bool>,
|
||
}
|
||
|
||
// 单条埋点原始事件明细,字段与 tracking_event 表一一对应并补充事件名称。
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminTrackingEventEntryPayload {
|
||
pub event_id: String,
|
||
pub event_key: String,
|
||
pub event_title: String,
|
||
pub scope_kind: String,
|
||
pub scope_id: String,
|
||
pub day_key: i64,
|
||
pub user_id: Option<String>,
|
||
pub owner_user_id: Option<String>,
|
||
pub profile_id: Option<String>,
|
||
pub module_key: Option<String>,
|
||
pub metadata_json: String,
|
||
pub occurred_at: String,
|
||
}
|
||
|
||
// 后台埋点明细列表响应,前端导出 Excel 时直接使用 entries。
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminTrackingEventListResponse {
|
||
pub entries: Vec<AdminTrackingEventEntryPayload>,
|
||
}
|
||
|
||
// 后台埋点 key 候选来自真实库中已经落库的 tracking_event,静态标题仅用于展示补充。
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminTrackingEventKeyPayload {
|
||
pub event_key: String,
|
||
pub event_title: String,
|
||
pub scope_kinds: Vec<String>,
|
||
}
|
||
|
||
// 后台埋点 key 列表响应,任务配置页和埋点明细页共用。
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminTrackingEventKeyListResponse {
|
||
pub event_keys: Vec<AdminTrackingEventKeyPayload>,
|
||
}
|
||
|
||
/// 后台充值订单筛选参数。陶泥号由 BFF 解析为内部用户 ID 后再访问运行时读模型。
|
||
#[derive(Clone, Debug, Serialize, Deserialize, Default, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminRechargeOrderListQuery {
|
||
pub order_id: Option<String>,
|
||
pub provider_transaction_id: Option<String>,
|
||
pub user_id: Option<String>,
|
||
pub public_user_code: Option<String>,
|
||
pub payment_channel: Option<String>,
|
||
pub status: Option<String>,
|
||
pub created_after: Option<String>,
|
||
pub created_before: Option<String>,
|
||
pub limit: Option<u32>,
|
||
}
|
||
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminUserSummaryPayload {
|
||
pub user_id: String,
|
||
pub public_user_code: String,
|
||
pub display_name: String,
|
||
pub avatar_url: Option<String>,
|
||
}
|
||
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminWalletManualRestrictionPayload {
|
||
pub frozen: bool,
|
||
pub reason: String,
|
||
pub created_by_admin_user_id: String,
|
||
pub created_by_admin_display_name: String,
|
||
pub created_at_micros: i64,
|
||
pub updated_by_admin_user_id: String,
|
||
pub updated_by_admin_display_name: String,
|
||
pub updated_at_micros: i64,
|
||
}
|
||
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminProfileWalletPayload {
|
||
pub user_id: String,
|
||
pub total_balance: u64,
|
||
pub spendable_balance: u64,
|
||
pub daily_free_points: u64,
|
||
pub membership_limited_points: u64,
|
||
pub permanent_points: u64,
|
||
pub held_points: u64,
|
||
pub refund_debt_points: u64,
|
||
pub manual_frozen: bool,
|
||
pub refund_debt_frozen: bool,
|
||
pub wallet_frozen: bool,
|
||
pub manual_restriction: Option<AdminWalletManualRestrictionPayload>,
|
||
}
|
||
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminRechargeRefundPayload {
|
||
pub out_refund_no: String,
|
||
pub provider_refund_id: String,
|
||
pub provider_transaction_id: String,
|
||
pub provider_status: String,
|
||
pub total_cents: u64,
|
||
pub refund_cents: u64,
|
||
pub payer_refund_cents: u64,
|
||
pub success_at_micros: Option<i64>,
|
||
pub first_observed_at_micros: i64,
|
||
pub updated_at_micros: i64,
|
||
pub observation_source: String,
|
||
pub target_recovery_points: u64,
|
||
pub recovered_points: u64,
|
||
pub unrecovered_points: u64,
|
||
pub recovery_status: String,
|
||
pub last_error_code: Option<String>,
|
||
pub manual_review_resolved_by_admin_user_id: Option<String>,
|
||
pub manual_review_resolution_reason: Option<String>,
|
||
pub manual_review_resolved_at_micros: Option<i64>,
|
||
pub manual_review_resolved_error_code: Option<String>,
|
||
}
|
||
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminRechargeRefundHoldPayload {
|
||
pub out_refund_no: String,
|
||
pub refund_cents: u64,
|
||
pub held_points: u64,
|
||
pub status: String,
|
||
pub admin_user_id: String,
|
||
pub reason: String,
|
||
pub created_at_micros: i64,
|
||
pub updated_at_micros: i64,
|
||
}
|
||
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminRechargeOrderEntryPayload {
|
||
pub order_id: String,
|
||
pub user_id: String,
|
||
pub user: Option<AdminUserSummaryPayload>,
|
||
pub product_id: String,
|
||
pub product_title: String,
|
||
pub product_kind: String,
|
||
pub amount_cents: u64,
|
||
pub status: String,
|
||
pub payment_channel: String,
|
||
pub paid_at_micros: Option<i64>,
|
||
pub provider_transaction_id: Option<String>,
|
||
pub created_at_micros: i64,
|
||
pub points_delta: i64,
|
||
pub cumulative_success_refund_cents: u64,
|
||
pub target_recovery_points: u64,
|
||
pub recovered_points: u64,
|
||
pub unrecovered_points: u64,
|
||
pub recovery_status: Option<String>,
|
||
pub wallet: AdminProfileWalletPayload,
|
||
pub refunds: Vec<AdminRechargeRefundPayload>,
|
||
pub active_hold: Option<AdminRechargeRefundHoldPayload>,
|
||
pub remaining_refundable_cents: u64,
|
||
pub refund_eligible: bool,
|
||
pub refund_block_reason_code: Option<String>,
|
||
}
|
||
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminRechargeOrderListResponse {
|
||
pub entries: Vec<AdminRechargeOrderEntryPayload>,
|
||
}
|
||
|
||
#[derive(Clone, Debug, Serialize, Deserialize, Default, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminUserDetailQuery {
|
||
pub user_id: Option<String>,
|
||
pub public_user_code: Option<String>,
|
||
}
|
||
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminUserDetailResponse {
|
||
pub user_id: String,
|
||
pub public_user_code: String,
|
||
pub display_name: String,
|
||
pub avatar_url: Option<String>,
|
||
pub phone_number_masked: Option<String>,
|
||
pub login_method: String,
|
||
pub binding_status: String,
|
||
pub phone_bound: bool,
|
||
pub wechat_bound: bool,
|
||
pub historical_consumed_points: u64,
|
||
pub can_reconcile_consumption: bool,
|
||
pub wallet: AdminProfileWalletPayload,
|
||
pub recharge_orders: Vec<AdminRechargeOrderEntryPayload>,
|
||
}
|
||
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminUserConsumptionReconcileRequest {
|
||
pub user_id: String,
|
||
}
|
||
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminUserConsumptionReconcileResponse {
|
||
pub user_id: String,
|
||
pub previous_historical_consumed_points: Option<u64>,
|
||
pub historical_consumed_points: u64,
|
||
pub changed: bool,
|
||
pub reconciled_at_micros: i64,
|
||
}
|
||
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminUserConsumptionProjectionInitializeResponse {
|
||
pub scanned_ledger_count: u64,
|
||
pub projected_user_count: u64,
|
||
pub inserted_projection_count: u64,
|
||
pub updated_projection_count: u64,
|
||
pub initialized_at_micros: i64,
|
||
}
|
||
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminRechargeRefundPreviewRequest {
|
||
pub order_id: String,
|
||
pub refund_amount_cents: u64,
|
||
}
|
||
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminRechargeRefundExecuteRequest {
|
||
pub order_id: String,
|
||
pub refund_amount_cents: u64,
|
||
pub request_id: String,
|
||
pub reason: Option<String>,
|
||
}
|
||
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminRechargeRefundRegisterRequest {
|
||
pub out_refund_no: String,
|
||
}
|
||
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminRechargeRefundManualReviewResolveRequest {
|
||
pub out_refund_no: String,
|
||
pub reason: String,
|
||
pub expected_error_code: String,
|
||
}
|
||
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminWalletRestrictionRequest {
|
||
pub user_id: String,
|
||
pub frozen: bool,
|
||
pub reason: String,
|
||
}
|
||
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminWechatPaymentCheckPayload {
|
||
pub verified: bool,
|
||
pub trade_state: String,
|
||
pub transaction_id: Option<String>,
|
||
pub amount_total_cents: Option<u64>,
|
||
pub known_refunds_refreshed: u32,
|
||
}
|
||
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminRechargeRefundPreviewResponse {
|
||
pub order: AdminRechargeOrderEntryPayload,
|
||
pub payment_check: AdminWechatPaymentCheckPayload,
|
||
pub refund_amount_cents: u64,
|
||
pub incremental_recovery_points: u64,
|
||
pub remaining_refundable_cents: u64,
|
||
pub can_submit: bool,
|
||
pub block_reason_code: Option<String>,
|
||
}
|
||
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminRechargeRefundActionResponse {
|
||
pub out_refund_no: String,
|
||
pub provider_status: String,
|
||
pub result_code: String,
|
||
pub provider_status_unknown: bool,
|
||
pub order: AdminRechargeOrderEntryPayload,
|
||
}
|
||
|
||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||
#[serde(rename_all = "camelCase")]
|
||
pub struct AdminWalletRestrictionResponse {
|
||
pub wallet: AdminProfileWalletPayload,
|
||
}
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use serde_json::json;
|
||
|
||
use super::{
|
||
AdminConfirmEditorShowcaseCampaignImageUploadRequest, AdminEditorImageSequenceFramePayload,
|
||
AdminEditorShowcaseAssetPayload, AdminRechargeRefundManualReviewResolveRequest,
|
||
};
|
||
|
||
#[test]
|
||
fn refund_manual_review_resolution_request_uses_camel_case_contract() {
|
||
let value = serde_json::to_value(AdminRechargeRefundManualReviewResolveRequest {
|
||
out_refund_no: "refund-1".to_string(),
|
||
reason: "已核对微信商户平台原始账单".to_string(),
|
||
expected_error_code: "provider_transaction_id_mismatch".to_string(),
|
||
})
|
||
.expect("request should serialize");
|
||
|
||
assert_eq!(value["outRefundNo"], json!("refund-1"));
|
||
assert_eq!(
|
||
value["expectedErrorCode"],
|
||
json!("provider_transaction_id_mismatch")
|
||
);
|
||
assert!(value.get("out_refund_no").is_none());
|
||
}
|
||
|
||
#[test]
|
||
fn editor_showcase_campaign_image_confirm_uses_camel_case_contract() {
|
||
let value = serde_json::to_value(AdminConfirmEditorShowcaseCampaignImageUploadRequest {
|
||
bucket: "genarrative-release".to_string(),
|
||
object_key: "generated-character-drafts/editor/showcase-campaign/current/card.png"
|
||
.to_string(),
|
||
content_type: "image/png".to_string(),
|
||
content_length: 1024,
|
||
})
|
||
.expect("request should serialize");
|
||
|
||
assert_eq!(
|
||
value["objectKey"],
|
||
json!("generated-character-drafts/editor/showcase-campaign/current/card.png")
|
||
);
|
||
assert_eq!(value["contentType"], json!("image/png"));
|
||
assert_eq!(value["contentLength"], json!(1024));
|
||
assert!(value.get("object_key").is_none());
|
||
}
|
||
|
||
#[test]
|
||
fn editor_showcase_asset_payload_serializes_author_fields_as_camel_case() {
|
||
let payload = AdminEditorShowcaseAssetPayload {
|
||
showcase_id: "showcase-1".to_string(),
|
||
asset_id: "asset-1".to_string(),
|
||
owner_user_id: "user-1".to_string(),
|
||
author_display_name: Some("作者昵称".to_string()),
|
||
author_public_user_code: Some("SY-00000042".to_string()),
|
||
label: "角色形象 1".to_string(),
|
||
image_src: "/generated-character-drafts/editor/spec.png".to_string(),
|
||
object_key: Some("generated-character-drafts/editor/spec.png".to_string()),
|
||
width: 1024,
|
||
height: 1024,
|
||
prompt: None,
|
||
actual_prompt: None,
|
||
model: None,
|
||
provider: None,
|
||
task_id: None,
|
||
source_resource_id: Some("source-resource-1".to_string()),
|
||
source_image_src: Some("/generated-character-drafts/editor/source.png".to_string()),
|
||
source_object_key: Some("generated-character-drafts/editor/source.png".to_string()),
|
||
source_asset_object_id: Some("source-asset-object-1".to_string()),
|
||
source_label: Some("来源素材".to_string()),
|
||
asset_kind: Some("character".to_string()),
|
||
generation_inputs: None,
|
||
thumbnail_src: Some("/generated-character-drafts/editor/spec-thumb.png".to_string()),
|
||
generation_cost_mud_points: 12,
|
||
refund_mud_points: 6,
|
||
review_status: "pending".to_string(),
|
||
display_enabled: false,
|
||
like_count: 0,
|
||
asset_deleted_while_pending: false,
|
||
reviewed_by_admin_user_id: None,
|
||
review_note: None,
|
||
refund_ledger_id: None,
|
||
refund_completed_at: None,
|
||
submitted_at: "2026-07-04T10:00:00.000Z".to_string(),
|
||
reviewed_at: None,
|
||
approved_at: None,
|
||
rejected_at: None,
|
||
updated_at: "2026-07-04T10:00:00.000Z".to_string(),
|
||
showcase_category: None,
|
||
image_sequence_frames: Some(vec![
|
||
AdminEditorImageSequenceFramePayload {
|
||
image_src: "/generated/action/frame-01.png".to_string(),
|
||
object_key: None,
|
||
asset_object_id: None,
|
||
width: 192,
|
||
height: 256,
|
||
},
|
||
AdminEditorImageSequenceFramePayload {
|
||
image_src: "/generated/action/frame-02.png".to_string(),
|
||
object_key: None,
|
||
asset_object_id: None,
|
||
width: 192,
|
||
height: 256,
|
||
},
|
||
]),
|
||
image_sequence_duration_ms: Some(5_000),
|
||
};
|
||
|
||
let value = serde_json::to_value(payload).expect("payload should serialize");
|
||
|
||
assert_eq!(value["authorDisplayName"], json!("作者昵称"));
|
||
assert_eq!(value["authorPublicUserCode"], json!("SY-00000042"));
|
||
assert_eq!(
|
||
value["thumbnailSrc"],
|
||
json!("/generated-character-drafts/editor/spec-thumb.png")
|
||
);
|
||
assert_eq!(value["sourceResourceId"], json!("source-resource-1"));
|
||
assert_eq!(
|
||
value["sourceImageSrc"],
|
||
json!("/generated-character-drafts/editor/source.png")
|
||
);
|
||
assert_eq!(
|
||
value["sourceObjectKey"],
|
||
json!("generated-character-drafts/editor/source.png")
|
||
);
|
||
assert_eq!(value["sourceAssetObjectId"], json!("source-asset-object-1"));
|
||
assert_eq!(value["sourceLabel"], json!("来源素材"));
|
||
assert_eq!(
|
||
value["imageSequenceFrames"].as_array().map(Vec::len),
|
||
Some(2)
|
||
);
|
||
assert_eq!(value["imageSequenceDurationMs"], json!(5_000));
|
||
assert!(value.get("author_display_name").is_none());
|
||
assert!(value.get("author_public_user_code").is_none());
|
||
assert!(value.get("thumbnail_src").is_none());
|
||
assert!(value.get("source_resource_id").is_none());
|
||
assert!(value.get("source_image_src").is_none());
|
||
assert!(value.get("source_object_key").is_none());
|
||
assert!(value.get("source_asset_object_id").is_none());
|
||
assert!(value.get("source_label").is_none());
|
||
assert!(value.get("image_sequence_frames").is_none());
|
||
assert!(value.get("image_sequence_duration_ms").is_none());
|
||
}
|
||
}
|
||
/// AGC 模型目录仅在后台返回实际模型名。
|
||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||
#[serde(rename_all = "camelCase", deny_unknown_fields)]
|
||
pub struct AdminAgcModel {
|
||
pub id: String,
|
||
pub alias: String,
|
||
pub model_id: String,
|
||
pub enabled: bool,
|
||
}
|
||
|
||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||
#[serde(rename_all = "camelCase", deny_unknown_fields)]
|
||
pub struct AdminAgcModelCatalog {
|
||
pub revision: u64,
|
||
pub default_model_id: String,
|
||
pub models: Vec<AdminAgcModel>,
|
||
}
|