// 复制属于同一书目记录的全部实体记录 // parameters: // strAction copy / move // return: // -1 error // >=0 实际复制或者移动的实体记录数 public int CopyBiblioChildRecords(RmsChannel channel, string strAction, List<DeleteEntityInfo> entityinfos, List<string> target_recpaths, string strTargetBiblioRecPath, List<string> newbarcodes, out string strError) { strError = ""; if (entityinfos == null || entityinfos.Count == 0) return 0; if (entityinfos.Count != target_recpaths.Count) { strError = "entityinfos.Count (" + entityinfos.Count.ToString() + ") != target_recpaths.Count (" + target_recpaths.Count .ToString()+ ")"; return -1; } int nOperCount = 0; 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]; string strTargetRecPath = target_recpaths[i]; string strNewBarcode = newbarcodes[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") { // 修改册条码号,避免发生条码号重复 string strOldItemBarcode = DomUtil.GetElementText(dom.DocumentElement, "barcode"); if (String.IsNullOrEmpty(strOldItemBarcode) == false) { // 2014/1/5 if (string.IsNullOrEmpty(strNewBarcode) == true) strNewBarcode = "temp_" + strOldItemBarcode; DomUtil.SetElementText(dom.DocumentElement, "barcode", strNewBarcode); } // 2014/1/5 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, strTargetRecPath, 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); } nOperCount++; } return nOperCount; ERROR1: // 不要Undo return -1; }
// 把实体记录借阅信息详细化 // parameters: // strEntityRecPath 册记录路径。如果本参数值为空,则表示希望通过strItemBarcode参数来找到册记录 // return: // -1 error // 0 册条码号没有找到对应的册记录 // 1 成功 int ModifyEntityRecord( // RmsChannelCollection Channels, RmsChannel channel, string strEntityRecPath, string strItemBarcode, string strReaderBarcode, string strBorrowDate, string strBorrowPeriod, out string strError) { strError = ""; int nRet = 0; long lRet = 0; int nRedoCount = 0; if (String.IsNullOrEmpty(strItemBarcode) == true) { strError = "strItemBarcode参数不能为空"; return -1; } // RmsChannel channel = null; REDO_CHANGE: string strOutputItemRecPath = ""; byte[] item_timestamp = null; string strItemXml = ""; List<string> aPath = null; #if NO if (channel == null) { channel = Channels.GetChannel(this.WsUrl); if (channel == null) { strError = "get channel error"; return -1; } } #endif if (String.IsNullOrEmpty(strEntityRecPath) == false) { string strStyle = "content,data,metadata,timestamp,outputpath"; string strMetaData = ""; lRet = channel.GetRes(strEntityRecPath, strStyle, out strItemXml, out strMetaData, out item_timestamp, out strOutputItemRecPath, out strError); if (lRet == -1) { if (channel.ErrorCode == ChannelErrorCode.NotFound) { return 0; } strError = "ModifyEntityRecord()通过册记录路径 '"+strEntityRecPath+"' 读入册记录时发生错误: " + strError; return -1; } } else { // 从册条码号获得册记录 // return: // -1 error // 0 not found // 1 命中1条 // >1 命中多于1条 nRet = this.GetItemRecXml( // Channels, channel, strItemBarcode, out strItemXml, 100, out aPath, out item_timestamp, out strError); if (nRet == 0) { // 册条码号不存在也是需要修复的情况之一。 return 0; } if (nRet == -1) { strError = "ModifyEntityRecord()读入册记录时发生错误: " + strError; return -1; } if (aPath.Count > 1) { // TODO: 需要将入围的记录全部提取出来,然后看borrower符合读者证条码号的那一条(或者多条?) // 可以参考UpgradeDt1000Loan中的SearchEntityRecord()函数 /* strError = "因册条码号 '" + strItemBarcode + "' 检索命中多条册记录: " + StringUtil.MakePathList(aPath) + ",修改册记录的操作ModifyEntityRecord()无法进行"; return -1; * */ int nSuccessCount = 0; string strTempError = ""; // 递归 for (int i = 0; i < aPath.Count; i++) { string strTempPath = aPath[i]; if (String.IsNullOrEmpty(strTempPath) == true) { Debug.Assert(false, ""); continue; } // return: // -1 error // 0 册条码号没有找到对应的册记录 // 1 成功 nRet = ModifyEntityRecord( // Channels, channel, strTempPath, strItemBarcode, strReaderBarcode, strBorrowDate, strBorrowPeriod, out strError); if (nRet == -1 && nSuccessCount == 0) { if (String.IsNullOrEmpty(strTempError) == false) strTempError += "; "; strTempError += "试探册记录 '" + strTempPath + "' 时发生错误: " + strError; } if (nRet == 1) { // 改为存储成功信息 if (nSuccessCount == 0) strTempError = ""; if (String.IsNullOrEmpty(strTempError) == false) strTempError += "; "; strTempError += strTempPath; nSuccessCount++; } } if (nSuccessCount > 0) { strError = "册条码号 '" + strItemBarcode + "' 检索命中" + aPath.Count.ToString() + "条册记录: " + StringUtil.MakePathList(aPath) + ",后面对它们进行了逐条试探,有 " + nSuccessCount.ToString() + " 条记录符合预期的要求,册中借阅信息得到增强。借阅信息得到增强的册记录路径如下: " + strTempError; return 1; } else { strError = "册条码号 '" + strItemBarcode + "' 检索命中" + aPath.Count.ToString() + "条册记录: " + StringUtil.MakePathList(aPath) + ",后面对它们进行了逐条试探,但是没有一条记录符合预期的要求。试探过程报错如下: " + strTempError; return -1; } /* result.Value = -1; result.ErrorInfo = "册条码号为 '" + strItemBarcode + "' 的册记录有 " + aPath.Count.ToString() + " 条,无法进行修复操作。请在附加册记录路径后重新提交修复操作。"; result.ErrorCode = ErrorCode.ItemBarcodeDup; aDupPath = new string[aPath.Count]; aPath.CopyTo(aDupPath); return result; * */ } else { Debug.Assert(nRet == 1, ""); Debug.Assert(aPath.Count == 1, ""); if (nRet == 1) { strOutputItemRecPath = aPath[0]; } } } XmlDocument itemdom = null; nRet = LibraryApplication.LoadToDom(strItemXml, out itemdom, out strError); if (nRet == -1) { strError = "装载册记录进入XML DOM时发生错误: " + strError; return -1; } // 校验册条码号参数是否和XML记录中完全一致 string strTempItemBarcode = DomUtil.GetElementText(itemdom.DocumentElement, "barcode"); if (strItemBarcode != strTempItemBarcode) { strError = "修改册记录ModifyEntityRecord()操作被拒绝。因册条码号参数 '" + strItemBarcode + "' 和册记录中<barcode>元素内的册条码号值 '" + strTempItemBarcode + "' 不一致。"; return -1; } // 看看册记录中是否有指回读者记录的链 string strBorrower = DomUtil.GetElementText(itemdom.DocumentElement, "borrower"); if (strBorrower != strReaderBarcode) { // strError = "ModifyEntityRecord()操作被拒绝。您所请求要修复的链,本是一条完整正确的链。可直接进行普通还书操作。"; strError = "修改册记录ModifyEntityRecord()操作被拒绝。因册记录 " + strOutputItemRecPath + " 中的[borrower]值 '" + strBorrower + "' 和发源(来找册条码号 '" + strItemBarcode + "')的读者证条码号 '" + strReaderBarcode + "' 不一致,不能构成一条完整正确的链。请及时排除此故障。"; return -1; } // 2007/1/1注:应当看看记录中<borrower>元素是否有内容才改写<borrowDate>和<borrowPeriod>元素。 DomUtil.SetElementText(itemdom.DocumentElement, "borrowDate", strBorrowDate); DomUtil.SetElementText(itemdom.DocumentElement, "borrowPeriod", strBorrowPeriod); #if NO if (channel == null) { channel = Channels.GetChannel(this.WsUrl); if (channel == null) { strError = "get channel error"; return -1; } } #endif byte[] output_timestamp = null; string strOutputPath = ""; // 写回册记录 lRet = channel.DoSaveTextRes(strOutputItemRecPath, itemdom.OuterXml, false, "content", // ,ignorechecktimestamp item_timestamp, out output_timestamp, out strOutputPath, out strError); if (lRet == -1) { if (channel.ErrorCode == ChannelErrorCode.TimestampMismatch) { nRedoCount++; if (nRedoCount > 10) { strError = "ModifyEntityRecord()写回册记录的时候,遇到时间戳冲突,并因此重试10次,仍失败..."; return -1; } goto REDO_CHANGE; } return -1; } // end of 写回册记录失败 return 1; }
// 保存记录到模板配置文件 // parameters: public void SaveToTemplate() { // 选择目标数据库 OpenResDlg dlg = new OpenResDlg(); dlg.Font = GuiUtil.GetDefaultFont(); dlg.Text = "请选择目标数据库"; dlg.EnabledIndices = new int[] { ResTree.RESTYPE_DB }; dlg.ap = this.MainForm.AppInfo; dlg.ApCfgTitle = "detailform_openresdlg"; dlg.Path = textBox_recPath.Text; dlg.Initial( MainForm.Servers, this.Channels); // dlg.StartPosition = FormStartPosition.CenterScreen; dlg.ShowDialog(this); if (dlg.DialogResult != DialogResult.OK) return; // 下载模板配置文件 ResPath respath = new ResPath(dlg.Path); string strError; string strContent; byte[] baTimeStamp = null; string strMetaData; string strOutputPath; string strCfgFilePath = respath.Path + "/cfgs/template"; long lRet = 0; // 使用Channel RmsChannel channelSave = channel; channel = Channels.GetChannel(respath.Url); Debug.Assert(channel != null, "Channels.GetChannel 异常"); try { stop.OnStop += new StopEventHandler(this.DoStop); stop.Initial("正在下载文件" + strCfgFilePath); stop.BeginLoop(); lRet = channel.GetRes( MainForm.cfgCache, strCfgFilePath, out strContent, out strMetaData, out baTimeStamp, out strOutputPath, out strError); stop.EndLoop(); stop.OnStop -= new StopEventHandler(this.DoStop); stop.Initial(""); if (lRet == -1) { this.TimeStamp = null; MessageBox.Show(this, strError); return; } } finally { channel = channelSave; } SelectRecordTemplateDlg tempdlg = new SelectRecordTemplateDlg(); tempdlg.Font = GuiUtil.GetDefaultFont(); int nRet = tempdlg.Initial(strContent, out strError); if (nRet == -1) goto ERROR1; tempdlg.Text = "请选择要修改的模板记录"; tempdlg.CheckNameExist = false; // 按OK按钮时不警告"名字不存在",这样允许新建一个模板 tempdlg.ap = this.MainForm.AppInfo; tempdlg.ApCfgTitle = "detailform_selecttemplatedlg"; tempdlg.ShowDialog(this); if (tempdlg.DialogResult != DialogResult.OK) return; string strXmlBody = ""; bool bHasUploadedFile = false; nRet = GetXmlRecord(out strXmlBody, out bHasUploadedFile, out strError); if (nRet == -1) goto ERROR1; // 修改配置文件内容 if (tempdlg.textBox_name.Text != "") { // 替换或者追加一个记录 nRet = tempdlg.ReplaceRecord(tempdlg.textBox_name.Text, strXmlBody, out strError); if (nRet == -1) { goto ERROR1; } } if (tempdlg.Changed == false) // 没有必要保存回去 return; string strOutputXml = tempdlg.OutputXml; // 使用Channel channelSave = channel; // 重新获得一次channel, 是因为前面GetXmlRecord()函数有可能摧毁这个变量 channel = Channels.GetChannel(respath.Url); Debug.Assert(channel != null, "Channels.GetChannel 异常"); try { // 存回配置文件 stop.OnStop += new StopEventHandler(this.DoStop); stop.Initial("正在保存配置文件 " + strCfgFilePath); stop.BeginLoop(); byte [] baOutputTimeStamp = null; // string strOutputPath = ""; EnableControlsInLoading(true); lRet = channel.DoSaveTextRes(strCfgFilePath, strOutputXml, true, // bInlucdePreamble "", // style baTimeStamp, out baOutputTimeStamp, out strOutputPath, out strError); EnableControlsInLoading(false); stop.EndLoop(); stop.OnStop -= new StopEventHandler(this.DoStop); stop.Initial(""); if (lRet == -1) { strError = "保存配置文件"+ strCfgFilePath +"失败,原因: "+strError; goto ERROR1; } } finally { channel = channelSave; } MessageBox.Show(this, "修改模板配置文件成功。"); return; ERROR1: MessageBox.Show(this, strError); }
// 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; }
// 复制属于同一书目记录的全部实体记录 // 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; }
// 覆盖一条XML记录 int DoOverwriteXmlRecord( bool bFastMode, string strRecFullPath, string strXmlBody, byte[] timestamp, out string strError) { strError = ""; ResPath respath = new ResPath(strRecFullPath); RmsChannel channelSave = channel; channel = this.Channels.GetChannel(respath.Url); try { string strWarning = ""; byte[] output_timestamp = null; string strOutputPath = ""; REDOSAVE: // 保存Xml记录 long lRet = channel.DoSaveTextRes(respath.Path, strXmlBody, false, // bIncludePreamble bFastMode == true ? "fastmode" : "",//strStyle, timestamp, out output_timestamp, out strOutputPath, out strError); if (lRet == -1) { if (stop != null) stop.Continue(); if (channel.ErrorCode == ChannelErrorCode.TimestampMismatch) { string strDisplayRecPath = strOutputPath; if (string.IsNullOrEmpty(strDisplayRecPath) == true) strDisplayRecPath = respath.Path; if (this.bNotAskTimestampMismatchWhenOverwrite == true) { timestamp = new byte[output_timestamp.Length]; Array.Copy(output_timestamp, 0, timestamp, 0, output_timestamp.Length); strWarning = " (时间戳不匹配, 自动重试)"; goto REDOSAVE; } DialogResult result = MessageDlg.Show(this, "上载 '" + strDisplayRecPath + " 时发现时间戳不匹配。详细情况如下:\r\n---\r\n" + strError + "\r\n---\r\n\r\n是否以新时间戳强行上载?\r\n注:(是)强行上载 (否)忽略当前记录或资源上载,但继续后面的处理 (取消)中断整个批处理", "dp2batch", MessageBoxButtons.YesNoCancel, MessageBoxDefaultButton.Button1, ref this.bNotAskTimestampMismatchWhenOverwrite); if (result == DialogResult.Yes) { timestamp = new byte[output_timestamp.Length]; Array.Copy(output_timestamp, 0, timestamp, 0, output_timestamp.Length); strWarning = " (时间戳不匹配, 应用户要求重试)"; goto REDOSAVE; } if (result == DialogResult.No) { return 0; // 继续作后面的资源 } if (result == DialogResult.Cancel) { strError = "用户中断"; goto ERROR1; // 中断整个处理 } } // 询问是否重试 DialogResult result1 = MessageBox.Show(this, "上载 '" + respath.Path + " 时发生错误。详细情况如下:\r\n---\r\n" + strError + "\r\n---\r\n\r\n是否重试?\r\n注:(是)重试 (否)不重试,但继续后面的处理 (取消)中断整个批处理", "dp2batch", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1); if (result1 == DialogResult.Yes) goto REDOSAVE; if (result1 == DialogResult.No) return 0; // 继续作后面的资源 goto ERROR1; } return 0; ERROR1: return -1; } finally { channel = channelSave; } }
// text-level: 内部处理 // 通知预约到书的操作 // 出于对读者库加锁方面的便利考虑, 单独做了此函数 // 注:本函数可能要删除部分通知记录 // parameters: // strItemBarcodeParam 册条码号。可以使用 "@refID:" 前缀 // return: // -1 error // 0 没有找到<request>元素 // 1 已成功处理 public int DoReservationNotify( CachedRecordCollection records, RmsChannel channel, string strReservationReaderBarcode, bool bNeedLockReader, string strItemBarcodeParam, bool bOnShelf, bool bModifyItemRecord, out List<string> DeletedNotifyRecPaths, out string strError) { int nRet = 0; strError = ""; long lRet = 0; DeletedNotifyRecPaths = null; // 获得相关读者记录 string strReaderXml = ""; string strOutputReaderRecPath = ""; string strItemXml = ""; // 加读者记录锁 if (bNeedLockReader == true) { #if DEBUG_LOCK_READER this.WriteErrorLog("DoReservationNotify 开始为读者加写锁 '" + strReservationReaderBarcode + "'"); #endif this.ReaderLocks.LockForWrite(strReservationReaderBarcode); } try { // 读入读者记录 nRet = this.GetReaderRecXml( //sessioninfo, // channels, channel, strReservationReaderBarcode, out strReaderXml, out strOutputReaderRecPath, out strError); if (nRet == 0) { strError = "读者证条码号 '" + strReservationReaderBarcode + "' 不存在"; return -1; } if (nRet == -1) { strError = "读入读者记录时发生错误: " + strError; return -1; } XmlDocument readerdom = null; CachedRecord reader_record = null; if (records != null) reader_record = records.Find(strOutputReaderRecPath); if (reader_record == null) { nRet = LibraryApplication.LoadToDom(strReaderXml, out readerdom, out strError); if (nRet == -1) { strError = "装载读者记录进入XML DOM时发生错误: " + strError; return -1; } } else readerdom = reader_record.Dom; byte[] timestamp = null; byte[] output_timestamp = null; string strOutputPath = ""; #if NO RmsChannel channel = channels.GetChannel(this.WsUrl); if (channel == null) { strError = "get channel error"; return -1; } #endif XmlNodeList nodes = readerdom.DocumentElement.SelectNodes("reservations/request"); XmlNode readerRequestNode = null; string strItems = ""; for (int i = 0; i < nodes.Count; i++) { readerRequestNode = nodes[i]; strItems = DomUtil.GetAttr(readerRequestNode, "items"); if (IsInBarcodeList(strItemBarcodeParam, strItems) == true) goto FOUND; } return 0; // not found request FOUND: Debug.Assert(readerRequestNode != null, ""); // 将相关册中的<request>元素清除 // 因为预约的时候可能列举了很多册,但只要其中一个到书,其它册的预约信息就需要解除了 string[] barcodes = strItems.Split(new char[] { ',' }); for (int i = 0; i < barcodes.Length; i++) { string strBarcode = barcodes[i].Trim(); if (String.IsNullOrEmpty(strBarcode) == true) continue; if (strBarcode == strItemBarcodeParam && bModifyItemRecord == false) { continue; // 这条册记录已经处理过了 } // 读入册记录 string strCurrentItemXml = ""; string strOutputItemRecPath = ""; // 获得册记录 // return: // -1 error // 0 not found // 1 命中1条 // >1 命中多于1条 nRet = this.GetItemRecXml( //sessioninfo, channel, strBarcode, out strCurrentItemXml, out strOutputItemRecPath, out strError); if (nRet == 0) { strError = "册条码号 '" + strBarcode + "' 不存在"; continue; } if (nRet == -1) { strError = "读入册记录 '" + strBarcode + "' 时发生错误: " + strError; return -1; } XmlDocument itemdom = null; CachedRecord item_record = null; if (records != null) item_record = records.Find(strOutputItemRecPath); if (item_record == null) { nRet = LibraryApplication.LoadToDom(strCurrentItemXml, out itemdom, out strError); if (nRet == -1) { strError = "装载册记录进入XML DOM时发生错误: " + strError; return -1; } } else itemdom = item_record.Dom; if (strBarcode == strItemBarcodeParam) strItemXml = strCurrentItemXml; if (strBarcode == strItemBarcodeParam) { string strTempReservationReaderBarcode = ""; // TODO: 确认一下这个函数内部不会保存册记录 // 如果正好是当前册记录,需要设置arrived状态 // 如果为丢失处理,本函数的调用者需要通知等待者:书已经丢失了,不用再等待 // parameters: // bMaskLocationReservation 不要给<location>打上#reservation标记 // return: // -1 error // 0 没有修改 // 1 进行过修改 nRet = DoItemReturnReservationCheck( bOnShelf, // 当册现在还在架上的时候,册记录的 <location> 就没有 #reservation。这样在借书环节检查册是否被预约的时候,就不能只看 <location> 了 ref itemdom, out strTempReservationReaderBarcode, out strError); if (nRet == -1) { strError = "修改册记录'" + strBarcode + "' 预约到书状态时(DoItemReturnReservationCheck)发生错误: " + strError; return -1; } // 对册记录如果没有改动 if (nRet == 0) continue; } else { // 如果是同一请求中的其他册记录 // 删除对应的<request>元素 XmlNode itemrequestnode = itemdom.DocumentElement.SelectSingleNode("reservations/request[@reader='" + strReservationReaderBarcode + "']"); if (itemrequestnode == null) continue; itemrequestnode.ParentNode.RemoveChild(itemrequestnode); } if (item_record == null) { // 写回册记录 lRet = channel.DoSaveTextRes(strOutputItemRecPath, itemdom.OuterXml, false, "content,ignorechecktimestamp", timestamp, out output_timestamp, out strOutputPath, out strError); if (lRet == -1) { strError = "写回册记录'" + strBarcode + "' (记录路径'" + strOutputItemRecPath + "')时发生错误: " + strError; return -1; } } else item_record.Changed = true; } // end of for // 读者记录中为对应的<request>元素打上状态记号 DomUtil.SetAttr(readerRequestNode, "state", "arrived"); // 到达时间 DomUtil.SetAttr(readerRequestNode, "arrivedDate", this.Clock.GetClock()); // 实际到达的一个册条码号 2007/1/18 DomUtil.SetAttr(readerRequestNode, "arrivedItemBarcode", strItemBarcodeParam); if (reader_record == null) { // 写回读者记录 lRet = channel.DoSaveTextRes(strOutputReaderRecPath, readerdom.OuterXml, false, "content,ignorechecktimestamp", timestamp, out output_timestamp, out strOutputPath, out strError); if (lRet == -1) return -1; } else reader_record.Changed = true; strReaderXml = readerdom.DocumentElement.OuterXml; } finally { if (bNeedLockReader == true) { this.ReaderLocks.UnlockForWrite(strReservationReaderBarcode); #if DEBUG_LOCK_READER this.WriteErrorLog("DoReservationNotify 结束为读者加写锁 '" + strReservationReaderBarcode + "'"); #endif } } string strLibraryCode = ""; nRet = this.GetLibraryCode(strOutputReaderRecPath, out strLibraryCode, out strError); if (nRet == -1) return -1; if (string.IsNullOrEmpty(strItemXml) == true) { // 读入册记录 string strOutputItemRecPath = ""; // 获得册记录 // return: // -1 error // 0 not found // 1 命中1条 // >1 命中多于1条 nRet = this.GetItemRecXml( channel, strItemBarcodeParam, out strItemXml, out strOutputItemRecPath, out strError); if (nRet == 0) { strError = "册条码号 '" + strItemBarcodeParam + "' 不存在"; } if (nRet == -1) { strError = "读入册记录 '" + strItemBarcodeParam + "' 时发生错误: " + strError; return -1; } CachedRecord temp_record = null; if (records != null) temp_record = records.Find(strOutputItemRecPath); if (temp_record != null && temp_record.Dom != null && temp_record.Dom.DocumentElement != null) strItemXml = temp_record.Dom.DocumentElement.OuterXml; } // 构造一个XML记录, 加入"预约到书"库 // 加入记录预约到书库的目的,是为了让工作线程可以监控读者是否来取书,如果超过保留期限,要转而通知下一个预约了此册的读者。 // 兼有email通知功能 nRet = AddNotifyRecordToQueueDatabase( // channels, channel, strItemBarcodeParam, "", // 暂时不使用此参数 strItemXml, bOnShelf, strLibraryCode, strReservationReaderBarcode, strReaderXml, out DeletedNotifyRecPaths, out strError); if (nRet == -1) return -1; if (this.Statis != null) this.Statis.IncreaseEntryValue(strLibraryCode, "出纳", "预约到书册", 1); return 1; }
// 结算一个交费记录 // 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; }
// Borrow()下级函数 // 撤销已经写入读者记录的借阅信息 // 如果记录已经不存在?是否需要用读者证条码号再查出新位置的读者记录来? // 因为没有借阅信息的读者记录,确实可能被另外的用户移动位置。 // parameters: // strReaderRecPath 读者记录路径 // strReaderBarcode 读者证条码号。若需要检查记录,看看里面条码号是否已经变化了,就使用这个参数。如果不想检查,就用null // strItemBarcode 已经借的册条码号 // return: // -1 error // 0 没有必要Undo // 1 Undo成功 int UndoBorrowReaderRecord( RmsChannel channel, string strReaderRecPath, string strReaderBarcode, string strItemBarcode, out string strError) { strError = ""; long lRet = 0; int nRet = 0; string strMetaData = ""; byte[] reader_timestamp = null; string strOutputPath = ""; string strReaderXml = ""; int nRedoCount = 0; REDO: lRet = channel.GetRes(strReaderRecPath, out strReaderXml, out strMetaData, out reader_timestamp, out strOutputPath, out strError); if (lRet == -1) { strError = "读出原记录 '" + strReaderRecPath + "' 时出错"; return -1; } XmlDocument readerdom = null; nRet = LibraryApplication.LoadToDom(strReaderXml, out readerdom, out strError); if (nRet == -1) { strError = "装载库中读者记录 '" + strReaderRecPath + "' 进入XML DOM时发生错误: " + strError; return -1; } // 检查读者证条码号字段 是否发生变化 if (String.IsNullOrEmpty(strReaderBarcode) == false) { string strReaderBarcodeContent = DomUtil.GetElementText(readerdom.DocumentElement, "barcode"); if (strReaderBarcode != strReaderBarcodeContent) { strError = "发现从数据库中读出的读者记录 '" + strReaderRecPath + "' ,其<barcode>字段内容 '" + strReaderBarcodeContent + "' 和要Undo的读者记录证条码号 '" + strReaderBarcode + "' 已不同。"; return -1; } } // 去除dom中表示借阅的节点 XmlNode node = readerdom.DocumentElement.SelectSingleNode("borrows/borrow[@barcode='" + strItemBarcode + "']"); if (node == null) return 0; // 已经没有必要Undo了 node.ParentNode.RemoveChild(node); byte[] output_timestamp = null; // string strOutputPath = ""; // 写回读者记录 lRet = channel.DoSaveTextRes(strReaderRecPath, readerdom.OuterXml, false, "content", // ,ignorechecktimestamp reader_timestamp, out output_timestamp, out strOutputPath, out strError); if (lRet == -1) { if (channel.ErrorCode == ChannelErrorCode.TimestampMismatch) { nRedoCount++; if (nRedoCount > 10) { strError = "写回读者记录的时候发生时间戳冲突,并且已经重试10次,仍发生错误,只好停止重试"; return -1; } goto REDO; } strError = "写回读者记录的时候发生错误" + strError; return -1; } return 1; // Undo已经成功 }
// 清除读者和册记录中的已到预约事项,并提取下一个预约读者证条码号 // 本函数还负责清除册记录中以前残留的state=arrived的<request>元素 // parameters: // strItemBarcode 册条码号。支持 "@refID:" 前缀用法 // bMaskLocationReservation 不要给册记录<location>打上#reservation标记 // strReservationReaderBarcode 返回下一个预约读者的证条码号 public int ClearArrivedInfo( CachedRecordCollection records, RmsChannel channel, string strReaderBarcode, string strItemBarcode, bool bDontMaskLocationReservation, out string strReservationReaderBarcode, out string strError) { strError = ""; byte[] timestamp = null; byte[] output_timestamp = null; string strOutputPath = ""; long lRet = 0; int nRet = 0; strReservationReaderBarcode = ""; bool bDontLock = false; // 加读者记录锁 try { #if DEBUG_LOCK_READER this.WriteErrorLog("ClearArrivedInfo 开始为读者加写锁 '" + strReaderBarcode + "'"); #endif this.ReaderLocks.LockForWrite(strReaderBarcode); } catch (System.Threading.LockRecursionException) { // 2012/5/31 // 有可能本函数被DigitalPlatform.LibraryServer.LibraryApplication.Reservation()调用时,已经对读者记录加了锁 bDontLock = true; #if DEBUG_LOCK_READER this.WriteErrorLog("ClearArrivedInfo 开始为读者加写锁 '" + strReaderBarcode + "' 时遇到抛出 LockRecursionException 异常"); #endif } try { // 读入读者记录 string strReaderXml = ""; string strOutputReaderRecPath = ""; nRet = this.GetReaderRecXml( // channels, channel, strReaderBarcode, out strReaderXml, out strOutputReaderRecPath, out strError); if (nRet == 0) { strError = "读者证条码号 '" + strReaderBarcode + "' 不存在"; goto DOITEM; } if (nRet == -1) { strError = "读入读者记录时发生错误: " + strError; return -1; } XmlDocument readerdom = null; CachedRecord reader_record = null; if (records != null) reader_record = records.Find(strOutputReaderRecPath); if (reader_record == null) { nRet = LibraryApplication.LoadToDom(strReaderXml, out readerdom, out strError); if (nRet == -1) { strError = "装载读者记录进入XML DOM时发生错误: " + strError; return -1; } } else readerdom = reader_record.Dom; // 从当前读者记录中删除有关字段 XmlNodeList nodes = readerdom.DocumentElement.SelectNodes("reservations/request"); XmlNode readerRequestNode = null; string strItems = ""; bool bFound = false; for (int i = 0; i < nodes.Count; i++) { readerRequestNode = nodes[i]; strItems = DomUtil.GetAttr(readerRequestNode, "items"); if (IsInBarcodeList(strItemBarcode, strItems) == true) { bFound = true; break; } } if (bFound == true) { Debug.Assert(readerRequestNode != null, ""); // 是清除,还是修改状态标记并保留一段? // 现在是清除。如果能同时写入日志最好,以便将来查询 readerRequestNode.ParentNode.RemoveChild(readerRequestNode); } // 写回读者记录 if (bFound == true) { if (reader_record == null) { lRet = channel.DoSaveTextRes(strOutputReaderRecPath, readerdom.OuterXml, false, "content,ignorechecktimestamp", timestamp, out output_timestamp, out strOutputPath, out strError); if (lRet == -1) { strError = "写回读者记录 '" + strOutputReaderRecPath + "' 时发生错误 : " + strError; return -1; } } else { reader_record.Changed = true; } } DOITEM: // 顺便获得下一个预约读者证条码号 string strItemXml = ""; string strOutputItemRecPath = ""; // 获得册记录 // return: // -1 error // 0 not found // 1 命中1条 // >1 命中多于1条 nRet = this.GetItemRecXml( channel, strItemBarcode, out strItemXml, out strOutputItemRecPath, out strError); if (nRet == 0) { strError = "册条码号 '" + strItemBarcode + "' 不存在"; return 0; } if (nRet == -1) { strError = "读入册记录时发生错误: " + strError; return -1; } XmlDocument itemdom = null; CachedRecord item_record = null; if (records != null) item_record = records.Find(strOutputItemRecPath); if (item_record == null) { nRet = LibraryApplication.LoadToDom(strItemXml, out itemdom, out strError); if (nRet == -1) { strError = "装载册记录进入XML DOM时发生错误: " + strError; return -1; } } else itemdom = item_record.Dom; // 察看本册预约情况, 如果有,提取出第一个预约读者的证条码号 // 该函数还负责清除以前残留的state=arrived的<request>元素 // return: // -1 error // 0 没有修改 // 1 进行过修改 nRet = DoItemReturnReservationCheck( bDontMaskLocationReservation, ref itemdom, out strReservationReaderBarcode, out strError); if (nRet == -1) return -1; if (nRet == 1) { if (item_record == null) { lRet = channel.DoSaveTextRes(strOutputItemRecPath, itemdom.OuterXml, false, "content,ignorechecktimestamp", timestamp, out output_timestamp, out strOutputPath, out strError); if (lRet == -1) { strError = "写回册记录 '" + strOutputItemRecPath + "' 时发生错误: " + strError; return -1; } } else item_record.Changed = true; } } finally { if (bDontLock == false) { this.ReaderLocks.UnlockForWrite(strReaderBarcode); #if DEBUG_LOCK_READER this.WriteErrorLog("ClearArrivedInfo 结束为读者加写锁 '" + strReaderBarcode + "'"); #endif } } return 1; }
// 修改册记录中的借者证条码号 // parameters: // domOperLog 日志记录DOM对象。如果==null,表示根本不创建日志(包括日志DOM) // attachment 如果!=null表示要把实体记录保存到日志的attachment中。如果==null,表示直接把实体记录保存到日志记录(DOM)中 // nAttachmentIndex 日志附件记录index。第一次调用的时候,此值应为0,然后如果本函数增添了日志附件记录,会自动增量这个值 // return: // -1 error // 0 entitybarcode not found // 1 found and changed int ChangeEntityBorrower( // RmsChannelCollection Channels, RmsChannel channel, string strEntityBarcode, string strOldReaderBarcode, string strNewReaderBarcode, string strOperTimeString, ref XmlDocument domOperLog, Stream attachment, ref int nAttachmentIndex, out string strError) { strError = ""; int nRet = 0; long lRet = 0; // 加册记录锁 this.EntityLocks.LockForWrite(strEntityBarcode); try // 册记录锁定范围开始 { // 从册条码号获得册记录 byte[] item_timestamp = null; List<string> aPath = null; string strItemXml = ""; string strOutputItemRecPath = ""; int nRedoCount = 0; REDO: // 获得册记录 // return: // -1 error // 0 not found // 1 命中1条 // >1 命中多于1条 nRet = this.GetItemRecXml( // Channels, channel, strEntityBarcode, out strItemXml, 100, out aPath, out item_timestamp, out strError); if (nRet == 0) { strError = "册条码号 '" + strEntityBarcode + "' 不存在"; return 0; } if (nRet == -1) { strError = "读入册记录 '" + strEntityBarcode + "' 时发生错误: " + strError; goto ERROR1; } // RmsChannel channel = null; if (aPath.Count > 1) { /* strError = "册条码号为 '" + strEntityBarcode + "' 的册记录有 " + aPath.Count.ToString() + " 条,无法进行修改"; return -1; * */ // bItemBarcodeDup = true; // 此时已经需要设置状态。虽然后面可以进一步识别出真正的册记录 // 构造strDupBarcodeList /* string[] pathlist = new string[aPath.Count]; aPath.CopyTo(pathlist); string strDupBarcodeList = String.Join(",", pathlist); * */ string strDupBarcodeList = StringUtil.MakePathList(aPath); List<string> aFoundPath = null; List<byte[]> aTimestamp = null; List<string> aItemXml = null; if (String.IsNullOrEmpty(strOldReaderBarcode) == true) { strError = "strOldReaderBarcode参数值不能为空"; goto ERROR1; } #if NO channel = Channels.GetChannel(this.WsUrl); if (channel == null) { strError = "get channel error"; goto ERROR1; } #endif // 从若干重复条码号的册记录中,选出其中符合当前读者证条码号的 // return: // -1 出错 // 其他 选出的数量 nRet = FindItem( channel, strOldReaderBarcode, aPath, true, // 优化 out aFoundPath, out aItemXml, out aTimestamp, out strError); if (nRet == -1) { strError = "选择重复条码号的册记录时发生错误: " + strError; goto ERROR1; } if (nRet == 0) { strError = "册条码号 '" + strEntityBarcode + "' 检索出的 " + aPath.Count + " 条记录中,没有任何一条其<borrower>元素表明了被读者 '" + strOldReaderBarcode + "' 借阅。"; goto ERROR1; } if (nRet > 1) { strError = "册条码号为 '" + strEntityBarcode + "' 并且<borrower>元素表明为读者 '" + strOldReaderBarcode + "' 借阅的册记录有 " + aFoundPath.Count.ToString() + " 条,无法进行移动操作。"; goto ERROR1; } Debug.Assert(nRet == 1, ""); strOutputItemRecPath = aFoundPath[0]; item_timestamp = aTimestamp[0]; strItemXml = aItemXml[0]; } else { Debug.Assert(nRet == 1, ""); Debug.Assert(aPath.Count == 1, ""); if (nRet == 1) { strOutputItemRecPath = aPath[0]; } } XmlDocument itemdom = null; nRet = LibraryApplication.LoadToDom(strItemXml, out itemdom, out strError); if (nRet == -1) { strError = "装载册记录进入XML DOM时发生错误: " + strError; goto ERROR1; } string strBorrower = DomUtil.GetElementText(itemdom.DocumentElement, "borrower"); if (String.IsNullOrEmpty(strBorrower) == true) { strError = "实体记录中没有借者信息(<borrower>元素内容)"; goto ERROR1; } // 核对旧读者证条码号 if (strBorrower != strOldReaderBarcode) { strError = "实体记录中,已有借者证条码号 '" + strBorrower + "' 和期望的改前证条码号 '" + strOldReaderBarcode + "' 不一致..."; goto ERROR1; } // 修改为新读者证条码号 DomUtil.SetElementText(itemdom.DocumentElement, "borrower", strNewReaderBarcode); // 加上一个注释 DomUtil.SetElementText(itemdom.DocumentElement, "devolveComment", "本册原为读者 " + strOldReaderBarcode + " 所借阅,后于 " + strOperTimeString + " 被转移到读者 " + strNewReaderBarcode + " 名下"); #if NO if (channel == null) { channel = Channels.GetChannel(this.WsUrl); if (channel == null) { strError = "get channel error"; goto ERROR1; } } #endif // 保存实体记录 byte[] output_timestamp = null; string strOutputPath = ""; lRet = channel.DoSaveTextRes(strOutputItemRecPath, itemdom.OuterXml, false, "content", // ,ignorechecktimestamp item_timestamp, out output_timestamp, out strOutputPath, out strError); if (lRet == -1) { if (channel.ErrorCode == ChannelErrorCode.TimestampMismatch) { if (nRedoCount > 10) goto ERROR1; nRedoCount++; item_timestamp = output_timestamp; goto REDO; } } // 将保存了的记录写入日志 if (domOperLog != null) { XmlNode nodeLogRecord = domOperLog.CreateElement("changedEntityRecord"); domOperLog.DocumentElement.AppendChild(nodeLogRecord); DomUtil.SetAttr(nodeLogRecord, "recPath", strOutputPath); if (attachment == null) { // 实体记录完全保存到日志记录中 nodeLogRecord.InnerText = itemdom.OuterXml; } else { // 实体记录保存到附件中,只在日志记录中留下序号 // 保存附件序号 DomUtil.SetAttr(nodeLogRecord, "attachmentIndex", nAttachmentIndex.ToString()); byte[] content = Encoding.UTF8.GetBytes(itemdom.OuterXml); byte[] length = BitConverter.GetBytes((long)content.LongLength); attachment.Write(length, 0, length.Length); attachment.Write(content, 0, content.Length); nAttachmentIndex++; } } } finally { this.EntityLocks.UnlockForWrite(strEntityBarcode); } return 1; ERROR1: return -1; }
// 在违约金数据库中创建若干新的违约金记录 // parameters: // AmerceRecordXmls 需要写入的新记录的数组 // CreatedNewPaths 已经创建的新记录的路径数组。可以用于Undo(删除刚刚创建的新记录) int CreateAmerceRecords( // RmsChannelCollection channels, RmsChannel channel, List<string> AmerceRecordXmls, out List<string> CreatedNewPaths, out string strError) { strError = ""; CreatedNewPaths = new List<string>(); long lRet = 0; if (string.IsNullOrEmpty(this.AmerceDbName) == true) { strError = "尚未配置违约金库名"; return -1; } #if NO RmsChannel channel = channels.GetChannel(this.WsUrl); if (channel == null) { strError = "get channel error"; return -1; } #endif for (int i = 0; i < AmerceRecordXmls.Count; i++) { string strXml = AmerceRecordXmls[i]; string strPath = this.AmerceDbName + "/?"; string strOutputPath = ""; byte[] timestamp = null; byte[] output_timestamp = null; // 写新记录 lRet = channel.DoSaveTextRes( strPath, strXml, false, "content,ignorechecktimestamp", timestamp, out output_timestamp, out strOutputPath, out strError); if (lRet == -1) return -1; CreatedNewPaths.Add(strOutputPath); } 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; }
// 写入一条 XML 记录 // return: // -1 出错 // 0 邀请中断整个处理 // 1 成功 // 2 跳过本条,继续处理后面的 public static int WriteOneXmlRecord( IWin32Window owner, Stop stop, RmsChannel channel, UploadRecord record, ref bool bDontPromptTimestampMismatchWhenOverwrite, out string strError) { strError = ""; string strWarning = ""; // 询问库名映射关系 string strTargetPath = record.RecordBody.Path; byte[] output_timestamp = null; string strOutputPath = ""; REDOSAVE: // 保存Xml记录 long lRet = channel.DoSaveTextRes(strTargetPath, record.RecordBody.Xml, false, // bIncludePreamble "", // bFastMode == true ? "fastmode" : "",//strStyle, record.RecordBody.Timestamp, out output_timestamp, out strOutputPath, out strError); if (lRet == -1) return -1; if (lRet == -1) { if (stop != null) stop.Continue(); if (channel.ErrorCode == ChannelErrorCode.TimestampMismatch) { string strDisplayRecPath = strOutputPath; if (string.IsNullOrEmpty(strDisplayRecPath) == true) strDisplayRecPath = strTargetPath; if (bDontPromptTimestampMismatchWhenOverwrite == true) { record.RecordBody.Timestamp = output_timestamp; strWarning = " (时间戳不匹配, 自动重试)"; // TODO: 如何防止死循环? goto REDOSAVE; } string strText = "保存 '" + strDisplayRecPath + " 时发现时间戳不匹配。详细情况如下:\r\n---\r\n" + strError + "\r\n---\r\n\r\n是否以新时间戳强行覆盖保存?\r\n注:\r\n[是] 强行覆盖保存\r\n[否] 忽略当前记录或资源保存,但继续后面的处理\r\n[取消] 中断整个批处理"; //WriteLog("打开对话框 '" + strText.Replace("\r\n", "\\n") + "'"); DialogResult result = MessageDlg.Show(owner, strText, "dp2batch", MessageBoxButtons.YesNoCancel, MessageBoxDefaultButton.Button1, ref bDontPromptTimestampMismatchWhenOverwrite); //WriteLog("关闭对话框 '" + strText.Replace("\r\n", "\\n") + "'"); if (result == DialogResult.Yes) { record.RecordBody.Timestamp = output_timestamp; strWarning = " (时间戳不匹配, 应用户要求重试)"; goto REDOSAVE; } if (result == DialogResult.No) { return 2; // 继续作后面的资源 } if (result == DialogResult.Cancel) { strError = "用户中断"; return 0; // 中断整个处理 } } // 询问是否重试 { string strText = "保存 '" + strTargetPath + " 时发生错误。详细情况如下:\r\n---\r\n" + strError + "\r\n---\r\n\r\n是否重试?\r\n注:(是)重试 (否)不重试,但继续后面的处理 (取消)中断整个批处理"; //WriteLog("打开对话框 '" + strText.Replace("\r\n", "\\n") + "'"); DialogResult result1 = MessageBox.Show(owner, strText, "dp2batch", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1); //WriteLog("关闭对话框 '" + strText.Replace("\r\n", "\\n") + "'"); if (result1 == DialogResult.Yes) goto REDOSAVE; if (result1 == DialogResult.No) return 2; // 继续作后面的资源 } return -1; } return 1; }
// 给读者记录里加上预约到书后超期不取的状态 int AddReaderOutOfReservationInfo( // RmsChannelCollection channels, RmsChannel channel, string strReaderBarcode, string strItemBarcode, string strNotifyDate, out string strError) { strError = ""; int nRet = 0; long lRet = 0; int nRedoCount = 0; REDO_MEMO: // 加读者记录锁 #if DEBUG_LOCK_READER this.App.WriteErrorLog("AddReaderOutOfReservationInfo 开始为读者加写锁 '" + strReaderBarcode + "'"); #endif this.App.ReaderLocks.LockForWrite(strReaderBarcode); try // 读者记录锁定范围开始 { // 读入读者记录 string strReaderXml = ""; string strOutputReaderRecPath = ""; byte[] reader_timestamp = null; nRet = this.App.GetReaderRecXml( // channels, channel, strReaderBarcode, out strReaderXml, out strOutputReaderRecPath, out reader_timestamp, out strError); if (nRet == 0) { strError = "读者证条码号 '" + strReaderBarcode + "' 不存在"; return -1; } if (nRet == -1) { strError = "读入读者记录时发生错误: " + strError; return -1; } XmlDocument readerdom = null; nRet = LibraryApplication.LoadToDom(strReaderXml, out readerdom, out strError); if (nRet == -1) { strError = "装载读者记录进入XML DOM时发生错误: " + strError; return -1; } XmlNode root = readerdom.DocumentElement.SelectSingleNode("outofReservations"); if (root == null) { root = readerdom.CreateElement("outofReservations"); readerdom.DocumentElement.AppendChild(root); } // 累计次数 string strCount = DomUtil.GetAttr(root, "count"); if (String.IsNullOrEmpty(strCount) == true) strCount = "0"; int nCount = 0; try { nCount = Convert.ToInt32(strCount); } catch { } nCount++; DomUtil.SetAttr(root, "count", nCount.ToString()); // 追加<request>元素 XmlNode request = readerdom.CreateElement("request"); root.AppendChild(request); DomUtil.SetAttr(request, "itemBarcode", strItemBarcode); DomUtil.SetAttr(request, "notifyDate", strNotifyDate); byte[] output_timestamp = null; string strOutputPath = ""; #if NO RmsChannel channel = channels.GetChannel(this.App.WsUrl); if (channel == null) { strError = "get channel error"; return -1; } #endif // 写回读者记录 lRet = channel.DoSaveTextRes(strOutputReaderRecPath, readerdom.OuterXml, false, "content", // ,ignorechecktimestamp reader_timestamp, out output_timestamp, out strOutputPath, out strError); if (lRet == -1) { if (channel.ErrorCode == ChannelErrorCode.TimestampMismatch) { nRedoCount++; if (nRedoCount > 10) { strError = "写回读者记录的时候,遇到时间戳冲突,并因此重试10次,仍失败..."; return -1; } goto REDO_MEMO; } return -1; } } // 读者记录锁定范围结束 finally { this.App.ReaderLocks.UnlockForWrite(strReaderBarcode); #if DEBUG_LOCK_READER this.App.WriteErrorLog("AddReaderOutOfReservationInfo 结束为读者加写锁 '" + strReaderBarcode + "'"); #endif } return 0; }
// 撤销对读者记录的借阅信息删除操作(撤销还书) // parameters: // strReaderRecPath 读者记录路径 // strReaderBarcode 读者证条码号。若需要检查记录,看看里面条码号是否已经变化了,就使用这个参数。如果不想检查,就用null // strItemBarcode 已经借的册条码号 // strDeleteBorrowFrag 被删除掉的<borrow>元素片断 // strAddedOverdueFrag 已经加入的<overdue>元素片断 // return: // -1 error // 0 没有必要Undo // 1 Undo成功 int UndoReturnReaderRecord( RmsChannel channel, string strReaderRecPath, string strReaderBarcode, string strItemBarcode, string strDeleteBorrowFrag, string strAddedOverdueFrag, out string strError) { strError = ""; long lRet = 0; int nRet = 0; string strMetaData = ""; byte[] reader_timestamp = null; string strOutputPath = ""; string strReaderXml = ""; int nRedoCount = 0; REDO: lRet = channel.GetRes(strReaderRecPath, out strReaderXml, out strMetaData, out reader_timestamp, out strOutputPath, out strError); if (lRet == -1) { strError = "读出原记录 '" + strReaderRecPath + "' 时出错"; return -1; } XmlDocument readerdom = null; nRet = LibraryApplication.LoadToDom(strReaderXml, out readerdom, out strError); if (nRet == -1) { strError = "装载库中读者记录 '" + strReaderRecPath + "' 进入XML DOM时发生错误: " + strError; return -1; } // 检查读者证条码号字段 是否发生变化 if (String.IsNullOrEmpty(strReaderBarcode) == false) { string strReaderBarcodeContent = DomUtil.GetElementText(readerdom.DocumentElement, "barcode"); if (strReaderBarcode != strReaderBarcodeContent) { strError = "发现从数据库中读出的读者记录 '" + strReaderRecPath + "' ,其<barcode>字段内容 '" + strReaderBarcodeContent + "' 和要Undo的读者记录证条码号 '" + strReaderBarcode + "' 已不同。"; return -1; } } // 观察dom中表示借阅的节点 XmlNode node = readerdom.DocumentElement.SelectSingleNode("borrows/borrow[@barcode='" + strItemBarcode + "']"); if (node != null) return 0; // 已经没有必要Undo了 // 检查<borrows>元素是否存在 XmlNode root = readerdom.DocumentElement.SelectSingleNode("borrows"); if (root == null) { root = readerdom.CreateElement("borrows"); root = readerdom.DocumentElement.AppendChild(root); } // 加回<borrow>元素 XmlDocumentFragment fragment = readerdom.CreateDocumentFragment(); fragment.InnerXml = strAddedOverdueFrag; root.AppendChild(fragment); // 删除已经加入的<overdue>元素 { XmlDocument tempdom = new XmlDocument(); tempdom.LoadXml(strAddedOverdueFrag); // 获得其id属性 string strID = DomUtil.GetAttr(tempdom.DocumentElement, "id"); if (String.IsNullOrEmpty(strID) == false) { XmlNode nodeOverdue = readerdom.DocumentElement.SelectSingleNode( "overdues/overdue[@id='" + strID + "']"); if (nodeOverdue != null) nodeOverdue.ParentNode.RemoveChild(nodeOverdue); } } // TODO: 删除已经加入到<borrowHistory>中的<borrow>元素? byte[] output_timestamp = null; // string strOutputPath = ""; // 写回读者记录 lRet = channel.DoSaveTextRes(strReaderRecPath, readerdom.OuterXml, false, "content", // ,ignorechecktimestamp reader_timestamp, out output_timestamp, out strOutputPath, out strError); if (lRet == -1) { if (channel.ErrorCode == ChannelErrorCode.TimestampMismatch) { nRedoCount++; if (nRedoCount > 10) { strError = "写回读者记录的时候发生时间戳冲突,并且已经重试10次,仍发生错误,只好停止重试"; return -1; } goto REDO; } strError = "写回读者记录的时候发生错误" + strError; return -1; } return 1; // Undo已经成功 }
// 移动或者复制书目记录 // strExistingXml和请求中传来的old xml的时间戳比较,在本函数外、调用前进行 // parameters: // strAction 动作。为"onlycopybiblio" "onlymovebiblio"之一。增加 copy / move // strNewBiblio 需要在目标记录中更新的内容。如果 == null,表示不特意更新 // strMergeStyle 如何合并两条记录的元数据部分? reserve_source / reserve_target。 空表示 reserve_source int DoBiblioOperMove( string strAction, SessionInfo sessioninfo, RmsChannel channel, string strOldRecPath, string strExistingSourceXml, // byte[] baExistingSourceTimestamp, // 请求中提交过来的时间戳 string strNewRecPath, string strNewBiblio, // 已经经过Merge预处理的新记录XML string strMergeStyle, out string strOutputTargetXml, out byte[] baOutputTimestamp, out string strOutputRecPath, out string strError) { strError = ""; long lRet = 0; baOutputTimestamp = null; strOutputRecPath = ""; strOutputTargetXml = ""; // 最后保存成功的记录 // 检查路径 if (strOldRecPath == strNewRecPath) { strError = "当action为\"" + strAction + "\"时,strNewRecordPath路径 '" + strNewRecPath + "' 和strOldRecPath '" + strOldRecPath + "' 必须不相同"; goto ERROR1; } if (String.IsNullOrEmpty(strNewRecPath) == true) { strError = "DoBiblioOperMove() strNewRecPath参数值不能为空"; goto ERROR1; } // 检查即将覆盖的目标位置是不是有记录,如果有,则不允许进行move操作。 bool bAppendStyle = false; // 目标路径是否为追加形态? string strTargetRecId = ResPath.GetRecordId(strNewRecPath); string strExistTargetXml = ""; if (strTargetRecId == "?" || String.IsNullOrEmpty(strTargetRecId) == true) { // 2009/11/1 if (String.IsNullOrEmpty(strTargetRecId) == true) strNewRecPath += "/?"; bAppendStyle = true; } string strOutputPath = ""; string strMetaData = ""; if (bAppendStyle == false) { byte[] exist_target_timestamp = null; // 获取覆盖目标位置的现有记录 lRet = channel.GetRes(strNewRecPath, out strExistTargetXml, out strMetaData, out exist_target_timestamp, out strOutputPath, out strError); if (lRet == -1) { if (channel.ErrorCode == ChannelErrorCode.NotFound) { // 如果记录不存在, 说明不会造成覆盖态势 /* strExistSourceXml = "<root />"; exist_source_timestamp = null; strOutputPath = info.NewRecPath; * */ } else { strError = "移动操作发生错误, 在读入即将覆盖的目标位置 '" + strNewRecPath + "' 原有记录阶段:" + strError; goto ERROR1; } } else { #if NO // 如果记录存在,则目前不允许这样的操作 strError = "移动(move)操作被拒绝。因为在即将覆盖的目标位置 '" + strNewRecPath + "' 已经存在书目记录。请先删除(delete)这条记录,再进行移动(move)操作"; goto ERROR1; #endif } } /* // 把两个记录装入DOM XmlDocument domSourceExist = new XmlDocument(); XmlDocument domNew = new XmlDocument(); try { domSourceExist.LoadXml(strExistingSourceXml); } catch (Exception ex) { strError = "strExistXml装载进入DOM时发生错误: " + ex.Message; goto ERROR1; } try { domNew.LoadXml(strNewBiblio); } catch (Exception ex) { strError = "strNewBiblio装载进入DOM时发生错误: " + ex.Message; goto ERROR1; } * */ // 只有order权限的情况 if (StringUtil.IsInList("setbiblioinfo", sessioninfo.RightsOrigin) == false && StringUtil.IsInList("order", sessioninfo.RightsOrigin) == true) { if (strAction == "onlymovebiblio" || strAction == "move") { string strSourceDbName = ResPath.GetDbName(strOldRecPath); // 源头书目库为 非工作库 情况 if (IsOrderWorkBiblioDb(strSourceDbName) == false) { // 非工作库不能删除记录 if (IsOrderWorkBiblioDb(strSourceDbName) == false) { // 非工作库。要求原来记录不存在 strError = "当前帐户只有order权限而没有setbiblioinfo权限,不能用" + strAction + "功能删除源书目记录 '" + strOldRecPath + "'"; goto ERROR1; } } } } // 移动记录 byte[] output_timestamp = null; string strIdChangeList = ""; // TODO: Copy后还要写一次?因为Copy并不写入新记录。 // 其实Copy的意义在于带走资源。否则还不如用Save+Delete lRet = channel.DoCopyRecord(strOldRecPath, strNewRecPath, strAction == "onlymovebiblio" || strAction == "move" ? true : false, // bDeleteSourceRecord strMergeStyle, out strIdChangeList, out output_timestamp, out strOutputRecPath, out strError); if (lRet == -1) { strError = "DoCopyRecord() error :" + strError; goto ERROR1; } // TODO: 兑现对 856 字段的合并,和来自源的 856 字段的 $u 修改 if (String.IsNullOrEmpty(strNewBiblio) == false) { this.BiblioLocks.LockForWrite(strOutputRecPath); try { // TODO: 如果新的、已存在的xml没有不同,或者新的xml为空,则这步保存可以省略 string strOutputBiblioRecPath = ""; lRet = channel.DoSaveTextRes(strOutputRecPath, strNewBiblio, false, "content", // ,ignorechecktimestamp output_timestamp, out baOutputTimestamp, out strOutputBiblioRecPath, out strError); if (lRet == -1) goto ERROR1; } finally { this.BiblioLocks.UnlockForWrite(strOutputRecPath); } } { // TODO: 是否和前面一起锁定? byte[] exist_target_timestamp = null; // 获取最后的记录 lRet = channel.GetRes(strOutputRecPath, out strOutputTargetXml, out strMetaData, out exist_target_timestamp, out strOutputPath, out strError); } return 0; ERROR1: return -1; }
// 根据消息记录路径获得消息 // 不检查消息是否属于特定用户 int GetMessageByPath( RmsChannel channel, string strPath, MessageLevel messagelevel, out MessageData data, out string strError) { data = new MessageData(); string strMetaData = ""; byte[] timestamp = null; string strXml = ""; string strOutputPath = ""; long lRet = channel.GetRes(strPath, out strXml, out strMetaData, out timestamp, out strOutputPath, out strError); if (lRet == -1) { // text-level: 内部错误 strError = "获得消息记录 '" + strPath + "' 时出错: " + strError; return -1; } XmlDocument dom = new XmlDocument(); try { dom.LoadXml(strXml); } catch (Exception ex) { // text-level: 内部错误 strError = "装载XML记录进入DOM时出错: " + ex.Message; return -1; } data.strSender = DomUtil.GetElementText(dom.DocumentElement, "sender"); data.strRecipient = DomUtil.GetElementText(dom.DocumentElement, "recipient"); data.strSubject = DomUtil.GetElementText(dom.DocumentElement, "subject"); data.strCreateTime = DomUtil.GetElementText(dom.DocumentElement, "date"); data.strMime = DomUtil.GetElementText(dom.DocumentElement, "mime"); data.strSize = DomUtil.GetElementText(dom.DocumentElement, "size"); string strTouched = DomUtil.GetElementText(dom.DocumentElement, "touched"); if (strTouched == "1") data.Touched = true; else data.Touched = false; data.strRecordID = ResPath.GetRecordId(strOutputPath); if (messagelevel == MessageLevel.Full) { data.strBody = DomUtil.GetElementText(dom.DocumentElement, "content"); } data.strUserName = DomUtil.GetElementText(dom.DocumentElement, "username"); // 恒定为中文名称 data.strBoxType = DomUtil.GetElementText(dom.DocumentElement, "box"); data.TimeStamp = timestamp; // 修改touched元素值 if (messagelevel == MessageLevel.Full && data.Touched == false) { DomUtil.SetElementText(dom.DocumentElement, "touched", "1"); byte[] output_timestamp = null; //string strOutputPath = ""; lRet = channel.DoSaveTextRes(strPath, dom.OuterXml, false, "content,ignorechecktimestamp", timestamp, out output_timestamp, out strOutputPath, out strError); if (lRet == -1) { // text-level: 内部错误 strError = "写回记录 '"+strPath+"' 时出错: " + strError; return -1; } data.Touched = true; data.TimeStamp = output_timestamp; } return 1; }
// 上载一个item // parameter: // strError: error info // return: // -1 出错 // 0 正常 // 1 结束 public int DoXmlItemUpload( bool bFastMode, string strXml, DbNameMap map, bool bSkip, string strCount, out string strError) { strError = ""; int nRet = 0; // bool bRet = false; // MessageBox.Show(this, strXml); if (bSkip == true) return 0; XmlDocument dataDom = new XmlDocument(); try { dataDom.LoadXml(strXml); } catch (Exception ex) { strError = "加载数据到dom出错!\r\n" + ex.Message; goto ERROR1; } XmlNode node = dataDom.DocumentElement; string strResPath = DomUtil.GetAttr(DpNs.dprms, node, "path"); string strTargetPath = ""; string strSourceDbPath = ""; if (strResPath != "") { // 从map中查询覆盖还是追加? ResPath respath0 = new ResPath(strResPath); respath0.MakeDbName(); strSourceDbPath = respath0.FullPath; } REDO: DbNameMapItem mapItem = null; mapItem = map.MatchItem(strSourceDbPath/*strResPath*/); if (mapItem != null) goto MAPITEMOK; if (mapItem == null) { if (strSourceDbPath/*strResPath*/ == "") { string strText = "源数据文件中记录 " + Convert.ToString(this.m_nRecordCount) + " 没有来源数据库,对所有这样的数据,将作如何处理?"; WriteLog("打开对话框 '" + strText.Replace("\r\n", "\\n") + "'"); nRet = DbNameMapItemDlg.AskNullOriginBox( this, this.AppInfo, strText, this.SearchPanel, map); WriteLog("关闭对话框 '" + strText.Replace("\r\n", "\\n") + "'"); if (nRet == 0) { strError = "用户中断"; goto ERROR1; // 中断整个处理 } goto REDO; } else { string strText = "源数据文件中记录 " + Convert.ToString(this.m_nRecordCount) + " 的来源数据库 '" + strSourceDbPath/*strResPath*/ + "' 没有找到对应的目标库, 对所有这样的数据,将作如何处理?"; WriteLog("打开对话框 '" + strText.Replace("\r\n", "\\n") + "'"); nRet = DbNameMapItemDlg.AskNotMatchOriginBox( this, this.AppInfo, strText, this.SearchPanel, strSourceDbPath/*strResPath*/, map); WriteLog("关闭对话框 '" + strText.Replace("\r\n", "\\n") + "'"); if (nRet == 0) { strError = "用户中断"; goto ERROR1; // 中断整个处理 } goto REDO; } } MAPITEMOK: if (mapItem.Style == "skip") return 0; // 构造目标路径 // 1)从源路径中提取id。源路径来自备份文件数据 ResPath respath = new ResPath(strResPath); string strID = respath.GetRecordId(); if (strID == null || strID == "" || (mapItem.Style == "append") ) { strID = "?"; // 将来加一个对话框 } // 2)用目标库路径构造完整的记录路径 string strTargetFullPath = ""; if (mapItem.Target == "*") { // 此时target为*, 需要从strResPath中获得库名 if (strResPath == "") { Debug.Assert(false, "不可能出现的情况"); } respath = new ResPath(strResPath); respath.MakeDbName(); strTargetFullPath = respath.FullPath; } else { strTargetFullPath = mapItem.Target; } respath = new ResPath(strTargetFullPath); // 需要检查目标库所允许的MARC格式 if (CheckTargetDb != null) { CheckTargetDbEventArgs e = new CheckTargetDbEventArgs(); e.DbFullPath = strTargetFullPath; e.CurrentMarcSyntax = this.CurMarcSyntax; this.CheckTargetDb(this, e); if (e.Cancel == true) { if (e.ErrorInfo == "") strError = "CheckTargetDb 事件导致中断"; else strError = e.ErrorInfo; return -1; } } strTargetPath = respath.Path + "/" + strID; // strRecordPath = strTargetPath; channel = this.Channels.GetChannel(respath.Url); string strTimeStamp = DomUtil.GetAttr(DpNs.dprms, node, "timestamp"); byte[] timestamp = ByteArray.GetTimeStampByteArray(strTimeStamp); // 2012/5/29 string strOutMarcSyntax = ""; string strMARC = ""; // 将MARCXML格式的xml记录转换为marc机内格式字符串 // parameters: // bWarning ==true, 警告后继续转换,不严格对待错误; = false, 非常严格对待错误,遇到错误后不继续转换 // strMarcSyntax 指示marc语法,如果=="",则自动识别 // strOutMarcSyntax out参数,返回marc,如果strMarcSyntax == "",返回找到marc语法,否则返回与输入参数strMarcSyntax相同的值 nRet = MarcUtil.Xml2Marc(strXml, false, "", out strOutMarcSyntax, out strMARC, out strError); /* if (nRet == -1) return -1; * */ // 2012/5/30 if (batchObj != null) { batchObj.MarcSyntax = strOutMarcSyntax; batchObj.MarcRecord = strMARC; batchObj.MarcRecordChanged = false; // 为本轮Script运行准备初始状态 } if (this.MarcFilter != null) { // 触发filter中的Record相关动作 nRet = MarcFilter.DoRecord( null, batchObj.MarcRecord, m_nRecordCount, out strError); if (nRet == -1) goto ERROR1; } // C#脚本 -- Inputing if (this.AssemblyMain != null) { // 这些变量要先初始化,因为filter代码可能用到这些Batch成员. batchObj.SkipInput = false; batchObj.XmlRecord = strXml; //batchObj.MarcSyntax = this.CurMarcSyntax; //batchObj.MarcRecord = strMarc; // MARC记录体 //batchObj.MarcRecordChanged = false; // 为本轮Script运行准备初始状态 batchObj.SearchPanel.ServerUrl = channel.Url; batchObj.ServerUrl = channel.Url; batchObj.RecPath = strTargetPath; // 记录路径 batchObj.RecIndex = m_nRecordCount; // 当前记录在一批中的序号 batchObj.TimeStamp = timestamp; BatchEventArgs args = new BatchEventArgs(); batchObj.Inputing(this, args); if (args.Continue == ContinueType.SkipAll) { strError = "脚本中断SkipAll"; goto END2; } if (batchObj.SkipInput == true) return 0; // 继续处理后面的 } string strWarning = ""; byte[] output_timestamp = null; string strOutputPath = ""; REDOSAVE: if (stop != null) { if (strTargetPath.IndexOf("?") == -1) { stop.SetMessage("正在上载 " + strTargetPath + strWarning + " " + strCount); } } // 保存Xml记录 long lRet = channel.DoSaveTextRes(strTargetPath, strXml, false, // bIncludePreamble bFastMode == true ? "fastmode" : "",//strStyle, timestamp, out output_timestamp, out strOutputPath, out strError); if (lRet == -1) { if (stop != null) stop.Continue(); if (channel.ErrorCode == ChannelErrorCode.TimestampMismatch) { string strDisplayRecPath = strOutputPath; if (string.IsNullOrEmpty(strDisplayRecPath) == true) strDisplayRecPath = strTargetPath; if (this.bNotAskTimestampMismatchWhenOverwrite == true) { timestamp = new byte[output_timestamp.Length]; Array.Copy(output_timestamp, 0, timestamp, 0, output_timestamp.Length); strWarning = " (时间戳不匹配, 自动重试)"; goto REDOSAVE; } string strText = "上载 '" + strDisplayRecPath + " 时发现时间戳不匹配。详细情况如下:\r\n---\r\n" + strError + "\r\n---\r\n\r\n是否以新时间戳强行上载?\r\n注:(是)强行上载 (否)忽略当前记录或资源上载,但继续后面的处理 (取消)中断整个批处理"; WriteLog("打开对话框 '" + strText.Replace("\r\n", "\\n") + "'"); DialogResult result = MessageDlg.Show(this, strText, "dp2batch", MessageBoxButtons.YesNoCancel, MessageBoxDefaultButton.Button1, ref this.bNotAskTimestampMismatchWhenOverwrite); WriteLog("关闭对话框 '" + strText.Replace("\r\n", "\\n") + "'"); if (result == DialogResult.Yes) { timestamp = new byte[output_timestamp.Length]; Array.Copy(output_timestamp, 0, timestamp, 0, output_timestamp.Length); strWarning = " (时间戳不匹配, 应用户要求重试)"; goto REDOSAVE; } if (result == DialogResult.No) { return 0; // 继续作后面的资源 } if (result == DialogResult.Cancel) { strError = "用户中断"; goto ERROR1; // 中断整个处理 } } // 询问是否重试 { string strText = "上载 '" + strTargetPath + " 时发生错误。详细情况如下:\r\n---\r\n" + strError + "\r\n---\r\n\r\n是否重试?\r\n注:(是)重试 (否)不重试,但继续后面的处理 (取消)中断整个批处理"; WriteLog("打开对话框 '" + strText.Replace("\r\n", "\\n") + "'"); DialogResult result1 = MessageBox.Show(this, strText, "dp2batch", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1); WriteLog("关闭对话框 '" + strText.Replace("\r\n", "\\n") + "'"); if (result1 == DialogResult.Yes) goto REDOSAVE; if (result1 == DialogResult.No) return 0; // 继续作后面的资源 } goto ERROR1; } // C#脚本 -- Inputed() if (this.AssemblyMain != null) { // 大部分变量保留刚才Inputing()时的原样,只修改部分 batchObj.RecPath = strOutputPath; // 记录路径 batchObj.TimeStamp = output_timestamp; BatchEventArgs args = new BatchEventArgs(); batchObj.Inputed(this, args); /* if (args.Continue == ContinueType.SkipMiddle) { strError = "脚本中断SkipMiddle"; goto END1; } if (args.Continue == ContinueType.SkipBeginMiddle) { strError = "脚本中断SkipBeginMiddle"; goto END1; } */ if (args.Continue == ContinueType.SkipAll) { strError = "脚本中断SkipAll"; goto END1; } } this.m_nRecordCount++; if (stop != null) { stop.SetMessage("已上载成功 '" + strOutputPath + "' " + strCount); } // strRecordPath = strOutputPath; return 0; END1: END2: ERROR1: return -1; }
/// <summary> /// 保存记录 /// </summary> /// <param name="strServerUrl">服务器URL</param> /// <param name="strPath"></param> /// <param name="strXml"></param> /// <param name="baTimestamp"></param> /// <param name="bForceSaveOnTimestampMismatch"></param> /// <param name="baOutputTimestamp"></param> /// <param name="strError"></param> /// <returns>-2 时间戳不匹配;-1 一般出错;0 正常</returns> public int SaveRecord( string strServerUrl, string strPath, string strXml, byte [] baTimestamp, bool bForceSaveOnTimestampMismatch, out byte [] baOutputTimestamp, out string strError) { strError = ""; baOutputTimestamp = null; if (String.IsNullOrEmpty(strServerUrl) == true) strServerUrl = this.ServerUrl; RmsChannel channelSave = channel; channel = Channels.GetChannel(strServerUrl); if (channel == null) { strError = "get channel error"; return -1; } try { string strOutputPath = ""; REDO: long lRet = channel.DoSaveTextRes(strPath, strXml, false, // bInlucdePreamble "", // style baTimestamp, out baOutputTimestamp, out strOutputPath, out strError); if (lRet == -1) { if (channel.ErrorCode == ChannelErrorCode.TimestampMismatch) { if (bForceSaveOnTimestampMismatch == true) { baTimestamp = baOutputTimestamp; goto REDO; } else return -2; } return -1; } strError = channel.ErrorInfo; // 特殊API风格 return 0; } finally { channel = channelSave; } }
// 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; }
// 执行"change"操作 // 1) 操作成功后, NewRecord中有实际保存的新记录,NewTimeStamp为新的时间戳 // 2) 如果返回TimeStampMismatch错,则OldRecord中有库中发生变化后的“原记录”,OldTimeStamp是其时间戳 // return: // -1 出错 // 0 成功 int DoReaderChange( string strCurrentLibraryCode, string[] element_names, string strAction, bool bForce, RmsChannel channel, string strRecPath, XmlDocument domNewRec, XmlDocument domOldRec, byte[] baOldTimestamp, ref XmlDocument domOperLog, out string strExistingRecord, out string strNewRecord, out byte[] baNewTimestamp, out string strError, out DigitalPlatform.rms.Client.rmsws_localhost.ErrorCodeValue errorcode) { strError = ""; strExistingRecord = ""; strNewRecord = ""; baNewTimestamp = null; errorcode = DigitalPlatform.rms.Client.rmsws_localhost.ErrorCodeValue.NoError; int nRedoCount = 0; bool bExist = true; // strRecPath所指的记录是否存在? int nRet = 0; long lRet = 0; string strExistXml = ""; byte[] exist_timestamp = null; string strOutputPath = ""; string strMetaData = ""; // 2015/11/16 string strTargetRecId = ResPath.GetRecordId(strRecPath); if (strTargetRecId == "?" || String.IsNullOrEmpty(strTargetRecId) == true) { strError = "修改读者记录的操作 '" + strAction + "' 不允许使用追加形态的记录路径 '" + strRecPath + "'"; goto ERROR1; } REDOLOAD: // 先读出数据库中此位置的已有记录 lRet = channel.GetRes(strRecPath, out strExistXml, out strMetaData, out exist_timestamp, out strOutputPath, out strError); if (lRet == -1) { if (channel.ErrorCode == ChannelErrorCode.NotFound) { // 如果记录不存在, 则构造一条空的记录 bExist = false; strExistXml = "<root />"; exist_timestamp = null; strOutputPath = strRecPath; } else { strError = "保存操作发生错误, 在读入原有记录阶段:" + strError; errorcode = channel.OriginErrorCode; return -1; } } // 把记录装入DOM XmlDocument domExist = new XmlDocument(); try { domExist.LoadXml(strExistXml); } catch (Exception ex) { strError = "strExistXml装载进入DOM时发生错误: " + ex.Message; goto ERROR1; } bool bChangeReaderBarcode = false; string strOldBarcode = ""; string strNewBarcode = ""; if (bExist == true) // 2008/5/29 { // 比较新旧记录的条码号是否有改变 // return: // -1 出错 // 0 相等 // 1 不相等 nRet = CompareTwoBarcode(domExist, domNewRec, out strOldBarcode, out strNewBarcode, out strError); if (nRet == -1) goto ERROR1; string strDetailInfo = ""; // 关于读者记录里面是否有流通信息的详细提示文字 bool bHasCirculationInfo = false; // 读者记录里面是否有流通信息 bool bDetectCiculationInfo = false; // 是否已经探测过读者记录中的流通信息 if (nRet == 1) // 读者证条码号有改变 { // 观察已经存在的记录是否有流通信息 bHasCirculationInfo = IsReaderHasCirculationInfo(domExist, out strDetailInfo); bDetectCiculationInfo = true; if (bHasCirculationInfo == true) { if (strAction != "changereaderbarcode" && bForce == false) { strError = "(在读者记录中尚有借还信息时)修改读者证条码号的操作被拒绝。建议用 changereaderbarcode 动作进行此项操作;或者用 forcechange 动作。" + "因读者记录 '" + strRecPath + "' 中包含有 " + strDetailInfo + ",所以修改它时证条码号字段内容不能改变。(当前证条码号 '" + strOldBarcode + "',试图修改为条码号 '" + strNewBarcode + "')"; goto ERROR1; } // TODO: 可否增加允许同时修改所关联的已借阅册记录修改能力? // 值得注意的是如何记录进操作日志,将来如何进行recover的问题 bChangeReaderBarcode = true; } } // 清除 LoginCache #if NO this.LoginCache.Remove(strOldBarcode); if (strNewBarcode != strOldBarcode) this.LoginCache.Remove(strNewBarcode); #endif this.ClearLoginCache(strOldBarcode); if (strNewBarcode != strOldBarcode) this.ClearLoginCache(strNewBarcode); // 2009/1/23 // 比较新旧记录的状态是否有改变,如果从其他状态修改为“注销”状态,则应引起注意,后面要进行必要的检查 string strOldState = ""; string strNewState = ""; // parameters: // strOldState 顺便返回旧记录中的状态字符串 // strNewState 顺便返回新记录中的状态字符串 // return: // -1 出错 // 0 相等 // 1 不相等 nRet = CompareTwoState(domExist, domNewRec, out strOldState, out strNewState, out strError); if (nRet == -1) goto ERROR1; if (nRet == 1) { if (strOldState != "注销" && strNewState == "注销" && bForce == false) { // 观察已经存在的记录是否有流通信息 if (bDetectCiculationInfo == false) { bHasCirculationInfo = IsReaderHasCirculationInfo(domExist, out strDetailInfo); bDetectCiculationInfo = true; } if (bHasCirculationInfo == true) { Debug.Assert(bDetectCiculationInfo == true, ""); strError = "注销操作被拒绝。因拟被注销的读者记录 '" + strRecPath + "' 中包含有 " + strDetailInfo + "。(当前证状态 '" + strOldState + "', 试图修改为新状态 '" + strNewState + "')"; goto ERROR1; } } } } // 观察时间戳是否发生变化 nRet = ByteArray.Compare(baOldTimestamp, exist_timestamp); if (nRet != 0) { if (bForce == true) { // 2008/5/29 // 在强制修改模式下,时间戳不一致意义重大,直接返回出错,而不进行要害字段的比对判断 strError = "保存操作发生错误: 数据库中的原记录 (路径为'" + strRecPath + "') 在编辑期间原记录已发生过修改(保存时发现提交的时间戳和原记录不匹配)"; errorcode = DigitalPlatform.rms.Client.rmsws_localhost.ErrorCodeValue.TimestampMismatch; return -1; // timestamp mismatch } // 时间戳不相等了 // 需要把domOldRec和strExistXml进行比较,看看和读者信息有关的元素(要害元素)值是否发生了变化。 // 如果这些要害元素并未发生变化,就继续进行合并、覆盖保存操作 // 比较两个记录, 看看和册登录有关的字段是否发生了变化 // return: // 0 没有变化 // 1 有变化 nRet = IsReaderInfoChanged( element_names, domOldRec, domExist); if (nRet == 1) { // 错误信息中, 返回了修改过的原记录和新时间戳 strExistingRecord = strExistXml; baNewTimestamp = exist_timestamp; if (bExist == false) strError = "保存操作发生错误: 数据库中的原记录 (路径为'" + strRecPath + "') 已被删除。"; else strError = "保存操作发生错误: 数据库中的原记录 (路径为'" + strRecPath + "') 在编辑期间原记录已发生过修改(保存时发现提交的时间戳和原记录不匹配)"; errorcode = DigitalPlatform.rms.Client.rmsws_localhost.ErrorCodeValue.TimestampMismatch; return -1; // timestamp mismatch } // exist_timestamp此时已经反映了库中被修改后的记录的时间戳 } // TODO: 当strAction==changestate时,只允许<state>和<comment>两个元素内容发生变化 if (bForce == false) { string strNewXml = ""; nRet = MergeTwoReaderXml( element_names, strAction, domExist, domNewRec, out strNewXml, out strError); if (nRet == -1) goto ERROR1; domNewRec = new XmlDocument(); try { domNewRec.LoadXml(strNewXml); } catch (Exception ex) { strError = "(1)读者记录装入 XMLDOM 时出错: " + ex.Message; return -1; } } string strLibraryCode = ""; // 观察一个读者记录路径,看看是不是在当前用户管辖的读者库范围内? if (this.IsCurrentChangeableReaderPath(strRecPath, strCurrentLibraryCode, out strLibraryCode) == false) { strError = "读者记录路径 '" + strRecPath + "' 的读者库不在当前用户管辖范围内"; goto ERROR1; } // 2014/7/4 if (this.VerifyReaderType == true) { string strReaderDbName = ""; if (String.IsNullOrEmpty(strRecPath) == false) strReaderDbName = ResPath.GetDbName(strRecPath); #if NO XmlDocument domTemp = new XmlDocument(); domTemp.LoadXml(strNewXml); #endif // 检查一个册记录的读者类型是否符合值列表要求 // parameters: // return: // -1 检查过程出错 // 0 符合要求 // 1 不符合要求 nRet = CheckReaderType(domNewRec, // domTemp, strLibraryCode, strReaderDbName, out strError); if (nRet == -1 || nRet == 1) { strError = strError + "。修改读者记录操作失败"; goto ERROR1; } } // 注:bForce 为 true 时,效果是允许直接修改读者记录而并不修改相关册记录里的回链证条码号。这是为备份恢复而准备的功能。在备份恢复操作中,后面自然有人去操心恢复册记录,不必劳烦这里去操心联动修改了 if (bChangeReaderBarcode && bForce == false) { // 要修改读者记录的附注字段 string strExistComment = DomUtil.GetElementText(domNewRec.DocumentElement, "comment"); if (string.IsNullOrEmpty(strExistComment) == true) strExistComment = ""; else strExistComment += "; "; strExistComment += DateTime.Now.ToString() + " 证条码号从 '" + strOldBarcode + "' 修改为 '" + strNewBarcode + "'"; DomUtil.SetElementText(domNewRec.DocumentElement, "comment", strExistComment); } // 保存新记录 byte[] output_timestamp = null; lRet = channel.DoSaveTextRes(strRecPath, domNewRec.OuterXml, // strNewXml, false, // include preamble? "content", exist_timestamp, out output_timestamp, out strOutputPath, out strError); if (lRet == -1) { if (channel.ErrorCode == ChannelErrorCode.TimestampMismatch) { if (nRedoCount > 10) { strError = "反复遇到时间戳冲突, 超过10次重试仍然失败"; goto ERROR1; } // 发现时间戳不匹配 // 重复进行提取已存在记录\比较的过程 nRedoCount++; goto REDOLOAD; } strError = "保存操作发生错误:" + strError; errorcode = channel.OriginErrorCode; return -1; } else // 成功 { DomUtil.SetElementText(domOperLog.DocumentElement, "libraryCode", strLibraryCode); // 读者所在的馆代码 DomUtil.SetElementText(domOperLog.DocumentElement, "action", "change"); XmlNode node = DomUtil.SetElementText(domOperLog.DocumentElement, "record", domNewRec.OuterXml); // strNewXml DomUtil.SetAttr(node, "recPath", strRecPath); node = DomUtil.SetElementText(domOperLog.DocumentElement, "oldRecord", strExistXml); DomUtil.SetAttr(node, "recPath", strRecPath); // 保存成功,需要返回信息元素。因为需要返回新的时间戳 baNewTimestamp = output_timestamp; strNewRecord = domNewRec.OuterXml; // strNewXml; strError = "保存操作成功。NewTimeStamp中返回了新的时间戳,NewRecord中返回了实际保存的新记录(可能和提交的新记录稍有差异)。"; errorcode = DigitalPlatform.rms.Client.rmsws_localhost.ErrorCodeValue.NoError; /// { if (strAction == "change") { if (this.Statis != null) this.Statis.IncreaseEntryValue( strLibraryCode, "修改读者信息", "修改记录数", 1); } else if (strAction == "changestate") { string strNewState = DomUtil.GetElementText(domNewRec.DocumentElement, "state"); if (String.IsNullOrEmpty(strNewBarcode) == true) strNewState = "[可用]"; if (this.Statis != null) this.Statis.IncreaseEntryValue( strLibraryCode, "修改读者信息之状态", strNewState, 1); } else if (strAction == "changeforegift") { if (this.Statis != null) this.Statis.IncreaseEntryValue(strLibraryCode, "修改读者信息之押金", "次数", 1); } } } // 注:bForce 为 true 时,效果是允许直接修改读者记录而并不修改相关册记录里的回链证条码号。这是为备份恢复而准备的功能。在备份恢复操作中,后面自然有人去操心恢复册记录,不必劳烦这里去操心联动修改了 if (bChangeReaderBarcode && bForce == false) { // parameters: // domNewRec 拟保存的新读者记录 // strOldReaderBarcode 旧的证条码号 // return: // -1 出错。错误信息已写入系统错误日志 // 0 成功 nRet = ChangeRelativeItemRecords( channel, domNewRec, strOldBarcode, domOperLog, out strError); if (nRet == -1) return -1; } return 0; ERROR1: errorcode = DigitalPlatform.rms.Client.rmsws_localhost.ErrorCodeValue.CommonError; return -1; }
// 执行API中的"change"操作 // 1) 操作成功后, NewRecord中有实际保存的新记录,NewTimeStamp为新的时间戳 // 2) 如果返回TimeStampMismatch错,则OldRecord中有库中发生变化后的“原记录”,OldTimeStamp是其时间戳 // return: // -1 出错 // 0 成功 public int DoOperChange( bool bForce, // string strUserID, SessionInfo sessioninfo, RmsChannel channel, EntityInfo info, ref XmlDocument domOperLog, ref List<EntityInfo> ErrorInfos) { int nRedoCount = 0; EntityInfo error = null; bool bExist = true; // info.RecPath所指的记录是否存在? int nRet = 0; long lRet = 0; string strError = ""; // 检查一下路径 if (String.IsNullOrEmpty(info.NewRecPath) == true) { strError = "info.NewRecPath中的路径不能为空"; goto ERROR1; } string strTargetRecId = ResPath.GetRecordId(info.NewRecPath); if (strTargetRecId == "?") { strError = "info.NewRecPath路径 '" + strTargetRecId + "' 中记录ID部分不能为'?'"; goto ERROR1; } if (String.IsNullOrEmpty(strTargetRecId) == true) { strError = "info.NewRecPath路径 '" + strTargetRecId + "' 中记录ID部分不能为空"; goto ERROR1; } if (info.OldRecPath != info.NewRecPath) { strError = "当action为\"change\"时,info.NewRecordPath路径 '" + info.NewRecPath + "' 和info.OldRecPath '" + info.OldRecPath + "' 必须相同"; goto ERROR1; } string strExistXml = ""; byte[] exist_timestamp = null; string strOutputPath = ""; string strMetaData = ""; // 先读出数据库中即将覆盖位置的已有记录 REDOLOAD: lRet = channel.GetRes(info.NewRecPath, out strExistXml, out strMetaData, out exist_timestamp, out strOutputPath, out strError); if (lRet == -1) { if (channel.ErrorCode == ChannelErrorCode.NotFound) { // 如果记录不存在, 则构造一条空的记录 bExist = false; strExistXml = "<root />"; exist_timestamp = null; strOutputPath = info.NewRecPath; } else { error = new EntityInfo(info); error.ErrorInfo = "保存操作发生错误, 在读入原有记录阶段:" + strError; error.ErrorCode = channel.OriginErrorCode; ErrorInfos.Add(error); return -1; } } // 把两个记录装入DOM XmlDocument domExist = new XmlDocument(); XmlDocument domNew = new XmlDocument(); try { domExist.LoadXml(strExistXml); } catch (Exception ex) { strError = "strExistXml装载进入DOM时发生错误: " + ex.Message; goto ERROR1; } try { domNew.LoadXml(info.NewRecord); } catch (Exception ex) { strError = "info.NewRecord装载进入DOM时发生错误: " + ex.Message; goto ERROR1; } // 观察时间戳是否发生变化 nRet = ByteArray.Compare(info.OldTimestamp, exist_timestamp); if (nRet != 0) { // 时间戳不相等了 // 需要把info.OldRecord和strExistXml进行比较,看看和业务有关的元素(要害元素)值是否发生了变化。 // 如果这些要害元素并未发生变化,就继续进行合并、覆盖保存操作 XmlDocument domOld = new XmlDocument(); try { domOld.LoadXml(info.OldRecord); } catch (Exception ex) { strError = "info.OldRecord装载进入DOM时发生错误: " + ex.Message; goto ERROR1; } if (bForce == false) { // 比较两个记录, 看看和事项有关的字段是否发生了变化 // return: // 0 没有变化 // 1 有变化 nRet = IsItemInfoChanged(domOld, domExist); } if (nRet == 1 || bForce == true) // 2008/10/19 { error = new EntityInfo(info); // 错误信息中, 返回了修改过的原记录和新时间戳 error.OldRecord = strExistXml; error.OldTimestamp = exist_timestamp; if (bExist == false) error.ErrorInfo = "保存操作发生错误: 数据库中的原记录 (路径为'" + info.OldRecPath + "') 已被删除。"; else error.ErrorInfo = "保存操作发生错误: 数据库中的原记录 (路径为'" + info.OldRecPath + "') 已发生过修改"; error.ErrorCode = ErrorCodeValue.TimestampMismatch; ErrorInfos.Add(error); return -1; } // exist_timestamp此时已经反映了库中被修改后的记录的时间戳 } // 合并新旧记录 string strWarning = ""; string strNewXml = ""; if (bForce == false) { // 2011/2/11 nRet = CanChange( sessioninfo, "change", domExist, domNew, out strError); if (nRet == -1) goto ERROR1; if (nRet == 0) { error = new EntityInfo(info); error.ErrorInfo = strError; error.ErrorCode = ErrorCodeValue.AccessDenied; ErrorInfos.Add(error); return -1; } // 2010/4/8 nRet = this.App.SetOperation( ref domNew, "lastModified", sessioninfo.UserID, "", out strError); if (nRet == -1) goto ERROR1; // return: // -1 出错 // 0 正确 // 1 有部分修改没有兑现。说明在strError中 nRet = MergeTwoItemXml( sessioninfo, domExist, domNew, out strNewXml, out strError); if (nRet == -1) goto ERROR1; if (nRet == 1) strWarning = strError; } else { // 2008/10/19 strNewXml = domNew.OuterXml; } // 保存新记录 byte[] output_timestamp = null; lRet = channel.DoSaveTextRes(info.NewRecPath, strNewXml, false, // include preamble? "content", exist_timestamp, out output_timestamp, out strOutputPath, 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.ErrorInfo = "保存操作发生错误:" + strError; error.ErrorCode = channel.OriginErrorCode; ErrorInfos.Add(error); return -1; } else // 成功 { DomUtil.SetElementText(domOperLog.DocumentElement, "action", "change"); // 新记录 XmlNode node = DomUtil.SetElementText(domOperLog.DocumentElement, "record", strNewXml); DomUtil.SetAttr(node, "recPath", info.NewRecPath); // 旧记录 node = DomUtil.SetElementText(domOperLog.DocumentElement, "oldRecord", strExistXml); DomUtil.SetAttr(node, "recPath", info.OldRecPath); // 保存成功,需要返回信息元素。因为需要返回新的时间戳 error = new EntityInfo(info); error.NewTimestamp = output_timestamp; error.NewRecord = strNewXml; error.ErrorInfo = "保存操作成功。NewTimeStamp中返回了新的时间戳,NewRecord中返回了实际保存的新记录(可能和提交的新记录稍有差异)。"; if (string.IsNullOrEmpty(strWarning) == false) { error.ErrorInfo = "保存操作成功。但" + strWarning; error.ErrorCode = ErrorCodeValue.PartialDenied; } else error.ErrorCode = ErrorCodeValue.NoError; ErrorInfos.Add(error); } return 0; ERROR1: error = new EntityInfo(info); error.ErrorInfo = strError; error.ErrorCode = ErrorCodeValue.CommonError; ErrorInfos.Add(error); return -1; }
// 修改一条册记录,的 borrower 元素内容 // parameters: // bLogRecover 是否为日志恢复时被调用?日志恢复时本函数检查不会那么严格 // return: // -2 保存记录时出错 // -1 一般性错误 // 0 成功 int ChangeBorrower( // SessionInfo sessioninfo, RmsChannel channel, string strItemBarcode, string strConfirmItemRecPath, string strOldBorrower, string strNewBorrower, bool bLogRecover, out string strError) { strError = ""; int nRet = 0; long lRet = 0; #if NO RmsChannel channel = sessioninfo.Channels.GetChannel(this.WsUrl); if (channel == null) { strError = "get channel error"; return -1; } #endif // 加册记录锁 this.EntityLocks.LockForWrite(strItemBarcode); try // 册记录锁定范围开始 { string strItemXml = ""; byte[] item_timestamp = null; string strOutputItemRecPath = ""; // 如果已经有确定的册记录路径 if (String.IsNullOrEmpty(strConfirmItemRecPath) == false) { if (bLogRecover == false) { // 检查路径中的库名,是不是实体库名 // return: // -1 error // 0 不是实体库名 // 1 是实体库名 nRet = this.CheckItemRecPath(strConfirmItemRecPath, out strError); if (nRet == -1) return -1; if (nRet == 0) { strError = strConfirmItemRecPath + strError; return -1; } } string strMetaData = ""; lRet = channel.GetRes(strConfirmItemRecPath, out strItemXml, out strMetaData, out item_timestamp, out strOutputItemRecPath, out strError); if (lRet == -1) { // text-level: 内部错误 if (bLogRecover == false) { strError = "根据 strConfirmItemRecPath '" + strConfirmItemRecPath + "' 获得册记录失败: " + strError; return -1; } // 注:如果是日志恢复,还会继续向后执行,试图用册条码号获得册记录 } } if (string.IsNullOrEmpty(strItemXml) == true && string.IsNullOrEmpty(strItemBarcode) == false) { // 从册条码号获得册记录 List<string> aPath = null; // 获得册记录 // return: // -1 error // 0 not found // 1 命中1条 // >1 命中多于1条 nRet = this.GetItemRecXml( channel, strItemBarcode, out strItemXml, 100, out aPath, out item_timestamp, out strError); if (nRet == 0) { // text-level: 用户提示 strError = string.Format(this.GetString("册条码号s不存在"), // "册条码号 '{0}' 不存在" strItemBarcode); return -1; } if (nRet == -1) { // text-level: 内部错误 strError = "读入册记录时发生错误: " + strError; return -1; } if (aPath.Count > 1) { // this.WriteErrorLog(result.ErrorInfo); strError = "册条码号 '" + strItemBarcode + "' 命中多于一条"; return -1; } else { Debug.Assert(nRet == 1, ""); Debug.Assert(aPath.Count == 1, ""); if (nRet == 1) { strOutputItemRecPath = aPath[0]; } } } XmlDocument itemdom = null; nRet = LibraryApplication.LoadToDom(strItemXml, out itemdom, out strError); if (nRet == -1) { // text-level: 内部错误 strError = "装载册记录进入 XML DOM 时发生错误: " + strError; return -1; } if (bLogRecover == false) { string strExistingBorrower = DomUtil.GetElementText(itemdom.DocumentElement, "borrower"); if (strExistingBorrower != strOldBorrower) { strError = "册记录 '" + strOutputItemRecPath + "' 中原有的 borrower 元素内容为 '" + strExistingBorrower + "',和期待的 '" + strOldBorrower + "' 不同。对册记录的修改被放弃"; return -1; } } DomUtil.SetElementText(itemdom.DocumentElement, "borrower", strNewBorrower); byte[] output_timestamp = null; string strOutputPath = ""; // 写回册记录 lRet = channel.DoSaveTextRes(strOutputItemRecPath, itemdom.OuterXml, false, "content", // ,ignorechecktimestamp item_timestamp, out output_timestamp, out strOutputPath, out strError); if (lRet == -1) { // 2015/9/2 if (bLogRecover == false) this.WriteErrorLog("Borrow() 写入册记录 '" + strOutputItemRecPath + "' 时出错: " + strError); return -2; } return 0; } // 册记录锁定范围结束 finally { // 解册记录锁 this.EntityLocks.UnlockForWrite(strItemBarcode); // strItemBarcode 在整个函数中不允许被修改 } }
// 执行API中的"move"操作 // 1) 操作成功后, NewRecord中有实际保存的新记录,NewTimeStamp为新的时间戳 // 2) 如果返回TimeStampMismatch错,则OldRecord中有库中发生变化后的“原记录”,OldTimeStamp是其时间戳 // return: // -1 出错 // 0 成功 int DoOperMove( SessionInfo sessioninfo, // string strUserID, RmsChannel channel, EntityInfo info, ref XmlDocument domOperLog, ref List<EntityInfo> ErrorInfos) { EntityInfo error = null; bool bExist = true; // info.RecPath所指的记录是否存在? int nRet = 0; long lRet = 0; string strError = ""; // 检查路径 if (info.OldRecPath == info.NewRecPath) { strError = "当action为\"move\"时,info.NewRecordPath路径 '" + info.NewRecPath + "' 和info.OldRecPath '" + info.OldRecPath + "' 必须不相同"; goto ERROR1; } // 检查即将覆盖的目标位置是不是有记录,如果有,则不允许进行move操作。 // 如果要进行带覆盖目标位置记录功能的move操作,前端可以先执行一个delete操作,然后再执行move操作。 // 这样规定,是为了避免过于复杂的判断逻辑,也便于前端操作者清楚操作的后果。 // 因为如果允许move带有覆盖目标记录功能,则被覆盖的记录的预删除操作,等于进行了一次事项注销,但这个效用不明显,对前端操作人员准确判断事态并对后果负责(而且可能这种注销需要额外的操作权限),不利 bool bAppendStyle = false; // 目标路径是否为追加形态? string strTargetRecId = ResPath.GetRecordId(info.NewRecPath); if (strTargetRecId == "?" || String.IsNullOrEmpty(strTargetRecId) == true) bAppendStyle = true; string strOutputPath = ""; string strMetaData = ""; if (bAppendStyle == false) { string strExistTargetXml = ""; byte[] exist_target_timestamp = null; // 获取覆盖目标位置的现有记录 lRet = channel.GetRes(info.NewRecPath, out strExistTargetXml, out strMetaData, out exist_target_timestamp, out strOutputPath, out strError); if (lRet == -1) { if (channel.ErrorCode == ChannelErrorCode.NotFound) { // 如果记录不存在, 说明不会造成覆盖态势 /* strExistSourceXml = "<root />"; exist_source_timestamp = null; strOutputPath = info.NewRecPath; * */ } else { error = new EntityInfo(info); error.ErrorInfo = "move操作发生错误, 发生在读入即将覆盖的目标位置 '" + info.NewRecPath + "' 原有记录阶段:" + strError; error.ErrorCode = channel.OriginErrorCode; ErrorInfos.Add(error); return -1; } } else { // 如果记录存在,则目前不允许这样的操作 strError = "移动(move)操作被拒绝。因为在即将覆盖的目标位置 '" + info.NewRecPath + "' 已经存在" + this.ItemName + "记录。请先删除(delete)这条记录,再进行移动(move)操作"; goto ERROR1; } } string strExistSourceXml = ""; byte[] exist_source_timestamp = null; // 先读出数据库中源位置的已有记录 // REDOLOAD: lRet = channel.GetRes(info.OldRecPath, out strExistSourceXml, out strMetaData, out exist_source_timestamp, out strOutputPath, out strError); if (lRet == -1) { if (channel.ErrorCode == ChannelErrorCode.NotFound) { /* // 如果记录不存在, 则构造一条空的记录 bExist = false; strExistSourceXml = "<root />"; exist_source_timestamp = null; strOutputPath = info.NewRecPath; * */ // 这种情况如果放宽,会有严重的副作用,所以不让放宽 strError = "move操作的源记录 '" + info.OldRecPath + "' 在数据库中不存在,所以无法进行移动操作。"; goto ERROR1; } else { error = new EntityInfo(info); error.ErrorInfo = "移动操作发生错误, 在读入库中原有源记录(路径在info.OldRecPath) '" + info.OldRecPath + "' 阶段:" + strError; error.ErrorCode = channel.OriginErrorCode; ErrorInfos.Add(error); return -1; } } // 把两个记录装入DOM XmlDocument domSourceExist = new XmlDocument(); XmlDocument domNew = new XmlDocument(); try { domSourceExist.LoadXml(strExistSourceXml); } catch (Exception ex) { strError = "strExistXml装载进入DOM时发生错误: " + ex.Message; goto ERROR1; } try { domNew.LoadXml(info.NewRecord); } catch (Exception ex) { strError = "info.NewRecord装载进入DOM时发生错误: " + ex.Message; goto ERROR1; } // 观察时间戳是否发生变化 nRet = ByteArray.Compare(info.OldTimestamp, exist_source_timestamp); if (nRet != 0) { // 时间戳不相等了 // 需要把info.OldRecord和strExistXml进行比较,看看和事项有关的元素(要害元素)值是否发生了变化。 // 如果这些要害元素并未发生变化,就继续进行合并、覆盖保存操作 XmlDocument domOld = new XmlDocument(); try { domOld.LoadXml(info.OldRecord); } catch (Exception ex) { strError = "info.OldRecord装载进入DOM时发生错误: " + ex.Message; goto ERROR1; } // 比较两个记录, 看看和事项有关的要害字段是否发生了变化 // return: // 0 没有变化 // 1 有变化 nRet = IsItemInfoChanged(domOld, domSourceExist); if (nRet == 1) { error = new EntityInfo(info); // 错误信息中, 返回了修改过的原记录和新时间戳 error.OldRecord = strExistSourceXml; error.OldTimestamp = exist_source_timestamp; if (bExist == false) error.ErrorInfo = "移动操作发生错误: 数据库中的原记录 (路径为'" + info.OldRecPath + "') 已被删除。"; else error.ErrorInfo = "移动操作发生错误: 数据库中的原记录 (路径为'" + info.OldRecPath + "') 已发生过修改"; error.ErrorCode = ErrorCodeValue.TimestampMismatch; ErrorInfos.Add(error); return -1; } // exist_source_timestamp此时已经反映了库中被修改后的记录的时间戳 } // 2011/2/11 nRet = CanChange( sessioninfo, "move", domSourceExist, domNew, out strError); if (nRet == -1) goto ERROR1; if (nRet == 0) { error = new EntityInfo(info); error.ErrorInfo = strError; error.ErrorCode = ErrorCodeValue.AccessDenied; ErrorInfos.Add(error); return -1; } // 2010/4/8 // nRet = this.App.SetOperation( ref domNew, "moved", sessioninfo.UserID, // strUserID, "", out strError); if (nRet == -1) goto ERROR1; string strWarning = ""; // 合并新旧记录 // return: // -1 出错 // 0 正确 // 1 有部分修改没有兑现。说明在strError中 string strNewXml = ""; nRet = MergeTwoItemXml( sessioninfo, domSourceExist, domNew, out strNewXml, out strError); if (nRet == -1) goto ERROR1; if (nRet == 1) strWarning = strError; // 移动记录 byte[] output_timestamp = null; // TODO: Copy后还要写一次?因为Copy并不写入新记录。 // 其实Copy的意义在于带走资源。否则还不如用Save+Delete lRet = channel.DoCopyRecord(info.OldRecPath, info.NewRecPath, true, // bDeleteSourceRecord out output_timestamp, out strOutputPath, out strError); if (lRet == -1) { strError = "DoCopyRecord() error :" + strError; goto ERROR1; } // Debug.Assert(strOutputPath == info.NewRecPath); string strTargetPath = strOutputPath; lRet = channel.DoSaveTextRes(strTargetPath, strNewXml, false, // include preamble? "content", output_timestamp, out output_timestamp, out strOutputPath, out strError); if (lRet == -1) { strError = "移动操作中," + this.ItemName + "记录 '" + info.OldRecPath + "' 已经被成功移动到 '" + strTargetPath + "' ,但在写入新内容时发生错误: " + strError; if (channel.ErrorCode == ChannelErrorCode.TimestampMismatch) { // 不进行反复处理。 // 因为源已经移动,情况很复杂 } // 仅仅写入错误日志即可。没有Undo this.App.WriteErrorLog(strError); error = new EntityInfo(info); error.ErrorInfo = "移动操作发生错误:" + strError; error.ErrorCode = channel.OriginErrorCode; ErrorInfos.Add(error); return -1; } else // 成功 { info.NewRecPath = strOutputPath; // 兑现保存的位置,因为可能有追加形式的路径 DomUtil.SetElementText(domOperLog.DocumentElement, "action", "move"); // 新记录 XmlNode node = DomUtil.SetElementText(domOperLog.DocumentElement, "record", strNewXml); DomUtil.SetAttr(node, "recPath", info.NewRecPath); // 旧记录 node = DomUtil.SetElementText(domOperLog.DocumentElement, "oldRecord", strExistSourceXml); DomUtil.SetAttr(node, "recPath", info.OldRecPath); // 保存成功,需要返回信息元素。因为需要返回新的时间戳 error = new EntityInfo(info); error.NewTimestamp = output_timestamp; error.NewRecord = strNewXml; error.ErrorInfo = "移动操作成功。NewRecPath中返回了实际保存的路径, NewTimeStamp中返回了新的时间戳,NewRecord中返回了实际保存的新记录(可能和提交的源记录稍有差异)。"; if (string.IsNullOrEmpty(strWarning) == false) { error.ErrorInfo = "移动操作成功。但" + strWarning; error.ErrorCode = ErrorCodeValue.PartialDenied; } else error.ErrorCode = ErrorCodeValue.NoError; ErrorInfos.Add(error); } return 0; ERROR1: error = new EntityInfo(info); error.ErrorInfo = strError; error.ErrorCode = ErrorCodeValue.CommonError; ErrorInfos.Add(error); return -1; }
// 从若干读者记录中,清除特定的所借事项。相当于针对读者记录执行了还书操作 // parameters: // return: // -1 出错 static int ClearBorrowItem( RmsChannel channel, string strBorrowItemBarcode, List<string> aPath, out string strError) { strError = ""; for (int i = 0; i < aPath.Count; i++) { string strXml = ""; string strMetaData = ""; string strOutputPath = ""; byte[] timestamp = null; string strPath = aPath[i]; long lRet = channel.GetRes(strPath, out strXml, out strMetaData, out timestamp, out strOutputPath, out strError); if (lRet == -1) goto ERROR1; // 装入DOM XmlDocument dom = new XmlDocument(); try { dom.LoadXml(strXml); } catch (Exception ex) { strError = "记录 '" + strPath + "' XML装入DOM出错: " + ex.Message; goto ERROR1; } bool bChanged = false; XmlNodeList nodes = dom.DocumentElement.SelectNodes("//borrows/borrow[@barcode='"+strBorrowItemBarcode+"']"); for(int j=0;j<nodes.Count;j++) { XmlNode node = nodes[j]; if (node.ParentNode != null) { node.ParentNode.RemoveChild(node); bChanged = true; } } if (bChanged == true) { // 写读者记录 byte[] output_timestamp = null; lRet = channel.DoSaveTextRes(strPath, dom.OuterXml, false, "content,ignorechecktimestamp", timestamp, out output_timestamp, out strOutputPath, out strError); if (lRet == -1) { strError = "写入读者记录 '" + strPath + "' 时发生错误: " + strError; return -1; } } } return 0; ERROR1: return -1; }
// 将刚从dt1000升级上来的读者和实体记录进行交叉处理 // parameters: // nStart 从第几个借阅的册事项开始处理 // nCount 共处理几个借阅的册事项 // nProcessedBorrowItems [out]本次处理了多少个借阅册事项 // nTotalBorrowItems [out]当前读者一共包含有多少个借阅册事项 // result.Value // -1 错误。 // 0 成功。 // 1 有警告 public LibraryServerResult CrossRefBorrowInfo( // RmsChannelCollection Channels, RmsChannel channel, string strReaderBarcode, int nStart, int nCount, out int nProcessedBorrowItems, out int nTotalBorrowItems) { string strError = ""; nTotalBorrowItems = 0; nProcessedBorrowItems = 0; int nRet = 0; string strWarning = ""; int nRedoCount = 0; // string strCheckError = ""; LibraryServerResult result = new LibraryServerResult(); // int nErrorCount = 0; REDO_CHANGE_READERREC: // 加读者记录锁 #if DEBUG_LOCK_READER this.WriteErrorLog("CrossRefBorrowInfo 开始为读者加写锁 '" + strReaderBarcode + "'"); #endif this.ReaderLocks.LockForWrite(strReaderBarcode); try // 读者记录锁定范围开始 { // 读入读者记录 string strReaderXml = ""; string strOutputReaderRecPath = ""; byte[] reader_timestamp = null; nRet = this.GetReaderRecXml( // Channels, channel, strReaderBarcode, out strReaderXml, out strOutputReaderRecPath, out reader_timestamp, out strError); if (nRet == 0) { result.Value = -1; result.ErrorInfo = "读者证条码号 '" + strReaderBarcode + "' 不存在"; result.ErrorCode = ErrorCode.ReaderBarcodeNotFound; return result; } if (nRet == -1) { strError = "读入读者记录时发生错误: " + strError; goto ERROR1; } XmlDocument readerdom = null; nRet = LibraryApplication.LoadToDom(strReaderXml, out readerdom, out strError); if (nRet == -1) { strError = "装载读者记录进入XML DOM时发生错误: " + strError; goto ERROR1; } bool bReaderRecChanged = false; // 修改读者记录中overdues/overdue中的价格单位,并加入id // return: // -1 error // 0 not changed // 1 changed nRet = ModifyReaderRecord( ref readerdom, out strWarning, out strError); if (nRet == -1) goto ERROR1; if (nRet == 1) bReaderRecChanged = true; // TODO: strWarning内容如何处理? XmlNodeList nodesBorrow = readerdom.DocumentElement.SelectNodes("borrows/borrow"); nTotalBorrowItems = nodesBorrow.Count; if (nTotalBorrowItems == 0) { result.Value = 0; result.ErrorInfo = "读者记录中没有借还信息。"; return result; } if (nStart >= nTotalBorrowItems) { strError = "nStart参数值" + nStart.ToString() + "大于当前读者记录中的借阅册个数" + nTotalBorrowItems.ToString(); goto ERROR1; } nProcessedBorrowItems = 0; for (int i = nStart; i < nTotalBorrowItems; i++) { if (nCount != -1 && nProcessedBorrowItems >= nCount) break; // 一个API最多做10条 if (nProcessedBorrowItems >= 10) break; XmlNode nodeBorrow = nodesBorrow[i]; string strItemBarcode = DomUtil.GetAttr(nodeBorrow, "barcode"); nProcessedBorrowItems++; if (String.IsNullOrEmpty(strItemBarcode) == true) { strWarning += "读者记录中<borrow>元素barcode属性值不能为空; "; continue; } string strBorrowDate = DomUtil.GetAttr(nodeBorrow, "borrowDate"); string strBorrowPeriod = DomUtil.GetAttr(nodeBorrow, "borrowPeriod"); if (String.IsNullOrEmpty(strBorrowDate) == true) { strWarning += "读者记录中<borrow>元素borrowDate属性不能为空; "; continue; } if (String.IsNullOrEmpty(strBorrowPeriod) == true) { strWarning += "读者记录中<borrow>元素borrowPeriod属性不能为空; "; continue; } // 把实体记录借阅信息详细化 // return: // 0 册条码号没有找到对应的册记录 // 1 成功 nRet = ModifyEntityRecord( // Channels, channel, null, // strEntityRecPath strItemBarcode, strReaderBarcode, strBorrowDate, strBorrowPeriod, out strError); if (nRet == -1) { strWarning += "ModifyEntityRecord() [strItemBarcode='" + strItemBarcode + "' strReaderBarcode='" + strReaderBarcode + "'] error : " + strError + "; "; continue; } // 2008/10/7 if (nRet == 0) { strWarning += "册条码号 '" + strItemBarcode + "' 对应的记录不存在; "; continue; } } if (bReaderRecChanged == true) { byte[] output_timestamp = null; string strOutputPath = ""; #if NO RmsChannel channel = Channels.GetChannel(this.WsUrl); if (channel == null) { strError = "get channel error"; goto ERROR1; } #endif // 写回读者记录 long lRet = channel.DoSaveTextRes(strOutputReaderRecPath, readerdom.OuterXml, false, "content", // ,ignorechecktimestamp reader_timestamp, out output_timestamp, out strOutputPath, out strError); if (lRet == -1) { if (channel.ErrorCode == ChannelErrorCode.TimestampMismatch) { nRedoCount++; if (nRedoCount > 10) { strError = "写回读者记录的时候,遇到时间戳冲突,并因此重试10次,仍失败..."; goto ERROR1; } goto REDO_CHANGE_READERREC; } goto ERROR1; } // 及时更新时间戳 reader_timestamp = output_timestamp; } } finally { this.ReaderLocks.UnlockForWrite(strReaderBarcode); #if DEBUG_LOCK_READER this.WriteErrorLog("CrossRefBorrowInfo 结束为读者加写锁 '" + strReaderBarcode + "'"); #endif } if (String.IsNullOrEmpty(strWarning) == false) { result.Value = 1; result.ErrorInfo = strWarning; } else result.Value = 0; return result; ERROR1: result.Value = -1; result.ErrorInfo = strError; result.ErrorCode = ErrorCode.SystemError; return result; }
// 去除读者记录侧的借阅信息链条 // return: // -1 出错 // 0 没有必要修复 // 1 修复成功 int RemoveReaderSideLink( // RmsChannelCollection Channels, RmsChannel channel, string strReaderBarcode, string strItemBarcode, out string strRemovedInfo, out string strError) { strError = ""; strRemovedInfo = ""; int nRedoCount = 0; // 因为时间戳冲突, 重试的次数 REDO_REPAIR: // 读入读者记录 string strReaderXml = ""; string strOutputReaderRecPath = ""; byte[] reader_timestamp = null; int nRet = this.GetReaderRecXml( // Channels, channel, strReaderBarcode, out strReaderXml, out strOutputReaderRecPath, out reader_timestamp, out strError); if (nRet == 0) { strError = "读者证条码号 '" + strReaderBarcode + "' 不存在"; return 0; } if (nRet == -1) { strError = "读入读者记录时发生错误: " + strError; return -1; } XmlDocument readerdom = null; nRet = LibraryApplication.LoadToDom(strReaderXml, out readerdom, out strError); if (nRet == -1) { strError = "装载读者记录进入XML DOM时发生错误: " + strError; return -1; } // 校验读者证条码号参数是否和XML记录中完全一致 string strTempBarcode = DomUtil.GetElementText(readerdom.DocumentElement, "barcode"); if (strReaderBarcode != strTempBarcode) { strError = "修复操作被拒绝。因读者证条码号参数 '" + strReaderBarcode + "' 和读者记录中<barcode>元素内的读者证条码号值 '" + strTempBarcode + "' 不一致。"; return -1; } XmlNode nodeBorrow = readerdom.DocumentElement.SelectSingleNode("borrows/borrow[@barcode='" + strItemBarcode + "']"); if (nodeBorrow == null) { strError = "在读者记录 '"+strReaderBarcode+"' 中没有找到关于册条码号 '"+strItemBarcode+"' 的链"; return 0; } strRemovedInfo = nodeBorrow.OuterXml; // 移除读者记录侧的链 nodeBorrow.ParentNode.RemoveChild(nodeBorrow); #if NO RmsChannel channel = Channels.GetChannel(this.WsUrl); if (channel == null) { strError = "get channel error"; return -1; } #endif byte[] output_timestamp = null; string strOutputPath = ""; // 写回读者记录 long lRet = channel.DoSaveTextRes(strOutputReaderRecPath, readerdom.OuterXml, false, "content", // ,ignorechecktimestamp reader_timestamp, out output_timestamp, out strOutputPath, out strError); if (lRet == -1) { if (channel.ErrorCode == ChannelErrorCode.TimestampMismatch) { nRedoCount++; if (nRedoCount > 10) { strError = "写回读者记录 '" + strOutputReaderRecPath + "' 的时候,遇到时间戳冲突,并因此重试10次,仍失败..."; return -1; } goto REDO_REPAIR; } return -1; } return 1; }
// 保存记录 // parameters: // strRecordPath 记录路径。如果==null,表示直接用textBox_recPath中当前的内容作为路径 public void SaveRecord(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 strError; string strXml = ""; bool bHasUploadedFile = false; int nRet = GetXmlRecord(out strXml, out bHasUploadedFile, out strError); if (nRet == -1) { MessageBox.Show(this, strError); return; } byte [] baOutputTimeStamp = null; string strOutputPath = ""; long lRet = 0; int nUploadCount = 0; // 使用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); //string strTemp = ByteArray.GetHexTimeStampString(this.TimeStamp); if (String.IsNullOrEmpty(this.strDatabaseOriginPath) == false && bHasUploadedFile == true && respath.FullPath != this.strDatabaseOriginPath) { ResPath respath_old = new ResPath(this.strDatabaseOriginPath); if (respath_old.Url != respath.Url) { MessageBox.Show(this, "目前暂不支持跨服务器情况下的资源复制。本记录中原有的已上载资源,在另存到目标库的时丢失(为空),请注意保存完后手动上载。"); goto SKIPCOPYRECORD; } // 复制记录 // return: // -1 出错。错误信息在strError中 // 0或者其他 成功 nRet = channel.DoCopyRecord(respath_old.Path, respath.Path, false, // bool bDeleteOriginRecord, out baOutputTimeStamp, out strOutputPath, out strError); if (nRet == -1) { MessageBox.Show(this, "复制资源时发生错误: " + strError); } else { // 为继续保存最新XML记录作准备 respath.Path = strOutputPath; // ?形式路径其实已经确定 this.TimeStamp = baOutputTimeStamp; } } SKIPCOPYRECORD: lRet = channel.DoSaveTextRes(respath.Path, strXml, false, // bInlucdePreamble "", // style this.TimeStamp, out baOutputTimeStamp, out strOutputPath, out strError); EnableControlsInLoading(false); stop.EndLoop(); stop.OnStop -= new StopEventHandler(this.DoStop); stop.Initial(""); if (lRet == -1) { MessageBox.Show(this, "保存记录失败,原因: "+strError); return; } // this.TimeStamp = baOutputTimeStamp; respath.Path = strOutputPath; textBox_recPath.Text = respath.FullPath; //// this.strDatabaseOriginPath = respath.Url + "?" + strOutputPath; // 保存从数据库中来的原始path stop.OnStop += new StopEventHandler(this.DoStop); stop.Initial("正在保存资源 " + respath.FullPath); stop.BeginLoop(); EnableControlsInLoading(true); Debug.Assert(channel != null, ""); // 保存对象资源,循环对象列表就可以了 nUploadCount = this.listView_resFiles.DoUpload( respath.Path, channel, stop, out strError); EnableControlsInLoading(false); stop.EndLoop(); stop.OnStop -= new StopEventHandler(this.DoStop); stop.Initial(""); } finally { channel = channelSave; } if (nUploadCount == -1) { MessageBox.Show(this, "XML记录保存成功, 但保存资源失败,原因: "+strError); return; } if (nUploadCount > 0) { // 使用Channel channelSave = channel; channel = Channels.GetChannel(respath.Url); Debug.Assert(channel != null, "Channels.GetChannel 异常"); // 需要重新获得时间戳 string strStyle = "timestamp,metadata"; // withresmetadata string strMetaData = ""; string strContent = ""; try { lRet = channel.GetRes(respath.Path, strStyle, out strContent, out strMetaData, out baOutputTimeStamp, out strOutputPath, out strError); if (lRet == -1) { MessageBox.Show(this, "重新获得时间戳 '" + respath.FullPath + "' 失败。原因 : " + strError); return; } } finally { channel = channelSave; } this.TimeStamp = baOutputTimeStamp; // 设置时间戳很重要。即便xml不合法,也应设置好时间戳,否则窗口无法进行正常删除。 this.m_strMetaData = strMetaData; // 记忆XML记录的元数据 } this.Changed = false; MessageBox.Show(this, "保存记录 '" + respath.FullPath + "' 成功。"); }
// 复制属于同一书目记录的全部实体记录 // 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; }