fix: align bootstrap with production config, disable ZK ribbon

- Restore bootstrap to match production (discovery.enabled=false,
  consul.host=371bfca4972c43d2aefcf302d0a4a277)
- Add spring.cloud.zookeeper.enabled=false to prevent
  zookeeperRibbonClientConfiguration runtime error (child context)
- Remove ConfigurationBasedServerList overrides from app properties
  (no longer needed with proper config alignment)
- Revert PersonFeignClient url hardcode back to placeholder name
Bump version to 2.0.16
This commit is contained in:
反编译工作区
2026-05-05 18:39:49 +08:00
parent 7d52fd5d0b
commit d838dd0897
43 changed files with 2459 additions and 9 deletions
@@ -83,14 +83,8 @@ feign.device.name=cwos-portal
feign.resource.name=cwos-portal feign.resource.name=cwos-portal
feign.cwos-portal.name=cwos-portal feign.cwos-portal.name=cwos-portal
feign.ninca-crk-std.name=ninca-crk-std feign.ninca-crk-std.name=ninca-crk-std
# bootstrap \u5DF2\u4E0E V1 \u5BF9\u9F50 spring.cloud.consul.discovery.enabled=false\uFF08\u4E0D\u4ECE Consul \u62C9\u5B9E\u4F8B\uFF09\u3002
# Feign \u8D70 Ribbon \u65F6\u4ECD\u9700\u9759\u6001\u5B9E\u4F8B\uFF08V1 \u8FD0\u884C\u5305\u672A\u5199\u6B64\u6BB5\uFF0CV2 \u8865\u9F50\u5E76\u914D NincaCrkStdRibbonConfiguration\uFF09\u3002\u4E0E ninca-crk-std.ip \u4E00\u540C\u6309\u73AF\u5883\u6539\u3002
ninca-crk-std.ribbon.NIWSServerListClassName=com.netflix.loadbalancer.ConfigurationBasedServerList
ninca-crk-std.ribbon.listOfServers=10.128.161.95:16106
feign.davinci-portal.name=cwos-portal feign.davinci-portal.name=cwos-portal
feign.component-organization.name=ninca-common-component-organization feign.component-organization.name=ninca-common-component-organization
ninca-common-component-organization.ribbon.NIWSServerListClassName=com.netflix.loadbalancer.ConfigurationBasedServerList
ninca-common-component-organization.ribbon.listOfServers=127.0.0.1:33011
feign.ninca-common.name=ninca-common feign.ninca-common.name=ninca-common
feign.mqtt.name=cloudwalk-device-thirdparty feign.mqtt.name=cloudwalk-device-thirdparty
# CWOS\u4E8B\u4EF6\u914D\u7F6E # CWOS\u4E8B\u4EF6\u914D\u7F6E
@@ -13,6 +13,7 @@ spring.cloud.consul.discovery.instance-id=${spring.application.name}-${spring.cl
spring.cloud.consul.discovery.ip-address=${spring.cloud.client.ipAddress} spring.cloud.consul.discovery.ip-address=${spring.cloud.client.ipAddress}
spring.cloud.consul.discovery.deregister=false spring.cloud.consul.discovery.deregister=false
# zookeeper\u914D\u7F6E # zookeeper\u914D\u7F6E
spring.cloud.zookeeper.enabled=false
dubbo.registry.address=zookeeper://10.0.22.207:2181 dubbo.registry.address=zookeeper://10.0.22.207:2181
dubbo.protocol.port=16107 dubbo.protocol.port=16107
dubbo.provider.version=1.0 dubbo.provider.version=1.0
@@ -0,0 +1,10 @@
artifact=cw-elevator-application-V1.0.0.20211103.jar
bundle_dir_name=cw-elevator-application-V2.0.15.20260505
directory=/media/zebra/9e8fa357-7db6-4d70-88ed-d5de5a059a663/星河湾星中星/源码/maven-cw-elevator-application/releases/cw-elevator-application-V2.0.15.20260505
built_at=2026-05-05T18:32:25+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=7d52fd5d0bd15a8a6fa46d502be0f76c6e187148
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,125 @@
# deploy/v2-maven \uFF1Amaven \u6784\u5EFA cw-elevator-application-2.0.0.jar\uFF08\u540C\u76EE\u5F55\u542F\u52A8\uFF09
# \u751F\u4EA7\u6A21\u62DF\uFF1Aaccess-control \u4E1A\u52A1/\u5206\u8868/\u95E8\u7981\u9608\u503C\u7B49\u89C1\u540C\u76EE\u5F55 application-access-control.properties
# \uFF08\u4E0E \u661F\u4E2D\u5FC3/cw-elevator-application-V1.0.0.20211103 \u5BF9\u9F50\uFF09\u3002\u672C\u6587\u4EF6\u4FDD\u7559\u7AEF\u53E3\u3001JDBC/Redis/Ribbon/Kafka \u7B49\u9A8C\u8BC1\u73AF\u5883\u8986\u76D6\u3002
server.port=18081
server.tomcat.uri-encoding=UTF-8
spring.application.name=elevator-app
# Boot 1.5 \u65E0 spring.main.allow-bean-definition-overriding\uFF1B\u82E5\u91CD\u590D Bean \u9700\u5728\u4EE3\u7801\u4FA7\u6D88\u6B67\u4E49\u6216\u5347\u7EA7 Spring Boot
# 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=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.enabled=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\uFF08\u672C\u673A Docker\uFF1Aybs-redis 6379->6379\uFF0C\u82E5\u7528 craftlabs-redis \u6539\u4E3A 6380\uFF09
spring.redis.host=127.0.0.1
spring.redis.port=6379
# \u672C\u673A Redis \u65E0\u5BC6\u7801\u65F6\u5FC5\u987B\u4FDD\u7559\u4E0B\u884C\u7A7A\u503C\uFF0C\u4EE5\u8986\u76D6 fat-jar \u5185\u5D4C\u65E7\u5BC6\u7801\uFF08\u5426\u5219 Redisson ERR AUTH\uFF09
spring.redis.password=
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://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
# \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.ninca-crk-std.name=ninca-crk-std
# bootstrap \u5DF2\u4E0E V1 \u5BF9\u9F50 spring.cloud.consul.discovery.enabled=false\uFF08\u4E0D\u4ECE Consul \u62C9\u5B9E\u4F8B\uFF09\u3002
# Feign \u8D70 Ribbon \u65F6\u4ECD\u9700\u9759\u6001\u5B9E\u4F8B\uFF08V1 \u8FD0\u884C\u5305\u672A\u5199\u6B64\u6BB5\uFF0CV2 \u8865\u9F50\u5E76\u914D NincaCrkStdRibbonConfiguration\uFF09\u3002\u4E0E ninca-crk-std.ip \u4E00\u540C\u6309\u73AF\u5883\u6539\u3002
ninca-crk-std.ribbon.NIWSServerListClassName=com.netflix.loadbalancer.ConfigurationBasedServerList
ninca-crk-std.ribbon.listOfServers=10.128.161.95:16106
feign.davinci-portal.name=cwos-portal
feign.component-organization.name=ninca-common-component-organization
ninca-common-component-organization.ribbon.NIWSServerListClassName=com.netflix.loadbalancer.ConfigurationBasedServerList
ninca-common-component-organization.ribbon.listOfServers=127.0.0.1:33011
feign.ninca-common.name=ninca-common
feign.mqtt.name=cloudwalk-device-thirdparty
# CWOS\u4E8B\u4EF6\u914D\u7F6E
cloudwalk.event.bootstrap-servers=192.168.3.12: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.128.161.95:16106
#\u53D1\u9001\u7B2C\u4E09\u65B9\u6570\u636Eip
sendRecord.ip=hrec.star-river.com:32165
sendRecord.token.corpId=53db867a8bb747a1bd04dd1afcad8ca6
sendRecord.token.appKey=293e2d708f0143c2957b702cef44d951
sendRecord.token.appSecret=5f6995009b864669b52041b8f5dc4625
sendRecord.boolean=true
# \u8BBE\u5907\u5904\u7406\u7EBF\u7A0B\u6C60\u914D\u7F6E
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
#\u697C\u680Bid
floor.building.id=605560539791228928
@@ -0,0 +1,19 @@
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
spring.cloud.zookeeper.enabled=false
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.15 (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.15.pid
ExecStartPre=/bin/rm -f /run/cw-elevator-application-2.0.15.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
--
-- 字段说明:allow_zone_ids 为 JSON 数组,元素使用 code_elevator_area.zone_idsnowflake 格式),
-- 与 PersonResult.floorList 和 image_rule_ref.zone_id 同一套 ID。
--
-- 数据来源(现场查询 192.168.3.12:3307):
-- org_idcomponent-organization.cw_is_organization
-- NAME='[28-38F]广发基金管理有限公司' -> ID = 488b8ad049bb43408a6fbcc50bcb89ac
-- 28F zone_idcw-elevator-application.code_elevator_area
-- zone_id = 605560545117995008zone_name=28Fcode=0x1C
--
-- 重复执行:使用固定 id + ON DUPLICATE KEY UPDATE,幂等。
SET NAMES utf8mb4;
INSERT INTO tenant_visitor_floor_policy (
id,
org_id,
business_id,
policy_type,
allow_zone_ids,
building_id,
enabled,
policy_version,
remark,
created_at,
updated_at
) VALUES (
'gf_vstr_policy_guangfa_fund_001x',
'488b8ad049bb43408a6fbcc50bcb89ac',
'2524639890ba4f2cba9ba1a4eeaa4015',
'INTERSECT_ALLOWLIST',
'["605560545117995008"]',
NULL,
1,
1,
'广发基金:访客与 floorList 求交后仅保留 allowlist(默认仅 28F zone)。',
UNIX_TIMESTAMP(NOW()) * 1000,
UNIX_TIMESTAMP(NOW()) * 1000
) ON DUPLICATE KEY UPDATE
org_id = VALUES(org_id),
policy_type = VALUES(policy_type),
allow_zone_ids = VALUES(allow_zone_ids),
enabled = VALUES(enabled),
policy_version = policy_version + 1,
remark = VALUES(remark),
updated_at = VALUES(updated_at);
@@ -0,0 +1,186 @@
-- 物业公司租户:访客默认楼层策略初始化(电梯库 cw-elevator-application
-- 访客不传 floorIds 时,默认仅开放 6F,与被访人 floorList 求交。
--
-- 数据来源(192.168.3.12:3307):
-- org_idcomponent-organization.cw_is_organization
-- 星河湾物业管理有限公司 → 64fdc8eaf5824df5a1329819af29b79f
-- 星河湾物业管理公司 → 8fc3f910bd834198a539832017fe920e
-- 星河湾物管公司 → cc760fdf9c384a0cbf4951ccf2c6452e
-- 星中心物业管理公司 → f216235e54ca42bfa0379e69b3754aff
-- 星中心物业服务中心 → 95818575a2284db6833289474d33671f
-- 星中心物管公司 → 348328d755624b3491cd307a3109f36a
-- 物业管理总部 → dde6cc9a4f6b4f5490d03e26fb016200
-- 6F zone_idcw-elevator-application.code_elevator_area
-- zone_id = 605560541473144832code=0x06
--
-- 重复执行:使用固定 id + ON DUPLICATE KEY UPDATE。
SET NAMES utf8mb4;
-- ============================================================
-- 1. 星河湾物业管理有限公司
-- ============================================================
INSERT INTO tenant_visitor_floor_policy (
id, business_id, org_id, policy_type, allow_zone_ids,
building_id, enabled, policy_version, remark, created_at, updated_at
) VALUES (
'pm_6f_vstr_policy_001',
'2524639890ba4f2cba9ba1a4eeaa4015',
'64fdc8eaf5824df5a1329819af29b79f',
'INTERSECT_ALLOWLIST',
'["605560541473144832"]',
NULL, 1, 1,
'星河湾物业管理有限公司:访客默认仅开放 6F。',
UNIX_TIMESTAMP(NOW()) * 1000,
UNIX_TIMESTAMP(NOW()) * 1000
) ON DUPLICATE KEY UPDATE
policy_type = VALUES(policy_type),
allow_zone_ids = VALUES(allow_zone_ids),
enabled = VALUES(enabled),
policy_version = policy_version + 1,
remark = VALUES(remark),
updated_at = VALUES(updated_at);
-- ============================================================
-- 2. 星河湾物业管理公司
-- ============================================================
INSERT INTO tenant_visitor_floor_policy (
id, business_id, org_id, policy_type, allow_zone_ids,
building_id, enabled, policy_version, remark, created_at, updated_at
) VALUES (
'pm_6f_vstr_policy_002',
'2524639890ba4f2cba9ba1a4eeaa4015',
'8fc3f910bd834198a539832017fe920e',
'INTERSECT_ALLOWLIST',
'["605560541473144832"]',
NULL, 1, 1,
'星河湾物业管理公司:访客默认仅开放 6F。',
UNIX_TIMESTAMP(NOW()) * 1000,
UNIX_TIMESTAMP(NOW()) * 1000
) ON DUPLICATE KEY UPDATE
policy_type = VALUES(policy_type),
allow_zone_ids = VALUES(allow_zone_ids),
enabled = VALUES(enabled),
policy_version = policy_version + 1,
remark = VALUES(remark),
updated_at = VALUES(updated_at);
-- ============================================================
-- 3. 星河湾物管公司
-- ============================================================
INSERT INTO tenant_visitor_floor_policy (
id, business_id, org_id, policy_type, allow_zone_ids,
building_id, enabled, policy_version, remark, created_at, updated_at
) VALUES (
'pm_6f_vstr_policy_003',
'2524639890ba4f2cba9ba1a4eeaa4015',
'cc760fdf9c384a0cbf4951ccf2c6452e',
'INTERSECT_ALLOWLIST',
'["605560541473144832"]',
NULL, 1, 1,
'星河湾物管公司:访客默认仅开放 6F。',
UNIX_TIMESTAMP(NOW()) * 1000,
UNIX_TIMESTAMP(NOW()) * 1000
) ON DUPLICATE KEY UPDATE
policy_type = VALUES(policy_type),
allow_zone_ids = VALUES(allow_zone_ids),
enabled = VALUES(enabled),
policy_version = policy_version + 1,
remark = VALUES(remark),
updated_at = VALUES(updated_at);
-- ============================================================
-- 4. 星中心物业管理公司
-- ============================================================
INSERT INTO tenant_visitor_floor_policy (
id, business_id, org_id, policy_type, allow_zone_ids,
building_id, enabled, policy_version, remark, created_at, updated_at
) VALUES (
'pm_6f_vstr_policy_004',
'2524639890ba4f2cba9ba1a4eeaa4015',
'f216235e54ca42bfa0379e69b3754aff',
'INTERSECT_ALLOWLIST',
'["605560541473144832"]',
NULL, 1, 1,
'星中心物业管理公司:访客默认仅开放 6F。',
UNIX_TIMESTAMP(NOW()) * 1000,
UNIX_TIMESTAMP(NOW()) * 1000
) ON DUPLICATE KEY UPDATE
policy_type = VALUES(policy_type),
allow_zone_ids = VALUES(allow_zone_ids),
enabled = VALUES(enabled),
policy_version = policy_version + 1,
remark = VALUES(remark),
updated_at = VALUES(updated_at);
-- ============================================================
-- 5. 星中心物业服务中心
-- ============================================================
INSERT INTO tenant_visitor_floor_policy (
id, business_id, org_id, policy_type, allow_zone_ids,
building_id, enabled, policy_version, remark, created_at, updated_at
) VALUES (
'pm_6f_vstr_policy_005',
'2524639890ba4f2cba9ba1a4eeaa4015',
'95818575a2284db6833289474d33671f',
'INTERSECT_ALLOWLIST',
'["605560541473144832"]',
NULL, 1, 1,
'星中心物业服务中心:访客默认仅开放 6F。',
UNIX_TIMESTAMP(NOW()) * 1000,
UNIX_TIMESTAMP(NOW()) * 1000
) ON DUPLICATE KEY UPDATE
policy_type = VALUES(policy_type),
allow_zone_ids = VALUES(allow_zone_ids),
enabled = VALUES(enabled),
policy_version = policy_version + 1,
remark = VALUES(remark),
updated_at = VALUES(updated_at);
-- ============================================================
-- 6. 星中心物管公司
-- ============================================================
INSERT INTO tenant_visitor_floor_policy (
id, business_id, org_id, policy_type, allow_zone_ids,
building_id, enabled, policy_version, remark, created_at, updated_at
) VALUES (
'pm_6f_vstr_policy_006',
'2524639890ba4f2cba9ba1a4eeaa4015',
'348328d755624b3491cd307a3109f36a',
'INTERSECT_ALLOWLIST',
'["605560541473144832"]',
NULL, 1, 1,
'星中心物管公司:访客默认仅开放 6F。',
UNIX_TIMESTAMP(NOW()) * 1000,
UNIX_TIMESTAMP(NOW()) * 1000
) ON DUPLICATE KEY UPDATE
policy_type = VALUES(policy_type),
allow_zone_ids = VALUES(allow_zone_ids),
enabled = VALUES(enabled),
policy_version = policy_version + 1,
remark = VALUES(remark),
updated_at = VALUES(updated_at);
-- ============================================================
-- 7. 物业管理总部
-- ============================================================
INSERT INTO tenant_visitor_floor_policy (
id, business_id, org_id, policy_type, allow_zone_ids,
building_id, enabled, policy_version, remark, created_at, updated_at
) VALUES (
'pm_6f_vstr_policy_007',
'2524639890ba4f2cba9ba1a4eeaa4015',
'dde6cc9a4f6b4f5490d03e26fb016200',
'INTERSECT_ALLOWLIST',
'["605560541473144832"]',
NULL, 1, 1,
'物业管理总部:访客默认仅开放 6F。',
UNIX_TIMESTAMP(NOW()) * 1000,
UNIX_TIMESTAMP(NOW()) * 1000
) ON DUPLICATE KEY UPDATE
policy_type = VALUES(policy_type),
allow_zone_ids = VALUES(allow_zone_ids),
enabled = VALUES(enabled),
policy_version = policy_version + 1,
remark = VALUES(remark),
updated_at = VALUES(updated_at);
@@ -0,0 +1,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-V1.0.0.20211103.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-V1.0.0.20211103.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-V1.0.0.20211103.jar 对应的 Java 进程(仅匹配命令行中含该 JAR 名的进程)。
set -euo pipefail
JAR="cw-elevator-application-V1.0.0.20211103.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,10 @@
artifact=cw-elevator-application-V1.0.0.20211103.jar
bundle_dir_name=cw-elevator-application-V2.0.16.20260505
directory=/media/zebra/9e8fa357-7db6-4d70-88ed-d5de5a059a663/星河湾星中星/源码/maven-cw-elevator-application/releases/cw-elevator-application-V2.0.16.20260505
built_at=2026-05-05T18:39:20+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=7d52fd5d0bd15a8a6fa46d502be0f76c6e187148
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,119 @@
# deploy/v2-maven \uFF1Amaven \u6784\u5EFA cw-elevator-application-2.0.0.jar\uFF08\u540C\u76EE\u5F55\u542F\u52A8\uFF09
# \u751F\u4EA7\u6A21\u62DF\uFF1Aaccess-control \u4E1A\u52A1/\u5206\u8868/\u95E8\u7981\u9608\u503C\u7B49\u89C1\u540C\u76EE\u5F55 application-access-control.properties
# \uFF08\u4E0E \u661F\u4E2D\u5FC3/cw-elevator-application-V1.0.0.20211103 \u5BF9\u9F50\uFF09\u3002\u672C\u6587\u4EF6\u4FDD\u7559\u7AEF\u53E3\u3001JDBC/Redis/Ribbon/Kafka \u7B49\u9A8C\u8BC1\u73AF\u5883\u8986\u76D6\u3002
server.port=18081
server.tomcat.uri-encoding=UTF-8
spring.application.name=elevator-app
# Boot 1.5 \u65E0 spring.main.allow-bean-definition-overriding\uFF1B\u82E5\u91CD\u590D Bean \u9700\u5728\u4EE3\u7801\u4FA7\u6D88\u6B67\u4E49\u6216\u5347\u7EA7 Spring Boot
# 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=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.enabled=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\uFF08\u672C\u673A Docker\uFF1Aybs-redis 6379->6379\uFF0C\u82E5\u7528 craftlabs-redis \u6539\u4E3A 6380\uFF09
spring.redis.host=127.0.0.1
spring.redis.port=6379
# \u672C\u673A Redis \u65E0\u5BC6\u7801\u65F6\u5FC5\u987B\u4FDD\u7559\u4E0B\u884C\u7A7A\u503C\uFF0C\u4EE5\u8986\u76D6 fat-jar \u5185\u5D4C\u65E7\u5BC6\u7801\uFF08\u5426\u5219 Redisson ERR AUTH\uFF09
spring.redis.password=
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://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
# \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.ninca-crk-std.name=ninca-crk-std
feign.davinci-portal.name=cwos-portal
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=192.168.3.12: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.128.161.95:16106
#\u53D1\u9001\u7B2C\u4E09\u65B9\u6570\u636Eip
sendRecord.ip=hrec.star-river.com:32165
sendRecord.token.corpId=53db867a8bb747a1bd04dd1afcad8ca6
sendRecord.token.appKey=293e2d708f0143c2957b702cef44d951
sendRecord.token.appSecret=5f6995009b864669b52041b8f5dc4625
sendRecord.boolean=true
# \u8BBE\u5907\u5904\u7406\u7EBF\u7A0B\u6C60\u914D\u7F6E
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
#\u697C\u680Bid
floor.building.id=605560539791228928
@@ -0,0 +1,19 @@
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
spring.cloud.zookeeper.enabled=false
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.16 (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.16.pid
ExecStartPre=/bin/rm -f /run/cw-elevator-application-2.0.16.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
--
-- 字段说明:allow_zone_ids 为 JSON 数组,元素使用 code_elevator_area.zone_idsnowflake 格式),
-- 与 PersonResult.floorList 和 image_rule_ref.zone_id 同一套 ID。
--
-- 数据来源(现场查询 192.168.3.12:3307):
-- org_idcomponent-organization.cw_is_organization
-- NAME='[28-38F]广发基金管理有限公司' -> ID = 488b8ad049bb43408a6fbcc50bcb89ac
-- 28F zone_idcw-elevator-application.code_elevator_area
-- zone_id = 605560545117995008zone_name=28Fcode=0x1C
--
-- 重复执行:使用固定 id + ON DUPLICATE KEY UPDATE,幂等。
SET NAMES utf8mb4;
INSERT INTO tenant_visitor_floor_policy (
id,
org_id,
business_id,
policy_type,
allow_zone_ids,
building_id,
enabled,
policy_version,
remark,
created_at,
updated_at
) VALUES (
'gf_vstr_policy_guangfa_fund_001x',
'488b8ad049bb43408a6fbcc50bcb89ac',
'2524639890ba4f2cba9ba1a4eeaa4015',
'INTERSECT_ALLOWLIST',
'["605560545117995008"]',
NULL,
1,
1,
'广发基金:访客与 floorList 求交后仅保留 allowlist(默认仅 28F zone)。',
UNIX_TIMESTAMP(NOW()) * 1000,
UNIX_TIMESTAMP(NOW()) * 1000
) ON DUPLICATE KEY UPDATE
org_id = VALUES(org_id),
policy_type = VALUES(policy_type),
allow_zone_ids = VALUES(allow_zone_ids),
enabled = VALUES(enabled),
policy_version = policy_version + 1,
remark = VALUES(remark),
updated_at = VALUES(updated_at);
@@ -0,0 +1,186 @@
-- 物业公司租户:访客默认楼层策略初始化(电梯库 cw-elevator-application
-- 访客不传 floorIds 时,默认仅开放 6F,与被访人 floorList 求交。
--
-- 数据来源(192.168.3.12:3307):
-- org_idcomponent-organization.cw_is_organization
-- 星河湾物业管理有限公司 → 64fdc8eaf5824df5a1329819af29b79f
-- 星河湾物业管理公司 → 8fc3f910bd834198a539832017fe920e
-- 星河湾物管公司 → cc760fdf9c384a0cbf4951ccf2c6452e
-- 星中心物业管理公司 → f216235e54ca42bfa0379e69b3754aff
-- 星中心物业服务中心 → 95818575a2284db6833289474d33671f
-- 星中心物管公司 → 348328d755624b3491cd307a3109f36a
-- 物业管理总部 → dde6cc9a4f6b4f5490d03e26fb016200
-- 6F zone_idcw-elevator-application.code_elevator_area
-- zone_id = 605560541473144832code=0x06
--
-- 重复执行:使用固定 id + ON DUPLICATE KEY UPDATE。
SET NAMES utf8mb4;
-- ============================================================
-- 1. 星河湾物业管理有限公司
-- ============================================================
INSERT INTO tenant_visitor_floor_policy (
id, business_id, org_id, policy_type, allow_zone_ids,
building_id, enabled, policy_version, remark, created_at, updated_at
) VALUES (
'pm_6f_vstr_policy_001',
'2524639890ba4f2cba9ba1a4eeaa4015',
'64fdc8eaf5824df5a1329819af29b79f',
'INTERSECT_ALLOWLIST',
'["605560541473144832"]',
NULL, 1, 1,
'星河湾物业管理有限公司:访客默认仅开放 6F。',
UNIX_TIMESTAMP(NOW()) * 1000,
UNIX_TIMESTAMP(NOW()) * 1000
) ON DUPLICATE KEY UPDATE
policy_type = VALUES(policy_type),
allow_zone_ids = VALUES(allow_zone_ids),
enabled = VALUES(enabled),
policy_version = policy_version + 1,
remark = VALUES(remark),
updated_at = VALUES(updated_at);
-- ============================================================
-- 2. 星河湾物业管理公司
-- ============================================================
INSERT INTO tenant_visitor_floor_policy (
id, business_id, org_id, policy_type, allow_zone_ids,
building_id, enabled, policy_version, remark, created_at, updated_at
) VALUES (
'pm_6f_vstr_policy_002',
'2524639890ba4f2cba9ba1a4eeaa4015',
'8fc3f910bd834198a539832017fe920e',
'INTERSECT_ALLOWLIST',
'["605560541473144832"]',
NULL, 1, 1,
'星河湾物业管理公司:访客默认仅开放 6F。',
UNIX_TIMESTAMP(NOW()) * 1000,
UNIX_TIMESTAMP(NOW()) * 1000
) ON DUPLICATE KEY UPDATE
policy_type = VALUES(policy_type),
allow_zone_ids = VALUES(allow_zone_ids),
enabled = VALUES(enabled),
policy_version = policy_version + 1,
remark = VALUES(remark),
updated_at = VALUES(updated_at);
-- ============================================================
-- 3. 星河湾物管公司
-- ============================================================
INSERT INTO tenant_visitor_floor_policy (
id, business_id, org_id, policy_type, allow_zone_ids,
building_id, enabled, policy_version, remark, created_at, updated_at
) VALUES (
'pm_6f_vstr_policy_003',
'2524639890ba4f2cba9ba1a4eeaa4015',
'cc760fdf9c384a0cbf4951ccf2c6452e',
'INTERSECT_ALLOWLIST',
'["605560541473144832"]',
NULL, 1, 1,
'星河湾物管公司:访客默认仅开放 6F。',
UNIX_TIMESTAMP(NOW()) * 1000,
UNIX_TIMESTAMP(NOW()) * 1000
) ON DUPLICATE KEY UPDATE
policy_type = VALUES(policy_type),
allow_zone_ids = VALUES(allow_zone_ids),
enabled = VALUES(enabled),
policy_version = policy_version + 1,
remark = VALUES(remark),
updated_at = VALUES(updated_at);
-- ============================================================
-- 4. 星中心物业管理公司
-- ============================================================
INSERT INTO tenant_visitor_floor_policy (
id, business_id, org_id, policy_type, allow_zone_ids,
building_id, enabled, policy_version, remark, created_at, updated_at
) VALUES (
'pm_6f_vstr_policy_004',
'2524639890ba4f2cba9ba1a4eeaa4015',
'f216235e54ca42bfa0379e69b3754aff',
'INTERSECT_ALLOWLIST',
'["605560541473144832"]',
NULL, 1, 1,
'星中心物业管理公司:访客默认仅开放 6F。',
UNIX_TIMESTAMP(NOW()) * 1000,
UNIX_TIMESTAMP(NOW()) * 1000
) ON DUPLICATE KEY UPDATE
policy_type = VALUES(policy_type),
allow_zone_ids = VALUES(allow_zone_ids),
enabled = VALUES(enabled),
policy_version = policy_version + 1,
remark = VALUES(remark),
updated_at = VALUES(updated_at);
-- ============================================================
-- 5. 星中心物业服务中心
-- ============================================================
INSERT INTO tenant_visitor_floor_policy (
id, business_id, org_id, policy_type, allow_zone_ids,
building_id, enabled, policy_version, remark, created_at, updated_at
) VALUES (
'pm_6f_vstr_policy_005',
'2524639890ba4f2cba9ba1a4eeaa4015',
'95818575a2284db6833289474d33671f',
'INTERSECT_ALLOWLIST',
'["605560541473144832"]',
NULL, 1, 1,
'星中心物业服务中心:访客默认仅开放 6F。',
UNIX_TIMESTAMP(NOW()) * 1000,
UNIX_TIMESTAMP(NOW()) * 1000
) ON DUPLICATE KEY UPDATE
policy_type = VALUES(policy_type),
allow_zone_ids = VALUES(allow_zone_ids),
enabled = VALUES(enabled),
policy_version = policy_version + 1,
remark = VALUES(remark),
updated_at = VALUES(updated_at);
-- ============================================================
-- 6. 星中心物管公司
-- ============================================================
INSERT INTO tenant_visitor_floor_policy (
id, business_id, org_id, policy_type, allow_zone_ids,
building_id, enabled, policy_version, remark, created_at, updated_at
) VALUES (
'pm_6f_vstr_policy_006',
'2524639890ba4f2cba9ba1a4eeaa4015',
'348328d755624b3491cd307a3109f36a',
'INTERSECT_ALLOWLIST',
'["605560541473144832"]',
NULL, 1, 1,
'星中心物管公司:访客默认仅开放 6F。',
UNIX_TIMESTAMP(NOW()) * 1000,
UNIX_TIMESTAMP(NOW()) * 1000
) ON DUPLICATE KEY UPDATE
policy_type = VALUES(policy_type),
allow_zone_ids = VALUES(allow_zone_ids),
enabled = VALUES(enabled),
policy_version = policy_version + 1,
remark = VALUES(remark),
updated_at = VALUES(updated_at);
-- ============================================================
-- 7. 物业管理总部
-- ============================================================
INSERT INTO tenant_visitor_floor_policy (
id, business_id, org_id, policy_type, allow_zone_ids,
building_id, enabled, policy_version, remark, created_at, updated_at
) VALUES (
'pm_6f_vstr_policy_007',
'2524639890ba4f2cba9ba1a4eeaa4015',
'dde6cc9a4f6b4f5490d03e26fb016200',
'INTERSECT_ALLOWLIST',
'["605560541473144832"]',
NULL, 1, 1,
'物业管理总部:访客默认仅开放 6F。',
UNIX_TIMESTAMP(NOW()) * 1000,
UNIX_TIMESTAMP(NOW()) * 1000
) ON DUPLICATE KEY UPDATE
policy_type = VALUES(policy_type),
allow_zone_ids = VALUES(allow_zone_ids),
enabled = VALUES(enabled),
policy_version = policy_version + 1,
remark = VALUES(remark),
updated_at = VALUES(updated_at);
@@ -0,0 +1,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-V1.0.0.20211103.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-V1.0.0.20211103.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-V1.0.0.20211103.jar 对应的 Java 进程(仅匹配命令行中含该 JAR 名的进程)。
set -euo pipefail
JAR="cw-elevator-application-V1.0.0.20211103.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)由业务与实施定稿。
其它租户不配策略则**不受影响**。
---
## 对您这边的影响
- **时间**:安排在**夜间**,具体周二/周三窗口见《升级计划》。
- **中断**:重启应用时派梯接口可能**短暂**不可用,一般会控制在很短时间。
- **配合**:指定一个对接人;确认广发基金侧要开放的**访客可达区域/楼层**(与前台、接待流程对齐);上线后若有异常派梯请及时反馈。
---
## 出问题怎么办
应用可先退回上一版包;策略数据可单独改/停,不一定整库回滚。细则见《升级计划》。
@@ -16,8 +16,7 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestMethod;
@FeignClient(name = "ninca-common-component-organization", @FeignClient(name = "${feign.component-organization.name:ninca-common-component-organization}",
url = "http://127.0.0.1:33011",
path = "/component/person", fallback = PersonFeignClientFallback.class) path = "/component/person", fallback = PersonFeignClientFallback.class)
public interface PersonFeignClient { public interface PersonFeignClient {
@RequestMapping(value = {"/add"}, method = {RequestMethod.POST}) @RequestMapping(value = {"/add"}, method = {RequestMethod.POST})