feat(payments): 增加兑换码与支付适配框架 (#299)

* feat(payments): 增加兑换码与支付适配框架

* fix(ci): 对齐 Rust 1.95 lint 与格式要求

* fix(payments): harden redeem code wallet credits

---------

Co-authored-by: fawney19 <elky0401@gmail.com>
This commit is contained in:
Entropy.Xu
2026-04-17 10:07:52 +08:00
committed by GitHub
parent 54d77598ae
commit 6964729cb7
36 changed files with 5129 additions and 274 deletions

View File

@@ -1,8 +1,9 @@
use aether_data::repository::wallet::{
AdminPaymentOrderListQuery, AdminWalletLedgerQuery, AdminWalletListQuery,
AdminWalletRefundRequestListQuery, StoredAdminPaymentCallback, StoredAdminPaymentOrder,
StoredAdminWalletLedgerItem, StoredAdminWalletListItem, StoredAdminWalletRefund,
StoredAdminWalletRefundRequestItem, StoredAdminWalletTransaction,
AdminPaymentOrderListQuery, AdminRedeemCodeBatchListQuery, AdminRedeemCodeListQuery,
AdminWalletLedgerQuery, AdminWalletListQuery, AdminWalletRefundRequestListQuery,
StoredAdminPaymentCallback, StoredAdminPaymentOrder, StoredAdminRedeemCodeBatch,
StoredAdminRedeemCodePage, StoredAdminWalletLedgerItem, StoredAdminWalletListItem,
StoredAdminWalletRefund, StoredAdminWalletRefundRequestItem, StoredAdminWalletTransaction,
};
use crate::{
@@ -386,6 +387,61 @@ impl AppState {
None => Ok(AdminWalletMutationOutcome::NotFound),
}
}
pub(crate) async fn list_admin_redeem_code_batches(
&self,
status: Option<&str>,
limit: usize,
offset: usize,
) -> Result<(Vec<StoredAdminRedeemCodeBatch>, u64), GatewayError> {
let page = self
.data
.list_admin_redeem_code_batches(&AdminRedeemCodeBatchListQuery {
status: status.map(ToOwned::to_owned),
limit,
offset,
})
.await
.map_err(|err| GatewayError::Internal(err.to_string()))?;
Ok((page.items, page.total))
}
pub(crate) async fn read_admin_redeem_code_batch(
&self,
batch_id: &str,
) -> Result<AdminWalletMutationOutcome<StoredAdminRedeemCodeBatch>, GatewayError> {
if !self.has_wallet_data_reader() {
return Ok(AdminWalletMutationOutcome::Unavailable);
}
match self
.data
.find_admin_redeem_code_batch(batch_id)
.await
.map_err(|err| GatewayError::Internal(err.to_string()))?
{
Some(batch) => Ok(AdminWalletMutationOutcome::Applied(batch)),
None => Ok(AdminWalletMutationOutcome::NotFound),
}
}
pub(crate) async fn list_admin_redeem_codes(
&self,
batch_id: &str,
status: Option<&str>,
limit: usize,
offset: usize,
) -> Result<StoredAdminRedeemCodePage, GatewayError> {
self.data
.list_admin_redeem_codes(&AdminRedeemCodeListQuery {
batch_id: batch_id.to_string(),
status: status.map(ToOwned::to_owned),
limit,
offset,
})
.await
.map_err(|err| GatewayError::Internal(err.to_string()))
}
}
fn stored_admin_payment_order_to_gateway(

View File

@@ -229,6 +229,126 @@ impl AppState {
None => Ok(AdminWalletMutationOutcome::Unavailable),
}
}
pub(crate) async fn admin_create_redeem_code_batch(
&self,
input: aether_data::repository::wallet::CreateAdminRedeemCodeBatchInput,
) -> Result<
Option<aether_data::repository::wallet::CreateAdminRedeemCodeBatchResult>,
GatewayError,
> {
self.data
.create_admin_redeem_code_batch(input)
.await
.map_err(|err| GatewayError::Internal(err.to_string()))
}
pub(crate) async fn admin_disable_redeem_code_batch(
&self,
batch_id: &str,
operator_id: Option<&str>,
) -> Result<
AdminWalletMutationOutcome<aether_data::repository::wallet::StoredAdminRedeemCodeBatch>,
GatewayError,
> {
match self
.data
.disable_admin_redeem_code_batch(
aether_data::repository::wallet::DisableAdminRedeemCodeBatchInput {
batch_id: batch_id.to_string(),
operator_id: operator_id.map(ToOwned::to_owned),
},
)
.await
.map_err(|err| GatewayError::Internal(err.to_string()))?
{
Some(aether_data::repository::wallet::WalletMutationOutcome::Applied(batch)) => {
Ok(AdminWalletMutationOutcome::Applied(batch))
}
Some(aether_data::repository::wallet::WalletMutationOutcome::NotFound) => {
Ok(AdminWalletMutationOutcome::NotFound)
}
Some(aether_data::repository::wallet::WalletMutationOutcome::Invalid(detail)) => {
Ok(AdminWalletMutationOutcome::Invalid(detail))
}
None => Ok(AdminWalletMutationOutcome::Unavailable),
}
}
pub(crate) async fn admin_delete_redeem_code_batch(
&self,
batch_id: &str,
operator_id: Option<&str>,
) -> Result<
AdminWalletMutationOutcome<aether_data::repository::wallet::StoredAdminRedeemCodeBatch>,
GatewayError,
> {
match self
.data
.delete_admin_redeem_code_batch(
aether_data::repository::wallet::DeleteAdminRedeemCodeBatchInput {
batch_id: batch_id.to_string(),
operator_id: operator_id.map(ToOwned::to_owned),
},
)
.await
.map_err(|err| GatewayError::Internal(err.to_string()))?
{
Some(aether_data::repository::wallet::WalletMutationOutcome::Applied(batch)) => {
Ok(AdminWalletMutationOutcome::Applied(batch))
}
Some(aether_data::repository::wallet::WalletMutationOutcome::NotFound) => {
Ok(AdminWalletMutationOutcome::NotFound)
}
Some(aether_data::repository::wallet::WalletMutationOutcome::Invalid(detail)) => {
Ok(AdminWalletMutationOutcome::Invalid(detail))
}
None => Ok(AdminWalletMutationOutcome::Unavailable),
}
}
pub(crate) async fn admin_disable_redeem_code(
&self,
code_id: &str,
operator_id: Option<&str>,
) -> Result<
AdminWalletMutationOutcome<aether_data::repository::wallet::StoredAdminRedeemCode>,
GatewayError,
> {
match self
.data
.disable_admin_redeem_code(
aether_data::repository::wallet::DisableAdminRedeemCodeInput {
code_id: code_id.to_string(),
operator_id: operator_id.map(ToOwned::to_owned),
},
)
.await
.map_err(|err| GatewayError::Internal(err.to_string()))?
{
Some(aether_data::repository::wallet::WalletMutationOutcome::Applied(code)) => {
Ok(AdminWalletMutationOutcome::Applied(code))
}
Some(aether_data::repository::wallet::WalletMutationOutcome::NotFound) => {
Ok(AdminWalletMutationOutcome::NotFound)
}
Some(aether_data::repository::wallet::WalletMutationOutcome::Invalid(detail)) => {
Ok(AdminWalletMutationOutcome::Invalid(detail))
}
None => Ok(AdminWalletMutationOutcome::Unavailable),
}
}
pub(crate) async fn redeem_wallet_code(
&self,
input: aether_data::repository::wallet::RedeemWalletCodeInput,
) -> Result<Option<aether_data::repository::wallet::RedeemWalletCodeOutcome>, GatewayError>
{
self.data
.redeem_wallet_code(input)
.await
.map_err(|err| GatewayError::Internal(err.to_string()))
}
}
fn stored_admin_payment_order_to_gateway(