Add skill for gameplay entry type workflows
This commit is contained in:
@@ -105,10 +105,14 @@ use crate::{
|
||||
},
|
||||
runtime_inventory::get_runtime_inventory_state,
|
||||
runtime_profile::{
|
||||
admin_disable_profile_redeem_code, admin_upsert_profile_invite_code,
|
||||
admin_upsert_profile_redeem_code, create_profile_recharge_order, get_profile_dashboard,
|
||||
admin_disable_profile_redeem_code, admin_disable_profile_task_config,
|
||||
admin_list_profile_invite_codes, admin_list_profile_redeem_codes,
|
||||
admin_list_profile_task_configs, admin_upsert_profile_invite_code,
|
||||
admin_upsert_profile_redeem_code, admin_upsert_profile_task_config,
|
||||
claim_profile_task_reward, create_profile_recharge_order, get_profile_dashboard,
|
||||
get_profile_play_stats, get_profile_recharge_center, get_profile_referral_invite_center,
|
||||
get_profile_wallet_ledger, redeem_profile_referral_invite_code, redeem_profile_reward_code,
|
||||
get_profile_task_center, get_profile_wallet_ledger, redeem_profile_referral_invite_code,
|
||||
redeem_profile_reward_code,
|
||||
},
|
||||
runtime_save::{
|
||||
delete_runtime_snapshot, get_runtime_snapshot, list_profile_save_archives,
|
||||
@@ -157,10 +161,12 @@ pub fn build_router(state: AppState) -> Router {
|
||||
)
|
||||
.route(
|
||||
"/admin/api/profile/redeem-codes",
|
||||
post(admin_upsert_profile_redeem_code).route_layer(middleware::from_fn_with_state(
|
||||
state.clone(),
|
||||
require_admin_auth,
|
||||
)),
|
||||
get(admin_list_profile_redeem_codes)
|
||||
.post(admin_upsert_profile_redeem_code)
|
||||
.route_layer(middleware::from_fn_with_state(
|
||||
state.clone(),
|
||||
require_admin_auth,
|
||||
)),
|
||||
)
|
||||
.route(
|
||||
"/admin/api/profile/redeem-codes/disable",
|
||||
@@ -171,7 +177,25 @@ pub fn build_router(state: AppState) -> Router {
|
||||
)
|
||||
.route(
|
||||
"/admin/api/profile/invite-codes",
|
||||
post(admin_upsert_profile_invite_code).route_layer(middleware::from_fn_with_state(
|
||||
get(admin_list_profile_invite_codes)
|
||||
.post(admin_upsert_profile_invite_code)
|
||||
.route_layer(middleware::from_fn_with_state(
|
||||
state.clone(),
|
||||
require_admin_auth,
|
||||
)),
|
||||
)
|
||||
.route(
|
||||
"/admin/api/profile/tasks",
|
||||
get(admin_list_profile_task_configs)
|
||||
.post(admin_upsert_profile_task_config)
|
||||
.route_layer(middleware::from_fn_with_state(
|
||||
state.clone(),
|
||||
require_admin_auth,
|
||||
)),
|
||||
)
|
||||
.route(
|
||||
"/admin/api/profile/tasks/disable",
|
||||
post(admin_disable_profile_task_config).route_layer(middleware::from_fn_with_state(
|
||||
state.clone(),
|
||||
require_admin_auth,
|
||||
)),
|
||||
@@ -1050,6 +1074,20 @@ pub fn build_router(state: AppState) -> Router {
|
||||
require_bearer_auth,
|
||||
)),
|
||||
)
|
||||
.route(
|
||||
"/api/profile/tasks",
|
||||
get(get_profile_task_center).route_layer(middleware::from_fn_with_state(
|
||||
state.clone(),
|
||||
require_bearer_auth,
|
||||
)),
|
||||
)
|
||||
.route(
|
||||
"/api/profile/tasks/{task_id}/claim",
|
||||
post(claim_profile_task_reward).route_layer(middleware::from_fn_with_state(
|
||||
state.clone(),
|
||||
require_bearer_auth,
|
||||
)),
|
||||
)
|
||||
.route(
|
||||
"/api/profile/save-archives",
|
||||
get(list_profile_save_archives).route_layer(middleware::from_fn_with_state(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use axum::{
|
||||
Json,
|
||||
extract::{Extension, State},
|
||||
extract::{Extension, Path, State},
|
||||
http::StatusCode,
|
||||
response::Response,
|
||||
};
|
||||
@@ -9,15 +9,22 @@ use module_runtime::{
|
||||
RuntimeProfileMembershipBenefitRecord, RuntimeProfileRechargeCenterRecord,
|
||||
RuntimeProfileRechargeOrderRecord, RuntimeProfileRechargeProductRecord,
|
||||
RuntimeProfileRedeemCodeMode, RuntimeProfileRedeemCodeRecord,
|
||||
RuntimeProfileRewardCodeRedeemRecord, RuntimeProfileWalletLedgerSourceType,
|
||||
RuntimeReferralInviteCenterRecord,
|
||||
RuntimeProfileRewardCodeRedeemRecord, RuntimeProfileTaskCenterRecord,
|
||||
RuntimeProfileTaskClaimRecord, RuntimeProfileTaskConfigRecord, RuntimeProfileTaskCycle,
|
||||
RuntimeProfileTaskItemRecord, RuntimeProfileTaskStatus, RuntimeProfileWalletLedgerSourceType,
|
||||
RuntimeReferralInviteCenterRecord, RuntimeTrackingScopeKind,
|
||||
};
|
||||
use serde_json::{Value, json};
|
||||
use shared_contracts::runtime::{
|
||||
AdminDisableProfileRedeemCodeRequest, AdminUpsertProfileInviteCodeRequest,
|
||||
AdminUpsertProfileRedeemCodeRequest, CreateProfileRechargeOrderRequest,
|
||||
CreateProfileRechargeOrderResponse, PROFILE_WALLET_LEDGER_SOURCE_TYPE_ASSET_OPERATION_CONSUME,
|
||||
AdminDisableProfileRedeemCodeRequest, AdminDisableProfileTaskConfigRequest,
|
||||
AdminUpsertProfileInviteCodeRequest, AdminUpsertProfileRedeemCodeRequest,
|
||||
AdminUpsertProfileTaskConfigRequest, ClaimProfileTaskRewardResponse,
|
||||
CreateProfileRechargeOrderRequest, CreateProfileRechargeOrderResponse,
|
||||
PROFILE_TASK_CYCLE_DAILY, PROFILE_TASK_STATUS_CLAIMABLE, PROFILE_TASK_STATUS_CLAIMED,
|
||||
PROFILE_TASK_STATUS_DISABLED, PROFILE_TASK_STATUS_INCOMPLETE,
|
||||
PROFILE_WALLET_LEDGER_SOURCE_TYPE_ASSET_OPERATION_CONSUME,
|
||||
PROFILE_WALLET_LEDGER_SOURCE_TYPE_ASSET_OPERATION_REFUND,
|
||||
PROFILE_WALLET_LEDGER_SOURCE_TYPE_DAILY_TASK_REWARD,
|
||||
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,
|
||||
@@ -25,13 +32,17 @@ use shared_contracts::runtime::{
|
||||
PROFILE_WALLET_LEDGER_SOURCE_TYPE_PUZZLE_AUTHOR_INCENTIVE_CLAIM,
|
||||
PROFILE_WALLET_LEDGER_SOURCE_TYPE_REDEEM_CODE_REWARD,
|
||||
PROFILE_WALLET_LEDGER_SOURCE_TYPE_SNAPSHOT_SYNC, ProfileDashboardSummaryResponse,
|
||||
ProfileInviteCodeAdminResponse, ProfileMembershipBenefitResponse, ProfileMembershipResponse,
|
||||
ProfilePlayStatsResponse, ProfilePlayedWorkSummaryResponse, ProfileRechargeCenterResponse,
|
||||
ProfileRechargeOrderResponse, ProfileRechargeProductResponse, ProfileRedeemCodeAdminResponse,
|
||||
ProfileReferralInviteCenterResponse, ProfileReferralInvitedUserResponse,
|
||||
ProfileInviteCodeAdminListResponse, ProfileInviteCodeAdminResponse,
|
||||
ProfileMembershipBenefitResponse, ProfileMembershipResponse, ProfilePlayStatsResponse,
|
||||
ProfilePlayedWorkSummaryResponse, ProfileRechargeCenterResponse, ProfileRechargeOrderResponse,
|
||||
ProfileRechargeProductResponse, ProfileRedeemCodeAdminListResponse,
|
||||
ProfileRedeemCodeAdminResponse, ProfileReferralInviteCenterResponse,
|
||||
ProfileReferralInvitedUserResponse, ProfileTaskCenterResponse,
|
||||
ProfileTaskConfigAdminListResponse, ProfileTaskConfigAdminResponse, ProfileTaskItemResponse,
|
||||
ProfileWalletLedgerEntryResponse, ProfileWalletLedgerResponse,
|
||||
RedeemProfileReferralInviteCodeRequest, RedeemProfileReferralInviteCodeResponse,
|
||||
RedeemProfileRewardCodeRequest, RedeemProfileRewardCodeResponse,
|
||||
RedeemProfileRewardCodeRequest, RedeemProfileRewardCodeResponse, TRACKING_SCOPE_KIND_MODULE,
|
||||
TRACKING_SCOPE_KIND_SITE, TRACKING_SCOPE_KIND_USER, TRACKING_SCOPE_KIND_WORK,
|
||||
};
|
||||
use spacetime_client::SpacetimeClientError;
|
||||
use time::OffsetDateTime;
|
||||
@@ -91,14 +102,7 @@ pub async fn get_profile_wallet_ledger(
|
||||
ProfileWalletLedgerResponse {
|
||||
entries: entries
|
||||
.into_iter()
|
||||
.map(|entry| ProfileWalletLedgerEntryResponse {
|
||||
id: entry.wallet_ledger_id,
|
||||
amount_delta: entry.amount_delta,
|
||||
balance_after: entry.balance_after,
|
||||
source_type: format_profile_wallet_ledger_source_type(entry.source_type)
|
||||
.to_string(),
|
||||
created_at: entry.created_at,
|
||||
})
|
||||
.map(build_profile_wallet_ledger_entry_response)
|
||||
.collect(),
|
||||
},
|
||||
))
|
||||
@@ -135,6 +139,9 @@ fn format_profile_wallet_ledger_source_type(
|
||||
RuntimeProfileWalletLedgerSourceType::PuzzleAuthorIncentiveClaim => {
|
||||
PROFILE_WALLET_LEDGER_SOURCE_TYPE_PUZZLE_AUTHOR_INCENTIVE_CLAIM
|
||||
}
|
||||
RuntimeProfileWalletLedgerSourceType::DailyTaskReward => {
|
||||
PROFILE_WALLET_LEDGER_SOURCE_TYPE_DAILY_TASK_REWARD
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -270,6 +277,184 @@ pub async fn redeem_profile_reward_code(
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn get_profile_task_center(
|
||||
State(state): State<AppState>,
|
||||
Extension(request_context): Extension<RequestContext>,
|
||||
Extension(authenticated): Extension<AuthenticatedAccessToken>,
|
||||
) -> Result<Json<Value>, Response> {
|
||||
let user_id = authenticated.claims().user_id().to_string();
|
||||
let record = state
|
||||
.spacetime_client()
|
||||
.get_profile_task_center(user_id)
|
||||
.await
|
||||
.map_err(|error| {
|
||||
runtime_profile_error_response(
|
||||
&request_context,
|
||||
map_runtime_profile_client_error(error),
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok(json_success_body(
|
||||
Some(&request_context),
|
||||
build_profile_task_center_response(record),
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn claim_profile_task_reward(
|
||||
State(state): State<AppState>,
|
||||
Extension(request_context): Extension<RequestContext>,
|
||||
Extension(authenticated): Extension<AuthenticatedAccessToken>,
|
||||
Path(task_id): Path<String>,
|
||||
) -> Result<Json<Value>, Response> {
|
||||
let user_id = authenticated.claims().user_id().to_string();
|
||||
let record = state
|
||||
.spacetime_client()
|
||||
.claim_profile_task_reward(user_id, task_id)
|
||||
.await
|
||||
.map_err(|error| {
|
||||
runtime_profile_error_response(
|
||||
&request_context,
|
||||
map_runtime_profile_client_error(error),
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok(json_success_body(
|
||||
Some(&request_context),
|
||||
build_claim_profile_task_reward_response(record),
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn admin_list_profile_task_configs(
|
||||
State(state): State<AppState>,
|
||||
Extension(request_context): Extension<RequestContext>,
|
||||
Extension(admin): Extension<AuthenticatedAdmin>,
|
||||
) -> Result<Json<Value>, Response> {
|
||||
let entries = state
|
||||
.spacetime_client()
|
||||
.admin_list_profile_task_configs(admin.session().subject.clone())
|
||||
.await
|
||||
.map_err(|error| {
|
||||
runtime_profile_error_response(
|
||||
&request_context,
|
||||
map_runtime_profile_client_error(error),
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok(json_success_body(
|
||||
Some(&request_context),
|
||||
ProfileTaskConfigAdminListResponse {
|
||||
entries: entries
|
||||
.into_iter()
|
||||
.map(build_profile_task_config_admin_response)
|
||||
.collect(),
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn admin_upsert_profile_task_config(
|
||||
State(state): State<AppState>,
|
||||
Extension(request_context): Extension<RequestContext>,
|
||||
Extension(admin): Extension<AuthenticatedAdmin>,
|
||||
Json(payload): Json<AdminUpsertProfileTaskConfigRequest>,
|
||||
) -> Result<Json<Value>, Response> {
|
||||
let cycle = parse_profile_task_cycle(&payload.cycle).map_err(|error| {
|
||||
runtime_profile_error_response(
|
||||
&request_context,
|
||||
AppError::from_status(StatusCode::BAD_REQUEST).with_message(error),
|
||||
)
|
||||
})?;
|
||||
let scope_kind = parse_tracking_scope_kind(&payload.scope_kind).map_err(|error| {
|
||||
runtime_profile_error_response(
|
||||
&request_context,
|
||||
AppError::from_status(StatusCode::BAD_REQUEST).with_message(error),
|
||||
)
|
||||
})?;
|
||||
let updated_at_micros = OffsetDateTime::now_utc().unix_timestamp_nanos() / 1_000;
|
||||
let record = state
|
||||
.spacetime_client()
|
||||
.admin_upsert_profile_task_config(
|
||||
admin.session().subject.clone(),
|
||||
payload.task_id,
|
||||
payload.title,
|
||||
payload.description.unwrap_or_default(),
|
||||
payload.event_key,
|
||||
cycle,
|
||||
scope_kind,
|
||||
payload.threshold,
|
||||
payload.reward_points,
|
||||
payload.enabled,
|
||||
payload.sort_order.unwrap_or(10),
|
||||
updated_at_micros as i64,
|
||||
)
|
||||
.await
|
||||
.map_err(|error| {
|
||||
runtime_profile_error_response(
|
||||
&request_context,
|
||||
map_runtime_profile_client_error(error),
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok(json_success_body(
|
||||
Some(&request_context),
|
||||
build_profile_task_config_admin_response(record),
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn admin_disable_profile_task_config(
|
||||
State(state): State<AppState>,
|
||||
Extension(request_context): Extension<RequestContext>,
|
||||
Extension(admin): Extension<AuthenticatedAdmin>,
|
||||
Json(payload): Json<AdminDisableProfileTaskConfigRequest>,
|
||||
) -> Result<Json<Value>, Response> {
|
||||
let updated_at_micros = OffsetDateTime::now_utc().unix_timestamp_nanos() / 1_000;
|
||||
let record = state
|
||||
.spacetime_client()
|
||||
.admin_disable_profile_task_config(
|
||||
admin.session().subject.clone(),
|
||||
payload.task_id,
|
||||
updated_at_micros as i64,
|
||||
)
|
||||
.await
|
||||
.map_err(|error| {
|
||||
runtime_profile_error_response(
|
||||
&request_context,
|
||||
map_runtime_profile_client_error(error),
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok(json_success_body(
|
||||
Some(&request_context),
|
||||
build_profile_task_config_admin_response(record),
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn admin_list_profile_redeem_codes(
|
||||
State(state): State<AppState>,
|
||||
Extension(request_context): Extension<RequestContext>,
|
||||
Extension(admin): Extension<AuthenticatedAdmin>,
|
||||
) -> Result<Json<Value>, Response> {
|
||||
let entries = state
|
||||
.spacetime_client()
|
||||
.admin_list_profile_redeem_codes(admin.session().subject.clone())
|
||||
.await
|
||||
.map_err(|error| {
|
||||
runtime_profile_error_response(
|
||||
&request_context,
|
||||
map_runtime_profile_client_error(error),
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok(json_success_body(
|
||||
Some(&request_context),
|
||||
ProfileRedeemCodeAdminListResponse {
|
||||
entries: entries
|
||||
.into_iter()
|
||||
.map(build_profile_redeem_code_admin_response)
|
||||
.collect(),
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn admin_upsert_profile_redeem_code(
|
||||
State(state): State<AppState>,
|
||||
Extension(request_context): Extension<RequestContext>,
|
||||
@@ -338,6 +523,33 @@ pub async fn admin_disable_profile_redeem_code(
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn admin_list_profile_invite_codes(
|
||||
State(state): State<AppState>,
|
||||
Extension(request_context): Extension<RequestContext>,
|
||||
Extension(admin): Extension<AuthenticatedAdmin>,
|
||||
) -> Result<Json<Value>, Response> {
|
||||
let entries = state
|
||||
.spacetime_client()
|
||||
.admin_list_profile_invite_codes(admin.session().subject.clone())
|
||||
.await
|
||||
.map_err(|error| {
|
||||
runtime_profile_error_response(
|
||||
&request_context,
|
||||
map_runtime_profile_client_error(error),
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok(json_success_body(
|
||||
Some(&request_context),
|
||||
ProfileInviteCodeAdminListResponse {
|
||||
entries: entries
|
||||
.into_iter()
|
||||
.map(build_profile_invite_code_admin_response)
|
||||
.collect(),
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn admin_upsert_profile_invite_code(
|
||||
State(state): State<AppState>,
|
||||
Extension(request_context): Extension<RequestContext>,
|
||||
@@ -553,14 +765,87 @@ fn build_redeem_profile_reward_code_response(
|
||||
RedeemProfileRewardCodeResponse {
|
||||
wallet_balance: record.wallet_balance,
|
||||
amount_granted: record.amount_granted,
|
||||
ledger_entry: ProfileWalletLedgerEntryResponse {
|
||||
id: record.ledger_entry.wallet_ledger_id,
|
||||
amount_delta: record.ledger_entry.amount_delta,
|
||||
balance_after: record.ledger_entry.balance_after,
|
||||
source_type: format_profile_wallet_ledger_source_type(record.ledger_entry.source_type)
|
||||
.to_string(),
|
||||
created_at: record.ledger_entry.created_at,
|
||||
},
|
||||
ledger_entry: build_profile_wallet_ledger_entry_response(record.ledger_entry),
|
||||
}
|
||||
}
|
||||
|
||||
fn build_profile_wallet_ledger_entry_response(
|
||||
record: module_runtime::RuntimeProfileWalletLedgerEntryRecord,
|
||||
) -> ProfileWalletLedgerEntryResponse {
|
||||
ProfileWalletLedgerEntryResponse {
|
||||
id: record.wallet_ledger_id,
|
||||
amount_delta: record.amount_delta,
|
||||
balance_after: record.balance_after,
|
||||
source_type: format_profile_wallet_ledger_source_type(record.source_type).to_string(),
|
||||
created_at: record.created_at,
|
||||
}
|
||||
}
|
||||
|
||||
fn build_profile_task_center_response(
|
||||
record: RuntimeProfileTaskCenterRecord,
|
||||
) -> ProfileTaskCenterResponse {
|
||||
ProfileTaskCenterResponse {
|
||||
day_key: record.day_key,
|
||||
wallet_balance: record.wallet_balance,
|
||||
tasks: record
|
||||
.tasks
|
||||
.into_iter()
|
||||
.map(build_profile_task_item_response)
|
||||
.collect(),
|
||||
updated_at: record.updated_at,
|
||||
}
|
||||
}
|
||||
|
||||
fn build_profile_task_item_response(
|
||||
record: RuntimeProfileTaskItemRecord,
|
||||
) -> ProfileTaskItemResponse {
|
||||
ProfileTaskItemResponse {
|
||||
task_id: record.task_id,
|
||||
title: record.title,
|
||||
description: record.description,
|
||||
event_key: record.event_key,
|
||||
cycle: format_profile_task_cycle(record.cycle).to_string(),
|
||||
threshold: record.threshold,
|
||||
progress_count: record.progress_count,
|
||||
reward_points: record.reward_points,
|
||||
status: format_profile_task_status(record.status).to_string(),
|
||||
day_key: record.day_key,
|
||||
claimed_at: record.claimed_at,
|
||||
updated_at: record.updated_at,
|
||||
}
|
||||
}
|
||||
|
||||
fn build_claim_profile_task_reward_response(
|
||||
record: RuntimeProfileTaskClaimRecord,
|
||||
) -> ClaimProfileTaskRewardResponse {
|
||||
ClaimProfileTaskRewardResponse {
|
||||
task_id: record.task_id,
|
||||
day_key: record.day_key,
|
||||
reward_points: record.reward_points,
|
||||
wallet_balance: record.wallet_balance,
|
||||
ledger_entry: build_profile_wallet_ledger_entry_response(record.ledger_entry),
|
||||
center: build_profile_task_center_response(record.center),
|
||||
}
|
||||
}
|
||||
|
||||
fn build_profile_task_config_admin_response(
|
||||
record: RuntimeProfileTaskConfigRecord,
|
||||
) -> ProfileTaskConfigAdminResponse {
|
||||
ProfileTaskConfigAdminResponse {
|
||||
task_id: record.task_id,
|
||||
title: record.title,
|
||||
description: record.description,
|
||||
event_key: record.event_key,
|
||||
cycle: format_profile_task_cycle(record.cycle).to_string(),
|
||||
scope_kind: format_tracking_scope_kind(record.scope_kind).to_string(),
|
||||
threshold: record.threshold,
|
||||
reward_points: record.reward_points,
|
||||
enabled: record.enabled,
|
||||
sort_order: record.sort_order,
|
||||
created_by: record.created_by,
|
||||
created_at: record.created_at,
|
||||
updated_by: record.updated_by,
|
||||
updated_at: record.updated_at,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -597,6 +882,47 @@ fn parse_profile_redeem_code_mode(raw: &str) -> Result<RuntimeProfileRedeemCodeM
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_profile_task_cycle(raw: &str) -> Result<RuntimeProfileTaskCycle, String> {
|
||||
match raw.trim().to_ascii_lowercase().as_str() {
|
||||
PROFILE_TASK_CYCLE_DAILY => Ok(RuntimeProfileTaskCycle::Daily),
|
||||
_ => Err("任务周期无效".to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_tracking_scope_kind(raw: &str) -> Result<RuntimeTrackingScopeKind, String> {
|
||||
match raw.trim().to_ascii_lowercase().as_str() {
|
||||
TRACKING_SCOPE_KIND_SITE => Ok(RuntimeTrackingScopeKind::Site),
|
||||
TRACKING_SCOPE_KIND_WORK => Ok(RuntimeTrackingScopeKind::Work),
|
||||
TRACKING_SCOPE_KIND_MODULE => Ok(RuntimeTrackingScopeKind::Module),
|
||||
TRACKING_SCOPE_KIND_USER => Ok(RuntimeTrackingScopeKind::User),
|
||||
_ => Err("埋点范围无效".to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
fn format_profile_task_cycle(cycle: RuntimeProfileTaskCycle) -> &'static str {
|
||||
match cycle {
|
||||
RuntimeProfileTaskCycle::Daily => PROFILE_TASK_CYCLE_DAILY,
|
||||
}
|
||||
}
|
||||
|
||||
fn format_profile_task_status(status: RuntimeProfileTaskStatus) -> &'static str {
|
||||
match status {
|
||||
RuntimeProfileTaskStatus::Incomplete => PROFILE_TASK_STATUS_INCOMPLETE,
|
||||
RuntimeProfileTaskStatus::Claimable => PROFILE_TASK_STATUS_CLAIMABLE,
|
||||
RuntimeProfileTaskStatus::Claimed => PROFILE_TASK_STATUS_CLAIMED,
|
||||
RuntimeProfileTaskStatus::Disabled => PROFILE_TASK_STATUS_DISABLED,
|
||||
}
|
||||
}
|
||||
|
||||
fn format_tracking_scope_kind(scope_kind: RuntimeTrackingScopeKind) -> &'static str {
|
||||
match scope_kind {
|
||||
RuntimeTrackingScopeKind::Site => TRACKING_SCOPE_KIND_SITE,
|
||||
RuntimeTrackingScopeKind::Work => TRACKING_SCOPE_KIND_WORK,
|
||||
RuntimeTrackingScopeKind::Module => TRACKING_SCOPE_KIND_MODULE,
|
||||
RuntimeTrackingScopeKind::User => TRACKING_SCOPE_KIND_USER,
|
||||
}
|
||||
}
|
||||
|
||||
fn build_profile_invite_code_admin_response(
|
||||
record: RuntimeProfileInviteCodeRecord,
|
||||
) -> ProfileInviteCodeAdminResponse {
|
||||
@@ -675,6 +1001,12 @@ mod tests {
|
||||
),
|
||||
shared_contracts::runtime::PROFILE_WALLET_LEDGER_SOURCE_TYPE_PUZZLE_AUTHOR_INCENTIVE_CLAIM
|
||||
);
|
||||
assert_eq!(
|
||||
format_profile_wallet_ledger_source_type(
|
||||
RuntimeProfileWalletLedgerSourceType::DailyTaskReward
|
||||
),
|
||||
shared_contracts::runtime::PROFILE_WALLET_LEDGER_SOURCE_TYPE_DAILY_TASK_REWARD
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -713,6 +1045,36 @@ mod tests {
|
||||
assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn profile_tasks_require_authentication() {
|
||||
let app = build_router(AppState::new(AppConfig::default()).expect("state should build"));
|
||||
|
||||
let list_response = app
|
||||
.clone()
|
||||
.oneshot(
|
||||
Request::builder()
|
||||
.method("GET")
|
||||
.uri("/api/profile/tasks")
|
||||
.body(Body::empty())
|
||||
.expect("request should build"),
|
||||
)
|
||||
.await
|
||||
.expect("request should succeed");
|
||||
let claim_response = app
|
||||
.oneshot(
|
||||
Request::builder()
|
||||
.method("POST")
|
||||
.uri("/api/profile/tasks/daily_login/claim")
|
||||
.body(Body::empty())
|
||||
.expect("request should build"),
|
||||
)
|
||||
.await
|
||||
.expect("request should succeed");
|
||||
|
||||
assert_eq!(list_response.status(), StatusCode::UNAUTHORIZED);
|
||||
assert_eq!(claim_response.status(), StatusCode::UNAUTHORIZED);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn profile_play_stats_requires_authentication() {
|
||||
let app = build_router(AppState::new(AppConfig::default()).expect("state should build"));
|
||||
@@ -892,6 +1254,78 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn admin_profile_task_routes_require_admin_authentication() {
|
||||
let app = build_router(
|
||||
AppState::new(admin_enabled_test_config()).expect("state should build"),
|
||||
);
|
||||
|
||||
let list_response = app
|
||||
.clone()
|
||||
.oneshot(
|
||||
Request::builder()
|
||||
.method("GET")
|
||||
.uri("/admin/api/profile/tasks")
|
||||
.body(Body::empty())
|
||||
.expect("request should build"),
|
||||
)
|
||||
.await
|
||||
.expect("request should succeed");
|
||||
let upsert_response = app
|
||||
.clone()
|
||||
.oneshot(
|
||||
Request::builder()
|
||||
.method("POST")
|
||||
.uri("/admin/api/profile/tasks")
|
||||
.header("content-type", "application/json")
|
||||
.body(Body::from(r#"{"taskId":"daily_login"}"#))
|
||||
.expect("request should build"),
|
||||
)
|
||||
.await
|
||||
.expect("request should succeed");
|
||||
let disable_response = app
|
||||
.oneshot(
|
||||
Request::builder()
|
||||
.method("POST")
|
||||
.uri("/admin/api/profile/tasks/disable")
|
||||
.header("content-type", "application/json")
|
||||
.body(Body::from(r#"{"taskId":"daily_login"}"#))
|
||||
.expect("request should build"),
|
||||
)
|
||||
.await
|
||||
.expect("request should succeed");
|
||||
|
||||
assert_eq!(list_response.status(), StatusCode::UNAUTHORIZED);
|
||||
assert_eq!(upsert_response.status(), StatusCode::UNAUTHORIZED);
|
||||
assert_eq!(disable_response.status(), StatusCode::UNAUTHORIZED);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn admin_profile_code_list_routes_require_admin_authentication() {
|
||||
let app = build_router(
|
||||
AppState::new(admin_enabled_test_config()).expect("state should build"),
|
||||
);
|
||||
|
||||
for uri in [
|
||||
"/admin/api/profile/redeem-codes",
|
||||
"/admin/api/profile/invite-codes",
|
||||
] {
|
||||
let response = app
|
||||
.clone()
|
||||
.oneshot(
|
||||
Request::builder()
|
||||
.method("GET")
|
||||
.uri(uri)
|
||||
.body(Body::empty())
|
||||
.expect("request should build"),
|
||||
)
|
||||
.await
|
||||
.expect("request should succeed");
|
||||
|
||||
assert_eq!(response.status(), StatusCode::UNAUTHORIZED, "{uri}");
|
||||
}
|
||||
}
|
||||
|
||||
async fn seed_authenticated_state() -> AppState {
|
||||
let state = AppState::new(fast_spacetime_timeout_config()).expect("state should build");
|
||||
state
|
||||
@@ -908,6 +1342,14 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
fn admin_enabled_test_config() -> AppConfig {
|
||||
AppConfig {
|
||||
admin_username: Some("root".to_string()),
|
||||
admin_password: Some("secret123".to_string()),
|
||||
..AppConfig::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn issue_access_token(state: &AppState) -> String {
|
||||
let claims = AccessTokenClaims::from_input(
|
||||
AccessTokenClaimsInput {
|
||||
|
||||
Reference in New Issue
Block a user