集团是使用钉钉进行工作交流的, 发现群里有很多问题其实是重复的,就在想是不是可以使用钉钉的群机器人,虽然说的确是可以部分实现,但是感觉还是差点什么,而且公司内部很多东西也不方便放上去,所以就想开发一个群机器人,然后就看钉钉开发文档,发现是有这个功能的,就开始研究,官方文档使用的语言主要是Java,并没有c#或者asp.net的相关文档,这就意味着要从头开始开发, 所幸的是他是有c#的SDK开发包,开发包里是有DLL的,这样能省下不少事,废话不多说,上链接

https://open.dingtalk.com/document/resourcedownload/download-server-sdk

打开页面后往下拉,知道如图所示处

我下载的是.net版本,下载下来后,导入到项目中即可
然后是配置机器人,这些在往上教程很多就不多赘诉了,直接上图

一开始我是在页面上面写的,看到官方文档上面说到了header,考虑到可能要使用到request 获取,就直接在页面写了,

后来在页面上通过以后改到了WebService中,毕竟感觉上webservice 会好一些,

把消息接收地址改成了这样,其实两者代码类似,只是我可能更喜欢在接口里写

 1  protected string secret = 改成你自己的机器人的appSecret;2     #region 机器人操作类3     [WebMethod]4     public void Reboot()5     {6         string result = "";7         using (StreamReader reader = new StreamReader(HttpContext.Current.Request.InputStream, Encoding.UTF8))8         {9             result = reader.ReadToEnd();
10         }
11         try
12         {
13             string sign = HttpContext.Current.Request.Headers.GetValues("sign")[0].ToString();
14             string timestamp = HttpContext.Current.Request.Headers.GetValues("timestamp")[0].ToString();
15             string json = result;
16             CommonJsonModel model = SymmetricMethod.DeSerialize(json);
17             string text = model.GetModel("text").GetValue("content");
18             string sessionWebhook = model.GetValue("sessionWebhook");
19             string senderStaffId = model.GetValue("senderStaffId");
20             DBHelper.InsertRebootLog(HttpContext.Current.Request, HttpContext.Current.Request.Url.ToString(), "调用机器人", text + "--" + sessionWebhook + "--" + senderStaffId, sign + "----------" + timestamp, result, HttpContext.Current.Request.Headers, "调用机器人");
21         }
22         catch (Exception ex)
23         {
24             DBHelper.InsertRebootLog(HttpContext.Current.Request, HttpContext.Current.Request.Url.ToString(), "调用机器人", result, ex.Message, "接口调用来源不正确","", "调用机器人");
25         }
26     }
27     #endregion

这是webservice 接口的

 1   string result = "";2         using (StreamReader reader = new StreamReader(Request.InputStream, Encoding.UTF8))3         {4             result = reader.ReadToEnd();5         }6         try7         {8             string sign = Request.Headers.GetValues("sign")[0].ToString();9             string timestamp = Request.Headers.GetValues("timestamp")[0].ToString();
10             string json = result;
11             CommonJsonModel model = SymmetricMethod.DeSerialize(json);
12             string text = model.GetModel("text").GetValue("content");
13             string sessionWebhook = model.GetValue("sessionWebhook");
14             string senderStaffId = model.GetValue("senderStaffId");
15             DBHelper.InsertRebootLog(Request, Request.Url.ToString(), "调用机器人", text + "--" + sessionWebhook + "--" + senderStaffId, sign + "----------" + timestamp, result, Request.Headers, "调用机器人");
16         }
17         catch (Exception ex)
18         {
19             DBHelper.InsertRebootLog(Request, Request.Url.ToString(), "调用机器人", result, ex.Message, "", "", "调用机器人");
20         }

这是写在页面Page_Load方法里面的,因为只要执行到这个页面,就是直接执行,没有任何其他操作,所以一定要写在Page_Load方法里

那么json 解析的源码我也放后面,也就是 CommonJsonModel 这个方法的代码

直接建两个类,名字分别是CommonJsonModelAnalyzer 和 CommonJsonModel

  1 using System;2 using System.Collections.Generic;3 using System.Web;4 using System.Text;5 6 /// <summary>7 ///CommonJsonModelAnalyzer 的摘要说明8 /// </summary>9 public class CommonJsonModelAnalyzer10 {11     public CommonJsonModelAnalyzer()12     {13         //14         //TODO: 在此处添加构造函数逻辑15         //16 17     }18     protected string _GetKey(string rawjson)19     {20         if (string.IsNullOrEmpty(rawjson))21             return rawjson;22 23         rawjson = rawjson.Trim();24 25         string[] jsons = rawjson.Split(new char[] { ':' });26 27         if (jsons.Length < 2)28             return rawjson;29 30         return jsons[0].Replace("\"", "").Trim();31     }32 33     protected string _GetValue(string rawjson)34     {35         if (string.IsNullOrEmpty(rawjson))36             return rawjson;37 38         rawjson = rawjson.Trim();39 40         string[] jsons = rawjson.Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries);41 42         if (jsons.Length < 2)43             return rawjson;44 45         StringBuilder builder = new StringBuilder();46 47         for (int i = 1; i < jsons.Length; i++)48         {49             builder.Append(jsons[i]);50 51             builder.Append(":");52         }53 54         if (builder.Length > 0)55             builder.Remove(builder.Length - 1, 1);56 57         string value = builder.ToString();58 59         if (value.StartsWith("\""))60             value = value.Substring(1);61 62         if (value.EndsWith("\""))63             value = value.Substring(0, value.Length - 1);64 65         return value;66     }67 68     protected List<string> _GetCollection(string rawjson)69     {70         //[{},{}]71 72         List<string> list = new List<string>();73 74         if (string.IsNullOrEmpty(rawjson))75             return list;76 77         rawjson = rawjson.Trim();78 79         StringBuilder builder = new StringBuilder();80 81         int nestlevel = -1;82 83         int mnestlevel = -1;84 85         for (int i = 0; i < rawjson.Length; i++)86         {87             if (i == 0)88                 continue;89             else if (i == rawjson.Length - 1)90                 continue;91 92             char jsonchar = rawjson[i];93 94             if (jsonchar == '{')95             {96                 nestlevel++;97             }98 99             if (jsonchar == '}')
100             {
101                 nestlevel--;
102             }
103
104             if (jsonchar == '[')
105             {
106                 mnestlevel++;
107             }
108
109             if (jsonchar == ']')
110             {
111                 mnestlevel--;
112             }
113
114             if (jsonchar == ',' && nestlevel == -1 && mnestlevel == -1)
115             {
116                 list.Add(builder.ToString());
117
118                 builder = new StringBuilder();
119             }
120             else
121             {
122                 builder.Append(jsonchar);
123             }
124         }
125
126         if (builder.Length > 0)
127             list.Add(builder.ToString());
128
129         return list;
130     }
131 }
  1 using System;2 using System.Collections.Generic;3 using System.Web;4 5 /// <summary>6 ///CommonJsonModel 的摘要说明7 /// </summary>8 public class CommonJsonModel : CommonJsonModelAnalyzer9 {10     private string rawjson;11 12     private bool isValue = false;13 14     private bool isModel = false;15 16     private bool isCollection = false;17     private string json;18 19    internal CommonJsonModel(string rawjson)20     {21         this.rawjson = rawjson;22 23         if (string.IsNullOrEmpty(rawjson))24             throw new Exception("missing rawjson");25 26         rawjson = rawjson.Trim();27 28         if (rawjson.StartsWith("{"))29         {30             isModel = true;31         }32         else if (rawjson.StartsWith("["))33         {34             isCollection = true;35         }36         else37         {38             isValue = true;39         }40     }41 42     public string Rawjson43     {44         get { return rawjson; }45     }46 47     public bool IsValue()48     {49         return isValue;50     }51     public bool IsValue(string key)52     {53         if (!isModel)54             return false;55 56         if (string.IsNullOrEmpty(key))57             return false;58 59         foreach (string subjson in base._GetCollection(this.rawjson))60         {61             CommonJsonModel model = new CommonJsonModel(subjson);62 63             if (!model.IsValue())64                 continue;65 66             if (model.Key == key)67             {68                 CommonJsonModel submodel = new CommonJsonModel(model.Value);69 70                 return submodel.IsValue();71             }72         }73 74         return false;75     }76     public bool IsModel()77     {78         return isModel;79     }80     public bool IsModel(string key)81     {82         if (!isModel)83             return false;84 85         if (string.IsNullOrEmpty(key))86             return false;87 88         foreach (string subjson in base._GetCollection(this.rawjson))89         {90             CommonJsonModel model = new CommonJsonModel(subjson);91 92             if (!model.IsValue())93                 continue;94 95             if (model.Key == key)96             {97                 CommonJsonModel submodel = new CommonJsonModel(model.Value);98 99                 return submodel.IsModel();
100             }
101         }
102
103         return false;
104     }
105     public bool IsCollection()
106     {
107         return isCollection;
108     }
109     public bool IsCollection(string key)
110     {
111         if (!isModel)
112             return false;
113
114         if (string.IsNullOrEmpty(key))
115             return false;
116
117         foreach (string subjson in base._GetCollection(this.rawjson))
118         {
119             CommonJsonModel model = new CommonJsonModel(subjson);
120
121             if (!model.IsValue())
122                 continue;
123
124             if (model.Key == key)
125             {
126                 CommonJsonModel submodel = new CommonJsonModel(model.Value);
127
128                 return submodel.IsCollection();
129             }
130         }
131
132         return false;
133     }
134
135
136     /// <summary>
137     /// 当模型是对象,返回拥有的key
138     /// </summary>
139     /// <returns></returns>
140     public List<string> GetKeys()
141     {
142         if (!isModel)
143             return null;
144
145         List<string> list = new List<string>();
146
147         foreach (string subjson in base._GetCollection(this.rawjson))
148         {
149             string key = new CommonJsonModel(subjson).Key;
150
151             if (!string.IsNullOrEmpty(key))
152                 list.Add(key);
153         }
154
155         return list;
156     }
157
158     /// <summary>
159     /// 当模型是对象,key对应是值,则返回key对应的值
160     /// </summary>
161     /// <param name="key"></param>
162     /// <returns></returns>
163     public string GetValue(string key)
164     {
165         if (!isModel)
166             return null;
167
168         if (string.IsNullOrEmpty(key))
169             return null;
170
171         foreach (string subjson in base._GetCollection(this.rawjson))
172         {
173             CommonJsonModel model = new CommonJsonModel(subjson);
174
175             if (!model.IsValue())
176                 continue;
177
178             if (model.Key == key)
179                 return model.Value;
180         }
181
182         return null;
183     }
184
185     /// <summary>
186     /// 模型是对象,key对应是对象,返回key对应的对象
187     /// </summary>
188     /// <param name="key"></param>
189     /// <returns></returns>
190     public CommonJsonModel GetModel(string key)
191     {
192         if (!isModel)
193             return null;
194
195         if (string.IsNullOrEmpty(key))
196             return null;
197
198         foreach (string subjson in base._GetCollection(this.rawjson))
199         {
200             CommonJsonModel model = new CommonJsonModel(subjson);
201
202             if (!model.IsValue())
203                 continue;
204
205             if (model.Key == key)
206             {
207                 CommonJsonModel submodel = new CommonJsonModel(model.Value);
208
209                 if (!submodel.IsModel())
210                     return null;
211                 else
212                     return submodel;
213             }
214         }
215
216         return null;
217     }
218
219     /// <summary>
220     /// 模型是对象,key对应是集合,返回集合
221     /// </summary>
222     /// <param name="key"></param>
223     /// <returns></returns>
224     public CommonJsonModel GetCollection(string key)
225     {
226         if (!isModel)
227             return null;
228
229         if (string.IsNullOrEmpty(key))
230             return null;
231
232         foreach (string subjson in base._GetCollection(this.rawjson))
233         {
234             CommonJsonModel model = new CommonJsonModel(subjson);
235
236             if (!model.IsValue())
237                 continue;
238
239             if (model.Key == key)
240             {
241                 CommonJsonModel submodel = new CommonJsonModel(model.Value);
242
243                 if (!submodel.IsCollection())
244                     return null;
245                 else
246                     return submodel;
247             }
248         }
249
250         return null;
251     }
252
253     /// <summary>
254     /// 模型是集合,返回自身
255     /// </summary>
256     /// <returns></returns>
257     public List<CommonJsonModel> GetCollection()
258     {
259         List<CommonJsonModel> list = new List<CommonJsonModel>();
260
261         if (IsValue())
262             return list;
263
264         foreach (string subjson in base._GetCollection(rawjson))
265         {
266             list.Add(new CommonJsonModel(subjson));
267         }
268
269         return list;
270     }
271
272
273
274
275     /// <summary>
276     /// 当模型是值对象,返回key
277     /// </summary>
278     private string Key
279     {
280         get
281         {
282             if (IsValue())
283                 return base._GetKey(rawjson);
284
285             return null;
286         }
287     }
288     /// <summary>
289     /// 当模型是值对象,返回value
290     /// </summary>
291     private string Value
292     {
293         get
294         {
295             if (!IsValue())
296                 return null;
297
298             return base._GetValue(rawjson);
299         }
300     }
301 }

另外还要再建一个调用json解析方法的类 我的名称叫做SymmetricMethod,你们就随意起

在这个类里面写一个方法

1  public static CommonJsonModel DeSerialize(string json)
2     {
3         return new CommonJsonModel(json);
4     }

一定要静态类,方便调用

其实到这一步一些关键内容的核心已经全部写完了,接下来就是如何使用

按照官方文档的说法,是需要对信息进行验证的

开发者需对header中的timestamp和sign进行验证,以判断是否是来自钉钉的合法请求,避免其他仿冒钉钉调用开发者的HTTPS服务传送数据,具体验证逻辑如下:timestamp 与系统当前时间戳如果相差1小时以上,则认为是非法的请求。sign 与开发者自己计算的结果不一致,则认为是非法的请求。必须当timestamp和sign同时验证通过,才能认为是来自钉钉的合法请求。

其中会有sign 计算方法,那么我们就按照文档说的做,

sign的计算方法header中的timestamp + "\n" + 机器人的appSecret当做签名字符串,使用HmacSHA256算法计算签名,然后进行Base64 encode,得到最终的签名值。 
 1   //获得时间戳2     public static long ToUTC(DateTime time)3     {4         var zts = TimeZoneInfo.Local.BaseUtcOffset;5         var yc = new DateTime(1970, 1, 1).Add(zts);6         return (long)(DateTime.Now - yc).TotalMilliseconds;7     }8     //计算签名值9     public static string GetHmac(string message, string secret)
10     {
11         byte[] keyByte = Encoding.UTF8.GetBytes(secret);
12         byte[] messageBytes = Encoding.UTF8.GetBytes(message);
13         using (var hmacsha256 = new HMACSHA256(keyByte))
14         {
15             byte[] hashmessage = hmacsha256.ComputeHash(messageBytes);
16             string hash = Convert.ToBase64String(hashmessage).Replace("+"," ");
17             return hash;
18         }
19     }

以上两段代码网上就能搜到,其中计算签名值网上写的并不完全,因为我们计算出来的签名值与钉钉的实际签名值就差一个“+”和“ ”,所以在最后直接替换就可以了 

 1 private bool GetSign(string timestamp, string secret, string sign)2     {3         try4         {5             //获取当前时间的时间戳6             long currentTime = SymmetricMethod.ToUTC(DateTime.Now);7             long dingTimestamp = long.Parse(timestamp);8             long time = currentTime - dingTimestamp;9             string stringToSign = SymmetricMethod.GetHmac(dingTimestamp + "\n" + secret, secret).ToString();
10             if (time < 3600000 && sign.Equals(stringToSign))
11             {
12                 return true;
13             }
14             return false;
15         }
16         catch (Exception ex)
17         {
18             return false;
19         }
20     }

这样我们就获得了钉钉返回的sign 和timestamp 和我们自己计算出来的sign ,然后根据规则进行判断即可

那么最终合在一起形成这样一段代码

 1   #region 机器人操作类2     [WebMethod]3     public void Reboot()4     {5         string result = "";6         using (StreamReader reader = new StreamReader(HttpContext.Current.Request.InputStream, Encoding.UTF8))7         {8             result = reader.ReadToEnd();9         }
10         try
11         {
12             string sign = HttpContext.Current.Request.Headers.GetValues("sign")[0].ToString();
13             string timestamp = HttpContext.Current.Request.Headers.GetValues("timestamp")[0].ToString();
14             string json = result;
15             CommonJsonModel model = SymmetricMethod.DeSerialize(json);
16             string text = model.GetModel("text").GetValue("content");
17             string sessionWebhook = model.GetValue("sessionWebhook");
18             string senderStaffId = model.GetValue("senderStaffId");
19             DBHelper.InsertRebootLog(HttpContext.Current.Request, HttpContext.Current.Request.Url.ToString(), "调用机器人", text + "--" + sessionWebhook + "--" + senderStaffId, sign + "----------" + timestamp, result, HttpContext.Current.Request.Headers, "调用机器人");
20
21             if (GetSign(timestamp, secret, sign))//验证,如果不通过另行操作或者不返回都可以
22             {
23                 DefaultDingTalkClient client = new DefaultDingTalkClient(sessionWebhook);
24                 text(client, userid, "返回文本测试效果");
25                 markdown(client, userid, "测试markdown", "返回markdown测试效果");
26                 actionCard(client, userid, "测试actionCard", "返回actionCard测试效果", "点击详情", "https://www.taiwei6.com");
27             }
28         }
29         catch (Exception ex)
30         {
31             DBHelper.InsertRebootLog(HttpContext.Current.Request, HttpContext.Current.Request.Url.ToString(), "调用机器人", result, ex.Message, "接口调用来源不正确","", "调用机器人");
32         }
33     }

钉钉机器人总共是能够范围三种类型的分别是text ,markdown,actioncard ,

上源码

  1     /**2     * 实现@人员3     * @param client4     * @param userId5      * 返回文本6     */7     private void text(DefaultDingTalkClient client, String userId, string textcontent)8     {9         try10         {11             OapiRobotSendRequest request = new OapiRobotSendRequest();12             request.Msgtype = "text";13             OapiRobotSendRequest.TextDomain text = new OapiRobotSendRequest.TextDomain();14             text.Content = " @" + userId + " \n  " + textcontent;15             request.Text_ = text;16             OapiRobotSendRequest.AtDomain at = new OapiRobotSendRequest.AtDomain();17 18             List<string> userids = new List<string>();19             userids.Add(userId);20             at.AtUserIds = userids;21             //           isAtAll类型如果不为Boolean,请升级至最新SDK22             at.IsAtAll = false;23             request.At_ = at;24             OapiRobotSendResponse response = client.Execute(request);25             int code = Convert.ToInt32(response.Errcode);26             string msg = response.Errmsg;27         }28         catch (Exception e)29         {30 31         }32     }33 34     /**35     * markdown@人员效果36     *37     * @param client38     * @param userId39      * 40      * 返回markdown41      * 42     */43     private void markdown(DefaultDingTalkClient client, String userId, string title, string textcontent)44     {45         try46         {47             OapiRobotSendRequest request = new OapiRobotSendRequest();48             request.Msgtype = "markdown";49             OapiRobotSendRequest.MarkdownDomain markdown = new OapiRobotSendRequest.MarkdownDomain();50             markdown.Title = title;51             markdown.Text = " @" + userId + "  \n  " + textcontent;52             request.Markdown_ = markdown;53             OapiRobotSendRequest.AtDomain at = new OapiRobotSendRequest.AtDomain();54             List<string> userids = new List<string>();55             userids.Add(userId);56             at.AtUserIds = userids;57          58             at.IsAtAll = false;59             request.At_ = at;60             OapiRobotSendResponse response = client.Execute(request);61             int code = Convert.ToInt32(response.Errcode);62             string msg = response.Errmsg;63         }64         catch (Exception e)65         {66 67         }68     }69     /**70        * actionCard@人员效果71        * @param client72        * @param userId73        */74     private void actionCard(DefaultDingTalkClient client, String userId, string title, string textcontent, string SingleTitle, string url)75     {76         try77         {78             OapiRobotSendRequest request = new OapiRobotSendRequest();79             request.Msgtype = "actionCard";80             OapiRobotSendRequest.ActioncardDomain actionCard = new OapiRobotSendRequest.ActioncardDomain();81             actionCard.Title = title;82             actionCard.Text = " @" + userId + "  \n  " + textcontent;83             ;84             actionCard.SingleTitle = SingleTitle;85             actionCard.SingleURL = url;86             request.ActionCard_ = actionCard;87             OapiRobotSendRequest.AtDomain at = new OapiRobotSendRequest.AtDomain();88             List<string> userids = new List<string>();89             userids.Add(userId);90             at.AtUserIds = userids;91  92             at.IsAtAll = false;93             request.At_ = at;94             OapiRobotSendResponse response = client.Execute(request);95             int code = Convert.ToInt32(response.Errcode);96             string msg = response.Errmsg;97         }98         catch (Exception e)99         {
100
101         }
102     }

文档中还提到有几种markdown 的用法,分别是标题,引用,字体,链接,图片,有序列表,无序列表的使用,从他的案例中可以看出,只是传入的text加上特殊符号即可

标题
# 一级标题
## 二级标题
### 三级标题
#### 四级标题
##### 五级标题
###### 六级标题引用
> A man who stands for nothing will fall for anything.文字加粗、斜体
**bold**
*italic*链接
[this is a link](https://www.dingtalk.com/)图片
![](http://name.com/pic.jpg)无序列表
- item1
- item2有序列表
1. item1
2. item2换行(建议\n前后各添加两个空格)\n

至此,开发钉钉群机器人的所有开发过程写完了。  

使用asp.net开发钉钉群机器人全过程相关推荐

  1. 使用禅道或Jira系统对接钉钉的群机器人消息管理,为什么没有艾特 @人呢?

    首先,这个功能的方法如下: 禅道系统的Bug动态,对接钉钉软件,实时进行钉钉群内提醒,机器人并@ 艾特指派的开发人员. https://blog.csdn.net/woshiyigerenlaide/ ...

  2. 钉钉群机器人定时发送消息并@所有人

    1.添加钉钉自定义群机器人 参考文章如下: 官方网址:https://open-doc.dingtalk.com/docs/doc.htm?spm=a219a.7629140.0.0.p2lr6t&a ...

  3. golang 钉钉群机器人

    目录 1. 钉钉SDK 2. 代码示例 3. 消息类型 3.1 text类型 3.2 link类型 3.3 markdown类型 3.4 整体跳转ActionCard类型 3.5 独立跳转Action ...

  4. 利用PHP实现钉钉群机器人的webhook自定义通知

    前言 这阵子除了写PHP, 还在写C#的socket服务器端, 第一次写软件, 所以bug总是特别的多. 放在远程服务器上, 说不准什么时候软件就出异常了. 于是在PHP端写了个监测程序, 如果服务器 ...

  5. python企业微信群聊_给企业微信加个群机器人

    现在很多企业在使用企业微信或钉钉进行工作交流,我们可以在群里添加一个自定义群机器人,定时发送一些提醒或咨询信息,它可以作为一个小组手,也为工作增加一点乐趣. 群机器人 下面是企业微信和钉钉的群机器人文 ...

  6. python + ldap +jira 发送 钉钉@艾特人

    jira内容变更后,发送钉钉到群,能@艾特对应的指定人提醒. 前提依赖:openldap:python3:ldap3:Django 关于部署openLDAP请观看上一篇文章:https://blog. ...

  7. 机器人聊天软件c#_使用python3.7配置开发钉钉群自定义机器人(2020年新版攻略)

    原文转载自「刘悦的技术博客」https://v3u.cn/a_id_132 最近疫情比较严重,很多公司依靠阿里旗下的办公软件钉钉来进行远程办公,当然了,钉钉这个产品真的是让人一言难尽,要多难用有多难用 ...

  8. Python 3 开发钉钉群机器人

    转载自「刘悦的技术博客」 链接: v3u.cn/a_id_132 最近疫情比较严重,很多公司依靠阿里旗下的办公软件钉钉来进行远程办公,当然了,钉钉这个产品真的是让人一言难尽,要多难用有多难用,真的让人 ...

  9. Asp.Net Core对接钉钉群机器人

    钉钉作为企业办公越来越常用的软件,对于企业内部自研系统提供接口支持,以此来打通多平台下的数据,本次先使用最简单的钉钉群机器人完成多种形式的消息推送,参考钉钉开发文档中自定义机器人环节,此次尝试所花的时 ...

最新文章

  1. win 复制linux文件命令行,windows与Linux间远程拷贝文件(pscp命令)
  2. python中用来占位的语句是_python占位语句
  3. 赞!《Python面试大全》PDF版来啦!
  4. 智慧显示:5G时代的新机遇
  5. 【Python科学计算系列】矩阵
  6. java时间格式转js_使用jquery或java脚本将日期时间转换为rfc3339格式
  7. nginx epoll详解
  8. Bitcoin 0.7.0 发布, P2P网络的匿名数字货币
  9. 20190903每日一句
  10. c语言程序设计mooc作业平台答案,C语言程序设计下mooc答案.docx
  11. qt 禁止alt+f4_禁止上下关闭按钮和Alt + F4
  12. 树莓派(raspberry pi)学习12: 树莓派创意无限,无所不能
  13. 明明可以靠脸吃饭偏要靠才华_你身边有女神程序员吗?
  14. SpringCache-redis缓存学习记录
  15. 程序员裸辞三个月,终于拿到大厂offer!网友:不应该!
  16. Keil 编译前后 自动将 hex 转 bin
  17. mysql格式化日期和时间
  18. MariaDB Galera Cluster 集群部署
  19. 【MySQL】逻辑库与数据表相关操作
  20. 笨方法学python练习7.更多打印

热门文章

  1. IDE集成开发环境介绍与安装
  2. 机器人卫士密码箱密码忘了怎么办_机器人卫士小学信息技术教案
  3. mysql查询数据库心得_Mysql数据库学习心得
  4. HTML5网页设计样式-CSS设置文字样式
  5. 不入耳运动耳机哪个品牌好、非入耳式运动耳机推荐
  6. vue+node.js+moogodb仿王者荣耀移动端项目一
  7. 关于梦想是计算机的作文英语,关于梦想的英语作文(精选11篇)
  8. 35岁的程序员,该给领导送礼了!
  9. mysql分库分表(一)
  10. 编写一个打印开往火星的车票价格清单列表