Add backend feedback submission and image preview
Some checks failed
CI / verify (push) Has been cancelled
Some checks failed
CI / verify (push) Has been cancelled
This commit is contained in:
@@ -5,7 +5,9 @@ use axum::{
|
||||
response::Response,
|
||||
};
|
||||
use module_runtime::{
|
||||
AnalyticsGranularity, PROFILE_RECHARGE_PAYMENT_CHANNEL_MOCK, RuntimeProfileInviteCodeRecord,
|
||||
AnalyticsGranularity, PROFILE_RECHARGE_PAYMENT_CHANNEL_MOCK,
|
||||
RuntimeProfileFeedbackEvidenceRecord, RuntimeProfileFeedbackEvidenceSnapshot,
|
||||
RuntimeProfileFeedbackSubmissionRecord, RuntimeProfileInviteCodeRecord,
|
||||
RuntimeProfileMembershipBenefitRecord, RuntimeProfileRechargeCenterRecord,
|
||||
RuntimeProfileRechargeOrderRecord, RuntimeProfileRechargeProductRecord,
|
||||
RuntimeProfileRedeemCodeMode, RuntimeProfileRedeemCodeRecord,
|
||||
@@ -23,8 +25,8 @@ use shared_contracts::runtime::{
|
||||
AdminUpsertProfileRedeemCodeRequest, AdminUpsertProfileTaskConfigRequest,
|
||||
AnalyticsBucketMetricResponse, AnalyticsMetricQueryResponse, ClaimProfileTaskRewardResponse,
|
||||
CreateProfileRechargeOrderRequest, CreateProfileRechargeOrderResponse,
|
||||
PROFILE_TASK_CYCLE_DAILY, PROFILE_TASK_STATUS_CLAIMABLE, PROFILE_TASK_STATUS_CLAIMED,
|
||||
PROFILE_TASK_STATUS_DISABLED, PROFILE_TASK_STATUS_INCOMPLETE,
|
||||
PROFILE_FEEDBACK_STATUS_OPEN, 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,
|
||||
@@ -35,6 +37,7 @@ 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,
|
||||
ProfileFeedbackEvidenceItemResponse, ProfileFeedbackSubmissionResponse,
|
||||
ProfileInviteCodeAdminListResponse, ProfileInviteCodeAdminResponse,
|
||||
ProfileMembershipBenefitResponse, ProfileMembershipResponse, ProfilePlayStatsResponse,
|
||||
ProfilePlayedWorkSummaryResponse, ProfileRechargeCenterResponse, ProfileRechargeOrderResponse,
|
||||
@@ -44,8 +47,9 @@ use shared_contracts::runtime::{
|
||||
ProfileTaskConfigAdminListResponse, ProfileTaskConfigAdminResponse, ProfileTaskItemResponse,
|
||||
ProfileWalletLedgerEntryResponse, ProfileWalletLedgerResponse,
|
||||
RedeemProfileReferralInviteCodeRequest, RedeemProfileReferralInviteCodeResponse,
|
||||
RedeemProfileRewardCodeRequest, RedeemProfileRewardCodeResponse, TRACKING_SCOPE_KIND_MODULE,
|
||||
TRACKING_SCOPE_KIND_SITE, TRACKING_SCOPE_KIND_USER, TRACKING_SCOPE_KIND_WORK,
|
||||
RedeemProfileRewardCodeRequest, RedeemProfileRewardCodeResponse, SubmitProfileFeedbackRequest,
|
||||
SubmitProfileFeedbackResponse, TRACKING_SCOPE_KIND_MODULE, TRACKING_SCOPE_KIND_SITE,
|
||||
TRACKING_SCOPE_KIND_USER, TRACKING_SCOPE_KIND_WORK,
|
||||
};
|
||||
use shared_kernel::{offset_datetime_to_unix_micros, parse_rfc3339};
|
||||
use spacetime_client::SpacetimeClientError;
|
||||
@@ -208,6 +212,51 @@ pub async fn create_profile_recharge_order(
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn submit_profile_feedback(
|
||||
State(state): State<AppState>,
|
||||
Extension(request_context): Extension<RequestContext>,
|
||||
Extension(authenticated): Extension<AuthenticatedAccessToken>,
|
||||
Json(payload): Json<SubmitProfileFeedbackRequest>,
|
||||
) -> Result<Json<Value>, Response> {
|
||||
let user_id = authenticated.claims().user_id().to_string();
|
||||
let created_at_micros = OffsetDateTime::now_utc().unix_timestamp_nanos() / 1_000;
|
||||
let evidence_items = payload
|
||||
.evidence_items
|
||||
.into_iter()
|
||||
.map(|item| RuntimeProfileFeedbackEvidenceSnapshot {
|
||||
evidence_id: String::new(),
|
||||
file_name: item.file_name,
|
||||
content_type: item.content_type,
|
||||
size_bytes: item.size_bytes,
|
||||
data_url: item.data_url,
|
||||
})
|
||||
.collect();
|
||||
|
||||
let record = state
|
||||
.spacetime_client()
|
||||
.submit_profile_feedback(
|
||||
user_id,
|
||||
payload.description,
|
||||
payload.contact_phone,
|
||||
evidence_items,
|
||||
created_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),
|
||||
SubmitProfileFeedbackResponse {
|
||||
feedback: build_profile_feedback_submission_response(record),
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn get_profile_referral_invite_center(
|
||||
State(state): State<AppState>,
|
||||
Extension(request_context): Extension<RequestContext>,
|
||||
@@ -782,6 +831,36 @@ fn build_profile_recharge_order_response(
|
||||
}
|
||||
}
|
||||
|
||||
fn build_profile_feedback_submission_response(
|
||||
record: RuntimeProfileFeedbackSubmissionRecord,
|
||||
) -> ProfileFeedbackSubmissionResponse {
|
||||
ProfileFeedbackSubmissionResponse {
|
||||
feedback_id: record.feedback_id,
|
||||
status: match record.status {
|
||||
module_runtime::RuntimeProfileFeedbackStatus::Open => {
|
||||
PROFILE_FEEDBACK_STATUS_OPEN.to_string()
|
||||
}
|
||||
},
|
||||
created_at: record.created_at,
|
||||
evidence_items: record
|
||||
.evidence_items
|
||||
.into_iter()
|
||||
.map(build_profile_feedback_evidence_response)
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
fn build_profile_feedback_evidence_response(
|
||||
record: RuntimeProfileFeedbackEvidenceRecord,
|
||||
) -> ProfileFeedbackEvidenceItemResponse {
|
||||
ProfileFeedbackEvidenceItemResponse {
|
||||
evidence_id: record.evidence_id,
|
||||
file_name: record.file_name,
|
||||
content_type: record.content_type,
|
||||
size_bytes: record.size_bytes,
|
||||
}
|
||||
}
|
||||
|
||||
fn build_profile_referral_invite_center_response(
|
||||
record: RuntimeReferralInviteCenterRecord,
|
||||
) -> ProfileReferralInviteCenterResponse {
|
||||
@@ -1245,6 +1324,27 @@ mod tests {
|
||||
assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn profile_feedback_requires_authentication() {
|
||||
let app = build_router(AppState::new(AppConfig::default()).expect("state should build"));
|
||||
|
||||
let response = app
|
||||
.oneshot(
|
||||
Request::builder()
|
||||
.method("POST")
|
||||
.uri("/api/profile/feedback")
|
||||
.header("content-type", "application/json")
|
||||
.body(Body::from(
|
||||
r#"{"description":"反馈页面上传图片后没有显示预览"}"#,
|
||||
))
|
||||
.expect("request should build"),
|
||||
)
|
||||
.await
|
||||
.expect("request should succeed");
|
||||
|
||||
assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn profile_referral_invite_center_requires_authentication() {
|
||||
let app = build_router(AppState::new(AppConfig::default()).expect("state should build"));
|
||||
@@ -1345,6 +1445,7 @@ mod tests {
|
||||
"/api/runtime/profile/wallet-ledger",
|
||||
"/api/runtime/profile/recharge-center",
|
||||
"/api/runtime/profile/recharge/orders",
|
||||
"/api/runtime/profile/feedback",
|
||||
"/api/runtime/profile/referrals/invite-center",
|
||||
"/api/runtime/profile/referrals/redeem-code",
|
||||
"/api/runtime/profile/redeem-codes/redeem",
|
||||
|
||||
Reference in New Issue
Block a user