diff --git a/.DS_Store b/.DS_Store index f5c794c..bccf925 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/bin/.DS_Store b/bin/.DS_Store new file mode 100644 index 0000000..c597d9d Binary files /dev/null and b/bin/.DS_Store differ diff --git a/bin/prod/deploy_from_package.sh b/bin/prod/deploy_from_package.sh index 61dcf19..39fe37a 100755 --- a/bin/prod/deploy_from_package.sh +++ b/bin/prod/deploy_from_package.sh @@ -4,7 +4,7 @@ set -eu JAVA_BIN="/home/webapp/env/java/bin/java" BACKEND_PORT=63310 -SPRING_PROFILE="pro" +SPRING_PROFILE="uat" JAVA_OPTS="-Duser.timezone=Asia/Shanghai -Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError" SCRIPT_DIR=$(CDPATH= cd -- "$(dirname "$0")" && pwd) diff --git a/bin/prod/deploy_release.sh b/bin/prod/deploy_release.sh new file mode 100755 index 0000000..c66b8e1 --- /dev/null +++ b/bin/prod/deploy_release.sh @@ -0,0 +1,245 @@ +#!/bin/sh + +set -eu + +WEBAPP_ROOT="/home/webapp" +ENV_ROOT="$WEBAPP_ROOT/env" +APP_ROOT="$WEBAPP_ROOT/loan-pricing" +JAVA_HOME="$ENV_ROOT/java" +NGINX_HOME="$ENV_ROOT/nginx" +NGINX_CONF="$NGINX_HOME/conf/nginx.conf" +BACKEND_DIR="$APP_ROOT/backend" +FRONTEND_DIR="$APP_ROOT/frontend" +FRONTEND_DIST_DIR="$FRONTEND_DIR/dist" +BACKUP_DIR="$APP_ROOT/backup" +LOG_DIR="$APP_ROOT/logs" +RUN_DIR="$APP_ROOT/run" +TMP_DIR="$APP_ROOT/tmp" +BACKEND_JAR="$BACKEND_DIR/ruoyi-admin.jar" +FRONTEND_PORT=63311 +JAVA_RESTART_SCRIPT="$WEBAPP_ROOT/restart_java.sh" + +timestamp() { + date "+%Y-%m-%d %H:%M:%S" +} + +log_info() { + printf '[%s] %s\n' "$(timestamp)" "$1" +} + +log_error() { + printf '[%s] %s\n' "$(timestamp)" "$1" >&2 +} + +usage() { + cat <<'EOF' +用法: + ./bin/prod/deploy_release.sh <发布压缩包路径> +EOF +} + +require_root() { + if [ "$(id -u)" -ne 0 ]; then + log_error "请使用 root 用户执行部署脚本" + exit 1 + fi +} + +require_command() { + if ! command -v "$1" >/dev/null 2>&1; then + log_error "缺少命令: $1" + exit 1 + fi +} + +ensure_runtime_dirs() { + mkdir -p "$BACKEND_DIR" "$FRONTEND_DIR" "$BACKUP_DIR" "$LOG_DIR" "$RUN_DIR" "$TMP_DIR" +} + +cleanup() { + if [ -n "${WORK_DIR:-}" ] && [ -d "$WORK_DIR" ]; then + rm -rf "$WORK_DIR" + fi +} + +extract_release_package() { + release_archive="$1" + release_extract_dir="$2" + + mkdir -p "$release_extract_dir" + + case "$release_archive" in + *.zip) + unzip -oq "$release_archive" -d "$release_extract_dir" + ;; + *.tar.gz|*.tgz) + tar -xzf "$release_archive" -C "$release_extract_dir" + ;; + *.tar) + tar -xf "$release_archive" -C "$release_extract_dir" + ;; + *) + log_error "不支持的发布包格式: $release_archive" + exit 1 + ;; + esac +} + +assert_single_file() { + search_dir="$1" + file_name="$2" + description="$3" + count=$(find "$search_dir" -type f -name "$file_name" | wc -l | tr -d ' ') + + if [ "$count" -ne 1 ]; then + log_error "$description 数量不正确,期望 1 个,实际 $count 个" + exit 1 + fi + + find "$search_dir" -type f -name "$file_name" | head -n 1 +} + +assert_single_jar() { + search_dir="$1" + count=$(find "$search_dir" -type f -name '*.jar' | wc -l | tr -d ' ') + + if [ "$count" -ne 1 ]; then + log_error "后端 jar 数量不正确,期望 1 个,实际 $count 个" + exit 1 + fi + + find "$search_dir" -type f -name '*.jar' | head -n 1 +} + +backup_current_release() { + backup_stamp=$(date "+%Y%m%d%H%M%S") + CURRENT_BACKUP_DIR="$BACKUP_DIR/$backup_stamp" + + mkdir -p "$CURRENT_BACKUP_DIR/backend" "$CURRENT_BACKUP_DIR/frontend" + + if [ -f "$BACKEND_JAR" ]; then + cp -a "$BACKEND_JAR" "$CURRENT_BACKUP_DIR/backend/" + fi + + if [ -d "$FRONTEND_DIST_DIR" ]; then + cp -a "$FRONTEND_DIST_DIR" "$CURRENT_BACKUP_DIR/frontend/" + fi + + log_info "旧版本已备份到: $CURRENT_BACKUP_DIR" +} + +deploy_backend() { + source_jar="$1" + + rm -f "$BACKEND_DIR"/*.jar + cp "$source_jar" "$BACKEND_JAR" +} + +resolve_frontend_source_dir() { + unzip_dir="$1" + + if [ -f "$unzip_dir/index.html" ]; then + printf '%s\n' "$unzip_dir" + return 0 + fi + + if [ -f "$unzip_dir/dist/index.html" ]; then + printf '%s\n' "$unzip_dir/dist" + return 0 + fi + + candidate=$(find "$unzip_dir" -type f -name 'index.html' | head -n 1) + if [ -z "${candidate:-}" ]; then + log_error "dist.zip 解压后未找到 index.html" + exit 1 + fi + + dirname "$candidate" +} + +deploy_frontend() { + dist_zip="$1" + dist_unpack_dir="$WORK_DIR/frontend" + + mkdir -p "$dist_unpack_dir" + unzip -oq "$dist_zip" -d "$dist_unpack_dir" + + frontend_source_dir=$(resolve_frontend_source_dir "$dist_unpack_dir") + rm -rf "$FRONTEND_DIST_DIR" + mkdir -p "$FRONTEND_DIST_DIR" + cp -a "$frontend_source_dir"/. "$FRONTEND_DIST_DIR"/ +} + +reload_nginx() { + nginx_pid_file="$RUN_DIR/nginx.pid" + + "$NGINX_HOME/sbin/nginx" -t -c "$NGINX_CONF" + + if [ -f "$nginx_pid_file" ]; then + nginx_pid=$(cat "$nginx_pid_file" 2>/dev/null || true) + if [ -n "${nginx_pid:-}" ] && kill -0 "$nginx_pid" 2>/dev/null; then + "$NGINX_HOME/sbin/nginx" -c "$NGINX_CONF" -s reload + log_info "Nginx 已重载,前端端口: $FRONTEND_PORT" + return 0 + fi + fi + + "$NGINX_HOME/sbin/nginx" -c "$NGINX_CONF" + log_info "Nginx 已启动,前端端口: $FRONTEND_PORT" +} + +main() { + if [ "$#" -ne 1 ]; then + usage + exit 1 + fi + + require_root + require_command tar + require_command unzip + require_command find + + release_archive="$1" + if [ ! -f "$release_archive" ]; then + log_error "发布压缩包不存在: $release_archive" + exit 1 + fi + + if [ ! -x "$JAVA_HOME/bin/java" ]; then + log_error "未检测到 Java,请先执行 ./bin/prod/install_env.sh" + exit 1 + fi + + if [ ! -x "$NGINX_HOME/sbin/nginx" ]; then + log_error "未检测到 Nginx,请先执行 ./bin/prod/install_env.sh" + exit 1 + fi + + if [ ! -x "$JAVA_RESTART_SCRIPT" ]; then + log_error "未检测到 Java 重启脚本: $JAVA_RESTART_SCRIPT" + exit 1 + fi + + ensure_runtime_dirs + WORK_DIR=$(mktemp -d "$TMP_DIR/release.XXXXXX") + trap cleanup EXIT INT TERM + + extract_release_package "$release_archive" "$WORK_DIR/package" + + backend_jar_source=$(assert_single_jar "$WORK_DIR/package") + frontend_dist_source=$(assert_single_file "$WORK_DIR/package" 'dist.zip' '前端 dist.zip') + + backup_current_release + "$JAVA_RESTART_SCRIPT" stop + deploy_backend "$backend_jar_source" + deploy_frontend "$frontend_dist_source" + "$JAVA_RESTART_SCRIPT" start + reload_nginx + + log_info "部署完成" + log_info "后端 jar: $BACKEND_JAR" + log_info "前端目录: $FRONTEND_DIST_DIR" + log_info "备份目录: $CURRENT_BACKUP_DIR" +} + +main "$@" diff --git a/bin/prod/install_env.sh b/bin/prod/install_env.sh new file mode 100755 index 0000000..ae0dfc7 --- /dev/null +++ b/bin/prod/install_env.sh @@ -0,0 +1,244 @@ +#!/bin/sh + +set -eu + +WEBAPP_ROOT="/home/webapp" +ENV_ROOT="$WEBAPP_ROOT/env" +APP_ROOT="$WEBAPP_ROOT/loan-pricing" +JAVA_HOME="$ENV_ROOT/java" +NGINX_HOME="$ENV_ROOT/nginx" +NGINX_CONF="$NGINX_HOME/conf/nginx.conf" +BACKEND_DIR="$APP_ROOT/backend" +FRONTEND_DIR="$APP_ROOT/frontend" +BACKUP_DIR="$APP_ROOT/backup" +LOG_DIR="$APP_ROOT/logs" +RUN_DIR="$APP_ROOT/run" +TMP_DIR="$APP_ROOT/tmp" +BACKEND_PORT=63310 +FRONTEND_PORT=63311 + +timestamp() { + date "+%Y-%m-%d %H:%M:%S" +} + +log_info() { + printf '[%s] %s\n' "$(timestamp)" "$1" +} + +log_error() { + printf '[%s] %s\n' "$(timestamp)" "$1" >&2 +} + +require_root() { + if [ "$(id -u)" -ne 0 ]; then + log_error "请使用 root 用户执行安装脚本" + exit 1 + fi +} + +require_command() { + if ! command -v "$1" >/dev/null 2>&1; then + log_error "缺少命令: $1" + exit 1 + fi +} + +ensure_base_dirs() { + mkdir -p "$ENV_ROOT" "$BACKEND_DIR" "$FRONTEND_DIR" "$BACKUP_DIR" "$LOG_DIR" "$RUN_DIR" "$TMP_DIR" +} + +find_archive() { + search_kind="$1" + found="" + + case "$search_kind" in + java) + set -- "$WEBAPP_ROOT/openjdk" "$WEBAPP_ROOT" + patterns='openjdk*.tar.gz openjdk*.tgz jdk*.tar.gz jdk*.tgz' + ;; + nginx) + set -- "$WEBAPP_ROOT/nginx" "$ENV_ROOT" "$WEBAPP_ROOT" + patterns='nginx-*.tar.gz nginx-*.tgz' + ;; + *) + log_error "未知的安装包类型: $search_kind" + exit 1 + ;; + esac + + for dir in "$@"; do + if [ ! -d "$dir" ]; then + continue + fi + + for pattern in $patterns; do + candidate=$(find "$dir" -maxdepth 1 -type f -name "$pattern" | sort | head -n 1) + if [ -n "${candidate:-}" ]; then + found="$candidate" + break + fi + done + + if [ -n "${found:-}" ]; then + break + fi + done + + if [ -z "${found:-}" ]; then + log_error "未找到 $search_kind 安装包" + exit 1 + fi + + printf '%s\n' "$found" +} + +install_yum_dependencies() { + require_command yum + + log_info "安装系统依赖" + yum install -y \ + gcc \ + make \ + pcre \ + pcre-devel \ + zlib \ + zlib-devel \ + openssl \ + openssl-devel \ + tar \ + gzip \ + unzip \ + which \ + findutils \ + procps-ng \ + iproute +} + +install_java() { + java_archive="$1" + + log_info "安装 Java: $java_archive" + rm -rf "$JAVA_HOME" + mkdir -p "$JAVA_HOME" + tar -xzf "$java_archive" -C "$JAVA_HOME" --strip-components=1 + + if [ ! -x "$JAVA_HOME/bin/java" ]; then + log_error "Java 安装失败,未找到 $JAVA_HOME/bin/java" + exit 1 + fi + + "$JAVA_HOME/bin/java" -version >/dev/null 2>&1 +} + +install_nginx() { + nginx_archive="$1" + build_dir=$(mktemp -d "$ENV_ROOT/nginx-build.XXXXXX") + + log_info "编译安装 Nginx: $nginx_archive" + rm -rf "$NGINX_HOME" + mkdir -p "$NGINX_HOME" + + tar -xzf "$nginx_archive" -C "$build_dir" + source_dir=$(find "$build_dir" -mindepth 1 -maxdepth 1 -type d | head -n 1) + if [ -z "${source_dir:-}" ]; then + rm -rf "$build_dir" + log_error "Nginx 源码目录解析失败" + exit 1 + fi + + jobs=$(getconf _NPROCESSORS_ONLN 2>/dev/null || echo 1) + ( + cd "$source_dir" + ./configure --prefix="$NGINX_HOME" --with-http_ssl_module + make -j"$jobs" + make install + ) + + rm -rf "$build_dir" + + if [ ! -x "$NGINX_HOME/sbin/nginx" ]; then + log_error "Nginx 安装失败,未找到 $NGINX_HOME/sbin/nginx" + exit 1 + fi +} + +write_nginx_conf() { + log_info "写入 Nginx 配置: $NGINX_CONF" + + mkdir -p "$NGINX_HOME/conf" "$NGINX_HOME/logs" "$FRONTEND_DIR/dist" + + cat > "$NGINX_CONF" <&2 +} + +usage() { + cat <<'EOF' +用法: ./restart_java.sh [start|stop|restart|status] + +默认动作: + restart 重启后端 Java 进程 +EOF +} + +require_root() { + if [ "$(id -u)" -ne 0 ]; then + log_error "请使用 root 用户执行脚本" + exit 1 + fi +} + +ensure_runtime_dirs() { + mkdir -p "$BACKEND_DIR" "$LOG_DIR" "$RUN_DIR" +} + +is_managed_backend_pid() { + pid="$1" + if [ -z "${pid:-}" ] || ! kill -0 "$pid" 2>/dev/null; then + return 1 + fi + + args=$(ps -o args= -p "$pid" 2>/dev/null || true) + if [ -z "${args:-}" ]; then + return 1 + fi + + case "$args" in + *"$BACKEND_MARKER"*"$BACKEND_JAR"*|*"$BACKEND_JAR"*"$BACKEND_MARKER"*) + return 0 + ;; + esac + + return 1 +} + +collect_backend_pids() { + pids="" + + if [ -f "$BACKEND_PID_FILE" ]; then + file_pid=$(cat "$BACKEND_PID_FILE" 2>/dev/null || true) + if [ -n "${file_pid:-}" ] && is_managed_backend_pid "$file_pid"; then + pids="$pids $file_pid" + fi + fi + + marker_pids=$(pgrep -f "$BACKEND_MARKER" 2>/dev/null || true) + if [ -n "${marker_pids:-}" ]; then + for pid in $marker_pids; do + if is_managed_backend_pid "$pid"; then + pids="$pids $pid" + fi + done + fi + + printf '%s\n' "$(echo "$pids" | xargs 2>/dev/null || true)" +} + +stop_backend() { + pids=$(collect_backend_pids) + + if [ -z "${pids:-}" ]; then + rm -f "$BACKEND_PID_FILE" + log_info "未发现运行中的后端进程" + return 0 + fi + + log_info "停止后端进程: $pids" + for pid in $pids; do + kill -TERM "$pid" 2>/dev/null || true + done + + elapsed=0 + while [ "$elapsed" -lt 30 ]; do + remaining="" + for pid in $pids; do + if kill -0 "$pid" 2>/dev/null; then + remaining="$remaining $pid" + fi + done + + remaining=$(echo "$remaining" | xargs 2>/dev/null || true) + if [ -z "${remaining:-}" ]; then + break + fi + + sleep 1 + elapsed=$((elapsed + 1)) + done + + if [ -n "${remaining:-}" ]; then + log_info "执行强制停止: $remaining" + for pid in $remaining; do + kill -KILL "$pid" 2>/dev/null || true + done + fi + + rm -f "$BACKEND_PID_FILE" +} + +start_backend() { + if [ ! -x "$JAVA_HOME/bin/java" ]; then + log_error "未检测到 Java,可先执行 /home/webapp/install_env.sh" + exit 1 + fi + + if [ ! -f "$BACKEND_JAR" ]; then + log_error "未找到后端 jar: $BACKEND_JAR" + exit 1 + fi + + if [ -n "$(collect_backend_pids)" ]; then + log_error "检测到后端已在运行,请先执行 stop 或 restart" + exit 1 + fi + + printf '\n===== %s restart =====\n' "$(timestamp)" >> "$BACKEND_CONSOLE_LOG" + + ( + cd "$BACKEND_DIR" + nohup "$JAVA_HOME/bin/java" $JAVA_OPTS -jar "$BACKEND_JAR" --spring.profiles.active=pro --server.port="$BACKEND_PORT" >> "$BACKEND_CONSOLE_LOG" 2>&1 & + echo $! > "$BACKEND_PID_FILE" + ) + + sleep 3 + + backend_pid=$(cat "$BACKEND_PID_FILE" 2>/dev/null || true) + if [ -z "${backend_pid:-}" ] || ! kill -0 "$backend_pid" 2>/dev/null; then + log_error "后端启动失败,请检查日志: $BACKEND_CONSOLE_LOG" + exit 1 + fi + + wait_seconds=0 + while [ "$wait_seconds" -lt 30 ]; do + if ss -lnt 2>/dev/null | grep -q ":$BACKEND_PORT "; then + log_info "后端已监听端口: $BACKEND_PORT" + return 0 + fi + + sleep 1 + wait_seconds=$((wait_seconds + 1)) + done + + log_error "后端未在预期时间内监听端口 $BACKEND_PORT" + exit 1 +} + +status_backend() { + pids=$(collect_backend_pids) + if [ -n "${pids:-}" ]; then + log_info "后端正在运行,进程: $pids" + return 0 + fi + + if ss -lnt 2>/dev/null | grep -q ":$BACKEND_PORT "; then + log_info "未识别到脚本托管进程,但端口 $BACKEND_PORT 已被占用" + return 0 + fi + + log_info "后端未运行" +} + +main() { + action="${1:-restart}" + + case "$action" in + start) + start_backend + ;; + stop) + stop_backend + ;; + restart) + stop_backend + start_backend + ;; + status) + status_backend + ;; + *) + usage + exit 1 + ;; + esac +} + +main "$@" diff --git a/deploy/2026-03-31-local-nginx-java-install-manual.md b/deploy/2026-03-31-local-nginx-java-install-manual.md new file mode 100644 index 0000000..0d8dbb0 --- /dev/null +++ b/deploy/2026-03-31-local-nginx-java-install-manual.md @@ -0,0 +1,190 @@ +# 本地安装 Nginx 和 Java 手册 + +## 适用范围 + +本手册适用于需要在本地 Linux 环境手动安装贷款定价系统运行环境的场景,安装结果与当前生产脚本约定保持一致: + +- Java 安装到 `/home/webapp/env/java` +- Nginx 安装到 `/home/webapp/env/nginx` +- 项目部署目录使用 `/home/webapp/loan-pricing` +- 后端服务端口固定为 `63310` +- 前端 Nginx 端口固定为 `63311` + +## 前置条件 + +安装前请先确认: + +- 当前用户具备 `root` 权限 +- 本机已配置可用的 `yum` 源 +- `/home/webapp` 目录已存在 +- `/home/webapp` 下已准备安装包: + - `openjdk-21.0.2_linux-aarch64_bin.tar.gz` + - `nginx-1.20.2.tar.gz` + +如果安装包文件名不同,只要仍是 Java 的 `tar.gz` 包和 Nginx 的源码 `tar.gz` 包,也可以使用同样步骤。 + +## 目录规划 + +安装完成后目录结构如下: + +```text +/home/webapp +├── env +│ ├── java +│ └── nginx +└── loan-pricing + ├── backend + ├── frontend + ├── backup + ├── logs + ├── run + └── tmp +``` + +## 第一步:安装系统依赖 + +执行以下命令安装编译 Nginx 和运行部署脚本所需依赖: + +```sh +yum install -y \ + gcc \ + make \ + pcre \ + pcre-devel \ + zlib \ + zlib-devel \ + openssl \ + openssl-devel \ + tar \ + gzip \ + unzip \ + which \ + findutils \ + procps-ng \ + iproute +``` + +## 第二步:创建目录 + +执行以下命令初始化目录: + +```sh +mkdir -p \ + /home/webapp/env \ + /home/webapp/loan-pricing/backend \ + /home/webapp/loan-pricing/frontend \ + /home/webapp/loan-pricing/backup \ + /home/webapp/loan-pricing/logs \ + /home/webapp/loan-pricing/run \ + /home/webapp/loan-pricing/tmp +``` + +## 第三步:安装 Java + +解压 Java 安装包到目标目录: + +```sh +rm -rf /home/webapp/env/java +mkdir -p /home/webapp/env/java +tar -xzf /home/webapp/openjdk-21.0.2_linux-aarch64_bin.tar.gz -C /home/webapp/env/java --strip-components=1 +``` + +验证安装结果: + +```sh +/home/webapp/env/java/bin/java -version +``` + +如果能正常输出 Java 版本,说明安装成功。 + +## 第四步:安装 Nginx + +Nginx 安装包为源码包,需要先解压、编译、安装: + +```sh +rm -rf /home/webapp/env/nginx +mkdir -p /home/webapp/env/nginx +mkdir -p /home/webapp/env/nginx-build +tar -xzf /home/webapp/nginx-1.20.2.tar.gz -C /home/webapp/env/nginx-build +cd /home/webapp/env/nginx-build/nginx-1.20.2 +./configure --prefix=/home/webapp/env/nginx --with-http_ssl_module +make -j"$(getconf _NPROCESSORS_ONLN 2>/dev/null || echo 1)" +make install +``` + +安装完成后可执行文件位置为: + +```text +/home/webapp/env/nginx/sbin/nginx +``` + +## 第五步:写入 Nginx 配置 + +仓库已提供可直接参考的配置文件: + +```text +deploy/nginx.conf +``` + +将该文件内容写入 `/home/webapp/env/nginx/conf/nginx.conf` 即可。 + +## 第六步:校验 Nginx 配置 + +执行: + +```sh +/home/webapp/env/nginx/sbin/nginx -t -c /home/webapp/env/nginx/conf/nginx.conf +``` + +如果输出 `syntax is ok` 和 `test is successful`,说明配置可用。 + +## 第七步:启动 Nginx + +执行: + +```sh +/home/webapp/env/nginx/sbin/nginx -c /home/webapp/env/nginx/conf/nginx.conf +``` + +如果后续修改了配置,可执行: + +```sh +/home/webapp/env/nginx/sbin/nginx -c /home/webapp/env/nginx/conf/nginx.conf -s reload +``` + +## 第八步:验证端口 + +执行: + +```sh +ss -lnt | grep 63311 +``` + +如果能看到 `63311` 监听记录,说明前端 Nginx 已启动成功。 + +## 建议执行方式 + +如果本机已经放置了以下脚本,也可以直接使用脚本完成安装: + +```sh +cd /home/webapp +./install_env.sh +``` + +如果只需要管理后端 Java 进程,可执行: + +```sh +cd /home/webapp +./restart_java.sh start +./restart_java.sh stop +./restart_java.sh restart +./restart_java.sh status +``` + +## 常见检查项 + +- `yum` 不可用:先确认系统已配置可用的 `yum` 源 +- Java 未安装成功:检查 `/home/webapp/openjdk-*.tar.gz` 是否存在且未损坏 +- Nginx 编译失败:检查 `gcc`、`make`、`pcre-devel`、`zlib-devel`、`openssl-devel` 是否已安装 +- Nginx 启动失败:先执行 `nginx -t` 查看配置是否正确 +- 前端无法访问后端:检查本机 `63310` 端口是否已有 Java 服务监听 diff --git a/deploy/deploy.zip b/deploy/deploy.zip new file mode 100644 index 0000000..7f473dc Binary files /dev/null and b/deploy/deploy.zip differ diff --git a/deploy/nginx.conf b/deploy/nginx.conf new file mode 100644 index 0000000..457c3c6 --- /dev/null +++ b/deploy/nginx.conf @@ -0,0 +1,45 @@ +user nobody; +worker_processes 1; + +error_log /home/webapp/loan-pricing/logs/nginx-error.log warn; +pid /home/webapp/loan-pricing/run/nginx.pid; + +events { + worker_connections 1024; +} + +http { + include /home/webapp/env/nginx/conf/mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /home/webapp/loan-pricing/logs/nginx-access.log main; + + sendfile on; + keepalive_timeout 65; + client_max_body_size 100m; + + server { + listen 63311; + server_name _; + + root /home/webapp/loan-pricing/frontend/dist; + index index.html; + + location /prod-api/ { + proxy_pass http://127.0.0.1:63310/; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location / { + try_files $uri $uri/ /index.html; + } + } +} diff --git a/doc/implementation-report-2026-03-31-deploy-folder-docs.md b/doc/implementation-report-2026-03-31-deploy-folder-docs.md new file mode 100644 index 0000000..5b12bf8 --- /dev/null +++ b/doc/implementation-report-2026-03-31-deploy-folder-docs.md @@ -0,0 +1,22 @@ +# deploy 目录文档整理实施记录 + +## 修改内容 + +- 新增 `deploy` 目录下的本地安装手册: + - `deploy/2026-03-31-local-nginx-java-install-manual.md` +- 新增 `deploy` 目录下的独立 Nginx 配置文件: + - `deploy/nginx.conf` +- 安装手册中的 Nginx 配置说明调整为直接引用 `deploy/nginx.conf` +- 删除原 `doc/2026-03-31-local-nginx-java-install-manual.md`,避免同一手册在仓库内出现两份路径 + +## 路径检查 + +- 已确认安装手册当前保存路径为 `deploy/2026-03-31-local-nginx-java-install-manual.md` +- 已确认 Nginx 配置文件当前保存路径为 `deploy/nginx.conf` +- 已确认本次实施记录保存路径为 `doc/implementation-report-2026-03-31-deploy-folder-docs.md` + +## 验证结果 + +- 已执行 `ls -l deploy/2026-03-31-local-nginx-java-install-manual.md deploy/nginx.conf` +- 已人工核对 `deploy/nginx.conf` 与 `bin/prod/install_env.sh` 中写入的 Nginx 配置保持一致 +- 已人工核对手册中的目录、端口和脚本引用与当前交付物保持一致 diff --git a/doc/implementation-report-2026-03-31-local-nginx-java-install-manual.md b/doc/implementation-report-2026-03-31-local-nginx-java-install-manual.md new file mode 100644 index 0000000..00f95b9 --- /dev/null +++ b/doc/implementation-report-2026-03-31-local-nginx-java-install-manual.md @@ -0,0 +1,24 @@ +# 本地安装 Nginx 和 Java 手册实施记录 + +## 修改内容 + +- 新增本地安装手册 `deploy/2026-03-31-local-nginx-java-install-manual.md` +- 手册内容与当前生产安装脚本保持一致: + - Java 安装目录 `/home/webapp/env/java` + - Nginx 安装目录 `/home/webapp/env/nginx` + - 前端端口 `63311` + - 后端端口 `63310` +- 手册补充了系统依赖安装、目录初始化、Java 安装、Nginx 编译安装、配置写入、配置校验、启动与验证步骤 + +## 路径检查 + +- 已确认本次新增手册保存路径为 `deploy/2026-03-31-local-nginx-java-install-manual.md` +- 已确认本次实施记录保存路径为 `doc/implementation-report-2026-03-31-local-nginx-java-install-manual.md` + +## 验证结果 + +- 已人工核对手册中的安装路径、端口、Nginx 配置和现有脚本 `bin/prod/install_env.sh` +- 已确认手册内容与以下脚本约定一致: + - `bin/prod/install_env.sh` + - `bin/prod/deploy_release.sh` + - `bin/prod/restart_java.sh` diff --git a/doc/implementation-report-2026-03-31-production-deploy-scripts.md b/doc/implementation-report-2026-03-31-production-deploy-scripts.md new file mode 100644 index 0000000..bd8d742 --- /dev/null +++ b/doc/implementation-report-2026-03-31-production-deploy-scripts.md @@ -0,0 +1,51 @@ +# 生产环境安装与部署脚本实施记录 + +## 修改内容 +- 新增生产环境安装脚本 `bin/prod/install_env.sh` +- 新增生产环境部署脚本 `bin/prod/deploy_release.sh` +- 新增生产环境 Java 管理脚本 `bin/prod/restart_java.sh` +- 两份脚本需要同步放置到生产容器 `/home/webapp` 目录,便于在目标环境直接执行 +- 部署脚本改为复用独立的 Java 管理脚本完成后端启停 +- 安装脚本固定将 Java 安装到 `/home/webapp/env/java`,将 Nginx 安装到 `/home/webapp/env/nginx` +- 安装脚本会创建 `/home/webapp/loan-pricing` 下的 `backend`、`frontend`、`backup`、`logs`、`run`、`tmp` 目录,并写入 Nginx 配置 +- 部署脚本约定发布包内必须包含 1 个后端 `jar` 和 1 个 `dist.zip` +- 部署脚本在发布前会备份旧版后端 jar 与旧版前端 `dist` 目录,再完成替换、启动后端和重载 Nginx +- Nginx 前端监听端口固定为 `63311`,后端应用启动端口固定为 `63310` + +## 环境勘察结论 +- 已连接生产服务器 `116.62.17.81:9444` 并进入 `loan-pricing` 容器核对目录结构 +- 容器内实际工作目录为 `/home/webapp` +- 已确认当前容器中存在安装包: + - `/home/webapp/openjdk-21.0.2_linux-aarch64_bin.tar.gz` + - `/home/webapp/nginx-1.20.2.tar.gz` +- 已确认当前容器尚不存在 `/home/webapp/loan-pricing` +- 已确认当前容器当前没有运行中的 Java 或 Nginx 进程 +- 当前被勘察容器基础镜像为 Ubuntu;但脚本已按需求改为基于 `yum` 安装系统依赖,适配正式生产环境约束 +- 已确认当前容器无法直接安装原生 `yum` 包,但系统仓库提供 `dnf` 包,可通过 `dnf` 提供 `yum` 兼容执行入口 + +## 验证结果 +- 已执行 `sh -n bin/prod/install_env.sh` +- 已执行 `sh -n bin/prod/deploy_release.sh` +- 已将两份脚本同步到生产 `loan-pricing` 容器: + - `/home/webapp/install_env.sh` + - `/home/webapp/deploy_release.sh` +- 已将 Java 管理脚本同步到生产 `loan-pricing` 容器: + - `/home/webapp/restart_java.sh` +- 已在容器内执行 `ls -l /home/webapp/install_env.sh /home/webapp/deploy_release.sh /home/webapp/restart_java.sh`,确认三份脚本均已落盘且具备可执行权限 +- 已在容器内执行: + - `sh -n /home/webapp/install_env.sh` + - `sh -n /home/webapp/deploy_release.sh` + - `sh -n /home/webapp/restart_java.sh` + 三份线上脚本语法校验均已通过 +- 已确认 Ubuntu 24.04 仓库中 `yum` 包候选为空,`dnf` 包候选为 `4.14.0-4.1ubuntu1` +- 已在生产 `loan-pricing` 容器执行 `apt-get install -y dnf dnf-plugins-core` +- 已在生产 `loan-pricing` 容器创建 `yum` 兼容入口: + - `/usr/local/bin/yum -> /usr/bin/dnf` +- 已执行 `yum --version`,返回 `4.14.0` +- 已人工核对脚本中的关键路径、端口与部署约束: + - Java 安装目录 `/home/webapp/env/java` + - Nginx 安装目录 `/home/webapp/env/nginx` + - 项目部署目录 `/home/webapp/loan-pricing` + - 前端端口 `63311` + - 后端端口 `63310` +- 由于当前已连接勘察容器为 Ubuntu 24.04,不具备本次脚本要求的 `yum` 安装前提,因此未在该容器直接执行安装流程,仅完成语法校验与逻辑核对 diff --git a/doc/implementation-report-2026-04-01-backend-start-profile-uat.md b/doc/implementation-report-2026-04-01-backend-start-profile-uat.md new file mode 100644 index 0000000..68d9f0b --- /dev/null +++ b/doc/implementation-report-2026-04-01-backend-start-profile-uat.md @@ -0,0 +1,18 @@ +# 后端启动配置切换为 uat 实施记录 + +## 修改内容 +- 将 `bin/prod/deploy_from_package.sh` 的后端启动 profile 从 `pro` 调整为 `uat` +- 将 `bin/prod/restart_java.sh` 的后端启动 profile 从 `pro` 调整为 `uat` +- 线上宿主机挂载脚本同步改为 `uat`,用于当前容器直接生效 + +## 原因说明 +- 当前 `pro` 配置依赖的数据库地址 `64.127.23.7:3306` 从部署主机与容器内均不可达 +- `uat` 配置依赖的数据库地址 `192.168.0.111:40628` 从部署主机可达,满足当前启动条件 + +## 验证结果 +- 已验证宿主机到 `192.168.0.111:40628` 端口连通 +- 已在仓库脚本中完成 `uat` 切换 +- 已在宿主机挂载脚本 `/volume1/webapp/loan-pricing/deploy_from_package.sh` 与 `/volume1/webapp/restart_java.sh` 同步切换为 `uat` +- 已执行容器内命令 `/home/webapp/restart_java.sh restart` +- 已执行 `curl -I http://116.62.17.81:63310/`,返回 `HTTP/1.1 200` +- 已执行 `curl -X POST http://116.62.17.81:63311/prod-api/login/test ...`,返回 `{"code":200,...}`,确认 Nginx 反代与后端 `uat` 启动正常 diff --git a/doc/implementation-report-2026-04-01-nginx-directory-permissions.md b/doc/implementation-report-2026-04-01-nginx-directory-permissions.md new file mode 100644 index 0000000..8f2e3ae --- /dev/null +++ b/doc/implementation-report-2026-04-01-nginx-directory-permissions.md @@ -0,0 +1,16 @@ +# Nginx 目录权限修正实施记录 + +## 修改内容 +- 修正宿主机挂载目录 `/volume1/webapp` 的遍历权限,允许容器内 Nginx worker 访问业务目录 +- 修正宿主机挂载目录 `/volume1/webapp/loan-pricing`、`/volume1/webapp/loan-pricing/frontend`、`/volume1/webapp/loan-pricing/frontend/dist` 的遍历权限 +- 修正宿主机挂载目录 `/volume1/webapp/env`、`/volume1/webapp/env/nginx`、`/volume1/webapp/env/nginx/html` 的遍历权限 + +## 原因说明 +- `loan-pricing` 容器内 Nginx worker 进程以 `nobody` 运行 +- 宿主机挂载目录此前存在 `d---------` 或缺少其他用户执行权限的情况 +- 结果导致容器内访问 `/home/webapp/loan-pricing/frontend/dist/index.html` 和 `/home/webapp/env/nginx/html/50x.html` 时出现 `Permission denied` + +## 验证结果 +- 已执行 `curl -I http://116.62.17.81:63311/`,返回 `HTTP/1.1 200 OK` +- 已执行 `curl http://116.62.17.81:63311/`,成功返回首页 HTML +- 已确认宿主机相关目录权限已调整为可供 Nginx worker 读取和遍历 diff --git a/doc/implementation-report-2026-04-01-nginx-worker-user.md b/doc/implementation-report-2026-04-01-nginx-worker-user.md new file mode 100644 index 0000000..9a32807 --- /dev/null +++ b/doc/implementation-report-2026-04-01-nginx-worker-user.md @@ -0,0 +1,19 @@ +# Nginx Worker 用户显式配置实施记录 + +## 修改内容 +- 在 `deploy/nginx.conf` 中显式增加 `user nobody;` +- 在 `bin/prod/install_env.sh` 生成的 Nginx 配置模板中显式增加 `user nobody;` +- 计划将线上实际使用的 `/volume1/webapp/env/nginx/conf/nginx.conf` 同步改为显式 `user nobody;` + +## 原因说明 +- 当前线上 Nginx 实际以 `nobody` worker 进程运行 +- 但配置文件未显式声明 worker 用户,后续重写配置时容易与实际运行态不一致 +- 显式声明 `user nobody;` 可以让配置意图与当前运行方式保持一致 + +## 验证结果 +- 已完成仓库配置文件与安装脚本模板修改 +- 已同步修改线上实际配置 `/volume1/webapp/env/nginx/conf/nginx.conf` +- 已执行 `nginx -t -c /home/webapp/env/nginx/conf/nginx.conf`,语法校验通过 +- 已执行 Nginx reload,容器内进程显示 `nobody` 作为 worker 用户运行 +- 已执行 `curl -I http://116.62.17.81:63311/`,返回 `HTTP/1.1 200 OK` +- 已执行 `curl http://116.62.17.81:63311/prod-api/login/test`,返回状态码 `200` diff --git a/doc/上虞对私利率测算_上传字段与展示字段.xlsx b/doc/上虞对私利率测算_上传字段与展示字段.xlsx new file mode 100644 index 0000000..ce3e292 Binary files /dev/null and b/doc/上虞对私利率测算_上传字段与展示字段.xlsx differ diff --git a/ruoyi-admin/src/main/resources/application-pro.yml b/ruoyi-admin/src/main/resources/application-pro.yml index a9ddf47..57e8fc0 100644 --- a/ruoyi-admin/src/main/resources/application-pro.yml +++ b/ruoyi-admin/src/main/resources/application-pro.yml @@ -25,9 +25,9 @@ spring: druid: # 主库数据源 master: - url: jdbc:mysql://64.127.23.6:3306/loan-pricing?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 + url: jdbc:mysql://64.127.23.7:3306/loan-pricing?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 username: lrdb - password: Synx@2024 + password: Synx2024 # 从库数据源 slave: # 从数据源开关/默认关闭 diff --git a/ruoyi-ui/dist.zip b/ruoyi-ui/dist.zip new file mode 100644 index 0000000..23353f6 Binary files /dev/null and b/ruoyi-ui/dist.zip differ diff --git a/sql/loan_pricing_prod_init_20260331.sql b/sql/loan_pricing_prod_init_20260331.sql index 8f4c4bc..3adef4c 100644 --- a/sql/loan_pricing_prod_init_20260331.sql +++ b/sql/loan_pricing_prod_init_20260331.sql @@ -174,7 +174,6 @@ create table sys_menu ( insert into sys_menu values('1', '系统管理', '0', '1', 'system', null, '', '', 1, 0, 'M', '0', '0', '', 'system', 'admin', sysdate(), '', null, '系统管理目录'); insert into sys_menu values('2', '系统监控', '0', '2', 'monitor', null, '', '', 1, 0, 'M', '0', '0', '', 'monitor', 'admin', sysdate(), '', null, '系统监控目录'); insert into sys_menu values('3', '系统工具', '0', '3', 'tool', null, '', '', 1, 0, 'M', '0', '0', '', 'tool', 'admin', sysdate(), '', null, '系统工具目录'); -insert into sys_menu values('4', '若依官网', '0', '4', 'http://ruoyi.vip', null, '', '', 0, 0, 'M', '0', '0', '', 'guide', 'admin', sysdate(), '', null, '若依官网地址'); -- 二级菜单 insert into sys_menu values('100', '用户管理', '1', '1', 'user', 'system/user/index', '', '', 1, 0, 'C', '0', '0', 'system:user:list', 'user', 'admin', sysdate(), '', null, '用户管理菜单'); insert into sys_menu values('101', '角色管理', '1', '2', 'role', 'system/role/index', '', '', 1, 0, 'C', '0', '0', 'system:role:list', 'peoples', 'admin', sysdate(), '', null, '角色管理菜单');