mirror of
https://github.com/hpd840321/starRiverProperty.git
synced 2026-06-09 08:20:31 +08:00
feat: 租户访客策略 SQL、访客邀约验证包、component-org 与发布脚本
- docs/sql: organization_* 与 tenant_* 访客楼层策略脚本 - docs/testing: 访客邀约页初始化验证、pack 脚本与 README(忽略 dist/__pycache__) - maven-ninca-common-component-organization: CpImageStoreServiceImpl、starter、run-verify、releases 脚本与 javap 审计 JSON - docs/superpowers: component-org 生产问题修复计划 - scripts/test-env/prepare-db.sh 更新 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -71,3 +71,7 @@ scripts/test-env/logs/
|
|||||||
scripts/test-env/services/
|
scripts/test-env/services/
|
||||||
!/maven-ninca-common-component-organization/
|
!/maven-ninca-common-component-organization/
|
||||||
!/maven-ninca-common-component-organization/**
|
!/maven-ninca-common-component-organization/**
|
||||||
|
|
||||||
|
# docs/testing: 打包脚本生成物与本地缓存
|
||||||
|
docs/testing/dist/
|
||||||
|
docs/testing/__pycache__/
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
-- 租户访客默认楼层策略(组织库 component-organization)
|
||||||
|
-- 表结构与电梯库 cw-elevator-application 在 tenant_visitor_floor_policy_v2.sql 之后一致,便于双库同步或备查。
|
||||||
|
-- 设计说明:docs/business/租户访客默认楼层-数据库配置阶段技术设计.md
|
||||||
|
-- 本脚本会先 DROP 再 CREATE:将删除现有 tenant_visitor_floor_policy 及其中全部数据,执行前请自行备份。
|
||||||
|
|
||||||
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
|
USE `component-organization`;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS tenant_visitor_floor_policy;
|
||||||
|
|
||||||
|
CREATE TABLE tenant_visitor_floor_policy (
|
||||||
|
id VARCHAR(32) NOT NULL COMMENT '主键',
|
||||||
|
business_id VARCHAR(64) NULL COMMENT 'DEPRECATED: 已废弃,以 org_id 为准',
|
||||||
|
org_id VARCHAR(32) NULL COMMENT '组织节点ID(cw_is_organization.ID)',
|
||||||
|
policy_type VARCHAR(32) NOT NULL DEFAULT 'INTERSECT_ALLOWLIST' COMMENT '策略类型',
|
||||||
|
allow_zone_ids TEXT NULL COMMENT 'JSON 数组,zoneId 列表',
|
||||||
|
building_id VARCHAR(64) NULL COMMENT '预留:楼栋维度;租户默认填 NULL',
|
||||||
|
enabled TINYINT(1) NOT NULL DEFAULT 1 COMMENT '1 启用 0 停用',
|
||||||
|
policy_version BIGINT NOT NULL DEFAULT 1 COMMENT '配置版本号',
|
||||||
|
remark VARCHAR(256) NULL,
|
||||||
|
created_by VARCHAR(64) NULL,
|
||||||
|
created_at BIGINT NULL,
|
||||||
|
updated_by VARCHAR(64) NULL,
|
||||||
|
updated_at BIGINT NULL,
|
||||||
|
PRIMARY KEY (id),
|
||||||
|
UNIQUE KEY uk_org_building (org_id, building_id),
|
||||||
|
KEY idx_business_enabled (business_id, enabled)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='租户访客默认楼层策略(组织库;与电梯库结构对齐)';
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
-- =============================================================================
|
||||||
|
-- 组织库 component-organization:策略表 + 初始化数据(一站式)
|
||||||
|
-- =============================================================================
|
||||||
|
-- 将先 DROP 现有 tenant_visitor_floor_policy(含全部数据),再建表并灌入种子。执行前请备份。
|
||||||
|
-- 推荐拆分执行:organization_tenant_visitor_floor_policy.sql + 两个 init_*.sql(与下列逻辑同源)。
|
||||||
|
-- =============================================================================
|
||||||
|
|
||||||
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
|
USE `component-organization`;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS tenant_visitor_floor_policy;
|
||||||
|
|
||||||
|
CREATE TABLE tenant_visitor_floor_policy (
|
||||||
|
id VARCHAR(32) NOT NULL COMMENT '主键',
|
||||||
|
business_id VARCHAR(64) NULL COMMENT 'DEPRECATED: 已废弃,以 org_id 为准',
|
||||||
|
org_id VARCHAR(32) NULL COMMENT '组织节点ID(cw_is_organization.ID)',
|
||||||
|
policy_type VARCHAR(32) NOT NULL DEFAULT 'INTERSECT_ALLOWLIST' COMMENT '策略类型',
|
||||||
|
allow_zone_ids TEXT NULL COMMENT 'JSON 数组,zoneId 列表',
|
||||||
|
building_id VARCHAR(64) NULL COMMENT '预留:楼栋维度;租户默认填 NULL',
|
||||||
|
enabled TINYINT(1) NOT NULL DEFAULT 1 COMMENT '1 启用 0 停用',
|
||||||
|
policy_version BIGINT NOT NULL DEFAULT 1 COMMENT '配置版本号',
|
||||||
|
remark VARCHAR(256) NULL,
|
||||||
|
created_by VARCHAR(64) NULL,
|
||||||
|
created_at BIGINT NULL,
|
||||||
|
updated_by VARCHAR(64) NULL,
|
||||||
|
updated_at BIGINT NULL,
|
||||||
|
PRIMARY KEY (id),
|
||||||
|
UNIQUE KEY uk_org_building (org_id, building_id),
|
||||||
|
KEY idx_business_enabled (business_id, enabled)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='租户访客默认楼层策略(组织库;与电梯库结构对齐)';
|
||||||
|
|
||||||
|
-- ----- 广发 -----
|
||||||
|
INSERT INTO tenant_visitor_floor_policy (
|
||||||
|
id, org_id, business_id, policy_type, allow_zone_ids, building_id,
|
||||||
|
enabled, policy_version, remark, created_at, updated_at
|
||||||
|
) VALUES (
|
||||||
|
'gf_vstr_policy_guangfa_fund_001x',
|
||||||
|
'488b8ad049bb43408a6fbcc50bcb89ac',
|
||||||
|
'2524639890ba4f2cba9ba1a4eeaa4015',
|
||||||
|
'INTERSECT_ALLOWLIST',
|
||||||
|
'["605560545117995008"]',
|
||||||
|
NULL,
|
||||||
|
1, 1,
|
||||||
|
'广发基金:访客与 floorList 求交后仅保留 allowlist(默认仅 28F zone)。',
|
||||||
|
UNIX_TIMESTAMP(NOW()) * 1000,
|
||||||
|
UNIX_TIMESTAMP(NOW()) * 1000
|
||||||
|
) ON DUPLICATE KEY UPDATE
|
||||||
|
org_id = VALUES(org_id),
|
||||||
|
policy_type = VALUES(policy_type),
|
||||||
|
allow_zone_ids = VALUES(allow_zone_ids),
|
||||||
|
enabled = VALUES(enabled),
|
||||||
|
policy_version = policy_version + 1,
|
||||||
|
remark = VALUES(remark),
|
||||||
|
updated_at = VALUES(updated_at);
|
||||||
|
|
||||||
|
-- ----- 物业 7 条 -----
|
||||||
|
INSERT INTO tenant_visitor_floor_policy (
|
||||||
|
id, business_id, org_id, policy_type, allow_zone_ids,
|
||||||
|
building_id, enabled, policy_version, remark, created_at, updated_at
|
||||||
|
) VALUES
|
||||||
|
('pm_6f_vstr_policy_001','2524639890ba4f2cba9ba1a4eeaa4015','64fdc8eaf5824df5a1329819af29b79f','INTERSECT_ALLOWLIST','["605560541473144832"]',NULL,1,1,'星河湾物业管理有限公司:访客默认仅开放 6F。',UNIX_TIMESTAMP(NOW())*1000,UNIX_TIMESTAMP(NOW())*1000),
|
||||||
|
('pm_6f_vstr_policy_002','2524639890ba4f2cba9ba1a4eeaa4015','8fc3f910bd834198a539832017fe920e','INTERSECT_ALLOWLIST','["605560541473144832"]',NULL,1,1,'星河湾物业管理公司:访客默认仅开放 6F。',UNIX_TIMESTAMP(NOW())*1000,UNIX_TIMESTAMP(NOW())*1000),
|
||||||
|
('pm_6f_vstr_policy_003','2524639890ba4f2cba9ba1a4eeaa4015','cc760fdf9c384a0cbf4951ccf2c6452e','INTERSECT_ALLOWLIST','["605560541473144832"]',NULL,1,1,'星河湾物管公司:访客默认仅开放 6F。',UNIX_TIMESTAMP(NOW())*1000,UNIX_TIMESTAMP(NOW())*1000),
|
||||||
|
('pm_6f_vstr_policy_004','2524639890ba4f2cba9ba1a4eeaa4015','f216235e54ca42bfa0379e69b3754aff','INTERSECT_ALLOWLIST','["605560541473144832"]',NULL,1,1,'星中心物业管理公司:访客默认仅开放 6F。',UNIX_TIMESTAMP(NOW())*1000,UNIX_TIMESTAMP(NOW())*1000),
|
||||||
|
('pm_6f_vstr_policy_005','2524639890ba4f2cba9ba1a4eeaa4015','95818575a2284db6833289474d33671f','INTERSECT_ALLOWLIST','["605560541473144832"]',NULL,1,1,'星中心物业服务中心:访客默认仅开放 6F。',UNIX_TIMESTAMP(NOW())*1000,UNIX_TIMESTAMP(NOW())*1000),
|
||||||
|
('pm_6f_vstr_policy_006','2524639890ba4f2cba9ba1a4eeaa4015','348328d755624b3491cd307a3109f36a','INTERSECT_ALLOWLIST','["605560541473144832"]',NULL,1,1,'星中心物管公司:访客默认仅开放 6F。',UNIX_TIMESTAMP(NOW())*1000,UNIX_TIMESTAMP(NOW())*1000),
|
||||||
|
('pm_6f_vstr_policy_007','2524639890ba4f2cba9ba1a4eeaa4015','dde6cc9a4f6b4f5490d03e26fb016200','INTERSECT_ALLOWLIST','["605560541473144832"]',NULL,1,1,'物业管理总部:访客默认仅开放 6F。',UNIX_TIMESTAMP(NOW())*1000,UNIX_TIMESTAMP(NOW())*1000)
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
policy_type = VALUES(policy_type),
|
||||||
|
allow_zone_ids = VALUES(allow_zone_ids),
|
||||||
|
enabled = VALUES(enabled),
|
||||||
|
policy_version = policy_version + 1,
|
||||||
|
remark = VALUES(remark),
|
||||||
|
updated_at = VALUES(updated_at);
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
-- 广发基金租户:访客默认楼层策略初始化(组织库 component-organization)
|
||||||
|
-- 与 tenant_visitor_floor_policy_init_guangfa_fund.sql(电梯库)数据一致,便于双库对齐。
|
||||||
|
-- 请先执行 organization_tenant_visitor_floor_policy.sql(会先 DROP 再 CREATE 表)。
|
||||||
|
--
|
||||||
|
-- 数据来源(现场查询 192.168.3.12:3307):
|
||||||
|
-- org_id:component-organization.cw_is_organization
|
||||||
|
-- NAME='[28-38F]广发基金管理有限公司' -> ID = 488b8ad049bb43408a6fbcc50bcb89ac
|
||||||
|
-- 28F zone_id:cw-elevator-application.code_elevator_area
|
||||||
|
-- zone_id = 605560545117995008(zone_name=28F,code=0x1C)
|
||||||
|
|
||||||
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
|
USE `component-organization`;
|
||||||
|
|
||||||
|
INSERT INTO tenant_visitor_floor_policy (
|
||||||
|
id,
|
||||||
|
org_id,
|
||||||
|
business_id,
|
||||||
|
policy_type,
|
||||||
|
allow_zone_ids,
|
||||||
|
building_id,
|
||||||
|
enabled,
|
||||||
|
policy_version,
|
||||||
|
remark,
|
||||||
|
created_at,
|
||||||
|
updated_at
|
||||||
|
) VALUES (
|
||||||
|
'gf_vstr_policy_guangfa_fund_001x',
|
||||||
|
'488b8ad049bb43408a6fbcc50bcb89ac',
|
||||||
|
'2524639890ba4f2cba9ba1a4eeaa4015',
|
||||||
|
'INTERSECT_ALLOWLIST',
|
||||||
|
'["605560545117995008"]',
|
||||||
|
NULL,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
'广发基金:访客与 floorList 求交后仅保留 allowlist(默认仅 28F zone)。',
|
||||||
|
UNIX_TIMESTAMP(NOW()) * 1000,
|
||||||
|
UNIX_TIMESTAMP(NOW()) * 1000
|
||||||
|
) ON DUPLICATE KEY UPDATE
|
||||||
|
org_id = VALUES(org_id),
|
||||||
|
policy_type = VALUES(policy_type),
|
||||||
|
allow_zone_ids = VALUES(allow_zone_ids),
|
||||||
|
enabled = VALUES(enabled),
|
||||||
|
policy_version = policy_version + 1,
|
||||||
|
remark = VALUES(remark),
|
||||||
|
updated_at = VALUES(updated_at);
|
||||||
@@ -0,0 +1,158 @@
|
|||||||
|
-- 物业公司租户:访客默认楼层策略初始化(组织库 component-organization)
|
||||||
|
-- 与 tenant_visitor_floor_policy_init_property_mgmt_6f.sql(电梯库)数据一致。
|
||||||
|
-- 请先执行 organization_tenant_visitor_floor_policy.sql(会先 DROP 再 CREATE 表)。
|
||||||
|
--
|
||||||
|
-- 数据来源(192.168.3.12:3307):
|
||||||
|
-- org_id:component-organization.cw_is_organization(下列 NAME → ID)
|
||||||
|
-- 6F zone_id:cw-elevator-application.code_elevator_area → 605560541473144832(code=0x06)
|
||||||
|
|
||||||
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
|
USE `component-organization`;
|
||||||
|
|
||||||
|
INSERT INTO tenant_visitor_floor_policy (
|
||||||
|
id, business_id, org_id, policy_type, allow_zone_ids,
|
||||||
|
building_id, enabled, policy_version, remark, created_at, updated_at
|
||||||
|
) VALUES (
|
||||||
|
'pm_6f_vstr_policy_001',
|
||||||
|
'2524639890ba4f2cba9ba1a4eeaa4015',
|
||||||
|
'64fdc8eaf5824df5a1329819af29b79f',
|
||||||
|
'INTERSECT_ALLOWLIST',
|
||||||
|
'["605560541473144832"]',
|
||||||
|
NULL, 1, 1,
|
||||||
|
'星河湾物业管理有限公司:访客默认仅开放 6F。',
|
||||||
|
UNIX_TIMESTAMP(NOW()) * 1000,
|
||||||
|
UNIX_TIMESTAMP(NOW()) * 1000
|
||||||
|
) ON DUPLICATE KEY UPDATE
|
||||||
|
policy_type = VALUES(policy_type),
|
||||||
|
allow_zone_ids = VALUES(allow_zone_ids),
|
||||||
|
enabled = VALUES(enabled),
|
||||||
|
policy_version = policy_version + 1,
|
||||||
|
remark = VALUES(remark),
|
||||||
|
updated_at = VALUES(updated_at);
|
||||||
|
|
||||||
|
INSERT INTO tenant_visitor_floor_policy (
|
||||||
|
id, business_id, org_id, policy_type, allow_zone_ids,
|
||||||
|
building_id, enabled, policy_version, remark, created_at, updated_at
|
||||||
|
) VALUES (
|
||||||
|
'pm_6f_vstr_policy_002',
|
||||||
|
'2524639890ba4f2cba9ba1a4eeaa4015',
|
||||||
|
'8fc3f910bd834198a539832017fe920e',
|
||||||
|
'INTERSECT_ALLOWLIST',
|
||||||
|
'["605560541473144832"]',
|
||||||
|
NULL, 1, 1,
|
||||||
|
'星河湾物业管理公司:访客默认仅开放 6F。',
|
||||||
|
UNIX_TIMESTAMP(NOW()) * 1000,
|
||||||
|
UNIX_TIMESTAMP(NOW()) * 1000
|
||||||
|
) ON DUPLICATE KEY UPDATE
|
||||||
|
policy_type = VALUES(policy_type),
|
||||||
|
allow_zone_ids = VALUES(allow_zone_ids),
|
||||||
|
enabled = VALUES(enabled),
|
||||||
|
policy_version = policy_version + 1,
|
||||||
|
remark = VALUES(remark),
|
||||||
|
updated_at = VALUES(updated_at);
|
||||||
|
|
||||||
|
INSERT INTO tenant_visitor_floor_policy (
|
||||||
|
id, business_id, org_id, policy_type, allow_zone_ids,
|
||||||
|
building_id, enabled, policy_version, remark, created_at, updated_at
|
||||||
|
) VALUES (
|
||||||
|
'pm_6f_vstr_policy_003',
|
||||||
|
'2524639890ba4f2cba9ba1a4eeaa4015',
|
||||||
|
'cc760fdf9c384a0cbf4951ccf2c6452e',
|
||||||
|
'INTERSECT_ALLOWLIST',
|
||||||
|
'["605560541473144832"]',
|
||||||
|
NULL, 1, 1,
|
||||||
|
'星河湾物管公司:访客默认仅开放 6F。',
|
||||||
|
UNIX_TIMESTAMP(NOW()) * 1000,
|
||||||
|
UNIX_TIMESTAMP(NOW()) * 1000
|
||||||
|
) ON DUPLICATE KEY UPDATE
|
||||||
|
policy_type = VALUES(policy_type),
|
||||||
|
allow_zone_ids = VALUES(allow_zone_ids),
|
||||||
|
enabled = VALUES(enabled),
|
||||||
|
policy_version = policy_version + 1,
|
||||||
|
remark = VALUES(remark),
|
||||||
|
updated_at = VALUES(updated_at);
|
||||||
|
|
||||||
|
INSERT INTO tenant_visitor_floor_policy (
|
||||||
|
id, business_id, org_id, policy_type, allow_zone_ids,
|
||||||
|
building_id, enabled, policy_version, remark, created_at, updated_at
|
||||||
|
) VALUES (
|
||||||
|
'pm_6f_vstr_policy_004',
|
||||||
|
'2524639890ba4f2cba9ba1a4eeaa4015',
|
||||||
|
'f216235e54ca42bfa0379e69b3754aff',
|
||||||
|
'INTERSECT_ALLOWLIST',
|
||||||
|
'["605560541473144832"]',
|
||||||
|
NULL, 1, 1,
|
||||||
|
'星中心物业管理公司:访客默认仅开放 6F。',
|
||||||
|
UNIX_TIMESTAMP(NOW()) * 1000,
|
||||||
|
UNIX_TIMESTAMP(NOW()) * 1000
|
||||||
|
) ON DUPLICATE KEY UPDATE
|
||||||
|
policy_type = VALUES(policy_type),
|
||||||
|
allow_zone_ids = VALUES(allow_zone_ids),
|
||||||
|
enabled = VALUES(enabled),
|
||||||
|
policy_version = policy_version + 1,
|
||||||
|
remark = VALUES(remark),
|
||||||
|
updated_at = VALUES(updated_at);
|
||||||
|
|
||||||
|
INSERT INTO tenant_visitor_floor_policy (
|
||||||
|
id, business_id, org_id, policy_type, allow_zone_ids,
|
||||||
|
building_id, enabled, policy_version, remark, created_at, updated_at
|
||||||
|
) VALUES (
|
||||||
|
'pm_6f_vstr_policy_005',
|
||||||
|
'2524639890ba4f2cba9ba1a4eeaa4015',
|
||||||
|
'95818575a2284db6833289474d33671f',
|
||||||
|
'INTERSECT_ALLOWLIST',
|
||||||
|
'["605560541473144832"]',
|
||||||
|
NULL, 1, 1,
|
||||||
|
'星中心物业服务中心:访客默认仅开放 6F。',
|
||||||
|
UNIX_TIMESTAMP(NOW()) * 1000,
|
||||||
|
UNIX_TIMESTAMP(NOW()) * 1000
|
||||||
|
) ON DUPLICATE KEY UPDATE
|
||||||
|
policy_type = VALUES(policy_type),
|
||||||
|
allow_zone_ids = VALUES(allow_zone_ids),
|
||||||
|
enabled = VALUES(enabled),
|
||||||
|
policy_version = policy_version + 1,
|
||||||
|
remark = VALUES(remark),
|
||||||
|
updated_at = VALUES(updated_at);
|
||||||
|
|
||||||
|
INSERT INTO tenant_visitor_floor_policy (
|
||||||
|
id, business_id, org_id, policy_type, allow_zone_ids,
|
||||||
|
building_id, enabled, policy_version, remark, created_at, updated_at
|
||||||
|
) VALUES (
|
||||||
|
'pm_6f_vstr_policy_006',
|
||||||
|
'2524639890ba4f2cba9ba1a4eeaa4015',
|
||||||
|
'348328d755624b3491cd307a3109f36a',
|
||||||
|
'INTERSECT_ALLOWLIST',
|
||||||
|
'["605560541473144832"]',
|
||||||
|
NULL, 1, 1,
|
||||||
|
'星中心物管公司:访客默认仅开放 6F。',
|
||||||
|
UNIX_TIMESTAMP(NOW()) * 1000,
|
||||||
|
UNIX_TIMESTAMP(NOW()) * 1000
|
||||||
|
) ON DUPLICATE KEY UPDATE
|
||||||
|
policy_type = VALUES(policy_type),
|
||||||
|
allow_zone_ids = VALUES(allow_zone_ids),
|
||||||
|
enabled = VALUES(enabled),
|
||||||
|
policy_version = policy_version + 1,
|
||||||
|
remark = VALUES(remark),
|
||||||
|
updated_at = VALUES(updated_at);
|
||||||
|
|
||||||
|
INSERT INTO tenant_visitor_floor_policy (
|
||||||
|
id, business_id, org_id, policy_type, allow_zone_ids,
|
||||||
|
building_id, enabled, policy_version, remark, created_at, updated_at
|
||||||
|
) VALUES (
|
||||||
|
'pm_6f_vstr_policy_007',
|
||||||
|
'2524639890ba4f2cba9ba1a4eeaa4015',
|
||||||
|
'dde6cc9a4f6b4f5490d03e26fb016200',
|
||||||
|
'INTERSECT_ALLOWLIST',
|
||||||
|
'["605560541473144832"]',
|
||||||
|
NULL, 1, 1,
|
||||||
|
'物业管理总部:访客默认仅开放 6F。',
|
||||||
|
UNIX_TIMESTAMP(NOW()) * 1000,
|
||||||
|
UNIX_TIMESTAMP(NOW()) * 1000
|
||||||
|
) ON DUPLICATE KEY UPDATE
|
||||||
|
policy_type = VALUES(policy_type),
|
||||||
|
allow_zone_ids = VALUES(allow_zone_ids),
|
||||||
|
enabled = VALUES(enabled),
|
||||||
|
policy_version = policy_version + 1,
|
||||||
|
remark = VALUES(remark),
|
||||||
|
updated_at = VALUES(updated_at);
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
-- 组织库:从「仅 business_id 版」升级到 org_id 版(与电梯库 tenant_visitor_floor_policy_v2.sql 等价,仅库名不同)
|
||||||
|
-- 使用场景:早期已在 component-organization 建过无 org_id 的 tenant_visitor_floor_policy,需原地升级。
|
||||||
|
-- 若改用 organization_tenant_visitor_floor_policy.sql(内含 DROP+CREATE),请勿再执行本文件。
|
||||||
|
|
||||||
|
USE `component-organization`;
|
||||||
|
|
||||||
|
-- 1. 新增 org_id 列
|
||||||
|
ALTER TABLE tenant_visitor_floor_policy
|
||||||
|
ADD COLUMN org_id VARCHAR(32) NULL COMMENT '组织节点ID(cw_is_organization.ID)'
|
||||||
|
AFTER business_id;
|
||||||
|
|
||||||
|
-- 2. 替换唯一约束
|
||||||
|
ALTER TABLE tenant_visitor_floor_policy
|
||||||
|
DROP INDEX uk_biz_building,
|
||||||
|
ADD UNIQUE KEY uk_org_building (org_id, building_id);
|
||||||
|
|
||||||
|
-- 3. 标记 business_id 为废弃
|
||||||
|
ALTER TABLE tenant_visitor_floor_policy
|
||||||
|
MODIFY COLUMN business_id VARCHAR(64) NULL COMMENT 'DEPRECATED: 已废弃,以 org_id 为准';
|
||||||
|
|
||||||
|
-- 验证
|
||||||
|
SELECT COLUMN_NAME, COLUMN_KEY, COLUMN_COMMENT
|
||||||
|
FROM INFORMATION_SCHEMA.COLUMNS
|
||||||
|
WHERE TABLE_SCHEMA = 'component-organization'
|
||||||
|
AND TABLE_NAME = 'tenant_visitor_floor_policy'
|
||||||
|
ORDER BY ORDINAL_POSITION;
|
||||||
@@ -1,9 +1,23 @@
|
|||||||
-- 租户访客默认楼层策略(电梯应用库)
|
-- 租户访客默认楼层策略(电梯应用库 cw-elevator-application)
|
||||||
-- 设计说明:docs/business/租户访客默认楼层-数据库配置阶段技术设计.md
|
-- 设计说明:docs/business/租户访客默认楼层-数据库配置阶段技术设计.md
|
||||||
|
--
|
||||||
|
-- 本脚本会先 DROP 再 CREATE:删除现有 tenant_visitor_floor_policy 及全部历史数据,然后按 v2 后最终结构建空表。
|
||||||
|
-- 执行前请备份;若仅需 ALTER 升级旧表而不删数据,请改用 tenant_visitor_floor_policy_v2.sql(勿与本脚本混用)。
|
||||||
|
--
|
||||||
|
-- 连接方式任选其一:
|
||||||
|
-- mysql -h ... -u ... -p... cw-elevator-application < tenant_visitor_floor_policy.sql
|
||||||
|
-- 或在本文件中依赖下方 USE(手工客户端执行时)。
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS tenant_visitor_floor_policy (
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
|
USE `cw-elevator-application`;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS tenant_visitor_floor_policy;
|
||||||
|
|
||||||
|
CREATE TABLE tenant_visitor_floor_policy (
|
||||||
id VARCHAR(32) NOT NULL COMMENT '主键',
|
id VARCHAR(32) NOT NULL COMMENT '主键',
|
||||||
business_id VARCHAR(64) NOT NULL COMMENT '机构/租户 ID',
|
business_id VARCHAR(64) NULL COMMENT 'DEPRECATED: 已废弃,以 org_id 为准',
|
||||||
|
org_id VARCHAR(32) NULL COMMENT '组织节点ID(cw_is_organization.ID)',
|
||||||
policy_type VARCHAR(32) NOT NULL DEFAULT 'INTERSECT_ALLOWLIST' COMMENT '策略类型',
|
policy_type VARCHAR(32) NOT NULL DEFAULT 'INTERSECT_ALLOWLIST' COMMENT '策略类型',
|
||||||
allow_zone_ids TEXT NULL COMMENT 'JSON 数组,zoneId 列表',
|
allow_zone_ids TEXT NULL COMMENT 'JSON 数组,zoneId 列表',
|
||||||
building_id VARCHAR(64) NULL COMMENT '预留:楼栋维度;租户默认填 NULL',
|
building_id VARCHAR(64) NULL COMMENT '预留:楼栋维度;租户默认填 NULL',
|
||||||
@@ -15,13 +29,6 @@ CREATE TABLE IF NOT EXISTS tenant_visitor_floor_policy (
|
|||||||
updated_by VARCHAR(64) NULL,
|
updated_by VARCHAR(64) NULL,
|
||||||
updated_at BIGINT NULL,
|
updated_at BIGINT NULL,
|
||||||
PRIMARY KEY (id),
|
PRIMARY KEY (id),
|
||||||
UNIQUE KEY uk_biz_building (business_id, building_id),
|
UNIQUE KEY uk_org_building (org_id, building_id),
|
||||||
KEY idx_business_enabled (business_id, enabled)
|
KEY idx_business_enabled (business_id, enabled)
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='租户访客默认楼层策略(与组织 floorList 求交)';
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='租户访客默认楼层策略(与组织 floorList 求交)';
|
||||||
|
|
||||||
-- 示例(实施时替换占位符后执行)
|
|
||||||
-- INSERT INTO tenant_visitor_floor_policy
|
|
||||||
-- (id, business_id, policy_type, allow_zone_ids, building_id, enabled, policy_version, remark, created_at, updated_at)
|
|
||||||
-- VALUES
|
|
||||||
-- (REPLACE(UUID(),'-',''), 'REPLACE_WITH_BUSINESS_ID', 'INTERSECT_ALLOWLIST',
|
|
||||||
-- '["REPLACE_ZONE_A","REPLACE_ZONE_B"]', NULL, 1, 1, '实施录入', UNIX_TIMESTAMP(NOW())*1000, UNIX_TIMESTAMP(NOW())*1000);
|
|
||||||
|
|||||||
@@ -10,9 +10,12 @@
|
|||||||
-- zone_id = 605560545117995008(zone_name=28F,code=0x1C)
|
-- zone_id = 605560545117995008(zone_name=28F,code=0x1C)
|
||||||
--
|
--
|
||||||
-- 重复执行:使用固定 id + ON DUPLICATE KEY UPDATE,幂等。
|
-- 重复执行:使用固定 id + ON DUPLICATE KEY UPDATE,幂等。
|
||||||
|
-- 请先执行 tenant_visitor_floor_policy.sql(会先 DROP 再建表)。
|
||||||
|
|
||||||
SET NAMES utf8mb4;
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
|
USE `cw-elevator-application`;
|
||||||
|
|
||||||
INSERT INTO tenant_visitor_floor_policy (
|
INSERT INTO tenant_visitor_floor_policy (
|
||||||
id,
|
id,
|
||||||
org_id,
|
org_id,
|
||||||
|
|||||||
@@ -14,9 +14,12 @@
|
|||||||
-- zone_id = 605560541473144832(code=0x06)
|
-- zone_id = 605560541473144832(code=0x06)
|
||||||
--
|
--
|
||||||
-- 重复执行:使用固定 id + ON DUPLICATE KEY UPDATE。
|
-- 重复执行:使用固定 id + ON DUPLICATE KEY UPDATE。
|
||||||
|
-- 请先执行 tenant_visitor_floor_policy.sql(会先 DROP 再建表)。
|
||||||
|
|
||||||
SET NAMES utf8mb4;
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
|
USE `cw-elevator-application`;
|
||||||
|
|
||||||
-- ============================================================
|
-- ============================================================
|
||||||
-- 1. 星河湾物业管理有限公司
|
-- 1. 星河湾物业管理有限公司
|
||||||
-- ============================================================
|
-- ============================================================
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
-- 租户访客楼层策略:org_id 粒度修复
|
-- 租户访客楼层策略:org_id 粒度修复(原地 ALTER,不删表)
|
||||||
-- 执行顺序:先 DDL → 数据迁移 → 发应用包
|
-- 若已执行新版 tenant_visitor_floor_policy.sql(内含 DROP+CREATE 完整结构),请勿再执行本文件。
|
||||||
|
-- 执行顺序(历史流程):先 DDL → 数据迁移 → 发应用包
|
||||||
-- 回滚:DROP INDEX uk_org_building, DROP COLUMN org_id, ADD UNIQUE KEY uk_biz_building (business_id, building_id)
|
-- 回滚:DROP INDEX uk_org_building, DROP COLUMN org_id, ADD UNIQUE KEY uk_biz_building (business_id, building_id)
|
||||||
|
|
||||||
USE `cw-elevator-application`;
|
USE `cw-elevator-application`;
|
||||||
|
|||||||
@@ -0,0 +1,132 @@
|
|||||||
|
# 预存问题 — 当前代码可完善项
|
||||||
|
|
||||||
|
**日期**: 2026-05-06
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 可修复: 4 项 (覆盖 ~38,000 错误/天)
|
||||||
|
|
||||||
|
### 1. 🔴 P0: addFace 图片为空无限重试 (36,560次)
|
||||||
|
|
||||||
|
**文件**: `CpImageStoreToolServiceImpl.java` L335-388
|
||||||
|
|
||||||
|
**根因**:
|
||||||
|
```java
|
||||||
|
// L343: 人员不存在时创建空对象
|
||||||
|
ImgStorePerson imgStorePerson = CollectionUtils.isEmpty(personList)
|
||||||
|
? new ImgStorePerson() // ← 空person,comparePicture=null
|
||||||
|
: personList.get(0);
|
||||||
|
// L346: 拿到的图片URL为空
|
||||||
|
extractParam.setImageUrl(imgStorePerson.getComparePicture());
|
||||||
|
// → 特征提取异常 → L386 日志 "图片为空"
|
||||||
|
```
|
||||||
|
|
||||||
|
**修复**:
|
||||||
|
```java
|
||||||
|
// 人员不存在时直接返回失败,不继续处理
|
||||||
|
if (CollectionUtils.isEmpty(personList)) {
|
||||||
|
this.logger.warn("人员不存在 imageId={}", param.getImageId());
|
||||||
|
return CloudwalkResult.fail("53060434", getMessage("53060434"));
|
||||||
|
}
|
||||||
|
ImgStorePerson imgStorePerson = personList.get(0);
|
||||||
|
if (StringUtils.isBlank(imgStorePerson.getComparePicture())) {
|
||||||
|
this.logger.warn("人员图片为空 personId={} imageId={}",
|
||||||
|
imgStorePerson.getId(), param.getImageId());
|
||||||
|
return CloudwalkResult.fail("53060434", getMessage("53060434"));
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**影响**: 消除 36,560 次错误日志 (占全部错误 45%)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2. 🟠 P1: addValidateData Redis 锁竞争 (688+322次)
|
||||||
|
|
||||||
|
**文件**: `CpImageStorePersonValidateManager.java` L102-141, L135-185
|
||||||
|
|
||||||
|
**根因**:
|
||||||
|
- `group-person-syn-pool` 线程池并发调用 `addValidateData`
|
||||||
|
- `removeMap`/`addMap` 中多个 entry 竞争同一个 Quartz job 的 Redis 锁
|
||||||
|
- 688 次 error 集中在同一秒 (2026-02-27 15:14:26)
|
||||||
|
|
||||||
|
**修复**:
|
||||||
|
```java
|
||||||
|
// L103: 在 for-loop 之前对 removeMap 按时间戳去重
|
||||||
|
// 避免多个线程处理相同的 job key
|
||||||
|
for (Map.Entry<Long, Set<SyncPersonLocal>> entry : dedupedRemoveMap.entrySet()) {
|
||||||
|
// L107: 锁获取失败时不打 ERROR,改为 WARN
|
||||||
|
if (!this.validateJobGroupLock(l, lockValue)) {
|
||||||
|
log.warn("获取job锁失败(可能已被其他线程处理) key={}", l);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// ... 原逻辑 ...
|
||||||
|
}
|
||||||
|
// L185: 同样处理 addMap
|
||||||
|
```
|
||||||
|
|
||||||
|
**影响**: 消除 1,010 次 error 日志 + 减少 Redis 连接竞争
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3. 🟡 P2: 图片角度识别失败 (35次)
|
||||||
|
|
||||||
|
**文件**: `ImageEditUtils.java` L140-148
|
||||||
|
|
||||||
|
**根因**: Commons Imaging 库解析 TIFF/JPEG 格式时抛异常
|
||||||
|
|
||||||
|
**修复**:
|
||||||
|
```java
|
||||||
|
// L145: 添加更详细的错误日志 + 降级处理
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("图片角度识别失败, 降级为0度: {}", e.getMessage());
|
||||||
|
return 0; // 降级为不旋转,而不是抛出异常
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**影响**: 消除 35 次 error 日志 + 提高图片处理容错性
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 4. 🟡 P2: device updatePerson 空指针 (4,749次)
|
||||||
|
|
||||||
|
**文件**: `CpOrgDeviceKitController.java` L160 (web 模块)
|
||||||
|
|
||||||
|
**根因**: `cause:` 后为空 — 下游返回 null
|
||||||
|
|
||||||
|
**修复**:
|
||||||
|
```java
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("device updatePerson error deviceCode={} cause={}",
|
||||||
|
param.getDeviceId(),
|
||||||
|
e.getMessage() != null ? e.getMessage() : e.getClass().getSimpleName(),
|
||||||
|
e); // 打印完整 stack trace
|
||||||
|
return CloudwalkResult.fail("53060411", getMessage("53060411"));
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**影响**: 可追踪 4,749 次失败的真实原因
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 不可修复: 5 项 (下游/基础设施)
|
||||||
|
|
||||||
|
| 错误 | 原因 |
|
||||||
|
|------|------|
|
||||||
|
| AggDeviceImageStoreFeignClient failed (36K) | cwos-portal 服务不可达 — 运维排查 |
|
||||||
|
| AtomicDeviceFeignClient failed (368) | 设备管理服务不可达 |
|
||||||
|
| VehicleFeignClient failed (68) | 车牌服务不可达 |
|
||||||
|
| ElevatorFeignClient failed (15) | 电梯服务不可达 |
|
||||||
|
| MySQL connection lost (13) | 数据库连接池配置 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 实施优先级
|
||||||
|
|
||||||
|
| 顺序 | 修复项 | 文件 | 消除错误数 | 预估行数 |
|
||||||
|
|------|--------|------|-----------|---------|
|
||||||
|
| 1 | addFace 空图片 | CpImageStoreToolServiceImpl.java | 36,560 | +8行 |
|
||||||
|
| 2 | Redis 锁竞争 | CpImageStorePersonValidateManager.java | 1,010 | +5行 |
|
||||||
|
| 3 | device updatePerson | CpOrgDeviceKitController.java | 4,749 | +3行 |
|
||||||
|
| 4 | 图片角度识别 | ImageEditUtils.java | 35 | +2行 |
|
||||||
|
|
||||||
|
**总计**: 4 文件, ~18 行代码, 消除 ~42,354 错误/天 (53%)
|
||||||
+49
@@ -0,0 +1,49 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# 将访客邀约页初始化测试脚本打为 tar.gz / zip,便于现场/运维拷贝部署验证。
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
cd "$SCRIPT_DIR"
|
||||||
|
|
||||||
|
STAMP="${STAMP:-$(date +%Y%m%d)}"
|
||||||
|
BUNDLE_ROOT="visitor-invite-page-init-test-${STAMP}"
|
||||||
|
DIST_DIR="${SCRIPT_DIR}/dist"
|
||||||
|
OUT_TAR_GZ="${DIST_DIR}/${BUNDLE_ROOT}.tar.gz"
|
||||||
|
OUT_ZIP="${DIST_DIR}/${BUNDLE_ROOT}.zip"
|
||||||
|
|
||||||
|
need_files=(
|
||||||
|
"visitor_invite_page_init_example.py"
|
||||||
|
"run_visitor_invite_page_one_click.sh"
|
||||||
|
"requirements-visitor-invite-test.txt"
|
||||||
|
)
|
||||||
|
|
||||||
|
for f in "${need_files[@]}" "visitor-invite-test-bundle-README.txt"; do
|
||||||
|
if [[ ! -f "$f" ]]; then
|
||||||
|
echo "ERROR: 缺少文件: $SCRIPT_DIR/$f" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
mkdir -p "$DIST_DIR"
|
||||||
|
rm -rf "${DIST_DIR}/${BUNDLE_ROOT}"
|
||||||
|
mkdir -p "${DIST_DIR}/${BUNDLE_ROOT}"
|
||||||
|
|
||||||
|
for f in "${need_files[@]}"; do
|
||||||
|
cp -a "$f" "${DIST_DIR}/${BUNDLE_ROOT}/"
|
||||||
|
done
|
||||||
|
cp -a "visitor-invite-test-bundle-README.txt" "${DIST_DIR}/${BUNDLE_ROOT}/README.txt"
|
||||||
|
|
||||||
|
chmod +x "${DIST_DIR}/${BUNDLE_ROOT}/run_visitor_invite_page_one_click.sh"
|
||||||
|
|
||||||
|
( cd "$DIST_DIR" && tar -czf "${BUNDLE_ROOT}.tar.gz" "$BUNDLE_ROOT" )
|
||||||
|
( cd "$DIST_DIR" && zip -qr "${BUNDLE_ROOT}.zip" "$BUNDLE_ROOT" )
|
||||||
|
|
||||||
|
( cd "$DIST_DIR" && sha256sum "${BUNDLE_ROOT}.tar.gz" "${BUNDLE_ROOT}.zip" > "${BUNDLE_ROOT}.sha256" )
|
||||||
|
|
||||||
|
ls -la "$OUT_TAR_GZ" "$OUT_ZIP" "${DIST_DIR}/${BUNDLE_ROOT}.sha256"
|
||||||
|
echo ""
|
||||||
|
echo "==> 已生成:"
|
||||||
|
echo " $OUT_TAR_GZ"
|
||||||
|
echo " $OUT_ZIP"
|
||||||
|
echo " ${DIST_DIR}/${BUNDLE_ROOT}.sha256"
|
||||||
|
echo "==> 校验: (cd $DIST_DIR && sha256sum -c ${BUNDLE_ROOT}.sha256)"
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
requests>=2.28.0
|
||||||
|
pymysql>=1.0.0
|
||||||
+41
@@ -0,0 +1,41 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# 一键:访客邀约页初始化测试(默认 DB 样本 + 输出 JSON)
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
# 仓库内:docs/testing → ../../scripts/...;独立部署包解压后无此文件则仅用下方默认环境变量
|
||||||
|
for _env in \
|
||||||
|
"${SCRIPT_DIR}/../../scripts/test-env/config/env.sh" \
|
||||||
|
"${SCRIPT_DIR}/../../../scripts/test-env/config/env.sh" \
|
||||||
|
"${SCRIPT_DIR}/../../../../scripts/test-env/config/env.sh"; do
|
||||||
|
if [[ -f "$_env" ]]; then
|
||||||
|
# shellcheck disable=SC1091
|
||||||
|
source "$_env"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
: "${PORT_COMPONENT_ORG:=33011}"
|
||||||
|
: "${MYSQL_HOST:=127.0.0.1}"
|
||||||
|
: "${MYSQL_PORT:=3307}"
|
||||||
|
: "${MYSQL_USER:=root}"
|
||||||
|
: "${MYSQL_PASS:=}"
|
||||||
|
|
||||||
|
# 组织组件 HTTP 与 MySQL 主机未必相同;默认本机 PORT_COMPONENT_ORG
|
||||||
|
export ORG_BASE="${ORG_BASE:-http://127.0.0.1:${PORT_COMPONENT_ORG}}"
|
||||||
|
export MYSQL_HOST MYSQL_PORT MYSQL_USER MYSQL_PASS
|
||||||
|
export DB_COMPONENT_ORG="${DB_COMPONENT_ORG:-component-organization}"
|
||||||
|
export DB_ELEVATOR="${DB_ELEVATOR:-cw-elevator-application}"
|
||||||
|
|
||||||
|
PY="${SCRIPT_DIR}/visitor_invite_page_init_example.py"
|
||||||
|
if [[ ! -f "$PY" ]]; then
|
||||||
|
echo "缺少脚本: $PY" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
python3 - <<'PYCHK' || { python3 -m pip install -q requests pymysql; }
|
||||||
|
import requests
|
||||||
|
import pymysql
|
||||||
|
PYCHK
|
||||||
|
|
||||||
|
echo "ORG_BASE=$ORG_BASE MYSQL=${MYSQL_HOST}:${MYSQL_PORT}" >&2
|
||||||
|
exec python3 "$PY" "$@"
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
访客邀约页初始化 — 部署测试包
|
||||||
|
================================
|
||||||
|
|
||||||
|
内含文件
|
||||||
|
--------
|
||||||
|
- visitor_invite_page_init_example.py 主程序(读库样本 + 调组织 /component/person/detail + 楼层预览 JSON)
|
||||||
|
- run_visitor_invite_page_one_click.sh 一键入口
|
||||||
|
- requirements-visitor-invite-test.txt Python 依赖
|
||||||
|
|
||||||
|
环境要求
|
||||||
|
--------
|
||||||
|
- Python 3.8+(建议 3.10)
|
||||||
|
- 可访问 MySQL(component-organization、cw-elevator-application)
|
||||||
|
- 可 HTTP 访问组织组件(默认端口常与本仓库 test-env 中 PORT_COMPONENT_ORG 一致)
|
||||||
|
|
||||||
|
安装依赖
|
||||||
|
--------
|
||||||
|
pip3 install -r requirements-visitor-invite-test.txt
|
||||||
|
|
||||||
|
一键执行(stdout 为完整 JSON,stderr 为日志)
|
||||||
|
--------
|
||||||
|
export MYSQL_HOST=<主机>
|
||||||
|
export MYSQL_PORT=3307
|
||||||
|
export MYSQL_USER=root
|
||||||
|
export MYSQL_PASS=<密码>
|
||||||
|
export ORG_BASE=http://<组织组件>:<端口>
|
||||||
|
bash run_visitor_invite_page_one_click.sh
|
||||||
|
|
||||||
|
可选:仅命令行指定被访人(不读库样本)
|
||||||
|
--------
|
||||||
|
python3 visitor_invite_page_init_example.py --no-db \
|
||||||
|
--business-id <BUSINESS_ID> --host-person-id <PERSON_ID>
|
||||||
|
|
||||||
|
说明
|
||||||
|
----
|
||||||
|
- 默认从库选一名「非访客标签」被访人,并从电梯库 tenant_visitor_floor_policy 取策略 allow_zone_ids(若存在)。
|
||||||
|
- 完整产品说明见源码仓库 docs/business/租户访客默认楼层技术产品方案.md(若未带仓库可忽略)。
|
||||||
|
|
||||||
|
版本:见压缩包文件名中的日期戳。
|
||||||
@@ -0,0 +1,372 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
访客邀约页初始化 —— 一键测试(默认使用数据库中的样本数据)
|
||||||
|
|
||||||
|
1)从 MySQL 读取:
|
||||||
|
- component-organization:一名在职被访人(排除「访客」标签)的 BUSINESS_ID、人员 ID
|
||||||
|
- cw-elevator-application:tenant_visitor_floor_policy(启用策略,优先匹配同一 business_id)的 allow_zone_ids
|
||||||
|
|
||||||
|
2)调用组织组件 POST /component/person/detail,计算 effectiveZoneIds 预览
|
||||||
|
|
||||||
|
依赖:pip install requests pymysql
|
||||||
|
|
||||||
|
一键(推荐走测试环境变量):
|
||||||
|
source ../../scripts/test-env/config/env.sh
|
||||||
|
python3 visitor_invite_page_init_example.py
|
||||||
|
|
||||||
|
或直接:
|
||||||
|
bash run_visitor_invite_page_one_click.sh
|
||||||
|
|
||||||
|
输出:仅向 stdout 打印 **一行合法 JSON**(便于 jq);进度写在 stderr。
|
||||||
|
|
||||||
|
文档:docs/business/租户访客默认楼层技术产品方案.md §2.4
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from datetime import datetime, timezone
|
||||||
|
from typing import Any, Dict, List, Optional, Set, Tuple
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
|
|
||||||
|
def log(msg: str) -> None:
|
||||||
|
print(msg, file=sys.stderr)
|
||||||
|
|
||||||
|
|
||||||
|
def cloudwalk_ok(body: Optional[Dict[str, Any]]) -> bool:
|
||||||
|
if not body or not isinstance(body, dict):
|
||||||
|
return False
|
||||||
|
code = body.get("code")
|
||||||
|
if code is None and "data" in body:
|
||||||
|
return True
|
||||||
|
return str(code) in ("0", "200", "00000000")
|
||||||
|
|
||||||
|
|
||||||
|
def extract_host_zone_ids(person_data: Dict[str, Any]) -> List[str]:
|
||||||
|
fl = person_data.get("floorList")
|
||||||
|
if not fl:
|
||||||
|
return []
|
||||||
|
out: List[str] = []
|
||||||
|
for x in fl:
|
||||||
|
if x is None:
|
||||||
|
continue
|
||||||
|
s = str(x).strip()
|
||||||
|
if s:
|
||||||
|
out.append(s)
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def compute_effective_preview(
|
||||||
|
host_zone_ids: List[str],
|
||||||
|
policy_allow_zone_ids_json: Optional[str],
|
||||||
|
) -> Dict[str, Any]:
|
||||||
|
if not policy_allow_zone_ids_json or not str(policy_allow_zone_ids_json).strip():
|
||||||
|
return {
|
||||||
|
"effective_zone_ids": list(host_zone_ids),
|
||||||
|
"mode": "host_only_no_policy",
|
||||||
|
}
|
||||||
|
try:
|
||||||
|
allow_raw = json.loads(policy_allow_zone_ids_json)
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
return {"error": str(e), "effective_zone_ids": [], "mode": "policy_json_invalid"}
|
||||||
|
allow_set: Set[str] = {str(x).strip() for x in allow_raw if x is not None}
|
||||||
|
eff = [z for z in host_zone_ids if z in allow_set]
|
||||||
|
return {
|
||||||
|
"effective_zone_ids": eff,
|
||||||
|
"mode": "intersect_host_with_allow_list",
|
||||||
|
"allow_zone_count": len(allow_set),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def fetch_sample_from_mysql(
|
||||||
|
host: str,
|
||||||
|
port: int,
|
||||||
|
user: str,
|
||||||
|
password: str,
|
||||||
|
org_db: str,
|
||||||
|
elevator_db: str,
|
||||||
|
) -> Tuple[Dict[str, Any], Optional[str]]:
|
||||||
|
"""
|
||||||
|
Returns (resolution_meta, policy_allow_zone_ids_json or None).
|
||||||
|
resolution_meta 至少含 business_id, host_person_id, host_name。
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
import pymysql # type: ignore
|
||||||
|
except ImportError as e:
|
||||||
|
raise RuntimeError(f"需要安装 pymysql: pip install pymysql ({e})") from e
|
||||||
|
|
||||||
|
meta: Dict[str, Any] = {
|
||||||
|
"org_database": org_db,
|
||||||
|
"elevator_database": elevator_db,
|
||||||
|
"host_row": None,
|
||||||
|
"policy_row": None,
|
||||||
|
}
|
||||||
|
|
||||||
|
conn_kw = dict(
|
||||||
|
host=host,
|
||||||
|
port=port,
|
||||||
|
user=user,
|
||||||
|
password=password,
|
||||||
|
charset="utf8mb4",
|
||||||
|
connect_timeout=10,
|
||||||
|
read_timeout=30,
|
||||||
|
write_timeout=30,
|
||||||
|
)
|
||||||
|
|
||||||
|
business_id = ""
|
||||||
|
host_person_id = ""
|
||||||
|
host_name = ""
|
||||||
|
|
||||||
|
with pymysql.connect(database=org_db, cursorclass=pymysql.cursors.DictCursor, **conn_kw) as conn:
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.execute(
|
||||||
|
"""
|
||||||
|
SELECT p.BUSINESS_ID AS business_id, p.ID AS host_person_id, p.NAME AS host_name
|
||||||
|
FROM cw_is_person p
|
||||||
|
INNER JOIN cw_is_person_organization_ref por ON por.PERSON_ID = p.ID
|
||||||
|
WHERE IFNULL(p.IS_DEL, 0) = 0
|
||||||
|
AND p.BUSINESS_ID IS NOT NULL AND p.BUSINESS_ID <> ''
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM cw_is_person_label_ref lr
|
||||||
|
INNER JOIN cw_is_label lb ON lb.ID = lr.LABEL_ID
|
||||||
|
WHERE lr.PERSON_ID = p.ID
|
||||||
|
AND lb.BUSINESS_ID = p.BUSINESS_ID
|
||||||
|
AND lb.NAME = '访客'
|
||||||
|
)
|
||||||
|
ORDER BY IFNULL(p.LAST_UPDATE_TIME, 0) DESC
|
||||||
|
LIMIT 1
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
row = cur.fetchone()
|
||||||
|
if not row:
|
||||||
|
raise RuntimeError(
|
||||||
|
f"组织库 {org_db} 未查询到可用被访人样本(需 cw_is_person + 组织挂靠且非访客标签)"
|
||||||
|
)
|
||||||
|
business_id = str(row["business_id"]).strip()
|
||||||
|
host_person_id = str(row["host_person_id"]).strip()
|
||||||
|
host_name = str(row.get("host_name") or "").strip()
|
||||||
|
meta["host_row"] = {
|
||||||
|
"business_id": business_id,
|
||||||
|
"host_person_id": host_person_id,
|
||||||
|
"host_name": host_name,
|
||||||
|
}
|
||||||
|
|
||||||
|
policy_allow_json: Optional[str] = None
|
||||||
|
try:
|
||||||
|
with pymysql.connect(database=elevator_db, cursorclass=pymysql.cursors.DictCursor, **conn_kw) as conn:
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.execute(
|
||||||
|
"""
|
||||||
|
SELECT id, business_id, org_id, allow_zone_ids, enabled, remark
|
||||||
|
FROM tenant_visitor_floor_policy
|
||||||
|
WHERE IFNULL(enabled, 0) = 1
|
||||||
|
ORDER BY
|
||||||
|
CASE WHEN business_id <=> %s THEN 0 ELSE 1 END,
|
||||||
|
IFNULL(updated_at, 0) DESC
|
||||||
|
LIMIT 1
|
||||||
|
""",
|
||||||
|
(business_id,),
|
||||||
|
)
|
||||||
|
prow = cur.fetchone()
|
||||||
|
if prow and prow.get("allow_zone_ids"):
|
||||||
|
policy_allow_json = str(prow["allow_zone_ids"]).strip()
|
||||||
|
meta["policy_row"] = {
|
||||||
|
"id": prow.get("id"),
|
||||||
|
"business_id": prow.get("business_id"),
|
||||||
|
"org_id": prow.get("org_id"),
|
||||||
|
"allow_zone_ids": policy_allow_json,
|
||||||
|
"remark": (prow.get("remark") or "")[:200],
|
||||||
|
}
|
||||||
|
except Exception as ex:
|
||||||
|
meta["policy_query_error"] = str(ex)
|
||||||
|
|
||||||
|
return meta, policy_allow_json
|
||||||
|
|
||||||
|
|
||||||
|
def run_http_flow(
|
||||||
|
org_base_url: str,
|
||||||
|
business_id: str,
|
||||||
|
host_person_id: str,
|
||||||
|
policy_allow_json: Optional[str],
|
||||||
|
common_base_url: str,
|
||||||
|
timeout: float,
|
||||||
|
) -> Dict[str, Any]:
|
||||||
|
session = requests.Session()
|
||||||
|
detail_url = org_base_url.rstrip("/") + "/component/person/detail"
|
||||||
|
headers = {
|
||||||
|
"Content-Type": "application/json;charset=UTF-8",
|
||||||
|
"businessid": business_id,
|
||||||
|
}
|
||||||
|
payload = {"id": host_person_id, "businessId": business_id}
|
||||||
|
|
||||||
|
log(f"POST {detail_url}")
|
||||||
|
r = session.post(detail_url, json=payload, headers=headers, timeout=timeout)
|
||||||
|
detail_http = r.status_code
|
||||||
|
try:
|
||||||
|
detail_body: Any = r.json()
|
||||||
|
except Exception:
|
||||||
|
detail_body = {"_parse_error": True, "text_head": (r.text or "")[:2000]}
|
||||||
|
|
||||||
|
step_detail = {
|
||||||
|
"request": payload,
|
||||||
|
"http_status": detail_http,
|
||||||
|
"response": detail_body,
|
||||||
|
}
|
||||||
|
|
||||||
|
if isinstance(detail_body, dict) and not cloudwalk_ok(detail_body):
|
||||||
|
return {
|
||||||
|
"ok": False,
|
||||||
|
"error": "person_detail_business_failed",
|
||||||
|
"steps": {"person_detail": step_detail},
|
||||||
|
}
|
||||||
|
|
||||||
|
data = detail_body.get("data") if isinstance(detail_body, dict) else {}
|
||||||
|
if not isinstance(data, dict):
|
||||||
|
data = {}
|
||||||
|
|
||||||
|
host_zone_ids = extract_host_zone_ids(data)
|
||||||
|
preview = compute_effective_preview(host_zone_ids, policy_allow_json)
|
||||||
|
|
||||||
|
zone_try: Optional[Dict[str, Any]] = None
|
||||||
|
if common_base_url.strip():
|
||||||
|
url = common_base_url.rstrip("/") + "/sysetting/zone/list"
|
||||||
|
zpayload: Dict[str, Any] = {"zoneIds": preview.get("effective_zone_ids") or []}
|
||||||
|
try:
|
||||||
|
zr = session.post(url, json=zpayload, headers=headers, timeout=timeout)
|
||||||
|
zone_try = {"url": url, "http_status": zr.status_code}
|
||||||
|
try:
|
||||||
|
zone_try["response"] = zr.json()
|
||||||
|
except Exception:
|
||||||
|
zone_try["text_head"] = (zr.text or "")[:1200]
|
||||||
|
except Exception as ex:
|
||||||
|
zone_try = {"url": url, "error": str(ex)}
|
||||||
|
|
||||||
|
page_init = {
|
||||||
|
"businessId": business_id,
|
||||||
|
"hostPersonId": host_person_id,
|
||||||
|
"hostPersonName": data.get("name"),
|
||||||
|
"hostZoneIds": host_zone_ids,
|
||||||
|
"effectiveZoneIds": preview.get("effective_zone_ids"),
|
||||||
|
"policyMode": preview.get("mode"),
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
"ok": True,
|
||||||
|
"started_at": datetime.now(timezone.utc).isoformat(),
|
||||||
|
"steps": {
|
||||||
|
"person_detail": step_detail,
|
||||||
|
"floor_preview": {
|
||||||
|
"policy_allow_zone_ids_json": policy_allow_json,
|
||||||
|
**preview,
|
||||||
|
},
|
||||||
|
"zone_optional": zone_try,
|
||||||
|
},
|
||||||
|
"page_init": page_init,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
ap = argparse.ArgumentParser(description="访客邀约页初始化一键测试")
|
||||||
|
ap.add_argument(
|
||||||
|
"--org-base-url",
|
||||||
|
default=os.environ.get("ORG_BASE", os.environ.get("COMPONENT_ORG_URL", "http://127.0.0.1:33011")),
|
||||||
|
help="组织组件 HTTP 基址(测试环境常见 PORT_COMPONENT_ORG)",
|
||||||
|
)
|
||||||
|
ap.add_argument("--business-id", default=os.environ.get("BUSINESS_ID", "").strip())
|
||||||
|
ap.add_argument("--host-person-id", default=os.environ.get("HOST_PERSON_ID", "").strip())
|
||||||
|
ap.add_argument("--policy-allow-json", default=os.environ.get("POLICY_ALLOW_ZONE_IDS_JSON", "").strip())
|
||||||
|
ap.add_argument("--common-base-url", default=os.environ.get("COMMON_BASE", "").strip())
|
||||||
|
ap.add_argument("--timeout", type=float, default=float(os.environ.get("HTTP_TIMEOUT", "30")))
|
||||||
|
ap.add_argument(
|
||||||
|
"--mysql-host",
|
||||||
|
default=os.environ.get("MYSQL_HOST", "127.0.0.1"),
|
||||||
|
)
|
||||||
|
ap.add_argument(
|
||||||
|
"--mysql-port",
|
||||||
|
type=int,
|
||||||
|
default=int(os.environ.get("MYSQL_PORT", "3307")),
|
||||||
|
)
|
||||||
|
ap.add_argument("--mysql-user", default=os.environ.get("MYSQL_USER", "root"))
|
||||||
|
ap.add_argument(
|
||||||
|
"--mysql-password",
|
||||||
|
default=os.environ.get("MYSQL_PASS", os.environ.get("MYSQL_PASSWORD", "")),
|
||||||
|
)
|
||||||
|
ap.add_argument(
|
||||||
|
"--org-database",
|
||||||
|
default=os.environ.get("DB_COMPONENT_ORG", "component-organization"),
|
||||||
|
)
|
||||||
|
ap.add_argument(
|
||||||
|
"--elevator-database",
|
||||||
|
default=os.environ.get("DB_ELEVATOR", "cw-elevator-application"),
|
||||||
|
)
|
||||||
|
ap.add_argument(
|
||||||
|
"--no-db",
|
||||||
|
action="store_true",
|
||||||
|
help="不从数据库拉样本(必须提供 --business-id 与 --host-person-id)",
|
||||||
|
)
|
||||||
|
args = ap.parse_args()
|
||||||
|
|
||||||
|
report: Dict[str, Any] = {"tool": "visitor_invite_page_init_example", "ok": False}
|
||||||
|
|
||||||
|
policy_allow = args.policy_allow_json or None
|
||||||
|
meta_db: Optional[Dict[str, Any]] = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
if not args.no_db and (not args.business_id or not args.host_person_id):
|
||||||
|
log("从数据库加载默认样本(BUSINESS_ID / 被访人 / 策略 allow_zone_ids)…")
|
||||||
|
meta_db, policy_allow = fetch_sample_from_mysql(
|
||||||
|
args.mysql_host,
|
||||||
|
args.mysql_port,
|
||||||
|
args.mysql_user,
|
||||||
|
args.mysql_password,
|
||||||
|
args.org_database,
|
||||||
|
args.elevator_database,
|
||||||
|
)
|
||||||
|
args.business_id = meta_db["host_row"]["business_id"]
|
||||||
|
args.host_person_id = meta_db["host_row"]["host_person_id"]
|
||||||
|
report["resolved_from_database"] = meta_db
|
||||||
|
elif not args.business_id or not args.host_person_id:
|
||||||
|
log("错误:未指定 --business-id / --host-person-id,且未使用数据库默认。", file=sys.stderr)
|
||||||
|
report["error"] = "missing_ids_use_db_or_pass_explicit"
|
||||||
|
print(json.dumps(report, ensure_ascii=False))
|
||||||
|
return 2
|
||||||
|
|
||||||
|
if policy_allow:
|
||||||
|
report["policy_allow_zone_ids_source"] = (
|
||||||
|
"database" if meta_db and meta_db.get("policy_row") else "cli_or_env"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
report["policy_allow_zone_ids_source"] = "none"
|
||||||
|
|
||||||
|
result = run_http_flow(
|
||||||
|
args.org_base_url,
|
||||||
|
args.business_id,
|
||||||
|
args.host_person_id,
|
||||||
|
policy_allow,
|
||||||
|
args.common_base_url,
|
||||||
|
args.timeout,
|
||||||
|
)
|
||||||
|
report.update(result)
|
||||||
|
if not report.get("ok"):
|
||||||
|
print(json.dumps(report, ensure_ascii=False))
|
||||||
|
return 1
|
||||||
|
|
||||||
|
log("测试完成。完整 JSON 见 stdout。")
|
||||||
|
print(json.dumps(report, ensure_ascii=False))
|
||||||
|
return 0
|
||||||
|
except Exception as ex:
|
||||||
|
report["ok"] = False
|
||||||
|
report["error"] = str(ex)
|
||||||
|
print(json.dumps(report, ensure_ascii=False))
|
||||||
|
log(str(ex), file=sys.stderr)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
+833
@@ -0,0 +1,833 @@
|
|||||||
|
/*
|
||||||
|
* 自 vendor cwos-component-organization-service-v2.9.2_xinghewan 反编译恢复;与现场逻辑一致。
|
||||||
|
* 原因为 reactor 曾排除 vendor jar 导致该 @Service 未打入 classpath。
|
||||||
|
*/
|
||||||
|
package cn.cloudwalk.service.organization.service;
|
||||||
|
|
||||||
|
|
||||||
|
import cn.cloudwalk.client.account.account.param.GeneralQueryBusinessParam;
|
||||||
|
import cn.cloudwalk.client.account.account.result.AcBusinessDTO;
|
||||||
|
import cn.cloudwalk.client.account.account.service.AcBusinessService;
|
||||||
|
import cn.cloudwalk.client.aggregate.application.param.ApplicationImageStoreQueryParam;
|
||||||
|
import cn.cloudwalk.client.aggregate.application.service.ApplicationImageStoreService;
|
||||||
|
import cn.cloudwalk.client.aggregate.common.enums.DelStatusEnum;
|
||||||
|
import cn.cloudwalk.client.aggregate.common.enums.SyncStatusEnum;
|
||||||
|
import cn.cloudwalk.client.aggregate.device.param.DeviceImageStoreQueryParam;
|
||||||
|
import cn.cloudwalk.client.aggregate.device.result.DeviceImageStoreQueryResult;
|
||||||
|
import cn.cloudwalk.client.aggregate.device.service.AggDeviceImageStoreService;
|
||||||
|
import cn.cloudwalk.client.aggregate.group.param.AgImageStoreAddParam;
|
||||||
|
import cn.cloudwalk.client.aggregate.group.param.AgImageStoreEditParam;
|
||||||
|
import cn.cloudwalk.client.aggregate.group.param.AgImageStoreKeyParam;
|
||||||
|
import cn.cloudwalk.client.aggregate.group.param.AgImageStoreQueryParam;
|
||||||
|
import cn.cloudwalk.client.aggregate.group.result.AgImageStoreResult;
|
||||||
|
import cn.cloudwalk.client.aggregate.group.service.AgImageStoreService;
|
||||||
|
import cn.cloudwalk.client.device.mgn.atomic.param.CoreDeviceQueryParam;
|
||||||
|
import cn.cloudwalk.client.device.mgn.atomic.result.AtomicDeviceGetResult;
|
||||||
|
import cn.cloudwalk.client.device.mgn.atomic.service.AtomicDeviceService;
|
||||||
|
import cn.cloudwalk.client.organization.common.enums.CpImageStoreMatchPatternEnum;
|
||||||
|
import cn.cloudwalk.client.organization.service.store.param.AddImageStoreParam;
|
||||||
|
import cn.cloudwalk.client.organization.service.store.param.AssociatedParam;
|
||||||
|
import cn.cloudwalk.client.organization.service.store.param.BaseImageStoreParam;
|
||||||
|
import cn.cloudwalk.client.organization.service.store.param.DelImageStoreParam;
|
||||||
|
import cn.cloudwalk.client.organization.service.store.param.DetailImageStoreParam;
|
||||||
|
import cn.cloudwalk.client.organization.service.store.param.EditImageStoreParam;
|
||||||
|
import cn.cloudwalk.client.organization.service.store.param.QueryImageStoreParam;
|
||||||
|
import cn.cloudwalk.client.organization.service.store.result.AssociatedResult;
|
||||||
|
import cn.cloudwalk.client.organization.service.store.result.DeviceInfoResult;
|
||||||
|
import cn.cloudwalk.client.organization.service.store.result.ImageStoreDetailResult;
|
||||||
|
import cn.cloudwalk.client.organization.service.store.result.ImageStoreResult;
|
||||||
|
import cn.cloudwalk.client.organization.service.store.result.ImgStorePersonResult;
|
||||||
|
import cn.cloudwalk.client.organization.service.store.result.LabelResult;
|
||||||
|
import cn.cloudwalk.client.organization.service.store.result.OrganizationResult;
|
||||||
|
import cn.cloudwalk.client.organization.service.store.result.PageImageStoreResult;
|
||||||
|
import cn.cloudwalk.client.organization.service.store.service.CpImageStoreService;
|
||||||
|
import cn.cloudwalk.client.resource.application.param.ApplicationBasicParam;
|
||||||
|
import cn.cloudwalk.client.resource.application.param.ApplicationQueryParam;
|
||||||
|
import cn.cloudwalk.client.resource.application.result.ApplicationResult;
|
||||||
|
import cn.cloudwalk.client.resource.application.service.ApplicationService;
|
||||||
|
import cn.cloudwalk.client.resource.common.en.CommonStatusEnum;
|
||||||
|
import cn.cloudwalk.cloud.annotation.CloudwalkParamsValidate;
|
||||||
|
import cn.cloudwalk.cloud.context.CloudwalkCallContext;
|
||||||
|
import cn.cloudwalk.cloud.exception.ServiceException;
|
||||||
|
import cn.cloudwalk.cloud.page.CloudwalkPageAble;
|
||||||
|
import cn.cloudwalk.cloud.page.CloudwalkPageInfo;
|
||||||
|
import cn.cloudwalk.cloud.result.CloudwalkResult;
|
||||||
|
import cn.cloudwalk.cloud.utils.BeanCopyUtils;
|
||||||
|
import cn.cloudwalk.cloud.utils.CloudwalkDateUtils;
|
||||||
|
import cn.cloudwalk.data.organization.dto.DelGroupPersonDTO;
|
||||||
|
import cn.cloudwalk.data.organization.dto.GetsImageStoreAssociatedDTO;
|
||||||
|
import cn.cloudwalk.data.organization.dto.ImageStoreCountDTO;
|
||||||
|
import cn.cloudwalk.data.organization.dto.ImgStorePersonQueryDto;
|
||||||
|
import cn.cloudwalk.data.organization.dto.OrganizationImageStoreQueryDTO;
|
||||||
|
import cn.cloudwalk.data.organization.dto.QueryGroupPersonDTO;
|
||||||
|
import cn.cloudwalk.data.organization.entity.ImgStorePerson;
|
||||||
|
import cn.cloudwalk.data.organization.entity.IsImageStoreAssociated;
|
||||||
|
import cn.cloudwalk.data.organization.mapper.GroupPersonRefMapper;
|
||||||
|
import cn.cloudwalk.data.organization.mapper.ImgStoreLabelMapper;
|
||||||
|
import cn.cloudwalk.data.organization.mapper.ImgStoreOrganizationMapper;
|
||||||
|
import cn.cloudwalk.data.organization.mapper.ImgStorePersonMapper;
|
||||||
|
import cn.cloudwalk.data.organization.mapper.IsImageStoreAssociatedMapper;
|
||||||
|
import cn.cloudwalk.data.organization.mapper.OrganizationImageStoreMapper;
|
||||||
|
import cn.cloudwalk.service.organization.common.AbstractImagStoreService;
|
||||||
|
import cn.cloudwalk.service.organization.common.JsonUtils;
|
||||||
|
import cn.cloudwalk.service.organization.service.CpImageStorePersonManager;
|
||||||
|
import cn.cloudwalk.service.organization.service.CpImageStorePersonSynManager;
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import org.springframework.util.CollectionUtils;
|
||||||
|
import org.springside.modules.utils.Collections3;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||||
|
public class CpImageStoreServiceImpl
|
||||||
|
extends AbstractImagStoreService
|
||||||
|
implements CpImageStoreService {
|
||||||
|
@Autowired
|
||||||
|
private IsImageStoreAssociatedMapper isImageStoreAssociatedMapper;
|
||||||
|
@Autowired
|
||||||
|
private ImgStoreLabelMapper imgStoreLabelMapper;
|
||||||
|
@Autowired
|
||||||
|
private ImgStoreOrganizationMapper imgStoreOrganizationMapper;
|
||||||
|
@Autowired
|
||||||
|
private ImgStorePersonMapper personMapper;
|
||||||
|
@Autowired
|
||||||
|
private GroupPersonRefMapper groupPersonRefMapper;
|
||||||
|
@Autowired
|
||||||
|
private OrganizationImageStoreMapper organizationImageStoreMapper;
|
||||||
|
@Autowired
|
||||||
|
private AcBusinessService acBusinessService;
|
||||||
|
@Autowired
|
||||||
|
private ApplicationService applicationService;
|
||||||
|
@Autowired
|
||||||
|
private AgImageStoreService agImageStoreService;
|
||||||
|
@Autowired
|
||||||
|
private ApplicationImageStoreService applicationImageStoreService;
|
||||||
|
@Autowired
|
||||||
|
private AggDeviceImageStoreService deviceImageStoreService;
|
||||||
|
@Autowired
|
||||||
|
private CpImageStorePersonSynManager cpImageStorePersonSynManager;
|
||||||
|
@Resource
|
||||||
|
private CpImageStorePersonManager cpImageStorePersonManager;
|
||||||
|
@Resource
|
||||||
|
private AtomicDeviceService atomicDeviceService;
|
||||||
|
@Resource
|
||||||
|
private ApplicationImageStoreService appImageStoreService;
|
||||||
|
@Value(value="${cloudwalk.imagestore.person.searchSize:2}")
|
||||||
|
private int searchSize;
|
||||||
|
|
||||||
|
@Transactional(rollbackFor={Exception.class})
|
||||||
|
@CloudwalkParamsValidate
|
||||||
|
public CloudwalkResult<String> add(AddImageStoreParam param, CloudwalkCallContext context) throws ServiceException {
|
||||||
|
CloudwalkResult addResult;
|
||||||
|
List<IsImageStoreAssociated> imageStoreAssociatedList;
|
||||||
|
String imageStoreId = CloudwalkDateUtils.getUUID();
|
||||||
|
AgImageStoreAddParam addParam = new AgImageStoreAddParam();
|
||||||
|
addParam.setId(imageStoreId);
|
||||||
|
addParam.setName(param.getName());
|
||||||
|
addParam.setType(param.getType());
|
||||||
|
addParam.setBusinessId(param.getBusinessId());
|
||||||
|
addParam.setSourceApplicationId(param.getSourceApplicationId());
|
||||||
|
if (StringUtils.isBlank((CharSequence)param.getBusinessId())) {
|
||||||
|
addParam.setBusinessId(context.getCompany().getCompanyId());
|
||||||
|
}
|
||||||
|
if ((imageStoreAssociatedList = this.addBaseImageStore((BaseImageStoreParam)param, context, imageStoreId, addParam.getBusinessId())).size() > 0) {
|
||||||
|
this.isImageStoreAssociatedMapper.batchInsert(imageStoreAssociatedList);
|
||||||
|
}
|
||||||
|
if (!(addResult = this.agImageStoreService.add(addParam, context)).isSuccess()) {
|
||||||
|
throw new ServiceException(addResult.getCode(), addResult.getMessage());
|
||||||
|
}
|
||||||
|
this.cpImageStorePersonSynManager.addGroupPersonSynTask(Lists.newArrayList(imageStoreId), "isAll");
|
||||||
|
return CloudwalkResult.<String>success(imageStoreId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(rollbackFor={Exception.class})
|
||||||
|
@CloudwalkParamsValidate
|
||||||
|
public CloudwalkResult<Boolean> edit(EditImageStoreParam param, CloudwalkCallContext context) throws ServiceException {
|
||||||
|
CloudwalkResult editResult;
|
||||||
|
this.checkExistAndStatus(param.getImageStoreId());
|
||||||
|
AgImageStoreEditParam editParam = new AgImageStoreEditParam();
|
||||||
|
editParam.setId(param.getImageStoreId());
|
||||||
|
editParam.setName(param.getName());
|
||||||
|
String businessId = param.getBusinessId();
|
||||||
|
if (StringUtils.isBlank((CharSequence)businessId)) {
|
||||||
|
businessId = context.getCompany().getCompanyId();
|
||||||
|
}
|
||||||
|
List<IsImageStoreAssociated> imageStoreAssociatedList = this.addBaseImageStore((BaseImageStoreParam)param, context, param.getImageStoreId(), businessId);
|
||||||
|
if (this.checkAssociatedIsChange(param.getImageStoreId(), imageStoreAssociatedList)) {
|
||||||
|
this.isImageStoreAssociatedMapper.deleteAllByImageId(param.getImageStoreId());
|
||||||
|
if (imageStoreAssociatedList.size() > 0) {
|
||||||
|
this.isImageStoreAssociatedMapper.batchInsert(imageStoreAssociatedList);
|
||||||
|
}
|
||||||
|
editParam.setStatus(SyncStatusEnum.SYNC_WAIT.getCode());
|
||||||
|
}
|
||||||
|
if (!(editResult = this.agImageStoreService.edit(editParam, context)).isSuccess()) {
|
||||||
|
throw new ServiceException(editResult.getCode(), editResult.getMessage());
|
||||||
|
}
|
||||||
|
this.cpImageStorePersonSynManager.addGroupPersonSynTask(Lists.newArrayList(param.getImageStoreId()), "isAll");
|
||||||
|
return CloudwalkResult.<Boolean>success(Boolean.TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean checkAssociatedIsChange(String imageStoreId, List<IsImageStoreAssociated> associatedListAfterUpdate) {
|
||||||
|
GetsImageStoreAssociatedDTO queryDTO = new GetsImageStoreAssociatedDTO();
|
||||||
|
queryDTO.setImageStoreId(imageStoreId);
|
||||||
|
List<IsImageStoreAssociated> associatedListBeforeUpdate =
|
||||||
|
(List<IsImageStoreAssociated>) (List) this.isImageStoreAssociatedMapper.gets(queryDTO);
|
||||||
|
if (CollectionUtils.isEmpty((Collection)associatedListBeforeUpdate) && CollectionUtils.isEmpty(associatedListAfterUpdate)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (associatedListBeforeUpdate.size() != associatedListAfterUpdate.size()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
HashSet<String> associatedStrBeforeUpdate = new HashSet<String>(associatedListBeforeUpdate.size());
|
||||||
|
for (IsImageStoreAssociated associateBefore : associatedListBeforeUpdate) {
|
||||||
|
String action = associateBefore.getAssociatedAction() != null ? String.valueOf(associateBefore.getAssociatedAction()) : "";
|
||||||
|
associatedStrBeforeUpdate.add(action + associateBefore.getAssociatedObjectIdType() + associateBefore.getAssociatedObjectId() + associateBefore.getExpiryBeginDate() + associateBefore.getExpiryEndDate() + associateBefore.getValidDateCron());
|
||||||
|
}
|
||||||
|
HashSet<String> associatedStrAfterUpdate = new HashSet<String>(associatedListAfterUpdate.size());
|
||||||
|
for (IsImageStoreAssociated associateBeforeAfter : associatedListAfterUpdate) {
|
||||||
|
String action = associateBeforeAfter.getAssociatedAction() != null ? String.valueOf(associateBeforeAfter.getAssociatedAction()) : "";
|
||||||
|
associatedStrAfterUpdate.add(action + associateBeforeAfter.getAssociatedObjectIdType() + associateBeforeAfter.getAssociatedObjectId() + associateBeforeAfter.getExpiryBeginDate() + associateBeforeAfter.getExpiryEndDate() + associateBeforeAfter.getValidDateCron());
|
||||||
|
}
|
||||||
|
associatedStrBeforeUpdate.removeAll(associatedStrAfterUpdate);
|
||||||
|
return !CollectionUtils.isEmpty(associatedStrBeforeUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(rollbackFor={Exception.class})
|
||||||
|
@CloudwalkParamsValidate
|
||||||
|
public CloudwalkResult<Boolean> delete(DelImageStoreParam param, CloudwalkCallContext context) throws ServiceException {
|
||||||
|
this.checkAppRefByImageStoreIdForDelete(param.getId(), context);
|
||||||
|
this.checkExistAndStatus(param.getId());
|
||||||
|
QueryGroupPersonDTO queryDTO = new QueryGroupPersonDTO();
|
||||||
|
queryDTO.setImageStoreId(param.getId());
|
||||||
|
queryDTO.setIsDel(Short.valueOf(DelStatusEnum.NORAML.getCode()));
|
||||||
|
List groupPersonList = this.groupPersonRefMapper.query(queryDTO);
|
||||||
|
this.isImageStoreAssociatedMapper.deleteAllByImageId(param.getId());
|
||||||
|
DelGroupPersonDTO delGroupPersonDTO = new DelGroupPersonDTO();
|
||||||
|
delGroupPersonDTO.setImageStoreId(param.getId());
|
||||||
|
delGroupPersonDTO.setLastUpdateTime(Long.valueOf(System.currentTimeMillis()));
|
||||||
|
delGroupPersonDTO.setLastUpdateUserId(context.getUser().getCaller());
|
||||||
|
this.groupPersonRefMapper.logicDeleteByParam(delGroupPersonDTO);
|
||||||
|
CloudwalkResult delete = this.agImageStoreService.delete((AgImageStoreKeyParam)BeanCopyUtils.copyProperties((Object)param, AgImageStoreKeyParam.class), context);
|
||||||
|
if (!delete.isSuccess()) {
|
||||||
|
throw new ServiceException(delete.getCode(), delete.getMessage());
|
||||||
|
}
|
||||||
|
return CloudwalkResult.<Boolean>success(Boolean.TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkAppRefByImageStoreIdForDelete(String imageStoreId, CloudwalkCallContext context) throws ServiceException {
|
||||||
|
ApplicationImageStoreQueryParam queryParam = new ApplicationImageStoreQueryParam();
|
||||||
|
queryParam.setImageStoreId(imageStoreId);
|
||||||
|
CloudwalkResult queryResult = this.applicationImageStoreService.query(queryParam, context);
|
||||||
|
if (!queryResult.isSuccess()) {
|
||||||
|
throw new ServiceException(queryResult.getCode(), queryResult.getMessage());
|
||||||
|
}
|
||||||
|
if (!CollectionUtils.isEmpty((Collection)((Collection)queryResult.getData()))) {
|
||||||
|
throw new ServiceException("53013545", this.getMessage("53013545"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public CloudwalkResult<CloudwalkPageAble<PageImageStoreResult>> page(QueryImageStoreParam queryImageStoreParam, CloudwalkCallContext context) throws ServiceException {
|
||||||
|
CloudwalkResult agPageResult;
|
||||||
|
boolean isEnd = this.handleQueryParam(queryImageStoreParam);
|
||||||
|
if (isEnd) {
|
||||||
|
return CloudwalkResult.success(
|
||||||
|
new CloudwalkPageAble<PageImageStoreResult>(
|
||||||
|
new ArrayList<PageImageStoreResult>(),
|
||||||
|
new CloudwalkPageInfo(
|
||||||
|
queryImageStoreParam.getCurrentPage(), queryImageStoreParam.getRowsOfPage()),
|
||||||
|
0L));
|
||||||
|
}
|
||||||
|
AgImageStoreQueryParam queryParam = (AgImageStoreQueryParam)BeanCopyUtils.copyProperties((Object)queryImageStoreParam, AgImageStoreQueryParam.class);
|
||||||
|
if (StringUtils.isBlank((CharSequence)queryParam.getBusinessId())) {
|
||||||
|
queryParam.setBusinessId(context.getCompany().getCompanyId());
|
||||||
|
}
|
||||||
|
if (!(agPageResult = this.agImageStoreService.page(queryParam)).isSuccess()) {
|
||||||
|
return CloudwalkResult.fail((String)agPageResult.getCode(), (String)agPageResult.getMessage());
|
||||||
|
}
|
||||||
|
if (agPageResult.getData() == null) {
|
||||||
|
return CloudwalkResult.success(
|
||||||
|
new CloudwalkPageAble<PageImageStoreResult>(
|
||||||
|
new ArrayList<PageImageStoreResult>(),
|
||||||
|
new CloudwalkPageInfo(queryParam.getCurrentPage(), queryParam.getRowsOfPage()),
|
||||||
|
0L));
|
||||||
|
}
|
||||||
|
List<PageImageStoreResult> resultList = this.packageCpResult(((CloudwalkPageAble)agPageResult.getData()).getDatas(), queryParam.getBusinessId());
|
||||||
|
CloudwalkPageAble<?> pageData = (CloudwalkPageAble<?>) agPageResult.getData();
|
||||||
|
return CloudwalkResult.success(
|
||||||
|
new CloudwalkPageAble<PageImageStoreResult>(
|
||||||
|
resultList,
|
||||||
|
new CloudwalkPageInfo((int) pageData.getCurrentPage(), (int) pageData.getPageSize()),
|
||||||
|
pageData.getTotalRows()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public CloudwalkResult<List<ImageStoreResult>> list(QueryImageStoreParam queryImageStoreParam, CloudwalkCallContext cloudwalkContext) throws ServiceException {
|
||||||
|
CloudwalkResult agQueryResult;
|
||||||
|
List<ImageStoreResult> resultList = new ArrayList<ImageStoreResult>();
|
||||||
|
boolean isEnd = this.handleQueryParam(queryImageStoreParam);
|
||||||
|
if (isEnd) {
|
||||||
|
return CloudwalkResult.success(resultList);
|
||||||
|
}
|
||||||
|
AgImageStoreQueryParam queryParam = (AgImageStoreQueryParam)BeanCopyUtils.copyProperties((Object)queryImageStoreParam, AgImageStoreQueryParam.class);
|
||||||
|
if (StringUtils.isBlank((CharSequence)queryParam.getBusinessId())) {
|
||||||
|
queryParam.setBusinessId(cloudwalkContext.getCompany().getCompanyId());
|
||||||
|
}
|
||||||
|
if (!(agQueryResult = this.agImageStoreService.query(queryParam)).isSuccess()) {
|
||||||
|
return CloudwalkResult.fail((String)agQueryResult.getCode(), (String)agQueryResult.getMessage());
|
||||||
|
}
|
||||||
|
if (!CollectionUtils.isEmpty((Collection)queryImageStoreParam.getLabelIds())) {
|
||||||
|
GetsImageStoreAssociatedDTO get = new GetsImageStoreAssociatedDTO();
|
||||||
|
get.setAssociatedObjectIds((Collection)queryImageStoreParam.getLabelIds());
|
||||||
|
get.setAssociatedObjectIdType(Integer.valueOf(2));
|
||||||
|
List<IsImageStoreAssociated> associateds =
|
||||||
|
(List<IsImageStoreAssociated>) (List) this.isImageStoreAssociatedMapper.gets(get);
|
||||||
|
if (CollectionUtils.isEmpty((Collection) associateds)) {
|
||||||
|
return CloudwalkResult.success(resultList);
|
||||||
|
}
|
||||||
|
List<String> imageStoreIds = Collections3.extractToList(associateds, "imageStoreId");
|
||||||
|
List<AgImageStoreResult> agList = (List<AgImageStoreResult>) agQueryResult.getData();
|
||||||
|
List<AgImageStoreResult> collect =
|
||||||
|
agList.stream().filter(a -> imageStoreIds.contains(a.getId())).collect(Collectors.toList());
|
||||||
|
agQueryResult.setData(collect);
|
||||||
|
}
|
||||||
|
resultList =
|
||||||
|
this.packageImageStoreResult(
|
||||||
|
(Collection<AgImageStoreResult>) agQueryResult.getData(), queryParam.getBusinessId());
|
||||||
|
return CloudwalkResult.success(resultList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@CloudwalkParamsValidate
|
||||||
|
public CloudwalkResult<ImageStoreDetailResult> detail(DetailImageStoreParam param, CloudwalkCallContext context) {
|
||||||
|
AgImageStoreQueryParam queryParam = new AgImageStoreQueryParam();
|
||||||
|
queryParam.setId(param.getId());
|
||||||
|
CloudwalkResult queryResult = this.agImageStoreService.query(queryParam);
|
||||||
|
if (!queryResult.isSuccess()) {
|
||||||
|
return CloudwalkResult.fail((String)queryResult.getCode(), (String)queryResult.getMessage());
|
||||||
|
}
|
||||||
|
if (CollectionUtils.isEmpty((Collection)((Collection)queryResult.getData()))) {
|
||||||
|
return CloudwalkResult.fail((String)"53013502", (String)this.getMessage("53013502"));
|
||||||
|
}
|
||||||
|
AgImageStoreResult agImageStoreResult = (AgImageStoreResult)((List)queryResult.getData()).get(0);
|
||||||
|
ImageStoreDetailResult result = (ImageStoreDetailResult)BeanCopyUtils.copyProperties((Object)agImageStoreResult, ImageStoreDetailResult.class);
|
||||||
|
result.setPersonNum(this.getPersonNum(agImageStoreResult.getId()));
|
||||||
|
try {
|
||||||
|
result.setBusinessName(this.getBusinessName(result.getBusinessId()));
|
||||||
|
}
|
||||||
|
catch (ServiceException e) {
|
||||||
|
this.logger.error("\u67e5\u8be2\u4f01\u4e1a\u540d\u79f0\u9519\u8befe:{}", (Object)e.getMessage());
|
||||||
|
return CloudwalkResult.fail((String)e.getCode(), (String)e.getMessage());
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
result.setSourceApplicationName(this.getSourceApplicationName(result.getSourceApplicationId()));
|
||||||
|
}
|
||||||
|
catch (ServiceException e) {
|
||||||
|
this.logger.error("\u67e5\u8be2\u6765\u6e90\u5e94\u7528\u540d\u79f0\u9519\u8befe:{}", (Object)e.getMessage());
|
||||||
|
return CloudwalkResult.fail((String)e.getCode(), (String)e.getMessage());
|
||||||
|
}
|
||||||
|
this.getAssociated(result);
|
||||||
|
DeviceImageStoreQueryParam deviceParam = new DeviceImageStoreQueryParam();
|
||||||
|
deviceParam.setImageStoreId(param.getId());
|
||||||
|
CloudwalkResult deviceResult = this.deviceImageStoreService.query(deviceParam, context);
|
||||||
|
if (deviceResult.isSuccess()) {
|
||||||
|
result.setDevices(this.getDeviceList((List)deviceResult.getData(), context));
|
||||||
|
} else {
|
||||||
|
this.logger.error("\u56fe\u5e93\u8be6\u60c5\u5173\u8054\u8bbe\u5907\u540d\u79f0\u83b7\u53d6\u5931\u8d25\uff0cresult:[{}]", (Object)JSONObject.toJSONString((Object)deviceResult));
|
||||||
|
}
|
||||||
|
ApplicationImageStoreQueryParam appParam = new ApplicationImageStoreQueryParam();
|
||||||
|
appParam.setImageStoreId(param.getId());
|
||||||
|
CloudwalkResult appResult = this.applicationImageStoreService.query(appParam, context);
|
||||||
|
if (appResult.isSuccess()) {
|
||||||
|
result.setApplicationNames(Collections3.extractToList((Collection)((Collection)appResult.getData()), (String)"applicationName"));
|
||||||
|
} else {
|
||||||
|
this.logger.error("\u56fe\u5e93\u8be6\u60c5\u5173\u8054\u5e94\u7528\u540d\u79f0\u83b7\u53d6\u5931\u8d25\uff0cresult:[{}]", (Object)JSONObject.toJSONString((Object)appResult));
|
||||||
|
}
|
||||||
|
return CloudwalkResult.success(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<DeviceInfoResult> getDeviceList(List<DeviceImageStoreQueryResult> resultList, CloudwalkCallContext context) {
|
||||||
|
List<DeviceInfoResult> list = Lists.newArrayListWithCapacity(resultList.size());
|
||||||
|
try {
|
||||||
|
List<String> deviceIds =
|
||||||
|
resultList.stream().map(DeviceImageStoreQueryResult::getDeviceId).collect(Collectors.toList());
|
||||||
|
CoreDeviceQueryParam param = new CoreDeviceQueryParam();
|
||||||
|
param.setIds(deviceIds);
|
||||||
|
param.setBusinessId(context.getCompany().getCompanyId());
|
||||||
|
CloudwalkResult deviceResult = this.atomicDeviceService.list(param, context);
|
||||||
|
Map<String, String> deviceMap = new HashMap<>();
|
||||||
|
if (deviceResult.isSuccess()) {
|
||||||
|
deviceMap =
|
||||||
|
(Map<String, String>)
|
||||||
|
((List<AtomicDeviceGetResult>) (List) deviceResult.getData())
|
||||||
|
.stream()
|
||||||
|
.collect(
|
||||||
|
Collectors.toMap(
|
||||||
|
device -> device.getId(),
|
||||||
|
device -> device.getDeviceCode()));
|
||||||
|
}
|
||||||
|
for (DeviceImageStoreQueryResult result : resultList) {
|
||||||
|
DeviceInfoResult dr = new DeviceInfoResult();
|
||||||
|
dr.setDeviceId(result.getDeviceId());
|
||||||
|
dr.setDeviceCode(deviceMap.get(result.getDeviceId()));
|
||||||
|
dr.setDeviceName(result.getDeviceName());
|
||||||
|
dr.setIdentifyType(result.getIdentifyType());
|
||||||
|
list.add(dr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
this.logger.error("exception:{}", e.getMessage());
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IsImageStoreAssociated getAssociated(String objectId, long time, String userId, String imageStoreId, Integer action, Integer objectType) {
|
||||||
|
IsImageStoreAssociated associated = new IsImageStoreAssociated();
|
||||||
|
associated.setId(this.getPrimaryId());
|
||||||
|
associated.setAssociatedAction(action);
|
||||||
|
associated.setAssociatedObjectId(objectId);
|
||||||
|
associated.setAssociatedObjectIdType(objectType);
|
||||||
|
associated.setCreateTime(Long.valueOf(time));
|
||||||
|
associated.setCreateUserId(userId);
|
||||||
|
associated.setImageStoreId(imageStoreId);
|
||||||
|
associated.setLastUpdateTime(Long.valueOf(time));
|
||||||
|
associated.setLastUpdateUserId(userId);
|
||||||
|
return associated;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IsImageStoreAssociated getAssociated(AssociatedParam associatedParam, long time, String userId, String imageStoreId, Integer action, Integer objectType) {
|
||||||
|
IsImageStoreAssociated associated = this.getAssociated(associatedParam.getObjectId(), time, userId, imageStoreId, action, objectType);
|
||||||
|
associated.setExpiryBeginDate(associatedParam.getExpiryBeginDate());
|
||||||
|
associated.setExpiryEndDate(associatedParam.getExpiryEndDate());
|
||||||
|
associated.setValidDateCron(null);
|
||||||
|
if (!CollectionUtils.isEmpty((Collection)associatedParam.getValidDateCron())) {
|
||||||
|
associated.setValidDateCron(JSON.toJSONString((Object)associatedParam.getValidDateCron()));
|
||||||
|
}
|
||||||
|
return associated;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<IsImageStoreAssociated> addBaseImageStore(BaseImageStoreParam param, CloudwalkCallContext context, String imageStoreId, String businessId) throws ServiceException {
|
||||||
|
long time = System.currentTimeMillis();
|
||||||
|
List<String> includePersonIds =
|
||||||
|
CollectionUtils.isEmpty((Collection) param.getIncludePersons())
|
||||||
|
? new ArrayList<String>()
|
||||||
|
: param.getIncludePersons().stream()
|
||||||
|
.map(AssociatedParam::getObjectId)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
if (CollectionUtils.isEmpty((Collection)param.getIncludeOrganizations()) && CollectionUtils.isEmpty((Collection)param.getIncludeLabels()) && CollectionUtils.isEmpty(includePersonIds)) {
|
||||||
|
return new ArrayList<IsImageStoreAssociated>();
|
||||||
|
}
|
||||||
|
ArrayList<IsImageStoreAssociated> imageStoreAssociatedList = new ArrayList<IsImageStoreAssociated>();
|
||||||
|
if (CpImageStoreMatchPatternEnum.getByCode((String)param.getMatchPattern()) != null) {
|
||||||
|
imageStoreAssociatedList.add(this.getAssociated(param.getMatchPattern(), time, context.getUser().getCaller(), imageStoreId, null, (Integer)4));
|
||||||
|
} else {
|
||||||
|
imageStoreAssociatedList.add(this.getAssociated(CpImageStoreMatchPatternEnum.MERGE.getCode(), time, context.getUser().getCaller(), imageStoreId, null, (Integer)4));
|
||||||
|
}
|
||||||
|
AssociatedParam imageStoreAssociatedParam = new AssociatedParam();
|
||||||
|
imageStoreAssociatedParam.setObjectId(imageStoreId);
|
||||||
|
imageStoreAssociatedParam.setExpiryBeginDate(param.getExpiryBeginDate());
|
||||||
|
imageStoreAssociatedParam.setExpiryEndDate(param.getExpiryEndDate());
|
||||||
|
imageStoreAssociatedParam.setValidDateCron(param.getValidDateCron());
|
||||||
|
imageStoreAssociatedList.add(this.getAssociated(imageStoreAssociatedParam, time, context.getUser().getCaller(), imageStoreId, (Integer)0, (Integer)5));
|
||||||
|
if (!CollectionUtils.isEmpty((Collection)param.getIncludeOrganizations())) {
|
||||||
|
if (this.imgStoreOrganizationMapper.getOrgByIds(param.getIncludeOrganizations(), businessId).size() != param.getIncludeOrganizations().size()) {
|
||||||
|
throw new ServiceException("53013513", this.getMessage("53013513"));
|
||||||
|
}
|
||||||
|
for (Object objectId : param.getIncludeOrganizations()) {
|
||||||
|
imageStoreAssociatedList.add(this.getAssociated((String)objectId, time, context.getUser().getCaller(), imageStoreId, (Integer)0, (Integer)1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!CollectionUtils.isEmpty((Collection)param.getIncludeLabels())) {
|
||||||
|
if (this.imgStoreLabelMapper.getLabelByIds(param.getIncludeLabels(), businessId).size() != param.getIncludeLabels().size()) {
|
||||||
|
throw new ServiceException("53013514", this.getMessage("53013514"));
|
||||||
|
}
|
||||||
|
for (Object objectId : param.getIncludeLabels()) {
|
||||||
|
imageStoreAssociatedList.add(this.getAssociated((String)objectId, time, context.getUser().getCaller(), imageStoreId, (Integer)0, (Integer)2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!CollectionUtils.isEmpty(includePersonIds)) {
|
||||||
|
ImgStorePersonQueryDto personQuery = new ImgStorePersonQueryDto();
|
||||||
|
personQuery.setIds(includePersonIds);
|
||||||
|
personQuery.setBusinessId(businessId);
|
||||||
|
personQuery.setIsDel(DelStatusEnum.NORAML.getCode());
|
||||||
|
if (this.personMapper.gets(personQuery).size() != includePersonIds.size()) {
|
||||||
|
throw new ServiceException("53013515", this.getMessage("53013515"));
|
||||||
|
}
|
||||||
|
for (AssociatedParam associatedParam : param.getIncludePersons()) {
|
||||||
|
if (!(param.getNullDateIsLongTerm().booleanValue() || null != associatedParam.getExpiryBeginDate() && null != associatedParam.getExpiryEndDate())) {
|
||||||
|
associatedParam.setExpiryBeginDate(imageStoreAssociatedParam.getExpiryBeginDate());
|
||||||
|
associatedParam.setExpiryEndDate(imageStoreAssociatedParam.getExpiryEndDate());
|
||||||
|
associatedParam.setValidDateCron(imageStoreAssociatedParam.getValidDateCron());
|
||||||
|
}
|
||||||
|
imageStoreAssociatedList.add(this.getAssociated(associatedParam, time, context.getUser().getCaller(), imageStoreId, (Integer)0, (Integer)3));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!CollectionUtils.isEmpty((Collection)param.getExcludeLabels())) {
|
||||||
|
if (this.imgStoreLabelMapper.getLabelByIds(param.getExcludeLabels(), businessId).size() != param.getExcludeLabels().size()) {
|
||||||
|
throw new ServiceException("53013516", this.getMessage("53013516"));
|
||||||
|
}
|
||||||
|
for (Object objectId : param.getExcludeLabels()) {
|
||||||
|
imageStoreAssociatedList.add(this.getAssociated((String)objectId, time, context.getUser().getCaller(), imageStoreId, (Integer)1, (Integer)2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!CollectionUtils.isEmpty((Collection) param.getExcludePersons())) {
|
||||||
|
ImgStorePersonQueryDto excludePersonQuery = new ImgStorePersonQueryDto();
|
||||||
|
excludePersonQuery.setIds((Collection) param.getExcludePersons());
|
||||||
|
excludePersonQuery.setBusinessId(businessId);
|
||||||
|
excludePersonQuery.setIsDel(DelStatusEnum.NORAML.getCode());
|
||||||
|
if (this.personMapper.gets(excludePersonQuery).size() != param.getExcludePersons().size()) {
|
||||||
|
throw new ServiceException("53013517", this.getMessage("53013517"));
|
||||||
|
}
|
||||||
|
for (String objectId : param.getExcludePersons()) {
|
||||||
|
imageStoreAssociatedList.add(this.getAssociated(objectId, time, context.getUser().getCaller(), imageStoreId, (Integer)1, (Integer)3));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return imageStoreAssociatedList;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getBusinessName(String businessId) throws ServiceException {
|
||||||
|
GeneralQueryBusinessParam generalQueryBusinessParam = new GeneralQueryBusinessParam();
|
||||||
|
generalQueryBusinessParam.setId(businessId);
|
||||||
|
CloudwalkResult cloudwalkResult = this.acBusinessService.generalQuery(generalQueryBusinessParam);
|
||||||
|
if (cloudwalkResult == null) {
|
||||||
|
throw new ServiceException("53013535", this.getMessage("53013535"));
|
||||||
|
}
|
||||||
|
if (!cloudwalkResult.isSuccess()) {
|
||||||
|
throw new ServiceException(cloudwalkResult.getCode(), cloudwalkResult.getMessage());
|
||||||
|
}
|
||||||
|
if (cloudwalkResult.getData() != null && ((List)cloudwalkResult.getData()).size() > 0) {
|
||||||
|
return ((AcBusinessDTO)((List)cloudwalkResult.getData()).get(0)).getName();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getSourceApplicationName(String sourceApplicationId) throws ServiceException {
|
||||||
|
if (StringUtils.isEmpty((CharSequence)sourceApplicationId)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
ApplicationBasicParam applicationBasicParam = new ApplicationBasicParam();
|
||||||
|
applicationBasicParam.setIds(Arrays.asList(sourceApplicationId));
|
||||||
|
CloudwalkResult cloudwalkResult = this.applicationService.gets(applicationBasicParam);
|
||||||
|
if (cloudwalkResult == null) {
|
||||||
|
throw new ServiceException("53013547", this.getMessage("53013547"));
|
||||||
|
}
|
||||||
|
if (!cloudwalkResult.isSuccess()) {
|
||||||
|
throw new ServiceException(cloudwalkResult.getCode(), cloudwalkResult.getMessage());
|
||||||
|
}
|
||||||
|
if (cloudwalkResult.getData() != null && ((List)cloudwalkResult.getData()).size() > 0) {
|
||||||
|
return ((ApplicationResult)((List)cloudwalkResult.getData()).get(0)).getName();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkExistAndStatus(String iamgeStoreId) throws ServiceException {
|
||||||
|
AgImageStoreQueryParam queryParam = new AgImageStoreQueryParam();
|
||||||
|
queryParam.setId(iamgeStoreId);
|
||||||
|
CloudwalkResult query = this.agImageStoreService.query(queryParam);
|
||||||
|
if (!query.isSuccess()) {
|
||||||
|
throw new ServiceException(query.getCode(), query.getMessage());
|
||||||
|
}
|
||||||
|
if (CollectionUtils.isEmpty((Collection)((Collection)query.getData()))) {
|
||||||
|
throw new ServiceException("53013502", this.getMessage("53013502"));
|
||||||
|
}
|
||||||
|
if (SyncStatusEnum.SYNC_ING.getCode().equals(((AgImageStoreResult)((List)query.getData()).get(0)).getStatus())) {
|
||||||
|
throw new ServiceException("53013512", this.getMessage("53013512"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, String> getBusinessNameMap() throws ServiceException {
|
||||||
|
CloudwalkResult listCloudwalkResult = this.acBusinessService.generalQuery(new GeneralQueryBusinessParam());
|
||||||
|
if (!listCloudwalkResult.isSuccess()) {
|
||||||
|
throw new ServiceException(listCloudwalkResult.getCode(), listCloudwalkResult.getMessage());
|
||||||
|
}
|
||||||
|
if (CollectionUtils.isEmpty((Collection)((Collection)listCloudwalkResult.getData()))) {
|
||||||
|
return new HashMap<String, String>();
|
||||||
|
}
|
||||||
|
return Collections3.extractToMap((Collection)((Collection)listCloudwalkResult.getData()), (String)"id", (String)"name");
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, String> getSourceApplicationNameMap(String businessId) throws ServiceException {
|
||||||
|
ApplicationQueryParam param = new ApplicationQueryParam();
|
||||||
|
param.setBusinessId(businessId);
|
||||||
|
param.setStatus(CommonStatusEnum.ENABLE.getCode());
|
||||||
|
CloudwalkResult listCloudwalkResult = this.applicationService.query(param);
|
||||||
|
if (!listCloudwalkResult.isSuccess()) {
|
||||||
|
throw new ServiceException(listCloudwalkResult.getCode(), listCloudwalkResult.getMessage());
|
||||||
|
}
|
||||||
|
if (CollectionUtils.isEmpty((Collection)((Collection)listCloudwalkResult.getData()))) {
|
||||||
|
return new HashMap<String, String>();
|
||||||
|
}
|
||||||
|
return Collections3.extractToMap((Collection)((Collection)listCloudwalkResult.getData()), (String)"id", (String)"name");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void getAssociated(ImageStoreDetailResult result) {
|
||||||
|
GetsImageStoreAssociatedDTO get = new GetsImageStoreAssociatedDTO();
|
||||||
|
get.setImageStoreId(result.getId());
|
||||||
|
List<IsImageStoreAssociated> associateds =
|
||||||
|
(List<IsImageStoreAssociated>) (List) this.isImageStoreAssociatedMapper.gets(get);
|
||||||
|
String matchPattern = null;
|
||||||
|
List<String> includeLabelIds = new ArrayList<String>();
|
||||||
|
List<String> includeOrganizationIds = new ArrayList<String>();
|
||||||
|
List<String> includePersonIds = new ArrayList<String>();
|
||||||
|
Map<String, AssociatedResult> includePersonMap = new HashMap<String, AssociatedResult>();
|
||||||
|
List<String> excludeLabelIds = new ArrayList<String>();
|
||||||
|
List<String> excludePersonIds = new ArrayList<String>();
|
||||||
|
String businessId = result.getBusinessId();
|
||||||
|
for (IsImageStoreAssociated associated : associateds) {
|
||||||
|
if (4 == associated.getAssociatedObjectIdType()) {
|
||||||
|
matchPattern = associated.getAssociatedObjectId();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (0 == associated.getAssociatedAction()) {
|
||||||
|
if (1 == associated.getAssociatedObjectIdType()) {
|
||||||
|
includeOrganizationIds.add(associated.getAssociatedObjectId());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (2 == associated.getAssociatedObjectIdType()) {
|
||||||
|
includeLabelIds.add(associated.getAssociatedObjectId());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (3 == associated.getAssociatedObjectIdType()) {
|
||||||
|
includePersonIds.add(associated.getAssociatedObjectId());
|
||||||
|
AssociatedResult associatedResult = new AssociatedResult();
|
||||||
|
BeanCopyUtils.copyProperties((Object)associated, (Object)associatedResult);
|
||||||
|
associatedResult.setObjectId(associated.getAssociatedObjectId());
|
||||||
|
includePersonMap.put(associated.getAssociatedObjectId(), associatedResult);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (5 != associated.getAssociatedObjectIdType()) continue;
|
||||||
|
result.setExpiryBeginDate(associated.getExpiryBeginDate());
|
||||||
|
result.setExpiryEndDate(associated.getExpiryEndDate());
|
||||||
|
result.setValidDateCron(JsonUtils.toStrList((String)associated.getValidDateCron()));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (1 != associated.getAssociatedAction()) continue;
|
||||||
|
if (2 == associated.getAssociatedObjectIdType()) {
|
||||||
|
excludeLabelIds.add(associated.getAssociatedObjectId());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (3 != associated.getAssociatedObjectIdType()) continue;
|
||||||
|
excludePersonIds.add(associated.getAssociatedObjectId());
|
||||||
|
}
|
||||||
|
result.setMatchPattern(matchPattern);
|
||||||
|
if (!CollectionUtils.isEmpty(includeLabelIds)) {
|
||||||
|
result.setIncludeLabels(BeanCopyUtils.copy((Collection)this.imgStoreLabelMapper.getLabelByIds(includeLabelIds, businessId), LabelResult.class));
|
||||||
|
} else {
|
||||||
|
result.setIncludeLabels(new ArrayList());
|
||||||
|
}
|
||||||
|
if (!CollectionUtils.isEmpty(includeOrganizationIds)) {
|
||||||
|
result.setIncludeOrganizations(BeanCopyUtils.copy((Collection)this.imgStoreOrganizationMapper.getOrgByIds(includeOrganizationIds, businessId), OrganizationResult.class));
|
||||||
|
} else {
|
||||||
|
result.setIncludeOrganizations(new ArrayList());
|
||||||
|
}
|
||||||
|
ImgStorePersonQueryDto getDTO = new ImgStorePersonQueryDto();
|
||||||
|
if (!CollectionUtils.isEmpty(includePersonIds)) {
|
||||||
|
getDTO.setIds(includePersonIds);
|
||||||
|
getDTO.setIsDel(DelStatusEnum.NORAML.getCode());
|
||||||
|
List<ImgStorePerson> personList = this.personMapper.gets(getDTO);
|
||||||
|
List<ImgStorePersonResult> includePersons = new ArrayList<ImgStorePersonResult>();
|
||||||
|
personList.forEach(imgStorePerson -> {
|
||||||
|
ImgStorePersonResult imgStorePersonResult = new ImgStorePersonResult();
|
||||||
|
BeanCopyUtils.copyProperties((Object)imgStorePerson, (Object)imgStorePersonResult);
|
||||||
|
AssociatedResult associatedResult = (AssociatedResult)includePersonMap.get(imgStorePerson.getId());
|
||||||
|
BeanCopyUtils.copyProperties((Object)associatedResult, (Object)imgStorePersonResult);
|
||||||
|
imgStorePersonResult.setValidDateCron(JsonUtils.toStrList((String)associatedResult.getValidDateCron()));
|
||||||
|
includePersons.add(imgStorePersonResult);
|
||||||
|
});
|
||||||
|
result.setIncludePersons(includePersons);
|
||||||
|
} else {
|
||||||
|
result.setIncludePersons(new ArrayList());
|
||||||
|
}
|
||||||
|
if (!CollectionUtils.isEmpty(excludeLabelIds)) {
|
||||||
|
result.setExcludeLabels(BeanCopyUtils.copy((Collection)this.imgStoreLabelMapper.getLabelByIds(excludeLabelIds, businessId), LabelResult.class));
|
||||||
|
} else {
|
||||||
|
result.setExcludeLabels(new ArrayList());
|
||||||
|
}
|
||||||
|
if (!CollectionUtils.isEmpty(excludePersonIds)) {
|
||||||
|
getDTO.setIds(excludePersonIds);
|
||||||
|
getDTO.setIsDel(DelStatusEnum.NORAML.getCode());
|
||||||
|
result.setExcludePersons(BeanCopyUtils.copy((Collection)this.personMapper.gets(getDTO), ImgStorePersonResult.class));
|
||||||
|
} else {
|
||||||
|
result.setExcludePersons(new ArrayList());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean handleQueryParam(QueryImageStoreParam queryParam) {
|
||||||
|
List imageStoreIds;
|
||||||
|
if (StringUtils.isNotBlank((CharSequence)queryParam.getOrgId()) || !CollectionUtils.isEmpty((Collection)queryParam.getOrgIds())) {
|
||||||
|
List orgImageStoreList = this.organizationImageStoreMapper.query((OrganizationImageStoreQueryDTO)BeanCopyUtils.copyProperties((Object)queryParam, OrganizationImageStoreQueryDTO.class));
|
||||||
|
GetsImageStoreAssociatedDTO orgParam = new GetsImageStoreAssociatedDTO();
|
||||||
|
orgParam.setAssociatedObjectIdType(Integer.valueOf(1));
|
||||||
|
ArrayList<String> orgIds = new ArrayList<String>(500);
|
||||||
|
if (StringUtils.isNotBlank((CharSequence)queryParam.getOrgId())) {
|
||||||
|
orgIds.add(queryParam.getOrgId());
|
||||||
|
}
|
||||||
|
if (!CollectionUtils.isEmpty((Collection)queryParam.getOrgIds())) {
|
||||||
|
orgIds.addAll(queryParam.getOrgIds());
|
||||||
|
}
|
||||||
|
orgParam.setAssociatedObjectIds(orgIds);
|
||||||
|
List orgResultList = this.isImageStoreAssociatedMapper.gets(orgParam);
|
||||||
|
if (CollectionUtils.isEmpty((Collection)orgImageStoreList) && CollectionUtils.isEmpty((Collection)orgResultList)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
List imageStoreIds2 = Collections3.extractToList((Collection)orgImageStoreList, (String)"imageStoreId");
|
||||||
|
imageStoreIds2.addAll(Collections3.extractToList((Collection)orgResultList, (String)"imageStoreId"));
|
||||||
|
if (StringUtils.isNotBlank((CharSequence)queryParam.getId())) {
|
||||||
|
if (!imageStoreIds2.contains(queryParam.getId())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (!CollectionUtils.isEmpty((Collection)queryParam.getIds())) {
|
||||||
|
imageStoreIds2.retainAll(queryParam.getIds());
|
||||||
|
}
|
||||||
|
if (CollectionUtils.isEmpty((Collection)imageStoreIds2)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
queryParam.setIds(imageStoreIds2);
|
||||||
|
}
|
||||||
|
if (!CollectionUtils.isEmpty((Collection)queryParam.getPersonIds())) {
|
||||||
|
QueryGroupPersonDTO queryGroupPersonDTO = (QueryGroupPersonDTO)BeanCopyUtils.copyProperties((Object)queryParam, QueryGroupPersonDTO.class);
|
||||||
|
queryGroupPersonDTO.setIsDel(DelStatusEnum.NORAML.getCode());
|
||||||
|
List queryResult = this.groupPersonRefMapper.query(queryGroupPersonDTO);
|
||||||
|
if (CollectionUtils.isEmpty((Collection)queryResult)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
imageStoreIds = Collections3.extractToList((Collection)queryResult, (String)"imageStoreId");
|
||||||
|
if (StringUtils.isNotBlank((CharSequence)queryParam.getId())) {
|
||||||
|
if (!imageStoreIds.contains(queryParam.getId())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (!CollectionUtils.isEmpty((Collection)queryParam.getIds())) {
|
||||||
|
imageStoreIds.retainAll(queryParam.getIds());
|
||||||
|
}
|
||||||
|
if (CollectionUtils.isEmpty((Collection)imageStoreIds)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
queryParam.setIds(imageStoreIds);
|
||||||
|
}
|
||||||
|
if (!CollectionUtils.isEmpty((Collection)queryParam.getLabelIds())) {
|
||||||
|
GetsImageStoreAssociatedDTO labelParam = new GetsImageStoreAssociatedDTO();
|
||||||
|
labelParam.setAssociatedObjectIdType(Integer.valueOf(2));
|
||||||
|
labelParam.setAssociatedObjectIds((Collection)queryParam.getLabelIds());
|
||||||
|
List labelResultList = this.isImageStoreAssociatedMapper.gets(labelParam);
|
||||||
|
if (CollectionUtils.isEmpty((Collection)labelResultList)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
imageStoreIds = Collections3.extractToList((Collection)labelResultList, (String)"imageStoreId");
|
||||||
|
if (StringUtils.isNotBlank((CharSequence)queryParam.getId())) {
|
||||||
|
if (!imageStoreIds.contains(queryParam.getId())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (!CollectionUtils.isEmpty((Collection)queryParam.getIds())) {
|
||||||
|
imageStoreIds.retainAll(queryParam.getIds());
|
||||||
|
}
|
||||||
|
if (CollectionUtils.isEmpty((Collection)imageStoreIds)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
queryParam.setIds(imageStoreIds);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ImageStoreResult> packageImageStoreResult(Collection<AgImageStoreResult> agDatas, String businessId) throws ServiceException {
|
||||||
|
if (CollectionUtils.isEmpty(agDatas)) {
|
||||||
|
return new ArrayList<ImageStoreResult>();
|
||||||
|
}
|
||||||
|
List<String> imageIds = agDatas.stream().map(AgImageStoreResult::getId).collect(Collectors.toList());
|
||||||
|
long startTime = System.currentTimeMillis();
|
||||||
|
List<ImageStoreCountDTO> countByImageStoreIds =
|
||||||
|
new ArrayList<ImageStoreCountDTO>(imageIds.size());
|
||||||
|
List<List<String>> partition = Lists.partition(imageIds, this.searchSize);
|
||||||
|
partition.stream()
|
||||||
|
.forEach(
|
||||||
|
p -> {
|
||||||
|
List<ImageStoreCountDTO> list =
|
||||||
|
this.groupPersonRefMapper.getCountByImageStoreIds(p);
|
||||||
|
if (!CollectionUtils.isEmpty(list)) {
|
||||||
|
countByImageStoreIds.addAll(list);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
long endTime = System.currentTimeMillis();
|
||||||
|
this.logger.info("\u56fe\u5e93\u5206\u9875\u67e5\u8be2\u8017\u65f6:[{}],searchSize:[{}]", (Object)(endTime - startTime), (Object)this.searchSize);
|
||||||
|
Map<String, Integer> countMap =
|
||||||
|
countByImageStoreIds.stream()
|
||||||
|
.filter(imageStoreCountDTO -> null != imageStoreCountDTO)
|
||||||
|
.collect(
|
||||||
|
Collectors.toMap(
|
||||||
|
ImageStoreCountDTO::getId,
|
||||||
|
ImageStoreCountDTO::getCount,
|
||||||
|
(k1, k2) -> k1));
|
||||||
|
Map<String, String> businessNameMap = this.getBusinessNameMap();
|
||||||
|
Map<String, String> sourceApplicationNameMap = this.getSourceApplicationNameMap(businessId);
|
||||||
|
List<ImageStoreResult> resultList = new ArrayList<ImageStoreResult>(agDatas.size());
|
||||||
|
for (AgImageStoreResult agData : agDatas) {
|
||||||
|
ImageStoreResult temp = (ImageStoreResult)BeanCopyUtils.copyProperties((Object)agData, ImageStoreResult.class);
|
||||||
|
temp.setBusinessName(businessNameMap.get(temp.getBusinessId()));
|
||||||
|
temp.setSourceApplicationName(sourceApplicationNameMap.get(temp.getSourceApplicationId()));
|
||||||
|
temp.setPersonNum(countMap.containsKey(agData.getId()) ? countMap.get(agData.getId()) : Integer.valueOf(0));
|
||||||
|
this.getAssociated((ImageStoreDetailResult)temp);
|
||||||
|
resultList.add(temp);
|
||||||
|
}
|
||||||
|
return resultList;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<PageImageStoreResult> packageCpResult(Collection<AgImageStoreResult> agDatas, String businessId) throws ServiceException {
|
||||||
|
if (CollectionUtils.isEmpty(agDatas)) {
|
||||||
|
return new ArrayList<PageImageStoreResult>();
|
||||||
|
}
|
||||||
|
List<String> imageIds = agDatas.stream().map(AgImageStoreResult::getId).collect(Collectors.toList());
|
||||||
|
long startTime = System.currentTimeMillis();
|
||||||
|
List<ImageStoreCountDTO> countByImageStoreIds =
|
||||||
|
new ArrayList<ImageStoreCountDTO>(imageIds.size());
|
||||||
|
List<List<String>> partition = Lists.partition(imageIds, this.searchSize);
|
||||||
|
partition.stream()
|
||||||
|
.forEach(
|
||||||
|
p -> {
|
||||||
|
List<ImageStoreCountDTO> list =
|
||||||
|
this.groupPersonRefMapper.getCountByImageStoreIds(p);
|
||||||
|
if (!CollectionUtils.isEmpty(list)) {
|
||||||
|
countByImageStoreIds.addAll(list);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
long endTime = System.currentTimeMillis();
|
||||||
|
this.logger.info("\u56fe\u5e93\u5206\u9875\u67e5\u8be2\u8017\u65f6:[{}],searchSize:[{}]", (Object)(endTime - startTime), (Object)this.searchSize);
|
||||||
|
Map<String, Integer> countMap =
|
||||||
|
countByImageStoreIds.stream()
|
||||||
|
.filter(imageStoreCountDTO -> null != imageStoreCountDTO)
|
||||||
|
.collect(
|
||||||
|
Collectors.toMap(
|
||||||
|
ImageStoreCountDTO::getId,
|
||||||
|
ImageStoreCountDTO::getCount,
|
||||||
|
(k1, k2) -> k1));
|
||||||
|
Map<String, String> businessNameMap = this.getBusinessNameMap();
|
||||||
|
Map<String, String> sourceApplicationNameMap = this.getSourceApplicationNameMap(businessId);
|
||||||
|
List<PageImageStoreResult> resultList = new ArrayList<PageImageStoreResult>(agDatas.size());
|
||||||
|
for (AgImageStoreResult agData : agDatas) {
|
||||||
|
PageImageStoreResult temp = (PageImageStoreResult)BeanCopyUtils.copyProperties((Object)agData, PageImageStoreResult.class);
|
||||||
|
temp.setBusinessName(businessNameMap.get(temp.getBusinessId()));
|
||||||
|
temp.setSourceApplicationName(sourceApplicationNameMap.get(temp.getSourceApplicationId()));
|
||||||
|
temp.setPersonNum(countMap.containsKey(agData.getId()) ? countMap.get(agData.getId()) : Integer.valueOf(0));
|
||||||
|
resultList.add(temp);
|
||||||
|
}
|
||||||
|
return resultList;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Integer getPersonNum(String id) {
|
||||||
|
List countByImageStoreIds = this.groupPersonRefMapper.getCountByImageStoreIds((List)Lists.newArrayList((Object[])new String[]{id}));
|
||||||
|
if (CollectionUtils.isEmpty((Collection)countByImageStoreIds) || countByImageStoreIds.get(0) == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return ((ImageStoreCountDTO)countByImageStoreIds.get(0)).getCount();
|
||||||
|
}
|
||||||
|
}
|
||||||
+176
@@ -0,0 +1,176 @@
|
|||||||
|
# =============================================================================
|
||||||
|
# 组织组件本地/测试启动覆盖配置
|
||||||
|
# 加载顺序:classpath(jar 内公共 application.properties)→ 本目录(见 run-verify.sh,仅 application 名,不含 application-node)
|
||||||
|
# 现场部署参考:部署包/.../application-node.properties + start.sh
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
# --- 本地/测试强制覆盖 ---
|
||||||
|
spring.application.name=ninca-common-component-organization
|
||||||
|
server.port=17026
|
||||||
|
server.instance-id=component-organization-verify-local
|
||||||
|
|
||||||
|
spring.datasource.username=root
|
||||||
|
spring.datasource.password=123456
|
||||||
|
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
|
||||||
|
spring.datasource.url=jdbc:mysql://192.168.3.12:3307/component-organization?useSSL=false&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
|
||||||
|
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
|
||||||
|
|
||||||
|
eureka.client.enabled=false
|
||||||
|
spring.cloud.consul.enabled=false
|
||||||
|
spring.cloud.consul.discovery.enabled=false
|
||||||
|
|
||||||
|
spring.redis.host=127.0.0.1
|
||||||
|
spring.redis.port=6380
|
||||||
|
spring.redis.password=1qaz!QAZ
|
||||||
|
spring.redis.database=6
|
||||||
|
spring.redis.timeout=5000
|
||||||
|
spring.redis.pool.max-wait=-1
|
||||||
|
spring.redis.pool.max-active=200
|
||||||
|
spring.redis.pool.min-idle=0
|
||||||
|
spring.redis.pool.max-idle=8
|
||||||
|
|
||||||
|
logging.path=/tmp/ninca-component-org-verify-logs
|
||||||
|
logging.level.root=INFO
|
||||||
|
|
||||||
|
# 日志实现由 run-verify.sh 传入 --logging.config=file:…/recognition-logback.xml(勿在此处写 classpath:recognition-logback.xml,以免覆盖失效)
|
||||||
|
|
||||||
|
# --- Spring 通用(与部署包一致)---
|
||||||
|
spring.jackson.time-zone=GMT+8
|
||||||
|
spring.http.encoding.charset=UTF-8
|
||||||
|
spring.http.encoding.enabled=true
|
||||||
|
spring.http.encoding.force=true
|
||||||
|
|
||||||
|
# --- i18n(BOOT-INF 同名)---
|
||||||
|
spring.messages.basename=messages_zh_CN,component-account/messages_zh_CN,component-resource/messages_zh_CN,core-aggregate/messages_zh_CN,core-device/messages_zh_CN,devicesdk/messages_zh_CN,component-org/messages_zh_CN
|
||||||
|
|
||||||
|
# --- 上传限制 ---
|
||||||
|
cloudwalk.multipart.maxFileSize=50MB
|
||||||
|
cloudwalk.multipart.maxRequestSize=50MB
|
||||||
|
|
||||||
|
# --- Quartz(占位符引用当前数据源)---
|
||||||
|
quartz.driver=${spring.datasource.driver-class-name}
|
||||||
|
quartz.url=${spring.datasource.url}
|
||||||
|
quartz.user=${spring.datasource.username}
|
||||||
|
quartz.password=${spring.datasource.password}
|
||||||
|
quartz.maxConnections=20
|
||||||
|
quartz.schedulerName=${spring.application.name}
|
||||||
|
quartz.clustered=true
|
||||||
|
quartz.strategy=local
|
||||||
|
quartz.lazyStart=120
|
||||||
|
quartz.pool-type=druid
|
||||||
|
|
||||||
|
# --- 注册照质量分 ---
|
||||||
|
imageQualityScore=0.65
|
||||||
|
|
||||||
|
# --- 本机验证路径(替代部署包 /data/cwos/…)---
|
||||||
|
cloudwalk.common-app.download.downDir=/tmp/ninca-component-org-verify/download
|
||||||
|
cloudwalk.component.file.urlPrefix=http://127.0.0.1:${server.port}/ninca-common-component-organization/file/imgByPath?path=
|
||||||
|
qr.code.url=http://127.0.0.1:${server.port}/general-person-h5/#/register?businessId=
|
||||||
|
|
||||||
|
# --- Feign 服务名(与部署包一致;上游需注册或可改 ribbon list)---
|
||||||
|
feign.davinci-portal.name=cwos-portal
|
||||||
|
feign.resource.name=cwos-portal
|
||||||
|
feign.portal.name=cwos-portal
|
||||||
|
feign.device.name=cwos-portal
|
||||||
|
feign.pineapple.name=cloudwalk-pineapple-manager
|
||||||
|
|
||||||
|
# --- 定时任务 cron ---
|
||||||
|
cloudwalk.imageStore.person.cron=0 */2 * * * ?
|
||||||
|
cloudwalk.imageStore.imageSync.cron=0 */2 * * * ?
|
||||||
|
|
||||||
|
# --- 导出限制(部署包数值)---
|
||||||
|
cloudwalk.common-app.download.excelMaxRows=10000
|
||||||
|
cloudwalk.common-app.download.exportAllCount=100000
|
||||||
|
cloudwalk.common-app.download.shardingSize=31457280
|
||||||
|
cloudwalk.common-app.download.compressionType=.zip
|
||||||
|
|
||||||
|
# --- Kafka(测试环境:未起 Kafka 时可改为本机或注释依赖;此处保留部署包结构便于对齐)---
|
||||||
|
cloudwalk.component-organization.kafka.service-code=imgstoreApp
|
||||||
|
cloudwalk.component-organization.kafka.producer.groupId=component-organization
|
||||||
|
cloudwalk.component-organization.kafka.consumer.groupId=component-organization
|
||||||
|
# 覆盖 BOOT-INF 内网地址:可按 env.sh / Docker 修改
|
||||||
|
cloudwalk.component-organization.kafka.producer.bootstrapServers=127.0.0.1:9092
|
||||||
|
cloudwalk.component-organization.kafka.consumer.bootstrapServers=127.0.0.1:9092
|
||||||
|
|
||||||
|
person.name.space=componentOrg
|
||||||
|
|
||||||
|
# --- Management(与部署包规律:业务端口 +100)---
|
||||||
|
management.port=17126
|
||||||
|
management.context-path=/actuator
|
||||||
|
management.security.enabled=false
|
||||||
|
endpoints.enabled=false
|
||||||
|
endpoints.info.enabled=true
|
||||||
|
endpoints.health.enabled=true
|
||||||
|
endpoints.prometheus.enabled=true
|
||||||
|
|
||||||
|
# --- 图库同步(线程池取部署包「根目录」application.properties 较大规格,便于压测对齐)---
|
||||||
|
group-person.syn.config.task_is_all.threshold=100
|
||||||
|
group-person.syn.config.lock-handle-syn-task-second=150
|
||||||
|
ninca.group.person.syn.pool.corePoolSize=40
|
||||||
|
ninca.group.person.syn.pool.maxPoolSize=80
|
||||||
|
ninca.group.person.syn.pool.queueCapacity=1000
|
||||||
|
ninca.group.person.syn.pool.keepAliveSeconds=150
|
||||||
|
ninca.group.person.syn.pool.allowCoreThreadTimeOut=true
|
||||||
|
group-person.delete.keep.days=7
|
||||||
|
|
||||||
|
device.report.thread.corePoolSize=20
|
||||||
|
device.report.thread.maxPoolSize=40
|
||||||
|
device.report.thread.queueCapacity=500
|
||||||
|
device.report.thread.keepAliveSeconds=150
|
||||||
|
device.report.thread.allowCoreThreadTimeOut=true
|
||||||
|
|
||||||
|
device.group.change.thread.corePoolSize=20
|
||||||
|
device.group.change.thread.maxPoolSize=40
|
||||||
|
device.group.change.thread.queueCapacity=500
|
||||||
|
device.group.change.thread.keepAliveSeconds=150
|
||||||
|
device.group.change.thread.allowCoreThreadTimeOut=true
|
||||||
|
|
||||||
|
handle.image.thread.corePoolSize=40
|
||||||
|
handle.image.thread.maxPoolSize=160
|
||||||
|
handle.image.thread.queueCapacity=2000
|
||||||
|
handle.image.thread.keepAliveSeconds=150
|
||||||
|
handle.image.thread.allowCoreThreadTimeOut=true
|
||||||
|
|
||||||
|
ninca.picture.revision.pool.corePoolSize=5
|
||||||
|
ninca.picture.revision.pool.maxPoolSize=5
|
||||||
|
ninca.picture.revision.pool.queueCapacity=100000
|
||||||
|
ninca.picture.revision.pool.keepAliveSeconds=150
|
||||||
|
ninca.picture.revision.pool.allowCoreThreadTimeOut=true
|
||||||
|
|
||||||
|
device.report.approach.time.diff.milliseconds=1000
|
||||||
|
device.person.sync.time.diff.minutes=60
|
||||||
|
cwos.image.store.sync.log=false
|
||||||
|
device.group.pull.time.diff.minutes=10
|
||||||
|
|
||||||
|
ribbon.ReadTimeout=10000
|
||||||
|
ribbon.ConnectTimeout=10000
|
||||||
|
|
||||||
|
# --- 图片 / 人脸像素限制 ---
|
||||||
|
image.size.min=10240
|
||||||
|
image.size.max=3145728
|
||||||
|
image.width.min=30
|
||||||
|
image.width.max=400
|
||||||
|
image.height.min=30
|
||||||
|
image.height.max=400
|
||||||
|
face.width.min=100
|
||||||
|
face.width.max=400
|
||||||
|
face.height.min=100
|
||||||
|
face.height.max=400
|
||||||
|
|
||||||
|
revision.engine.port=20010
|
||||||
|
group-person.syn.config.delay-add-validate-data=false
|
||||||
|
group-person.syn.config.delay-add-validate-hour=48
|
||||||
|
group-person.syn.config.delay-handle-validate.cron=1 0 0 /1 * ? *
|
||||||
|
group-person.syn.config.handle-group-face-exception.cron=0 */5 * * * ?
|
||||||
|
|
||||||
|
# --- 星河湾业务常量(与部署包根目录 application.properties 一致)---
|
||||||
|
xhwId=21474e012cd14e26bc62771873b22562
|
||||||
|
xhwDefaultFloorId=605560547135455232
|
||||||
|
xhwSixFloorId=605560541473144832
|
||||||
|
support.muti.group.device.type=CW-IS1330,-8,MTD8
|
||||||
|
|
||||||
|
# --- cloudwalk-common-serial(UUIDSerial / Snowflake;与电梯应用一致)---
|
||||||
|
cloudwalk.serial.enabled=true
|
||||||
|
cloudwalk.serial.serial-length=8
|
||||||
|
cloudwalk.serial.serial-type=redis
|
||||||
|
cloudwalk.serial.serial-redis-key=CLOUDWALK-ACS-SERIAL-KEY
|
||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# 配置顺序:classpath(fat jar 内公共 application.properties)
|
||||||
|
# → 本目录 application.properties(本地 MySQL/Redis、关 Consul)。
|
||||||
|
set -euo pipefail
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
REPO_ROOT="$(cd "$SCRIPT_DIR/../../../../" && pwd)"
|
||||||
|
source "$REPO_ROOT/scripts/test-env/config/env.sh" 2>/dev/null || true
|
||||||
|
|
||||||
|
JAR="$SCRIPT_DIR/../../target/ninca-common-component-organization-2.9.4-xinghewan.jar"
|
||||||
|
JAVA_BIN="${JAVA_HOME:-/usr/lib/jvm/java-8-openjdk-amd64}/bin/java"
|
||||||
|
|
||||||
|
if [[ ! -f "$JAR" ]]; then
|
||||||
|
echo "Missing $JAR — run: mvn -pl cwos-component-organization-starter -am package -DskipTests" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec "$JAVA_BIN" -jar "$JAR" \
|
||||||
|
--spring.config.location="classpath:/,classpath:/config/,file:${SCRIPT_DIR}/" \
|
||||||
|
--spring.config.name=application \
|
||||||
|
--spring.cloud.bootstrap.location="file:${SCRIPT_DIR}/bootstrap.properties" \
|
||||||
|
--logging.config="file:${SCRIPT_DIR}/recognition-logback.xml" \
|
||||||
|
--spring.main.banner-mode=off \
|
||||||
|
"$@"
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>cn.cloudwalk.ninca</groupId>
|
||||||
|
<artifactId>ninca-common-component-organization-reactor</artifactId>
|
||||||
|
<version>2.9.4-xinghewan</version>
|
||||||
|
<relativePath>../pom.xml</relativePath>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>cwos-component-organization-starter</artifactId>
|
||||||
|
<name>cwos-component-organization-starter</name>
|
||||||
|
<description>可执行聚合层:Spring Boot repackage,与现场 MANIFEST Start-Class / PropertiesLauncher(layout=ZIP)对齐</description>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<!-- 与现场嵌入式 pom 一致:starter 依赖 web,由 service 经 all-lib 提供运行时栈 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.cloudwalk.ninca</groupId>
|
||||||
|
<artifactId>cwos-component-organization-web</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<finalName>ninca-common-component-organization-${project.version}</finalName>
|
||||||
|
<plugins>
|
||||||
|
<!-- 使用 2.7.x 插件以支持 includeSystemScope,将大量 system/all-lib 打入 fat jar -->
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
<version>2.7.18</version>
|
||||||
|
<configuration>
|
||||||
|
<mainClass>cn.cloudwalk.starter.organization.OrganizationServer</mainClass>
|
||||||
|
<layout>ZIP</layout>
|
||||||
|
<includeSystemScope>true</includeSystemScope>
|
||||||
|
</configuration>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<goals>
|
||||||
|
<goal>repackage</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
# `releases/` 发布目录
|
||||||
|
|
||||||
|
## 目的
|
||||||
|
|
||||||
|
`releases/` 用于存放 **可交付的发布包(release bundle)**:从本 reactor 构建的 **Spring Boot fat JAR**、与现场/部署包一致的 **配置文件**、**启动脚本** 与 **校验信息(SHA256)** 的固定组合。
|
||||||
|
该目录是 **对外或运维交付的“落盘点”**,与单纯的 Maven `target/` 构建输出区分开。
|
||||||
|
|
||||||
|
## 目录命名规范
|
||||||
|
|
||||||
|
每个发布包为一个子目录,命名格式:
|
||||||
|
|
||||||
|
```text
|
||||||
|
ninca-common-component-organization-<maven-version>-<YYYYMMDD>/
|
||||||
|
```
|
||||||
|
|
||||||
|
- **`<maven-version>`**:反应堆根 `pom.xml` 中 `<artifactId>ninca-common-component-organization-reactor</artifactId>` 对应的 `<version>`(例如 `2.9.4-xinghewan`)。
|
||||||
|
- **`<YYYYMMDD>`**:发布日期戳;默认取构建当日 `date +%Y%m%d`,也可用环境变量覆盖(见脚本说明)。
|
||||||
|
|
||||||
|
示例:`ninca-common-component-organization-2.9.4-xinghewan-YYYYMMDD/`。
|
||||||
|
|
||||||
|
## 每个 bundle 应包含的内容
|
||||||
|
|
||||||
|
| 内容 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| **Fat JAR** | 从 `cwos-component-organization-starter/target/` 拷贝的可执行 fat JAR(由 `spring-boot-maven-plugin` repackage,`finalName` 为 `ninca-common-component-organization-${project.version}.jar`)。 |
|
||||||
|
| **`application-node.properties`** | 节点差异(Consul 主机、instance-id、management-suffix 等);公共项在 fat jar 内 `application.properties`。从 **`部署包/`** 模板目录复制(见下文)。 |
|
||||||
|
| **`bootstrap.properties`** | 引导配置;来源同上。 |
|
||||||
|
| **`sql/`** | 租户访客策略脚本(与 **`源码/docs/sql`** 一致):电梯库 `tenant_visitor_floor_policy*` + 组织库 **`organization_tenant_visitor_floor_policy*`**(含 **`organization_tenant_visitor_floor_policy_full_install.sql`** 一站式);内含 **`README.txt`**。 |
|
||||||
|
| **`start.sh`** | 启动脚本:**`--spring.config.location=classpath:/,file:<发布目录>/`** + **`--spring.config.name=application,application-node`** + **`--logging.path=<发布目录>/logs`**。 |
|
||||||
|
| **可选符号链接** | 运维友好别名:`ninca-common-component-organization-V<主版本三元组>_<YYYYMMDD>.jar` → 实际 fat JAR(例如 `V2.9.4_20260506` 对应版本 `2.9.4-xinghewan`)。 |
|
||||||
|
| **`RELEASE.txt`** | 发布说明:版本、目录名、构建命令、文件清单、fat JAR 的 **SHA256**。 |
|
||||||
|
|
||||||
|
其中 `application-node.properties` / `bootstrap.properties` 的「权威模板」通常来自现场 **部署包** 目录(与运维目录对齐),路径示例:
|
||||||
|
|
||||||
|
`星河湾星中星/部署包/ninca_common_component_organization_01-ninca_common_component_organization/`
|
||||||
|
|
||||||
|
若该路径下不存在对应文件,脚本会 **告警并跳过**,不会中断构建(仍可从别处手工放入同名文件)。
|
||||||
|
|
||||||
|
## 前置条件
|
||||||
|
|
||||||
|
- **JDK 8**:与本 reactor 一致;默认使用 `/usr/lib/jvm/java-8-openjdk-amd64`(可通过 `JAVA_HOME` 覆盖)。
|
||||||
|
- **`all-lib/`**:反应堆依赖的本地扁平库目录必须存在且齐全,否则 `mvn package` 可能失败(根 `pom.xml` 中 `all-lib.dir` 指向 `${maven.multiModuleProjectDirectory}/all-lib`)。
|
||||||
|
|
||||||
|
## JDK / `JAVA_HOME` 说明
|
||||||
|
|
||||||
|
- 脚本默认使用 **`/usr/lib/jvm/java-8-openjdk-amd64`** 进行 **`mvn clean package`**。
|
||||||
|
- 若交互式环境中 **`JAVA_HOME` 已指向 JDK 11+**,仅写 `JAVA_HOME:-默认值` **不会**覆盖已导出变量;脚本会检测并在非 JDK 8 时 **发出 WARN 并强制改用上述 JDK8 路径**,以保证与本 reactor 一致。
|
||||||
|
- 若需使用其他 JDK 8 安装路径,请先 **`export JAVA_HOME=/path/to/jdk8`**(确保 `java -version` 为 1.8),再执行脚本。
|
||||||
|
|
||||||
|
## 自动生成脚本
|
||||||
|
|
||||||
|
使用 **`create-release-bundle.sh`**(位于本目录,可执行)从反应堆根执行 **`mvn clean package -DskipTests`**,并生成符合上述规范的目录与 `RELEASE.txt`。
|
||||||
|
|
||||||
|
用法示例:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /path/to/maven-ninca-common-component-organization
|
||||||
|
./releases/create-release-bundle.sh
|
||||||
|
RELEASE_DATE=20260506 ./releases/create-release-bundle.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
环境变量(可选):
|
||||||
|
|
||||||
|
- **`RELEASE_DATE`**:目录后缀 `YYYYMMDD`,默认当天。
|
||||||
|
- **`DEPLOY_TEMPLATE_ROOT`**:覆盖部署包模板目录(默认见上文「配置文件来源」)。
|
||||||
|
- **`DOCS_SQL_ROOT`**:覆盖 **`sql/`** 脚本来源目录(默认 **`源码/docs/sql`**,即反应堆上一级目录下的 `docs/sql`)。
|
||||||
|
|
||||||
|
## 与 `deploy-implementation-package/` 的关系
|
||||||
|
|
||||||
|
构建产物路径、制品暂存等说明见同仓库:
|
||||||
|
|
||||||
|
**[../deploy-implementation-package/README.md](../deploy-implementation-package/README.md)**
|
||||||
|
|
||||||
|
(若该文件不存在,请以反应堆根 `pom.xml` 与 starter 模块 `target/` 为准。)
|
||||||
|
|
||||||
|
## Git 与大文件
|
||||||
|
|
||||||
|
本目录下的 **`ninca-common-component-organization-*/` 发布子目录**(含 fat jar)通过 **`releases/.gitignore`** 忽略,避免将大体积二进制提交入库;**`README.md`** 与 **`create-release-bundle.sh`** 仍正常跟踪。
|
||||||
|
|
||||||
|
## 手工 `java -jar`(不用 start.sh)时
|
||||||
|
|
||||||
|
若从其它工作目录直接启动,请显式指定配置目录与可写日志路径,例如:
|
||||||
|
|
||||||
|
`--spring.config.location=file:<发布包绝对路径>/` 与 `--logging.path=<同目录下 logs 或 /tmp/...>`。
|
||||||
|
|
||||||
|
仅写 `--spring.config.location` 时若未覆盖 `logging.path`,且 `application.properties` 仍指向生产路径 `/data/cwos/...`,会因无法创建目录导致 **Logback 初始化失败**。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**English (short):** `releases/` holds versioned, deployable bundles (fat JAR + config + `start.sh` + `RELEASE.txt` + optional symlink), named `ninca-common-component-organization-<maven-version>-<YYYYMMDD>/`. Build requires JDK 8 and `all-lib/`. Use `create-release-bundle.sh` to reproduce bundles consistently.
|
||||||
@@ -0,0 +1,189 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# 从反应堆根构建 fat JAR,并在 releases/ 下生成发布目录。
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
REACTOR_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||||
|
DEFAULT_JAVA_HOME="/usr/lib/jvm/java-8-openjdk-amd64"
|
||||||
|
export JAVA_HOME="${JAVA_HOME:-$DEFAULT_JAVA_HOME}"
|
||||||
|
|
||||||
|
# 若登录 shell 已将 JAVA_HOME 设为 JDK 11+,:- 默认值不会生效;发布编译仍须 JDK 8
|
||||||
|
if ! "${JAVA_HOME}/bin/java" -version 2>&1 | grep -qE 'version "1\.8\.|java version "1\.8\.'; then
|
||||||
|
echo "WARN: 当前 JAVA_HOME(${JAVA_HOME})不是 JDK 8;发布构建将改用 ${DEFAULT_JAVA_HOME}" >&2
|
||||||
|
export JAVA_HOME="$DEFAULT_JAVA_HOME"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 默认:顶层部署包中的组件模板目录(application-node.properties / bootstrap.properties)
|
||||||
|
DEFAULT_DEPLOY_TEMPLATE="/media/zebra/9e8fa357-7db6-4d70-88ed-d5de5a059a663/星河湾星中星/部署包/ninca_common_component_organization_01-ninca_common_component_organization"
|
||||||
|
DEPLOY_TEMPLATE="${DEPLOY_TEMPLATE_ROOT:-$DEFAULT_DEPLOY_TEMPLATE}"
|
||||||
|
# 与租户访客策略相关的 SQL 权威源(与电梯 prepare-db / release-cw-elevator 共用 docs/sql)
|
||||||
|
DEFAULT_DOCS_SQL="$(cd "${SCRIPT_DIR}/../../docs/sql" 2>/dev/null && pwd)" || true
|
||||||
|
if [[ -z "${DEFAULT_DOCS_SQL}" || ! -d "${DEFAULT_DOCS_SQL}" ]]; then
|
||||||
|
DEFAULT_DOCS_SQL="/media/zebra/9e8fa357-7db6-4d70-88ed-d5de5a059a663/星河湾星中星/源码/docs/sql"
|
||||||
|
fi
|
||||||
|
DOCS_SQL="${DOCS_SQL_ROOT:-$DEFAULT_DOCS_SQL}"
|
||||||
|
|
||||||
|
RELEASE_DATE="${RELEASE_DATE:-$(date +%Y%m%d)}"
|
||||||
|
|
||||||
|
die() { echo "ERROR: $*" >&2; exit 1; }
|
||||||
|
|
||||||
|
read_version_from_pom() {
|
||||||
|
local pom="${REACTOR_ROOT}/pom.xml"
|
||||||
|
[[ -f "$pom" ]] || die "找不到反应堆 pom.xml: $pom"
|
||||||
|
awk '
|
||||||
|
/<artifactId>ninca-common-component-organization-reactor<\/artifactId>/ { inr=1 }
|
||||||
|
inr && /<version>/ {
|
||||||
|
sub(/.*<version>/, "")
|
||||||
|
sub(/<\/version>.*/, "")
|
||||||
|
print
|
||||||
|
exit
|
||||||
|
}
|
||||||
|
' "$pom"
|
||||||
|
}
|
||||||
|
|
||||||
|
read_version_mvn() {
|
||||||
|
# 备用:mvn help:evaluate(部分环境 -q 会吞掉 stdout,故解析最后一行语义化版本)
|
||||||
|
(cd "$REACTOR_ROOT" && mvn -DforceStdout help:evaluate -Dexpression=project.version -f pom.xml 2>/dev/null \
|
||||||
|
| grep -E '^[0-9]' | tail -1) || true
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ ! -x "${JAVA_HOME}/bin/java" ]]; then
|
||||||
|
die "JAVA_HOME 无效或缺少 java 可执行文件: ${JAVA_HOME}/bin/java"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -d "${REACTOR_ROOT}/all-lib" ]]; then
|
||||||
|
echo "WARN: 未找到 all-lib/(${REACTOR_ROOT}/all-lib)。打包可能失败;请先准备本地扁平依赖库。" >&2
|
||||||
|
fi
|
||||||
|
|
||||||
|
VERSION="$(read_version_from_pom)"
|
||||||
|
if [[ -z "${VERSION}" ]]; then
|
||||||
|
VERSION="$(read_version_mvn)"
|
||||||
|
fi
|
||||||
|
[[ -n "${VERSION}" ]] || die "无法从 pom.xml 解析反应堆版本"
|
||||||
|
|
||||||
|
SEMVER="${VERSION%%-*}"
|
||||||
|
JAR_BASENAME="ninca-common-component-organization-${VERSION}.jar"
|
||||||
|
SOURCE_JAR="${REACTOR_ROOT}/cwos-component-organization-starter/target/${JAR_BASENAME}"
|
||||||
|
BUNDLE_NAME="ninca-common-component-organization-${VERSION}-${RELEASE_DATE}"
|
||||||
|
OUT_DIR="${SCRIPT_DIR}/${BUNDLE_NAME}"
|
||||||
|
SYMLINK_NAME="ninca-common-component-organization-V${SEMVER}_${RELEASE_DATE}.jar"
|
||||||
|
|
||||||
|
echo "==> Reactor: ${REACTOR_ROOT}"
|
||||||
|
echo "==> Version: ${VERSION}"
|
||||||
|
echo "==> Release date: ${RELEASE_DATE}"
|
||||||
|
echo "==> Output dir: ${OUT_DIR}"
|
||||||
|
|
||||||
|
(cd "$REACTOR_ROOT" && mvn clean package -DskipTests)
|
||||||
|
|
||||||
|
[[ -f "$SOURCE_JAR" ]] || die "未找到构建产物: ${SOURCE_JAR}(请先确认 starter 已成功 repackage)"
|
||||||
|
|
||||||
|
mkdir -p "$OUT_DIR"
|
||||||
|
cp -f "$SOURCE_JAR" "${OUT_DIR}/${JAR_BASENAME}"
|
||||||
|
|
||||||
|
copy_if_exists() {
|
||||||
|
local src="$1" dest="$2"
|
||||||
|
if [[ -f "$src" ]]; then
|
||||||
|
cp -f "$src" "$dest"
|
||||||
|
echo "==> 已复制: $(basename "$src") <- ${src}"
|
||||||
|
else
|
||||||
|
echo "WARN: 跳过(文件不存在): $src" >&2
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
copy_if_exists "${DEPLOY_TEMPLATE}/application-node.properties" "${OUT_DIR}/application-node.properties"
|
||||||
|
copy_if_exists "${DEPLOY_TEMPLATE}/bootstrap.properties" "${OUT_DIR}/bootstrap.properties"
|
||||||
|
# 可选:与 jar 内 logging.config=classpath:recognition-logback.xml 搭配外置覆盖时使用
|
||||||
|
copy_if_exists "${REACTOR_ROOT}/cwos-component-organization-starter/src/main/resources/recognition-logback.xml" "${OUT_DIR}/recognition-logback.xml"
|
||||||
|
|
||||||
|
# 组织组件交付包内附带与策略表相关的 SQL(执行库以脚本注释为准;多为 cw-elevator-application,_init 含 component-organization 主数据 id)
|
||||||
|
SQL_DIR="${OUT_DIR}/sql"
|
||||||
|
mkdir -p "${SQL_DIR}"
|
||||||
|
SQL_NAMES=(
|
||||||
|
tenant_visitor_floor_policy.sql
|
||||||
|
tenant_visitor_floor_policy_v2.sql
|
||||||
|
tenant_visitor_floor_policy_migrate_org_id.sql
|
||||||
|
tenant_visitor_floor_policy_init_guangfa_fund.sql
|
||||||
|
tenant_visitor_floor_policy_init_property_mgmt_6f.sql
|
||||||
|
organization_tenant_visitor_floor_policy.sql
|
||||||
|
organization_tenant_visitor_floor_policy_v2.sql
|
||||||
|
organization_tenant_visitor_floor_policy_init_guangfa_fund.sql
|
||||||
|
organization_tenant_visitor_floor_policy_init_property_mgmt_6f.sql
|
||||||
|
organization_tenant_visitor_floor_policy_full_install.sql
|
||||||
|
)
|
||||||
|
for base in "${SQL_NAMES[@]}"; do
|
||||||
|
copy_if_exists "${DOCS_SQL}/${base}" "${SQL_DIR}/${base}"
|
||||||
|
done
|
||||||
|
if [[ -d "${SQL_DIR}" ]] && [[ -n "$(find "${SQL_DIR}" -maxdepth 1 -name '*.sql' -print -quit)" ]]; then
|
||||||
|
cat > "${SQL_DIR}/README.txt" << 'EOSQL'
|
||||||
|
本目录 SQL 与源码 docs/sql 下权威文件一致,随组织组件发布包一并交付。
|
||||||
|
|
||||||
|
说明(概要):
|
||||||
|
- tenant_visitor_floor_policy.sql:电梯库,先 DROP 再 CREATE(含 org_id 最终结构);见各 init 文件头。
|
||||||
|
- organization_tenant_visitor_floor_policy*.sql:组织库 component-organization;DDL/full_install 会先 DROP 策略表再重建。
|
||||||
|
- tenant_visitor_floor_policy_migrate_org_id.sql:迁移/运维说明(注释为主)。
|
||||||
|
- *_init_*.sql:种子数据(DML),org_id 需与 cw_is_organization 一致。
|
||||||
|
|
||||||
|
执行顺序与现网是否已有表有关,实施前请对照 docs/business 下设计文档。
|
||||||
|
EOSQL
|
||||||
|
echo "==> 已写入: sql/README.txt"
|
||||||
|
fi
|
||||||
|
|
||||||
|
cat > "${OUT_DIR}/start.sh" << EOF
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
DIR="\$(cd "\$(dirname "\${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
cd "\${DIR}"
|
||||||
|
JAR_NAME="${JAR_BASENAME}"
|
||||||
|
# classpath 加载 jar 内公共 application.properties;file 加载本目录 application-node + bootstrap
|
||||||
|
exec "\${JAVA_HOME:-/usr/lib/jvm/java-8-openjdk-amd64}/bin/java" -jar "\${DIR}/\${JAR_NAME}" \\
|
||||||
|
--spring.config.location="classpath:/,file:\${DIR}/" \\
|
||||||
|
--spring.config.name=application,application-node \\
|
||||||
|
--logging.path="\${DIR}/logs" \\
|
||||||
|
"\$@"
|
||||||
|
EOF
|
||||||
|
chmod +x "${OUT_DIR}/start.sh"
|
||||||
|
|
||||||
|
(cd "$OUT_DIR" && ln -sf "${JAR_BASENAME}" "${SYMLINK_NAME}")
|
||||||
|
|
||||||
|
SHA256="$(sha256sum "${OUT_DIR}/${JAR_BASENAME}" | awk '{ print $1 }')"
|
||||||
|
ABS_OUT="$(cd "$OUT_DIR" && pwd)"
|
||||||
|
ABS_JAR="${ABS_OUT}/${JAR_BASENAME}"
|
||||||
|
|
||||||
|
{
|
||||||
|
echo "Component: ninca-common-component-organization (starter fat jar)"
|
||||||
|
echo "Reactor version (pom.xml): ${VERSION}"
|
||||||
|
echo "Release bundle: ${BUNDLE_NAME}"
|
||||||
|
echo "Date stamp: ${RELEASE_DATE}"
|
||||||
|
echo ""
|
||||||
|
echo "Build command:"
|
||||||
|
echo " export JAVA_HOME=${JAVA_HOME}"
|
||||||
|
echo " cd \"${REACTOR_ROOT}\""
|
||||||
|
echo " mvn clean package -DskipTests"
|
||||||
|
echo ""
|
||||||
|
echo "Fat jar artifact:"
|
||||||
|
echo " ${ABS_JAR}"
|
||||||
|
echo "SHA256:"
|
||||||
|
echo " ${SHA256}"
|
||||||
|
echo ""
|
||||||
|
echo "Files in this directory:"
|
||||||
|
echo " - sql/(租户访客策略相关脚本,来自 docs/sql;含 README.txt)"
|
||||||
|
echo " - application-node.properties(节点 Consul/instance-id;若部署包模板存在则已复制)"
|
||||||
|
echo " - bootstrap.properties (若部署包模板存在则已复制)"
|
||||||
|
echo " - ${JAR_BASENAME}"
|
||||||
|
echo " - ${SYMLINK_NAME}"
|
||||||
|
echo " - RELEASE.txt"
|
||||||
|
echo " - start.sh"
|
||||||
|
echo ""
|
||||||
|
echo "Symlink (ops naming):"
|
||||||
|
echo " ${SYMLINK_NAME} -> ${JAR_BASENAME}"
|
||||||
|
echo ""
|
||||||
|
echo "Start:"
|
||||||
|
echo " bash \"${ABS_OUT}/start.sh\""
|
||||||
|
} > "${OUT_DIR}/RELEASE.txt"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "========================================"
|
||||||
|
echo "Release bundle: ${ABS_OUT}"
|
||||||
|
echo "SHA256 (${JAR_BASENAME}):"
|
||||||
|
echo " ${SHA256}"
|
||||||
|
echo "========================================"
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -77,15 +77,9 @@ if [[ -f "$DATA_BACKUP/34_2026_04_23_17_28_33.sql.gz" ]]; then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Apply V2.0.7 DDL (tenant_visitor_floor_policy) — skip if table exists
|
# 电梯库策略表:DROP + 按最终结构重建(清空历史策略数据;表结构含 org_id / uk_org_building)
|
||||||
log_info "Checking V2.0.7 DDL (tenant_visitor_floor_policy)..."
|
log_info "Applying elevator tenant_visitor_floor_policy DDL (DROP + CREATE)..."
|
||||||
TABLE_EXISTS=$($MYSQL_CMD -N -e "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='$DB_ELEVATOR' AND table_name='tenant_visitor_floor_policy'" 2>/dev/null || echo "0")
|
$MYSQL_CMD "$DB_ELEVATOR" < "$REPO_ROOT/docs/sql/tenant_visitor_floor_policy.sql"
|
||||||
if [[ "$TABLE_EXISTS" -gt 0 ]]; then
|
log_ok " tenant_visitor_floor_policy reset (see docs/sql/tenant_visitor_floor_policy.sql)"
|
||||||
log_ok " tenant_visitor_floor_policy already exists — DDL skipped"
|
|
||||||
else
|
|
||||||
log_info "Applying V2.0.7 DDL..."
|
|
||||||
$MYSQL_CMD "$DB_ELEVATOR" < "$REPO_ROOT/docs/sql/tenant_visitor_floor_policy.sql"
|
|
||||||
log_ok " V2 DDL applied"
|
|
||||||
fi
|
|
||||||
|
|
||||||
log_info "Database preparation complete"
|
log_info "Database preparation complete"
|
||||||
|
|||||||
Reference in New Issue
Block a user