diff --git a/dp2-v3.sln b/dp2-v3.sln index 3f036df5..042eb21f 100644 --- a/dp2-v3.sln +++ b/dp2-v3.sln @@ -25,10 +25,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DigitalPlatform.IO", "Digit EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DigitalPlatform.LibraryRestClient", "DigitalPlatform.LibraryRestClient\DigitalPlatform.LibraryRestClient.csproj", "{1B74D69A-530F-4275-845B-8CA6D1463BDD}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dp2Command.Service", "dp2Command.Server\dp2Command.Service.csproj", "{A77B8A4A-3AE8-4BD7-B09A-BF8D92FE802D}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dp2weixin", "dp2weixin\dp2weixin.csproj", "{2BD6F79E-A776-4A08-B972-C61F70F78BD5}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ilovelibrary", "ilovelibrary\ilovelibrary.csproj", "{7C31EA17-404D-4CE6-8401-71808BEB5883}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ilovelibrary.Server", "ilovelibrary.Server\ilovelibrary.Server.csproj", "{159E08AE-5C0F-4DA7-9F73-AF07791247CE}" @@ -47,7 +43,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DigitalPlatform.LibraryClie EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DigitalPlatform.ServiceProcess", "DigitalPlatform.ServiceProcess\DigitalPlatform.ServiceProcess.csproj", "{696B2B7F-D5F6-47AE-8113-C0CD27FAD21E}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dp2weixinP2P", "dp2weixinP2P\dp2weixinP2P.csproj", "{95CB7353-995B-4B82-8DFF-41C0133FFF78}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dp2weixinWeb", "dp2weixinP2P\dp2weixinWeb.csproj", "{95CB7353-995B-4B82-8DFF-41C0133FFF78}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dp2weixin.service", "dp2weixin.service\dp2weixin.service.csproj", "{50E36C13-A3DE-4638-BE87-E5E254EEC386}" EndProject @@ -135,18 +131,6 @@ Global {1B74D69A-530F-4275-845B-8CA6D1463BDD}.Release|Any CPU.ActiveCfg = Release|Any CPU {1B74D69A-530F-4275-845B-8CA6D1463BDD}.Release|Any CPU.Build.0 = Release|Any CPU {1B74D69A-530F-4275-845B-8CA6D1463BDD}.Release|x86.ActiveCfg = Release|Any CPU - {A77B8A4A-3AE8-4BD7-B09A-BF8D92FE802D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A77B8A4A-3AE8-4BD7-B09A-BF8D92FE802D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A77B8A4A-3AE8-4BD7-B09A-BF8D92FE802D}.Debug|x86.ActiveCfg = Debug|Any CPU - {A77B8A4A-3AE8-4BD7-B09A-BF8D92FE802D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A77B8A4A-3AE8-4BD7-B09A-BF8D92FE802D}.Release|Any CPU.Build.0 = Release|Any CPU - {A77B8A4A-3AE8-4BD7-B09A-BF8D92FE802D}.Release|x86.ActiveCfg = Release|Any CPU - {2BD6F79E-A776-4A08-B972-C61F70F78BD5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2BD6F79E-A776-4A08-B972-C61F70F78BD5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2BD6F79E-A776-4A08-B972-C61F70F78BD5}.Debug|x86.ActiveCfg = Debug|Any CPU - {2BD6F79E-A776-4A08-B972-C61F70F78BD5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2BD6F79E-A776-4A08-B972-C61F70F78BD5}.Release|Any CPU.Build.0 = Release|Any CPU - {2BD6F79E-A776-4A08-B972-C61F70F78BD5}.Release|x86.ActiveCfg = Release|Any CPU {7C31EA17-404D-4CE6-8401-71808BEB5883}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7C31EA17-404D-4CE6-8401-71808BEB5883}.Debug|Any CPU.Build.0 = Debug|Any CPU {7C31EA17-404D-4CE6-8401-71808BEB5883}.Debug|x86.ActiveCfg = Debug|Any CPU diff --git a/dp2Command.Server/dp2BaseCommandService.cs b/dp2Command.Server/dp2BaseCommandService.cs index e07ec736..dc6da58c 100644 --- a/dp2Command.Server/dp2BaseCommandService.cs +++ b/dp2Command.Server/dp2BaseCommandService.cs @@ -160,7 +160,7 @@ public virtual int GetMyInfo(string remoteUserName, /// /// /// - public string GetContactString(XmlDocument dom) + public static string GetContactString(XmlDocument dom) { string strTel = DomUtil.GetElementText(dom.DocumentElement, "tel"); string strEmail = DomUtil.GetElementText(dom.DocumentElement, "email"); diff --git a/dp2weixin.service/ApiResult.cs b/dp2weixin.service/ApiResult.cs new file mode 100644 index 00000000..77dce5b1 --- /dev/null +++ b/dp2weixin.service/ApiResult.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace dp2weixin.service +{ + // API返回结果 + public class ApiResult + { + public string errorInfo = ""; + + /// + /// -1:表示出错 + /// + public int errorCode = 0; + } + + public class WxUserResult + { + public WxUserItem userItem { get; set; } + public ApiResult apiResult { get; set; } + } +} diff --git a/dp2weixin.service/Command/BaseCommand.cs b/dp2weixin.service/Command/BaseCommand.cs new file mode 100644 index 00000000..8e7851c6 --- /dev/null +++ b/dp2weixin.service/Command/BaseCommand.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace dp2Command.Service +{ + public class BaseCommand + { + public string CommandName { get;set;} + } +} diff --git a/dp2weixin.service/Command/BindingCommand.cs b/dp2weixin.service/Command/BindingCommand.cs new file mode 100644 index 00000000..d9d8a096 --- /dev/null +++ b/dp2weixin.service/Command/BindingCommand.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace dp2Command.Service +{ + public class BindingCommand:BaseCommand + { + public string ReaderBarcode = ""; + public string Password = ""; + } +} diff --git a/dp2weixin.service/Command/CommandContainer.cs b/dp2weixin.service/Command/CommandContainer.cs new file mode 100644 index 00000000..57f9ca1e --- /dev/null +++ b/dp2weixin.service/Command/CommandContainer.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace dp2Command.Service +{ + public class CommandContainer + { + public Dictionary CmdDict = null; + + public CommandContainer() + { + CmdDict = new Dictionary(); + } + + public BaseCommand GetCommand(string cmdName) + { + if (CmdDict.ContainsKey(cmdName)) + { + return CmdDict[cmdName]; + } + + // 不存在自动创建 + BaseCommand command = null; + if (cmdName == dp2CommandUtility.C_Command_Search) + { + command = new SearchCommand(); + } + else if (cmdName == dp2CommandUtility.C_Command_Binding) + { + command = new BindingCommand(); + } + else + { + command = new BaseCommand(); + } + command.CommandName = cmdName; + CmdDict[cmdName] = command; + return command; + } + + } +} diff --git a/dp2weixin.service/Command/SearchCommand.cs b/dp2weixin.service/Command/SearchCommand.cs new file mode 100644 index 00000000..fbe7b355 --- /dev/null +++ b/dp2weixin.service/Command/SearchCommand.cs @@ -0,0 +1,102 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace dp2Command.Service +{ + public class SearchCommand:BaseCommand + { + + /// + /// 书目检索结果集,存路径 + /// + public List BiblioResultPathList { get; set; } + + /// + /// 是否继续输入n翻页 + /// + public bool IsCanNextPage = false; + + /// + /// 下一步开始序号 + /// + public long ResultNextStart = -1; + + /// + /// 获取下一页检索结果 + /// + /// + /// + /// + public bool GetNextPage(out string strText, + out string strError) + { + strText = ""; + strError = ""; + + if (this.IsCanNextPage == false) + { + strError = "已到末页。"; + return false; + } + + long lTotalCount = this.BiblioResultPathList.Count; + if (this.ResultNextStart >= lTotalCount) + { + strError = "内部错误,下页起始序号>=总记录数了"; + return false; + } + + // 本页显示的最大序号 + long nMaxIndex = this.ResultNextStart + dp2CommandUtility.C_ViewCount_OnePage; + if (nMaxIndex > lTotalCount) + { + nMaxIndex = lTotalCount; + } + + string strPreMessage = ""; + if (nMaxIndex < dp2CommandUtility.C_ViewCount_OnePage + || (this.ResultNextStart == 0 && nMaxIndex == lTotalCount)) + { + // 没有下页了 + this.IsCanNextPage = false; + strPreMessage = "命中'" + lTotalCount + "'条书目记录。您可以回复序列查看详细信息。\r\n"; + } + else if (nMaxIndex < lTotalCount) + { + // 有下页 + this.IsCanNextPage = true; + strPreMessage = "命中'" + lTotalCount + "'条书目记录。本次显示第" + (this.ResultNextStart + 1).ToString() + "-" + nMaxIndex + "条,您可以回复N继续显示下一页,或者回复序列查看详细信息。\r\n"; + } + else if (nMaxIndex == lTotalCount) + { + //无下页 + this.IsCanNextPage = false; + strPreMessage = "命中'" + lTotalCount + "'条书目记录。本次显示第" + (this.ResultNextStart + 1).ToString() + "-" + nMaxIndex + "条,已到末页。您可以回复序列查看详细信息。\r\n"; + } + + string strBrowse = ""; + for (long i = this.ResultNextStart; i < nMaxIndex; i++) + { + if (strBrowse != "") + strBrowse += "\n"; + + string text = this.BiblioResultPathList[(int)i]; + int index = text.IndexOf("*"); + if (index >= 0) + text = text.Substring(index + 1); + strBrowse += (i + 1).ToString().PadRight(5, ' ') + text; + } + + // 设置下页索引 + this.ResultNextStart = nMaxIndex; + + //返回结果 + strText = strPreMessage + strBrowse; + + return true; + } + } +} diff --git a/dp2weixin.service/Command/dp2CommandUtility.cs b/dp2weixin.service/Command/dp2CommandUtility.cs new file mode 100644 index 00000000..c9560a27 --- /dev/null +++ b/dp2weixin.service/Command/dp2CommandUtility.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; + +namespace dp2Command.Service +{ + public class dp2CommandUtility + { + // 2016/2/20 选择图书馆 + public const string C_Command_SelectLib = "selectlib"; + // 切换读者,用于微信用户绑定多个读者的情况 + public const string C_Command_ChangePatron = "changepatron"; + + + public const string C_Command_Binding = "binding"; + public const string C_Command_Unbinding = "unbinding"; + public const string C_Command_MyInfo = "myinfo"; + public const string C_Command_BorrowInfo = "borrowinfo"; + public const string C_Command_Renew = "renew"; + public const string C_Command_Search = "search"; + public const string C_Command_SearchDetail = "search-detail"; + // 公共信息 + public const string C_Command_BookRecommend = "bookrecommend"; + public const string C_Command_Notice = "notice";//Notice + // 检索每页显示记录数 + public const int C_ViewCount_OnePage = 20; + + + public const String C_WeiXinIdPrefix = "weixinid:"; + + /// + /// 校验字符串是否是命令 + /// + /// + /// + public static bool CheckIsCommand(string strText) + { + strText = strText.ToLower(); + if (strText == C_Command_Search + || strText == C_Command_Binding + || strText == C_Command_Unbinding + || strText == C_Command_MyInfo + || strText == C_Command_BorrowInfo + || strText == C_Command_Renew + || strText == C_Command_BookRecommend + || strText == C_Command_Notice + || strText == C_Command_SelectLib + || strText == C_Command_ChangePatron + ) + { + return true; + } + + return false; + } + } +} \ No newline at end of file diff --git a/dp2weixin.service/Interface/MessageInterface.cs b/dp2weixin.service/Interface/MessageInterface.cs new file mode 100644 index 00000000..aff8574b --- /dev/null +++ b/dp2weixin.service/Interface/MessageInterface.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace DigitalPlatform.Interfaces +{ + // 一个扩展消息接口 + public class MessageInterface + { + public string Type = ""; + public Assembly Assembly = null; + public ExternalMessageHost HostObj = null; + } +} diff --git a/dp2weixin.service/Interface/ScriptManager.cs b/dp2weixin.service/Interface/ScriptManager.cs new file mode 100644 index 00000000..f717ccda --- /dev/null +++ b/dp2weixin.service/Interface/ScriptManager.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace DigitalPlatform.Interfaces +{ + public class ScriptManager + { + // 获得从指定类或者接口派生的类 + public static Type GetDerivedClassType(Assembly assembly, + string strBaseTypeFullName) + { + if (assembly == null) + return null; + + Type[] types = assembly.GetTypes(); + foreach (Type type in types) + { + if (type.IsClass == false) + continue; + + // 2015/5/28 + Type[] interfaces = type.GetInterfaces(); + foreach (Type inter in interfaces) + { + if (inter.FullName == strBaseTypeFullName) + return type; + } + + if (IsDerivedFrom(type, + strBaseTypeFullName) == true) + return type; + } + + return null; + } + + // 观察type的基类中是否有类名为strBaseTypeFullName的类。 + public static bool IsDerivedFrom(Type type, + string strBaseTypeFullName) + { + Type curType = type; + for (; ; ) + { + if (curType == null + || curType.FullName == "System.Object") + return false; + + if (curType.FullName == strBaseTypeFullName) + return true; + + curType = curType.BaseType; + } + + } + } +} diff --git a/dp2weixin.service/LibDatabase.cs b/dp2weixin.service/LibDatabase.cs new file mode 100644 index 00000000..807a3344 --- /dev/null +++ b/dp2weixin.service/LibDatabase.cs @@ -0,0 +1,289 @@ +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace dp2weixin.service +{ + + + /// + /// 用户数据库 + /// + public sealed class LibDatabase + { + // 饿汉模式 + private static readonly LibDatabase repo = new LibDatabase(); + public static LibDatabase Current + { + get + { + return repo; + } + } + + + MongoClient _mongoClient = null; + IMongoDatabase _database = null; + string _libDbName = ""; + + IMongoCollection _libCollection = null; + public IMongoCollection LibCollection + { + get + { + return this._libCollection; + } + } + + // 初始化 + public void Open( + string strMongoDbConnStr, + string strInstancePrefix) + { + if (string.IsNullOrEmpty(strMongoDbConnStr) == true) + throw new ArgumentException("strMongoDbConnStr 参数值不应为空"); + + if (string.IsNullOrEmpty(strInstancePrefix) == false) + strInstancePrefix = strInstancePrefix + "_"; + _libDbName = strInstancePrefix + "lib"; + + this._mongoClient = new MongoClient(strMongoDbConnStr); + this._database = this._mongoClient.GetDatabase(this._libDbName); + + //图书馆点对点账号 + _libCollection = this._database.GetCollection("item"); + + /* + // todo 创建索引 + bool bExist = false; + var indexes = _libCollection.Indexes.ListAsync().Result.ToListAsync().Result; + foreach (BsonDocument doc in indexes) + { + } + // _logCollection.DropAllIndexes(); + if (bExist == false) + { + //await CreateIndex(); + } + */ + } + + // 创建索引 + public async Task CreateIndex() + { + /* + var options = new CreateIndexOptions() { Unique = true }; + await _libCollection.Indexes.CreateOneAsync( + Builders.IndexKeys.Ascending("libCode"), + options); + */ + } + + // 清除集合内的全部内容 + public async Task Clear() + { + if (_libCollection == null) + { + throw new Exception("访问日志 mongodb 集合尚未初始化"); + } + + // https://docs.mongodb.org/getting-started/csharp/remove/ + var filter = new BsonDocument(); + await _libCollection.DeleteManyAsync(filter); + //await CreateIndex(); + } + + public LibItem GetLibById(string id) + { + var filter = Builders.Filter.Eq("id", id); + + var list = this.LibCollection.Find(new BsonDocument("id", id)).ToListAsync().Result; + if (list.Count > 0) + return list[0]; + + return null; + } + public LibItem GetLibByLibCode(string libCode) + { + var filter = Builders.Filter.Eq("libCode", libCode); + List list = this.LibCollection.Find(filter).ToList(); + if (list.Count > 0) + return list[0]; + + return null; + } + + public List GetLibs() + { + return this.LibCollection.Find(new BsonDocument()).ToListAsync().Result; + } + + /// + /// 根据libCode获得图书馆对象 + /// + /// *获取全部 + /// 0 + /// -1 + /// + public async Task> GetLibs(string libCode, + int start, + int count) + { + IMongoCollection collection = this.LibCollection; + + List results = new List(); + + var filter = Builders.Filter.Eq("libCode", libCode); + var index = 0; + using (var cursor = await collection.FindAsync( + libCode == "*" ? new BsonDocument() : filter + )) + { + while (await cursor.MoveNextAsync()) + { + var batch = cursor.Current; + foreach (var document in batch) + { + if (count != -1 && index - start >= count) + break; + if (index >= start) + results.Add(document); + index++; + } + } + } + return results; + } + + public LibItem Add(LibItem item) + { + item.OperTime = DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss"); + + this.LibCollection.InsertOne(item); + + return item; + } + + // 更新 + public async Task Update(LibItem item) + { + IMongoCollection collection = this.LibCollection; + + var filter = Builders.Filter.Eq("id", item.id); + //var filter = Builders.Filter.Eq("libCode", item.libCode); + var update = Builders.Update + .Set("libCode", item.libCode) + .Set("libName", item.libName) + .Set("libP2PAccount", item.libUserName) + .Set("comment", item.comment) + .Set("OperTime", item.OperTime); + + UpdateResult ret = await collection.UpdateOneAsync(filter, update); + return ret.ModifiedCount; + } + + + /// + /// 删除 + /// + /// + public void Delete(String id) + { + IMongoCollection collection = this.LibCollection; + + var filter = Builders.Filter.Eq("id", id); + //var filter = Builders.Filter.Eq("libCode", item.libCode); + + collection.DeleteOne(filter); + } + + } + + public class LibItem + { + [BsonId] + [BsonRepresentation(BsonType.ObjectId)] + public string id { get; private set; } + + public string libCode { get; set; } + public string libName { get; set; } + public string libUserName { get; set; } + + + public string comment { get; set; } // 注释 + + public string OperTime { get; set; } // 操作时间 + + } + /* + public class LibraryRespository + { + private static LibraryRespository repo = new LibraryRespository(); + + public static LibraryRespository Current + { + get + { + return repo; + } + } + + private List data = new List { + new LibItem { + id="001", libCode = "lib1", libName = "图书馆1", libP2PAccount = "a1"}, + new LibItem { + id="002",libCode = "lib2", libName = "图书馆2", libP2PAccount = "a2"}, + new LibItem { + id="003",libCode = "lib3", libName = "图书馆3", libP2PAccount = "a3"}, + }; + + public IEnumerable GetAll() + { + return data; + } + + public LibItem Get(string libId) + { + return data.Where(r => r.id == libId).FirstOrDefault(); + } + + public LibItem Add(LibItem item) + { + // id取guid todo + item.id = Guid.NewGuid().ToString(); + + data.Add(item); + return item; + } + + public void Remove(string libId) + { + LibItem item = Get(libId); + if (item != null) + { + data.Remove(item); + } + } + + public bool Update(LibItem item) + { + LibItem storedItem = Get(item.libCode); + if (storedItem != null) + { + storedItem.libName = item.libName; + storedItem.libP2PAccount = item.libP2PAccount; + return true; + } + else + { + return false; + } + } + } + */ +} diff --git a/dp2weixin.service/MsgRouter.cs b/dp2weixin.service/MsgRouter.cs new file mode 100644 index 00000000..23051bc3 --- /dev/null +++ b/dp2weixin.service/MsgRouter.cs @@ -0,0 +1,288 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Collections; + +using DigitalPlatform; +using DigitalPlatform.MessageClient; +using DigitalPlatform.Message; + +namespace dp2weixin.service +{ + public class MsgRouter : ThreadBase + { + public event SendMessageEventHandler SendMessageEvent = null; + + // 这里是引用外部的对象,不负责创建和销毁 + public MessageConnectionCollection Channels = null; + + public string Url { get; set; } + public string GroupName { get; set; } + + // 存储从 AddMessage() 得到的消息 + List _messageList = new List(); + private static readonly Object _syncRoot_messageList = new Object(); + + // 记忆已经发送过的消息,避免重复发送 + Hashtable _sendedTable = new Hashtable(); + + public MsgRouter() + { + this.PerTime = 60 * 1000; // 60 * 1000 + } + + public void Start(MessageConnectionCollection channels, + string url, + string groupName) + { + this.Url = url; + this.GroupName = groupName; + this.Channels = channels; + + //Channels.Login += _channels_Login; + Channels.AddMessage -= _channels_AddMessage; + Channels.AddMessage += _channels_AddMessage; + + Channels.ConnectionStateChange -= _channels_ConnectionStateChange; + Channels.ConnectionStateChange += _channels_ConnectionStateChange; + + this.BeginThread(); + } + + public void Stop() + { + Channels.AddMessage -= _channels_AddMessage; + Channels.ConnectionStateChange -= _channels_ConnectionStateChange; + + this.StopThread(false); + } + + void _channels_ConnectionStateChange(object sender, ConnectionEventArgs e) + { + if (e.Action == "Reconnected" + || e.Action == "Connected") + { + this.Activate();//激活线程 + } + } + + void _channels_AddMessage(object sender, AddMessageEventArgs e) + { + if (e.Action != "create") + return; + + lock (_syncRoot_messageList) + { + // 累积太多了就不送入 list 了,只是激活线程等 GetMessage() 慢慢一百条地处理 + if (this._messageList.Count < 10000) + this._messageList.AddRange(e.Records); + } + this.Activate(); + } + + // 工作线程每一轮循环的实质性工作 + public override void Worker() + { + //this.WriteLog("走到worker1"); + List records = GetMessage(); + if (records.Count > 0) + { + lock (_syncRoot_messageList) + { + this._messageList.AddRange(records); + } + } + //this.WriteErrorLog("走到worker2:" +records.Count); + + if (this._messageList.Count > 0) + { + // 取出前面 100 个加以处理 + // 这样锁定的时间很短 + List temp_records = new List(); + lock (_syncRoot_messageList) + { + int i = 0; + foreach(MessageRecord record in this._messageList) + { + if (i >= 100) + break; + temp_records.Add(record); + i++; + } + this._messageList.RemoveRange(0, temp_records.Count); + } + + //this.WriteErrorLog("走到worker3:" + temp_records.Count); + + // 发送消息给下游模块 + SendMessage(temp_records); + + //this.WriteErrorLog("走到worker4:"); + + // 从 dp2mserver 中删除这些消息 + DeleteMessage(temp_records, this.GroupName); + + //this.WriteErrorLog("走到worker5:"); + } + + // 如果本轮主动获得过消息,就要连续激活线程,让线程下次继续处理。只有本轮发现没有新消息了,才会进入休眠期 + if (records.Count > 0) + this.Activate(); + + CleanSendedTable(); // TODO: 可以改进为判断间隔至少 5 分钟才做一次 + } + + // 将消息发送给下游模块 + void SendMessage(List records) + { + SendMessageEventHandler handler = this.SendMessageEvent; + + foreach (MessageRecord record in records) + { + if (this._sendedTable.ContainsKey(record.id)) + continue; + + this.WriteLog("开始处理:" + record.id); + + // 发送 + if (handler != null) + { + SendMessageEventArgs e = new SendMessageEventArgs(); + e.Message = record; + handler(this, e); + } + + this.WriteLog("处理结束:" + record.id); + + this._sendedTable[record.id] = DateTime.Now; + } + } + + // 清理超过一定时间的“已发送”记忆事项 + void CleanSendedTable() + { + DateTime now = DateTime.Now; + TimeSpan delta = new TimeSpan(0, 30, 0); + List delete_keys = new List(); + foreach (string key in this._sendedTable.Keys) + { + var time = (DateTime)this._sendedTable[key]; + if (time - now > delta) + delete_keys.Add(key); + } + + foreach (string key in delete_keys) + { + this._sendedTable.Remove(key); + } + } + + void WriteLog(string strText) + { + dp2WeiXinService.Instance.WriteLog(strText); + //MessageRecord record = new MessageRecord(); + //record.data = "*** error *** " + strText; + //SendMessageEventArgs e = new SendMessageEventArgs(); + //e.Message = record; + //this.SendMessageEvent(this, e); + } + + // 从 dp2mserver 获得消息 + // 每次最多获得 100 条 + List GetMessage() + { + string strError = ""; + CancellationToken cancel_token = new CancellationToken(); + + string id = Guid.NewGuid().ToString(); + GetMessageRequest request = new GetMessageRequest(id, + "", + this.GroupName, // "" 表示默认群组 + "", + "", // strTimeRange, + 0, + 100); + try + { + MessageConnection connection = this.Channels.GetConnectionAsync( + this.Url, + "").Result; + GetMessageResult result = connection.GetMessageAsync( + request, + new TimeSpan(0, 1, 0), + cancel_token).Result; + if (result.Value == -1) + goto ERROR1; + return result.Results; + } + catch (AggregateException ex) + { + strError = MessageConnection.GetExceptionText(ex); + goto ERROR1; + } + catch (Exception ex) + { + strError = ex.Message; + goto ERROR1; + } + ERROR1: + this.WriteLog("GetMessage() error: " + strError); + return new List(); + } + + void DeleteMessage(List records, + string strGroupName) + { + List delete_records = new List(); + + foreach (MessageRecord source in records) + { + MessageRecord record = new MessageRecord(); + record.groups = strGroupName.Split(new char[] { ',' }); + record.id = source.id; + delete_records.Add(record); + } + + string strError = ""; + + // CancellationToken cancel_token = new CancellationToken(); + + try + { + MessageConnection connection = this.Channels.GetConnectionAsync( + this.Url, + "").Result; + SetMessageRequest param = new SetMessageRequest("expire", + "dontNotifyMe", + records); + + SetMessageResult result = connection.SetMessageAsync(param).Result; + if (result.Value == -1) + goto ERROR1; + } + catch (AggregateException ex) + { + strError = MessageConnection.GetExceptionText(ex); + goto ERROR1; + } + catch (Exception ex) + { + strError = ex.Message; + goto ERROR1; + } + return; + ERROR1: + this.WriteLog("DeleteMessage() error : " + strError); + } + } + + public delegate void SendMessageEventHandler(object sender, + SendMessageEventArgs e); + + public class SendMessageEventArgs : EventArgs + { + public MessageRecord Message = null; + } +} diff --git a/dp2weixin.service/Patron.cs b/dp2weixin.service/Patron.cs index 89490569..e1828c55 100644 --- a/dp2weixin.service/Patron.cs +++ b/dp2weixin.service/Patron.cs @@ -12,7 +12,7 @@ public class PatronInfo public Patron patron { get; set; } // 在借册 - public List borrowList { get; set; } + public List borrowList { get; set; } // 违约/交费信息 public List overdueList { get; set; } @@ -79,7 +79,7 @@ public class OverdueInfo } - public class BorrowInfo + public class BorrowInfo2 { public string barcode { get; set; } public string renewNo { get; set; } diff --git a/dp2weixin.service/SearchBiblioResult.cs b/dp2weixin.service/SearchBiblioResult.cs new file mode 100644 index 00000000..b1a4cf7b --- /dev/null +++ b/dp2weixin.service/SearchBiblioResult.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace dp2weixin.service +{ + public class SearchBiblioResult + { + public ApiResult apiResult { get; set; } + + // 在借册 + public List records { get; set; } + + public long resultCount = 0; + + public bool isCanNext { get; set; } + } + + public class BiblioRecord + { + public string no = ""; + public string recPath = ""; + public string name = ""; + public string libUserName = ""; + } + + public class BiblioRecordResult : ApiResult + { + public string biblioPath { get; set; } + + public string summary { get; set; } + public List itemList { get; set; } + } + + public class BiblioItem + { + /* +册条码 +状态 +卷册 +馆藏地 +价格 + +在借情况 +册记录路径 + */ + + public string barcode { get; set; } + public string state { get; set; } + public string volumn { get; set; } + public string location { get; set; } + public string price { get; set; } + + // 索引号 + public string accessNo { get; set; } + // 出版日期 + public string publishTime { get; set; } + public string borrowInfo { get; set; } + // 备注 + public string comment { get; set; } + + } +} diff --git a/dp2weixin.service/TemplateData.cs b/dp2weixin.service/TemplateData.cs new file mode 100644 index 00000000..7b17dcd0 --- /dev/null +++ b/dp2weixin.service/TemplateData.cs @@ -0,0 +1,178 @@ +using Senparc.Weixin.MP.AdvancedAPIs.TemplateMessage; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace dp2weixin.service +{ +//{{first.DATA}} +//图书书名:{{keyword1.DATA}} +//册条码号:{{keyword2.DATA}} +//借阅日期:{{keyword3.DATA}} +//借阅期限:{{keyword4.DATA}} +//应还日期:{{keyword5.DATA}} +//{{remark.DATA}} + +//尊敬的XXX,恭喜您借书成功。 +//图书书名:C#开发教程 +//册条码号:C0000001 +//借阅日期:2016-5-27 +//借阅期限:31 +//应还日期:2016-6-27 +//祝您阅读愉快,欢迎再借。 + public class BorrowTemplateData + { + public TemplateDataItem first { get; set; } + public TemplateDataItem keyword1 { get; set; } + public TemplateDataItem keyword2 { get; set; } + public TemplateDataItem keyword3 { get; set; } + public TemplateDataItem keyword4 { get; set; } + public TemplateDataItem keyword5 { get; set; } + public TemplateDataItem remark { get; set; } + } + + +//{{first.DATA}} +//书名:{{keyword1.DATA}} +//归还时间:{{keyword2.DATA}} +//借阅人:{{keyword3.DATA}} +//{{remark.DATA}} +//您好,你借阅的图书已确认归还. +//书名:算法导论 +//归还时间:2015-10-10 12:14 +//借阅人:李明 +//欢迎继续借书! + public class ReturnTemplateData + { + public TemplateDataItem first { get; set; } + public TemplateDataItem keyword1 { get; set; } + public TemplateDataItem keyword2 { get; set; } + public TemplateDataItem keyword3 { get; set; } + public TemplateDataItem remark { get; set; } + } + + + +//{{first.DATA}} +//订单号:{{keyword1.DATA}} +//缴费人:{{keyword2.DATA}} +//缴费金额:{{keyword3.DATA}} +//费用类型:{{keyword4.DATA}} +//缴费时间:{{keyword5.DATA}} +//{{remark.DATA}} +//您好,您已缴费成功! +//订单号:书名(册条码号) +//缴费人:张三 +//缴费金额:¥100.00 +//费用类型:违约 +//缴费时间:2015-12-27 13:15 +//如有疑问,请联系学校管理员,感谢您的使用! + public class PayTemplateData + { + public TemplateDataItem first { get; set; } + public TemplateDataItem keyword1 { get; set; } + public TemplateDataItem keyword2 { get; set; } + public TemplateDataItem keyword3 { get; set; } + public TemplateDataItem keyword4 { get; set; } + public TemplateDataItem keyword5 { get; set; } + public TemplateDataItem remark { get; set; } + } + +//{{first.DATA}} +//退款原因:{{reason.DATA}} +//退款金额:{{refund.DATA}} +//{{remark.DATA}} +//您好,您对微信数据容灾服务的抢购未成功,已退款。 +//退款原因:未抢购成功 +//退款金额:2570元 +//备注:如有疑问,请致电13912345678联系我们,或回复M来了解详情。 + public class ReturnPayTemplateData + { + public TemplateDataItem first { get; set; } + public TemplateDataItem reason { get; set; } + public TemplateDataItem refund { get; set; } + public TemplateDataItem remark { get; set; } + } + + +//{{first.DATA}} +//标题:{{keyword1.DATA}} +//时间:{{keyword2.DATA}} +//内容:{{keyword3.DATA}} +//{{remark.DATA}} +//您好,您有新的消息! +//标题:车辆剩余油量过少 +//时间:2015年8月20日 +//内容:您的车辆剩余测量过少,请注意加油 +//感谢您使用车管家! + public class MessageTemplateData + { + public TemplateDataItem first { get; set; } + public TemplateDataItem keyword1 { get; set; } + public TemplateDataItem keyword2 { get; set; } + public TemplateDataItem keyword3 { get; set; } + public TemplateDataItem remark { get; set; } + } + + +//{{first.DATA}} +//图书书名:{{keyword1.DATA}} +//应还日期:{{keyword2.DATA}} +//超期天数:{{keyword3.DATA}} +//{{remark.DATA}} + public class CaoQiTemplateData + { + public TemplateDataItem first { get; set; } + public TemplateDataItem keyword1 { get; set; } + public TemplateDataItem keyword2 { get; set; } + public TemplateDataItem keyword3 { get; set; } + public TemplateDataItem remark { get; set; } + + } + + //{{first.DATA}} + //图书书名:{{keyword1.DATA}} + //到书日期:{{keyword2.DATA}} + //保留期限:{{keyword3.DATA}} + //{{remark.DATA}} + public class ArrivedTemplateData + { + public TemplateDataItem first { get; set; } + public TemplateDataItem keyword1 { get; set; } + public TemplateDataItem keyword2 { get; set; } + public TemplateDataItem keyword3 { get; set; } + public TemplateDataItem remark { get; set; } + + } + + + //{{first.DATA}} + //绑定帐号:{{keyword1.DATA}} + //绑定说明:{{keyword2.DATA}} + //{{remark.DATA}} + public class BindTemplateData + { + public TemplateDataItem first { get; set; } + public TemplateDataItem keyword1 { get; set; } + public TemplateDataItem keyword2 { get; set; } + public TemplateDataItem remark { get; set; } + + } + + + +//{{first.DATA}} +//解绑帐号:{{keyword1.DATA}} +//解绑说明:{{keyword2.DATA}} +//{{remark.DATA}} + public class UnBindTemplateData + { + public TemplateDataItem first { get; set; } + public TemplateDataItem keyword1 { get; set; } + public TemplateDataItem keyword2 { get; set; } + public TemplateDataItem remark { get; set; } + + } +} diff --git a/dp2weixin.service/WxUserDatabase.cs b/dp2weixin.service/WxUserDatabase.cs new file mode 100644 index 00000000..08a5c53f --- /dev/null +++ b/dp2weixin.service/WxUserDatabase.cs @@ -0,0 +1,354 @@ +using DigitalPlatform.IO; +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace dp2weixin.service +{ + /// + /// 用户数据库 + /// + public sealed class WxUserDatabase + { + private static readonly WxUserDatabase _db = new WxUserDatabase(); + public static WxUserDatabase Current + { + get + { + return _db; + } + } + + MongoClient _mongoClient = null; + IMongoDatabase _database = null; + string _wxUserDbName = ""; + + IMongoCollection _wxUserCollection = null; + public IMongoCollection wxUserCollection + { + get + { + return this._wxUserCollection; + } + } + + // 初始化 + public void Open( + string strMongoDbConnStr, + string strInstancePrefix) + { + if (string.IsNullOrEmpty(strMongoDbConnStr) == true) + throw new ArgumentException("strMongoDbConnStr 参数值不应为空"); + + if (string.IsNullOrEmpty(strInstancePrefix) == false) + strInstancePrefix = strInstancePrefix + "_"; + _wxUserDbName = strInstancePrefix + "user"; + + this._mongoClient = new MongoClient(strMongoDbConnStr); + this._database = this._mongoClient.GetDatabase(this._wxUserDbName); + + //图书馆点对点账号 + _wxUserCollection = this._database.GetCollection("item"); + + // todo 创建索引 + bool bExist = false; + var indexes = _wxUserCollection.Indexes.ListAsync().Result.ToListAsync().Result; + foreach (BsonDocument doc in indexes) + { + } + // _logCollection.DropAllIndexes(); + if (bExist == false) + { + CreateIndex(); + } + + } + + // 创建索引 + public void CreateIndex() + { + var options = new CreateIndexOptions() { Unique = false }; //不唯一,一个微信用户可能对应多个读者 + _wxUserCollection.Indexes.CreateOne( + Builders.IndexKeys.Ascending("weixinId"), + options); + + } + + // 清除集合内的全部内容 + public async Task Clear() + { + if (_wxUserCollection == null) + { + throw new Exception("访问日志 mongodb 集合尚未初始化"); + } + + // https://docs.mongodb.org/getting-started/csharp/remove/ + var filter = new BsonDocument(); + await _wxUserCollection.DeleteManyAsync(filter); + CreateIndex(); + } + + /* + /// + /// 根据微信号与图书馆代码查 + /// + /// + /// + /// + public WxUserItem GetActive(string weixinId,string libCode) + { + var filter = Builders.Filter.Eq("weixinId", weixinId) + & Builders.Filter.Eq("libCode", libCode) + & Builders.Filter.Eq("isActive", 1); + + List list = this.wxUserCollection .Find(filter).ToList(); + if (list.Count > 0) + return list[0]; + + return null; + } + */ + public WxUserItem GetActiveOrFirst(string weixinId, string libCode) + { + // 先查active的 + var filter = Builders.Filter.Eq("weixinId", weixinId) + & Builders.Filter.Eq("libCode", libCode) + & Builders.Filter.Eq("isActive", 1); + + List list = this.wxUserCollection.Find(filter).ToList(); + if (list.Count > 0) + return list[0]; + + // 没有查first + filter = Builders.Filter.Eq("weixinId", weixinId) + & Builders.Filter.Eq("libCode", libCode); + + list = this.wxUserCollection.Find(filter).ToList(); + if (list.Count > 0) + return list[0]; + + return null; + } + + public WxUserItem GetOneOrEmptyPatron(string weixinId, string libCode,string readerBarcode) + { + // 先查到weixinId+libCode+readerBarcode唯一的记录 + var filter = Builders.Filter.Eq("weixinId", weixinId) + & Builders.Filter.Eq("libCode", libCode) + & Builders.Filter.Eq("readerBarcode", readerBarcode); + List list = this.wxUserCollection.Find(filter).ToList(); + if (list.Count >= 1) + return list[0]; + + // 未找到查weixinId+libCode,readerBarcoe为空的记录 + filter = Builders.Filter.Eq("weixinId", weixinId) + & Builders.Filter.Eq("libCode", libCode) + & Builders.Filter.Eq("readerBarcode", ""); + list = this.wxUserCollection.Find(filter).ToList(); + if (list.Count >= 1) + return list[0]; + + + return null; + } + + public WxUserItem GetActive(string weixinId) + { + var filter = Builders.Filter.Eq("weixinId", weixinId) + & Builders.Filter.Eq("isActive", 1); + + List list= this.wxUserCollection.Find(filter).ToList();//.ToListAsync().Result; + if (list.Count > 1) + throw new Exception("程序异常:微信号活动读者数量有" +list.Count+"个"); + + if (list.Count == 1) + return list[0]; + + return null; + } + + public List GetByWeixinId(string weixinId) + { + var filter = Builders.Filter.Eq("weixinId", weixinId); + + return this.wxUserCollection.Find(filter).ToList();//.ToListAsync().Result; + } + + public WxUserItem GetOneByWeixinId(string weixinId) + { + var filter = Builders.Filter.Eq("weixinId", weixinId); + + Listlist= this.wxUserCollection.Find(filter).ToList();//.ToListAsync().Result; + if (list.Count > 0) + return list[0]; + + return null; + } + + /// + /// 查找所有用户 + /// + /// + public List GetUsers() + { + IFindFluent f = this.wxUserCollection.Find(new BsonDocument()); + if (f != null) + return f.ToList(); + + return null; + } + + + + public WxUserItem Add(WxUserItem item) + { + //item.CreateTime = DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss"); + + this.wxUserCollection.InsertOne(item); + + return item; + } + + // 更新 + public long Update(WxUserItem item) + { + IMongoCollection collection = this.wxUserCollection; + + var filter = Builders.Filter.Eq("id", item.id); + var update = Builders.Update + .Set("weixinId", item.weixinId) + .Set("readerBarcode", item.readerBarcode) + .Set("readerName", item.readerName) + .Set("libCode", item.libCode) + .Set("libUserName", item.libUserName) + .Set("libName", item.libName) + .Set("createTime", item.createTime) + .Set("updateTime", item.updateTime) + .Set("xml", item.xml) + .Set("refID", item.refID) + .Set("isActive", item.isActive); + + UpdateResult ret = collection.UpdateOne(filter, update); + return ret.ModifiedCount; + } + + public WxUserItem GetById(String id) + { + if (string.IsNullOrEmpty(id) == true || id == "null") + return null; + IMongoCollection collection = this.wxUserCollection; + var filter = Builders.Filter.Eq("id", id); + List list = this.wxUserCollection.Find(filter).ToList(); + if (list.Count > 0) + { + return list[0]; + } + return null; + } + + + /// + /// 删除 + /// + /// + public void Delete(String id) + { + if (string.IsNullOrEmpty(id) == true || id=="null") + return; + + + + IMongoCollection collection = this.wxUserCollection; + var filter = Builders.Filter.Eq("id", id); + + // 检查一下是否被删除读者是否为默认读者,如果是,把自动将默认值设了第一个读者上。 + List list = this.wxUserCollection.Find(filter).ToList(); + string weixinId = ""; + if (list.Count > 0) + { + weixinId = list[0].weixinId; + } + + // 先删除 + collection.DeleteOne(filter); + + // 自动将第一个设为默认的 + WxUserItem newUserItem = this.GetOneByWeixinId(weixinId); + if (newUserItem != null) + this.SetActive(newUserItem); + } + + public void SetActive(WxUserItem item) + { + this.SetActive(item.weixinId, item.id); + } + + public void SetActive(string weixinId,string id) + { + if (string.IsNullOrEmpty(weixinId) == true || weixinId=="null") + return; + + if (string.IsNullOrEmpty(weixinId) == true || id=="null") + return; + + IMongoCollection collection = this.wxUserCollection; + + // 先将该微信用户的所有绑定读者都设为非活动 + var filter = Builders.Filter.Eq("weixinId", weixinId); + var update = Builders.Update + .Set("isActive", 0) + .Set("updateTime", DateTimeUtil.DateTimeToString(DateTime.Now)); + UpdateResult ret = collection.UpdateMany(filter, update); + + // 再将参数传入的记录设为活动状态 + filter = Builders.Filter.Eq("id",id); + update = Builders.Update + .Set("isActive", 1) + .Set("updateTime", DateTimeUtil.DateTimeToString(DateTime.Now)); + ret = collection.UpdateMany(filter, update); + } + + + + + + } + public class WxUserItem + { + [BsonId] + [BsonRepresentation(BsonType.ObjectId)] + public string id { get; private set; } + + public string weixinId { get; set; } // 绑定必备 + public string readerBarcode { get; set; } + public string readerName { get; set; } + + public string libCode { get; set; } // 绑定必备 + public string libUserName { get; set; }// 绑定必备 + public string libName { get; set; }// 绑定必备 + + public string createTime { get; set; } // 创建时间 + public string updateTime { get; set; } // 更校报时间 + + + public string xml { get; set; } + public string refID { get; set; } + + public int isActive = 0; + + + // 绑定必备 + public string prefix { get; set; } //必须设为属性,才能在前端传值。 + public string word { get; set; } + public string password { get; set; } + + public string fullWord { get; set; } // 服务器用fullWord将strPrefix:strWord存在一起 + } + + +} diff --git a/dp2weixin.service/app.config b/dp2weixin.service/app.config index f3940287..fdc9c16f 100644 --- a/dp2weixin.service/app.config +++ b/dp2weixin.service/app.config @@ -1,19 +1,19 @@ - + - - + + - - + + - - + + - + diff --git a/dp2weixin.service/dp2CmdService2.cs b/dp2weixin.service/dp2CmdService2.cs new file mode 100644 index 00000000..d14fc8ae --- /dev/null +++ b/dp2weixin.service/dp2CmdService2.cs @@ -0,0 +1,3082 @@ +using DigitalPlatform.Interfaces; +using DigitalPlatform.IO; +using DigitalPlatform.Message; +using DigitalPlatform.MessageClient; +using DigitalPlatform.Text; +using DigitalPlatform.Xml; +using Senparc.Weixin; +using Senparc.Weixin.MP.AdvancedAPIs; +using Senparc.Weixin.MP.AdvancedAPIs.TemplateMessage; +using Senparc.Weixin.MP.CommonAPIs; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Xml; + +namespace dp2weixin.service +{ + public class dp2WeiXinService + { + public static string EncryptKey = "dp2weixinPassword"; + public const String C_WeiXinIdPrefix = "weixinid:"; + + // 检索限制最大命中数常量 + public const int C_Search_MaxCount = 200; + public const int C_OnePage_Count = 10; + + // 微信web程序url + public string weiXinUrl = ""; + // 微信目录 + public string weiXinDataDir = ""; + public string weiXinLogDir = ""; + + #region 模板消息 + + //微信绑定通知 + public const string C_Template_Bind = "hFmNH7on2FqSOAiYPZVJN-FcXBv4xpVLBvHsfpLLQKU"; + // 微信解绑通知 overdues + public const string C_Template_UnBind = "1riAKkt2W0AOtkx5rx-Lwa0RKRydDTHaMjSoUBGuHog"; + //预约到书通知 + public const string C_Template_Arrived = "Wm-7-0HJay4yloWEgGG9HXq9eOF5cL8Qm2aAUy-isoM"; + //图书超期提醒 + public const string C_Template_CaoQi = "QcS3LoLHk37Jh0rgKJId2o93IZjulr5XxgshzlW5VkY"; + //图书到期提醒 + public const string C_Template_DaoQi = "Q6O3UFPxPnq0rSz82r9P9be41tqEPaJVPD3U0PU8XOU"; + + //借阅成功通知 + public const string C_Template_Borrow = "_F9kVyDWhunqM5ijvcwm6HwzVCnwbkeZl6GV6awB_fc"; + //图书归还通知 + public const string C_Template_Return = "86Ee0NevuLIVGZE4Xu0uzDdmg0T3xnRMOJ5tREIEG_w"; + //缴费成功通知 + public const string C_Template_Pay = "4HNhEfLcroEMdX0Pr6aFo_n7_aHuvAzD8_6lzABHkiM"; + //退款通知 + public const string C_Template_ReturnPay = "sIzSJJ-VRbFUFrDHszxCqwiIYjr9IyyqEqLr95iJVTs"; + //个人消息通知 + public const string C_Template_Message = "rtAx0BoUAwZ3npbNIO8Y9eIbdWO-weLGE2iOacGqN_s"; + + #endregion + + MessageConnectionCollection _channels = new MessageConnectionCollection(); + public MessageConnectionCollection Channels + { + get + { + return this._channels; + } + } + + // 配置文件 + public string _cfgFile = ""; + + // dp2服务器地址与代理账号 + public string dp2MServerUrl = ""; + public string userName = ""; + public string password = ""; + + // 微信信息 + public string weiXinAppId { get; set; } + public string weiXinSecret { get; set; } + public bool bTrace = false; + + // 背景图管理器 + public string TodayUrl = ""; + + // dp2消息处理类 + MsgRouter _msgRouter = new MsgRouter(); + + //================= + // 设为单一实例 + static dp2WeiXinService _instance; + private dp2WeiXinService() + { + } + private static object _lock = new object(); + static public dp2WeiXinService Instance + { + get + { + if (null == _instance) + { + lock (_lock) //线程安全的 + { + _instance = new dp2WeiXinService(); + } + } + return _instance; + } + } + //=========== + + public void Init(string dataDir) + { + this.weiXinDataDir = dataDir; + + this._cfgFile = this.weiXinDataDir + "\\" + "weixin.xml"; + if (File.Exists(this._cfgFile) == false) + { + throw new Exception("配置文件" + this._cfgFile + "不存在。"); + } + + // 日志目录 + this.weiXinLogDir = this.weiXinDataDir + "/log"; + if (!Directory.Exists(weiXinLogDir)) + { + Directory.CreateDirectory(weiXinLogDir); + } + + + XmlDocument dom = new XmlDocument(); + dom.Load(this._cfgFile); + XmlNode root = dom.DocumentElement; + + // 取出mserver服务器配置信息 + XmlNode nodeDp2mserver = root.SelectSingleNode("dp2mserver"); + this.dp2MServerUrl = DomUtil.GetAttr(nodeDp2mserver, "url");// WebConfigurationManager.AppSettings["dp2MServerUrl"]; + this.userName = DomUtil.GetAttr(nodeDp2mserver, "username");//WebConfigurationManager.AppSettings["userName"]; + this.password = DomUtil.GetAttr(nodeDp2mserver, "password");//WebConfigurationManager.AppSettings["password"]; + if (string.IsNullOrEmpty(this.password) == false)// 解密 + this.password = Cryptography.Decrypt(this.password, dp2WeiXinService.EncryptKey); + + // 取出微信配置信息 + XmlNode nodeDp2weixin = root.SelectSingleNode("dp2weixin"); + this.weiXinUrl = DomUtil.GetAttr(nodeDp2weixin, "url"); //WebConfigurationManager.AppSettings["weiXinUrl"]; + this.weiXinAppId = DomUtil.GetAttr(nodeDp2weixin, "AppId"); //WebConfigurationManager.AppSettings["weiXinAppId"]; + this.weiXinSecret = DomUtil.GetAttr(nodeDp2weixin, "Secret"); //WebConfigurationManager.AppSettings["weiXinSecret"]; + string trace = DomUtil.GetAttr(nodeDp2weixin, "trace"); + if (trace.ToLower() == "true") + this.bTrace = true; + + + // mongo配置 + XmlNode nodeMongoDB = root.SelectSingleNode("mongoDB"); + string connectionString = DomUtil.GetAttr(nodeMongoDB, "connectionString"); + if (String.IsNullOrEmpty(connectionString) == true) + { + throw new Exception("尚未配置mongoDB节点的connectionString属性"); + } + string instancePrefix = DomUtil.GetAttr(nodeMongoDB, "instancePrefix"); + // 打开图书馆账号库与用户库 + WxUserDatabase.Current.Open(connectionString, instancePrefix); + LibDatabase.Current.Open(connectionString, instancePrefix); + + // 初始化接口类 + string strError = ""; + int nRet = this.InitialExternalMessageInterfaces(dom, out strError); + if (nRet == -1) + throw new Exception("初始化接口配置信息出错:" + strError); + + + //全局只需注册一次 + AccessTokenContainer.Register(this.weiXinAppId, this.weiXinSecret); + + + _channels.Login -= _channels_Login; + _channels.Login += _channels_Login; + + if (bTrace == true) + { + if (this.Channels.TraceWriter != null) + this.Channels.TraceWriter.Close(); + StreamWriter sw = new StreamWriter(Path.Combine(this.weiXinDataDir, "trace.txt")); + sw.AutoFlush = true; + _channels.TraceWriter = sw; + } + + // 消息处理类 + this._msgRouter.SendMessageEvent -= _msgRouter_SendMessageEvent; + this._msgRouter.SendMessageEvent += _msgRouter_SendMessageEvent; + this._msgRouter.Start(this._channels, + this.dp2MServerUrl, + "_patronNotify"); + + } + + public void Close() + { + if (this._msgRouter != null) + { + this._msgRouter.SendMessageEvent -= _msgRouter_SendMessageEvent; + this._msgRouter.Stop(); + } + if (this.Channels != null) + { + this.Channels.Login -= _channels_Login; + if (this.Channels.TraceWriter != null) + this.Channels.TraceWriter.Close(); + } + + this.WriteLog("走到close()"); + } + + public void SetDp2mserverInfo(string dp2mserverUrl, + string userName, + string password) + { + XmlDocument dom = new XmlDocument(); + dom.Load(this._cfgFile); + XmlNode root = dom.DocumentElement; + + // 设置mserver服务器配置信息 + XmlNode nodeDp2mserver = root.SelectSingleNode("dp2mserver"); + if (nodeDp2mserver == null) + { + nodeDp2mserver = dom.CreateElement("dp2mserver"); + root.AppendChild(nodeDp2mserver); + } + DomUtil.SetAttr(nodeDp2mserver, "url", dp2mserverUrl); + DomUtil.SetAttr(nodeDp2mserver, "username", userName); + string encryptPassword = Cryptography.Encrypt(password, dp2WeiXinService.EncryptKey); + DomUtil.SetAttr(nodeDp2mserver, "password", encryptPassword); + dom.Save(this._cfgFile); + + // 更新内存的信息 + this.dp2MServerUrl = dp2mserverUrl; + this.userName = userName; + this.password = password; + } + + public void GetDp2mserverInfo(out string dp2mserverUrl, + out string userName, + out string password) + { + dp2mserverUrl = ""; + userName = ""; + password = ""; + + XmlDocument dom = new XmlDocument(); + dom.Load(this._cfgFile); + XmlNode root = dom.DocumentElement; + + // 设置mserver服务器配置信息 + XmlNode nodeDp2mserver = root.SelectSingleNode("dp2mserver"); + if (nodeDp2mserver != null) + { + dp2mserverUrl = DomUtil.GetAttr(nodeDp2mserver, "url"); + userName = DomUtil.GetAttr(nodeDp2mserver, "username"); + password = DomUtil.GetAttr(nodeDp2mserver, "password"); + if (string.IsNullOrEmpty(password) == false)// 解密 + password = Cryptography.Decrypt(this.password, dp2WeiXinService.EncryptKey); + } + } + + #region 消息处理 + + // 处理收到的消息 + void _msgRouter_SendMessageEvent(object sender, SendMessageEventArgs e) + { + MessageRecord record = e.Message; + if (record == null) + { + this.WriteErrorLog("传过来的e.Message为null"); + return; + } + + //this.WriteErrorLog("走进_msgRouter_SendMessageEvent"); + + try + { + string strError = ""; + /// + /// -1 不符合条件,不处理 + /// 0 未绑定微信id,未处理 + /// 1 成功 + /// + int nRet = this.InternalDoMessage(record, out strError); + if (nRet == -1) + { + this.WriteErrorLog("[" + record.id + "]未发送成功:" + strError); + } + else if (nRet == 0) + { + this.WriteErrorLog("[" + record.id + "]未发送成功:未绑定微信id。"); + } + else + { + this.WriteErrorLog("[" + record.id + "]发送成功。"); + } + } + catch (Exception ex) + { + this.WriteErrorLog("[" + record.id + "]异常:"+ex.Message); + + } + } + + /// + /// 内部处理消息 + /// + /// + /// + /// + /// -1 不符合条件,不处理 + /// 0 未绑定微信id,未处理 + /// 1 成功 + /// + public int InternalDoMessage(MessageRecord record, out string strError) + { + strError = ""; + + string id = record.id; + string data = record.data; + string[] group = record.groups; + string create = record.creator; + + + // + // patronNotify + // R0000001@LUID:62637a12-1965-4876-af3a-fc1d3009af8a + // xml + // ... + // + XmlDocument dataDom = new XmlDocument(); + try + { + dataDom.LoadXml(data); + } + catch (Exception ex) + { + strError = "加载消息返回的data到xml出错:" + ex.Message; + return -1; + } + + XmlNode nodeType = dataDom.DocumentElement.SelectSingleNode("type"); + if (nodeType == null) + { + strError = "尚未定义节点"; + return -1; + } + string type = DomUtil.GetNodeText(nodeType); + if (type != "patronNotify") //只处理通知消息 + { + strError = "节点值不是patronNotify。"; + return -1; + } + + XmlNode nodeBody = dataDom.DocumentElement.SelectSingleNode("body"); + if (nodeBody == null) + { + strError = "data中不存在body节点"; + return -1; + } + + /* + body元素里面是预约到书通知记录(注意这是一个字符串,需要另行装入一个XmlDocument解析),其格式如下: + + + 预约到书通知 + 0000001 + + /book.aspx?barcode=0000001 + 2天 + 2016/5/17 10:10:59 + 船舶柴油机 / 聂云超主编. -- ISBN 7-... + 张三 + + R0000001 + 本科生 + 张三 + be13ecc5-6a9c-4400-9453-a072c50cede1 + 数学系 +
address
+ C12345 + 8aa41a9a-fb42-48c0-b9b9-9d6656dbeb76 + email:xietao@dp2003.com,weixinid:testwx2 + 13641016400 + 1234567890123 +
+
+ /// -1 出错,格式出错或者发送模板消息出错 + /// 0 未绑定微信id + /// 1 成功 + /// + private int SendMessageMsg(XmlDocument bodyDom, out string strError) + { + strError = ""; + /* + + 以停代金到期 +… + +根元素下的items元素下,有一个或者多个overdue元素,记载了刚到期的以停代金事项信息。 +在patronRecord的下级元素overdues下,可以看到若干overdue元素,这是当前还未到期或者交费的事项。只要还有这样的事项,读者就不被允许借书,只能还书。所以通知消息文字组织的时候,可以考虑提醒尚余多少违约事项,这样可以避免读者空高兴一场以为马上可以借书了 + + */ + + string patronName = ""; + List weiXinIdList = this.GetWeiXinIds(bodyDom, out patronName); + if (weiXinIdList.Count == 0) + { + strError = "未绑定微信id"; + return 0; + } + + XmlNode root = bodyDom.DocumentElement; + XmlNode nodeOperTime = root.SelectSingleNode("operTime"); + if (nodeOperTime == null) + { + strError = "尚未定义节点"; + return -1; + } + string operTime = DomUtil.GetNodeText(nodeOperTime); + operTime = DateTimeUtil.ToLocalTime(operTime, "yyyy/MM/dd"); + + + XmlNodeList listOverdue = root.SelectNodes("items/overdue"); + string barcodes = ""; + double totalPrice = 0; + foreach (XmlNode node in listOverdue) + { + string oneBarcode = DomUtil.GetAttr(node, "barcode"); + if (barcodes != "") + barcodes += ","; + barcodes += oneBarcode; + + string price = DomUtil.GetAttr(node, "price"); + if (String.IsNullOrEmpty(price) == false && price.Length > 3) + { + double dPrice = Convert.ToDouble(price.Substring(3)); + totalPrice += dPrice; + } + } + + string strText = "您有["+barcodes + "]项违约以停代金到期了,"; + XmlNodeList listOverdue1 = root.SelectNodes("patronRecord/overdues/overdue"); + if (listOverdue1.Count > 0) + { + strText += "您还有" + listOverdue1.Count.ToString() + "项违约未到期,还不能借书。"; + } + else + { + strText += "您可以继续借书了。"; + } + + + foreach (string weiXinId in weiXinIdList) + { + try + { + var accessToken = AccessTokenContainer.GetAccessToken(this.weiXinAppId); + + //{{first.DATA}} + //标题:{{keyword1.DATA}} + //时间:{{keyword2.DATA}} + //内容:{{keyword3.DATA}} + //{{remark.DATA}} + var msgData = new BorrowTemplateData() + { + first = new TemplateDataItem("〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓", "#9400D3"),// dark violet //this._msgFirstLeft + "您的停借期限到期了。" //$$$$$$$$$$$$$$$$ + keyword1 = new TemplateDataItem("以停代金到期", "#000000"),//text.ToString()),// "请让我慢慢长大"), + keyword2 = new TemplateDataItem(operTime, "#000000"), + keyword3 = new TemplateDataItem(strText, "#000000"), + remark = new TemplateDataItem(this._msgRemark, "#CCCCCC") + }; + + // 发送模板消息 + var result1 = TemplateApi.SendTemplateMessage(accessToken, + weiXinId, + dp2WeiXinService.C_Template_Message, + "#FF0000", + "",//不出现详细了 + msgData); + if (result1.errcode != 0) + { + strError = result1.errmsg; + return -1; + } + } + catch (Exception ex) + { + this.WriteErrorLog("给读者" + patronName + "发送'以停代金到期'通知异常:" + ex.Message); + } + } + + + return 1; + } + + /// + /// -1 出错,格式出错或者发送模板消息出错 + /// 0 未绑定微信id + /// 1 成功 + /// + private int SendReturnPayMsg(XmlDocument bodyDom, out string strError) + { + strError = ""; + /* + + + 撤销交费 + + amerce + undo + R0000001 + supervisor + Sun, 22 May 2016 19:15:54 +0800 + ::1 + 1.02 + + R0000001 + 本科生 + 张三 + be13ecc5-6a9c-4400-9453-a072c50cede1 + / +
address
+ C12345 + + + + 8aa41a9a-fb42-48c0-b9b9-9d6656dbeb76 + email:xietao@dp2003.com,weixinid:testwx2,testid:123456 + 1234567890123 + 13641016400 + +
+ + + +
+ + */ + + string patronName = ""; + List weiXinIdList = this.GetWeiXinIds(bodyDom, out patronName); + if (weiXinIdList.Count == 0) + { + strError = "未绑定微信id"; + return 0; + } + + XmlNode root = bodyDom.DocumentElement; + XmlNode nodeOperTime = root.SelectSingleNode("operTime"); + if (nodeOperTime == null) + { + strError = "尚未定义节点"; + return -1; + } + string operTime = DomUtil.GetNodeText(nodeOperTime); + operTime = DateTimeUtil.ToLocalTime(operTime, "yyyy/MM/dd"); + + + XmlNodeList listOverdue = root.SelectNodes("items/overdue"); + string barcodes = ""; + double totalPrice = 0; + foreach (XmlNode node in listOverdue) + { + string oneBarcode = DomUtil.GetAttr(node, "barcode"); + if (barcodes != "") + barcodes += ","; + barcodes += oneBarcode; + + string price = DomUtil.GetAttr(node, "price"); + if (String.IsNullOrEmpty(price) == false && price.Length > 3) + { + double dPrice = Convert.ToDouble(price.Substring(3)); + totalPrice += dPrice; + } + } + + foreach (string weiXinId in weiXinIdList) + { + try + { + var accessToken = AccessTokenContainer.GetAccessToken(this.weiXinAppId); + + //{{first.DATA}} + //退款原因:{{reason.DATA}} + //退款金额:{{refund.DATA}} + //{{remark.DATA}} + var msgData = new ReturnPayTemplateData() + { + first = new TemplateDataItem("━━━━━━$━━━━━━", "#B8860B"), // ☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆ dark golden rod//this._msgFirstLeft + "撤消交费成功!" + reason = new TemplateDataItem("撤消[" + barcodes + "]交费。", "#000000"),//text.ToString()),// "请让我慢慢长大"), + refund = new TemplateDataItem("CNY" + totalPrice, "#000000"), + remark = new TemplateDataItem(this._msgRemark, "#CCCCCC") + }; + + // 发送模板消息 + var result1 = TemplateApi.SendTemplateMessage(accessToken, + weiXinId, + dp2WeiXinService.C_Template_ReturnPay, + "#FF0000", + "",//不出现详细了 + msgData); + if (result1.errcode != 0) + { + strError = result1.errmsg; + return -1; + } + } + catch (Exception ex) + { + this.WriteErrorLog("给读者" + patronName + "发送'撤消交费成功'通知异常:" + ex.Message); + } + } + + + return 1; + } + + /// + /// -1 出错,格式出错或者发送模板消息出错 + /// 0 未绑定微信id + /// 1 成功 + /// + private int SendPayMsg(XmlDocument bodyDom, out string strError) + { + strError = ""; + /* + + + 交费 + + amerce + amerce + R0000001 + supervisor + Sun, 22 May 2016 19:28:52 +0800 + ::1 + 1.02 + + R0000001 + 本科生 + 张三 + be13ecc5-6a9c-4400-9453-a072c50cede1 + / +
address
+ C12345 + 8aa41a9a-fb42-48c0-b9b9-9d6656dbeb76 + email:xietao@dp2003.com,weixinid:testwx2,testid:123456 + 1234567890123 + 13641016400 + +
+ + + +
+ + */ + + string patronName = ""; + List weiXinIdList = this.GetWeiXinIds(bodyDom, out patronName); + if (weiXinIdList.Count == 0) + { + strError = "未绑定微信id"; + return 0; + } + + XmlNode root = bodyDom.DocumentElement; + XmlNode nodeOperTime = root.SelectSingleNode("operTime"); + if (nodeOperTime == null) + { + strError = "尚未定义节点"; + return -1; + } + string operTime = DomUtil.GetNodeText(nodeOperTime); + operTime = DateTimeUtil.ToLocalTime(operTime, "yyyy/MM/dd"); + + + XmlNodeList listOverdue = root.SelectNodes("items/overdue"); + string barcodes = ""; + double totalPrice = 0; + string reasons = ""; + foreach (XmlNode node in listOverdue) + { + string oneBarcode = DomUtil.GetAttr(node, "barcode"); + if (barcodes != "") + barcodes += ","; + barcodes += oneBarcode; + + string price = DomUtil.GetAttr(node, "price"); + if (String.IsNullOrEmpty(price) == false && price.Length > 3) + { + double dPrice = Convert.ToDouble(price.Substring(3)); + totalPrice += dPrice; + } + + string oneReason = DomUtil.GetAttr(node, "reason"); + if (reasons != "") + reasons += ","; + reasons += oneReason; + } + + foreach (string weiXinId in weiXinIdList) + { + + try + { + var accessToken = AccessTokenContainer.GetAccessToken(this.weiXinAppId); + + //{{first.DATA}} + //订单号:{{keyword1.DATA}} + //缴费人:{{keyword2.DATA}} + //缴费金额:{{keyword3.DATA}} + //费用类型:{{keyword4.DATA}} + //缴费时间:{{keyword5.DATA}} + //{{remark.DATA}} + //您好,您已缴费成功! + //订单号:书名(册条码号) + //缴费人:张三 + //缴费金额:¥100.00 + //费用类型:违约 + //缴费时间:2015-12-27 13:15 + //如有疑问,请联系学校管理员,感谢您的使用!、 + var msgData = new PayTemplateData() + { + first = new TemplateDataItem("++++++$++++++", "#556B2F"),//★★★★★★★★★★★★★★★ dark olive green//this._msgFirstLeft+"您已交费成功!" + keyword1 = new TemplateDataItem(barcodes, "#000000"),//text.ToString()),// "请让我慢慢长大"), + keyword2 = new TemplateDataItem(patronName, "#000000"), + keyword3 = new TemplateDataItem("CNY" + totalPrice, "#000000"), + keyword4 = new TemplateDataItem(reasons, "#000000"), + keyword5 = new TemplateDataItem(operTime, "#000000"), + remark = new TemplateDataItem(this._msgRemark, "#CCCCCC") + }; + + // 发送模板消息 + var result1 = TemplateApi.SendTemplateMessage(accessToken, + weiXinId, + dp2WeiXinService.C_Template_Pay, + "#FF0000", + "",//不出现详细了 + msgData); + if (result1.errcode != 0) + { + strError = result1.errmsg; + return -1; + } + } + catch (Exception ex) + { + this.WriteErrorLog("给读者" + patronName + "发送交费成功通知异常:" + ex.Message); + } + } + + + return 1; + } + + /// + /// -1 出错,格式出错或者发送模板消息出错 + /// 0 未绑定微信id + /// 1 成功 + /// + private int SendReturnMsg(XmlDocument bodyDom, out string strError) + { + strError = ""; + /* + + + 还书成功 + + return + return + 0000001 + R0000001 + supervisor + Sun, 22 May 2016 13:11:33 +0800 + ::1 + 1.02 + 4a9730b1-a6d7-4fd5-9e6f-57c074f73661 + + R0000001 + 本科生 + 张三 + + + be13ecc5-6a9c-4400-9453-a072c50cede1 + + + / +
address
+ C12345 + + + 8aa41a9a-fb42-48c0-b9b9-9d6656dbeb76 + email:xietao@dp2003.com,weixinid:testwx2,testid:123456 + 1234567890123 + 13641016400 + +
+ + 602 + 59b613c6-fe09-4280-8884-43f2b045c41c + 0000001 + 流通库 + $4.65 + 普通 + U664.121/N590 + 船舶柴油机 / 聂云超主编. -- ISBN 7-81007-115-7 : $4.65 + +
+ + */ + + string patronName = ""; + List weiXinIdList = this.GetWeiXinIds(bodyDom, out patronName); + if (weiXinIdList.Count == 0) + { + strError = "未绑定微信id"; + return 0; + } + + XmlNode root = bodyDom.DocumentElement; + //0000001 + //Sun, 22 May 2016 19:48:01 +0800 + //31day + //Wed, 22 Jun 2016 12:00:00 +0800 + XmlNode nodeItemBarcode = root.SelectSingleNode("itemBarcode"); + if (nodeItemBarcode == null) + { + strError = "尚未定义节点"; + return -1; + } + string itemBarcode = DomUtil.GetNodeText(nodeItemBarcode); + + XmlNode nodeOperTime = root.SelectSingleNode("operTime"); + if (nodeOperTime == null) + { + strError = "尚未定义节点"; + return -1; + } + string operTime = DomUtil.GetNodeText(nodeOperTime); + operTime = DateTimeUtil.ToLocalTime(operTime, "yyyy/MM/dd"); + + + XmlNode nodeSummary = root.SelectSingleNode("itemRecord/summary"); + if (nodeSummary == null) + { + strError = "尚未定义itemRecord/summary节点"; + return -1; + } + string summary = DomUtil.GetNodeText(nodeSummary); + + // 检查是否有超期信息 + string remark = "\n" + patronName + ",感谢及时归还,欢迎继续借书。"; + XmlNodeList listOverdue = root.SelectNodes("patronRecord/overdues/overdue"); + if (listOverdue.Count > 0) + { + remark = "\n"+patronName+",您有" + listOverdue.Count + "笔超期违约记录,请履行超期手续。"; + } + + + foreach (string weiXinId in weiXinIdList) + { + try + { + var accessToken = AccessTokenContainer.GetAccessToken(this.weiXinAppId); + + //{{first.DATA}} + //书名:{{keyword1.DATA}} + //归还时间:{{keyword2.DATA}} + //借阅人:{{keyword3.DATA}} + //{{remark.DATA}} + //您好,你借阅的图书已确认归还. + //书名:算法导论 + //归还时间:2015-10-10 12:14 + //借阅人:李明 + //欢迎继续借书! + var msgData = new ReturnTemplateData() + { + first = new TemplateDataItem("▉▊▋▍▎▉▊▋▍▎▉▊▋▍▎", "#00008B"), // dark blue//this._msgFirstLeft + "您借出的图书已确认归还。" + keyword1 = new TemplateDataItem(summary, "#000000"),//text.ToString()),// "请让我慢慢长大"), + keyword2 = new TemplateDataItem(operTime, "#000000"), + keyword3 = new TemplateDataItem(patronName, "#000000"), + remark = new TemplateDataItem(remark, "#CCCCCC") + }; + + // 发送模板消息 + var result1 = TemplateApi.SendTemplateMessage(accessToken, + weiXinId, + dp2WeiXinService.C_Template_Return, + "#00008B", + "",//不出现详细了 + msgData); + if (result1.errcode != 0) + { + strError = result1.errmsg; + return -1; + } + } + catch (Exception ex) + { + this.WriteErrorLog("给读者" + patronName + "发送还书成功通知异常:" + ex.Message); + } + } + + + return 1; + } + + /// + /// -1 出错,格式出错或者发送模板消息出错 + /// 0 未绑定微信id + /// 1 成功 + /// + private int SendBorrowMsg(XmlDocument bodyDom, out string strError) + { + strError = ""; + /* + + 借书成功 + + borrow + borrow + R0000001 + 0000001 + Sun, 22 May 2016 19:48:01 +0800 + 31day + Wed, 22 Jun 2016 12:00:00 +0800 + $4.65 + 0 + 普通 + supervisor + Sun, 22 May 2016 19:48:01 +0800 + ::1 + 1.02 + 062724d8-80c1-4752-979a-b1cd548466be + + R0000001 + 本科生 + 张三 + + + + be13ecc5-6a9c-4400-9453-a072c50cede1 + / +
address
+ C12345 + 8aa41a9a-fb42-48c0-b9b9-9d6656dbeb76 + email:xietao@dp2003.com,weixinid:testwx2,testid:123456 + 1234567890123 + 13641016400 + +
+ + 602 + 59b613c6-fe09-4280-8884-43f2b045c41c + 0000001 + 流通库 + $4.65 + 普通 + U664.121/N590 + R0000001 + 本科生 + 读者/1 + Sun, 22 May 2016 19:48:01 +0800 + 31day + Wed, 22 Jun 2016 12:00:00 +0800 + supervisor + 船舶柴油机 / 聂云超主编. -- ISBN 7-81007-115-7 : $4.65 + +
+ */ + + string patronName = ""; + List weiXinIdList = this.GetWeiXinIds(bodyDom, out patronName); + if (weiXinIdList.Count == 0) + { + strError = "未绑定微信id"; + return 0; + } + + XmlNode root = bodyDom.DocumentElement; + //0000001 + //Sun, 22 May 2016 19:48:01 +0800 + //31day + //Wed, 22 Jun 2016 12:00:00 +0800 + XmlNode nodeItemBarcode = root.SelectSingleNode("itemBarcode"); + if (nodeItemBarcode == null) + { + strError = "尚未定义节点"; + return -1; + } + string itemBarcode = DomUtil.GetNodeText(nodeItemBarcode); + + XmlNode nodeBorrowDate = root.SelectSingleNode("borrowDate"); + if (nodeBorrowDate == null) + { + strError = "尚未定义节点"; + return -1; + } + string borrowDate = DomUtil.GetNodeText(nodeBorrowDate); + borrowDate=DateTimeUtil.ToLocalTime(borrowDate, "yyyy/MM/dd"); + + XmlNode nodeBorrowPeriod = root.SelectSingleNode("borrowPeriod"); + if (nodeBorrowPeriod == null) + { + strError = "尚未定义节点"; + return -1; + } + string borrowPeriod = DomUtil.GetNodeText(nodeBorrowPeriod); + + XmlNode nodeReturningDate = root.SelectSingleNode("returningDate"); + if (nodeReturningDate == null) + { + strError = "尚未定义节点"; + return -1; + } + string returningDate = DomUtil.GetNodeText(nodeReturningDate); + returningDate = DateTimeUtil.ToLocalTime(returningDate, "yyyy/MM/dd"); + + + XmlNode nodeSummary = root.SelectSingleNode("itemRecord/summary"); + if (nodeSummary == null) + { + strError = "尚未定义itemRecord/summary节点"; + return -1; + } + string summary = DomUtil.GetNodeText(nodeSummary); + + foreach (string weiXinId in weiXinIdList) + { + try + { + var accessToken = AccessTokenContainer.GetAccessToken(this.weiXinAppId); + + //尊敬的XXX,恭喜您借书成功。 + //图书书名:C#开发教程 + //册条码号:C0000001 + //借阅日期:2016-5-27 + //借阅期限:31 + //应还日期:2016-6-27 + //祝您阅读愉快,欢迎再借。 + var msgData = new BorrowTemplateData() + { + first = new TemplateDataItem("▉▊▋▍▎▉▊▋▍▎▉▊▋▍▎", "#006400"), // dark green //this._msgFirstLeft + "恭喜您借书成功。" + keyword1 = new TemplateDataItem(summary, "#000000"),//text.ToString()),// "请让我慢慢长大"), + keyword2 = new TemplateDataItem(itemBarcode, "#000000"), + keyword3 = new TemplateDataItem(borrowDate, "#000000"), + keyword4 = new TemplateDataItem(borrowPeriod, "#000000"), + keyword5 = new TemplateDataItem(returningDate, "#000000"), + remark = new TemplateDataItem("\n"+patronName+",祝您阅读愉快,欢迎再借。", "#CCCCCC") + }; + + // 发送模板消息 + var result1 = TemplateApi.SendTemplateMessage(accessToken, + weiXinId, + dp2WeiXinService.C_Template_Borrow, + "#006400", //FF0000 + "",//不出现详细了 + msgData); + if (result1.errcode != 0) + { + strError = result1.errmsg; + return -1; + } + } + catch (Exception ex) + { + this.WriteErrorLog("给读者" + patronName + "发送借书成功通知异常:" + ex.Message); + } + } + return 1; + } + + + + /// + /// 发送预约通知 + /// + /// + /// + /// + /// -1 出错,格式出错或者发送模板消息出错 + /// 0 未绑定微信id + /// 1 成功 + /// + private int SendArrived(XmlDocument bodyDom, out string strError) + { + strError = ""; + + + + /* + body元素里面是预约到书通知记录(注意这是一个字符串,需要另行装入一个XmlDocument解析),其格式如下: + + + 预约到书通知 + 0000001 + +false + /book.aspx?barcode=0000001 + 2天 + 2016/5/17 10:10:59 + 船舶柴油机 / 聂云超主编. -- ISBN 7-... + 张三 + + R0000001 + 本科生 + 张三 + be13ecc5-6a9c-4400-9453-a072c50cede1 + 数学系 +
address
+ C12345 + 8aa41a9a-fb42-48c0-b9b9-9d6656dbeb76 + email:xietao@dp2003.com,weixinid:testwx2 + 13641016400 + 1234567890123 +
+
+ + */ + + string patronName = ""; + List weiXinIdList = this.GetWeiXinIds(bodyDom, out patronName); + if (weiXinIdList.Count == 0) + { + strError = "未绑定微信id"; + return 0; + } + + XmlNode root = bodyDom.DocumentElement; + //false + // 2天 + // 2016/5/17 10:10:59 + // 取出预约消息 + XmlNode nodeSummary = root.SelectSingleNode("summary"); + if (nodeSummary == null) + { + strError = "尚未定义节点"; + return -1; + } + string summary = DomUtil.GetNodeText(nodeSummary); + + XmlNode nodeReserveTime = root.SelectSingleNode("reserveTime"); + if (nodeReserveTime == null) + { + strError = "尚未定义节点"; + return -1; + } + string reserveTime = DomUtil.GetNodeText(nodeReserveTime); + + XmlNode nodeToday = root.SelectSingleNode("today"); + if (nodeToday == null) + { + strError = "尚未定义节点"; + return -1; + } + string today = DomUtil.GetNodeText(nodeToday); + + // 是否在架 + XmlNode nodeOnShelf = root.SelectSingleNode("onShelf"); + if (nodeOnShelf == null) + { + strError = "尚未定义节点"; + return -1; + } + string onShelf = DomUtil.GetNodeText(nodeOnShelf); + bool bOnShelf = false; + if (onShelf == "true") + bOnShelf = true; + + //string first = this._msgFirstLeft+"我们很高兴地通知您,您预约的图书到了,请尽快来图书馆办理借书手续。"; + string end = "\n" + patronName + ",您预约的图书到了,请尽快来图书馆办理借书手续,请尽快来图书馆办理借书手续。如果您未能在保留期限内来馆办理借阅手续,图书馆将把优先借阅权转给后面排队等待的预约者,或做归架处理。"; + if (bOnShelf == true) + { + //first = this._msgFirstLeft + "我们很高兴地通知您,您预约的图书已经在架上,请尽快来图书馆办理借书手续。"; + end = "\n" + patronName + ",您预约的图书已经在架上,请尽快来图书馆办理借书手续。如果您未能在保留期限内来馆办理借阅手续,图书馆将把优先借阅权转给后面排队等待的预约者,或允许其他读者借阅。"; + } + + foreach (string weiXinId in weiXinIdList) + { + try + { + var accessToken = AccessTokenContainer.GetAccessToken(this.weiXinAppId); + + //{{first.DATA}} + //图书书名:{{keyword1.DATA}} + //到书日期:{{keyword2.DATA}} + //保留期限:{{keyword3.DATA}} + //{{remark.DATA}} + var msgData = new ArrivedTemplateData() + { + first = new TemplateDataItem("▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉", "#FF8C00"),// dark orange yellow #FFFF00 + keyword1 = new TemplateDataItem(summary, "#000000"),//text.ToString()),// "请让我慢慢长大"), + keyword2 = new TemplateDataItem(today, "#000000"), + keyword3 = new TemplateDataItem("保留" + reserveTime, "#000000"), + remark = new TemplateDataItem(end, "#CCCCCC") + }; + + // 发送预约模板消息 + //string detailUrl = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx57aa3682c59d16c2&redirect_uri=http%3a%2f%2fdp2003.com%2fdp2weixin%2fPatron%2fIndex&response_type=code&scope=snsapi_base&state=dp2weixin#wechat_redirect"; + var result1 = TemplateApi.SendTemplateMessage(accessToken, + weiXinId, + dp2WeiXinService.C_Template_Arrived, + "#FF0000", + "",//不出现详细了 + msgData); + if (result1.errcode != 0) + { + strError = result1.errmsg; + return -1; + } + } + catch (Exception ex) + { + this.WriteErrorLog("给读者" + patronName + "发送预约到书通知异常:" + ex.Message); + } + } + + + return 1; + } + + /// + /// 发送超期通知 + /// + /// + private int SendCaoQi(XmlDocument bodyDom, out string strError) + { + strError = ""; + + /* + + 超期通知 + + + + 您借阅的下列书刊: +船舶柴油机 / 聂云超主编. -- ISBN 7-... 应还日期: 2016/5/18 已超期 31 天 + + ... + + + +//overdueType是超期类型,overdue表示超期,warning表示即将超期。 + */ + + // 得到绑定的微信id + string patronName = ""; + List weiXinIdList = this.GetWeiXinIds(bodyDom, out patronName); + if (weiXinIdList.Count == 0) + { + strError = "未绑定微信id"; + return 0; + } + + XmlNode root = bodyDom.DocumentElement; + + // 取出册列表 + XmlNode nodeItems = root.SelectSingleNode("items"); + string overdueCount = DomUtil.GetAttr(nodeItems, "overdueCount"); + + XmlNodeList nodeList = nodeItems.SelectNodes("item"); + // 一册一个通知 + foreach (XmlNode item in nodeList) + { + string summary = DomUtil.GetAttr(item, "summary"); + string timeReturning = DomUtil.GetAttr(item, "timeReturning"); + string overdue = DomUtil.GetAttr(item, "overdue"); + + //overdueType是超期类型,overdue表示超期,warning表示即将超期。 + string templateId = ""; + string overdueType = DomUtil.GetAttr(item, "overdueType"); + //string first = ""; + string end = ""; + if (overdueType == "overdue") + { + templateId = dp2WeiXinService.C_Template_CaoQi; + //first = this._msgFirstLeft+"您借出的图书已超期,请尽快归还。"; + end = "\n"+patronName+",您借出的图书已超期,请尽快归还。"; + } + else if (overdueType == "warning") + { + templateId = dp2WeiXinService.C_Template_DaoQi; + // first = this._msgFirstLeft+"您借出的图书即将到期,请注意不要超期,留意归还。"; + end = "\n" + patronName + ",您借出的图书即将到期,请注意不要超期,留意归还。"; + } + else + { + strError ="overdueType属性值[]不合法。"; + return -1;//整个不处理 //continue; + } + + foreach (string weiXinId in weiXinIdList) + { + try + { + var accessToken = AccessTokenContainer.GetAccessToken(this.weiXinAppId); + + //{{first.DATA}} + //图书书名:{{keyword1.DATA}} + //应还日期:{{keyword2.DATA}} + //超期天数:{{keyword3.DATA}} + //{{remark.DATA}} + + //{{first.DATA}} + //图书书名:{{keyword1.DATA}} + //归还日期:{{keyword2.DATA}} + //剩余天数:{{keyword3.DATA}} + //{{remark.DATA}} + //超期和到期格式一样,就不用再建一个TemplateData类了 + var msgData = new ArrivedTemplateData() + { + first = new TemplateDataItem("▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉", "#FFFF00"), //yellow # + + keyword1 = new TemplateDataItem(summary, "#000000"),//text.ToString()),// "请让我慢慢长大"), + keyword2 = new TemplateDataItem(timeReturning, "#000000"), + keyword3 = new TemplateDataItem(overdue, "#000000"), + remark = new TemplateDataItem(end, "#CCCCCC")//"\n点击下方”详情“查看个人详细信息。" + }; + + string detailUrl = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx57aa3682c59d16c2&redirect_uri=http%3a%2f%2fdp2003.com%2fdp2weixin%2fPatron%2fIndex&response_type=code&scope=snsapi_base&state=dp2weixin#wechat_redirect"; + var result1 = TemplateApi.SendTemplateMessage(accessToken, + weiXinId, + templateId, + "#FF0000", + detailUrl,//不出现详细了 + msgData); + if (result1.errcode != 0) + { + strError = result1.errmsg; + return -1; + } + } + catch (Exception ex) + { + this.WriteErrorLog("给读者"+patronName+"发送超期通知异常:"+ex.Message); + } + } + } + + // 发送成功 + return 1; + } + + /// + /// 获取读者记录中绑定的微信id,返回数组 + /// + /// + /// + /// + private List GetWeiXinIds(XmlDocument bodyDom, out string patronName) + { + patronName = ""; + + XmlNode root = bodyDom.DocumentElement; + XmlNode patronRecordNode = root.SelectSingleNode("patronRecord"); + if (patronRecordNode == null) + throw new Exception("尚未定义节点"); + patronName = DomUtil.GetNodeText(patronRecordNode.SelectSingleNode("name")); + XmlNode emailNode = patronRecordNode.SelectSingleNode("email"); + if (emailNode == null) + throw new Exception("尚未定义节点"); + string email = DomUtil.GetNodeText(emailNode); + //test@163.com,123,weixinid:o4xvUviTxj2HbRqbQb9W2nMl4fGg,weixinid:o4xvUvnLTg6NnflbYdcS-sxJCGFo,weixinid:testid + string[] emailList = email.Split(new char[] { ',' }); + List weiXinIdList = new List(); + for (int i = 0; i < emailList.Length; i++) + { + string oneEmail = emailList[i].Trim(); + if (oneEmail.Length > 9 && oneEmail.Substring(0, 9) == dp2WeiXinService.C_WeiXinIdPrefix) + { + string weiwinId = oneEmail.Substring(9).Trim(); + if (weiwinId != "") + weiXinIdList.Add(weiwinId); + } + } + return weiXinIdList; + } + + #endregion + + #region 短信接口 + + public List m_externalMessageInterfaces = null; + + // 初始化扩展的消息接口 + /* + + + + */ + // parameters: + // return: + // -1 出错 + // 0 当前没有配置任何扩展消息接口 + // 1 成功初始化 + public int InitialExternalMessageInterfaces(XmlDocument dom, out string strError) + { + strError = ""; + + this.m_externalMessageInterfaces = null; + XmlNode root = dom.DocumentElement.SelectSingleNode("externalMessageInterface"); + if (root == null) + { + strError = "在weixin.xml中没有找到元素"; + return 0; + } + + this.m_externalMessageInterfaces = new List(); + + XmlNodeList nodes = root.SelectNodes("interface"); + foreach (XmlNode node in nodes) + { + string strType = DomUtil.GetAttr(node, "type"); + if (String.IsNullOrEmpty(strType) == true) + { + strError = "元素未配置type属性值"; + return -1; + } + + string strAssemblyName = DomUtil.GetAttr(node, "assemblyName"); + if (String.IsNullOrEmpty(strAssemblyName) == true) + { + strError = "元素未配置assemblyName属性值"; + return -1; + } + + MessageInterface message_interface = new MessageInterface(); + message_interface.Type = strType; + message_interface.Assembly = Assembly.Load(strAssemblyName); + if (message_interface.Assembly == null) + { + strError = "名字为 '" + strAssemblyName + "' 的Assembly加载失败..."; + return -1; + } + + Type hostEntryClassType = ScriptManager.GetDerivedClassType( + message_interface.Assembly, + "DigitalPlatform.Interfaces.ExternalMessageHost"); + if (hostEntryClassType == null) + { + strError = "名字为 '" + strAssemblyName + "' 的Assembly中未找到 DigitalPlatform.Interfaces.ExternalMessageHost类的派生类,初始化扩展消息接口失败..."; + return -1; + } + + message_interface.HostObj = (ExternalMessageHost)hostEntryClassType.InvokeMember(null, + BindingFlags.DeclaredOnly | + BindingFlags.Public | BindingFlags.NonPublic | + BindingFlags.Instance | BindingFlags.CreateInstance, null, null, + null); + if (message_interface.HostObj == null) + { + strError = "创建 type 为 '" + strType + "' 的 DigitalPlatform.Interfaces.ExternalMessageHost 类的派生类的对象(构造函数)失败,初始化扩展消息接口失败..."; + return -1; + } + + message_interface.HostObj.App = this; + + this.m_externalMessageInterfaces.Add(message_interface); + } + + return 1; + } + + public MessageInterface GetMessageInterface(string strType) + { + // 2012/3/29 + if (this.m_externalMessageInterfaces == null) + return null; + + foreach (MessageInterface message_interface in this.m_externalMessageInterfaces) + { + if (message_interface.Type == strType) + return message_interface; + } + + return null; + } + + #endregion + + #region 本方账号登录 + void _channels_Login(object sender, LoginEventArgs e) + { + MessageConnection connection = sender as MessageConnection; + + e.UserName = GetUserName(); + if (string.IsNullOrEmpty(e.UserName) == true) + throw new Exception("尚未指定用户名,无法进行登录"); + + e.Password = GetPassword(); + e.Parameters = ""; + + // TODO: 登录如果失败,界面会有提示么? + } + + string GetUserName() + { + return this.userName; + } + string GetPassword() + { + return this.password; + } + + #endregion + + + + #region 绑定解绑 + + public List GetBindInfo(string weixinId) + { + List list = new List(); + + // 目前只支持从数据库中查找 + list = WxUserDatabase.Current.GetByWeixinId(weixinId); + + + return list; + } + + public int ResetPassword(string remoteUserName, + string strLibraryCode, + string name, + string tel, + out string strError) + { + strError = ""; + string resultXml = ""; + string patronParam = "style=returnMessage," + + "queryword=NB:" + name + "|," + + "tel=" + tel + "," + + "name=" + name; + CancellationToken cancel_token = new CancellationToken(); + + string id = Guid.NewGuid().ToString(); + CirculationRequest request = new CirculationRequest(id, + "resetPassword", + patronParam, + "",//this.textBox_circulation_item.Text, + "",//this.textBox_circulation_style.Text, + "",//this.textBox_circulation_patronFormatList.Text, + "",//this.textBox_circulation_itemFormatList.Text, + "");//this.textBox_circulation_biblioFormatList.Text); + try + { + MessageConnection connection = this._channels.GetConnectionAsync( + this.dp2MServerUrl, + "").Result; + CirculationResult result = connection.CirculationAsync( + remoteUserName, + request, + new TimeSpan(0, 1, 10), // 10 秒 + cancel_token).Result; + if (result.Value == -1) + { + strError = "出错:" + result.ErrorInfo; + return -1; + } + + if (result.Value == 0) + { + if (result.String == "NotFound") + { + strError = "操作未成功:读者 " + name + " 尚未在图书馆账户中注册过手机号码,因此无法找回密码。请先去图书馆出纳台请工作人员帮助注册一下手机号码。"; + } + else + { + strError = "操作未成功:" + result.ErrorInfo; + } + + return 0; + } + + resultXml = result.PatronBarcode; + } + catch (AggregateException ex) + { + strError = MessageConnection.GetExceptionText(ex); + goto ERROR1; + } + catch (Exception ex) + { + strError = ex.Message; + goto ERROR1; + } + + // 发送短信 + string strMessageTemplate = ""; + MessageInterface external_interface = this.GetMessageInterface("sms"); + if (string.IsNullOrEmpty(strMessageTemplate) == true) + { + //strMessageTemplate = "%name% 您好!\n您的读者帐户(证条码号为 %barcode%)已设临时密码 %temppassword%,在 %period% 内登录会成为正式密码"; + strMessageTemplate = "%name% 您好!密码为 %temppassword%。一小时内有效。"; + } + /* + DomUtil.SetElementText(node, "tel", strTelParam); + DomUtil.SetElementText(node, "barcode", strBarcode); + DomUtil.SetElementText(node, "name", strName); + DomUtil.SetElementText(node, "tempPassword", strReaderTempPassword); + DomUtil.SetElementText(node, "expireTime", expire.ToLongTimeString()); + DomUtil.SetElementText(node, "period", "一小时"); + DomUtil.SetElementText(node, "refID", strRefID); + */ + /* + + + 13862157150 + R00001 + 任延华 + 586284 + 13:24:57 + 一小时 + 63aeb890-8936-4471-bfc5-8e72d5c7fe94 + + + */ + XmlDocument dom = new XmlDocument(); + dom.LoadXml(resultXml); + XmlNode nodePatron = dom.DocumentElement.SelectSingleNode("patron"); + string strRefID = DomUtil.GetNodeText(nodePatron.SelectSingleNode("refID")); + string strTel = DomUtil.GetNodeText(nodePatron.SelectSingleNode("tel")); + + string strBarcode = DomUtil.GetNodeText(nodePatron.SelectSingleNode("barcode")); + string strName = DomUtil.GetNodeText(nodePatron.SelectSingleNode("name")); + string strReaderTempPassword = DomUtil.GetNodeText(nodePatron.SelectSingleNode("tempPassword")); + string expireTime = DomUtil.GetNodeText(nodePatron.SelectSingleNode("expireTime")); + string period = DomUtil.GetNodeText(nodePatron.SelectSingleNode("period")); + + string strBody = strMessageTemplate.Replace("%barcode%", strBarcode) + .Replace("%name%", strName) + .Replace("%temppassword%", strReaderTempPassword) + .Replace("%expiretime%", expireTime) + .Replace("%period%", period); + // string strBody = "读者(证条码号) " + strBarcode + " 的帐户密码已经被重设为 " + strReaderNewPassword + ""; + int nRet = 0; + // 向手机号码发送短信 + { + // 得到高级xml + string strXml = "" + strTel + ""; + // 发送消息 + try + { + + // 发送一条消息 + // parameters: + // strPatronBarcode 读者证条码号 + // strPatronXml 读者记录XML字符串。如果需要除证条码号以外的某些字段来确定消息发送地址,可以从XML记录中取 + // strMessageText 消息文字 + // strError [out]返回错误字符串 + // return: + // -1 发送失败 + // 0 没有必要发送 + // >=1 发送成功,返回实际发送的消息条数 + nRet = external_interface.HostObj.SendMessage( + strBarcode, + strXml, + strBody, + strLibraryCode, + out strError); + if (nRet == -1 || nRet == 0) + return nRet; + } + catch (Exception ex) + { + strError = external_interface.Type + " 类型的外部消息接口Assembly中SendMessage()函数抛出异常: " + ex.Message; + nRet = -1; + } + if (nRet == -1) + { + strError = "向读者 '" + strBarcode + "' 发送" + external_interface.Type + " message时出错: " + strError; + + this.WriteErrorLog(strError); + return -1; + } + } + + return 1; + + ERROR1: + return -1; + } + + + /// + /// + /// + /// + /// + /// + /// + /// -1 出错 + /// 0 成功 + /// + public int Bind(string remoteUserName, + string libCode, + string strFullWord, + string strPassword, + string strWeiXinId, + out WxUserItem userItem, + out string strReaderBarcode, + out string strError) + { + userItem = null; + strError = ""; + strReaderBarcode = ""; + long lRet = -1; + + + CancellationToken cancel_token = new CancellationToken(); + + string fullWeixinId = dp2WeiXinService.C_WeiXinIdPrefix + strWeiXinId; + string id = Guid.NewGuid().ToString(); + BindPatronRequest request = new BindPatronRequest(id, + "bind", + strFullWord, + strPassword, + fullWeixinId, + "multiple",//single + "xml"); + + try + { + MessageConnection connection = this._channels.GetConnectionAsync( + this.dp2MServerUrl, + remoteUserName).Result; + + + BindPatronResult result = connection.BindPatronAsync( + remoteUserName, + request, + new TimeSpan(0, 1, 0), + cancel_token).Result; + + if (result.Value == -1) + { + strError = result.ErrorInfo; + return -1; + } + + string xml = result.Results[0]; + XmlDocument dom = new XmlDocument(); + dom.LoadXml(xml); + + // 绑定成功,把读者证条码记下来,用于续借 2015/11/7,不要用strbarcode变量,因为可能做的大小写转换 + strReaderBarcode = DomUtil.GetNodeText(dom.DocumentElement.SelectSingleNode("barcode")); + + // 将关系存到mongodb库 + string name = ""; + XmlNode node = dom.DocumentElement.SelectSingleNode("name"); + if (node != null) + name = DomUtil.GetNodeText(node); + string refID = ""; + node = dom.DocumentElement.SelectSingleNode("refID"); + if (node != null) + refID = DomUtil.GetNodeText(node); + + // 找到库中对应的记录 + userItem = WxUserDatabase.Current.GetOneOrEmptyPatron(strWeiXinId, libCode, strReaderBarcode); + if (userItem == null) + { + LibItem lib = LibDatabase.Current.GetLibByLibCode(libCode); + + + userItem = new WxUserItem(); + userItem.weixinId = strWeiXinId; + userItem.libCode = libCode; + userItem.libUserName = remoteUserName; + userItem.libName = lib.libName; + userItem.readerBarcode = strReaderBarcode; + userItem.readerName = name; + userItem.xml = xml; + userItem.refID = refID; + userItem.createTime = DateTimeUtil.DateTimeToString(DateTime.Now); + userItem.updateTime = userItem.createTime; + userItem.isActive = 1; + userItem.fullWord = strFullWord; + userItem.password = strPassword; + WxUserDatabase.Current.Add(userItem); + } + else + { + userItem.readerBarcode = strReaderBarcode; + userItem.readerName = name; + userItem.xml = xml; + userItem.refID = refID; + userItem.updateTime = DateTimeUtil.DateTimeToString(DateTime.Now); + userItem.isActive = 1; + userItem.fullWord = strFullWord; + userItem.password = strPassword; + lRet = WxUserDatabase.Current.Update(userItem); + } + // 置为活动状态 + WxUserDatabase.Current.SetActive(userItem); + + // 发送绑定成功的客服消息 + string accessToken = AccessTokenContainer.GetAccessToken(this.weiXinAppId); + var testData = new BindTemplateData() + { + first = new TemplateDataItem("恭喜您!您已成功绑定图书馆账号。", "#000000"), + keyword1 = new TemplateDataItem(userItem.readerName + "(" + userItem.readerBarcode + ")", "#000000"), + keyword2 = new TemplateDataItem("图书馆[" + userItem.libName + "]", "#000000"), + remark = new TemplateDataItem("您可以直接通过微信公众号访问图书馆,进行信息查询,预约续借等功能。如需解绑,请在“绑定账号”菜单操作。", "#CCCCCC") + }; + + // 详细转到账户管理界面 + string detailUrl = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx57aa3682c59d16c2&redirect_uri=http%3a%2f%2fdp2003.com%2fdp2weixin%2fAccount%2fIndex&response_type=code&scope=snsapi_base&state=dp2weixin#wechat_redirect"; + var result1 = TemplateApi.SendTemplateMessage(accessToken, + strWeiXinId, + dp2WeiXinService.C_Template_Bind, + "#FF0000", + detailUrl,//k"dp2003.com/dp2weixin/patron/index", // todo注意这里是否需要oauth接口,想着消息既然是从web发过来了,立即点进去还有session信息存在,但时间长了session失效就没有信息了 + testData); + if (result1.errcode != 0) + { + strError = result1.errmsg; + return -1; + } + + return 0; + } + catch (AggregateException ex) + { + strError = MessageConnection.GetExceptionText(ex); + goto ERROR1; + } + catch (Exception ex) + { + strError = ex.Message; + goto ERROR1; + } + + ERROR1: + return -1; + } + + + + + /// + /// + /// + /// + /// + /// + /// -1 出错 + /// 0 成功 + /// + public int Unbind(string userId, + out string strError) + { + strError = ""; + + //string remoteUserName, + // string libCode, + // string strBarcode, + // string strWeiXinId, + + WxUserItem userItem = WxUserDatabase.Current.GetById(userId); + if (userItem == null) + { + strError = "绑定账号未找到"; + return -1; + } + + + // 调点对点解绑接口 + string fullWeixinId = dp2WeiXinService.C_WeiXinIdPrefix + userItem.weixinId; + CancellationToken cancel_token = new CancellationToken(); + string id = Guid.NewGuid().ToString(); + BindPatronRequest request = new BindPatronRequest(id, + "unbind", + userItem.readerBarcode, + "",//password todo + fullWeixinId, + "multiple,null_password", + "xml"); + try + { + // 得到连接 + MessageConnection connection = this._channels.GetConnectionAsync( + this.dp2MServerUrl, + userItem.libUserName).Result; + + BindPatronResult result = connection.BindPatronAsync( + userItem.libUserName, + request, + new TimeSpan(0, 1, 0), + cancel_token).Result; + if (result.Value == -1) + { + //strError = result.ErrorInfo; + //return -1; + } + + // 删除mongodb库的记录 + WxUserDatabase.Current.Delete(userId); + + // 发送解绑消息 + string accessToken = AccessTokenContainer.GetAccessToken(this.weiXinAppId); + var data = new UnBindTemplateData() + { + first = new TemplateDataItem("您已成功对图书馆账号解除绑定。", "#000000"), + keyword1 = new TemplateDataItem(userItem.readerName + "(" + userItem.readerBarcode + ")", "#000000"), + keyword2 = new TemplateDataItem("图书馆[" + userItem.libName + "]", "#000000"), + remark = new TemplateDataItem("\n您现在不能访问该图书馆信息了,如需访问,请重新绑定。", "#CCCCCC") + }; + SendTemplateMessageResult result1 = TemplateApi.SendTemplateMessage(accessToken, + userItem.weixinId, + dp2WeiXinService.C_Template_UnBind, + "#FF0000", + "",//k"dp2003.com/dp2weixin/patron/index", // todo注意这里是否需要oauth接口,想着消息既然是从web发过来了,立即点进去还有session信息存在,但时间长了session失效就没有信息了 + data); + if (result1.errcode != 0) + { + strError = result1.errmsg; + return -1; + } + return 0; + } + catch (AggregateException ex) + { + strError = MessageConnection.GetExceptionText(ex); + goto ERROR1; + } + catch (Exception ex) + { + strError = ex.Message; + goto ERROR1; + } + + ERROR1: + return -1; + } + + public long SearchOnePatronByWeiXinId(string remoteUserName, + string libCode, + string strWeiXinId, + out string strBarcode, + out string strError) + { + strError = ""; + strBarcode = ""; + + // 从mongodb中检查是否绑定了用户 + WxUserItem userItem = WxUserDatabase.Current.GetActiveOrFirst(strWeiXinId, libCode); + if (userItem == null) + { + strError = "异常的情况,未怎么图书馆时不应走到SearchPatronByWeiXinId函数。"; + return -1; + } + + // mongodb存在 + if (String.IsNullOrEmpty(userItem.readerBarcode) == false) + { + strBarcode = userItem.readerBarcode; + return 1; + } + + // 从远程dp2library中查 + string strWord = dp2WeiXinService.C_WeiXinIdPrefix + strWeiXinId; + CancellationToken cancel_token = new CancellationToken(); + string id = Guid.NewGuid().ToString(); + SearchRequest request = new SearchRequest(id, + "searchPatron", + "<全部>", + strWord, + "email", + "left", + "wx-patron", + "id,cols", + 1000, + 0, + C_Search_MaxCount); + try + { + MessageConnection connection = this._channels.GetConnectionAsync( + this.dp2MServerUrl, + remoteUserName).Result; + + SearchResult result = connection.SearchAsync( + remoteUserName, + request, + new TimeSpan(0, 1, 0), + cancel_token).Result; + if (result.ResultCount == -1) + { + strError = result.ErrorInfo; + return -1; + } + if (result.ResultCount == 0) + return 0; + + // 找到对应的读者记录 + if (result.ResultCount > 0) + { + LibItem libItem = LibDatabase.Current.GetLibByLibCode(libCode); + string libName = libItem.libName; + for (int i = 0; i < result.ResultCount; i++) + { + // 可能会检索出多笔记录,先取第一笔 todo + string strXml = result.Records[i].Data; + XmlDocument dom = new XmlDocument(); + dom.LoadXml(strXml); + string strTempBarcode = DomUtil.GetNodeText(dom.DocumentElement.SelectSingleNode("barcode")); + + // 更新到mongodb库 + string name = ""; + XmlNode node = dom.DocumentElement.SelectSingleNode("name"); + if (node != null) + name = DomUtil.GetNodeText(node); + string refID = ""; + node = dom.DocumentElement.SelectSingleNode("refID"); + if (node != null) + refID = DomUtil.GetNodeText(node); + + if (i == 0) + { + userItem.readerBarcode = strTempBarcode; + userItem.readerName = name; + userItem.xml = strXml; + userItem.refID = refID; + userItem.updateTime = DateTimeUtil.DateTimeToString(DateTime.Now); + WxUserDatabase.Current.Update(userItem); + //将第一笔设为活动状态 + WxUserDatabase.Current.SetActive(userItem); + //返回的strBarcode //todo refID + strBarcode = strTempBarcode; + } + else + { + userItem = new WxUserItem(); + userItem.weixinId = strWeiXinId; + userItem.libCode = libCode; + userItem.libUserName = remoteUserName; + userItem.libName = libName; + userItem.readerBarcode = strTempBarcode; + userItem.readerName = name; + userItem.xml = strXml; + userItem.refID = refID; + userItem.createTime = DateTimeUtil.DateTimeToString(DateTime.Now); + userItem.updateTime = userItem.createTime; + WxUserDatabase.Current.Add(userItem); + } + } + + return 1; + } + + } + catch (AggregateException ex) + { + strError = MessageConnection.GetExceptionText(ex); + goto ERROR1; + } + catch (Exception ex) + { + strError = ex.Message; + goto ERROR1; + } + ERROR1: + return -1; + } + + + #endregion + + #region 检索书目 + + /// + /// 检索书目 + /// + /// + /// + /// + /// + public long SearchBiblio(string remoteUserName, + string strFrom, + string strWord, + out List records, + out string strError) + { + strError = ""; + records = new List(); + + long start = 0; + long count = 10; + int connIndex = 0; + try + { + + CancellationToken cancel_token = new CancellationToken(); + string id = Guid.NewGuid().ToString(); + SearchRequest request = new SearchRequest(id, + "searchBiblio", + "", + strWord, + strFrom, + "middle", + "weixin", + "id,cols", + C_Search_MaxCount, //最大数量 + start, //每次获取范围 + count); + + int tempNo = connIndex;// connIndex % 2; + MessageConnection connection = this._channels.GetConnectionAsync( + this.dp2MServerUrl, + remoteUserName).Result; //+ tempNo todo + connIndex++; + + SearchResult result = connection.SearchAsync( + remoteUserName, + request, + new TimeSpan(0, 1, 0), + cancel_token).Result; + if (result.ResultCount == -1) + { + strError = "检索出错:" + result.ErrorInfo; + return -1; + } + if (result.ResultCount == 0) + { + strError = "未命中"; + return 0; + } + + + List resultPathList = new List(); + for (int i = 0; i < result.Records.Count; i++) + { + string xml = result.Records[i].Data; + /*请让我慢慢长大 + * 吴蓓著 + * 天津教育出版社 + * 2009 + * G61-53 + * 儿童教育儿童教育 + * + * 978-7-5309-5335-8*/ + XmlDocument dom = new XmlDocument(); + dom.LoadXml(xml); + string name = DomUtil.GetNodeText(dom.DocumentElement.SelectSingleNode("col")); + string path = result.Records[i].RecPath; + int nIndex = path.IndexOf("@"); + path = path.Substring(0, nIndex); + + BiblioRecord record = new BiblioRecord(); + record.recPath = path; + record.name = name; + record.no = (i+start + 1).ToString();//todo 注意下一页的时候 + record.libUserName = remoteUserName; + records.Add(record); + } + + if (result.Records.Count > 0 && start+result.Records.Count < result.ResultCount) + { + start += result.Records.Count; + //goto REDO1; + } + + return records.Count; + } + catch (AggregateException ex) + { + strError = ex.Message; + return -1; + } + catch (Exception ex) + { + strError = ex.Message; + return -1; + } + + } + + public BiblioRecordResult GetBiblioDetail(string remoteUserName, + string biblioPath) + { + BiblioRecordResult result = new BiblioRecordResult(); + result.errorCode = 0; + result.biblioPath = biblioPath; + + DateTime start_time = DateTime.Now; + + try + { + string strError=""; + int nRet = 0; + TimeSpan time_length = DateTime.Now - start_time; + string logInfo = ""; + + // 取出summary + this.WriteLog("开始获取summary"); + string strSummary = ""; + nRet = this.GetBiblioSummary(remoteUserName, biblioPath, out strSummary, out strError); + if (nRet == -1 || nRet == 0) + { + result.errorCode = -1; + result.errorInfo = strError; + return result; + } + result.summary = strSummary; + time_length = DateTime.Now - start_time; + string info = "获取[" + biblioPath + "]的summary信息完毕 time span: " + time_length.TotalSeconds.ToString() + " secs"; + this.WriteLog(info); + + //Thread.Sleep(1000); + + // 取item + this.WriteLog("开始获取items"); + List itemList = null; + nRet = (int)this.GetItemInfo(remoteUserName, biblioPath, out itemList, out strError); + if (nRet == -1) //0的情况表示没有册,不是错误 + { + result.errorCode = -1; + result.errorInfo = strError; + return result; + } + + // 计算用了多少时间 + time_length = DateTime.Now - start_time; + logInfo = "获取[" + biblioPath + "]的item信息完毕 time span: " + time_length.TotalSeconds.ToString() + " secs"; + this.WriteLog(logInfo); + + result.itemList = itemList; + result.errorCode = 1; + return result; + } + catch (Exception ex) + { + result.errorCode = -1; + result.errorInfo = ex.Message; + return result; + } + + + + #region delete + /* + string strError = ""; + CancellationToken cancel_token = new CancellationToken(); + // 获取书目记录 + string id1 = Guid.NewGuid().ToString(); + SearchRequest request1 = new SearchRequest(id1, + "getBiblioInfo", + "<全部>", + biblioPath, + "", + "", + "", + "summary", + 1, + 0, + -1); + // 获取下属记录 + string id2 = Guid.NewGuid().ToString(); + SearchRequest request2 = new SearchRequest(id2, + "getItemInfo", + "entity", + biblioPath, + "", + "", + "", + "opac", + 50, + 0, + 10); + try + { + MessageConnection connection = this._channels.GetConnectionAsync( + this.dp2MServerUrl, + remoteUserName).Result; + + Task task1 = connection.SearchAsync( + remoteUserName, + request1, + new TimeSpan(0, 1, 0), + cancel_token); + + Task task2 = connection.SearchAsync( + remoteUserName, + request2, + new TimeSpan(0, 1, 0), + cancel_token); + + Task[] tasks = new Task[2]; + tasks[0] = task1; + tasks[1] = task2; + Task.WaitAll(tasks); + + if (task1.Result.ResultCount == -1) + { + strError = "获取摘要出错:" + task1.Result.ErrorInfo; + goto ERROR1; + } + if (task1.Result.ResultCount == 0) + { + result.errorCode = 0; + result.errorInfo = "获取Summary时未命中"; + return result; + } + result.summary = task1.Result.Records[0].Data; + + if (task2.Result.ResultCount == -1) + { + strError = "获取册出错:" + task2.Result.ErrorInfo; + goto ERROR1; + } + // 没有册的情况,不算出错 + //if (task2.Result.ResultCount == 0) + //{ + // result.errorCode = 0; + // result.errorInfo = "获取册时未命中"; + // return result; + //} + + result.itemList = new List(); + for (int i = 0; i < task2.Result.Records.Count; i++) + { + BiblioItem item = new BiblioItem(); + + string xml = task2.Result.Records[i].Data; + XmlDocument dom = new XmlDocument(); + dom.LoadXml(xml); + + string strBarcode = DomUtil.GetElementText(dom.DocumentElement, "barcode"); + string strRefID = DomUtil.GetElementText(dom.DocumentElement, "refID"); + // 册条码号 + string strViewBarcode = ""; + if (string.IsNullOrEmpty(strBarcode) == false) + strViewBarcode = strBarcode; + else + strViewBarcode = "refID:" + strRefID; //"@refID:" + item.barcode = strViewBarcode; + + //状态 + item.state = DomUtil.GetElementText(dom.DocumentElement, "state"); + // 馆藏地 + item.location = DomUtil.GetElementText(dom.DocumentElement, "location"); + // 索引号 + item.accessNo = DomUtil.GetElementText(dom.DocumentElement, "accessNo"); + + // 出版日期 + item.publishTime = DomUtil.GetElementText(dom.DocumentElement, "publishTime"); + // 价格 + item.price= DomUtil.GetElementText(dom.DocumentElement, "price"); + // 注释 + item.comment = DomUtil.GetElementText(dom.DocumentElement, "comment"); + + // 借阅情况 + string strBorrowInfo = "借阅情况:在架"; + + // R00001 + //教职工 + //读者/1 + //Sun, 17 Apr 2016 23:57:40 +0800 + //31day + //Wed, 18 May 2016 12:00:00 +0800 + + string strBorrower = DomUtil.GetElementText(dom.DocumentElement, "borrower"); + string borrowDate = DateTimeUtil.ToLocalTime(DomUtil.GetElementText(dom.DocumentElement, + "borrowDate"), "yyyy/MM/dd"); + string borrowPeriod = DomUtil.GetElementText(dom.DocumentElement, "borrowPeriod"); + if (string.IsNullOrEmpty(strBorrower) == false) + strBorrowInfo = "借阅者:*** 借阅时间:" + borrowDate + " 借期:" + borrowPeriod; + item.borrowInfo = strBorrower; + + // 加到集合里 + result.itemList.Add(item); + } + + result.errorCode = 1; + + return result; + + } + catch (AggregateException ex) + { + strError = MessageConnection.GetExceptionText(ex); + goto ERROR1; + } + catch (Exception ex) + { + strError = ex.Message; + goto ERROR1; + } + + ERROR1: + result.errorCode = -1; + result.errorInfo = strError; + return result; + */ + + #endregion + } + + private int GetBiblioSummary(string remoteUserName, + string biblioPath, + out string summary, + out string strError) + { + summary = ""; + strError = ""; + + CancellationToken cancel_token = new CancellationToken(); + string id = "1-summary";//Guid.NewGuid().ToString(); + SearchRequest request = new SearchRequest(id, + "getBiblioInfo", + "<全部>", + biblioPath, + "", + "", + "", + "summary", + 1, + 0, + -1); + try + { + MessageConnection connection = this._channels.GetConnectionAsync( + this.dp2MServerUrl, + remoteUserName + "-1").Result; + + SearchResult result = connection.SearchAsync( + remoteUserName, + request, + new TimeSpan(0, 1, 0), + cancel_token).Result; + if (result.ResultCount == -1) + { + strError = "检索出错:" + result.ErrorInfo; + return -1; + } + if (result.ResultCount == 0) + { + strError = "未命中"; + return 0; + } + + summary = result.Records[0].Data; + + + return 1; + } + catch (AggregateException ex) + { + strError = MessageConnection.GetExceptionText(ex); + goto ERROR1; + } + catch (Exception ex) + { + strError = ex.Message; + goto ERROR1; + } + ERROR1: + return -1; + } + + + private long GetItemInfo(string remoteUserName, + string biblioPath, + out List itemList, + out string strError) + { + itemList = new List(); + strError = ""; + + CancellationToken cancel_token = new CancellationToken(); + string id = "2-item";//Guid.NewGuid().ToString(); + SearchRequest request = new SearchRequest(id, + "getItemInfo", + "entity", + biblioPath, + "", + "", + "", + "opac", + 10, + 0, + -1); + try + { + this.WriteLog("GetItemInfo1"); + + MessageConnection connection = this._channels.GetConnectionAsync( + this.dp2MServerUrl, + remoteUserName+"-2").Result; + this.WriteLog("GetItemInfo2"); + + //string strFilename = string.Format(this.weiXinLogDir + "/log_{0}.txt", DateTime.Now.ToString("yyyyMMdd")); + //connection.logFileName = strFilename; + SearchResult result = null; + try + { + result = connection.SearchAsync( + remoteUserName, + request, + new TimeSpan(0, 1, 0), + cancel_token).Result; + } + catch (Exception ex) + { + strError = "检索出错:[SearchAsync异常]" + ex.Message; + return -1; + } + + this.WriteLog("GetItemInfo3"); + if (result.ResultCount == -1) + { + strError = "检索出错:" + result.ErrorInfo; + return -1; + } + if (result.ResultCount == 0) + { + strError = "未命中"; + return 0; + } + + this.WriteLog("GetItemInfo4"); + for (int i = 0; i < result.Records.Count; i++) + { + BiblioItem item = new BiblioItem(); + + string xml = result.Records[i].Data; + XmlDocument dom = new XmlDocument(); + dom.LoadXml(xml); + + string strBarcode = DomUtil.GetElementText(dom.DocumentElement, "barcode"); + string strRefID = DomUtil.GetElementText(dom.DocumentElement, "refID"); + // 册条码号 + string strViewBarcode = ""; + if (string.IsNullOrEmpty(strBarcode) == false) + strViewBarcode = strBarcode; + else + strViewBarcode = "refID:" + strRefID; //"@refID:" + item.barcode = strViewBarcode; + + //状态 + item.state = DomUtil.GetElementText(dom.DocumentElement, "state"); + // 馆藏地 + item.location = DomUtil.GetElementText(dom.DocumentElement, "location"); + // 索引号 + item.accessNo = DomUtil.GetElementText(dom.DocumentElement, "accessNo"); + + // 出版日期 + item.publishTime = DomUtil.GetElementText(dom.DocumentElement, "publishTime"); + // 价格 + item.price = DomUtil.GetElementText(dom.DocumentElement, "price"); + // 注释 + item.comment = DomUtil.GetElementText(dom.DocumentElement, "comment"); + + // 借阅情况 + string strBorrowInfo = "借阅情况:在架"; + /* + R00001 + 教职工 + 读者/1 + Sun, 17 Apr 2016 23:57:40 +0800 + 31day + Wed, 18 May 2016 12:00:00 +0800 + */ + string strBorrower = DomUtil.GetElementText(dom.DocumentElement, "borrower"); + string borrowDate = DateTimeUtil.ToLocalTime(DomUtil.GetElementText(dom.DocumentElement, + "borrowDate"), "yyyy/MM/dd"); + string borrowPeriod = DomUtil.GetElementText(dom.DocumentElement, "borrowPeriod"); + if (string.IsNullOrEmpty(strBorrower) == false) + strBorrowInfo = "借阅者:*** 借阅时间:" + borrowDate + " 借期:" + borrowPeriod; + item.borrowInfo = strBorrowInfo; + + itemList.Add(item); + } + + this.WriteLog("GetItemInfo5"); + return result.Records.Count; + } + catch (AggregateException ex) + { + strError = MessageConnection.GetExceptionText(ex); + goto ERROR1; + } + catch (Exception ex) + { + strError = ex.Message; + goto ERROR1; + } + ERROR1: + return -1; + } + + + #endregion + + /// + /// -1 出错 + /// 0 未查到对应记录 + /// 1 成功 + /// + public int GetMyInfo(string remoteUserName, + string strReaderBarcode, + out string strMyInfo, + out string strError) + { + strError = ""; + strMyInfo = ""; + Debug.Assert(String.IsNullOrEmpty(strReaderBarcode) == false); + + + // 得到高级xml + string strXml = ""; + int nRet = this.GetPatronInfo(remoteUserName, + strReaderBarcode, + "xml", + out strXml, + out strError); + if (nRet == -1) + return -1; + if (nRet == 0) + { + strError = "从dp2library未找到证条码号为'" + strReaderBarcode + "'的记录"; //todo refID + return 0; + } + + // 取出个人信息 + XmlDocument dom = new XmlDocument(); + dom.LoadXml(strXml); + //string strReaderBarcode = DomUtil.GetElementText(dom.DocumentElement, "barcode"); + string strName = DomUtil.GetElementText(dom.DocumentElement, "name"); + string strDepartment = DomUtil.GetElementText(dom.DocumentElement, "department"); + string strState = DomUtil.GetElementText(dom.DocumentElement, "state"); + string strCreateDate = DateTimeUtil.ToLocalTime(DomUtil.GetElementText(dom.DocumentElement, + "createDate"), "yyyy/MM/dd"); + string strExpireDate = DateTimeUtil.ToLocalTime(DomUtil.GetElementText(dom.DocumentElement, + "expireDate"), "yyyy/MM/dd"); + string strReaderType = DomUtil.GetElementText(dom.DocumentElement, + "readerType"); + string strComment = DomUtil.GetElementText(dom.DocumentElement, + "comment"); + + strMyInfo = "个人信息" + "\n" + + "姓名:" + strName + "\n" + + "证条码号:" + strReaderBarcode + "\n" + + "部门:" + strDepartment + "\n" + + "联系方式:\n" + GetContactString(dom) + "\n" + + "状态:" + strState + "\n" + + "有效期:" + strCreateDate + "~" + strExpireDate + "\n" + + "读者类别:" + strReaderType + "\n" + + "注释:" + strComment; + return 1; + } + + /// + /// -1 出错 + /// 0 未找到读者记录 + /// 1 成功 + /// + public int GetBorrowInfo(string remoteUserName, + string strReaderBarcode, + out string strBorrowInfo, + out string strError) + { + strError = ""; + strBorrowInfo = ""; + + + // 得到高级xml + string strXml = ""; + long lRet = this.GetPatronInfo(remoteUserName, + strReaderBarcode, + "advancexml,advancexml_borrow_bibliosummary", + out strXml, + out strError); + if (lRet == -1) + return -1; + + // 提取借书信息 + lRet = this.GetBorrowsInfoInternal(strXml, out strBorrowInfo); + if (lRet == -1) + return -1; + + + return 1; + + } + + /// + /// 详细借阅信息 + /// + /// + /// + /// + public int GetBorrowsInfoInternal(string strXml, out string strBorrowInfo) + { + strBorrowInfo = ""; + + XmlDocument dom = new XmlDocument(); + dom.LoadXml(strXml); + + /* + + + + 基本日历 + + + +'' + */ + string maxBorrowCount = ""; + string curBorrowCount = ""; + XmlNode nodeMax = dom.DocumentElement.SelectSingleNode("info/item[@name='可借总册数']"); + if (nodeMax == null) + { + maxBorrowCount = "获取当前读者可借总册数出错:未找到对应节点。"; + } + else + { + string maxCount = DomUtil.GetAttr(nodeMax, "value"); + if (maxCount == "") + { + maxBorrowCount = "获取当前读者可借总册数出错:未设置对应值。"; + } + else + { + maxBorrowCount = "最多可借:" + maxCount; ; + XmlNode nodeCurrent = dom.DocumentElement.SelectSingleNode("info/item[@name='当前还可借']"); + if (nodeCurrent == null) + { + curBorrowCount = "获取当前还可借出错:未找到对应节点。"; + } + else + { + curBorrowCount = "当前可借:" + DomUtil.GetAttr(nodeCurrent, "value"); + } + } + } + + strBorrowInfo = maxBorrowCount + " " + curBorrowCount + "\n"; + + XmlNodeList nodes = dom.DocumentElement.SelectNodes("borrows/borrow"); + if (nodes.Count == 0) + { + strBorrowInfo += "无借阅记录"; + return 0; + } + + Dictionary borrowLit = new Dictionary(); + int index = 1; + string books = ""; + foreach (XmlElement borrow in nodes) + { + if (books != "") + books += "===============\n"; + + string overdueText = ""; + string strIsOverdue = DomUtil.GetAttr(borrow, "isOverdue"); + if (strIsOverdue == "yes") + overdueText = DomUtil.GetAttr(borrow, "overdueInfo1"); + else + overdueText = "未超期"; + + + string itemBarcode = DomUtil.GetAttr(borrow, "barcode"); + borrowLit[index.ToString()] = itemBarcode; // 设到字典点,已变续借 + + /* + string bookName = DomUtil.GetAttr(borrow, "summary");//borrow.GetAttribute("summary") + int tempIndex = bookName.IndexOf('/'); + if (tempIndex > 0) + { + bookName = bookName.Substring(0, tempIndex); + } + */ + string summary = DomUtil.GetAttr(borrow, "summary"); + books += "编号:" + index.ToString() + "\n" + + "册条码号:" + itemBarcode + "\n" + + "摘 要:" + summary + "\n" + + "借阅时间:" + DateTimeUtil.ToLocalTime(borrow.GetAttribute("borrowDate"), "yyyy-MM-dd HH:mm") + "\n" + + "借 期:" + DateTimeUtil.GetDisplayTimePeriodString(borrow.GetAttribute("borrowPeriod")) + "\n" + + "应还时间:" + DateTimeUtil.ToLocalTime(borrow.GetAttribute("returningDate"), "yyyy-MM-dd") + "\n" + + "是否超期:" + overdueText + "\n"; + + + index++; //编号+1 + } + + strBorrowInfo += books; + + // 设到用户上下文 + //this.CurrentMessageContext.BorrowDict = borrowLit; + + return nodes.Count; + + } + + /// + /// 获取读者信息 + /// + /// + /// + /// + /// + /// + /// + public int GetPatronInfo(string remoteUserName, + string strReaderBarocde, //todo refID + string strFormat, + out string xml, + out string strError) + { + xml = ""; + strError = ""; + + CancellationToken cancel_token = new CancellationToken(); + + string id = Guid.NewGuid().ToString(); + SearchRequest request = new SearchRequest(id, + "getPatronInfo", + "", + strReaderBarocde, + "", + "", + "", + strFormat, + 1, + 0, + -1); + + try + { + MessageConnection connection = this._channels.GetConnectionAsync( + this.dp2MServerUrl, + remoteUserName).Result; + + SearchResult result = connection.SearchAsync( + remoteUserName, + request, + new TimeSpan(0, 1, 0), + cancel_token).Result; + + if (result.ResultCount == -1) + { + strError = result.ErrorInfo; + return -1; + } + + if (result.ResultCount == 0) + { + strError = result.ErrorInfo; + return -1; + } + + xml = result.Records[0].Data; + return 1; + + } + catch (AggregateException ex) + { + strError = MessageConnection.GetExceptionText(ex); + goto ERROR1; + } + catch (Exception ex) + { + strError = ex.Message; + goto ERROR1; + } + + ERROR1: + return -1; + } + + + /// + /// 根据code获取微信id + /// + /// + /// + /// + /// + /// + public int GetWeiXinId(string code, string state, out string weiXinId, + out string strError) + { + strError = ""; + weiXinId = ""; + + try + { + //可以传一个state用于校验 + if (state != "dp2weixin") + { + strError = "验证失败!请从正规途径进入!"; + return -1; + } + + //用code换取access_token + var result = OAuthApi.GetAccessToken(this.weiXinAppId, this.weiXinSecret, code); + if (result.errcode != ReturnCode.请求成功) + { + strError = "获取微信id出错:" + result.errmsg; + return -1; + } + + //下面2个数据也可以自己封装成一个类,储存在数据库中(建议结合缓存) + //如果可以确保安全,可以将access_token存入用户的cookie中,每一个人的access_token是不一样的 + //Session["OAuthAccessTokenStartTime"] = DateTime.Now; + //Session["OAuthAccessToken"] = result; + + // 取出微信id + weiXinId = result.openid; + return 0; + } + catch (Exception ex) + { + strError = ex.Message; + return -1; + + } + } + + /// + /// 发送客服消息 + /// + /// + /// + public void SendCustomerMsg(string openId, string text) + { + var accessToken = AccessTokenContainer.GetAccessToken(this.weiXinAppId); + CustomApi.SendText(accessToken, openId, "error"); + } + + /// + /// 得到的读者的联系方式 + /// + /// + /// + public static string GetContactString(XmlDocument dom) + { + string strTel = DomUtil.GetElementText(dom.DocumentElement, "tel"); + string strEmail = DomUtil.GetElementText(dom.DocumentElement, "email"); + string strAddress = DomUtil.GetElementText(dom.DocumentElement, "address"); + List list = new List(); + if (string.IsNullOrEmpty(strTel) == false) + list.Add(strTel); + if (string.IsNullOrEmpty(strEmail) == false) + { + //strEmail = JoinEmail(strEmail, ""); + list.Add(strEmail); + } + if (string.IsNullOrEmpty(strAddress) == false) + list.Add(strAddress); + return StringUtil.MakePathList(list, "; "); + } + + public int Renew(string remoteUserName, + string strReaderBarcode, + string strItemBarcode, + out BorrowInfo2 borrowInfo, + out string strError) + { + borrowInfo = null; + strError = "未实现"; + + return -1; + } + #region 错误日志 + + public void WriteErrorLog(string strText) + { + this.WriteLog("ERROR:" + strText); + } + + public void WriteLog(string strText) + { + // todo 有空比对下谢老师写日志的代码 + //DateTime now = DateTime.Now; + //// 每天一个日志文件 + //string strFilename = Path.Combine(this.LogDir, "log_" + DateTimeUtil.DateTimeToString8(now) + ".txt"); + //string strTime = now.ToString(); + //FileUtil.WriteText(strFilename, + // strTime + " " + strText + "\r\n"); + + var logDir = this.weiXinLogDir; + string strFilename = string.Format(logDir + "/log_{0}.txt", DateTime.Now.ToString("yyyyMMdd")); + FileUtil.WriteLog(strFilename, strText, "dp2weixin"); + } + + + /// + /// 获得异常信息 + /// + /// 异常对象 + /// + public static string GetExceptionMessage(Exception ex) + { + string strResult = ex.GetType().ToString() + ":" + ex.Message; + while (ex != null) + { + if (ex.InnerException != null) + strResult += "\r\n" + ex.InnerException.GetType().ToString() + ": " + ex.InnerException.Message; + + ex = ex.InnerException; + } + + return strResult; + } + + #endregion + } +} diff --git a/dp2weixin.service/dp2MessageHandler/dp2weixinMessageHandler.cs b/dp2weixin.service/dp2MessageHandler/dp2weixinMessageHandler.cs index 7ad2e175..dbc0298c 100644 --- a/dp2weixin.service/dp2MessageHandler/dp2weixinMessageHandler.cs +++ b/dp2weixin.service/dp2MessageHandler/dp2weixinMessageHandler.cs @@ -21,6 +21,7 @@ using Senparc.Weixin.MP.CommonAPIs; using System.Web.Mvc.Async; using System.Threading.Tasks; +using dp2weixin.service; using dp2Command.Service; namespace dp2weixin @@ -32,7 +33,7 @@ namespace dp2weixin public partial class dp2weixinMessageHandler : MessageHandler { // 由外面传进来的CommandServer - private dp2BaseCommandService CmdService = null; + private dp2WeiXinService CmdService = null; // 公众号程序目录,用于获取新书推荐与公告配置文件的路径 private string Dp2WeiXinAppDir = ""; // 是否显示消息路径 @@ -49,7 +50,7 @@ public partial class dp2weixinMessageHandler : MessageHandler /// /// - public dp2weixinMessageHandler(dp2BaseCommandService cmdServer, + public dp2weixinMessageHandler(dp2WeiXinService cmdServer, Stream inputStream, PostModel postModel, int maxRecordCount = 0) : base(inputStream, postModel, maxRecordCount) { @@ -308,6 +309,8 @@ public string getPatronList() private IResponseMessageBase DoSelectLib(string strParam) { + return this.CreateTextResponseMessage("未实现"); + /* // 设置当前命令 this.CurrentMessageContext.CurrentCmdName = dp2CommandUtility.C_Command_SelectLib; long lRet = 0; @@ -375,6 +378,7 @@ private IResponseMessageBase DoSelectLib(string strParam) this.CurrentMessageContext.LibUserName = userItem.libUserName; return this.CreateTextResponseMessage("您成功选择了图书馆[" + libCode + "]"); + */ } public string getLibList() @@ -401,6 +405,8 @@ public string getLibList() /// private IResponseMessageBase DoSearch(string strParam) { + return this.CreateTextResponseMessage("未实现"); + /* // 设置当前命令 this.CurrentMessageContext.CurrentCmdName = dp2CommandUtility.C_Command_Search; @@ -469,23 +475,10 @@ private IResponseMessageBase DoSearch(string strParam) // 返回空 var responseMessage = CreateResponseMessage(); responseMessage.Content = ""; - return responseMessage; + return responseMessage; - - /* - string strBiblioInfo = ""; - lRet = this.CmdService.GetDetailBiblioInfo(searchCmd, nBiblioIndex, - out strBiblioInfo, - out strError); - if (lRet == -1 || lRet==0) - { - return this.CreateTextResponseMessage(strError); - } - - // 输出详细信息 - return this.CreateTextResponseMessage(strBiblioInfo); - */ } + } // 检索 @@ -507,8 +500,10 @@ private IResponseMessageBase DoSearch(string strParam) { return this.CreateTextResponseMessage(strFirstPage); } + */ } + /* // 消息处理 public void SendBiblioDetail(SearchCommand searchCmd, int nBiblioIndex) { @@ -532,7 +527,7 @@ public void SendBiblioDetail(SearchCommand searchCmd, int nBiblioIndex) ((dp2CmdService2)this.CmdService).SendCustomerMsg(this.WeixinOpenId, strResult); //this.SendCustomeMessage(strResult); } - + */ /* // 消息处理 public void SendCustomeMessage(string strText) @@ -561,8 +556,8 @@ public void SendCustomeMessage(string strText) /// private IResponseMessageBase DoBinding(string strParam) { - - + return this.CreateTextResponseMessage("目前不支持该命令!"); + /* // 设置当前命令 this.CurrentMessageContext.CurrentCmdName = dp2CommandUtility.C_Command_Binding; @@ -618,6 +613,7 @@ private IResponseMessageBase DoBinding(string strParam) // 设到当前读者变量上 this.CurrentMessageContext.ReaderBarcode = strReaderBarcode; return this.CreateTextResponseMessage("绑定成功!"); + */ } /// @@ -626,6 +622,8 @@ private IResponseMessageBase DoBinding(string strParam) /// private IResponseMessageBase DoUnbinding() { + return this.CreateTextResponseMessage("不支持。"); + /* // 设置当前命令 this.CurrentMessageContext.CurrentCmdName = ""; @@ -658,6 +656,7 @@ private IResponseMessageBase DoUnbinding() // 置空当前读者变量上 this.CurrentMessageContext.ReaderBarcode = ""; return this.CreateTextResponseMessage("解除绑定成功。"); + */ } /// @@ -782,8 +781,8 @@ private IResponseMessageBase DoRenew(string strParam) // 目前只认作册条码,todo支持序号 - BorrowInfo borrowInfo = null; - lRet = this.CmdService.Renew(this.CurrentMessageContext.LibUserName, + BorrowInfo2 borrowInfo = null; + lRet = this.CmdService.Renew(this.CurrentMessageContext.LibUserName, this.CurrentMessageContext.ReaderBarcode, strParam, out borrowInfo, @@ -794,7 +793,7 @@ private IResponseMessageBase DoRenew(string strParam) } // 显示续借成功信息信息 - string returnTime = DateTimeUtil.ToLocalTime(borrowInfo.LatestReturnTime, "yyyy/MM/dd"); + string returnTime = DateTimeUtil.ToLocalTime(borrowInfo.returnDate, "yyyy/MM/dd"); string strText = strParam + "续借成功,还书日期为:" + returnTime + "。"; return this.CreateTextResponseMessage(strText); } @@ -852,6 +851,8 @@ private IResponseMessageBase CreateTextResponseMessage(string strText) /// private bool CheckIsSelectLib() { + return false; + /* if (String.IsNullOrEmpty(this.CurrentMessageContext.LibCode1) == true) { // 从mongodb中查 @@ -865,6 +866,7 @@ private bool CheckIsSelectLib() } return true; + */ } /// diff --git a/dp2weixin.service/dp2weixin.service.csproj b/dp2weixin.service/dp2weixin.service.csproj index 1f30bb17..deb33a2e 100644 --- a/dp2weixin.service/dp2weixin.service.csproj +++ b/dp2weixin.service/dp2weixin.service.csproj @@ -35,6 +35,18 @@ ..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll True + + False + ..\packages\MongoDB.Bson.2.2.4\lib\net45\MongoDB.Bson.dll + + + False + ..\packages\MongoDB.Driver.2.2.4\lib\net45\MongoDB.Driver.dll + + + False + ..\packages\MongoDB.Driver.Core.2.2.4\lib\net45\MongoDB.Driver.Core.dll + ..\packages\Senparc.Weixin.4.5.19\lib\net45\Senparc.Weixin.dll True @@ -77,19 +89,41 @@ + + + + + + + + + + + + + + + + {56eb94fd-45d0-4c82-ae8e-d398f1c4efcf} + DigitalPlatform.Common + + + {3c4f8133-8652-40b6-b37b-200b25c2b043} + DigitalPlatform.Interfaces + {441cb814-af78-4503-91ef-dce5f3269502} DigitalPlatform.IO @@ -98,6 +132,14 @@ {1b74d69a-530f-4275-845b-8ca6d1463bdd} DigitalPlatform.LibraryRestClient + + {e4b339d9-4843-40f1-a404-33043adcd6a3} + DigitalPlatform.MessageClient + + + {2fbb1294-0814-447d-b8c6-77e206417231} + DigitalPlatform.Message + {eeb5253b-ab62-4d06-896e-8f17e3ffd215} DigitalPlatform.Text @@ -106,10 +148,6 @@ {a62f763a-96ee-43ad-80c5-9ce57ecfeaf4} DigitalPlatform.Xml - - {a77b8a4a-3ae8-4bd7-b09a-bf8d92fe802d} - dp2Command.Service -