应用服务和应用消费增加心跳
智能石头 authored at 2021-03-18 16:52:17
5.94 KiB
Stardust
using System;
using System.Collections.Generic;
using System.Diagnostics;
using NewLife;
using NewLife.Log;
using NewLife.Net;
using NewLife.Remoting;
using NewLife.Serialization;
using Stardust.Data;

namespace Stardust.Server
{
    [Api(null)]
    public class StarService : IApi, IActionFilter
    {
        #region 属性
        /// <summary>本地节点</summary>
        public static NetUri Local { get; set; }
        #endregion

        #region 登录
        public IApiSession Session { get; set; }

        /// <summary>
        /// 传入应用名和密钥登陆,
        /// 返回应用名和应用显示名
        /// </summary>
        /// <param name="user">应用名</param>
        /// <param name="pass"></param>
        /// <returns></returns>
        [Api(nameof(Login))]
        public Object Login(String user, String pass)
        {
            if (user.IsNullOrEmpty()) throw new ArgumentNullException(nameof(user));
            if (pass.IsNullOrEmpty()) throw new ArgumentNullException(nameof(pass));

            var ns = Session as INetSession;
            var ip = ns.Remote.Host;
            var ps = ControllerContext.Current.Parameters;

            WriteLog("[{0}]从[{1}]登录", user, ns.Remote);

            // 找应用
            var app = App.FindByName(user);
            if (app == null || app.Secret.IsNullOrEmpty())
            {
                if (app == null) app = new App();

                if (app.Id == 0)
                {
                    app.Name = user;
                    //app.Secret = pass;
                    app.CreateIP = ip;
                    app.CreateTime = DateTime.Now;
                    app.Enable = true;
                }

                var name = ps["name"] + "";
                if (!name.IsNullOrEmpty()) app.DisplayName = name;

                app.UpdateIP = ip;
                app.UpdateTime = DateTime.Now;

                app.Save();
            }

            if (!app.Enable) throw new Exception("已禁用!");

            // 核对密码
            if (!app.Secret.IsNullOrEmpty())
            {
                var pass2 = app.Secret.MD5();
                if (pass != pass2) throw new Exception("密码错误!");
            }

            // 应用上线
            CreateOnline(app, ns, ps);

            app.LastIP = ip;
            app.LastLogin = DateTime.Now;
            app.Save();

            // 记录当前用户
            Session["App"] = app;

            return new
            {
                app.Name,
                app.DisplayName,
            };
        }

        void IActionFilter.OnActionExecuting(ControllerContext filterContext)
        {
            var act = filterContext.ActionName;
            if (act == nameof(Login)) return;

            if (Session["App"] is App app)
            {
                var online = GetOnline(app, Session as INetSession);
                online.UpdateTime = DateTime.Now;
                online.SaveAsync();
            }
            else
            {
                var ns = Session as INetSession;
                throw new ApiException(401, $"{ns.Remote}未登录!不能执行{act}");
            }
        }

        void IActionFilter.OnActionExecuted(ControllerContext filterContext)
        {
            var ex = filterContext.Exception;
            if (ex != null && !filterContext.ExceptionHandled)
            {
                // 显示错误
                if (ex is ApiException)
                    XTrace.Log.Error(ex.Message);
                else
                    XTrace.WriteException(ex);
            }
        }
        #endregion

        #region 业务
        /// <summary>报告服务列表</summary>
        /// <param name="nameSpace"></param>
        /// <param name="services"></param>
        /// <returns></returns>
        [Api(nameof(Report))]
        public Boolean Report(String nameSpace, String[] services)
        {
            XTrace.WriteLine(nameSpace);
            XTrace.WriteLine(services.ToJson());
            return false;
        }
        #endregion

        #region 在线状态
        AppOnline CreateOnline(App app, INetSession ns, IDictionary<String, Object> ps)
        {
            var ip = ns.Remote.Host;

            var machine = ps["machine"] + "";
            var pid = ps["processid"].ToInt();
            var ver = ps["version"] + "";
            var compile = ps["compile"].ToDateTime();

            var online = GetOnline(app, ns);

            // 客户端特性
            online.Client = $"{(ip.IsNullOrEmpty() ? machine : ip)}@{pid}";
            online.Name = machine;
            online.Version = ver;
            online.Compile = compile;

            // 服务器特性
            pid = Process.GetCurrentProcess().Id;
            //online.Server = Local.EndPoint + "@" + pid;
            online.Save();

            // 真正的用户
            Session["AppOnline"] = online;

            // 下线
            ns.OnDisposed += (s, e) => online.Delete();

            //// 版本和编译时间
            //if (app.Version.IsNullOrEmpty() || app.Version.CompareTo(ver) < 0) app.Version = ver;
            //if (app.Compile.Year < 2000 || app.Compile < compile) app.Compile = compile;

            return online;
        }

        AppOnline GetOnline(App app, INetSession ns)
        {
            if (Session["AppOnline"] is AppOnline online) return online;

            var ip = ns.Remote.Host;
            var ins = ns.Remote.EndPoint + "";
            online = AppOnline.FindByClient(ins) ?? new AppOnline { CreateIP = ip };
            online.AppId = app.Id;
            online.Client = ins;

            return online;
        }
        #endregion

        #region 日志
        /// <summary>日志</summary>
        public static ILog Log { get; set; }

        /// <summary>写日志</summary>
        /// <param name="format"></param>
        /// <param name="args"></param>
        public static void WriteLog(String format, params Object[] args) => Log?.Info(format, args);
        #endregion
    }
}