Exemple #1
0
        // 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;
        }
Exemple #2
0
        // 借阅API的从属函数
        // 在读者记录和册记录中加入借书信息
        // text-level: 用户提示
        // parameters:
        //      domOperLog 构造日志记录DOM
        //      this_return_time    本次借阅的应还最后时间。GMT时间。
        // return:
        //      -1  error
        //      0   正常
        //      // 1   发现先前借阅的图书目前有超期情况
        int DoBorrowReaderAndItemXml(
            bool bRenew,
            string strLibraryCode,
            ref XmlDocument readerdom,
            ref XmlDocument itemdom,
            bool bForce,
            string strOperator,
            string strItemRecPath,
            string strReaderRecPath,
            ref XmlDocument domOperLog,
            out BorrowInfo borrow_info,
            out string strError)
        {
            strError = "";
            int nRet = 0;
            borrow_info = new BorrowInfo();

            DateTime this_return_time = new DateTime(0);

            LibraryApplication app = this;

            // 获得例行参数
            string strReaderBarcode = DomUtil.GetElementText(readerdom.DocumentElement,
                "barcode");

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

            string strLocation = DomUtil.GetElementText(itemdom.DocumentElement,
    "location");
            strLocation = StringUtil.GetPureLocation(strLocation);

            if (String.IsNullOrEmpty(strItemBarcode) == true)
            {
#if NO
                // text-level: 内部错误
                strError = "册记录中册条码号不能为空";
                return -1;
#endif
                // 如果册条码号为空,则记载 参考ID
                string strRefID = DomUtil.GetElementText(itemdom.DocumentElement,
    "refID");
                if (String.IsNullOrEmpty(strRefID) == true)
                {
                    // text-level: 内部错误
                    strError = "册记录中册条码号和参考ID不应同时为空";
                    return -1;
                }
                strItemBarcode = "@refID:" + strRefID;
            }

            if (String.IsNullOrEmpty(strReaderBarcode) == true)
            {
                // text-level: 内部错误
                strError = "读者记录中读者证条码号不能为空";
                return -1;
            }

            // 从想要借阅的册信息中,找到图书类型
            string strBookType = DomUtil.GetElementText(itemdom.DocumentElement, "bookType");

            // 从读者信息中, 找到读者类型
            string strReaderType = DomUtil.GetElementText(readerdom.DocumentElement, "readerType");


            // 修改读者记录
            int nNo = 0;

            XmlNode nodeBorrow = null;

            if (bRenew == true)
            {
                // 看看是否已经有先前已经借阅的册
                nodeBorrow = readerdom.DocumentElement.SelectSingleNode("borrows/borrow[@barcode='" + strItemBarcode + "']");
                if (nodeBorrow == null)
                {
                    // text-level: 用户提示
                    strError = string.Format(this.GetString("该读者未曾借阅过册s,因此无法续借"),
                        // "该读者未曾借阅过册 '{0}',因此无法续借。"
                        strItemBarcode);
                    // "该读者未曾借阅过册 '" + strItemBarcode + "',因此无法续借。";
                    return -1;
                }

                // 获得上次的序号
                string strNo = DomUtil.GetAttr(nodeBorrow, "no");
                if (String.IsNullOrEmpty(strNo) == true)
                    nNo = 0;
                else
                {
                    try
                    {
                        nNo = Convert.ToInt32(strNo);
                    }
                    catch
                    {
                        if (bForce == false)
                        {
                            // text-level: 内部错误
                            strError = "读者记录中 XML 片断 " + nodeBorrow.OuterXml + "其中 no 属性值'" + strNo + "' 格式错误";
                            return -1;
                        }
                        nNo = 0;
                    }
                }

            }
            else // bRenew == false
            {
                // 检查<borrows>元素是否存在
                XmlNode root = readerdom.DocumentElement.SelectSingleNode("borrows");
                if (root == null)
                {
                    root = readerdom.CreateElement("borrows");
                    root = readerdom.DocumentElement.AppendChild(root);
                }

                // 加入借阅册信息
                nodeBorrow = readerdom.CreateElement("borrow");
                nodeBorrow = DomUtil.InsertFirstChild(root, nodeBorrow); // 2006/12/24 changed,2015/1/12 增加等号左边的部分 
                // nodeBorrow = root.AppendChild(nodeBorrow);
            }

            //
            string strThisBorrowPeriod = "10day";   // 本次借阅的期限
            string strLastBorrowPeriod = "";    // 上次借阅的期限
            string strThisDenyPeriod = "";

            // barcode
            DomUtil.SetAttr(nodeBorrow, "barcode", strItemBarcode);

            // 记载册记录路径
            if (String.IsNullOrEmpty(strItemRecPath) == false)
            {
                DomUtil.SetAttr(nodeBorrow, "recPath", strItemRecPath); // 2006/12/24
                string strParentID = DomUtil.GetElementText(itemdom.DocumentElement, "parent");

                string strBiblioRecPath = "";
                // 通过册记录路径和parentid得知从属的种记录路径
                // parameters:
                // return:
                //      -1  error
                //      1   找到
                nRet = GetBiblioRecPathByItemRecPath(
                    strItemRecPath,
                    strParentID,
                    out strBiblioRecPath,
                    out strError);
                if (nRet == -1)
                {
                    strError = "根据册记录路径 '" + strItemRecPath + "' 和 parent_id '" + strParentID + "' 获得书目库路径时出错: " + strError;
                    return -1;
                }
                DomUtil.SetAttr(nodeBorrow, "biblioRecPath", strBiblioRecPath); // 2015/10/2
            }

            DomUtil.SetAttr(nodeBorrow, "location", strLocation); // 2016/9/5

            // 加入借期字段
            // 读者记录中的借期字段,目的是为了查询方便,但注意没有法律效力。
            // 真正对超期判断起作用的,是册记录中的借期字段。
            string strBorrowPeriodList = "";
            MatchResult matchresult;
            // return:
            //      reader和book类型均匹配 算4分
            //      只有reader类型匹配,算3分
            //      只有book类型匹配,算2分
            //      reader和book类型都不匹配,算1分
            nRet = app.GetLoanParam(
                //null,
                strLibraryCode,
                strReaderType,
                strBookType,
                "借期",
                out strBorrowPeriodList,
                out matchresult,
                out strError);
            if (nRet == -1)
            {
                if (bForce == true)
                    goto DOCHANGE;
                // text-level: 内部错误
                strError = "借阅失败。获得 馆代码 '" + strLibraryCode + "' 中 读者类型 '" + strReaderType + "' 针对图书类型 '" + strBookType + "' 的 借期 参数时发生错误: " + strError;
                return -1;
            }
            if (nRet < 4)  // nRet == 0
            {
                if (bForce == true)
                    goto DOCHANGE;

                // text-level: 内部错误
                strError = "借阅失败。馆代码 '" + strLibraryCode + "' 中 读者类型 '" + strReaderType + "' 针对图书类型 '" + strBookType + "' 的 借期 参数无法获得: " + strError;
                return -1;
            }

            // 按照逗号分列值,需要根据序号取出某个参数
            string[] aPeriod = strBorrowPeriodList.Split(new char[] { ',' });

            if (aPeriod.Length == 0)
            {
                if (bForce == true)
                    goto DOCHANGE;

                // text-level: 内部错误
                strError = "借阅失败。馆代码 '" + strLibraryCode + "' 中 读者类型 '" + strReaderType + "' 针对图书类型 '" + strBookType + "' 的 借期 参数 '" + strBorrowPeriodList + "'格式错误";
                return -1;
            }

            if (bRenew == true)
            {
                nNo++;
                if (nNo >= aPeriod.Length)
                {
                    if (aPeriod.Length == 1)
                    {
                        // text-level: 用户提示
                        strError = string.Format(this.GetString("续借失败。读者类型s针对图书类型s的借期参数值s规定,不能续借"),
                            // "续借失败。读者类型 '{0}' 针对图书类型 '{1}' 的 借期 参数值 '{2}' 规定,不能续借。(所定义的一个期限,是指第一次借阅的期限)"
                            strReaderType,
                            strBookType,
                            strBorrowPeriodList);

                        // "续借失败。读者类型 '" + strReaderType + "' 针对图书类型 '" + strBookType + "' 的 借期 参数值 '" + strBorrowPeriodList + "' 规定,不能续借。(所定义的一个期限,是指第一次借阅的期限)";
                    }
                    else
                    {
                        // text-level: 用户提示
                        strError = string.Format(this.GetString("续借失败。读者类型s针对图书类型s的借期参数值s规定,只能续借s次"),
                            // "续借失败。读者类型 '{0}' 针对图书类型 '{1}' 的 借期 参数值 '{2}' 规定,只能续借 {3} 次。"
                            strReaderType,
                            strBookType,
                            strBorrowPeriodList,
                            Convert.ToString(aPeriod.Length - 1));
                        // "续借失败。读者类型 '" + strReaderType + "' 针对图书类型 '" + strBookType + "' 的 借期 参数值 '" + strBorrowPeriodList + "' 规定,只能续借 " + Convert.ToString(aPeriod.Length - 1) + " 次。";
                    }
                    return -1;
                }
                strThisBorrowPeriod = aPeriod[nNo].Trim();
                strLastBorrowPeriod = aPeriod[nNo - 1].Trim();

                if (String.IsNullOrEmpty(strThisBorrowPeriod) == true)
                {
                    if (bForce == true)
                        goto DOCHANGE;

                    // text-level: 内部错误
                    strError = "借阅失败。馆代码 '" + strLibraryCode + "' 中 读者类型 '" + strReaderType + "' 针对图书类型 '" + strBookType + "' 的 借期 参数 '" + strBorrowPeriodList + "' 格式错误:第 " + Convert.ToString(nNo) + "个部分为空。";
                    return -1;
                }
            }
            else
            {
                strThisBorrowPeriod = aPeriod[0].Trim();

                if (String.IsNullOrEmpty(strThisBorrowPeriod) == true)
                {
                    if (bForce == true)
                        goto DOCHANGE;

                    // text-level: 内部错误
                    strError = "借阅失败。馆代码 '" + strLibraryCode + "' 中 读者类型 '" + strReaderType + "' 针对图书类型 '" + strBookType + "' 的 借期 参数 '" + strBorrowPeriodList + "' 格式错误:第一部分为空。";
                    return -1;
                }
            }

            {
                List<string> parts = StringUtil.ParseTwoPart(strThisBorrowPeriod, "|");
                strThisBorrowPeriod = parts[0];
                strThisDenyPeriod = parts[1];
            }
            string strLastDenyPeriod = "";
            {
                List<string> parts = StringUtil.ParseTwoPart(strLastBorrowPeriod, "|");
                strLastBorrowPeriod = parts[0];
                strLastDenyPeriod = parts[1];
            }
            // 检查strBorrowPeriod是否合法
            {
                long lPeriodValue = 0;
                string strPeriodUnit = "";
                nRet = LibraryApplication.ParsePeriodUnit(
                    strThisBorrowPeriod,
                    out lPeriodValue,
                    out strPeriodUnit,
                    out strError);
                if (nRet == -1)
                {
                    if (bForce == true)
                        goto DOCHANGE;

                    // text-level: 内部错误
                    strError = "借阅失败。馆代码 '" + strLibraryCode + "' 中 读者类型 '" + strReaderType + "' 针对图书类型 '" + strBookType + "' 的 借期 参数 '" + strBorrowPeriodList + "' 格式错误:'" +
                         strThisBorrowPeriod + "' 格式错误: " + strError;
                    return -1;
                }
            }

        DOCHANGE:

            // 测算本次 借阅/续借 的应还书时间
            DateTime now = app.Clock.UtcNow;  //  今天,当下。GMT时间

            {
                long lPeriodValue = 0;
                string strPeriodUnit = "";
                nRet = LibraryApplication.ParsePeriodUnit(
                    strThisBorrowPeriod,
                    out lPeriodValue,
                    out strPeriodUnit,
                    out strError);
                if (nRet == -1)
                    goto SKIP_CHECK_RENEW_PERIOD;

                DateTime nextWorkingDay;

                // parameters:
                //      calendar    工作日历。如果为null,表示函数不进行非工作日判断。
                // return:
                //      -1  出错
                //      0   成功。timeEnd在工作日范围内。
                //      1   成功。timeEnd正好在非工作日。nextWorkingDay已经返回了下一个工作日的时间
                nRet = GetReturnDay(
                    null,
                    now,
                    lPeriodValue,
                    strPeriodUnit,
                    out this_return_time,
                    out nextWorkingDay,
                    out strError);
                if (nRet == -1)
                {
                    // text-level: 内部错误
                    strError = "测算这次还书时间过程发生错误: " + strError;
                    return -1;
                }

                // 正规化时间
                nRet = DateTimeUtil.RoundTime(strPeriodUnit,
                    ref this_return_time,
                    out strError);
                if (nRet == -1)
                {
                    // text-level: 内部错误
                    strError = "测算这次还书时间过程发生错误: " + strError;
                    return -1;
                }
            }

            // 如果是续借,检查不续借的应还最后日期和续借后的应还最后日期哪个靠后。
            // 如果不续借的日期还靠后,则不许读者续借。
            if (bRenew == true)
            {
                // 上次借阅日
                string strLastBorrowDate = DomUtil.GetAttr(nodeBorrow, "borrowDate");
                if (String.IsNullOrEmpty(strLastBorrowDate) == true)
                    goto SKIP_CHECK_RENEW_PERIOD;

                DateTime last_borrowdate;
                try
                {
                    last_borrowdate = DateTimeUtil.FromRfc1123DateTimeString(strLastBorrowDate);
                }
                catch
                {
                    goto SKIP_CHECK_RENEW_PERIOD;
                }

                long lLastPeriodValue = 0;
                string strLastPeriodUnit = "";
                nRet = ParsePeriodUnit(strLastBorrowPeriod,
                    out lLastPeriodValue,
                    out strLastPeriodUnit,
                    out strError);
                if (nRet == -1)
                {
                    // text-level: 内部错误
                    strError = "借阅期限 值 '" + strLastBorrowPeriod + "' 格式错误: " + strError;
                    goto SKIP_CHECK_RENEW_PERIOD;
                }

                DateTime nextWorkingDay;

                DateTime last_return_time;
                // 测算上次借书的还书日期
                nRet = GetReturnDay(
                    null,
                    last_borrowdate,
                    lLastPeriodValue,
                    strLastPeriodUnit,
                    out last_return_time,
                    out nextWorkingDay,
                    out strError);
                if (nRet == -1)
                {
                    // text-level: 内部错误
                    strError = "测算上次还书时间过程发生错误: " + strError;
                    goto SKIP_CHECK_RENEW_PERIOD;
                }

                // 正规化时间
                nRet = DateTimeUtil.RoundTime(strLastPeriodUnit,
                    ref last_return_time,
                    out strError);
                if (nRet == -1)
                    goto SKIP_CHECK_RENEW_PERIOD;

                TimeSpan delta = last_return_time - this_return_time;

                if (delta.Ticks > 0)
                {
                    strError = string.Format(this.GetString("本次续借操作被拒绝。假如这次续借实行后,应还日期将反而提前了"),
                        // "本次续借操作被拒绝。假如这次续借实行后,应还日期将为 {0} (注:续借借期从当日开始计算。从今日开始借期为 {1});而如果您不续借,应还日期本来为 {2} (注:从 {3} 开始借期为 {4} )。即,续借后的应还日期反而提前了。"
                        GetLocalTimeString(strLastPeriodUnit, this_return_time),
                        GetDisplayTimePeriodStringEx(strThisBorrowPeriod),
                        GetLocalTimeString(strLastPeriodUnit, last_return_time),
                        GetLocalTimeString(strLastPeriodUnit, last_borrowdate),
                        GetDisplayTimePeriodStringEx(strLastBorrowPeriod));
                    /*
                    // 2008/5/8 changed
                    strError = "本次续借操作被拒绝。假如这次续借实行后,应还日期将为 "
                        + GetLocalTimeString(strLastPeriodUnit, this_return_time)
                        + " (注:续借借期从当日开始计算。从今日开始借期为 "
                        + GetDisplayTimePeriodString(strThisBorrowPeriod)
                        + " );而如果您不续借,应还日期本来为 " 
                        + GetLocalTimeString(strLastPeriodUnit, last_return_time) // this_return_time.ToString() BUG!!!
                        + " (注:从 "
                        + GetLocalTimeString(strLastPeriodUnit, last_borrowdate)
                        + " 开始借期为 "
                        + GetDisplayTimePeriodString(strLastBorrowPeriod)
                        + " )。即,续借后的应还日期反而提前了。";
                     * */
                    return -1;
                }
            }

        SKIP_CHECK_RENEW_PERIOD:

            string strRenewComment = "";

            string strBorrowDate = app.Clock.GetClock();

            string strLastReturningDate = "";   // 上次的应还时间

            if (bRenew == true)
            {
                strLastReturningDate = DomUtil.GetAttr(nodeBorrow, "returningDate");

                // 修正序号
                nNo = Math.Max(nNo, 1);

                // 保存前一次借阅的信息
                strRenewComment = DomUtil.GetAttr(nodeBorrow, "renewComment");

                if (strRenewComment != "")
                    strRenewComment += "; ";

                strRenewComment += "no=" + Convert.ToString(nNo - 1) + ", ";
                strRenewComment += "borrowDate=" + DomUtil.GetAttr(nodeBorrow, "borrowDate") + ", ";
                strRenewComment += "borrowPeriod=" + DomUtil.GetAttr(nodeBorrow, "borrowPeriod") + ", ";
                strRenewComment += "returnDate=" + strBorrowDate + ", ";
                strRenewComment += "operator=" + DomUtil.GetAttr(nodeBorrow, "operator");
            }

            // borrowDate
            DomUtil.SetAttr(nodeBorrow, "borrowDate",
                strBorrowDate);

            if (nNo > 0)    // 2013/12/23
                DomUtil.SetAttr(nodeBorrow, "no", Convert.ToString(nNo));

            DomUtil.SetAttr(nodeBorrow, "borrowPeriod",
                strThisBorrowPeriod);
            // 2016/6/7
            if (string.IsNullOrEmpty(strThisDenyPeriod) == false)
                DomUtil.SetAttr(nodeBorrow, "denyPeriod",
                   strThisDenyPeriod);

            // 2014/11/14
            // returningDate
            string strReturningDate = DateTimeUtil.Rfc1123DateTimeStringEx(this_return_time.ToLocalTime());
            DomUtil.SetAttr(nodeBorrow, "returningDate",
                strReturningDate);

            // 2014/11/14
            // lastReturningDate

            if (nNo > 0)
                DomUtil.SetAttr(nodeBorrow, "lastReturningDate",
                    strLastReturningDate);

            if (string.IsNullOrEmpty(strRenewComment) == false)    // 2013/12/23
                DomUtil.SetAttr(nodeBorrow, "renewComment", strRenewComment);

            DomUtil.SetAttr(nodeBorrow, "operator", strOperator);

            // 2007/11/5
            DomUtil.SetAttr(nodeBorrow, "type", strBookType);   // 在读者记录<borrows/borrow>元素中写入type属性,内容为图书册类型,便于后续借书的时候判断某一种册类型是否超过读者权限规定值。这种方式可以节省时间,不必从多个册记录中去获得册类型字段

            // 2006/11/12
            string strBookPrice = DomUtil.GetElementText(itemdom.DocumentElement, "price");
            DomUtil.SetAttr(nodeBorrow, "price", strBookPrice);   // 在读者记录<borrows/borrow>元素中写入price属性,内容为图书册价格类型,便于后续借书的时候判断已经借的和即将借的总价格是否超过读者的押金余额。这种方式可以节省时间,不必从多个册记录中去获得册价格字段

            // 修改册记录
            string strOldReaderBarcode = "";

            strOldReaderBarcode = DomUtil.GetElementText(itemdom.DocumentElement,
                "borrower");

            if (bRenew == false)
            {
                if (bForce == false
                    && String.IsNullOrEmpty(strOldReaderBarcode) == false)
                {
                    // 2007/1/2
                    if (strOldReaderBarcode == strReaderBarcode)
                    {
                        // text-level: 用户提示
                        strError = "借阅操作被拒绝。因册 '" + strItemBarcode + "' 在本次操作前已经被当前读者 '" + strReaderBarcode + "' 借阅了。";
                        return -1;
                    }

                    // text-level: 用户提示
                    strError = "借阅操作被拒绝。因册 '" + strItemBarcode + "' 在本次操作前已经处于被读者 '" + strOldReaderBarcode + "' 借阅(持有)状态(尚未归还)。\r\n如果属于拿错情况,请工作人员立即扣留此书,设法交还给持有人;\r\n如果确系(经持有人同意)其他读者要转借此册,需先履行还书手续;\r\n如果持有人要续借此册,请履行续借手续。";
                    return -1;
                }
            }

            DomUtil.SetElementText(itemdom.DocumentElement,
                "borrower", strReaderBarcode);

            // 2008/9/18
            DomUtil.SetElementText(itemdom.DocumentElement,
                "borrowerReaderType", strReaderType);

            // 2012/9/8
            DomUtil.SetElementText(itemdom.DocumentElement,
                "borrowerRecPath", strReaderRecPath);

            DomUtil.SetElementText(itemdom.DocumentElement,
                "borrowDate",
                strBorrowDate);

            if (nNo > 0)    // 2013/12/23
                DomUtil.SetElementText(itemdom.DocumentElement,
                    "no",
                    Convert.ToString(nNo));
            else
                DomUtil.DeleteElements(itemdom.DocumentElement,
    "no");  // 2016/6/8

            DomUtil.SetElementText(itemdom.DocumentElement,
                "borrowPeriod",
                strThisBorrowPeriod);   // strBorrowPeriod现在已经是个别参数,不是逗号分隔的列举值了
            // 2016/6/7
            if (string.IsNullOrEmpty(strThisDenyPeriod) == false)
                DomUtil.SetElementText(itemdom.DocumentElement,
                    "denyPeriod",
                    strThisDenyPeriod);
            else
                DomUtil.DeleteElements(itemdom.DocumentElement,
                    "denyPeriod");  // 2016/6/8

            DomUtil.SetElementText(itemdom.DocumentElement,
                "returningDate",
                strReturningDate);

            if (nNo > 0)
                DomUtil.SetElementText(itemdom.DocumentElement,
                    "lastReturningDate",
                    strLastReturningDate);

            if (string.IsNullOrEmpty(strRenewComment) == false) // 2013/12/23
                DomUtil.SetElementText(itemdom.DocumentElement,
                    "renewComment",
                    strRenewComment);

            DomUtil.SetElementText(itemdom.DocumentElement,
                "operator",
                strOperator);
            // 注:虽然是借书操作本来不操作 borrowHistory 元素,但有这样一种情况,以前的极限数量较大,后来极限数量改小了,由于目前并没有专门批处理册记录的模块,所以希望借书时候顺便把元素数量删减,这样后面创建日志记录的时候,在日志记录里面记载的也就是尺寸减小后的册记录内容了
            // 删除超过极限数量的 BorrowHistory/borrower 元素
            // return:
            //      -1  出错
            //      0   册记录没有改变
            //      1   册记录发生改变
            nRet = RemoveItemHistoryItems(itemdom,
                out strError);
            if (nRet == -1)
                return -1;

            // 创建日志记录
            DomUtil.SetElementText(domOperLog.DocumentElement, "readerBarcode",
                strReaderBarcode);     // 读者证条码号
            DomUtil.SetElementText(domOperLog.DocumentElement, "itemBarcode",
                strItemBarcode);    // 册条码号
            DomUtil.SetElementText(domOperLog.DocumentElement, "borrowDate",
                strBorrowDate);     // 借阅日期
            DomUtil.SetElementText(domOperLog.DocumentElement, "borrowPeriod",
                strThisBorrowPeriod);   // 借阅期限
            // 2016/6/7
            if (string.IsNullOrEmpty(strThisDenyPeriod) == false)
                DomUtil.SetElementText(domOperLog.DocumentElement, "denyPeriod",
                    strThisDenyPeriod);

            DomUtil.SetElementText(domOperLog.DocumentElement, "returningDate",
                strReturningDate);     // 应还日期

            // 2015/1/12
            DomUtil.SetElementText(domOperLog.DocumentElement, "type",
    strBookType);    // 图书类型
            DomUtil.SetElementText(domOperLog.DocumentElement, "price",
strBookPrice);    // 图书价格

            // TODO: 0 需要忽略写入
            DomUtil.SetElementText(domOperLog.DocumentElement, "no",
                Convert.ToString(nNo)); // 续借次数

            if (nNo > 0)
                DomUtil.SetElementText(domOperLog.DocumentElement, "lastReturningDate",
    strLastReturningDate);     // 上次应还日期

            DomUtil.SetElementText(domOperLog.DocumentElement, "operator",
                strOperator);   // 操作者
            DomUtil.SetElementText(domOperLog.DocumentElement, "operTime",
                strBorrowDate);   // 操作时间

            // 返回借阅成功的信息

            // 返回满足RFC1123的时间值字符串 GMT时间
            borrow_info.LatestReturnTime = DateTimeUtil.Rfc1123DateTimeStringEx(this_return_time.ToLocalTime());
            borrow_info.Period = strThisBorrowPeriod;
            borrow_info.DenyPeriod = strThisDenyPeriod;
            borrow_info.BorrowCount = nNo;

            // 2011/6/26
            borrow_info.BorrowOperator = strOperator;
            /*
            borrow_info.BookType = strBookType;
            string strLocation = DomUtil.GetElementText(itemdom.DocumentElement,
                "location");
            borrow_info.Location = strLocation;
             * */

            return 0;
        }