using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Tiku.Api.Contracts; using Tiku.Application.Commerce; using Tiku.Application.Jobs; using Tiku.Application.Security; using Tiku.Domain.Commerce; namespace Tiku.Api.Controllers; [ApiController] [Tags("租户端-交易运营")] [Authorize(Policy = BackendPermissions.TenantCommerceOperate)] [Tiku.Api.Security.RequireSaasFeature(SaasFeatureCatalog.StudentStore)] [Produces("application/json")] [Route("api/tenant-commerce")] public sealed class TenantCommerceController( ICommerceAdminService commerceAdminService, ICurrentUser currentUser, ITenantContext currentTenant) : ControllerBase { [HttpGet("payment-accounts")] [Authorize(Policy = TikuPolicies.TenantCommerceOperateAllScope)] [EndpointSummary("查询租户支付账号")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> PaymentAccounts( [FromQuery] TenantCommerceQueryDto query, CancellationToken cancellationToken) { return Ok(await commerceAdminService.GetPaymentAccountsAsync( ResolveActor(), ToQuery(query), cancellationToken)); } [HttpPut("payment-accounts")] [Authorize(Policy = TikuPolicies.TenantCommerceOperateAllScope)] [EndpointSummary("新增或更新租户支付账号")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> UpsertPaymentAccount( UpsertPaymentAccountDto request, CancellationToken cancellationToken) { return Ok(await commerceAdminService.UpsertPaymentAccountAsync( ResolveActor(), request.ToCommand(), cancellationToken)); } [HttpPut("secrets")] [Authorize(Policy = TikuPolicies.TenantCommerceOperateAllScope)] [EndpointSummary("写入或轮换租户密钥")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> UpsertSecret( UpsertTenantSecretDto request, CancellationToken cancellationToken) { return Ok(await commerceAdminService.UpsertTenantSecretAsync( ResolveActor(), request.ToCommand(), cancellationToken)); } [HttpGet("orders")] [EndpointSummary("查询租户订单")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> Orders( [FromQuery] TenantCommerceQueryDto query, CancellationToken cancellationToken) { return Ok(await commerceAdminService.GetOrdersAsync( ResolveActor(), ToQuery(query), cancellationToken)); } [HttpGet("payments")] [EndpointSummary("查询租户支付记录")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> Payments( [FromQuery] TenantCommerceQueryDto query, CancellationToken cancellationToken) { return Ok(await commerceAdminService.GetPaymentsAsync( ResolveActor(), ToQuery(query), cancellationToken)); } [HttpPost("code-batches")] [Authorize(Policy = TikuPolicies.TenantCommerceOperateAllScope)] [EndpointSummary("创建兑换码批次")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> CreateCodeBatch( CreateCodeBatchDto request, CancellationToken cancellationToken) { return Ok(await commerceAdminService.CreateCodeBatchAsync( ResolveActor(), request.ToCommand(), cancellationToken)); } [HttpGet("activation-codes")] [Authorize(Policy = TikuPolicies.TenantCommerceOperateAllScope)] [EndpointSummary("查询兑换码")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> ActivationCodes( [FromQuery] TenantCommerceQueryDto query, CancellationToken cancellationToken) { return Ok(await commerceAdminService.GetActivationCodesAsync( ResolveActor(), ToQuery(query), cancellationToken)); } [HttpPost("activation-codes/redeem")] [Authorize(Policy = TikuPolicies.TenantCommerceOperateAllScope)] [EndpointSummary("后台核销兑换码")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> RedeemActivationCode( RedeemActivationCodeDto request, CancellationToken cancellationToken) { return Ok(await commerceAdminService.RedeemActivationCodeAsync( ResolveActor(), request.ToCommand(), cancellationToken)); } [HttpGet("point-activity-tasks")] [Authorize(Policy = TikuPolicies.TenantCommerceOperateAllScope)] [EndpointSummary("查询积分活动任务")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> PointTasks( [FromQuery] TenantCommerceQueryDto query, CancellationToken cancellationToken) { return Ok(await commerceAdminService.GetPointTasksAsync( ResolveActor(), ToPointQuery(query), cancellationToken)); } [HttpPut("point-activity-tasks")] [Authorize(Policy = TikuPolicies.TenantCommerceOperateAllScope)] [EndpointSummary("新增或更新积分活动任务")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> UpsertPointTask( UpsertPointTaskDto request, CancellationToken cancellationToken) { return Ok(await commerceAdminService.UpsertPointTaskAsync( ResolveActor(), request.ToCommand(), cancellationToken)); } [HttpGet("point-activity-claims")] [Authorize(Policy = TikuPolicies.TenantCommerceOperateAllScope)] [EndpointSummary("查询积分任务领取记录")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> PointClaims( [FromQuery] TenantCommerceQueryDto query, CancellationToken cancellationToken) { return Ok(await commerceAdminService.GetPointClaimsAsync( ResolveActor(), ToPointQuery(query), cancellationToken)); } [HttpGet("point-exchange-items")] [Authorize(Policy = TikuPolicies.TenantCommerceOperateAllScope)] [EndpointSummary("查询积分兑换项")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> PointExchangeItems( [FromQuery] TenantCommerceQueryDto query, CancellationToken cancellationToken) { return Ok(await commerceAdminService.GetPointExchangeItemsAsync( ResolveActor(), ToPointQuery(query), cancellationToken)); } [HttpPut("point-exchange-items")] [Authorize(Policy = TikuPolicies.TenantCommerceOperateAllScope)] [EndpointSummary("新增或更新积分兑换项")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> UpsertPointExchangeItem( UpsertPointExchangeItemDto request, CancellationToken cancellationToken) { return Ok(await commerceAdminService.UpsertPointExchangeItemAsync( ResolveActor(), request.ToCommand(), cancellationToken)); } [HttpGet("point-exchange-orders")] [Authorize(Policy = TikuPolicies.TenantCommerceOperateAllScope)] [EndpointSummary("查询积分兑换订单")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> PointExchangeOrders( [FromQuery] TenantCommerceQueryDto query, CancellationToken cancellationToken) { return Ok(await commerceAdminService.GetPointExchangeOrdersAsync( ResolveActor(), ToPointQuery(query), cancellationToken)); } [HttpPost("point-exchange-orders/status")] [Authorize(Policy = TikuPolicies.TenantCommerceOperateAllScope)] [EndpointSummary("更新积分兑换订单状态")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> UpdatePointExchangeOrderStatus( UpdatePointExchangeOrderStatusDto request, CancellationToken cancellationToken) { return Ok(await commerceAdminService.UpdatePointExchangeOrderStatusAsync( ResolveActor(), request.ToCommand(), cancellationToken)); } [HttpGet("coupons")] [Authorize(Policy = TikuPolicies.TenantCommerceOperateAllScope)] [EndpointSummary("查询租户优惠券")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> Coupons( [FromQuery] TenantCommerceQueryDto query, CancellationToken cancellationToken) { return Ok(await commerceAdminService.GetCouponsAsync( ResolveActor(), ToQuery(query), cancellationToken)); } [HttpPut("coupons")] [Authorize(Policy = TikuPolicies.TenantCommerceOperateAllScope)] [EndpointSummary("新增或更新租户优惠券")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> UpsertCoupon( UpsertTenantCouponDto request, CancellationToken cancellationToken) { return Ok(await commerceAdminService.UpsertCouponAsync( ResolveActor(), request.ToCommand(), cancellationToken)); } [HttpGet("coupons/redemptions")] [Authorize(Policy = TikuPolicies.TenantCommerceOperateAllScope)] [EndpointSummary("查询优惠券领取和核销记录")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> CouponRedemptions( [FromQuery] TenantCommerceQueryDto query, CancellationToken cancellationToken) { return Ok(await commerceAdminService.GetCouponRedemptionsAsync( ResolveActor(), ToQuery(query), cancellationToken)); } [HttpGet("coupons/report")] [Authorize(Policy = TikuPolicies.TenantCommerceOperateAllScope)] [EndpointSummary("查询优惠券基础报表")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> CouponReport( [FromQuery] TenantCommerceQueryDto query, CancellationToken cancellationToken) { return Ok(await commerceAdminService.GetCouponReportAsync( ResolveActor(), ToQuery(query), cancellationToken)); } [HttpGet("refunds")] [EndpointSummary("查询退款申请")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> Refunds( [FromQuery] TenantCommerceQueryDto query, CancellationToken cancellationToken) { return Ok(await commerceAdminService.GetRefundsAsync( ResolveActor(), ToQuery(query), cancellationToken)); } [HttpPost("refunds")] [EndpointSummary("创建退款申请")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> CreateRefund( CreateRefundRequestDto request, CancellationToken cancellationToken) { return Ok(await commerceAdminService.CreateRefundRequestAsync( ResolveActor(), request.ToCommand(), cancellationToken)); } [HttpPost("refunds/status")] [EndpointSummary("更新退款状态")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> UpdateRefundStatus( UpdateRefundStatusDto request, CancellationToken cancellationToken) { return Ok(await commerceAdminService.UpdateRefundStatusAsync( ResolveActor(), request.ToCommand(), cancellationToken)); } [HttpGet("refunds/{refundRequestId:guid}/events")] [EndpointSummary("查询退款事件")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> RefundEvents( Guid refundRequestId, CancellationToken cancellationToken) { return Ok(await commerceAdminService.GetRefundEventsAsync( ResolveActor(), refundRequestId, cancellationToken)); } [HttpGet("reconciliation/batches")] [Authorize(Policy = TikuPolicies.TenantCommerceOperateAllScope)] [EndpointSummary("查询对账批次")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> ReconciliationBatches( [FromQuery] TenantCommerceQueryDto query, CancellationToken cancellationToken) { return Ok(await commerceAdminService.GetReconciliationBatchesAsync( ResolveActor(), ToQuery(query), cancellationToken)); } [HttpPost("reconciliation/batches")] [Authorize(Policy = TikuPolicies.TenantCommerceOperateAllScope)] [EndpointSummary("创建对账批次")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> CreateReconciliationBatch( CreateReconciliationBatchDto request, CancellationToken cancellationToken) { return Ok(await commerceAdminService.CreateReconciliationBatchAsync( ResolveActor(), request.ToCommand(), cancellationToken)); } [HttpGet("reconciliation/issues")] [Authorize(Policy = TikuPolicies.TenantCommerceOperateAllScope)] [EndpointSummary("查询对账异常")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> ReconciliationIssues( [FromQuery] TenantCommerceQueryDto query, CancellationToken cancellationToken) { return Ok(await commerceAdminService.GetReconciliationIssuesAsync( ResolveActor(), ToQuery(query), cancellationToken)); } [HttpGet("reconciliation/items")] [Authorize(Policy = TikuPolicies.TenantCommerceOperateAllScope)] [EndpointSummary("查询对账明细")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> ReconciliationItems( [FromQuery] Guid batchId, CancellationToken cancellationToken) { return Ok(await commerceAdminService.GetReconciliationItemsAsync( ResolveActor(), batchId, cancellationToken)); } [HttpGet("reconciliation/issues/events")] [Authorize(Policy = TikuPolicies.TenantCommerceOperateAllScope)] [EndpointSummary("查询对账工单事件")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> ReconciliationIssueEvents( [FromQuery] Guid issueId, CancellationToken cancellationToken) { return Ok(await commerceAdminService.GetReconciliationIssueEventsAsync( ResolveActor(), issueId, cancellationToken)); } [HttpPost("reconciliation/issues/status")] [Authorize(Policy = TikuPolicies.TenantCommerceOperateAllScope)] [EndpointSummary("更新对账异常状态")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> UpdateReconciliationIssue( UpdateReconciliationIssueDto request, CancellationToken cancellationToken) { return Ok(await commerceAdminService.UpdateReconciliationIssueAsync( ResolveActor(), request.ToCommand(), cancellationToken)); } [HttpGet("adjustment-vouchers")] [Authorize(Policy = TikuPolicies.TenantCommerceOperateAllScope)] [EndpointSummary("查询调账凭证")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> AdjustmentVouchers( [FromQuery] TenantCommerceQueryDto query, CancellationToken cancellationToken) { return Ok(await commerceAdminService.GetAdjustmentVouchersAsync( ResolveActor(), ToQuery(query), cancellationToken)); } [HttpGet("adjustment-vouchers/detail")] [Authorize(Policy = TikuPolicies.TenantCommerceOperateAllScope)] [EndpointSummary("查询调账凭证详情")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> AdjustmentVoucherDetail( [FromQuery] Guid voucherId, CancellationToken cancellationToken) { return Ok(await commerceAdminService.GetAdjustmentVoucherAsync( ResolveActor(), voucherId, cancellationToken)); } [HttpPost("adjustment-vouchers")] [Authorize(Policy = TikuPolicies.TenantCommerceOperateAllScope)] [EndpointSummary("创建调账凭证")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> CreateAdjustmentVoucher( CreateAdjustmentVoucherDto request, CancellationToken cancellationToken) { return Ok(await commerceAdminService.CreateAdjustmentVoucherAsync( ResolveActor(), request.ToCommand(), cancellationToken)); } [HttpPost("adjustment-vouchers/status")] [Authorize(Policy = TikuPolicies.TenantCommerceOperateAllScope)] [EndpointSummary("审核或关闭调账凭证")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> UpdateAdjustmentVoucherStatus( UpdateAdjustmentVoucherStatusDto request, CancellationToken cancellationToken) { return Ok(await commerceAdminService.UpdateAdjustmentVoucherStatusAsync( ResolveActor(), request.ToCommand(), cancellationToken)); } [HttpGet("adjustment-vouchers/events")] [Authorize(Policy = TikuPolicies.TenantCommerceOperateAllScope)] [EndpointSummary("查询调账凭证事件")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> AdjustmentVoucherEvents( [FromQuery] Guid voucherId, CancellationToken cancellationToken) { return Ok(await commerceAdminService.GetAdjustmentVoucherEventsAsync( ResolveActor(), voucherId, cancellationToken)); } [HttpGet("adjustment-vouchers/report")] [Authorize(Policy = TikuPolicies.TenantCommerceOperateAllScope)] [EndpointSummary("查询调账统计报告")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> AdjustmentVoucherReport(CancellationToken cancellationToken) { return Ok(await commerceAdminService.GetAdjustmentReportAsync( ResolveActor(), cancellationToken)); } [HttpGet("reconciliation/anomalies")] [Authorize(Policy = TikuPolicies.TenantCommerceOperateAllScope)] [EndpointSummary("查询对账异常汇总")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> ReconciliationAnomalies(CancellationToken cancellationToken) { return Ok(await commerceAdminService.GetAnomalySummaryAsync( ResolveActor(), cancellationToken)); } [HttpPost("reconciliation/preview")] [Authorize(Policy = TikuPolicies.TenantCommerceOperateAllScope)] [EndpointSummary("预览支付渠道账单对账")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> PreviewReconciliation( PreviewReconciliationImportDto request, CancellationToken cancellationToken) { return Ok(await commerceAdminService.PreviewReconciliationImportAsync( ResolveActor(), request.ToPreviewCommand(), cancellationToken)); } [HttpPost("reconciliation/import")] [Authorize(Policy = TikuPolicies.TenantCommerceOperateAllScope)] [EndpointSummary("导入支付渠道账单并创建对账批次")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> ImportReconciliation( PreviewReconciliationImportDto request, CancellationToken cancellationToken) { return Ok(await commerceAdminService.ImportReconciliationAsync( ResolveActor(), request.ToImportCommand(), cancellationToken)); } [HttpGet("reconciliation/provider-bills/jobs")] [Authorize(Policy = TikuPolicies.TenantCommerceOperateAllScope)] [EndpointSummary("查询渠道账单下载任务")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> ProviderBillJobs( [FromQuery] TenantCommerceQueryDto query, CancellationToken cancellationToken) { return Ok(await commerceAdminService.GetProviderBillJobsAsync( ResolveActor(), ToQuery(query), cancellationToken)); } [HttpPost("reconciliation/provider-bills/request")] [Authorize(Policy = TikuPolicies.TenantCommerceOperateAllScope)] [EndpointSummary("创建渠道官方账单下载任务")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> RequestProviderBill( RequestProviderBillJobDto request, CancellationToken cancellationToken) { return Ok(await commerceAdminService.RequestProviderBillJobAsync( ResolveActor(), request.ToCommand(), cancellationToken)); } private CommerceAdminActor ResolveActor() { if (currentTenant.TenantId is null || currentUser.UserId is null) { throw new CommerceException("Tenant commerce actor was not resolved.", "tenant_admin_access_denied"); } return new CommerceAdminActor(currentTenant.TenantId.Value, currentUser.UserId.Value); } private static CommerceAdminQuery ToQuery(TenantCommerceQueryDto query) { return new CommerceAdminQuery(query.Provider, query.Status, query.Limit); } private static TenantPointQuery ToPointQuery(TenantCommerceQueryDto query) { return new TenantPointQuery(query.Status, query.Limit, query.UserId, query.RegionId); } }