Files
kami/license-system-backend/src/License.Api/Controllers/AdminAgentsController.cs
2026-01-04 23:00:21 +08:00

330 lines
12 KiB
C#

using System.Text.Json;
using License.Api.Data;
using License.Api.DTOs;
using License.Api.Models;
using License.Api.Services;
using License.Api.Utils;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
namespace License.Api.Controllers;
[ApiController]
[Authorize(Policy = "SuperAdmin")]
[Route("api/admin/agents")]
public class AdminAgentsController : ControllerBase
{
private readonly AppDbContext _db;
private readonly ConfigService _config;
public AdminAgentsController(AppDbContext db, ConfigService config)
{
_db = db;
_config = config;
}
[HttpPost]
public async Task<IActionResult> Create([FromBody] AgentCreateRequest request)
{
var agentSystemEnabled = await _config.GetBoolAsync("feature.agent_system", true);
if (!agentSystemEnabled)
return StatusCode(StatusCodes.Status403Forbidden, ApiResponse.Fail(403, "forbidden"));
var agent = new Agent
{
AdminId = request.AdminId,
AgentCode = request.AgentCode,
CompanyName = request.CompanyName,
ContactPerson = request.ContactPerson,
ContactPhone = request.ContactPhone,
ContactEmail = request.ContactEmail,
PasswordHash = PasswordHasher.Hash(request.Password),
Balance = request.InitialBalance,
Discount = request.Discount,
CreditLimit = request.CreditLimit,
MaxProjects = request.AllowedProjects?.Count ?? 0,
AllowedProjects = request.AllowedProjects == null ? null : JsonSerializer.Serialize(request.AllowedProjects),
Status = "active",
CreatedAt = DateTime.UtcNow,
UpdatedAt = DateTime.UtcNow
};
_db.Agents.Add(agent);
await _db.SaveChangesAsync();
var data = new AgentDetailResponse
{
Id = agent.Id,
AgentCode = agent.AgentCode,
CompanyName = agent.CompanyName,
ContactPerson = agent.ContactPerson,
ContactPhone = agent.ContactPhone,
ContactEmail = agent.ContactEmail,
Balance = agent.Balance,
Discount = agent.Discount,
CreditLimit = agent.CreditLimit,
Status = agent.Status,
CreatedAt = agent.CreatedAt
};
return Ok(ApiResponse<AgentDetailResponse>.Ok(data));
}
[HttpGet]
public async Task<IActionResult> List([FromQuery] string? status, [FromQuery] int page = 1, [FromQuery] int pageSize = 20)
{
var agentSystemEnabled = await _config.GetBoolAsync("feature.agent_system", true);
if (!agentSystemEnabled)
return StatusCode(StatusCodes.Status403Forbidden, ApiResponse.Fail(403, "forbidden"));
page = Math.Max(1, page);
pageSize = Math.Clamp(pageSize, 1, 100);
var query = _db.Agents.AsQueryable();
if (!string.IsNullOrWhiteSpace(status))
query = query.Where(a => a.Status == status);
var total = await query.CountAsync();
var items = await query.OrderByDescending(a => a.CreatedAt)
.Skip((page - 1) * pageSize)
.Take(pageSize)
.Select(a => new AgentListItem
{
Id = a.Id,
AgentCode = a.AgentCode,
CompanyName = a.CompanyName,
ContactPerson = a.ContactPerson,
ContactPhone = a.ContactPhone,
Balance = a.Balance,
Discount = a.Discount,
Status = a.Status,
CreatedAt = a.CreatedAt
})
.ToListAsync();
var result = new PagedResult<AgentListItem>
{
Items = items,
Pagination = new PaginationInfo
{
Page = page,
PageSize = pageSize,
Total = total,
TotalPages = (int)Math.Ceiling(total / (double)pageSize)
}
};
return Ok(ApiResponse<PagedResult<AgentListItem>>.Ok(result));
}
[HttpGet("{id:int}")]
public async Task<IActionResult> Get(int id)
{
var agentSystemEnabled = await _config.GetBoolAsync("feature.agent_system", true);
if (!agentSystemEnabled)
return StatusCode(StatusCodes.Status403Forbidden, ApiResponse.Fail(403, "forbidden"));
var agent = await _db.Agents.FindAsync(id);
if (agent == null)
return NotFound(ApiResponse.Fail(404, "not_found"));
var transactions = await _db.AgentTransactions
.Where(t => t.AgentId == id)
.OrderByDescending(t => t.CreatedAt)
.Take(50)
.ToListAsync();
var stats = await _db.CardKeys
.Where(c => c.AgentId == id && c.DeletedAt == null)
.GroupBy(c => c.AgentId)
.Select(g => new
{
totalCards = g.Count(),
activeCards = g.Count(x => x.Status == "active"),
totalRevenue = g.Sum(x => x.SoldPrice ?? 0)
})
.SingleOrDefaultAsync();
return Ok(ApiResponse<object>.Ok(new
{
agent = new AgentDetailResponse
{
Id = agent.Id,
AgentCode = agent.AgentCode,
CompanyName = agent.CompanyName,
ContactPerson = agent.ContactPerson,
ContactPhone = agent.ContactPhone,
ContactEmail = agent.ContactEmail,
Balance = agent.Balance,
Discount = agent.Discount,
CreditLimit = agent.CreditLimit,
Status = agent.Status,
CreatedAt = agent.CreatedAt
},
stats,
transactions
}));
}
[HttpPut("{id:int}")]
public async Task<IActionResult> Update(int id, [FromBody] AgentUpdateRequest request)
{
var agentSystemEnabled = await _config.GetBoolAsync("feature.agent_system", true);
if (!agentSystemEnabled)
return StatusCode(StatusCodes.Status403Forbidden, ApiResponse.Fail(403, "forbidden"));
var agent = await _db.Agents.FindAsync(id);
if (agent == null)
return NotFound(ApiResponse.Fail(404, "not_found"));
if (!string.IsNullOrWhiteSpace(request.CompanyName))
agent.CompanyName = request.CompanyName;
if (!string.IsNullOrWhiteSpace(request.ContactPerson))
agent.ContactPerson = request.ContactPerson;
if (!string.IsNullOrWhiteSpace(request.ContactPhone))
agent.ContactPhone = request.ContactPhone;
if (!string.IsNullOrWhiteSpace(request.ContactEmail))
agent.ContactEmail = request.ContactEmail;
if (request.Discount.HasValue)
agent.Discount = request.Discount.Value;
if (request.CreditLimit.HasValue)
agent.CreditLimit = request.CreditLimit.Value;
if (request.AllowedProjects != null)
{
agent.AllowedProjects = JsonSerializer.Serialize(request.AllowedProjects);
agent.MaxProjects = request.AllowedProjects.Count;
}
if (!string.IsNullOrWhiteSpace(request.Status))
agent.Status = request.Status;
agent.UpdatedAt = DateTime.UtcNow;
await _db.SaveChangesAsync();
return Ok(ApiResponse.Ok());
}
[HttpPost("{id:int}/disable")]
public async Task<IActionResult> Disable(int id)
{
var agentSystemEnabled = await _config.GetBoolAsync("feature.agent_system", true);
if (!agentSystemEnabled)
return StatusCode(StatusCodes.Status403Forbidden, ApiResponse.Fail(403, "forbidden"));
var agent = await _db.Agents.FindAsync(id);
if (agent == null)
return NotFound(ApiResponse.Fail(404, "not_found"));
agent.Status = "disabled";
agent.UpdatedAt = DateTime.UtcNow;
await _db.SaveChangesAsync();
return Ok(ApiResponse.Ok());
}
[HttpPost("{id:int}/enable")]
public async Task<IActionResult> Enable(int id)
{
var agentSystemEnabled = await _config.GetBoolAsync("feature.agent_system", true);
if (!agentSystemEnabled)
return StatusCode(StatusCodes.Status403Forbidden, ApiResponse.Fail(403, "forbidden"));
var agent = await _db.Agents.FindAsync(id);
if (agent == null)
return NotFound(ApiResponse.Fail(404, "not_found"));
agent.Status = "active";
agent.UpdatedAt = DateTime.UtcNow;
await _db.SaveChangesAsync();
return Ok(ApiResponse.Ok());
}
[HttpDelete("{id:int}")]
public async Task<IActionResult> Delete(int id)
{
var agentSystemEnabled = await _config.GetBoolAsync("feature.agent_system", true);
if (!agentSystemEnabled)
return StatusCode(StatusCodes.Status403Forbidden, ApiResponse.Fail(403, "forbidden"));
var agent = await _db.Agents.FindAsync(id);
if (agent == null)
return NotFound(ApiResponse.Fail(404, "not_found"));
_db.Agents.Remove(agent);
await _db.SaveChangesAsync();
return Ok(ApiResponse.Ok());
}
[HttpPost("{id:int}/recharge")]
public async Task<IActionResult> Recharge(int id, [FromBody] AgentBalanceRequest request)
{
var agentSystemEnabled = await _config.GetBoolAsync("feature.agent_system", true);
if (!agentSystemEnabled)
return StatusCode(StatusCodes.Status403Forbidden, ApiResponse.Fail(403, "forbidden"));
return await AdjustBalance(id, request.Amount, "recharge", request.Remark);
}
[HttpPost("{id:int}/deduct")]
public async Task<IActionResult> Deduct(int id, [FromBody] AgentBalanceRequest request)
{
var agentSystemEnabled = await _config.GetBoolAsync("feature.agent_system", true);
if (!agentSystemEnabled)
return StatusCode(StatusCodes.Status403Forbidden, ApiResponse.Fail(403, "forbidden"));
return await AdjustBalance(id, -Math.Abs(request.Amount), "consume", request.Remark);
}
[HttpGet("{id:int}/transactions")]
public async Task<IActionResult> Transactions(int id)
{
var agentSystemEnabled = await _config.GetBoolAsync("feature.agent_system", true);
if (!agentSystemEnabled)
return StatusCode(StatusCodes.Status403Forbidden, ApiResponse.Fail(403, "forbidden"));
var items = await _db.AgentTransactions
.Where(t => t.AgentId == id)
.OrderByDescending(t => t.CreatedAt)
.ToListAsync();
return Ok(ApiResponse<List<AgentTransaction>>.Ok(items));
}
private async Task<IActionResult> AdjustBalance(int id, decimal amount, string type, string? remark)
{
if (!User.TryGetUserId(out var adminId))
return Unauthorized(ApiResponse.Fail(401, "unauthorized"));
await using var tx = await _db.Database.BeginTransactionAsync();
var agent = await _db.Agents
.FromSqlRaw("SELECT * FROM \"Agents\" WHERE \"Id\" = {0} FOR UPDATE", id)
.FirstOrDefaultAsync();
if (agent == null)
return NotFound(ApiResponse.Fail(404, "not_found"));
var balanceBefore = agent.Balance;
var balanceAfter = balanceBefore + amount;
if (balanceAfter < -agent.CreditLimit)
return StatusCode(StatusCodes.Status403Forbidden, ApiResponse.Fail(403, "forbidden"));
agent.Balance = balanceAfter;
agent.UpdatedAt = DateTime.UtcNow;
_db.AgentTransactions.Add(new AgentTransaction
{
AgentId = agent.Id,
Type = type,
Amount = amount,
BalanceBefore = balanceBefore,
BalanceAfter = balanceAfter,
Remark = remark,
CreatedBy = adminId,
CreatedAt = DateTime.UtcNow
});
await _db.SaveChangesAsync();
await tx.CommitAsync();
return Ok(ApiResponse.Ok());
}
}