This commit is contained in:
2026-05-01 20:29:09 +08:00
parent 8718472dbd
commit 87fbf41fab
137 changed files with 2922 additions and 989 deletions

View File

@@ -1835,6 +1835,10 @@ mod tests {
payload["user"]["loginMethod"],
Value::String("password".to_string())
);
assert_eq!(
payload["user"]["createdAt"],
Value::String(seed_user.created_at)
);
assert!(payload["token"].as_str().is_some());
}
@@ -2114,6 +2118,7 @@ mod tests {
payload["user"]["phoneNumberMasked"],
Value::String("138****8000".to_string())
);
assert!(payload["user"]["createdAt"].as_str().is_some());
assert_eq!(payload["created"], Value::Bool(true));
assert!(payload["referral"].is_null());
}

View File

@@ -29,7 +29,7 @@ where
}
}
/// 资产操作统一预扣陶泥币;扣费流水 ID 由业务资源 ID 参与构造,保证重试幂等。
/// 资产操作统一预扣光点;扣费流水 ID 由业务资源 ID 参与构造,保证重试幂等。
async fn consume_asset_operation_points(
state: &AppState,
owner_user_id: &str,
@@ -79,7 +79,7 @@ async fn refund_asset_operation_points(
asset_kind,
asset_id,
error = %error,
"资产操作失败后的陶泥币退款失败"
"资产操作失败后的光点退款失败"
);
}
}
@@ -89,10 +89,10 @@ pub(crate) fn map_asset_operation_wallet_error(error: SpacetimeClientError) -> A
tracing::warn!(
provider = "profile-wallet",
error = %message,
"资产操作陶泥币预扣失败"
"资产操作光点预扣失败"
);
let status = match &error {
SpacetimeClientError::Procedure(message) if message.contains("陶泥币余额不足") => {
SpacetimeClientError::Procedure(message) if message.contains("光点余额不足") => {
StatusCode::CONFLICT
}
_ => StatusCode::BAD_GATEWAY,

View File

@@ -12,6 +12,7 @@ pub fn map_auth_user_payload(user: AuthUser) -> AuthUserPayload {
login_method: user.login_method.as_str().to_string(),
binding_status: user.binding_status.as_str().to_string(),
wechat_bound: user.wechat_bound,
created_at: user.created_at,
}
}

View File

@@ -20,7 +20,7 @@ pub async fn get_public_user_by_code(
.get_user_by_public_user_code(&code)
.map_err(map_public_user_search_error)?
.ok_or_else(|| {
AppError::from_status(StatusCode::NOT_FOUND).with_message("未找到对应陶泥号用户")
AppError::from_status(StatusCode::NOT_FOUND).with_message("未找到对应百梦号用户")
})?;
Ok(json_success_body(
@@ -60,7 +60,7 @@ pub async fn get_public_user_by_id(
fn map_public_user_search_error(error: module_auth::PasswordEntryError) -> AppError {
match error {
module_auth::PasswordEntryError::InvalidPublicUserCode => {
AppError::from_status(StatusCode::BAD_REQUEST).with_message("陶泥号格式不正确")
AppError::from_status(StatusCode::BAD_REQUEST).with_message("百梦号格式不正确")
}
module_auth::PasswordEntryError::Store(_)
| module_auth::PasswordEntryError::PasswordHash(_)

View File

@@ -3462,7 +3462,7 @@ fn resolve_author_public_user_code(
request_context,
AppError::from_status(StatusCode::INTERNAL_SERVER_ERROR).with_details(json!({
"provider": "custom-world-library",
"message": format!("作者陶泥号读取失败:{error}"),
"message": format!("作者百梦号读取失败:{error}"),
})),
)
})?
@@ -3473,7 +3473,7 @@ fn resolve_author_public_user_code(
request_context,
AppError::from_status(StatusCode::UNAUTHORIZED).with_details(json!({
"provider": "custom-world-library",
"message": "当前登录用户缺少陶泥",
"message": "当前登录用户缺少百梦",
})),
)
})

View File

@@ -41,7 +41,7 @@ pub async fn generate_custom_world_foundation_draft(
emit_foundation_draft_progress(
&mut on_progress,
"整理世界骨架",
"正在根据陶泥主锚点生成第一版世界框架。",
"正在根据百梦主锚点生成第一版世界框架。",
12,
);
let mut framework = request_foundation_json_stage(

View File

@@ -49,6 +49,7 @@ mod prompt;
mod puzzle;
mod puzzle_agent_turn;
mod refresh_session;
mod registration_reward;
mod request_context;
mod response_headers;
mod runtime_browse_history;

View File

@@ -39,6 +39,14 @@ pub async fn password_entry(
state.password_entry_service().execute(input).await
}
.map_err(map_password_entry_error)?;
if result.created {
crate::registration_reward::grant_new_user_registration_wallet_reward(
&state,
&request_context,
&result.user.id,
)
.await;
}
let session_client = resolve_session_client_context(&headers);
let signed_session = create_password_auth_session(&state, &result.user, &session_client)?;
state
@@ -80,7 +88,7 @@ fn map_password_entry_error(error: PasswordEntryError) -> AppError {
"field": "password",
})),
PasswordEntryError::InvalidPublicUserCode => AppError::from_status(StatusCode::BAD_REQUEST)
.with_message("陶泥号格式不正确")
.with_message("百梦号格式不正确")
.with_details(json!({
"field": "phone",
})),

View File

@@ -149,6 +149,14 @@ pub async fn phone_login(
}
};
let created = result.created;
if created {
crate::registration_reward::grant_new_user_registration_wallet_reward(
&state,
&request_context,
&result.user.id,
)
.await;
}
let referral = if created {
bind_referral_invite_code_on_registration(
&state,

View File

@@ -10,7 +10,7 @@ use crate::creation_agent_anchor_templates::{
};
use crate::creation_agent_chat::render_quick_fill_extra_rules;
pub(crate) const BIG_FISH_AGENT_SYSTEM_PROMPT: &str = r#"你是一个负责和陶泥主共创“大鱼吃小鱼”竖屏玩法的中文创意策划。
pub(crate) const BIG_FISH_AGENT_SYSTEM_PROMPT: &str = r#"你是一个负责和百梦主共创“大鱼吃小鱼”竖屏玩法的中文创意策划。
你必须把用户灵感收束成可以编译为可玩草稿的玩法、生态视觉、成长阶梯和风险节奏。

View File

@@ -12,7 +12,7 @@ use crate::creation_agent_chat::render_quick_fill_extra_rules;
/// 拼图共创 Agent 的系统提示词。
///
/// 这里作为拼图聊天提示词主源,业务文件只负责调用 LLM、解析结果和写回状态。
pub(crate) const PUZZLE_AGENT_SYSTEM_PROMPT: &str = r#"你是一个负责和陶泥主共创拼图画面的中文创意策划。
pub(crate) const PUZZLE_AGENT_SYSTEM_PROMPT: &str = r#"你是一个负责和百梦主共创拼图画面的中文创意策划。
你要帮助用户把一句灵感逐步收束成可以发布成拼图关卡的视觉方案。

View File

@@ -14,7 +14,7 @@ pub(crate) const PUZZLE_TEXT_TO_IMAGE_PROMPT_MAX_CHARS: usize = 500;
const PUZZLE_IMAGE_LEVEL_NAME_MAX_CHARS: usize = 40;
const PUZZLE_IMAGE_PROMPT_FALLBACK: &str = "清晰、有辨识度的拼图画面";
/// 根据拼图关卡名和陶泥主输入构造最终发给图片模型的提示词。
/// 根据拼图关卡名和百梦主输入构造最终发给图片模型的提示词。
pub(crate) fn build_puzzle_image_prompt(level_name: &str, prompt: &str) -> String {
let level_name =
truncate_puzzle_prompt_segment(level_name.trim(), PUZZLE_IMAGE_LEVEL_NAME_MAX_CHARS);

View File

@@ -3620,7 +3620,7 @@ mod tests {
}));
let other_error = AppError::from_status(StatusCode::BAD_GATEWAY).with_details(json!({
"provider": "spacetimedb",
"message": "陶泥币余额不足",
"message": "光点余额不足",
}));
assert!(should_sync_puzzle_freeze_boundary(&invalid_operation, true));

View File

@@ -0,0 +1,30 @@
#[cfg(not(test))]
use tracing::warn;
use crate::{request_context::RequestContext, state::AppState};
pub async fn grant_new_user_registration_wallet_reward(
state: &AppState,
request_context: &RequestContext,
user_id: &str,
) {
#[cfg(test)]
{
let _ = (state, request_context, user_id);
}
#[cfg(not(test))]
if let Err(error) = state
.spacetime_client()
.grant_new_user_registration_wallet_reward(user_id.to_string())
.await
{
warn!(
request_id = request_context.request_id(),
operation = request_context.operation(),
user_id = user_id,
error = %error,
"新用户注册光点赠送失败,注册流程继续"
);
}
}

View File

@@ -20,6 +20,7 @@ use shared_contracts::runtime::{
PROFILE_WALLET_LEDGER_SOURCE_TYPE_ASSET_OPERATION_REFUND,
PROFILE_WALLET_LEDGER_SOURCE_TYPE_INVITE_INVITEE_REWARD,
PROFILE_WALLET_LEDGER_SOURCE_TYPE_INVITE_INVITER_REWARD,
PROFILE_WALLET_LEDGER_SOURCE_TYPE_NEW_USER_REGISTRATION_REWARD,
PROFILE_WALLET_LEDGER_SOURCE_TYPE_POINTS_RECHARGE,
PROFILE_WALLET_LEDGER_SOURCE_TYPE_PUZZLE_AUTHOR_INCENTIVE_CLAIM,
PROFILE_WALLET_LEDGER_SOURCE_TYPE_REDEEM_CODE_REWARD,
@@ -27,10 +28,10 @@ use shared_contracts::runtime::{
ProfileInviteCodeAdminResponse, ProfileMembershipBenefitResponse, ProfileMembershipResponse,
ProfilePlayStatsResponse, ProfilePlayedWorkSummaryResponse, ProfileRechargeCenterResponse,
ProfileRechargeOrderResponse, ProfileRechargeProductResponse, ProfileRedeemCodeAdminResponse,
ProfileReferralInviteCenterResponse, ProfileWalletLedgerEntryResponse,
ProfileWalletLedgerResponse, RedeemProfileReferralInviteCodeRequest,
RedeemProfileReferralInviteCodeResponse, RedeemProfileRewardCodeRequest,
RedeemProfileRewardCodeResponse,
ProfileReferralInviteCenterResponse, ProfileReferralInvitedUserResponse,
ProfileWalletLedgerEntryResponse, ProfileWalletLedgerResponse,
RedeemProfileReferralInviteCodeRequest, RedeemProfileReferralInviteCodeResponse,
RedeemProfileRewardCodeRequest, RedeemProfileRewardCodeResponse,
};
use spacetime_client::SpacetimeClientError;
use time::OffsetDateTime;
@@ -110,6 +111,9 @@ fn format_profile_wallet_ledger_source_type(
RuntimeProfileWalletLedgerSourceType::SnapshotSync => {
PROFILE_WALLET_LEDGER_SOURCE_TYPE_SNAPSHOT_SYNC
}
RuntimeProfileWalletLedgerSourceType::NewUserRegistrationReward => {
PROFILE_WALLET_LEDGER_SOURCE_TYPE_NEW_USER_REGISTRATION_REWARD
}
RuntimeProfileWalletLedgerSourceType::InviteInviterReward => {
PROFILE_WALLET_LEDGER_SOURCE_TYPE_INVITE_INVITER_REWARD
}
@@ -514,6 +518,16 @@ fn build_profile_referral_invite_center_response(
today_inviter_reward_count: record.today_inviter_reward_count,
today_inviter_reward_remaining: record.today_inviter_reward_remaining,
reward_points: record.reward_points,
invited_users: record
.invited_users
.into_iter()
.map(|user| ProfileReferralInvitedUserResponse {
user_id: user.user_id,
display_name: user.display_name,
avatar_url: user.avatar_url,
bound_at: user.bound_at,
})
.collect(),
has_redeemed_code: record.has_redeemed_code,
bound_inviter_user_id: record.bound_inviter_user_id,
bound_at: record.bound_at,
@@ -637,6 +651,12 @@ mod tests {
#[test]
fn profile_wallet_ledger_source_type_formats_backend_values() {
assert_eq!(
format_profile_wallet_ledger_source_type(
RuntimeProfileWalletLedgerSourceType::NewUserRegistrationReward
),
shared_contracts::runtime::PROFILE_WALLET_LEDGER_SOURCE_TYPE_NEW_USER_REGISTRATION_REWARD
);
assert_eq!(
format_profile_wallet_ledger_source_type(
RuntimeProfileWalletLedgerSourceType::AssetOperationConsume

View File

@@ -187,6 +187,14 @@ pub async fn bind_wechat_phone(
)
.await
.map_err(map_wechat_bind_phone_error)?;
if result.activated_new_user {
crate::registration_reward::grant_new_user_registration_wallet_reward(
&state,
&request_context,
&result.user.id,
)
.await;
}
let session_client = resolve_session_client_context(&headers);
let signed_session = create_auth_session(
&state,