Upgrade Nuget
大石头 编写于 2024-09-28 08:10:09
NewLife.Cube
using System.Buffers;
using System.ComponentModel;
using System.Text;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;
using NewLife.Cube.Entity;
using NewLife.Data;
using NewLife.Reflection;
using XCode;
using XCode.Membership;
using static XCode.Membership.User;
using AreaX = XCode.Membership.Area;

namespace NewLife.Cube.Controllers;

/// <summary>魔方前端数据接口</summary>
[DisplayName("数据接口")]
[ApiExplorerSettings(GroupName = "Basic")]
[Route("[controller]/[action]")]
public class CubeController : ControllerBaseX
{
    private readonly IList<EndpointDataSource> _sources;

    /// <summary>构造函数</summary>
    /// <param name="sources"></param>
    public CubeController(IEnumerable<EndpointDataSource> sources)
    {
        _sources = sources.ToList();
    }

    #region 拦截
    ///// <summary>执行后</summary>
    ///// <param name="context"></param>
    //public override void OnActionExecuted(ActionExecutedContext context)
    //{
    //    if (context.Exception != null && !context.ExceptionHandled)
    //    {
    //        var ex = context.Exception.GetTrue();
    //        context.Result = Json(0, null, ex);
    //        context.ExceptionHandled = true;

    //        if (XTrace.Debug) XTrace.WriteException(ex);

    //        return;
    //    }

    //    base.OnActionExecuted(context);
    //}
    #endregion

    #region 服务器信息
    private static readonly String _OS = Environment.OSVersion + "";

    ///// <summary>服务器信息</summary>
    ///// <returns></returns>
    //[Route("[controller]")]
    //public ActionResult Get() => Info(null);

    /// <summary>服务器信息,用户健康检测</summary>
    /// <param name="state">状态信息</param>
    /// <returns></returns>
    [AllowAnonymous]
    [HttpGet]
    public ActionResult Info(String state)
    {
        var asmx = AssemblyX.Entry;
        var conn = HttpContext.Connection;
        var remote = conn.RemoteIpAddress;
        if (remote.IsIPv4MappedToIPv6) remote = remote.MapToIPv4();
        var ip = HttpContext.GetUserHost();

        var rs = new
        {
            asmx?.Name,
            asmx?.Title,
            asmx?.FileVersion,
            asmx?.Compile,
            OS = _OS,

            UserHost = ip?.ToString(),
            Remote = remote?.ToString(),
            Port = conn.LocalPort,
            Time = DateTime.Now,
            State = state,
        };

        return Json(0, null, rs);
    }
    #endregion

    #region 接口信息
    /// <summary>获取所有接口信息</summary>
    /// <returns></returns>
    [AllowAnonymous]
    [HttpGet]
    public ActionResult Apis()
    {
        var set = new List<EndpointDataSource>();
        var eps = new List<String>();
        foreach (var item in _sources)
        {
            if (!set.Contains(item))
            {
                set.Add(item);

                //eps.AddRange(item.Endpoints);
                foreach (var elm in item.Endpoints)
                {
                    var area = elm.Metadata.GetMetadata<AreaAttribute>();
                    var disp = elm.Metadata.GetMetadata<DisplayNameAttribute>();
                    var desc = elm.Metadata.GetMetadata<ControllerActionDescriptor>();
                    var post = elm.Metadata.GetMetadata<HttpPostAttribute>();
                    if (desc == null) continue;

                    //var name = area == null ?
                    //    $"{desc.ControllerName}/{desc.ActionName}" :
                    //    $"{area?.RouteValue}/{desc.ControllerName}/{desc.ActionName}";

                    var sb = new StringBuilder();
                    sb.Append(post != null ? "POST " : "GET ");
                    sb.Append(desc.ControllerName);
                    sb.Append("/");
                    sb.Append(desc.ActionName);
                    sb.Append("(");
                    sb.Append(desc.MethodInfo.GetParameters().Join(",", pi => $"{pi.ParameterType.Name} {pi.Name}"));
                    sb.Append(")");

                    var name = sb.ToString();

                    if (!eps.Contains(name)) eps.Add(name);
                }
            }
        }

        return Json(0, null, eps);
    }
    #endregion

    #region 用户
    /// <summary>用户搜索</summary>
    /// <param name="roleId"></param>
    /// <param name="departmentId"></param>
    /// <param name="key"></param>
    /// <returns></returns>
    [HttpGet]
    public ActionResult UserSearch(Int32 roleId = 0, Int32 departmentId = 0, String key = null)
    {
        var exp = new WhereExpression();
        if (roleId > 0) exp &= _.RoleID == roleId;
        if (departmentId > 0) exp &= _.DepartmentID == departmentId;
        exp &= _.Enable == true;
        if (!key.IsNullOrEmpty()) exp &= _.Code.StartsWith(key) | _.Name.StartsWith(key) | _.DisplayName.StartsWith(key) | _.Mobile.StartsWith(key);

        var page = new PageParameter { PageSize = 20 };

        // 默认排序
        if (page.Sort.IsNullOrEmpty()) page.Sort = _.Name;

        var list = XCode.Membership.User.FindAll(exp, page);

        return Json(0, null, list.Select(e => new
        {
            e.ID,
            e.Code,
            e.Name,
            e.DisplayName,
            //e.DepartmentID,
            DepartmentName = e.Department?.ToString(),
        }).ToArray());
    }
    #endregion

    #region 部门
    /// <summary>网点搜索</summary>
    /// <param name="parentid"></param>
    /// <param name="key"></param>
    /// <returns></returns>
    [HttpGet]
    public ActionResult DepartmentSearch(Int32 parentid = -1, String key = null)
    {
        var exp = new WhereExpression();
        if (parentid >= 0) exp &= Department._.ParentID == parentid;
        exp &= Department._.Enable == true & Department._.Visible == true;
        if (!key.IsNullOrEmpty()) exp &= Department._.Code.StartsWith(key) | Department._.Name.StartsWith(key) | Department._.FullName.StartsWith(key);

        var page = new PageParameter { PageSize = 20 };

        // 默认排序
        if (page.Sort.IsNullOrEmpty()) page.Sort = Department._.Name;

        var list = Department.FindAll(exp, page);

        return Json(0, null, list.Select(e => new
        {
            e.ID,
            e.Code,
            e.Name,
            e.FullName,
            //e.ManagerID,
            Manager = FindByID(e.ManagerId)?.ToString(),
        }).ToArray());
    }
    #endregion

    #region 地区
    /// <summary>地区信息</summary>
    /// <param name="id"></param>
    /// <returns></returns>
    [AllowAnonymous]
    [HttpGet]
    public ActionResult GetArea(Int32 id = 0)
    {
        var r = id <= 0 ? AreaX.Root : AreaX.FindByID(id);
        if (r == null) return Json(500, null, "找不到地区");

        return Json(0, null, new
        {
            r.ID,
            r.Name,
            r.FullName,
            r.ParentID,
            r.Level,
            r.Path,
            IdPath = r.AllParents.Where(e => e.ID > 0).Select(e => e.ID).Join("/"),
            r.ParentPath
        });
    }

    /// <summary>获取地区子级</summary>
    /// <param name="id"></param>
    /// <returns></returns>
    [AllowAnonymous]
    [HttpGet]
    public ActionResult AreaChilds(Int32 id = 0)
    {
        var r = id <= 0 ? AreaX.Root : AreaX.FindByID(id);
        if (r == null) return Json(500, null, "找不到地区");

        if (r.ID == 0)
            return Json(0, null, r.Childs.Where(e => e.Enable).Select(e => new { e.ID, e.Name, e.FullName, BigArea = e.GetBig() }).ToArray());
        else
            return Json(0, null, r.Childs.Where(e => e.Enable).Select(e => new { e.ID, e.Name, e.FullName }).ToArray());
    }

    /// <summary>获取地区父级</summary>
    /// <param name="id">查询地区编号</param>
    /// <param name="isContainSelf">是否包含查询的地区</param>
    /// <returns></returns>
    [AllowAnonymous]
    [HttpGet]
    public ActionResult AreaParents(Int32 id = 0, Boolean isContainSelf = false)
    {
        var r = id <= 0 ? AreaX.Root : AreaX.FindByID(id);
        if (r == null) return Json(500, null, "找不到地区");

        var list = new List<Object>();
        foreach (var e in r.AllParents)
        {
            if (e.ID == 0) continue;
            if (r.ID == 0)
                list.Add(new { e.ID, e.Name, e.FullName, e.ParentID, e.Level, BigArea = e.GetBig() });
            else
                list.Add(new { e.ID, e.Name, e.FullName, e.ParentID, e.Level });
        }

        if (isContainSelf) list.Add(r);

        return Json(0, null, list);
    }

    /// <summary>获取地区所有父级</summary>
    /// <param name="id"></param>
    /// <returns></returns>
    [AllowAnonymous]
    [HttpGet]
    public ActionResult AreaAllParents(Int32 id = 0)
    {
        var r = id <= 0 ? AreaX.Root : AreaX.FindByID(id);
        if (r == null) return Json(500, null, "找不到地区");

        var rs = new List<AreaX>();
        foreach (var item in r.AllParents)
        {
            rs.AddRange(item.Parent.Childs.Where(e => e.Enable));
        }
        rs.AddRange(r.Parent.Childs);

        var list = new List<Object>();
        foreach (var e in rs)
        {
            if (e.ParentID == 0)
                list.Add(new { e.ID, e.Name, e.ParentID, e.Level, BigArea = e.GetBig() });
            else
                list.Add(new { e.ID, e.Name, e.ParentID, e.Level });
        }

        return Json(0, null, list);
    }
    #endregion

    #region 头像
    /// <summary>获取用户头像</summary>
    /// <param name="id">用户编号</param>
    /// <returns></returns>
    [AllowAnonymous]
    [HttpGet]
    public virtual ActionResult Avatar(Int32 id)
    {
        if (id <= 0) throw new ArgumentNullException(nameof(id));

        var user = ManageProvider.Provider?.FindByID(id) as IUser;
        if (user == null) throw new Exception("用户不存在 " + id);

        var set = CubeSetting.Current;
        var av = "";
        if (!user.Avatar.IsNullOrEmpty() && !user.Avatar.StartsWith("/"))
        {
            av = set.AvatarPath.CombinePath(user.Avatar).GetBasePath();
            if (!System.IO.File.Exists(av)) av = null;
        }

        // 用于兼容旧代码
        if (av.IsNullOrEmpty() && !set.AvatarPath.IsNullOrEmpty())
        {
            av = set.AvatarPath.CombinePath(user.ID + ".png").GetBasePath();
            if (!System.IO.File.Exists(av)) av = null;
        }

        if (!System.IO.File.Exists(av)) throw new Exception("用户头像不存在 " + id);

        var vs = System.IO.File.ReadAllBytes(av);
        return File(vs, "image/png");
    }
    #endregion

    #region 字典参数
    /// <summary>查询一批Code的数据源</summary>
    /// <param name="codes"></param>
    /// <returns></returns>
    [AllowAnonymous]
    [HttpGet]
    public ActionResult Lookup(String codes)
    {
        var source = new Dictionary<String, Object>();
        foreach (var code in codes.Split(","))
        {
            var type = code.GetTypeEx();
            if (type != null && type.IsEnum)
            {
                var ns = Enum.GetNames(type);
                var vs = Enum.GetValues(type);
                var list = new Dictionary<String, Object>[ns.Length];
                for (var i = 0; i < ns.Length; i++)
                {
                    list[i] = new Dictionary<String, Object>
                    {
                        ["Label"] = ns[i],
                        ["Value"] = vs.GetValue(i),
                    };
                }
                source.Add(code, list);
            }
        }

        return Json(0, null, source);
    }

    ///// <summary>
    ///// 保存字典参数到后台
    ///// </summary>
    ///// <param name="para">The para.</param>
    ///// <returns></returns>
    ///// <exception cref="System.ArgumentNullException">para</exception>
    //[HttpPost]
    //public ActionResult SaveParameter(Int32 userid, Parameter para)
    //{
    //    if(para == null) throw new ArgumentNullException(nameof(para));
    //    para.SaveAsync();

    //    return Json(0, "ok");
    //}

    /// <summary>
    /// 根据用户、类别及具体的名字保存字典参数到后台
    /// </summary>
    /// <param name="userid">The user identifier.</param>
    /// <param name="category">The category.</param>
    /// <param name="name">The name.</param>
    /// <param name="value">The value.</param>
    /// <returns></returns>
    [HttpPost]
    public ActionResult SaveLayout(Int32 userid, String category, String name, String value)
    {
        if (!category.EqualIgnoreCase("LayoutSetting"))
            return Json(203, "非授权操作,不允许保存系统布局以外的信息");

        var para = Parameter.GetOrAdd(userid, category, name);
        para.SetItem("Value", value);
        para.Save();

        return Json(0, "ok");
    }
    #endregion

    #region 附件
    /// <summary>
    /// 访问图片附件
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    [AllowAnonymous]
    [HttpGet]
    public async Task<ActionResult> Image(String id)
    {
        if (id.IsNullOrEmpty()) return NotFound("非法附件编号");

        // 去掉仅用于装饰的后缀名
        var p = id.IndexOf('.');
        if (p > 0) id = id[..p];

        var att = Attachment.FindById(id.ToLong());
        if (att == null) return NotFound("找不到附件信息");

        // 如果附件不存在,则抓取
        var filePath = att.GetFilePath();
        if (filePath.IsNullOrEmpty() || !System.IO.File.Exists(filePath))
        {
            var url = att.Source;
            if (url.IsNullOrEmpty()) return NotFound("找不到附件文件");

            var rs = await att.Fetch(url);
            if (!rs) return NotFound("附件远程抓取失败");

            filePath = att.GetFilePath();
        }
        if (filePath.IsNullOrEmpty() || !System.IO.File.Exists(filePath)) return NotFound("附件文件不存在");

        if (!att.ContentType.IsNullOrEmpty())
            return PhysicalFile(filePath, att.ContentType, att.FileName);
        else
            return PhysicalFile(filePath, "image/png", att.FileName);
    }

    /// <summary>
    /// 访问附件
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    [AllowAnonymous]
    [HttpGet]
    public async Task<ActionResult> File(String id)
    {
        if (id.IsNullOrEmpty()) return NotFound("非法附件编号");

        // 去掉仅用于装饰的后缀名
        var p = id.IndexOf('.');
        if (p > 0) id = id[..p];

        var att = Attachment.FindById(id.ToLong());
        if (att == null) return NotFound("找不到附件信息");

        // 如果附件不存在,则抓取
        var filePath = att.GetFilePath();
        if (filePath.IsNullOrEmpty() || !System.IO.File.Exists(filePath))
        {
            var url = att.Source;
            if (url.IsNullOrEmpty()) return NotFound("找不到附件文件");

            var rs = await att.Fetch(url);
            if (!rs) return NotFound("附件远程抓取失败");

            filePath = att.GetFilePath();
        }
        if (filePath.IsNullOrEmpty() || !System.IO.File.Exists(filePath)) return NotFound("附件文件不存在");

        if (!att.ContentType.IsNullOrEmpty() && !att.ContentType.EqualIgnoreCase("application/octet-stream"))
            return PhysicalFile(filePath, att.ContentType, att.FileName);
        else
            return PhysicalFile(filePath, "application/octet-stream", att.FileName, true);
    }
    #endregion
}