After several months of development, the project is about to enter the delivery phase.
However, the client restricts deployment to a Windows environment, whereas I am more familiar with Linux, and deploying middleware on Windows is quite troublesome.
Therefore, two deployment methods were used to indirectly utilize a Linux system: 1. Executing a one-click deployment script via WSL, and 2. Docker images.
Deployment Strategy
For the client, deployment needs to be as “one-click” as possible.
Therefore, PowerShell versions of the deployment script, service start script, and service stop script were generated using an LLM.
Corresponding Bash versions of deployment scripts can also be generated with the help of an LLM.
Before delivering to the client, write the configuration file explanations and commands into the documentation, and record a demonstration video.
Finally, two deployment methods were provided to the user: a Linux one-click deployment package and a Docker one-click deployment package.
LLM Prompt
Of course, there is no need to manually write the scripts from scratch; an LLM can handle it instead. The prompt is as follows:
# 新增:导入 xxx.sql 到 MySQL 数据库 import_xxx_sql() { local host=$1 local port=$2 local user=$3 local password=$4 local sql_file=$5
# 检查 SQL 文件是否存在 if [ ! -f "$sql_file" ]; then echo"错误:SQL 文件 $sql_file 不存在" exit 1 fi
# 检查数据库是否存在 if mysql -h$host -P$port -u$user -p$password -e "USE xxx" 2>/dev/null; then echo"数据库 xxx 已存在,跳过创建" else echo"创建数据库 xxx..." mysql -h$host -P$port -u$user -p$password -e "CREATE DATABASE xxx;" fi
# 导入 SQL 文件 echo"导入 $sql_file 到 xxx 数据库..." mysql -h$host -P$port -u$user -p$password xxx < "$sql_file" if [ $? -eq 0 ]; then echo"导入成功" else echo"导入失败" exit 1 fi }
# 更新 sys_menu 表 update_sys_menu() { local host=$1 local port=$2 local user=$3 local password=$4 local thingsboard_url=$5
if [ -z "$thingsboard_url" ]; then echo"警告:thingsboard_url 未配置,跳过 sys_menu 更新" return fi
echo"更新 sys_menu 表 (menu_id=2005) 的 path 字段为 $thingsboard_url" mysql -h$host -P$port -u$user -p$password xxx <<EOF UPDATE sys_menu SET path = "$thingsboard_url" WHERE menu_id = 2005; EOF
if [ $? -eq 0 ]; then echo"sys_menu 表更新成功" else echo"警告:sys_menu 表更新失败" fi }
# 获取系统信息 detect_os() { if [ -f /etc/os-release ]; then . /etc/os-release OS_NAME=$ID
# 兼容Alibaba Linux if [[ "$ID" == "alinux" ]]; then OS_NAME="centos" # 根据版本号映射到CentOS版本 if [[ "$VERSION_ID" == "2"* ]]; then OS_VERSION="7" elif [[ "$VERSION_ID" == "3"* ]]; then OS_VERSION="8" fi else OS_VERSION=${VERSION_ID%%.*} fi else echo"无法检测操作系统" exit 1 fi }
# 检查是否已存在相关配置 if grep -q "net.ipv6.conf.all.disable_ipv6" /etc/sysctl.conf; then # 更新现有配置 sudo sed -i 's/^net.ipv6.conf.all.disable_ipv6.*/net.ipv6.conf.all.disable_ipv6 = 1/' /etc/sysctl.conf sudo sed -i 's/^net.ipv6.conf.default.disable_ipv6.*/net.ipv6.conf.default.disable_ipv6 = 1/' /etc/sysctl.conf else # 添加新配置 echo"net.ipv6.conf.all.disable_ipv6 = 1" | sudo tee -a /etc/sysctl.conf echo"net.ipv6.conf.default.disable_ipv6 = 1" | sudo tee -a /etc/sysctl.conf fi
# 立即应用设置 sudo sysctl -p
echo"IPv6已禁用并设置为持续生效" }
# 安装包管理工具 install_package() { local package=$1 case$OS_NAMEin ubuntu) sudo apt install -y $package ;; centos|rhel|alinux) # 统一处理CentOS/RHEL/Alibaba Linux if [ $OS_VERSION -eq 7 ]; then sudo yum install -y $package --nogpgcheck else sudo dnf install -y $package --nogpgcheck fi ;; *) echo"不支持的发行版: $OS_NAME" exit 1 ;; esac }
# 检查服务连接 check_db_connection() { local db_type=$1 local host=$2 local port=$3 local user=$4 local password=$5
case$db_typein mysql) if ! command -v mysql &> /dev/null; then install_package "mysql-client" fi mysql -h$host -P$port -u$user -p$password -e "SELECT 1;" &> /dev/null return $? ;; postgresql) if ! command -v psql &> /dev/null; then install_package "postgresql-client" fi PGPASSWORD="$password" psql -h $host -p $port -U $user -d postgres -c "SELECT 1;" &> /dev/null return $? ;; *) return 1 ;; esac }
# 等待数据库可用 wait_for_db() { local db_type=$1 local host=$2 local port=$3 local user=$4 local password=$5
echo"等待 $db_type 在 $host:$port 可用..." for i in {1..60}; do if check_db_connection "$db_type""$host""$port""$user""$password"; then echo"$db_type 已就绪" return 0 fi sleep 5 echo"等待 $db_type 服务 ($i/60)..." done echo"错误: $db_type 服务启动超时" return 1 }
# 创建 PostgreSQL 数据库 ensure_postgresql_db() { local host=$1 local port=$2 local user=$3 local password=$4 local db_name=$5 local admin_user="postgres" local admin_password=$PG_POSTGRES_PASSWORD
# 使用管理员用户创建数据库 PGPASSWORD="$admin_password" psql -h $host -p $port -U $admin_user <<EOF CREATE DATABASE $db_name; GRANT ALL PRIVILEGES ON DATABASE $db_name TO $user; EOF fi
# 确保用户对public模式有权限 echo"确保 $user 对 $db_name 的 public 模式有权限..." PGPASSWORD="$admin_password" psql -h $host -p $port -U $admin_user -d $db_name <<EOF GRANT ALL ON SCHEMA public TO $user; GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO $user; GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO $user; ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO $user; ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO $user; EOF }
# 自动创建配置用户 sudo mysql -uroot -p$MYSQL_ROOT_PASSWORD <<EOF CREATE USER '$DB_USER'@'%' IDENTIFIED BY '$DB_PASSWORD'; GRANT ALL PRIVILEGES ON *.* TO '$DB_USER'@'%' WITH GRANT OPTION; FLUSH PRIVILEGES; EOF ;;
centos|rhel|alinux) sudo yum module -y disable mysql if [ $OS_VERSION -eq 7 ]; then sudo yum install -y rpm/mysql84-community-release-el7-1.noarch.rpm --nogpgcheck else sudo yum install -y rpm/mysql84-community-release-el8-1.noarch.rpm --nogpgcheck fi
# 自动修改root密码 mysql -uroot -p"$temp_pass" --connect-expired-password <<EOF 2>/dev/null ALTER USER 'root'@'localhost' IDENTIFIED BY '$MYSQL_ROOT_PASSWORD'; FLUSH PRIVILEGES; EOF
# 创建配置用户 mysql -uroot -p$MYSQL_ROOT_PASSWORD <<EOF CREATE USER '$DB_USER'@'%' IDENTIFIED BY '$DB_PASSWORD'; GRANT ALL PRIVILEGES ON *.* TO '$DB_USER'@'%' WITH GRANT OPTION; FLUSH PRIVILEGES; EOF ;; esac }
# 使用单个psql命令避免目录错误 sudo -u postgres psql <<EOF ALTER USER postgres WITH PASSWORD '$PG_POSTGRES_PASSWORD'; CREATE USER $PG_USER WITH PASSWORD '$PG_PASSWORD'; ALTER USER $PG_USER CREATEDB; CREATE DATABASE thingsboard; GRANT ALL PRIVILEGES ON DATABASE thingsboard TO $PG_USER; EOF
# 确保用户对public模式有权限 sudo -u postgres psql -d thingsboard <<EOF GRANT ALL ON SCHEMA public TO $PG_USER; GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO $PG_USER; GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO $PG_USER; ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO $PG_USER; ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO $PG_USER; EOF ;;
sudo /usr/pgsql-16/bin/postgresql-16-setup initdb sudo systemctl enable --now postgresql-16 fi
# 创建用户和数据库 sudo -u postgres psql <<EOF ALTER USER postgres WITH PASSWORD '$PG_POSTGRES_PASSWORD'; CREATE USER $PG_USER WITH PASSWORD '$PG_PASSWORD'; ALTER USER $PG_USER CREATEDB; CREATE DATABASE thingsboard; GRANT ALL PRIVILEGES ON DATABASE thingsboard TO $PG_USER; EOF
# 确保用户对public模式有权限 sudo -u postgres psql -d thingsboard <<EOF GRANT ALL ON SCHEMA public TO $PG_USER; GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO $PG_USER; GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO $PG_USER; ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO $PG_USER; ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUences TO $PG_USER; EOF ;; esac
# 配置密码(如果设置了) if [ -n "$redis_password" ]; then echo"设置Redis密码..." sudo sed -i "s/^# requirepass .*/requirepass $redis_password/""$redis_conf" sudo sed -i "s/^requirepass .*/requirepass $redis_password/""$redis_conf" fi
# 确保正确的安装目录结构 if [ ! -d "/usr/share/thingsboard" ]; then echo"错误:ThingsBoard 未正确安装到 /usr/share/thingsboard" exit 1 fi ;; centos|rhel|alinux) sudo yum install -y rpm/thingsboard-4.0.1.rpm --nogpgcheck
# 确保正确的安装目录结构 if [ ! -d "/usr/share/thingsboard" ]; then echo"错误:ThingsBoard 未正确安装到 /usr/share/thingsboard" exit 1 fi ;; esac
}
# 配置ThingsBoard数据库连接 configure_thingsboard() { local config_file="/etc/thingsboard/conf/thingsboard.conf" local pg_user=$(get_config 'postgresql_user') local pg_password=$(get_config 'postgresql_password')
# 检查配置文件是否存在 if [ ! -f "$config_file" ]; then echo"警告:ThingsBoard配置文件 $config_file 不存在" return fi
# 检查配置是否已存在 if grep -q "SPRING_DATASOURCE_URL""$config_file"; then echo"数据库配置已存在,跳过配置" return fi
# 添加数据库配置 echo"# DB Configuration" | sudo tee -a "$config_file" > /dev/null echo"export DATABASE_TS_TYPE=sql" | sudo tee -a "$config_file" > /dev/null echo"export SPRING_DATASOURCE_URL=jdbc:postgresql://${PG_HOST}:${PG_PORT}/thingsboard" | sudo tee -a "$config_file" > /dev/null echo"export SPRING_DATASOURCE_USERNAME=$pg_user" | sudo tee -a "$config_file" > /dev/null echo"export SPRING_DATASOURCE_PASSWORD=$pg_password" | sudo tee -a "$config_file" > /dev/null
if [ -z "$mem_total" ]; then echo"错误:无法检测系统内存" return fi
if [ "$mem_total" -le 4096 ]; then echo"系统内存为 ${mem_total}MB <= 4GB,配置ThingsBoard使用2048MB内存"
# 检查是否已配置 if ! grep -q "Xms2048M""$config_file"; then echo"# Update ThingsBoard memory usage for low-memory systems" | sudo tee -a "$config_file" echo"export JAVA_OPTS=\"\$JAVA_OPTS -Xms2048M -Xmx2048M\"" | sudo tee -a "$config_file" else echo"内存配置已存在,跳过" fi else echo"系统内存为 ${mem_total}MB > 4GB,使用默认内存配置" fi }
# 初始化ThingsBoard数据库 init_thingsboard() { echo"初始化ThingsBoard数据库..." local pg_user=$(get_config 'postgresql_user') local pg_password=$(get_config 'postgresql_password') local pg_host=$(get_config 'postgresql_host') local pg_port=$(get_config 'postgresql_port')
# 确保psql命令可用 if ! command -v psql &> /dev/null; then echo"安装PostgreSQL客户端..." if [ "$OS_NAME" = "ubuntu" ]; then install_package "postgresql-client" else install_package "postgresql" fi fi
# 创建索引并清理数据 PGPASSWORD="$pg_password" psql \ -h "$pg_host" \ -p "$pg_port" \ -U "$pg_user" \ -d thingsboard \ -c " CREATE INDEX IF NOT EXISTS idx_device_label ON device (tenant_id, label); DELETE FROM device; DELETE FROM device_credentials; DELETE FROM customer; "
if [ $? -eq 0 ]; then echo"数据库初始化成功" else echo"警告:数据库初始化失败" fi }
# 备份主配置文件 local nginx_conf="/etc/nginx/nginx.conf" if [ -f "$nginx_conf" ]; then sudo mv "$nginx_conf""${nginx_conf}_bak" echo"已备份 $nginx_conf 为 ${nginx_conf}_bak" fi
# 备份默认站点配置 local default_conf="" case$OS_NAMEin ubuntu) default_conf="/etc/nginx/sites-enabled/default" ;; centos|rhel|alinux) default_conf="/etc/nginx/conf.d/default.conf" if [ ! -f "$default_conf" ]; then default_conf="/etc/nginx/nginx.conf.default" fi ;; esac
if [ -f "$default_conf" ]; then sudo mv "$default_conf""${default_conf}_bak" echo"已备份 $default_conf 为 ${default_conf}_bak" fi
# 获取前端基础目录 local basedir=$(get_config 'frontend_basedir') if [ -z "$basedir" ]; then echo"警告:前端基础目录未配置,跳过Nginx配置" return fi
# 创建基础目录 sudo mkdir -p "$basedir"
# 复制前端资源并替换URL local iot_gateway_url=$(get_config 'frontend_iot_gateway_url') local xxx_url=$(get_config 'frontend_xxx_url')
# 复制iot_blockly_frontend local src_dir1="$SCRIPT_DIR/iot_gateway/iot_blockly_frontend" local dest_dir1="$basedir/iot_blockly_frontend" if [ -d "$src_dir1" ]; then sudo cp -r "$src_dir1""$basedir/" # 替换URL sudo find "$dest_dir1" -type f -exec sed -i "s|IOT_GATEWAY_URL|${iot_gateway_url}|g" {} \; else echo"警告:前端资源目录 $src_dir1 不存在" fi
# 复制iot_xxx_frontend local src_dir2="$SCRIPT_DIR/ruoyi-admin/iot_xxx_frontend" local dest_dir2="$basedir/iot_xxx_frontend" if [ -d "$src_dir2" ]; then sudo cp -r "$src_dir2""$basedir/" # 替换URL sudo find "$dest_dir2" -type f -exec sed -i "s|xxx_URL|${xxx_url}|g" {} \; else echo"警告:前端资源目录 $src_dir2 不存在" fi
# 创建Nginx配置文件目录 sudo mkdir -p /etc/nginx/conf.d
# 确定Nginx运行时使用的用户 local nginx_user if [ "$OS_NAME" = "ubuntu" ]; then nginx_user="www-data"# Ubuntu默认使用www-data else nginx_user="nginx"# CentOS使用nginx fi
# 创建主配置文件(总是覆盖) echo"创建新的Nginx主配置..." sudo tee /etc/nginx/nginx.conf > /dev/null <<EOF user $nginx_user; # 动态设置用户 worker_processes auto; error_log /var/log/nginx/error.log; pid /run/nginx.pid;
# Load dynamic modules. See /usr/share/doc/nginx/README.dynamic. include /usr/share/nginx/modules/*.conf;
include /etc/nginx/mime.types; default_type application/octet-stream;
# Load modular configuration files from the /etc/nginx/conf.d directory. include /etc/nginx/conf.d/*.conf; } EOF
# 创建Nginx配置文件 local tb_conf="/etc/nginx/conf.d/tb.conf" local xxx_conf="/etc/nginx/conf.d/xxx.conf"
# 获取配置参数 local thingsboard_port=$(get_config 'port_thingsboard_port') local iot_gateway_port=$(get_config 'port_iot_gateway_port') local xxx_backend_port=$(get_config 'port_xxx_backend_port') local tb_ssl_cert=$(get_config 'nginx_thingsboard_ssl_certificate') local tb_ssl_key=$(get_config 'nginx_thingsboard_ssl_certificate_key') local xxx_ssl_cert=$(get_config 'nginx_xxx_ssl_certificate') local xxx_ssl_key=$(get_config 'nginx_xxx_ssl_certificate_key')
# 安装 Java if [ "$(get_config 'common_install_java')" = "yes" ]; then # 先检查 java 命令是否存在 if ! command -v java &> /dev/null; then echo"Java 未安装,开始安装..." install_java else # 如果 java 命令存在,再检查版本 if ! java -version 2>&1 | grep -q "17"; then echo"已安装的 Java 版本不是 17,开始安装 JDK 17..." install_java else echo"JDK 17 已安装" fi fi
# 验证安装 ifcommand -v java &> /dev/null; then echo"Java 安装验证:" java -version else echo"错误:Java 安装后仍然不可用" exit 1 fi fi
# 处理 MySQL if [ "$(get_config 'mysql_install')" = "yes" ]; then host=$(get_config 'mysql_host') port=$(get_config 'mysql_port') user=$(get_config 'mysql_user') password=$(get_config 'mysql_password')
if check_db_connection "mysql"$host$port$user$password; then echo"MySQL 连接成功,跳过安装" else echo"MySQL 连接失败,开始安装..." install_mysql echo"MySQL 已自动配置用户:$DB_USER" fi
# 新增:导入 xxx.sql 到 MySQL echo"处理 xxx 数据库导入..." import_xxx_sql $host$port$user$password"$SQL_FILE"
# 新增:更新 sys_menu 表 thingsboard_url=$(get_config 'frontend_thingsboard_url') update_sys_menu $host$port$user$password"$thingsboard_url" fi
# 安装 Nginx if [ "$(get_config 'common_install_nginx')" = "yes" ]; then if ! command -v nginx &> /dev/null; then install_nginx nginx -v else echo"Nginx 已安装" fi