Files
ERP/README.md
T
Qiufeng f9545a9d0f
Signed Release / release (push) Failing after 33s
fix: harden first-run install and clean reinstall
2026-08-04 12:59:24 +08:00

18 KiB
Raw Blame History

凯迪 ERP + OA 一体化平台

凯迪 ERP + OA 是面向企业内部运营的综合业务平台,覆盖组织协同、审批、合同、采购、库存、财务、人力、项目、制造、运营、档案、审计和系统管理等工作域。前端采用 Vue 3 + TypeScript + Element Plus,后端采用 Spring Boot 3.2 + Spring Data JPA。

生产部署固定使用 PostgreSQL 15 或更高版本,不使用 Docker。源码目录保留的 SQLite 配置只用于本地开发和测试;正式 Release JAR 会排除 SQLite 驱动和社区方言。

项目现状

  • 业务范围覆盖 29 个机构/部门,包含约 700 个 Vue 业务页面和大规模 REST/JPA 模型。
  • 当前有效后端位于 oa-backend/
  • 当前有效前端位于 ofbiz-framework/plugins/modern-ui/app/;其上层旧 OFBiz 运行框架已经弃用。
  • 前端构建产物直接写入后端 src/main/resources/static/,由同一个 Spring Boot JAR 提供页面与 /api/oa/* API。
  • 已实现非 Docker 一键安装、PostgreSQL Flyway 迁移、独立网页安装向导、Gitea Release 更新、Ed25519/SHA-256 校验、服务重启、健康检查和自动回滚。
  • 业务功能和外部系统接入仍需按项目需求验收;技术部署链路通过不等于可以跳过生产安全、备份和数据迁移评审。

两种运行模式

模式 数据库 用途 关键约束
源码开发 SQLite 或 PostgreSQL 本机开发、界面调试、自动化测试 SQLite 仅为零配置开发选项,不代表在线环境
正式安装 PostgreSQL 15+ 服务器部署、在线更新 使用签名 Release、systemd/launchd,生产 JAR 不含 SQLite

架构

Browser / Mobile Web
        |
        v
Spring Boot 3.2
  |- Vue 3 static application
  |- /api/oa/* REST API
  |- authentication and permission gates
  |- system update administration API
        |
        v
PostgreSQL 15+  <--- Flyway migrations

Administrator -> System Update UI -> erp-update helper
                                      |- Gitea Release API
                                      |- Ed25519 + SHA-256 verification
                                      |- atomic current symlink switch
                                      `- health check and rollback

仓库结构

.
|- README.md
|- run.command                       # macOS 本地预览与 ngrok 启动器
|- install.sh                        # 非 Docker 一键安装器
|- uninstall.sh                      # Linux 完整卸载与数据库 schema 重置
|- distribution/
|  |- bin/erp-run                    # 正式环境应用启动器
|  `- bin/erp-update                 # 下载、校验、切换和回滚助手
|- scripts/package-release.sh        # PostgreSQL-only Release 打包与签名
|- .gitea/workflows/release.yml      # v* tag 发布流水线
|- oa-backend/                       # Spring Boot 后端
|  `- src/main/resources/
|     |- application.yml             # 源码开发默认配置
|     |- application-postgres.yml    # 正式 PostgreSQL profile
|     `- db/migration/postgresql/    # Flyway 迁移
|- ofbiz-framework/plugins/modern-ui/app/
|  |- src/data/oaModules.ts          # 导航与路由数据源
|  `- src/oa/pages/                  # Vue 业务页面
|- tests/                            # 启动器和 Release 脚本测试
|- docs/                             # 部署、设计和实现文档
`- requirements/                     # 原始需求与合规审计材料

本地构建

克隆仓库并进入开发分支:

git clone git@38.76.196.225:awaioi/ERP.git
cd ERP
git switch dev
git config core.hooksPath .githooks

环境要求

  • Java 17 或更高版本
  • Node.js 和 npm
  • Python 3、curl、lsof
  • 使用公网预览时需要已登录并配置好的 ngrok 3
  • PostgreSQL 模式需要 PostgreSQL 15+ 以及可连接的数据库账号

1. 构建前端

cd ofbiz-framework/plugins/modern-ui/app
npm ci
NODE_OPTIONS=--max-old-space-size=8192 npm run build

Vite 会清空并重新生成 oa-backend/src/main/resources/static/。修改前端后必须先执行前端构建,再执行 bootJar,否则 JAR 中仍是旧页面。

2. 构建后端

开发构建保留 SQLite 运行库:

cd oa-backend
./gradlew test
./gradlew bootJar

PostgreSQL-only 正式构建:

cd oa-backend
./gradlew clean bootJar -PreleaseVersion=0.2.0 -PproductionBuild=true

正式 JAR 必须包含 PostgreSQL 驱动,并且不得包含 sqlite-jdbchibernate-community-dialects

一键启动本地预览

根目录的 run.command 会检查环境、启动或复用 ERP 后端、启动或复用固定 ngrok 隧道、验证本地登录和公网页面,然后打开浏览器。它不会安装 PostgreSQL、构建前端或生成 JAR。

chmod +x run.command
./run.command

macOS 也可以在 Finder 中双击 run.command。终端保持运行用于监控服务,按 Ctrl+C 只会停止本次启动器自己创建的进程,不会批量终止其他 Java 进程。

常用覆盖项:

环境变量 默认值 说明
ERP_RUN_BACKEND_PORT 8091 本地 Spring Boot 端口
ERP_RUN_JAR_PATH 最新的 oa-backend-*.jar 指定要运行的 JAR
ERP_RUN_PROFILE 当前环境的 Spring profile 正式环境设为 postgres
ERP_RUN_CONFIG_FILE 读取安装器生成的 erp.env
ERP_RUN_PUBLIC_URL 固定 ngrok 域名 公网预览地址
ERP_RUN_NGROK_API_PORT 4040 ngrok 本地管理端口
ERP_RUN_NO_OPEN 0 设为 1 时不自动打开浏览器

直接以 PostgreSQL profile 运行源码构建时:

export SPRING_PROFILES_ACTIVE=postgres
export OA_DB_URL='jdbc:postgresql://127.0.0.1:5432/kaidi_erp?sslmode=disable'
export OA_DB_USERNAME='kaidi_erp'
export OA_DB_PASSWORD='replace-with-a-strong-password'
./run.command

默认本地地址为 http://127.0.0.1:8091。开发预览固定域名为:

https://resonant-elated-launder.ngrok-free.dev/

演示环境初始账号为 admin / 123456。任何共享或生产环境都必须立即更换默认密码,并限制公网访问。

非 Docker 正式安装

安装器支持 64 位 Linux amd64/arm64 和 macOS arm64/amd64。Linux 安装需要 rootmacOS 不应使用 sudo 启动 LaunchAgent。正式安装前必须已有包含下列四个资产的 Gitea Release

  • kaidi-erp-<version>.tar.gz
  • kaidi-erp-installer-<version>.jar
  • SHA256SUMS
  • SHA256SUMS.sig

Linux 一键安装

生产环境应先为 Gitea 配置 HTTPS

curl -fsSL https://git.example.com/awaioi/ERP/raw/branch/main/install.sh \
  | sudo -E bash -s -- \
      --gitea-url https://git.example.com \
      --repository awaioi/ERP

命令行只做环境准备:优先使用已有的 Java 17+,缺少时通过当前系统的 apt-getdnfyumzypper 或 Homebrew 安装 Java、curl、tar、Python 3 和 OpenSSL 3,然后下载并启动独立安装器。数据库信息不在命令行输入。

Linux 生产服务要求主机使用 systemd;没有 systemd 的容器、WSL 或精简系统只能显式使用 --no-service 做开发验收,在线更新也会保持关闭。

安装器启动后会输出带一次性令牌的局域网地址,例如:

Setup URL: http://192.168.1.20:8091/?token=<one-time-token>

首次打开该地址进入网页向导,依次完成环境检查、PostgreSQL 地址/端口/库名/账号/密码/SSL 测试、管理员账号/姓名/密码设置、数据库迁移和初始化。项目当前没有 Redis 依赖,因此向导不会显示 Redis 配置项。正式服务真实健康检查通过后,启动器才会原子写入安装锁并物理删除 installer/install.pending

PostgreSQL 必须使用专用空数据库,网页中填写的账号必须是该数据库的所有者。该约束保证账号拥有 public schema 建表权限,并能持有安装器创建的 pg_trgm 扩展;只授予 CONNECT 权限不足以完成迁移。

macOS

macOS 需要 Homebrew,并使用已有 PostgreSQL

curl -fsSL https://git.example.com/awaioi/ERP/raw/branch/main/install.sh \
  | bash -s -- \
      --gitea-url https://git.example.com \
      --repository awaioi/ERP

当前 HTTP 测试服务器

当前 Gitea 地址 http://38.76.196.225:10099 只允许用于开发验收:

(
  set -e
  tmp="$(mktemp)"
  trap 'rm -f -- "$tmp"' EXIT
  curl -fsSL http://38.76.196.225:10099/awaioi/ERP/raw/tag/v0.3.1/install.sh -o "$tmp"
  printf '%s  %s\n' '89a3c45e76f500c9475cb596ea29e3518bfafdc59f36dd3c7b316ed3cdd0448c' "$tmp" | sha256sum -c -
  sudo -E bash "$tmp" \
    --gitea-url http://38.76.196.225:10099 \
    --repository awaioi/ERP \
    --allow-insecure
)

只有 v0.3.1 Release 发布后这条命令才可下载安装包。固定 tag 和 SHA-256 只用于保护当前 HTTP 引导脚本不被传输途中篡改;Release 资产仍会继续执行 Ed25519 和 SHA-256 双重校验。HTTP 会暴露请求、Release 元数据和可能使用的访问令牌,不得作为生产方案。

完整卸载后重装

以下命令具有破坏性:它会先停止服务,使用现有配置中的 ERP 数据库账号删除并重建目标数据库的 public schema,然后删除 systemd unit、程序、配置、状态和日志。脚本只允许数据库所有者执行 schema 清理,并拒绝 postgrestemplate0template1 和危险文件路径。

(
  set -e
  tmp="$(mktemp)"
  trap 'rm -f -- "$tmp"' EXIT
  curl -fsSL http://38.76.196.225:10099/awaioi/ERP/raw/tag/v0.3.1/uninstall.sh -o "$tmp"
  printf '%s  %s\n' '98c56fed2fd4d01874e4ab5a1a4f3ec42ec3e29b315ffd87385a95488d587546' "$tmp" | sha256sum -c -
  sudo -E bash "$tmp" --purge-database --yes
)

卸载器会先核对安装配置、路径和 systemd 停止状态,再清理数据库和文件。卸载成功后,再执行上面的 Linux 一键安装命令。不要对包含其他系统数据的共享数据库运行此命令。

正式安装目录

Linux 默认路径:

内容 路径
程序和版本目录 /opt/kaidi-erp
环境配置 /etc/kaidi-erp/erp.env
更新状态 /var/lib/kaidi-erp/update-state.json
安装状态 /var/lib/kaidi-erp/install.pending/var/lib/kaidi-erp/install.lock
首次安装器 /opt/kaidi-erp/installer/(健康后自动删除)
systemd 服务 kaidi-erp.service

使用 --no-service 只用于开发验收:启动器会在当前用户下运行,但默认设置 OA_UPDATE_ENABLED=false。生产环境应使用 systemd/launchd,让在线更新可以在 Java 进程退出后自动拉起新版本。

在线更新与回滚

管理员登录后进入:

应用定制平台 -> 系统更新

对应前端路由为 /appdev/update,后端 API 为 /api/oa/system-update/*。更新过程如下:

  1. 从 Gitea 读取 stable channel 的最新 Release。
  2. 下载归档、SHA256SUMS 和 Ed25519 签名(Release 里的独立安装器资产只用于首次安装)。
  3. 先验证签名和 SHA-256,再拒绝路径穿越、绝对路径、符号链接、硬链接和结构不完整的归档。
  4. 校验 manifest.json 中的版本、database=postgresqlrollbackCompatible=true
  5. 可选执行 pg_dump,将新版本写入独立目录。
  6. 原子切换 current 符号链接,并停止旧 Java 进程。
  7. systemd/launchd 拉起新版本,更新助手等待新的 PID 和 /api/oa/health
  8. 新版本不健康时切回上一链接,终止故障进程并再次验证旧版本健康状态。

同一安装目录使用操作系统文件锁,不能并发执行两个更新任务。也可以手工触发:

/opt/kaidi-erp/current/bin/erp-update install 0.2.0

应用回滚不等于数据库回滚。包含不可逆 Flyway 迁移的版本必须先保证旧应用仍兼容新结构,并建议在安装配置中启用:

ERP_UPDATE_BACKUP_MODE=pg_dump

更新助手不会自动覆盖生产数据库。需要恢复数据库时,应由管理员确认后使用 pg_restore

Gitea Release 发布

推送 v* tag 会触发 .gitea/workflows/release.yml。流水线会先执行 shell、后端和独立安装器测试,再构建前端、生成 PostgreSQL-only JAR、打包、签名,并创建或更新对应 Gitea Release。

Actions 前置配置

Gitea 1.27 仓库需要启用 Actions,并配置带 ubuntu-latest 标签的在线 Runner。Runner 必须提供:

  • Java 17+
  • Node.js 和 npm
  • Python 3
  • curl、tar
  • OpenSSL 3,且支持 Ed25519

流水线使用 Gitea 内置短期 GITEA_TOKEN,权限限定为代码只读、当前仓库 Release 可写。仓库设置只需添加:

  • Secret RELEASE_PRIVATE_KEY_B64
  • 当前纯 HTTP 测试服务器额外添加 Variable ERP_RELEASE_ALLOW_INSECURE_HTTP=1Gitea 禁止变量名以保留前缀 GITEA_GITHUB_ 开头)

签名私钥不得提交到 Git。生成 Secret 值:

base64 < ~/.config/kaidi-erp/release-signing-key.pem | tr -d '\n'

发布稳定版本:

git switch main
git pull --ff-only origin main
git tag -a v0.2.0 -m 'Kaidi ERP v0.2.0'
git push origin v0.2.0

发布完成后必须确认 Release 页面存在四个资产,并使用仓库中的 distribution/release-public-key.pem 验证签名。私钥与该公钥不匹配时打包脚本会直接失败。

本地手工生成签名资产:

ERP_RELEASE_PRIVATE_KEY_FILE="$HOME/.config/kaidi-erp/release-signing-key.pem" \
  bash scripts/package-release.sh 0.2.0

配置参考

环境变量 说明 默认值
SPRING_PROFILES_ACTIVE 正式环境必须为 postgres 源码默认 profile
OA_DB_URL JDBC PostgreSQL URL 正式环境必填
OA_DB_USERNAME PostgreSQL 用户 正式环境必填
OA_DB_PASSWORD PostgreSQL 密码 正式环境必填
OA_DB_POOL_MAX 最大连接池 20
OA_DB_POOL_MIN 最小空闲连接 2
OA_UPDATE_ENABLED 启用管理后台在线更新 安装服务时为 true
OA_UPDATE_GITEA_BASE_URL Gitea 外部地址 正式安装时写入
OA_UPDATE_REPOSITORY Release 仓库 awaioi/ERP
OA_UPDATE_CHANNEL 更新通道 stable
OA_UPDATE_TOKEN 私有仓库下载令牌 空;公开仓库不需要
OA_UPDATE_ALLOW_INSECURE_HTTP 允许 HTTP 更新地址 false
OA_SEED_DEMO 是否生成演示数据 正式安装为 false
ERP_UPDATE_BACKUP_MODE 更新前数据库备份 none,可设 pg_dump
ERP_UPDATE_HEALTH_TIMEOUT_SECONDS 新旧版本健康检查超时 120
ERP_UPDATE_HEALTH_POLL_SECONDS 健康轮询间隔 2

测试与验收

后端

cd oa-backend
./gradlew test

前端类型检查与构建

cd ofbiz-framework/plugins/modern-ui/app
npm ci
NODE_OPTIONS=--max-old-space-size=8192 npm run build

启动器与发布脚本

bash tests/run-command.test.sh
bash tests/release-scripts.test.sh

已运行服务的 OA 冒烟和集成测试

OA=http://127.0.0.1:8091 bash oa-smoke.sh
OA=http://127.0.0.1:8091 bash oa-itest.sh

测试脚本会创建业务测试数据,应使用测试数据库,不要直接对生产数据库运行写入型集成测试。

安全与运维注意事项

  • 正式 Gitea、安装和更新流量必须使用 HTTPS;--allow-insecure 仅限隔离测试环境。
  • RELEASE_PRIVATE_KEY_B64 和 PostgreSQL 密码不得写入 Git、日志或聊天记录。
  • 新增返回金额、个人信息或机密汇总的 API 时,必须复核 AuthInterceptor 的敏感读取权限前缀。
  • 金额字段和计算统一使用 BigDecimal,禁止使用 double 处理货币。
  • 不要使用 pkill javakillall java;只停止明确属于本项目的 PID。
  • 修改前端后始终先执行前端构建,再打包后端。
  • 每次包含数据库结构变更的 Release 都必须新增 Flyway 迁移并做新库首次迁移、旧库升级和回滚兼容性验证。
  • 默认演示账号只能用于开发环境,生产环境需要更换密码并实施最小权限、备份、监控和审计策略。

常见问题

安装器提示没有 Release

Gitea 仓库尚未发布首个可安装版本,或 Release 缺少四个必需资产。先检查 /api/v1/repos/awaioi/ERP/releases/latest 和 Release 页面。

如何填写数据库

数据库连接信息只在首次网页向导填写。安装器会执行 SELECT 1、检查 PostgreSQL 15+ 并创建/验证 pg_trgm,错误凭据不会启动正式应用,也不会把密码写入响应或日志。数据库中已经有业务用户时,安装器会拒绝接管。

PostgreSQL profile 启动失败

确认 SPRING_PROFILES_ACTIVE=postgres,并检查 OA_DB_URLOA_DB_USERNAMEOA_DB_PASSWORD。正式 profile 会执行 Flyway,再使用 Hibernate ddl-auto=validate 校验实体与数据库结构。

run.command 提示端口被占用

启动器只会复用能够通过项目健康检查的监听进程。其他程序占用端口时不会被自动终止;请停止对应程序或设置新的 ERP_RUN_BACKEND_PORT

ngrok 无法启动

运行 ngrok config check 确认配置有效,并检查固定域名是否属于当前 ngrok 账号。可以通过 ERP_RUN_NGROK_BINERP_RUN_NGROK_CONFIGERP_RUN_NGROK_API_PORT 覆盖路径与端口。

签名验证失败

不要跳过验证。确认 Release 的四个资产来自同一次构建、SHA256SUMS 未被改写、签名私钥与 distribution/release-public-key.pem 匹配。

Git 协作

  • dev 是日常开发分支。
  • main 只接收已经通过构建、测试、启动和冒烟验证的稳定提交。
  • 新环境执行 git config core.hooksPath .githooks 启用仓库 hooks。
  • 不要在脏工作区执行破坏性 reset;先确认哪些修改属于正在进行的开发。

远程仓库:

git@38.76.196.225:awaioi/ERP.git

延伸文档