Exemplo n.º 1
0
        // 执行API中的"move"操作
        // 1) 操作成功后, NewRecord中有实际保存的新记录,NewTimeStamp为新的时间戳
        // 2) 如果返回TimeStampMismatch错,则OldRecord中有库中发生变化后的“原记录”,OldTimeStamp是其时间戳
        // return:
        //      -1  出错
        //      0   成功
        int DoOperMove(
            SessionInfo sessioninfo,
            // string strUserID,
            RmsChannel channel,
            EntityInfo info,
            ref XmlDocument domOperLog,
            ref List<EntityInfo> ErrorInfos)
        {
            EntityInfo error = null;
            bool bExist = true;    // info.RecPath所指的记录是否存在?

            int nRet = 0;
            long lRet = 0;

            string strError = "";

            // 检查路径
            if (info.OldRecPath == info.NewRecPath)
            {
                strError = "当action为\"move\"时,info.NewRecordPath路径 '" + info.NewRecPath + "' 和info.OldRecPath '" + info.OldRecPath + "' 必须不相同";
                goto ERROR1;
            }

            // 检查即将覆盖的目标位置是不是有记录,如果有,则不允许进行move操作。
            // 如果要进行带覆盖目标位置记录功能的move操作,前端可以先执行一个delete操作,然后再执行move操作。
            // 这样规定,是为了避免过于复杂的判断逻辑,也便于前端操作者清楚操作的后果。
            // 因为如果允许move带有覆盖目标记录功能,则被覆盖的记录的预删除操作,等于进行了一次事项注销,但这个效用不明显,对前端操作人员准确判断事态并对后果负责(而且可能这种注销需要额外的操作权限),不利
            bool bAppendStyle = false;  // 目标路径是否为追加形态?
            string strTargetRecId = ResPath.GetRecordId(info.NewRecPath);

            if (strTargetRecId == "?" || String.IsNullOrEmpty(strTargetRecId) == true)
                bAppendStyle = true;

            string strOutputPath = "";
            string strMetaData = "";

            if (bAppendStyle == false)
            {
                string strExistTargetXml = "";
                byte[] exist_target_timestamp = null;

                // 获取覆盖目标位置的现有记录
                lRet = channel.GetRes(info.NewRecPath,
                    out strExistTargetXml,
                    out strMetaData,
                    out exist_target_timestamp,
                    out strOutputPath,
                    out strError);
                if (lRet == -1)
                {
                    if (channel.ErrorCode == ChannelErrorCode.NotFound)
                    {
                        // 如果记录不存在, 说明不会造成覆盖态势
                        /*
                        strExistSourceXml = "<root />";
                        exist_source_timestamp = null;
                        strOutputPath = info.NewRecPath;
                         * */
                    }
                    else
                    {
                        error = new EntityInfo(info);
                        error.ErrorInfo = "move操作发生错误, 发生在读入即将覆盖的目标位置 '" + info.NewRecPath + "' 原有记录阶段:" + strError;
                        error.ErrorCode = channel.OriginErrorCode;
                        ErrorInfos.Add(error);
                        return -1;
                    }
                }
                else
                {
                    // 如果记录存在,则目前不允许这样的操作
                    strError = "移动(move)操作被拒绝。因为在即将覆盖的目标位置 '" + info.NewRecPath + "' 已经存在" + this.ItemName + "记录。请先删除(delete)这条记录,再进行移动(move)操作";
                    goto ERROR1;
                }
            }


            string strExistSourceXml = "";
            byte[] exist_source_timestamp = null;

            // 先读出数据库中源位置的已有记录
            // REDOLOAD:

            lRet = channel.GetRes(info.OldRecPath,
                out strExistSourceXml,
                out strMetaData,
                out exist_source_timestamp,
                out strOutputPath,
                out strError);
            if (lRet == -1)
            {
                if (channel.ErrorCode == ChannelErrorCode.NotFound)
                {
                    /*
                    // 如果记录不存在, 则构造一条空的记录
                    bExist = false;
                    strExistSourceXml = "<root />";
                    exist_source_timestamp = null;
                    strOutputPath = info.NewRecPath;
                     * */
                    // 这种情况如果放宽,会有严重的副作用,所以不让放宽
                    strError = "move操作的源记录 '" + info.OldRecPath + "' 在数据库中不存在,所以无法进行移动操作。";
                    goto ERROR1;
                }
                else
                {
                    error = new EntityInfo(info);
                    error.ErrorInfo = "移动操作发生错误, 在读入库中原有源记录(路径在info.OldRecPath) '" + info.OldRecPath + "' 阶段:" + strError;
                    error.ErrorCode = channel.OriginErrorCode;
                    ErrorInfos.Add(error);
                    return -1;
                }
            }

            // 把两个记录装入DOM

            XmlDocument domSourceExist = new XmlDocument();
            XmlDocument domNew = new XmlDocument();

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

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


            // 观察时间戳是否发生变化
            nRet = ByteArray.Compare(info.OldTimestamp, exist_source_timestamp);
            if (nRet != 0)
            {
                // 时间戳不相等了
                // 需要把info.OldRecord和strExistXml进行比较,看看和事项有关的元素(要害元素)值是否发生了变化。
                // 如果这些要害元素并未发生变化,就继续进行合并、覆盖保存操作

                XmlDocument domOld = new XmlDocument();

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

                // 比较两个记录, 看看和事项有关的要害字段是否发生了变化
                // return:
                //      0   没有变化
                //      1   有变化
                nRet = IsItemInfoChanged(domOld,
                    domSourceExist);
                if (nRet == 1)
                {
                    error = new EntityInfo(info);
                    // 错误信息中, 返回了修改过的原记录和新时间戳
                    error.OldRecord = strExistSourceXml;
                    error.OldTimestamp = exist_source_timestamp;

                    if (bExist == false)
                        error.ErrorInfo = "移动操作发生错误: 数据库中的原记录 (路径为'" + info.OldRecPath + "') 已被删除。";
                    else
                        error.ErrorInfo = "移动操作发生错误: 数据库中的原记录 (路径为'" + info.OldRecPath + "') 已发生过修改";
                    error.ErrorCode = ErrorCodeValue.TimestampMismatch;
                    ErrorInfos.Add(error);
                    return -1;
                }

                // exist_source_timestamp此时已经反映了库中被修改后的记录的时间戳
            }

            // 2011/2/11
            nRet = CanChange(
sessioninfo,
"move",
domSourceExist,
domNew,
out strError);
            if (nRet == -1)
                goto ERROR1;
            if (nRet == 0)
            {
                error = new EntityInfo(info);
                error.ErrorInfo = strError;
                error.ErrorCode = ErrorCodeValue.AccessDenied;
                ErrorInfos.Add(error);
                return -1;
            }

            // 2010/4/8
            // 
            nRet = this.App.SetOperation(
                ref domNew,
                "moved",
                sessioninfo.UserID, // strUserID,
                "",
                out strError);
            if (nRet == -1)
                goto ERROR1;

            string strWarning = "";
            // 合并新旧记录
            // return:
            //      -1  出错
            //      0   正确
            //      1   有部分修改没有兑现。说明在strError中
            string strNewXml = "";
            nRet = MergeTwoItemXml(
                sessioninfo,
                domSourceExist,
                domNew,
                out strNewXml,
                out strError);
            if (nRet == -1)
                goto ERROR1;
            if (nRet == 1)
                strWarning = strError;


            // 移动记录
            byte[] output_timestamp = null;

            // TODO: Copy后还要写一次?因为Copy并不写入新记录。
            // 其实Copy的意义在于带走资源。否则还不如用Save+Delete
            lRet = channel.DoCopyRecord(info.OldRecPath,
                info.NewRecPath,
                true,   // bDeleteSourceRecord
                out output_timestamp,
                out strOutputPath,
                out strError);
            if (lRet == -1)
            {
                strError = "DoCopyRecord() error :" + strError;
                goto ERROR1;
            }

            // Debug.Assert(strOutputPath == info.NewRecPath);
            string strTargetPath = strOutputPath;

            lRet = channel.DoSaveTextRes(strTargetPath,
                strNewXml,
                false,   // include preamble?
                "content",
                output_timestamp,
                out output_timestamp,
                out strOutputPath,
                out strError);
            if (lRet == -1)
            {
                strError = "移动操作中," + this.ItemName + "记录 '" + info.OldRecPath + "' 已经被成功移动到 '" + strTargetPath + "' ,但在写入新内容时发生错误: " + strError;

                if (channel.ErrorCode == ChannelErrorCode.TimestampMismatch)
                {
                    // 不进行反复处理。
                    // 因为源已经移动,情况很复杂
                }

                // 仅仅写入错误日志即可。没有Undo
                this.App.WriteErrorLog(strError);

                error = new EntityInfo(info);
                error.ErrorInfo = "移动操作发生错误:" + strError;
                error.ErrorCode = channel.OriginErrorCode;
                ErrorInfos.Add(error);
                return -1;
            }
            else // 成功
            {
                info.NewRecPath = strOutputPath;    // 兑现保存的位置,因为可能有追加形式的路径

                DomUtil.SetElementText(domOperLog.DocumentElement, "action", "move");

                // 新记录
                XmlNode node = DomUtil.SetElementText(domOperLog.DocumentElement,
                    "record", strNewXml);
                DomUtil.SetAttr(node, "recPath", info.NewRecPath);

                // 旧记录
                node = DomUtil.SetElementText(domOperLog.DocumentElement,
                    "oldRecord", strExistSourceXml);
                DomUtil.SetAttr(node, "recPath", info.OldRecPath);

                // 保存成功,需要返回信息元素。因为需要返回新的时间戳
                error = new EntityInfo(info);
                error.NewTimestamp = output_timestamp;
                error.NewRecord = strNewXml;

                error.ErrorInfo = "移动操作成功。NewRecPath中返回了实际保存的路径, NewTimeStamp中返回了新的时间戳,NewRecord中返回了实际保存的新记录(可能和提交的源记录稍有差异)。";
                if (string.IsNullOrEmpty(strWarning) == false)
                {
                    error.ErrorInfo = "移动操作成功。但" + strWarning;
                    error.ErrorCode = ErrorCodeValue.PartialDenied;
                }
                else
                    error.ErrorCode = ErrorCodeValue.NoError;
                ErrorInfos.Add(error);
            }

            return 0;

        ERROR1:
            error = new EntityInfo(info);
            error.ErrorInfo = strError;
            error.ErrorCode = ErrorCodeValue.CommonError;
            ErrorInfos.Add(error);
            return -1;
        }
Exemplo n.º 2
0
		// 保存记录
		// parameters:
		//		strRecordPath	记录路径。如果==null,表示直接用textBox_recPath中当前的内容作为路径
		public void SaveRecord(string strRecordPath)
		{
			if (strRecordPath != null)
				textBox_recPath.Text = strRecordPath;

			if (textBox_recPath.Text == "")
			{
				MessageBox.Show(this, "路径不能为空");
				return;
			}

			ResPath respath = new ResPath(textBox_recPath.Text);

			Uri uri = null;
			try 
			{
				uri = new Uri(respath.Url);
			}
			catch (Exception ex)
			{
				MessageBox.Show(this, "路径错误: " + ex.Message);
				return;
			}
			// 保存到文件
			if (uri.IsFile)
			{
				MessageBox.Show(this, "暂时不支持保存到文件");
				return;
			}


			string strError;

			string strXml = "";
            bool bHasUploadedFile = false;

			int nRet = GetXmlRecord(out strXml,
                out bHasUploadedFile,
                out strError);
			if (nRet == -1)
			{
				MessageBox.Show(this, strError);
				return;
			}


			byte [] baOutputTimeStamp = null;
			string strOutputPath = "";
			long lRet = 0;

            int nUploadCount = 0;

			// 使用Channel
			RmsChannel channelSave = channel;

			channel = Channels.GetChannel(respath.Url);
			Debug.Assert(channel != null, "Channels.GetChannel 异常");

			try 
			{

                stop.OnStop += new StopEventHandler(this.DoStop);
				stop.Initial("正在保存记录 " + respath.FullPath);
				stop.BeginLoop();


				EnableControlsInLoading(true);

				//string strTemp = ByteArray.GetHexTimeStampString(this.TimeStamp);

                if (String.IsNullOrEmpty(this.strDatabaseOriginPath) == false
                    && bHasUploadedFile == true
                    && respath.FullPath != this.strDatabaseOriginPath)
                {
                    ResPath respath_old = new ResPath(this.strDatabaseOriginPath);

                    if (respath_old.Url != respath.Url)
                    {
                        MessageBox.Show(this, "目前暂不支持跨服务器情况下的资源复制。本记录中原有的已上载资源,在另存到目标库的时丢失(为空),请注意保存完后手动上载。");
                        goto SKIPCOPYRECORD;
                    }
                    // 复制记录
                    // return:
                    //		-1	出错。错误信息在strError中
                    //		0或者其他		成功
                    nRet = channel.DoCopyRecord(respath_old.Path,
                        respath.Path,
                        false,  // bool bDeleteOriginRecord,
                        out baOutputTimeStamp,
                        out strOutputPath,
                        out strError);
                    if (nRet == -1)
                    {
                        MessageBox.Show(this, "复制资源时发生错误: " + strError);
                    }
                    else
                    {
                        // 为继续保存最新XML记录作准备
                        respath.Path = strOutputPath;   // ?形式路径其实已经确定
                        this.TimeStamp = baOutputTimeStamp;
                    }
                }
                SKIPCOPYRECORD:

				lRet = channel.DoSaveTextRes(respath.Path,
					strXml,
					false,	// bInlucdePreamble
					"",	// style
					this.TimeStamp,
					out baOutputTimeStamp,
					out strOutputPath,
					out strError);


				EnableControlsInLoading(false);

				stop.EndLoop();
                stop.OnStop -= new StopEventHandler(this.DoStop);
				stop.Initial("");


				if (lRet == -1) 
				{
					MessageBox.Show(this, "保存记录失败,原因: "+strError);
					return;
                }

                //


                this.TimeStamp = baOutputTimeStamp;
                respath.Path = strOutputPath;
                textBox_recPath.Text = respath.FullPath;

                ////
                this.strDatabaseOriginPath = respath.Url + "?" + strOutputPath; // 保存从数据库中来的原始path

                stop.OnStop += new StopEventHandler(this.DoStop);
                stop.Initial("正在保存资源 " + respath.FullPath);
                stop.BeginLoop();

                EnableControlsInLoading(true);
                Debug.Assert(channel != null, "");
                // 保存对象资源,循环对象列表就可以了
                nUploadCount = this.listView_resFiles.DoUpload(
                    respath.Path,
                    channel,
                    stop,
                    out strError);
                EnableControlsInLoading(false);

                stop.EndLoop();
                stop.OnStop -= new StopEventHandler(this.DoStop);
                stop.Initial("");

            }
            finally
            {
                channel = channelSave;
            }

			if (nUploadCount == -1) 
			{
				MessageBox.Show(this, "XML记录保存成功, 但保存资源失败,原因: "+strError);
				return;
			}

			if (nUploadCount > 0)
			{
				// 使用Channel
				channelSave = channel;

				channel = Channels.GetChannel(respath.Url);
				Debug.Assert(channel != null, "Channels.GetChannel 异常");


				// 需要重新获得时间戳
				string strStyle = "timestamp,metadata";	// withresmetadata
				string strMetaData = "";
				string strContent = "";

				try 
				{
					lRet = channel.GetRes(respath.Path,
						strStyle,
						out strContent,
						out strMetaData,
						out baOutputTimeStamp,
						out strOutputPath,
						out strError);
					if (lRet == -1)
					{
						MessageBox.Show(this, "重新获得时间戳 '" + respath.FullPath + "' 失败。原因 : " + strError);
						return;
					}
				}
				finally 
				{
					channel = channelSave;
				}
				this.TimeStamp = baOutputTimeStamp;	// 设置时间戳很重要。即便xml不合法,也应设置好时间戳,否则窗口无法进行正常删除。
				this.m_strMetaData = strMetaData;	// 记忆XML记录的元数据



			}

			this.Changed = false;

			MessageBox.Show(this, "保存记录 '" + respath.FullPath + "' 成功。");
		}
Exemplo n.º 3
0
        // 复制属于同一书目记录的全部实体记录
        // parameters:
        //      strAction   copy / move
        // return:
        //      -2  目标实体库不存在,无法进行复制或者删除
        //      -1  error
        //      >=0  实际复制或者移动的实体记录数
        public int CopyBiblioChildItems(RmsChannel channel,
            string strAction,
            List<DeleteEntityInfo> entityinfos,
            string strTargetBiblioRecPath,
            XmlDocument domOperLog,
            out string strError)
        {
            strError = "";

            if (entityinfos == null || entityinfos.Count == 0)
                return 0;

            int nOperCount = 0;

            XmlNode root = null;

            if (domOperLog != null)
            {
                root = domOperLog.CreateElement(strAction == "copy" ? "copy" + this.ItemNameInternal + "Records" : "move" + this.ItemNameInternal + "Records");
                domOperLog.DocumentElement.AppendChild(root);
            }

            // 获得目标书目库下属的实体库名
            string strTargetItemDbName = "";
            string strTargetBiblioDbName = ResPath.GetDbName(strTargetBiblioRecPath);
            // return:
            //      -1  出错
            //      0   没有找到
            //      1   找到
            int nRet = this.GetItemDbName(strTargetBiblioDbName,
                out strTargetItemDbName,
                out strError);
            if (nRet == 0 || string.IsNullOrEmpty(strTargetItemDbName) == true)
            {
                return -2;   // 目标实体库不存在
            }

            string strParentID = ResPath.GetRecordId(strTargetBiblioRecPath);
            if (string.IsNullOrEmpty(strParentID) == true)
            {
                strError = "目标书目记录路径 '" + strTargetBiblioRecPath + "' 不正确,无法获得记录号";
                return -1;
            }



            List<string> newrecordpaths = new List<string>();
            List<string> oldrecordpaths = new List<string>();
            for (int i = 0; i < entityinfos.Count; i++)
            {
                DeleteEntityInfo info = entityinfos[i];

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

                // this.EntityLocks.LockForWrite(info.ItemBarcode);
                try
                {
                    XmlDocument dom = new XmlDocument();
                    try
                    {
                        dom.LoadXml(info.OldRecord);
                    }
                    catch (Exception ex)
                    {
                        strError = "记录 '" + info.RecPath + "' 装入XMLDOM发生错误: " + ex.Message;
                        goto ERROR1;
                    }
                    DomUtil.SetElementText(dom.DocumentElement,
                        "parent",
                        strParentID);

                    // 复制的情况
                    if (strAction == "copy")
                    {
                        // 避免refID重复
                        DomUtil.SetElementText(dom.DocumentElement,
                            "refID",
                            null);
                    }

                    long lRet = channel.DoCopyRecord(info.RecPath,
                         strTargetItemDbName + "/?",
                         strAction == "move" ? true : false,   // bDeleteSourceRecord
                         out output_timestamp,
                         out strOutputRecPath,
                         out strError);
                    if (lRet == -1)
                    {
                        if (channel.ErrorCode == ChannelErrorCode.NotFound)
                            continue;
                        strError = "复制" + this.ItemName + "记录 '" + info.RecPath + "' 时发生错误: " + strError;
                        goto ERROR1;
                    }

                    // 2011/5/24
                    // 修改xml记录。<parent>元素发生了变化
                    byte[] baOutputTimestamp = null;
                    string strOutputRecPath1 = "";
                    lRet = channel.DoSaveTextRes(strOutputRecPath,
                        dom.OuterXml,
                        false,
                        "content", // ,ignorechecktimestamp
                        output_timestamp,
                        out baOutputTimestamp,
                        out strOutputRecPath1,
                        out strError);
                    if (lRet == -1)
                        goto ERROR1;

                    oldrecordpaths.Add(info.RecPath);
                    newrecordpaths.Add(strOutputRecPath);
                }
                finally
                {
                    // this.EntityLocks.UnlockForWrite(info.ItemBarcode);
                }

                // 增补到日志DOM中
                if (domOperLog != null)
                {
                    Debug.Assert(root != null, "");

                    XmlNode node = domOperLog.CreateElement("record");
                    root.AppendChild(node);

                    DomUtil.SetAttr(node, "recPath", info.RecPath);
                    DomUtil.SetAttr(node, "targetRecPath", strOutputRecPath);
                }

                nOperCount++;
            }


            return nOperCount;
        ERROR1:
            // Undo已经进行过的操作
            if (strAction == "copy")
            {
                string strWarning = "";

                foreach (string strRecPath in newrecordpaths)
                {
                    string strTempError = "";
                    byte[] timestamp = null;
                    byte[] output_timestamp = null;
                REDO_DELETE:
                    long lRet = channel.DoDeleteRes(strRecPath,
                        timestamp,
                        out output_timestamp,
                        out strTempError);
                    if (lRet == -1)
                    {
                        if (channel.ErrorCode == ChannelErrorCode.TimestampMismatch)
                        {
                            if (timestamp == null)
                            {
                                timestamp = output_timestamp;
                                goto REDO_DELETE;
                            }
                        }
                        strWarning += strTempError + ";";
                    }

                }
                if (string.IsNullOrEmpty(strWarning) == false)
                    strError = strError + "。在Undo过程中,又遇到出错: " + strWarning;
            }
            else if (strAction == "move")
            {
                string strWarning = "";
                for (int i = 0; i < newrecordpaths.Count; i++)
                {
                    byte[] output_timestamp = null;
                    string strOutputRecPath = "";
                    string strTempError = "";
                    long lRet = channel.DoCopyRecord(newrecordpaths[i],
         oldrecordpaths[i],
         true,   // bDeleteSourceRecord
         out output_timestamp,
         out strOutputRecPath,
         out strTempError);
                    if (lRet == -1)
                    {
                        strWarning += strTempError + ";";
                    }
                }
                if (string.IsNullOrEmpty(strWarning) == false)
                    strError = strError + "。在Undo过程中,又遇到出错: " + strWarning;
            }
            return -1;
        }
Exemplo n.º 4
0
        // 移动或者复制书目记录
        // strExistingXml和请求中传来的old xml的时间戳比较,在本函数外、调用前进行
        // parameters:
        //      strAction   动作。为"onlycopybiblio" "onlymovebiblio"之一。增加 copy / move
        //      strNewBiblio    需要在目标记录中更新的内容。如果 == null,表示不特意更新
        //      strMergeStyle   如何合并两条记录的元数据部分? reserve_source / reserve_target。 空表示 reserve_source
        int DoBiblioOperMove(
            string strAction,
            SessionInfo sessioninfo,
            RmsChannel channel,
            string strOldRecPath,
            string strExistingSourceXml,
            // byte[] baExistingSourceTimestamp, // 请求中提交过来的时间戳
            string strNewRecPath,
            string strNewBiblio,    // 已经经过Merge预处理的新记录XML
            string strMergeStyle,
            out string strOutputTargetXml,
            out byte[] baOutputTimestamp,
            out string strOutputRecPath,
            out string strError)
        {
            strError = "";
            long lRet = 0;
            baOutputTimestamp = null;
            strOutputRecPath = "";

            strOutputTargetXml = ""; // 最后保存成功的记录

            // 检查路径
            if (strOldRecPath == strNewRecPath)
            {
                strError = "当action为\"" + strAction + "\"时,strNewRecordPath路径 '" + strNewRecPath + "' 和strOldRecPath '" + strOldRecPath + "' 必须不相同";
                goto ERROR1;
            }

            if (String.IsNullOrEmpty(strNewRecPath) == true)
            {
                strError = "DoBiblioOperMove() strNewRecPath参数值不能为空";
                goto ERROR1;
            }

            // 检查即将覆盖的目标位置是不是有记录,如果有,则不允许进行move操作。
            bool bAppendStyle = false;  // 目标路径是否为追加形态?
            string strTargetRecId = ResPath.GetRecordId(strNewRecPath);
            string strExistTargetXml = "";

            if (strTargetRecId == "?" || String.IsNullOrEmpty(strTargetRecId) == true)
            {
                // 2009/11/1 
                if (String.IsNullOrEmpty(strTargetRecId) == true)
                    strNewRecPath += "/?";

                bAppendStyle = true;
            }


            string strOutputPath = "";
            string strMetaData = "";

            if (bAppendStyle == false)
            {
                byte[] exist_target_timestamp = null;

                // 获取覆盖目标位置的现有记录
                lRet = channel.GetRes(strNewRecPath,
                    out strExistTargetXml,
                    out strMetaData,
                    out exist_target_timestamp,
                    out strOutputPath,
                    out strError);
                if (lRet == -1)
                {
                    if (channel.ErrorCode == ChannelErrorCode.NotFound)
                    {
                        // 如果记录不存在, 说明不会造成覆盖态势
                        /*
                        strExistSourceXml = "<root />";
                        exist_source_timestamp = null;
                        strOutputPath = info.NewRecPath;
                         * */
                    }
                    else
                    {
                        strError = "移动操作发生错误, 在读入即将覆盖的目标位置 '" + strNewRecPath + "' 原有记录阶段:" + strError;
                        goto ERROR1;
                    }
                }
                else
                {
#if NO
                    // 如果记录存在,则目前不允许这样的操作
                    strError = "移动(move)操作被拒绝。因为在即将覆盖的目标位置 '" + strNewRecPath + "' 已经存在书目记录。请先删除(delete)这条记录,再进行移动(move)操作";
                    goto ERROR1;
#endif
                }
            }

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

            XmlDocument domSourceExist = new XmlDocument();
            XmlDocument domNew = new XmlDocument();

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

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

            // 只有order权限的情况
            if (StringUtil.IsInList("setbiblioinfo", sessioninfo.RightsOrigin) == false
                && StringUtil.IsInList("order", sessioninfo.RightsOrigin) == true)
            {
                if (strAction == "onlymovebiblio"
                    || strAction == "move")
                {
                    string strSourceDbName = ResPath.GetDbName(strOldRecPath);
                    // 源头书目库为 非工作库 情况
                    if (IsOrderWorkBiblioDb(strSourceDbName) == false)
                    {
                        // 非工作库不能删除记录
                        if (IsOrderWorkBiblioDb(strSourceDbName) == false)
                        {
                            // 非工作库。要求原来记录不存在
                            strError = "当前帐户只有order权限而没有setbiblioinfo权限,不能用" + strAction + "功能删除源书目记录 '" + strOldRecPath + "'";
                            goto ERROR1;
                        }
                    }
                }
            }

            // 移动记录
            byte[] output_timestamp = null;
            string strIdChangeList = "";

            // TODO: Copy后还要写一次?因为Copy并不写入新记录。
            // 其实Copy的意义在于带走资源。否则还不如用Save+Delete
            lRet = channel.DoCopyRecord(strOldRecPath,
                 strNewRecPath,
                 strAction == "onlymovebiblio" || strAction == "move" ? true : false,   // bDeleteSourceRecord
                 strMergeStyle,
                 out strIdChangeList,
                 out output_timestamp,
                 out strOutputRecPath,
                 out strError);
            if (lRet == -1)
            {
                strError = "DoCopyRecord() error :" + strError;
                goto ERROR1;
            }

            // TODO: 兑现对 856 字段的合并,和来自源的 856 字段的 $u 修改

            if (String.IsNullOrEmpty(strNewBiblio) == false)
            {
                this.BiblioLocks.LockForWrite(strOutputRecPath);

                try
                {
                    // TODO: 如果新的、已存在的xml没有不同,或者新的xml为空,则这步保存可以省略
                    string strOutputBiblioRecPath = "";
                    lRet = channel.DoSaveTextRes(strOutputRecPath,
                        strNewBiblio,
                        false,
                        "content", // ,ignorechecktimestamp
                        output_timestamp,
                        out baOutputTimestamp,
                        out strOutputBiblioRecPath,
                        out strError);
                    if (lRet == -1)
                        goto ERROR1;
                }
                finally
                {
                    this.BiblioLocks.UnlockForWrite(strOutputRecPath);
                }
            }

            {
                // TODO: 是否和前面一起锁定?
                byte[] exist_target_timestamp = null;

                // 获取最后的记录
                lRet = channel.GetRes(strOutputRecPath,
                    out strOutputTargetXml,
                    out strMetaData,
                    out exist_target_timestamp,
                    out strOutputPath,
                    out strError);
            }

            return 0;
        ERROR1:
            return -1;
        }
Exemplo n.º 5
0
        // 复制属于同一书目记录的全部实体记录
        // parameters:
        //      strAction   copy / move
        // return:
        //      -1  error
        //      >=0  实际复制或者移动的实体记录数
        public int CopyBiblioChildRecords(RmsChannel channel,
            string strAction,
            List<DeleteEntityInfo> entityinfos,
            List<string> target_recpaths,
            string strTargetBiblioRecPath,
            List<string> newbarcodes,
            out string strError)
        {
            strError = "";

            if (entityinfos == null || entityinfos.Count == 0)
                return 0;

            if (entityinfos.Count != target_recpaths.Count)
            {
                strError = "entityinfos.Count (" + entityinfos.Count.ToString() + ") != target_recpaths.Count (" + target_recpaths.Count .ToString()+ ")";
                return -1;
            }

            int nOperCount = 0;

            string strParentID = ResPath.GetRecordId(strTargetBiblioRecPath);
            if (string.IsNullOrEmpty(strParentID) == true)
            {
                strError = "目标书目记录路径 '" + strTargetBiblioRecPath + "' 不正确,无法获得记录号";
                return -1;
            }

            List<string> newrecordpaths = new List<string>();
            List<string> oldrecordpaths = new List<string>();
            List<string> parentids = new List<string>();
            List<string> oldrecords = new List<string>();

            for (int i = 0; i < entityinfos.Count; i++)
            {
                DeleteEntityInfo info = entityinfos[i];
                string strTargetRecPath = target_recpaths[i];

                string strNewBarcode = newbarcodes[i];

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

                this.EntityLocks.LockForWrite(info.ItemBarcode);
                try
                {
                    XmlDocument dom = new XmlDocument();
                    try
                    {
                        dom.LoadXml(info.OldRecord);
                    }
                    catch (Exception ex)
                    {
                        strError = "记录 '" + info.RecPath + "' 装入XMLDOM发生错误: " + ex.Message;
                        goto ERROR1;
                    }
                    DomUtil.SetElementText(dom.DocumentElement,
                        "parent",
                        strParentID);

                    // 复制的情况,要避免出现操作后的条码号重复现象
                    if (strAction == "copy")
                    {
                        // 修改册条码号,避免发生条码号重复
                        string strOldItemBarcode = DomUtil.GetElementText(dom.DocumentElement,
                            "barcode");
                        if (String.IsNullOrEmpty(strOldItemBarcode) == false)
                        {
                            // 2014/1/5
                            if (string.IsNullOrEmpty(strNewBarcode) == true)
                                strNewBarcode = "temp_" + strOldItemBarcode;
                            DomUtil.SetElementText(dom.DocumentElement,
                                "barcode",
                                strNewBarcode);
                        }

                        // 2014/1/5
                        DomUtil.SetElementText(dom.DocumentElement,
                            "refID",
                            null);

                        // 把借者清除
                        // (源实体记录中如果有借阅信息,在普通界面上是无法删除此记录的。只能用出纳窗正规进行归还,然后才能删除)
                        {
                            DomUtil.SetElementText(dom.DocumentElement,
                                "borrower",
                                null);
                            DomUtil.SetElementText(dom.DocumentElement,
                                "borrowPeriod",
                                null);
                            DomUtil.SetElementText(dom.DocumentElement,
                                "borrowDate",
                                null);
                        }
                    }

                    // TODO: 可以顺便确认有没有对象资源。如果没有,就省略CopyRecord操作

                    long lRet = channel.DoCopyRecord(info.RecPath,
                         strTargetRecPath,
                         strAction == "move" ? true : false,   // bDeleteSourceRecord
                         out output_timestamp,
                         out strOutputRecPath,
                         out strError);
                    if (lRet == -1)
                    {
                        if (channel.ErrorCode == ChannelErrorCode.NotFound)
                            continue;
                        strError = "复制实体记录 '" + info.RecPath + "' 时发生错误: " + strError;
                        goto ERROR1;
                    }



                    // 修改xml记录。<parent>元素发生了变化
                    byte[] baOutputTimestamp = null;
                    string strOutputRecPath1 = "";
                    lRet = channel.DoSaveTextRes(strOutputRecPath,
                        dom.OuterXml,
                        false,
                        "content", // ,ignorechecktimestamp
                        output_timestamp,
                        out baOutputTimestamp,
                        out strOutputRecPath1,
                        out strError);
                    if (lRet == -1)
                        goto ERROR1;

                    oldrecordpaths.Add(info.RecPath);
                    newrecordpaths.Add(strOutputRecPath);
                    parentids.Add(strParentID);
                    if (strAction == "move")
                        oldrecords.Add(info.OldRecord);
                }
                finally
                {
                    this.EntityLocks.UnlockForWrite(info.ItemBarcode);
                }

                nOperCount++;
            }

            return nOperCount;
        ERROR1:
            // 不要Undo
            return -1;
        }
Exemplo n.º 6
0
        // 复制属于同一书目记录的全部实体记录
        // parameters:
        //      strAction   copy / move
        // return:
        //      -2  目标实体库不存在,无法进行复制或者删除
        //      -1  error
        //      >=0  实际复制或者移动的实体记录数
        public int CopyBiblioChildEntities(RmsChannel channel,
            string strAction,
            List<DeleteEntityInfo> entityinfos,
            string strTargetBiblioRecPath,
            XmlDocument domOperLog,
            out string strError)
        {
            strError = "";

            if (entityinfos == null || entityinfos.Count == 0)
                return 0;

            int nOperCount = 0;

            XmlNode root = null;

            if (domOperLog != null)
            {
                root = domOperLog.CreateElement(strAction == "copy" ? "copyEntityRecords" : "moveEntityRecords");
                domOperLog.DocumentElement.AppendChild(root);
            }

            // 获得目标书目库下属的实体库名
            string strTargetItemDbName = "";
            string strTargetBiblioDbName = ResPath.GetDbName(strTargetBiblioRecPath);
            // return:
            //      -1  出错
            //      0   没有找到
            //      1   找到
            int nRet = this.GetItemDbName(strTargetBiblioDbName,
                out strTargetItemDbName,
                out strError);
            if (nRet == 0 || string.IsNullOrEmpty(strTargetItemDbName) == true)
            {
                return -2;   // 目标实体库不存在
            }

            string strParentID = ResPath.GetRecordId(strTargetBiblioRecPath);
            if (string.IsNullOrEmpty(strParentID) == true)
            {
                strError = "目标书目记录路径 '"+strTargetBiblioRecPath+"' 不正确,无法获得记录号";
                return -1;
            }

            List<string> newrecordpaths = new List<string>();
            List<string> oldrecordpaths = new List<string>();
            List<string> parentids = new List<string>();
            List<string> oldrecords = new List<string>();

            for (int i = 0; i < entityinfos.Count; i++)
            {
                DeleteEntityInfo info = entityinfos[i];

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

                string strNewBarcode = "";  // 复制中修改后的册条码号

                this.EntityLocks.LockForWrite(info.ItemBarcode);
                try
                {
                    XmlDocument dom = new XmlDocument();
                    try
                    {
                        dom.LoadXml(info.OldRecord);
                    }
                    catch (Exception ex)
                    {
                        strError = "记录 '" + info.RecPath + "' 装入XMLDOM发生错误: " + ex.Message;
                        goto ERROR1;
                    }
                    DomUtil.SetElementText(dom.DocumentElement,
                        "parent",
                        strParentID);

                    // 复制的情况,要避免出现操作后的条码号重复现象
                    if (strAction == "copy")
                    {
                        // 修改册条码号,避免发生条码号重复
                        string strOldItemBarcode = DomUtil.GetElementText(dom.DocumentElement,
                            "barcode");
                        if (String.IsNullOrEmpty(strOldItemBarcode) == false)
                        {
                            strNewBarcode = strOldItemBarcode + "_" + Guid.NewGuid().ToString();
                            DomUtil.SetElementText(dom.DocumentElement,
                                "barcode",
                                strNewBarcode);
                        }

                        // *** 后面这几个清除动作要作为规则出现

                        // 清除 refid
                        DomUtil.SetElementText(dom.DocumentElement,
                            "refID",
                            null);


                        // 把借者清除
                        // (源实体记录中如果有借阅信息,在普通界面上是无法删除此记录的。只能用出纳窗正规进行归还,然后才能删除)
                        {
                            DomUtil.SetElementText(dom.DocumentElement,
                                "borrower",
                                null);
                            DomUtil.SetElementText(dom.DocumentElement,
                                "borrowPeriod",
                                null);
                            DomUtil.SetElementText(dom.DocumentElement,
                                "borrowDate",
                                null);
                        }
                    }

                    // TODO: 可以顺便确认有没有对象资源。如果没有,就省略CopyRecord操作

                    long lRet = channel.DoCopyRecord(info.RecPath,
                         strTargetItemDbName + "/?",
                         strAction == "move" ? true : false,   // bDeleteSourceRecord
                         out output_timestamp,
                         out strOutputRecPath,
                         out strError);
                    if (lRet == -1)
                    {
                        if (channel.ErrorCode == ChannelErrorCode.NotFound)
                            continue;
                        strError = "复制实体记录 '" + info.RecPath + "' 时发生错误: " + strError;
                        goto ERROR1;
                    }



                    // 修改xml记录。<parent>元素发生了变化
                    byte[] baOutputTimestamp = null;
                    string strOutputRecPath1 = "";
                    lRet = channel.DoSaveTextRes(strOutputRecPath,
                        dom.OuterXml,
                        false,
                        "content", // ,ignorechecktimestamp
                        output_timestamp,
                        out baOutputTimestamp,
                        out strOutputRecPath1,
                        out strError);
                    if (lRet == -1)
                        goto ERROR1;

                    oldrecordpaths.Add(info.RecPath);
                    newrecordpaths.Add(strOutputRecPath);
                    parentids.Add(strParentID);
                    if (strAction == "move")
                        oldrecords.Add(info.OldRecord);
                }
                finally
                {
                    this.EntityLocks.UnlockForWrite(info.ItemBarcode);
                }

                // 增补到日志DOM中
                if (domOperLog != null)
                {
                    Debug.Assert(root != null, "");

                    XmlNode node = domOperLog.CreateElement("record");
                    root.AppendChild(node);

                    DomUtil.SetAttr(node, "recPath", info.RecPath);
                    DomUtil.SetAttr(node, "targetRecPath", strOutputRecPath);

                    // 2014/1/5
                    if (string.IsNullOrEmpty(strNewBarcode) == false)
                        DomUtil.SetAttr(node, "newBarcode", strNewBarcode);
                }

                nOperCount++;
            }


            return nOperCount;
        ERROR1:
            // Undo已经进行过的操作
            if (strAction == "copy")
            {
                string strWarning = "";

                foreach (string strRecPath in newrecordpaths)
                {
                    string strTempError = "";
                    byte[] timestamp = null;
                    byte[] output_timestamp = null;
                REDO_DELETE:
                    long lRet = channel.DoDeleteRes(strRecPath,
                        timestamp,
                        out output_timestamp,
                        out strTempError);
                    if (lRet == -1)
                    {
                        if (channel.ErrorCode == ChannelErrorCode.TimestampMismatch)
                        {
                            if (timestamp == null)
                            {
                                timestamp = output_timestamp;
                                goto REDO_DELETE;
                            }
                        }
                        strWarning += strTempError + ";";
                    }

                }
                if (string.IsNullOrEmpty(strWarning) == false)
                    strError = strError + "。在Undo过程中,又遇到出错: " + strWarning;
            }
            else if (strAction == "move")
            {
                string strWarning = "";
                for (int i = 0; i < newrecordpaths.Count; i++)
                {
                    byte[] output_timestamp = null;
                    string strOutputRecPath = "";
                    string strTempError = "";
                    // TODO: 如果确认没有对象,就可以省略这一步
                    long lRet = channel.DoCopyRecord(newrecordpaths[i],
         oldrecordpaths[i],
         true,   // bDeleteSourceRecord
         out output_timestamp,
         out strOutputRecPath,
         out strTempError);
                    if (lRet == -1)
                    {
                        strWarning += strTempError + ";";
                    }

                    // 修改xml记录。<parent>元素发生了变化
                    byte[] baOutputTimestamp = null;
                    string strOutputRecPath1 = "";
                    lRet = channel.DoSaveTextRes(oldrecordpaths[i],
                        oldrecords[i],
                        false,
                        "content", // ,ignorechecktimestamp
                        output_timestamp,
                        out baOutputTimestamp,
                        out strOutputRecPath1,
                        out strTempError);
                    if (lRet == -1)
                    {
                        strWarning += strTempError + ";";
                    }
                }
                if (string.IsNullOrEmpty(strWarning) == false)
                    strError = strError + "。在Undo过程中,又遇到出错: " + strWarning;
            }
            return -1;
        }
Exemplo n.º 7
0
        // 执行SetEntities API中的"move"操作
        // 1) 操作成功后, NewRecord中有实际保存的新记录,NewTimeStamp为新的时间戳
        // 2) 如果返回TimeStampMismatch错,则OldRecord中有库中发生变化后的“原记录”,OldTimeStamp是其时间戳
        // return:
        //      -1  出错
        //      0   成功
        int DoEntityOperMove(
            string strStyle,
            SessionInfo sessioninfo,
            RmsChannel channel,
            EntityInfo info,
            ref XmlDocument domOperLog,
            ref List<EntityInfo> ErrorInfos)
        {
            // int nRedoCount = 0;
            EntityInfo error = null;
            bool bExist = true;    // info.RecPath所指的记录是否存在?

            int nRet = 0;
            long lRet = 0;

            string strError = "";

            // 检查路径
            if (info.OldRecPath == info.NewRecPath)
            {
                strError = "当action为\"move\"时,info.NewRecordPath路径 '" + info.NewRecPath + "' 和info.OldRecPath '" + info.OldRecPath + "' 必须不相同";
                goto ERROR1;
            }

            // 检查即将覆盖的目标位置是不是有记录,如果有,则不允许进行move操作。
            // 如果要进行带覆盖目标位置记录功能的move操作,前端可以先执行一个delete操作,然后再执行move操作。
            // 这样规定,是为了避免过于复杂的判断逻辑,也便于前端操作者清楚操作的后果。
            // 因为如果允许move带有覆盖目标记录功能,则被覆盖的记录的预删除操作,等于进行了一次注销,但这个效用不明显,对前端操作人员准确判断事态并对后果负责(而且可能这种注销需要额外的操作权限),不利
            bool bAppendStyle = false;  // 目标路径是否为追加形态?
            string strTargetRecId = ResPath.GetRecordId(info.NewRecPath);

            if (strTargetRecId == "?" || String.IsNullOrEmpty(strTargetRecId) == true)
                bAppendStyle = true;

            string strOutputPath = "";
            string strMetaData = "";


            if (bAppendStyle == false)
            {
                string strExistTargetXml = "";
                byte[] exist_target_timestamp = null;

                // 获取覆盖目标位置的现有记录
                lRet = channel.GetRes(info.NewRecPath,
                    out strExistTargetXml,
                    out strMetaData,
                    out exist_target_timestamp,
                    out strOutputPath,
                    out strError);
                if (lRet == -1)
                {
                    if (channel.ErrorCode == ChannelErrorCode.NotFound)
                    {
                        // 如果记录不存在, 说明不会造成覆盖态势
                        /*
                        strExistSourceXml = "<root />";
                        exist_source_timestamp = null;
                        strOutputPath = info.NewRecPath;
                         * */
                    }
                    else
                    {
                        error = new EntityInfo(info);
                        error.ErrorInfo = "移动操作发生错误, 在读入即将覆盖的目标位置 '" + info.NewRecPath + "' 原有记录阶段:" + strError;
                        error.ErrorCode = channel.OriginErrorCode;
                        ErrorInfos.Add(error);
                        return -1;
                    }
                }
                else
                {
                    // 如果记录存在,则目前不允许这样的操作
                    strError = "移动(move)操作被拒绝。因为在即将覆盖的目标位置 '" + info.NewRecPath + "' 已经存在册记录。请先删除(delete)这条记录,再进行移动(move)操作";
                    goto ERROR1;
                }
            }


            string strExistSourceXml = "";
            byte[] exist_source_timestamp = null;

            // 先读出数据库中源位置的已有记录
            // REDOLOAD:

            lRet = channel.GetRes(info.OldRecPath,
                out strExistSourceXml,
                out strMetaData,
                out exist_source_timestamp,
                out strOutputPath,
                out strError);
            if (lRet == -1)
            {
                if (channel.ErrorCode == ChannelErrorCode.NotFound)
                {
                    /*
                    // 如果记录不存在, 则构造一条空的记录
                    bExist = false;
                    strExistSourceXml = "<root />";
                    exist_source_timestamp = null;
                    strOutputPath = info.NewRecPath;
                     * */
                    // 这种情况如果放宽,会有严重的副作用,所以不让放宽
                    strError = "移动(move)操作的源记录 '" + info.OldRecPath + "' 在数据库中不存在,所以无法进行移动操作。";
                    goto ERROR1;
                }
                else
                {
                    error = new EntityInfo(info);
                    error.ErrorInfo = "移动(move)操作发生错误, 在读入库中原有源记录(路径在info.OldRecPath) '" + info.OldRecPath + "' 阶段:" + strError;
                    error.ErrorCode = channel.OriginErrorCode;
                    ErrorInfos.Add(error);
                    return -1;
                }
            }

            // 把两个记录装入DOM

            XmlDocument domSourceExist = new XmlDocument();
            XmlDocument domNew = new XmlDocument();

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

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


            // 观察时间戳是否发生变化
            nRet = ByteArray.Compare(info.OldTimestamp, exist_source_timestamp);
            if (nRet != 0)
            {
                // 时间戳不相等了
                // 需要把info.OldRecord和strExistXml进行比较,看看和册登录有关的元素(要害元素)值是否发生了变化。
                // 如果这些要害元素并未发生变化,就继续进行合并、覆盖保存操作

                XmlDocument domOld = new XmlDocument();

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

                // 比较两个记录, 看看和册登录有关的字段是否发生了变化
                // return:
                //      0   没有变化
                //      1   有变化
                nRet = IsRegisterInfoChanged(domOld,
                    domSourceExist);
                if (nRet == 1)
                {
                    error = new EntityInfo(info);
                    // 错误信息中, 返回了修改过的原记录和新时间戳
                    error.OldRecord = strExistSourceXml;
                    error.OldTimestamp = exist_source_timestamp;

                    if (bExist == false)
                        error.ErrorInfo = "移动操作发生错误: 数据库中的原记录 (路径为'" + info.OldRecPath + "') 已被删除。";
                    else
                        error.ErrorInfo = "移动操作发生错误: 数据库中的原记录 (路径为'" + info.OldRecPath + "') 已发生过修改";
                    error.ErrorCode = ErrorCodeValue.TimestampMismatch;
                    ErrorInfos.Add(error);
                    return -1;
                }

                // exist_source_timestamp此时已经反映了库中被修改后的记录的时间戳
            }

            string strSourceLibraryCode = "";
            // 检查一个册记录的馆藏地点是否符合馆代码列表要求
            // return:
            //      -1  检查过程出错
            //      0   符合要求
            //      1   不符合要求
            nRet = CheckItemLibraryCode(domSourceExist,
                sessioninfo,
                        // sessioninfo.LibraryCodeList,
                        out strSourceLibraryCode,
                        out strError);
            if (nRet == -1)
                goto ERROR1;


            // 检查旧记录是否属于管辖范围
            if (sessioninfo.GlobalUser == false
                || sessioninfo.UserType == "reader")
            {
                if (nRet != 0)
                {
                    strError = "即将被移动的册记录其馆藏地点不符合要求: " + strError;
                    goto ERROR1;
                }
            }

            bool bNoOperations = false; // 是否为不要覆盖<operations>内容
            if (StringUtil.IsInList("nooperations", strStyle) == true)
            {
                bNoOperations = true;
            }

            if (bNoOperations == false)
            {
                // 2010/4/8
                // 
                nRet = SetOperation(
    ref domNew,
    "moved",
    sessioninfo.UserID,
    "",
    out strError);
                if (nRet == -1)
                    goto ERROR1;
            }


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


            // 只有order权限的情况
            if (StringUtil.IsInList("setiteminfo", sessioninfo.RightsOrigin) == false
                && StringUtil.IsInList("setentities", sessioninfo.RightsOrigin) == false
                && StringUtil.IsInList("order", sessioninfo.RightsOrigin) == true)
            {
                // 2009/11/26 changed
                string strEntityDbName = ResPath.GetDbName(info.OldRecPath);
                if (String.IsNullOrEmpty(strEntityDbName) == true)
                {
                    strError = "从路径 '" + info.OldRecPath + "' 中获得数据库名时失败";
                    goto ERROR1;
                }

                string strBiblioDbName = "";

                // 根据实体库名, 找到对应的书目库名
                // 注意,返回1的时候,strBiblioDbName也有可能为空
                // return:
                //      -1  出错
                //      0   没有找到
                //      1   找到
                nRet = GetBiblioDbNameByItemDbName(strEntityDbName,
                out strBiblioDbName,
                out strError);
                if (nRet == 0 || nRet == -1)
                {
                    strError = "根据实体库名 '" + strEntityDbName + "' 中获得书目库名时失败";
                    goto ERROR1;
                }

                // 非工作库
                if (IsOrderWorkBiblioDb(strBiblioDbName) == false)
                {
                    // 非工作库。要求<state>包含“加工中”
                    string strState = DomUtil.GetElementText(domSourceExist.DocumentElement,
                        "state");
                    if (IncludeStateProcessing(strState) == false)
                    {
                        strError = "当前帐户只有order权限而没有setiteminfo(或setentities)权限,不能用move功能删除从属于非工作库的、状态不包含“加工中”的实体记录 '" + info.OldRecPath + "'";
                        goto ERROR1;
                    }
                }

                // TODO: 如果原样移动,目标记录并不被修改,似乎也该允许?
            }

            // 移动记录
            byte[] output_timestamp = null;

            // TODO: Copy后还要写一次?因为Copy并不写入新记录。(注:Copy/Move有时候会跨库,这样记录中<parent>需要改变)
            // 其实Copy的意义在于带走资源。否则还不如用Save+Delete
            lRet = channel.DoCopyRecord(info.OldRecPath,
                info.NewRecPath,
                true,   // bDeleteSourceRecord
                out output_timestamp,
                out strOutputPath,
                out strError);
            if (lRet == -1)
            {
                strError = "DoCopyRecord() error :" + strError;
                goto ERROR1;
            }

            string strTargetLibraryCode = "";
            // 检查一个册记录的馆藏地点是否符合馆代码列表要求
            // return:
            //      -1  检查过程出错
            //      0   符合要求
            //      1   不符合要求
            nRet = CheckItemLibraryCode(strNewXml,
                sessioninfo,
                        // sessioninfo.LibraryCodeList,
                        out strTargetLibraryCode,
                        out strError);
            if (nRet == -1)
                goto ERROR1;

            // 2014/7/3
            if (this.VerifyBookType == true)
            {
                string strEntityDbName = ResPath.GetDbName(info.NewRecPath);
                if (String.IsNullOrEmpty(strEntityDbName) == true)
                {
                    strError = "从路径 '" + info.NewRecPath + "' 中获得数据库名时失败";
                    goto ERROR1;
                }

                XmlDocument domTemp = new XmlDocument();
                domTemp.LoadXml(strNewXml);

                // 检查一个册记录的读者类型是否符合值列表要求
                // parameters:
                // return:
                //      -1  检查过程出错
                //      0   符合要求
                //      1   不符合要求
                nRet = CheckItemBookType(domTemp,
                    strEntityDbName,
                    out strError);
                if (nRet == -1 || nRet == 1)
                    goto ERROR1;
            }


            // 检查新记录是否属于管辖范围
            if (sessioninfo.GlobalUser == false
                || sessioninfo.UserType == "reader")
            {
                if (nRet != 0)
                {
                    strError = "册记录新内容中的馆藏地点不符合要求: " + strError;
                    goto ERROR1;
                }
            }

            // Debug.Assert(strOutputPath == info.NewRecPath);
            string strTargetPath = strOutputPath;

            lRet = channel.DoSaveTextRes(strTargetPath,
                strNewXml,
                false,   // include preamble?
                "content",
                output_timestamp,
                out output_timestamp,
                out strOutputPath,
                out strError);
            if (lRet == -1)
            {
                strError = "WriteEntities()API move操作中,实体记录 '" + info.OldRecPath + "' 已经被成功移动到 '" + strTargetPath + "' ,但在写入新内容时发生错误: " + strError;

                if (channel.ErrorCode == ChannelErrorCode.TimestampMismatch)
                {
                    // 不进行反复处理。
                    // 因为源已经移动,情况很复杂
                }

                // 仅仅写入错误日志即可。没有Undo
                this.WriteErrorLog(strError);

                /*
                if (channel.ErrorCode == ChannelErrorCode.TimestampMismatch)
                {
                    if (nRedoCount > 10)
                    {
                        strError = "反复保存(DoCopyRecord())均遇到时间戳冲突, 超过10次重试仍然失败";
                        goto ERROR1;
                    }
                    // 发现时间戳不匹配
                    // 重复进行提取已存在记录\比较的过程
                    nRedoCount++;
                    goto REDOLOAD;
                }*/


                error = new EntityInfo(info);
                error.ErrorInfo = "移动操作发生错误:" + strError;
                error.ErrorCode = channel.OriginErrorCode;
                ErrorInfos.Add(error);
                return -1;
            }
            else // 成功
            {
                info.NewRecPath = strOutputPath;    // 兑现保存的位置,因为可能有追加形式的路径

                DomUtil.SetElementText(domOperLog.DocumentElement,
    "libraryCode",
    strSourceLibraryCode + "," + strTargetLibraryCode);    // 册所在的馆代码

                DomUtil.SetElementText(domOperLog.DocumentElement, "action", "move");
                if (String.IsNullOrEmpty(strStyle) == false)
                    DomUtil.SetElementText(domOperLog.DocumentElement, "style", strStyle);

                // 新记录
                XmlNode node = DomUtil.SetElementText(domOperLog.DocumentElement,
                    "record", strNewXml);
                DomUtil.SetAttr(node, "recPath", info.NewRecPath);

                // 旧记录
                node = DomUtil.SetElementText(domOperLog.DocumentElement,
                    "oldRecord", strExistSourceXml);
                DomUtil.SetAttr(node, "recPath", info.OldRecPath);

                // 保存成功,需要返回信息元素。因为需要返回新的时间戳
                error = new EntityInfo(info);
                error.NewTimestamp = output_timestamp;
                error.NewRecord = strNewXml;

                error.ErrorInfo = "移动操作成功。NewRecPath中返回了实际保存的路径, NewTimeStamp中返回了新的时间戳,NewRecord中返回了实际保存的新记录(可能和提交的源记录稍有差异)。";
                error.ErrorCode = ErrorCodeValue.NoError;
                ErrorInfos.Add(error);
            }

            return 0;

        ERROR1:
            error = new EntityInfo(info);
            error.ErrorInfo = strError;
            error.ErrorCode = ErrorCodeValue.CommonError;
            ErrorInfos.Add(error);
            return -1;
        }