// 删除事项记录的操作 int DoOperDelete( SessionInfo sessioninfo, bool bForce, RmsChannel channel, EntityInfo info, List<string> oldLocateParams, /* string strIssueDbName, string strParentID, string strOldPublishTime, * */ XmlDocument domOldRec, ref XmlDocument domOperLog, ref List<EntityInfo> ErrorInfos) { int nRedoCount = 0; EntityInfo error = null; int nRet = 0; long lRet = 0; string strError = ""; /* // 如果newrecpath为空但是oldrecpath有值,就用oldrecpath的值 // 2007/10/23 if (String.IsNullOrEmpty(info.NewRecPath) == true) { if (String.IsNullOrEmpty(info.OldRecPath) == false) info.NewRecPath = info.OldRecPath; }*/ // 2008/6/24 if (String.IsNullOrEmpty(info.NewRecPath) == false) { if (info.NewRecPath != info.OldRecPath) { strError = "action为delete时, 如果info.NewRecPath不空,则其内容必须和info.OldRecPath一致。(info.NewRecPath='" + info.NewRecPath + "' info.OldRecPath='" + info.OldRecPath + "')"; return -1; } } else { info.NewRecPath = info.OldRecPath; } string strText = ""; // 构造定位提示信息。用于报错。 nRet = GetLocateText( oldLocateParams, out strText, out strError); if (nRet == -1) { strError = "GetLocateText()函数报错: " + strError; goto ERROR1; } // 如果记录路径为空, 则先获得记录路径 if (String.IsNullOrEmpty(info.NewRecPath) == true) { List<string> aPath = null; nRet = IsLocateParamNullOrEmpty( oldLocateParams, out strError); if (nRet == -1) goto ERROR1; if (nRet == 1) { strError += "info.OldRecord中的" + strError + " 和 info.RecPath参数值为空,同时出现,这是不允许的"; goto ERROR1; } /* RmsChannel channel = sessioninfo.Channels.GetChannel(this.App.WsUrl); if (channel == null) { strError = "get channel error"; goto ERROR1; } * */ // 本函数只负责查重, 并不获得记录体 // return: // -1 error // 其他 命中记录条数(不超过nMax规定的极限) nRet = this.SearchItemRecDup( // sessioninfo.Channels, channel, oldLocateParams, 100, out aPath, out strError); if (nRet == -1) { strError = "删除操作中事项查重阶段发生错误:" + strError; goto ERROR1; } if (nRet == 0) { error = new EntityInfo(info); error.ErrorInfo = strText + " 的记录已不存在"; error.ErrorCode = ErrorCodeValue.NotFound; ErrorInfos.Add(error); return -1; } if (nRet > 1) { /* string[] pathlist = new string[aPath.Count]; aPath.CopyTo(pathlist); * */ strError = strText + " 已经被下列多条事项记录使用了: " + StringUtil.MakePathList(aPath)/*String.Join(",", pathlist)*/ + "',这是一个严重的系统故障,请尽快通知系统管理员处理。"; goto ERROR1; } info.NewRecPath = aPath[0]; } Debug.Assert(String.IsNullOrEmpty(info.NewRecPath) == false, ""); // Debug.Assert(strEntityDbName != "", ""); byte[] exist_timestamp = null; string strOutputPath = ""; string strMetaData = ""; string strExistingXml = ""; REDOLOAD: // 先读出数据库中此位置的已有记录 lRet = channel.GetRes(info.NewRecPath, out strExistingXml, out strMetaData, out exist_timestamp, out strOutputPath, out strError); if (lRet == -1) { if (channel.ErrorCode == ChannelErrorCode.NotFound) { error = new EntityInfo(info); error.ErrorInfo = strText + " 的事项记录 '" + info.NewRecPath + "' 已不存在"; error.ErrorCode = channel.OriginErrorCode; ErrorInfos.Add(error); return -1; } else { error = new EntityInfo(info); error.ErrorInfo = "删除操作发生错误, 在读入原有记录 '" + info.NewRecPath + "' 阶段:" + strError; error.ErrorCode = channel.OriginErrorCode; ErrorInfos.Add(error); return -1; } } // 把记录装入DOM XmlDocument domExist = new XmlDocument(); try { domExist.LoadXml(strExistingXml); } catch (Exception ex) { strError = "strExistXml装载进入DOM时发生错误: " + ex.Message; goto ERROR1; } // 观察已存在的记录中,唯一性字段是否和要求的一致 // return: // -1 出错 // 0 一致 // 1 不一致。报错信息在strError中 nRet = IsLocateInfoCorrect( oldLocateParams, domExist, out strError); if (nRet != 0) goto ERROR1; if (bForce == false) { // 观察已经存在的记录是否有流通信息 // return: // -1 出错 // 0 没有 // 1 有。报错信息在strError中 nRet = HasCirculationInfo(domExist, out strError); if (nRet != 0) goto ERROR1; } if (bForce == false) { // 记录是否允许删除? // return: // -1 出错。不允许删除。 // 0 不允许删除,因为权限不够等原因。原因在strError中 // 1 可以删除 nRet = CanDelete( sessioninfo, domExist, out strError); if (nRet != 1) goto ERROR1; } // 比较时间戳 // 观察时间戳是否发生变化 nRet = ByteArray.Compare(info.OldTimestamp, exist_timestamp); if (nRet != 0) { // 2008/10/19 if (bForce == true) { error = new EntityInfo(info); error.NewTimestamp = exist_timestamp; // 让前端知道库中记录实际上发生过变化 error.ErrorInfo = "数据库中即将删除的册记录已经发生了变化,请重新装载、仔细核对后再行删除。"; error.ErrorCode = ErrorCodeValue.TimestampMismatch; ErrorInfos.Add(error); return -1; } // 如果前端给出了旧记录,就有和库中记录进行比较的基础 if (String.IsNullOrEmpty(info.OldRecord) == false) { // 比较两个记录, 看看和事项要害信息有关的字段是否发生了变化 // return: // 0 没有变化 // 1 有变化 nRet = IsItemInfoChanged(domExist, domOldRec); if (nRet == 1) { error = new EntityInfo(info); error.NewTimestamp = exist_timestamp; // 让前端知道库中记录实际上发生过变化 error.ErrorInfo = "数据库中即将删除的" + this.ItemName + "记录已经发生了变化,请重新装载、仔细核对后再行删除。"; error.ErrorCode = ErrorCodeValue.TimestampMismatch; ErrorInfos.Add(error); return -1; } } info.OldTimestamp = exist_timestamp; info.NewTimestamp = exist_timestamp; } byte[] output_timestamp = null; lRet = channel.DoDeleteRes(info.NewRecPath, info.OldTimestamp, out output_timestamp, out strError); if (lRet == -1) { if (channel.ErrorCode == ChannelErrorCode.TimestampMismatch) { if (nRedoCount > 10) { strError = "反复删除均遇到时间戳冲突, 超过10次重试仍然失败"; goto ERROR1; } // 发现时间戳不匹配 // 重复进行提取已存在记录\比较的过程 nRedoCount++; goto REDOLOAD; } error = new EntityInfo(info); error.NewTimestamp = output_timestamp; error.ErrorInfo = "删除操作发生错误:" + strError; error.ErrorCode = channel.OriginErrorCode; ErrorInfos.Add(error); return -1; } else { // 成功 DomUtil.SetElementText(domOperLog.DocumentElement, "action", "delete"); // 不创建<record>元素 // 创建<oldRecord>元素 XmlNode node = DomUtil.SetElementText(domOperLog.DocumentElement, "oldRecord", strExistingXml); DomUtil.SetAttr(node, "recPath", info.NewRecPath); // 如果删除成功,则不必要在数组中返回表示成功的信息元素了 } return 0; ERROR1: error = new EntityInfo(info); error.ErrorInfo = strError; error.ErrorCode = ErrorCodeValue.CommonError; ErrorInfos.Add(error); return -1; }
// 删除记录 // parameters: // strRecordPath 记录路径。如果==null,表示直接用textBox_recPath中当前的内容作为路径 public void DeleteRecord(string strRecordPath) { if (strRecordPath != null) textBox_recPath.Text = strRecordPath; if (textBox_recPath.Text == "") { MessageBox.Show(this, "路径不能为空"); return; } ResPath respath = new ResPath(textBox_recPath.Text); Uri uri = null; try { uri = new Uri(respath.Url); } catch (Exception ex) { MessageBox.Show(this, "路径错误: " + ex.Message); return; } // 保存到文件 if (uri.IsFile) { MessageBox.Show(this, "暂时不支持删除文件"); return; } string strText = "你确实要删除位于服务器 '"+respath.Url+"' 上的记录 '"+respath.Path + "' 吗?"; DialogResult msgResult = MessageBox.Show(this, strText, "dp2rms", MessageBoxButtons.OKCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2); if (msgResult != DialogResult.OK) { MessageBox.Show(this, "删除操作被放弃..."); return; } string strError; byte [] baOutputTimeStamp = null; // 使用Channel RmsChannel channelSave = channel; channel = Channels.GetChannel(respath.Url); Debug.Assert(channel != null, "Channels.GetChannel 异常"); try { stop.OnStop += new StopEventHandler(this.DoStop); stop.Initial("正在删除记录 " + respath.FullPath); stop.BeginLoop(); EnableControlsInLoading(true); long lRet = channel.DoDeleteRes(respath.Path, this.TimeStamp, out baOutputTimeStamp, out strError); EnableControlsInLoading(false); stop.EndLoop(); stop.OnStop -= new StopEventHandler(this.DoStop); stop.Initial(""); if (lRet == -1) { MessageBox.Show(this, "删除记录 '"+respath.Path+"' 失败,原因: "+strError); return; } } finally { channel = channelSave; } // 如果删除成功,原来时间戳遗留在this.TimeStamp中,也无害 MessageBox.Show(this, "删除记录 '" + respath.FullPath + "' 成功。"); }
// 复制属于同一书目记录的全部实体记录 // parameters: // strAction copy / move // return: // -2 目标实体库不存在,无法进行复制或者删除 // -1 error // >=0 实际复制或者移动的实体记录数 public int CopyBiblioChildItems(RmsChannel channel, string strAction, List<DeleteEntityInfo> entityinfos, string strTargetBiblioRecPath, XmlDocument domOperLog, out string strError) { strError = ""; if (entityinfos == null || entityinfos.Count == 0) return 0; int nOperCount = 0; XmlNode root = null; if (domOperLog != null) { root = domOperLog.CreateElement(strAction == "copy" ? "copy" + this.ItemNameInternal + "Records" : "move" + this.ItemNameInternal + "Records"); domOperLog.DocumentElement.AppendChild(root); } // 获得目标书目库下属的实体库名 string strTargetItemDbName = ""; string strTargetBiblioDbName = ResPath.GetDbName(strTargetBiblioRecPath); // return: // -1 出错 // 0 没有找到 // 1 找到 int nRet = this.GetItemDbName(strTargetBiblioDbName, out strTargetItemDbName, out strError); if (nRet == 0 || string.IsNullOrEmpty(strTargetItemDbName) == true) { return -2; // 目标实体库不存在 } string strParentID = ResPath.GetRecordId(strTargetBiblioRecPath); if (string.IsNullOrEmpty(strParentID) == true) { strError = "目标书目记录路径 '" + strTargetBiblioRecPath + "' 不正确,无法获得记录号"; return -1; } List<string> newrecordpaths = new List<string>(); List<string> oldrecordpaths = new List<string>(); for (int i = 0; i < entityinfos.Count; i++) { DeleteEntityInfo info = entityinfos[i]; byte[] output_timestamp = null; string strOutputRecPath = ""; // this.EntityLocks.LockForWrite(info.ItemBarcode); try { XmlDocument dom = new XmlDocument(); try { dom.LoadXml(info.OldRecord); } catch (Exception ex) { strError = "记录 '" + info.RecPath + "' 装入XMLDOM发生错误: " + ex.Message; goto ERROR1; } DomUtil.SetElementText(dom.DocumentElement, "parent", strParentID); // 复制的情况 if (strAction == "copy") { // 避免refID重复 DomUtil.SetElementText(dom.DocumentElement, "refID", null); } long lRet = channel.DoCopyRecord(info.RecPath, strTargetItemDbName + "/?", strAction == "move" ? true : false, // bDeleteSourceRecord out output_timestamp, out strOutputRecPath, out strError); if (lRet == -1) { if (channel.ErrorCode == ChannelErrorCode.NotFound) continue; strError = "复制" + this.ItemName + "记录 '" + info.RecPath + "' 时发生错误: " + strError; goto ERROR1; } // 2011/5/24 // 修改xml记录。<parent>元素发生了变化 byte[] baOutputTimestamp = null; string strOutputRecPath1 = ""; lRet = channel.DoSaveTextRes(strOutputRecPath, dom.OuterXml, false, "content", // ,ignorechecktimestamp output_timestamp, out baOutputTimestamp, out strOutputRecPath1, out strError); if (lRet == -1) goto ERROR1; oldrecordpaths.Add(info.RecPath); newrecordpaths.Add(strOutputRecPath); } finally { // this.EntityLocks.UnlockForWrite(info.ItemBarcode); } // 增补到日志DOM中 if (domOperLog != null) { Debug.Assert(root != null, ""); XmlNode node = domOperLog.CreateElement("record"); root.AppendChild(node); DomUtil.SetAttr(node, "recPath", info.RecPath); DomUtil.SetAttr(node, "targetRecPath", strOutputRecPath); } nOperCount++; } return nOperCount; ERROR1: // Undo已经进行过的操作 if (strAction == "copy") { string strWarning = ""; foreach (string strRecPath in newrecordpaths) { string strTempError = ""; byte[] timestamp = null; byte[] output_timestamp = null; REDO_DELETE: long lRet = channel.DoDeleteRes(strRecPath, timestamp, out output_timestamp, out strTempError); if (lRet == -1) { if (channel.ErrorCode == ChannelErrorCode.TimestampMismatch) { if (timestamp == null) { timestamp = output_timestamp; goto REDO_DELETE; } } strWarning += strTempError + ";"; } } if (string.IsNullOrEmpty(strWarning) == false) strError = strError + "。在Undo过程中,又遇到出错: " + strWarning; } else if (strAction == "move") { string strWarning = ""; for (int i = 0; i < newrecordpaths.Count; i++) { byte[] output_timestamp = null; string strOutputRecPath = ""; string strTempError = ""; long lRet = channel.DoCopyRecord(newrecordpaths[i], oldrecordpaths[i], true, // bDeleteSourceRecord out output_timestamp, out strOutputRecPath, out strTempError); if (lRet == -1) { strWarning += strTempError + ";"; } } if (string.IsNullOrEmpty(strWarning) == false) strError = strError + "。在Undo过程中,又遇到出错: " + strWarning; } return -1; }
// 删除属于同一书目记录的全部实体记录 // 这是需要提供EntityInfo数组的版本 // return: // -1 error // 0 没有找到属于书目记录的任何实体记录,因此也就无从删除 // >0 实际删除的实体记录数 public int DeleteBiblioChildItems( // RmsChannelCollection Channels, RmsChannel channel, List<DeleteEntityInfo> entityinfos, XmlDocument domOperLog, out string strError) { strError = ""; if (entityinfos == null || entityinfos.Count == 0) return 0; int nDeletedCount = 0; XmlNode root = null; if (domOperLog != null) { root = domOperLog.CreateElement("deleted"+this.ItemNameInternal+"Records"); domOperLog.DocumentElement.AppendChild(root); } #if NO RmsChannel channel = Channels.GetChannel(this.App.WsUrl); if (channel == null) { strError = "get channel error"; goto ERROR1; } #endif // 真正实行删除 for (int i = 0; i < entityinfos.Count; i++) { DeleteEntityInfo info = entityinfos[i]; byte[] output_timestamp = null; int nRedoCount = 0; REDO_DELETE: // this.EntityLocks.LockForWrite(info.ItemBarcode); try { long lRet = channel.DoDeleteRes(info.RecPath, info.OldTimestamp, out output_timestamp, out strError); if (lRet == -1) { if (channel.ErrorCode == ChannelErrorCode.NotFound) continue; // 如果不重试,让时间戳出错暴露出来。 // 如果要重试,也得加上重新读入册记录并判断重新判断无借还信息才能删除 if (channel.ErrorCode == ChannelErrorCode.TimestampMismatch) { if (nRedoCount > 10) { strError = "重试了10次还不行。删除"+this.ItemName+"记录 '" + info.RecPath + "' 时发生错误: " + strError; goto ERROR1; } nRedoCount++; // 重新读入记录 string strMetaData = ""; string strXml = ""; string strOutputPath = ""; string strError_1 = ""; lRet = channel.GetRes(info.RecPath, out strXml, out strMetaData, out output_timestamp, out strOutputPath, out strError_1); if (lRet == -1) { if (channel.ErrorCode == ChannelErrorCode.NotFound) continue; strError = "在删除"+this.ItemName+"记录 '" + info.RecPath + "' 时发生时间戳冲突,于是自动重新获取记录,但又发生错误: " + strError_1; goto ERROR1; // goto CONTINUE; } // 检查是否有借阅信息 // 把记录装入DOM XmlDocument domExist = new XmlDocument(); try { if (String.IsNullOrEmpty(strXml) == false) domExist.LoadXml(strXml); else domExist.LoadXml("<root />"); } catch (Exception ex) { strError = this.ItemName+"记录 '" + info.RecPath + "' XML装载进入DOM时发生错误: " + ex.Message; goto ERROR1; } /* info.ItemBarcode = DomUtil.GetElementText(domExist.DocumentElement, "barcode"); * */ // 观察已经存在的记录是否有流通信息 // return: // -1 出错 // 0 没有 // 1 有。报错信息在strError中 int nRet = this.HasCirculationInfo(domExist, out strError); if (nRet == -1) goto ERROR1; if (nRet == 1) { strError = "拟删除的"+this.ItemName+"记录 '" + info.RecPath + "' 中"+strError+"(此种情况可能不限于这一条),不能删除。"; goto ERROR1; } info.OldTimestamp = output_timestamp; goto REDO_DELETE; } strError = "删除"+this.ItemName+"记录 '" + info.RecPath + "' 时发生错误: " + strError; goto ERROR1; } } finally { // this.EntityLocks.UnlockForWrite(info.ItemBarcode); } // 增补到日志DOM中 if (domOperLog != null) { Debug.Assert(root != null, ""); XmlNode node = domOperLog.CreateElement("record"); root.AppendChild(node); DomUtil.SetAttr(node, "recPath", info.RecPath); } nDeletedCount++; } return nDeletedCount; ERROR1: return -1; }
// TODO: 各个环节要改为尽量使用 refID。要做大量测试 // text-level: 内部处理 // 在 预约到书 库中,追加一条新的记录,并作 email / dpmail / mq 通知 // 注:本函数可能要删除部分通知记录 // parameters: // strItemBarcode 册条码号。必须是册条码号。如果册条码号为空,参考ID需要使用 strRefID 参数 // strRefID 参考ID // bOnShelf 要通知的册是否在架。在架指并没有人借阅过,本来就在书架上。 // strLibraryCode 读者所在的馆代码 // strReaderXml 预约了图书的读者的XML记录。用于消息通知接口 int AddNotifyRecordToQueueDatabase( // RmsChannelCollection channels, RmsChannel channel, string strItemBarcodeParam, string strRefIDParam, string strItemXml, bool bOnShelf, string strLibraryCode, string strReaderBarcode, string strReaderXml, out List<string> DeletedNotifyRecPaths, out string strError) { strError = ""; DeletedNotifyRecPaths = new List<string>(); // 2010/12/31 if (String.IsNullOrEmpty(this.ArrivedDbName) == true) { strError = "预约到书库尚未定义, AddNotifyRecordToQueue()调用失败"; return -1; } // 准备写记录 byte[] timestamp = null; byte[] output_timestamp = null; string strOutputPath = ""; int nRet = 0; long lRet = 0; if (String.IsNullOrEmpty(strItemBarcodeParam) == true) { // 如果检索用的册条码号为空,加上对命中结果数量不设限,那就会造成系统严重繁忙。 strError = "参数strItemBarcode中的册条码号不能为空。"; return -1; } #if NO RmsChannel channel = channels.GetChannel(this.WsUrl); if (channel == null) { strError = "get channel error"; return -1; } #endif REDODELETE: // 如果队列中已经存在同册条码号的记录, 要先删除 string strNotifyXml = ""; // 获得预约到书队列记录 // return: // -1 error // 0 not found // 1 命中1条 // >1 命中多于1条 nRet = GetArrivedQueueRecXml( // channels, channel, strItemBarcodeParam, out strNotifyXml, out timestamp, out strOutputPath, out strError); if (nRet == -1) { // 写入错误日志? this.WriteErrorLog("在还书操作中,检索册条码号为 " + strItemBarcodeParam + " 的预约到书库记录时出错: " + strError); } if (nRet >= 1) { int nRedoDeleteCount = 0; // TODO: 这一段删除代码可以专门编制在一个函数中,不必这么费力循环。可以优化处理 REDO_DELETE: lRet = channel.DoDeleteRes(strOutputPath, timestamp, out output_timestamp, out strError); if (lRet == -1) { // 时间戳不匹配 if (channel.ErrorCode == ChannelErrorCode.TimestampMismatch && nRedoDeleteCount < 10) { nRedoDeleteCount++; timestamp = output_timestamp; goto REDO_DELETE; } // 写入错误日志? this.WriteErrorLog("在还书操作中,加入新预约到书记录前, 删除已存在的预约到书库记录 '" + strOutputPath + "' 出错: " + strError); } DeletedNotifyRecPaths.Add(strOutputPath); // 记忆已经被删除的记录路径 2007/7/5 goto REDODELETE; // 如果有多条,循环删除 } XmlDocument itemdom = null; nRet = LibraryApplication.LoadToDom(strItemXml, out itemdom, out strError); if (nRet == -1) { strError = "装载册记录 '" + strItemBarcodeParam + "' 的 XML 进入 DOM 时发生错误: " + strError; return -1; } string strLocation = DomUtil.GetElementText(itemdom.DocumentElement, "location"); strLocation = StringUtil.GetPureLocationString(strLocation); string strAccessNo = DomUtil.GetElementText(itemdom.DocumentElement, "accessNo"); /* <reservations> <request reader="R0000001" requestDate="Mon, 05 Sep 2016 16:57:47 +0800" operator="R0000001" /> </reservations> * */ // 从册记录 reservations 元素下找第一个 request 元素,其 requestDate 属性 string strRequestDate = ""; XmlElement request = itemdom.DocumentElement.SelectSingleNode("reservations/request") as XmlElement; if (request != null) strRequestDate = request.GetAttribute("requestDate"); // 创建预约到书记录 XmlDocument new_queue_dom = new XmlDocument(); new_queue_dom.LoadXml("<root />"); // TODO: 以后增加 <refID> 元素,存储册记录的参考ID #if NO XmlNode nodeItemBarcode = DomUtil.SetElementText(dom.DocumentElement, "itemBarcode", strItemBarcode); // 在<itemBarcode>元素中增加一个onShelf属性,表示属于在架情况 Debug.Assert(nodeItemBarcode != null, ""); if (bOnShelf == true) DomUtil.SetAttr(nodeItemBarcode, "onShelf", "true"); #endif string strItemRefID = ""; // 计划存储纯粹的 refid string strItemBarcode = strItemBarcodeParam; // 计划存储纯粹的册条码号 // 兼容 strItemBarcode 中含有前缀的用法 string strHead = "@refID:"; if (StringUtil.HasHead(strItemBarcodeParam, strHead, true) == true) { strItemRefID = strItemBarcodeParam.Substring(strHead.Length); strItemBarcode = ""; } string strUnionItemBarcode = GetUnionBarcode(strItemBarcode, strItemRefID); if (this.ArrivedDbKeysContainsRefIDKey() == true) { DomUtil.SetElementText(new_queue_dom.DocumentElement, "itemBarcode", strItemBarcode); DomUtil.SetElementText(new_queue_dom.DocumentElement, "refID", strItemRefID); } else { if (string.IsNullOrEmpty(strItemBarcode) == true) { if (string.IsNullOrEmpty(strItemRefID) == true) { strError = "AddNotifyRecordToQueue() 函数当 strItemBarcode 参数为空的时候,必须让 strRefID 参数不为空"; return -1; } Debug.Assert(string.IsNullOrEmpty(strItemRefID) == false, ""); // 旧的用法。避免检索时候查不到 DomUtil.SetElementText(new_queue_dom.DocumentElement, "itemBarcode", "@refID:" + strItemRefID); } else DomUtil.SetElementText(new_queue_dom.DocumentElement, "itemBarcode", strItemBarcode); // 2015/5/20 添加,修正 BUG } // 改为存储在元素中 2015/5/7 if (bOnShelf == true) DomUtil.SetElementText(new_queue_dom.DocumentElement, "onShelf", "true"); // 2012/10/26 DomUtil.SetElementText(new_queue_dom.DocumentElement, "libraryCode", strLibraryCode); DomUtil.SetElementText(new_queue_dom.DocumentElement, "readerBarcode", strReaderBarcode); DomUtil.SetElementText(new_queue_dom.DocumentElement, "notifyDate", this.Clock.GetClock()); // 2015/6/13 DomUtil.SetElementText(new_queue_dom.DocumentElement, "location", strLocation); string strPath = this.ArrivedDbName + "/?"; // 写新记录 lRet = channel.DoSaveTextRes( strPath, new_queue_dom.OuterXml, false, "content,ignorechecktimestamp", timestamp, out output_timestamp, out strOutputPath, out strError); if (lRet == -1) { // 写入错误日志 2007/1/3 this.WriteErrorLog("创建新的预约到书队列记录时出错: " + strError); return -1; } string strReaderEmailAddress = ""; string strName = ""; nRet = GetReaderNotifyInfo( strReaderXml, out strName, out strReaderEmailAddress, out strError); if (nRet == -1) return -1; // 获得图书摘要信息 string strSummary = ""; // 没有被截断的摘要字符串 string strShortSummary = ""; // 截断后的摘要字符串 string strBiblioRecPath = ""; nRet = this.GetBiblioSummary(strUnionItemBarcode, "", // strConfirmItemRecPath, null, // strBiblioRecPathExclude, -1, // 25, out strBiblioRecPath, out strSummary, out strError); if (nRet == -1) { strSummary = "ERROR: " + strError; } else { strShortSummary = LibraryApplication.CutSummary(strSummary, 25); } #if NO // 临时的SessionInfo对象 SessionInfo sessioninfo = new SessionInfo(this); // 模拟一个账户 Account account = new Account(); account.LoginName = "CacheBuilder"; account.Password = ""; account.Rights = "getbibliosummary"; account.Type = ""; account.Barcode = ""; account.Name = "AddNotifyRecordToQueue"; account.UserID = "AddNotifyRecordToQueue"; account.RmsUserName = this.ManagerUserName; account.RmsPassword = this.ManagerPassword; sessioninfo.Account = account; try { string strBiblioRecPath = ""; LibraryServerResult result = this.GetBiblioSummary( sessioninfo, strItemBarcode, "", // strConfirmItemRecPath, null, out strBiblioRecPath, out strSummary); if (result.Value == -1) { strSummary = "ERROR: " + result.ErrorInfo; } else { // 截断 if (strSummary.Length > 25) strSummary = strSummary.Substring(0, 25) + "..."; } } finally { sessioninfo.Close(); sessioninfo = null; } #endif // 发送短消息通知 string strTotalError = ""; // *** dpmail if (this.MessageCenter != null && StringUtil.IsInList("dpmail", this.ArrivedNotifyTypes)) { string strTemplate = ""; // 获得邮件模板 nRet = GetMailTemplate( "dpmail", bOnShelf == false ? "预约到书通知" : "预约到书通知(在架)", out strTemplate, out strError); if (nRet == -1) return -1; if (nRet == 0) { strError = "预约到书通知<mailTemplate/template>尚未配置。"; return -1; } /* %item% 册信息 %reservetime% 保留期限 %today% 发出email的当天 %summary% 书目摘要 %itembarcode% 册条码号 %name% 读者姓名 * */ Hashtable table = new Hashtable(); table["%item%"] = "(册条码号为: " + strUnionItemBarcode + " URL为: " + this.OpacServerUrl + "/book.aspx?barcode=" + strUnionItemBarcode + " )"; table["%reservetime%"] = this.GetDisplayTimePeriodStringEx(this.ArrivedReserveTimeSpan); table["%today%"] = DateTime.Now.ToString(); table["%summary%"] = strShortSummary; table["%itembarcode%"] = strUnionItemBarcode; table["%name%"] = strName; string strBody = ""; nRet = GetMailText(strTemplate, table, out strBody, out strError); if (nRet == -1) return -1; if (String.IsNullOrEmpty(this.MessageCenter.MessageDbName) == false) { Debug.Assert(channel.Container != null, ""); // 发送消息 nRet = this.MessageCenter.SendMessage( channel.Container, // channels, strReaderBarcode, "图书馆", "预约到书通知", "text", strBody, false, out strError); if (nRet == -1) { strTotalError += "发送dpmail消息时出错: " + strError + "\r\n"; } } } // 2016/4/26 // *** mq if (string.IsNullOrEmpty(this.OutgoingQueue) == false && StringUtil.IsInList("mq", this.ArrivedNotifyTypes)) { XmlDocument dom = new XmlDocument(); dom.LoadXml("<root />"); /* 元素名 * type 消息类型。预约到书通知 * itemBarcode 册条码号 * location 馆藏地 2016/9/5 * refID 册的参考 ID * onShelf 是否在架。true/false * opacURL 图书在 OPAC 中的 URL。相对路径 * requestDate 预约请求创建时间 2016/9/5 * reserveTime 保留的时间 * today 今天的日期 * summary 书目摘要 * patronName 读者姓名 * patronRecord 读者 XML 记录 * */ DomUtil.SetElementText(dom.DocumentElement, "type", "预约到书通知"); DomUtil.SetElementText(dom.DocumentElement, "itemBarcode", strItemBarcode); DomUtil.SetElementText(dom.DocumentElement, "location", strLocation); // 2016/11/15 DomUtil.SetElementText(dom.DocumentElement, "accessNo", strAccessNo); // 2016/9/5 if (string.IsNullOrEmpty(strItemRefID)) strItemRefID = DomUtil.GetElementText(itemdom.DocumentElement, "refID"); DomUtil.SetElementText(dom.DocumentElement, "refID", strItemRefID); DomUtil.SetElementText(dom.DocumentElement, "onShelf", bOnShelf ? "true" : "false"); DomUtil.SetElementText(dom.DocumentElement, "opacURL", this.OpacServerUrl + "/book.aspx?barcode=" + strUnionItemBarcode); DomUtil.SetElementText(dom.DocumentElement, "requestDate", strRequestDate); DomUtil.SetElementText(dom.DocumentElement, "reserveTime", this.GetDisplayTimePeriodStringEx(this.ArrivedReserveTimeSpan)); DomUtil.SetElementText(dom.DocumentElement, "today", DateTime.Now.ToString()); DomUtil.SetElementText(dom.DocumentElement, "summary", strSummary); DomUtil.SetElementText(dom.DocumentElement, "patronName", strName); string strReaderRefID = ""; { XmlDocument readerdom = new XmlDocument(); readerdom.LoadXml(strReaderXml); strReaderRefID = DomUtil.GetElementText(readerdom.DocumentElement, "refID"); XmlElement record = dom.CreateElement("patronRecord"); dom.DocumentElement.AppendChild(record); record.InnerXml = readerdom.DocumentElement.InnerXml; DomUtil.DeleteElement(record, "borrowHistory"); DomUtil.DeleteElement(record, "password"); DomUtil.DeleteElement(record, "fingerprint"); DomUtil.SetElementText(record, "libraryCode", strLibraryCode); } try { MessageQueue queue = new MessageQueue(this.OutgoingQueue); // 向 MSMQ 消息队列发送消息 nRet = ReadersMonitor.SendToQueue(queue, (string.IsNullOrEmpty(strReaderRefID) ? strReaderBarcode : "!refID:" + strReaderRefID) + "@LUID:" + this.UID, "xml", dom.DocumentElement.OuterXml, out strError); if (nRet == -1) { strTotalError += "发送 MQ 消息时出错: " + strError + "\r\n"; } } catch (Exception ex) { strTotalError += "创建路径为 '" + this.OutgoingQueue + "' 的 MessageQueue 对象失败: " + ExceptionUtil.GetDebugText(ex); } } // ** email if (String.IsNullOrEmpty(strReaderEmailAddress) == false && StringUtil.IsInList("email", this.ArrivedNotifyTypes)) { string strTemplate = ""; // 获得邮件模板 nRet = GetMailTemplate( "email", bOnShelf == false ? "预约到书通知" : "预约到书通知(在架)", out strTemplate, out strError); if (nRet == -1) return -1; if (nRet == 0) { strError = "预约到书通知<mailTemplate/template>尚未配置。"; return -1; } /* %item% 册信息 %reservetime% 保留期限 %today% 发出email的当天 %summary% 书目摘要 %itembarcode% 册条码号 %name% 读者姓名 * */ Hashtable table = new Hashtable(); table["%item%"] = "(册条码号为: " + strUnionItemBarcode + " URL为: " + this.OpacServerUrl + "/book.aspx?barcode=" + strUnionItemBarcode + " )"; table["%reservetime%"] = this.GetDisplayTimePeriodStringEx(this.ArrivedReserveTimeSpan); table["%today%"] = DateTime.Now.ToString(); table["%summary%"] = strShortSummary; table["%itembarcode%"] = strUnionItemBarcode; table["%name%"] = strName; string strBody = ""; nRet = GetMailText(strTemplate, table, out strBody, out strError); if (nRet == -1) return -1; { // 发送email // return: // -1 error // 0 not found smtp server cfg // 1 succeed nRet = SendEmail(strReaderEmailAddress, "预约到书通知", strBody, "text", out strError); if (nRet == -1) { strTotalError += "发送email消息时出错: " + strError + "\r\n"; } } } // *** external messageinterfaces if (this.m_externalMessageInterfaces != null) { foreach (MessageInterface message_interface in this.m_externalMessageInterfaces) { // types if (StringUtil.IsInList(message_interface.Type, this.ArrivedNotifyTypes) == false) continue; string strTemplate = ""; // 获得邮件模板 nRet = GetMailTemplate( message_interface.Type, bOnShelf == false ? "预约到书通知" : "预约到书通知(在架)", out strTemplate, out strError); if (nRet == -1) return -1; if (nRet == 0) { strError = "预约到书通知<mailTemplate/template>尚未配置。"; return -1; } /* %item% 册信息 %reservetime% 保留期限 %today% 发出email的当天 %summary% 书目摘要 %itembarcode% 册条码号 %name% 读者姓名 * */ Hashtable table = new Hashtable(); table["%item%"] = "(册条码号为: " + strUnionItemBarcode + " URL为: " + this.OpacServerUrl + "/book.aspx?barcode=" + strUnionItemBarcode + " )"; table["%reservetime%"] = this.GetDisplayTimePeriodStringEx(this.ArrivedReserveTimeSpan); table["%today%"] = DateTime.Now.ToString(); table["%summary%"] = strShortSummary; table["%itembarcode%"] = strUnionItemBarcode; table["%name%"] = strName; string strBody = ""; nRet = GetMailText(strTemplate, table, out strBody, out strError); if (nRet == -1) return -1; // 发送消息 nRet = message_interface.HostObj.SendMessage( strReaderBarcode, strReaderXml, strBody, strLibraryCode, out strError); if (nRet == -1) { strTotalError += "发送" + message_interface.Type + "消息时出错: " + strError + "\r\n"; } } } if (String.IsNullOrEmpty(strTotalError) == false) { strError = strTotalError; return -1; } return 0; }
// text-level: 内部处理 // 通知下一个预约者,或者(因没有下一个预约者了)而归架 // 调用前,需要先获得预约队列记录 // return: // -1 error // 0 正常 // 1 后面已经没有预约者,已通知管理员归架 public int DoNotifyNext( CachedRecordCollection records, RmsChannel channel, string strQueueRecPath, XmlDocument queue_rec_dom, byte[] baQueueRecTimeStamp, out string strError) { strError = ""; long lRet = 0; int nRet = 0; // RmsChannel channel = null; byte[] output_timestamp = null; string strState = DomUtil.GetElementText(queue_rec_dom.DocumentElement, "state"); string strNotifyDate = DomUtil.GetElementText(queue_rec_dom.DocumentElement, "notifyDate"); // XmlNode nodeItemBarcode = null; string strItemBarcode = DomUtil.GetElementText(queue_rec_dom.DocumentElement, "itemBarcode"/*, out nodeItemBarcode*/); // 2015/5/7 string strRefID = DomUtil.GetElementText(queue_rec_dom.DocumentElement, "refID"); string strReaderBarcode = DomUtil.GetElementText(queue_rec_dom.DocumentElement, "readerBarcode"); bool bOnShelf = false; #if NO // <itemBarcode>元素是否有onShelf属性。 if (nodeItemBarcode != null) { string strOnShelf = DomUtil.GetAttr(nodeItemBarcode, "onShelf"); if (strOnShelf.ToLower() == "true" || strOnShelf.ToLower() == "yes" || strOnShelf.ToLower() == "on") bOnShelf = true; } #endif // 2015/5/7 // <onShelf> 元素 { string strOnShelf = DomUtil.GetElementText(queue_rec_dom.DocumentElement, "onShelf"); if (DomUtil.IsBooleanTrue(strOnShelf, false) == true) bOnShelf = true; } // 2015/5/7 if (string.IsNullOrEmpty(strItemBarcode) == true && string.IsNullOrEmpty(strRefID) == false) { strItemBarcode = "@refID:" + strRefID; } // 要通知下一位预约者 string strReservationReaderBarcode = ""; // 清除读者和册记录中的已到预约事项,并提取下一个预约读者证条码号 // 本函数还负责清除册记录中以前残留的state=arrived的<request>元素 nRet = this.ClearArrivedInfo( records, channel, strReaderBarcode, strItemBarcode, bOnShelf, out strReservationReaderBarcode, out strError); if (nRet == -1) return -1; // 3) 通知预约到书的操作 List<string> DeletedNotifyRecPaths = null; // 被删除的通知记录。 if (String.IsNullOrEmpty(strReservationReaderBarcode) == false) { // 通知下一读者 // 出于对读者库加锁方面的便利考虑, 单独做了此函数 // return: // -1 error // 0 没有找到<request>元素 // 1 已成功处理 nRet = this.DoReservationNotify( records, channel, strReservationReaderBarcode, true, strItemBarcode, bOnShelf, false, // 不要修改当前册记录的<request> state属性,因为前面ClearArrivedInfo()中已经调用了DoItemReturnReservationCheck(), 修改了当前册的<request> state属性。 out DeletedNotifyRecPaths, out strError); if (nRet == -1) return -1; } else { // outof 的记录何时删除? // 册记录中的馆藏地点 #reservation何时消除?一个是现在就消除,一个是盘点模块扫描条码时消除。 // 把记录状态修改为 outofreservation DomUtil.SetElementText(queue_rec_dom.DocumentElement, "state", "outof"); // channel = channels.GetChannel(this.WsUrl); string strOutputPath = ""; lRet = channel.DoSaveTextRes(strQueueRecPath, queue_rec_dom.OuterXml, false, "content,ignorechecktimestamp", baQueueRecTimeStamp, out output_timestamp, out strOutputPath, out strError); if (lRet == -1) { strError = "写回预约到书记录 '" + strQueueRecPath + "' 时发生错误: " + strError; return -1; } // TODO: 通知馆员进行上架操作 // 可以在系统某目录不断追加到一个文本文件,工作人员可以查对。 // 格式:每行 册条码号 最后一个预约的读者证条码号 if (String.IsNullOrEmpty(this.StatisDir) == false) { string strLogFileName = this.StatisDir + "\\outof_reservation_" + Statis.GetCurrentDate() + ".txt"; StreamUtil.WriteText(strLogFileName, strItemBarcode + " " + strReaderBarcode + "\r\n"); } return 1; } // 4) 删除当前通知记录 // 2007/7/5 bool bAlreadeDeleted = false; if (DeletedNotifyRecPaths != null) { if (DeletedNotifyRecPaths.IndexOf(strQueueRecPath) != -1) bAlreadeDeleted = true; } if (bAlreadeDeleted == false) { lRet = channel.DoDeleteRes( strQueueRecPath, baQueueRecTimeStamp, out output_timestamp, out strError); if (lRet == -1) { strError = "DoNotifyNext()删除通知记录 '" + strQueueRecPath + "' 时失败: " + strError; return -1; } } return 0; }
public int SubmitLog(out string strErrorText) { strErrorText = ""; int nRet; ObjEventCollection log = this.Log; string strError = ""; for (int i = 0; i < log.Count; i++) { ObjEvent objevent = (ObjEvent)log[i]; if (objevent.Obj.Type == -1) // 不完整的对象 continue; if (objevent.Obj.Type == ResTree.RESTYPE_DB) continue; // 缺省配置文件,忽略操作 if (objevent.Oper == ObjEventOper.New || objevent.Oper == ObjEventOper.Change) { if (DatabaseObject.IsDefaultFile(objevent.Path) == true) continue; } if (objevent.Oper == ObjEventOper.New) { MemoryStream stream = null; try { if (objevent.Obj.Type == ResTree.RESTYPE_FILE && objevent.Obj.Content != null) stream = new MemoryStream(objevent.Obj.Content); string strPath = objevent.Path; byte[] baOutputTimestamp = null; nRet = NewServerSideObject(strPath, objevent.Obj.Type, stream, objevent.Obj.TimeStamp, out baOutputTimestamp, out strError); if (nRet == -1) { strError = "新建对象 '" + strPath + "' 时发生错误: " + strError; MessageBox.Show(this, strError); strErrorText += strError + "\r\n"; // return -1; } else { // 如果创建成功,需要把队列中后面的所有即将操作相同对象的动作修改时间戳 // 刷新队列中制定位置以后的、针对某个对象的全部操作的时间戳 RefreshTimestamp( i + 1, strPath, baOutputTimestamp); } } finally { if (stream != null) stream.Close(); } } if (objevent.Oper == ObjEventOper.Change) { MemoryStream stream = null; try { if (objevent.Obj.Type == ResTree.RESTYPE_FILE) stream = new MemoryStream(objevent.Obj.Content); string strPath = objevent.Path; byte[] baOutputTimestamp = null; nRet = NewServerSideObject(strPath, objevent.Obj.Type, stream, objevent.Obj.TimeStamp, out baOutputTimestamp, out strError); if (nRet == -1) { strError = "修改对象 '" + strPath + "' 时发生错误: " + strError; MessageBox.Show(this, strError); strErrorText += strError + "\r\n"; } else { // 如果创建成功,需要把队列中后面的所有即将操作相同对象的动作修改时间戳 // 刷新队列中制定位置以后的、针对某个对象的全部操作的时间戳 RefreshTimestamp( i + 1, strPath, baOutputTimestamp); } } finally { if (stream != null) stream.Close(); } } else if (objevent.Oper == ObjEventOper.Delete) { // TODO: 现在已经有时间戳了,可以不必重试 this.channel = Channels.GetChannel(this.ServerUrl); Debug.Assert(channel != null, "Channels.GetChannel() 异常"); byte[] baTimestamp = new byte[1]; byte[] baOutputTimestamp = null; string strPath = objevent.Path; // string strOutputPath = ""; REDO: // 删除数据库对象 long lRet = channel.DoDeleteRes(strPath, baTimestamp, "", out baOutputTimestamp, out strError); if (lRet == -1) { // 时间戳不匹配 if (channel.ErrorCode == ChannelErrorCode.TimestampMismatch) { baTimestamp = baOutputTimestamp; goto REDO; } strError = "删除对象 '" + strPath + "' 时发生错误: " + strError; MessageBox.Show(this, strError); strErrorText += strError + "\r\n"; } } } log.Clear(); if (strErrorText == "") return 0; return -1; }
// return: // -1 error // 0 正常结束 // 1 希望跳过后来的OnEnd() int DoExportFile( string[] dbpaths, string strOutputFileName, ExportFileType exportType, Encoding targetEncoding, out string strError) { strError = ""; int nRet = 0; string strDeleteStyle = ""; if (this.checkBox_export_fastMode.Checked == true) strDeleteStyle = "fastmode"; string strInfo = ""; // 汇总信息,在完成后显示 FileStream outputfile = null; // Backup和Xml格式输出都需要这个 XmlTextWriter writer = null; // Xml格式输出时需要这个 bool bAppend = true; Debug.Assert(dbpaths != null, ""); if (dbpaths.Length == 0) { strError = "尚未指定源库..."; goto ERROR1; } if (String.IsNullOrEmpty(strOutputFileName) == false) { // 探测输出文件是否已经存在 FileInfo fi = new FileInfo(strOutputFileName); bAppend = true; if (fi.Exists == true && fi.Length > 0) { if (exportType == ExportFileType.BackupFile || exportType == ExportFileType.ISO2709File) { DialogResult result = MessageBox.Show(this, "文件 '" + strOutputFileName + "' 已存在,是否追加?\r\n\r\n--------------------\r\n注:(是)追加 (否)覆盖 (取消)中断处理", "dp2batch", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1); if (result == DialogResult.Yes) { bAppend = true; } if (result == DialogResult.No) { bAppend = false; } if (result == DialogResult.Cancel) { strError = "放弃处理..."; goto ERROR1; } } else if (exportType == ExportFileType.XmlFile) { DialogResult result = MessageBox.Show(this, "文件 '" + strOutputFileName + "' 已存在,是否覆盖?\r\n\r\n--------------------\r\n注:(是)覆盖 (否)中断处理", "dp2batch", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2); if (result != DialogResult.Yes) { strError = "放弃处理..."; goto ERROR1; } } } // 打开文件 if (exportType == ExportFileType.BackupFile || exportType == ExportFileType.ISO2709File) { outputfile = File.Open( strOutputFileName, FileMode.OpenOrCreate, // 原来是Open,后来修改为OpenOrCreate。这样对临时文件被系统管理员手动意外删除(但是xml文件中仍然记载了任务)的情况能够适应。否则会抛出FileNotFoundException异常 FileAccess.Write, FileShare.ReadWrite); } else if (exportType == ExportFileType.XmlFile) { outputfile = File.Create( strOutputFileName); writer = new XmlTextWriter(outputfile, Encoding.UTF8); writer.Formatting = Formatting.Indented; writer.Indentation = 4; } } if ((exportType == ExportFileType.BackupFile || exportType == ExportFileType.ISO2709File) && outputfile != null) { if (bAppend == true) outputfile.Seek(0, SeekOrigin.End); // 具有追加的能力 else outputfile.SetLength(0); } WriteLog("开始输出"); try { // string[] dbpaths = textBox_dbPath.Text.Split(new char[] { ';' }); for (int f = 0; f < dbpaths.Length; f++) { string strOneDbPath = dbpaths[f]; ResPath respath = new ResPath(strOneDbPath); channel = this.Channels.GetChannel(respath.Url); string strDbName = respath.Path; if (String.IsNullOrEmpty(strInfo) == false) strInfo += "\r\n"; strInfo += "" + strDbName; // 实际处理的首尾号 string strRealStartNo = ""; string strRealEndNo = ""; /* DialogResult result; if (checkBox_export_delete.Checked == true) { result = MessageBox.Show(this, "确实要删除 '" + respath.Path + "' 内指定范围的记录?\r\n\r\n---------\r\n(是)删除 (否)放弃批处理", "dp2batch", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2); if (result != DialogResult.Yes) continue; } * * */ //channel = this.Channels.GetChannel(respath.Url); //string strDbName = respath.Path; // 如果为多库输出 if (dbpaths.Length > 0) { // 如果为全选 if (this.radioButton_all.Checked == true) { // 恢复为最大范围 this.textBox_startNo.Text = "1"; this.textBox_endNo.Text = "9999999999"; } // 校验起止号 if (checkBox_verifyNumber.Checked == true) { nRet = VerifyRange(channel, strDbName, out strError); if (nRet == -1) MessageBox.Show(this, strError); if (nRet == 0) { // 库中无记录 AutoCloseMessageBox.Show(this, "数据库 " + strDbName + " 中无记录。"); strInfo += "(无记录)"; WriteLog("发现数据库 " + strDbName + " 中无记录"); continue; } } else { if (this.textBox_startNo.Text == "") { strError = "尚未指定起始号"; goto ERROR1; } if (this.textBox_endNo.Text == "") { strError = "尚未指定结束号"; goto ERROR1; } } } string strOutputStartNo = ""; string strOutputEndNo = ""; // 虽然界面不让校验起止号,但是也要校验,为了设置好进度条 if (checkBox_verifyNumber.Checked == false) { // 校验起止号 // return: // 0 不存在记录 // 1 存在记录 nRet = VerifyRange(channel, strDbName, this.textBox_startNo.Text, this.textBox_endNo.Text, out strOutputStartNo, out strOutputEndNo, out strError); } //try //{ Int64 nStart = 0; Int64 nEnd = 0; Int64 nCur = 0; bool bAsc = true; bAsc = GetDirection( this.textBox_startNo.Text, this.textBox_endNo.Text, out nStart, out nEnd); // 探测到的号码 long nOutputEnd = 0; long nOutputStart = 0; if (checkBox_verifyNumber.Checked == false) { GetDirection( strOutputStartNo, strOutputEndNo, out nOutputStart, out nOutputEnd); } // 设置进度条范围 if (checkBox_verifyNumber.Checked == true) { Int64 nMax = nEnd - nStart; if (nMax < 0) nMax *= -1; nMax++; /* ProgressRatio = nMax / 10000; if (ProgressRatio < 1.0) ProgressRatio = 1.0; progressBar_main.Minimum = 0; progressBar_main.Maximum = (int)(nMax / ProgressRatio); progressBar_main.Value = 0; * */ stop.SetProgressRange(0, nMax); } else { Int64 nMax = nOutputEnd - nOutputStart; if (nMax < 0) nMax *= -1; nMax++; stop.SetProgressRange(0, nMax); } bool bFirst = true; // 是否为第一次取记录 string strID = this.textBox_startNo.Text; stop.OnStop += new StopEventHandler(this.DoStop); stop.Initial("正在导出数据"); stop.BeginLoop(); EnableControls(false); if (exportType == ExportFileType.XmlFile && writer != null) { writer.WriteStartDocument(); writer.WriteStartElement("dprms", "collection", DpNs.dprms); //writer.WriteStartElement("collection"); //writer.WriteAttributeString("xmlns:marc", // "http://www.loc.gov/MARC21/slim"); } WriteLog("开始输出数据库 '" + strDbName + "' 内的数据记录"); m_nRecordCount = 0; // 循环 for (; ; ) { Application.DoEvents(); // 出让界面控制权 if (stop.State != 0) { WriteLog("打开对话框 '确实要中断当前批处理操作?'"); DialogResult result = MessageBox.Show(this, "确实要中断当前批处理操作?", "dp2batch", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2); WriteLog("关闭对话框 '确实要中断当前批处理操作?'"); if (result == DialogResult.Yes) { strError = "用户中断"; goto ERROR1; } else { stop.Continue(); } } string strDirectionComment = ""; string strStyle = ""; if (outputfile != null) strStyle = "data,content,timestamp,outputpath"; else strStyle = "timestamp,outputpath"; // 优化 if (bFirst == true) { strStyle += ""; } else { if (bAsc == true) { strStyle += ",next"; strDirectionComment = "的后一条记录"; } else { strStyle += ",prev"; strDirectionComment = "的前一条记录"; } } string strPath = strDbName + "/" + strID; string strXmlBody = ""; string strMetaData = ""; byte[] baOutputTimeStamp = null; string strOutputPath = ""; bool bFoundRecord = false; bool bNeedRetry = true; REDO_GETRES: // 获得资源 // return: // -1 出错。具体出错原因在this.ErrorCode中。this.ErrorInfo中有出错信息。 // 0 成功 long lRet = channel.GetRes(strPath, strStyle, out strXmlBody, out strMetaData, out baOutputTimeStamp, out strOutputPath, out strError); if (lRet == -1) { if (channel.ErrorCode == ChannelErrorCode.NotFound) { if (bFirst == true) { if (checkBox_forceLoop.Checked == true) { string strText = "记录 " + strID + strDirectionComment + " 不存在。\r\n\r\n按 确认 继续。"; WriteLog("打开对话框 '" + strText.Replace("\r\n", "\\n") + "'"); AutoCloseMessageBox.Show(this, strText); WriteLog("关闭对话框 '" + strText.Replace("\r\n", "\\n") + "'"); bFirst = false; goto CONTINUE; } else { // 如果不要强制循环,此时也不能结束,否则会让用户以为数据库里面根本没有数据 string strText = "您为数据库 " + strDbName + " 指定的首记录 " + strID + strDirectionComment + " 不存在。\r\n\r\n(注:为避免出现此提示,可在操作前勾选“校准首尾ID”)\r\n\r\n按 确认 继续向后找..."; WriteLog("打开对话框 '" + strText.Replace("\r\n", "\\n") + "'"); AutoCloseMessageBox.Show(this, strText); WriteLog("关闭对话框 '" + strText.Replace("\r\n", "\\n") + "'"); bFirst = false; goto CONTINUE; } } else { Debug.Assert(bFirst == false, ""); if (bFirst == true) { strError = "记录 " + strID + strDirectionComment + " 不存在。处理结束。"; } else { if (bAsc == true) strError = "记录 " + strID + " 是最末一条记录。处理结束。"; else strError = "记录 " + strID + " 是最前一条记录。处理结束。"; } if (dbpaths.Length > 1) break; // 多库情况,继续其它库循环 else { bNeedRetry = false; // 单库情况,也没有必要出现重试对话框 WriteLog("打开对话框 '" + strError.Replace("\r\n", "\\n") + "'"); MessageBox.Show(this, strError); WriteLog("关闭对话框 '" + strError.Replace("\r\n", "\\n") + "'"); break; } } } else if (channel.ErrorCode == ChannelErrorCode.EmptyRecord) { bFirst = false; bFoundRecord = false; // 把id解析出来 strID = ResPath.GetRecordId(strOutputPath); goto CONTINUE; } // 允许重试 if (bNeedRetry == true) { string strText = "获取记录 '" + strPath + "' (style='" + strStyle + "')时出现错误: " + strError + "\r\n\r\n重试,还是中断当前批处理操作?\r\n(Retry 重试;Cancel 中断批处理)"; WriteLog("打开对话框 '" + strText.Replace("\r\n", "\\n") + "'"); DialogResult redo_result = MessageBox.Show(this, strText, "dp2batch", MessageBoxButtons.RetryCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1); WriteLog("关闭对话框 '" + strText.Replace("\r\n", "\\n") + "'"); if (redo_result == DialogResult.Cancel) goto ERROR1; goto REDO_GETRES; } else { goto ERROR1; } } // 2008/11/9 if (String.IsNullOrEmpty(strXmlBody) == true) { bFirst = false; bFoundRecord = false; // 把id解析出来 strID = ResPath.GetRecordId(strOutputPath); goto CONTINUE; } bFirst = false; bFoundRecord = true; // 把id解析出来 strID = ResPath.GetRecordId(strOutputPath); stop.SetMessage("已导出记录 " + strOutputPath + " " + m_nRecordCount.ToString()); if (String.IsNullOrEmpty(strRealStartNo) == true) { strRealStartNo = strID; } strRealEndNo = strID; CONTINUE: // 是否超过循环范围 try { nCur = Convert.ToInt64(strID); } catch { // ??? nCur = 0; } if (checkBox_verifyNumber.Checked == false) { // 如果当前记录号码突破预计的头部和尾部 if (nCur > nOutputEnd || nCur < nOutputStart) { if (nCur > nOutputEnd) nOutputEnd = nCur; if (nCur < nOutputStart) nOutputStart = nCur; // 重新计算和设置进度条 long nMax = nOutputEnd - nOutputStart; if (nMax < 0) nMax *= -1; nMax++; stop.SetProgressRange(0, nMax); } } if (bAsc == true && nCur > nEnd) break; if (bAsc == false && nCur < nEnd) break; string strMarc = ""; // 将Xml转换为MARC if (exportType == ExportFileType.ISO2709File && bFoundRecord == true) // 2008/11/13 { nRet = GetMarc(strXmlBody, out strMarc, out strError); if (nRet == -1) { strError = "记录 " + strOutputPath + " 在将XML格式转换为MARC时出错: " + strError; goto ERROR1; } } if (this.MarcFilter != null) { // 触发filter中的Record相关动作 // TODO: 有可能strMarc为空哟,需要测试一下 nRet = MarcFilter.DoRecord( null, strMarc, m_nRecordCount, out strError); if (nRet == -1) goto ERROR1; } // 触发Script的Outputing()代码 if (bFoundRecord == true && this.AssemblyMain != null) { // 这些变量要先初始化,因为filter代码可能用到这些Batch成员. batchObj.XmlRecord = strXmlBody; batchObj.MarcSyntax = this.CurMarcSyntax; batchObj.MarcRecord = strMarc; // MARC记录体 batchObj.MarcRecordChanged = false; // 为本轮Script运行准备初始状态 batchObj.SearchPanel.ServerUrl = channel.Url; batchObj.ServerUrl = channel.Url; batchObj.RecPath = strOutputPath; // 记录路径 batchObj.RecIndex = m_nRecordCount; // 当前记录在一批中的序号 batchObj.TimeStamp = baOutputTimeStamp; BatchEventArgs args = new BatchEventArgs(); batchObj.Outputing(this, args); /* if (args.Continue == ContinueType.SkipMiddle) goto CONTINUEDBS; if (args.Continue == ContinueType.SkipBeginMiddle) goto CONTINUEDBS; */ if (args.Continue == ContinueType.SkipAll) goto CONTINUEDBS; // 观察用于输出的MARC记录是否被改变 if (batchObj.MarcRecordChanged == true) strMarc = batchObj.MarcRecord; // 观察XML记录是否被改变 if (batchObj.XmlRecordChanged == true) strXmlBody = batchObj.XmlRecord; } if (bFoundRecord == true && outputfile != null) { if (exportType == ExportFileType.BackupFile) { // 写磁盘 nRet = WriteRecordToBackupFile( outputfile, strDbName, strID, strMetaData, strXmlBody, baOutputTimeStamp, out strError); if (nRet == -1) { // 询问是否继续 goto ERROR1; } } else if (exportType == ExportFileType.ISO2709File) { // 写磁盘 nRet = WriteRecordToISO2709File( outputfile, strDbName, strID, strMarc, baOutputTimeStamp, targetEncoding, this.OutputCrLf, this.AddG01, this.Remove998, out strError); if (nRet == -1) { // 询问是否继续 goto ERROR1; } } else if (exportType == ExportFileType.XmlFile) { XmlDocument dom = new XmlDocument(); try { dom.LoadXml(strXmlBody); ResPath respathtemp = new ResPath(); respathtemp.Url = channel.Url; respathtemp.Path = strOutputPath; // DomUtil.SetAttr(dom.DocumentElement, "xmlns:dprms", DpNs.dprms); // 给根元素设置几个参数 DomUtil.SetAttr(dom.DocumentElement, "path", DpNs.dprms, respathtemp.FullPath); DomUtil.SetAttr(dom.DocumentElement, "timestamp", DpNs.dprms, ByteArray.GetHexTimeStampString(baOutputTimeStamp)); // DomUtil.SetAttr(dom.DocumentElement, "xmlns:marc", null); dom.DocumentElement.WriteTo(writer); } catch (Exception ex) { strError = ExceptionUtil.GetAutoText(ex); // 询问是否继续 goto ERROR1; } } } // 删除 if (checkBox_export_delete.Checked == true) { byte[] baOutputTimeStamp1 = null; strPath = strOutputPath; // 得到实际的路径 lRet = channel.DoDeleteRes( strPath, baOutputTimeStamp, strDeleteStyle, out baOutputTimeStamp1, out strError); if (lRet == -1) { // 询问是否继续 goto ERROR1; } stop.SetMessage("已删除记录" + strPath + " " + m_nRecordCount.ToString()); } if (bFoundRecord == true) m_nRecordCount++; if (bAsc == true) { //progressBar_main.Value = (int)((nCur - nStart + 1) / ProgressRatio); stop.SetProgressValue(nCur - nStart + 1); } else { // ? // progressBar_main.Value = (int)((nStart - nCur + 1) / ProgressRatio); stop.SetProgressValue(nStart - nCur + 1); } // 对已经作过的进行判断 if (bAsc == true && nCur >= nEnd) break; if (bAsc == false && nCur <= nEnd) break; } // end of for one database stop.EndLoop(); stop.OnStop -= new StopEventHandler(this.DoStop); stop.Initial(""); EnableControls(true); //} CONTINUEDBS: strInfo += " : " + m_nRecordCount.ToString() + "条 (ID " + strRealStartNo + "-" + strRealEndNo + ")"; } // end of dbpaths loop } // end of try finally { if (writer != null) { writer.WriteEndElement(); writer.WriteEndDocument(); writer.Close(); writer = null; } if (outputfile != null) { outputfile.Close(); outputfile = null; } } // END1: channel = null; if (checkBox_export_delete.Checked == true) strError = "数据导出和删除完成。\r\n---\r\n" + strInfo; else strError = "数据导出完成。\r\n---\r\n" + strInfo; WriteLog("结束输出"); return 0; ERROR1: stop.EndLoop(); stop.OnStop -= new StopEventHandler(this.DoStop); stop.Initial(""); EnableControls(true); channel = null; return -1; }
// 删除期记录的操作 int DoIssueOperDelete( SessionInfo sessioninfo, RmsChannel channel, IssueInfo info, string strIssueDbName, string strParentID, string strOldPublishTime, string strNewPublishTime, // TODO: 本参数是否可以废除? XmlDocument domOldRec, ref XmlDocument domOperLog, ref List<IssueInfo> ErrorInfos) { int nRedoCount = 0; IssueInfo error = null; int nRet = 0; long lRet = 0; string strError = ""; // 如果newrecpath为空但是oldrecpath有值,就用oldrecpath的值 // 2007/10/23 if (String.IsNullOrEmpty(info.NewRecPath) == true) { if (String.IsNullOrEmpty(info.OldRecPath) == false) info.NewRecPath = info.OldRecPath; } // 如果记录路径为空, 则先获得记录路径 if (String.IsNullOrEmpty(info.NewRecPath) == true) { List<string> aPath = null; if (String.IsNullOrEmpty(strOldPublishTime) == true) { strError = "info.OldRecord中的<publishTime>元素中的出版时间,和info.RecPath参数值,不能同时为空。"; goto ERROR1; } // 本函数只负责查重, 并不获得记录体 // return: // -1 error // 其他 命中记录条数(不超过nMax规定的极限) nRet = this.SearchIssueRecDup( sessioninfo.Channels, strIssueDbName, strParentID, strOldPublishTime, 100, out aPath, out strError); if (nRet == -1) { strError = "删除操作中出版时间查重阶段发生错误:" + strError; goto ERROR1; } if (nRet == 0) { error = new IssueInfo(info); error.ErrorInfo = "父记录ID为 '"+strParentID+"', + 出版时间为 '" + strOldPublishTime + "' 的期记录已不存在"; error.ErrorCode = ErrorCodeValue.NotFound; ErrorInfos.Add(error); return -1; } if (nRet > 1) { string[] pathlist = new string[aPath.Count]; aPath.CopyTo(pathlist); // 在删除操作中,遇到重复的是很平常的事情。只要 // info.OldRecPath能够清晰地指出要删除的那一条,就可以执行删除 if (String.IsNullOrEmpty(info.OldRecPath) == false) { if (aPath.IndexOf(info.OldRecPath) == -1) { strError = "出版时间 '" + strOldPublishTime + "' 已经被下列多条期记录使用了: " + String.Join(",", pathlist) + "',但并不包括info.OldRecPath所指的路径 '" + info.OldRecPath + "'。"; goto ERROR1; } info.NewRecPath = info.OldRecPath; } else { strError = "出版时间 '" + strOldPublishTime + "' 已经被下列多条期记录使用了: " + String.Join(",", pathlist) + "',这是一个严重的系统故障,请尽快通知系统管理员处理。"; goto ERROR1; } } else { Debug.Assert(nRet == 1, ""); info.NewRecPath = aPath[0]; } /// /* if (nRet > 1) { string[] pathlist = new string[aPath.Count]; aPath.CopyTo(pathlist); strError = "出版时间 '" + strOldPublishTime + "' 已经被下列多条期记录使用了: " + String.Join(",", pathlist) + "',这是一个严重的系统故障,请尽快通知系统管理员处理。"; goto ERROR1; } info.NewRecPath = aPath[0]; * */ } Debug.Assert(String.IsNullOrEmpty(info.NewRecPath) == false, ""); // Debug.Assert(strEntityDbName != "", ""); byte[] exist_timestamp = null; string strOutputPath = ""; string strMetaData = ""; string strExistingXml = ""; REDOLOAD: // 先读出数据库中此位置的已有记录 lRet = channel.GetRes(info.NewRecPath, out strExistingXml, out strMetaData, out exist_timestamp, out strOutputPath, out strError); if (lRet == -1) { if (channel.ErrorCode == ChannelErrorCode.NotFound) { error = new IssueInfo(info); error.ErrorInfo = "出版时间为 '" + strOldPublishTime + "' 的期记录 '" + info.NewRecPath + "' 已不存在"; error.ErrorCode = channel.OriginErrorCode; ErrorInfos.Add(error); return -1; } else { error = new IssueInfo(info); error.ErrorInfo = "删除操作发生错误, 在读入原有记录 '" + info.NewRecPath + "' 阶段:" + strError; error.ErrorCode = channel.OriginErrorCode; ErrorInfos.Add(error); return -1; } } // 把记录装入DOM XmlDocument domExist = new XmlDocument(); try { domExist.LoadXml(strExistingXml); } catch (Exception ex) { strError = "strExistXml装载进入DOM时发生错误: " + ex.Message; goto ERROR1; } // 观察已经存在的记录中,出版时间是否和strOldPublishTime一致 if (String.IsNullOrEmpty(strOldPublishTime) == false) { string strExistingPublishTime = DomUtil.GetElementText(domExist.DocumentElement, "publishTime"); if (strExistingPublishTime != strOldPublishTime) { strError = "路径为 '" + info.NewRecPath + "' 的期记录中<publishTime>元素中的出版时间 '" + strExistingPublishTime + "' 和strOldXml中<publishTime>元素中的出版时间 '" + strOldPublishTime + "' 不一致。拒绝删除(如果允许删除,则会造成不经意删除了别的期记录的危险)。"; goto ERROR1; } } /* // 观察已经存在的记录是否有流通信息 if (IsIssueHasCirculationInfo(domExist) == true) { strError = "拟删除的期记录 '" + info.NewRecPath + "' 中包含有流通信息,不能删除。"; goto ERROR1; }*/ // 比较时间戳 // 观察时间戳是否发生变化 nRet = ByteArray.Compare(info.OldTimestamp, exist_timestamp); if (nRet != 0) { // 如果前端给出了旧记录,就有和库中记录进行比较的基础 if (String.IsNullOrEmpty(info.OldRecord) == false) { // 比较两个记录, 看看和期要害信息有关的字段是否发生了变化 // return: // 0 没有变化 // 1 有变化 nRet = IsIssueInfoChanged(domExist, domOldRec); if (nRet == 1) { error = new IssueInfo(info); error.NewTimestamp = exist_timestamp; // 让前端知道库中记录实际上发生过变化 error.ErrorInfo = "数据库中即将删除的期记录已经发生了变化,请重新装载、仔细核对后再行删除。"; error.ErrorCode = ErrorCodeValue.TimestampMismatch; ErrorInfos.Add(error); return -1; } } info.OldTimestamp = exist_timestamp; info.NewTimestamp = exist_timestamp; } byte[] output_timestamp = null; lRet = channel.DoDeleteRes(info.NewRecPath, info.OldTimestamp, out output_timestamp, out strError); if (lRet == -1) { if (channel.ErrorCode == ChannelErrorCode.TimestampMismatch) { if (nRedoCount > 10) { strError = "反复删除均遇到时间戳冲突, 超过10次重试仍然失败"; goto ERROR1; } // 发现时间戳不匹配 // 重复进行提取已存在记录\比较的过程 nRedoCount++; goto REDOLOAD; } error = new IssueInfo(info); error.NewTimestamp = output_timestamp; error.ErrorInfo = "删除操作发生错误:" + strError; error.ErrorCode = channel.OriginErrorCode; ErrorInfos.Add(error); return -1; } else { // 成功 DomUtil.SetElementText(domOperLog.DocumentElement, "action", "delete"); // 不创建<record>元素 XmlNode node = DomUtil.SetElementText(domOperLog.DocumentElement, "oldRecord", strExistingXml); DomUtil.SetAttr(node, "recPath", info.NewRecPath); // 如果删除成功,则不必要在数组中返回表示成功的信息元素了 } return 0; ERROR1: error = new IssueInfo(info); error.ErrorInfo = strError; error.ErrorCode = ErrorCodeValue.CommonError; ErrorInfos.Add(error); return -1; }
// 删除读者记录的操作 // return: // -2 记录中有流通信息,不能删除 // -1 出错 // 0 记录本来就不存在 // 1 记录成功删除 int DoReaderOperDelete( string strCurrentLibraryCode, string[] element_names, SessionInfo sessioninfo, bool bForce, RmsChannel channel, string strRecPath, string strOldXml, byte[] baOldTimestamp, string strOldBarcode, // string strNewBarcode, XmlDocument domOldRec, ref string strExistingXml, ref byte[] baNewTimestamp, ref XmlDocument domOperLog, ref DigitalPlatform.rms.Client.rmsws_localhost.ErrorCodeValue kernel_errorcode, out string strError) { strError = ""; int nRedoCount = 0; int nRet = 0; long lRet = 0; // 如果记录路径为空, 则先获得记录路径 if (String.IsNullOrEmpty(strRecPath) == true) { List<string> aPath = null; if (String.IsNullOrEmpty(strOldBarcode) == true) { strError = "strOldXml中的<barcode>元素中的证条码号,和strRecPath参数值,不能同时为空。"; goto ERROR1; } // 本函数只负责查重, 并不获得记录体 // return: // -1 error // 其他 命中记录条数(不超过nMax规定的极限) nRet = this.SearchReaderRecDup( // sessioninfo.Channels, channel, strOldBarcode, 100, out aPath, out strError); if (nRet == -1) goto ERROR1; if (nRet == 0) { strError = "证条码号为 '" + strOldBarcode + "' 的读者记录已不存在"; kernel_errorcode = ErrorCodeValue.NotFound; // goto ERROR1; return 0; // 2009/7/17 changed } if (nRet > 1) { /* string[] pathlist = new string[aPath.Count]; aPath.CopyTo(pathlist); * */ // 2007/11/22 // 在删除操作中,遇到重复的是很平常的事情。只要 // strRecPath能够清晰地指出要删除的那一条,就可以执行删除 if (String.IsNullOrEmpty(strRecPath) == false) { if (aPath.IndexOf(strRecPath) == -1) { strError = "证条码号 '" + strOldBarcode + "' 已经被下列多条读者记录使用了: " + StringUtil.MakePathList(aPath)/*String.Join(",", pathlist)*/ + "',但并不包括strRecPath所指的路径 '" + strRecPath + "'。删除操作失败。"; goto ERROR1; } } else { strError = "证条码号 '" + strOldBarcode + "' 已经被下列多条读者记录使用了: " + StringUtil.MakePathList(aPath)/*String.Join(",", pathlist)*/ + "',在未指定记录路径的情况下,无法定位和删除。"; goto ERROR1; } } else { strRecPath = aPath[0]; // strReaderDbName = ResPath.GetDbName(strRecPath); } } // 删除动作,API 的 strRecPath 参数可能为空,所以这里要单独检查一次 if (this.TestMode == true || sessioninfo.TestMode == true) { // 检查评估模式 // return: // -1 检查过程出错 // 0 可以通过 // 1 不允许通过 nRet = CheckTestModePath(strRecPath, out strError); if (nRet != 0) { strError = "删除读者记录的操作被拒绝: " + strError; goto ERROR1; } } // Debug.Assert(strReaderDbName != "", ""); byte[] exist_timestamp = null; string strOutputPath = ""; string strMetaData = ""; REDOLOAD: // 先读出数据库中此位置的已有记录 lRet = channel.GetRes(strRecPath, out strExistingXml, out strMetaData, out exist_timestamp, out strOutputPath, out strError); if (lRet == -1) { if (channel.ErrorCode == ChannelErrorCode.NotFound) { kernel_errorcode = channel.OriginErrorCode; goto ERROR1; } else { strError = "删除操作发生错误, 在读入原有记录阶段:" + strError; kernel_errorcode = channel.OriginErrorCode; goto ERROR1; } } // 把记录装入DOM XmlDocument domExist = new XmlDocument(); try { domExist.LoadXml(strExistingXml); } catch (Exception ex) { strError = "strExistXml装载进入DOM时发生错误: " + ex.Message; goto ERROR1; } string strExistingBarcode = DomUtil.GetElementText(domExist.DocumentElement, "barcode"); // 观察已经存在的记录中,证条码号是否和strOldBarcode一致 if (String.IsNullOrEmpty(strOldBarcode) == false) { if (strExistingBarcode != strOldBarcode) { strError = "路径为 '" + strRecPath + "' 的读者记录中<barcode>元素中的证条码号 '" + strExistingBarcode + "' 和strOldXml中<barcode>元素中的证条码号 '" + strOldBarcode + "' 不一致。拒绝删除(如果允许删除,则会造成不经意删除了别的读者记录的危险)。"; goto ERROR1; } } // 清除 LoginCache // this.LoginCache.Remove(strExistingBarcode); this.ClearLoginCache(strExistingBarcode); // 观察已经存在的记录是否有流通信息 string strDetailInfo = ""; bool bHasCirculationInfo = IsReaderHasCirculationInfo(domExist, out strDetailInfo); if (bForce == false) { if (bHasCirculationInfo == true) { strError = "删除操作被拒绝。因拟删除的读者记录 '" + strRecPath + "' 中包含有 " + strDetailInfo + ""; goto ERROR2; } } // 比较时间戳 // 观察时间戳是否发生变化 nRet = ByteArray.Compare(baOldTimestamp, exist_timestamp); if (nRet != 0) { // 2008/5/29 if (bForce == true) { strError = "数据库中即将删除的读者记录已经发生了变化,请重新装载、仔细核对后再行删除。"; kernel_errorcode = ErrorCodeValue.TimestampMismatch; baNewTimestamp = exist_timestamp; // 让前端知道库中记录实际上发生过变化 goto ERROR1; } // 是否报错? // 功能做的精细一点,需要比较strOldXml和strExistingXml中要害字段是否被改变了,如果没有改变,是不必报错的 // 如果前端给出了旧记录,就有和库中记录进行比较的基础 if (String.IsNullOrEmpty(strOldXml) == false) { // 比较两个记录, 看看和读者静态信息有关的字段是否发生了变化 // return: // 0 没有变化 // 1 有变化 nRet = IsReaderInfoChanged( element_names, domExist, domOldRec); if (nRet == 1) { strError = "数据库中即将删除的读者记录已经发生了变化,请重新装载、仔细核对后再行删除。"; kernel_errorcode = ErrorCodeValue.TimestampMismatch; baNewTimestamp = exist_timestamp; // 让前端知道库中记录实际上发生过变化 goto ERROR1; } } baOldTimestamp = exist_timestamp; baNewTimestamp = exist_timestamp; // 让前端知道库中记录实际上发生过变化 } string strLibraryCode = ""; // 观察一个读者记录路径,看看是不是在当前用户管辖的读者库范围内? if (this.IsCurrentChangeableReaderPath(strRecPath, strCurrentLibraryCode, out strLibraryCode) == false) { strError = "读者记录路径 '" + strRecPath + "' 的读者库不在当前用户管辖范围内"; goto ERROR1; } byte[] output_timestamp = null; Debug.Assert(strRecPath != "", ""); lRet = channel.DoDeleteRes(strRecPath, baOldTimestamp, out output_timestamp, out strError); if (lRet == -1) { // 2009/7/17 if (channel.ErrorCode == ChannelErrorCode.NotFound) { strError = "证条码号为 '" + strOldBarcode + "' 的读者记录(在删除的时候发现)已不存在"; kernel_errorcode = ErrorCodeValue.NotFound; return 0; } if (channel.ErrorCode == ChannelErrorCode.TimestampMismatch) { if (nRedoCount > 10) { strError = "反复删除均遇到时间戳冲突, 超过10次重试仍然失败"; baNewTimestamp = output_timestamp; kernel_errorcode = channel.OriginErrorCode; goto ERROR1; } // 发现时间戳不匹配 // 重复进行提取已存在记录\比较的过程 nRedoCount++; goto REDOLOAD; } baNewTimestamp = output_timestamp; strError = "删除操作发生错误:" + strError; kernel_errorcode = channel.OriginErrorCode; goto ERROR1; } else { // 成功 DomUtil.SetElementText(domOperLog.DocumentElement, "libraryCode", strLibraryCode); // 读者所在的馆代码 DomUtil.SetElementText(domOperLog.DocumentElement, "action", "delete"); // 不创建<record>元素 { XmlNode node = DomUtil.SetElementText(domOperLog.DocumentElement, "oldRecord", strExistingXml); DomUtil.SetAttr(node, "recPath", strRecPath); } // 2014/11/17 if (bForce == true) { XmlNode node = DomUtil.SetElementText(domOperLog.DocumentElement, "style", "force"); if (string.IsNullOrEmpty(strDetailInfo) == false && bHasCirculationInfo == true) DomUtil.SetAttr(node, "description", strDetailInfo); } // 如果删除成功,则不必要在数组中返回表示成功的信息元素了 /// if (this.Statis != null) this.Statis.IncreaseEntryValue(strLibraryCode, "修改读者信息", "删除记录数", 1); } return 1; ERROR1: kernel_errorcode = ErrorCodeValue.CommonError; domOperLog = null; // 表示不必写入日志 return -1; ERROR2: kernel_errorcode = ErrorCodeValue.CommonError; domOperLog = null; // 表示不必写入日志 return -2; }
// 结算一个交费记录 // parameters: // strLibraryCodeList 当前操作者管辖的图书馆代码 // bCreateOperLog 是否创建日志 // strOperTime 结算的操作时间 // strOperator 结算的操作者 // return: // -2 致命出错,不宜再继续循环调用本函数 // -1 一般出错,可以继续循环调用本函数 // 0 正常 int SettlementOneRecord( string strLibraryCodeList, bool bCreateOperLog, RmsChannel channel, string strAction, string strAmercedRecPath, string strOperTime, string strOperator, string strClientAddress, out string strError) { strError = ""; string strMetaData = ""; byte[] amerced_timestamp = null; string strOutputPath = ""; string strAmercedXml = ""; // 准备日志DOM XmlDocument domOperLog = null; if (bCreateOperLog == true) { } int nRedoCount = 0; REDO: long lRet = channel.GetRes(strAmercedRecPath, out strAmercedXml, out strMetaData, out amerced_timestamp, out strOutputPath, out strError); if (lRet == -1) { strError = "获取违约金记录 '" + strAmercedRecPath + "' 时出错: " + strError; return -1; } XmlDocument amerced_dom = null; int nRet = LibraryApplication.LoadToDom(strAmercedXml, out amerced_dom, out strError); if (nRet == -1) { strError = "装载违约金记录进入XML DOM时发生错误: " + strError; return -1; } string strLibraryCode = DomUtil.GetElementText(amerced_dom.DocumentElement, "libraryCode"); if (SessionInfo.IsGlobalUser(strLibraryCodeList) == false) { if (StringUtil.IsInList(strLibraryCode, strLibraryCodeList) == false) { strError = "当前用户未能管辖违约金记录 '" + strAmercedRecPath + "' 所在的馆代码 '" + strLibraryCode + "'"; return -1; } } if (bCreateOperLog == true) { domOperLog = new XmlDocument(); domOperLog.LoadXml("<root />"); // 2012/10/2 // 相关读者所在的馆代码 DomUtil.SetElementText(domOperLog.DocumentElement, "libraryCode", strLibraryCode); DomUtil.SetElementText(domOperLog.DocumentElement, "operation", "settlement"); DomUtil.SetElementText(domOperLog.DocumentElement, "action", strAction); // 在日志中记忆 id string strID = DomUtil.GetElementText(amerced_dom.DocumentElement, "id"); DomUtil.SetElementText(domOperLog.DocumentElement, "id", strID); } string strOldState = DomUtil.GetElementText(amerced_dom.DocumentElement, "state"); if (strAction == "settlement") { if (strOldState != "amerced") { strError = "结算操作前,记录状态必须为amerced。(但发现为'" + strOldState + "')"; return -1; } if (strOldState == "settlemented") { strError = "结算操作前,记录状态已经为settlemented"; return -1; } } else if (strAction == "undosettlement") { if (strOldState != "settlemented") { strError = "撤销结算操作前,记录状态必须为settlemented。(但发现为'" + strOldState + "')"; return -1; } if (strOldState == "amerced") { strError = "撤销结算操作前,记录状态已经为settlemented"; return -1; } } else if (strAction == "delete") { if (strOldState != "settlemented") { strError = "删除结算操作前,记录状态必须为settlemented。(但发现为'" + strOldState + "')"; return -1; } } else { strError = "无法识别的strAction参数值 '" + strAction + "'"; return -1; } byte[] output_timestamp = null; if (bCreateOperLog == true) { // oldAmerceRecord XmlNode nodeOldAmerceRecord = DomUtil.SetElementText(domOperLog.DocumentElement, "oldAmerceRecord", strAmercedXml); DomUtil.SetAttr(nodeOldAmerceRecord, "recPath", strAmercedRecPath); } if (strAction == "delete") { // 删除已结算违约金记录 lRet = channel.DoDeleteRes(strAmercedRecPath, amerced_timestamp, out output_timestamp, out strError); if (lRet == -1) { if (channel.ErrorCode == ChannelErrorCode.TimestampMismatch && nRedoCount < 10) { nRedoCount++; amerced_timestamp = output_timestamp; goto REDO; } strError = "删除已结算违约金记录 '" + strAmercedRecPath + "' 失败: " + strError; this.WriteErrorLog(strError); return -1; } goto END1; // 写日志 } // 修改状态 if (strAction == "settlement") { DomUtil.SetElementText(amerced_dom.DocumentElement, "state", "settlemented"); // 清除两个信息 DomUtil.DeleteElement(amerced_dom.DocumentElement, "undoSettlementOperTime"); DomUtil.DeleteElement(amerced_dom.DocumentElement, "undoSettlementOperator"); DomUtil.SetElementText(amerced_dom.DocumentElement, "settlementOperTime", strOperTime); DomUtil.SetElementText(amerced_dom.DocumentElement, "settlementOperator", strOperator); } else { Debug.Assert(strAction == "undosettlement", ""); DomUtil.SetElementText(amerced_dom.DocumentElement, "state", "amerced"); // 清除两个信息 DomUtil.SetElementText(amerced_dom.DocumentElement, "settlementOperTime", ""); DomUtil.SetElementText(amerced_dom.DocumentElement, "settlementOperator", ""); DomUtil.SetElementText(amerced_dom.DocumentElement, "undoSettlementOperTime", strOperTime); DomUtil.SetElementText(amerced_dom.DocumentElement, "undoSettlementOperator", strOperator); } if (bCreateOperLog == true) { DomUtil.SetElementText(domOperLog.DocumentElement, "operator", strOperator); // 操作者 DomUtil.SetElementText(domOperLog.DocumentElement, "operTime", strOperTime); // 操作时间 } // 保存回数据库 lRet = channel.DoSaveTextRes(strAmercedRecPath, amerced_dom.OuterXml, false, "content", // ?????,ignorechecktimestamp amerced_timestamp, out output_timestamp, out strOutputPath, out strError); if (lRet == -1) { strError = "写回违约金记录 '" + strAmercedRecPath + "' 时出错: " + strError; return -1; } if (bCreateOperLog == true) { // amerceRecord XmlNode nodeAmerceRecord = DomUtil.SetElementText(domOperLog.DocumentElement, "amerceRecord", amerced_dom.OuterXml); DomUtil.SetAttr(nodeAmerceRecord, "recPath", strAmercedRecPath); } END1: if (bCreateOperLog == true) { if (this.Statis != null) { if (strAction == "settlement") this.Statis.IncreaseEntryValue( strLibraryCode, "费用结算", "结算记录数", 1); else if (strAction == "undosettlement") this.Statis.IncreaseEntryValue( strLibraryCode, "费用结算", "撤销结算记录数", 1); else if (strAction == "delete") this.Statis.IncreaseEntryValue( strLibraryCode, "费用结算", "删除结算记录数", 1); } nRet = this.OperLog.WriteOperLog(domOperLog, strClientAddress, out strError); if (nRet == -1) { strError = "settlement() API 写入日志时发生错误: " + strError; return -2; } } return 0; }
// text-level: 内部处理 // 在 预约到书 库中,追加一条新的记录 // 并作email通知 // 注:本函数可能要删除部分通知记录 // parameters: // strItemBarcode 册条码号。必须是册条码号。如果册条码号为空,参考ID需要使用 strRefID 参数 // strRefID 参考ID // bOnShelf 要通知的册是否在架。在架指并没有人借阅过,本来就在书架上。 // strLibraryCode 读者所在的馆代码 // strReaderXml 预约了图书的读者的XML记录。用于消息通知接口 int AddNotifyRecordToQueue( // RmsChannelCollection channels, RmsChannel channel, string strItemBarcode, string strRefID, string strItemXml, bool bOnShelf, string strLibraryCode, string strReaderBarcode, string strReaderXml, out List<string> DeletedNotifyRecPaths, out string strError) { strError = ""; DeletedNotifyRecPaths = new List<string>(); // 2010/12/31 if (String.IsNullOrEmpty(this.ArrivedDbName) == true) { strError = "预约到书库尚未定义, AddNotifyRecordToQueue()调用失败"; return -1; } // 准备写记录 byte[] timestamp = null; byte[] output_timestamp = null; string strOutputPath = ""; int nRet = 0; long lRet = 0; if (String.IsNullOrEmpty(strItemBarcode) == true) { // 如果检索用的册条码号为空,加上对命中结果数量不设限,那就会造成系统严重繁忙。 strError = "参数strItemBarcode中的册条码号不能为空。"; return -1; } #if NO RmsChannel channel = channels.GetChannel(this.WsUrl); if (channel == null) { strError = "get channel error"; return -1; } #endif REDODELETE: // 如果队列中已经存在同册条码号的记录, 要先删除 string strNotifyXml = ""; // 获得预约到书队列记录 // return: // -1 error // 0 not found // 1 命中1条 // >1 命中多于1条 nRet = GetArrivedQueueRecXml( // channels, channel, strItemBarcode, out strNotifyXml, out timestamp, out strOutputPath, out strError); if (nRet == -1) { // 写入错误日志? this.WriteErrorLog("在还书操作中,检索册条码号为 " + strItemBarcode + " 的预约到书库记录时出错: " + strError); } if (nRet >= 1) { int nRedoDeleteCount = 0; // TODO: 这一段删除代码可以专门编制在一个函数中,不必这么费力循环。可以优化处理 REDO_DELETE: lRet = channel.DoDeleteRes(strOutputPath, timestamp, out output_timestamp, out strError); if (lRet == -1) { // 时间戳不匹配 if (channel.ErrorCode == ChannelErrorCode.TimestampMismatch && nRedoDeleteCount < 10) { nRedoDeleteCount++; timestamp = output_timestamp; goto REDO_DELETE; } // 写入错误日志? this.WriteErrorLog("在还书操作中,加入新预约到书记录前, 删除已存在的预约到书库记录 '" + strOutputPath + "' 出错: " + strError); } DeletedNotifyRecPaths.Add(strOutputPath); // 记忆已经被删除的记录路径 2007/7/5 goto REDODELETE; // 如果有多条,循环删除 } XmlDocument itemdom = null; nRet = LibraryApplication.LoadToDom(strItemXml, out itemdom, out strError); if (nRet == -1) { strError = "装载册记录 '"+strItemBarcode+"' 的 XML 进入 DOM 时发生错误: " + strError; return -1; } string strLocation = DomUtil.GetElementText(itemdom.DocumentElement, "location"); strLocation = StringUtil.GetPureLocationString(strLocation); // 创建预约到书记录 XmlDocument new_queue_dom = new XmlDocument(); new_queue_dom.LoadXml("<root />"); // TODO: 以后增加 <refID> 元素,存储册记录的参考ID #if NO XmlNode nodeItemBarcode = DomUtil.SetElementText(dom.DocumentElement, "itemBarcode", strItemBarcode); // 在<itemBarcode>元素中增加一个onShelf属性,表示属于在架情况 Debug.Assert(nodeItemBarcode != null, ""); if (bOnShelf == true) DomUtil.SetAttr(nodeItemBarcode, "onShelf", "true"); #endif // 兼容 strItemBarcode 中含有前缀的用法 string strHead = "@refID:"; if (StringUtil.HasHead(strItemBarcode, strHead, true) == true) { strRefID = strItemBarcode.Substring(strHead.Length); strItemBarcode = ""; } if (this.ArrivedDbKeysContainsRefIDKey() == true) { DomUtil.SetElementText(new_queue_dom.DocumentElement, "itemBarcode", strItemBarcode); DomUtil.SetElementText(new_queue_dom.DocumentElement, "refID", strRefID); } else { if (string.IsNullOrEmpty(strItemBarcode) == true) { if (string.IsNullOrEmpty(strRefID) == true) { strError = "AddNotifyRecordToQueue() 函数当 strItemBarcode 参数为空的时候,必须让 strRefID 参数不为空"; return -1; } Debug.Assert(string.IsNullOrEmpty(strRefID) == false, ""); // 旧的用法。避免检索时候查不到 DomUtil.SetElementText(new_queue_dom.DocumentElement, "itemBarcode", "@refID:" + strRefID); } else DomUtil.SetElementText(new_queue_dom.DocumentElement, "itemBarcode", strItemBarcode); // 2015/5/20 添加,修正 BUG } // 改为存储在元素中 2015/5/7 if (bOnShelf == true) DomUtil.SetElementText(new_queue_dom.DocumentElement, "onShelf", "true"); // 2012/10/26 DomUtil.SetElementText(new_queue_dom.DocumentElement, "libraryCode", strLibraryCode); DomUtil.SetElementText(new_queue_dom.DocumentElement, "readerBarcode", strReaderBarcode); DomUtil.SetElementText(new_queue_dom.DocumentElement, "notifyDate", this.Clock.GetClock()); // 2015/6/13 DomUtil.SetElementText(new_queue_dom.DocumentElement, "location", strLocation); string strPath = this.ArrivedDbName + "/?"; // 写新记录 lRet = channel.DoSaveTextRes( strPath, new_queue_dom.OuterXml, false, "content,ignorechecktimestamp", timestamp, out output_timestamp, out strOutputPath, out strError); if (lRet == -1) { // 写入错误日志 2007/1/3 this.WriteErrorLog("创建新的预约到书队列记录时出错: " + strError); return -1; } string strReaderEmailAddress = ""; string strName = ""; nRet = GetReaderNotifyInfo( strReaderXml, out strName, out strReaderEmailAddress, out strError); if (nRet == -1) return -1; // 获得图书摘要信息 string strSummary = ""; string strBiblioRecPath = ""; nRet = this.GetBiblioSummary(strItemBarcode, "", // strConfirmItemRecPath, null, // strBiblioRecPathExclude, 25, out strBiblioRecPath, out strSummary, out strError); if (nRet == -1) { strSummary = "ERROR: " + strError; } #if NO // 临时的SessionInfo对象 SessionInfo sessioninfo = new SessionInfo(this); // 模拟一个账户 Account account = new Account(); account.LoginName = "CacheBuilder"; account.Password = ""; account.Rights = "getbibliosummary"; account.Type = ""; account.Barcode = ""; account.Name = "AddNotifyRecordToQueue"; account.UserID = "AddNotifyRecordToQueue"; account.RmsUserName = this.ManagerUserName; account.RmsPassword = this.ManagerPassword; sessioninfo.Account = account; try { string strBiblioRecPath = ""; LibraryServerResult result = this.GetBiblioSummary( sessioninfo, strItemBarcode, "", // strConfirmItemRecPath, null, out strBiblioRecPath, out strSummary); if (result.Value == -1) { strSummary = "ERROR: " + result.ErrorInfo; } else { // 截断 if (strSummary.Length > 25) strSummary = strSummary.Substring(0, 25) + "..."; } } finally { sessioninfo.Close(); sessioninfo = null; } #endif // 发送短消息通知 string strTotalError = ""; // *** dpmail if (this.MessageCenter != null) { string strTemplate = ""; // 获得邮件模板 nRet = GetMailTemplate( "dpmail", bOnShelf == false ? "预约到书通知" : "预约到书通知(在架)", out strTemplate, out strError); if (nRet == -1) return -1; if (nRet == 0) { strError = "预约到书通知<mailTemplate/template>尚未配置。"; return -1; } /* %item% 册信息 %reservetime% 保留期限 %today% 发出email的当天 %summary% 书目摘要 %itembarcode% 册条码号 %name% 读者姓名 * */ Hashtable table = new Hashtable(); table["%item%"] = "(册条码号为: " + strItemBarcode + " URL为: " + this.OpacServerUrl + "/book.aspx?barcode=" + strItemBarcode + " )"; table["%reservetime%"] = this.GetDisplayTimePeriodStringEx(this.ArrivedReserveTimeSpan); table["%today%"] = DateTime.Now.ToString(); table["%summary%"] = strSummary; table["%itembarcode%"] = strItemBarcode; table["%name%"] = strName; string strBody = ""; nRet = GetMailText(strTemplate, table, out strBody, out strError); if (nRet == -1) return -1; if (String.IsNullOrEmpty(this.MessageCenter.MessageDbName) == false) { Debug.Assert(channel.Container != null, ""); // 发送消息 nRet = this.MessageCenter.SendMessage( channel.Container, // channels, strReaderBarcode, "图书馆", "预约到书通知", "text", strBody, false, out strError); if (nRet == -1) { strTotalError += "发送dpmail消息时出错: " + strError + "\r\n"; } } } // ** email if (String.IsNullOrEmpty(strReaderEmailAddress) == false) { string strTemplate = ""; // 获得邮件模板 nRet = GetMailTemplate( "email", bOnShelf == false ? "预约到书通知" : "预约到书通知(在架)", out strTemplate, out strError); if (nRet == -1) return -1; if (nRet == 0) { strError = "预约到书通知<mailTemplate/template>尚未配置。"; return -1; } /* %item% 册信息 %reservetime% 保留期限 %today% 发出email的当天 %summary% 书目摘要 %itembarcode% 册条码号 %name% 读者姓名 * */ Hashtable table = new Hashtable(); table["%item%"] = "(册条码号为: " + strItemBarcode + " URL为: " + this.OpacServerUrl + "/book.aspx?barcode=" + strItemBarcode + " )"; table["%reservetime%"] = this.GetDisplayTimePeriodStringEx(this.ArrivedReserveTimeSpan); table["%today%"] = DateTime.Now.ToString(); table["%summary%"] = strSummary; table["%itembarcode%"] = strItemBarcode; table["%name%"] = strName; string strBody = ""; nRet = GetMailText(strTemplate, table, out strBody, out strError); if (nRet == -1) return -1; { // 发送email // return: // -1 error // 0 not found smtp server cfg // 1 succeed nRet = SendEmail(strReaderEmailAddress, "预约到书通知", strBody, "text", out strError); if (nRet == -1) { strTotalError += "发送email消息时出错: " + strError + "\r\n"; } } } // *** external messageinterfaces if (this.m_externalMessageInterfaces != null) { foreach (MessageInterface message_interface in this.m_externalMessageInterfaces) { string strTemplate = ""; // 获得邮件模板 nRet = GetMailTemplate( message_interface.Type, bOnShelf == false ? "预约到书通知" : "预约到书通知(在架)", out strTemplate, out strError); if (nRet == -1) return -1; if (nRet == 0) { strError = "预约到书通知<mailTemplate/template>尚未配置。"; return -1; } /* %item% 册信息 %reservetime% 保留期限 %today% 发出email的当天 %summary% 书目摘要 %itembarcode% 册条码号 %name% 读者姓名 * */ Hashtable table = new Hashtable(); table["%item%"] = "(册条码号为: " + strItemBarcode + " URL为: " + this.OpacServerUrl + "/book.aspx?barcode=" + strItemBarcode + " )"; table["%reservetime%"] = this.GetDisplayTimePeriodStringEx(this.ArrivedReserveTimeSpan); table["%today%"] = DateTime.Now.ToString(); table["%summary%"] = strSummary; table["%itembarcode%"] = strItemBarcode; table["%name%"] = strName; string strBody = ""; nRet = GetMailText(strTemplate, table, out strBody, out strError); if (nRet == -1) return -1; // 发送消息 nRet = message_interface.HostObj.SendMessage( strReaderBarcode, strReaderXml, strBody, strLibraryCode, out strError); if (nRet == -1) { strTotalError += "发送"+message_interface.Type+"消息时出错: " + strError + "\r\n"; } } } if (String.IsNullOrEmpty(strTotalError) == false) { strError = strTotalError; return -1; } return 0; }
// 删除属于同一书目记录的全部实体记录 // 这是需要提供EntityInfo数组的版本 // return: // -1 error // 0 没有找到属于书目记录的任何实体记录,因此也就无从删除 // >0 实际删除的实体记录数 public int DeleteBiblioChildEntities(RmsChannel channel, List<DeleteEntityInfo> entityinfos, XmlDocument domOperLog, out string strError) { strError = ""; if (entityinfos == null || entityinfos.Count == 0) return 0; int nDeletedCount = 0; XmlNode root = null; if (domOperLog != null) { root = domOperLog.CreateElement("deletedEntityRecords"); domOperLog.DocumentElement.AppendChild(root); } // 真正实行删除 for (int i = 0; i < entityinfos.Count; i++) { DeleteEntityInfo info = entityinfos[i]; byte[] output_timestamp = null; int nRedoCount = 0; REDO_DELETE: this.EntityLocks.LockForWrite(info.ItemBarcode); try { long lRet = channel.DoDeleteRes(info.RecPath, info.OldTimestamp, out output_timestamp, out strError); if (lRet == -1) { if (channel.ErrorCode == ChannelErrorCode.NotFound) continue; // 如果不重试,让时间戳出错暴露出来。 // 如果要重试,也得加上重新读入册记录并判断重新判断无借还信息才能删除 if (channel.ErrorCode == ChannelErrorCode.TimestampMismatch) { if (nRedoCount > 10) { strError = "重试了10次还不行。删除实体记录 '" + info.RecPath + "' 时发生错误: " + strError; goto ERROR1; } nRedoCount++; // 重新读入记录 string strMetaData = ""; string strXml = ""; string strOutputPath = ""; string strError_1 = ""; lRet = channel.GetRes(info.RecPath, out strXml, out strMetaData, out output_timestamp, out strOutputPath, out strError_1); if (lRet == -1) { if (channel.ErrorCode == ChannelErrorCode.NotFound) continue; strError = "在删除实体记录 '" + info.RecPath + "' 时发生时间戳冲突,于是自动重新获取记录,但又发生错误: " + strError_1; goto ERROR1; // goto CONTINUE; } // 检查是否有借阅信息 // 把记录装入DOM XmlDocument domExist = new XmlDocument(); try { domExist.LoadXml(strXml); } catch (Exception ex) { strError = "实体记录 '"+info.RecPath+"' XML装载进入DOM时发生错误: " + ex.Message; goto ERROR1; } info.ItemBarcode = DomUtil.GetElementText(domExist.DocumentElement, "barcode"); // 观察已经存在的记录是否有流通信息 string strDetail = ""; bool bHasCirculationInfo = IsEntityHasCirculationInfo(domExist, out strDetail); if (bHasCirculationInfo == true) { strError = "拟删除的册记录 '" + info.RecPath + "' 中包含有流通信息("+strDetail+")(此种情况可能不限于这一条),不能删除。"; goto ERROR1; } info.OldTimestamp = output_timestamp; goto REDO_DELETE; } strError = "删除实体记录 '" + info.RecPath + "' 时发生错误: " + strError; goto ERROR1; } } finally { this.EntityLocks.UnlockForWrite(info.ItemBarcode); } // 增补到日志DOM中 if (domOperLog != null) { Debug.Assert(root != null, ""); XmlNode node = domOperLog.CreateElement("record"); root.AppendChild(node); DomUtil.SetAttr(node, "recPath", info.RecPath); } nDeletedCount++; } return nDeletedCount; ERROR1: return -1; }
// 复制属于同一书目记录的全部实体记录 // parameters: // strAction copy / move // return: // -2 目标实体库不存在,无法进行复制或者删除 // -1 error // >=0 实际复制或者移动的实体记录数 public int CopyBiblioChildEntities(RmsChannel channel, string strAction, List<DeleteEntityInfo> entityinfos, string strTargetBiblioRecPath, XmlDocument domOperLog, out string strError) { strError = ""; if (entityinfos == null || entityinfos.Count == 0) return 0; int nOperCount = 0; XmlNode root = null; if (domOperLog != null) { root = domOperLog.CreateElement(strAction == "copy" ? "copyEntityRecords" : "moveEntityRecords"); domOperLog.DocumentElement.AppendChild(root); } // 获得目标书目库下属的实体库名 string strTargetItemDbName = ""; string strTargetBiblioDbName = ResPath.GetDbName(strTargetBiblioRecPath); // return: // -1 出错 // 0 没有找到 // 1 找到 int nRet = this.GetItemDbName(strTargetBiblioDbName, out strTargetItemDbName, out strError); if (nRet == 0 || string.IsNullOrEmpty(strTargetItemDbName) == true) { return -2; // 目标实体库不存在 } string strParentID = ResPath.GetRecordId(strTargetBiblioRecPath); if (string.IsNullOrEmpty(strParentID) == true) { strError = "目标书目记录路径 '"+strTargetBiblioRecPath+"' 不正确,无法获得记录号"; return -1; } List<string> newrecordpaths = new List<string>(); List<string> oldrecordpaths = new List<string>(); List<string> parentids = new List<string>(); List<string> oldrecords = new List<string>(); for (int i = 0; i < entityinfos.Count; i++) { DeleteEntityInfo info = entityinfos[i]; byte[] output_timestamp = null; string strOutputRecPath = ""; string strNewBarcode = ""; // 复制中修改后的册条码号 this.EntityLocks.LockForWrite(info.ItemBarcode); try { XmlDocument dom = new XmlDocument(); try { dom.LoadXml(info.OldRecord); } catch (Exception ex) { strError = "记录 '" + info.RecPath + "' 装入XMLDOM发生错误: " + ex.Message; goto ERROR1; } DomUtil.SetElementText(dom.DocumentElement, "parent", strParentID); // 复制的情况,要避免出现操作后的条码号重复现象 if (strAction == "copy") { // 修改册条码号,避免发生条码号重复 string strOldItemBarcode = DomUtil.GetElementText(dom.DocumentElement, "barcode"); if (String.IsNullOrEmpty(strOldItemBarcode) == false) { strNewBarcode = strOldItemBarcode + "_" + Guid.NewGuid().ToString(); DomUtil.SetElementText(dom.DocumentElement, "barcode", strNewBarcode); } // *** 后面这几个清除动作要作为规则出现 // 清除 refid DomUtil.SetElementText(dom.DocumentElement, "refID", null); // 把借者清除 // (源实体记录中如果有借阅信息,在普通界面上是无法删除此记录的。只能用出纳窗正规进行归还,然后才能删除) { DomUtil.SetElementText(dom.DocumentElement, "borrower", null); DomUtil.SetElementText(dom.DocumentElement, "borrowPeriod", null); DomUtil.SetElementText(dom.DocumentElement, "borrowDate", null); } } // TODO: 可以顺便确认有没有对象资源。如果没有,就省略CopyRecord操作 long lRet = channel.DoCopyRecord(info.RecPath, strTargetItemDbName + "/?", strAction == "move" ? true : false, // bDeleteSourceRecord out output_timestamp, out strOutputRecPath, out strError); if (lRet == -1) { if (channel.ErrorCode == ChannelErrorCode.NotFound) continue; strError = "复制实体记录 '" + info.RecPath + "' 时发生错误: " + strError; goto ERROR1; } // 修改xml记录。<parent>元素发生了变化 byte[] baOutputTimestamp = null; string strOutputRecPath1 = ""; lRet = channel.DoSaveTextRes(strOutputRecPath, dom.OuterXml, false, "content", // ,ignorechecktimestamp output_timestamp, out baOutputTimestamp, out strOutputRecPath1, out strError); if (lRet == -1) goto ERROR1; oldrecordpaths.Add(info.RecPath); newrecordpaths.Add(strOutputRecPath); parentids.Add(strParentID); if (strAction == "move") oldrecords.Add(info.OldRecord); } finally { this.EntityLocks.UnlockForWrite(info.ItemBarcode); } // 增补到日志DOM中 if (domOperLog != null) { Debug.Assert(root != null, ""); XmlNode node = domOperLog.CreateElement("record"); root.AppendChild(node); DomUtil.SetAttr(node, "recPath", info.RecPath); DomUtil.SetAttr(node, "targetRecPath", strOutputRecPath); // 2014/1/5 if (string.IsNullOrEmpty(strNewBarcode) == false) DomUtil.SetAttr(node, "newBarcode", strNewBarcode); } nOperCount++; } return nOperCount; ERROR1: // Undo已经进行过的操作 if (strAction == "copy") { string strWarning = ""; foreach (string strRecPath in newrecordpaths) { string strTempError = ""; byte[] timestamp = null; byte[] output_timestamp = null; REDO_DELETE: long lRet = channel.DoDeleteRes(strRecPath, timestamp, out output_timestamp, out strTempError); if (lRet == -1) { if (channel.ErrorCode == ChannelErrorCode.TimestampMismatch) { if (timestamp == null) { timestamp = output_timestamp; goto REDO_DELETE; } } strWarning += strTempError + ";"; } } if (string.IsNullOrEmpty(strWarning) == false) strError = strError + "。在Undo过程中,又遇到出错: " + strWarning; } else if (strAction == "move") { string strWarning = ""; for (int i = 0; i < newrecordpaths.Count; i++) { byte[] output_timestamp = null; string strOutputRecPath = ""; string strTempError = ""; // TODO: 如果确认没有对象,就可以省略这一步 long lRet = channel.DoCopyRecord(newrecordpaths[i], oldrecordpaths[i], true, // bDeleteSourceRecord out output_timestamp, out strOutputRecPath, out strTempError); if (lRet == -1) { strWarning += strTempError + ";"; } // 修改xml记录。<parent>元素发生了变化 byte[] baOutputTimestamp = null; string strOutputRecPath1 = ""; lRet = channel.DoSaveTextRes(oldrecordpaths[i], oldrecords[i], false, "content", // ,ignorechecktimestamp output_timestamp, out baOutputTimestamp, out strOutputRecPath1, out strTempError); if (lRet == -1) { strWarning += strTempError + ";"; } } if (string.IsNullOrEmpty(strWarning) == false) strError = strError + "。在Undo过程中,又遇到出错: " + strWarning; } return -1; }
// 删除册记录的操作 int DoEntityOperDelete( SessionInfo sessioninfo, bool bForce, string strStyle, RmsChannel channel, EntityInfo info, string strOldBarcode, string strNewBarcode, // TODO: 本参数是否可以废除? XmlDocument domOldRec, ref XmlDocument domOperLog, ref List<EntityInfo> ErrorInfos) { int nRedoCount = 0; EntityInfo error = null; int nRet = 0; long lRet = 0; string strError = ""; // 2008/6/24 if (String.IsNullOrEmpty(info.NewRecPath) == false) { if (info.NewRecPath != info.OldRecPath) { strError = "action为delete时, 如果info.NewRecPath不空,则其内容必须和info.OldRecPath一致。(info.NewRecPath='" + info.NewRecPath + "' info.OldRecPath='" + info.OldRecPath + "')"; return -1; } } else { info.NewRecPath = info.OldRecPath; } // 如果记录路径为空, 则先获得记录路径 if (String.IsNullOrEmpty(info.NewRecPath) == true) { List<string> aPath = null; if (String.IsNullOrEmpty(strOldBarcode) == true) { strError = "info.OldRecord中的<barcode>元素中的册条码号,和info.RecPath参数值,不能同时为空。"; goto ERROR1; } // 本函数只负责查重, 并不获得记录体 // return: // -1 error // 其他 命中记录条数(不超过nMax规定的极限) nRet = this.SearchItemRecDup( // sessioninfo.Channels, channel, strOldBarcode, 100, out aPath, out strError); if (nRet == -1) { strError = "删除操作中条码号查重阶段发生错误:" + strError; goto ERROR1; } if (nRet == 0) { error = new EntityInfo(info); error.ErrorInfo = "册条码号为 '" + strOldBarcode + "' 的册记录已不存在"; error.ErrorCode = ErrorCodeValue.NotFound; ErrorInfos.Add(error); return -1; } if (nRet > 1) { /* string[] pathlist = new string[aPath.Count]; aPath.CopyTo(pathlist); * */ // 在删除操作中,遇到重复的是很平常的事情。只要 // info.OldRecPath能够清晰地指出要删除的那一条,就可以执行删除 if (String.IsNullOrEmpty(info.OldRecPath) == false) { if (aPath.IndexOf(info.OldRecPath) == -1) { strError = "条码号 '" + strOldBarcode + "' 已经被下列多条册记录使用了: " + StringUtil.MakePathList(aPath)/*String.Join(",", pathlist)*/ + "',但并不包括info.OldRecPath所指的路径 '" + info.OldRecPath + "'。删除操作失败。"; goto ERROR1; } info.NewRecPath = info.OldRecPath; } else { strError = "条码号 '" + strOldBarcode + "' 已经被下列多条册记录使用了: " + StringUtil.MakePathList(aPath)/*String.Join(",", pathlist)*/ + "',在没有指定info.OldRecPath的情况下,无法定位和删除。"; goto ERROR1; } } else { Debug.Assert(nRet == 1, ""); info.NewRecPath = aPath[0]; // strEntityDbName = ResPath.GetDbName(strRecPath); } } Debug.Assert(String.IsNullOrEmpty(info.NewRecPath) == false, ""); // Debug.Assert(strEntityDbName != "", ""); byte[] exist_timestamp = null; string strOutputPath = ""; string strMetaData = ""; string strExistingXml = ""; REDOLOAD: // 先读出数据库中此位置的已有记录 lRet = channel.GetRes(info.NewRecPath, out strExistingXml, out strMetaData, out exist_timestamp, out strOutputPath, out strError); if (lRet == -1) { if (channel.ErrorCode == ChannelErrorCode.NotFound) { error = new EntityInfo(info); error.ErrorInfo = "册条码号为 '" + strOldBarcode + "' 的册记录 '" + info.NewRecPath + "' 已不存在"; error.ErrorCode = channel.OriginErrorCode; ErrorInfos.Add(error); return -1; } else { error = new EntityInfo(info); error.ErrorInfo = "删除操作发生错误, 在读入原有记录 '" + info.NewRecPath + "' 阶段:" + strError; error.ErrorCode = channel.OriginErrorCode; ErrorInfos.Add(error); return -1; } } // 把记录装入DOM XmlDocument domExist = new XmlDocument(); try { domExist.LoadXml(strExistingXml); } catch (Exception ex) { strError = "strExistXml装载进入DOM时发生错误: " + ex.Message; goto ERROR1; } // 观察已经存在的记录中,册条码号是否和strOldBarcode一致 if (String.IsNullOrEmpty(strOldBarcode) == false) { string strExistingBarcode = DomUtil.GetElementText(domExist.DocumentElement, "barcode"); if (strExistingBarcode != strOldBarcode) { strError = "路径为 '" + info.NewRecPath + "' 的册记录中<barcode>元素中的册条码号 '" + strExistingBarcode + "' 和strOldXml中<barcode>元素中的册条码号 '" + strOldBarcode + "' 不一致。拒绝删除(如果允许删除,则会造成不经意删除了别的册记录的危险)。"; goto ERROR1; } } if (bForce == false) { // 观察已经存在的记录是否有流通信息 string strDetail = ""; bool bHasCirculationInfo = IsEntityHasCirculationInfo(domExist, out strDetail); if (bHasCirculationInfo == true) { strError = "拟删除的册记录 '" + info.NewRecPath + "' 中包含有流通信息(" + strDetail + "),不能删除。"; goto ERROR1; } } // 比较时间戳 // 观察时间戳是否发生变化 nRet = ByteArray.Compare(info.OldTimestamp, exist_timestamp); if (nRet != 0) { // 2008/5/29 if (bForce == true) { error = new EntityInfo(info); error.NewTimestamp = exist_timestamp; // 让前端知道库中记录实际上发生过变化 error.ErrorInfo = "数据库中即将删除的册记录已经发生了变化,请重新装载、仔细核对后再行删除。"; error.ErrorCode = ErrorCodeValue.TimestampMismatch; ErrorInfos.Add(error); return -1; } // 如果前端给出了旧记录,就有和库中记录进行比较的基础 if (String.IsNullOrEmpty(info.OldRecord) == false) { // 比较两个记录, 看看和册要害信息有关的字段是否发生了变化 // return: // 0 没有变化 // 1 有变化 nRet = IsRegisterInfoChanged(domExist, domOldRec); if (nRet == 1) { error = new EntityInfo(info); error.NewTimestamp = exist_timestamp; // 让前端知道库中记录实际上发生过变化 error.ErrorInfo = "数据库中即将删除的册记录已经发生了变化,请重新装载、仔细核对后再行删除。"; error.ErrorCode = ErrorCodeValue.TimestampMismatch; ErrorInfos.Add(error); return -1; } } info.OldTimestamp = exist_timestamp; info.NewTimestamp = exist_timestamp; } // 只有order权限的情况 if (StringUtil.IsInList("setiteminfo", sessioninfo.RightsOrigin) == false && StringUtil.IsInList("setentities", sessioninfo.RightsOrigin) == false && StringUtil.IsInList("order", sessioninfo.RightsOrigin) == true) { // 2009/11/26 changed string strEntityDbName = ResPath.GetDbName(info.NewRecPath); if (String.IsNullOrEmpty(strEntityDbName) == true) { strError = "从路径 '" + info.NewRecPath + "' 中获得数据库名时失败"; goto ERROR1; } string strBiblioDbName = ""; // 根据实体库名, 找到对应的书目库名 // 注意,返回1的时候,strBiblioDbName也有可能为空 // return: // -1 出错 // 0 没有找到 // 1 找到 nRet = GetBiblioDbNameByItemDbName(strEntityDbName, out strBiblioDbName, out strError); if (nRet == 0 || nRet == -1) { strError = "根据实体库名 '" + strEntityDbName + "' 中获得书目库名时失败"; goto ERROR1; } // BUG !!! string strBiblioDbName = ResPath.GetDbName(info.NewRecPath); // 非工作库 if (IsOrderWorkBiblioDb(strBiblioDbName) == false) { // 非工作库。要求<state>包含“加工中” string strState = DomUtil.GetElementText(domExist.DocumentElement, "state"); if (IncludeStateProcessing(strState) == false) { strError = "当前帐户只有order权限而没有setiteminfo(或setentities)权限,不能用delete功能删除从属于非工作库的、状态不包含“加工中”的实体记录 '" + info.NewRecPath + "'"; goto ERROR1; // TODO: 如何返回AccessDenied错误码呢? } } } string strLibraryCode = ""; // 检查一个册记录的馆藏地点是否符合馆代码列表要求 // return: // -1 检查过程出错 // 0 符合要求 // 1 不符合要求 nRet = CheckItemLibraryCode(domExist, sessioninfo, // sessioninfo.LibraryCodeList, out strLibraryCode, out strError); if (nRet == -1) goto ERROR1; // 检查旧记录是否属于管辖范围 if (sessioninfo.GlobalUser == false) { if (nRet != 0) { strError = "即将被删除的册记录 '" + info.NewRecPath + "' 其馆藏地点不符合要求: " + strError; goto ERROR1; } } byte[] output_timestamp = null; lRet = channel.DoDeleteRes(info.NewRecPath, info.OldTimestamp, out output_timestamp, out strError); if (lRet == -1) { if (channel.ErrorCode == ChannelErrorCode.TimestampMismatch) { if (nRedoCount > 10) { strError = "反复删除均遇到时间戳冲突, 超过10次重试仍然失败"; goto ERROR1; } // 发现时间戳不匹配 // 重复进行提取已存在记录\比较的过程 nRedoCount++; goto REDOLOAD; } error = new EntityInfo(info); error.NewTimestamp = output_timestamp; error.ErrorInfo = "删除操作发生错误:" + strError; error.ErrorCode = channel.OriginErrorCode; ErrorInfos.Add(error); return -1; } else { // 成功 DomUtil.SetElementText(domOperLog.DocumentElement, "libraryCode", strLibraryCode); // 册所在的馆代码 DomUtil.SetElementText(domOperLog.DocumentElement, "action", "delete"); if (String.IsNullOrEmpty(strStyle) == false) DomUtil.SetElementText(domOperLog.DocumentElement, "style", strStyle); // 不创建<record>元素 XmlNode node = DomUtil.SetElementText(domOperLog.DocumentElement, "oldRecord", strExistingXml); DomUtil.SetAttr(node, "recPath", info.NewRecPath); // 如果删除成功,则不必要在数组中返回表示成功的信息元素了 } return 0; ERROR1: error = new EntityInfo(info); error.ErrorInfo = strError; error.ErrorCode = ErrorCodeValue.CommonError; ErrorInfos.Add(error); return -1; }