diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 1852330d..7a9e3e77 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -29,6 +29,7 @@ networks: # 共享卷定义 volumes: + # 基础设施 nacos-data: driver: local nacos-logs: @@ -37,6 +38,7 @@ volumes: driver: local minio-config: driver: local + # Jitsi jitsi-web: driver: local jitsi-prosody: @@ -45,7 +47,13 @@ volumes: driver: local jitsi-jvb: driver: local + # 后端服务 serv-logs: driver: local + serv-config: + driver: local + # 前端服务 web-logs: driver: local + web-config: + driver: local diff --git a/docker/nginx/docker-compose.yml b/docker/nginx/docker-compose.yml new file mode 100644 index 00000000..13656163 --- /dev/null +++ b/docker/nginx/docker-compose.yml @@ -0,0 +1,37 @@ +# ================================================ +# Level 1: 基础设施服务 +# Nacos, MinIO, Nginx, Jitsi Meet +# ================================================ + +services: + # ====================== Nginx 反向代理 ====================== + nginx: + image: nginx:alpine + container_name: urban-lifeline-nginx + restart: unless-stopped + networks: + - urban-lifeline + ports: + - "80:80" + - "443:443" + environment: + TZ: Asia/Shanghai + volumes: + - ${DATA_ROOT:-../volumes}/nginx/logs:/var/log/nginx + - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro + - ./nginx/conf.d:/etc/nginx/conf.d:ro + # SSL 证书(可选) + # - ./nginx/ssl:/etc/nginx/ssl:ro + depends_on: + - urban-lifeline-serv + - urban-lifeline-web + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 30s + +networks: + urban-lifeline: + name: urban-lifeline diff --git a/docker/nginx/nginx/conf.d/default.conf b/docker/nginx/nginx/conf.d/default.conf new file mode 100644 index 00000000..28ad83e9 --- /dev/null +++ b/docker/nginx/nginx/conf.d/default.conf @@ -0,0 +1,131 @@ +# ================================================ +# Urban Lifeline - 站点配置 (All-in-One 模式) +# ================================================ + +# 上游服务定义 - 后端 All-in-One 容器 +upstream gateway { + server urban-lifeline-serv:8080; + keepalive 32; +} + +# 上游服务定义 - 前端 All-in-One 容器 +upstream shared { + server urban-lifeline-web:8000; +} + +upstream platform { + server urban-lifeline-web:8001; +} + +upstream workcase-web { + server urban-lifeline-web:8002; +} + +upstream bidding-web { + server urban-lifeline-web:8003; +} + +upstream workcase-wechat { + server urban-lifeline-web:8004; +} + +server { + listen 80; + server_name localhost; + + # 健康检查端点 + location /health { + access_log off; + return 200 "healthy\n"; + add_header Content-Type text/plain; + } + + # ====================== 前端应用代理 ====================== + + # Shared 公共模块 (Module Federation 远程模块) + location /shared/ { + proxy_pass http://shared/; + 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; + + # 允许跨域 (Module Federation 需要) + add_header Access-Control-Allow-Origin *; + add_header Access-Control-Allow-Methods "GET, OPTIONS"; + add_header Access-Control-Allow-Headers "Origin, Content-Type, Accept"; + } + + # Platform 管理平台 + location /platform/ { + proxy_pass http://platform/; + 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; + } + + # Workcase 工单系统 PC端 + location /workcase/ { + proxy_pass http://workcase-web/; + 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; + } + + # Bidding 招标系统 + location /bidding/ { + proxy_pass http://bidding-web/; + 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; + } + + # Workcase 工单系统微信端 + location /workcase-wechat/ { + proxy_pass http://workcase-wechat/; + 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; + } + + # 默认首页(重定向到 platform) + location = / { + return 302 /platform/; + } + + # ====================== API 代理 ====================== + + # 后端 API 代理 + location /urban-lifeline/ { + proxy_pass http://gateway/urban-lifeline/; + 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; + + # WebSocket 支持 + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + + # 超时设置 + proxy_connect_timeout 60s; + proxy_send_timeout 60s; + proxy_read_timeout 60s; + } + + # ====================== 错误页面 ====================== + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } +} diff --git a/docker/nginx/nginx/nginx.conf b/docker/nginx/nginx/nginx.conf new file mode 100644 index 00000000..ed8a2477 --- /dev/null +++ b/docker/nginx/nginx/nginx.conf @@ -0,0 +1,47 @@ +# ================================================ +# Urban Lifeline - Nginx 主配置 +# ================================================ + +user nginx; +worker_processes auto; +error_log /var/log/nginx/error.log warn; +pid /var/run/nginx.pid; + +events { + worker_connections 1024; + use epoll; + multi_accept on; +} + +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"'; + + access_log /var/log/nginx/access.log main; + + # 性能优化 + sendfile on; + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 65; + types_hash_max_size 2048; + + # Gzip 压缩 + gzip on; + gzip_vary on; + gzip_proxied any; + gzip_comp_level 6; + gzip_types text/plain text/css text/xml application/json application/javascript + application/xml application/xml+rss text/javascript application/x-javascript; + + # 上传文件大小限制 + client_max_body_size 100M; + + # 引入站点配置 + include /etc/nginx/conf.d/*.conf; +} diff --git a/docker/urbanLifeline/.env.example b/docker/urbanLifeline/.env.example index e294c73f..fdeb9f22 100644 --- a/docker/urbanLifeline/.env.example +++ b/docker/urbanLifeline/.env.example @@ -6,6 +6,10 @@ # 镜像版本 IMAGE_VERSION=latest +# 数据目录 (日志、配置等) +DATA_ROOT=../volumes + +# ==================== 后端配置 ==================== # Spring 运行环境 SPRING_PROFILES_ACTIVE=prod @@ -13,21 +17,30 @@ SPRING_PROFILES_ACTIVE=prod NACOS_SERVER_ADDR=nacos:8848 NACOS_NAMESPACE= +# ==================== 前端配置 ==================== +# 前端端口 (可自定义) +SHARED_PORT=8000 +PLATFORM_PORT=8001 +WORKCASE_PORT=8002 +BIDDING_PORT=8003 +WORKCASE_WECHAT_PORT=8004 + +# ==================== 基础设施 ==================== +# MySQL (Nacos 使用) +MYSQL_HOST=host.docker.internal +MYSQL_PORT=3306 +MYSQL_USER=root +MYSQL_PASSWORD=123456 + # MinIO 配置 -MINIO_ENDPOINT=http://minio:9000 +MINIO_ROOT_USER=minioadmin +MINIO_ROOT_PASSWORD=minioadmin123 # Dify AI 配置 DIFY_API_URL=http://dify-api:5001 -# 日志目录 -LOG_ROOT=../../volumes/logs - -# 前端 API 地址 -API_BASE_URL=http://gateway:8080 - -# 端口映射 -GATEWAY_PORT=8080 -PLATFORM_PORT=8001 -WORKCASE_PORT=8002 -BIDDING_WEB_PORT=8003 -WORKCASE_WECHAT_PORT=8004 +# ==================== Jitsi 视频会议 ==================== +JWT_APP_ID=urbanLifeline +JWT_APP_SECRET=urbanLifeline-jitsi-secret-key-2025-production-safe-hs256 +JVB_HOST_ADDRESS=192.168.0.253 +JITSI_PUBLIC_URL=https://org.xyzh.yslg.jitsi diff --git a/docker/urbanLifeline/README.md b/docker/urbanLifeline/README.md index 03968f5d..27051785 100644 --- a/docker/urbanLifeline/README.md +++ b/docker/urbanLifeline/README.md @@ -1,28 +1,26 @@ -# Urban Lifeline Docker 部署 +# Urban Lifeline Docker 部署 (All-in-One) ## 目录结构 ``` docker/urbanLifeline/ ├── .env.example # 环境变量示例 +├── Makefile # 快捷命令 ├── README.md # 本文档 ├── serv/ # 后端服务 -│ ├── docker-compose.yml │ ├── build.sh # 构建脚本 -│ ├── start.sh # 启动脚本 │ ├── Dockerfile.base # 基础镜像 -│ ├── Dockerfile.template # 服务模板 -│ └── Dockerfile.* # 各服务 Dockerfile +│ ├── Dockerfile.serv # 服务镜像 +│ └── service-manager.sh # 服务管理脚本 └── web/ # 前端应用 - ├── docker-compose.yml ├── build.sh # 构建脚本 - ├── nginx.conf # Nginx 配置 - └── Dockerfile.* # 各前端 Dockerfile + ├── Dockerfile.web # 前端镜像 + └── web-manager.sh # 前端管理脚本 ``` ## 服务端口 -### 后端服务 +### 后端服务 (urban-lifeline-serv) | 服务 | 端口 | 说明 | |------|------|------| | gateway | 8080 | API 网关 | @@ -37,56 +35,82 @@ docker/urbanLifeline/ | platform | 8089 | 平台服务 | | ai | 8090 | AI 服务 | -### 前端应用 +### 前端应用 (urban-lifeline-web) | 应用 | 端口 | 说明 | |------|------|------| +| shared | 8000 | 公共模块 (Module Federation) | | platform | 8001 | 管理平台 | -| workcase-web | 8002 | 工单系统 PC | -| bidding-web | 8003 | 招标系统 | -| workcase-wechat | 8004 | 工单微信端 | +| workcase | 8002 | 工单系统 PC | +| bidding | 8003 | 招标系统 | +| workcase_wechat | 8004 | 工单微信端 | ## 快速开始 ### 1. 准备环境变量 ```bash +cd docker/urbanLifeline cp .env.example .env # 编辑 .env 配置 ``` -### 2. 构建后端 +### 2. 构建镜像 ```bash -# 先编译 Java 项目 -cd urbanLifelineServ -mvn clean package -DskipTests +# 构建后端 (需先编译 Java) +cd urbanLifelineServ && mvn clean package -DskipTests +cd docker/urbanLifeline && make build-serv -# 构建 Docker 镜像 -cd docker/urbanLifeline/serv -./build.sh # 构建所有 -./build.sh gateway # 构建单个 +# 构建前端 (需先构建前端) +cd urbanLifelineWeb && pnpm build +cd docker/urbanLifeline && make build-web ``` -### 3. 构建前端 +### 3. 启动服务 ```bash -# 先构建前端项目 -cd urbanLifelineWeb -pnpm install -pnpm build - -# 构建 Docker 镜像 -cd docker/urbanLifeline/web -./build.sh # 构建所有 -./build.sh platform # 构建单个 +make up ``` -### 4. 启动服务 +### 4. 管理服务 ```bash -# 启动后端 -cd docker/urbanLifeline/serv -docker-compose --profile serv up -d +# 查看状态 +make status -# 启动前端 -cd docker/urbanLifeline/web -docker-compose --profile web up -d +# 重启单个后端服务 +make restart-serv SERVICE=gateway + +# 重启单个前端站点 +make restart-web SITE=platform + +# 查看日志 +make logs +``` + +## 配置外挂 + +容器支持配置外挂,可在运行时覆盖默认配置: + +### 后端配置目录结构 +``` +volumes/config/serv/ +├── gateway.yml # gateway 服务配置 +├── system.yml # system 服务配置 +├── auth.yml # auth 服务配置 +├── file.yml # file 服务配置 +├── log.yml # log 服务配置 +├── message.yml # message 服务配置 +├── crontab.yml # crontab 服务配置 +├── ai.yml # ai 服务配置 +├── bidding.yml # bidding 服务配置 +├── platform.yml # platform 服务配置 +└── workcase.yml # workcase 服务配置 +``` + +每个服务的配置文件会通过 `spring.config.additional-location` 加载,覆盖 JAR 包内的默认配置。 + +### 前端配置 +``` +volumes/config/web/ +├── env.js # 运行时环境变量 +└── config.json # 应用配置 ``` ## 依赖服务 @@ -97,4 +121,4 @@ docker-compose --profile web up -d - Redis (缓存) - MinIO (对象存储) -可通过 `docker/infra` 目录启动基础设施。 +通过 `docker compose --profile infra up -d` 启动基础设施。 diff --git a/docker/urbanLifeline/postgres/.env.example b/docker/urbanLifeline/postgres/.env.example new file mode 100644 index 00000000..3a00e2f9 --- /dev/null +++ b/docker/urbanLifeline/postgres/.env.example @@ -0,0 +1,33 @@ +# ================================================ +# PostgreSQL 配置 +# 复制此文件为 .env 并修改配置 +# ================================================ + +# ------------------------------ +# PostgreSQL 版本 +# ------------------------------ +POSTGRES_VERSION=16 + +# ------------------------------ +# 端口配置 +# ------------------------------ +POSTGRES_PORT=5432 + +# ------------------------------ +# 数据库配置 +# ------------------------------ +POSTGRES_USER=postgres +POSTGRES_PASSWORD=postgres123456 +POSTGRES_DB=urban_lifeline + +# ------------------------------ +# 挂载目录配置 +# ------------------------------ +# 数据目录 +DATA_ROOT=./volumes + +# 日志目录 +LOG_ROOT=./volumes + +# 配置文件目录 +CONFIG_ROOT=./volumes diff --git a/docker/urbanLifeline/postgres/Dockerfile.pg b/docker/urbanLifeline/postgres/Dockerfile.pg new file mode 100644 index 00000000..889772ce --- /dev/null +++ b/docker/urbanLifeline/postgres/Dockerfile.pg @@ -0,0 +1,107 @@ +# ================================================ +# Urban Lifeline - PostgreSQL 数据库镜像 +# 支持初始化脚本和自定义配置 +# ================================================ + +FROM postgres:16-alpine + +# 设置环境变量 +ENV LANG=C.UTF-8 \ + TZ=Asia/Shanghai \ + PGCLIENTENCODING=UTF8 + +# 安装时区数据 +RUN apk add --no-cache tzdata \ + && ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ + && echo "Asia/Shanghai" > /etc/timezone + +# 创建初始化目录 +RUN mkdir -p /docker-entrypoint-initdb.d /opt/sql + +# 复制初始化脚本和 SQL 文件 +COPY init/sql/ /opt/sql/ +COPY init/bin.sh /opt/bin.sh + +# 创建 Docker 初始化入口脚本 +RUN cat > /docker-entrypoint-initdb.d/01-init-database.sh <<'EOF' +#!/bin/bash +set -e + +echo "==========================================" +echo "Urban Lifeline - PostgreSQL 数据库初始化" +echo "==========================================" + +# 设置环境变量 +export PGCLIENTENCODING=UTF8 + +# 检查数据库是否已初始化(通过检查初始化状态表) +check_initialized() { + # 先检查数据库是否存在 + if ! psql -U "$POSTGRES_USER" -d "postgres" -tAc "SELECT 1 FROM pg_database WHERE datname = '$POSTGRES_DB'" | grep -q 1; then + return 1 # 数据库不存在,需要初始化 + fi + + # 检查初始化状态表是否存在且标记为成功 + if psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -tAc "SELECT 1 FROM _db_init_status WHERE script_name = '01-init-database.sh' AND status = 'success'" 2>/dev/null | grep -q 1; then + return 0 # 已初始化 + fi + + return 1 # 未初始化 +} + +# 检查是否已经初始化 +if check_initialized; then + echo "==========================================" + echo "✅ 数据库已初始化,跳过初始化步骤" + echo "==========================================" + exit 0 +fi + +echo "首次启动,开始初始化数据库..." + +cd /opt/sql + +# 执行 initAll.sql(包含创建数据库、表结构和初始数据) +echo "执行数据库初始化脚本..." +psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "postgres" -f initAll.sql + +if [ $? -ne 0 ]; then + echo "==========================================" + echo "❌ 数据库初始化失败!" + echo "==========================================" + exit 1 +fi + +# 创建初始化状态表并标记完成 +echo "标记初始化状态..." +psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL + -- 创建初始化状态表 + CREATE TABLE IF NOT EXISTS _db_init_status ( + id SERIAL PRIMARY KEY, + script_name VARCHAR(255) NOT NULL UNIQUE, + executed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + status VARCHAR(50) DEFAULT 'init' + ); + + -- 标记初始化完成 + INSERT INTO _db_init_status (script_name, status) + VALUES ('01-init-database.sh', 'success') + ON CONFLICT (script_name) DO UPDATE SET status = 'success', executed_at = CURRENT_TIMESTAMP; +EOSQL + +echo "==========================================" +echo "✅ 数据库初始化完成!" +echo "默认用户: admin, 密码: 123456" +echo "==========================================" +EOF + +# 设置执行权限 +RUN chmod +x /docker-entrypoint-initdb.d/01-init-database.sh \ + && chmod +x /opt/bin.sh + +# 暴露端口 +EXPOSE 5432 + +# 健康检查 +HEALTHCHECK --interval=10s --timeout=5s --retries=5 --start-period=60s \ + CMD pg_isready -U ${POSTGRES_USER:-postgres} -d ${POSTGRES_DB:-urban_lifeline} || exit 1 diff --git a/docker/urbanLifeline/postgres/docker-compose.yml b/docker/urbanLifeline/postgres/docker-compose.yml new file mode 100644 index 00000000..7ff6d0d7 --- /dev/null +++ b/docker/urbanLifeline/postgres/docker-compose.yml @@ -0,0 +1,68 @@ +# ================================================ +# Urban Lifeline - PostgreSQL 数据库 +# ================================================ + +services: + postgres: + build: + context: . + dockerfile: Dockerfile.pg + image: urban-lifeline-postgres:${POSTGRES_VERSION:-16} + container_name: urban-lifeline-postgres + restart: unless-stopped + env_file: + - .env + networks: + - urban-lifeline + ports: + - "${POSTGRES_PORT:-5432}:5432" + environment: + POSTGRES_USER: ${POSTGRES_USER:-postgres} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-postgres123456} + POSTGRES_DB: ${POSTGRES_DB:-urban_lifeline} + TZ: Asia/Shanghai + PGTZ: Asia/Shanghai + PGCLIENTENCODING: UTF8 + volumes: + # 数据目录 + - ${DATA_ROOT:-./volumes}/data:/var/lib/postgresql/data + # 日志目录 + - ${LOG_ROOT:-./volumes}/logs:/var/log/postgresql + # 配置文件目录(可选,用于自定义 postgresql.conf) + - ${CONFIG_ROOT:-./volumes}/config:/etc/postgresql/conf.d:ro + command: + - "postgres" + - "-c" + - "max_connections=200" + - "-c" + - "shared_buffers=256MB" + - "-c" + - "effective_cache_size=768MB" + - "-c" + - "maintenance_work_mem=64MB" + - "-c" + - "checkpoint_completion_target=0.9" + - "-c" + - "wal_buffers=16MB" + - "-c" + - "default_statistics_target=100" + - "-c" + - "random_page_cost=1.1" + - "-c" + - "effective_io_concurrency=200" + - "-c" + - "log_timezone=Asia/Shanghai" + - "-c" + - "timezone=Asia/Shanghai" + - "-c" + - "client_encoding=UTF8" + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-postgres} -d ${POSTGRES_DB:-urban_lifeline}"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 60s + +networks: + urban-lifeline: + name: urban-lifeline diff --git a/docker/urbanLifeline/postgres/init/bin.sh b/docker/urbanLifeline/postgres/init/bin.sh new file mode 100644 index 00000000..672ba116 --- /dev/null +++ b/docker/urbanLifeline/postgres/init/bin.sh @@ -0,0 +1,290 @@ + +#!/bin/bash + +# 定义颜色输出 +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# 设置脚本所在目录为工作目录 +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +SQL_DIR="${SCRIPT_DIR}/sql" + +# 打印带时间戳的日志 +log() { + local level=$1 + local message=$2 + local color=$NC + + case $level in + "INFO") color=$BLUE;; + "SUCCESS") color=$GREEN;; + "WARN") color=$YELLOW;; + "ERROR") color=$RED;; + esac + + echo -e "[$(date '+%Y-%m-%d %H:%M:%S')] ${color}${level}${NC}: ${message}" +} + +# 数据库连接信息(可通过环境变量覆盖) +DB_HOST=${POSTGRES_HOST:-"localhost"} +DB_PORT=${POSTGRES_PORT:-"5432"} +DB_NAME=${POSTGRES_DB:-"urban_lifeline"} +DB_USER=${POSTGRES_USER:-"postgres"} +DB_PASSWORD=${POSTGRES_PASSWORD:-"postgres"} + +# 设置 PSQL 环境变量以支持中文 +export PGCLIENTENCODING=UTF8 + +# 检查psql命令是否可用 +check_psql() { + if ! command -v psql &> /dev/null; then + echo -e "${RED}Error: psql command not found. Please install PostgreSQL client.${NC}" + exit 1 + fi +} + +# 检查并创建数据库用户 +check_and_create_user() { + local new_user=$1 + local new_password=$2 + + # 使用 postgres 用户执行 + if sudo -u postgres psql -c "SELECT 1 FROM pg_roles WHERE rolname = '$new_user'" | grep -q 1; then + echo -e "${GREEN}User $new_user already exists${NC}" + else + echo -e "${YELLOW}Creating user $new_user...${NC}" + sudo -u postgres psql -c "CREATE USER $new_user WITH PASSWORD '$new_password' CREATEDB;" + if [ $? -eq 0 ]; then + echo -e "${GREEN}User $new_user created successfully${NC}" + else + echo -e "${RED}Failed to create user $new_user${NC}" + exit 1 + fi + fi +} + +# 检查数据库连接 +check_db_connection() { + # 首先尝试以当前用户身份连接 + if ! psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "postgres" -c '\q' &> /dev/null; then + echo -e "${YELLOW}Could not connect with current settings, attempting to create user...${NC}" + # 创建用户并设置权限 + check_and_create_user "$DB_USER" "$DB_PASSWORD" + + # 再次检查连接 + if ! psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "postgres" -c '\q' &> /dev/null; then + echo -e "${RED}Error: Could not connect to PostgreSQL server.${NC}" + echo "Please check your connection settings:" + echo "Host: $DB_HOST" + echo "Port: $DB_PORT" + echo "User: $DB_USER" + exit 1 + fi + fi +} + +# 执行SQL文件 +execute_sql_file() { + local sql_file=$1 + if [ ! -f "$sql_file" ]; then + echo -e "${RED}Error: SQL file not found: $sql_file${NC}" + return 1 + fi + + echo -e "${YELLOW}Executing SQL file: $sql_file${NC}" + PGPASSWORD=$DB_PASSWORD psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -f "$sql_file" + local status=$? + if [ $status -eq 0 ]; then + echo -e "${GREEN}Successfully executed: $sql_file${NC}" + else + echo -e "${RED}Failed to execute: $sql_file${NC}" + return $status + fi +} + +# 初始化数据库 +init() { + echo -e "${YELLOW}Initializing database...${NC}" + + # 执行完整的初始化脚本 + log "INFO" "Executing initialization script..." + # Run from inside the SQL_DIR so relative \i includes in initAll.sql (like createDB.sql) + # resolve relative to the SQL directory. + ( + if [ ! -d "$SQL_DIR" ]; then + echo -e "${RED}Error: SQL directory not found: $SQL_DIR${NC}" + exit 1 + fi + cd "$SQL_DIR" || exit 1 + PGPASSWORD=$DB_PASSWORD psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "postgres" -v ON_ERROR_STOP=1 -f "initAll.sql" + ) + + if [ $? -eq 0 ]; then + log "SUCCESS" "Database initialization completed successfully" + else + log "ERROR" "Database initialization failed" + return 1 + fi + + if [ $? -ne 0 ]; then + echo -e "${RED}Failed to create database.${NC}" + return 1 + fi + + # 2. 创建扩展和设置搜索路径 + echo -e "${YELLOW}Creating extensions...${NC}" + check_extensions_availability() { + # 检查服务器上是否存在需创建的扩展 + local missing=() + local exts=("uuid-ossp" "pg_trgm" "btree_gist") + for ext in "${exts[@]}"; do + # 查询 pg_available_extensions 来判断扩展是否已安装到服务器目录 + if ! PGPASSWORD=$DB_PASSWORD psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -tAc "SELECT 1 FROM pg_available_extensions WHERE name = '$ext';" | grep -q 1; then + missing+=("$ext") + fi + done + + if [ ${#missing[@]} -ne 0 ]; then + echo -e "${RED}Error: The following server-side extensions are not available: ${missing[*]}${NC}" + echo "If you compiled PostgreSQL from source, you need to build and install the contrib modules into the server's installation prefix. Example steps:" + echo " # 在 PostgreSQL 源码目录下运行:" + echo " cd /path/to/postgresql-source/contrib" + echo " make" + echo " sudo make install" + echo "或者只安装缺失的模块(例如 uuid-ossp):" + echo " cd /path/to/postgresql-source/contrib/uuid-ossp" + echo " make" + echo " sudo make install" + echo "安装完成后,重启 PostgreSQL 服务并重新运行此脚本:" + echo " sudo systemctl restart postgresql" + echo "如果你使用的是容器或自定义路径,请确保将编译安装的扩展安装到 PostgreSQL 的 \$(pg_config --sharedir)/extension 目录下。" + return 1 + fi + return 0 + } + + # 检查扩展可用性,若缺失则给出建议并退出 + if ! check_extensions_availability; then + return 1 + fi + + PGPASSWORD=$DB_PASSWORD psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -c " + CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\"; + CREATE EXTENSION IF NOT EXISTS \"pg_trgm\"; + CREATE EXTENSION IF NOT EXISTS \"btree_gist\";" + + if [ $? -ne 0 ]; then + echo -e "${RED}Failed to create extensions.${NC}" + return 1 + fi + + # 3. 逐个执行初始化SQL文件 + echo -e "${YELLOW}Initializing tables...${NC}" + while IFS= read -r line || [[ -n "$line" ]]; do + # 跳过注释和空行 + [[ $line =~ ^--.*$ ]] && continue + [[ -z "${line// }" ]] && continue + + # 从 \i 命令中提取文件名 + if [[ $line =~ \\i[[:space:]]+([^[:space:]]+) ]]; then + sql_file="${SQL_DIR}/${BASH_REMATCH[1]}" + if [[ $sql_file != *"createDB.sql"* ]]; then # 跳过createDB.sql + echo -e "${YELLOW}Executing: $sql_file${NC}" + PGPASSWORD=$DB_PASSWORD psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -f "$sql_file" + if [ $? -ne 0 ]; then + echo -e "${RED}Failed to execute: $sql_file${NC}" + return 1 + fi + fi + fi + done < "${SQL_DIR}/initAll.sql" + + # 4. 设置搜索路径 + echo -e "${YELLOW}Setting search path...${NC}" + PGPASSWORD=$DB_PASSWORD psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -c " + ALTER DATABASE $DB_NAME SET search_path TO sys, public;" + + echo -e "${GREEN}Database initialization completed successfully.${NC}" + return 0 +} + +# 重新初始化数据库 +reinit() { + echo -e "${YELLOW}Reinitializing database...${NC}" + delete + init +} + +# 删除数据库 +delete() { + echo -e "${YELLOW}Deleting database...${NC}" + + # 多次尝试终止连接(因为某些连接可能会立即重连) + for i in {1..3}; do + log "INFO" "Terminating database connections (attempt $i/3)..." + PGPASSWORD=$DB_PASSWORD psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "postgres" -c " + SELECT pg_terminate_backend(pg_stat_activity.pid) + FROM pg_stat_activity + WHERE pg_stat_activity.datname = '$DB_NAME' + AND pid <> pg_backend_pid();" > /dev/null 2>&1 + + # 等待连接完全关闭 + sleep 1 + done + + # 尝试删除数据库,最多重试3次 + for i in {1..3}; do + log "INFO" "Attempting to drop database (attempt $i/3)..." + if PGPASSWORD=$DB_PASSWORD psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "postgres" -c "DROP DATABASE IF EXISTS $DB_NAME;" 2>&1; then + log "SUCCESS" "Database deleted successfully" + return 0 + else + log "WARN" "Failed to drop database, retrying after 2 seconds..." + sleep 2 + fi + done + + log "ERROR" "Failed to delete database after 3 attempts" + log "ERROR" "Please ensure all connections to the database are closed, including:" + log "ERROR" " - Running application servers" + log "ERROR" " - IDE database connections" + log "ERROR" " - pgAdmin or other database tools" + return 1 +} + +# 显示帮助信息 +show_help() { + echo "Usage: $0 {init|reinit|delete}" + echo "Commands:" + echo " init Initialize the database" + echo " reinit Reinitialize the database (delete and create)" + echo " delete Delete the database" +} + +# 主函数 +main() { + check_psql + check_db_connection + + case "$1" in + "init") + init + ;; + "reinit") + reinit + ;; + "delete") + delete + ;; + *) + show_help + exit 1 + ;; + esac +} + +# Call main with all passed arguments so the script runs when invoked +main "$@" \ No newline at end of file diff --git a/docker/urbanLifeline/postgres/init/sql/createDB.sql b/docker/urbanLifeline/postgres/init/sql/createDB.sql new file mode 100644 index 00000000..5ff18039 --- /dev/null +++ b/docker/urbanLifeline/postgres/init/sql/createDB.sql @@ -0,0 +1,28 @@ + +-- 删除已存在的数据库(如果存在) +DROP DATABASE IF EXISTS urban_lifeline; + +-- 创建新数据库,使用 UTF8 编码,并设置适合中文的排序规则 +-- 使用 template0 确保干净的数据库模板 +-- zh_CN.UTF-8 支持中文字符排序和比较 +CREATE DATABASE urban_lifeline + ENCODING 'UTF8' + TEMPLATE template0 + LC_COLLATE 'zh_CN.UTF-8' + LC_CTYPE 'zh_CN.UTF-8'; + +-- 连接到新创建的数据库 +\c urban_lifeline; + +-- -- 创建扩展(如果需要) +CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; -- UUID 支持 +CREATE EXTENSION IF NOT EXISTS "pg_trgm"; -- 文本搜索支持 +CREATE EXTENSION IF NOT EXISTS "btree_gist"; -- GiST 索引支持 +CREATE EXTENSION IF NOT EXISTS "vector"; -- 向量 + +-- 设置搜索路径(可选,但建议设置) +-- ALTER DATABASE urban-lifeline SET search_path TO sys, public; + +-- sudo ./configure --prefix=/opt/postgres/postgres-17.6 +-- --with-uuid=ossp --with-openssl --with-libxml --with-pam +-- && sudo make && sudo make install diff --git a/docker/urbanLifeline/postgres/init/sql/createTableAI.sql b/docker/urbanLifeline/postgres/init/sql/createTableAI.sql new file mode 100644 index 00000000..3752bd0b --- /dev/null +++ b/docker/urbanLifeline/postgres/init/sql/createTableAI.sql @@ -0,0 +1,188 @@ +CREATE SCHEMA IF NOT EXISTS ai; + +-- AI智能体配置 +DROP TABLE IF EXISTS ai.tb_agent CASCADE; +CREATE TABLE ai.tb_agent( + optsn VARCHAR(50) NOT NULL, -- 流水号 + agent_id VARCHAR(50) NOT NULL, -- 智能体ID + name VARCHAR(50) NOT NULL, -- 智能体名称 + description VARCHAR(500) DEFAULT NULL, -- 智能体描述 + link VARCHAR(500) DEFAULT NULL, -- 智能体url + api_key VARCHAR(500) NOT NULL, -- dify智能体APIKEY + is_outer BOOLEAN DEFAULT false, -- 是否是对外智能体,未登录可用 + introduce VARCHAR(500) NOT NULL, -- 引导词 + prompt_cards JSONB DEFAULT '[]'::jsonb, -- 提示卡片数组 [{file_id:'', prompt:''}] + category VARCHAR(50) NOT NULL, -- 分类 + creator VARCHAR(50) DEFAULT NULL, -- 创建者 + updater VARCHAR(50) DEFAULT NULL, -- 更新者 + create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间 + update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间 + delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间 + deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 + PRIMARY KEY (agent_id), + UNIQUE (optsn), + UNIQUE (api_key) +); + +-- AI智能体对话 +DROP TABLE IF EXISTS ai.tb_chat CASCADE; +CREATE TABLE ai.tb_chat( + optsn VARCHAR(50) NOT NULL, -- 流水号 + chat_id VARCHAR(50) NOT NULL, -- 对话ID + agent_id VARCHAR(50) NOT NULL, -- 智能体ID + user_id VARCHAR(50) NOT NULL, -- 用户ID + user_type BOOLEAN NOT NULL DEFAULT true, -- 用户类型 true-系统内部人员 false-系统外部人员 + title VARCHAR(500) NOT NULL, -- 对话标题 + channel VARCHAR(50) DEFAULT 'agent', -- 对话渠道 agent、wechat + create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间 + update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间 + delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间 + deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 + PRIMARY KEY (chat_id), + UNIQUE (optsn) +); + +-- AI智能体对话消息 +DROP TABLE IF EXISTS ai.tb_chat_message CASCADE; +CREATE TABLE ai.tb_chat_message( + optsn VARCHAR(50) NOT NULL, -- 流水号 + message_id VARCHAR(50) NOT NULL, -- 消息ID + dify_message_id VARCHAR(100) DEFAULT NULL, -- Dify消息ID + chat_id VARCHAR(50) NOT NULL, -- 对话ID + role VARCHAR(50) NOT NULL, -- 角色:user-用户/ai-智能体/recipient-来客 + content TEXT NOT NULL, -- 消息内容 + files VARCHAR(50)[] DEFAULT NULL, -- 文件id数组 + comment VARCHAR(50) DEFAULT NULL, -- 评价 + create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间 + update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间 + delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间 + deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 + PRIMARY KEY (message_id), + UNIQUE (optsn) +); + + +-- 知识库配置 bidding和workcase2个服务使用 +DROP TABLE IF EXISTS ai.tb_knowledge CASCADE; +CREATE TABLE ai.tb_knowledge( + -- 知识库dify相关配置 + optsn VARCHAR(50) NOT NULL, -- 流水号 + knowledge_id VARCHAR(50) NOT NULL, -- 知识库ID + title VARCHAR(255) NOT NULL, -- 知识库标题 + avatar VARCHAR(255) DEFAULT NULL, -- 知识库头像 + description VARCHAR(500) DEFAULT NULL, -- 知识库描述 + dify_dataset_id VARCHAR(100) DEFAULT NULL, -- Dify知识库ID(Dataset ID) + dify_indexing_technique VARCHAR(50) DEFAULT 'high_quality', -- Dify索引方式(high_quality/economy) + embedding_model VARCHAR(100) DEFAULT NULL, -- 向量模型名称 + embedding_model_provider VARCHAR(100) DEFAULT NULL, -- 向量模型提供商 + rerank_model VARCHAR(100) DEFAULT NULL, -- Rerank模型名称 + rerank_model_provider VARCHAR(100) DEFAULT NULL, -- Rerank模型提供商 + reranking_enable BOOLEAN DEFAULT false, -- 是否启用Rerank + retrieval_top_k INTEGER DEFAULT 2, -- 检索Top K(返回前K个结果) + retrieval_score_threshold DECIMAL(3,2) DEFAULT 0.00, -- 检索分数阈值(0.00-1.00) + document_count INTEGER DEFAULT 0, -- 文档数量 + total_chunks INTEGER DEFAULT 0, -- 总分段数 + -- 下面是服务使用 + service VARCHAR(50) DEFAULT NULL, -- 所属服务 workcase、bidding + project_id VARCHAR(50) DEFAULT NULL, -- bidding所属项目ID + category VARCHAR(50) DEFAULT NULL, -- 所属分类 workcase 内部知识库、外部知识库 + creator VARCHAR(50) NOT NULL, -- 创建者(用户ID) + dept_path VARCHAR(50) DEFAULT NULL, -- 创建者部门路径 + updater VARCHAR(50) DEFAULT NULL, -- 更新者 + create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间 + update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间 + delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间 + deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 + PRIMARY KEY (optsn), + UNIQUE (knowledge_id), + UNIQUE (dify_dataset_id) +); + +-- 知识库配置表字段注释 +COMMENT ON TABLE ai.tb_knowledge IS '知识库配置表'; +COMMENT ON COLUMN ai.tb_knowledge.optsn IS '流水号'; +COMMENT ON COLUMN ai.tb_knowledge.knowledge_id IS '知识库ID'; +COMMENT ON COLUMN ai.tb_knowledge.title IS '知识库标题'; +COMMENT ON COLUMN ai.tb_knowledge.avatar IS '知识库头像'; +COMMENT ON COLUMN ai.tb_knowledge.description IS '知识库描述'; +COMMENT ON COLUMN ai.tb_knowledge.dify_dataset_id IS 'Dify知识库ID(Dataset ID)'; +COMMENT ON COLUMN ai.tb_knowledge.dify_indexing_technique IS 'Dify索引方式(high_quality/economy)'; +COMMENT ON COLUMN ai.tb_knowledge.embedding_model IS '向量模型名称'; +COMMENT ON COLUMN ai.tb_knowledge.embedding_model_provider IS '向量模型提供商'; +COMMENT ON COLUMN ai.tb_knowledge.rerank_model IS 'Rerank模型名称'; +COMMENT ON COLUMN ai.tb_knowledge.rerank_model_provider IS 'Rerank模型提供商'; +COMMENT ON COLUMN ai.tb_knowledge.reranking_enable IS '是否启用Rerank'; +COMMENT ON COLUMN ai.tb_knowledge.retrieval_top_k IS '检索Top K(返回前K个结果)'; +COMMENT ON COLUMN ai.tb_knowledge.retrieval_score_threshold IS '检索分数阈值(0.00-1.00)'; +COMMENT ON COLUMN ai.tb_knowledge.document_count IS '文档数量'; +COMMENT ON COLUMN ai.tb_knowledge.total_chunks IS '总分段数'; +COMMENT ON COLUMN ai.tb_knowledge.service IS '所属服务 workcase、bidding'; +COMMENT ON COLUMN ai.tb_knowledge.project_id IS 'bidding所属项目ID'; +COMMENT ON COLUMN ai.tb_knowledge.category IS '所属分类 workcase 内部知识库、外部知识库'; +COMMENT ON COLUMN ai.tb_knowledge.creator IS '创建者(用户ID)'; +COMMENT ON COLUMN ai.tb_knowledge.dept_path IS '创建者部门路径'; +COMMENT ON COLUMN ai.tb_knowledge.updater IS '更新者'; +COMMENT ON COLUMN ai.tb_knowledge.create_time IS '创建时间'; +COMMENT ON COLUMN ai.tb_knowledge.update_time IS '更新时间'; +COMMENT ON COLUMN ai.tb_knowledge.delete_time IS '删除时间'; +COMMENT ON COLUMN ai.tb_knowledge.deleted IS '是否删除'; +-- bidding知识库根据project等变化 +-- workcase知识库固定8个 +-- workcase外部知识库:4个知识库: +-- 1. 设备操作指南 +-- 2. 常见故障解决方案 +-- 3. 三包外服务政策 +-- 4. 配件咨询话术 +-- workcase内部知识库:4个知识库: +-- 1. 技术维修手册 +-- 2. 产品参数明细 +-- 3. 内部服务流程规范 +-- 4. 客户服务话术模板 + +-- 知识库文件 文件上传dify知识库,对dify内的文件修改不生成新版本, 只有重新上传才生成新版本 +DROP TABLE IF EXISTS ai.tb_knowledge_file CASCADE; +CREATE TABLE ai.tb_knowledge_file( + optsn VARCHAR(50) NOT NULL, -- 流水号 + knowledge_id VARCHAR(50) NOT NULL, -- 知识库ID + file_root_id VARCHAR(50) NOT NULL, -- 文件根ID + file_id VARCHAR(50) NOT NULL, -- 文件ID + dify_file_id VARCHAR(50) NOT NULL, -- dify文件ID + version INTEGER NOT NULL DEFAULT 1, -- 文件版本 + create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间 + update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间 + delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间 + deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 + PRIMARY KEY (optsn), + UNIQUE (knowledge_id, file_id) +); + +-- 知识库文件表字段注释 +COMMENT ON TABLE ai.tb_knowledge_file IS '知识库文件表'; +COMMENT ON COLUMN ai.tb_knowledge_file.optsn IS '流水号'; +COMMENT ON COLUMN ai.tb_knowledge_file.knowledge_id IS '知识库ID'; +COMMENT ON COLUMN ai.tb_knowledge_file.file_root_id IS '文件根ID'; +COMMENT ON COLUMN ai.tb_knowledge_file.file_id IS '文件ID'; +COMMENT ON COLUMN ai.tb_knowledge_file.dify_file_id IS 'dify文件ID'; +COMMENT ON COLUMN ai.tb_knowledge_file.version IS '文件版本'; +COMMENT ON COLUMN ai.tb_knowledge_file.create_time IS '创建时间'; +COMMENT ON COLUMN ai.tb_knowledge_file.update_time IS '更新时间'; +COMMENT ON COLUMN ai.tb_knowledge_file.delete_time IS '删除时间'; +COMMENT ON COLUMN ai.tb_knowledge_file.deleted IS '是否删除'; + +DROP TABLE IF EXISTS ai.tb_knowledge_file_log CASCADE; +CREATE TABLE ai.tb_knowledge_file_log( + optsn VARCHAR(50) NOT NULL, -- 流水号 + log_id VARCHAR(50) NOT NULL, -- 日志ID + knowledge_id VARCHAR(50) NOT NULL, -- 知识库ID + file_root_id VARCHAR(50) NOT NULL, -- 文件根ID + file_id VARCHAR(50) NOT NULL, -- 文件ID + file_name VARCHAR(100) NOT NULL, -- 文件名 + service VARCHAR(50) NOT NULL, -- 所属服务 workcase、bidding + version INTEGER NOT NULL DEFAULT 1, -- 文件版本 + action VARCHAR(50) NOT NULL, -- 操作类型 upload、update、delete + creator VARCHAR(50) NOT NULL, -- 创建者(用户ID) + creator_name VARCHAR(100) NOT NULL, -- 创建者姓名 + create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间 + PRIMARY KEY (optsn), + UNIQUE (knowledge_id, file_id) +); diff --git a/docker/urbanLifeline/postgres/init/sql/createTableBidding.sql b/docker/urbanLifeline/postgres/init/sql/createTableBidding.sql new file mode 100644 index 00000000..4818df60 --- /dev/null +++ b/docker/urbanLifeline/postgres/init/sql/createTableBidding.sql @@ -0,0 +1,264 @@ +-- ============================= +-- 招投标智能体业务模块 +-- 支持:招标文件管理、投标文件生成、评分分析、流程跟踪 +-- ============================= +CREATE SCHEMA IF NOT EXISTS bidding; + +-- 招标项目表 +DROP TABLE IF EXISTS bidding.tb_bidding_project CASCADE; +CREATE TABLE bidding.tb_bidding_project ( + optsn VARCHAR(50) NOT NULL, -- 流水号 + project_id VARCHAR(50) NOT NULL, -- 项目ID + project_no VARCHAR(100) NOT NULL, -- 项目编号 + project_name VARCHAR(500) NOT NULL, -- 项目名称 + project_type VARCHAR(50) NOT NULL, -- 项目类型:public-公开招标/invitation-邀请招标/competitive_negotiation-竞争性谈判 + industry VARCHAR(100), -- 所属行业 + source_platform VARCHAR(100), -- 来源平台(如:政府采购网、企业官网等) + source_url VARCHAR(500), -- 来源URL + publish_date TIMESTAMPTZ, -- 发布日期 + deadline TIMESTAMPTZ, -- 投标截止日期 + opening_date TIMESTAMPTZ, -- 开标日期 + budget_amount DECIMAL(18,2), -- 预算金额 + currency VARCHAR(10) DEFAULT 'CNY', -- 货币单位 + project_status VARCHAR(30) NOT NULL DEFAULT 'collecting', -- 项目状态:collecting-收集中/analyzing-分析中/preparing-准备投标/submitted-已提交/opened-已开标/won-中标/lost-未中标/abandoned-放弃 + winning_status VARCHAR(30), -- 中标状态:pending-待定/won-中标/lost-未中标 + winning_amount DECIMAL(18,2), -- 中标金额 + client_name VARCHAR(255), -- 客户名称 + client_contact VARCHAR(100), -- 客户联系方式 + contact_person VARCHAR(100), -- 联系人 + project_location VARCHAR(500), -- 项目地点 + description TEXT, -- 项目描述 + keywords TEXT[], -- 关键词数组 + metadata JSONB DEFAULT NULL, -- 项目元数据 + dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径 + responsible_user VARCHAR(50), -- 负责人 + team_members VARCHAR(50)[], -- 团队成员数组 + creator VARCHAR(50) DEFAULT NULL, -- 创建者 + updater VARCHAR(50) DEFAULT NULL, -- 更新者 + create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间 + update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间 + delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间 + deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 + PRIMARY KEY (project_id), + UNIQUE (optsn), + UNIQUE (project_no) +); + +CREATE INDEX idx_project_status ON bidding.tb_bidding_project(project_status) WHERE deleted = false; +CREATE INDEX idx_project_deadline ON bidding.tb_bidding_project(deadline) WHERE deleted = false; +CREATE INDEX idx_project_dept ON bidding.tb_bidding_project(dept_path) WHERE deleted = false; +CREATE INDEX idx_project_responsible ON bidding.tb_bidding_project(responsible_user) WHERE deleted = false; + +COMMENT ON TABLE bidding.tb_bidding_project IS '招标项目表'; +COMMENT ON COLUMN bidding.tb_bidding_project.project_status IS '项目状态:collecting/analyzing/preparing/submitted/opened/won/lost/abandoned'; + +-- 招标文件表 +DROP TABLE IF EXISTS bidding.tb_bidding_document CASCADE; +CREATE TABLE bidding.tb_bidding_document ( + optsn VARCHAR(50) NOT NULL, -- 流水号 + doc_id VARCHAR(50) NOT NULL, -- 文档ID + project_id VARCHAR(50) NOT NULL, -- 所属项目ID + doc_type VARCHAR(50) NOT NULL, -- 文档类型:tender-招标文件/technical-技术标/commercial-商务标/clarification-澄清文件/other-其他 + doc_name VARCHAR(500) NOT NULL, -- 文档名称 + file_id VARCHAR(50), -- 关联文件表ID + file_path VARCHAR(500), -- 文件路径 + file_size BIGINT, -- 文件大小 + mime_type VARCHAR(100), -- MIME类型 + version INTEGER DEFAULT 1, -- 版本号 + language VARCHAR(20) DEFAULT 'zh-CN', -- 语言 + page_count INTEGER, -- 页数 + parse_status VARCHAR(30) DEFAULT 'pending', -- 解析状态:pending-待解析/parsing-解析中/completed-已完成/failed-失败 + parse_result JSONB, -- 解析结果(JSON格式:提取的要素、表格、图纸等) + extraction_data JSONB, -- 提取的结构化数据 + ai_analysis TEXT, -- AI分析结果 + upload_date TIMESTAMPTZ DEFAULT now(), -- 上传日期 + dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径 + creator VARCHAR(50) DEFAULT NULL, -- 创建者 + updater VARCHAR(50) DEFAULT NULL, -- 更新者 + create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间 + update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间 + delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间 + deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 + PRIMARY KEY (doc_id), + UNIQUE (optsn), + FOREIGN KEY (project_id) REFERENCES bidding.tb_bidding_project(project_id) +); + +CREATE INDEX idx_doc_project ON bidding.tb_bidding_document(project_id) WHERE deleted = false; +CREATE INDEX idx_doc_type ON bidding.tb_bidding_document(doc_type) WHERE deleted = false; + +COMMENT ON TABLE bidding.tb_bidding_document IS '招标文件表'; +COMMENT ON COLUMN bidding.tb_bidding_document.parse_status IS '解析状态:pending/parsing/completed/failed'; + +-- 招标要素提取表 +DROP TABLE IF EXISTS bidding.tb_bidding_requirement CASCADE; +CREATE TABLE bidding.tb_bidding_requirement ( + optsn VARCHAR(50) NOT NULL, -- 流水号 + req_id VARCHAR(50) NOT NULL, -- 要素ID + project_id VARCHAR(50) NOT NULL, -- 所属项目ID + doc_id VARCHAR(50), -- 来源文档ID + req_category VARCHAR(50) NOT NULL, -- 要素类别:commercial-商务要素/technical-技术参数/veto-否决项/qualification-资质要求/delivery-交付要求/payment-付款条件/scoring-评分标准 + req_name VARCHAR(255) NOT NULL, -- 要素名称 + req_content TEXT NOT NULL, -- 要素内容 + req_value VARCHAR(500), -- 要素值 + is_mandatory BOOLEAN DEFAULT false, -- 是否必填 + is_veto BOOLEAN DEFAULT false, -- 是否为否决项 + priority INTEGER DEFAULT 0, -- 优先级 + extraction_method VARCHAR(30) DEFAULT 'ai', -- 提取方式:ai-AI提取/manual-人工录入 + confidence_score DECIMAL(5,4), -- 置信度分数(0-1) + source_location JSONB, -- 来源位置(页码、段落等) + compliance_status VARCHAR(30), -- 合规状态:compliant-符合/non_compliant-不符合/pending-待确认 + response_content TEXT, -- 响应内容(我方的应答) + notes TEXT, -- 备注 + dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径 + creator VARCHAR(50) DEFAULT NULL, -- 创建者 + updater VARCHAR(50) DEFAULT NULL, -- 更新者 + create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间 + update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间 + delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间 + deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 + PRIMARY KEY (req_id), + UNIQUE (optsn), + FOREIGN KEY (project_id) REFERENCES bidding.tb_bidding_project(project_id) +); + +CREATE INDEX idx_req_project ON bidding.tb_bidding_requirement(project_id) WHERE deleted = false; +CREATE INDEX idx_req_category ON bidding.tb_bidding_requirement(req_category) WHERE deleted = false; +CREATE INDEX idx_req_veto ON bidding.tb_bidding_requirement(is_veto) WHERE deleted = false AND is_veto = true; + +COMMENT ON TABLE bidding.tb_bidding_requirement IS '招标要素提取表'; +COMMENT ON COLUMN bidding.tb_bidding_requirement.req_category IS '要素类别:commercial/technical/veto/qualification/delivery/payment/scoring'; + +-- 投标文件生成表 +DROP TABLE IF EXISTS bidding.tb_bid_response CASCADE; +CREATE TABLE bidding.tb_bid_response ( + optsn VARCHAR(50) NOT NULL, -- 流水号 + response_id VARCHAR(50) NOT NULL, -- 响应文件ID + project_id VARCHAR(50) NOT NULL, -- 所属项目ID + response_type VARCHAR(50) NOT NULL, -- 响应类型:technical-技术标/commercial-商务标/comprehensive-综合标 + doc_name VARCHAR(500) NOT NULL, -- 文档名称 + outline TEXT, -- 文档大纲(JSON格式) + content TEXT, -- 文档内容 + generation_method VARCHAR(30) DEFAULT 'ai', -- 生成方式:ai-AI生成/template-模板生成/manual-人工编写 + template_id VARCHAR(50), -- 使用的模板ID + ai_model VARCHAR(100), -- 使用的AI模型 + generation_status VARCHAR(30) DEFAULT 'draft', -- 生成状态:draft-草稿/reviewing-审核中/approved-已批准/rejected-已拒绝/submitted-已提交 + file_id VARCHAR(50), -- 生成的文件ID + file_path VARCHAR(500), -- 文件路径 + version INTEGER DEFAULT 1, -- 版本号 + parent_version_id VARCHAR(50), -- 父版本ID + review_comments TEXT, -- 审核意见 + dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径 + creator VARCHAR(50) DEFAULT NULL, -- 创建者 + updater VARCHAR(50) DEFAULT NULL, -- 更新者 + create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间 + update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间 + delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间 + deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 + PRIMARY KEY (response_id), + UNIQUE (optsn), + FOREIGN KEY (project_id) REFERENCES bidding.tb_bidding_project(project_id) +); + +CREATE INDEX idx_response_project ON bidding.tb_bid_response(project_id) WHERE deleted = false; +CREATE INDEX idx_response_status ON bidding.tb_bid_response(generation_status) WHERE deleted = false; + +COMMENT ON TABLE bidding.tb_bid_response IS '投标文件生成表'; +COMMENT ON COLUMN bidding.tb_bid_response.generation_status IS '生成状态:draft/reviewing/approved/rejected/submitted'; + +-- 评分规则表 +DROP TABLE IF EXISTS bidding.tb_bidding_scoring_rule CASCADE; +CREATE TABLE bidding.tb_bidding_scoring_rule ( + optsn VARCHAR(50) NOT NULL, -- 流水号 + rule_id VARCHAR(50) NOT NULL, -- 规则ID + project_id VARCHAR(50) NOT NULL, -- 所属项目ID + rule_category VARCHAR(50) NOT NULL, -- 规则类别:technical-技术分/commercial-商务分/price-价格分/credit-信誉分 + rule_name VARCHAR(255) NOT NULL, -- 规则名称 + rule_description TEXT, -- 规则描述 + max_score DECIMAL(10,2) NOT NULL, -- 最高分值 + weight DECIMAL(5,4), -- 权重(0-1) + scoring_method VARCHAR(50), -- 评分方法:fixed-固定分值/range-区间评分/formula-公式计算 + calculation_formula TEXT, -- 计算公式 + evaluation_criteria TEXT, -- 评分标准 + our_score DECIMAL(10,2), -- 我方得分(预估) + score_analysis TEXT, -- 得分分析 + optimization_advice TEXT, -- 优化建议 + dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径 + creator VARCHAR(50) DEFAULT NULL, -- 创建者 + updater VARCHAR(50) DEFAULT NULL, -- 更新者 + create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间 + update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间 + delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间 + deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 + PRIMARY KEY (rule_id), + UNIQUE (optsn), + FOREIGN KEY (project_id) REFERENCES bidding.tb_bidding_project(project_id) +); + +CREATE INDEX idx_rule_project ON bidding.tb_bidding_scoring_rule(project_id) WHERE deleted = false; + +COMMENT ON TABLE bidding.tb_bidding_scoring_rule IS '评分规则表'; + +-- 项目流程节点表 +DROP TABLE IF EXISTS bidding.tb_bidding_process CASCADE; +CREATE TABLE bidding.tb_bidding_process ( + optsn VARCHAR(50) NOT NULL, -- 流水号 + process_id VARCHAR(50) NOT NULL, -- 流程节点ID + project_id VARCHAR(50) NOT NULL, -- 所属项目ID + node_name VARCHAR(255) NOT NULL, -- 节点名称 + node_type VARCHAR(50) NOT NULL, -- 节点类型:collection-文件收集/analysis-需求分析/preparation-文件准备/review-内部审核/submission-投标提交/opening-开标/result-结果通知 + node_order INTEGER NOT NULL, -- 节点顺序 + node_status VARCHAR(30) DEFAULT 'pending', -- 节点状态:pending-待处理/in_progress-进行中/completed-已完成/skipped-已跳过 + planned_start_time TIMESTAMPTZ, -- 计划开始时间 + planned_end_time TIMESTAMPTZ, -- 计划结束时间 + actual_start_time TIMESTAMPTZ, -- 实际开始时间 + actual_end_time TIMESTAMPTZ, -- 实际结束时间 + responsible_user VARCHAR(50), -- 负责人 + participants VARCHAR(50)[], -- 参与人员数组 + notes TEXT, -- 节点备注 + attachments VARCHAR(50)[], -- 附件ID数组 + dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径 + creator VARCHAR(50) DEFAULT NULL, -- 创建者 + updater VARCHAR(50) DEFAULT NULL, -- 更新者 + create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间 + update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间 + delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间 + deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 + PRIMARY KEY (process_id), + UNIQUE (optsn), + FOREIGN KEY (project_id) REFERENCES bidding.tb_bidding_project(project_id) +); + +CREATE INDEX idx_process_project ON bidding.tb_bidding_process(project_id, node_order) WHERE deleted = false; + +COMMENT ON TABLE bidding.tb_bidding_process IS '项目流程节点表'; +COMMENT ON COLUMN bidding.tb_bidding_process.node_type IS '节点类型:collection/analysis/preparation/review/submission/opening/result'; + +-- 投标模板表 +DROP TABLE IF EXISTS bidding.tb_bid_template CASCADE; +CREATE TABLE bidding.tb_bid_template ( + optsn VARCHAR(50) NOT NULL, -- 流水号 + template_id VARCHAR(50) NOT NULL, -- 模板ID + template_name VARCHAR(255) NOT NULL, -- 模板名称 + template_type VARCHAR(50) NOT NULL, -- 模板类型:technical-技术标/commercial-商务标/comprehensive-综合标 + industry VARCHAR(100), -- 适用行业 + template_content TEXT, -- 模板内容 + outline_structure JSONB, -- 大纲结构(JSON格式) + file_id VARCHAR(50), -- 模板文件ID + usage_count INTEGER DEFAULT 0, -- 使用次数 + is_default BOOLEAN DEFAULT false, -- 是否默认模板 + dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径 + status VARCHAR(20) DEFAULT 'active', -- 状态:active-激活/inactive-停用 + creator VARCHAR(50) DEFAULT NULL, -- 创建者 + updater VARCHAR(50) DEFAULT NULL, -- 更新者 + create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间 + update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间 + delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间 + deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 + PRIMARY KEY (template_id), + UNIQUE (optsn) +); + +CREATE INDEX idx_template_type ON bidding.tb_bid_template(template_type) WHERE deleted = false; + +COMMENT ON TABLE bidding.tb_bid_template IS '投标模板表'; diff --git a/docker/urbanLifeline/postgres/init/sql/createTableConfig.sql b/docker/urbanLifeline/postgres/init/sql/createTableConfig.sql new file mode 100644 index 00000000..f3bd68d4 --- /dev/null +++ b/docker/urbanLifeline/postgres/init/sql/createTableConfig.sql @@ -0,0 +1,51 @@ +CREATE SCHEMA IF NOT EXISTS config; +DROP TABLE IF EXISTS config.tb_sys_config CASCADE; +CREATE TABLE config.tb_sys_config ( + optsn VARCHAR(50) NOT NULL, -- 流水号 + config_id VARCHAR(50) NOT NULL, -- 配置ID + key VARCHAR(255) NOT NULL, -- 配置键 + name VARCHAR(255) NOT NULL, -- 配置名称 + value VARCHAR(255) NOT NULL, -- 配置值 + config_type VARCHAR(50) NOT NULL, -- 数据类型(String, INTEGER, BOOLEAN, Float, Double) + render_type VARCHAR(50) NOT NULL, -- 配置渲染类型(select, input, textarea, checkbox, radio, switch) + description VARCHAR(255) NOT NULL, -- 配置描述 + re JSON DEFAULT NULL, -- 正则表达式校验规则 + options JSON DEFAULT NULL, -- 可选项,render_type为select、checkbox、radio时使用 + "group" VARCHAR(255) NOT NULL, -- 配置组 + module_id VARCHAR(255) NOT NULL, -- 模块id + order_num INT NOT NULL, -- 配置顺序 + status INT NOT NULL DEFAULT 0, -- 配置状态 0:启用 1:禁用 + remark VARCHAR(255) NOT NULL, -- 配置备注 + creator VARCHAR(50) DEFAULT NULL, -- 创建者 + dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径,支持like递归(如/1/2/3/) + updater VARCHAR(50) DEFAULT NULL, -- 更新者 + create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 配置创建时间 + update_time TIMESTAMPTZ DEFAULT NULL, -- 配置更新时间 + delete_time TIMESTAMPTZ DEFAULT NULL, -- 配置删除时间 + deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 + PRIMARY KEY (config_id), + UNIQUE (optsn) +); +COMMENT ON TABLE config.tb_sys_config IS '系统配置表'; +COMMENT ON COLUMN config.tb_sys_config.optsn IS '流水号'; +COMMENT ON COLUMN config.tb_sys_config.config_id IS '配置ID'; +COMMENT ON COLUMN config.tb_sys_config.key IS '配置键'; +COMMENT ON COLUMN config.tb_sys_config.name IS '配置名称'; +COMMENT ON COLUMN config.tb_sys_config.value IS '配置值'; +COMMENT ON COLUMN config.tb_sys_config.config_type IS '数据类型'; +COMMENT ON COLUMN config.tb_sys_config.render_type IS '数据渲染类型'; +COMMENT ON COLUMN config.tb_sys_config.description IS '配置描述'; +COMMENT ON COLUMN config.tb_sys_config.re IS '正则表达式校验规则'; +COMMENT ON COLUMN config.tb_sys_config.options IS '可选项'; +COMMENT ON COLUMN config.tb_sys_config.group IS'配置组名称'; +COMMENT ON COLUMN config.tb_sys_config.module_id IS '模块id'; +COMMENT ON COLUMN config.tb_sys_config.order_num IS '配置顺序'; +COMMENT ON COLUMN config.tb_sys_config.status IS '配置状态'; +COMMENT ON COLUMN config.tb_sys_config.remark IS '配置备注'; +COMMENT ON COLUMN config.tb_sys_config.creator IS '创建者'; +COMMENT ON COLUMN config.tb_sys_config.dept_path IS '部门全路径'; +COMMENT ON COLUMN config.tb_sys_config.updater IS '更新者'; +COMMENT ON COLUMN config.tb_sys_config.create_time IS '配置创建时间'; +COMMENT ON COLUMN config.tb_sys_config.update_time IS '配置更新时间'; +COMMENT ON COLUMN config.tb_sys_config.delete_time IS '配置删除时间'; +COMMENT ON COLUMN config.tb_sys_config.deleted IS '是否删除'; \ No newline at end of file diff --git a/docker/urbanLifeline/postgres/init/sql/createTableCrontab.sql b/docker/urbanLifeline/postgres/init/sql/createTableCrontab.sql new file mode 100644 index 00000000..1dfc5e72 --- /dev/null +++ b/docker/urbanLifeline/postgres/init/sql/createTableCrontab.sql @@ -0,0 +1,153 @@ +-- ==================================================== +-- 定时任务表 +-- ==================================================== +CREATE SCHEMA IF NOT EXISTS crontab; +DROP TABLE IF EXISTS crontab.tb_crontab_task CASCADE; +CREATE TABLE crontab.tb_crontab_task ( + id VARCHAR(64) NOT NULL, + task_id VARCHAR(64) NOT NULL, + task_name VARCHAR(100) NOT NULL, + task_group VARCHAR(50) NOT NULL DEFAULT 'DEFAULT', + meta_id VARCHAR(64) NOT NULL, + default_recipient SMALLINT NOT NULL DEFAULT 0, -- 是否使用默认接收人(0:否 1:是) + bean_name VARCHAR(100) NOT NULL, + method_name VARCHAR(100) NOT NULL, + method_params VARCHAR(500) DEFAULT NULL, + cron_expression VARCHAR(100) NOT NULL, + status SMALLINT NOT NULL DEFAULT 0, -- 任务状态(0:暂停 1:运行中) + description VARCHAR(500) DEFAULT NULL, + concurrent SMALLINT NOT NULL DEFAULT 0, -- 是否允许并发执行(0:否 1:是) + misfire_policy SMALLINT NOT NULL DEFAULT 1, -- 错过执行策略(1:立即执行 2:执行一次 3:放弃执行) + creator VARCHAR(64) DEFAULT NULL, + updater VARCHAR(64) DEFAULT NULL, + create_time TIMESTAMPTZ NOT NULL DEFAULT now(), + update_time TIMESTAMPTZ DEFAULT NULL, + delete_time TIMESTAMPTZ DEFAULT NULL, + deleted SMALLINT NOT NULL DEFAULT 0, -- 是否删除(0:否 1:是) + PRIMARY KEY (id) +); + +CREATE INDEX IF NOT EXISTS idx_task_name ON crontab.tb_crontab_task(task_name); +CREATE INDEX IF NOT EXISTS idx_bean_name ON crontab.tb_crontab_task(bean_name); +CREATE INDEX IF NOT EXISTS idx_status ON crontab.tb_crontab_task(status); +CREATE INDEX IF NOT EXISTS idx_deleted ON crontab.tb_crontab_task(deleted); + +COMMENT ON TABLE crontab.tb_crontab_task IS '定时任务配置表'; +COMMENT ON COLUMN crontab.tb_crontab_task.id IS '主键ID'; +COMMENT ON COLUMN crontab.tb_crontab_task.task_id IS '任务ID'; +COMMENT ON COLUMN crontab.tb_crontab_task.task_name IS '任务名称'; +COMMENT ON COLUMN crontab.tb_crontab_task.task_group IS '任务分组'; +COMMENT ON COLUMN crontab.tb_crontab_task.meta_id IS '任务元数据ID'; +COMMENT ON COLUMN crontab.tb_crontab_task.default_recipient IS '是否使用默认接收人(0:否 1:是)'; +COMMENT ON COLUMN crontab.tb_crontab_task.bean_name IS 'Bean名称'; +COMMENT ON COLUMN crontab.tb_crontab_task.method_name IS '方法名称'; +COMMENT ON COLUMN crontab.tb_crontab_task.method_params IS '方法参数'; +COMMENT ON COLUMN crontab.tb_crontab_task.cron_expression IS 'Cron表达式'; +COMMENT ON COLUMN crontab.tb_crontab_task.status IS '任务状态(0:暂停 1:运行中)'; +COMMENT ON COLUMN crontab.tb_crontab_task.description IS '任务描述'; +COMMENT ON COLUMN crontab.tb_crontab_task.concurrent IS '是否允许并发执行(0:否 1:是)'; +COMMENT ON COLUMN crontab.tb_crontab_task.misfire_policy IS '错过执行策略(1:立即执行 2:执行一次 3:放弃执行)'; +COMMENT ON COLUMN crontab.tb_crontab_task.creator IS '创建者'; +COMMENT ON COLUMN crontab.tb_crontab_task.updater IS '更新者'; +COMMENT ON COLUMN crontab.tb_crontab_task.create_time IS '创建时间'; +COMMENT ON COLUMN crontab.tb_crontab_task.update_time IS '更新时间'; +COMMENT ON COLUMN crontab.tb_crontab_task.delete_time IS '删除时间'; +COMMENT ON COLUMN crontab.tb_crontab_task.deleted IS '是否删除(0:否 1:是)'; + +-- ==================================================== +-- 定时任务执行日志表 +-- ==================================================== +DROP TABLE IF EXISTS crontab.tb_crontab_log CASCADE; +CREATE TABLE crontab.tb_crontab_log ( + id VARCHAR(64) NOT NULL, + task_id VARCHAR(64) NOT NULL, + task_name VARCHAR(100) NOT NULL, + task_group VARCHAR(50) NOT NULL DEFAULT 'DEFAULT', + bean_name VARCHAR(100) NOT NULL, + method_name VARCHAR(100) NOT NULL, + method_params VARCHAR(500) DEFAULT NULL, + execute_status SMALLINT NOT NULL, -- 执行状态(0:失败 1:成功) + execute_message TEXT DEFAULT NULL, + exception_info TEXT DEFAULT NULL, + start_time TIMESTAMPTZ NOT NULL, + end_time TIMESTAMPTZ DEFAULT NULL, + execute_duration INT DEFAULT NULL, + create_time TIMESTAMPTZ NOT NULL DEFAULT now(), + update_time TIMESTAMPTZ DEFAULT NULL, + delete_time TIMESTAMPTZ DEFAULT NULL, + deleted SMALLINT NOT NULL DEFAULT 0, -- 是否删除(0:否 1:是) + PRIMARY KEY (id) +); + +CREATE INDEX IF NOT EXISTS idx_task_id ON crontab.tb_crontab_log(task_id); +CREATE INDEX IF NOT EXISTS idx_log_task_name ON crontab.tb_crontab_log(task_name); +CREATE INDEX IF NOT EXISTS idx_execute_status ON crontab.tb_crontab_log(execute_status); +CREATE INDEX IF NOT EXISTS idx_start_time ON crontab.tb_crontab_log(start_time); +CREATE INDEX IF NOT EXISTS idx_log_deleted ON crontab.tb_crontab_log(deleted); + +COMMENT ON TABLE crontab.tb_crontab_log IS '定时任务执行日志表'; +COMMENT ON COLUMN crontab.tb_crontab_log.id IS '主键ID'; +COMMENT ON COLUMN crontab.tb_crontab_log.task_id IS '任务ID'; +COMMENT ON COLUMN crontab.tb_crontab_log.task_name IS '任务名称'; +COMMENT ON COLUMN crontab.tb_crontab_log.task_group IS '任务分组'; +COMMENT ON COLUMN crontab.tb_crontab_log.bean_name IS 'Bean名称'; +COMMENT ON COLUMN crontab.tb_crontab_log.method_name IS '方法名称'; +COMMENT ON COLUMN crontab.tb_crontab_log.method_params IS '方法参数'; +COMMENT ON COLUMN crontab.tb_crontab_log.execute_status IS '执行状态(0:失败 1:成功)'; +COMMENT ON COLUMN crontab.tb_crontab_log.execute_message IS '执行结果信息'; +COMMENT ON COLUMN crontab.tb_crontab_log.exception_info IS '异常信息'; +COMMENT ON COLUMN crontab.tb_crontab_log.start_time IS '开始时间'; +COMMENT ON COLUMN crontab.tb_crontab_log.end_time IS '结束时间'; +COMMENT ON COLUMN crontab.tb_crontab_log.execute_duration IS '执行时长(毫秒)'; +COMMENT ON COLUMN crontab.tb_crontab_log.create_time IS '创建时间'; +COMMENT ON COLUMN crontab.tb_crontab_log.update_time IS '更新时间'; +COMMENT ON COLUMN crontab.tb_crontab_log.delete_time IS '删除时间'; +COMMENT ON COLUMN crontab.tb_crontab_log.deleted IS '是否删除(0:否 1:是)'; + +-- ==================================================== +-- 定时任务元数据表(存储爬虫任务的元数据配置) +-- ==================================================== +DROP TABLE IF EXISTS crontab.tb_crontab_task_meta CASCADE; +CREATE TABLE crontab.tb_crontab_task_meta ( + id VARCHAR(64) NOT NULL, + meta_id VARCHAR(64) NOT NULL, + name VARCHAR(100) NOT NULL, + description VARCHAR(500) DEFAULT NULL, + category VARCHAR(50) NOT NULL, + bean_name VARCHAR(100) NOT NULL, + method_name VARCHAR(100) NOT NULL, + script_path VARCHAR(255) DEFAULT NULL, + param_schema TEXT DEFAULT NULL, + auto_publish SMALLINT NOT NULL DEFAULT 0, -- 是否自动发布 + sort_order INT DEFAULT 0, + creator VARCHAR(64) DEFAULT NULL, + updater VARCHAR(64) DEFAULT NULL, + create_time TIMESTAMPTZ NOT NULL DEFAULT now(), + update_time TIMESTAMPTZ DEFAULT NULL, + delete_time TIMESTAMPTZ DEFAULT NULL, + deleted SMALLINT NOT NULL DEFAULT 0, -- 是否删除(0:否 1:是) + PRIMARY KEY (id), + UNIQUE (meta_id) +); + +CREATE INDEX IF NOT EXISTS idx_category ON crontab.tb_crontab_task_meta(category); +CREATE INDEX IF NOT EXISTS idx_meta_deleted ON crontab.tb_crontab_task_meta(deleted); + +COMMENT ON TABLE crontab.tb_crontab_task_meta IS '定时任务元数据表'; +COMMENT ON COLUMN crontab.tb_crontab_task_meta.id IS '主键ID'; +COMMENT ON COLUMN crontab.tb_crontab_task_meta.meta_id IS '元数据ID'; +COMMENT ON COLUMN crontab.tb_crontab_task_meta.name IS '任务名称'; +COMMENT ON COLUMN crontab.tb_crontab_task_meta.description IS '任务描述'; +COMMENT ON COLUMN crontab.tb_crontab_task_meta.category IS '任务分类(如:人民日报新闻爬取)'; +COMMENT ON COLUMN crontab.tb_crontab_task_meta.bean_name IS 'Bean名称(执行器类名)'; +COMMENT ON COLUMN crontab.tb_crontab_task_meta.method_name IS '执行方法名'; +COMMENT ON COLUMN crontab.tb_crontab_task_meta.script_path IS 'Python脚本路径(相对于basePath)'; +COMMENT ON COLUMN crontab.tb_crontab_task_meta.param_schema IS '参数模板(JSON格式,定义参数名、类型、描述、默认值等)'; +COMMENT ON COLUMN crontab.tb_crontab_task_meta.auto_publish IS '是否自动发布'; +COMMENT ON COLUMN crontab.tb_crontab_task_meta.sort_order IS '排序号'; +COMMENT ON COLUMN crontab.tb_crontab_task_meta.creator IS '创建者'; +COMMENT ON COLUMN crontab.tb_crontab_task_meta.updater IS '更新者'; +COMMENT ON COLUMN crontab.tb_crontab_task_meta.create_time IS '创建时间'; +COMMENT ON COLUMN crontab.tb_crontab_task_meta.update_time IS '更新时间'; +COMMENT ON COLUMN crontab.tb_crontab_task_meta.delete_time IS '删除时间'; +COMMENT ON COLUMN crontab.tb_crontab_task_meta.deleted IS '是否删除(0:否 1:是)'; \ No newline at end of file diff --git a/docker/urbanLifeline/postgres/init/sql/createTableFile.sql b/docker/urbanLifeline/postgres/init/sql/createTableFile.sql new file mode 100644 index 00000000..06b2f7ac --- /dev/null +++ b/docker/urbanLifeline/postgres/init/sql/createTableFile.sql @@ -0,0 +1,79 @@ +CREATE SCHEMA IF NOT EXISTS file; + +DROP TABLE IF EXISTS file.tb_sys_file CASCADE; +CREATE TABLE file.tb_sys_file ( + -- BaseDTO 继承字段 + optsn VARCHAR(50) NOT NULL, -- 操作流水号 + creator VARCHAR(50) DEFAULT NULL, -- 创建人 + updater VARCHAR(50) DEFAULT NULL, -- 更新人 + dept_path VARCHAR(255) DEFAULT NULL, -- 部门路径 + remark TEXT DEFAULT NULL, -- 备注 + create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间 + update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间 + delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间 + deleted BOOLEAN NOT NULL DEFAULT false, -- 是否已删除(false-未删除,true-已删除) + + -- TbSysFileDTO 特有字段 + file_id VARCHAR(50) NOT NULL, -- 文件ID (主键) + file_root_id VARCHAR(50) DEFAULT NULL, -- 文件根ID + version INTEGER DEFAULT 1, -- 文件版本 + name VARCHAR(255) NOT NULL, -- 文件名 + path VARCHAR(500) NOT NULL, -- 文件路径 + size BIGINT NOT NULL, -- 文件大小(字节) + type VARCHAR(50) DEFAULT NULL, -- 文件类型 + storage_type VARCHAR(50) DEFAULT NULL, -- 存储类型 + mime_type VARCHAR(255) DEFAULT NULL, -- MIME 类型 + url VARCHAR(500) DEFAULT NULL, -- 后端下载接口路径(保留用于扩展,建议使用 /api/file/download/{fileId}) + status VARCHAR(50) DEFAULT NULL, -- 文件状态 + module VARCHAR(100) DEFAULT NULL, -- 所属模块 + business_id VARCHAR(50) DEFAULT NULL, -- 业务ID + uploader VARCHAR(50) DEFAULT NULL, -- 上传者用户ID + object_name VARCHAR(500) DEFAULT NULL, -- MinIO对象名称 + bucket_name VARCHAR(100) DEFAULT NULL, -- MinIO存储桶名称 + md5_hash VARCHAR(32) DEFAULT NULL, -- 文件MD5值 + extension VARCHAR(20) DEFAULT NULL, -- 文件扩展名 + + PRIMARY KEY (file_id), + UNIQUE (optsn) +); + +COMMENT ON TABLE file.tb_sys_file IS '系统文件表'; + +-- BaseDTO 继承字段注释 +COMMENT ON COLUMN file.tb_sys_file.optsn IS '操作流水号'; +COMMENT ON COLUMN file.tb_sys_file.creator IS '创建人'; +COMMENT ON COLUMN file.tb_sys_file.updater IS '更新人'; +COMMENT ON COLUMN file.tb_sys_file.dept_path IS '部门路径'; +COMMENT ON COLUMN file.tb_sys_file.remark IS '备注'; +COMMENT ON COLUMN file.tb_sys_file.create_time IS '创建时间'; +COMMENT ON COLUMN file.tb_sys_file.update_time IS '更新时间'; +COMMENT ON COLUMN file.tb_sys_file.delete_time IS '删除时间'; +COMMENT ON COLUMN file.tb_sys_file.deleted IS '是否已删除(false-未删除,true-已删除)'; + +-- TbSysFileDTO 特有字段注释 +COMMENT ON COLUMN file.tb_sys_file.file_id IS '文件ID (主键)'; +COMMENT ON COLUMN file.tb_sys_file.file_root_id IS '文件根ID'; +COMMENT ON COLUMN file.tb_sys_file.version IS '文件版本'; +COMMENT ON COLUMN file.tb_sys_file.name IS '文件名'; +COMMENT ON COLUMN file.tb_sys_file.path IS '文件路径'; +COMMENT ON COLUMN file.tb_sys_file.size IS '文件大小(字节)'; +COMMENT ON COLUMN file.tb_sys_file.type IS '文件类型'; +COMMENT ON COLUMN file.tb_sys_file.storage_type IS '存储类型'; +COMMENT ON COLUMN file.tb_sys_file.mime_type IS 'MIME 类型'; +COMMENT ON COLUMN file.tb_sys_file.url IS '后端下载接口路径(保留用于扩展,建议使用 /api/file/download/{fileId})'; +COMMENT ON COLUMN file.tb_sys_file.status IS '文件状态'; +COMMENT ON COLUMN file.tb_sys_file.module IS '所属模块'; +COMMENT ON COLUMN file.tb_sys_file.business_id IS '业务ID'; +COMMENT ON COLUMN file.tb_sys_file.uploader IS '上传者用户ID'; +COMMENT ON COLUMN file.tb_sys_file.object_name IS 'MinIO对象名称'; +COMMENT ON COLUMN file.tb_sys_file.bucket_name IS 'MinIO存储桶名称'; +COMMENT ON COLUMN file.tb_sys_file.md5_hash IS '文件MD5值'; +COMMENT ON COLUMN file.tb_sys_file.extension IS '文件扩展名'; + +-- 文件表索引 +-- CREATE INDEX idx_file_module_business ON file.tb_sys_file(module, business_id) WHERE deleted = false; +-- CREATE INDEX idx_file_uploader ON file.tb_sys_file(uploader) WHERE deleted = false; +-- CREATE INDEX idx_file_bucket ON file.tb_sys_file(bucket_name) WHERE deleted = false; +-- CREATE INDEX idx_file_status ON file.tb_sys_file(status) WHERE deleted = false; +-- CREATE INDEX idx_file_create_time ON file.tb_sys_file(create_time) WHERE deleted = false; +-- CREATE INDEX idx_file_md5 ON file.tb_sys_file(md5_hash) WHERE deleted = false; \ No newline at end of file diff --git a/docker/urbanLifeline/postgres/init/sql/createTableLog.sql b/docker/urbanLifeline/postgres/init/sql/createTableLog.sql new file mode 100644 index 00000000..34f64652 --- /dev/null +++ b/docker/urbanLifeline/postgres/init/sql/createTableLog.sql @@ -0,0 +1,42 @@ +DROP TABLE IF EXISTS sys.tb_sys_log CASCADE; +CREATE TABLE sys.tb_sys_log ( + optsn VARCHAR(50) NOT NULL, -- 流水号 + log_id VARCHAR(50) NOT NULL, -- 日志ID + type VARCHAR(50) NOT NULL, -- 日志类型 + level VARCHAR(50) NOT NULL, -- 日志级别 + module VARCHAR(50) NOT NULL, -- 日志模块 + ip_address VARCHAR(45), -- IP地址 + ip_source VARCHAR(100), -- IP来源 + browser VARCHAR(100), -- 浏览器 + os VARCHAR(100), -- 操作系统 + message VARCHAR(255) NOT NULL, -- 日志消息 + data JSONB DEFAULT NULL, -- 日志数据 + creator VARCHAR(50) DEFAULT NULL, -- 创建者 + creator_name VARCHAR(200) DEFAULT NULL, -- 创建者姓名 + service VARCHAR(50) NOT NULL, -- 服务类型 + dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径 + updater VARCHAR(50) DEFAULT NULL, -- 更新者 + create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 日志创建时间 + update_time TIMESTAMPTZ DEFAULT NULL, -- 日志更新时间 + delete_time TIMESTAMPTZ DEFAULT NULL, -- 日志删除时间 + deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 + PRIMARY KEY (log_id), + UNIQUE (optsn) +); +COMMENT ON TABLE sys.tb_sys_log IS '系统日志表'; +COMMENT ON COLUMN sys.tb_sys_log.optsn IS '流水号'; +COMMENT ON COLUMN sys.tb_sys_log.log_id IS '日志ID'; +COMMENT ON COLUMN sys.tb_sys_log.type IS '日志类型'; +COMMENT ON COLUMN sys.tb_sys_log.level IS '日志级别'; +COMMENT ON COLUMN sys.tb_sys_log.module IS '日志模块'; +COMMENT ON COLUMN sys.tb_sys_log.message IS '日志消息'; +COMMENT ON COLUMN sys.tb_sys_log.data IS '日志数据'; +COMMENT ON COLUMN sys.tb_sys_log.creator IS '创建者'; +COMMENT ON COLUMN sys.tb_sys_log.creator_name IS '创建者姓名'; +COMMENT ON COLUMN sys.tb_sys_log.service IS '服务类型'; +COMMENT ON COLUMN sys.tb_sys_log.dept_path IS '部门全路径'; +COMMENT ON COLUMN sys.tb_sys_log.updater IS '更新者'; +COMMENT ON COLUMN sys.tb_sys_log.create_time IS '日志创建时间'; +COMMENT ON COLUMN sys.tb_sys_log.update_time IS '日志更新时间'; +COMMENT ON COLUMN sys.tb_sys_log.delete_time IS '日志删除时间'; +COMMENT ON COLUMN sys.tb_sys_log.deleted IS '是否删除'; \ No newline at end of file diff --git a/docker/urbanLifeline/postgres/init/sql/createTableMessage.sql b/docker/urbanLifeline/postgres/init/sql/createTableMessage.sql new file mode 100644 index 00000000..43aad8ba --- /dev/null +++ b/docker/urbanLifeline/postgres/init/sql/createTableMessage.sql @@ -0,0 +1,200 @@ +CREATE SCHEMA IF NOT EXISTS message; + +DROP TABLE IF EXISTS message.tb_message CASCADE; +CREATE TABLE message.tb_message ( + optsn VARCHAR(50) NOT NULL, -- 流水号 + message_id VARCHAR(50) NOT NULL, -- 消息ID + title VARCHAR(255) NOT NULL, -- 消息标题 + content VARCHAR(255) NOT NULL, -- 消息内容 + type VARCHAR(50) NOT NULL, -- 消息类型 + status VARCHAR(50) NOT NULL, -- 消息状态 + service VARCHAR(50) NOT NULL, -- 服务类型 + dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径(隔离) + creator VARCHAR(50) NOT NULL DEFAULT 'system',-- 创建者 + updater VARCHAR(50) DEFAULT NULL, -- 更新者 + create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间 + update_time TIMESTAMPTZ DEFAULT NULL, + delete_time TIMESTAMPTZ DEFAULT NULL, + deleted BOOLEAN NOT NULL DEFAULT false, + PRIMARY KEY (message_id), + UNIQUE (optsn) +); + +COMMENT ON TABLE message.tb_message IS '消息表'; +COMMENT ON COLUMN message.tb_message.optsn IS '流水号'; +COMMENT ON COLUMN message.tb_message.message_id IS '消息ID'; +COMMENT ON COLUMN message.tb_message.title IS '消息标题'; +COMMENT ON COLUMN message.tb_message.content IS '消息内容'; +COMMENT ON COLUMN message.tb_message.type IS '消息类型'; +COMMENT ON COLUMN message.tb_message.status IS '消息状态'; +COMMENT ON COLUMN message.tb_message.service IS '服务类型'; +COMMENT ON COLUMN message.tb_message.dept_path IS '部门全路径'; +COMMENT ON COLUMN message.tb_message.creator IS '创建者'; +COMMENT ON COLUMN message.tb_message.updater IS '更新者'; +COMMENT ON COLUMN message.tb_message.create_time IS '创建时间'; +COMMENT ON COLUMN message.tb_message.update_time IS '更新时间'; +COMMENT ON COLUMN message.tb_message.delete_time IS '删除时间'; +COMMENT ON COLUMN message.tb_message.deleted IS '是否删除'; + + +-- 消息发送范围定义表(定义消息要发送给哪些对象,通过什么渠道) +DROP TABLE IF EXISTS message.tb_message_range CASCADE; +CREATE TABLE message.tb_message_range ( + optsn VARCHAR(50) NOT NULL, -- 流水号 + message_id VARCHAR(50) NOT NULL, -- 消息ID + target_type VARCHAR(20) NOT NULL, -- 目标类型:user/dept/role/all + target_id VARCHAR(50) DEFAULT NULL, -- 目标ID(用户、部门、角色ID等,all类型时为空) + channel VARCHAR(20) NOT NULL DEFAULT 'app', -- 发送渠道:app/sms/email/wechat_official_account/wechat_applet等 + dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径,支持like递归(如/1/2/3/) + creator VARCHAR(50) NOT NULL DEFAULT 'system',-- 创建者 + updater VARCHAR(50) DEFAULT NULL, -- 更新者 + create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间 + update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间 + delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间 + deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 + PRIMARY KEY (optsn), + UNIQUE (message_id, target_type, target_id, channel) +); + +COMMENT ON TABLE message.tb_message_range IS '消息发送范围定义表'; +COMMENT ON COLUMN message.tb_message_range.optsn IS '流水号'; +COMMENT ON COLUMN message.tb_message_range.message_id IS '消息ID'; +COMMENT ON COLUMN message.tb_message_range.target_type IS '目标类型:user-指定用户/dept-部门/role-角色/all-全员'; +COMMENT ON COLUMN message.tb_message_range.target_id IS '目标ID(用户、部门、角色ID等,all类型时为空)'; +COMMENT ON COLUMN message.tb_message_range.channel IS '发送渠道:app/sms/email/wechat_official_account/wechat_applet等'; +COMMENT ON COLUMN message.tb_message_range.dept_path IS '部门全路径'; +COMMENT ON COLUMN message.tb_message_range.creator IS '创建者'; +COMMENT ON COLUMN message.tb_message_range.updater IS '更新者'; +COMMENT ON COLUMN message.tb_message_range.create_time IS '创建时间'; +COMMENT ON COLUMN message.tb_message_range.update_time IS '更新时间'; +COMMENT ON COLUMN message.tb_message_range.delete_time IS '删除时间'; +COMMENT ON COLUMN message.tb_message_range.deleted IS '是否删除'; + + +-- 用户消息接收记录表(记录每个用户实际收到的消息及处理状态) +DROP TABLE IF EXISTS message.tb_message_receiver CASCADE; +CREATE TABLE message.tb_message_receiver ( + optsn VARCHAR(50) NOT NULL, -- 流水号 + message_id VARCHAR(50) NOT NULL, -- 消息ID + user_id VARCHAR(50) NOT NULL, -- 用户ID + channel VARCHAR(20) DEFAULT 'app', -- 接收渠道:app/sms/email/wechat等 + status VARCHAR(20) NOT NULL DEFAULT 'unread', -- 消息状态:unread-未读/read-已读/handled-已处理/deleted-已删除 + read_time TIMESTAMPTZ DEFAULT NULL, -- 阅读时间 + handle_time TIMESTAMPTZ DEFAULT NULL, -- 处理时间 + dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径(数据隔离) + creator VARCHAR(50) NOT NULL DEFAULT 'system',-- 创建者 + updater VARCHAR(50) DEFAULT NULL, -- 更新者 + create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间(接收时间) + update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间 + delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间 + deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 + PRIMARY KEY (optsn), + UNIQUE (message_id, user_id, channel) +); + +-- 创建索引以提高查询效率 +CREATE INDEX idx_message_user_user_status ON message.tb_message_receiver(user_id, status, create_time DESC) WHERE deleted = false; +CREATE INDEX idx_message_user_message ON message.tb_message_receiver(message_id) WHERE deleted = false; + +COMMENT ON TABLE message.tb_message_receiver IS '用户消息接收记录表'; +COMMENT ON COLUMN message.tb_message_receiver.optsn IS '流水号'; +COMMENT ON COLUMN message.tb_message_receiver.message_id IS '消息ID'; +COMMENT ON COLUMN message.tb_message_receiver.user_id IS '用户ID'; +COMMENT ON COLUMN message.tb_message_receiver.channel IS '接收渠道:app/sms/email/wechat等'; +COMMENT ON COLUMN message.tb_message_receiver.status IS '消息状态:unread-未读/read-已读/handled-已处理/deleted-已删除'; +COMMENT ON COLUMN message.tb_message_receiver.read_time IS '阅读时间'; +COMMENT ON COLUMN message.tb_message_receiver.handle_time IS '处理时间'; +COMMENT ON COLUMN message.tb_message_receiver.dept_path IS '部门全路径'; +COMMENT ON COLUMN message.tb_message_receiver.creator IS '创建者'; +COMMENT ON COLUMN message.tb_message_receiver.updater IS '更新者'; +COMMENT ON COLUMN message.tb_message_receiver.create_time IS '创建时间(接收时间)'; +COMMENT ON COLUMN message.tb_message_receiver.update_time IS '更新时间'; +COMMENT ON COLUMN message.tb_message_receiver.delete_time IS '删除时间'; +COMMENT ON COLUMN message.tb_message_receiver.deleted IS '是否删除'; + + +-- 消息渠道配置表(管理各种消息发送渠道的配置) +DROP TABLE IF EXISTS message.tb_message_channel CASCADE; +CREATE TABLE message.tb_message_channel ( + optsn VARCHAR(50) NOT NULL, -- 流水号 + channel_id VARCHAR(50) NOT NULL, -- 渠道ID + channel_code VARCHAR(50) NOT NULL, -- 渠道编码:app/sms/email/wechat/dingtalk等 + channel_name VARCHAR(100) NOT NULL, -- 渠道名称 + channel_desc VARCHAR(255) DEFAULT NULL, -- 渠道描述 + config JSON DEFAULT NULL, -- 渠道配置(如API密钥、服务器地址等) + status VARCHAR(20) NOT NULL DEFAULT 'enabled', -- 渠道状态:enabled-启用/disabled-禁用/maintenance-维护中 + priority INTEGER DEFAULT 0, -- 优先级(数字越大优先级越高) + dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径(数据隔离) + creator VARCHAR(50) NOT NULL DEFAULT 'system',-- 创建者 + updater VARCHAR(50) DEFAULT NULL, -- 更新者 + create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间 + update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间 + delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间 + deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 + PRIMARY KEY (channel_id), + UNIQUE (optsn), + UNIQUE (channel_code) +); + +COMMENT ON TABLE message.tb_message_channel IS '消息渠道配置表'; +COMMENT ON COLUMN message.tb_message_channel.optsn IS '流水号'; +COMMENT ON COLUMN message.tb_message_channel.channel_id IS '渠道ID'; +COMMENT ON COLUMN message.tb_message_channel.channel_code IS '渠道编码:app/sms/email/wechat/dingtalk等'; +COMMENT ON COLUMN message.tb_message_channel.channel_name IS '渠道名称'; +COMMENT ON COLUMN message.tb_message_channel.channel_desc IS '渠道描述'; +COMMENT ON COLUMN message.tb_message_channel.config IS '渠道配置(JSON格式)'; +COMMENT ON COLUMN message.tb_message_channel.status IS '渠道状态:enabled-启用/disabled-禁用/maintenance-维护中'; +COMMENT ON COLUMN message.tb_message_channel.priority IS '优先级(数字越大优先级越高)'; +COMMENT ON COLUMN message.tb_message_channel.dept_path IS '部门全路径'; +COMMENT ON COLUMN message.tb_message_channel.creator IS '创建者'; +COMMENT ON COLUMN message.tb_message_channel.updater IS '更新者'; +COMMENT ON COLUMN message.tb_message_channel.create_time IS '创建时间'; +COMMENT ON COLUMN message.tb_message_channel.update_time IS '更新时间'; +COMMENT ON COLUMN message.tb_message_channel.delete_time IS '删除时间'; +COMMENT ON COLUMN message.tb_message_channel.deleted IS '是否删除'; + +-- ============================= +-- 消息模板表 +-- ============================= +DROP TABLE IF EXISTS message.tb_message_template CASCADE; +CREATE TABLE message.tb_message_template ( + optsn VARCHAR(50) NOT NULL, + template_id VARCHAR(50) NOT NULL, + template_code VARCHAR(100) NOT NULL, -- 模板编码 + template_name VARCHAR(255) NOT NULL, -- 模板名称 + template_type VARCHAR(30) NOT NULL, -- 模板类型:system-系统/business-业务 + title_template TEXT, -- 标题模板(支持变量) + content_template TEXT NOT NULL, -- 内容模板(支持变量) + variables JSONB, -- 模板变量定义 + service VARCHAR(50) NOT NULL, -- 服务类型 + dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径 + creator VARCHAR(50) DEFAULT NULL, -- 创建者 + updater VARCHAR(50) DEFAULT NULL, -- 更新者 + create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间 + update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间 + delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间 + deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 + PRIMARY KEY (template_id), + UNIQUE (optsn), + UNIQUE (template_code) +); + +COMMENT ON TABLE message.tb_message_template IS '消息模板表'; +COMMENT ON COLUMN message.tb_message_template.optsn IS '流水号'; +COMMENT ON COLUMN message.tb_message_template.template_id IS '模板ID'; +COMMENT ON COLUMN message.tb_message_template.template_code IS '模板编码'; +COMMENT ON COLUMN message.tb_message_template.template_name IS '模板名称'; +COMMENT ON COLUMN message.tb_message_template.template_type IS '模板类型:system-系统/business-业务'; +COMMENT ON COLUMN message.tb_message_template.title_template IS '标题模板(支持变量)'; +COMMENT ON COLUMN message.tb_message_template.content_template IS '内容模板(支持变量)'; +COMMENT ON COLUMN message.tb_message_template.variables IS '模板变量定义'; +COMMENT ON COLUMN message.tb_message_template.service IS '服务类型'; +COMMENT ON COLUMN message.tb_message_template.dept_path IS '部门全路径'; +COMMENT ON COLUMN message.tb_message_template.creator IS '创建者'; +COMMENT ON COLUMN message.tb_message_template.updater IS '更新者'; +COMMENT ON COLUMN message.tb_message_template.create_time IS '创建时间'; +COMMENT ON COLUMN message.tb_message_template.update_time IS '更新时间'; +COMMENT ON COLUMN message.tb_message_template.delete_time IS '删除时间'; +COMMENT ON COLUMN message.tb_message_template.deleted IS '是否删除'; + +CREATE INDEX idx_template_type ON message.tb_message_template(template_type) WHERE deleted = false; diff --git a/docker/urbanLifeline/postgres/init/sql/createTablePermission.sql b/docker/urbanLifeline/postgres/init/sql/createTablePermission.sql new file mode 100644 index 00000000..dcf5082a --- /dev/null +++ b/docker/urbanLifeline/postgres/init/sql/createTablePermission.sql @@ -0,0 +1,473 @@ +CREATE SCHEMA IF NOT EXISTS sys; + +-- 通用更新时间触发函数(用于模拟 MySQL 的 ON UPDATE CURRENT_TIMESTAMP) +-- CREATE OR REPLACE FUNCTION sys.set_update_time() +-- RETURNS trigger AS $$ +-- BEGIN +-- NEW.update_time := CURRENT_TIMESTAMP; +-- RETURN NEW; +-- END; +-- $$ LANGUAGE plpgsql; + +-- 部门表 +DROP TABLE IF EXISTS sys.tb_sys_dept CASCADE; +CREATE TABLE sys.tb_sys_dept ( + optsn VARCHAR(50) NOT NULL, -- 流水号 + dept_id VARCHAR(50) NOT NULL, -- 部门ID + name VARCHAR(100) NOT NULL, -- 部门名称 + parent_id VARCHAR(50) DEFAULT NULL, -- 父部门ID + dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径 + description VARCHAR(255) DEFAULT NULL, -- 部门描述 + creator VARCHAR(50) DEFAULT NULL, -- 创建者 + updater VARCHAR(50) DEFAULT NULL, -- 更新者 + create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间 + update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间(由触发器维护) + delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间 + deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 + PRIMARY KEY (dept_id), + UNIQUE (optsn) +); +-- 创建索引 +CREATE INDEX idx_sys_dept_parent ON sys.tb_sys_dept USING btree (parent_id); +COMMENT ON TABLE sys.tb_sys_dept IS '部门表'; +COMMENT ON COLUMN sys.tb_sys_dept.optsn IS '流水号'; +COMMENT ON COLUMN sys.tb_sys_dept.dept_id IS '部门ID'; +COMMENT ON COLUMN sys.tb_sys_dept.name IS '部门名称'; +COMMENT ON COLUMN sys.tb_sys_dept.parent_id IS '父部门ID'; +COMMENT ON COLUMN sys.tb_sys_dept.dept_path IS '部门全路径'; +COMMENT ON COLUMN sys.tb_sys_dept.description IS '部门描述'; +COMMENT ON COLUMN sys.tb_sys_dept.creator IS '创建者'; +COMMENT ON COLUMN sys.tb_sys_dept.updater IS '更新者'; +COMMENT ON COLUMN sys.tb_sys_dept.create_time IS '创建时间'; +COMMENT ON COLUMN sys.tb_sys_dept.update_time IS '更新时间'; +COMMENT ON COLUMN sys.tb_sys_dept.delete_time IS '删除时间'; +COMMENT ON COLUMN sys.tb_sys_dept.deleted IS '是否删除'; + +-- 角色表 +DROP TABLE IF EXISTS sys.tb_sys_role CASCADE; +CREATE TABLE sys.tb_sys_role ( + optsn VARCHAR(50) NOT NULL, -- 流水号 + role_id VARCHAR(50) NOT NULL, -- 角色ID + name VARCHAR(100) NOT NULL, + description VARCHAR(200) DEFAULT NULL, -- 角色名称 + scope VARCHAR(20) NOT NULL DEFAULT 'dept', -- 角色作用域:global/dept + owner_dept_id VARCHAR(50) DEFAULT NULL, -- 当scope=dept时,所属部门ID + creator VARCHAR(50) DEFAULT NULL, -- 创建者 + dept_path VARCHAR(255) DEFAULT NULL, + status BOOLEAN NOT NULL DEFAULT false, -- 部门全路径 + updater VARCHAR(50) DEFAULT NULL, -- 更新者 + create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间 + update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间(由触发器维护) + delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间 + deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 + PRIMARY KEY (role_id), + UNIQUE (optsn) +); +COMMENT ON TABLE sys.tb_sys_role IS '角色表'; +COMMENT ON COLUMN sys.tb_sys_role.optsn IS '流水号'; +COMMENT ON COLUMN sys.tb_sys_role.role_id IS '角色ID'; +COMMENT ON COLUMN sys.tb_sys_role.name IS '角色名称'; +COMMENT ON COLUMN sys.tb_sys_role.description IS '角色名称'; +COMMENT ON COLUMN sys.tb_sys_role.scope IS '角色作用域:global=通用,dept=部门私有'; +COMMENT ON COLUMN sys.tb_sys_role.owner_dept_id IS '部门私有角色的所属部门ID(scope=dept 时必填)'; +COMMENT ON COLUMN sys.tb_sys_role.status IS '角色状态'; +COMMENT ON COLUMN sys.tb_sys_role.creator IS '创建者'; +COMMENT ON COLUMN sys.tb_sys_role.dept_path IS '部门全路径'; +COMMENT ON COLUMN sys.tb_sys_role.updater IS '更新者'; +COMMENT ON COLUMN sys.tb_sys_role.create_time IS '创建时间'; +COMMENT ON COLUMN sys.tb_sys_role.update_time IS '更新时间'; +COMMENT ON COLUMN sys.tb_sys_role.delete_time IS '删除时间'; +COMMENT ON COLUMN sys.tb_sys_role.deleted IS '是否删除'; + +-- 唯一性: +-- 全局角色:name 在未删除且 scope=global 下唯一 +-- 部门私有角色:同一部门下 name 唯一(scope=dept) +CREATE UNIQUE INDEX IF NOT EXISTS uq_sys_role_global_name + ON sys.tb_sys_role USING btree (name) + WHERE deleted = false AND scope = 'global'; +CREATE UNIQUE INDEX IF NOT EXISTS uq_sys_role_dept_name + ON sys.tb_sys_role USING btree (owner_dept_id, name) + WHERE deleted = false AND scope = 'dept'; +CREATE INDEX IF NOT EXISTS idx_sys_role_owner_dept ON sys.tb_sys_role USING btree (owner_dept_id) WHERE deleted = false; + +-- 部门角色关联表 +DROP TABLE IF EXISTS sys.tb_sys_dept_role CASCADE; +CREATE TABLE sys.tb_sys_dept_role ( + optsn VARCHAR(50) NOT NULL, -- 流水号 + dept_id VARCHAR(50) NOT NULL, -- 部门ID + role_id VARCHAR(50) NOT NULL, -- 角色ID + dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径 + creator VARCHAR(50) DEFAULT NULL, -- 创建者 + updater VARCHAR(50) DEFAULT NULL, -- 更新者 + create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间 + update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间(由触发器维护) + delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间 + deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 + PRIMARY KEY (dept_id, role_id), + UNIQUE (optsn) +); +COMMENT ON TABLE sys.tb_sys_dept_role IS '部门角色关联表'; +COMMENT ON COLUMN sys.tb_sys_dept_role.optsn IS '流水号'; +COMMENT ON COLUMN sys.tb_sys_dept_role.dept_id IS '部门ID'; +COMMENT ON COLUMN sys.tb_sys_dept_role.role_id IS '角色ID'; +COMMENT ON COLUMN sys.tb_sys_dept_role.dept_path IS '部门全路径'; +COMMENT ON COLUMN sys.tb_sys_dept_role.creator IS '创建者'; +COMMENT ON COLUMN sys.tb_sys_dept_role.updater IS '更新者'; +COMMENT ON COLUMN sys.tb_sys_dept_role.create_time IS '创建时间'; +COMMENT ON COLUMN sys.tb_sys_dept_role.update_time IS '更新时间'; +COMMENT ON COLUMN sys.tb_sys_dept_role.delete_time IS '删除时间'; +COMMENT ON COLUMN sys.tb_sys_dept_role.deleted IS '是否删除'; + +-- 用户角色关联表 +DROP TABLE IF EXISTS sys.tb_sys_user_role CASCADE; +CREATE TABLE sys.tb_sys_user_role ( + optsn VARCHAR(50) NOT NULL, -- 流水号 + user_id VARCHAR(50) NOT NULL, -- 用户ID + role_id VARCHAR(50) NOT NULL, -- 角色ID + dept_id VARCHAR(50) NOT NULL, -- 部门ID + dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径 + creator VARCHAR(50) DEFAULT NULL, -- 创建者 + updater VARCHAR(50) DEFAULT NULL, -- 更新者 + create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间 + update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间(由触发器维护) + delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间 + deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 + PRIMARY KEY (user_id, role_id), + UNIQUE (optsn) +); +COMMENT ON TABLE sys.tb_sys_user_role IS '用户角色关联表'; +COMMENT ON COLUMN sys.tb_sys_user_role.optsn IS '流水号'; +COMMENT ON COLUMN sys.tb_sys_user_role.user_id IS '用户ID'; +COMMENT ON COLUMN sys.tb_sys_user_role.role_id IS '角色ID'; +COMMENT ON COLUMN sys.tb_sys_user_role.dept_id IS '部门ID'; +COMMENT ON COLUMN sys.tb_sys_user_role.dept_path IS '部门全路径'; +COMMENT ON COLUMN sys.tb_sys_user_role.creator IS '创建者'; +COMMENT ON COLUMN sys.tb_sys_user_role.updater IS '更新者'; +COMMENT ON COLUMN sys.tb_sys_user_role.create_time IS '创建时间'; +COMMENT ON COLUMN sys.tb_sys_user_role.update_time IS '更新时间'; +COMMENT ON COLUMN sys.tb_sys_user_role.delete_time IS '删除时间'; +COMMENT ON COLUMN sys.tb_sys_user_role.deleted IS '是否删除'; + +-- 视图表 +DROP TABLE IF EXISTS sys.tb_sys_view CASCADE; +CREATE TABLE sys.tb_sys_view ( + optsn VARCHAR(50) NOT NULL, -- 流水号 + view_id VARCHAR(50) NOT NULL, -- 视图ID + name VARCHAR(100) NOT NULL, -- 视图名称 + parent_id VARCHAR(50) DEFAULT NULL, -- 父视图ID + url VARCHAR(255) DEFAULT NULL, -- 视图URL + component VARCHAR(255) DEFAULT NULL, -- 视图组件 + icon VARCHAR(100) DEFAULT NULL, -- 视图图标 + type INTEGER DEFAULT 0, -- 视图类型:0=导航栏 1=侧边栏 2=按钮,3空白页 + view_type VARCHAR(20) DEFAULT 'route', -- 页面类型:route=路由页面 iframe=嵌入页面 + iframe_url VARCHAR(500) DEFAULT NULL, -- iframe URL(仅当view_type=iframe时有效) + service VARCHAR(20) DEFAULT 'platform', -- 所属服务:platform=平台应用 bidding=招标应用 workcase=客服应用 + layout VARCHAR(100) DEFAULT NULL, -- 布局组件路径名称 + order_num INTEGER DEFAULT 0, -- 视图排序号 + dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径 + description VARCHAR(255) DEFAULT NULL, -- 视图描述 + creator VARCHAR(50) DEFAULT NULL, -- 创建者 + updater VARCHAR(50) DEFAULT NULL, -- 更新者 + create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间 + update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间(由触发器维护) + delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间 + deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 + PRIMARY KEY (view_id), + UNIQUE (optsn) +); +-- 创建索引 +CREATE INDEX idx_sys_view_parent ON sys.tb_sys_view USING btree (parent_id) WHERE deleted = false; +CREATE INDEX idx_sys_view_service ON sys.tb_sys_view USING btree (service) WHERE deleted = false; +CREATE INDEX idx_sys_view_type ON sys.tb_sys_view USING btree (view_type) WHERE deleted = false; +COMMENT ON TABLE sys.tb_sys_view IS '视图表(菜单表)'; +COMMENT ON COLUMN sys.tb_sys_view.optsn IS '流水号'; +COMMENT ON COLUMN sys.tb_sys_view.view_id IS '视图ID'; +COMMENT ON COLUMN sys.tb_sys_view.name IS '视图名称'; +COMMENT ON COLUMN sys.tb_sys_view.parent_id IS '父视图ID'; +COMMENT ON COLUMN sys.tb_sys_view.url IS '视图URL'; +COMMENT ON COLUMN sys.tb_sys_view.component IS '视图组件'; +COMMENT ON COLUMN sys.tb_sys_view.icon IS '视图图标'; +COMMENT ON COLUMN sys.tb_sys_view.type IS '视图类型:0=目录 1=菜单页面 2=按钮'; +COMMENT ON COLUMN sys.tb_sys_view.view_type IS '页面类型:route=路由页面 iframe=嵌入页面'; +COMMENT ON COLUMN sys.tb_sys_view.iframe_url IS 'iframe URL(仅当view_type=iframe时有效)'; +COMMENT ON COLUMN sys.tb_sys_view.service IS '所属服务:platform=平台应用 bidding=招标应用 workcase=客服应用'; +COMMENT ON COLUMN sys.tb_sys_view.layout IS '布局组件路径名称'; +COMMENT ON COLUMN sys.tb_sys_view.order_num IS '视图排序号'; +COMMENT ON COLUMN sys.tb_sys_view.description IS '视图描述'; +COMMENT ON COLUMN sys.tb_sys_view.dept_path IS '部门全路径'; +COMMENT ON COLUMN sys.tb_sys_view.creator IS '创建者'; +COMMENT ON COLUMN sys.tb_sys_view.updater IS '更新者'; +COMMENT ON COLUMN sys.tb_sys_view.create_time IS '创建时间'; +COMMENT ON COLUMN sys.tb_sys_view.update_time IS '更新时间'; +COMMENT ON COLUMN sys.tb_sys_view.delete_time IS '删除时间'; +COMMENT ON COLUMN sys.tb_sys_view.deleted IS '是否删除'; + + +-- 模块表 +DROP TABLE IF EXISTS sys.tb_sys_module CASCADE; +CREATE TABLE sys.tb_sys_module ( + optsn VARCHAR(50) NOT NULL, -- 流水号 + module_id VARCHAR(50) NOT NULL, -- 模块ID + name VARCHAR(100) NOT NULL, -- 模块名称 + description VARCHAR(255) DEFAULT NULL, -- 模块描述 + creator VARCHAR(50) DEFAULT NULL, -- 创建者 + dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径 + updater VARCHAR(50) DEFAULT NULL, -- 更新者 + create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间 + update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间(由触发器维护) + delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间 + deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 + PRIMARY KEY (module_id), + UNIQUE (optsn) +); +COMMENT ON TABLE sys.tb_sys_module IS '模块表'; +COMMENT ON COLUMN sys.tb_sys_module.optsn IS '流水号'; +COMMENT ON COLUMN sys.tb_sys_module.module_id IS '模块ID'; +COMMENT ON COLUMN sys.tb_sys_module.name IS '模块名称'; +COMMENT ON COLUMN sys.tb_sys_module.description IS '模块描述'; +COMMENT ON COLUMN sys.tb_sys_module.creator IS '创建者'; +COMMENT ON COLUMN sys.tb_sys_module.dept_path IS '部门全路径'; +COMMENT ON COLUMN sys.tb_sys_module.updater IS '更新者'; +COMMENT ON COLUMN sys.tb_sys_module.create_time IS '创建时间'; +COMMENT ON COLUMN sys.tb_sys_module.update_time IS '更新时间'; +COMMENT ON COLUMN sys.tb_sys_module.delete_time IS '删除时间'; +COMMENT ON COLUMN sys.tb_sys_module.deleted IS '是否删除'; + +-- 权限表 +DROP TABLE IF EXISTS sys.tb_sys_permission CASCADE; +CREATE TABLE sys.tb_sys_permission ( + optsn VARCHAR(50) NOT NULL, -- 流水号 + permission_id VARCHAR(50) NOT NULL, -- 权限ID + name VARCHAR(100) NOT NULL, -- 权限名称 + code VARCHAR(100) NOT NULL, -- 权限代码 + description VARCHAR(255) DEFAULT NULL, -- 权限描述 + module_id VARCHAR(50) DEFAULT NULL, -- 所属模块ID + creator VARCHAR(50) DEFAULT NULL, -- 创建者 + dept_path VARCHAR(255) DEFAULT NULL, + status BOOLEAN NOT NULL DEFAULT false, -- 部门全路径 + updater VARCHAR(50) DEFAULT NULL, -- 更新者 + create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间 + update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间(由触发器维护) + delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间 + deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 + PRIMARY KEY (permission_id), + UNIQUE (optsn) +); +COMMENT ON TABLE sys.tb_sys_permission IS '权限表'; +COMMENT ON COLUMN sys.tb_sys_permission.optsn IS '流水号'; +COMMENT ON COLUMN sys.tb_sys_permission.permission_id IS '权限ID'; +COMMENT ON COLUMN sys.tb_sys_permission.name IS '权限名称'; +COMMENT ON COLUMN sys.tb_sys_permission.code IS '权限代码'; +COMMENT ON COLUMN sys.tb_sys_permission.description IS '权限描述' ; +COMMENT ON COLUMN sys.tb_sys_permission.module_id IS '所属模块ID'; +COMMENT ON COLUMN sys.tb_sys_permission.creator IS '创建者'; +COMMENT ON COLUMN sys.tb_sys_permission.dept_path IS '部门全路径'; +COMMENT ON COLUMN sys.tb_sys_permission.status IS '角色状态'; +COMMENT ON COLUMN sys.tb_sys_permission.updater IS '更新者'; +COMMENT ON COLUMN sys.tb_sys_permission.create_time IS '创建时间'; +COMMENT ON COLUMN sys.tb_sys_permission.update_time IS '更新时间'; +COMMENT ON COLUMN sys.tb_sys_permission.delete_time IS '删除时间'; +COMMENT ON COLUMN sys.tb_sys_permission.deleted IS '是否删除'; + +-- 角色权限 +DROP TABLE IF EXISTS sys.tb_sys_role_permission CASCADE; +CREATE TABLE sys.tb_sys_role_permission ( + optsn VARCHAR(50) NOT NULL, -- 流水号 + role_id VARCHAR(50) NOT NULL, -- 角色ID + permission_id VARCHAR(50) NOT NULL, -- 权限ID + creator VARCHAR(50) DEFAULT NULL, -- 创建者 + dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径 + updater VARCHAR(50) DEFAULT NULL, -- 更新者 + create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间 + update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间(由触发器维护) + delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间 + deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 + PRIMARY KEY (role_id, permission_id), + UNIQUE (optsn) +); +COMMENT ON TABLE sys.tb_sys_role_permission IS '角色权限表'; +COMMENT ON COLUMN sys.tb_sys_role_permission.optsn IS '流水号'; +COMMENT ON COLUMN sys.tb_sys_role_permission.role_id IS '角色ID'; +COMMENT ON COLUMN sys.tb_sys_role_permission.permission_id IS '权限ID'; +COMMENT ON COLUMN sys.tb_sys_role_permission.creator IS '创建者'; +COMMENT ON COLUMN sys.tb_sys_role_permission.dept_path IS '部门全路径'; +COMMENT ON COLUMN sys.tb_sys_role_permission.updater IS '更新者'; +COMMENT ON COLUMN sys.tb_sys_role_permission.create_time IS '创建时间'; +COMMENT ON COLUMN sys.tb_sys_role_permission.update_time IS '更新时间'; +COMMENT ON COLUMN sys.tb_sys_role_permission.delete_time IS '删除时间'; +COMMENT ON COLUMN sys.tb_sys_role_permission.deleted IS '是否删除'; + +-- 视图权限 +DROP TABLE IF EXISTS sys.tb_sys_view_permission CASCADE; +CREATE TABLE sys.tb_sys_view_permission ( + optsn VARCHAR(50) NOT NULL, -- 流水号 + view_id VARCHAR(50) NOT NULL, -- 视图ID + permission_id VARCHAR(50) NOT NULL, -- 权限ID + creator VARCHAR(50) DEFAULT NULL, -- 创建者 + dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径 + updater VARCHAR(50) DEFAULT NULL, -- 更新者 + create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间 + update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间(由触发器维护) + delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间 + deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 + PRIMARY KEY (view_id, permission_id), + UNIQUE (optsn) +); +COMMENT ON TABLE sys.tb_sys_view_permission IS '视图权限表'; +COMMENT ON COLUMN sys.tb_sys_view_permission.optsn IS '流水号'; +COMMENT ON COLUMN sys.tb_sys_view_permission.view_id IS '视图ID'; +COMMENT ON COLUMN sys.tb_sys_view_permission.permission_id IS '权限ID'; +COMMENT ON COLUMN sys.tb_sys_view_permission.creator IS '创建者'; +COMMENT ON COLUMN sys.tb_sys_view_permission.dept_path IS '部门全路径'; +COMMENT ON COLUMN sys.tb_sys_view_permission.updater IS '更新者'; +COMMENT ON COLUMN sys.tb_sys_view_permission.create_time IS '创建时间'; +COMMENT ON COLUMN sys.tb_sys_view_permission.update_time IS '更新时间'; +COMMENT ON COLUMN sys.tb_sys_view_permission.delete_time IS '删除时间'; +COMMENT ON COLUMN sys.tb_sys_view_permission.deleted IS '是否删除'; + +-- 为所有表创建更新时间触发器 +-- DROP TRIGGER IF EXISTS trg_tb_sys_dept_update_time ON sys.tb_sys_dept; +-- CREATE TRIGGER trg_tb_sys_dept_update_time +-- BEFORE UPDATE ON sys.tb_sys_dept +-- FOR EACH ROW +-- EXECUTE FUNCTION sys.set_update_time(); + +-- DROP TRIGGER IF EXISTS trg_tb_sys_role_update_time ON sys.tb_sys_role; +-- CREATE TRIGGER trg_tb_sys_role_update_time +-- BEFORE UPDATE ON sys.tb_sys_role +-- FOR EACH ROW +-- EXECUTE FUNCTION sys.set_update_time(); + +-- DROP TRIGGER IF EXISTS trg_tb_sys_dept_role_update_time ON sys.tb_sys_dept_role; +-- CREATE TRIGGER trg_tb_sys_dept_role_update_time +-- BEFORE UPDATE ON sys.tb_sys_dept_role +-- FOR EACH ROW +-- EXECUTE FUNCTION sys.set_update_time(); + +-- DROP TRIGGER IF EXISTS trg_tb_sys_user_role_update_time ON sys.tb_sys_user_role; +-- CREATE TRIGGER trg_tb_sys_user_role_update_time +-- BEFORE UPDATE ON sys.tb_sys_user_role +-- FOR EACH ROW +-- EXECUTE FUNCTION sys.set_update_time(); + +-- DROP TRIGGER IF EXISTS trg_tb_sys_view_update_time ON sys.tb_sys_view; +-- CREATE TRIGGER trg_tb_sys_view_update_time +-- BEFORE UPDATE ON sys.tb_sys_view +-- FOR EACH ROW +-- EXECUTE FUNCTION sys.set_update_time(); + +-- DROP TRIGGER IF EXISTS trg_tb_sys_module_update_time ON sys.tb_sys_module; +-- CREATE TRIGGER trg_tb_sys_module_update_time +-- BEFORE UPDATE ON sys.tb_sys_module +-- FOR EACH ROW +-- EXECUTE FUNCTION sys.set_update_time(); + +-- DROP TRIGGER IF EXISTS trg_tb_sys_permission_update_time ON sys.tb_sys_permission; +-- CREATE TRIGGER trg_tb_sys_permission_update_time +-- BEFORE UPDATE ON sys.tb_sys_permission +-- FOR EACH ROW +-- EXECUTE FUNCTION sys.set_update_time(); + +-- DROP TRIGGER IF EXISTS trg_tb_sys_role_permission_update_time ON sys.tb_sys_role_permission; +-- CREATE TRIGGER trg_tb_sys_role_permission_update_time +-- BEFORE UPDATE ON sys.tb_sys_role_permission +-- FOR EACH ROW +-- EXECUTE FUNCTION sys.set_update_time(); + +-- DROP TRIGGER IF EXISTS trg_tb_sys_view_permission_update_time ON sys.tb_sys_view_permission; +-- CREATE TRIGGER trg_tb_sys_view_permission_update_time +-- BEFORE UPDATE ON sys.tb_sys_view_permission +-- FOR EACH ROW +-- EXECUTE FUNCTION sys.set_update_time(); + +-- ============================= +-- 通用对象级权限(ACL) +-- 支持对象类型:任意(如 article/file/course/...) +-- 支持主体:user/dept/role +-- 权限位:1=读(read),2=写(write),4=执行(exec),可相加 +-- include_descendants:当主体为 dept/role 时,是否包含其子级(便于“所有子级可查看”场景) +-- allow:true=允许;false=显式拒绝(拒绝优先生效) +-- dept_path:用于数据范围隔离(与现有规则一致) +-- ============================= + +DROP TABLE IF EXISTS sys.tb_sys_acl CASCADE; +CREATE TABLE sys.tb_sys_acl ( + optsn VARCHAR(50) NOT NULL, -- 流水号 + acl_id VARCHAR(50) NOT NULL, -- ACL主键 + object_type VARCHAR(50) NOT NULL, -- 对象类型:article/file/course/... + object_id VARCHAR(50) NOT NULL, -- 对象ID + principal_type VARCHAR(20) NOT NULL, -- 主体类型:user/dept/role + principal_id VARCHAR(50) NOT NULL, -- 主体ID + principal_dept_id VARCHAR(50) DEFAULT NULL, -- 当主体为role且限定到某部门时的部门ID(支持“某部门的某角色”) + permission SMALLINT NOT NULL, -- 权限位:1读 2写 4执行 + allow BOOLEAN NOT NULL DEFAULT true, -- 允许或显式拒绝 + include_descendants BOOLEAN NOT NULL DEFAULT false, -- 是否包含子级(对dept/role生效) + dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径(数据隔离) + creator VARCHAR(50) DEFAULT NULL, -- 创建者 + updater VARCHAR(50) DEFAULT NULL, -- 更新者 + create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间 + update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间 + delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间 + deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 + PRIMARY KEY (acl_id), + UNIQUE (object_type, object_id, principal_type, principal_id, principal_dept_id, deleted), + UNIQUE (optsn) +); + +COMMENT ON TABLE sys.tb_sys_acl IS '通用对象级权限表(ACL)'; +COMMENT ON COLUMN sys.tb_sys_acl.object_type IS '对象类型:article/file/course/...'; +COMMENT ON COLUMN sys.tb_sys_acl.object_id IS '对象ID'; +COMMENT ON COLUMN sys.tb_sys_acl.principal_type IS '主体类型:user/dept/role'; +COMMENT ON COLUMN sys.tb_sys_acl.principal_id IS '主体ID'; +COMMENT ON COLUMN sys.tb_sys_acl.principal_dept_id IS '当主体为角色时,可选的限定部门ID(某部门的某角色)'; +COMMENT ON COLUMN sys.tb_sys_acl.permission IS '权限位:1读 2写 4执行,可相加'; +COMMENT ON COLUMN sys.tb_sys_acl.allow IS '是否允许(false=显式拒绝)'; +COMMENT ON COLUMN sys.tb_sys_acl.include_descendants IS '是否包含子级(dept/role时启用)'; +COMMENT ON COLUMN sys.tb_sys_acl.dept_path IS '部门全路径(用于数据范围隔离)'; + +CREATE INDEX idx_sys_acl_object ON sys.tb_sys_acl USING btree (object_type, object_id) WHERE deleted = false; +CREATE INDEX idx_sys_acl_principal ON sys.tb_sys_acl USING btree (principal_type, principal_id) WHERE deleted = false; +CREATE INDEX idx_sys_acl_dept_path ON sys.tb_sys_acl USING btree (dept_path) WHERE deleted = false; +-- 针对“某部门的某角色”的检索优化 +CREATE INDEX idx_sys_acl_role_scoped ON sys.tb_sys_acl USING btree (principal_id, principal_dept_id) + WHERE deleted = false AND principal_type = 'role'; + +-- ============================= +-- ACL 策略表:定义对象类型的层级可见/可编辑规则 +-- 例如: +-- - 同级与父级管理员可修改(edit_hierarchy_rule = 'parent_or_same_admin') +-- - 子级全部可查看(view_hierarchy_rule = 'children_all') +-- - 课程:下级只有指定部门/角色可查看(view_hierarchy_rule = 'children_specified') +-- 说明:业务侧需结合“管理员”的判定(通常由角色判定)在应用层实现;本表做规则声明 +-- ============================= + +DROP TABLE IF EXISTS sys.tb_sys_acl_policy CASCADE; +CREATE TABLE sys.tb_sys_acl_policy ( + optsn VARCHAR(50) NOT NULL, -- 流水号 + policy_id VARCHAR(50) NOT NULL, -- 策略ID + name VARCHAR(255) NOT NULL, -- 策略名称 + object_type VARCHAR(50) NOT NULL, -- 目标对象类型 + edit_hierarchy_rule VARCHAR(50) DEFAULT NULL, -- 编辑层级规则:parent_only/parent_or_same_admin/owner_only/none + view_hierarchy_rule VARCHAR(50) DEFAULT NULL, -- 查看层级规则:children_all/children_specified/none + default_permission SMALLINT DEFAULT 0, -- 默认权限位(无显式ACL时应用) + default_allow BOOLEAN DEFAULT true, -- 默认是否允许 + apply_to_children BOOLEAN DEFAULT true, -- 是否默认应用到子级 + creator VARCHAR(50) DEFAULT NULL, + updater VARCHAR(50) DEFAULT NULL, + create_time TIMESTAMPTZ NOT NULL DEFAULT now(), + update_time TIMESTAMPTZ DEFAULT NULL, + delete_time TIMESTAMPTZ DEFAULT NULL, + deleted BOOLEAN NOT NULL DEFAULT false, + PRIMARY KEY (policy_id), + UNIQUE (object_type, deleted), + UNIQUE (optsn) +); + +COMMENT ON TABLE sys.tb_sys_acl_policy IS 'ACL对象类型策略表(层级可见/可编辑规则)'; +COMMENT ON COLUMN sys.tb_sys_acl_policy.object_type IS '对象类型:article/file/course/...'; +COMMENT ON COLUMN sys.tb_sys_acl_policy.edit_hierarchy_rule IS '编辑层级规则:parent_only/parent_or_same_admin/owner_only/none'; +COMMENT ON COLUMN sys.tb_sys_acl_policy.view_hierarchy_rule IS '查看层级规则:children_all/children_specified/none'; +COMMENT ON COLUMN sys.tb_sys_acl_policy.default_permission IS '默认权限位(无显式ACL时兜底)'; +COMMENT ON COLUMN sys.tb_sys_acl_policy.apply_to_children IS '默认是否应用到子级'; + +CREATE INDEX idx_sys_acl_policy_object ON sys.tb_sys_acl_policy USING btree (object_type) WHERE deleted = false; \ No newline at end of file diff --git a/docker/urbanLifeline/postgres/init/sql/createTableUser.sql b/docker/urbanLifeline/postgres/init/sql/createTableUser.sql new file mode 100644 index 00000000..22e0f225 --- /dev/null +++ b/docker/urbanLifeline/postgres/init/sql/createTableUser.sql @@ -0,0 +1,124 @@ + +-- 创建 sys schema(如果不存在) +CREATE SCHEMA IF NOT EXISTS sys; + +-- 用户表 +DROP TABLE IF EXISTS sys.tb_sys_user CASCADE; +CREATE TABLE sys.tb_sys_user ( + optsn VARCHAR(50) NOT NULL, -- 流水号 + user_id VARCHAR(50) NOT NULL, -- 用户ID + usercode VARCHAR(100) DEFAULT NULL, -- 用户code。sso同步数据获取 + password VARCHAR(128) NOT NULL, -- 密码(建议存储 bcrypt/argon2 哈希) + email VARCHAR(100), -- 电子邮件 + phone VARCHAR(500), -- 电话号码 + phone_hash VARCHAR(200), -- 电话hash + wechat_id VARCHAR(50), -- 微信ID + creator VARCHAR(50) DEFAULT NULL, -- 创建者 + create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间(使用带时区时间) + update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间(由触发器维护) + delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间 + deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除(使用 BOOLEAN) + status INTEGER NOT NULL DEFAULT 1, -- 状态 + remark VARCHAR(255) DEFAULT NULL, + PRIMARY KEY (user_id), + UNIQUE (optsn), + UNIQUE (email), + UNIQUE (phone), + UNIQUE (wechat_id), + UNIQUE (usercode) +); +CREATE INDEX idx_tb_sys_user_phone ON sys.tb_sys_user USING btree (phone); + +-- 按 email 域名建立表达式索引(对域名小写处理以实现不区分大小写的域名查询) +-- 使用 split_part(email, '@', 2) 提取 @ 之后的域名部分,再做 lower() 归一化 +-- WHERE email IS NOT NULL 可以避免索引包含大量 NULL +CREATE INDEX idx_tb_sys_user_email_domain ON sys.tb_sys_user USING btree (lower(split_part(email, '@', 2))) + WHERE email IS NOT NULL; +CREATE INDEX idx_tb_sys_user_wechat_id ON sys.tb_sys_user USING btree (wechat_id); + +-- 可选:保留列注释(如果你想把 MySQL 的 COMMENT 同步到 Postgres) +COMMENT ON TABLE sys.tb_sys_user IS '用户表'; +COMMENT ON COLUMN sys.tb_sys_user.optsn IS '流水号'; +COMMENT ON COLUMN sys.tb_sys_user.user_id IS '用户ID'; +COMMENT ON COLUMN sys.tb_sys_user.password IS '密码(建议存储 bcrypt/argon2 哈希)'; +COMMENT ON COLUMN sys.tb_sys_user.email IS '电子邮件'; +COMMENT ON COLUMN sys.tb_sys_user.phone IS '电话号码'; +COMMENT ON COLUMN sys.tb_sys_user.wechat_id IS '微信ID'; +COMMENT ON COLUMN sys.tb_sys_user.create_time IS '创建时间'; +COMMENT ON COLUMN sys.tb_sys_user.update_time IS '更新时间'; +COMMENT ON COLUMN sys.tb_sys_user.delete_time IS '删除时间'; +COMMENT ON COLUMN sys.tb_sys_user.deleted IS '是否删除'; +COMMENT ON COLUMN sys.tb_sys_user.status IS '状态'; + +-- 用户信息表 +DROP TABLE IF EXISTS sys.tb_sys_user_info CASCADE; +CREATE TABLE sys.tb_sys_user_info ( + optsn VARCHAR(50) NOT NULL, -- 流水号 + user_id VARCHAR(50) NOT NULL, -- 用户ID + avatar VARCHAR(255), -- 头像 + gender INTEGER DEFAULT 0, -- 性别 + username VARCHAR(100) NOT NULL, -- 用户名 + level INTEGER DEFAULT 1, -- 等级 + id_card VARCHAR(50), -- 身份证号 + address VARCHAR(255), -- 地址 + creator VARCHAR(50) DEFAULT NULL, -- 创建者 + remark VARCHAR(500) DEFAULT NULL, -- 备注 + create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间 + update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间(触发器维护) + delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间 + deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 + PRIMARY KEY (user_id), + UNIQUE (optsn) +); +COMMENT ON TABLE sys.tb_sys_user_info IS '用户信息表'; +COMMENT ON COLUMN sys.tb_sys_user_info.optsn IS '流水号'; +COMMENT ON COLUMN sys.tb_sys_user_info.user_id IS '用户ID'; +COMMENT ON COLUMN sys.tb_sys_user_info.avatar IS '头像'; +COMMENT ON COLUMN sys.tb_sys_user_info.gender IS '性别'; +COMMENT ON COLUMN sys.tb_sys_user_info.username IS '用户名'; +COMMENT ON COLUMN sys.tb_sys_user_info.level IS '等级'; +COMMENT ON COLUMN sys.tb_sys_user_info.id_card IS '身份证号'; +COMMENT ON COLUMN sys.tb_sys_user_info.address IS '地址'; +COMMENT ON COLUMN sys.tb_sys_user_info.remark IS '备注'; +COMMENT ON COLUMN sys.tb_sys_user_info.create_time IS '创建时间'; +COMMENT ON COLUMN sys.tb_sys_user_info.update_time IS '更新时间'; +COMMENT ON COLUMN sys.tb_sys_user_info.delete_time IS '删除时间'; +COMMENT ON COLUMN sys.tb_sys_user_info.deleted IS '是否删除'; + +-- 登录日志表 +DROP TABLE IF EXISTS sys.tb_sys_login_log CASCADE; +CREATE TABLE sys.tb_sys_login_log ( + optsn VARCHAR(50) NOT NULL, -- 流水号(作为主键) + user_id VARCHAR(50) NOT NULL, -- 用户ID + username VARCHAR(50) NOT NULL, -- 用户名 + ip_address VARCHAR(45), -- IP地址 + ip_source VARCHAR(100), -- IP来源 + browser VARCHAR(100), -- 浏览器 + os VARCHAR(100), -- 操作系统 + password VARCHAR(128), -- 密码(建议存储 bcrypt/argon2 哈希) + login_time TIMESTAMPTZ DEFAULT now(), -- 登录时间 + status INTEGER DEFAULT 1, -- 登录状态(0失败 1成功) + error_count INTEGER DEFAULT 0, -- 错误次数 + message VARCHAR(255), -- 登录消息 + create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间 + PRIMARY KEY (optsn) +); +-- B-tree 索引(显式指定 USING btree,Postgres 默认即为 btree) +CREATE INDEX idx_tb_sys_login_log_user_id ON sys.tb_sys_login_log USING btree (user_id); +CREATE INDEX idx_tb_sys_login_log_login_time ON sys.tb_sys_login_log USING btree (login_time); + +-- 可选:保留列注释 +COMMENT ON TABLE sys.tb_sys_login_log IS '登录日志表'; +COMMENT ON COLUMN sys.tb_sys_login_log.optsn IS '流水号'; +COMMENT ON COLUMN sys.tb_sys_login_log.user_id IS '用户ID'; +COMMENT ON COLUMN sys.tb_sys_login_log.username IS '用户名'; +COMMENT ON COLUMN sys.tb_sys_login_log.ip_address IS 'IP地址'; +COMMENT ON COLUMN sys.tb_sys_login_log.ip_source IS 'IP来源'; +COMMENT ON COLUMN sys.tb_sys_login_log.browser IS '浏览器'; +COMMENT ON COLUMN sys.tb_sys_login_log.os IS '操作系统'; +COMMENT ON COLUMN sys.tb_sys_login_log.password IS '密码(建议存储 bcrypt/argon2 哈希)'; +COMMENT ON COLUMN sys.tb_sys_login_log.login_time IS '登录时间'; +COMMENT ON COLUMN sys.tb_sys_login_log.status IS '登录状态(0失败 1成功)'; +COMMENT ON COLUMN sys.tb_sys_login_log.error_count IS '错误次数'; +COMMENT ON COLUMN sys.tb_sys_login_log.message IS '登录消息'; +COMMENT ON COLUMN sys.tb_sys_login_log.create_time IS '创建时间'; diff --git a/docker/urbanLifeline/postgres/init/sql/createTableWorkcase.sql b/docker/urbanLifeline/postgres/init/sql/createTableWorkcase.sql new file mode 100644 index 00000000..cc2d3d57 --- /dev/null +++ b/docker/urbanLifeline/postgres/init/sql/createTableWorkcase.sql @@ -0,0 +1,331 @@ +CREATE SCHEMA IF NOT EXISTS workcase; + +-- 系统外部人员(来客)管理 用于给系统外人员创建id +DROP TABLE IF EXISTS sys.tb_guest CASCADE; +CREATE TABLE sys.tb_guest( + optsn VARCHAR(50) NOT NULL, -- 流水号 + user_id VARCHAR(50) NOT NULL, -- 来客ID + name VARCHAR(50) NOT NULL, -- 姓名 + phone VARCHAR(50) DEFAULT NULL, -- 电话 + email VARCHAR(50) DEFAULT NULL, -- 邮箱 + wechat_id VARCHAR(50) DEFAULT NULL, -- 微信号 + create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间 + update_time TIMESTAMPTZ DEFAULT NULL, -- 创建时间 + delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间 + deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 + PRIMARY KEY (user_id), + UNIQUE (wechat_id), + UNIQUE (phone), + UNIQUE (email) +); + + +-- ========================================== +-- IM聊天室 + Jitsi Meet 视频会议 表设计 +-- ========================================== + +-- 1. 聊天室表(核心表) +-- 一个工单对应一个聊天室,来客创建,客服人员可加入 +DROP TABLE IF EXISTS workcase.tb_chat_room CASCADE; +CREATE TABLE workcase.tb_chat_room( + optsn VARCHAR(50) NOT NULL, -- 流水号 + room_id VARCHAR(50) NOT NULL, -- 聊天室ID + workcase_id VARCHAR(50) DEFAULT NULL, -- 关联工单ID + room_name VARCHAR(200) NOT NULL, -- 聊天室名称(如:工单#12345的客服支持) + room_type VARCHAR(20) NOT NULL DEFAULT 'workcase', -- 聊天室类型:workcase-工单客服 + status VARCHAR(20) NOT NULL DEFAULT 'active', -- 状态:active-活跃 closed-已关闭 archived-已归档 + guest_id VARCHAR(50) NOT NULL, -- 来客ID(创建者) + guest_name VARCHAR(100) NOT NULL, -- 来客姓名 + ai_session_id VARCHAR(50) DEFAULT NULL, -- AI对话会话ID(从ai.tb_chat同步) + message_count INTEGER NOT NULL DEFAULT 0, -- 消息总数 + device_code VARCHAR(50) NOT NULL, -- 设备代码 + last_message_time TIMESTAMPTZ DEFAULT NULL, -- 最后消息时间 + last_message TEXT DEFAULT NULL, -- 最后一条消息内容(用于列表展示) + comment_level INTEGER DEFAULT 0, -- 服务评分(1-5) + closed_by VARCHAR(50) DEFAULT NULL, -- 关闭人 + closed_time TIMESTAMPTZ DEFAULT NULL, -- 关闭时间 + creator VARCHAR(50) NOT NULL, -- 创建人(系统自动创建) + create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间 + update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间 + delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间 + deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 + PRIMARY KEY (room_id), + UNIQUE (workcase_id), + UNIQUE (optsn) +); +CREATE INDEX idx_chat_room_guest ON workcase.tb_chat_room(guest_id, status); +CREATE INDEX idx_chat_room_time ON workcase.tb_chat_room(last_message_time DESC); +COMMENT ON TABLE workcase.tb_chat_room IS 'IM聊天室表,一个工单对应一个聊天室'; + +-- 2. 聊天室成员表 +-- 记录聊天室内的所有成员(来客+客服人员) +DROP TABLE IF EXISTS workcase.tb_chat_room_member CASCADE; +CREATE TABLE workcase.tb_chat_room_member( + optsn VARCHAR(50) NOT NULL, -- 流水号 + member_id VARCHAR(50) NOT NULL, -- 成员记录ID + room_id VARCHAR(50) NOT NULL, -- 聊天室ID + user_id VARCHAR(50) NOT NULL, -- 用户ID(来客ID或员工ID) + user_type VARCHAR(20) NOT NULL, -- 用户类型:guest-来客 staff-客服 ai-AI助手 + user_name VARCHAR(100) NOT NULL, -- 用户名称 + status VARCHAR(20) NOT NULL DEFAULT 'active', -- 状态:active-活跃 left-已离开 removed-被移除 + unread_count INTEGER NOT NULL DEFAULT 0, -- 该成员的未读消息数 + last_read_time TIMESTAMPTZ DEFAULT NULL, -- 最后阅读时间 + last_read_msg_id VARCHAR(50) DEFAULT NULL, -- 最后阅读的消息ID + join_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 加入时间 + leave_time TIMESTAMPTZ DEFAULT NULL, -- 离开时间 + creator VARCHAR(50) NOT NULL, -- 创建人 + create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间 + update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间 + PRIMARY KEY (member_id), + UNIQUE (room_id, user_id) +); +CREATE INDEX idx_chat_member_room ON workcase.tb_chat_room_member(room_id, status); +CREATE INDEX idx_chat_member_user ON workcase.tb_chat_room_member(user_id, user_type, status); +COMMENT ON TABLE workcase.tb_chat_room_member IS '聊天室成员表,记录来客和客服人员'; + +-- 3. 聊天室消息表 +-- 存储所有聊天消息(AI对话+人工客服对话) +DROP TABLE IF EXISTS workcase.tb_chat_room_message CASCADE; +CREATE TABLE workcase.tb_chat_room_message( + optsn VARCHAR(50) NOT NULL, -- 流水号 + message_id VARCHAR(50) NOT NULL, -- 消息ID + room_id VARCHAR(50) NOT NULL, -- 聊天室ID + sender_id VARCHAR(50) NOT NULL, -- 发送者ID + sender_type VARCHAR(20) NOT NULL, -- 发送者类型:guest-来客 agent-客服 ai-AI助手 system-系统消息 + sender_name VARCHAR(100) NOT NULL, -- 发送者名称 + message_type VARCHAR(20) NOT NULL DEFAULT 'text', -- 消息类型:text-文本 image-图片 file-文件 voice-语音 video-视频 system-系统消息 meeting-会议通知 + content TEXT NOT NULL, -- 消息内容 + files VARCHAR(50)[] DEFAULT '{}', -- 附件文件ID数组(图片、文件、语音、视频等) + content_extra JSONB DEFAULT NULL, -- 扩展内容(会议链接、引用信息等) + reply_to_msg_id VARCHAR(50) DEFAULT NULL, -- 回复的消息ID(引用回复) + is_ai_message BOOLEAN NOT NULL DEFAULT false, -- 是否AI消息(标记从ai.tb_chat同步的消息) + ai_message_id VARCHAR(50) DEFAULT NULL, -- AI原始消息ID(用于追溯) + status VARCHAR(20) NOT NULL DEFAULT 'sent', -- 状态:sending-发送中 sent-已发送 delivered-已送达 read-已读 failed-失败 recalled-已撤回 + read_count INTEGER NOT NULL DEFAULT 0, -- 已读人数 + send_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 发送时间 + creator VARCHAR(50) NOT NULL, -- 创建人 + create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间 + update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间 + PRIMARY KEY (message_id) +); +CREATE INDEX idx_chat_msg_room ON workcase.tb_chat_room_message(room_id, send_time DESC); +CREATE INDEX idx_chat_msg_sender ON workcase.tb_chat_room_message(sender_id, sender_type); +CREATE INDEX idx_chat_msg_ai ON workcase.tb_chat_room_message(ai_message_id) WHERE ai_message_id IS NOT NULL; +COMMENT ON TABLE workcase.tb_chat_room_message IS 'IM聊天消息表,包含AI对话和人工客服消息'; + +DROP TABLE IF EXISTS workcase.tb_chat_room_summary CASCADE; +CREATE TABLE workcase.tb_chat_room_summary ( + optsn VARCHAR(50) NOT NULL, -- 流水号 + summary_id VARCHAR(50) NOT NULL, -- 总结ID + room_id VARCHAR(50) NOT NULL, -- 聊天室ID + question TEXT DEFAULT NULL, -- 核心问题 + needs VARCHAR(500)[] DEFAULT '{}', -- 核心诉求数组 + answer TEXT DEFAULT NULL, -- 解决方案 + workcloud VARCHAR(500)[] DEFAULT '{}', -- 词云关键词数组 + message_count INTEGER DEFAULT 0, -- 参与总结的消息数量 + summary_time TIMESTAMPTZ DEFAULT NULL, -- 总结生成时间 + creator VARCHAR(50) NOT NULL, -- 创建人 + create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间 + update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间 + delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间 + deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 + PRIMARY KEY (summary_id), + UNIQUE (optsn) +); +CREATE INDEX idx_chat_room_summary_room ON workcase.tb_chat_room_summary(room_id, summary_time DESC); +CREATE INDEX idx_chat_room_summary_time ON workcase.tb_chat_room_summary(summary_time DESC); +COMMENT ON TABLE workcase.tb_chat_room_summary IS '聊天室总结表,保存AI生成的聊天总结分析'; + + +-- 4. 视频会议表(Jitsi Meet) +-- 记录聊天室内创建的视频会议 +DROP TABLE IF EXISTS workcase.tb_video_meeting CASCADE; +CREATE TABLE workcase.tb_video_meeting( + optsn VARCHAR(50) NOT NULL, -- 流水号 + meeting_id VARCHAR(50) NOT NULL, -- 会议ID(也是Jitsi房间名) + room_id VARCHAR(50) NOT NULL, -- 关联聊天室ID + workcase_id VARCHAR(50) NOT NULL, -- 关联工单ID + meeting_name VARCHAR(200) NOT NULL, -- 会议名称 + meeting_password VARCHAR(50) DEFAULT NULL, -- 会议密码(可选) + description VARCHAR(500) DEFAULT NULL, -- 会议模式 + jwt_token TEXT DEFAULT NULL, -- JWT Token(用于身份验证) + jitsi_room_name VARCHAR(200) NOT NULL, -- Jitsi房间名(格式:workcase_{workcase_id}_{timestamp}) + jitsi_server_url VARCHAR(500) NOT NULL DEFAULT 'https://meet.jit.si', -- Jitsi服务器地址 + status VARCHAR(20) NOT NULL DEFAULT 'scheduled', -- 状态:scheduled-已安排 ongoing-进行中 ended-已结束 cancelled-已取消 + creator_type VARCHAR(20) NOT NULL, -- 创建者类型:guest-来客 agent-客服 + creator_name VARCHAR(100) NOT NULL, -- 创建者名称 + participant_count INTEGER NOT NULL DEFAULT 0, -- 参与人数 + max_participants INTEGER DEFAULT 10, -- 最大参与人数 + start_time TIMESTAMPTZ NOT NULL, -- 定义会议开始时间 + end_time TIMESTAMPTZ NOT NULL, -- 定义会议结束时间 + advance INTEGER DEFAULT 5, -- 提前入会时间(分钟) + actual_start_time TIMESTAMPTZ DEFAULT NULL, -- 真正会议开始时间 + actual_end_time TIMESTAMPTZ DEFAULT NULL, -- 真正会议结束时间 + duration_seconds INTEGER DEFAULT 0, -- 会议时长(秒) + iframe_url TEXT DEFAULT NULL, -- iframe嵌入URL(生成后存储) + config JSONB DEFAULT NULL, -- Jitsi配置项(自定义配置) + creator VARCHAR(50) NOT NULL, -- 创建人 + create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间 + update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间 + delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间 + deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 + PRIMARY KEY (meeting_id), + UNIQUE (jitsi_room_name) +); +CREATE INDEX idx_meeting_room ON workcase.tb_video_meeting(room_id, status); +CREATE INDEX idx_meeting_workcase ON workcase.tb_video_meeting(workcase_id, status); +CREATE INDEX idx_meeting_time ON workcase.tb_video_meeting(create_time DESC); +COMMENT ON TABLE workcase.tb_video_meeting IS 'Jitsi Meet视频会议表'; + +-- 5. 会议参与记录表(可选,用于审计和统计) +DROP TABLE IF EXISTS workcase.tb_meeting_participant CASCADE; +CREATE TABLE workcase.tb_meeting_participant( + optsn VARCHAR(50) NOT NULL, -- 流水号 + participant_id VARCHAR(50) NOT NULL, -- 参与记录ID + meeting_id VARCHAR(50) NOT NULL, -- 会议ID + user_id VARCHAR(50) NOT NULL, -- 用户ID + user_type VARCHAR(20) NOT NULL, -- 用户类型:guest-来客 agent-客服 + user_name VARCHAR(100) NOT NULL, -- 用户名称 + join_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 加入时间 + leave_time TIMESTAMPTZ DEFAULT NULL, -- 离开时间 + duration_seconds INTEGER DEFAULT 0, -- 参与时长(秒) + is_moderator BOOLEAN NOT NULL DEFAULT false, -- 是否主持人 + join_method VARCHAR(20) DEFAULT 'web', -- 加入方式:web-网页 mobile-移动端 desktop-桌面端 + device_info VARCHAR(200) DEFAULT NULL, -- 设备信息 + create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间 + update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间 + PRIMARY KEY (participant_id) +); +CREATE INDEX idx_meeting_participant ON workcase.tb_meeting_participant(meeting_id, join_time); +CREATE INDEX idx_participant_user ON workcase.tb_meeting_participant(user_id, user_type); +COMMENT ON TABLE workcase.tb_meeting_participant IS '视频会议参与记录表'; + +-- 7. 会议转录记录表(音频转文字) +DROP TABLE IF EXISTS workcase.tb_meeting_transcription CASCADE; +CREATE TABLE workcase.tb_meeting_transcription( + optsn VARCHAR(50) NOT NULL, -- 流水号 + transcription_id VARCHAR(50) NOT NULL, -- 转录记录ID + meeting_id VARCHAR(50) NOT NULL, -- 会议ID + speaker_id VARCHAR(50) NOT NULL, -- 说话人ID + speaker_name VARCHAR(100) NOT NULL, -- 说话人名称 + speaker_type VARCHAR(20) NOT NULL, -- 说话人类型:guest-来客 agent-客服 + content TEXT NOT NULL, -- 转录文本内容 + content_raw TEXT DEFAULT NULL, -- 原始转录结果(含标点前) + language VARCHAR(10) DEFAULT 'zh-CN', -- 语言:zh-CN en-US等 + confidence NUMERIC(3,2) DEFAULT NULL, -- 识别置信度(0-1) + speech_start_time TIMESTAMPTZ NOT NULL, -- 语音开始时间 + speech_end_time TIMESTAMPTZ NOT NULL, -- 语音结束时间 + duration_ms INTEGER NOT NULL, -- 语音时长(毫秒) + audio_url VARCHAR(500) DEFAULT NULL, -- 音频片段URL(可选) + segment_index INTEGER NOT NULL DEFAULT 0, -- 片段序号(按时间排序) + is_final BOOLEAN NOT NULL DEFAULT true, -- 是否最终结果(实时转录会有中间结果) + service_provider VARCHAR(50) DEFAULT 'xunfei', -- 服务提供商:xunfei aliyun tencent google + create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间 + PRIMARY KEY (transcription_id) +); +CREATE INDEX idx_transcription_meeting ON workcase.tb_meeting_transcription(meeting_id, segment_index); +COMMENT ON TABLE workcase.tb_meeting_transcription IS '会议转录记录表,用于保存视频会议的语音转文字内容'; + +-- 8. 员工配置表 +-- 用于控制哪些人员可以在聊天室里接待来客 +DROP TABLE IF EXISTS workcase.tb_customer_service CASCADE; +CREATE TABLE workcase.tb_customer_service( + optsn VARCHAR(50) NOT NULL, -- 流水号 + user_id VARCHAR(50) NOT NULL, -- 员工ID(关联sys用户ID) + username VARCHAR(100) NOT NULL, -- 员工姓名 + user_code VARCHAR(50) DEFAULT NULL, -- 员工工号 + status VARCHAR(20) NOT NULL DEFAULT 'offline', -- 状态:online-在线 busy-忙碌 offline-离线 + skill_tags VARCHAR(50)[] DEFAULT '{}', -- 技能标签(如:电力、燃气、水务) + max_concurrent INTEGER NOT NULL DEFAULT 5, -- 最大并发接待数 + current_workload INTEGER NOT NULL DEFAULT 0, -- 当前工作量 + total_served INTEGER NOT NULL DEFAULT 0, -- 累计服务次数 + avg_response_time INTEGER DEFAULT NULL, -- 平均响应时间(秒) + satisfaction_score NUMERIC(3,2) DEFAULT NULL, -- 满意度评分(0-5) + creator VARCHAR(50) NOT NULL, -- 创建人 + create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间 + update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间 + delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间 + deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 + PRIMARY KEY (user_id) +); +CREATE INDEX idx_customer_service_status ON workcase.tb_customer_service(status, current_workload); +COMMENT ON TABLE workcase.tb_customer_service IS '员工配置表,用于控制哪些人员可以在聊天室接待来客'; + +-- 工单表 +DROP TABLE IF EXISTS workcase.tb_workcase CASCADE; +CREATE TABLE workcase.tb_workcase( + optsn VARCHAR(50) NOT NULL, -- 流水号 + workcase_id VARCHAR(50) NOT NULL, -- 工单ID + room_id VARCHAR(50) NOT NULL, -- 聊天室ID + user_id VARCHAR(50) NOT NULL, -- 来客ID + username VARCHAR(200) NOT NULL, -- 来客姓名 + phone VARCHAR(20) NOT NULL, -- 来客电话 + type VARCHAR(50) NOT NULL, -- 故障类型 + device VARCHAR(50) DEFAULT NULL, -- 设备名称 + device_code VARCHAR(50) DEFAULT NULL, -- 设备代码 + device_name_plate VARCHAR(50) DEFAULT NULL, -- 设备名称牌 + device_name_plate_img VARCHAR(50) NOT NULL, -- 设备名称牌图片 + address VARCHAR(1000) DEFAULT NULL, -- 现场地址 + description VARCHAR(1000) DEFAULT NULL, -- 故障描述 + imgs VARCHAR(50)[] DEFAULT '{}', -- 工单图片id + emergency VARCHAR(50) NOT NULL DEFAULT 'normal', -- 紧急程度 normal-普通 emergency-紧急 + status VARCHAR(50) NOT NULL DEFAULT 'pending', -- 状态 pending-待处理 processing-处理中 done-已完成 + processor VARCHAR(50) DEFAULT NULL, -- 处理人 + creator VARCHAR(50) NOT NULL, -- 创建人 + create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间 + update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间 + delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间 + deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 + PRIMARY KEY (workcase_id), + UNIQUE (room_id), + UNIQUE (optsn) +); + +-- 工单处理过程表(包含工单流转) +DROP TABLE IF EXISTS workcase.tb_workcase_process CASCADE; +CREATE TABLE workcase.tb_workcase_process( + optsn VARCHAR(50) NOT NULL, -- 流水号 + workcase_id VARCHAR(50) NOT NULL, -- 工单ID + process_id VARCHAR(50) NOT NULL, -- 过程id + action VARCHAR(50) NOT NULL, -- 动作 info:记录,assign:指派,redeploy:转派,repeal:撤销,finish:完成 + message VARCHAR(200) DEFAULT NULL, -- 消息 + files VARCHAR(50)[] DEFAULT '{}', -- 携带文件 + processor VARCHAR(50) DEFAULT NULL, -- 处理人(指派、转派专属) + remark VARCHAR(500) DEFAULT NULL, -- 备注 + creator VARCHAR(50) NOT NULL, -- 过程发起人 + create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间 + PRIMARY KEY (process_id) +); + +-- 工单设备涉及的文件表 +DROP TABLE IF EXISTS workcase.tb_workcase_device CASCADE; +CREATE TABLE workcase.tb_workcase_device( + optsn VARCHAR(50) NOT NULL, -- 流水号 + workcase_id VARCHAR(50) NOT NULL, -- 工单ID + device VARCHAR(50) NOT NULL, -- 设备名称 + device_code VARCHAR(50) DEFAULT NULL, -- 设备代码 + file_id VARCHAR(50) NOT NULL, -- 文件id + file_name VARCHAR(50) NOT NULL, -- 文件名 + file_root_id VARCHAR(50) DEFAULT NULL, -- 文件根id + PRIMARY KEY(workcase_id, file_id) +); + +-- 来客对话、工单过程中生成的词云表 +DROP TABLE IF EXISTS workcase.tb_word_cloud CASCADE; +CREATE TABLE workcase.tb_word_cloud( + optsn VARCHAR(50) NOT NULL, -- 流水号 + word_id VARCHAR(50) NOT NULL, -- 词条ID + word VARCHAR(100) NOT NULL, -- 词语 + frequency INTEGER NOT NULL DEFAULT 1, -- 词频 + source_type VARCHAR(20) NOT NULL, -- 来源类型 chat-聊天 workcase-工单 global-全局 + source_id VARCHAR(50) DEFAULT NULL, -- 来源ID(room_id/workcase_id,NULL表示全局统计) + category VARCHAR(50) DEFAULT NULL, -- 分类(如:fault-故障类型 device-设备 emotion-情绪词等) + stat_date DATE NOT NULL, -- 统计日期(按天聚合) + create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间 + update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间 + PRIMARY KEY (word_id), + UNIQUE (word, source_type, source_id, stat_date, category) -- 同一天同一来源同一分类的词唯一 +); +CREATE INDEX idx_word_cloud_source ON workcase.tb_word_cloud(source_type, source_id, stat_date); +CREATE INDEX idx_word_cloud_category ON workcase.tb_word_cloud(category, stat_date); +COMMENT ON TABLE workcase.tb_word_cloud IS '词云统计表,记录聊天和工单中的关键词'; \ No newline at end of file diff --git a/docker/urbanLifeline/postgres/init/sql/initAll.sql b/docker/urbanLifeline/postgres/init/sql/initAll.sql new file mode 100644 index 00000000..2e8b5549 --- /dev/null +++ b/docker/urbanLifeline/postgres/init/sql/initAll.sql @@ -0,0 +1,52 @@ +-- ============================= +-- 城市生命线AI数智化平台 - 数据库初始化脚本 +-- 按顺序执行各模块建表SQL及数据初始化 +-- ============================= + +-- ============================= +-- 第一阶段:创建数据库和表结构 +-- ============================= +\i createDB.sql + +-- 1. 系统基础模块 +\i createTablePermission.sql +\i createTableUser.sql + +-- 2. 文件管理模块 +\i createTableFile.sql + +-- 3. 消息通知模块 +\i createTableMessage.sql + +-- 4. 日志模块 +\i createTableLog.sql + +-- 5. 配置管理模块 +\i createTableConfig.sql + +-- 6. AI模块 智能体+知识库 +\i createTableAI.sql + +-- 7. 招投标业务模块 +\i createTableBidding.sql + +-- 8. 智能客服业务模块 +\i createTableWorkcase.sql + +-- ============================= +-- 第二阶段:初始化基础数据 +-- ============================= + +-- 1. 初始化权限相关基础数据(部门、角色、权限、视图、模块) +\i initDataPermission.sql + +-- 2. 初始化用户数据(管理员账户) +\i initDataUser.sql + +-- 3. 初始化消息渠道配置 +\i initDataMessage.sql + +-- 4. 初始化系统配置 +\i initDataConfig.sql + +-- 注意:文件、日志、知识库、招投标、客服等业务表无需初始化数据 \ No newline at end of file diff --git a/docker/urbanLifeline/postgres/init/sql/initDataConfig.sql b/docker/urbanLifeline/postgres/init/sql/initDataConfig.sql new file mode 100644 index 00000000..b011712b --- /dev/null +++ b/docker/urbanLifeline/postgres/init/sql/initDataConfig.sql @@ -0,0 +1,108 @@ +-- 初始化系统常用配置(与 config.tb_sys_config 对应) +-- 仅插入常用示例,可按需调整 value/remark + +INSERT INTO config.tb_sys_config ( + optsn, config_id, key, name, value, config_type, render_type, description, + re, options, "group", module_id, order_num, status, remark, + creator, dept_path, updater, create_time, update_time, delete_time, deleted +) VALUES + +-- 站点与品牌 +('CFG-0001', 'cfg_site_name', 'site.name', '站点名称', 'urban-lifeline 平台', 'String', 'input', '站点名称', NULL, NULL, 'site', 'mod_system', 10, 0, '展示在标题/登录/页脚', 'system', NULL, NULL, now(), NULL, NULL, false), +('CFG-0002', 'cfg_site_logo', 'site.logo', '站点Logo', '/static/logo.png', 'String', 'input', '站点Logo地址', NULL, NULL, 'site', 'mod_system', 20, 0, '相对或绝对URL', 'system', NULL, NULL, now(), NULL, NULL, false), +('CFG-0003', 'cfg_site_icp', 'site.icp', 'ICP备案号', '', 'String', 'input', 'ICP备案号', NULL, NULL, 'site', 'mod_system', 30, 0, '页脚展示', 'system', NULL, NULL, now(), NULL, NULL, false), + +-- 国际化与时区 +('CFG-0101', 'cfg_i18n_locale', 'i18n.defaultLocale', '默认语言', 'zh-CN', 'String', 'select', '默认语言', NULL, '["zh-CN", "en-US"]'::json, 'i18n', 'mod_system', 10, 0, '如 zh-CN/en-US', 'system', NULL, NULL, now(), NULL, NULL, false), +('CFG-0102', 'cfg_timezone', 'system.timezone', '系统时区', 'Asia/Shanghai', 'String', 'input', '系统默认时区', NULL, NULL, 'i18n', 'mod_system', 20, 0, 'IANA时区名', 'system', NULL, NULL, now(), NULL, NULL, false), + +-- 安全与认证 +('CFG-0201', 'cfg_pwd_policy', 'security.passwordPolicy','密码策略', '{"minLen":8,"upper":1,"lower":1,"digit":1,"special":0}', 'String', 'textarea', '密码策略', NULL, NULL, 'security', 'mod_system', 10, 0, 'JSON结构', 'system', NULL, NULL, now(), NULL, NULL, false), +('CFG-0202', 'cfg_jwt_exp', 'security.jwt.expireSeconds','JWT过期时间', '86400', 'INTEGER', 'input', 'JWT过期秒数', NULL, NULL, 'security', 'mod_system', 20, 0, '默认24小时', 'system', NULL, NULL, now(), NULL, NULL, false), +('CFG-0203', 'cfg_session_timeout', 'security.session.timeoutMinutes','会话超时', '30', 'INTEGER', 'input', '会话超时(分钟)', NULL, NULL, 'security', 'mod_system', 30, 0, '空闲登出', 'system', NULL, NULL, now(), NULL, NULL, false), +('CFG-0204', 'cfg_signup_enabled', 'security.signup.enabled','开放注册', 'false', 'BOOLEAN', 'switch', '是否开放注册', NULL, NULL, 'security', 'mod_system', 40, 0, '生产建议关闭', 'system', NULL, NULL, now(), NULL, NULL, false), + +-- 存储与上传 +('CFG-0301', 'cfg_upload_max', 'upload.maxSizeMB', '最大上传大小', '50', 'INTEGER', 'input', '单文件最大上传(MB)', NULL, NULL, 'storage', 'mod_file', 10, 0, '前后端需一致校验', 'system', NULL, NULL, now(), NULL, NULL, false), +('CFG-0302', 'cfg_storage_backend', 'storage.backend', '存储后端', 'minio', 'String', 'select', '存储后端类型', NULL, '["local", "minio", "s3"]'::json, 'storage', 'mod_file', 20, 0, '本地/MinIO/S3等,当前默认MinIO', 'system', NULL, NULL, now(), NULL, NULL, false), +('CFG-0303', 'cfg_storage_base', 'storage.basePath', '存储路径', '/data/urban-lifeline', 'String', 'input', '本地存储基路径', NULL, NULL, 'storage', 'mod_file', 30, 0, '当 backend=local', 'system', NULL, NULL, now(), NULL, NULL, false), + +-- MinIO 对象存储配置 +('CFG-0310', 'cfg_minio_endpoint', 'minio.endpoint', 'MinIO服务端点', 'http://localhost:9000', 'String', 'input', 'MinIO服务器地址', NULL, NULL, 'storage', 'mod_file', 40, 0, 'MinIO API服务地址,如 http://localhost:9000', 'system', NULL, NULL, now(), NULL, NULL, false), +('CFG-0311', 'cfg_minio_accesskey', 'minio.accessKey', 'MinIO访问密钥', 'minioadmin', 'String', 'input', 'MinIO AccessKey', NULL, NULL, 'storage', 'mod_file', 50, 0, 'MinIO认证的AccessKey', 'system', NULL, NULL, now(), NULL, NULL, false), +('CFG-0312', 'cfg_minio_secretkey', 'minio.secretKey', 'MinIO私钥', 'minioadmin123', 'String', 'password', 'MinIO SecretKey', NULL, NULL, 'storage', 'mod_file', 60, 0, 'MinIO认证的SecretKey', 'system', NULL, NULL, now(), NULL, NULL, false), +('CFG-0313', 'cfg_minio_bucket', 'minio.bucketName', 'MinIO存储桶', 'urban-lifeline', 'String', 'input', 'MinIO默认存储桶名称', NULL, NULL, 'storage', 'mod_file', 70, 0, '用于存储文件的默认bucket', 'system', NULL, NULL, now(), NULL, NULL, false), +('CFG-0314', 'cfg_minio_publicurl', 'minio.publicUrl', 'MinIO公网地址', 'http://localhost:9000', 'String', 'input', 'MinIO公网访问地址', NULL, NULL, 'storage', 'mod_file', 80, 0, '用于生成文件访问URL,可与endpoint不同', 'system', NULL, NULL, now(), NULL, NULL, false), +('CFG-0315', 'cfg_minio_ssl', 'minio.ssl.enabled', '启用SSL', 'false', 'BOOLEAN', 'switch', '是否启用SSL连接', NULL, NULL, 'storage', 'mod_file', 90, 0, 'HTTPS连接MinIO服务', 'system', NULL, NULL, now(), NULL, NULL, false), +('CFG-0316', 'cfg_minio_region', 'minio.region', 'MinIO区域', 'us-east-1', 'String', 'input', 'MinIO存储区域', NULL, NULL, 'storage', 'mod_file', 100, 0, 'AWS S3兼容的区域配置', 'system', NULL, NULL, now(), NULL, NULL, false), + +-- 文件管理配置 +('CFG-0320', 'cfg_file_allowed_exts','file.allowedExtensions', '允许的文件扩展名', 'jpg,jpeg,png,gif,pdf,doc,docx,xls,xlsx,ppt,pptx,txt,zip,rar', 'String', 'textarea', '允许上传的文件扩展名', NULL, NULL, 'storage', 'mod_file', 110, 0, '逗号分隔,如 jpg,png,pdf', 'system', NULL, NULL, now(), NULL, NULL, false), +('CFG-0321', 'cfg_file_forbidden_exts','file.forbiddenExtensions','禁止的文件扩展名', 'exe,bat,sh,php,jsp,asp', 'String', 'textarea', '禁止上传的文件扩展名', NULL, NULL, 'storage', 'mod_file', 120, 0, '逗号分隔,安全考虑', 'system', NULL, NULL, now(), NULL, NULL, false), +('CFG-0322', 'cfg_file_cleanup_days', 'file.tempCleanupDays', '临时文件清理天数', '7', 'INTEGER', 'input', '临时文件自动清理天数', NULL, NULL, 'storage', 'mod_file', 130, 0, '超过天数的临时文件将被清理', 'system', NULL, NULL, now(), NULL, NULL, false), +('CFG-0323', 'cfg_file_duplicate_check','file.duplicateCheck.enabled','启用文件去重', 'true', 'BOOLEAN', 'switch', '是否启用MD5去重检查', NULL, NULL, 'storage', 'mod_file', 140, 0, '基于MD5值检查重复文件', 'system', NULL, NULL, now(), NULL, NULL, false), +('CFG-0324', 'cfg_file_virus_scan', 'file.virusScan.enabled', '启用病毒扫描', 'false', 'BOOLEAN', 'switch', '是否启用文件病毒扫描', NULL, NULL, 'storage', 'mod_file', 150, 0, '需要集成防病毒引擎', 'system', NULL, NULL, now(), NULL, NULL, false), + +-- 通知(邮件/SMS) +-- 邮件配置 +('CFG-0401', 'cfg_mail_host', 'email.host', 'SMTP服务器地址', 'smtp.qq.com', 'String', 'input', 'SMTP服务器地址', NULL, NULL, 'notify', 'mod_message', 10, 1, '邮件发送服务器地址', 'system', NULL, NULL, now(), NULL, NULL, false), +('CFG-0402', 'cfg_mail_port', 'email.port', 'SMTP端口', '587', 'INTEGER', 'input', 'SMTP服务器端口', NULL, NULL, 'notify', 'mod_message', 20, 1, '常用:25/465/587', 'system', NULL, NULL, now(), NULL, NULL, false), +('CFG-0403', 'cfg_mail_username', 'email.username', '发件人邮箱', '3223905473@qq.com', 'String', 'input', '发件人邮箱地址', NULL, NULL, 'notify', 'mod_message', 30, 1, '用于发送邮件的邮箱账号', 'system', NULL, NULL, now(), NULL, NULL, false), +('CFG-0404', 'cfg_mail_password', 'email.password', '邮箱授权码', 'xmdmxvtjumxocicc', 'String', 'password', '邮箱授权码/密码', NULL, NULL, 'notify', 'mod_message', 40, 1, '邮箱的授权码或密码', 'system', NULL, NULL, now(), NULL, NULL, false), +('CFG-0405', 'cfg_mail_fromname', 'email.fromName', '发件人名称', 'urban-lifeline平台', 'String', 'input', '发件人显示名称', NULL, NULL, 'notify', 'mod_message', 50, 1, '邮件中显示的发件人名称', 'system', NULL, NULL, now(), NULL, NULL, false), +('CFG-0406', 'cfg_mail_ssl', 'email.ssl.enable', '启用SSL', 'true', 'BOOLEAN', 'switch', '是否启用SSL', NULL, NULL, 'notify', 'mod_message', 60, 1, 'SSL加密连接', 'system', NULL, NULL, now(), NULL, NULL, false), +('CFG-0407', 'cfg_mail_timeout', 'email.timeout', '连接超时时间', '30000', 'INTEGER', 'input', '连接超时时间(毫秒)', NULL, NULL, 'notify', 'mod_message', 70, 1, 'SMTP连接超时时间(5000-60000)', 'system', NULL, NULL, now(), NULL, NULL, false), + +-- 短信配置 +('CFG-0411', 'cfg_sms_provider', 'sms.provider', '短信服务商', 'aliyun', 'String', 'select', '短信服务提供商', NULL, '["aliyun", "tencent"]'::json, 'notify', 'mod_message', 80, 1, '短信服务提供商类型', 'system', NULL, NULL, now(), NULL, NULL, false), +('CFG-0412', 'cfg_sms_keyid', 'sms.accessKeyId', 'AccessKey ID', 'LTAI5t68do3qVXx5Rufugt3X', 'String', 'input', '短信服务AccessKey ID', NULL, NULL, 'notify', 'mod_message', 90, 1, '云服务商的AccessKey ID', 'system', NULL, NULL, now(), NULL, NULL, false), +('CFG-0413', 'cfg_sms_secret', 'sms.accessKeySecret', 'AccessKey Secret', '2vD9ToIff49Vph4JQXsn0Cy8nXQfzA', 'String', 'password', '短信服务AccessKey Secret', NULL, NULL, 'notify', 'mod_message', 100, 1, '云服务商的AccessKey Secret', 'system', NULL, NULL, now(), NULL, NULL, false), +('CFG-0414', 'cfg_sms_sign', 'sms.signName', '短信签名', 'urban-lifeline', 'String', 'input', '短信签名', NULL, NULL, 'notify', 'mod_message', 110, 1, '发送短信使用的签名', 'system', NULL, NULL, now(), NULL, NULL, false), +('CFG-0415', 'cfg_sms_tpl_login', 'sms.templateCode.login', '登录验证码模板', 'SMS_491985030', 'String', 'input', '登录验证码模板编码', NULL, NULL, 'notify', 'mod_message', 120, 1, '登录验证码短信模板', 'system', NULL, NULL, now(), NULL, NULL, false), +('CFG-0416', 'cfg_sms_tpl_register', 'sms.templateCode.register','注册验证码模板', 'SMS_491985030', 'String', 'input', '注册验证码模板编码', NULL, NULL, 'notify', 'mod_message', 130, 1, '注册验证码短信模板', 'system', NULL, NULL, now(), NULL, NULL, false), +('CFG-0417', 'cfg_sms_timeout', 'sms.timeout', '请求超时时间', '30000', 'INTEGER', 'input', '请求超时时间(毫秒)', NULL, NULL, 'notify', 'mod_message', 140, 1, 'API请求超时时间(5000-60000)', 'system', NULL, NULL, now(), NULL, NULL, false), + +-- Dify AI 配置 +-- Dify 基础配置 +('CFG-0450', 'cfg_dify_api_base', 'dify.apiBaseUrl', 'Dify API地址', 'http://localhost:8000/v1', 'String', 'input', 'Dify API基础地址', NULL, NULL, 'dify', 'mod_agent', 10, 1, 'Dify服务的API基础地址,如 http://localhost/dify/api', 'system', NULL, NULL, now(), NULL, NULL, false), +('CFG-0452', 'cfg_dify_knowledge_key','dify.knowledgeApiKey', '知识库API密钥', 'dataset-LepcmgOE95n2S7yweNhQzNoB', 'String', 'password', '知识库API密钥', NULL, NULL, 'dify', 'mod_agent', 30, 1, '用于访问Dify知识库的API密钥', 'system', NULL, NULL, now(), NULL, NULL, false), +('CFG-0453', 'cfg_dify_timeout', 'dify.timeout', '请求超时时间', '60', 'INTEGER', 'input', '请求超时时间(秒)', NULL, NULL, 'dify', 'mod_agent', 40, 1, 'API请求的超时时间(10-600秒)', 'system', NULL, NULL, now(), NULL, NULL, false), +('CFG-0454', 'cfg_dify_conn_timeout','dify.connectTimeout', '连接超时时间', '10', 'INTEGER', 'input', '连接超时时间(秒)', NULL, NULL, 'dify', 'mod_agent', 50, 1, 'API连接的超时时间(5-60秒)', 'system', NULL, NULL, now(), NULL, NULL, false), +('CFG-0455', 'cfg_dify_read_timeout','dify.readTimeout', '读取超时时间', '60', 'INTEGER', 'input', '读取超时时间(秒)', NULL, NULL, 'dify', 'mod_agent', 60, 1, 'API读取响应的超时时间(10-600秒)', 'system', NULL, NULL, now(), NULL, NULL, false), +('CFG-0456', 'cfg_dify_stream_timeout','dify.streamTimeout', '流式响应超时时间', '300', 'INTEGER', 'input', '流式响应超时时间(秒)', NULL, NULL, 'dify', 'mod_agent', 70, 1, '流式API响应的超时时间(30-1800秒)', 'system', NULL, NULL, now(), NULL, NULL, false), + +-- Dify 上传配置 +('CFG-0460', 'cfg_dify_upload_types','dify.upload.allowedTypes','允许的文件类型', 'pdf,txt,docx,doc,md,html,htm,xlsx,xls,csv', 'String', 'textarea', '上传文件允许的类型', NULL, NULL, 'dify', 'mod_agent', 80, 1, '支持上传的文件类型列表,逗号分隔', 'system', NULL, NULL, now(), NULL, NULL, false), +('CFG-0461', 'cfg_dify_upload_max', 'dify.upload.maxSize', '最大文件大小', '50', 'INTEGER', 'input', '最大文件大小(MB)', NULL, NULL, 'dify', 'mod_agent', 90, 1, '单个文件上传的最大大小限制(1-500MB)', 'system', NULL, NULL, now(), NULL, NULL, false), + +-- Dify 知识库配置 +('CFG-0470', 'cfg_dify_index_tech', 'dify.knowledge.indexing.tchnique','默认索引方式', 'high_quality', 'String', 'select', '默认索引方式', NULL, '["high_quality", "economy"]'::json, 'dify', 'mod_agent', 100, 1, '知识库文档的默认索引方式:high_quality(高质量)或 economy(经济)', 'system', NULL, NULL, now(), NULL, NULL, false), +('CFG-0471', 'cfg_dify_embed_model', 'dify.knowledge.embedding.model','默认Embedding模型', 'Qwen/Qwen3-Embedding-8B', 'String', 'input', '默认Embedding模型', NULL, NULL, 'dify', 'mod_agent', 110, 1, '知识库使用的默认Embedding模型名称', 'system', NULL, NULL, now(), NULL, NULL, false), +('CFG-0472', 'cfg_dify_embed_provider', 'dify.knowledge.embedding.model.provider','Embedding模型供应商', 'langgenius/siliconflow/siliconflow', 'String', 'input', 'Embedding模型供应商', NULL, NULL, 'dify', 'mod_agent', 120, 1, 'Embedding模型的供应商标识', 'system', NULL, NULL, now(), NULL, NULL, false), +('CFG-0473', 'cfg_dify_rerank_enable', 'dify.knowledge.reranking.enable','启用Rerank', 'true', 'BOOLEAN', 'switch', '是否启用Rerank重排序', NULL, NULL, 'dify', 'mod_agent', 130, 1, '启用后会对检索结果进行重排序提升相关性', 'system', NULL, NULL, now(), NULL, NULL, false), +('CFG-0474', 'cfg_dify_rerank_model', 'dify.knowledge.rerank.model','Rerank模型', 'Qwen/Qwen3-Reranker-8B', 'String', 'input', 'Rerank重排序模型', NULL, NULL, 'dify', 'mod_agent', 140, 1, '知识库使用的Rerank模型名称', 'system', NULL, NULL, now(), NULL, NULL, false), +('CFG-0475', 'cfg_dify_rerank_provider', 'dify.knowledge.rerank.model.provider','Rerank模型供应商', 'langgenius/siliconflow/siliconflow', 'String', 'input', 'Rerank模型供应商', NULL, NULL, 'dify', 'mod_agent', 150, 1, 'Rerank模型的供应商标识', 'system', NULL, NULL, now(), NULL, NULL, false), +('CFG-0476', 'cfg_dify_retrieval_topk', 'dify.knowledge.retrieval.top.k','检索TopK', '5', 'INTEGER', 'input', '检索返回的最大文档数', NULL, NULL, 'dify', 'mod_agent', 160, 1, '知识库检索时返回的最相关文档数量(1-20)', 'system', NULL, NULL, now(), NULL, NULL, false), +('CFG-0477', 'cfg_dify_retrieval_threshold', 'dify.knowledge.retrieval.score.threshold','相似度阈值', '0.5', 'DOUBLE', 'input', '检索相似度阈值', NULL, NULL, 'dify', 'mod_agent', 170, 1, '低于此阈值的文档将被过滤(0.0-1.0)', 'system', NULL, NULL, now(), NULL, NULL, false), +-- Dify workcase相关智能体配置 +('CFG-0478', 'cfg_dify_workcase_chat', 'dify.workcase.agent.chat','泰豪小电AgentApiKey', 'app-CDKy0wYkPnl6dA6G7eu113Vw', 'String', 'input', '泰豪小电AgentApiKey', NULL, NULL, 'dify', 'mod_agent', 160, 1, '泰豪小电AgentApiKey', 'system', NULL, NULL, now(), NULL, NULL, false), +('CFG-0479', 'cfg_dify_workcase_summary', 'dify.workcase.workflow.summary','工单总结AgentApikey', 'app-YMlj2B0m21KpYZPv3YdObi7r', 'String', 'input', '工单总结AgentApikey', NULL, NULL, 'dify', 'mod_agent', 170, 1, '工单总结AgentApikey', 'system', NULL, NULL, now(), NULL, NULL, false), + + +-- 日志与审计 +('CFG-0501', 'cfg_log_level', 'log.level', '日志级别', 'INFO', 'String', 'select', '系统日志级别', NULL, '["DEBUG", "INFO", "WARN", "ERROR"]'::json, 'log', 'mod_system', 10, 0, 'DEBUG/INFO/WARN/ERROR', 'system', NULL, NULL, now(), NULL, NULL, false), +('CFG-0502', 'cfg_audit_retention', 'audit.retentionDays', '审计日志保留', '90', 'INTEGER', 'input', '审计日志保留天数', NULL, NULL, 'log', 'mod_system', 20, 0, '合规按需调整', 'system', NULL, NULL, now(), NULL, NULL, false), + +-- 平台特性 +('CFG-0601', 'cfg_maintenance', 'platform.maintenance', '维护模式', 'false', 'BOOLEAN', 'switch', '维护模式开关', NULL, NULL, 'platform', 'mod_system', 10, 0, 'true时仅管理员可用', 'system', NULL, NULL, now(), NULL, NULL, false), +('CFG-0602', 'cfg_feature_acl_policy','feature.acl.policy', 'ACL策略', 'enabled', 'String', 'select', 'ACL策略开关', NULL, '["enabled", "disabled"]'::json, 'platform', 'mod_system', 20, 0, 'enabled/disabled', 'system', NULL, NULL, now(), NULL, NULL, false), + +-- 微信客服配置 +('CFG-0701', 'cfg_wechat_kefu_corpid', 'wechat.kefu.corpId', '企业ID', '', 'String', 'input', '企业微信的企业ID', NULL, NULL, 'wechat', 'mod_workcase', 10, 1, '企业微信管理后台获取', 'system', NULL, NULL, now(), NULL, NULL, false), +('CFG-0702', 'cfg_wechat_kefu_secret', 'wechat.kefu.secret', '客服应用Secret', '', 'String', 'password', '微信客服应用的Secret', NULL, NULL, 'wechat', 'mod_workcase', 20, 1, '微信客服应用的密钥', 'system', NULL, NULL, now(), NULL, NULL, false), +('CFG-0703', 'cfg_wechat_kefu_token', 'wechat.kefu.token', '回调Token', '', 'String', 'input', '消息回调的Token', NULL, NULL, 'wechat', 'mod_workcase', 30, 1, '用于验证消息回调的Token', 'system', NULL, NULL, now(), NULL, NULL, false), +('CFG-0704', 'cfg_wechat_kefu_aeskey', 'wechat.kefu.encodingAesKey','回调加密密钥', '', 'String', 'password', '消息回调的EncodingAESKey', NULL, NULL, 'wechat', 'mod_workcase', 40, 1, '用于解密消息回调的AES密钥', 'system', NULL, NULL, now(), NULL, NULL, false), +('CFG-0705', 'cfg_wechat_kefu_openkfid', 'wechat.kefu.openKfid', '客服账号ID', '', 'String', 'input', '微信客服账号的open_kfid', NULL, NULL, 'wechat', 'mod_workcase', 50, 1, '用于发送消息的客服账号ID', 'system', NULL, NULL, now(), NULL, NULL, false), +('CFG-0706', 'cfg_wechat_kefu_welcome', 'wechat.kefu.welcomeTemplate','欢迎语模板', '您好,您的工单已创建。\n工单编号:{workcaseId}\n问题类型:{type}\n设备:{device}\n我们将尽快为您处理。', 'String', 'textarea', '客服欢迎语消息模板', NULL, NULL, 'wechat', 'mod_workcase', 60, 1, '支持变量:{workcaseId},{type},{device},{username}', 'system', NULL, NULL, now(), NULL, NULL, false); + + diff --git a/docker/urbanLifeline/postgres/init/sql/initDataMessage.sql b/docker/urbanLifeline/postgres/init/sql/initDataMessage.sql new file mode 100644 index 00000000..cd86f138 --- /dev/null +++ b/docker/urbanLifeline/postgres/init/sql/initDataMessage.sql @@ -0,0 +1,118 @@ +-- 初始化消息渠道配置(与 message schema 对应) +-- 配置常用消息发送渠道 + +-- ============================= +-- 1. 初始化消息渠道 +-- ============================= +INSERT INTO message.tb_message_channel ( + optsn, channel_id, channel_code, channel_name, channel_desc, + config, status, priority, creator, create_time, deleted +) VALUES +-- 应用内消息(默认渠道,优先级最高) +('CH-0001', 'channel_app', 'app', '应用内消息', '系统内部消息通知', + '{"enabled": true, "realtime": true}'::json, + 'enabled', 100, 'system', now(), false), + +-- 短信通知 +('CH-0002', 'channel_sms', 'sms', '短信通知', '短信发送服务', + '{ + "enabled": false, + "provider": "", + "apiKey": "", + "apiSecret": "", + "signName": "", + "templateCode": "" + }'::json, + 'disabled', 80, 'system', now(), false), + +-- 邮件通知 +('CH-0003', 'channel_email', 'email', '邮件通知', '电子邮件发送服务', + '{ + "enabled": false, + "smtpHost": "", + "smtpPort": 465, + "username": "", + "password": "", + "fromAddress": "", + "useSsl": true + }'::json, + 'disabled', 70, 'system', now(), false), + +-- 微信公众号 +('CH-0004', 'channel_wechat_mp', 'wechat_official_account', '微信公众号', '微信公众号模板消息', + '{ + "enabled": false, + "appId": "", + "appSecret": "", + "templateId": "" + }'::json, + 'disabled', 60, 'system', now(), false), + +-- 微信小程序 +('CH-0005', 'channel_wechat_mini', 'wechat_applet', '微信小程序', '微信小程序订阅消息', + '{ + "enabled": false, + "appId": "", + "appSecret": "", + "templateId": "" + }'::json, + 'disabled', 50, 'system', now(), false), + +-- 钉钉通知 +('CH-0006', 'channel_dingtalk', 'dingtalk', '钉钉通知', '钉钉工作通知', + '{ + "enabled": false, + "agentId": "", + "appKey": "", + "appSecret": "", + "robotToken": "" + }'::json, + 'disabled', 40, 'system', now(), false); + +-- ============================= +-- 2. 初始化消息模板(系统通用模板) +-- ============================= +INSERT INTO message.tb_message_template ( + optsn, template_id, template_code, template_name, template_type, + title_template, content_template, variables, service, + creator, create_time, deleted +) VALUES +-- 用户注册欢迎消息 +('TPL-0001', 'tpl_user_welcome', 'USER_WELCOME', '用户注册欢迎', 'system', + '欢迎加入 {{platformName}}', + '您好,{{username}}!\n\n欢迎加入 {{platformName}} 平台。您的账号已成功创建。\n\n账号信息:\n- 用户名:{{usercode}}\n- 邮箱:{{email}}\n- 注册时间:{{registerTime}}\n\n祝您使用愉快!', + '["platformName", "username", "usercode", "email", "registerTime"]'::jsonb, + 'system', + 'system', now(), false), + +-- 密码重置通知 +('TPL-0002', 'tpl_password_reset', 'PASSWORD_RESET', '密码重置通知', 'system', + '密码重置验证码', + '您好,{{username}}!\n\n您正在重置密码,验证码为:{{code}}\n\n验证码有效期为 {{expireMinutes}} 分钟,请尽快完成操作。\n\n如非本人操作,请忽略此消息。', + '["username", "code", "expireMinutes"]'::jsonb, + 'system', + 'system', now(), false), + +-- 系统维护通知 +('TPL-0003', 'tpl_system_maintenance', 'SYSTEM_MAINTENANCE', '系统维护通知', 'system', + '系统维护通知', + '尊敬的用户:\n\n系统将于 {{startTime}} 至 {{endTime}} 进行维护升级。\n\n维护内容:{{content}}\n\n维护期间系统将暂停服务,请您提前做好相关安排。\n\n给您带来不便,敬请谅解!', + '["startTime", "endTime", "content"]'::jsonb, + 'system', + 'system', now(), false), + +-- 工单创建通知 +('TPL-0101', 'tpl_ticket_created', 'TICKET_CREATED', '工单创建通知', 'business', + '新工单通知', + '您好,{{username}}!\n\n您有一条新的工单需要处理:\n\n工单编号:{{ticketNo}}\n工单标题:{{title}}\n优先级:{{priority}}\n创建时间:{{createTime}}\n\n请及时登录系统查看处理。', + '["username", "ticketNo", "title", "priority", "createTime"]'::jsonb, + 'customer_service', + 'system', now(), false), + +-- 招标公告发布通知 +('TPL-0201', 'tpl_bidding_published', 'BIDDING_PUBLISHED', '招标公告发布', 'business', + '招标公告发布通知', + '您好!\n\n新的招标项目已发布:\n\n项目名称:{{projectName}}\n项目编号:{{projectNo}}\n发布时间:{{publishTime}}\n截止时间:{{deadlineTime}}\n\n详情请登录系统查看。', + '["projectName", "projectNo", "publishTime", "deadlineTime"]'::jsonb, + 'bidding', + 'system', now(), false); diff --git a/docker/urbanLifeline/postgres/init/sql/initDataPermission.sql b/docker/urbanLifeline/postgres/init/sql/initDataPermission.sql new file mode 100644 index 00000000..2afabc29 --- /dev/null +++ b/docker/urbanLifeline/postgres/init/sql/initDataPermission.sql @@ -0,0 +1,471 @@ +-- 初始化权限相关基础数据(与 sys schema 对应) +-- 包含:部门、角色、模块、权限、视图及其关联关系 + +-- ============================= +-- 1. 初始化根部门 +-- ============================= +INSERT INTO sys.tb_sys_dept ( + optsn, dept_id, name, parent_id, dept_path, description, + creator, create_time, deleted +) VALUES +('DEPT-0001', 'dept_root', '根部门', NULL, '/dept_root/', '系统根部门', + 'system', now(), false); + +-- ============================= +-- 2. 初始化全局角色 +-- ============================= +INSERT INTO sys.tb_sys_role ( + optsn, role_id, name, description, scope, owner_dept_id, + status, creator, dept_path, create_time, deleted +) VALUES +-- 超级管理员(全局) +('ROLE-0001', 'role_super_admin', '超级管理员', '拥有系统所有权限的最高管理员', + 'global', NULL, true, 'system', NULL, now(), false), + +-- 系统管理员(全局) +('ROLE-0002', 'role_system_admin', '系统管理员', '负责系统配置和用户管理', + 'global', NULL, true, 'system', NULL, now(), false), + +-- 普通用户(全局) +('ROLE-0003', 'role_user', '普通用户', '系统普通用户角色', + 'global', NULL, true, 'system', NULL, now(), false), + +-- 访客(全局)- 注册用户默认角色,具备客服聊天和工单的所有接口权限 +('ROLE-0004', 'role_guest', '访客', '访客角色,具备客服聊天和工单的所有接口权限', + 'global', NULL, true, 'system', NULL, now(), false); + +-- ============================= +-- 3. 初始化系统模块 +-- ============================= +INSERT INTO sys.tb_sys_module ( + optsn, module_id, name, description, creator, dept_path, create_time, deleted +) VALUES +('MODULE-0001', 'module_system', '系统管理', '用户、角色、权限、部门管理', 'system', NULL, now(), false), +('MODULE-0002', 'module_file', '文件管理', '文件上传、下载、关联管理', 'system', NULL, now(), false), +('MODULE-0003', 'module_message', '消息通知', '消息发送、接收、模板管理', 'system', NULL, now(), false), +('MODULE-0004', 'module_config', '配置管理', '系统配置参数管理', 'system', NULL, now(), false), +('MODULE-0008', 'module_agent', '智能体', '智能体管理', 'system', NULL, now(), false), +('MODULE-0005', 'module_knowledge', '知识库', '知识文档管理', 'system', NULL, now(), false), +('MODULE-0006', 'module_bidding', '招投标', '招投标业务管理', 'system', NULL, now(), false), +('MODULE-0007', 'module_workcase', '智能客服', '客服工单管理', 'system', NULL, now(), false), +('MODULE-0009', 'module_meeting', '视频会议', 'Jitsi Meet视频会议管理', 'system', NULL, now(), false); + +-- ============================= +-- 4. 初始化系统权限 +-- ============================= +INSERT INTO sys.tb_sys_permission ( + optsn, permission_id, name, code, description, module_id, status, creator, dept_path, create_time, deleted +) VALUES +-- 系统管理模块权限 +('PERM-0001', 'perm_user_view', '用户查看', 'system:user:view', '查看用户列表和详情', 'module_system', true, 'system', NULL, now(), false), +('PERM-0002', 'perm_user_create', '用户创建', 'system:user:create', '创建新用户', 'module_system', true, 'system', NULL, now(), false), +('PERM-0003', 'perm_user_edit', '用户编辑', 'system:user:edit', '编辑用户信息', 'module_system', true, 'system', NULL, now(), false), +('PERM-0004', 'perm_user_delete', '用户删除', 'system:user:delete', '删除用户', 'module_system', true, 'system', NULL, now(), false), +('PERM-0011', 'perm_role_view', '角色查看', 'system:role:view', '查看角色列表和详情', 'module_system', true, 'system', NULL, now(), false), +('PERM-0012', 'perm_role_create', '角色创建', 'system:role:create', '创建新角色', 'module_system', true, 'system', NULL, now(), false), +('PERM-0013', 'perm_role_edit', '角色编辑', 'system:role:edit', '编辑角色信息', 'module_system', true, 'system', NULL, now(), false), +('PERM-0014', 'perm_role_delete', '角色删除', 'system:role:delete', '删除角色', 'module_system', true, 'system', NULL, now(), false), +('PERM-0021', 'perm_dept_view', '部门查看', 'system:dept:view', '查看部门列表和详情', 'module_system', true, 'system', NULL, now(), false), +('PERM-0022', 'perm_dept_create', '部门创建', 'system:dept:create', '创建新部门', 'module_system', true, 'system', NULL, now(), false), +('PERM-0023', 'perm_dept_edit', '部门编辑', 'system:dept:edit', '编辑部门信息', 'module_system', true, 'system', NULL, now(), false), +('PERM-0024', 'perm_dept_delete', '部门删除', 'system:dept:delete', '删除部门', 'module_system', true, 'system', NULL, now(), false), +('PERM-0031', 'perm_permission_view', '权限查看', 'system:permission:view', '查看权限列表', 'module_system', true, 'system', NULL, now(), false), +('PERM-0032', 'perm_permission_manage', '权限管理', 'system:permission:manage', '管理权限配置', 'module_system', true, 'system', NULL, now(), false), + +-- 系统管理模块导出权限 +('PERM-0041', 'perm_user_export', '用户导出', 'system:user:export', '导出用户数据', 'module_system', true, 'system', NULL, now(), false), +('PERM-0042', 'perm_role_export', '角色导出', 'system:role:export', '导出角色数据', 'module_system', true, 'system', NULL, now(), false), +('PERM-0043', 'perm_dept_export', '部门导出', 'system:dept:export', '导出部门数据', 'module_system', true, 'system', NULL, now(), false), + +-- 文件管理模块权限 +('PERM-0101', 'perm_file_view', '文件查看', 'file:file:view', '查看文件列表', 'module_file', true, 'system', NULL, now(), false), +('PERM-0102', 'perm_file_upload', '文件上传', 'file:file:upload', '上传文件', 'module_file', true, 'system', NULL, now(), false), +('PERM-0103', 'perm_file_download', '文件下载', 'file:file:download', '下载文件', 'module_file', true, 'system', NULL, now(), false), +('PERM-0104', 'perm_file_delete', '文件删除', 'file:file:delete', '删除文件', 'module_file', true, 'system', NULL, now(), false), +('PERM-0105', 'perm_file_export', '文件导出', 'file:file:export', '导出文件列表数据', 'module_file', true, 'system', NULL, now(), false), +-- 智能体权限 +('PERM-0120', 'perm_ai_create', '智能体创建', 'ai:agent:create', '创建智能体', 'module_agent', true, 'system', NULL, now(), false), +('PERM-0121', 'perm_ai_update', '智能体更新', 'ai:agent:update', '更新智能体', 'module_agent', true, 'system', NULL, now(), false), +('PERM-0122', 'perm_ai_delete', '智能体删除', 'ai:agent:delete', '删除智能体', 'module_agent', true, 'system', NULL, now(), false), +('PERM-0123', 'perm_ai_view', '智能体查询', 'ai:agent:view', '查询智能体', 'module_agent', true, 'system', NULL, now(), false), +-- 智能体对话权限 没有,因为所有人都可以 +-- Dify代理功能权限(知识库分段管理) +('PERM-0130', 'perm_dify_segment_view', '分段查看', 'ai:dify:segment:view', '查看文档分段列表', 'module_knowledge', true, 'system', NULL, now(), false), +('PERM-0131', 'perm_dify_segment_create', '分段创建', 'ai:dify:segment:create', '创建文档分段', 'module_knowledge', true, 'system', NULL, now(), false), +('PERM-0132', 'perm_dify_segment_update', '分段更新', 'ai:dify:segment:update', '更新文档分段', 'module_knowledge', true, 'system', NULL, now(), false), +('PERM-0133', 'perm_dify_segment_delete', '分段删除', 'ai:dify:segment:delete', '删除文档分段', 'module_knowledge', true, 'system', NULL, now(), false), +('PERM-0134', 'perm_dify_document_status', '文档状态管理', 'ai:dify:document:status', '更新文档状态(启用/禁用/归档)', 'module_knowledge', true, 'system', NULL, now(), false), +-- 知识库管理权限 +('PERM-0140', 'perm_knowledge_create', '知识库创建', 'ai:knowledge:create', '创建知识库', 'module_knowledge', true, 'system', NULL, now(), false), +('PERM-0141', 'perm_knowledge_update', '知识库更新', 'ai:knowledge:update', '更新知识库', 'module_knowledge', true, 'system', NULL, now(), false), +('PERM-0142', 'perm_knowledge_delete', '知识库删除', 'ai:knowledge:delete', '删除知识库', 'module_knowledge', true, 'system', NULL, now(), false), +('PERM-0143', 'perm_knowledge_view', '知识库查看', 'ai:knowledge:view', '查看知识库列表和详情', 'module_knowledge', true, 'system', NULL, now(), false), +-- 知识库文件管理权限 +('PERM-0150', 'perm_knowledge_file_upload', '知识库文件上传', 'ai:knowledge:file:upload', '上传文件到知识库', 'module_knowledge', true, 'system', NULL, now(), false), +('PERM-0151', 'perm_knowledge_file_update', '知识库文件更新', 'ai:knowledge:file:update', '更新知识库文件信息', 'module_knowledge', true, 'system', NULL, now(), false), +('PERM-0152', 'perm_knowledge_file_delete', '知识库文件删除', 'ai:knowledge:file:delete', '删除知识库文件', 'module_knowledge', true, 'system', NULL, now(), false), +('PERM-0153', 'perm_knowledge_file_view', '知识库文件查看', 'ai:knowledge:file:view', '查看知识库文件历史', 'module_knowledge', true, 'system', NULL, now(), false), + +-- 消息通知模块权限 +('PERM-0201', 'perm_message_view', '消息查看', 'message:message:view', '查看消息列表', 'module_message', true, 'system', NULL, now(), false), +('PERM-0202', 'perm_message_send', '消息发送', 'message:message:send', '发送消息通知', 'module_message', true, 'system', NULL, now(), false), +('PERM-0203', 'perm_message_manage', '消息管理', 'message:message:manage', '管理消息模板和配置', 'module_message', true, 'system', NULL, now(), false), +('PERM-0204', 'perm_message_export', '消息导出', 'message:message:export', '导出消息数据', 'module_message', true, 'system', NULL, now(), false), +-- 配置管理模块权限 +('PERM-0301', 'perm_config_view', '配置查看', 'config:config:view', '查看系统配置', 'module_config', true, 'system', NULL, now(), false), +('PERM-0302', 'perm_config_edit', '配置编辑', 'config:config:edit', '修改系统配置', 'module_config', true, 'system', NULL, now(), false), +('PERM-0303', 'perm_config_export', '配置导出', 'config:config:export', '导出系统配置数据', 'module_config', true, 'system', NULL, now(), false), +-- 日志模块权限 +('PERM-0401', 'perm_log_view', '日志查看', 'log:log:view', '查看系统日志', 'module_system', true, 'system', NULL, now(), false), +('PERM-0402', 'perm_log_export', '日志导出', 'log:log:export', '导出系统日志数据', 'module_system', true, 'system', NULL, now(), false), +-- 平台基础菜单访问权限(所有登录用户都有) +('PERM-0501', 'perm_platform_home', '工作台访问', 'platform:home:view', '访问平台工作台', 'module_system', true, 'system', NULL, now(), false), +('PERM-0502', 'perm_platform_chat', 'AI助手访问', 'platform:chat:view', '访问AI助手', 'module_system', true, 'system', NULL, now(), false), +('PERM-0503', 'perm_platform_bidding', '招标助手访问', 'platform:bidding:view', '访问招标助手(iframe)', 'module_bidding', true, 'system', NULL, now(), false), +('PERM-0504', 'perm_platform_workcase', '泰豪小电访问', 'platform:workcase:view', '访问泰豪小电客服(iframe)', 'module_workcase', true, 'system', NULL, now(), false), +('PERM-0505', 'perm_platform_workflow', '智能体编排访问', 'platform:workflow:view', '访问智能体编排(iframe)', 'module_system', true, 'system', NULL, now(), false), +-- Platform 管理后台功能权限 +('PERM-0601', 'perm_platform_admin', '平台管理后台', 'platform:admin:view', '访问平台管理后台', 'module_system', true, 'system', NULL, now(), false), +('PERM-0602', 'perm_platform_admin_overview', '平台数据概览', 'platform:admin:overview', '访问平台数据概览', 'module_system', true, 'system', NULL, now(), false), +('PERM-0603', 'perm_platform_admin_user', '平台用户管理', 'platform:admin:user', '访问平台用户管理', 'module_system', true, 'system', NULL, now(), false), +('PERM-0604', 'perm_platform_admin_knowledge', '平台知识库', 'platform:admin:knowledge', '访问平台知识库', 'module_knowledge', true, 'system', NULL, now(), false), +('PERM-0605', 'perm_platform_admin_config', '平台系统配置', 'platform:admin:config', '访问平台系统配置', 'module_config', true, 'system', NULL, now(), false), +-- Bidding 管理后台功能权限 +('PERM-0611', 'perm_bidding_admin', '招标管理后台', 'bidding:admin:view', '访问招标管理后台', 'module_bidding', true, 'system', NULL, now(), false), +-- Workcase 管理后台功能权限 +('PERM-0621', 'perm_workcase_admin', '客服管理后台', 'workcase:admin:view', '访问客服管理后台', 'module_workcase', true, 'system', NULL, now(), false), +('PERM-0622', 'perm_workcase_overview', '数据概览', 'workcase:overview:view', '访问泰豪小电数据概览', 'module_workcase', true, 'system', NULL, now(), false), +('PERM-0623', 'perm_workcase_knowledge', '知识库管理', 'workcase:knowledge:view', '访问知识库管理', 'module_workcase', true, 'system', NULL, now(), false), +('PERM-0624', 'perm_workcase_tickets', '工单管理', 'workcase:tickets:view', '访问工单管理', 'module_workcase', true, 'system', NULL, now(), false), +('PERM-0625', 'perm_workcase_conversation', '对话数据', 'workcase:conversation:view', '访问对话数据管理', 'module_workcase', true, 'system', NULL, now(), false), +('PERM-0626', 'perm_workcase_agent', '智能体管理', 'workcase:agent:view', '访问智能体管理', 'module_workcase', true, 'system', NULL, now(), false), +('PERM-0627', 'perm_workcase_log', '日志管理', 'workcase:log:view', '访问日志管理', 'module_workcase', true, 'system', NULL, now(), false), +('PERM-0628', 'perm_workcase_chatroom', '聊天室控制台', 'workcase:chatroom:view', '访问聊天室控制台', 'module_workcase', true, 'system', NULL, now(), false), + +-- Workcase 接口权限(访客用户可用) +-- AI对话接口权限 +('PERM-0701', 'perm_workcase_chat_create', 'AI对话创建', 'workcase:chat:create', '创建对话', 'module_workcase', true, 'system', NULL, now(), false), +('PERM-0702', 'perm_workcase_chat_update', 'AI对话更新', 'workcase:chat:update', '更新对话', 'module_workcase', true, 'system', NULL, now(), false), +('PERM-0703', 'perm_workcase_chat_list', 'AI对话查询', 'workcase:chat:list', '查询对话列表', 'module_workcase', true, 'system', NULL, now(), false), +('PERM-0704', 'perm_workcase_chat_message', 'AI对话消息', 'workcase:chat:message', '获取对话消息列表', 'module_workcase', true, 'system', NULL, now(), false), +('PERM-0705', 'perm_workcase_chat_stream', 'AI流式对话', 'workcase:chat:stream', '流式对话接口', 'module_workcase', true, 'system', NULL, now(), false), +('PERM-0706', 'perm_workcase_chat_analyze', 'AI对话分析', 'workcase:chat:analyze', '分析对话生成工单信息', 'module_workcase', true, 'system', NULL, now(), false), +-- 聊天室接口权限 +('PERM-0711', 'perm_workcase_room_create', '聊天室创建', 'workcase:room:create', '创建聊天室', 'module_workcase', true, 'system', NULL, now(), false), +('PERM-0712', 'perm_workcase_room_update', '聊天室更新', 'workcase:room:update', '更新聊天室', 'module_workcase', true, 'system', NULL, now(), false), +('PERM-0713', 'perm_workcase_room_close', '聊天室关闭', 'workcase:room:close', '关闭聊天室', 'module_workcase', true, 'system', NULL, now(), false), +('PERM-0714', 'perm_workcase_room_view', '聊天室查看', 'workcase:room:view', '查看聊天室', 'module_workcase', true, 'system', NULL, now(), false), +('PERM-0715', 'perm_workcase_room_member', '聊天室成员', 'workcase:room:member', '管理聊天室成员', 'module_workcase', true, 'system', NULL, now(), false), +('PERM-0716', 'perm_workcase_room_message', '聊天室消息', 'workcase:room:message', '发送和查看聊天室消息', 'module_workcase', true, 'system', NULL, now(), false), +-- 工单接口权限 +('PERM-0721', 'perm_workcase_ticket_create', '工单创建', 'workcase:ticket:create', '创建工单', 'module_workcase', true, 'system', NULL, now(), false), +('PERM-0722', 'perm_workcase_ticket_update', '工单更新', 'workcase:ticket:update', '更新工单', 'module_workcase', true, 'system', NULL, now(), false), +('PERM-0723', 'perm_workcase_ticket_view', '工单查看', 'workcase:ticket:view', '查看工单详情和列表', 'module_workcase', true, 'system', NULL, now(), false), +('PERM-0724', 'perm_workcase_ticket_process', '工单处理', 'workcase:ticket:process', '工单处理过程管理', 'module_workcase', true, 'system', NULL, now(), false), +('PERM-0725', 'perm_workcase_ticket_device', '工单设备', 'workcase:ticket:device', '工单设备管理', 'module_workcase', true, 'system', NULL, now(), false), + +-- 视频会议模块权限(Jitsi Meet) +('PERM-0730', 'perm_meeting_create', '创建会议', 'meeting:create:own', '创建视频会议', 'module_meeting', true, 'system', NULL, now(), false), +('PERM-0731', 'perm_meeting_join', '加入会议', 'meeting:join:any', '加入视频会议', 'module_meeting', true, 'system', NULL, now(), false), +('PERM-0732', 'perm_meeting_url', '获取会议链接', 'meeting:url:any', '获取会议加入链接', 'module_meeting', true, 'system', NULL, now(), false), +('PERM-0733', 'perm_meeting_token', '获取会议令牌', 'meeting:token:any', '获取会议参与令牌', 'module_meeting', true, 'system', NULL, now(), false); + +-- ============================= +-- 5. 初始化视图(菜单) +-- ============================= +INSERT INTO sys.tb_sys_view ( + optsn, view_id, name, parent_id, url, component, icon, type, + view_type, iframe_url, service, layout, order_num, description, + creator, create_time, deleted +) VALUES +-- ========================= +-- 平台应用菜单 (platform) +-- ========================= +-- 一级菜单 (图标使用 lucide-vue-next) +('VIEW-P002', 'view_platform_chat', '泰豪AI助手', NULL, '/aichat', 'public/Chat/AIChatView.vue', 'MessageCircle', 1, + 'route', NULL, 'platform', 'SidebarLayout', 10, '泰豪AI助手-直接智能体对话', 'system', now(), false), +('VIEW-P001', 'view_platform_home', '全部应用', NULL, '/agents', 'public/Agents/AgentPlatformView.vue', 'LayoutGrid', 1, + 'route', NULL, 'platform', 'SidebarLayout', 20, '全部智能体', 'system', now(), false), +-- iframe 嵌入菜单 +-- url: platform中的路由路径(用于sidebar定位和路由跳转) +-- iframe_url: iframe的src地址(实际内容的URL) +('VIEW-P005', 'view_platform_workflow', '智能体编排', NULL, '/app/workflow', NULL, 'Workflow', 1, + 'iframe', 'http://localhost:3000', 'platform', 'SidebarLayout', 30, 'Dify智能体编排(iframe)', 'system', now(), false), +('VIEW-P003', 'view_platform_bidding', '招标助手', NULL, '/app/bidding', NULL, 'FileText', 1, + 'iframe', '/bidding/', 'platform', 'SidebarLayout', 40, '招标应用(iframe)', 'system', now(), false), +('VIEW-P004', 'view_platform_workcase', '泰豪小电', NULL, '/app/workcase', NULL, 'Headphones', 1, + 'iframe', '/workcase/', 'platform', 'SidebarLayout', 50, '客服应用(iframe)', 'system', now(), false), + + +-- 平台管理后台内部视图(SubSidebarLayout布局,在platform服务内) +('VIEW-P201', 'view_platform_admin_overview', '数据概览', NULL, '/admin/overview', 'admin/overview/OverviewView.vue', 'BarChart3', 1, + 'route', NULL, 'platform', 'SubSidebarLayout', 210, '平台数据概览', 'system', now(), false), +('VIEW-P202', 'view_platform_admin_user', '用户管理', NULL, '/admin/userManagement', 'admin/userManagement/UserManagementView.vue', 'Users', 1, + 'route', NULL, 'platform', 'SubSidebarLayout', 220, '平台用户管理', 'system', now(), false), +('VIEW-P203', 'view_platform_admin_knowledge', '知识库', NULL, '/admin/knowledge', 'admin/knowledge/KnowledgeView.vue', 'FileText', 1, + 'route', NULL, 'platform', 'SubSidebarLayout', 230, '平台知识库管理', 'system', now(), false), +('VIEW-P204', 'view_platform_admin_config', '系统配置', NULL, '/admin/config', 'admin/config/ConfigView.vue', 'Settings', 1, + 'route', NULL, 'platform', 'SubSidebarLayout', 240, '平台系统配置', 'system', now(), false), + +-- -- 系统管理目录 +-- ('VIEW-P100', 'view_system', '系统管理', NULL, '/system', NULL, 'Settings', 0, +-- 'route', NULL, 'platform', 'SidebarLayout', 100, '系统管理目录', 'system', now(), false), +-- +-- -- 系统管理子菜单 +-- ('VIEW-P101', 'view_user', '用户管理', 'view_system', '/system/user', 'system/UserList', 'Users', 1, +-- 'route', NULL, 'platform', 'SidebarLayout', 10, '用户管理页面', 'system', now(), false), +-- +-- ('VIEW-P102', 'view_role', '角色管理', 'view_system', '/system/role', 'system/RoleList', 'Shield', 1, +-- 'route', NULL, 'platform', 'SidebarLayout', 20, '角色管理页面', 'system', now(), false), +-- +-- ('VIEW-P103', 'view_dept', '部门管理', 'view_system', '/system/dept', 'system/DeptList', 'Building', 1, +-- 'route', NULL, 'platform', 'SidebarLayout', 30, '部门管理页面', 'system', now(), false), +-- +-- ('VIEW-P104', 'view_permission', '权限管理', 'view_system', '/system/permission', 'system/PermissionList', 'Lock', 1, +-- 'route', NULL, 'platform', 'SidebarLayout', 40, '权限管理页面', 'system', now(), false), +-- +-- ('VIEW-P105', 'view_config', '配置管理', 'view_system', '/system/config', 'system/ConfigList', 'Settings', 1, +-- 'route', NULL, 'platform', 'SidebarLayout', 50, '配置管理页面', 'system', now(), false), +-- +-- ('VIEW-P106', 'view_file', '文件管理', 'view_system', '/system/file', 'system/FileList', 'FileText', 1, +-- 'route', NULL, 'platform', 'SidebarLayout', 60, '文件管理页面', 'system', now(), false), +-- +-- ('VIEW-P107', 'view_message', '消息管理', 'view_system', '/system/message', 'system/MessageList', 'Mail', 1, +-- 'route', NULL, 'platform', 'SidebarLayout', 70, '消息管理页面', 'system', now(), false), +-- +-- -- ========================= +-- -- 招标应用菜单 (bidding) +-- -- ========================= +-- ('VIEW-B001', 'view_bidding_home', '首页', NULL, '/home', 'Home', 'House', 1, +-- 'route', NULL, 'bidding', 'DefaultLayout', 10, '招标应用首页', 'system', now(), false), +-- +-- ('VIEW-B002', 'view_bidding_list', '招标列表', NULL, '/bidding/list', 'bidding/List', 'List', 1, +-- 'route', NULL, 'bidding', 'DefaultLayout', 20, '招标项目列表', 'system', now(), false), +-- +-- ('VIEW-B003', 'view_bidding_detail', '招标详情', NULL, '/bidding/detail', 'bidding/Detail', 'Document', 1, +-- 'route', NULL, 'bidding', 'DefaultLayout', 30, '招标项目详情', 'system', now(), false), +-- +-- ('VIEW-B004', 'view_bidding_offer', '投标管理', NULL, '/bidding/offer', 'bidding/Offer', 'Edit', 1, +-- 'route', NULL, 'bidding', 'DefaultLayout', 40, '投标管理页面', 'system', now(), false), + +-- ========================= +-- 客服应用菜单 (workcase) - 图标使用 lucide-vue-next +-- ========================= +-- 用户端视图 +('VIEW-W001', 'view_workcase_home', '智能客服', NULL, '/aichat', 'public/AIChat/AIChatView.vue', 'Home', 1, + 'route', NULL, 'workcase', 'SubSidebarLayout', 10, '智能客服首页', 'system', now(), false), + +('VIEW-W002', 'view_workcase_chatroom', '聊天室控制台', NULL, '/chatroom', 'public/ChatRoom/ChatRoomView.vue', 'MessageSquare', 1, + 'route', NULL, 'workcase', 'SubSidebarLayout', 20, '实时聊天室控制台', 'system', now(), false), + +-- 管理端视图(使用 SubSidebarLayout 布局) +('VIEW-W101', 'view_workcase_admin_overview', '数据概览', NULL, '/admin/overview', 'admin/overview/OverviewView.vue', 'BarChart3', 1, + 'route', NULL, 'workcase', 'SubSidebarLayout', 110, '泰豪小电数据概览', 'system', now(), false), + +('VIEW-W102', 'view_workcase_admin_knowledge', '知识库管理', NULL, '/admin/knowledge', 'admin/knowledge/KnowLedgeView.vue', 'FileText', 1, + 'route', NULL, 'workcase', 'SubSidebarLayout', 120, '知识库文档管理', 'system', now(), false), + +('VIEW-W103', 'view_workcase_admin_tickets', '工单管理', NULL, '/admin/workcase', 'admin/workcase/WorkcaseView.vue', 'Ticket', 1, + 'route', NULL, 'workcase', 'SubSidebarLayout', 130, '客服工单管理', 'system', now(), false), + +('VIEW-W104', 'view_workcase_admin_conversation', '对话数据', NULL, '/admin/customerChat', 'admin/customerChat/CustomerChatView.vue', 'MessageCircle', 1, + 'route', NULL, 'workcase', 'SubSidebarLayout', 140, '客户对话数据管理', 'system', now(), false), + +('VIEW-W105', 'view_workcase_admin_agent', '智能体管理', NULL, '/admin/agent', 'admin/agent/AgentView.vue', 'Bot', 1, + 'route', NULL, 'workcase', 'SubSidebarLayout', 150, '智能体配置管理', 'system', now(), false), + +-- 日志管理(带子级的目录) +('VIEW-W106', 'view_workcase_admin_log', '日志管理', NULL, '/admin/log', NULL, 'ScrollText', 1, + 'route', NULL, 'workcase', 'SubSidebarLayout', 160, '日志管理目录', 'system', now(), false), + +('VIEW-W107', 'view_workcase_admin_log_knowledge', '知识库日志', 'view_workcase_admin_log', '/admin/log/knowledge', 'admin/log/knowledgeLog/KnowledgeLogView.vue', 'FileText', 1, + 'route', NULL, 'workcase', NULL, 161, '知识库操作日志', 'system', now(), false), + +('VIEW-W108', 'view_workcase_admin_log_workcase', '工单日志', 'view_workcase_admin_log', '/admin/log/workcase', 'admin/log/workcaseLog/WorkcaseLogView.vue', 'Ticket', 1, + 'route', NULL, 'workcase', NULL, 162, '工单操作日志', 'system', now(), false), + +('VIEW-W109', 'view_workcase_admin_log_system', '系统日志', 'view_workcase_admin_log', '/admin/log/system', 'admin/log/systemLog/SystemLogView.vue', 'Settings', 1, + 'route', NULL, 'workcase', NULL, 163, '系统运行日志', 'system', now(), false); +-- ============================= +-- 6. 角色权限关联(超级管理员拥有所有权限) +-- ============================= +INSERT INTO sys.tb_sys_role_permission ( + optsn, role_id, permission_id, creator, dept_path, create_time, deleted +) +SELECT + 'RP-' || LPAD(ROW_NUMBER() OVER (ORDER BY permission_id)::TEXT, 4, '0'), + 'role_super_admin', + permission_id, + 'system', + NULL, + now(), + false +FROM sys.tb_sys_permission +WHERE deleted = false; + +-- 系统管理员权限(除了用户删除外的系统管理权限 + 所有平台基础菜单) +INSERT INTO sys.tb_sys_role_permission ( + optsn, role_id, permission_id, creator, dept_path, create_time, deleted +) +SELECT + 'RP-SA-' || LPAD(ROW_NUMBER() OVER (ORDER BY permission_id)::TEXT, 4, '0'), + 'role_system_admin', + permission_id, + 'system', + NULL, + now(), + false +FROM sys.tb_sys_permission +WHERE deleted = false + AND code NOT IN ('system:user:delete', 'system:role:delete', 'system:dept:delete') + AND ( + module_id IN ('module_system', 'module_file', 'module_message', 'module_config', 'module_bidding', 'module_workcase') + OR code LIKE 'platform:%:view' -- 包含所有平台基础菜单权限 + ); + +-- 普通用户权限(基础查看和文件操作 + 平台基础菜单访问) +INSERT INTO sys.tb_sys_role_permission ( + optsn, role_id, permission_id, creator, dept_path, create_time, deleted +) VALUES +-- 平台基础菜单访问权限 +('RP-U-0001', 'role_user', 'perm_platform_home', 'system', NULL, now(), false), +('RP-U-0002', 'role_user', 'perm_platform_chat', 'system', NULL, now(), false), +('RP-U-0003', 'role_user', 'perm_platform_bidding', 'system', NULL, now(), false), +('RP-U-0004', 'role_user', 'perm_platform_workcase', 'system', NULL, now(), false), +('RP-U-0005', 'role_user', 'perm_platform_workflow', 'system', NULL, now(), false), +-- 系统功能权限 +('RP-U-0011', 'role_user', 'perm_user_view', 'system', NULL, now(), false), +('RP-U-0012', 'role_user', 'perm_file_view', 'system', NULL, now(), false), +('RP-U-0013', 'role_user', 'perm_file_upload', 'system', NULL, now(), false), +('RP-U-0014', 'role_user', 'perm_file_download', 'system', NULL, now(), false), +('RP-U-0015', 'role_user', 'perm_message_view', 'system', NULL, now(), false), +('RP-U-0016', 'role_user', 'perm_config_view', 'system', NULL, now(), false), +--- 视频会议权限 +('RP-U-0050', 'role_user', 'perm_meeting_create', 'system', NULL, now(), false), +('RP-U-0051', 'role_user', 'perm_meeting_join', 'system', NULL, now(), false), +('RP-U-0052', 'role_user', 'perm_meeting_url', 'system', NULL, now(), false), +('RP-U-0053', 'role_user', 'perm_meeting_token', 'system', NULL, now(), false); + +-- 访客权限(基础菜单 + workcase聊天和工单全部接口权限) +INSERT INTO sys.tb_sys_role_permission ( + optsn, role_id, permission_id, creator, dept_path, create_time, deleted +) VALUES +-- 平台基础菜单访问权限 +('RP-G-0001', 'role_guest', 'perm_platform_home', 'system', NULL, now(), false), +('RP-G-0002', 'role_guest', 'perm_platform_chat', 'system', NULL, now(), false), +('RP-G-0003', 'role_guest', 'perm_platform_workcase', 'system', NULL, now(), false), +-- 系统功能权限(仅查看) +('RP-G-0011', 'role_guest', 'perm_user_view', 'system', NULL, now(), false), +('RP-G-0012', 'role_guest', 'perm_file_view', 'system', NULL, now(), false), +('RP-G-0013', 'role_guest', 'perm_message_view', 'system', NULL, now(), false), +-- Workcase AI对话接口权限 +('RP-G-0021', 'role_guest', 'perm_workcase_chat_create', 'system', NULL, now(), false), +('RP-G-0022', 'role_guest', 'perm_workcase_chat_update', 'system', NULL, now(), false), +('RP-G-0023', 'role_guest', 'perm_workcase_chat_list', 'system', NULL, now(), false), +('RP-G-0024', 'role_guest', 'perm_workcase_chat_message', 'system', NULL, now(), false), +('RP-G-0025', 'role_guest', 'perm_workcase_chat_stream', 'system', NULL, now(), false), +('RP-G-0026', 'role_guest', 'perm_workcase_chat_analyze', 'system', NULL, now(), false), +-- Workcase 聊天室接口权限 +('RP-G-0031', 'role_guest', 'perm_workcase_room_create', 'system', NULL, now(), false), +('RP-G-0032', 'role_guest', 'perm_workcase_room_update', 'system', NULL, now(), false), +('RP-G-0033', 'role_guest', 'perm_workcase_room_close', 'system', NULL, now(), false), +('RP-G-0034', 'role_guest', 'perm_workcase_room_view', 'system', NULL, now(), false), +('RP-G-0035', 'role_guest', 'perm_workcase_room_member', 'system', NULL, now(), false), +('RP-G-0036', 'role_guest', 'perm_workcase_room_message', 'system', NULL, now(), false), +('RP-G-0037', 'role_guest', 'perm_workcase_chatroom', 'system', NULL, now(), false), +-- Workcase 工单接口权限 +('RP-G-0041', 'role_guest', 'perm_workcase_ticket_create', 'system', NULL, now(), false), +('RP-G-0042', 'role_guest', 'perm_workcase_ticket_update', 'system', NULL, now(), false), +('RP-G-0043', 'role_guest', 'perm_workcase_ticket_view', 'system', NULL, now(), false), +('RP-G-0044', 'role_guest', 'perm_workcase_ticket_process', 'system', NULL, now(), false), +('RP-G-0045', 'role_guest', 'perm_workcase_ticket_device', 'system', NULL, now(), false), +--- 视频会议权限 +('RP-G-0050', 'role_guest', 'perm_meeting_create', 'system', NULL, now(), false), +('RP-G-0051', 'role_guest', 'perm_meeting_join', 'system', NULL, now(), false), +('RP-G-0052', 'role_guest', 'perm_meeting_url', 'system', NULL, now(), false), +('RP-G-0053', 'role_guest', 'perm_meeting_token', 'system', NULL, now(), false); + +-- ============================= +-- 7. 视图权限关联 +-- ============================= +-- 将视图与对应模块的权限关联(使用真正的 view_id,不是 optsn) +INSERT INTO sys.tb_sys_view_permission ( + optsn, view_id, permission_id, creator, dept_path, create_time, deleted +) VALUES +-- 平台基础菜单权限关联(所有登录用户都可访问) +('VP-P001', 'view_platform_home', 'perm_platform_home', 'system', NULL, now(), false), +('VP-P002', 'view_platform_chat', 'perm_platform_chat', 'system', NULL, now(), false), +('VP-P003', 'view_platform_bidding', 'perm_platform_bidding', 'system', NULL, now(), false), +('VP-P004', 'view_platform_workcase', 'perm_platform_workcase', 'system', NULL, now(), false), +('VP-P005', 'view_platform_workflow', 'perm_platform_workflow', 'system', NULL, now(), false), + +-- 管理后台入口权限关联(iframe入口) +('VP-P101', 'view_platform_admin_entry', 'perm_platform_admin', 'system', NULL, now(), false), +('VP-P102', 'view_bidding_admin_entry', 'perm_bidding_admin', 'system', NULL, now(), false), +('VP-P103', 'view_workcase_admin_entry', 'perm_workcase_admin', 'system', NULL, now(), false), + +-- 平台管理后台内部视图权限关联(SubSidebarLayout) +('VP-P201', 'view_platform_admin_overview', 'perm_platform_admin_overview', 'system', NULL, now(), false), +('VP-P202', 'view_platform_admin_user', 'perm_platform_admin_user', 'system', NULL, now(), false), +('VP-P203', 'view_platform_admin_knowledge', 'perm_platform_admin_knowledge', 'system', NULL, now(), false), +('VP-P204', 'view_platform_admin_config', 'perm_platform_admin_config', 'system', NULL, now(), false), + +-- Workcase服务用户端视图关联 +('VP-W001', 'view_workcase_home', 'perm_platform_workcase', 'system', NULL, now(), false), +('VP-W002', 'view_workcase_chatroom', 'perm_workcase_chatroom', 'system', NULL, now(), false), + +-- Workcase服务管理端视图关联 +('VP-W101', 'view_workcase_admin_overview', 'perm_workcase_overview', 'system', NULL, now(), false), +('VP-W102', 'view_workcase_admin_knowledge', 'perm_workcase_knowledge', 'system', NULL, now(), false), +('VP-W103', 'view_workcase_admin_tickets', 'perm_workcase_tickets', 'system', NULL, now(), false), +('VP-W104', 'view_workcase_admin_conversation', 'perm_workcase_conversation', 'system', NULL, now(), false), +('VP-W105', 'view_workcase_admin_agent', 'perm_workcase_agent', 'system', NULL, now(), false), + +-- 日志管理视图关联(包括父级和子级) +('VP-W106', 'view_workcase_admin_log', 'perm_workcase_log', 'system', NULL, now(), false), +('VP-W107', 'view_workcase_admin_log_knowledge', 'perm_workcase_log', 'system', NULL, now(), false), +('VP-W108', 'view_workcase_admin_log_workcase', 'perm_workcase_log', 'system', NULL, now(), false), +('VP-W109', 'view_workcase_admin_log_system', 'perm_workcase_log', 'system', NULL, now(), false), + +-- -- 用户管理视图关联用户权限(已注释,因为view_user被注释掉了) +-- -- ('VP-0001', 'view_user', 'perm_user_view', 'system', NULL, now(), false), +-- -- ('VP-0002', 'view_user', 'perm_user_create', 'system', NULL, now(), false), +-- -- ('VP-0003', 'view_user', 'perm_user_edit', 'system', NULL, now(), false), +-- -- ('VP-0004', 'view_user', 'perm_user_delete', 'system', NULL, now(), false), +-- -- ('VP-0005', 'view_user', 'perm_user_export', 'system', NULL, now(), false), +-- -- +-- -- -- 角色管理视图关联角色权限 +-- -- ('VP-0011', 'view_role', 'perm_role_view', 'system', NULL, now(), false), +-- -- ('VP-0012', 'view_role', 'perm_role_create', 'system', NULL, now(), false), +-- -- ('VP-0013', 'view_role', 'perm_role_edit', 'system', NULL, now(), false), +-- -- ('VP-0014', 'view_role', 'perm_role_delete', 'system', NULL, now(), false), +-- -- ('VP-0015', 'view_role', 'perm_role_export', 'system', NULL, now(), false), +-- -- +-- -- -- 部门管理视图关联部门权限 +-- -- ('VP-0021', 'view_dept', 'perm_dept_view', 'system', NULL, now(), false), +-- -- ('VP-0022', 'view_dept', 'perm_dept_create', 'system', NULL, now(), false), +-- -- ('VP-0023', 'view_dept', 'perm_dept_edit', 'system', NULL, now(), false), +-- -- ('VP-0024', 'view_dept', 'perm_dept_delete', 'system', NULL, now(), false), +-- -- ('VP-0025', 'view_dept', 'perm_dept_export', 'system', NULL, now(), false), +-- -- +-- -- -- 权限管理视图关联权限管理权限 +-- -- ('VP-0031', 'view_permission', 'perm_permission_view', 'system', NULL, now(), false), +-- -- ('VP-0032', 'view_permission', 'perm_permission_manage', 'system', NULL, now(), false), +-- -- +-- -- -- 配置管理视图关联配置权限 +-- -- ('VP-0041', 'view_config', 'perm_config_view', 'system', NULL, now(), false), +-- -- ('VP-0042', 'view_config', 'perm_config_edit', 'system', NULL, now(), false), +-- -- ('VP-0043', 'view_config', 'perm_config_export', 'system', NULL, now(), false), +-- -- +-- -- -- 文件管理视图关联文件权限 +-- -- ('VP-0051', 'view_file', 'perm_file_view', 'system', NULL, now(), false), +-- -- ('VP-0052', 'view_file', 'perm_file_upload', 'system', NULL, now(), false), +-- -- ('VP-0053', 'view_file', 'perm_file_download', 'system', NULL, now(), false), +-- -- ('VP-0054', 'view_file', 'perm_file_delete', 'system', NULL, now(), false), +-- -- ('VP-0055', 'view_file', 'perm_file_export', 'system', NULL, now(), false), +-- -- +-- -- -- 消息管理视图关联消息权限 +-- -- ('VP-0061', 'view_message', 'perm_message_view', 'system', NULL, now(), false), +-- -- ('VP-0062', 'view_message', 'perm_message_send', 'system', NULL, now(), false), +-- -- ('VP-0063', 'view_message', 'perm_message_manage', 'system', NULL, now(), false), +-- -- ('VP-0064', 'view_message', 'perm_message_export', 'system', NULL, now(), false); diff --git a/docker/urbanLifeline/postgres/init/sql/initDataUser.sql b/docker/urbanLifeline/postgres/init/sql/initDataUser.sql new file mode 100644 index 00000000..507b2ec7 --- /dev/null +++ b/docker/urbanLifeline/postgres/init/sql/initDataUser.sql @@ -0,0 +1,64 @@ +-- 初始化用户数据(与 sys schema 对应) +-- 创建系统管理员账户和示例用户 + +-- ============================= +-- 1. 创建超级管理员用户 +-- ============================= +-- 注意:密码需要使用 bcrypt 加密,这里使用的是 'admin123' 的 bcrypt hash +-- 实际部署时应该修改为安全的密码 +INSERT INTO sys.tb_sys_user ( + optsn, user_id, usercode, password, email, phone, phone_hash, + create_time, status, deleted +) VALUES +('USER-0001', 'user_admin', 'admin', + '$2a$10$XAe0TE2p0ym94bKJ8LJ52el3M4oYyiExVH/kNCh.pWLLGDZWNM9Yu', -- admin123 + 'admin@urbanlifeline.com', 'DAWTIvnCQI/KmtwkBYI5WP2NpnSKTq4kStJpOJKahOeJLNhAQ0s1', '7503bbfc6171077b737cdc4f76e781893a9a474c9ead05b6b946ac936e5a0288', + now(), 0, false); + +-- 超级管理员用户信息 +INSERT INTO sys.tb_sys_user_info ( + optsn, user_id, username, avatar, gender, level, remark, + create_time, deleted +) VALUES +('UINFO-0001', 'user_admin', '系统管理员', + '/static/avatar/admin.png', 1, 10, '系统超级管理员账户', + now(), false); + +-- ============================= +-- 2. 关联超级管理员角色 +-- ============================= +INSERT INTO sys.tb_sys_user_role ( + optsn, user_id, role_id, dept_id, dept_path, + creator, create_time, deleted +) VALUES +('UR-0001', 'user_admin', 'role_super_admin', 'dept_root', '/dept_root/', + 'system', now(), false); + +-- ============================= +-- 3. 创建示例普通用户(可选) +-- ============================= +INSERT INTO sys.tb_sys_user ( + optsn, user_id, usercode, password, email, phone, phone_hash, + create_time, status, deleted +) VALUES +('USER-0002', 'user_demo', 'demo', + '$2a$10$XAe0TE2p0ym94bKJ8LJ52el3M4oYyiExVH/kNCh.pWLLGDZWNM9Yu', -- admin123 + 'demo@urbanlifeline.com', 'Y9tsAZOppzsxmKvI7iqqRBMDHzvWym2DE5FX1KgEGVBC5Ii1UG68', '4e98ffd0e02a7f746291bff77c6c497225e8884758d503bde2efad64e45ad44b', + now(), 0, false); + +-- 示例用户信息 +INSERT INTO sys.tb_sys_user_info ( + optsn, user_id, username, avatar, gender, level, remark, + create_time, deleted +) VALUES +('UINFO-0002', 'user_demo', '演示用户', + '/static/avatar/demo.png', 0, 1, '系统演示账户', + now(), false); + +-- 关联普通用户角色 +INSERT INTO sys.tb_sys_user_role ( + optsn, user_id, role_id, dept_id, dept_path, + creator, create_time, deleted +) VALUES +('UR-0002', 'user_demo', 'role_user', 'dept_root', '/dept_root/', + 'system', now(), false); diff --git a/docker/urbanLifeline/postgres/init/sql/initDataWorkcase.sql b/docker/urbanLifeline/postgres/init/sql/initDataWorkcase.sql new file mode 100644 index 00000000..cf786a91 --- /dev/null +++ b/docker/urbanLifeline/postgres/init/sql/initDataWorkcase.sql @@ -0,0 +1,6 @@ +-- 初始化聊天室人员 +-- user_admin +INSERT INTO workcase.tb_chat_room_member( + optsn, member_id, room_id, user_id, user_type, user_name, status, unread_count, last_read_time, last_read_msg_id, join_time, leave_time, creator, create_time, update_time +) VALUES +('MEM-0001', 'member_admin', 'room_0001', 'user_admin', 'staff', '系统管理员', 'active', 0, null, null, now(), null, 'system', now(), null); \ No newline at end of file diff --git a/docker/urbanLifeline/serv/.env.example b/docker/urbanLifeline/serv/.env.example new file mode 100644 index 00000000..13bb9c1c --- /dev/null +++ b/docker/urbanLifeline/serv/.env.example @@ -0,0 +1,39 @@ +# ================================================ +# Urban Lifeline 后端服务配置 +# 复制此文件为 .env 并修改配置 +# ================================================ + +# ------------------------------ +# 镜像版本 +# ------------------------------ +IMAGE_VERSION=latest + +# ------------------------------ +# 端口配置 +# ------------------------------ +GATEWAY_PORT=8080 + +# ------------------------------ +# Spring 配置 +# ------------------------------ +SPRING_PROFILES_ACTIVE=prod +NACOS_SERVER_ADDR=nacos:8848 +NACOS_NAMESPACE= + +# ------------------------------ +# 挂载目录配置 +# ------------------------------ +# 配置文件目录 (存放各服务的 yml 配置) +CONFIG_ROOT=./volumes/config + +# 日志目录 +LOG_ROOT=./volumes/logs + +# 数据目录 (上传文件、临时文件等) +DATA_ROOT=./volumes/data + +# ------------------------------ +# 资源限制 +# ------------------------------ +MEMORY_LIMIT=4G +MEMORY_RESERVATION=2G diff --git a/docker/urbanLifeline/serv/Dockerfile.serv b/docker/urbanLifeline/serv/Dockerfile.serv index e69de29b..420fc11f 100644 --- a/docker/urbanLifeline/serv/Dockerfile.serv +++ b/docker/urbanLifeline/serv/Dockerfile.serv @@ -0,0 +1,38 @@ +# ================================================ +# Urban Lifeline - All-in-One 后端服务镜像 +# 单容器运行所有微服务,通过脚本管理 +# +# 构建: docker build -t urban-lifeline-serv:latest -f Dockerfile.serv . +# ================================================ +FROM urban-lifeline-base-serv:latest + +WORKDIR /app +RUN mkdir -p /app/jars /app/logs /app/pids /app/config + +# 复制管理脚本 +COPY docker/urbanLifeline/serv/service-manager.sh /app/service-manager.sh +RUN chmod +x /app/service-manager.sh + +# 复制所有服务 JAR 包 +COPY urbanLifelineServ/gateway/target/*.jar /app/jars/gateway.jar +COPY urbanLifelineServ/system/target/*.jar /app/jars/system.jar +COPY urbanLifelineServ/auth/target/*.jar /app/jars/auth.jar +COPY urbanLifelineServ/file/target/*.jar /app/jars/file.jar +COPY urbanLifelineServ/log/target/*.jar /app/jars/log.jar +COPY urbanLifelineServ/message/target/*.jar /app/jars/message.jar +COPY urbanLifelineServ/crontab/target/*.jar /app/jars/crontab.jar +COPY urbanLifelineServ/ai/target/*.jar /app/jars/ai.jar +COPY urbanLifelineServ/bidding/target/*.jar /app/jars/bidding.jar +COPY urbanLifelineServ/platform/target/*.jar /app/jars/platform.jar +COPY urbanLifelineServ/workcase/target/*.jar /app/jars/workcase.jar + +# 配置目录 (可外挂) +VOLUME ["/app/config", "/app/logs"] + +EXPOSE 8080 8081 8082 8083 8084 8085 8086 8087 8088 8089 8090 + +HEALTHCHECK --interval=30s --timeout=10s --start-period=180s --retries=3 \ + CMD curl -f http://localhost:8080/actuator/health || exit 1 + +ENTRYPOINT ["/app/service-manager.sh"] +CMD ["start-all"] diff --git a/docker/urbanLifeline/serv/config/application-ai.yml b/docker/urbanLifeline/serv/config/application-ai.yml new file mode 100644 index 00000000..b5dd320d --- /dev/null +++ b/docker/urbanLifeline/serv/config/application-ai.yml @@ -0,0 +1,41 @@ +# ================== AI 服务配置 (Docker) ================== +server: + port: 8090 + +spring: + application: + name: ai-service + servlet: + multipart: + enabled: true + max-file-size: 500MB + max-request-size: 500MB + +auth: + enabled: true + gateway-mode: true + whitelist: + - /swagger-ui/** + - /swagger-ui.html + - /v3/api-docs/** + - /webjars/** + - /favicon.ico + - /error + - /actuator/health + - /actuator/info + - /ai/chat/** + +springdoc: + group-configs: + - group: 'default' + display-name: 'AI代理服务 API' + paths-to-match: '/**' + +dubbo: + application: + name: urban-lifeline-agent + qos-enable: false + protocol: + payload: 110100480 + scan: + base-packages: org.xyzh.ai.service.impl diff --git a/docker/urbanLifeline/serv/config/application-auth.yml b/docker/urbanLifeline/serv/config/application-auth.yml new file mode 100644 index 00000000..1eff6324 --- /dev/null +++ b/docker/urbanLifeline/serv/config/application-auth.yml @@ -0,0 +1,31 @@ +# ================== Auth 认证服务配置 (Docker) ================== +server: + port: 8081 + +spring: + application: + name: auth-service + +auth: + enabled: false + gateway-mode: false + whitelist: + - /** + +springdoc: + group-configs: + - group: 'default' + display-name: '认证服务 API' + paths-to-match: '/**' + +dubbo: + application: + name: urban-lifeline-auth + qos-enable: false + scan: + base-packages: org.xyzh.auth.service.impl + +jwt: + secret: ${JWT_SECRET:urban-lifeline-secret-key-2025-xyzh} + expiration: 86400 + refresh-expiration: 604800 diff --git a/docker/urbanLifeline/serv/config/application-bidding.yml b/docker/urbanLifeline/serv/config/application-bidding.yml new file mode 100644 index 00000000..fa95ad67 --- /dev/null +++ b/docker/urbanLifeline/serv/config/application-bidding.yml @@ -0,0 +1,33 @@ +# ================== Bidding 招投标服务配置 (Docker) ================== +server: + port: 8087 + +spring: + application: + name: bidding-service + +auth: + enabled: true + gateway-mode: true + whitelist: + - /swagger-ui/** + - /swagger-ui.html + - /v3/api-docs/** + - /webjars/** + - /favicon.ico + - /error + - /actuator/health + - /actuator/info + +springdoc: + group-configs: + - group: 'default' + display-name: '招投标服务 API' + paths-to-match: '/**' + +dubbo: + application: + name: urban-lifeline-bidding + qos-enable: false + scan: + base-packages: org.xyzh.bidding.service.impl diff --git a/docker/urbanLifeline/serv/config/application-crontab.yml b/docker/urbanLifeline/serv/config/application-crontab.yml new file mode 100644 index 00000000..47671dc6 --- /dev/null +++ b/docker/urbanLifeline/serv/config/application-crontab.yml @@ -0,0 +1,32 @@ +# ================== Crontab 定时任务服务配置 (Docker) ================== +server: + port: 8086 + +spring: + application: + name: crontab-service + +auth: + enabled: false + whitelist: + - /swagger-ui/** + - /swagger-ui.html + - /v3/api-docs/** + - /webjars/** + - /favicon.ico + - /error + - /actuator/health + - /actuator/info + +springdoc: + group-configs: + - group: 'default' + display-name: '定时任务服务 API' + paths-to-match: '/**' + +dubbo: + application: + name: urban-lifeline-crontab + qos-enable: false + scan: + base-packages: org.xyzh.crontab.service.impl diff --git a/docker/urbanLifeline/serv/config/application-file.yml b/docker/urbanLifeline/serv/config/application-file.yml new file mode 100644 index 00000000..180e9e39 --- /dev/null +++ b/docker/urbanLifeline/serv/config/application-file.yml @@ -0,0 +1,41 @@ +# ================== File 文件服务配置 (Docker) ================== +server: + port: 8084 + +spring: + application: + name: file-service + servlet: + multipart: + enabled: true + max-file-size: 500MB + max-request-size: 500MB + +auth: + enabled: true + gateway-mode: true + whitelist: + - /swagger-ui/** + - /swagger-ui.html + - /v3/api-docs/** + - /webjars/** + - /favicon.ico + - /error + - /actuator/health + - /actuator/info + - /file/download/** + +springdoc: + group-configs: + - group: 'default' + display-name: '文件服务 API' + paths-to-match: '/**' + +dubbo: + application: + name: urban-lifeline-file + qos-enable: false + protocol: + payload: 110100480 + scan: + base-packages: org.xyzh.file.service.impl diff --git a/docker/urbanLifeline/serv/config/application-gateway.yml b/docker/urbanLifeline/serv/config/application-gateway.yml new file mode 100644 index 00000000..7cba3b97 --- /dev/null +++ b/docker/urbanLifeline/serv/config/application-gateway.yml @@ -0,0 +1,154 @@ +# ================== Gateway 服务配置 (Docker) ================== +server: + port: 8080 + +spring: + application: + name: gateway-service + + main: + web-application-type: reactive + + cloud: + nacos: + config: + enabled: false + + gateway: + discovery: + locator: + enabled: false + + routes: + - id: auth-service + uri: lb://auth-service + predicates: + - Path=/urban-lifeline/auth/** + filters: + - StripPrefix=1 + - name: RequestRateLimiter + args: + redis-rate-limiter.replenishRate: 100 + redis-rate-limiter.burstCapacity: 200 + + - id: system-service + uri: lb://system-service + predicates: + - Path=/urban-lifeline/system/** + filters: + - StripPrefix=1 + + - id: log-service + uri: lb://log-service + predicates: + - Path=/urban-lifeline/log/** + filters: + - StripPrefix=1 + + - id: file-service + uri: lb://file-service + predicates: + - Path=/urban-lifeline/file/** + filters: + - StripPrefix=1 + + - id: message-service + uri: lb://message-service + predicates: + - Path=/urban-lifeline/message/** + filters: + - StripPrefix=1 + + - id: bidding-service + uri: lb://bidding-service + predicates: + - Path=/urban-lifeline/bidding/** + filters: + - StripPrefix=1 + + - id: platform-service + uri: lb://platform-service + predicates: + - Path=/urban-lifeline/platform/** + filters: + - StripPrefix=1 + + - id: workcase-websocket + uri: lb:ws://workcase-service + predicates: + - Path=/urban-lifeline/workcase/ws/** + filters: + - StripPrefix=1 + + - id: workcase-service + uri: lb://workcase-service + predicates: + - Path=/urban-lifeline/workcase/** + filters: + - StripPrefix=1 + + - id: crontab-service + uri: lb://crontab-service + predicates: + - Path=/urban-lifeline/crontab/** + filters: + - StripPrefix=1 + + - id: ai-service + uri: lb://ai-service + predicates: + - Path=/urban-lifeline/ai/** + filters: + - StripPrefix=1 + + globalcors: + cors-configurations: + '[/**]': + allowedOriginPatterns: "*" + allowedMethods: [GET, POST, PUT, DELETE, OPTIONS] + allowedHeaders: "*" + allowCredentials: true + maxAge: 3600 + + data: + redis: + timeout: 5000ms + lettuce: + pool: + max-active: 20 + max-wait: -1ms + max-idle: 10 + min-idle: 5 + +auth: + enabled: true + token-header: Authorization + token-prefix: "Bearer " + login-path: /urban-lifeline/auth/login + logout-path: /urban-lifeline/auth/logout + captcha-path: /urban-lifeline/auth/captcha + refresh-path: /urban-lifeline/auth/refresh + whitelist: + - /actuator/** + - /v3/api-docs/** + - /swagger-ui/** + - /swagger-resources/** + - /webjars/** + - /doc.html + - /favicon.ico + - /error + - /urban-lifeline/*/v3/api-docs/** + - /urban-lifeline/*/swagger-ui/** + - /urban-lifeline/file/download/** + - /urban-lifeline/ai/chat/** + - /urban-lifeline/system/guest/identify + - /urban-lifeline/workcase/meeting/*/entry + +management: + endpoints: + web: + exposure: + include: health,info,gateway + endpoint: + health: + show-details: always diff --git a/docker/urbanLifeline/serv/config/application-message.yml b/docker/urbanLifeline/serv/config/application-message.yml new file mode 100644 index 00000000..74e92d56 --- /dev/null +++ b/docker/urbanLifeline/serv/config/application-message.yml @@ -0,0 +1,33 @@ +# ================== Message 消息服务配置 (Docker) ================== +server: + port: 8085 + +spring: + application: + name: message-service + +auth: + enabled: true + gateway-mode: true + whitelist: + - /swagger-ui/** + - /swagger-ui.html + - /v3/api-docs/** + - /webjars/** + - /favicon.ico + - /error + - /actuator/health + - /actuator/info + +springdoc: + group-configs: + - group: 'default' + display-name: '消息服务 API' + paths-to-match: '/**' + +dubbo: + application: + name: urban-lifeline-message + qos-enable: false + scan: + base-packages: org.xyzh.message.service.impl diff --git a/docker/urbanLifeline/serv/config/application-platform.yml b/docker/urbanLifeline/serv/config/application-platform.yml new file mode 100644 index 00000000..ed026a11 --- /dev/null +++ b/docker/urbanLifeline/serv/config/application-platform.yml @@ -0,0 +1,33 @@ +# ================== Platform 平台服务配置 (Docker) ================== +server: + port: 8089 + +spring: + application: + name: platform-service + +auth: + enabled: true + gateway-mode: true + whitelist: + - /swagger-ui/** + - /swagger-ui.html + - /v3/api-docs/** + - /webjars/** + - /favicon.ico + - /error + - /actuator/health + - /actuator/info + +springdoc: + group-configs: + - group: 'default' + display-name: '平台服务 API' + paths-to-match: '/**' + +dubbo: + application: + name: urban-lifeline-platform + qos-enable: false + scan: + base-packages: org.xyzh.platform.service.impl diff --git a/docker/urbanLifeline/serv/config/application-system.yml b/docker/urbanLifeline/serv/config/application-system.yml new file mode 100644 index 00000000..7def7a40 --- /dev/null +++ b/docker/urbanLifeline/serv/config/application-system.yml @@ -0,0 +1,41 @@ +# ================== System 系统服务配置 (Docker) ================== +server: + port: 8082 + +spring: + application: + name: system-service + +auth: + enabled: true + gateway-mode: true + whitelist: + - /swagger-ui/** + - /swagger-ui.html + - /v3/api-docs/** + - /webjars/** + - /favicon.ico + - /error + - /actuator/health + - /actuator/info + - /system/guest/identify + +springdoc: + swagger-ui: + try-it-out-enabled: true + show-common-extensions: true + show-extensions: true + filter: true + tags-sorter: alpha + operations-sorter: alpha + group-configs: + - group: 'default' + display-name: '系统服务 API' + paths-to-match: '/**' + +dubbo: + application: + name: urban-lifeline-system + qos-enable: false + scan: + base-packages: org.xyzh.system.service.impl diff --git a/docker/urbanLifeline/serv/config/application-workcase.yml b/docker/urbanLifeline/serv/config/application-workcase.yml new file mode 100644 index 00000000..20bb78b0 --- /dev/null +++ b/docker/urbanLifeline/serv/config/application-workcase.yml @@ -0,0 +1,51 @@ +# ================== Workcase 工单服务配置 (Docker) ================== +server: + port: 8088 + +spring: + application: + name: workcase-service + servlet: + multipart: + enabled: true + max-file-size: 500MB + max-request-size: 500MB + +auth: + enabled: true + gateway-mode: true + whitelist: + - /swagger-ui/** + - /swagger-ui.html + - /v3/api-docs/** + - /webjars/** + - /favicon.ico + - /error + - /actuator/health + - /actuator/info + - /workcase/chat/kefu/callback + - /workcase/receive/crm + +springdoc: + group-configs: + - group: 'default' + display-name: '工单服务 API' + paths-to-match: '/**' + +dubbo: + application: + name: urban-lifeline-workcase + qos-enable: false + protocol: + payload: 110100480 + scan: + base-packages: org.xyzh.workcase.service.impl + +jitsi: + app: + id: ${JITSI_APP_ID:urbanLifeline} + secret: ${JITSI_APP_SECRET:urbanLifeline-jitsi-secret-key-2025-production-safe-hs256} + server: + url: ${JITSI_SERVER_URL:https://meet.example.com} + token: + expiration: 7200000 diff --git a/docker/urbanLifeline/serv/config/bootstrap.yml b/docker/urbanLifeline/serv/config/bootstrap.yml new file mode 100644 index 00000000..1f807a68 --- /dev/null +++ b/docker/urbanLifeline/serv/config/bootstrap.yml @@ -0,0 +1,62 @@ +# ================================================ +# Urban Lifeline - 通用 Bootstrap 配置 (Docker) +# 所有微服务共享的基础配置 +# ================================================ + +# ================== Spring Cloud Nacos ================== +spring: + cloud: + nacos: + discovery: + server-addr: ${NACOS_SERVER_ADDR:nacos:8848} + namespace: ${NACOS_NAMESPACE:} + group: ${NACOS_GROUP:DEFAULT_GROUP} + + # ================== DataSource ================== + datasource: + url: ${DB_URL:jdbc:postgresql://postgres:5432/urban_lifeline} + username: ${DB_USERNAME:postgres} + password: ${DB_PASSWORD:postgres} + driver-class-name: org.postgresql.Driver + + # ================== Redis ================== + data: + redis: + host: ${REDIS_HOST:redis} + port: ${REDIS_PORT:6379} + database: ${REDIS_DATABASE:0} + password: ${REDIS_PASSWORD:} + +# ================== Security AES ================== +security: + aes: + secret-key: ${AES_SECRET_KEY:MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=} + +# ================== Dubbo ================== +dubbo: + protocol: + name: dubbo + port: -1 + registry: + address: nacos://${NACOS_SERVER_ADDR:nacos:8848} + +# ================== MyBatis-Plus ================== +mybatis-plus: + mapper-locations: classpath:mapper/**/*.xml + type-aliases-package: org.xyzh.common.dto, org.xyzh.api + +# ================== SpringDoc 基础配置 ================== +springdoc: + api-docs: + enabled: true + path: /v3/api-docs + swagger-ui: + enabled: true + path: /swagger-ui.html + +# ================== Logging ================== +logging: + config: classpath:log4j2.xml + charset: + console: UTF-8 + file: UTF-8 diff --git a/docker/urbanLifeline/serv/docker-compose.yml b/docker/urbanLifeline/serv/docker-compose.yml index 10d277b1..25b5872f 100644 --- a/docker/urbanLifeline/serv/docker-compose.yml +++ b/docker/urbanLifeline/serv/docker-compose.yml @@ -1,5 +1,5 @@ # ================================================ -# Urban Lifeline - All-in-One 部署 +# Urban Lifeline - All-in-One 后端部署 # 单容器运行所有后端服务 # ================================================ @@ -8,34 +8,30 @@ services: image: urban-lifeline-serv:${IMAGE_VERSION:-latest} container_name: urban-lifeline-serv restart: unless-stopped + env_file: + - .env networks: - urban-lifeline ports: - - "8080:8080" # gateway - - "8081:8081" # auth - - "8082:8082" # system - - "8083:8083" # log - - "8084:8084" # file - - "8085:8085" # message - - "8086:8086" # crontab - - "8087:8087" # bidding - - "8088:8088" # workcase - - "8089:8089" # platform - - "8090:8090" # ai + - "${GATEWAY_PORT:-8080}:8080" environment: TZ: Asia/Shanghai SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE:-prod} NACOS_SERVER_ADDR: ${NACOS_SERVER_ADDR:-nacos:8848} NACOS_NAMESPACE: ${NACOS_NAMESPACE:-} volumes: - - ${LOG_ROOT:-../../volumes/logs}:/app/logs - # 内存限制 (根据服务器配置调整) + # 配置文件目录 + - ${CONFIG_ROOT:-./volumes/config}:/app/config:ro + # 日志目录 + - ${LOG_ROOT:-./volumes/logs}:/app/logs + # 数据目录 (上传文件等) + - ${DATA_ROOT:-./volumes/data}:/app/data deploy: resources: limits: - memory: 4G + memory: ${MEMORY_LIMIT:-4G} reservations: - memory: 2G + memory: ${MEMORY_RESERVATION:-2G} healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"] interval: 30s @@ -45,4 +41,4 @@ services: networks: urban-lifeline: - external: true + name: urban-lifeline diff --git a/docker/urbanLifeline/serv/service-manager.sh b/docker/urbanLifeline/serv/service-manager.sh index 8dfd6309..f01d98ed 100644 --- a/docker/urbanLifeline/serv/service-manager.sh +++ b/docker/urbanLifeline/serv/service-manager.sh @@ -17,6 +17,7 @@ set -e JAR_DIR="/app/jars" LOG_DIR="/app/logs" PID_DIR="/app/pids" +CONFIG_DIR="/app/config" # 服务配置: 服务名=端口:内存 declare -A SERVICES=( @@ -98,6 +99,8 @@ start_service() { local jar_file="${JAR_DIR}/${service}.jar" local log_file="${LOG_DIR}/${service}.log" local pid_file="${PID_DIR}/${service}.pid" + local bootstrap_file="${CONFIG_DIR}/bootstrap.yml" + local app_config_file="${CONFIG_DIR}/application-${service}.yml" if [ ! -f "$jar_file" ]; then log_error "JAR 文件不存在: $jar_file" @@ -127,6 +130,24 @@ start_service() { java_opts="$java_opts -Dspring.cloud.nacos.config.namespace=$NACOS_NAMESPACE" fi + # 外挂配置文件: bootstrap.yml + application-{service}.yml + local config_locations="" + if [ -f "$bootstrap_file" ]; then + config_locations="file:${bootstrap_file}" + log_info " 加载通用配置: ${bootstrap_file}" + fi + if [ -f "$app_config_file" ]; then + if [ -n "$config_locations" ]; then + config_locations="${config_locations},file:${app_config_file}" + else + config_locations="file:${app_config_file}" + fi + log_info " 加载服务配置: ${app_config_file}" + fi + if [ -n "$config_locations" ]; then + java_opts="$java_opts -Dspring.config.additional-location=${config_locations}" + fi + # 启动服务 nohup java $java_opts -jar "$jar_file" > "$log_file" 2>&1 & local pid=$! diff --git a/docker/urbanLifeline/web/.env.example b/docker/urbanLifeline/web/.env.example new file mode 100644 index 00000000..29ff3ee3 --- /dev/null +++ b/docker/urbanLifeline/web/.env.example @@ -0,0 +1,41 @@ +# ================================================ +# Urban Lifeline - 前端环境变量配置 +# +# 使用方法: +# 1. 复制此文件为 .env +# 2. 修改配置值 +# 3. docker-compose 会自动加载 +# +# 应用配置: +# 修改 config/app-config-*.js 文件,重启容器生效 +# - config/app-config-platform.js +# - config/app-config-workcase.js +# - config/app-config-bidding.js +# ================================================ + +# ============================================ +# 目录配置 +# ============================================ +CONFIG_ROOT=./config +LOG_ROOT=./volumes/logs +DATA_ROOT=./volumes/data + +# ============================================ +# 镜像版本 +# ============================================ +IMAGE_VERSION=latest + +# ============================================ +# 端口配置 +# ============================================ +SHARED_PORT=8000 +PLATFORM_PORT=8001 +WORKCASE_PORT=8002 +BIDDING_PORT=8003 +WORKCASE_WECHAT_PORT=8004 + +# ============================================ +# 资源限制 +# ============================================ +MEMORY_LIMIT=512M +MEMORY_RESERVATION=128M diff --git a/docker/urbanLifeline/web/Dockerfile.web b/docker/urbanLifeline/web/Dockerfile.web index 4e0f3f74..b55b7695 100644 --- a/docker/urbanLifeline/web/Dockerfile.web +++ b/docker/urbanLifeline/web/Dockerfile.web @@ -8,7 +8,7 @@ FROM node:20-alpine ENV TZ=Asia/Shanghai -RUN apk add --no-cache tzdata curl bash \ +RUN apk add --no-cache tzdata curl bash sed \ && ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ && echo "Asia/Shanghai" > /etc/timezone @@ -30,13 +30,18 @@ COPY urbanLifelineWeb/packages/workcase/dist/ /app/sites/workcase/ COPY urbanLifelineWeb/packages/bidding/dist/ /app/sites/bidding/ COPY urbanLifelineWeb/packages/workcase_wechat/dist/ /app/sites/workcase_wechat/ -# 默认端口 (可通过环境变量覆盖) +# ============================================ +# 端口配置 (可通过环境变量覆盖) +# ============================================ ENV SHARED_PORT=8000 \ PLATFORM_PORT=8001 \ WORKCASE_PORT=8002 \ BIDDING_PORT=8003 \ WORKCASE_WECHAT_PORT=8004 +# 配置和日志目录 (可外挂) +VOLUME ["/app/config", "/app/logs"] + EXPOSE 8000 8001 8002 8003 8004 HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \ diff --git a/docker/urbanLifeline/web/config/app-config-bidding.js b/docker/urbanLifeline/web/config/app-config-bidding.js new file mode 100644 index 00000000..5fdf4884 --- /dev/null +++ b/docker/urbanLifeline/web/config/app-config-bidding.js @@ -0,0 +1,53 @@ +/** + * @description Bidding 运行时配置 (Docker 部署用) + * + * 域名: https://demo-urbanlifeline.tensorgrove.com + * 修改此文件后重启容器生效 + */ + +window.APP_RUNTIME_CONFIG = { + env: 'production', + + api: { + baseUrl: '/api', + timeout: 30000 + }, + + baseUrl: '/', + + file: { + downloadUrl: '/api/urban-lifeline/file/download/', + uploadUrl: '/api/urban-lifeline/file/upload', + maxSize: { + image: 5, + video: 100, + document: 10 + }, + acceptTypes: { + image: 'image/*', + video: 'video/*', + document: '.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx' + } + }, + + token: { + key: 'token', + refreshThreshold: 300000 + }, + + publicImgPath: '/img', + publicWebPath: '/', + + sso: { + platformUrl: 'https://demo-urbanlifeline.tensorgrove.com/', + workcaseUrl: 'https://demo-urbanlifeline.tensorgrove.com/workcase', + biddingUrl: 'https://demo-urbanlifeline.tensorgrove.com/bidding' + }, + + aesSecretKey: 'MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=', + + features: { + enableDebug: false, + enableMockData: false + } +}; diff --git a/docker/urbanLifeline/web/config/app-config-platform.js b/docker/urbanLifeline/web/config/app-config-platform.js new file mode 100644 index 00000000..1e7d43b1 --- /dev/null +++ b/docker/urbanLifeline/web/config/app-config-platform.js @@ -0,0 +1,53 @@ +/** + * @description Platform 运行时配置 (Docker 部署用) + * + * 域名: https://demo-urbanlifeline.tensorgrove.com + * 修改此文件后重启容器生效 + */ + +window.APP_RUNTIME_CONFIG = { + env: 'production', + + api: { + baseUrl: '/api', + timeout: 30000 + }, + + baseUrl: '/', + + file: { + downloadUrl: '/api/urban-lifeline/file/download/', + uploadUrl: '/api/urban-lifeline/file/upload', + maxSize: { + image: 5, + video: 100, + document: 10 + }, + acceptTypes: { + image: 'image/*', + video: 'video/*', + document: '.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx' + } + }, + + token: { + key: 'token', + refreshThreshold: 300000 + }, + + publicImgPath: '/img', + publicWebPath: '/', + + sso: { + platformUrl: 'https://demo-urbanlifeline.tensorgrove.com/', + workcaseUrl: 'https://demo-urbanlifeline.tensorgrove.com/workcase', + biddingUrl: 'https://demo-urbanlifeline.tensorgrove.com/bidding' + }, + + aesSecretKey: 'MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=', + + features: { + enableDebug: false, + enableMockData: false + } +}; diff --git a/docker/urbanLifeline/web/config/app-config-workcase.js b/docker/urbanLifeline/web/config/app-config-workcase.js new file mode 100644 index 00000000..d520dca4 --- /dev/null +++ b/docker/urbanLifeline/web/config/app-config-workcase.js @@ -0,0 +1,57 @@ +/** + * @description Workcase 运行时配置 (Docker 部署用) + * + * 域名: https://demo-urbanlifeline.tensorgrove.com + * 修改此文件后重启容器生效 + */ + +window.APP_RUNTIME_CONFIG = { + env: 'production', + + api: { + baseUrl: '/api', + timeout: 30000 + }, + + baseUrl: '/', + + file: { + downloadUrl: '/api/urban-lifeline/file/download/', + uploadUrl: '/api/urban-lifeline/file/upload', + maxSize: { + image: 5, + video: 100, + document: 10 + }, + acceptTypes: { + image: 'image/*', + video: 'video/*', + document: '.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx' + } + }, + + token: { + key: 'token', + refreshThreshold: 300000 + }, + + publicImgPath: '/img', + publicWebPath: '/', + + sso: { + platformUrl: 'https://demo-urbanlifeline.tensorgrove.com/', + workcaseUrl: 'https://demo-urbanlifeline.tensorgrove.com/workcase', + biddingUrl: 'https://demo-urbanlifeline.tensorgrove.com/bidding' + }, + + aesSecretKey: 'MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=', + + jitsi: { + serverUrl: 'https://demo-jitsi.tensorgrove.com' + }, + + features: { + enableDebug: false, + enableMockData: false + } +}; diff --git a/docker/urbanLifeline/web/docker-compose.yml b/docker/urbanLifeline/web/docker-compose.yml index 6ddcaa59..cad5b07c 100644 --- a/docker/urbanLifeline/web/docker-compose.yml +++ b/docker/urbanLifeline/web/docker-compose.yml @@ -8,29 +8,38 @@ services: image: urban-lifeline-web:${IMAGE_VERSION:-latest} container_name: urban-lifeline-web restart: unless-stopped + env_file: + - .env networks: - urban-lifeline ports: - - "${PLATFORM_PORT:-8001}:${PLATFORM_PORT:-8001}" - - "${WORKCASE_PORT:-8002}:${WORKCASE_PORT:-8002}" - - "${BIDDING_PORT:-8003}:${BIDDING_PORT:-8003}" - - "${WORKCASE_WECHAT_PORT:-8004}:${WORKCASE_WECHAT_PORT:-8004}" + - "${SHARED_PORT:-8000}:8000" + - "${PLATFORM_PORT:-8001}:8001" + - "${WORKCASE_PORT:-8002}:8002" + - "${BIDDING_PORT:-8003}:8003" + - "${WORKCASE_WECHAT_PORT:-8004}:8004" environment: TZ: Asia/Shanghai + SHARED_PORT: ${SHARED_PORT:-8000} PLATFORM_PORT: ${PLATFORM_PORT:-8001} WORKCASE_PORT: ${WORKCASE_PORT:-8002} BIDDING_PORT: ${BIDDING_PORT:-8003} WORKCASE_WECHAT_PORT: ${WORKCASE_WECHAT_PORT:-8004} volumes: - - ${LOG_ROOT:-../../volumes/logs}/web:/app/logs + # 配置文件目录 (app-config-*.js) + - ${CONFIG_ROOT:-./config}:/app/config:ro + # 日志目录 + - ${LOG_ROOT:-./volumes/logs}:/app/logs + # 数据目录 (静态资源等) + - ${DATA_ROOT:-./volumes/data}:/app/data deploy: resources: limits: - memory: 512M + memory: ${MEMORY_LIMIT:-512M} reservations: - memory: 128M + memory: ${MEMORY_RESERVATION:-128M} healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:${PLATFORM_PORT:-8001}/"] + test: ["CMD", "curl", "-f", "http://localhost:8000/"] interval: 30s timeout: 10s retries: 3 @@ -38,4 +47,4 @@ services: networks: urban-lifeline: - external: true + name: urban-lifeline diff --git a/docker/urbanLifeline/web/web-manager.sh b/docker/urbanLifeline/web/web-manager.sh index 51547cd9..f5549501 100644 --- a/docker/urbanLifeline/web/web-manager.sh +++ b/docker/urbanLifeline/web/web-manager.sh @@ -15,9 +15,43 @@ set -e SITES_DIR="/app/sites" PID_DIR="/app/pids" LOG_DIR="/app/logs" +CONFIG_DIR="/app/config" mkdir -p "$PID_DIR" "$LOG_DIR" +# ============================================ +# 配置处理函数 +# ============================================ + +# 同步挂载的配置文件到站点目录 +sync_mounted_config() { + local web=$1 + local mounted_config="${CONFIG_DIR}/app-config-${web}.js" + local site_config="${SITES_DIR}/${web}/app-config.js" + + if [ -f "$mounted_config" ]; then + log_info "同步挂载配置: $mounted_config -> $site_config" + cp "$mounted_config" "$site_config" + return 0 + else + log_warn "$web 没有挂载配置文件,使用构建默认配置" + return 1 + fi +} + +# 处理所有前端配置 +sync_all_configs() { + log_info "==========================================" + log_info " 同步前端配置文件" + log_info "==========================================" + + for web in "${BOOT_ORDER[@]}"; do + sync_mounted_config "$web" + done + + log_info "配置同步完成" +} + # 前端配置: 名称=端口环境变量名:默认端口 declare -A WEBS=( ["shared"]="SHARED_PORT:8000" @@ -83,6 +117,7 @@ wait_for_web() { # 启动单个前端 start_web() { local web=$1 + local skip_config=$2 local port=$(get_port "$web") local site_dir="${SITES_DIR}/${web}" local pid_file="${PID_DIR}/${web}.pid" @@ -98,6 +133,11 @@ start_web() { return 0 fi + # 同步配置 (除非已经批量同步过) + if [ "$skip_config" != "skip_config" ]; then + sync_mounted_config "$web" + fi + log_info "启动 $web (端口: $port)..." # 使用 serve 启动静态服务 @@ -151,8 +191,11 @@ start_all() { log_info " Urban Lifeline - 启动所有前端" log_info "==========================================" + # 同步所有挂载的配置文件 + sync_all_configs + for web in "${BOOT_ORDER[@]}"; do - start_web "$web" + start_web "$web" "skip_config" log_info "等待 $web 就绪..." if wait_for_web "$web"; then diff --git a/docker/域名.md b/docker/域名.md new file mode 100644 index 00000000..4391da1f --- /dev/null +++ b/docker/域名.md @@ -0,0 +1,9 @@ +- dify: https://demo-dify.tensorgrove.com + +- minio: https://demo-minio.tensorgrove.com + +- jitsi: https://demo-jitsi.tensorgrove.com + +- nacos: https://demo-nacos.tensorgrove.com + +- urbanLifeline: https://demo-urbanlifeline.tensorgrove.com \ No newline at end of file diff --git a/urbanLifelineServ/.bin/database/postgres/sql/admin_routes_architecture.md b/urbanLifelineServ/.bin/database/postgres/sql/admin_routes_architecture.md deleted file mode 100644 index f4f8823a..00000000 --- a/urbanLifelineServ/.bin/database/postgres/sql/admin_routes_architecture.md +++ /dev/null @@ -1,229 +0,0 @@ -# 管理后台路由架构说明 - -## 三层架构设计 - -### 第一层:外层主 Sidebar (platform 服务) -显示在平台主界面的侧边栏,包含用户应用和管理后台入口。 - -**布局**: `SidebarLayout` -**服务**: `platform` - -#### 用户应用入口 -- 泰豪AI助手 (`/aichat`) -- 全部应用 (`/agents`) -- 智能体编排 (`/app/workflow`) - iframe -- 招标助手 (`/app/bidding`) - iframe -- 泰豪小电 (`/app/workcase`) - iframe - -#### 管理后台入口(iframe类型) -1. **平台管理后台** (`/admin/platform`) - - iframe_url: `/platform/admin` - - 权限: `perm_platform_admin` - -2. **智能标书管理后台** (`/admin/bidding`) - - iframe_url: `/bidding/admin` - - 权限: `perm_bidding_admin` - -3. **泰豪小电管理后台** (`/admin/workcase`) - - iframe_url: `/workcase/admin` - - 权限: `perm_workcase_admin` - ---- - -### 第二层:SubSidebarLayout (各服务内部) -每个管理后台的内部二级菜单,使用 `SubSidebarLayout` 布局。 - -**布局**: `SubSidebarLayout` -**类型**: `route`(非 iframe) - ---- - -## Platform 管理后台(第二层) - -**服务**: `platform` -**布局**: `SubSidebarLayout` - -### 视图列表 - -| 序号 | 视图名称 | URL | 组件路径 | 权限 | -|------|---------|-----|----------|------| -| 1 | 数据概览 | `/admin/overview` | `admin/overview/OverviewView.vue` | `perm_platform_admin_overview` | -| 2 | 用户管理 | `/admin/user` | `admin/user/UserView.vue` | `perm_platform_admin_user` | -| 3 | 知识库 | `/admin/knowledge` | `admin/knowledge/KnowledgeView.vue` | `perm_platform_admin_knowledge` | -| 4 | 系统配置 | `/admin/config` | `admin/config/ConfigView.vue` | `perm_platform_admin_config` | - -### 权限配置 -```sql --- 管理后台入口权限 -PERM-0601: perm_platform_admin (platform:admin:view) - --- 内部功能权限 -PERM-0602: perm_platform_admin_overview (platform:admin:overview) -PERM-0603: perm_platform_admin_user (platform:admin:user) -PERM-0604: perm_platform_admin_knowledge (platform:admin:knowledge) -PERM-0605: perm_platform_admin_config (platform:admin:config) -``` - ---- - -## Bidding 管理后台(第二层) - -**服务**: `bidding` -**布局**: `SubSidebarLayout` - -### 权限配置 -```sql --- 管理后台入口权限 -PERM-0611: perm_bidding_admin (bidding:admin:view) -``` - -*注: bidding 管理后台的具体视图需要后续配置* - ---- - -## Workcase 管理后台(第二层) - -**服务**: `workcase` -**布局**: `SubSidebarLayout` - -### 视图列表 - -| 序号 | 视图名称 | URL | 组件路径 | 权限 | -|------|---------|-----|----------|------| -| 1 | 数据概览 | `/admin/overview` | `admin/overview/OverviewView.vue` | `perm_workcase_overview` | -| 2 | 知识库管理 | `/admin/knowledge` | `admin/knowledge/KnowLedgeView.vue` | `perm_workcase_knowledge` | -| 3 | 工单管理 | `/admin/workcase` | `admin/workcase/WorkcaseView.vue` | `perm_workcase_tickets` | -| 4 | 对话数据 | `/admin/customerChat` | `admin/customerChat/CustomerChatView.vue` | `perm_workcase_conversation` | -| 5 | 智能体管理 | `/admin/agent` | `admin/agent/AgentView.vue` | `perm_workcase_agent` | -| 6 | 日志管理 | `/admin/log` | *(目录)* | `perm_workcase_log` | -| 6.1 | └ 知识库日志 | `/admin/log/knowledge` | `admin/log/knowledgeLog/KnowledgeLogView.vue` | `perm_workcase_log` | -| 6.2 | └ 工单日志 | `/admin/log/workcase` | `admin/log/workcaseLog/WorkcaseLogView.vue` | `perm_workcase_log` | -| 6.3 | └ 系统日志 | `/admin/log/system` | `admin/log/systemLog/SystemLogView.vue` | `perm_workcase_log` | - -### 权限配置 -```sql --- 管理后台入口权限 -PERM-0621: perm_workcase_admin (workcase:admin:view) - --- 内部功能权限 -PERM-0622: perm_workcase_overview (workcase:overview:view) -PERM-0623: perm_workcase_knowledge (workcase:knowledge:view) -PERM-0624: perm_workcase_tickets (workcase:tickets:view) -PERM-0625: perm_workcase_conversation (workcase:conversation:view) -PERM-0626: perm_workcase_agent (workcase:agent:view) -PERM-0627: perm_workcase_log (workcase:log:view) -``` - ---- - -## 路由过滤规则 - -### 外层主 Sidebar (platform/SidebarLayout) -显示所有 `service='platform'` 且 `layout='SidebarLayout'` 的视图: -- 用户应用入口(普通路由和iframe) -- 管理后台入口(iframe类型) - -### 内层 SubSidebarLayout -各服务内部,显示 `layout='SubSidebarLayout'` 且 `url.startsWith('/admin')` 的视图: - -#### Platform AdminSidebar -```typescript -service === 'platform' && -layout === 'SubSidebarLayout' && -url.startsWith('/admin') -``` - -#### Bidding AdminSidebar -```typescript -service === 'bidding' && -layout === 'SubSidebarLayout' && -url.startsWith('/admin') -``` - -#### Workcase AdminSidebar -```typescript -service === 'workcase' && -layout === 'SubSidebarLayout' && -url.startsWith('/admin') -``` - ---- - -## 数据库表关系 - -### 视图类型说明 -| type | view_type | 说明 | 示例 | -|------|-----------|------|------| -| 1 | route | 普通路由视图 | 数据概览、用户管理 | -| 1 | iframe | iframe嵌入视图 | 管理后台入口、Dify编排 | -| 0 | route | 目录(不可点击) | 日志管理 | -| 3 | route | 默认首页(隐藏) | 智能客服首页 | - -### 视图权限关联 -每个视图通过 `tb_sys_view_permission` 表关联到对应权限: -- 一个视图可以关联多个权限(AND 逻辑) -- 用户需要拥有所有关联权限才能访问该视图 - -### 角色权限继承 -- **超级管理员**: 自动拥有所有权限 -- **系统管理员**: 拥有所有 module 的管理权限(除删除外) -- **普通用户**: 基础查看权限 + 平台基础菜单访问 -- **访客**: 仅查看权限 + 部分平台菜单访问 - ---- - -## 前端实现注意事项 - -### 1. Layout 组件 -- **SidebarLayout**: 外层主侧边栏(platform 主界面) -- **SubSidebarLayout**: 内层管理侧边栏(各管理后台内部) -- **BlankLayout**: 空白布局(如智能客服首页) - -### 2. iframe 嵌套 -- 外层 platform 的 iframe 菜单 → 加载各服务的管理后台 -- 各服务内部使用 SubSidebarLayout 渲染二级菜单 -- iframe_url 必须正确指向实际服务地址 - -### 3. 路由配置 -所有 `/admin/*` 路径统一用于管理后台: -- platform: `/admin/overview`, `/admin/user` 等 -- bidding: `/admin/*` (待定义) -- workcase: `/admin/overview`, `/admin/knowledge` 等 - -### 4. 权限验证 -前端需要根据 `loginDomain.userViews` 动态渲染菜单: -- 检查 `layout` 字段匹配当前布局 -- 检查 `service` 字段匹配当前服务 -- 检查用户是否拥有关联的权限 - ---- - -## 迁移说明 - -如果从旧的单层架构迁移到三层架构: - -1. **视图迁移** - - 将原 platform 的管理视图改为 `layout='SubSidebarLayout'` - - URL 统一改为 `/admin/*` 格式 - - 在 platform 主侧边栏添加 iframe 入口 - -2. **权限迁移** - - 保留原有权限定义 - - 新增管理后台入口权限(如 `perm_platform_admin`) - - 更新视图权限关联 - -3. **前端组件** - - 创建 SubSidebarLayout 组件(参考 workcase 实现) - - 更新 platform 路由配置支持 `/admin/*` 路径 - - 配置 iframe 路由指向各服务管理后台 - ---- - -## 总结 - -这个三层架构设计实现了: -- ✅ **统一入口**: 所有管理后台统一在 platform 主侧边栏 -- ✅ **独立管理**: 各服务管理后台相互独立,便于维护 -- ✅ **权限细分**: 入口权限 + 功能权限双重控制 -- ✅ **灵活扩展**: 新增服务只需添加 iframe 入口和内部视图 -- ✅ **用户体验**: 统一的导航风格,清晰的层级结构 diff --git a/urbanLifelineServ/.bin/database/postgres/sql/workcase_admin_routes_summary.md b/urbanLifelineServ/.bin/database/postgres/sql/workcase_admin_routes_summary.md deleted file mode 100644 index efebe5f2..00000000 --- a/urbanLifelineServ/.bin/database/postgres/sql/workcase_admin_routes_summary.md +++ /dev/null @@ -1,130 +0,0 @@ -# Workcase 管理端路由配置总结 - -## 已配置的管理端视图 - -### 1. 数据概览 (Overview) -- **view_id**: `view_workcase_admin_overview` -- **URL**: `/admin/overview` -- **组件**: `admin/overview/OverviewView.vue` -- **图标**: DataLine -- **权限**: `perm_workcase_overview` (workcase:overview:view) -- **描述**: 泰豪小电数据概览 - -### 2. 知识库管理 (Knowledge) -- **view_id**: `view_workcase_admin_knowledge` -- **URL**: `/admin/knowledge` -- **组件**: `admin/knowledge/KnowLedgeView.vue` -- **图标**: Document -- **权限**: `perm_workcase_knowledge` (workcase:knowledge:view) -- **描述**: 知识库文档管理 - -### 3. 工单管理 (Tickets) -- **view_id**: `view_workcase_admin_tickets` -- **URL**: `/admin/workcase` -- **组件**: `admin/workcase/WorkcaseView.vue` -- **图标**: Tickets -- **权限**: `perm_workcase_tickets` (workcase:tickets:view) -- **描述**: 客服工单管理 - -### 4. 对话数据 (Conversation) -- **view_id**: `view_workcase_admin_conversation` -- **URL**: `/admin/customerChat` -- **组件**: `admin/customerChat/CustomerChatView.vue` -- **图标**: ChatDotRound -- **权限**: `perm_workcase_conversation` (workcase:conversation:view) -- **描述**: 客户对话数据管理 - -### 5. 智能体管理 (Agent) -- **view_id**: `view_workcase_admin_agent` -- **URL**: `/admin/agent` -- **组件**: `admin/agent/AgentView.vue` -- **图标**: Service -- **权限**: `perm_workcase_agent` (workcase:agent:view) -- **描述**: 智能体配置管理 - -### 6. 日志管理 (Logs) -#### 6.1 日志管理目录 -- **view_id**: `view_workcase_admin_log` -- **URL**: `/admin/log` -- **组件**: NULL (目录类型) -- **图标**: List -- **type**: 0 (目录) -- **权限**: `perm_workcase_log` (workcase:log:view) -- **描述**: 日志管理目录 - -#### 6.2 知识库日志 -- **view_id**: `view_workcase_admin_log_knowledge` -- **parent_id**: `view_workcase_admin_log` -- **URL**: `/admin/log/knowledge` -- **组件**: `admin/log/knowledgeLog/KnowledgeLogView.vue` -- **图标**: Document -- **权限**: `perm_workcase_log` (workcase:log:view) -- **描述**: 知识库操作日志 - -#### 6.3 工单日志 -- **view_id**: `view_workcase_admin_log_workcase` -- **parent_id**: `view_workcase_admin_log` -- **URL**: `/admin/log/workcase` -- **组件**: `admin/log/workcaseLog/WorkcaseLogView.vue` -- **图标**: Tickets -- **权限**: `perm_workcase_log` (workcase:log:view) -- **描述**: 工单操作日志 - -#### 6.4 系统日志 -- **view_id**: `view_workcase_admin_log_system` -- **parent_id**: `view_workcase_admin_log` -- **URL**: `/admin/log/system` -- **组件**: `admin/log/systemLog/SystemLogView.vue` -- **图标**: Setting -- **权限**: `perm_workcase_log` (workcase:log:view) -- **描述**: 系统运行日志 - -## 权限配置 - -### 新增权限 -1. **PERM-0601**: `perm_workcase_overview` - 数据概览 -2. **PERM-0602**: `perm_workcase_knowledge` - 知识库管理 -3. **PERM-0603**: `perm_workcase_tickets` - 工单管理 -4. **PERM-0604**: `perm_workcase_conversation` - 对话数据 -5. **PERM-0605**: `perm_workcase_agent` - 智能体管理 -6. **PERM-0606**: `perm_workcase_log` - 日志管理 - -### 视图权限关联 -- VP-W101 ~ VP-W105: 管理端主功能视图 -- VP-W106 ~ VP-W109: 日志管理视图(含父级和3个子级) - -## 布局配置 -- **Layout**: `SubSidebarLayout` -- **Service**: `workcase` -- **Type**: 1 (菜单项) / 0 (目录) -- **View Type**: `route` - -## Order Number 排序 -- 110: 数据概览 -- 120: 知识库管理 -- 130: 工单管理 -- 140: 对话数据 -- 150: 智能体管理 -- 160: 日志管理(父级) - - 161: 知识库日志 - - 162: 工单日志 - - 163: 系统日志 - -## 路由过滤规则 -在 `SubSidebarLayout.vue` 的 `loadMenuFromStorage()` 中: -```typescript -const sidebarViews = userViews.filter((view: any) => - view.layout === 'SidebarLayout' && // 使用 SidebarLayout 布局 - !view.parentId && // 顶级菜单 - view.type === 1 && // 菜单类型 - view.service === 'workcase' && // workcase 服务 - view.url?.startsWith('/admin') // admin 路由 -) -``` - -## 注意事项 -1. 所有管理端路由都以 `/admin/` 开头 -2. 日志管理使用父子级结构(type=0 的目录 + type=1 的子菜单) -3. 所有视图都使用 `SubSidebarLayout` 布局 -4. 权限都归属于 `module_workcase` 模块 -5. 超级管理员和系统管理员默认拥有所有 workcase 管理端权限 diff --git a/urbanLifelineServ/ai/src/main/resources/application-dev.yml b/urbanLifelineServ/ai/src/main/resources/application-dev.yml deleted file mode 100644 index 310d49ba..00000000 --- a/urbanLifelineServ/ai/src/main/resources/application-dev.yml +++ /dev/null @@ -1,98 +0,0 @@ -# ================== Server ================== -server: - port: 8090 - # servlet: - # context-path: /urban-lifeline/agent - -# ================== Auth ==================== -auth: - enabled: true - gateway-mode: true - whitelist: - - /swagger-ui/** - - /swagger-ui.html - - /v3/api-docs/** - - /webjars/** - - /favicon.ico - - /error - - /actuator/health - - /actuator/info - - /ai/chat/** # AI对话,有非系统用户对话的接口,无登录状态 - -security: - aes: - # AES-256 密钥(Base64编码,必须与所有服务保持一致) - # 警告:这是开发环境密钥,生产环境请使用密钥管理系统 - secret-key: MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI= - -# ================== Spring ================== -spring: - application: - name: ai-service - # 文件上传配置 - servlet: - multipart: - enabled: true - max-file-size: 500MB - max-request-size: 500MB - # ================== Spring Cloud Nacos ================== - cloud: - nacos: - discovery: - server-addr: 127.0.0.1:8848 - namespace: dev - group: DEFAULT_GROUP - - # ================== DataSource ================== - datasource: - url: jdbc:postgresql://127.0.0.1:5432/urban_lifeline - username: postgres - password: postgres - driver-class-name: org.postgresql.Driver - - # ================== Redis ================== - data: - redis: - host: 127.0.0.1 - port: 6379 - database: 0 - # password: "" - -# ================== SpringDoc ================== -springdoc: - api-docs: - enabled: true - path: /v3/api-docs - swagger-ui: - enabled: true - path: /swagger-ui.html - group-configs: - - group: 'default' - display-name: 'AI代理服务 API' - paths-to-match: '/**' - -# ================== Dubbo + Nacos ================== -dubbo: - application: - name: urban-lifeline-agent - qos-enable: false - protocol: - payload: 110100480 - name: dubbo - port: -1 - registry: - address: nacos://127.0.0.1:8848 - scan: - base-packages: org.xyzh.ai.service.impl - -# ================== MyBatis ================== -mybatis-plus: - mapper-locations: classpath:mapper/**/*.xml - type-aliases-package: org.xyzh.common.dto, org.xyzh.api -logging: - config: classpath:log4j2.xml - charset: - console: UTF-8 - file: UTF-8 - level: - org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping: TRACE \ No newline at end of file diff --git a/urbanLifelineServ/ai/src/main/resources/application.yml b/urbanLifelineServ/ai/src/main/resources/application.yml index 35253da8..8c201a0e 100644 --- a/urbanLifelineServ/ai/src/main/resources/application.yml +++ b/urbanLifelineServ/ai/src/main/resources/application.yml @@ -1,10 +1,17 @@ -# ================== Server ================== +# ================== AI 服务配置 ================== server: - port: 8190 - # servlet: - # context-path: /urban-lifeline/agent # 微服务架构下,context-path由Gateway管理 + port: 8090 -# ================== Auth ==================== +spring: + application: + name: ai-service + servlet: + multipart: + enabled: true + max-file-size: 500MB + max-request-size: 500MB + +# ================== Auth ================== auth: enabled: true gateway-mode: true @@ -17,79 +24,21 @@ auth: - /error - /actuator/health - /actuator/info - - /ai/chat/** # AI对话,有非系统用户对话的接口,无登录状态 - -security: - aes: - # AES-256 密钥(Base64编码,必须与所有服务保持一致) - # 警告:这是开发环境密钥,生产环境请使用密钥管理系统 - secret-key: MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI= - -# ================== Spring ================== -spring: - application: - name: ai-service - - # ================== Spring Cloud Nacos ================== - cloud: - nacos: - discovery: - server-addr: 127.0.0.1:8848 - namespace: dev - group: DEFAULT_GROUP - - # ================== DataSource ================== - datasource: - url: jdbc:postgresql://127.0.0.1:5432/urban_lifeline - username: postgres - password: postgres - driver-class-name: org.postgresql.Driver - - # ================== Redis ================== - data: - redis: - host: 127.0.0.1 # 如果是 docker 跑的 redis,按实际 host / 端口改 - port: 6379 - database: 0 - password: 123456 # 如果有密码就填上,没密码可以去掉这一行 + - /ai/chat/** # ================== SpringDoc ================== springdoc: - api-docs: - enabled: true - path: /v3/api-docs - swagger-ui: - enabled: true - path: /swagger-ui.html group-configs: - group: 'default' display-name: 'AI代理服务 API' paths-to-match: '/**' -# ================== Dubbo + Nacos ================== +# ================== Dubbo ================== dubbo: application: name: urban-lifeline-agent qos-enable: false protocol: - payload: 110100480 - name: dubbo - port: -1 - registry: - address: nacos://127.0.0.1:8848 + payload: 110100480 scan: base-packages: org.xyzh.ai.service.impl - -# ================== MyBatis ================== -mybatis-plus: - mapper-locations: classpath:mapper/**/*.xml - type-aliases-package: org.xyzh.common.dto, org.xyzh.api - -# ================== Logging ================== -logging: - config: classpath:log4j2.xml - charset: - console: UTF-8 - file: UTF-8 - level: - org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping: TRACE diff --git a/urbanLifelineServ/auth/src/main/resources/application.yml b/urbanLifelineServ/auth/src/main/resources/application.yml index 1f51ec21..e6ad1909 100644 --- a/urbanLifelineServ/auth/src/main/resources/application.yml +++ b/urbanLifelineServ/auth/src/main/resources/application.yml @@ -1,97 +1,35 @@ -# ================== Server ================== +# ================== Auth 认证服务配置 ================== server: - port: 8181 - # servlet: - # context-path: /urban-lifeline/auth # 微服务架构下,context-path由Gateway管理,服务本身不需要设置 + port: 8081 -# ================== Auth ==================== -auth: - enabled: false # 认证服务自己不需要认证 - gateway-mode: false # 不使用gateway模式(auth服务作为独立服务) - whitelist: - - /** # 认证服务的所有接口都放行 -security: - aes: - # AES-256 密钥(Base64编码,必须与所有服务保持一致) - # 警告:这是开发环境密钥,生产环境请使用密钥管理系统 - secret-key: MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI= -# ================== Spring ================== spring: application: name: auth-service - - # ================== Spring Cloud Nacos ================== - cloud: - nacos: - discovery: - server-addr: 127.0.0.1:8848 - namespace: dev - group: DEFAULT_GROUP - - # ================== DataSource ================== - datasource: - url: jdbc:postgresql://127.0.0.1:5432/urban_lifeline - username: postgres - password: postgres - driver-class-name: org.postgresql.Driver - # ================== Redis ================== - data: - redis: - host: 127.0.0.1 # 如果是 docker 跑的 redis,按实际 host / 端口改 - port: 6379 - database: 0 - password: 123456 # 如果有密码就填上,没密码可以去掉这一行 +# ================== Auth ================== +auth: + enabled: false # 认证服务自己不需要认证 + gateway-mode: false + whitelist: + - /** # ================== SpringDoc ================== springdoc: - api-docs: - enabled: true - path: /v3/api-docs - swagger-ui: - enabled: true - path: /swagger-ui.html - try-it-out-enabled: true - show-common-extensions: true - show-extensions: true - show-request-duration: true - filter: true - tags-sorter: alpha - operations-sorter: alpha group-configs: - group: 'default' display-name: '认证服务 API' paths-to-match: '/**' -# ================== Dubbo + Nacos ================== +# ================== Dubbo ================== dubbo: application: name: urban-lifeline-auth qos-enable: false - protocol: - name: dubbo - port: -1 - registry: - address: nacos://127.0.0.1:8848 scan: base-packages: org.xyzh.auth.service.impl -# ================== MyBatis ================== -mybatis-plus: - mapper-locations: classpath:mapper/**/*.xml - type-aliases-package: org.xyzh.common.dto, org.xyzh.api - -# ================== JWT Configuration ================== +# ================== JWT ================== jwt: - secret: urban-lifeline-secret-key-2025-xyzh + secret: ${JWT_SECRET:urban-lifeline-secret-key-2025-xyzh} expiration: 86400 # 24小时(秒) refresh-expiration: 604800 # 7天(秒) - -# ================== Logging ================== -logging: - config: classpath:log4j2.xml - charset: - console: UTF-8 - file: UTF-8 - level: - org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping: TRACE diff --git a/urbanLifelineServ/bidding/src/main/resources/application.yml b/urbanLifelineServ/bidding/src/main/resources/application.yml index f7b78cb8..27fa9285 100644 --- a/urbanLifelineServ/bidding/src/main/resources/application.yml +++ b/urbanLifelineServ/bidding/src/main/resources/application.yml @@ -1,89 +1,36 @@ -# ================== Server ================== +# ================== Bidding 招投标服务配置 ================== server: - port: 8186 - # servlet: - # context-path: /urban-lifeline/bidding # 微服务架构下,context-path由Gateway管理 + port: 8087 -# ================== Auth ==================== -urban-lifeline: - auth: - enabled: true - whitelist: - - /swagger-ui/** - - /swagger-ui.html - - /v3/api-docs/** - - /webjars/** - - /favicon.ico - - /error - - /actuator/health - - /actuator/info - -security: - aes: - secret-key: 1234567890qwer - -# ================== Spring ================== spring: application: name: bidding-service - - # ================== Spring Cloud Nacos ================== - cloud: - nacos: - discovery: - server-addr: 127.0.0.1:8848 - namespace: dev - group: DEFAULT_GROUP - - # ================== DataSource ================== - datasource: - url: jdbc:postgresql://127.0.0.1:5432/urban_lifeline - username: postgres - password: postgres - driver-class-name: org.postgresql.Driver - # ================== Redis ================== - data: - redis: - host: 127.0.0.1 # 如果是 docker 跑的 redis,按实际 host / 端口改 - port: 6379 - database: 0 - password: 123456 # 如果有密码就填上,没密码可以去掉这一行 +# ================== Auth ================== +auth: + enabled: true + gateway-mode: true + whitelist: + - /swagger-ui/** + - /swagger-ui.html + - /v3/api-docs/** + - /webjars/** + - /favicon.ico + - /error + - /actuator/health + - /actuator/info # ================== SpringDoc ================== springdoc: - api-docs: - enabled: true - path: /v3/api-docs - swagger-ui: - enabled: true - path: /swagger-ui.html group-configs: - group: 'default' display-name: '招投标服务 API' paths-to-match: '/**' -# ================== Dubbo + Nacos ================== +# ================== Dubbo ================== dubbo: application: name: urban-lifeline-bidding qos-enable: false - protocol: - name: dubbo - port: -1 - registry: - address: nacos://127.0.0.1:8848 scan: base-packages: org.xyzh.bidding.service.impl - -# ================== MyBatis ================== -mybatis-plus: - mapper-locations: classpath:mapper/**/*.xml - type-aliases-package: org.xyzh.common.dto, org.xyzh.api - -# ================== Logging ================== -logging: - config: classpath:log4j2.xml - charset: - console: UTF-8 - file: UTF-8 diff --git a/urbanLifelineServ/common/common-core/src/main/resources/bootstrap.yml b/urbanLifelineServ/common/common-core/src/main/resources/bootstrap.yml new file mode 100644 index 00000000..bc9b9303 --- /dev/null +++ b/urbanLifelineServ/common/common-core/src/main/resources/bootstrap.yml @@ -0,0 +1,64 @@ +# ================================================ +# Urban Lifeline - 通用 Bootstrap 配置 +# 所有微服务共享的基础配置 +# ================================================ + +# ================== Spring Cloud Nacos ================== +spring: + cloud: + nacos: + discovery: + server-addr: ${NACOS_SERVER_ADDR:127.0.0.1:8848} + namespace: ${NACOS_NAMESPACE:dev} + group: ${NACOS_GROUP:DEFAULT_GROUP} + + # ================== DataSource ================== + datasource: + url: ${DB_URL:jdbc:postgresql://127.0.0.1:5432/urban_lifeline} + username: ${DB_USERNAME:postgres} + password: ${DB_PASSWORD:postgres} + driver-class-name: org.postgresql.Driver + + # ================== Redis ================== + data: + redis: + host: ${REDIS_HOST:127.0.0.1} + port: ${REDIS_PORT:6379} + database: ${REDIS_DATABASE:0} + password: ${REDIS_PASSWORD:} + +# ================== Security AES ================== +security: + aes: + # AES-256 密钥(Base64编码,必须与所有服务保持一致) + # 警告:这是开发环境密钥,生产环境请使用密钥管理系统 + secret-key: ${AES_SECRET_KEY:MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=} + +# ================== Dubbo ================== +dubbo: + protocol: + name: dubbo + port: -1 + registry: + address: nacos://${NACOS_SERVER_ADDR:127.0.0.1:8848} + +# ================== MyBatis-Plus ================== +mybatis-plus: + mapper-locations: classpath:mapper/**/*.xml + type-aliases-package: org.xyzh.common.dto, org.xyzh.api + +# ================== SpringDoc 基础配置 ================== +springdoc: + api-docs: + enabled: true + path: /v3/api-docs + swagger-ui: + enabled: true + path: /swagger-ui.html + +# ================== Logging ================== +logging: + config: classpath:log4j2.xml + charset: + console: UTF-8 + file: UTF-8 diff --git a/urbanLifelineServ/crontab/src/main/resources/application.yml b/urbanLifelineServ/crontab/src/main/resources/application.yml index f2258367..464ea2e0 100644 --- a/urbanLifelineServ/crontab/src/main/resources/application.yml +++ b/urbanLifelineServ/crontab/src/main/resources/application.yml @@ -1,80 +1,35 @@ -# ================== Server ================== +# ================== Crontab 定时任务服务配置 ================== server: - port: 8189 - # servlet: - # context-path: /urban-lifeline/crontab # 微服务架构下,context-path由Gateway管理 + port: 8086 -# ================== Auth ==================== -urban-lifeline: - auth: - enabled: false # 定时任务服务通常不需要认证 - -security: - aes: - secret-key: 1234567890qwer - -# ================== Spring ================== spring: application: name: crontab-service - - # ================== Spring Cloud Nacos ================== - cloud: - nacos: - discovery: - server-addr: 127.0.0.1:8848 - namespace: dev - group: DEFAULT_GROUP - - # ================== DataSource ================== - datasource: - url: jdbc:postgresql://127.0.0.1:5432/urban_lifeline - username: postgres - password: postgres - driver-class-name: org.postgresql.Driver - # ================== Redis ================== - data: - redis: - host: 127.0.0.1 # 如果是 docker 跑的 redis,按实际 host / 端口改 - port: 6379 - database: 0 - password: 123456 # 如果有密码就填上,没密码可以去掉这一行 +# ================== Auth ================== +auth: + enabled: false # 定时任务服务不需要认证 + whitelist: + - /swagger-ui/** + - /swagger-ui.html + - /v3/api-docs/** + - /webjars/** + - /favicon.ico + - /error + - /actuator/health + - /actuator/info # ================== SpringDoc ================== springdoc: - api-docs: - enabled: true - path: /v3/api-docs - swagger-ui: - enabled: true - path: /swagger-ui.html group-configs: - group: 'default' display-name: '定时任务服务 API' paths-to-match: '/**' -# ================== Dubbo + Nacos ================== +# ================== Dubbo ================== dubbo: application: name: urban-lifeline-crontab qos-enable: false - protocol: - name: dubbo - port: -1 - registry: - address: nacos://127.0.0.1:8848 scan: base-packages: org.xyzh.crontab.service.impl - -# ================== MyBatis ================== -mybatis-plus: - mapper-locations: classpath:mapper/**/*.xml - type-aliases-package: org.xyzh.common.dto, org.xyzh.api - -# ================== Logging ================== -logging: - config: classpath:log4j2.xml - charset: - console: UTF-8 - file: UTF-8 diff --git a/urbanLifelineServ/file/src/main/resources/application.yml b/urbanLifelineServ/file/src/main/resources/application.yml index 0570b2ea..b93b3be9 100644 --- a/urbanLifelineServ/file/src/main/resources/application.yml +++ b/urbanLifelineServ/file/src/main/resources/application.yml @@ -1,10 +1,17 @@ -# ================== Server ================== +# ================== File 文件服务配置 ================== server: - port: 8184 - # servlet: - # context-path: /urban-lifeline/file # 微服务架构下,context-path由Gateway管理 + port: 8084 -# ================== Auth ==================== +spring: + application: + name: file-service + servlet: + multipart: + enabled: true + max-file-size: 500MB + max-request-size: 500MB + +# ================== Auth ================== auth: enabled: true gateway-mode: true @@ -19,83 +26,19 @@ auth: - /actuator/info - /file/download/** -security: - aes: - # AES-256 密钥(Base64编码,必须与所有服务保持一致) - # 警告:这是开发环境密钥,生产环境请使用密钥管理系统 - secret-key: MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI= - -# ================== Spring ================== -spring: - application: - name: file-service - # ================== Spring Cloud Nacos ================== - cloud: - nacos: - discovery: - server-addr: 127.0.0.1:8848 - namespace: dev - group: DEFAULT_GROUP - - # ================== DataSource ================== - datasource: - url: jdbc:postgresql://127.0.0.1:5432/urban_lifeline - username: postgres - password: postgres - driver-class-name: org.postgresql.Driver - - # ================== Redis ================== - data: - redis: - host: 127.0.0.1 # 如果是 docker 跑的 redis,按实际 host / 端口改 - port: 6379 - database: 0 - password: 123456 # 如果有密码就填上,没密码可以去掉这一行 - - # ================== 文件上传配置 ================== - servlet: - multipart: - enabled: true - max-file-size: 500MB - max-request-size: 500MB - # ================== SpringDoc ================== springdoc: - api-docs: - enabled: true - path: /v3/api-docs - swagger-ui: - enabled: true - path: /swagger-ui.html group-configs: - group: 'default' display-name: '文件服务 API' paths-to-match: '/**' -# ================== Dubbo + Nacos ================== +# ================== Dubbo ================== dubbo: application: name: urban-lifeline-file qos-enable: false protocol: - payload: 110100480 - name: dubbo - port: -1 - registry: - address: nacos://127.0.0.1:8848 + payload: 110100480 scan: base-packages: org.xyzh.file.service.impl - -# ================== MyBatis ================== -mybatis-plus: - mapper-locations: classpath:mapper/**/*.xml - type-aliases-package: org.xyzh.common.dto, org.xyzh.api - -# ================== Logging ================== -logging: - config: classpath:log4j2.xml - charset: - console: UTF-8 - file: UTF-8 - level: - org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping: TRACE diff --git a/urbanLifelineServ/gateway/src/main/resources/application-dev.yml b/urbanLifelineServ/gateway/src/main/resources/application-dev.yml deleted file mode 100644 index fcd44792..00000000 --- a/urbanLifelineServ/gateway/src/main/resources/application-dev.yml +++ /dev/null @@ -1,12 +0,0 @@ -# 开发环境专用配置 -# 注意:不要在这里配置routes,会覆盖application.yml的配置 -# 路由配置统一在application.yml中管理 - -# 开发环境日志 -logging: - config: classpath:log4j2.xml - charset: - console: UTF-8 - file: UTF-8 - level: - org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping: TRACE diff --git a/urbanLifelineServ/gateway/src/main/resources/application.yml b/urbanLifelineServ/gateway/src/main/resources/application.yml index acf9fac1..fbbcfe09 100644 --- a/urbanLifelineServ/gateway/src/main/resources/application.yml +++ b/urbanLifelineServ/gateway/src/main/resources/application.yml @@ -1,50 +1,40 @@ +# ================== Gateway 服务配置 ================== server: - port: 8180 + port: 8080 spring: application: name: gateway-service - # Gateway 必须使用 reactive 模式(WebFlux),不能使用 Spring MVC + # Gateway 必须使用 reactive 模式 main: web-application-type: reactive - # 配置中心 cloud: nacos: - discovery: - server-addr: ${NACOS_SERVER_ADDR:localhost:8848} - namespace: dev - group: DEFAULT_GROUP config: - enabled: false # 禁用Nacos配置中心,使用本地配置 - server-addr: ${NACOS_SERVER_ADDR:localhost:8848} - file-extension: yml - namespace: dev - group: DEFAULT_GROUP + enabled: false # Gateway 路由配置 gateway: - # 服务发现路由(自动路由) discovery: locator: - enabled: false # 关闭自动路由,使用手动配置 + enabled: false - # 手动配置路由 routes: - # ==================== 认证服务路由 ==================== + # 认证服务 - id: auth-service uri: lb://auth-service predicates: - Path=/urban-lifeline/auth/** filters: - - StripPrefix=1 # 去掉前缀:/urban-lifeline/auth/login → /auth/login + - StripPrefix=1 - name: RequestRateLimiter args: redis-rate-limiter.replenishRate: 100 redis-rate-limiter.burstCapacity: 200 - # ==================== 系统服务路由 ==================== + # 系统服务 - id: system-service uri: lb://system-service predicates: @@ -52,7 +42,7 @@ spring: filters: - StripPrefix=1 - # ==================== 日志服务路由 ==================== + # 日志服务 - id: log-service uri: lb://log-service predicates: @@ -60,7 +50,7 @@ spring: filters: - StripPrefix=1 - # ==================== 文件服务路由 ==================== + # 文件服务 - id: file-service uri: lb://file-service predicates: @@ -68,7 +58,7 @@ spring: filters: - StripPrefix=1 - # ==================== 消息服务路由 ==================== + # 消息服务 - id: message-service uri: lb://message-service predicates: @@ -76,7 +66,7 @@ spring: filters: - StripPrefix=1 - # ==================== 招投标服务路由 ==================== + # 招投标服务 - id: bidding-service uri: lb://bidding-service predicates: @@ -84,7 +74,7 @@ spring: filters: - StripPrefix=1 - # ==================== 平台服务路由 ==================== + # 平台服务 - id: platform-service uri: lb://platform-service predicates: @@ -92,7 +82,7 @@ spring: filters: - StripPrefix=1 - # ==================== 工单服务 WebSocket 路由 ==================== + # 工单服务 WebSocket - id: workcase-websocket uri: lb:ws://workcase-service predicates: @@ -100,7 +90,7 @@ spring: filters: - StripPrefix=1 - # ==================== 工单服务路由 ==================== + # 工单服务 - id: workcase-service uri: lb://workcase-service predicates: @@ -108,7 +98,7 @@ spring: filters: - StripPrefix=1 - # ==================== 定时任务服务路由 ==================== + # 定时任务服务 - id: crontab-service uri: lb://crontab-service predicates: @@ -116,7 +106,7 @@ spring: filters: - StripPrefix=1 - # ==================== AI 服务路由 ==================== + # AI 服务 - id: ai-service uri: lb://ai-service predicates: @@ -129,28 +119,14 @@ spring: cors-configurations: '[/**]': allowedOriginPatterns: "*" - allowedMethods: - - GET - - POST - - PUT - - DELETE - - OPTIONS + allowedMethods: [GET, POST, PUT, DELETE, OPTIONS] allowedHeaders: "*" allowCredentials: true maxAge: 3600 - datasource: - # 按你的实际库名改一下,比如 urban-lifeline_system - url: jdbc:postgresql://127.0.0.1:5432/urban_lifeline # 换成你的 PG 库名 - username: postgres # PG 用户 - password: postgres # PG 密码 - driver-class-name: org.postgresql.Driver - # Redis 配置(用于限流、缓存) + + # Redis 连接池配置(Gateway 限流用) data: redis: - host: ${REDIS_HOST:localhost} - port: ${REDIS_PORT:6379} - password: 123456 - database: 0 timeout: 5000ms lettuce: pool: @@ -159,18 +135,15 @@ spring: max-idle: 10 min-idle: 5 -# 认证配置 +# ================== Gateway 认证配置 ================== auth: enabled: true - # gateway-mode 是给下游微服务用的,gateway本身不需要此配置 token-header: Authorization token-prefix: "Bearer " - # 认证接口白名单(login/logout/captcha/refresh) login-path: /urban-lifeline/auth/login logout-path: /urban-lifeline/auth/logout captcha-path: /urban-lifeline/auth/captcha refresh-path: /urban-lifeline/auth/refresh - # 通用白名单(Swagger、健康检查等) whitelist: - /actuator/** - /v3/api-docs/** @@ -180,20 +153,14 @@ auth: - /doc.html - /favicon.ico - /error - # 各服务的 Swagger 文档 - /urban-lifeline/*/v3/api-docs/** - /urban-lifeline/*/swagger-ui/** - # file 服务白名单 - /urban-lifeline/file/download/** - # ai 服务白名单 - /urban-lifeline/ai/chat/** - /urban-lifeline/system/guest/identify - # workcase 会议入口白名单(支持URL参数token认证) - /urban-lifeline/workcase/meeting/*/entry -security: - aes: - secret-key: MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI= # Base64 编码,32字节(256位) -# Actuator 监控端点 + +# ================== Actuator ================== management: endpoints: web: @@ -202,12 +169,3 @@ management: endpoint: health: show-details: always - -# 日志配置(详细配置见 log4j2.xml) -logging: - config: classpath:log4j2.xml - charset: - console: UTF-8 - file: UTF-8 - level: - org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping: TRACE \ No newline at end of file diff --git a/urbanLifelineServ/message/src/main/resources/application.yml b/urbanLifelineServ/message/src/main/resources/application.yml index 43cac2fe..1d2c6bc7 100644 --- a/urbanLifelineServ/message/src/main/resources/application.yml +++ b/urbanLifelineServ/message/src/main/resources/application.yml @@ -1,11 +1,12 @@ -# ================== Server ================== +# ================== Message 消息服务配置 ================== server: - port: 8185 - # servlet: - # context-path: /urban-lifeline/message # 微服务架构下,context-path由Gateway管理 + port: 8085 -# ================== Auth ==================== +spring: + application: + name: message-service +# ================== Auth ================== auth: enabled: true gateway-mode: true @@ -18,74 +19,18 @@ auth: - /error - /actuator/health - /actuator/info - -security: - aes: - secret-key: 1234567890qwer -# ================== Spring ================== -spring: - application: - name: message-service - - # ================== Spring Cloud Nacos ================== - cloud: - nacos: - discovery: - server-addr: 127.0.0.1:8848 - namespace: dev - group: DEFAULT_GROUP - - # ================== DataSource ================== - datasource: - url: jdbc:postgresql://127.0.0.1:5432/urban_lifeline - username: postgres - password: postgres - driver-class-name: org.postgresql.Driver - - # ================== Redis ================== - data: - redis: - host: 127.0.0.1 # 如果是 docker 跑的 redis,按实际 host / 端口改 - port: 6379 - database: 0 - password: 123456 # 如果有密码就填上,没密码可以去掉这一行 # ================== SpringDoc ================== springdoc: - api-docs: - enabled: true - path: /v3/api-docs - swagger-ui: - enabled: true - path: /swagger-ui.html group-configs: - group: 'default' display-name: '消息服务 API' paths-to-match: '/**' -# ================== Dubbo + Nacos ================== +# ================== Dubbo ================== dubbo: application: name: urban-lifeline-message qos-enable: false - protocol: - name: dubbo - port: -1 - registry: - address: nacos://127.0.0.1:8848 scan: base-packages: org.xyzh.message.service.impl - -# ================== MyBatis ================== -mybatis-plus: - mapper-locations: classpath:mapper/**/*.xml - type-aliases-package: org.xyzh.common.dto, org.xyzh.api - -# ================== Logging ================== -logging: - config: classpath:log4j2.xml - charset: - console: UTF-8 - file: UTF-8 - level: - org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping: TRACE diff --git a/urbanLifelineServ/platform/src/main/resources/application.yml b/urbanLifelineServ/platform/src/main/resources/application.yml index 04a39beb..6bc62186 100644 --- a/urbanLifelineServ/platform/src/main/resources/application.yml +++ b/urbanLifelineServ/platform/src/main/resources/application.yml @@ -1,11 +1,12 @@ -# ================== Server ================== +# ================== Platform 平台服务配置 ================== server: - port: 8187 - # servlet: - # context-path: /urban-lifeline/platform # 微服务架构下,context-path由Gateway管理 + port: 8089 -# ================== Auth ==================== +spring: + application: + name: platform-service +# ================== Auth ================== auth: enabled: true gateway-mode: true @@ -19,74 +20,17 @@ auth: - /actuator/health - /actuator/info -security: - aes: - secret-key: 1234567890qwer - -# ================== Spring ================== -spring: - application: - name: platform-service - - # ================== Spring Cloud Nacos ================== - cloud: - nacos: - discovery: - server-addr: 127.0.0.1:8848 - namespace: dev - group: DEFAULT_GROUP - - # ================== DataSource ================== - datasource: - url: jdbc:postgresql://127.0.0.1:5432/urban_lifeline - username: postgres - password: postgres - driver-class-name: org.postgresql.Driver - - # ================== Redis ================== - data: - redis: - host: 127.0.0.1 # 如果是 docker 跑的 redis,按实际 host / 端口改 - port: 6379 - database: 0 - password: 123456 # 如果有密码就填上,没密码可以去掉这一行 - # ================== SpringDoc ================== springdoc: - api-docs: - enabled: true - path: /v3/api-docs - swagger-ui: - enabled: true - path: /swagger-ui.html group-configs: - group: 'default' display-name: '平台服务 API' paths-to-match: '/**' -# ================== Dubbo + Nacos ================== +# ================== Dubbo ================== dubbo: application: name: urban-lifeline-platform qos-enable: false - protocol: - name: dubbo - port: -1 - registry: - address: nacos://127.0.0.1:8848 scan: base-packages: org.xyzh.platform.service.impl - -# ================== MyBatis ================== -mybatis-plus: - mapper-locations: classpath:mapper/**/*.xml - type-aliases-package: org.xyzh.common.dto, org.xyzh.api - -# ================== Logging ================== -logging: - config: classpath:log4j2.xml - charset: - console: UTF-8 - file: UTF-8 - level: - org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping: TRACE diff --git a/urbanLifelineServ/system/src/main/resources/application-dev.yml b/urbanLifelineServ/system/src/main/resources/application-dev.yml deleted file mode 100644 index f0acf0bc..00000000 --- a/urbanLifelineServ/system/src/main/resources/application-dev.yml +++ /dev/null @@ -1,124 +0,0 @@ -# ================== Server ================== -server: - port: 8182 - # servlet: - # context-path: /urban-lifeline/system # 微服务架构下,context-path由Gateway管理 -# ================== Auth ==================== - -auth: - enabled: true - gateway-mode: true - # 认证接口:可以按服务自定义 - login-path: /urban-lifeline/auth/login - logout-path: /urban-lifeline/auth/logout - captcha-path: /urban-lifeline/auth/captcha - refresh-path: /urban-lifeline/auth/refresh - - # 通用白名单(非认证接口) - whitelist: - # Swagger/OpenAPI 文档相关(建议不带 context-path) - - /swagger-ui/** - - /swagger-ui.html - - /v3/api-docs/** - - /webjars/** - - # 静态资源 - - /favicon.ico - - /error - - # 健康检查 - - /actuator/health - - /actuator/info - - /system/guest/identify - # 其他需要放行的路径 - # - /public/** - # - /api/public/** - -security: - aes: - # AES-256 密钥(Base64编码) - # 警告:这是开发环境密钥,生产环境请使用密钥管理系统 - secret-key: MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI= - -# ================== Spring ================== -spring: - application: - name: system-service - - # ================== Spring Cloud Nacos ================== - cloud: - nacos: - discovery: - server-addr: 127.0.0.1:8848 - namespace: dev - group: DEFAULT_GROUP - - # ================== DataSource ================== - datasource: - # 按你的实际库名改一下,比如 urban-lifeline_system - url: jdbc:postgresql://127.0.0.1:5432/urban_lifeline # 换成你的 PG 库名 - username: postgres # PG 用户 - password: postgres # PG 密码 - driver-class-name: org.postgresql.Driver - - # ================== Redis ================== - data: - redis: - host: 127.0.0.1 # 如果是 docker 跑的 redis,按实际 host / 端口改 - port: 6379 - database: 0 - password: 123456 # 如果有密码就填上,没密码可以去掉这一行 -# ================== SpringDoc ================== -springdoc: - api-docs: - enabled: true - path: /v3/api-docs - swagger-ui: - enabled: true - path: /swagger-ui.html - try-it-out-enabled: true - show-common-extensions: true - show-extensions: true - show-request-duration: true - filter: true - tags-sorter: alpha - operations-sorter: alpha - group-configs: - - group: 'default' - display-name: '系统服务 API' - paths-to-match: '/**' - -# ================== Dubbo + Nacos 注册中心 ================== -dubbo: - application: - name: urban-lifeline-system - qos-enable: false - - protocol: - name: dubbo - port: -1 # -1 表示随机端口,避免端口冲突;也可以写死一个端口 - - registry: - # Nacos 注册中心地址:使用 docker-compose 中映射到宿主机的 8848 端口 - address: nacos://127.0.0.1:8848 - # 如果 Nacos 有用户名密码,可以加上: - # username: nacos - # password: nacos - - # Dubbo 服务扫描包(你 @DubboService 标注的位置) - scan: - base-packages: org.xyzh.system.service.impl - -# ================== MyBatis ================== -mybatis-plus: - mapper-locations: classpath:mapper/**/*.xml - type-aliases-package: org.xyzh.common.dto, org.xyzh.api - -# ================== Logging ================== -logging: - config: classpath:log4j2.xml - charset: - console: UTF-8 - file: UTF-8 - level: - org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping: TRACE diff --git a/urbanLifelineServ/system/src/main/resources/application.yml b/urbanLifelineServ/system/src/main/resources/application.yml index f5158608..f6a5ddf0 100644 --- a/urbanLifelineServ/system/src/main/resources/application.yml +++ b/urbanLifelineServ/system/src/main/resources/application.yml @@ -1,83 +1,32 @@ -# ================== Server ================== +# ================== System 系统服务配置 ================== server: - port: 8182 - # servlet: - # context-path: /urban-lifeline/system # 微服务架构下,context-path由Gateway管理 -# ================== Auth ==================== + port: 8082 +spring: + application: + name: system-service + +# ================== Auth ================== auth: enabled: true gateway-mode: true - # 认证接口:可以按服务自定义 - login-path: /urban-lifeline/auth/login - logout-path: /urban-lifeline/auth/logout - captcha-path: /urban-lifeline/auth/captcha - refresh-path: /urban-lifeline/auth/refresh - - # 通用白名单(非认证接口) whitelist: - # Swagger/OpenAPI 文档相关(建议不带 context-path) - /swagger-ui/** - /swagger-ui.html - /v3/api-docs/** - /webjars/** - - # 静态资源 - /favicon.ico - /error - - # 健康检查 - /actuator/health - /actuator/info - /system/guest/identify - # 其他需要放行的路径 - # - /public/** - # - /api/public/** -security: - aes: - secret-key: MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI= - -# ================== Spring ================== -spring: - application: - name: system-service - - # ================== Spring Cloud Nacos ================== - cloud: - nacos: - discovery: - server-addr: 127.0.0.1:8848 - namespace: dev - group: DEFAULT_GROUP - - # ================== DataSource ================== - datasource: - # 按你的实际库名改一下,比如 urban-lifeline_system - url: jdbc:postgresql://127.0.0.1:5432/urban_lifeline # 换成你的 PG 库名 - username: postgres # PG 用户 - password: postgres # PG 密码 - driver-class-name: org.postgresql.Driver - - # ================== Redis ================== - data: - redis: - host: 127.0.0.1 # 如果是 docker 跑的 redis,按实际 host / 端口改 - port: 6379 - database: 0 - password: 123456 # 如果有密码就填上,没密码可以去掉这一行 # ================== SpringDoc ================== springdoc: - api-docs: - enabled: true - path: /v3/api-docs swagger-ui: - enabled: true - path: /swagger-ui.html try-it-out-enabled: true show-common-extensions: true show-extensions: true - show-request-duration: true filter: true tags-sorter: alpha operations-sorter: alpha @@ -86,37 +35,10 @@ springdoc: display-name: '系统服务 API' paths-to-match: '/**' -# ================== Dubbo + Nacos 注册中心 ================== +# ================== Dubbo ================== dubbo: application: name: urban-lifeline-system qos-enable: false - - protocol: - name: dubbo - port: -1 # -1 表示随机端口,避免端口冲突;也可以写死一个端口 - - registry: - # Nacos 注册中心地址:使用 docker-compose 中映射到宿主机的 8848 端口 - address: nacos://127.0.0.1:8848 - # 如果 Nacos 有用户名密码,可以加上: - # username: nacos - # password: nacos - - # Dubbo 服务扫描包(你 @DubboService 标注的位置) scan: base-packages: org.xyzh.system.service.impl - -# ================== MyBatis ================== -mybatis-plus: - mapper-locations: classpath:mapper/**/*.xml - type-aliases-package: org.xyzh.common.dto, org.xyzh.api - -# ================== Logging ================== -logging: - config: classpath:log4j2.xml - charset: - console: UTF-8 - file: UTF-8 - level: - org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping: TRACE \ No newline at end of file diff --git a/urbanLifelineServ/workcase/src/main/resources/application-dev.yml b/urbanLifelineServ/workcase/src/main/resources/application-dev.yml deleted file mode 100644 index 930a8654..00000000 --- a/urbanLifelineServ/workcase/src/main/resources/application-dev.yml +++ /dev/null @@ -1,117 +0,0 @@ -# ================== Server ================== -server: - port: 8088 - # servlet: - # context-path: /urban-lifeline/workcase - -# ================== Auth ==================== -auth: - enabled: true - gate-way: true - whitelist: - - /swagger-ui/** - - /swagger-ui.html - - /v3/api-docs/** - - /webjars/** - - /favicon.ico - - /error - - /actuator/health - - /actuator/info - # 微信客服回调接口(无需鉴权) - - /workcase/chat/kefu/callback - # CRM回调接口(无需鉴权,但需签名验证) - - /workcase/receive/crm - -security: - aes: - # AES-256 密钥(Base64编码,必须与所有服务保持一致) - # 警告:这是开发环境密钥,生产环境请使用密钥管理系统 - secret-key: MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI= - -# ================== Spring ================== -spring: - application: - name: workcase-service - # 文件上传配置 - servlet: - multipart: - enabled: true - max-file-size: 500MB - max-request-size: 500MB - # ================== Spring Cloud Nacos ================== - cloud: - nacos: - discovery: - server-addr: 127.0.0.1:8848 - namespace: dev - group: DEFAULT_GROUP - - # ================== DataSource ================== - datasource: - url: jdbc:postgresql://127.0.0.1:5432/urban_lifeline - username: postgres - password: postgres - driver-class-name: org.postgresql.Driver - - # ================== Redis ================== - data: - redis: - host: 127.0.0.1 - port: 6379 - database: 0 - # password: "" - -# ================== SpringDoc ================== -springdoc: - api-docs: - enabled: true - path: /v3/api-docs - swagger-ui: - enabled: true - path: /swagger-ui.html - group-configs: - - group: 'default' - display-name: '工单服务 API' - paths-to-match: '/**' - -# ================== Dubbo + Nacos ================== -dubbo: - application: - name: urban-lifeline-workcase - qos-enable: false - protocol: - payload: 110100480 - name: dubbo - port: -1 - registry: - address: nacos://127.0.0.1:8848 - scan: - base-packages: org.xyzh.workcase.service.impl - -# ================== MyBatis ================== -mybatis-plus: - mapper-locations: classpath:mapper/**/*.xml - type-aliases-package: org.xyzh.common.dto, org.xyzh.api -logging: - config: classpath:log4j2.xml - charset: - console: UTF-8 - file: UTF-8 - level: - org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping: TRACE - -# ================== Jitsi Meet 视频会议配置 ================== -jitsi: - app: - # 应用ID(必须与Docker配置中的JWT_APP_ID一致) - id: urbanLifeline - # JWT密钥(必须与Docker配置中的JWT_APP_SECRET一致) - # 警告:生产环境请修改为强随机字符串,并妥善保管 - # 注意:HS256算法要求密钥长度至少32字节(256 bits) - secret: urbanLifeline-jitsi-secret-key-2025-production-safe-hs256 - server: - # Jitsi Meet服务器地址(独立子域名) - url: https://org.xyzh.yslg.jitsi - token: - # JWT Token有效期(毫秒)- 默认2小时 - expiration: 7200000 \ No newline at end of file diff --git a/urbanLifelineServ/workcase/src/main/resources/application.yml b/urbanLifelineServ/workcase/src/main/resources/application.yml index d671c900..c571cd3a 100644 --- a/urbanLifelineServ/workcase/src/main/resources/application.yml +++ b/urbanLifelineServ/workcase/src/main/resources/application.yml @@ -1,10 +1,17 @@ -# ================== Server ================== +# ================== Workcase 工单服务配置 ================== server: - port: 8188 - # servlet: - # context-path: /urban-lifeline/workcase # 微服务架构下,context-path由Gateway管理 + port: 8088 -# ================== Auth ==================== +spring: + application: + name: workcase-service + servlet: + multipart: + enabled: true + max-file-size: 500MB + max-request-size: 500MB + +# ================== Auth ================== auth: enabled: true gateway-mode: true @@ -17,81 +24,32 @@ auth: - /error - /actuator/health - /actuator/info - # 微信客服回调接口(无需鉴权) - /workcase/chat/kefu/callback - # CRM回调接口(无需鉴权,但需签名验证) - /workcase/receive/crm -security: - aes: - # AES-256 密钥(Base64编码,必须与所有服务保持一致) - # 警告:这是开发环境密钥,生产环境请使用密钥管理系统 - secret-key: MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI= - -# ================== Spring ================== -spring: - application: - name: workcase-service - - # ================== Spring Cloud Nacos ================== - cloud: - nacos: - discovery: - server-addr: 127.0.0.1:8848 - namespace: dev - group: DEFAULT_GROUP - - # ================== DataSource ================== - datasource: - url: jdbc:postgresql://127.0.0.1:5432/urban_lifeline - username: postgres - password: postgres - driver-class-name: org.postgresql.Driver - - # ================== Redis ================== - data: - redis: - host: 127.0.0.1 # 如果是 docker 跑的 redis,按实际 host / 端口改 - port: 6379 - database: 0 - password: 123456 # 如果有密码就填上,没密码可以去掉这一行 - # ================== SpringDoc ================== springdoc: - api-docs: - enabled: true - path: /v3/api-docs - swagger-ui: - enabled: true - path: /swagger-ui.html group-configs: - group: 'default' display-name: '工单服务 API' paths-to-match: '/**' -# ================== Dubbo + Nacos ================== +# ================== Dubbo ================== dubbo: application: name: urban-lifeline-workcase qos-enable: false protocol: - name: dubbo - port: -1 - registry: - address: nacos://127.0.0.1:8848 + payload: 110100480 scan: base-packages: org.xyzh.workcase.service.impl -# ================== MyBatis ================== -mybatis-plus: - mapper-locations: classpath:mapper/**/*.xml - type-aliases-package: org.xyzh.common.dto, org.xyzh.api - -# ================== Logging ================== -logging: - config: classpath:log4j2.xml - charset: - console: UTF-8 - file: UTF-8 - level: - org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping: TRACE +# ================== Jitsi Meet 视频会议配置 ================== +jitsi: + app: + id: ${JITSI_APP_ID:urbanLifeline} + secret: ${JITSI_APP_SECRET:urbanLifeline-jitsi-secret-key-2025-production-safe-hs256} + server: + url: ${JITSI_SERVER_URL:https://org.xyzh.yslg.jitsi} + token: + expiration: 7200000 diff --git a/urbanLifelineWeb/packages/bidding/public/app-config.js b/urbanLifelineWeb/packages/bidding/public/app-config.js index 327e13e4..b196c62c 100644 --- a/urbanLifelineWeb/packages/bidding/public/app-config.js +++ b/urbanLifelineWeb/packages/bidding/public/app-config.js @@ -1,24 +1,22 @@ /** - * @description 应用运行时配置文件 - * @author yslg - * @since 2025-12-06 + * @description 应用运行时配置文件 (支持 Docker 环境变量替换) * - * 说明: - * 1. 此文件在生产环境中被加载,用于覆盖内置配置 - * 2. Docker 部署时,可通过挂载此文件来修改配置,无需重新构建 - * 3. 配置结构必须与 packages/shared/src/config/index.ts 中的 AppRuntimeConfig 保持一致 + * 占位符说明: + * - __PLACEHOLDER__ 格式的值会在 Docker 启动时被环境变量替换 + * - 如果环境变量未设置,将使用默认值 * - * 使用示例(Docker): - * docker run -v /path/to/app-config.js:/app/public/app-config.js my-app:latest + * Docker 部署: + * 1. 通过 volume 挂载覆盖此文件 + * 2. 或通过启动脚本替换占位符 */ window.APP_RUNTIME_CONFIG = { // 环境标识 - env: 'production', + env: '__APP_ENV__', // API 配置 api: { - baseUrl: '/api', + baseUrl: '__API_BASE_URL__', timeout: 30000 }, @@ -27,12 +25,12 @@ window.APP_RUNTIME_CONFIG = { // 文件配置 file: { - downloadUrl: '/api/urban-lifeline/file/download/', - uploadUrl: '/api/urban-lifeline/file/upload', + downloadUrl: '__API_BASE_URL__/urban-lifeline/file/download/', + uploadUrl: '__API_BASE_URL__/urban-lifeline/file/upload', maxSize: { - image: 5, // MB - video: 100, // MB - document: 10 // MB + image: 5, + video: 100, + document: 10 }, acceptTypes: { image: 'image/*', @@ -44,20 +42,23 @@ window.APP_RUNTIME_CONFIG = { // Token 配置 token: { key: 'token', - refreshThreshold: 300000 // 5分钟 + refreshThreshold: 300000 }, // 公共资源路径 - publicImgPath: '/img', - publicWebPath: '/', + publicImgPath: '__PUBLIC_PATH__/img', + publicWebPath: '__PUBLIC_PATH__', // 单点登录配置 sso: { - platformUrl: '/', // platform 平台地址 - workcaseUrl: '/workcase', // workcase 服务地址 - biddingUrl: '/bidding' // bidding 服务地址 + platformUrl: '__SSO_PLATFORM_URL__', + workcaseUrl: '__SSO_WORKCASE_URL__', + biddingUrl: '__SSO_BIDDING_URL__' }, + // AES 加密密钥 + aesSecretKey: '__AES_SECRET_KEY__', + // 功能开关 features: { enableDebug: false, diff --git a/urbanLifelineWeb/packages/bidding/src/config/index.ts b/urbanLifelineWeb/packages/bidding/src/config/index.ts index a0e90ff6..fb6c89ec 100644 --- a/urbanLifelineWeb/packages/bidding/src/config/index.ts +++ b/urbanLifelineWeb/packages/bidding/src/config/index.ts @@ -1,16 +1,10 @@ /** - * @description Bidding 应用运行时配置 - * @author yslg - * @since 2025-12-06 + * @description 应用运行时配置 * * 配置加载策略: - * 1. 开发环境:使用下面定义的开发配置 + * 1. 开发环境:使用内置开发配置 * 2. 生产环境:从 window.APP_RUNTIME_CONFIG 读取(来自 app-config.js) - * 3. Docker部署:替换 app-config.js 文件实现配置外挂 - * - * 配置结构说明: - * 此文件的配置结构与 app-config.js 完全对应 - * 修改 app-config.js 后,这里的配置会自动应用 + * 3. Docker部署:启动时替换 app-config.js 中的占位符 */ // ============================================ @@ -43,12 +37,12 @@ export interface AppRuntimeConfig { }; publicImgPath: string; publicWebPath: string; - // 单点登录配置 sso?: { - platformUrl: string; // platform 平台地址 - workcaseUrl: string; // workcase 服务地址 - biddingUrl: string; // bidding 服务地址 + platformUrl: string; + workcaseUrl: string; + biddingUrl: string; }; + aesSecretKey?: string; features?: { enableDebug?: boolean; enableMockData?: boolean; @@ -57,101 +51,80 @@ export interface AppRuntimeConfig { } // ============================================ -// 配置定义(与 app-config.js 结构一致) +// 环境检测 // ============================================ const isDev = (import.meta as any).env?.DEV ?? false; +// ============================================ // 开发环境配置 +// ============================================ const devConfig: AppRuntimeConfig = { env: 'development', - api: { - // 开发环境通过 Vite 代理转发到后端,避免浏览器直接跨域 - // 实际请求路径示例:/api/... → 由代理转发到实际后端 baseUrl: '/api', timeout: 30000 }, - baseUrl: '/', - file: { - // 通过 Nginx → Gateway 访问文件服务,使用 /urban-lifeline 前缀 downloadUrl: '/api/urban-lifeline/file/download/', uploadUrl: '/api/urban-lifeline/file/upload', - maxSize: { - image: 5, - video: 100, - document: 10 - }, + maxSize: { image: 5, video: 100, document: 10 }, acceptTypes: { image: 'image/*', video: 'video/*', document: '.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx' } }, - token: { key: 'token', refreshThreshold: 300000 }, - - publicImgPath: 'http://localhost:7002/img', - publicWebPath: 'http://localhost:7002', - - // 单点登录配置 + publicImgPath: '/img', + publicWebPath: '/', sso: { - platformUrl: '/', // 通过nginx访问platform - workcaseUrl: '/workcase', // 通过nginx访问workcase - biddingUrl: '/bidding' // 通过nginx访问bidding + platformUrl: '/', + workcaseUrl: '/workcase', + biddingUrl: '/bidding' }, - + aesSecretKey: 'MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=', features: { enableDebug: true, enableMockData: false } }; +// ============================================ // 生产环境默认配置(兜底) +// ============================================ const prodDefaultConfig: AppRuntimeConfig = { env: 'production', - api: { baseUrl: '/api', timeout: 30000 }, - baseUrl: '/', - file: { downloadUrl: '/api/urban-lifeline/file/download/', uploadUrl: '/api/urban-lifeline/file/upload', - maxSize: { - image: 5, - video: 100, - document: 10 - }, + maxSize: { image: 5, video: 100, document: 10 }, acceptTypes: { image: 'image/*', video: 'video/*', document: '.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx' } }, - token: { key: 'token', refreshThreshold: 300000 }, - publicImgPath: '/img', publicWebPath: '/', - - // 单点登录配置(生产环境通过nginx代理) sso: { platformUrl: '/', workcaseUrl: '/workcase', biddingUrl: '/bidding' }, - + aesSecretKey: 'MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=', features: { enableDebug: false, enableMockData: false @@ -162,92 +135,82 @@ const prodDefaultConfig: AppRuntimeConfig = { // 配置加载 // ============================================ +/** + * 检查值是否为未替换的占位符 + */ +const isPlaceholder = (value: any): boolean => { + return typeof value === 'string' && value.startsWith('__') && value.endsWith('__'); +}; + +/** + * 深度合并配置,跳过占位符值 + */ +const mergeConfig = (target: any, source: any): any => { + const result = { ...target }; + for (const key in source) { + const value = source[key]; + if (value && typeof value === 'object' && !Array.isArray(value)) { + result[key] = mergeConfig(target[key] || {}, value); + } else if (!isPlaceholder(value)) { + result[key] = value; + } + } + return result; +}; + /** * 获取运行时配置 - * 生产环境优先从 window.APP_RUNTIME_CONFIG 读取(app-config.js 注入) */ const getRuntimeConfig = (): AppRuntimeConfig => { if (isDev) { - console.log('[配置] 开发环境,使用内置配置'); + console.log('[Config] 开发环境,使用内置配置'); return devConfig; } - // 生产环境:尝试读取外部配置 try { const runtimeConfig = (window as any).APP_RUNTIME_CONFIG; if (runtimeConfig && typeof runtimeConfig === 'object') { - console.log('[配置] 加载外部配置 app-config.js'); - console.log('[配置] API地址:', runtimeConfig.api?.baseUrl); - console.log('[配置] 环境:', runtimeConfig.env || 'production'); - return runtimeConfig as AppRuntimeConfig; + const merged = mergeConfig(prodDefaultConfig, runtimeConfig); + console.log('[Config] 加载运行时配置', merged); + return merged; } } catch (e) { - console.warn('[配置] 无法读取外部配置,使用默认配置', e); + console.warn('[Config] 无法读取外部配置', e); } - console.log('[配置] 使用默认生产配置'); + console.log('[Config] 使用默认生产配置'); return prodDefaultConfig; }; -// 当前应用配置 +// 当前配置 const config = getRuntimeConfig(); -console.log('[配置] 当前配置', config); // ============================================ -// 导出配置(向后兼容) +// 导出 // ============================================ -// 单独导出常用配置项 +// AES 密钥 +export const AES_SECRET_KEY = config.aesSecretKey || 'MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI='; + +// 常用配置项 export const API_BASE_URL = config.api.baseUrl; export const FILE_DOWNLOAD_URL = config.file.downloadUrl; +export const FILE_UPLOAD_URL = config.file.uploadUrl; export const PUBLIC_IMG_PATH = config.publicImgPath; export const PUBLIC_WEB_PATH = config.publicWebPath; -// 导出完整配置对象 +// 完整配置对象 export const APP_CONFIG = { - // 应用标题 title: '泰豪电源招投标系统', - - // 环境标识 env: config.env || 'production', - - // 应用基础路径 baseUrl: config.baseUrl, - - // API 配置 - api: { - baseUrl: config.api.baseUrl, - timeout: config.api.timeout - }, - - // 文件配置 - file: { - downloadUrl: config.file.downloadUrl, - uploadUrl: config.file.uploadUrl, - maxSize: config.file.maxSize, - acceptTypes: config.file.acceptTypes - }, - - // Token 配置 - token: { - key: config.token.key, - refreshThreshold: config.token.refreshThreshold - }, - - // 公共路径 + api: config.api, + file: config.file, + token: config.token, publicImgPath: config.publicImgPath, publicWebPath: config.publicWebPath, - - // 单点登录配置 - sso: config.sso || { - platformUrl: '/', - workcaseUrl: '/workcase', - biddingUrl: '/bidding' - }, - - // 功能开关 + sso: config.sso || { platformUrl: '/', workcaseUrl: '/workcase', biddingUrl: '/bidding' }, features: config.features || {} }; -// 默认导出 export default APP_CONFIG; diff --git a/urbanLifelineWeb/packages/bidding/vite.config.ts b/urbanLifelineWeb/packages/bidding/vite.config.ts index da3ee2ec..0f8e97b3 100644 --- a/urbanLifelineWeb/packages/bidding/vite.config.ts +++ b/urbanLifelineWeb/packages/bidding/vite.config.ts @@ -1,6 +1,7 @@ import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import vueJsx from '@vitejs/plugin-vue-jsx' +import { federation } from '@module-federation/vite' import { resolve, dirname } from 'path' import { fileURLToPath } from 'url' import fs from 'fs' @@ -8,61 +9,95 @@ import fs from 'fs' const __filename = fileURLToPath(import.meta.url) const __dirname = dirname(__filename) -export default defineConfig(({ mode }) => ({ - // 开发和生产环境都通过nginx代理访问/bidding - base: '/bidding/', - - plugins: [ - vue({ - script: { - defineModel: true, - propsDestructure: true - } - }), - vueJsx() - ], - - define: { - __VUE_OPTIONS_API__: true, - __VUE_PROD_DEVTOOLS__: true, - __VUE_PROD_HYDRATION_MISMATCH_DETAILS__: true - }, - - resolve: { - alias: { - '@': resolve(__dirname, 'src') - } - }, - - server: { - port: 7002, - host: true, - cors: true, - open: '/bidding/', // 开发时自动打开到 /bidding/ 路径 - // HTTPS 配置(使用 mkcert 生成的本地开发证书) - https: { - key: fs.readFileSync('C:/Users/FK05/443/localhost+3-key.pem'), - cert: fs.readFileSync('C:/Users/FK05/443/localhost+3.pem') +// 开发环境 shared 模块地址 +const DEV_SHARED_URL = 'https://localhost:7000/shared/mf-manifest.json' +// 生产环境使用相对路径,通过 Nginx 代理访问 +const PROD_SHARED_URL = '/shared/mf-manifest.json' + +export default defineConfig(({ mode }) => { + const isDev = mode === 'development' + const sharedEntry = isDev ? DEV_SHARED_URL : PROD_SHARED_URL + + return { + base: '/bidding/', + + plugins: [ + vue({ + script: { + defineModel: true, + propsDestructure: true + } + }), + vueJsx(), + federation({ + name: 'bidding', + remotes: { + shared: { + type: 'module', + name: 'shared', + entry: sharedEntry + } + }, + shared: { + vue: {}, + 'vue-router': {}, + 'element-plus': {}, + axios: {} + } + }) + ], + + define: { + __VUE_OPTIONS_API__: true, + __VUE_PROD_DEVTOOLS__: true, + __VUE_PROD_HYDRATION_MISMATCH_DETAILS__: true }, - proxy: { - '/api': { - target: 'http://localhost:8180', - changeOrigin: true, - rewrite: (path: string) => path.replace(/^\/api/, '') + + resolve: { + alias: { + '@': resolve(__dirname, 'src') } - } - }, - - build: { - outDir: 'dist', - sourcemap: true, - rollupOptions: { - output: { - manualChunks: { - 'vue-vendor': ['vue', 'vue-router', 'pinia'], - 'element-plus': ['element-plus'] + }, + + server: { + port: 7002, + host: true, + cors: true, + open: '/bidding/', + https: (() => { + try { + return { + key: fs.readFileSync('C:/Users/FK05/443/localhost+3-key.pem'), + cert: fs.readFileSync('C:/Users/FK05/443/localhost+3.pem') + } + } catch { + return undefined + } + })(), + hmr: { + path: '/@vite/client', + port: 7002 + }, + proxy: { + '/api': { + target: 'http://localhost:8180', + changeOrigin: true, + rewrite: (path: string) => path.replace(/^\/api/, '') + } + } + }, + + build: { + outDir: 'dist', + sourcemap: true, + rollupOptions: { + output: { + manualChunks: { + 'vue-vendor': ['vue', 'vue-router', 'pinia'], + 'element-plus': ['element-plus'] + } } } } } -})) +}) diff --git a/urbanLifelineWeb/packages/platform/public/app-config.js b/urbanLifelineWeb/packages/platform/public/app-config.js index 327e13e4..b196c62c 100644 --- a/urbanLifelineWeb/packages/platform/public/app-config.js +++ b/urbanLifelineWeb/packages/platform/public/app-config.js @@ -1,24 +1,22 @@ /** - * @description 应用运行时配置文件 - * @author yslg - * @since 2025-12-06 + * @description 应用运行时配置文件 (支持 Docker 环境变量替换) * - * 说明: - * 1. 此文件在生产环境中被加载,用于覆盖内置配置 - * 2. Docker 部署时,可通过挂载此文件来修改配置,无需重新构建 - * 3. 配置结构必须与 packages/shared/src/config/index.ts 中的 AppRuntimeConfig 保持一致 + * 占位符说明: + * - __PLACEHOLDER__ 格式的值会在 Docker 启动时被环境变量替换 + * - 如果环境变量未设置,将使用默认值 * - * 使用示例(Docker): - * docker run -v /path/to/app-config.js:/app/public/app-config.js my-app:latest + * Docker 部署: + * 1. 通过 volume 挂载覆盖此文件 + * 2. 或通过启动脚本替换占位符 */ window.APP_RUNTIME_CONFIG = { // 环境标识 - env: 'production', + env: '__APP_ENV__', // API 配置 api: { - baseUrl: '/api', + baseUrl: '__API_BASE_URL__', timeout: 30000 }, @@ -27,12 +25,12 @@ window.APP_RUNTIME_CONFIG = { // 文件配置 file: { - downloadUrl: '/api/urban-lifeline/file/download/', - uploadUrl: '/api/urban-lifeline/file/upload', + downloadUrl: '__API_BASE_URL__/urban-lifeline/file/download/', + uploadUrl: '__API_BASE_URL__/urban-lifeline/file/upload', maxSize: { - image: 5, // MB - video: 100, // MB - document: 10 // MB + image: 5, + video: 100, + document: 10 }, acceptTypes: { image: 'image/*', @@ -44,20 +42,23 @@ window.APP_RUNTIME_CONFIG = { // Token 配置 token: { key: 'token', - refreshThreshold: 300000 // 5分钟 + refreshThreshold: 300000 }, // 公共资源路径 - publicImgPath: '/img', - publicWebPath: '/', + publicImgPath: '__PUBLIC_PATH__/img', + publicWebPath: '__PUBLIC_PATH__', // 单点登录配置 sso: { - platformUrl: '/', // platform 平台地址 - workcaseUrl: '/workcase', // workcase 服务地址 - biddingUrl: '/bidding' // bidding 服务地址 + platformUrl: '__SSO_PLATFORM_URL__', + workcaseUrl: '__SSO_WORKCASE_URL__', + biddingUrl: '__SSO_BIDDING_URL__' }, + // AES 加密密钥 + aesSecretKey: '__AES_SECRET_KEY__', + // 功能开关 features: { enableDebug: false, diff --git a/urbanLifelineWeb/packages/platform/src/config/index.ts b/urbanLifelineWeb/packages/platform/src/config/index.ts index 7960152d..dcd54616 100644 --- a/urbanLifelineWeb/packages/platform/src/config/index.ts +++ b/urbanLifelineWeb/packages/platform/src/config/index.ts @@ -1,28 +1,12 @@ /** - * @description Platform 应用运行时配置 - * @author yslg - * @since 2025-12-06 + * @description 应用运行时配置 * * 配置加载策略: - * 1. 开发环境:使用下面定义的开发配置 + * 1. 开发环境:使用内置开发配置 * 2. 生产环境:从 window.APP_RUNTIME_CONFIG 读取(来自 app-config.js) - * 3. Docker部署:替换 app-config.js 文件实现配置外挂 - * - * 配置结构说明: - * 此文件的配置结构与 app-config.js 完全对应 - * 修改 app-config.js 后,这里的配置会自动应用 + * 3. Docker部署:启动时替换 app-config.js 中的占位符 */ -// ============================================ -// AES 加密密钥 -// ============================================ -/** - * AES 加密密钥(与后端保持一致) - * 对应后端配置:security.aes.secret-key - * Base64 编码的 32 字节密钥(256 位) - */ -export const AES_SECRET_KEY = 'MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=' // Base64 编码,解码后是 "12345678901234567890123456789012" (32字节) - // ============================================ // 类型定义 // ============================================ @@ -53,12 +37,12 @@ export interface AppRuntimeConfig { }; publicImgPath: string; publicWebPath: string; - // 单点登录配置 sso?: { - platformUrl: string; // platform 平台地址 - workcaseUrl: string; // workcase 服务地址 - biddingUrl: string; // bidding 服务地址 + platformUrl: string; + workcaseUrl: string; + biddingUrl: string; }; + aesSecretKey?: string; features?: { enableDebug?: boolean; enableMockData?: boolean; @@ -67,101 +51,80 @@ export interface AppRuntimeConfig { } // ============================================ -// 配置定义(与 app-config.js 结构一致) +// 环境检测 // ============================================ const isDev = (import.meta as any).env?.DEV ?? false; +// ============================================ // 开发环境配置 +// ============================================ const devConfig: AppRuntimeConfig = { env: 'development', - api: { - // 开发环境通过 Vite 代理转发到后端,避免浏览器直接跨域 - // 实际请求路径示例:/api/... → 由代理转发到实际后端 baseUrl: '/api', timeout: 30000 }, - baseUrl: '/', - file: { - // 通过 Nginx → Gateway 访问文件服务,使用 /urban-lifeline 前缀 downloadUrl: '/api/urban-lifeline/file/download/', uploadUrl: '/api/urban-lifeline/file/upload', - maxSize: { - image: 5, - video: 100, - document: 10 - }, + maxSize: { image: 5, video: 100, document: 10 }, acceptTypes: { image: 'image/*', video: 'video/*', document: '.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx' } }, - token: { key: 'token', refreshThreshold: 300000 }, - - publicImgPath: 'http://localhost:7001/img', - publicWebPath: 'http://localhost:7001', - - // 单点登录配置 + publicImgPath: '/img', + publicWebPath: '/', sso: { - platformUrl: '/', // 通过nginx访问platform - workcaseUrl: '/workcase', // 通过nginx访问workcase - biddingUrl: '/bidding' // 通过nginx访问bidding + platformUrl: '/', + workcaseUrl: '/workcase', + biddingUrl: '/bidding' }, - + aesSecretKey: 'MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=', features: { enableDebug: true, enableMockData: false } }; +// ============================================ // 生产环境默认配置(兜底) +// ============================================ const prodDefaultConfig: AppRuntimeConfig = { env: 'production', - api: { baseUrl: '/api', timeout: 30000 }, - baseUrl: '/', - file: { downloadUrl: '/api/urban-lifeline/file/download/', uploadUrl: '/api/urban-lifeline/file/upload', - maxSize: { - image: 5, - video: 100, - document: 10 - }, + maxSize: { image: 5, video: 100, document: 10 }, acceptTypes: { image: 'image/*', video: 'video/*', document: '.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx' } }, - token: { key: 'token', refreshThreshold: 300000 }, - publicImgPath: '/img', publicWebPath: '/', - - // 单点登录配置(生产环境通过nginx代理) sso: { platformUrl: '/', workcaseUrl: '/workcase', biddingUrl: '/bidding' }, - + aesSecretKey: 'MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=', features: { enableDebug: false, enableMockData: false @@ -172,95 +135,86 @@ const prodDefaultConfig: AppRuntimeConfig = { // 配置加载 // ============================================ +/** + * 检查值是否为未替换的占位符 + */ +const isPlaceholder = (value: any): boolean => { + return typeof value === 'string' && value.startsWith('__') && value.endsWith('__'); +}; + +/** + * 深度合并配置,跳过占位符值 + */ +const mergeConfig = (target: any, source: any): any => { + const result = { ...target }; + for (const key in source) { + const value = source[key]; + if (value && typeof value === 'object' && !Array.isArray(value)) { + result[key] = mergeConfig(target[key] || {}, value); + } else if (!isPlaceholder(value)) { + result[key] = value; + } + } + return result; +}; + /** * 获取运行时配置 - * 生产环境优先从 window.APP_RUNTIME_CONFIG 读取(app-config.js 注入) */ const getRuntimeConfig = (): AppRuntimeConfig => { if (isDev) { - console.log('[配置] 开发环境,使用内置配置'); + console.log('[Config] 开发环境,使用内置配置'); return devConfig; } - // 生产环境:尝试读取外部配置 try { const runtimeConfig = (window as any).APP_RUNTIME_CONFIG; if (runtimeConfig && typeof runtimeConfig === 'object') { - console.log('[配置] 加载外部配置 app-config.js'); - console.log('[配置] API地址:', runtimeConfig.api?.baseUrl); - console.log('[配置] 环境:', runtimeConfig.env || 'production'); - return runtimeConfig as AppRuntimeConfig; + // 合并配置,未替换的占位符使用默认值 + const merged = mergeConfig(prodDefaultConfig, runtimeConfig); + console.log('[Config] 加载运行时配置', merged); + return merged; } } catch (e) { - console.warn('[配置] 无法读取外部配置,使用默认配置', e); + console.warn('[Config] 无法读取外部配置', e); } - console.log('[配置] 使用默认生产配置'); + console.log('[Config] 使用默认生产配置'); return prodDefaultConfig; }; -// 当前应用配置 +// 当前配置 const config = getRuntimeConfig(); -console.log('[配置] 当前配置', config); // ============================================ -// 导出配置(向后兼容) +// 导出 // ============================================ -// 单独导出常用配置项 +// AES 密钥 +export const AES_SECRET_KEY = config.aesSecretKey || 'MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI='; + +// 常用配置项 export const API_BASE_URL = config.api.baseUrl; export const FILE_DOWNLOAD_URL = config.file.downloadUrl; +export const FILE_UPLOAD_URL = config.file.uploadUrl; export const PUBLIC_IMG_PATH = config.publicImgPath; export const PUBLIC_WEB_PATH = config.publicWebPath; -// 导出完整配置对象 +// 完整配置对象 export const APP_CONFIG = { - // 应用标题 title: '泰豪电源 AI 数智化平台', name: '泰豪电源 AI 数智化平台', version: '1.0.0', copyright: '泰豪电源', - - // 环境标识 env: config.env || 'production', - - // 应用基础路径 baseUrl: config.baseUrl, - - // API 配置 - api: { - baseUrl: config.api.baseUrl, - timeout: config.api.timeout - }, - - // 文件配置 - file: { - downloadUrl: config.file.downloadUrl, - uploadUrl: config.file.uploadUrl, - maxSize: config.file.maxSize, - acceptTypes: config.file.acceptTypes - }, - - // Token 配置 - token: { - key: config.token.key, - refreshThreshold: config.token.refreshThreshold - }, - - // 公共路径 + api: config.api, + file: config.file, + token: config.token, publicImgPath: config.publicImgPath, publicWebPath: config.publicWebPath, - - // 单点登录配置 - sso: config.sso || { - platformUrl: '/', - workcaseUrl: '/workcase', - biddingUrl: '/bidding' - }, - - // 功能开关 + sso: config.sso || { platformUrl: '/', workcaseUrl: '/workcase', biddingUrl: '/bidding' }, features: config.features || {} }; -// 默认导出 export default APP_CONFIG; diff --git a/urbanLifelineWeb/packages/platform/src/types/shared.d.ts b/urbanLifelineWeb/packages/platform/src/types/shared.d.ts index 89e856fc..0e142e0b 100644 --- a/urbanLifelineWeb/packages/platform/src/types/shared.d.ts +++ b/urbanLifelineWeb/packages/platform/src/types/shared.d.ts @@ -52,7 +52,7 @@ declare module 'shared/api' { import type { AxiosResponse, AxiosRequestConfig } from 'axios' interface ApiInstance { - get(url: string, config?: AxiosRequestConfig): Promise> + get(url: string,data?: any, config?: AxiosRequestConfig): Promise> post(url: string, data?: any, config?: AxiosRequestConfig): Promise> put(url: string, data?: any, config?: AxiosRequestConfig): Promise> delete(url: string, config?: AxiosRequestConfig): Promise> @@ -61,8 +61,6 @@ declare module 'shared/api' { export const api: ApiInstance export const TokenManager: any - export const authAPI: any - export const fileAPI: any } declare module 'shared/api/auth' { @@ -81,26 +79,52 @@ declare module 'shared/api/ai' { // ============ types模块 ================== declare module 'shared/types' { - export type { BaseDTO, BaseVO } from '../../../shared/src/types/base' - + // 基础类型 + export interface OrderField { + field: string + order: 'ASC' | 'DESC' + } + + export interface BaseDTO { + optsn?: string + creator?: string + updater?: string + deptPath?: string + remark?: string + createTime?: string + updateTime?: string + deleteTime?: string + deleted?: boolean + limit?: number + startTime?: string + endTime?: string + orderFields?: OrderField[] + } + + export interface BaseVO extends BaseDTO { + id?: string + creatorName?: string + updaterName?: string + } + // 重新导出 response export type { ResultDomain } from '../../../shared/src/types/response' - + // 重新导出 page export type { PageDomain, PageParam, PageRequest } from '../../../shared/src/types/page' - + // 重新导出 auth export type { LoginParam, LoginDomain } from '../../../shared/src/types/auth' - + // 重新导出 sys export type { SysUserVO, SysConfigVO, TbSysViewDTO } from '../../../shared/src/types/sys' - + // 重新导出 file export type { TbSysFileDTO } from '../../../shared/src/types/file' - + // 重新导出 ai - export type { - TbKnowledge, + export type { + TbKnowledge, TbKnowledgeFile, TbAgent, PromptCard, @@ -108,10 +132,17 @@ declare module 'shared/types' { TbChatMessage, DifyFileInfo, ChatPrepareData, - StopChatParams, - CommentMessageParams + CreateChatParam, + StopChatParam, + CommentMessageParam, + ChatListParam, + ChatMessageListParam, + SSEMessageData, + SSECallbacks, + SSETask, + TbKnowledgeFileLog } from '../../../shared/src/types/ai' - + // 重新导出 menu export type { MenuItem, toMenuItem, toMenuItems } from '../../../shared/src/types/menu' } diff --git a/urbanLifelineWeb/packages/platform/vite.config.ts b/urbanLifelineWeb/packages/platform/vite.config.ts index 7eecb8e8..6f8a00bc 100644 --- a/urbanLifelineWeb/packages/platform/vite.config.ts +++ b/urbanLifelineWeb/packages/platform/vite.config.ts @@ -9,80 +9,93 @@ import fs from 'fs' const __filename = fileURLToPath(import.meta.url) const __dirname = dirname(__filename) -export default defineConfig({ - // Platform 应用的基础路径 - base: '/platform/', - - plugins: [ - vue({ - script: { - defineModel: true, - propsDestructure: true - } - }), - vueJsx(), - federation({ - name: 'platform', - remotes: { - shared: { - type: 'module', - name: 'shared', - entry: 'https://org.xyzh.yslg/shared/remoteEntry.js' +// 开发环境 shared 模块地址 +const DEV_SHARED_URL = 'https://localhost:7000/shared/mf-manifest.json' +// 生产环境使用相对路径,通过 Nginx 代理访问 +const PROD_SHARED_URL = '/shared/mf-manifest.json' + +export default defineConfig(({ mode }) => { + const isDev = mode === 'development' + const sharedEntry = isDev ? DEV_SHARED_URL : PROD_SHARED_URL + + return { + base: '/platform/', + + plugins: [ + vue({ + script: { + defineModel: true, + propsDestructure: true } + }), + vueJsx(), + federation({ + name: 'platform', + remotes: { + shared: { + type: 'module', + name: 'shared', + entry: sharedEntry + } + }, + shared: { + vue: {}, + 'vue-router': {}, + 'element-plus': {}, + axios: {} + } + }) + ], + + define: { + __VUE_OPTIONS_API__: true, + __VUE_PROD_DEVTOOLS__: true, + __VUE_PROD_HYDRATION_MISMATCH_DETAILS__: true + }, + + resolve: { + alias: { + '@': resolve(__dirname, 'src') + } + }, + + server: { + port: 7001, + host: true, + cors: true, + open: '/', + https: (() => { + try { + return { + key: fs.readFileSync('C:/Users/FK05/443/localhost+3-key.pem'), + cert: fs.readFileSync('C:/Users/FK05/443/localhost+3.pem') + } + } catch { + return undefined + } + })(), + hmr: { + path: '/@vite/client', + port: 7001 }, - shared: { - vue: {}, - 'vue-router': {}, - 'element-plus': {}, - axios: {} + proxy: { + '/api': { + target: 'http://localhost:8180', + changeOrigin: true, + rewrite: (path: string) => path.replace(/^\/api/, '') + } } - }) - ], - - define: { - __VUE_OPTIONS_API__: true, - __VUE_PROD_DEVTOOLS__: true, - __VUE_PROD_HYDRATION_MISMATCH_DETAILS__: true - }, - - resolve: { - alias: { - '@': resolve(__dirname, 'src') - } - }, - - server: { - port: 7001, - host: true, - cors: true, - open: '/', // 开发时自动打开到根路径 - // HTTPS 配置(使用 mkcert 生成的本地开发证书) - https: { - key: fs.readFileSync('C:/Users/FK05/443/localhost+3-key.pem'), - cert: fs.readFileSync('C:/Users/FK05/443/localhost+3.pem') }, - hmr: { - // 修复 base 路径导致的 WebSocket 连接问题 - path: '/@vite/client', - port: 7001 - }, - proxy: { - '/api': { - target: 'http://localhost:8180', - changeOrigin: true, - rewrite: (path: string) => path.replace(/^\/api/, '') - } - } - }, - - build: { - outDir: 'dist', - sourcemap: true, - rollupOptions: { - output: { - manualChunks: { - 'vue-vendor': ['vue', 'vue-router', 'pinia'], - 'element-plus': ['element-plus'] + + build: { + outDir: 'dist', + sourcemap: true, + rollupOptions: { + output: { + manualChunks: { + 'vue-vendor': ['vue', 'vue-router', 'pinia'], + 'element-plus': ['element-plus'] + } } } } diff --git a/urbanLifelineWeb/packages/workcase/public/app-config.js b/urbanLifelineWeb/packages/workcase/public/app-config.js index 327e13e4..31b1976d 100644 --- a/urbanLifelineWeb/packages/workcase/public/app-config.js +++ b/urbanLifelineWeb/packages/workcase/public/app-config.js @@ -1,24 +1,22 @@ /** - * @description 应用运行时配置文件 - * @author yslg - * @since 2025-12-06 + * @description 应用运行时配置文件 (支持 Docker 环境变量替换) * - * 说明: - * 1. 此文件在生产环境中被加载,用于覆盖内置配置 - * 2. Docker 部署时,可通过挂载此文件来修改配置,无需重新构建 - * 3. 配置结构必须与 packages/shared/src/config/index.ts 中的 AppRuntimeConfig 保持一致 + * 占位符说明: + * - __PLACEHOLDER__ 格式的值会在 Docker 启动时被环境变量替换 + * - 如果环境变量未设置,将使用默认值 * - * 使用示例(Docker): - * docker run -v /path/to/app-config.js:/app/public/app-config.js my-app:latest + * Docker 部署: + * 1. 通过 volume 挂载覆盖此文件 + * 2. 或通过启动脚本替换占位符 */ window.APP_RUNTIME_CONFIG = { // 环境标识 - env: 'production', + env: '__APP_ENV__', // API 配置 api: { - baseUrl: '/api', + baseUrl: '__API_BASE_URL__', timeout: 30000 }, @@ -27,12 +25,12 @@ window.APP_RUNTIME_CONFIG = { // 文件配置 file: { - downloadUrl: '/api/urban-lifeline/file/download/', - uploadUrl: '/api/urban-lifeline/file/upload', + downloadUrl: '__API_BASE_URL__/urban-lifeline/file/download/', + uploadUrl: '__API_BASE_URL__/urban-lifeline/file/upload', maxSize: { - image: 5, // MB - video: 100, // MB - document: 10 // MB + image: 5, + video: 100, + document: 10 }, acceptTypes: { image: 'image/*', @@ -44,18 +42,26 @@ window.APP_RUNTIME_CONFIG = { // Token 配置 token: { key: 'token', - refreshThreshold: 300000 // 5分钟 + refreshThreshold: 300000 }, // 公共资源路径 - publicImgPath: '/img', - publicWebPath: '/', + publicImgPath: '__PUBLIC_PATH__/img', + publicWebPath: '__PUBLIC_PATH__', // 单点登录配置 sso: { - platformUrl: '/', // platform 平台地址 - workcaseUrl: '/workcase', // workcase 服务地址 - biddingUrl: '/bidding' // bidding 服务地址 + platformUrl: '__SSO_PLATFORM_URL__', + workcaseUrl: '__SSO_WORKCASE_URL__', + biddingUrl: '__SSO_BIDDING_URL__' + }, + + // AES 加密密钥 + aesSecretKey: '__AES_SECRET_KEY__', + + // Jitsi 视频会议配置 + jitsi: { + serverUrl: '__JITSI_SERVER_URL__' }, // 功能开关 diff --git a/urbanLifelineWeb/packages/workcase/src/config/index.ts b/urbanLifelineWeb/packages/workcase/src/config/index.ts index dfd9f344..06923192 100644 --- a/urbanLifelineWeb/packages/workcase/src/config/index.ts +++ b/urbanLifelineWeb/packages/workcase/src/config/index.ts @@ -1,28 +1,12 @@ /** * @description 应用运行时配置 - * @author yslg - * @since 2025-12-06 * * 配置加载策略: - * 1. 开发环境:使用下面定义的开发配置 + * 1. 开发环境:使用内置开发配置 * 2. 生产环境:从 window.APP_RUNTIME_CONFIG 读取(来自 app-config.js) - * 3. Docker部署:替换 app-config.js 文件实现配置外挂 - * - * 配置结构说明: - * 此文件的配置结构与 app-config.js 完全对应 - * 修改 app-config.js 后,这里的配置会自动应用 + * 3. Docker部署:启动时替换 app-config.js 中的占位符 */ -// ============================================ -// AES 加密密钥 -// ============================================ -/** - * AES 加密密钥(与后端保持一致) - * 对应后端配置:security.aes.secret-key - * Base64 编码的 32 字节密钥(256 位) - */ -export const AES_SECRET_KEY = 'MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=' // Base64 编码,解码后是 "12345678901234567890123456789012" (32字节) -export const AGENT_ID = '17664699513920001' // ============================================ // 类型定义 // ============================================ @@ -53,11 +37,14 @@ export interface AppRuntimeConfig { }; publicImgPath: string; publicWebPath: string; - // 单点登录配置 sso?: { - platformUrl: string; // platform 平台地址 - workcaseUrl: string; // workcase 服务地址 - biddingUrl: string; // bidding 服务地址 + platformUrl: string; + workcaseUrl: string; + biddingUrl: string; + }; + aesSecretKey?: string; + jitsi?: { + serverUrl: string; }; features?: { enableDebug?: boolean; @@ -67,103 +54,86 @@ export interface AppRuntimeConfig { } // ============================================ -// 配置定义(与 app-config.js 结构一致) +// 环境检测 // ============================================ const isDev = (import.meta as any).env?.DEV ?? false; +// ============================================ // 开发环境配置 +// ============================================ const devConfig: AppRuntimeConfig = { env: 'development', - api: { - // 开发环境通过 Vite 代理转发到后端,避免浏览器直接跨域 - // 实际请求路径示例:/api/... → 由代理转发到实际后端 baseUrl: '/api', timeout: 30000 }, - baseUrl: '/', - file: { - // 通过 Nginx → Gateway 访问文件服务,使用 /urban-lifeline 前缀 downloadUrl: '/api/urban-lifeline/file/download/', uploadUrl: '/api/urban-lifeline/file/upload', - maxSize: { - image: 5, - video: 100, - document: 10 - }, + maxSize: { image: 5, video: 100, document: 10 }, acceptTypes: { image: 'image/*', video: 'video/*', document: '.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx' } }, - token: { key: 'token', refreshThreshold: 300000 }, - - publicImgPath: 'http://localhost:5173/img', - publicWebPath: 'http://localhost:5173', - - // 单点登录配置 - // 推荐:开发环境也通过nginx访问(http://localhost) - // 备选:直接访问各服务端口(platformUrl: 'http://localhost:7001') + publicImgPath: '/img', + publicWebPath: '/', sso: { - platformUrl: '/', // 通过nginx访问platform - workcaseUrl: '/workcase', // 通过nginx访问workcase - biddingUrl: '/bidding' // 通过nginx访问bidding + platformUrl: '/', + workcaseUrl: '/workcase', + biddingUrl: '/bidding' + }, + aesSecretKey: 'MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=', + jitsi: { + serverUrl: 'https://meet.example.com' }, - features: { enableDebug: true, enableMockData: false } }; +// ============================================ // 生产环境默认配置(兜底) +// ============================================ const prodDefaultConfig: AppRuntimeConfig = { env: 'production', - api: { baseUrl: '/api', timeout: 30000 }, - baseUrl: '/', - file: { - downloadUrl: '/api/file/download/', - uploadUrl: '/api/file/upload', - maxSize: { - image: 5, - video: 100, - document: 10 - }, + downloadUrl: '/api/urban-lifeline/file/download/', + uploadUrl: '/api/urban-lifeline/file/upload', + maxSize: { image: 5, video: 100, document: 10 }, acceptTypes: { image: 'image/*', video: 'video/*', document: '.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx' } }, - token: { key: 'token', refreshThreshold: 300000 }, - publicImgPath: '/img', publicWebPath: '/', - - // 单点登录配置(生产环境通过nginx代理) sso: { platformUrl: '/', workcaseUrl: '/workcase', biddingUrl: '/bidding' }, - + aesSecretKey: 'MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=', + jitsi: { + serverUrl: 'https://meet.example.com' + }, features: { enableDebug: false, enableMockData: false @@ -174,96 +144,90 @@ const prodDefaultConfig: AppRuntimeConfig = { // 配置加载 // ============================================ +/** + * 检查值是否为未替换的占位符 + */ +const isPlaceholder = (value: any): boolean => { + return typeof value === 'string' && value.startsWith('__') && value.endsWith('__'); +}; + +/** + * 深度合并配置,跳过占位符值 + */ +const mergeConfig = (target: any, source: any): any => { + const result = { ...target }; + for (const key in source) { + const value = source[key]; + if (value && typeof value === 'object' && !Array.isArray(value)) { + result[key] = mergeConfig(target[key] || {}, value); + } else if (!isPlaceholder(value)) { + result[key] = value; + } + } + return result; +}; + /** * 获取运行时配置 - * 生产环境优先从 window.APP_RUNTIME_CONFIG 读取(app-config.js 注入) */ const getRuntimeConfig = (): AppRuntimeConfig => { if (isDev) { - console.log('[配置] 开发环境,使用内置配置'); + console.log('[Config] 开发环境,使用内置配置'); return devConfig; } - // 生产环境:尝试读取外部配置 try { const runtimeConfig = (window as any).APP_RUNTIME_CONFIG; if (runtimeConfig && typeof runtimeConfig === 'object') { - console.log('[配置] 加载外部配置 app-config.js'); - console.log('[配置] API地址:', runtimeConfig.api?.baseUrl); - console.log('[配置] 环境:', runtimeConfig.env || 'production'); - return runtimeConfig as AppRuntimeConfig; + const merged = mergeConfig(prodDefaultConfig, runtimeConfig); + console.log('[Config] 加载运行时配置', merged); + return merged; } } catch (e) { - console.warn('[配置] 无法读取外部配置,使用默认配置', e); + console.warn('[Config] 无法读取外部配置', e); } - console.log('[配置] 使用默认生产配置'); + console.log('[Config] 使用默认生产配置'); return prodDefaultConfig; }; -// 当前应用配置 +// 当前配置 const config = getRuntimeConfig(); -console.log('[配置] 当前配置', config); // ============================================ -// 导出配置(向后兼容) +// 导出 // ============================================ -// 单独导出常用配置项 +// AES 密钥 +export const AES_SECRET_KEY = config.aesSecretKey || 'MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI='; + +// AI Agent ID +export const AGENT_ID = '17664699513920001'; + +// 常用配置项 export const API_BASE_URL = config.api.baseUrl; export const FILE_DOWNLOAD_URL = config.file.downloadUrl; export const FILE_UPLOAD_URL = config.file.uploadUrl; export const PUBLIC_IMG_PATH = config.publicImgPath; export const PUBLIC_WEB_PATH = config.publicWebPath; - -// 文件上传大小限制(100MB) export const FILE_MAX_SIZE = 100 * 1024 * 1024; -// 导出完整配置对象 +// Jitsi 配置 +export const JITSI_SERVER_URL = config.jitsi?.serverUrl || 'https://meet.example.com'; + +// 完整配置对象 export const APP_CONFIG = { - // 应用标题 - title: '泰豪电源 AI 数智化平台', - - // 环境标识 + title: '泰豪电源工单系统', env: config.env || 'production', - - // 应用基础路径 baseUrl: config.baseUrl, - - // API 配置 - api: { - baseUrl: config.api.baseUrl, - timeout: config.api.timeout - }, - - // 文件配置 - file: { - downloadUrl: config.file.downloadUrl, - uploadUrl: config.file.uploadUrl, - maxSize: config.file.maxSize, - acceptTypes: config.file.acceptTypes - }, - - // Token 配置 - token: { - key: config.token.key, - refreshThreshold: config.token.refreshThreshold - }, - - // 公共路径 + api: config.api, + file: config.file, + token: config.token, publicImgPath: config.publicImgPath, publicWebPath: config.publicWebPath, - - // 单点登录配置 - sso: config.sso || { - platformUrl: '/', - workcaseUrl: '/workcase', - biddingUrl: '/bidding' - }, - - // 功能开关 + sso: config.sso || { platformUrl: '/', workcaseUrl: '/workcase', biddingUrl: '/bidding' }, + jitsi: config.jitsi || { serverUrl: 'https://meet.example.com' }, features: config.features || {} }; -// 默认导出 export default APP_CONFIG; diff --git a/urbanLifelineWeb/packages/workcase/vite.config.ts b/urbanLifelineWeb/packages/workcase/vite.config.ts index 0b4f85e0..d246badb 100644 --- a/urbanLifelineWeb/packages/workcase/vite.config.ts +++ b/urbanLifelineWeb/packages/workcase/vite.config.ts @@ -9,84 +9,97 @@ import fs from 'fs' const __filename = fileURLToPath(import.meta.url) const __dirname = dirname(__filename) -export default defineConfig(({ mode }) => ({ - // 开发和生产环境都通过nginx代理访问/workcase - base: '/workcase/', - - plugins: [ - vue({ - script: { - defineModel: true, - propsDestructure: true - } - }), - vueJsx(), - federation({ - name: 'workcase', - remotes: { - shared: { - type: 'module', - name: 'shared', - entry: 'https://org.xyzh.yslg/shared/remoteEntry.js' +// 开发环境 shared 模块地址 +const DEV_SHARED_URL = 'https://localhost:7000/shared/mf-manifest.json' +// 生产环境使用相对路径,通过 Nginx 代理访问 +const PROD_SHARED_URL = '/shared/mf-manifest.json' + +export default defineConfig(({ mode }) => { + const isDev = mode === 'development' + const sharedEntry = isDev ? DEV_SHARED_URL : PROD_SHARED_URL + + return { + base: '/workcase/', + + plugins: [ + vue({ + script: { + defineModel: true, + propsDestructure: true } + }), + vueJsx(), + federation({ + name: 'workcase', + remotes: { + shared: { + type: 'module', + name: 'shared', + entry: sharedEntry + } + }, + shared: { + vue: {}, + 'vue-router': {}, + 'element-plus': {}, + axios: {} + } + }) + ], + + define: { + __VUE_OPTIONS_API__: true, + __VUE_PROD_DEVTOOLS__: true, + __VUE_PROD_HYDRATION_MISMATCH_DETAILS__: true, + global: 'globalThis' + }, + + resolve: { + alias: { + '@': resolve(__dirname, 'src') + } + }, + + server: { + port: 7003, + host: true, + cors: true, + open: '/workcase/', + https: (() => { + try { + return { + key: fs.readFileSync('C:/Users/FK05/443/localhost+3-key.pem'), + cert: fs.readFileSync('C:/Users/FK05/443/localhost+3.pem') + } + } catch { + return undefined + } + })(), + hmr: { + path: '/@vite/client', + port: 7003 }, - shared: { - vue: {}, - 'vue-router': {}, - 'element-plus': {}, - axios: {} + proxy: { + '/api': { + target: 'http://localhost:8180', + changeOrigin: true, + ws: true, + rewrite: (path: string) => path.replace(/^\/api/, '') + } } - }) - ], - - define: { - __VUE_OPTIONS_API__: true, - __VUE_PROD_DEVTOOLS__: true, - __VUE_PROD_HYDRATION_MISMATCH_DETAILS__: true, - global: 'globalThis' - }, - - resolve: { - alias: { - '@': resolve(__dirname, 'src') - } - }, - - server: { - port: 7003, - host: true, - cors: true, - open: '/workcase/', // 开发时自动打开到 /workcase/ 路径 - // HTTPS 配置(使用 mkcert 生成的本地开发证书) - https: { - key: fs.readFileSync('C:/Users/FK05/443/localhost+3-key.pem'), - cert: fs.readFileSync('C:/Users/FK05/443/localhost+3.pem') }, - hmr: { - // 修复 base 路径导致的 WebSocket 连接问题 - path: '/@vite/client', - port: 7003 - }, - proxy: { - '/api': { - target: 'http://localhost:8180', - changeOrigin: true, - ws: true, // 启用 WebSocket 代理 - rewrite: (path: string) => path.replace(/^\/api/, '') - } - } - }, - - build: { - outDir: 'dist', - sourcemap: true, - rollupOptions: { - output: { - manualChunks: { - 'vue-vendor': ['vue', 'vue-router', 'pinia'], - 'element-plus': ['element-plus'] + + build: { + outDir: 'dist', + sourcemap: true, + rollupOptions: { + output: { + manualChunks: { + 'vue-vendor': ['vue', 'vue-router', 'pinia'], + 'element-plus': ['element-plus'] + } } } } } -})) +})