feat: add service config templates and extraction script

Former-commit-id: 1de24b7eb79676d1aba9d799a58c5a753290cf52
This commit is contained in:
反编译工作区
2026-05-01 19:38:01 +08:00
parent 3175b7074b
commit 8b15445328
2433 changed files with 8322164 additions and 1604 deletions
@@ -1 +0,0 @@
a990d38e5a660a4f40507e535f83b45b88bff6f4
@@ -0,0 +1 @@
2343e262c5e1e3a163208067761126459fdce5a0
@@ -0,0 +1,60 @@
#!/bin/bash
# 组织服务楼层数据诊断
# 用法: bash diag_person_floors.sh
HOST="127.0.0.1"
PORT="18081"
BUSINESS_ID="2524639890ba4f2cba9ba1a4eeaa4015"
PERSONS=(
"1060601019894960128|陈国辉|1403艾斯"
"1090779433129840640|王姣|1405一博"
"1072908835884208128|秦夏|广发基金"
)
echo "=== 电梯健康检查 ==="
curl -s "http://${HOST}:${PORT}/health" 2>/dev/null || echo "FAIL"
for p in "${PERSONS[@]}"; do
IFS='|' read -r PID PNAME PORG <<< "$p"
echo ""
echo "=== $PNAME ($PID) [$PORG] ==="
# 调电梯 addVisitor 接口(会内部调组织服务获取人员详情)
RESP=$(curl -s -X POST "http://${HOST}:${PORT}/elevator/person/add/visitor" \
-H "Content-Type: application/json" \
-H "businessid: ${BUSINESS_ID}" \
-d "{
\"personId\": \"${PID}\",
\"visitorId\": \"diag_$(date +%s)\",
\"floorIds\": [],
\"begVisitorTime\": $(date +%s)000,
\"endVisitorTime\": $(($(date +%s) + 86400))000
}")
CODE=$(echo "$RESP" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('code',''))" 2>/dev/null)
MSG=$(echo "$RESP" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('message',''))" 2>/dev/null)
echo " code=$CODE"
echo " message=$MSG"
echo " raw=$RESP"
done
echo ""
echo "=== 试探:用特定 floorId 绕过策略 ==="
# 看看哪些 zoneId 能被组织服务接受
for zone in "605560541473144832" "605560545117995008" "605560542752407552" "605560545449345024"; do
RESP=$(curl -s -X POST "http://${HOST}:${PORT}/elevator/person/add/visitor" \
-H "Content-Type: application/json" \
-H "businessid: ${BUSINESS_ID}" \
-d "{
\"personId\": \"1060601019894960128\",
\"visitorId\": \"zone_test_${zone}\",
\"floorIds\": [\"${zone}\"],
\"begVisitorTime\": $(date +%s)000,
\"endVisitorTime\": $(($(date +%s) + 86400))000
}")
CODE=$(echo "$RESP" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('code',''))" 2>/dev/null)
MSG=$(echo "$RESP" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('message',''))" 2>/dev/null)
echo " zone=$zone → code=$CODE msg=$MSG"
done
@@ -0,0 +1,95 @@
#!/usr/bin/env python3
"""组织服务桩 — 模拟 ninca-common-component-organization 的 /component/person/detail 端点"""
from flask import Flask, request, jsonify
app = Flask(__name__)
# 被访人数据映射:personId → { floorList, organizationIds }
HOST_DATA = {
"1060601019894960128": { # 陈国辉 — 1403艾斯 + 星中心物业
"name": "陈国辉",
"floorList": ["605560541473144832", "605560545117995008"], # 6F, 28F
"organizationIds": [
"72fb65ec5de94201b909a98b8bae1892", # 1403艾斯
"f216235e54ca42bfa0379e69b3754aff", # 星中心物业
],
},
"1090779433129840640": { # 王姣 — 1405一博环保 + 一博
"name": "王姣",
"floorList": ["605560542752407552", "605560543834537984"], # 15F, 20F
"organizationIds": [
"2095de3d541f44eba686c78fda68336f", # 1405一博环保
"5c129c5eae114309933042d7f2006aa2", # 一博
],
},
"1072908835884208128": { # 秦夏 — 广发基金 + 正式员工
"name": "秦夏",
"floorList": [
"605560545117995008", # 28F
"605560545449345024", # 30F
"605560545596145664", # 31F
"605560545738752000", # 32F
"605560545893941248", # 33F
],
"organizationIds": [
"488b8ad049bb43408a6fbcc50bcb89ac", # 广发基金
"b549a73065374ecf871841544f329a98", # 正式员工
],
},
}
@app.route("/component/person/detail", methods=["POST"])
def person_detail():
data = request.get_json(force=True, silent=True) or {}
person_id = data.get("id", "")
host = HOST_DATA.get(person_id)
if not host:
return jsonify({
"success": False,
"code": "76260531",
"message": f"person not found: {person_id}",
"data": None,
})
return jsonify({
"success": True,
"code": "0",
"message": "ok",
"data": {
"id": person_id,
"businessId": "2524639890ba4f2cba9ba1a4eeaa4015",
"name": host["name"],
"floorList": host["floorList"],
"organizationIds": host["organizationIds"],
},
})
@app.route("/health", methods=["GET"])
def health():
return jsonify({"status": "UP"})
@app.route("/sysetting/zone/page", methods=["POST"])
def zone_page():
return jsonify({
"success": True,
"code": "0",
"data": {
"totalRows": 1,
"currentPage": 1,
"pageSize": 10,
"datas": [{
"id": "605560545117995008",
"zoneId": "605560545117995008",
"parentId": "605560539791228928",
}],
},
})
if __name__ == "__main__":
app.run(host="0.0.0.0", port=18082, debug=False)
@@ -0,0 +1,13 @@
-- ============================================================
-- org_id 策略修复 — 测试数据清理(验证完成后执行)
-- 目标库:192.168.3.12:3307 / cw-elevator-application
-- 执行:mysql -h 192.168.3.12 -P 3307 -u root -p123456 cw-elevator-application < test_data_cleanup.sql
-- ============================================================
DELETE FROM tenant_visitor_floor_policy WHERE id IN ('policy_t1_1403', 'policy_t3_invalid', 'policy_t5_disabled');
UPDATE tenant_visitor_floor_policy SET org_id = NULL WHERE id = 'gf_vstr_policy_guangfa_fund_001x';
-- 验证清理结果(应返回空集或 org_id=NULL
SELECT id, org_id, enabled FROM tenant_visitor_floor_policy
WHERE id IN ('policy_t1_1403', 'policy_t3_invalid', 'policy_t5_disabled', 'gf_vstr_policy_guangfa_fund_001x');
@@ -0,0 +1,26 @@
DELETE FROM tenant_visitor_floor_policy WHERE id IN ('policy_t1_1403', 'policy_t3_invalid', 'policy_t5_disabled');
INSERT INTO tenant_visitor_floor_policy
(id, org_id, business_id, policy_type, allow_zone_ids, building_id, enabled, policy_version, created_at, updated_at)
VALUES
('policy_t1_1403', '72fb65ec5de94201b909a98b8bae1892', NULL, 'INTERSECT_ALLOWLIST',
'["db18ffc1346a44fcba96fad0fd2d7d3a"]', NULL, 1, 1, UNIX_TIMESTAMP(NOW())*1000, UNIX_TIMESTAMP(NOW())*1000);
INSERT INTO tenant_visitor_floor_policy
(id, org_id, business_id, policy_type, allow_zone_ids, building_id, enabled, policy_version, created_at, updated_at)
VALUES
('policy_t3_invalid', '72fb65ec5de94201b909a98b8bae1892', NULL, 'INTERSECT_ALLOWLIST',
'["db18ffc1346a44fcba96fad0fd2d7d3a","605560540000000000"]', NULL, 1, 1, UNIX_TIMESTAMP(NOW())*1000, UNIX_TIMESTAMP(NOW())*1000);
INSERT INTO tenant_visitor_floor_policy
(id, org_id, business_id, policy_type, allow_zone_ids, building_id, enabled, policy_version, created_at, updated_at)
VALUES
('policy_t5_disabled', '72fb65ec5de94201b909a98b8bae1892', NULL, 'INTERSECT_ALLOWLIST',
'["db18ffc1346a44fcba96fad0fd2d7d3a"]', NULL, 0, 1, UNIX_TIMESTAMP(NOW())*1000, UNIX_TIMESTAMP(NOW())*1000);
UPDATE tenant_visitor_floor_policy SET org_id = '488b8ad049bb43408a6fbcc50bcb89ac'
WHERE id = 'gf_vstr_policy_guangfa_fund_001x';
SELECT id, org_id, enabled, allow_zone_ids
FROM tenant_visitor_floor_policy
WHERE id IN ('policy_t1_1403', 'policy_t3_invalid', 'policy_t5_disabled', 'gf_vstr_policy_guangfa_fund_001x');
@@ -0,0 +1,70 @@
server.port=18081
logging.path=/tmp/elevator-logs
logging.file=cw-elevator
spring.cloud.consul.host=192.168.3.12
spring.cloud.consul.port=8500
spring.cloud.consul.enabled=true
spring.redis.host=127.0.0.1
spring.redis.port=6380
spring.redis.database=5
spring.redis.timeout=3000
spring.redis.pool.max-active=10
spring.redis.pool.max-idle=1
spring.redis.pool.max-wait=10
spring.redis.pool.min-idle=0
spring.shardingsphere.datasource.names=ds0
spring.shardingsphere.datasource.ds0.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds0.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds0.jdbc-url=jdbc:mysql://192.168.3.12:3307/cw-elevator-application?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true
spring.shardingsphere.datasource.ds0.username=root
spring.shardingsphere.datasource.ds0.password=123456
spring.shardingsphere.datasource.ds0.connection-timeout=60000
spring.shardingsphere.datasource.ds0.maximum-pool-size=20
spring.shardingsphere.datasource.ds0.minimum-idle=5
spring.shardingsphere.datasource.ds0.max-lifetime=1765000
spring.shardingsphere.datasource.ds0.auto-commit=true
spring.shardingsphere.datasource.ds0.pool-name=ds0-pool
spring.shardingsphere.props.sql.show=false
spring.shardingsphere.sharding.default-data-source-name=ds0
cloudwalk.event.bootstrap-servers=127.0.0.1:9092
cloudwalk.event.group-id=cw-elevator-local
feign.device.name=cwos-portal
feign.resource.name=cwos-portal
feign.cwos-portal.name=cwos-portal
feign.davinci-portal.name=cwos-portal
feign.ninca-crk-std.name=ninca-crk-std
feign.component-organization.name=ninca-common-component-organization
feign.ninca-common.name=ninca-common
feign.mqtt.name=cloudwalk-device-thirdparty
feign.hystrix.enable=true
feign.okhttp.enable=true
ribbon.okhttp.enabled=true
ribbon.ReadTimeout=10000
ribbon.ConnectTimeout=10000
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=10000
spring.mvc.throw-exception-if-no-handler-found=true
spring.messages.basename=access-control
mybatis.mapper-locations=classpath*:cn/cloudwalk/elevator/**/*.xml
mybatis.config-location=classpath:mapper/mybatis-config.xml
cloudwalk.serial.enable=true
cloudwalk.serial.serial-type=redis
cloudwalk.serial.serial-redis-key=CLOUDWALK-ACS-SERIAL-KEY
management.health.redis.enabled=false
management.health.db.enabled=false
cloudwalk.datafield.enable=true
cloudwalk.datafield.securityKey=d4b2aabc97394a12a27fc3cca6cd9ba1
intelligent.lock.enable=true
intelligent.lock.config.default-wait-time=10000
lockWatchdogTimeout=21000
person.name.space=recordEvent
elevator.application.key=xinghewan
elevator.application.time=600
elevator.application.keyA=5B7DEF88FF04
ninca-crk-std.ip=127.0.0.1:16106
sendRecord.boolean=false
floor.building.id=605560539791228928
spring.redis.password=1qaz!QAZ
ninca-common-component-organization.ribbon.listOfServers=127.0.0.1:18082
ninca-common-component-organization.ribbon.ServerListRefreshInterval=5000
ninca-common.ribbon.listOfServers=127.0.0.1:18082
ninca-common.ribbon.ServerListRefreshInterval=5000
@@ -0,0 +1,269 @@
#!/usr/bin/env python3
"""org_id 策略修复 — 无鉴权验证脚本"""
import argparse
import json
import os
import sys
import time
from datetime import datetime
from typing import Any, Dict, List, Optional
import requests
# ===== 配置常量 =====
DB_CONFIG = {
"host": "192.168.3.12",
"port": 3307,
"user": "root",
"password": "123456",
"db_org": "component-organization",
"db_elevator": "cw-elevator-application",
}
BUSINESS_ID = "2524639890ba4f2cba9ba1a4eeaa4015"
ORG_1403 = "72fb65ec5de94201b909a98b8bae1892"
ORG_1405 = "2095de3d541f44eba686c78fda68336f"
ORG_GUANGFA = "488b8ad049bb43408a6fbcc50bcb89ac"
HOST_1403 = "1060601250460012544" # 丘文明 (1403艾斯, 122 devices)
HOST_1405 = "1090914042800263168" # 陈美全 (1405一博环保, 116 devices)
HOST_GUANGFA = "964454497399468032" # 蒙海文 (广发基金, 279 devices)
VISITOR_IDS = [
"vfy_t1_" + str(int(time.time())), "vfy_t2_" + str(int(time.time()) + 1),
"vfy_t3_" + str(int(time.time()) + 2), "vfy_t4_" + str(int(time.time()) + 3),
"vfy_t5_" + str(int(time.time()) + 4), "vfy_t6_" + str(int(time.time()) + 5),
"vfy_t7_" + str(int(time.time()) + 6),
]
ZONE_12F = "db18ffc1346a44fcba96fad0fd2d7d3a" # 12F (from cw_qz_device_area)
ZONE_4F = "dcfeeced3ad040c485df82cc95417b34" # 4F (alternate)
ZONE_INVALID = "605560540000000000" # non-existent zone
OK_CODES = {"0", "200"}
TEST_CASES = [
{"id":"T1","name":"有策略→allow替换floorList","host_id":HOST_1403,"visitor_id":VISITOR_IDS[0],"policy_id":"policy_t1_1403","expected_pass":True,"expected_floors":[ZONE_12F]},
{"id":"T2","name":"无策略→floorList","host_id":HOST_1405,"visitor_id":VISITOR_IDS[1],"policy_id":None,"expected_pass":True,"expected_floors":None},
{"id":"T3","name":"allow含无效zone→拒绝","host_id":HOST_1403,"visitor_id":VISITOR_IDS[2],"policy_id":"policy_t3_invalid","expected_pass":False,"expected_code":"76260533"},
{"id":"T4","name":"多组织命中第一个策略","host_id":HOST_1403,"visitor_id":VISITOR_IDS[3],"policy_id":"policy_t1_1403","expected_pass":True,"expected_floors":[ZONE_12F]},
{"id":"T5","name":"enabled=0等同无策略","host_id":HOST_1403,"visitor_id":VISITOR_IDS[4],"policy_id":"policy_t5_disabled","expected_pass":True,"expected_floors":None},
{"id":"T6","name":"UC-02策略优先","host_id":HOST_1403,"visitor_id":VISITOR_IDS[5],"policy_id":"policy_t1_1403","expected_pass":True,"expected_floors":[ZONE_12F],"floor_ids_override":[ZONE_4F]},
{"id":"T7","name":"广发基金迁移验证","host_id":HOST_GUANGFA,"visitor_id":VISITOR_IDS[6],"policy_id":"gf_vstr_policy_guangfa_fund_001x","expected_pass":True,"expected_floors":[ZONE_12F]},
]
def parse_args():
p = argparse.ArgumentParser(description="org_id 策略修复验证")
p.add_argument("--elevator-base-url", default="http://127.0.0.1:18081")
p.add_argument("--skip-db", action="store_true")
return p.parse_args()
def health_check(base_url):
try:
r = requests.get(f"{base_url}/health", timeout=10)
ok = r.status_code == 200
print(f"[HEALTH] {base_url} -> {r.status_code} {'OK' if ok else 'FAIL'}")
return ok
except Exception as e:
print(f"[HEALTH] {base_url} -> ERROR: {e}")
return False
def get_db_conn():
import pymysql # 延迟导入,--skip-db 模式无需安装
return pymysql.connect(
host=DB_CONFIG["host"], port=DB_CONFIG["port"],
user=DB_CONFIG["user"], password=DB_CONFIG["password"],
database=DB_CONFIG["db_elevator"],
charset="utf8mb4", autocommit=True,
)
def execute_sql(sql, params=None):
conn = get_db_conn()
try:
with conn.cursor() as cur:
cur.execute(sql, params)
finally:
conn.close()
def prepare_test_data():
policies = [
("policy_t1_1403", ORG_1403, f'["{ZONE_12F}"]', 1),
("policy_t3_invalid", ORG_1403, f'["{ZONE_12F}","{ZONE_INVALID}"]', 1),
("policy_t5_disabled", ORG_1403, f'["{ZONE_12F}"]', 0),
]
for pid, oid, zones_json, enabled in policies:
execute_sql("DELETE FROM tenant_visitor_floor_policy WHERE id=%s", (pid,))
execute_sql(
"INSERT INTO tenant_visitor_floor_policy "
"(id, org_id, business_id, policy_type, allow_zone_ids, building_id, enabled, policy_version, created_at, updated_at) "
"VALUES (%s, %s, NULL, 'INTERSECT_ALLOWLIST', %s, NULL, %s, 1, UNIX_TIMESTAMP(NOW())*1000, UNIX_TIMESTAMP(NOW())*1000)",
(pid, oid, zones_json, enabled),
)
print(f" INSERT policy {pid} org={oid} enabled={enabled}")
execute_sql("UPDATE tenant_visitor_floor_policy SET org_id=%s WHERE id='gf_vstr_policy_guangfa_fund_001x'", (ORG_GUANGFA,))
print(f" UPDATE 广发基金 org_id={ORG_GUANGFA}")
def cleanup_test_data():
for pid in ["policy_t1_1403", "policy_t3_invalid", "policy_t5_disabled"]:
execute_sql("DELETE FROM tenant_visitor_floor_policy WHERE id=%s", (pid,))
print(f" DELETE {pid}")
execute_sql("UPDATE tenant_visitor_floor_policy SET org_id=NULL WHERE id='gf_vstr_policy_guangfa_fund_001x'")
print(" UPDATE 广发基金 org_id=NULL (回滚)")
def build_noauth_headers():
return {"Content-Type": "application/json", "businessid": BUSINESS_ID}
def now_ms():
return int(time.time() * 1000)
def tomorrow_ms():
return int((time.time() + 86400) * 1000)
def call_add_visitor(base_url, person_id, visitor_id, floor_ids=None):
body = {
"personId": person_id, "visitorId": visitor_id,
"floorIds": floor_ids if floor_ids is not None else [],
"begVisitorTime": now_ms(), "endVisitorTime": tomorrow_ms(),
}
try:
r = requests.post(f"{base_url}/elevator/person/add/visitor", json=body, headers=build_noauth_headers(), timeout=30)
return {"http_status": r.status_code, "body": r.json() if r.headers.get("content-type","").startswith("application/json") else r.text}
except Exception as e:
return {"http_status": 0, "error": str(e)}
def call_passrule_image(base_url, visitor_id):
body = {"personId": visitor_id}
try:
r = requests.post(f"{base_url}/elevator/passRule/image", json=body, headers=build_noauth_headers(), timeout=30)
return {"http_status": r.status_code, "body": r.json() if r.headers.get("content-type","").startswith("application/json") else r.text}
except Exception as e:
return {"http_status": 0, "error": str(e)}
def extract_zone_ids(passrule_response):
try:
datas = passrule_response["body"]["data"]["datas"]
return [d["zoneId"] for d in datas if "zoneId" in d]
except (KeyError, TypeError):
return []
def run_case(base_url, case, skip_db=False):
cid = case["id"]
print(f"\n[{cid}] {case['name']}")
floor_ids = case.get("floor_ids_override")
pid = case.get("policy_id")
if pid and cid == "T3":
if skip_db:
print(" [SKIP-DB] 请 DBA 手动执行: DELETE FROM tenant_visitor_floor_policy WHERE id='policy_t1_1403'")
else:
execute_sql("DELETE FROM tenant_visitor_floor_policy WHERE id='policy_t1_1403'")
print(" [DB] 临时删除 policy_t1_1403")
result = {"id": cid, "name": case["name"]}
r = call_add_visitor(base_url, case["host_id"], case["visitor_id"], floor_ids)
body = r.get("body") if isinstance(r.get("body"), dict) else {}
result["add_visitor"] = {
"http_status": r.get("http_status"),
"success": body.get("success"),
"code": body.get("code"),
"message": body.get("message"),
"error": r.get("error"),
}
av = result["add_visitor"]
business_ok = av["http_status"] == 200 and str(av.get("code", "")) in OK_CODES
if case["expected_pass"]:
if business_ok:
pr = call_passrule_image(base_url, case["visitor_id"])
actual_zones = extract_zone_ids(pr)
result["passrule_image"] = {"zones": actual_zones}
expected = case.get("expected_floors")
if expected is not None:
match = set(actual_zones) == set(expected)
result["floor_match"] = match
result["passed"] = match
print(f" add/visitor OK, floors actual={actual_zones} expected={expected} match={match}")
else:
result["passed"] = True
print(f" add/visitor OK, floors={actual_zones}")
else:
result["passed"] = False
print(f" expected success but got code={av.get('code')} msg={av.get('message')}")
else:
expected_code = case.get("expected_code")
actual_code = str(av.get("code", ""))
result["passed"] = (not business_ok) and (actual_code == expected_code)
print(f" expected fail code={expected_code} actual={actual_code} passed={result['passed']}")
if cid == "T3":
if skip_db:
print(" [SKIP-DB] 请 DBA 手动恢复 policy_t1_1403 (见 test_data_prepare.sql)")
else:
execute_sql(
"INSERT INTO tenant_visitor_floor_policy "
"(id, org_id, business_id, policy_type, allow_zone_ids, building_id, enabled, policy_version, created_at, updated_at) "
"VALUES ('policy_t1_1403', %s, NULL, 'INTERSECT_ALLOWLIST', %s, NULL, 1, 1, UNIX_TIMESTAMP(NOW())*1000, UNIX_TIMESTAMP(NOW())*1000)",
(ORG_1403, f'["{ZONE_12F}"]'),
)
print(" [DB] 恢复 policy_t1_1403")
return result
def generate_report(results, base_url):
passed = sum(1 for r in results if r.get("passed"))
return {
"test": "org_id policy fix verification",
"timestamp": datetime.now().isoformat(),
"elevator_url": base_url,
"mode": "noauth-probe",
"business_id": BUSINESS_ID,
"summary": {"total": len(results), "passed": passed, "failed": len(results) - passed},
"results": results,
}
def main():
args = parse_args()
base = args.elevator_base_url.rstrip("/")
if not health_check(base):
print("FATAL: elevator not reachable")
sys.exit(1)
if not args.skip_db:
print("\n=== Phase 1: prepare ===")
prepare_test_data()
print(f"\n=== Phase 2: run {len(TEST_CASES)} cases ===")
results = [run_case(base, c, args.skip_db) for c in TEST_CASES]
if not args.skip_db:
print("\n=== Phase 3: cleanup ===")
cleanup_test_data()
report = generate_report(results, base)
report_path = f"report/org-policy-fix-verify-{datetime.now().strftime('%Y%m%d-%H%M%S')}.json"
os.makedirs("report", exist_ok=True)
with open(report_path, "w", encoding="utf-8") as f:
json.dump(report, f, indent=2, ensure_ascii=False)
print(f"\n=== Report: {report_path} ===")
print(f"Passed: {report['summary']['passed']}/{report['summary']['total']}")
for r in results:
print(f" {'OK' if r.get('passed') else 'FAIL'} [{r['id']}] {r['name']}")
sys.exit(0 if report["summary"]["failed"] == 0 else 1)
if __name__ == "__main__":
main()
@@ -0,0 +1 @@
939d6e8e108579f1fcfc05deb037b17eae6f7d88
@@ -0,0 +1,10 @@
artifact=cw-elevator-application-2.0.6.jar
bundle_dir_name=cw-elevator-application-V2.0.6.20260430
directory=/media/zebra/9e8fa357-7db6-4d70-88ed-d5de5a059a663/星河湾星中星/源码/maven-cw-elevator-application/releases/cw-elevator-application-V2.0.6.20260430
built_at=2026-04-30T07:57:09+08:00
java_home=/usr/lib/jvm/java-8-openjdk-amd64
java_version_line=openjdk version "1.8.0_482"
java_version_line=OpenJDK Runtime Environment (build 1.8.0_482-8u482-ga~us1-0ubuntu1~22.04-b08)
java_version_line=OpenJDK 64-Bit Server VM (build 25.482-b08, mixed mode)
git_rev=376f94edd602d3dfed1cbada6e08361ee7b08e73
git_branch=release/cw-elevator-v1-lib-min-risk
@@ -0,0 +1,75 @@
# cw-elevator-application v2.0.6 SQL与代码一致性审核记录
**审核目标**:确认发布规范涉及 SQL 脚本与当前代码逻辑一致,满足实施交付依据留档要求。
**审核范围**`tenant_visitor_floor_policy` 建表脚本、初始化脚本、访客派梯策略读取与求交流程。
**审核时间**`2026-04-29`
**审核人**`____________`
---
## 1. 审核过程
1. 审阅 SQL 脚本:
- `docs/sql/tenant_visitor_floor_policy.sql`
- `docs/sql/tenant_visitor_floor_policy_init_guangfa_fund.sql`
2. 审阅代码路径:
- `cw-elevator-application-service/.../PersonRuleServiceImpl#addVisitor`
- `cw-elevator-application-data/.../TenantVisitorFloorPolicyMapper.xml`
- `cw-elevator-application-data/.../TenantVisitorFloorPolicyDto`
3. 做场景对照:
- UC-01:调用方未传 `floorIds`
- UC-02:调用方已传 `floorIds`
- 策略缺失/无效 JSON/交集为空等异常分支
4. 形成一致性结论与风险提示,并纳入发布包。
---
## 2. 审核依据与结果
| 检查项 | SQL 依据 | 代码依据 | 结论 |
|------|----------|----------|------|
| 策略表字段是否齐全(business_id/policy_type/allow_zone_ids/building_id/enabled/policy_version | `tenant_visitor_floor_policy.sql` DDL 定义上述字段 | Mapper 查询并映射到 DTO 同名语义字段 | 一致 |
| 代码是否只读取“启用+租户默认+INTERSECT_ALLOWLIST”策略 | 初始化脚本使用 `policy_type='INTERSECT_ALLOWLIST'``building_id=NULL``enabled=1` | Mapper `WHERE enabled=1 AND policy_type='INTERSECT_ALLOWLIST' AND (building_id IS NULL OR building_id='')` | 一致 |
| allow_zone_ids 的数据格式是否匹配代码解析方式 | SQL 注释与初始化脚本均为 JSON 数组字符串(如 `["605560545117995008"]` | `parseAllowZoneIds` 使用 `JSON.parseArray(..., String.class)` 解析 | 一致 |
| 未传 floorIds 时是否执行“被访人楼层 ∩ 策略楼层” | 策略表提供 allowlist 数据来源 | `addVisitor``!callerProvidedFloors` 分支求交 | 一致 |
| 交集为空是否按规范失败 | 初始化脚本可构造交集为空场景 | `intersected.isEmpty()` 返回 `76260532` | 一致 |
| 已传 floorIds 是否跳过策略表 | SQL 与此分支无冲突 | `callerProvidedFloors=true` 时不进入策略读取分支 | 一致 |
---
## 3. 关键证据(摘录)
- 代码读取策略(启用、类型、租户默认)来自 `TenantVisitorFloorPolicyMapper.xml`
- 代码在 `PersonRuleServiceImpl#addVisitor` 中:
- `!callerProvidedFloors` 才读取被访人楼层与租户策略;
- `allow_zone_ids` 解析成功且非空才参与求交;
- 求交为空返回 `76260532`
- 调用方已传楼层时不走策略求交流程。
- 初始化脚本 `tenant_visitor_floor_policy_init_guangfa_fund.sql` 的字段取值与上述查询条件完全兼容。
---
## 4. 审核结论
**结论:通过。**
发布规范涉及的 SQL 脚本内容与当前代码逻辑一致,满足 v2.0.6 发布包“数据库脚本 + 功能升级说明 + 实施交付依据”要求。
---
## 5. 风险提示与建议
1. **唯一性治理风险(中)**
DDL 使用 `UNIQUE KEY (business_id, building_id)`,在 MySQL 下 `building_id=NULL` 可能存在多行;当前代码通过 `ORDER BY updated_at DESC, policy_version DESC LIMIT 1` 取最新一条,不阻断功能,但建议运维侧增加“每租户默认策略唯一”巡检。
2. **配置数据质量风险(中)**
`allow_zone_ids` 必须是电梯域 `zone_id` 字符串数组,若误填其它系统 UUID 会导致策略不生效或交集为空。
---
## 6. 签字确认
| 角色 | 姓名 | 日期 | 备注 |
|------|------|------|------|
| 审核人 | `____________` | `____` | `____` |
| 实施负责人 | `____________` | `____` | `____` |
| 甲方确认(可选) | `____________` | `____` | `____` |
@@ -0,0 +1,45 @@
# \u56FE\u7247\u524D\u7F00
cloudwalk.elevator.common.relativePrefix=/cwos-portal/portal/fileManager/imgByPath?path=
# \u6570\u636E\u5E93sharding\u914D\u7F6E
spring.shardingsphere.sharding.tables.IT_ACS_RECOG_RECORD.actual-data-nodes=ds0.IT_ACS_RECOG_RECORD_$->{2020..2030}
spring.shardingsphere.sharding.tables.IT_ACS_RECOG_RECORD.table-strategy.standard.sharding-column=RECOGNITION_TIME
spring.shardingsphere.sharding.tables.IT_ACS_RECOG_RECORD.table-strategy.standard.precise-algorithm-class-name=cn.cloudwalk.elevator.YearlyShardingAlgorithm
spring.shardingsphere.sharding.tables.IT_ACS_RECOG_RECORD.table-strategy.standard.range-algorithm-class-name=cn.cloudwalk.elevator.YearlyShardingAlgorithm
# \u7535\u68AF\u5F00\u95E8\u4E8B\u4EF6\u8868
spring.shardingsphere.sharding.tables.IT_ACS_ELEVATOR_RECORD.actual-data-nodes=ds0.IT_ACS_ELEVATOR_RECORD_$->{2020..2030}
spring.shardingsphere.sharding.tables.IT_ACS_ELEVATOR_RECORD.table-strategy.standard.sharding-column=RECOGNITION_TIME
spring.shardingsphere.sharding.tables.IT_ACS_ELEVATOR_RECORD.table-strategy.standard.precise-algorithm-class-name=cn.cloudwalk.elevator.YearlyShardingAlgorithm
spring.shardingsphere.sharding.tables.IT_ACS_ELEVATOR_RECORD.table-strategy.standard.range-algorithm-class-name=cn.cloudwalk.elevator.YearlyShardingAlgorithm
spring.shardingsphere.sharding.binding-tables=IT_ACS_ELEVATOR_RECORD,IT_ACS_RECOG_RECORD
# \u4EBA\u8138\u6293\u62CD\u8BC6\u522B\u9608\u503C
cloudwalk.access-control.common.device-atrr-map.ACS_FACE_REG_THRESHOLD=75
# \u4EBA\u8138\u6BD4\u5BF9\u67E5\u8BE2\u8FC7\u6EE4\u9608\u503C
cloudwalk.access-control.common.face-compare-THRESHOLD=80
# \u5B9A\u65F6\u4EFB\u52A1\u914D\u7F6E
cloudwalk.access-control.schedual.jobs.AcsRecordStatisticsByDayJob.name=AcsRecordStatisticsByDayJob
cloudwalk.access-control.schedual.jobs.AcsRecordStatisticsByDayJob.group=ACCESS-CONTROL_GROUP
cloudwalk.access-control.schedual.jobs.AcsRecordStatisticsByDayJob.executable-class=cn.cloudwalk.service.ninca.accesscontrol.common.job.executable.AcsRecordStatisticsByDayJob
cloudwalk.access-control.schedual.jobs.AcsRecordStatisticsByDayJob.description=AcsRecordStatisticsByDay job is starting.........
## \u6BCF\u5929\u51CC\u66680\u70B910\u5206\u6267\u884C
cloudwalk.access-control.schedual.jobs.AcsRecordStatisticsByDayJob.cron-expression=0 10 0 * * ?
cloudwalk.access-control.schedual.jobs.AcsRecordStatisticsByDayJob.priority=1
# \u5F00\u95E8\u8BB0\u5F55\u63A8\u9001\u5F00\u5173\uFF1Atrue-\u5F00\uFF1Bfalse-\u5173\u3002\u9ED8\u8BA4\u4E3A\u5173
cloudwalk.access-control.common.publish-opendoor-switch=false
# \u5F00\u95E8\u8BB0\u5F55\u63A8\u9001serviceCode
cloudwalk.access-control.common.publish-opendoor-service-code=access-control
# \u540E\u7AEF\u8BC6\u522B\u4E0B\u53D1\u5F00\u95E8\u6307\u4EE4\u76F8\u5173\u914D\u7F6E
# \u6293\u62CD\u65F6\u95F4\u5728\u591A\u4E45\u4E4B\u524D\u4E0D\u4E0B\u53D1\u5F00\u95E8\u6307\u4EE4\u3002\u9ED8\u8BA45\u5206\u949F\u3002\u5355\u4F4D\uFF1A\u6BEB\u79D2\u3002
cloudwalk.access-control.common.face-capture-time-expired-milliseconds=300000
# \u591A\u5C11\u6BEB\u79D2\u4EE5\u5185\u7684\u591A\u6761\u6293\u62CD\u8BB0\u5F55\u53EA\u4E0B\u53D1\u4E00\u6B21\u5F00\u95E8\u6307\u4EE4\u3002\u9ED8\u8BA43\u79D2\u3002\u5355\u4F4D\uFF1A\u6BEB\u79D2
cloudwalk.access-control.common.face-capture-interval-milliseconds=3000
# \u5F00\u95E8\u8BB0\u5F55\u8FC7\u671F\u65F6\u95F4\u3002\u591A\u5C11\u6BEB\u79D2\u4E4B\u540E\uFF0C\u672A\u4E0A\u62A5\u5F00\u95E8\u8BB0\u5F55\uFF0C\u5F00\u95E8\u8BB0\u5F55\u72B6\u6001\u66F4\u65B0\u4E3A\u5931\u8D25\u3002\u9ED8\u8BA410\u5206\u949F\u3002\u5355\u4F4D\uFF1A\u6BEB\u79D2
cloudwalk.access-control.common.face-capture-open-door-fail-milliseconds=600000
# \u95E8\u7981\u63A7\u5236\u5668\u7C7B\u578B\u96C6\u5408
cloudwalk.access-control.common.device-controller-array[0]=mqtt
# \u8BBE\u5907\u79CD\u7C7Bid\u96C6\u5408
cloudwalk.access-control.common.device-category-array[0]=4
cloudwalk.access-control.common.device-category-array[1]=5
cloudwalk.access-control.common.device-category-array[2]=7
cloudwalk.access-control.common.device-category-array[3]=2
cloudwalk.access-control.common.device-category-array[4]=8
cloudwalk.access-control.common.device-category-array[5]=11
@@ -0,0 +1,114 @@
# spring\u914D\u7F6E
spring.mvc.throw-exception-if-no-handler-found=true
spring.mvc.locale=zh_CN
# \u8D44\u6E90\u6587\u4EF6\u914D\u7F6E
spring.messages.basename=access-control
spring.messages.always-use-message-format=true
spring.messages.encoding=utf-8
# http\u914D\u7F6E
spring.http.multipart.max-file-size=200MB
spring.http.multipart.max-request-size=200MB
spring.http.encoding.force=true
spring.http.encoding.charset=UTF-8
spring.http.encoding.enabled=true
# \u65E5\u5FD7\u914D\u7F6E
logging.config=classpath:logs/logback.xml
logging.file=${spring.application.name}
logging.path=/data/cwos/cw-elevator-application-V1.0.0.20211103/logs
logging.level.root=info
logging.level.cn.cloudwalk=info
# mybatis\u914D\u7F6E
mybatis.mapper-locations=classpath*:cn/cloudwalk/elevator/**/*.xml
mybatis.config-location=classpath:mapper/mybatis-config.xml
# \u5E8F\u5217\u53F7\u914D\u7F6E
cloudwalk.serial.enable=true
cloudwalk.serial.serial-length=8
cloudwalk.serial.serial-type=redis
cloudwalk.serial.serial-redis-key=CLOUDWALK-ACS-SERIAL-KEY
# \u7F13\u5B58\u914D\u7F6E
cloudwalk.spring.cache.expires=CACHE_NAME_APPLICATIONIDS#21600,ACS_DeviceTypesCache#7200,ACS_DeviceTypeFeaturesCache#7200,ACS_DeviceAttrsCache#7200,ACS_RecordStatisticsCache#90000,ACS_AreaTreeCache#60
# \u5185\u90E8\u63A5\u53E3\u8C03\u7528\u5BA2\u6237\u7AEF\u53CA\u8D85\u65F6\u914D\u7F6E
feign.hystrix.enable=true
feign.httpclient.enable=false
feign.okhttp.enable=true
ribbon.http.client.enabled=false
ribbon.okhttp.enabled=true
ribbon.ReadTimeout=10000
ribbon.ConnectTimeout=10000
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=10000
# \u5065\u5EB7\u68C0\u67E5\u914D\u7F6E
management.health.redis.enabled=false
management.health.db.enabled=false
# \u6570\u636E\u8131\u654F\u914D\u7F6E
cloudwalk.datafield.enable=true
cloudwalk.datafield.securityKey=d4b2aabc97394a12a27fc3cca6cd9ba1
cloudwalk.datafield.encrypt=AES
# redis\u914D\u7F6E
spring.redis.host=redis_01.redis_ip
spring.redis.port=6379
spring.redis.password=1qaz!QAZ
spring.redis.database=5
spring.redis.timeout=0
spring.redis.pool.max-active=10
spring.redis.pool.max-idle=1
spring.redis.pool.max-wait=10
spring.redis.pool.min-idle=0
# \u6570\u636E\u5E93sharding\u914D\u7F6E
spring.shardingsphere.datasource.names=ds0
spring.shardingsphere.datasource.ds0.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds0.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds0.jdbc-url=jdbc:mysql://mysql_01.mysql_ip:3306/cw-elevator-application?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true
spring.shardingsphere.datasource.ds0.username=cloudwalk
spring.shardingsphere.datasource.ds0.password=1qaz!QAZ
spring.shardingsphere.datasource.ds0.connection-timeout=60000
spring.shardingsphere.datasource.ds0.maximum-pool-size=20
spring.shardingsphere.datasource.ds0.minimum-idle=5
spring.shardingsphere.datasource.ds0.max-lifetime=1765000
spring.shardingsphere.datasource.ds0.auto-commit=true
spring.shardingsphere.datasource.ds0.pool-name=ds0-pool
spring.shardingsphere.props.sql.show=false
spring.shardingsphere.sharding.default-data-source-name=ds0
# \u5FAE\u670D\u52A1\u670D\u52A1\u540D\u914D\u7F6E
feign.device.name=cwos-portal
feign.resource.name=cwos-portal
feign.cwos-portal.name=cwos-portal
feign.davinci-portal.name=cwos-portal
feign.ninca-crk-std.name=ninca-crk-std
feign.component-organization.name=ninca-common-component-organization
feign.ninca-common.name=ninca-common
feign.mqtt.name=cloudwalk-device-thirdparty
# CWOS\u4E8B\u4EF6\u914D\u7F6E
cloudwalk.event.bootstrap-servers=371bfca4972c43d2aefcf302d0a4a277:9092,44700995ee904679a7ad5afddcf93bb5:9092,0837a70b5fab47569391828f5feb2561:9092
cloudwalk.event.group-id=cw-elevator-application-1
cloudwalk.event.handler-executor-config.core-pool-size=10
cloudwalk.event.handler-executor-config.maximum-pool-size=30
# \u5206\u5E03\u5F0F\u9501\u914D\u7F6E
intelligent.lock.enable=true
intelligent.lock.config.default-wait-time=10000
lockWatchdogTimeout=21000
# PERSON_NAME_SPACE
person.name.space=recordEvent
elevator.application.key=xinghewan
elevator.application.time=600
elevator.application.keyA=5B7DEF88FF04
ninca-crk-std.ip=10.0.22.102:16106
#发送第三方数据ip
sendRecord.ip=hrec.star-river.com:32165
#sendRecord.ip=172.16.16.56:32165
sendRecord.token.corpId=53db867a8bb747a1bd04dd1afcad8ca6
sendRecord.token.appKey=293e2d708f0143c2957b702cef44d951
sendRecord.token.appSecret=5f6995009b864669b52041b8f5dc4625
#是否推送
sendRecord.boolean=true
# 设备处理线程池配置
ninca.update.floor.pool.corePoolSize=5
ninca.update.floor.pool.maxPoolSize=5
ninca.update.floor.pool.queueCapacity=100000
ninca.update.floor.pool.keepAliveSeconds=150
ninca.update.floor.pool.allowCoreThreadTimeOut=true
#楼栋id
floor.building.id=605560539791228928
# 启动阶段打印关键配置来源(用于生产复现定位)
# 诊断探针已改为代码固定启用,见 cn.cloudwalk.elevator.debug.ElevatorProbeConstants(勿再使用 elevator.config.probe.enabled
@@ -0,0 +1,18 @@
server.port=16112
server.tomcat.uri-encoding=UTF-8
spring.application.name=elevator-app
spring.profiles.active=access-control
# consul\u914D\u7F6E
spring.cloud.consul.host=371bfca4972c43d2aefcf302d0a4a277
spring.cloud.consul.port=8500
spring.cloud.consul.enabled=true
spring.cloud.consul.discovery.register=true
spring.cloud.consul.discovery.enabled=false
spring.cloud.consul.discovery.prefer-ip-address=true
spring.cloud.consul.discovery.instance-id=${spring.application.name}-${spring.cloud.client.ipAddress}:${server.port}
spring.cloud.consul.discovery.ip-address=${spring.cloud.client.ipAddress}
spring.cloud.consul.discovery.deregister=false
# zookeeper\u914D\u7F6E
dubbo.registry.address=zookeeper://10.0.22.207:2181
dubbo.protocol.port=16107
dubbo.provider.version=1.0
@@ -0,0 +1,149 @@
#!/usr/bin/env bash
set -euo pipefail
# 生产只读证据采集(Maven 发布包内与本脚本同置于部署根目录,与 start.sh / properties 同层):
# - 进程参数/环境/工作目录
# - 本地配置文件
# - jcmd system properties
# - 应用日志关键片段
# - Consul 健康与 KV 快照
# 最终输出 tar.gz,便于离线定位“配置来源 -> Ribbon 实例列表”问题。
APP_DIR="${1:-/data/cwos/cw-elevator-application-V1.0.0.20211103}"
CONSUL_ADDR="${2:-10.0.22.102:8500}"
OUT_ROOT="${3:-${APP_DIR}/evidence}"
APP_NAME="${4:-elevator-app}"
# 现场 JDK(生产 cwos-node 固定路径;不依赖 PATH
CWOS_JAVA_BIN="/data/cwos/java/bin"
JAVA_BIN="${CWOS_JAVA_BIN}/java"
JAR_BIN="${CWOS_JAVA_BIN}/jar"
JCMD_BIN="${CWOS_JAVA_BIN}/jcmd"
DATE_BIN="/bin/date"
timestamp="$(${DATE_BIN} +%Y%m%d-%H%M%S)"
OUT_DIR="${OUT_ROOT}/elevator-evidence-${timestamp}"
mkdir -p "${OUT_DIR}"
log() { echo "[collect] $*"; }
log "APP_DIR=${APP_DIR}"
log "CONSUL_ADDR=${CONSUL_ADDR}"
log "OUT_DIR=${OUT_DIR}"
log "JAVA_BIN=${JAVA_BIN} JAR_BIN=${JAR_BIN} JCMD_BIN=${JCMD_BIN}"
PID="$(ps -ef | awk '/java/ && /cw-elevator-application/ && !/awk/ {print $2; exit}')"
if [[ -z "${PID}" ]]; then
echo "ERROR: 未找到 cw-elevator-application Java 进程" >&2
exit 1
fi
log "PID=${PID}"
echo "${PID}" > "${OUT_DIR}/pid.txt"
# 1) 进程与系统基础信息
ps -ef > "${OUT_DIR}/ps-ef.txt"
uname -a > "${OUT_DIR}/uname.txt"
${DATE_BIN} +%Y-%m-%dT%H:%M:%S%z > "${OUT_DIR}/collected-at.txt"
tr '\0' ' ' < "/proc/${PID}/cmdline" > "${OUT_DIR}/proc-cmdline.txt" || true
tr '\0' '\n' < "/proc/${PID}/environ" > "${OUT_DIR}/proc-environ.txt" || true
ls -l "/proc/${PID}/cwd" > "${OUT_DIR}/proc-cwd.txt" || true
# 2) 本地配置快照(若存在)
for f in bootstrap.properties application.properties application-access-control.properties start.sh stop.sh cw-elevator-application.service; do
if [[ -f "${APP_DIR}/${f}" ]]; then
cp -a "${APP_DIR}/${f}" "${OUT_DIR}/${f}"
fi
done
# 3) JAR 与结构快照
JAR_PATH="$(awk '{print $1}' "${OUT_DIR}/proc-cmdline.txt" | sed 's/[[:space:]]*$//')"
if [[ -f "${APP_DIR}/cw-elevator-application-V1.0.0.20211103.jar" ]]; then
JAR_PATH="${APP_DIR}/cw-elevator-application-V1.0.0.20211103.jar"
fi
echo "${JAR_PATH}" > "${OUT_DIR}/jar-path.txt"
if [[ -f "${JAR_PATH}" ]]; then
sha256sum "${JAR_PATH}" > "${OUT_DIR}/jar.sha256.txt" || true
if [[ -x "${JAR_BIN}" ]]; then
"${JAR_BIN}" tf "${JAR_PATH}" > "${OUT_DIR}/jar-tf.txt" || true
else
echo "jar not found or not executable: ${JAR_BIN}" > "${OUT_DIR}/jar-tf.txt"
fi
unzip -p "${JAR_PATH}" application.properties > "${OUT_DIR}/jar-application.properties.txt" 2>/dev/null || true
unzip -p "${JAR_PATH}" bootstrap.properties > "${OUT_DIR}/jar-bootstrap.properties.txt" 2>/dev/null || true
fi
# 4) jcmd system properties + attach 诊断(不修改应用配置;便于修复 AttachNotSupportedException
{
echo "=== current shell user ==="
id 2>/dev/null || true
echo "=== target java process ==="
ps -o user=,group=,pid=,args= -p "${PID}" 2>/dev/null || true
PROC_USER="$(stat -c '%U' "/proc/${PID}" 2>/dev/null || echo "")"
PROC_UID="$(stat -c '%u' "/proc/${PID}" 2>/dev/null || echo "")"
echo "proc_owner=${PROC_USER} uid=${PROC_UID}"
echo "=== /tmp hsperfdata (HotSpot perf counter; attach 相关) ==="
if [[ -n "${PROC_USER}" && "${PROC_USER}" != "unknown" ]]; then
HS="/tmp/hsperfdata_${PROC_USER}"
if [[ -d "${HS}" ]]; then
ls -la "${HS}" 2>/dev/null | head -30 || true
ls -la "${HS}/${PID}" 2>/dev/null || echo "missing ${HS}/${PID}"
else
echo "no directory ${HS}"
fi
fi
echo "=== cmdline tokens (attach / jdwp) ==="
tr '\0' '\n' < "/proc/${PID}/cmdline" 2>/dev/null | grep -E 'DisableAttach|Attach|jdwp|agentpath' || echo "(none matched)"
} > "${OUT_DIR}/jcmd-attach-diagnose.txt" 2>&1
if [[ -x "${JCMD_BIN}" ]]; then
"${JCMD_BIN}" "${PID}" VM.system_properties > "${OUT_DIR}/jcmd-system-properties.txt" 2>&1 || true
if grep -q 'AttachNotSupportedException\|Unable to open socket file' "${OUT_DIR}/jcmd-system-properties.txt" 2>/dev/null; then
{
echo ""
echo "HINT: jcmd attach 失败常见原因:"
echo " 1) 与 Java 进程不同用户执行 jcmd(请用与进程相同用户,例如: sudo -u <java_user> ${JCMD_BIN} ${PID} VM.system_properties"
echo " 2) /tmp/hsperfdata_<user>/<pid> 缺失或权限异常"
echo " 3) JVM 启动参数含 -XX:+DisableAttachMechanism(见 jcmd-attach-diagnose.txt 中 cmdline"
echo " 4) 进程非 HotSpot 或尚未完全初始化(极少见于长期运行的 Spring Boot"
} >> "${OUT_DIR}/jcmd-system-properties.txt"
fi
else
echo "jcmd not found or not executable: ${JCMD_BIN}" > "${OUT_DIR}/jcmd-system-properties.txt"
fi
# 4b) java 版本(与现场 JDK 一致性的旁证)
if [[ -x "${JAVA_BIN}" ]]; then
"${JAVA_BIN}" -version > "${OUT_DIR}/java-version.txt" 2>&1 || true
else
echo "java not found or not executable: ${JAVA_BIN}" > "${OUT_DIR}/java-version.txt"
fi
# 5) 应用日志关键行
LOG_FILE="${APP_DIR}/logs/elevator-app.log"
if [[ -f "${LOG_FILE}" ]]; then
cp -a "${LOG_FILE}" "${OUT_DIR}/elevator-app.log.full"
awk '
/CONFIG SOURCE PROBE START|CONFIG SOURCE PROBE END|probe key=|ConfigurationBasedServerList|Load balancer does not have available server|DynamicServerListLoadBalancer|ConsulServiceRegistry|Registering service with consul/ { print }
' "${LOG_FILE}" > "${OUT_DIR}/elevator-app.log.keylines.txt"
fi
# 6) Consul 快照
CURL="curl -sS --max-time 8"
${CURL} "http://${CONSUL_ADDR}/v1/health/service/${APP_NAME}?passing=true" > "${OUT_DIR}/consul-health-${APP_NAME}.json" || true
for svc in cwos-portal ninca-common ninca-common-component-organization ninca-crk-std cloudwalk-device-thirdparty; do
${CURL} "http://${CONSUL_ADDR}/v1/health/service/${svc}?passing=true" > "${OUT_DIR}/consul-health-${svc}.json" || true
done
${CURL} "http://${CONSUL_ADDR}/v1/kv/config/${APP_NAME}/data?raw" > "${OUT_DIR}/consul-kv-${APP_NAME}.properties" || true
${CURL} "http://${CONSUL_ADDR}/v1/kv/config/${APP_NAME},access-control/data?raw" > "${OUT_DIR}/consul-kv-${APP_NAME},access-control.properties" || true
# 7) 现场可达性快照(已知主机名)
for host in 0837a70b5fab47569391828f5feb2561 371bfca4972c43d2aefcf302d0a4a277 44700995ee904679a7ad5afddcf93bb5; do
getent hosts "${host}" > "${OUT_DIR}/getent-${host}.txt" 2>&1 || true
curl -I --max-time 5 "http://${host}:8089/" > "${OUT_DIR}/curl-head-${host}-8089.txt" 2>&1 || true
done
ARCHIVE="${OUT_DIR}.tar.gz"
tar -czf "${ARCHIVE}" -C "$(dirname "${OUT_DIR}")" "$(basename "${OUT_DIR}")"
log "DONE archive=${ARCHIVE}"
echo "${ARCHIVE}"
@@ -0,0 +1,38 @@
#!/usr/bin/env bash
# shellcheck shell=bash
# 由 v1-legacy/run.sh、v2-maven/run.sh sourceJAVA_HOME;非 JDK8 时追加 --add-opens。
#
# === 本机 JDK 8 安装根目录(含 bin/java);换机器只需改下行默认路径或通过环境变量覆盖 ===
: "${DEPLOY_JDK8:=/usr/lib/jvm/java-8-openjdk-amd64}"
_pick_java_home() {
if [[ "${ELEVATOR_USE_ENV_JAVA:-0}" == "1" ]] && [[ -n "${JAVA_HOME:-}" && -x "${JAVA_HOME}/bin/java" ]]; then
return 0
fi
if [[ -x "${DEPLOY_JDK8}/bin/java" ]]; then
export JAVA_HOME="${DEPLOY_JDK8}"
return 0
fi
for d in /usr/lib/jvm/java-8-openjdk-amd64 /usr/lib/jvm/java-1.8.0-openjdk; do
if [[ -x "$d/bin/java" ]]; then
export JAVA_HOME="$d"
return 0
fi
done
if [[ -n "${JAVA_HOME:-}" && -x "${JAVA_HOME}/bin/java" ]]; then
return 0
fi
export JAVA_HOME="${JAVA_HOME:-${DEPLOY_JDK8}}"
}
_jdk8_open_flags() {
local java="$1"
if "$java" -version 2>&1 | grep -qE 'version "1\.8\.'; then
echo ""
return
fi
echo "--add-opens=java.base/java.lang=ALL-UNNAMED"
echo "--add-opens=java.base/java.lang.reflect=ALL-UNNAMED"
echo "--add-opens=java.base/java.util=ALL-UNNAMED"
echo "--add-opens=java.base/java.io=ALL-UNNAMED"
}
@@ -0,0 +1,23 @@
[Unit]
Description=cw-elevator-application 2.0.6 (Maven V2 fat jar)
After=network.target remote-fs.target nss-lookup.target
[Service]
# 将 WorkingDirectory、ExecStart、ExecStop 中的路径占位改为实际部署绝对路径(与 JAR、start.sh、properties 同目录)。
PIDFile=/run/cw-elevator-application-2.0.6.pid
ExecStartPre=/bin/rm -f /run/cw-elevator-application-2.0.6.pid
ExecStart=/bin/bash /path/to/cw-elevator-application/start.sh
ExecStop=/bin/bash /path/to/cw-elevator-application/stop.sh
ExecReload=/bin/kill -s HUP $MAINPID
KillSignal=SIGQUIT
TimeoutStopSec=5
KillMode=process
PrivateTmp=true
Restart=always
RestartSec=10
StandardOutput=null
StandardError=null
WorkingDirectory=/path/to/cw-elevator-application
[Install]
WantedBy=multi-user.target
@@ -0,0 +1,27 @@
-- 租户访客默认楼层策略(电梯应用库)
-- 设计说明:docs/business/租户访客默认楼层-数据库配置阶段技术设计.md
CREATE TABLE IF NOT EXISTS tenant_visitor_floor_policy (
id VARCHAR(32) NOT NULL COMMENT '主键',
business_id VARCHAR(64) NOT NULL COMMENT '机构/租户 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_biz_building (business_id, building_id),
KEY idx_business_enabled (business_id, enabled)
) 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);
@@ -0,0 +1,47 @@
-- 广发基金租户:访客默认楼层策略初始化(电梯库 cw-elevator-application
--
-- 请先执行同目录 tenant_visitor_floor_policy.sql 建表(或 releases/v2.0.0/ddl 同源 DDL)。
--
-- 字段说明:allow_zone_ids 为 JSON 数组字符串,元素须与组织/派梯侧 floorList、image_rule_ref 使用的
-- zone_id(电梯库 snowflake 形态)一致,勿使用 cw_is_area 的 UUID。
--
-- 数据来源(现场查询 192.168.3.12:3307,截至脚本编写日):
-- business_idcomponent-organization.cw_is_organization
-- NAME='[28-38F]广发基金管理有限公司' -> BUSINESS_ID = 2524639890ba4f2cba9ba1a4eeaa4015
-- 28F zone_idcw-elevator-application.code_elevator_area / image_rule_ref
-- zone_id = 605560545117995008zone_name=28Fcode=0x1C
--
-- 重复执行:依赖主键 id 固定,使用 ON DUPLICATE KEY UPDATE 刷新策略字段。
SET NAMES utf8mb4;
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 (
'gf_vstr_policy_guangfa_fund_001x',
'2524639890ba4f2cba9ba1a4eeaa4015',
'INTERSECT_ALLOWLIST',
'["605560545117995008"]',
NULL,
1,
1,
'广发基金:访客与 floorList 求交后仅保留 allowlist(默认仅 28F zone)。业务配置见产品方案。',
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,38 @@
#!/usr/bin/env bash
# 与当前目录下 application.properties 同路径启动 V2maven 构建)包。
# 默认优先系统 JDK 8;若只有 JDK11+ 会自动附加 --add-opens。
# ELEVATOR_USE_ENV_JAVA=1 ./run.sh 使用当前 JAVA_HOME(如 Conda)。
# 发布包内脚本与 JAR、properties 均位于发布根目录(与 cw-elevator-application-V1.0.0.20211103 布局一致)。
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=../common-java.sh
source "${SCRIPT_DIR}/../common-java.sh"
cd "$SCRIPT_DIR"
JAR=""
for candidate in $(ls -1t cw-elevator-application-*.jar 2>/dev/null || true); do
if [[ "${candidate}" == *.jar.original ]]; then
continue
fi
JAR="${candidate}"
break
done
if [[ -z "${JAR}" || ! -f "${JAR}" ]]; then
echo "缺少 cw-elevator-application-*.jar,请在 deploy 目录执行: ./sync-jars.sh" >&2
exit 1
fi
_pick_java_home
if [[ ! -x "${JAVA_HOME}/bin/java" ]]; then
echo "ERROR: 未找到可执行的 JDK。请安装 openjdk-8-jdk,或设定 JAVA_HOME / ELEVATOR_USE_ENV_JAVA=1 ./run.sh(使用 Conda 等当前环境)。" >&2
exit 1
fi
JAVA="${JAVA_HOME}/bin/java"
OPEN_FLAGS=()
while IFS= read -r line; do
[[ -n "$line" ]] && OPEN_FLAGS+=("$line")
done < <(_jdk8_open_flags "$JAVA")
# 强制走 Consul:不再注入本地 application*.properties / redis-override.properties。
# shellcheck disable=SC2086
exec "$JAVA" "${OPEN_FLAGS[@]}" ${ELEVATOR_JAVA_OPTS:-} -jar "$JAR"
@@ -0,0 +1,23 @@
#!/usr/bin/env bash
# 与「星中心」V1 部署习惯对齐:本脚本与 cw-elevator-application-2.0.6.jar、bootstrap/application*.properties 位于同一目录。
# 覆盖 JVM:设置环境变量 ELEVATOR_JAVA_OPTS(可选);指定 JavaJAVA_HOME 或 JAVA_CMD。
set -euo pipefail
dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
cd "$dir"
JAR="cw-elevator-application-2.0.6.jar"
if [[ ! -f "$JAR" ]]; then
echo "ERROR: 未找到 ${JAR}(当前目录 $(pwd)" >&2
exit 1
fi
if [[ -n "${JAVA_CMD:-}" ]]; then
JAVA_EXEC="$JAVA_CMD"
elif [[ -n "${JAVA_HOME:-}" && -x "${JAVA_HOME}/bin/java" ]]; then
JAVA_EXEC="${JAVA_HOME}/bin/java"
else
JAVA_EXEC="/usr/lib/jvm/java-8-openjdk-amd64/bin/java"
fi
if [[ -z "${ELEVATOR_JAVA_OPTS:-}" ]]; then
ELEVATOR_JAVA_OPTS="-Xmx3072m -Xms3072m -Xmn1024m"
fi
# shellcheck disable=SC2086
exec "$JAVA_EXEC" $ELEVATOR_JAVA_OPTS -jar "$JAR"
@@ -0,0 +1,9 @@
#!/usr/bin/env bash
# 停止与本目录 cw-elevator-application-2.0.6.jar 对应的 Java 进程(仅匹配命令行中含该 JAR 名的进程)。
set -euo pipefail
JAR="cw-elevator-application-2.0.6.jar"
# shellcheck disable=SC2009
pid=$(ps -ef | grep '[j]ava' | grep "$JAR" | awk '{print $2}' || true)
if [[ -n "${pid}" ]]; then
kill -9 $pid
fi
@@ -0,0 +1,106 @@
# cw-elevator-application v2.0.6 升级计划
**项目名称**(可填):智慧电梯 / 访客派梯系统升级
**版本**v2.0.6
**计划性质**:执行级排期草案,**具体日期、起止时刻以与甲方书面/邮件确认为准**。
---
## 1. 升级目标
完成电梯派梯应用 **v2.0.6** 上线,启用**租户访客固定访问楼层**能力所需的应用与数据库对象;按业务需要为指定租户配置策略并完成验收。
---
## 2. 计划时间窗口(夜间 · 暂定周二、周三)
| 窗口 | 意向安排 | 建议内容(可合并或调整) |
|------|-----------|----------------------------|
| **第一次:周二晚间** | 夜间低峰段,具体 **HH:MMHH:MM** 待定 | 环境检查、数据库备份、执行 **DDL**(新建策略表)、(可选)预发布验证、必要时准备回滚包与检查单 |
| **第二次:周三晚间** | 夜间低峰段,具体 **HH:MMHH:MM** 待定 | 部署新版本 **JAR**、滚动/重启应用、按需 **INSERT** 租户策略数据、业务联调与验收、监控与值守 |
**说明**
- 若贵方现场要求**一个晚上完成全部步骤**,可将 DDL 与部署**合并在同一晚间窗口**执行,本表仅体现「周二、周三两晚」的**当前意向拆分**,最终以确认单为准。
- 两次窗口建议**间隔至少数小时至一个工作日**,便于第一次变更后观察库表与低风险项;若合并为单次窗口,须在计划单中注明**连续操作顺序与回滚点**。
**待确认栏**(实施时填写):
- 周二实施日期:`____年____月____日`,时段: `____ : ____` `____ : ____`
- 周三实施日期:`____年____月____日`,时段: `____ : ____` `____ : ____`
- 甲方现场联系人: `____________` 乙方/实施负责人: `____________`
---
## 3. 影响范围与沟通
| 项目 | 说明 |
|------|------|
| 影响系统 | 电梯派梯相关应用(`cw-elevator-application`)及同一业务库。 |
| 用户感知 | 应用重启期间可能出现短时派梯接口失败;策略误配可能导致部分访客路径失败,需按验收清单核对。 |
| 通知范围 | 建议提前通知:物业/客服、前台与访客登记、安保与梯控相关值班(按项目实际 roster 确定)。 |
---
## 4. 前置条件(升级前)
- [ ] 已与甲方确认 **周二 / 周三** 夜间窗口。
- [ ] 取得 **v2.0.6** 发布包(含 `jar``ddl/tenant_visitor_floor_policy.sql`、说明书)。
- [ ] 目标环境 **JDK 版本**符合实施方要求(与构建说明一致,一般为 JDK 8)。
- [ ] 数据库已做**备份**(全库或按运维规范),并可从发布包定位 DDL。
- [ ] 明确需启用「固定访客楼层」的租户列表及**允许区域**配置(若不启用,可跳过策略数据录入,行为与升级前一致)。
- [ ] 回滚包:保留**当前线上 JAR** 备份与回滚步骤(见下文)。
---
## 5. 实施步骤(建议顺序)
### 5.1 周二晚间(或首个窗口)
1. 备份数据库。
2. 执行 `tenant_visitor_floor_policy.sql``CREATE TABLE IF NOT EXISTS`,可重复执行需与 DBA 确认)。
3. (可选)在测试/预发环境先执行一遍并验证。
4. 记录执行人、时间与结果。
### 5.2 周三晚间(或第二个窗口 / 同晚续作)
1. 停止或滚动发布应用(按现网规范)。
2. 替换为 `cw-elevator-application-2.0.6.jar` 并启动。
3. 按业务需求对需启用的租户执行策略 **INSERT**`enabled=1``allow_zone_ids` 等为合法 JSON 等,字段级以技术说明书为准)。
4. 抽样验证:未传显式楼层时的访客派梯、租户策略开/关、与组织楼层无交集时的失败提示等。
5. 观察监控与日志,**值守**至约定结束时间。
*若合并为单次窗口:按「备份 → DDL → 部署 JAR → 策略数据 → 验收」顺序连续执行,并预留回滚决策时间。*
---
## 6. 验收要点(摘要)
- 未配置或未启用策略时:与升级前行为一致。
- 已启用策略且业务走「未显式指定楼层」路径:访客可去楼层为**组织允许**与**租户允许**的**交集**。
- 交集为空时:接口返回预期业务错误(技术码见实施方说明书)。
- 第三方已显式传入楼层的路径:不因本策略表改变原逻辑。
---
## 7. 回滚预案
| 场景 | 建议动作 |
|------|-----------|
| 应用异常 | 回退至上一版本 JAR;数据库新表若已存在且旧应用不读该表,通常可继续服务;与实施方确认。 |
| 策略配置错误 | 优先**停用或修正策略行**,避免大规模回滚应用。 |
| 必须撤表 | 仅在评估无依赖后由 DBA 执行删表;**高风险**,需书面确认。 |
---
## 8. 交付物核对
- [ ] `cw-elevator-application-2.0.6.jar`
- [ ] `ddl/tenant_visitor_floor_policy.sql`
- [ ] 《版本升级说明书》(实施/技术)
- [ ] 本《甲方版本升级说明》(如已作为对甲交付)
- [ ] 本《升级计划》(双方确认签字/邮件留档)
---
**文档状态**:草案;**时间点为暂定周二、周三晚间**,正式实施前请更新「待确认栏」并留存确认记录。
@@ -0,0 +1,32 @@
# cw-elevator-application v2.0.6 发布索引
本版本**功能升级说明**已单独成文,**仅覆盖「租户访客固定访问楼层」**及配套数据库 DDL,见:
**[cw-elevator-application-v2.0.6-版本升级说明书](./cw-elevator-application-v2.0.6-版本升级说明书.md)**
**对甲交付(业务表述与排期)**
- [甲方版本升级说明](./cw-elevator-application-v2.0.6-甲方版本升级说明.md)
- [升级计划(含夜间 · 周二/周三窗口)](./cw-elevator-application-v2.0.6-升级计划.md)
- [实施交付清单(实施方内部与对甲交付核对)](./cw-elevator-application-v2.0.6-实施交付清单.md)
- [实施验收记录模板(上线当晚记录)](./cw-elevator-application-v2.0.6-实施验收记录模板.md)
- [SQL与代码一致性审核记录(发布规范走查依据)](./cw-elevator-application-v2.0.6-SQL与代码一致性审核记录.md)
---
## 一键构建与发布目录
在仓库根目录、**JDK 8** 下执行:
```bash
export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64
./scripts/release-cw-elevator-application.sh 2.0.6
```
输出:**`maven-cw-elevator-application/releases/cw-elevator-application-V2.0.6.<YYYYMMDD>/`**(目录名含构建日期,与 `cw-elevator-application-V1.0.0.20211103` 命名风格一致;可用环境变量 **`RELEASE_DATE_LABEL`** 指定日期),含 `cw-elevator-application-2.0.6.jar``ddl/`、发布根目录下的 `bootstrap.properties` / `application*.properties`(与星中心/V1 同层摆放,**无额外 `config/` 子目录**)、`start.sh` / `stop.sh` / `cw-elevator-application.service`(路径占位符需现场替换)、`run.sh``common-java.sh`、**`collect_elevator_runtime_evidence.sh`**(根目录,现场只读证据采集)、`版本升级说明书.md`、甲方与实施类 Markdown、`BUILD_MANIFEST.txt`;默认另生成 **`releases/cw-elevator-application-V2.0.6.<YYYYMMDD>.zip`**(设 `RELEASE_MAKE_ZIP=0` 可跳过)。
---
## Git 与大文件
`maven-cw-elevator-application/.gitignore` 忽略 `**releases/**/*.jar**`;**DDL 与说明书**可提交;可执行 JAR 请通过制品库或制品服务器分发。
@@ -0,0 +1,60 @@
# cw-elevator-application v2.0.6 实施交付清单
**用途**:用于正式发布交付前后的材料核对,满足“发布包 + 数据库脚本 + 升级说明 + 实施验收记录”闭环。
---
## 1. 发布包文件清单
- [ ] `cw-elevator-application-2.0.6.jar`
- [ ] `ddl/tenant_visitor_floor_policy.sql`(建表脚本)
- [ ] `ddl/tenant_visitor_floor_policy_init_guangfa_fund.sql`(广发基金初始化示例/可直接执行)
- [ ] `版本升级说明书.md`(技术实施口径)
- [ ] `甲方版本升级说明.md`(业务口径)
- [ ] `升级计划.md`(实施窗口与回滚安排)
- [ ] `实施验收记录模板.md`(上线当晚记录)
- [ ] `BUILD_MANIFEST.txt`(构建时间、分支、提交号)
---
## 2. 数据库变更交付项
- [ ] DBA 已确认目标库与执行窗口
- [ ] 已备份目标库(全库/指定库按现场规范)
- [ ] 已执行 `tenant_visitor_floor_policy.sql`
- [ ] 已执行或评审 `tenant_visitor_floor_policy_init_guangfa_fund.sql`
- [ ] 已确认 `allow_zone_ids` 使用的是电梯库 `zone_id`(非其它系统 UUID
- [ ] 已留存 SQL 执行记录(执行人、时间、结果)
---
## 3. 应用部署交付项
- [ ] 上线前已保存旧版 JAR 回滚包
- [ ] 已按窗口替换为 `cw-elevator-application-2.0.6.jar`
- [ ] 应用启动日志无严重报错
- [ ] 关键接口健康检查通过(含 `add/visitor` 主链路)
- [ ] 监控观察窗口内无持续异常
---
## 4. 验收交付项
- [ ] UC-01:未传 `floorIds` 且策略启用时,楼层为 `floorList ∩ allow_zone_ids`
- [ ] UC-01:无交集时返回 `76260532`
- [ ] UC-02:显式传 `floorIds` 时不受策略表影响
- [ ] 未启用策略租户行为与升级前一致
- [ ] 已按《实施验收记录模板》完成签字/邮件留档
---
## 5. 交付确认信息
| 项目 | 内容 |
|------|------|
| 实施项目 | cw-elevator-application v2.0.6 |
| 实施日期 | `____年____月____日` |
| 甲方确认人 | `____________` |
| 乙方实施人 | `____________` |
| 文档留存路径 | `____________` |
@@ -0,0 +1,73 @@
# cw-elevator-application v2.0.6 实施验收记录模板
**用途**:实施当晚记录数据库执行、应用部署、业务验收和回滚判定,作为交付归档依据。
---
## 1. 基本信息
| 项目 | 内容 |
|------|------|
| 项目名称 | 智慧电梯 / 访客派梯系统 |
| 发布版本 | v2.0.6 |
| 实施日期 | `____年____月____日` |
| 实施时段 | `____:____` - `____:____` |
| 环境 | 生产 / 预生产(圈选) |
| 实施负责人 | `____________` |
| 甲方联系人 | `____________` |
---
## 2. 数据库执行记录
| 序号 | 脚本 | 执行时间 | 执行人 | 结果 | 备注 |
|------|------|----------|--------|------|------|
| 1 | tenant_visitor_floor_policy.sql | `____` | `____` | 成功/失败 | `____` |
| 2 | tenant_visitor_floor_policy_init_guangfa_fund.sql | `____` | `____` | 成功/失败 | `____` |
**异常记录**`______________________________________________`
---
## 3. 应用发布记录
| 项目 | 记录 |
|------|------|
| 上线前版本 | `____________` |
| 上线后版本 | `cw-elevator-application-2.0.6.jar` |
| 启停方式 | `____________` |
| 服务恢复时间 | `____________` |
| 日志健康检查 | 通过 / 不通过 |
| 监控观察结论 | 正常 / 异常(说明) |
---
## 4. 验收结果记录
| 用例 | 期望 | 实际 | 结论 |
|------|------|------|------|
| UC-01(未传 floorIds,策略启用) | floorList 与 allow_zone_ids 求交 | `____` | 通过/不通过 |
| UC-01(交集为空) | 返回 `76260532` | `____` | 通过/不通过 |
| UC-02(显式传 floorIds | 不受策略表影响 | `____` | 通过/不通过 |
| 无策略租户回归 | 行为与升级前一致 | `____` | 通过/不通过 |
---
## 5. 回滚判定
- [ ] 无需回滚,发布成功
- [ ] 需要回滚应用(原因:`________________`
- [ ] 需要回滚数据/策略(原因:`________________`
回滚执行记录(如发生):`______________________________________________`
---
## 6. 签字确认
| 角色 | 姓名 | 日期 | 备注 |
|------|------|------|------|
| 甲方确认 | `____________` | `____` | `____` |
| 乙方实施 | `____________` | `____` | `____` |
| 乙方复核 | `____________` | `____` | `____` |
@@ -0,0 +1,112 @@
# cw-elevator-application v2.0.6 版本升级说明书
**适用范围**:本说明**仅**描述 **v2.0.6** 中与 **租户访客固定访问楼层**(租户级允许区域与组织 `floorList` 求交)相关的升级内容,不含其它性能优化或通用发布项。
**应用制品**`cw-elevator-application-2.0.6.jar`Spring Boot 可执行 fat JAR)。
---
## 1. 功能概述
**访客派梯** 接口 **`add/visitor`**(实现类 `PersonRuleServiceImpl#addVisitor`)中,当调用方 **未传入非空 `floorIds`**(业务流程 **UC-01**:按被访人组织侧楼层推导)时:
1. 服务仍先通过 **`person/detail`** 取得被访人 **`floorList`**(与升级前一致)。
2. **新增**:若数据库中存在**启用**的租户策略行,且 **`allow_zone_ids`** 解析为非空 JSON 数组,则最终生效楼层为
**`effectiveFloors = floorList ∩ allow_zone_ids`**(顺序保持 **`floorList`** 原有顺序)。
3. 若交集 **为空**,接口返回失败码 **`76260532`**(不允许静默放宽到其它楼层)。
4. 若无策略表、无启用行、`allow_zone_ids` 为空或 JSON 无效:行为与升级前一致,使用 **`floorList` 全集**。
当调用方 **已传入非空 `floorIds`**(**UC-02**,第三方显式指定楼层)时:**不读取**策略表,**不对入参求交**,与升级前一致。
> **产品表述**:「固定访问楼层」在本阶段由租户管理员通过 **`allow_zone_ids`** 声明**允许开放给访客派梯的区域(zoneId)集合**;在 UC-01 路径下与组织返回的 **`floorList`** 取交集,从而将访客权限**收敛**到既有「被访人可去楼层」与「租户允许楼层」的双重约束内。
---
## 2. 涉及的数据库变更
### 2.1 变更类型
| 类型 | 对象 |
|------|------|
| **新增表** | `tenant_visitor_floor_policy` |
**执行库**:与电梯应用 **同一数据源**MySQL/InnoDB,与现有派梯业务库一致)。
### 2.2 表用途(摘要)
| 字段 | 含义 |
|------|------|
| `business_id` | 租户/机构 ID |
| `policy_type` | 本阶段固定 **`INTERSECT_ALLOWLIST`** |
| `allow_zone_ids` | **TEXT**,存 **JSON 数组**字符串,元素为允许访客派梯的 **zoneId** |
| `building_id` | 租户级默认策略填 **NULL**(预留楼栋维) |
| `enabled` | **1** 启用 / **0** 停用 |
| `policy_version` | 配置版本号(审计) |
唯一约束 **`uk_biz_building (business_id, building_id)`**:同一租户、租户级策略(`building_id` 为空)**建议仅维护一行**,避免多行时查询仅命中「最新一条」与运维预期不符。
### 2.3 DDL 脚本位置
| 用途 | 仓库内路径 |
|------|------------|
| **权威 DDL** | `docs/sql/tenant_visitor_floor_policy.sql` |
**发布包内副本**:随 v2.0.6 发布目录一并下发,路径为 **`ddl/tenant_visitor_floor_policy.sql`**(与根目录 JAR 同级下的 `ddl` 子目录)。
脚本内容为 **`CREATE TABLE IF NOT EXISTS`**,在未建表环境可重复执行;**不含**业务数据 `INSERT`,上线需按租户配置自行 **`INSERT`** 策略行。
### 2.4 上线执行顺序(建议)
1. **备份**当前电梯应用库(至少包含待变更库的全库或相关表备份策略)。
2. 在目标库执行 **`ddl/tenant_visitor_floor_policy.sql`**(或直接使用仓库 `docs/sql` 下同源文件)。
3. 对需启用策略的租户 **`INSERT`** 一行(示例字段:`business_id``policy_type='INTERSECT_ALLOWLIST'``allow_zone_ids` 为合法 JSON 数组、`enabled=1``building_id`**NULL**)。
4. 部署 **`cw-elevator-application-2.0.6.jar`** 并滚动重启应用。
5. 按 §4 做 **UC-01 / UC-02** 验收。
### 2.5 回滚说明
- **仅回滚应用**:还原旧版本 JAR 后,若库中**已存在**策略行且仍为 **enabled=1**,旧版本应用**通常不读取**该表,行为与历史一致;表结构可保留。
- **回滚数据库**:若需删除表(谨慎),在确认无其它依赖后执行 **`DROP TABLE tenant_visitor_floor_policy`**;请在变更窗口与 DBA 确认。
---
## 3. 行为与错误码(验收)
| 场景 | `floorIds` | 策略库 | 期望 |
|------|------------|--------|------|
| UC-01 基线 | 空/未传 | 无启用行或 allow 空/无效 | 使用组织 **`floorList` 全集**(与升级前一致) |
| UC-01 + 固定楼层 | 空/未传 | 有启用行且 allow 非空 | **`floorList ∩ allow`**(保序) |
| 无交集 | 空/未传 | allow 与 `floorList` 无交集 | **`76260532`** |
| 被访人无楼层 | 空/未传 | 任意 | **`76260531`** |
| UC-02 | **非空** | 任意 | **不读策略表**,按请求楼层处理 |
---
## 4. 发布包目录结构(v2.0.6
执行仓库根目录 **`./scripts/release-cw-elevator-application.sh 2.0.6`** 后,输出目录为(名称含构建日期 `<YYYYMMDD>`,与历史运行包 **`cw-elevator-application-V1.0.0.20211103`** 同构;可用环境变量 **`RELEASE_DATE_LABEL`** 固定日期):
**`maven-cw-elevator-application/releases/cw-elevator-application-V2.0.6.<YYYYMMDD>/`**
| 文件/目录 | 说明 |
|-----------|------|
| `cw-elevator-application-2.0.6.jar` | 可执行应用 |
| `bootstrap.properties``application*.properties` | 与 JAR 同层(**不**再使用 `config/` 子目录重复存放) |
| `ddl/tenant_visitor_floor_policy.sql` | 与本功能相关的 **唯一 DDL**(与 `docs/sql` 同源) |
| `版本升级说明书.md` | 本文件副本(便于随包交付) |
| `BUILD_MANIFEST.txt` | 构建时间、JDK、`git` 修订号 |
---
## 5. 参考文档(仓库内)
| 文档 | 路径 |
|------|------|
| 数据库阶段变更记录 | `docs/business/租户访客默认楼层-数据库阶段变更记录.md` |
| 数据库配置阶段技术设计 | `docs/business/租户访客默认楼层-数据库配置阶段技术设计.md` |
| 技术产品方案 | `docs/business/租户访客默认楼层技术产品方案.md` |
| 访客注册与派梯楼层走查 | `docs/business/访客注册与派梯楼层业务流程走查.md` |
---
**文档版本**:与制品 **`cw-elevator-application-2.0.6`** 对齐;若仅升级文档而不改代码,请以 **`BUILD_MANIFEST.txt`** 中构建时间为准。
@@ -0,0 +1,34 @@
# 电梯派梯应用 v2.0.6 — 版本升级说明(广发基金)
**制品**`cw-elevator-application-2.0.6.jar`。技术细节、验收码与脚本位置见同目录《版本升级说明书》。
---
## 本次升级做什么
针对**广发基金**租户:访客走常见派梯路径(接口**未单独传楼层**)时,系统在「被访人所在单位给的可去楼层」基础上,再按**广发基金侧配置的允许区域**做一次收紧,两边**都满足**的楼层才能派梯。这样可以把访客权限收在比如固定接待层,而员工本人仍可按组织权限去多层办公。
广发基金若**未配置或未启用**该策略,行为与现在一致。
---
## 上线要动什么
- 换新版应用包。
- 库里**多一张策略表**(脚本在发布包 `ddl/` 下);表里为广发基金写入**一行**启用策略即可,允许区域(如接待层对应的 zone)由业务与实施定稿。
其它租户不配策略则**不受影响**。
---
## 对您这边的影响
- **时间**:安排在**夜间**,具体周二/周三窗口见《升级计划》。
- **中断**:重启应用时派梯接口可能**短暂**不可用,一般会控制在很短时间。
- **配合**:指定一个对接人;确认广发基金侧要开放的**访客可达区域/楼层**(与前台、接待流程对齐);上线后若有异常派梯请及时反馈。
---
## 出问题怎么办
应用可先退回上一版包;策略数据可单独改/停,不一定整库回滚。细则见《升级计划》。
@@ -0,0 +1 @@
12f88c2f61add0cc239d273d884aaf9fd0d1b025
@@ -0,0 +1,10 @@
artifact=cw-elevator-application-2.0.7.jar
bundle_dir_name=cw-elevator-application-V2.0.7.20260430
directory=/media/zebra/9e8fa357-7db6-4d70-88ed-d5de5a059a663/星河湾星中星/源码/maven-cw-elevator-application/releases/cw-elevator-application-V2.0.7.20260430
built_at=2026-04-30T10:58:50+08:00
java_home=/usr/lib/jvm/java-8-openjdk-amd64
java_version_line=openjdk version "1.8.0_482"
java_version_line=OpenJDK Runtime Environment (build 1.8.0_482-8u482-ga~us1-0ubuntu1~22.04-b08)
java_version_line=OpenJDK 64-Bit Server VM (build 25.482-b08, mixed mode)
git_rev=376f94edd602d3dfed1cbada6e08361ee7b08e73
git_branch=release/cw-elevator-v1-lib-min-risk
@@ -0,0 +1,75 @@
# cw-elevator-application v2.0.7 SQL与代码一致性审核记录
**审核目标**:确认发布规范涉及 SQL 脚本与当前代码逻辑一致,满足实施交付依据留档要求。
**审核范围**`tenant_visitor_floor_policy` 建表脚本、初始化脚本、访客派梯策略读取与求交流程。
**审核时间**`2026-04-29`
**审核人**`____________`
---
## 1. 审核过程
1. 审阅 SQL 脚本:
- `docs/sql/tenant_visitor_floor_policy.sql`
- `docs/sql/tenant_visitor_floor_policy_init_guangfa_fund.sql`
2. 审阅代码路径:
- `cw-elevator-application-service/.../PersonRuleServiceImpl#addVisitor`
- `cw-elevator-application-data/.../TenantVisitorFloorPolicyMapper.xml`
- `cw-elevator-application-data/.../TenantVisitorFloorPolicyDto`
3. 做场景对照:
- UC-01:调用方未传 `floorIds`
- UC-02:调用方已传 `floorIds`
- 策略缺失/无效 JSON/交集为空等异常分支
4. 形成一致性结论与风险提示,并纳入发布包。
---
## 2. 审核依据与结果
| 检查项 | SQL 依据 | 代码依据 | 结论 |
|------|----------|----------|------|
| 策略表字段是否齐全(business_id/policy_type/allow_zone_ids/building_id/enabled/policy_version | `tenant_visitor_floor_policy.sql` DDL 定义上述字段 | Mapper 查询并映射到 DTO 同名语义字段 | 一致 |
| 代码是否只读取“启用+租户默认+INTERSECT_ALLOWLIST”策略 | 初始化脚本使用 `policy_type='INTERSECT_ALLOWLIST'``building_id=NULL``enabled=1` | Mapper `WHERE enabled=1 AND policy_type='INTERSECT_ALLOWLIST' AND (building_id IS NULL OR building_id='')` | 一致 |
| allow_zone_ids 的数据格式是否匹配代码解析方式 | SQL 注释与初始化脚本均为 JSON 数组字符串(如 `["605560545117995008"]` | `parseAllowZoneIds` 使用 `JSON.parseArray(..., String.class)` 解析 | 一致 |
| 未传 floorIds 时是否执行“被访人楼层 ∩ 策略楼层” | 策略表提供 allowlist 数据来源 | `addVisitor``!callerProvidedFloors` 分支求交 | 一致 |
| 交集为空是否按规范失败 | 初始化脚本可构造交集为空场景 | `intersected.isEmpty()` 返回 `76260532` | 一致 |
| 已传 floorIds 是否跳过策略表 | SQL 与此分支无冲突 | `callerProvidedFloors=true` 时不进入策略读取分支 | 一致 |
---
## 3. 关键证据(摘录)
- 代码读取策略(启用、类型、租户默认)来自 `TenantVisitorFloorPolicyMapper.xml`
- 代码在 `PersonRuleServiceImpl#addVisitor` 中:
- `!callerProvidedFloors` 才读取被访人楼层与租户策略;
- `allow_zone_ids` 解析成功且非空才参与求交;
- 求交为空返回 `76260532`
- 调用方已传楼层时不走策略求交流程。
- 初始化脚本 `tenant_visitor_floor_policy_init_guangfa_fund.sql` 的字段取值与上述查询条件完全兼容。
---
## 4. 审核结论
**结论:通过。**
发布规范涉及的 SQL 脚本内容与当前代码逻辑一致,满足 v2.0.7 发布包“数据库脚本 + 功能升级说明 + 实施交付依据”要求。
---
## 5. 风险提示与建议
1. **唯一性治理风险(中)**
DDL 使用 `UNIQUE KEY (business_id, building_id)`,在 MySQL 下 `building_id=NULL` 可能存在多行;当前代码通过 `ORDER BY updated_at DESC, policy_version DESC LIMIT 1` 取最新一条,不阻断功能,但建议运维侧增加“每租户默认策略唯一”巡检。
2. **配置数据质量风险(中)**
`allow_zone_ids` 必须是电梯域 `zone_id` 字符串数组,若误填其它系统 UUID 会导致策略不生效或交集为空。
---
## 6. 签字确认
| 角色 | 姓名 | 日期 | 备注 |
|------|------|------|------|
| 审核人 | `____________` | `____` | `____` |
| 实施负责人 | `____________` | `____` | `____` |
| 甲方确认(可选) | `____________` | `____` | `____` |
@@ -0,0 +1,45 @@
# \u56FE\u7247\u524D\u7F00
cloudwalk.elevator.common.relativePrefix=/cwos-portal/portal/fileManager/imgByPath?path=
# \u6570\u636E\u5E93sharding\u914D\u7F6E
spring.shardingsphere.sharding.tables.IT_ACS_RECOG_RECORD.actual-data-nodes=ds0.IT_ACS_RECOG_RECORD_$->{2020..2030}
spring.shardingsphere.sharding.tables.IT_ACS_RECOG_RECORD.table-strategy.standard.sharding-column=RECOGNITION_TIME
spring.shardingsphere.sharding.tables.IT_ACS_RECOG_RECORD.table-strategy.standard.precise-algorithm-class-name=cn.cloudwalk.elevator.YearlyShardingAlgorithm
spring.shardingsphere.sharding.tables.IT_ACS_RECOG_RECORD.table-strategy.standard.range-algorithm-class-name=cn.cloudwalk.elevator.YearlyShardingAlgorithm
# \u7535\u68AF\u5F00\u95E8\u4E8B\u4EF6\u8868
spring.shardingsphere.sharding.tables.IT_ACS_ELEVATOR_RECORD.actual-data-nodes=ds0.IT_ACS_ELEVATOR_RECORD_$->{2020..2030}
spring.shardingsphere.sharding.tables.IT_ACS_ELEVATOR_RECORD.table-strategy.standard.sharding-column=RECOGNITION_TIME
spring.shardingsphere.sharding.tables.IT_ACS_ELEVATOR_RECORD.table-strategy.standard.precise-algorithm-class-name=cn.cloudwalk.elevator.YearlyShardingAlgorithm
spring.shardingsphere.sharding.tables.IT_ACS_ELEVATOR_RECORD.table-strategy.standard.range-algorithm-class-name=cn.cloudwalk.elevator.YearlyShardingAlgorithm
spring.shardingsphere.sharding.binding-tables=IT_ACS_ELEVATOR_RECORD,IT_ACS_RECOG_RECORD
# \u4EBA\u8138\u6293\u62CD\u8BC6\u522B\u9608\u503C
cloudwalk.access-control.common.device-atrr-map.ACS_FACE_REG_THRESHOLD=75
# \u4EBA\u8138\u6BD4\u5BF9\u67E5\u8BE2\u8FC7\u6EE4\u9608\u503C
cloudwalk.access-control.common.face-compare-THRESHOLD=80
# \u5B9A\u65F6\u4EFB\u52A1\u914D\u7F6E
cloudwalk.access-control.schedual.jobs.AcsRecordStatisticsByDayJob.name=AcsRecordStatisticsByDayJob
cloudwalk.access-control.schedual.jobs.AcsRecordStatisticsByDayJob.group=ACCESS-CONTROL_GROUP
cloudwalk.access-control.schedual.jobs.AcsRecordStatisticsByDayJob.executable-class=cn.cloudwalk.service.ninca.accesscontrol.common.job.executable.AcsRecordStatisticsByDayJob
cloudwalk.access-control.schedual.jobs.AcsRecordStatisticsByDayJob.description=AcsRecordStatisticsByDay job is starting.........
## \u6BCF\u5929\u51CC\u66680\u70B910\u5206\u6267\u884C
cloudwalk.access-control.schedual.jobs.AcsRecordStatisticsByDayJob.cron-expression=0 10 0 * * ?
cloudwalk.access-control.schedual.jobs.AcsRecordStatisticsByDayJob.priority=1
# \u5F00\u95E8\u8BB0\u5F55\u63A8\u9001\u5F00\u5173\uFF1Atrue-\u5F00\uFF1Bfalse-\u5173\u3002\u9ED8\u8BA4\u4E3A\u5173
cloudwalk.access-control.common.publish-opendoor-switch=false
# \u5F00\u95E8\u8BB0\u5F55\u63A8\u9001serviceCode
cloudwalk.access-control.common.publish-opendoor-service-code=access-control
# \u540E\u7AEF\u8BC6\u522B\u4E0B\u53D1\u5F00\u95E8\u6307\u4EE4\u76F8\u5173\u914D\u7F6E
# \u6293\u62CD\u65F6\u95F4\u5728\u591A\u4E45\u4E4B\u524D\u4E0D\u4E0B\u53D1\u5F00\u95E8\u6307\u4EE4\u3002\u9ED8\u8BA45\u5206\u949F\u3002\u5355\u4F4D\uFF1A\u6BEB\u79D2\u3002
cloudwalk.access-control.common.face-capture-time-expired-milliseconds=300000
# \u591A\u5C11\u6BEB\u79D2\u4EE5\u5185\u7684\u591A\u6761\u6293\u62CD\u8BB0\u5F55\u53EA\u4E0B\u53D1\u4E00\u6B21\u5F00\u95E8\u6307\u4EE4\u3002\u9ED8\u8BA43\u79D2\u3002\u5355\u4F4D\uFF1A\u6BEB\u79D2
cloudwalk.access-control.common.face-capture-interval-milliseconds=3000
# \u5F00\u95E8\u8BB0\u5F55\u8FC7\u671F\u65F6\u95F4\u3002\u591A\u5C11\u6BEB\u79D2\u4E4B\u540E\uFF0C\u672A\u4E0A\u62A5\u5F00\u95E8\u8BB0\u5F55\uFF0C\u5F00\u95E8\u8BB0\u5F55\u72B6\u6001\u66F4\u65B0\u4E3A\u5931\u8D25\u3002\u9ED8\u8BA410\u5206\u949F\u3002\u5355\u4F4D\uFF1A\u6BEB\u79D2
cloudwalk.access-control.common.face-capture-open-door-fail-milliseconds=600000
# \u95E8\u7981\u63A7\u5236\u5668\u7C7B\u578B\u96C6\u5408
cloudwalk.access-control.common.device-controller-array[0]=mqtt
# \u8BBE\u5907\u79CD\u7C7Bid\u96C6\u5408
cloudwalk.access-control.common.device-category-array[0]=4
cloudwalk.access-control.common.device-category-array[1]=5
cloudwalk.access-control.common.device-category-array[2]=7
cloudwalk.access-control.common.device-category-array[3]=2
cloudwalk.access-control.common.device-category-array[4]=8
cloudwalk.access-control.common.device-category-array[5]=11
@@ -0,0 +1,114 @@
# spring\u914D\u7F6E
spring.mvc.throw-exception-if-no-handler-found=true
spring.mvc.locale=zh_CN
# \u8D44\u6E90\u6587\u4EF6\u914D\u7F6E
spring.messages.basename=access-control
spring.messages.always-use-message-format=true
spring.messages.encoding=utf-8
# http\u914D\u7F6E
spring.http.multipart.max-file-size=200MB
spring.http.multipart.max-request-size=200MB
spring.http.encoding.force=true
spring.http.encoding.charset=UTF-8
spring.http.encoding.enabled=true
# \u65E5\u5FD7\u914D\u7F6E\uff08\u6392\u67e5\u95ee\u9898\u671f\u95f4\u7edf\u4e00\u4e3a debug\uff1b\u7a33\u6001\u751f\u4ea7\u5efa\u8bae\u6539\u56de info\uff09
logging.config=classpath:logs/logback.xml
logging.file=${spring.application.name}
logging.path=/data/cwos/cw-elevator-application-V1.0.0.20211103/logs
logging.level.root=debug
logging.level.cn.cloudwalk=debug
# mybatis\u914D\u7F6E
mybatis.mapper-locations=classpath*:cn/cloudwalk/elevator/**/*.xml
mybatis.config-location=classpath:mapper/mybatis-config.xml
# \u5E8F\u5217\u53F7\u914D\u7F6E
cloudwalk.serial.enable=true
cloudwalk.serial.serial-length=8
cloudwalk.serial.serial-type=redis
cloudwalk.serial.serial-redis-key=CLOUDWALK-ACS-SERIAL-KEY
# \u7F13\u5B58\u914D\u7F6E
cloudwalk.spring.cache.expires=CACHE_NAME_APPLICATIONIDS#21600,ACS_DeviceTypesCache#7200,ACS_DeviceTypeFeaturesCache#7200,ACS_DeviceAttrsCache#7200,ACS_RecordStatisticsCache#90000,ACS_AreaTreeCache#60
# \u5185\u90E8\u63A5\u53E3\u8C03\u7528\u5BA2\u6237\u7AEF\u53CA\u8D85\u65F6\u914D\u7F6E
feign.hystrix.enable=true
feign.httpclient.enable=false
feign.okhttp.enable=true
ribbon.http.client.enabled=false
ribbon.okhttp.enabled=true
ribbon.ReadTimeout=10000
ribbon.ConnectTimeout=10000
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=10000
# \u5065\u5EB7\u68C0\u67E5\u914D\u7F6E
management.health.redis.enabled=false
management.health.db.enabled=false
# \u6570\u636E\u8131\u654F\u914D\u7F6E
cloudwalk.datafield.enable=true
cloudwalk.datafield.securityKey=d4b2aabc97394a12a27fc3cca6cd9ba1
cloudwalk.datafield.encrypt=AES
# redis\u914D\u7F6E
spring.redis.host=redis_01.redis_ip
spring.redis.port=6379
spring.redis.password=1qaz!QAZ
spring.redis.database=5
spring.redis.timeout=0
spring.redis.pool.max-active=10
spring.redis.pool.max-idle=1
spring.redis.pool.max-wait=10
spring.redis.pool.min-idle=0
# \u6570\u636E\u5E93sharding\u914D\u7F6E
spring.shardingsphere.datasource.names=ds0
spring.shardingsphere.datasource.ds0.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds0.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds0.jdbc-url=jdbc:mysql://mysql_01.mysql_ip:3306/cw-elevator-application?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true
spring.shardingsphere.datasource.ds0.username=cloudwalk
spring.shardingsphere.datasource.ds0.password=1qaz!QAZ
spring.shardingsphere.datasource.ds0.connection-timeout=60000
spring.shardingsphere.datasource.ds0.maximum-pool-size=20
spring.shardingsphere.datasource.ds0.minimum-idle=5
spring.shardingsphere.datasource.ds0.max-lifetime=1765000
spring.shardingsphere.datasource.ds0.auto-commit=true
spring.shardingsphere.datasource.ds0.pool-name=ds0-pool
spring.shardingsphere.props.sql.show=false
spring.shardingsphere.sharding.default-data-source-name=ds0
# \u5FAE\u670D\u52A1\u670D\u52A1\u540D\u914D\u7F6E
feign.device.name=cwos-portal
feign.resource.name=cwos-portal
feign.cwos-portal.name=cwos-portal
feign.davinci-portal.name=cwos-portal
feign.ninca-crk-std.name=ninca-crk-std
feign.component-organization.name=ninca-common-component-organization
feign.ninca-common.name=ninca-common
feign.mqtt.name=cloudwalk-device-thirdparty
# CWOS\u4E8B\u4EF6\u914D\u7F6E
cloudwalk.event.bootstrap-servers=371bfca4972c43d2aefcf302d0a4a277:9092,44700995ee904679a7ad5afddcf93bb5:9092,0837a70b5fab47569391828f5feb2561:9092
cloudwalk.event.group-id=cw-elevator-application-1
cloudwalk.event.handler-executor-config.core-pool-size=10
cloudwalk.event.handler-executor-config.maximum-pool-size=30
# \u5206\u5E03\u5F0F\u9501\u914D\u7F6E
intelligent.lock.enable=true
intelligent.lock.config.default-wait-time=10000
lockWatchdogTimeout=21000
# PERSON_NAME_SPACE
person.name.space=recordEvent
elevator.application.key=xinghewan
elevator.application.time=600
elevator.application.keyA=5B7DEF88FF04
ninca-crk-std.ip=10.0.22.102:16106
#发送第三方数据ip
sendRecord.ip=hrec.star-river.com:32165
#sendRecord.ip=172.16.16.56:32165
sendRecord.token.corpId=53db867a8bb747a1bd04dd1afcad8ca6
sendRecord.token.appKey=293e2d708f0143c2957b702cef44d951
sendRecord.token.appSecret=5f6995009b864669b52041b8f5dc4625
#是否推送
sendRecord.boolean=true
# 设备处理线程池配置
ninca.update.floor.pool.corePoolSize=5
ninca.update.floor.pool.maxPoolSize=5
ninca.update.floor.pool.queueCapacity=100000
ninca.update.floor.pool.keepAliveSeconds=150
ninca.update.floor.pool.allowCoreThreadTimeOut=true
#楼栋id
floor.building.id=605560539791228928
# 诊断探针(ConfigSource / Consul HTTP 逐实例 / Ribbon)固定启用;延迟秒数见源码 cn.cloudwalk.elevator.debug.ElevatorProbeConstants(不再使用 elevator.*.probe.*
# 探针写入 cn.cloudwalk.elevator.debug${logging.path}/${logging.file}-probe.log 见 logback.xml(含 org.springframework.cloud.consul INFO 双写)
@@ -0,0 +1,18 @@
server.port=16112
server.tomcat.uri-encoding=UTF-8
spring.application.name=elevator-app
spring.profiles.active=access-control
# consul\u914D\u7F6E
spring.cloud.consul.host=371bfca4972c43d2aefcf302d0a4a277
spring.cloud.consul.port=8500
spring.cloud.consul.enabled=true
spring.cloud.consul.discovery.register=true
spring.cloud.consul.discovery.enabled=false
spring.cloud.consul.discovery.prefer-ip-address=true
spring.cloud.consul.discovery.instance-id=${spring.application.name}-${spring.cloud.client.ipAddress}:${server.port}
spring.cloud.consul.discovery.ip-address=${spring.cloud.client.ipAddress}
spring.cloud.consul.discovery.deregister=false
# zookeeper\u914D\u7F6E
dubbo.registry.address=zookeeper://10.0.22.207:2181
dubbo.protocol.port=16107
dubbo.provider.version=1.0
@@ -0,0 +1,149 @@
#!/usr/bin/env bash
set -euo pipefail
# 生产只读证据采集(Maven 发布包内与本脚本同置于部署根目录,与 start.sh / properties 同层):
# - 进程参数/环境/工作目录
# - 本地配置文件
# - jcmd system properties
# - 应用日志关键片段
# - Consul 健康与 KV 快照
# 最终输出 tar.gz,便于离线定位“配置来源 -> Ribbon 实例列表”问题。
APP_DIR="${1:-/data/cwos/cw-elevator-application-V1.0.0.20211103}"
CONSUL_ADDR="${2:-10.0.22.102:8500}"
OUT_ROOT="${3:-${APP_DIR}/evidence}"
APP_NAME="${4:-elevator-app}"
# 现场 JDK(生产 cwos-node 固定路径;不依赖 PATH
CWOS_JAVA_BIN="/data/cwos/java/bin"
JAVA_BIN="${CWOS_JAVA_BIN}/java"
JAR_BIN="${CWOS_JAVA_BIN}/jar"
JCMD_BIN="${CWOS_JAVA_BIN}/jcmd"
DATE_BIN="/bin/date"
timestamp="$(${DATE_BIN} +%Y%m%d-%H%M%S)"
OUT_DIR="${OUT_ROOT}/elevator-evidence-${timestamp}"
mkdir -p "${OUT_DIR}"
log() { echo "[collect] $*"; }
log "APP_DIR=${APP_DIR}"
log "CONSUL_ADDR=${CONSUL_ADDR}"
log "OUT_DIR=${OUT_DIR}"
log "JAVA_BIN=${JAVA_BIN} JAR_BIN=${JAR_BIN} JCMD_BIN=${JCMD_BIN}"
PID="$(ps -ef | awk '/java/ && /cw-elevator-application/ && !/awk/ {print $2; exit}')"
if [[ -z "${PID}" ]]; then
echo "ERROR: 未找到 cw-elevator-application Java 进程" >&2
exit 1
fi
log "PID=${PID}"
echo "${PID}" > "${OUT_DIR}/pid.txt"
# 1) 进程与系统基础信息
ps -ef > "${OUT_DIR}/ps-ef.txt"
uname -a > "${OUT_DIR}/uname.txt"
${DATE_BIN} +%Y-%m-%dT%H:%M:%S%z > "${OUT_DIR}/collected-at.txt"
tr '\0' ' ' < "/proc/${PID}/cmdline" > "${OUT_DIR}/proc-cmdline.txt" || true
tr '\0' '\n' < "/proc/${PID}/environ" > "${OUT_DIR}/proc-environ.txt" || true
ls -l "/proc/${PID}/cwd" > "${OUT_DIR}/proc-cwd.txt" || true
# 2) 本地配置快照(若存在)
for f in bootstrap.properties application.properties application-access-control.properties start.sh stop.sh cw-elevator-application.service; do
if [[ -f "${APP_DIR}/${f}" ]]; then
cp -a "${APP_DIR}/${f}" "${OUT_DIR}/${f}"
fi
done
# 3) JAR 与结构快照
JAR_PATH="$(awk '{print $1}' "${OUT_DIR}/proc-cmdline.txt" | sed 's/[[:space:]]*$//')"
if [[ -f "${APP_DIR}/cw-elevator-application-V1.0.0.20211103.jar" ]]; then
JAR_PATH="${APP_DIR}/cw-elevator-application-V1.0.0.20211103.jar"
fi
echo "${JAR_PATH}" > "${OUT_DIR}/jar-path.txt"
if [[ -f "${JAR_PATH}" ]]; then
sha256sum "${JAR_PATH}" > "${OUT_DIR}/jar.sha256.txt" || true
if [[ -x "${JAR_BIN}" ]]; then
"${JAR_BIN}" tf "${JAR_PATH}" > "${OUT_DIR}/jar-tf.txt" || true
else
echo "jar not found or not executable: ${JAR_BIN}" > "${OUT_DIR}/jar-tf.txt"
fi
unzip -p "${JAR_PATH}" application.properties > "${OUT_DIR}/jar-application.properties.txt" 2>/dev/null || true
unzip -p "${JAR_PATH}" bootstrap.properties > "${OUT_DIR}/jar-bootstrap.properties.txt" 2>/dev/null || true
fi
# 4) jcmd system properties + attach 诊断(不修改应用配置;便于修复 AttachNotSupportedException
{
echo "=== current shell user ==="
id 2>/dev/null || true
echo "=== target java process ==="
ps -o user=,group=,pid=,args= -p "${PID}" 2>/dev/null || true
PROC_USER="$(stat -c '%U' "/proc/${PID}" 2>/dev/null || echo "")"
PROC_UID="$(stat -c '%u' "/proc/${PID}" 2>/dev/null || echo "")"
echo "proc_owner=${PROC_USER} uid=${PROC_UID}"
echo "=== /tmp hsperfdata (HotSpot perf counter; attach 相关) ==="
if [[ -n "${PROC_USER}" && "${PROC_USER}" != "unknown" ]]; then
HS="/tmp/hsperfdata_${PROC_USER}"
if [[ -d "${HS}" ]]; then
ls -la "${HS}" 2>/dev/null | head -30 || true
ls -la "${HS}/${PID}" 2>/dev/null || echo "missing ${HS}/${PID}"
else
echo "no directory ${HS}"
fi
fi
echo "=== cmdline tokens (attach / jdwp) ==="
tr '\0' '\n' < "/proc/${PID}/cmdline" 2>/dev/null | grep -E 'DisableAttach|Attach|jdwp|agentpath' || echo "(none matched)"
} > "${OUT_DIR}/jcmd-attach-diagnose.txt" 2>&1
if [[ -x "${JCMD_BIN}" ]]; then
"${JCMD_BIN}" "${PID}" VM.system_properties > "${OUT_DIR}/jcmd-system-properties.txt" 2>&1 || true
if grep -q 'AttachNotSupportedException\|Unable to open socket file' "${OUT_DIR}/jcmd-system-properties.txt" 2>/dev/null; then
{
echo ""
echo "HINT: jcmd attach 失败常见原因:"
echo " 1) 与 Java 进程不同用户执行 jcmd(请用与进程相同用户,例如: sudo -u <java_user> ${JCMD_BIN} ${PID} VM.system_properties"
echo " 2) /tmp/hsperfdata_<user>/<pid> 缺失或权限异常"
echo " 3) JVM 启动参数含 -XX:+DisableAttachMechanism(见 jcmd-attach-diagnose.txt 中 cmdline"
echo " 4) 进程非 HotSpot 或尚未完全初始化(极少见于长期运行的 Spring Boot"
} >> "${OUT_DIR}/jcmd-system-properties.txt"
fi
else
echo "jcmd not found or not executable: ${JCMD_BIN}" > "${OUT_DIR}/jcmd-system-properties.txt"
fi
# 4b) java 版本(与现场 JDK 一致性的旁证)
if [[ -x "${JAVA_BIN}" ]]; then
"${JAVA_BIN}" -version > "${OUT_DIR}/java-version.txt" 2>&1 || true
else
echo "java not found or not executable: ${JAVA_BIN}" > "${OUT_DIR}/java-version.txt"
fi
# 5) 应用日志关键行
LOG_FILE="${APP_DIR}/logs/elevator-app.log"
if [[ -f "${LOG_FILE}" ]]; then
cp -a "${LOG_FILE}" "${OUT_DIR}/elevator-app.log.full"
awk '
/CONFIG SOURCE PROBE START|CONFIG SOURCE PROBE END|probe key=|ConfigurationBasedServerList|Load balancer does not have available server|DynamicServerListLoadBalancer|ConsulServiceRegistry|Registering service with consul/ { print }
' "${LOG_FILE}" > "${OUT_DIR}/elevator-app.log.keylines.txt"
fi
# 6) Consul 快照
CURL="curl -sS --max-time 8"
${CURL} "http://${CONSUL_ADDR}/v1/health/service/${APP_NAME}?passing=true" > "${OUT_DIR}/consul-health-${APP_NAME}.json" || true
for svc in cwos-portal ninca-common ninca-common-component-organization ninca-crk-std cloudwalk-device-thirdparty; do
${CURL} "http://${CONSUL_ADDR}/v1/health/service/${svc}?passing=true" > "${OUT_DIR}/consul-health-${svc}.json" || true
done
${CURL} "http://${CONSUL_ADDR}/v1/kv/config/${APP_NAME}/data?raw" > "${OUT_DIR}/consul-kv-${APP_NAME}.properties" || true
${CURL} "http://${CONSUL_ADDR}/v1/kv/config/${APP_NAME},access-control/data?raw" > "${OUT_DIR}/consul-kv-${APP_NAME},access-control.properties" || true
# 7) 现场可达性快照(已知主机名)
for host in 0837a70b5fab47569391828f5feb2561 371bfca4972c43d2aefcf302d0a4a277 44700995ee904679a7ad5afddcf93bb5; do
getent hosts "${host}" > "${OUT_DIR}/getent-${host}.txt" 2>&1 || true
curl -I --max-time 5 "http://${host}:8089/" > "${OUT_DIR}/curl-head-${host}-8089.txt" 2>&1 || true
done
ARCHIVE="${OUT_DIR}.tar.gz"
tar -czf "${ARCHIVE}" -C "$(dirname "${OUT_DIR}")" "$(basename "${OUT_DIR}")"
log "DONE archive=${ARCHIVE}"
echo "${ARCHIVE}"
@@ -0,0 +1,38 @@
#!/usr/bin/env bash
# shellcheck shell=bash
# 由 v1-legacy/run.sh、v2-maven/run.sh sourceJAVA_HOME;非 JDK8 时追加 --add-opens。
#
# === 本机 JDK 8 安装根目录(含 bin/java);换机器只需改下行默认路径或通过环境变量覆盖 ===
: "${DEPLOY_JDK8:=/usr/lib/jvm/java-8-openjdk-amd64}"
_pick_java_home() {
if [[ "${ELEVATOR_USE_ENV_JAVA:-0}" == "1" ]] && [[ -n "${JAVA_HOME:-}" && -x "${JAVA_HOME}/bin/java" ]]; then
return 0
fi
if [[ -x "${DEPLOY_JDK8}/bin/java" ]]; then
export JAVA_HOME="${DEPLOY_JDK8}"
return 0
fi
for d in /usr/lib/jvm/java-8-openjdk-amd64 /usr/lib/jvm/java-1.8.0-openjdk; do
if [[ -x "$d/bin/java" ]]; then
export JAVA_HOME="$d"
return 0
fi
done
if [[ -n "${JAVA_HOME:-}" && -x "${JAVA_HOME}/bin/java" ]]; then
return 0
fi
export JAVA_HOME="${JAVA_HOME:-${DEPLOY_JDK8}}"
}
_jdk8_open_flags() {
local java="$1"
if "$java" -version 2>&1 | grep -qE 'version "1\.8\.'; then
echo ""
return
fi
echo "--add-opens=java.base/java.lang=ALL-UNNAMED"
echo "--add-opens=java.base/java.lang.reflect=ALL-UNNAMED"
echo "--add-opens=java.base/java.util=ALL-UNNAMED"
echo "--add-opens=java.base/java.io=ALL-UNNAMED"
}
@@ -0,0 +1,23 @@
[Unit]
Description=cw-elevator-application 2.0.7 (Maven V2 fat jar)
After=network.target remote-fs.target nss-lookup.target
[Service]
# 将 WorkingDirectory、ExecStart、ExecStop 中的路径占位改为实际部署绝对路径(与 JAR、start.sh、properties 同目录)。
PIDFile=/run/cw-elevator-application-2.0.7.pid
ExecStartPre=/bin/rm -f /run/cw-elevator-application-2.0.7.pid
ExecStart=/bin/bash /path/to/cw-elevator-application/start.sh
ExecStop=/bin/bash /path/to/cw-elevator-application/stop.sh
ExecReload=/bin/kill -s HUP $MAINPID
KillSignal=SIGQUIT
TimeoutStopSec=5
KillMode=process
PrivateTmp=true
Restart=always
RestartSec=10
StandardOutput=null
StandardError=null
WorkingDirectory=/path/to/cw-elevator-application
[Install]
WantedBy=multi-user.target
@@ -0,0 +1,27 @@
-- 租户访客默认楼层策略(电梯应用库)
-- 设计说明:docs/business/租户访客默认楼层-数据库配置阶段技术设计.md
CREATE TABLE IF NOT EXISTS tenant_visitor_floor_policy (
id VARCHAR(32) NOT NULL COMMENT '主键',
business_id VARCHAR(64) NOT NULL COMMENT '机构/租户 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_biz_building (business_id, building_id),
KEY idx_business_enabled (business_id, enabled)
) 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);
@@ -0,0 +1,47 @@
-- 广发基金租户:访客默认楼层策略初始化(电梯库 cw-elevator-application
--
-- 请先执行同目录 tenant_visitor_floor_policy.sql 建表(或 releases/v2.0.0/ddl 同源 DDL)。
--
-- 字段说明:allow_zone_ids 为 JSON 数组字符串,元素须与组织/派梯侧 floorList、image_rule_ref 使用的
-- zone_id(电梯库 snowflake 形态)一致,勿使用 cw_is_area 的 UUID。
--
-- 数据来源(现场查询 192.168.3.12:3307,截至脚本编写日):
-- business_idcomponent-organization.cw_is_organization
-- NAME='[28-38F]广发基金管理有限公司' -> BUSINESS_ID = 2524639890ba4f2cba9ba1a4eeaa4015
-- 28F zone_idcw-elevator-application.code_elevator_area / image_rule_ref
-- zone_id = 605560545117995008zone_name=28Fcode=0x1C
--
-- 重复执行:依赖主键 id 固定,使用 ON DUPLICATE KEY UPDATE 刷新策略字段。
SET NAMES utf8mb4;
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 (
'gf_vstr_policy_guangfa_fund_001x',
'2524639890ba4f2cba9ba1a4eeaa4015',
'INTERSECT_ALLOWLIST',
'["605560545117995008"]',
NULL,
1,
1,
'广发基金:访客与 floorList 求交后仅保留 allowlist(默认仅 28F zone)。业务配置见产品方案。',
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,38 @@
#!/usr/bin/env bash
# 与当前目录下 application.properties 同路径启动 V2maven 构建)包。
# 默认优先系统 JDK 8;若只有 JDK11+ 会自动附加 --add-opens。
# ELEVATOR_USE_ENV_JAVA=1 ./run.sh 使用当前 JAVA_HOME(如 Conda)。
# 发布包内脚本与 JAR、properties 均位于发布根目录(与 cw-elevator-application-V1.0.0.20211103 布局一致)。
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=../common-java.sh
source "${SCRIPT_DIR}/../common-java.sh"
cd "$SCRIPT_DIR"
JAR=""
for candidate in $(ls -1t cw-elevator-application-*.jar 2>/dev/null || true); do
if [[ "${candidate}" == *.jar.original ]]; then
continue
fi
JAR="${candidate}"
break
done
if [[ -z "${JAR}" || ! -f "${JAR}" ]]; then
echo "缺少 cw-elevator-application-*.jar,请在 deploy 目录执行: ./sync-jars.sh" >&2
exit 1
fi
_pick_java_home
if [[ ! -x "${JAVA_HOME}/bin/java" ]]; then
echo "ERROR: 未找到可执行的 JDK。请安装 openjdk-8-jdk,或设定 JAVA_HOME / ELEVATOR_USE_ENV_JAVA=1 ./run.sh(使用 Conda 等当前环境)。" >&2
exit 1
fi
JAVA="${JAVA_HOME}/bin/java"
OPEN_FLAGS=()
while IFS= read -r line; do
[[ -n "$line" ]] && OPEN_FLAGS+=("$line")
done < <(_jdk8_open_flags "$JAVA")
# 强制走 Consul:不再注入本地 application*.properties / redis-override.properties。
# shellcheck disable=SC2086
exec "$JAVA" "${OPEN_FLAGS[@]}" ${ELEVATOR_JAVA_OPTS:-} -jar "$JAR"
@@ -0,0 +1,23 @@
#!/usr/bin/env bash
# 与「星中心」V1 部署习惯对齐:本脚本与 cw-elevator-application-2.0.7.jar、bootstrap/application*.properties 位于同一目录。
# 覆盖 JVM:设置环境变量 ELEVATOR_JAVA_OPTS(可选);指定 JavaJAVA_HOME 或 JAVA_CMD。
set -euo pipefail
dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
cd "$dir"
JAR="cw-elevator-application-2.0.7.jar"
if [[ ! -f "$JAR" ]]; then
echo "ERROR: 未找到 ${JAR}(当前目录 $(pwd)" >&2
exit 1
fi
if [[ -n "${JAVA_CMD:-}" ]]; then
JAVA_EXEC="$JAVA_CMD"
elif [[ -n "${JAVA_HOME:-}" && -x "${JAVA_HOME}/bin/java" ]]; then
JAVA_EXEC="${JAVA_HOME}/bin/java"
else
JAVA_EXEC="/usr/lib/jvm/java-8-openjdk-amd64/bin/java"
fi
if [[ -z "${ELEVATOR_JAVA_OPTS:-}" ]]; then
ELEVATOR_JAVA_OPTS="-Xmx3072m -Xms3072m -Xmn1024m"
fi
# shellcheck disable=SC2086
exec "$JAVA_EXEC" $ELEVATOR_JAVA_OPTS -jar "$JAR"
@@ -0,0 +1,9 @@
#!/usr/bin/env bash
# 停止与本目录 cw-elevator-application-2.0.7.jar 对应的 Java 进程(仅匹配命令行中含该 JAR 名的进程)。
set -euo pipefail
JAR="cw-elevator-application-2.0.7.jar"
# shellcheck disable=SC2009
pid=$(ps -ef | grep '[j]ava' | grep "$JAR" | awk '{print $2}' || true)
if [[ -n "${pid}" ]]; then
kill -9 $pid
fi
@@ -0,0 +1,106 @@
# cw-elevator-application v2.0.7 升级计划
**项目名称**(可填):智慧电梯 / 访客派梯系统升级
**版本**v2.0.7
**计划性质**:执行级排期草案,**具体日期、起止时刻以与甲方书面/邮件确认为准**。
---
## 1. 升级目标
完成电梯派梯应用 **v2.0.7** 上线,启用**租户访客固定访问楼层**能力所需的应用与数据库对象;按业务需要为指定租户配置策略并完成验收。
---
## 2. 计划时间窗口(夜间 · 暂定周二、周三)
| 窗口 | 意向安排 | 建议内容(可合并或调整) |
|------|-----------|----------------------------|
| **第一次:周二晚间** | 夜间低峰段,具体 **HH:MMHH:MM** 待定 | 环境检查、数据库备份、执行 **DDL**(新建策略表)、(可选)预发布验证、必要时准备回滚包与检查单 |
| **第二次:周三晚间** | 夜间低峰段,具体 **HH:MMHH:MM** 待定 | 部署新版本 **JAR**、滚动/重启应用、按需 **INSERT** 租户策略数据、业务联调与验收、监控与值守 |
**说明**
- 若贵方现场要求**一个晚上完成全部步骤**,可将 DDL 与部署**合并在同一晚间窗口**执行,本表仅体现「周二、周三两晚」的**当前意向拆分**,最终以确认单为准。
- 两次窗口建议**间隔至少数小时至一个工作日**,便于第一次变更后观察库表与低风险项;若合并为单次窗口,须在计划单中注明**连续操作顺序与回滚点**。
**待确认栏**(实施时填写):
- 周二实施日期:`____年____月____日`,时段: `____ : ____` `____ : ____`
- 周三实施日期:`____年____月____日`,时段: `____ : ____` `____ : ____`
- 甲方现场联系人: `____________` 乙方/实施负责人: `____________`
---
## 3. 影响范围与沟通
| 项目 | 说明 |
|------|------|
| 影响系统 | 电梯派梯相关应用(`cw-elevator-application`)及同一业务库。 |
| 用户感知 | 应用重启期间可能出现短时派梯接口失败;策略误配可能导致部分访客路径失败,需按验收清单核对。 |
| 通知范围 | 建议提前通知:物业/客服、前台与访客登记、安保与梯控相关值班(按项目实际 roster 确定)。 |
---
## 4. 前置条件(升级前)
- [ ] 已与甲方确认 **周二 / 周三** 夜间窗口。
- [ ] 取得 **v2.0.7** 发布包(含 `jar``ddl/tenant_visitor_floor_policy.sql`、说明书)。
- [ ] 目标环境 **JDK 版本**符合实施方要求(与构建说明一致,一般为 JDK 8)。
- [ ] 数据库已做**备份**(全库或按运维规范),并可从发布包定位 DDL。
- [ ] 明确需启用「固定访客楼层」的租户列表及**允许区域**配置(若不启用,可跳过策略数据录入,行为与升级前一致)。
- [ ] 回滚包:保留**当前线上 JAR** 备份与回滚步骤(见下文)。
---
## 5. 实施步骤(建议顺序)
### 5.1 周二晚间(或首个窗口)
1. 备份数据库。
2. 执行 `tenant_visitor_floor_policy.sql``CREATE TABLE IF NOT EXISTS`,可重复执行需与 DBA 确认)。
3. (可选)在测试/预发环境先执行一遍并验证。
4. 记录执行人、时间与结果。
### 5.2 周三晚间(或第二个窗口 / 同晚续作)
1. 停止或滚动发布应用(按现网规范)。
2. 替换为 `cw-elevator-application-2.0.7.jar` 并启动。
3. 按业务需求对需启用的租户执行策略 **INSERT**`enabled=1``allow_zone_ids` 等为合法 JSON 等,字段级以技术说明书为准)。
4. 抽样验证:未传显式楼层时的访客派梯、租户策略开/关、与组织楼层无交集时的失败提示等。
5. 观察监控与日志,**值守**至约定结束时间。
*若合并为单次窗口:按「备份 → DDL → 部署 JAR → 策略数据 → 验收」顺序连续执行,并预留回滚决策时间。*
---
## 6. 验收要点(摘要)
- 未配置或未启用策略时:与升级前行为一致。
- 已启用策略且业务走「未显式指定楼层」路径:访客可去楼层为**组织允许**与**租户允许**的**交集**。
- 交集为空时:接口返回预期业务错误(技术码见实施方说明书)。
- 第三方已显式传入楼层的路径:不因本策略表改变原逻辑。
---
## 7. 回滚预案
| 场景 | 建议动作 |
|------|-----------|
| 应用异常 | 回退至上一版本 JAR;数据库新表若已存在且旧应用不读该表,通常可继续服务;与实施方确认。 |
| 策略配置错误 | 优先**停用或修正策略行**,避免大规模回滚应用。 |
| 必须撤表 | 仅在评估无依赖后由 DBA 执行删表;**高风险**,需书面确认。 |
---
## 8. 交付物核对
- [ ] `cw-elevator-application-2.0.7.jar`
- [ ] `ddl/tenant_visitor_floor_policy.sql`
- [ ] 《版本升级说明书》(实施/技术)
- [ ] 本《甲方版本升级说明》(如已作为对甲交付)
- [ ] 本《升级计划》(双方确认签字/邮件留档)
---
**文档状态**:草案;**时间点为暂定周二、周三晚间**,正式实施前请更新「待确认栏」并留存确认记录。
@@ -0,0 +1,32 @@
# cw-elevator-application v2.0.7 发布索引
本版本**功能升级说明**已单独成文,**仅覆盖「租户访客固定访问楼层」**及配套数据库 DDL,见:
**[cw-elevator-application-v2.0.7-版本升级说明书](./cw-elevator-application-v2.0.7-版本升级说明书.md)**
**对甲交付(业务表述与排期)**
- [甲方版本升级说明](./cw-elevator-application-v2.0.7-甲方版本升级说明.md)
- [升级计划(含夜间 · 周二/周三窗口)](./cw-elevator-application-v2.0.7-升级计划.md)
- [实施交付清单(实施方内部与对甲交付核对)](./cw-elevator-application-v2.0.7-实施交付清单.md)
- [实施验收记录模板(上线当晚记录)](./cw-elevator-application-v2.0.7-实施验收记录模板.md)
- [SQL与代码一致性审核记录(发布规范走查依据)](./cw-elevator-application-v2.0.7-SQL与代码一致性审核记录.md)
---
## 一键构建与发布目录
在仓库根目录、**JDK 8** 下执行:
```bash
export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64
./scripts/release-cw-elevator-application.sh 2.0.7
```
输出:**`maven-cw-elevator-application/releases/cw-elevator-application-V2.0.7.<YYYYMMDD>/`**(目录名含构建日期,与 `cw-elevator-application-V1.0.0.20211103` 命名风格一致;可用环境变量 **`RELEASE_DATE_LABEL`** 指定日期),含 `cw-elevator-application-2.0.7.jar``ddl/`、发布根目录下的 `bootstrap.properties` / `application*.properties`(与星中心/V1 同层摆放,**无额外 `config/` 子目录**)、`start.sh` / `stop.sh` / `cw-elevator-application.service`(路径占位符需现场替换)、`run.sh``common-java.sh`、**`collect_elevator_runtime_evidence.sh`**(根目录,现场只读证据采集)、`版本升级说明书.md`、甲方与实施类 Markdown、`BUILD_MANIFEST.txt`;默认另生成 **`releases/cw-elevator-application-V2.0.7.<YYYYMMDD>.zip`**(设 `RELEASE_MAKE_ZIP=0` 可跳过)。
---
## Git 与大文件
`maven-cw-elevator-application/.gitignore` 忽略 `**releases/**/*.jar**`;**DDL 与说明书**可提交;可执行 JAR 请通过制品库或制品服务器分发。
@@ -0,0 +1,60 @@
# cw-elevator-application v2.0.7 实施交付清单
**用途**:用于正式发布交付前后的材料核对,满足“发布包 + 数据库脚本 + 升级说明 + 实施验收记录”闭环。
---
## 1. 发布包文件清单
- [ ] `cw-elevator-application-2.0.7.jar`
- [ ] `ddl/tenant_visitor_floor_policy.sql`(建表脚本)
- [ ] `ddl/tenant_visitor_floor_policy_init_guangfa_fund.sql`(广发基金初始化示例/可直接执行)
- [ ] `版本升级说明书.md`(技术实施口径)
- [ ] `甲方版本升级说明.md`(业务口径)
- [ ] `升级计划.md`(实施窗口与回滚安排)
- [ ] `实施验收记录模板.md`(上线当晚记录)
- [ ] `BUILD_MANIFEST.txt`(构建时间、分支、提交号)
---
## 2. 数据库变更交付项
- [ ] DBA 已确认目标库与执行窗口
- [ ] 已备份目标库(全库/指定库按现场规范)
- [ ] 已执行 `tenant_visitor_floor_policy.sql`
- [ ] 已执行或评审 `tenant_visitor_floor_policy_init_guangfa_fund.sql`
- [ ] 已确认 `allow_zone_ids` 使用的是电梯库 `zone_id`(非其它系统 UUID
- [ ] 已留存 SQL 执行记录(执行人、时间、结果)
---
## 3. 应用部署交付项
- [ ] 上线前已保存旧版 JAR 回滚包
- [ ] 已按窗口替换为 `cw-elevator-application-2.0.7.jar`
- [ ] 应用启动日志无严重报错
- [ ] 关键接口健康检查通过(含 `add/visitor` 主链路)
- [ ] 监控观察窗口内无持续异常
---
## 4. 验收交付项
- [ ] UC-01:未传 `floorIds` 且策略启用时,楼层为 `floorList ∩ allow_zone_ids`
- [ ] UC-01:无交集时返回 `76260532`
- [ ] UC-02:显式传 `floorIds` 时不受策略表影响
- [ ] 未启用策略租户行为与升级前一致
- [ ] 已按《实施验收记录模板》完成签字/邮件留档
---
## 5. 交付确认信息
| 项目 | 内容 |
|------|------|
| 实施项目 | cw-elevator-application v2.0.7 |
| 实施日期 | `____年____月____日` |
| 甲方确认人 | `____________` |
| 乙方实施人 | `____________` |
| 文档留存路径 | `____________` |
@@ -0,0 +1,73 @@
# cw-elevator-application v2.0.7 实施验收记录模板
**用途**:实施当晚记录数据库执行、应用部署、业务验收和回滚判定,作为交付归档依据。
---
## 1. 基本信息
| 项目 | 内容 |
|------|------|
| 项目名称 | 智慧电梯 / 访客派梯系统 |
| 发布版本 | v2.0.7 |
| 实施日期 | `____年____月____日` |
| 实施时段 | `____:____` - `____:____` |
| 环境 | 生产 / 预生产(圈选) |
| 实施负责人 | `____________` |
| 甲方联系人 | `____________` |
---
## 2. 数据库执行记录
| 序号 | 脚本 | 执行时间 | 执行人 | 结果 | 备注 |
|------|------|----------|--------|------|------|
| 1 | tenant_visitor_floor_policy.sql | `____` | `____` | 成功/失败 | `____` |
| 2 | tenant_visitor_floor_policy_init_guangfa_fund.sql | `____` | `____` | 成功/失败 | `____` |
**异常记录**`______________________________________________`
---
## 3. 应用发布记录
| 项目 | 记录 |
|------|------|
| 上线前版本 | `____________` |
| 上线后版本 | `cw-elevator-application-2.0.7.jar` |
| 启停方式 | `____________` |
| 服务恢复时间 | `____________` |
| 日志健康检查 | 通过 / 不通过 |
| 监控观察结论 | 正常 / 异常(说明) |
---
## 4. 验收结果记录
| 用例 | 期望 | 实际 | 结论 |
|------|------|------|------|
| UC-01(未传 floorIds,策略启用) | floorList 与 allow_zone_ids 求交 | `____` | 通过/不通过 |
| UC-01(交集为空) | 返回 `76260532` | `____` | 通过/不通过 |
| UC-02(显式传 floorIds | 不受策略表影响 | `____` | 通过/不通过 |
| 无策略租户回归 | 行为与升级前一致 | `____` | 通过/不通过 |
---
## 5. 回滚判定
- [ ] 无需回滚,发布成功
- [ ] 需要回滚应用(原因:`________________`
- [ ] 需要回滚数据/策略(原因:`________________`
回滚执行记录(如发生):`______________________________________________`
---
## 6. 签字确认
| 角色 | 姓名 | 日期 | 备注 |
|------|------|------|------|
| 甲方确认 | `____________` | `____` | `____` |
| 乙方实施 | `____________` | `____` | `____` |
| 乙方复核 | `____________` | `____` | `____` |
@@ -0,0 +1,112 @@
# cw-elevator-application v2.0.7 版本升级说明书
**适用范围**:本说明**仅**描述 **v2.0.7** 中与 **租户访客固定访问楼层**(租户级允许区域与组织 `floorList` 求交)相关的升级内容,不含其它性能优化或通用发布项。
**应用制品**`cw-elevator-application-2.0.7.jar`Spring Boot 可执行 fat JAR)。
---
## 1. 功能概述
**访客派梯** 接口 **`add/visitor`**(实现类 `PersonRuleServiceImpl#addVisitor`)中,当调用方 **未传入非空 `floorIds`**(业务流程 **UC-01**:按被访人组织侧楼层推导)时:
1. 服务仍先通过 **`person/detail`** 取得被访人 **`floorList`**(与升级前一致)。
2. **新增**:若数据库中存在**启用**的租户策略行,且 **`allow_zone_ids`** 解析为非空 JSON 数组,则最终生效楼层为
**`effectiveFloors = floorList ∩ allow_zone_ids`**(顺序保持 **`floorList`** 原有顺序)。
3. 若交集 **为空**,接口返回失败码 **`76260532`**(不允许静默放宽到其它楼层)。
4. 若无策略表、无启用行、`allow_zone_ids` 为空或 JSON 无效:行为与升级前一致,使用 **`floorList` 全集**。
当调用方 **已传入非空 `floorIds`**(**UC-02**,第三方显式指定楼层)时:**不读取**策略表,**不对入参求交**,与升级前一致。
> **产品表述**:「固定访问楼层」在本阶段由租户管理员通过 **`allow_zone_ids`** 声明**允许开放给访客派梯的区域(zoneId)集合**;在 UC-01 路径下与组织返回的 **`floorList`** 取交集,从而将访客权限**收敛**到既有「被访人可去楼层」与「租户允许楼层」的双重约束内。
---
## 2. 涉及的数据库变更
### 2.1 变更类型
| 类型 | 对象 |
|------|------|
| **新增表** | `tenant_visitor_floor_policy` |
**执行库**:与电梯应用 **同一数据源**MySQL/InnoDB,与现有派梯业务库一致)。
### 2.2 表用途(摘要)
| 字段 | 含义 |
|------|------|
| `business_id` | 租户/机构 ID |
| `policy_type` | 本阶段固定 **`INTERSECT_ALLOWLIST`** |
| `allow_zone_ids` | **TEXT**,存 **JSON 数组**字符串,元素为允许访客派梯的 **zoneId** |
| `building_id` | 租户级默认策略填 **NULL**(预留楼栋维) |
| `enabled` | **1** 启用 / **0** 停用 |
| `policy_version` | 配置版本号(审计) |
唯一约束 **`uk_biz_building (business_id, building_id)`**:同一租户、租户级策略(`building_id` 为空)**建议仅维护一行**,避免多行时查询仅命中「最新一条」与运维预期不符。
### 2.3 DDL 脚本位置
| 用途 | 仓库内路径 |
|------|------------|
| **权威 DDL** | `docs/sql/tenant_visitor_floor_policy.sql` |
**发布包内副本**:随 v2.0.7 发布目录一并下发,路径为 **`ddl/tenant_visitor_floor_policy.sql`**(与根目录 JAR 同级下的 `ddl` 子目录)。
脚本内容为 **`CREATE TABLE IF NOT EXISTS`**,在未建表环境可重复执行;**不含**业务数据 `INSERT`,上线需按租户配置自行 **`INSERT`** 策略行。
### 2.4 上线执行顺序(建议)
1. **备份**当前电梯应用库(至少包含待变更库的全库或相关表备份策略)。
2. 在目标库执行 **`ddl/tenant_visitor_floor_policy.sql`**(或直接使用仓库 `docs/sql` 下同源文件)。
3. 对需启用策略的租户 **`INSERT`** 一行(示例字段:`business_id``policy_type='INTERSECT_ALLOWLIST'``allow_zone_ids` 为合法 JSON 数组、`enabled=1``building_id`**NULL**)。
4. 部署 **`cw-elevator-application-2.0.7.jar`** 并滚动重启应用。
5. 按 §4 做 **UC-01 / UC-02** 验收。
### 2.5 回滚说明
- **仅回滚应用**:还原旧版本 JAR 后,若库中**已存在**策略行且仍为 **enabled=1**,旧版本应用**通常不读取**该表,行为与历史一致;表结构可保留。
- **回滚数据库**:若需删除表(谨慎),在确认无其它依赖后执行 **`DROP TABLE tenant_visitor_floor_policy`**;请在变更窗口与 DBA 确认。
---
## 3. 行为与错误码(验收)
| 场景 | `floorIds` | 策略库 | 期望 |
|------|------------|--------|------|
| UC-01 基线 | 空/未传 | 无启用行或 allow 空/无效 | 使用组织 **`floorList` 全集**(与升级前一致) |
| UC-01 + 固定楼层 | 空/未传 | 有启用行且 allow 非空 | **`floorList ∩ allow`**(保序) |
| 无交集 | 空/未传 | allow 与 `floorList` 无交集 | **`76260532`** |
| 被访人无楼层 | 空/未传 | 任意 | **`76260531`** |
| UC-02 | **非空** | 任意 | **不读策略表**,按请求楼层处理 |
---
## 4. 发布包目录结构(v2.0.7
执行仓库根目录 **`./scripts/release-cw-elevator-application.sh 2.0.7`** 后,输出目录为(名称含构建日期 `<YYYYMMDD>`,与历史运行包 **`cw-elevator-application-V1.0.0.20211103`** 同构;可用环境变量 **`RELEASE_DATE_LABEL`** 固定日期):
**`maven-cw-elevator-application/releases/cw-elevator-application-V2.0.7.<YYYYMMDD>/`**
| 文件/目录 | 说明 |
|-----------|------|
| `cw-elevator-application-2.0.7.jar` | 可执行应用 |
| `bootstrap.properties``application*.properties` | 与 JAR 同层(**不**再使用 `config/` 子目录重复存放) |
| `ddl/tenant_visitor_floor_policy.sql` | 与本功能相关的 **唯一 DDL**(与 `docs/sql` 同源) |
| `版本升级说明书.md` | 本文件副本(便于随包交付) |
| `BUILD_MANIFEST.txt` | 构建时间、JDK、`git` 修订号 |
---
## 5. 参考文档(仓库内)
| 文档 | 路径 |
|------|------|
| 数据库阶段变更记录 | `docs/business/租户访客默认楼层-数据库阶段变更记录.md` |
| 数据库配置阶段技术设计 | `docs/business/租户访客默认楼层-数据库配置阶段技术设计.md` |
| 技术产品方案 | `docs/business/租户访客默认楼层技术产品方案.md` |
| 访客注册与派梯楼层走查 | `docs/business/访客注册与派梯楼层业务流程走查.md` |
---
**文档版本**:与制品 **`cw-elevator-application-2.0.7`** 对齐;若仅升级文档而不改代码,请以 **`BUILD_MANIFEST.txt`** 中构建时间为准。
@@ -0,0 +1,34 @@
# 电梯派梯应用 v2.0.7 — 版本升级说明(广发基金)
**制品**`cw-elevator-application-2.0.7.jar`。技术细节、验收码与脚本位置见同目录《版本升级说明书》。
---
## 本次升级做什么
针对**广发基金**租户:访客走常见派梯路径(接口**未单独传楼层**)时,系统在「被访人所在单位给的可去楼层」基础上,再按**广发基金侧配置的允许区域**做一次收紧,两边**都满足**的楼层才能派梯。这样可以把访客权限收在比如固定接待层,而员工本人仍可按组织权限去多层办公。
广发基金若**未配置或未启用**该策略,行为与现在一致。
---
## 上线要动什么
- 换新版应用包。
- 库里**多一张策略表**(脚本在发布包 `ddl/` 下);表里为广发基金写入**一行**启用策略即可,允许区域(如接待层对应的 zone)由业务与实施定稿。
其它租户不配策略则**不受影响**。
---
## 对您这边的影响
- **时间**:安排在**夜间**,具体周二/周三窗口见《升级计划》。
- **中断**:重启应用时派梯接口可能**短暂**不可用,一般会控制在很短时间。
- **配合**:指定一个对接人;确认广发基金侧要开放的**访客可达区域/楼层**(与前台、接待流程对齐);上线后若有异常派梯请及时反馈。
---
## 出问题怎么办
应用可先退回上一版包;策略数据可单独改/停,不一定整库回滚。细则见《升级计划》。
@@ -0,0 +1 @@
e15240a2b84b4bdff8248b9ddb224cbbe5c4cf74
@@ -0,0 +1,10 @@
artifact=cw-elevator-application-2.0.8.jar
bundle_dir_name=cw-elevator-application-V2.0.8.20260430
directory=/media/zebra/9e8fa357-7db6-4d70-88ed-d5de5a059a663/星河湾星中星/源码/maven-cw-elevator-application/releases/cw-elevator-application-V2.0.8.20260430
built_at=2026-04-30T12:55:05+08:00
java_home=/usr/lib/jvm/java-8-openjdk-amd64
java_version_line=openjdk version "1.8.0_482"
java_version_line=OpenJDK Runtime Environment (build 1.8.0_482-8u482-ga~us1-0ubuntu1~22.04-b08)
java_version_line=OpenJDK 64-Bit Server VM (build 25.482-b08, mixed mode)
git_rev=376f94edd602d3dfed1cbada6e08361ee7b08e73
git_branch=release/cw-elevator-v1-lib-min-risk
@@ -0,0 +1,75 @@
# cw-elevator-application v2.0.6 SQL与代码一致性审核记录
**审核目标**:确认发布规范涉及 SQL 脚本与当前代码逻辑一致,满足实施交付依据留档要求。
**审核范围**`tenant_visitor_floor_policy` 建表脚本、初始化脚本、访客派梯策略读取与求交流程。
**审核时间**`2026-04-29`
**审核人**`____________`
---
## 1. 审核过程
1. 审阅 SQL 脚本:
- `docs/sql/tenant_visitor_floor_policy.sql`
- `docs/sql/tenant_visitor_floor_policy_init_guangfa_fund.sql`
2. 审阅代码路径:
- `cw-elevator-application-service/.../PersonRuleServiceImpl#addVisitor`
- `cw-elevator-application-data/.../TenantVisitorFloorPolicyMapper.xml`
- `cw-elevator-application-data/.../TenantVisitorFloorPolicyDto`
3. 做场景对照:
- UC-01:调用方未传 `floorIds`
- UC-02:调用方已传 `floorIds`
- 策略缺失/无效 JSON/交集为空等异常分支
4. 形成一致性结论与风险提示,并纳入发布包。
---
## 2. 审核依据与结果
| 检查项 | SQL 依据 | 代码依据 | 结论 |
|------|----------|----------|------|
| 策略表字段是否齐全(business_id/policy_type/allow_zone_ids/building_id/enabled/policy_version | `tenant_visitor_floor_policy.sql` DDL 定义上述字段 | Mapper 查询并映射到 DTO 同名语义字段 | 一致 |
| 代码是否只读取“启用+租户默认+INTERSECT_ALLOWLIST”策略 | 初始化脚本使用 `policy_type='INTERSECT_ALLOWLIST'``building_id=NULL``enabled=1` | Mapper `WHERE enabled=1 AND policy_type='INTERSECT_ALLOWLIST' AND (building_id IS NULL OR building_id='')` | 一致 |
| allow_zone_ids 的数据格式是否匹配代码解析方式 | SQL 注释与初始化脚本均为 JSON 数组字符串(如 `["605560545117995008"]` | `parseAllowZoneIds` 使用 `JSON.parseArray(..., String.class)` 解析 | 一致 |
| 未传 floorIds 时是否执行“被访人楼层 ∩ 策略楼层” | 策略表提供 allowlist 数据来源 | `addVisitor``!callerProvidedFloors` 分支求交 | 一致 |
| 交集为空是否按规范失败 | 初始化脚本可构造交集为空场景 | `intersected.isEmpty()` 返回 `76260532` | 一致 |
| 已传 floorIds 是否跳过策略表 | SQL 与此分支无冲突 | `callerProvidedFloors=true` 时不进入策略读取分支 | 一致 |
---
## 3. 关键证据(摘录)
- 代码读取策略(启用、类型、租户默认)来自 `TenantVisitorFloorPolicyMapper.xml`
- 代码在 `PersonRuleServiceImpl#addVisitor` 中:
- `!callerProvidedFloors` 才读取被访人楼层与租户策略;
- `allow_zone_ids` 解析成功且非空才参与求交;
- 求交为空返回 `76260532`
- 调用方已传楼层时不走策略求交流程。
- 初始化脚本 `tenant_visitor_floor_policy_init_guangfa_fund.sql` 的字段取值与上述查询条件完全兼容。
---
## 4. 审核结论
**结论:通过。**
发布规范涉及的 SQL 脚本内容与当前代码逻辑一致,满足 v2.0.6 发布包“数据库脚本 + 功能升级说明 + 实施交付依据”要求。
---
## 5. 风险提示与建议
1. **唯一性治理风险(中)**
DDL 使用 `UNIQUE KEY (business_id, building_id)`,在 MySQL 下 `building_id=NULL` 可能存在多行;当前代码通过 `ORDER BY updated_at DESC, policy_version DESC LIMIT 1` 取最新一条,不阻断功能,但建议运维侧增加“每租户默认策略唯一”巡检。
2. **配置数据质量风险(中)**
`allow_zone_ids` 必须是电梯域 `zone_id` 字符串数组,若误填其它系统 UUID 会导致策略不生效或交集为空。
---
## 6. 签字确认
| 角色 | 姓名 | 日期 | 备注 |
|------|------|------|------|
| 审核人 | `____________` | `____` | `____` |
| 实施负责人 | `____________` | `____` | `____` |
| 甲方确认(可选) | `____________` | `____` | `____` |
@@ -0,0 +1,45 @@
# \u56FE\u7247\u524D\u7F00
cloudwalk.elevator.common.relativePrefix=/cwos-portal/portal/fileManager/imgByPath?path=
# \u6570\u636E\u5E93sharding\u914D\u7F6E
spring.shardingsphere.sharding.tables.IT_ACS_RECOG_RECORD.actual-data-nodes=ds0.IT_ACS_RECOG_RECORD_$->{2020..2030}
spring.shardingsphere.sharding.tables.IT_ACS_RECOG_RECORD.table-strategy.standard.sharding-column=RECOGNITION_TIME
spring.shardingsphere.sharding.tables.IT_ACS_RECOG_RECORD.table-strategy.standard.precise-algorithm-class-name=cn.cloudwalk.elevator.YearlyShardingAlgorithm
spring.shardingsphere.sharding.tables.IT_ACS_RECOG_RECORD.table-strategy.standard.range-algorithm-class-name=cn.cloudwalk.elevator.YearlyShardingAlgorithm
# \u7535\u68AF\u5F00\u95E8\u4E8B\u4EF6\u8868
spring.shardingsphere.sharding.tables.IT_ACS_ELEVATOR_RECORD.actual-data-nodes=ds0.IT_ACS_ELEVATOR_RECORD_$->{2020..2030}
spring.shardingsphere.sharding.tables.IT_ACS_ELEVATOR_RECORD.table-strategy.standard.sharding-column=RECOGNITION_TIME
spring.shardingsphere.sharding.tables.IT_ACS_ELEVATOR_RECORD.table-strategy.standard.precise-algorithm-class-name=cn.cloudwalk.elevator.YearlyShardingAlgorithm
spring.shardingsphere.sharding.tables.IT_ACS_ELEVATOR_RECORD.table-strategy.standard.range-algorithm-class-name=cn.cloudwalk.elevator.YearlyShardingAlgorithm
spring.shardingsphere.sharding.binding-tables=IT_ACS_ELEVATOR_RECORD,IT_ACS_RECOG_RECORD
# \u4EBA\u8138\u6293\u62CD\u8BC6\u522B\u9608\u503C
cloudwalk.access-control.common.device-atrr-map.ACS_FACE_REG_THRESHOLD=75
# \u4EBA\u8138\u6BD4\u5BF9\u67E5\u8BE2\u8FC7\u6EE4\u9608\u503C
cloudwalk.access-control.common.face-compare-THRESHOLD=80
# \u5B9A\u65F6\u4EFB\u52A1\u914D\u7F6E
cloudwalk.access-control.schedual.jobs.AcsRecordStatisticsByDayJob.name=AcsRecordStatisticsByDayJob
cloudwalk.access-control.schedual.jobs.AcsRecordStatisticsByDayJob.group=ACCESS-CONTROL_GROUP
cloudwalk.access-control.schedual.jobs.AcsRecordStatisticsByDayJob.executable-class=cn.cloudwalk.service.ninca.accesscontrol.common.job.executable.AcsRecordStatisticsByDayJob
cloudwalk.access-control.schedual.jobs.AcsRecordStatisticsByDayJob.description=AcsRecordStatisticsByDay job is starting.........
## \u6BCF\u5929\u51CC\u66680\u70B910\u5206\u6267\u884C
cloudwalk.access-control.schedual.jobs.AcsRecordStatisticsByDayJob.cron-expression=0 10 0 * * ?
cloudwalk.access-control.schedual.jobs.AcsRecordStatisticsByDayJob.priority=1
# \u5F00\u95E8\u8BB0\u5F55\u63A8\u9001\u5F00\u5173\uFF1Atrue-\u5F00\uFF1Bfalse-\u5173\u3002\u9ED8\u8BA4\u4E3A\u5173
cloudwalk.access-control.common.publish-opendoor-switch=false
# \u5F00\u95E8\u8BB0\u5F55\u63A8\u9001serviceCode
cloudwalk.access-control.common.publish-opendoor-service-code=access-control
# \u540E\u7AEF\u8BC6\u522B\u4E0B\u53D1\u5F00\u95E8\u6307\u4EE4\u76F8\u5173\u914D\u7F6E
# \u6293\u62CD\u65F6\u95F4\u5728\u591A\u4E45\u4E4B\u524D\u4E0D\u4E0B\u53D1\u5F00\u95E8\u6307\u4EE4\u3002\u9ED8\u8BA45\u5206\u949F\u3002\u5355\u4F4D\uFF1A\u6BEB\u79D2\u3002
cloudwalk.access-control.common.face-capture-time-expired-milliseconds=300000
# \u591A\u5C11\u6BEB\u79D2\u4EE5\u5185\u7684\u591A\u6761\u6293\u62CD\u8BB0\u5F55\u53EA\u4E0B\u53D1\u4E00\u6B21\u5F00\u95E8\u6307\u4EE4\u3002\u9ED8\u8BA43\u79D2\u3002\u5355\u4F4D\uFF1A\u6BEB\u79D2
cloudwalk.access-control.common.face-capture-interval-milliseconds=3000
# \u5F00\u95E8\u8BB0\u5F55\u8FC7\u671F\u65F6\u95F4\u3002\u591A\u5C11\u6BEB\u79D2\u4E4B\u540E\uFF0C\u672A\u4E0A\u62A5\u5F00\u95E8\u8BB0\u5F55\uFF0C\u5F00\u95E8\u8BB0\u5F55\u72B6\u6001\u66F4\u65B0\u4E3A\u5931\u8D25\u3002\u9ED8\u8BA410\u5206\u949F\u3002\u5355\u4F4D\uFF1A\u6BEB\u79D2
cloudwalk.access-control.common.face-capture-open-door-fail-milliseconds=600000
# \u95E8\u7981\u63A7\u5236\u5668\u7C7B\u578B\u96C6\u5408
cloudwalk.access-control.common.device-controller-array[0]=mqtt
# \u8BBE\u5907\u79CD\u7C7Bid\u96C6\u5408
cloudwalk.access-control.common.device-category-array[0]=4
cloudwalk.access-control.common.device-category-array[1]=5
cloudwalk.access-control.common.device-category-array[2]=7
cloudwalk.access-control.common.device-category-array[3]=2
cloudwalk.access-control.common.device-category-array[4]=8
cloudwalk.access-control.common.device-category-array[5]=11
@@ -0,0 +1,114 @@
# spring\u914D\u7F6E
spring.mvc.throw-exception-if-no-handler-found=true
spring.mvc.locale=zh_CN
# \u8D44\u6E90\u6587\u4EF6\u914D\u7F6E
spring.messages.basename=access-control
spring.messages.always-use-message-format=true
spring.messages.encoding=utf-8
# http\u914D\u7F6E
spring.http.multipart.max-file-size=200MB
spring.http.multipart.max-request-size=200MB
spring.http.encoding.force=true
spring.http.encoding.charset=UTF-8
spring.http.encoding.enabled=true
# \u65E5\u5FD7\u914D\u7F6E\uff08\u6392\u67e5\u95ee\u9898\u671f\u95f4\u7edf\u4e00\u4e3a debug\uff1b\u7a33\u6001\u751f\u4ea7\u5efa\u8bae\u6539\u56de info\uff09
logging.config=classpath:logs/logback.xml
logging.file=${spring.application.name}
logging.path=/data/cwos/cw-elevator-application-V1.0.0.20211103/logs
logging.level.root=debug
logging.level.cn.cloudwalk=debug
# mybatis\u914D\u7F6E
mybatis.mapper-locations=classpath*:cn/cloudwalk/elevator/**/*.xml
mybatis.config-location=classpath:mapper/mybatis-config.xml
# \u5E8F\u5217\u53F7\u914D\u7F6E
cloudwalk.serial.enable=true
cloudwalk.serial.serial-length=8
cloudwalk.serial.serial-type=redis
cloudwalk.serial.serial-redis-key=CLOUDWALK-ACS-SERIAL-KEY
# \u7F13\u5B58\u914D\u7F6E
cloudwalk.spring.cache.expires=CACHE_NAME_APPLICATIONIDS#21600,ACS_DeviceTypesCache#7200,ACS_DeviceTypeFeaturesCache#7200,ACS_DeviceAttrsCache#7200,ACS_RecordStatisticsCache#90000,ACS_AreaTreeCache#60
# \u5185\u90E8\u63A5\u53E3\u8C03\u7528\u5BA2\u6237\u7AEF\u53CA\u8D85\u65F6\u914D\u7F6E
feign.hystrix.enable=true
feign.httpclient.enable=false
feign.okhttp.enable=true
ribbon.http.client.enabled=false
ribbon.okhttp.enabled=true
ribbon.ReadTimeout=10000
ribbon.ConnectTimeout=10000
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=10000
# \u5065\u5EB7\u68C0\u67E5\u914D\u7F6E
management.health.redis.enabled=false
management.health.db.enabled=false
# \u6570\u636E\u8131\u654F\u914D\u7F6E
cloudwalk.datafield.enable=true
cloudwalk.datafield.securityKey=d4b2aabc97394a12a27fc3cca6cd9ba1
cloudwalk.datafield.encrypt=AES
# redis\u914D\u7F6E
spring.redis.host=redis_01.redis_ip
spring.redis.port=6379
spring.redis.password=1qaz!QAZ
spring.redis.database=5
spring.redis.timeout=0
spring.redis.pool.max-active=10
spring.redis.pool.max-idle=1
spring.redis.pool.max-wait=10
spring.redis.pool.min-idle=0
# \u6570\u636E\u5E93sharding\u914D\u7F6E
spring.shardingsphere.datasource.names=ds0
spring.shardingsphere.datasource.ds0.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds0.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds0.jdbc-url=jdbc:mysql://mysql_01.mysql_ip:3306/cw-elevator-application?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true
spring.shardingsphere.datasource.ds0.username=cloudwalk
spring.shardingsphere.datasource.ds0.password=1qaz!QAZ
spring.shardingsphere.datasource.ds0.connection-timeout=60000
spring.shardingsphere.datasource.ds0.maximum-pool-size=20
spring.shardingsphere.datasource.ds0.minimum-idle=5
spring.shardingsphere.datasource.ds0.max-lifetime=1765000
spring.shardingsphere.datasource.ds0.auto-commit=true
spring.shardingsphere.datasource.ds0.pool-name=ds0-pool
spring.shardingsphere.props.sql.show=false
spring.shardingsphere.sharding.default-data-source-name=ds0
# \u5FAE\u670D\u52A1\u670D\u52A1\u540D\u914D\u7F6E
feign.device.name=cwos-portal
feign.resource.name=cwos-portal
feign.cwos-portal.name=cwos-portal
feign.davinci-portal.name=cwos-portal
feign.ninca-crk-std.name=ninca-crk-std
feign.component-organization.name=ninca-common-component-organization
feign.ninca-common.name=ninca-common
feign.mqtt.name=cloudwalk-device-thirdparty
# CWOS\u4E8B\u4EF6\u914D\u7F6E
cloudwalk.event.bootstrap-servers=371bfca4972c43d2aefcf302d0a4a277:9092,44700995ee904679a7ad5afddcf93bb5:9092,0837a70b5fab47569391828f5feb2561:9092
cloudwalk.event.group-id=cw-elevator-application-1
cloudwalk.event.handler-executor-config.core-pool-size=10
cloudwalk.event.handler-executor-config.maximum-pool-size=30
# \u5206\u5E03\u5F0F\u9501\u914D\u7F6E
intelligent.lock.enable=true
intelligent.lock.config.default-wait-time=10000
lockWatchdogTimeout=21000
# PERSON_NAME_SPACE
person.name.space=recordEvent
elevator.application.key=xinghewan
elevator.application.time=600
elevator.application.keyA=5B7DEF88FF04
ninca-crk-std.ip=10.0.22.102:16106
#发送第三方数据ip
sendRecord.ip=hrec.star-river.com:32165
#sendRecord.ip=172.16.16.56:32165
sendRecord.token.corpId=53db867a8bb747a1bd04dd1afcad8ca6
sendRecord.token.appKey=293e2d708f0143c2957b702cef44d951
sendRecord.token.appSecret=5f6995009b864669b52041b8f5dc4625
#是否推送
sendRecord.boolean=true
# 设备处理线程池配置
ninca.update.floor.pool.corePoolSize=5
ninca.update.floor.pool.maxPoolSize=5
ninca.update.floor.pool.queueCapacity=100000
ninca.update.floor.pool.keepAliveSeconds=150
ninca.update.floor.pool.allowCoreThreadTimeOut=true
#楼栋id
floor.building.id=605560539791228928
# 诊断探针(ConfigSource / Consul HTTP 逐实例 / Ribbon)固定启用;延迟秒数见源码 cn.cloudwalk.elevator.debug.ElevatorProbeConstants(不再使用 elevator.*.probe.*
# 探针写入 cn.cloudwalk.elevator.debug${logging.path}/${logging.file}-probe.log 见 logback.xml(含 org.springframework.cloud.consul INFO 双写)
@@ -0,0 +1,18 @@
server.port=16112
server.tomcat.uri-encoding=UTF-8
spring.application.name=elevator-app
spring.profiles.active=access-control
# consul\u914D\u7F6E
spring.cloud.consul.host=371bfca4972c43d2aefcf302d0a4a277
spring.cloud.consul.port=8500
spring.cloud.consul.enabled=true
spring.cloud.consul.discovery.register=true
spring.cloud.consul.discovery.enabled=false
spring.cloud.consul.discovery.prefer-ip-address=true
spring.cloud.consul.discovery.instance-id=${spring.application.name}-${spring.cloud.client.ipAddress}:${server.port}
spring.cloud.consul.discovery.ip-address=${spring.cloud.client.ipAddress}
spring.cloud.consul.discovery.deregister=false
# zookeeper\u914D\u7F6E
dubbo.registry.address=zookeeper://10.0.22.207:2181
dubbo.protocol.port=16107
dubbo.provider.version=1.0
@@ -0,0 +1,149 @@
#!/usr/bin/env bash
set -euo pipefail
# 生产只读证据采集(Maven 发布包内与本脚本同置于部署根目录,与 start.sh / properties 同层):
# - 进程参数/环境/工作目录
# - 本地配置文件
# - jcmd system properties
# - 应用日志关键片段
# - Consul 健康与 KV 快照
# 最终输出 tar.gz,便于离线定位“配置来源 -> Ribbon 实例列表”问题。
APP_DIR="${1:-/data/cwos/cw-elevator-application-V1.0.0.20211103}"
CONSUL_ADDR="${2:-10.0.22.102:8500}"
OUT_ROOT="${3:-${APP_DIR}/evidence}"
APP_NAME="${4:-elevator-app}"
# 现场 JDK(生产 cwos-node 固定路径;不依赖 PATH
CWOS_JAVA_BIN="/data/cwos/java/bin"
JAVA_BIN="${CWOS_JAVA_BIN}/java"
JAR_BIN="${CWOS_JAVA_BIN}/jar"
JCMD_BIN="${CWOS_JAVA_BIN}/jcmd"
DATE_BIN="/bin/date"
timestamp="$(${DATE_BIN} +%Y%m%d-%H%M%S)"
OUT_DIR="${OUT_ROOT}/elevator-evidence-${timestamp}"
mkdir -p "${OUT_DIR}"
log() { echo "[collect] $*"; }
log "APP_DIR=${APP_DIR}"
log "CONSUL_ADDR=${CONSUL_ADDR}"
log "OUT_DIR=${OUT_DIR}"
log "JAVA_BIN=${JAVA_BIN} JAR_BIN=${JAR_BIN} JCMD_BIN=${JCMD_BIN}"
PID="$(ps -ef | awk '/java/ && /cw-elevator-application/ && !/awk/ {print $2; exit}')"
if [[ -z "${PID}" ]]; then
echo "ERROR: 未找到 cw-elevator-application Java 进程" >&2
exit 1
fi
log "PID=${PID}"
echo "${PID}" > "${OUT_DIR}/pid.txt"
# 1) 进程与系统基础信息
ps -ef > "${OUT_DIR}/ps-ef.txt"
uname -a > "${OUT_DIR}/uname.txt"
${DATE_BIN} +%Y-%m-%dT%H:%M:%S%z > "${OUT_DIR}/collected-at.txt"
tr '\0' ' ' < "/proc/${PID}/cmdline" > "${OUT_DIR}/proc-cmdline.txt" || true
tr '\0' '\n' < "/proc/${PID}/environ" > "${OUT_DIR}/proc-environ.txt" || true
ls -l "/proc/${PID}/cwd" > "${OUT_DIR}/proc-cwd.txt" || true
# 2) 本地配置快照(若存在)
for f in bootstrap.properties application.properties application-access-control.properties start.sh stop.sh cw-elevator-application.service; do
if [[ -f "${APP_DIR}/${f}" ]]; then
cp -a "${APP_DIR}/${f}" "${OUT_DIR}/${f}"
fi
done
# 3) JAR 与结构快照
JAR_PATH="$(awk '{print $1}' "${OUT_DIR}/proc-cmdline.txt" | sed 's/[[:space:]]*$//')"
if [[ -f "${APP_DIR}/cw-elevator-application-V1.0.0.20211103.jar" ]]; then
JAR_PATH="${APP_DIR}/cw-elevator-application-V1.0.0.20211103.jar"
fi
echo "${JAR_PATH}" > "${OUT_DIR}/jar-path.txt"
if [[ -f "${JAR_PATH}" ]]; then
sha256sum "${JAR_PATH}" > "${OUT_DIR}/jar.sha256.txt" || true
if [[ -x "${JAR_BIN}" ]]; then
"${JAR_BIN}" tf "${JAR_PATH}" > "${OUT_DIR}/jar-tf.txt" || true
else
echo "jar not found or not executable: ${JAR_BIN}" > "${OUT_DIR}/jar-tf.txt"
fi
unzip -p "${JAR_PATH}" application.properties > "${OUT_DIR}/jar-application.properties.txt" 2>/dev/null || true
unzip -p "${JAR_PATH}" bootstrap.properties > "${OUT_DIR}/jar-bootstrap.properties.txt" 2>/dev/null || true
fi
# 4) jcmd system properties + attach 诊断(不修改应用配置;便于修复 AttachNotSupportedException
{
echo "=== current shell user ==="
id 2>/dev/null || true
echo "=== target java process ==="
ps -o user=,group=,pid=,args= -p "${PID}" 2>/dev/null || true
PROC_USER="$(stat -c '%U' "/proc/${PID}" 2>/dev/null || echo "")"
PROC_UID="$(stat -c '%u' "/proc/${PID}" 2>/dev/null || echo "")"
echo "proc_owner=${PROC_USER} uid=${PROC_UID}"
echo "=== /tmp hsperfdata (HotSpot perf counter; attach 相关) ==="
if [[ -n "${PROC_USER}" && "${PROC_USER}" != "unknown" ]]; then
HS="/tmp/hsperfdata_${PROC_USER}"
if [[ -d "${HS}" ]]; then
ls -la "${HS}" 2>/dev/null | head -30 || true
ls -la "${HS}/${PID}" 2>/dev/null || echo "missing ${HS}/${PID}"
else
echo "no directory ${HS}"
fi
fi
echo "=== cmdline tokens (attach / jdwp) ==="
tr '\0' '\n' < "/proc/${PID}/cmdline" 2>/dev/null | grep -E 'DisableAttach|Attach|jdwp|agentpath' || echo "(none matched)"
} > "${OUT_DIR}/jcmd-attach-diagnose.txt" 2>&1
if [[ -x "${JCMD_BIN}" ]]; then
"${JCMD_BIN}" "${PID}" VM.system_properties > "${OUT_DIR}/jcmd-system-properties.txt" 2>&1 || true
if grep -q 'AttachNotSupportedException\|Unable to open socket file' "${OUT_DIR}/jcmd-system-properties.txt" 2>/dev/null; then
{
echo ""
echo "HINT: jcmd attach 失败常见原因:"
echo " 1) 与 Java 进程不同用户执行 jcmd(请用与进程相同用户,例如: sudo -u <java_user> ${JCMD_BIN} ${PID} VM.system_properties"
echo " 2) /tmp/hsperfdata_<user>/<pid> 缺失或权限异常"
echo " 3) JVM 启动参数含 -XX:+DisableAttachMechanism(见 jcmd-attach-diagnose.txt 中 cmdline"
echo " 4) 进程非 HotSpot 或尚未完全初始化(极少见于长期运行的 Spring Boot"
} >> "${OUT_DIR}/jcmd-system-properties.txt"
fi
else
echo "jcmd not found or not executable: ${JCMD_BIN}" > "${OUT_DIR}/jcmd-system-properties.txt"
fi
# 4b) java 版本(与现场 JDK 一致性的旁证)
if [[ -x "${JAVA_BIN}" ]]; then
"${JAVA_BIN}" -version > "${OUT_DIR}/java-version.txt" 2>&1 || true
else
echo "java not found or not executable: ${JAVA_BIN}" > "${OUT_DIR}/java-version.txt"
fi
# 5) 应用日志关键行
LOG_FILE="${APP_DIR}/logs/elevator-app.log"
if [[ -f "${LOG_FILE}" ]]; then
cp -a "${LOG_FILE}" "${OUT_DIR}/elevator-app.log.full"
awk '
/CONFIG SOURCE PROBE START|CONFIG SOURCE PROBE END|probe key=|ConfigurationBasedServerList|Load balancer does not have available server|DynamicServerListLoadBalancer|ConsulServiceRegistry|Registering service with consul/ { print }
' "${LOG_FILE}" > "${OUT_DIR}/elevator-app.log.keylines.txt"
fi
# 6) Consul 快照
CURL="curl -sS --max-time 8"
${CURL} "http://${CONSUL_ADDR}/v1/health/service/${APP_NAME}?passing=true" > "${OUT_DIR}/consul-health-${APP_NAME}.json" || true
for svc in cwos-portal ninca-common ninca-common-component-organization ninca-crk-std cloudwalk-device-thirdparty; do
${CURL} "http://${CONSUL_ADDR}/v1/health/service/${svc}?passing=true" > "${OUT_DIR}/consul-health-${svc}.json" || true
done
${CURL} "http://${CONSUL_ADDR}/v1/kv/config/${APP_NAME}/data?raw" > "${OUT_DIR}/consul-kv-${APP_NAME}.properties" || true
${CURL} "http://${CONSUL_ADDR}/v1/kv/config/${APP_NAME},access-control/data?raw" > "${OUT_DIR}/consul-kv-${APP_NAME},access-control.properties" || true
# 7) 现场可达性快照(已知主机名)
for host in 0837a70b5fab47569391828f5feb2561 371bfca4972c43d2aefcf302d0a4a277 44700995ee904679a7ad5afddcf93bb5; do
getent hosts "${host}" > "${OUT_DIR}/getent-${host}.txt" 2>&1 || true
curl -I --max-time 5 "http://${host}:8089/" > "${OUT_DIR}/curl-head-${host}-8089.txt" 2>&1 || true
done
ARCHIVE="${OUT_DIR}.tar.gz"
tar -czf "${ARCHIVE}" -C "$(dirname "${OUT_DIR}")" "$(basename "${OUT_DIR}")"
log "DONE archive=${ARCHIVE}"
echo "${ARCHIVE}"
@@ -0,0 +1,38 @@
#!/usr/bin/env bash
# shellcheck shell=bash
# 由 v1-legacy/run.sh、v2-maven/run.sh sourceJAVA_HOME;非 JDK8 时追加 --add-opens。
#
# === 本机 JDK 8 安装根目录(含 bin/java);换机器只需改下行默认路径或通过环境变量覆盖 ===
: "${DEPLOY_JDK8:=/usr/lib/jvm/java-8-openjdk-amd64}"
_pick_java_home() {
if [[ "${ELEVATOR_USE_ENV_JAVA:-0}" == "1" ]] && [[ -n "${JAVA_HOME:-}" && -x "${JAVA_HOME}/bin/java" ]]; then
return 0
fi
if [[ -x "${DEPLOY_JDK8}/bin/java" ]]; then
export JAVA_HOME="${DEPLOY_JDK8}"
return 0
fi
for d in /usr/lib/jvm/java-8-openjdk-amd64 /usr/lib/jvm/java-1.8.0-openjdk; do
if [[ -x "$d/bin/java" ]]; then
export JAVA_HOME="$d"
return 0
fi
done
if [[ -n "${JAVA_HOME:-}" && -x "${JAVA_HOME}/bin/java" ]]; then
return 0
fi
export JAVA_HOME="${JAVA_HOME:-${DEPLOY_JDK8}}"
}
_jdk8_open_flags() {
local java="$1"
if "$java" -version 2>&1 | grep -qE 'version "1\.8\.'; then
echo ""
return
fi
echo "--add-opens=java.base/java.lang=ALL-UNNAMED"
echo "--add-opens=java.base/java.lang.reflect=ALL-UNNAMED"
echo "--add-opens=java.base/java.util=ALL-UNNAMED"
echo "--add-opens=java.base/java.io=ALL-UNNAMED"
}
@@ -0,0 +1,23 @@
[Unit]
Description=cw-elevator-application 2.0.8 (Maven V2 fat jar)
After=network.target remote-fs.target nss-lookup.target
[Service]
# 将 WorkingDirectory、ExecStart、ExecStop 中的路径占位改为实际部署绝对路径(与 JAR、start.sh、properties 同目录)。
PIDFile=/run/cw-elevator-application-2.0.8.pid
ExecStartPre=/bin/rm -f /run/cw-elevator-application-2.0.8.pid
ExecStart=/bin/bash /path/to/cw-elevator-application/start.sh
ExecStop=/bin/bash /path/to/cw-elevator-application/stop.sh
ExecReload=/bin/kill -s HUP $MAINPID
KillSignal=SIGQUIT
TimeoutStopSec=5
KillMode=process
PrivateTmp=true
Restart=always
RestartSec=10
StandardOutput=null
StandardError=null
WorkingDirectory=/path/to/cw-elevator-application
[Install]
WantedBy=multi-user.target
@@ -0,0 +1,27 @@
-- 租户访客默认楼层策略(电梯应用库)
-- 设计说明:docs/business/租户访客默认楼层-数据库配置阶段技术设计.md
CREATE TABLE IF NOT EXISTS tenant_visitor_floor_policy (
id VARCHAR(32) NOT NULL COMMENT '主键',
business_id VARCHAR(64) NOT NULL COMMENT '机构/租户 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_biz_building (business_id, building_id),
KEY idx_business_enabled (business_id, enabled)
) 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);
@@ -0,0 +1,47 @@
-- 广发基金租户:访客默认楼层策略初始化(电梯库 cw-elevator-application
--
-- 请先执行同目录 tenant_visitor_floor_policy.sql 建表(或 releases/v2.0.0/ddl 同源 DDL)。
--
-- 字段说明:allow_zone_ids 为 JSON 数组字符串,元素须与组织/派梯侧 floorList、image_rule_ref 使用的
-- zone_id(电梯库 snowflake 形态)一致,勿使用 cw_is_area 的 UUID。
--
-- 数据来源(现场查询 192.168.3.12:3307,截至脚本编写日):
-- business_idcomponent-organization.cw_is_organization
-- NAME='[28-38F]广发基金管理有限公司' -> BUSINESS_ID = 2524639890ba4f2cba9ba1a4eeaa4015
-- 28F zone_idcw-elevator-application.code_elevator_area / image_rule_ref
-- zone_id = 605560545117995008zone_name=28Fcode=0x1C
--
-- 重复执行:依赖主键 id 固定,使用 ON DUPLICATE KEY UPDATE 刷新策略字段。
SET NAMES utf8mb4;
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 (
'gf_vstr_policy_guangfa_fund_001x',
'2524639890ba4f2cba9ba1a4eeaa4015',
'INTERSECT_ALLOWLIST',
'["605560545117995008"]',
NULL,
1,
1,
'广发基金:访客与 floorList 求交后仅保留 allowlist(默认仅 28F zone)。业务配置见产品方案。',
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,38 @@
#!/usr/bin/env bash
# 与当前目录下 application.properties 同路径启动 V2maven 构建)包。
# 默认优先系统 JDK 8;若只有 JDK11+ 会自动附加 --add-opens。
# ELEVATOR_USE_ENV_JAVA=1 ./run.sh 使用当前 JAVA_HOME(如 Conda)。
# 发布包内脚本与 JAR、properties 均位于发布根目录(与 cw-elevator-application-V1.0.0.20211103 布局一致)。
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=../common-java.sh
source "${SCRIPT_DIR}/../common-java.sh"
cd "$SCRIPT_DIR"
JAR=""
for candidate in $(ls -1t cw-elevator-application-*.jar 2>/dev/null || true); do
if [[ "${candidate}" == *.jar.original ]]; then
continue
fi
JAR="${candidate}"
break
done
if [[ -z "${JAR}" || ! -f "${JAR}" ]]; then
echo "缺少 cw-elevator-application-*.jar,请在 deploy 目录执行: ./sync-jars.sh" >&2
exit 1
fi
_pick_java_home
if [[ ! -x "${JAVA_HOME}/bin/java" ]]; then
echo "ERROR: 未找到可执行的 JDK。请安装 openjdk-8-jdk,或设定 JAVA_HOME / ELEVATOR_USE_ENV_JAVA=1 ./run.sh(使用 Conda 等当前环境)。" >&2
exit 1
fi
JAVA="${JAVA_HOME}/bin/java"
OPEN_FLAGS=()
while IFS= read -r line; do
[[ -n "$line" ]] && OPEN_FLAGS+=("$line")
done < <(_jdk8_open_flags "$JAVA")
# 强制走 Consul:不再注入本地 application*.properties / redis-override.properties。
# shellcheck disable=SC2086
exec "$JAVA" "${OPEN_FLAGS[@]}" ${ELEVATOR_JAVA_OPTS:-} -jar "$JAR"
@@ -0,0 +1,23 @@
#!/usr/bin/env bash
# 与「星中心」V1 部署习惯对齐:本脚本与 cw-elevator-application-2.0.8.jar、bootstrap/application*.properties 位于同一目录。
# 覆盖 JVM:设置环境变量 ELEVATOR_JAVA_OPTS(可选);指定 JavaJAVA_HOME 或 JAVA_CMD。
set -euo pipefail
dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
cd "$dir"
JAR="cw-elevator-application-2.0.8.jar"
if [[ ! -f "$JAR" ]]; then
echo "ERROR: 未找到 ${JAR}(当前目录 $(pwd)" >&2
exit 1
fi
if [[ -n "${JAVA_CMD:-}" ]]; then
JAVA_EXEC="$JAVA_CMD"
elif [[ -n "${JAVA_HOME:-}" && -x "${JAVA_HOME}/bin/java" ]]; then
JAVA_EXEC="${JAVA_HOME}/bin/java"
else
JAVA_EXEC="/usr/lib/jvm/java-8-openjdk-amd64/bin/java"
fi
if [[ -z "${ELEVATOR_JAVA_OPTS:-}" ]]; then
ELEVATOR_JAVA_OPTS="-Xmx3072m -Xms3072m -Xmn1024m"
fi
# shellcheck disable=SC2086
exec "$JAVA_EXEC" $ELEVATOR_JAVA_OPTS -jar "$JAR"
@@ -0,0 +1,9 @@
#!/usr/bin/env bash
# 停止与本目录 cw-elevator-application-2.0.8.jar 对应的 Java 进程(仅匹配命令行中含该 JAR 名的进程)。
set -euo pipefail
JAR="cw-elevator-application-2.0.8.jar"
# shellcheck disable=SC2009
pid=$(ps -ef | grep '[j]ava' | grep "$JAR" | awk '{print $2}' || true)
if [[ -n "${pid}" ]]; then
kill -9 $pid
fi
@@ -0,0 +1,106 @@
# cw-elevator-application v2.0.6 升级计划
**项目名称**(可填):智慧电梯 / 访客派梯系统升级
**版本**v2.0.6
**计划性质**:执行级排期草案,**具体日期、起止时刻以与甲方书面/邮件确认为准**。
---
## 1. 升级目标
完成电梯派梯应用 **v2.0.6** 上线,启用**租户访客固定访问楼层**能力所需的应用与数据库对象;按业务需要为指定租户配置策略并完成验收。
---
## 2. 计划时间窗口(夜间 · 暂定周二、周三)
| 窗口 | 意向安排 | 建议内容(可合并或调整) |
|------|-----------|----------------------------|
| **第一次:周二晚间** | 夜间低峰段,具体 **HH:MMHH:MM** 待定 | 环境检查、数据库备份、执行 **DDL**(新建策略表)、(可选)预发布验证、必要时准备回滚包与检查单 |
| **第二次:周三晚间** | 夜间低峰段,具体 **HH:MMHH:MM** 待定 | 部署新版本 **JAR**、滚动/重启应用、按需 **INSERT** 租户策略数据、业务联调与验收、监控与值守 |
**说明**
- 若贵方现场要求**一个晚上完成全部步骤**,可将 DDL 与部署**合并在同一晚间窗口**执行,本表仅体现「周二、周三两晚」的**当前意向拆分**,最终以确认单为准。
- 两次窗口建议**间隔至少数小时至一个工作日**,便于第一次变更后观察库表与低风险项;若合并为单次窗口,须在计划单中注明**连续操作顺序与回滚点**。
**待确认栏**(实施时填写):
- 周二实施日期:`____年____月____日`,时段: `____ : ____` `____ : ____`
- 周三实施日期:`____年____月____日`,时段: `____ : ____` `____ : ____`
- 甲方现场联系人: `____________` 乙方/实施负责人: `____________`
---
## 3. 影响范围与沟通
| 项目 | 说明 |
|------|------|
| 影响系统 | 电梯派梯相关应用(`cw-elevator-application`)及同一业务库。 |
| 用户感知 | 应用重启期间可能出现短时派梯接口失败;策略误配可能导致部分访客路径失败,需按验收清单核对。 |
| 通知范围 | 建议提前通知:物业/客服、前台与访客登记、安保与梯控相关值班(按项目实际 roster 确定)。 |
---
## 4. 前置条件(升级前)
- [ ] 已与甲方确认 **周二 / 周三** 夜间窗口。
- [ ] 取得 **v2.0.6** 发布包(含 `jar``ddl/tenant_visitor_floor_policy.sql`、说明书)。
- [ ] 目标环境 **JDK 版本**符合实施方要求(与构建说明一致,一般为 JDK 8)。
- [ ] 数据库已做**备份**(全库或按运维规范),并可从发布包定位 DDL。
- [ ] 明确需启用「固定访客楼层」的租户列表及**允许区域**配置(若不启用,可跳过策略数据录入,行为与升级前一致)。
- [ ] 回滚包:保留**当前线上 JAR** 备份与回滚步骤(见下文)。
---
## 5. 实施步骤(建议顺序)
### 5.1 周二晚间(或首个窗口)
1. 备份数据库。
2. 执行 `tenant_visitor_floor_policy.sql``CREATE TABLE IF NOT EXISTS`,可重复执行需与 DBA 确认)。
3. (可选)在测试/预发环境先执行一遍并验证。
4. 记录执行人、时间与结果。
### 5.2 周三晚间(或第二个窗口 / 同晚续作)
1. 停止或滚动发布应用(按现网规范)。
2. 替换为 `cw-elevator-application-2.0.6.jar` 并启动。
3. 按业务需求对需启用的租户执行策略 **INSERT**`enabled=1``allow_zone_ids` 等为合法 JSON 等,字段级以技术说明书为准)。
4. 抽样验证:未传显式楼层时的访客派梯、租户策略开/关、与组织楼层无交集时的失败提示等。
5. 观察监控与日志,**值守**至约定结束时间。
*若合并为单次窗口:按「备份 → DDL → 部署 JAR → 策略数据 → 验收」顺序连续执行,并预留回滚决策时间。*
---
## 6. 验收要点(摘要)
- 未配置或未启用策略时:与升级前行为一致。
- 已启用策略且业务走「未显式指定楼层」路径:访客可去楼层为**组织允许**与**租户允许**的**交集**。
- 交集为空时:接口返回预期业务错误(技术码见实施方说明书)。
- 第三方已显式传入楼层的路径:不因本策略表改变原逻辑。
---
## 7. 回滚预案
| 场景 | 建议动作 |
|------|-----------|
| 应用异常 | 回退至上一版本 JAR;数据库新表若已存在且旧应用不读该表,通常可继续服务;与实施方确认。 |
| 策略配置错误 | 优先**停用或修正策略行**,避免大规模回滚应用。 |
| 必须撤表 | 仅在评估无依赖后由 DBA 执行删表;**高风险**,需书面确认。 |
---
## 8. 交付物核对
- [ ] `cw-elevator-application-2.0.6.jar`
- [ ] `ddl/tenant_visitor_floor_policy.sql`
- [ ] 《版本升级说明书》(实施/技术)
- [ ] 本《甲方版本升级说明》(如已作为对甲交付)
- [ ] 本《升级计划》(双方确认签字/邮件留档)
---
**文档状态**:草案;**时间点为暂定周二、周三晚间**,正式实施前请更新「待确认栏」并留存确认记录。
@@ -0,0 +1,32 @@
# cw-elevator-application v2.0.6 发布索引
本版本**功能升级说明**已单独成文,**仅覆盖「租户访客固定访问楼层」**及配套数据库 DDL,见:
**[cw-elevator-application-v2.0.6-版本升级说明书](./cw-elevator-application-v2.0.6-版本升级说明书.md)**
**对甲交付(业务表述与排期)**
- [甲方版本升级说明](./cw-elevator-application-v2.0.6-甲方版本升级说明.md)
- [升级计划(含夜间 · 周二/周三窗口)](./cw-elevator-application-v2.0.6-升级计划.md)
- [实施交付清单(实施方内部与对甲交付核对)](./cw-elevator-application-v2.0.6-实施交付清单.md)
- [实施验收记录模板(上线当晚记录)](./cw-elevator-application-v2.0.6-实施验收记录模板.md)
- [SQL与代码一致性审核记录(发布规范走查依据)](./cw-elevator-application-v2.0.6-SQL与代码一致性审核记录.md)
---
## 一键构建与发布目录
在仓库根目录、**JDK 8** 下执行:
```bash
export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64
./scripts/release-cw-elevator-application.sh 2.0.6
```
输出:**`maven-cw-elevator-application/releases/cw-elevator-application-V2.0.6.<YYYYMMDD>/`**(目录名含构建日期,与 `cw-elevator-application-V1.0.0.20211103` 命名风格一致;可用环境变量 **`RELEASE_DATE_LABEL`** 指定日期),含 `cw-elevator-application-2.0.6.jar``ddl/`、发布根目录下的 `bootstrap.properties` / `application*.properties`(与星中心/V1 同层摆放,**无额外 `config/` 子目录**)、`start.sh` / `stop.sh` / `cw-elevator-application.service`(路径占位符需现场替换)、`run.sh``common-java.sh`、**`collect_elevator_runtime_evidence.sh`**(根目录,现场只读证据采集)、`版本升级说明书.md`、甲方与实施类 Markdown、`BUILD_MANIFEST.txt`;默认另生成 **`releases/cw-elevator-application-V2.0.6.<YYYYMMDD>.zip`**(设 `RELEASE_MAKE_ZIP=0` 可跳过)。
---
## Git 与大文件
`maven-cw-elevator-application/.gitignore` 忽略 `**releases/**/*.jar**`;**DDL 与说明书**可提交;可执行 JAR 请通过制品库或制品服务器分发。
@@ -0,0 +1,60 @@
# cw-elevator-application v2.0.6 实施交付清单
**用途**:用于正式发布交付前后的材料核对,满足“发布包 + 数据库脚本 + 升级说明 + 实施验收记录”闭环。
---
## 1. 发布包文件清单
- [ ] `cw-elevator-application-2.0.6.jar`
- [ ] `ddl/tenant_visitor_floor_policy.sql`(建表脚本)
- [ ] `ddl/tenant_visitor_floor_policy_init_guangfa_fund.sql`(广发基金初始化示例/可直接执行)
- [ ] `版本升级说明书.md`(技术实施口径)
- [ ] `甲方版本升级说明.md`(业务口径)
- [ ] `升级计划.md`(实施窗口与回滚安排)
- [ ] `实施验收记录模板.md`(上线当晚记录)
- [ ] `BUILD_MANIFEST.txt`(构建时间、分支、提交号)
---
## 2. 数据库变更交付项
- [ ] DBA 已确认目标库与执行窗口
- [ ] 已备份目标库(全库/指定库按现场规范)
- [ ] 已执行 `tenant_visitor_floor_policy.sql`
- [ ] 已执行或评审 `tenant_visitor_floor_policy_init_guangfa_fund.sql`
- [ ] 已确认 `allow_zone_ids` 使用的是电梯库 `zone_id`(非其它系统 UUID
- [ ] 已留存 SQL 执行记录(执行人、时间、结果)
---
## 3. 应用部署交付项
- [ ] 上线前已保存旧版 JAR 回滚包
- [ ] 已按窗口替换为 `cw-elevator-application-2.0.6.jar`
- [ ] 应用启动日志无严重报错
- [ ] 关键接口健康检查通过(含 `add/visitor` 主链路)
- [ ] 监控观察窗口内无持续异常
---
## 4. 验收交付项
- [ ] UC-01:未传 `floorIds` 且策略启用时,楼层为 `floorList ∩ allow_zone_ids`
- [ ] UC-01:无交集时返回 `76260532`
- [ ] UC-02:显式传 `floorIds` 时不受策略表影响
- [ ] 未启用策略租户行为与升级前一致
- [ ] 已按《实施验收记录模板》完成签字/邮件留档
---
## 5. 交付确认信息
| 项目 | 内容 |
|------|------|
| 实施项目 | cw-elevator-application v2.0.6 |
| 实施日期 | `____年____月____日` |
| 甲方确认人 | `____________` |
| 乙方实施人 | `____________` |
| 文档留存路径 | `____________` |
@@ -0,0 +1,73 @@
# cw-elevator-application v2.0.6 实施验收记录模板
**用途**:实施当晚记录数据库执行、应用部署、业务验收和回滚判定,作为交付归档依据。
---
## 1. 基本信息
| 项目 | 内容 |
|------|------|
| 项目名称 | 智慧电梯 / 访客派梯系统 |
| 发布版本 | v2.0.6 |
| 实施日期 | `____年____月____日` |
| 实施时段 | `____:____` - `____:____` |
| 环境 | 生产 / 预生产(圈选) |
| 实施负责人 | `____________` |
| 甲方联系人 | `____________` |
---
## 2. 数据库执行记录
| 序号 | 脚本 | 执行时间 | 执行人 | 结果 | 备注 |
|------|------|----------|--------|------|------|
| 1 | tenant_visitor_floor_policy.sql | `____` | `____` | 成功/失败 | `____` |
| 2 | tenant_visitor_floor_policy_init_guangfa_fund.sql | `____` | `____` | 成功/失败 | `____` |
**异常记录**`______________________________________________`
---
## 3. 应用发布记录
| 项目 | 记录 |
|------|------|
| 上线前版本 | `____________` |
| 上线后版本 | `cw-elevator-application-2.0.6.jar` |
| 启停方式 | `____________` |
| 服务恢复时间 | `____________` |
| 日志健康检查 | 通过 / 不通过 |
| 监控观察结论 | 正常 / 异常(说明) |
---
## 4. 验收结果记录
| 用例 | 期望 | 实际 | 结论 |
|------|------|------|------|
| UC-01(未传 floorIds,策略启用) | floorList 与 allow_zone_ids 求交 | `____` | 通过/不通过 |
| UC-01(交集为空) | 返回 `76260532` | `____` | 通过/不通过 |
| UC-02(显式传 floorIds | 不受策略表影响 | `____` | 通过/不通过 |
| 无策略租户回归 | 行为与升级前一致 | `____` | 通过/不通过 |
---
## 5. 回滚判定
- [ ] 无需回滚,发布成功
- [ ] 需要回滚应用(原因:`________________`
- [ ] 需要回滚数据/策略(原因:`________________`
回滚执行记录(如发生):`______________________________________________`
---
## 6. 签字确认
| 角色 | 姓名 | 日期 | 备注 |
|------|------|------|------|
| 甲方确认 | `____________` | `____` | `____` |
| 乙方实施 | `____________` | `____` | `____` |
| 乙方复核 | `____________` | `____` | `____` |
@@ -0,0 +1,112 @@
# cw-elevator-application v2.0.6 版本升级说明书
**适用范围**:本说明**仅**描述 **v2.0.6** 中与 **租户访客固定访问楼层**(租户级允许区域与组织 `floorList` 求交)相关的升级内容,不含其它性能优化或通用发布项。
**应用制品**`cw-elevator-application-2.0.6.jar`Spring Boot 可执行 fat JAR)。
---
## 1. 功能概述
**访客派梯** 接口 **`add/visitor`**(实现类 `PersonRuleServiceImpl#addVisitor`)中,当调用方 **未传入非空 `floorIds`**(业务流程 **UC-01**:按被访人组织侧楼层推导)时:
1. 服务仍先通过 **`person/detail`** 取得被访人 **`floorList`**(与升级前一致)。
2. **新增**:若数据库中存在**启用**的租户策略行,且 **`allow_zone_ids`** 解析为非空 JSON 数组,则最终生效楼层为
**`effectiveFloors = floorList ∩ allow_zone_ids`**(顺序保持 **`floorList`** 原有顺序)。
3. 若交集 **为空**,接口返回失败码 **`76260532`**(不允许静默放宽到其它楼层)。
4. 若无策略表、无启用行、`allow_zone_ids` 为空或 JSON 无效:行为与升级前一致,使用 **`floorList` 全集**。
当调用方 **已传入非空 `floorIds`**(**UC-02**,第三方显式指定楼层)时:**不读取**策略表,**不对入参求交**,与升级前一致。
> **产品表述**:「固定访问楼层」在本阶段由租户管理员通过 **`allow_zone_ids`** 声明**允许开放给访客派梯的区域(zoneId)集合**;在 UC-01 路径下与组织返回的 **`floorList`** 取交集,从而将访客权限**收敛**到既有「被访人可去楼层」与「租户允许楼层」的双重约束内。
---
## 2. 涉及的数据库变更
### 2.1 变更类型
| 类型 | 对象 |
|------|------|
| **新增表** | `tenant_visitor_floor_policy` |
**执行库**:与电梯应用 **同一数据源**MySQL/InnoDB,与现有派梯业务库一致)。
### 2.2 表用途(摘要)
| 字段 | 含义 |
|------|------|
| `business_id` | 租户/机构 ID |
| `policy_type` | 本阶段固定 **`INTERSECT_ALLOWLIST`** |
| `allow_zone_ids` | **TEXT**,存 **JSON 数组**字符串,元素为允许访客派梯的 **zoneId** |
| `building_id` | 租户级默认策略填 **NULL**(预留楼栋维) |
| `enabled` | **1** 启用 / **0** 停用 |
| `policy_version` | 配置版本号(审计) |
唯一约束 **`uk_biz_building (business_id, building_id)`**:同一租户、租户级策略(`building_id` 为空)**建议仅维护一行**,避免多行时查询仅命中「最新一条」与运维预期不符。
### 2.3 DDL 脚本位置
| 用途 | 仓库内路径 |
|------|------------|
| **权威 DDL** | `docs/sql/tenant_visitor_floor_policy.sql` |
**发布包内副本**:随 v2.0.6 发布目录一并下发,路径为 **`ddl/tenant_visitor_floor_policy.sql`**(与根目录 JAR 同级下的 `ddl` 子目录)。
脚本内容为 **`CREATE TABLE IF NOT EXISTS`**,在未建表环境可重复执行;**不含**业务数据 `INSERT`,上线需按租户配置自行 **`INSERT`** 策略行。
### 2.4 上线执行顺序(建议)
1. **备份**当前电梯应用库(至少包含待变更库的全库或相关表备份策略)。
2. 在目标库执行 **`ddl/tenant_visitor_floor_policy.sql`**(或直接使用仓库 `docs/sql` 下同源文件)。
3. 对需启用策略的租户 **`INSERT`** 一行(示例字段:`business_id``policy_type='INTERSECT_ALLOWLIST'``allow_zone_ids` 为合法 JSON 数组、`enabled=1``building_id`**NULL**)。
4. 部署 **`cw-elevator-application-2.0.6.jar`** 并滚动重启应用。
5. 按 §4 做 **UC-01 / UC-02** 验收。
### 2.5 回滚说明
- **仅回滚应用**:还原旧版本 JAR 后,若库中**已存在**策略行且仍为 **enabled=1**,旧版本应用**通常不读取**该表,行为与历史一致;表结构可保留。
- **回滚数据库**:若需删除表(谨慎),在确认无其它依赖后执行 **`DROP TABLE tenant_visitor_floor_policy`**;请在变更窗口与 DBA 确认。
---
## 3. 行为与错误码(验收)
| 场景 | `floorIds` | 策略库 | 期望 |
|------|------------|--------|------|
| UC-01 基线 | 空/未传 | 无启用行或 allow 空/无效 | 使用组织 **`floorList` 全集**(与升级前一致) |
| UC-01 + 固定楼层 | 空/未传 | 有启用行且 allow 非空 | **`floorList ∩ allow`**(保序) |
| 无交集 | 空/未传 | allow 与 `floorList` 无交集 | **`76260532`** |
| 被访人无楼层 | 空/未传 | 任意 | **`76260531`** |
| UC-02 | **非空** | 任意 | **不读策略表**,按请求楼层处理 |
---
## 4. 发布包目录结构(v2.0.6
执行仓库根目录 **`./scripts/release-cw-elevator-application.sh 2.0.6`** 后,输出目录为(名称含构建日期 `<YYYYMMDD>`,与历史运行包 **`cw-elevator-application-V1.0.0.20211103`** 同构;可用环境变量 **`RELEASE_DATE_LABEL`** 固定日期):
**`maven-cw-elevator-application/releases/cw-elevator-application-V2.0.6.<YYYYMMDD>/`**
| 文件/目录 | 说明 |
|-----------|------|
| `cw-elevator-application-2.0.6.jar` | 可执行应用 |
| `bootstrap.properties``application*.properties` | 与 JAR 同层(**不**再使用 `config/` 子目录重复存放) |
| `ddl/tenant_visitor_floor_policy.sql` | 与本功能相关的 **唯一 DDL**(与 `docs/sql` 同源) |
| `版本升级说明书.md` | 本文件副本(便于随包交付) |
| `BUILD_MANIFEST.txt` | 构建时间、JDK、`git` 修订号 |
---
## 5. 参考文档(仓库内)
| 文档 | 路径 |
|------|------|
| 数据库阶段变更记录 | `docs/business/租户访客默认楼层-数据库阶段变更记录.md` |
| 数据库配置阶段技术设计 | `docs/business/租户访客默认楼层-数据库配置阶段技术设计.md` |
| 技术产品方案 | `docs/business/租户访客默认楼层技术产品方案.md` |
| 访客注册与派梯楼层走查 | `docs/business/访客注册与派梯楼层业务流程走查.md` |
---
**文档版本**:与制品 **`cw-elevator-application-2.0.6`** 对齐;若仅升级文档而不改代码,请以 **`BUILD_MANIFEST.txt`** 中构建时间为准。
@@ -0,0 +1,34 @@
# 电梯派梯应用 v2.0.6 — 版本升级说明(广发基金)
**制品**`cw-elevator-application-2.0.6.jar`。技术细节、验收码与脚本位置见同目录《版本升级说明书》。
---
## 本次升级做什么
针对**广发基金**租户:访客走常见派梯路径(接口**未单独传楼层**)时,系统在「被访人所在单位给的可去楼层」基础上,再按**广发基金侧配置的允许区域**做一次收紧,两边**都满足**的楼层才能派梯。这样可以把访客权限收在比如固定接待层,而员工本人仍可按组织权限去多层办公。
广发基金若**未配置或未启用**该策略,行为与现在一致。
---
## 上线要动什么
- 换新版应用包。
- 库里**多一张策略表**(脚本在发布包 `ddl/` 下);表里为广发基金写入**一行**启用策略即可,允许区域(如接待层对应的 zone)由业务与实施定稿。
其它租户不配策略则**不受影响**。
---
## 对您这边的影响
- **时间**:安排在**夜间**,具体周二/周三窗口见《升级计划》。
- **中断**:重启应用时派梯接口可能**短暂**不可用,一般会控制在很短时间。
- **配合**:指定一个对接人;确认广发基金侧要开放的**访客可达区域/楼层**(与前台、接待流程对齐);上线后若有异常派梯请及时反馈。
---
## 出问题怎么办
应用可先退回上一版包;策略数据可单独改/停,不一定整库回滚。细则见《升级计划》。
@@ -0,0 +1 @@
542f53f22538942c21355f407c71f283eccd1c31
@@ -0,0 +1,10 @@
artifact=cw-elevator-application-2.0.9.jar
bundle_dir_name=cw-elevator-application-V2.0.9.20260430
directory=/media/zebra/9e8fa357-7db6-4d70-88ed-d5de5a059a663/星河湾星中星/源码/maven-cw-elevator-application/releases/cw-elevator-application-V2.0.9.20260430
built_at=2026-04-30T19:14:01+08:00
java_home=/usr/lib/jvm/java-8-openjdk-amd64
java_version_line=openjdk version "1.8.0_482"
java_version_line=OpenJDK Runtime Environment (build 1.8.0_482-8u482-ga~us1-0ubuntu1~22.04-b08)
java_version_line=OpenJDK 64-Bit Server VM (build 25.482-b08, mixed mode)
git_rev=376f94edd602d3dfed1cbada6e08361ee7b08e73
git_branch=release/cw-elevator-v1-lib-min-risk
@@ -0,0 +1,75 @@
# cw-elevator-application v2.0.6 SQL与代码一致性审核记录
**审核目标**:确认发布规范涉及 SQL 脚本与当前代码逻辑一致,满足实施交付依据留档要求。
**审核范围**`tenant_visitor_floor_policy` 建表脚本、初始化脚本、访客派梯策略读取与求交流程。
**审核时间**`2026-04-29`
**审核人**`____________`
---
## 1. 审核过程
1. 审阅 SQL 脚本:
- `docs/sql/tenant_visitor_floor_policy.sql`
- `docs/sql/tenant_visitor_floor_policy_init_guangfa_fund.sql`
2. 审阅代码路径:
- `cw-elevator-application-service/.../PersonRuleServiceImpl#addVisitor`
- `cw-elevator-application-data/.../TenantVisitorFloorPolicyMapper.xml`
- `cw-elevator-application-data/.../TenantVisitorFloorPolicyDto`
3. 做场景对照:
- UC-01:调用方未传 `floorIds`
- UC-02:调用方已传 `floorIds`
- 策略缺失/无效 JSON/交集为空等异常分支
4. 形成一致性结论与风险提示,并纳入发布包。
---
## 2. 审核依据与结果
| 检查项 | SQL 依据 | 代码依据 | 结论 |
|------|----------|----------|------|
| 策略表字段是否齐全(business_id/policy_type/allow_zone_ids/building_id/enabled/policy_version | `tenant_visitor_floor_policy.sql` DDL 定义上述字段 | Mapper 查询并映射到 DTO 同名语义字段 | 一致 |
| 代码是否只读取“启用+租户默认+INTERSECT_ALLOWLIST”策略 | 初始化脚本使用 `policy_type='INTERSECT_ALLOWLIST'``building_id=NULL``enabled=1` | Mapper `WHERE enabled=1 AND policy_type='INTERSECT_ALLOWLIST' AND (building_id IS NULL OR building_id='')` | 一致 |
| allow_zone_ids 的数据格式是否匹配代码解析方式 | SQL 注释与初始化脚本均为 JSON 数组字符串(如 `["605560545117995008"]` | `parseAllowZoneIds` 使用 `JSON.parseArray(..., String.class)` 解析 | 一致 |
| 未传 floorIds 时是否执行“被访人楼层 ∩ 策略楼层” | 策略表提供 allowlist 数据来源 | `addVisitor``!callerProvidedFloors` 分支求交 | 一致 |
| 交集为空是否按规范失败 | 初始化脚本可构造交集为空场景 | `intersected.isEmpty()` 返回 `76260532` | 一致 |
| 已传 floorIds 是否跳过策略表 | SQL 与此分支无冲突 | `callerProvidedFloors=true` 时不进入策略读取分支 | 一致 |
---
## 3. 关键证据(摘录)
- 代码读取策略(启用、类型、租户默认)来自 `TenantVisitorFloorPolicyMapper.xml`
- 代码在 `PersonRuleServiceImpl#addVisitor` 中:
- `!callerProvidedFloors` 才读取被访人楼层与租户策略;
- `allow_zone_ids` 解析成功且非空才参与求交;
- 求交为空返回 `76260532`
- 调用方已传楼层时不走策略求交流程。
- 初始化脚本 `tenant_visitor_floor_policy_init_guangfa_fund.sql` 的字段取值与上述查询条件完全兼容。
---
## 4. 审核结论
**结论:通过。**
发布规范涉及的 SQL 脚本内容与当前代码逻辑一致,满足 v2.0.6 发布包“数据库脚本 + 功能升级说明 + 实施交付依据”要求。
---
## 5. 风险提示与建议
1. **唯一性治理风险(中)**
DDL 使用 `UNIQUE KEY (business_id, building_id)`,在 MySQL 下 `building_id=NULL` 可能存在多行;当前代码通过 `ORDER BY updated_at DESC, policy_version DESC LIMIT 1` 取最新一条,不阻断功能,但建议运维侧增加“每租户默认策略唯一”巡检。
2. **配置数据质量风险(中)**
`allow_zone_ids` 必须是电梯域 `zone_id` 字符串数组,若误填其它系统 UUID 会导致策略不生效或交集为空。
---
## 6. 签字确认
| 角色 | 姓名 | 日期 | 备注 |
|------|------|------|------|
| 审核人 | `____________` | `____` | `____` |
| 实施负责人 | `____________` | `____` | `____` |
| 甲方确认(可选) | `____________` | `____` | `____` |
@@ -0,0 +1,45 @@
# \u56FE\u7247\u524D\u7F00
cloudwalk.elevator.common.relativePrefix=/cwos-portal/portal/fileManager/imgByPath?path=
# \u6570\u636E\u5E93sharding\u914D\u7F6E
spring.shardingsphere.sharding.tables.IT_ACS_RECOG_RECORD.actual-data-nodes=ds0.IT_ACS_RECOG_RECORD_$->{2020..2030}
spring.shardingsphere.sharding.tables.IT_ACS_RECOG_RECORD.table-strategy.standard.sharding-column=RECOGNITION_TIME
spring.shardingsphere.sharding.tables.IT_ACS_RECOG_RECORD.table-strategy.standard.precise-algorithm-class-name=cn.cloudwalk.elevator.YearlyShardingAlgorithm
spring.shardingsphere.sharding.tables.IT_ACS_RECOG_RECORD.table-strategy.standard.range-algorithm-class-name=cn.cloudwalk.elevator.YearlyShardingAlgorithm
# \u7535\u68AF\u5F00\u95E8\u4E8B\u4EF6\u8868
spring.shardingsphere.sharding.tables.IT_ACS_ELEVATOR_RECORD.actual-data-nodes=ds0.IT_ACS_ELEVATOR_RECORD_$->{2020..2030}
spring.shardingsphere.sharding.tables.IT_ACS_ELEVATOR_RECORD.table-strategy.standard.sharding-column=RECOGNITION_TIME
spring.shardingsphere.sharding.tables.IT_ACS_ELEVATOR_RECORD.table-strategy.standard.precise-algorithm-class-name=cn.cloudwalk.elevator.YearlyShardingAlgorithm
spring.shardingsphere.sharding.tables.IT_ACS_ELEVATOR_RECORD.table-strategy.standard.range-algorithm-class-name=cn.cloudwalk.elevator.YearlyShardingAlgorithm
spring.shardingsphere.sharding.binding-tables=IT_ACS_ELEVATOR_RECORD,IT_ACS_RECOG_RECORD
# \u4EBA\u8138\u6293\u62CD\u8BC6\u522B\u9608\u503C
cloudwalk.access-control.common.device-atrr-map.ACS_FACE_REG_THRESHOLD=75
# \u4EBA\u8138\u6BD4\u5BF9\u67E5\u8BE2\u8FC7\u6EE4\u9608\u503C
cloudwalk.access-control.common.face-compare-THRESHOLD=80
# \u5B9A\u65F6\u4EFB\u52A1\u914D\u7F6E
cloudwalk.access-control.schedual.jobs.AcsRecordStatisticsByDayJob.name=AcsRecordStatisticsByDayJob
cloudwalk.access-control.schedual.jobs.AcsRecordStatisticsByDayJob.group=ACCESS-CONTROL_GROUP
cloudwalk.access-control.schedual.jobs.AcsRecordStatisticsByDayJob.executable-class=cn.cloudwalk.service.ninca.accesscontrol.common.job.executable.AcsRecordStatisticsByDayJob
cloudwalk.access-control.schedual.jobs.AcsRecordStatisticsByDayJob.description=AcsRecordStatisticsByDay job is starting.........
## \u6BCF\u5929\u51CC\u66680\u70B910\u5206\u6267\u884C
cloudwalk.access-control.schedual.jobs.AcsRecordStatisticsByDayJob.cron-expression=0 10 0 * * ?
cloudwalk.access-control.schedual.jobs.AcsRecordStatisticsByDayJob.priority=1
# \u5F00\u95E8\u8BB0\u5F55\u63A8\u9001\u5F00\u5173\uFF1Atrue-\u5F00\uFF1Bfalse-\u5173\u3002\u9ED8\u8BA4\u4E3A\u5173
cloudwalk.access-control.common.publish-opendoor-switch=false
# \u5F00\u95E8\u8BB0\u5F55\u63A8\u9001serviceCode
cloudwalk.access-control.common.publish-opendoor-service-code=access-control
# \u540E\u7AEF\u8BC6\u522B\u4E0B\u53D1\u5F00\u95E8\u6307\u4EE4\u76F8\u5173\u914D\u7F6E
# \u6293\u62CD\u65F6\u95F4\u5728\u591A\u4E45\u4E4B\u524D\u4E0D\u4E0B\u53D1\u5F00\u95E8\u6307\u4EE4\u3002\u9ED8\u8BA45\u5206\u949F\u3002\u5355\u4F4D\uFF1A\u6BEB\u79D2\u3002
cloudwalk.access-control.common.face-capture-time-expired-milliseconds=300000
# \u591A\u5C11\u6BEB\u79D2\u4EE5\u5185\u7684\u591A\u6761\u6293\u62CD\u8BB0\u5F55\u53EA\u4E0B\u53D1\u4E00\u6B21\u5F00\u95E8\u6307\u4EE4\u3002\u9ED8\u8BA43\u79D2\u3002\u5355\u4F4D\uFF1A\u6BEB\u79D2
cloudwalk.access-control.common.face-capture-interval-milliseconds=3000
# \u5F00\u95E8\u8BB0\u5F55\u8FC7\u671F\u65F6\u95F4\u3002\u591A\u5C11\u6BEB\u79D2\u4E4B\u540E\uFF0C\u672A\u4E0A\u62A5\u5F00\u95E8\u8BB0\u5F55\uFF0C\u5F00\u95E8\u8BB0\u5F55\u72B6\u6001\u66F4\u65B0\u4E3A\u5931\u8D25\u3002\u9ED8\u8BA410\u5206\u949F\u3002\u5355\u4F4D\uFF1A\u6BEB\u79D2
cloudwalk.access-control.common.face-capture-open-door-fail-milliseconds=600000
# \u95E8\u7981\u63A7\u5236\u5668\u7C7B\u578B\u96C6\u5408
cloudwalk.access-control.common.device-controller-array[0]=mqtt
# \u8BBE\u5907\u79CD\u7C7Bid\u96C6\u5408
cloudwalk.access-control.common.device-category-array[0]=4
cloudwalk.access-control.common.device-category-array[1]=5
cloudwalk.access-control.common.device-category-array[2]=7
cloudwalk.access-control.common.device-category-array[3]=2
cloudwalk.access-control.common.device-category-array[4]=8
cloudwalk.access-control.common.device-category-array[5]=11
@@ -0,0 +1,118 @@
# spring\u914D\u7F6E
spring.mvc.throw-exception-if-no-handler-found=true
spring.mvc.locale=zh_CN
# \u8D44\u6E90\u6587\u4EF6\u914D\u7F6E
spring.messages.basename=access-control
spring.messages.always-use-message-format=true
spring.messages.encoding=utf-8
# http\u914D\u7F6E
spring.http.multipart.max-file-size=200MB
spring.http.multipart.max-request-size=200MB
spring.http.encoding.force=true
spring.http.encoding.charset=UTF-8
spring.http.encoding.enabled=true
# \u65E5\u5FD7\u914D\u7F6E\uff08\u6392\u67e5\u95ee\u9898\u671f\u95f4\u7edf\u4e00\u4e3a debug\uff1b\u7a33\u6001\u751f\u4ea7\u5efa\u8bae\u6539\u56de info\uff09
logging.config=classpath:logs/logback.xml
logging.file=${spring.application.name}
logging.path=/data/cwos/cw-elevator-application-V1.0.0.20211103/logs
logging.level.root=info
logging.level.cn.cloudwalk=info
logging.level.cn.cloudwalk.elevator.debug=warn
logging.level.org.springframework.cloud.consul=warn
logging.level.com.netflix.loadbalancer=warn
logging.level.org.springframework.cloud.client.discovery=warn
# mybatis\u914D\u7F6E
mybatis.mapper-locations=classpath*:cn/cloudwalk/elevator/**/*.xml
mybatis.config-location=classpath:mapper/mybatis-config.xml
# \u5E8F\u5217\u53F7\u914D\u7F6E
cloudwalk.serial.enable=true
cloudwalk.serial.serial-length=8
cloudwalk.serial.serial-type=redis
cloudwalk.serial.serial-redis-key=CLOUDWALK-ACS-SERIAL-KEY
# \u7F13\u5B58\u914D\u7F6E
cloudwalk.spring.cache.expires=CACHE_NAME_APPLICATIONIDS#21600,ACS_DeviceTypesCache#7200,ACS_DeviceTypeFeaturesCache#7200,ACS_DeviceAttrsCache#7200,ACS_RecordStatisticsCache#90000,ACS_AreaTreeCache#60
# \u5185\u90E8\u63A5\u53E3\u8C03\u7528\u5BA2\u6237\u7AEF\u53CA\u8D85\u65F6\u914D\u7F6E
feign.hystrix.enable=true
feign.httpclient.enable=false
feign.okhttp.enable=true
ribbon.http.client.enabled=false
ribbon.okhttp.enabled=true
ribbon.ReadTimeout=10000
ribbon.ConnectTimeout=10000
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=10000
# \u5065\u5EB7\u68C0\u67E5\u914D\u7F6E
management.health.redis.enabled=false
management.health.db.enabled=false
# \u6570\u636E\u8131\u654F\u914D\u7F6E
cloudwalk.datafield.enable=true
cloudwalk.datafield.securityKey=d4b2aabc97394a12a27fc3cca6cd9ba1
cloudwalk.datafield.encrypt=AES
# redis\u914D\u7F6E
spring.redis.host=redis_01.redis_ip
spring.redis.port=6379
spring.redis.password=1qaz!QAZ
spring.redis.database=5
spring.redis.timeout=0
spring.redis.pool.max-active=10
spring.redis.pool.max-idle=1
spring.redis.pool.max-wait=10
spring.redis.pool.min-idle=0
# \u6570\u636E\u5E93sharding\u914D\u7F6E
spring.shardingsphere.datasource.names=ds0
spring.shardingsphere.datasource.ds0.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds0.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds0.jdbc-url=jdbc:mysql://mysql_01.mysql_ip:3306/cw-elevator-application?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true
spring.shardingsphere.datasource.ds0.username=cloudwalk
spring.shardingsphere.datasource.ds0.password=1qaz!QAZ
spring.shardingsphere.datasource.ds0.connection-timeout=60000
spring.shardingsphere.datasource.ds0.maximum-pool-size=20
spring.shardingsphere.datasource.ds0.minimum-idle=5
spring.shardingsphere.datasource.ds0.max-lifetime=1765000
spring.shardingsphere.datasource.ds0.auto-commit=true
spring.shardingsphere.datasource.ds0.pool-name=ds0-pool
spring.shardingsphere.props.sql.show=false
spring.shardingsphere.sharding.default-data-source-name=ds0
# \u5FAE\u670D\u52A1\u670D\u52A1\u540D\u914D\u7F6E
feign.device.name=cwos-portal
feign.resource.name=cwos-portal
feign.cwos-portal.name=cwos-portal
feign.davinci-portal.name=cwos-portal
feign.ninca-crk-std.name=ninca-crk-std
feign.component-organization.name=ninca-common-component-organization
feign.ninca-common.name=ninca-common
feign.mqtt.name=cloudwalk-device-thirdparty
# CWOS\u4E8B\u4EF6\u914D\u7F6E
cloudwalk.event.bootstrap-servers=371bfca4972c43d2aefcf302d0a4a277:9092,44700995ee904679a7ad5afddcf93bb5:9092,0837a70b5fab47569391828f5feb2561:9092
cloudwalk.event.group-id=cw-elevator-application-1
cloudwalk.event.handler-executor-config.core-pool-size=10
cloudwalk.event.handler-executor-config.maximum-pool-size=30
# \u5206\u5E03\u5F0F\u9501\u914D\u7F6E
intelligent.lock.enable=true
intelligent.lock.config.default-wait-time=10000
lockWatchdogTimeout=21000
# PERSON_NAME_SPACE
person.name.space=recordEvent
elevator.application.key=xinghewan
elevator.application.time=600
elevator.application.keyA=5B7DEF88FF04
ninca-crk-std.ip=10.0.22.102:16106
#发送第三方数据ip
sendRecord.ip=hrec.star-river.com:32165
#sendRecord.ip=172.16.16.56:32165
sendRecord.token.corpId=53db867a8bb747a1bd04dd1afcad8ca6
sendRecord.token.appKey=293e2d708f0143c2957b702cef44d951
sendRecord.token.appSecret=5f6995009b864669b52041b8f5dc4625
#是否推送
sendRecord.boolean=true
# 设备处理线程池配置
ninca.update.floor.pool.corePoolSize=5
ninca.update.floor.pool.maxPoolSize=5
ninca.update.floor.pool.queueCapacity=100000
ninca.update.floor.pool.keepAliveSeconds=150
ninca.update.floor.pool.allowCoreThreadTimeOut=true
#楼栋id
floor.building.id=605560539791228928
# 诊断探针(ConfigSource / Consul HTTP 逐实例 / Ribbon)固定启用;延迟秒数见源码 cn.cloudwalk.elevator.debug.ElevatorProbeConstants(不再使用 elevator.*.probe.*
# 探针写入 cn.cloudwalk.elevator.debug${logging.path}/${logging.file}-probe.log 见 logback.xml(探针相关日志级别已提升到 WARN)
@@ -0,0 +1,18 @@
server.port=16112
server.tomcat.uri-encoding=UTF-8
spring.application.name=elevator-app
spring.profiles.active=access-control
# consul\u914D\u7F6E
spring.cloud.consul.host=371bfca4972c43d2aefcf302d0a4a277
spring.cloud.consul.port=8500
spring.cloud.consul.enabled=true
spring.cloud.consul.discovery.register=true
spring.cloud.consul.discovery.enabled=false
spring.cloud.consul.discovery.prefer-ip-address=true
spring.cloud.consul.discovery.instance-id=${spring.application.name}-${spring.cloud.client.ipAddress}:${server.port}
spring.cloud.consul.discovery.ip-address=${spring.cloud.client.ipAddress}
spring.cloud.consul.discovery.deregister=false
# zookeeper\u914D\u7F6E
dubbo.registry.address=zookeeper://10.0.22.207:2181
dubbo.protocol.port=16107
dubbo.provider.version=1.0
@@ -0,0 +1,149 @@
#!/usr/bin/env bash
set -euo pipefail
# 生产只读证据采集(Maven 发布包内与本脚本同置于部署根目录,与 start.sh / properties 同层):
# - 进程参数/环境/工作目录
# - 本地配置文件
# - jcmd system properties
# - 应用日志关键片段
# - Consul 健康与 KV 快照
# 最终输出 tar.gz,便于离线定位“配置来源 -> Ribbon 实例列表”问题。
APP_DIR="${1:-/data/cwos/cw-elevator-application-V1.0.0.20211103}"
CONSUL_ADDR="${2:-10.0.22.102:8500}"
OUT_ROOT="${3:-${APP_DIR}/evidence}"
APP_NAME="${4:-elevator-app}"
# 现场 JDK(生产 cwos-node 固定路径;不依赖 PATH
CWOS_JAVA_BIN="/data/cwos/java/bin"
JAVA_BIN="${CWOS_JAVA_BIN}/java"
JAR_BIN="${CWOS_JAVA_BIN}/jar"
JCMD_BIN="${CWOS_JAVA_BIN}/jcmd"
DATE_BIN="/bin/date"
timestamp="$(${DATE_BIN} +%Y%m%d-%H%M%S)"
OUT_DIR="${OUT_ROOT}/elevator-evidence-${timestamp}"
mkdir -p "${OUT_DIR}"
log() { echo "[collect] $*"; }
log "APP_DIR=${APP_DIR}"
log "CONSUL_ADDR=${CONSUL_ADDR}"
log "OUT_DIR=${OUT_DIR}"
log "JAVA_BIN=${JAVA_BIN} JAR_BIN=${JAR_BIN} JCMD_BIN=${JCMD_BIN}"
PID="$(ps -ef | awk '/java/ && /cw-elevator-application/ && !/awk/ {print $2; exit}')"
if [[ -z "${PID}" ]]; then
echo "ERROR: 未找到 cw-elevator-application Java 进程" >&2
exit 1
fi
log "PID=${PID}"
echo "${PID}" > "${OUT_DIR}/pid.txt"
# 1) 进程与系统基础信息
ps -ef > "${OUT_DIR}/ps-ef.txt"
uname -a > "${OUT_DIR}/uname.txt"
${DATE_BIN} +%Y-%m-%dT%H:%M:%S%z > "${OUT_DIR}/collected-at.txt"
tr '\0' ' ' < "/proc/${PID}/cmdline" > "${OUT_DIR}/proc-cmdline.txt" || true
tr '\0' '\n' < "/proc/${PID}/environ" > "${OUT_DIR}/proc-environ.txt" || true
ls -l "/proc/${PID}/cwd" > "${OUT_DIR}/proc-cwd.txt" || true
# 2) 本地配置快照(若存在)
for f in bootstrap.properties application.properties application-access-control.properties start.sh stop.sh cw-elevator-application.service; do
if [[ -f "${APP_DIR}/${f}" ]]; then
cp -a "${APP_DIR}/${f}" "${OUT_DIR}/${f}"
fi
done
# 3) JAR 与结构快照
JAR_PATH="$(awk '{print $1}' "${OUT_DIR}/proc-cmdline.txt" | sed 's/[[:space:]]*$//')"
if [[ -f "${APP_DIR}/cw-elevator-application-V1.0.0.20211103.jar" ]]; then
JAR_PATH="${APP_DIR}/cw-elevator-application-V1.0.0.20211103.jar"
fi
echo "${JAR_PATH}" > "${OUT_DIR}/jar-path.txt"
if [[ -f "${JAR_PATH}" ]]; then
sha256sum "${JAR_PATH}" > "${OUT_DIR}/jar.sha256.txt" || true
if [[ -x "${JAR_BIN}" ]]; then
"${JAR_BIN}" tf "${JAR_PATH}" > "${OUT_DIR}/jar-tf.txt" || true
else
echo "jar not found or not executable: ${JAR_BIN}" > "${OUT_DIR}/jar-tf.txt"
fi
unzip -p "${JAR_PATH}" application.properties > "${OUT_DIR}/jar-application.properties.txt" 2>/dev/null || true
unzip -p "${JAR_PATH}" bootstrap.properties > "${OUT_DIR}/jar-bootstrap.properties.txt" 2>/dev/null || true
fi
# 4) jcmd system properties + attach 诊断(不修改应用配置;便于修复 AttachNotSupportedException
{
echo "=== current shell user ==="
id 2>/dev/null || true
echo "=== target java process ==="
ps -o user=,group=,pid=,args= -p "${PID}" 2>/dev/null || true
PROC_USER="$(stat -c '%U' "/proc/${PID}" 2>/dev/null || echo "")"
PROC_UID="$(stat -c '%u' "/proc/${PID}" 2>/dev/null || echo "")"
echo "proc_owner=${PROC_USER} uid=${PROC_UID}"
echo "=== /tmp hsperfdata (HotSpot perf counter; attach 相关) ==="
if [[ -n "${PROC_USER}" && "${PROC_USER}" != "unknown" ]]; then
HS="/tmp/hsperfdata_${PROC_USER}"
if [[ -d "${HS}" ]]; then
ls -la "${HS}" 2>/dev/null | head -30 || true
ls -la "${HS}/${PID}" 2>/dev/null || echo "missing ${HS}/${PID}"
else
echo "no directory ${HS}"
fi
fi
echo "=== cmdline tokens (attach / jdwp) ==="
tr '\0' '\n' < "/proc/${PID}/cmdline" 2>/dev/null | grep -E 'DisableAttach|Attach|jdwp|agentpath' || echo "(none matched)"
} > "${OUT_DIR}/jcmd-attach-diagnose.txt" 2>&1
if [[ -x "${JCMD_BIN}" ]]; then
"${JCMD_BIN}" "${PID}" VM.system_properties > "${OUT_DIR}/jcmd-system-properties.txt" 2>&1 || true
if grep -q 'AttachNotSupportedException\|Unable to open socket file' "${OUT_DIR}/jcmd-system-properties.txt" 2>/dev/null; then
{
echo ""
echo "HINT: jcmd attach 失败常见原因:"
echo " 1) 与 Java 进程不同用户执行 jcmd(请用与进程相同用户,例如: sudo -u <java_user> ${JCMD_BIN} ${PID} VM.system_properties"
echo " 2) /tmp/hsperfdata_<user>/<pid> 缺失或权限异常"
echo " 3) JVM 启动参数含 -XX:+DisableAttachMechanism(见 jcmd-attach-diagnose.txt 中 cmdline"
echo " 4) 进程非 HotSpot 或尚未完全初始化(极少见于长期运行的 Spring Boot"
} >> "${OUT_DIR}/jcmd-system-properties.txt"
fi
else
echo "jcmd not found or not executable: ${JCMD_BIN}" > "${OUT_DIR}/jcmd-system-properties.txt"
fi
# 4b) java 版本(与现场 JDK 一致性的旁证)
if [[ -x "${JAVA_BIN}" ]]; then
"${JAVA_BIN}" -version > "${OUT_DIR}/java-version.txt" 2>&1 || true
else
echo "java not found or not executable: ${JAVA_BIN}" > "${OUT_DIR}/java-version.txt"
fi
# 5) 应用日志关键行
LOG_FILE="${APP_DIR}/logs/elevator-app.log"
if [[ -f "${LOG_FILE}" ]]; then
cp -a "${LOG_FILE}" "${OUT_DIR}/elevator-app.log.full"
awk '
/CONFIG SOURCE PROBE START|CONFIG SOURCE PROBE END|probe key=|ConfigurationBasedServerList|Load balancer does not have available server|DynamicServerListLoadBalancer|ConsulServiceRegistry|Registering service with consul/ { print }
' "${LOG_FILE}" > "${OUT_DIR}/elevator-app.log.keylines.txt"
fi
# 6) Consul 快照
CURL="curl -sS --max-time 8"
${CURL} "http://${CONSUL_ADDR}/v1/health/service/${APP_NAME}?passing=true" > "${OUT_DIR}/consul-health-${APP_NAME}.json" || true
for svc in cwos-portal ninca-common ninca-common-component-organization ninca-crk-std cloudwalk-device-thirdparty; do
${CURL} "http://${CONSUL_ADDR}/v1/health/service/${svc}?passing=true" > "${OUT_DIR}/consul-health-${svc}.json" || true
done
${CURL} "http://${CONSUL_ADDR}/v1/kv/config/${APP_NAME}/data?raw" > "${OUT_DIR}/consul-kv-${APP_NAME}.properties" || true
${CURL} "http://${CONSUL_ADDR}/v1/kv/config/${APP_NAME},access-control/data?raw" > "${OUT_DIR}/consul-kv-${APP_NAME},access-control.properties" || true
# 7) 现场可达性快照(已知主机名)
for host in 0837a70b5fab47569391828f5feb2561 371bfca4972c43d2aefcf302d0a4a277 44700995ee904679a7ad5afddcf93bb5; do
getent hosts "${host}" > "${OUT_DIR}/getent-${host}.txt" 2>&1 || true
curl -I --max-time 5 "http://${host}:8089/" > "${OUT_DIR}/curl-head-${host}-8089.txt" 2>&1 || true
done
ARCHIVE="${OUT_DIR}.tar.gz"
tar -czf "${ARCHIVE}" -C "$(dirname "${OUT_DIR}")" "$(basename "${OUT_DIR}")"
log "DONE archive=${ARCHIVE}"
echo "${ARCHIVE}"
@@ -0,0 +1,38 @@
#!/usr/bin/env bash
# shellcheck shell=bash
# 由 v1-legacy/run.sh、v2-maven/run.sh sourceJAVA_HOME;非 JDK8 时追加 --add-opens。
#
# === 本机 JDK 8 安装根目录(含 bin/java);换机器只需改下行默认路径或通过环境变量覆盖 ===
: "${DEPLOY_JDK8:=/usr/lib/jvm/java-8-openjdk-amd64}"
_pick_java_home() {
if [[ "${ELEVATOR_USE_ENV_JAVA:-0}" == "1" ]] && [[ -n "${JAVA_HOME:-}" && -x "${JAVA_HOME}/bin/java" ]]; then
return 0
fi
if [[ -x "${DEPLOY_JDK8}/bin/java" ]]; then
export JAVA_HOME="${DEPLOY_JDK8}"
return 0
fi
for d in /usr/lib/jvm/java-8-openjdk-amd64 /usr/lib/jvm/java-1.8.0-openjdk; do
if [[ -x "$d/bin/java" ]]; then
export JAVA_HOME="$d"
return 0
fi
done
if [[ -n "${JAVA_HOME:-}" && -x "${JAVA_HOME}/bin/java" ]]; then
return 0
fi
export JAVA_HOME="${JAVA_HOME:-${DEPLOY_JDK8}}"
}
_jdk8_open_flags() {
local java="$1"
if "$java" -version 2>&1 | grep -qE 'version "1\.8\.'; then
echo ""
return
fi
echo "--add-opens=java.base/java.lang=ALL-UNNAMED"
echo "--add-opens=java.base/java.lang.reflect=ALL-UNNAMED"
echo "--add-opens=java.base/java.util=ALL-UNNAMED"
echo "--add-opens=java.base/java.io=ALL-UNNAMED"
}
@@ -0,0 +1,23 @@
[Unit]
Description=cw-elevator-application 2.0.9 (Maven V2 fat jar)
After=network.target remote-fs.target nss-lookup.target
[Service]
# 将 WorkingDirectory、ExecStart、ExecStop 中的路径占位改为实际部署绝对路径(与 JAR、start.sh、properties 同目录)。
PIDFile=/run/cw-elevator-application-2.0.9.pid
ExecStartPre=/bin/rm -f /run/cw-elevator-application-2.0.9.pid
ExecStart=/bin/bash /path/to/cw-elevator-application/start.sh
ExecStop=/bin/bash /path/to/cw-elevator-application/stop.sh
ExecReload=/bin/kill -s HUP $MAINPID
KillSignal=SIGQUIT
TimeoutStopSec=5
KillMode=process
PrivateTmp=true
Restart=always
RestartSec=10
StandardOutput=null
StandardError=null
WorkingDirectory=/path/to/cw-elevator-application
[Install]
WantedBy=multi-user.target
@@ -0,0 +1,27 @@
-- 租户访客默认楼层策略(电梯应用库)
-- 设计说明:docs/business/租户访客默认楼层-数据库配置阶段技术设计.md
CREATE TABLE IF NOT EXISTS tenant_visitor_floor_policy (
id VARCHAR(32) NOT NULL COMMENT '主键',
business_id VARCHAR(64) NOT NULL COMMENT '机构/租户 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_biz_building (business_id, building_id),
KEY idx_business_enabled (business_id, enabled)
) 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);
@@ -0,0 +1,47 @@
-- 广发基金租户:访客默认楼层策略初始化(电梯库 cw-elevator-application
--
-- 请先执行同目录 tenant_visitor_floor_policy.sql 建表(或 releases/v2.0.0/ddl 同源 DDL)。
--
-- 字段说明:allow_zone_ids 为 JSON 数组字符串,元素须与组织/派梯侧 floorList、image_rule_ref 使用的
-- zone_id(电梯库 snowflake 形态)一致,勿使用 cw_is_area 的 UUID。
--
-- 数据来源(现场查询 192.168.3.12:3307,截至脚本编写日):
-- business_idcomponent-organization.cw_is_organization
-- NAME='[28-38F]广发基金管理有限公司' -> BUSINESS_ID = 2524639890ba4f2cba9ba1a4eeaa4015
-- 28F zone_idcw-elevator-application.code_elevator_area / image_rule_ref
-- zone_id = 605560545117995008zone_name=28Fcode=0x1C
--
-- 重复执行:依赖主键 id 固定,使用 ON DUPLICATE KEY UPDATE 刷新策略字段。
SET NAMES utf8mb4;
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 (
'gf_vstr_policy_guangfa_fund_001x',
'2524639890ba4f2cba9ba1a4eeaa4015',
'INTERSECT_ALLOWLIST',
'["605560545117995008"]',
NULL,
1,
1,
'广发基金:访客与 floorList 求交后仅保留 allowlist(默认仅 28F zone)。业务配置见产品方案。',
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,38 @@
#!/usr/bin/env bash
# 与当前目录下 application.properties 同路径启动 V2maven 构建)包。
# 默认优先系统 JDK 8;若只有 JDK11+ 会自动附加 --add-opens。
# ELEVATOR_USE_ENV_JAVA=1 ./run.sh 使用当前 JAVA_HOME(如 Conda)。
# 发布包内脚本与 JAR、properties 均位于发布根目录(与 cw-elevator-application-V1.0.0.20211103 布局一致)。
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=../common-java.sh
source "${SCRIPT_DIR}/../common-java.sh"
cd "$SCRIPT_DIR"
JAR=""
for candidate in $(ls -1t cw-elevator-application-*.jar 2>/dev/null || true); do
if [[ "${candidate}" == *.jar.original ]]; then
continue
fi
JAR="${candidate}"
break
done
if [[ -z "${JAR}" || ! -f "${JAR}" ]]; then
echo "缺少 cw-elevator-application-*.jar,请在 deploy 目录执行: ./sync-jars.sh" >&2
exit 1
fi
_pick_java_home
if [[ ! -x "${JAVA_HOME}/bin/java" ]]; then
echo "ERROR: 未找到可执行的 JDK。请安装 openjdk-8-jdk,或设定 JAVA_HOME / ELEVATOR_USE_ENV_JAVA=1 ./run.sh(使用 Conda 等当前环境)。" >&2
exit 1
fi
JAVA="${JAVA_HOME}/bin/java"
OPEN_FLAGS=()
while IFS= read -r line; do
[[ -n "$line" ]] && OPEN_FLAGS+=("$line")
done < <(_jdk8_open_flags "$JAVA")
# 强制走 Consul:不再注入本地 application*.properties / redis-override.properties。
# shellcheck disable=SC2086
exec "$JAVA" "${OPEN_FLAGS[@]}" ${ELEVATOR_JAVA_OPTS:-} -jar "$JAR"
@@ -0,0 +1,23 @@
#!/usr/bin/env bash
# 与「星中心」V1 部署习惯对齐:本脚本与 cw-elevator-application-2.0.9.jar、bootstrap/application*.properties 位于同一目录。
# 覆盖 JVM:设置环境变量 ELEVATOR_JAVA_OPTS(可选);指定 JavaJAVA_HOME 或 JAVA_CMD。
set -euo pipefail
dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
cd "$dir"
JAR="cw-elevator-application-2.0.9.jar"
if [[ ! -f "$JAR" ]]; then
echo "ERROR: 未找到 ${JAR}(当前目录 $(pwd)" >&2
exit 1
fi
if [[ -n "${JAVA_CMD:-}" ]]; then
JAVA_EXEC="$JAVA_CMD"
elif [[ -n "${JAVA_HOME:-}" && -x "${JAVA_HOME}/bin/java" ]]; then
JAVA_EXEC="${JAVA_HOME}/bin/java"
else
JAVA_EXEC="/usr/lib/jvm/java-8-openjdk-amd64/bin/java"
fi
if [[ -z "${ELEVATOR_JAVA_OPTS:-}" ]]; then
ELEVATOR_JAVA_OPTS="-Xmx3072m -Xms3072m -Xmn1024m"
fi
# shellcheck disable=SC2086
exec "$JAVA_EXEC" $ELEVATOR_JAVA_OPTS -jar "$JAR"
@@ -0,0 +1,9 @@
#!/usr/bin/env bash
# 停止与本目录 cw-elevator-application-2.0.9.jar 对应的 Java 进程(仅匹配命令行中含该 JAR 名的进程)。
set -euo pipefail
JAR="cw-elevator-application-2.0.9.jar"
# shellcheck disable=SC2009
pid=$(ps -ef | grep '[j]ava' | grep "$JAR" | awk '{print $2}' || true)
if [[ -n "${pid}" ]]; then
kill -9 $pid
fi
@@ -0,0 +1,106 @@
# cw-elevator-application v2.0.6 升级计划
**项目名称**(可填):智慧电梯 / 访客派梯系统升级
**版本**v2.0.6
**计划性质**:执行级排期草案,**具体日期、起止时刻以与甲方书面/邮件确认为准**。
---
## 1. 升级目标
完成电梯派梯应用 **v2.0.6** 上线,启用**租户访客固定访问楼层**能力所需的应用与数据库对象;按业务需要为指定租户配置策略并完成验收。
---
## 2. 计划时间窗口(夜间 · 暂定周二、周三)
| 窗口 | 意向安排 | 建议内容(可合并或调整) |
|------|-----------|----------------------------|
| **第一次:周二晚间** | 夜间低峰段,具体 **HH:MMHH:MM** 待定 | 环境检查、数据库备份、执行 **DDL**(新建策略表)、(可选)预发布验证、必要时准备回滚包与检查单 |
| **第二次:周三晚间** | 夜间低峰段,具体 **HH:MMHH:MM** 待定 | 部署新版本 **JAR**、滚动/重启应用、按需 **INSERT** 租户策略数据、业务联调与验收、监控与值守 |
**说明**
- 若贵方现场要求**一个晚上完成全部步骤**,可将 DDL 与部署**合并在同一晚间窗口**执行,本表仅体现「周二、周三两晚」的**当前意向拆分**,最终以确认单为准。
- 两次窗口建议**间隔至少数小时至一个工作日**,便于第一次变更后观察库表与低风险项;若合并为单次窗口,须在计划单中注明**连续操作顺序与回滚点**。
**待确认栏**(实施时填写):
- 周二实施日期:`____年____月____日`,时段: `____ : ____` `____ : ____`
- 周三实施日期:`____年____月____日`,时段: `____ : ____` `____ : ____`
- 甲方现场联系人: `____________` 乙方/实施负责人: `____________`
---
## 3. 影响范围与沟通
| 项目 | 说明 |
|------|------|
| 影响系统 | 电梯派梯相关应用(`cw-elevator-application`)及同一业务库。 |
| 用户感知 | 应用重启期间可能出现短时派梯接口失败;策略误配可能导致部分访客路径失败,需按验收清单核对。 |
| 通知范围 | 建议提前通知:物业/客服、前台与访客登记、安保与梯控相关值班(按项目实际 roster 确定)。 |
---
## 4. 前置条件(升级前)
- [ ] 已与甲方确认 **周二 / 周三** 夜间窗口。
- [ ] 取得 **v2.0.6** 发布包(含 `jar``ddl/tenant_visitor_floor_policy.sql`、说明书)。
- [ ] 目标环境 **JDK 版本**符合实施方要求(与构建说明一致,一般为 JDK 8)。
- [ ] 数据库已做**备份**(全库或按运维规范),并可从发布包定位 DDL。
- [ ] 明确需启用「固定访客楼层」的租户列表及**允许区域**配置(若不启用,可跳过策略数据录入,行为与升级前一致)。
- [ ] 回滚包:保留**当前线上 JAR** 备份与回滚步骤(见下文)。
---
## 5. 实施步骤(建议顺序)
### 5.1 周二晚间(或首个窗口)
1. 备份数据库。
2. 执行 `tenant_visitor_floor_policy.sql``CREATE TABLE IF NOT EXISTS`,可重复执行需与 DBA 确认)。
3. (可选)在测试/预发环境先执行一遍并验证。
4. 记录执行人、时间与结果。
### 5.2 周三晚间(或第二个窗口 / 同晚续作)
1. 停止或滚动发布应用(按现网规范)。
2. 替换为 `cw-elevator-application-2.0.6.jar` 并启动。
3. 按业务需求对需启用的租户执行策略 **INSERT**`enabled=1``allow_zone_ids` 等为合法 JSON 等,字段级以技术说明书为准)。
4. 抽样验证:未传显式楼层时的访客派梯、租户策略开/关、与组织楼层无交集时的失败提示等。
5. 观察监控与日志,**值守**至约定结束时间。
*若合并为单次窗口:按「备份 → DDL → 部署 JAR → 策略数据 → 验收」顺序连续执行,并预留回滚决策时间。*
---
## 6. 验收要点(摘要)
- 未配置或未启用策略时:与升级前行为一致。
- 已启用策略且业务走「未显式指定楼层」路径:访客可去楼层为**组织允许**与**租户允许**的**交集**。
- 交集为空时:接口返回预期业务错误(技术码见实施方说明书)。
- 第三方已显式传入楼层的路径:不因本策略表改变原逻辑。
---
## 7. 回滚预案
| 场景 | 建议动作 |
|------|-----------|
| 应用异常 | 回退至上一版本 JAR;数据库新表若已存在且旧应用不读该表,通常可继续服务;与实施方确认。 |
| 策略配置错误 | 优先**停用或修正策略行**,避免大规模回滚应用。 |
| 必须撤表 | 仅在评估无依赖后由 DBA 执行删表;**高风险**,需书面确认。 |
---
## 8. 交付物核对
- [ ] `cw-elevator-application-2.0.6.jar`
- [ ] `ddl/tenant_visitor_floor_policy.sql`
- [ ] 《版本升级说明书》(实施/技术)
- [ ] 本《甲方版本升级说明》(如已作为对甲交付)
- [ ] 本《升级计划》(双方确认签字/邮件留档)
---
**文档状态**:草案;**时间点为暂定周二、周三晚间**,正式实施前请更新「待确认栏」并留存确认记录。
@@ -0,0 +1,32 @@
# cw-elevator-application v2.0.6 发布索引
本版本**功能升级说明**已单独成文,**仅覆盖「租户访客固定访问楼层」**及配套数据库 DDL,见:
**[cw-elevator-application-v2.0.6-版本升级说明书](./cw-elevator-application-v2.0.6-版本升级说明书.md)**
**对甲交付(业务表述与排期)**
- [甲方版本升级说明](./cw-elevator-application-v2.0.6-甲方版本升级说明.md)
- [升级计划(含夜间 · 周二/周三窗口)](./cw-elevator-application-v2.0.6-升级计划.md)
- [实施交付清单(实施方内部与对甲交付核对)](./cw-elevator-application-v2.0.6-实施交付清单.md)
- [实施验收记录模板(上线当晚记录)](./cw-elevator-application-v2.0.6-实施验收记录模板.md)
- [SQL与代码一致性审核记录(发布规范走查依据)](./cw-elevator-application-v2.0.6-SQL与代码一致性审核记录.md)
---
## 一键构建与发布目录
在仓库根目录、**JDK 8** 下执行:
```bash
export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64
./scripts/release-cw-elevator-application.sh 2.0.6
```
输出:**`maven-cw-elevator-application/releases/cw-elevator-application-V2.0.6.<YYYYMMDD>/`**(目录名含构建日期,与 `cw-elevator-application-V1.0.0.20211103` 命名风格一致;可用环境变量 **`RELEASE_DATE_LABEL`** 指定日期),含 `cw-elevator-application-2.0.6.jar``ddl/`、发布根目录下的 `bootstrap.properties` / `application*.properties`(与星中心/V1 同层摆放,**无额外 `config/` 子目录**)、`start.sh` / `stop.sh` / `cw-elevator-application.service`(路径占位符需现场替换)、`run.sh``common-java.sh`、**`collect_elevator_runtime_evidence.sh`**(根目录,现场只读证据采集)、`版本升级说明书.md`、甲方与实施类 Markdown、`BUILD_MANIFEST.txt`;默认另生成 **`releases/cw-elevator-application-V2.0.6.<YYYYMMDD>.zip`**(设 `RELEASE_MAKE_ZIP=0` 可跳过)。
---
## Git 与大文件
`maven-cw-elevator-application/.gitignore` 忽略 `**releases/**/*.jar**`;**DDL 与说明书**可提交;可执行 JAR 请通过制品库或制品服务器分发。
@@ -0,0 +1,60 @@
# cw-elevator-application v2.0.6 实施交付清单
**用途**:用于正式发布交付前后的材料核对,满足“发布包 + 数据库脚本 + 升级说明 + 实施验收记录”闭环。
---
## 1. 发布包文件清单
- [ ] `cw-elevator-application-2.0.6.jar`
- [ ] `ddl/tenant_visitor_floor_policy.sql`(建表脚本)
- [ ] `ddl/tenant_visitor_floor_policy_init_guangfa_fund.sql`(广发基金初始化示例/可直接执行)
- [ ] `版本升级说明书.md`(技术实施口径)
- [ ] `甲方版本升级说明.md`(业务口径)
- [ ] `升级计划.md`(实施窗口与回滚安排)
- [ ] `实施验收记录模板.md`(上线当晚记录)
- [ ] `BUILD_MANIFEST.txt`(构建时间、分支、提交号)
---
## 2. 数据库变更交付项
- [ ] DBA 已确认目标库与执行窗口
- [ ] 已备份目标库(全库/指定库按现场规范)
- [ ] 已执行 `tenant_visitor_floor_policy.sql`
- [ ] 已执行或评审 `tenant_visitor_floor_policy_init_guangfa_fund.sql`
- [ ] 已确认 `allow_zone_ids` 使用的是电梯库 `zone_id`(非其它系统 UUID
- [ ] 已留存 SQL 执行记录(执行人、时间、结果)
---
## 3. 应用部署交付项
- [ ] 上线前已保存旧版 JAR 回滚包
- [ ] 已按窗口替换为 `cw-elevator-application-2.0.6.jar`
- [ ] 应用启动日志无严重报错
- [ ] 关键接口健康检查通过(含 `add/visitor` 主链路)
- [ ] 监控观察窗口内无持续异常
---
## 4. 验收交付项
- [ ] UC-01:未传 `floorIds` 且策略启用时,楼层为 `floorList ∩ allow_zone_ids`
- [ ] UC-01:无交集时返回 `76260532`
- [ ] UC-02:显式传 `floorIds` 时不受策略表影响
- [ ] 未启用策略租户行为与升级前一致
- [ ] 已按《实施验收记录模板》完成签字/邮件留档
---
## 5. 交付确认信息
| 项目 | 内容 |
|------|------|
| 实施项目 | cw-elevator-application v2.0.6 |
| 实施日期 | `____年____月____日` |
| 甲方确认人 | `____________` |
| 乙方实施人 | `____________` |
| 文档留存路径 | `____________` |
@@ -0,0 +1,73 @@
# cw-elevator-application v2.0.6 实施验收记录模板
**用途**:实施当晚记录数据库执行、应用部署、业务验收和回滚判定,作为交付归档依据。
---
## 1. 基本信息
| 项目 | 内容 |
|------|------|
| 项目名称 | 智慧电梯 / 访客派梯系统 |
| 发布版本 | v2.0.6 |
| 实施日期 | `____年____月____日` |
| 实施时段 | `____:____` - `____:____` |
| 环境 | 生产 / 预生产(圈选) |
| 实施负责人 | `____________` |
| 甲方联系人 | `____________` |
---
## 2. 数据库执行记录
| 序号 | 脚本 | 执行时间 | 执行人 | 结果 | 备注 |
|------|------|----------|--------|------|------|
| 1 | tenant_visitor_floor_policy.sql | `____` | `____` | 成功/失败 | `____` |
| 2 | tenant_visitor_floor_policy_init_guangfa_fund.sql | `____` | `____` | 成功/失败 | `____` |
**异常记录**`______________________________________________`
---
## 3. 应用发布记录
| 项目 | 记录 |
|------|------|
| 上线前版本 | `____________` |
| 上线后版本 | `cw-elevator-application-2.0.6.jar` |
| 启停方式 | `____________` |
| 服务恢复时间 | `____________` |
| 日志健康检查 | 通过 / 不通过 |
| 监控观察结论 | 正常 / 异常(说明) |
---
## 4. 验收结果记录
| 用例 | 期望 | 实际 | 结论 |
|------|------|------|------|
| UC-01(未传 floorIds,策略启用) | floorList 与 allow_zone_ids 求交 | `____` | 通过/不通过 |
| UC-01(交集为空) | 返回 `76260532` | `____` | 通过/不通过 |
| UC-02(显式传 floorIds | 不受策略表影响 | `____` | 通过/不通过 |
| 无策略租户回归 | 行为与升级前一致 | `____` | 通过/不通过 |
---
## 5. 回滚判定
- [ ] 无需回滚,发布成功
- [ ] 需要回滚应用(原因:`________________`
- [ ] 需要回滚数据/策略(原因:`________________`
回滚执行记录(如发生):`______________________________________________`
---
## 6. 签字确认
| 角色 | 姓名 | 日期 | 备注 |
|------|------|------|------|
| 甲方确认 | `____________` | `____` | `____` |
| 乙方实施 | `____________` | `____` | `____` |
| 乙方复核 | `____________` | `____` | `____` |
@@ -0,0 +1,112 @@
# cw-elevator-application v2.0.6 版本升级说明书
**适用范围**:本说明**仅**描述 **v2.0.6** 中与 **租户访客固定访问楼层**(租户级允许区域与组织 `floorList` 求交)相关的升级内容,不含其它性能优化或通用发布项。
**应用制品**`cw-elevator-application-2.0.6.jar`Spring Boot 可执行 fat JAR)。
---
## 1. 功能概述
**访客派梯** 接口 **`add/visitor`**(实现类 `PersonRuleServiceImpl#addVisitor`)中,当调用方 **未传入非空 `floorIds`**(业务流程 **UC-01**:按被访人组织侧楼层推导)时:
1. 服务仍先通过 **`person/detail`** 取得被访人 **`floorList`**(与升级前一致)。
2. **新增**:若数据库中存在**启用**的租户策略行,且 **`allow_zone_ids`** 解析为非空 JSON 数组,则最终生效楼层为
**`effectiveFloors = floorList ∩ allow_zone_ids`**(顺序保持 **`floorList`** 原有顺序)。
3. 若交集 **为空**,接口返回失败码 **`76260532`**(不允许静默放宽到其它楼层)。
4. 若无策略表、无启用行、`allow_zone_ids` 为空或 JSON 无效:行为与升级前一致,使用 **`floorList` 全集**。
当调用方 **已传入非空 `floorIds`**(**UC-02**,第三方显式指定楼层)时:**不读取**策略表,**不对入参求交**,与升级前一致。
> **产品表述**:「固定访问楼层」在本阶段由租户管理员通过 **`allow_zone_ids`** 声明**允许开放给访客派梯的区域(zoneId)集合**;在 UC-01 路径下与组织返回的 **`floorList`** 取交集,从而将访客权限**收敛**到既有「被访人可去楼层」与「租户允许楼层」的双重约束内。
---
## 2. 涉及的数据库变更
### 2.1 变更类型
| 类型 | 对象 |
|------|------|
| **新增表** | `tenant_visitor_floor_policy` |
**执行库**:与电梯应用 **同一数据源**MySQL/InnoDB,与现有派梯业务库一致)。
### 2.2 表用途(摘要)
| 字段 | 含义 |
|------|------|
| `business_id` | 租户/机构 ID |
| `policy_type` | 本阶段固定 **`INTERSECT_ALLOWLIST`** |
| `allow_zone_ids` | **TEXT**,存 **JSON 数组**字符串,元素为允许访客派梯的 **zoneId** |
| `building_id` | 租户级默认策略填 **NULL**(预留楼栋维) |
| `enabled` | **1** 启用 / **0** 停用 |
| `policy_version` | 配置版本号(审计) |
唯一约束 **`uk_biz_building (business_id, building_id)`**:同一租户、租户级策略(`building_id` 为空)**建议仅维护一行**,避免多行时查询仅命中「最新一条」与运维预期不符。
### 2.3 DDL 脚本位置
| 用途 | 仓库内路径 |
|------|------------|
| **权威 DDL** | `docs/sql/tenant_visitor_floor_policy.sql` |
**发布包内副本**:随 v2.0.6 发布目录一并下发,路径为 **`ddl/tenant_visitor_floor_policy.sql`**(与根目录 JAR 同级下的 `ddl` 子目录)。
脚本内容为 **`CREATE TABLE IF NOT EXISTS`**,在未建表环境可重复执行;**不含**业务数据 `INSERT`,上线需按租户配置自行 **`INSERT`** 策略行。
### 2.4 上线执行顺序(建议)
1. **备份**当前电梯应用库(至少包含待变更库的全库或相关表备份策略)。
2. 在目标库执行 **`ddl/tenant_visitor_floor_policy.sql`**(或直接使用仓库 `docs/sql` 下同源文件)。
3. 对需启用策略的租户 **`INSERT`** 一行(示例字段:`business_id``policy_type='INTERSECT_ALLOWLIST'``allow_zone_ids` 为合法 JSON 数组、`enabled=1``building_id`**NULL**)。
4. 部署 **`cw-elevator-application-2.0.6.jar`** 并滚动重启应用。
5. 按 §4 做 **UC-01 / UC-02** 验收。
### 2.5 回滚说明
- **仅回滚应用**:还原旧版本 JAR 后,若库中**已存在**策略行且仍为 **enabled=1**,旧版本应用**通常不读取**该表,行为与历史一致;表结构可保留。
- **回滚数据库**:若需删除表(谨慎),在确认无其它依赖后执行 **`DROP TABLE tenant_visitor_floor_policy`**;请在变更窗口与 DBA 确认。
---
## 3. 行为与错误码(验收)
| 场景 | `floorIds` | 策略库 | 期望 |
|------|------------|--------|------|
| UC-01 基线 | 空/未传 | 无启用行或 allow 空/无效 | 使用组织 **`floorList` 全集**(与升级前一致) |
| UC-01 + 固定楼层 | 空/未传 | 有启用行且 allow 非空 | **`floorList ∩ allow`**(保序) |
| 无交集 | 空/未传 | allow 与 `floorList` 无交集 | **`76260532`** |
| 被访人无楼层 | 空/未传 | 任意 | **`76260531`** |
| UC-02 | **非空** | 任意 | **不读策略表**,按请求楼层处理 |
---
## 4. 发布包目录结构(v2.0.6
执行仓库根目录 **`./scripts/release-cw-elevator-application.sh 2.0.6`** 后,输出目录为(名称含构建日期 `<YYYYMMDD>`,与历史运行包 **`cw-elevator-application-V1.0.0.20211103`** 同构;可用环境变量 **`RELEASE_DATE_LABEL`** 固定日期):
**`maven-cw-elevator-application/releases/cw-elevator-application-V2.0.6.<YYYYMMDD>/`**
| 文件/目录 | 说明 |
|-----------|------|
| `cw-elevator-application-2.0.6.jar` | 可执行应用 |
| `bootstrap.properties``application*.properties` | 与 JAR 同层(**不**再使用 `config/` 子目录重复存放) |
| `ddl/tenant_visitor_floor_policy.sql` | 与本功能相关的 **唯一 DDL**(与 `docs/sql` 同源) |
| `版本升级说明书.md` | 本文件副本(便于随包交付) |
| `BUILD_MANIFEST.txt` | 构建时间、JDK、`git` 修订号 |
---
## 5. 参考文档(仓库内)
| 文档 | 路径 |
|------|------|
| 数据库阶段变更记录 | `docs/business/租户访客默认楼层-数据库阶段变更记录.md` |
| 数据库配置阶段技术设计 | `docs/business/租户访客默认楼层-数据库配置阶段技术设计.md` |
| 技术产品方案 | `docs/business/租户访客默认楼层技术产品方案.md` |
| 访客注册与派梯楼层走查 | `docs/business/访客注册与派梯楼层业务流程走查.md` |
---
**文档版本**:与制品 **`cw-elevator-application-2.0.6`** 对齐;若仅升级文档而不改代码,请以 **`BUILD_MANIFEST.txt`** 中构建时间为准。
@@ -0,0 +1,34 @@
# 电梯派梯应用 v2.0.6 — 版本升级说明(广发基金)
**制品**`cw-elevator-application-2.0.6.jar`。技术细节、验收码与脚本位置见同目录《版本升级说明书》。
---
## 本次升级做什么
针对**广发基金**租户:访客走常见派梯路径(接口**未单独传楼层**)时,系统在「被访人所在单位给的可去楼层」基础上,再按**广发基金侧配置的允许区域**做一次收紧,两边**都满足**的楼层才能派梯。这样可以把访客权限收在比如固定接待层,而员工本人仍可按组织权限去多层办公。
广发基金若**未配置或未启用**该策略,行为与现在一致。
---
## 上线要动什么
- 换新版应用包。
- 库里**多一张策略表**(脚本在发布包 `ddl/` 下);表里为广发基金写入**一行**启用策略即可,允许区域(如接待层对应的 zone)由业务与实施定稿。
其它租户不配策略则**不受影响**。
---
## 对您这边的影响
- **时间**:安排在**夜间**,具体周二/周三窗口见《升级计划》。
- **中断**:重启应用时派梯接口可能**短暂**不可用,一般会控制在很短时间。
- **配合**:指定一个对接人;确认广发基金侧要开放的**访客可达区域/楼层**(与前台、接待流程对齐);上线后若有异常派梯请及时反馈。
---
## 出问题怎么办
应用可先退回上一版包;策略数据可单独改/停,不一定整库回滚。细则见《升级计划》。
@@ -0,0 +1 @@
ecebd1389de3e1774d873f6a665946f3aef821e0
@@ -0,0 +1 @@
456d1fbf4cf407d3702606ea37e8bffee35e7ce5
@@ -0,0 +1 @@
5b5b9bff16ac8ab3bc5a2fc9324239d75cfdb301
@@ -0,0 +1,54 @@
# 电梯应用 v2.0.10 — 服务发现修复 + org_id 策略验证
## 本版变更
| 变更项 | 说明 |
|--------|------|
| 移除 @RibbonClients | ElevatorApplication 不再绑定任何 RibbonClient,三个上游服务统一走 Dubbo/ZooKeeper 发现 |
| 删除 RibbonConfiguration | NincaCrkStdRibbonConfiguration / OrgService / CommonService 全部删除 |
| 测试数据更新 | 人员/zone 替换为生产 DB 核验后的真实数据 |
## 部署
```bash
tar xzf cw-elevator-application-v2.0.10-*.tar.gz
cd cw-elevator-application-v2.0.10-*
# 1. DBA 写入测试策略
mysql -h <host> -u <user> -p cw-elevator-application < sql/01-test-policy-data.sql
# 2. 替换 JAR
cp cw-elevator-application-v2.0.10.jar /path/to/deploy/
./stop.sh && ./start.sh
# 3. 验证 (DBA 先准备策略数据,无需 DB 直连)
python3 scripts/verify_org_policy_fix.py --elevator-base-url http://127.0.0.1:18081 --skip-db
# 4. 清理
mysql -h <host> -u <user> -p cw-elevator-application < sql/02-test-data-cleanup.sql
```
## 测试人员 (均来自生产 DB,有 device → org → floorList 完整数据)
| 用例 | person_id | 姓名 | 组织 | devices |
|------|-----------|------|------|---------|
| T1/T3/T4/T5/T6 | 1060601250460012544 | 丘文明 | 1403艾斯 | 122 |
| T2 | 1090914042800263168 | 陈美全 | 1405一博环保 | 116 |
| T7 | 964454497399468032 | 蒙海文 | 广发基金 | 279 |
## 文件清单
```
├── cw-elevator-application-v2.0.10.jar
├── README.md
├── sql/
│ ├── 01-test-policy-data.sql # 准备测试策略
│ ├── 02-test-data-cleanup.sql # 清理
│ └── 03-t3-manual-ops.sql # T3 skip-db 手动操作
├── scripts/
│ ├── verify_org_policy_fix.py # 7用例验证
│ ├── stub_org_service.py # 本地测试桩
│ └── diag_person_floors.sh # 楼层诊断
└── config/
└── v2-local-config.properties # 本地测试配置
```
@@ -0,0 +1,7 @@
spring.cloud.consul.host=192.168.3.12
spring.cloud.consul.port=8500
spring.cloud.consul.enabled=true
spring.cloud.consul.discovery.enabled=true
spring.cloud.consul.discovery.register=false
spring.cloud.consul.discovery.prefer-ip-address=true
spring.cloud.consul.discovery.instance-id=elevator-app-test:18081
@@ -0,0 +1,70 @@
server.port=18081
logging.path=/tmp/elevator-logs
logging.file=cw-elevator
spring.cloud.consul.host=192.168.3.12
spring.cloud.consul.port=8500
spring.cloud.consul.enabled=true
spring.redis.host=127.0.0.1
spring.redis.port=6380
spring.redis.database=5
spring.redis.timeout=3000
spring.redis.pool.max-active=10
spring.redis.pool.max-idle=1
spring.redis.pool.max-wait=10
spring.redis.pool.min-idle=0
spring.shardingsphere.datasource.names=ds0
spring.shardingsphere.datasource.ds0.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds0.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds0.jdbc-url=jdbc:mysql://192.168.3.12:3307/cw-elevator-application?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true
spring.shardingsphere.datasource.ds0.username=root
spring.shardingsphere.datasource.ds0.password=123456
spring.shardingsphere.datasource.ds0.connection-timeout=60000
spring.shardingsphere.datasource.ds0.maximum-pool-size=20
spring.shardingsphere.datasource.ds0.minimum-idle=5
spring.shardingsphere.datasource.ds0.max-lifetime=1765000
spring.shardingsphere.datasource.ds0.auto-commit=true
spring.shardingsphere.datasource.ds0.pool-name=ds0-pool
spring.shardingsphere.props.sql.show=false
spring.shardingsphere.sharding.default-data-source-name=ds0
cloudwalk.event.bootstrap-servers=127.0.0.1:9092
cloudwalk.event.group-id=cw-elevator-local
feign.device.name=cwos-portal
feign.resource.name=cwos-portal
feign.cwos-portal.name=cwos-portal
feign.davinci-portal.name=cwos-portal
feign.ninca-crk-std.name=ninca-crk-std
feign.component-organization.name=ninca-common-component-organization
feign.ninca-common.name=ninca-common
feign.mqtt.name=cloudwalk-device-thirdparty
feign.hystrix.enable=true
feign.okhttp.enable=true
ribbon.okhttp.enabled=true
ribbon.ReadTimeout=10000
ribbon.ConnectTimeout=10000
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=10000
spring.mvc.throw-exception-if-no-handler-found=true
spring.messages.basename=access-control
mybatis.mapper-locations=classpath*:cn/cloudwalk/elevator/**/*.xml
mybatis.config-location=classpath:mapper/mybatis-config.xml
cloudwalk.serial.enable=true
cloudwalk.serial.serial-type=redis
cloudwalk.serial.serial-redis-key=CLOUDWALK-ACS-SERIAL-KEY
management.health.redis.enabled=false
management.health.db.enabled=false
cloudwalk.datafield.enable=true
cloudwalk.datafield.securityKey=d4b2aabc97394a12a27fc3cca6cd9ba1
intelligent.lock.enable=true
intelligent.lock.config.default-wait-time=10000
lockWatchdogTimeout=21000
person.name.space=recordEvent
elevator.application.key=xinghewan
elevator.application.time=600
elevator.application.keyA=5B7DEF88FF04
ninca-crk-std.ip=127.0.0.1:16106
sendRecord.boolean=false
floor.building.id=605560539791228928
spring.redis.password=1qaz!QAZ
ninca-common-component-organization.ribbon.listOfServers=127.0.0.1:18082
ninca-common-component-organization.ribbon.ServerListRefreshInterval=5000
ninca-common.ribbon.listOfServers=127.0.0.1:18082
ninca-common.ribbon.ServerListRefreshInterval=5000
@@ -0,0 +1,222 @@
# server
spring.application.name=ninca-common-component-organization
server.port=17016
# 集群服务节点ID
server.instance-id=component-organization-02
#spring配置
spring.jackson.time-zone=GMT+8
spring.http.encoding.charset=UTF-8
spring.http.encoding.enabled=true
spring.http.encoding.force=true
spring.datasource.username=cloudwalk
spring.datasource.password=1qaz!QAZ
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://mysql_01.mysql_ip:3306/component-organization?useSSL=false&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
# consul
eureka.client.enabled=false
spring.cloud.consul.host=44700995ee904679a7ad5afddcf93bb5
spring.cloud.consul.port=8505
spring.cloud.consul.discovery.prefer-ip-address=true
spring.cloud.consul.discovery.ip-address=44700995ee904679a7ad5afddcf93bb5
spring.cloud.consul.discovery.instance-id=${spring.application.name}:44700995ee904679a7ad5afddcf93bb5:${server.port}
#redis
spring.redis.host=redis_01.redis_ip
spring.redis.port=6379
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
#mybatis
mybatis.mapper-locations=classpath*:mybatis/mysql/*/*.xml,classpath*:mybatis/*.xml,classpath*:cn/cloudwalk/task/data/mapper/mysql/*.xml, classpath*:cn/cloudwalk/data/authentication/mapper/plat/*.xml,classpath*:cn/cloudwalk/data/device/devices/mapper/mysql/*.xml,classpath*:cn/cloudwalk/data/resource/mapper/mysql/**/*.xml,classpath*:cn/cloudwalk/imgstore/mapper/mybatis/*.xml,classpath*:cn/cloudwalk/data/davinci/biology/mapper/mysql/*.xml,classpath*:cn/cloudwalk/data/imgstore/mapper/mysql/*.xml,classpath*:feature/*.xml,classpath*:cn/cloudwalk/data/aks/mapper/mysql/*/*.xml,classpath*:worker/*.xml, classpath*:mybatis/hardware/monitor/mapper/mysql/*.xml,classpath*:mybatis/mysql/account/*.xml,classpath*:mybatis/mysql/InvokeLogMapper.xml,classpath*:mybatis/mysql/ClusterInvokeLogMapper.xml, classpath*:mybatis/mysql/*.xml, classpath*:cn/cloudwalk/data/aggregate/mapper/mysql/*/*.xml,classpath*:cn/cloudwalk/data/device/mgn/atomic/mysql/*.xml,classpath*:cn/cloudwalk/data/cache/mapper/mysql/*.xml,classpath*:cn/cloudwalk/data/organization/mapper/mysql/*.xml,classpath*:cn/cloudwalk/storage/oss/data/mapper/mysql/*.xml
#log
logging.config=classpath:recognition-logback.xml
logging.level.root=INFO
logging.level.cn.cloudwalk.web.organization.controller.PersonController=info
logging.path=/data/cwos/ninca-common-component-organization-V2.9.2_20210730/logs
logging.file.info.max-index=10
logging.file.error.max-index=20
#resource
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
# formdata\u4F20\u8F93\u6587\u4EF6\u9650\u5236
cloudwalk.multipart.maxFileSize=50MB
cloudwalk.multipart.maxRequestSize=50MB
# quartz\u914D\u7F6E
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
# \u4EBA\u5458\u4FE1\u606F\u6CE8\u518C\u7167\u6807\u51C6\u8D28\u91CF\u5206\uFF1A
imageQualityScore=0.65
# \u6587\u4EF6\u8DEF\u5F84\u524D\u7F00\u914D\u7F6E
cloudwalk.component.file.urlPrefix=http://10.0.22.102:80/ninca-common-component-organization/file/imgByPath?path=
# cwos\u6587\u4EF6\u7BA1\u7406\u670D\u52A1\u540D
feign.davinci-portal.name=cwos-portal
feign.resource.name=cwos-portal
feign.portal.name=cwos-portal
# task
cloudwalk.imageStore.person.cron=0 */2 * * * ?
cloudwalk.imageStore.imageSync.cron=0 */2 * * * ?
# download file
cloudwalk.common-app.download.downDir=/data/cwos/ninca-common-component-organization-V2.9.2_20210730/download
cloudwalk.common-app.download.excelMaxRows=10000
cloudwalk.common-app.download.exportAllCount=100000
cloudwalk.common-app.download.shardingSize=31457280
cloudwalk.common-app.download.compressionType=.zip
# component-organization's service code on kafka and component-organization's group id
cloudwalk.component-organization.kafka.service-code=imgstoreApp
cloudwalk.component-organization.kafka.producer.groupId=component-organization
cloudwalk.component-organization.kafka.producer.bootstrapServers=44700995ee904679a7ad5afddcf93bb5:9092,0837a70b5fab47569391828f5feb2561:9092,371bfca4972c43d2aefcf302d0a4a277:9092
cloudwalk.component-organization.kafka.consumer.groupId=component-organization
cloudwalk.component-organization.kafka.consumer.bootstrapServers=44700995ee904679a7ad5afddcf93bb5:9092,0837a70b5fab47569391828f5feb2561:9092,371bfca4972c43d2aefcf302d0a4a277:9092
# PERSON_NAME_SPACE
person.name.space=componentOrg
# \u4e8c\u7ef4\u7801url
qr.code.url=http://10.0.22.102:80/general-person-h5/#/register?businessId=
# 监控使用单独的端口与服务端口隔离,提高安全性
# Nginx限制此端口的外网访问权限
management.port=17116
# springboot2的健康检查接口是/actuator/health
# springboot1的健康检查接口是/health
# 此配置可以把上述两个版本的健康检查接口统一设置为/actuator/health
# Nginx禁止外网访问以/actuator开头的url
management.context-path=/actuator
# springboot1开启管理接口的访问权限
management.security.enabled=false
# 关闭所有management接口
endpoints.enabled=false
# 开启/actuator/info
endpoints.info.enabled=true
# 开启/actuator/health
endpoints.health.enabled=true
# 开启/actuator/prometheus
endpoints.prometheus.enabled=true
# 注册consul时设置统一的健康检查地址
spring.cloud.consul.discovery.health-check-path=${management.context-path}/health
# 多实例注册management服务避免覆盖,这是springboot1.x存在的bug
spring.cloud.consul.discovery.management-suffix=44700995ee904679a7ad5afddcf93bb5-management
#图库同步配置
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
#设备-人员同步时间差N
device.person.sync.time.diff.minutes=60
#开关控制调用cwos同步日志
cwos.image.store.sync.log=false
feign.device.name=cwos-portal
ribbon.ReadTimeout=10000
ribbon.ConnectTimeout=10000
#设备图库结束拉取时间差
device.group.pull.time.diff.minutes=10
# 注册照内存要求(字节)
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
# 菠萝引擎consul服务名
feign.pineapple.name=cloudwalk-pineapple-manager
#修图引擎端口
revision.engine.port=20010
# 延迟添加有效期开关(默认关闭)
group-person.syn.config.delay-add-validate-data=false
# 延迟添加有效期周期(小时)
group-person.syn.config.delay-add-validate-hour=48
# 延迟添加人员有效期定时任务cron表达式
group-person.syn.config.delay-handle-validate.cron=1 0 0 /1 * ? *
# 处理人脸异常定时任务
group-person.syn.config.handle-group-face-exception.cron=0 /5 * * * ?
# 星河湾集团总部id
xhwId=21474e012cd14e26bc62771873b22562
# 星河湾集团总部默认楼层id
xhwDefaultFloorId=605560547135455232
# 其他机构默认楼层
xhwSixFloorId=605560541473144832
support.muti.group.device.type=CW-IS1330,-8,MTD8
@@ -0,0 +1,14 @@
# 启用feign熔断
feign.hystrix.enabled=true
# 限流配置
#hystrix.command.default.circuitBreaker.requestVolumeThreshold=1
hystrix.command.default.circuitBreaker.errorThresholdPercentage=50
hystrix.command.default.circuitBreaker.enabled=true
hystrix.command.default.execution.isolation.strategy=THREAD
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=120000
# hystrix线程池配置
hystrix.threadpool.default.coreSize=100
hystrix.threadpool.default.maximumSize=1000
hystrix.threadpool.default.allowMaximumSizeToDivergeFromCoreSize=true
hystrix.threadpool.default.maxQueueSize=-1
hystrix.threadpool.default.execution.isolation.thread.timeoutInMilliseconds=60000
@@ -0,0 +1,7 @@
#!/bin/bash
# shellcheck disable=SC2045
proPath=$1
cd $proPath/download/
name=$2
echo $name
zip -q -r $name.zip $name

Some files were not shown because too many files have changed in this diff Show More