Ejemplo n.º 1
0
        // 在册记录中加入预约信息
        // TODO: 要关注本函数到底保存了 itemdom 代表的册记录没有?因为这样会引起时间戳发生变化
        // parameters:
        //      strFunction "new"新增预约信息;"delete"删除预约信息
        //      bOnShelf    strFunction为"new"的情况下,如果册本来就没有人借阅,并且当前读者为预约该册的第一人,则bOnShelf返回true
        //      bArrived    strFunciont为"delete"的情况下,删除了状态为"arrived"的预约请求
        public int DoReservationItemXml(
            CachedRecordCollection records,
            RmsChannel channel,
            string strFunction,
            string strReaderBarcode,
            string strOperator,
            ref XmlDocument itemdom,
            out bool bOnShelf,
            out bool bArrived,
            out string strError)
        {
            strError = "";
            bOnShelf = false;
            bArrived = false;

            if (String.IsNullOrEmpty(strReaderBarcode) == true)
            {
                // text-level: 用户提示
                strError = this.GetString("读者证条码号不能为空");    // 读者证条码号不能为空
                return -1;
            }

            XmlNode root = null;

            root = itemdom.DocumentElement.SelectSingleNode("reservations");
            if (root == null)
            {
                root = itemdom.CreateElement("reservations");
                root = itemdom.DocumentElement.AppendChild(root);
            }
            // 看看是否已经存在元素
            XmlNode nodeRequest = root.SelectSingleNode("request[@reader='" + strReaderBarcode + "']");

            if (String.Compare(strFunction, "new", true) == 0)
            {
                // 检查是否没有被人借阅
                string strBorrower = DomUtil.GetElementText(itemdom.DocumentElement,
                    "borrower");
                string strState = DomUtil.GetElementText(itemdom.DocumentElement,
                    "state");
                string strItemBarcode = DomUtil.GetElementText(itemdom.DocumentElement,
                    "barcode");

                XmlNodeList nodesRequest = itemdom.DocumentElement.SelectNodes("reservations/request");
                if (String.IsNullOrEmpty(strBorrower) == true
                    && nodesRequest.Count == 0)
                {
                    // 2009/10/19 
                    // 状态为“加工中”
                    if (IncludeStateProcessing(strState) == true
                        && this.CanReserveOnshelf == false)
                    {
                        // text-level: 用户提示
                        strError = string.Format(this.GetString("不能预约加工中的册s"), // "不能预约状态为加工中的册 {0}"
                            strItemBarcode);
                        return -1;
                    }

                    if (this.CanReserveOnshelf == false)
                    {
                        // text-level: 用户提示
                        strError = string.Format(this.GetString("不能预约在架的册s"), // "不能预约在架(未被借出的)册 {0}"
                            strItemBarcode);
                        // "不能预约在架(未被借出的)册 " + strItemBarcode;
                        return -1;
                    }

                    if (IncludeStateProcessing(strState) == false)   // 只有不包含“加工中”的,才马上通知。否则只能等以后状态改变时通知
                    {
                        // 如果本来就没有人借阅,而且当前读者是第一个预约该册的
                        bOnShelf = true;
                    }
                }

                if (nodeRequest == null)
                {
                    nodeRequest = itemdom.CreateElement("request");
                    nodeRequest = root.AppendChild(nodeRequest);
                    DomUtil.SetAttr(nodeRequest, "reader", strReaderBarcode);
                }

                // 请求时间
                DomUtil.SetAttr(nodeRequest, "requestDate", this.Clock.GetClock());
                // 操作者
                DomUtil.SetAttr(nodeRequest, "operator", strOperator);
            }

            if (String.Compare(strFunction, "delete", true) == 0)
            {
                if (nodeRequest != null)
                {
                    // 删除前要检查状态,是不是arrived
                    string strState = DomUtil.GetAttr(nodeRequest, "state");
                    if (strState == "arrived")
                    {
                        // TODO: 是否需要短信通知当前读者,这样意味着已经到的书放弃取。

                        string strItemBarcode = DomUtil.GetElementText(itemdom.DocumentElement,
                            "barcode");

                        string strQueueRecXml = "";
                        byte[] baQueueRecTimestamp = null;
                        string strQueueRecPath = "";

                        // 获得预约到书队列记录
                        // return:
                        //      -1  error
                        //      0   not found
                        //      1   命中1条
                        //      >1  命中多于1条
                        int nRet = GetArrivedQueueRecXml(
                            // channels,
                            channel,
                            strItemBarcode,
                            out strQueueRecXml,
                            out baQueueRecTimestamp,
                            out strQueueRecPath,
                            out strError);
                        if (nRet == -1)
                            return -1;
                        if (nRet >= 1)
                        {
                            XmlDocument queue_rec_dom = new XmlDocument();
                            try
                            {
                                queue_rec_dom.LoadXml(strQueueRecXml);
                            }
                            catch (Exception ex)
                            {
                                // text-level: 内部错误
                                strError = "预约队列记录XML装入DOM时失败: " + ex.Message;
                                return -1;
                            }
                            nRet = DoNotifyNext(
                                records,
                                channel,
                                strQueueRecPath,
                                queue_rec_dom,
                                baQueueRecTimestamp,
                                out strError);
                            if (nRet == -1)
                                return -1;
                            if (nRet == 1)
                            {
                                // 需要归架
                                // 册记录中<location>需要去掉#reservation,相关<request>元素也需要删除
                                string strLocation = DomUtil.GetElementText(itemdom.DocumentElement,
                                    "location");
                                // StringUtil.RemoveFromInList("#reservation", true, ref strLocation);
                                strLocation = StringUtil.GetPureLocationString(strLocation);
                                DomUtil.SetElementText(itemdom.DocumentElement,
                                    "location", strLocation);

                            }
                        }

                        // 给出返回状态
                        bArrived = true;

                    } // end of -- if (strState == "arrived")

                    // 经过 DoNotifyNext() 以后 itemdom 内容可能会发生变化
                    nodeRequest = root.SelectSingleNode("request[@reader='" + strReaderBarcode + "']");
                    if (nodeRequest != null && nodeRequest.ParentNode != null)
                        nodeRequest.ParentNode.RemoveChild(nodeRequest);
                } // end of -- if (nodeRequest != null)
            }

            return 0;
        }
Ejemplo n.º 2
0
        // text-level: 内部处理
        // 通知下一个预约者,或者(因没有下一个预约者了)而归架
        // 调用前,需要先获得预约队列记录
        // return:
        //      -1  error
        //      0   正常
        //      1   后面已经没有预约者,已通知管理员归架
        public int DoNotifyNext(
            CachedRecordCollection records,
            RmsChannel channel,
            string strQueueRecPath,
            XmlDocument queue_rec_dom,
            byte[] baQueueRecTimeStamp,
            out string strError)
        {
            strError = "";
            long lRet = 0;
            int nRet = 0;

            // RmsChannel channel = null;
            byte[] output_timestamp = null;

            string strState = DomUtil.GetElementText(queue_rec_dom.DocumentElement,
                "state");

            string strNotifyDate = DomUtil.GetElementText(queue_rec_dom.DocumentElement,
                "notifyDate");
            // XmlNode nodeItemBarcode = null;
            string strItemBarcode = DomUtil.GetElementText(queue_rec_dom.DocumentElement,
                "itemBarcode"/*, out nodeItemBarcode*/);

            // 2015/5/7
            string strRefID = DomUtil.GetElementText(queue_rec_dom.DocumentElement,
    "refID");

            string strReaderBarcode = DomUtil.GetElementText(queue_rec_dom.DocumentElement,
                "readerBarcode");

            bool bOnShelf = false;
#if NO
            // <itemBarcode>元素是否有onShelf属性。
            if (nodeItemBarcode != null)
            {
                string strOnShelf = DomUtil.GetAttr(nodeItemBarcode, "onShelf");
                if (strOnShelf.ToLower() == "true"
                    || strOnShelf.ToLower() == "yes"
                    || strOnShelf.ToLower() == "on")
                    bOnShelf = true;
            }
#endif
            // 2015/5/7
            // <onShelf> 元素
            {
                string strOnShelf = DomUtil.GetElementText(queue_rec_dom.DocumentElement, "onShelf");
                if (DomUtil.IsBooleanTrue(strOnShelf, false) == true)
                    bOnShelf = true;
            }

            // 2015/5/7
            if (string.IsNullOrEmpty(strItemBarcode) == true && string.IsNullOrEmpty(strRefID) == false)
            {
                strItemBarcode = "@refID:" + strRefID;
            }

            // 要通知下一位预约者

            string strReservationReaderBarcode = "";

            // 清除读者和册记录中的已到预约事项,并提取下一个预约读者证条码号
            // 本函数还负责清除册记录中以前残留的state=arrived的<request>元素
            nRet = this.ClearArrivedInfo(
                records,
                channel,
                strReaderBarcode,
                strItemBarcode,
                bOnShelf,
                out strReservationReaderBarcode,
                out strError);
            if (nRet == -1)
                return -1;

            // 3) 通知预约到书的操作
            List<string> DeletedNotifyRecPaths = null;  // 被删除的通知记录。

            if (String.IsNullOrEmpty(strReservationReaderBarcode) == false)
            {
                // 通知下一读者

                // 出于对读者库加锁方面的便利考虑, 单独做了此函数
                // return:
                //      -1  error
                //      0   没有找到<request>元素
                //      1   已成功处理
                nRet = this.DoReservationNotify(
                    records,
                    channel,
                    strReservationReaderBarcode,
                    true,
                    strItemBarcode,
                    bOnShelf,
                    false,   // 不要修改当前册记录的<request> state属性,因为前面ClearArrivedInfo()中已经调用了DoItemReturnReservationCheck(), 修改了当前册的<request> state属性。
                    out DeletedNotifyRecPaths,
                    out strError);
                if (nRet == -1)
                    return -1;
            }
            else
            {
                // outof 的记录何时删除?
                // 册记录中的馆藏地点 #reservation何时消除?一个是现在就消除,一个是盘点模块扫描条码时消除。

                // 把记录状态修改为 outofreservation
                DomUtil.SetElementText(queue_rec_dom.DocumentElement,
                    "state",
                    "outof");

                // channel = channels.GetChannel(this.WsUrl);

                string strOutputPath = "";
                lRet = channel.DoSaveTextRes(strQueueRecPath,
                    queue_rec_dom.OuterXml,
                    false,
                    "content,ignorechecktimestamp",
                    baQueueRecTimeStamp,
                    out output_timestamp,
                    out strOutputPath,
                    out strError);
                if (lRet == -1)
                {
                    strError = "写回预约到书记录 '" + strQueueRecPath + "' 时发生错误: " + strError;
                    return -1;
                }

                // TODO: 通知馆员进行上架操作
                // 可以在系统某目录不断追加到一个文本文件,工作人员可以查对。
                // 格式:每行 册条码号 最后一个预约的读者证条码号
                if (String.IsNullOrEmpty(this.StatisDir) == false)
                {
                    string strLogFileName = this.StatisDir + "\\outof_reservation_" + Statis.GetCurrentDate() + ".txt";
                    StreamUtil.WriteText(strLogFileName, strItemBarcode + " " + strReaderBarcode + "\r\n");
                }

                return 1;
            }

            // 4) 删除当前通知记录

            // 2007/7/5 
            bool bAlreadeDeleted = false;
            if (DeletedNotifyRecPaths != null)
            {
                if (DeletedNotifyRecPaths.IndexOf(strQueueRecPath) != -1)
                    bAlreadeDeleted = true;
            }

            if (bAlreadeDeleted == false)
            {
                lRet = channel.DoDeleteRes(
                    strQueueRecPath,
                    baQueueRecTimeStamp,
                    out output_timestamp,
                    out strError);
                if (lRet == -1)
                {
                    strError = "DoNotifyNext()删除通知记录 '" + strQueueRecPath + "' 时失败: " + strError;
                    return -1;
                }
            }

            return 0;
        }
Ejemplo n.º 3
0
        // text-level: 内部处理
        // 通知预约到书的操作
        // 出于对读者库加锁方面的便利考虑, 单独做了此函数
        // 注:本函数可能要删除部分通知记录
        // parameters:
        //      strItemBarcodeParam  册条码号。可以使用 "@refID:" 前缀
        // return:
        //      -1  error
        //      0   没有找到<request>元素
        //      1   已成功处理
        public int DoReservationNotify(
            CachedRecordCollection records,
            RmsChannel channel,
            string strReservationReaderBarcode,
            bool bNeedLockReader,
            string strItemBarcodeParam,
            bool bOnShelf,
            bool bModifyItemRecord,
            out List<string> DeletedNotifyRecPaths,
            out string strError)
        {
            int nRet = 0;
            strError = "";
            long lRet = 0;
            DeletedNotifyRecPaths = null;

            // 获得相关读者记录
            string strReaderXml = "";
            string strOutputReaderRecPath = "";

            string strItemXml = "";

            // 加读者记录锁
            if (bNeedLockReader == true)
            {
#if DEBUG_LOCK_READER
                this.WriteErrorLog("DoReservationNotify 开始为读者加写锁 '" + strReservationReaderBarcode + "'");
#endif
                this.ReaderLocks.LockForWrite(strReservationReaderBarcode);
            }
            try
            {
                // 读入读者记录
                nRet = this.GetReaderRecXml(
                    //sessioninfo,
                    // channels,
                    channel,
                    strReservationReaderBarcode,
                    out strReaderXml,
                    out strOutputReaderRecPath,
                    out strError);
                if (nRet == 0)
                {
                    strError = "读者证条码号 '" + strReservationReaderBarcode + "' 不存在";
                    return -1;
                }
                if (nRet == -1)
                {
                    strError = "读入读者记录时发生错误: " + strError;
                    return -1;
                }

                XmlDocument readerdom = null;

                CachedRecord reader_record = null;
                if (records != null)
                    reader_record = records.Find(strOutputReaderRecPath);

                if (reader_record == null)
                {
                    nRet = LibraryApplication.LoadToDom(strReaderXml,
                        out readerdom,
                        out strError);
                    if (nRet == -1)
                    {
                        strError = "装载读者记录进入XML DOM时发生错误: " + strError;
                        return -1;
                    }
                }
                else
                    readerdom = reader_record.Dom;

                byte[] timestamp = null;
                byte[] output_timestamp = null;
                string strOutputPath = "";

#if NO
                RmsChannel channel = channels.GetChannel(this.WsUrl);
                if (channel == null)
                {
                    strError = "get channel error";
                    return -1;
                }
#endif

                XmlNodeList nodes = readerdom.DocumentElement.SelectNodes("reservations/request");
                XmlNode readerRequestNode = null;
                string strItems = "";
                for (int i = 0; i < nodes.Count; i++)
                {
                    readerRequestNode = nodes[i];
                    strItems = DomUtil.GetAttr(readerRequestNode, "items");
                    if (IsInBarcodeList(strItemBarcodeParam, strItems) == true)
                        goto FOUND;
                }

                return 0;   // not found request
            FOUND:
                Debug.Assert(readerRequestNode != null, "");

                // 将相关册中的<request>元素清除
                // 因为预约的时候可能列举了很多册,但只要其中一个到书,其它册的预约信息就需要解除了
                string[] barcodes = strItems.Split(new char[] { ',' });
                for (int i = 0; i < barcodes.Length; i++)
                {
                    string strBarcode = barcodes[i].Trim();
                    if (String.IsNullOrEmpty(strBarcode) == true)
                        continue;
                    if (strBarcode == strItemBarcodeParam
                        && bModifyItemRecord == false)
                    {
                        continue;   // 这条册记录已经处理过了
                    }

                    // 读入册记录
                    string strCurrentItemXml = "";
                    string strOutputItemRecPath = "";
                    // 获得册记录
                    // return:
                    //      -1  error
                    //      0   not found
                    //      1   命中1条
                    //      >1  命中多于1条
                    nRet = this.GetItemRecXml(
                        //sessioninfo,
                        channel,
                        strBarcode,
                        out strCurrentItemXml,
                        out strOutputItemRecPath,
                        out strError);
                    if (nRet == 0)
                    {
                        strError = "册条码号 '" + strBarcode + "' 不存在";
                        continue;
                    }
                    if (nRet == -1)
                    {
                        strError = "读入册记录 '" + strBarcode + "' 时发生错误: " + strError;
                        return -1;
                    }

                    XmlDocument itemdom = null;

                    CachedRecord item_record = null;
                    if (records != null)
                        item_record = records.Find(strOutputItemRecPath);

                    if (item_record == null)
                    {
                        nRet = LibraryApplication.LoadToDom(strCurrentItemXml,
                            out itemdom,
                            out strError);
                        if (nRet == -1)
                        {
                            strError = "装载册记录进入XML DOM时发生错误: " + strError;
                            return -1;
                        }
                    }
                    else
                        itemdom = item_record.Dom;

                    if (strBarcode == strItemBarcodeParam)
                        strItemXml = strCurrentItemXml;

                    if (strBarcode == strItemBarcodeParam)
                    {
                        string strTempReservationReaderBarcode = "";

                        // TODO: 确认一下这个函数内部不会保存册记录
                        // 如果正好是当前册记录,需要设置arrived状态
                        // 如果为丢失处理,本函数的调用者需要通知等待者:书已经丢失了,不用再等待
                        // parameters:
                        //      bMaskLocationReservation    不要给<location>打上#reservation标记
                        // return:
                        //      -1  error
                        //      0   没有修改
                        //      1   进行过修改
                        nRet = DoItemReturnReservationCheck(
                            bOnShelf,   // 当册现在还在架上的时候,册记录的 <location> 就没有 #reservation。这样在借书环节检查册是否被预约的时候,就不能只看 <location> 了
                            ref itemdom,
                            out strTempReservationReaderBarcode,
                            out strError);
                        if (nRet == -1)
                        {
                            strError = "修改册记录'" + strBarcode + "' 预约到书状态时(DoItemReturnReservationCheck)发生错误: " + strError;
                            return -1;
                        }
                        // 对册记录如果没有改动
                        if (nRet == 0)
                            continue;
                    }
                    else
                    {
                        // 如果是同一请求中的其他册记录

                        // 删除对应的<request>元素
                        XmlNode itemrequestnode = itemdom.DocumentElement.SelectSingleNode("reservations/request[@reader='" + strReservationReaderBarcode + "']");
                        if (itemrequestnode == null)
                            continue;

                        itemrequestnode.ParentNode.RemoveChild(itemrequestnode);
                    }

                    if (item_record == null)
                    {
                        // 写回册记录
                        lRet = channel.DoSaveTextRes(strOutputItemRecPath,
                            itemdom.OuterXml,
                            false,
                            "content,ignorechecktimestamp",
                            timestamp,
                            out output_timestamp,
                            out strOutputPath,
                            out strError);
                        if (lRet == -1)
                        {
                            strError = "写回册记录'" + strBarcode + "' (记录路径'" + strOutputItemRecPath + "')时发生错误: " + strError;
                            return -1;
                        }
                    }
                    else
                        item_record.Changed = true;

                } // end of for

                // 读者记录中为对应的<request>元素打上状态记号
                DomUtil.SetAttr(readerRequestNode, "state", "arrived");
                // 到达时间
                DomUtil.SetAttr(readerRequestNode, "arrivedDate", this.Clock.GetClock());
                // 实际到达的一个册条码号 2007/1/18 
                DomUtil.SetAttr(readerRequestNode, "arrivedItemBarcode", strItemBarcodeParam);

                if (reader_record == null)
                {
                    // 写回读者记录
                    lRet = channel.DoSaveTextRes(strOutputReaderRecPath,
                        readerdom.OuterXml,
                        false,
                        "content,ignorechecktimestamp",
                        timestamp,
                        out output_timestamp,
                        out strOutputPath,
                        out strError);
                    if (lRet == -1)
                        return -1;
                }
                else
                    reader_record.Changed = true;

                strReaderXml = readerdom.DocumentElement.OuterXml;
            }
            finally
            {
                if (bNeedLockReader == true)
                {
                    this.ReaderLocks.UnlockForWrite(strReservationReaderBarcode);
#if DEBUG_LOCK_READER
                    this.WriteErrorLog("DoReservationNotify 结束为读者加写锁 '" + strReservationReaderBarcode + "'");
#endif
                }
            }

            string strLibraryCode = "";
            nRet = this.GetLibraryCode(strOutputReaderRecPath,
                out strLibraryCode,
                out strError);
            if (nRet == -1)
                return -1;

            if (string.IsNullOrEmpty(strItemXml) == true)
            {
                // 读入册记录
                string strOutputItemRecPath = "";
                // 获得册记录
                // return:
                //      -1  error
                //      0   not found
                //      1   命中1条
                //      >1  命中多于1条
                nRet = this.GetItemRecXml(
                    channel,
                    strItemBarcodeParam,
                    out strItemXml,
                    out strOutputItemRecPath,
                    out strError);
                if (nRet == 0)
                {
                    strError = "册条码号 '" + strItemBarcodeParam + "' 不存在";
                }
                if (nRet == -1)
                {
                    strError = "读入册记录 '" + strItemBarcodeParam + "' 时发生错误: " + strError;
                    return -1;
                }

                CachedRecord temp_record = null;
                if (records != null)
                    temp_record = records.Find(strOutputItemRecPath);

                if (temp_record != null
                    && temp_record.Dom != null
                    && temp_record.Dom.DocumentElement != null)
                    strItemXml = temp_record.Dom.DocumentElement.OuterXml;
            }

            // 构造一个XML记录, 加入"预约到书"库
            // 加入记录预约到书库的目的,是为了让工作线程可以监控读者是否来取书,如果超过保留期限,要转而通知下一个预约了此册的读者。
            // 兼有email通知功能
            nRet = AddNotifyRecordToQueueDatabase(
                // channels,
                channel,
                strItemBarcodeParam,
                "", // 暂时不使用此参数
                strItemXml,
                bOnShelf,
                strLibraryCode,
                strReservationReaderBarcode,
                strReaderXml,
                out DeletedNotifyRecPaths,
                out strError);
            if (nRet == -1)
                return -1;

            if (this.Statis != null)
                this.Statis.IncreaseEntryValue(strLibraryCode,
    "出纳",
    "预约到书册",
    1);

            return 1;
        }
Ejemplo n.º 4
0
        // 预约
        // 权限:需要有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;
        }
Ejemplo n.º 5
0
        // 清除读者和册记录中的已到预约事项,并提取下一个预约读者证条码号
        // 本函数还负责清除册记录中以前残留的state=arrived的<request>元素
        // parameters:
        //      strItemBarcode  册条码号。支持 "@refID:" 前缀用法
        //      bMaskLocationReservation    不要给册记录<location>打上#reservation标记
        //      strReservationReaderBarcode 返回下一个预约读者的证条码号
        public int ClearArrivedInfo(
            CachedRecordCollection records,
            RmsChannel channel,
            string strReaderBarcode,
            string strItemBarcode,
            bool bDontMaskLocationReservation,
            out string strReservationReaderBarcode,
            out string strError)
        {
            strError = "";

            byte[] timestamp = null;
            byte[] output_timestamp = null;
            string strOutputPath = "";
            long lRet = 0;
            int nRet = 0;
            strReservationReaderBarcode = "";

            bool bDontLock = false;

            // 加读者记录锁
            try
            {
#if DEBUG_LOCK_READER
                this.WriteErrorLog("ClearArrivedInfo 开始为读者加写锁 '" + strReaderBarcode + "'");
#endif
                this.ReaderLocks.LockForWrite(strReaderBarcode);
            }
            catch (System.Threading.LockRecursionException)
            {
                // 2012/5/31
                // 有可能本函数被DigitalPlatform.LibraryServer.LibraryApplication.Reservation()调用时,已经对读者记录加了锁
                bDontLock = true;
#if DEBUG_LOCK_READER
                this.WriteErrorLog("ClearArrivedInfo 开始为读者加写锁 '" + strReaderBarcode + "' 时遇到抛出 LockRecursionException 异常");
#endif
            }

            try
            {
                // 读入读者记录
                string strReaderXml = "";
                string strOutputReaderRecPath = "";
                nRet = this.GetReaderRecXml(
                    // channels,
                    channel,
                    strReaderBarcode,
                    out strReaderXml,
                    out strOutputReaderRecPath,
                    out strError);
                if (nRet == 0)
                {
                    strError = "读者证条码号 '" + strReaderBarcode + "' 不存在";
                    goto DOITEM;
                }
                if (nRet == -1)
                {
                    strError = "读入读者记录时发生错误: " + strError;
                    return -1;
                }

                XmlDocument readerdom = null;

                CachedRecord reader_record = null;

                if (records != null)
                    reader_record = records.Find(strOutputReaderRecPath);

                if (reader_record == null)
                {
                    nRet = LibraryApplication.LoadToDom(strReaderXml,
                        out readerdom,
                        out strError);
                    if (nRet == -1)
                    {
                        strError = "装载读者记录进入XML DOM时发生错误: " + strError;
                        return -1;
                    }
                }
                else
                    readerdom = reader_record.Dom;

                // 从当前读者记录中删除有关字段
                XmlNodeList nodes = readerdom.DocumentElement.SelectNodes("reservations/request");
                XmlNode readerRequestNode = null;
                string strItems = "";
                bool bFound = false;
                for (int i = 0; i < nodes.Count; i++)
                {
                    readerRequestNode = nodes[i];
                    strItems = DomUtil.GetAttr(readerRequestNode, "items");
                    if (IsInBarcodeList(strItemBarcode, strItems) == true)
                    {
                        bFound = true;
                        break;
                    }
                }

                if (bFound == true)
                {
                    Debug.Assert(readerRequestNode != null, "");

                    // 是清除,还是修改状态标记并保留一段?
                    // 现在是清除。如果能同时写入日志最好,以便将来查询
                    readerRequestNode.ParentNode.RemoveChild(readerRequestNode);
                }

                // 写回读者记录
                if (bFound == true)
                {
                    if (reader_record == null)
                    {
                        lRet = channel.DoSaveTextRes(strOutputReaderRecPath,
                            readerdom.OuterXml,
                            false,
                            "content,ignorechecktimestamp",
                            timestamp,
                            out output_timestamp,
                            out strOutputPath,
                            out strError);
                        if (lRet == -1)
                        {
                            strError = "写回读者记录 '" + strOutputReaderRecPath + "' 时发生错误 : " + strError;
                            return -1;
                        }
                    }
                    else
                    {
                        reader_record.Changed = true;
                    }
                }

            DOITEM:
                // 顺便获得下一个预约读者证条码号
                string strItemXml = "";
                string strOutputItemRecPath = "";
                // 获得册记录
                // return:
                //      -1  error
                //      0   not found
                //      1   命中1条
                //      >1  命中多于1条
                nRet = this.GetItemRecXml(
                    channel,
                    strItemBarcode,
                    out strItemXml,
                    out strOutputItemRecPath,
                    out strError);
                if (nRet == 0)
                {
                    strError = "册条码号 '" + strItemBarcode + "' 不存在";
                    return 0;
                }
                if (nRet == -1)
                {
                    strError = "读入册记录时发生错误: " + strError;
                    return -1;
                }

                XmlDocument itemdom = null;

                CachedRecord item_record = null;

                if (records != null)
                    item_record = records.Find(strOutputItemRecPath);

                if (item_record == null)
                {
                    nRet = LibraryApplication.LoadToDom(strItemXml,
                        out itemdom,
                        out strError);
                    if (nRet == -1)
                    {
                        strError = "装载册记录进入XML DOM时发生错误: " + strError;
                        return -1;
                    }
                }
                else
                    itemdom = item_record.Dom;

                // 察看本册预约情况, 如果有,提取出第一个预约读者的证条码号
                // 该函数还负责清除以前残留的state=arrived的<request>元素
                // return:
                //      -1  error
                //      0   没有修改
                //      1   进行过修改
                nRet = DoItemReturnReservationCheck(
                    bDontMaskLocationReservation,
                    ref itemdom,
                    out strReservationReaderBarcode,
                    out strError);
                if (nRet == -1)
                    return -1;

                if (nRet == 1)
                {
                    if (item_record == null)
                    {
                        lRet = channel.DoSaveTextRes(strOutputItemRecPath,
                            itemdom.OuterXml,
                            false,
                            "content,ignorechecktimestamp",
                            timestamp,
                            out output_timestamp,
                            out strOutputPath,
                            out strError);
                        if (lRet == -1)
                        {
                            strError = "写回册记录 '" + strOutputItemRecPath + "' 时发生错误: " + strError;
                            return -1;
                        }
                    }
                    else
                        item_record.Changed = true;
                }
            }
            finally
            {
                if (bDontLock == false)
                {
                    this.ReaderLocks.UnlockForWrite(strReaderBarcode);
#if DEBUG_LOCK_READER
                    this.WriteErrorLog("ClearArrivedInfo 结束为读者加写锁 '" + strReaderBarcode + "'");
#endif
                }
            }

            return 1;
        }