修复FindS企业映射并清理临时文件

master
shenzhidan 2026-08-07 14:31:27 +08:00
parent 0a705c28ed
commit d6ac29e24e
24 changed files with 454 additions and 882 deletions

View File

@ -1,23 +0,0 @@
<!doctype html><html lang="zh"><head data-built-info="@cqsjjb/scripts@2.0.0 Env/production (2026/7/29 08:39:27) App/personnelPosition"><meta charset="UTF-8"/><meta name="renderer" content="webkit"/><meta http-equiv="X-UA-Compatible" content="IE=edge,Chrome=1"/><meta name="viewport" content="width=device-width,minimum-scale=1,maximum-scale=1,user-scalable=no,viewport-fit=cover"><meta name="referrer" content="strict-origin-when-cross-origin"/><title>--</title><script>(function () {
const APP_ENV = {
antd: {
'ant-prefix': 'micro-temp',
fontFamily: 'PingFangSC-Regular',
colorPrimary: '#1677ff',
borderRadius: parseInt('2')
},
appKey: '',
basename: 'personnelPosition',
API_HOST: ''
};
APP_ENV.API_HOST = sessionStorage.API_HOST || APP_ENV.API_HOST || window.location.origin;
window.process = {
env: { app: APP_ENV },
NODE_ENV: 'production'
};
window.__JJB_ENVIRONMENT__ = {
API_HOST: APP_ENV.API_HOST,
redirect: '',
FRAMEWORK: APP_ENV.antd
};
})();</script><script defer="defer" src="/personnelPosition/static/js/766.e7608964937e89d010cb.js"></script><script defer="defer" src="/personnelPosition/static/js/4.2fae1ea81a5a7fcec4e3.js"></script><script defer="defer" src="/personnelPosition/static/js/main.00f439807c97d9248112.js"></script><link href="/personnelPosition/static/css/main.d160d67c789db3671cf4.css" rel="stylesheet"></head><body><noscript>此网页需要开启JavaScript功能。</noscript><div id="root" style="width: 100%; height: 100%; position: relative;overflow-y: auto"></div><script type="text/javascript">/* @cqsjjb/script 输出当前应用基本信息、构建时间 */console.log("%c@cqsjjb/scripts@2.0.0 Env/production (2026/7/29 08:39:27) App/personnelPosition Version/main Java/<branch-name>", "color: #1890ff; border-radius: 2px; padding: 0 4px; border: 1px solid #1890ff; background: #f9fcff")</script></body></html>

View File

@ -1 +0,0 @@
.header-back{background-color:#fff;border-bottom:1px solid #dcdfe6;margin-bottom:0;padding:10px 20px;position:sticky;top:0;z-index:9}.header-back .action,.header-back .action .back{align-items:center;display:flex}.header-back .action .back{cursor:pointer;font-size:14px;gap:10px}.header-back .action .title{font-size:17px}.micro-temp-table-cell-scrollbar{width:15px}.micro-temp-table-cell .micro-temp-btn-link{padding:0}.micro-temp-pro-table-list-toolbar-container{padding-bottom:16px;padding-top:0!important}body,html{height:100%;width:100%}input::-ms-clear,input::-ms-reveal{display:none}*,:after,:before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;-ms-overflow-style:scrollbar;-webkit-tap-highlight-color:rgba(0,0,0,0)}@-ms-viewport{width:device-width}body{margin:0}[tabindex="-1"]:focus{outline:none}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{font-weight:500;margin-bottom:.5em;margin-top:0}p{margin-bottom:1em;margin-top:0}abbr[data-original-title],abbr[title]{border-bottom:0;cursor:help;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}address{font-style:normal;line-height:inherit;margin-bottom:1em}input[type=number],input[type=password],input[type=text],textarea{-webkit-appearance:none}dl,ol,ul{margin-bottom:1em;margin-top:0}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:500}dd{margin-bottom:.5em;margin-left:0}blockquote{margin:0 0 1em}dfn{font-style:italic}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}code,kbd,pre,samp{font-family:SFMono-Regular,Consolas,Liberation Mono,Menlo,Courier,monospace;font-size:1em}pre{margin-bottom:1em;margin-top:0;overflow:auto}figure{margin:0 0 1em}img{border-style:none;vertical-align:middle}[role=button],a,area,button,input:not([type=range]),label,select,summary,textarea{touch-action:manipulation}table{border-collapse:collapse}caption{caption-side:bottom;padding-bottom:.3em;padding-top:.75em;text-align:left}button,input,optgroup,select,textarea{color:inherit;font-family:inherit;font-size:inherit;line-height:inherit;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{border:0;margin:0;min-width:0;padding:0}legend{color:inherit;display:block;font-size:1.5em;line-height:inherit;margin-bottom:.5em;max-width:100%;padding:0;white-space:normal;width:100%}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:none;outline-offset:-2px}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}output{display:inline-block}summary{display:list-item}template{display:none}[hidden]{display:none!important}mark{background-color:#feffe6;padding:.2em}.micro-temp-modal-header{border-bottom:1px solid #ccc!important;margin:0 -24px 15px!important;padding:0 24px 15px!important}.micro-temp-modal-footer{text-align:center!important}.search-layout{position:relative}.search-layout:after{background-color:#f1f1f2;bottom:-10px;content:"";height:10px;left:-20px;position:absolute;right:-20px}.search-layout+.table-layout{padding-top:26px!important}.card-layout{background-color:#fff;border-radius:6px}

File diff suppressed because one or more lines are too long

View File

@ -1,77 +0,0 @@
/**
* @license React
* react-dom.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* react-is.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* react-jsx-runtime.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* react.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* scheduler.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* use-sync-external-store-shim.production.js
*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/** @license React v16.13.1
* react-is.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/** @license React v17.0.2
* react-is.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

View File

@ -1,104 +0,0 @@
/*!
Copyright (c) 2018 Jed Watson.
Licensed under the MIT License (MIT), see
http://jedwatson.github.io/classnames
*/
/*!
* is-plain-object <https://github.com/jonschlinkert/is-plain-object>
*
* Copyright (c) 2014-2017, Jon Schlinkert.
* Released under the MIT License.
*/
/*!
* isobject <https://github.com/jonschlinkert/isobject>
*
* Copyright (c) 2014-2017, Jon Schlinkert.
* Released under the MIT License.
*/
/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */
/** @preserve
* Counter block mode compatible with Dr Brian Gladman fileenc.c
* derived from CryptoJS.mode.CTR
* Jan Hruby jhruby.web@gmail.com
*/
/** @preserve
(c) 2012 by Cédric Mesnil. All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**![arrow-down](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAiIGhlaWdodD0iNTAiIGZpbGw9IiNjYWNhY2EiIHZpZXdCb3g9IjY0IDY0IDg5NiA4OTYiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTg2MiA0NjUuM2gtODFjLTQuNiAwLTkgMi0xMi4xIDUuNUw1NTAgNzIzLjFWMTYwYzAtNC40LTMuNi04LTgtOGgtNjBjLTQuNCAwLTggMy42LTggOHY1NjMuMUwyNTUuMSA0NzAuOGMtMy0zLjUtNy40LTUuNS0xMi4xLTUuNWgtODFjLTYuOCAwLTEwLjUgOC4xLTYgMTMuMkw0ODcuOSA4NjFhMzEuOTYgMzEuOTYgMCAwMDQ4LjMgMEw4NjggNDc4LjVjNC41LTUuMi44LTEzLjItNi0xMy4yeiIgLz48L3N2Zz4=) */
/**![arrow-up](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAiIGhlaWdodD0iNTAiIGZpbGw9IiNjYWNhY2EiIHZpZXdCb3g9IjY0IDY0IDg5NiA4OTYiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTg2OCA1NDUuNUw1MzYuMSAxNjNhMzEuOTYgMzEuOTYgMCAwMC00OC4zIDBMMTU2IDU0NS41YTcuOTcgNy45NyAwIDAwNiAxMy4yaDgxYzQuNiAwIDktMiAxMi4xLTUuNUw0NzQgMzAwLjlWODY0YzAgNC40IDMuNiA4IDggOGg2MGM0LjQgMCA4LTMuNiA4LThWMzAwLjlsMjE4LjkgMjUyLjNjMyAzLjUgNy40IDUuNSAxMi4xIDUuNWg4MWM2LjggMCAxMC41LTggNi0xMy4yeiIgLz48L3N2Zz4=) */
/**![calendar](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAiIGhlaWdodD0iNTAiIGZpbGw9IiNjYWNhY2EiIHZpZXdCb3g9IjY0IDY0IDg5NiA4OTYiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTg4MCAxODRINzEydi02NGMwLTQuNC0zLjYtOC04LThoLTU2Yy00LjQgMC04IDMuNi04IDh2NjRIMzg0di02NGMwLTQuNC0zLjYtOC04LThoLTU2Yy00LjQgMC04IDMuNi04IDh2NjRIMTQ0Yy0xNy43IDAtMzIgMTQuMy0zMiAzMnY2NjRjMCAxNy43IDE0LjMgMzIgMzIgMzJoNzM2YzE3LjcgMCAzMi0xNC4zIDMyLTMyVjIxNmMwLTE3LjctMTQuMy0zMi0zMi0zMnptLTQwIDY1NkgxODRWNDYwaDY1NnYzODB6TTE4NCAzOTJWMjU2aDEyOHY0OGMwIDQuNCAzLjYgOCA4IDhoNTZjNC40IDAgOC0zLjYgOC04di00OGgyNTZ2NDhjMCA0LjQgMy42IDggOCA4aDU2YzQuNCAwIDgtMy42IDgtOHYtNDhoMTI4djEzNkgxODR6IiAvPjwvc3ZnPg==) */
/**![caret-down](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAiIGhlaWdodD0iNTAiIGZpbGw9IiNjYWNhY2EiIHZpZXdCb3g9IjAgMCAxMDI0IDEwMjQiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTg0MC40IDMwMEgxODMuNmMtMTkuNyAwLTMwLjcgMjAuOC0xOC41IDM1bDMyOC40IDM4MC44YzkuNCAxMC45IDI3LjUgMTAuOSAzNyAwTDg1OC45IDMzNWMxMi4yLTE0LjIgMS4yLTM1LTE4LjUtMzV6IiAvPjwvc3ZnPg==) */
/**![caret-up](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAiIGhlaWdodD0iNTAiIGZpbGw9IiNjYWNhY2EiIHZpZXdCb3g9IjAgMCAxMDI0IDEwMjQiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTg1OC45IDY4OUw1MzAuNSAzMDguMmMtOS40LTEwLjktMjcuNS0xMC45LTM3IDBMMTY1LjEgNjg5Yy0xMi4yIDE0LjItMS4yIDM1IDE4LjUgMzVoNjU2LjhjMTkuNyAwIDMwLjctMjAuOCAxOC41LTM1eiIgLz48L3N2Zz4=) */
/**![clock-circle](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAiIGhlaWdodD0iNTAiIGZpbGw9IiNjYWNhY2EiIHZpZXdCb3g9IjY0IDY0IDg5NiA4OTYiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTUxMiA2NEMyNjQuNiA2NCA2NCAyNjQuNiA2NCA1MTJzMjAwLjYgNDQ4IDQ0OCA0NDggNDQ4LTIwMC42IDQ0OC00NDhTNzU5LjQgNjQgNTEyIDY0em0wIDgyMGMtMjA1LjQgMC0zNzItMTY2LjYtMzcyLTM3MnMxNjYuNi0zNzIgMzcyLTM3MiAzNzIgMTY2LjYgMzcyIDM3Mi0xNjYuNiAzNzItMzcyIDM3MnoiIC8+PHBhdGggZD0iTTY4Ni43IDYzOC42TDU0NC4xIDUzNS41VjI4OGMwLTQuNC0zLjYtOC04LThINDg4Yy00LjQgMC04IDMuNi04IDh2Mjc1LjRjMCAyLjYgMS4yIDUgMy4zIDYuNWwxNjUuNCAxMjAuNmMzLjYgMi42IDguNiAxLjggMTEuMi0xLjdsMjguNi0zOWMyLjYtMy43IDEuOC04LjctMS44LTExLjJ6IiAvPjwvc3ZnPg==) */
/**![column-height](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAiIGhlaWdodD0iNTAiIGZpbGw9IiNjYWNhY2EiIHZpZXdCb3g9IjY0IDY0IDg5NiA4OTYiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTg0MCA4MzZIMTg0Yy00LjQgMC04IDMuNi04IDh2NjBjMCA0LjQgMy42IDggOCA4aDY1NmM0LjQgMCA4LTMuNiA4LTh2LTYwYzAtNC40LTMuNi04LTgtOHptMC03MjRIMTg0Yy00LjQgMC04IDMuNi04IDh2NjBjMCA0LjQgMy42IDggOCA4aDY1NmM0LjQgMCA4LTMuNiA4LTh2LTYwYzAtNC40LTMuNi04LTgtOHpNNjEwLjggMzc4YzYgMCA5LjQtNyA1LjctMTEuN0w1MTUuNyAyMzguN2E3LjE0IDcuMTQgMCAwMC0xMS4zIDBMNDAzLjYgMzY2LjNhNy4yMyA3LjIzIDAgMDA1LjcgMTEuN0g0NzZ2MjY4aC02Mi44Yy02IDAtOS40IDctNS43IDExLjdsMTAwLjggMTI3LjVjMi45IDMuNyA4LjUgMy43IDExLjMgMGwxMDAuOC0xMjcuNWMzLjctNC43LjQtMTEuNy01LjctMTEuN0g1NDhWMzc4aDYyLjh6IiAvPjwvc3ZnPg==) */
/**![copy](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAiIGhlaWdodD0iNTAiIGZpbGw9IiNjYWNhY2EiIHZpZXdCb3g9IjY0IDY0IDg5NiA4OTYiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTgzMiA2NEgyOTZjLTQuNCAwLTggMy42LTggOHY1NmMwIDQuNCAzLjYgOCA4IDhoNDk2djY4OGMwIDQuNCAzLjYgOCA4IDhoNTZjNC40IDAgOC0zLjYgOC04Vjk2YzAtMTcuNy0xNC4zLTMyLTMyLTMyek03MDQgMTkySDE5MmMtMTcuNyAwLTMyIDE0LjMtMzIgMzJ2NTMwLjdjMCA4LjUgMy40IDE2LjYgOS40IDIyLjZsMTczLjMgMTczLjNjMi4yIDIuMiA0LjcgNCA3LjQgNS41djEuOWg0LjJjMy41IDEuMyA3LjIgMiAxMSAySDcwNGMxNy43IDAgMzItMTQuMyAzMi0zMlYyMjRjMC0xNy43LTE0LjMtMzItMzItMzJ6TTM1MCA4NTYuMkwyNjMuOSA3NzBIMzUwdjg2LjJ6TTY2NCA4ODhINDE0Vjc0NmMwLTIyLjEtMTcuOS00MC00MC00MEgyMzJWMjY0aDQzMnY2MjR6IiAvPjwvc3ZnPg==) */
/**![double-left](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAiIGhlaWdodD0iNTAiIGZpbGw9IiNjYWNhY2EiIHZpZXdCb3g9IjY0IDY0IDg5NiA4OTYiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTI3Mi45IDUxMmwyNjUuNC0zMzkuMWM0LjEtNS4yLjQtMTIuOS02LjMtMTIuOWgtNzcuM2MtNC45IDAtOS42IDIuMy0xMi42IDYuMUwxODYuOCA0OTIuM2EzMS45OSAzMS45OSAwIDAwMCAzOS41bDI1NS4zIDMyNi4xYzMgMy45IDcuNyA2LjEgMTIuNiA2LjFINTMyYzYuNyAwIDEwLjQtNy43IDYuMy0xMi45TDI3Mi45IDUxMnptMzA0IDBsMjY1LjQtMzM5LjFjNC4xLTUuMi40LTEyLjktNi4zLTEyLjloLTc3LjNjLTQuOSAwLTkuNiAyLjMtMTIuNiA2LjFMNDkwLjggNDkyLjNhMzEuOTkgMzEuOTkgMCAwMDAgMzkuNWwyNTUuMyAzMjYuMWMzIDMuOSA3LjcgNi4xIDEyLjYgNi4xSDgzNmM2LjcgMCAxMC40LTcuNyA2LjMtMTIuOUw1NzYuOSA1MTJ6IiAvPjwvc3ZnPg==) */
/**![double-right](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAiIGhlaWdodD0iNTAiIGZpbGw9IiNjYWNhY2EiIHZpZXdCb3g9IjY0IDY0IDg5NiA4OTYiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTUzMy4yIDQ5Mi4zTDI3Ny45IDE2Ni4xYy0zLTMuOS03LjctNi4xLTEyLjYtNi4xSDE4OGMtNi43IDAtMTAuNCA3LjctNi4zIDEyLjlMNDQ3LjEgNTEyIDE4MS43IDg1MS4xQTcuOTggNy45OCAwIDAwMTg4IDg2NGg3Ny4zYzQuOSAwIDkuNi0yLjMgMTIuNi02LjFsMjU1LjMtMzI2LjFjOS4xLTExLjcgOS4xLTI3LjkgMC0zOS41em0zMDQgMEw1ODEuOSAxNjYuMWMtMy0zLjktNy43LTYuMS0xMi42LTYuMUg0OTJjLTYuNyAwLTEwLjQgNy43LTYuMyAxMi45TDc1MS4xIDUxMiA0ODUuNyA4NTEuMUE3Ljk4IDcuOTggMCAwMDQ5MiA4NjRoNzcuM2M0LjkgMCA5LjYtMi4zIDEyLjYtNi4xbDI1NS4zLTMyNi4xYzkuMS0xMS43IDkuMS0yNy45IDAtMzkuNXoiIC8+PC9zdmc+) */
/**![edit](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAiIGhlaWdodD0iNTAiIGZpbGw9IiNjYWNhY2EiIHZpZXdCb3g9IjY0IDY0IDg5NiA4OTYiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTI1Ny43IDc1MmMyIDAgNC0uMiA2LS41TDQzMS45IDcyMmMyLS40IDMuOS0xLjMgNS4zLTIuOGw0MjMuOS00MjMuOWE5Ljk2IDkuOTYgMCAwMDAtMTQuMUw2OTQuOSAxMTQuOWMtMS45LTEuOS00LjQtMi45LTcuMS0yLjlzLTUuMiAxLTcuMSAyLjlMMjU2LjggNTM4LjhjLTEuNSAxLjUtMi40IDMuMy0yLjggNS4zbC0yOS41IDE2OC4yYTMzLjUgMzMuNSAwIDAwOS40IDI5LjhjNi42IDYuNCAxNC45IDkuOSAyMy44IDkuOXptNjcuNC0xNzQuNEw2ODcuOCAyMTVsNzMuMyA3My4zLTM2Mi43IDM2Mi42LTg4LjkgMTUuNyAxNS42LTg5ek04ODAgODM2SDE0NGMtMTcuNyAwLTMyIDE0LjMtMzIgMzJ2MzZjMCA0LjQgMy42IDggOCA4aDc4NGM0LjQgMCA4LTMuNiA4LTh2LTM2YzAtMTcuNy0xNC4zLTMyLTMyLTMyeiIgLz48L3N2Zz4=) */
/**![enter](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAiIGhlaWdodD0iNTAiIGZpbGw9IiNjYWNhY2EiIHZpZXdCb3g9IjY0IDY0IDg5NiA4OTYiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTg2NCAxNzBoLTYwYy00LjQgMC04IDMuNi04IDh2NTE4SDMxMHYtNzNjMC02LjctNy44LTEwLjUtMTMtNi4zbC0xNDEuOSAxMTJhOCA4IDAgMDAwIDEyLjZsMTQxLjkgMTEyYzUuMyA0LjIgMTMgLjQgMTMtNi4zdi03NWg0OThjMzUuMyAwIDY0LTI4LjcgNjQtNjRWMTc4YzAtNC40LTMuNi04LTgtOHoiIC8+PC9zdmc+) */
/**![file](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAiIGhlaWdodD0iNTAiIGZpbGw9IiNjYWNhY2EiIHZpZXdCb3g9IjY0IDY0IDg5NiA4OTYiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTg1NC42IDI4OC42TDYzOS40IDczLjRjLTYtNi0xNC4xLTkuNC0yMi42LTkuNEgxOTJjLTE3LjcgMC0zMiAxNC4zLTMyIDMydjgzMmMwIDE3LjcgMTQuMyAzMiAzMiAzMmg2NDBjMTcuNyAwIDMyLTE0LjMgMzItMzJWMzExLjNjMC04LjUtMy40LTE2LjctOS40LTIyLjd6TTc5MC4yIDMyNkg2MDJWMTM3LjhMNzkwLjIgMzI2em0xLjggNTYySDIzMlYxMzZoMzAydjIxNmE0MiA0MiAwIDAwNDIgNDJoMjE2djQ5NHoiIC8+PC9zdmc+) */
/**![filter](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAiIGhlaWdodD0iNTAiIGZpbGw9IiNjYWNhY2EiIHZpZXdCb3g9IjY0IDY0IDg5NiA4OTYiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTM0OSA4MzhjMCAxNy43IDE0LjIgMzIgMzEuOCAzMmgyNjIuNGMxNy42IDAgMzEuOC0xNC4zIDMxLjgtMzJWNjQySDM0OXYxOTZ6bTUzMS4xLTY4NEgxNDMuOWMtMjQuNSAwLTM5LjggMjYuNy0yNy41IDQ4bDIyMS4zIDM3NmgzNDguOGwyMjEuMy0zNzZjMTIuMS0yMS4zLTMuMi00OC0yNy43LTQ4eiIgLz48L3N2Zz4=) */
/**![filter](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAiIGhlaWdodD0iNTAiIGZpbGw9IiNjYWNhY2EiIHZpZXdCb3g9IjY0IDY0IDg5NiA4OTYiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTg4MC4xIDE1NEgxNDMuOWMtMjQuNSAwLTM5LjggMjYuNy0yNy41IDQ4TDM0OSA1OTcuNFY4MzhjMCAxNy43IDE0LjIgMzIgMzEuOCAzMmgyNjIuNGMxNy42IDAgMzEuOC0xNC4zIDMxLjgtMzJWNTk3LjRMOTA3LjcgMjAyYzEyLjItMjEuMy0zLjEtNDgtMjcuNi00OHpNNjAzLjQgNzk4SDQyMC42VjY0MmgxODIuOXYxNTZ6bTkuNi0yMzYuNmwtOS41IDE2LjZoLTE4M2wtOS41LTE2LjZMMjEyLjcgMjI2aDU5OC42TDYxMyA1NjEuNHoiIC8+PC9zdmc+) */
/**![folder-open](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAiIGhlaWdodD0iNTAiIGZpbGw9IiNjYWNhY2EiIHZpZXdCb3g9IjY0IDY0IDg5NiA4OTYiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTkyOCA0NDRIODIwVjMzMC40YzAtMTcuNy0xNC4zLTMyLTMyLTMySDQ3M0wzNTUuNyAxODYuMmE4LjE1IDguMTUgMCAwMC01LjUtMi4ySDk2Yy0xNy43IDAtMzIgMTQuMy0zMiAzMnY1OTJjMCAxNy43IDE0LjMgMzIgMzIgMzJoNjk4YzEzIDAgMjQuOC03LjkgMjkuNy0yMGwxMzQtMzMyYzEuNS0zLjggMi4zLTcuOSAyLjMtMTIgMC0xNy43LTE0LjMtMzItMzItMzJ6TTEzNiAyNTZoMTg4LjVsMTE5LjYgMTE0LjRINzQ4VjQ0NEgyMzhjLTEzIDAtMjQuOCA3LjktMjkuNyAyMEwxMzYgNjQzLjJWMjU2em02MzUuMyA1MTJIMTU5bDEwMy4zLTI1Nmg2MTIuNEw3NzEuMyA3Njh6IiAvPjwvc3ZnPg==) */
/**![folder](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAiIGhlaWdodD0iNTAiIGZpbGw9IiNjYWNhY2EiIHZpZXdCb3g9IjY0IDY0IDg5NiA4OTYiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTg4MCAyOTguNEg1MjFMNDAzLjcgMTg2LjJhOC4xNSA4LjE1IDAgMDAtNS41LTIuMkgxNDRjLTE3LjcgMC0zMiAxNC4zLTMyIDMydjU5MmMwIDE3LjcgMTQuMyAzMiAzMiAzMmg3MzZjMTcuNyAwIDMyLTE0LjMgMzItMzJWMzMwLjRjMC0xNy43LTE0LjMtMzItMzItMzJ6TTg0MCA3NjhIMTg0VjI1NmgxODguNWwxMTkuNiAxMTQuNEg4NDBWNzY4eiIgLz48L3N2Zz4=) */
/**![fullscreen-exit](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAiIGhlaWdodD0iNTAiIGZpbGw9IiNjYWNhY2EiIHZpZXdCb3g9IjY0IDY0IDg5NiA4OTYiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTM5MSAyNDAuOWMtLjgtNi42LTguOS05LjQtMTMuNi00LjdsLTQzLjcgNDMuN0wyMDAgMTQ2LjNhOC4wMyA4LjAzIDAgMDAtMTEuMyAwbC00Mi40IDQyLjNhOC4wMyA4LjAzIDAgMDAwIDExLjNMMjgwIDMzMy42bC00My45IDQzLjlhOC4wMSA4LjAxIDAgMDA0LjcgMTMuNkw0MDEgNDEwYzUuMS42IDkuNS0zLjcgOC45LTguOUwzOTEgMjQwLjl6bTEwLjEgMzczLjJMMjQwLjggNjMzYy02LjYuOC05LjQgOC45LTQuNyAxMy42bDQzLjkgNDMuOUwxNDYuMyA4MjRhOC4wMyA4LjAzIDAgMDAwIDExLjNsNDIuNCA0Mi4zYzMuMSAzLjEgOC4yIDMuMSAxMS4zIDBMMzMzLjcgNzQ0bDQzLjcgNDMuN0E4LjAxIDguMDEgMCAwMDM5MSA3ODNsMTguOS0xNjAuMWMuNi01LjEtMy43LTkuNC04LjgtOC44em0yMjEuOC0yMDQuMkw3ODMuMiAzOTFjNi42LS44IDkuNC04LjkgNC43LTEzLjZMNzQ0IDMzMy42IDg3Ny43IDIwMGMzLjEtMy4xIDMuMS04LjIgMC0xMS4zbC00Mi40LTQyLjNhOC4wMyA4LjAzIDAgMDAtMTEuMyAwTDY5MC4zIDI3OS45bC00My43LTQzLjdhOC4wMSA4LjAxIDAgMDAtMTMuNiA0LjdMNjE0LjEgNDAxYy0uNiA1LjIgMy43IDkuNSA4LjggOC45ek03NDQgNjkwLjRsNDMuOS00My45YTguMDEgOC4wMSAwIDAwLTQuNy0xMy42TDYyMyA2MTRjLTUuMS0uNi05LjUgMy43LTguOSA4LjlMNjMzIDc4My4xYy44IDYuNiA4LjkgOS40IDEzLjYgNC43bDQzLjctNDMuN0w4MjQgODc3LjdjMy4xIDMuMSA4LjIgMy4xIDExLjMgMGw0Mi40LTQyLjNjMy4xLTMuMSAzLjEtOC4yIDAtMTEuM0w3NDQgNjkwLjR6IiAvPjwvc3ZnPg==) */
/**![fullscreen](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAiIGhlaWdodD0iNTAiIGZpbGw9IiNjYWNhY2EiIHZpZXdCb3g9IjY0IDY0IDg5NiA4OTYiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTI5MCAyMzYuNGw0My45LTQzLjlhOC4wMSA4LjAxIDAgMDAtNC43LTEzLjZMMTY5IDE2MGMtNS4xLS42LTkuNSAzLjctOC45IDguOUwxNzkgMzI5LjFjLjggNi42IDguOSA5LjQgMTMuNiA0LjdsNDMuNy00My43TDM3MCA0MjMuN2MzLjEgMy4xIDguMiAzLjEgMTEuMyAwbDQyLjQtNDIuM2MzLjEtMy4xIDMuMS04LjIgMC0xMS4zTDI5MCAyMzYuNHptMzUyLjcgMTg3LjNjMy4xIDMuMSA4LjIgMy4xIDExLjMgMGwxMzMuNy0xMzMuNiA0My43IDQzLjdhOC4wMSA4LjAxIDAgMDAxMy42LTQuN0w4NjMuOSAxNjljLjYtNS4xLTMuNy05LjUtOC45LTguOUw2OTQuOCAxNzljLTYuNi44LTkuNCA4LjktNC43IDEzLjZsNDMuOSA0My45TDYwMC4zIDM3MGE4LjAzIDguMDMgMCAwMDAgMTEuM2w0Mi40IDQyLjR6TTg0NSA2OTQuOWMtLjgtNi42LTguOS05LjQtMTMuNi00LjdsLTQzLjcgNDMuN0w2NTQgNjAwLjNhOC4wMyA4LjAzIDAgMDAtMTEuMyAwbC00Mi40IDQyLjNhOC4wMyA4LjAzIDAgMDAwIDExLjNMNzM0IDc4Ny42bC00My45IDQzLjlhOC4wMSA4LjAxIDAgMDA0LjcgMTMuNkw4NTUgODY0YzUuMS42IDkuNS0zLjcgOC45LTguOUw4NDUgNjk0Ljl6bS00NjMuNy05NC42YTguMDMgOC4wMyAwIDAwLTExLjMgMEwyMzYuMyA3MzMuOWwtNDMuNy00My43YTguMDEgOC4wMSAwIDAwLTEzLjYgNC43TDE2MC4xIDg1NWMtLjYgNS4xIDMuNyA5LjUgOC45IDguOUwzMjkuMiA4NDVjNi42LS44IDkuNC04LjkgNC43LTEzLjZMMjkwIDc4Ny42IDQyMy43IDY1NGMzLjEtMy4xIDMuMS04LjIgMC0xMS4zbC00Mi40LTQyLjR6IiAvPjwvc3ZnPg==) */
/**![holder](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAiIGhlaWdodD0iNTAiIGZpbGw9IiNjYWNhY2EiIHZpZXdCb3g9IjY0IDY0IDg5NiA4OTYiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTMwMCAyNzYuNWE1NiA1NiAwIDEwNTYtOTcgNTYgNTYgMCAwMC01NiA5N3ptMCAyODRhNTYgNTYgMCAxMDU2LTk3IDU2IDU2IDAgMDAtNTYgOTd6TTY0MCAyMjhhNTYgNTYgMCAxMDExMiAwIDU2IDU2IDAgMDAtMTEyIDB6bTAgMjg0YTU2IDU2IDAgMTAxMTIgMCA1NiA1NiAwIDAwLTExMiAwek0zMDAgODQ0LjVhNTYgNTYgMCAxMDU2LTk3IDU2IDU2IDAgMDAtNTYgOTd6TTY0MCA3OTZhNTYgNTYgMCAxMDExMiAwIDU2IDU2IDAgMDAtMTEyIDB6IiAvPjwvc3ZnPg==) */
/**![left](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAiIGhlaWdodD0iNTAiIGZpbGw9IiNjYWNhY2EiIHZpZXdCb3g9IjY0IDY0IDg5NiA4OTYiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTcyNCAyMTguM1YxNDFjMC02LjctNy43LTEwLjQtMTIuOS02LjNMMjYwLjMgNDg2LjhhMzEuODYgMzEuODYgMCAwMDAgNTAuM2w0NTAuOCAzNTIuMWM1LjMgNC4xIDEyLjkuNCAxMi45LTYuM3YtNzcuM2MwLTQuOS0yLjMtOS42LTYuMS0xMi42bC0zNjAtMjgxIDM2MC0yODEuMWMzLjgtMyA2LjEtNy43IDYuMS0xMi42eiIgLz48L3N2Zz4=) */
/**![minus-square](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAiIGhlaWdodD0iNTAiIGZpbGw9IiNjYWNhY2EiIHZpZXdCb3g9IjY0IDY0IDg5NiA4OTYiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTMyOCA1NDRoMzY4YzQuNCAwIDgtMy42IDgtOHYtNDhjMC00LjQtMy42LTgtOC04SDMyOGMtNC40IDAtOCAzLjYtOCA4djQ4YzAgNC40IDMuNiA4IDggOHoiIC8+PHBhdGggZD0iTTg4MCAxMTJIMTQ0Yy0xNy43IDAtMzIgMTQuMy0zMiAzMnY3MzZjMCAxNy43IDE0LjMgMzIgMzIgMzJoNzM2YzE3LjcgMCAzMi0xNC4zIDMyLTMyVjE0NGMwLTE3LjctMTQuMy0zMi0zMi0zMnptLTQwIDcyOEgxODRWMTg0aDY1NnY2NTZ6IiAvPjwvc3ZnPg==) */
/**![plus-square](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAiIGhlaWdodD0iNTAiIGZpbGw9IiNjYWNhY2EiIHZpZXdCb3g9IjY0IDY0IDg5NiA4OTYiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTMyOCA1NDRoMTUydjE1MmMwIDQuNCAzLjYgOCA4IDhoNDhjNC40IDAgOC0zLjYgOC04VjU0NGgxNTJjNC40IDAgOC0zLjYgOC04di00OGMwLTQuNC0zLjYtOC04LThINTQ0VjMyOGMwLTQuNC0zLjYtOC04LThoLTQ4Yy00LjQgMC04IDMuNi04IDh2MTUySDMyOGMtNC40IDAtOCAzLjYtOCA4djQ4YzAgNC40IDMuNiA4IDggOHoiIC8+PHBhdGggZD0iTTg4MCAxMTJIMTQ0Yy0xNy43IDAtMzIgMTQuMy0zMiAzMnY3MzZjMCAxNy43IDE0LjMgMzIgMzIgMzJoNzM2YzE3LjcgMCAzMi0xNC4zIDMyLTMyVjE0NGMwLTE3LjctMTQuMy0zMi0zMi0zMnptLTQwIDcyOEgxODRWMTg0aDY1NnY2NTZ6IiAvPjwvc3ZnPg==) */
/**![rotate-left](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAiIGhlaWdodD0iNTAiIGZpbGw9IiNjYWNhY2EiIHZpZXdCb3g9IjY0IDY0IDg5NiA4OTYiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PHN0eWxlIC8+PC9kZWZzPjxwYXRoIGQ9Ik02NzIgNDE4SDE0NGMtMTcuNyAwLTMyIDE0LjMtMzIgMzJ2NDE0YzAgMTcuNyAxNC4zIDMyIDMyIDMyaDUyOGMxNy43IDAgMzItMTQuMyAzMi0zMlY0NTBjMC0xNy43LTE0LjMtMzItMzItMzJ6bS00NCA0MDJIMTg4VjQ5NGg0NDB2MzI2eiIgLz48cGF0aCBkPSJNODE5LjMgMzI4LjVjLTc4LjgtMTAwLjctMTk2LTE1My42LTMxNC42LTE1NC4ybC0uMi02NGMwLTYuNS03LjYtMTAuMS0xMi42LTYuMWwtMTI4IDEwMWMtNCAzLjEtMy45IDkuMSAwIDEyLjNMNDkyIDMxOC42YzUuMSA0IDEyLjcuNCAxMi42LTYuMXYtNjMuOWMxMi45LjEgMjUuOS45IDM4LjggMi41IDQyLjEgNS4yIDgyLjEgMTguMiAxMTkgMzguNyAzOC4xIDIxLjIgNzEuMiA0OS43IDk4LjQgODQuMyAyNy4xIDM0LjcgNDYuNyA3My43IDU4LjEgMTE1LjhhMzI1Ljk1IDMyNS45NSAwIDAxNi41IDE0MC45aDc0LjljMTQuOC0xMDMuNi0xMS4zLTIxMy04MS0zMDIuM3oiIC8+PC9zdmc+) */
/**![rotate-right](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAiIGhlaWdodD0iNTAiIGZpbGw9IiNjYWNhY2EiIHZpZXdCb3g9IjY0IDY0IDg5NiA4OTYiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PHN0eWxlIC8+PC9kZWZzPjxwYXRoIGQ9Ik00ODAuNSAyNTEuMmMxMy0xLjYgMjUuOS0yLjQgMzguOC0yLjV2NjMuOWMwIDYuNSA3LjUgMTAuMSAxMi42IDYuMUw2NjAgMjE3LjZjNC0zLjIgNC05LjIgMC0xMi4zbC0xMjgtMTAxYy01LjEtNC0xMi42LS40LTEyLjYgNi4xbC0uMiA2NGMtMTE4LjYuNS0yMzUuOCA1My40LTMxNC42IDE1NC4yQTM5OS43NSAzOTkuNzUgMCAwMDEyMy41IDYzMWg3NC45Yy0uOS01LjMtMS43LTEwLjctMi40LTE2LjEtNS4xLTQyLjEtMi4xLTg0LjEgOC45LTEyNC44IDExLjQtNDIuMiAzMS04MS4xIDU4LjEtMTE1LjggMjcuMi0zNC43IDYwLjMtNjMuMiA5OC40LTg0LjMgMzctMjAuNiA3Ni45LTMzLjYgMTE5LjEtMzguOHoiIC8+PHBhdGggZD0iTTg4MCA0MThIMzUyYy0xNy43IDAtMzIgMTQuMy0zMiAzMnY0MTRjMCAxNy43IDE0LjMgMzIgMzIgMzJoNTI4YzE3LjcgMCAzMi0xNC4zIDMyLTMyVjQ1MGMwLTE3LjctMTQuMy0zMi0zMi0zMnptLTQ0IDQwMkgzOTZWNDk0aDQ0MHYzMjZ6IiAvPjwvc3ZnPg==) */
/**![setting](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAiIGhlaWdodD0iNTAiIGZpbGw9IiNjYWNhY2EiIHZpZXdCb3g9IjY0IDY0IDg5NiA4OTYiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTkyNC44IDYyNS43bC02NS41LTU2YzMuMS0xOSA0LjctMzguNCA0LjctNTcuOHMtMS42LTM4LjgtNC43LTU3LjhsNjUuNS01NmEzMi4wMyAzMi4wMyAwIDAwOS4zLTM1LjJsLS45LTIuNmE0NDMuNzQgNDQzLjc0IDAgMDAtNzkuNy0xMzcuOWwtMS44LTIuMWEzMi4xMiAzMi4xMiAwIDAwLTM1LjEtOS41bC04MS4zIDI4LjljLTMwLTI0LjYtNjMuNS00NC05OS43LTU3LjZsLTE1LjctODVhMzIuMDUgMzIuMDUgMCAwMC0yNS44LTI1LjdsLTIuNy0uNWMtNTIuMS05LjQtMTA2LjktOS40LTE1OSAwbC0yLjcuNWEzMi4wNSAzMi4wNSAwIDAwLTI1LjggMjUuN2wtMTUuOCA4NS40YTM1MS44NiAzNTEuODYgMCAwMC05OSA1Ny40bC04MS45LTI5LjFhMzIgMzIgMCAwMC0zNS4xIDkuNWwtMS44IDIuMWE0NDYuMDIgNDQ2LjAyIDAgMDAtNzkuNyAxMzcuOWwtLjkgMi42Yy00LjUgMTIuNS0uOCAyNi41IDkuMyAzNS4ybDY2LjMgNTYuNmMtMy4xIDE4LjgtNC42IDM4LTQuNiA1Ny4xIDAgMTkuMiAxLjUgMzguNCA0LjYgNTcuMUw5OSA2MjUuNWEzMi4wMyAzMi4wMyAwIDAwLTkuMyAzNS4ybC45IDIuNmMxOC4xIDUwLjQgNDQuOSA5Ni45IDc5LjcgMTM3LjlsMS44IDIuMWEzMi4xMiAzMi4xMiAwIDAwMzUuMSA5LjVsODEuOS0yOS4xYzI5LjggMjQuNSA2My4xIDQzLjkgOTkgNTcuNGwxNS44IDg1LjRhMzIuMDUgMzIuMDUgMCAwMDI1LjggMjUuN2wyLjcuNWE0NDkuNCA0NDkuNCAwIDAwMTU5IDBsMi43LS41YTMyLjA1IDMyLjA1IDAgMDAyNS44LTI1LjdsMTUuNy04NWEzNTAgMzUwIDAgMDA5OS43LTU3LjZsODEuMyAyOC45YTMyIDMyIDAgMDAzNS4xLTkuNWwxLjgtMi4xYzM0LjgtNDEuMSA2MS42LTg3LjUgNzkuNy0xMzcuOWwuOS0yLjZjNC41LTEyLjMuOC0yNi4zLTkuMy0zNXpNNzg4LjMgNDY1LjljMi41IDE1LjEgMy44IDMwLjYgMy44IDQ2LjFzLTEuMyAzMS0zLjggNDYuMWwtNi42IDQwLjEgNzQuNyA2My45YTM3MC4wMyAzNzAuMDMgMCAwMS00Mi42IDczLjZMNzIxIDcwMi44bC0zMS40IDI1LjhjLTIzLjkgMTkuNi01MC41IDM1LTc5LjMgNDUuOGwtMzguMSAxNC4zLTE3LjkgOTdhMzc3LjUgMzc3LjUgMCAwMS04NSAwbC0xNy45LTk3LjItMzcuOC0xNC41Yy0yOC41LTEwLjgtNTUtMjYuMi03OC43LTQ1LjdsLTMxLjQtMjUuOS05My40IDMzLjJjLTE3LTIyLjktMzEuMi00Ny42LTQyLjYtNzMuNmw3NS41LTY0LjUtNi41LTQwYy0yLjQtMTQuOS0zLjctMzAuMy0zLjctNDUuNSAwLTE1LjMgMS4yLTMwLjYgMy43LTQ1LjVsNi41LTQwLTc1LjUtNjQuNWMxMS4zLTI2LjEgMjUuNi01MC43IDQyLjYtNzMuNmw5My40IDMzLjIgMzEuNC0yNS45YzIzLjctMTkuNSA1MC4yLTM0LjkgNzguNy00NS43bDM3LjktMTQuMyAxNy45LTk3LjJjMjguMS0zLjIgNTYuOC0zLjIgODUgMGwxNy45IDk3IDM4LjEgMTQuM2MyOC43IDEwLjggNTUuNCAyNi4yIDc5LjMgNDUuOGwzMS40IDI1LjggOTIuOC0zMi45YzE3IDIyLjkgMzEuMiA0Ny42IDQyLjYgNzMuNkw3ODEuOCA0MjZsNi41IDM5Ljl6TTUxMiAzMjZjLTk3LjIgMC0xNzYgNzguOC0xNzYgMTc2czc4LjggMTc2IDE3NiAxNzYgMTc2LTc4LjggMTc2LTE3Ni03OC44LTE3Ni0xNzYtMTc2em03OS4yIDI1NS4yQTExMS42IDExMS42IDAgMDE1MTIgNjE0Yy0yOS45IDAtNTgtMTEuNy03OS4yLTMyLjhBMTExLjYgMTExLjYgMCAwMTQwMCA1MDJjMC0yOS45IDExLjctNTggMzIuOC03OS4yQzQ1NCA0MDEuNiA0ODIuMSAzOTAgNTEyIDM5MGMyOS45IDAgNTggMTEuNiA3OS4yIDMyLjhBMTExLjYgMTExLjYgMCAwMTYyNCA1MDJjMCAyOS45LTExLjcgNTgtMzIuOCA3OS4yeiIgLz48L3N2Zz4=) */
/**![star](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAiIGhlaWdodD0iNTAiIGZpbGw9IiNjYWNhY2EiIHZpZXdCb3g9IjY0IDY0IDg5NiA4OTYiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTkwOC4xIDM1My4xbC0yNTMuOS0zNi45TDU0MC43IDg2LjFjLTMuMS02LjMtOC4yLTExLjQtMTQuNS0xNC41LTE1LjgtNy44LTM1LTEuMy00Mi45IDE0LjVMMzY5LjggMzE2LjJsLTI1My45IDM2LjljLTcgMS0xMy40IDQuMy0xOC4zIDkuM2EzMi4wNSAzMi4wNSAwIDAwLjYgNDUuM2wxODMuNyAxNzkuMS00My40IDI1Mi45YTMxLjk1IDMxLjk1IDAgMDA0Ni40IDMzLjdMNTEyIDc1NGwyMjcuMSAxMTkuNGM2LjIgMy4zIDEzLjQgNC40IDIwLjMgMy4yIDE3LjQtMyAyOS4xLTE5LjUgMjYuMS0zNi45bC00My40LTI1Mi45IDE4My43LTE3OS4xYzUtNC45IDguMy0xMS4zIDkuMy0xOC4zIDIuNy0xNy41LTkuNS0zMy43LTI3LTM2LjN6IiAvPjwvc3ZnPg==) */
/**![swap](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAiIGhlaWdodD0iNTAiIGZpbGw9IiNjYWNhY2EiIHZpZXdCb3g9IjY0IDY0IDg5NiA4OTYiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTg0Ny45IDU5MkgxNTJjLTQuNCAwLTggMy42LTggOHY2MGMwIDQuNCAzLjYgOCA4IDhoNjA1LjJMNjEyLjkgODUxYy00LjEgNS4yLS40IDEzIDYuMyAxM2g3Mi41YzQuOSAwIDkuNS0yLjIgMTIuNi02LjFsMTY4LjgtMjE0LjFjMTYuNS0yMSAxLjYtNTEuOC0yNS4yLTUxLjh6TTg3MiAzNTZIMjY2LjhsMTQ0LjMtMTgzYzQuMS01LjIuNC0xMy02LjMtMTNoLTcyLjVjLTQuOSAwLTkuNSAyLjItMTIuNiA2LjFMMTUwLjkgMzgwLjJjLTE2LjUgMjEtMS42IDUxLjggMjUuMSA1MS44aDY5NmM0LjQgMCA4LTMuNiA4LTh2LTYwYzAtNC40LTMuNi04LTgtOHoiIC8+PC9zdmc+) */
/**![vertical-align-bottom](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAiIGhlaWdodD0iNTAiIGZpbGw9IiNjYWNhY2EiIHZpZXdCb3g9IjY0IDY0IDg5NiA4OTYiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTg1OS45IDc4MEgxNjQuMWMtNC41IDAtOC4xIDMuNi04LjEgOHY2MGMwIDQuNCAzLjYgOCA4LjEgOGg2OTUuOGM0LjUgMCA4LjEtMy42IDguMS04di02MGMwLTQuNC0zLjYtOC04LjEtOHpNNTA1LjcgNjY5YTggOCAwIDAwMTIuNiAwbDExMi0xNDEuN2M0LjEtNS4yLjQtMTIuOS02LjMtMTIuOWgtNzQuMVYxNzZjMC00LjQtMy42LTgtOC04aC02MGMtNC40IDAtOCAzLjYtOCA4djMzOC4zSDQwMGMtNi43IDAtMTAuNCA3LjctNi4zIDEyLjlsMTEyIDE0MS44eiIgLz48L3N2Zz4=) */
/**![vertical-align-middle](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAiIGhlaWdodD0iNTAiIGZpbGw9IiNjYWNhY2EiIHZpZXdCb3g9IjY0IDY0IDg5NiA4OTYiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTg1OS45IDQ3NEgxNjQuMWMtNC41IDAtOC4xIDMuNi04LjEgOHY2MGMwIDQuNCAzLjYgOCA4LjEgOGg2OTUuOGM0LjUgMCA4LjEtMy42IDguMS04di02MGMwLTQuNC0zLjYtOC04LjEtOHptLTM1My42LTc0LjdjMi45IDMuNyA4LjUgMy43IDExLjMgMGwxMDAuOC0xMjcuNWMzLjctNC43LjQtMTEuNy01LjctMTEuN0g1NTBWMTA0YzAtNC40LTMuNi04LTgtOGgtNjBjLTQuNCAwLTggMy42LTggOHYxNTZoLTYyLjhjLTYgMC05LjQgNy01LjcgMTEuN2wxMDAuOCAxMjcuNnptMTEuNCAyMjUuNGE3LjE0IDcuMTQgMCAwMC0xMS4zIDBMNDA1LjYgNzUyLjNhNy4yMyA3LjIzIDAgMDA1LjcgMTEuN0g0NzR2MTU2YzAgNC40IDMuNiA4IDggOGg2MGM0LjQgMCA4LTMuNiA4LThWNzY0aDYyLjhjNiAwIDkuNC03IDUuNy0xMS43TDUxNy43IDYyNC43eiIgLz48L3N2Zz4=) */
/**![vertical-align-top](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAiIGhlaWdodD0iNTAiIGZpbGw9IiNjYWNhY2EiIHZpZXdCb3g9IjY0IDY0IDg5NiA4OTYiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTg1OS45IDE2OEgxNjQuMWMtNC41IDAtOC4xIDMuNi04LjEgOHY2MGMwIDQuNCAzLjYgOCA4LjEgOGg2OTUuOGM0LjUgMCA4LjEtMy42IDguMS04di02MGMwLTQuNC0zLjYtOC04LjEtOHpNNTE4LjMgMzU1YTggOCAwIDAwLTEyLjYgMGwtMTEyIDE0MS43YTcuOTggNy45OCAwIDAwNi4zIDEyLjloNzMuOVY4NDhjMCA0LjQgMy42IDggOCA4aDYwYzQuNCAwIDgtMy42IDgtOFY1MDkuN0g2MjRjNi43IDAgMTAuNC03LjcgNi4zLTEyLjlMNTE4LjMgMzU1eiIgLz48L3N2Zz4=) */
/**![zoom-in](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAiIGhlaWdodD0iNTAiIGZpbGw9IiNjYWNhY2EiIHZpZXdCb3g9IjY0IDY0IDg5NiA4OTYiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTYzNyA0NDNINTE5VjMwOWMwLTQuNC0zLjYtOC04LThoLTYwYy00LjQgMC04IDMuNi04IDh2MTM0SDMyNWMtNC40IDAtOCAzLjYtOCA4djYwYzAgNC40IDMuNiA4IDggOGgxMTh2MTM0YzAgNC40IDMuNiA4IDggOGg2MGM0LjQgMCA4LTMuNiA4LThWNTE5aDExOGM0LjQgMCA4LTMuNiA4LTh2LTYwYzAtNC40LTMuNi04LTgtOHptMjg0IDQyNEw3NzUgNzIxYzEyMi4xLTE0OC45IDExMy42LTM2OS41LTI2LTUwOS0xNDgtMTQ4LjEtMzg4LjQtMTQ4LjEtNTM3IDAtMTQ4LjEgMTQ4LjYtMTQ4LjEgMzg5IDAgNTM3IDEzOS41IDEzOS42IDM2MC4xIDE0OC4xIDUwOSAyNmwxNDYgMTQ2YzMuMiAyLjggOC4zIDIuOCAxMSAwbDQzLTQzYzIuOC0yLjcgMi44LTcuOCAwLTExek02OTYgNjk2Yy0xMTguOCAxMTguNy0zMTEuMiAxMTguNy00MzAgMC0xMTguNy0xMTguOC0xMTguNy0zMTEuMiAwLTQzMCAxMTguOC0xMTguNyAzMTEuMi0xMTguNyA0MzAgMCAxMTguNyAxMTguOCAxMTguNyAzMTEuMiAwIDQzMHoiIC8+PC9zdmc+) */
/**![zoom-out](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAiIGhlaWdodD0iNTAiIGZpbGw9IiNjYWNhY2EiIHZpZXdCb3g9IjY0IDY0IDg5NiA4OTYiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTYzNyA0NDNIMzI1Yy00LjQgMC04IDMuNi04IDh2NjBjMCA0LjQgMy42IDggOCA4aDMxMmM0LjQgMCA4LTMuNiA4LTh2LTYwYzAtNC40LTMuNi04LTgtOHptMjg0IDQyNEw3NzUgNzIxYzEyMi4xLTE0OC45IDExMy42LTM2OS41LTI2LTUwOS0xNDgtMTQ4LjEtMzg4LjQtMTQ4LjEtNTM3IDAtMTQ4LjEgMTQ4LjYtMTQ4LjEgMzg5IDAgNTM3IDEzOS41IDEzOS42IDM2MC4xIDE0OC4xIDUwOSAyNmwxNDYgMTQ2YzMuMiAyLjggOC4zIDIuOCAxMSAwbDQzLTQzYzIuOC0yLjcgMi44LTcuOCAwLTExek02OTYgNjk2Yy0xMTguOCAxMTguNy0zMTEuMiAxMTguNy00MzAgMC0xMTguNy0xMTguOC0xMTguNy0zMTEuMiAwLTQzMCAxMTguOC0xMTguNyAzMTEuMi0xMTguNyA0MzAgMCAxMTguNyAxMTguOCAxMTguNyAzMTEuMiAwIDQzMHoiIC8+PC9zdmc+) */

View File

@ -1 +0,0 @@
/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */

View File

@ -1,27 +1,191 @@
package com.zcloud.personnel.positioning.command;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zcloud.personnel.positioning.persistence.dataobject.OrgLocalCorpDO;
import com.zcloud.personnel.positioning.persistence.dataobject.OrgLocalDepartmentDO;
import com.zcloud.personnel.positioning.persistence.dataobject.OrgMatchSnapshotDO;
import com.zcloud.personnel.positioning.persistence.repository.OrgMatchRepository;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.time.LocalDateTime;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/** Resolves a FindS organization snapshot to the local organization master data. */
@Component
@RequiredArgsConstructor
public class FindsCorpMappingService {
private static final String DEFAULT_CONFIG_CODE = "FINDS";
private final OrgMatchRepository orgMatchRepository;
private final ObjectMapper objectMapper;
@Autowired
public FindsCorpMappingService(OrgMatchRepository orgMatchRepository, ObjectMapper objectMapper) {
this.orgMatchRepository = orgMatchRepository;
this.objectMapper = objectMapper;
}
public FindsCorpMappingService(OrgMatchRepository orgMatchRepository) {
this(orgMatchRepository, new ObjectMapper());
}
public PathSyncSummary synchronizePaths(Collection<List<String>> sourcePaths,
LocalDateTime syncTime) {
LocalDateTime effectiveSyncTime = syncTime == null
? LocalDateTime.now().withNano(0) : syncTime.withNano(0);
Set<List<String>> paths = cleanDistinctPaths(sourcePaths);
List<OrgLocalCorpDO> corps = listAllCorps();
Map<String, List<OrgLocalCorpDO>> exact = indexCorps(corps, false);
Map<String, List<OrgLocalCorpDO>> normalized = indexCorps(corps, true);
PathSyncSummary summary = new PathSyncSummary(paths.size(), distinctNodeCount(paths));
for (List<String> path : paths) {
PathMatch match = matchPath(path, exact, normalized);
savePath(path, match, effectiveSyncTime);
summary.add(match.status);
}
orgMatchRepository.disableMissing(DEFAULT_CONFIG_CODE, effectiveSyncTime);
return summary;
}
private PathMatch matchPath(List<String> path,
Map<String, List<OrgLocalCorpDO>> exact,
Map<String, List<OrgLocalCorpDO>> normalized) {
// FindS 的末级组织名称对应 finds_department_name只允许它匹配本地企业。
// 上级节点可能是“二公司”等管理单位,回退匹配会把下属相关方错误归到上级企业。
String departmentName = path.get(path.size() - 1);
PathMatch exactMatch = matchNode(departmentName, exact.get(departmentName),
"FINDS_DEPARTMENT_CORP_NAME");
if (exactMatch != null) {
return exactMatch;
}
String normalizedName = normalizeCompanyName(departmentName);
PathMatch normalizedMatch = matchNode(departmentName, normalized.get(normalizedName),
"FINDS_DEPARTMENT_CORP_NAME_NORMALIZED");
return normalizedMatch == null ? PathMatch.unmatched() : normalizedMatch;
}
private PathMatch matchNode(String node, List<OrgLocalCorpDO> candidates, String matchType) {
if (candidates == null || candidates.isEmpty()) {
return null;
}
Map<Long, OrgLocalCorpDO> distinct = new LinkedHashMap<>();
for (OrgLocalCorpDO candidate : candidates) {
if (candidate != null && candidate.getCorpinfoId() != null) {
distinct.putIfAbsent(candidate.getCorpinfoId(), candidate);
}
}
if (distinct.size() > 1) {
return PathMatch.ambiguous(node, matchType);
}
if (distinct.isEmpty()) {
return null;
}
OrgLocalCorpDO corp = distinct.values().iterator().next();
return PathMatch.matched(node, matchType, corp.getCorpinfoId(), corp.getCorpinfoName());
}
private List<OrgLocalCorpDO> listAllCorps() {
Map<String, Object> params = new LinkedHashMap<>();
params.put("allCorp", true);
return orgMatchRepository.listCorpCandidates(params);
}
private Map<String, List<OrgLocalCorpDO>> indexCorps(List<OrgLocalCorpDO> corps,
boolean normalized) {
Map<String, List<OrgLocalCorpDO>> index = new HashMap<>();
for (OrgLocalCorpDO corp : corps == null ? Collections.<OrgLocalCorpDO>emptyList() : corps) {
String key = normalized ? normalizeCompanyName(corp.getCorpinfoName()) : clean(corp.getCorpinfoName());
if (StringUtils.hasText(key)) {
index.computeIfAbsent(key, ignored -> new ArrayList<>()).add(corp);
}
}
return index;
}
private Set<List<String>> cleanDistinctPaths(Collection<List<String>> sourcePaths) {
Map<String, List<String>> pathsByDepartmentName = new LinkedHashMap<>();
if (sourcePaths == null) {
return Collections.emptySet();
}
for (List<String> sourcePath : sourcePaths) {
List<String> path = new ArrayList<>();
if (sourcePath != null) {
for (String node : sourcePath) {
if (StringUtils.hasText(node)) {
path.add(node.trim());
}
}
}
if (!path.isEmpty()) {
// 企业匹配键是 finds_department_name同名末级组织只保留一条映射记录。
pathsByDepartmentName.putIfAbsent(path.get(path.size() - 1), path);
}
}
return new LinkedHashSet<>(pathsByDepartmentName.values());
}
private int distinctNodeCount(Set<List<String>> paths) {
Set<String> nodes = new LinkedHashSet<>();
for (List<String> path : paths) {
nodes.addAll(path);
}
return nodes.size();
}
private void savePath(List<String> path, PathMatch match, LocalDateTime syncTime) {
String json = pathJson(path);
Map<String, Object> params = new LinkedHashMap<>();
params.put("sourceConfigCode", DEFAULT_CONFIG_CODE);
params.put("findsCorpinfoId", null);
params.put("findsCorpinfoName", path.get(0));
params.put("findsDepartmentName", path.get(path.size() - 1));
params.put("findsOrgNamePath", json);
params.put("findsOrgPathHash", sha256(json));
params.put("matchedPathNode", match.node);
params.put("localCorpinfoId", match.corpinfoId);
params.put("localCorpinfoName", match.corpinfoName);
params.put("localDepartmentId", null);
params.put("localDepartmentName", null);
params.put("matchStatus", match.status);
params.put("matchType", match.matchType);
params.put("syncTime", syncTime);
orgMatchRepository.upsert(params);
}
private String pathJson(List<String> path) {
try {
return objectMapper.writeValueAsString(path);
} catch (JsonProcessingException e) {
throw new IllegalStateException("Cannot serialize FindS orgNamePath", e);
}
}
private String sha256(String value) {
try {
byte[] digest = MessageDigest.getInstance("SHA-256")
.digest(value.getBytes(StandardCharsets.UTF_8));
StringBuilder hex = new StringBuilder(digest.length * 2);
for (byte item : digest) {
hex.append(String.format("%02x", item & 0xff));
}
return hex.toString();
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("SHA-256 is unavailable", e);
}
}
public CorpMatch resolve(String sourceConfigCode, String findsCorpinfoId, String findsCorpinfoName,
String findsDepartmentName, String creditCode) {
@ -38,24 +202,48 @@ public class FindsCorpMappingService {
OrgMatchSnapshotDO saved = StringUtils.hasText(corpName) || StringUtils.hasText(departmentName)
? orgMatchRepository.findMatched(configCode, emptyIfBlank(corpName), emptyIfBlank(departmentName))
: null;
if (saved != null && saved.getLocalCorpinfoId() != null) {
if (!reusableSavedMatch(saved)) {
String mappingName = firstText(departmentName, corpName);
saved = StringUtils.hasText(mappingName)
? orgMatchRepository.findMatchedByDepartmentName(configCode, mappingName)
: null;
}
boolean childCompany = StringUtils.hasText(departmentName);
if (reusableSavedMatch(saved)) {
orgMatchRepository.touch(configCode, emptyIfBlank(corpName),
emptyIfBlank(departmentName), LocalDateTime.now());
return fromSaved(saved);
}
MatchAttempt attempt = queryCorp("CORP_ID", "corpinfoId", validCorpinfoId(sourceCorpId));
if (!hasCandidates(attempt)) {
attempt = queryCorp("CORP_CODE", "creditCode", sourceCreditCode);
}
if (!hasCandidates(attempt)) {
attempt = queryCorp("CORP_NAME", "corpinfoName", corpName);
}
if (!hasCandidates(attempt)) {
attempt = queryDepartment(departmentName);
}
if (!hasCandidates(attempt) && StringUtils.hasText(corpName) && corpName.length() >= 3) {
attempt = queryCorp("CORP_NAME_SUFFIX", "corpinfoNameSuffix", escapeLike(corpName));
MatchAttempt attempt = null;
if (childCompany) {
// FindS models companies below its single root company as departments.
attempt = queryCorp("CHILD_CORP_NAME", "corpinfoName", departmentName);
if (!hasCandidates(attempt) && departmentName.length() >= 3) {
attempt = queryCorp("CHILD_CORP_NAME_SUFFIX", "corpinfoNameSuffix",
escapeLike(departmentName));
}
if (!hasCandidates(attempt)) {
attempt = queryCorpByNormalizedName("CHILD_CORP_NAME_NORMALIZED", departmentName);
}
} else {
attempt = queryCorp("CORP_ID", "corpinfoId", validCorpinfoId(sourceCorpId));
if (!hasCandidates(attempt)) {
attempt = queryCorp("CORP_CODE", "creditCode", sourceCreditCode);
}
if (!hasCandidates(attempt)) {
attempt = queryCorp("CORP_NAME", "corpinfoName", corpName);
}
if (!hasCandidates(attempt) && StringUtils.hasText(corpName) && corpName.length() >= 3) {
attempt = queryCorp("CORP_NAME_SUFFIX", "corpinfoNameSuffix", escapeLike(corpName));
}
if (!hasCandidates(attempt)) {
attempt = queryCorpByNormalizedName("CORP_NAME_NORMALIZED", corpName);
}
if (!hasCandidates(attempt)) {
attempt = queryCorp("CORP_NAME_ORDERED", "corpinfoNameOrderedPattern",
orderedLikePattern(corpName));
}
}
CorpMatch resolved = attempt == null ? null : attempt.uniqueMatch();
@ -82,16 +270,28 @@ public class FindsCorpMappingService {
return distinct(matchType, matches);
}
private MatchAttempt queryDepartment(String departmentName) {
if (!StringUtils.hasText(departmentName)) {
private MatchAttempt queryCorpByNormalizedName(String matchType, String name) {
String normalizedName = normalizeCompanyName(name);
if (!StringUtils.hasText(normalizedName)) {
return null;
}
Map<String, Object> params = new LinkedHashMap<>();
params.put("allCorp", true);
List<CorpMatch> matches = new ArrayList<>();
for (OrgLocalDepartmentDO row : orgMatchRepository.listDepartmentCandidates(departmentName)) {
matches.add(new CorpMatch(row.getRootCorpinfoId(), row.getRootCorpinfoName(),
row.getDepartmentId(), row.getDepartmentName(), "DEPARTMENT_NAME"));
for (OrgLocalCorpDO row : orgMatchRepository.listCorpCandidates(params)) {
if (normalizedName.equals(normalizeCompanyName(row.getCorpinfoName()))) {
matches.add(new CorpMatch(row.getCorpinfoId(), row.getCorpinfoName(),
null, null, matchType));
}
}
return distinct("DEPARTMENT_NAME", matches);
return distinct(matchType, matches);
}
private boolean reusableSavedMatch(OrgMatchSnapshotDO saved) {
if (saved == null || saved.getLocalCorpinfoId() == null) {
return false;
}
return saved.getMatchType() != null && !"DEPARTMENT_NAME".equals(saved.getMatchType());
}
private MatchAttempt distinct(String matchType, List<CorpMatch> rows) {
@ -111,6 +311,13 @@ public class FindsCorpMappingService {
params.put("findsCorpinfoId", sourceCorpId);
params.put("findsCorpinfoName", emptyIfBlank(corpName));
params.put("findsDepartmentName", emptyIfBlank(departmentName));
String fallbackPath = pathJson(StringUtils.hasText(departmentName)
? java.util.Arrays.asList(corpName, departmentName)
: Collections.singletonList(corpName));
params.put("findsOrgNamePath", fallbackPath);
params.put("findsOrgPathHash", sha256(fallbackPath));
params.put("matchedPathNode", match == null ? null
: firstText(departmentName, corpName));
params.put("localCorpinfoId", match == null ? null : match.corpinfoId);
params.put("localCorpinfoName", match == null ? null : match.corpinfoName);
params.put("localDepartmentId", match == null ? null : match.departmentId);
@ -157,6 +364,39 @@ public class FindsCorpMappingService {
return value.replace("\\", "\\\\").replace("%", "\\%").replace("_", "\\_");
}
private String normalizeCompanyName(String value) {
if (!StringUtils.hasText(value)) {
return null;
}
String normalized = value.trim().toLowerCase()
.replaceAll("[\\s\\u3000()\\[\\]【】._\\-—,/\\\\]", "");
String[] suffixes = {"集团有限责任公司", "股份有限责任公司", "有限责任公司",
"集团有限公司", "股份有限公司", "集团公司", "有限公司", "分公司", "公司"};
for (String suffix : suffixes) {
if (normalized.endsWith(suffix) && normalized.length() > suffix.length()) {
return normalized.substring(0, normalized.length() - suffix.length());
}
}
return normalized;
}
private String orderedLikePattern(String value) {
if (!StringUtils.hasText(value)) {
return null;
}
StringBuilder pattern = new StringBuilder("%");
int characterCount = 0;
for (int i = 0; i < value.length(); i++) {
char character = value.charAt(i);
if (!Character.isLetterOrDigit(character)) {
continue;
}
pattern.append(escapeLike(String.valueOf(character))).append('%');
characterCount++;
}
return characterCount >= 4 ? pattern.toString() : null;
}
@Getter
public static class CorpMatch {
private final Long corpinfoId;
@ -188,4 +428,61 @@ public class FindsCorpMappingService {
return candidates.size() == 1 ? candidates.get(0) : null;
}
}
private static class PathMatch {
private final String status;
private final String matchType;
private final String node;
private final Long corpinfoId;
private final String corpinfoName;
private PathMatch(String status, String matchType, String node,
Long corpinfoId, String corpinfoName) {
this.status = status;
this.matchType = matchType;
this.node = node;
this.corpinfoId = corpinfoId;
this.corpinfoName = corpinfoName;
}
private static PathMatch matched(String node, String type, Long id, String name) {
return new PathMatch("MATCHED", type, node, id, name);
}
private static PathMatch ambiguous(String node, String type) {
return new PathMatch("AMBIGUOUS", type, node, null, null);
}
private static PathMatch unmatched() {
return new PathMatch("UNMATCHED", null, null, null, null);
}
}
@Getter
public static class PathSyncSummary {
private final int distinctPathCount;
private final int distinctNodeCount;
private int matchedCount;
private int ambiguousCount;
private int unmatchedCount;
private PathSyncSummary(int distinctPathCount, int distinctNodeCount) {
this.distinctPathCount = distinctPathCount;
this.distinctNodeCount = distinctNodeCount;
}
public static PathSyncSummary empty() {
return new PathSyncSummary(0, 0);
}
private void add(String status) {
if ("MATCHED".equals(status)) {
matchedCount++;
} else if ("AMBIGUOUS".equals(status)) {
ambiguousCount++;
} else {
unmatchedCount++;
}
}
}
}

View File

@ -39,16 +39,28 @@ public class FindsStaffMappingSyncService {
private final PositionPersonLocalLookupService localLookupService;
private final PersonnelMatchRepository personnelMatchRepository;
private final FindsCorpMappingService findsCorpMappingService;
private FindsCorpMappingService.PathSyncSummary lastOrgPathSummary =
FindsCorpMappingService.PathSyncSummary.empty();
public FindsCorpMappingService.PathSyncSummary getLastOrgPathSummary() {
return lastOrgPathSummary;
}
/** 拉取 FindS 完整组织路径并单独刷新企业映射,不执行人员映射落库。 */
public FindsCorpMappingService.PathSyncSummary syncOrganizations() {
LocalDateTime syncTime = LocalDateTime.now().withNano(0);
return synchronizeOrganizations(loadAllStaff(), syncTime);
}
public int syncAll() {
// The schema uses second precision; keep the same precision for the stale-row cutoff.
LocalDateTime syncTime = LocalDateTime.now().withNano(0);
List<StaffSnapshot> staffRows = loadAllStaff();
FindsCorpMappingService.PathSyncSummary orgSummary =
synchronizeOrganizations(staffRows, syncTime);
PersonIndexes localPeople = new PersonIndexes(localLookupService.findAllPeople());
int matchedCount = 0;
for (StaffSnapshot row : staffRows) {
findsCorpMappingService.resolve(SOURCE_CONFIG_CODE, row.findsCorpinfoId,
row.corpinfoName, row.departmentName, null);
LocalMatch localMatch = localPeople.match(row);
upsert(row, localMatch, syncTime);
if (localMatch.person != null) {
@ -56,11 +68,37 @@ public class FindsStaffMappingSyncService {
}
}
disableMissingRows(syncTime);
LOGGER.info("FindS staff mapping synchronization completed, total={}, matched={}",
staffRows.size(), matchedCount);
LOGGER.info("FindS staff mapping synchronization completed, total={}, matched={}, "
+ "orgPaths={}, orgNodes={}, orgMatched={}, orgAmbiguous={}, orgUnmatched={}",
staffRows.size(), matchedCount, orgSummary.getDistinctPathCount(),
orgSummary.getDistinctNodeCount(), orgSummary.getMatchedCount(),
orgSummary.getAmbiguousCount(), orgSummary.getUnmatchedCount());
return staffRows.size();
}
private FindsCorpMappingService.PathSyncSummary synchronizeOrganizations(
List<StaffSnapshot> staffRows, LocalDateTime syncTime) {
Set<List<String>> orgPaths = new LinkedHashSet<>();
for (StaffSnapshot row : staffRows) {
if (row.orgNamePath != null && !row.orgNamePath.isEmpty()) {
orgPaths.add(row.orgNamePath);
}
}
FindsCorpMappingService.PathSyncSummary orgSummary =
findsCorpMappingService.synchronizePaths(orgPaths, syncTime);
if (orgSummary == null) {
orgSummary = FindsCorpMappingService.PathSyncSummary.empty();
}
lastOrgPathSummary = orgSummary;
if (orgPaths.isEmpty()) {
for (StaffSnapshot row : staffRows) {
findsCorpMappingService.resolve(SOURCE_CONFIG_CODE, row.findsCorpinfoId,
row.corpinfoName, row.departmentName, null);
}
}
return orgSummary;
}
private List<StaffSnapshot> loadAllStaff() {
List<StaffSnapshot> result = new ArrayList<>();
JsonNode firstData = successData(
@ -141,11 +179,12 @@ public class FindsStaffMappingSyncService {
snapshot.staffName = firstText(text(firstPresent(row, "staffName", "name", "realName", "userName")));
snapshot.findsCorpinfoId = firstText(text(firstPresent(row,
"corpinfoId", "companyId", "corpId", "enterpriseId")));
snapshot.orgNamePath = orgNamePath(row.path("orgNamePath"));
// FindS staff rows carry the org hierarchy as orgNamePath (root-to-leaf, e.g.
// ["秦港股份","杂货公司","机械一部"]) plus a single orgName for the leaf (assigned) node.
// There is no dedicated company-name field, so the path root doubles as the company name.
snapshot.corpinfoName = firstText(text(firstPresent(row,
"companyName", "corpName", "enterpriseName")), firstArrayElementText(row.path("orgNamePath")));
"companyName", "corpName", "enterpriseName")), firstPathNode(snapshot.orgNamePath));
snapshot.departmentName = firstText(text(firstPresent(row,
"departmentName", "deptName", "orgDepartmentName")), text(firstPresent(row, "orgName")));
snapshot.terminalNo = firstText(
@ -157,6 +196,23 @@ public class FindsStaffMappingSyncService {
return snapshot;
}
private List<String> orgNamePath(JsonNode pathNode) {
List<String> path = new ArrayList<>();
if (pathNode != null && pathNode.isArray()) {
for (JsonNode node : pathNode) {
String value = text(node);
if (StringUtils.hasText(value)) {
path.add(value.trim());
}
}
}
return path;
}
private String firstPathNode(List<String> path) {
return path == null || path.isEmpty() ? null : path.get(0);
}
private void upsert(StaffSnapshot row, LocalMatch localMatch, LocalDateTime syncTime) {
PositionPersonLocalLookupService.LocalPerson local = localMatch.person;
String matchStatus = localMatch.status;
@ -292,6 +348,7 @@ public class FindsStaffMappingSyncService {
private String findsCorpinfoId;
private String corpinfoName;
private String departmentName;
private List<String> orgNamePath;
private String terminalNo;
}

View File

@ -1,82 +0,0 @@
//package com.zcloud.personnel.positioning.command;
//
//import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
//import com.fasterxml.jackson.databind.JsonNode;
//import com.fasterxml.jackson.databind.ObjectMapper;
//import com.zcloud.personnel.positioning.integration.finds.FindsOpenApiClient;
//import com.zcloud.personnel.positioning.persistence.dataobject.AlarmRecordDO;
//import com.zcloud.personnel.positioning.persistence.dataobject.AlarmRecordTargetDO;
//import com.zcloud.personnel.positioning.persistence.dataobject.OpenapiConfigDO;
//import com.zcloud.personnel.positioning.persistence.repository.AlarmRecordRepository;
//import com.zcloud.personnel.positioning.persistence.repository.AlarmRecordTargetRepository;
//import com.zcloud.personnel.positioning.persistence.repository.OpenapiConfigRepository;
//import com.zcloud.personnel.positioning.persistence.repository.SyncTaskRepository;
//import org.junit.jupiter.api.BeforeEach;
//import org.junit.jupiter.api.Test;
//import org.mockito.ArgumentCaptor;
//import org.springframework.transaction.support.TransactionTemplate;
//
//import static org.junit.jupiter.api.Assertions.assertEquals;
//import static org.junit.jupiter.api.Assertions.assertFalse;
//import static org.junit.jupiter.api.Assertions.assertTrue;
//import static org.mockito.ArgumentMatchers.any;
//import static org.mockito.ArgumentMatchers.eq;
//import static org.mockito.Mockito.mock;
//import static org.mockito.Mockito.verify;
//import static org.mockito.Mockito.when;
//
//class AlarmRecordFindsPullServiceTest {
// private final ObjectMapper objectMapper = new ObjectMapper();
// private AlarmRecordRepository alarmRecordRepository;
// private AlarmRecordTargetRepository alarmRecordTargetRepository;
// private FindsPersonCorpResolver personCorpResolver;
// private AlarmRecordFindsPullService service;
//
// @BeforeEach
// void setUp() {
// alarmRecordRepository = mock(AlarmRecordRepository.class);
// alarmRecordTargetRepository = mock(AlarmRecordTargetRepository.class);
// personCorpResolver = mock(FindsPersonCorpResolver.class);
// service = new AlarmRecordFindsPullService(
// mock(FindsOpenApiClient.class),
// mock(OpenapiConfigRepository.class),
// alarmRecordRepository,
// alarmRecordTargetRepository,
// mock(SyncTaskRepository.class),
// objectMapper,
// mock(TransactionTemplate.class),
// mock(FindsCorpMappingService.class),
// personCorpResolver);
// }
//
// @Test
// void existingAlarmIsFoundWithoutCompanyConditionAndTargetUsesResolvedCompany() throws Exception {
// AlarmRecordDO existing = new AlarmRecordDO();
// existing.setId(100L);
// when(alarmRecordRepository.getOne(any(QueryWrapper.class), eq(false))).thenReturn(existing);
// FindsCorpMappingService.CorpMatch company = new FindsCorpMappingService.CorpMatch(
// "LOCAL-9", "本平台九公司", null, null, "PERSONNEL_MATCH");
// when(personCorpResolver.resolve(any(), any(), any(), any())).thenReturn(company);
// OpenapiConfigDO config = new OpenapiConfigDO();
// config.setConfigCode("FINDS");
// config.setCorpinfoId("0");
// JsonNode row = objectMapper.readTree("{\"id\":9001,\"alarmNo\":\"A9001\","
// + "\"trackInfoList\":[{\"trackType\":\"STAFF\",\"trackNo\":\"F009\","
// + "\"trackName\":\"测试人员\",\"idCardNo\":\"ID009\"}],"
// + "\"terminalNoList\":[\"T009\"]}");
//
// AlarmRecordDO result = service.upsertRecord(config, row);
//
// assertEquals("LOCAL-9", result.getCorpinfoId());
// verify(alarmRecordRepository).updateById(existing);
// ArgumentCaptor<QueryWrapper<AlarmRecordDO>> queryCaptor = ArgumentCaptor.forClass(QueryWrapper.class);
// verify(alarmRecordRepository).getOne(queryCaptor.capture(), eq(false));
// assertFalse(queryCaptor.getValue().getParamNameValuePairs().containsValue("0"));
// assertTrue(queryCaptor.getValue().getCustomSqlSegment().contains("finds_alarm_id"));
//
// ArgumentCaptor<AlarmRecordTargetDO> targetCaptor = ArgumentCaptor.forClass(AlarmRecordTargetDO.class);
// verify(alarmRecordTargetRepository).save(targetCaptor.capture());
// assertEquals("LOCAL-9", targetCaptor.getValue().getCorpinfoId());
// assertEquals("本平台九公司", targetCaptor.getValue().getCorpinfoName());
// }
//}

View File

@ -1,119 +0,0 @@
//package com.zcloud.personnel.positioning.command;
//
//import com.zcloud.personnel.positioning.persistence.dataobject.OrgLocalCorpDO;
//import com.zcloud.personnel.positioning.persistence.dataobject.OrgLocalDepartmentDO;
//import com.zcloud.personnel.positioning.persistence.dataobject.OrgMatchSnapshotDO;
//import com.zcloud.personnel.positioning.persistence.repository.OrgMatchRepository;
//import org.junit.jupiter.api.BeforeEach;
//import org.junit.jupiter.api.Test;
//import org.mockito.ArgumentCaptor;
//
//import java.util.Arrays;
//import java.util.Collections;
//import java.util.Map;
//
//import static org.junit.jupiter.api.Assertions.assertEquals;
//import static org.junit.jupiter.api.Assertions.assertNull;
//import static org.mockito.ArgumentMatchers.any;
//import static org.mockito.ArgumentMatchers.anyString;
//import static org.mockito.Mockito.mock;
//import static org.mockito.Mockito.never;
//import static org.mockito.Mockito.verify;
//import static org.mockito.Mockito.when;
//
//class FindsCorpMappingServiceTest {
// private OrgMatchRepository repository;
// private FindsCorpMappingService service;
//
// @BeforeEach
// void setUp() {
// repository = mock(OrgMatchRepository.class);
// service = new FindsCorpMappingService(repository);
// when(repository.listCorpCandidates(any())).thenReturn(Collections.emptyList());
// when(repository.listDepartmentCandidates(anyString())).thenReturn(Collections.emptyList());
// }
//
// @Test
// void usesSavedMatchedMappingFirst() {
// OrgMatchSnapshotDO saved = new OrgMatchSnapshotDO();
// saved.setLocalCorpinfoId("LOCAL-1");
// saved.setLocalCorpinfoName("本平台公司");
// saved.setMatchType("CORP_NAME");
// when(repository.findMatched("FINDS", "第三方公司", "")).thenReturn(saved);
//
// FindsCorpMappingService.CorpMatch result = service.resolve("FINDS", "0", "第三方公司", null, null);
//
// assertEquals("LOCAL-1", result.getCorpinfoId());
// verify(repository, never()).listCorpCandidates(any());
// verify(repository).touch(anyString(), anyString(), anyString(), any());
// }
//
// @Test
// void exactCompanyNameIsPersistedAsMatched() {
// when(repository.listCorpCandidates(any())).thenAnswer(invocation -> {
// Map<String, Object> params = invocation.getArgument(0);
// return params.containsKey("corpinfoName")
// ? Collections.singletonList(corp("LOCAL-2", "卸车队")) : Collections.emptyList();
// });
//
// FindsCorpMappingService.CorpMatch result = service.resolve("FINDS", "0", "卸车队", null, null);
//
// assertEquals("LOCAL-2", result.getCorpinfoId());
// assertSaved("MATCHED", "CORP_NAME");
// }
//
// @Test
// void departmentNameResolvesOwningCompany() {
// OrgLocalDepartmentDO department = new OrgLocalDepartmentDO();
// department.setDepartmentId(88L);
// department.setDepartmentName("卸车作业部");
// department.setRootCorpinfoId("LOCAL-DEP");
// department.setRootCorpinfoName("港区公司");
// when(repository.listDepartmentCandidates("卸车作业部"))
// .thenReturn(Collections.singletonList(department));
//
// FindsCorpMappingService.CorpMatch result = service.resolve(
// "FINDS", null, "第三方公司", "卸车作业部", null);
//
// assertEquals("LOCAL-DEP", result.getCorpinfoId());
// assertEquals(88L, result.getDepartmentId());
// assertSaved("MATCHED", "DEPARTMENT_NAME");
// }
//
// @Test
// void ambiguousSuffixDoesNotAssignCompany() {
// when(repository.listCorpCandidates(any())).thenAnswer(invocation -> {
// Map<String, Object> params = invocation.getArgument(0);
// return params.containsKey("corpinfoNameSuffix")
// ? Arrays.asList(corp("LOCAL-3", "一公司卸车队"), corp("LOCAL-4", "二公司卸车队"))
// : Collections.emptyList();
// });
//
// FindsCorpMappingService.CorpMatch result = service.resolve("FINDS", null, "卸车队", null, null);
//
// assertNull(result);
// assertSaved("AMBIGUOUS", "CORP_NAME_SUFFIX");
// }
//
// @Test
// void unknownOrganizationIsPersistedAsUnmatched() {
// FindsCorpMappingService.CorpMatch result = service.resolve("FINDS", null, "不存在的公司", null, null);
//
// assertNull(result);
// assertSaved("UNMATCHED", "CORP_NAME_SUFFIX");
// }
//
// private OrgLocalCorpDO corp(String id, String name) {
// OrgLocalCorpDO corp = new OrgLocalCorpDO();
// corp.setCorpinfoId(id);
// corp.setCorpinfoName(name);
// return corp;
// }
//
// private void assertSaved(String status, String matchType) {
// ArgumentCaptor<Map<String, Object>> captor = ArgumentCaptor.forClass(Map.class);
// verify(repository).upsert(captor.capture());
// assertEquals(status, captor.getValue().get("matchStatus"));
// assertEquals(matchType, captor.getValue().get("matchType"));
// }
//}

View File

@ -1,38 +0,0 @@
//package com.zcloud.personnel.positioning.command;
//
//import com.zcloud.personnel.positioning.persistence.dataobject.CorpInfoSnapshotDO;
//import com.zcloud.personnel.positioning.persistence.repository.PersonnelMatchRepository;
//import org.junit.jupiter.api.Test;
//
//import java.util.Arrays;
//import static org.junit.jupiter.api.Assertions.assertNull;
//import static org.mockito.ArgumentMatchers.anyMap;
//import static org.mockito.Mockito.mock;
//import static org.mockito.Mockito.never;
//import static org.mockito.Mockito.verify;
//import static org.mockito.Mockito.when;
//
//class FindsPersonCorpResolverTest {
//
// @Test
// void ambiguousHighPriorityMatchDoesNotFallBackToLowerPriorityFields() {
// PersonnelMatchRepository repository = mock(PersonnelMatchRepository.class);
// when(repository.listMatchedPersonCompanies(anyMap()))
// .thenReturn(Arrays.asList(company("C1"), company("C2")));
// FindsPersonCorpResolver resolver = new FindsPersonCorpResolver(repository);
//
// FindsCorpMappingService.CorpMatch result = resolver.resolve(
// "ID-AMBIGUOUS", "F001", "测试人员", "T001");
//
// assertNull(result);
// verify(repository, never()).listLocalPersonCompanies(anyMap());
// verify(repository, never()).listTerminalCompanies("T001");
// }
//
// private CorpInfoSnapshotDO company(String id) {
// CorpInfoSnapshotDO company = new CorpInfoSnapshotDO();
// company.setCorpinfoId(id);
// company.setCorpName("公司" + id);
// return company;
// }
//}

View File

@ -1,103 +0,0 @@
package com.zcloud.personnel.positioning.command.query;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zcloud.personnel.positioning.dto.BiCompanyOnlinePersonStatQry;
import com.zcloud.personnel.positioning.dto.clientobject.BiCompanyOnlinePersonCO;
import com.zcloud.personnel.positioning.dto.clientobject.BiCompanyOnlinePersonStatCO;
import com.zcloud.personnel.positioning.integration.finds.FindsOpenApiClient;
import com.zcloud.personnel.positioning.persistence.dataobject.BiCorpInfoDO;
import com.zcloud.personnel.positioning.persistence.repository.AreaRepository;
import com.zcloud.personnel.positioning.persistence.repository.FenceRepository;
import com.zcloud.personnel.positioning.persistence.repository.PersonnelPositioningBiRepository;
import com.zcloud.personnel.positioning.persistence.repository.UserRepository;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
class CompanyOnlinePersonStatPaginationTest {
private PersonnelPositioningBiRepository biRepository;
private BiPersonLocationProvider personLocationProvider;
private PersonnelPositioningBiQueryExe queryExe;
@BeforeEach
void setUp() {
biRepository = mock(PersonnelPositioningBiRepository.class);
personLocationProvider = mock(BiPersonLocationProvider.class);
queryExe = new PersonnelPositioningBiQueryExe(
mock(FindsOpenApiClient.class), new ObjectMapper(), biRepository,
mock(AreaRepository.class), mock(FenceRepository.class), mock(UserRepository.class),
personLocationProvider, mock(BiOverviewCache.class),
mock(PositionPersonLocalLookupService.class));
stubCompanyAndPeople();
}
@Test
void returnsAllPeopleWhenPaginationIsNotProvided() {
BiCompanyOnlinePersonStatCO response =
queryExe.queryCompanyOnlinePersonStat(new BiCompanyOnlinePersonStatQry());
assertEquals(3, response.getOnlineCount());
assertEquals(2, response.getInternalCount());
assertEquals(1, response.getTemporaryCount());
assertEquals(3, response.getTotalCount());
assertEquals(3, response.getPersonList().size());
assertEquals("Person 3", response.getPersonList().get(0).getStaffName());
assertNull(response.getPageIndex());
assertNull(response.getPageSize());
}
@Test
void returnsRequestedPageWithoutChangingAggregateCounts() {
BiCompanyOnlinePersonStatQry qry = new BiCompanyOnlinePersonStatQry();
qry.setPageIndex(2);
qry.setPageSize(1);
BiCompanyOnlinePersonStatCO response = queryExe.queryCompanyOnlinePersonStat(qry);
assertEquals(3, response.getOnlineCount());
assertEquals(2, response.getInternalCount());
assertEquals(1, response.getTemporaryCount());
assertEquals(3, response.getTotalCount());
assertEquals(2, response.getPageIndex());
assertEquals(1, response.getPageSize());
assertEquals(1, response.getPersonList().size());
assertEquals("Person 2", response.getPersonList().get(0).getStaffName());
}
private void stubCompanyAndPeople() {
BiCorpInfoDO company = new BiCorpInfoDO();
company.setCorpinfoId(2001L);
company.setCorpName("Company");
company.setCode("COMPANY-CODE");
when(biRepository.listCorpInfos(null)).thenReturn(Collections.singletonList(company));
List<BiCompanyOnlinePersonCO> people = Arrays.asList(
onlinePerson("Person 1", 1000L, true),
onlinePerson("Person 2", 2000L, false),
onlinePerson("Person 3", 3000L, true));
when(personLocationProvider.listCompanyOnlinePersons(2001L, "Company", "COMPANY-CODE"))
.thenReturn(people);
when(personLocationProvider.isOnline(any(BiCompanyOnlinePersonCO.class)))
.thenAnswer(invocation -> "ONLINE".equals(
invocation.<BiCompanyOnlinePersonCO>getArgument(0).getLocationStatus()));
}
private BiCompanyOnlinePersonCO onlinePerson(String name, long lastLocationTime,
boolean internalEmployee) {
BiCompanyOnlinePersonCO person = new BiCompanyOnlinePersonCO();
person.setStaffName(name);
person.setLastLocationTime(lastLocationTime);
person.setLocationStatus("ONLINE");
person.setInternalEmployee(internalEmployee);
return person;
}
}

View File

@ -1,117 +0,0 @@
//package com.zcloud.personnel.positioning.command.query;
//
//import com.fasterxml.jackson.databind.JsonNode;
//import com.fasterxml.jackson.databind.ObjectMapper;
//import com.zcloud.personnel.positioning.integration.finds.FindsOpenApiClient;
//import org.junit.jupiter.api.BeforeEach;
//import org.junit.jupiter.api.Test;
//import org.springframework.jdbc.core.JdbcTemplate;
//
//import java.time.LocalDateTime;
//import java.util.Collections;
//
//import static org.junit.jupiter.api.Assertions.assertThrows;
//import static org.junit.jupiter.api.Assertions.assertFalse;
//import static org.junit.jupiter.api.Assertions.assertTrue;
//import static org.mockito.ArgumentMatchers.any;
//import static org.mockito.ArgumentMatchers.anyMap;
//import static org.mockito.ArgumentMatchers.anyString;
//import static org.mockito.ArgumentMatchers.eq;
//import static org.mockito.Mockito.mock;
//import static org.mockito.Mockito.never;
//import static org.mockito.Mockito.verify;
//import static org.mockito.Mockito.when;
//
//class FindsStaffLocationStatusSyncServiceTest {
// private final ObjectMapper objectMapper = new ObjectMapper();
// private FindsOpenApiClient findsOpenApiClient;
// private FindsStaffMappingSyncService mappingSyncService;
// private JdbcTemplate jdbcTemplate;
//
// @BeforeEach
// void setUp() {
// findsOpenApiClient = mock(FindsOpenApiClient.class);
// mappingSyncService = mock(FindsStaffMappingSyncService.class);
// jdbcTemplate = mock(JdbcTemplate.class);
// }
//
// @Test
// void unchangedStaffCountSkipsFullSyncAndRefreshesStatus() throws Exception {
// stubCount(2L);
// stubTerminalStatusPages();
//
// FindsStaffLocationStatusSyncService service = new FindsStaffLocationStatusSyncService(
// findsOpenApiClient, mappingSyncService, jdbcTemplate);
// FindsStaffLocationStatusSyncService.SyncSummary summary = service.synchronize();
//
// assertFalse(summary.isFullSync());
// verify(mappingSyncService, never()).syncAll();
// verify(jdbcTemplate).update(anyString(), any(), eq("FINDS"), any());
// }
//
// @Test
// void changedStaffCountRunsFullSyncBeforeStatusRefresh() throws Exception {
// stubCount(2L);
// when(jdbcTemplate.queryForObject(anyString(), eq(Long.class), eq("FINDS"))).thenReturn(1L);
// stubTerminalStatusPages();
// when(mappingSyncService.syncAll()).thenReturn(2);
//
// FindsStaffLocationStatusSyncService service = new FindsStaffLocationStatusSyncService(
// findsOpenApiClient, mappingSyncService, jdbcTemplate);
// FindsStaffLocationStatusSyncService.SyncSummary summary = service.synchronize();
//
// assertTrue(summary.isFullSync());
// verify(mappingSyncService).syncAll();
// }
//
// @Test
// void failedOnlineStatusPageDoesNotMarkExistingRowsOffline() throws Exception {
// stubCount(2L);
// when(findsOpenApiClient.postFailFast(eq("finds.terminal.list"), anyMap()))
// .thenReturn(json("{\"code\":500,\"data\":{\"total\":0,\"data\":[]}}"));
//
// FindsStaffLocationStatusSyncService service = new FindsStaffLocationStatusSyncService(
// findsOpenApiClient, mappingSyncService, jdbcTemplate);
//
// try {
// service.synchronize();
// } catch (RuntimeException expected) {
// // The status cycle must fail before any database status update.
// }
// verify(jdbcTemplate, never()).batchUpdate(anyString(), any(java.util.List.class));
// verify(jdbcTemplate, never()).update(anyString(), any(), any(), any());
// }
//
// @Test
// void missingRemoteStaffTotalStopsBeforeDatabaseStatusUpdate() throws Exception {
// when(findsOpenApiClient.postFailFast(eq("finds.staff.list"), anyMap()))
// .thenReturn(json("{\"code\":200,\"data\":{\"data\":[]}}"));
//
// FindsStaffLocationStatusSyncService service = new FindsStaffLocationStatusSyncService(
// findsOpenApiClient, mappingSyncService, jdbcTemplate);
//
// assertThrows(RuntimeException.class, service::synchronize);
// verify(mappingSyncService, never()).syncAll();
// verify(jdbcTemplate, never()).batchUpdate(anyString(), any(java.util.List.class));
// verify(jdbcTemplate, never()).update(anyString(), any(), any(), any());
// }
//
// private void stubCount(long remoteCount) throws Exception {
// when(findsOpenApiClient.postFailFast(eq("finds.staff.list"), anyMap()))
// .thenReturn(json("{\"code\":200,\"data\":{\"total\":" + remoteCount
// + ",\"data\":[]}}"));
// when(jdbcTemplate.queryForObject(anyString(), eq(Long.class), eq("FINDS")))
// .thenReturn(remoteCount);
// when(jdbcTemplate.queryForObject(anyString(), eq(LocalDateTime.class), eq("FINDS")))
// .thenReturn(LocalDateTime.now());
// }
//
// private void stubTerminalStatusPages() throws Exception {
// when(findsOpenApiClient.postFailFast(eq("finds.terminal.list"), anyMap()))
// .thenReturn(json("{\"code\":200,\"data\":{\"total\":0,\"data\":[]}}"));
// }
//
// private JsonNode json(String value) throws Exception {
// return objectMapper.readTree(value);
// }
//}

View File

@ -1,96 +0,0 @@
package com.zcloud.personnel.positioning.command.query;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zcloud.personnel.positioning.command.FindsCorpMappingService;
import com.zcloud.personnel.positioning.integration.finds.FindsOpenApiClient;
import com.zcloud.personnel.positioning.persistence.repository.PersonnelMatchRepository;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import java.util.Arrays;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyMap;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
class FindsStaffMappingSyncServiceTest {
private final ObjectMapper objectMapper = new ObjectMapper();
private FindsOpenApiClient findsOpenApiClient;
private PositionPersonLocalLookupService localLookupService;
private PersonnelMatchRepository personnelMatchRepository;
private FindsCorpMappingService corpMappingService;
private FindsStaffMappingSyncService service;
@BeforeEach
void setUp() {
findsOpenApiClient = mock(FindsOpenApiClient.class);
localLookupService = mock(PositionPersonLocalLookupService.class);
personnelMatchRepository = mock(PersonnelMatchRepository.class);
corpMappingService = mock(FindsCorpMappingService.class);
service = new FindsStaffMappingSyncService(
findsOpenApiClient, localLookupService, personnelMatchRepository, corpMappingService);
}
@Test
void idCardHasPriorityOverStaffNumber() throws Exception {
stubFindsStaff("F001", "张三", "ID-001");
when(localLookupService.findAllPeople()).thenReturn(Arrays.asList(
person(1L, "OTHER", "张三", "13800000001", "ID-001"),
person(2L, "F001", "李四", "13800000002", "ID-002")));
service.syncAll();
Map<String, Object> values = insertedPersonnelValues();
assertEquals(1L, values.get("localUserId"));
assertEquals("MATCHED", values.get("matchStatus"));
assertEquals("ID_CARD", values.get("matchType"));
verify(corpMappingService).resolve(eq("FINDS"), any(), any(), any(), any());
}
@Test
void duplicateNameIsAmbiguous() throws Exception {
stubFindsStaff("F404", "同名人员", null);
when(localLookupService.findAllPeople()).thenReturn(Arrays.asList(
person(3L, "U003", "同名人员", "13800000003", "ID-003"),
person(4L, "U004", "同名人员", "13800000004", "ID-004")));
service.syncAll();
Map<String, Object> values = insertedPersonnelValues();
assertNull(values.get("localUserId"));
assertEquals("AMBIGUOUS", values.get("matchStatus"));
assertNull(values.get("matchType"));
}
private void stubFindsStaff(String staffNo, String name, String idCardNo) throws Exception {
String idCardJson = idCardNo == null ? "null" : "\"" + idCardNo + "\"";
when(findsOpenApiClient.postFailFast(eq("finds.staff.list"), anyMap())).thenReturn(
objectMapper.readTree("{\"code\":200,\"data\":{\"total\":1,\"data\":[{"
+ "\"trackNo\":\"" + staffNo + "\",\"staffName\":\"" + name
+ "\",\"idCardNo\":" + idCardJson + "}]}}"));
}
private PositionPersonLocalLookupService.LocalPerson person(Long id, String username, String name,
String phone, String idCardNo) {
PositionPersonLocalLookupService.LocalPerson person = new PositionPersonLocalLookupService.LocalPerson();
person.setUserId(id);
person.setLocalStaffNo(username);
person.setStaffName(name);
person.setMobileNo(phone);
person.setIdCardNo(idCardNo);
return person;
}
private Map<String, Object> insertedPersonnelValues() {
ArgumentCaptor<Map<String, Object>> captor = ArgumentCaptor.forClass(Map.class);
verify(personnelMatchRepository).upsert(captor.capture());
return captor.getValue();
}
}

View File

@ -1,65 +0,0 @@
package com.zcloud.personnel.positioning.command.query;
import com.alibaba.cola.dto.PageResponse;
import com.zcloud.personnel.positioning.dto.PositionPersonPageQry;
import com.zcloud.personnel.positioning.dto.clientobject.PositionPersonCO;
import com.zcloud.personnel.positioning.integration.finds.FindsOpenApiClient;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.util.Collections;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.anyMap;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anySet;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
class PositionPersonStoredStatusTest {
private FindsOpenApiClient findsOpenApiClient;
private PositionPersonLocalLookupService localLookupService;
private PositionPersonQueryExe queryExe;
@BeforeEach
void setUp() {
findsOpenApiClient = mock(FindsOpenApiClient.class);
localLookupService = mock(PositionPersonLocalLookupService.class);
queryExe = new PositionPersonQueryExe(findsOpenApiClient, localLookupService);
}
@Test
void persistedStatusIsUsedWithoutCallingFindsLocate() {
PositionPersonLocalLookupService.LocalPerson local = new PositionPersonLocalLookupService.LocalPerson();
local.setUserId(1L);
local.setStaffName("Stored User");
local.setMobileNo("13800000001");
PositionPersonLocalLookupService.PersonMapping mapping =
new PositionPersonLocalLookupService.PersonMapping();
mapping.setFindsStaffNo("13800000001");
mapping.setFindsTerminalNo("T-1");
mapping.setLocationStatus("ONLINE");
mapping.setLocal(local);
when(localLookupService.hasPersonnelMappings()).thenReturn(true);
when(localLookupService.findMatchedPersonnel(any(PositionPersonPageQry.class)))
.thenReturn(Collections.singletonList(mapping));
when(localLookupService.findTerminalNosByIdCardNos(anySet()))
.thenReturn(Collections.emptyMap());
when(localLookupService.findTerminals(eq(Collections.singleton("T-1"))))
.thenReturn(Collections.emptyMap());
PositionPersonPageQry qry = new PositionPersonPageQry();
qry.setPageIndex(1);
qry.setPageSize(20);
PageResponse<PositionPersonCO> response = queryExe.list(qry);
assertEquals(1, response.getTotalCount());
assertEquals("ONLINE", response.getData().get(0).getLocationStatus());
verify(findsOpenApiClient, never()).postFailFast(eq("finds.point.locate"), anyMap());
}
}

View File

@ -1,7 +1,6 @@
package com.zcloud.personnel.positioning.persistence.mapper;
import com.zcloud.personnel.positioning.persistence.dataobject.OrgLocalCorpDO;
import com.zcloud.personnel.positioning.persistence.dataobject.OrgLocalDepartmentDO;
import com.zcloud.personnel.positioning.persistence.dataobject.OrgMatchSnapshotDO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@ -15,13 +14,17 @@ public interface OrgMatchMapper {
OrgMatchSnapshotDO findMatched(@Param("sourceConfigCode") String sourceConfigCode,
@Param("findsCorpinfoName") String findsCorpinfoName,
@Param("findsDepartmentName") String findsDepartmentName);
OrgMatchSnapshotDO findMatchedByDepartmentName(
@Param("sourceConfigCode") String sourceConfigCode,
@Param("findsDepartmentName") String findsDepartmentName);
List<OrgLocalCorpDO> listCorpCandidates(@Param("params") Map<String, Object> params);
List<OrgLocalDepartmentDO> listDepartmentCandidates(@Param("departmentName") String departmentName);
int upsert(@Param("params") Map<String, Object> params);
int touch(@Param("sourceConfigCode") String sourceConfigCode,
@Param("findsCorpinfoName") String findsCorpinfoName,
@Param("findsDepartmentName") String findsDepartmentName,
@Param("seenTime") LocalDateTime seenTime);
int disableMissing(@Param("sourceConfigCode") String sourceConfigCode,
@Param("syncTime") LocalDateTime syncTime);
long countAll();
long countMatched();
}

View File

@ -1,7 +1,6 @@
package com.zcloud.personnel.positioning.persistence.repository;
import com.zcloud.personnel.positioning.persistence.dataobject.OrgLocalCorpDO;
import com.zcloud.personnel.positioning.persistence.dataobject.OrgLocalDepartmentDO;
import com.zcloud.personnel.positioning.persistence.dataobject.OrgMatchSnapshotDO;
import java.time.LocalDateTime;
@ -11,11 +10,13 @@ import java.util.Map;
public interface OrgMatchRepository {
OrgMatchSnapshotDO findMatched(String sourceConfigCode, String findsCorpinfoName,
String findsDepartmentName);
OrgMatchSnapshotDO findMatchedByDepartmentName(String sourceConfigCode,
String findsDepartmentName);
List<OrgLocalCorpDO> listCorpCandidates(Map<String, Object> params);
List<OrgLocalDepartmentDO> listDepartmentCandidates(String departmentName);
void upsert(Map<String, Object> params);
void touch(String sourceConfigCode, String findsCorpinfoName, String findsDepartmentName,
LocalDateTime seenTime);
void disableMissing(String sourceConfigCode, LocalDateTime syncTime);
long countAll();
long countMatched();
}

View File

@ -1,7 +1,6 @@
package com.zcloud.personnel.positioning.persistence.repository.impl;
import com.zcloud.personnel.positioning.persistence.dataobject.OrgLocalCorpDO;
import com.zcloud.personnel.positioning.persistence.dataobject.OrgLocalDepartmentDO;
import com.zcloud.personnel.positioning.persistence.dataobject.OrgMatchSnapshotDO;
import com.zcloud.personnel.positioning.persistence.mapper.OrgMatchMapper;
import com.zcloud.personnel.positioning.persistence.repository.OrgMatchRepository;
@ -19,16 +18,19 @@ public class OrgMatchRepositoryImpl implements OrgMatchRepository {
@Override public OrgMatchSnapshotDO findMatched(String code, String corpName, String departmentName) {
return orgMatchMapper.findMatched(code, corpName, departmentName);
}
@Override public OrgMatchSnapshotDO findMatchedByDepartmentName(String code, String departmentName) {
return orgMatchMapper.findMatchedByDepartmentName(code, departmentName);
}
@Override public List<OrgLocalCorpDO> listCorpCandidates(Map<String, Object> params) {
return orgMatchMapper.listCorpCandidates(params);
}
@Override public List<OrgLocalDepartmentDO> listDepartmentCandidates(String departmentName) {
return orgMatchMapper.listDepartmentCandidates(departmentName);
}
@Override public void upsert(Map<String, Object> params) { orgMatchMapper.upsert(params); }
@Override public void touch(String code, String corpName, String departmentName, LocalDateTime time) {
orgMatchMapper.touch(code, corpName, departmentName, time);
}
@Override public void disableMissing(String code, LocalDateTime time) {
orgMatchMapper.disableMissing(code, time);
}
@Override public long countAll() { return orgMatchMapper.countAll(); }
@Override public long countMatched() { return orgMatchMapper.countMatched(); }
}

View File

@ -0,0 +1,15 @@
ALTER TABLE `org_match`
ADD COLUMN `finds_org_name_path` text DEFAULT NULL COMMENT 'FindS orgNamePath full snapshot' AFTER `finds_department_name`,
ADD COLUMN `finds_org_path_hash` char(64) NOT NULL DEFAULT '' COMMENT 'SHA-256 of the full FindS org path' AFTER `finds_org_name_path`,
ADD COLUMN `matched_path_node` varchar(255) DEFAULT NULL COMMENT 'orgNamePath node matched to corp_info' AFTER `finds_org_path_hash`;
UPDATE `org_match`
SET `finds_org_name_path` = CONCAT('["', REPLACE(`finds_corpinfo_name`, '"', '\\"'), '","',
REPLACE(`finds_department_name`, '"', '\\"'), '"]'),
`finds_org_path_hash` = SHA2(CONCAT(`finds_corpinfo_name`, CHAR(31), `finds_department_name`), 256)
WHERE `finds_org_path_hash` = '';
ALTER TABLE `org_match`
DROP INDEX `uk_org_match_source_name_delete`,
ADD UNIQUE KEY `uk_org_match_source_path_delete`
(`source_config_code`, `finds_org_path_hash`, `delete_enum`);

View File

@ -14,12 +14,31 @@
LIMIT 1
</select>
<select id="findMatchedByDepartmentName"
resultType="com.zcloud.personnel.positioning.persistence.dataobject.OrgMatchSnapshotDO">
SELECT MAX(local_corpinfo_id) AS local_corpinfo_id,
MAX(local_corpinfo_name) AS local_corpinfo_name,
MAX(local_department_id) AS local_department_id,
MAX(local_department_name) AS local_department_name,
MAX(match_type) AS match_type
FROM org_match
WHERE source_config_code = #{sourceConfigCode}
AND finds_department_name = #{findsDepartmentName}
AND match_status = 'MATCHED'
AND local_corpinfo_id IS NOT NULL
AND (delete_enum IS NULL OR delete_enum = 'FALSE')
HAVING COUNT(DISTINCT local_corpinfo_id) = 1
LIMIT 1
</select>
<select id="listCorpCandidates" resultType="com.zcloud.personnel.positioning.persistence.dataobject.OrgLocalCorpDO">
SELECT ci.id AS corpinfo_id, ci.corp_name AS corpinfo_name,
ci.code AS credit_code
FROM corp_info ci
WHERE (ci.delete_enum IS NULL OR ci.delete_enum = 'FALSE')
<choose>
<when test="params.allCorp != null and params.allCorp">
</when>
<when test="params.corpinfoId != null and params.corpinfoId != ''">
AND ci.id = #{params.corpinfoId}
</when>
@ -29,34 +48,33 @@
<when test="params.corpinfoName != null and params.corpinfoName != ''">
AND ci.corp_name = #{params.corpinfoName}
</when>
<when test="params.corpinfoNameOrderedPattern != null and params.corpinfoNameOrderedPattern != ''">
AND ci.corp_name LIKE #{params.corpinfoNameOrderedPattern} ESCAPE '\\'
</when>
<otherwise>
AND ci.corp_name LIKE CONCAT('%', #{params.corpinfoNameSuffix}) ESCAPE '\\'
</otherwise>
</choose>
</select>
<select id="listDepartmentCandidates" resultType="com.zcloud.personnel.positioning.persistence.dataobject.OrgLocalDepartmentDO">
SELECT d.id AS department_id, d.dept_name AS department_name,
ci.id AS root_corpinfo_id,
ci.corp_name AS root_corpinfo_name
FROM department d
INNER JOIN corp_info ci ON ci.id = d.org_id
WHERE (d.delete_enum IS NULL OR d.delete_enum = 'FALSE')
AND (ci.delete_enum IS NULL OR ci.delete_enum = 'FALSE')
AND d.dept_name = #{departmentName}
</select>
<insert id="upsert">
INSERT INTO org_match (source_system, source_config_code, finds_corpinfo_id,
finds_corpinfo_name, finds_department_name, local_corpinfo_id, local_corpinfo_name,
finds_corpinfo_name, finds_department_name, finds_org_name_path, finds_org_path_hash,
matched_path_node, local_corpinfo_id, local_corpinfo_name,
local_department_id, local_department_name, match_status, match_type,
last_seen_time, last_sync_time, sync_status, sync_error, delete_enum)
VALUES ('FINDS', #{params.sourceConfigCode}, #{params.findsCorpinfoId},
#{params.findsCorpinfoName}, #{params.findsDepartmentName},
#{params.findsCorpinfoName}, #{params.findsDepartmentName}, #{params.findsOrgNamePath},
#{params.findsOrgPathHash}, #{params.matchedPathNode},
#{params.localCorpinfoId}, #{params.localCorpinfoName},
#{params.localDepartmentId}, #{params.localDepartmentName},
#{params.matchStatus}, #{params.matchType}, #{params.syncTime},
#{params.syncTime}, 'SUCCESS', NULL, 'FALSE')
ON DUPLICATE KEY UPDATE finds_corpinfo_id = VALUES(finds_corpinfo_id),
finds_corpinfo_name = VALUES(finds_corpinfo_name),
finds_department_name = VALUES(finds_department_name),
finds_org_name_path = VALUES(finds_org_name_path),
matched_path_node = VALUES(matched_path_node),
local_corpinfo_id = VALUES(local_corpinfo_id), local_corpinfo_name = VALUES(local_corpinfo_name),
local_department_id = VALUES(local_department_id), local_department_name = VALUES(local_department_name),
match_status = VALUES(match_status), match_type = VALUES(match_type),
@ -70,6 +88,15 @@
AND finds_department_name = #{findsDepartmentName}
AND (delete_enum IS NULL OR delete_enum = 'FALSE')
</update>
<update id="disableMissing">
UPDATE org_match SET match_status = 'DISABLED', sync_status = 'SUCCESS',
local_corpinfo_id = NULL, local_corpinfo_name = NULL,
local_department_id = NULL, local_department_name = NULL,
last_sync_time = #{syncTime}, version = version + 1
WHERE source_config_code = #{sourceConfigCode}
AND (delete_enum IS NULL OR delete_enum = 'FALSE')
AND (last_seen_time IS NULL OR last_seen_time &lt; #{syncTime})
</update>
<select id="countAll" resultType="long">SELECT COUNT(1) FROM org_match</select>
<select id="countMatched" resultType="long">SELECT COUNT(1) FROM org_match WHERE match_status = 'MATCHED'</select>
</mapper>

View File

@ -28,9 +28,11 @@
finds_id_card_no, finds_staff_name, finds_corpinfo_id, finds_corpinfo_name,
finds_department_name, finds_terminal_no, local_user_id, local_mobile_no, local_id_card_no,
match_status, match_type, last_seen_time, last_sync_time, sync_status, sync_error, delete_enum
) VALUES ('FINDS', #{sourceConfigCode}, #{staffNo}, #{staffNoType}, #{findsTrackId}, #{idCardNo},
#{staffName}, #{corpinfoId}, #{corpinfoName}, #{departmentName}, #{terminalNo}, #{localUserId},
#{localMobileNo}, #{localIdCardNo}, #{matchStatus}, #{matchType}, #{syncTime}, #{syncTime},
) VALUES ('FINDS', #{params.sourceConfigCode}, #{params.staffNo}, #{params.staffNoType},
#{params.findsTrackId}, #{params.idCardNo}, #{params.staffName}, #{params.corpinfoId},
#{params.corpinfoName}, #{params.departmentName}, #{params.terminalNo}, #{params.localUserId},
#{params.localMobileNo}, #{params.localIdCardNo}, #{params.matchStatus}, #{params.matchType},
#{params.syncTime}, #{params.syncTime},
'SUCCESS', NULL, 'FALSE')
ON DUPLICATE KEY UPDATE finds_staff_no_type = VALUES(finds_staff_no_type),
finds_track_id = VALUES(finds_track_id), finds_id_card_no = VALUES(finds_id_card_no),