鼎鼎知识库
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

2021.2.3极光推送原理.md 14KB

引用包

<PackageReference Include="Flurl" Version="2.8.2" />
<PackageReference Include="Flurl.Http" Version="2.4.2" />

触发时间

  • 触发原理:当用户在手机上点击了某个按钮,我们需要把延迟的时间记录到Quartz.Net数据表里。
  • QuartzNet运作原理:创建一个IJob作业,把设置保存到QuartzNet专有的表。当触发的时候,QuartzNet会调用我们定义好的接口,发送极光推送以及把一条通知记录保存到数据库的Noti表。

首先有关配置。

namespace DD.Libs.TasksManager
{
    /// <summary>
    /// 极光配置
    /// </summary>
    public class JobSetting : IOptions<JobSetting>
    {
        public JobSetting Value => this;

        /// <summary>
        /// 执行场景任务的url
        /// http://47.103.61.198:5002/api/tasks/cj/
        /// </summary>
        public string sceneUrl { get; set; }

        /// <summary>
        /// 定时任务的url
        /// http://47.103.61.198:5002/api/tasks/ds/ 
        public string timingUrl { get; set; }

        /// <summary>
        /// 连接字符串的一部分
        /// Port=3306;Database=quartznet;Uid=root;Pwd=pass;SslMode=none
        /// </summary>
        public string quartz { get; set; }

        /// <summary>
        /// 连接字符串的一部分
        /// 192.168.9.108
        /// </summary>
        public string server { get; set; }
    }
}

appSetting.json中配置

  "Tasks": { //场景和定时
    "sceneUrl": "http://47.103.61.198:5005/api/tasks/noti/", //写字楼版QuartzNet触发发生时调用的接口
    "timingUrl": "", //定时地址
    "server": "47.103.61.198",
    "quartz": "Port=3306;Database=ddquartz1;Uid=root;Pwd=TecheDing2019;SslMode=none"
  },

接口实现:

namespace DD.Warning.WebUI.Controllers
{
    /// <summary>
    /// 场景和定时在这里执行
    /// </summary>
    [AllowAnonymous]
    [Produces("application/json")]
    [Route("api/tasks")]
    public class TaiHeRealController : Controller
    {

        private readonly IMediator _mediator; //中介者
        private readonly ILogger<TaiHeRealController> _logger;

        public TaiHeRealController(IMediator mediator, ILogger<TaiHeRealController> logger)
        {
            _mediator = mediator;
            _logger = logger;
        }

        /// <summary>
        /// 场景执行
        /// </summary>
        /// <param name="extra"></param>
        /// <returns></returns>
        [HttpGet("cj/{extra}")]
        public async Task<IActionResult> cj(string extra)
        {
            //rlq_nuode,SceneId,SceneScheduleId
            string[] tempArr = extra.Split(',');

            var innerCommand = new AutoTriggerSceneCommand
            {
                SceneId = tempArr[1],
                SceneScheudleId = tempArr[2],
                ConnKey=tempArr[0]
            };

            _logger.LogWarning($"正在触发场景:{extra}{WarningConstants.LogPlaceholder}", WarningConstants.LogTest);

            await _mediator.Send(innerCommand);
            return Ok();
        }

    }
}

定义一个IJob

namespace DD.Libs.TasksManager
{
    public class SceneJob : IJob
    {
        private readonly IOptions<JobSetting> _settings;

        public SceneJob(IOptions<JobSetting> settings)
        {
            _settings = settings;
        }
        public async Task Execute(IJobExecutionContext context)
        {
            //获取job上下文中传来的额外参数
            string extra = context.JobDetail.JobDataMap.GetString("extra");

            using(var client = new HttpClient())
            {
                client.Timeout = TimeSpan.FromSeconds(1200);
                //"http://47.103.61.198:5002/api/tasks/cj/
                string url = $"{_settings.Value.sceneUrl }{extra}";
                await client.GetAsync(url);
            }
        }
    }
}

Startup中注册:

#region 场景定时任务,Quartz
services.Configure<JobSetting>(Configuration.GetSection("Tasks"));
services.AddTransient<SceneJob>();


NameValueCollection props = new NameValueCollection {
        { "quartz.threadPool.threadCount","100"},
        { "quartz.jobStore.type","Quartz.Impl.AdoJobStore.JobStoreTX, Quartz"},
        { "quartz.jobStore.driverDelegateType","Quartz.Impl.AdoJobStore.StdAdoDelegate, Quartz"},
        { "quartz.jobStore.dataSource","myDS"},
        { "quartz.dataSource.myDS.connectionString",$"Server={Configuration["Tasks:server"]};{Configuration["Tasks:quartz"]}"},
        //{ "quartz.dataSource.myDS.connectionString",$"server={Configuration["App:redis"]};port=3306;database=quartznet;user=root;password=lcDb_!@34%^_Mantunsci;SslMode=none"},
        { "quartz.dataSource.myDS.provider","MySql"},
        { "quartz.serializer.type","json"}
        //{ "quartz.jobStore.useProperties","true"}
    };

//计划工厂
var schedulerFactory = new StdSchedulerFactory(props);
services.AddSingleton<ISchedulerFactory>(schedulerFactory); //计划工厂

//job工厂
services.AddSingleton<IJobFactory, SimpleInjectorJobFactory>();//Job工厂

//帮助类
services.AddSingleton<DDJobScheduler>((ctx) => {

    var tempSchedulerFactory = ctx.GetRequiredService<ISchedulerFactory>();
    var tempJobFactory = ctx.GetRequiredService<IJobFactory>();

    var tempScheduler = tempSchedulerFactory.GetScheduler().Result;
    tempScheduler.JobFactory = tempJobFactory;
    var tempJobScheduler = new DDJobScheduler(tempScheduler);
    return tempJobScheduler;

});
#endregion

在请求管道中注册:

#region 场景定时任务
var scheduler = serviceProvider.GetService<DDJobScheduler>();
scheduler.Init();
#endregion

如何使用呢?

public class AddSceneSchedulesCommandHandler : IRequestHandler<AddSceneSchedulesCommand, ResponseWrapperBase>
{

    private readonly DDJobScheduler _scheduler;

    public AddSceneSchedulesCommandHandler(DDJobScheduler scheduler)
    {
        _scheduler = scheduler;
    }
    public async Task<ResponseWrapperBase> Handle(AddSceneSchedulesCommand request, CancellationToken cancellationToken)
    {
        await _scheduler.DeleteJob(delJoinName, delGroupName);
        string cron = JobUtil.GetCronByTime(newSceneSchedule.Hour, newSceneSchedule.Minute, newSceneSchedule.Second, (TimingWeekEnum)newSceneSchedule.Week);
        await _scheduler.AddSceneJob(joinName, groupName, cron, joinName);

        return await Task.FromResult(result);
    }

调用极光

  • 确定别名
  • 往数据库的Noti存入一条记录 ``` 对应的领域模型是:

///

/// 通知 /// public class Noti : Entity, IAggregateRoot {

/// <summary>
/// 通知标题
/// </summary>
public string Title { get; private set; }
/// <summary>
/// 通知内容
/// </summary>
public string Content { get; private set; }
/// <summary>
/// 是否已读
/// </summary>
public string IsRead { get;private set; }

/// <summary>
/// 通知类型
/// 报警通知的Type=0
/// 延迟断电通知的Type=alert
/// </summary>
public string Type { get; private set; }

/// <summary>
/// 报警通知用的报警级别描述
/// </summary>
public string Level { get; private set; }

/// <summary>
/// 传递的主键
/// 用来点击某个通知跳转到某个地方
/// </summary>
public string ExtraId { get; private set; }

/// <summary>
/// 创建方式1
/// </summary>
/// <param name="title"></param>
/// <param name="content"></param>
public Noti(string title, string content)
{
    if (string.IsNullOrEmpty(title)) throw new ArgumentNullException("title cannot be null");
    if (string.IsNullOrEmpty(content)) throw new ArgumentNullException("content cannot be null");

    Title = title;
    Content = content;

    CreateTime = DateTime.Now.ToFullTimeStr();
    Id = Guid.NewGuid();
    LastUpdateTime = DateTime.Now.ToFullTimeStr();
    IsLogicDel = CommonConstants.LogicDelNo;
}

/// <summary>
/// 创建方式2
/// </summary>
/// <param name="title"></param>
/// <param name="content"></param>
/// <param name="type"></param>
/// <param name="extraId"></param>
public Noti(string title, string content, string type, string extraId)
{
    if (string.IsNullOrEmpty(title)) throw new ArgumentNullException("title cannot be null");
    if (string.IsNullOrEmpty(content)) throw new ArgumentNullException("content cannot be null");
    if (string.IsNullOrEmpty(extraId)) throw new ArgumentNullException("extraId cannot be null");

    Title = title;
    Content = content;
    Type = type;
    ExtraId = extraId;

    CreateTime = DateTime.Now.ToFullTimeStr();
    Id = Guid.NewGuid();
    LastUpdateTime = DateTime.Now.ToFullTimeStr();
    IsLogicDel = CommonConstants.LogicDelNo;
}

/// <summary>
/// 设置报警级别
/// </summary>
/// <param name="level"></param>
public void SetLevel(string level)
{
    Level = level;
}

/// <summary>
/// 标记已读
/// </summary>
/// <param name="regId"></param>
public void MarkRead(string regId)
{
    if (string.IsNullOrEmpty(regId)) throw new ArgumentNullException("title cannot be null");

    if(string.IsNullOrEmpty(IsRead))
    {
        IsRead = regId;
    }
    else
    {
        var currentRegIds = regId.Split(',').ToList();
        currentRegIds.Add(regId);
        IsRead = string.Join(',', currentRegIds);
    }
}

}

//对应的仓储 private readonly INotiRepository _notiRepo;

public SomeRequestHandler(INotiRepository notiRepo) {

_notiRepo = notiRepo;

}

//大致用法 var newNoti = new Noti(“title”,“content”, “alert”, string.Empty); _notiRepo.Add(newNoti); await _notiRepo.UnitOfWork.SaveChangesAsync(cancellationToken);

- 极光推送

var request = new JiGuanPushRequest

{
    platform = "all",
    //audience = "all",//针对所有
    audience = new Audience
    {
        alias = tempAlias
    },
    notification = new Notification
    {
        alert = $"{locationDesc},{kgDesc},{warningDesc}",
        android = new Android
        {
            extras = new Extras
            {
                android_key1 = string.Empty,
                kaiGuanId = cloudKaiGuan.Id.ToString(),
                connKey = cloudKaiGuan.ConnKey,
                startTime = startEndTime,
                warningType = ((short)type).ToString(),
                projectId = _appOptions.Value.projectid,
                notiType = "0", //通知消息类型
                notiId = notis.Id.ToString()
            }
        },
        ios = new Ios
        {
            sound = "sound.caf",
            badge = "+1",
            extras = new Extras2
            {
                ios_key1 = string.Empty,
                kaiGuanId = cloudKaiGuan.Id.ToString(),
                connKey = cloudKaiGuan.ConnKey,
                startTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
                warningType = ((short)type).ToString(),
                projectId = _appOptions.Value.projectid,
                notiType = "0", //通知消息类型
                notiId = notis.Id.ToString()
            }
        }
    },
    options = new JiGuangs.Options
    {
        apns_production = true
    }
};

var response = await _appOptions.Value.jiguang
    .WithHeader("Content-Type", "application/json")
    .WithHeader("Authorization", "Basic M2ZhOTE0NTUzNmEwMDFmY2UwYjg3NjU5OjIxYjE0ZGM0OGZkZjRmMDk4NzZiMTg0MQ==")
    .PostJsonAsync(request);

//相关Dto

public class JiGuanPushRequest
{
    [JsonProperty("platform")]
    public string platform { get; set; }

    //[JsonProperty("audience")]
    //public string audience { get; set; }

    [JsonProperty("audience")]
    public Audience audience { get; set; }

    [JsonProperty("notification")]
    public Notification notification { get; set; }

    [JsonProperty("options")]
    public Options options { get; set; }
}

public class Audience
{
    [JsonProperty("alias")]
    public List<string> alias { get; set; } = new List<string>();
}

public class Android
{
    [JsonProperty("extras")]
    public Extras extras { get; set; }
}

public class Extras
{
    [JsonProperty("android-key1")]
    public string android_key1 { get; set; }

    [JsonProperty("kaiGuanId")]
    public string kaiGuanId { get; set; }

    [JsonProperty("warningType")]
    public string warningType { get; set; }

    [JsonProperty("startTime")]
    public string startTime { get; set; }

    [JsonProperty("connKey")]
    public string connKey { get; set; }

    [JsonProperty("notiType")]
    public string notiType { get; set; }

    [JsonProperty("projectId")]
    public string projectId { get; set; }

    [JsonProperty("notiId")]
    public string notiId { get; set; }
}

public class Extras2
{
    [JsonProperty("ios-key1")]
    public string ios_key1 { get; set; }
    [JsonProperty("kaiGuanId")]
    public string kaiGuanId { get; set; }

    [JsonProperty("warningType")]
    public string warningType { get; set; }

    [JsonProperty("startTime")]
    public string startTime { get; set; }

    [JsonProperty("connKey")]
    public string connKey { get; set; }

    [JsonProperty("notiType")]
    public string notiType { get; set; }

    [JsonProperty("projectId")]
    public string projectId { get; set; }

    [JsonProperty("notiId")]
    public string notiId { get; set; }
}

public class Ios
{
    [JsonProperty("sound")]
    public string sound { get; set; }

    [JsonProperty("badge")]
    public string badge { get; set; }

    [JsonProperty("extras")]
    public Extras2 extras { get; set; }
}

public class Notification
{
    [JsonProperty("alert")]
    public string alert { get; set; }

    [JsonProperty("android")]
    public Android android { get; set; }

    [JsonProperty("ios")]
    public Ios ios { get; set; }
}

public class Options
{
    [JsonProperty("apns_production")]
    public bool apns_production { get; set; }
}


public class TempRegIdsAndAlias
{
    public string RegId { get; set; }
    public string Alias { get; set; }
}

```