节点在线、应用在线、配置在线使用令牌查询
大石头 authored at 2021-12-16 19:49:30
14.29 KiB
Stardust
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Web.Script.Serialization;
using System.Xml.Serialization;
using NewLife;
using NewLife.Data;
using NewLife.Log;
using NewLife.Remoting.Models;
using NewLife.Serialization;
using Stardust.Models;
using XCode;
using XCode.Membership;

namespace Stardust.Data.Nodes;

/// <summary>节点在线</summary>
public partial class NodeOnline : Entity<NodeOnline>, IOnlineModel2
{
    #region 对象操作
    static NodeOnline()
    {
        var df = Meta.Factory.AdditionalFields;
        df.Add(__.PingCount);

        Meta.Interceptors.Add<TimeInterceptor>();
        Meta.Interceptors.Add<IPInterceptor>();
        Meta.Interceptors.Add<TraceInterceptor>();

        var sc = Meta.SingleCache;
        sc.FindSlaveKeyMethod = k => Find(_.SessionID == k);
        sc.GetSlaveKeyMethod = e => e.SessionID;
    }

    /// <summary>校验数据</summary>
    /// <param name="isNew"></param>
    public override void Valid(Boolean isNew)
    {
        // 截取部分进程字段,避免过长无法保存
        this.TrimExtraLong(__.Processes, __.MACs, __.DriveInfo);

        if (!Dirtys[nameof(MemoryUsed)] && Node != null) MemoryUsed = Node.Memory - (FreeMemory > 0 ? FreeMemory : AvailableMemory);
        if (!Dirtys[nameof(SpaceUsed)] && Node != null) SpaceUsed = Node.TotalSize - AvailableFreeSpace;

        base.Valid(isNew);
    }
    #endregion

    #region 扩展属性
    /// <summary>节点</summary>
    [XmlIgnore, ScriptIgnore]
    public Node Node => Extends.Get(nameof(Node), k => Node.FindByID(NodeID));

    /// <summary>节点</summary>
    [Map(__.NodeID)]
    public String NodeName => Node + "";

    /// <summary>省份</summary>
    [XmlIgnore, IgnoreDataMember]
    public Area Province => Extends.Get(nameof(Province), k => Area.FindByID(ProvinceID));

    /// <summary>省份名</summary>
    [Map(__.ProvinceID)]
    public String ProvinceName => Province + "";

    /// <summary>城市</summary>
    [XmlIgnore, IgnoreDataMember]
    public Area City => Extends.Get(nameof(City), k => Area.FindByID(CityID));

    /// <summary>城市名</summary>
    [Map(__.CityID)]
    public String CityName => City?.Path ?? Province?.Path;

    String IOnlineModel.SessionId { get => SessionID; set => SessionID = value; }
    #endregion

    #region 扩展查询
    /// <summary>根据id查找</summary>
    /// <param name="id">会话</param>
    /// <returns></returns>
    public static NodeOnline FindById(Int32 id) => Find(__.ID, id);

    /// <summary>根据会话查找</summary>
    /// <param name="nodeId">会话</param>
    /// <returns></returns>
    public static NodeOnline FindByNodeId(Int32 nodeId) => Find(__.NodeID, nodeId);

    /// <summary>根据会话查找</summary>
    /// <param name="sessionId">会话</param>
    /// <param name="cache">是否走缓存</param>
    /// <returns></returns>
    public static NodeOnline FindBySessionId(String sessionId, Boolean cache)
    {
        if (sessionId.IsNullOrEmpty()) return null;

        if (!cache) return Find(_.SessionID == sessionId);

        return Meta.SingleCache.GetItemWithSlaveKey(sessionId) as NodeOnline;
    }

    /// <summary>根据节点查找所有在线记录</summary>
    /// <param name="nodeId"></param>
    /// <returns></returns>
    public static IList<NodeOnline> FindAllByNodeId(Int32 nodeId) => FindAll(_.NodeID == nodeId);

    /// <summary>根据令牌查找</summary>
    /// <param name="token">令牌</param>
    /// <returns>实体列表</returns>
    public static IList<NodeOnline> FindAllByToken(String token)
    {
        if (token.IsNullOrEmpty()) return new List<NodeOnline>();

        // 实体缓存
        if (Meta.Session.Count < 1000) return Meta.Cache.FindAll(e => e.Token.EqualIgnoreCase(token));

        return FindAll(_.Token == token);
    }

    /// <summary>根据省份、城市查找</summary>
    /// <param name="provinceId">省份</param>
    /// <param name="cityId">城市</param>
    /// <returns>实体列表</returns>
    public static IList<NodeOnline> FindAllByProvinceIDAndCityID(Int32 provinceId, Int32 cityId)
    {
        // 实体缓存
        if (Meta.Session.Count < 1000) return Meta.Cache.FindAll(e => e.ProvinceID == provinceId && e.CityID == cityId);

        return FindAll(_.ProvinceID == provinceId & _.CityID == cityId);
    }

    /// <summary>根据项目查找</summary>
    /// <param name="projectId">项目</param>
    /// <returns>实体列表</returns>
    public static IList<NodeOnline> FindAllByProjectId(Int32 projectId)
    {
        if (projectId <= 0) return new List<NodeOnline>();

        // 实体缓存
        if (Meta.Session.Count < 1000) return Meta.Cache.FindAll(e => e.ProjectId == projectId);

        return FindAll(_.ProjectId == projectId);
    }
    #endregion

    #region 高级查询
    /// <summary>查询满足条件的记录集,分页、排序</summary>
    /// <param name="nodeId">节点</param>
    /// <param name="provinceId">省份</param>
    /// <param name="cityId">城市</param>
    /// <param name="category">类别</param>
    /// <param name="start">开始时间</param>
    /// <param name="end">结束时间</param>
    /// <param name="key">关键字</param>
    /// <param name="page">分页排序参数,同时返回满足条件的总记录数</param>
    /// <returns>实体集</returns>
    public static IList<NodeOnline> Search(Int32 projectId, Int32 nodeId, Int32 provinceId, Int32 cityId, String category, DateTime start, DateTime end, String key, PageParameter page)
    {
        var exp = new WhereExpression();

        if (projectId >= 0) exp &= _.ProjectId == projectId;
        if (nodeId >= 0) exp &= _.NodeID == nodeId;
        if (provinceId >= 0) exp &= _.ProvinceID == provinceId;
        if (cityId >= 0) exp &= _.CityID == cityId;
        if (!category.IsNullOrEmpty()) exp &= _.Category == category;

        exp &= _.CreateTime.Between(start, end);

        if (!key.IsNullOrEmpty()) exp &= _.Name.Contains(key) | _.Data.Contains(key) | _.SessionID.Contains(key);

        return FindAll(exp, page);
    }

    /// <summary>根据产品,分组统计在线数</summary>
    /// <returns></returns>
    public static IDictionary<Int32, Int32> SearchGroupByProvince()
    {
        var list = FindAll(_.ProvinceID.GroupBy(), null, _.ID.Count() & _.ProvinceID);
        return list.ToDictionary(e => e.ProvinceID, e => e.ID);
    }
    #endregion

    #region 业务操作
    /// <summary>根据编码查询或添加</summary>
    /// <param name="sessionId"></param>
    /// <returns></returns>
    public static NodeOnline GetOrAdd(String sessionId) => GetOrAdd(sessionId, FindBySessionId, k => new NodeOnline { SessionID = k });

    /// <summary>删除过期,指定过期时间</summary>
    /// <param name="expire">超时时间,秒</param>
    /// <returns></returns>
    public static IList<NodeOnline> ClearExpire(TimeSpan expire)
    {
        if (Meta.Count == 0) return null;

        // 10分钟不活跃将会被删除
        var exp = _.UpdateTime < DateTime.Now.Subtract(expire);
        var list = FindAll(exp, null, null, 0, 0);
        list.Delete();

        return list;
    }

    ///// <summary>更新并保存在线状态</summary>
    ///// <param name="di"></param>
    ///// <param name="pi"></param>
    ///// <param name="token"></param>
    ///// <param name="ip"></param>
    //public void Save(NodeInfo di, PingInfo pi, String token, String ip)
    //{
    //    var olt = this;

    //    if (di != null)
    //    {
    //        olt.Fill(di);
    //        olt.LocalTime = di.Time.ToLocalTime();
    //        olt.MACs = di.Macs;
    //        //olt.COMs = di.COMs;
    //    }
    //    else
    //    {
    //        olt.Fill(pi);
    //        olt.CreateData(pi, ip);
    //    }

    //    olt.Token = token;
    //    olt.PingCount++;
    //    olt.UpdateIP = ip;
    //    olt.TraceId = DefaultSpan.Current?.TraceId;

    //    // 5秒内直接保存
    //    if (olt.CreateTime.AddSeconds(5) > DateTime.Now)
    //        olt.Save();
    //    else
    //        olt.SaveAsync();
    //}

    /// <summary>填充节点信息</summary>
    /// <param name="inf"></param>
    public void Fill(NodeInfo inf)
    {
        var online = this;

        online.LocalTime = inf.Time.ToLocalTime();
        online.MACs = inf.Macs;
        //online.COMs = di.COMs;
        online.IP = inf.IP;
        online.Gateway = inf.Gateway;
        online.Dns = inf.Dns;

        if (inf.AvailableMemory > 0) online.AvailableMemory = (Int32)(inf.AvailableMemory / 1024 / 1024);
        if (inf.FreeMemory > 0) online.FreeMemory = (Int32)(inf.FreeMemory / 1024 / 1024);
        MemoryUsed = online.Memory - (online.FreeMemory > 0 ? online.FreeMemory : online.AvailableMemory);
        if (inf.AvailableFreeSpace > 0) online.AvailableFreeSpace = (Int32)(inf.AvailableFreeSpace / 1024 / 1024);
        if (!online.DriveInfo.IsNullOrEmpty()) online.DriveInfo = online.DriveInfo;
    }

    /// <summary>填充在线节点信息</summary>
    /// <param name="inf"></param>
    private void Fill(PingInfo inf)
    {
        var online = this;

        if (inf.AvailableMemory > 0) online.AvailableMemory = (Int32)(inf.AvailableMemory / 1024 / 1024);
        if (inf.FreeMemory > 0) online.FreeMemory = (Int32)(inf.FreeMemory / 1024 / 1024);
        MemoryUsed = online.Memory - (online.FreeMemory > 0 ? online.FreeMemory : online.AvailableMemory);
        if (inf.AvailableFreeSpace > 0) online.AvailableFreeSpace = (Int32)(inf.AvailableFreeSpace / 1024 / 1024);
        if (!inf.DriveInfo.IsNullOrEmpty()) online.DriveInfo = inf.DriveInfo;
        if (inf.CpuRate > 0) online.CpuRate = inf.CpuRate;
        if (inf.Temperature > 0) online.Temperature = inf.Temperature;
        if (inf.Battery > 0) online.Battery = inf.Battery;
        /*if (inf.Signal > 0)*/
        online.Signal = inf.Signal;
        if (inf.UplinkSpeed > 0) online.UplinkSpeed = (Int64)inf.UplinkSpeed;
        if (inf.DownlinkSpeed > 0) online.DownlinkSpeed = (Int64)inf.DownlinkSpeed;
        if (inf.SystemLoad > 0) online.SystemLoad = inf.SystemLoad;
        if (inf.DiskIOPS > 0) online.DiskIOPS = inf.DiskIOPS;
        if (inf.DiskActiveTime > 0) online.DiskActiveTime = inf.DiskActiveTime;
        if (inf.ProcessCount > 0) online.ProcessCount = inf.ProcessCount;
        if (inf.TcpConnections > 0) online.TcpConnections = inf.TcpConnections;
        if (inf.TcpTimeWait > 0) online.TcpTimeWait = inf.TcpTimeWait;
        if (inf.TcpCloseWait > 0) online.TcpCloseWait = inf.TcpCloseWait;
        if (inf.IntranetScore > 0) online.IntranetScore = inf.IntranetScore;
        if (inf.InternetScore > 0) online.InternetScore = inf.InternetScore;
        if (inf.GatewayLatency > 0) online.GatewayLatency = inf.GatewayLatency;
        if (inf.GatewayLossRate > 0) online.GatewayLossRate = inf.GatewayLossRate;
        if (inf.DnsLatency > 0) online.DnsLatency = inf.DnsLatency;
        if (inf.DnsLossRate > 0) online.DnsLossRate = inf.DnsLossRate;
        if (inf.ServerLatency > 0) online.ServerLatency = inf.ServerLatency;
        if (inf.ServerLossRate > 0) online.ServerLossRate = inf.ServerLossRate;
        if (inf.Uptime > 0) online.Uptime = inf.Uptime;
        if (inf.Delay > 0) online.Delay = inf.Delay;

        var dt = inf.Time.ToDateTime().ToLocalTime();
        if (dt.Year > 2000)
        {
            online.LocalTime = dt;
            //olt.Offset = (Int32)Math.Round((dt - DateTime.Now).TotalSeconds);
            online.Offset = (Int32)(inf.Time + (Delay / 2) - DateTime.UtcNow.ToLong());
        }

        if (!inf.Processes.IsNullOrEmpty()) online.Processes = inf.Processes;
        if (!inf.Macs.IsNullOrEmpty()) online.MACs = inf.Macs;
        //if (!inf.COMs.IsNullOrEmpty()) olt.COMs = inf.COMs;
        if (!inf.IP.IsNullOrEmpty()) online.IP = inf.IP;
        if (!inf.Gateway.IsNullOrEmpty()) online.Gateway = inf.Gateway;

        //olt.Data = inf.ToJson();
        var dic = inf.ToDictionary();
        dic.Remove("Processes");
        online.Data = dic.ToJson();
    }

    private void CreateData(PingInfo inf, String ip)
    {
        var olt = this;

        var dt = inf.Time.ToDateTime().ToLocalTime();

        // 插入节点数据
        var data = new NodeData
        {
            NodeID = olt.NodeID,
            Name = olt.Name,
            AvailableMemory = olt.AvailableMemory,
            FreeMemory = olt.FreeMemory,
            AvailableFreeSpace = olt.AvailableFreeSpace,
            CpuRate = inf.CpuRate,
            SystemLoad = inf.SystemLoad,
            Temperature = inf.Temperature,
            Battery = inf.Battery,
            Signal = inf.Signal,
            UplinkSpeed = (Int64)inf.UplinkSpeed,
            DownlinkSpeed = (Int64)inf.DownlinkSpeed,
            DiskIOPS = inf.DiskIOPS,
            DiskActiveTime = inf.DiskActiveTime,
            ProcessCount = inf.ProcessCount,
            TcpConnections = inf.TcpConnections,
            TcpTimeWait = inf.TcpTimeWait,
            TcpCloseWait = inf.TcpCloseWait,
            IntranetScore = inf.IntranetScore,
            InternetScore = inf.InternetScore,
            GatewayLatency = inf.GatewayLatency,
            GatewayLossRate = inf.GatewayLossRate,
            DnsLatency = inf.DnsLatency,
            DnsLossRate = inf.DnsLossRate,
            ServerLatency = inf.ServerLatency,
            ServerLossRate = inf.ServerLossRate,
            Uptime = inf.Uptime,
            Delay = inf.Delay,
            LocalTime = dt,
            Offset = olt.Offset,
            TraceId = DefaultSpan.Current?.TraceId,
            CreateIP = ip,
            Creator = Environment.MachineName,
        };

        //data.SaveAsync();
        data.Insert();
    }

    public Int32 Save(IPingRequest request, Object context)
    {
        if (context is DeviceContext ctx)
        {
            Token = ctx.Token;
            UpdateIP = ctx.UserHost;

            if (ctx.Device is Node node)
            {
                Name = node.Name;
                Category = node.Category;
                Version = node.Version;
                CompileTime = node.CompileTime;
                OSKind = node.OSKind;
                //Save(null, inf, context.Token, ip);
            }
            if (request is PingInfo ping)
            {
                Fill(ping);
                CreateData(ping, ctx.UserHost);
            }
        }

        PingCount++;

        //return SaveAsync() ? 1 : 0;
        return Save();
    }
    #endregion
}