// 创建租金交费请求 // parameters: // strOutputReaderXml 返回修改后的读者记录 // strOutputID 返回本次创建的交费请求的 ID // result.Value -1 出错 其他 本次创建的交费请求条数 public LibraryServerResult Hire( SessionInfo sessioninfo, string strAction, string strReaderBarcode, out string strOutputReaderXml, out string strOutputID) { strOutputReaderXml = ""; strOutputID = ""; string strError = ""; int nRet = 0; LibraryServerResult result = new LibraryServerResult(); string strReaderXml = ""; strAction = strAction.ToLower(); if (strAction == "hire") { // 权限判断 if (StringUtil.IsInList("hire", sessioninfo.RightsOrigin) == false) { result.Value = -1; result.ErrorInfo = "创建租金交费请求的操作被拒绝。不具备 hire 权限。"; result.ErrorCode = ErrorCode.AccessDenied; return result; } } else if (strAction == "hirelate") { // 权限判断 if (StringUtil.IsInList("hirelate", sessioninfo.RightsOrigin) == false) { result.Value = -1; result.ErrorInfo = "(延迟)创建租金交费请求的操作被拒绝。不具备 hirelate 权限。"; result.ErrorCode = ErrorCode.AccessDenied; return result; } } RmsChannel channel = sessioninfo.Channels.GetChannel(this.WsUrl); if (channel == null) { strError = "get channel error"; goto ERROR1; } int nRedoCount = 0; // 因为时间戳冲突, 重试的次数 REDO_HIRE: // 加读者记录锁 #if DEBUG_LOCK_READER this.WriteErrorLog("Hire 开始为读者加读锁 '" + strReaderBarcode + "'"); #endif this.ReaderLocks.LockForRead(strReaderBarcode); try // 读者记录锁定范围开始 { // 读入读者记录 string strOutputReaderRecPath = ""; byte[] reader_timestamp = null; nRet = this.GetReaderRecXml( // sessioninfo.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; } string strLibraryCode = ""; // 看看读者记录所从属的读者库的馆代码,是否被当前用户管辖 if (String.IsNullOrEmpty(strOutputReaderRecPath) == false) { // 检查当前操作者是否管辖这个读者库 // 观察一个读者记录路径,看看是不是在当前用户管辖的读者库范围内? if (this.IsCurrentChangeableReaderPath(strOutputReaderRecPath, sessioninfo.LibraryCodeList, out strLibraryCode) == false) { strError = "读者记录路径 '" + strOutputReaderRecPath + "' 从属的读者库不在当前用户管辖范围内"; goto ERROR1; } } XmlDocument readerdom = null; nRet = LibraryApplication.LoadToDom(strReaderXml, out readerdom, out strError); if (nRet == -1) { strError = "装载读者记录进入XML DOM时发生错误: " + strError; goto ERROR1; } // 检查当前是不是已经有了租金交费请求 XmlNodeList nodeOverdues = readerdom.DocumentElement.SelectNodes("overdues/overdue"); for (int i = 0; i < nodeOverdues.Count; i++) { XmlNode node = nodeOverdues[i]; string strWord = "租金。"; string strReason = DomUtil.GetAttr(node, "reason"); if (strReason.Length < strWord.Length) continue; string strPart = strReason.Substring(0, strWord.Length); if (strPart == strWord) { strError = "读者 '" + strReaderBarcode + "' 已经存在租金交费请求。需要先将此租金请求交费完成后,才能创建新的租金交费请求。"; goto ERROR1; } } string strOperTime = this.Clock.GetClock(); string strOverdueString = ""; // 根据Hire() API要求,修改readerdom nRet = DoHire(strAction, readerdom, ref strOutputID, // 为空表示函数内自动创建id sessioninfo.UserID, strOperTime, out strOverdueString, out strError); if (nRet == -1) goto ERROR1; #if NO // *** RmsChannel channel = sessioninfo.Channels.GetChannel(this.WsUrl); if (channel == null) { strError = "get channel error"; goto ERROR1; } #endif byte[] output_timestamp = null; string strOutputPath = ""; strOutputReaderXml = readerdom.OuterXml; // 写回读者记录 long lRet = channel.DoSaveTextRes(strOutputReaderRecPath, strOutputReaderXml, 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_HIRE; } goto ERROR1; } // 增量总量 if (this.Statis != null) this.Statis.IncreaseEntryValue( strLibraryCode, "租金", "创建交费请求次", 1); // TODO: 增加价格量? XmlDocument domOperLog = new XmlDocument(); domOperLog.LoadXml("<root />"); DomUtil.SetElementText(domOperLog.DocumentElement, "libraryCode", strLibraryCode); // 读者所在的馆代码 DomUtil.SetElementText(domOperLog.DocumentElement, "operation", "hire"); DomUtil.SetElementText(domOperLog.DocumentElement, "action", strAction); DomUtil.SetElementText(domOperLog.DocumentElement, "readerBarcode", strReaderBarcode); // 新增的细节字符串 一个或者多个<overdue> OuterXml内容 DomUtil.SetElementText(domOperLog.DocumentElement, "overdues", strOverdueString/*nodeOverdue.OuterXml*/); // 新的读者记录 XmlNode nodeReaderRecord = DomUtil.SetElementText(domOperLog.DocumentElement, "readerRecord", readerdom.OuterXml); DomUtil.SetAttr(nodeReaderRecord, "recPath", strOutputReaderRecPath); DomUtil.SetElementText(domOperLog.DocumentElement, "operator", sessioninfo.UserID); // string strOperTime = this.Clock.GetClock(); DomUtil.SetElementText(domOperLog.DocumentElement, "operTime", strOperTime); nRet = this.OperLog.WriteOperLog(domOperLog, sessioninfo.ClientAddress, out strError); if (nRet == -1) { strError = "PassGate() API 写入日志时发生错误: " + strError; goto ERROR1; } } // 读者记录锁定范围结束 finally { this.ReaderLocks.UnlockForRead(strReaderBarcode); #if DEBUG_LOCK_READER this.WriteErrorLog("Hire 结束为读者加读锁 '" + strReaderBarcode + "'"); #endif } // END1: result.Value = 1; return result; ERROR1: result.Value = -1; result.ErrorCode = ErrorCode.SystemError; result.ErrorInfo = strError; return result; }
// parameters: // strBrowseInfoStyle 返回的特性。cols 返回实体记录的浏览列 public LibraryServerResult GetCallNumberSearchResult( SessionInfo sessioninfo, string strArrangeGroupName, string strResultSetName, long lStart, long lCount, string strBrowseInfoStyle, string strLang, out CallNumberSearchResult[] searchresults) { string strError = ""; searchresults = null; LibraryServerResult result = new LibraryServerResult(); // int nRet = 0; long lRet = 0; if (String.IsNullOrEmpty(strArrangeGroupName) == true) { strError = "strArrangeGroupName参数值不能为空"; goto ERROR1; } if (strArrangeGroupName[0] == '!') { string strTemp = GetArrangeGroupName(strArrangeGroupName.Substring(1)); if (strTemp == null) { strError = "馆藏地点名 " + strArrangeGroupName.Substring(1) + " 没有找到对应的排架体系名"; goto ERROR1; } strArrangeGroupName = strTemp; } RmsChannel channel = sessioninfo.Channels.GetChannel(this.WsUrl); if (channel == null) { result.Value = -1; result.ErrorInfo = "get channel error"; result.ErrorCode = ErrorCode.SystemError; return result; } if (String.IsNullOrEmpty(strResultSetName) == true) strResultSetName = "default"; bool bCols = StringUtil.IsInList("cols", strBrowseInfoStyle); string strBrowseStyle = "keyid,id,key"; if (bCols == true) strBrowseStyle += ",cols,format:cfgs/browse_callnumber"; Record[] origin_searchresults = null; // lRet = channel.DoGetSearchResult( strResultSetName, lStart, lCount, strBrowseStyle, // "id", strLang, null, out origin_searchresults, out strError); if (lRet == -1) goto ERROR1; long lResultCount = lRet; searchresults = new CallNumberSearchResult[origin_searchresults.Length]; for (int i = 0; i < origin_searchresults.Length; i++) { CallNumberSearchResult item = new CallNumberSearchResult(); Record record = origin_searchresults[i]; item.ItemRecPath = record.Path; searchresults[i] = item; string strLocation = ""; item.CallNumber = BuildAccessNoKeyString(record.Keys, out strLocation); if (bCols == true && record.Cols != null) { if (record.Cols.Length > 0) item.ParentID = record.Cols[0]; if (record.Cols.Length > 1) item.Location = record.Cols[1]; if (record.Cols.Length > 2) item.Barcode = record.Cols[2]; } if (string.IsNullOrEmpty(item.Location) == true) item.Location = strLocation; // 用从keys中得来的代替。可能有大小写的差异 --- keys中都是大写 #if NO if (bCols == true) { // 继续填充其余成员 string strXml = ""; string strMetaData = ""; byte[] timestamp = null; string strOutputPath = ""; lRet = channel.GetRes(item.ItemRecPath, out strXml, out strMetaData, out timestamp, out strOutputPath, out strError); if (lRet == -1) { item.ErrorInfo = "获取记录 '" + item.ItemRecPath + "' 出错: " + strError; continue; } XmlDocument dom = new XmlDocument(); try { dom.LoadXml(strXml); } catch (Exception ex) { item.ErrorInfo = "记录 '" + item.ItemRecPath + "' XML装载到DOM时出错: " + ex.Message; continue; } /* item.CallNumber = DomUtil.GetElementText(dom.DocumentElement, "accessNo"); * */ item.ParentID = DomUtil.GetElementText(dom.DocumentElement, "parent"); item.Location = DomUtil.GetElementText(dom.DocumentElement, "location"); item.Barcode = DomUtil.GetElementText(dom.DocumentElement, "barcode"); } #endif } result.Value = lResultCount; return result; ERROR1: result.Value = -1; result.ErrorCode = ErrorCode.SystemError; result.ErrorInfo = strError; return result; }
// 设置种次号尾号 public LibraryServerResult SetOneClassTailNumber( SessionInfo sessioninfo, string strAction, string strArrangeGroupName, string strClass, string strTestNumber, out string strOutputNumber) { strOutputNumber = ""; string strError = ""; LibraryServerResult result = new LibraryServerResult(); RmsChannel channel = sessioninfo.Channels.GetChannel(this.WsUrl); if (channel == null) { strError = "get channel error"; goto ERROR1; } string strPath = ""; string strXml = ""; byte[] timestamp = null; // 检索尾号记录的路径和记录体 // return: // -1 error // 0 not found // 1 found int nRet = SearchOneClassTailNumberPathAndRecord( // sessioninfo.Channels, channel, strArrangeGroupName, strClass, out strPath, out strXml, out timestamp, out strError); if (nRet == -1) goto ERROR1; string strZhongcihaoDbName = GetTailDbName(strArrangeGroupName); if (String.IsNullOrEmpty(strZhongcihaoDbName) == true) { // TODO: 这里报错还需要精确一些,对于带有'!'的馆藏地点名 strError = "无法通过排架体系名 '" + strArrangeGroupName + "' 获得种次号库名"; goto ERROR1; } // byte[] baOutputTimestamp = null; bool bNewRecord = false; long lRet = 0; #if NO RmsChannel channel = sessioninfo.Channels.GetChannel(this.WsUrl); if (channel == null) { strError = "get channel error"; goto ERROR1; } #endif byte[] output_timestamp = null; string strOutputPath = ""; if (strAction == "conditionalpush") { if (nRet == 0) { // 新创建记录 strPath = strZhongcihaoDbName + "/?"; strXml = "<r c='" + strClass + "' v='" + strTestNumber + "'/>"; bNewRecord = true; } else { string strPartXml = "/xpath/<locate>@v</locate><action>Push</action>"; strPath += strPartXml; strXml = strTestNumber; bNewRecord = false; } lRet = channel.DoSaveTextRes(strPath, strXml, false, "content", timestamp, // timestamp, out output_timestamp, out strOutputPath, out strError); if (lRet == -1) { strError = "保存尾号记录时出错: " + strError; goto ERROR1; } if (bNewRecord == true) { strOutputNumber = strTestNumber; } else { strOutputNumber = strError; } goto END1; } else if (strAction == "increase") { string strDefaultNumber = strTestNumber; if (nRet == 0) { // 新创建记录 strPath = strZhongcihaoDbName + "/?"; strXml = "<r c='" + strClass + "' v='" + strDefaultNumber + "'/>"; bNewRecord = true; } else { string strPartXml = "/xpath/<locate>@v</locate><action>+AddInteger</action>"; strPath += strPartXml; strXml = "1"; bNewRecord = false; } // lRet = channel.DoSaveTextRes(strPath, strXml, false, "content", timestamp, // timestamp, out output_timestamp, out strOutputPath, out strError); if (lRet == -1) { strError = "保存尾号记录时出错: " + strError; goto ERROR1; } if (bNewRecord == true) { strOutputNumber = strDefaultNumber; } else { strOutputNumber = strError; } goto END1; } else if (strAction == "save") { string strTailNumber = strTestNumber; if (nRet == 0) { strPath = strZhongcihaoDbName + "/?"; } else { // 覆盖记录 if (String.IsNullOrEmpty(strPath) == true) { strError = "记录存在时strPath居然为空"; goto ERROR1; } } strXml = "<r c='" + strClass + "' v='" + strTailNumber + "'/>"; lRet = channel.DoSaveTextRes(strPath, strXml, false, "content", timestamp, // timestamp, out output_timestamp, out strOutputPath, out strError); if (lRet == -1) { if (channel.ErrorCode == ChannelErrorCode.TimestampMismatch) { strError = "尾号记录时间戳不匹配,说明可能被他人修改过。详细原因: " + strError; goto ERROR1; } strError = "保存尾号记录时出错: " + strError; goto ERROR1; } } else { strError = "无法识别的strAction参数值 '" + strAction + "'"; goto ERROR1; } END1: result.Value = 1; return result; ERROR1: result.Value = -1; result.ErrorCode = ErrorCode.SystemError; result.ErrorInfo = strError; return result; }
// 设置/保存事项信息 // parameters: // strBiblioRecPath 书目记录路径,仅包含库名和id部分。库名可以用来确定书目库,id可以被实体记录用来设置<parent>元素内容。另外书目库名和IssueInfo中的NewRecPath形成映照关系,需要检查它们是否正确对应 // issueinfos 要提交的的期信息数组 // 权限:需要有setissues权限 // 修改意见: 写入期库中的记录, 还缺乏<operator>和<operTime>字段 // TODO: 需要改写,增加upgrade中直接写入不查重不创建事件日志的功能 // TODO: 需要检查订购记录的<parent>元素内容是否合法。不能为问号 public LibraryServerResult SetItems( SessionInfo sessioninfo, string strBiblioRecPath, EntityInfo[] iteminfos, out EntityInfo[] errorinfos) { errorinfos = null; LibraryServerResult result = new LibraryServerResult(); int nRet = 0; long lRet = 0; string strError = ""; string strBiblioDbName = ResPath.GetDbName(strBiblioRecPath); string strBiblioRecId = ResPath.GetRecordId(strBiblioRecPath); if (string.IsNullOrEmpty(strBiblioRecPath) == false) // 2013/9/26 { if (String.IsNullOrEmpty(strBiblioRecId) == true) { strError = "书目记录路径 '" + strBiblioRecPath + "' 中的记录ID部分不能为空"; goto ERROR1; } if (StringUtil.IsPureNumber(strBiblioRecId) == false) { strError = "书目记录路径 '" + strBiblioRecPath + "' 中的记录ID部分 '" + strBiblioRecId + "' 格式不正确,应为纯数字"; goto ERROR1; } } // 获得书目库对应的事项库名 string strItemDbName = ""; nRet = this.GetItemDbName(strBiblioDbName, out strItemDbName, out strError); if (nRet == -1) goto ERROR1; #if NO if (nRet == 0) { strError = "书目库名 '" + strBiblioDbName + "' 没有找到"; goto ERROR1; } if (String.IsNullOrEmpty(strItemDbName) == true) { strError = "书目库名 '" + strBiblioDbName + "' 对应的"+this.ItemName+"库名没有定义"; goto ERROR1; } #endif // 2012/3/29 if (sessioninfo == null) { strError = "sessioninfo == null"; goto ERROR1; } if (sessioninfo.Channels == null) { strError = "sessioninfo.Channels == null"; goto ERROR1; } RmsChannel channel = sessioninfo.Channels.GetChannel(this.App.WsUrl); if (channel == null) { strError = "get channel error"; goto ERROR1; } byte[] output_timestamp = null; string strOutputPath = ""; List<EntityInfo> ErrorInfos = new List<EntityInfo>(); if (iteminfos == null) { strError = "iteminfos == null"; goto ERROR1; } for (int i = 0; i < iteminfos.Length; i++) { EntityInfo info = iteminfos[i]; if (info == null) { Debug.Assert(false, ""); continue; } string strAction = info.Action; bool bForce = false; // 是否为强制操作(强制操作不去除源记录中的流通信息字段内容) bool bNoCheckDup = false; // 是否为不查重? bool bNoEventLog = false; // 是否为不记入事件日志? string strStyle = info.Style; if (StringUtil.IsInList("force", info.Style) == true) { if (sessioninfo.UserType == "reader") { result.Value = -1; result.ErrorInfo = "带有风格 'force' 的修改" + this.ItemName + "信息的" + strAction + "操作被拒绝。读者身份不能进行这样的操作。"; result.ErrorCode = ErrorCode.AccessDenied; return result; } bForce = true; if (StringUtil.IsInList("restore", sessioninfo.RightsOrigin) == false) { result.Value = -1; result.ErrorInfo = "带有风格 'force' 的修改"+this.ItemName+"信息的" + strAction + "操作被拒绝。不具备restore权限。"; result.ErrorCode = ErrorCode.AccessDenied; return result; } } if (StringUtil.IsInList("nocheckdup", info.Style) == true) { bNoCheckDup = true; if (StringUtil.IsInList("restore", sessioninfo.RightsOrigin) == false) { result.Value = -1; result.ErrorInfo = "带有风格 'nocheckdup' 的修改"+this.ItemName+"信息的" + strAction + "操作被拒绝。不具备restore权限。"; result.ErrorCode = ErrorCode.AccessDenied; return result; } } if (StringUtil.IsInList("noeventlog", info.Style) == true) { bNoEventLog = true; if (StringUtil.IsInList("restore", sessioninfo.RightsOrigin) == false) { result.Value = -1; result.ErrorInfo = "带有风格 'noeventlog' 的修改"+this.ItemName+"信息的" + strAction + "操作被拒绝。不具备restore权限。"; result.ErrorCode = ErrorCode.AccessDenied; return result; } } // 对info内的参数进行检查。 strError = ""; if (iteminfos.Length > 1 // 2013/9/26 只有一个记录的时候,不必依靠 refid 定位返回信息,因而也就不需要明显给出这个 RefID 成员了 && String.IsNullOrEmpty(info.RefID) == true) { strError = "info.RefID 没有给出"; } if (string.IsNullOrEmpty(info.NewRecPath) == false && info.NewRecPath.IndexOf(",") != -1) { strError = "info.NewRecPath值 '" + info.NewRecPath + "' 中不能包含逗号"; } else if (string.IsNullOrEmpty(info.OldRecPath) == false && info.OldRecPath.IndexOf(",") != -1) { strError = "info.OldRecPath值 '" + info.OldRecPath + "' 中不能包含逗号"; } // TODO: 当操作为"delete"时,是否可以允许只设置OldRecPath,而不必设置NewRecPath // 如果两个都设置,则要求设置为一致的。 if (info.Action == "delete") { if (String.IsNullOrEmpty(info.NewRecord) == false) { strError = "strAction值为delete时, info.NewRecord参数必须为空"; } else if (info.NewTimestamp != null) { strError = "strAction值为delete时, info.NewTimestamp参数必须为空"; } // 2008/6/24 else if (String.IsNullOrEmpty(info.NewRecPath) == false) { if (info.NewRecPath != info.OldRecPath) { strError = "strAction值为delete时, 如果info.NewRecPath不空,则其内容必须和info.OldRecPath一致。(info.NewRecPath='" + info.NewRecPath + "' info.OldRecPath='" + info.OldRecPath + "')"; } } } else { // 非delete情况 info.NewRecord则必须不为空 if (String.IsNullOrEmpty(info.NewRecord) == true) { strError = "strAction值为" + info.Action + "时, info.NewRecord参数不能为空"; } } if (info.Action == "new") { if (String.IsNullOrEmpty(info.OldRecord) == false) { strError = "strAction值为new时, info.OldRecord参数必须为空"; } else if (info.OldTimestamp != null) { strError = "strAction值为new时, info.OldTimestamp参数必须为空"; } } if (strError != "") { EntityInfo error = new EntityInfo(info); error.ErrorInfo = strError; error.ErrorCode = ErrorCodeValue.CommonError; ErrorInfos.Add(error); continue; } // 检查路径中的库名部分 if (String.IsNullOrEmpty(info.NewRecPath) == false) { strError = ""; string strDbName = ResPath.GetDbName(info.NewRecPath); if (String.IsNullOrEmpty(strDbName) == true) { strError = "NewRecPath中数据库名不应为空"; } if (string.IsNullOrEmpty(strItemDbName) == false // 有可能前面 strBiblioRecPath 为空,则 strItemDbName 也为空 && strDbName != strItemDbName) { // 检测是否为其他语言的等同库名 // parameters: // strDbName 要检测的数据库名 // strNeutralDbName 已知的中立语言数据库名 if (this.App.IsOtherLangName(strDbName, strItemDbName) == false) { if (strAction == "copy" || strAction == "move") { // 再看strDbName是否至少是一个实体库 if (this.IsItemDbName(strDbName) == false) strError = "RecPath中数据库名 '" + strDbName + "' 不正确,应为"+this.ItemName+"库名"; } else strError = "RecPath中数据库名 '" + strDbName + "' 不正确,应为 '" + strItemDbName + "'。(因为书目库名为 '" + strBiblioDbName + "',其对应的" + this.ItemName + "库名应为 '" + strItemDbName + "' )"; } } else if (string.IsNullOrEmpty(strItemDbName) == true) // 2013/9/26 { // 要检查看看 strDbName 是否为一个实体库名 if (this.IsItemDbName(strDbName) == false) strError = "RecPath中数据库名 '" + strDbName + "' 不正确,应为"+this.ItemName+"库名"; } if (strError != "") { EntityInfo error = new EntityInfo(info); error.ErrorInfo = strError; error.ErrorCode = ErrorCodeValue.CommonError; ErrorInfos.Add(error); continue; } } // 把(前端发过来的)旧记录装载到DOM XmlDocument domOldRec = new XmlDocument(); try { // 用strOldRecord的目的是不想改变info.OldRecord内容, 因为后者可能被复制到输出信息中 string strOldRecord = info.OldRecord; if (String.IsNullOrEmpty(strOldRecord) == true) strOldRecord = "<root />"; domOldRec.LoadXml(strOldRecord); } catch (Exception ex) { strError = "info.OldRecord XML记录装载到DOM时出错: " + ex.Message; EntityInfo error = new EntityInfo(info); error.ErrorInfo = strError; error.ErrorCode = ErrorCodeValue.CommonError; ErrorInfos.Add(error); continue; } // 把要保存的新记录装载到DOM XmlDocument domNewRec = new XmlDocument(); try { // 用strNewRecord的目的是不想改变info.NewRecord内容, 因为后者可能被复制到输出信息中 string strNewRecord = info.NewRecord; if (String.IsNullOrEmpty(strNewRecord) == true) strNewRecord = "<root />"; domNewRec.LoadXml(strNewRecord); } catch (Exception ex) { strError = "info.NewRecord XML记录装载到DOM时出错: " + ex.Message; EntityInfo error = new EntityInfo(info); error.ErrorInfo = strError; error.ErrorCode = ErrorCodeValue.CommonError; ErrorInfos.Add(error); continue; } // locateParam多元组 准备 List<string> oldLocateParam = null; List<string> newLocateParam = null; /* string strOldPublishTime = ""; string strNewPublishTime = ""; string strOldParentID = ""; string strNewParentID = ""; * */ // 加锁用的参数 List<string> lockLocateParam = null; bool bLocked = false; try { // 命令new和change的共有部分 -- 出版时间查重, 也需要加锁 // delete则需要加锁 if (info.Action == "new" || info.Action == "change" || info.Action == "delete" || info.Action == "move") { // 仅仅用来获取一下新出版时间 // 看看新旧出版时间是否有差异 // 对IssueInfo中的OldRecord和NewRecord中包含的条码号进行比较, 看看是否发生了变化(进而就需要查重) // return: // -1 出错 // 0 相等 // 1 不相等 nRet = CompareTwoItemLocateInfo( strItemDbName, domOldRec, domNewRec, out oldLocateParam, out newLocateParam, out strError); if (nRet == -1) { strError = "CompareTwoIssueNo() error : " + strError; goto ERROR1; } bool bIsOldNewLocateSame = false; if (nRet == 0) bIsOldNewLocateSame = true; else bIsOldNewLocateSame = false; if (info.Action == "new" || info.Action == "change" || info.Action == "move") lockLocateParam = newLocateParam; else if (info.Action == "delete") { // 顺便进行一些检查 /* if (String.IsNullOrEmpty(strNewPublishTime) == false) { strError = "没有必要在delete操作的EntityInfo中, 包含NewRecord内容...。相反,注意一定要在OldRecord中包含即将删除的原记录"; goto ERROR1; } * */ if (String.IsNullOrEmpty(info.NewRecord) == false) { strError = "没有必要在delete操作的EntityInfo中, 包含NewRecord内容...。相反,注意一定要在OldRecord中包含即将删除的原记录"; goto ERROR1; } lockLocateParam = oldLocateParam; } nRet = this.IsLocateParamNullOrEmpty( lockLocateParam, out strError); if (nRet == -1) goto ERROR1; // 加锁 if (nRet == 0) { this.LockItem(lockLocateParam); bLocked = true; } bool bIsNewLocateParamNull = false; nRet = this.IsLocateParamNullOrEmpty( newLocateParam, out strError); if (nRet == -1) goto ERROR1; if (nRet == 1) bIsNewLocateParamNull = true; else bIsNewLocateParamNull = false; if ((info.Action == "new" || info.Action == "change" || info.Action == "move") // delete操作不校验记录 && bNoCheckDup == false) { nRet = this.DoVerifyItemFunction( sessioninfo, strAction, domNewRec, out strError); if (nRet != 0) { EntityInfo error = new EntityInfo(info); error.ErrorInfo = strError; error.ErrorCode = ErrorCodeValue.CommonError; ErrorInfos.Add(error); continue; } } // 进行出版时间查重 // TODO: 查重的时候要注意,如果操作类型为“move”,则可以允许查出和info.OldRecPath重的,因为它即将被删除 if (/*bIsOldNewLocateSame == false // 新旧出版时间不等,才查重。这样可以提高运行效率。 &&*/ (info.Action == "new" || info.Action == "change" || info.Action == "move") // delete操作不查重 && bIsNewLocateParamNull == false && bNoCheckDup == false) // 2008/10/19 { /* string strParentID = strNewParentID; if (String.IsNullOrEmpty(strParentID) == true) strParentID = strOldParentID; * */ // TODO: 对于期记录,oldLocateParm和newLocateParam中的parentid应当相等,预先检查好 List<string> aPath = null; // 根据 父记录ID+出版时间 对期库进行查重 // 本函数只负责查重, 并不获得记录体 // return: // -1 error // 其他 命中记录条数(不超过nMax规定的极限) nRet = this.SearchItemRecDup( // sessioninfo.Channels, channel, newLocateParam, 100, out aPath, out strError); if (nRet == -1) goto ERROR1; bool bDup = false; if (nRet == 0) { bDup = false; } else if (nRet == 1) // 命中一条 { if (aPath == null || aPath.Count == 0) { strError = "aPath == null || aPath.Count == 0"; goto ERROR1; } Debug.Assert(aPath.Count == 1, ""); if (info.Action == "new") { if (aPath[0] == info.NewRecPath) // 正好是自己 bDup = false; else bDup = true;// 别的记录中已经使用了这个条码号 } else if (info.Action == "change") { Debug.Assert(info.NewRecPath == info.OldRecPath, "当操作类型为change时,info.NewRecPath应当和info.OldRecPath相同"); if (aPath[0] == info.OldRecPath) // 正好是自己 bDup = false; else bDup = true;// 别的记录中已经使用了这个条码号 } else if (info.Action == "move") { if (aPath[0] == info.OldRecPath) // 正好是源记录 bDup = false; else bDup = true;// 别的记录中已经使用了这个条码号 } else { Debug.Assert(false, "这里不可能出现的info.Action值 '" + info.Action + "'"); } } // end of if (nRet == 1) else { Debug.Assert(nRet > 1, ""); bDup = true; // 因为move操作不允许目标位置存在记录,所以这里就不再费力考虑了 // 如果将来move操作允许目标位置存在记录,则这里需要判断:无论源还是目标位置发现条码号重,都不算重。 } // 报错 if (bDup == true) { /* string[] pathlist = new string[aPath.Count]; aPath.CopyTo(pathlist); * */ string strText = ""; // 构造定位提示信息。用于报错。 nRet = GetLocateText( newLocateParam, out strText, out strError); if (nRet == -1) { strError = "定位信息重复。并且GetLocateText()函数报错: " + strError; } else { strError = strText + " 已经被下列" + this.ItemName + "记录使用了: " + StringUtil.MakePathList(aPath)/*String.Join(",", pathlist)*/; } EntityInfo error = new EntityInfo(info); error.ErrorInfo = strError; // "出版时间 '" + strNewPublishTime + "' 已经被下列"+this.ItemName+"记录使用了: " + String.Join(",", pathlist); error.ErrorCode = ErrorCodeValue.CommonError; ErrorInfos.Add(error); continue; } } } // 准备日志DOM XmlDocument domOperLog = new XmlDocument(); domOperLog.LoadXml("<root />"); Debug.Assert(String.IsNullOrEmpty(this.OperLogSetName) == false, ""); Debug.Assert(Char.IsLower(this.OperLogSetName[0]) == true, this.OperLogSetName + " 的第一个字符应当为小写字母,这是惯例"); // 和馆代码模糊有关。如果要写入馆代码,可以考虑滞后写入 DomUtil.SetElementText(domOperLog.DocumentElement, "operation", OperLogSetName /*"setIssue"*/); // 兑现一个命令 if (info.Action == "new") { // 检查新记录的路径中的id部分是否正确 // 库名部分,前面已经统一检查过了 strError = ""; if (String.IsNullOrEmpty(info.NewRecPath) == true) { info.NewRecPath = strItemDbName + "/?"; } else { string strID = ResPath.GetRecordId(info.NewRecPath); if (String.IsNullOrEmpty(strID) == true) { strError = "RecPath中id部分应当为'?'"; } if (strError != "") { EntityInfo error = new EntityInfo(info); error.ErrorInfo = strError; error.ErrorCode = ErrorCodeValue.CommonError; ErrorInfos.Add(error); continue; } } // 构造出适合保存的新事项记录 string strNewXml = ""; nRet = BuildNewItemRecord( sessioninfo, bForce, strBiblioRecId, info.NewRecord, out strNewXml, out strError); if (nRet == -1) { EntityInfo error = new EntityInfo(info); error.ErrorInfo = strError; error.ErrorCode = ErrorCodeValue.CommonError; ErrorInfos.Add(error); continue; } { XmlDocument domNew = new XmlDocument(); try { domNew.LoadXml(strNewXml); } catch (Exception ex) { EntityInfo error = new EntityInfo(info); error.ErrorInfo = "将拟创建的XML记录装入DOM时出错:" + ex.Message; error.ErrorCode = ErrorCodeValue.CommonError; ErrorInfos.Add(error); continue; } // 2011/4/11 nRet = CanCreate( sessioninfo, domNew, out strError); if (nRet == -1) { EntityInfo error = new EntityInfo(info); error.ErrorInfo = strError; error.ErrorCode = ErrorCodeValue.CommonError; ErrorInfos.Add(error); continue; } if (nRet == 0) { EntityInfo error = new EntityInfo(info); error.ErrorInfo = strError; error.ErrorCode = ErrorCodeValue.AccessDenied; ErrorInfos.Add(error); continue; } } // 2010/4/8 XmlDocument temp = new XmlDocument(); temp.LoadXml(strNewXml); nRet = this.App.SetOperation( ref temp, "create", sessioninfo.UserID, "", out strError); if (nRet == -1) { EntityInfo error = new EntityInfo(info); error.ErrorInfo = strError; error.ErrorCode = ErrorCodeValue.CommonError; ErrorInfos.Add(error); continue; } strNewXml = temp.DocumentElement.OuterXml; lRet = channel.DoSaveTextRes(info.NewRecPath, strNewXml, false, // include preamble? "content", info.OldTimestamp, out output_timestamp, out strOutputPath, out strError); if (lRet == -1) { EntityInfo error = new EntityInfo(info); error.NewTimestamp = output_timestamp; error.ErrorInfo = "保存新记录的操作发生错误:" + strError; error.ErrorCode = channel.OriginErrorCode; ErrorInfos.Add(error); domOperLog = null; // 表示不必写入日志 } else // 成功 { DomUtil.SetElementText(domOperLog.DocumentElement, "action", "new"); // 不创建<oldRecord>元素 // 创建<record>元素 XmlNode node = DomUtil.SetElementText(domOperLog.DocumentElement, "record", strNewXml); DomUtil.SetAttr(node, "recPath", strOutputPath); // 新记录保存成功,需要返回信息元素。因为需要返回新的时间戳和实际保存的记录路径 EntityInfo error = new EntityInfo(info); error.NewRecPath = strOutputPath; error.NewRecord = strNewXml; // 所真正保存的记录,可能稍有变化, 因此需要返回给前端 error.NewTimestamp = output_timestamp; error.ErrorInfo = "保存新记录的操作成功。NewTimeStamp中返回了新的时间戳, RecPath中返回了实际存入的记录路径。"; error.ErrorCode = ErrorCodeValue.NoError; ErrorInfos.Add(error); } } else if (info.Action == "change") { // 执行SetIssues API中的"change"操作 nRet = DoOperChange( bForce, sessioninfo, channel, info, ref domOperLog, ref ErrorInfos); if (nRet == -1) { // 失败 domOperLog = null; // 表示不必写入日志 } } else if (info.Action == "move") { // 执行SetIssues API中的"move"操作 nRet = DoOperMove( sessioninfo, channel, info, ref domOperLog, ref ErrorInfos); if (nRet == -1) { // 失败 domOperLog = null; // 表示不必写入日志 } } else if (info.Action == "delete") { /* string strParentID = strNewParentID; if (String.IsNullOrEmpty(strParentID) == true) strParentID = strOldParentID; * */ // TODO: 对于期记录,oldLocateParm中应当包含parentid,预先检查好 // 删除期记录的操作 nRet = DoOperDelete( sessioninfo, bForce, channel, info, oldLocateParam, domOldRec, ref domOperLog, ref ErrorInfos); if (nRet == -1) { // 失败 domOperLog = null; // 表示不必写入日志 } } else { // 不支持的命令 EntityInfo error = new EntityInfo(info); error.ErrorInfo = "不支持的操作命令 '" + info.Action + "'"; error.ErrorCode = ErrorCodeValue.CommonError; ErrorInfos.Add(error); } // 写入日志 if (domOperLog != null && bNoEventLog == false) // 2008/10/19 { string strOperTime = this.App.Clock.GetClock(); DomUtil.SetElementText(domOperLog.DocumentElement, "operator", sessioninfo.UserID); // 操作者 DomUtil.SetElementText(domOperLog.DocumentElement, "operTime", strOperTime); // 操作时间 nRet = this.App.OperLog.WriteOperLog(domOperLog, sessioninfo.ClientAddress, out strError); if (nRet == -1) { strError = this.SetApiName + "() API 写入日志时发生错误: " + strError; goto ERROR1; } } } finally { if (bLocked == true) this.UnlockItem(lockLocateParam); } } // 复制到结果中 errorinfos = new EntityInfo[ErrorInfos.Count]; for (int i = 0; i < ErrorInfos.Count; i++) { errorinfos[i] = ErrorInfos[i]; } result.Value = ErrorInfos.Count; // 返回信息的数量 return result; ERROR1: // 这里的报错,是比较严重的错误。如果是数组中部分的请求发生的错误,则不在这里报错,而是通过返回错误信息数组的方式来表现 result.Value = -1; result.ErrorInfo = strError; result.ErrorCode = ErrorCode.SystemError; return result; }
// 对事项查重 public LibraryServerResult SearchItemDup( // RmsChannelCollection Channels, RmsChannel channel, List<string> locateParam, /* string strPublishTime, string strBiblioRecPath, * */ int nMax, out string[] paths) { paths = null; LibraryServerResult result = new LibraryServerResult(); int nRet = 0; string strError = ""; List<string> aPath = null; nRet = this.SearchItemRecDup( // Channels, channel, locateParam, nMax, out aPath, out strError); if (nRet == -1) goto ERROR1; if (nRet == 0) { paths = new string[0]; result.Value = 0; result.ErrorInfo = "没有找到"; result.ErrorCode = ErrorCode.NotFound; return result; } // 复制到结果中 paths = new string[aPath.Count]; for (int i = 0; i < aPath.Count; i++) { paths[i] = aPath[i]; } result.Value = paths.Length; return result; ERROR1: result.Value = -1; result.ErrorInfo = strError; result.ErrorCode = ErrorCode.SystemError; return result; }
// 复制或者移动编目记录 // parameters: // strAction 动作。为"onlycopybiblio" "onlymovebiblio" "copy" "move" 之一 // strBiblioType 目前只允许xml一种 // strBiblio 源书目记录。目前需要用null调用 // baTimestamp 源记录的时间戳 // strNewBiblio 需要在目标记录中更新的内容。如果 == null,表示不特意更新 // strMergeStyle 如何合并两条书目记录的元数据部分? reserve_source / reserve_target / missing_source_subrecord / overwrite_target_subrecord。 空表示 reserve_source + combine_subrecord // reserve_source 表示采用源书目记录; reserve_target 表示采用目标书目记录 // missing_source_subrecord 表示丢失来自源的下级记录(保留目标原本的下级记录); overwrite_target_subrecord 表示采纳来自源的下级记录,删除目标记录原本的下级记录(注:此功能暂时没有实现); combine_subrecord 表示组合来源和目标的下级记录 // strOutputBiblioRecPath 输出的书目记录路径。当strBiblioRecPath中末级为问号,表示追加保存书目记录的时候,本参数返回实际保存的书目记录路径 // baOutputTimestamp 操作完成后,新的时间戳 // result.Value: // -1 出错 // 0 成功,没有警告信息。 // 1 成功,有警告信息。警告信息在 result.ErrorInfo 中 public LibraryServerResult CopyBiblioInfo( SessionInfo sessioninfo, string strAction, string strBiblioRecPath, string strBiblioType, string strBiblio, byte[] baTimestamp, string strNewBiblioRecPath, string strNewBiblio, string strMergeStyle, out string strOutputBiblio, out string strOutputBiblioRecPath, out byte[] baOutputTimestamp) { string strError = ""; long lRet = 0; int nRet = 0; strOutputBiblioRecPath = ""; baOutputTimestamp = null; strOutputBiblio = ""; LibraryServerResult result = new LibraryServerResult(); if (StringUtil.IsInList("overwrite_target_subrecord", strMergeStyle) == true) { strError = "strMergeStyle 中的 overwrite_target_subrecord 尚未实现"; goto ERROR1; } bool bChangePartDenied = false; // 修改操作部分被拒绝 string strDeniedComment = ""; // 关于部分字段被拒绝的注释 string strLibraryCodeList = sessioninfo.LibraryCodeList; // 检查参数 if (strAction != null) strAction = strAction.ToLower(); if (strAction != "onlymovebiblio" && strAction != "onlycopybiblio" && strAction != "copy" && strAction != "move") { strError = "strAction参数值应当为onlymovebiblio/onlycopybiblio/move/copy之一"; goto ERROR1; } strBiblioType = strBiblioType.ToLower(); if (strBiblioType != "xml") { strError = "strBiblioType必须为\"xml\""; goto ERROR1; } { if (this.TestMode == true || sessioninfo.TestMode == true) { // 检查评估模式 // return: // -1 检查过程出错 // 0 可以通过 // 1 不允许通过 nRet = CheckTestModePath(strBiblioRecPath, out strError); if (nRet != 0) { strError = "复制/移动书目记录的操作被拒绝: " + strError; goto ERROR1; } } } string strUnionCatalogStyle = ""; string strAccessParameters = ""; bool bRightVerified = false; // TODO: 也需要检查 strNewBiblioRecPath // 检查数据库路径,看看是不是已经正规定义的编目库? if (String.IsNullOrEmpty(strBiblioRecPath) == false) { string strBiblioDbName = ResPath.GetDbName(strBiblioRecPath); if (this.IsBiblioDbName(strBiblioDbName) == false) { strError = "书目记录路径 '" + strBiblioRecPath + "' 中包含的数据库名 '" + strBiblioDbName + "' 不是合法的书目库名"; goto ERROR1; } #if NO if (this.TestMode == true) { string strID = ResPath.GetRecordId(strBiblioRecPath); if (StringUtil.IsPureNumber(strID) == true) { long v = 0; long.TryParse(strID, out v); if (v > 1000) { strError = "dp2Library XE 评估模式下只能修改 ID 小于等于 1000 的书目记录"; goto ERROR1; } } } #endif ItemDbCfg cfg = null; cfg = GetBiblioDbCfg(strBiblioDbName); Debug.Assert(cfg != null, ""); strUnionCatalogStyle = cfg.UnionCatalogStyle; // 检查存取权限 if (String.IsNullOrEmpty(sessioninfo.Access) == false) { // return: // null 指定的操作类型的权限没有定义 // "" 定义了指定类型的操作权限,但是否定的定义 // 其它 权限列表。* 表示通配的权限列表 string strActionList = GetDbOperRights(sessioninfo.Access, strBiblioDbName, "setbiblioinfo"); if (strActionList == null) { // 看看是不是关于 setbiblioinfo 的任何权限都没有定义? strActionList = GetDbOperRights(sessioninfo.Access, "", "setbiblioinfo"); if (strActionList == null) { // 2014/3/12 // TODO: 可以提示"既没有... 也没有 ..." goto CHECK_RIGHTS_2; } else { strError = "当前用户 '" + sessioninfo.UserID + "' 不具备 针对数据库 '" + strBiblioDbName + "' 执行 setbiblioinfo " + strAction + " 操作的存取权限"; result.Value = -1; result.ErrorInfo = strError; result.ErrorCode = ErrorCode.AccessDenied; return result; } #if NO strError = "当前用户 '" + sessioninfo.UserID + "' 不具备 针对数据库 '" + strBiblioDbName + "' 执行 setbiblioinfo " + strAction + " 操作的存取权限"; result.Value = -1; result.ErrorInfo = strError; result.ErrorCode = ErrorCode.AccessDenied; return result; #endif } if (strActionList == "*") { // 通配 } else { if (IsInAccessList(strAction, strActionList, out strAccessParameters) == false) { strError = "当前用户 '" + sessioninfo.UserID + "' 不具备 针对数据库 '" + strBiblioDbName + "' 执行 setbiblioinfo " + strAction + " 操作的存取权限"; result.Value = -1; result.ErrorInfo = strError; result.ErrorCode = ErrorCode.AccessDenied; return result; } } bRightVerified = true; } } CHECK_RIGHTS_2: if (bRightVerified == false) { // 权限字符串 if (StringUtil.IsInList("setbiblioinfo", sessioninfo.RightsOrigin) == false && StringUtil.IsInList("order", sessioninfo.RightsOrigin) == false) { result.Value = -1; result.ErrorInfo = "设置书目信息被拒绝。不具备order或setbiblioinfo权限。"; result.ErrorCode = ErrorCode.AccessDenied; return result; } } // TODO: 需要额外的检查,看看所保存的数据MARC格式是不是这个数据库要求的格式? RmsChannel channel = sessioninfo.Channels.GetChannel(this.WsUrl); if (channel == null) { strError = "channel == null"; goto ERROR1; } // 准备日志DOM XmlDocument domOperLog = new XmlDocument(); domOperLog.LoadXml("<root />"); // 操作不涉及到读者库,所以没有<libraryCode>元素 DomUtil.SetElementText(domOperLog.DocumentElement, "operation", "setBiblioInfo"); DomUtil.SetElementText(domOperLog.DocumentElement, "action", strAction); string strOperTime = this.Clock.GetClock(); string strExistingSourceXml = ""; byte[] exist_source_timestamp = null; if (strAction == "onlymovebiblio" || strAction == "onlycopybiblio" || strAction == "copy" || strAction == "move") { string strMetaData = ""; string strOutputPath = ""; // 先读出数据库中此位置的已有记录 lRet = channel.GetRes(strBiblioRecPath, out strExistingSourceXml, out strMetaData, out exist_source_timestamp, out strOutputPath, out strError); if (lRet == -1) { if (channel.ErrorCode == ChannelErrorCode.NotFound) { goto SKIP_MEMO_OLDRECORD; } else { strError = "设置书目信息发生错误, 在读入原有记录阶段:" + strError; goto ERROR1; } } if (strBiblioRecPath != strOutputPath) { strError = "根据路径 '" + strBiblioRecPath + "' 读入原有记录时,发现返回的路径 '" + strOutputPath + "' 和前者不一致"; goto ERROR1; } XmlNode node = DomUtil.SetElementText(domOperLog.DocumentElement, "oldRecord", strExistingSourceXml); DomUtil.SetAttr(node, "recPath", strBiblioRecPath); // TODO: 如果已存在的XML记录中,MARC根不是文档根,那么表明书目记录 // 还存储有其他信息,这时就需要把前端送来的XML记录和已存在的记录进行合并处理, // 防止贸然覆盖了文档根下的有用信息。 } SKIP_MEMO_OLDRECORD: // bool bBiblioNotFound = false; string strRights = ""; if (sessioninfo.Account != null) strRights = sessioninfo.Account.Rights; if (strAction == "onlycopybiblio" || strAction == "onlymovebiblio" || strAction == "copy" || strAction == "move") { if (string.IsNullOrEmpty(strNewBiblio) == false) { // 观察时间戳是否发生变化 nRet = ByteArray.Compare(baTimestamp, exist_source_timestamp); if (nRet != 0) { strError = "移动或复制操作发生错误,源记录已经发生了修改(时间戳不匹配。当前提交的时间戳: '" + ByteArray.GetHexTimeStampString(baTimestamp) + "', 数库库中原记录的时间戳: '" + ByteArray.GetHexTimeStampString(exist_source_timestamp) + "')"; goto ERROR1; } } // TODO: 如果目标书目记录路径已知,则需要对两个路径都加锁。注意从小号到大号顺次加锁,避免死锁 this.BiblioLocks.LockForWrite(strBiblioRecPath); try { if (String.IsNullOrEmpty(strNewBiblio) == false) { // 合并联合编目的新旧书目库XML记录 // 功能:排除新记录中对strLibraryCode定义以外的905字段的修改 // parameters: // bChangePartDenied 如果本次被设定为 true,则 strError 中返回了关于部分修改的注释信息 // return: // -1 error // 0 not delete any fields // 1 deleted some fields nRet = MergeOldNewBiblioRec( strRights, strUnionCatalogStyle, strLibraryCodeList, "insert,replace,delete", strAccessParameters, strExistingSourceXml, ref strNewBiblio, ref bChangePartDenied, out strError); if (nRet == -1) goto ERROR1; if (bChangePartDenied == true && string.IsNullOrEmpty(strError) == false) strDeniedComment += " " + strError; /* // 2011/11/30 nRet = this.SetOperation( ref strNewBiblio, strAction, sessioninfo.UserID, "source: " + strBiblioRecPath, out strError); if (nRet == -1) goto ERROR1; * */ } nRet = DoBiblioOperMove( strAction, sessioninfo, channel, strBiblioRecPath, strExistingSourceXml, strNewBiblioRecPath, strNewBiblio, // 已经经过Merge预处理的新记录XML strMergeStyle, out strOutputBiblio, out baOutputTimestamp, out strOutputBiblioRecPath, out strError); if (nRet == -1) goto ERROR1; if ((strAction == "copy" || strAction == "move") && StringUtil.IsInList("missing_source_subrecord", strMergeStyle) == false) { string strWarning = ""; // // 调用前,假定书目记录已经被锁定 // parameters: // strAction copy / move // return: // -2 权限不够 // -1 出错 // 0 成功 nRet = DoCopySubRecord( sessioninfo, strAction, strBiblioRecPath, strOutputBiblioRecPath, domOperLog, out strWarning, out strError); if (nRet == -1) { // Undo Copy biblio record // 移动回去 if (strAction == "onlymovebiblio" || strAction == "move") { byte[] output_timestamp = null; string strTempOutputRecPath = ""; string strError_1 = ""; lRet = channel.DoCopyRecord(strOutputBiblioRecPath, strBiblioRecPath, true, // bDeleteSourceRecord out output_timestamp, out strTempOutputRecPath, out strError_1); if (lRet == -1) { this.WriteErrorLog("复制 '" + strBiblioRecPath + "' 下属的册记录时出错: " + strError + ",并且Undo的时候(从 '" + strOutputBiblioRecPath + "' 复制回 '" + strBiblioRecPath + "')失败: " + strError_1); } } else if (strAction == "onlycopybiblio" || strAction == "copy") { // 删除刚刚复制的目标记录 string strError_1 = ""; int nRedoCount = 0; REDO_DELETE: lRet = channel.DoDeleteRes(strOutputBiblioRecPath, baTimestamp, out baOutputTimestamp, out strError_1); if (lRet == -1 && channel.ErrorCode != ChannelErrorCode.NotFound) { if (channel.ErrorCode == ChannelErrorCode.TimestampMismatch && nRedoCount < 10) { baTimestamp = baOutputTimestamp; nRedoCount++; goto REDO_DELETE; } this.WriteErrorLog("复制 '" + strBiblioRecPath + "' 下属的册记录时出错: " + strError + ",并且Undo的时候(删除记录 '" + strOutputBiblioRecPath + "')失败: " + strError_1); } } goto ERROR1; } result.ErrorInfo = strWarning; } } finally { this.BiblioLocks.UnlockForWrite(strBiblioRecPath); } } else { strError = "未知的strAction参数值 '" + strAction + "'"; goto ERROR1; } { // 注:如果strNewBiblio为空,则表明仅仅进行了复制,并没有在目标记录写什么新内容 // 如果在日志记录中要查到到底复制了什么内容,可以看<oldRecord>元素的文本内容 // 注: 如果 strMergeStyle 为 reserve_target, 需要记载一下这个位置已经存在的记录 XmlNode node = DomUtil.SetElementText(domOperLog.DocumentElement, "record", string.IsNullOrEmpty(strOutputBiblio) == false ? strOutputBiblio : strNewBiblio); DomUtil.SetAttr(node, "recPath", strOutputBiblioRecPath); } // 2015/1/21 DomUtil.SetElementText(domOperLog.DocumentElement, "mergeStyle", strMergeStyle); DomUtil.SetElementText(domOperLog.DocumentElement, "operator", sessioninfo.UserID); DomUtil.SetElementText(domOperLog.DocumentElement, "operTime", strOperTime); // 写入日志 nRet = this.OperLog.WriteOperLog(domOperLog, sessioninfo.ClientAddress, out strError); if (nRet == -1) { strError = "CopyBiblioInfo() API 写入日志时发生错误: " + strError; goto ERROR1; } if (string.IsNullOrEmpty(result.ErrorInfo) == true) result.Value = 0; // 没有警告 else result.Value = 1; // 有警告 // 2013/3/5 if (bChangePartDenied == true) { result.ErrorCode = ErrorCode.PartialDenied; if (string.IsNullOrEmpty(strDeniedComment) == false) { if (string.IsNullOrEmpty(result.ErrorInfo) == false) result.ErrorInfo += " ; "; result.ErrorInfo += strDeniedComment; } } return result; ERROR1: result.Value = -1; result.ErrorInfo = strError; result.ErrorCode = ErrorCode.SystemError; return result; }
// 获得实用库信息 public LibraryServerResult GetUtilInfo( SessionInfo sessioninfo, string strAction, string strDbName, string strFrom, string strKey, string strValueAttrName, out string strValue) { string strError = ""; strValue = ""; int nRet = 0; LibraryServerResult result = new LibraryServerResult(); /* if (String.IsNullOrEmpty(strKeyAttrName) == true) strKeyAttrName = "k"; * */ if (String.IsNullOrEmpty(strValueAttrName) == true) strValueAttrName = "v"; RmsChannel channel = sessioninfo.Channels.GetChannel(this.WsUrl); if (channel == null) { strError = "get channel error"; goto ERROR1; } string strPath = ""; string strXml = ""; byte[] timestamp = null; // 检索实用库记录的路径和记录体 // return: // -1 error(注:检索命中多条情况被当作错误返回) // 0 not found // 1 found nRet = SearchUtilPathAndRecord( // sessioninfo.Channels, channel, strDbName, strKey, strFrom, out strPath, out strXml, out timestamp, out strError); if (nRet == -1) goto ERROR1; if (nRet == 0) { result.ErrorCode = ErrorCode.NotFound; result.ErrorInfo = "库名为 '"+strDbName+"' 途径为 '"+strFrom+"' 键值为 '" + strKey + "' 的记录没有找到"; result.Value = 0; return result; } // 如果动作为获得整个记录 if (strAction == "getrecord") { strValue = strXml; result.Value = 1; return result; } XmlDocument domRecord = new XmlDocument(); try { domRecord.LoadXml(strXml); } catch (Exception ex) { strError = "装载路径为'" + strPath + "'的xml记录时出错: " + ex.Message; goto ERROR1; } strValue = DomUtil.GetAttr(domRecord.DocumentElement, strValueAttrName); result.Value = 1; return result; ERROR1: result.Value = -1; result.ErrorCode = ErrorCode.SystemError; result.ErrorInfo = strError; return result; }
// 修改读者密码 // Result.Value -1出错 0旧密码不正确 1旧密码正确,已修改为新密码 // 权限: // 工作人员或者读者,必须有changereaderpassword权限 // 如果为读者, 附加限制还只能修改属于自己的密码 public LibraryServerResult ChangeReaderPassword( SessionInfo sessioninfo, string strReaderBarcode, string strReaderOldPassword, string strReaderNewPassword) { LibraryServerResult result = new LibraryServerResult(); // 权限判断 // 权限字符串 if (StringUtil.IsInList("changereaderpassword", sessioninfo.RightsOrigin) == false) { result.Value = -1; result.ErrorInfo = "修改读者密码被拒绝。不具备 changereaderpassword 权限。"; result.ErrorCode = ErrorCode.AccessDenied; return result; } // 对读者身份的附加判断 if (sessioninfo.UserType == "reader") { if (strReaderBarcode != sessioninfo.Account.Barcode) { result.Value = -1; result.ErrorInfo = "修改读者密码被拒绝。作为读者只能修改自己的密码"; result.ErrorCode = ErrorCode.AccessDenied; return result; } } string strError = ""; // 加读者记录锁 #if DEBUG_LOCK_READER this.WriteErrorLog("ChangeReaderPassword 开始为读者加写锁 '" + strReaderBarcode + "'"); #endif this.ReaderLocks.LockForWrite(strReaderBarcode); try { RmsChannel channel = sessioninfo.Channels.GetChannel(this.WsUrl); if (channel == null) { strError = "get channel error"; goto ERROR1; } string strXml = ""; string strOutputPath = ""; byte[] timestamp = null; // 获得读者记录 // return: // -1 error // 0 not found // 1 命中1条 // >1 命中多于1条 int nRet = this.GetReaderRecXml( // sessioninfo.Channels, channel, strReaderBarcode, out strXml, out strOutputPath, out timestamp, out strError); if (nRet == 0) { strError = "证条码号为 '" + strReaderBarcode + "' 的读者不存在"; goto ERROR1; } if (nRet == -1) { strError = "获得证条码号为 '" + strReaderBarcode + "' 的读者记录时出错: " + strError; goto ERROR1; } if (nRet > 1) { strError = "系统错误: 证条码号为 '" + strReaderBarcode + "' 的读者记录多于一个"; goto ERROR1; } string strLibraryCode = ""; // 看看读者记录所从属的读者库的馆代码,是否被当前用户管辖 if (String.IsNullOrEmpty(strOutputPath) == false) { // 检查当前操作者是否管辖这个读者库 // 观察一个读者记录路径,看看是不是在当前用户管辖的读者库范围内? if (this.IsCurrentChangeableReaderPath(strOutputPath, sessioninfo.LibraryCodeList, out strLibraryCode) == false) { strError = "读者记录路径 '" + strOutputPath + "' 从属的读者库不在当前用户管辖范围内"; goto ERROR1; } } XmlDocument readerdom = null; nRet = LibraryApplication.LoadToDom(strXml, out readerdom, out strError); if (nRet == -1) { strError = "装载读者记录进入XML DOM时发生错误: " + strError; goto ERROR1; } string strExistingBarcode = DomUtil.GetElementText(readerdom.DocumentElement, "barcode"); // 如果是读者身份, 或者通过参数 strReaderOldPassword (非null)要求,需要验证旧密码 if (sessioninfo.UserType == "reader" || strReaderOldPassword != null) { // 验证读者密码 // return: // -1 error // 0 密码不正确 // 1 密码正确 nRet = LibraryApplication.VerifyReaderPassword( sessioninfo.ClientIP, readerdom, strReaderOldPassword, this.Clock.Now, out strError); if (nRet == -1) goto ERROR1; if (nRet == 0) { result.Value = 0; result.ErrorInfo = "旧密码不正确。"; return result; } else { result.Value = 1; } } byte[] output_timestamp = null; nRet = ChangeReaderPassword( sessioninfo, strOutputPath, ref readerdom, strReaderNewPassword, timestamp, out output_timestamp, out strError); if (nRet == -1) goto ERROR1; // 清除 LoginCache this.ClearLoginCache(strExistingBarcode); result.Value = 1; // 成功 } finally { this.ReaderLocks.UnlockForWrite(strReaderBarcode); #if DEBUG_LOCK_READER this.WriteErrorLog("ChangeReaderPassword 结束为读者加写锁 '" + strReaderBarcode + "'"); #endif } return result; ERROR1: result.Value = -1; result.ErrorInfo = strError; result.ErrorCode = ErrorCode.SystemError; return result; }
public LibraryServerResult Clone() { LibraryServerResult other = new LibraryServerResult(); other.Value = this.Value; other.ErrorCode = this.ErrorCode; other.ErrorInfo = this.ErrorInfo; return other; }
// API: 借书 // text-level: 用户提示 OPAC续借功能要调用此函数 // parameters: // strReaderBarcode 读者证条码号。续借的时候可以为空 // strItemBarcode 册条码号 // strConfirmItemRecPath 册记录路径。在册条码号重复的情况下,才需要使用这个参数,平时为null即可 // saBorrowedItemBarcode 同一读者先前已经借阅成功的册条码号集合。用于在返回的读者html中显示出特定的颜色而已。 // strStyle 操作风格。"item"表示将返回册记录;"reader"表示将返回读者记录 // strItemFormat 规定strItemRecord参数所返回的数据格式 // strItemRecord 返回册记录 // strReaderFormat 规定strReaderRecord参数所返回的数据格式 // strReaderRecord 返回读者记录 // aDupPath 如果发生条码号重复,这里返回了相关册记录的路径 // return_time 本次借阅要求的最后还书时间。GMT时间。 // 权限:无论工作人员还是读者,首先应具备borrow或renew权限。 // 对于读者,还需要他进行的借阅(续借)操作是针对自己的,即strReaderBarcode必须和账户信息中的证条码号一致。 // 也就是说,读者不允许替他人借阅(续借)图书,这样规定是为了防止读者捣乱。 public LibraryServerResult Borrow( SessionInfo sessioninfo, bool bRenew, string strReaderBarcode, string strItemBarcode, string strConfirmItemRecPath, bool bForce, string[] saBorrowedItemBarcode, string strStyle, string strItemFormatList, out string[] item_records, string strReaderFormatList, out string[] reader_records, string strBiblioFormatList, // 2008/5/9 out string[] biblio_records, // 2008/5/9 out string[] aDupPath, out string strOutputReaderBarcodeParam, // 2011/9/25 out BorrowInfo borrow_info // 2007/12/6 ) { item_records = null; reader_records = null; biblio_records = null; aDupPath = null; strOutputReaderBarcodeParam = ""; borrow_info = new BorrowInfo(); List<string> time_lines = new List<string>(); DateTime start_time = DateTime.Now; string strOperLogUID = ""; LibraryServerResult result = new LibraryServerResult(); string strAction = "borrow"; if (bRenew == true) strAction = "renew"; string strActionName = GetBorrowActionName(strAction); // 个人书斋名 string strPersonalLibrary = ""; if (sessioninfo.UserType == "reader" && sessioninfo.Account != null) strPersonalLibrary = sessioninfo.Account.PersonalLibrary; // 权限判断 if (bRenew == false) { // 权限字符串 if (StringUtil.IsInList("borrow", sessioninfo.RightsOrigin) == false) { result.Value = -1; result.ErrorInfo = "借阅操作被拒绝。不具备 borrow 权限。"; result.ErrorCode = ErrorCode.AccessDenied; // return result; } // 对读者身份的附加判断 // 注:具有个人书斋的,还可以继续向后执行 if (sessioninfo.UserType == "reader" && sessioninfo.Account != null && strReaderBarcode != sessioninfo.Account.Barcode && string.IsNullOrEmpty(strPersonalLibrary) == true) { result.Value = -1; result.ErrorInfo = "借阅操作被拒绝。作为读者不能代他人进行借阅操作。"; result.ErrorCode = ErrorCode.AccessDenied; return result; } } else { // 权限字符串 if (StringUtil.IsInList("renew", sessioninfo.RightsOrigin) == false) { result.Value = -1; // text-level: 用户提示 result.ErrorInfo = this.GetString("续借操作被拒绝。不具备 renew 权限。"); // "续借操作被拒绝。不具备renew权限。" result.ErrorCode = ErrorCode.AccessDenied; // return result; } #if NO // 延迟到后面判断。因为 strReaderBarcode 可能是 PQR:xxxx 形态 // 对读者身份的附加判断 // 注:具有个人书斋的,还可以继续向后执行 if (sessioninfo.UserType == "reader" && sessioninfo.Account != null && strReaderBarcode != sessioninfo.Account.Barcode && string.IsNullOrEmpty(strPersonalLibrary) == true) { result.Value = -1; // text-level: 用户提示 // result.ErrorInfo = this.GetString("续借操作被拒绝。作为读者不能代他人进行续借操作。"); // "续借操作被拒绝。作为读者不能代他人进行续借操作。" result.ErrorInfo = string.Format("续借操作被拒绝。作为读者 '{0}' 不能代他人 '{1}' 进行续借操作。", sessioninfo.Account.Barcode, strReaderBarcode); result.ErrorCode = ErrorCode.AccessDenied; return result; } #endif } // 如果没有普通的权限,需要预检查存取权限 LibraryServerResult result_save = null; if (result.Value == -1 && String.IsNullOrEmpty(sessioninfo.Access) == false) { string strAccessActionList = GetDbOperRights(sessioninfo.Access, "", // 此时还不知道实体库名,先取得当前帐户关于任意一个实体库的存取定义 "circulation"); if (string.IsNullOrEmpty(strAccessActionList) == true) return result; // 通过了这样一番检查后,后面依然要检查存取权限。 // 如果后面检查中,精确针对某个实体库的存取权限存在,则依存取权限;如果不存在,则依普通权限 result_save = result.Clone(); } else if (result.Value == -1) return result; // 延迟报错 2014/9/16 result = new LibraryServerResult(); string strError = ""; if (bForce == true) { strError = "bForce参数不能为true"; goto ERROR1; } int nRet = 0; long lRet = 0; string strIdcardNumber = ""; // 身份证号 string strQrCode = ""; // if (string.IsNullOrEmpty(strReaderBarcode) == false) { string strOutputCode = ""; // 把二维码字符串转换为读者证条码号 // parameters: // strReaderBcode [out]读者证条码号 // return: // -1 出错 // 0 所给出的字符串不是读者证号二维码 // 1 成功 nRet = DecodeQrCode(strReaderBarcode, out strOutputCode, out strError); if (nRet == -1) goto ERROR1; if (nRet == 1) { strQrCode = strReaderBarcode; strReaderBarcode = strOutputCode; } } // 因为 strReaderBarcode 可能是 PQR:xxxx 形态,需要替换为真正的证条码号以后才能参与判断,所以放到这里判断 if (bRenew == true) { // 对读者身份的附加判断 // 注:具有个人书斋的,还可以继续向后执行 if (sessioninfo.UserType == "reader" && sessioninfo.Account != null && strReaderBarcode != sessioninfo.Account.Barcode && string.IsNullOrEmpty(strPersonalLibrary) == true) { result.Value = -1; // text-level: 用户提示 // result.ErrorInfo = this.GetString("续借操作被拒绝。作为读者不能代他人进行续借操作。"); // "续借操作被拒绝。作为读者不能代他人进行续借操作。" result.ErrorInfo = string.Format("续借操作被拒绝。作为读者 '{0}' 不能代他人 '{1}' 进行续借操作。", sessioninfo.Account.Barcode, strReaderBarcode); // TODO: 测试验证完成后需要改回用上一句 result.ErrorCode = ErrorCode.AccessDenied; return result; } } int nRedoCount = 0; // 因为时间戳冲突, 重试的次数 string strLockReaderBarcode = strReaderBarcode; // 加锁专用字符串,不怕后面被修改了 REDO_BORROW: bool bReaderLocked = false; string strOutputReaderXml = ""; string strOutputItemXml = ""; string strBiblioRecID = ""; string strOutputItemRecPath = ""; string strOutputReaderRecPath = ""; string strLibraryCode = ""; // 加读者记录锁 // this.ReaderLocks.LockForWrite(strLockReaderBarcode); if (String.IsNullOrEmpty(strReaderBarcode) == false) { // 加读者记录锁 strLockReaderBarcode = strReaderBarcode; #if DEBUG_LOCK_READER this.WriteErrorLog("Borrow 开始为读者加写锁 '" + strReaderBarcode + "'"); #endif this.ReaderLocks.LockForWrite(strReaderBarcode); bReaderLocked = true; strOutputReaderBarcodeParam = strReaderBarcode; } try // 读者记录锁定范围开始 { // 读取读者记录 XmlDocument readerdom = null; byte[] reader_timestamp = null; string strOldReaderXml = ""; if (string.IsNullOrEmpty(strReaderBarcode) == false) { bool bReaderDbInCirculation = true; LibraryServerResult result1 = GetReaderRecord( sessioninfo, strActionName, time_lines, true, ref strReaderBarcode, ref strIdcardNumber, ref strLibraryCode, out bReaderDbInCirculation, out readerdom, out strOutputReaderRecPath, out reader_timestamp); if (result1.Value == 0) { } else { return result1; } if (bReaderDbInCirculation == false) { // text-level: 用户提示 strError = string.Format(this.GetString("借书操作被拒绝。读者证条码号s所在的读者记录s因其数据库s属于未参与流通的读者库"), // "借书操作被拒绝。读者证条码号 '{0}' 所在的读者记录 '{1}' 因其数据库 '{2}' 属于未参与流通的读者库" strReaderBarcode, strOutputReaderRecPath, StringUtil.GetDbName(strOutputReaderRecPath)); // "借书操作被拒绝。读者证条码号 '" + strReaderBarcode + "' 所在的读者记录 '" +strOutputReaderRecPath + "' 因其数据库 '" +strReaderDbName+ "' 属于未参与流通的读者库"; goto ERROR1; } // 记忆修改前的读者记录 strOldReaderXml = readerdom.OuterXml; if (String.IsNullOrEmpty(strIdcardNumber) == false || string.IsNullOrEmpty(strReaderBarcode) == true /* 2013/5/23 */) { // 获得读者证条码号 strReaderBarcode = DomUtil.GetElementText(readerdom.DocumentElement, "barcode"); } strOutputReaderBarcodeParam = DomUtil.GetElementText(readerdom.DocumentElement, "barcode"); string strReaderDbName = ResPath.GetDbName(strOutputReaderRecPath); // 检查当前用户管辖的读者范围 // return: // -1 出错 // 0 允许继续访问 // 1 权限限制,不允许继续访问。strError 中有说明原因的文字 nRet = CheckReaderRange(sessioninfo, readerdom, strReaderDbName, out strError); if (nRet == -1) goto ERROR1; if (nRet == 1) { // strError = "当前用户 '" + sessioninfo.UserID + "' 的存取权限或好友关系禁止操作读者(证条码号为 " + strReaderBarcode + ")。具体原因:" + strError; result.Value = -1; result.ErrorInfo = strError; result.ErrorCode = ErrorCode.AccessDenied; return result; } } byte[] item_timestamp = null; List<string> aPath = null; string strItemXml = ""; // string strOutputItemRecPath = ""; RmsChannel channel = sessioninfo.Channels.GetChannel(this.WsUrl); if (channel == null) { // text-level: 内部错误 strError = "get channel error"; goto ERROR1; } // 加册记录锁 this.EntityLocks.LockForWrite(strItemBarcode); try // 册记录锁定范围开始 { // 读入册记录 DateTime start_time_read_item = DateTime.Now; // 如果已经有确定的册记录路径 if (String.IsNullOrEmpty(strConfirmItemRecPath) == false) { // 检查路径中的库名,是不是实体库名 // return: // -1 error // 0 不是实体库名 // 1 是实体库名 nRet = this.CheckItemRecPath(strConfirmItemRecPath, out strError); if (nRet == -1) goto ERROR1; if (nRet == 0) { strError = strConfirmItemRecPath + strError; goto ERROR1; } string strMetaData = ""; lRet = channel.GetRes(strConfirmItemRecPath, out strItemXml, out strMetaData, out item_timestamp, out strOutputItemRecPath, out strError); if (lRet == -1) { // text-level: 内部错误 strError = "根据strConfirmItemRecPath '" + strConfirmItemRecPath + "' 获得册记录失败: " + strError; goto ERROR1; } } else { // 从册条码号获得册记录 // 获得册记录 // return: // -1 error // 0 not found // 1 命中1条 // >1 命中多于1条 nRet = this.GetItemRecXml( // sessioninfo.Channels, channel, strItemBarcode, "first", // 在若干实体库中顺次检索,命中一个以上则返回,不再继续检索更多 out strItemXml, 100, out aPath, out item_timestamp, out strError); if (nRet == 0) { result.Value = -1; // text-level: 用户提示 result.ErrorInfo = string.Format(this.GetString("册条码号s不存在"), // "册条码号 '{0}' 不存在" strItemBarcode); // "册条码号 '" + strItemBarcode + "' 不存在"; result.ErrorCode = ErrorCode.ItemBarcodeNotFound; return result; } if (nRet == -1) { // text-level: 内部错误 strError = "读入册记录时发生错误: " + strError; goto ERROR1; } if (aPath.Count > 1) { if (this.Statis != null) this.Statis.IncreaseEntryValue( strLibraryCode, "出纳", "借书遇册条码号重复次数", 1); result.Value = -1; // text-level: 用户提示 result.ErrorInfo = string.Format(this.GetString("册条码号为s的册记录有s条,无法进行借阅操作"), // "册条码号为 '{0}' 的册记录有 "{1}" 条,无法进行借阅操作。请在附加册记录路径后重新提交借阅操作。" strItemBarcode, aPath.Count.ToString()); this.WriteErrorLog(result.ErrorInfo); // 2012/12/30 // "册条码号为 '" + 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]; } } } string strItemDbName = ""; // 看看册记录所从属的数据库,是否在参与流通的实体库之列 // 2008/6/4 if (String.IsNullOrEmpty(strOutputItemRecPath) == false) { strItemDbName = ResPath.GetDbName(strOutputItemRecPath); bool bItemDbInCirculation = true; if (this.IsItemDbName(strItemDbName, out bItemDbInCirculation) == false) { // text-level: 内部错误 strError = "册记录路径 '" + strOutputItemRecPath + "' 中的数据库名 '" + strItemDbName + "' 居然不在定义的实体库之列。"; goto ERROR1; } if (bItemDbInCirculation == false) { // text-level: 内部错误 strError = "借书操作被拒绝。册条码号 '" + strItemBarcode + "' 所在的册记录 '" + strOutputItemRecPath + "' 因其数据库 '" + strItemDbName + "' 属于未参与流通的实体库"; goto ERROR1; } } // 检查存取权限 string strAccessParameters = ""; { // 检查存取权限 circulation if (String.IsNullOrEmpty(sessioninfo.Access) == false) { string strAccessActionList = ""; strAccessActionList = GetDbOperRights(sessioninfo.Access, strItemDbName, "circulation"); #if NO if (String.IsNullOrEmpty(strAccessActionList) == true && result_save != null) { // TODO: 也可以直接返回 result_save strError = "当前用户 '" + sessioninfo.UserID + "' 不具备 针对数据库 '" + strItemDbName + "' 执行 出纳 操作的存取权限"; result.Value = -1; result.ErrorInfo = strError; result.ErrorCode = ErrorCode.AccessDenied; return result; } #endif if (strAccessActionList == null) { strAccessActionList = GetDbOperRights(sessioninfo.Access, "", // 此时还不知道实体库名,先取得当前帐户关于任意一个实体库的存取定义 "circulation"); if (strAccessActionList == null) { // 对所有实体库都没有定义任何存取权限,这时候要退而使用普通权限 strAccessActionList = sessioninfo.Rights; // 注:其实此时 result_save == null 即表明普通权限检查已经通过了的 } else { // 对其他实体库定义了存取权限,但对 strItemDbName 没有定义 strError = "用户 '" + sessioninfo.UserID + "' 不具备 针对数据库 '" + strItemDbName + "' 执行 出纳 操作的存取权限"; result.Value = -1; result.ErrorInfo = strError; result.ErrorCode = ErrorCode.AccessDenied; return result; } } if (strAccessActionList == "*") { // 通配 } else { if (IsInAccessList(strAction, strAccessActionList, out strAccessParameters) == false) { strError = "用户 '" + sessioninfo.UserID + "' 不具备 针对数据库 '" + strItemDbName + "' 执行 出纳 " + strActionName + " 操作的存取权限"; result.Value = -1; result.ErrorInfo = strError; result.ErrorCode = ErrorCode.AccessDenied; return result; } } } } XmlDocument itemdom = null; nRet = LibraryApplication.LoadToDom(strItemXml, out itemdom, out strError); if (nRet == -1) { // text-level: 内部错误 strError = "装载册记录进入XML DOM时发生错误: " + strError; goto ERROR1; } WriteTimeUsed( time_lines, start_time_read_item, "Borrow() 中读取册记录 耗时 "); DateTime start_time_process = DateTime.Now; // 检查评估模式下书目记录路径 if (this.TestMode == true || sessioninfo.TestMode == true) { string strBiblioDbName = ""; // 根据实体库名, 找到对应的书目库名 // return: // -1 出错 // 0 没有找到 // 1 找到 nRet = this.GetBiblioDbNameByItemDbName(strItemDbName, out strBiblioDbName, out strError); if (nRet == -1) { strError = "根据实体库名 '" + strItemDbName + "' 获得书目库名时出错: " + strError; goto ERROR1; } string strParentID = DomUtil.GetElementText(itemdom.DocumentElement, "parent"); // 检查评估模式 // return: // -1 检查过程出错 // 0 可以通过 // 1 不允许通过 nRet = CheckTestModePath(strBiblioDbName + "/" + strParentID, out strError); if (nRet != 0) { strError = strActionName + "操作被拒绝: " + strError; goto ERROR1; } } // *** // 延迟获得读者证条码号 if (string.IsNullOrEmpty(strReaderBarcode) == true) { if (bRenew == false) { strError = "必须提供 strReaderBarcode 参数值才能进行 " + strActionName + " 操作"; goto ERROR1; } string strOutputReaderBarcode = ""; // 返回的借阅者证条码号 // 在册记录中获得借阅者证条码号 // return: // -1 出错 // 0 该册为未借出状态 // 1 成功 nRet = GetBorrowerBarcode(itemdom, out strOutputReaderBarcode, out strError); if (nRet == -1) { strError = strError + " (册记录路径为 '" + strOutputItemRecPath + "')"; goto ERROR1; } if (nRet == 0 || string.IsNullOrEmpty(strOutputReaderBarcode) == true) { strError = "册 '" + strItemBarcode + "' 当前未曾被任何读者借阅,所以无法进行" + strActionName + "操作"; goto ERROR1; } #if NO // 如果提供了读者证条码号,则需要核实 if (String.IsNullOrEmpty(strReaderBarcode) == false) { if (strOutputReaderBarcode != strReaderBarcode) { // 暂时不报错,滞后验证 bDelayVerifyReaderBarcode = true; strIdcardNumber = strReaderBarcode; } } #endif if (String.IsNullOrEmpty(strReaderBarcode) == true) strReaderBarcode = strOutputReaderBarcode; // *** 如果读者记录在前面没有锁定, 在这里锁定 if (bReaderLocked == false) { Debug.Assert(string.IsNullOrEmpty(strReaderBarcode) == false, ""); // 2015/9/2 nRedoCount++; if (nRedoCount > 10) { strError = "Borrow() 为锁定读者,试图重试,但发现已经超过 10 次, 只好放弃..."; this.WriteErrorLog(strError); goto ERROR1; } goto REDO_BORROW; } } // *** // 校验读者证条码号参数是否和XML记录中完全一致 #if NO string strTempReaderBarcode = DomUtil.GetElementText(readerdom.DocumentElement, "barcode"); if (string.IsNullOrEmpty(strReaderBarcode) == false && strReaderBarcode != strTempReaderBarcode) { // text-level: 内部错误 strError = "借阅操作被拒绝。因读者证条码号参数 '" + strReaderBarcode + "' 和读者记录中<barcode>元素内的读者证条码号值 '" + strTempReaderBarcode + "' 不一致。"; goto ERROR1; } #endif { // return: // false 不匹配 // true 匹配 bool bRet = CheckBarcode(readerdom, strReaderBarcode, "读者", out strError); if (bRet == false) { strError = "借阅操作被拒绝。因" + strError + "。"; goto ERROR1; } } // 2007/1/2 // 校验册条码号参数是否和XML记录中完全一致 #if NO string strRefID = ""; string strHead = "@refID:"; // string strFrom = "册条码"; if (StringUtil.HasHead(strItemBarcode, strHead, true) == true) { // strFrom = "参考ID"; strRefID = strItemBarcode.Substring(strHead.Length); string strTempRefID = DomUtil.GetElementText(itemdom.DocumentElement, "refID"); if (strRefID != strTempRefID) { // text-level: 内部错误 strError = "借阅操作被拒绝。因册参考ID参数 '" + strRefID + "' 和册记录中<refID>元素内的册条码号值 '" + strTempRefID + "' 不一致。"; goto ERROR1; } } else { string strTempItemBarcode = DomUtil.GetElementText(itemdom.DocumentElement, "barcode"); if (strItemBarcode != strTempItemBarcode) { // text-level: 内部错误 strError = "借阅操作被拒绝。因册条码号参数 '" + strItemBarcode + "' 和册记录中<barcode>元素内的册条码号值 '" + strTempItemBarcode + "' 不一致。"; goto ERROR1; } } #endif { // return: // false 不匹配 // true 匹配 bool bRet = CheckBarcode(itemdom, strItemBarcode, "册", out strError); if (bRet == false) { // text-level: 内部错误 strError = "借阅操作被拒绝。因" + strError + "。"; goto ERROR1; } } string strReaderType = DomUtil.GetElementText(readerdom.DocumentElement, "readerType"); Calendar calendar = null; // return: // -1 出错 // 0 没有找到日历 // 1 找到日历 nRet = GetReaderCalendar(strReaderType, strLibraryCode, out calendar, out strError); if (nRet == -1 || nRet == 0) goto ERROR1; /* string strBookType = DomUtil.GetElementText(itemdom.DocumentElement, "bookType"); */ bool bReaderDomChanged = false; // 刷新以停代金事项 if (StringUtil.IsInList("pauseBorrowing", this.OverdueStyle) == true) { // // 处理以停代金功能 // return: // -1 error // 0 readerdom没有修改 // 1 readerdom发生了修改 nRet = ProcessPauseBorrowing( strLibraryCode, ref readerdom, strOutputReaderRecPath, sessioninfo.UserID, "refresh", sessioninfo.ClientAddress, // 前端触发 out strError); if (nRet == -1) { // text-level: 内部错误 strError = "在刷新以停代金的过程中发生错误: " + strError; goto ERROR1; } if (nRet == 1) bReaderDomChanged = true; } byte[] output_timestamp = null; string strOutputPath = ""; StringBuilder debugInfo = null; // 检查借阅权限 // return: // -1 配置参数错误 // 0 权限不够,借阅操作应当被拒绝 // 1 权限够 nRet = CheckBorrowRights( sessioninfo.Account, calendar, bRenew, strLibraryCode, // 读者记录所在读者库的馆代码 strAccessParameters, ref readerdom, ref itemdom, ref debugInfo, out strError); if (nRet == -1 || nRet == 0) { // 如果有必要保存回读者记录(因前面刷新了以停代金数据) if (bReaderDomChanged == true) { string strError_1 = ""; /* byte[] output_timestamp = null; string strOutputPath = ""; * */ // 写回读者记录 lRet = channel.DoSaveTextRes(strOutputReaderRecPath, readerdom.OuterXml, false, "content", // ,ignorechecktimestamp reader_timestamp, out output_timestamp, out strOutputPath, out strError_1); if (lRet == -1) { // text-level: 内部错误 strError = strError + "。然而在写入读者记录过程中,发生错误: " + strError_1; goto ERROR1; } } if (nRet == 0) { result.Value = -1; result.ErrorInfo = strError; result.ErrorCode = ErrorCode.AccessDenied; // 权限不够 return result; } goto ERROR1; } XmlDocument domOperLog = new XmlDocument(); domOperLog.LoadXml("<root />"); DomUtil.SetElementText(domOperLog.DocumentElement, "libraryCode", strLibraryCode); // 读者所在的馆代码 DomUtil.SetElementText(domOperLog.DocumentElement, "operation", "borrow"); DomUtil.SetElementText(domOperLog.DocumentElement, "action", bRenew == true ? "renew" : "borrow"); // 原来在这里 // 借阅API的从属函数 // 检查预约相关信息 // return: // -1 error // 0 正常 // 1 发现该册被保留), 不能借阅 // 2 发现该册预约, 不能续借 // 3 发现该册被保留, 不能借阅。而且本函数修改了册记录(<location>元素发生了变化),需要本函数返回后,把册记录保存。 nRet = DoBorrowReservationCheck( sessioninfo, bRenew, ref readerdom, ref itemdom, bForce, out strError); if (nRet == -1) goto ERROR1; if (nRet == 1 || nRet == 2) { // 被预约保留, 不能借阅 result.Value = -1; result.ErrorInfo = strError; if (nRet == 1) result.ErrorCode = ErrorCode.BorrowReservationDenied; if (nRet == 2) result.ErrorCode = ErrorCode.RenewReservationDenied; return result; } if (nRet == 3) { result.Value = -1; result.ErrorInfo = strError; result.ErrorCode = ErrorCode.BorrowReservationDenied; /* 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) { // text-level: 内部错误 strError = "借阅操作中遇在架预约图书需要写回册记录 " + strOutputItemRecPath + " 时出错: " + strError; this.WriteErrorLog(strError); strError += "。借阅操作被拒绝。"; goto ERROR1; } return result; } // 移动到这里 // 在读者记录和册记录中添加借阅信息 // string strNewReaderXml = ""; nRet = DoBorrowReaderAndItemXml( bRenew, strLibraryCode, ref readerdom, ref itemdom, bForce, sessioninfo.UserID, strOutputItemRecPath, strOutputReaderRecPath, ref domOperLog, out borrow_info, out strError); if (nRet == -1) goto ERROR1; WriteTimeUsed( time_lines, start_time_process, "Borrow() 中进行各种数据处理 耗时 "); DateTime start_time_write_reader = DateTime.Now; // 原来输出xml或xml的语句在此 // 写回读者、册记录 // 写回读者记录 lRet = channel.DoSaveTextRes(strOutputReaderRecPath, readerdom.OuterXml, false, "content", // ,ignorechecktimestamp reader_timestamp, out output_timestamp, out strOutputPath, out strError); if (lRet == -1) { // 2015/9/2 this.WriteErrorLog("Borrow() 写入读者记录 '" + strOutputReaderRecPath + "' 时出错: " + strError); if (channel.ErrorCode == ChannelErrorCode.TimestampMismatch) { nRedoCount++; if (nRedoCount > 10) { // text-level: 内部错误 strError = "Borrow() 写回读者记录的时候,遇到时间戳冲突,并因此重试10次未能成功,只好放弃..."; this.WriteErrorLog(strError); goto ERROR1; } goto REDO_BORROW; } goto ERROR1; } WriteTimeUsed( time_lines, start_time_write_reader, "Borrow() 中写回读者记录 耗时 "); DateTime start_time_write_item = DateTime.Now; // 及时更新时间戳 reader_timestamp = output_timestamp; // 写回册记录 lRet = channel.DoSaveTextRes(strOutputItemRecPath, itemdom.OuterXml, false, "content", // ,ignorechecktimestamp item_timestamp, out output_timestamp, out strOutputPath, out strError); if (lRet == -1) { // 2015/9/2 this.WriteErrorLog("Borrow() 写入册记录 '" + strOutputItemRecPath + "' 时出错: " + strError); // 要Undo刚才对读者记录的写入 string strError1 = ""; lRet = channel.DoSaveTextRes(strOutputReaderRecPath, strOldReaderXml, false, "content", // ,ignorechecktimestamp reader_timestamp, out output_timestamp, out strOutputPath, out strError1); if (lRet == -1) // 初次Undo失败 { if (channel.ErrorCode == ChannelErrorCode.TimestampMismatch) { // 读者记录Undo的时候, 发现时间戳冲突了 // 这时需要读出现存记录, 试图删除新增加的<borrows><borrow>元素 // return: // -1 error // 0 没有必要Undo // 1 Undo成功 nRet = UndoBorrowReaderRecord( channel, strOutputReaderRecPath, strReaderBarcode, strItemBarcode, out strError); if (nRet == -1) { // text-level: 内部错误 strError = "Borrow() Undo读者记录 '" + strOutputReaderRecPath + "' (读者证条码号为'" + strReaderBarcode + "') 借阅册条码号 '" + strItemBarcode + "' 的修改时,发生错误,无法Undo: " + strError; this.WriteErrorLog(strError); goto ERROR1; } // 成功 // 2015/9/2 增加下列防止死循环的语句 nRedoCount++; if (nRedoCount > 10) { strError = "Borrow() Undo 读者记录(1)成功,试图重试 Borrow 时,发现先前重试已经超过 10 次,只好不重试了,做出错返回..."; this.WriteErrorLog(strError); goto ERROR1; } goto REDO_BORROW; } // 以下为 不是时间戳冲突的其他错误情形 // text-level: 内部错误 strError = "Borrow() Undo读者记录 '" + strOutputReaderRecPath + "' (读者证条码号为'" + strReaderBarcode + "') 借阅册条码号 '" + strItemBarcode + "' 的修改时,发生错误,无法Undo: " + strError; // strError = strError + ", 并且Undo写回旧读者记录也失败: " + strError1; this.WriteErrorLog(strError); goto ERROR1; } // end of 初次Undo失败 // 以下为Undo成功的情形 // 2015/9/2 增加下列防止死循环的语句 nRedoCount++; if (nRedoCount > 10) { strError = "Borrow() Undo 读者记录(2)成功,试图重试 Borrow 时,发现先前重试已经超过 10 次,只好不重试了,做出错返回..."; this.WriteErrorLog(strError); goto ERROR1; } goto REDO_BORROW; } // end of 写回册记录失败 WriteTimeUsed( time_lines, start_time_write_item, "Borrow() 中写回册记录 耗时 "); DateTime start_time_write_operlog = DateTime.Now; // 写入日志 if (string.IsNullOrEmpty(strConfirmItemRecPath) == false) { DomUtil.SetElementText(domOperLog.DocumentElement, "confirmItemRecPath", strConfirmItemRecPath); } if (string.IsNullOrEmpty(strIdcardNumber) == false) { // 表明是使用身份证号来完成借阅操作的 DomUtil.SetElementText(domOperLog.DocumentElement, "idcardNumber", strIdcardNumber); } // 记载读者记录 XmlNode node = DomUtil.SetElementText(domOperLog.DocumentElement, "readerRecord", readerdom.OuterXml); DomUtil.SetAttr(node, "recPath", strOutputReaderRecPath); // 记载册记录 node = DomUtil.SetElementText(domOperLog.DocumentElement, "itemRecord", itemdom.OuterXml); DomUtil.SetAttr(node, "recPath", strOutputItemRecPath); nRet = this.OperLog.WriteOperLog(domOperLog, sessioninfo.ClientAddress, start_time, out strOperLogUID, out strError); if (nRet == -1) { strError = "Borrow() API 写入日志时发生错误: " + strError; goto ERROR1; } WriteTimeUsed( time_lines, start_time_write_operlog, "Borrow() 中写操作日志 耗时 "); DateTime start_time_write_statis = DateTime.Now; // 写入统计指标 #if NO if (this.m_strLastReaderBarcode != strReaderBarcode) { if (this.Statis != null) this.Statis.IncreaseEntryValue( strLibraryCode, "出纳", "读者数", 1); this.m_strLastReaderBarcode = strReaderBarcode; } #endif if (this.Garden != null) this.Garden.Activate(strReaderBarcode, strLibraryCode); if (this.Statis != null) this.Statis.IncreaseEntryValue( strLibraryCode, "出纳", "借册", 1); WriteTimeUsed( time_lines, start_time_write_statis, "Borrow() 中写统计指标 耗时 "); strOutputItemXml = itemdom.OuterXml; // strOutputReaderXml 将用于构造读者记录返回格式 DomUtil.DeleteElement(readerdom.DocumentElement, "password"); strOutputReaderXml = readerdom.OuterXml; strBiblioRecID = DomUtil.GetElementText(itemdom.DocumentElement, "parent"); // } // 册记录锁定范围结束 finally { // 解册记录锁 this.EntityLocks.UnlockForWrite(strItemBarcode); // strItemBarcode 在整个函数中不允许被修改 } } // 读者记录锁定范围结束 finally { // this.ReaderLocks.UnlockForWrite(strLockReaderBarcode); if (bReaderLocked == true) { this.ReaderLocks.UnlockForWrite(strLockReaderBarcode); #if DEBUG_LOCK_READER this.WriteErrorLog("Borrow 结束为读者加写锁 '" + strLockReaderBarcode + "'"); #endif } } // 输出数据 // 把输出数据部分放在读者锁以外范围,是为了尽量减少锁定的时间,提高并发运行效率 DateTime output_start_time = DateTime.Now; if (String.IsNullOrEmpty(strOutputReaderXml) == false && StringUtil.IsInList("reader", strStyle) == true) { DateTime start_time_1 = DateTime.Now; nRet = BuildReaderResults( sessioninfo, null, strOutputReaderXml, strReaderFormatList, strLibraryCode, // calendar/advancexml/html 时需要 null, // recpaths 时需要 strOutputReaderRecPath, // recpaths 时需要 null, // timestamp 时需要 OperType.Borrow, saBorrowedItemBarcode, strItemBarcode, ref reader_records, out strError); if (nRet == -1) { // text-level: 用户提示 strError = string.Format(this.GetString("虽然出现了下列错误,但是借阅操作已经成功s"), // "虽然出现了下列错误,但是借阅操作已经成功: {0}"; strError); // "虽然出现了下列错误,但是借阅操作已经成功: " + strError; goto ERROR1; } WriteTimeUsed( time_lines, start_time_1, "Borrow() 中返回读者记录(" + strReaderFormatList + ") 耗时 "); } #if NO if (String.IsNullOrEmpty(strOutputReaderXml) == false && StringUtil.IsInList("reader", strStyle) == true) { DateTime start_time_1 = DateTime.Now; string[] reader_formats = strReaderFormatList.Split(new char[] { ',' }); reader_records = new string[reader_formats.Length]; for (int i = 0; i < reader_formats.Length; i++) { string strReaderFormat = reader_formats[i]; // 将读者记录数据从XML格式转换为HTML格式 // if (String.Compare(strReaderFormat, "html", true) == 0) if (IsResultType(strReaderFormat, "html") == true) { string strReaderRecord = ""; nRet = this.ConvertReaderXmlToHtml( sessioninfo, this.CfgDir + "\\readerxml2html.cs", this.CfgDir + "\\readerxml2html.cs.ref", strLibraryCode, strOutputReaderXml, strOutputReaderRecPath, // 2009/10/18 OperType.Borrow, saBorrowedItemBarcode, strItemBarcode, strReaderFormat, out strReaderRecord, out strError); if (nRet == -1) { // text-level: 用户提示 strError = string.Format(this.GetString("虽然出现了下列错误,但是借阅操作已经成功s"), // "虽然出现了下列错误,但是借阅操作已经成功: {0}"; strError); // "虽然出现了下列错误,但是借阅操作已经成功: " + strError; goto ERROR1; } reader_records[i] = strReaderRecord; } // 将读者记录数据从XML格式转换为text格式 // else if (String.Compare(strReaderFormat, "text", true) == 0) else if (IsResultType(strReaderFormat, "text") == true) { string strReaderRecord = ""; nRet = this.ConvertReaderXmlToHtml( sessioninfo, this.CfgDir + "\\readerxml2text.cs", this.CfgDir + "\\readerxml2text.cs.ref", strLibraryCode, strOutputReaderXml, strOutputReaderRecPath, // 2009/10/18 OperType.Borrow, saBorrowedItemBarcode, strItemBarcode, strReaderFormat, out strReaderRecord, out strError); if (nRet == -1) { // text-level: 用户提示 strError = string.Format(this.GetString("虽然出现了下列错误,但是借阅操作已经成功s"), // "虽然出现了下列错误,但是借阅操作已经成功: {0}"; strError); goto ERROR1; } reader_records[i] = strReaderRecord; } // else if (String.Compare(strReaderFormat, "xml", true) == 0) else if (IsResultType(strReaderFormat, "xml") == true) { // reader_records[i] = strOutputReaderXml; string strResultXml = ""; nRet = GetItemXml(strOutputReaderXml, strReaderFormat, out strResultXml, out strError); if (nRet == -1) { // text-level: 用户提示 strError = string.Format(this.GetString("虽然出现了下列错误,但是借阅操作已经成功s"), // "虽然出现了下列错误,但是借阅操作已经成功: {0}"; strError); goto ERROR1; } reader_records[i] = strResultXml; } else if (IsResultType(strReaderFormat, "summary") == true) { // 2013/12/15 XmlDocument dom = new XmlDocument(); try { dom.LoadXml(strOutputReaderXml); } catch (Exception ex) { strError = "读者 XML 装入 DOM 出错: " + ex.Message; // text-level: 用户提示 strError = string.Format(this.GetString("虽然出现了下列错误,但是借阅操作已经成功s"), // "虽然出现了下列错误,但是借阅操作已经成功: {0}"; strError); goto ERROR1; } reader_records[i] = DomUtil.GetElementText(dom.DocumentElement, "name"); } else { strError = "strReaderFormatList参数出现了不支持的数据格式类型 '" + strReaderFormat + "'"; // text-level: 用户提示 strError = string.Format(this.GetString("虽然出现了下列错误,但是借阅操作已经成功s"), // "虽然出现了下列错误,但是借阅操作已经成功: {0}"; strError); goto ERROR1; } } // end of for WriteTimeUsed( time_lines, start_time_1, "Borrow() 中返回读者记录(" + strReaderFormatList + ") 耗时 "); } #endif if (String.IsNullOrEmpty(strOutputItemXml) == false && StringUtil.IsInList("item", strStyle) == true) { DateTime start_time_1 = DateTime.Now; string[] item_formats = strItemFormatList.Split(new char[] { ',' }); item_records = new string[item_formats.Length]; for (int i = 0; i < item_formats.Length; i++) { string strItemFormat = item_formats[i]; // 将册记录数据从XML格式转换为HTML格式 //if (String.Compare(strItemFormat, "html", true) == 0) if (IsResultType(strItemFormat, "html") == true) { string strItemRecord = ""; nRet = this.ConvertItemXmlToHtml( this.CfgDir + "\\itemxml2html.cs", this.CfgDir + "\\itemxml2html.cs.ref", strOutputItemXml, strOutputItemRecPath, // 2009/10/18 out strItemRecord, out strError); if (nRet == -1) { // text-level: 用户提示 strError = string.Format(this.GetString("虽然出现了下列错误,但是借阅操作已经成功s"), // "虽然出现了下列错误,但是借阅操作已经成功: {0}"; strError); goto ERROR1; } item_records[i] = strItemRecord; } // 将册记录数据从XML格式转换为text格式 // else if (String.Compare(strItemFormat, "text", true) == 0) else if (IsResultType(strItemFormat, "text") == true) { string strItemRecord = ""; nRet = this.ConvertItemXmlToHtml( this.CfgDir + "\\itemxml2text.cs", this.CfgDir + "\\itemxml2text.cs.ref", strOutputItemXml, strOutputItemRecPath, // 2009/10/18 out strItemRecord, out strError); if (nRet == -1) { // text-level: 用户提示 strError = string.Format(this.GetString("虽然出现了下列错误,但是借阅操作已经成功s"), // "虽然出现了下列错误,但是借阅操作已经成功: {0}"; strError); goto ERROR1; } item_records[i] = strItemRecord; } // else if (String.Compare(strItemFormat, "xml", true) == 0) else if (IsResultType(strItemFormat, "xml") == true) { string strResultXml = ""; nRet = GetItemXml(strOutputItemXml, strItemFormat, out strResultXml, out strError); if (nRet == -1) { // text-level: 用户提示 strError = string.Format(this.GetString("虽然出现了下列错误,但是借阅操作已经成功s"), // "虽然出现了下列错误,但是借阅操作已经成功: {0}"; strError); goto ERROR1; } item_records[i] = strResultXml; } else { strError = "strItemFormatList参数出现了不支持的数据格式类型 '" + strItemFormat + "'"; // text-level: 用户提示 strError = string.Format(this.GetString("虽然出现了下列错误,但是借阅操作已经成功s"), // "虽然出现了下列错误,但是借阅操作已经成功: {0}"; strError); goto ERROR1; } } // end of for WriteTimeUsed( time_lines, start_time_1, "Borrow() 中返回册记录(" + strItemFormatList + ") 耗时 "); } // 2008/5/9 if (StringUtil.IsInList("biblio", strStyle) == true) { DateTime start_time_1 = DateTime.Now; if (String.IsNullOrEmpty(strBiblioRecID) == true) { strError = "册记录XML中<parent>元素缺乏或者值为空, 因此无法定位种记录ID"; // text-level: 用户提示 strError = string.Format(this.GetString("虽然出现了下列错误,但是借阅操作已经成功s"), // "虽然出现了下列错误,但是借阅操作已经成功: {0}"; strError); goto ERROR1; } string strItemDbName = ResPath.GetDbName(strOutputItemRecPath); string strBiblioDbName = ""; // 根据实体库名, 找到对应的书目库名 // return: // -1 出错 // 0 没有找到 // 1 找到 nRet = this.GetBiblioDbNameByItemDbName(strItemDbName, out strBiblioDbName, out strError); if (nRet == -1) goto ERROR1; if (nRet == 0) { strError = "实体库名 '" + strItemDbName + "' 没有找到对应的书目库名"; // text-level: 用户提示 strError = string.Format(this.GetString("虽然出现了下列错误,但是借阅操作已经成功s"), // "虽然出现了下列错误,但是借阅操作已经成功: {0}"; strError); goto ERROR1; } string strBiblioRecPath = strBiblioDbName + "/" + strBiblioRecID; string[] biblio_formats = strBiblioFormatList.Split(new char[] { ',' }); biblio_records = new string[biblio_formats.Length]; string strBiblioXml = ""; // 至少有html xml text之一,才获取strBiblioXml if (StringUtil.IsInList("html", strBiblioFormatList) == true || StringUtil.IsInList("xml", strBiblioFormatList) == true || StringUtil.IsInList("text", strBiblioFormatList) == true) { RmsChannel channel = sessioninfo.Channels.GetChannel(this.WsUrl); if (channel == null) { strError = "get channel error"; // text-level: 用户提示 strError = string.Format(this.GetString("虽然出现了下列错误,但是借阅操作已经成功s"), // "虽然出现了下列错误,但是借阅操作已经成功: {0}"; strError); goto ERROR1; } string strMetaData = ""; byte[] timestamp = null; string strTempOutputPath = ""; lRet = channel.GetRes(strBiblioRecPath, out strBiblioXml, out strMetaData, out timestamp, out strTempOutputPath, out strError); if (lRet == -1) { strError = "获得种记录 '" + strBiblioRecPath + "' 时出错: " + strError; // text-level: 用户提示 strError = string.Format(this.GetString("虽然出现了下列错误,但是借阅操作已经成功s"), // "虽然出现了下列错误,但是借阅操作已经成功: {0}"; strError); goto ERROR1; } } for (int i = 0; i < biblio_formats.Length; i++) { string strBiblioFormat = biblio_formats[i]; // 需要从内核映射过来文件 string strLocalPath = ""; string strBiblio = ""; // 将书目记录数据从XML格式转换为HTML格式 if (String.Compare(strBiblioFormat, "html", true) == 0) { // TODO: 可以cache nRet = this.MapKernelScriptFile( sessioninfo, strBiblioDbName, "./cfgs/loan_biblio.fltx", out strLocalPath, out strError); if (nRet == -1) { // text-level: 用户提示 strError = string.Format(this.GetString("虽然出现了下列错误,但是借阅操作已经成功s"), // "虽然出现了下列错误,但是借阅操作已经成功: {0}"; strError); goto ERROR1; } // 将种记录数据从XML格式转换为HTML格式 string strFilterFileName = strLocalPath; // app.CfgDir + "\\biblio.fltx"; if (string.IsNullOrEmpty(strBiblioXml) == false) { nRet = this.ConvertBiblioXmlToHtml( strFilterFileName, strBiblioXml, null, strBiblioRecPath, out strBiblio, out strError); if (nRet == -1) { // text-level: 用户提示 strError = string.Format(this.GetString("虽然出现了下列错误,但是借阅操作已经成功s"), // "虽然出现了下列错误,但是借阅操作已经成功: {0}"; strError); goto ERROR1; } } else strBiblio = ""; biblio_records[i] = strBiblio; } // 将册记录数据从XML格式转换为text格式 else if (String.Compare(strBiblioFormat, "text", true) == 0) { // TODO: 可以cache nRet = this.MapKernelScriptFile( sessioninfo, strBiblioDbName, "./cfgs/loan_biblio_text.fltx", out strLocalPath, out strError); if (nRet == -1) { // text-level: 用户提示 strError = string.Format(this.GetString("虽然出现了下列错误,但是借阅操作已经成功s"), // "虽然出现了下列错误,但是借阅操作已经成功: {0}"; strError); goto ERROR1; } // 将种记录数据从XML格式转换为TEXT格式 string strFilterFileName = strLocalPath; // app.CfgDir + "\\biblio.fltx"; if (string.IsNullOrEmpty(strBiblioXml) == false) { nRet = this.ConvertBiblioXmlToHtml( strFilterFileName, strBiblioXml, null, strBiblioRecPath, out strBiblio, out strError); if (nRet == -1) { // text-level: 用户提示 strError = string.Format(this.GetString("虽然出现了下列错误,但是借阅操作已经成功s"), // "虽然出现了下列错误,但是借阅操作已经成功: {0}"; strError); goto ERROR1; } } else strBiblio = ""; biblio_records[i] = strBiblio; } else if (String.Compare(strBiblioFormat, "xml", true) == 0) { biblio_records[i] = strBiblioXml; } else if (String.Compare(strBiblioFormat, "recpath", true) == 0) { biblio_records[i] = strBiblioRecPath; } else if (string.IsNullOrEmpty(strBiblioFormat) == true) { biblio_records[i] = ""; } else { strError = "strBiblioFormatList参数出现了不支持的数据格式类型 '" + strBiblioFormat + "'"; // text-level: 用户提示 strError = string.Format(this.GetString("虽然出现了下列错误,但是借阅操作已经成功s"), // "虽然出现了下列错误,但是借阅操作已经成功: {0}"; strError); goto ERROR1; } } // end of for WriteTimeUsed( time_lines, start_time_1, "Borrow() 中返回书目记录(" + strBiblioFormatList + ") 耗时 "); } WriteTimeUsed( time_lines, start_time, "Borrow() 耗时 "); // 如果整个时间超过一秒,则需要计入操作日志 if (DateTime.Now - start_time > new TimeSpan(0, 0, 1)) { WriteLongTimeOperLog( sessioninfo, strAction, start_time, "整个操作耗时超过 1 秒。详情:" + StringUtil.MakePathList(time_lines, ";"), strOperLogUID, out strError); } // 如果创建输出数据的时间超过一秒,则需要计入操作日志 if (DateTime.Now - output_start_time > new TimeSpan(0, 0, 1)) { WriteLongTimeOperLog( sessioninfo, strAction, output_start_time, "output 阶段耗时超过 1 秒", strOperLogUID, out strError); } return result; ERROR1: result.Value = -1; result.ErrorInfo = strError; result.ErrorCode = ErrorCode.SystemError; return result; }
// 交违约金/撤销交违约金 // parameters: // strReaderBarcode 如果功能是"undo",可以将此参数设置为null。如果此参数不为null,则软件要进行核对,如果不是这个读者的已付违约金记录,则要报错 // strAmerceItemIdList id列表, 以逗号分割 // 权限:需要有amerce/amercemodifyprice/amerceundo/amercemodifycomment等权限 // 日志: // 要产生日志 // return: // result.Value 0 成功;1 部分成功(result.ErrorInfo中有信息) public LibraryServerResult Amerce( SessionInfo sessioninfo, string strFunction, string strReaderBarcode, AmerceItem[] amerce_items, out AmerceItem[] failed_items, out string strReaderXml) { strReaderXml = ""; failed_items = null; LibraryServerResult result = new LibraryServerResult(); if (String.Compare(strFunction, "amerce", true) == 0) { // 权限字符串 if (StringUtil.IsInList("amerce", sessioninfo.RightsOrigin) == false) { result.Value = -1; result.ErrorInfo = "交违约金操作被拒绝。不具备 amerce 权限。"; result.ErrorCode = ErrorCode.AccessDenied; return result; } } if (String.Compare(strFunction, "modifyprice", true) == 0) { // 权限字符串 if (StringUtil.IsInList("amercemodifyprice", sessioninfo.RightsOrigin) == false) { result.Value = -1; result.ErrorInfo = "修改违约金额的操作被拒绝。不具备 amercemodifyprice 权限。"; result.ErrorCode = ErrorCode.AccessDenied; return result; } } if (String.Compare(strFunction, "modifycomment", true) == 0) { /* // 权限字符串 if (StringUtil.IsInList("amercemodifycomment", sessioninfo.RightsOrigin) == false) { result.Value = -1; result.ErrorInfo = "修改违约金之注释的操作被拒绝。不具备 amercemodifycomment 权限。"; result.ErrorCode = ErrorCode.AccessDenied; return result; } * */ } if (String.Compare(strFunction, "undo", true) == 0) { // 权限字符串 if (StringUtil.IsInList("amerceundo", sessioninfo.RightsOrigin) == false) { result.Value = -1; result.ErrorInfo = "撤销交违约金操作被拒绝。不具备 amerceundo 权限。"; result.ErrorCode = ErrorCode.AccessDenied; return result; } } if (String.Compare(strFunction, "rollback", true) == 0) { // 权限字符串 if (StringUtil.IsInList("amerce", sessioninfo.RightsOrigin) == false) { result.Value = -1; result.ErrorInfo = "撤回交违约金事务的操作被拒绝。不具备 amerce 权限。"; result.ErrorCode = ErrorCode.AccessDenied; return result; } } if (strFunction != "rollback") { // 看看amerce_items中是否有价格变更或注释变更的情况 bool bHasNewPrice = false; bool bHasOverwriteComment = false; // NewComment具有、并且为覆盖。也就是说包括NewPrice和NewComment同时具有的情况 for (int i = 0; i < amerce_items.Length; i++) { AmerceItem item = amerce_items[i]; // NewPrice域中有值 if (String.IsNullOrEmpty(item.NewPrice) == false) { bHasNewPrice = true; } // NewComment域中有值 if (String.IsNullOrEmpty(item.NewComment) == false) { string strNewComment = item.NewComment; bool bAppend = true; if (string.IsNullOrEmpty(strNewComment) == false && strNewComment[0] == '<') { bAppend = false; strNewComment = strNewComment.Substring(1); } else if (string.IsNullOrEmpty(strNewComment) == false && strNewComment[0] == '>') { bAppend = true; strNewComment = strNewComment.Substring(1); } if (bAppend == false) bHasOverwriteComment = true; } } // 如果要变更价格,则需要额外的amercemodifyprice权限。 // amercemodifyprice在功能amerce和modifyprice中都可能用到,关键是看是否提交了有新价格的参数 if (bHasNewPrice == true) { if (StringUtil.IsInList("amercemodifyprice", sessioninfo.RightsOrigin) == false) { result.Value = -1; result.ErrorInfo = "含有价格变更要求的交违约金操作被拒绝。不具备 amercemodifyprice 权限。(仅仅具备 amerce 权限还不够的)"; result.ErrorCode = ErrorCode.AccessDenied; return result; } } if (bHasOverwriteComment == true) { // 如果有了amerce权限,则暗含有了amerceappendcomment的权限 if (StringUtil.IsInList("amercemodifycomment", sessioninfo.RightsOrigin) == false) { result.Value = -1; result.ErrorInfo = "含有违约金注释(覆盖型)变更要求的操作被拒绝。不具备 amercemodifycomment 权限。(仅仅具备 amerce 权限还不够的)"; result.ErrorCode = ErrorCode.AccessDenied; return result; } } } int nRet = 0; string strError = ""; RmsChannel channel = sessioninfo.Channels.GetChannel(this.WsUrl); if (channel == null) { strError = "get channel error"; goto ERROR1; } if (String.Compare(strFunction, "amerce", true) != 0 && String.Compare(strFunction, "undo", true) != 0 && String.Compare(strFunction, "modifyprice", true) != 0 && String.Compare(strFunction, "modifycomment", true) != 0 && String.Compare(strFunction, "rollback", true) != 0) { result.Value = -1; result.ErrorInfo = "未知的strFunction参数值 '" + strFunction + "'"; result.ErrorCode = ErrorCode.InvalidParameter; return result; } // 如果是undo, 需要先检索出指定id的违约金库记录,然后从记录中得到<readerBarcode>,和参数核对 if (String.Compare(strFunction, "undo", true) == 0) { // UNDO违约金交纳 // return: // -1 error // 0 succeed // 1 部分成功。strError中有报错信息 nRet = UndoAmerces( sessioninfo, strReaderBarcode, amerce_items, out failed_items, out strReaderXml, out strError); if (nRet == -1) goto ERROR1; // 2009/10/10 changed result.Value = nRet; if (nRet == 1) result.ErrorInfo = strError; return result; } // 回滚 // 2009/7/14 if (String.Compare(strFunction, "rollback", true) == 0) { if (amerce_items != null) { strError = "调用rollback功能时amerce_item参数必须为空"; goto ERROR1; } if (sessioninfo.AmerceIds == null || sessioninfo.AmerceIds.Count == 0) { strError = "当前没有可以rollback的违约金事项"; goto ERROR1; } // strReaderBarcode参数值一般为空即可。如果有值,则要求和SessionInfo对象中储存的最近一次的Amerce操作读者证条码号一致 if (String.IsNullOrEmpty(strReaderBarcode) == false) { if (sessioninfo.AmerceReaderBarcode != strReaderBarcode) { strError = "调用rollback功能时strReaderBarcode参数和最近一次Amerce操作的读者证条码号不一致"; goto ERROR1; } } amerce_items = new AmerceItem[sessioninfo.AmerceIds.Count]; for (int i = 0; i < sessioninfo.AmerceIds.Count; i++) { AmerceItem item = new AmerceItem(); item.ID = sessioninfo.AmerceIds[i]; amerce_items[i] = item; } // UNDO违约金交纳 // return: // -1 error // 0 succeed // 1 部分成功。strError中有报错信息 nRet = UndoAmerces( sessioninfo, sessioninfo.AmerceReaderBarcode, amerce_items, out failed_items, out strReaderXml, out strError); if (nRet == -1 || nRet == 1) goto ERROR1; // 清空ids sessioninfo.AmerceIds = new List<string>(); sessioninfo.AmerceReaderBarcode = ""; result.Value = 0; return result; } // 加读者记录锁 #if DEBUG_LOCK_READER this.WriteErrorLog("Amerce 开始为读者加写锁 '" + strReaderBarcode + "'"); #endif this.ReaderLocks.LockForWrite(strReaderBarcode); try { // 读入读者记录 strReaderXml = ""; string strOutputReaderRecPath = ""; byte[] reader_timestamp = null; nRet = this.GetReaderRecXml( // sessioninfo.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; } // 所操作的读者库德馆代码 string strLibraryCode = ""; // 看看读者记录所从属的读者库的馆代码,是否被当前用户管辖 if (String.IsNullOrEmpty(strOutputReaderRecPath) == false) { // 获得读者库的馆代码 // return: // -1 出错 // 0 成功 nRet = GetLibraryCode( strOutputReaderRecPath, out strLibraryCode, out strError); if (nRet == -1) goto ERROR1; // 检查当前操作者是否管辖这个读者库 // 观察一个读者记录路径,看看是不是在当前用户管辖的读者库范围内? if (this.IsCurrentChangeableReaderPath(strOutputReaderRecPath, sessioninfo.LibraryCodeList) == false) { strError = "读者记录路径 '" + strOutputReaderRecPath + "' 从属的读者库不在当前用户管辖范围内"; goto ERROR1; } } XmlDocument readerdom = null; nRet = LibraryApplication.LoadToDom(strReaderXml, out readerdom, out strError); if (nRet == -1) { strError = "装载读者记录进入XML DOM时发生错误: " + strError; goto ERROR1; } // 准备日志DOM XmlDocument domOperLog = new XmlDocument(); domOperLog.LoadXml("<root />"); DomUtil.SetElementText(domOperLog.DocumentElement, "libraryCode", strLibraryCode); // 读者所在的馆代码 DomUtil.SetElementText(domOperLog.DocumentElement, "operation", "amerce"); // 具体动作 DomUtil.SetElementText(domOperLog.DocumentElement, "action", strFunction.ToLower()); // 读者证条码号 DomUtil.SetElementText(domOperLog.DocumentElement, "readerBarcode", strReaderBarcode); // List<string> AmerceRecordXmls = null; List<string> CreatedNewPaths = null; List<string> Ids = null; string strOperTimeString = this.Clock.GetClock(); // RFC1123格式 bool bReaderDomChanged = false; // 读者dom是否发生了变化,需要回存 { // 在日志中保留旧的读者记录 XmlNode node = DomUtil.SetElementText(domOperLog.DocumentElement, "oldReaderRecord", strReaderXml); DomUtil.SetAttr(node, "recPath", strOutputReaderRecPath); } if (String.Compare(strFunction, "modifyprice", true) == 0) { /* // 在日志中保留旧的读者记录 XmlNode node = DomUtil.SetElementText(domOperLog.DocumentElement, "oldReaderRecord", strReaderXml); DomUtil.SetAttr(node, "recPath", strOutputReaderRecPath); * */ nRet = ModifyPrice(ref readerdom, amerce_items, out strError); if (nRet == -1) goto ERROR1; if (nRet != 0) { bReaderDomChanged = true; if (this.Statis != null) this.Statis.IncreaseEntryValue( strLibraryCode, "违约金", "修改次", nRet); } else { // 如果一个事项也没有发生修改,则需要返回错误信息,以引起前端的警觉 strError = "警告:没有任何事项的价格(和注释)被修改。"; goto ERROR1; } goto SAVERECORD; } if (String.Compare(strFunction, "modifycomment", true) == 0) { /* // 在日志中保留旧的读者记录 XmlNode node = DomUtil.SetElementText(domOperLog.DocumentElement, "oldReaderRecord", strReaderXml); DomUtil.SetAttr(node, "recPath", strOutputReaderRecPath); * */ nRet = ModifyComment( ref readerdom, amerce_items, out strError); if (nRet == -1) goto ERROR1; if (nRet != 0) { bReaderDomChanged = true; if (this.Statis != null) this.Statis.IncreaseEntryValue( strLibraryCode, "违约金之注释", "修改次", nRet); } else { // 如果一个事项也没有发生修改,则需要返回错误信息,以引起前端的警觉 strError = "警告:没有任何事项的注释被修改。"; goto ERROR1; } goto SAVERECORD; } List<string> NotFoundIds = null; Ids = null; // 交违约金:在读者记录中去除所选的<overdue>元素,并且构造一批新记录准备加入违约金库 // return: // -1 error // 0 读者dom没有变化 // 1 读者dom发生了变化 nRet = DoAmerceReaderXml( strLibraryCode, ref readerdom, amerce_items, sessioninfo.UserID, strOperTimeString, out AmerceRecordXmls, out NotFoundIds, out Ids, out strError); if (nRet == -1) goto ERROR1; if (nRet == 1) bReaderDomChanged = true; // 在违约金数据库中创建若干新的违约金记录 // parameters: // AmerceRecordXmls 需要写入的新记录的数组 // CreatedNewPaths 已经创建的新记录的路径数组。可以用于Undo(删除刚刚创建的新记录) nRet = CreateAmerceRecords( // sessioninfo.Channels, channel, AmerceRecordXmls, out CreatedNewPaths, out strError); if (nRet == -1) { // undo已经写入的部分记录 if (CreatedNewPaths != null && CreatedNewPaths.Count != 0) { string strNewError = ""; nRet = DeleteAmerceRecords( sessioninfo.Channels, CreatedNewPaths, out strNewError); if (nRet == -1) { string strList = ""; for (int i = 0; i < CreatedNewPaths.Count; i++) { if (strList != "") strList += ","; strList += CreatedNewPaths[i]; } strError = "在创建新的违约金记录的过程中发生错误: " + strError + "。在Undo新创建的违约金记录的过程中,又发生错误: " + strNewError + ", 请系统管理员手工删除新创建的罚款记录: " + strList; goto ERROR1; } } goto ERROR1; } SAVERECORD: // 为写回读者、册记录做准备 // byte[] timestamp = null; byte[] output_timestamp = null; string strOutputPath = ""; #if NO RmsChannel channel = sessioninfo.Channels.GetChannel(this.WsUrl); if (channel == null) { strError = "get channel error"; goto ERROR1; } #endif long lRet = 0; if (bReaderDomChanged == true) { strReaderXml = readerdom.OuterXml; lRet = channel.DoSaveTextRes(strOutputReaderRecPath, strReaderXml, false, "content,ignorechecktimestamp", reader_timestamp, out output_timestamp, out strOutputPath, out strError); if (lRet == -1) goto ERROR1; // id list /* DomUtil.SetElementText(domOperLog.DocumentElement, "idList", strAmerceItemIdList); * */ WriteAmerceItemList(domOperLog, amerce_items); /* DomUtil.SetElementText(domOperLog.DocumentElement, "readerBarcode", strReaderBarcode); DomUtil.SetElementText(domOperLog.DocumentElement, "itemBarcodeList", strItemBarcodeList); */ // 仅当功能为amerce时,才把被修改的实体记录写入日志。 if (String.Compare(strFunction, "amerce", true) == 0) { Debug.Assert(AmerceRecordXmls.Count == CreatedNewPaths.Count, ""); // 写入多个重复的<amerceRecord>元素 for (int i = 0; i < AmerceRecordXmls.Count; i++) { XmlNode nodeAmerceRecord = domOperLog.CreateElement("amerceRecord"); domOperLog.DocumentElement.AppendChild(nodeAmerceRecord); nodeAmerceRecord.InnerText = AmerceRecordXmls[i]; DomUtil.SetAttr(nodeAmerceRecord, "recPath", CreatedNewPaths[i]); /* DomUtil.SetElementText(domOperLog.DocumentElement, "record", AmerceRecordXmls[i]); **/ if (this.Statis != null) this.Statis.IncreaseEntryValue( strLibraryCode, "违约金", "给付次", 1); { string strPrice = ""; // 取出违约金记录中的金额数字 nRet = GetAmerceRecordPrice(AmerceRecordXmls[i], out strPrice, out strError); if (nRet != -1) { string strPrefix = ""; string strPostfix = ""; double fValue = 0.0; // 分析价格参数 nRet = ParsePriceUnit(strPrice, out strPrefix, out fValue, out strPostfix, out strError); if (nRet != -1) { if (this.Statis != null) this.Statis.IncreaseEntryValue( strLibraryCode, "违约金", "给付元", fValue); } else { // 2012/11/15 this.WriteErrorLog("累计 违约金 给付元 [" + strPrice + "] 时出错: " + strError); } } } } // end of for } // 最新的读者记录 XmlNode node = DomUtil.SetElementText(domOperLog.DocumentElement, "readerRecord", strReaderXml); DomUtil.SetAttr(node, "recPath", strOutputReaderRecPath); string strOperTime = this.Clock.GetClock(); DomUtil.SetElementText(domOperLog.DocumentElement, "operator", sessioninfo.UserID); // 操作者 DomUtil.SetElementText(domOperLog.DocumentElement, "operTime", strOperTime); // 操作时间 nRet = this.OperLog.WriteOperLog(domOperLog, sessioninfo.ClientAddress, out strError); if (nRet == -1) { strError = "Amerce() API 写入日志时发生错误: " + strError; goto ERROR1; } } // 记忆下最近一次Amerce操作的ID和读者证条码号 if (strFunction != "rollback" && Ids != null && Ids.Count != 0) { sessioninfo.AmerceReaderBarcode = strReaderBarcode; sessioninfo.AmerceIds = Ids; } } finally { this.ReaderLocks.UnlockForWrite(strReaderBarcode); #if DEBUG_LOCK_READER this.WriteErrorLog("Amerce 结束为读者加写锁 '" + strReaderBarcode + "'"); #endif } return result; ERROR1: result.Value = -1; result.ErrorInfo = strError; result.ErrorCode = ErrorCode.SystemError; return result; }
// API: 还书 // 权限: 工作人员需要return权限,如果是丢失处理需要lost权限;所有读者均不具备还书操作权限。盘点需要 inventory 权限 // parameters: // strAction return/lost/inventory/read // strReaderBarcodeParam 读者证条码号。当 strAction 为 "inventory" 时,这里是批次号 // return: // Result.Value -1 出错 0 操作成功 1 操作成功,但有值得操作人员留意的情况:如有超期情况;发现条码号重复;需要放入预约架 public LibraryServerResult Return( SessionInfo sessioninfo, string strAction, string strReaderBarcodeParam, string strItemBarcodeParam, string strConfirmItemRecPath, bool bForce, string strStyle, string strItemFormatList, // 2008/5/9 out string[] item_records, // 2008/5/9 string strReaderFormatList, out string[] reader_records, string strBiblioFormatList, // 2008/5/9 out string[] biblio_records, // 2008/5/9 out string[] aDupPath, out string strOutputReaderBarcodeParam, out ReturnInfo return_info) { item_records = null; reader_records = null; biblio_records = null; aDupPath = null; strOutputReaderBarcodeParam = ""; return_info = new ReturnInfo(); string strError = ""; List<string> time_lines = new List<string>(); DateTime start_time = DateTime.Now; string strOperLogUID = ""; LibraryServerResult result = new LibraryServerResult(); string strActionName = GetReturnActionName(strAction); // 个人书斋名 string strPersonalLibrary = ""; if (sessioninfo.UserType == "reader" && sessioninfo.Account != null) strPersonalLibrary = sessioninfo.Account.PersonalLibrary; // 权限判断 if (strAction == "return") { // 权限字符串 if (StringUtil.IsInList("return", sessioninfo.RightsOrigin) == false) { result.Value = -1; result.ErrorInfo = strActionName + "操作被拒绝。不具备 return 权限。"; result.ErrorCode = ErrorCode.AccessDenied; // return result; } } else if (strAction == "lost") { // 权限字符串 if (StringUtil.IsInList("lost", sessioninfo.RightsOrigin) == false) { result.Value = -1; result.ErrorInfo = strActionName + " 操作被拒绝。不具备 lost 权限。"; result.ErrorCode = ErrorCode.AccessDenied; // return result; } } else if (strAction == "inventory") { // 权限字符串 if (StringUtil.IsInList("inventory", sessioninfo.RightsOrigin) == false) { result.Value = -1; result.ErrorInfo = strActionName + " 操作被拒绝。不具备 inventory 权限。"; result.ErrorCode = ErrorCode.AccessDenied; // return result; } } else if (strAction == "read") { // 权限字符串 if (StringUtil.IsInList("read", sessioninfo.RightsOrigin) == false) { result.Value = -1; result.ErrorInfo = strActionName + " 操作被拒绝。不具备 read 权限。"; result.ErrorCode = ErrorCode.AccessDenied; // return result; } } else { strError = "无法识别的 strAction 参数值 '" + strAction + "'。"; goto ERROR1; } // 对读者身份的附加判断 // 注:具有个人书斋的,还可以继续向后执行 if (sessioninfo.UserType == "reader" && string.IsNullOrEmpty(strPersonalLibrary) == true) { result.Value = -1; result.ErrorInfo = strActionName + "操作被拒绝。作为读者不能进行此类操作。"; result.ErrorCode = ErrorCode.AccessDenied; return result; } // 如果没有普通的权限,需要预检查存取权限 LibraryServerResult result_save = null; if (result.Value == -1 && String.IsNullOrEmpty(sessioninfo.Access) == false) { string strAccessActionList = GetDbOperRights(sessioninfo.Access, "", // 此时还不知道实体库名,先取得当前帐户关于任意一个实体库的存取定义 "circulation"); if (string.IsNullOrEmpty(strAccessActionList) == true) return result; // 通过了这样一番检查后,后面依然要检查存取权限。 // 如果后面检查中,精确针对某个实体库的存取权限存在,则依存取权限;如果不存在,则依普通权限 result_save = result.Clone(); } else if (result.Value == -1) return result; // 延迟报错 2014/9/16 result = new LibraryServerResult(); string strReservationReaderBarcode = ""; string strReaderBarcode = strReaderBarcodeParam; if (strAction == "read" && string.IsNullOrEmpty(strReaderBarcode)) { strError = "读过功能 strReaderBarcode 参数值不应为空"; goto ERROR1; } string strBatchNo = ""; if (strAction == "inventory") { strBatchNo = strReaderBarcodeParam; // 为避免判断发生混乱,后面统一用 strBatchNo 存储批次号 strReaderBarcodeParam = ""; strReaderBarcode = ""; } long lRet = 0; int nRet = 0; string strIdcardNumber = ""; string strQrCode = ""; // bool bDelayVerifyReaderBarcode = false; // 是否延迟验证 string strLockReaderBarcode = ""; if (bForce == true) { strError = "bForce 参数不能为 true"; goto ERROR1; } if (string.IsNullOrEmpty(strReaderBarcode) == false) { string strOutputCode = ""; // 把二维码字符串转换为读者证条码号 // parameters: // strReaderBcode [out]读者证条码号 // return: // -1 出错 // 0 所给出的字符串不是读者证号二维码 // 1 成功 nRet = this.DecodeQrCode(strReaderBarcode, out strOutputCode, out strError); if (nRet == -1) goto ERROR1; if (nRet == 1) { strQrCode = strReaderBarcode; strReaderBarcode = strOutputCode; } } RmsChannel channel = sessioninfo.Channels.GetChannel(this.WsUrl); if (channel == null) { strError = "get channel error"; goto ERROR1; } int nRedoCount = 0; REDO_RETURN: bool bReaderLocked = false; bool bEntityLocked = false; if (String.IsNullOrEmpty(strReaderBarcodeParam) == false) { // 加读者记录锁 strLockReaderBarcode = strReaderBarcodeParam; #if DEBUG_LOCK_READER this.WriteErrorLog("Return 开始为读者加写锁 1 '" + strReaderBarcodeParam + "'"); #endif this.ReaderLocks.LockForWrite(strReaderBarcodeParam); bReaderLocked = true; strOutputReaderBarcodeParam = strReaderBarcode; } string strOutputReaderXml = ""; string strOutputItemXml = ""; string strBiblioRecID = ""; string strOutputItemRecPath = ""; string strOutputReaderRecPath = ""; string strLibraryCode = ""; string strInventoryWarning = ""; // 盘点时的警告信息。先存储在其中,等读者记录完全获得后再报错 try // 读者记录锁定范围(可能)开始 { // 2016/1/27 // 读取读者记录 XmlDocument readerdom = null; byte[] reader_timestamp = null; string strOldReaderXml = ""; bool bReaderDbInCirculation = true; if (string.IsNullOrEmpty(strReaderBarcode) == false) { LibraryServerResult result1 = GetReaderRecord( sessioninfo, strActionName, time_lines, strAction != "inventory", ref strReaderBarcode, ref strIdcardNumber, ref strLibraryCode, out bReaderDbInCirculation, out readerdom, out strOutputReaderRecPath, out reader_timestamp); if (result1.Value == 0) { } else { return result1; } // 记忆修改前的读者记录 strOldReaderXml = readerdom.OuterXml; if (String.IsNullOrEmpty(strIdcardNumber) == false || string.IsNullOrEmpty(strReaderBarcode) == true /* 2013/5/23 */) { // 获得读者证条码号 strReaderBarcode = DomUtil.GetElementText(readerdom.DocumentElement, "barcode"); } strOutputReaderBarcodeParam = DomUtil.GetElementText(readerdom.DocumentElement, "barcode"); string strReaderDbName = ResPath.GetDbName(strOutputReaderRecPath); // 检查当前用户管辖的读者范围 // return: // -1 出错 // 0 允许继续访问 // 1 权限限制,不允许继续访问。strError 中有说明原因的文字 nRet = CheckReaderRange(sessioninfo, readerdom, strReaderDbName, out strError); if (nRet == -1) goto ERROR1; if (nRet == 1) { // strError = "当前用户 '" + sessioninfo.UserID + "' 的存取权限或好友关系禁止操作读者(证条码号为 " + strReaderBarcode + ")。具体原因:" + strError; result.Value = -1; result.ErrorInfo = strError; result.ErrorCode = ErrorCode.AccessDenied; return result; } } // List<string> aPath = null; string strItemXml = ""; byte[] item_timestamp = null; // *** 获得册记录 *** bool bItemBarcodeDup = false; // 是否发生册条码号重复情况 string strDupBarcodeList = ""; // 用于最后返回ErrorInfo的重复册条码号列表 // 册记录可能加锁 // 如果读者记录此时已经加锁, 就为册记录加锁 if (bReaderLocked == true) { this.EntityLocks.LockForWrite(strItemBarcodeParam); bEntityLocked = true; } try // 册记录锁定范围开始 { WriteTimeUsed( time_lines, start_time, "Return() 中前期检查和锁定 耗时 "); DateTime start_time_read_item = DateTime.Now; // 如果已经有确定的册记录路径 if (String.IsNullOrEmpty(strConfirmItemRecPath) == false) { // 检查路径中的库名,是不是实体库名 // return: // -1 error // 0 不是实体库名 // 1 是实体库名 nRet = this.CheckItemRecPath(strConfirmItemRecPath, out strError); if (nRet == -1) goto ERROR1; if (nRet == 0) { strError = strConfirmItemRecPath + strError; goto ERROR1; } string strMetaData = ""; lRet = channel.GetRes(strConfirmItemRecPath, out strItemXml, out strMetaData, out item_timestamp, out strOutputItemRecPath, out strError); if (lRet == -1) { strError = "根据strConfirmItemRecPath '" + strConfirmItemRecPath + "' 获得册记录失败: " + strError; goto ERROR1; } } else if (IsBiblioRecPath(strItemBarcodeParam) == false) { // 从册条码号获得册记录 // 获得册记录 // return: // -1 error // 0 not found // 1 命中1条 // >1 命中多于1条 nRet = this.GetItemRecXml( // sessioninfo.Channels, channel, strItemBarcodeParam, "first", // 在若干实体库中顺次检索,命中一个以上则返回,不再继续检索更多 out strItemXml, 100, out aPath, out item_timestamp, out strError); if (nRet == 0) { result.Value = -1; result.ErrorInfo = "册条码号 '" + strItemBarcodeParam + "' 不存在"; result.ErrorCode = ErrorCode.ItemBarcodeNotFound; return result; } if (nRet == -1) { strError = "读入册记录时发生错误: " + strError; goto ERROR1; } if (aPath.Count > 1) { if (this.Statis != null) this.Statis.IncreaseEntryValue( strLibraryCode, "出纳", "还书遇册条码号重复次数", 1); bItemBarcodeDup = true; // 此时已经需要设置状态。虽然后面可以进一步识别出真正的册记录 // 构造strDupBarcodeList /* string[] pathlist = new string[aPath.Count]; aPath.CopyTo(pathlist); strDupBarcodeList = String.Join(",", pathlist); * */ strDupBarcodeList = StringUtil.MakePathList(aPath); List<string> aFoundPath = null; List<byte[]> aTimestamp = null; List<string> aItemXml = null; if (String.IsNullOrEmpty(strReaderBarcodeParam) == true) { if (this.Statis != null) this.Statis.IncreaseEntryValue( strLibraryCode, "出纳", "还书遇册条码号重复并无读者证条码号辅助判断次数", 1); // 如果没有给出读者证条码号参数 result.Value = -1; result.ErrorInfo = "册条码号为 '" + strItemBarcodeParam + "' 册记录有 " + aPath.Count.ToString() + " 条,无法进行还书操作。请在附加册记录路径后重新提交还书操作。"; result.ErrorCode = ErrorCode.ItemBarcodeDup; aDupPath = new string[aPath.Count]; aPath.CopyTo(aDupPath); return result; } // 从若干重复条码号的册记录中,选出其中符合当前读者证条码号的 // return: // -1 出错 // 其他 选出的数量 nRet = FindItem( channel, strReaderBarcode, aPath, true, // 优化 out aFoundPath, out aItemXml, out aTimestamp, out strError); if (nRet == -1) { strError = "选择重复条码号的册记录时发生错误: " + strError; goto ERROR1; } if (nRet == 0) { result.Value = -1; result.ErrorInfo = "册条码号 '" + strItemBarcodeParam + "' 检索出的 " + aPath.Count + " 条记录中,没有任何一条其<borrower>元素表明了被读者 '" + strReaderBarcode + "' 借阅。"; result.ErrorCode = ErrorCode.ItemBarcodeNotFound; return result; } if (nRet > 1) { if (this.Statis != null) this.Statis.IncreaseEntryValue( strLibraryCode, "出纳", "借书遇册条码号重复并读者证条码号也无法去重次数", 1); result.Value = -1; result.ErrorInfo = "册条码号为 '" + strItemBarcodeParam + "' 并且<borrower>元素表明为读者 '" + strReaderBarcode + "' 借阅的册记录有 " + aFoundPath.Count.ToString() + " 条,无法进行还书操作。请在附加册记录路径后重新提交还书操作。"; result.ErrorCode = ErrorCode.ItemBarcodeDup; this.WriteErrorLog(result.ErrorInfo); // 2012/12/30 aDupPath = new string[aFoundPath.Count]; aFoundPath.CopyTo(aDupPath); return result; } Debug.Assert(nRet == 1, ""); if (this.Statis != null) this.Statis.IncreaseEntryValue(strLibraryCode, "出纳", "借书遇册条码号重复但根据读者证条码号成功去重次数", 1); this.WriteErrorLog("借书遇册条码号 '" + strItemBarcodeParam + "' 重复但根据读者证条码号 '" + strReaderBarcode + "' 成功去重"); // 2012/12/30 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]; // strItemXml已经有册记录了 } } // 函数返回后有用 aDupPath = new string[1]; aDupPath[0] = strOutputItemRecPath; } // 看看册记录所从属的数据库,是否在参与流通的实体库之列 // 2008/6/4 string strItemDbName = ""; bool bItemDbInCirculation = true; if (strAction != "inventory") { if (String.IsNullOrEmpty(strOutputItemRecPath) == false) { strItemDbName = ResPath.GetDbName(strOutputItemRecPath); if (this.IsItemDbName(strItemDbName, out bItemDbInCirculation) == false) { strError = "册记录路径 '" + strOutputItemRecPath + "' 中的数据库名 '" + strItemDbName + "' 居然不在定义的实体库之列。"; goto ERROR1; } } } // 检查存取权限 string strAccessParameters = ""; { // 检查存取权限 if (String.IsNullOrEmpty(sessioninfo.Access) == false) { string strAccessActionList = ""; strAccessActionList = GetDbOperRights(sessioninfo.Access, strItemDbName, "circulation"); #if NO if (String.IsNullOrEmpty(strAccessActionList) == true && result_save != null) { // TODO: 也可以直接返回 result_save strError = "当前用户 '" + sessioninfo.UserID + "' 不具备 针对数据库 '" + strItemDbName + "' 执行 出纳 操作的存取权限"; result.Value = -1; result.ErrorInfo = strError; result.ErrorCode = ErrorCode.AccessDenied; return result; } #endif if (strAccessActionList == null) { strAccessActionList = GetDbOperRights(sessioninfo.Access, "", // 此时还不知道实体库名,先取得当前帐户关于任意一个实体库的存取定义 "circulation"); if (strAccessActionList == null) { // 对所有实体库都没有定义任何存取权限,这时候要退而使用普通权限 strAccessActionList = sessioninfo.Rights; // 注:其实此时 result_save == null 即表明普通权限检查已经通过了的 } else { // 对其他实体库定义了存取权限,但对 strItemDbName 没有定义 strError = "用户 '" + sessioninfo.UserID + "' 不具备 针对数据库 '" + strItemDbName + "' 执行 出纳 操作的存取权限"; result.Value = -1; result.ErrorInfo = strError; result.ErrorCode = ErrorCode.AccessDenied; return result; } } if (strAccessActionList == "*") { // 通配 } else { if (IsInAccessList(strAction, strAccessActionList, out strAccessParameters) == false) { strError = "用户 '" + sessioninfo.UserID + "' 不具备 针对数据库 '" + strItemDbName + "' 执行 出纳 " + strActionName + " 操作的存取权限"; result.Value = -1; result.ErrorInfo = strError; result.ErrorCode = ErrorCode.AccessDenied; return result; } } } } XmlDocument itemdom = null; if (string.IsNullOrEmpty(strItemXml) == false) { nRet = LibraryApplication.LoadToDom(strItemXml, out itemdom, out strError); if (nRet == -1) { strError = "装载册记录进入 XML DOM 时发生错误: " + strError; goto ERROR1; } } WriteTimeUsed( time_lines, start_time_read_item, "Return() 中读取册记录 耗时 "); DateTime start_time_lock = DateTime.Now; // 检查评估模式下书目记录路径 if (this.TestMode == true || sessioninfo.TestMode == true) { string strBiblioDbName = ""; // 根据实体库名, 找到对应的书目库名 // return: // -1 出错 // 0 没有找到 // 1 找到 nRet = this.GetBiblioDbNameByItemDbName(strItemDbName, out strBiblioDbName, out strError); if (nRet == -1) { strError = "根据实体库名 '" + strItemDbName + "' 获得书目库名时出错: " + strError; goto ERROR1; } string strParentID = DomUtil.GetElementText(itemdom.DocumentElement, "parent"); // 检查评估模式 // return: // -1 检查过程出错 // 0 可以通过 // 1 不允许通过 nRet = CheckTestModePath(strBiblioDbName + "/" + strParentID, out strError); if (nRet != 0) { strError = strActionName + "操作被拒绝: " + strError; goto ERROR1; } } string strOutputReaderBarcode = ""; // 返回的借阅者证条码号 if (strAction != "read") { // 在册记录中获得借阅者证条码号 // return: // -1 出错 // 0 该册为未借出状态 // 1 成功 nRet = GetBorrowerBarcode(itemdom, out strOutputReaderBarcode, out strError); if (strAction == "inventory") { if (nRet == -1) { strError = strError + " (册记录路径为 '" + strOutputItemRecPath + "')"; goto ERROR1; } if (string.IsNullOrEmpty(strOutputReaderBarcode) == false) { // 该册处于被借阅状态,需要警告前端,建议立即进行还书操作 strInventoryWarning = "册 " + strItemBarcodeParam + " 当前处于被借阅状态。如确属在架已还图书,建议立即为之补办还书手续。" + " (册记录路径为 '" + strOutputItemRecPath + "')"; } } else { if (nRet == -1 || nRet == 0) { strError = strError + " (册记录路径为 '" + strOutputItemRecPath + "')"; goto ERROR1; } } // 如果提供了读者证条码号,则需要核实 if (String.IsNullOrEmpty(strReaderBarcodeParam) == false) { if (strOutputReaderBarcode != strReaderBarcodeParam) { #if NO if (StringUtil.IsIdcardNumber(strReaderBarcodeParam) == true) { // 暂时不报错,滞后验证 bDelayVerifyReaderBarcode = true; strIdcardNumber = strReaderBarcodeParam; } else { strError = "册记录表明,册 " + strItemBarcode + " 实际被读者 " + strOutputReaderBarcode + " 所借阅,而不是您当前输入的读者(证条码号) " + strReaderBarcodeParam + "。还书操作被放弃。"; goto ERROR1; } #endif // 暂时不报错,滞后验证 bDelayVerifyReaderBarcode = true; strIdcardNumber = strReaderBarcodeParam; } } if (String.IsNullOrEmpty(strReaderBarcode) == true) strReaderBarcode = strOutputReaderBarcode; } // *** 如果读者记录在前面没有锁定, 在这里锁定 if (bReaderLocked == false && string.IsNullOrEmpty(strReaderBarcode) == false) { // 加读者记录锁 strLockReaderBarcode = strReaderBarcode; #if DEBUG_LOCK_READER this.WriteErrorLog("Return 开始为读者加写锁 2 '" + strLockReaderBarcode + "'"); #endif this.ReaderLocks.LockForWrite(strLockReaderBarcode); bReaderLocked = true; strOutputReaderBarcodeParam = strReaderBarcode; } // *** 如果册记录在前面没有锁定,则在这里锁定 if (bEntityLocked == false) { this.EntityLocks.LockForWrite(strItemBarcodeParam); bEntityLocked = true; // 因为前面对于册记录一直没有加锁,所以这里锁定后要 // 检查时间戳,确保记录内容没有(实质性)改变 byte[] temp_timestamp = null; string strTempOutputPath = ""; string strTempItemXml = ""; string strMetaData = ""; lRet = channel.GetRes( strOutputItemRecPath, out strTempItemXml, out strMetaData, out temp_timestamp, out strTempOutputPath, out strError); if (lRet == -1) { strError = "册条码号(滞后)加锁后重新提取册记录 '" + strOutputItemRecPath + "' 时发生错误: " + strError; goto ERROR1; } // 如果时间戳发生过改变 if (ByteArray.Compare(item_timestamp, temp_timestamp) != 0) { // 装载新记录进入DOM XmlDocument temp_itemdom = null; nRet = LibraryApplication.LoadToDom(strTempItemXml, out temp_itemdom, out strError); if (nRet == -1) { strError = "装载册记录strTempItemXml 路径'" + strOutputItemRecPath + "' 进入XML DOM时发生错误: " + strError; goto ERROR1; } // 检查新旧册记录有无要害性改变? if (IsItemRecordSignificantChanged(itemdom, temp_itemdom) == true) { // 则只好重做 nRedoCount++; if (nRedoCount > 10) { strError = "Return() 册条码号(滞后)加锁后重新提取册记录的时候,遇到时间戳冲突,并因此重试超过 10 次未能成功, 只好放弃..."; this.WriteErrorLog(strError); goto ERROR1; } /* // 如果重做超过5次,则索性修改读者证条码号参数,让它具有(经检索提取的)确定的值,这样就不会滞后加锁了 if (nRedoCount > 5) strReaderBarcodeParam = strReaderBarcode; * */ #if DEBUG_LOCK_READER this.WriteErrorLog("Return goto REDO_RETURN 1 nRedoCount=" + nRedoCount + ""); #endif goto REDO_RETURN; } // 如果没有要害性改变,就刷新相关参数,然后继续向后进行 itemdom = temp_itemdom; item_timestamp = temp_timestamp; strItemXml = strTempItemXml; } // 如果时间戳没有发生过改变,则不必刷新任何参数 } WriteTimeUsed( time_lines, start_time_lock, "Return() 中补充锁定 耗时 "); // 读入读者记录 DateTime start_time_read_reader = DateTime.Now; if (readerdom == null && string.IsNullOrEmpty(strReaderBarcode) == false) { LibraryServerResult result1 = GetReaderRecord( sessioninfo, strActionName, time_lines, strAction != "inventory", ref strReaderBarcode, ref strIdcardNumber, ref strLibraryCode, out bReaderDbInCirculation, out readerdom, out strOutputReaderRecPath, out reader_timestamp); if (result1.Value == 0) { } else { return result1; } // 记忆修改前的读者记录 strOldReaderXml = readerdom.OuterXml; if (String.IsNullOrEmpty(strIdcardNumber) == false || string.IsNullOrEmpty(strReaderBarcode) == true /* 2013/5/23 */) { // 获得读者证条码号 strReaderBarcode = DomUtil.GetElementText(readerdom.DocumentElement, "barcode"); } strOutputReaderBarcodeParam = DomUtil.GetElementText(readerdom.DocumentElement, "barcode"); string strReaderDbName = ResPath.GetDbName(strOutputReaderRecPath); // 检查当前用户管辖的读者范围 // return: // -1 出错 // 0 允许继续访问 // 1 权限限制,不允许继续访问。strError 中有说明原因的文字 nRet = CheckReaderRange(sessioninfo, readerdom, strReaderDbName, out strError); if (nRet == -1) goto ERROR1; if (nRet == 1) { // strError = "当前用户 '" + sessioninfo.UserID + "' 的存取权限或好友关系禁止操作读者(证条码号为 " + strReaderBarcode + ")。具体原因:" + strError; result.Value = -1; result.ErrorInfo = strError; result.ErrorCode = ErrorCode.AccessDenied; return result; } } #if NO string strReaderXml = ""; byte[] reader_timestamp = null; if (string.IsNullOrEmpty(strReaderBarcode) == false) { nRet = this.TryGetReaderRecXml( // sessioninfo.Channels, channel, strReaderBarcode, sessioninfo.LibraryCodeList, // TODO: 测试个人书斋情况 out strReaderXml, out strOutputReaderRecPath, out reader_timestamp, out strError); if (nRet == 0) { // 如果是身份证号,则试探检索“身份证号”途径 if (StringUtil.IsIdcardNumber(strReaderBarcode) == true) { strIdcardNumber = strReaderBarcode; strReaderBarcode = ""; // 通过特定检索途径获得读者记录 // return: // -1 error // 0 not found // 1 命中1条 // >1 命中多于1条 nRet = this.GetReaderRecXmlByFrom( // sessioninfo.Channels, channel, strIdcardNumber, "身份证号", out strReaderXml, out strOutputReaderRecPath, out reader_timestamp, out strError); if (nRet == -1) { // text-level: 内部错误 strError = "用身份证号 '" + strIdcardNumber + "' 读入读者记录时发生错误: " + strError; goto ERROR1; } if (nRet == 0) { result.Value = -1; // text-level: 用户提示 result.ErrorInfo = string.Format(this.GetString("身份证号s不存在"), // "身份证号 '{0}' 不存在" strIdcardNumber); result.ErrorCode = ErrorCode.IdcardNumberNotFound; return result; } if (nRet > 1) { // text-level: 用户提示 result.Value = -1; result.ErrorInfo = "用身份证号 '" + strIdcardNumber + "' 检索读者记录命中 " + nRet.ToString() + " 条,因此无法用身份证号来进行借还操作。请改用证条码号来进行借还操作。"; result.ErrorCode = ErrorCode.IdcardNumberDup; return result; } Debug.Assert(nRet == 1, ""); goto SKIP0; } else { // 2013/5/24 // 如果需要,从读者证号等辅助途径进行检索 foreach (string strFrom in this.PatronAdditionalFroms) { nRet = this.GetReaderRecXmlByFrom( // sessioninfo.Channels, channel, null, strReaderBarcode, strFrom, out strReaderXml, out strOutputReaderRecPath, out reader_timestamp, out strError); if (nRet == -1) { // text-level: 内部错误 strError = "用" + strFrom + " '" + strReaderBarcode + "' 读入读者记录时发生错误: " + strError; goto ERROR1; } if (nRet == 0) continue; if (nRet > 1) { // text-level: 用户提示 result.Value = -1; result.ErrorInfo = "用" + strFrom + " '" + strReaderBarcode + "' 检索读者记录命中 " + nRet.ToString() + " 条,因此无法用" + strFrom + "来进行借还操作。请改用证条码号来进行借还操作。"; result.ErrorCode = ErrorCode.IdcardNumberDup; return result; } Debug.Assert(nRet == 1, ""); strIdcardNumber = ""; strReaderBarcode = ""; goto SKIP0; } } result.Value = -1; result.ErrorInfo = "读者证条码号 '" + strReaderBarcode + "' 不存在"; result.ErrorCode = ErrorCode.ReaderBarcodeNotFound; return result; } if (nRet == -1) { strError = "读入读者记录时发生错误: " + strError; goto ERROR1; } // 2008/6/17 if (nRet > 1) { strError = "读入读者记录时,发现读者证条码号 '" + strReaderBarcode + "' 命中 " + nRet.ToString() + " 条,这是一个严重错误,请系统管理员尽快处理。"; goto ERROR1; } } #endif SKIP0: if (strAction == "inventory") { nRet = DoInventory( sessioninfo, strAccessParameters, itemdom, strOutputItemRecPath, strBatchNo, out strError); if (nRet == -1) goto ERROR1; strOutputItemXml = itemdom.OuterXml; strOutputReaderXml = strOldReaderXml; // strReaderXml; nRet = RemovePassword(ref strOutputReaderXml, out strError); if (nRet == -1) { strError = "从读者记录中去除 password 阶段出错: " + strError; goto ERROR1; } strBiblioRecID = DomUtil.GetElementText(itemdom.DocumentElement, "parent"); // SetReturnInfo(ref return_info, itemdom); goto END3; } #if NO // 看看读者记录所从属的数据库,是否在参与流通的读者库之列 // 2008/6/4 bool bReaderDbInCirculation = true; string strReaderDbName = ""; if (strAction != "inventory" && String.IsNullOrEmpty(strOutputReaderRecPath) == false) { if (this.TestMode == true || sessioninfo.TestMode == true) { // 检查评估模式 // return: // -1 检查过程出错 // 0 可以通过 // 1 不允许通过 nRet = CheckTestModePath(strOutputReaderRecPath, out strError); if (nRet != 0) { strError = strActionName + "操作被拒绝: " + strError; goto ERROR1; } } strReaderDbName = ResPath.GetDbName(strOutputReaderRecPath); if (this.IsReaderDbName(strReaderDbName, out bReaderDbInCirculation, out strLibraryCode) == false) { strError = "读者记录路径 '" + strOutputReaderRecPath + "' 中的数据库名 '" + strReaderDbName + "' 居然不在定义的读者库之列。"; goto ERROR1; } } // TODO: 即便不是参与流通的数据库,也让还书? // 检查当前操作者是否管辖这个读者库 // 观察一个读者记录路径,看看是不是在当前用户管辖的读者库范围内? if (strAction != "inventory" && this.IsCurrentChangeableReaderPath(strOutputReaderRecPath, sessioninfo.LibraryCodeList) == false) { strError = "读者记录路径 '" + strOutputReaderRecPath + "' 的读者库不在当前用户管辖范围内"; goto ERROR1; } XmlDocument readerdom = null; nRet = LibraryApplication.LoadToDom(strReaderXml, out readerdom, out strError); if (nRet == -1) { strError = "装载读者记录进入XML DOM时发生错误: " + strError; goto ERROR1; } WriteTimeUsed( time_lines, start_time_read_reader, "Return() 中读取读者记录 耗时 "); // string strReaderDbName = ResPath.GetDbName(strOutputReaderRecPath); // 观察读者记录是否在操作范围内 // return: // -1 出错 // 0 允许继续访问 // 1 权限限制,不允许继续访问。strError 中有说明原因的文字 nRet = CheckReaderRange(sessioninfo, readerdom, strReaderDbName, out strError); if (nRet == -1) goto ERROR1; if (nRet == 1) { // strError = "当前用户 '" + sessioninfo.UserID + "' 的存取权限禁止操作读者(证条码号为 " + strReaderBarcode + ")。具体原因:" + strError; result.Value = -1; result.ErrorInfo = strError; result.ErrorCode = ErrorCode.AccessDenied; return result; } #endif DateTime start_time_process = DateTime.Now; string strReaderName = readerdom == null ? "" : DomUtil.GetElementText(readerdom.DocumentElement, "name"); if (bDelayVerifyReaderBarcode == true) { // 顺便验证一下身份证号 if (string.IsNullOrEmpty(strIdcardNumber) == false) { Debug.Assert(string.IsNullOrEmpty(strIdcardNumber) == false, ""); string strTempIdcardNumber = DomUtil.GetElementText(readerdom.DocumentElement, "idCardNumber"); if (strIdcardNumber != strTempIdcardNumber) { strError = "册记录表明,册 " + strItemBarcodeParam + " 实际被读者(证条码号) " + strOutputReaderBarcode + " 所借阅,此读者的身份证号为 " + strTempIdcardNumber + ",不是您当前输入的(验证用)身份证号 " + strIdcardNumber + "。还书操作被放弃。"; goto ERROR1; } } // 重新获取读者证条码号 strReaderBarcode = DomUtil.GetElementText(readerdom.DocumentElement, "barcode"); strOutputReaderBarcodeParam = strReaderBarcode; // 为了返回值 { if (strOutputReaderBarcode != strReaderBarcode) { strError = "册记录表明,册 " + strItemBarcodeParam + " 实际被读者 " + strOutputReaderBarcode + " 所借阅,而不是您当前指定的读者(证条码号) " + strReaderBarcodeParam + "。还书操作被放弃。"; goto ERROR1; } } } XmlDocument domOperLog = new XmlDocument(); domOperLog.LoadXml("<root />"); DomUtil.SetElementText(domOperLog.DocumentElement, "libraryCode", strLibraryCode); // 读者所在的馆代码 DomUtil.SetElementText(domOperLog.DocumentElement, "operation", "return"); DomUtil.SetElementText(domOperLog.DocumentElement, "action", strAction); // 从读者信息中, 找到读者类型 string strReaderType = DomUtil.GetElementText(readerdom.DocumentElement, "readerType"); // 证状态 2009/1/29 string strReaderState = DomUtil.GetElementText(readerdom.DocumentElement, "state"); string strOperTime = this.Clock.GetClock(); string strWarning = ""; // 处理册记录 string strOverdueString = ""; string strLostComment = ""; if (strAction != "read") { // 获得相关日历 Calendar calendar = null; // return: // -1 出错 // 0 没有找到日历 // 1 找到日历 nRet = GetReaderCalendar(strReaderType, strLibraryCode, out calendar, out strError); if (nRet == -1 || nRet == 0) goto ERROR1; // return: // -1 出错 // 0 正常 // 1 超期还书或者丢失处理的情况 nRet = DoReturnItemXml( strAction, sessioninfo, // sessioninfo.Account, calendar, strReaderType, strLibraryCode, strAccessParameters, readerdom, // 为了调用GetLost()脚本函数 ref itemdom, bForce, bItemBarcodeDup, // 若条码号足以定位,则不记载实体记录路径 strOutputItemRecPath, sessioninfo.UserID, // 还书操作者 strOperTime, domOperLog, out strOverdueString, out strLostComment, out return_info, out strWarning, out strError); if (nRet == -1) goto ERROR1; if (string.IsNullOrEmpty(strWarning) == false) { if (String.IsNullOrEmpty(result.ErrorInfo) == false) result.ErrorInfo += "\r\n"; result.ErrorInfo += strWarning; result.Value = 1; } } else nRet = 0; string strItemBarcode = ""; if (itemdom != null) { strItemBarcode = DomUtil.GetElementText(itemdom.DocumentElement, "barcode"); // 创建日志记录 DomUtil.SetElementText(domOperLog.DocumentElement, "itemBarcode", string.IsNullOrEmpty(strItemBarcode) == false ? strItemBarcode : strItemBarcodeParam); /* 后面会写入<overdues> if (nRet == 1) { // 如果有超期和或丢失处理信息 DomUtil.SetElementText(domOperLog.DocumentElement, "overdueString", strOverdueString); } * */ } bool bOverdue = false; string strOverdueInfo = ""; if (nRet == 1) { bOverdue = true; strOverdueInfo = strError; } // 处理读者记录 // string strNewReaderXml = ""; string strDeletedBorrowFrag = ""; if (strAction != "read") { nRet = DoReturnReaderXml( strLibraryCode, ref readerdom, strItemBarcodeParam, strItemBarcode, strOverdueString.StartsWith("!") ? "" : strOverdueString, sessioninfo.UserID, // 还书操作者 strOperTime, sessioninfo.ClientAddress, // 前端触发 out strDeletedBorrowFrag, out strError); if (nRet == -1) goto ERROR1; } // 创建日志记录 Debug.Assert(string.IsNullOrEmpty(strReaderBarcode) == false, ""); DomUtil.SetElementText(domOperLog.DocumentElement, "readerBarcode", strReaderBarcode); DomUtil.SetElementText(domOperLog.DocumentElement, "operator", sessioninfo.UserID); DomUtil.SetElementText(domOperLog.DocumentElement, "operTime", strOperTime); if (strAction == "read") { string strBiblioRecPath = ""; string strVolume = ""; if (IsBiblioRecPath(strItemBarcodeParam) == false) { strBiblioRecID = DomUtil.GetElementText(itemdom.DocumentElement, "parent"); // string strBiblioDbName = ""; // 根据实体库名, 找到对应的书目库名 // return: // -1 出错 // 0 没有找到 // 1 找到 nRet = this.GetBiblioDbNameByItemDbName(strItemDbName, out strBiblioDbName, out strError); if (nRet == -1) goto ERROR1; if (string.IsNullOrEmpty(strBiblioDbName) == false) { strBiblioRecPath = strBiblioDbName + "/" + strBiblioRecID; DomUtil.SetElementText(domOperLog.DocumentElement, "biblioRecPath", strBiblioRecPath); } strVolume = DomUtil.GetElementText(itemdom.DocumentElement, "volume"); if (string.IsNullOrEmpty(strVolume) == false) DomUtil.SetElementText(domOperLog.DocumentElement, "no", strVolume); } else strBiblioRecPath = strItemBarcodeParam.Substring("@biblioRecPath:".Length); // 探测 mongodb 库中是否已经存在这样的事项 IEnumerable<ChargingOperItem> collection = this.ChargingOperDatabase.Exists( strReaderBarcode, "", // string.IsNullOrEmpty(strItemBarcode) == false ? strItemBarcode : strItemBarcodeParam, strBiblioRecPath, strVolume, new DateTime(0), // DateTime.Now - new TimeSpan(0, 5, 0), new DateTime(0), "read"); if (collection != null) { DateTime existingOperTime = new DateTime(0); foreach (ChargingOperItem item in collection) { existingOperTime = item.OperTime; break; } if (existingOperTime != new DateTime(0)) { strError = "读者 '" + strReaderBarcode + "' 早先 (" + existingOperTime.ToString("G") + ") 已经读过 [" + GetReadCaption(strBiblioRecPath, strVolume) + "] 了,本次操作被拒绝"; goto ERROR1; } } DomUtil.SetElementText(domOperLog.DocumentElement, "biblioRecPath", strBiblioRecPath); goto WRITE_OPERLOG; } WriteTimeUsed( time_lines, start_time_process, "Return() 中进行各种数据处理 耗时 "); // 原来创建输出xml或html格式的代码在此 DateTime start_time_reservation_check = DateTime.Now; if (StringUtil.IsInList("simulate_reservation_arrive", strStyle)) { // 模拟预约情况 nRet = SimulateReservation( ref readerdom, ref itemdom, out strError); if (nRet == -1) goto ERROR1; } // 察看本册预约情况, 并进行初步处理 // 如果为丢失处理,需要通知等待者,书已经丢失了,不用再等待 // return: // -1 error // 0 没有修改 // 1 进行过修改 nRet = DoItemReturnReservationCheck( (strAction == "lost") ? true : false, ref itemdom, out strReservationReaderBarcode, out strError); if (nRet == -1) goto ERROR1; if (nRet == 1 && return_info != null) { // <location>元素中可能增加了 #reservation 部分 return_info.Location = DomUtil.GetElementText(itemdom.DocumentElement, "location"); } WriteTimeUsed( time_lines, start_time_reservation_check, "Return() 中进行预约检查 耗时 "); // 写回读者、册记录 // byte[] timestamp = null; byte[] output_timestamp = null; string strOutputPath = ""; /* Channel channel = sessioninfo.Channels.GetChannel(this.WsUrl); if (channel == null) { strError = "get channel error"; goto ERROR1; } * */ DateTime start_time_write_reader = DateTime.Now; lRet = channel.DoSaveTextRes(strOutputReaderRecPath, readerdom.OuterXml, false, "content", // ,ignorechecktimestamp reader_timestamp, out output_timestamp, out strOutputPath, out strError); if (lRet == -1) { // 2015/9/2 this.WriteErrorLog("Return() 写入读者记录 '" + strOutputReaderRecPath + "' 时出错: " + strError); if (channel.ErrorCode == ChannelErrorCode.TimestampMismatch) { nRedoCount++; if (nRedoCount > 10) { strError = "Return() 写回读者记录的时候,遇到时间戳冲突,并因此重试超过 10 次未能成功, 只好放弃重试..."; this.WriteErrorLog(strError); goto ERROR1; } #if DEBUG_LOCK_READER this.WriteErrorLog("Return goto REDO_RETURN 2 nRedoCount=" + nRedoCount + ""); #endif goto REDO_RETURN; } goto ERROR1; } reader_timestamp = output_timestamp; WriteTimeUsed( time_lines, start_time_write_reader, "Return() 中写回读者记录 耗时 "); DateTime start_time_write_item = DateTime.Now; lRet = channel.DoSaveTextRes(strOutputItemRecPath, itemdom.OuterXml, false, "content,ignorechecktimestamp", item_timestamp, out output_timestamp, out strOutputPath, out strError); if (lRet == -1) { // 2015/9/2 this.WriteErrorLog("Return() 写入册记录 '" + strOutputItemRecPath + "' 时出错: " + strError); // 要Undo刚才对读者记录的写入 string strError1 = ""; lRet = channel.DoSaveTextRes(strOutputReaderRecPath, strOldReaderXml, // strReaderXml, false, "content,ignorechecktimestamp", reader_timestamp, out output_timestamp, out strOutputPath, out strError1); if (lRet == -1) { // 2015/9/2 this.WriteErrorLog("Return() 写入读者记录 '" + strOutputReaderRecPath + "' 时出错: " + strError); if (channel.ErrorCode == ChannelErrorCode.TimestampMismatch) { // 读者记录Undo的时候, 发现时间戳冲突了 // 这时需要读出现存记录, 试图增加回刚删除的<borrows><borrow>元素 // return: // -1 error // 0 没有必要Undo // 1 Undo成功 nRet = UndoReturnReaderRecord( channel, strOutputReaderRecPath, strReaderBarcode, strItemBarcodeParam, strDeletedBorrowFrag, strOverdueString.StartsWith("!") ? "" : strOverdueString, out strError); if (nRet == -1) { strError = "Return() Undo读者记录 '" + strOutputReaderRecPath + "' (读者证条码号为 '" + strReaderBarcode + "' 读者姓名为 '" + strReaderName + "') 还书册条码号 '" + strItemBarcodeParam + "' 的修改时,发生错误,无法Undo: " + strError; this.WriteErrorLog(strError); goto ERROR1; } // 成功 // 2015/9/2 增加下列防止死循环的语句 nRedoCount++; if (nRedoCount > 10) { strError = "Return() Undo 读者记录(1)成功,试图重试 Return 时,发现先前重试已经超过 10 次,只好不重试了,做出错返回..."; this.WriteErrorLog(strError); goto ERROR1; } #if DEBUG_LOCK_READER this.WriteErrorLog("Return goto REDO_RETURN 3 nRedoCount=" + nRedoCount + ""); #endif goto REDO_RETURN; } // 以下为 不是时间戳冲突的其他错误情形 strError = "Return() Undo读者记录 '" + strOutputReaderRecPath + "' (读者证条码号为 '" + strReaderBarcode + "' 读者姓名为 '" + strReaderName + "') 还书册条码号 '" + strItemBarcodeParam + "' 的修改时,发生错误,无法Undo: " + strError; // strError = strError + ", 并且Undo写回旧读者记录也失败: " + strError1; this.WriteErrorLog(strError); goto ERROR1; } // 以下为Undo成功的情形 // 2015/9/2 增加下列防止死循环的语句 nRedoCount++; if (nRedoCount > 10) { strError = "Return() Undo 读者记录(2)成功,试图重试 Return 时,发现先前重试已经超过 10 次,只好不重试了,做出错返回..."; this.WriteErrorLog(strError); goto ERROR1; } #if DEBUG_LOCK_READER this.WriteErrorLog("Return goto REDO_RETURN 4 nRedoCount=" + nRedoCount + ""); #endif goto REDO_RETURN; } WriteTimeUsed( time_lines, start_time_write_item, "Return() 中写回册记录 耗时 "); WRITE_OPERLOG: DateTime start_time_write_operlog = DateTime.Now; // 写入日志 // overdue信息 if (String.IsNullOrEmpty(strOverdueString) == false) { DomUtil.SetElementText(domOperLog.DocumentElement, "overdues", strOverdueString.StartsWith("!") ? strOverdueString.Substring(1) : strOverdueString); } // 确认册路径 if (string.IsNullOrEmpty(strConfirmItemRecPath) == false) { DomUtil.SetElementText(domOperLog.DocumentElement, "confirmItemRecPath", strConfirmItemRecPath); } if (string.IsNullOrEmpty(strIdcardNumber) == false) { // 表明是使用身份证号来完成还书操作的 DomUtil.SetElementText(domOperLog.DocumentElement, "idcardNumber", strIdcardNumber); } // 写入读者记录 XmlNode node = DomUtil.SetElementText(domOperLog.DocumentElement, "readerRecord", readerdom.OuterXml); DomUtil.SetAttr(node, "recPath", strOutputReaderRecPath); // 写入册记录 if (itemdom != null) { node = DomUtil.SetElementText(domOperLog.DocumentElement, "itemRecord", itemdom.OuterXml); DomUtil.SetAttr(node, "recPath", strOutputItemRecPath); } if (strLostComment != "") { DomUtil.SetElementText(domOperLog.DocumentElement, "lostComment", strLostComment); } nRet = this.OperLog.WriteOperLog(domOperLog, sessioninfo.ClientAddress, start_time, out strOperLogUID, out strError); if (nRet == -1) { strError = "Return() API 写入日志时发生错误: " + strError; goto ERROR1; } WriteTimeUsed( time_lines, start_time_write_operlog, "Return() 中写操作日志 耗时 "); DateTime start_time_write_statis = DateTime.Now; // 写入统计指标 #if NO if (this.m_strLastReaderBarcode != strReaderBarcode) { if (this.Statis != null) this.Statis.IncreaseEntryValue(strLibraryCode, "出纳", "读者数", 1); this.m_strLastReaderBarcode = strReaderBarcode; } #endif if (this.Garden != null) this.Garden.Activate(strReaderBarcode, strLibraryCode); if (this.Statis != null) this.Statis.IncreaseEntryValue(strLibraryCode, "出纳", strAction == "read" ? "读过册" : "还册", 1); if (strAction == "lost") { if (this.Statis != null) this.Statis.IncreaseEntryValue(strLibraryCode, "出纳", "声明丢失", 1); } WriteTimeUsed( time_lines, start_time_write_statis, "Return() 中写统计指标 耗时 "); result.ErrorInfo = strActionName + "操作成功。" + result.ErrorInfo; // 2013/11/13 if (bReaderDbInCirculation == false) { if (String.IsNullOrEmpty(result.ErrorInfo) == false) result.ErrorInfo += "\r\n"; result.ErrorInfo += "读者证条码号 '" + strReaderBarcode + "' 所在的读者记录 '" + strOutputReaderRecPath + "' 其数据库 '" + StringUtil.GetDbName(strOutputReaderRecPath) + "' 属于未参与流通的读者库。"; result.Value = 1; } if (bItemDbInCirculation == false) { if (String.IsNullOrEmpty(result.ErrorInfo) == false) result.ErrorInfo += "\r\n"; result.ErrorInfo += "册条码号 '" + strItemBarcodeParam + "' 所在的册记录 '" + strOutputItemRecPath + "' 其数据库 '" + StringUtil.GetDbName(strOutputReaderRecPath) + "' 属于未参与流通的实体库。"; result.Value = 1; } if (bOverdue == true) { if (this.Statis != null) this.Statis.IncreaseEntryValue(strLibraryCode, "出纳", "还超期册", 1); if (String.IsNullOrEmpty(result.ErrorInfo) == false) result.ErrorInfo += "\r\n"; result.ErrorInfo += strOverdueInfo; result.ErrorCode = ErrorCode.Overdue; result.Value = 1; } if (bItemBarcodeDup == true) { if (String.IsNullOrEmpty(result.ErrorInfo) == false) result.ErrorInfo += "\r\n"; result.ErrorInfo += "***警告***: " + strActionName + "操作过程中发现下列册记录它们的册条码号发生了重复: " + strDupBarcodeList + "。请通知系统管理员纠正此数据错误。"; result.Value = 1; } if (String.IsNullOrEmpty(strReservationReaderBarcode) == false // 2009/10/19 changed //bFoundReservation == true && strAction != "lost") { // 为了提示信息中出现读者姓名,这里特以获取读者姓名 string strReservationReaderName = ""; if (strReaderBarcode == strReservationReaderBarcode) strReservationReaderName = strReaderName; else { DateTime start_time_getname = DateTime.Now; // 获得读者姓名 // return: // -1 error // 0 not found // 1 found nRet = GetReaderName( sessioninfo, strReservationReaderBarcode, out strReservationReaderName, out strError); WriteTimeUsed( time_lines, start_time_getname, "Return() 中获得预约者的姓名 耗时 "); } if (String.IsNullOrEmpty(result.ErrorInfo) == false) result.ErrorInfo += "\r\n"; result.ErrorInfo += "因本册图书已被读者 " + strReservationReaderBarcode + " " + strReservationReaderName + " 预约,请放入预约保留架。"; // 2009/10/10 changed result.Value = 1; } // 读者证状态不为空情况下的提示 // 2008/1/29 if (String.IsNullOrEmpty(strReaderState) == false) { if (String.IsNullOrEmpty(result.ErrorInfo) == false) result.ErrorInfo += "\r\n"; result.ErrorInfo += "***警告***: 当前读者证状态为: " + strReaderState + "。请注意进行后续处理。"; result.Value = 1; } if (itemdom != null) strOutputItemXml = itemdom.OuterXml; // strOutputReaderXml 将用于构造读者记录返回格式 DomUtil.DeleteElement(readerdom.DocumentElement, "password"); strOutputReaderXml = readerdom.OuterXml; if (itemdom != null) { strBiblioRecID = DomUtil.GetElementText(itemdom.DocumentElement, "parent"); // } } // 册记录锁定范围结束 finally { // 册记录解锁 if (bEntityLocked == true) this.EntityLocks.UnlockForWrite(strItemBarcodeParam); } } // 读者记录锁定范围结束 finally { if (bReaderLocked == true) { this.ReaderLocks.UnlockForWrite(strLockReaderBarcode); #if DEBUG_LOCK_READER this.WriteErrorLog("Return 结束为读者加写锁 '" + strLockReaderBarcode + "'"); #endif } } // TODO: 将来可以改进为,丢失时发现有人预约,也通知,不过通知的内容是要读者不再等待了。 if (String.IsNullOrEmpty(strReservationReaderBarcode) == false && strAction != "lost") { DateTime start_time_1 = DateTime.Now; List<string> DeletedNotifyRecPaths = null; // 被删除的通知记录。不用。 // 通知预约到书的操作 // 出于对读者库加锁方面的便利考虑, 单独做了此函数 // return: // -1 error // 0 没有找到<request>元素 nRet = DoReservationNotify( null, channel, strReservationReaderBarcode, true, // 需要函数内加锁 strItemBarcodeParam, false, // 不在大架 false, // 不需要再修改当前册记录,因为前面已经修改过了 out DeletedNotifyRecPaths, out strError); if (nRet == -1) { strError = "还书操作已经成功, 但是预约到书通知功能失败, 原因: " + strError; goto ERROR1; } WriteTimeUsed( time_lines, start_time_1, "Return() 中预约到书通知 耗时 "); /* 前面已经通知过了 result.Value = 1; result.ErrorCode = ErrorCode.ReturnReservation; if (result.ErrorInfo != "") result.ErrorInfo += "\r\n"; result.ErrorInfo += "还书操作成功。因此册图书被读者 " + strReservationReaderBarcode + " 预约,请放入预约保留架。"; * */ // 最好超期和保留两种状态码可以并存? } END3: // 输出数据 // 把输出数据部分放在读者锁以外范围,是为了尽量减少锁定的时间,提高并发运行效率 DateTime output_start_time = DateTime.Now; if (String.IsNullOrEmpty(strOutputReaderXml) == false && StringUtil.IsInList("reader", strStyle) == true) { DateTime start_time_1 = DateTime.Now; nRet = BuildReaderResults( sessioninfo, null, strOutputReaderXml, strReaderFormatList, strLibraryCode, // calendar/advancexml/html 时需要 null, // recpaths 时需要 strOutputReaderRecPath, // recpaths 时需要 null, // timestamp 时需要 OperType.Return, null, strItemBarcodeParam, ref reader_records, out strError); if (nRet == -1) { strError = "虽然出现了下列错误,但是还书操作已经成功: " + strError; goto ERROR1; } WriteTimeUsed( time_lines, start_time_1, "Return() 中返回读者记录(" + strReaderFormatList + ") 耗时 "); } #if NO if (String.IsNullOrEmpty(strOutputReaderXml) == false && StringUtil.IsInList("reader", strStyle) == true) { DateTime start_time_1 = DateTime.Now; string[] reader_formats = strReaderFormatList.Split(new char[] { ',' }); reader_records = new string[reader_formats.Length]; for (int i = 0; i < reader_formats.Length; i++) { string strReaderFormat = reader_formats[i]; // 将读者记录数据从XML格式转换为HTML格式 // if (String.Compare(strReaderFormat, "html", true) == 0) if (IsResultType(strReaderFormat, "html") == true) { string strReaderRecord = ""; nRet = this.ConvertReaderXmlToHtml( sessioninfo, this.CfgDir + "\\readerxml2html.cs", this.CfgDir + "\\readerxml2html.cs.ref", strLibraryCode, strOutputReaderXml, strOutputReaderRecPath, // 2009/10/18 OperType.Return, null, strItemBarcodeParam, strReaderFormat, out strReaderRecord, out strError); if (nRet == -1) { strError = "虽然出现了下列错误,但是还书操作已经成功: " + strError; goto ERROR1; } reader_records[i] = strReaderRecord; } // 将读者记录数据从XML格式转换为text格式 // else if (String.Compare(strReaderFormat, "text", true) == 0) else if (IsResultType(strReaderFormat, "text") == true) { string strReaderRecord = ""; nRet = this.ConvertReaderXmlToHtml( sessioninfo, this.CfgDir + "\\readerxml2text.cs", this.CfgDir + "\\readerxml2text.cs.ref", strLibraryCode, strOutputReaderXml, strOutputReaderRecPath, // 2009/10/18 OperType.Return, null, strItemBarcodeParam, strReaderFormat, out strReaderRecord, out strError); if (nRet == -1) { strError = "虽然出现了下列错误,但是还书操作已经成功: " + strError; goto ERROR1; } reader_records[i] = strReaderRecord; } // else if (String.Compare(strReaderFormat, "xml", true) == 0) else if (IsResultType(strReaderFormat, "xml") == true) { // reader_records[i] = strOutputReaderXml; string strResultXml = ""; nRet = GetItemXml(strOutputReaderXml, strReaderFormat, out strResultXml, out strError); if (nRet == -1) { strError = "虽然出现了下列错误,但是还书操作已经成功: " + strError; goto ERROR1; } reader_records[i] = strResultXml; } else if (IsResultType(strReaderFormat, "summary") == true) { // 2013/12/15 XmlDocument dom = new XmlDocument(); try { dom.LoadXml(strOutputReaderXml); } catch (Exception ex) { strError = "读者 XML 装入 DOM 出错: " + ex.Message; strError = "虽然出现了下列错误,但是还书操作已经成功: " + strError; goto ERROR1; } reader_records[i] = DomUtil.GetElementText(dom.DocumentElement, "name"); } else { strError = "strReaderFormatList参数出现了不支持的数据格式类型 '" + strReaderFormat + "'"; strError = "虽然出现了下列错误,但是还书操作已经成功: " + strError; goto ERROR1; } } // end of for WriteTimeUsed( time_lines, start_time_1, "Return() 中返回读者记录(" + strReaderFormatList + ") 耗时 "); } // end if #endif // 2008/5/9 if (String.IsNullOrEmpty(strOutputItemXml) == false && StringUtil.IsInList("item", strStyle) == true) { DateTime start_time_1 = DateTime.Now; string[] item_formats = strItemFormatList.Split(new char[] { ',' }); item_records = new string[item_formats.Length]; for (int i = 0; i < item_formats.Length; i++) { string strItemFormat = item_formats[i]; // 将册记录数据从XML格式转换为HTML格式 // if (String.Compare(strItemFormat, "html", true) == 0) if (IsResultType(strItemFormat, "html") == true) { string strItemRecord = ""; nRet = this.ConvertItemXmlToHtml( this.CfgDir + "\\itemxml2html.cs", this.CfgDir + "\\itemxml2html.cs.ref", strOutputItemXml, strOutputItemRecPath, // 2009/10/18 out strItemRecord, out strError); if (nRet == -1) { strError = "虽然出现了下列错误,但是还书操作已经成功: " + strError; goto ERROR1; } item_records[i] = strItemRecord; } // 将册记录数据从XML格式转换为text格式 // else if (String.Compare(strItemFormat, "text", true) == 0) else if (IsResultType(strItemFormat, "text") == true) { string strItemRecord = ""; nRet = this.ConvertItemXmlToHtml( this.CfgDir + "\\itemxml2text.cs", this.CfgDir + "\\itemxml2text.cs.ref", strOutputItemXml, strOutputItemRecPath, // 2009/10/18 out strItemRecord, out strError); if (nRet == -1) { strError = "虽然出现了下列错误,但是还书操作已经成功: " + strError; goto ERROR1; } item_records[i] = strItemRecord; } // else if (String.Compare(strItemFormat, "xml", true) == 0) else if (IsResultType(strItemFormat, "xml") == true) { // item_records[i] = strOutputItemXml; string strResultXml = ""; nRet = GetItemXml(strOutputItemXml, strItemFormat, out strResultXml, out strError); if (nRet == -1) { strError = "虽然出现了下列错误,但是还书操作已经成功: " + strError; goto ERROR1; } item_records[i] = strResultXml; } else { strError = "strItemFormatList参数出现了不支持的数据格式类型 '" + strItemFormat + "'"; strError = "虽然出现了下列错误,但是还书操作已经成功: " + strError; goto ERROR1; } } // end of for WriteTimeUsed( time_lines, start_time_1, "Return() 中返回册记录(" + strItemFormatList + ") 耗时 "); } // 2008/5/9 if (StringUtil.IsInList("biblio", strStyle) == true) { DateTime start_time_1 = DateTime.Now; string strBiblioRecPath = ""; if (IsBiblioRecPath(strItemBarcodeParam) == true) strBiblioRecPath = strItemBarcodeParam.Substring("@biblioRecPath:".Length); if (string.IsNullOrEmpty(strBiblioRecPath) == true) { if (String.IsNullOrEmpty(strBiblioRecID) == true) { strError = "册记录XML中<parent>元素缺乏或者值为空, 因此无法定位种记录ID"; strError = "虽然出现了下列错误,但是还书操作已经成功: " + strError; goto ERROR1; } string strItemDbName = ResPath.GetDbName(strOutputItemRecPath); string strBiblioDbName = ""; // 根据实体库名, 找到对应的书目库名 // return: // -1 出错 // 0 没有找到 // 1 找到 nRet = this.GetBiblioDbNameByItemDbName(strItemDbName, out strBiblioDbName, out strError); if (nRet == -1) goto ERROR1; if (nRet == 0) { strError = "实体库名 '" + strItemDbName + "' 没有找到对应的书目库名"; strError = "虽然出现了下列错误,但是还书操作已经成功: " + strError; goto ERROR1; } strBiblioRecPath = strBiblioDbName + "/" + strBiblioRecID; } string[] biblio_formats = strBiblioFormatList.Split(new char[] { ',' }); biblio_records = new string[biblio_formats.Length]; string strBiblioXml = ""; // 至少有html xml text之一,才获取strBiblioXml if (StringUtil.IsInList("html", strBiblioFormatList) == true || StringUtil.IsInList("xml", strBiblioFormatList) == true || StringUtil.IsInList("text", strBiblioFormatList) == true) { #if NO RmsChannel channel = sessioninfo.Channels.GetChannel(this.WsUrl); if (channel == null) { strError = "get channel error"; strError = "虽然出现了下列错误,但是还书操作已经成功: " + strError; goto ERROR1; } #endif string strMetaData = ""; byte[] timestamp = null; string strTempOutputPath = ""; lRet = channel.GetRes(strBiblioRecPath, out strBiblioXml, out strMetaData, out timestamp, out strTempOutputPath, out strError); if (lRet == -1) { strError = "获得种记录 '" + strBiblioRecPath + "' 时出错: " + strError; strError = "虽然出现了下列错误,但是还书操作已经成功: " + strError; goto ERROR1; } } for (int i = 0; i < biblio_formats.Length; i++) { string strBiblioFormat = biblio_formats[i]; // 需要从内核映射过来文件 string strLocalPath = ""; string strBiblio = ""; // 将书目记录数据从XML格式转换为HTML格式 if (String.Compare(strBiblioFormat, "html", true) == 0) { // TODO: 可以cache nRet = this.MapKernelScriptFile( sessioninfo, StringUtil.GetDbName(strBiblioRecPath), "./cfgs/loan_biblio.fltx", out strLocalPath, out strError); if (nRet == -1) { strError = "虽然出现了下列错误,但是还书操作已经成功: " + strError; goto ERROR1; } // 将种记录数据从XML格式转换为HTML格式 string strFilterFileName = strLocalPath; // app.CfgDir + "\\biblio.fltx"; if (string.IsNullOrEmpty(strBiblioXml) == false) { nRet = this.ConvertBiblioXmlToHtml( strFilterFileName, strBiblioXml, null, strBiblioRecPath, out strBiblio, out strError); if (nRet == -1) { strError = "虽然出现了下列错误,但是还书操作已经成功: " + strError; goto ERROR1; } } else strBiblio = ""; biblio_records[i] = strBiblio; } // 将册记录数据从XML格式转换为text格式 else if (String.Compare(strBiblioFormat, "text", true) == 0) { // TODO: 可以cache nRet = this.MapKernelScriptFile( sessioninfo, StringUtil.GetDbName(strBiblioRecPath), "./cfgs/loan_biblio_text.fltx", out strLocalPath, out strError); if (nRet == -1) { strError = "虽然出现了下列错误,但是还书操作已经成功: " + strError; goto ERROR1; } // 将种记录数据从XML格式转换为TEXT格式 string strFilterFileName = strLocalPath; // app.CfgDir + "\\biblio.fltx"; if (string.IsNullOrEmpty(strBiblioXml) == false) { nRet = this.ConvertBiblioXmlToHtml( strFilterFileName, strBiblioXml, null, strBiblioRecPath, out strBiblio, out strError); if (nRet == -1) { strError = "虽然出现了下列错误,但是还书操作已经成功: " + strError; goto ERROR1; } } else strBiblio = ""; biblio_records[i] = strBiblio; } else if (String.Compare(strBiblioFormat, "xml", true) == 0) { biblio_records[i] = strBiblioXml; } else if (String.Compare(strBiblioFormat, "recpath", true) == 0) { biblio_records[i] = strBiblioRecPath; } else if (string.IsNullOrEmpty(strBiblioFormat) == true) { biblio_records[i] = ""; } else { strError = "strBiblioFormatList参数出现了不支持的数据格式类型 '" + strBiblioFormat + "'"; strError = "虽然出现了下列错误,但是还书操作已经成功: " + strError; goto ERROR1; } } // end of for WriteTimeUsed( time_lines, start_time_1, "Return() 中返回书目记录(" + strBiblioFormatList + ") 耗时 "); } this.WriteTimeUsed( time_lines, start_time, "Return() 耗时 "); // 如果整个时间超过一秒,则需要计入操作日志 if (DateTime.Now - start_time > new TimeSpan(0, 0, 1)) { WriteLongTimeOperLog( sessioninfo, strAction, start_time, "整个操作耗时超过 1 秒。详情:" + StringUtil.MakePathList(time_lines, ";"), strOperLogUID, out strError); } if (string.IsNullOrEmpty(strInventoryWarning) == false) { result.ErrorInfo = strInventoryWarning; result.ErrorCode = ErrorCode.Borrowing; result.Value = 1; } // 如果创建输出数据的时间超过一秒,则需要计入操作日志 if (DateTime.Now - output_start_time > new TimeSpan(0, 0, 1)) { WriteLongTimeOperLog( sessioninfo, strAction, output_start_time, "output 阶段耗时超过 1 秒", strOperLogUID, out strError); } // result.Value值在前面可能被设置成1 return result; ERROR1: result.Value = -1; result.ErrorInfo = strError; result.ErrorCode = ErrorCode.SystemError; return result; }
LibraryServerResult GetReaderRecord( SessionInfo sessioninfo, string strActionName, List<string> time_lines, bool bVerifyReaderRecPath, ref string strReaderBarcode, // 2015/1/4 加上 ref ref string strIdcardNumber, ref string strLibraryCode, out bool bReaderDbInCirculation, out XmlDocument readerdom, out string strOutputReaderRecPath, out byte[] reader_timestamp) { string strError = ""; int nRet = 0; bReaderDbInCirculation = true; LibraryServerResult result = new LibraryServerResult(); strOutputReaderRecPath = ""; readerdom = null; reader_timestamp = null; DateTime start_time_read_reader = DateTime.Now; RmsChannel channel = sessioninfo.Channels.GetChannel(this.WsUrl); if (channel == null) { strError = "get channel error"; goto ERROR1; } // 读入读者记录 string strReaderXml = ""; nRet = this.TryGetReaderRecXml( // sessioninfo.Channels, channel, strReaderBarcode, sessioninfo.LibraryCodeList, // TODO: 对个人书斋情况要测试一下 out strReaderXml, out strOutputReaderRecPath, out reader_timestamp, out strError); if (nRet == -1) { // text-level: 内部错误 strError = "读入读者记录时发生错误: " + strError; goto ERROR1; } if (nRet == 0) { // 如果是身份证号,则试探检索“身份证号”途径 if (StringUtil.IsIdcardNumber(strReaderBarcode) == true) { strIdcardNumber = strReaderBarcode; strReaderBarcode = ""; // 迫使函数返回后,重新获得 reader barcode // 通过特定检索途径获得读者记录 // return: // -1 error // 0 not found // 1 命中1条 // >1 命中多于1条 nRet = this.GetReaderRecXmlByFrom( // sessioninfo.Channels, channel, strIdcardNumber, "身份证号", out strReaderXml, out strOutputReaderRecPath, out reader_timestamp, out strError); if (nRet == -1) { // text-level: 内部错误 strError = "用身份证号 '" + strIdcardNumber + "' 读入读者记录时发生错误: " + strError; goto ERROR1; } if (nRet == 0) { result.Value = -1; // text-level: 用户提示 result.ErrorInfo = string.Format(this.GetString("身份证号s不存在"), // "身份证号 '{0}' 不存在" strIdcardNumber); result.ErrorCode = ErrorCode.IdcardNumberNotFound; return result; } if (nRet > 1) { // text-level: 用户提示 result.Value = -1; result.ErrorInfo = "用身份证号 '" + strIdcardNumber + "' 检索读者记录命中 " + nRet.ToString() + " 条,因此无法用身份证号来进行借还操作。请改用证条码号来进行借还操作。"; result.ErrorCode = ErrorCode.IdcardNumberDup; return result; } Debug.Assert(nRet == 1, ""); goto SKIP0; } else { // 2013/5/24 // 如果需要,从读者证号等辅助途径进行检索 foreach (string strFrom in this.PatronAdditionalFroms) { nRet = this.GetReaderRecXmlByFrom( // sessioninfo.Channels, channel, null, strReaderBarcode, strFrom, out strReaderXml, out strOutputReaderRecPath, out reader_timestamp, out strError); if (nRet == -1) { // text-level: 内部错误 strError = "用" + strFrom + " '" + strReaderBarcode + "' 读入读者记录时发生错误: " + strError; goto ERROR1; } if (nRet == 0) continue; if (nRet > 1) { // text-level: 用户提示 result.Value = -1; result.ErrorInfo = "用" + strFrom + " '" + strReaderBarcode + "' 检索读者记录命中 " + nRet.ToString() + " 条,因此无法用" + strFrom + "来进行借还操作。请改用证条码号来进行借还操作。"; result.ErrorCode = ErrorCode.IdcardNumberDup; return result; } strReaderBarcode = ""; #if NO result.ErrorInfo = strError; result.Value = nRet; #endif goto SKIP0; } } result.Value = -1; // text-level: 用户提示 result.ErrorInfo = string.Format(this.GetString("读者证条码号s不存在"), // "读者证条码号 '{0}' 不存在" strReaderBarcode); // "读者证条码号 '" + strReaderBarcode + "' 不存在"; result.ErrorCode = ErrorCode.ReaderBarcodeNotFound; return result; } // 2008/6/17 if (nRet > 1) { // text-level: 内部错误 strError = "读入读者记录时,发现读者证条码号 '" + strReaderBarcode + "' 命中 " + nRet.ToString() + " 条,这是一个严重错误,请系统管理员尽快处理。"; goto ERROR1; } SKIP0: // 看看读者记录所从属的数据库,是否在参与流通的读者库之列 // 2008/6/4 if (bVerifyReaderRecPath == true && String.IsNullOrEmpty(strOutputReaderRecPath) == false) { if (this.TestMode == true || sessioninfo.TestMode == true) { // 检查评估模式 // return: // -1 检查过程出错 // 0 可以通过 // 1 不允许通过 nRet = CheckTestModePath(strOutputReaderRecPath, out strError); if (nRet != 0) { strError = strActionName + "操作被拒绝: " + strError; goto ERROR1; } } string strReaderDbName = ResPath.GetDbName(strOutputReaderRecPath); // bool bReaderDbInCirculation = true; if (this.IsReaderDbName(strReaderDbName, out bReaderDbInCirculation, out strLibraryCode) == false) { // text-level: 内部错误 strError = "读者记录路径 '" + strOutputReaderRecPath + "' 中的数据库名 '" + strReaderDbName + "' 居然不在定义的读者库之列。"; goto ERROR1; } #if NO if (bReaderDbInCirculation == false) { // text-level: 用户提示 strError = string.Format(this.GetString("借书操作被拒绝。读者证条码号s所在的读者记录s因其数据库s属于未参与流通的读者库"), // "借书操作被拒绝。读者证条码号 '{0}' 所在的读者记录 '{1}' 因其数据库 '{2}' 属于未参与流通的读者库" strReaderBarcode, strOutputReaderRecPath, strReaderDbName); // "借书操作被拒绝。读者证条码号 '" + strReaderBarcode + "' 所在的读者记录 '" +strOutputReaderRecPath + "' 因其数据库 '" +strReaderDbName+ "' 属于未参与流通的读者库"; goto ERROR1; } #endif // 检查当前操作者是否管辖这个读者库 // 观察一个读者记录路径,看看是不是在当前用户管辖的读者库范围内? if (this.IsCurrentChangeableReaderPath(strOutputReaderRecPath, sessioninfo.LibraryCodeList) == false) { strError = "读者记录路径 '" + strOutputReaderRecPath + "' 的读者库不在当前用户管辖范围内"; goto ERROR1; } } nRet = LibraryApplication.LoadToDom(strReaderXml, out readerdom, out strError); if (nRet == -1) { // text-level: 内部错误 strError = "装载读者记录进入 XML DOM 时发生错误: " + strError; goto ERROR1; } WriteTimeUsed( time_lines, start_time_read_reader, strActionName + " 中读取读者记录 耗时 "); return result; ERROR1: result.Value = -1; result.ErrorInfo = strError; result.ErrorCode = ErrorCode.SystemError; return result; }
// 结算 public LibraryServerResult Settlement( SessionInfo sessioninfo, string strAction, string[] ids) { string strError = ""; int nRet = 0; LibraryServerResult result = new LibraryServerResult(); strAction = strAction.ToLower(); if (strAction == "settlement") { // 权限判断 if (StringUtil.IsInList("settlement", sessioninfo.RightsOrigin) == false) { result.Value = -1; result.ErrorInfo = "结算操作被拒绝。不具备 settlement 权限。"; result.ErrorCode = ErrorCode.AccessDenied; return result; } } else if (strAction == "undosettlement") { // 权限判断 if (StringUtil.IsInList("undosettlement", sessioninfo.RightsOrigin) == false) { result.Value = -1; result.ErrorInfo = "撤销结算的操作被拒绝。不具备 undosettlement 权限。"; result.ErrorCode = ErrorCode.AccessDenied; return result; } } else if (strAction == "delete") { // 权限判断 if (StringUtil.IsInList("deletesettlement", sessioninfo.RightsOrigin) == false) { result.Value = -1; result.ErrorInfo = "删除结算记录的操作被拒绝。不具备 deletesettlement 权限。"; result.ErrorCode = ErrorCode.AccessDenied; return result; } } else { strError = "无法识别的strAction参数值 '" + strAction + "'"; goto ERROR1; } string strOperTime = this.Clock.GetClock(); string strOperator = sessioninfo.UserID; // string strText = ""; string strCount = ""; strCount = "<maxCount>100</maxCount>"; for (int i = 0; i < ids.Length; i++) { string strID = ids[i]; if (i != 0) { strText += "<operator value='OR' />"; } strText += "<item><word>" + StringUtil.GetXmlStringSimple(strID) + "</word>" + strCount + "<match>exact</match><relation>=</relation><dataType>string</dataType>" + "</item>"; } string strQueryXml = "<target list='" + StringUtil.GetXmlStringSimple(this.AmerceDbName + ":" + "ID") // 2007/9/14 + "'>" + strText + "<lang>zh</lang></target>"; string strIds = String.Join(",", ids); RmsChannel channel = sessioninfo.Channels.GetChannel(this.WsUrl); if (channel == null) { strError = "get channel error"; goto ERROR1; } long lRet = channel.DoSearch(strQueryXml, "amerced", "", // strOuputStyle out strError); if (lRet == -1) { strError = "检索ID为 '" + strIds + "' 的违约金记录出错: " + strError; goto ERROR1; } if (lRet == 0) { strError = "没有找到id为 '" + strIds + "' 的违约金记录"; goto ERROR1; } long lHitCount = lRet; long lStart = 0; long lPerCount = Math.Min(50, lHitCount); List<string> aPath = null; // 获得结果集,对逐个记录进行处理 for (; ; ) { lRet = channel.DoGetSearchResult( "amerced", // strResultSetName lStart, lPerCount, "zh", null, // stop out aPath, out strError); if (lRet == -1) goto ERROR1; if (lRet == 0) { strError = "未命中"; break; // ?? } // TODO: 要判断 aPath.Count == 0 跳出循环。否则容易进入死循环 // 处理浏览结果 for (int i = 0; i < aPath.Count; i++) { string strPath = aPath[i]; string strCurrentError = ""; // 结算一个交费记录 nRet = SettlementOneRecord( sessioninfo.LibraryCodeList, true, // 要创建日志 channel, strAction, strPath, strOperTime, strOperator, sessioninfo.ClientAddress, out strCurrentError); // 遇到一般出错应当继续处理 if (nRet == -1) { strError += strAction + "违约金记录 '" + strPath + "' 时发生错误: " + strCurrentError + "\r\n"; } // 但是遇到日志空间满这样的错误就不能继续处理了 if (nRet == -2) { strError = strCurrentError; goto ERROR1; } } lStart += aPath.Count; if (lStart >= lHitCount || lPerCount <= 0) break; } if (strError != "") goto ERROR1; result.Value = 1; return result; ERROR1: result.Value = -1; result.ErrorCode = ErrorCode.SystemError; result.ErrorInfo = strError; return result; }
// 修改编目记录 // parameters: // strAction 动作。为"new" "change" "delete" "onlydeletebiblio" "onlydeletesubrecord"之一。"delete"在删除书目记录的同时,会自动删除下属的实体记录。不过要求实体均未被借出才能删除。 // strBiblioType 目前只允许xml一种 // baTimestamp 时间戳。如果为新创建记录,可以为null // strOutputBiblioRecPath 输出的书目记录路径。当strBiblioRecPath中末级为问号,表示追加保存书目记录的时候,本参数返回实际保存的书目记录路径 // baOutputTimestamp 操作完成后,新的时间戳 public LibraryServerResult SetBiblioInfo( SessionInfo sessioninfo, string strAction, string strBiblioRecPath, string strBiblioType, string strBiblio, byte[] baTimestamp, string strComment, out string strOutputBiblioRecPath, out byte[] baOutputTimestamp) { string strError = ""; long lRet = 0; int nRet = 0; strOutputBiblioRecPath = ""; baOutputTimestamp = null; LibraryServerResult result = new LibraryServerResult(); bool bChangePartDenied = false; // 修改操作部分被拒绝 string strDeniedComment = ""; // 关于部分字段被拒绝的注释 string strLibraryCode = ""; // 图书馆代码 if (sessioninfo.Account != null) strLibraryCode = sessioninfo.Account.AccountLibraryCode; // 检查参数 strAction = strAction.ToLower(); bool bSimulate = false; // 2015/6/8 if (StringUtil.HasHead(strAction, "simulate_") == true) { strAction = strAction.Substring("simulate_".Length); bSimulate = true; } if (strAction != "new" && strAction != "change" && strAction != "delete" && strAction != "onlydeletebiblio" && strAction != "onlydeletesubrecord") { strError = "strAction参数值应当为new change delete onlydeletebiblio onlydeletesubrecord之一"; goto ERROR1; } strBiblioType = strBiblioType.ToLower(); if (strBiblioType != "xml") { strError = "strBiblioType必须为\"xml\""; goto ERROR1; } { if (this.TestMode == true || sessioninfo.TestMode == true) { // 检查评估模式 // return: // -1 检查过程出错 // 0 可以通过 // 1 不允许通过 nRet = CheckTestModePath(strBiblioRecPath, out strError); if (nRet != 0) { strError = "修改书目记录的操作被拒绝: " + strError; goto ERROR1; } } } string strUnionCatalogStyle = ""; string strBiblioDbName = ""; bool bRightVerified = false; bool bOwnerOnly = false; string strAccessParameters = ""; // 检查数据库路径,看看是不是已经正规定义的编目库? if (String.IsNullOrEmpty(strBiblioRecPath) == false) { strBiblioDbName = ResPath.GetDbName(strBiblioRecPath); if (this.IsBiblioDbName(strBiblioDbName) == false) { strError = "书目记录路径 '" + strBiblioRecPath + "' 中包含的数据库名 '" + strBiblioDbName + "' 不是合法的书目库名"; goto ERROR1; } #if NO if (this.TestMode == true || sessioninfo.TestMode == true) { string strID = ResPath.GetRecordId(strBiblioRecPath); if (StringUtil.IsPureNumber(strID) == true) { long v = 0; long.TryParse(strID, out v); if (v > 1000) { strError = "评估模式下只能修改 ID 小于等于 1000 的书目记录"; goto ERROR1; } } } #endif ItemDbCfg cfg = null; cfg = GetBiblioDbCfg(strBiblioDbName); Debug.Assert(cfg != null, ""); strUnionCatalogStyle = cfg.UnionCatalogStyle; // 检查存取权限 if (String.IsNullOrEmpty(sessioninfo.Access) == false) { string strAccessActionList = ""; // return: // null 指定的操作类型的权限没有定义 // "" 定义了指定类型的操作权限,但是否定的定义 // 其它 权限列表。* 表示通配的权限列表 strAccessActionList = GetDbOperRights(sessioninfo.Access, strBiblioDbName, "setbiblioinfo"); if (strAccessActionList == null) { // 看看是不是关于 setbiblioinfo 的任何权限都没有定义? strAccessActionList = GetDbOperRights(sessioninfo.Access, "", "setbiblioinfo"); if (strAccessActionList == null) { // 2013/4/18 // TODO: 可以提示"既没有... 也没有 ..." goto CHECK_RIGHTS_2; } else { strError = "当前用户 '" + sessioninfo.UserID + "' 不具备 针对数据库 '" + strBiblioDbName + "' 执行 setbiblioinfo " + strAction + " 操作的存取权限"; result.Value = -1; result.ErrorInfo = strError; result.ErrorCode = ErrorCode.AccessDenied; return result; } } if (strAccessActionList == "*") { // 通配 } else { if (strAction == "delete" && IsInAccessList("ownerdelete", strAccessActionList, out strAccessParameters) == true) { bOwnerOnly = true; } else if (strAction == "change" && IsInAccessList("ownerchange", strAccessActionList, out strAccessParameters) == true) { bOwnerOnly = true; } else if (strAction == "onlydeletebiblio" && IsInAccessList("owneronlydeletebiblio", strAccessActionList, out strAccessParameters) == true) { bOwnerOnly = true; } else if (strAction == "onlydeletesubrecord" && IsInAccessList("owneronlydeletesubrecord", strAccessActionList, out strAccessParameters) == true) { bOwnerOnly = true; } else if (IsInAccessList(strAction, strAccessActionList, out strAccessParameters) == false) { strError = "当前用户 '" + sessioninfo.UserID + "' 不具备 针对数据库 '" + strBiblioDbName + "' 执行 setbiblioinfo " + strAction + " 操作的存取权限"; result.Value = -1; result.ErrorInfo = strError; result.ErrorCode = ErrorCode.AccessDenied; return result; } } bRightVerified = true; } } CHECK_RIGHTS_2: if (bRightVerified == false) { // 权限字符串 if (StringUtil.IsInList("setbiblioinfo", sessioninfo.RightsOrigin) == false && StringUtil.IsInList("order", sessioninfo.RightsOrigin) == false) { result.Value = -1; result.ErrorInfo = "设置书目信息被拒绝。不具备order或setbiblioinfo权限。"; result.ErrorCode = ErrorCode.AccessDenied; return result; } } // TODO: 需要额外的检查,看看所保存的数据MARC格式是不是这个数据库要求的格式? RmsChannel channel = sessioninfo.Channels.GetChannel(this.WsUrl); if (channel == null) { strError = "channel == null"; goto ERROR1; } // 准备日志DOM XmlDocument domOperLog = new XmlDocument(); domOperLog.LoadXml("<root />"); // 操作不涉及到读者库,所以没有<libraryCode>元素 DomUtil.SetElementText(domOperLog.DocumentElement, "operation", "setBiblioInfo"); DomUtil.SetElementText(domOperLog.DocumentElement, "action", strAction); if (string.IsNullOrEmpty(strComment) == false) { DomUtil.SetElementText(domOperLog.DocumentElement, "comment", strComment); } string strOperTime = this.Clock.GetClock(); string strExistingXml = ""; byte[] exist_timestamp = null; if (strAction == "change" || strAction == "delete" || strAction == "onlydeletebiblio" || strAction == "onlydeletesubrecord") { string strMetaData = ""; string strOutputPath = ""; // 先读出数据库中此位置的已有记录 lRet = channel.GetRes(strBiblioRecPath, out strExistingXml, out strMetaData, out exist_timestamp, out strOutputPath, out strError); if (lRet == -1) { if (channel.ErrorCode == ChannelErrorCode.NotFound) { // 2013/3/12 if (strAction == "change" && bSimulate == false) // 模拟操作情况下,不在乎以前这个位置的记录是否存在 { strError = "原有记录 '" + strBiblioRecPath + "' 不存在, 因此 setbiblioinfo " + strAction + " 操作被拒绝 (此时如果要保存新记录,请使用 new 子功能)"; result.Value = -1; result.ErrorInfo = strError; result.ErrorCode = ErrorCode.NotFound; return result; } goto SKIP_MEMO_OLDRECORD; } else { strError = "设置书目信息发生错误, 在读入原有记录阶段:" + strError; goto ERROR1; } } if (strBiblioRecPath != strOutputPath) { strError = "根据路径 '" + strBiblioRecPath + "' 读入原有记录时,发现返回的路径 '" + strOutputPath + "' 和前者不一致"; goto ERROR1; } XmlNode node = DomUtil.SetElementText(domOperLog.DocumentElement, "oldRecord", strExistingXml); DomUtil.SetAttr(node, "recPath", strBiblioRecPath); // 检查书目记录原来的创建者 998$z if (bOwnerOnly) { string strOwner = ""; // 获得书目记录的创建者 // return: // -1 出错 // 0 没有找到 998$z子字段 // 1 找到 nRet = GetBiblioOwner(strExistingXml, out strOwner, out strError); if (nRet == -1) goto ERROR1; if (strOwner != sessioninfo.UserID) { strError = "当前用户 '" + sessioninfo.UserID + "' 不是书目记录 '" + strBiblioDbName + "' 的创建者(998$z),因此 setbiblioinfo " + strAction + " 操作被拒绝"; result.Value = -1; result.ErrorInfo = strError; result.ErrorCode = ErrorCode.AccessDenied; return result; } } // TODO: 如果已存在的XML记录中,MARC根不是文档根,那么表明书目记录 // 还存储有其他信息,这时就需要把前端送来的XML记录和已存在的记录进行合并处理, // 防止贸然覆盖了文档根下的有用信息。 } SKIP_MEMO_OLDRECORD: bool bBiblioNotFound = false; string strRights = ""; if (sessioninfo.Account != null) strRights = sessioninfo.Account.Rights; if (strAction == "new") { // 对order权限的判断。order权限允许对任何库进行new操作 // TODO: 不只是联合编目模块要进行记录预处理。 // 也要结合当前用户是不是具有writeobject权限,进行判断和处理。 // 如果但前用户不具备writeobject权限,则也不应在XML中包含任何<dprms:file>元素(如果包含了,则处理为出错或者警告(这会增加前端的负担)?还是忽略后写入?) { /* // 对strBiblio中内容进行加工,确保905字段符合联合编目要求 // 准备联合编目的新书目库XML记录 // 功能:排除strLibraryCode定义以外的905字段 // return: // -1 error // 0 not delete any fields // 1 deleted some fields nRet = PrepareNewBiblioRec( strLibraryCode, ref strBiblio, out strError); if (nRet == -1) goto ERROR1; * */ strExistingXml = ""; // 合并联合编目的新旧书目库XML记录 // 功能:排除新记录中对strLibraryCode定义以外的905字段的修改 // parameters: // bChangePartDenied 如果本次被设定为 true,则 strError 中返回了关于部分修改的注释信息 // return: // -1 error // 0 not delete any fields // 1 deleted some fields nRet = MergeOldNewBiblioRec( strRights, strUnionCatalogStyle, strLibraryCode, "insert", strAccessParameters, strExistingXml, ref strBiblio, ref bChangePartDenied, out strError); if (nRet == -1) goto ERROR1; if (bChangePartDenied == true && string.IsNullOrEmpty(strError) == false) strDeniedComment += " " + strError; } // 2009/11/2 // 需要判断路径最后一级是否为问号? string strTargetRecId = ResPath.GetRecordId(strBiblioRecPath); if (strTargetRecId == "?" || String.IsNullOrEmpty(strTargetRecId) == true) { if (String.IsNullOrEmpty(strTargetRecId) == true) strBiblioRecPath += "/?"; } else { /* strError = "当创建书目记录的时候,只能使用“书目库名/?”形式的路径(而不能使用 '"+strBiblioRecPath+"' 形式)。如果要在指定位置保存,可使用修改(change)子功能"; goto ERROR1; * */ } // 2011/11/30 nRet = this.ClearOperation( ref strBiblio, out strError); if (nRet == -1) goto ERROR1; nRet = this.SetOperation( ref strBiblio, "create", sessioninfo.UserID, "", true, 10, out strError); if (nRet == -1) goto ERROR1; if (bSimulate) { // 模拟创建新记录的操作 baOutputTimestamp = null; strOutputBiblioRecPath = strBiblioRecPath; // 路径中 ID 依然为问号,没有被处理 } else { lRet = channel.DoSaveTextRes(strBiblioRecPath, strBiblio, false, "content", // ,ignorechecktimestamp baTimestamp, out baOutputTimestamp, out strOutputBiblioRecPath, out strError); if (lRet == -1) goto ERROR1; if (this.TestMode == true || sessioninfo.TestMode) { string strID = ResPath.GetRecordId(strOutputBiblioRecPath); if (StringUtil.IsPureNumber(strID) == true) { long v = 0; long.TryParse(strID, out v); if (v > 1000) { strError = "评估模式下只能修改 ID 小于等于 1000 的书目记录。本记录 " + strOutputBiblioRecPath + " 虽然创建成功,但以后无法对其进行修改 "; goto ERROR1; } } } } } else if (strAction == "change") { // 只有order权限的情况 if (StringUtil.IsInList("setbiblioinfo", sessioninfo.RightsOrigin) == false && StringUtil.IsInList("order", sessioninfo.RightsOrigin) == true) { // 工作库允许全部操作,非工作库只能追加记录 if (IsOrderWorkBiblioDb(strBiblioDbName) == false) { // 非工作库。要求原来记录不存在 if (String.IsNullOrEmpty(strExistingXml) == false) { strError = "当前帐户只有 order 权限而没有 setbiblioinfo 权限,不能用 change 功能修改已经存在的书目记录 '"+strBiblioRecPath+"'"; goto ERROR1; } } } { // 合并联合编目的新旧书目库XML记录 // 功能:排除新记录中对strLibraryCode定义以外的905字段的修改 // parameters: // bChangePartDenied 如果本次被设定为 true,则 strError 中返回了关于部分修改的注释信息 // return: // -1 error // 0 not delete any fields // 1 deleted some fields nRet = MergeOldNewBiblioRec( strRights, strUnionCatalogStyle, strLibraryCode, "insert,replace,delete", strAccessParameters, strExistingXml, ref strBiblio, ref bChangePartDenied, out strError); if (nRet == -1) goto ERROR1; if (bChangePartDenied == true && string.IsNullOrEmpty(strError) == false) strDeniedComment += " " + strError; } // 2011/11/30 nRet = this.SetOperation( ref strBiblio, "change", sessioninfo.UserID, "", true, 10, out strError); if (nRet == -1) goto ERROR1; if (bSimulate) { // 模拟修改记录的操作 baOutputTimestamp = null; strOutputBiblioRecPath = strBiblioRecPath; } else { // 需要判断路径是否为具备最末一级索引号的形式? this.BiblioLocks.LockForWrite(strBiblioRecPath); try { lRet = channel.DoSaveTextRes(strBiblioRecPath, strBiblio, false, "content", // ,ignorechecktimestamp baTimestamp, out baOutputTimestamp, out strOutputBiblioRecPath, out strError); if (lRet == -1) { if (channel.ErrorCode == ChannelErrorCode.TimestampMismatch) { result.Value = -1; result.ErrorInfo = strError; result.ErrorCode = ErrorCode.TimestampMismatch; return result; } goto ERROR1; } } finally { this.BiblioLocks.UnlockForWrite(strBiblioRecPath); } } } else if (strAction == "delete" || strAction == "onlydeletesubrecord") { // 只有order权限的情况 if (StringUtil.IsInList("setbiblioinfo", sessioninfo.RightsOrigin) == false && StringUtil.IsInList("order", sessioninfo.RightsOrigin) == true) { // 工作库允许全部操作,非工作库不能删除记录 if (IsOrderWorkBiblioDb(strBiblioDbName) == false) { // 非工作库。要求原来记录不存在 strError = "当前帐户只有order权限而没有setbiblioinfo权限,不能用delete功能删除书目记录 '" + strBiblioRecPath + "'"; result.Value = -1; result.ErrorInfo = strError; result.ErrorCode = ErrorCode.AccessDenied; return result; } } if (strAction == "delete") { strBiblio = ""; // 合并联合编目的新旧书目库XML记录 // 功能:排除新记录中对strLibraryCode定义以外的905字段的修改 // parameters: // bChangePartDenied 如果本次被设定为 true,则 strError 中返回了关于部分修改的注释信息 // return: // -1 error // 0 not delete any fields // 1 deleted some fields nRet = MergeOldNewBiblioRec( strRights, strUnionCatalogStyle, strLibraryCode, "delete", strAccessParameters, strExistingXml, ref strBiblio, ref bChangePartDenied, out strError); if (nRet == -1) goto ERROR1; if (bChangePartDenied == true && string.IsNullOrEmpty(strError) == false) strDeniedComment += " " + strError; // 检查根下面是不是没有任何元素了。如果还有,说明当前权限不足以删除它们。 // 如果已经为空,就表示不必检查了 if (String.IsNullOrEmpty(strBiblio) == false) { XmlDocument tempdom = new XmlDocument(); try { tempdom.LoadXml(strBiblio); } catch (Exception ex) { strError = "经过 MergeOldNewBiblioRec() 处理后的 strBiblio 装入 XmlDocument 失败: " + ex.Message; goto ERROR1; } // 2011/11/30 // 删除全部<operations>元素 XmlNodeList nodes = tempdom.DocumentElement.SelectNodes("//operations"); for (int i = 0; i < nodes.Count; i++) { XmlNode node = nodes[i]; if (node.ParentNode != null) node.ParentNode.RemoveChild(node); } if (tempdom.DocumentElement.ChildNodes.Count != 0) { result.Value = -1; result.ErrorInfo = "当前用户的权限不足以删除所有MARC字段,因此删除操作被拒绝。可改用修改操作。"; result.ErrorCode = ErrorCode.AccessDenied; return result; } } } // 注:这里的模拟删除不是太容易模拟。 // 因为真正删除的时候,是根据实际存在的下属记录的类型来检查权限的。也许模拟删除可以根据假定每种下属记录都存在的情况来检查权限。但这样往往是比实际情况要偏严的。如果有其他参数,能指出调用者关注哪些下属记录的类型就好了 if (bSimulate == true) { //strError = "尚未实现对 '"+strAction+"' 的模拟操作"; //goto ERROR1; // 模拟删除记录的操作 baOutputTimestamp = null; strOutputBiblioRecPath = strBiblioRecPath; goto END1; } // 删除书目记录的下级记录 // return: // -1 失败 // 0 成功 // 1 需要结束运行,result 结果已经设置好了 nRet = DeleteBiblioAndSubRecords( sessioninfo, strAction, strBiblioRecPath, strExistingXml, baTimestamp, ref bBiblioNotFound, ref strBiblio, ref baOutputTimestamp, ref domOperLog, ref result, out strError); if (nRet == -1) goto ERROR1; if (nRet == 1) return result; } else if (strAction == "onlydeletebiblio") { // 只有order权限的情况 if (StringUtil.IsInList("setbiblioinfo", sessioninfo.RightsOrigin) == false && StringUtil.IsInList("order", sessioninfo.RightsOrigin) == true) { // 工作库允许全部操作,非工作库不能删除记录 if (IsOrderWorkBiblioDb(strBiblioDbName) == false) { // 非工作库。要求原来记录不存在 strError = "当前帐户只有order权限而没有setbiblioinfo权限,不能用onlydeletebiblio功能删除书目记录 '" + strBiblioRecPath + "'"; result.Value = -1; result.ErrorInfo = strError; result.ErrorCode = ErrorCode.AccessDenied; return result; } } { strBiblio = ""; // 合并联合编目的新旧书目库XML记录 // 功能:排除新记录中对strLibraryCode定义以外的905字段的修改 // parameters: // bChangePartDenied 如果本次被设定为 true,则 strError 中返回了关于部分修改的注释信息 // return: // -1 error // 0 not delete any fields // 1 deleted some fields nRet = MergeOldNewBiblioRec( strRights, strUnionCatalogStyle, strLibraryCode, "delete", strAccessParameters, strExistingXml, ref strBiblio, ref bChangePartDenied, out strError); if (nRet == -1) goto ERROR1; if (bChangePartDenied == true && string.IsNullOrEmpty(strError) == false) strDeniedComment += " " + strError; // 检查根下面是不是没有任何元素了。如果还有,说明当前权限不足以删除它们。 // 如果已经为空,就表示不必检查了 if (String.IsNullOrEmpty(strBiblio) == false) { XmlDocument tempdom = new XmlDocument(); try { tempdom.LoadXml(strBiblio); } catch (Exception ex) { strError = "经过 MergeOldNewBiblioRec() 处理后的 strBiblio 装入 XmlDocument 失败: " + ex.Message; goto ERROR1; } // 2011/12/9 // 删除全部<operations>元素 XmlNodeList nodes = tempdom.DocumentElement.SelectNodes("//operations"); for (int i = 0; i < nodes.Count; i++) { XmlNode node = nodes[i]; if (node.ParentNode != null) node.ParentNode.RemoveChild(node); } if (tempdom.DocumentElement.ChildNodes.Count != 0) { result.Value = -1; result.ErrorInfo = "当前用户的权限不足以删除所有MARC字段,因此删除操作被拒绝。可改用修改操作。"; result.ErrorCode = ErrorCode.AccessDenied; return result; } } } if (bSimulate) { baOutputTimestamp = null; } else { // 不需要同时删除下属的实体记录 this.BiblioLocks.LockForWrite(strBiblioRecPath); try { baOutputTimestamp = null; // 删除书目记录 lRet = channel.DoDeleteRes(strBiblioRecPath, baTimestamp, out baOutputTimestamp, out strError); if (lRet == -1) { // 只删除书目记录,但是如果书目记录却不存在,要报错 goto ERROR1; } } finally { this.BiblioLocks.UnlockForWrite(strBiblioRecPath); } } } else { strError = "未知的strAction参数值 '" + strAction + "'"; goto ERROR1; } END1: if (bSimulate == false) { if (string.IsNullOrEmpty(strOutputBiblioRecPath) == false) { XmlNode node = DomUtil.SetElementText(domOperLog.DocumentElement, "record", strBiblio); DomUtil.SetAttr(node, "recPath", strOutputBiblioRecPath); } DomUtil.SetElementText(domOperLog.DocumentElement, "operator", sessioninfo.UserID); DomUtil.SetElementText(domOperLog.DocumentElement, "operTime", strOperTime); // 写入日志 nRet = this.OperLog.WriteOperLog(domOperLog, sessioninfo.ClientAddress, out strError); if (nRet == -1) { strError = "SetBiblioInfo() API 写入日志时发生错误: " + strError; goto ERROR1; } } result.Value = 0; if (bBiblioNotFound == true) result.ErrorInfo = "虽然书目记录 '" + strBiblioRecPath + "' 不存在,但是删除下属的实体记录成功。"; // 虽然...但是... // 2013/3/5 if (bChangePartDenied == true) { result.ErrorCode = ErrorCode.PartialDenied; if (string.IsNullOrEmpty(strDeniedComment) == false) { if (string.IsNullOrEmpty(result.ErrorInfo) == false) result.ErrorInfo += " ; "; result.ErrorInfo += strDeniedComment; } } return result; ERROR1: result.Value = -1; result.ErrorInfo = strError; result.ErrorCode = ErrorCode.SystemError; return result; }
// 获得读者信息 // parameters: // strBarcode 读者证条码号。如果前方引导以"@path:",则表示读者记录路径。在@path引导下,路径后面还可以跟随 "$prev"或"$next"表示方向 // 可以使用读者证号二维码 // TODO: 是否可以使用身份证号? // strResultTypeList 结果类型数组 xml/html/text/calendar/advancexml/recpaths/summary // 其中calendar表示获得读者所关联的日历名;advancexml表示经过运算了的提供了丰富附加信息的xml,例如具有超期和停借期附加信息 // strRecPath [out] 读者记录路径。如果命中多个读者记录,这里是逗号分隔的路径列表字符串。最多 100 个路径 // Result.Value -1出错 0没有找到 1找到 >1命中多于1条 // 权限: // 工作人员或者读者,必须有getreaderinfo权限 // 如果为读者, 附加限制还只能看属于自己的读者信息 public LibraryServerResult GetReaderInfo( SessionInfo sessioninfo, string strBarcode, string strResultTypeList, out string[] results, out string strRecPath, out byte[] baTimestamp) { results = null; baTimestamp = null; strRecPath = ""; List<string> recpaths = null; LibraryServerResult result = new LibraryServerResult(); // 个人书斋名 string strPersonalLibrary = ""; if (sessioninfo.UserType == "reader" && sessioninfo.Account != null) strPersonalLibrary = sessioninfo.Account.PersonalLibrary; // 权限判断 // 权限字符串 if (StringUtil.IsInList("getreaderinfo", sessioninfo.RightsOrigin) == false) { result.Value = -1; result.ErrorInfo = "读取读者信息被拒绝。不具备 getreaderinfo 权限。"; result.ErrorCode = ErrorCode.AccessDenied; return result; } string strError = ""; // 2007/12/2 if (String.IsNullOrEmpty(strBarcode) == true) { strError = "strBarcode参数值不能为空"; goto ERROR1; } // 对读者身份的附加判断 if (sessioninfo.UserType == "reader") { // TODO: 如果使用身份证号,似乎这里会遇到阻碍 if (strBarcode[0] != '@' && StringUtil.HasHead(strBarcode, "PQR:") == false) { if (StringUtil.IsIdcardNumber(strBarcode) == true) { // 2013/5/20 // 延迟判断 } else if (strBarcode != sessioninfo.Account.Barcode && string.IsNullOrEmpty(strPersonalLibrary) == true) { // 注:具有个人书斋的,还可以继续向后执行 result.Value = -1; result.ErrorInfo = "获得读者信息被拒绝。作为读者只能察看自己的读者记录"; result.ErrorCode = ErrorCode.AccessDenied; return result; } } // 后面还要判断 } RmsChannel channel = sessioninfo.Channels.GetChannel(this.WsUrl); if (channel == null) { strError = "get channel error"; goto ERROR1; } string strIdcardNumber = ""; string strXml = ""; string strOutputPath = ""; int nRet = 0; long lRet = 0; // 前端提供临时记录 if (strBarcode[0] == '<') { strXml = strBarcode; strRecPath = "?"; strOutputPath = "?"; // TODO: 数据库名需要从前端发来的XML记录中获取,或者要知道当前用户的馆代码? goto SKIP1; } bool bOnlyBarcode = false; // 是否仅仅在 证条码号中寻找 bool bRecordGetted = false; // 记录释放后已经获取到 // 命令状态 if (strBarcode[0] == '@' && strBarcode.StartsWith("@refID:") == false) { // 获得册记录,通过册记录路径 string strLeadPath = "@path:"; string strLeadDisplayName = "@displayName:"; string strLeadBarcode = "@barcode:"; /* if (strBarcode.Length <= strLead.Length) { strError = "错误的检索词格式: '" + strBarcode + "'"; goto ERROR1; } string strPart = strBarcode.Substring(0, strLead.Length); * */ if (StringUtil.HasHead(strBarcode, strLeadPath) == true) { string strReaderRecPath = strBarcode.Substring(strLeadPath.Length); // 2008/6/20 // 继续分离出(方向)命令部分 string strCommand = ""; nRet = strReaderRecPath.IndexOf("$"); if (nRet != -1) { strCommand = strReaderRecPath.Substring(nRet + 1); strReaderRecPath = strReaderRecPath.Substring(0, nRet); } #if NO string strReaderDbName = ResPath.GetDbName(strReaderRecPath); // 需要检查一下数据库名是否在允许的读者库名之列 if (this.IsReaderDbName(strReaderDbName) == false) { strError = "读者记录路径 '" + strReaderRecPath + "' 中的数据库名 '" + strReaderDbName + "' 不在配置的读者库名之列,因此拒绝操作。"; goto ERROR1; } #endif if (this.IsReaderRecPath(strReaderRecPath) == false) { strError = "记录路径 '" + strReaderRecPath + "' 并不是一个读者库记录路径,因此拒绝操作。"; goto ERROR1; } string strMetaData = ""; // 2008/6/20 changed string strStyle = "content,data,metadata,timestamp,outputpath"; if (String.IsNullOrEmpty(strCommand) == false && (strCommand == "prev" || strCommand == "next")) { strStyle += "," + strCommand; } // 观察一个读者记录路径,看看是不是在当前用户管辖的读者库范围内? if (this.IsCurrentChangeableReaderPath(strReaderRecPath, sessioninfo.LibraryCodeList) == false) { strError = "读者记录路径 '" + strReaderRecPath + "' 的读者库不在当前用户管辖范围内"; goto ERROR1; } lRet = channel.GetRes(strReaderRecPath, strStyle, out strXml, out strMetaData, out baTimestamp, out strOutputPath, out strError); if (lRet == -1) { if (channel.ErrorCode == ChannelErrorCode.NotFound) { result.Value = 0; if (strCommand == "prev") result.ErrorInfo = "到头"; else if (strCommand == "next") result.ErrorInfo = "到尾"; else result.ErrorInfo = "没有找到"; result.ErrorCode = ErrorCode.NotFound; return result; } nRet = -1; } else { nRet = 1; } bRecordGetted = true; } else if (StringUtil.HasHead(strBarcode, strLeadDisplayName) == true) { // 2011/2/19 string strDisplayName = strBarcode.Substring(strLeadDisplayName.Length); // 通过读者显示名获得读者记录 // return: // -1 error // 0 not found // 1 命中1条 // >1 命中多于1条 nRet = this.GetReaderRecXmlByDisplayName( // sessioninfo.Channels, channel, strDisplayName, out strXml, out strOutputPath, out baTimestamp, out strError); if (nRet == -1) goto ERROR1; if (nRet == 0) { result.ErrorInfo = "没有找到"; result.ErrorCode = ErrorCode.NotFound; return result; } // 观察一个读者记录路径,看看是不是在当前用户管辖的读者库范围内? if (this.IsCurrentChangeableReaderPath(strOutputPath, sessioninfo.LibraryCodeList) == false) { strError = "读者记录路径 '" + strOutputPath + "' 的读者库不在当前用户管辖范围内"; goto ERROR1; } bRecordGetted = true; } else if (StringUtil.HasHead(strBarcode, strLeadBarcode) == true) { strBarcode = strBarcode.Substring(strLeadBarcode.Length); bOnlyBarcode = true; bRecordGetted = false; } else { strError = "不支持的检索词格式: '" + strBarcode + "'。目前仅支持'@path:'和'@displayName:'引导的检索词"; goto ERROR1; } result.ErrorInfo = strError; result.Value = nRet; // } // 从证条码号获得 if (bRecordGetted == false) { if (string.IsNullOrEmpty(strBarcode) == false) { string strOutputCode = ""; // 把二维码字符串转换为读者证条码号 // parameters: // strReaderBcode [out]读者证条码号 // return: // -1 出错 // 0 所给出的字符串不是读者证号二维码 // 1 成功 nRet = this.DecodeQrCode(strBarcode, out strOutputCode, out strError); if (nRet == -1) goto ERROR1; if (nRet == 1) { // strQrCode = strBarcode; strBarcode = strOutputCode; } } // 加读锁 // 可以避免拿到读者记录处理中途的临时状态 #if DEBUG_LOCK_READER this.WriteErrorLog("GetReaderInfo 开始为读者加读锁 '" + strBarcode + "'"); #endif this.ReaderLocks.LockForRead(strBarcode); try { // 返回测试读者记录 if (IsTestReaderBarcode(strBarcode)) { nRet = 1; strXml = GetTestReaderXml(); recpaths = new List<string>(); recpaths.Add("测试读者/1"); baTimestamp = null; } else { // 慢速版本。但能获得重复的记录路径 // return: // -1 error // 0 not found // 1 命中1条 // >1 命中多于1条 nRet = this.GetReaderRecXml( // sessioninfo.Channels, channel, strBarcode, 100, sessioninfo.LibraryCodeList, out recpaths, out strXml, // out strOutputPath, out baTimestamp, out strError); } if (nRet > 0) strOutputPath = recpaths[0]; #if NO // 快速版本。无法获得全部重复的路径 // return: // -1 error // 0 not found // 1 命中1条 // >1 命中多于1条 nRet = this.TryGetReaderRecXml( channel, strBarcode, sessioninfo.LibraryCodeList, out strXml, out strOutputPath, out baTimestamp, out strError); #endif } finally { this.ReaderLocks.UnlockForRead(strBarcode); #if DEBUG_LOCK_READER this.WriteErrorLog("GetReaderInfo 结束为读者加读锁 '" + strBarcode + "'"); #endif } #if NO if (SessionInfo.IsGlobalUser(sessioninfo.LibraryCodeList) == false && nRet > 0) { // nRet 被修正 nRet = FilterReaderRecPath(ref recpaths, sessioninfo.LibraryCodeList, out strError); if (nRet == -1) goto ERROR1; } #endif if (nRet == 0) { if (bOnlyBarcode == true) goto NOT_FOUND; // 如果是身份证号,则试探检索“身份证号”途径 if (StringUtil.IsIdcardNumber(strBarcode) == true) { strIdcardNumber = strBarcode; strBarcode = ""; // 通过特定检索途径获得读者记录 // return: // -1 error // 0 not found // 1 命中1条 // >1 命中多于1条 /* nRet = this.GetReaderRecXmlByFrom( sessioninfo.Channels, strIdcardNumber, "身份证号", out strXml, out strOutputPath, out baTimestamp, out strError); * */ nRet = this.GetReaderRecXmlByFrom( // sessioninfo.Channels, channel, null, strIdcardNumber, "身份证号", 100, sessioninfo.LibraryCodeList, out recpaths, out strXml, // out strOutputPath, out baTimestamp, out strError); if (nRet == -1) { // text-level: 内部错误 strError = "用身份证号 '" + strIdcardNumber + "' 读入读者记录时发生错误: " + strError; goto ERROR1; } #if NO if (SessionInfo.IsGlobalUser(sessioninfo.LibraryCodeList) == false && nRet > 0) { // nRet 被修正 nRet = FilterReaderRecPath(ref recpaths, sessioninfo.LibraryCodeList, out strError); if (nRet == -1) goto ERROR1; } #endif if (nRet == 0) { result.Value = 0; // text-level: 用户提示 result.ErrorInfo = string.Format(this.GetString("身份证号s不存在"), // "身份证号 '{0}' 不存在" strIdcardNumber); result.ErrorCode = ErrorCode.IdcardNumberNotFound; return result; } if (nRet > 0) strOutputPath = recpaths[0]; /* * 不必明显报错,前端从返回值已经可以看出有重 if (nRet > 1) { // text-level: 用户提示 result.Value = -1; result.ErrorInfo = "用身份证号 '" + strIdcardNumber + "' 检索读者记录命中 " + nRet.ToString() + " 条,因此无法用身份证号来进行借还操作。请改用证条码号来进行借还操作。"; result.ErrorCode = ErrorCode.IdcardNumberDup; return result; } Debug.Assert(nRet == 1, ""); * */ result.ErrorInfo = strError; result.Value = nRet; goto SKIP0; } else { // 如果需要,从读者证号等辅助途径进行检索 foreach (string strFrom in this.PatronAdditionalFroms) { nRet = this.GetReaderRecXmlByFrom( // sessioninfo.Channels, channel, null, strBarcode, strFrom, 100, sessioninfo.LibraryCodeList, out recpaths, out strXml, out baTimestamp, out strError); if (nRet == -1) { // text-level: 内部错误 strError = "用" + strFrom + " '" + strBarcode + "' 读入读者记录时发生错误: " + strError; goto ERROR1; } #if NO if (SessionInfo.IsGlobalUser(sessioninfo.LibraryCodeList) == false && nRet > 0) { // nRet 被修正 nRet = FilterReaderRecPath(ref recpaths, sessioninfo.LibraryCodeList, out strError); if (nRet == -1) goto ERROR1; } #endif if (nRet == 0) continue; if (nRet > 0) strOutputPath = recpaths[0]; result.ErrorInfo = strError; result.Value = nRet; goto SKIP0; } } NOT_FOUND: result.Value = 0; result.ErrorInfo = "没有找到"; result.ErrorCode = ErrorCode.NotFound; return result; } // 观察一个读者记录路径,看看是不是在当前用户管辖的读者库范围内? if (IsTestReaderBarcode(strBarcode) == false && this.IsCurrentChangeableReaderPath(strOutputPath, sessioninfo.LibraryCodeList) == false) { strError = "读者记录路径 '" + strOutputPath + "' 的读者库不在当前用户管辖范围内"; goto ERROR1; } /* * 不必明显报错,前端从返回值已经可以看出有重 if (nRet > 1) { result.Value = nRet; result.ErrorInfo = "读者证条码号 '" +strBarcode+ "' 命中 " +nRet.ToString() + " 条。这是一个严重错误,请系统管理员尽快排除。"; result.ErrorCode = ErrorCode.ReaderBarcodeDup; return result; } * */ if (nRet == -1) goto ERROR1; result.ErrorInfo = strError; result.Value = nRet; } SKIP0: // strRecPath = strOutputPath; // 2013/5/21 if (recpaths != null) strRecPath = StringUtil.MakePathList(recpaths); else strRecPath = strOutputPath; SKIP1: if (String.IsNullOrEmpty(strResultTypeList) == true) { results = null; // 不返回任何结果 return result; } XmlDocument readerdom = null; if (sessioninfo.UserType == "reader") { nRet = LibraryApplication.LoadToDom(strXml, out readerdom, out strError); if (nRet == -1) { strError = "装载读者记录进入 XML DOM 时发生错误: " + strError; goto ERROR1; } // 对读者身份的附加判断 if (sessioninfo.UserType == "reader" && string.IsNullOrEmpty(strPersonalLibrary) == true) { string strBarcode1 = DomUtil.GetElementText(readerdom.DocumentElement, "barcode"); if (strBarcode1 != sessioninfo.Account.Barcode) { result.Value = -1; result.ErrorInfo = "获得读者信息被拒绝。作为读者只能察看自己的读者记录"; result.ErrorCode = ErrorCode.AccessDenied; return result; } } } string strLibraryCode = ""; if (strRecPath == "?" || IsTestReaderBarcode(strBarcode)) { // 从当前用户管辖的馆代码中选择第一个 // TODO: 如果发来的XML记录中有读者库名和馆代码帮助判断则更好 List<string> librarycodes = StringUtil.FromListString(sessioninfo.LibraryCodeList); if (librarycodes != null && librarycodes.Count > 0) strLibraryCode = librarycodes[0]; else strLibraryCode = ""; } else { nRet = this.GetLibraryCode(strRecPath, out strLibraryCode, out strError); if (nRet == -1) goto ERROR1; } { if (readerdom != null) { DomUtil.DeleteElement(readerdom.DocumentElement, "password"); DomUtil.SetElementText(readerdom.DocumentElement, "libraryCode", strLibraryCode); } if (string.IsNullOrEmpty(strXml) == false) { XmlDocument temp = new XmlDocument(); try { temp.LoadXml(strXml); } catch (Exception ex) { strError = "读者记录 XML 装入 DOM 时出错:" + ex.Message; goto ERROR1; } DomUtil.DeleteElement(temp.DocumentElement, "password"); DomUtil.SetElementText(temp.DocumentElement, "libraryCode", strLibraryCode); strXml = temp.DocumentElement.OuterXml; } } nRet = BuildReaderResults( sessioninfo, readerdom, strXml, strResultTypeList, strLibraryCode, recpaths, strOutputPath, baTimestamp, OperType.None, null, "", ref results, out strError); if (nRet == -1) goto ERROR1; #if NO string[] result_types = strResultTypeList.Split(new char[] { ',' }); results = new string[result_types.Length]; for (int i = 0; i < result_types.Length; i++) { string strResultType = result_types[i]; // 2008/4/3 // if (String.Compare(strResultType, "calendar", true) == 0) if (IsResultType(strResultType, "calendar") == true) { if (readerdom == null) { nRet = LibraryApplication.LoadToDom(strXml, out readerdom, out strError); if (nRet == -1) { strError = "装载读者记录进入XML DOM时发生错误: " + strError; goto ERROR1; } } string strReaderType = DomUtil.GetElementText(readerdom, "readerType"); // 获得日历 DigitalPlatform.LibraryServer.Calendar calendar = null; // return: // -1 出错 // 0 没有找到日历 // 1 找到日历 nRet = this.GetReaderCalendar(strReaderType, strLibraryCode, out calendar, out strError); if (nRet == -1 || nRet == 0) { calendar = null; } string strCalendarName = ""; if (calendar != null) strCalendarName = calendar.Name; results[i] = strCalendarName; } // else if (String.Compare(strResultType, "xml", true) == 0) else if (IsResultType(strResultType, "xml") == true) { // results[i] = strXml; string strResultXml = ""; nRet = GetItemXml(strXml, strResultType, out strResultXml, out strError); if (nRet == -1) { strError = "获取 " + strResultType + " 格式的 XML 字符串时出错: " + strError; goto ERROR1; } results[i] = strResultXml; } else if (String.Compare(strResultType, "timestamp", true) == 0) { // 2011/1/27 results[i] = ByteArray.GetHexTimeStampString(baTimestamp); } else if (String.Compare(strResultType, "recpaths", true) == 0) { // 2013/5/21 if (recpaths != null) results[i] = StringUtil.MakePathList(recpaths); else results[i] = strOutputPath; } else if (String.Compare(strResultType, "advancexml_borrow_bibliosummary", true) == 0 || String.Compare(strResultType, "advancexml_overdue_bibliosummary", true) == 0 || String.Compare(strResultType, "advancexml_history_bibliosummary", true) == 0 ) { // 2011/1/27 continue; } // else if (String.Compare(strResultType, "summary", true) == 0) else if (IsResultType(strResultType, "summary") == true) { // 2013/11/15 string strSummary = ""; XmlDocument dom = new XmlDocument(); try { dom.LoadXml(strXml); } catch (Exception ex) { strSummary = "读者 XML 装入 DOM 出错: " + ex.Message; results[i] = strSummary; continue; } strSummary = DomUtil.GetElementText(dom.DocumentElement, "name"); results[i] = strSummary; } // else if (String.Compare(strResultType, "advancexml", true) == 0) else if (IsResultType(strResultType, "advancexml") == true) { // 2008/4/3 string strOutputXml = ""; nRet = this.GetAdvanceReaderXml( sessioninfo, strResultTypeList, // strResultType, BUG!!! 2012/4/8 strLibraryCode, strXml, out strOutputXml, out strError); if (nRet == -1) { strError = "GetAdvanceReaderXml()出错: " + strError; goto ERROR1; } results[i] = strOutputXml; } // else if (String.Compare(strResultType, "html", true) == 0) else if (IsResultType(strResultType, "html") == true) { string strReaderRecord = ""; // 将读者记录数据从XML格式转换为HTML格式 nRet = this.ConvertReaderXmlToHtml( sessioninfo, this.CfgDir + "\\readerxml2html.cs", this.CfgDir + "\\readerxml2html.cs.ref", strLibraryCode, strXml, strOutputPath, // 2009/10/18 OperType.None, null, "", strResultType, out strReaderRecord, out strError); if (nRet == -1) { strError = "ConvertReaderXmlToHtml()出错(脚本程序为" + this.CfgDir + "\\readerxml2html.cs" + "): " + strError; goto ERROR1; } // test strReaderRecord = "<html><body><p>test</p></body></html>"; results[i] = strReaderRecord; } // else if (String.Compare(strResultType, "text", true) == 0) else if (IsResultType(strResultType, "text") == true) { string strReaderRecord = ""; // 将读者记录数据从XML格式转换为text格式 nRet = this.ConvertReaderXmlToHtml( sessioninfo, this.CfgDir + "\\readerxml2text.cs", this.CfgDir + "\\readerxml2text.cs.ref", strLibraryCode, strXml, strOutputPath, // 2009/10/18 OperType.None, null, "", strResultType, out strReaderRecord, out strError); if (nRet == -1) { strError = "ConvertReaderXmlToHtml()出错(脚本程序为" + this.CfgDir + "\\readerxml2html.cs" + "): " + strError; goto ERROR1; } results[i] = strReaderRecord; } else { strError = "未知的结果类型 '" + strResultType + "'"; goto ERROR1; } } #endif return result; ERROR1: result.Value = -1; result.ErrorInfo = strError; result.ErrorCode = ErrorCode.SystemError; return result; }
// 删除书目记录的下级记录 // return: // -1 失败 // 0 成功 // 1 需要结束运行,result 结果已经设置好了 int DeleteBiblioAndSubRecords( SessionInfo sessioninfo, string strAction, string strBiblioRecPath, string strExistingXml, byte [] baTimestamp, ref bool bBiblioNotFound, ref string strBiblio, ref byte[] baOutputTimestamp, ref XmlDocument domOperLog, ref LibraryServerResult result, out string strError) { strError = ""; int nRet = 0; long lRet = 0; // 这个删除不是那么简单,需要同时删除下属的实体记录 // 要对种和实体都进行锁定 this.BiblioLocks.LockForWrite(strBiblioRecPath); try { // 探测书目记录有没有下属的实体记录(也顺便看看实体记录里面是否有流通信息)? List<DeleteEntityInfo> entityinfos = null; string strStyle = "check_borrow_info"; long lHitCount = 0; RmsChannel channel = sessioninfo.Channels.GetChannel(this.WsUrl); if (channel == null) { strError = "channel == null"; goto ERROR1; } // return: // -2 not exist entity dbname // -1 error // >=0 含有流通信息的实体记录个数 nRet = SearchChildEntities(channel, strBiblioRecPath, strStyle, sessioninfo.GlobalUser == false ? CheckItemRecord : (Delegate_checkRecord)null, sessioninfo.GlobalUser == false ? sessioninfo.LibraryCodeList : null, out lHitCount, out entityinfos, out strError); if (nRet == -1) goto ERROR1; if (nRet == -2) { Debug.Assert(entityinfos.Count == 0, ""); } // 如果有实体记录,则要求setentities权限,才能一同删除实体们 if (entityinfos != null && entityinfos.Count > 0) { // 权限字符串 if (StringUtil.IsInList("setentities", sessioninfo.RightsOrigin) == false && StringUtil.IsInList("setiteminfo", sessioninfo.RightsOrigin) == false) { result.Value = -1; result.ErrorInfo = "设置书目信息的删除(delete)操作被拒绝。因拟删除的书目记录带有下属的实体记录,但当前用户不具备setiteminfo或setentities权限,不能删除它们。"; result.ErrorCode = ErrorCode.AccessDenied; // return result; return 1; } if (this.DeleteBiblioSubRecords == false) { result.Value = -1; result.ErrorInfo = "设置书目信息的删除(delete)操作被拒绝。因拟删除的书目记录带有下属的实体记录,不允许删除书目记录"; result.ErrorCode = ErrorCode.AccessDenied; // return result; return 1; } // bFoundEntities = true; } // // 探测书目记录有没有下属的订购记录 List<DeleteEntityInfo> orderinfos = null; // bool bFoundOrders = false; // return: // -1 error // 0 not exist entity dbname // 1 exist entity dbname nRet = this.OrderItemDatabase.SearchChildItems(channel, strBiblioRecPath, "check_circulation_info", // 在DeleteEntityInfo结构中*不*返回OldRecord内容 out lHitCount, out orderinfos, out strError); if (nRet == -1) goto ERROR1; if (nRet == 0) { Debug.Assert(orderinfos.Count == 0, ""); } // 如果有订购记录,则要求setorders权限,才能一同删除它们 if (orderinfos != null && orderinfos.Count > 0) { // 权限字符串 if (StringUtil.IsInList("setorders", sessioninfo.RightsOrigin) == false && StringUtil.IsInList("setorderinfo", sessioninfo.RightsOrigin) == false && StringUtil.IsInList("order", sessioninfo.RightsOrigin) == false) { result.Value = -1; result.ErrorInfo = "设置书目信息的删除(delete)操作被拒绝。因拟删除的书目记录带有下属的订购记录,但当前用户不具备order、setorderinfo或setorders权限,不能删除它们。"; result.ErrorCode = ErrorCode.AccessDenied; // return result; return 1; } if (this.DeleteBiblioSubRecords == false) { result.Value = -1; result.ErrorInfo = "设置书目信息的删除(delete)操作被拒绝。因拟删除的书目记录带有下属的订购记录,不允许删除书目记录"; result.ErrorCode = ErrorCode.AccessDenied; // return result; return 1; } // bFoundOrders = true; } // // 探测书目记录有没有下属的期记录 List<DeleteEntityInfo> issueinfos = null; // bool bFoundIssues = false; // return: // -1 error // 0 not exist entity dbname // 1 exist entity dbname nRet = this.IssueItemDatabase.SearchChildItems(channel, strBiblioRecPath, "check_circulation_info", // 在DeleteEntityInfo结构中*不*返回OldRecord内容 out lHitCount, out issueinfos, out strError); if (nRet == -1) goto ERROR1; if (nRet == 0) { Debug.Assert(issueinfos.Count == 0, ""); } // 如果有期记录,则要求setissues权限,才能一同删除它们 if (issueinfos != null && issueinfos.Count > 0) { // 权限字符串 if (StringUtil.IsInList("setissues", sessioninfo.RightsOrigin) == false && StringUtil.IsInList("setissueinfo", sessioninfo.RightsOrigin) == false) { result.Value = -1; result.ErrorInfo = "设置书目信息的删除(delete)操作被拒绝。因拟删除的书目记录带有下属的期记录,但当前用户不具备setissueinfo或setissues权限,不能删除它们。"; result.ErrorCode = ErrorCode.AccessDenied; // return result; return 1; } if (this.DeleteBiblioSubRecords == false) { result.Value = -1; result.ErrorInfo = "设置书目信息的删除(delete)操作被拒绝。因拟删除的书目记录带有下属的期记录,不允许删除书目记录"; result.ErrorCode = ErrorCode.AccessDenied; // return result; return 1; } // bFoundIssues = true; } // 探测书目记录有没有下属的评注记录 List<DeleteEntityInfo> commentinfos = null; // return: // -1 error // 0 not exist entity dbname // 1 exist entity dbname nRet = this.CommentItemDatabase.SearchChildItems(channel, strBiblioRecPath, "check_circulation_info", // 在DeleteEntityInfo结构中*不*返回OldRecord内容 out lHitCount, out commentinfos, out strError); if (nRet == -1) goto ERROR1; if (nRet == 0) { Debug.Assert(commentinfos.Count == 0, ""); } // 如果有评注记录,则要求setcommentinfo权限,才能一同删除它们 if (commentinfos != null && commentinfos.Count > 0) { // 权限字符串 if (StringUtil.IsInList("setcommentinfo", sessioninfo.RightsOrigin) == false) { result.Value = -1; result.ErrorInfo = "设置书目信息的删除(delete)操作被拒绝。因拟删除的书目记录带有下属的评注记录,但当前用户不具备setcommentinfo权限,不能删除它们。"; result.ErrorCode = ErrorCode.AccessDenied; // return result; return 1; } if (this.DeleteBiblioSubRecords == false) { result.Value = -1; result.ErrorInfo = "设置书目信息的删除(delete)操作被拒绝。因拟删除的书目记录带有下属的评注记录,不允许删除书目记录"; result.ErrorCode = ErrorCode.AccessDenied; // return result; return 1; } } baOutputTimestamp = null; if (strAction == "delete") { // 删除书目记录 lRet = channel.DoDeleteRes(strBiblioRecPath, baTimestamp, out baOutputTimestamp, out strError); if (lRet == -1) { if (channel.ErrorCode == ChannelErrorCode.NotFound && (entityinfos.Count > 0 || orderinfos.Count > 0 || issueinfos.Count > 0) ) { bBiblioNotFound = true; // strWarning = "书目记录 '" + strBiblioRecPath + "' 不存在"; } else goto ERROR1; } } strBiblio = ""; // 以免后面把残余信息写入操作日志的 <record>元素 2013/3/11 baOutputTimestamp = null; // 删除属于同一书目记录的全部实体记录 // 这是需要提供EntityInfo数组的版本 // return: // -1 error // 0 没有找到属于书目记录的任何实体记录,因此也就无从删除 // >0 实际删除的实体记录数 nRet = DeleteBiblioChildEntities(channel, entityinfos, domOperLog, out strError); if (nRet == -1 && bBiblioNotFound == false) { // TODO: 当书目记录中有对象资源时,DoSaveTextRes就无法恢复了 // 重新保存回去书目记录, 以便还有下次重试删除的机会 // 因此需要注意,前端在删除失败后,不要忘记了更新timestamp if (strAction == "delete") { string strError_1 = ""; string strOutputBiblioRecPath = ""; lRet = channel.DoSaveTextRes(strBiblioRecPath, strExistingXml, false, "content", // ,ignorechecktimestamp null, // timestamp out baOutputTimestamp, out strOutputBiblioRecPath, out strError_1); if (lRet == -1) { strError = "删除下级实体记录失败: " + strError + ";\r\n并且试图重新写回刚刚已删除的书目记录 '" + strBiblioRecPath + "' 的操作也发生了错误: " + strError_1; goto ERROR1; } } goto ERROR1; } // return: // -1 error // 0 没有找到属于书目记录的任何实体记录,因此也就无从删除 // >0 实际删除的实体记录数 nRet = this.OrderItemDatabase.DeleteBiblioChildItems( // sessioninfo.Channels, channel, orderinfos, domOperLog, out strError); if (nRet == -1 && bBiblioNotFound == false) { // 重新保存回去书目记录, 以便还有下次重试删除的机会 // 因此需要注意,前端在删除失败后,不要忘记了更新timestamp try { string strError_1 = ""; string strOutputBiblioRecPath = ""; lRet = channel.DoSaveTextRes(strBiblioRecPath, strExistingXml, false, "content", // ,ignorechecktimestamp null, // timestamp out baOutputTimestamp, out strOutputBiblioRecPath, out strError_1); if (lRet == -1) { strError = "删除下级订购记录失败: " + strError + ";\r\n并且试图重新写回刚刚已删除的书目记录 '" + strBiblioRecPath + "' 的操作也发生了错误: " + strError_1; goto ERROR1; } goto ERROR1; } finally { if (entityinfos.Count > 0) strError += ";\r\n刚删除的 " + entityinfos.Count.ToString() + " 个册记录已经无法恢复"; } } // return: // -1 error // 0 没有找到属于书目记录的任何实体记录,因此也就无从删除 // >0 实际删除的实体记录数 nRet = this.IssueItemDatabase.DeleteBiblioChildItems( // sessioninfo.Channels, channel, issueinfos, domOperLog, out strError); if (nRet == -1 && bBiblioNotFound == false) { // 重新保存回去书目记录, 以便还有下次重试删除的机会 // 因此需要注意,前端在删除失败后,不要忘记了更新timestamp try { string strError_1 = ""; string strOutputBiblioRecPath = ""; lRet = channel.DoSaveTextRes(strBiblioRecPath, strExistingXml, false, "content", // ,ignorechecktimestamp null, // timestamp out baOutputTimestamp, out strOutputBiblioRecPath, out strError_1); if (lRet == -1) { strError = "删除下级期记录失败: " + strError + ";\r\n并且试图重新写回刚刚已删除的书目记录 '" + strBiblioRecPath + "' 的操作也发生了错误: " + strError_1; goto ERROR1; } goto ERROR1; } finally { if (entityinfos.Count > 0) strError += ";\r\n刚删除的 " + entityinfos.Count.ToString() + " 个册记录已经无法恢复"; if (orderinfos.Count > 0) strError += ";\r\n刚删除的 " + orderinfos.Count.ToString() + " 个订购记录已经无法恢复"; } } // return: // -1 error // 0 没有找到属于书目记录的任何实体记录,因此也就无从删除 // >0 实际删除的实体记录数 nRet = this.CommentItemDatabase.DeleteBiblioChildItems( // sessioninfo.Channels, channel, commentinfos, domOperLog, out strError); if (nRet == -1 && bBiblioNotFound == false) { // 重新保存回去书目记录, 以便还有下次重试删除的机会 // 因此需要注意,前端在删除失败后,不要忘记了更新timestamp try { string strError_1 = ""; string strOutputBiblioRecPath = ""; lRet = channel.DoSaveTextRes(strBiblioRecPath, strExistingXml, false, "content", // ,ignorechecktimestamp null, // timestamp out baOutputTimestamp, out strOutputBiblioRecPath, out strError_1); if (lRet == -1) { strError = "删除下级评注记录失败: " + strError + ";\r\n并且试图重新写回刚刚已删除的书目记录 '" + strBiblioRecPath + "' 的操作也发生了错误: " + strError_1; goto ERROR1; } goto ERROR1; } finally { if (entityinfos.Count > 0) strError += ";\r\n刚删除的 " + entityinfos.Count.ToString() + " 个册记录已经无法恢复"; if (orderinfos.Count > 0) strError += ";\r\n刚删除的 " + orderinfos.Count.ToString() + " 个订购记录已经无法恢复"; if (issueinfos.Count > 0) strError += ";\r\n刚删除的 " + issueinfos.Count.ToString() + " 个期记录已经无法恢复"; } } } finally { this.BiblioLocks.UnlockForWrite(strBiblioRecPath); } return 0; ERROR1: return -1; }
// 移动读者记录 // parameters: // strTargetRecPath [in][out]目标记录路径 // return: // result.Value: // -1 error // 0 已经成功移动 // 权限: // 需要movereaderinfo权限 // 日志: // 要产生日志 public LibraryServerResult MoveReaderInfo( SessionInfo sessioninfo, string strSourceRecPath, ref string strTargetRecPath, out byte[] target_timestamp) { string strError = ""; target_timestamp = null; int nRet = 0; long lRet = 0; // bool bChanged = false; // 是否发生过实质性改动 LibraryServerResult result = new LibraryServerResult(); // 权限字符串 if (StringUtil.IsInList("movereaderinfo", sessioninfo.RightsOrigin) == false) { result.Value = -1; result.ErrorInfo = "移动读者记录的操作被拒绝。不具备 movereaderinfo 权限。"; result.ErrorCode = ErrorCode.AccessDenied; return result; } // 检查源和目标记录路径不能相同 if (strSourceRecPath == strTargetRecPath) { strError = "源和目标读者记录路径不能相同"; goto ERROR1; } if (String.IsNullOrEmpty(strSourceRecPath) == true) { strError = "源读者记录路径不能为空"; goto ERROR1; } if (String.IsNullOrEmpty(strTargetRecPath) == true) { strError = "目标读者记录路径不能为空"; goto ERROR1; } // 检查两个路径是否都是读者库路径 if (this.IsReaderRecPath(strSourceRecPath) == false) { strError = "strSourceRecPath参数所给出的源记录路径 '" + strSourceRecPath + "' 并不是一个读者库记录路径"; goto ERROR1; } if (this.IsReaderRecPath(strTargetRecPath) == false) { strError = "strTargetRecPath参数所给出的目标记录路径 '" + strTargetRecPath + "' 并不是一个读者库记录路径"; goto ERROR1; } RmsChannel channel = sessioninfo.Channels.GetChannel(this.WsUrl); if (channel == null) { strError = "channel == null"; goto ERROR1; } // 读出源记录 string strExistingSourceXml = ""; byte[] exist_soutce_timestamp = null; string strTempOutputPath = ""; string strMetaData = ""; int nRedoCount = 0; REDOLOAD: // 先读出数据库中此位置的已有记录 lRet = channel.GetRes(strSourceRecPath, out strExistingSourceXml, out strMetaData, out exist_soutce_timestamp, out strTempOutputPath, out strError); if (lRet == -1) { if (channel.ErrorCode == ChannelErrorCode.NotFound) { strError = "源记录 '" + strSourceRecPath + "' 不存在"; // errorcode = channel.OriginErrorCode; goto ERROR1; } else { strError = "移动操作发生错误, 在读入源记录 '" + strSourceRecPath + "' 阶段:" + strError; // errorcode = channel.OriginErrorCode; goto ERROR1; } } string strSourceLibraryCode = ""; string strTargetLibraryCode = ""; // 看看读者记录所从属的读者库的馆代码,是否被当前用户管辖 if (String.IsNullOrEmpty(strTempOutputPath) == false) { // 检查当前操作者是否管辖这个读者库 // 观察一个读者记录路径,看看是不是在当前用户管辖的读者库范围内? if (this.IsCurrentChangeableReaderPath(strTempOutputPath, sessioninfo.LibraryCodeList, out strSourceLibraryCode) == false) { strError = "源读者记录路径 '" + strTempOutputPath + "' 从属的读者库不在当前用户管辖范围内"; goto ERROR1; } } // 把记录装入DOM XmlDocument domExist = new XmlDocument(); try { domExist.LoadXml(strExistingSourceXml); } catch (Exception ex) { strError = "strExistingSourceXml装载进入DOM时发生错误: " + ex.Message; goto ERROR1; } string strLockBarcode = DomUtil.GetElementText(domExist.DocumentElement, "barcode"); // 加读者记录锁 if (String.IsNullOrEmpty(strLockBarcode) == false) { #if DEBUG_LOCK_READER this.WriteErrorLog("MoveReaderInfo 开始为读者加写锁 '" + strLockBarcode + "'"); #endif this.ReaderLocks.LockForWrite(strLockBarcode); } try { // 锁定后重新读入一次源读者记录。这是因为担心第一次为了获得证条码号的读取和锁定之间存在可能被其他地方修改了此条记录的可能 byte[] temp_timestamp = null; lRet = channel.GetRes(strSourceRecPath, out strExistingSourceXml, out strMetaData, out temp_timestamp, out strTempOutputPath, out strError); if (lRet == -1) { strError = "移动操作发生错误, 在重新读入源记录 '" + strSourceRecPath + "' 阶段:" + strError; goto ERROR1; } nRet = ByteArray.Compare(exist_soutce_timestamp, temp_timestamp); if (nRet != 0) { // 重新把记录装入DOM domExist = new XmlDocument(); try { domExist.LoadXml(strExistingSourceXml); } catch (Exception ex) { strError = "strExistingSourceXml装载进入DOM时发生错误(2): " + ex.Message; goto ERROR1; } // 重新核对条码号 if (strLockBarcode != DomUtil.GetElementText(domExist.DocumentElement, "barcode")) { if (nRedoCount < 10) { nRedoCount++; goto REDOLOAD; } strError = "争夺锁定过程中发生太多次的错误。请稍后重试移动操作"; goto ERROR1; } exist_soutce_timestamp = temp_timestamp; } // 检查即将覆盖的目标位置是不是有记录,如果有,则不允许进行move操作。 bool bAppendStyle = false; // 目标路径是否为追加形态? string strTargetRecId = ResPath.GetRecordId(strTargetRecPath); if (strTargetRecId == "?" || String.IsNullOrEmpty(strTargetRecId) == true) { // 2009/11/1 if (String.IsNullOrEmpty(strTargetRecId) == true) strTargetRecPath += "/?"; bAppendStyle = true; } if (bAppendStyle == false) { string strExistTargetXml = ""; byte[] exist_target_timestamp = null; string strOutputPath = ""; // 获取覆盖目标位置的现有记录 lRet = channel.GetRes(strTargetRecPath, out strExistTargetXml, out strMetaData, out exist_target_timestamp, out strOutputPath, out strError); if (lRet == -1) { if (channel.ErrorCode == ChannelErrorCode.NotFound) { // 如果记录不存在, 说明不会造成覆盖态势 } else { strError = "移动操作发生错误, 在读入即将覆盖的目标位置 '" + strTargetRecPath + "' 原有记录阶段:" + strError; goto ERROR1; } } else { // 如果记录存在,则目前不允许这样的操作 strError = "移动操作被拒绝。因为在即将覆盖的目标位置 '" + strTargetRecPath + "' 已经存在记录。除非先删除(delete)这条记录,才能进行移动(move)操作"; goto ERROR1; } } // 看看读者记录所从属的读者库的馆代码,是否被当前用户管辖 if (String.IsNullOrEmpty(strTargetRecPath) == false) { // 检查当前操作者是否管辖这个读者库 // 观察一个读者记录路径,看看是不是在当前用户管辖的读者库范围内? if (this.IsCurrentChangeableReaderPath(strTargetRecPath, sessioninfo.LibraryCodeList, out strTargetLibraryCode) == false) { strError = "目标读者记录路径 '" + strTargetRecPath + "' 从属的读者库不在当前用户管辖范围内"; goto ERROR1; } } // 移动记录 // byte[] output_timestamp = null; string strOutputRecPath = ""; // TODO: Copy后还要写一次?因为Copy并不写入新记录。 // 其实Copy的意义在于带走资源。否则还不如用Save+Delete lRet = channel.DoCopyRecord(strSourceRecPath, strTargetRecPath, true, // bDeleteSourceRecord out target_timestamp, out strOutputRecPath, out strError); if (lRet == -1) { strError = "DoCopyRecord() error :" + strError; goto ERROR1; } strTargetRecPath = strOutputRecPath; /* 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); } } */ } catch (Exception ex) { result.Value = -1; result.ErrorCode = ErrorCode.SystemError; result.ErrorInfo = "抛出异常:" + ex.Message; return result; } finally { if (String.IsNullOrEmpty(strLockBarcode) == false) { this.ReaderLocks.UnlockForWrite(strLockBarcode); #if DEBUG_LOCK_READER this.WriteErrorLog("MoveReaderInfo 结束为读者加写锁 '" + strLockBarcode + "'"); #endif } } XmlDocument domOperLog = new XmlDocument(); domOperLog.LoadXml("<root />"); DomUtil.SetElementText(domOperLog.DocumentElement, "libraryCode", strSourceLibraryCode + "," + strTargetLibraryCode); // 读者所在的馆代码 DomUtil.SetElementText(domOperLog.DocumentElement, "operation", "setReaderInfo"); DomUtil.SetElementText(domOperLog.DocumentElement, "action", "move"); string strOperTimeString = this.Clock.GetClock(); // RFC1123格式 DomUtil.SetElementText(domOperLog.DocumentElement, "operator", sessioninfo.UserID); DomUtil.SetElementText(domOperLog.DocumentElement, "operTime", strOperTimeString); XmlNode node = DomUtil.SetElementText(domOperLog.DocumentElement, "record", ""); DomUtil.SetAttr(node, "recPath", strTargetRecPath); node = DomUtil.SetElementText(domOperLog.DocumentElement, "oldRecord", strExistingSourceXml); DomUtil.SetAttr(node, "recPath", strSourceRecPath); nRet = this.OperLog.WriteOperLog(domOperLog, sessioninfo.ClientAddress, out strError); if (nRet == -1) { strError = "MoveReaderInfo() API 写入日志时发生错误: " + strError; goto ERROR1; } result.Value = 1; return result; ERROR1: result.Value = -1; result.ErrorInfo = strError; result.ErrorCode = ErrorCode.SystemError; return result; }
// 预约 // 权限:需要有reservation权限 public LibraryServerResult Reservation( SessionInfo sessioninfo, string strFunction, string strReaderBarcode, string strItemBarcodeList) { LibraryServerResult result = new LibraryServerResult(); // 权限字符串 if (StringUtil.IsInList("reservation", sessioninfo.RightsOrigin) == false) { result.Value = -1; // text-level: 用户提示 result.ErrorInfo = this.GetString("预约操作被拒绝。不具备 reservation 权限。"); result.ErrorCode = ErrorCode.AccessDenied; return result; } int nRet = 0; string strError = ""; // 2010/12/31 if (String.IsNullOrEmpty(this.ArrivedDbName) == true) { strError = "预约到书库尚未定义, 预约操作失败"; goto ERROR1; } if (String.Compare(strFunction, "new", true) != 0 && String.Compare(strFunction, "delete", true) != 0 && String.Compare(strFunction, "merge", true) != 0 && String.Compare(strFunction, "split", true) != 0 ) { result.Value = -1; // text-level: 内部错误 result.ErrorInfo = string.Format(this.GetString("未知的strFunction参数值s"), // "未知的strFunction参数值 '{0}'" strFunction); // "未知的strFunction参数值 '" + strFunction + "'"; result.ErrorCode = ErrorCode.InvalidParameter; return result; } RmsChannel channel = sessioninfo.Channels.GetChannel(this.WsUrl); if (channel == null) { strError = "get channel error"; goto ERROR1; } // 在架册集合 List<string> OnShelfItemBarcodes = new List<string>(); // 被删除的已到书状态册集合 List<string> ArriveItemBarcodes = new List<string>(); // 加读者记录锁 #if DEBUG_LOCK_READER this.WriteErrorLog("Reservation 开始为读者加写锁 '" + strReaderBarcode + "'"); #endif this.ReaderLocks.LockForWrite(strReaderBarcode); try { // 读入读者记录 string strReaderXml = ""; string strOutputReaderRecPath = ""; byte[] reader_timestamp = null; nRet = this.GetReaderRecXml( // sessioninfo.Channels, channel, strReaderBarcode, out strReaderXml, out strOutputReaderRecPath, out reader_timestamp, out strError); if (nRet == 0) { result.Value = -1; // text-level: 用户提示 result.ErrorInfo = string.Format(this.GetString("读者证条码号s不存在"), // 读者证条码号 {0} 不存在 strReaderBarcode); // "读者证条码号 '" + strReaderBarcode + "' 不存在"; result.ErrorCode = ErrorCode.ReaderBarcodeNotFound; return result; } if (nRet == -1) { // text-level: 内部错误 strError = string.Format(this.GetString("读入读者记录时发生错误s"), // "读入读者记录时发生错误: {0}" strError); // "读入读者记录时发生错误: " + strError; goto ERROR1; } string strLibraryCode = ""; // 看看读者记录所从属的数据库,是否在参与流通的读者库之列 // 2012/9/8 if (String.IsNullOrEmpty(strOutputReaderRecPath) == false) { string strReaderDbName = ResPath.GetDbName(strOutputReaderRecPath); bool bReaderDbInCirculation = true; if (this.IsReaderDbName(strReaderDbName, out bReaderDbInCirculation, out strLibraryCode) == false) { // text-level: 内部错误 strError = "读者记录路径 '" + strOutputReaderRecPath + "' 中的数据库名 '" + strReaderDbName + "' 居然不在定义的读者库之列。"; goto ERROR1; } if (bReaderDbInCirculation == false) { // text-level: 用户提示 strError = string.Format(this.GetString("预约操作被拒绝。读者证条码号s所在的读者记录s因其数据库s属于未参与流通的读者库"), // "预约操作被拒绝。读者证条码号 '{0}' 所在的读者记录 '{1}' 因其数据库 '{2}' 属于未参与流通的读者库" strReaderBarcode, strOutputReaderRecPath, strReaderDbName); goto ERROR1; } // 检查当前操作者是否管辖这个读者库 // 观察一个读者记录路径,看看是不是在当前用户管辖的读者库范围内? if (this.IsCurrentChangeableReaderPath(strOutputReaderRecPath, sessioninfo.LibraryCodeList) == false) { strError = "读者记录路径 '" + strOutputReaderRecPath + "' 的读者库不在当前用户管辖范围内"; goto ERROR1; } } XmlDocument readerdom = null; nRet = LibraryApplication.LoadToDom(strReaderXml, out readerdom, out strError); if (nRet == -1) { // text-level: 内部错误 strError = string.Format(this.GetString("装载读者记录进入XMLDOM时发生错误s"), // "装载读者记录进入XML DOM时发生错误: {0}" strError); // "装载读者记录进入XML DOM时发生错误: " + strError; goto ERROR1; } CachedRecordCollection records = new CachedRecordCollection(); records.Add(strOutputReaderRecPath, readerdom, reader_timestamp); if (strFunction == "delete" && sessioninfo.UserType != "reader") { // 当工作人员代为操作时, 对于delete操作网开一面,不做基本检查(读者证状态和检查和未取次数的检查) } else { // return: // -1 检测过程发生了错误。应当作不能借阅来处理 // 0 可以借阅 // 1 证已经过了失效期,不能借阅 // 2 证有不让借阅的状态 nRet = CheckReaderExpireAndState(readerdom, out strError); if (nRet != 0) { // text-level: 用户提示 strError = string.Format(this.GetString("预约操作被拒绝,原因s"), // "预约操作被拒绝,原因: {0}" strError); // "预约操作被拒绝,原因: " + strError; goto ERROR1; } // 检查到书未取次数是否超标 XmlNode nodeOutof = readerdom.DocumentElement.SelectSingleNode("outofReservations"); if (nodeOutof != null) { string strCount = DomUtil.GetAttr(nodeOutof, "count"); int nCount = 0; try { nCount = Convert.ToInt32(strCount); } catch { } if (nCount >= this.OutofReservationThreshold) { strError = string.Format(this.GetString("预约操作被拒绝,因为次数超过"), // "预约操作被拒绝,因为当前读者以前预约到书后未取的次数超过了 {0} 次,被取消预约能力。如果要恢复预约能力,请读者到图书馆柜台办理解除手续。" this.OutofReservationThreshold.ToString()); // "预约操作被拒绝,因为当前读者以前预约到书后未取的次数超过了 " + this.OutofReservationThreshold.ToString() + " 次,被取消预约能力。如果要恢复预约能力,请读者到图书馆柜台办理解除手续。"; goto ERROR1; } } } // 准备日志DOM XmlDocument domOperLog = new XmlDocument(); domOperLog.LoadXml("<root />"); DomUtil.SetElementText(domOperLog.DocumentElement, "libraryCode", strLibraryCode); // 读者所在的馆代码 DomUtil.SetElementText(domOperLog.DocumentElement, "operation", "reservation"); if (String.Compare(strFunction, "new", true) == 0) { // 对即将预约的册条码号进行查重 // 要求本读者先前未曾用这些条码号预约过 // return: // -1 出错 // 0 没有重 // 1 有重 提示信息在strError中 nRet = this.ReservationCheckDup( strItemBarcodeList, strLibraryCode, ref readerdom, out strError); if (nRet == -1) goto ERROR1; if (nRet == 1) { result.Value = -1; // text-level: 用户提示 result.ErrorInfo = string.Format(this.GetString("预约操作被拒绝,原因s"), strError); // result.ErrorInfo = "预约请求被拒绝: " + strError; result.ErrorCode = ErrorCode.DupItemBarcode; return result; } } // end of "new" // 为写回读者、册记录做准备 // byte[] timestamp = null; byte[] output_timestamp = null; string strOutputPath = ""; #if NO RmsChannel channel = sessioninfo.Channels.GetChannel(this.WsUrl); if (channel == null) { strError = "get channel error"; goto ERROR1; } #endif long lRet = 0; // 切割出单个的册条码号 string[] itembarcodes = strItemBarcodeList.Split(new char[] { ',' }); for (int i = 0; i < itembarcodes.Length; i++) { string strItemBarcode = itembarcodes[i].Trim(); if (String.IsNullOrEmpty(strItemBarcode) == true) continue; string strItemXml = ""; string strOutputItemRecPath = ""; // 册记录加锁 this.EntityLocks.LockForWrite(strItemBarcode); try { int nRedoCount = 0; REDO_LOAD: byte[] item_timestamp = null; // 获得册记录 // return: // -1 error // 0 not found // 1 命中1条 // >1 命中多于1条 nRet = this.GetItemRecXml( channel, strItemBarcode, out strItemXml, out strOutputItemRecPath, out item_timestamp, out strError); if (nRet == 0) { result.Value = -1; // text-level: 用户提示 result.ErrorInfo = string.Format(this.GetString("册条码号s不存在"), // "册条码号 {0} 不存在" strItemBarcode); // "册条码号 '" + strItemBarcode + "' 不存在"; result.ErrorCode = ErrorCode.ItemBarcodeNotFound; return result; } if (nRet == -1) { // text-level: 内部错误 strError = string.Format(this.GetString("读入册记录时发生错误s"), // "读入册记录时发生错误: {0}" strError); // "读入册记录时发生错误: " + strError; goto ERROR1; } if (nRet > 1) { // text-level: 内部错误 strError = string.Format(this.GetString("册条码号s有重复"), // "册条码号 '{0}' 有重复({1}条),无法进行预约操作。" strItemBarcode, nRet.ToString()); // "册条码号 '" + strItemBarcode + "' 有重复(" + nRet.ToString() + "条),无法进行预约操作。"; } XmlDocument itemdom = null; nRet = LibraryApplication.LoadToDom(strItemXml, out itemdom, out strError); if (nRet == -1) { // text-level: 内部错误 strError = string.Format(this.GetString("装载册记录进入XMLDOM时发生错误s"), // "装载册记录进入XML DOM时发生错误: {0}" strError); // "装载册记录进入XML DOM时发生错误: " + strError; goto ERROR1; } records.Add(strOutputItemRecPath, itemdom, item_timestamp); // TODO: 若册属于个人藏书,则不仅要求预约者是同一分馆的读者,另外还要求预约者是主人的好友。即,主人的读者记录中 firends 列表中有预约者 // 2012/9/13 // 检查一个册记录的馆藏地点是否符合馆代码列表要求 // return: // -1 检查过程出错 // 0 符合要求 // 1 不符合要求 nRet = CheckItemLibraryCode(itemdom, strLibraryCode, out strError); if (nRet == -1) goto ERROR1; if (nRet == 1) { strError = "册记录 '" + strItemBarcode + "' 因馆藏地而不能进行预约: " + strError; goto ERROR1; } // 2011/12/7 // 检查册记录状态 string strState = DomUtil.GetElementText(itemdom.DocumentElement, "state"); if (string.IsNullOrEmpty(strState) == false) { // text-level: 用户提示 strError = string.Format(this.GetString("册状态为s无法预约"), // "册 {0} 状态为 '{1}' ,无法进行预约操作。" strItemBarcode, strState, nRet.ToString()); goto ERROR1; } bool bOnShelf = false; bool bArrived = false; // 在册记录中添加或者删除预约信息 nRet = this.DoReservationItemXml( records, channel, strFunction, strReaderBarcode, sessioninfo.UserID, ref itemdom, out bOnShelf, out bArrived, out strError); if (nRet == -1) goto ERROR1; if (strFunction == "delete") { nRet = NotifyCancelArrive( channel, strItemBarcode, "", // strRefID 暂时不使用此参数 itemdom, strLibraryCode, strReaderBarcode, strReaderXml, out strError); if (nRet == -1) { this.WriteErrorLog("发出放弃取书通知(册条码号=" + strItemBarcode + ")时出错: " + strError); } } if (bOnShelf == true) OnShelfItemBarcodes.Add(strItemBarcode); if (bArrived == true) ArriveItemBarcodes.Add(strItemBarcode); // 写回册记录 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) { // text-level: 内部错误 strError = "写回册记录 '" + strOutputItemRecPath + "' 时反复遇到时间戳冲突, 超过10次重试仍然失败"; goto ERROR1; } nRedoCount++; goto REDO_LOAD; } // 当写操作发生错误时,将来用更科学的措施来undo, // 即把刚才增加的<request>元素找到后删除 goto ERROR1; } records.Remove(strOutputItemRecPath); } finally { this.EntityLocks.UnlockForWrite(strItemBarcode); } } // end of for // 在读者记录中加入或删除预约信息 // parameters: // strFunction "new"新增预约信息;"delete"删除预约信息; "merge"合并; "split"拆散 // return: // -1 error // 0 unchanged // 1 changed nRet = this.DoReservationReaderXml( strFunction, strItemBarcodeList, sessioninfo.UserID, ref readerdom, out strError); if (nRet == -1) goto ERROR1; CachedRecord reader_record = records.Find(strOutputReaderRecPath); if (nRet == 1 || (reader_record != null && reader_record.Changed)) { // 野蛮写入 lRet = channel.DoSaveTextRes(strOutputReaderRecPath, readerdom.OuterXml, false, "content,ignorechecktimestamp", reader_timestamp, out output_timestamp, out strOutputPath, out strError); if (lRet == -1) goto ERROR1; records.Remove(strOutputReaderRecPath); DomUtil.SetElementText(domOperLog.DocumentElement, "action", strFunction); DomUtil.SetElementText(domOperLog.DocumentElement, "readerBarcode", strReaderBarcode); DomUtil.SetElementText(domOperLog.DocumentElement, "itemBarcodeList", strItemBarcodeList); string strOperTime = this.Clock.GetClock(); DomUtil.SetElementText(domOperLog.DocumentElement, "operator", sessioninfo.UserID); // 操作者 DomUtil.SetElementText(domOperLog.DocumentElement, "operTime", strOperTime); // 操作时间 nRet = this.OperLog.WriteOperLog(domOperLog, sessioninfo.ClientAddress, out strError); if (nRet == -1) { // text-level: 内部错误 strError = "Reservation() API 写入日志时发生错误: " + strError; goto ERROR1; } // 写入统计指标 if (this.Statis != null) this.Statis.IncreaseEntryValue( strLibraryCode, "出纳", "预约次", 1); } // 对当前在普通架的图书,立即发出到书通知 if (this.CanReserveOnshelf == true && OnShelfItemBarcodes.Count > 0) { // 只通知第一个在架的册 string strItemBarcode = OnShelfItemBarcodes[0]; if (string.IsNullOrEmpty(strItemBarcode) == true) { strError = "内部错误:OnShelfItemBarcodes 的第一个元素为空。数组情况 '" + StringUtil.MakePathList(OnShelfItemBarcodes) + "'"; goto ERROR1; } List<string> DeletedNotifyRecPaths = null; // 被删除的通知记录。不用。 // 通知预约到书的操作 // 出于对读者库加锁方面的便利考虑, 单独做了此函数 // return: // -1 error // 0 没有找到<request>元素 nRet = DoReservationNotify( null, channel, strReaderBarcode, false, // 不需要函数内加读者锁,因为这里已经加了 strItemBarcode, true, // 在普通架 true, // 需要修改当前册记录的<request>元素state属性 out DeletedNotifyRecPaths, out strError); if (nRet == -1) { // text-level: 用户提示 strError = string.Format(this.GetString("预约操作已经成功, 但是在架立即通知功能失败, 原因s"), // "预约操作已经成功, 但是在架立即通知功能失败, 原因: {0}" strError); // "预约操作已经成功, 但是在架立即通知功能失败, 原因: " + strError; goto ERROR1; } /* if (this.Statis != null) this.Statis.IncreaseEntryValue( strLibraryCode, "出纳", "预约到书册", 1); * */ // 给与成功提示 // text-level: 用户提示 string strMessage = string.Format(this.GetString("请注意,您刚提交的预约请求立即就得到了兑现"), // "请注意,您刚提交的预约请求立即就得到了兑现(预约到书通知消息也向您发出了,请注意查收)。所预约的册 {0} 为在架状态,已为您保留,您从现在起就可来图书馆办理借阅手续。" strItemBarcode); // "请注意,您刚提交的预约请求立即就得到了兑现(预约到书通知消息也向您发出了,请注意查收)。所预约的册 " + strItemBarcode + " 为在架状态,已为您保留,您从现在起就可来图书馆办理借阅手续。"; if (OnShelfItemBarcodes.Count > 1) { OnShelfItemBarcodes.Remove(strItemBarcode); string[] barcodelist = new string[OnShelfItemBarcodes.Count]; OnShelfItemBarcodes.CopyTo(barcodelist); // text-level: 用户提示 strMessage += string.Format(this.GetString("您在同一预约请求中也同时提交了其他在架状态的册"), // "您在同一预约请求中也同时提交了其他在架状态的册: {0}。因同一集合中的前述册 {1} 的生效,这些册同时被忽略。(如确要预约多个在架的册让它们都独立生效,请每次勾选一个后单独提交,而不要把多个册一次性提交。)" String.Join(",", barcodelist), strItemBarcode); // "您在同一预约请求中也同时提交了其他在架状态的册: " + String.Join(",", barcodelist) + "。因同一集合中的前述册 " + strItemBarcode + " 的生效,这些册同时被忽略。(如确要预约多个在架的册让它们都独立生效,请每次勾选一个后单独提交,而不要把多个册一次性提交。)"; } result.ErrorInfo = strMessage; } if (ArriveItemBarcodes.Count > 0) { string[] barcodelist = new string[ArriveItemBarcodes.Count]; ArriveItemBarcodes.CopyTo(barcodelist); // text-level: 用户提示 result.ErrorInfo += string.Format(this.GetString("册s在删除前已经处在到书状态"), // "册 {0} 在删除前已经处在“到书”状态。您刚刚删除了这(些)请求,这意味着您已经放弃取书。图书馆将顺次满足后面排队等待的预约者的请求,或允许其他读者借阅此书。(若您意图要去图书馆正常取书,请一定不要去删除这样的状态为“已到书”的请求,软件会在您取书后自动删除)" String.Join(",", barcodelist)); // "册 " + String.Join(",", barcodelist) + " 在删除前已经处在“到书”状态。您刚刚删除了这(些)请求,这意味着您已经放弃取书。图书馆将顺次满足后面排队等待的预约者的请求,或允许其他读者借阅此书。(若您意图要去图书馆正常取书,请一定不要去删除这样的状态为“已到书”的请求,软件会在您取书后自动删除)"; } } finally { this.ReaderLocks.UnlockForWrite(strReaderBarcode); #if DEBUG_LOCK_READER this.WriteErrorLog("Reservation 结束为读者加写锁 '" + strReaderBarcode + "'"); #endif } return result; ERROR1: result.Value = -1; result.ErrorInfo = strError; result.ErrorCode = ErrorCode.SystemError; return result; }
// 修改读者记录 // TODO: 是否要提供条码号重的情况下强制写入的功能? // 需要一并发来旧记录的原因, 是为了和数据库中当前可能已经变化了的记录进行比较, // 如果SetReaderInfo能覆盖的部分字段,这一部分没有发生实质性变化,整条记录仅仅是 // 流通实时信息发生了变化,本函数就能仍适当合并后保存记录,而不会返回错误,增加 // 了API的可用性。如果实际运用中不允许发回旧记录,可发来空字符串,就会牺牲上述 // 可用性,变成,不论数据库中当前记录的改变具体在那些字段范围,都只能报错返回了。 // paramters: // strAction 操作。new change delete changestate changeforegift forcenew forcechange forcedelete changereaderbarcode // strRecPath 希望保存到的记录路径。可以为空。 // strNewXml 希望保存的记录体 // strOldXml 原先获得的旧记录体。可以为空。 // baOldTimestamp 原先获得旧记录的时间戳。可以为空。 // strExistringXml 覆盖操作失败时,返回数据库中已经存在的记录,供前端参考 // strSavedXml 实际保存的新记录。内容可能和strNewXml有所差异。 // strSavedRecPath 实际保存的记录路径 // baNewTimestamp 实际保存后的新时间戳 // return: // result -1失败 0 正常 1部分字段被拒绝(注意这个是否实现?记得还有一个专门的错误码可以使用) // 权限: // 读者不能修改任何人的读者记录,包括他自己的。 // 工作人员则要看 setreaderinfo权限是否具备 // 特殊操作可能还需要 changereaderstate 和 changereaderforegift changereaderbarcode 权限 // 日志: // 要产生日志 public LibraryServerResult SetReaderInfo( SessionInfo sessioninfo, string strAction, string strRecPath, string strNewXml, string strOldXml, byte[] baOldTimestamp, out string strExistingXml, out string strSavedXml, out string strSavedRecPath, out byte[] baNewTimestamp, out DigitalPlatform.rms.Client.rmsws_localhost.ErrorCodeValue kernel_errorcode) { strExistingXml = ""; strSavedXml = ""; strSavedRecPath = ""; baNewTimestamp = null; string[] element_names = StringUtil.Append(_reader_element_names, this.PatronAdditionalFields.ToArray()); LibraryServerResult result = new LibraryServerResult(); LibraryApplication app = this; kernel_errorcode = DigitalPlatform.rms.Client.rmsws_localhost.ErrorCodeValue.NoError; bool bForce = false; if (strAction == "forcenew" || strAction == "forcechange" || strAction == "forcedelete") { if (StringUtil.IsInList("restore", sessioninfo.RightsOrigin) == false) { result.Value = -1; result.ErrorInfo = "修改读者信息的" + strAction + "操作被拒绝。不具备 restore 权限。"; result.ErrorCode = ErrorCode.AccessDenied; return result; } bForce = true; // 将strAction内容修改为不带有force前缀部分 strAction = strAction.Remove(0, "force".Length); } else { // 权限字符串 if (strAction == "changestate") { // 有setreaderinfo和changereaderstate之一均可 if (StringUtil.IsInList("setreaderinfo", sessioninfo.RightsOrigin) == false) { if (StringUtil.IsInList("changereaderstate", sessioninfo.RightsOrigin) == false) { result.Value = -1; result.ErrorInfo = "修改读者信息被拒绝。不具备 changereaderstate 权限。"; result.ErrorCode = ErrorCode.AccessDenied; return result; } } } else if (strAction == "changereaderbarcode") { if (StringUtil.IsInList("changereaderbarcode", sessioninfo.RightsOrigin) == false) { result.Value = -1; result.ErrorInfo = "修改读者信息被拒绝。不具备 changereaderbarcode 权限。"; result.ErrorCode = ErrorCode.AccessDenied; return result; } } else if (strAction == "changeforegift") { // changereaderforegift if (StringUtil.IsInList("changereaderforegift", sessioninfo.RightsOrigin) == false) { result.Value = -1; result.ErrorInfo = "changeforegift方式修改读者信息被拒绝。不具备 changereaderforegift 权限。"; result.ErrorCode = ErrorCode.AccessDenied; return result; } } else { if (StringUtil.IsInList("setreaderinfo", sessioninfo.RightsOrigin) == false) { result.Value = -1; result.ErrorInfo = "修改读者信息被拒绝。不具备 setreaderinfo 权限。"; result.ErrorCode = ErrorCode.AccessDenied; return result; } } } // 对读者身份的附加判断 if (strAction != "change" && sessioninfo.UserType == "reader") { // 不允许读者修改其他读者的记录,不允许读者创建读者记录.但是允许读者修改自己的记录中的某些元素 result.Value = -1; result.ErrorInfo = "读者身份执行 '" + strAction + "' 的修改读者信息操作操作被拒绝"; result.ErrorCode = ErrorCode.AccessDenied; return result; } string strError = ""; int nRet = 0; long lRet = 0; // 参数检查 if (strAction == "delete") { if (String.IsNullOrEmpty(strNewXml) == false) { strError = "strAction 值为 delete 时, strNewXml 参数必须为空"; goto ERROR1; } if (baNewTimestamp != null) { strError = "strAction 值 为delete 时, baNewTimestamp 参数必须为空"; goto ERROR1; } } else { // 非 delete 情况 strNewXml 则必须不为空 if (String.IsNullOrEmpty(strNewXml) == true) { strError = "strAction 值为 " + strAction + " 时, strNewXml 参数不能为空"; goto ERROR1; } } // 2007/11/12 if (strAction == "new") { if (String.IsNullOrEmpty(strOldXml) == false) { strError = "strAction 值为 new 时, strOldXml 参数必须为空"; goto ERROR1; } if (baOldTimestamp != null) { strError = "strAction 值为 new 时, baOldTimestamp 参数必须为空"; goto ERROR1; } } else { if (this.TestMode == true || sessioninfo.TestMode == true) { // 检查评估模式 // return: // -1 检查过程出错 // 0 可以通过 // 1 不允许通过 nRet = CheckTestModePath(strRecPath, out strError); if (nRet != 0) { strError = "修改读者记录的操作被拒绝: " + strError; goto ERROR1; } } } // 把旧记录装载到DOM XmlDocument domOldRec = new XmlDocument(); try { if (String.IsNullOrEmpty(strOldXml) == true) strOldXml = "<root />"; domOldRec.LoadXml(strOldXml); } catch (Exception ex) { strError = "strOldXml XML 记录装载到 DOM 时出错: " + ex.Message; goto ERROR1; } // 把要保存的新记录装载到DOM XmlDocument domNewRec = new XmlDocument(); try { if (String.IsNullOrEmpty(strNewXml) == true) strNewXml = "<root />"; domNewRec.LoadXml(strNewXml); } catch (Exception ex) { strError = "strNewXml XML 记录装载到 DOM 时出错: " + ex.Message; goto ERROR1; } string strOldBarcode = ""; string strNewBarcode = ""; // return: // -1 出错 // 0 相等 // 1 不相等 nRet = CompareTwoBarcode(domOldRec, domNewRec, out strOldBarcode, out strNewBarcode, out strError); if (nRet == -1) goto ERROR1; // 对读者身份的附加判断 if (strAction == "change" && sessioninfo.UserType == "reader") { /* // 暂时不允许读者自己修改任何读者的信息 // 今后修改为:读者只能修改自己的记录,而且只能修改某些字段(其他修改被忽略)。 result.Value = -1; result.ErrorInfo = "修改读者信息被拒绝。作为读者不能修改读者记录"; result.ErrorCode = ErrorCode.AccessDenied; return result; * */ if (sessioninfo.Account.Barcode != strNewBarcode) { result.Value = -1; result.ErrorInfo = "修改读者信息被拒绝。作为读者不能修改其他读者的读者记录"; result.ErrorCode = ErrorCode.AccessDenied; return result; } element_names = _selfchangeable_reader_element_names; } // 注意: oldDom 是前端提供过来的,显然前端可能会说谎,那么这个比较新旧条码号的结果就堪忧了。改进的办法可以是这里真正从读者库取出来,然后进行比较 bool bBarcodeChanged = false; if (nRet == 1) bBarcodeChanged = true; string strOldDisplayName = ""; string strNewDisplayName = ""; // return: // -1 出错 // 0 相等 // 1 不相等 nRet = CompareTwoDisplayName(domOldRec, domNewRec, out strOldDisplayName, out strNewDisplayName, out strError); if (nRet == -1) goto ERROR1; bool bDisplayNameChanged = false; if (nRet == 1) bDisplayNameChanged = true; string strLockBarcode = ""; if (strAction == "new" || strAction == "change" || strAction == "changestate" || strAction == "changereaderbarcode") strLockBarcode = strNewBarcode; else if (strAction == "delete") { // 顺便进行一些检查 if (String.IsNullOrEmpty(strNewBarcode) == false) { strError = "没有必要在 delete 操作的 strNewXml 参数中, 包含新记录内容...。相反,注意一定要在 strOldXml 参数中包含即将删除的原记录"; goto ERROR1; } strLockBarcode = strOldBarcode; } RmsChannel channel = sessioninfo.Channels.GetChannel(this.WsUrl); if (channel == null) { strError = "get channel error"; goto ERROR1; } // 加读者记录锁 if (String.IsNullOrEmpty(strLockBarcode) == false) { #if DEBUG_LOCK_READER app.WriteErrorLog("SetReaderInfo 开始为读者加写锁 '" + strLockBarcode + "'"); #endif app.ReaderLocks.LockForWrite(strLockBarcode); } try { // 2014/1/10 // 检查空条码号 if (// bBarcodeChanged == true && (strAction == "new" || strAction == "change" || strAction == "changestate" || strAction == "changeforegift" || strAction == "changereaderbarcode") && String.IsNullOrEmpty(strNewBarcode) == true ) { if (this.AcceptBlankReaderBarcode == false) { result.Value = -1; result.ErrorInfo = strError + "证条码号不能为空。保存操作失败"; result.ErrorCode = ErrorCode.InvalidReaderBarcode; return result; } } // 对读者证条码号查重,如果必要,并获得strRecPath if ( // bBarcodeChanged == true && (strAction == "new" || strAction == "change" || strAction == "changestate" || strAction == "changeforegift" || strAction == "changereaderbarcode") && String.IsNullOrEmpty(strNewBarcode) == false ) { #if NO // 验证条码号 if (this.VerifyBarcode == true) { // return: // 0 invalid barcode // 1 is valid reader barcode // 2 is valid item barcode int nResultValue = 0; // return: // -2 not found script // -1 出错 // 0 成功 nRet = this.DoVerifyBarcodeScriptFunction( null, sessioninfo.LibraryCodeList, strNewBarcode, out nResultValue, out strError); if (nRet == -2 || nRet == -1 || nResultValue != 1) { if (nRet == -2) strError = "library.xml 中没有配置条码号验证函数,无法进行条码号验证"; else if (nRet == -1) { strError = "验证读者证条码号的过程中出错" + (string.IsNullOrEmpty(strError) == true ? "" : ": " + strError); } else if (nResultValue != 1) { strError = "条码号 '" + strNewBarcode + "' 经验证发现不是一个合法的读者证条码号" + (string.IsNullOrEmpty(strError) == true ? "" : "(" + strError + ")"); } result.Value = -1; result.ErrorInfo = strError + "。保存操作失败"; result.ErrorCode = ErrorCode.InvalidReaderBarcode; return result; } } #endif List<string> aPath = null; // 本函数只负责查重, 并不获得记录体 // return: // -1 error // 其他 命中记录条数(不超过nMax规定的极限) nRet = app.SearchReaderRecDup( // sessioninfo.Channels, channel, strNewBarcode, 100, out aPath, out strError); if (nRet == -1) goto ERROR1; bool bDup = false; if (nRet == 0) { bDup = false; } else if (nRet == 1) // 命中一条 { Debug.Assert(aPath.Count == 1, ""); // 如果输入参数中没有指定strRecPath if (String.IsNullOrEmpty(strRecPath) == true) { if (strAction == "new") // 2006/12/23 add bDup = true; else strRecPath = aPath[0]; } else { if (aPath[0] == strRecPath) // 正好是自己 { bDup = false; } else { // 别的记录中已经使用了这个条码号 bDup = true; } } } else { Debug.Assert(nRet > 1, ""); bDup = true; } // 报错 if (bDup == true) { /* string[] pathlist = new string[aPath.Count]; aPath.CopyTo(pathlist); strError = "条码号 '" + strNewBarcode + "' 已经被下列读者记录使用了: " + String.Join(",", pathlist) + "。操作失败。"; * */ if (String.IsNullOrEmpty(strNewDisplayName) == false) strError = "证条码号 '" + strNewBarcode + "' 或 显示名 '" + strNewDisplayName + "' 已经被下列读者记录使用了: " + StringUtil.MakePathList(aPath) + "。操作失败。"; else strError = "证条码号 '" + strNewBarcode + "' 已经被下列读者记录使用了: " + StringUtil.MakePathList(aPath) + "。操作失败。"; // 2008/8/15 changed result.Value = -1; result.ErrorInfo = strError; result.ErrorCode = ErrorCode.ReaderBarcodeDup; return result; } } if (strAction == "new" || strAction == "change" || strAction == "changereaderbarcode" || strAction == "move") { // 注:要在 strRecPath 决定后再进行此调用 // return: // -3 条码号错误 // -2 not found script // -1 出错 // 0 成功 nRet = this.DoVerifyReaderFunction( sessioninfo, strAction, strRecPath, domNewRec, out strError); if (nRet != 0) { result.Value = -1; result.ErrorInfo = strError + "。保存操作失败"; if (nRet == -1) result.ErrorCode = ErrorCode.InvalidReaderBarcode; else result.ErrorCode = ErrorCode.SystemError; return result; } } // 对显示名检查和查重 if (bDisplayNameChanged == true && (strAction == "new" || strAction == "change" || strAction == "changestate" || strAction == "changeforegift" || strAction == "changereaderbarcode") && String.IsNullOrEmpty(strNewDisplayName) == false ) { { int nResultValue = -1; // 检查名字空间。 // return: // -2 not found script // -1 出错 // 0 成功 nRet = this.DoVerifyBarcodeScriptFunction( null, "", strNewDisplayName, out nResultValue, out strError); if (nRet == -2) { // 没有校验条码号功能,所以无法校验用户名和条码号名字空间的冲突 goto SKIP_VERIFY; } if (nRet == -1) { strError = "校验显示名 '" + strNewDisplayName + "' 和证条码号(空间)潜在冲突过程中(调用函数DoVerifyBarcodeScriptFunction()时)发生错误: " + strError; goto ERROR1; } Debug.Assert(nRet == 0, ""); if (nResultValue == -1) { strError = "校验显示名 '" + strNewDisplayName + "' 和证条码号(空间)潜在冲突过程中发生错误: " + strError; goto ERROR1; } if (nResultValue == 1) { // TODO: 需要多语种 strError = "显示名 '" + strNewDisplayName + "' 和读者证条码号名字空间发生冲突,不能作为显示名。"; goto ERROR1; } } SKIP_VERIFY: List<string> aPath = null; // 防止和其他读者的显示名相重复 // 本函数只负责查重, 并不获得记录体 // return: // -1 error // 其他 命中记录条数(不超过nMax规定的极限) nRet = app.SearchReaderDisplayNameDup( // sessioninfo.Channels, channel, strNewDisplayName, 100, out aPath, out strError); if (nRet == -1) goto ERROR1; bool bDup = false; if (nRet == 0) { bDup = false; } else if (nRet == 1) // 命中一条 { Debug.Assert(aPath.Count == 1, ""); // 如果输入参数中没有指定strRecPath if (String.IsNullOrEmpty(strRecPath) == true) { if (strAction == "new") bDup = true; else strRecPath = aPath[0]; } else { if (aPath[0] == strRecPath) // 正好是自己 { bDup = false; } else { // 别的记录中已经使用了这个条码号 bDup = true; } } } else { Debug.Assert(nRet > 1, ""); bDup = true; } // 报错 if (bDup == true) { strError = "显示名 '" + strNewDisplayName + "' 已经被下列读者记录使用了: " + StringUtil.MakePathList(aPath) + "。操作失败。"; result.Value = -1; result.ErrorInfo = strError; result.ErrorCode = ErrorCode.ReaderBarcodeDup; return result; } // 对工作人员帐户名进行查重。虽然不是强制性的,但是可以避免大部分误会 // 注:工作人员依然可以创建和读者显示名相重的帐户名 if (SearchUserNameDup(strNewDisplayName) == true) { strError = "显示名 '" + strNewDisplayName + "' 已经被工作人员帐户使用。操作失败。"; result.Value = -1; result.ErrorInfo = strError; result.ErrorCode = ErrorCode.ReaderBarcodeDup; return result; } } string strReaderDbName = ""; if (String.IsNullOrEmpty(strRecPath) == false) strReaderDbName = ResPath.GetDbName(strRecPath); // BUG. 缺乏'strReaderDbName = ' 2008/6/4 changed // 准备日志DOM XmlDocument domOperLog = new XmlDocument(); domOperLog.LoadXml("<root />"); DomUtil.SetElementText(domOperLog.DocumentElement, "operation", "setReaderInfo"); // 2014/11/17 if (bForce == true) { XmlNode node = DomUtil.SetElementText(domOperLog.DocumentElement, "style", "force"); } #if NO RmsChannel channel = sessioninfo.Channels.GetChannel(app.WsUrl); if (channel == null) { strError = "get channel error"; goto ERROR1; } #endif // 兑现一个命令 if (strAction == "new") { // 检查新记录的路径中的id部分是否正确 // 库名部分,前面已经统一检查过了 if (String.IsNullOrEmpty(strRecPath) == true) { // 当路径整个为空的时候,自动选用第一个读者库 if (String.IsNullOrEmpty(strReaderDbName) == true) { if (app.ReaderDbs.Count == 0) { strError = "dp2Library尚未定义读者库, 因此无法新创建读者记录。"; goto ERROR1; } // 选用当前用户能管辖的第一个读者库 // strReaderDbName = app.ReaderDbs[0].DbName; List<string> dbnames = app.GetCurrentReaderDbNameList(sessioninfo.LibraryCodeList); if (dbnames.Count > 0) strReaderDbName = dbnames[0]; else { strReaderDbName = ""; strError = "当前用户没有管辖任何读者库, 因此无法新创建读者记录。"; goto ERROR1; } } strRecPath = strReaderDbName + "/?"; } else { string strID = ResPath.GetRecordId(strRecPath); if (String.IsNullOrEmpty(strID) == true) { strError = "RecPath中id部分应当为'?'"; goto ERROR1; } // 2007/11/12 // 加上了这句话,就禁止了action为new时的定id保存功能。这个功能本来是被允许的。不过禁止后,更可避免概念混淆、出错。 if (strID != "?") { strError = "当strAction为new时,strRecPath必须为 读者库名/? 形态,或空(空表示取第一个读者库的当前最尾号)。(但目前strRecPath为'" + strRecPath + "')"; goto ERROR1; } } // 构造出适合保存的新读者记录 if (bForce == false) { // 主要是为了把待加工的记录中,可能出现的属于“流通信息”的字段去除,避免出现安全性问题 nRet = BuildNewReaderRecord(domNewRec, out strSavedXml, out strError); if (nRet == -1) goto ERROR1; } else { // 2008/5/29 strSavedXml = domNewRec.OuterXml; } string strLibraryCode = ""; // 观察一个读者记录路径,看看是不是在当前用户管辖的读者库范围内? if (app.IsCurrentChangeableReaderPath(strRecPath, sessioninfo.LibraryCodeList, out strLibraryCode) == false) { strError = "读者记录路径 '" + strRecPath + "' 的读者库不在当前用户管辖范围内"; goto ERROR1; } // 2014/7/4 if (this.VerifyReaderType == true) { XmlDocument domTemp = new XmlDocument(); domTemp.LoadXml(strSavedXml); // 检查一个册记录的读者类型是否符合值列表要求 // parameters: // return: // -1 检查过程出错 // 0 符合要求 // 1 不符合要求 nRet = CheckReaderType(domTemp, strLibraryCode, strReaderDbName, out strError); if (nRet == -1 || nRet == 1) { strError = strError + "。创建读者记录操作失败"; goto ERROR1; } } byte[] output_timestamp = null; string strOutputPath = ""; lRet = channel.DoSaveTextRes(strRecPath, strSavedXml, false, // include preamble? "content", baOldTimestamp, out output_timestamp, out strOutputPath, out strError); if (lRet == -1) { strSavedXml = ""; strSavedRecPath = strOutputPath; // 2011/9/6 add baNewTimestamp = output_timestamp; if (channel.OriginErrorCode == ErrorCodeValue.TimestampMismatch) { // 2011/9/6 add strError = "创建新读者记录的时候,数据库内核决定创建新记录的位置 '" + strOutputPath + "' 居然已经存在记录。这通常是因为该数据库的尾号不正常导致的。请提醒系统管理员及时处理这个故障。原始错误信息: " + strError; } else strError = "保存新记录的操作发生错误:" + strError; kernel_errorcode = channel.OriginErrorCode; goto ERROR1; } else // 成功 { DomUtil.SetElementText(domOperLog.DocumentElement, "libraryCode", strLibraryCode); // 读者所在的馆代码 DomUtil.SetElementText(domOperLog.DocumentElement, "action", "new"); // 不创建<oldRecord>元素 XmlNode node = DomUtil.SetElementText(domOperLog.DocumentElement, "record", strNewXml); DomUtil.SetAttr(node, "recPath", strOutputPath); // 新记录保存成功,需要返回信息元素。因为需要返回新的时间戳和实际保存的记录路径 strSavedRecPath = strOutputPath; // strSavedXml // 所真正保存的记录,可能稍有变化, 因此需要返回给前端 baNewTimestamp = output_timestamp; // 成功 if (this.Statis != null) this.Statis.IncreaseEntryValue( strLibraryCode, "修改读者信息", "创建新记录数", 1); } this.SessionTable.CloseSessionByReaderBarcode(strNewBarcode); } else if (strAction == "change" || strAction == "changestate" || strAction == "changeforegift" || strAction == "changereaderbarcode") { // DigitalPlatform.rms.Client.rmsws_localhost.ErrorCodeValue errorcode; // 执行"change"操作 // 1) 操作成功后, NewRecord中有实际保存的新记录,NewTimeStamp为新的时间戳 // 2) 如果返回TimeStampMismatch错,则OldRecord中有库中发生变化后的“原记录”,OldTimeStamp是其时间戳 // return: // -1 出错 // 0 成功 nRet = DoReaderChange( sessioninfo.LibraryCodeList, element_names, strAction, bForce, channel, strRecPath, domNewRec, domOldRec, baOldTimestamp, ref domOperLog, out strExistingXml, // strExistingRecord, out strSavedXml, // strNewRecord, out baNewTimestamp, out strError, out kernel_errorcode); if (nRet == -1) { // 失败 domOperLog = null; // 表示不必写入日志 goto ERROR1; } this.SessionTable.CloseSessionByReaderBarcode(strNewBarcode); // 2016/9/9 if (bBarcodeChanged && this.ChargingOperDatabase != null && this.ChargingOperDatabase.Enabled) this.ChargingOperDatabase.ChangePatronBarcode(strOldBarcode, strNewBarcode); strSavedRecPath = strRecPath; // 保存过程不会改变记录路径 } else if (strAction == "delete") { // return: // -2 记录中有流通信息,不能删除 // -1 出错 // 0 记录本来就不存在 // 1 记录成功删除 nRet = DoReaderOperDelete( sessioninfo.LibraryCodeList, element_names, sessioninfo, bForce, channel, strRecPath, strOldXml, baOldTimestamp, strOldBarcode, // strNewBarcode, domOldRec, ref strExistingXml, ref baNewTimestamp, ref domOperLog, ref kernel_errorcode, out strError); if (nRet == -1) goto ERROR1; if (nRet == -2) { result.Value = -1; result.ErrorInfo = strError; result.ErrorCode = ErrorCode.HasCirculationInfo; return result; } // 记录没有找到 if (nRet == 0) { result.Value = -1; result.ErrorInfo = strError; result.ErrorCode = ErrorCode.ReaderBarcodeNotFound; return result; } this.SessionTable.CloseSessionByReaderBarcode(strNewBarcode); } else { // 不支持的命令 strError = "不支持的操作命令 '" + strAction + "'"; goto ERROR1; } // 写入日志 if (domOperLog != null) { string strOperTime = app.Clock.GetClock(); DomUtil.SetElementText(domOperLog.DocumentElement, "operator", sessioninfo.UserID); // 操作者 DomUtil.SetElementText(domOperLog.DocumentElement, "operTime", strOperTime); // 操作时间 nRet = app.OperLog.WriteOperLog(domOperLog, sessioninfo.ClientAddress, out strError); if (nRet == -1) { strError = "SetReaderInfo() API 写入日志时发生错误: " + strError; goto ERROR1; } } } catch (Exception ex) { result.Value = -1; result.ErrorCode = ErrorCode.SystemError; result.ErrorInfo = "抛出异常:" + ex.Message; return result; } finally { if (String.IsNullOrEmpty(strLockBarcode) == false) { app.ReaderLocks.UnlockForWrite(strLockBarcode); #if DEBUG_LOCK_READER app.WriteErrorLog("SetReaderInfo 结束为读者加写锁 '" + strLockBarcode + "'"); #endif } } return result; ERROR1: result.Value = -1; result.ErrorInfo = strError; result.ErrorCode = ErrorCode.SystemError; return result; }
// 设置实用库信息 // strRootElementName 根元素名。如果为空,系统自会用<r>作为根元素 // strKeyAttrName key属性名。如果为空,系统自动会用k // strValueAttrName value属性名。如果为空,系统自动会用v public LibraryServerResult SetUtilInfo( SessionInfo sessioninfo, string strAction, string strDbName, string strFrom, string strRootElementName, string strKeyAttrName, string strValueAttrName, string strKey, string strValue) { string strError = ""; int nRet = 0; LibraryServerResult result = new LibraryServerResult(); string strPath = ""; string strXml = ""; byte[] timestamp = null; bool bRedo = false; if (String.IsNullOrEmpty(strRootElementName) == true) strRootElementName = "r"; // 最简单的缺省模式 if (String.IsNullOrEmpty(strKeyAttrName) == true) strKeyAttrName = "k"; if (String.IsNullOrEmpty(strValueAttrName) == true) strValueAttrName = "v"; RmsChannel channel = sessioninfo.Channels.GetChannel(this.WsUrl); if (channel == null) { strError = "get channel error"; goto ERROR1; } // 检索实用库记录的路径和记录体 // return: // -1 error(注:检索命中多条情况被当作错误返回) // 0 not found // 1 found nRet = SearchUtilPathAndRecord( // sessioninfo.Channels, channel, strDbName, strKey, strFrom, out strPath, out strXml, out timestamp, out strError); if (nRet == -1) goto ERROR1; // 如果动作为直接设置整个记录 if (strAction == "setrecord") { if (nRet == 0) { strPath = strDbName + "/?"; } strXml = strValue; } else { // 根据若干信息构造出记录 if (nRet == 0) { strPath = strDbName + "/?"; // strXml = "<" + strRootElementName + " " + strKeyAttrName + "='" + strKey + "' " + strValueAttrName + "='" + strValue + "'/>"; // 2011/12/11 XmlDocument dom = new XmlDocument(); dom.LoadXml("<" + strRootElementName + "/>"); DomUtil.SetAttr(dom.DocumentElement, strKeyAttrName, strKey); DomUtil.SetAttr(dom.DocumentElement, strValueAttrName, strValue); strXml = dom.DocumentElement.OuterXml; } else { string strPartXml = "/xpath/<locate>@" + strValueAttrName + "</locate><create>@" + strValueAttrName + "</create>"; strPath += strPartXml; strXml = strValue; } } #if NO RmsChannel channel = sessioninfo.Channels.GetChannel(this.WsUrl); if (channel == null) { strError = "get channel error"; goto ERROR1; } #endif byte[] baOutputTimeStamp = null; string strOutputPath = ""; int nRedoCount = 0; REDO: long lRet = channel.DoSaveTextRes(strPath, strXml, false, // bInlucdePreamble "ignorechecktimestamp", // style timestamp, out baOutputTimeStamp, out strOutputPath, out strError); if (lRet == -1) { if (bRedo == true) { if (channel.ErrorCode == ChannelErrorCode.TimestampMismatch && nRedoCount < 10) { timestamp = baOutputTimeStamp; nRedoCount++; goto REDO; } } goto ERROR1; } result.Value = 1; return result; ERROR1: result.Value = -1; result.ErrorCode = ErrorCode.SystemError; result.ErrorInfo = strError; return result; }
// TODO: 对于期记录,需要有限定期范围的能力 // 获得事项库中全部从属于strBiblioRecPath的记录信息 // 注:要求每类事项库都有一个“父记录”检索途径 // parameters: // strBiblioRecPath 书目记录路径,仅包含库名和id部分 // strStyle "onlygetpath" 仅返回每个路径(OldRecPath) // "getfirstxml" 是对onlygetpath的补充,仅获得第一个元素的XML记录,其余的依然只返回路径 // "query:父记录+期号|..." 使用特定的检索途径和检索词。...部分表示检索词,例如 1|2005|1|,默认前方一致 // items 返回的事项信息数组 // 权限:权限要在API外面判断(需要有get...s权限)。 // return: // Result.Value -1出错 0没有找到 其他 实体记录的个数 public LibraryServerResult GetItems( SessionInfo sessioninfo, string strBiblioRecPath, long lStart, long lCount, string strStyle, string strLang, out EntityInfo[] items) { items = null; LibraryServerResult result = new LibraryServerResult(); int nRet = 0; string strError = ""; // 规范化参数值 if (lCount == 0) lCount = -1; string strBiblioDbName = ResPath.GetDbName(strBiblioRecPath); string strBiblioRecId = ResPath.GetRecordId(strBiblioRecPath); // 获得书目库对应的事项库名 string strItemDbName = ""; nRet = this.GetItemDbName(strBiblioDbName, out strItemDbName, out strError); if (nRet == -1) goto ERROR1; if (nRet == 0) { strError = "书目库名 '" + strBiblioDbName + "' 没有找到"; goto ERROR1; } if (String.IsNullOrEmpty(strItemDbName) == true) { strError = "书目库名 '" + strBiblioDbName + "' 对应的" + this.ItemName + "库名没有定义"; result.Value = -1; result.ErrorInfo = strError; result.ErrorCode = ErrorCode.ItemDbNotDef; // 2016/4/15 return result; } RmsChannel channel = sessioninfo.Channels.GetChannel(this.App.WsUrl); if (channel == null) { strError = "get channel error"; goto ERROR1; } // 2016/10/6 // 从style字符串中得到 format:XXXX子串 string strQueryParam = StringUtil.GetStyleParam(strStyle, "query"); string strFrom = "父记录"; string strWord = strBiblioRecId; string strMatchStyle = "exact"; if (string.IsNullOrEmpty(strQueryParam) == false) { List<string> parts = StringUtil.ParseTwoPart(strQueryParam, "|"); strFrom = parts[0]; strWord = parts[1]; strMatchStyle = "left"; } // 检索事项库中全部从属于特定id的记录 string strQueryXml = "<target list='" + StringUtil.GetXmlStringSimple(strItemDbName + ":" + strFrom) // 2007/9/14 + "'><item><word>" + strWord + "</word><match>" + strMatchStyle + "</match><relation>=</relation><dataType>string</dataType><maxCount>-1</maxCount></item><lang>" + "zh" + "</lang></target>"; long lRet = channel.DoSearch(strQueryXml, this.DefaultResultsetName, "", // strOuputStyle out strError); if (lRet == -1) goto ERROR1; if (lRet == 0) { result.Value = 0; result.ErrorInfo = "没有找到"; return result; } int MAXPERBATCH = 100; int nResultCount = (int)lRet; if (lCount == -1) lCount = nResultCount - (int)lStart; // lStart是否越界 if (lStart >= (long)nResultCount) { strError = "lStart参数值 " + lStart.ToString() + " 超过了命中结果集的尾部。命中结果数量为 " + nResultCount.ToString(); goto ERROR1; } // 修正lCount if (lStart + lCount > (long)nResultCount) { lCount = (long)nResultCount - lStart; } // 是否超过每批最大值 if (lCount > MAXPERBATCH) lCount = MAXPERBATCH; /* if (nResultCount > 10000) { strError = "命中"+this.ItemName+"记录数 " + nResultCount.ToString() + " 超过 10000, 暂时不支持"; goto ERROR1; } * */ bool bOnlyGetPath = StringUtil.IsInList("onlygetpath", strStyle); bool bGetFirstXml = StringUtil.IsInList("getfirstxml", strStyle); string strColumnStyle = "id,xml,timestamp"; if (bOnlyGetPath) strColumnStyle = "id"; List<EntityInfo> iteminfos = new List<EntityInfo>(); /* int nStart = 0; int nPerCount = 100; * */ int nStart = (int)lStart; int nPerCount = Math.Min(MAXPERBATCH, (int)lCount); for (; ; ) { #if NO List<string> aPath = null; lRet = channel.DoGetSearchResult( this.DefaultResultsetName, nStart, nPerCount, strLang, null, out aPath, out strError); if (lRet == -1) goto ERROR1; if (aPath.Count == 0) { strError = "aPath.Count == 0"; goto ERROR1; } #endif Record[] searchresults = null; lRet = channel.DoGetSearchResult( this.DefaultResultsetName, nStart, nPerCount, strColumnStyle, strLang, null, out searchresults, out strError); if (lRet == -1) goto ERROR1; if (searchresults == null) { strError = "searchresults == null"; goto ERROR1; } if (searchresults.Length == 0) { strError = "searchresults.Length == 0"; goto ERROR1; } // 获得每条记录 // for (int i = 0; i < aPath.Count; i++) foreach (Record record in searchresults) { EntityInfo iteminfo = new EntityInfo(); iteminfo.OldRecPath = record.Path; if (bOnlyGetPath == true) { if (bGetFirstXml == false || iteminfos.Count > 0) { // iteminfo.OldRecPath = aPath[i]; goto CONTINUE; } } string strMetaData = ""; string strXml = ""; byte[] timestamp = null; string strOutputPath = ""; if (bGetFirstXml && iteminfos.Count == 0 && !(record.RecordBody != null && string.IsNullOrEmpty(record.RecordBody.Xml) == false)) { lRet = channel.GetRes(// aPath[i], record.Path, out strXml, out strMetaData, out timestamp, out strOutputPath, out strError); } else { lRet = 0; if (record.RecordBody != null) { strXml = record.RecordBody.Xml; timestamp = record.RecordBody.Timestamp; strOutputPath = record.Path; } else { strOutputPath = record.Path; iteminfo.ErrorCode = ErrorCodeValue.NotFound; } } if (lRet == -1) { // iteminfo.OldRecPath = aPath[i]; iteminfo.OldRecPath = record.Path; iteminfo.ErrorCode = channel.OriginErrorCode; iteminfo.ErrorInfo = channel.ErrorInfo; iteminfo.OldRecord = ""; iteminfo.OldTimestamp = null; iteminfo.NewRecPath = ""; iteminfo.NewRecord = ""; iteminfo.NewTimestamp = null; iteminfo.Action = ""; goto CONTINUE; } iteminfo.OldRecPath = strOutputPath; iteminfo.OldRecord = strXml; iteminfo.OldTimestamp = timestamp; iteminfo.NewRecPath = ""; iteminfo.NewRecord = ""; iteminfo.NewTimestamp = null; iteminfo.Action = ""; CONTINUE: iteminfos.Add(iteminfo); } // nStart += aPath.Count; nStart += searchresults.Length; if (nStart >= nResultCount) break; if (iteminfos.Count >= lCount) break; // 修正nPerCount if (iteminfos.Count + nPerCount > lCount) nPerCount = (int)lCount - iteminfos.Count; } // 挂接到结果中 #if NO items = new EntityInfo[iteminfos.Count]; for (int i = 0; i < iteminfos.Count; i++) { items[i] = iteminfos[i]; } #endif items = iteminfos.ToArray(); result.Value = nResultCount; // items.Length; return result; ERROR1: result.Value = -1; result.ErrorInfo = strError; result.ErrorCode = ErrorCode.SystemError; return result; }
// TODO: 对于期记录,需要有限定期范围的能力 // 获得事项库中全部从属于strBiblioRecPath的记录信息 // 注:要求每类事项库都有一个“父记录”检索途径 // parameters: // strBiblioRecPath 书目记录路径,仅包含库名和id部分 // strStyle "onlygetpath" 仅返回每个路径(OldRecPath) // "getfirstxml" 是对onlygetpath的补充,仅获得第一个元素的XML记录,其余的依然只返回路径 // items 返回的事项信息数组 // 权限:权限要在API外面判断(需要有get...s权限)。 // return: // Result.Value -1出错 0没有找到 其他 实体记录的个数 public LibraryServerResult GetItems( SessionInfo sessioninfo, string strBiblioRecPath, long lStart, long lCount, string strStyle, string strLang, out EntityInfo[] items) { items = null; LibraryServerResult result = new LibraryServerResult(); int nRet = 0; string strError = ""; // 规范化参数值 if (lCount == 0) lCount = -1; string strBiblioDbName = ResPath.GetDbName(strBiblioRecPath); string strBiblioRecId = ResPath.GetRecordId(strBiblioRecPath); // 获得书目库对应的事项库名 string strItemDbName = ""; nRet = this.GetItemDbName(strBiblioDbName, out strItemDbName, out strError); if (nRet == -1) goto ERROR1; if (nRet == 0) { strError = "书目库名 '" + strBiblioDbName + "' 没有找到"; goto ERROR1; } if (String.IsNullOrEmpty(strItemDbName) == true) { strError = "书目库名 '" + strBiblioDbName + "' 对应的"+this.ItemName+"库名没有定义"; goto ERROR1; } RmsChannel channel = sessioninfo.Channels.GetChannel(this.App.WsUrl); if (channel == null) { strError = "get channel error"; goto ERROR1; } // 检索事项库中全部从属于特定id的记录 string strQueryXml = "<target list='" + StringUtil.GetXmlStringSimple(strItemDbName + ":" + "父记录") // 2007/9/14 + "'><item><word>" + strBiblioRecId + "</word><match>exact</match><relation>=</relation><dataType>string</dataType><maxCount>-1</maxCount></item><lang>" + "zh" + "</lang></target>"; long lRet = channel.DoSearch(strQueryXml, this.DefaultResultsetName, "", // strOuputStyle out strError); if (lRet == -1) goto ERROR1; if (lRet == 0) { result.Value = 0; result.ErrorInfo = "没有找到"; return result; } int MAXPERBATCH = 100; int nResultCount = (int)lRet; if (lCount == -1) lCount = nResultCount - (int)lStart; // lStart是否越界 if (lStart >= (long)nResultCount) { strError = "lStart参数值 " + lStart.ToString() + " 超过了命中结果集的尾部。命中结果数量为 " + nResultCount.ToString(); goto ERROR1; } // 修正lCount if (lStart + lCount > (long)nResultCount) { lCount = (long)nResultCount - lStart; } // 是否超过每批最大值 if (lCount > MAXPERBATCH) lCount = MAXPERBATCH; /* if (nResultCount > 10000) { strError = "命中"+this.ItemName+"记录数 " + nResultCount.ToString() + " 超过 10000, 暂时不支持"; goto ERROR1; } * */ List<EntityInfo> iteminfos = new List<EntityInfo>(); /* int nStart = 0; int nPerCount = 100; * */ int nStart = (int)lStart; int nPerCount = Math.Min(MAXPERBATCH, (int)lCount); for (; ; ) { List<string> aPath = null; lRet = channel.DoGetSearchResult( this.DefaultResultsetName, nStart, nPerCount, strLang, null, out aPath, out strError); if (lRet == -1) goto ERROR1; if (aPath.Count == 0) { strError = "aPath.Count == 0"; goto ERROR1; } bool bOnlyGetPath = StringUtil.IsInList("onlygetpath", strStyle); bool bGetFirstXml = StringUtil.IsInList("getfirstxml", strStyle); // 获得每条记录 for (int i = 0; i < aPath.Count; i++) { EntityInfo iteminfo = new EntityInfo(); if (bOnlyGetPath == true) { if (bGetFirstXml == false || i > 0) { iteminfo.OldRecPath = aPath[i]; goto CONTINUE; } } string strMetaData = ""; string strXml = ""; byte[] timestamp = null; string strOutputPath = ""; lRet = channel.GetRes(aPath[i], out strXml, out strMetaData, out timestamp, out strOutputPath, out strError); if (lRet == -1) { iteminfo.OldRecPath = aPath[i]; iteminfo.ErrorCode = channel.OriginErrorCode; iteminfo.ErrorInfo = channel.ErrorInfo; iteminfo.OldRecord = ""; iteminfo.OldTimestamp = null; iteminfo.NewRecPath = ""; iteminfo.NewRecord = ""; iteminfo.NewTimestamp = null; iteminfo.Action = ""; goto CONTINUE; } iteminfo.OldRecPath = strOutputPath; iteminfo.OldRecord = strXml; iteminfo.OldTimestamp = timestamp; iteminfo.NewRecPath = ""; iteminfo.NewRecord = ""; iteminfo.NewTimestamp = null; iteminfo.Action = ""; CONTINUE: iteminfos.Add(iteminfo); } nStart += aPath.Count; if (nStart >= nResultCount) break; if (iteminfos.Count >= lCount) break; // 修正nPerCount if (iteminfos.Count + nPerCount > lCount) nPerCount = (int)lCount - iteminfos.Count; } // 挂接到结果中 items = new EntityInfo[iteminfos.Count]; for (int i = 0; i < iteminfos.Count; i++) { items[i] = iteminfos[i]; } result.Value = nResultCount; // items.Length; return result; ERROR1: result.Value = -1; result.ErrorInfo = strError; result.ErrorCode = ErrorCode.SystemError; return result; }
// 从册条码号(+册记录路径)获得种记录摘要,或者从订购记录路径、期记录路径、评注记录路径获得种记录摘要 // 权限: 需要具有getbibliosummary权限 // parameters: // strBiblioRecPathExclude 除开列表中的这些种路径, 才返回摘要内容, 否则仅仅返回种路径即可 public LibraryServerResult GetBiblioSummary( SessionInfo sessioninfo, RmsChannel channel, string strItemBarcode, string strConfirmItemRecPath, string strBiblioRecPathExclude, out string strBiblioRecPath, out string strSummary) { strBiblioRecPath = ""; strSummary = ""; string strError = ""; LibraryServerResult result = new LibraryServerResult(); // 权限判断 // 权限字符串 if (StringUtil.IsInList("getbibliosummary", sessioninfo.RightsOrigin) == false && StringUtil.IsInList("order", sessioninfo.RightsOrigin) == false) { result.Value = -1; result.ErrorInfo = "获取种摘要信息被拒绝。不具备order、getbibliosummary权限。"; result.ErrorCode = ErrorCode.AccessDenied; return result; } int nRet = 0; long lRet = 0; if (string.IsNullOrEmpty(strItemBarcode) == true && string.IsNullOrEmpty(strConfirmItemRecPath) == true) { strError = "strItemBarcode和strConfirmItemRecPath参数值不能同时为空"; goto ERROR1; } string strItemXml = ""; string strOutputItemPath = ""; string strMetaData = ""; /* RmsChannel channel = sessioninfo.Channels.GetChannel(this.WsUrl); if (channel == null) { strError = "channel == null"; goto ERROR1; } * */ // 特殊情况,通过种路径 string strHead = "@bibliorecpath:"; if (strItemBarcode.Length > strHead.Length && strItemBarcode.Substring(0, strHead.Length) == strHead) { strBiblioRecPath = strItemBarcode.Substring(strHead.Length); // 检查书目库名是否合法 string strTempBiblioDbName = ResPath.GetDbName(strBiblioRecPath); if (this.IsBiblioDbName(strTempBiblioDbName) == false) { strError = "strItemBarcode参数中析出的书目库路径 '" + strBiblioRecPath + "' 中,书目库名 '" + strTempBiblioDbName + "' 不是系统定义的书目库名"; goto ERROR1; } goto LOADBIBLIO; } bool bByRecPath = false; // 是否经过记录路径来获取的? if (string.IsNullOrEmpty(strItemBarcode) == false) { // 获得册记录 // return: // -1 error // 0 not found // 1 命中1条 // >1 命中多于1条 nRet = this.GetItemRecXml( channel, strItemBarcode, out strItemXml, out strOutputItemPath, out strError); if (nRet == 0) { result.Value = 0; result.ErrorInfo = "册记录没有找到"; result.ErrorCode = ErrorCode.NotFound; return result; } if (nRet == -1) goto ERROR1; } // 如果命中多于一条(或者没有条码号),并且已经有确定的册记录路径辅助判断 if (string.IsNullOrEmpty(strItemBarcode) == true || (nRet > 1 && String.IsNullOrEmpty(strConfirmItemRecPath) == false)) { // 检查路径中的库名,是不是实体库、订购库、期库、评注库名 nRet = CheckRecPath(strConfirmItemRecPath, "item,order,issue,comment", out strError); if (nRet != 1) goto ERROR1; byte[] item_timestamp = null; lRet = channel.GetRes(strConfirmItemRecPath, out strItemXml, out strMetaData, out item_timestamp, out strOutputItemPath, out strError); if (lRet == -1) { strError = "根据strConfirmItemRecPath '" + strConfirmItemRecPath + "' 获得记录失败: " + strError; goto ERROR1; } bByRecPath = true; } // 从册记录中获得从属的种id string strBiblioRecID = ""; XmlDocument dom = new XmlDocument(); try { dom.LoadXml(strItemXml); } catch (Exception ex) { strError = "册记录XML装载到DOM出错:" + ex.Message; goto ERROR1; } if (bByRecPath == true && string.IsNullOrEmpty(strItemBarcode) == false) // 2011/9/6 { // 这种情况需要核实册条码号 string strTempItemBarcode = DomUtil.GetElementText(dom.DocumentElement, "//barcode"); if (strTempItemBarcode != strItemBarcode) { strError = "通过册条码号 '" + strItemBarcode + "' 获取实体记录发现命中多条,然后自动用记录路径 '" + strConfirmItemRecPath + "' 来获取实体记录,虽然获取成功,但是发现所获取的记录中<barcode>元素中的册条码号 '" + strTempItemBarcode + "' 不符合要求的册条码号 '" + strItemBarcode + "。(后面)这种情况可能是由于实体记录发生过移动造成的。"; goto ERROR1; } } strBiblioRecID = DomUtil.GetElementText(dom.DocumentElement, "parent"); // if (String.IsNullOrEmpty(strBiblioRecID) == true) { strError = "种下属记录XML中<parent>元素缺乏或者值为空, 因此无法定位种记录"; goto ERROR1; } // 从配置文件中获得和实体库对应的书目库名 /* // 准备工作: 映射数据库名 nRet = this.GetGlobalCfg(sessioninfo.Channels, out strError); if (nRet == -1) goto ERROR1; * */ string strItemDbName = ResPath.GetDbName(strOutputItemPath); string strBiblioDbName = ""; // 根据书目下属库名, 找到对应的书目库名 // return: // -1 出错 // 0 没有找到 // 1 找到 nRet = this.GetBiblioDbNameByChildDbName(strItemDbName, out strBiblioDbName, out strError); if (nRet == -1) goto ERROR1; if (nRet == 0) { strError = "下属库名 '" + strItemDbName + "' 没有找到所从属的书目库名"; goto ERROR1; } string strBiblioXml = ""; strBiblioRecPath = strBiblioDbName + "/" + strBiblioRecID; LOADBIBLIO: // 看看是否在排除列表中 if (String.IsNullOrEmpty(strBiblioRecPathExclude) == false && IsInBarcodeList(strBiblioRecPath, strBiblioRecPathExclude) == true) { result.Value = 1; return result; } /* strSummary = ""; result.Value = 1; return result; * */ // 获得本地配置文件 string strLocalPath = ""; string strRemotePath = BrowseFormat.CanonicalizeScriptFileName( ResPath.GetDbName(strBiblioRecPath), "./cfgs/summary.fltx"); nRet = this.CfgsMap.MapFileToLocal( // sessioninfo.Channels, channel, strRemotePath, out strLocalPath, out strError); if (nRet == -1) goto ERROR1; if (nRet == 0) { // 配置.fltx文件不存在, 再试探.cs文件 strRemotePath = BrowseFormat.CanonicalizeScriptFileName( ResPath.GetDbName(strBiblioRecPath), "./cfgs/summary.cs"); nRet = this.CfgsMap.MapFileToLocal( // sessioninfo.Channels, channel, strRemotePath, out strLocalPath, out strError); if (nRet == -1) goto ERROR1; if (nRet == 0) { strError = strRemotePath + "不存在..."; goto ERROR1; } } bool bFltx = false; // 如果是一般.cs文件, 还需要获得.cs.ref配置文件 if (IsCsFileName(strRemotePath) == true) { string strTempPath = ""; nRet = this.CfgsMap.MapFileToLocal( // sessioninfo.Channels, channel, strRemotePath + ".ref", out strTempPath, out strError); if (nRet == -1) goto ERROR1; bFltx = false; } else { bFltx = true; } // 取得种记录 byte[] timestamp = null; lRet = channel.GetRes(strBiblioRecPath, out strBiblioXml, out strMetaData, out timestamp, out strOutputItemPath, out strError); if (lRet == -1) { strError = "获得种记录 '" + strBiblioRecPath + "' 时出错: " + strError; goto ERROR1; } string strMarc = ""; string strMarcSyntax = ""; { // 转换为MARC格式 // 将MARCXML格式的xml记录转换为marc机内格式字符串 // parameters: // bWarning ==true, 警告后继续转换,不严格对待错误; = false, 非常严格对待错误,遇到错误后不继续转换 // strMarcSyntax 指示marc语法,如果=="",则自动识别 // strOutMarcSyntax out参数,返回marc,如果strMarcSyntax == "",返回找到marc语法,否则返回与输入参数strMarcSyntax相同的值 nRet = MarcUtil.Xml2Marc(strBiblioXml, true, "", // this.CurMarcSyntax, out strMarcSyntax, out strMarc, out strError); if (nRet == -1) goto ERROR1; } string strFragment = ""; if (StringUtil.IsInList("coverimage", strBiblioRecPathExclude) == true) { // 获得封面图像 URL string strImageUrl = ScriptUtil.GetCoverImageUrl(strMarc, "SmallImage"); if (string.IsNullOrEmpty(strImageUrl) == false) { if (StringUtil.HasHead(strImageUrl, "uri:") == true) { strImageUrl = "object-path:" + strBiblioRecPath + "/object/" + strImageUrl.Substring(4); strFragment = "<img class='biblio pending' name='" + strImageUrl + "'/>"; } else { strFragment = "<img class='biblio' src='" + strImageUrl + "'/>"; } } } // 将种记录数据从XML格式转换为HTML格式 if (string.IsNullOrEmpty(strBiblioXml) == false) { if (bFltx == true) { string strFilterFileName = strLocalPath; nRet = this.ConvertBiblioXmlToHtml( strFilterFileName, strMarc, // strBiblioXml, strMarcSyntax, strBiblioRecPath, out strSummary, out strError); } else { nRet = this.ConvertRecordXmlToHtml( strLocalPath, strLocalPath + ".ref", strBiblioXml, strBiblioRecPath, // 2009/10/18 out strSummary, out strError); } if (nRet == -1) goto ERROR1; } else strSummary = ""; strSummary = strFragment + strSummary; result.Value = 1; return result; ERROR1: result.Value = -1; result.ErrorInfo = strError; result.ErrorCode = ErrorCode.SystemError; return result; }
// 将刚从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; }
// 获得书目信息 // 可以用多种格式:xml html text @??? summary outputpath // TODO: 将来可以增加在strBiblioRecPath中允许多种检索入口的能力,比方说允许使用itembarcode和itemconfirmpath(甚至和excludebibliopath)结合起来定位种。这样就完全可以取代原有GetBiblioSummary API的功能 // parameters: // strBiblioRecPath 种记录路径。如果在最后接续"$prev" "$next",表示前一条或后一条。 // formats 希望获得信息的若干格式。如果 == null,表示希望只返回timestamp (results返回空) // Result.Value -1出错 0没有找到 1找到 public LibraryServerResult GetBiblioInfos( SessionInfo sessioninfo, string strBiblioRecPath, string strBiblioXmlParam, // 2013/3/6 string[] formats, out string[] results, out byte[] timestamp) { results = null; timestamp = null; LibraryServerResult result = new LibraryServerResult(); int nRet = 0; long lRet = 0; string strError = ""; if (String.IsNullOrEmpty(strBiblioRecPath) == true) { strError = "strBiblioRecPath参数不能为空"; goto ERROR1; } // 检查特定格式的权限 if (formats != null) { foreach (string format in formats) { if (String.Compare(format, "summary", true) == 0) { // 权限字符串 if (StringUtil.IsInList("getbibliosummary", sessioninfo.RightsOrigin) == false && StringUtil.IsInList("order", sessioninfo.RightsOrigin) == false) { result.Value = -1; result.ErrorInfo = "获取种摘要信息被拒绝。不具备order、getbibliosummary权限。"; result.ErrorCode = ErrorCode.AccessDenied; return result; } } } } List<string> commands = new List<string>(); List<string> biblio_records = new List<string>(); if (StringUtil.HasHead(strBiblioRecPath, "@path-list:") == true) { string strText = strBiblioRecPath.Substring("@path-list:".Length); commands = StringUtil.SplitList(strText); // 如果前端发来记录,需要切割为独立的字符串 if (string.IsNullOrEmpty(strBiblioXmlParam) == false) { biblio_records = StringUtil.SplitList(strBiblioXmlParam.Replace("<!-->", new string((char)0x01, 1)), (char)0x01); if (commands.Count != biblio_records.Count) { strError = "strBiblioXml 参数中包含的子串个数 "+biblio_records.Count.ToString()+" 和 strBiblioRecPath 中包含记录路径子串个数 "+commands.Count.ToString()+" 应该相等才对"; goto ERROR1; } } } else { commands.Add(strBiblioRecPath); } int nPackageSize = 0; // 估算通讯包的尺寸 List<String> result_strings = new List<string>(); string strErrorText = ""; foreach (string command in commands) { if (string.IsNullOrEmpty(command) == true) continue; string strOutputPath = ""; string strCurrentBiblioRecPath = ""; // 检查数据库路径,看看是不是已经正规定义的编目库? // 分离出命令部分 string strCommand = ""; nRet = command.IndexOf("$"); if (nRet != -1) { strCommand = command.Substring(nRet + 1); strCurrentBiblioRecPath = command.Substring(0, nRet); } else strCurrentBiblioRecPath = command; string strBiblioDbName = ResPath.GetDbName(strCurrentBiblioRecPath); if (IsBiblioDbName(strBiblioDbName) == false) { strError = "书目记录路径 '" + strCurrentBiblioRecPath + "' 中包含的数据库名 '" + strBiblioDbName + "' 不是合法的书目库名"; goto ERROR1; } string strAccessParameters = ""; bool bRightVerified = false; // 检查存取权限 if (String.IsNullOrEmpty(sessioninfo.Access) == false) { string strAction = "*"; // return: // null 指定的操作类型的权限没有定义 // "" 定义了指定类型的操作权限,但是否定的定义 // 其它 权限列表。* 表示通配的权限列表 string strActionList = LibraryApplication.GetDbOperRights(sessioninfo.Access, strBiblioDbName, "getbiblioinfo"); if (strActionList == null) { if (LibraryApplication.GetDbOperRights(sessioninfo.Access, "", "getbiblioinfo") != null) { strError = "当前用户 '" + sessioninfo.UserID + "' 不具备 针对数据库 '" + strBiblioDbName + "' 执行 getbiblioinfo 操作的存取权限"; result.Value = -1; result.ErrorInfo = strError; result.ErrorCode = ErrorCode.AccessDenied; return result; } else { // 没有定义任何 getbiblioinfo 的存取定义(虽然定义了其他操作的存取定义) // 这时应该转过去看普通的权限 // TODO: 这种算法,速度较慢 goto VERIFY_NORMAL_RIGHTS; } } if (strActionList == "*") { // 通配 } else { if (IsInAccessList(strAction, strActionList, out strAccessParameters) == false) { strError = "当前用户 '" + sessioninfo.UserID + "' 不具备 针对数据库 '" + strBiblioDbName + "' 执行 getbiblioinfo 操作的存取权限"; result.Value = -1; result.ErrorInfo = strError; result.ErrorCode = ErrorCode.AccessDenied; return result; } } bRightVerified = true; } VERIFY_NORMAL_RIGHTS: if (bRightVerified == false) { // 权限字符串 if (StringUtil.IsInList("getbiblioinfo", sessioninfo.RightsOrigin) == false && StringUtil.IsInList("order", sessioninfo.RightsOrigin) == false) { result.Value = -1; result.ErrorInfo = "获取书目信息被拒绝。不具备order或getbiblioinfo权限。"; result.ErrorCode = ErrorCode.AccessDenied; return result; } } // 检查特定格式的权限 if (formats != null) { foreach (string format in formats) { if (String.Compare(format, "summary", true) == 0) { // 检查存取权限 if (String.IsNullOrEmpty(sessioninfo.Access) == false) { string strAction = "*"; // return: // null 指定的操作类型的权限没有定义 // "" 定义了指定类型的操作权限,但是否定的定义 // 其它 权限列表。* 表示通配的权限列表 string strActionList = LibraryApplication.GetDbOperRights(sessioninfo.Access, strBiblioDbName, "getbibliosummary"); if (strActionList == null) { if (LibraryApplication.GetDbOperRights(sessioninfo.Access, "", "getbibliosummary") != null) { strError = "当前用户 '" + sessioninfo.UserID + "' 不具备 针对数据库 '" + strBiblioDbName + "' 执行 getbibliosummary 操作的存取权限"; result.Value = -1; result.ErrorInfo = strError; result.ErrorCode = ErrorCode.AccessDenied; return result; } else { // 没有定义任何 getbibliosummary 的存取定义(虽然定义了其他操作的存取定义) // 这时应该转过去看普通的权限 // TODO: 这种算法,速度较慢 strActionList = "*"; // 为了能够通过验证 } } if (strActionList == "*") { // 通配 } else { if (IsInAccessList(strAction, strActionList, out strAccessParameters) == false) { strError = "当前用户 '" + sessioninfo.UserID + "' 不具备 针对数据库 '" + strBiblioDbName + "' 执行 getbibliosummary 操作的存取权限"; result.Value = -1; result.ErrorInfo = strError; result.ErrorCode = ErrorCode.AccessDenied; return result; } } } } } } string strBiblioXml = ""; string strMetaData = ""; if (String.IsNullOrEmpty(strBiblioXmlParam) == false) { // 前端已经发送过来一条记录 if (commands.Count > 1) { // 前端发来的书目记录是多个记录的形态 int index = commands.IndexOf(command); strBiblioXml = biblio_records[index]; } else strBiblioXml = strBiblioXmlParam; } else { RmsChannel channel = sessioninfo.Channels.GetChannel(this.WsUrl); if (channel == null) { strError = "channel == null"; goto ERROR1; } string strStyle = "timestamp,outputpath"; // "metadata,timestamp,outputpath"; if (formats != null && formats.Length > 0) strStyle += ",content,data"; string strSearchRecPath = strCurrentBiblioRecPath; // 用于实际检索的路径,用于显示 if (String.IsNullOrEmpty(strCommand) == false && (strCommand == "prev" || strCommand == "next")) { strStyle += "," + strCommand; strSearchRecPath += "," + strCommand; } lRet = channel.GetRes(strCurrentBiblioRecPath, strStyle, out strBiblioXml, out strMetaData, out timestamp, out strOutputPath, out strError); if (lRet == -1) { if (channel.ErrorCode == ChannelErrorCode.NotFound) { if (commands.Count == 1) { result.Value = 0; result.ErrorCode = ErrorCode.NotFound; // 2009/8/8 result.ErrorInfo = "书目记录 '" + strSearchRecPath + "' 不存在"; // 2009/8/8 return result; } // 填入空字符串 if (formats != null) { for (int i = 0; i < formats.Length; i++) { result_strings.Add(""); } // strErrorText += "书目记录 '" + strCurrentBiblioRecPath + "' 不存在;\r\n"; } continue; } strError = "获得书目记录 '" + strSearchRecPath + "' 时出错: " + strError; goto ERROR1; } // 2014/12/16 strCurrentBiblioRecPath = strOutputPath; // 2013/3/6 // 过滤字段内容 if (string.IsNullOrEmpty(strAccessParameters) == false || !(StringUtil.IsInList("writeres", sessioninfo.Rights) == true || StringUtil.IsInList("writeobject", sessioninfo.Rights) == true) ) { // 根据字段权限定义过滤出允许的内容 // return: // -1 出错 // 0 成功 // 1 有部分字段被修改或滤除 nRet = FilterBiblioByFieldNameList( sessioninfo.Rights, strAccessParameters, ref strBiblioXml, out strError); if (nRet == -1) goto ERROR1; } } if (formats != null) { List<string> temp_results = null; // TODO: getbibliopart 应能返回函数的值 nRet = BuildFormats( sessioninfo, strCurrentBiblioRecPath, strBiblioXml, strOutputPath, strMetaData, timestamp, formats, out temp_results, out strError); if (nRet == -1) goto ERROR1; strBiblioXml = ""; // 避免后面的循环以为是前端发过来的记录 int nSize = GetSize(temp_results); if (nPackageSize > 0 && nPackageSize + nSize >= QUOTA_SIZE) break; // 没有返回全部事项就中断了 nPackageSize += nSize; if (string.IsNullOrEmpty(strError) == false) strErrorText += strError; if (temp_results != null) result_strings.AddRange(temp_results); } } // end of each command // 复制到结果中 if (result_strings.Count > 0) { results = new string[result_strings.Count]; result_strings.CopyTo(results); if (String.IsNullOrEmpty(strErrorText) == false) { // 统一报错 // strError = strErrorText; // goto ERROR1; result.ErrorInfo = strError; // 2014/1/8 } } result.Value = 1; return result; ERROR1: result.Value = -1; result.ErrorInfo = strError; result.ErrorCode = ErrorCode.SystemError; return result; }
// 获得种次号尾号 public LibraryServerResult GetOneClassTailNumber( SessionInfo sessioninfo, string strArrangeGroupName, string strClass, out string strTailNumber) { strTailNumber = ""; string strError = ""; LibraryServerResult result = new LibraryServerResult(); if (String.IsNullOrEmpty(strArrangeGroupName) == true) { strError = "strArrangeGroupName参数值不能为空"; goto ERROR1; } RmsChannel channel = sessioninfo.Channels.GetChannel(this.WsUrl); if (channel == null) { strError = "get channel error"; goto ERROR1; } string strPath = ""; string strXml = ""; byte[] timestamp = null; // 检索尾号记录的路径和记录体 // return: // -1 error // 0 not found // 1 found int nRet = SearchOneClassTailNumberPathAndRecord( // sessioninfo.Channels, channel, strArrangeGroupName, strClass, out strPath, out strXml, out timestamp, out strError); if (nRet == -1) goto ERROR1; if (nRet == 0) { result.ErrorCode = ErrorCode.NotFound; result.ErrorInfo = strError; result.Value = 0; return result; } XmlDocument dom = new XmlDocument(); try { dom.LoadXml(strXml); } catch (Exception ex) { strError = "尾号记录 '" + strPath + "' XML装入DOM时发生错误: " + ex.Message; goto ERROR1; } strTailNumber = DomUtil.GetAttr(dom.DocumentElement, "v"); result.Value = 1; return result; ERROR1: result.Value = -1; result.ErrorCode = ErrorCode.SystemError; result.ErrorInfo = strError; return result; }
// 通知读者推荐的新书到书 // parameters: // strLibraryCodeList 要通知的读者所从属的馆代码列表。空表示只通知全局读者 public LibraryServerResult NotifyNewBook( SessionInfo sessioninfo, string strBiblioRecPath, string strLibraryCodeList) { string strError = ""; LibraryServerResult result = new LibraryServerResult(); int nRet = 0; // 检查 strLibraryCodeList 中的馆代码是否全在当前用户管辖之下 if (sessioninfo.GlobalUser == false) { if (FullyContainIn(strLibraryCodeList, sessioninfo.LibraryCodeList) == false) { strError = "所请求的馆代码 '" + strLibraryCodeList + "' 不是完全包含于当前用户的管辖范围馆代码 '"+sessioninfo.LibraryCodeList+"' 中"; goto ERROR1; } } RmsChannel channel = sessioninfo.Channels.GetChannel(this.WsUrl); if (channel == null) { strError = "get channel error"; goto ERROR1; } // 探测书目记录有没有下属的评注记录 List<DeleteEntityInfo> commentinfos = null; long lHitCount = 0; // return: // -1 error // 0 not exist entity dbname // 1 exist entity dbname nRet = this.CommentItemDatabase.SearchChildItems(channel, strBiblioRecPath, "return_record_xml", // 在DeleteEntityInfo结构中返回OldRecord内容, 并且不要检查流通信息 out lHitCount, out commentinfos, out strError); if (nRet == -1) goto ERROR1; if (nRet == 0) { Debug.Assert(commentinfos.Count == 0, ""); } // 如果没有评注记录,则不必通知 if (commentinfos == null || commentinfos.Count == 0) { result.Value = 0; // 表示没有可通知的 return result; } // List<string> suggestors = new List<string>(); Hashtable suggestor_table = new Hashtable(); // 操作者 --> 馆代码 foreach (DeleteEntityInfo info in commentinfos) { if (string.IsNullOrEmpty(info.OldRecord) == true) continue; XmlDocument domExist = new XmlDocument(); try { domExist.LoadXml(info.OldRecord); } catch (Exception ex) { strError = "评注记录 '" + info.RecPath + "' 装载进入DOM时发生错误: " + ex.Message; goto ERROR1; } // 是否为推荐者 string strType = DomUtil.GetElementText(domExist.DocumentElement, "type"); if (strType != "订购征询") continue; string strOrderSuggestion = DomUtil.GetElementText(domExist.DocumentElement, "orderSuggestion"); if (strOrderSuggestion != "yes") continue; string strLibraryCode = DomUtil.GetElementText(domExist.DocumentElement, "libraryCode"); // 检查读者所从属的馆代码是否在列表中 if (string.IsNullOrEmpty(strLibraryCodeList) == true && string.IsNullOrEmpty(strLibraryCode) == true) { // 全局的读者,全局的馆代码要求 } else { if (StringUtil.IsInList(strLibraryCode, strLibraryCodeList) == false) continue; // 不是列表中的分馆的读者不要通知。因为一旦通知了,会发生这部分读者到自己的分馆借不到书的窘况 } // 获得创建者用户名 XmlNode node = domExist.DocumentElement.SelectSingleNode("operations/operation[@name='create']"); if (node == null) continue; string strOperator = DomUtil.GetAttr(node, "operator"); if (string.IsNullOrEmpty(strOperator) == true) continue; // suggestors.Add(strOperator); // 从评注记录中获得<libraryCode>元素,这是创建评注记录时刻的操作者的馆代码 // 这样就可以不必根据读者记录路径来推导读者的馆代码 suggestor_table[strOperator] = strLibraryCode; // 自然就去重了 } if (suggestor_table.Count == 0) { result.Value = 0; // 表示没有可通知的 return result; } // 获得书目记录 #if NO RmsChannel channel = sessioninfo.Channels.GetChannel(this.WsUrl); if (channel == null) { strError = "channel == null"; goto ERROR1; } #endif string strMetaData = ""; string strOutputPath = ""; byte[] exist_timestamp = null; string strBiblioXml = ""; // 先读出数据库中此位置的已有记录 long lRet = channel.GetRes(strBiblioRecPath, out strBiblioXml, out strMetaData, out exist_timestamp, out strOutputPath, out strError); if (lRet == -1) goto ERROR1; if (strBiblioRecPath != strOutputPath) { strError = "根据路径 '" + strBiblioRecPath + "' 读入原有记录时,发现返回的路径 '" + strOutputPath + "' 和前者不一致"; goto ERROR1; } // 创建书目摘要 string[] formats = new string[1]; formats[0] = "summary"; List<string> temp_results = null; nRet = BuildFormats( sessioninfo, strBiblioRecPath, strBiblioXml, "", // strOutputPath, // 册记录的路径 "", // strMetaData, // 册记录的metadata null, formats, out temp_results, out strError); if (nRet == -1) goto ERROR1; if (temp_results == null || temp_results.Count == 0) { strError = "temp_results error"; goto ERROR1; } string strSummary = temp_results[0]; // 去重 // StringUtil.RemoveDupNoSort(ref suggestors); foreach (string id in suggestor_table.Keys) { if (string.IsNullOrEmpty(id) == true) continue; string strLibraryCode = (string)suggestor_table[id]; #if NO string strReaderXml = ""; byte[] reader_timestamp = null; string strOutputReaderRecPath = ""; int nIsReader = -1; // -1 不清楚 0 不是读者 1 是读者 // return: // -1 error // 0 not found // 1 命中1条 // >1 命中多于1条 nRet = this.GetReaderRecXml( sessioninfo.Channels, id, out strReaderXml, out strOutputReaderRecPath, out reader_timestamp, out strError); if (nRet == -1) { // text-level: 内部错误 strError = "读入读者记录 '" + id + "' 时发生错误: " + strError; goto ERROR1; } if (nRet == 0 || string.IsNullOrEmpty(strReaderXml) == true) nIsReader = 0; // 不是读者 else nIsReader = 1; // 获得读者从属的馆代码 string strLibraryCode = ""; if (nIsReader == 1) { // 观察一个读者记录路径,看看是不是在当前用户管辖的读者库范围内? if (this.IsCurrentChangeableReaderPath(strOutputReaderRecPath, sessioninfo.LibraryCodeList, out strLibraryCode) == false) { continue; // 读者的馆代码不在当前用户管辖范围内 } // 检查读者所从属的馆代码是否在列表中 if (string.IsNullOrEmpty(strLibraryCodeList) == true && string.IsNullOrEmpty(strLibraryCode) == true) { // 全局的读者,全局的馆代码要求 } else { if (StringUtil.IsInList(strLibraryCode, strLibraryCodeList) == false) continue; // 不是列表中的分馆的读者不要通知。因为一旦通知了,会发生这部分读者到自己的分馆借不到书的窘况 } } else { // 工作人员帐户,获得工作人员的馆代码 UserInfo userinfo = null; // return: // -1 出错 // 0 没有找到 // 1 找到 nRet = GetUserInfo(id, out userinfo, out strError); if (nRet == -1) { strError = "获取帐户 '" + id + "' 的信息时出错: " + strError; goto ERROR1; } if (nRet == 0) continue; // 没有这个用户 // 检查工作人员管辖的图书馆和 strLibraryCodeList 之间的交叉情况 Debug.Assert(userinfo != null, ""); strLibraryCode = Cross(userinfo.LibraryCode, strLibraryCodeList); if (strLibraryCode == null) continue; // 和 strLibraryCodeList 没有交集 } #endif string strBody = "尊敬的读者:\r\n\r\n您推荐订购的图书\r\n\r\n------\r\n" + strSummary + "\r\n------\r\n\r\n已经到达图书馆,欢迎您到图书馆借阅或阅览。感谢您对图书馆工作的大力支持。"; nRet = MessageNotify( sessioninfo, strLibraryCode, id, "", // strReaderXml, "新书到书通知", // strTitle, strBody, "text", // strMime, // "text", "图书馆", "新书到书通知", out strError); if (nRet == -1) goto ERROR1; } result.Value = 0; return result; ERROR1: result.Value = -1; result.ErrorInfo = strError; result.ErrorCode = ErrorCode.SystemError; return result; }
// (根据一定排架体系)检索出某一类的同类书的索取号 // parameters: // strArrangeGroupName 排架体系名。如果为"!xxx"形式,表示通过馆藏地点名来暗示排架体系名 public LibraryServerResult SearchOneClassCallNumber( SessionInfo sessioninfo, string strArrangeGroupName, string strClass, string strResultSetName, out string strQueryXml) { strQueryXml = ""; string strError = ""; LibraryServerResult result = new LibraryServerResult(); if (String.IsNullOrEmpty(strArrangeGroupName) == true) { strError = "strArrangeGroupName参数值不能为空"; goto ERROR1; } if (strArrangeGroupName[0] == '!') { string strTemp = GetArrangeGroupName(strArrangeGroupName.Substring(1)); if (strTemp == null) { strError = "馆藏地点名 " + strArrangeGroupName.Substring(1) + " 没有找到对应的排架体系名"; goto ERROR1; } strArrangeGroupName = strTemp; } // <location>元素数组 XmlNodeList nodes = this.LibraryCfgDom.DocumentElement.SelectNodes("//callNumber/group[@name='" + strArrangeGroupName + "']/location"); if (nodes.Count == 0) { strError = "library.xml中尚未配置有关 '" + strArrangeGroupName + "' 的<callNumber>/<group>/<location>相关参数"; goto ERROR1; } string strTargetList = ""; // 遍历所有实体库 for (int i = 0; i < this.ItemDbs.Count; i++) { string strItemDbName = this.ItemDbs[i].DbName; if (String.IsNullOrEmpty(strItemDbName) == true) continue; if (String.IsNullOrEmpty(strTargetList) == false) strTargetList += ";"; strTargetList += strItemDbName + ":索取类号"; } int nCount = 0; // 构造检索式 for (int i = 0; i < nodes.Count; i++) { XmlNode node = nodes[i]; string strLocationName = DomUtil.GetAttr(node, "name"); if (String.IsNullOrEmpty(strLocationName) == true) continue; if (nCount > 0) { Debug.Assert(String.IsNullOrEmpty(strQueryXml) == false, ""); strQueryXml += "<operator value='OR'/>"; } strLocationName = strLocationName.Replace("*", "%"); /* strQueryXml += "<item><word>" + StringUtil.GetXmlStringSimple(strLocationName + "|" + strClass) + "</word><match>exact</match><relation>=</relation><dataType>string</dataType><maxCount>-1</maxCount></item><lang>zh</lang>"; * */ strQueryXml += "<item><word>" + StringUtil.GetXmlStringSimple(strLocationName + "|" + strClass + "/") + "</word><match>left</match><relation>=</relation><dataType>string</dataType><maxCount>-1</maxCount></item><lang>zh</lang>"; nCount++; } strQueryXml = "<target list='" + StringUtil.GetXmlStringSimple(strTargetList) // 2007/9/14 + "'>" + strQueryXml + "</target>"; RmsChannel channel = sessioninfo.Channels.GetChannel(this.WsUrl); if (channel == null) { strError = "get channel error"; goto ERROR1; } long lRet = channel.DoSearch(strQueryXml, strResultSetName, // "default", "keyid", // "", // strOuputStyle out strError); if (lRet == -1) goto ERROR1; if (lRet == 0) { result.Value = 0; result.ErrorInfo = "not found"; result.ErrorCode = ErrorCode.NotFound; return result; } result.Value = lRet; return result; ERROR1: result.Value = -1; result.ErrorCode = ErrorCode.SystemError; result.ErrorInfo = strError; return result; }
// 入馆登记 // result.Value -1 出错 其他 特定门(strGateName)的本次的累计量 public LibraryServerResult PassGate( SessionInfo sessioninfo, string strReaderBarcode, string strGateName, string strResultTypeList, out string[] results) { string strError = ""; int nRet = 0; results = null; int nResultValue = 0; LibraryServerResult result = new LibraryServerResult(); if (string.IsNullOrEmpty(strReaderBarcode) == false) { string strOutputCode = ""; // 把二维码字符串转换为读者证条码号 // parameters: // strReaderBcode [out]读者证条码号 // return: // -1 出错 // 0 所给出的字符串不是读者证号二维码 // 1 成功 nRet = DecodeQrCode(strReaderBarcode, out strOutputCode, out strError); if (nRet == -1) goto ERROR1; if (nRet == 1) { // strQrCode = strBarcode; strReaderBarcode = strOutputCode; } } if (sessioninfo.UserType == "reader") { // TODO: 如果使用身份证号,似乎这里会遇到阻碍 if (strReaderBarcode != sessioninfo.Account.Barcode) { result.Value = -1; result.ErrorInfo = "获得读者信息被拒绝。作为读者只能对自己进行入馆登记操作"; result.ErrorCode = ErrorCode.AccessDenied; return result; } } RmsChannel channel = sessioninfo.Channels.GetChannel(this.WsUrl); if (channel == null) { strError = "get channel error"; goto ERROR1; } string strReaderXml = ""; string strOutputReaderRecPath = ""; string strLibraryCode = ""; // 加读者记录锁 #if DEBUG_LOCK_READER this.WriteErrorLog("PassGate 开始为读者加读锁 '" + strReaderBarcode + "'"); #endif this.ReaderLocks.LockForRead(strReaderBarcode); try // 读者记录锁定范围开始 { // 读入读者记录 byte[] reader_timestamp = null; nRet = this.GetReaderRecXml( // sessioninfo.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; } nRet = this.GetLibraryCode(strOutputReaderRecPath, out strLibraryCode, out strError); if (nRet == -1) goto ERROR1; if (string.IsNullOrEmpty(strLibraryCode) == false) strGateName = strLibraryCode + ":" + strGateName; // 看看读者记录所从属的读者库的馆代码,是否被当前用户管辖 if (String.IsNullOrEmpty(strOutputReaderRecPath) == false) { // 检查当前操作者是否管辖这个读者库 // 观察一个读者记录路径,看看是不是在当前用户管辖的读者库范围内? if (this.IsCurrentChangeableReaderPath(strOutputReaderRecPath, sessioninfo.LibraryCodeList) == false) { strError = "读者记录路径 '" + strOutputReaderRecPath + "' 从属的读者库不在当前用户管辖范围内"; goto ERROR1; } } /* XmlDocument readerdom = null; nRet = LibraryApplication.LoadToDom(strReaderXml, out readerdom, out strError); if (nRet == -1) { strError = "装载读者记录进入XML DOM时发生错误: " + strError; goto ERROR1; } * */ // 增量总量 if (this.Statis != null) this.Statis.IncreaseEntryValue( strLibraryCode, "入馆人次", "所有门之总量", 1); // 增量特定门的累计量 if (this.Statis != null) nResultValue = this.Statis.IncreaseEntryValue( strLibraryCode, "入馆人次", String.IsNullOrEmpty(strGateName) == true ? "(blank)" : strGateName, (int)1); if (this.PassgateWriteToOperLog == true) { XmlDocument domOperLog = new XmlDocument(); domOperLog.LoadXml("<root />"); #if NO DomUtil.SetElementText(domOperLog.DocumentElement, "libraryCode", strLibraryCode); // 读者所在的馆代码 #endif DomUtil.SetElementText(domOperLog.DocumentElement, "operation", "passgate"); DomUtil.SetElementText(domOperLog.DocumentElement, "readerBarcode", strReaderBarcode); DomUtil.SetElementText(domOperLog.DocumentElement, "libraryCode", strLibraryCode); DomUtil.SetElementText(domOperLog.DocumentElement, "gateName", strGateName); DomUtil.SetElementText(domOperLog.DocumentElement, "operator", sessioninfo.UserID); string strOperTime = this.Clock.GetClock(); DomUtil.SetElementText(domOperLog.DocumentElement, "operTime", strOperTime); nRet = this.OperLog.WriteOperLog(domOperLog, sessioninfo.ClientAddress, out strError); if (nRet == -1) { strError = "PassGate() API 写入日志时发生错误: " + strError; goto ERROR1; } } } // 读者记录锁定范围结束 finally { this.ReaderLocks.UnlockForRead(strReaderBarcode); #if DEBUG_LOCK_READER this.WriteErrorLog("PassGate 结束为读者加读锁 '" + strReaderBarcode + "'"); #endif } if (String.IsNullOrEmpty(strResultTypeList) == true) { results = null; // 不返回任何结果 goto END1; } string[] result_types = strResultTypeList.Split(new char[] { ',' }); results = new string[result_types.Length]; for (int i = 0; i < result_types.Length; i++) { string strResultType = result_types[i]; if (String.Compare(strResultType, "xml", true) == 0) { results[i] = strReaderXml; } // else if (String.Compare(strResultType, "html", true) == 0) else if (IsResultType(strResultType, "html") == true) { string strReaderRecord = ""; // 将读者记录数据从XML格式转换为HTML格式 nRet = this.ConvertReaderXmlToHtml( sessioninfo, this.CfgDir + "\\readerxml2html.cs", this.CfgDir + "\\readerxml2html.cs.ref", strLibraryCode, strReaderXml, strOutputReaderRecPath, // 2009/10/18 OperType.None, null, "", strResultType, out strReaderRecord, out strError); if (nRet == -1) { strError = "ConvertReaderXmlToHtml()出错(脚本程序为" + this.CfgDir + "\\readerxml2html.cs" + "): " + strError; goto ERROR1; } results[i] = strReaderRecord; } // else if (String.Compare(strResultType, "text", true) == 0) else if (IsResultType(strResultType, "text") == true) { string strReaderRecord = ""; // 将读者记录数据从XML格式转换为text格式 nRet = this.ConvertReaderXmlToHtml( sessioninfo, this.CfgDir + "\\readerxml2text.cs", this.CfgDir + "\\readerxml2text.cs.ref", strLibraryCode, strReaderXml, strOutputReaderRecPath, // 2009/10/18 OperType.None, null, "", strResultType, out strReaderRecord, out strError); if (nRet == -1) { strError = "ConvertReaderXmlToHtml()出错(脚本程序为" + this.CfgDir + "\\readerxml2html.cs" + "): " + strError; goto ERROR1; } results[i] = strReaderRecord; } else { strError = "未知的结果类型 '" + strResultType + "'"; goto ERROR1; } } END1: result.Value = nResultValue; return result; ERROR1: result.Value = -1; result.ErrorCode = ErrorCode.SystemError; result.ErrorInfo = strError; return result; }