using MediatR; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using TakeoutSaaS.Application.App.Merchants.Commands; using TakeoutSaaS.Application.App.Merchants.Dto; using TakeoutSaaS.Application.App.Merchants.Queries; using TakeoutSaaS.Domain.Merchants.Enums; using TakeoutSaaS.Module.Authorization.Attributes; using TakeoutSaaS.Shared.Abstractions.Constants; using TakeoutSaaS.Shared.Abstractions.Results; using TakeoutSaaS.Shared.Web.Api; namespace TakeoutSaaS.AdminApi.Controllers; /// /// 商户管理。 /// /// /// 初始化控制器。 /// [ApiVersion("1.0")] [Authorize] [Route("api/admin/v{version:apiVersion}/merchants")] public sealed class MerchantsController(IMediator mediator) : BaseApiController { /// /// 创建商户。 /// [HttpPost] [PermissionAuthorize("merchant:create")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task> Create([FromBody] CreateMerchantCommand command, CancellationToken cancellationToken) { var result = await mediator.Send(command, cancellationToken); return ApiResponse.Ok(result); } /// /// 查询商户列表。 /// [HttpGet] [PermissionAuthorize("merchant:read")] [ProducesResponseType(typeof(ApiResponse>), StatusCodes.Status200OK)] public async Task>> List( [FromQuery] MerchantStatus? status, [FromQuery] int page = 1, [FromQuery] int pageSize = 20, [FromQuery] string? sortBy = null, [FromQuery] bool sortDesc = true, CancellationToken cancellationToken = default) { var result = await mediator.Send(new SearchMerchantsQuery { Status = status, Page = page, PageSize = pageSize, SortBy = sortBy, SortDescending = sortDesc }, cancellationToken); return ApiResponse>.Ok(result); } /// /// 更新商户。 /// [HttpPut("{merchantId:long}")] [PermissionAuthorize("merchant:update")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status404NotFound)] public async Task> Update(long merchantId, [FromBody] UpdateMerchantCommand command, CancellationToken cancellationToken) { if (command.MerchantId == 0) { command = command with { MerchantId = merchantId }; } var result = await mediator.Send(command, cancellationToken); return result == null ? ApiResponse.Error(ErrorCodes.NotFound, "商户不存在") : ApiResponse.Ok(result); } /// /// 删除商户。 /// [HttpDelete("{merchantId:long}")] [PermissionAuthorize("merchant:delete")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status404NotFound)] public async Task> Delete(long merchantId, CancellationToken cancellationToken) { var success = await mediator.Send(new DeleteMerchantCommand { MerchantId = merchantId }, cancellationToken); return success ? ApiResponse.Ok(null) : ApiResponse.Error(ErrorCodes.NotFound, "商户不存在"); } /// /// 获取商户概览。 /// [HttpGet("{merchantId:long}")] [PermissionAuthorize("merchant:read")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status404NotFound)] public async Task> Detail(long merchantId, CancellationToken cancellationToken) { var result = await mediator.Send(new GetMerchantByIdQuery { MerchantId = merchantId }, cancellationToken); return result == null ? ApiResponse.Error(ErrorCodes.NotFound, "商户不存在") : ApiResponse.Ok(result); } /// /// 获取商户详细资料(含证照、合同)。 /// [HttpGet("{merchantId:long}/detail")] [PermissionAuthorize("merchant:read")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task> FullDetail(long merchantId, CancellationToken cancellationToken) { var result = await mediator.Send(new GetMerchantDetailQuery(merchantId), cancellationToken); return ApiResponse.Ok(result); } /// /// 上传商户证照信息(先通过文件上传接口获取 COS 地址)。 /// [HttpPost("{merchantId:long}/documents")] [PermissionAuthorize("merchant:update")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task> CreateDocument( long merchantId, [FromBody] AddMerchantDocumentCommand body, CancellationToken cancellationToken) { var command = body with { MerchantId = merchantId }; var result = await mediator.Send(command, cancellationToken); return ApiResponse.Ok(result); } /// /// 商户证照列表。 /// [HttpGet("{merchantId:long}/documents")] [PermissionAuthorize("merchant:read")] [ProducesResponseType(typeof(ApiResponse>), StatusCodes.Status200OK)] public async Task>> Documents(long merchantId, CancellationToken cancellationToken) { var result = await mediator.Send(new GetMerchantDocumentsQuery(merchantId), cancellationToken); return ApiResponse>.Ok(result); } /// /// 审核指定证照。 /// [HttpPost("{merchantId:long}/documents/{documentId:long}/review")] [PermissionAuthorize("merchant:review")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task> ReviewDocument( long merchantId, long documentId, [FromBody] ReviewMerchantDocumentCommand body, CancellationToken cancellationToken) { var command = body with { MerchantId = merchantId, DocumentId = documentId }; var result = await mediator.Send(command, cancellationToken); return ApiResponse.Ok(result); } /// /// 新增商户合同。 /// [HttpPost("{merchantId:long}/contracts")] [PermissionAuthorize("merchant:update")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task> CreateContract( long merchantId, [FromBody] CreateMerchantContractCommand body, CancellationToken cancellationToken) { var command = body with { MerchantId = merchantId }; var result = await mediator.Send(command, cancellationToken); return ApiResponse.Ok(result); } /// /// 合同列表。 /// [HttpGet("{merchantId:long}/contracts")] [PermissionAuthorize("merchant:read")] [ProducesResponseType(typeof(ApiResponse>), StatusCodes.Status200OK)] public async Task>> Contracts(long merchantId, CancellationToken cancellationToken) { var result = await mediator.Send(new GetMerchantContractsQuery(merchantId), cancellationToken); return ApiResponse>.Ok(result); } /// /// 更新合同状态(生效/终止等)。 /// [HttpPut("{merchantId:long}/contracts/{contractId:long}/status")] [PermissionAuthorize("merchant:update")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task> UpdateContractStatus( long merchantId, long contractId, [FromBody] UpdateMerchantContractStatusCommand body, CancellationToken cancellationToken) { var command = body with { MerchantId = merchantId, ContractId = contractId }; var result = await mediator.Send(command, cancellationToken); return ApiResponse.Ok(result); } /// /// 审核商户(通过/驳回)。 /// [HttpPost("{merchantId:long}/review")] [PermissionAuthorize("merchant:review")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task> Review(long merchantId, [FromBody] ReviewMerchantCommand body, CancellationToken cancellationToken) { var command = body with { MerchantId = merchantId }; var result = await mediator.Send(command, cancellationToken); return ApiResponse.Ok(result); } /// /// 审核日志。 /// [HttpGet("{merchantId:long}/audits")] [PermissionAuthorize("merchant:read")] [ProducesResponseType(typeof(ApiResponse>), StatusCodes.Status200OK)] public async Task>> AuditLogs( long merchantId, [FromQuery] int page = 1, [FromQuery] int pageSize = 20, CancellationToken cancellationToken = default) { var result = await mediator.Send(new GetMerchantAuditLogsQuery(merchantId, page, pageSize), cancellationToken); return ApiResponse>.Ok(result); } /// /// 可选商户类目列表。 /// [HttpGet("categories")] [PermissionAuthorize("merchant:read")] [ProducesResponseType(typeof(ApiResponse>), StatusCodes.Status200OK)] public async Task>> Categories(CancellationToken cancellationToken) { var result = await mediator.Send(new GetMerchantCategoriesQuery(), cancellationToken); return ApiResponse>.Ok(result); } }