This commit is contained in:
南浔
2025-07-31 22:52:41 +08:00
73 changed files with 7108 additions and 16 deletions
@@ -0,0 +1,100 @@
using Apimanager_backend.Models;
using System.Reflection;
using System;
using Apimanager_backend.Data;
using Microsoft.EntityFrameworkCore;
using Apimanager_backend.Exceptions;
using Apimanager_backend.Dtos;
using Public;
using Apimanager_backend.Services;
namespace Apimanager_backend.Tools
{
public class BillableApiDispatcher
{
private readonly IServiceProvider _serviceProvider;
private readonly ApiContext _db;
private PluginLoaderService _pluginLoaderService;
public BillableApiDispatcher(IServiceProvider sp, ApiContext db,PluginLoaderService pluginLoaderService)
{
_serviceProvider = sp;
_db = db;
_pluginLoaderService = pluginLoaderService;
}
public async Task<object> DispatchAsync(string webPath,int apiId,string code, ApiCallContext context)
{
//_pluginLoaderService.LoadPlugins(new ServiceCollection());
// 1. 安全验证
string dllPath = webPath + "\\ApiHandler\\" + code + ".dll";
if (!System.IO.File.Exists(dllPath))
throw new BaseException(3002, "接口实现未找到");
// 2. 加载程序集
var assembly = Assembly.LoadFrom(dllPath); // 或使用 AssemblyLoadContext.Default.LoadFromAssemblyPath
// 3. 查找实现类
var type = assembly.GetTypes()
.FirstOrDefault(t =>
typeof(IBillableApiHandler).IsAssignableFrom(t) &&
!t.IsInterface &&
!t.IsAbstract &&
t.Name.Equals(code, StringComparison.OrdinalIgnoreCase));
if (type == null)
throw new BaseException(3002,"接口实现未找到");
List<UserPackage> userPackages = context.HttpContext.Items["userPackages"] as List<UserPackage>;
List<UserPackage> userPackageList = null;
if (userPackages == null || !(userPackages is IEnumerable<UserPackage>))
{
throw new BaseException(3004, "未购买该API套餐或权限不足");
}
userPackageList = ((IEnumerable<UserPackage>)userPackages).ToList();
userPackageList = userPackageList.Where(x => x.ExpiryDate > DateTime.Now).ToList();
if (userPackageList.Count == 0)
{
throw new BaseException(3004, "未购买该API套餐或权限不足");
}
var maxCountUserPackage = userPackageList.MaxBy(x => x.RemainingCalls);
if (maxCountUserPackage.RemainingCalls <= 0)
{
throw new BaseException(3004, "套餐可用额度不足");
}
var uPackage = await _db.UserPackages.SingleOrDefaultAsync(x => x.Id == maxCountUserPackage.Id);
if (uPackage == null)
{
throw new BaseException(3004, "未购买该API套餐或权限不足");
}
uPackage.RemainingCalls -= 1;
_db.UserPackages.Update(uPackage);
var handler = (IBillableApiHandler)Activator.CreateInstance(type)!;
// 执行接口
var result = await handler.ExecuteAsync(context);
// 记录调用日志
_db.CallLogs.Add(new ApiCallLog
{
ApiId = apiId,
UserId = context.UserId,
CallTime = DateTime.UtcNow
});
await _db.SaveChangesAsync();
return result;
}
}
}
+53
View File
@@ -0,0 +1,53 @@
using Apimanager_backend.Dtos;
using Newtonsoft.Json;
using System.Security.Cryptography;
using System.Text;
namespace Apimanager_backend.Tools
{
public static class EpayHelper
{
/// <summary>
/// 创建签名
/// </summary>
public static string CreateSign(Dictionary<string, string> parameters, string key)
{
var sorted = parameters
.Where(kv => !string.IsNullOrEmpty(kv.Value) && kv.Key != "sign" && kv.Key != "sign_type")
.OrderBy(kv => kv.Key)
.ToList();
string query = string.Join("&", sorted.Select(kv => $"{kv.Key}={kv.Value}"));
string toSign = query + key;
using (var md5 = MD5.Create())
{
byte[] hash = md5.ComputeHash(Encoding.UTF8.GetBytes(toSign));
return BitConverter.ToString(hash).Replace("-", "").ToLower();
}
}
/// <summary>
/// 发起API支付(返回 json
/// </summary>
public static async Task<EpayResponse?> ApiPayAsync(Dictionary<string, string> parameters, string key,string baseUrl)
{
parameters["sign_type"] = "MD5";
parameters["sign"] = CreateSign(parameters, key);
using var client = new HttpClient();
var content = new FormUrlEncodedContent(parameters);
var response = await client.PostAsync($"{baseUrl}/mapi.php", content);
return JsonConvert.DeserializeObject<EpayResponse>(await response.Content.ReadAsStringAsync());
}
/// <summary>
/// 校验签名(通常用于异步通知/页面跳转)
/// </summary>
public static bool VerifySign(Dictionary<string, string> parameters, string key)
{
if (!parameters.TryGetValue("sign", out string originalSign)) return false;
string newSign = CreateSign(parameters, key);
return originalSign.Equals(newSign, StringComparison.OrdinalIgnoreCase);
}
}
}
@@ -0,0 +1,14 @@
namespace Apimanager_backend.Tools
{
public static class OrderNumberGenerator
{
public static string Generate(int userId = 0, string prefix = "ORD")
{
var datePart = DateTime.UtcNow.ToString("yyyyMMddHHmmssfff"); // 17位
var userPart = (userId % 10000).ToString("D4"); // 后4位用户ID
var randPart = Random.Shared.Next(1000, 9999).ToString(); // 4位随机数
return $"{prefix}{datePart}{userPart}{randPart}"; // 最终:ORD + 时间 + 用户 + 随机数
}
}
}
@@ -0,0 +1,29 @@
namespace Apimanager_backend.Tools
{
public static class UserInfoHelper
{
public static string GetClientIp(HttpContext context)
{
var ip = context.Connection.RemoteIpAddress?.ToString();
// 支持代理(如 Nginx)时使用 X-Forwarded-For
if (context.Request.Headers.ContainsKey("X-Forwarded-For"))
{
ip = context.Request.Headers["X-Forwarded-For"].FirstOrDefault();
}
return ip ?? "unknown";
}
public static string GetDeviceType(HttpContext context)
{
var userAgent = context.Request.Headers["User-Agent"].ToString().ToLower();
if (userAgent.Contains("mobile") || userAgent.Contains("android") || userAgent.Contains("iphone"))
return "mobile";
return "pc";
}
}
}