使用C#开发windows服务定时发消息到钉钉群_群组简单消息
前言:本提醒服务,是由C#语言开发的,主要由windows服务项目和winform项目组成,运行服务可实现功能:向钉钉自定义机器人群组里,定时,定次,推送多个自定义消息内容,并实现主要功能的日志记录。 可以说功能强大!!!
备注: 本文主要2部分:1-关键代码,2-安装步骤。
A-关键代码:
1-服务:
public partial class MyTipsService : ServiceBase
{
public MyTipsService()
{
InitializeComponent();
} protected override void OnStart(string[] args)
{
//服务启动
List<TimeCycle> timeCycleList = new List<TimeCycle> {
new TimeCycle {
ID=,
Action =this.SendTipsToDingding,
BeginTime="09:05:00",
EndTime="09:15:00",
MaxActionTimes=,
ActionSeconds=
},
new TimeCycle {
ID=,
Action =this.SendTipsToDingding,
BeginTime="17:50:00",
EndTime="18:05:00",
MaxActionTimes=,
ActionSeconds=
},
new TimeCycle {
ID=,
Action =this.MyProjectBugTips,
BeginTime="09:10:00",
EndTime="09:15:00",
MaxActionTimes=,
ActionSeconds=
},
}; MyLog.WriteLog("服务启动");
MyServiceHelp myServiceHelp = new MyServiceHelp(timeCycleList);
myServiceHelp.Start(); } protected override void OnStop()
{
//服务终止
MyLog.WriteLog("服务终止");
} /// <summary>
/// 测试方法
/// </summary>
public void Test()
{
MyProjectBugTips();
} #region 获取提醒消息 /// <summary>
/// 每天上下班提醒
/// </summary>
private void SendTipsToDingding()
{
MyLog.WatchAction(() =>
{
StringBuilder strBuilder = new StringBuilder();
DateTime now = DateTime.Now;
if (now.Hour < )
{
strBuilder.Append("现在时间是:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "\r\n");
strBuilder.Append("上班记得打卡!打卡迟到时间不能大于60分钟,多1分钟扣10块!\r\n");
strBuilder.Append("上班记得佩戴胸牌!被抓住一次扣30块钱!\r\n");
}
else
{
strBuilder.Append("现在时间是:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "\r\n");
strBuilder.Append("下班记得打卡!\r\n");
} string shangbanTipMessage = strBuilder.ToString(); if (!string.IsNullOrEmpty(shangbanTipMessage))
{
DingDingHelp dingdingInstance = new DingDingHelp(ConfigHelper.GetAppSettingValue("dingdingGroupUrl"));
string result = dingdingInstance.SendMesasge(shangbanTipMessage, "接收人手机号"); MyLog.WriteLog("发送打卡提醒消息结果:" + result);
}
});
} /// <summary>
/// 我的项目BUG的提醒
/// </summary>
private void MyProjectBugTips()
{
MyLog.WatchAction(() =>
{
DateTime now = DateTime.Now; List<string> bugWhereList = new List<string>();
bugWhereList.Add(string.Format(@"{0}!='{1}'", Sys_commonlog._LOGTYPE_, "HttpException"));
bugWhereList.Add(string.Format(@"{0}>='{1}'", Sys_commonlog._CREATETIME_, now.AddDays(-)));
bugWhereList.Add(string.Format(@"{0}<='{1}'", Sys_commonlog._CREATETIME_, now)); //A-获取所有人的异常监控配置
NameValueCollection userNameValueColl = ConfigHelper.GetSectionNameValueCollection("bugUser");
Dictionary<string, List<string>> userKeyWordListDic = new Dictionary<string, List<string>>(userNameValueColl.Count);
List<string> userNameList = new List<string>(userNameValueColl.Count);
List<string> userPhoneList = new List<string>(userNameValueColl.Count); List<string> allKeyWordWhereList = new List<string>();
string[] userArray = null; foreach (string userKeyAt in userNameValueColl)
{
if (!string.IsNullOrEmpty(userKeyAt))
{
userArray = userKeyAt.Split(',');
if (userArray.Length > )
{
string userName = userArray[];
userNameList.Add(userName);
userPhoneList.Add(userArray[]); userKeyWordListDic.Add(userName, userNameValueColl[userKeyAt].Split(',').ToList()); if (userKeyWordListDic[userName].Count > )
{
foreach (string keyWord in userKeyWordListDic[userName])
{
allKeyWordWhereList.Add(string.Format(@"{0} LIKE '%{1}%'", Sys_commonlog._URL_, keyWord));
}
} }
}
}
userNameValueColl = null; string whereSql = string.Format(@"{0} AND ({1})", string.Join(" AND ", bugWhereList), string.Join(" OR ", allKeyWordWhereList));
allKeyWordWhereList.Clear();
allKeyWordWhereList = null;
bugWhereList.Clear();
bugWhereList = null; //B-获取所有站点
string[] websiteNameArray = ConfigHelper.GetAppSettingValue("websiteName").Split(','); //C-收集每个人每个站的BUG汇总信息 Dictionary<string, List<Sys_commonlog>> websiteDataListDic = new Dictionary<string, List<Sys_commonlog>>(websiteNameArray.Length); Sys_commonlogDAL instance = new Sys_commonlogDAL();
foreach (string webSiteName in websiteNameArray)
{
websiteDataListDic.Add(webSiteName, instance.Select(whereSql, Sys_commonlog._ID_ + " DESC", Conn.GetConnectionString("Constr_" + webSiteName)));
} List<UserExectionHelp> userExectionHelpList = new List<UserExectionHelp>(userNameList.Count);
List<Sys_commonlog> userWebsiteCommonLogList = null;
List<WebSiteExectionHelp> webSiteExectionHelpList = null;
WebSiteExectionHelp webSiteExection = null;
int userBugTotalCount = ;
List<string> userKeyWordList = null;
for (var i = ; i < userNameList.Count; i++)
{
userKeyWordList = userKeyWordListDic[userNameList[i]];
webSiteExectionHelpList = new List<WebSiteExectionHelp>(websiteNameArray.Length);
foreach (var websiteDicItem in websiteDataListDic)
{
foreach (var userKeyWord in userKeyWordList)
{
userWebsiteCommonLogList = websiteDicItem.Value.FindAll(item => item.Url.Split('?')[].Contains(userKeyWord));
if (userWebsiteCommonLogList.Count > )
{
break;
}
}
if (userWebsiteCommonLogList != null && userWebsiteCommonLogList.Count > )
{
userBugTotalCount += userWebsiteCommonLogList.Count;
webSiteExection = new WebSiteExectionHelp { WebsiteName = websiteDicItem.Key, TotalCount = userWebsiteCommonLogList.Count, ExectionHelpList = userWebsiteCommonLogList };
webSiteExectionHelpList.Add(webSiteExection);
}
} if (webSiteExectionHelpList.Count > )
{
userExectionHelpList.Add(new UserExectionHelp { UserName = userNameList[i], UserPhone = userPhoneList[i], TotalCount = userBugTotalCount, WebSiteExectionHelpList = webSiteExectionHelpList });
} //重置bug总数
userBugTotalCount = ; } //D-循环输出信息
DingDingHelp dingdingInstance = new DingDingHelp(ConfigHelper.GetAppSettingValue("dingdingGroupUrl"));
Sys_commonlog execItem = null;
StringBuilder strBuilder = new StringBuilder();
//等待发送的消息数量
int toBeSendMessageCount = ;
userExectionHelpList.ForEach(item =>
{
strBuilder.AppendFormat("网站异常_{0}_{1}_BUG总数:{2}:\r\n", item.UserName, now.ToShortDateString(), item.TotalCount);
MyLog.WriteLog(string.Format("网站异常_{0}_{1}_BUG总数:{2}:", item.UserName, now.ToShortDateString(), item.TotalCount)); item.WebSiteExectionHelpList.ForEach(webItem =>
{
for (var i = ; i < webItem.ExectionHelpList.Count; i++)
{
execItem = webItem.ExectionHelpList[i];
strBuilder.AppendFormat("【{0}-BUG-{1}】:\r\n", webItem.WebsiteName, (i + ));
strBuilder.AppendFormat("ID:{0}\nLogType:{1}\nCreateTime:{2}\nLogContent:{3}\nUrl:{4}\r\n", execItem.ID, execItem.LogType, execItem.CreateTime, execItem.LogContent, execItem.Url);
toBeSendMessageCount++;
//超出50条自动发送
if (toBeSendMessageCount >= )
{
dingdingInstance.SendMesasge(strBuilder.ToString(), item.UserPhone);
toBeSendMessageCount = ;
strBuilder.Clear();
}
}
});
//发送剩余未发送消息数量
if (toBeSendMessageCount > )
{
dingdingInstance.SendMesasge(strBuilder.ToString(), item.UserPhone);
strBuilder.Clear();
}
toBeSendMessageCount = ;
});
strBuilder = null; userNameList.Clear();
userNameList = null;
userPhoneList.Clear();
userPhoneList = null; userExectionHelpList.Clear();
userExectionHelpList = null;
if (userWebsiteCommonLogList != null)
{
userWebsiteCommonLogList.Clear();
userWebsiteCommonLogList = null;
}
websiteDataListDic.Clear();
websiteDataListDic = null;
if (webSiteExectionHelpList != null)
{
webSiteExectionHelpList.Clear();
webSiteExection = null;
}
});
} #endregion }
原来服务帮助类中遇到了很多问题,如:如何统计多个任务中,每个任务的已执行次数问题? 如何让多个任务准时执行? 如何让多个任务中,每个任务按照自己的任务间隔执行?
针对这些问题,就设计了一个TimeCycel类(包含一个任务的ID,任务内容,任务的开始时间,任务时间间隔,任务已执行次数,任务最大执行次数)。
重点:服务运行后会形成一个单例模式,计时器,及服务帮助类的所有属性的初始化值,均会存在该单例中,因此我们可以将每个任务的信息记录在每个任务的实例中去,这样更合理;另外在任务执行时,新增了多线程的使用来处理任务的准时和执行次数问题。
原本走过一些弯路:原本TimeCycel类中没有任务已执行次数属性,我是通过一个方法计算的:当前时间-任务开始时间/任务执行间隔,计算的,但是这个方式计算存在时延问题以及任务的执行快慢,无法保证执行次数和执行间隔的准确性,使用每个任务的实例单独统计快捷方便,保证了已执行次数的准确性。
public class MyServiceHelp
{
public MyServiceHelp(List<TimeCycle> timeCycleList)
{
this.TimeCycleList = timeCycleList;
this.Timer = new Timer();
} /// <summary>
/// 服务专属计时器
/// </summary>
private System.Timers.Timer Timer; /// <summary>
/// 默认计时器时间间隔1秒(提高计时器开始时间准确度)
/// </summary>
private double DefaultTimerInterval = * ; /// <summary>
/// 设置多个循环周期
/// </summary>
public List<TimeCycle> TimeCycleList { get; set; } /// <summary>
/// 更新一个计时器的计时周期
/// </summary>
/// <param name="newTimerInterval">新的计时周期</param>
/// <param name="isFirstStart">是否是首次更新计时器周期</param>
public void UpdateTimeInterval(double newTimerInterval, bool isFirstStart = false)
{
if (this.Timer != null && newTimerInterval > )
{
this.Timer.Stop();
if (this.Timer.Interval != newTimerInterval)
{
this.Timer.Interval = newTimerInterval;
}
if (isFirstStart)
{
this.Timer.Elapsed += new System.Timers.ElapsedEventHandler(this.ServiceAction);
}
this.Timer.AutoReset = true;
this.Timer.Start();
}
} /// <summary>
/// 内部辅助方法
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ServiceAction(object sender, ElapsedEventArgs e)
{
List<TimeCycle> currentTimeCycleList = new List<TimeCycle>(); DateTime now = DateTime.Now;
DateTime cycleBeginTime;
DateTime cycleEndTime;
foreach (TimeCycle timeCycle in this.TimeCycleList)
{
cycleBeginTime = Convert.ToDateTime(timeCycle.BeginTime);
cycleBeginTime = now.Date.AddHours(cycleBeginTime.Hour).AddMinutes(cycleBeginTime.Minute).AddSeconds(cycleBeginTime.Second);
cycleEndTime = Convert.ToDateTime(timeCycle.EndTime);
cycleEndTime = now.Date.AddHours(cycleEndTime.Hour).AddMinutes(cycleEndTime.Minute).AddSeconds(cycleEndTime.Second);
if (cycleEndTime < cycleBeginTime)
{
cycleEndTime = cycleEndTime.AddDays();
} if (now >= cycleBeginTime && now <= cycleEndTime)
{
if (timeCycle.ActionExecutionTimes < timeCycle.MaxActionTimes)
{
TimeSpan timeSpan = now - cycleBeginTime;
bool isCanAction = (int)timeSpan.TotalSeconds % timeCycle.ActionSeconds == ? true : false;
if (isCanAction)
{
timeCycle.ActionExecutionTimes++;
currentTimeCycleList.Add(timeCycle);
}
}
}
else
{
//不在计时周期内,已执行次数清零
timeCycle.ActionExecutionTimes = ;
}
}
//找到当前循环周期后,执行周期内动作
if (currentTimeCycleList.Count > )
{
currentTimeCycleList.ForEach(item =>
{
//使用多线程执行任务,让代码快速执行
Task.Run(item.Action);
});
}
} public void Start()
{
//设置首次计时器周期(首次动作执行,是在计时器启动后在设置的时间间隔后做出的动作)
this.UpdateTimeInterval(this.DefaultTimerInterval, true); } } /// <summary>
/// 计时周期类
/// </summary>
public class TimeCycle
{
/// <summary>
/// 唯一标识
/// </summary>
public int ID { get; set; }
/// <summary>
/// 开始时间(误差1秒=取决于计时器默认时间间隔)
/// </summary>
public string BeginTime { get; set; }
/// <summary>
/// 结束时间
/// </summary>
public string EndTime { get; set; }
/// <summary>
/// 最大执行次数
/// </summary>
public int MaxActionTimes { get; set; }
/// <summary>
/// 计时周期内执行的动作(动作会在到达开始时间后的)
/// </summary>
public Action Action { get; set; }
/// <summary>
/// 动作执行时间间隔(秒)
/// </summary>
public int ActionSeconds { get; set; }
/// <summary>
/// 方法执行次数
/// </summary>
internal int ActionExecutionTimes { get; set; }
}
2-服务管理windowform代码:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.textServicePath.Text = ConfigHelper.GetAppSettingValue("serviceExeUrl");
} private static string serviceName = "MyTips"; private void btnStartService_Click(object sender, EventArgs e)
{
this.InstallService();
} private void btnEndService_Click(object sender, EventArgs e)
{
this.UninstallService();
} private void btnTestService_Click(object sender, EventArgs e)
{
new MyTipsService().Test();
} //判断服务是否存在
private bool IsServiceExisted()
{
ServiceController[] services = ServiceController.GetServices();
foreach (ServiceController sc in services)
{
if (sc.ServiceName.ToLower() == serviceName.ToLower())
{
return true;
}
}
return false;
} //安装服务
private void InstallService()
{
if (IsServiceExisted() == false)
{
string[] args = new string[] { this.textServicePath.Text };
ManagedInstallerClass.InstallHelper(args);
MessageBox.Show("安装成功!"); using (ServiceController control = new ServiceController(serviceName))
{
if (control.Status == ServiceControllerStatus.Stopped)
{
control.Start();
}
}
}
else
{
MessageBox.Show("已安装!");
}
} //卸载服务
private void UninstallService()
{
if (IsServiceExisted())
{
//停止服务
using (ServiceController control = new ServiceController(serviceName))
{
if (control.Status == ServiceControllerStatus.Running)
{
control.Stop();
}
}
//开始卸载
string[] args = new string[] { "/u", this.textServicePath.Text };
ManagedInstallerClass.InstallHelper(args);
MessageBox.Show("卸载成功!");
}
else
{
MessageBox.Show("已卸载!");
}
} //查找项目下、安装目录下服务EXE文件路径
private void btnFindExe_Click(object sender, EventArgs e)
{
string[] resultArray = Directory.GetFiles(Directory.GetCurrentDirectory(), "我的每日提醒项目.exe", SearchOption.AllDirectories);
if (resultArray.Length > )
{
this.textServicePath.Text = resultArray[];
}
}
}
B-安装过程:
准备资料:
1-钉钉群组添加自定义机器人,生成webhook链接URL。
2-一个windows服务项目。
3-一个管理服务安装,卸载,启动,停止的winform窗体项目。
1-创建自定义机器人:
在钉钉的群组里,有群机器人的入口进入,添加一个自定义机器人:
添加成功后,会生成一个开发的webhook推送消息的接口地址:
2-一个windows服务项目,以VS2015为例:
在服务的设计图里,右键添加安装程序:
安装程序如下:我们可以设置关于服务的一些说明和程序设置:
说明相关:描述,显示名,服务名称
服务程序账户设置:这里我们要选择本地系统
至此一个服务的项目及安装程序就搭建起来了。
3-一个管理服务安装,卸载,启动,停止的winform窗体项目:
这里我只展示页面,具体创建很简单,就不多说了,我将安装和服务的启用,卸载和服务的停用写在一起了。
使用C#开发windows服务定时发消息到钉钉群_群组简单消息的更多相关文章
- C# DateTime的11种构造函数 [Abp 源码分析]十五、自动审计记录 .Net 登陆的时候添加验证码 使用Topshelf开发Windows服务、记录日志 日常杂记——C#验证码 c#_生成图片式验证码 C# 利用SharpZipLib生成压缩包 Sql2012如何将远程服务器数据库及表、表结构、表数据导入本地数据库
C# DateTime的11种构造函数 别的也不多说没直接贴代码 using System; using System.Collections.Generic; using System.Glob ...
- VS2013开发Windows服务项目
这篇随笔里,我将介绍如何用VS2013开发Windows服务项目,实现的功能是定时发送电子邮件. 开发环境:VS2013,SQL Server2008,采用C#语言开发 步骤一:创建Windows服务 ...
- js replace 全局替换 以表单的方式提交参数 判断是否为ie浏览器 将jquery.qqFace.js表情转换成微信的字符码 手机端省市区联动 新字体引用本地运行可以获得,放到服务器上报404 C#提取html中的汉字 MVC几种找不到资源的解决方式 使用Windows服务定时去执行一个方法的三种方式
js replace 全局替换 js 的replace 默认替换只替换第一个匹配的字符,如果字符串有超过两个以上的对应字符就无法进行替换,这时候就要进行一点操作,进行全部替换. <scrip ...
- C#开发Windows服务 附简单实例实现禁止QQ运行
本实例主要实现下面三个基本功能 1.C#开发windows服务 2.禁止QQ等程序运行 3.为windows服务创建自动安装程序 下面针对这三个基本功能进行实现 一.C#开发windows服务 Win ...
- C#开发windows服务如何调试——资料整理
原文标题:C# Windows服务程序如何进行调试 原文地址:https://jingyan.baidu.com/article/456c463b18e1b00a583144b3.html 第一种: ...
- 使用Visual Studio 2015 Community 开发windows服务
昨天研究在.NET下开发Windows服务程序,期间遇到一些小问题,这里仅将自己的开发过程和需要注意的地方写下和广大网友分享…… 1.基础 Windows服务是指系统启动时能够自己运行的程序.W ...
- C#开发Windows服务详细流程
1.Windows服务简单介绍 Windows服务程序是在Windows操作系统下能完成特定功能的可执行的应用程序,主要用于长时间运行的功能或者执行定时任务.一般情况下,用户不能通过用户界面来安装和启 ...
- 使用Topshelf开发Windows服务、log4net记录日志
开发windows服务,除了在vs里新建服务项目外(之前有写过具体开发方法,可点击查看),还可以使用Topshelf. 不过使用topshelf需要.netframework 4.5.2版本,在vs2 ...
- 开发Windows服务
在开发Windows服务时需要注意一点,如果在开发完成后,需要通过命令来进行安装的,那么在开发的时候,需要在服务类上面添加一个安装文件.如下图: 添加完成后,就 ...
随机推荐
- Java报错信息 java.lang.SecurityException: Prohibited package name: java.xxx
package java.yun.System; public class SystemOut { public static void main(String[] args) { System.ou ...
- 搞定! iTunes 不能添加铃声进去
最近换个新铃声,但转换成.m4r 怎么都拖不到铃声里很莫名奇妙,首先确定苹果是允许自己定义铃声的,然后网上查了不少文章,都无济于事所以我想自己记录下自己成功搞定的方法,供各位参考第一步: 把自己想转为 ...
- Django_cookie_session
登录时候后台打印request.COOKIE 1.login页面正确登录的话,后台页面可以获取到浏览器携带的cookie的. 2.第一行的sessionid其实就是cookie值 3.session的 ...
- Hugo + Github Pages 搭建个人博客
尝试过 Hexo .GatsbyJs. Vuepress 搭建博客后,对这些工具最大的不满,就是运行速度以及打包速度. 后来看到 Hugo ,号称最快的静态站点生成器后. 尝试搭建博客,发现不管是运行 ...
- 从壹开始前后端分离[.NetCore ] 38 ║自动初始化数据库(不定期更新)
缘起 哈喽大家好呀,我们又见面啦,这里先祝大家圣诞节快乐哟,昨天的红包不知道有没有小伙伴抢到呢.今天的这篇内容灰常简单,只是对我们的系统的数据库进行CodeFirst,然后就是数据处理,因为这几个月来 ...
- 性能超前,详解腾讯云新一代Redis缓存数据库
背景 当前内存数据库发展迅速,用户对于存储系统的要求也越来越高,为了满足各类业务场景的需要,腾讯云设计了新一代的内存数据库,不但保留了原来系统的高性能,高可用等特性,同时还兼容了当前流行的Redis原 ...
- Docker镜像细节
前言 只有光头才能变强. 文本已收录至我的GitHub仓库,欢迎Star:https://github.com/ZhongFuCheng3y/3y 回顾前面: 为什么需要Docker? Docker入 ...
- Eureka服务配置与进阶
1. Eureka服务配置与进阶 1.1. 主要配置 1.1.1. 服务端(eureka.server.*) enableSelfPreservation默认true,启用注册中心的自保护机制,Eur ...
- 5.App Inventor 2编程实例--指南针
本视频来自:https://www.17coding.net 的 国庆特辑——指南针 共3个视频. 注意: 项目名字要使用英文. 项目完成后可以选择“打包APK”—“ 打包APK并下载到电脑”,然后 ...
- oppo8.0系统怎么无需Root激活Xposed框架的经验
在较多部门的引流,或业务操作中,基本都需要使用安卓的神一般的xposed框架,近期我们部门购来了一批新的oppo8.0系统,基本都都是基于7.0以上版本,基本都不能够刷入root的su权限,虽说一部分 ...