Beispiel #1
0
        // 一次操作循环
        public override void Worker()
        {
            // 把系统挂起
            this.App.HangupReason = HangupReason.LogRecover;

            try
            {

                string strError = "";

                BatchTaskStartInfo startinfo = this.StartInfo;
                if (startinfo == null)
                    startinfo = new BatchTaskStartInfo();   // 按照缺省值来

                long lStartIndex = 0;// 开始位置
                string strStartFileName = "";// 开始文件名
                int nRet = ParseLogRecorverStart(startinfo.Start,
                    out lStartIndex,
                    out strStartFileName,
                    out strError);
                if (nRet == -1)
                {
                    this.AppendResultText("启动失败: " + strError + "\r\n");
                    return;
                }

                //
                string strRecoverLevel = "";
                bool bClearFirst = false;
                nRet = ParseLogRecoverParam(startinfo.Param,
                    out strRecoverLevel,
                    out bClearFirst,
                    out strError);
                if (nRet == -1)
                {
                    this.AppendResultText("启动失败: " + strError + "\r\n");
                    return;
                }

                if (String.IsNullOrEmpty(strRecoverLevel) == true)
                    strRecoverLevel = "Snapshot";

                try
                {
                    this.RecoverLevel = (RecoverLevel)Enum.Parse(typeof(RecoverLevel), strRecoverLevel, true);
                }
                catch (Exception ex)
                {
                    this.AppendResultText("启动失败: 启动参数Param中的recoverLevel枚举值 '" + strRecoverLevel + "' 错误: " + ex.Message + "\r\n");
                    return;
                }

                this.App.WriteErrorLog("日志恢复 任务启动。");

                if (bClearFirst == true)
                {
                    nRet = this.App.ClearAllDbs(this.RmsChannels,
                        out strError);
                    if (nRet == -1)
                    {
                        this.AppendResultText("清除全部数据库记录时发生错误: " + strError + "\r\n");
                        return;
                    }
                }

                bool bStart = false;
                if (String.IsNullOrEmpty(strStartFileName) == true)
                {
                    // 做所有文件
                    bStart = true;
                }


                // 列出所有日志文件
                DirectoryInfo di = new DirectoryInfo(this.App.OperLog.Directory);

                FileInfo[] fis = di.GetFiles("*.log");

                // BUG!!! 以前缺乏排序。2008/2/1
                Array.Sort(fis, new FileInfoCompare());


                for (int i = 0; i < fis.Length; i++)
                {
                    if (this.Stopped == true)
                        break;

                    string strFileName = fis[i].Name;

                    this.AppendResultText("检查文件 " + strFileName + "\r\n");

                    if (bStart == false)
                    {
                        // 从特定文件开始做
                        if (string.CompareOrdinal(strStartFileName, strFileName) <= 0)  // 2015/9/12 从等号修改为 Compare
                        {
                            bStart = true;
                            if (lStartIndex < 0)
                                lStartIndex = 0;
                            // lStartIndex = Convert.ToInt64(startinfo.Param);
                        }
                    }

                    if (bStart == true)
                    {
                        nRet = DoOneLogFile(strFileName,
                            lStartIndex,
                            out strError);
                        if (nRet == -1)
                            goto ERROR1;
                        lStartIndex = 0;    // 第一个文件以后的文件就全做了
                    }

                }

                this.AppendResultText("循环结束\r\n");
                
                this.App.WriteErrorLog("日志恢复 任务结束。");

                return;

            ERROR1:
                return;
            }
            finally
            {
                this.App.HangupReason = HangupReason.None;
            }
        }
Beispiel #2
0
        // 一次操作循环
        public override void Worker()
        {
            // 把系统挂起
            // this.App.HangupReason = HangupReason.LogRecover;
            this.App.AddHangup("LogRecover");
            try
            {
                string strError = "";

                BatchTaskStartInfo startinfo = this.StartInfo;
                if (startinfo == null)
                    startinfo = new BatchTaskStartInfo();   // 按照缺省值来

                long lStartIndex = 0;// 开始位置
                string strStartFileName = "";// 开始文件名
                int nRet = ParseLogRecorverStart(startinfo.Start,
                    out lStartIndex,
                    out strStartFileName,
                    out strError);
                if (nRet == -1)
                {
                    this.AppendResultText("启动失败: " + strError + "\r\n");
                    return;
                }

                //
                string strRecoverLevel = "";
                bool bClearFirst = false;
                bool bContinueWhenError = false;

                nRet = ParseLogRecoverParam(startinfo.Param,
                    out strRecoverLevel,
                    out bClearFirst,
                    out bContinueWhenError,
                    out strError);
                if (nRet == -1)
                {
                    this.AppendResultText("启动失败: " + strError + "\r\n");
                    return;
                }

                if (String.IsNullOrEmpty(strRecoverLevel) == true)
                    strRecoverLevel = "Snapshot";

                try
                {
                    this.RecoverLevel = (RecoverLevel)Enum.Parse(typeof(RecoverLevel), strRecoverLevel, true);
                }
                catch (Exception ex)
                {
                    this.AppendResultText("启动失败: 启动参数Param中的recoverLevel枚举值 '" + strRecoverLevel + "' 错误: " + ex.Message + "\r\n");
                    return;
                }

                this.App.WriteErrorLog("日志恢复 任务启动。");

                // 当为容错恢复级别时,检查当前全部读者库的检索点是否符合要求
                if (this.RecoverLevel == LibraryServer.RecoverLevel.Robust)
                {
                    // 检查全部读者库的检索途径,看是否满足都有“所借册条码号”这个检索途径的这个条件
                    // return:
                    //      -1  出错
                    //      0   不满足
                    //      1   满足
                    nRet = this.App.DetectReaderDbFroms(out strError);
                    if (nRet == -1)
                    {
                        this.AppendResultText("检查读者库检索点时发生错误: " + strError + "\r\n");
                        return;
                    }
                    if (nRet == 0)
                    {
                        this.AppendResultText("在容错恢复级别下,当前读者库中有部分或全部读者库缺乏“所借册条码号”检索点,无法进行日志恢复。请按照日志恢复要求,刷新所有读者库的检索点配置,然后再进行日志恢复\r\n");
                        return;
                    }
                }

                // TODO: 检查当前是否有 重建检索点 的后台任务正在运行,或者还有没有运行完的部分。
                // 要求重建检索点的任务运行完以后才能执行日志恢复任务

                if (bClearFirst == true)
                {
                    nRet = this.App.ClearAllDbs(this.RmsChannels,
                        out strError);
                    if (nRet == -1)
                    {
                        this.AppendResultText("清除全部数据库记录时发生错误: " + strError + "\r\n");
                        return;
                    }
                }

                bool bStart = false;
                if (String.IsNullOrEmpty(strStartFileName) == true)
                {
                    // 做所有文件
                    bStart = true;
                }


                // 列出所有日志文件
                DirectoryInfo di = new DirectoryInfo(this.App.OperLog.Directory);

                FileInfo[] fis = di.GetFiles("*.log");

                // BUG!!! 以前缺乏排序。2008/2/1
                Array.Sort(fis, new FileInfoCompare());


                for (int i = 0; i < fis.Length; i++)
                {
                    if (this.Stopped == true)
                        break;

                    string strFileName = fis[i].Name;

                    this.AppendResultText("检查文件 " + strFileName + "\r\n");

                    if (bStart == false)
                    {
                        // 从特定文件开始做
                        if (string.CompareOrdinal(strStartFileName, strFileName) <= 0)  // 2015/9/12 从等号修改为 Compare
                        {
                            bStart = true;
                            if (lStartIndex < 0)
                                lStartIndex = 0;
                            // lStartIndex = Convert.ToInt64(startinfo.Param);
                        }
                    }

                    if (bStart == true)
                    {
                        nRet = DoOneLogFile(strFileName,
                            lStartIndex,
                            bContinueWhenError,
                            out strError);
                        if (nRet == -1)
                            goto ERROR1;
                        lStartIndex = 0;    // 第一个文件以后的文件就全做了
                    }

                }

                this.AppendResultText("循环结束\r\n");

                this.App.WriteErrorLog("日志恢复 任务结束。");

                return;

            ERROR1:
                return;
            }
            finally
            {
                // this.App.HangupReason = HangupReason.None;
                this.App.ClearHangup("LogRecover");
            }
        }
Beispiel #3
0
        /*
<root>
<operation>writeRes</operation> 
<requestResPath>...</requestResPath> 资源路径参数。也就是请求API是的strResPath参数值。可能在路径中的记录ID部分包含问号,表示要追加创建新的记录
<resPath>...</resPath> 资源路径。资源的确定路径。
<ranges>...</ranges> 字节范围
<totalLength>...</totalLength> 总长度
<metadata>...</metadata> 此元素的文本即是记录体,但注意为不透明的字符串(HtmlEncoding后的记录字符串)。
<style>...</style> 当 style 中包含 delete 子串时表示要删除这个资源 
<operator>test</operator> 
<operTime>Fri, 08 Dec 2006 10:12:20 GMT</operTime> 
</root>
         * 可能会有一个attachment
 * * */
        public int RecoverWriteRes(
            RmsChannelCollection Channels,
            RecoverLevel level,
            XmlDocument domLog,
            Stream attachmentLog,
            out string strError)
        {
            strError = "";

            // 暂时把Robust当作Logic处理
            if (level == RecoverLevel.Robust)
                level = RecoverLevel.Logic;

            long lRet = 0;
            // int nRet = 0;

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

            bool bReuse = false;    // 是否能够不顾RecorverLevel状态而重用部分代码

        DO_SNAPSHOT:

            // 快照恢复
            if (level == RecoverLevel.Snapshot
                || bReuse == true)
            {
                string strResPath = DomUtil.GetElementText(
                    domLog.DocumentElement,
                    "resPath");
                if (string.IsNullOrEmpty(strResPath) == true)
                {
                    strError = "日志记录中缺<resPath>元素";
                    return -1;
                }

                string strRanges = DomUtil.GetElementText(
    domLog.DocumentElement,
    "ranges");
                if (string.IsNullOrEmpty(strRanges) == true)
                {
                    strError = "日志记录中缺<ranges>元素";
                    return -1;
                }

                string strTotalLength = DomUtil.GetElementText(
domLog.DocumentElement,
"totalLength");
                if (string.IsNullOrEmpty(strTotalLength) == true)
                {
                    strError = "日志记录中缺<totalLength>元素";
                    return -1;
                }

                long lTotalLength = 0;
                try
                {
                    lTotalLength = Convert.ToInt64(strTotalLength);
                }
                catch
                {
                    strError = "lTotalLength值 '"+strTotalLength+"' 格式不正确";
                    return -1;
                }
                string strMetadata = DomUtil.GetElementText(
domLog.DocumentElement,
"metadata");
                string strStyle = DomUtil.GetElementText(
domLog.DocumentElement,
"style");

                // 读入记录内容
                byte[] baRecord = null;

                if (attachmentLog != null && attachmentLog.Length > 0)
                {
                    baRecord = new byte[(int)attachmentLog.Length];
                    attachmentLog.Seek(0, SeekOrigin.Begin);
                    attachmentLog.Read(baRecord, 0, (int)attachmentLog.Length);
                }

                strStyle += ",ignorechecktimestamp";

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

                if (StringUtil.IsInList("delete", strStyle) == true)
                {
                    // 2015/9/3 增加
                    lRet = channel.DoDeleteRes(strResPath,
                        timestamp,
                        strStyle,
                        out output_timestamp,
                        out strError);
                }
                else
                {
                    lRet = channel.WriteRes(strResPath,
        strRanges,
        lTotalLength,
        baRecord,
        strMetadata,
        strStyle,
        timestamp,
        out strOutputResPath,
        out output_timestamp,
        out strError);
                }
                if (lRet == -1)
                {
                    strError = "WriteRes() '" + strResPath + "' 时发生错误: " + strError;
                    return -1;
                }

                return 0;
            }

            // 逻辑恢复或者混合恢复
            if (level == RecoverLevel.Logic
                || level == RecoverLevel.LogicAndSnapshot)
            {
                // 和SnapShot方式相同
                bReuse = true;
                goto DO_SNAPSHOT;
            }
            return 0;
        ERROR1:
            if (level == RecoverLevel.LogicAndSnapshot)
            {
                level = RecoverLevel.Snapshot;
                goto DO_SNAPSHOT;
            }
            return -1;
        }
Beispiel #4
0
        /*
<root>
  <operation>repairBorrowInfo</operation> 
  <action>...</action> 具体动作 有 repairreaderside repairitemside
  <readerBarcode>...</readerBarcode>
  <itemBarcode>...</itemBarcode>
  <confirmItemRecPath>...</confirmItemRecPath> 辅助判断用的册记录路径
  <operator>test</operator> 
  <operTime>Fri, 08 Dec 2006 10:12:20 GMT</operTime> 
</root>
         * * 
         * */
        public int RecoverRepairBorrowInfo(
    RmsChannelCollection Channels,
    RecoverLevel level,
    XmlDocument domLog,
    Stream attachmentLog,
    out string strError)
        {
            strError = "";
            int nRet = 0;

            // 暂时把Robust当作Logic处理
            if (level == RecoverLevel.Robust)
                level = RecoverLevel.Logic;

            long lRet = 0;
            // int nRet = 0;

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

            bool bReuse = false;    // 是否能够不顾RecorverLevel状态而重用部分代码

DO_SNAPSHOT:

            // 快照恢复
            if (level == RecoverLevel.Snapshot
                || bReuse == true)
            {
                string strAction = DomUtil.GetElementText(domLog.DocumentElement,
                    "action");

                string strReaderBarcode = DomUtil.GetElementText(domLog.DocumentElement,
                    "readerBarcode");
                if (String.IsNullOrEmpty(strReaderBarcode) == true)
                {
                    strError = "<readerBarcode>元素值为空";
                    goto ERROR1;
                }

                // 读入读者记录
                string strReaderXml = "";
                string strOutputReaderRecPath = "";
                byte[] reader_timestamp = null;

                nRet = this.GetReaderRecXml(
                    // Channels,
                    channel,
                    strReaderBarcode,
                    out strReaderXml,
                    out strOutputReaderRecPath,
                    out reader_timestamp,
                    out strError);
                if (nRet == 0)
                {
                    if (strAction == "repairreaderside")
                    {
                        strError = "读者证条码号 '" + strReaderBarcode + "' 不存在";
                        goto ERROR1;
                    }

                    // 从实体侧恢复的时候,是允许读者记录不存在的
                }
                if (nRet == -1)
                {
                    strError = "读入证条码号为 '" + strReaderBarcode + "' 的读者记录时发生错误: " + strError;
                    goto ERROR1;
                }

                XmlDocument readerdom = null;
                if (string.IsNullOrEmpty(strReaderXml) == false)
                {
                    nRet = LibraryApplication.LoadToDom(strReaderXml,
                        out readerdom,
                        out strError);
                    if (nRet == -1)
                    {
                        strError = "装载读者记录进入XML DOM时发生错误: " + strError;
                        goto ERROR1;
                    }
                }

                // 校验读者证条码号参数是否和XML记录中完全一致
                if (readerdom != null)
                {
                    string strTempBarcode = DomUtil.GetElementText(readerdom.DocumentElement,
                        "barcode");
                    if (strReaderBarcode != strTempBarcode)
                    {
                        strError = "修复操作被拒绝。因读者证条码号参数 '" + strReaderBarcode + "' 和读者记录中<barcode>元素内的读者证条码号值 '" + strTempBarcode + "' 不一致。";
                        goto ERROR1;
                    }
                }
                
                // 读入册记录
                string strConfirmItemRecPath = DomUtil.GetElementText(domLog.DocumentElement,
                    "confirmItemRecPath");
                string strItemBarcode = DomUtil.GetElementText(domLog.DocumentElement,
                    "itemBarcode");
                if (String.IsNullOrEmpty(strItemBarcode) == true)
                {
                    strError = "<strItemBarcode>元素值为空";
                    goto ERROR1;
                }

                string strItemXml = "";
                string strOutputItemRecPath = "";
                byte[] item_timestamp = null;

                // 如果已经有确定的册记录路径
                if (String.IsNullOrEmpty(strConfirmItemRecPath) == false)
                {
                    string strMetaData = "";
                    lRet = channel.GetRes(strConfirmItemRecPath,
                        out strItemXml,
                        out strMetaData,
                        out item_timestamp,
                        out strOutputItemRecPath,
                        out strError);
                    if (lRet == -1)
                    {
                        strError = "根据strConfirmItemRecPath '" + strConfirmItemRecPath + "' 获得册记录失败: " + strError;
                        goto ERROR1;
                    }

                    // 需要检查记录中的<barcode>元素值是否匹配册条码号


                    // TODO: 如果记录路径所表达的记录不存在,或者其<barcode>元素值和要求的册条码号不匹配,那么都要改用逻辑方法,也就是利用册条码号来获得记录。
                    // 当然,这种情况下,非常要紧的是确保数据库的素质很好,本身没有重条码号的情况出现。
                }
                else
                {
                    // 从册条码号获得册记录
                    List<string> aPath = null;

                    // 获得册记录
                    // return:
                    //      -1  error
                    //      0   not found
                    //      1   命中1条
                    //      >1  命中多于1条
                    nRet = this.GetItemRecXml(
                        // Channels,
                        channel,
                        strItemBarcode,
                        out strItemXml,
                        100,
                        out aPath,
                        out item_timestamp,
                        out strError);
                    if (nRet == 0)
                    {
                        if (strAction == "repairitemside")
                        {
                            strError = "册条码号 '" + strItemBarcode + "' 不存在";
                            goto ERROR1;
                        }

                        // 从读者侧恢复的时候,册条码号不存在是允许的
                        goto CONTINUE_REPAIR;
                    }
                    if (nRet == -1)
                    {
                        strError = "读入册条码号为 '" + strItemBarcode + "' 的册记录时发生错误: " + strError;
                        goto ERROR1;
                    }

                    if (aPath.Count > 1)
                    {

                        strError = "册条码号为 '" + strItemBarcode + "' 的册记录有 " + aPath.Count.ToString() + " 条,但此时comfirmItemRecPath却为空";
                        goto ERROR1;
                    }
                    else
                    {

                        Debug.Assert(nRet == 1, "");
                        Debug.Assert(aPath.Count == 1, "");

                        if (nRet == 1)
                        {
                            strOutputItemRecPath = aPath[0];
                        }
                    }
                }

            CONTINUE_REPAIR:

                XmlDocument itemdom = null;
                if (string.IsNullOrEmpty(strItemXml) == false)
                {
                    nRet = LibraryApplication.LoadToDom(strItemXml,
                        out itemdom,
                        out strError);
                    if (nRet == -1)
                    {
                        strError = "装载册记录进入XML DOM时发生错误: " + strError;
                        goto ERROR1;
                    }

                    // 校验册条码号参数是否和XML记录中完全一致
                    string strTempItemBarcode = DomUtil.GetElementText(itemdom.DocumentElement,
                        "barcode");
                    if (strItemBarcode != strTempItemBarcode)
                    {
                        strError = "修复操作被拒绝。因册条码号参数 '" + strItemBarcode + "' 和册记录中<barcode>元素内的册条码号值 '" + strTempItemBarcode + "' 不一致。";
                        goto ERROR1;
                    }
                }

                if (strAction == "repairreaderside")
                {
                    XmlNode nodeBorrow = readerdom.DocumentElement.SelectSingleNode("borrows/borrow[@barcode='" + strItemBarcode + "']");
                    if (nodeBorrow == null)
                    {
                        strError = "修复操作被拒绝。读者记录 " + strReaderBarcode + " 中并不存在有关册 " + strItemBarcode + " 的借阅信息。";
                        goto ERROR1;
                    }

                    if (itemdom != null)
                    {
                        // 看看册记录中是否有指回读者记录的链
                        string strBorrower = DomUtil.GetElementText(itemdom.DocumentElement,
                            "borrower");
                        if (strBorrower == strReaderBarcode)
                        {
                            strError = "修复操作被拒绝。您所请求要修复的链,本是一条完整正确的链。可直接进行普通还书操作。";
                            goto ERROR1;
                        }
                    }

                    // 移除读者记录侧的链
                    nodeBorrow.ParentNode.RemoveChild(nodeBorrow);

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


                    // 写回读者记录
                    lRet = channel.DoSaveTextRes(strOutputReaderRecPath,
                        readerdom.OuterXml,
                        false,
                        "content,ignorechecktimestamp",
                        reader_timestamp,
                        out output_timestamp,
                        out strOutputPath,
                        out strError);
                    if (lRet == -1)
                        goto ERROR1;
                }
                else if (strAction == "repairitemside")
                {
                    // 看看册记录中是否有指向读者记录的链
                    string strBorrower = DomUtil.GetElementText(itemdom.DocumentElement,
                        "borrower");
                    if (String.IsNullOrEmpty(strBorrower) == true)
                    {
                        strError = "修复操作被拒绝。您所请求要修复的册记录中,本来就没有借阅信息,因此谈不上修复。";
                        goto ERROR1;
                    }

                    if (strBorrower != strReaderBarcode)
                    {
                        strError = "修复操作被拒绝。您所请求要修复的册记录中,并没有指明借阅者是读者 " + strReaderBarcode + "。";
                        goto ERROR1;
                    }

                    // 看看读者记录中是否有指回链条。
                    if (readerdom != null)
                    {
                        XmlNode nodeBorrow = readerdom.DocumentElement.SelectSingleNode("borrows/borrow[@barcode='" + strItemBarcode + "']");
                        if (nodeBorrow != null)
                        {
                            strError = "修复操作被拒绝。您所请求要修复的链,本是一条完整正确的链。可直接进行普通还书操作。";
                            goto ERROR1;
                        }
                    }

                    // 移除册记录侧的链
                    DomUtil.SetElementText(itemdom.DocumentElement,
                        "borrower", "");
                    DomUtil.SetElementText(itemdom.DocumentElement,
                        "borrowDate", "");
                    DomUtil.SetElementText(itemdom.DocumentElement,
                        "borrowPeriod", "");

                    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)
                        goto ERROR1;
                }
                else
                {
                    strError = "不可识别的strAction值 '"+strAction+"'";
                    goto ERROR1;
                }

                return 0;
            }

            // 逻辑恢复或者混合恢复
            if (level == RecoverLevel.Logic
                || level == RecoverLevel.LogicAndSnapshot)
            {
                // 和SnapShot方式相同
                bReuse = true;
                goto DO_SNAPSHOT;
            }
            return 0;
        ERROR1:
            if (level == RecoverLevel.LogicAndSnapshot)
            {
                level = RecoverLevel.Snapshot;
                goto DO_SNAPSHOT;
            }
            return -1;
        }
Beispiel #5
0
        /*
foregift 创建押金记录

API: Foregift()

<root>
  <operation>foregift</operation> 操作类型
  <action>...</action> 具体动作 目前有foregift return (注: return操作时,overdue元素里面的price属性,可以使用宏 %return_foregift_price% 表示当前剩余的押金额)
  <readerBarcode>R0000002</readerBarcode> 读者证条码号
  <operator>test</operator> 操作者
  <operTime>Fri, 08 Dec 2006 04:17:45 GMT</operTime> 操作时间
  <overdues>...</overdues> 押金信息 通常内容为一个字符串,为一个或多个<overdue>元素XML文本片断
  <readerRecord recPath='...'>...</readerRecord>	最新读者记录
</root>
         * * */
        public int RecoverForegift(
            RmsChannelCollection Channels,
            RecoverLevel level,
            XmlDocument domLog,
            out string strError)
        {
            strError = "";

            // 暂时把Robust当作Logic处理
            if (level == RecoverLevel.Robust)
                level = RecoverLevel.Logic;


            long lRet = 0;
            int nRet = 0;

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

        DO_SNAPSHOT:

            // 快照恢复
            if (level == RecoverLevel.Snapshot)
            {
                XmlNode node = null;
                string strReaderXml = DomUtil.GetElementText(domLog.DocumentElement,
                    "readerRecord",
                    out node);
                if (node == null)
                {
                    strError = "日志记录中缺<readerRecord>元素";
                    return -1;
                }
                string strReaderRecPath = DomUtil.GetAttr(node, "recPath");

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

                // 写读者记录
                lRet = channel.DoSaveTextRes(strReaderRecPath,
                    strReaderXml,
                    false,
                    "content,ignorechecktimestamp",
                    timestamp,
                    out output_timestamp,
                    out strOutputPath,
                    out strError);
                if (lRet == -1)
                {
                    strError = "写入读者记录 '" + strReaderRecPath + "' 时发生错误: " + strError;
                    return -1;
                }

                return 0;
            }


            // 逻辑恢复或者混合恢复
            if (level == RecoverLevel.Logic
                || level == RecoverLevel.LogicAndSnapshot)
            {
                // string strRecoverComment = "";

                string strAction = DomUtil.GetElementText(domLog.DocumentElement,
                    "action");

                string strReaderBarcode = DomUtil.GetElementText(domLog.DocumentElement,
                    "readerBarcode");
                ///
                if (String.IsNullOrEmpty(strReaderBarcode) == true)
                {
                    strError = "日志记录中<readerBarcode>元素值为空";
                    goto ERROR1;
                }

                string strOperator = DomUtil.GetElementText(domLog.DocumentElement,
                    "operator");

                string strOperTime = DomUtil.GetElementText(domLog.DocumentElement,
                    "operTime");

                string strOverdues = DomUtil.GetElementText(domLog.DocumentElement,
                    "overdues");
                if (String.IsNullOrEmpty(strOverdues) == true)
                {
                    strError = "日志记录中<overdues>元素值为空";
                    goto ERROR1;
                }

                // 从overdues字符串中分析出id
                XmlDocument tempdom = new XmlDocument();
                tempdom.LoadXml("<root />");
                XmlDocumentFragment fragment = tempdom.CreateDocumentFragment();
                fragment.InnerXml = strOverdues;
                tempdom.DocumentElement.AppendChild(fragment);

                XmlNode tempnode = tempdom.DocumentElement.SelectSingleNode("overdue");
                if (tempnode == null)
                {
                    strError = "<overdues>元素内容有误,缺乏<overdue>元素";
                    goto ERROR1;
                }

                string strID = DomUtil.GetAttr(tempnode, "id");
                if (String.IsNullOrEmpty(strID) == true)
                {
                    strError = "日志记录中<overdues>内容中<overdue>元素中id属性值为空";
                    goto ERROR1;
                }

                // 读入读者记录
                string strReaderXml = "";
                string strOutputReaderRecPath = "";
                byte[] reader_timestamp = null;

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

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


                // 
                string strOverdueString = "";
                // 根据Foregift() API要求,修改readerdom
                nRet = DoForegift(strAction,
                    readerdom,
                    ref strID,
                    strOperator,
                    strOperTime,
                    out strOverdueString,
                    out strError);
                if (nRet == -1)
                    goto ERROR1;

                // 写回读者、册记录
                byte[] output_timestamp = null;
                string strOutputPath = "";


                // 写回读者记录
                lRet = channel.DoSaveTextRes(strOutputReaderRecPath,
                    readerdom.OuterXml,
                    false,
                    "content,ignorechecktimestamp",
                    reader_timestamp,
                    out output_timestamp,
                    out strOutputPath,
                    out strError);
                if (lRet == -1)
                    goto ERROR1;


            }


            return 0;
        ERROR1:
            if (level == RecoverLevel.LogicAndSnapshot)
            {
                level = RecoverLevel.Snapshot;
                goto DO_SNAPSHOT;
            }
            return -1;
        }
Beispiel #6
0
        /*
settlement 结算违约金

API: Settlement()

<root>
  <operation>settlement</operation> 操作类型
  <action>...</action> 具体动作 有settlement undosettlement delete 3种
  <id>1234567-1</id> ID
  <operator>test</operator> 操作者
  <operTime>Fri, 08 Dec 2006 04:17:45 GMT</operTime> 操作时间
  
  <oldAmerceRecord recPath='...'>...</oldAmerceRecord>	旧违约金记录
  <amerceRecord recPath='...'>...</amerceRecord>	新违约金记录 delete操作无此元素
</root>
         * */
        public int RecoverSettlement(
            RmsChannelCollection Channels,
            RecoverLevel level,
            XmlDocument domLog,
            out string strError)
        {
            strError = "";

            // 暂时把Robust当作Logic处理
            if (level == RecoverLevel.Robust)
                level = RecoverLevel.Logic;

            long lRet = 0;
            int nRet = 0;

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

        DO_SNAPSHOT:

            // 快照恢复
            if (level == RecoverLevel.Snapshot)
            {
                string strAction = DomUtil.GetElementText(domLog.DocumentElement,
                    "action");

                if (strAction == "settlement"
                    || strAction == "undosettlement")
                {

                    XmlNode node = null;
                    string strAmerceXml = DomUtil.GetElementText(domLog.DocumentElement,
                        "amerceRecord",
                        out node);
                    if (node == null)
                    {
                        strError = "日志记录中缺<amerceRecord>元素";
                        return -1;
                    }
                    string strAmerceRecPath = DomUtil.GetAttr(node, "recPath");

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

                    // 写违约金记录
                    lRet = channel.DoSaveTextRes(strAmerceRecPath,
                        strAmerceXml,
                        false,
                        "content,ignorechecktimestamp",
                        timestamp,
                        out output_timestamp,
                        out strOutputPath,
                        out strError);
                    if (lRet == -1)
                    {
                        strError = "写入违约金记录 '" + strAmerceRecPath + "' 时发生错误: " + strError;
                        return -1;
                    }

                }
                else if (strAction == "delete")
                {
                    XmlNode node = null;
                    string strOldAmerceXml = DomUtil.GetElementText(domLog.DocumentElement,
                        "oldAmerceRecord",
                        out node);
                    if (node == null)
                    {
                        strError = "日志记录中缺<oldAmerceRecord>元素";
                        return -1;
                    }
                    string strOldAmerceRecPath = DomUtil.GetAttr(node, "recPath");

                    // 删除违约金记录
                    int nRedoCount = 0;
                    byte[] timestamp = null;
                    byte[] output_timestamp = null;

                REDO_DELETE:
                    lRet = channel.DoDeleteRes(strOldAmerceRecPath,
                        timestamp,
                        out output_timestamp,
                        out strError);
                    if (lRet == -1)
                    {
                        if (channel.ErrorCode == ChannelErrorCode.NotFound)
                            return 0;   // 记录本来就不存在

                        if (channel.ErrorCode == ChannelErrorCode.TimestampMismatch)
                        {
                            if (nRedoCount < 10)
                            {
                                timestamp = output_timestamp;
                                nRedoCount++;
                                goto REDO_DELETE;
                            }
                        }
                        strError = "删除违约金记录 '" + strOldAmerceRecPath + "' 时发生错误: " + strError;
                        return -1;

                    }
                }
                else
                {
                    strError = "未能识别的action值 '" + strAction + "'";
                }

                return 0;
            }

            // 逻辑恢复或者混合恢复
            if (level == RecoverLevel.Logic
                || level == RecoverLevel.LogicAndSnapshot)
            {
                string strAction = DomUtil.GetElementText(domLog.DocumentElement,
                    "action");

                string strID = DomUtil.GetElementText(domLog.DocumentElement,
                    "id");

                ///
                if (String.IsNullOrEmpty(strID) == true)
                {
                    strError = "日志记录中<id>元素值为空";
                    goto ERROR1;
                }

                string strOperator = DomUtil.GetElementText(domLog.DocumentElement,
                    "operator");

                string strOperTime = DomUtil.GetElementText(domLog.DocumentElement,
                    "operTime");

                // 通过id获得违约金记录的路径
                string strText = "";
                string strCount = "";

                strCount = "<maxCount>100</maxCount>";

                strText = "<item><word>"
    + StringUtil.GetXmlStringSimple(strID)
    + "</word>"
    + strCount
    + "<match>exact</match><relation>=</relation><dataType>string</dataType>"
    + "</item>";
                string strQueryXml = "<target list='"
                    + StringUtil.GetXmlStringSimple(this.AmerceDbName + ":" + "ID")       // 2007/9/14
                    + "'>" + strText
    + "<lang>zh</lang></target>";

                lRet = channel.DoSearch(strQueryXml,
                    "amerced",
                    "", // strOuputStyle
                    out strError);
                if (lRet == -1)
                {
                    strError = "检索ID为 '" + strID + "' 的违约金记录出错: " + strError;
                    goto ERROR1;
                }

                if (lRet == 0)
                {
                    strError = "没有找到id为 '" + strID + "' 的违约金记录";
                    goto ERROR1;
                }

                List<string> aPath = null;
                lRet = channel.DoGetSearchResult(
                    "amerced",   // strResultSetName
                    0,
                    1,
                    "zh",
                    null,   // stop
                    out aPath,
                    out strError);
                if (lRet == -1)
                    goto ERROR1;

                if (lRet == 0)
                {
                    strError = "获取结果集未命中";
                    goto ERROR1;
                }

                if (aPath.Count != 1)
                {
                    strError = "aPath.Count != 1";
                    goto ERROR1;
                }

                string strAmerceRecPath = aPath[0];

                // 结算一个交费记录
                // parameters:
                //      bCreateOperLog  是否创建日志
                //      strOperTime 结算的操作时间
                //      strOperator 结算的操作者
                // return:
                //      -2  致命出错,不宜再继续循环调用本函数
                //      -1  一般出错,可以继续循环调用本函数
                //      0   正常
                nRet = SettlementOneRecord(
                    "", // 确保可以执行
                    false,  // 不创建日志
                    channel,
                    strAction,
                    strAmerceRecPath,
                    strOperTime,
                    strOperator,
                    "", // 表示本机触发
                    out strError);
                if (nRet == -1 || nRet == -2)
                    goto ERROR1;

            }


            return 0;
        ERROR1:
            if (level == RecoverLevel.LogicAndSnapshot)
            {
                level = RecoverLevel.Snapshot;
                goto DO_SNAPSHOT;
            }
            return -1;
        }
Beispiel #7
0
        /*
<root>
  <operation>devolveReaderInfo</operation> 
  <sourceReaderBarcode>...</sourceReaderBarcode> 源读者证条码号
  <targetReaderBarcode>...</targetReaderBarcode> 目标读者证条码号
  <borrows>...</borrows> 移动过去的<borrows>内容,下级为<borrow>元素
  <overdues>...</overdues> 移动过去的<overdue>内容,下级为<overdue>元素
  <sourceReaderRecord recPath='...'>...</sourceReaderRecord>	最新源读者记录
  <targetReaderRecord recPath='...'>...</targetReaderRecord>	最新目标读者记录
  <changedEntityRecord recPath='...' attahchmentIndex='.'>...</changedEntityRecord> 所牵连到的发生了修改的实体记录。此元素的文本即是记录体,但注意为不透明的字符串(HtmlEncoding后的记录字符串)。如果存在attachmentIndex属性,则表明实体记录不在此元素文本中,而在日志记录的附件中
  <operator>test</operator> 
  <operTime>Fri, 08 Dec 2006 10:12:20 GMT</operTime> 
</root>
         * * */
        public int RecoverDevolveReaderInfo(
            RmsChannelCollection Channels,
            RecoverLevel level,
            XmlDocument domLog,
            Stream attachmentLog,
            out string strError)
        {
            strError = "";

            // 暂时把Robust当作Logic处理
            if (level == RecoverLevel.Robust)
                level = RecoverLevel.Logic;

            long lRet = 0;
            int nRet = 0;

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

        DO_SNAPSHOT:

            // 快照恢复
            if (level == RecoverLevel.Snapshot)
            {
                /*
                // 观察是否有<warning>元素
                XmlNode nodeWarning = domLog.SelectSingleNode("warning");
                if (nodeWarning != null)
                {
                    // 如果<warning元素存在,表明只能采用逻辑恢复>
                    strError = nodeWarning.InnerText;
                    return -1;
                }
                */

                // 获源读者记录
                XmlNode node = null;
                string strSourceReaderXml = DomUtil.GetElementText(
                    domLog.DocumentElement,
                    "sourceReaderRecord",
                    out node);
                if (node == null)
                {
                    strError = "日志记录中缺<sourceReaderRecord>元素";
                    return -1;
                }
                string strSourceReaderRecPath = DomUtil.GetAttr(node, "recPath");

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

                // 写源读者记录
                lRet = channel.DoSaveTextRes(strSourceReaderRecPath,
    strSourceReaderXml,
    false,
    "content,ignorechecktimestamp",
    timestamp,
    out output_timestamp,
    out strOutputPath,
    out strError);
                if (lRet == -1)
                {
                    strError = "写入源读者记录 '" + strSourceReaderRecPath + "' 时发生错误: " + strError;
                    return -1;
                }

                // 获目标读者记录
                node = null;
                string strTargetReaderXml = DomUtil.GetElementText(
                    domLog.DocumentElement,
                    "targetReaderRecord",
                    out node);
                if (node == null)
                {
                    strError = "日志记录中缺<targetReaderRecord>元素";
                    return -1;
                }
                string strTargetReaderRecPath = DomUtil.GetAttr(node, "recPath");

                // 写目标读者记录
                lRet = channel.DoSaveTextRes(strTargetReaderRecPath,
                    strTargetReaderXml,
                    false,
                    "content,ignorechecktimestamp",
                    timestamp,
                    out output_timestamp,
                    out strOutputPath,
                    out strError);
                if (lRet == -1)
                {
                    strError = "写入目标读者记录 '" + strSourceReaderRecPath + "' 时发生错误: " + strError;
                    return -1;
                }

                // 循环,写入相关的若干实体记录
                XmlNodeList nodeEntities = domLog.DocumentElement.SelectNodes("changedEntityRecord");
                for (int i = 0; i < nodeEntities.Count; i++)
                {
                    XmlNode nodeEntity = nodeEntities[i];

                    string strItemRecPath = DomUtil.GetAttr(nodeEntity,
                        "recPath");
                    string strAttachmentIndex = DomUtil.GetAttr(nodeEntity,
                        "attachmentIndex");

                    string strItemXml = "";

                    if (String.IsNullOrEmpty(strAttachmentIndex) == true)
                    {
                        strItemXml = nodeEntity.InnerText;
                        if (String.IsNullOrEmpty(strItemXml) == true)
                        {
                            strError = "<changedEntityRecord>元素缺乏文本内容。";
                            return -1;
                        }
                    }
                    else
                    {
                        // 实体记录在附件中
                        int nAttachmentIndex = 0;
                        try
                        {
                            nAttachmentIndex = Convert.ToInt32(strAttachmentIndex);
                        }
                        catch
                        {
                            strError = "<changedEntityRecord>元素的attachmentIndex属性值'"+strAttachmentIndex+"'格式不正确,应当为>=0的纯数字";
                            return -1;
                        }

                        byte[] baItem = null;
                        nRet = GetAttachmentRecord(
                            attachmentLog,
                            nAttachmentIndex,
                            out baItem,
                            out strError);
                        if (nRet == -1)
                        {
                            strError = "获得 index 为 "+nAttachmentIndex.ToString()+" 的日志附件记录时出错:" + strError;
                            return -1;
                        }
                        strItemXml = Encoding.UTF8.GetString(baItem);
                    }

                    // 写册记录
                    lRet = channel.DoSaveTextRes(strItemRecPath,
                        strItemXml,
                        false,
                        "content,ignorechecktimestamp",
                        timestamp,
                        out output_timestamp,
                        out strOutputPath,
                        out strError);
                    if (lRet == -1)
                    {
                        strError = "写入册记录 '" + strItemRecPath + "' 时发生错误: " + strError;
                        return -1;
                    }
                }

                return 0;
            }

            // 逻辑恢复或者混合恢复
            if (level == RecoverLevel.Logic
                || level == RecoverLevel.LogicAndSnapshot)
            {
                string strOperTimeString = DomUtil.GetElementText(domLog.DocumentElement,
                    "operTime");

                string strSourceReaderBarcode = DomUtil.GetElementText(domLog.DocumentElement,
                    "sourceReaderBarcode");
                if (String.IsNullOrEmpty(strSourceReaderBarcode) == true)
                {
                    strError = "<sourceReaderBarcode>元素值为空";
                    goto ERROR1;
                }

                string strTargetReaderBarcode = DomUtil.GetElementText(domLog.DocumentElement,
                    "targetReaderBarcode");
                if (String.IsNullOrEmpty(strTargetReaderBarcode) == true)
                {
                    strError = "<targetReaderBarcode>元素值为空";
                    goto ERROR1;
                }

                // 读入源读者记录
                string strSourceReaderXml = "";
                string strSourceOutputReaderRecPath = "";
                byte[] source_reader_timestamp = null;

                nRet = this.GetReaderRecXml(
                    // Channels,
                    channel,
                    strSourceReaderBarcode,
                    out strSourceReaderXml,
                    out strSourceOutputReaderRecPath,
                    out source_reader_timestamp,
                    out strError);
                if (nRet == 0)
                {
                    strError = "源读者证条码号 '" + strSourceReaderBarcode + "' 不存在";
                    goto ERROR1;
                }
                if (nRet == -1)
                {
                    strError = "读入证条码号为 '" + strSourceReaderBarcode + "' 的源读者记录时发生错误: " + strError;
                    goto ERROR1;
                }

                XmlDocument source_readerdom = null;
                nRet = LibraryApplication.LoadToDom(strSourceReaderXml,
                    out source_readerdom,
                    out strError);
                if (nRet == -1)
                {
                    strError = "装载源读者记录进入XML DOM时发生错误: " + strError;
                    goto ERROR1;
                }

                //
                // 读入目标读者记录
                string strTargetReaderXml = "";
                string strTargetOutputReaderRecPath = "";
                byte[] target_reader_timestamp = null;

                nRet = this.GetReaderRecXml(
                    // Channels,
                    channel,
                    strTargetReaderBarcode,
                    out strTargetReaderXml,
                    out strTargetOutputReaderRecPath,
                    out target_reader_timestamp,
                    out strError);
                if (nRet == 0)
                {
                    strError = "目标读者证条码号 '" + strTargetReaderBarcode + "' 不存在";
                    goto ERROR1;
                }
                if (nRet == -1)
                {
                    strError = "读入证条码号为 '" + strTargetReaderBarcode + "' 的目标读者记录时发生错误: " + strError;
                    goto ERROR1;
                }

                XmlDocument target_readerdom = null;
                nRet = LibraryApplication.LoadToDom(strTargetReaderXml,
                    out target_readerdom,
                    out strError);
                if (nRet == -1)
                {
                    strError = "装载目标读者记录进入XML DOM时发生错误: " + strError;
                    goto ERROR1;
                }

                Stream tempstream = null;

                // 移动信息
                XmlDocument domTemp = null;
                // 移动借阅信息 -- <borrows>元素内容
                // return:
                //      -1  error
                //      0   not found brrowinfo
                //      1   found and moved
                nRet = DevolveBorrowInfo(
                    // Channels,
                    channel,
                    strSourceReaderBarcode,
                    strTargetReaderBarcode,
                    strOperTimeString,
                    ref source_readerdom,
                    ref target_readerdom,
                    ref domTemp,
                    "",
                    out tempstream,
                    out strError);
                if (nRet == -1)
                    goto ERROR1;

                // 移动超期违约金信息 -- <overdues>元素内容
                // return:
                //      -1  error
                //      0   not found overdueinfo
                //      1   found and moved
                nRet = DevolveOverdueInfo(
                    strSourceReaderBarcode,
                    strTargetReaderBarcode,
                    strOperTimeString,
                    ref source_readerdom,
                    ref target_readerdom,
                    ref domTemp,
                    out strError);
                if (nRet == -1)
                    goto ERROR1;

                // 写回读者记录
                byte[] output_timestamp = null;
                string strOutputPath = "";

                // 写回源读者记录
                lRet = channel.DoSaveTextRes(strSourceOutputReaderRecPath,
                    source_readerdom.OuterXml,
                    false,
                    "content,ignorechecktimestamp",
                    source_reader_timestamp,
                    out output_timestamp,
                    out strOutputPath,
                    out strError);
                if (lRet == -1)
                    goto ERROR1;

                // 写回目标读者记录
                lRet = channel.DoSaveTextRes(strTargetOutputReaderRecPath,
                    target_readerdom.OuterXml,
                    false,
                    "content,ignorechecktimestamp",
                    source_reader_timestamp,
                    out output_timestamp,
                    out strOutputPath,
                    out strError);
                if (lRet == -1)
                    goto ERROR1;
            }

            return 0;
        ERROR1:
            if (level == RecoverLevel.LogicAndSnapshot)
            {
                level = RecoverLevel.Snapshot;
                goto DO_SNAPSHOT;
            }
            return -1;
        }
Beispiel #8
0
        // SetEntities() API 恢复动作
        /* 日志记录格式
<root>
  <operation>setEntity</operation> 操作类型
  <action>new</action> 具体动作。有new change delete 3种
  <style>...</style> 风格。有force nocheckdup noeventlog 3种
  <record recPath='中文图书实体/3'><root><parent>2</parent><barcode>0000003</barcode><state>状态2</state><location>阅览室</location><price></price><bookType>教学参考</bookType><registerNo></registerNo><comment>test</comment><mergeComment></mergeComment><batchNo>111</batchNo><borrower></borrower><borrowDate></borrowDate><borrowPeriod></borrowPeriod></root></record> 记录体
  <oldRecord recPath='中文图书实体/3'>...</oldRecord> 被覆盖或者删除的记录 动作为change和delete时具备此元素
  <operator>test</operator> 操作者
  <operTime>Fri, 08 Dec 2006 08:41:46 GMT</operTime> 操作时间
</root>

注:1) 当<action>为delete时,没有<record>元素。为new时,没有<oldRecord>元素。
	2) <record>中的内容, 涉及到流通的<borrower><borrowDate><borrowPeriod>等, 在日志恢复阶段, 都应当无效, 这几个内容应当从当前位置库中记录获取, 和<record>中其他内容合并后, 再写入数据库
	3) 一次SetEntities()API调用, 可能创建多条日志记录。
         
         * */
        // TODO: 要兑现style中force nocheckdup功能
        public int RecoverSetEntity(
            RmsChannelCollection Channels,
            RecoverLevel level,
            XmlDocument domLog,
            out string strError)
        {
            strError = "";

            long lRet = 0;
            int nRet = 0;

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

            bool bReuse = false;    // 是否能够不顾RecorverLevel状态而重用部分代码

        DO_SNAPSHOT:

            string strAction = DomUtil.GetElementText(domLog.DocumentElement,
                "action");

            // 快照恢复
            if (level == RecoverLevel.Snapshot
                || bReuse == true)
            {

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

                if (strAction == "new" 
                    || strAction == "change"
                    || strAction == "move")
                {
                    XmlNode node = null;
                    string strRecord = DomUtil.GetElementText(domLog.DocumentElement,
                        "record",
                        out node);
                    if (node == null)
                    {
                        strError = "日志记录中缺<record>元素";
                        return -1;
                    }

                    string strNewRecPath = DomUtil.GetAttr(node, "recPath");

                    // 
                    string strOldRecord = "";
                    string strOldRecPath = "";
                    if (strAction == "move")
                    {
                        strOldRecord = DomUtil.GetElementText(domLog.DocumentElement,
                            "oldRecord",
                            out node);
                        if (node == null)
                        {
                            strError = "日志记录中缺<oldRecord>元素";
                            return -1;
                        }

                        strOldRecPath = DomUtil.GetAttr(node, "recPath");
                    }

                    // 写册记录
                    lRet = channel.DoSaveTextRes(strNewRecPath,
                        strRecord,
                        false,
                        "content,ignorechecktimestamp",
                        timestamp,
                        out output_timestamp,
                        out strOutputPath,
                        out strError);
                    if (lRet == -1)
                    {
                        strError = "写入册记录 '" + strNewRecPath + "' 时发生错误: " + strError;
                        return -1;
                    }

                    if (strAction == "move")
                    {
                        // 删除册记录
                        int nRedoCount = 0;

                    REDO_DELETE:
                        lRet = channel.DoDeleteRes(strOldRecPath,
                            timestamp,
                            out output_timestamp,
                            out strError);
                        if (lRet == -1)
                        {
                            if (channel.ErrorCode == ChannelErrorCode.NotFound)
                                return 0;   // 记录本来就不存在

                            if (channel.ErrorCode == ChannelErrorCode.TimestampMismatch)
                            {
                                if (nRedoCount < 10)
                                {
                                    timestamp = output_timestamp;
                                    nRedoCount++;
                                    goto REDO_DELETE;
                                }
                            }
                            strError = "删除册记录 '" + strOldRecPath + "' 时发生错误: " + strError;
                            return -1;

                        }
                    }
                     
                }
                else if (strAction == "delete")
                {
                    XmlNode node = null;
                    string strOldRecord = DomUtil.GetElementText(domLog.DocumentElement,
                        "oldRecord",
                        out node);
                    if (node == null)
                    {
                        strError = "日志记录中缺<oldRecord>元素";
                        return -1;
                    }
                    string strRecPath = DomUtil.GetAttr(node, "recPath");

                    int nRedoCount = 0;
                REDO:
                    // 删除册记录
                    lRet = channel.DoDeleteRes(strRecPath,
                        timestamp,
                        out output_timestamp,
                        out strError);
                    if (lRet == -1)
                    {
                        if (channel.ErrorCode == ChannelErrorCode.NotFound)
                            return 0;   // 记录本来就不存在
                        if (channel.ErrorCode == ChannelErrorCode.TimestampMismatch)
                        {
                            if (nRedoCount < 10)
                            {
                                timestamp = output_timestamp;
                                nRedoCount++;
                                goto REDO;
                            }
                        }
                        strError = "删除册记录 '" + strRecPath + "' 时发生错误: " + strError;
                        return -1;

                    }
                }
                else
                {
                    strError = "无法识别的<action>内容 '" + strAction + "'";
                    return -1;
                }


                return 0;
            }

            bool bForce = false;
            bool bNoCheckDup = false;

            string strStyle = DomUtil.GetElementText(domLog.DocumentElement,
                "style");

            if (StringUtil.IsInList("force", strStyle) == true)
                bForce = true;

            if (StringUtil.IsInList("nocheckdup", strStyle) == true)
                bNoCheckDup = true;

            // 逻辑恢复或者混合恢复
            if (level == RecoverLevel.Logic
                || level == RecoverLevel.LogicAndSnapshot)
            {



                // 和数据库中已有记录合并,然后保存
                if (strAction == "new"
                    || strAction == "change"
                    || strAction == "move")
                {
                    XmlNode node = null;
                    string strRecord = DomUtil.GetElementText(domLog.DocumentElement,
                        "record",
                        out node);
                    if (node == null)
                    {
                        strError = "日志记录中缺<record>元素";
                        return -1;
                    }

                    string strNewRecPath = DomUtil.GetAttr(node, "recPath");

                    // 
                    string strOldRecord = "";
                    string strOldRecPath = "";
                    if (strAction == "move")
                    {
                        strOldRecord = DomUtil.GetElementText(domLog.DocumentElement,
                            "oldRecord",
                            out node);
                        if (node == null)
                        {
                            strError = "日志记录中缺<oldRecord>元素";
                            return -1;
                        }

                        strOldRecPath = DomUtil.GetAttr(node, "recPath");
                    }


                    // 读出数据库中原有的记录
                    string strExistXml = "";
                    string strMetaData = "";
                    byte[] exist_timestamp = null;
                    string strOutputPath = "";

                    if ((strAction == "change" 
                        || strAction == "move")
                        && bForce == false) // 2008/10/6
                    {
                        string strSourceRecPath = "";

                        if (strAction == "change")
                            strSourceRecPath = strNewRecPath;
                        if (strAction == "move")
                            strSourceRecPath = strOldRecPath;

                        lRet = channel.GetRes(strSourceRecPath,
                            out strExistXml,
                            out strMetaData,
                            out exist_timestamp,
                            out strOutputPath,
                            out strError);
                        if (lRet == -1)
                        {
                            // 容错
                            if (channel.ErrorCode == ChannelErrorCode.NotFound
                                && level == RecoverLevel.LogicAndSnapshot)
                            {
                                // 如果记录不存在, 则构造一条空的记录
                                // bExist = false;
                                strExistXml = "<root />";
                                exist_timestamp = null;
                            }
                            else
                            {
                                strError = "在读入原有记录 '"+strNewRecPath+"' 时失败: " + strError;
                                goto ERROR1;
                            }
                        }
                    }

                    //
                    // 把两个记录装入DOM

                    XmlDocument domExist = new XmlDocument();
                    XmlDocument domNew = new XmlDocument();

                    try
                    {
                        // 防范空记录
                        if (String.IsNullOrEmpty(strExistXml) == true)
                            strExistXml = "<root />";

                        domExist.LoadXml(strExistXml);
                    }
                    catch (Exception ex)
                    {
                        strError = "strExistXml装载进入DOM时发生错误: " + ex.Message;
                        goto ERROR1;
                    }

                    try
                    {
                        domNew.LoadXml(strRecord);
                    }
                    catch (Exception ex)
                    {
                        strError = "strRecord装载进入DOM时发生错误: " + ex.Message;
                        goto ERROR1;
                    }

                    // 合并新旧记录
                    string strNewXml = "";

                    if (bForce == false)
                    {
                        nRet = MergeTwoEntityXml(domExist,
                            domNew,
                            out strNewXml,
                            out strError);
                        if (nRet == -1)
                            goto ERROR1;
                    }
                    else
                    {
                        strNewXml = domNew.OuterXml;
                    }

                    // 保存新记录
                    byte[] output_timestamp = null;

                    if (strAction == "move")
                    {
                        // 复制源记录到目标位置,然后自动删除源记录
                        // 但是尚未在目标位置写入最新内容
                        lRet = channel.DoCopyRecord(strOldRecPath,
                            strNewRecPath,
                            true,   // bDeleteSourceRecord
                            out output_timestamp,
                            out strOutputPath,
                            out strError);
                        if (lRet == -1)
                            goto ERROR1;

                        exist_timestamp = output_timestamp; // 及时更新时间戳
                    }


                    lRet = channel.DoSaveTextRes(strNewRecPath,
                        strNewXml,
                        false,   // include preamble?
                        "content,ignorechecktimestamp",
                        exist_timestamp,
                        out output_timestamp,
                        out strOutputPath,
                        out strError);
                    if (lRet == -1)
                        goto ERROR1;

                    /*
                    if (strAction == "move")
                    {
                        // 删除册记录
                        int nRedoCount = 0;

                        byte[] timestamp = null;

                    REDO_DELETE:
                        lRet = channel.DoDeleteRes(strOldRecPath,
                            timestamp,
                            out output_timestamp,
                            out strError);
                        if (lRet == -1)
                        {
                            if (channel.ErrorCode == ChannelErrorCode.NotFound)
                                return 0;   // 记录本来就不存在

                            if (channel.ErrorCode == ChannelErrorCode.TimestampMismatch)
                            {
                                if (nRedoCount < 10)
                                {
                                    timestamp = output_timestamp;
                                    nRedoCount++;
                                    goto REDO_DELETE;
                                }
                            }
                            strError = "删除册记录 '" + strRecPath + "' 时发生错误: " + strError;
                            return -1;

                        }
                    }
                     * */
                }
                else if (strAction == "delete")
                {
                    // 和SnapShot方式相同
                    bReuse = true;
                    goto DO_SNAPSHOT;
                }
                else
                {
                    strError = "无法识别的<action>内容 '" + strAction + "'";
                    return -1;
                }
            }

            // 容错恢复
            if (level == RecoverLevel.Robust)
            {
                if (strAction == "move")
                {
                    strError = "暂不支持SetEntity的move恢复操作";
                    return -1;
                }

                // 和数据库中已有记录合并,然后保存
                if (strAction == "change" || strAction == "new")
                {
                    XmlNode node = null;
                    string strRecord = DomUtil.GetElementText(domLog.DocumentElement,
                        "record",
                        out node);
                    if (node == null)
                    {
                        strError = "日志记录中缺<record>元素";
                        return -1;
                    }

                    // 取得日志记录中声称的新记录路径。不能轻易相信这个路径。
                    string strNewRecPath = DomUtil.GetAttr(node, "recPath");

                    // 
                    string strOldRecord = "";
                    string strOldRecPath = "";

                    string strOldItemBarcode = "";
                    string strNewItemBarcode = "";


                    string strExistXml = "";
                    byte[] exist_timestamp = null;


                    // 日志记录中记载的旧记录体
                    strOldRecord = DomUtil.GetElementText(domLog.DocumentElement,
                        "oldRecord",
                        out node);
                    if (node == null)
                    {
                        if (strAction == "change")
                        {
                            strError = "日志记录中缺<oldRecord>元素";
                            return -1;
                        }
                    }

                    // 日志记录中声称的旧记录路径。不能轻易相信这个路径。
                    if (node != null)
                        strOldRecPath = DomUtil.GetAttr(node, "recPath");


                    // 从日志记录中记载的旧记录体中,获得旧记录册条码号
                    if (String.IsNullOrEmpty(strOldRecord) == false)
                    {
                        nRet = GetItemBarcode(strOldRecord,
                            out strOldItemBarcode,
                            out strError);
                    }

                    nRet = GetItemBarcode(strRecord,
                        out strNewItemBarcode,
                        out strError);

                    // TODO: 需要检查新旧记录中,<barcode>是否一致?如果不一致,则需要对新条码号进行查重?
                    if (strAction == "new" && strOldItemBarcode == "")
                    {
                        if (String.IsNullOrEmpty(strNewItemBarcode) == true)
                        {
                            strError = "因为拟新创建的记录内容中没有包含册条码号,所以new操作被放弃";
                            return -1;
                        }

                        strOldItemBarcode = strNewItemBarcode;
                    }


                    // 如果有旧记录的册条码号,则需要从数据库中提取最新鲜的旧记录
                    // (如果没有旧记录的册条码号,则依日志记录中的旧记录)
                    if (String.IsNullOrEmpty(strOldItemBarcode) == false)
                    {
                        string strOutputItemRecPath = "";

                        // 从册条码号获得册记录
                        List<string> aPath = null;

                        // 获得册记录
                        // return:
                        //      -1  error
                        //      0   not found
                        //      1   命中1条
                        //      >1  命中多于1条
                        nRet = this.GetItemRecXml(
                            // Channels,
                            channel,
                            strOldItemBarcode,
                            out strExistXml,
                            100,
                            out aPath,
                            out exist_timestamp,
                            out strError);
                        if (nRet == 0 || nRet == -1)
                        {
                            if (strAction == "change")
                            {
                                /*
                                // 从库中没有找到,只好依日志记录中记载的旧记录
                                strExistXml = strOldRecord;
                                 * */
                                strExistXml = "";

                                // 需要创建一条新记录。strOldRecPath中的路径似乎也可以用,但是要严格检查这个路径是否已经存在记录 -- 只能在这里位置不存在记录时才能用。既然如此麻烦,那就不如纯粹用一个新位置
                                strOutputItemRecPath = ResPath.GetDbName(strOldRecPath) + "/?";
                            }
                            else
                            {
                                Debug.Assert(strAction == "new", "");
                                strExistXml = "";
                                strOutputItemRecPath = ResPath.GetDbName(strNewRecPath) + "/?";
                            }
                        }
                        else
                        {
                            // 找到一条或者多条旧记录
                            Debug.Assert(aPath != null && aPath.Count >= 1, "");

                            bool bNeedReload = false;

                            if (aPath.Count == 1)
                            {
                                Debug.Assert(nRet == 1, "");

                                strOutputItemRecPath = aPath[0];

                                // 是否需要重新装载?
                                bNeedReload = false;    // 所取得的第一个路径,其记录已经装载
                            }
                            else
                            {
                                // 多条
                                Debug.Assert(aPath.Count > 1, "");

                                ///
                                // 建议根据strOldRecPath来进行挑选
                                if (String.IsNullOrEmpty(strOldRecPath) == true)
                                {
                                    // 空,无法挑选

                                    // 容错!
                                    strOutputItemRecPath = aPath[0];

                                    // 是否需要重新装载?
                                    bNeedReload = false;    // 所取得的第一个路径,其记录已经装载
                                }
                                else
                                {

                                    ///// 
                                    nRet = aPath.IndexOf(strOldRecPath);
                                    if (nRet != -1)
                                    {
                                        // 选中
                                        strOutputItemRecPath = aPath[nRet];

                                        // 是否需要重新装载?
                                        if (nRet != 0)
                                            bNeedReload = true; // 第一个以外的路径才需要装载

                                    }
                                    else
                                    {
                                        // 没有选中,只好依第一个

                                        // 容错
                                        strOutputItemRecPath = aPath[0];

                                        // 是否需要重新装载?
                                        bNeedReload = false;    // 所取得的第一个路径,其记录已经装载
                                    }
                                }

                                ///

                            }

                            // 重新装载
                            if (bNeedReload == true)
                            {
                                string strMetaData = "";
                                lRet = channel.GetRes(strOutputItemRecPath,
                                    out strExistXml,
                                    out strMetaData,
                                    out exist_timestamp,
                                    out strOutputItemRecPath,
                                    out strError);
                                if (lRet == -1)
                                {
                                    strError = "根据strOutputItemRecPath '" + strOutputItemRecPath + "' 重新获得册记录失败: " + strError;
                                    return -1;
                                }

                                // 需要检查记录中的<barcode>元素值是否匹配册条码号

                            }

                        }

                        // 修正strOldRecPath
                        if (strOutputItemRecPath != "")
                            strOldRecPath = strOutputItemRecPath;
                        else
                            strOldRecPath = ""; // 破坏掉,以免后面被用

                        strNewRecPath = strOutputItemRecPath;

                    } // end if 如果有旧记录的册条码号
                    else
                    {
                        // (如果没有旧记录的册条码号,则依日志记录中的旧记录)
                        // 但无法确定旧记录的路径。也就无法确定覆盖位置。因此建议放弃这种特定的“修改操作”。
                        strError = "因为日志记录中没有记载旧记录条码号,因此无法确定记录位置,因此change操作被放弃";
                        return -1;
                    }

                    if (strAction == "change")
                    {
                        if (strNewItemBarcode != ""
                            && strNewItemBarcode != strOldItemBarcode)
                        {
                            // 新旧记录的条码号不一致,需要对新条码号进行查重
                            List<string> aPath = null;

                            string strTempXml = "";
                            byte[] temp_timestamp = null;
                            // 获得册记录
                            // return:
                            //      -1  error
                            //      0   not found
                            //      1   命中1条
                            //      >1  命中多于1条
                            nRet = this.GetItemRecXml(
                                // Channels,
                                channel,
                                strNewItemBarcode,
                                out strTempXml,
                                100,
                                out aPath,
                                out temp_timestamp,
                                out strError);
                            if (nRet > 0)
                            {
                                // 有重复,取其第一条,作为老记录进行合并,并保存回这条的位置
                                strNewRecPath = aPath[0];
                                exist_timestamp = temp_timestamp;
                                strExistXml = strTempXml;
                            }
                        }
                    }


                    // 把两个记录装入DOM
                    XmlDocument domExist = new XmlDocument();
                    XmlDocument domNew = new XmlDocument();

                    try
                    {
                        // 防范空记录
                        if (String.IsNullOrEmpty(strExistXml) == true)
                            strExistXml = "<root />";

                        domExist.LoadXml(strExistXml);
                    }
                    catch (Exception ex)
                    {
                        strError = "strExistXml装载进入DOM时发生错误: " + ex.Message;
                        goto ERROR1;
                    }

                    try
                    {
                        domNew.LoadXml(strRecord);
                    }
                    catch (Exception ex)
                    {
                        strError = "strRecord装载进入DOM时发生错误: " + ex.Message;
                        goto ERROR1;
                    }




                    // 合并新旧记录
                    string strNewXml = "";

                    if (bForce == false)
                    {
                        nRet = MergeTwoEntityXml(domExist,
                            domNew,
                            out strNewXml,
                            out strError);
                        if (nRet == -1)
                            goto ERROR1;
                    }
                    else
                    {
                        strNewXml = domNew.OuterXml;
                    }

                    // 保存新记录
                    byte[] output_timestamp = null;

                    string strOutputPath = "";

                    if (strAction == "move")
                    {
                        // 复制源记录到目标位置,然后自动删除源记录
                        // 但是尚未在目标位置写入最新内容
                        lRet = channel.DoCopyRecord(strOldRecPath,
                            strNewRecPath,
                            true,   // bDeleteSourceRecord
                            out output_timestamp,
                            out strOutputPath,
                            out strError);
                        if (lRet == -1)
                            goto ERROR1;

                        exist_timestamp = output_timestamp; // 及时更新时间戳
                    }

                    /*
                    // 测试
                    {
                        string strRecID = ResPath.GetRecordId(strNewRecPath);

                        if (strRecID != "?")
                        {
                            try
                            {
                                long id = Convert.ToInt64(strRecID);
                                if (id > 150848)
                                {
                                    Debug.Assert(false, "id超过尾部");
                                }
                            }
                            catch
                            {
                            }
                        }
                    }
                     * */

                    lRet = channel.DoSaveTextRes(strNewRecPath,
                        strNewXml,
                        false,   // include preamble?
                        "content,ignorechecktimestamp",
                        exist_timestamp,
                        out output_timestamp,
                        out strOutputPath,
                        out strError);
                    if (lRet == -1)
                        goto ERROR1;


                }
                else if (strAction == "delete")
                {
                    XmlNode node = null;
                    string strOldRecord = DomUtil.GetElementText(domLog.DocumentElement,
                        "oldRecord",
                        out node);
                    if (node == null)
                    {
                        strError = "日志记录中缺<oldRecord>元素";
                        return -1;
                    }
                    string strRecPath = DomUtil.GetAttr(node, "recPath");

                    string strOldItemBarcode = "";
                    nRet = GetItemBarcode(strOldRecord,
                        out strOldItemBarcode,
                        out strError);
                    if (String.IsNullOrEmpty(strOldItemBarcode) == true)
                    {
                        strError = "因为日志记录中的旧记录中缺乏非空的<barcode>内容,所以无法进行依据条码号定位的删除,delete操作被放弃";
                        return -1;
                    }

                    string strOutputItemRecPath = "";
                    string strExistXml = "";
                    byte[] exist_timestamp = null;

                    // 从册条码号获得册记录
                    List<string> aPath = null;

                    // 获得册记录
                    // return:
                    //      -1  error
                    //      0   not found
                    //      1   命中1条
                    //      >1  命中多于1条
                    nRet = this.GetItemRecXml(
                        // Channels,
                        channel,
                        strOldItemBarcode,
                        out strExistXml,
                        100,
                        out aPath,
                        out exist_timestamp,
                        out strError);
                    if (nRet == -1)
                        return -1;
                    if (nRet == 0)
                    {
                        // 本来就不存在
                        return 0;
                    }
                    if (nRet >= 1)
                    {
                        ///
                        // 找到一条或者多条旧记录
                        Debug.Assert(aPath != null && aPath.Count >= 1, "");

                        bool bNeedReload = false;

                        if (aPath.Count == 1)
                        {
                            Debug.Assert(nRet == 1, "");

                            /*
                            strOutputItemRecPath = aPath[0];

                            // 是否需要重新装载?
                            bNeedReload = false;    // 所取得的第一个路径,其记录已经装载
                             * */
                            strError = "册条码号 " + strOldItemBarcode + " 目前仅有唯一一条记录,放弃删除";
                            return -1;
                        }
                        else
                        {
                            // 多条
                            Debug.Assert(aPath.Count > 1, "");

                            ///
                            // 建议根据strRecPath来进行挑选
                            if (String.IsNullOrEmpty(strRecPath) == true)
                            {
                                strError = "册条码号 '" + strOldItemBarcode + "' 命中 " + aPath.Count.ToString() + " 条记录,而<oldRecord>的recPath参数缺乏,因此无法进行精确删除,delete操作被放弃";
                                return -1;
                            }
                            else
                            {

                                ///// 
                                nRet = aPath.IndexOf(strRecPath);
                                if (nRet != -1)
                                {
                                    // 选中
                                    strOutputItemRecPath = aPath[nRet];

                                    // 是否需要重新装载?
                                    if (nRet != 0)
                                        bNeedReload = true; // 第一个以外的路径才需要装载

                                }
                                else
                                {
                                    strError = "册条码号 '" + strOldItemBarcode + "' 命中 " + aPath.Count.ToString() + " 条记录,虽用了(<oldRecord>元素中属性recPath的)确认路径 '" + strRecPath + "' 也无法确认出其中一条,无法精确删除,因此delete操作被放弃";
                                    return -1;
                                }
                            }



                        }

                        ///

                        // 重新装载
                        if (bNeedReload == true)
                        {
                            string strMetaData = "";
                            lRet = channel.GetRes(strOutputItemRecPath,
                                out strExistXml,
                                out strMetaData,
                                out exist_timestamp,
                                out strOutputItemRecPath,
                                out strError);
                            if (lRet == -1)
                            {
                                strError = "根据strOutputItemRecPath '" + strOutputItemRecPath + "' 重新获得册记录失败: " + strError;
                                return -1;
                            }

                            // 需要检查记录中的<barcode>元素值是否匹配册条码号

                        }

                    }

                    // 把两个记录装入DOM
                    XmlDocument domExist = new XmlDocument();
                    try
                    {
                        // 防范空记录
                        if (String.IsNullOrEmpty(strExistXml) == true)
                            strExistXml = "<root />";

                        domExist.LoadXml(strExistXml);
                    }
                    catch (Exception ex)
                    {
                        strError = "strExistXml装载进入DOM时发生错误: " + ex.Message;
                        return -1;
                    }

                    string strDetail = "";
                    bool bHasCirculationInfo = IsEntityHasCirculationInfo(domExist,
                        out strDetail);


                    // 观察已经存在的记录是否有流通信息
                    if (bHasCirculationInfo == true
                        && bForce == false)
                    {
                        strError = "拟删除的册记录 '" + strOutputItemRecPath + "' 中包含有流通信息("+strDetail+"),不能删除。";
                        goto ERROR1;
                    }

                    int nRedoCount = 0;
                    byte[] timestamp = exist_timestamp;
                    byte[] output_timestamp = null; 

                REDO:
                    // 删除册记录
                    lRet = channel.DoDeleteRes(strOutputItemRecPath,
                        timestamp,
                        out output_timestamp,
                        out strError);
                    if (lRet == -1)
                    {
                        if (channel.ErrorCode == ChannelErrorCode.NotFound)
                            return 0;   // 记录本来就不存在
                        if (channel.ErrorCode == ChannelErrorCode.TimestampMismatch)
                        {
                            if (nRedoCount < 10)
                            {
                                timestamp = output_timestamp;
                                nRedoCount++;
                                goto REDO;
                            }
                        }
                        strError = "删除册记录 '" + strRecPath + "' 时发生错误: " + strError;
                        return -1;

                    }
                }
                else
                {
                    strError = "无法识别的<action>内容 '" + strAction + "'";
                    return -1;
                }
            }

            return 0;
        ERROR1:
            if (level == RecoverLevel.LogicAndSnapshot)
            {
                level = RecoverLevel.Snapshot;
                goto DO_SNAPSHOT;
            }
            return -1;
        }
Beispiel #9
0
        // Borrow() API 恢复动作
        /* 日志记录格式如下
<root>
  <operation>borrow</operation> 操作类型
  <readerBarcode>R0000002</readerBarcode> 读者证条码号
  <itemBarcode>0000001</itemBarcode>  册条码号
  <borrowDate>Fri, 08 Dec 2006 04:17:31 GMT</borrowDate> 借阅日期
  <borrowPeriod>30day</borrowPeriod> 借阅期限
  <no>0</no> 续借次数。0为首次普通借阅,1开始为续借
  <operator>test</operator> 操作者
  <operTime>Fri, 08 Dec 2006 04:17:31 GMT</operTime> 操作时间
  <confirmItemRecPath>...</confirmItemRecPath> 辅助判断用的册记录路径
  
  <readerRecord recPath='...'>...</readerRecord>	最新读者记录
  <itemRecord recPath='...'>...</itemRecord>	最新册记录
</root>
         * */
        // parameters:
        //      bForce  是否为容错状态。在容错状态下,如果遇到重复的册条码号,就算做第一条。
        public int RecoverBorrow(
            RmsChannelCollection Channels,
            RecoverLevel level,
            XmlDocument domLog,
            bool bForce,
            out string strError)
        {
            strError = "";

            long lRet = 0;
            int nRet = 0;

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

            DO_SNAPSHOT:

            // 快照恢复
            if (level == RecoverLevel.Snapshot)
            {
                XmlNode node = null;
                string strReaderXml = DomUtil.GetElementText(domLog.DocumentElement,
                    "readerRecord", 
                    out node);
                if (node == null)
                {
                    strError = "日志记录中缺<readerRecord>元素";
                    return -1;
                }
                string strReaderRecPath = DomUtil.GetAttr(node, "recPath");

                string strItemXml = DomUtil.GetElementText(domLog.DocumentElement,
    "itemRecord",
    out node);
                if (node == null)
                {
                    strError = "日志记录中缺<itemRecord>元素";
                    return -1;
                }
                string strItemRecPath = DomUtil.GetAttr(node, "recPath");

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

                // 写读者记录
                lRet = channel.DoSaveTextRes(strReaderRecPath,
    strReaderXml,
    false,
    "content,ignorechecktimestamp",
    timestamp,
    out output_timestamp,
    out strOutputPath,
    out strError);
                if (lRet == -1)
                {
                    strError = "写入读者记录 '" + strReaderRecPath + "' 时发生错误: " + strError;
                    return -1;
                }

                // 写册记录
                lRet = channel.DoSaveTextRes(strItemRecPath,
strItemXml,
false,
"content,ignorechecktimestamp",
timestamp,
out output_timestamp,
out strOutputPath,
out strError);
                if (lRet == -1)
                {
                    strError = "写入册记录 '" + strItemRecPath + "' 时发生错误: " + strError;
                    return -1;
                }

                return 0;
            }

            // 逻辑恢复或者混合恢复
            if (level == RecoverLevel.Logic
                || level == RecoverLevel.LogicAndSnapshot)
            {
                string strRecoverComment = "";

                string strReaderBarcode = DomUtil.GetElementText(domLog.DocumentElement,
                    "readerBarcode");
                if (String.IsNullOrEmpty(strReaderBarcode) == true)
                {
                    strError = "<readerBarcode>元素值为空";
                    goto ERROR1;
                }

                // 读入读者记录
                string strReaderXml = "";
                string strOutputReaderRecPath = "";
                byte[] reader_timestamp = null;

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

                string strLibraryCode = "";
                // 获得读者库的馆代码
                // return:
                //      -1  出错
                //      0   成功
                nRet = GetLibraryCode(
                        strOutputReaderRecPath,
                        out strLibraryCode,
                        out strError);
                if (nRet == -1)
                    goto ERROR1;

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

                // 读入册记录
                string strConfirmItemRecPath = DomUtil.GetElementText(domLog.DocumentElement,
                    "confirmItemRecPath");
                string strItemBarcode = DomUtil.GetElementText(domLog.DocumentElement,
                    "itemBarcode");
                if (String.IsNullOrEmpty(strItemBarcode) == true)
                {
                    strError = "<strItemBarcode>元素值为空";
                    goto ERROR1;
                }

                string strItemXml = "";
                string strOutputItemRecPath = "";
                byte[] item_timestamp = null;

                // 如果已经有确定的册记录路径
                if (String.IsNullOrEmpty(strConfirmItemRecPath) == false)
                {
                    string strMetaData = "";
                    lRet = channel.GetRes(strConfirmItemRecPath,
                        out strItemXml,
                        out strMetaData,
                        out item_timestamp,
                        out strOutputItemRecPath,
                        out strError);
                    if (lRet == -1)
                    {
                        strError = "根据strConfirmItemRecPath '" + strConfirmItemRecPath + "' 获得册记录失败: " + strError;
                        goto ERROR1;
                    }

                    // 需要检查记录中的<barcode>元素值是否匹配册条码号


                    // TODO: 如果记录路径所表达的记录不存在,或者其<barcode>元素值和要求的册条码号不匹配,那么都要改用逻辑方法,也就是利用册条码号来获得记录。
                    // 当然,这种情况下,非常要紧的是确保数据库的素质很好,本身没有重条码号的情况出现。
                }
                else
                {
                    // 从册条码号获得册记录
                    List<string> aPath = null;

                    // 获得册记录
                    // return:
                    //      -1  error
                    //      0   not found
                    //      1   命中1条
                    //      >1  命中多于1条
                    nRet = this.GetItemRecXml(
                        // Channels,
                        channel,
                        strItemBarcode,
                        out strItemXml,
                        100,
                        out aPath,
                        out item_timestamp,
                        out strError);
                    if (nRet == 0)
                    {
                        strError = "册条码号 '" + strItemBarcode + "' 不存在";
                        goto ERROR1;
                    }
                    if (nRet == -1)
                    {
                        strError = "读入册条码号为 '" + strItemBarcode + "' 的册记录时发生错误: " + strError;
                        goto ERROR1;
                    }

                    if (aPath.Count > 1)
                    {
                        if (bForce == true)
                        {
                            // 容错!
                            strOutputItemRecPath = aPath[0];

                            strRecoverComment += "册条码号 " + strItemBarcode + " 有 "
                                + aPath.Count.ToString() + " 条重复记录,因受容错要求所迫,权且采用其中第一个记录 "
                                + strOutputItemRecPath + " 来进行借阅操作。";
                        }
                        else
                        {
                            strError = "册条码号为 '" + strItemBarcode + "' 的册记录有 " + aPath.Count.ToString() + " 条,但此时comfirmItemRecPath却为空";
                            goto ERROR1;
                        }
                    }
                    else
                    {

                        Debug.Assert(nRet == 1, "");
                        Debug.Assert(aPath.Count == 1, "");

                        if (nRet == 1)
                        {
                            strOutputItemRecPath = aPath[0];
                        }
                    }

                }

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

                // 修改读者记录
                // 修改册记录

                // TODO: 容错情况下如果遇到册条码号是重复的,要写入额外的日志。
                nRet = BorrowChangeReaderAndItemRecord(
                    // Channels,
                    channel,
                    strItemBarcode,
                    strReaderBarcode,
                    domLog,
                    strRecoverComment,
                    strLibraryCode,
                    ref readerdom,
                    ref itemdom,
                    out strError);
                if (nRet == -1)
                    goto ERROR1;

                // 写回读者、册记录
                byte[] output_timestamp = null;
                string strOutputPath = "";


                // 写回读者记录
                lRet = channel.DoSaveTextRes(strOutputReaderRecPath,
                    readerdom.OuterXml,
                    false,
                    "content,ignorechecktimestamp",
                    reader_timestamp,
                    out output_timestamp,
                    out strOutputPath,
                    out strError);
                if (lRet == -1)
                    goto ERROR1;

                // 写回册记录
                lRet = channel.DoSaveTextRes(strOutputItemRecPath,
                    itemdom.OuterXml,
                    false,
                    "content,ignorechecktimestamp",
                    item_timestamp,
                    out output_timestamp,
                    out strOutputPath,
                    out strError);
                if (lRet == -1)
                    goto ERROR1;

            }

            // 容错恢复
            if (level == RecoverLevel.Robust)
            {
                string strRecoverComment = "";

                string strReaderBarcode = DomUtil.GetElementText(domLog.DocumentElement,
                    "readerBarcode");
                if (String.IsNullOrEmpty(strReaderBarcode) == true)
                {
                    strError = "<readerBarcode>元素值为空";
                    return -1;
                }

                // 读入读者记录
                string strReaderXml = "";
                string strOutputReaderRecPath = "";
                byte[] reader_timestamp = null;

                nRet = this.GetReaderRecXml(
                    // Channels,
                    channel,
                    strReaderBarcode,
                    out strReaderXml,
                    out strOutputReaderRecPath,
                    out reader_timestamp,
                    out strError);
                if (nRet == 0)
                {
                    strError = "读者证条码号 '" + strReaderBarcode + "' 不存在";
                    // TODO: 记入信息文件

                    // 从日志记录中获得读者记录
                    XmlNode node = null;
                    strReaderXml = DomUtil.GetElementText(domLog.DocumentElement,
                        "readerRecord",
                        out node);
                    if (node == null)
                    {
                        strError = "日志记录中缺<readerRecord>元素";
                        return -1;
                    }
                    string strReaderRecPath = DomUtil.GetAttr(node, "recPath");
                    if (String.IsNullOrEmpty(strReaderRecPath) == true)
                    {
                        strError = "日志记录中<readerRecord>元素缺recPath属性";
                        return -1;
                    }

                    // 新增一条读者记录
                    strOutputReaderRecPath = ResPath.GetDbName(strReaderRecPath) + "/?";
                    reader_timestamp = null;
                }
                else
                {
                    if (nRet == -1)
                    {
                        strError = "读入证条码号为 '" + strReaderBarcode + "' 的读者记录时发生错误: " + strError;
                        return -1;
                    }
                }

                string strLibraryCode = "";
                // 获得读者库的馆代码
                // return:
                //      -1  出错
                //      0   成功
                nRet = GetLibraryCode(
                        strOutputReaderRecPath,
                        out strLibraryCode,
                        out strError);
                if (nRet == -1)
                    goto ERROR1;

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

                // 读入册记录
                string strConfirmItemRecPath = DomUtil.GetElementText(domLog.DocumentElement,
                    "confirmItemRecPath");
                string strItemBarcode = DomUtil.GetElementText(domLog.DocumentElement,
                    "itemBarcode");
                if (String.IsNullOrEmpty(strItemBarcode) == true)
                {
                    strError = "<strItemBarcode>元素值为空";
                    return -1;
                }

                string strItemXml = "";
                string strOutputItemRecPath = "";
                byte[] item_timestamp = null;


                // 从册条码号获得册记录
                List<string> aPath = null;

                // 获得册记录
                // return:
                //      -1  error
                //      0   not found
                //      1   命中1条
                //      >1  命中多于1条
                nRet = this.GetItemRecXml(
                    // Channels,
                    channel,
                    strItemBarcode,
                    out strItemXml,
                    100,
                    out aPath,
                    out item_timestamp,
                    out strError);
                if (nRet == 0)
                {
                    strError = "册条码号 '" + strItemBarcode + "' 不存在";
                    // TODO: 记入信息文件

                    XmlNode node = null;
                    strItemXml = DomUtil.GetElementText(domLog.DocumentElement,
                        "itemRecord",
                        out node);
                    if (node == null)
                    {
                        strError = "日志记录中缺<itemRecord>元素";
                        return -1;
                    }
                    string strItemRecPath = DomUtil.GetAttr(node, "recPath");
                    if (String.IsNullOrEmpty(strItemRecPath) == true)
                    {
                        strError = "日志记录中<itemRecord>元素缺recPath属性";
                        return -1;
                    }

                    // 新增一条册记录
                    strOutputItemRecPath = ResPath.GetDbName(strItemRecPath) + "/?";
                    item_timestamp = null;
                }
                else
                {

                    if (nRet == -1)
                    {
                        strError = "读入册条码号为 '" + strItemBarcode + "' 的册记录时发生错误: " + strError;
                        return -1;
                    }

                    Debug.Assert(aPath != null, "");

                    bool bNeedReload = false;

                    if (aPath.Count > 1)
                    {

                        // 建议根据strConfirmItemRecPath来进行挑选
                        if (String.IsNullOrEmpty(strConfirmItemRecPath) == true)
                        {
                            // 容错!
                            strOutputItemRecPath = aPath[0];

                            strRecoverComment += "册条码号 " + strItemBarcode + " 有 "
                                + aPath.Count.ToString() + " 条重复记录,因受容错要求所迫,权且采用其中第一个记录 "
                                + strOutputItemRecPath + " 来进行借阅操作。";

                            // 是否需要重新装载?
                            bNeedReload = false;    // 所取得的第一个路径,其记录已经装载
                        }
                        else
                        {

                            ///// 
                            nRet = aPath.IndexOf(strConfirmItemRecPath);
                            if (nRet != -1)
                            {
                                strOutputItemRecPath = aPath[nRet];
                                strRecoverComment += "册条码号 " + strItemBarcode + " 有 "
                                    + aPath.Count.ToString() + " 条重复记录,经过找到strConfirmItemRecPath=[" + strConfirmItemRecPath + "]"
                                    + "来进行借阅操作。";

                                // 是否需要重新装载?
                                if (nRet != 0)
                                    bNeedReload = true; // 第一个以外的路径才需要装载

                            }
                            else
                            {
                                // 容错
                                strOutputItemRecPath = aPath[0];

                                strRecoverComment += "册条码号 " + strItemBarcode + " 有 "
                                    + aPath.Count.ToString() + " 条重复记录,在其中无法找到strConfirmItemRecPath=[" + strConfirmItemRecPath + "]的记录"
                                    + "因受容错要求所迫,权且采用其中第一个记录 "
                                    + strOutputItemRecPath + " 来进行借阅操作。";

                                // 是否需要重新装载?
                                bNeedReload = false;    // 所取得的第一个路径,其记录已经装载

                                /* 
                                                                    strError = "册条码号 " + strItemBarcode + " 有 "
                                                                        + aPath.Count.ToString() + " 条重复记录,在其中无法找到strConfirmItemRecPath=[" + strConfirmItemRecPath + "]的记录";
                                                                    return -1;
                                 * */

                            }
                        }


                    } // if (aPath.Count > 1)
                    else
                    {

                        Debug.Assert(nRet == 1, "");
                        Debug.Assert(aPath.Count == 1, "");

                        if (nRet == 1)
                        {
                            strOutputItemRecPath = aPath[0];

                            // 是否需要重新装载?
                            bNeedReload = false;    // 所取得的第一个路径,其记录已经装载
                        }
                    }


                    // 重新装载
                    if (bNeedReload == true)
                    {
                        string strMetaData = "";
                        lRet = channel.GetRes(strOutputItemRecPath,
                            out strItemXml,
                            out strMetaData,
                            out item_timestamp,
                            out strOutputItemRecPath,
                            out strError);
                        if (lRet == -1)
                        {
                            strError = "根据strOutputItemRecPath '" + strOutputItemRecPath + "' 重新获得册记录失败: " + strError;
                            return -1;
                        }

                        // 需要检查记录中的<barcode>元素值是否匹配册条码号

                    }
                }

                ////

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

                // 修改读者记录
                // 修改册记录

                nRet = BorrowChangeReaderAndItemRecord(
                    // Channels,
                    channel,
                    strItemBarcode,
                    strReaderBarcode,
                    domLog,
                    strRecoverComment,
                    strLibraryCode,
                    ref readerdom,
                    ref itemdom,
                    out strError);
                if (nRet == -1)
                    goto ERROR1;

                // 写回读者、册记录
                byte[] output_timestamp = null;
                string strOutputPath = "";


                // 写回读者记录
                lRet = channel.DoSaveTextRes(strOutputReaderRecPath,
                    readerdom.OuterXml,
                    false,
                    "content,ignorechecktimestamp",
                    reader_timestamp,
                    out output_timestamp,
                    out strOutputPath,
                    out strError);
                if (lRet == -1)
                    goto ERROR1;

                // 写回册记录
                lRet = channel.DoSaveTextRes(strOutputItemRecPath,
                    itemdom.OuterXml,
                    false,
                    "content,ignorechecktimestamp",
                    item_timestamp,
                    out output_timestamp,
                    out strOutputPath,
                    out strError);
                if (lRet == -1)
                    goto ERROR1;

            }


            return 0;
            ERROR1:
            if (level == RecoverLevel.LogicAndSnapshot)
            {
                level = RecoverLevel.Snapshot;
                goto DO_SNAPSHOT;
            }
            return -1;
        }
Beispiel #10
0
        // Amerce() API 恢复动作
        /*
<root>
  <operation>amerce</operation> 操作类型
  <action>amerce</action> 具体动作。有amerce undo modifyprice
  <readerBarcode>...</readerBarcode> 读者证条码号
  <!-- <idList>...<idList> ID列表,逗号间隔 已废止 -->
  <amerceItems>
	<amerceItem id="..." newPrice="..." newComment="..." /> newComment中内容追加或替换原来的注释内容。到底是追加还是覆盖,取决于第一个字符是否为'>'还是'<',前者为追加(这时第一个字符不被当作内容)。如果第一个字符不是这两者之一,则默认为追加
	...
  </amerceItems>
  <amerceRecord recPath='...'><root><itemBarcode>0000001</itemBarcode><readerBarcode>R0000002</readerBarcode><state>amerced</state><id>632958375041543888-1</id><over>31day</over><borrowDate>Sat, 07 Oct 2006 09:04:28 GMT</borrowDate><borrowPeriod>30day</borrowPeriod><returnDate>Thu, 07 Dec 2006 09:04:27 GMT</returnDate><returnOperator>test</returnOperator></root></amerceRecord> 在罚款库中创建的新记录。注意<amerceRecord>元素可以重复。<amerceRecord>元素内容里面的<itemBarcode><readerBarcode><id>等具备了足够的信息。
  <operator>test</operator> 操作者
  <operTime>Fri, 08 Dec 2006 10:09:36 GMT</operTime> 操作时间
  
  <readerRecord recPath='...'>...</readerRecord>	最新读者记录
</root>

<root>
  <operation>amerce</operation> 
  <action>undo</action> 
  <readerBarcode>...</readerBarcode> 读者证条码号
  <!-- <idList>...<idList> ID列表,逗号间隔 已废止 -->
  <amerceItems>
	<amerceItem id="..." newPrice="..."/>
	...
  </amerceItems>
  <amerceRecord recPath='...'><root><itemBarcode>0000001</itemBarcode><readerBarcode>R0000002</readerBarcode><state>amerced</state><id>632958375041543888-1</id><over>31day</over><borrowDate>Sat, 07 Oct 2006 09:04:28 GMT</borrowDate><borrowPeriod>30day</borrowPeriod><returnDate>Thu, 07 Dec 2006 09:04:27 GMT</returnDate><returnOperator>test</returnOperator></root></amerceRecord> Undo所去掉的罚款库记录
  <operator>test</operator> 
  <operTime>Fri, 08 Dec 2006 10:12:20 GMT</operTime> 
  
  <readerRecord recPath='...'>...</readerRecord>	最新读者记录

</root>

<root>
  <operation>amerce</operation> 
  <action>modifyprice</action> 
  <readerBarcode>...</readerBarcode> 读者证条码号
  <amerceItems>
	<amerceItem id="..." newPrice="..." newComment="..."/> newComment中内容追加或替换原来的注释内容。到底是追加还是覆盖,取决于第一个字符是否为'>'还是'<',前者为追加(这时第一个字符不被当作内容)。如果第一个字符不是这两者之一,则默认为追加
	...
  </amerceItems>
  <!-- modifyprice操作时不产生<amerceRecord>元素 -->
  <operator>test</operator> 
  <operTime>Fri, 08 Dec 2006 10:12:20 GMT</operTime> 
  
  <oldReaderRecord recPath='...'>...</oldReaderRecord>	操作前旧的读者记录。<oldReaderRecord>元素是modifyprice操作时特有的元素
  <readerRecord recPath='...'>...</readerRecord>	最新读者记录

</root>

2007/12/18
<root>
  <operation>amerce</operation> 操作类型
  <action>expire</action> 以停代金到期
  <readerBarcode>...</readerBarcode> 读者证条码号
  <expiredOverdues> 已经到期的若干<overdue>元素
	<overdue ... />
	...
  </expiredOverdues>
  <operator>test</operator> 操作者 如果为#readersMonitor,表示为后台线程
  <operTime>Fri, 08 Dec 2006 10:09:36 GMT</operTime> 操作时间
  
  <readerRecord recPath='...'>...</readerRecord>	最新读者记录
</root>
         * 
2008/6/20
<root>
  <operation>amerce</operation> 
  <action>modifycomment</action> 
  <readerBarcode>...</readerBarcode> 读者证条码号
  <amerceItems>
	<amerceItem id="..." newComment="..."/> newComment中内容追加或替换原来的注释内容。到底是追加还是覆盖,取决于第一个字符是否为'>'还是'<',前者为追加(这时第一个字符不被当作内容)。如果第一个字符不是这两者之一,则默认为追加
	...
  </amerceItems>
  <!-- modifycomment操作时不产生<amerceRecord>元素 -->
  <operator>test</operator> 
  <operTime>Fri, 08 Dec 2006 10:12:20 GMT</operTime> 
  
  <oldReaderRecord recPath='...'>...</oldReaderRecord>	操作前旧的读者记录。<oldReaderRecord>元素是modifycomment操作时特有的元素
  <readerRecord recPath='...'>...</readerRecord>	最新读者记录
</root>

         * * 
         * */
        public int RecoverAmerce(
            RmsChannelCollection Channels,
            RecoverLevel level,
            XmlDocument domLog,
            out string strError)
        {
            strError = "";

            // 暂时把Robust当作Logic处理
            if (level == RecoverLevel.Robust)
                level = RecoverLevel.Logic;


            long lRet = 0;
            int nRet = 0;

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

            DO_SNAPSHOT:

            string strAction = DomUtil.GetElementText(domLog.DocumentElement,
                "action");

            // 快照恢复
            if (level == RecoverLevel.Snapshot)
            {

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

                if (strAction == "amerce")
                {
                    XmlNodeList nodes = domLog.DocumentElement.SelectNodes("amerceRecord");

                    int nErrorCount = 0;
                    for (int i = 0; i < nodes.Count; i++)
                    {
                        XmlNode node = nodes[i];
                        string strRecord = node.InnerText;
                        string strRecPath = DomUtil.GetAttr(node, "recPath");


                        // 写违约金记录
                        string strError0 = "";
                        lRet = channel.DoSaveTextRes(strRecPath,
            strRecord,
            false,
            "content,ignorechecktimestamp",
            timestamp,
            out output_timestamp,
            out strOutputPath,
            out strError0);
                        if (lRet == -1)
                        {
                            // 继续循环
                            if (strError != "")
                                strError += "\r\n";
                            strError += "写入违约金记录 '" + strRecPath + "' 时发生错误: " + strError0;
                            nErrorCount++;
                        }
                    }

                    if (nErrorCount > 0)
                        return -1;
                }
                else if (strAction == "undo")
                {
                    XmlNodeList nodes = domLog.DocumentElement.SelectNodes("amerceRecord");

                    int nErrorCount = 0;
                    for (int i = 0; i < nodes.Count; i++)
                    {
                        XmlNode node = nodes[i];
                        string strRecPath = DomUtil.GetAttr(node, "recPath");

                        int nRedoCount = 0;
                        string strError0 = "";
                    REDO:
                        // 删除违约金记录
                        lRet = channel.DoDeleteRes(strRecPath,
                            timestamp,
                            out output_timestamp,
                            out strError0);
                        if (lRet == -1)
                        {
                            if (channel.ErrorCode == ChannelErrorCode.NotFound)
                                continue;   // 记录本来就不存在
                            if (channel.ErrorCode == ChannelErrorCode.TimestampMismatch)
                            {
                                if (nRedoCount < 10)
                                {
                                    timestamp = output_timestamp;
                                    nRedoCount++;
                                    goto REDO;
                                }
                            }

                            // 继续循环
                            if (strError != "")
                                strError += "\r\n";
                            strError += "删除违约金记录 '" + strRecPath + "' 时发生错误: " + strError0;
                            nErrorCount++;
                        }
                    } // end of for

                    if (nErrorCount > 0)
                        return -1;
                }
                else if (strAction == "modifyprice")
                {
                    // 这里什么都不作,只等后面用快照的读者记录来恢复
                }
                else if (strAction == "expire")
                {
                    // 这里什么都不作,只等后面用快照的读者记录来恢复

                }
                else if (strAction == "modifycomment")
                {
                    // 这里什么都不作,只等后面用快照的读者记录来恢复
                }
                else if (strAction == "appendcomment")
                {
                    // 这里什么都不作,只等后面用快照的读者记录来恢复
                }
                else
                {
                    strError = "未知的<action>类型: " + strAction;
                    return -1;
                }

                {
                    XmlNode node = null;
                    // 写入读者记录
                    string strReaderRecord = DomUtil.GetElementText(domLog.DocumentElement,
                        "readerRecord",
                        out node);
                    string strReaderRecPath = DomUtil.GetAttr(node, "recPath");

                    // 写读者记录
                    lRet = channel.DoSaveTextRes(strReaderRecPath,
        strReaderRecord,
        false,
        "content,ignorechecktimestamp",
        timestamp,
        out output_timestamp,
        out strOutputPath,
        out strError);
                    if (lRet == -1)
                    {
                        strError = "写入读者记录 '" + strReaderRecPath + "' 时发生错误: " + strError;
                        return -1;
                    }
                }

                return 0;
            }

            // 逻辑恢复或者混合恢复
            if (level == RecoverLevel.Logic
                || level == RecoverLevel.LogicAndSnapshot)
            {
                string strReaderBarcode = DomUtil.GetElementText(domLog.DocumentElement,
                    "readerBarcode");
                if (String.IsNullOrEmpty(strReaderBarcode) == true)
                {
                    strError = "日志记录中缺乏<readerBarcode>元素";
                    return -1;
                }
                string strLibraryCode = DomUtil.GetElementText(domLog.DocumentElement,
                    "libraryCode");

                string strOperator = DomUtil.GetElementText(domLog.DocumentElement,
                    "operator");
                string strOperTime = DomUtil.GetElementText(domLog.DocumentElement,
                    "operTime");

                /*
                string strAmerceItemIdList = DomUtil.GetElementText(domLog.DocumentElement,
                    "idList");
                if (String.IsNullOrEmpty(strAmerceItemIdList) == true)
                {
                    strError = "日志记录中缺乏<idList>元素";
                    return -1;
                }
                 * */

                AmerceItem[] amerce_items = ReadAmerceItemList(domLog);


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

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

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

                if (strAction == "amerce")
                {
                    List<string> NotFoundIds = null;
                    List<string> Ids = null;
                    List<string> AmerceRecordXmls = null;
                    // 交违约金:在读者记录中去除所选的<overdue>元素,并且构造一批新记录准备加入违约金库
                    // return:
                    //      -1  error
                    //      0   读者dom没有变化
                    //      1   读者dom发生了变化
                    nRet = DoAmerceReaderXml(
                        strLibraryCode,
                        ref readerdom,
                        amerce_items,
                        strOperator,
                        strOperTime,
                        out AmerceRecordXmls,
                        out NotFoundIds,
                        out Ids,
                        out strError);
                    if (nRet == -1)
                    {
                        // 在错误信息后面增补每个id对应的amerce record
                        if (NotFoundIds != null && NotFoundIds.Count > 0)
                        {
                            strError += "。读者证条码号为 " + strReaderBarcode + ",日志记录中相关的AmerceRecord如下:\r\n" + GetAmerceRecordStringByID(domLog, NotFoundIds);
                        }

                        goto ERROR1;
                    }

                    // 如果有精力,可以把AmerceRecordXmls和日志记录中的<amerceRecord>逐个进行核对


                    // 写入违约金记录
                    XmlNodeList nodes = domLog.DocumentElement.SelectNodes("amerceRecord");

                    for (int i = 0; i < nodes.Count; i++)
                    {
                        XmlNode node = nodes[i];
                        string strRecord = node.InnerText;
                        string strRecPath = DomUtil.GetAttr(node, "recPath");


                        // 写违约金记录
                        lRet = channel.DoSaveTextRes(strRecPath,
                            strRecord,
                            false,
                            "content,ignorechecktimestamp",
                            null,
                            out output_timestamp,
                            out strOutputPath,
                            out strError);
                        if (lRet == -1)
                        {
                            strError = "写入违约金记录 '" + strRecPath + "' 时发生错误: " + strError;
                            goto ERROR1;
                        }
                    }
                }

                if (strAction == "undo")
                {
                    XmlNodeList nodes = domLog.DocumentElement.SelectNodes("amerceRecord");

                    // 看看根下面是否有overdues元素
                    XmlNode root = readerdom.DocumentElement.SelectSingleNode("overdues");
                    if (root == null)
                    {
                        root = readerdom.CreateElement("overdues");
                        readerdom.DocumentElement.AppendChild(root);
                    }

                    for (int i = 0; i < nodes.Count; i++)
                    {
                        XmlNode node = nodes[i];
                        string strRecord = node.InnerText;
                        string strRecPath = DomUtil.GetAttr(node, "recPath");


                        // 如果有精力,可以把违约金记录中的id和日志记录<amerceItems>中的id对比检查

                        // 违约金信息加回读者记录
                        string strTempReaderBarcode = "";
                        string strOverdueString = "";

                        // 将违约金记录格式转换为读者记录中的<overdue>元素格式
                        nRet = ConvertAmerceRecordToOverdueString(strRecord,
                            out strTempReaderBarcode,
                            out strOverdueString,
                            out strError);
                        if (nRet == -1)
                            goto ERROR1;

                        if (strTempReaderBarcode != strReaderBarcode)
                        {
                            strError = "<amerceRecord>中的读者证条码号和日志记录中的<readerBarcode>读者证条码号不一致";
                            goto ERROR1;
                        }

                        XmlDocumentFragment fragment = readerdom.CreateDocumentFragment();
                        fragment.InnerXml = strOverdueString;

                        // 2008/11/13 changed
                        XmlNode node_added = root.AppendChild(fragment);
                        Debug.Assert(node_added != null, "");
                        string strReason = DomUtil.GetAttr(node_added, "reason");
                        if (strReason == "押金。")
                        {
                            string strPrice = DomUtil.GetAttr(node_added, "price");

                            if (String.IsNullOrEmpty(strPrice) == false)
                            {
                                // 需要从<foregift>元素中减去这个价格
                                string strContent = DomUtil.GetElementText(readerdom.DocumentElement,
                                    "foregift");


                                string strNegativePrice = "";
                                // 将形如"-123.4+10.55-20.3"的价格字符串反转正负号
                                // parameters:
                                //      bSum    是否要顺便汇总? true表示要汇总
                                nRet = PriceUtil.NegativePrices(strPrice,
                                    false,
                                    out strNegativePrice,
                                    out strError);
                                if (nRet == -1)
                                {
                                    strError = "反转价格字符串 '" + strPrice + "时发生错误: " + strError;
                                    goto ERROR1;
                                }

                                strContent = PriceUtil.JoinPriceString(strContent, strNegativePrice);

                                DomUtil.SetElementText(readerdom.DocumentElement,
                                    "foregift",
                                    strContent);
                                // bReaderDomChanged = true;
                            }
                        }


                        // 删除违约金记录
                        int nRedoCount = 0;
                        byte[] timestamp = null;
                    REDO:
                        // 删除违约金记录
                        lRet = channel.DoDeleteRes(strRecPath,
                            timestamp,
                            out output_timestamp,
                            out strError);
                        if (lRet == -1)
                        {
                            if (channel.ErrorCode == ChannelErrorCode.NotFound)
                                continue;   // 记录本来就不存在
                            if (channel.ErrorCode == ChannelErrorCode.TimestampMismatch)
                            {
                                if (nRedoCount < 10)
                                {
                                    timestamp = output_timestamp;
                                    nRedoCount++;
                                    goto REDO;
                                }
                            }

                            // 是否需要继续循环?
                            strError = "删除违约金记录 '" + strRecPath + "' 时发生错误: " + strError;
                            goto ERROR1;
                        }
                    }

                }

                if (strAction == "modifyprice")
                {
                    nRet = ModifyPrice(ref readerdom,
                        amerce_items,
                        out strError);
                    if (nRet == -1)
                    {
                        strError = "ModifyPrice()时发生错误: " + strError;
                        goto ERROR1;
                    }
                }

                // 2008/6/20
                if (strAction == "modifycomment")
                {
                    nRet = ModifyComment(
                        ref readerdom,
                        amerce_items,
                        out strError);
                    if (nRet == -1)
                    {
                        strError = "ModifyComment()时发生错误: " + strError;
                        goto ERROR1;
                    }
                }

                if (strAction == "expire")
                {
                    // 寻找<expiredOverdues/overdue>元素
                    XmlNodeList nodes = domLog.DocumentElement.SelectNodes("//expiredOverdues/overdue");
                    for (int i = 0; i < nodes.Count; i++)
                    {
                        XmlNode node = nodes[i];
                        string strID = DomUtil.GetAttr(node, "id");

                        if (String.IsNullOrEmpty(strID) == true)
                            continue;

                        // 从读者记录中去掉这个id的<overdue>元素
                        XmlNode nodeOverdue = readerdom.DocumentElement.SelectSingleNode("overdues/overdue[@id='"+strID+"']");
                        if (nodeOverdue != null)
                        {
                            if (nodeOverdue.ParentNode != null)
                                nodeOverdue.ParentNode.RemoveChild(nodeOverdue);
                        }
                    }
                }

                // 写回读者记录
                strReaderXml = readerdom.OuterXml;

                lRet = channel.DoSaveTextRes(strOutputReaderRecPath,
                    strReaderXml,
                    false,
                    "content,ignorechecktimestamp",
                    reader_timestamp,
                    out output_timestamp,
                    out strOutputPath,
                    out strError);
                if (lRet == -1)
                    goto ERROR1;
 
            }

            return 0;

        ERROR1:
            if (level == RecoverLevel.LogicAndSnapshot)
            {
                level = RecoverLevel.Snapshot;
                goto DO_SNAPSHOT;
            }
            return -1;
        }
Beispiel #11
0
        // SetReaderInfo() API 恢复动作
        /*
<root>
	<operation>setReaderInfo</operation> 操作类型
	<action>...</action> 具体动作。有new change delete move 4种
	<record recPath='...'>...</record> 新记录
    <oldRecord recPath='...'>...</oldRecord> 被覆盖或者删除的记录 动作为 change 和 delete 时具备此元素
    <changedEntityRecord itemBarcode='...' recPath='...' oldBorrower='...' newBorrower='...' /> 若干个元素。表示连带发生修改的册记录
	<operator>test</operator> 操作者
	<operTime>Fri, 08 Dec 2006 09:01:38 GMT</operTime> 操作时间
</root>

注: new 的时候只有<record>元素,delete的时候只有<oldRecord>元素,change的时候两者都有

         * */
        public int RecoverSetReaderInfo(
            RmsChannelCollection Channels,
            RecoverLevel level_param,
            XmlDocument domLog,
            out string strError)
        {
            strError = "";

            string[] element_names = reader_element_names;

            RecoverLevel level = level_param;

            // 暂时把Robust当作Logic处理
            if (level == RecoverLevel.Robust)
                level = RecoverLevel.Logic;

            long lRet = 0;
            int nRet = 0;

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

            bool bReuse = false;    // 是否能够不顾RecorverLevel状态而重用部分代码

            DO_SNAPSHOT:

            string strAction = DomUtil.GetElementText(domLog.DocumentElement,
                "action");

            // 快照恢复
            if (level == RecoverLevel.Snapshot
                || bReuse == true)
            {
                byte[] timestamp = null;
                byte[] output_timestamp = null;
                string strOutputPath = "";

                if (strAction == "new"
                    || strAction == "change")
                {
                    XmlNode node = null;
                    string strRecord = DomUtil.GetElementText(domLog.DocumentElement,
                        "record",
                        out node);
                    if (node == null)
                    {
                        strError = "日志记录中缺<record>元素";
                        return -1;
                    }
                    string strRecPath = DomUtil.GetAttr(node, "recPath");

                    // 写读者记录
                    lRet = channel.DoSaveTextRes(strRecPath,
        strRecord,
        false,
        "content,ignorechecktimestamp",
        timestamp,
        out output_timestamp,
        out strOutputPath,
        out strError);
                    if (lRet == -1)
                    {
                        strError = "写入读者记录 '" + strRecPath + "' 时发生错误: " + strError;
                        return -1;
                    }

                    // 2015/9/11
                    XmlNodeList nodes = domLog.DocumentElement.SelectNodes("changedEntityRecord");
                    foreach(XmlElement item in nodes)
                    {
                        string strItemBarcode = item.GetAttribute("itemBarcode");
                        string strItemRecPath = item.GetAttribute("recPath");
                        string strOldReaderBarcode = item.GetAttribute("oldBorrower");
                        string strNewReaderBarcode = item.GetAttribute("newBorrower");

                        // 修改一条册记录,的 borrower 元素内容
                        // parameters:
                        //      -2  保存记录时出错
                        //      -1  一般性错误
                        //      0   成功
                        nRet = ChangeBorrower(
                            channel,
                            strItemBarcode,
                            strItemRecPath,
                            strOldReaderBarcode,
                            strNewReaderBarcode,
                            true,
                            out strError);
                        if (nRet == -1 || nRet == -2)
                        {
                            strError = "修改读者记录所关联的在借册记录时出错:" + strError;
                            return -1;
                        }
                    }
                }
                else if (strAction == "delete")
                {
                    XmlNode node = null;
                    string strOldRecord = DomUtil.GetElementText(domLog.DocumentElement,
                        "oldRecord",
                        out node);
                    if (node == null)
                    {
                        strError = "日志记录中缺<oldRecord>元素";
                        return -1;
                    }
                    string strRecPath = DomUtil.GetAttr(node, "recPath");

                    int nRedoCount = 0;
                REDO:
                    // 删除读者记录
                    lRet = channel.DoDeleteRes(strRecPath,
                        timestamp,
                        out output_timestamp,
                        out strError);
                    if (lRet == -1)
                    {
                        if (channel.ErrorCode == ChannelErrorCode.NotFound)
                            return 0;   // 记录本来就不存在
                        if (channel.ErrorCode == ChannelErrorCode.TimestampMismatch)
                        {
                            if (nRedoCount < 10)
                            {
                                timestamp = output_timestamp;
                                nRedoCount++;
                                goto REDO;
                            }
                        }
                        strError = "删除读者记录 '" + strRecPath + "' 时发生错误: " + strError;
                        return -1;

                    }
                }
                else if (strAction == "move")
                {
                    XmlNode node = null;
                    string strRecord = DomUtil.GetElementText(domLog.DocumentElement,
    "record",
    out node);
                    if (node == null)
                    {
                        strError = "日志记录中缺<record>元素";
                        return -1;
                    }
                    string strRecPath = DomUtil.GetAttr(node, "recPath");
                    if (string.IsNullOrEmpty(strRecPath) == true)
                    {
                        strError = "日志记录中<record>元素内缺recPath属性值";
                        return -1;
                    }

                    string strOldRecord = DomUtil.GetElementText(domLog.DocumentElement,
                        "oldRecord",
                        out node);
                    if (node == null)
                    {
                        strError = "日志记录中缺<oldRecord>元素";
                        return -1;
                    }
                    string strOldRecPath = DomUtil.GetAttr(node, "recPath");
                    if (string.IsNullOrEmpty(strOldRecPath) == true)
                    {
                        strError = "日志记录中<oldRecord>元素内缺recPath属性值";
                        return -1;
                    }
                    /*
                    int nRedoCount = 0;
                REDO:
                     * */

                    // 移动读者记录
                    lRet = channel.DoCopyRecord(
                        strOldRecPath,
                        strRecPath,
                        true,
                        out output_timestamp,
                        out strOutputPath,
                        out strError);
                    if (lRet == -1)
                    {
                        // 源记录本来就不存在。进行容错处理
                        if (channel.ErrorCode == ChannelErrorCode.NotFound
                            && level_param == RecoverLevel.Robust)
                        {
                            // 优先用最新的记录内容复原。实在没有才用旧的记录内容
                            if (string.IsNullOrEmpty(strRecord) == true)
                                strRecord = strOldRecord;

                            if (string.IsNullOrEmpty(strRecord) == false)
                            {
                                // 写读者记录
                                lRet = channel.DoSaveTextRes(strRecPath,
                    strRecord,
                    false,
                    "content,ignorechecktimestamp",
                    timestamp,
                    out output_timestamp,
                    out strOutputPath,
                    out strError);
                                if (lRet == -1)
                                {
                                    strError = "为容错,写入读者记录 '" + strRecPath + "' 时发生错误: " + strError;
                                    return -1;
                                }

                                return 0;
                            }
                        }

                        strError = "移动读者记录 '" + strOldRecPath + "' 到 '" + strRecPath + "' 时发生错误: " + strError;
                        return -1;
                    }

                    // <record>中如果有记录体,则还需要写入一次
                    // 所以这里需要注意,在创建日志记录的时候,如果没有在CopyRecord()后追加修改过记录,则不要创建<record>记录正文部分,以免引起多余的日志恢复时写入动作
                    if (string.IsNullOrEmpty(strRecord) == false)
                    {
                        lRet = channel.DoSaveTextRes(strRecPath,
            strRecord,
            false,
            "content,ignorechecktimestamp",
            timestamp,
            out output_timestamp,
            out strOutputPath,
            out strError);
                        if (lRet == -1)
                        {
                            strError = "写入读者记录 '" + strRecPath + "' 时发生错误: " + strError;
                            return -1;
                        }
                    }
                }

                return 0;
            }

            // 逻辑恢复或者混合恢复
            if (level == RecoverLevel.Logic
                || level == RecoverLevel.LogicAndSnapshot)
            {
                // 和数据库中已有记录合并,然后保存
                if (strAction == "new" || strAction == "change")
                {
                    XmlNode node = null;
                    string strRecord = DomUtil.GetElementText(domLog.DocumentElement,
                        "record",
                        out node);
                    if (node == null)
                    {
                        strError = "日志记录中缺<record>元素";
                        return -1;
                    }

                    string strRecPath = DomUtil.GetAttr(node, "recPath");

                    // 读出数据库中原有的记录
                    string strExistXml = "";
                    string strMetaData = "";
                    byte[] exist_timestamp = null;
                    string strOutputPath = "";

                    if (strAction == "change")
                    {
                        lRet = channel.GetRes(strRecPath,
                            out strExistXml,
                            out strMetaData,
                            out exist_timestamp,
                            out strOutputPath,
                            out strError);
                        if (lRet == -1)
                        {
                            // 容错
                            if (channel.ErrorCode == ChannelErrorCode.NotFound
                                && level == RecoverLevel.LogicAndSnapshot)
                            {
                                // 如果记录不存在, 则构造一条空的记录
                                // bExist = false;
                                strExistXml = "<root />";
                                exist_timestamp = null;
                            }
                            else
                            {
                                strError = "在读入原有记录 '" + strRecPath + "' 时失败: " + strError;
                                goto ERROR1;
                            }
                        }
                    }

                    //
                    // 把两个记录装入DOM

                    XmlDocument domExist = new XmlDocument();
                    XmlDocument domNew = new XmlDocument();

                    try
                    {
                        // 防范空记录
                        if (String.IsNullOrEmpty(strExistXml) == true)
                            strExistXml = "<root />";

                        domExist.LoadXml(strExistXml);
                    }
                    catch (Exception ex)
                    {
                        strError = "strExistXml装载进入DOM时发生错误: " + ex.Message;
                        goto ERROR1;
                    }

                    try
                    {
                        domNew.LoadXml(strRecord);
                    }
                    catch (Exception ex)
                    {
                        strError = "strRecord装载进入DOM时发生错误: " + ex.Message;
                        goto ERROR1;
                    }

                    // 合并新旧记录
                    string strNewXml = "";
                    nRet = MergeTwoReaderXml(
                        element_names,
                        "change",
                        domExist,
                        domNew,
                        out strNewXml,
                        out strError);
                    if (nRet == -1)
                        goto ERROR1;

                    // 保存新记录
                    byte[] output_timestamp = null;
                    lRet = channel.DoSaveTextRes(strRecPath,
                        strNewXml,
                        false,   // include preamble?
                        "content,ignorechecktimestamp",
                        exist_timestamp,
                        out output_timestamp,
                        out strOutputPath,
                        out strError);
                    if (lRet == -1)
                    {
                        goto ERROR1;
                    }
                }
                else if (strAction == "delete")
                {
                    // 和SnapShot方式相同
                    bReuse = true;
                    goto DO_SNAPSHOT;
                }
                else if (strAction == "move")
                {
                    // 和SnapShot方式相同
                    bReuse = true;
                    goto DO_SNAPSHOT;
                }
                else
                {
                    strError = "无法识别的<action>内容 '" + strAction + "'";
                    return -1;
                }
            }

            return 0;
        ERROR1:
            if (level == RecoverLevel.LogicAndSnapshot)
            {
                level = RecoverLevel.Snapshot;
                goto DO_SNAPSHOT;
            }
            return -1;
        }
Beispiel #12
0
        // ChangeReaderPassword() API 恢复动作
        /*
<root>
  <operation>changeReaderPassword</operation> 
  <readerBarcode>...</readerBarcode>	读者证条码号
  <newPassword>5npAUJ67/y3aOvdC0r+Dj7SeXGE=</newPassword> 
  <operator>test</operator> 
  <operTime>Fri, 08 Dec 2006 09:01:38 GMT</operTime> 
  <readerRecord recPath='...'>...</readerRecord>	最新读者记录
</root>
         * */
        public int RecoverChangeReaderPassword(
            RmsChannelCollection Channels,
            RecoverLevel level,
            XmlDocument domLog,
            out string strError)
        {
            strError = "";

            // 暂时把Robust当作Logic处理
            if (level == RecoverLevel.Robust)
                level = RecoverLevel.Logic;


            long lRet = 0;
            int nRet = 0;

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

            DO_SNAPSHOT:

            // 快照恢复
            if (level == RecoverLevel.Snapshot)
            {
                XmlNode node = null;
                string strReaderXml = DomUtil.GetElementText(domLog.DocumentElement,
                    "readerRecord",
                    out node);
                if (node == null)
                {
                    strError = "日志记录中缺<readerRecord>元素";
                    return -1;
                }
                string strReaderRecPath = DomUtil.GetAttr(node, "recPath");

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

                // 写读者记录
                lRet = channel.DoSaveTextRes(strReaderRecPath,
    strReaderXml,
    false,
    "content,ignorechecktimestamp",
    timestamp,
    out output_timestamp,
    out strOutputPath,
    out strError);
                if (lRet == -1)
                {
                    strError = "写入读者记录 '" + strReaderRecPath + "' 时发生错误: " + strError;
                    return -1;
                }

                return 0;
            }

            // 逻辑恢复或者混合恢复
            if (level == RecoverLevel.Logic
                || level == RecoverLevel.LogicAndSnapshot)
            {

                // 读出原有读者记录,修改密码后存回
                string strReaderBarcode = DomUtil.GetElementText(domLog.DocumentElement,
                    "readerBarcode");
                if (String.IsNullOrEmpty(strReaderBarcode) == true)
                {
                    strError = "日志记录中缺乏<readerBarcode>元素";
                    goto ERROR1;
                }

                string strNewPassword = DomUtil.GetElementText(domLog.DocumentElement,
                    "newPassword");
                if (String.IsNullOrEmpty(strNewPassword) == true)
                {
                    strError = "日志记录中缺乏<newPassword>元素";
                    goto ERROR1;
                }

                // 读入读者记录
                string strReaderXml = "";
                string strOutputReaderRecPath = "";
                byte[] reader_timestamp = null;

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

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

                // strNewPassword中本来就是SHA1形态
                DomUtil.SetElementText(readerdom.DocumentElement,
                    "password", strNewPassword);

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

                // 写回读者记录
                lRet = channel.DoSaveTextRes(strOutputReaderRecPath,
                    readerdom.OuterXml,
                    false,
                    "content,ignorechecktimestamp",
                    reader_timestamp,
                    out output_timestamp,
                    out strOutputPath,
                    out strError);
                if (lRet == -1)
                    goto ERROR1;
            }


            return 0;
        ERROR1:
            if (level == RecoverLevel.LogicAndSnapshot)
            {
                level = RecoverLevel.Snapshot;
                goto DO_SNAPSHOT;
            }
            return -1;
        }
Beispiel #13
0
        // SetComments() API 恢复动作
        /* 日志记录格式
<root>
  <operation>setComment</operation> 操作类型
  <action>new</action> 具体动作。有new change delete 3种
  <style>...</style> 风格。有force nocheckdup noeventlog 3种
  <record recPath='中文图书评注/3'>...</record> 记录体
  <oldRecord recPath='中文图书评注/3'>...</oldRecord> 被覆盖或者删除的记录 动作为change和delete时具备此元素
  <operator>test</operator> 操作者
  <operTime>Fri, 08 Dec 2006 08:41:46 GMT</operTime> 操作时间
</root>

注:1) 当<action>为delete时,没有<record>元素。为new时,没有<oldRecord>元素。
	2) 一次SetComments()API调用, 可能创建多条日志记录。
         
         * */
        // TODO: 要兑现style中force nocheckdup功能
        public int RecoverSetComment(
            RmsChannelCollection Channels,
            RecoverLevel level,
            XmlDocument domLog,
            out string strError)
        {
            strError = "";

            long lRet = 0;
            int nRet = 0;

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

            bool bReuse = false;    // 是否能够不顾RecorverLevel状态而重用部分代码

        DO_SNAPSHOT:

            string strAction = DomUtil.GetElementText(domLog.DocumentElement,
                "action");

            // 快照恢复
            if (level == RecoverLevel.Snapshot
                || bReuse == true)
            {

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

                if (strAction == "new"
                    || strAction == "change"
                    || strAction == "move")
                {
                    XmlNode node = null;
                    string strRecord = DomUtil.GetElementText(domLog.DocumentElement,
                        "record",
                        out node);
                    if (node == null)
                    {
                        strError = "日志记录中缺<record>元素";
                        return -1;
                    }

                    string strNewRecPath = DomUtil.GetAttr(node, "recPath");

                    // 
                    string strOldRecord = "";
                    string strOldRecPath = "";
                    if (strAction == "move")
                    {
                        strOldRecord = DomUtil.GetElementText(domLog.DocumentElement,
                            "oldRecord",
                            out node);
                        if (node == null)
                        {
                            strError = "日志记录中缺<oldRecord>元素";
                            return -1;
                        }

                        strOldRecPath = DomUtil.GetAttr(node, "recPath");
                    }

                    // 写评注记录
                    lRet = channel.DoSaveTextRes(strNewRecPath,
                        strRecord,
                        false,
                        "content,ignorechecktimestamp",
                        timestamp,
                        out output_timestamp,
                        out strOutputPath,
                        out strError);
                    if (lRet == -1)
                    {
                        strError = "写入评注记录 '" + strNewRecPath + "' 时发生错误: " + strError;
                        return -1;
                    }

                    if (strAction == "move")
                    {
                        // 删除评注记录
                        int nRedoCount = 0;

                    REDO_DELETE:
                        lRet = channel.DoDeleteRes(strOldRecPath,
                            timestamp,
                            out output_timestamp,
                            out strError);
                        if (lRet == -1)
                        {
                            if (channel.ErrorCode == ChannelErrorCode.NotFound)
                                return 0;   // 记录本来就不存在

                            if (channel.ErrorCode == ChannelErrorCode.TimestampMismatch)
                            {
                                if (nRedoCount < 10)
                                {
                                    timestamp = output_timestamp;
                                    nRedoCount++;
                                    goto REDO_DELETE;
                                }
                            }
                            strError = "删除评注记录 '" + strOldRecPath + "' 时发生错误: " + strError;
                            return -1;

                        }
                    }

                }
                else if (strAction == "delete")
                {
                    XmlNode node = null;
                    string strOldRecord = DomUtil.GetElementText(domLog.DocumentElement,
                        "oldRecord",
                        out node);
                    if (node == null)
                    {
                        strError = "日志记录中缺<oldRecord>元素";
                        return -1;
                    }
                    string strRecPath = DomUtil.GetAttr(node, "recPath");

                    int nRedoCount = 0;
                REDO:
                    // 删除评注记录
                    lRet = channel.DoDeleteRes(strRecPath,
                        timestamp,
                        out output_timestamp,
                        out strError);
                    if (lRet == -1)
                    {
                        if (channel.ErrorCode == ChannelErrorCode.NotFound)
                            return 0;   // 记录本来就不存在
                        if (channel.ErrorCode == ChannelErrorCode.TimestampMismatch)
                        {
                            if (nRedoCount < 10)
                            {
                                timestamp = output_timestamp;
                                nRedoCount++;
                                goto REDO;
                            }
                        }
                        strError = "删除评注记录 '" + strRecPath + "' 时发生错误: " + strError;
                        return -1;
                    }
                }
                else
                {
                    strError = "无法识别的<action>内容 '" + strAction + "'";
                    return -1;
                }


                return 0;
            }

            bool bForce = false;
            bool bNoCheckDup = false;

            string strStyle = DomUtil.GetElementText(domLog.DocumentElement,
                "style");

            if (StringUtil.IsInList("force", strStyle) == true)
                bForce = true;

            if (StringUtil.IsInList("nocheckdup", strStyle) == true)
                bNoCheckDup = true;

            // 逻辑恢复或者混合恢复或者容错恢复
            if (level == RecoverLevel.Logic
                || level == RecoverLevel.LogicAndSnapshot
                || level == RecoverLevel.Robust)    // 容错恢复没有单独实现
            {
                // 和数据库中已有记录合并,然后保存
                if (strAction == "new"
                    || strAction == "change"
                    || strAction == "move")
                {
                    XmlNode node = null;
                    string strRecord = DomUtil.GetElementText(domLog.DocumentElement,
                        "record",
                        out node);
                    if (node == null)
                    {
                        strError = "日志记录中缺<record>元素";
                        return -1;
                    }

                    string strNewRecPath = DomUtil.GetAttr(node, "recPath");

                    // 
                    string strOldRecord = "";
                    string strOldRecPath = "";
                    if (strAction == "move")
                    {
                        strOldRecord = DomUtil.GetElementText(domLog.DocumentElement,
                            "oldRecord",
                            out node);
                        if (node == null)
                        {
                            strError = "日志记录中缺<oldRecord>元素";
                            return -1;
                        }

                        strOldRecPath = DomUtil.GetAttr(node, "recPath");
                    }


                    // 读出数据库中原有的记录
                    string strExistXml = "";
                    string strMetaData = "";
                    byte[] exist_timestamp = null;
                    string strOutputPath = "";

                    if ((strAction == "change"
                        || strAction == "move")
                        && bForce == false)
                    {
                        string strSourceRecPath = "";

                        if (strAction == "change")
                            strSourceRecPath = strNewRecPath;
                        if (strAction == "move")
                            strSourceRecPath = strOldRecPath;

                        lRet = channel.GetRes(strSourceRecPath,
                            out strExistXml,
                            out strMetaData,
                            out exist_timestamp,
                            out strOutputPath,
                            out strError);
                        if (lRet == -1)
                        {
                            // 容错
                            if (channel.ErrorCode == ChannelErrorCode.NotFound
                                && level == RecoverLevel.LogicAndSnapshot)
                            {
                                // 如果记录不存在, 则构造一条空的记录
                                // bExist = false;
                                strExistXml = "<root />";
                                exist_timestamp = null;
                            }
                            else
                            {
                                strError = "在读入原有记录 '" + strNewRecPath + "' 时失败: " + strError;
                                goto ERROR1;
                            }
                        }
                    }

                    //
                    // 把两个记录装入DOM

                    XmlDocument domExist = new XmlDocument();
                    XmlDocument domNew = new XmlDocument();

                    try
                    {
                        // 防范空记录
                        if (String.IsNullOrEmpty(strExistXml) == true)
                            strExistXml = "<root />";

                        domExist.LoadXml(strExistXml);
                    }
                    catch (Exception ex)
                    {
                        strError = "strExistXml装载进入DOM时发生错误: " + ex.Message;
                        goto ERROR1;
                    }

                    try
                    {
                        domNew.LoadXml(strRecord);
                    }
                    catch (Exception ex)
                    {
                        strError = "strRecord装载进入DOM时发生错误: " + ex.Message;
                        goto ERROR1;
                    }

                    // 合并新旧记录
                    string strNewXml = "";

                    if (bForce == false)
                    {
                        nRet = this.CommentItemDatabase.MergeTwoItemXml(
                            null,
                            domExist,
                            domNew,
                            out strNewXml,
                            out strError);
                        if (nRet == -1)
                            goto ERROR1;
                    }
                    else
                    {
                        strNewXml = domNew.OuterXml;
                    }

                    // 保存新记录
                    byte[] output_timestamp = null;

                    if (strAction == "move")
                    {
                        // 复制源记录到目标位置,然后自动删除源记录
                        // 但是尚未在目标位置写入最新内容
                        lRet = channel.DoCopyRecord(strOldRecPath,
                            strNewRecPath,
                            true,   // bDeleteSourceRecord
                            out output_timestamp,
                            out strOutputPath,
                            out strError);
                        if (lRet == -1)
                            goto ERROR1;

                        exist_timestamp = output_timestamp; // 及时更新时间戳
                    }


                    lRet = channel.DoSaveTextRes(strNewRecPath,
                        strNewXml,
                        false,   // include preamble?
                        "content,ignorechecktimestamp",
                        exist_timestamp,
                        out output_timestamp,
                        out strOutputPath,
                        out strError);
                    if (lRet == -1)
                        goto ERROR1;
                }
                else if (strAction == "delete")
                {
                    // 和SnapShot方式相同
                    bReuse = true;
                    goto DO_SNAPSHOT;
                }
                else
                {
                    strError = "无法识别的<action>内容 '" + strAction + "'";
                    return -1;
                }
            }

            return 0;
        ERROR1:
            if (level == RecoverLevel.LogicAndSnapshot)
            {
                level = RecoverLevel.Snapshot;
                goto DO_SNAPSHOT;
            }
            return -1;
        }
Beispiel #14
0
        // 执行一个日志记录的恢复动作
        // parameters:
        //      attachment  附件流对象。注意文件指针在流的尾部
        public int DoOperLogRecord(
            RecoverLevel level,
            string strXml,
            Stream attachment,
            string strStyle,
            out string strError)
        {
            strError = "";
            int nRet = 0;

            XmlDocument dom = new XmlDocument();

            try
            {
                dom.LoadXml(strXml);
            }
            catch (Exception ex)
            {
                strError = "日志记录装载到DOM时出错: " + ex.Message;
                return(-1);
            }

            string strOperation = DomUtil.GetElementText(dom.DocumentElement,
                                                         "operation");

            if (strOperation == "borrow")
            {
                nRet = this.App.RecoverBorrow(this.RmsChannels,
                                              level,
                                              dom,
                                              false,
                                              out strError);
            }
            else if (strOperation == "return")
            {
                nRet = this.App.RecoverReturn(this.RmsChannels,
                                              level,
                                              dom,
                                              false,
                                              out strError);
            }
            else if (strOperation == "setEntity")
            {
                nRet = this.App.RecoverSetEntity(this.RmsChannels,
                                                 level,
                                                 dom,
                                                 out strError);
            }
            else if (strOperation == "setOrder")
            {
                nRet = this.App.RecoverSetOrder(this.RmsChannels,
                                                level,
                                                dom,
                                                out strError);
            }
            else if (strOperation == "setIssue")
            {
                nRet = this.App.RecoverSetIssue(this.RmsChannels,
                                                level,
                                                dom,
                                                out strError);
            }
            else if (strOperation == "setComment")
            {
                nRet = this.App.RecoverSetComment(this.RmsChannels,
                                                  level,
                                                  dom,
                                                  out strError);
            }
            else if (strOperation == "changeReaderPassword")
            {
                nRet = this.App.RecoverChangeReaderPassword(this.RmsChannels,
                                                            level,
                                                            dom,
                                                            out strError);
            }
            else if (strOperation == "changeReaderTempPassword")
            {
                // 2013/11/3
            }
            else if (strOperation == "setReaderInfo")
            {
                nRet = this.App.RecoverSetReaderInfo(this.RmsChannels,
                                                     level,
                                                     dom,
                                                     out strError);
            }
            else if (strOperation == "devolveReaderInfo")
            {
                nRet = this.App.RecoverDevolveReaderInfo(this.RmsChannels,
                                                         level,
                                                         dom,
                                                         attachment,
                                                         out strError);
            }
            else if (strOperation == "amerce")
            {
                nRet = this.App.RecoverAmerce(this.RmsChannels,
                                              level,
                                              dom,
                                              out strError);
            }
            else if (strOperation == "setBiblioInfo")
            {
                nRet = this.App.RecoverSetBiblioInfo(this.RmsChannels,
                                                     level,
                                                     dom,
                                                     out strError);
            }
            else if (strOperation == "hire")
            {
                nRet = this.App.RecoverHire(this.RmsChannels,
                                            level,
                                            dom,
                                            out strError);
            }
            else if (strOperation == "foregift")
            {
                // 2008/11/11
                nRet = this.App.RecoverForegift(this.RmsChannels,
                                                level,
                                                dom,
                                                out strError);
            }
            else if (strOperation == "settlement")
            {
                nRet = this.App.RecoverSettlement(this.RmsChannels,
                                                  level,
                                                  dom,
                                                  out strError);
            }
            else if (strOperation == "writeRes")
            {
                // 2011/5/26
                nRet = this.App.RecoverWriteRes(this.RmsChannels,
                                                level,
                                                dom,
                                                attachment,
                                                out strError);
            }
            else if (strOperation == "repairBorrowInfo")
            {
                // 2012/6/21
                nRet = this.App.RecoverRepairBorrowInfo(this.RmsChannels,
                                                        level,
                                                        dom,
                                                        attachment,
                                                        out strError);
            }
            else if (strOperation == "reservation")
            {
                // 暂未实现
            }
            else if (strOperation == "setUser")
            {
                // 暂未实现
            }
            else if (strOperation == "passgate")
            {
                // 只读
            }
            else if (strOperation == "getRes")
            {
                // 只读 2015/7/14
            }
            else if (strOperation == "crashReport")
            {
                // 只读 2015/7/16
            }
            else if (strOperation == "memo")
            {
                // 注记 2015/9/8
            }
            else if (strOperation == "statis")
            {
                // 统计 2019/6/24
            }
            else if (strOperation == "manageDatabase")
            {
                // 管理数据库 2017/5/23
                // 2017/10/15
                nRet = this.App.RecoverManageDatabase(this.RmsChannels,
                                                      level,
                                                      dom,
                                                      attachment,
                                                      strStyle,
                                                      out strError);
            }
            else
            {
                strError = "不能识别的日志操作类型 '" + strOperation + "'";
                return(-1);
            }

            if (nRet == -1)
            {
                string strAction = DomUtil.GetElementText(dom.DocumentElement,
                                                          "action");
                strError = "operation=" + strOperation + ";action=" + strAction + ": " + strError;
                return(-1);
            }

            return(0);
        }
Beispiel #15
0
        // SetBiblioInfo() API 或CopyBiblioInfo() API 的恢复动作
        // 函数内,使用return -1;还是goto ERROR1; 要看错误发生的时候,是否还有价值继续探索SnapShot重试。如果是,就用后者。
        /*
<root>
  <operation>setBiblioInfo</operation> 
  <action>...</action> 具体动作 有 new/change/delete/onlydeletebiblio/onlydeletesubrecord 和 onlycopybiblio/onlymovebiblio/copy/move
  <record recPath='中文图书/3'>...</record> 记录体 动作为new/change/ *move* / *copy* 时具有此元素(即delete时没有此元素)
  <oldRecord recPath='中文图书/3'>...</oldRecord> 被覆盖、删除或者移动的记录 动作为change/ *delete* / *move* / *copy* 时具备此元素
  <deletedEntityRecords> 被删除的实体记录(容器)。只有当<action>为delete时才有这个元素。
	  <record recPath='中文图书实体/100'>...</record> 这个元素可以重复。注意元素内文本内容目前为空。
	  ...
  </deletedEntityRecords>
  <copyEntityRecords> 被复制的实体记录(容器)。只有当<action>为*copy*时才有这个元素。
	  <record recPath='中文图书实体/100' targetRecPath='中文图书实体/110'>...</record> 这个元素可以重复。注意元素内文本内容目前为空。recPath属性为源记录路径,targetRecPath为目标记录路径
	  ...
  </copyEntityRecords>
  <moveEntityRecords> 被移动的实体记录(容器)。只有当<action>为*move*时才有这个元素。
	  <record recPath='中文图书实体/100' targetRecPath='中文图书实体/110'>...</record> 这个元素可以重复。注意元素内文本内容目前为空。recPath属性为源记录路径,targetRecPath为目标记录路径
	  ...
  </moveEntityRecords>
  <copyOrderRecords /> <moveOrderRecords />
  <copyIssueRecords /> <moveIssueRecords />
  <copyCommentRecords /> <moveCommentRecords />
  <operator>test</operator> 
  <operTime>Fri, 08 Dec 2006 10:12:20 GMT</operTime> 
</root>

逻辑恢复delete操作的时候,检索出全部下属的实体记录删除。
快照恢复的时候,可以根据operlogdom直接删除记录了path的那些实体记录
         * */
        public int RecoverSetBiblioInfo(
            RmsChannelCollection Channels,
            RecoverLevel level,
            XmlDocument domLog,
            out string strError)
        {
            strError = "";

            // 暂时把Robust当作Logic处理
            if (level == RecoverLevel.Robust)
                level = RecoverLevel.Logic;

            long lRet = 0;
            int nRet = 0;

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

            bool bReuse = false;    // 是否能够不顾RecorverLevel状态而重用部分代码

        DO_SNAPSHOT:

            string strAction = DomUtil.GetElementText(domLog.DocumentElement,
                "action");

            // 快照恢复
            if (level == RecoverLevel.Snapshot 
                || bReuse == true)
            {
                byte[] timestamp = null;
                byte[] output_timestamp = null;
                string strOutputPath = "";

                if (strAction == "new" || strAction == "change")
                {
                    XmlNode node = null;
                    string strRecord = DomUtil.GetElementText(domLog.DocumentElement,
                        "record",
                        out node);
                    if (node == null)
                    {
                        strError = "日志记录中缺<record>元素";
                        goto ERROR1;
                    }
                    string strRecPath = DomUtil.GetAttr(node, "recPath");

                    // 写书目记录
                    lRet = channel.DoSaveTextRes(strRecPath,
                        strRecord,
                        false,
                        "content,ignorechecktimestamp",
                        timestamp,
                        out output_timestamp,
                        out strOutputPath,
                        out strError);
                    if (lRet == -1)
                    {
                        strError = "写入书目记录 '" + strRecPath + "' 时发生错误: " + strError;
                        return -1;
                    }
                }
                else if (strAction == "onlymovebiblio"
                    || strAction == "onlycopybiblio"
                    || strAction == "move"
                    || strAction == "copy")
                {
                    XmlNode node = null;
                    string strTargetRecord = DomUtil.GetElementText(domLog.DocumentElement,
                        "record",
                        out node);
                    if (node == null)
                    {
                        strError = "日志记录中缺<record>元素";
                        goto ERROR1;
                    }
                    string strTargetRecPath = DomUtil.GetAttr(node, "recPath");

                    string strOldRecord = DomUtil.GetElementText(domLog.DocumentElement,
                        "oldRecord",
                        out node);
                    if (node == null)
                    {
                        strError = "日志记录中缺<oldRecord>元素";
                        return -1;
                    }
                    string strOldRecPath = DomUtil.GetAttr(node, "recPath");

                    string strMergeStyle = DomUtil.GetElementText(domLog.DocumentElement,
                        "mergeStyle");

                    bool bSourceExist = true;
                    // 观察源记录是否存在
                    {
                        string strMetaData = "";
                        string strXml = "";
                        byte[] temp_timestamp = null;

                        lRet = channel.GetRes(strOldRecPath,
                            out strXml,
                            out strMetaData,
                            out temp_timestamp,
                            out strOutputPath,
                            out strError);
                        if (lRet == -1)
                        {
                            if (channel.ErrorCode == ChannelErrorCode.NotFound)
                            {
                                bSourceExist = false;
                            }
                        }
                    }

                    if (bSourceExist == true)
                    {
                        string strIdChangeList = "";

                        // 复制书目记录
                        lRet = channel.DoCopyRecord(strOldRecPath,
                            strTargetRecPath,
                            strAction == "onlymovebiblio" ? true : false,   // bDeleteSourceRecord
                            strMergeStyle,
                            out strIdChangeList,
                            out output_timestamp,
                            out strOutputPath,
                            out strError);
                        if (lRet == -1)
                        {
                            strError = "DoCopyRecord() error :" + strError;
                            goto ERROR1;
                        }
                    }

                    /*
                    // 写书目记录
                    lRet = channel.DoSaveTextRes(strRecPath,
                        strRecord,
                        false,
                        "content,ignorechecktimestamp",
                        timestamp,
                        out output_timestamp,
                        out strOutputPath,
                        out strError);
                    if (lRet == -1)
                    {
                        strError = "复制书目记录 '" + strOldRecPath + "' 到 '" + strTargetRecPath + "' 时发生错误: " + strError;
                        return -1;
                    }                     * */


                    if (bSourceExist == false)
                    {
                        if (String.IsNullOrEmpty(strTargetRecord) == true)
                        {
                            if (String.IsNullOrEmpty(strOldRecord) == true)
                            {
                                strError = "源记录 '" + strOldRecPath + "' 不存在,并且<record>元素无文本内容,这时<oldRecord>元素也无文本内容,无法获得要写入的记录内容";
                                return -1;
                            }

                            strTargetRecord = strOldRecord;
                        }
                    }

                    // 如果有“新记录”内容
                    if (String.IsNullOrEmpty(strTargetRecord) == false)
                    {

                        // 写书目记录
                        lRet = channel.DoSaveTextRes(strTargetRecPath,
                            strTargetRecord,
                            false,
                            "content,ignorechecktimestamp",
                            timestamp,
                            out output_timestamp,
                            out strOutputPath,
                            out strError);
                        if (lRet == -1)
                        {
                            strError = "写书目记录 '" + strTargetRecPath + "' 时发生错误: " + strError;
                            return -1;
                        }
                    }

                    // 复制或者移动下级子纪录
                    if (strAction == "move"
                    || strAction == "copy")
                    {
                        string[] element_names = new string[] {
                            "copyEntityRecords",
                            "moveEntityRecords",  
                            "copyOrderRecords", 
                            "moveOrderRecords",
                            "copyIssueRecords", 
                            "moveIssueRecords",   
                            "copyCommentRecords", 
                            "moveCommentRecords"     
                        };

                        for (int i = 0; i < element_names.Length; i++)
                        {
                            XmlNode node_subrecords = domLog.DocumentElement.SelectSingleNode(
                                element_names[i]);
                            if (node_subrecords != null)
                            {
                                nRet = CopySubRecords(
                                    channel,
                                    node_subrecords,
                                    strTargetRecPath,
                                    out strError);
                                if (nRet == -1)
                                    return -1;
                            }
                        }

                    }

                    // 2011/12/12
                    if (bSourceExist == true
                        && (strAction == "move" || strAction == "onlymovebiblio")
                        )
                    {
                        int nRedoCount = 0;
                    REDO_DELETE:
                        // 删除源书目记录
                        lRet = channel.DoDeleteRes(strOldRecPath,
                            timestamp,
                            out output_timestamp,
                            out strError);
                        if (lRet == -1)
                        {
                            if (channel.ErrorCode == ChannelErrorCode.NotFound)
                            {
                                // 记录本来就不存在
                            }
                            else if (channel.ErrorCode == ChannelErrorCode.TimestampMismatch)
                            {
                                if (nRedoCount < 10)
                                {
                                    timestamp = output_timestamp;
                                    nRedoCount++;
                                    goto REDO_DELETE;
                                }
                            }
                            else
                            {
                                strError = "删除书目记录 '" + strOldRecPath + "' 时发生错误: " + strError;
                                return -1;
                            }
                        }
                    }
                }
                else if (strAction == "delete"
                    || strAction == "onlydeletebiblio"
                    || strAction == "onlydeletesubrecord")
                {
                    XmlNode node = null;
                    string strOldRecord = DomUtil.GetElementText(domLog.DocumentElement,
                        "oldRecord",
                        out node);
                    if (node == null)
                    {
                        strError = "日志记录中缺<oldRecord>元素";
                        return -1;
                    }
                    string strRecPath = DomUtil.GetAttr(node, "recPath");

                    if (strAction != "onlydeletesubrecord")
                    {
                        int nRedoCount = 0;
                    REDO:
                        // 删除书目记录
                        lRet = channel.DoDeleteRes(strRecPath,
                            timestamp,
                            out output_timestamp,
                            out strError);
                        if (lRet == -1)
                        {
                            if (channel.ErrorCode == ChannelErrorCode.NotFound)
                                goto DO_DELETE_CHILD_ENTITYRECORDS;   // 记录本来就不存在
                            if (channel.ErrorCode == ChannelErrorCode.TimestampMismatch)
                            {
                                if (nRedoCount < 10)
                                {
                                    timestamp = output_timestamp;
                                    nRedoCount++;
                                    goto REDO;
                                }
                            }
                            strError = "删除书目记录 '" + strRecPath + "' 时发生错误: " + strError;
                            return -1;
                        }
                    }

                DO_DELETE_CHILD_ENTITYRECORDS:
                    if (strAction == "delete" || strAction == "onlydeletesubrecord")
                    {
                        XmlNodeList nodes = domLog.DocumentElement.SelectNodes("deletedEntityRecords/record");
                        for (int i = 0; i < nodes.Count; i++)
                        {
                            string strEntityRecPath = DomUtil.GetAttr(nodes[i], "recPath");

                            /*
                            if (String.IsNullOrEmpty(strEntityRecPath) == true)
                                continue;
                             * */
                            int nRedoDeleteCount = 0;
                        REDO_DELETE_ENTITY:
                            // 删除实体记录
                            lRet = channel.DoDeleteRes(strEntityRecPath,
                                timestamp,
                                out output_timestamp,
                                out strError);
                            if (lRet == -1)
                            {
                                if (channel.ErrorCode == ChannelErrorCode.NotFound)
                                    continue;   // 记录本来就不存在
                                if (channel.ErrorCode == ChannelErrorCode.TimestampMismatch)
                                {
                                    if (nRedoDeleteCount < 10)
                                    {
                                        timestamp = output_timestamp;
                                        nRedoDeleteCount++;
                                        goto REDO_DELETE_ENTITY;
                                    }
                                }
                                strError = "删除实体记录 '" + strEntityRecPath + "' 时发生错误: " + strError;
                                return -1;
                            }
                        }

                        nodes = domLog.DocumentElement.SelectNodes("deletedOrderRecords/record");
                        for (int i = 0; i < nodes.Count; i++)
                        {
                            string strOrderRecPath = DomUtil.GetAttr(nodes[i], "recPath");

                            if (String.IsNullOrEmpty(strOrderRecPath) == true)
                                continue;
                            int nRedoDeleteCount = 0;
                        REDO_DELETE_ORDER:
                            // 删除订购记录
                            lRet = channel.DoDeleteRes(strOrderRecPath,
                                timestamp,
                                out output_timestamp,
                                out strError);
                            if (lRet == -1)
                            {
                                if (channel.ErrorCode == ChannelErrorCode.NotFound)
                                    continue;   // 记录本来就不存在
                                if (channel.ErrorCode == ChannelErrorCode.TimestampMismatch)
                                {
                                    if (nRedoDeleteCount < 10)
                                    {
                                        timestamp = output_timestamp;
                                        nRedoDeleteCount++;
                                        goto REDO_DELETE_ORDER;
                                    }
                                }
                                strError = "删除订购记录 '" + strOrderRecPath + "' 时发生错误: " + strError;
                                return -1;
                            }
                        }

                        nodes = domLog.DocumentElement.SelectNodes("deletedIssueRecords/record");
                        for (int i = 0; i < nodes.Count; i++)
                        {
                            string strIssueRecPath = DomUtil.GetAttr(nodes[i], "recPath");

                            if (String.IsNullOrEmpty(strIssueRecPath) == true)
                                continue;
                            int nRedoDeleteCount = 0;
                        REDO_DELETE_ISSUE:
                            // 删除期记录
                            lRet = channel.DoDeleteRes(strIssueRecPath,
                                timestamp,
                                out output_timestamp,
                                out strError);
                            if (lRet == -1)
                            {
                                if (channel.ErrorCode == ChannelErrorCode.NotFound)
                                    continue;   // 记录本来就不存在
                                if (channel.ErrorCode == ChannelErrorCode.TimestampMismatch)
                                {
                                    if (nRedoDeleteCount < 10)
                                    {
                                        timestamp = output_timestamp;
                                        nRedoDeleteCount++;
                                        goto REDO_DELETE_ISSUE;
                                    }
                                }
                                strError = "删除期记录 '" + strIssueRecPath + "' 时发生错误: " + strError;
                                return -1;
                            }
                        }

                    } // end if
                }


                return 0;
            }

            // 逻辑恢复或者混合恢复
            if (level == RecoverLevel.Logic
                || level == RecoverLevel.LogicAndSnapshot)
            {
                // 和数据库中已有记录合并,然后保存
                if (strAction == "new" || strAction == "change")
                {
                    // 和SnapShot方式相同
                    bReuse = true;
                    goto DO_SNAPSHOT;
                }
                else if (strAction == "onlymovebiblio"
                    || strAction == "onlycopybiblio"
                    || strAction == "move"
                    || strAction == "copy")
                {
                    // 和SnapShot方式相同
                    bReuse = true;
                    goto DO_SNAPSHOT;
                }
                else if (strAction == "delete"
                    || strAction == "onlydeletebiblio"
                    || strAction == "onlydeletesubrecord")
                {
                    XmlNode node = null;
                    string strOldRecord = DomUtil.GetElementText(domLog.DocumentElement,
                        "oldRecord",
                        out node);
                    if (node == null)
                    {
                        strError = "日志记录中缺<oldRecord>元素";
                        return -1;
                    }
                    string strRecPath = DomUtil.GetAttr(node, "recPath");

                    if (strAction != "onlydeletesubrecord")
                    {
                        int nRedoCount = 0;
                        byte[] timestamp = null;
                        byte[] output_timestamp = null;
                    REDO:
                        // 删除书目记录
                        lRet = channel.DoDeleteRes(strRecPath,
                            timestamp,
                            out output_timestamp,
                            out strError);
                        if (lRet == -1)
                        {
                            if (channel.ErrorCode == ChannelErrorCode.NotFound)
                                goto DO_DELETE_CHILD_ENTITYRECORDS;   // 记录本来就不存在
                            if (channel.ErrorCode == ChannelErrorCode.TimestampMismatch)
                            {
                                if (nRedoCount < 10)
                                {
                                    timestamp = output_timestamp;
                                    nRedoCount++;
                                    goto REDO;
                                }
                            }
                            strError = "删除书目记录 '" + strRecPath + "' 时发生错误: " + strError;
                            goto ERROR1;
                        }
                    }

                DO_DELETE_CHILD_ENTITYRECORDS:

                    if (strAction == "delete" || strAction == "onlydeletesubrecord")
                    {
                        // 删除属于同一书目记录的全部实体记录
                        // return:
                        //      -1  error
                        //      0   没有找到属于书目记录的任何实体记录,因此也就无从删除
                        //      >0  实际删除的实体记录数
                        nRet = DeleteBiblioChildEntities(channel,
                            strRecPath,
                            null,
                            out strError);
                        if (nRet == -1)
                        {
                            strError = "删除书目记录 '" + strRecPath + "' 下属的实体记录时出错: " + strError;
                            goto ERROR1;
                        }

                        // return:
                        //      -1  error
                        //      0   没有找到属于书目记录的任何实体记录,因此也就无从删除
                        //      >0  实际删除的实体记录数
                        nRet = this.OrderItemDatabase.DeleteBiblioChildItems(
                            // Channels,
                            channel,
                            strRecPath,
                            null,
                            out strError);
                        if (nRet == -1)
                        {
                            strError = "删除书目记录 '" + strRecPath + "' 下属的订购记录时出错: " + strError;
                            goto ERROR1;
                        }

                        // return:
                        //      -1  error
                        //      0   没有找到属于书目记录的任何实体记录,因此也就无从删除
                        //      >0  实际删除的实体记录数
                        nRet = this.IssueItemDatabase.DeleteBiblioChildItems(
                            // Channels,
                            channel,
                            strRecPath,
                            null,
                            out strError);
                        if (nRet == -1)
                        {
                            strError = "删除书目记录 '" + strRecPath + "' 下属的期记录时出错: " + strError;
                            goto ERROR1;
                        }
                    }
                }
                else
                {
                    strError = "无法识别的<action>内容 '" + strAction + "'";
                    return -1;
                }
            }
            return 0;
        ERROR1:
            if (level == RecoverLevel.LogicAndSnapshot)
            {
                level = RecoverLevel.Snapshot;
                goto DO_SNAPSHOT;
            }
            return -1;
        }
Beispiel #16
0
        // 一次操作循环
        public override void Worker()
        {
            // 把系统挂起
            // this.App.HangupReason = HangupReason.LogRecover;
            this.App.HangupList.Add("LogRecover");
            try
            {
                string strError = "";

                BatchTaskStartInfo startinfo = this.StartInfo;
                if (startinfo == null)
                {
                    startinfo = new BatchTaskStartInfo(); // 按照缺省值来
                }
                long   lStartIndex      = 0;              // 开始位置
                string strStartFileName = "";             // 开始文件名
                int    nRet             = ParseLogRecorverStart(startinfo.Start,
                                                                out lStartIndex,
                                                                out strStartFileName,
                                                                out strError);
                if (nRet == -1)
                {
                    this.AppendResultText("启动失败: " + strError + "\r\n");
                    return;
                }

                //
                string strRecoverLevel    = "";
                bool   bClearFirst        = false;
                bool   bContinueWhenError = false;

                nRet = ParseLogRecoverParam(startinfo.Param,
                                            out strRecoverLevel,
                                            out bClearFirst,
                                            out bContinueWhenError,
                                            out strError);
                if (nRet == -1)
                {
                    this.AppendResultText("启动失败: " + strError + "\r\n");
                    return;
                }

                if (String.IsNullOrEmpty(strRecoverLevel) == true)
                {
                    strRecoverLevel = "Snapshot";
                }

                try
                {
                    this.RecoverLevel = (RecoverLevel)Enum.Parse(typeof(RecoverLevel), strRecoverLevel, true);
                }
                catch (Exception ex)
                {
                    this.AppendResultText("启动失败: 启动参数Param中的recoverLevel枚举值 '" + strRecoverLevel + "' 错误: " + ex.Message + "\r\n");
                    return;
                }

                this.App.WriteErrorLog("日志恢复 任务启动。");

                // 当为容错恢复级别时,检查当前全部读者库的检索点是否符合要求
                if (this.RecoverLevel == LibraryServer.RecoverLevel.Robust)
                {
                    // 检查全部读者库的检索途径,看是否满足都有“所借册条码号”这个检索途径的这个条件
                    // return:
                    //      -1  出错
                    //      0   不满足
                    //      1   满足
                    nRet = this.App.DetectReaderDbFroms(out strError);
                    if (nRet == -1)
                    {
                        this.AppendResultText("检查读者库检索点时发生错误: " + strError + "\r\n");
                        return;
                    }
                    if (nRet == 0)
                    {
                        this.AppendResultText("在容错恢复级别下,当前读者库中有部分或全部读者库缺乏“所借册条码号”检索点,无法进行日志恢复。请按照日志恢复要求,刷新所有读者库的检索点配置,然后再进行日志恢复\r\n");
                        return;
                    }
                }

                // TODO: 检查当前是否有 重建检索点 的后台任务正在运行,或者还有没有运行完的部分。
                // 要求重建检索点的任务运行完以后才能执行日志恢复任务

                if (bClearFirst == true)
                {
                    nRet = this.App.ClearAllDbs(this.RmsChannels,
                                                out strError);
                    if (nRet == -1)
                    {
                        this.AppendResultText("清除全部数据库记录时发生错误: " + strError + "\r\n");
                        return;
                    }
                }

                bool bStart = false;
                if (String.IsNullOrEmpty(strStartFileName) == true)
                {
                    // 做所有文件
                    bStart = true;
                }


                // 列出所有日志文件
                DirectoryInfo di = new DirectoryInfo(this.App.OperLog.Directory);

                FileInfo[] fis = di.GetFiles("*.log");

                // BUG!!! 以前缺乏排序。2008/2/1
                Array.Sort(fis, new FileInfoCompare());


                for (int i = 0; i < fis.Length; i++)
                {
                    if (this.Stopped == true)
                    {
                        break;
                    }

                    string strFileName = fis[i].Name;

                    this.AppendResultText("检查文件 " + strFileName + "\r\n");

                    if (bStart == false)
                    {
                        // 从特定文件开始做
                        if (string.CompareOrdinal(strStartFileName, strFileName) <= 0)  // 2015/9/12 从等号修改为 Compare
                        {
                            bStart = true;
                            if (lStartIndex < 0)
                            {
                                lStartIndex = 0;
                            }
                            // lStartIndex = Convert.ToInt64(startinfo.Param);
                        }
                    }

                    if (bStart == true)
                    {
                        nRet = DoOneLogFile(strFileName,
                                            lStartIndex,
                                            bContinueWhenError,
                                            out strError);
                        if (nRet == -1)
                        {
                            goto ERROR1;
                        }
                        lStartIndex = 0;    // 第一个文件以后的文件就全做了
                    }
                }

                this.AppendResultText("循环结束\r\n");

                this.App.WriteErrorLog("日志恢复 任务结束。");

                return;

ERROR1:
                return;
            }
            finally
            {
                // this.App.HangupReason = HangupReason.None;
                this.App.ClearHangup("LogRecover");
            }
        }
Beispiel #17
0
        // Return() API 恢复动作
        /* 日志记录格式
<root>
  <operation>return</operation> 操作类型
  <itemBarcode>0000001</itemBarcode> 册条码号
  <readerBarcode>R0000002</readerBarcode> 读者证条码号
  <operator>test</operator> 操作者
  <operTime>Fri, 08 Dec 2006 04:17:45 GMT</operTime> 操作时间
  <overdues>...</overdues> 超期信息 通常内容为一个字符串,为一个<overdue>元素XML文本片断
  
  <confirmItemRecPath>...</confirmItemRecPath> 辅助判断用的册记录路径
  
  <readerRecord recPath='...'>...</readerRecord>	最新读者记录
  <itemRecord recPath='...'>...</itemRecord>	最新册记录
  
</root>
         * * */
        // parameters:
        //      bForce  是否为容错状态。在容错状态下,如果遇到重复的册条码号,就算做第一条。
        public int RecoverReturn(
            RmsChannelCollection Channels,
            RecoverLevel level,
            XmlDocument domLog,
            bool bForce,
            out string strError)
        {
            strError = "";

            long lRet = 0;
            int nRet = 0;

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

        DO_SNAPSHOT:

            // 快照恢复
            if (level == RecoverLevel.Snapshot)
            {
                XmlNode node = null;
                string strReaderXml = DomUtil.GetElementText(domLog.DocumentElement,
                    "readerRecord",
                    out node);
                if (node == null)
                {
                    strError = "日志记录中缺<readerRecord>元素";
                    return -1;
                }
                string strReaderRecPath = DomUtil.GetAttr(node, "recPath");

                string strItemXml = DomUtil.GetElementText(domLog.DocumentElement,
                    "itemRecord",
                    out node);
                if (node == null)
                {
                    strError = "日志记录中缺<itemRecord>元素";
                    return -1;
                }
                string strItemRecPath = DomUtil.GetAttr(node, "recPath");

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

                // 写读者记录
                lRet = channel.DoSaveTextRes(strReaderRecPath,
                    strReaderXml,
                    false,
                    "content,ignorechecktimestamp",
                    timestamp,
                    out output_timestamp,
                    out strOutputPath,
                    out strError);
                if (lRet == -1)
                {
                    strError = "写入读者记录 '" + strReaderRecPath + "' 时发生错误: " + strError;
                    return -1;
                }

                // 写册记录
                lRet = channel.DoSaveTextRes(strItemRecPath,
                    strItemXml,
                    false,
                    "content,ignorechecktimestamp",
                    timestamp,
                    out output_timestamp,
                    out strOutputPath,
                    out strError);
                if (lRet == -1)
                {
                    strError = "写入读者记录 '" + strReaderRecPath + "' 时发生错误: " + strError;
                    return -1;
                }


                return 0;
            }


            // 逻辑恢复或者混合恢复
            if (level == RecoverLevel.Logic
                || level == RecoverLevel.LogicAndSnapshot)
            {
                string strRecoverComment = "";

                string strAction = DomUtil.GetElementText(domLog.DocumentElement,
                    "action");

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


                // 读入册记录
                string strConfirmItemRecPath = DomUtil.GetElementText(domLog.DocumentElement,
                    "confirmItemRecPath");
                string strItemBarcode = DomUtil.GetElementText(domLog.DocumentElement,
                    "itemBarcode");

                if (String.IsNullOrEmpty(strItemBarcode) == true)
                {
                    strError = "<strItemBarcode>元素值为空";
                    goto ERROR1;
                }

                string strItemXml = "";
                string strOutputItemRecPath = "";
                byte[] item_timestamp = null;

                // 如果已经有确定的册记录路径
                if (String.IsNullOrEmpty(strConfirmItemRecPath) == false)
                {
                    string strMetaData = "";
                    lRet = channel.GetRes(strConfirmItemRecPath,
                        out strItemXml,
                        out strMetaData,
                        out item_timestamp,
                        out strOutputItemRecPath,
                        out strError);
                    if (lRet == -1)
                    {
                        strError = "根据strConfirmItemRecPath '" + strConfirmItemRecPath + "' 获得册记录失败: " + strError;
                        goto ERROR1;
                    }

                    // 需要检查记录中的<barcode>元素值是否匹配册条码号
                }
                else
                {
                    // 从册条码号获得册记录
                    List<string> aPath = null;

                    // 获得册记录
                    // return:
                    //      -1  error
                    //      0   not found
                    //      1   命中1条
                    //      >1  命中多于1条
                    nRet = this.GetItemRecXml(
                        // Channels,
                        channel,
                        strItemBarcode,
                        out strItemXml,
                        100,
                        out aPath,
                        out item_timestamp,
                        out strError);
                    if (nRet == 0)
                    {
                        strError = "册条码号 '" + strItemBarcode + "' 不存在";
                        goto ERROR1;
                    }
                    if (nRet == -1)
                    {
                        strError = "读入册条码号为 '" + strItemBarcode + "' 的册记录时发生错误: " + strError;
                        goto ERROR1;
                    }

                    if (aPath.Count > 1)
                    {

                        if (string.IsNullOrEmpty(strReaderBarcode) == true)
                        {
                            // 发生重条码号的时候,又没有读者证条码号辅助判断
                            if (bForce == false)
                            {
                                strError = "册条码号为 '" + strItemBarcode + "' 的册记录有 " + aPath.Count.ToString() + " 条,但此时日志记录中没有提供读者证条码号辅助判断,无法进行还书操作。";
                                goto ERROR1;
                            }
                            // TODO: 那就至少看看这些册中,哪些表明被人借阅着?如果正巧只有一个人借过,那就...。
                            strRecoverComment += "册条码号 " + strItemBarcode + "有 " + aPath.Count.ToString() + " 条重复记录,而且没有读者证条码号进行辅助选择。";
                        }

                        /*
                        strError = "册条码号为 '" + strItemBarcode + "' 的册记录有 " + aPath.Count.ToString() + " 条,但此时comfirmItemRecPath却为空";
                        goto ERROR1;
                         * */
                        // bItemBarcodeDup = true; // 此时已经需要设置状态。虽然后面可以进一步识别出真正的册记录

                        /*
                        // 构造strDupBarcodeList
                        string[] pathlist = new string[aPath.Count];
                        aPath.CopyTo(pathlist);
                        strDupBarcodeList = String.Join(",", pathlist);
                         * */

                        List<string> aFoundPath = null;
                        List<byte[]> aTimestamp = null;
                        List<string> aItemXml = null;

                        // 从若干重复条码号的册记录中,选出其中符合当前读者证条码号的
                        // return:
                        //      -1  出错
                        //      其他    选出的数量
                        nRet = FindItem(
                            channel,
                            strReaderBarcode,
                            aPath,
                            true,   // 优化
                            out aFoundPath,
                            out aItemXml,
                            out aTimestamp,
                            out strError);
                        if (nRet == -1)
                        {
                            strError = "选择重复条码号的册记录时发生错误: " + strError;
                            goto ERROR1;
                        }

                        if (nRet == 0)
                        {
                            strError = "册条码号 '" + strItemBarcode + "' 检索出的 " + aPath.Count + " 条记录中,没有任何一条其<borrower>元素表明了被读者 '" + strReaderBarcode + "' 借阅。";
                            goto ERROR1;
                        }

                        if (nRet > 1)
                        {
                            if (bForce == true)
                            {
                                // 容错情况下,选择第一个册条码号
                                strOutputItemRecPath = aFoundPath[0];
                                item_timestamp = aTimestamp[0];
                                strItemXml = aItemXml[0];

                                // TODO: 不过,应当在记录中记载注释,表示这是容错处理方式
                                if (string.IsNullOrEmpty(strReaderBarcode) == true)
                                {
                                    strRecoverComment += "经过筛选,仍然有 " + aFoundPath.Count.ToString() + " 条册记录含有借阅者信息(无论什么读者证条码号),那么就只好选择其中第一个册记录 " + strOutputItemRecPath + " 进行还书操作。";
                                }
                                else
                                {
                                    strRecoverComment += "经过筛选,仍然有 " + aFoundPath.Count.ToString() + " 条册记录含有借阅者 '"+strReaderBarcode+"' 信息,那么就只好选择其中第一个册记录 " + strOutputItemRecPath + " 进行还书操作。";
                                }
                            }
                            else
                            {
                                strError = "册条码号为 '" + strItemBarcode + "' 并且<borrower>元素表明为读者 '" + strReaderBarcode + "' 借阅的册记录有 " + aFoundPath.Count.ToString() + " 条,无法进行还书操作。";
                                /*
                                aDupPath = new string[aFoundPath.Count];
                                aFoundPath.CopyTo(aDupPath);
                                 * */
                                goto ERROR1;
                            }
                        }

                        Debug.Assert(nRet == 1, "");

                        strOutputItemRecPath = aFoundPath[0];
                        item_timestamp = aTimestamp[0];
                        strItemXml = aItemXml[0];
                    }
                    else
                    {

                        Debug.Assert(nRet == 1, "");
                        Debug.Assert(aPath.Count == 1, "");

                        if (nRet == 1)
                        {
                            strOutputItemRecPath = aPath[0];
                        }
                    }

                }

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

                ///
                if (String.IsNullOrEmpty(strReaderBarcode) == true)
                {
                    if (bForce == true)
                    {
                        // 容错的情况下,从册记录中获得借者证条码号
                        strReaderBarcode = DomUtil.GetElementText(itemdom.DocumentElement,
                            "borrower");
                        if (String.IsNullOrEmpty(strReaderBarcode) == true)
                        {
                            strError = "在不知道读者证条码号的情况下,册记录中的<borrower>元素值为空。无法进行还书操作。";
                            goto ERROR1;
                        }

                    }
                    else
                    {
                        strError = "日志记录中<readerBarcode>元素值为空";
                        goto ERROR1;
                    }
                }

                // 读入读者记录
                string strReaderXml = "";
                string strOutputReaderRecPath = "";
                byte[] reader_timestamp = null;

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

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

                // 修改读者记录
                // 修改册记录
                nRet = ReturnChangeReaderAndItemRecord(
                    // Channels,
                    channel,
                    strAction,
                    strItemBarcode,
                    strReaderBarcode,
                    domLog,
                    strRecoverComment,
                    ref readerdom,
                    ref itemdom,
                    out strError);
                if (nRet == -1)
                    goto ERROR1;

                // 写回读者、册记录
                byte[] output_timestamp = null;
                string strOutputPath = "";


                // 写回读者记录
                lRet = channel.DoSaveTextRes(strOutputReaderRecPath,
                    readerdom.OuterXml,
                    false,
                    "content,ignorechecktimestamp",
                    reader_timestamp,
                    out output_timestamp,
                    out strOutputPath,
                    out strError);
                if (lRet == -1)
                    goto ERROR1;

                // 写回册记录
                lRet = channel.DoSaveTextRes(strOutputItemRecPath,
                    itemdom.OuterXml,
                    false,
                    "content,ignorechecktimestamp",
                    item_timestamp,
                    out output_timestamp,
                    out strOutputPath,
                    out strError);
                if (lRet == -1)
                    goto ERROR1;

            }


            // 容错恢复
            if (level == RecoverLevel.Robust)
            {
                string strRecoverComment = "";

                string strAction = DomUtil.GetElementText(domLog.DocumentElement,
                    "action");

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


                // 读入册记录
                string strConfirmItemRecPath = DomUtil.GetElementText(domLog.DocumentElement,
                    "confirmItemRecPath");
                string strItemBarcode = DomUtil.GetElementText(domLog.DocumentElement,
                    "itemBarcode");

                if (String.IsNullOrEmpty(strItemBarcode) == true)
                {
                    strError = "<strItemBarcode>元素值为空";
                    goto ERROR1;
                }

                string strItemXml = "";
                string strOutputItemRecPath = "";
                byte[] item_timestamp = null;


                // 从册条码号获得册记录
                List<string> aPath = null;

                bool bDupItemBarcode = false;   // 册条码号是否发生了重复

                // 获得册记录
                // return:
                //      -1  error
                //      0   not found
                //      1   命中1条
                //      >1  命中多于1条
                nRet = this.GetItemRecXml(
                    // Channels,
                    channel,
                    strItemBarcode,
                    out strItemXml,
                    100,
                    out aPath,
                    out item_timestamp,
                    out strError);
                if (nRet == 0)
                {
                    strError = "册条码号 '" + strItemBarcode + "' 不存在";
                    // TODO: 记入信息文件

                    XmlNode node = null;
                    strItemXml = DomUtil.GetElementText(domLog.DocumentElement,
                        "itemRecord",
                        out node);
                    if (node == null)
                    {
                        strError = "日志记录中缺<itemRecord>元素";
                        return -1;
                    }
                    string strItemRecPath = DomUtil.GetAttr(node, "recPath");
                    if (String.IsNullOrEmpty(strItemRecPath) == true)
                    {
                        strError = "日志记录中<itemRecord>元素缺recPath属性";
                        return -1;
                    }

                    // 新增一条册记录
                    strOutputItemRecPath = ResPath.GetDbName(strItemRecPath) + "/?";
                    item_timestamp = null;
                }
                else
                {
                    if (nRet == -1)
                    {
                        strError = "读入册条码号为 '" + strItemBarcode + "' 的册记录时发生错误: " + strError;
                        return -1;
                    }

                    if (aPath.Count > 1)
                    {
                        bDupItemBarcode = true;

                        if (string.IsNullOrEmpty(strReaderBarcode) == true)
                        {
                            // 发生重条码号的时候,又没有读者证条码号辅助判断
                            if (bForce == false)
                            {
                                strError = "册条码号为 '" + strItemBarcode + "' 的册记录有 " + aPath.Count.ToString() + " 条,但此时日志记录中没有提供读者证条码号辅助判断,无法进行还书操作。";
                                return -1;
                            }
                            // TODO: 那就至少看看这些册中,哪些表明被人借阅着?如果正巧只有一个人借过,那就...。
                            strRecoverComment += "册条码号 " + strItemBarcode + " 有 " + aPath.Count.ToString() + " 条重复记录,而且没有读者证条码号进行辅助选择。";
                        }

                        List<string> aFoundPath = null;
                        List<byte[]> aTimestamp = null;
                        List<string> aItemXml = null;

                        // 从若干重复条码号的册记录中,选出其中符合当前读者证条码号的
                        // return:
                        //      -1  出错
                        //      其他    选出的数量
                        nRet = FindItem(
                            channel,
                            strReaderBarcode,
                            aPath,
                            true,   // 优化
                            out aFoundPath,
                            out aItemXml,
                            out aTimestamp,
                            out strError);
                        if (nRet == -1)
                        {
                            strError = "选择重复条码号的册记录时发生错误: " + strError;
                            return -1;
                        }

                        if (nRet == 0)
                        {
                            if (bDupItemBarcode == false)
                            {
                                // 没有重复册条码号的情况下才作
                                // 需要把根据“所借册条码号”清除读者记录中借阅信息的动作提前进行? 这样遇到特殊情况范围时,至少读者记录中的信息是被清除了的,这是容错的需要
                                string strError_1 = "";
                                nRet = ReturnAllReader(
                                        // Channels,
                                        channel,
                                        strItemBarcode,
                                        "",
                                        out strError_1);
                                if (nRet == -1)
                                {
                                    // 故意不报,继续处理
                                }
                            }

                            strError = "册条码号 '" + strItemBarcode + "' 检索出的 " + aPath.Count + " 条记录中,没有任何一条其<borrower>元素表明了被读者 '" + strReaderBarcode + "' 借阅。";
                            return -1;
                        }

                        if (nRet > 1)
                        {
                            if (bForce == true)
                            {
                                // 容错情况下,选择第一个册条码号
                                strOutputItemRecPath = aFoundPath[0];
                                item_timestamp = aTimestamp[0];
                                strItemXml = aItemXml[0];

                                // TODO: 不过,应当在记录中记载注释,表示这是容错处理方式
                                if (string.IsNullOrEmpty(strReaderBarcode) == true)
                                {
                                    strRecoverComment += "经过筛选,仍然有 " + aFoundPath.Count.ToString() + " 条册记录含有借阅者信息(无论什么读者证条码号),那么就只好选择其中第一个册记录 " + strOutputItemRecPath + " 进行还书操作。";
                                }
                                else
                                {
                                    strRecoverComment += "经过筛选,仍然有 " + aFoundPath.Count.ToString() + " 条册记录含有借阅者 '" + strReaderBarcode + "' 信息,那么就只好选择其中第一个册记录 " + strOutputItemRecPath + " 进行还书操作。";
                                }
                            }
                            else
                            {
                                strError = "册条码号为 '" + strItemBarcode + "' 并且<borrower>元素表明为读者 '" + strReaderBarcode + "' 借阅的册记录有 " + aFoundPath.Count.ToString() + " 条,无法进行还书操作。";
                                return -1;
                            }
                        }

                        Debug.Assert(nRet == 1, "");

                        strOutputItemRecPath = aFoundPath[0];
                        item_timestamp = aTimestamp[0];
                        strItemXml = aItemXml[0];
                    }
                    else
                    {

                        Debug.Assert(nRet == 1, "");
                        Debug.Assert(aPath.Count == 1, "");

                        if (nRet == 1)
                        {
                            strOutputItemRecPath = aPath[0];
                        }
                    }
                }


                ////

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


                ///
                if (String.IsNullOrEmpty(strReaderBarcode) == true)
                {
                    if (bForce == true)
                    {
                        // 容错的情况下,从册记录中获得借者证条码号
                        strReaderBarcode = DomUtil.GetElementText(itemdom.DocumentElement,
                            "borrower");
                        if (String.IsNullOrEmpty(strReaderBarcode) == true)
                        {
                            strError = "在不知道读者证条码号的情况下,册记录中的<borrower>元素值为空。无法进行还书操作。";
                            return -1;
                        }

                    }
                    else
                    {
                        strError = "日志记录中<readerBarcode>元素值为空";
                        return -1;
                    }
                }

                // 读入读者记录
                string strReaderXml = "";
                string strOutputReaderRecPath = "";
                byte[] reader_timestamp = null;

                nRet = this.GetReaderRecXml(
                    // Channels,
                    channel,
                    strReaderBarcode,
                    out strReaderXml,
                    out strOutputReaderRecPath,
                    out reader_timestamp,
                    out strError);
                if (nRet == 0)
                {
                    strError = "读者证条码号 '" + strReaderBarcode + "' 不存在";
                    // TODO: 记入信息文件

                    // 从日志记录中获得读者记录
                    XmlNode node = null;
                    strReaderXml = DomUtil.GetElementText(domLog.DocumentElement,
                        "readerRecord",
                        out node);
                    if (node == null)
                    {
                        strError = "日志记录中缺<readerRecord>元素";
                        return -1;
                    }
                    string strReaderRecPath = DomUtil.GetAttr(node, "recPath");
                    if (String.IsNullOrEmpty(strReaderRecPath) == true)
                    {
                        strError = "日志记录中<readerRecord>元素缺recPath属性";
                        return -1;
                    }

                    // 新增一条读者记录
                    strOutputReaderRecPath = ResPath.GetDbName(strReaderRecPath) + "/?";
                    reader_timestamp = null;
                }
                else
                {
                    if (nRet == -1)
                    {
                        strError = "读入证条码号为 '" + strReaderBarcode + "' 的读者记录时发生错误: " + strError;
                        return -1;
                    }
                }

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


                // 修改读者记录
                // 修改册记录
                nRet = ReturnChangeReaderAndItemRecord(
                    // Channels,
                    channel,
                    strAction,
                    strItemBarcode,
                    strReaderBarcode,
                    domLog,
                    strRecoverComment,
                    ref readerdom,
                    ref itemdom,
                    out strError);
                if (nRet == -1)
                    return -1;

                // 在容错(并且没有重复册条码号的情况下)的情况下,需要利用读者库的“所借册条码号”检索途径,把除了当前关注的读者记录以外的潜在相关读者记录调出,
                // 把它们中的相关<borrows/borrow>抹除,以免造成多头的借阅信息。
                if (bDupItemBarcode == false)
                {
                    nRet = ReturnAllReader(
                            // Channels,
                            channel,
                            strItemBarcode,
                            strOutputReaderRecPath,
                            out strError);
                    if (nRet == -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);
                if (lRet == -1)
                    return -1;

                // 写回册记录
                lRet = channel.DoSaveTextRes(strOutputItemRecPath,
                    itemdom.OuterXml,
                    false,
                    "content,ignorechecktimestamp",
                    item_timestamp,
                    out output_timestamp,
                    out strOutputPath,
                    out strError);
                if (lRet == -1)
                    return -1;

            }



            return 0;
        ERROR1:
            if (level == RecoverLevel.LogicAndSnapshot)
            {
                level = RecoverLevel.Snapshot;
                goto DO_SNAPSHOT;
            }
            return -1;
        }