DigitalPlatform / dp2

Integrated Library System / 图书馆集成系统
http://digitalplatform.github.io/dp2
Apache License 2.0
107 stars 54 forks source link

(dp2library) library.xml 中 rfid 元素配置方法 #942

Open DigitalPlatform opened 2 years ago

DigitalPlatform commented 2 years ago

0.01 版机构代码映射算法

dp2library 模块的 library.xml 文件中,rfid 元素负责配置册记录和读者记录如何映射到 OI (机构代码)。

dp2 系统内的册记录和读者记录中,并不直接存储机构代码,而是通过定义一套映射规则,将册记录和读者记录中的馆代码或者某些字段映射到机构代码。映射关系在 dp2library 模块的 library.xml 文件的 rfid/ownerInstitution 元素中配置定义。当 rfid/ownerInstitution 元素定义修改后,即便系统中的册记录和读者记录本身没有任何变化,这些记录所对应的机构代码也会发生变化,这一点要引起注意。

定义举例如下:

    <rfid>
        <ownerInstitution>
            <item map="星洲小学/" isil="CN-0000001-XZ" />
            <item map="/" isil="CN-0000001-ZG" />
            <item map="海淀分馆/" isil="CN-0000001-HD" />
            <item map="西城分馆/" isil="CN-0000001-XC" />
        </ownerInstitution>
    </rfid>

(注:最新版也提供了一种改进后的匹配算法,以 ownerInstitution/@version 属性值为 "0.02" 表示新版本算法。若 ownerInstitution/@version 属性缺省、或者属性值为 "0.01",则表示这是 0.01 旧版算法。本节介绍的是旧版算法)

模式匹配、实例字符串、模式字符串

映射算法采用了模式匹配技术。模式匹配是把类似 "星洲小学/西区*"这样的模式字符串,和类似 "星洲小学/西区阅览室" 这样的实例字符串进行匹配,其中模式字符串中允许使用星号、问号来表示模糊匹配的效果。但注意实例字符串中不允许使用星号和问号。

如何确定一个册记录的机构代码

要从册记录中抽取实例字符串,用于匹配。实例字符串直接采用册记录的 location 元素内容。

匹配原理: 将实例字符串,依次和每个 ownerInstitution/item 元素进行匹配。如果命中,则取出命中 item 元素的 isil 或 alternative 属性值,即得到机构代码。

匹配过程中,针对每个 item 元素,首先检查 type 属性是否和当前”册记录类型“匹配。type 属性缺省情况,其值为 "entity,patron",其中 "entity" 即指册记录类型。也就是说 type 属性为 "entity" 或 ”entity,patron“ 都是匹配的,而 "patron" 或 "" 都是不匹配的。(type 属性值里面是逗号分隔的值列表,一个一个的值,是不会用通配符的。)

然后继续针对 item 元素的 map 属性进行匹配,用册记录 location 元素内容(实例字符串)和 map 属性值里面定义的“模式字符串”进行模式匹配。注意,map 属性值,为了书写方便,”星洲小学/“ 实际上要理解为 "星洲小学/*",即,默认为末尾加了一个星号,以便实现前方一致匹配的效果。如果不希望进行前方一致的匹配(也就是说希望精确一致),可以这样定义 map 属性值”星洲小学/$“,即在末尾加上一个符号 $ 表示阻止系统自动加星号。

map 属性值中的模式字符串可以使用问号和星号,表示通配一个字符和任意多个字符。

注:匹配过程中,如果有多个 item 元素命中,则取 map 属性最长的一个 item 当作最后命中结果。

如何确定一个读者记录的机构代码

读者记录是用其从属馆代码+"/"构成实例字符串。(注: 0.01 版算法并不使用读者记录中的字段来构造实例字符串,这一点和 0.02 版不同)

匹配原理:

将实例字符串和每个 ownerInstitution/item 元素进行匹配。如果任何一轮命中,则取出命中 item 元素的 isil 或 alternative 属性值,即得到机构代码,不再进行后面的匹配。

匹配过程中,针对每个 item 元素,首先检查 type 属性是否和当前”读者记录类型“匹配。type 属性缺省情况,其值为 "entity,patron",其中 "patron" 即指册记录类型。也就是说 type 属性为 "patron" 或 ”entity,patron“ 都是匹配的,而 "entity" 或 "" 都是不匹配的。(type 属性值里面是逗号分隔的值列表,一个一个的值,是不会用通配符的。)

然后继续针对 item 元素的 map 属性(模式字符串)和实例字符串进行匹配。注意,map 属性值,为了书写方便,”星洲小学/“ 实际上要理解为 "星洲小学/*",即,默认为末尾加了一个星号,以便实现前方一致匹配的效果。如果不希望进行前方一致的匹配(也就是说希望精确一致),可以这样定义 map 属性值”星洲小学/$“,即在末尾加上一个符号 $ 表示阻止系统自动加星号。

注:每一轮匹配过程中,如果有多个 item 元素命中,则取 map 属性最长的一个 item 当作最后命中结果。

0.01 版匹配算法的缺点

0.01 版匹配算法,无法对同一个馆代码的读者记录,区分定义出两种或者以上的机构代码。而对同一个馆代码的册记录是可以做到定义多种机构代码的。

这是因为算法在对读者记录抽取实例字符串的时候,只能使用馆代码作为实例字符串;而对册记录抽取实例字符串的时候,是使用的馆藏地字段内容,馆藏地地字段内容一般为“馆代码/阅览室名”这样的形态,信息比读者记录抽取的字符串丰富得多,可以利用阅览室名部分的局部,根据模式匹配来区分映射。

为了克服这个缺点,后来增加了 0.02 版算法

DigitalPlatform commented 2 years ago

0.02 版机构代码映射算法

0.02 版机构代码的映射算法针对 0.01 版做了改进。改进的目的是支持同一个馆代码的读者记录,可以按照一定特征定义多种机构代码,这样和册记录的能力拉齐了。

改进之处,主要是 1) ownerInstitution 增加了一个属性 version,如果这个属性缺省,表示 0.01 版;如果属性值为 "0.02",表示 0.02 版。 2) item 元素的 map 属性值,在 0.02 版中不再默认前方一致匹配的效果,而是精确一致的效果。要实现前方一致的匹配效果,应在模式字符串最后加上一个星号。 3) 对读者记录匹配的算法进行了改进,从 0.01 版的直接利用 馆代码+"/" 作为实例字符串进行匹配,改为利用读者记录中 department 和 readerType 元素内容构造出实例字符串进行匹配。 4) 给 ownerInstitution/item 元素增加了 type 属性定义

定义举例如下:

    <rfid>
        <ownerInstitution version="0.02">
            <item map="星洲小学/*" isil="CN-0000001-XZ" />
            <item map="/*" isil="CN-0000001-ZG" />
            <item map="海淀分馆/*" isil="CN-0000001-HD" />
            <item map="西城分馆/*" isil="CN-0000001-XC" />
        </ownerInstitution>
    </rfid>

如何确定一个册记录的机构代码

册记录的机构代码是根据其 location 元素内容映射得到的。获取实例字符串的方法和 0.01 版一致。即:将 location 元素内容依次和每个 ownerInstitution/item 元素进行匹配。不过需要注意的是,item 元素的 map 属性值,也就是模式字符串,是默认精确一致的,这一点和 0.01 算法不同。

如何确定一个读者记录的机构代码

从读者记录中抽取的实例字符串,是根据其从属馆代码+department 元素内容、馆代码+readerType 元素一共两种内容映射得到的。

这两种内容的产生规则是: 1) 馆代码+department元素内容:假设馆代码为”海淀分馆“,department 元素内容为”2019级1班“,那么产生一个字符串内容是”海淀分馆/2019级1班“ 2) 馆代码+readerType元素内容:假设馆代码为”海淀分馆“,readerType 元素内容为”普通读者“,那么产生一个字符串内容是”海淀分馆/readerType:普通读者“。注意大小写敏感,T 是大写字母

匹配算法是:

将上述两种内容,分两轮,依次和每个 ownerInstitution/item 元素进行匹配。如果任何一轮命中,则取出命中 item 元素的 isil 或 alternative 属性值,即得到机构代码,不再进行后面的匹配。

匹配过程中,针对每个 item 元素,首先检查 type 属性是否和当前”读者记录类型“匹配。type 属性缺省情况,其值为 "entity,patron",其中 "patron" 即指册记录类型。也就是说 type 属性为 "patron" 或 ”entity,patron“ 都是匹配的,而 "entity" 或 "" 都是不匹配的。(type 属性值里面是逗号分隔的值列表,一个一个的值,是不会用通配符的。)

然后继续针对 item 元素的 map 属性进行匹配,用准备好的实例字符串内容进行匹配。注意,map 属性值,0.02 版默认精确一致算法。即,不再像 0.01 版那样自动为模式字符串最后加一个星号进行匹配。

注:每一轮匹配过程中,如果有多个 item 元素命中,则取 map 属性最长的一个 item 当作最后命中结果。

DigitalPlatform commented 2 years ago

dp2circulation 前端用到机构代码映射算法的功能

DigitalPlatform commented 2 years ago

dp2ssl 前端用到机构代码映射算法的功能

dp2SSL.ShelfData.GetOwerInstitution() 函数:

        // parameters:
        //      cfg_dom 根元素是 rfid
        //      strLocation 纯净的 location 元素内容。
        //      isil    [out] 返回 ISIL 形态的代码
        //      alternative [out] 返回其他形态的代码
        // return:
        //      true    找到。信息在 isil 和 alternative 参数里面返回
        //      false   没有找到
        public static bool GetOwnerInstitution(
            string strLocation,
            out string isil,
            out string alternative)

dp2SSL.EntityReplication.GetInstitution() 函数:

        public static string GetInstitution(string location)

dp2SSL.EntityReplication.DownloadAllEntityRecordAsync() 函数:

        public static async Task<NormalResult> DownloadAllEntityRecordAsync(
    List<string> item_dbnames,
    List<string> unprocessed_dbnames,
    Delegate_writeLog writeLog,
    CancellationToken token)

用于 dp2ssl 首次自动下载全部册记录到本地缓存。配置页面的菜单“重做全量同步册记录和书目摘要”可以随时促使 dp2ssl 重做一次自动下载。

dp2SSL.EntityReplication.GetUII() 函数:

        static string GetUii(string strRecord)

用于同步操作日志中的 setEntity 类型(action='new')动作时,获得 UII 以便兑现刷新本地库中缓存的册记录

dp2SSL.EntityReplication.UpdateLocalEntityRecordAsync() 函数:

        static async Task<NormalResult> UpdateLocalEntityRecordAsync(
            string strRecPath,
            string strRecord)

用于同步操作日志中的 setEntity 类型(action='new' 'change' 'transfer')动作时,兑现刷新本地库中缓存的册记录

dp2SSL.EntityReplication.DeleteLocalEntityRecord() 函数:

        static NormalResult DeleteLocalEntityRecord(
            string strRecPath,
            string strRecord)

用于同步操作日志中的 setEntity 类型(action='delete')动作时,兑现删除本地库中缓存的册记录

DigitalPlatform commented 2 years ago

dp2inventory 前端用到机构代码映射算法的功能

renyh commented 2 years ago

dp2ssl读者OI引用层次

GetOwnerInstitution(string, System.Xml.XmlDocument, out string, out string) (dp2SSL.ShelfData)
--GetPatronOiPii(System.Xml.XmlDocument) (dp2SSL.LibraryChannelUtil)
----DeleteLocalPatronRecord(string) (dp2SSL.LibraryChannelUtil)
------TraceSetReaderInfo(System.Xml.XmlDocument, dp2SSL.Models.PatronReplication.ProcessInfo) (dp2SSL.Models.PatronReplication)
--------DoReplication(string, string, DigitalPlatform.LibraryClient.LogType, System.Threading.CancellationToken) (dp2SSL.Models.PatronReplication)
----------StartMonitorTask() (dp2SSL.ShelfData)
------------PrepareShelfAsync() (dp2SSL.App)
--------------PageShelf_Loaded(object, System.Windows.RoutedEventArgs) (dp2SSL.PageShelf)

----UpdateLocalPatronRecord(dp2SSL.LibraryChannelUtil.GetReaderInfoResult, System.DateTime) (dp2SSL.LibraryChannelUtil)
------TraceBorrowOrReturn(DigitalPlatform.LibraryClient.OperLogItem, System.Xml.XmlDocument, dp2SSL.Models.PatronReplication.ProcessInfo) (dp2SSL.Models.PatronReplication)
--------DoReplication(string, string, DigitalPlatform.LibraryClient.LogType, System.Threading.CancellationToken) (dp2SSL.Models.PatronReplication)
----------StartMonitorTask() (dp2SSL.ShelfData)
------------PrepareShelfAsync() (dp2SSL.App)
--------------PageShelf_Loaded(object, System.Windows.RoutedEventArgs) (dp2SSL.PageShelf)
------TraceSetReaderInfo(System.Xml.XmlDocument, dp2SSL.Models.PatronReplication.ProcessInfo) (dp2SSL.Models.PatronReplication)
--------DoReplication(string, string, DigitalPlatform.LibraryClient.LogType, System.Threading.CancellationToken) (dp2SSL.Models.PatronReplication)
----------StartMonitorTask() (dp2SSL.ShelfData)
------------PrepareShelfAsync() (dp2SSL.App)
--------------PageShelf_Loaded(object, System.Windows.RoutedEventArgs) (dp2SSL.PageShelf)

------FillPatronDetailAsync(dp2SSL.PageShelf.Delegate_welcome, bool) (dp2SSL.PageShelf)
--------App_LineFeed(object, dp2SSL.LineFeedEventArgs) (dp2SSL.PageShelf)
----------PageShelf_Loaded(object, System.Windows.RoutedEventArgs) (dp2SSL.PageShelf)
----------PageShelf_Unloaded(object, System.Windows.RoutedEventArgs) (dp2SSL.PageShelf)
--------FingerprintManager_Touched(object, DigitalPlatform.IO.TouchedEventArgs) (dp2SSL.PageShelf)
----------PageShelf_Loaded(object, System.Windows.RoutedEventArgs) (dp2SSL.PageShelf)
----------PageShelf_Unloaded(object, System.Windows.RoutedEventArgs) (dp2SSL.PageShelf)
--------PatronControl_InputFace(object, System.EventArgs) (dp2SSL.PageShelf)
----------PageShelf() (dp2SSL.PageShelf)
--------RefreshPatronsAsync(System.Collections.Generic.List<DigitalPlatform.RFID.TagAndData>) (dp2SSL.PageShelf)
----------App_PatronTagChanged(object, dp2SSL.NewTagChangedEventArgs) (dp2SSL.PageShelf)
------------PageShelf_Loaded(object, System.Windows.RoutedEventArgs) (dp2SSL.PageShelf)
------------PageShelf_Unloaded(object, System.Windows.RoutedEventArgs) (dp2SSL.PageShelf)

--Set(dp2SSL.PatronItem, DigitalPlatform.LibraryClient.localhost.Record, System.DateTime) (dp2SSL.Models.PatronReplication)
----DownloadAllPatronRecordAsync(dp2SSL.Models.PatronReplication.Delegate_writeLog, System.Threading.CancellationToken) (dp2SSL.Models.PatronReplication)
------StartMonitorTask() (dp2SSL.ShelfData)
--------PrepareShelfAsync() (dp2SSL.App)
----------PageShelf_Loaded(object, System.Windows.RoutedEventArgs) (dp2SSL.PageShelf)

DigitalPlatform.LibraryServer.Common/LibraryServerUtil.cs

        #region 读者记录 OI

        // 2022/3/5
        // return:
        //      true    找到。信息在 isil 和 alternative 参数里面返回
        //      false   没有找到
        // exception:
        //      可能会抛出异常 Exception
        public static bool GetOwnerInstitution(
            XmlElement rfid,
            string libraryCode,
            XmlDocument readerdom,
            out string isil,
            out string alternative

引用层次1

dp2SSL/Models/shelfData.cs

        // 专用于读者记录
        public static bool GetOwnerInstitution(
    string libraryCode,
    XmlDocument readerdom,
    out string isil,
    out string alternative)

引用层次1-1

dp2SSL/LibraryChannelUtil.cs

        // 获得读者的 PII。注意包含了 OI 部分
        static string GetPatronOiPii(XmlDocument dom)

引用层次1-1-1

dp2SSL/LibraryChannelUtil.cs

        // 把读者记录保存(更新)到本地数据库
        // parameters:
        //          lastWriteTime   最后写入时间。采用服务器时间
        // result.Value
        //      -1  出错
        //      0   没有发生修改
        //      1   发生了创建或者修改
        public static NormalResult UpdateLocalPatronRecord(
            GetReaderInfoResult get_result,
            DateTime lastWriteTime)

引用层次1-1-1-1

\dp2SSL\Models\PatronReplication.cs

        static async Task<NormalResult> TraceBorrowOrReturn(
            OperLogItem item,
            XmlDocument domLog,
            ProcessInfo info)

引用层次1-1-1-2

\dp2SSL\Models\PatronReplication.cs

        static NormalResult TraceSetReaderInfo(
        XmlDocument domLog,
        ProcessInfo info)

引用层次1-1-1-3

\dp2SSL\Page\PageShelf.xaml.cs

        // 填充读者信息的其他字段(第二阶段)
        // resut.Value
        //      -1  出错
        //      0   没有填充
        //      1   成功填充
        async Task<NormalResult> FillPatronDetailAsync(
            Delegate_welcome func_welcome,
            bool force = false)

引用层次1-1-2

C:\0-ryh\code\dp2\dp2SSL\LibraryChannelUtil.cs

        public static NormalResult DeleteLocalPatronRecord(string strXml)

引用层次1-1-2-1

C:\0-ryh\code\dp2\dp2SSL\Models\PatronReplication.cs

        static NormalResult TraceSetReaderInfo(
        XmlDocument domLog,
        ProcessInfo info)

引用层次1-2

\dp2SSL\Models\PatronReplication.cs

        // 设置 PatronItem 对象成员
        // result.Value:
        //      -1  出错
        //      0   需要跳过这条读者记录
        //      1   成功
        static SetResult Set(PatronItem patron,
            Record record,
            DateTime lastWriteTime)

引用层次1-2-1

C:\0-ryh\code\dp2\dp2SSL\Models\PatronReplication.cs

        // 第一阶段:获得全部读者库记录,进入本地数据库
        // result.Value
        //      -1  出错
        //      >=0 实际获得的读者记录条数
        public static async Task<ReplicationPlan> DownloadAllPatronRecordAsync(
            Delegate_writeLog writeLog,
            CancellationToken token)

引用层次1-2-1-1

C:\0-ryh\code\dp2\dp2SSL\Models\ShelfData_monitor.cs

        // 启动一般监控任务
        public static void StartMonitorTask()
引用层次1-2-1-1-1

C:\0-ryh\code\dp2\dp2SSL\App.xaml.cs

        // 为智能书柜执行一些初始化操作
        public static async Task<NormalResult> PrepareShelfAsync()
引用层次1-2-1-1-1-1

C:\0-ryh\code\dp2\dp2SSL\Page\PageShelf.xaml.cs

        private async void PageShelf_Loaded(object sender, RoutedEventArgs e)
#pragma warning restore VSTHRD100 // 避免使用 Async Void 方法
renyh commented 2 years ago

dp2ssl关于册OI的引用层次

GetOwnerInstitution(System.Xml.XmlElement, string, string, out string, out string) (DigitalPlatform.LibraryServer.LibraryServerUtil)

GetOwnerInstitution(string, out string, out string) (dp2SSL.ShelfData)
--无下级引用DownloadTagsInfo(System.Collections.Generic.List<string>, int, dp2SSL.LibraryChannelUtil.delegate_showText, System.Threading.CancellationToken) (dp2SSL.LibraryChannelUtil)
--GetInstitution(string) (dp2SSL.Models.EntityReplication)
----DownloadUidTable(System.Collections.Generic.List<string>, System.Collections.Hashtable, dp2SSL.InventoryData.delegate_showText, System.Threading.CancellationToken) (dp2SSL.InventoryData)
------beginInventory_Click(object, System.Windows.RoutedEventArgs) (dp2SSL.PageInventory)
--------System.Windows.Markup.IComponentConnector.Connect(int, object) (dp2SSL.PageInventory)
----DeleteLocalEntityRecord(string, string) (dp2SSL.Models.EntityReplication)
------TraceSetEntity(System.Xml.XmlDocument, dp2SSL.Models.PatronReplication.ProcessInfo) (dp2SSL.Models.EntityReplication)
--------DoReplication(string, string, DigitalPlatform.LibraryClient.LogType, System.Threading.CancellationToken) (dp2SSL.Models.PatronReplication)
----------StartMonitorTask() (dp2SSL.ShelfData)
------------PrepareShelfAsync() (dp2SSL.App)
--------------PageShelf_Loaded(object, System.Windows.RoutedEventArgs) (dp2SSL.PageShelf)
----DownloadAllEntityRecordAsync(System.Collections.Generic.List<string>, System.Collections.Generic.List<string>, dp2SSL.Models.EntityReplication.Delegate_writeLog, System.Threading.CancellationToken) (dp2SSL.Models.EntityReplication)
------StartMonitorTask() (dp2SSL.ShelfData)
--------PrepareShelfAsync() (dp2SSL.App)
----------PageShelf_Loaded(object, System.Windows.RoutedEventArgs) (dp2SSL.PageShelf)
----GetUii(string) (dp2SSL.Models.EntityReplication)
------TraceSetEntity(System.Xml.XmlDocument, dp2SSL.Models.PatronReplication.ProcessInfo) (dp2SSL.Models.EntityReplication)
--------DoReplication(string, string, DigitalPlatform.LibraryClient.LogType, System.Threading.CancellationToken) (dp2SSL.Models.PatronReplication)
----------StartMonitorTask() (dp2SSL.ShelfData)
------------PrepareShelfAsync() (dp2SSL.App)
--------------PageShelf_Loaded(object, System.Windows.RoutedEventArgs) (dp2SSL.PageShelf)
----UpdateLocalEntityRecordAsync(string, string) (dp2SSL.Models.EntityReplication)
------TraceSetEntity(System.Xml.XmlDocument, dp2SSL.Models.PatronReplication.ProcessInfo) (dp2SSL.Models.EntityReplication)
--------DoReplication(string, string, DigitalPlatform.LibraryClient.LogType, System.Threading.CancellationToken) (dp2SSL.Models.PatronReplication)
----------StartMonitorTask() (dp2SSL.ShelfData)
------------PrepareShelfAsync() (dp2SSL.App)
--------------PageShelf_Loaded(object, System.Windows.RoutedEventArgs) (dp2SSL.PageShelf)
renyh commented 2 years ago

dp2inventory关于册OI的引用层次

GetOwnerInstitution(System.Xml.XmlElement, string, string, out string, out string) (DigitalPlatform.LibraryServer.LibraryServerUtil)
GetOwnerInstitution(string, out string, out string) (dp2Inventory.LibraryChannelUtil)
--DownloadUidTable(System.Collections.Generic.List<string>, System.Collections.Hashtable, dp2Inventory.LibraryChannelUtil.delegate_showText, System.Threading.CancellationToken) (dp2Inventory.LibraryChannelUtil)
----toolStripButton_begin_Click(object, System.EventArgs) (dp2Inventory.InventoryDialog)
------InitializeComponent() (dp2Inventory.InventoryDialog)
-------InventoryDialog() (dp2Inventory.InventoryDialog)