服务打包初步结构

This commit is contained in:
2026-01-02 14:56:14 +08:00
parent 19026c1b30
commit 89bc8bf1d4
77 changed files with 5290 additions and 2070 deletions

View File

@@ -29,6 +29,7 @@ networks:
# 共享卷定义 # 共享卷定义
volumes: volumes:
# 基础设施
nacos-data: nacos-data:
driver: local driver: local
nacos-logs: nacos-logs:
@@ -37,6 +38,7 @@ volumes:
driver: local driver: local
minio-config: minio-config:
driver: local driver: local
# Jitsi
jitsi-web: jitsi-web:
driver: local driver: local
jitsi-prosody: jitsi-prosody:
@@ -45,7 +47,13 @@ volumes:
driver: local driver: local
jitsi-jvb: jitsi-jvb:
driver: local driver: local
# 后端服务
serv-logs: serv-logs:
driver: local driver: local
serv-config:
driver: local
# 前端服务
web-logs: web-logs:
driver: local driver: local
web-config:
driver: local

View File

@@ -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

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -6,6 +6,10 @@
# 镜像版本 # 镜像版本
IMAGE_VERSION=latest IMAGE_VERSION=latest
# 数据目录 (日志、配置等)
DATA_ROOT=../volumes
# ==================== 后端配置 ====================
# Spring 运行环境 # Spring 运行环境
SPRING_PROFILES_ACTIVE=prod SPRING_PROFILES_ACTIVE=prod
@@ -13,21 +17,30 @@ SPRING_PROFILES_ACTIVE=prod
NACOS_SERVER_ADDR=nacos:8848 NACOS_SERVER_ADDR=nacos:8848
NACOS_NAMESPACE= 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 配置
MINIO_ENDPOINT=http://minio:9000 MINIO_ROOT_USER=minioadmin
MINIO_ROOT_PASSWORD=minioadmin123
# Dify AI 配置 # Dify AI 配置
DIFY_API_URL=http://dify-api:5001 DIFY_API_URL=http://dify-api:5001
# 日志目录 # ==================== Jitsi 视频会议 ====================
LOG_ROOT=../../volumes/logs JWT_APP_ID=urbanLifeline
JWT_APP_SECRET=urbanLifeline-jitsi-secret-key-2025-production-safe-hs256
# 前端 API 地址 JVB_HOST_ADDRESS=192.168.0.253
API_BASE_URL=http://gateway:8080 JITSI_PUBLIC_URL=https://org.xyzh.yslg.jitsi
# 端口映射
GATEWAY_PORT=8080
PLATFORM_PORT=8001
WORKCASE_PORT=8002
BIDDING_WEB_PORT=8003
WORKCASE_WECHAT_PORT=8004

View File

@@ -1,28 +1,26 @@
# Urban Lifeline Docker 部署 # Urban Lifeline Docker 部署 (All-in-One)
## 目录结构 ## 目录结构
``` ```
docker/urbanLifeline/ docker/urbanLifeline/
├── .env.example # 环境变量示例 ├── .env.example # 环境变量示例
├── Makefile # 快捷命令
├── README.md # 本文档 ├── README.md # 本文档
├── serv/ # 后端服务 ├── serv/ # 后端服务
│ ├── docker-compose.yml
│ ├── build.sh # 构建脚本 │ ├── build.sh # 构建脚本
│ ├── start.sh # 启动脚本
│ ├── Dockerfile.base # 基础镜像 │ ├── Dockerfile.base # 基础镜像
│ ├── Dockerfile.template # 服务模板 │ ├── Dockerfile.serv # 服务镜像
│ └── Dockerfile.* # 服务 Dockerfile │ └── service-manager.sh # 服务管理脚本
└── web/ # 前端应用 └── web/ # 前端应用
├── docker-compose.yml
├── build.sh # 构建脚本 ├── build.sh # 构建脚本
├── nginx.conf # Nginx 配置 ├── Dockerfile.web # 前端镜像
└── Dockerfile.* # 前端 Dockerfile └── web-manager.sh # 前端管理脚本
``` ```
## 服务端口 ## 服务端口
### 后端服务 ### 后端服务 (urban-lifeline-serv)
| 服务 | 端口 | 说明 | | 服务 | 端口 | 说明 |
|------|------|------| |------|------|------|
| gateway | 8080 | API 网关 | | gateway | 8080 | API 网关 |
@@ -37,56 +35,82 @@ docker/urbanLifeline/
| platform | 8089 | 平台服务 | | platform | 8089 | 平台服务 |
| ai | 8090 | AI 服务 | | ai | 8090 | AI 服务 |
### 前端应用 ### 前端应用 (urban-lifeline-web)
| 应用 | 端口 | 说明 | | 应用 | 端口 | 说明 |
|------|------|------| |------|------|------|
| shared | 8000 | 公共模块 (Module Federation) |
| platform | 8001 | 管理平台 | | platform | 8001 | 管理平台 |
| workcase-web | 8002 | 工单系统 PC | | workcase | 8002 | 工单系统 PC |
| bidding-web | 8003 | 招标系统 | | bidding | 8003 | 招标系统 |
| workcase-wechat | 8004 | 工单微信端 | | workcase_wechat | 8004 | 工单微信端 |
## 快速开始 ## 快速开始
### 1. 准备环境变量 ### 1. 准备环境变量
```bash ```bash
cd docker/urbanLifeline
cp .env.example .env cp .env.example .env
# 编辑 .env 配置 # 编辑 .env 配置
``` ```
### 2. 构建后端 ### 2. 构建镜像
```bash ```bash
# 先编译 Java 项目 # 构建后端 (需先编译 Java)
cd urbanLifelineServ cd urbanLifelineServ && mvn clean package -DskipTests
mvn clean package -DskipTests cd docker/urbanLifeline && make build-serv
# 构建 Docker 镜像 # 构建前端 (需先构建前端)
cd docker/urbanLifeline/serv cd urbanLifelineWeb && pnpm build
./build.sh # 构建所有 cd docker/urbanLifeline && make build-web
./build.sh gateway # 构建单个
``` ```
### 3. 构建前端 ### 3. 启动服务
```bash ```bash
# 先构建前端项目 make up
cd urbanLifelineWeb
pnpm install
pnpm build
# 构建 Docker 镜像
cd docker/urbanLifeline/web
./build.sh # 构建所有
./build.sh platform # 构建单个
``` ```
### 4. 启动服务 ### 4. 管理服务
```bash ```bash
# 启动后端 # 查看状态
cd docker/urbanLifeline/serv make status
docker-compose --profile serv up -d
# 启动前端 # 重启单个后端服务
cd docker/urbanLifeline/web make restart-serv SERVICE=gateway
docker-compose --profile web up -d
# 重启单个前端站点
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 (缓存) - Redis (缓存)
- MinIO (对象存储) - MinIO (对象存储)
通过 `docker/infra` 目录启动基础设施。 通过 `docker compose --profile infra up -d` 启动基础设施。

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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 "$@"

View File

@@ -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

View File

@@ -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知识库IDDataset 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知识库IDDataset 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)
);

View File

@@ -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 '投标模板表';

View File

@@ -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 '是否删除';

View File

@@ -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:是)';

View File

@@ -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;

View File

@@ -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 '是否删除';

View File

@@ -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;

View File

@@ -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 '部门私有角色的所属部门IDscope=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 时,是否包含其子级(便于“所有子级可查看”场景)
-- allowtrue=允许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;

View File

@@ -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 btreePostgres 默认即为 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 '创建时间';

View File

@@ -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, -- 来源IDroom_id/workcase_idNULL表示全局统计
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 '词云统计表,记录聊天和工单中的关键词';

View File

@@ -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
-- 注意:文件、日志、知识库、招投标、客服等业务表无需初始化数据

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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

View File

@@ -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"]

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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} image: urban-lifeline-serv:${IMAGE_VERSION:-latest}
container_name: urban-lifeline-serv container_name: urban-lifeline-serv
restart: unless-stopped restart: unless-stopped
env_file:
- .env
networks: networks:
- urban-lifeline - urban-lifeline
ports: ports:
- "8080:8080" # gateway - "${GATEWAY_PORT:-8080}:8080"
- "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
environment: environment:
TZ: Asia/Shanghai TZ: Asia/Shanghai
SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE:-prod} SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE:-prod}
NACOS_SERVER_ADDR: ${NACOS_SERVER_ADDR:-nacos:8848} NACOS_SERVER_ADDR: ${NACOS_SERVER_ADDR:-nacos:8848}
NACOS_NAMESPACE: ${NACOS_NAMESPACE:-} NACOS_NAMESPACE: ${NACOS_NAMESPACE:-}
volumes: 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: deploy:
resources: resources:
limits: limits:
memory: 4G memory: ${MEMORY_LIMIT:-4G}
reservations: reservations:
memory: 2G memory: ${MEMORY_RESERVATION:-2G}
healthcheck: healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"] test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"]
interval: 30s interval: 30s
@@ -45,4 +41,4 @@ services:
networks: networks:
urban-lifeline: urban-lifeline:
external: true name: urban-lifeline

View File

@@ -17,6 +17,7 @@ set -e
JAR_DIR="/app/jars" JAR_DIR="/app/jars"
LOG_DIR="/app/logs" LOG_DIR="/app/logs"
PID_DIR="/app/pids" PID_DIR="/app/pids"
CONFIG_DIR="/app/config"
# 服务配置: 服务名=端口:内存 # 服务配置: 服务名=端口:内存
declare -A SERVICES=( declare -A SERVICES=(
@@ -98,6 +99,8 @@ start_service() {
local jar_file="${JAR_DIR}/${service}.jar" local jar_file="${JAR_DIR}/${service}.jar"
local log_file="${LOG_DIR}/${service}.log" local log_file="${LOG_DIR}/${service}.log"
local pid_file="${PID_DIR}/${service}.pid" 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 if [ ! -f "$jar_file" ]; then
log_error "JAR 文件不存在: $jar_file" log_error "JAR 文件不存在: $jar_file"
@@ -127,6 +130,24 @@ start_service() {
java_opts="$java_opts -Dspring.cloud.nacos.config.namespace=$NACOS_NAMESPACE" java_opts="$java_opts -Dspring.cloud.nacos.config.namespace=$NACOS_NAMESPACE"
fi 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 & nohup java $java_opts -jar "$jar_file" > "$log_file" 2>&1 &
local pid=$! local pid=$!

View File

@@ -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

View File

@@ -8,7 +8,7 @@ FROM node:20-alpine
ENV TZ=Asia/Shanghai 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 \ && ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& echo "Asia/Shanghai" > /etc/timezone && 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/bidding/dist/ /app/sites/bidding/
COPY urbanLifelineWeb/packages/workcase_wechat/dist/ /app/sites/workcase_wechat/ COPY urbanLifelineWeb/packages/workcase_wechat/dist/ /app/sites/workcase_wechat/
# 默认端口 (可通过环境变量覆盖) # ============================================
# 端口配置 (可通过环境变量覆盖)
# ============================================
ENV SHARED_PORT=8000 \ ENV SHARED_PORT=8000 \
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
# 配置和日志目录 (可外挂)
VOLUME ["/app/config", "/app/logs"]
EXPOSE 8000 8001 8002 8003 8004 EXPOSE 8000 8001 8002 8003 8004
HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \ HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \

View File

@@ -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
}
};

View File

@@ -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
}
};

View File

@@ -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
}
};

View File

@@ -8,29 +8,38 @@ services:
image: urban-lifeline-web:${IMAGE_VERSION:-latest} image: urban-lifeline-web:${IMAGE_VERSION:-latest}
container_name: urban-lifeline-web container_name: urban-lifeline-web
restart: unless-stopped restart: unless-stopped
env_file:
- .env
networks: networks:
- urban-lifeline - urban-lifeline
ports: ports:
- "${PLATFORM_PORT:-8001}:${PLATFORM_PORT:-8001}" - "${SHARED_PORT:-8000}:8000"
- "${WORKCASE_PORT:-8002}:${WORKCASE_PORT:-8002}" - "${PLATFORM_PORT:-8001}:8001"
- "${BIDDING_PORT:-8003}:${BIDDING_PORT:-8003}" - "${WORKCASE_PORT:-8002}:8002"
- "${WORKCASE_WECHAT_PORT:-8004}:${WORKCASE_WECHAT_PORT:-8004}" - "${BIDDING_PORT:-8003}:8003"
- "${WORKCASE_WECHAT_PORT:-8004}:8004"
environment: environment:
TZ: Asia/Shanghai TZ: Asia/Shanghai
SHARED_PORT: ${SHARED_PORT:-8000}
PLATFORM_PORT: ${PLATFORM_PORT:-8001} PLATFORM_PORT: ${PLATFORM_PORT:-8001}
WORKCASE_PORT: ${WORKCASE_PORT:-8002} WORKCASE_PORT: ${WORKCASE_PORT:-8002}
BIDDING_PORT: ${BIDDING_PORT:-8003} BIDDING_PORT: ${BIDDING_PORT:-8003}
WORKCASE_WECHAT_PORT: ${WORKCASE_WECHAT_PORT:-8004} WORKCASE_WECHAT_PORT: ${WORKCASE_WECHAT_PORT:-8004}
volumes: 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: deploy:
resources: resources:
limits: limits:
memory: 512M memory: ${MEMORY_LIMIT:-512M}
reservations: reservations:
memory: 128M memory: ${MEMORY_RESERVATION:-128M}
healthcheck: healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:${PLATFORM_PORT:-8001}/"] test: ["CMD", "curl", "-f", "http://localhost:8000/"]
interval: 30s interval: 30s
timeout: 10s timeout: 10s
retries: 3 retries: 3
@@ -38,4 +47,4 @@ services:
networks: networks:
urban-lifeline: urban-lifeline:
external: true name: urban-lifeline

View File

@@ -15,9 +15,43 @@ set -e
SITES_DIR="/app/sites" SITES_DIR="/app/sites"
PID_DIR="/app/pids" PID_DIR="/app/pids"
LOG_DIR="/app/logs" LOG_DIR="/app/logs"
CONFIG_DIR="/app/config"
mkdir -p "$PID_DIR" "$LOG_DIR" 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=( declare -A WEBS=(
["shared"]="SHARED_PORT:8000" ["shared"]="SHARED_PORT:8000"
@@ -83,6 +117,7 @@ wait_for_web() {
# 启动单个前端 # 启动单个前端
start_web() { start_web() {
local web=$1 local web=$1
local skip_config=$2
local port=$(get_port "$web") local port=$(get_port "$web")
local site_dir="${SITES_DIR}/${web}" local site_dir="${SITES_DIR}/${web}"
local pid_file="${PID_DIR}/${web}.pid" local pid_file="${PID_DIR}/${web}.pid"
@@ -98,6 +133,11 @@ start_web() {
return 0 return 0
fi fi
# 同步配置 (除非已经批量同步过)
if [ "$skip_config" != "skip_config" ]; then
sync_mounted_config "$web"
fi
log_info "启动 $web (端口: $port)..." log_info "启动 $web (端口: $port)..."
# 使用 serve 启动静态服务 # 使用 serve 启动静态服务
@@ -151,8 +191,11 @@ start_all() {
log_info " Urban Lifeline - 启动所有前端" log_info " Urban Lifeline - 启动所有前端"
log_info "==========================================" log_info "=========================================="
# 同步所有挂载的配置文件
sync_all_configs
for web in "${BOOT_ORDER[@]}"; do for web in "${BOOT_ORDER[@]}"; do
start_web "$web" start_web "$web" "skip_config"
log_info "等待 $web 就绪..." log_info "等待 $web 就绪..."
if wait_for_web "$web"; then if wait_for_web "$web"; then

9
docker/域名.md Normal file
View File

@@ -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

View File

@@ -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 入口和内部视图
-**用户体验**: 统一的导航风格,清晰的层级结构

View File

@@ -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 管理端权限

View File

@@ -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

View File

@@ -1,10 +1,17 @@
# ================== Server ================== # ================== AI 服务配置 ==================
server: server:
port: 8190 port: 8090
# servlet:
# context-path: /urban-lifeline/agent # 微服务架构下context-path由Gateway管理
# ================== Auth ==================== spring:
application:
name: ai-service
servlet:
multipart:
enabled: true
max-file-size: 500MB
max-request-size: 500MB
# ================== Auth ==================
auth: auth:
enabled: true enabled: true
gateway-mode: true gateway-mode: true
@@ -17,79 +24,21 @@ auth:
- /error - /error
- /actuator/health - /actuator/health
- /actuator/info - /actuator/info
- /ai/chat/** # AI对话有非系统用户对话的接口无登录状态 - /ai/chat/**
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 # 如果有密码就填上,没密码可以去掉这一行
# ================== SpringDoc ================== # ================== SpringDoc ==================
springdoc: springdoc:
api-docs:
enabled: true
path: /v3/api-docs
swagger-ui:
enabled: true
path: /swagger-ui.html
group-configs: group-configs:
- group: 'default' - group: 'default'
display-name: 'AI代理服务 API' display-name: 'AI代理服务 API'
paths-to-match: '/**' paths-to-match: '/**'
# ================== Dubbo + Nacos ================== # ================== Dubbo ==================
dubbo: dubbo:
application: application:
name: urban-lifeline-agent name: urban-lifeline-agent
qos-enable: false qos-enable: false
protocol: protocol:
payload: 110100480 payload: 110100480
name: dubbo
port: -1
registry:
address: nacos://127.0.0.1:8848
scan: scan:
base-packages: org.xyzh.ai.service.impl 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

View File

@@ -1,97 +1,35 @@
# ================== Server ================== # ================== Auth 认证服务配置 ==================
server: server:
port: 8181 port: 8081
# servlet:
# context-path: /urban-lifeline/auth # 微服务架构下context-path由Gateway管理服务本身不需要设置
# ================== Auth ====================
auth:
enabled: false # 认证服务自己不需要认证
gateway-mode: false # 不使用gateway模式auth服务作为独立服务
whitelist:
- /** # 认证服务的所有接口都放行
security:
aes:
# AES-256 密钥Base64编码必须与所有服务保持一致
# 警告:这是开发环境密钥,生产环境请使用密钥管理系统
secret-key: MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=
# ================== Spring ==================
spring: spring:
application: application:
name: auth-service 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 ================== # ================== Auth ==================
data: auth:
redis: enabled: false # 认证服务自己不需要认证
host: 127.0.0.1 # 如果是 docker 跑的 redis按实际 host / 端口改 gateway-mode: false
port: 6379 whitelist:
database: 0 - /**
password: 123456 # 如果有密码就填上,没密码可以去掉这一行
# ================== SpringDoc ================== # ================== SpringDoc ==================
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-configs:
- group: 'default' - group: 'default'
display-name: '认证服务 API' display-name: '认证服务 API'
paths-to-match: '/**' paths-to-match: '/**'
# ================== Dubbo + Nacos ================== # ================== Dubbo ==================
dubbo: dubbo:
application: application:
name: urban-lifeline-auth name: urban-lifeline-auth
qos-enable: false qos-enable: false
protocol:
name: dubbo
port: -1
registry:
address: nacos://127.0.0.1:8848
scan: scan:
base-packages: org.xyzh.auth.service.impl base-packages: org.xyzh.auth.service.impl
# ================== MyBatis ================== # ================== JWT ==================
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小时 expiration: 86400 # 24小时
refresh-expiration: 604800 # 7天 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

View File

@@ -1,89 +1,36 @@
# ================== Server ================== # ================== Bidding 招投标服务配置 ==================
server: server:
port: 8186 port: 8087
# servlet:
# context-path: /urban-lifeline/bidding # 微服务架构下context-path由Gateway管理
# ================== 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: spring:
application: application:
name: bidding-service 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 ================== # ================== Auth ==================
data: auth:
redis: enabled: true
host: 127.0.0.1 # 如果是 docker 跑的 redis按实际 host / 端口改 gateway-mode: true
port: 6379 whitelist:
database: 0 - /swagger-ui/**
password: 123456 # 如果有密码就填上,没密码可以去掉这一行 - /swagger-ui.html
- /v3/api-docs/**
- /webjars/**
- /favicon.ico
- /error
- /actuator/health
- /actuator/info
# ================== SpringDoc ================== # ================== SpringDoc ==================
springdoc: springdoc:
api-docs:
enabled: true
path: /v3/api-docs
swagger-ui:
enabled: true
path: /swagger-ui.html
group-configs: group-configs:
- group: 'default' - group: 'default'
display-name: '招投标服务 API' display-name: '招投标服务 API'
paths-to-match: '/**' paths-to-match: '/**'
# ================== Dubbo + Nacos ================== # ================== Dubbo ==================
dubbo: dubbo:
application: application:
name: urban-lifeline-bidding name: urban-lifeline-bidding
qos-enable: false qos-enable: false
protocol:
name: dubbo
port: -1
registry:
address: nacos://127.0.0.1:8848
scan: scan:
base-packages: org.xyzh.bidding.service.impl 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

View File

@@ -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

View File

@@ -1,80 +1,35 @@
# ================== Server ================== # ================== Crontab 定时任务服务配置 ==================
server: server:
port: 8189 port: 8086
# servlet:
# context-path: /urban-lifeline/crontab # 微服务架构下context-path由Gateway管理
# ================== Auth ====================
urban-lifeline:
auth:
enabled: false # 定时任务服务通常不需要认证
security:
aes:
secret-key: 1234567890qwer
# ================== Spring ==================
spring: spring:
application: application:
name: crontab-service 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 ================== # ================== Auth ==================
data: auth:
redis: enabled: false # 定时任务服务不需要认证
host: 127.0.0.1 # 如果是 docker 跑的 redis按实际 host / 端口改 whitelist:
port: 6379 - /swagger-ui/**
database: 0 - /swagger-ui.html
password: 123456 # 如果有密码就填上,没密码可以去掉这一行 - /v3/api-docs/**
- /webjars/**
- /favicon.ico
- /error
- /actuator/health
- /actuator/info
# ================== SpringDoc ================== # ================== SpringDoc ==================
springdoc: springdoc:
api-docs:
enabled: true
path: /v3/api-docs
swagger-ui:
enabled: true
path: /swagger-ui.html
group-configs: group-configs:
- group: 'default' - group: 'default'
display-name: '定时任务服务 API' display-name: '定时任务服务 API'
paths-to-match: '/**' paths-to-match: '/**'
# ================== Dubbo + Nacos ================== # ================== Dubbo ==================
dubbo: dubbo:
application: application:
name: urban-lifeline-crontab name: urban-lifeline-crontab
qos-enable: false qos-enable: false
protocol:
name: dubbo
port: -1
registry:
address: nacos://127.0.0.1:8848
scan: scan:
base-packages: org.xyzh.crontab.service.impl 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

View File

@@ -1,10 +1,17 @@
# ================== Server ================== # ================== File 文件服务配置 ==================
server: server:
port: 8184 port: 8084
# servlet:
# context-path: /urban-lifeline/file # 微服务架构下context-path由Gateway管理
# ================== Auth ==================== spring:
application:
name: file-service
servlet:
multipart:
enabled: true
max-file-size: 500MB
max-request-size: 500MB
# ================== Auth ==================
auth: auth:
enabled: true enabled: true
gateway-mode: true gateway-mode: true
@@ -19,83 +26,19 @@ auth:
- /actuator/info - /actuator/info
- /file/download/** - /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 ==================
springdoc: springdoc:
api-docs:
enabled: true
path: /v3/api-docs
swagger-ui:
enabled: true
path: /swagger-ui.html
group-configs: group-configs:
- group: 'default' - group: 'default'
display-name: '文件服务 API' display-name: '文件服务 API'
paths-to-match: '/**' paths-to-match: '/**'
# ================== Dubbo + Nacos ================== # ================== Dubbo ==================
dubbo: dubbo:
application: application:
name: urban-lifeline-file name: urban-lifeline-file
qos-enable: false qos-enable: false
protocol: protocol:
payload: 110100480 payload: 110100480
name: dubbo
port: -1
registry:
address: nacos://127.0.0.1:8848
scan: scan:
base-packages: org.xyzh.file.service.impl 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

View File

@@ -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

View File

@@ -1,50 +1,40 @@
# ================== Gateway 服务配置 ==================
server: server:
port: 8180 port: 8080
spring: spring:
application: application:
name: gateway-service name: gateway-service
# Gateway 必须使用 reactive 模式WebFlux不能使用 Spring MVC # Gateway 必须使用 reactive 模式
main: main:
web-application-type: reactive web-application-type: reactive
# 配置中心
cloud: cloud:
nacos: nacos:
discovery:
server-addr: ${NACOS_SERVER_ADDR:localhost:8848}
namespace: dev
group: DEFAULT_GROUP
config: config:
enabled: false # 禁用Nacos配置中心使用本地配置 enabled: false
server-addr: ${NACOS_SERVER_ADDR:localhost:8848}
file-extension: yml
namespace: dev
group: DEFAULT_GROUP
# Gateway 路由配置 # Gateway 路由配置
gateway: gateway:
# 服务发现路由(自动路由)
discovery: discovery:
locator: locator:
enabled: false # 关闭自动路由,使用手动配置 enabled: false
# 手动配置路由
routes: routes:
# ==================== 认证服务路由 ==================== # 认证服务
- id: auth-service - id: auth-service
uri: lb://auth-service uri: lb://auth-service
predicates: predicates:
- Path=/urban-lifeline/auth/** - Path=/urban-lifeline/auth/**
filters: filters:
- StripPrefix=1 # 去掉前缀:/urban-lifeline/auth/login → /auth/login - StripPrefix=1
- name: RequestRateLimiter - name: RequestRateLimiter
args: args:
redis-rate-limiter.replenishRate: 100 redis-rate-limiter.replenishRate: 100
redis-rate-limiter.burstCapacity: 200 redis-rate-limiter.burstCapacity: 200
# ==================== 系统服务路由 ==================== # 系统服务
- id: system-service - id: system-service
uri: lb://system-service uri: lb://system-service
predicates: predicates:
@@ -52,7 +42,7 @@ spring:
filters: filters:
- StripPrefix=1 - StripPrefix=1
# ==================== 日志服务路由 ==================== # 日志服务
- id: log-service - id: log-service
uri: lb://log-service uri: lb://log-service
predicates: predicates:
@@ -60,7 +50,7 @@ spring:
filters: filters:
- StripPrefix=1 - StripPrefix=1
# ==================== 文件服务路由 ==================== # 文件服务
- id: file-service - id: file-service
uri: lb://file-service uri: lb://file-service
predicates: predicates:
@@ -68,7 +58,7 @@ spring:
filters: filters:
- StripPrefix=1 - StripPrefix=1
# ==================== 消息服务路由 ==================== # 消息服务
- id: message-service - id: message-service
uri: lb://message-service uri: lb://message-service
predicates: predicates:
@@ -76,7 +66,7 @@ spring:
filters: filters:
- StripPrefix=1 - StripPrefix=1
# ==================== 招投标服务路由 ==================== # 招投标服务
- id: bidding-service - id: bidding-service
uri: lb://bidding-service uri: lb://bidding-service
predicates: predicates:
@@ -84,7 +74,7 @@ spring:
filters: filters:
- StripPrefix=1 - StripPrefix=1
# ==================== 平台服务路由 ==================== # 平台服务
- id: platform-service - id: platform-service
uri: lb://platform-service uri: lb://platform-service
predicates: predicates:
@@ -92,7 +82,7 @@ spring:
filters: filters:
- StripPrefix=1 - StripPrefix=1
# ==================== 工单服务 WebSocket 路由 ==================== # 工单服务 WebSocket
- id: workcase-websocket - id: workcase-websocket
uri: lb:ws://workcase-service uri: lb:ws://workcase-service
predicates: predicates:
@@ -100,7 +90,7 @@ spring:
filters: filters:
- StripPrefix=1 - StripPrefix=1
# ==================== 工单服务路由 ==================== # 工单服务
- id: workcase-service - id: workcase-service
uri: lb://workcase-service uri: lb://workcase-service
predicates: predicates:
@@ -108,7 +98,7 @@ spring:
filters: filters:
- StripPrefix=1 - StripPrefix=1
# ==================== 定时任务服务路由 ==================== # 定时任务服务
- id: crontab-service - id: crontab-service
uri: lb://crontab-service uri: lb://crontab-service
predicates: predicates:
@@ -116,7 +106,7 @@ spring:
filters: filters:
- StripPrefix=1 - StripPrefix=1
# ==================== AI 服务路由 ==================== # AI 服务
- id: ai-service - id: ai-service
uri: lb://ai-service uri: lb://ai-service
predicates: predicates:
@@ -129,28 +119,14 @@ spring:
cors-configurations: cors-configurations:
'[/**]': '[/**]':
allowedOriginPatterns: "*" allowedOriginPatterns: "*"
allowedMethods: allowedMethods: [GET, POST, PUT, DELETE, OPTIONS]
- GET
- POST
- PUT
- DELETE
- OPTIONS
allowedHeaders: "*" allowedHeaders: "*"
allowCredentials: true allowCredentials: true
maxAge: 3600 maxAge: 3600
datasource:
# 按你的实际库名改一下,比如 urban-lifeline_system # Redis 连接池配置Gateway 限流用)
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: data:
redis: redis:
host: ${REDIS_HOST:localhost}
port: ${REDIS_PORT:6379}
password: 123456
database: 0
timeout: 5000ms timeout: 5000ms
lettuce: lettuce:
pool: pool:
@@ -159,18 +135,15 @@ spring:
max-idle: 10 max-idle: 10
min-idle: 5 min-idle: 5
# 认证配置 # ================== Gateway 认证配置 ==================
auth: auth:
enabled: true enabled: true
# gateway-mode 是给下游微服务用的gateway本身不需要此配置
token-header: Authorization token-header: Authorization
token-prefix: "Bearer " token-prefix: "Bearer "
# 认证接口白名单login/logout/captcha/refresh
login-path: /urban-lifeline/auth/login login-path: /urban-lifeline/auth/login
logout-path: /urban-lifeline/auth/logout logout-path: /urban-lifeline/auth/logout
captcha-path: /urban-lifeline/auth/captcha captcha-path: /urban-lifeline/auth/captcha
refresh-path: /urban-lifeline/auth/refresh refresh-path: /urban-lifeline/auth/refresh
# 通用白名单Swagger、健康检查等
whitelist: whitelist:
- /actuator/** - /actuator/**
- /v3/api-docs/** - /v3/api-docs/**
@@ -180,20 +153,14 @@ auth:
- /doc.html - /doc.html
- /favicon.ico - /favicon.ico
- /error - /error
# 各服务的 Swagger 文档
- /urban-lifeline/*/v3/api-docs/** - /urban-lifeline/*/v3/api-docs/**
- /urban-lifeline/*/swagger-ui/** - /urban-lifeline/*/swagger-ui/**
# file 服务白名单
- /urban-lifeline/file/download/** - /urban-lifeline/file/download/**
# ai 服务白名单
- /urban-lifeline/ai/chat/** - /urban-lifeline/ai/chat/**
- /urban-lifeline/system/guest/identify - /urban-lifeline/system/guest/identify
# workcase 会议入口白名单支持URL参数token认证
- /urban-lifeline/workcase/meeting/*/entry - /urban-lifeline/workcase/meeting/*/entry
security:
aes: # ================== Actuator ==================
secret-key: MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI= # Base64 编码32字节256位
# Actuator 监控端点
management: management:
endpoints: endpoints:
web: web:
@@ -202,12 +169,3 @@ management:
endpoint: endpoint:
health: health:
show-details: always 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

View File

@@ -1,11 +1,12 @@
# ================== Server ================== # ================== Message 消息服务配置 ==================
server: server:
port: 8185 port: 8085
# servlet:
# context-path: /urban-lifeline/message # 微服务架构下context-path由Gateway管理
# ================== Auth ==================== spring:
application:
name: message-service
# ================== Auth ==================
auth: auth:
enabled: true enabled: true
gateway-mode: true gateway-mode: true
@@ -18,74 +19,18 @@ auth:
- /error - /error
- /actuator/health - /actuator/health
- /actuator/info - /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 ==================
springdoc: springdoc:
api-docs:
enabled: true
path: /v3/api-docs
swagger-ui:
enabled: true
path: /swagger-ui.html
group-configs: group-configs:
- group: 'default' - group: 'default'
display-name: '消息服务 API' display-name: '消息服务 API'
paths-to-match: '/**' paths-to-match: '/**'
# ================== Dubbo + Nacos ================== # ================== Dubbo ==================
dubbo: dubbo:
application: application:
name: urban-lifeline-message name: urban-lifeline-message
qos-enable: false qos-enable: false
protocol:
name: dubbo
port: -1
registry:
address: nacos://127.0.0.1:8848
scan: scan:
base-packages: org.xyzh.message.service.impl 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

View File

@@ -1,11 +1,12 @@
# ================== Server ================== # ================== Platform 平台服务配置 ==================
server: server:
port: 8187 port: 8089
# servlet:
# context-path: /urban-lifeline/platform # 微服务架构下context-path由Gateway管理
# ================== Auth ==================== spring:
application:
name: platform-service
# ================== Auth ==================
auth: auth:
enabled: true enabled: true
gateway-mode: true gateway-mode: true
@@ -19,74 +20,17 @@ auth:
- /actuator/health - /actuator/health
- /actuator/info - /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 ==================
springdoc: springdoc:
api-docs:
enabled: true
path: /v3/api-docs
swagger-ui:
enabled: true
path: /swagger-ui.html
group-configs: group-configs:
- group: 'default' - group: 'default'
display-name: '平台服务 API' display-name: '平台服务 API'
paths-to-match: '/**' paths-to-match: '/**'
# ================== Dubbo + Nacos ================== # ================== Dubbo ==================
dubbo: dubbo:
application: application:
name: urban-lifeline-platform name: urban-lifeline-platform
qos-enable: false qos-enable: false
protocol:
name: dubbo
port: -1
registry:
address: nacos://127.0.0.1:8848
scan: scan:
base-packages: org.xyzh.platform.service.impl 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

View File

@@ -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

View File

@@ -1,83 +1,32 @@
# ================== Server ================== # ================== System 系统服务配置 ==================
server: server:
port: 8182 port: 8082
# servlet:
# context-path: /urban-lifeline/system # 微服务架构下context-path由Gateway管理
# ================== Auth ====================
spring:
application:
name: system-service
# ================== Auth ==================
auth: auth:
enabled: true enabled: true
gateway-mode: 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: whitelist:
# Swagger/OpenAPI 文档相关(建议不带 context-path
- /swagger-ui/** - /swagger-ui/**
- /swagger-ui.html - /swagger-ui.html
- /v3/api-docs/** - /v3/api-docs/**
- /webjars/** - /webjars/**
# 静态资源
- /favicon.ico - /favicon.ico
- /error - /error
# 健康检查
- /actuator/health - /actuator/health
- /actuator/info - /actuator/info
- /system/guest/identify - /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 ==================
springdoc: springdoc:
api-docs:
enabled: true
path: /v3/api-docs
swagger-ui: swagger-ui:
enabled: true
path: /swagger-ui.html
try-it-out-enabled: true try-it-out-enabled: true
show-common-extensions: true show-common-extensions: true
show-extensions: true show-extensions: true
show-request-duration: true
filter: true filter: true
tags-sorter: alpha tags-sorter: alpha
operations-sorter: alpha operations-sorter: alpha
@@ -86,37 +35,10 @@ springdoc:
display-name: '系统服务 API' display-name: '系统服务 API'
paths-to-match: '/**' paths-to-match: '/**'
# ================== Dubbo + Nacos 注册中心 ================== # ================== Dubbo ==================
dubbo: dubbo:
application: application:
name: urban-lifeline-system name: urban-lifeline-system
qos-enable: false 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: scan:
base-packages: org.xyzh.system.service.impl 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

View File

@@ -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

View File

@@ -1,10 +1,17 @@
# ================== Server ================== # ================== Workcase 工单服务配置 ==================
server: server:
port: 8188 port: 8088
# servlet:
# context-path: /urban-lifeline/workcase # 微服务架构下context-path由Gateway管理
# ================== Auth ==================== spring:
application:
name: workcase-service
servlet:
multipart:
enabled: true
max-file-size: 500MB
max-request-size: 500MB
# ================== Auth ==================
auth: auth:
enabled: true enabled: true
gateway-mode: true gateway-mode: true
@@ -17,81 +24,32 @@ auth:
- /error - /error
- /actuator/health - /actuator/health
- /actuator/info - /actuator/info
# 微信客服回调接口(无需鉴权)
- /workcase/chat/kefu/callback - /workcase/chat/kefu/callback
# CRM回调接口无需鉴权但需签名验证
- /workcase/receive/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 ==================
springdoc: springdoc:
api-docs:
enabled: true
path: /v3/api-docs
swagger-ui:
enabled: true
path: /swagger-ui.html
group-configs: group-configs:
- group: 'default' - group: 'default'
display-name: '工单服务 API' display-name: '工单服务 API'
paths-to-match: '/**' paths-to-match: '/**'
# ================== Dubbo + Nacos ================== # ================== Dubbo ==================
dubbo: dubbo:
application: application:
name: urban-lifeline-workcase name: urban-lifeline-workcase
qos-enable: false qos-enable: false
protocol: protocol:
name: dubbo payload: 110100480
port: -1
registry:
address: nacos://127.0.0.1:8848
scan: scan:
base-packages: org.xyzh.workcase.service.impl base-packages: org.xyzh.workcase.service.impl
# ================== MyBatis ================== # ================== Jitsi Meet 视频会议配置 ==================
mybatis-plus: jitsi:
mapper-locations: classpath:mapper/**/*.xml app:
type-aliases-package: org.xyzh.common.dto, org.xyzh.api id: ${JITSI_APP_ID:urbanLifeline}
secret: ${JITSI_APP_SECRET:urbanLifeline-jitsi-secret-key-2025-production-safe-hs256}
# ================== Logging ================== server:
logging: url: ${JITSI_SERVER_URL:https://org.xyzh.yslg.jitsi}
config: classpath:log4j2.xml token:
charset: expiration: 7200000
console: UTF-8
file: UTF-8
level:
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping: TRACE

View File

@@ -1,24 +1,22 @@
/** /**
* @description 应用运行时配置文件 * @description 应用运行时配置文件 (支持 Docker 环境变量替换)
* @author yslg
* @since 2025-12-06
* *
* 说明: * 占位符说明:
* 1. 此文件在生产环境中被加载,用于覆盖内置配置 * - __PLACEHOLDER__ 格式的值会在 Docker 启动时被环境变量替换
* 2. Docker 部署时,可通过挂载此文件来修改配置,无需重新构建 * - 如果环境变量未设置,将使用默认值
* 3. 配置结构必须与 packages/shared/src/config/index.ts 中的 AppRuntimeConfig 保持一致
* *
* 使用示例(Docker * Docker 部署
* docker run -v /path/to/app-config.js:/app/public/app-config.js my-app:latest * 1. 通过 volume 挂载覆盖此文件
* 2. 或通过启动脚本替换占位符
*/ */
window.APP_RUNTIME_CONFIG = { window.APP_RUNTIME_CONFIG = {
// 环境标识 // 环境标识
env: 'production', env: '__APP_ENV__',
// API 配置 // API 配置
api: { api: {
baseUrl: '/api', baseUrl: '__API_BASE_URL__',
timeout: 30000 timeout: 30000
}, },
@@ -27,12 +25,12 @@ window.APP_RUNTIME_CONFIG = {
// 文件配置 // 文件配置
file: { file: {
downloadUrl: '/api/urban-lifeline/file/download/', downloadUrl: '__API_BASE_URL__/urban-lifeline/file/download/',
uploadUrl: '/api/urban-lifeline/file/upload', uploadUrl: '__API_BASE_URL__/urban-lifeline/file/upload',
maxSize: { maxSize: {
image: 5, // MB image: 5,
video: 100, // MB video: 100,
document: 10 // MB document: 10
}, },
acceptTypes: { acceptTypes: {
image: 'image/*', image: 'image/*',
@@ -44,20 +42,23 @@ window.APP_RUNTIME_CONFIG = {
// Token 配置 // Token 配置
token: { token: {
key: 'token', key: 'token',
refreshThreshold: 300000 // 5分钟 refreshThreshold: 300000
}, },
// 公共资源路径 // 公共资源路径
publicImgPath: '/img', publicImgPath: '__PUBLIC_PATH__/img',
publicWebPath: '/', publicWebPath: '__PUBLIC_PATH__',
// 单点登录配置 // 单点登录配置
sso: { sso: {
platformUrl: '/', // platform 平台地址 platformUrl: '__SSO_PLATFORM_URL__',
workcaseUrl: '/workcase', // workcase 服务地址 workcaseUrl: '__SSO_WORKCASE_URL__',
biddingUrl: '/bidding' // bidding 服务地址 biddingUrl: '__SSO_BIDDING_URL__'
}, },
// AES 加密密钥
aesSecretKey: '__AES_SECRET_KEY__',
// 功能开关 // 功能开关
features: { features: {
enableDebug: false, enableDebug: false,

View File

@@ -1,16 +1,10 @@
/** /**
* @description Bidding 应用运行时配置 * @description 应用运行时配置
* @author yslg
* @since 2025-12-06
* *
* 配置加载策略: * 配置加载策略:
* 1. 开发环境:使用下面定义的开发配置 * 1. 开发环境:使用内置开发配置
* 2. 生产环境:从 window.APP_RUNTIME_CONFIG 读取(来自 app-config.js * 2. 生产环境:从 window.APP_RUNTIME_CONFIG 读取(来自 app-config.js
* 3. Docker部署替换 app-config.js 文件实现配置外挂 * 3. Docker部署启动时替换 app-config.js 中的占位符
*
* 配置结构说明:
* 此文件的配置结构与 app-config.js 完全对应
* 修改 app-config.js 后,这里的配置会自动应用
*/ */
// ============================================ // ============================================
@@ -43,12 +37,12 @@ export interface AppRuntimeConfig {
}; };
publicImgPath: string; publicImgPath: string;
publicWebPath: string; publicWebPath: string;
// 单点登录配置
sso?: { sso?: {
platformUrl: string; // platform 平台地址 platformUrl: string;
workcaseUrl: string; // workcase 服务地址 workcaseUrl: string;
biddingUrl: string; // bidding 服务地址 biddingUrl: string;
}; };
aesSecretKey?: string;
features?: { features?: {
enableDebug?: boolean; enableDebug?: boolean;
enableMockData?: boolean; enableMockData?: boolean;
@@ -57,101 +51,80 @@ export interface AppRuntimeConfig {
} }
// ============================================ // ============================================
// 配置定义(与 app-config.js 结构一致) // 环境检测
// ============================================ // ============================================
const isDev = (import.meta as any).env?.DEV ?? false; const isDev = (import.meta as any).env?.DEV ?? false;
// ============================================
// 开发环境配置 // 开发环境配置
// ============================================
const devConfig: AppRuntimeConfig = { const devConfig: AppRuntimeConfig = {
env: 'development', env: 'development',
api: { api: {
// 开发环境通过 Vite 代理转发到后端,避免浏览器直接跨域
// 实际请求路径示例:/api/... → 由代理转发到实际后端
baseUrl: '/api', baseUrl: '/api',
timeout: 30000 timeout: 30000
}, },
baseUrl: '/', baseUrl: '/',
file: { file: {
// 通过 Nginx → Gateway 访问文件服务,使用 /urban-lifeline 前缀
downloadUrl: '/api/urban-lifeline/file/download/', downloadUrl: '/api/urban-lifeline/file/download/',
uploadUrl: '/api/urban-lifeline/file/upload', uploadUrl: '/api/urban-lifeline/file/upload',
maxSize: { maxSize: { image: 5, video: 100, document: 10 },
image: 5,
video: 100,
document: 10
},
acceptTypes: { acceptTypes: {
image: 'image/*', image: 'image/*',
video: 'video/*', video: 'video/*',
document: '.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx' document: '.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx'
} }
}, },
token: { token: {
key: 'token', key: 'token',
refreshThreshold: 300000 refreshThreshold: 300000
}, },
publicImgPath: '/img',
publicImgPath: 'http://localhost:7002/img', publicWebPath: '/',
publicWebPath: 'http://localhost:7002',
// 单点登录配置
sso: { sso: {
platformUrl: '/', // 通过nginx访问platform platformUrl: '/',
workcaseUrl: '/workcase', // 通过nginx访问workcase workcaseUrl: '/workcase',
biddingUrl: '/bidding' // 通过nginx访问bidding biddingUrl: '/bidding'
}, },
aesSecretKey: 'MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=',
features: { features: {
enableDebug: true, enableDebug: true,
enableMockData: false enableMockData: false
} }
}; };
// ============================================
// 生产环境默认配置(兜底) // 生产环境默认配置(兜底)
// ============================================
const prodDefaultConfig: AppRuntimeConfig = { const prodDefaultConfig: AppRuntimeConfig = {
env: 'production', env: 'production',
api: { api: {
baseUrl: '/api', baseUrl: '/api',
timeout: 30000 timeout: 30000
}, },
baseUrl: '/', baseUrl: '/',
file: { file: {
downloadUrl: '/api/urban-lifeline/file/download/', downloadUrl: '/api/urban-lifeline/file/download/',
uploadUrl: '/api/urban-lifeline/file/upload', uploadUrl: '/api/urban-lifeline/file/upload',
maxSize: { maxSize: { image: 5, video: 100, document: 10 },
image: 5,
video: 100,
document: 10
},
acceptTypes: { acceptTypes: {
image: 'image/*', image: 'image/*',
video: 'video/*', video: 'video/*',
document: '.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx' document: '.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx'
} }
}, },
token: { token: {
key: 'token', key: 'token',
refreshThreshold: 300000 refreshThreshold: 300000
}, },
publicImgPath: '/img', publicImgPath: '/img',
publicWebPath: '/', publicWebPath: '/',
// 单点登录配置生产环境通过nginx代理
sso: { sso: {
platformUrl: '/', platformUrl: '/',
workcaseUrl: '/workcase', workcaseUrl: '/workcase',
biddingUrl: '/bidding' biddingUrl: '/bidding'
}, },
aesSecretKey: 'MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=',
features: { features: {
enableDebug: false, enableDebug: false,
enableMockData: 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 => { const getRuntimeConfig = (): AppRuntimeConfig => {
if (isDev) { if (isDev) {
console.log('[配置] 开发环境,使用内置配置'); console.log('[Config] 开发环境,使用内置配置');
return devConfig; return devConfig;
} }
// 生产环境:尝试读取外部配置
try { try {
const runtimeConfig = (window as any).APP_RUNTIME_CONFIG; const runtimeConfig = (window as any).APP_RUNTIME_CONFIG;
if (runtimeConfig && typeof runtimeConfig === 'object') { if (runtimeConfig && typeof runtimeConfig === 'object') {
console.log('[配置] 加载外部配置 app-config.js'); const merged = mergeConfig(prodDefaultConfig, runtimeConfig);
console.log('[配置] API地址:', runtimeConfig.api?.baseUrl); console.log('[Config] 加载运行时配置', merged);
console.log('[配置] 环境:', runtimeConfig.env || 'production'); return merged;
return runtimeConfig as AppRuntimeConfig;
} }
} catch (e) { } catch (e) {
console.warn('[配置] 无法读取外部配置,使用默认配置', e); console.warn('[Config] 无法读取外部配置', e);
} }
console.log('[配置] 使用默认生产配置'); console.log('[Config] 使用默认生产配置');
return prodDefaultConfig; return prodDefaultConfig;
}; };
// 当前应用配置 // 当前配置
const config = getRuntimeConfig(); 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 API_BASE_URL = config.api.baseUrl;
export const FILE_DOWNLOAD_URL = config.file.downloadUrl; 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_IMG_PATH = config.publicImgPath;
export const PUBLIC_WEB_PATH = config.publicWebPath; export const PUBLIC_WEB_PATH = config.publicWebPath;
// 导出完整配置对象 // 完整配置对象
export const APP_CONFIG = { export const APP_CONFIG = {
// 应用标题
title: '泰豪电源招投标系统', title: '泰豪电源招投标系统',
// 环境标识
env: config.env || 'production', env: config.env || 'production',
// 应用基础路径
baseUrl: config.baseUrl, baseUrl: config.baseUrl,
api: config.api,
// API 配置 file: config.file,
api: { token: config.token,
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
},
// 公共路径
publicImgPath: config.publicImgPath, publicImgPath: config.publicImgPath,
publicWebPath: config.publicWebPath, publicWebPath: config.publicWebPath,
sso: config.sso || { platformUrl: '/', workcaseUrl: '/workcase', biddingUrl: '/bidding' },
// 单点登录配置
sso: config.sso || {
platformUrl: '/',
workcaseUrl: '/workcase',
biddingUrl: '/bidding'
},
// 功能开关
features: config.features || {} features: config.features || {}
}; };
// 默认导出
export default APP_CONFIG; export default APP_CONFIG;

View File

@@ -1,6 +1,7 @@
import { defineConfig } from 'vite' import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue' import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx' import vueJsx from '@vitejs/plugin-vue-jsx'
import { federation } from '@module-federation/vite'
import { resolve, dirname } from 'path' import { resolve, dirname } from 'path'
import { fileURLToPath } from 'url' import { fileURLToPath } from 'url'
import fs from 'fs' import fs from 'fs'
@@ -8,61 +9,95 @@ import fs from 'fs'
const __filename = fileURLToPath(import.meta.url) const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename) const __dirname = dirname(__filename)
export default defineConfig(({ mode }) => ({ // 开发环境 shared 模块地址
// 开发和生产环境都通过nginx代理访问/bidding const DEV_SHARED_URL = 'https://localhost:7000/shared/mf-manifest.json'
base: '/bidding/', // 生产环境使用相对路径,通过 Nginx 代理访问
const PROD_SHARED_URL = '/shared/mf-manifest.json'
plugins: [
vue({ export default defineConfig(({ mode }) => {
script: { const isDev = mode === 'development'
defineModel: true, const sharedEntry = isDev ? DEV_SHARED_URL : PROD_SHARED_URL
propsDestructure: true
} return {
}), base: '/bidding/',
vueJsx()
], plugins: [
vue({
define: { script: {
__VUE_OPTIONS_API__: true, defineModel: true,
__VUE_PROD_DEVTOOLS__: true, propsDestructure: true
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__: true }
}, }),
vueJsx(),
resolve: { federation({
alias: { name: 'bidding',
'@': resolve(__dirname, 'src') remotes: {
} shared: {
}, type: 'module',
name: 'shared',
server: { entry: sharedEntry
port: 7002, }
host: true, },
cors: true, shared: {
open: '/bidding/', // 开发时自动打开到 /bidding/ 路径 vue: {},
// HTTPS 配置(使用 mkcert 生成的本地开发证书) 'vue-router': {},
https: { 'element-plus': {},
key: fs.readFileSync('C:/Users/FK05/443/localhost+3-key.pem'), axios: {}
cert: fs.readFileSync('C:/Users/FK05/443/localhost+3.pem') }
})
],
define: {
__VUE_OPTIONS_API__: true,
__VUE_PROD_DEVTOOLS__: true,
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__: true
}, },
proxy: {
'/api': { resolve: {
target: 'http://localhost:8180', alias: {
changeOrigin: true, '@': resolve(__dirname, 'src')
rewrite: (path: string) => path.replace(/^\/api/, '')
} }
} },
},
server: {
build: { port: 7002,
outDir: 'dist', host: true,
sourcemap: true, cors: true,
rollupOptions: { open: '/bidding/',
output: { https: (() => {
manualChunks: { try {
'vue-vendor': ['vue', 'vue-router', 'pinia'], return {
'element-plus': ['element-plus'] 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']
}
} }
} }
} }
} }
})) })

View File

@@ -1,24 +1,22 @@
/** /**
* @description 应用运行时配置文件 * @description 应用运行时配置文件 (支持 Docker 环境变量替换)
* @author yslg
* @since 2025-12-06
* *
* 说明: * 占位符说明:
* 1. 此文件在生产环境中被加载,用于覆盖内置配置 * - __PLACEHOLDER__ 格式的值会在 Docker 启动时被环境变量替换
* 2. Docker 部署时,可通过挂载此文件来修改配置,无需重新构建 * - 如果环境变量未设置,将使用默认值
* 3. 配置结构必须与 packages/shared/src/config/index.ts 中的 AppRuntimeConfig 保持一致
* *
* 使用示例(Docker * Docker 部署
* docker run -v /path/to/app-config.js:/app/public/app-config.js my-app:latest * 1. 通过 volume 挂载覆盖此文件
* 2. 或通过启动脚本替换占位符
*/ */
window.APP_RUNTIME_CONFIG = { window.APP_RUNTIME_CONFIG = {
// 环境标识 // 环境标识
env: 'production', env: '__APP_ENV__',
// API 配置 // API 配置
api: { api: {
baseUrl: '/api', baseUrl: '__API_BASE_URL__',
timeout: 30000 timeout: 30000
}, },
@@ -27,12 +25,12 @@ window.APP_RUNTIME_CONFIG = {
// 文件配置 // 文件配置
file: { file: {
downloadUrl: '/api/urban-lifeline/file/download/', downloadUrl: '__API_BASE_URL__/urban-lifeline/file/download/',
uploadUrl: '/api/urban-lifeline/file/upload', uploadUrl: '__API_BASE_URL__/urban-lifeline/file/upload',
maxSize: { maxSize: {
image: 5, // MB image: 5,
video: 100, // MB video: 100,
document: 10 // MB document: 10
}, },
acceptTypes: { acceptTypes: {
image: 'image/*', image: 'image/*',
@@ -44,20 +42,23 @@ window.APP_RUNTIME_CONFIG = {
// Token 配置 // Token 配置
token: { token: {
key: 'token', key: 'token',
refreshThreshold: 300000 // 5分钟 refreshThreshold: 300000
}, },
// 公共资源路径 // 公共资源路径
publicImgPath: '/img', publicImgPath: '__PUBLIC_PATH__/img',
publicWebPath: '/', publicWebPath: '__PUBLIC_PATH__',
// 单点登录配置 // 单点登录配置
sso: { sso: {
platformUrl: '/', // platform 平台地址 platformUrl: '__SSO_PLATFORM_URL__',
workcaseUrl: '/workcase', // workcase 服务地址 workcaseUrl: '__SSO_WORKCASE_URL__',
biddingUrl: '/bidding' // bidding 服务地址 biddingUrl: '__SSO_BIDDING_URL__'
}, },
// AES 加密密钥
aesSecretKey: '__AES_SECRET_KEY__',
// 功能开关 // 功能开关
features: { features: {
enableDebug: false, enableDebug: false,

View File

@@ -1,28 +1,12 @@
/** /**
* @description Platform 应用运行时配置 * @description 应用运行时配置
* @author yslg
* @since 2025-12-06
* *
* 配置加载策略: * 配置加载策略:
* 1. 开发环境:使用下面定义的开发配置 * 1. 开发环境:使用内置开发配置
* 2. 生产环境:从 window.APP_RUNTIME_CONFIG 读取(来自 app-config.js * 2. 生产环境:从 window.APP_RUNTIME_CONFIG 读取(来自 app-config.js
* 3. Docker部署替换 app-config.js 文件实现配置外挂 * 3. Docker部署启动时替换 app-config.js 中的占位符
*
* 配置结构说明:
* 此文件的配置结构与 app-config.js 完全对应
* 修改 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; publicImgPath: string;
publicWebPath: string; publicWebPath: string;
// 单点登录配置
sso?: { sso?: {
platformUrl: string; // platform 平台地址 platformUrl: string;
workcaseUrl: string; // workcase 服务地址 workcaseUrl: string;
biddingUrl: string; // bidding 服务地址 biddingUrl: string;
}; };
aesSecretKey?: string;
features?: { features?: {
enableDebug?: boolean; enableDebug?: boolean;
enableMockData?: boolean; enableMockData?: boolean;
@@ -67,101 +51,80 @@ export interface AppRuntimeConfig {
} }
// ============================================ // ============================================
// 配置定义(与 app-config.js 结构一致) // 环境检测
// ============================================ // ============================================
const isDev = (import.meta as any).env?.DEV ?? false; const isDev = (import.meta as any).env?.DEV ?? false;
// ============================================
// 开发环境配置 // 开发环境配置
// ============================================
const devConfig: AppRuntimeConfig = { const devConfig: AppRuntimeConfig = {
env: 'development', env: 'development',
api: { api: {
// 开发环境通过 Vite 代理转发到后端,避免浏览器直接跨域
// 实际请求路径示例:/api/... → 由代理转发到实际后端
baseUrl: '/api', baseUrl: '/api',
timeout: 30000 timeout: 30000
}, },
baseUrl: '/', baseUrl: '/',
file: { file: {
// 通过 Nginx → Gateway 访问文件服务,使用 /urban-lifeline 前缀
downloadUrl: '/api/urban-lifeline/file/download/', downloadUrl: '/api/urban-lifeline/file/download/',
uploadUrl: '/api/urban-lifeline/file/upload', uploadUrl: '/api/urban-lifeline/file/upload',
maxSize: { maxSize: { image: 5, video: 100, document: 10 },
image: 5,
video: 100,
document: 10
},
acceptTypes: { acceptTypes: {
image: 'image/*', image: 'image/*',
video: 'video/*', video: 'video/*',
document: '.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx' document: '.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx'
} }
}, },
token: { token: {
key: 'token', key: 'token',
refreshThreshold: 300000 refreshThreshold: 300000
}, },
publicImgPath: '/img',
publicImgPath: 'http://localhost:7001/img', publicWebPath: '/',
publicWebPath: 'http://localhost:7001',
// 单点登录配置
sso: { sso: {
platformUrl: '/', // 通过nginx访问platform platformUrl: '/',
workcaseUrl: '/workcase', // 通过nginx访问workcase workcaseUrl: '/workcase',
biddingUrl: '/bidding' // 通过nginx访问bidding biddingUrl: '/bidding'
}, },
aesSecretKey: 'MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=',
features: { features: {
enableDebug: true, enableDebug: true,
enableMockData: false enableMockData: false
} }
}; };
// ============================================
// 生产环境默认配置(兜底) // 生产环境默认配置(兜底)
// ============================================
const prodDefaultConfig: AppRuntimeConfig = { const prodDefaultConfig: AppRuntimeConfig = {
env: 'production', env: 'production',
api: { api: {
baseUrl: '/api', baseUrl: '/api',
timeout: 30000 timeout: 30000
}, },
baseUrl: '/', baseUrl: '/',
file: { file: {
downloadUrl: '/api/urban-lifeline/file/download/', downloadUrl: '/api/urban-lifeline/file/download/',
uploadUrl: '/api/urban-lifeline/file/upload', uploadUrl: '/api/urban-lifeline/file/upload',
maxSize: { maxSize: { image: 5, video: 100, document: 10 },
image: 5,
video: 100,
document: 10
},
acceptTypes: { acceptTypes: {
image: 'image/*', image: 'image/*',
video: 'video/*', video: 'video/*',
document: '.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx' document: '.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx'
} }
}, },
token: { token: {
key: 'token', key: 'token',
refreshThreshold: 300000 refreshThreshold: 300000
}, },
publicImgPath: '/img', publicImgPath: '/img',
publicWebPath: '/', publicWebPath: '/',
// 单点登录配置生产环境通过nginx代理
sso: { sso: {
platformUrl: '/', platformUrl: '/',
workcaseUrl: '/workcase', workcaseUrl: '/workcase',
biddingUrl: '/bidding' biddingUrl: '/bidding'
}, },
aesSecretKey: 'MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=',
features: { features: {
enableDebug: false, enableDebug: false,
enableMockData: 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 => { const getRuntimeConfig = (): AppRuntimeConfig => {
if (isDev) { if (isDev) {
console.log('[配置] 开发环境,使用内置配置'); console.log('[Config] 开发环境,使用内置配置');
return devConfig; return devConfig;
} }
// 生产环境:尝试读取外部配置
try { try {
const runtimeConfig = (window as any).APP_RUNTIME_CONFIG; const runtimeConfig = (window as any).APP_RUNTIME_CONFIG;
if (runtimeConfig && typeof runtimeConfig === 'object') { if (runtimeConfig && typeof runtimeConfig === 'object') {
console.log('[配置] 加载外部配置 app-config.js'); // 合并配置,未替换的占位符使用默认值
console.log('[配置] API地址:', runtimeConfig.api?.baseUrl); const merged = mergeConfig(prodDefaultConfig, runtimeConfig);
console.log('[配置] 环境:', runtimeConfig.env || 'production'); console.log('[Config] 加载运行时配置', merged);
return runtimeConfig as AppRuntimeConfig; return merged;
} }
} catch (e) { } catch (e) {
console.warn('[配置] 无法读取外部配置,使用默认配置', e); console.warn('[Config] 无法读取外部配置', e);
} }
console.log('[配置] 使用默认生产配置'); console.log('[Config] 使用默认生产配置');
return prodDefaultConfig; return prodDefaultConfig;
}; };
// 当前应用配置 // 当前配置
const config = getRuntimeConfig(); 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 API_BASE_URL = config.api.baseUrl;
export const FILE_DOWNLOAD_URL = config.file.downloadUrl; 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_IMG_PATH = config.publicImgPath;
export const PUBLIC_WEB_PATH = config.publicWebPath; export const PUBLIC_WEB_PATH = config.publicWebPath;
// 导出完整配置对象 // 完整配置对象
export const APP_CONFIG = { export const APP_CONFIG = {
// 应用标题
title: '泰豪电源 AI 数智化平台', title: '泰豪电源 AI 数智化平台',
name: '泰豪电源 AI 数智化平台', name: '泰豪电源 AI 数智化平台',
version: '1.0.0', version: '1.0.0',
copyright: '泰豪电源', copyright: '泰豪电源',
// 环境标识
env: config.env || 'production', env: config.env || 'production',
// 应用基础路径
baseUrl: config.baseUrl, baseUrl: config.baseUrl,
api: config.api,
// API 配置 file: config.file,
api: { token: config.token,
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
},
// 公共路径
publicImgPath: config.publicImgPath, publicImgPath: config.publicImgPath,
publicWebPath: config.publicWebPath, publicWebPath: config.publicWebPath,
sso: config.sso || { platformUrl: '/', workcaseUrl: '/workcase', biddingUrl: '/bidding' },
// 单点登录配置
sso: config.sso || {
platformUrl: '/',
workcaseUrl: '/workcase',
biddingUrl: '/bidding'
},
// 功能开关
features: config.features || {} features: config.features || {}
}; };
// 默认导出
export default APP_CONFIG; export default APP_CONFIG;

View File

@@ -52,7 +52,7 @@ declare module 'shared/api' {
import type { AxiosResponse, AxiosRequestConfig } from 'axios' import type { AxiosResponse, AxiosRequestConfig } from 'axios'
interface ApiInstance { interface ApiInstance {
get<T = any>(url: string, config?: AxiosRequestConfig): Promise<AxiosResponse<T>> get<T = any>(url: string,data?: any, config?: AxiosRequestConfig): Promise<AxiosResponse<T>>
post<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<AxiosResponse<T>> post<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<AxiosResponse<T>>
put<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<AxiosResponse<T>> put<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<AxiosResponse<T>>
delete<T = any>(url: string, config?: AxiosRequestConfig): Promise<AxiosResponse<T>> delete<T = any>(url: string, config?: AxiosRequestConfig): Promise<AxiosResponse<T>>
@@ -61,8 +61,6 @@ declare module 'shared/api' {
export const api: ApiInstance export const api: ApiInstance
export const TokenManager: any export const TokenManager: any
export const authAPI: any
export const fileAPI: any
} }
declare module 'shared/api/auth' { declare module 'shared/api/auth' {
@@ -81,26 +79,52 @@ declare module 'shared/api/ai' {
// ============ types模块 ================== // ============ types模块 ==================
declare module 'shared/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 // 重新导出 response
export type { ResultDomain } from '../../../shared/src/types/response' export type { ResultDomain } from '../../../shared/src/types/response'
// 重新导出 page // 重新导出 page
export type { PageDomain, PageParam, PageRequest } from '../../../shared/src/types/page' export type { PageDomain, PageParam, PageRequest } from '../../../shared/src/types/page'
// 重新导出 auth // 重新导出 auth
export type { LoginParam, LoginDomain } from '../../../shared/src/types/auth' export type { LoginParam, LoginDomain } from '../../../shared/src/types/auth'
// 重新导出 sys // 重新导出 sys
export type { SysUserVO, SysConfigVO, TbSysViewDTO } from '../../../shared/src/types/sys' export type { SysUserVO, SysConfigVO, TbSysViewDTO } from '../../../shared/src/types/sys'
// 重新导出 file // 重新导出 file
export type { TbSysFileDTO } from '../../../shared/src/types/file' export type { TbSysFileDTO } from '../../../shared/src/types/file'
// 重新导出 ai // 重新导出 ai
export type { export type {
TbKnowledge, TbKnowledge,
TbKnowledgeFile, TbKnowledgeFile,
TbAgent, TbAgent,
PromptCard, PromptCard,
@@ -108,10 +132,17 @@ declare module 'shared/types' {
TbChatMessage, TbChatMessage,
DifyFileInfo, DifyFileInfo,
ChatPrepareData, ChatPrepareData,
StopChatParams, CreateChatParam,
CommentMessageParams StopChatParam,
CommentMessageParam,
ChatListParam,
ChatMessageListParam,
SSEMessageData,
SSECallbacks,
SSETask,
TbKnowledgeFileLog
} from '../../../shared/src/types/ai' } from '../../../shared/src/types/ai'
// 重新导出 menu // 重新导出 menu
export type { MenuItem, toMenuItem, toMenuItems } from '../../../shared/src/types/menu' export type { MenuItem, toMenuItem, toMenuItems } from '../../../shared/src/types/menu'
} }

View File

@@ -9,80 +9,93 @@ import fs from 'fs'
const __filename = fileURLToPath(import.meta.url) const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename) const __dirname = dirname(__filename)
export default defineConfig({ // 开发环境 shared 模块地址
// Platform 应用的基础路径 const DEV_SHARED_URL = 'https://localhost:7000/shared/mf-manifest.json'
base: '/platform/', // 生产环境使用相对路径,通过 Nginx 代理访问
const PROD_SHARED_URL = '/shared/mf-manifest.json'
plugins: [
vue({ export default defineConfig(({ mode }) => {
script: { const isDev = mode === 'development'
defineModel: true, const sharedEntry = isDev ? DEV_SHARED_URL : PROD_SHARED_URL
propsDestructure: true
} return {
}), base: '/platform/',
vueJsx(),
federation({ plugins: [
name: 'platform', vue({
remotes: { script: {
shared: { defineModel: true,
type: 'module', propsDestructure: true
name: 'shared',
entry: 'https://org.xyzh.yslg/shared/remoteEntry.js'
} }
}),
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: { proxy: {
vue: {}, '/api': {
'vue-router': {}, target: 'http://localhost:8180',
'element-plus': {}, changeOrigin: true,
axios: {} 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 连接问题 build: {
path: '/@vite/client', outDir: 'dist',
port: 7001 sourcemap: true,
}, rollupOptions: {
proxy: { output: {
'/api': { manualChunks: {
target: 'http://localhost:8180', 'vue-vendor': ['vue', 'vue-router', 'pinia'],
changeOrigin: true, 'element-plus': ['element-plus']
rewrite: (path: string) => path.replace(/^\/api/, '') }
}
}
},
build: {
outDir: 'dist',
sourcemap: true,
rollupOptions: {
output: {
manualChunks: {
'vue-vendor': ['vue', 'vue-router', 'pinia'],
'element-plus': ['element-plus']
} }
} }
} }

View File

@@ -1,24 +1,22 @@
/** /**
* @description 应用运行时配置文件 * @description 应用运行时配置文件 (支持 Docker 环境变量替换)
* @author yslg
* @since 2025-12-06
* *
* 说明: * 占位符说明:
* 1. 此文件在生产环境中被加载,用于覆盖内置配置 * - __PLACEHOLDER__ 格式的值会在 Docker 启动时被环境变量替换
* 2. Docker 部署时,可通过挂载此文件来修改配置,无需重新构建 * - 如果环境变量未设置,将使用默认值
* 3. 配置结构必须与 packages/shared/src/config/index.ts 中的 AppRuntimeConfig 保持一致
* *
* 使用示例(Docker * Docker 部署
* docker run -v /path/to/app-config.js:/app/public/app-config.js my-app:latest * 1. 通过 volume 挂载覆盖此文件
* 2. 或通过启动脚本替换占位符
*/ */
window.APP_RUNTIME_CONFIG = { window.APP_RUNTIME_CONFIG = {
// 环境标识 // 环境标识
env: 'production', env: '__APP_ENV__',
// API 配置 // API 配置
api: { api: {
baseUrl: '/api', baseUrl: '__API_BASE_URL__',
timeout: 30000 timeout: 30000
}, },
@@ -27,12 +25,12 @@ window.APP_RUNTIME_CONFIG = {
// 文件配置 // 文件配置
file: { file: {
downloadUrl: '/api/urban-lifeline/file/download/', downloadUrl: '__API_BASE_URL__/urban-lifeline/file/download/',
uploadUrl: '/api/urban-lifeline/file/upload', uploadUrl: '__API_BASE_URL__/urban-lifeline/file/upload',
maxSize: { maxSize: {
image: 5, // MB image: 5,
video: 100, // MB video: 100,
document: 10 // MB document: 10
}, },
acceptTypes: { acceptTypes: {
image: 'image/*', image: 'image/*',
@@ -44,18 +42,26 @@ window.APP_RUNTIME_CONFIG = {
// Token 配置 // Token 配置
token: { token: {
key: 'token', key: 'token',
refreshThreshold: 300000 // 5分钟 refreshThreshold: 300000
}, },
// 公共资源路径 // 公共资源路径
publicImgPath: '/img', publicImgPath: '__PUBLIC_PATH__/img',
publicWebPath: '/', publicWebPath: '__PUBLIC_PATH__',
// 单点登录配置 // 单点登录配置
sso: { sso: {
platformUrl: '/', // platform 平台地址 platformUrl: '__SSO_PLATFORM_URL__',
workcaseUrl: '/workcase', // workcase 服务地址 workcaseUrl: '__SSO_WORKCASE_URL__',
biddingUrl: '/bidding' // bidding 服务地址 biddingUrl: '__SSO_BIDDING_URL__'
},
// AES 加密密钥
aesSecretKey: '__AES_SECRET_KEY__',
// Jitsi 视频会议配置
jitsi: {
serverUrl: '__JITSI_SERVER_URL__'
}, },
// 功能开关 // 功能开关

View File

@@ -1,28 +1,12 @@
/** /**
* @description 应用运行时配置 * @description 应用运行时配置
* @author yslg
* @since 2025-12-06
* *
* 配置加载策略: * 配置加载策略:
* 1. 开发环境:使用下面定义的开发配置 * 1. 开发环境:使用内置开发配置
* 2. 生产环境:从 window.APP_RUNTIME_CONFIG 读取(来自 app-config.js * 2. 生产环境:从 window.APP_RUNTIME_CONFIG 读取(来自 app-config.js
* 3. Docker部署替换 app-config.js 文件实现配置外挂 * 3. Docker部署启动时替换 app-config.js 中的占位符
*
* 配置结构说明:
* 此文件的配置结构与 app-config.js 完全对应
* 修改 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; publicImgPath: string;
publicWebPath: string; publicWebPath: string;
// 单点登录配置
sso?: { sso?: {
platformUrl: string; // platform 平台地址 platformUrl: string;
workcaseUrl: string; // workcase 服务地址 workcaseUrl: string;
biddingUrl: string; // bidding 服务地址 biddingUrl: string;
};
aesSecretKey?: string;
jitsi?: {
serverUrl: string;
}; };
features?: { features?: {
enableDebug?: boolean; enableDebug?: boolean;
@@ -67,103 +54,86 @@ export interface AppRuntimeConfig {
} }
// ============================================ // ============================================
// 配置定义(与 app-config.js 结构一致) // 环境检测
// ============================================ // ============================================
const isDev = (import.meta as any).env?.DEV ?? false; const isDev = (import.meta as any).env?.DEV ?? false;
// ============================================
// 开发环境配置 // 开发环境配置
// ============================================
const devConfig: AppRuntimeConfig = { const devConfig: AppRuntimeConfig = {
env: 'development', env: 'development',
api: { api: {
// 开发环境通过 Vite 代理转发到后端,避免浏览器直接跨域
// 实际请求路径示例:/api/... → 由代理转发到实际后端
baseUrl: '/api', baseUrl: '/api',
timeout: 30000 timeout: 30000
}, },
baseUrl: '/', baseUrl: '/',
file: { file: {
// 通过 Nginx → Gateway 访问文件服务,使用 /urban-lifeline 前缀
downloadUrl: '/api/urban-lifeline/file/download/', downloadUrl: '/api/urban-lifeline/file/download/',
uploadUrl: '/api/urban-lifeline/file/upload', uploadUrl: '/api/urban-lifeline/file/upload',
maxSize: { maxSize: { image: 5, video: 100, document: 10 },
image: 5,
video: 100,
document: 10
},
acceptTypes: { acceptTypes: {
image: 'image/*', image: 'image/*',
video: 'video/*', video: 'video/*',
document: '.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx' document: '.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx'
} }
}, },
token: { token: {
key: 'token', key: 'token',
refreshThreshold: 300000 refreshThreshold: 300000
}, },
publicImgPath: '/img',
publicImgPath: 'http://localhost:5173/img', publicWebPath: '/',
publicWebPath: 'http://localhost:5173',
// 单点登录配置
// 推荐开发环境也通过nginx访问http://localhost
// 备选直接访问各服务端口platformUrl: 'http://localhost:7001'
sso: { sso: {
platformUrl: '/', // 通过nginx访问platform platformUrl: '/',
workcaseUrl: '/workcase', // 通过nginx访问workcase workcaseUrl: '/workcase',
biddingUrl: '/bidding' // 通过nginx访问bidding biddingUrl: '/bidding'
},
aesSecretKey: 'MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=',
jitsi: {
serverUrl: 'https://meet.example.com'
}, },
features: { features: {
enableDebug: true, enableDebug: true,
enableMockData: false enableMockData: false
} }
}; };
// ============================================
// 生产环境默认配置(兜底) // 生产环境默认配置(兜底)
// ============================================
const prodDefaultConfig: AppRuntimeConfig = { const prodDefaultConfig: AppRuntimeConfig = {
env: 'production', env: 'production',
api: { api: {
baseUrl: '/api', baseUrl: '/api',
timeout: 30000 timeout: 30000
}, },
baseUrl: '/', baseUrl: '/',
file: { file: {
downloadUrl: '/api/file/download/', downloadUrl: '/api/urban-lifeline/file/download/',
uploadUrl: '/api/file/upload', uploadUrl: '/api/urban-lifeline/file/upload',
maxSize: { maxSize: { image: 5, video: 100, document: 10 },
image: 5,
video: 100,
document: 10
},
acceptTypes: { acceptTypes: {
image: 'image/*', image: 'image/*',
video: 'video/*', video: 'video/*',
document: '.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx' document: '.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx'
} }
}, },
token: { token: {
key: 'token', key: 'token',
refreshThreshold: 300000 refreshThreshold: 300000
}, },
publicImgPath: '/img', publicImgPath: '/img',
publicWebPath: '/', publicWebPath: '/',
// 单点登录配置生产环境通过nginx代理
sso: { sso: {
platformUrl: '/', platformUrl: '/',
workcaseUrl: '/workcase', workcaseUrl: '/workcase',
biddingUrl: '/bidding' biddingUrl: '/bidding'
}, },
aesSecretKey: 'MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=',
jitsi: {
serverUrl: 'https://meet.example.com'
},
features: { features: {
enableDebug: false, enableDebug: false,
enableMockData: 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 => { const getRuntimeConfig = (): AppRuntimeConfig => {
if (isDev) { if (isDev) {
console.log('[配置] 开发环境,使用内置配置'); console.log('[Config] 开发环境,使用内置配置');
return devConfig; return devConfig;
} }
// 生产环境:尝试读取外部配置
try { try {
const runtimeConfig = (window as any).APP_RUNTIME_CONFIG; const runtimeConfig = (window as any).APP_RUNTIME_CONFIG;
if (runtimeConfig && typeof runtimeConfig === 'object') { if (runtimeConfig && typeof runtimeConfig === 'object') {
console.log('[配置] 加载外部配置 app-config.js'); const merged = mergeConfig(prodDefaultConfig, runtimeConfig);
console.log('[配置] API地址:', runtimeConfig.api?.baseUrl); console.log('[Config] 加载运行时配置', merged);
console.log('[配置] 环境:', runtimeConfig.env || 'production'); return merged;
return runtimeConfig as AppRuntimeConfig;
} }
} catch (e) { } catch (e) {
console.warn('[配置] 无法读取外部配置,使用默认配置', e); console.warn('[Config] 无法读取外部配置', e);
} }
console.log('[配置] 使用默认生产配置'); console.log('[Config] 使用默认生产配置');
return prodDefaultConfig; return prodDefaultConfig;
}; };
// 当前应用配置 // 当前配置
const config = getRuntimeConfig(); 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 API_BASE_URL = config.api.baseUrl;
export const FILE_DOWNLOAD_URL = config.file.downloadUrl; export const FILE_DOWNLOAD_URL = config.file.downloadUrl;
export const FILE_UPLOAD_URL = config.file.uploadUrl; export const FILE_UPLOAD_URL = config.file.uploadUrl;
export const PUBLIC_IMG_PATH = config.publicImgPath; export const PUBLIC_IMG_PATH = config.publicImgPath;
export const PUBLIC_WEB_PATH = config.publicWebPath; export const PUBLIC_WEB_PATH = config.publicWebPath;
// 文件上传大小限制100MB
export const FILE_MAX_SIZE = 100 * 1024 * 1024; 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 = { export const APP_CONFIG = {
// 应用标题 title: '泰豪电源工单系统',
title: '泰豪电源 AI 数智化平台',
// 环境标识
env: config.env || 'production', env: config.env || 'production',
// 应用基础路径
baseUrl: config.baseUrl, baseUrl: config.baseUrl,
api: config.api,
// API 配置 file: config.file,
api: { token: config.token,
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
},
// 公共路径
publicImgPath: config.publicImgPath, publicImgPath: config.publicImgPath,
publicWebPath: config.publicWebPath, publicWebPath: config.publicWebPath,
sso: config.sso || { platformUrl: '/', workcaseUrl: '/workcase', biddingUrl: '/bidding' },
// 单点登录配置 jitsi: config.jitsi || { serverUrl: 'https://meet.example.com' },
sso: config.sso || {
platformUrl: '/',
workcaseUrl: '/workcase',
biddingUrl: '/bidding'
},
// 功能开关
features: config.features || {} features: config.features || {}
}; };
// 默认导出
export default APP_CONFIG; export default APP_CONFIG;

View File

@@ -9,84 +9,97 @@ import fs from 'fs'
const __filename = fileURLToPath(import.meta.url) const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename) const __dirname = dirname(__filename)
export default defineConfig(({ mode }) => ({ // 开发环境 shared 模块地址
// 开发和生产环境都通过nginx代理访问/workcase const DEV_SHARED_URL = 'https://localhost:7000/shared/mf-manifest.json'
base: '/workcase/', // 生产环境使用相对路径,通过 Nginx 代理访问
const PROD_SHARED_URL = '/shared/mf-manifest.json'
plugins: [
vue({ export default defineConfig(({ mode }) => {
script: { const isDev = mode === 'development'
defineModel: true, const sharedEntry = isDev ? DEV_SHARED_URL : PROD_SHARED_URL
propsDestructure: true
} return {
}), base: '/workcase/',
vueJsx(),
federation({ plugins: [
name: 'workcase', vue({
remotes: { script: {
shared: { defineModel: true,
type: 'module', propsDestructure: true
name: 'shared',
entry: 'https://org.xyzh.yslg/shared/remoteEntry.js'
} }
}),
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: { proxy: {
vue: {}, '/api': {
'vue-router': {}, target: 'http://localhost:8180',
'element-plus': {}, changeOrigin: true,
axios: {} 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 连接问题 build: {
path: '/@vite/client', outDir: 'dist',
port: 7003 sourcemap: true,
}, rollupOptions: {
proxy: { output: {
'/api': { manualChunks: {
target: 'http://localhost:8180', 'vue-vendor': ['vue', 'vue-router', 'pinia'],
changeOrigin: true, 'element-plus': ['element-plus']
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']
} }
} }
} }
} }
})) })