鼎鼎知识库
Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

2021.2.3极光推送原理.md 14KB

3 роки тому
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519
  1. # 引用包
  2. ```
  3. <PackageReference Include="Flurl" Version="2.8.2" />
  4. <PackageReference Include="Flurl.Http" Version="2.4.2" />
  5. ```
  6. # 触发时间
  7. - 触发原理:当用户在手机上点击了某个按钮,我们需要把延迟的时间记录到Quartz.Net数据表里。
  8. - QuartzNet运作原理:创建一个`IJob`作业,把设置保存到QuartzNet专有的表。当触发的时候,QuartzNet会调用我们定义好的接口,发送极光推送以及把一条通知记录保存到数据库的Noti表。
  9. 首先有关配置。
  10. ```
  11. namespace DD.Libs.TasksManager
  12. {
  13. /// <summary>
  14. /// 极光配置
  15. /// </summary>
  16. public class JobSetting : IOptions<JobSetting>
  17. {
  18. public JobSetting Value => this;
  19. /// <summary>
  20. /// 执行场景任务的url
  21. /// http://47.103.61.198:5002/api/tasks/cj/
  22. /// </summary>
  23. public string sceneUrl { get; set; }
  24. /// <summary>
  25. /// 定时任务的url
  26. /// http://47.103.61.198:5002/api/tasks/ds/
  27. public string timingUrl { get; set; }
  28. /// <summary>
  29. /// 连接字符串的一部分
  30. /// Port=3306;Database=quartznet;Uid=root;Pwd=pass;SslMode=none
  31. /// </summary>
  32. public string quartz { get; set; }
  33. /// <summary>
  34. /// 连接字符串的一部分
  35. /// 192.168.9.108
  36. /// </summary>
  37. public string server { get; set; }
  38. }
  39. }
  40. ```
  41. 在`appSetting.json`中配置
  42. ```
  43. "Tasks": { //场景和定时
  44. "sceneUrl": "http://47.103.61.198:5005/api/tasks/noti/", //写字楼版QuartzNet触发发生时调用的接口
  45. "timingUrl": "", //定时地址
  46. "server": "47.103.61.198",
  47. "quartz": "Port=3306;Database=ddquartz1;Uid=root;Pwd=TecheDing2019;SslMode=none"
  48. },
  49. ```
  50. 接口实现:
  51. ```
  52. namespace DD.Warning.WebUI.Controllers
  53. {
  54. /// <summary>
  55. /// 场景和定时在这里执行
  56. /// </summary>
  57. [AllowAnonymous]
  58. [Produces("application/json")]
  59. [Route("api/tasks")]
  60. public class TaiHeRealController : Controller
  61. {
  62. private readonly IMediator _mediator; //中介者
  63. private readonly ILogger<TaiHeRealController> _logger;
  64. public TaiHeRealController(IMediator mediator, ILogger<TaiHeRealController> logger)
  65. {
  66. _mediator = mediator;
  67. _logger = logger;
  68. }
  69. /// <summary>
  70. /// 场景执行
  71. /// </summary>
  72. /// <param name="extra"></param>
  73. /// <returns></returns>
  74. [HttpGet("cj/{extra}")]
  75. public async Task<IActionResult> cj(string extra)
  76. {
  77. //rlq_nuode,SceneId,SceneScheduleId
  78. string[] tempArr = extra.Split(',');
  79. var innerCommand = new AutoTriggerSceneCommand
  80. {
  81. SceneId = tempArr[1],
  82. SceneScheudleId = tempArr[2],
  83. ConnKey=tempArr[0]
  84. };
  85. _logger.LogWarning($"正在触发场景:{extra}{WarningConstants.LogPlaceholder}", WarningConstants.LogTest);
  86. await _mediator.Send(innerCommand);
  87. return Ok();
  88. }
  89. }
  90. }
  91. ```
  92. 定义一个`IJob`
  93. ```
  94. namespace DD.Libs.TasksManager
  95. {
  96. public class SceneJob : IJob
  97. {
  98. private readonly IOptions<JobSetting> _settings;
  99. public SceneJob(IOptions<JobSetting> settings)
  100. {
  101. _settings = settings;
  102. }
  103. public async Task Execute(IJobExecutionContext context)
  104. {
  105. //获取job上下文中传来的额外参数
  106. string extra = context.JobDetail.JobDataMap.GetString("extra");
  107. using(var client = new HttpClient())
  108. {
  109. client.Timeout = TimeSpan.FromSeconds(1200);
  110. //"http://47.103.61.198:5002/api/tasks/cj/
  111. string url = $"{_settings.Value.sceneUrl }{extra}";
  112. await client.GetAsync(url);
  113. }
  114. }
  115. }
  116. }
  117. ```
  118. 在`Startup`中注册:
  119. ```
  120. #region 场景定时任务,Quartz
  121. services.Configure<JobSetting>(Configuration.GetSection("Tasks"));
  122. services.AddTransient<SceneJob>();
  123. NameValueCollection props = new NameValueCollection {
  124. { "quartz.threadPool.threadCount","100"},
  125. { "quartz.jobStore.type","Quartz.Impl.AdoJobStore.JobStoreTX, Quartz"},
  126. { "quartz.jobStore.driverDelegateType","Quartz.Impl.AdoJobStore.StdAdoDelegate, Quartz"},
  127. { "quartz.jobStore.dataSource","myDS"},
  128. { "quartz.dataSource.myDS.connectionString",$"Server={Configuration["Tasks:server"]};{Configuration["Tasks:quartz"]}"},
  129. //{ "quartz.dataSource.myDS.connectionString",$"server={Configuration["App:redis"]};port=3306;database=quartznet;user=root;password=lcDb_!@34%^_Mantunsci;SslMode=none"},
  130. { "quartz.dataSource.myDS.provider","MySql"},
  131. { "quartz.serializer.type","json"}
  132. //{ "quartz.jobStore.useProperties","true"}
  133. };
  134. //计划工厂
  135. var schedulerFactory = new StdSchedulerFactory(props);
  136. services.AddSingleton<ISchedulerFactory>(schedulerFactory); //计划工厂
  137. //job工厂
  138. services.AddSingleton<IJobFactory, SimpleInjectorJobFactory>();//Job工厂
  139. //帮助类
  140. services.AddSingleton<DDJobScheduler>((ctx) => {
  141. var tempSchedulerFactory = ctx.GetRequiredService<ISchedulerFactory>();
  142. var tempJobFactory = ctx.GetRequiredService<IJobFactory>();
  143. var tempScheduler = tempSchedulerFactory.GetScheduler().Result;
  144. tempScheduler.JobFactory = tempJobFactory;
  145. var tempJobScheduler = new DDJobScheduler(tempScheduler);
  146. return tempJobScheduler;
  147. });
  148. #endregion
  149. ```
  150. 在请求管道中注册:
  151. ```
  152. #region 场景定时任务
  153. var scheduler = serviceProvider.GetService<DDJobScheduler>();
  154. scheduler.Init();
  155. #endregion
  156. ```
  157. 如何使用呢?
  158. ```
  159. public class AddSceneSchedulesCommandHandler : IRequestHandler<AddSceneSchedulesCommand, ResponseWrapperBase>
  160. {
  161. private readonly DDJobScheduler _scheduler;
  162. public AddSceneSchedulesCommandHandler(DDJobScheduler scheduler)
  163. {
  164. _scheduler = scheduler;
  165. }
  166. public async Task<ResponseWrapperBase> Handle(AddSceneSchedulesCommand request, CancellationToken cancellationToken)
  167. {
  168. await _scheduler.DeleteJob(delJoinName, delGroupName);
  169. string cron = JobUtil.GetCronByTime(newSceneSchedule.Hour, newSceneSchedule.Minute, newSceneSchedule.Second, (TimingWeekEnum)newSceneSchedule.Week);
  170. await _scheduler.AddSceneJob(joinName, groupName, cron, joinName);
  171. return await Task.FromResult(result);
  172. }
  173. ```
  174. # 调用极光
  175. - 确定别名
  176. - 往数据库的Noti存入一条记录
  177. ```
  178. 对应的领域模型是:
  179. /// <summary>
  180. /// 通知
  181. /// </summary>
  182. public class Noti : Entity<Guid>, IAggregateRoot
  183. {
  184. /// <summary>
  185. /// 通知标题
  186. /// </summary>
  187. public string Title { get; private set; }
  188. /// <summary>
  189. /// 通知内容
  190. /// </summary>
  191. public string Content { get; private set; }
  192. /// <summary>
  193. /// 是否已读
  194. /// </summary>
  195. public string IsRead { get;private set; }
  196. /// <summary>
  197. /// 通知类型
  198. /// 报警通知的Type=0
  199. /// 延迟断电通知的Type=alert
  200. /// </summary>
  201. public string Type { get; private set; }
  202. /// <summary>
  203. /// 报警通知用的报警级别描述
  204. /// </summary>
  205. public string Level { get; private set; }
  206. /// <summary>
  207. /// 传递的主键
  208. /// 用来点击某个通知跳转到某个地方
  209. /// </summary>
  210. public string ExtraId { get; private set; }
  211. /// <summary>
  212. /// 创建方式1
  213. /// </summary>
  214. /// <param name="title"></param>
  215. /// <param name="content"></param>
  216. public Noti(string title, string content)
  217. {
  218. if (string.IsNullOrEmpty(title)) throw new ArgumentNullException("title cannot be null");
  219. if (string.IsNullOrEmpty(content)) throw new ArgumentNullException("content cannot be null");
  220. Title = title;
  221. Content = content;
  222. CreateTime = DateTime.Now.ToFullTimeStr();
  223. Id = Guid.NewGuid();
  224. LastUpdateTime = DateTime.Now.ToFullTimeStr();
  225. IsLogicDel = CommonConstants.LogicDelNo;
  226. }
  227. /// <summary>
  228. /// 创建方式2
  229. /// </summary>
  230. /// <param name="title"></param>
  231. /// <param name="content"></param>
  232. /// <param name="type"></param>
  233. /// <param name="extraId"></param>
  234. public Noti(string title, string content, string type, string extraId)
  235. {
  236. if (string.IsNullOrEmpty(title)) throw new ArgumentNullException("title cannot be null");
  237. if (string.IsNullOrEmpty(content)) throw new ArgumentNullException("content cannot be null");
  238. if (string.IsNullOrEmpty(extraId)) throw new ArgumentNullException("extraId cannot be null");
  239. Title = title;
  240. Content = content;
  241. Type = type;
  242. ExtraId = extraId;
  243. CreateTime = DateTime.Now.ToFullTimeStr();
  244. Id = Guid.NewGuid();
  245. LastUpdateTime = DateTime.Now.ToFullTimeStr();
  246. IsLogicDel = CommonConstants.LogicDelNo;
  247. }
  248. /// <summary>
  249. /// 设置报警级别
  250. /// </summary>
  251. /// <param name="level"></param>
  252. public void SetLevel(string level)
  253. {
  254. Level = level;
  255. }
  256. /// <summary>
  257. /// 标记已读
  258. /// </summary>
  259. /// <param name="regId"></param>
  260. public void MarkRead(string regId)
  261. {
  262. if (string.IsNullOrEmpty(regId)) throw new ArgumentNullException("title cannot be null");
  263. if(string.IsNullOrEmpty(IsRead))
  264. {
  265. IsRead = regId;
  266. }
  267. else
  268. {
  269. var currentRegIds = regId.Split(',').ToList();
  270. currentRegIds.Add(regId);
  271. IsRead = string.Join(',', currentRegIds);
  272. }
  273. }
  274. }
  275. //对应的仓储
  276. private readonly INotiRepository _notiRepo;
  277. public SomeRequestHandler(INotiRepository notiRepo)
  278. {
  279. _notiRepo = notiRepo;
  280. }
  281. //大致用法
  282. var newNoti = new Noti("title","content", "alert", string.Empty);
  283. _notiRepo.Add(newNoti);
  284. await _notiRepo.UnitOfWork.SaveChangesAsync(cancellationToken);
  285. ```
  286. - 极光推送
  287. ```
  288. var request = new JiGuanPushRequest
  289. {
  290. platform = "all",
  291. //audience = "all",//针对所有
  292. audience = new Audience
  293. {
  294. alias = tempAlias
  295. },
  296. notification = new Notification
  297. {
  298. alert = $"{locationDesc},{kgDesc},{warningDesc}",
  299. android = new Android
  300. {
  301. extras = new Extras
  302. {
  303. android_key1 = string.Empty,
  304. kaiGuanId = cloudKaiGuan.Id.ToString(),
  305. connKey = cloudKaiGuan.ConnKey,
  306. startTime = startEndTime,
  307. warningType = ((short)type).ToString(),
  308. projectId = _appOptions.Value.projectid,
  309. notiType = "0", //通知消息类型
  310. notiId = notis.Id.ToString()
  311. }
  312. },
  313. ios = new Ios
  314. {
  315. sound = "sound.caf",
  316. badge = "+1",
  317. extras = new Extras2
  318. {
  319. ios_key1 = string.Empty,
  320. kaiGuanId = cloudKaiGuan.Id.ToString(),
  321. connKey = cloudKaiGuan.ConnKey,
  322. startTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
  323. warningType = ((short)type).ToString(),
  324. projectId = _appOptions.Value.projectid,
  325. notiType = "0", //通知消息类型
  326. notiId = notis.Id.ToString()
  327. }
  328. }
  329. },
  330. options = new JiGuangs.Options
  331. {
  332. apns_production = true
  333. }
  334. };
  335. var response = await _appOptions.Value.jiguang
  336. .WithHeader("Content-Type", "application/json")
  337. .WithHeader("Authorization", "Basic M2ZhOTE0NTUzNmEwMDFmY2UwYjg3NjU5OjIxYjE0ZGM0OGZkZjRmMDk4NzZiMTg0MQ==")
  338. .PostJsonAsync(request);
  339. //相关Dto
  340. public class JiGuanPushRequest
  341. {
  342. [JsonProperty("platform")]
  343. public string platform { get; set; }
  344. //[JsonProperty("audience")]
  345. //public string audience { get; set; }
  346. [JsonProperty("audience")]
  347. public Audience audience { get; set; }
  348. [JsonProperty("notification")]
  349. public Notification notification { get; set; }
  350. [JsonProperty("options")]
  351. public Options options { get; set; }
  352. }
  353. public class Audience
  354. {
  355. [JsonProperty("alias")]
  356. public List<string> alias { get; set; } = new List<string>();
  357. }
  358. public class Android
  359. {
  360. [JsonProperty("extras")]
  361. public Extras extras { get; set; }
  362. }
  363. public class Extras
  364. {
  365. [JsonProperty("android-key1")]
  366. public string android_key1 { get; set; }
  367. [JsonProperty("kaiGuanId")]
  368. public string kaiGuanId { get; set; }
  369. [JsonProperty("warningType")]
  370. public string warningType { get; set; }
  371. [JsonProperty("startTime")]
  372. public string startTime { get; set; }
  373. [JsonProperty("connKey")]
  374. public string connKey { get; set; }
  375. [JsonProperty("notiType")]
  376. public string notiType { get; set; }
  377. [JsonProperty("projectId")]
  378. public string projectId { get; set; }
  379. [JsonProperty("notiId")]
  380. public string notiId { get; set; }
  381. }
  382. public class Extras2
  383. {
  384. [JsonProperty("ios-key1")]
  385. public string ios_key1 { get; set; }
  386. [JsonProperty("kaiGuanId")]
  387. public string kaiGuanId { get; set; }
  388. [JsonProperty("warningType")]
  389. public string warningType { get; set; }
  390. [JsonProperty("startTime")]
  391. public string startTime { get; set; }
  392. [JsonProperty("connKey")]
  393. public string connKey { get; set; }
  394. [JsonProperty("notiType")]
  395. public string notiType { get; set; }
  396. [JsonProperty("projectId")]
  397. public string projectId { get; set; }
  398. [JsonProperty("notiId")]
  399. public string notiId { get; set; }
  400. }
  401. public class Ios
  402. {
  403. [JsonProperty("sound")]
  404. public string sound { get; set; }
  405. [JsonProperty("badge")]
  406. public string badge { get; set; }
  407. [JsonProperty("extras")]
  408. public Extras2 extras { get; set; }
  409. }
  410. public class Notification
  411. {
  412. [JsonProperty("alert")]
  413. public string alert { get; set; }
  414. [JsonProperty("android")]
  415. public Android android { get; set; }
  416. [JsonProperty("ios")]
  417. public Ios ios { get; set; }
  418. }
  419. public class Options
  420. {
  421. [JsonProperty("apns_production")]
  422. public bool apns_production { get; set; }
  423. }
  424. public class TempRegIdsAndAlias
  425. {
  426. public string RegId { get; set; }
  427. public string Alias { get; set; }
  428. }
  429. ```