前端打包
This commit is contained in:
@@ -17,15 +17,16 @@ RUN mkdir -p /docker-entrypoint-initdb.d /opt/sql
|
||||
# 复制所有SQL文件(保持目录结构)
|
||||
COPY schoolNewsServ/.bin/mysql/sql/ /opt/sql/
|
||||
|
||||
# 复制并调整reInit.sh为Docker环境
|
||||
# 复制并调整reInit.sh为Docker环境,设置执行权限
|
||||
COPY schoolNewsServ/.bin/mysql/sql/reInit.sh /opt/sql/
|
||||
RUN sed -i 's/DB_HOST="localhost"/DB_HOST="localhost"/' /opt/sql/reInit.sh && \
|
||||
sed -i 's/DB_PORT="3306"/DB_PORT="3306"/' /opt/sql/reInit.sh && \
|
||||
sed -i 's/DB_USER="root"/DB_USER="root"/' /opt/sql/reInit.sh && \
|
||||
sed -i 's/DB_PASSWORD="123456"/DB_PASSWORD="${MYSQL_ROOT_PASSWORD}"/' /opt/sql/reInit.sh && \
|
||||
sed -i 's/DB_NAME="school_news"/DB_NAME="${MYSQL_DATABASE}"/' /opt/sql/reInit.sh && \
|
||||
chmod +x /opt/sql/reInit.sh
|
||||
|
||||
sed -i 's|LOG_FILE="$SCRIPT_DIR/reInit.log"|LOG_FILE="/tmp/reInit.log"|' /opt/sql/reInit.sh && \
|
||||
chmod +x /opt/sql/reInit.sh && \
|
||||
chmod +x /opt/sql/sensitiveData/importSensitiveWords.sh
|
||||
# 创建Docker初始化适配脚本
|
||||
RUN cat > /docker-entrypoint-initdb.d/01-init-database.sh <<'EOF'
|
||||
#!/bin/bash
|
||||
@@ -54,10 +55,8 @@ export DB_PASSWORD="${MYSQL_ROOT_PASSWORD}"
|
||||
export DB_NAME="${MYSQL_DATABASE}"
|
||||
export MYSQL_PWD="${MYSQL_ROOT_PASSWORD}"
|
||||
|
||||
# 直接调用reInit.sh的初始化函数
|
||||
echo "执行数据库初始化..."
|
||||
|
||||
# Source reInit.sh并调用其初始化函数
|
||||
echo "执行数据库初始化(使用reInit.sh)..."
|
||||
source reInit.sh
|
||||
|
||||
# 调用reInit.sh的核心函数(跳过备份和删除)
|
||||
@@ -76,11 +75,9 @@ UPDATE tb_sys_config
|
||||
SET config_value = '/app/crawler'
|
||||
WHERE config_key = 'crawler.basePath';
|
||||
|
||||
-- 如果配置不存在则插入
|
||||
INSERT IGNORE INTO tb_sys_config (config_key, config_value, config_desc, created_at)
|
||||
VALUES
|
||||
('crawler.pythonPath', '/usr/bin/python3', 'Docker容器内Python路径', NOW()),
|
||||
('crawler.basePath', '/app/crawler', 'Docker容器内爬虫脚本路径', NOW());
|
||||
UPDATE _db_init_status
|
||||
SET status = 'success'
|
||||
WHERE script_name = '01-init-database.sql';
|
||||
|
||||
SELECT '✅ 数据库初始化完成!' AS message;
|
||||
SELECT '默认用户: admin, 密码: admin123' AS tip;
|
||||
|
||||
@@ -1,12 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
校园新闻管理系统 - Docker环境日志配置
|
||||
校园新闻管理系统 - Admin模块日志配置
|
||||
-->
|
||||
<configuration status="WARN" monitorInterval="30">
|
||||
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
|
||||
|
||||
<!--变量配置-->
|
||||
<Properties>
|
||||
<!-- 格式化输出:%date表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度 %msg:日志消息,%n是换行符-->
|
||||
<!-- %logger{36} 表示 Logger 名字最长36个字符 -->
|
||||
<property name="LOG_PATTERN" value="%date{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n" />
|
||||
<property name="FILE_PATH" value="/app/logs" />
|
||||
<!-- 定义日志存储的路径 -->
|
||||
<property name="FILE_PATH" value="./logs" />
|
||||
<!-- Admin模块日志文件名 -->
|
||||
<property name="FILE_NAME" value="school-news-admin" />
|
||||
<!-- 设置系统属性 -->
|
||||
<property name="file.encoding" value="UTF-8" />
|
||||
<property name="console.encoding" value="UTF-8" />
|
||||
<property name="stdout.encoding" value="UTF-8" />
|
||||
@@ -14,60 +22,171 @@
|
||||
</Properties>
|
||||
|
||||
<appenders>
|
||||
|
||||
<console name="Console" target="SYSTEM_OUT">
|
||||
<!--输出日志的格式-->
|
||||
<PatternLayout pattern="${LOG_PATTERN}" charset="UTF-8"/>
|
||||
<ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
|
||||
<!--控制台输出debug及以上级别的信息-->
|
||||
<ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY"/>
|
||||
</console>
|
||||
|
||||
<!--文件会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,适合临时测试用-->
|
||||
<File name="Filelog" fileName="${FILE_PATH}/${FILE_NAME}-test.log" append="false">
|
||||
<PatternLayout pattern="${LOG_PATTERN}" charset="UTF-8"/>
|
||||
</File>
|
||||
|
||||
<!-- 这个会打印出所有的info及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档-->
|
||||
<RollingFile name="RollingFileInfo" fileName="${FILE_PATH}/${FILE_NAME}-info.log" filePattern="${FILE_PATH}/${FILE_NAME}-INFO-%d{yyyy-MM-dd}_%i.log.gz">
|
||||
<!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
|
||||
<ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
|
||||
<PatternLayout pattern="${LOG_PATTERN}" charset="UTF-8"/>
|
||||
<Policies>
|
||||
<!--interval属性用来指定多久滚动一次,默认是1 hour-->
|
||||
<TimeBasedTriggeringPolicy interval="1"/>
|
||||
<SizeBasedTriggeringPolicy size="10MB"/>
|
||||
</Policies>
|
||||
<!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖-->
|
||||
<DefaultRolloverStrategy max="15"/>
|
||||
</RollingFile>
|
||||
|
||||
<!-- 这个会打印出所有的warn及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档-->
|
||||
<RollingFile name="RollingFileWarn" fileName="${FILE_PATH}/${FILE_NAME}-warn.log" filePattern="${FILE_PATH}/${FILE_NAME}-WARN-%d{yyyy-MM-dd}_%i.log.gz">
|
||||
<!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
|
||||
<ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/>
|
||||
<PatternLayout pattern="${LOG_PATTERN}" charset="UTF-8"/>
|
||||
<Policies>
|
||||
<!--interval属性用来指定多久滚动一次,默认是1 hour-->
|
||||
<TimeBasedTriggeringPolicy interval="1"/>
|
||||
<SizeBasedTriggeringPolicy size="10MB"/>
|
||||
</Policies>
|
||||
<!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖-->
|
||||
<DefaultRolloverStrategy max="15"/>
|
||||
</RollingFile>
|
||||
|
||||
<!-- 这个会打印出所有的error及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档-->
|
||||
<RollingFile name="RollingFileError" fileName="${FILE_PATH}/${FILE_NAME}-error.log" filePattern="${FILE_PATH}/${FILE_NAME}-ERROR-%d{yyyy-MM-dd}_%i.log.gz">
|
||||
<!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
|
||||
<ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>
|
||||
<PatternLayout pattern="${LOG_PATTERN}" charset="UTF-8"/>
|
||||
<Policies>
|
||||
<!--interval属性用来指定多久滚动一次,默认是1 hour-->
|
||||
<TimeBasedTriggeringPolicy interval="1"/>
|
||||
<SizeBasedTriggeringPolicy size="10MB"/>
|
||||
</Policies>
|
||||
<!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖-->
|
||||
<DefaultRolloverStrategy max="15"/>
|
||||
</RollingFile>
|
||||
|
||||
<!-- 数据库日志Appender - 异步写入DEBUG级别及以上的日志到数据库 -->
|
||||
<DatabaseAppender name="DatabaseAppender" ignoreExceptions="false">
|
||||
<ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
|
||||
<ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY"/>
|
||||
</DatabaseAppender>
|
||||
|
||||
</appenders>
|
||||
|
||||
<!--Logger节点用来单独指定日志的形式,比如要为指定包下的class指定不同的日志级别等。-->
|
||||
<!--然后定义loggers,只有定义了logger并引入的appender,appender才会生效-->
|
||||
<loggers>
|
||||
<logger name="org.mybatis" level="info" additivity="false">
|
||||
|
||||
<!--过滤掉spring的一些无用的DEBUG信息-->
|
||||
<logger name="org.mybatis" level="debug" additivity="false">
|
||||
<AppenderRef ref="Console"/>
|
||||
</logger>
|
||||
|
||||
<!--监控系统信息-->
|
||||
<!--若是additivity设为false,则 子Logger 只会在自己的appender里输出,不会在 父Logger 的appender里输出。-->
|
||||
<Logger name="org.springframework" level="info" additivity="false">
|
||||
<AppenderRef ref="Console"/>
|
||||
</Logger>
|
||||
|
||||
<Logger name="org.xyzh" level="info" additivity="false">
|
||||
<!-- MyBatis Mapper 日志配置 - 打印SQL -->
|
||||
<Logger name="org.xyzh.achievement.mapper" level="debug" additivity="false">
|
||||
<AppenderRef ref="Console"/>
|
||||
</Logger>
|
||||
<Logger name="org.xyzh.ai.mapper" level="debug" additivity="false">
|
||||
<AppenderRef ref="Console"/>
|
||||
</Logger>
|
||||
<Logger name="org.xyzh.system.mapper" level="debug" additivity="false">
|
||||
<AppenderRef ref="Console"/>
|
||||
</Logger>
|
||||
<Logger name="org.xyzh.news.mapper" level="debug" additivity="false">
|
||||
<AppenderRef ref="Console"/>
|
||||
</Logger>
|
||||
<Logger name="org.xyzh.study.mapper" level="debug" additivity="false">
|
||||
<AppenderRef ref="Console"/>
|
||||
</Logger>
|
||||
<Logger name="org.xyzh.crontab.mapper" level="debug" additivity="false">
|
||||
<AppenderRef ref="Console"/>
|
||||
</Logger>
|
||||
<Logger name="org.xyzh.message.mapper" level="debug" additivity="false">
|
||||
<AppenderRef ref="Console"/>
|
||||
</Logger>
|
||||
<!-- 项目包日志配置 - Auth模块 -->
|
||||
<Logger name="org.xyzh.auth" level="debug" additivity="false">
|
||||
<AppenderRef ref="Console"/>
|
||||
<AppenderRef ref="Filelog"/>
|
||||
<AppenderRef ref="RollingFileInfo"/>
|
||||
<AppenderRef ref="RollingFileWarn"/>
|
||||
<AppenderRef ref="RollingFileError"/>
|
||||
<AppenderRef ref="DatabaseAppender"/>
|
||||
</Logger>
|
||||
|
||||
<!-- 项目包日志配置 - System模块 -->
|
||||
<Logger name="org.xyzh.system" level="debug" additivity="false">
|
||||
<AppenderRef ref="Console"/>
|
||||
<AppenderRef ref="Filelog"/>
|
||||
<AppenderRef ref="RollingFileInfo"/>
|
||||
<AppenderRef ref="RollingFileWarn"/>
|
||||
<AppenderRef ref="RollingFileError"/>
|
||||
<AppenderRef ref="DatabaseAppender"/>
|
||||
</Logger>
|
||||
|
||||
<!-- 项目包日志配置 - News模块 -->
|
||||
<Logger name="org.xyzh.news" level="debug" additivity="false">
|
||||
<AppenderRef ref="Console"/>
|
||||
<AppenderRef ref="Filelog"/>
|
||||
<AppenderRef ref="RollingFileInfo"/>
|
||||
<AppenderRef ref="RollingFileWarn"/>
|
||||
<AppenderRef ref="RollingFileError"/>
|
||||
<AppenderRef ref="DatabaseAppender"/>
|
||||
</Logger>
|
||||
|
||||
<!-- 项目包日志配置 - Common模块 -->
|
||||
<Logger name="org.xyzh.common" level="debug" additivity="false">
|
||||
<AppenderRef ref="Console"/>
|
||||
<AppenderRef ref="Filelog"/>
|
||||
<AppenderRef ref="RollingFileInfo"/>
|
||||
<AppenderRef ref="RollingFileWarn"/>
|
||||
<AppenderRef ref="RollingFileError"/>
|
||||
<AppenderRef ref="DatabaseAppender"/>
|
||||
</Logger>
|
||||
|
||||
<!-- 项目包日志配置 - Achievement模块 -->
|
||||
<Logger name="org.xyzh.achievement" level="debug" additivity="false">
|
||||
<AppenderRef ref="Console"/>
|
||||
<AppenderRef ref="Filelog"/>
|
||||
<AppenderRef ref="RollingFileInfo"/>
|
||||
<AppenderRef ref="RollingFileWarn"/>
|
||||
<AppenderRef ref="RollingFileError"/>
|
||||
<AppenderRef ref="DatabaseAppender"/>
|
||||
</Logger>
|
||||
|
||||
<Logger name="org.xyzh.crontab" level="debug" additivity="false">
|
||||
<AppenderRef ref="Console"/>
|
||||
<AppenderRef ref="Filelog"/>
|
||||
<AppenderRef ref="RollingFileInfo"/>
|
||||
<AppenderRef ref="RollingFileWarn"/>
|
||||
<AppenderRef ref="RollingFileError"/>
|
||||
<AppenderRef ref="DatabaseAppender"/>
|
||||
</Logger>
|
||||
<Logger name="org.xyzh.message" level="debug" additivity="false">
|
||||
<AppenderRef ref="Console"/>
|
||||
<AppenderRef ref="Filelog"/>
|
||||
<AppenderRef ref="RollingFileInfo"/>
|
||||
<AppenderRef ref="RollingFileWarn"/>
|
||||
<AppenderRef ref="RollingFileError"/>
|
||||
<AppenderRef ref="DatabaseAppender"/>
|
||||
</Logger>
|
||||
<Logger name="org.xyzh.sensitive" level="debug" additivity="false">
|
||||
<AppenderRef ref="Console"/>
|
||||
<AppenderRef ref="Filelog"/>
|
||||
<AppenderRef ref="RollingFileInfo"/>
|
||||
@@ -85,4 +204,5 @@
|
||||
<appender-ref ref="DatabaseAppender"/>
|
||||
</root>
|
||||
</loggers>
|
||||
|
||||
</configuration>
|
||||
|
||||
@@ -13,7 +13,7 @@ services:
|
||||
MYSQL_PASSWORD: ${MYSQL_PASSWORD:-123456}
|
||||
TZ: Asia/Shanghai
|
||||
ports:
|
||||
- "${MYSQL_PORT:-3306}:3306"
|
||||
- "${MYSQL_PORT:-3307}:3306"
|
||||
volumes:
|
||||
# 数据持久化(命名卷)
|
||||
- mysql-data:/var/lib/mysql
|
||||
@@ -26,14 +26,16 @@ services:
|
||||
- --default-authentication-plugin=mysql_native_password
|
||||
- --max_connections=1000
|
||||
- --max_allowed_packet=64M
|
||||
- --local-infile=1
|
||||
networks:
|
||||
- school-news-network
|
||||
healthcheck:
|
||||
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-p${MYSQL_ROOT_PASSWORD:-123456}"]
|
||||
# 只有当 MySQL 可访问且敏感词表中至少有一条 deny 记录时,才认为 healthy
|
||||
test: ["CMD-SHELL", "mysql -uroot -p${MYSQL_ROOT_PASSWORD:-123456} -D ${MYSQL_DATABASE:-school_news} -e \"SELECT 'ok' FROM tb_sensitive_word WHERE type='deny' LIMIT 1;\" 2>/dev/null | grep -q ok"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 30s
|
||||
timeout: 10s
|
||||
retries: 10
|
||||
start_period: 60s
|
||||
|
||||
# Redis缓存
|
||||
redis:
|
||||
@@ -143,8 +145,10 @@ services:
|
||||
ports:
|
||||
- "${NGINX_PORT:-80}:80"
|
||||
volumes:
|
||||
# Nginx配置文件(命名卷)
|
||||
- nginx-config:/etc/nginx:ro
|
||||
# 仅挂载自定义 Nginx 主配置文件
|
||||
- ./volumes/nginx/config/nginx.conf:/etc/nginx/nginx.conf
|
||||
# 仅挂载站点配置目录(conf.d),保留镜像内的 mime.types 等其他文件
|
||||
- nginx-conf-d:/etc/nginx/conf.d:ro
|
||||
# 日志目录(命名卷)
|
||||
- nginx-logs:/var/log/nginx
|
||||
networks:
|
||||
@@ -233,12 +237,19 @@ volumes:
|
||||
device: ./volumes/web/logs
|
||||
|
||||
# ===== Nginx =====
|
||||
nginx-config:
|
||||
nginx-conf-file:
|
||||
driver: local
|
||||
driver_opts:
|
||||
type: none
|
||||
o: bind
|
||||
device: ./volumes/nginx/config
|
||||
device: ./volumes/nginx/config/nginx.conf
|
||||
|
||||
nginx-conf-d:
|
||||
driver: local
|
||||
driver_opts:
|
||||
type: none
|
||||
o: bind
|
||||
device: ./volumes/nginx/config/conf.d
|
||||
|
||||
nginx-logs:
|
||||
driver: local
|
||||
|
||||
@@ -19,7 +19,7 @@ CREATE TABLE IF NOT EXISTS _db_init_status (
|
||||
id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
script_name VARCHAR(255) NOT NULL UNIQUE,
|
||||
executed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
status VARCHAR(50) DEFAULT 'success'
|
||||
status VARCHAR(50) DEFAULT 'init'
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- 记录初始化标记
|
||||
|
||||
@@ -1,231 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
##############################################
|
||||
# 数据库初始化SQL准备脚本
|
||||
# 功能:将SQL文件复制到Docker初始化目录
|
||||
# 基于:schoolNewsServ/.bin/mysql/sql/reInit.sh
|
||||
##############################################
|
||||
|
||||
set -e
|
||||
|
||||
# 颜色定义
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() {
|
||||
echo -e "${GREEN}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# 路径定义
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
SQL_SOURCE_DIR="../../schoolNewsServ/.bin/mysql/sql"
|
||||
INIT_DB_DIR="$SCRIPT_DIR"
|
||||
|
||||
echo "========================================"
|
||||
echo "数据库初始化SQL准备"
|
||||
echo "========================================"
|
||||
log_info "源SQL目录: $SQL_SOURCE_DIR"
|
||||
log_info "目标目录: $INIT_DB_DIR"
|
||||
echo "========================================"
|
||||
echo ""
|
||||
|
||||
# 检查源目录
|
||||
if [ ! -d "$SQL_SOURCE_DIR" ]; then
|
||||
log_error "源SQL目录不存在: $SQL_SOURCE_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 备份现有SQL文件
|
||||
if ls "$INIT_DB_DIR"/*.sql 1> /dev/null 2>&1; then
|
||||
log_warn "发现现有SQL文件,正在备份..."
|
||||
BACKUP_DIR="$INIT_DB_DIR/backup_$(date +%Y%m%d_%H%M%S)"
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
mv "$INIT_DB_DIR"/*.sql "$BACKUP_DIR/" 2>/dev/null || true
|
||||
log_info "已备份到: $BACKUP_DIR"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# 创建02-create-tables.sql
|
||||
log_info "创建表结构脚本: 02-create-tables.sql"
|
||||
cat > "$INIT_DB_DIR/02-create-tables.sql" << 'EOF'
|
||||
-- ========================================
|
||||
-- 校园新闻管理系统 - 表结构创建脚本
|
||||
-- 自动生成时间: $(date '+%Y-%m-%d %H:%M:%S')
|
||||
-- 基于: schoolNewsServ/.bin/mysql/sql/
|
||||
-- ========================================
|
||||
|
||||
USE school_news;
|
||||
|
||||
-- 检查是否已执行过此脚本
|
||||
SET @executed = (SELECT COUNT(*) FROM _db_init_status WHERE script_name = '02-create-tables.sql');
|
||||
|
||||
-- 如果已执行过,记录并退出
|
||||
SELECT CASE
|
||||
WHEN @executed > 0 THEN '表结构已存在,跳过创建'
|
||||
ELSE '开始创建表结构...'
|
||||
END AS message;
|
||||
|
||||
EOF
|
||||
|
||||
# 合并所有createTable*.sql文件
|
||||
log_info "合并表结构文件..."
|
||||
TABLE_FILES=(
|
||||
"createTableUser.sql"
|
||||
"createTablePermission.sql"
|
||||
"createTablePermissionControl.sql"
|
||||
"createTableResource.sql"
|
||||
"createTableCourse.sql"
|
||||
"createTableLearning.sql"
|
||||
"createTableUserCenter.sql"
|
||||
"createTableAI.sql"
|
||||
"createTableSystem.sql"
|
||||
"createTableAchievement.sql"
|
||||
"createTableCrontab.sql"
|
||||
"createTableMessage.sql"
|
||||
"createTableSensitive.sql"
|
||||
)
|
||||
|
||||
for file in "${TABLE_FILES[@]}"; do
|
||||
if [ -f "$SQL_SOURCE_DIR/$file" ]; then
|
||||
echo "" >> "$INIT_DB_DIR/02-create-tables.sql"
|
||||
echo "-- ========================================" >> "$INIT_DB_DIR/02-create-tables.sql"
|
||||
echo "-- $file" >> "$INIT_DB_DIR/02-create-tables.sql"
|
||||
echo "-- ========================================" >> "$INIT_DB_DIR/02-create-tables.sql"
|
||||
cat "$SQL_SOURCE_DIR/$file" >> "$INIT_DB_DIR/02-create-tables.sql"
|
||||
log_info " ✓ $file"
|
||||
else
|
||||
log_warn " ✗ $file (文件不存在)"
|
||||
fi
|
||||
done
|
||||
|
||||
# 添加执行状态记录
|
||||
cat >> "$INIT_DB_DIR/02-create-tables.sql" << 'EOF'
|
||||
|
||||
-- 记录执行状态
|
||||
INSERT IGNORE INTO _db_init_status (script_name)
|
||||
VALUES ('02-create-tables.sql');
|
||||
|
||||
SELECT '表结构创建完成' AS message;
|
||||
EOF
|
||||
|
||||
echo ""
|
||||
|
||||
# 创建03-init-data.sql
|
||||
log_info "创建初始数据脚本: 03-init-data.sql"
|
||||
cat > "$INIT_DB_DIR/03-init-data.sql" << 'EOF'
|
||||
-- ========================================
|
||||
-- 校园新闻管理系统 - 初始数据导入脚本
|
||||
-- 自动生成时间: $(date '+%Y-%m-%d %H:%M:%S')
|
||||
-- 基于: schoolNewsServ/.bin/mysql/sql/
|
||||
-- ========================================
|
||||
|
||||
USE school_news;
|
||||
|
||||
-- 检查是否已执行过此脚本
|
||||
SET @executed = (SELECT COUNT(*) FROM _db_init_status WHERE script_name = '03-init-data.sql');
|
||||
|
||||
SELECT CASE
|
||||
WHEN @executed > 0 THEN '初始数据已存在,跳过导入'
|
||||
ELSE '开始导入初始数据...'
|
||||
END AS message;
|
||||
|
||||
EOF
|
||||
|
||||
# 合并初始数据文件
|
||||
log_info "合并初始数据文件..."
|
||||
DATA_FILES=(
|
||||
"initMenuData.sql"
|
||||
"initAllData.sql"
|
||||
"initCrontabMetaData.sql"
|
||||
)
|
||||
|
||||
for file in "${DATA_FILES[@]}"; do
|
||||
if [ -f "$SQL_SOURCE_DIR/$file" ]; then
|
||||
echo "" >> "$INIT_DB_DIR/03-init-data.sql"
|
||||
echo "-- ========================================" >> "$INIT_DB_DIR/03-init-data.sql"
|
||||
echo "-- $file" >> "$INIT_DB_DIR/03-init-data.sql"
|
||||
echo "-- ========================================" >> "$INIT_DB_DIR/03-init-data.sql"
|
||||
cat "$SQL_SOURCE_DIR/$file" >> "$INIT_DB_DIR/03-init-data.sql"
|
||||
log_info " ✓ $file"
|
||||
else
|
||||
log_warn " ✗ $file (文件不存在)"
|
||||
fi
|
||||
done
|
||||
|
||||
# 添加爬虫配置初始化
|
||||
cat >> "$INIT_DB_DIR/03-init-data.sql" << 'EOF'
|
||||
|
||||
-- ========================================
|
||||
-- Docker环境爬虫配置
|
||||
-- ========================================
|
||||
INSERT IGNORE INTO tb_sys_config (config_key, config_value, config_desc, created_at)
|
||||
VALUES
|
||||
('crawler.pythonPath', '/usr/bin/python3', 'Docker容器内Python路径', NOW()),
|
||||
('crawler.basePath', '/app/crawler', 'Docker容器内爬虫脚本路径', NOW());
|
||||
|
||||
-- 记录执行状态
|
||||
INSERT IGNORE INTO _db_init_status (script_name)
|
||||
VALUES ('03-init-data.sql');
|
||||
|
||||
SELECT '初始数据导入完成' AS message;
|
||||
SELECT '默认用户: admin, 密码: admin123' AS tip;
|
||||
EOF
|
||||
|
||||
echo ""
|
||||
|
||||
# 生成统计信息
|
||||
log_info "生成文件统计..."
|
||||
cat > "$INIT_DB_DIR/00-init-summary.txt" << EOF
|
||||
========================================
|
||||
数据库初始化脚本准备完成
|
||||
========================================
|
||||
生成时间: $(date '+%Y-%m-%d %H:%M:%S')
|
||||
源目录: $SQL_SOURCE_DIR
|
||||
|
||||
生成的文件:
|
||||
01-init-database.sql - 数据库创建(已存在)
|
||||
02-create-tables.sql - 表结构创建
|
||||
03-init-data.sql - 初始数据导入
|
||||
|
||||
执行顺序:
|
||||
1. 创建数据库和初始化标记表
|
||||
2. 创建所有业务表
|
||||
3. 导入初始数据(用户、菜单、系统配置等)
|
||||
|
||||
默认账户:
|
||||
用户名: admin
|
||||
密码: admin123
|
||||
角色: 管理员
|
||||
|
||||
注意事项:
|
||||
1. 脚本具有幂等性,可以重复执行
|
||||
2. 使用 _db_init_status 表跟踪执行状态
|
||||
3. Docker容器启动时自动执行这些脚本
|
||||
4. 如需重新初始化,删除数据卷: docker-compose down -v
|
||||
|
||||
========================================
|
||||
EOF
|
||||
|
||||
echo ""
|
||||
log_info "========================================"
|
||||
log_info "准备完成!"
|
||||
log_info "========================================"
|
||||
log_info "生成的文件:"
|
||||
ls -lh "$INIT_DB_DIR"/*.sql 2>/dev/null | awk '{print " " $9 " (" $5 ")"}'
|
||||
echo ""
|
||||
log_info "下一步:"
|
||||
echo " 1. 检查生成的SQL文件"
|
||||
echo " 2. 运行Docker构建: ./build.sh"
|
||||
echo " 3. 启动服务: cd docker && docker-compose up -d"
|
||||
log_info "========================================"
|
||||
@@ -32,6 +32,7 @@ mkdir -p "${VOLUMES_DIR}/nginx/logs"
|
||||
# 复制配置文件模板
|
||||
echo "复制配置文件模板..."
|
||||
[ -f redis/redis.conf ] && cp redis/redis.conf "${VOLUMES_DIR}/redis/config/" || echo " ⚠️ redis.conf 不存在"
|
||||
[ -f mysql/my.cnf ] && cp mysql/my.cnf "${VOLUMES_DIR}/mysql/" || echo " ⚠️ my.cnf 不存在"
|
||||
[ -f config/application.yml ] && cp config/application.yml "${VOLUMES_DIR}/serv/config/" || echo " ⚠️ application.yml 不存在"
|
||||
[ -f config/log4j2-spring.xml ] && cp config/log4j2-spring.xml "${VOLUMES_DIR}/serv/config/" || echo " ⚠️ log4j2-spring.xml 不存在"
|
||||
[ -f config/web-app-config.js ] && cp config/web-app-config.js "${VOLUMES_DIR}/web/config/app-config.js" || echo " ⚠️ web-app-config.js 不存在"
|
||||
|
||||
@@ -1,23 +1,20 @@
|
||||
# ====================================
|
||||
# Nginx主配置文件
|
||||
# 可通过docker-compose.yml挂载自定义配置
|
||||
# ====================================
|
||||
|
||||
user nginx;
|
||||
worker_processes auto;
|
||||
|
||||
error_log /var/log/nginx/error.log notice;
|
||||
pid /var/run/nginx.pid;
|
||||
pid /run/nginx.pid;
|
||||
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
|
||||
http {
|
||||
include /etc/nginx/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"';
|
||||
@@ -25,20 +22,11 @@ http {
|
||||
access_log /var/log/nginx/access.log main;
|
||||
|
||||
sendfile on;
|
||||
tcp_nopush on;
|
||||
tcp_nodelay on;
|
||||
#tcp_nopush on;
|
||||
|
||||
keepalive_timeout 65;
|
||||
types_hash_max_size 2048;
|
||||
|
||||
# Gzip压缩
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_min_length 1024;
|
||||
gzip_types text/plain text/css text/xml text/javascript
|
||||
application/json application/javascript application/xml+rss
|
||||
application/rss+xml font/truetype font/opentype
|
||||
application/vnd.ms-fontobject image/svg+xml;
|
||||
#gzip on;
|
||||
|
||||
# 包含站点配置
|
||||
include /etc/nginx/conf.d/*.conf;
|
||||
}
|
||||
|
||||
@@ -93,70 +93,34 @@ echo ""
|
||||
log_info "开始导入敏感词..."
|
||||
START_TIME=$(date +%s)
|
||||
|
||||
# 创建临时SQL文件
|
||||
TEMP_SQL=$(mktemp)
|
||||
trap "rm -f ${TEMP_SQL}" EXIT
|
||||
# 预处理:转义单引号,生成临时词表文件(仅一列:word)
|
||||
TEMP_WORDS=$(mktemp)
|
||||
trap "rm -f ${TEMP_WORDS}" EXIT
|
||||
sed "s/'/''/g" "${DICT_FILE}" > "${TEMP_WORDS}"
|
||||
|
||||
# 生成SQL语句
|
||||
log_info "生成SQL语句..."
|
||||
cat > "${TEMP_SQL}" <<EOF
|
||||
-- 敏感词批量导入
|
||||
USE ${DB_NAME};
|
||||
|
||||
-- 设置字符集
|
||||
# 使用 LOAD DATA LOCAL INFILE 批量导入,极大提升导入性能
|
||||
log_info "使用 LOAD DATA LOCAL INFILE 批量导入敏感词..."
|
||||
if mysql --local-infile=1 -h"${DB_HOST}" -P"${DB_PORT}" -u"${DB_USER}" -p"${DB_PASSWORD}" "${DB_NAME}" <<EOF
|
||||
SET NAMES utf8mb4;
|
||||
|
||||
-- 清除现有的deny类型敏感词
|
||||
-- 清除现有的 deny 类型敏感词
|
||||
DELETE FROM tb_sensitive_word WHERE type = 'deny';
|
||||
|
||||
-- 批量插入敏感词
|
||||
INSERT INTO tb_sensitive_word (word, type) VALUES
|
||||
-- 从预处理文件批量导入敏感词(LOCAL 由客户端读取,避免 secure-file-priv 限制)
|
||||
LOAD DATA LOCAL INFILE '${TEMP_WORDS}'
|
||||
INTO TABLE tb_sensitive_word
|
||||
CHARACTER SET utf8mb4
|
||||
LINES TERMINATED BY '\n'
|
||||
(word)
|
||||
SET type = 'deny';
|
||||
EOF
|
||||
|
||||
# 读取敏感词并生成INSERT语句
|
||||
COUNTER=0
|
||||
while IFS= read -r word || [ -n "$word" ]; do
|
||||
# 跳过空行
|
||||
[ -z "$word" ] && continue
|
||||
|
||||
# 转义单引号
|
||||
word=$(echo "$word" | sed "s/'/''/g")
|
||||
|
||||
COUNTER=$((COUNTER + 1))
|
||||
|
||||
# 添加到SQL(最后一个不加逗号)
|
||||
if [ $COUNTER -eq ${TOTAL_WORDS} ]; then
|
||||
echo "('${word}', 'deny');" >> "${TEMP_SQL}"
|
||||
else
|
||||
echo "('${word}', 'deny')," >> "${TEMP_SQL}"
|
||||
fi
|
||||
|
||||
# 进度提示(每1000个)
|
||||
if [ $((COUNTER % 1000)) -eq 0 ]; then
|
||||
log_info "已处理 ${COUNTER}/${TOTAL_WORDS} 个敏感词..."
|
||||
fi
|
||||
done < "${DICT_FILE}"
|
||||
|
||||
# 添加查询语句
|
||||
cat >> "${TEMP_SQL}" <<EOF
|
||||
|
||||
-- 验证导入结果
|
||||
SELECT COUNT(*) AS '导入数量' FROM tb_sensitive_word WHERE type = 'deny';
|
||||
EOF
|
||||
|
||||
log_info "SQL语句生成完成(${COUNTER}个敏感词)"
|
||||
echo ""
|
||||
|
||||
# 执行SQL
|
||||
log_info "执行数据库导入..."
|
||||
if mysql -h"${DB_HOST}" -P"${DB_PORT}" -u"${DB_USER}" -p"${DB_PASSWORD}" < "${TEMP_SQL}"; then
|
||||
then
|
||||
END_TIME=$(date +%s)
|
||||
DURATION=$((END_TIME - START_TIME))
|
||||
|
||||
|
||||
echo ""
|
||||
echo "=================================================="
|
||||
log_info "导入完成!"
|
||||
log_info "成功导入: ${COUNTER} 个敏感词"
|
||||
log_info "成功导入: ${TOTAL_WORDS} 个敏感词(基于文件统计)"
|
||||
log_info "耗时: ${DURATION} 秒"
|
||||
echo "=================================================="
|
||||
else
|
||||
@@ -172,10 +136,10 @@ IMPORTED_COUNT=$(mysql -h"${DB_HOST}" -P"${DB_PORT}" -u"${DB_USER}" -p"${DB_PASS
|
||||
echo ""
|
||||
log_info "数据库中当前有 ${IMPORTED_COUNT} 个 deny 类型敏感词"
|
||||
|
||||
if [ "${IMPORTED_COUNT}" -eq "${COUNTER}" ]; then
|
||||
if [ "${IMPORTED_COUNT}" -eq "${TOTAL_WORDS}" ]; then
|
||||
log_info "✅ 验证通过:导入数量与预期一致"
|
||||
else
|
||||
log_warn "⚠️ 导入数量不匹配:预期 ${COUNTER},实际 ${IMPORTED_COUNT}"
|
||||
log_warn "⚠️ 导入数量不匹配:预期 ${TOTAL_WORDS},实际 ${IMPORTED_COUNT}"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import axios, { AxiosResponse, AxiosError, InternalAxiosRequestConfig } from "axios";
|
||||
import { ElLoading, ElMessage } from "element-plus";
|
||||
import type { ResultDomain } from "@/types";
|
||||
import { API_BASE_URL } from "@/config";
|
||||
|
||||
/**
|
||||
* 扩展AxiosRequestConfig以支持自定义配置
|
||||
@@ -67,9 +68,13 @@ export const TokenManager = {
|
||||
|
||||
/**
|
||||
* 创建axios实例
|
||||
*
|
||||
* 说明:
|
||||
* - 统一使用配置模块提供的 API_BASE_URL 作为基础路径
|
||||
* - API_BASE_URL 在开发环境来自 devConfig,在生产环境来自 window.APP_RUNTIME_CONFIG
|
||||
*/
|
||||
const request = axios.create({
|
||||
baseURL: import.meta.env.VITE_API_BASE_URL || "/api",
|
||||
baseURL: API_BASE_URL,
|
||||
timeout: 30000,
|
||||
headers: {
|
||||
'Content-Type': 'application/json;charset=UTF-8',
|
||||
|
||||
@@ -9,6 +9,9 @@ import type { SysMenu } from '@/types';
|
||||
import { MenuType } from '@/types/enums';
|
||||
import { routes } from '@/router';
|
||||
|
||||
// 预注册所有视图组件,构建时由 Vite 解析并生成按需加载的 chunk
|
||||
const VIEW_MODULES = import.meta.glob('../views/**/*.vue');
|
||||
|
||||
/**
|
||||
* 布局组件映射
|
||||
*/
|
||||
@@ -281,69 +284,44 @@ function findFirstMenuWithUrl(menus: SysMenu[]): SysMenu | null {
|
||||
* @returns 组件异步加载函数
|
||||
*/
|
||||
function getComponent(componentName: string) {
|
||||
// 检查是否是布局组件
|
||||
// 1. 若是布局组件,直接返回预定义映射
|
||||
if (LAYOUT_MAP[componentName]) {
|
||||
return LAYOUT_MAP[componentName];
|
||||
}
|
||||
|
||||
// 处理页面组件路径
|
||||
|
||||
// 2. 将后台给的 component 字段转换为 ../views/**.vue 形式的 key
|
||||
let componentPath = componentName;
|
||||
|
||||
// 如果不是以@/开头的完整路径,则添加@/views/前缀
|
||||
|
||||
// 如果不是以 @/ 开头,则认为是相对 views 根目录的路径,例如 "user/home/HomeView"
|
||||
if (!componentPath.startsWith('@/')) {
|
||||
// 确保路径以/开头
|
||||
if (!componentPath.startsWith('/')) {
|
||||
componentPath = '/' + componentPath;
|
||||
}
|
||||
// 添加@/views前缀
|
||||
componentPath = '@/views' + componentPath;
|
||||
componentPath = '@/views' + componentPath; // => '@/views/user/home/HomeView'
|
||||
}
|
||||
|
||||
// 将@/别名转换为相对路径,因为Vite动态导入可能无法正确解析别名
|
||||
if (componentPath.startsWith('@/')) {
|
||||
componentPath = componentPath.replace('@/', '../');
|
||||
}
|
||||
|
||||
// 如果没有.vue扩展名,添加它
|
||||
|
||||
// 将别名 @/ 转为相对于当前文件的路径,必须与 import.meta.glob 中的模式一致
|
||||
componentPath = componentPath.replace(/^@\//, '../'); // => '../views/user/home/HomeView'
|
||||
|
||||
// 补全 .vue 后缀
|
||||
if (!componentPath.endsWith('.vue')) {
|
||||
componentPath += '.vue';
|
||||
}
|
||||
|
||||
|
||||
// 动态导入组件
|
||||
return () => {
|
||||
return import(/* @vite-ignore */ componentPath)
|
||||
.then(module => {
|
||||
return module;
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('[路由生成] 组件加载失败:', {
|
||||
原始组件名: componentName,
|
||||
最终路径: componentPath,
|
||||
错误: error
|
||||
});
|
||||
// 返回404组件
|
||||
return import('@/views/public/error/404.vue').catch(() =>
|
||||
Promise.resolve({
|
||||
template: `<div class="component-error">
|
||||
<h3>组件加载失败</h3>
|
||||
<p>无法加载组件: ${componentPath}</p>
|
||||
<p>原始组件名: ${componentName}</p>
|
||||
<p>错误: ${error instanceof Error ? error.message : String(error)}</p>
|
||||
</div>`,
|
||||
style: `
|
||||
.component-error {
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
color: #f56565;
|
||||
background: #fed7d7;
|
||||
border-radius: 4px;
|
||||
}
|
||||
`
|
||||
})
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
// 3. 从 VIEW_MODULES 中查找对应的 loader
|
||||
const loader = VIEW_MODULES[componentPath];
|
||||
|
||||
if (!loader) {
|
||||
console.error('[路由生成] 未找到组件模块', {
|
||||
原始组件名: componentName,
|
||||
期望路径: componentPath,
|
||||
可用模块: Object.keys(VIEW_MODULES)
|
||||
});
|
||||
// 找不到时退回到 404 组件
|
||||
return () => import('@/views/public/error/404.vue');
|
||||
}
|
||||
|
||||
return loader as () => Promise<any>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user