feat: 租户访客策略 SQL、访客邀约验证包、component-org 与发布脚本

- docs/sql: organization_* 与 tenant_* 访客楼层策略脚本
- docs/testing: 访客邀约页初始化验证、pack 脚本与 README(忽略 dist/__pycache__)
- maven-ninca-common-component-organization: CpImageStoreServiceImpl、starter、run-verify、releases 脚本与 javap 审计 JSON
- docs/superpowers: component-org 生产问题修复计划
- scripts/test-env/prepare-db.sh 更新

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
反编译工作区
2026-05-06 22:00:16 +08:00
parent 86cf41890e
commit 25db029859
24 changed files with 6016 additions and 27 deletions
@@ -0,0 +1,90 @@
# `releases/` 发布目录
## 目的
`releases/` 用于存放 **可交付的发布包(release bundle**:从本 reactor 构建的 **Spring Boot fat JAR**、与现场/部署包一致的 **配置文件**、**启动脚本** 与 **校验信息(SHA256** 的固定组合。
该目录是 **对外或运维交付的“落盘点”**,与单纯的 Maven `target/` 构建输出区分开。
## 目录命名规范
每个发布包为一个子目录,命名格式:
```text
ninca-common-component-organization-<maven-version>-<YYYYMMDD>/
```
- **`<maven-version>`**:反应堆根 `pom.xml``<artifactId>ninca-common-component-organization-reactor</artifactId>` 对应的 `<version>`(例如 `2.9.4-xinghewan`)。
- **`<YYYYMMDD>`**:发布日期戳;默认取构建当日 `date +%Y%m%d`,也可用环境变量覆盖(见脚本说明)。
示例:`ninca-common-component-organization-2.9.4-xinghewan-YYYYMMDD/`
## 每个 bundle 应包含的内容
| 内容 | 说明 |
|------|------|
| **Fat JAR** | 从 `cwos-component-organization-starter/target/` 拷贝的可执行 fat JAR(由 `spring-boot-maven-plugin` repackage`finalName``ninca-common-component-organization-${project.version}.jar`)。 |
| **`application-node.properties`** | 节点差异(Consul 主机、instance-id、management-suffix 等);公共项在 fat jar 内 `application.properties`。从 **`部署包/`** 模板目录复制(见下文)。 |
| **`bootstrap.properties`** | 引导配置;来源同上。 |
| **`sql/`** | 租户访客策略脚本(与 **`源码/docs/sql`** 一致):电梯库 `tenant_visitor_floor_policy*` + 组织库 **`organization_tenant_visitor_floor_policy*`**(含 **`organization_tenant_visitor_floor_policy_full_install.sql`** 一站式);内含 **`README.txt`**。 |
| **`start.sh`** | 启动脚本:**`--spring.config.location=classpath:/,file:<发布目录>/`** + **`--spring.config.name=application,application-node`** + **`--logging.path=<发布目录>/logs`**。 |
| **可选符号链接** | 运维友好别名:`ninca-common-component-organization-V<主版本三元组>_<YYYYMMDD>.jar` → 实际 fat JAR(例如 `V2.9.4_20260506` 对应版本 `2.9.4-xinghewan`)。 |
| **`RELEASE.txt`** | 发布说明:版本、目录名、构建命令、文件清单、fat JAR 的 **SHA256**。 |
其中 `application-node.properties` / `bootstrap.properties` 的「权威模板」通常来自现场 **部署包** 目录(与运维目录对齐),路径示例:
`星河湾星中星/部署包/ninca_common_component_organization_01-ninca_common_component_organization/`
若该路径下不存在对应文件,脚本会 **告警并跳过**,不会中断构建(仍可从别处手工放入同名文件)。
## 前置条件
- **JDK 8**:与本 reactor 一致;默认使用 `/usr/lib/jvm/java-8-openjdk-amd64`(可通过 `JAVA_HOME` 覆盖)。
- **`all-lib/`**:反应堆依赖的本地扁平库目录必须存在且齐全,否则 `mvn package` 可能失败(根 `pom.xml``all-lib.dir` 指向 `${maven.multiModuleProjectDirectory}/all-lib`)。
## JDK / `JAVA_HOME` 说明
- 脚本默认使用 **`/usr/lib/jvm/java-8-openjdk-amd64`** 进行 **`mvn clean package`**。
- 若交互式环境中 **`JAVA_HOME` 已指向 JDK 11+**,仅写 `JAVA_HOME:-默认值` **不会**覆盖已导出变量;脚本会检测并在非 JDK 8 时 **发出 WARN 并强制改用上述 JDK8 路径**,以保证与本 reactor 一致。
- 若需使用其他 JDK 8 安装路径,请先 **`export JAVA_HOME=/path/to/jdk8`**(确保 `java -version` 为 1.8),再执行脚本。
## 自动生成脚本
使用 **`create-release-bundle.sh`**(位于本目录,可执行)从反应堆根执行 **`mvn clean package -DskipTests`**,并生成符合上述规范的目录与 `RELEASE.txt`
用法示例:
```bash
cd /path/to/maven-ninca-common-component-organization
./releases/create-release-bundle.sh
RELEASE_DATE=20260506 ./releases/create-release-bundle.sh
```
环境变量(可选):
- **`RELEASE_DATE`**:目录后缀 `YYYYMMDD`,默认当天。
- **`DEPLOY_TEMPLATE_ROOT`**:覆盖部署包模板目录(默认见上文「配置文件来源」)。
- **`DOCS_SQL_ROOT`**:覆盖 **`sql/`** 脚本来源目录(默认 **`源码/docs/sql`**,即反应堆上一级目录下的 `docs/sql`)。
## 与 `deploy-implementation-package/` 的关系
构建产物路径、制品暂存等说明见同仓库:
**[../deploy-implementation-package/README.md](../deploy-implementation-package/README.md)**
(若该文件不存在,请以反应堆根 `pom.xml` 与 starter 模块 `target/` 为准。)
## Git 与大文件
本目录下的 **`ninca-common-component-organization-*/` 发布子目录**(含 fat jar)通过 **`releases/.gitignore`** 忽略,避免将大体积二进制提交入库;**`README.md`** 与 **`create-release-bundle.sh`** 仍正常跟踪。
## 手工 `java -jar`(不用 start.sh)时
若从其它工作目录直接启动,请显式指定配置目录与可写日志路径,例如:
`--spring.config.location=file:<发布包绝对路径>/``--logging.path=<同目录下 logs 或 /tmp/...>`
仅写 `--spring.config.location` 时若未覆盖 `logging.path`,且 `application.properties` 仍指向生产路径 `/data/cwos/...`,会因无法创建目录导致 **Logback 初始化失败**
---
**English (short):** `releases/` holds versioned, deployable bundles (fat JAR + config + `start.sh` + `RELEASE.txt` + optional symlink), named `ninca-common-component-organization-<maven-version>-<YYYYMMDD>/`. Build requires JDK 8 and `all-lib/`. Use `create-release-bundle.sh` to reproduce bundles consistently.
@@ -0,0 +1,189 @@
#!/usr/bin/env bash
# 从反应堆根构建 fat JAR,并在 releases/ 下生成发布目录。
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REACTOR_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
DEFAULT_JAVA_HOME="/usr/lib/jvm/java-8-openjdk-amd64"
export JAVA_HOME="${JAVA_HOME:-$DEFAULT_JAVA_HOME}"
# 若登录 shell 已将 JAVA_HOME 设为 JDK 11+:- 默认值不会生效;发布编译仍须 JDK 8
if ! "${JAVA_HOME}/bin/java" -version 2>&1 | grep -qE 'version "1\.8\.|java version "1\.8\.'; then
echo "WARN: 当前 JAVA_HOME${JAVA_HOME})不是 JDK 8;发布构建将改用 ${DEFAULT_JAVA_HOME}" >&2
export JAVA_HOME="$DEFAULT_JAVA_HOME"
fi
# 默认:顶层部署包中的组件模板目录(application-node.properties / bootstrap.properties
DEFAULT_DEPLOY_TEMPLATE="/media/zebra/9e8fa357-7db6-4d70-88ed-d5de5a059a663/星河湾星中星/部署包/ninca_common_component_organization_01-ninca_common_component_organization"
DEPLOY_TEMPLATE="${DEPLOY_TEMPLATE_ROOT:-$DEFAULT_DEPLOY_TEMPLATE}"
# 与租户访客策略相关的 SQL 权威源(与电梯 prepare-db / release-cw-elevator 共用 docs/sql
DEFAULT_DOCS_SQL="$(cd "${SCRIPT_DIR}/../../docs/sql" 2>/dev/null && pwd)" || true
if [[ -z "${DEFAULT_DOCS_SQL}" || ! -d "${DEFAULT_DOCS_SQL}" ]]; then
DEFAULT_DOCS_SQL="/media/zebra/9e8fa357-7db6-4d70-88ed-d5de5a059a663/星河湾星中星/源码/docs/sql"
fi
DOCS_SQL="${DOCS_SQL_ROOT:-$DEFAULT_DOCS_SQL}"
RELEASE_DATE="${RELEASE_DATE:-$(date +%Y%m%d)}"
die() { echo "ERROR: $*" >&2; exit 1; }
read_version_from_pom() {
local pom="${REACTOR_ROOT}/pom.xml"
[[ -f "$pom" ]] || die "找不到反应堆 pom.xml: $pom"
awk '
/<artifactId>ninca-common-component-organization-reactor<\/artifactId>/ { inr=1 }
inr && /<version>/ {
sub(/.*<version>/, "")
sub(/<\/version>.*/, "")
print
exit
}
' "$pom"
}
read_version_mvn() {
# 备用:mvn help:evaluate(部分环境 -q 会吞掉 stdout,故解析最后一行语义化版本)
(cd "$REACTOR_ROOT" && mvn -DforceStdout help:evaluate -Dexpression=project.version -f pom.xml 2>/dev/null \
| grep -E '^[0-9]' | tail -1) || true
}
if [[ ! -x "${JAVA_HOME}/bin/java" ]]; then
die "JAVA_HOME 无效或缺少 java 可执行文件: ${JAVA_HOME}/bin/java"
fi
if [[ ! -d "${REACTOR_ROOT}/all-lib" ]]; then
echo "WARN: 未找到 all-lib/${REACTOR_ROOT}/all-lib)。打包可能失败;请先准备本地扁平依赖库。" >&2
fi
VERSION="$(read_version_from_pom)"
if [[ -z "${VERSION}" ]]; then
VERSION="$(read_version_mvn)"
fi
[[ -n "${VERSION}" ]] || die "无法从 pom.xml 解析反应堆版本"
SEMVER="${VERSION%%-*}"
JAR_BASENAME="ninca-common-component-organization-${VERSION}.jar"
SOURCE_JAR="${REACTOR_ROOT}/cwos-component-organization-starter/target/${JAR_BASENAME}"
BUNDLE_NAME="ninca-common-component-organization-${VERSION}-${RELEASE_DATE}"
OUT_DIR="${SCRIPT_DIR}/${BUNDLE_NAME}"
SYMLINK_NAME="ninca-common-component-organization-V${SEMVER}_${RELEASE_DATE}.jar"
echo "==> Reactor: ${REACTOR_ROOT}"
echo "==> Version: ${VERSION}"
echo "==> Release date: ${RELEASE_DATE}"
echo "==> Output dir: ${OUT_DIR}"
(cd "$REACTOR_ROOT" && mvn clean package -DskipTests)
[[ -f "$SOURCE_JAR" ]] || die "未找到构建产物: ${SOURCE_JAR}(请先确认 starter 已成功 repackage"
mkdir -p "$OUT_DIR"
cp -f "$SOURCE_JAR" "${OUT_DIR}/${JAR_BASENAME}"
copy_if_exists() {
local src="$1" dest="$2"
if [[ -f "$src" ]]; then
cp -f "$src" "$dest"
echo "==> 已复制: $(basename "$src") <- ${src}"
else
echo "WARN: 跳过(文件不存在): $src" >&2
fi
}
copy_if_exists "${DEPLOY_TEMPLATE}/application-node.properties" "${OUT_DIR}/application-node.properties"
copy_if_exists "${DEPLOY_TEMPLATE}/bootstrap.properties" "${OUT_DIR}/bootstrap.properties"
# 可选:与 jar 内 logging.config=classpath:recognition-logback.xml 搭配外置覆盖时使用
copy_if_exists "${REACTOR_ROOT}/cwos-component-organization-starter/src/main/resources/recognition-logback.xml" "${OUT_DIR}/recognition-logback.xml"
# 组织组件交付包内附带与策略表相关的 SQL(执行库以脚本注释为准;多为 cw-elevator-application_init 含 component-organization 主数据 id
SQL_DIR="${OUT_DIR}/sql"
mkdir -p "${SQL_DIR}"
SQL_NAMES=(
tenant_visitor_floor_policy.sql
tenant_visitor_floor_policy_v2.sql
tenant_visitor_floor_policy_migrate_org_id.sql
tenant_visitor_floor_policy_init_guangfa_fund.sql
tenant_visitor_floor_policy_init_property_mgmt_6f.sql
organization_tenant_visitor_floor_policy.sql
organization_tenant_visitor_floor_policy_v2.sql
organization_tenant_visitor_floor_policy_init_guangfa_fund.sql
organization_tenant_visitor_floor_policy_init_property_mgmt_6f.sql
organization_tenant_visitor_floor_policy_full_install.sql
)
for base in "${SQL_NAMES[@]}"; do
copy_if_exists "${DOCS_SQL}/${base}" "${SQL_DIR}/${base}"
done
if [[ -d "${SQL_DIR}" ]] && [[ -n "$(find "${SQL_DIR}" -maxdepth 1 -name '*.sql' -print -quit)" ]]; then
cat > "${SQL_DIR}/README.txt" << 'EOSQL'
本目录 SQL 与源码 docs/sql 下权威文件一致,随组织组件发布包一并交付。
说明(概要):
- tenant_visitor_floor_policy.sql:电梯库,先 DROP 再 CREATE(含 org_id 最终结构);见各 init 文件头。
- organization_tenant_visitor_floor_policy*.sql:组织库 component-organizationDDL/full_install 会先 DROP 策略表再重建。
- tenant_visitor_floor_policy_migrate_org_id.sql:迁移/运维说明(注释为主)。
- *_init_*.sql:种子数据(DML),org_id 需与 cw_is_organization 一致。
执行顺序与现网是否已有表有关,实施前请对照 docs/business 下设计文档。
EOSQL
echo "==> 已写入: sql/README.txt"
fi
cat > "${OUT_DIR}/start.sh" << EOF
#!/usr/bin/env bash
set -euo pipefail
DIR="\$(cd "\$(dirname "\${BASH_SOURCE[0]}")" && pwd)"
cd "\${DIR}"
JAR_NAME="${JAR_BASENAME}"
# classpath 加载 jar 内公共 application.propertiesfile 加载本目录 application-node + bootstrap
exec "\${JAVA_HOME:-/usr/lib/jvm/java-8-openjdk-amd64}/bin/java" -jar "\${DIR}/\${JAR_NAME}" \\
--spring.config.location="classpath:/,file:\${DIR}/" \\
--spring.config.name=application,application-node \\
--logging.path="\${DIR}/logs" \\
"\$@"
EOF
chmod +x "${OUT_DIR}/start.sh"
(cd "$OUT_DIR" && ln -sf "${JAR_BASENAME}" "${SYMLINK_NAME}")
SHA256="$(sha256sum "${OUT_DIR}/${JAR_BASENAME}" | awk '{ print $1 }')"
ABS_OUT="$(cd "$OUT_DIR" && pwd)"
ABS_JAR="${ABS_OUT}/${JAR_BASENAME}"
{
echo "Component: ninca-common-component-organization (starter fat jar)"
echo "Reactor version (pom.xml): ${VERSION}"
echo "Release bundle: ${BUNDLE_NAME}"
echo "Date stamp: ${RELEASE_DATE}"
echo ""
echo "Build command:"
echo " export JAVA_HOME=${JAVA_HOME}"
echo " cd \"${REACTOR_ROOT}\""
echo " mvn clean package -DskipTests"
echo ""
echo "Fat jar artifact:"
echo " ${ABS_JAR}"
echo "SHA256:"
echo " ${SHA256}"
echo ""
echo "Files in this directory:"
echo " - sql/(租户访客策略相关脚本,来自 docs/sql;含 README.txt"
echo " - application-node.properties(节点 Consul/instance-id;若部署包模板存在则已复制)"
echo " - bootstrap.properties (若部署包模板存在则已复制)"
echo " - ${JAR_BASENAME}"
echo " - ${SYMLINK_NAME}"
echo " - RELEASE.txt"
echo " - start.sh"
echo ""
echo "Symlink (ops naming):"
echo " ${SYMLINK_NAME} -> ${JAR_BASENAME}"
echo ""
echo "Start:"
echo " bash \"${ABS_OUT}/start.sh\""
} > "${OUT_DIR}/RELEASE.txt"
echo ""
echo "========================================"
echo "Release bundle: ${ABS_OUT}"
echo "SHA256 (${JAR_BASENAME}):"
echo " ${SHA256}"
echo "========================================"