XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFEExtension.NetCore.ServerInteractive

[DLL] Server interaction extension, including user identity verification and querying in conjunction with AutoConfig

公开
关注 0 Fork 0 Star 0
UTF-8
using System.Net;
using System.Reflection;
using System.Text.Json;
using XFEExtension.NetCore.AutoConfig;
using XFEExtension.NetCore.Exceptions;
using XFEExtension.NetCore.ServerInteractive.Interfaces;
using XFEExtension.NetCore.ServerInteractive.Models;
using XFEExtension.NetCore.ServerInteractive.Models.ServerModels;
using XFEExtension.NetCore.ServerInteractive.Models.UserModels;
using XFEExtension.NetCore.ServerInteractive.Utilities.Helpers;
using XFEExtension.NetCore.StringExtension;
using XFEExtension.NetCore.XFETransform.Json;

namespace XFEExtension.NetCore.ServerInteractive.Utilities.DataTable;

/// <summary>
/// XFE数据表格
/// </summary>
/// <typeparam name="T"></typeparam>
public class XFEDataListTable<T> : IXFEDataTable where T : IIdModel
{
    private readonly SemaphoreSlim _operationGate = new(1, 1);
    /// <inheritdoc/>
    public Func<IEnumerable<EncryptedUserLoginModel>> GetEncryptedUserLoginModelFunction { get; set; } = () => [];
    /// <inheritdoc/>
    public Func<IEnumerable<IUserInfo>> GetUsersFunction { get; set; } = () => [];
    /// <summary>
    /// 获取列表的方法
    /// </summary>
    public Func<IEnumerable<T>> GetTableFunction { get; set; } = () => [];
    /// <summary>
    /// 添加元素至列表的方法
    /// </summary>
    public Action<T> AddToTableFunction { get; set; } = _ => { };
    /// <summary>
    /// 从列表中移除元素的方法
    /// </summary>
    public Action<string> RemoveFromTableFunction { get; set; } = _ => { };
    /// <summary>
    /// 更改列表中元素的方法
    /// </summary>
    public Action<T> ChangeItemTableFunction { get; set; } = _ => { };
    /// <inheritdoc/>
    public string TableShowName { get; set; } = typeof(T).Name;
    /// <inheritdoc/>
    public string TableName { get; set; } = typeof(T).Name;
    /// <inheritdoc/>
    public string TableNameInRequest { get; set; }
    /// <inheritdoc/>
    public int GetPermissionLevel { get; set; }
    /// <inheritdoc/>
    public int RemovePermissionLevel { get; set; }
    /// <inheritdoc/>
    public int ChangePermissionLevel { get; set; }
    /// <inheritdoc/>
    public int AddPermissionLevel { get; set; }
    /// <inheritdoc/>
    public JsonSerializerOptions? JsonSerializerOptions { get; set; }
    /// <inheritdoc/>
    public JsonSerializerOptions? UserJsonSerializerOptions { get; set; }

    /// <summary>
    /// 创建列表并手动设置Get Set方法等
    /// </summary>
    public XFEDataListTable() => TableNameInRequest = $"{TableName[0]}".ToLower() + TableName[1..];

    /// <summary>
    /// 根据指定type自动设置Get Set方法
    /// </summary>
    /// <param name="type">继承于XFEProfile的配置文件类型</param>
    /// <remarks>
    /// 对于自动设置Get Set方法的Table,请确保配置文件使用XFEProfile创建ProfileList,属性名称为XXXTable
    /// </remarks>
    public XFEDataListTable(Type type)
    {
        TableNameInRequest = $"{TableName[0]}".ToLower() + TableName[1..];
        var property = type.GetProperty($"{typeof(T).Name}Table", BindingFlags.Public | BindingFlags.Static);
        var profileListType = typeof(ProfileList<T>);
        var addMethod = profileListType.GetMethod("Add", [typeof(T)]);
        var removeMethod = profileListType.GetMethod("Remove", [typeof(T)]);
        var saveMethod = type.GetMethod("SaveProfile", BindingFlags.Public | BindingFlags.Static);
        if (property is null || addMethod is null || removeMethod is null || saveMethod is null) return;
        GetTableFunction = () => property.GetValue(null) as ProfileList<T> ?? [];
        AddToTableFunction = item => addMethod.Invoke(GetTableFunction(), [item]);
        RemoveFromTableFunction = id =>
        {
            var table = GetTableFunction();
            removeMethod.Invoke(table, [table.FirstOrDefault(item => item.Id == id)]);
        };
        ChangeItemTableFunction = item =>
        {
            var table = GetTableFunction();
            var targetItem = table.FirstOrDefault(originItem => originItem.Id == item.Id);
            if (targetItem is null) return;
            foreach (var itemProperty in targetItem.GetType().GetProperties())
            {
                itemProperty.SetValue(targetItem, itemProperty.GetValue(item));
            }
            saveMethod.Invoke(null, []);
        };
    }

    /// <summary>
    /// 获取列表
    /// </summary>
    /// <returns></returns>
    public IEnumerable<T> GetList() => GetTableFunction();

    /// <summary>
    /// 添加一个元素
    /// </summary>
    /// <param name="item"></param>
    public void Add(T item) => AddToTableFunction(item);

    /// <summary>
    /// 移除一个元素
    /// </summary>
    /// <param name="id"></param>
    public void Remove(string id) => RemoveFromTableFunction(id);

    /// <summary>
    /// 更改一个元素
    /// </summary>
    /// <param name="item"></param>
    public void Change(T item) => ChangeItemTableFunction(item);

    /// <inheritdoc/>
    public async Task<HttpStatusCode> Execute(string execute, XFEJsonNode requestJsonNode, ServerCoreReturnArgs r)
    {
        var statusCode = HttpStatusCode.OK;
        await _operationGate.WaitAsync().ConfigureAwait(false);
        try
        {
            switch (execute)
            {
                case "get":
                    Console.Write($"获取{TableShowName}列表请求");
                    UserHelper.ValidatePermission(requestJsonNode["session"]?.GetString(), requestJsonNode["deviceInfo"]?.GetString(), r.ClientIP, GetPermissionLevel, GetEncryptedUserLoginModelFunction(), GetUsersFunction(), r);
                    List<T> tableList = [.. GetTableFunction()];
                    var pageSize = requestJsonNode["pageSize"]?.GetValue<int>() ?? requestJsonNode["pageCount"]?.GetValue<int>() ?? -1;
                    if (pageSize == -1)
                    {
                        await r.Args.ReplyAndClose(JsonSerializer.Serialize(new
                        {
                            totalCount = tableList.Count,
                            lastPage = -1,
                            dataList = tableList.Select(item => ExternalDataTableItem.From(item)).ToArray()
                        }, GetResponseJsonOptions()), HttpStatusCode.OK, "application/json; charset=utf-8");
                    }
                    else
                    {
                        var page = requestJsonNode["page"]?.GetValue<int>() ?? 1;
                        if (pageSize is < 1 or > 1000 || page < 1)
                            throw r.Error("page 必须大于等于 1,pageSize 必须介于 1 和 1000 之间", HttpStatusCode.BadRequest);
                        var skip = (long)(page - 1) * pageSize;
                        await r.Args.ReplyAndClose(JsonSerializer.Serialize(new
                        {
                            totalCount = tableList.Count,
                            lastPage = (int)Math.Ceiling((double)tableList.Count / pageSize),
                            dataList = skip >= tableList.Count ? [] : tableList.Skip(checked((int)skip)).Take(pageSize).Select(item => ExternalDataTableItem.From(item)).ToArray()
                        }, GetResponseJsonOptions()), HttpStatusCode.OK, "application/json; charset=utf-8");
                    }
                    break;
                case "add":
                    Console.Write($"添加{TableShowName}请求");
                    var item = JsonSerializer.Deserialize<T>(Convert.FromBase64String(requestJsonNode["data"]?.GetString() ?? string.Empty), JsonSerializerOptions);
                    if (item is null)
                    {
                        statusCode = HttpStatusCode.BadRequest;
                        throw new StopAction(() => { }, $"\n无法使用Json转换目标{TableShowName}信息");
                    }
                    Console.Write($":{item.Id}");
                    UserHelper.ValidatePermission(requestJsonNode["session"]?.GetString(), requestJsonNode["deviceInfo"]?.GetString(), r.ClientIP, AddPermissionLevel, GetEncryptedUserLoginModelFunction(), GetUsersFunction(), r);
                    if (item is IUserInfo newUser && !UserHelper.PreparePasswordCredential(newUser))
                        throw r.Error("新用户必须提供密码", HttpStatusCode.BadRequest);
                    if (item.Id.IsNullOrWhiteSpace())
                        item.Id = Guid.NewGuid().ToString("N");
                    if (IdModelHelper.HasSameId(item.Id, GetTableFunction().Cast<IIdModel>()))
                        throw r.Error($"{TableShowName}ID已存在:{item.Id}", HttpStatusCode.Conflict);
                    item.Version = 1;
                    Add(item);
                    await r.Args.ReplyAndClose(JsonSerializer.Serialize(new { id = item.Id, version = item.Version }), HttpStatusCode.Created, "application/json; charset=utf-8");
                    break;
                case "remove":
                    Console.Write($"删除{TableShowName}请求");
                    var id = requestJsonNode["id"]?.GetString();
                    Console.Write($":{id}");
                    UserHelper.ValidatePermission(requestJsonNode["session"]?.GetString(), requestJsonNode["deviceInfo"]?.GetString(), r.ClientIP, RemovePermissionLevel, GetEncryptedUserLoginModelFunction(), GetUsersFunction(), r);
                    if (id.IsNullOrWhiteSpace())
                    {
                        statusCode = HttpStatusCode.BadRequest;
                        throw new StopAction(() => { }, $"{TableShowName}ID不能为空");
                    }
                    if (!GetTableFunction().Any(existing => existing.Id == id))
                        throw r.Error($"未找到{TableShowName}:{id}", HttpStatusCode.NotFound);
                    Remove(id);
                    r.Args.Close();
                    break;
                case "change":
                    Console.Write($"更改{TableShowName}请求");
                    item = JsonSerializer.Deserialize<T>(Convert.FromBase64String(requestJsonNode["data"]?.GetString() ?? string.Empty), JsonSerializerOptions);
                    if (item is null)
                    {
                        statusCode = HttpStatusCode.BadRequest;
                        throw new StopAction(() => { }, $"\n无法使用Json转换目标{TableShowName}信息");
                    }
                    Console.Write($":{item.Id}");
                    if (item.Id.IsNullOrWhiteSpace() || item.Id.IsNullOrWhiteSpace())
                    {
                        statusCode = HttpStatusCode.BadRequest;
                        throw new StopAction(() => { }, $"\n{TableShowName}ID不能为空");
                    }
                    UserHelper.ValidatePermission(requestJsonNode["session"]?.GetString(), requestJsonNode["deviceInfo"]?.GetString(), r.ClientIP, ChangePermissionLevel, GetEncryptedUserLoginModelFunction(), GetUsersFunction(), r);
                    var existingItem = GetTableFunction().FirstOrDefault(existing => existing.Id == item.Id);
                    if (existingItem is null)
                        throw r.Error($"未找到{TableShowName}:{item.Id}", HttpStatusCode.NotFound);
                    if (item.Version != existingItem.Version)
                        throw r.Error($"{TableShowName}已被其他请求修改,请刷新后重试", HttpStatusCode.Conflict);
                    if (item is IUserInfo changedUser && existingItem is IUserInfo existingUser &&
                        !UserHelper.PreparePasswordCredential(changedUser, existingUser))
                        throw r.Error("用户密码凭据无效", HttpStatusCode.BadRequest);
                    item.Version = checked(existingItem.Version + 1);
                    Change(item);
                    await r.Args.ReplyAndClose(JsonSerializer.Serialize(new { id = item.Id, version = item.Version }), HttpStatusCode.OK, "application/json; charset=utf-8");
                    break;
                default:
                    Console.WriteLine($"[ERROR] 试图查询不存在的表格:{execute}");
                    await r.Args.ReplyAndClose($"试图查询不存在的表格:{execute}", HttpStatusCode.BadRequest);
                    break;
            }
        }
        catch (ServerCoreReturnArgs returnArgs)
        {
            Console.WriteLine($"[WARN]({r.ServerCore.ServerCoreName}){returnArgs.ReturnMessage}");
            await returnArgs.Args.ReplyAndClose(returnArgs.ReturnMessage, returnArgs.StatusCode);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"[WARN]({r.ServerCore.ServerCoreName}){ExceptionHelper.GetExceptionMessage(ex)}");
            Console.WriteLine($"[TRACE]{ex.StackTrace}");
            await r.Args.ReplyAndClose("服务器内部异常", HttpStatusCode.InternalServerError);
        }
        finally
        {
            _operationGate.Release();
        }
        return statusCode;
    }

    private JsonSerializerOptions? GetResponseJsonOptions()
        => typeof(IUserInfo).IsAssignableFrom(typeof(T)) ? UserJsonSerializerOptions ?? JsonSerializerOptions : JsonSerializerOptions;
}