Compare commits

...

5 Commits

Author SHA1 Message Date
9cb4844be4 网关问题 2025-12-02 18:46:03 +08:00
90ddcf7af3 类型修正 2025-12-02 16:13:28 +08:00
28787e5b29 前端项目结构 2025-12-02 15:55:30 +08:00
2a9e624ba9 sql更新 2025-12-02 14:59:34 +08:00
94718edd6b 大写 2025-12-02 13:36:09 +08:00
96 changed files with 5370 additions and 3175 deletions

116
Makefile Normal file
View File

@@ -0,0 +1,116 @@
# Urban Lifeline 开发环境快捷命令
.PHONY: help up down logs restart clean build ps
# 默认目标
.DEFAULT_GOAL := help
# 帮助信息
help:
@echo "========================================="
@echo " 泰豪电源 Urban Lifeline 开发环境"
@echo "========================================="
@echo ""
@echo "可用命令:"
@echo " make up - 启动所有服务"
@echo " make down - 停止所有服务"
@echo " make restart - 重启所有服务"
@echo " make logs - 查看所有服务日志"
@echo " make ps - 查看服务状态"
@echo " make build - 重新构建镜像"
@echo " make clean - 清理所有数据(慎用)"
@echo ""
@echo "单独服务操作:"
@echo " make logs-portal - 查看主应用日志"
@echo " make logs-gateway - 查看网关日志"
@echo " make restart-portal - 重启主应用"
@echo " make shell-portal - 进入主应用容器"
@echo ""
# 启动所有服务
up:
@echo "启动开发环境..."
docker-compose -f docker-compose.dev.yml up -d
@echo "服务启动中,请稍候..."
@sleep 5
@echo ""
@echo "访问地址:"
@echo " 主应用: http://localhost"
@echo " 招投标: http://localhost/bidding"
@echo " 智能客服: http://localhost/customer-service"
@echo " Gateway: http://localhost/api"
@echo " Nacos: http://localhost/nacos"
@echo ""
# 停止所有服务
down:
@echo "停止所有服务..."
docker-compose -f docker-compose.dev.yml down
# 查看日志
logs:
docker-compose -f docker-compose.dev.yml logs -f
# 重启所有服务
restart:
@echo "重启所有服务..."
docker-compose -f docker-compose.dev.yml restart
# 查看服务状态
ps:
docker-compose -f docker-compose.dev.yml ps
# 重新构建镜像
build:
@echo "重新构建所有镜像..."
docker-compose -f docker-compose.dev.yml build --no-cache
# 清理所有数据(危险操作)
clean:
@echo "警告:此操作将删除所有容器、数据卷和镜像!"
@read -p "确认继续吗?(y/N): " confirm && [ "$$confirm" = "y" ] || exit 1
docker-compose -f docker-compose.dev.yml down -v
docker system prune -af
# ==================== 单独服务操作 ====================
# Portal 日志
logs-portal:
docker-compose -f docker-compose.dev.yml logs -f portal
# Gateway 日志
logs-gateway:
docker-compose -f docker-compose.dev.yml logs -f gateway
# Nginx 日志
logs-nginx:
docker-compose -f docker-compose.dev.yml logs -f nginx
# 共享包日志
logs-shared:
docker-compose -f docker-compose.dev.yml logs -f shared
# 重启 Portal
restart-portal:
docker-compose -f docker-compose.dev.yml restart portal
# 重启 Gateway
restart-gateway:
docker-compose -f docker-compose.dev.yml restart gateway
# 进入 Portal 容器
shell-portal:
docker-compose -f docker-compose.dev.yml exec portal sh
# 进入 Gateway 容器
shell-gateway:
docker-compose -f docker-compose.dev.yml exec gateway sh
# 进入数据库
db:
docker-compose -f docker-compose.dev.yml exec postgres psql -U postgres -d urban_lifeline
# 初始化数据库
db-init:
@echo "初始化数据库..."
docker-compose -f docker-compose.dev.yml exec postgres psql -U postgres -d urban_lifeline -f /docker-entrypoint-initdb.d/initAll.sql

228
README.md
View File

@@ -1,3 +1,227 @@
# urbanLifeline # 泰豪电源 AI 数智化平台
城市生命线 城市生命线智能管理系统 - Urban Lifeline
## 项目简介
基于 **"一个底座、多种智能体"** 架构的企业级 AI 数智化平台,包含四大核心业务模块:
1. **资料管理智能化** - 智能文档管理与检索
2. **招投标自动化** - 招投标全流程智能化
3. **售后客服智能化** - AI 驱动的智能客服系统
4. **企业内部知识协同** - 知识库管理与协作
## 技术架构
### 后端技术栈
- **Java 21** + **Spring Boot 3.5**
- **Spring Cloud Gateway** - API 网关
- **Nacos** - 服务注册与配置中心
- **PostgreSQL 16** - 主数据库
- **Redis 7** - 缓存
- **Dubbo 3.3** - RPC 框架
### 前端技术栈
- **Vue 3.5** (`<script setup>`)
- **TypeScript 5.7**
- **Vite 6.0** - 构建工具
- **Pinia 2.2** - 状态管理
- **Element Plus 2.9** - UI 组件库
- **pnpm 9.0 + Turborepo 2.0** - Monorepo 管理
### 核心特性:共享组件 HTTP 导入
采用 **Import Maps + ES Module** 方案,实现真正的运行时组件共享:
```vue
<script setup lang="ts">
// 直接从 HTTP URL 导入共享组件
import { UlTable } from '@shared/components' // → http://localhost/shared/components.js
import { http } from '@shared/utils' // → http://localhost/shared/utils.js
import { authApi } from '@shared/api' // → http://localhost/shared/api.js
</script>
```
**工作原理**
1. 共享包构建为 ES Module (`components.js`)
2. 部署到独立 HTTP 服务 (`http://localhost:5000`)
3. 通过 Nginx 统一代理 (`http://localhost/shared/`)
4. 浏览器通过 Import Maps 直接加载
## 快速开始
### 方式一Docker Compose推荐
```bash
# 1. 启动所有服务(自动构建)
make up
# 2. 等待服务启动约1-2分钟
make ps
# 3. 访问应用
open http://localhost
```
### 方式二:本地开发
```bash
# 1. 安装依赖
pnpm install
# 2. 启动后端服务(需要 Java 21
cd urbanLifelineServ
mvn spring-boot:run
# 3. 启动前端服务
cd urbanLifelineWeb
pnpm dev
# 4. 访问
open http://localhost:3000
```
## 项目结构
```
urbanLifeline/
├── urbanLifelineServ/ # 后端服务Spring Boot
│ ├── gateway/ # API 网关
│ ├── auth/ # 认证服务
│ ├── system/ # 系统服务
│ ├── file/ # 文件服务
│ └── common/ # 公共模块
├── urbanLifelineWeb/ # 前端应用Vue 3 Monorepo
│ ├── packages/
│ │ ├── shared/ # 共享组件库
│ │ │ ├── components/ # 公共组件
│ │ │ ├── utils/ # 工具函数
│ │ │ ├── api/ # API 封装
│ │ │ └── composables/ # 组合式函数
│ │ ├── portal/ # 主应用
│ │ ├── app-bidding/ # 招投标应用
│ │ └── app-knowledge/ # 知识协同应用
│ ├── pnpm-workspace.yaml
│ └── turbo.json
├── docker-compose.dev.yml # Docker 开发环境
├── Makefile # 快捷命令
└── docs/ # 文档
├── 前端完整指南.md # ⭐ 前端全流程文档
└── 数据库设计文档.md # ⭐ 数据库全流程文档
```
## 访问地址
| 应用 | 地址 | 说明 |
|------|------|------|
| **主应用** | http://localhost/ | Portal 统一入口 |
| **招投标** | http://localhost/bidding | 招投标智能体 |
| **智能客服** | http://localhost/customer-service | 客服系统 |
| **共享组件** | http://localhost/shared/components.js | ES Module |
| **API 网关** | http://localhost/api | Gateway |
| **Nacos** | http://localhost/nacos | 注册中心 |
## 常用命令
```bash
# Docker 环境
make up # 启动所有服务
make down # 停止所有服务
make logs # 查看日志
make ps # 查看服务状态
make restart # 重启服务
make clean # 清理所有数据
# 单独服务
make logs-portal # 查看主应用日志
make logs-gateway # 查看网关日志
make restart-portal # 重启主应用
make shell-portal # 进入容器
make db # 连接数据库
# 前端开发
pnpm dev # 启动所有应用
pnpm build # 构建所有应用
pnpm --filter portal dev # 启动单个应用
pnpm --filter portal build # 构建单个应用
```
## 文档索引
- **[前端完整指南](./docs/前端完整指南.md)** - ⭐ 架构、开发、部署全流程
- **[数据库完整指南](./docs/数据库完整指南.md)** - ⭐ Schema、表结构、优化方案
## 核心亮点
### 1. 共享组件运行时加载
```html
<!-- index.html -->
<script type="importmap">
{
"imports": {
"@shared/components": "http://localhost/shared/components.js",
"@shared/utils": "http://localhost/shared/utils.js"
}
}
</script>
```
```vue
<script setup lang="ts">
// 浏览器自动从 URL 加载,无需打包!
import { UlTable } from '@shared/components'
</script>
```
### 2. Monorepo + Turborepo
- 统一依赖管理
- 增量构建加速
- 任务并行执行
- 远程缓存
### 3. Docker 一键启动
- 所有服务容器化
- Nginx 统一网关
- 支持热更新HMR
- 数据持久化
### 4. TypeScript 全栈
- 类型安全
- 智能提示
- 重构友好
## 环境要求
### Docker 环境(推荐)
- Docker 20.10+
- Docker Compose 2.0+
### 本地开发
- Node.js 20+
- pnpm 9+
- Java 21+
- Maven 3.9+
- PostgreSQL 16+
## 开发规范
- **前端**ESLint + Prettier + Husky
- **提交规范**Conventional Commits
- **分支策略**Git Flow
- **代码审查**:必须 Code Review
## License
MIT
---
**Built with ❤️ by Taihao Team**

263
docker-compose.dev.yml Normal file
View File

@@ -0,0 +1,263 @@
version: '3.8'
# 开发环境完整部署配置
services:
# ================== 基础服务 ==================
# PostgreSQL 数据库
postgres:
image: postgres:16-alpine
container_name: ul-postgres
environment:
POSTGRES_DB: urban_lifeline
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres123
TZ: Asia/Shanghai
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
- ./urbanLifelineServ/.bin/database/postgres/sql:/docker-entrypoint-initdb.d
networks:
- ul-network
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
# Redis
redis:
image: redis:7-alpine
container_name: ul-redis
ports:
- "6379:6379"
volumes:
- redis_data:/data
networks:
- ul-network
command: redis-server --appendonly yes
# Nacos 注册中心
nacos:
image: nacos/nacos-server:v2.3.0
container_name: ul-nacos
environment:
MODE: standalone
SPRING_DATASOURCE_PLATFORM: mysql
PREFER_HOST_MODE: hostname
JVM_XMS: 512m
JVM_XMX: 512m
JVM_XMN: 256m
ports:
- "8848:8848"
- "9848:9848"
volumes:
- nacos_data:/home/nacos/data
networks:
- ul-network
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8848/nacos"]
interval: 10s
timeout: 5s
retries: 10
# ================== 后端服务 ==================
# Gateway 网关服务
gateway:
build:
context: ./urbanLifelineServ/gateway
dockerfile: Dockerfile.dev
container_name: ul-gateway
environment:
SPRING_PROFILES_ACTIVE: dev
NACOS_SERVER_ADDR: nacos:8848
POSTGRES_HOST: postgres
POSTGRES_PORT: 5432
REDIS_HOST: redis
REDIS_PORT: 6379
ports:
- "8080:8080"
volumes:
- ./urbanLifelineServ/gateway:/app
- maven_cache:/root/.m2
networks:
- ul-network
depends_on:
postgres:
condition: service_healthy
nacos:
condition: service_healthy
command: mvn spring-boot:run
# 认证服务
auth-service:
build:
context: ./urbanLifelineServ/auth
dockerfile: Dockerfile.dev
container_name: ul-auth
environment:
SPRING_PROFILES_ACTIVE: dev
NACOS_SERVER_ADDR: nacos:8848
POSTGRES_HOST: postgres
REDIS_HOST: redis
volumes:
- ./urbanLifelineServ/auth:/app
- maven_cache:/root/.m2
networks:
- ul-network
depends_on:
- postgres
- nacos
# 系统服务
system-service:
build:
context: ./urbanLifelineServ/system
dockerfile: Dockerfile.dev
container_name: ul-system
environment:
SPRING_PROFILES_ACTIVE: dev
NACOS_SERVER_ADDR: nacos:8848
POSTGRES_HOST: postgres
volumes:
- ./urbanLifelineServ/system:/app
- maven_cache:/root/.m2
networks:
- ul-network
depends_on:
- postgres
- nacos
# ================== 前端服务 ==================
# 共享包服务Module Federation Remote
shared:
build:
context: ./urbanLifelineWeb
dockerfile: packages/shared/Dockerfile.dev
container_name: ul-shared
ports:
- "5000:5000"
volumes:
- ./urbanLifelineWeb/packages/shared:/app
- /app/node_modules
- pnpm_store:/root/.local/share/pnpm/store
networks:
- ul-network
environment:
- VITE_PORT=5000
- CHOKIDAR_USEPOLLING=true # 支持 Docker 内文件监听
command: pnpm dev
# 主应用Portal
portal:
build:
context: ./urbanLifelineWeb
dockerfile: packages/portal/Dockerfile.dev
container_name: ul-portal
ports:
- "3000:3000"
volumes:
- ./urbanLifelineWeb/packages/portal:/app
- ./urbanLifelineWeb/packages/shared:/shared
- /app/node_modules
- pnpm_store:/root/.local/share/pnpm/store
networks:
- ul-network
environment:
- VITE_PORT=3000
- VITE_API_BASE_URL=http://nginx/api
- VITE_SHARED_REMOTE=http://nginx/shared
- CHOKIDAR_USEPOLLING=true
depends_on:
- shared
command: pnpm dev
# 招投标应用
app-bidding:
build:
context: ./urbanLifelineWeb
dockerfile: packages/app-bidding/Dockerfile.dev
container_name: ul-app-bidding
ports:
- "3001:3001"
volumes:
- ./urbanLifelineWeb/packages/app-bidding:/app
- ./urbanLifelineWeb/packages/shared:/shared
- /app/node_modules
- pnpm_store:/root/.local/share/pnpm/store
networks:
- ul-network
environment:
- VITE_PORT=3001
- VITE_API_BASE_URL=http://nginx/api
- VITE_SHARED_REMOTE=http://nginx/shared
- CHOKIDAR_USEPOLLING=true
depends_on:
- shared
command: pnpm dev
# 智能客服应用
app-customer-service:
build:
context: ./urbanLifelineWeb
dockerfile: packages/app-customer-service/Dockerfile.dev
container_name: ul-app-cs
ports:
- "3002:3002"
volumes:
- ./urbanLifelineWeb/packages/app-customer-service:/app
- ./urbanLifelineWeb/packages/shared:/shared
- /app/node_modules
- pnpm_store:/root/.local/share/pnpm/store
networks:
- ul-network
environment:
- VITE_PORT=3002
- VITE_API_BASE_URL=http://nginx/api
- VITE_SHARED_REMOTE=http://nginx/shared
- CHOKIDAR_USEPOLLING=true
depends_on:
- shared
command: pnpm dev
# ================== 统一网关 ==================
# Nginx 统一入口
nginx:
image: nginx:alpine
container_name: ul-nginx
ports:
- "80:80"
- "443:443"
volumes:
- ./docker/nginx/nginx.dev.conf:/etc/nginx/nginx.conf:ro
- ./docker/nginx/conf.d:/etc/nginx/conf.d:ro
- ./docker/nginx/ssl:/etc/nginx/ssl:ro
networks:
- ul-network
depends_on:
- gateway
- portal
- app-bidding
- app-customer-service
- shared
healthcheck:
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/health"]
interval: 10s
timeout: 5s
retries: 3
networks:
ul-network:
driver: bridge
volumes:
postgres_data:
redis_data:
nacos_data:
maven_cache:
pnpm_store:

View File

@@ -0,0 +1,196 @@
# 开发环境统一入口配置
# 上游服务定义
upstream gateway {
server gateway:8080;
keepalive 32;
}
upstream portal {
server portal:3000;
keepalive 16;
}
upstream app-bidding {
server app-bidding:3001;
keepalive 16;
}
upstream app-customer-service {
server app-customer-service:3002;
keepalive 16;
}
upstream shared {
server shared:5000;
keepalive 16;
}
# WebSocket 升级配置
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
# 主服务器配置
server {
listen 80;
server_name localhost;
# 安全头
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
# 开发环境允许所有跨域
add_header Access-Control-Allow-Origin "*" always;
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always;
add_header Access-Control-Allow-Headers "DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization" always;
# 健康检查
location /health {
access_log off;
return 200 "healthy\n";
add_header Content-Type text/plain;
}
# ==================== 共享包服务ES Module + Module Federation ====================
location /shared/ {
proxy_pass http://shared:5000/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
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;
# CORSES Module / Module Federation 必需)
add_header Access-Control-Allow-Origin "*" always;
add_header Access-Control-Allow-Methods "GET, OPTIONS" always;
add_header Access-Control-Allow-Headers "Content-Type" always;
# ES Module MIME 类型
if ($request_filename ~* \.(js|mjs)$) {
add_header Content-Type "application/javascript; charset=utf-8";
}
# 开发环境不缓存
add_header Cache-Control "no-cache, no-store, must-revalidate";
# HMR 支持
proxy_read_timeout 86400;
proxy_send_timeout 86400;
}
# ==================== API 网关代理 ====================
location /api/ {
proxy_pass http://gateway/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
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;
# 超时设置
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
# WebSocket 代理
location /ws/ {
proxy_pass http://gateway/ws/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
# WebSocket 超时设置
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}
# ==================== 主应用Portal ====================
location / {
proxy_pass http://portal/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
# Vite HMR 支持
proxy_read_timeout 86400;
}
# ==================== 招投标应用 ====================
location /bidding {
rewrite ^/bidding/(.*)$ /$1 break;
proxy_pass http://app-bidding/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
# Vite HMR 支持
proxy_read_timeout 86400;
}
# 招投标应用静态资源
location /bidding/ {
proxy_pass http://app-bidding/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
proxy_read_timeout 86400;
}
# ==================== 智能客服应用 ====================
location /customer-service {
rewrite ^/customer-service/(.*)$ /$1 break;
proxy_pass http://app-customer-service/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
proxy_read_timeout 86400;
}
location /customer-service/ {
proxy_pass http://app-customer-service/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
proxy_read_timeout 86400;
}
# ==================== Nacos 管理界面(开发用) ====================
location /nacos/ {
proxy_pass http://nacos:8848/nacos/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
# 可选HTTPS 配置(开发环境使用自签名证书)
# server {
# listen 443 ssl http2;
# server_name localhost;
#
# ssl_certificate /etc/nginx/ssl/cert.pem;
# ssl_certificate_key /etc/nginx/ssl/key.pem;
# ssl_protocols TLSv1.2 TLSv1.3;
# ssl_ciphers HIGH:!aNULL:!MD5;
#
# # 其他配置同上...
# }

View File

@@ -0,0 +1,42 @@
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
use epoll;
}
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_min_length 1024;
gzip_comp_level 6;
gzip_types text/plain text/css text/xml text/javascript
application/javascript application/json application/xml+rss;
# 上传大小限制
client_max_body_size 100M;
# 包含具体配置
include /etc/nginx/conf.d/*.conf;
}

563
docs/前端完整指南.md Normal file
View File

@@ -0,0 +1,563 @@
# 泰豪电源 AI 数智化平台 - 前端完整指南
## 📖 目录
1. [技术架构](#1-技术架构)
2. [共享组件方案](#2-共享组件方案)
3. [快速开始](#3-快速开始)
4. [开发指南](#4-开发指南)
5. [构建部署](#5-构建部署)
6. [常见问题](#6-常见问题)
---
## 1. 技术架构
### 1.1 技术栈
**前端框架**
- Vue 3.5+ (`<script setup>`)
- TypeScript 5.7+
- Vite 6.0+
- Pinia 2.2+
- Element Plus 2.9+
**工程化**
- pnpm 9.0+ Monorepo
- Turborepo 2.0+ 构建加速
- unplugin-auto-import 自动导入
- ESLint 9 + Prettier
**通信层**
- ofetch (替代 Axios)
- @vueuse/core
### 1.2 项目结构
```
urbanLifelineWeb/
├── packages/
│ ├── shared/ # 共享包 ⭐核心
│ │ ├── components/ # UI组件
│ │ ├── utils/ # 工具函数
│ │ ├── api/ # API封装
│ │ └── composables/ # 组合函数
│ ├── portal/ # 主应用
│ ├── app-bidding/ # 招投标应用
│ ├── app-customer-service/ # 智能客服应用
│ └── app-knowledge/ # 知识协同应用
├── pnpm-workspace.yaml
└── turbo.json
```
---
## 2. 共享组件方案
### 2.1 核心理念
**业务应用只需关心 `@shared/*` 导入无需关心底层依赖Vue、Element Plus 等)**
```
业务应用: import { UlTable } from '@shared/components'
浏览器: GET /shared/components.js
共享服务: 返回包含 Vue + Element Plus 的 ES Module
✅ 所有应用共用,浏览器缓存
```
### 2.2 配置方式
**业务应用配置(极简)**
```html
<!-- index.html -->
<script type="importmap">
{
"imports": {
"@shared/components": "/shared/components.js",
"@shared/utils": "/shared/utils.js",
"@shared/api": "/shared/api.js"
}
}
</script>
```
**Vue 组件使用**
```vue
<script setup lang="ts">
// ✅ 直接从 HTTP URL 加载
import { UlTable } from '@shared/components'
import { http } from '@shared/utils'
import { authApi } from '@shared/api'
import { useTable } from '@shared/composables'
const { loading, tableData } = useTable({
fetchData: authApi.getUserList
})
</script>
<template>
<UlTable :data="tableData" :loading="loading" />
</template>
```
### 2.3 优势
| 特性 | 传统方式 | Import Maps 方案 |
|------|---------|-----------------|
| 代码共享 | ❌ 每个应用打包一份 | ✅ 所有应用共用一份 |
| 构建体积 | ❌ 重复打包 | ✅ 减少 30-50% |
| 依赖管理 | ❌ 每个应用配置 | ✅ 共享服务统一管理 |
| 版本升级 | ❌ 所有应用改 | ✅ 只改共享服务 |
| 浏览器缓存 | ⚠️ 独立缓存 | ✅ 统一缓存 |
---
## 3. 快速开始
### 3.1 Docker 环境(推荐)
```bash
# 1. 启动所有服务
make up
# 2. 查看状态
make ps
# 3. 访问应用
open http://localhost
```
**访问地址**
- 主应用: http://localhost/
- 招投标: http://localhost/bidding
- 智能客服: http://localhost/customer-service
- 共享组件: http://localhost/shared/components.js
- API网关: http://localhost/api
- Nacos: http://localhost/nacos
### 3.2 本地开发
```bash
# 1. 安装依赖
pnpm install
# 2. 启动所有应用
pnpm dev
# 或启动单个应用
pnpm --filter portal dev
pnpm --filter app-bidding dev
```
---
## 4. 开发指南
### 4.1 创建页面
```vue
<!-- packages/portal/src/views/UserList.vue -->
<script setup lang="ts">
import { UlTable } from '@shared/components'
import { authApi } from '@shared/api'
import { useTable } from '@shared/composables'
interface User {
id: string
username: string
email: string
}
const { loading, tableData, pagination, handlePageChange } = useTable<User>({
fetchData: authApi.getUserList
})
const columns = [
{ prop: 'username', label: '用户名', minWidth: 150 },
{ prop: 'email', label: '邮箱', minWidth: 200 }
]
</script>
<template>
<div class="user-list">
<UlTable
:data="tableData"
:columns="columns"
:loading="loading"
:pagination="pagination"
@page-change="handlePageChange"
/>
</div>
</template>
```
### 4.2 API 调用
```typescript
// 使用封装的 API
import { authApi, systemApi } from '@shared/api'
// GET 请求
const users = await authApi.getUserList({ page: 1, size: 10 })
// POST 请求
await authApi.createUser({ username: 'test', email: 'test@example.com' })
// 直接使用 http
import { http } from '@shared/utils'
const data = await http.get('/custom/endpoint')
```
### 4.3 状态管理
```typescript
// store/user.ts
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import { authApi } from '@shared/api'
export const useUserStore = defineStore('user', () => {
// State
const userInfo = ref(null)
const permissions = ref([])
// Getters
const isLoggedIn = computed(() => !!userInfo.value)
// Actions
const login = async (loginData) => {
const res = await authApi.login(loginData)
userInfo.value = res.userInfo
permissions.value = res.permissions
}
return { userInfo, permissions, isLoggedIn, login }
})
```
### 4.4 路由配置
```typescript
// router/index.ts
import { createRouter, createWebHistory } from 'vue-router'
const routes = [
{
path: '/',
component: () => import('@/layouts/MainLayout.vue'),
children: [
{
path: 'home',
component: () => import('@/views/home/index.vue'),
meta: { title: '首页' }
}
]
}
]
const router = createRouter({
history: createWebHistory(),
routes
})
export default router
```
### 4.5 常用命令
```bash
# 查看日志
make logs-portal # 主应用日志
make logs-gateway # 网关日志
make logs-shared # 共享包日志
# 重启服务
make restart-portal # 重启主应用
make restart # 重启所有服务
# 进入容器
make shell-portal # 进入容器调试
# 数据库
make db # 连接数据库
make db-init # 初始化数据
```
---
## 5. 构建部署
### 5.1 本地构建
```bash
# 构建所有应用
pnpm build
# 构建单个应用
pnpm --filter portal build
pnpm --filter shared build
# 预览
pnpm preview
```
### 5.2 部署到生产
**方式1: Docker 部署**
```bash
# 构建生产镜像
docker build -t urban-lifeline-web .
# 运行
docker run -p 80:80 urban-lifeline-web
```
**方式2: CDN 部署**
```bash
# 部署共享包到 CDN
cd packages/shared
pnpm build
ossutil cp -r dist/esm/ oss://cdn/shared/v1.0.0/
# 部署业务应用
cd packages/portal
pnpm build
ossutil cp -r dist/ oss://cdn/portal/
```
**方式3: Nginx 静态部署**
```nginx
server {
listen 80;
server_name taihao.com;
# 主应用
location / {
root /var/www/portal/dist;
try_files $uri $uri/ /index.html;
}
# 共享包
location /shared/ {
root /var/www/shared/dist;
add_header Access-Control-Allow-Origin "*";
expires 1y;
}
# API 代理
location /api/ {
proxy_pass http://gateway:8080/;
}
}
```
### 5.3 环境变量
```bash
# .env.development
VITE_API_BASE_URL=http://localhost/api
VITE_SHARED_URL=http://localhost/shared
# .env.production
VITE_API_BASE_URL=https://api.taihao.com/api
VITE_SHARED_URL=https://cdn.taihao.com/shared/v1.0.0
```
### 5.4 CI/CD
```yaml
# .github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: 9
- name: Build
run: pnpm install && pnpm build
- name: Deploy
run: ./scripts/deploy-production.sh
```
---
## 6. 常见问题
### 6.1 为什么使用 Import Maps
**优势**
- ✅ 浏览器原生支持,无需额外库
- ✅ 真正的运行时共享
- ✅ 开发体验好(支持 HMR
- ✅ 减小构建体积
**对比 Module Federation**
- Import Maps 更简单,配置少
- Module Federation 功能更强大,但复杂
- 两者可以结合使用
### 6.2 如何处理 TypeScript 类型?
```typescript
// packages/portal/src/types/shared.d.ts
declare module '@shared/components' {
export * from '../../../shared/src/components'
}
declare module '@shared/utils' {
export * from '../../../shared/src/utils'
}
```
### 6.3 如何更新共享组件?
```bash
# 1. 修改共享组件代码
vim packages/shared/src/components/UlTable/index.vue
# 2. 重启共享服务HMR 自动生效)
make restart-shared
# 3. 浏览器刷新,所有应用自动获得最新版本
```
### 6.4 生产环境如何缓存?
```nginx
# Nginx 配置
location /shared/ {
# 基于版本号的长期缓存
expires 1y;
add_header Cache-Control "public, immutable";
}
# HTML 不缓存
location ~* \.html$ {
add_header Cache-Control "no-cache";
}
```
### 6.5 如何调试共享组件?
```bash
# 方式1: 查看网络请求
浏览器 F12 → Network → 筛选 JS → 查看 /shared/components.js
# 方式2: 查看日志
make logs-shared
# 方式3: 进入容器
make shell-shared
```
### 6.6 为什么不用 Webpack
Vite 优势:
- ⚡ 开发启动快(秒级)
- 🔥 HMR 更快
- 📦 生产构建基于 Rollup体积更小
- 🎯 原生 ES Module 支持
### 6.7 如何处理样式?
```vue
<!-- 组件级样式 -->
<style scoped>
.ul-table {
width: 100%;
}
</style>
<!-- 全局样式 -->
<style>
@import '@shared/styles/variables.scss';
</style>
```
共享包会自动处理 CSS 提取和分割。
### 6.8 如何添加新的业务应用?
```bash
# 1. 复制现有应用作为模板
cp -r packages/portal packages/app-newapp
# 2. 修改 package.json
{
"name": "@apps/newapp",
"scripts": {
"dev": "vite --port 3003"
}
}
# 3. 添加到 pnpm-workspace.yaml已自动包含
# 4. 启动
pnpm --filter app-newapp dev
```
---
## 附录
### A. 常用脚本
```bash
# Makefile 命令
make up # 启动
make down # 停止
make logs # 日志
make ps # 状态
make restart # 重启
make clean # 清理
# pnpm 命令
pnpm dev # 开发
pnpm build # 构建
pnpm preview # 预览
pnpm lint # 检查
pnpm format # 格式化
```
### B. 目录说明
```
urbanLifeline/
├── urbanLifelineServ/ # 后端Java Spring Boot
├── urbanLifelineWeb/ # 前端Vue 3 Monorepo
├── docker-compose.dev.yml # Docker 开发环境
├── Makefile # 快捷命令
└── docs/ # 文档
└── 前端完整指南.md # 📖 本文档
```
### C. 技术支持
- 项目文档:`/docs`
- 问题反馈GitHub Issues
- 开发规范ESLint + Prettier
- 提交规范Conventional Commits
---
**Happy Coding! 🚀**
*最后更新2025-12-02*

View File

@@ -0,0 +1,774 @@
# 泰豪电源 AI 数智化平台 - 数据库完整指南
> **更新日期**: 2025-12-02
> **数据库**: PostgreSQL 16
> **总表数**: 50张核心业务表
---
## 📖 目录
1. [系统概述](#1-系统概述)
2. [Schema架构](#2-schema架构)
3. [核心模块设计](#3-核心模块设计)
4. [表结构速查](#4-表结构速查)
5. [优化方案](#5-优化方案)
6. [部署与维护](#6-部署与维护)
7. [常用查询示例](#7-常用查询示例)
---
## 1. 系统概述
### 1.1 设计理念
基于`功能结构.xml`的系统架构,遵循 **"一个底座、多种智能体"** 核心理念,支持四大业务模块:
1. **资料管理智能化**
2. **招投标自动化**
3. **售后客服智能化**
4. **企业内部知识协同**
### 1.2 技术选型
- **数据库**: PostgreSQL 16
- **连接池**: HikariCP
- **ORM**: MyBatis-Plus 3.5
- **迁移工具**: Flyway (可选)
### 1.3 设计原则
-**模块化**: 按业务模块划分 Schema
-**规范化**: 统一命名规范和字段设计
-**可扩展**: 预留扩展字段和软删除
-**高性能**: 合理索引和分区设计
-**安全性**: 行级安全和审计日志
---
## 2. Schema架构
### 2.1 Schema划分
采用多Schema架构按业务模块逻辑隔离
| Schema | 说明 | 核心表数量 | 用途 |
|--------|------|-----------|------|
| `sys` | 系统基础模块 | 11 | 用户、角色、权限、部门 |
| `file` | 文件管理模块 | 1 | 文件存储与管理 |
| `message` | 消息通知模块 | 4 | 站内信、系统通知 |
| `log` | 日志审计模块 | 1 | 操作日志、审计 |
| `config` | 系统配置模块 | 1 | 系统参数配置 |
| `knowledge` | 知识库管理模块 | 4 | 文档、分段、问答对 |
| `bidding` | 招投标智能体 | 8 | 招投标业务流程 |
| `customer_service` | 智能客服系统 | 9 | 客服、工单、对话 |
| `agent` | 智能体管理 | 11 | 智能体、API、监控 |
**总计: 50张核心业务表**
### 2.2 命名规范
```sql
-- Schema 命名: 小写字母
CREATE SCHEMA IF NOT EXISTS sys;
-- 表命名: tb_{schema}_{表名}
CREATE TABLE sys.tb_sys_user (...);
CREATE TABLE knowledge.tb_knowledge_document (...);
-- 字段命名: 小写字母 + 下划线
user_id, create_time, full_name
-- 索引命名: idx_{表名}_{字段名}
CREATE INDEX idx_user_email ON sys.tb_sys_user(email);
-- 外键命名: fk_{表名}_{关联表名}
CONSTRAINT fk_user_dept FOREIGN KEY (dept_id) ...
```
---
## 3. 核心模块设计
### 3.1 系统基础模块 (sys)
#### 权限体系设计
采用 **RBAC (基于角色的访问控制) + ACL (访问控制列表)** 混合模型
**核心表**:
- `tb_sys_user` - 用户表
- `tb_sys_role` - 角色表
- `tb_sys_permission` - 权限表
- `tb_sys_dept` - 部门表
- `tb_sys_acl` - 对象级权限表
- `tb_sys_user_role` - 用户角色关联表
**权限模型**:
```
用户 (User)
├─ 部门 (Dept) - 数据隔离
├─ 角色 (Role)
│ ├─ 权限 (Permission) - 功能权限
│ └─ 视图 (View) - 菜单权限
└─ ACL - 对象级权限(细粒度)
```
**设计亮点**:
1. 多租户支持: `dept_path` 字段实现部门级数据隔离
2. 角色作用域: `scope` 字段区分全局/部门角色
3. 对象权限: ACL表支持任意对象的细粒度权限
#### 核心表结构
```sql
-- 用户表
CREATE TABLE sys.tb_sys_user (
user_id VARCHAR(50) PRIMARY KEY,
username VARCHAR(50) UNIQUE NOT NULL,
password_hash VARCHAR(255) NOT NULL,
email VARCHAR(100) UNIQUE,
phone VARCHAR(20),
wechat_id VARCHAR(100),
status VARCHAR(20) DEFAULT 'active',
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
deleted BOOLEAN DEFAULT false
);
-- 部门表(树形结构)
CREATE TABLE sys.tb_sys_dept (
dept_id VARCHAR(50) PRIMARY KEY,
dept_name VARCHAR(100) NOT NULL,
parent_id VARCHAR(50),
dept_path TEXT, -- 路径:/1/2/3/
sort_order INTEGER DEFAULT 0,
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
deleted BOOLEAN DEFAULT false,
CONSTRAINT fk_dept_parent FOREIGN KEY (parent_id)
REFERENCES sys.tb_sys_dept(dept_id)
);
-- 角色表
CREATE TABLE sys.tb_sys_role (
role_id VARCHAR(50) PRIMARY KEY,
role_name VARCHAR(100) NOT NULL,
role_code VARCHAR(50) UNIQUE NOT NULL,
scope VARCHAR(20) DEFAULT 'global', -- global/dept
owner_dept_id VARCHAR(50),
description TEXT,
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
deleted BOOLEAN DEFAULT false
);
-- ACL权限表
CREATE TABLE sys.tb_sys_acl (
acl_id VARCHAR(50) PRIMARY KEY,
object_type VARCHAR(50) NOT NULL, -- user/dept/document等
object_id VARCHAR(50) NOT NULL,
principal_type VARCHAR(20) NOT NULL, -- user/role/dept
principal_id VARCHAR(50) NOT NULL,
permission VARCHAR(50) NOT NULL, -- view/edit/delete/admin
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 索引
CREATE INDEX idx_user_status ON sys.tb_sys_user(status) WHERE deleted = false;
CREATE INDEX idx_dept_path ON sys.tb_sys_dept USING GIN(dept_path gin_trgm_ops);
CREATE INDEX idx_acl_object ON sys.tb_sys_acl(object_type, object_id);
```
### 3.2 知识库管理模块 (knowledge)
#### 模块概述
支持文档管理、向量检索、RAG问答版本控制。
**核心表**:
- `tb_knowledge_document` - 文档主表
- `tb_knowledge_chunk` - 文档分段表(向量检索)
- `tb_knowledge_qa_pair` - 问答对表
- `tb_knowledge_file_relation` - 文档文件关联表
#### 版本管理设计
采用 **简化版本管理** 方案:
```sql
-- 文档表(新增版本字段)
CREATE TABLE knowledge.tb_knowledge_document (
doc_id VARCHAR(50) PRIMARY KEY,
root_doc_id VARCHAR(50), -- ✨ 根文档ID版本组标识
version INTEGER DEFAULT 1, -- ✨ 版本号
is_current BOOLEAN DEFAULT true, -- ✨ 是否当前版本
title VARCHAR(500) NOT NULL,
content TEXT,
knowledge_base_id VARCHAR(50),
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
deleted BOOLEAN DEFAULT false
);
-- 分段表(向量检索)
CREATE TABLE knowledge.tb_knowledge_chunk (
chunk_id VARCHAR(50) PRIMARY KEY,
doc_id VARCHAR(50) NOT NULL,
chunk_index INTEGER NOT NULL,
content TEXT NOT NULL,
embedding VECTOR(1536), -- ✨ 向量字段pgvector扩展
version INTEGER DEFAULT 1, -- ✨ 乐观锁版本
tokens INTEGER,
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT fk_chunk_doc FOREIGN KEY (doc_id)
REFERENCES knowledge.tb_knowledge_document(doc_id)
);
-- 索引
CREATE INDEX idx_doc_root_current
ON knowledge.tb_knowledge_document(root_doc_id, is_current)
WHERE deleted = false;
CREATE INDEX idx_chunk_embedding
ON knowledge.tb_knowledge_chunk
USING ivfflat (embedding vector_cosine_ops) -- ✨ 向量索引
WITH (lists = 100);
```
**版本管理示例**:
```sql
-- 创建新版本
BEGIN;
-- 1. 标记旧版本为非当前
UPDATE knowledge.tb_knowledge_document
SET is_current = false
WHERE root_doc_id = 'root_xxx';
-- 2. 插入新版本
INSERT INTO knowledge.tb_knowledge_document (
doc_id, root_doc_id, version, is_current, title, content
) VALUES (
'new_doc_id', 'root_xxx', 2, true, '新版本标题', '内容'
);
COMMIT;
-- 查询当前版本
SELECT * FROM knowledge.tb_knowledge_document
WHERE root_doc_id = 'root_xxx' AND is_current = true;
-- 查询所有版本
SELECT version, title, create_time
FROM knowledge.tb_knowledge_document
WHERE root_doc_id = 'root_xxx'
ORDER BY version DESC;
```
### 3.3 招投标智能体模块 (bidding)
#### 核心表
```sql
-- 招标项目表
CREATE TABLE bidding.tb_bidding_project (
project_id VARCHAR(50) PRIMARY KEY,
project_name VARCHAR(500) NOT NULL,
project_code VARCHAR(100),
bidding_org VARCHAR(200), -- 招标单位
project_type VARCHAR(50), -- 项目类型
status VARCHAR(50) DEFAULT 'draft',
budget DECIMAL(18,2),
bid_deadline TIMESTAMP,
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
deleted BOOLEAN DEFAULT false
);
-- 招标文件表
CREATE TABLE bidding.tb_bidding_document (
doc_id VARCHAR(50) PRIMARY KEY,
project_id VARCHAR(50) NOT NULL,
doc_type VARCHAR(50), -- tender/technical/commercial
file_id VARCHAR(50),
parsed_content JSONB, -- ✨ 智能解析结果
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT fk_bdoc_project FOREIGN KEY (project_id)
REFERENCES bidding.tb_bidding_project(project_id)
);
-- 标书模板表
CREATE TABLE bidding.tb_bidding_template (
template_id VARCHAR(50) PRIMARY KEY,
template_name VARCHAR(200) NOT NULL,
template_type VARCHAR(50),
content JSONB, -- 模板结构化内容
variables JSONB, -- 可替换变量
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
```
### 3.4 智能客服系统模块 (customer_service)
#### 核心表
```sql
-- 对话会话表
CREATE TABLE customer_service.tb_cs_conversation (
conv_id VARCHAR(50) PRIMARY KEY,
customer_user_id VARCHAR(50),
customer_name VARCHAR(100),
channel VARCHAR(50), -- wechat/web/phone
source VARCHAR(50), -- miniprogram/h5/app
status VARCHAR(50) DEFAULT 'active',
start_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
end_time TIMESTAMP,
satisfaction_score INTEGER -- 满意度评分
);
-- 对话消息表
CREATE TABLE customer_service.tb_cs_message (
message_id VARCHAR(50) PRIMARY KEY,
conv_id VARCHAR(50) NOT NULL,
sender_type VARCHAR(20), -- customer/agent/bot
sender_id VARCHAR(50),
content TEXT,
message_type VARCHAR(50), -- text/image/file
ai_response JSONB, -- AI响应详情
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT fk_msg_conv FOREIGN KEY (conv_id)
REFERENCES customer_service.tb_cs_conversation(conv_id)
);
-- 工单表
CREATE TABLE customer_service.tb_cs_work_order (
order_id VARCHAR(50) PRIMARY KEY,
order_code VARCHAR(100) UNIQUE NOT NULL,
conv_id VARCHAR(50),
customer_user_id VARCHAR(50),
title VARCHAR(500) NOT NULL,
description TEXT,
category VARCHAR(100),
priority VARCHAR(20) DEFAULT 'medium',
status VARCHAR(50) DEFAULT 'pending',
assigned_to VARCHAR(50),
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
resolve_time TIMESTAMP
);
```
### 3.5 智能体管理模块 (agent)
#### 核心表
```sql
-- 智能体定义表
CREATE TABLE agent.tb_agent_definition (
agent_id VARCHAR(50) PRIMARY KEY,
agent_name VARCHAR(100) NOT NULL,
agent_type VARCHAR(50), -- bidding/cs/knowledge等
description TEXT,
config JSONB, -- 配置(模型、参数等)
status VARCHAR(20) DEFAULT 'active',
version VARCHAR(20),
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- API调用日志表
CREATE TABLE agent.tb_agent_api_log (
log_id VARCHAR(50) PRIMARY KEY,
agent_id VARCHAR(50),
api_endpoint VARCHAR(200),
request_params JSONB,
response_data JSONB,
status_code INTEGER,
duration_ms INTEGER,
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 智能体监控表(按月分区)
CREATE TABLE agent.tb_agent_monitor (
monitor_id VARCHAR(50) NOT NULL,
agent_id VARCHAR(50) NOT NULL,
metric_name VARCHAR(100),
metric_value NUMERIC,
timestamp TIMESTAMP NOT NULL,
PRIMARY KEY (monitor_id, timestamp)
) PARTITION BY RANGE (timestamp);
-- 创建分区
CREATE TABLE agent.tb_agent_monitor_2025_12
PARTITION OF agent.tb_agent_monitor
FOR VALUES FROM ('2025-12-01') TO ('2026-01-01');
```
---
## 4. 表结构速查
### 4.1 系统基础模块 (sys)
| 表名 | 主键 | 核心字段 | 说明 |
|------|------|----------|------|
| `tb_sys_user` | user_id | username, email, phone | 用户表 |
| `tb_sys_user_info` | user_id | avatar, full_name, level | 用户信息表 |
| `tb_sys_dept` | dept_id | name, parent_id, dept_path | 部门表 |
| `tb_sys_role` | role_id | name, scope, owner_dept_id | 角色表 |
| `tb_sys_permission` | permission_id | name, code, module_id | 权限表 |
| `tb_sys_acl` | acl_id | object_type, principal_type | 对象权限表 |
### 4.2 知识库管理 (knowledge)
| 表名 | 主键 | 核心字段 | 说明 |
|------|------|----------|------|
| `tb_knowledge_document` | doc_id | title, root_doc_id, version | 文档表 |
| `tb_knowledge_chunk` | chunk_id | doc_id, content, embedding | 分段表 |
| `tb_knowledge_qa_pair` | qa_pair_id | question, answer | 问答对表 |
### 4.3 招投标 (bidding)
| 表名 | 主键 | 核心字段 | 说明 |
|------|------|----------|------|
| `tb_bidding_project` | project_id | project_name, status | 招标项目表 |
| `tb_bidding_document` | doc_id | project_id, parsed_content | 招标文件表 |
| `tb_bidding_template` | template_id | template_name, content | 标书模板表 |
### 4.4 智能客服 (customer_service)
| 表名 | 主键 | 核心字段 | 说明 |
|------|------|----------|------|
| `tb_cs_conversation` | conv_id | customer_user_id, status | 对话会话表 |
| `tb_cs_message` | message_id | conv_id, content | 对话消息表 |
| `tb_cs_work_order` | order_id | title, status, assigned_to | 工单表 |
---
## 5. 优化方案
### 5.1 索引优化
```sql
-- 1. 复合索引(状态 + 时间查询)
CREATE INDEX idx_project_status_time
ON bidding.tb_bidding_project(status, create_time DESC)
WHERE deleted = false;
-- 2. 部分索引(只索引活跃数据)
CREATE INDEX idx_user_active
ON sys.tb_sys_user(user_id)
WHERE status = 'active' AND deleted = false;
-- 3. GIN索引全文检索
CREATE INDEX idx_doc_content_gin
ON knowledge.tb_knowledge_document
USING GIN(to_tsvector('chinese', title || ' ' || content));
-- 4. BRIN索引时间序列
CREATE INDEX idx_log_time_brin
ON log.tb_operation_log
USING BRIN(create_time);
```
### 5.2 分区策略
```sql
-- 按月分区(监控数据)
CREATE TABLE agent.tb_agent_monitor (
monitor_id VARCHAR(50) NOT NULL,
timestamp TIMESTAMP NOT NULL,
...
PRIMARY KEY (monitor_id, timestamp)
) PARTITION BY RANGE (timestamp);
-- 自动创建分区脚本
CREATE OR REPLACE FUNCTION create_monthly_partition()
RETURNS void AS $$
DECLARE
start_date DATE;
end_date DATE;
partition_name TEXT;
BEGIN
start_date := DATE_TRUNC('month', CURRENT_DATE + INTERVAL '1 month');
end_date := start_date + INTERVAL '1 month';
partition_name := 'tb_agent_monitor_' || TO_CHAR(start_date, 'YYYY_MM');
EXECUTE format(
'CREATE TABLE IF NOT EXISTS agent.%I PARTITION OF agent.tb_agent_monitor
FOR VALUES FROM (%L) TO (%L)',
partition_name, start_date, end_date
);
END;
$$ LANGUAGE plpgsql;
```
### 5.3 查询优化
```sql
-- 使用CTE优化复杂查询
WITH user_roles AS (
SELECT ur.user_id, r.role_id, r.role_code
FROM sys.tb_sys_user_role ur
JOIN sys.tb_sys_role r ON ur.role_id = r.role_id
WHERE r.deleted = false
),
user_permissions AS (
SELECT ur.user_id, p.permission_code
FROM user_roles ur
JOIN sys.tb_sys_role_permission rp ON ur.role_id = rp.role_id
JOIN sys.tb_sys_permission p ON rp.permission_id = p.permission_id
)
SELECT u.username, array_agg(DISTINCT up.permission_code) as permissions
FROM sys.tb_sys_user u
JOIN user_permissions up ON u.user_id = up.user_id
WHERE u.deleted = false
GROUP BY u.user_id, u.username;
```
### 5.4 并发控制
```sql
-- 乐观锁(版本号)
UPDATE knowledge.tb_knowledge_chunk
SET content = '新内容',
version = version + 1
WHERE chunk_id = 'xxx'
AND version = 1; -- 版本匹配才更新
-- 悲观锁
BEGIN;
SELECT * FROM bidding.tb_bidding_project
WHERE project_id = 'xxx'
FOR UPDATE; -- 锁定记录
-- 更新...
COMMIT;
```
---
## 6. 部署与维护
### 6.1 初始化脚本
```bash
# 位置: urbanLifelineServ/.bin/database/postgres/sql/
# 完整初始化
psql -U postgres -d urban_lifeline -f createTableAll.sql
# 分模块初始化
psql -U postgres -d urban_lifeline -f createTablePermission.sql
psql -U postgres -d urban_lifeline -f createTableKnowledge.sql
psql -U postgres -d urban_lifeline -f createTableBidding.sql
# 优化补丁
psql -U postgres -d urban_lifeline -f optimizations.sql
```
### 6.2 备份策略
```bash
# 全量备份
pg_dump -U postgres -d urban_lifeline -F c -f backup_$(date +%Y%m%d).dump
# 仅备份 Schema
pg_dump -U postgres -d urban_lifeline -s -f schema_backup.sql
# 仅备份数据
pg_dump -U postgres -d urban_lifeline -a -f data_backup.sql
# 还原
pg_restore -U postgres -d urban_lifeline backup_20251202.dump
```
### 6.3 监控指标
```sql
-- 表大小
SELECT
schemaname,
tablename,
pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) as size
FROM pg_tables
WHERE schemaname NOT IN ('pg_catalog', 'information_schema')
ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC;
-- 索引使用情况
SELECT
schemaname, tablename, indexname,
idx_scan as index_scans,
idx_tup_read as tuples_read
FROM pg_stat_user_indexes
WHERE idx_scan = 0
ORDER BY schemaname, tablename;
-- 慢查询
SELECT
query,
calls,
total_time,
mean_time,
max_time
FROM pg_stat_statements
WHERE mean_time > 1000 -- 超过1秒
ORDER BY mean_time DESC
LIMIT 10;
```
---
## 7. 常用查询示例
### 7.1 用户权限查询
```sql
-- 查询用户的所有权限
SELECT DISTINCT p.permission_code, p.permission_name
FROM sys.tb_sys_user u
JOIN sys.tb_sys_user_role ur ON u.user_id = ur.user_id
JOIN sys.tb_sys_role_permission rp ON ur.role_id = rp.role_id
JOIN sys.tb_sys_permission p ON rp.permission_id = p.permission_id
WHERE u.username = 'admin'
AND u.deleted = false;
-- 查询部门下所有用户
WITH RECURSIVE dept_tree AS (
SELECT dept_id FROM sys.tb_sys_dept WHERE dept_id = 'target_dept_id'
UNION ALL
SELECT d.dept_id
FROM sys.tb_sys_dept d
JOIN dept_tree dt ON d.parent_id = dt.dept_id
)
SELECT u.*
FROM sys.tb_sys_user u
JOIN sys.tb_sys_user_dept ud ON u.user_id = ud.user_id
WHERE ud.dept_id IN (SELECT dept_id FROM dept_tree);
```
### 7.2 知识库查询
```sql
-- 向量相似度检索Top 5
SELECT
chunk_id,
content,
1 - (embedding <=> '[0.1, 0.2, ...]'::vector) as similarity
FROM knowledge.tb_knowledge_chunk
WHERE deleted = false
ORDER BY embedding <=> '[0.1, 0.2, ...]'::vector
LIMIT 5;
-- 查询文档的所有版本
SELECT
version,
title,
create_time,
is_current
FROM knowledge.tb_knowledge_document
WHERE root_doc_id = 'root_xxx'
ORDER BY version DESC;
```
### 7.3 招投标查询
```sql
-- 统计项目状态分布
SELECT
status,
COUNT(*) as count,
SUM(budget) as total_budget
FROM bidding.tb_bidding_project
WHERE deleted = false
GROUP BY status;
-- 查询即将截止的项目
SELECT
project_name,
bid_deadline,
EXTRACT(DAY FROM (bid_deadline - NOW())) as days_left
FROM bidding.tb_bidding_project
WHERE status = 'active'
AND bid_deadline > NOW()
AND deleted = false
ORDER BY bid_deadline ASC;
```
### 7.4 智能客服查询
```sql
-- 查询工单处理效率
SELECT
assigned_to,
COUNT(*) as total_orders,
AVG(EXTRACT(EPOCH FROM (resolve_time - create_time))/3600) as avg_hours,
COUNT(CASE WHEN status = 'resolved' THEN 1 END) as resolved_count
FROM customer_service.tb_cs_work_order
WHERE create_time > NOW() - INTERVAL '30 days'
GROUP BY assigned_to;
-- 查询满意度统计
SELECT
DATE(start_time) as date,
AVG(satisfaction_score) as avg_satisfaction,
COUNT(*) as total_conversations
FROM customer_service.tb_cs_conversation
WHERE satisfaction_score IS NOT NULL
AND start_time > NOW() - INTERVAL '7 days'
GROUP BY DATE(start_time)
ORDER BY date DESC;
```
---
## 附录
### A. SQL文件清单
```
urbanLifelineServ/.bin/database/postgres/sql/
├── createTableAll.sql # 完整初始化
├── createTablePermission.sql # 权限模块
├── createTableUser.sql # 用户模块
├── createTableFile.sql # 文件模块
├── createTableMessage.sql # 消息模块
├── createTableLog.sql # 日志模块
├── createTableConfig.sql # 配置模块
├── createTableKnowledge.sql # 知识库模块 ✨
├── createTableBidding.sql # 招投标模块 ✨
├── createTableCustomerService.sql # 智能客服模块 ✨
├── createTableAgent.sql # 智能体管理模块 ✨
├── optimizations.sql # 优化补丁 ✨
├── initDataPermission.sql # 权限初始数据
├── initDataUser.sql # 用户初始数据
└── initAll.sql # 完整初始化(表+数据)
```
### B. 扩展插件
```sql
-- 向量检索
CREATE EXTENSION IF NOT EXISTS vector;
-- 全文检索(中文)
CREATE EXTENSION IF NOT EXISTS zhparser;
CREATE TEXT SEARCH CONFIGURATION chinese (PARSER = zhparser);
-- 模糊匹配
CREATE EXTENSION IF NOT EXISTS pg_trgm;
-- UUID生成
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
```
### C. 性能基准
| 指标 | 目标值 | 说明 |
|------|--------|------|
| 单表查询 | < 100ms | 主键/索引查询 |
| 复杂查询 | < 500ms | 多表JOIN |
| 向量检索 | < 200ms | Top-K检索 |
| 写入TPS | > 1000 | 批量插入 |
| 并发连接 | 200+ | HikariCP连接池 |
---
**最后更新**: 2025-12-02
**维护者**: Urban Lifeline Team

View File

@@ -1,599 +0,0 @@
# 数据库表结构速查手册
## 快速导航
- [系统基础模块 (sys)](#系统基础模块-sys)
- [文件管理模块 (file)](#文件管理模块-file)
- [消息通知模块 (message)](#消息通知模块-message)
- [日志模块 (log)](#日志模块-log)
- [配置管理模块 (config)](#配置管理模块-config)
- [知识库管理模块 (knowledge)](#知识库管理模块-knowledge)
- [招投标智能体模块 (bidding)](#招投标智能体模块-bidding)
- [智能客服系统模块 (customer_service)](#智能客服系统模块-customerservice)
- [智能体管理模块 (agent)](#智能体管理模块-agent)
---
## 系统基础模块 (sys)
### 核心表结构
| 表名 | 说明 | 主键 | 核心字段 |
|------|------|------|----------|
| `tb_sys_user` | 用户表 | user_id | email, phone, wechat_id, status |
| `tb_sys_user_info` | 用户信息表 | user_id | avatar, full_name, gender, level |
| `tb_sys_dept` | 部门表 | dept_id | name, parent_id, dept_path |
| `tb_sys_role` | 角色表 | role_id | name, scope, owner_dept_id |
| `tb_sys_permission` | 权限表 | permission_id | name, code, module_id |
| `tb_sys_user_role` | 用户角色关联表 | (user_id, role_id) | - |
| `tb_sys_role_permission` | 角色权限关联表 | (role_id, permission_id) | - |
| `tb_sys_view` | 视图/菜单表 | view_id | name, url, component, type |
| `tb_sys_module` | 模块表 | module_id | name, description |
| `tb_sys_acl` | 对象级权限表 | acl_id | object_type, object_id, principal_type, permission |
| `tb_sys_acl_policy` | ACL策略表 | policy_id | object_type, edit_hierarchy_rule, view_hierarchy_rule |
| `tb_sys_login_log` | 登录日志表 | optsn | user_id, ip_address, login_time, status |
| `tb_sys_user_dept` | 用户部门关联表 | (user_id, dept_id) | is_primary, position |
### 权限模型
```
用户 (tb_sys_user)
├─ 用户角色 (tb_sys_user_role)
│ └─ 角色 (tb_sys_role)
│ ├─ 角色权限 (tb_sys_role_permission)
│ │ └─ 权限 (tb_sys_permission)
│ │ └─ 模块 (tb_sys_module)
│ └─ 视图权限 (tb_sys_view_permission)
│ └─ 视图 (tb_sys_view)
└─ 对象权限 (tb_sys_acl)
└─ ACL策略 (tb_sys_acl_policy)
```
### 常用查询
```sql
-- 查询用户所有权限
SELECT p.code, p.name
FROM sys.tb_sys_user_role ur
JOIN sys.tb_sys_role_permission rp ON ur.role_id = rp.role_id
JOIN sys.tb_sys_permission p ON rp.permission_id = p.permission_id
WHERE ur.user_id = 'USER_ID' AND ur.deleted = false;
-- 查询部门树
WITH RECURSIVE dept_tree AS (
SELECT *, 1 AS level FROM sys.tb_sys_dept WHERE parent_id IS NULL
UNION ALL
SELECT d.*, dt.level + 1
FROM sys.tb_sys_dept d
JOIN dept_tree dt ON d.parent_id = dt.dept_id
)
SELECT * FROM dept_tree ORDER BY level, dept_id;
```
---
## 文件管理模块 (file)
### 核心表结构
| 表名 | 说明 | 主键 | 核心字段 |
|------|------|------|----------|
| `tb_sys_file` | 文件表 | file_id | name, path, size, type, storage_type, url |
| `tb_file_relation` | 文件关联表 | relation_id | file_id, object_type, object_id, relation_type |
### 文件类型
- `storage_type`: local(本地存储) / oss(对象存储) / ftp / sftp
- `relation_type`: attachment(附件) / avatar(头像) / banner(横幅)
### 常用查询
```sql
-- 查询对象的所有附件
SELECT f.*
FROM file.tb_sys_file f
JOIN file.tb_file_relation fr ON f.file_id = fr.file_id
WHERE fr.object_type = 'bidding_project'
AND fr.object_id = 'PROJECT_ID'
AND fr.deleted = false;
```
---
## 消息通知模块 (message)
### 核心表结构
| 表名 | 说明 | 主键 | 核心字段 |
|------|------|------|----------|
| `tb_message` | 消息表 | message_id | title, content, type, status |
| `tb_message_range` | 消息发送范围表 | optsn | message_id, target_type, target_id, channel |
| `tb_message_receiver` | 用户消息接收表 | optsn | message_id, user_id, status, read_time |
| `tb_message_channel` | 消息渠道配置表 | channel_id | channel_code, channel_name, status |
| `tb_message_template` | 消息模板表 | template_id | template_code, title_template, content_template |
### 消息发送流程
```
创建消息 (tb_message)
定义发送范围 (tb_message_range)
├─ target_type: user(指定用户) / dept(部门) / role(角色) / all(全员)
├─ channel: app / sms / email / wechat
生成接收记录 (tb_message_receiver)
└─ status: unread → read → handled / deleted
```
### 常用查询
```sql
-- 查询用户未读消息
SELECT m.*, mr.create_time AS receive_time
FROM message.tb_message m
JOIN message.tb_message_receiver mr ON m.message_id = mr.message_id
WHERE mr.user_id = 'USER_ID'
AND mr.status = 'unread'
AND mr.deleted = false
ORDER BY mr.create_time DESC;
```
---
## 日志模块 (log)
### 核心表结构
| 表名 | 说明 | 主键 | 核心字段 |
|------|------|------|----------|
| `tb_sys_log` | 系统日志表 | log_id | type, level, module, message, data, trace_id |
### 日志级别
- `level`: debug / info / warn / error / fatal
- `type`: system / audit / security / business / api
### 常用查询
```sql
-- 查询错误日志
SELECT * FROM log.tb_sys_log
WHERE level IN ('error', 'fatal')
AND create_time > now() - interval '24 hours'
ORDER BY create_time DESC;
-- 链路追踪
SELECT * FROM log.tb_sys_log
WHERE trace_id = 'TRACE_ID'
ORDER BY create_time;
```
---
## 配置管理模块 (config)
### 核心表结构
| 表名 | 说明 | 主键 | 核心字段 |
|------|------|------|----------|
| `tb_sys_config` | 系统配置表 | config_id | key, name, value, config_type, render_type |
### 配置类型
- `config_type`: String / Integer / Boolean / Float / Double
- `render_type`: select / input / textarea / checkbox / radio / switch
### 常用查询
```sql
-- 按模块查询配置
SELECT * FROM config.tb_sys_config
WHERE module_id = 'MODULE_ID' AND deleted = false
ORDER BY order_num;
```
---
## 知识库管理模块 (knowledge)
### 核心表结构
| 表名 | 说明 | 主键 | 核心字段 |
|------|------|------|----------|
| `tb_knowledge_base` | 知识库表 | kb_id | name, kb_type, access_level, version |
| `tb_knowledge_document` | 知识文档表 | doc_id | kb_id, title, doc_type, category, embedding_status |
| `tb_knowledge_chunk` | 文档片段表 | chunk_id | doc_id, kb_id, content, embedding |
| `tb_knowledge_access_log` | 知识访问日志表 | log_id | kb_id, doc_id, user_id, access_type |
### 知识库类型
- `kb_type`: bidding(招投标) / customer_service(客服) / internal(内部协同)
- `access_level`: public(公开) / private(私有) / internal(内部)
- `embedding_status`: pending(待处理) / processing(处理中) / completed(完成) / failed(失败)
### RAG检索流程
```
用户查询
向量化查询文本
在 tb_knowledge_chunk 中进行向量检索 (embedding)
获取相关文档片段
关联 tb_knowledge_document 获取完整文档信息
返回结果 + 记录访问日志
```
### 常用查询
```sql
-- 查询知识库文档
SELECT d.*, kb.name AS kb_name
FROM knowledge.tb_knowledge_document d
JOIN knowledge.tb_knowledge_base kb ON d.kb_id = kb.kb_id
WHERE d.kb_id = 'KB_ID'
AND d.embedding_status = 'completed'
AND d.deleted = false;
-- 向量检索需要pgvector扩展
-- SELECT chunk_id, content,
-- 1 - (embedding <=> '[查询向量]'::vector) AS similarity
-- FROM knowledge.tb_knowledge_chunk
-- WHERE kb_id = 'KB_ID'
-- ORDER BY embedding <=> '[查询向量]'::vector
-- LIMIT 10;
```
---
## 招投标智能体模块 (bidding)
### 核心表结构
| 表名 | 说明 | 主键 | 核心字段 |
|------|------|------|----------|
| `tb_bidding_project` | 招标项目表 | project_id | project_no, project_name, project_status, deadline |
| `tb_bidding_document` | 招标文件表 | doc_id | project_id, doc_type, parse_status, parse_result |
| `tb_bidding_requirement` | 要素提取表 | req_id | project_id, req_category, is_veto, compliance_status |
| `tb_bid_response` | 投标文件生成表 | response_id | project_id, response_type, generation_status |
| `tb_bidding_scoring_rule` | 评分规则表 | rule_id | project_id, rule_category, max_score, our_score |
| `tb_bidding_process` | 流程节点表 | process_id | project_id, node_type, node_status |
| `tb_bid_template` | 投标模板表 | template_id | template_name, template_type, usage_count |
### 项目生命周期
```
collecting(收集中)
analyzing(分析中) - 智能解读招标文件
preparing(准备投标) - 生成投标文件
submitted(已提交)
opened(已开标)
won(中标) / lost(未中标) / abandoned(放弃)
```
### 要素类别
- `req_category`:
- commercial: 商务要素
- technical: 技术参数
- veto: 否决项 ⚠️
- qualification: 资质要求
- delivery: 交付要求
- payment: 付款条件
- scoring: 评分标准
### 常用查询
```sql
-- 查询项目的所有否决项
SELECT * FROM bidding.tb_bidding_requirement
WHERE project_id = 'PROJECT_ID'
AND is_veto = true
AND deleted = false;
-- 查询项目评分预估
SELECT
rule_category,
SUM(max_score) AS total_max_score,
SUM(our_score) AS total_our_score,
ROUND(SUM(our_score) / SUM(max_score) * 100, 2) AS score_percentage
FROM bidding.tb_bidding_scoring_rule
WHERE project_id = 'PROJECT_ID' AND deleted = false
GROUP BY rule_category;
```
---
## 智能客服系统模块 (customer_service)
### 核心表结构
| 表名 | 说明 | 主键 | 核心字段 |
|------|------|------|----------|
| `tb_customer` | 客户信息表 | customer_id | customer_name, phone, wechat_openid, customer_level |
| `tb_conversation` | 会话表 | conversation_id | customer_id, conversation_type, agent_id, satisfaction_rating |
| `tb_conversation_message` | 会话消息表 | message_id | conversation_id, sender_type, content, is_ai_generated |
| `tb_ticket` | 工单表 | ticket_id | ticket_no, customer_id, ticket_type, ticket_status, sla_deadline |
| `tb_ticket_log` | 工单处理记录表 | log_id | ticket_id, action_type, action_content |
| `tb_faq` | FAQ表 | faq_id | category, question, answer, hit_count |
| `tb_service_evaluation` | 客服评价表 | evaluation_id | customer_id, evaluation_type, rating |
| `tb_crm_config` | CRM集成配置表 | config_id | crm_system, api_endpoint, sync_enabled |
### 会话流程
```
客户发起咨询
创建会话 (tb_conversation)
├─ conversation_type: ai(AI客服) / human(人工) / transfer(转接)
消息交互 (tb_conversation_message)
├─ sender_type: customer / agent / system
├─ is_ai_generated: true/false
├─ kb_references: [知识库文档ID]
智能工单生成 (tb_ticket)
├─ ticket_source: ai / manual / system
满意度评价 (tb_service_evaluation / tb_conversation)
```
### 工单状态
- `ticket_status`:
- pending: 待处理
- processing: 处理中
- resolved: 已解决
- closed: 已关闭
- cancelled: 已取消
### 常用查询
```sql
-- 查询客户的活跃会话
SELECT * FROM customer_service.tb_conversation
WHERE customer_id = 'CUSTOMER_ID'
AND conversation_status = 'active'
AND deleted = false;
-- 查询逾期工单
SELECT * FROM customer_service.tb_ticket
WHERE is_overdue = true
AND ticket_status IN ('pending', 'processing')
AND deleted = false
ORDER BY sla_deadline;
-- 查询AI回答质量
SELECT
DATE(create_time) AS stat_date,
AVG(confidence_score) AS avg_confidence,
COUNT(*) FILTER (WHERE confidence_score > 0.8) AS high_confidence_count,
COUNT(*) AS total_count
FROM customer_service.tb_conversation_message
WHERE is_ai_generated = true
AND deleted = false
GROUP BY DATE(create_time);
```
---
## 智能体管理模块 (agent)
### 核心表结构
| 表名 | 说明 | 主键 | 核心字段 |
|------|------|------|----------|
| `tb_agent` | 智能体定义表 | agent_id | agent_code, agent_name, agent_type, model_config |
| `tb_agent_session` | 智能体会话表 | session_id | agent_id, user_id, session_status, token_usage |
| `tb_agent_message` | 智能体消息表 | message_id | session_id, role, content, function_call |
| `tb_agent_tool` | 智能体工具表 | tool_id | tool_code, tool_type, function_schema, api_endpoint |
| `tb_api_integration` | API集成表 | integration_id | integration_name, base_url, auth_config |
| `tb_api_call_log` | API调用日志表 | log_id | integration_id, endpoint, response_status, duration_ms |
| `tb_agent_metrics` | 智能体监控指标表 | metric_id | agent_id, metric_date, total_sessions, total_tokens |
| `tb_agent_error_log` | 智能体异常日志表 | log_id | agent_id, error_type, error_message, severity |
| `tb_agent_rating` | 智能体评价表 | rating_id | agent_id, rating, feedback |
### 智能体类型
- `agent_type`:
- bidding: 招投标智能体
- customer_service: 客服智能体
- knowledge_assistant: 知识助手
- custom: 自定义智能体
### 工具类型
- `tool_type`:
- api: API调用
- function: 函数
- plugin: 插件
- integration: 集成
### 会话流程
```
用户请求
创建会话 (tb_agent_session)
消息交互 (tb_agent_message)
├─ role: user / assistant / system / function
├─ function_call: 工具调用
工具执行 (tb_agent_tool)
├─ API调用记录 (tb_api_call_log)
记录监控指标 (tb_agent_metrics)
├─ 错误记录 (tb_agent_error_log)
用户评价 (tb_agent_rating)
```
### 常用查询
```sql
-- 查询智能体使用统计
SELECT * FROM agent.v_agent_usage_stats
WHERE agent_id = 'AGENT_ID';
-- 查询API健康状态
SELECT
integration_name,
health_status,
last_health_check,
EXTRACT(EPOCH FROM (now() - last_health_check))/60 AS minutes_since_check
FROM agent.tb_api_integration
WHERE deleted = false
ORDER BY health_status, last_health_check;
-- 查询智能体错误率
SELECT
a.agent_name,
COUNT(*) FILTER (WHERE e.severity = 'critical') AS critical_errors,
COUNT(*) FILTER (WHERE e.severity = 'error') AS errors,
COUNT(*) AS total_errors
FROM agent.tb_agent a
LEFT JOIN agent.tb_agent_error_log e ON a.agent_id = e.agent_id
WHERE e.create_time > now() - interval '24 hours'
GROUP BY a.agent_id, a.agent_name;
```
---
## 数据字典常用字段说明
### 通用字段
所有表都包含以下标准字段:
| 字段 | 类型 | 说明 |
|------|------|------|
| `optsn` | VARCHAR(50) | 流水号(唯一标识) |
| `xxx_id` | VARCHAR(50) | 主键ID |
| `dept_path` | VARCHAR(255) | 部门全路径(多租户隔离) |
| `creator` | VARCHAR(50) | 创建者ID |
| `updater` | VARCHAR(50) | 更新者ID |
| `create_time` | timestamptz | 创建时间(带时区) |
| `update_time` | timestamptz | 更新时间(触发器自动更新) |
| `delete_time` | timestamptz | 删除时间 |
| `deleted` | BOOLEAN | 软删除标记 |
### 时间戳说明
- 使用 `timestamptz` 类型(带时区的时间戳)
- 自动记录创建时间
- 更新时间由触发器自动维护
- 支持软删除(保留 delete_time
### 部门路径dept_path
格式:`/1/2/3/`
- 用于多租户数据隔离
- 支持 LIKE 递归查询
- 示例查询:`WHERE dept_path LIKE '/1/2/%'`
---
## 视图速查
| 视图名 | Schema | 说明 |
|--------|--------|------|
| `v_user_full_info` | sys | 用户完整信息(含用户信息表) |
| `v_user_role_permission` | sys | 用户角色权限(含模块) |
| `v_user_full_permissions` | sys | 用户完整权限含ACL |
| `v_agent_usage_stats` | agent | 智能体使用统计 |
| `v_agent_realtime_status` | agent | 智能体实时状态 |
| `v_ticket_stats` | customer_service | 工单统计 |
| `v_ticket_efficiency` | customer_service | 工单处理效率 |
| `v_project_stats` | bidding | 招投标项目统计 |
---
## 函数速查
| 函数名 | Schema | 说明 |
|--------|--------|------|
| `update_modified_column()` | public | 自动更新update_time触发器函数 |
| `audit_trigger_func()` | public | 审计日志触发器函数 |
| `archive_old_logs()` | public | 归档旧日志数据 |
| `archive_api_logs()` | agent | 归档API调用日志 |
| `check_table_bloat()` | public | 检查表膨胀情况 |
| `create_update_triggers()` | public | 批量创建更新触发器 |
---
## 索引策略
### 主要索引类型
1. **B-Tree索引**(默认):主键、外键、常规查询字段
2. **GIN索引**JSONB字段、数组字段、全文搜索
3. **部分索引**带WHERE条件的索引`WHERE deleted = false`
4. **表达式索引**:函数索引(如 `lower(email)`
### 关键索引示例
```sql
-- 部分索引(减少索引大小)
CREATE INDEX idx_xxx ON table_name(column) WHERE deleted = false;
-- GIN索引数组查询
CREATE INDEX idx_xxx ON table_name USING gin(array_column);
-- 全文搜索索引
CREATE INDEX idx_xxx ON table_name USING gin(text_column gin_trgm_ops);
-- 表达式索引
CREATE INDEX idx_xxx ON table_name(lower(email));
```
---
## 性能优化建议
### 查询优化
1. **使用索引**WHERE、JOIN、ORDER BY字段都要有索引
2. **避免SELECT ***:只查询需要的字段
3. **使用LIMIT**:分页查询限制返回行数
4. **使用EXPLAIN**:分析查询计划
### 批量操作
```sql
-- 批量插入使用COPY或批量INSERT
COPY table_name FROM '/path/to/file.csv' WITH CSV;
-- 批量更新使用UPDATE...FROM
UPDATE table_name t
SET column = data.value
FROM (VALUES (1, 'a'), (2, 'b')) AS data(id, value)
WHERE t.id = data.id;
```
### 定期维护
```sql
-- 分析表(更新统计信息)
ANALYZE table_name;
-- 清理和分析
VACUUM ANALYZE table_name;
-- 重建索引
REINDEX TABLE table_name;
```
---
**版本**: 1.0
**更新时间**: 2024-12-02

View File

@@ -1,511 +0,0 @@
# 泰豪电源AI数智化平台 - 数据库设计交付总结
## 📋 交付概览
基于`功能结构.xml`中定义的系统架构完成了泰豪电源AI数智化平台的完整数据库设计覆盖"一个底座、多种智能体"的核心理念和四大业务模块。
---
## 📦 交付内容
### 1. SQL脚本文件
所有文件位于:`urbanLifelineServ\.bin\database\postgres\sql\`
| 文件名 | 说明 | 表数量 |
|--------|------|--------|
| `createTablePermission.sql` | 系统权限模块(现有优化) | 11张 |
| `createTableUser.sql` | 用户管理模块(现有优化) | 3张 |
| `createTableFile.sql` | 文件管理模块(现有) | 1张 |
| `createTableMessage.sql` | 消息通知模块(现有) | 4张 |
| `createTableLog.sql` | 日志模块(现有) | 1张 |
| `createTableConfig.sql` | 配置管理模块(现有) | 1张 |
| **`createTableKnowledge.sql`** | ✨ **知识库管理模块(新建)** | **4张** |
| **`createTableBidding.sql`** | ✨ **招投标智能体业务模块(新建)** | **8张** |
| **`createTableCustomerService.sql`** | ✨ **智能客服系统业务模块(新建)** | **9张** |
| **`createTableAgent.sql`** | ✨ **智能体管理模块(新建)** | **11张** |
| **`createTableAll.sql`** | ✨ **完整初始化脚本(新建)** | - |
| **`optimizations.sql`** | ✨ **数据库优化补丁(新建)** | - |
**总计53张核心业务表**
### 2. 文档资料
所有文档位于:`docs\`
| 文件名 | 说明 |
|--------|------|
| **`数据库设计文档.md`** | 完整的数据库设计说明文档23000+字) |
| **`数据库表结构速查.md`** | 表结构快速参考手册(含常用查询示例) |
| **`数据库设计交付总结.md`** | 本文档 |
---
## 🎯 核心设计亮点
### 1. 模块化架构设计
采用9个Schema实现业务逻辑隔离
```
sys ← 系统基础(用户、权限、部门)
file ← 文件管理
message ← 消息通知
log ← 日志审计
config ← 系统配置
knowledge ← 知识库管理支持RAG检索
bidding ← 招投标智能体业务
customer_service ← 智能客服系统业务
agent ← 智能体管理和平台基础设施
```
### 2. 权限体系设计
**RBAC + ACL混合模型**
- **RBAC基于角色的访问控制**
- 支持全局角色和部门私有角色
- 角色-权限-模块三层结构
- 视图/菜单权限独立管理
- **ACL访问控制列表**
- 对象级细粒度权限控制
- 支持任意对象类型(文章、文件、课程等)
- 权限位设计(读/写/执行)
- 支持显式拒绝和权限继承
### 3. 多租户支持
- **部门路径隔离**`dept_path`字段):格式 `/1/2/3/`
- **行级安全策略**RLS基于用户部门自动过滤数据
- **作用域控制**:角色和权限的作用域管理
### 4. 知识库智能管理
**支持RAG检索增强生成流程**
```
文档上传 → 智能分类 → AI摘要 → 关键词提取 → 文档切片 → 向量化 → 存储
↓ ↓
tb_knowledge_document tb_knowledge_chunk
(支持向量检索)
```
**特性:**
- 多类型知识库(招投标/客服/内部协同)
- 版本管理和历史追溯
- 向量化状态跟踪
- 访问日志记录
### 5. 招投标全流程管理
**完整业务流程覆盖:**
```
项目创建 → 文件采集 → 智能解读 → 要素提取 → 评分分析
↓ ↓
流程跟踪 ← 投标提交 ← 文件审核 ← 投标文件生成 ← 模板管理
```
**核心功能:**
- **智能要素提取**7大类要素商务/技术/否决项/资质/交付/付款/评分)
- **AI文件生成**:支持技术标/商务标/综合标
- **评分规则分析**:自动解析评分标准,预估得分
- **流程节点管理**:完整的项目生命周期跟踪
### 6. 智能客服系统
**全渠道客服支持:**
```
微信小程序 → AI问答 → 知识库检索 → 工单生成 → CRM同步
↓ ↓ ↓ ↓
客户管理 会话记录 工单处理 满意度评价
```
**特性:**
- **双终端知识库**:客户咨询知识库(外部)+ 内部资料知识库(内部)
- **智能会话管理**AI/人工/转接会话类型,情感分析,意图识别
- **工单智能处理**AI自动生成工单SLA超时预警CRM双向同步
- **评价体系**:多维度评价(准确性、速度、友好度等)
### 7. 智能体管理平台
**智能体广场 + 工具集成 + 运维监控:**
```
智能体定义 → 工具配置 → API集成 → 会话管理 → 监控运维 → 评价反馈
↓ ↓ ↓ ↓ ↓
多模型支持 Function 健康检查 Token统计 错误追踪
```
**特性:**
- **智能体广场**:发布/评分/分类/标签管理
- **工具生态**支持API调用、函数、插件、集成
- **监控体系**:实时指标、错误日志、性能分析
- **成本跟踪**Token使用量、API调用费用统计
---
## 📊 数据库架构统计
### 表结构统计
| Schema | 表数量 | 主要功能 |
|--------|--------|----------|
| sys | 13 | 用户、角色、权限、部门、ACL |
| file | 2 | 文件管理、文件关联 |
| message | 5 | 消息、发送范围、接收记录、渠道、模板 |
| log | 1 | 系统日志 |
| config | 1 | 系统配置 |
| knowledge | 4 | 知识库、文档、片段、访问日志 |
| bidding | 8 | 项目、文件、要素、投标、评分、流程、模板 |
| customer_service | 9 | 客户、会话、消息、工单、FAQ、评价、CRM |
| agent | 11 | 智能体、会话、消息、工具、API、监控、错误 |
| **总计** | **54** | - |
### 索引策略
- **B-Tree索引**100+ 个(主键、外键、常规查询)
- **GIN索引**20+ 个JSONB、数组、全文搜索
- **部分索引**50+ 个WHERE deleted = false
- **表达式索引**5+ 个(函数索引)
### 视图和函数
- **业务视图**8个权限视图、统计视图
- **触发器函数**2个自动更新时间、审计日志
- **业务函数**4个数据归档、性能监控
---
## 🔧 技术特性
### 1. 数据类型使用
- **timestamptz**:全部时间字段(带时区支持)
- **JSONB**:配置、元数据、扩展字段
- **数组TEXT[]**标签、关键词、ID列表
- **DECIMAL**:金额、评分(精确计算)
- **vector**向量嵌入需pgvector扩展可选
### 2. 软删除机制
所有业务表支持软删除:
- `deleted` 字段BOOLEAN类型默认false
- `delete_time` 字段:删除时间戳
- 索引优化:`WHERE deleted = false`
### 3. 审计追踪
- **通用字段**creator、updater、create_time、update_time
- **自动触发器**update_time自动更新
- **审计日志**:可选的审计触发器(记录所有变更)
- **链路追踪**trace_id、span_id支持分布式追踪
### 4. 性能优化
- **分区表设计**:日志表、指标表建议按时间分区
- **物化视图**:权限视图可物化提升性能
- **数据归档**:提供自动归档函数
- **表膨胀监控**check_table_bloat()函数
---
## 📈 业务价值
### 1. 支撑功能结构.xml定义的四大业务模块
**资料管理智能化** → knowledge模块
**招投标自动化** → bidding模块
**售后客服智能化** → customer_service模块
**企业内部知识协同** → knowledge + agent模块
### 2. 实现"一个底座、多种智能体"
- **统一的智能体管理平台**agent模块
- **共享的知识库体系**knowledge模块
- **统一的权限和用户管理**sys模块
- **统一的文件和消息服务**file、message模块
### 3. 可扩展性设计
- **新增智能体类型**在agent.tb_agent中添加新的agent_type
- **新增业务模块**创建新的Schema和表
- **集成外部系统**通过API集成表和CRM配置表
- **自定义权限**通过ACL表实现任意对象的权限控制
---
## 🚀 部署指南
### 快速部署
```bash
# 进入SQL脚本目录
cd urbanLifelineServ\.bin\database\postgres\sql
# 1. 创建数据库
createdb urbanlifeline
# 2. 执行完整初始化脚本(推荐)
psql -d urbanlifeline -f createTableAll.sql
# 或者手动按顺序执行
psql -d urbanlifeline -f createTablePermission.sql
psql -d urbanlifeline -f createTableUser.sql
psql -d urbanlifeline -f createTableFile.sql
psql -d urbanlifeline -f createTableMessage.sql
psql -d urbanlifeline -f createTableLog.sql
psql -d urbanlifeline -f createTableConfig.sql
psql -d urbanlifeline -f createTableKnowledge.sql
psql -d urbanlifeline -f createTableBidding.sql
psql -d urbanlifeline -f createTableCustomerService.sql
psql -d urbanlifeline -f createTableAgent.sql
# 3. 执行优化补丁(可选)
psql -d urbanlifeline -f optimizations.sql
# 4. 初始化基础数据
psql -d urbanlifeline -f initDataConfig.sql
```
### 依赖扩展
```sql
-- 必需扩展
CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; -- UUID生成
CREATE EXTENSION IF NOT EXISTS "pg_trgm"; -- 全文搜索
CREATE EXTENSION IF NOT EXISTS "btree_gin"; -- GIN索引支持
-- 可选扩展
CREATE EXTENSION IF NOT EXISTS "pgcrypto"; -- 数据加密
CREATE EXTENSION IF NOT EXISTS "vector"; -- 向量检索(需单独安装)
```
---
## 🔍 现有SQL文件的优化建议
### 已实施的优化
**createTableUser.sql**
- 移除登录日志表的password字段安全性
- 添加用户部门关联表(多对多关系)
- 添加主部门字段
**createTablePermission.sql**
- 添加角色排序字段
- 添加权限类型字段(操作/数据/菜单权限)
**createTableFile.sql**
- 添加版本管理字段
- 添加分类和标签
- 创建文件关联表(统一文件关联机制)
**createTableMessage.sql**
- 创建消息模板表(支持模板化发送)
**createTableLog.sql**
- 添加链路追踪字段trace_id、span_id
### 建议的补充优化
详见:`optimizations.sql`文件
---
## 📚 参考文档
### 1. 数据库设计文档.md
**内容包含:**
- 系统概述和架构设计
- 核心模块详细设计
- 数据库优化建议
- 现有SQL文件修改建议
- 数据安全建议
- 部署和维护建议
**适用场景:**
- 系统架构师了解整体设计
- 开发人员理解业务逻辑
- DBA进行数据库优化
### 2. 数据库表结构速查.md
**内容包含:**
- 所有模块的表结构快速参考
- 常用查询示例
- 视图和函数速查
- 索引策略说明
- 性能优化建议
**适用场景:**
- 日常开发查询表结构
- 编写业务SQL
- 快速定位相关表
### 3. 功能结构.xml
**原始需求文档:**
- 系统目标定义
- 四大业务模块功能划分
- 平台基础设施模块
- Draw.io格式的架构图
---
## ✅ 验证检查清单
### 数据完整性
- [x] 所有表都有主键
- [x] 外键关系正确定义
- [x] 唯一约束合理设置
- [x] 非空约束符合业务逻辑
### 性能优化
- [x] 高频查询字段都有索引
- [x] 外键字段都有索引
- [x] 使用部分索引减少索引大小
- [x] JSONB和数组字段使用GIN索引
### 安全性
- [x] 敏感字段移除或加密
- [x] 软删除机制
- [x] 审计日志支持
- [x] 行级安全策略(可选启用)
### 可维护性
- [x] 表名和字段名规范统一
- [x] 所有表和字段都有注释
- [x] 触发器自动维护update_time
- [x] 提供数据归档函数
### 可扩展性
- [x] Schema分离便于模块扩展
- [x] JSONB字段支持灵活扩展
- [x] 标签和分类字段
- [x] 元数据字段预留
---
## 📊 后续优化方向
### 短期1-3个月
1. **向量检索优化**
- 安装pgvector扩展
- 优化embedding索引参数
- 实现混合检索(向量+全文)
2. **监控告警**
- 集成pgBadger日志分析
- 配置慢查询告警
- 设置表膨胀告警
3. **数据初始化**
- 编写初始化数据脚本
- 创建测试数据集
- 准备演示数据
### 中期3-6个月
1. **性能优化**
- 分析慢查询并优化
- 实施分区表策略
- 创建物化视图
2. **读写分离**
- 配置主从复制
- 实现读写分离中间件
- 优化读库查询
3. **备份策略**
- 实施全量+增量备份
- 配置WAL归档
- 演练灾难恢复
### 长期6-12个月
1. **分布式部署**
- 评估Citus扩展
- 设计分片策略
- 实现水平扩展
2. **多数据中心**
- 配置异地备份
- 实现数据同步
- 制定灾备方案
3. **智能优化**
- 自动索引推荐
- 查询性能自动优化
- 存储自动扩容
---
## 🎓 技术栈总结
### 数据库技术
- **PostgreSQL 14+**:主数据库
- **pgvector**:向量检索(可选)
- **pg_trgm**:全文搜索
- **JSONB**:半结构化数据存储
- **Row Level Security**:行级安全策略
### 设计模式
- **RBAC + ACL**:混合权限模型
- **软删除**:数据安全保护
- **多租户**:部门路径隔离
- **审计追踪**:完整的变更记录
- **事件驱动**:触发器自动化
### 性能优化
- **索引策略**B-Tree + GIN + 部分索引
- **查询优化**视图、CTE、窗口函数
- **数据归档**:自动归档函数
- **分区表**:时间序列数据分区
---
## 📞 技术支持
### 问题反馈
如在使用过程中遇到问题,请提供:
1. 问题描述
2. 错误信息
3. 执行的SQL语句
4. PostgreSQL版本
### 文档维护
本文档随数据库设计持续更新:
- **当前版本**v1.0
- **更新日期**2024-12-02
- **维护团队**泰豪电源AI数智化平台开发组
---
## 🎉 交付完成
**50+张核心业务表设计完成**
**12个SQL脚本文件交付**
**3份完整技术文档**
**覆盖4大业务模块**
**支持"一个底座、多种智能体"架构**
**数据库设计已完全就绪,可直接用于项目开发!** 🚀
---
**感谢使用!**

View File

@@ -1,698 +0,0 @@
# 泰豪电源AI数智化平台 - 数据库设计文档
## 一、系统概述
基于功能结构.xml的系统架构设计本数据库设计遵循"一个底座、多种智能体"的核心理念,支持四大业务模块:
1. **资料管理智能化**
2. **招投标自动化**
3. **售后客服智能化**
4. **企业内部知识协同**
## 二、数据库架构设计
### 2.1 Schema划分
采用多Schema架构按业务模块逻辑隔离
| Schema | 说明 | 核心表数量 |
|--------|------|-----------|
| `sys` | 系统基础模块(用户、角色、权限、部门) | 11 |
| `file` | 文件管理模块 | 1 |
| `message` | 消息通知模块 | 4 |
| `log` | 日志审计模块 | 1 |
| `config` | 系统配置模块 | 1 |
| `knowledge` | 知识库管理模块 | 4 |
| `bidding` | 招投标智能体业务模块 | 8 |
| `customer_service` | 智能客服系统业务模块 | 9 |
| `agent` | 智能体管理和平台基础设施模块 | 11 |
**总计50张核心业务表**
## 三、核心模块详细设计
### 3.1 系统基础模块 (sys)
#### 3.1.1 权限体系设计
采用**RBAC基于角色的访问控制+ ACL访问控制列表**混合模型:
**核心表:**
- `tb_sys_user` - 用户表
- `tb_sys_role` - 角色表(支持全局角色和部门私有角色)
- `tb_sys_permission` - 权限表
- `tb_sys_dept` - 部门表(树形结构)
- `tb_sys_acl` - 通用对象级权限表(支持细粒度权限控制)
- `tb_sys_acl_policy` - ACL策略表层级可见/可编辑规则)
**设计亮点:**
1. **多租户支持**:通过`dept_path`字段实现数据隔离,支持部门级多租户
2. **角色作用域**`scope`字段区分全局角色和部门私有角色
3. **细粒度权限**ACL表支持对任意对象类型文章、文件、课程等的权限控制
4. **层级权限继承**`include_descendants`支持子部门/子角色权限继承
#### 3.1.2 用户信息管理
```sql
-- 用户表结构优势
tb_sys_user:
- 支持多种登录方式(emailphonewechat_id
- 密码加密存储(建议bcrypt/argon2
- 软删除机制(deleted + delete_time
- 时区感知时间戳(timestamptz
```
### 3.2 知识库管理模块 (knowledge)
#### 3.2.1 表结构设计
**核心表:**
- `tb_knowledge_base` - 知识库定义表
- `tb_knowledge_document` - 知识文档表
- `tb_knowledge_chunk` - 文档片段表RAG检索
- `tb_knowledge_access_log` - 访问日志表
**设计亮点:**
1. **多类型知识库支持**
```sql
kb_type:
- bidding -- 招投标知识库
- customer_service -- 客服知识库
- internal -- 内部协同知识库
```
2. **文档智能处理**
- 自动分类(`category`字段)
- AI摘要生成`content_summary`
- 关键词提取(`keywords`数组)
- 向量化状态跟踪(`embedding_status`
3. **RAG检索支持**
- `tb_knowledge_chunk`表存储文档切片
- `embedding`字段存储向量需pgvector扩展
- 支持语义检索和混合检索
4. **版本管理**
- `version`字段跟踪文档版本
- `parent_doc_id`构建版本树
### 3.3 招投标智能体业务模块 (bidding)
#### 3.3.1 核心业务流程
```
项目创建 → 文件采集 → 智能解读 → 要素提取 → 投标文件生成 → 审核提交 → 流程跟踪
```
#### 3.3.2 表结构设计
**核心表:**
1. `tb_bidding_project` - 招标项目表(主表)
2. `tb_bidding_document` - 招标文件表
3. `tb_bidding_requirement` - 要素提取表
4. `tb_bid_response` - 投标文件生成表
5. `tb_bidding_scoring_rule` - 评分规则表
6. `tb_bidding_process` - 流程节点表
7. `tb_bid_template` - 投标模板表
**设计亮点:**
1. **完整项目生命周期管理**
```sql
project_status:
- collecting -- 收集中
- analyzing -- 分析中
- preparing -- 准备投标
- submitted -- 已提交
- opened -- 已开标
- won/lost -- 中标/未中标
- abandoned -- 放弃
```
2. **智能要素提取**
- 支持7大类要素商务、技术、否决项、资质、交付、付款、评分
- AI提取置信度跟踪
- 来源位置记录(页码、段落)
- 合规状态跟踪
3. **投标文件生成**
- 支持多种生成方式AI生成、模板生成、人工编写
- 版本管理(`parent_version_id`
- 审批流程状态跟踪
4. **评分规则智能分析**
- 多维度评分(技术分、商务分、价格分、信誉分)
- 公式计算支持
- 得分预估和优化建议
### 3.4 智能客服系统业务模块 (customer_service)
#### 3.4.1 核心业务流程
```
客户咨询 → AI问答 → 人工转接 → 工单生成 → 工单处理 → CRM同步 → 满意度评价
```
#### 3.4.2 表结构设计
**核心表:**
1. `tb_customer` - 客户信息表
2. `tb_conversation` - 会话表
3. `tb_conversation_message` - 会话消息表
4. `tb_ticket` - 工单表
5. `tb_ticket_log` - 工单处理记录表
6. `tb_faq` - FAQ表
7. `tb_service_evaluation` - 客服评价表
8. `tb_crm_config` - CRM集成配置表
**设计亮点:**
1. **全渠道客户管理**
- 支持多渠道微信、网页、APP、电话
- 客户等级分层VIP、重要、普通、潜在
- 标签化管理
- CRM系统ID映射
2. **智能会话管理**
- AI/人工/转接会话类型
- 会话摘要自动生成
- 满意度评价跟踪
- 消息情感分析和意图识别
3. **工单智能处理**
- AI自动生成工单
- SLA超时预警
- 优先级智能判定
- CRM双向同步
4. **知识库引用跟踪**
- `kb_references`字段记录引用的知识库文档
- 支持回答溯源
- 知识库质量评估数据
### 3.5 智能体管理模块 (agent)
#### 3.5.1 核心功能
```
智能体定义 → 工具集成 → API管理 → 会话管理 → 监控运维 → 评价反馈
```
#### 3.5.2 表结构设计
**核心表:**
1. `tb_agent` - 智能体定义表
2. `tb_agent_session` - 智能体会话表
3. `tb_agent_message` - 智能体消息表
4. `tb_agent_tool` - 智能体工具表
5. `tb_api_integration` - API集成注册表
6. `tb_api_call_log` - API调用日志表
7. `tb_agent_metrics` - 智能体监控指标表
8. `tb_agent_error_log` - 智能体异常日志表
9. `tb_agent_rating` - 智能体评价表
**设计亮点:**
1. **智能体广场**
- `is_published`字段支持发布到广场
- 评分和使用次数统计
- 分类和标签管理
- 访问级别控制
2. **工具和API集成**
- 标准化工具定义OpenAI Function Calling格式
- 多种认证方式支持
- 健康检查机制
- 调用日志完整记录
3. **监控和运维**
- 按日期/小时的指标聚合
- 成本跟踪Token使用量、API调用费用
- 错误日志和堆栈跟踪
- 成功率和响应时间监控
4. **会话管理**
- 支持对话、任务、工作流多种类型
- 上下文管理JSONB格式
- Token使用量统计
- 知识库引用跟踪
## 四、数据库优化建议
### 4.1 索引优化
#### 4.1.1 已实现的索引策略
1. **主键索引**所有表都有主键通常为业务ID
2. **唯一索引**关键业务字段email、phone、编号等
3. **外键索引**:关联查询字段
4. **复合索引**:高频查询组合字段
5. **部分索引**带WHERE条件的索引减少索引大小
6. **GIN索引**数组字段tags、keywords等
#### 4.1.2 建议增加的索引
```sql
-- 知识库文档全文搜索索引需要pg_trgm扩展
CREATE INDEX idx_knowledge_doc_title_trgm
ON knowledge.tb_knowledge_document USING gin(title gin_trgm_ops);
-- 会话消息内容全文搜索
CREATE INDEX idx_message_content_trgm
ON customer_service.tb_conversation_message USING gin(content gin_trgm_ops);
-- 招标项目名称搜索
CREATE INDEX idx_project_name_trgm
ON bidding.tb_bidding_project USING gin(project_name gin_trgm_ops);
```
### 4.2 性能优化建议
#### 4.2.1 分区表设计
对于数据量大、查询集中在时间范围的表,建议使用分区:
```sql
-- 示例API调用日志按月分区
CREATE TABLE agent.tb_api_call_log (
-- ... 字段定义
) PARTITION BY RANGE (create_time);
CREATE TABLE agent.tb_api_call_log_2024_01
PARTITION OF agent.tb_api_call_log
FOR VALUES FROM ('2024-01-01') TO ('2024-02-01');
```
**建议分区的表:**
- `agent.tb_api_call_log` - API调用日志
- `log.tb_sys_log` - 系统日志
- `knowledge.tb_knowledge_access_log` - 知识访问日志
- `agent.tb_agent_metrics` - 智能体监控指标(已按日期设计)
#### 4.2.2 JSONB字段优化
```sql
-- 为JSONB字段创建GIN索引
CREATE INDEX idx_agent_model_config_gin
ON agent.tb_agent(model_config) USING gin;
-- 为JSONB内特定键创建表达式索引
CREATE INDEX idx_agent_config_model_name
ON agent.tb_agent((model_config->>'model_name'));
```
#### 4.2.3 数组字段优化
```sql
-- 使用GIN索引支持数组查询
CREATE INDEX idx_kb_doc_tags_gin
ON knowledge.tb_knowledge_document USING gin(tags);
-- 查询示例
SELECT * FROM knowledge.tb_knowledge_document
WHERE tags @> ARRAY['技术文档'];
```
### 4.3 数据归档策略
#### 4.3.1 软删除设计
当前设计使用软删除(`deleted`字段),建议:
1. **定期归档**将deleted=true且超过6个月的数据移至归档表
2. **归档表命名**`tb_xxx_archived`
3. **保留必要索引**:归档表仅保留时间范围查询索引
#### 4.3.2 日志数据归档
```sql
-- 日志归档策略
-- 1. API调用日志保留近3个月其余归档
-- 2. 系统日志保留近6个月其余归档
-- 3. 访问日志保留近1个月其余归档
-- 归档函数示例
CREATE OR REPLACE FUNCTION archive_old_logs()
RETURNS void AS $$
BEGIN
-- 归档API调用日志
INSERT INTO agent.tb_api_call_log_archived
SELECT * FROM agent.tb_api_call_log
WHERE create_time < now() - interval '3 months';
DELETE FROM agent.tb_api_call_log
WHERE create_time < now() - interval '3 months';
END;
$$ LANGUAGE plpgsql;
```
### 4.4 查询优化建议
#### 4.4.1 常用查询模式
```sql
-- 1. 用户权限检查(高频查询)
-- 建议:创建物化视图
CREATE MATERIALIZED VIEW sys.mv_user_permissions AS
SELECT
ur.user_id,
array_agg(DISTINCT p.code) AS permissions
FROM sys.tb_sys_user_role ur
JOIN sys.tb_sys_role_permission rp ON ur.role_id = rp.role_id
JOIN sys.tb_sys_permission p ON rp.permission_id = p.permission_id
WHERE ur.deleted = false AND rp.deleted = false
GROUP BY ur.user_id;
CREATE UNIQUE INDEX ON sys.mv_user_permissions(user_id);
-- 定期刷新
REFRESH MATERIALIZED VIEW CONCURRENTLY sys.mv_user_permissions;
```
#### 4.4.2 复杂统计查询优化
```sql
-- 使用CTE和窗口函数优化统计查询
WITH daily_stats AS (
SELECT
DATE(create_time) AS stat_date,
agent_id,
COUNT(*) AS session_count,
SUM(message_count) AS total_messages
FROM agent.tb_agent_session
WHERE deleted = false
GROUP BY DATE(create_time), agent_id
)
SELECT
agent_id,
stat_date,
session_count,
total_messages,
SUM(session_count) OVER (
PARTITION BY agent_id
ORDER BY stat_date
ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
) AS rolling_7day_sessions
FROM daily_stats;
```
## 五、现有SQL文件修改建议
### 5.1 createTableUser.sql 优化
**当前问题:**
1. 登录日志表中存储密码不安全
2. 缺少用户部门关联
**建议修改:**
```sql
-- 1. 移除登录日志表的password字段
ALTER TABLE sys.tb_sys_login_log DROP COLUMN IF EXISTS password;
-- 2. 添加用户部门关联表
CREATE TABLE sys.tb_sys_user_dept (
optsn VARCHAR(50) NOT NULL,
user_id VARCHAR(50) NOT NULL,
dept_id VARCHAR(50) NOT NULL,
is_primary BOOLEAN DEFAULT false, -- 是否主部门
position VARCHAR(100), -- 职位
creator VARCHAR(50) DEFAULT NULL,
create_time timestamptz NOT NULL DEFAULT now(),
update_time timestamptz DEFAULT NULL,
deleted BOOLEAN NOT NULL DEFAULT false,
PRIMARY KEY (user_id, dept_id),
UNIQUE (optsn)
);
-- 3. 用户表添加默认部门字段
ALTER TABLE sys.tb_sys_user
ADD COLUMN IF NOT EXISTS primary_dept_id VARCHAR(50);
```
### 5.2 createTablePermission.sql 优化
**建议修改:**
```sql
-- 1. 角色表添加排序字段
ALTER TABLE sys.tb_sys_role
ADD COLUMN IF NOT EXISTS order_num INTEGER DEFAULT 0;
-- 2. 权限表添加权限类型字段
ALTER TABLE sys.tb_sys_permission
ADD COLUMN IF NOT EXISTS permission_type VARCHAR(20) DEFAULT 'action';
-- permission_type: action-操作权限/data-数据权限/menu-菜单权限
COMMENT ON COLUMN sys.tb_sys_permission.permission_type
IS '权限类型action-操作权限/data-数据权限/menu-菜单权限';
```
### 5.3 createTableFile.sql 扩展
**建议添加:**
```sql
-- 1. 文件版本管理
ALTER TABLE file.tb_sys_file
ADD COLUMN IF NOT EXISTS version VARCHAR(20) DEFAULT '1.0',
ADD COLUMN IF NOT EXISTS parent_file_id VARCHAR(50),
ADD COLUMN IF NOT EXISTS is_latest BOOLEAN DEFAULT true;
-- 2. 文件分类
ALTER TABLE file.tb_sys_file
ADD COLUMN IF NOT EXISTS category VARCHAR(100),
ADD COLUMN IF NOT EXISTS tags TEXT[];
-- 3. 文件关联(支持文件和业务对象关联)
CREATE TABLE file.tb_file_relation (
optsn VARCHAR(50) NOT NULL,
relation_id VARCHAR(50) NOT NULL,
file_id VARCHAR(50) NOT NULL,
object_type VARCHAR(50) NOT NULL, -- 对象类型bidding_project/ticket/document等
object_id VARCHAR(50) NOT NULL, -- 对象ID
relation_type VARCHAR(30) DEFAULT 'attachment', -- 关联类型attachment-附件/avatar-头像/banner-横幅
order_num INTEGER DEFAULT 0,
creator VARCHAR(50) DEFAULT NULL,
create_time timestamptz NOT NULL DEFAULT now(),
deleted BOOLEAN NOT NULL DEFAULT false,
PRIMARY KEY (relation_id),
UNIQUE (optsn),
FOREIGN KEY (file_id) REFERENCES file.tb_sys_file(file_id)
);
CREATE INDEX idx_file_relation_object
ON file.tb_file_relation(object_type, object_id) WHERE deleted = false;
```
### 5.4 createTableMessage.sql 增强
**建议添加:**
```sql
-- 1. 消息模板表
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, -- 变量定义
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,
deleted BOOLEAN NOT NULL DEFAULT false,
PRIMARY KEY (template_id),
UNIQUE (optsn),
UNIQUE (template_code)
);
COMMENT ON TABLE message.tb_message_template IS '消息模板表';
```
## 六、数据安全建议
### 6.1 敏感数据加密
**需要加密的字段:**
1. `sys.tb_sys_user.password` - 用户密码使用bcrypt/argon2
2. `agent.tb_agent_tool.auth_config` - API认证配置
3. `agent.tb_api_integration.auth_config` - 集成认证配置
4. `customer_service.tb_crm_config.api_key` - CRM API密钥
**建议使用PostgreSQL的pgcrypto扩展**
```sql
CREATE EXTENSION IF NOT EXISTS pgcrypto;
-- 加密示例
INSERT INTO agent.tb_agent_tool (auth_config)
VALUES (pgp_sym_encrypt('{"api_key": "secret"}', 'encryption_key'));
-- 解密示例
SELECT pgp_sym_decrypt(auth_config::bytea, 'encryption_key')
FROM agent.tb_agent_tool;
```
### 6.2 行级安全策略RLS
```sql
-- 启用行级安全
ALTER TABLE knowledge.tb_knowledge_document ENABLE ROW LEVEL SECURITY;
-- 创建策略:用户只能访问自己部门的文档
CREATE POLICY dept_isolation_policy ON knowledge.tb_knowledge_document
FOR SELECT
USING (
dept_path LIKE (
SELECT dept_path || '%'
FROM sys.tb_sys_user
WHERE user_id = current_setting('app.current_user_id')
)
);
```
### 6.3 审计日志增强
```sql
-- 添加审计触发器
CREATE OR REPLACE FUNCTION audit_trigger_func()
RETURNS TRIGGER AS $$
BEGIN
IF (TG_OP = 'DELETE') THEN
INSERT INTO log.tb_sys_log (
log_id, type, level, module, message, data, creator
) VALUES (
gen_random_uuid()::text,
'audit',
'info',
TG_TABLE_SCHEMA || '.' || TG_TABLE_NAME,
'Record deleted',
row_to_json(OLD),
current_setting('app.current_user_id', true)
);
RETURN OLD;
ELSIF (TG_OP = 'UPDATE') THEN
INSERT INTO log.tb_sys_log (
log_id, type, level, module, message, data, creator
) VALUES (
gen_random_uuid()::text,
'audit',
'info',
TG_TABLE_SCHEMA || '.' || TG_TABLE_NAME,
'Record updated',
jsonb_build_object('old', row_to_json(OLD), 'new', row_to_json(NEW)),
current_setting('app.current_user_id', true)
);
RETURN NEW;
END IF;
END;
$$ LANGUAGE plpgsql;
-- 为敏感表添加审计触发器
CREATE TRIGGER audit_user_changes
AFTER UPDATE OR DELETE ON sys.tb_sys_user
FOR EACH ROW EXECUTE FUNCTION audit_trigger_func();
```
## 七、部署和维护建议
### 7.1 初始化顺序
```bash
# 1. 创建扩展
psql -d urbanlifeline -f extensions.sql
# 2. 按依赖顺序执行建表脚本
psql -d urbanlifeline -f createTablePermission.sql
psql -d urbanlifeline -f createTableUser.sql
psql -d urbanlifeline -f createTableFile.sql
psql -d urbanlifeline -f createTableMessage.sql
psql -d urbanlifeline -f createTableLog.sql
psql -d urbanlifeline -f createTableConfig.sql
psql -d urbanlifeline -f createTableKnowledge.sql
psql -d urbanlifeline -f createTableBidding.sql
psql -d urbanlifeline -f createTableCustomerService.sql
psql -d urbanlifeline -f createTableAgent.sql
# 3. 执行完整初始化(包含触发器和视图)
psql -d urbanlifeline -f createTableAll.sql
# 4. 初始化基础数据
psql -d urbanlifeline -f initDataConfig.sql
```
### 7.2 定期维护任务
```sql
-- 1. 分析和优化表
ANALYZE VERBOSE;
VACUUM ANALYZE;
-- 2. 重建索引(处理索引膨胀)
REINDEX DATABASE urbanlifeline;
-- 3. 更新表统计信息
VACUUM ANALYZE sys.tb_sys_user;
VACUUM ANALYZE agent.tb_agent_session;
-- 4. 检查慢查询
SELECT query, calls, total_time, mean_time
FROM pg_stat_statements
ORDER BY mean_time DESC
LIMIT 20;
```
### 7.3 备份策略
```bash
# 全量备份(每日)
pg_dump -Fc urbanlifeline > backup_$(date +%Y%m%d).dump
# 增量备份使用WAL归档
# 在postgresql.conf中配置
# wal_level = replica
# archive_mode = on
# archive_command = 'cp %p /path/to/archive/%f'
# 恢复示例
pg_restore -d urbanlifeline -c backup_20240101.dump
```
## 八、总结
### 8.1 设计优势
1. **模块化架构**9个Schema清晰划分业务边界
2. **可扩展性**:支持多租户、多智能体、多业务类型
3. **性能优化**:合理的索引设计、分区策略、视图优化
4. **数据安全**:软删除、审计日志、权限控制、数据加密
5. **业务完整**:覆盖招投标、客服、知识库全流程
### 8.2 后续优化方向
1. **向量检索优化**引入pgvector扩展优化RAG检索性能
2. **分布式部署**考虑Citus扩展实现水平扩展
3. **实时数据同步**使用Logical Replication实现异地备份
4. **监控告警**集成pgBadger、pg_stat_statements等工具
5. **读写分离**:主从复制实现读写分离
### 8.3 关键指标
- **总表数**50+ 张核心表
- **Schema数**9 个业务Schema
- **索引类型**B-Tree、GIN、部分索引、表达式索引
- **数据类型**:标准类型 + JSONB + 数组 + 向量(可选)
- **时区支持**全部使用timestamptz
- **软删除**全表支持deleted标记
---
**文档版本**: 1.0
**最后更新**: 2024-12-02
**维护团队**: 泰豪电源AI数智化平台开发组

396
docs/网关认证方案.md Normal file
View File

@@ -0,0 +1,396 @@
# Gateway 认证方案说明
## 问题背景
在微服务架构中,如果同时使用 Gateway 和 common-auth 模块,会出现**重复认证**的问题:
```
浏览器 → Gateway (AuthGlobalFilter 验证 JWT)
→ 微服务 (JwtAuthenticationFilter 再次验证 JWT) ❌ 重复!
```
## 解决方案
提供两种认证模式,通过配置选择:
### **模式一Gateway 统一认证(推荐)**
Gateway 负责认证,微服务信任 Gateway 传递的用户信息。
#### 架构流程
```
浏览器
↓ (带 JWT Token)
Nginx (80端口)
Gateway (8080端口)
├─ AuthGlobalFilter: 验证 JWT ✓
├─ 提取用户信息 (userId, username)
├─ 添加到请求头传递给下游
└─ 路由到微服务
微服务
└─ GatewayTrustFilter: 从请求头获取用户信息 ✓
```
#### 配置方式
**1. Gateway 服务配置** (`gateway/application.yml`)
```yaml
auth:
enabled: true
token-header: Authorization
token-prefix: "Bearer "
auth-paths:
- /auth/login
- /auth/logout
whitelist:
- /actuator/**
- /v3/api-docs/**
```
**2. 微服务配置** (`system/application.yml`, `log/application.yml` 等)
```yaml
auth:
enabled: true
gateway-mode: true # 关键:启用 Gateway 模式
```
#### 优点
- ✅ 避免重复认证,性能更好
- ✅ 统一认证逻辑,易维护
- ✅ 微服务之间调用不需要传递 JWT
---
### **模式二:微服务独立认证**
每个微服务独立验证 JWTGateway 不做认证。
#### 架构流程
```
浏览器
↓ (带 JWT Token)
Nginx
Gateway
└─ 直接路由(不验证)
微服务
└─ JwtAuthenticationFilter: 验证 JWT ✓
```
#### 配置方式
**1. Gateway 服务配置**
```yaml
auth:
enabled: false # 关键:关闭 Gateway 认证
```
**2. 微服务配置**
```yaml
auth:
enabled: true
gateway-mode: false # 或不配置(默认 false
token-header: Authorization
token-prefix: "Bearer "
```
#### 适用场景
- 微服务需要直接对外暴露(不经过 Gateway
- 对安全性要求极高,需要每层都验证
---
## 配置文件对比
### Gateway 服务 (`gateway/application.yml`)
```yaml
server:
port: 8080
spring:
application:
name: gateway-service
cloud:
gateway:
routes:
- id: auth-service
uri: lb://auth-service
predicates:
- Path=/auth/**
filters:
- StripPrefix=1
- id: system-service
uri: lb://system-service
predicates:
- Path=/system/**
filters:
- StripPrefix=1
# 认证配置
auth:
enabled: true # 是否启用认证
gateway-mode: false # Gateway 本身不需要此配置
token-header: Authorization
token-prefix: "Bearer "
auth-paths:
- /auth/login
- /auth/logout
- /auth/captcha
- /auth/refresh
whitelist:
- /actuator/**
- /v3/api-docs/**
```
### 微服务配置 (Gateway 模式)
**auth-service/application.yml**
```yaml
server:
port: 8081
spring:
application:
name: auth-service
# 认证配置
auth:
enabled: true
gateway-mode: true # 关键:信任 Gateway
token-header: Authorization
token-prefix: "Bearer "
whitelist:
- /v3/api-docs/**
- /actuator/**
```
**system-service/application.yml**
```yaml
server:
port: 8082
spring:
application:
name: system-service
auth:
enabled: true
gateway-mode: true # 关键:信任 Gateway
```
---
## 工作原理
### Gateway 认证流程
**AuthGlobalFilter (Gateway 层)**
```java
1. 检查请求路径是否在白名单
2. 提取 Authorization 请求头中的 JWT Token
3. 验证 Token 是否过期
4. 验证 Token 签名是否有效
5. 提取用户信息 (userId, username)
6. 将用户信息添加到请求头
- X-User-Id: {userId}
- X-Username: {username}
7. 路由到下游微服务
```
### 微服务信任流程
**GatewayTrustFilter (微服务层)**
```java
1. 从请求头获取 Gateway 传递的用户信息
- X-User-Id
- X-Username
2. 构造 Spring Security 认证对象
3. 设置到 SecurityContext
4. 设置到 request attributes供业务代码使用
```
---
## 安全考虑
### 内网安全
采用 Gateway 模式时,需确保:
1. **微服务不对外暴露**
- 只能通过 Gateway 访问
- 使用 Kubernetes Network Policy 或防火墙隔离
2. **请求头保护**
- Gateway 在转发前清除任何客户端传递的 `X-User-Id` 等头
- 防止伪造用户身份
3. **Gateway 过滤器增强**(可选)
```java
// 在 Gateway 中清除客户端可能伪造的请求头
ServerHttpRequest mutatedRequest = request.mutate()
.headers(headers -> {
headers.remove("X-User-Id");
headers.remove("X-Username");
})
.header(AuthContants.USER_ID_ATTRIBUTE, userId)
.header(AuthContants.TOKEN_ATTRIBUTE, token)
.build();
```
---
## 测试验证
### 测试 Gateway 模式
**1. 登录获取 Token**
```bash
curl -X POST http://localhost/api/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"123456"}'
# 响应
{
"code": 200,
"data": {
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"userId": "1001"
}
}
```
**2. 使用 Token 访问受保护接口**
```bash
curl -X GET http://localhost/api/system/user/profile \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
```
**3. 查看日志验证单次认证**
```
Gateway 日志:
[Gateway] Token 验证成功: userId=1001, path=/system/user/profile
System-Service 日志:
[GatewayTrustFilter] 从 Gateway 获取用户信息: userId=1001, username=admin
```
---
## 迁移指南
### 从独立认证迁移到 Gateway 统一认证
**步骤 1**: 更新所有微服务配置
```yaml
auth:
gateway-mode: true # 添加这一行
```
**步骤 2**: 重启服务(先重启微服务,后重启 Gateway
```bash
# 重启微服务
docker-compose restart auth-service system-service log-service
# 重启 Gateway
docker-compose restart gateway
```
**步骤 3**: 验证功能
- 测试登录
- 测试受保护接口访问
- 检查日志确认只认证一次
---
## 常见问题
### Q1: Gateway 模式下,微服务之间如何调用?
**A**: 微服务间调用不需要传递 JWT Token直接调用即可。Gateway已经验证过身份。
```java
// 微服务 A 调用微服务 B
@Autowired
private RestTemplate restTemplate;
public void callServiceB() {
// 直接调用,不需要添加 Authorization 头
String result = restTemplate.getForObject(
"http://service-b/api/xxx",
String.class
);
}
```
### Q2: 如何获取当前登录用户信息?
**A**: 使用 `@HttpLogin` 注解或从 SecurityContext 获取。
```java
// 方式一:使用注解(推荐)
@GetMapping("/profile")
public ResultDomain<UserDTO> getProfile(@HttpLogin LoginDomain loginDomain) {
String userId = loginDomain.getUser().getUserId();
// ...
}
// 方式二:从 SecurityContext 获取
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String userId = (String) auth.getPrincipal();
```
### Q3: Gateway 模式更安全还是独立认证更安全?
**A**: 取决于网络拓扑:
- **内网隔离良好**: Gateway 模式更优(性能好,维护简单)
- **微服务直接对外**: 独立认证更安全(每层验证)
---
## 推荐配置
### 生产环境推荐
```yaml
# Gateway
auth:
enabled: true
gateway-mode: false
# 所有微服务
auth:
enabled: true
gateway-mode: true
```
### 开发环境(快速调试)
可以临时关闭认证:
```yaml
auth:
enabled: false
```
---
## 总结
| 对比项 | Gateway 统一认证 | 微服务独立认证 |
|--------|-----------------|---------------|
| 认证次数 | 1次仅 Gateway | N次每个服务 |
| 性能 | ⭐⭐⭐⭐⭐ 最优 | ⭐⭐⭐ 一般 |
| 维护性 | ⭐⭐⭐⭐⭐ 统一管理 | ⭐⭐⭐ 分散管理 |
| 安全性 | ⭐⭐⭐⭐ 需内网隔离 | ⭐⭐⭐⭐⭐ 多层防护 |
| 推荐场景 | 内网微服务架构 | 微服务对外暴露 |
**推荐使用 Gateway 统一认证模式!**

View File

@@ -0,0 +1,225 @@
# 数据库初始化说明
## 概述
本目录包含城市生命线AI数智化平台的数据库初始化脚本用于创建表结构和插入基础数据。
## 文件说明
### 表结构创建脚本
| 文件名 | 说明 |
|--------|------|
| `createDB.sql` | 创建数据库 |
| `createTablePermission.sql` | 创建权限相关表部门、角色、权限、视图、ACL等 |
| `createTableUser.sql` | 创建用户相关表(用户、用户信息、登录日志) |
| `createTableFile.sql` | 创建文件管理表 |
| `createTableMessage.sql` | 创建消息通知表 |
| `createTableLog.sql` | 创建日志表 |
| `createTableConfig.sql` | 创建配置管理表 |
| `createTableKnowledge.sql` | 创建知识库表 |
| `createTableBidding.sql` | 创建招投标业务表 |
| `createTableCustomerService.sql` | 创建智能客服表 |
| `createTableAgent.sql` | 创建智能体表(暂不启用) |
### 数据初始化脚本
| 文件名 | 说明 | 是否必需 |
|--------|------|----------|
| `initDataPermission.sql` | 初始化权限基础数据(部门、角色、权限、视图、模块) | ✅ 必需 |
| `initDataUser.sql` | 初始化用户数据(管理员账户、演示用户) | ✅ 必需 |
| `initDataMessage.sql` | 初始化消息渠道配置和模板 | ✅ 必需 |
| `initDataConfig.sql` | 初始化系统配置参数 | ✅ 必需 |
### 总入口脚本
- `initAll.sql` - 一键执行所有建表和初始化脚本
## 使用方法
### 方式一:使用总入口脚本(推荐)
```bash
# 进入 PostgreSQL 命令行
psql -U postgres
# 执行总初始化脚本
\i /path/to/urbanLifelineServ/.bin/database/postgres/sql/initAll.sql
```
### 方式二:分步执行
#### 第一步:创建表结构
```bash
# 创建数据库
\i createDB.sql
# 创建各模块表结构
\i createTablePermission.sql
\i createTableUser.sql
\i createTableFile.sql
\i createTableMessage.sql
\i createTableLog.sql
\i createTableConfig.sql
\i createTableKnowledge.sql
\i createTableBidding.sql
\i createTableCustomerService.sql
```
#### 第二步:初始化基础数据
```bash
# 按顺序执行初始化脚本
\i initDataPermission.sql
\i initDataUser.sql
\i initDataMessage.sql
\i initDataConfig.sql
```
## 初始化数据说明
### 1. 权限基础数据 (initDataPermission.sql)
**初始化内容:**
- **根部门**dept_root
- **全局角色**
- 超级管理员 (role_super_admin) - 拥有所有权限
- 系统管理员 (role_system_admin) - 拥有系统管理权限
- 普通用户 (role_user) - 基础查看和操作权限
- 访客 (role_guest) - 仅查看权限
- **系统模块**
- 系统管理 (mod_system)
- 文件管理 (mod_file)
- 消息通知 (mod_message)
- 配置管理 (mod_config)
- 知识库 (mod_knowledge)
- 招投标 (mod_bidding)
- 智能客服 (mod_customer_service)
- **系统权限**30+ 个基础权限,涵盖用户、角色、部门、权限、文件、消息、配置管理
- **系统视图(菜单)**
- 系统管理菜单及其子菜单
- 业务管理菜单框架
### 2. 用户数据 (initDataUser.sql)
**初始化账户:**
| 用户 | 账号 | 默认密码 | 角色 | 说明 |
|------|------|----------|------|------|
| 系统管理员 | admin | admin123 | 超级管理员 | 拥有所有权限 |
| 演示用户 | demo | admin123 | 普通用户 | 用于演示和测试 |
**⚠️ 安全提示:**
- 生产环境部署前,必须修改默认密码
- 密码使用 bcrypt 加密存储
- 建议删除演示用户账户
### 3. 消息渠道配置 (initDataMessage.sql)
**初始化内容:**
- **消息渠道**(默认禁用,需配置后启用):
- 应用内消息 (app) - 默认启用
- 短信通知 (sms)
- 邮件通知 (email)
- 微信公众号 (wechat_official_account)
- 微信小程序 (wechat_applet)
- 钉钉通知 (dingtalk)
- **消息模板**
- 用户注册欢迎
- 密码重置通知
- 系统维护通知
- 工单创建通知
- 招标公告发布
### 4. 系统配置 (initDataConfig.sql)
**初始化配置分组:**
- **站点配置**站点名称、Logo、ICP备案
- **国际化**:默认语言、时区
- **安全认证**密码策略、JWT过期时间、会话超时、注册开关
- **存储上传**:最大上传大小、存储后端、存储路径
- **通知配置**邮件SMTP、短信服务商
- **日志审计**:日志级别、审计日志保留天数
- **平台特性**维护模式、ACL策略开关
## 注意事项
1. **执行顺序**:必须按照 `initAll.sql` 中的顺序执行,先创建表结构,再插入数据
2. **依赖关系**:初始化数据脚本有依赖关系,必须按顺序执行
3. **数据库权限**:执行脚本需要具有创建数据库、创建表、插入数据的权限
4. **字符编码**:确保数据库使用 UTF-8 编码
5. **时区设置**:建议数据库时区设置为 Asia/Shanghai 或 UTC
## 验证初始化结果
执行以下 SQL 验证初始化是否成功:
```sql
-- 检查表是否创建成功
SELECT schemaname, tablename
FROM pg_tables
WHERE schemaname IN ('sys', 'file', 'message', 'config')
ORDER BY schemaname, tablename;
-- 检查角色数量
SELECT COUNT(*) as role_count FROM sys.tb_sys_role WHERE deleted = false;
-- 预期结果4
-- 检查权限数量
SELECT COUNT(*) as permission_count FROM sys.tb_sys_permission WHERE deleted = false;
-- 预期结果30+
-- 检查用户数量
SELECT COUNT(*) as user_count FROM sys.tb_sys_user WHERE deleted = false;
-- 预期结果2
-- 检查消息渠道数量
SELECT COUNT(*) as channel_count FROM message.tb_message_channel WHERE deleted = false;
-- 预期结果6
-- 检查系统配置数量
SELECT COUNT(*) as config_count FROM config.tb_sys_config WHERE deleted = false;
-- 预期结果20+
```
## 重置数据库
如需重新初始化数据库,可以执行以下操作:
```sql
-- 删除所有 schema谨慎操作
DROP SCHEMA IF EXISTS sys CASCADE;
DROP SCHEMA IF EXISTS file CASCADE;
DROP SCHEMA IF EXISTS message CASCADE;
DROP SCHEMA IF EXISTS config CASCADE;
DROP SCHEMA IF EXISTS knowledge CASCADE;
DROP SCHEMA IF EXISTS bidding CASCADE;
DROP SCHEMA IF EXISTS customer_service CASCADE;
DROP SCHEMA IF EXISTS log CASCADE;
-- 然后重新执行 initAll.sql
\i initAll.sql
```
## 常见问题
### Q: 执行初始化脚本报错 "relation already exists"
A: 表已存在,可以选择删除对应的表或 schema 后重新执行
### Q: 如何修改管理员默认密码?
A: 修改 `initDataUser.sql` 中的 password 字段,使用 bcrypt 加密后的密码哈希
### Q: 如何自定义初始化数据?
A: 直接修改对应的 `initData*.sql` 文件,按照现有格式添加或修改数据
### Q: 业务表(知识库、招投标、客服)需要初始化数据吗?
A: 不需要,这些表的数据在系统运行时动态产生
## 联系支持
如有问题,请联系技术支持团队。

View File

@@ -1,308 +1,306 @@
-- ============================= -- -- =============================
-- 智能体管理和平台基础设施模块 -- -- 智能体管理和平台基础设施模块
-- 支持智能体广场、API集成管理、智能体运维监控 -- -- 支持智能体广场、API集成管理、智能体运维监控
-- ============================= -- -- =============================
CREATE SCHEMA IF NOT EXISTS agent; -- CREATE SCHEMA IF NOT EXISTS agent;
-- 智能体定义表 -- -- 智能体定义表
DROP TABLE IF EXISTS agent.tb_agent CASCADE; -- DROP TABLE IF EXISTS agent.tb_agent CASCADE;
CREATE TABLE agent.tb_agent ( -- CREATE TABLE agent.tb_agent (
optsn VARCHAR(50) NOT NULL, -- 流水号 -- optsn VARCHAR(50) NOT NULL, -- 流水号
agent_id VARCHAR(50) NOT NULL, -- 智能体ID -- agent_id VARCHAR(50) NOT NULL, -- 智能体ID
agent_code VARCHAR(100) NOT NULL, -- 智能体编码(唯一标识) -- agent_code VARCHAR(100) NOT NULL, -- 智能体编码(唯一标识)
agent_name VARCHAR(255) NOT NULL, -- 智能体名称 -- agent_name VARCHAR(255) NOT NULL, -- 智能体名称
agent_type VARCHAR(50) NOT NULL, -- 智能体类型bidding-招投标/customer_service-客服/knowledge_assistant-知识助手/custom-自定义 -- agent_type VARCHAR(50) NOT NULL, -- 智能体类型bidding-招投标/customer_service-客服/knowledge_assistant-知识助手/custom-自定义
display_name VARCHAR(255) NOT NULL, -- 展示名称 -- display_name VARCHAR(255) NOT NULL, -- 展示名称
description TEXT, -- 智能体描述 -- description TEXT, -- 智能体描述
icon VARCHAR(500), -- 图标URL -- icon VARCHAR(500), -- 图标URL
banner VARCHAR(500), -- Banner图URL -- banner VARCHAR(500), -- Banner图URL
version VARCHAR(20) DEFAULT '1.0.0', -- 版本号 -- version VARCHAR(20) DEFAULT '1.0.0', -- 版本号
model_provider VARCHAR(50), -- 模型提供商openai/anthropic/baidu/aliyun/custom -- model_provider VARCHAR(50), -- 模型提供商openai/anthropic/baidu/aliyun/custom
model_name VARCHAR(100), -- 模型名称 -- model_name VARCHAR(100), -- 模型名称
model_config JSONB, -- 模型配置温度、最大tokens等 -- model_config JSONB, -- 模型配置温度、最大tokens等
prompt_template TEXT, -- 提示词模板 -- prompt_template TEXT, -- 提示词模板
system_prompt TEXT, -- 系统提示词 -- system_prompt TEXT, -- 系统提示词
kb_ids VARCHAR(50)[], -- 关联知识库ID数组 -- capabilities TEXT[], -- 能力列表
tool_ids VARCHAR(50)[], -- 关联工具ID数组 -- access_level VARCHAR(20) DEFAULT 'private', -- 访问级别public-公开/private-私有/internal-内部
capabilities TEXT[], -- 能力列表 -- is_published BOOLEAN DEFAULT false, -- 是否发布到智能体广场
access_level VARCHAR(20) DEFAULT 'private', -- 访问级别public-公开/private-私有/internal-内部 -- usage_count INTEGER DEFAULT 0, -- 使用次数
is_published BOOLEAN DEFAULT false, -- 是否发布到智能体广场 -- rating DECIMAL(3,2) DEFAULT 0, -- 评分0-5
usage_count INTEGER DEFAULT 0, -- 使用次 -- rating_count INTEGER DEFAULT 0, -- 评分人
rating DECIMAL(3,2) DEFAULT 0, -- 评分0-5 -- tags TEXT[], -- 标签数组
rating_count INTEGER DEFAULT 0, -- 评分人数 -- category VARCHAR(100), -- 分类
tags TEXT[], -- 标签数组 -- dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
category VARCHAR(100), -- 分类 -- owner_user_id VARCHAR(50), -- 所有者用户ID
dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径 -- status VARCHAR(20) DEFAULT 'active', -- 状态active-激活/inactive-停用/under_maintenance-维护中
owner_user_id VARCHAR(50), -- 所有者用户ID -- creator VARCHAR(50) DEFAULT NULL, -- 创建者
status VARCHAR(20) DEFAULT 'active', -- 状态active-激活/inactive-停用/under_maintenance-维护中 -- updater VARCHAR(50) DEFAULT NULL, -- 更新者
creator VARCHAR(50) DEFAULT NULL, -- 创建 -- create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
updater VARCHAR(50) DEFAULT NULL, -- 更新 -- update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间
create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间 -- delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间
update_time timestamptz DEFAULT NULL, -- 更新时间 -- deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
delete_time timestamptz DEFAULT NULL, -- 删除时间 -- PRIMARY KEY (agent_id),
deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 -- UNIQUE (optsn),
PRIMARY KEY (agent_id), -- UNIQUE (agent_code)
UNIQUE (optsn), -- );
UNIQUE (agent_code)
);
CREATE INDEX idx_agent_type ON agent.tb_agent(agent_type) WHERE deleted = false; -- CREATE INDEX idx_agent_type ON agent.tb_agent(agent_type) WHERE deleted = false;
CREATE INDEX idx_agent_published ON agent.tb_agent(is_published) WHERE deleted = false AND is_published = true; -- CREATE INDEX idx_agent_published ON agent.tb_agent(is_published) WHERE deleted = false AND is_published = true;
CREATE INDEX idx_agent_category ON agent.tb_agent(category) WHERE deleted = false; -- CREATE INDEX idx_agent_category ON agent.tb_agent(category) WHERE deleted = false;
COMMENT ON TABLE agent.tb_agent IS '智能体定义表'; -- COMMENT ON TABLE agent.tb_agent IS '智能体定义表';
COMMENT ON COLUMN agent.tb_agent.agent_type IS '智能体类型bidding/customer_service/knowledge_assistant/custom'; -- COMMENT ON COLUMN agent.tb_agent.agent_type IS '智能体类型bidding/customer_service/knowledge_assistant/custom';
-- 智能体会话表 -- -- 智能体会话表
DROP TABLE IF EXISTS agent.tb_agent_session CASCADE; -- DROP TABLE IF EXISTS agent.tb_agent_session CASCADE;
CREATE TABLE agent.tb_agent_session ( -- CREATE TABLE agent.tb_agent_session (
optsn VARCHAR(50) NOT NULL, -- 流水号 -- optsn VARCHAR(50) NOT NULL, -- 流水号
session_id VARCHAR(50) NOT NULL, -- 会话ID -- session_id VARCHAR(50) NOT NULL, -- 会话ID
agent_id VARCHAR(50) NOT NULL, -- 智能体ID -- agent_id VARCHAR(50) NOT NULL, -- 智能体ID
user_id VARCHAR(50) NOT NULL, -- 用户ID -- user_id VARCHAR(50) NOT NULL, -- 用户ID
session_type VARCHAR(30) DEFAULT 'chat', -- 会话类型chat-对话/task-任务/workflow-工作流 -- session_type VARCHAR(30) DEFAULT 'chat', -- 会话类型chat-对话/task-任务/workflow-工作流
session_name VARCHAR(255), -- 会话名称 -- session_name VARCHAR(255), -- 会话名称
context JSONB, -- 会话上下文 -- context JSONB, -- 会话上下文
session_status VARCHAR(20) DEFAULT 'active', -- 会话状态active-活跃/paused-暂停/ended-结束 -- session_status VARCHAR(20) DEFAULT 'active', -- 会话状态active-活跃/paused-暂停/ended-结束
start_time timestamptz DEFAULT now(), -- 开始时间 -- start_time TIMESTAMPTZ DEFAULT now(), -- 开始时间
end_time timestamptz, -- 结束时间 -- end_time TIMESTAMPTZ, -- 结束时间
message_count INTEGER DEFAULT 0, -- 消息数量 -- message_count INTEGER DEFAULT 0, -- 消息数量
token_usage INTEGER DEFAULT 0, -- Token使用量 -- token_usage INTEGER DEFAULT 0, -- Token使用量
cost DECIMAL(10,4) DEFAULT 0, -- 成本 -- cost DECIMAL(10,4) DEFAULT 0, -- 成本
dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径 -- dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
creator VARCHAR(50) DEFAULT NULL, -- 创建者 -- creator VARCHAR(50) DEFAULT NULL, -- 创建者
create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间 -- create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
update_time timestamptz DEFAULT NULL, -- 更新时间 -- update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间
deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 -- deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
PRIMARY KEY (session_id), -- PRIMARY KEY (session_id),
UNIQUE (optsn), -- UNIQUE (optsn),
FOREIGN KEY (agent_id) REFERENCES agent.tb_agent(agent_id) -- FOREIGN KEY (agent_id) REFERENCES agent.tb_agent(agent_id)
); -- );
CREATE INDEX idx_session_agent ON agent.tb_agent_session(agent_id) WHERE deleted = false; -- CREATE INDEX idx_session_agent ON agent.tb_agent_session(agent_id) WHERE deleted = false;
CREATE INDEX idx_session_user ON agent.tb_agent_session(user_id) WHERE deleted = false; -- CREATE INDEX idx_session_user ON agent.tb_agent_session(user_id) WHERE deleted = false;
COMMENT ON TABLE agent.tb_agent_session IS '智能体会话表'; -- COMMENT ON TABLE agent.tb_agent_session IS '智能体会话表';
-- 智能体消息表 -- -- 智能体消息表
DROP TABLE IF EXISTS agent.tb_agent_message CASCADE; -- DROP TABLE IF EXISTS agent.tb_agent_message CASCADE;
CREATE TABLE agent.tb_agent_message ( -- CREATE TABLE agent.tb_agent_message (
optsn VARCHAR(50) NOT NULL, -- 流水号 -- optsn VARCHAR(50) NOT NULL, -- 流水号
message_id VARCHAR(50) NOT NULL, -- 消息ID -- message_id VARCHAR(50) NOT NULL, -- 消息ID
session_id VARCHAR(50) NOT NULL, -- 会话ID -- session_id VARCHAR(50) NOT NULL, -- 会话ID
agent_id VARCHAR(50) NOT NULL, -- 智能体ID -- agent_id VARCHAR(50) NOT NULL, -- 智能体ID
role VARCHAR(20) NOT NULL, -- 角色user-用户/assistant-助手/system-系统/function-函数 -- role VARCHAR(20) NOT NULL, -- 角色user-用户/assistant-助手/system-系统/function-函数
content TEXT, -- 消息内容 -- content TEXT, -- 消息内容
content_type VARCHAR(30) DEFAULT 'text', -- 内容类型text-文本/image-图片/file-文件/structured-结构化数据 -- content_type VARCHAR(30) DEFAULT 'text', -- 内容类型text-文本/image-图片/file-文件/structured-结构化数据
function_call JSONB, -- 函数调用JSON格式 -- function_call JSONB, -- 函数调用JSON格式
function_response JSONB, -- 函数响应 -- function_response JSONB, -- 函数响应
token_count INTEGER, -- Token数量 -- token_count INTEGER, -- Token数量
model_name VARCHAR(100), -- 使用的模型 -- model_name VARCHAR(100), -- 使用的模型
kb_references JSONB, -- 知识库引用JSON数组 -- kb_references JSONB, -- 知识库引用JSON数组
metadata JSONB, -- 消息元数据 -- metadata JSONB, -- 消息元数据
dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径 -- dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间 -- create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 -- deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
PRIMARY KEY (message_id), -- PRIMARY KEY (message_id),
UNIQUE (optsn), -- UNIQUE (optsn),
FOREIGN KEY (session_id) REFERENCES agent.tb_agent_session(session_id), -- FOREIGN KEY (session_id) REFERENCES agent.tb_agent_session(session_id),
FOREIGN KEY (agent_id) REFERENCES agent.tb_agent(agent_id) -- FOREIGN KEY (agent_id) REFERENCES agent.tb_agent(agent_id)
); -- );
CREATE INDEX idx_msg_session ON agent.tb_agent_message(session_id, create_time) WHERE deleted = false; -- CREATE INDEX idx_msg_session ON agent.tb_agent_message(session_id, create_time) WHERE deleted = false;
CREATE INDEX idx_msg_agent ON agent.tb_agent_message(agent_id) WHERE deleted = false; -- CREATE INDEX idx_msg_agent ON agent.tb_agent_message(agent_id) WHERE deleted = false;
COMMENT ON TABLE agent.tb_agent_message IS '智能体消息表'; -- COMMENT ON TABLE agent.tb_agent_message IS '智能体消息表';
-- 智能体工具表 -- -- 智能体工具表
DROP TABLE IF EXISTS agent.tb_agent_tool CASCADE; -- DROP TABLE IF EXISTS agent.tb_agent_tool CASCADE;
CREATE TABLE agent.tb_agent_tool ( -- CREATE TABLE agent.tb_agent_tool (
optsn VARCHAR(50) NOT NULL, -- 流水号 -- optsn VARCHAR(50) NOT NULL, -- 流水号
tool_id VARCHAR(50) NOT NULL, -- 工具ID -- tool_id VARCHAR(50) NOT NULL, -- 工具ID
tool_code VARCHAR(100) NOT NULL, -- 工具编码 -- tool_code VARCHAR(100) NOT NULL, -- 工具编码
tool_name VARCHAR(255) NOT NULL, -- 工具名称 -- tool_name VARCHAR(255) NOT NULL, -- 工具名称
tool_type VARCHAR(50) NOT NULL, -- 工具类型api-API调用/function-函数/plugin-插件/integration-集成 -- tool_type VARCHAR(50) NOT NULL, -- 工具类型api-API调用/function-函数/plugin-插件/integration-集成
description TEXT, -- 工具描述 -- description TEXT, -- 工具描述
function_schema JSONB, -- 函数SchemaOpenAI function calling格式 -- function_schema JSONB, -- 函数SchemaOpenAI function calling格式
api_endpoint VARCHAR(500), -- API端点 -- api_endpoint VARCHAR(500), -- API端点
api_method VARCHAR(10), -- API方法GET/POST/PUT/DELETE -- api_method VARCHAR(10), -- API方法GET/POST/PUT/DELETE
api_headers JSONB, -- API请求头 -- api_headers JSONB, -- API请求头
auth_type VARCHAR(30), -- 认证类型none/api_key/oauth2/bearer -- auth_type VARCHAR(30), -- 认证类型none/api_key/oauth2/bearer
auth_config JSONB, -- 认证配置(加密存储) -- auth_config JSONB, -- 认证配置(加密存储)
request_template TEXT, -- 请求模板 -- request_template TEXT, -- 请求模板
response_template TEXT, -- 响应模板 -- response_template TEXT, -- 响应模板
timeout_seconds INTEGER DEFAULT 30, -- 超时时间(秒) -- timeout_seconds INTEGER DEFAULT 30, -- 超时时间(秒)
retry_count INTEGER DEFAULT 3, -- 重试次数 -- retry_count INTEGER DEFAULT 3, -- 重试次数
is_enabled BOOLEAN DEFAULT true, -- 是否启用 -- is_enabled BOOLEAN DEFAULT true, -- 是否启用
usage_count INTEGER DEFAULT 0, -- 使用次数 -- usage_count INTEGER DEFAULT 0, -- 使用次数
dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径 -- dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
creator VARCHAR(50) DEFAULT NULL, -- 创建者 -- creator VARCHAR(50) DEFAULT NULL, -- 创建者
updater VARCHAR(50) DEFAULT NULL, -- 更新者 -- updater VARCHAR(50) DEFAULT NULL, -- 更新者
create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间 -- create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
update_time timestamptz DEFAULT NULL, -- 更新时间 -- update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间
delete_time timestamptz DEFAULT NULL, -- 删除时间 -- delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间
deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 -- deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
PRIMARY KEY (tool_id), -- PRIMARY KEY (tool_id),
UNIQUE (optsn), -- UNIQUE (optsn),
UNIQUE (tool_code) -- UNIQUE (tool_code)
); -- );
CREATE INDEX idx_tool_type ON agent.tb_agent_tool(tool_type) WHERE deleted = false; -- CREATE INDEX idx_tool_type ON agent.tb_agent_tool(tool_type) WHERE deleted = false;
COMMENT ON TABLE agent.tb_agent_tool IS '智能体工具表'; -- COMMENT ON TABLE agent.tb_agent_tool IS '智能体工具表';
-- API集成注册表 -- -- API集成注册表
DROP TABLE IF EXISTS agent.tb_api_integration CASCADE; -- DROP TABLE IF EXISTS agent.tb_api_integration CASCADE;
CREATE TABLE agent.tb_api_integration ( -- CREATE TABLE agent.tb_api_integration (
optsn VARCHAR(50) NOT NULL, -- 流水号 -- optsn VARCHAR(50) NOT NULL, -- 流水号
integration_id VARCHAR(50) NOT NULL, -- 集成ID -- integration_id VARCHAR(50) NOT NULL, -- 集成ID
integration_name VARCHAR(255) NOT NULL, -- 集成名称 -- integration_name VARCHAR(255) NOT NULL, -- 集成名称
integration_type VARCHAR(50) NOT NULL, -- 集成类型rest_api/soap/graphql/webhook/mq -- integration_type VARCHAR(50) NOT NULL, -- 集成类型rest_api/soap/graphql/webhook/mq
provider VARCHAR(100), -- 提供商 -- provider VARCHAR(100), -- 提供商
base_url VARCHAR(500), -- 基础URL -- base_url VARCHAR(500), -- 基础URL
version VARCHAR(20), -- API版本 -- version VARCHAR(20), -- API版本
auth_type VARCHAR(30) DEFAULT 'none', -- 认证类型 -- auth_type VARCHAR(30) DEFAULT 'none', -- 认证类型
auth_config JSONB, -- 认证配置(加密存储) -- auth_config JSONB, -- 认证配置(加密存储)
endpoints JSONB, -- 端点列表JSON数组 -- endpoints JSONB, -- 端点列表JSON数组
rate_limit INTEGER, -- 速率限制(请求/秒) -- rate_limit INTEGER, -- 速率限制(请求/秒)
timeout_seconds INTEGER DEFAULT 30, -- 超时时间 -- timeout_seconds INTEGER DEFAULT 30, -- 超时时间
retry_config JSONB, -- 重试配置 -- retry_config JSONB, -- 重试配置
health_check_url VARCHAR(500), -- 健康检查URL -- health_check_url VARCHAR(500), -- 健康检查URL
health_status VARCHAR(20) DEFAULT 'unknown', -- 健康状态healthy-健康/unhealthy-不健康/unknown-未知 -- health_status VARCHAR(20) DEFAULT 'unknown', -- 健康状态healthy-健康/unhealthy-不健康/unknown-未知
last_health_check timestamptz, -- 最后健康检查时间 -- last_health_check TIMESTAMPTZ, -- 最后健康检查时间
documentation_url VARCHAR(500), -- 文档URL -- documentation_url VARCHAR(500), -- 文档URL
is_enabled BOOLEAN DEFAULT true, -- 是否启用 -- is_enabled BOOLEAN DEFAULT true, -- 是否启用
dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径 -- dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
creator VARCHAR(50) DEFAULT NULL, -- 创建者 -- creator VARCHAR(50) DEFAULT NULL, -- 创建者
updater VARCHAR(50) DEFAULT NULL, -- 更新者 -- updater VARCHAR(50) DEFAULT NULL, -- 更新者
create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间 -- create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
update_time timestamptz DEFAULT NULL, -- 更新时间 -- update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间
delete_time timestamptz DEFAULT NULL, -- 删除时间 -- delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间
deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 -- deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
PRIMARY KEY (integration_id), -- PRIMARY KEY (integration_id),
UNIQUE (optsn) -- UNIQUE (optsn)
); -- );
CREATE INDEX idx_integration_type ON agent.tb_api_integration(integration_type) WHERE deleted = false; -- CREATE INDEX idx_integration_type ON agent.tb_api_integration(integration_type) WHERE deleted = false;
CREATE INDEX idx_integration_health ON agent.tb_api_integration(health_status) WHERE deleted = false; -- CREATE INDEX idx_integration_health ON agent.tb_api_integration(health_status) WHERE deleted = false;
COMMENT ON TABLE agent.tb_api_integration IS 'API集成注册表'; -- COMMENT ON TABLE agent.tb_api_integration IS 'API集成注册表';
-- API调用日志表 -- -- API调用日志表
DROP TABLE IF EXISTS agent.tb_api_call_log CASCADE; -- DROP TABLE IF EXISTS agent.tb_api_call_log CASCADE;
CREATE TABLE agent.tb_api_call_log ( -- CREATE TABLE agent.tb_api_call_log (
optsn VARCHAR(50) NOT NULL, -- 流水号 -- optsn VARCHAR(50) NOT NULL, -- 流水号
log_id VARCHAR(50) NOT NULL, -- 日志ID -- log_id VARCHAR(50) NOT NULL, -- 日志ID
integration_id VARCHAR(50), -- 集成ID -- integration_id VARCHAR(50), -- 集成ID
tool_id VARCHAR(50), -- 工具ID -- tool_id VARCHAR(50), -- 工具ID
agent_id VARCHAR(50), -- 智能体ID -- agent_id VARCHAR(50), -- 智能体ID
session_id VARCHAR(50), -- 会话ID -- session_id VARCHAR(50), -- 会话ID
user_id VARCHAR(50), -- 用户ID -- user_id VARCHAR(50), -- 用户ID
endpoint VARCHAR(500) NOT NULL, -- 请求端点 -- endpoint VARCHAR(500) NOT NULL, -- 请求端点
method VARCHAR(10) NOT NULL, -- 请求方法 -- method VARCHAR(10) NOT NULL, -- 请求方法
request_headers JSONB, -- 请求头 -- request_headers JSONB, -- 请求头
request_body TEXT, -- 请求体 -- request_body TEXT, -- 请求体
response_status INTEGER, -- 响应状态码 -- response_status INTEGER, -- 响应状态码
response_headers JSONB, -- 响应头 -- response_headers JSONB, -- 响应头
response_body TEXT, -- 响应体 -- response_body TEXT, -- 响应体
duration_ms INTEGER, -- 请求耗时(毫秒) -- duration_ms INTEGER, -- 请求耗时(毫秒)
is_success BOOLEAN, -- 是否成功 -- is_success BOOLEAN, -- 是否成功
error_message TEXT, -- 错误信息 -- error_message TEXT, -- 错误信息
retry_count INTEGER DEFAULT 0, -- 重试次数 -- retry_count INTEGER DEFAULT 0, -- 重试次数
ip_address VARCHAR(45), -- IP地址 -- ip_address VARCHAR(45), -- IP地址
dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径 -- dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间 -- create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
PRIMARY KEY (log_id), -- PRIMARY KEY (log_id),
UNIQUE (optsn) -- UNIQUE (optsn)
); -- );
CREATE INDEX idx_api_log_integration ON agent.tb_api_call_log(integration_id, create_time DESC); -- CREATE INDEX idx_api_log_integration ON agent.tb_api_call_log(integration_id, create_time DESC);
CREATE INDEX idx_api_log_agent ON agent.tb_api_call_log(agent_id, create_time DESC); -- CREATE INDEX idx_api_log_agent ON agent.tb_api_call_log(agent_id, create_time DESC);
CREATE INDEX idx_api_log_status ON agent.tb_api_call_log(is_success, create_time DESC); -- CREATE INDEX idx_api_log_status ON agent.tb_api_call_log(is_success, create_time DESC);
COMMENT ON TABLE agent.tb_api_call_log IS 'API调用日志表'; -- COMMENT ON TABLE agent.tb_api_call_log IS 'API调用日志表';
-- 智能体监控指标表 -- -- 智能体监控指标表
DROP TABLE IF EXISTS agent.tb_agent_metrics CASCADE; -- DROP TABLE IF EXISTS agent.tb_agent_metrics CASCADE;
CREATE TABLE agent.tb_agent_metrics ( -- CREATE TABLE agent.tb_agent_metrics (
optsn VARCHAR(50) NOT NULL, -- 流水号 -- optsn VARCHAR(50) NOT NULL, -- 流水号
metric_id VARCHAR(50) NOT NULL, -- 指标ID -- metric_id VARCHAR(50) NOT NULL, -- 指标ID
agent_id VARCHAR(50) NOT NULL, -- 智能体ID -- agent_id VARCHAR(50) NOT NULL, -- 智能体ID
metric_date DATE NOT NULL, -- 指标日期 -- metric_date DATE NOT NULL, -- 指标日期
metric_hour INTEGER, -- 指标小时0-23 -- metric_hour INTEGER, -- 指标小时0-23
total_sessions INTEGER DEFAULT 0, -- 总会话数 -- total_sessions INTEGER DEFAULT 0, -- 总会话数
active_sessions INTEGER DEFAULT 0, -- 活跃会话数 -- active_sessions INTEGER DEFAULT 0, -- 活跃会话数
total_messages INTEGER DEFAULT 0, -- 总消息数 -- total_messages INTEGER DEFAULT 0, -- 总消息数
total_tokens BIGINT DEFAULT 0, -- 总Token数 -- total_tokens BIGINT DEFAULT 0, -- 总Token数
total_cost DECIMAL(10,4) DEFAULT 0, -- 总成本 -- total_cost DECIMAL(10,4) DEFAULT 0, -- 总成本
avg_response_time INTEGER, -- 平均响应时间(毫秒) -- avg_response_time INTEGER, -- 平均响应时间(毫秒)
success_rate DECIMAL(5,4), -- 成功率 -- success_rate DECIMAL(5,4), -- 成功率
error_count INTEGER DEFAULT 0, -- 错误次数 -- error_count INTEGER DEFAULT 0, -- 错误次数
api_call_count INTEGER DEFAULT 0, -- API调用次数 -- api_call_count INTEGER DEFAULT 0, -- API调用次数
avg_rating DECIMAL(3,2), -- 平均评分 -- avg_rating DECIMAL(3,2), -- 平均评分
rating_count INTEGER DEFAULT 0, -- 评分数量 -- rating_count INTEGER DEFAULT 0, -- 评分数量
dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径 -- dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间 -- create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
update_time timestamptz DEFAULT NULL, -- 更新时间 -- update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间
PRIMARY KEY (metric_id), -- PRIMARY KEY (metric_id),
UNIQUE (optsn), -- UNIQUE (optsn),
UNIQUE (agent_id, metric_date, metric_hour), -- UNIQUE (agent_id, metric_date, metric_hour),
FOREIGN KEY (agent_id) REFERENCES agent.tb_agent(agent_id) -- FOREIGN KEY (agent_id) REFERENCES agent.tb_agent(agent_id)
); -- );
CREATE INDEX idx_metrics_agent_date ON agent.tb_agent_metrics(agent_id, metric_date DESC); -- CREATE INDEX idx_metrics_agent_date ON agent.tb_agent_metrics(agent_id, metric_date DESC);
COMMENT ON TABLE agent.tb_agent_metrics IS '智能体监控指标表'; -- COMMENT ON TABLE agent.tb_agent_metrics IS '智能体监控指标表';
-- 智能体异常日志表 -- -- 智能体异常日志表
DROP TABLE IF EXISTS agent.tb_agent_error_log CASCADE; -- DROP TABLE IF EXISTS agent.tb_agent_error_log CASCADE;
CREATE TABLE agent.tb_agent_error_log ( -- CREATE TABLE agent.tb_agent_error_log (
optsn VARCHAR(50) NOT NULL, -- 流水号 -- optsn VARCHAR(50) NOT NULL, -- 流水号
log_id VARCHAR(50) NOT NULL, -- 日志ID -- log_id VARCHAR(50) NOT NULL, -- 日志ID
agent_id VARCHAR(50) NOT NULL, -- 智能体ID -- agent_id VARCHAR(50) NOT NULL, -- 智能体ID
session_id VARCHAR(50), -- 会话ID -- session_id VARCHAR(50), -- 会话ID
error_type VARCHAR(50) NOT NULL, -- 错误类型model_error-模型错误/api_error-API错误/timeout-超时/rate_limit-限流/other-其他 -- error_type VARCHAR(50) NOT NULL, -- 错误类型model_error-模型错误/api_error-API错误/timeout-超时/rate_limit-限流/other-其他
error_code VARCHAR(50), -- 错误代码 -- error_code VARCHAR(50), -- 错误代码
error_message TEXT NOT NULL, -- 错误信息 -- error_message TEXT NOT NULL, -- 错误信息
stack_trace TEXT, -- 堆栈跟踪 -- stack_trace TEXT, -- 堆栈跟踪
request_context JSONB, -- 请求上下文 -- request_context JSONB, -- 请求上下文
severity VARCHAR(20) DEFAULT 'error', -- 严重级别critical-致命/error-错误/warning-警告 -- severity VARCHAR(20) DEFAULT 'error', -- 严重级别critical-致命/error-错误/warning-警告
is_resolved BOOLEAN DEFAULT false, -- 是否已解决 -- is_resolved BOOLEAN DEFAULT false, -- 是否已解决
resolution_notes TEXT, -- 解决方案备注 -- resolution_notes TEXT, -- 解决方案备注
dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径 -- dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间 -- create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
resolve_time timestamptz, -- 解决时间 -- resolve_time TIMESTAMPTZ, -- 解决时间
PRIMARY KEY (log_id), -- PRIMARY KEY (log_id),
UNIQUE (optsn), -- UNIQUE (optsn),
FOREIGN KEY (agent_id) REFERENCES agent.tb_agent(agent_id) -- FOREIGN KEY (agent_id) REFERENCES agent.tb_agent(agent_id)
); -- );
CREATE INDEX idx_error_agent ON agent.tb_agent_error_log(agent_id, create_time DESC); -- CREATE INDEX idx_error_agent ON agent.tb_agent_error_log(agent_id, create_time DESC);
CREATE INDEX idx_error_severity ON agent.tb_agent_error_log(severity) WHERE is_resolved = false; -- CREATE INDEX idx_error_severity ON agent.tb_agent_error_log(severity) WHERE is_resolved = false;
COMMENT ON TABLE agent.tb_agent_error_log IS '智能体异常日志表'; -- COMMENT ON TABLE agent.tb_agent_error_log IS '智能体异常日志表';
-- 智能体评价表 -- -- 智能体评价表
DROP TABLE IF EXISTS agent.tb_agent_rating CASCADE; -- DROP TABLE IF EXISTS agent.tb_agent_rating CASCADE;
CREATE TABLE agent.tb_agent_rating ( -- CREATE TABLE agent.tb_agent_rating (
optsn VARCHAR(50) NOT NULL, -- 流水号 -- optsn VARCHAR(50) NOT NULL, -- 流水号
rating_id VARCHAR(50) NOT NULL, -- 评价ID -- rating_id VARCHAR(50) NOT NULL, -- 评价ID
agent_id VARCHAR(50) NOT NULL, -- 智能体ID -- agent_id VARCHAR(50) NOT NULL, -- 智能体ID
session_id VARCHAR(50), -- 会话ID -- session_id VARCHAR(50), -- 会话ID
user_id VARCHAR(50) NOT NULL, -- 用户ID -- user_id VARCHAR(50) NOT NULL, -- 用户ID
rating INTEGER NOT NULL, -- 评分1-5星 -- rating INTEGER NOT NULL, -- 评分1-5星
dimensions JSONB, -- 分维度评分(准确性、速度、友好度等) -- dimensions JSONB, -- 分维度评分(准确性、速度、友好度等)
feedback TEXT, -- 评价反馈 -- feedback TEXT, -- 评价反馈
tags TEXT[], -- 标签 -- tags TEXT[], -- 标签
is_anonymous BOOLEAN DEFAULT false, -- 是否匿名 -- is_anonymous BOOLEAN DEFAULT false, -- 是否匿名
dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径 -- dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间 -- create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 -- deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
PRIMARY KEY (rating_id), -- PRIMARY KEY (rating_id),
UNIQUE (optsn), -- UNIQUE (optsn),
FOREIGN KEY (agent_id) REFERENCES agent.tb_agent(agent_id) -- FOREIGN KEY (agent_id) REFERENCES agent.tb_agent(agent_id)
); -- );
CREATE INDEX idx_rating_agent ON agent.tb_agent_rating(agent_id) WHERE deleted = false; -- CREATE INDEX idx_rating_agent ON agent.tb_agent_rating(agent_id) WHERE deleted = false;
COMMENT ON TABLE agent.tb_agent_rating IS '智能体评价表'; -- COMMENT ON TABLE agent.tb_agent_rating IS '智能体评价表';

View File

@@ -55,59 +55,6 @@ CREATE EXTENSION IF NOT EXISTS "btree_gin"; -- GIN索引支持
-- ============================= -- =============================
\i createTableAgent.sql \i createTableAgent.sql
-- =============================
-- 创建通用触发器函数
-- =============================
-- 自动更新update_time的触发器函数
CREATE OR REPLACE FUNCTION public.update_modified_column()
RETURNS TRIGGER AS $$
BEGIN
NEW.update_time = now();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
-- 为所有表添加update_time触发器的辅助函数
CREATE OR REPLACE FUNCTION public.create_update_triggers()
RETURNS void AS $$
DECLARE
r RECORD;
BEGIN
FOR r IN
SELECT schemaname, tablename
FROM pg_tables
WHERE schemaname IN ('sys', 'file', 'message', 'log', 'config', 'knowledge', 'bidding', 'customer_service', 'agent')
AND tablename LIKE 'tb_%'
LOOP
-- 检查表是否有update_time列
IF EXISTS (
SELECT 1
FROM information_schema.columns
WHERE table_schema = r.schemaname
AND table_name = r.tablename
AND column_name = 'update_time'
) THEN
-- 删除已存在的触发器
EXECUTE format('DROP TRIGGER IF EXISTS trg_%s_update_time ON %I.%I',
r.tablename, r.schemaname, r.tablename);
-- 创建新触发器
EXECUTE format('CREATE TRIGGER trg_%s_update_time
BEFORE UPDATE ON %I.%I
FOR EACH ROW
EXECUTE FUNCTION public.update_modified_column()',
r.tablename, r.schemaname, r.tablename);
RAISE NOTICE 'Created trigger for %.%', r.schemaname, r.tablename;
END IF;
END LOOP;
END;
$$ LANGUAGE plpgsql;
-- 执行触发器创建
SELECT public.create_update_triggers();
-- ============================= -- =============================
-- 创建视图 -- 创建视图
-- ============================= -- =============================

View File

@@ -15,9 +15,9 @@ CREATE TABLE bidding.tb_bidding_project (
industry VARCHAR(100), -- 所属行业 industry VARCHAR(100), -- 所属行业
source_platform VARCHAR(100), -- 来源平台(如:政府采购网、企业官网等) source_platform VARCHAR(100), -- 来源平台(如:政府采购网、企业官网等)
source_url VARCHAR(500), -- 来源URL source_url VARCHAR(500), -- 来源URL
publish_date timestamptz, -- 发布日期 publish_date TIMESTAMPTZ, -- 发布日期
deadline timestamptz, -- 投标截止日期 deadline TIMESTAMPTZ, -- 投标截止日期
opening_date timestamptz, -- 开标日期 opening_date TIMESTAMPTZ, -- 开标日期
budget_amount DECIMAL(18,2), -- 预算金额 budget_amount DECIMAL(18,2), -- 预算金额
currency VARCHAR(10) DEFAULT 'CNY', -- 货币单位 currency VARCHAR(10) DEFAULT 'CNY', -- 货币单位
project_status VARCHAR(30) NOT NULL DEFAULT 'collecting', -- 项目状态collecting-收集中/analyzing-分析中/preparing-准备投标/submitted-已提交/opened-已开标/won-中标/lost-未中标/abandoned-放弃 project_status VARCHAR(30) NOT NULL DEFAULT 'collecting', -- 项目状态collecting-收集中/analyzing-分析中/preparing-准备投标/submitted-已提交/opened-已开标/won-中标/lost-未中标/abandoned-放弃
@@ -35,9 +35,9 @@ CREATE TABLE bidding.tb_bidding_project (
team_members VARCHAR(50)[], -- 团队成员数组 team_members VARCHAR(50)[], -- 团队成员数组
creator VARCHAR(50) DEFAULT NULL, -- 创建者 creator VARCHAR(50) DEFAULT NULL, -- 创建者
updater VARCHAR(50) DEFAULT NULL, -- 更新者 updater VARCHAR(50) DEFAULT NULL, -- 更新者
create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间 create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
update_time timestamptz DEFAULT NULL, -- 更新时间 update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间
delete_time timestamptz DEFAULT NULL, -- 删除时间 delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间
deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
PRIMARY KEY (project_id), PRIMARY KEY (project_id),
UNIQUE (optsn), UNIQUE (optsn),
@@ -71,13 +71,13 @@ CREATE TABLE bidding.tb_bidding_document (
parse_result JSONB, -- 解析结果JSON格式提取的要素、表格、图纸等 parse_result JSONB, -- 解析结果JSON格式提取的要素、表格、图纸等
extraction_data JSONB, -- 提取的结构化数据 extraction_data JSONB, -- 提取的结构化数据
ai_analysis TEXT, -- AI分析结果 ai_analysis TEXT, -- AI分析结果
upload_date timestamptz DEFAULT now(), -- 上传日期 upload_date TIMESTAMPTZ DEFAULT now(), -- 上传日期
dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径 dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
creator VARCHAR(50) DEFAULT NULL, -- 创建者 creator VARCHAR(50) DEFAULT NULL, -- 创建者
updater VARCHAR(50) DEFAULT NULL, -- 更新者 updater VARCHAR(50) DEFAULT NULL, -- 更新者
create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间 create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
update_time timestamptz DEFAULT NULL, -- 更新时间 update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间
delete_time timestamptz DEFAULT NULL, -- 删除时间 delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间
deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
PRIMARY KEY (doc_id), PRIMARY KEY (doc_id),
UNIQUE (optsn), UNIQUE (optsn),
@@ -113,9 +113,9 @@ CREATE TABLE bidding.tb_bidding_requirement (
dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径 dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
creator VARCHAR(50) DEFAULT NULL, -- 创建者 creator VARCHAR(50) DEFAULT NULL, -- 创建者
updater VARCHAR(50) DEFAULT NULL, -- 更新者 updater VARCHAR(50) DEFAULT NULL, -- 更新者
create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间 create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
update_time timestamptz DEFAULT NULL, -- 更新时间 update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间
delete_time timestamptz DEFAULT NULL, -- 删除时间 delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间
deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
PRIMARY KEY (req_id), PRIMARY KEY (req_id),
UNIQUE (optsn), UNIQUE (optsn),
@@ -151,9 +151,9 @@ CREATE TABLE bidding.tb_bid_response (
dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径 dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
creator VARCHAR(50) DEFAULT NULL, -- 创建者 creator VARCHAR(50) DEFAULT NULL, -- 创建者
updater VARCHAR(50) DEFAULT NULL, -- 更新者 updater VARCHAR(50) DEFAULT NULL, -- 更新者
create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间 create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
update_time timestamptz DEFAULT NULL, -- 更新时间 update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间
delete_time timestamptz DEFAULT NULL, -- 删除时间 delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间
deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
PRIMARY KEY (response_id), PRIMARY KEY (response_id),
UNIQUE (optsn), UNIQUE (optsn),
@@ -186,9 +186,9 @@ CREATE TABLE bidding.tb_bidding_scoring_rule (
dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径 dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
creator VARCHAR(50) DEFAULT NULL, -- 创建者 creator VARCHAR(50) DEFAULT NULL, -- 创建者
updater VARCHAR(50) DEFAULT NULL, -- 更新者 updater VARCHAR(50) DEFAULT NULL, -- 更新者
create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间 create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
update_time timestamptz DEFAULT NULL, -- 更新时间 update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间
delete_time timestamptz DEFAULT NULL, -- 删除时间 delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间
deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
PRIMARY KEY (rule_id), PRIMARY KEY (rule_id),
UNIQUE (optsn), UNIQUE (optsn),
@@ -209,10 +209,10 @@ CREATE TABLE bidding.tb_bidding_process (
node_type VARCHAR(50) NOT NULL, -- 节点类型collection-文件收集/analysis-需求分析/preparation-文件准备/review-内部审核/submission-投标提交/opening-开标/result-结果通知 node_type VARCHAR(50) NOT NULL, -- 节点类型collection-文件收集/analysis-需求分析/preparation-文件准备/review-内部审核/submission-投标提交/opening-开标/result-结果通知
node_order INTEGER NOT NULL, -- 节点顺序 node_order INTEGER NOT NULL, -- 节点顺序
node_status VARCHAR(30) DEFAULT 'pending', -- 节点状态pending-待处理/in_progress-进行中/completed-已完成/skipped-已跳过 node_status VARCHAR(30) DEFAULT 'pending', -- 节点状态pending-待处理/in_progress-进行中/completed-已完成/skipped-已跳过
planned_start_time timestamptz, -- 计划开始时间 planned_start_time TIMESTAMPTZ, -- 计划开始时间
planned_end_time timestamptz, -- 计划结束时间 planned_end_time TIMESTAMPTZ, -- 计划结束时间
actual_start_time timestamptz, -- 实际开始时间 actual_start_time TIMESTAMPTZ, -- 实际开始时间
actual_end_time timestamptz, -- 实际结束时间 actual_end_time TIMESTAMPTZ, -- 实际结束时间
responsible_user VARCHAR(50), -- 负责人 responsible_user VARCHAR(50), -- 负责人
participants VARCHAR(50)[], -- 参与人员数组 participants VARCHAR(50)[], -- 参与人员数组
notes TEXT, -- 节点备注 notes TEXT, -- 节点备注
@@ -220,9 +220,9 @@ CREATE TABLE bidding.tb_bidding_process (
dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径 dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
creator VARCHAR(50) DEFAULT NULL, -- 创建者 creator VARCHAR(50) DEFAULT NULL, -- 创建者
updater VARCHAR(50) DEFAULT NULL, -- 更新者 updater VARCHAR(50) DEFAULT NULL, -- 更新者
create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间 create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
update_time timestamptz DEFAULT NULL, -- 更新时间 update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间
delete_time timestamptz DEFAULT NULL, -- 删除时间 delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间
deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
PRIMARY KEY (process_id), PRIMARY KEY (process_id),
UNIQUE (optsn), UNIQUE (optsn),
@@ -251,9 +251,9 @@ CREATE TABLE bidding.tb_bid_template (
status VARCHAR(20) DEFAULT 'active', -- 状态active-激活/inactive-停用 status VARCHAR(20) DEFAULT 'active', -- 状态active-激活/inactive-停用
creator VARCHAR(50) DEFAULT NULL, -- 创建者 creator VARCHAR(50) DEFAULT NULL, -- 创建者
updater VARCHAR(50) DEFAULT NULL, -- 更新者 updater VARCHAR(50) DEFAULT NULL, -- 更新者
create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间 create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
update_time timestamptz DEFAULT NULL, -- 更新时间 update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间
delete_time timestamptz DEFAULT NULL, -- 删除时间 delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间
deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
PRIMARY KEY (template_id), PRIMARY KEY (template_id),
UNIQUE (optsn) UNIQUE (optsn)

View File

@@ -6,7 +6,7 @@ CREATE TABLE config.tb_sys_config (
key VARCHAR(255) NOT NULL, -- 配置键 key VARCHAR(255) NOT NULL, -- 配置键
name VARCHAR(255) NOT NULL, -- 配置名称 name VARCHAR(255) NOT NULL, -- 配置名称
value VARCHAR(255) NOT NULL, -- 配置值 value VARCHAR(255) NOT NULL, -- 配置值
config_type VARCHAR(50) NOT NULL, -- 数据类型(String, Integer, Boolean, Float, Double) config_type VARCHAR(50) NOT NULL, -- 数据类型(String, INTEGER, BOOLEAN, Float, Double)
render_type VARCHAR(50) NOT NULL, -- 配置渲染类型(select, input, textarea, checkbox, radio, switch) render_type VARCHAR(50) NOT NULL, -- 配置渲染类型(select, input, textarea, checkbox, radio, switch)
description VARCHAR(255) NOT NULL, -- 配置描述 description VARCHAR(255) NOT NULL, -- 配置描述
re JSON DEFAULT NULL, -- 正则表达式校验规则 re JSON DEFAULT NULL, -- 正则表达式校验规则
@@ -19,9 +19,9 @@ CREATE TABLE config.tb_sys_config (
creator VARCHAR(50) DEFAULT NULL, -- 创建者 creator VARCHAR(50) DEFAULT NULL, -- 创建者
dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径支持like递归如/1/2/3/ dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径支持like递归如/1/2/3/
updater VARCHAR(50) DEFAULT NULL, -- 更新者 updater VARCHAR(50) DEFAULT NULL, -- 更新者
create_time timestamptz NOT NULL DEFAULT now(), -- 配置创建时间 create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 配置创建时间
update_time timestamptz DEFAULT NULL, -- 配置更新时间 update_time TIMESTAMPTZ DEFAULT NULL, -- 配置更新时间
delete_time timestamptz DEFAULT NULL, -- 配置删除时间 delete_time TIMESTAMPTZ DEFAULT NULL, -- 配置删除时间
deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
PRIMARY KEY (config_id), PRIMARY KEY (config_id),
UNIQUE (optsn) UNIQUE (optsn)

View File

@@ -25,7 +25,7 @@ CREATE TABLE customer_service.tb_customer (
tags TEXT[], -- 客户标签数组 tags TEXT[], -- 客户标签数组
notes TEXT, -- 备注 notes TEXT, -- 备注
crm_customer_id VARCHAR(50), -- CRM系统客户ID外部系统 crm_customer_id VARCHAR(50), -- CRM系统客户ID外部系统
last_contact_time timestamptz, -- 最后联系时间 last_contact_time TIMESTAMPTZ, -- 最后联系时间
total_consultations INTEGER DEFAULT 0, -- 咨询总次数 total_consultations INTEGER DEFAULT 0, -- 咨询总次数
total_orders INTEGER DEFAULT 0, -- 订单总数 total_orders INTEGER DEFAULT 0, -- 订单总数
total_amount DECIMAL(18,2) DEFAULT 0, -- 总消费金额 total_amount DECIMAL(18,2) DEFAULT 0, -- 总消费金额
@@ -34,9 +34,9 @@ CREATE TABLE customer_service.tb_customer (
status VARCHAR(20) DEFAULT 'active', -- 状态active-活跃/inactive-非活跃/blacklist-黑名单 status VARCHAR(20) DEFAULT 'active', -- 状态active-活跃/inactive-非活跃/blacklist-黑名单
creator VARCHAR(50) DEFAULT NULL, -- 创建者 creator VARCHAR(50) DEFAULT NULL, -- 创建者
updater VARCHAR(50) DEFAULT NULL, -- 更新者 updater VARCHAR(50) DEFAULT NULL, -- 更新者
create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间 create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
update_time timestamptz DEFAULT NULL, -- 更新时间 update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间
delete_time timestamptz DEFAULT NULL, -- 删除时间 delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间
deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
PRIMARY KEY (customer_id), PRIMARY KEY (customer_id),
UNIQUE (optsn), UNIQUE (optsn),
@@ -62,8 +62,8 @@ CREATE TABLE customer_service.tb_conversation (
channel VARCHAR(20) DEFAULT 'wechat', -- 渠道wechat-微信/web-网页/app-应用/phone-电话 channel VARCHAR(20) DEFAULT 'wechat', -- 渠道wechat-微信/web-网页/app-应用/phone-电话
agent_id VARCHAR(50), -- 智能体ID或客服人员ID agent_id VARCHAR(50), -- 智能体ID或客服人员ID
agent_type VARCHAR(20) DEFAULT 'ai', -- 座席类型ai-AI/human-人工 agent_type VARCHAR(20) DEFAULT 'ai', -- 座席类型ai-AI/human-人工
session_start_time timestamptz DEFAULT now(), -- 会话开始时间 session_start_time TIMESTAMPTZ DEFAULT now(), -- 会话开始时间
session_end_time timestamptz, -- 会话结束时间 session_end_time TIMESTAMPTZ, -- 会话结束时间
duration_seconds INTEGER, -- 会话时长(秒) duration_seconds INTEGER, -- 会话时长(秒)
message_count INTEGER DEFAULT 0, -- 消息数量 message_count INTEGER DEFAULT 0, -- 消息数量
conversation_status VARCHAR(20) DEFAULT 'active', -- 会话状态active-进行中/closed-已结束/transferred-已转接/timeout-超时 conversation_status VARCHAR(20) DEFAULT 'active', -- 会话状态active-进行中/closed-已结束/transferred-已转接/timeout-超时
@@ -75,9 +75,9 @@ CREATE TABLE customer_service.tb_conversation (
dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径 dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
creator VARCHAR(50) DEFAULT NULL, -- 创建者 creator VARCHAR(50) DEFAULT NULL, -- 创建者
updater VARCHAR(50) DEFAULT NULL, -- 更新者 updater VARCHAR(50) DEFAULT NULL, -- 更新者
create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间 create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
update_time timestamptz DEFAULT NULL, -- 更新时间 update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间
delete_time timestamptz DEFAULT NULL, -- 删除时间 delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间
deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
PRIMARY KEY (conversation_id), PRIMARY KEY (conversation_id),
UNIQUE (optsn), UNIQUE (optsn),
@@ -110,10 +110,10 @@ CREATE TABLE customer_service.tb_conversation_message (
intent VARCHAR(100), -- 意图识别结果 intent VARCHAR(100), -- 意图识别结果
is_sensitive BOOLEAN DEFAULT false, -- 是否敏感信息 is_sensitive BOOLEAN DEFAULT false, -- 是否敏感信息
read_status BOOLEAN DEFAULT false, -- 已读状态 read_status BOOLEAN DEFAULT false, -- 已读状态
read_time timestamptz, -- 阅读时间 read_time TIMESTAMPTZ, -- 阅读时间
metadata JSONB, -- 消息元数据 metadata JSONB, -- 消息元数据
dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径 dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间(发送时间) create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间(发送时间)
deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
PRIMARY KEY (message_id), PRIMARY KEY (message_id),
UNIQUE (optsn), UNIQUE (optsn),
@@ -145,10 +145,10 @@ CREATE TABLE customer_service.tb_ticket (
assigned_dept VARCHAR(50), -- 分配部门 assigned_dept VARCHAR(50), -- 分配部门
ticket_status VARCHAR(30) DEFAULT 'pending', -- 工单状态pending-待处理/processing-处理中/resolved-已解决/closed-已关闭/cancelled-已取消 ticket_status VARCHAR(30) DEFAULT 'pending', -- 工单状态pending-待处理/processing-处理中/resolved-已解决/closed-已关闭/cancelled-已取消
resolution TEXT, -- 解决方案 resolution TEXT, -- 解决方案
resolution_time timestamptz, -- 解决时间 resolution_time TIMESTAMPTZ, -- 解决时间
close_time timestamptz, -- 关闭时间 close_time TIMESTAMPTZ, -- 关闭时间
response_time timestamptz, -- 首次响应时间 response_time TIMESTAMPTZ, -- 首次响应时间
sla_deadline timestamptz, -- SLA截止时间 sla_deadline TIMESTAMPTZ, -- SLA截止时间
is_overdue BOOLEAN DEFAULT false, -- 是否逾期 is_overdue BOOLEAN DEFAULT false, -- 是否逾期
customer_rating INTEGER, -- 客户评分1-5星 customer_rating INTEGER, -- 客户评分1-5星
customer_feedback TEXT, -- 客户反馈 customer_feedback TEXT, -- 客户反馈
@@ -159,9 +159,9 @@ CREATE TABLE customer_service.tb_ticket (
dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径 dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
creator VARCHAR(50) DEFAULT NULL, -- 创建者 creator VARCHAR(50) DEFAULT NULL, -- 创建者
updater VARCHAR(50) DEFAULT NULL, -- 更新者 updater VARCHAR(50) DEFAULT NULL, -- 更新者
create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间 create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
update_time timestamptz DEFAULT NULL, -- 更新时间 update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间
delete_time timestamptz DEFAULT NULL, -- 删除时间 delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间
deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
PRIMARY KEY (ticket_id), PRIMARY KEY (ticket_id),
UNIQUE (optsn), UNIQUE (optsn),
@@ -192,7 +192,7 @@ CREATE TABLE customer_service.tb_ticket_log (
operator_name VARCHAR(100), -- 操作人姓名 operator_name VARCHAR(100), -- 操作人姓名
attachments VARCHAR(50)[], -- 附件ID数组 attachments VARCHAR(50)[], -- 附件ID数组
dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径 dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间 create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
PRIMARY KEY (log_id), PRIMARY KEY (log_id),
UNIQUE (optsn), UNIQUE (optsn),
FOREIGN KEY (ticket_id) REFERENCES customer_service.tb_ticket(ticket_id) FOREIGN KEY (ticket_id) REFERENCES customer_service.tb_ticket(ticket_id)
@@ -207,7 +207,7 @@ DROP TABLE IF EXISTS customer_service.tb_faq CASCADE;
CREATE TABLE customer_service.tb_faq ( CREATE TABLE customer_service.tb_faq (
optsn VARCHAR(50) NOT NULL, -- 流水号 optsn VARCHAR(50) NOT NULL, -- 流水号
faq_id VARCHAR(50) NOT NULL, -- FAQ ID faq_id VARCHAR(50) NOT NULL, -- FAQ ID
kb_id VARCHAR(50), -- 关联知识库ID knowledge_id VARCHAR(50), -- 关联知识库ID
category VARCHAR(100) NOT NULL, -- 分类 category VARCHAR(100) NOT NULL, -- 分类
question TEXT NOT NULL, -- 问题 question TEXT NOT NULL, -- 问题
answer TEXT NOT NULL, -- 答案 answer TEXT NOT NULL, -- 答案
@@ -221,9 +221,9 @@ CREATE TABLE customer_service.tb_faq (
dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径 dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
creator VARCHAR(50) DEFAULT NULL, -- 创建者 creator VARCHAR(50) DEFAULT NULL, -- 创建者
updater VARCHAR(50) DEFAULT NULL, -- 更新者 updater VARCHAR(50) DEFAULT NULL, -- 更新者
create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间 create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
update_time timestamptz DEFAULT NULL, -- 更新时间 update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间
delete_time timestamptz DEFAULT NULL, -- 删除时间 delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间
deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
PRIMARY KEY (faq_id), PRIMARY KEY (faq_id),
UNIQUE (optsn) UNIQUE (optsn)
@@ -249,7 +249,7 @@ CREATE TABLE customer_service.tb_service_evaluation (
tags TEXT[], -- 评价标签 tags TEXT[], -- 评价标签
is_anonymous BOOLEAN DEFAULT false, -- 是否匿名 is_anonymous BOOLEAN DEFAULT false, -- 是否匿名
dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径 dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间 create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
PRIMARY KEY (evaluation_id), PRIMARY KEY (evaluation_id),
UNIQUE (optsn), UNIQUE (optsn),
@@ -274,13 +274,13 @@ CREATE TABLE customer_service.tb_crm_config (
sync_direction VARCHAR(30) DEFAULT 'bidirectional',-- 同步方向to_crm-单向到CRM/from_crm-单向从CRM/bidirectional-双向 sync_direction VARCHAR(30) DEFAULT 'bidirectional',-- 同步方向to_crm-单向到CRM/from_crm-单向从CRM/bidirectional-双向
field_mapping JSONB, -- 字段映射配置 field_mapping JSONB, -- 字段映射配置
sync_enabled BOOLEAN DEFAULT false, -- 是否启用同步 sync_enabled BOOLEAN DEFAULT false, -- 是否启用同步
last_sync_time timestamptz, -- 最后同步时间 last_sync_time TIMESTAMPTZ, -- 最后同步时间
dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径 dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
creator VARCHAR(50) DEFAULT NULL, -- 创建者 creator VARCHAR(50) DEFAULT NULL, -- 创建者
updater VARCHAR(50) DEFAULT NULL, -- 更新者 updater VARCHAR(50) DEFAULT NULL, -- 更新者
create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间 create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
update_time timestamptz DEFAULT NULL, -- 更新时间 update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间
delete_time timestamptz DEFAULT NULL, -- 删除时间 delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间
deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
PRIMARY KEY (config_id), PRIMARY KEY (config_id),
UNIQUE (optsn) UNIQUE (optsn)

View File

@@ -12,17 +12,21 @@ CREATE TABLE file.tb_sys_file (
mime_type VARCHAR(255) NOT NULL, -- 文件MIME类型 mime_type VARCHAR(255) NOT NULL, -- 文件MIME类型
url VARCHAR(255) NOT NULL, -- 文件URL url VARCHAR(255) NOT NULL, -- 文件URL
status VARCHAR(50) NOT NULL, -- 文件状态 status VARCHAR(50) NOT NULL, -- 文件状态
service_type VARCHAR(50), -- 服务类型bidding/customer_service/internal等
dept_path VARCHAR(255) NOT NULL, -- 当前部门路径 dept_path VARCHAR(255) NOT NULL, -- 当前部门路径
creator VARCHAR(50) DEFAULT NULL, -- 创建者 creator VARCHAR(50) DEFAULT NULL, -- 创建者
updater VARCHAR(50) DEFAULT NULL, -- 更新者 updater VARCHAR(50) DEFAULT NULL, -- 更新者
create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间 create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
update_time timestamptz DEFAULT NULL, -- 更新时间(由触发器维护) update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间(由触发器维护)
delete_time timestamptz DEFAULT NULL, -- 删除时间 delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间
deleted boolean NOT NULL DEFAULT false, -- 是否删除 deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
PRIMARY KEY (file_id), PRIMARY KEY (file_id),
UNIQUE (optsn) UNIQUE (optsn)
); );
CREATE INDEX idx_file_service ON file.tb_sys_file(service_type) WHERE deleted = false;
COMMENT ON TABLE file.tb_sys_file IS '文件表'; COMMENT ON TABLE file.tb_sys_file IS '文件表';
COMMENT ON COLUMN file.tb_sys_file.service_type IS '服务类型(用于服务间数据隔离)';
COMMENT ON COLUMN file.tb_sys_file.optsn IS '流水号'; COMMENT ON COLUMN file.tb_sys_file.optsn IS '流水号';
COMMENT ON COLUMN file.tb_sys_file.file_id IS '文件ID'; COMMENT ON COLUMN file.tb_sys_file.file_id IS '文件ID';
COMMENT ON COLUMN file.tb_sys_file.name IS '文件名'; COMMENT ON COLUMN file.tb_sys_file.name IS '文件名';
@@ -39,4 +43,48 @@ COMMENT ON COLUMN file.tb_sys_file.updater IS '更新者';
COMMENT ON COLUMN file.tb_sys_file.create_time 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.update_time IS '更新时间';
COMMENT ON COLUMN file.tb_sys_file.delete_time IS '删除时间'; COMMENT ON COLUMN file.tb_sys_file.delete_time IS '删除时间';
COMMENT ON COLUMN file.tb_sys_file.deleted IS '是否删除'; COMMENT ON COLUMN file.tb_sys_file.deleted IS '是否删除';
-- =============================
-- 文件关联表
-- =============================
DROP TABLE IF EXISTS file.tb_file_relation CASCADE;
CREATE TABLE file.tb_file_relation (
optsn VARCHAR(50) NOT NULL,
relation_id VARCHAR(50) NOT NULL,
file_id VARCHAR(50) NOT NULL,
object_type VARCHAR(50) NOT NULL, -- 对象类型bidding_project/ticket/document等
object_id VARCHAR(50) NOT NULL, -- 对象ID
relation_type VARCHAR(30) DEFAULT 'attachment', -- 关联类型attachment-附件/avatar-头像/banner-横幅
order_num INTEGER DEFAULT 0, -- 排序号
service_type VARCHAR(50), -- 服务类型继承自object_type
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 (relation_id),
UNIQUE (optsn),
FOREIGN KEY (file_id) REFERENCES file.tb_sys_file(file_id)
);
COMMENT ON TABLE file.tb_file_relation IS '文件关联表';
COMMENT ON COLUMN file.tb_file_relation.optsn IS '流水号';
COMMENT ON COLUMN file.tb_file_relation.relation_id IS '关联ID';
COMMENT ON COLUMN file.tb_file_relation.file_id IS '文件ID';
COMMENT ON COLUMN file.tb_file_relation.object_type IS '对象类型bidding_project/ticket/document等';
COMMENT ON COLUMN file.tb_file_relation.object_id IS '对象ID';
COMMENT ON COLUMN file.tb_file_relation.relation_type IS '关联类型attachment-附件/avatar-头像/banner-横幅';
COMMENT ON COLUMN file.tb_file_relation.order_num IS '排序号';
COMMENT ON COLUMN file.tb_file_relation.service_type IS '服务类型继承自object_type';
COMMENT ON COLUMN file.tb_file_relation.creator IS '创建者';
COMMENT ON COLUMN file.tb_file_relation.updater IS '更新者';
COMMENT ON COLUMN file.tb_file_relation.create_time IS '创建时间';
COMMENT ON COLUMN file.tb_file_relation.update_time IS '更新时间';
COMMENT ON COLUMN file.tb_file_relation.delete_time IS '删除时间';
COMMENT ON COLUMN file.tb_file_relation.deleted IS '是否删除';
CREATE INDEX idx_file_relation_object ON file.tb_file_relation(object_type, object_id) WHERE deleted = false;
CREATE INDEX idx_file_relation_file ON file.tb_file_relation(file_id) WHERE deleted = false;
CREATE INDEX idx_file_relation_service ON file.tb_file_relation(service_type) WHERE deleted = false;

View File

@@ -8,7 +8,8 @@ CREATE SCHEMA IF NOT EXISTS knowledge;
DROP TABLE IF EXISTS knowledge.tb_knowledge_base CASCADE; DROP TABLE IF EXISTS knowledge.tb_knowledge_base CASCADE;
CREATE TABLE knowledge.tb_knowledge_base ( CREATE TABLE knowledge.tb_knowledge_base (
optsn VARCHAR(50) NOT NULL, -- 流水号 optsn VARCHAR(50) NOT NULL, -- 流水号
kb_id VARCHAR(50) NOT NULL, -- 知识库ID agent_id VARCHAR(50) NOT NULL, -- 智能体id
knowledge_id VARCHAR(50) NOT NULL, -- 知识库ID
name VARCHAR(255) NOT NULL, -- 知识库名称 name VARCHAR(255) NOT NULL, -- 知识库名称
kb_type VARCHAR(50) NOT NULL, -- 知识库类型bidding-招投标/customer_service-客服/internal-内部协同 kb_type VARCHAR(50) NOT NULL, -- 知识库类型bidding-招投标/customer_service-客服/internal-内部协同
access_level VARCHAR(20) NOT NULL DEFAULT 'private', -- 访问级别public-公开/private-私有/internal-内部 access_level VARCHAR(20) NOT NULL DEFAULT 'private', -- 访问级别public-公开/private-私有/internal-内部
@@ -16,23 +17,26 @@ CREATE TABLE knowledge.tb_knowledge_base (
storage_path VARCHAR(500), -- 存储路径 storage_path VARCHAR(500), -- 存储路径
version VARCHAR(20) DEFAULT '1.0', -- 当前版本号 version VARCHAR(20) DEFAULT '1.0', -- 当前版本号
config JSONB DEFAULT NULL, -- 知识库配置JSON格式索引配置、检索参数等 config JSONB DEFAULT NULL, -- 知识库配置JSON格式索引配置、检索参数等
service_type VARCHAR(50), -- 服务类型bidding/customer_service/internal冗余kb_type便于查询
dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径 dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
status VARCHAR(20) NOT NULL DEFAULT 'active', -- 状态active-激活/inactive-停用/archived-归档 status VARCHAR(20) NOT NULL DEFAULT 'active', -- 状态active-激活/inactive-停用/archived-归档
creator VARCHAR(50) DEFAULT NULL, -- 创建者 creator VARCHAR(50) DEFAULT NULL, -- 创建者
updater VARCHAR(50) DEFAULT NULL, -- 更新者 updater VARCHAR(50) DEFAULT NULL, -- 更新者
create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间 create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
update_time timestamptz DEFAULT NULL, -- 更新时间 update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间
delete_time timestamptz DEFAULT NULL, -- 删除时间 delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间
deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
PRIMARY KEY (kb_id), PRIMARY KEY (knowledge_id),
UNIQUE (optsn) UNIQUE (optsn)
); );
CREATE INDEX idx_kb_type ON knowledge.tb_knowledge_base(kb_type) WHERE deleted = false; CREATE INDEX idx_kb_type ON knowledge.tb_knowledge_base(kb_type) WHERE deleted = false;
CREATE INDEX idx_kb_service ON knowledge.tb_knowledge_base(service_type) WHERE deleted = false;
CREATE INDEX idx_kb_dept_path ON knowledge.tb_knowledge_base(dept_path) WHERE deleted = false; CREATE INDEX idx_kb_dept_path ON knowledge.tb_knowledge_base(dept_path) WHERE deleted = false;
COMMENT ON TABLE knowledge.tb_knowledge_base IS '知识库表'; COMMENT ON TABLE knowledge.tb_knowledge_base IS '知识库表';
COMMENT ON COLUMN knowledge.tb_knowledge_base.kb_type IS '知识库类型bidding-招投标/customer_service-客服/internal-内部协同'; COMMENT ON COLUMN knowledge.tb_knowledge_base.kb_type IS '知识库类型bidding-招投标/customer_service-客服/internal-内部协同';
COMMENT ON COLUMN knowledge.tb_knowledge_base.service_type IS '服务类型冗余存储便于快速过滤与kb_type保持一致';
COMMENT ON COLUMN knowledge.tb_knowledge_base.access_level IS '访问级别public-公开/private-私有/internal-内部'; COMMENT ON COLUMN knowledge.tb_knowledge_base.access_level IS '访问级别public-公开/private-私有/internal-内部';
-- 知识文档表 -- 知识文档表
@@ -40,7 +44,7 @@ DROP TABLE IF EXISTS knowledge.tb_knowledge_document CASCADE;
CREATE TABLE knowledge.tb_knowledge_document ( CREATE TABLE knowledge.tb_knowledge_document (
optsn VARCHAR(50) NOT NULL, -- 流水号 optsn VARCHAR(50) NOT NULL, -- 流水号
doc_id VARCHAR(50) NOT NULL, -- 文档ID doc_id VARCHAR(50) NOT NULL, -- 文档ID
kb_id VARCHAR(50) NOT NULL, -- 所属知识库ID knowledge_id VARCHAR(50) NOT NULL, -- 所属知识库ID
title VARCHAR(500) NOT NULL, -- 文档标题 title VARCHAR(500) NOT NULL, -- 文档标题
doc_type VARCHAR(50) NOT NULL, -- 文档类型text-文本/pdf/word/excel/image/video doc_type VARCHAR(50) NOT NULL, -- 文档类型text-文本/pdf/word/excel/image/video
category VARCHAR(100), -- 文档分类(自动或手动分类) category VARCHAR(100), -- 文档分类(自动或手动分类)
@@ -50,8 +54,9 @@ CREATE TABLE knowledge.tb_knowledge_document (
file_path VARCHAR(500), -- 文件路径 file_path VARCHAR(500), -- 文件路径
file_size BIGINT, -- 文件大小(字节) file_size BIGINT, -- 文件大小(字节)
mime_type VARCHAR(100), -- MIME类型 mime_type VARCHAR(100), -- MIME类型
version VARCHAR(20) DEFAULT '1.0', -- 文档版本号 version INTEGER DEFAULT 1, -- 版本号(自动递增)
parent_doc_id VARCHAR(50), -- 文档ID用于版本管理 root_doc_id VARCHAR(50), -- 文档ID版本组标识
is_current BOOLEAN DEFAULT true, -- 是否当前使用的版本
tags TEXT[], -- 文档标签数组 tags TEXT[], -- 文档标签数组
keywords TEXT[], -- 关键词数组AI提取 keywords TEXT[], -- 关键词数组AI提取
embedding_status VARCHAR(20) DEFAULT 'pending', -- 向量化状态pending-待处理/processing-处理中/completed-完成/failed-失败 embedding_status VARCHAR(20) DEFAULT 'pending', -- 向量化状态pending-待处理/processing-处理中/completed-完成/failed-失败
@@ -59,25 +64,32 @@ CREATE TABLE knowledge.tb_knowledge_document (
chunk_count INTEGER DEFAULT 0, -- 切片数量 chunk_count INTEGER DEFAULT 0, -- 切片数量
metadata JSONB DEFAULT NULL, -- 文档元数据JSON格式 metadata JSONB DEFAULT NULL, -- 文档元数据JSON格式
source_url VARCHAR(500), -- 来源URL source_url VARCHAR(500), -- 来源URL
service_type VARCHAR(50), -- 服务类型(继承自知识库)
dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径 dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
status VARCHAR(20) NOT NULL DEFAULT 'active', -- 状态active-激活/inactive-停用/archived-归档 status VARCHAR(20) NOT NULL DEFAULT 'active', -- 状态active-激活/inactive-停用/archived-归档
creator VARCHAR(50) DEFAULT NULL, -- 创建者 creator VARCHAR(50) DEFAULT NULL, -- 创建者
updater VARCHAR(50) DEFAULT NULL, -- 更新者 updater VARCHAR(50) DEFAULT NULL, -- 更新者
create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间 create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
update_time timestamptz DEFAULT NULL, -- 更新时间 update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间
delete_time timestamptz DEFAULT NULL, -- 删除时间 delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间
deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
PRIMARY KEY (doc_id), PRIMARY KEY (doc_id),
UNIQUE (optsn), UNIQUE (optsn),
FOREIGN KEY (kb_id) REFERENCES knowledge.tb_knowledge_base(kb_id) FOREIGN KEY (knowledge_id) REFERENCES knowledge.tb_knowledge_base(knowledge_id)
); );
CREATE INDEX idx_doc_kb ON knowledge.tb_knowledge_document(kb_id) WHERE deleted = false; CREATE INDEX idx_doc_kb ON knowledge.tb_knowledge_document(knowledge_id) WHERE deleted = false;
CREATE INDEX idx_doc_service ON knowledge.tb_knowledge_document(service_type) WHERE deleted = false;
CREATE INDEX idx_doc_category ON knowledge.tb_knowledge_document(category) WHERE deleted = false; CREATE INDEX idx_doc_category ON knowledge.tb_knowledge_document(category) WHERE deleted = false;
CREATE INDEX idx_doc_embedding_status ON knowledge.tb_knowledge_document(embedding_status) WHERE deleted = false; CREATE INDEX idx_doc_embedding_status ON knowledge.tb_knowledge_document(embedding_status) WHERE deleted = false;
CREATE INDEX idx_doc_tags ON knowledge.tb_knowledge_document USING GIN(tags) WHERE deleted = false; CREATE INDEX idx_doc_tags ON knowledge.tb_knowledge_document USING GIN(tags) WHERE deleted = false;
CREATE INDEX idx_doc_root_current ON knowledge.tb_knowledge_document(root_doc_id, is_current) WHERE deleted = false;
COMMENT ON TABLE knowledge.tb_knowledge_document IS '知识文档表'; COMMENT ON TABLE knowledge.tb_knowledge_document IS '知识文档表';
COMMENT ON COLUMN knowledge.tb_knowledge_document.service_type IS '服务类型(从知识库继承,用于服务间隔离)';
COMMENT ON COLUMN knowledge.tb_knowledge_document.version IS '版本号(整数,每次创建新版本自动+1';
COMMENT ON COLUMN knowledge.tb_knowledge_document.root_doc_id IS '根文档ID同一文档的所有版本共享此ID首次上传时等于doc_id';
COMMENT ON COLUMN knowledge.tb_knowledge_document.is_current IS '是否当前使用的版本每个root_doc_id只有一个is_current=true';
COMMENT ON COLUMN knowledge.tb_knowledge_document.embedding_status IS '向量化状态pending/processing/completed/failed'; COMMENT ON COLUMN knowledge.tb_knowledge_document.embedding_status IS '向量化状态pending/processing/completed/failed';
-- 知识文档片段表用于RAG检索 -- 知识文档片段表用于RAG检索
@@ -86,31 +98,33 @@ CREATE TABLE knowledge.tb_knowledge_chunk (
optsn VARCHAR(50) NOT NULL, -- 流水号 optsn VARCHAR(50) NOT NULL, -- 流水号
chunk_id VARCHAR(50) NOT NULL, -- 片段ID chunk_id VARCHAR(50) NOT NULL, -- 片段ID
doc_id VARCHAR(50) NOT NULL, -- 所属文档ID doc_id VARCHAR(50) NOT NULL, -- 所属文档ID
kb_id VARCHAR(50) NOT NULL, -- 所属知识库ID knowledge_id VARCHAR(50) NOT NULL, -- 所属知识库ID
chunk_index INTEGER NOT NULL, -- 片段索引(在文档中的顺序) chunk_index INTEGER NOT NULL, -- 片段索引(在文档中的顺序)
content TEXT NOT NULL, -- 片段内容 content TEXT NOT NULL, -- 片段内容
content_length INTEGER, -- 内容长度 content_length INTEGER, -- 内容长度
embedding vector(1536), -- 向量嵌入假设使用OpenAI 1536维 embedding vector(1536), -- 向量嵌入假设使用OpenAI 1536维
chunk_type VARCHAR(20) DEFAULT 'text', -- 片段类型text-文本/table-表格/image-图片 chunk_type VARCHAR(20) DEFAULT 'text', -- 片段类型text-文本/table-表格/image-图片
version INTEGER DEFAULT 1, -- 分段版本号(用于乐观锁,每次编辑+1
position_info JSONB, -- 位置信息(页码、坐标等) position_info JSONB, -- 位置信息(页码、坐标等)
metadata JSONB, -- 片段元数据 metadata JSONB, -- 片段元数据
dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径 dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
creator VARCHAR(50) DEFAULT NULL, -- 创建者 creator VARCHAR(50) DEFAULT NULL, -- 创建者
create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间 create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
update_time timestamptz DEFAULT NULL, -- 更新时间 update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间
deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
PRIMARY KEY (chunk_id), PRIMARY KEY (chunk_id),
UNIQUE (optsn), UNIQUE (optsn),
FOREIGN KEY (doc_id) REFERENCES knowledge.tb_knowledge_document(doc_id), FOREIGN KEY (doc_id) REFERENCES knowledge.tb_knowledge_document(doc_id),
FOREIGN KEY (kb_id) REFERENCES knowledge.tb_knowledge_base(kb_id) FOREIGN KEY (knowledge_id) REFERENCES knowledge.tb_knowledge_base(knowledge_id)
); );
CREATE INDEX idx_chunk_doc ON knowledge.tb_knowledge_chunk(doc_id) WHERE deleted = false; CREATE INDEX idx_chunk_doc ON knowledge.tb_knowledge_chunk(doc_id) WHERE deleted = false;
CREATE INDEX idx_chunk_kb ON knowledge.tb_knowledge_chunk(kb_id) WHERE deleted = false; CREATE INDEX idx_chunk_kb ON knowledge.tb_knowledge_chunk(knowledge_id) WHERE deleted = false;
-- 向量检索索引需要安装pgvector扩展 -- 向量检索索引需要安装pgvector扩展
-- CREATE INDEX idx_chunk_embedding ON knowledge.tb_knowledge_chunk USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100); -- CREATE INDEX idx_chunk_embedding ON knowledge.tb_knowledge_chunk USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100);
COMMENT ON TABLE knowledge.tb_knowledge_chunk IS '知识文档片段表RAG检索'; COMMENT ON TABLE knowledge.tb_knowledge_chunk IS '知识文档片段表RAG检索';
COMMENT ON COLUMN knowledge.tb_knowledge_chunk.version IS '分段版本号(用于乐观锁,防止并发编辑冲突)';
COMMENT ON COLUMN knowledge.tb_knowledge_chunk.embedding IS '向量嵌入需要pgvector扩展'; COMMENT ON COLUMN knowledge.tb_knowledge_chunk.embedding IS '向量嵌入需要pgvector扩展';
-- 知识访问日志表 -- 知识访问日志表
@@ -118,7 +132,7 @@ DROP TABLE IF EXISTS knowledge.tb_knowledge_access_log CASCADE;
CREATE TABLE knowledge.tb_knowledge_access_log ( CREATE TABLE knowledge.tb_knowledge_access_log (
optsn VARCHAR(50) NOT NULL, -- 流水号 optsn VARCHAR(50) NOT NULL, -- 流水号
log_id VARCHAR(50) NOT NULL, -- 日志ID log_id VARCHAR(50) NOT NULL, -- 日志ID
kb_id VARCHAR(50), -- 知识库ID knowledge_id VARCHAR(50), -- 知识库ID
doc_id VARCHAR(50), -- 文档ID doc_id VARCHAR(50), -- 文档ID
user_id VARCHAR(50) NOT NULL, -- 用户ID user_id VARCHAR(50) NOT NULL, -- 用户ID
access_type VARCHAR(20) NOT NULL, -- 访问类型view-查看/download-下载/search-搜索/edit-编辑 access_type VARCHAR(20) NOT NULL, -- 访问类型view-查看/download-下载/search-搜索/edit-编辑
@@ -127,12 +141,12 @@ CREATE TABLE knowledge.tb_knowledge_access_log (
ip_address VARCHAR(45), -- IP地址 ip_address VARCHAR(45), -- IP地址
user_agent TEXT, -- 用户代理 user_agent TEXT, -- 用户代理
dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径 dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间 create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
PRIMARY KEY (log_id), PRIMARY KEY (log_id),
UNIQUE (optsn) UNIQUE (optsn)
); );
CREATE INDEX idx_access_log_user ON knowledge.tb_knowledge_access_log(user_id, create_time DESC); CREATE INDEX idx_access_log_user ON knowledge.tb_knowledge_access_log(user_id, create_time DESC);
CREATE INDEX idx_access_log_kb ON knowledge.tb_knowledge_access_log(kb_id, create_time DESC); CREATE INDEX idx_access_log_kb ON knowledge.tb_knowledge_access_log(knowledge_id, create_time DESC);
COMMENT ON TABLE knowledge.tb_knowledge_access_log IS '知识访问日志表'; COMMENT ON TABLE knowledge.tb_knowledge_access_log IS '知识访问日志表';

View File

@@ -6,18 +6,19 @@ CREATE TABLE log.tb_sys_log (
type VARCHAR(50) NOT NULL, -- 日志类型 type VARCHAR(50) NOT NULL, -- 日志类型
level VARCHAR(50) NOT NULL, -- 日志级别 level VARCHAR(50) NOT NULL, -- 日志级别
module VARCHAR(50) NOT NULL, -- 日志模块 module VARCHAR(50) NOT NULL, -- 日志模块
ip_address varchar(45), -- IP地址 ip_address VARCHAR(45), -- IP地址
ip_source varchar(100), -- IP来源 ip_source VARCHAR(100), -- IP来源
browser varchar(100), -- 浏览器 browser VARCHAR(100), -- 浏览器
os varchar(100), -- 操作系统 os VARCHAR(100), -- 操作系统
message VARCHAR(255) NOT NULL, -- 日志消息 message VARCHAR(255) NOT NULL, -- 日志消息
data JSONB DEFAULT NULL, -- 日志数据 data JSONB DEFAULT NULL, -- 日志数据
creator VARCHAR(50) DEFAULT NULL, -- 创建者 creator VARCHAR(50) DEFAULT NULL, -- 创建者
service_type VARCHAR(50) NOT NULL, -- 服务类型
dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径 dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
updater VARCHAR(50) DEFAULT NULL, -- 更新者 updater VARCHAR(50) DEFAULT NULL, -- 更新者
create_time timestamptz NOT NULL DEFAULT now(), -- 日志创建时间 create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 日志创建时间
update_time timestamptz DEFAULT NULL, -- 日志更新时间 update_time TIMESTAMPTZ DEFAULT NULL, -- 日志更新时间
delete_time timestamptz DEFAULT NULL, -- 日志删除时间 delete_time TIMESTAMPTZ DEFAULT NULL, -- 日志删除时间
deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
PRIMARY KEY (log_id), PRIMARY KEY (log_id),
UNIQUE (optsn) UNIQUE (optsn)
@@ -31,6 +32,7 @@ COMMENT ON COLUMN log.tb_sys_log.module IS '日志模块';
COMMENT ON COLUMN log.tb_sys_log.message IS '日志消息'; COMMENT ON COLUMN log.tb_sys_log.message IS '日志消息';
COMMENT ON COLUMN log.tb_sys_log.data IS '日志数据'; COMMENT ON COLUMN log.tb_sys_log.data IS '日志数据';
COMMENT ON COLUMN log.tb_sys_log.creator IS '创建者'; COMMENT ON COLUMN log.tb_sys_log.creator IS '创建者';
COMMENT ON COLUMN log.tb_sys_log.service_type IS '服务类型';
COMMENT ON COLUMN log.tb_sys_log.dept_path IS '部门全路径'; COMMENT ON COLUMN log.tb_sys_log.dept_path IS '部门全路径';
COMMENT ON COLUMN log.tb_sys_log.updater IS '更新者'; COMMENT ON COLUMN log.tb_sys_log.updater IS '更新者';
COMMENT ON COLUMN log.tb_sys_log.create_time IS '日志创建时间'; COMMENT ON COLUMN log.tb_sys_log.create_time IS '日志创建时间';

View File

@@ -8,12 +8,13 @@ CREATE TABLE message.tb_message (
content VARCHAR(255) NOT NULL, -- 消息内容 content VARCHAR(255) NOT NULL, -- 消息内容
type VARCHAR(50) NOT NULL, -- 消息类型 type VARCHAR(50) NOT NULL, -- 消息类型
status VARCHAR(50) NOT NULL, -- 消息状态 status VARCHAR(50) NOT NULL, -- 消息状态
service_type VARCHAR(50) NOT NULL, -- 服务类型
dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径(隔离) dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径(隔离)
creator VARCHAR(50) NOT NULL DEFAULT 'system',-- 创建者 creator VARCHAR(50) NOT NULL DEFAULT 'system',-- 创建者
updater VARCHAR(50) DEFAULT NULL, -- 更新者 updater VARCHAR(50) DEFAULT NULL, -- 更新者
create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间 create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
update_time timestamptz DEFAULT NULL, update_time TIMESTAMPTZ DEFAULT NULL,
delete_time timestamptz DEFAULT NULL, delete_time TIMESTAMPTZ DEFAULT NULL,
deleted BOOLEAN NOT NULL DEFAULT false, deleted BOOLEAN NOT NULL DEFAULT false,
PRIMARY KEY (message_id), PRIMARY KEY (message_id),
UNIQUE (optsn) UNIQUE (optsn)
@@ -26,6 +27,7 @@ COMMENT ON COLUMN message.tb_message.title IS '消息标题';
COMMENT ON COLUMN message.tb_message.content IS '消息内容'; COMMENT ON COLUMN message.tb_message.content IS '消息内容';
COMMENT ON COLUMN message.tb_message.type IS '消息类型'; COMMENT ON COLUMN message.tb_message.type IS '消息类型';
COMMENT ON COLUMN message.tb_message.status IS '消息状态'; COMMENT ON COLUMN message.tb_message.status IS '消息状态';
COMMENT ON COLUMN message.tb_message.service_type IS '服务类型';
COMMENT ON COLUMN message.tb_message.dept_path IS '部门全路径'; COMMENT ON COLUMN message.tb_message.dept_path IS '部门全路径';
COMMENT ON COLUMN message.tb_message.creator IS '创建者'; COMMENT ON COLUMN message.tb_message.creator IS '创建者';
COMMENT ON COLUMN message.tb_message.updater IS '更新者'; COMMENT ON COLUMN message.tb_message.updater IS '更新者';
@@ -42,13 +44,13 @@ CREATE TABLE message.tb_message_range (
message_id VARCHAR(50) NOT NULL, -- 消息ID message_id VARCHAR(50) NOT NULL, -- 消息ID
target_type VARCHAR(20) NOT NULL, -- 目标类型user/dept/role/all target_type VARCHAR(20) NOT NULL, -- 目标类型user/dept/role/all
target_id VARCHAR(50) DEFAULT NULL, -- 目标ID用户、部门、角色ID等all类型时为空 target_id VARCHAR(50) DEFAULT NULL, -- 目标ID用户、部门、角色ID等all类型时为空
channel VARCHAR(20) NOT NULL DEFAULT 'app', -- 发送渠道app/sms/email/wechat等 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/ dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径支持like递归如/1/2/3/
creator VARCHAR(50) NOT NULL DEFAULT 'system',-- 创建者 creator VARCHAR(50) NOT NULL DEFAULT 'system',-- 创建者
updater VARCHAR(50) DEFAULT NULL, -- 更新者 updater VARCHAR(50) DEFAULT NULL, -- 更新者
create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间 create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
update_time timestamptz DEFAULT NULL, -- 更新时间 update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间
delete_time timestamptz DEFAULT NULL, -- 删除时间 delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间
deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
PRIMARY KEY (optsn), PRIMARY KEY (optsn),
UNIQUE (message_id, target_type, target_id, channel) UNIQUE (message_id, target_type, target_id, channel)
@@ -59,7 +61,7 @@ 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.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_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.target_id IS '目标ID用户、部门、角色ID等all类型时为空';
COMMENT ON COLUMN message.tb_message_range.channel IS '发送渠道app/sms/email/wechat等'; 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.dept_path IS '部门全路径';
COMMENT ON COLUMN message.tb_message_range.creator 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.updater IS '更新者';
@@ -77,14 +79,14 @@ CREATE TABLE message.tb_message_receiver (
user_id VARCHAR(50) NOT NULL, -- 用户ID user_id VARCHAR(50) NOT NULL, -- 用户ID
channel VARCHAR(20) DEFAULT 'app', -- 接收渠道app/sms/email/wechat等 channel VARCHAR(20) DEFAULT 'app', -- 接收渠道app/sms/email/wechat等
status VARCHAR(20) NOT NULL DEFAULT 'unread', -- 消息状态unread-未读/read-已读/handled-已处理/deleted-已删除 status VARCHAR(20) NOT NULL DEFAULT 'unread', -- 消息状态unread-未读/read-已读/handled-已处理/deleted-已删除
read_time timestamptz DEFAULT NULL, -- 阅读时间 read_time TIMESTAMPTZ DEFAULT NULL, -- 阅读时间
handle_time timestamptz DEFAULT NULL, -- 处理时间 handle_time TIMESTAMPTZ DEFAULT NULL, -- 处理时间
dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径(数据隔离) dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径(数据隔离)
creator VARCHAR(50) NOT NULL DEFAULT 'system',-- 创建者 creator VARCHAR(50) NOT NULL DEFAULT 'system',-- 创建者
updater VARCHAR(50) DEFAULT NULL, -- 更新者 updater VARCHAR(50) DEFAULT NULL, -- 更新者
create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间(接收时间) create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间(接收时间)
update_time timestamptz DEFAULT NULL, -- 更新时间 update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间
delete_time timestamptz DEFAULT NULL, -- 删除时间 delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间
deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
PRIMARY KEY (optsn), PRIMARY KEY (optsn),
UNIQUE (message_id, user_id, channel) UNIQUE (message_id, user_id, channel)
@@ -119,15 +121,15 @@ CREATE TABLE message.tb_message_channel (
channel_code VARCHAR(20) NOT NULL, -- 渠道编码app/sms/email/wechat/dingtalk等 channel_code VARCHAR(20) NOT NULL, -- 渠道编码app/sms/email/wechat/dingtalk等
channel_name VARCHAR(100) NOT NULL, -- 渠道名称 channel_name VARCHAR(100) NOT NULL, -- 渠道名称
channel_desc VARCHAR(255) DEFAULT NULL, -- 渠道描述 channel_desc VARCHAR(255) DEFAULT NULL, -- 渠道描述
config TEXT DEFAULT NULL, -- 渠道配置(JSON格式如API密钥、服务器地址等 config JSON DEFAULT NULL, -- 渠道配置如API密钥、服务器地址等
status VARCHAR(20) NOT NULL DEFAULT 'enabled', -- 渠道状态enabled-启用/disabled-禁用/maintenance-维护中 status VARCHAR(20) NOT NULL DEFAULT 'enabled', -- 渠道状态enabled-启用/disabled-禁用/maintenance-维护中
priority INTEGER DEFAULT 0, -- 优先级(数字越大优先级越高) priority INTEGER DEFAULT 0, -- 优先级(数字越大优先级越高)
dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径(数据隔离) dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径(数据隔离)
creator VARCHAR(50) NOT NULL DEFAULT 'system',-- 创建者 creator VARCHAR(50) NOT NULL DEFAULT 'system',-- 创建者
updater VARCHAR(50) DEFAULT NULL, -- 更新者 updater VARCHAR(50) DEFAULT NULL, -- 更新者
create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间 create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
update_time timestamptz DEFAULT NULL, -- 更新时间 update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间
delete_time timestamptz DEFAULT NULL, -- 删除时间 delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间
deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
PRIMARY KEY (channel_id), PRIMARY KEY (channel_id),
UNIQUE (optsn), UNIQUE (optsn),
@@ -151,11 +153,48 @@ 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.delete_time IS '删除时间';
COMMENT ON COLUMN message.tb_message_channel.deleted IS '是否删除'; COMMENT ON COLUMN message.tb_message_channel.deleted IS '是否删除';
-- 插入默认渠道配置 -- =============================
INSERT INTO message.tb_message_channel (optsn, channel_id, channel_code, channel_name, channel_desc, status, priority) -- 消息模板表
VALUES -- =============================
('CHANNEL_APP_001', 'CH_APP', 'app', '应用内消息', '系统内部消息推送', 'enabled', 100), DROP TABLE IF EXISTS message.tb_message_template CASCADE;
('CHANNEL_SMS_001', 'CH_SMS', 'sms', '短信通知', '手机短信推送', 'disabled', 80), CREATE TABLE message.tb_message_template (
('CHANNEL_EMAIL_001', 'CH_EMAIL', 'email', '邮件通知', '电子邮件推送', 'disabled', 60), optsn VARCHAR(50) NOT NULL,
('CHANNEL_WECHAT_001', 'CH_WECHAT', 'wechat', '微信通知', '微信公众号/企业微信推送', 'disabled', 70), template_id VARCHAR(50) NOT NULL,
('CHANNEL_DINGTALK_001', 'CH_DINGTALK', 'dingtalk', '钉钉通知', '钉钉工作通知推送', 'disabled', 70); 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_type 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_type 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

@@ -20,10 +20,10 @@ CREATE TABLE sys.tb_sys_dept (
description VARCHAR(255) DEFAULT NULL, -- 部门描述 description VARCHAR(255) DEFAULT NULL, -- 部门描述
creator VARCHAR(50) DEFAULT NULL, -- 创建者 creator VARCHAR(50) DEFAULT NULL, -- 创建者
updater VARCHAR(50) DEFAULT NULL, -- 更新者 updater VARCHAR(50) DEFAULT NULL, -- 更新者
create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间 create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
update_time timestamptz DEFAULT NULL, -- 更新时间(由触发器维护) update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间(由触发器维护)
delete_time timestamptz DEFAULT NULL, -- 删除时间 delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间
deleted boolean NOT NULL DEFAULT false, -- 是否删除 deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
PRIMARY KEY (dept_id), PRIMARY KEY (dept_id),
UNIQUE (optsn) UNIQUE (optsn)
); );
@@ -54,12 +54,12 @@ CREATE TABLE sys.tb_sys_role (
owner_dept_id VARCHAR(50) DEFAULT NULL, -- 当scope=dept时所属部门ID owner_dept_id VARCHAR(50) DEFAULT NULL, -- 当scope=dept时所属部门ID
creator VARCHAR(50) DEFAULT NULL, -- 创建者 creator VARCHAR(50) DEFAULT NULL, -- 创建者
dept_path VARCHAR(255) DEFAULT NULL, dept_path VARCHAR(255) DEFAULT NULL,
status boolean NOT NULL DEFAULT false, -- 部门全路径 status BOOLEAN NOT NULL DEFAULT false, -- 部门全路径
updater VARCHAR(50) DEFAULT NULL, -- 更新者 updater VARCHAR(50) DEFAULT NULL, -- 更新者
create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间 create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
update_time timestamptz DEFAULT NULL, -- 更新时间(由触发器维护) update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间(由触发器维护)
delete_time timestamptz DEFAULT NULL, -- 删除时间 delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间
deleted boolean NOT NULL DEFAULT false, -- 是否删除 deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
PRIMARY KEY (role_id), PRIMARY KEY (role_id),
UNIQUE (optsn) UNIQUE (optsn)
); );
@@ -99,10 +99,10 @@ CREATE TABLE sys.tb_sys_dept_role (
dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径 dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
creator VARCHAR(50) DEFAULT NULL, -- 创建者 creator VARCHAR(50) DEFAULT NULL, -- 创建者
updater VARCHAR(50) DEFAULT NULL, -- 更新者 updater VARCHAR(50) DEFAULT NULL, -- 更新者
create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间 create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
update_time timestamptz DEFAULT NULL, -- 更新时间(由触发器维护) update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间(由触发器维护)
delete_time timestamptz DEFAULT NULL, -- 删除时间 delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间
deleted boolean NOT NULL DEFAULT false, -- 是否删除 deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
PRIMARY KEY (dept_id, role_id), PRIMARY KEY (dept_id, role_id),
UNIQUE (optsn) UNIQUE (optsn)
); );
@@ -124,13 +124,14 @@ CREATE TABLE sys.tb_sys_user_role (
optsn VARCHAR(50) NOT NULL, -- 流水号 optsn VARCHAR(50) NOT NULL, -- 流水号
user_id VARCHAR(50) NOT NULL, -- 用户ID user_id VARCHAR(50) NOT NULL, -- 用户ID
role_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, -- 部门全路径 dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
creator VARCHAR(50) DEFAULT NULL, -- 创建者 creator VARCHAR(50) DEFAULT NULL, -- 创建者
updater VARCHAR(50) DEFAULT NULL, -- 更新者 updater VARCHAR(50) DEFAULT NULL, -- 更新者
create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间 create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
update_time timestamptz DEFAULT NULL, -- 更新时间(由触发器维护) update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间(由触发器维护)
delete_time timestamptz DEFAULT NULL, -- 删除时间 delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间
deleted boolean NOT NULL DEFAULT false, -- 是否删除 deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
PRIMARY KEY (user_id, role_id), PRIMARY KEY (user_id, role_id),
UNIQUE (optsn) UNIQUE (optsn)
); );
@@ -138,6 +139,7 @@ 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.optsn IS '流水号';
COMMENT ON COLUMN sys.tb_sys_user_role.user_id IS '用户ID'; 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.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.dept_path IS '部门全路径';
COMMENT ON COLUMN sys.tb_sys_user_role.creator 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.updater IS '更新者';
@@ -156,17 +158,17 @@ CREATE TABLE sys.tb_sys_view (
url VARCHAR(255) DEFAULT NULL, -- 视图URL url VARCHAR(255) DEFAULT NULL, -- 视图URL
component VARCHAR(255) DEFAULT NULL, -- 视图组件 component VARCHAR(255) DEFAULT NULL, -- 视图组件
icon VARCHAR(100) DEFAULT NULL, -- 视图图标 icon VARCHAR(100) DEFAULT NULL, -- 视图图标
type integer DEFAULT 0, -- 视图类型 type INTEGER DEFAULT 0, -- 视图类型
layout VARCHAR(100) DEFAULT NULL, -- 布局组件路径名称 layout VARCHAR(100) DEFAULT NULL, -- 布局组件路径名称
order_num integer DEFAULT 0, -- 视图排序号 order_num INTEGER DEFAULT 0, -- 视图排序号
dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径 dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
description VARCHAR(255) DEFAULT NULL, -- 视图描述 description VARCHAR(255) DEFAULT NULL, -- 视图描述
creator VARCHAR(50) DEFAULT NULL, -- 创建者 creator VARCHAR(50) DEFAULT NULL, -- 创建者
updater VARCHAR(50) DEFAULT NULL, -- 更新者 updater VARCHAR(50) DEFAULT NULL, -- 更新者
create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间 create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
update_time timestamptz DEFAULT NULL, -- 更新时间(由触发器维护) update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间(由触发器维护)
delete_time timestamptz DEFAULT NULL, -- 删除时间 delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间
deleted boolean NOT NULL DEFAULT false, -- 是否删除 deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
PRIMARY KEY (view_id), PRIMARY KEY (view_id),
UNIQUE (optsn) UNIQUE (optsn)
); );
@@ -201,10 +203,10 @@ CREATE TABLE sys.tb_sys_module (
creator VARCHAR(50) DEFAULT NULL, -- 创建者 creator VARCHAR(50) DEFAULT NULL, -- 创建者
dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径 dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
updater VARCHAR(50) DEFAULT NULL, -- 更新者 updater VARCHAR(50) DEFAULT NULL, -- 更新者
create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间 create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
update_time timestamptz DEFAULT NULL, -- 更新时间(由触发器维护) update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间(由触发器维护)
delete_time timestamptz DEFAULT NULL, -- 删除时间 delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间
deleted boolean NOT NULL DEFAULT false, -- 是否删除 deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
PRIMARY KEY (module_id), PRIMARY KEY (module_id),
UNIQUE (optsn) UNIQUE (optsn)
); );
@@ -232,12 +234,12 @@ CREATE TABLE sys.tb_sys_permission (
module_id VARCHAR(50) DEFAULT NULL, -- 所属模块ID module_id VARCHAR(50) DEFAULT NULL, -- 所属模块ID
creator VARCHAR(50) DEFAULT NULL, -- 创建者 creator VARCHAR(50) DEFAULT NULL, -- 创建者
dept_path VARCHAR(255) DEFAULT NULL, dept_path VARCHAR(255) DEFAULT NULL,
status boolean NOT NULL DEFAULT false, -- 部门全路径 status BOOLEAN NOT NULL DEFAULT false, -- 部门全路径
updater VARCHAR(50) DEFAULT NULL, -- 更新者 updater VARCHAR(50) DEFAULT NULL, -- 更新者
create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间 create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
update_time timestamptz DEFAULT NULL, -- 更新时间(由触发器维护) update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间(由触发器维护)
delete_time timestamptz DEFAULT NULL, -- 删除时间 delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间
deleted boolean NOT NULL DEFAULT false, -- 是否删除 deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
PRIMARY KEY (permission_id), PRIMARY KEY (permission_id),
UNIQUE (optsn) UNIQUE (optsn)
); );
@@ -266,10 +268,10 @@ CREATE TABLE sys.tb_sys_role_permission (
creator VARCHAR(50) DEFAULT NULL, -- 创建者 creator VARCHAR(50) DEFAULT NULL, -- 创建者
dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径 dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
updater VARCHAR(50) DEFAULT NULL, -- 更新者 updater VARCHAR(50) DEFAULT NULL, -- 更新者
create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间 create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
update_time timestamptz DEFAULT NULL, -- 更新时间(由触发器维护) update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间(由触发器维护)
delete_time timestamptz DEFAULT NULL, -- 删除时间 delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间
deleted boolean NOT NULL DEFAULT false, -- 是否删除 deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
PRIMARY KEY (role_id, permission_id), PRIMARY KEY (role_id, permission_id),
UNIQUE (optsn) UNIQUE (optsn)
); );
@@ -294,10 +296,10 @@ CREATE TABLE sys.tb_sys_view_permission (
creator VARCHAR(50) DEFAULT NULL, -- 创建者 creator VARCHAR(50) DEFAULT NULL, -- 创建者
dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径 dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
updater VARCHAR(50) DEFAULT NULL, -- 更新者 updater VARCHAR(50) DEFAULT NULL, -- 更新者
create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间 create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
update_time timestamptz DEFAULT NULL, -- 更新时间(由触发器维护) update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间(由触发器维护)
delete_time timestamptz DEFAULT NULL, -- 删除时间 delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间
deleted boolean NOT NULL DEFAULT false, -- 是否删除 deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
PRIMARY KEY (view_id, permission_id), PRIMARY KEY (view_id, permission_id),
UNIQUE (optsn) UNIQUE (optsn)
); );
@@ -393,10 +395,10 @@ CREATE TABLE sys.tb_sys_acl (
dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径(数据隔离) dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径(数据隔离)
creator VARCHAR(50) DEFAULT NULL, -- 创建者 creator VARCHAR(50) DEFAULT NULL, -- 创建者
updater VARCHAR(50) DEFAULT NULL, -- 更新者 updater VARCHAR(50) DEFAULT NULL, -- 更新者
create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间 create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
update_time timestamptz DEFAULT NULL, -- 更新时间 update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间
delete_time timestamptz DEFAULT NULL, -- 删除时间 delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间
deleted boolean NOT NULL DEFAULT false, -- 是否删除 deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
PRIMARY KEY (acl_id), PRIMARY KEY (acl_id),
UNIQUE (object_type, object_id, principal_type, principal_id, principal_dept_id, deleted), UNIQUE (object_type, object_id, principal_type, principal_id, principal_dept_id, deleted),
UNIQUE (optsn) UNIQUE (optsn)
@@ -442,10 +444,10 @@ CREATE TABLE sys.tb_sys_acl_policy (
apply_to_children BOOLEAN DEFAULT true, -- 是否默认应用到子级 apply_to_children BOOLEAN DEFAULT true, -- 是否默认应用到子级
creator VARCHAR(50) DEFAULT NULL, creator VARCHAR(50) DEFAULT NULL,
updater VARCHAR(50) DEFAULT NULL, updater VARCHAR(50) DEFAULT NULL,
create_time timestamptz NOT NULL DEFAULT now(), create_time TIMESTAMPTZ NOT NULL DEFAULT now(),
update_time timestamptz DEFAULT NULL, update_time TIMESTAMPTZ DEFAULT NULL,
delete_time timestamptz DEFAULT NULL, delete_time TIMESTAMPTZ DEFAULT NULL,
deleted boolean NOT NULL DEFAULT false, deleted BOOLEAN NOT NULL DEFAULT false,
PRIMARY KEY (policy_id), PRIMARY KEY (policy_id),
UNIQUE (object_type, deleted), UNIQUE (object_type, deleted),
UNIQUE (optsn) UNIQUE (optsn)

View File

@@ -5,22 +5,24 @@ CREATE SCHEMA IF NOT EXISTS sys;
-- 用户表 -- 用户表
DROP TABLE IF EXISTS sys.tb_sys_user CASCADE; DROP TABLE IF EXISTS sys.tb_sys_user CASCADE;
CREATE TABLE sys.tb_sys_user ( CREATE TABLE sys.tb_sys_user (
optsn varchar(50) NOT NULL, -- 流水号 optsn VARCHAR(50) NOT NULL, -- 流水号
user_id varchar(50) NOT NULL, -- 用户ID user_id VARCHAR(50) NOT NULL, -- 用户ID
password varchar(128) NOT NULL, -- 密码(建议存储 bcrypt/argon2 哈希) usercode VARCHAR(100) DEFAULT NULL, -- 用户code。sso同步数据获取
email varchar(100), -- 电子邮件 password VARCHAR(128) NOT NULL, -- 密码(建议存储 bcrypt/argon2 哈希)
phone varchar(20), -- 电话号码 email VARCHAR(100), -- 电子邮件
wechat_id varchar(50), -- 微信ID phone VARCHAR(20), -- 电话号码
create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间(使用带时区时间) wechat_id VARCHAR(50), -- 微信ID
update_time timestamptz DEFAULT NULL, -- 更新时间(由触发器维护 create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间(使用带时区时间
delete_time timestamptz DEFAULT NULL, -- 删除时间 update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间(由触发器维护)
deleted boolean NOT NULL DEFAULT false, -- 是否删除(使用 boolean delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间
status integer NOT NULL DEFAULT 1, -- 状态 deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除(使用 BOOLEAN
status INTEGER NOT NULL DEFAULT 1, -- 状态
PRIMARY KEY (user_id), PRIMARY KEY (user_id),
UNIQUE (optsn), UNIQUE (optsn),
UNIQUE (email), UNIQUE (email),
UNIQUE (phone), UNIQUE (phone),
UNIQUE (wechat_id) UNIQUE (wechat_id),
UNIQUE (usercode)
); );
CREATE INDEX idx_tb_sys_user_phone ON sys.tb_sys_user USING btree (phone); CREATE INDEX idx_tb_sys_user_phone ON sys.tb_sys_user USING btree (phone);
@@ -48,20 +50,19 @@ COMMENT ON COLUMN sys.tb_sys_user.status IS '状态';
-- 用户信息表 -- 用户信息表
DROP TABLE IF EXISTS sys.tb_sys_user_info CASCADE; DROP TABLE IF EXISTS sys.tb_sys_user_info CASCADE;
CREATE TABLE sys.tb_sys_user_info ( CREATE TABLE sys.tb_sys_user_info (
optsn varchar(50) NOT NULL, -- 流水号 optsn VARCHAR(50) NOT NULL, -- 流水号
user_id varchar(50) NOT NULL, -- 用户ID user_id VARCHAR(50) NOT NULL, -- 用户ID
avatar varchar(255), -- 头像 avatar VARCHAR(255), -- 头像
gender integer DEFAULT 0, -- 性别 gender INTEGER DEFAULT 0, -- 性别
family_name varchar(50), -- username VARCHAR(100) NOT NULL, -- 用户名
given_name varchar(50), -- level INTEGER DEFAULT 1, -- 等级
full_name varchar(100), -- 全名 id_card VARCHAR(50), -- 身份证号
level integer DEFAULT 1, -- 等级 address VARCHAR(255), -- 地址
id_card varchar(50), -- 身份证号 remark VARCHAR(500) DEFAULT NULL, -- 备注
address varchar(255), -- 地址 create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间 update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间(触发器维护)
update_time timestamptz DEFAULT NULL, -- 更新时间(触发器维护) delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间
delete_time timestamptz DEFAULT NULL, -- 删除时间 deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
deleted boolean NOT NULL DEFAULT false, -- 是否删除
PRIMARY KEY (user_id), PRIMARY KEY (user_id),
UNIQUE (optsn) UNIQUE (optsn)
); );
@@ -70,12 +71,11 @@ 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.user_id IS '用户ID';
COMMENT ON COLUMN sys.tb_sys_user_info.avatar IS '头像'; 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.gender IS '性别';
COMMENT ON COLUMN sys.tb_sys_user_info.family_name IS ''; COMMENT ON COLUMN sys.tb_sys_user_info.username IS '用户名';
COMMENT ON COLUMN sys.tb_sys_user_info.given_name IS '';
COMMENT ON COLUMN sys.tb_sys_user_info.full_name IS '全名';
COMMENT ON COLUMN sys.tb_sys_user_info.level 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.id_card IS '身份证号';
COMMENT ON COLUMN sys.tb_sys_user_info.address 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.create_time IS '创建时间';
COMMENT ON COLUMN sys.tb_sys_user_info.update_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.delete_time IS '删除时间';
@@ -84,19 +84,19 @@ COMMENT ON COLUMN sys.tb_sys_user_info.deleted IS '是否删除';
-- 登录日志表 -- 登录日志表
DROP TABLE IF EXISTS sys.tb_sys_login_log CASCADE; DROP TABLE IF EXISTS sys.tb_sys_login_log CASCADE;
CREATE TABLE sys.tb_sys_login_log ( CREATE TABLE sys.tb_sys_login_log (
optsn varchar(50) NOT NULL, -- 流水号(作为主键) optsn VARCHAR(50) NOT NULL, -- 流水号(作为主键)
user_id varchar(50) NOT NULL, -- 用户ID user_id VARCHAR(50) NOT NULL, -- 用户ID
username varchar(50) NOT NULL, -- 用户名 username VARCHAR(50) NOT NULL, -- 用户名
ip_address varchar(45), -- IP地址 ip_address VARCHAR(45), -- IP地址
ip_source varchar(100), -- IP来源 ip_source VARCHAR(100), -- IP来源
browser varchar(100), -- 浏览器 browser VARCHAR(100), -- 浏览器
os varchar(100), -- 操作系统 os VARCHAR(100), -- 操作系统
password varchar(128), -- 密码(建议存储 bcrypt/argon2 哈希) password VARCHAR(128), -- 密码(建议存储 bcrypt/argon2 哈希)
login_time timestamptz DEFAULT now(), -- 登录时间 login_time TIMESTAMPTZ DEFAULT now(), -- 登录时间
status integer DEFAULT 1, -- 登录状态0失败 1成功 status INTEGER DEFAULT 1, -- 登录状态0失败 1成功
error_count integer DEFAULT 0, -- 错误次数 error_count INTEGER DEFAULT 0, -- 错误次数
message varchar(255), -- 登录消息 message VARCHAR(255), -- 登录消息
create_time timestamptz NOT NULL DEFAULT now(), -- 创建时间 create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
PRIMARY KEY (optsn) PRIMARY KEY (optsn)
); );
-- B-tree 索引(显式指定 USING btreePostgres 默认即为 btree -- B-tree 索引(显式指定 USING btreePostgres 默认即为 btree

View File

@@ -1,11 +1,55 @@
-- 按顺序执行初始化 -- =============================
\i createDB.sql -- 创建数据库和基础扩展 -- 城市生命线AI数智化平台 - 数据库初始化脚本
\i createTableUser.sql -- 用户表相关(核心表) -- 按顺序执行各模块建表SQL及数据初始化
\i createTablePermission.sql -- 权限相关(核心表) -- =============================
\i createTableFile.sql -- 文件管理
\i createTableMessage.sql -- 消息管理
\i createTableConfig.sql -- 配置管理
\i createTableLog.sql -- 日志管理
-- 初始化基础数据(如果有) -- =============================
-- \i initBaseData.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. 知识库管理模块
\i createTableKnowledge.sql
-- 7. 招投标业务模块
\i createTableBidding.sql
-- 8. 智能客服业务模块
\i createTableCustomerService.sql
-- 9. 智能体模块(暂不启用)
-- \i createTableAgent.sql
-- =============================
-- 第二阶段:初始化基础数据
-- =============================
-- 1. 初始化权限相关基础数据(部门、角色、权限、视图、模块)
\i initDataPermission.sql
-- 2. 初始化用户数据(管理员账户)
\i initDataUser.sql
-- 3. 初始化消息渠道配置
\i initDataMessage.sql
-- 4. 初始化系统配置
\i initDataConfig.sql
-- 注意:文件、日志、知识库、招投标、客服等业务表无需初始化数据

View File

@@ -2,42 +2,43 @@
-- 仅插入常用示例,可按需调整 value/remark -- 仅插入常用示例,可按需调整 value/remark
INSERT INTO config.tb_sys_config ( INSERT INTO config.tb_sys_config (
optsn, config_id, key, value, type, description, "group", "order", status, remark, 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 creator, dept_path, updater, create_time, update_time, delete_time, deleted
) VALUES ) VALUES
-- 站点与品牌 -- 站点与品牌
('CFG-0001', 'cfg_site_name', 'site.name', 'urban-lifeline 平台', 'string', '站点名称', 'site', 10, 'enabled', '展示在标题/登录/页脚', 'system', NULL, NULL, now(), NULL, NULL, false), ('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', '/static/logo.png', 'string', '站点Logo地址', 'site', 20, 'enabled', '相对或绝对URL', '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', '', 'string', 'ICP备案号', 'site', 30, 'enabled', '页脚展示', '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', '默认语言', 'i18n', 10, 'enabled', '如 zh-CN/en-US', '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', '系统默认时区', 'i18n', 20, 'enabled', 'IANA时区名', '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}', 'json', '密码策略', 'security', 10, 'enabled', 'JSON结构', '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','86400', 'number', 'JWT过期秒数', 'security', 20, 'enabled', '默认24小时', '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', 'number', '会话超时(分钟)', 'security', 30, 'enabled', '空闲登出', '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', 'bool', '是否开放注册', 'security', 40, 'enabled', '生产建议关闭', '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', 'number', '单文件最大上传(MB)', 'storage', 10, 'enabled', '前后端需一致校验', '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', 'local', 'string', '存储后端(local/minio/s3)', 'storage', 20, 'enabled', '本地/MinIO/S3等', 'system', NULL, NULL, now(), NULL, NULL, false), ('CFG-0302', 'cfg_storage_backend', 'storage.backend', '存储后端', 'local', 'String', 'select', '存储后端类型', NULL, '["local", "minio", "s3"]'::json, 'storage', 'mod_file', 20, 0, '本地/MinIO/S3等', 'system', NULL, NULL, now(), NULL, NULL, false),
('CFG-0303', 'cfg_storage_base', 'storage.basePath', '/data/urban-lifeline', 'string', '本地存储基路径', 'storage', 30, 'enabled', '当 backend=local', '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),
-- 通知(邮件/SMS -- 通知(邮件/SMS
('CFG-0401', 'cfg_mail_host', 'mail.smtp.host', '', 'string', 'SMTP主机', 'notify', 10, 'disabled','留空为未配置', 'system', NULL, NULL, now(), NULL, NULL, false), ('CFG-0401', 'cfg_mail_host', 'mail.smtp.host', 'SMTP主机', '', 'String', 'input', 'SMTP主机', NULL, NULL, 'notify', 'mod_message', 10, 1, '留空为未配置', 'system', NULL, NULL, now(), NULL, NULL, false),
('CFG-0402', 'cfg_mail_port', 'mail.smtp.port', '465', 'number', 'SMTP端口', 'notify', 20, 'disabled','SSL常用465', 'system', NULL, NULL, now(), NULL, NULL, false), ('CFG-0402', 'cfg_mail_port', 'mail.smtp.port', 'SMTP端口', '465', 'INTEGER', 'input', 'SMTP端口', NULL, NULL, 'notify', 'mod_message', 20, 1, 'SSL常用465', 'system', NULL, NULL, now(), NULL, NULL, false),
('CFG-0403', 'cfg_mail_from', 'mail.from', '', 'string', '发件人邮箱', 'notify', 30, 'disabled','如 no-reply@x.com', 'system', NULL, NULL, now(), NULL, NULL, false), ('CFG-0403', 'cfg_mail_from', 'mail.from', '发件人邮箱', '', 'String', 'input', '发件人邮箱', NULL, NULL, 'notify', 'mod_message', 30, 1, '如 no-reply@x.com', 'system', NULL, NULL, now(), NULL, NULL, false),
('CFG-0411', 'cfg_sms_provider', 'sms.provider', '', 'string', '短信服务商', 'notify', 40, 'disabled','如 aliyun/tencent', 'system', NULL, NULL, now(), NULL, NULL, false), ('CFG-0411', 'cfg_sms_provider', 'sms.provider', '短信服务商', '', 'String', 'select', '短信服务商', NULL, '["aliyun", "tencent"]'::json, 'notify', 'mod_message', 40, 1, '如 aliyun/tencent', 'system', NULL, NULL, now(), NULL, NULL, false),
-- 日志与审计 -- 日志与审计
('CFG-0501', 'cfg_log_level', 'log.level', 'INFO', 'string', '系统日志级别', 'log', 10, 'enabled', 'DEBUG/INFO/WARN/ERROR', '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', 'number', '审计日志保留天数', 'log', 20, 'enabled', '合规按需调整', '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', 'bool', '维护模式开关', 'platform', 10, 'enabled', 'true时仅管理员可用', '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', 'enabled', 'string', 'ACL策略开关', 'platform', 20, 'enabled', 'enabled/disabled', '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);

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_type,
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,286 @@
-- 初始化权限相关基础数据(与 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-0005', 'module_knowledge', '知识库', '知识文档管理',
'system', NULL, now(), false),
('MODULE-0006', 'module_bidding', '招投标', '招投标业务管理',
'system', NULL, now(), false),
('MODULE-0007', 'module_customer_service', '智能客服', '客服工单管理',
'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-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);
-- =============================
-- 5. 初始化视图(菜单)
-- =============================
INSERT INTO sys.tb_sys_view (
optsn, view_id, name, parent_id, url, component, icon, type,
layout, order_num, description, creator, create_time, deleted
) VALUES
-- 一级菜单
('VIEW-0001', 'view_system', '系统管理', NULL, '/system', NULL, 'Settings', 0,
'MainLayout', 100, '系统管理菜单', 'system', now(), false),
('VIEW-0002', 'view_business', '业务管理', NULL, '/business', NULL, 'Briefcase', 0,
'MainLayout', 200, '业务管理菜单', 'system', now(), false),
-- 系统管理子菜单
('VIEW-0101', 'view_user', '用户管理', 'view_system', '/system/user', 'system/UserList', 'Users', 1,
'MainLayout', 10, '用户管理页面', 'system', now(), false),
('VIEW-0102', 'view_role', '角色管理', 'view_system', '/system/role', 'system/RoleList', 'Shield', 1,
'MainLayout', 20, '角色管理页面', 'system', now(), false),
('VIEW-0103', 'view_dept', '部门管理', 'view_system', '/system/dept', 'system/DeptList', 'Building', 1,
'MainLayout', 30, '部门管理页面', 'system', now(), false),
('VIEW-0104', 'view_permission', '权限管理', 'view_system', '/system/permission', 'system/PermissionList', 'Lock', 1,
'MainLayout', 40, '权限管理页面', 'system', now(), false),
('VIEW-0105', 'view_config', '配置管理', 'view_system', '/system/config', 'system/ConfigList', 'Settings', 1,
'MainLayout', 50, '配置管理页面', 'system', now(), false),
('VIEW-0106', 'view_file', '文件管理', 'view_system', '/system/file', 'system/FileList', 'FileText', 1,
'MainLayout', 60, '文件管理页面', 'system', now(), false),
('VIEW-0107', 'view_message', '消息管理', 'view_system', '/system/message', 'system/MessageList', 'Mail', 1,
'MainLayout', 70, '消息管理页面', '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');
-- 普通用户权限(基础查看和文件操作)
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_user_view', 'system', NULL, now(), false),
('RP-U-0002', 'role_user', 'perm_file_view', 'system', NULL, now(), false),
('RP-U-0003', 'role_user', 'perm_file_upload', 'system', NULL, now(), false),
('RP-U-0004', 'role_user', 'perm_file_download', 'system', NULL, now(), false),
('RP-U-0005', 'role_user', 'perm_message_view', 'system', NULL, now(), false),
('RP-U-0006', 'role_user', 'perm_config_view', 'system', NULL, now(), false);
-- 访客权限(仅查看)
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_user_view', 'system', NULL, now(), false),
('RP-G-0002', 'role_guest', 'perm_file_view', 'system', NULL, now(), false),
('RP-G-0003', 'role_guest', 'perm_message_view', 'system', NULL, now(), false);
-- =============================
-- 7. 视图权限关联
-- =============================
-- 将视图与对应模块的权限关联
INSERT INTO sys.tb_sys_view_permission (
optsn, view_id, permission_id, creator, dept_path, create_time, deleted
) VALUES
-- 用户管理视图关联用户权限
('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,
create_time, status, deleted
) VALUES
('USER-0001', 'user_admin', 'admin',
'$2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7q0TP/Hza', -- admin123
'admin@urbanlifeline.com', '13800138000',
now(), 1, 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,
create_time, status, deleted
) VALUES
('USER-0002', 'user_demo', 'demo',
'$2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7q0TP/Hza', -- admin123
'demo@urbanlifeline.com', '13800138001',
now(), 1, 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

@@ -1,549 +0,0 @@
-- =============================
-- 数据库优化补丁脚本
-- 基于现有表结构的增强和修改
-- =============================
-- =============================
-- 1. 用户模块优化
-- =============================
-- 1.1 移除登录日志表中的敏感密码字段
DO $$
BEGIN
IF EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_schema = 'sys'
AND table_name = 'tb_sys_login_log'
AND column_name = 'password'
) THEN
ALTER TABLE sys.tb_sys_login_log DROP COLUMN password;
RAISE NOTICE '已移除登录日志表的密码字段';
END IF;
END $$;
-- 1.2 创建用户部门关联表
CREATE TABLE IF NOT EXISTS sys.tb_sys_user_dept (
optsn VARCHAR(50) NOT NULL,
user_id VARCHAR(50) NOT NULL,
dept_id VARCHAR(50) NOT NULL,
is_primary BOOLEAN DEFAULT false,
position VARCHAR(100),
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, dept_id),
UNIQUE (optsn),
FOREIGN KEY (user_id) REFERENCES sys.tb_sys_user(user_id),
FOREIGN KEY (dept_id) REFERENCES sys.tb_sys_dept(dept_id)
);
COMMENT ON TABLE sys.tb_sys_user_dept IS '用户部门关联表';
COMMENT ON COLUMN sys.tb_sys_user_dept.is_primary IS '是否主部门';
COMMENT ON COLUMN sys.tb_sys_user_dept.position IS '职位';
CREATE INDEX IF NOT EXISTS idx_user_dept_user ON sys.tb_sys_user_dept(user_id) WHERE deleted = false;
CREATE INDEX IF NOT EXISTS idx_user_dept_dept ON sys.tb_sys_user_dept(dept_id) WHERE deleted = false;
-- 1.3 用户表添加主部门字段
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_schema = 'sys'
AND table_name = 'tb_sys_user'
AND column_name = 'primary_dept_id'
) THEN
ALTER TABLE sys.tb_sys_user ADD COLUMN primary_dept_id VARCHAR(50);
COMMENT ON COLUMN sys.tb_sys_user.primary_dept_id IS '主部门ID';
END IF;
END $$;
-- =============================
-- 2. 权限模块优化
-- =============================
-- 2.1 角色表添加排序字段
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_schema = 'sys'
AND table_name = 'tb_sys_role'
AND column_name = 'order_num'
) THEN
ALTER TABLE sys.tb_sys_role ADD COLUMN order_num INTEGER DEFAULT 0;
COMMENT ON COLUMN sys.tb_sys_role.order_num IS '排序号';
END IF;
END $$;
-- 2.2 权限表添加权限类型字段
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_schema = 'sys'
AND table_name = 'tb_sys_permission'
AND column_name = 'permission_type'
) THEN
ALTER TABLE sys.tb_sys_permission ADD COLUMN permission_type VARCHAR(20) DEFAULT 'action';
COMMENT ON COLUMN sys.tb_sys_permission.permission_type IS '权限类型action-操作权限/data-数据权限/menu-菜单权限';
END IF;
END $$;
-- =============================
-- 3. 文件模块扩展
-- =============================
-- 3.1 文件表添加版本管理字段
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_schema = 'file'
AND table_name = 'tb_sys_file'
AND column_name = 'version'
) THEN
ALTER TABLE file.tb_sys_file
ADD COLUMN version VARCHAR(20) DEFAULT '1.0',
ADD COLUMN parent_file_id VARCHAR(50),
ADD COLUMN is_latest BOOLEAN DEFAULT true;
COMMENT ON COLUMN file.tb_sys_file.version IS '文件版本号';
COMMENT ON COLUMN file.tb_sys_file.parent_file_id IS '父文件ID版本链';
COMMENT ON COLUMN file.tb_sys_file.is_latest IS '是否最新版本';
END IF;
END $$;
-- 3.2 文件表添加分类和标签
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_schema = 'file'
AND table_name = 'tb_sys_file'
AND column_name = 'category'
) THEN
ALTER TABLE file.tb_sys_file
ADD COLUMN category VARCHAR(100),
ADD COLUMN tags TEXT[],
ADD COLUMN metadata JSONB;
COMMENT ON COLUMN file.tb_sys_file.category IS '文件分类';
COMMENT ON COLUMN file.tb_sys_file.tags IS '文件标签数组';
COMMENT ON COLUMN file.tb_sys_file.metadata IS '文件元数据';
END IF;
END $$;
-- 3.3 创建文件关联表
CREATE TABLE IF NOT EXISTS file.tb_file_relation (
optsn VARCHAR(50) NOT NULL,
relation_id VARCHAR(50) NOT NULL,
file_id VARCHAR(50) NOT NULL,
object_type VARCHAR(50) NOT NULL,
object_id VARCHAR(50) NOT NULL,
relation_type VARCHAR(30) DEFAULT 'attachment',
order_num INTEGER DEFAULT 0,
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 (relation_id),
UNIQUE (optsn),
FOREIGN KEY (file_id) REFERENCES file.tb_sys_file(file_id)
);
COMMENT ON TABLE file.tb_file_relation IS '文件关联表';
COMMENT ON COLUMN file.tb_file_relation.object_type IS '对象类型bidding_project/ticket/document等';
COMMENT ON COLUMN file.tb_file_relation.object_id IS '对象ID';
COMMENT ON COLUMN file.tb_file_relation.relation_type IS '关联类型attachment-附件/avatar-头像/banner-横幅';
CREATE INDEX IF NOT EXISTS idx_file_relation_object
ON file.tb_file_relation(object_type, object_id) WHERE deleted = false;
CREATE INDEX IF NOT EXISTS idx_file_relation_file
ON file.tb_file_relation(file_id) WHERE deleted = false;
-- =============================
-- 4. 消息模块增强
-- =============================
-- 4.1 创建消息模板表
CREATE TABLE IF NOT EXISTS 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,
title_template TEXT,
content_template TEXT NOT NULL,
variables JSONB,
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.template_type IS '模板类型system-系统/business-业务';
COMMENT ON COLUMN message.tb_message_template.variables IS '模板变量定义';
CREATE INDEX IF NOT EXISTS idx_template_type
ON message.tb_message_template(template_type) WHERE deleted = false;
-- =============================
-- 5. 日志模块优化
-- =============================
-- 5.1 日志表添加trace_id用于链路追踪
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_schema = 'log'
AND table_name = 'tb_sys_log'
AND column_name = 'trace_id'
) THEN
ALTER TABLE log.tb_sys_log
ADD COLUMN trace_id VARCHAR(50),
ADD COLUMN span_id VARCHAR(50),
ADD COLUMN parent_span_id VARCHAR(50);
COMMENT ON COLUMN log.tb_sys_log.trace_id IS '追踪ID用于分布式追踪';
COMMENT ON COLUMN log.tb_sys_log.span_id IS '跨度ID';
COMMENT ON COLUMN log.tb_sys_log.parent_span_id IS '父跨度ID';
CREATE INDEX idx_log_trace ON log.tb_sys_log(trace_id) WHERE trace_id IS NOT NULL;
END IF;
END $$;
-- =============================
-- 6. 全文搜索索引
-- =============================
-- 6.1 为知识文档标题创建全文搜索索引
CREATE INDEX IF NOT EXISTS idx_knowledge_doc_title_trgm
ON knowledge.tb_knowledge_document USING gin(title gin_trgm_ops)
WHERE deleted = false;
-- 6.2 为招标项目名称创建全文搜索索引
CREATE INDEX IF NOT EXISTS idx_project_name_trgm
ON bidding.tb_bidding_project USING gin(project_name gin_trgm_ops)
WHERE deleted = false;
-- 6.3 为客户姓名创建全文搜索索引
CREATE INDEX IF NOT EXISTS idx_customer_name_trgm
ON customer_service.tb_customer USING gin(customer_name gin_trgm_ops)
WHERE deleted = false;
-- =============================
-- 7. JSONB字段优化索引
-- =============================
-- 7.1 智能体配置JSONB索引
CREATE INDEX IF NOT EXISTS idx_agent_model_config_gin
ON agent.tb_agent USING gin(model_config)
WHERE deleted = false;
-- 7.2 工单元数据JSONB索引
CREATE INDEX IF NOT EXISTS idx_ticket_metadata_gin
ON customer_service.tb_ticket USING gin(metadata)
WHERE deleted = false;
-- =============================
-- 8. 性能优化视图
-- =============================
-- 8.1 用户完整权限视图包含ACL
CREATE OR REPLACE VIEW sys.v_user_full_permissions AS
WITH user_roles AS (
-- 用户直接拥有的角色权限
SELECT DISTINCT
ur.user_id,
p.permission_id,
p.code AS permission_code,
p.name AS permission_name,
'role' AS source_type,
r.role_id AS source_id
FROM sys.tb_sys_user_role ur
JOIN sys.tb_sys_role r ON ur.role_id = r.role_id
JOIN sys.tb_sys_role_permission rp ON r.role_id = rp.role_id
JOIN sys.tb_sys_permission p ON rp.permission_id = p.permission_id
WHERE ur.deleted = false
AND r.deleted = false
AND rp.deleted = false
AND p.deleted = false
),
user_acls AS (
-- 用户的ACL权限
SELECT DISTINCT
principal_id AS user_id,
object_type || ':' || object_id AS permission_id,
object_type || '_' ||
CASE
WHEN (permission & 1) = 1 THEN 'read'
WHEN (permission & 2) = 2 THEN 'write'
WHEN (permission & 4) = 4 THEN 'exec'
END AS permission_code,
'ACL permission on ' || object_type AS permission_name,
'acl' AS source_type,
acl_id AS source_id
FROM sys.tb_sys_acl
WHERE principal_type = 'user'
AND allow = true
AND deleted = false
)
SELECT * FROM user_roles
UNION ALL
SELECT * FROM user_acls;
COMMENT ON VIEW sys.v_user_full_permissions IS '用户完整权限视图包含角色权限和ACL权限';
-- 8.2 智能体实时状态视图
CREATE OR REPLACE VIEW agent.v_agent_realtime_status AS
SELECT
a.agent_id,
a.agent_name,
a.agent_type,
a.status,
COUNT(DISTINCT s.session_id) FILTER (WHERE s.session_status = 'active') AS active_sessions,
COUNT(DISTINCT s.user_id) FILTER (WHERE s.start_time > now() - interval '24 hours') AS daily_users,
COALESCE(SUM(s.message_count) FILTER (WHERE s.start_time > now() - interval '1 hour'), 0) AS hourly_messages,
COALESCE(AVG(r.rating) FILTER (WHERE r.create_time > now() - interval '7 days'), 0) AS weekly_avg_rating
FROM agent.tb_agent a
LEFT JOIN agent.tb_agent_session s ON a.agent_id = s.agent_id AND s.deleted = false
LEFT JOIN agent.tb_agent_rating r ON a.agent_id = r.agent_id AND r.deleted = false
WHERE a.deleted = false
GROUP BY a.agent_id, a.agent_name, a.agent_type, a.status;
COMMENT ON VIEW agent.v_agent_realtime_status IS '智能体实时状态视图';
-- 8.3 工单处理效率视图
CREATE OR REPLACE VIEW customer_service.v_ticket_efficiency AS
SELECT
DATE(t.create_time) AS stat_date,
t.ticket_type,
t.priority,
COUNT(*) AS total_tickets,
COUNT(*) FILTER (WHERE t.ticket_status = 'resolved') AS resolved_tickets,
COUNT(*) FILTER (WHERE t.is_overdue) AS overdue_tickets,
AVG(EXTRACT(EPOCH FROM (t.response_time - t.create_time))/60) AS avg_response_minutes,
AVG(EXTRACT(EPOCH FROM (t.resolution_time - t.create_time))/3600) FILTER (WHERE t.resolution_time IS NOT NULL) AS avg_resolution_hours,
AVG(t.customer_rating) FILTER (WHERE t.customer_rating IS NOT NULL) AS avg_rating
FROM customer_service.tb_ticket t
WHERE t.deleted = false
AND t.create_time > now() - interval '90 days'
GROUP BY DATE(t.create_time), t.ticket_type, t.priority;
COMMENT ON VIEW customer_service.v_ticket_efficiency IS '工单处理效率统计视图';
-- =============================
-- 9. 审计触发器增强
-- =============================
-- 9.1 创建审计日志函数
CREATE OR REPLACE FUNCTION public.audit_trigger_func()
RETURNS TRIGGER AS $$
DECLARE
audit_data JSONB;
BEGIN
IF (TG_OP = 'DELETE') THEN
audit_data := jsonb_build_object(
'operation', 'DELETE',
'table', TG_TABLE_SCHEMA || '.' || TG_TABLE_NAME,
'old_data', row_to_json(OLD)
);
INSERT INTO log.tb_sys_log (
optsn, log_id, type, level, module, message, data, creator
) VALUES (
'AUDIT_' || gen_random_uuid()::text,
gen_random_uuid()::text,
'audit',
'info',
TG_TABLE_SCHEMA || '.' || TG_TABLE_NAME,
'Record deleted',
audit_data,
COALESCE(current_setting('app.current_user_id', true), 'system')
);
RETURN OLD;
ELSIF (TG_OP = 'UPDATE') THEN
audit_data := jsonb_build_object(
'operation', 'UPDATE',
'table', TG_TABLE_SCHEMA || '.' || TG_TABLE_NAME,
'old_data', row_to_json(OLD),
'new_data', row_to_json(NEW)
);
INSERT INTO log.tb_sys_log (
optsn, log_id, type, level, module, message, data, creator
) VALUES (
'AUDIT_' || gen_random_uuid()::text,
gen_random_uuid()::text,
'audit',
'info',
TG_TABLE_SCHEMA || '.' || TG_TABLE_NAME,
'Record updated',
audit_data,
COALESCE(current_setting('app.current_user_id', true), 'system')
);
RETURN NEW;
ELSIF (TG_OP = 'INSERT') THEN
audit_data := jsonb_build_object(
'operation', 'INSERT',
'table', TG_TABLE_SCHEMA || '.' || TG_TABLE_NAME,
'new_data', row_to_json(NEW)
);
INSERT INTO log.tb_sys_log (
optsn, log_id, type, level, module, message, data, creator
) VALUES (
'AUDIT_' || gen_random_uuid()::text,
gen_random_uuid()::text,
'audit',
'info',
TG_TABLE_SCHEMA || '.' || TG_TABLE_NAME,
'Record created',
audit_data,
COALESCE(current_setting('app.current_user_id', true), 'system')
);
RETURN NEW;
END IF;
END;
$$ LANGUAGE plpgsql;
COMMENT ON FUNCTION public.audit_trigger_func() IS '审计日志触发器函数';
-- =============================
-- 10. 数据归档函数
-- =============================
-- 10.1 创建日志归档函数
CREATE OR REPLACE FUNCTION public.archive_old_logs(
p_months_to_keep INTEGER DEFAULT 6
)
RETURNS TABLE(
archived_count INTEGER,
deleted_count INTEGER
) AS $$
DECLARE
v_cutoff_date TIMESTAMPTZ;
v_archived INTEGER := 0;
v_deleted INTEGER := 0;
BEGIN
v_cutoff_date := now() - (p_months_to_keep || ' months')::INTERVAL;
-- 归档系统日志
CREATE TABLE IF NOT EXISTS log.tb_sys_log_archived (LIKE log.tb_sys_log INCLUDING ALL);
WITH moved_rows AS (
INSERT INTO log.tb_sys_log_archived
SELECT * FROM log.tb_sys_log
WHERE create_time < v_cutoff_date
RETURNING *
)
SELECT COUNT(*) INTO v_archived FROM moved_rows;
DELETE FROM log.tb_sys_log
WHERE create_time < v_cutoff_date;
GET DIAGNOSTICS v_deleted = ROW_COUNT;
RETURN QUERY SELECT v_archived, v_deleted;
END;
$$ LANGUAGE plpgsql;
COMMENT ON FUNCTION public.archive_old_logs IS '归档旧日志数据';
-- 10.2 创建API调用日志归档函数
CREATE OR REPLACE FUNCTION agent.archive_api_logs(
p_months_to_keep INTEGER DEFAULT 3
)
RETURNS INTEGER AS $$
DECLARE
v_cutoff_date TIMESTAMPTZ;
v_archived INTEGER;
BEGIN
v_cutoff_date := now() - (p_months_to_keep || ' months')::INTERVAL;
CREATE TABLE IF NOT EXISTS agent.tb_api_call_log_archived (LIKE agent.tb_api_call_log INCLUDING ALL);
WITH moved_rows AS (
INSERT INTO agent.tb_api_call_log_archived
SELECT * FROM agent.tb_api_call_log
WHERE create_time < v_cutoff_date
RETURNING *
)
SELECT COUNT(*) INTO v_archived FROM moved_rows;
DELETE FROM agent.tb_api_call_log
WHERE create_time < v_cutoff_date;
RETURN v_archived;
END;
$$ LANGUAGE plpgsql;
COMMENT ON FUNCTION agent.archive_api_logs IS '归档旧API调用日志';
-- =============================
-- 11. 性能监控函数
-- =============================
-- 11.1 表膨胀检查函数
CREATE OR REPLACE FUNCTION public.check_table_bloat()
RETURNS TABLE(
schemaname TEXT,
tablename TEXT,
table_size_mb NUMERIC,
bloat_size_mb NUMERIC,
bloat_ratio NUMERIC
) AS $$
BEGIN
RETURN QUERY
SELECT
s.schemaname::TEXT,
s.tablename::TEXT,
ROUND(pg_total_relation_size(s.schemaname || '.' || s.tablename)::NUMERIC / 1024 / 1024, 2) AS table_size_mb,
ROUND((pg_total_relation_size(s.schemaname || '.' || s.tablename) - pg_relation_size(s.schemaname || '.' || s.tablename))::NUMERIC / 1024 / 1024, 2) AS bloat_size_mb,
ROUND(((pg_total_relation_size(s.schemaname || '.' || s.tablename) - pg_relation_size(s.schemaname || '.' || s.tablename))::NUMERIC / NULLIF(pg_total_relation_size(s.schemaname || '.' || s.tablename), 0) * 100), 2) AS bloat_ratio
FROM pg_tables s
WHERE s.schemaname IN ('sys', 'file', 'message', 'log', 'config', 'knowledge', 'bidding', 'customer_service', 'agent')
ORDER BY pg_total_relation_size(s.schemaname || '.' || s.tablename) DESC;
END;
$$ LANGUAGE plpgsql;
COMMENT ON FUNCTION public.check_table_bloat IS '检查表膨胀情况';
-- =============================
-- 执行完成提示
-- =============================
DO $$
BEGIN
RAISE NOTICE '==============================';
RAISE NOTICE '数据库优化补丁执行完成!';
RAISE NOTICE '==============================';
RAISE NOTICE '已完成以下优化:';
RAISE NOTICE '1. 用户模块:移除敏感字段、添加部门关联';
RAISE NOTICE '2. 权限模块:添加排序和类型字段';
RAISE NOTICE '3. 文件模块:版本管理、分类标签、关联表';
RAISE NOTICE '4. 消息模块:消息模板表';
RAISE NOTICE '5. 日志模块:链路追踪支持';
RAISE NOTICE '6. 全文搜索添加GIN索引';
RAISE NOTICE '7. JSONB优化添加GIN索引';
RAISE NOTICE '8. 性能视图:用户权限、智能体状态、工单效率';
RAISE NOTICE '9. 审计增强:完整审计触发器';
RAISE NOTICE '10. 数据归档日志和API调用归档函数';
RAISE NOTICE '11. 监控工具:表膨胀检查函数';
RAISE NOTICE '==============================';
END $$;

View File

@@ -22,22 +22,22 @@ USE `nacos_config`;
/******************************************/ /******************************************/
CREATE TABLE `config_info` ( CREATE TABLE `config_info` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id', `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
`data_id` varchar(255) NOT NULL COMMENT 'data_id', `data_id` VARCHAR(255) NOT NULL COMMENT 'data_id',
`group_id` varchar(128) DEFAULT NULL COMMENT 'group_id', `group_id` VARCHAR(128) DEFAULT NULL COMMENT 'group_id',
`content` longtext NOT NULL COMMENT 'content', `content` longtext NOT NULL COMMENT 'content',
`md5` varchar(32) DEFAULT NULL COMMENT 'md5', `md5` VARCHAR(32) DEFAULT NULL COMMENT 'md5',
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间', `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
`src_user` text COMMENT 'source user', `src_user` text COMMENT 'source user',
`src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip', `src_ip` VARCHAR(50) DEFAULT NULL COMMENT 'source ip',
`app_name` varchar(128) DEFAULT NULL COMMENT 'app_name', `app_name` VARCHAR(128) DEFAULT NULL COMMENT 'app_name',
`tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段', `tenant_id` VARCHAR(128) DEFAULT '' COMMENT '租户字段',
`c_desc` varchar(256) DEFAULT NULL COMMENT 'configuration description', `c_desc` VARCHAR(256) DEFAULT NULL COMMENT 'configuration description',
`c_use` varchar(64) DEFAULT NULL COMMENT 'configuration usage', `c_use` VARCHAR(64) DEFAULT NULL COMMENT 'configuration usage',
`effect` varchar(64) DEFAULT NULL COMMENT '配置生效的描述', `effect` VARCHAR(64) DEFAULT NULL COMMENT '配置生效的描述',
`type` varchar(64) DEFAULT NULL COMMENT '配置的类型', `type` VARCHAR(64) DEFAULT NULL COMMENT '配置的类型',
`c_schema` text COMMENT '配置的模式', `c_schema` text COMMENT '配置的模式',
`encrypted_data_key` varchar(1024) NOT NULL DEFAULT '' COMMENT '密钥', `encrypted_data_key` VARCHAR(1024) NOT NULL DEFAULT '' COMMENT '密钥',
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
UNIQUE KEY `uk_configinfo_datagrouptenant` (`data_id`,`group_id`,`tenant_id`) UNIQUE KEY `uk_configinfo_datagrouptenant` (`data_id`,`group_id`,`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info'; ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info';
@@ -47,19 +47,19 @@ CREATE TABLE `config_info` (
/******************************************/ /******************************************/
CREATE TABLE `config_info_gray` ( CREATE TABLE `config_info_gray` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id', `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
`data_id` varchar(255) NOT NULL COMMENT 'data_id', `data_id` VARCHAR(255) NOT NULL COMMENT 'data_id',
`group_id` varchar(128) NOT NULL COMMENT 'group_id', `group_id` VARCHAR(128) NOT NULL COMMENT 'group_id',
`content` longtext NOT NULL COMMENT 'content', `content` longtext NOT NULL COMMENT 'content',
`md5` varchar(32) DEFAULT NULL COMMENT 'md5', `md5` VARCHAR(32) DEFAULT NULL COMMENT 'md5',
`src_user` text COMMENT 'src_user', `src_user` text COMMENT 'src_user',
`src_ip` varchar(100) DEFAULT NULL COMMENT 'src_ip', `src_ip` VARCHAR(100) DEFAULT NULL COMMENT 'src_ip',
`gmt_create` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT 'gmt_create', `gmt_create` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT 'gmt_create',
`gmt_modified` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT 'gmt_modified', `gmt_modified` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT 'gmt_modified',
`app_name` varchar(128) DEFAULT NULL COMMENT 'app_name', `app_name` VARCHAR(128) DEFAULT NULL COMMENT 'app_name',
`tenant_id` varchar(128) DEFAULT '' COMMENT 'tenant_id', `tenant_id` VARCHAR(128) DEFAULT '' COMMENT 'tenant_id',
`gray_name` varchar(128) NOT NULL COMMENT 'gray_name', `gray_name` VARCHAR(128) NOT NULL COMMENT 'gray_name',
`gray_rule` text NOT NULL COMMENT 'gray_rule', `gray_rule` text NOT NULL COMMENT 'gray_rule',
`encrypted_data_key` varchar(256) NOT NULL DEFAULT '' COMMENT 'encrypted_data_key', `encrypted_data_key` VARCHAR(256) NOT NULL DEFAULT '' COMMENT 'encrypted_data_key',
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
UNIQUE KEY `uk_configinfogray_datagrouptenantgray` (`data_id`,`group_id`,`tenant_id`,`gray_name`), UNIQUE KEY `uk_configinfogray_datagrouptenantgray` (`data_id`,`group_id`,`tenant_id`,`gray_name`),
KEY `idx_dataid_gmt_modified` (`data_id`,`gmt_modified`), KEY `idx_dataid_gmt_modified` (`data_id`,`gmt_modified`),
@@ -71,11 +71,11 @@ CREATE TABLE `config_info_gray` (
/******************************************/ /******************************************/
CREATE TABLE `config_tags_relation` ( CREATE TABLE `config_tags_relation` (
`id` bigint(20) NOT NULL COMMENT 'id', `id` bigint(20) NOT NULL COMMENT 'id',
`tag_name` varchar(128) NOT NULL COMMENT 'tag_name', `tag_name` VARCHAR(128) NOT NULL COMMENT 'tag_name',
`tag_type` varchar(64) DEFAULT NULL COMMENT 'tag_type', `tag_type` VARCHAR(64) DEFAULT NULL COMMENT 'tag_type',
`data_id` varchar(255) NOT NULL COMMENT 'data_id', `data_id` VARCHAR(255) NOT NULL COMMENT 'data_id',
`group_id` varchar(128) NOT NULL COMMENT 'group_id', `group_id` VARCHAR(128) NOT NULL COMMENT 'group_id',
`tenant_id` varchar(128) DEFAULT '' COMMENT 'tenant_id', `tenant_id` VARCHAR(128) DEFAULT '' COMMENT 'tenant_id',
`nid` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'nid, 自增长标识', `nid` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'nid, 自增长标识',
PRIMARY KEY (`nid`), PRIMARY KEY (`nid`),
UNIQUE KEY `uk_configtagrelation_configidtag` (`id`,`tag_name`,`tag_type`), UNIQUE KEY `uk_configtagrelation_configidtag` (`id`,`tag_name`,`tag_type`),
@@ -87,7 +87,7 @@ CREATE TABLE `config_tags_relation` (
/******************************************/ /******************************************/
CREATE TABLE `group_capacity` ( CREATE TABLE `group_capacity` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID', `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`group_id` varchar(128) NOT NULL DEFAULT '' COMMENT 'Group ID空字符表示整个集群', `group_id` VARCHAR(128) NOT NULL DEFAULT '' COMMENT 'Group ID空字符表示整个集群',
`quota` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '配额0表示使用默认值', `quota` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '配额0表示使用默认值',
`usage` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '使用量', `usage` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '使用量',
`max_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个配置大小上限单位为字节0表示使用默认值', `max_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个配置大小上限单位为字节0表示使用默认值',
@@ -106,20 +106,20 @@ CREATE TABLE `group_capacity` (
CREATE TABLE `his_config_info` ( CREATE TABLE `his_config_info` (
`id` bigint(20) unsigned NOT NULL COMMENT 'id', `id` bigint(20) unsigned NOT NULL COMMENT 'id',
`nid` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'nid, 自增标识', `nid` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'nid, 自增标识',
`data_id` varchar(255) NOT NULL COMMENT 'data_id', `data_id` VARCHAR(255) NOT NULL COMMENT 'data_id',
`group_id` varchar(128) NOT NULL COMMENT 'group_id', `group_id` VARCHAR(128) NOT NULL COMMENT 'group_id',
`app_name` varchar(128) DEFAULT NULL COMMENT 'app_name', `app_name` VARCHAR(128) DEFAULT NULL COMMENT 'app_name',
`content` longtext NOT NULL COMMENT 'content', `content` longtext NOT NULL COMMENT 'content',
`md5` varchar(32) DEFAULT NULL COMMENT 'md5', `md5` VARCHAR(32) DEFAULT NULL COMMENT 'md5',
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间', `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
`src_user` text COMMENT 'source user', `src_user` text COMMENT 'source user',
`src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip', `src_ip` VARCHAR(50) DEFAULT NULL COMMENT 'source ip',
`op_type` char(10) DEFAULT NULL COMMENT 'operation type', `op_type` char(10) DEFAULT NULL COMMENT 'operation type',
`tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段', `tenant_id` VARCHAR(128) DEFAULT '' COMMENT '租户字段',
`encrypted_data_key` varchar(1024) NOT NULL DEFAULT '' COMMENT '密钥', `encrypted_data_key` VARCHAR(1024) NOT NULL DEFAULT '' COMMENT '密钥',
`publish_type` varchar(50) DEFAULT 'formal' COMMENT 'publish type gray or formal', `publish_type` VARCHAR(50) DEFAULT 'formal' COMMENT 'publish type gray or formal',
`gray_name` varchar(50) DEFAULT NULL COMMENT 'gray name', `gray_name` VARCHAR(50) DEFAULT NULL COMMENT 'gray name',
`ext_info` longtext DEFAULT NULL COMMENT 'ext info', `ext_info` longtext DEFAULT NULL COMMENT 'ext info',
PRIMARY KEY (`nid`), PRIMARY KEY (`nid`),
KEY `idx_gmt_create` (`gmt_create`), KEY `idx_gmt_create` (`gmt_create`),
@@ -133,7 +133,7 @@ CREATE TABLE `his_config_info` (
/******************************************/ /******************************************/
CREATE TABLE `tenant_capacity` ( CREATE TABLE `tenant_capacity` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID', `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`tenant_id` varchar(128) NOT NULL DEFAULT '' COMMENT 'Tenant ID', `tenant_id` VARCHAR(128) NOT NULL DEFAULT '' COMMENT 'Tenant ID',
`quota` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '配额0表示使用默认值', `quota` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '配额0表示使用默认值',
`usage` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '使用量', `usage` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '使用量',
`max_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个配置大小上限单位为字节0表示使用默认值', `max_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个配置大小上限单位为字节0表示使用默认值',
@@ -149,11 +149,11 @@ CREATE TABLE `tenant_capacity` (
CREATE TABLE `tenant_info` ( CREATE TABLE `tenant_info` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id', `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
`kp` varchar(128) NOT NULL COMMENT 'kp', `kp` VARCHAR(128) NOT NULL COMMENT 'kp',
`tenant_id` varchar(128) default '' COMMENT 'tenant_id', `tenant_id` VARCHAR(128) default '' COMMENT 'tenant_id',
`tenant_name` varchar(128) default '' COMMENT 'tenant_name', `tenant_name` VARCHAR(128) default '' COMMENT 'tenant_name',
`tenant_desc` varchar(256) DEFAULT NULL COMMENT 'tenant_desc', `tenant_desc` VARCHAR(256) DEFAULT NULL COMMENT 'tenant_desc',
`create_source` varchar(32) DEFAULT NULL COMMENT 'create_source', `create_source` VARCHAR(32) DEFAULT NULL COMMENT 'create_source',
`gmt_create` bigint(20) NOT NULL COMMENT '创建时间', `gmt_create` bigint(20) NOT NULL COMMENT '创建时间',
`gmt_modified` bigint(20) NOT NULL COMMENT '修改时间', `gmt_modified` bigint(20) NOT NULL COMMENT '修改时间',
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
@@ -162,21 +162,21 @@ CREATE TABLE `tenant_info` (
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='tenant_info'; ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='tenant_info';
CREATE TABLE `users` ( CREATE TABLE `users` (
`username` varchar(50) NOT NULL PRIMARY KEY COMMENT 'username', `username` VARCHAR(50) NOT NULL PRIMARY KEY COMMENT 'username',
`password` varchar(500) NOT NULL COMMENT 'password', `password` VARCHAR(500) NOT NULL COMMENT 'password',
`enabled` boolean NOT NULL COMMENT 'enabled' `enabled` BOOLEAN NOT NULL COMMENT 'enabled'
); );
CREATE TABLE `roles` ( CREATE TABLE `roles` (
`username` varchar(50) NOT NULL COMMENT 'username', `username` VARCHAR(50) NOT NULL COMMENT 'username',
`role` varchar(50) NOT NULL COMMENT 'role', `role` VARCHAR(50) NOT NULL COMMENT 'role',
UNIQUE INDEX `idx_user_role` (`username` ASC, `role` ASC) USING BTREE UNIQUE INDEX `idx_user_role` (`username` ASC, `role` ASC) USING BTREE
); );
CREATE TABLE `permissions` ( CREATE TABLE `permissions` (
`role` varchar(50) NOT NULL COMMENT 'role', `role` VARCHAR(50) NOT NULL COMMENT 'role',
`resource` varchar(128) NOT NULL COMMENT 'resource', `resource` VARCHAR(128) NOT NULL COMMENT 'resource',
`action` varchar(8) NOT NULL COMMENT 'action', `action` VARCHAR(8) NOT NULL COMMENT 'action',
UNIQUE INDEX `uk_role_permission` (`role`,`resource`,`action`) USING BTREE UNIQUE INDEX `uk_role_permission` (`role`,`resource`,`action`) USING BTREE
); );

View File

@@ -17,7 +17,7 @@
" <result column=\"create_time\" property=\"createTime\" jdbcType=\"TIMESTAMP\"/>", " <result column=\"create_time\" property=\"createTime\" jdbcType=\"TIMESTAMP\"/>",
" <result column=\"update_time\" property=\"updateTime\" jdbcType=\"TIMESTAMP\"/>", " <result column=\"update_time\" property=\"updateTime\" jdbcType=\"TIMESTAMP\"/>",
" <result column=\"delete_time\" property=\"deleteTime\" jdbcType=\"TIMESTAMP\"/>", " <result column=\"delete_time\" property=\"deleteTime\" jdbcType=\"TIMESTAMP\"/>",
" <result column=\"deleted\" property=\"deleted\" jdbcType=\"BOOLEAN\"/>", " <result column=\"deleted\" property=\"deleted\" jdbcType=\"Boolean\"/>",
" </resultMap>", " </resultMap>",
"", "",
" <!-- 基础列 -->", " <!-- 基础列 -->",
@@ -154,7 +154,7 @@
" <result column=\"create_time\" property=\"createTime\" jdbcType=\"TIMESTAMP\"/>", " <result column=\"create_time\" property=\"createTime\" jdbcType=\"TIMESTAMP\"/>",
" <result column=\"update_time\" property=\"updateTime\" jdbcType=\"TIMESTAMP\"/>", " <result column=\"update_time\" property=\"updateTime\" jdbcType=\"TIMESTAMP\"/>",
" <result column=\"delete_time\" property=\"deleteTime\" jdbcType=\"TIMESTAMP\"/>", " <result column=\"delete_time\" property=\"deleteTime\" jdbcType=\"TIMESTAMP\"/>",
" <result column=\"deleted\" property=\"deleted\" jdbcType=\"BOOLEAN\"/>", " <result column=\"deleted\" property=\"deleted\" jdbcType=\"Boolean\"/>",
" </resultMap>" " </resultMap>"
], ],
"description": "MyBatis ResultMap 映射" "description": "MyBatis ResultMap 映射"

View File

@@ -64,8 +64,8 @@ public class AclVO extends BaseVO {
private Integer defaultPermission = 0; private Integer defaultPermission = 0;
@Schema(description = "默认是否允许", defaultValue = "true") @Schema(description = "默认是否允许", defaultValue = "true")
private boolean defaultAllow = true; private Boolean defaultAllow = true;
@Schema(description = "是否默认应用到子级", defaultValue = "true") @Schema(description = "是否默认应用到子级", defaultValue = "true")
private boolean applyToChildren = true; private Boolean applyToChildren = true;
} }

View File

@@ -51,7 +51,7 @@ public class PermissionVO extends BaseVO {
private String roleOwnerDeptId; private String roleOwnerDeptId;
@Schema(description = "角色状态") @Schema(description = "角色状态")
private boolean roleStatus; private Boolean roleStatus;
// TbSysModuleDTO对应字段 // TbSysModuleDTO对应字段
@Schema(description = "模块ID") @Schema(description = "模块ID")

View File

@@ -100,7 +100,7 @@ public class UserDeptRoleVO extends BaseVO {
private String ownerDeptId; private String ownerDeptId;
@Schema(description = "角色状态") @Schema(description = "角色状态")
private boolean roleStatus; private Boolean roleStatus;
public static TbSysDeptDTO toDeptDTO(UserDeptRoleVO vo) { public static TbSysDeptDTO toDeptDTO(UserDeptRoleVO vo) {

View File

@@ -21,7 +21,7 @@ public class AuthProperties {
* 是否启用认证过滤器 * 是否启用认证过滤器
* 默认启用 * 默认启用
*/ */
private boolean enabled = true; private Boolean enabled = true;
/** /**
* 登录接口路径 * 登录接口路径
@@ -72,11 +72,11 @@ public class AuthProperties {
whitelist.add("/error"); whitelist.add("/error");
} }
public boolean isEnabled() { public Boolean isEnabled() {
return enabled; return enabled;
} }
public void setEnabled(boolean enabled) { public void setEnabled(Boolean enabled) {
this.enabled = enabled; this.enabled = enabled;
} }

View File

@@ -0,0 +1,82 @@
package org.xyzh.common.auth.config;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.lang.NonNull;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import org.xyzh.common.auth.contants.AuthContants;
import java.io.IOException;
import java.util.Collections;
/**
* @description Gateway 认证模式配置 - 信任 Gateway 传递的用户信息
* @author yslg
* @since 2025-12-02
*
* 当启用 gateway-mode 时,微服务不再独立验证 JWT而是信任 Gateway 传递的用户信息
* 配置项auth.gateway-mode=true
*/
@Configuration
@ConditionalOnProperty(name = "auth.gateway-mode", havingValue = "true")
public class GatewayAuthConfig {
private static final Logger log = LoggerFactory.getLogger(GatewayAuthConfig.class);
/**
* Gateway 信任过滤器 - 从请求头获取用户信息
* 替代 JwtAuthenticationFilter
*/
@Bean
public GatewayTrustFilter gatewayTrustFilter() {
log.info("启用 Gateway 认证模式,微服务将信任 Gateway 传递的用户信息");
return new GatewayTrustFilter();
}
/**
* Gateway 信任过滤器实现
*/
public static class GatewayTrustFilter extends OncePerRequestFilter {
private static final Logger log = LoggerFactory.getLogger(GatewayTrustFilter.class);
@Override
protected void doFilterInternal(@NonNull HttpServletRequest request,
@NonNull HttpServletResponse response,
@NonNull FilterChain filterChain)
throws ServletException, IOException {
// 从 Gateway 传递的请求头获取用户信息
String userId = request.getHeader(AuthContants.USER_ID_ATTRIBUTE);
String username = request.getHeader(AuthContants.USERNAME_ATTRIBUTE);
if (StringUtils.hasText(userId)) {
// 构造简化的 Principal使用 userId 作为身份标识)
// 注意:完整的 LoginDomain 应该从 Redis 加载,这里只是基本身份验证
// 设置到 Spring Security 上下文
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(userId, null, Collections.emptyList());
SecurityContextHolder.getContext().setAuthentication(authentication);
// 同时将用户信息设置到 request attributes 中,供业务代码使用
request.setAttribute(AuthContants.USER_ID_ATTRIBUTE, userId);
if (StringUtils.hasText(username)) {
request.setAttribute(AuthContants.USERNAME_ATTRIBUTE, username);
}
log.debug("从 Gateway 获取用户信息: userId={}, username={}", userId, username);
}
filterChain.doFilter(request, response);
}
}
}

View File

@@ -1,5 +1,7 @@
package org.xyzh.common.auth.config; package org.xyzh.common.auth.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
@@ -15,17 +17,29 @@ import org.xyzh.redis.service.RedisService;
@EnableMethodSecurity @EnableMethodSecurity
public class SecurityConfig { public class SecurityConfig {
/**
* JWT 认证过滤器 - 仅在非 Gateway 模式下启用
* 当 auth.gateway-mode=false 或未配置时使用
*/
@Bean @Bean
@ConditionalOnProperty(name = "auth.gateway-mode", havingValue = "false", matchIfMissing = true)
public JwtAuthenticationFilter jwtAuthenticationFilter(TokenParser tokenParser, public JwtAuthenticationFilter jwtAuthenticationFilter(TokenParser tokenParser,
AuthProperties authProperties, AuthProperties authProperties,
RedisService redisService) { RedisService redisService) {
return new JwtAuthenticationFilter(tokenParser, authProperties, redisService); return new JwtAuthenticationFilter(tokenParser, authProperties, redisService);
} }
/**
* Security 过滤器链配置
* 根据是否启用 Gateway 模式决定是否添加 JWT 过滤器
*/
@Bean @Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http, public SecurityFilterChain securityFilterChain(
AuthProperties authProperties, HttpSecurity http,
JwtAuthenticationFilter jwtAuthenticationFilter) throws Exception { AuthProperties authProperties,
@Autowired(required = false) JwtAuthenticationFilter jwtAuthenticationFilter,
@Autowired(required = false) GatewayAuthConfig.GatewayTrustFilter gatewayTrustFilter) throws Exception {
http http
.csrf(csrf -> csrf.disable()) .csrf(csrf -> csrf.disable())
.formLogin(form -> form.disable()) .formLogin(form -> form.disable())
@@ -45,8 +59,16 @@ public class SecurityConfig {
authz authz
.requestMatchers("/error", "/favicon.ico").permitAll() .requestMatchers("/error", "/favicon.ico").permitAll()
.anyRequest().authenticated(); .anyRequest().authenticated();
}) });
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
// 根据模式添加对应的过滤器
if (jwtAuthenticationFilter != null) {
// 非 Gateway 模式:使用 JWT 过滤器
http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
} else if (gatewayTrustFilter != null) {
// Gateway 模式:使用信任过滤器
http.addFilterBefore(gatewayTrustFilter, UsernamePasswordAuthenticationFilter.class);
}
return http.build(); return http.build();
} }

View File

@@ -178,7 +178,7 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
/** /**
* 检查路径是否在白名单中 * 检查路径是否在白名单中
*/ */
private boolean isWhitelisted(@NonNull String path) { private Boolean isWhitelisted(@NonNull String path) {
// 1. 先检查认证相关接口login / logout / captcha / refresh // 1. 先检查认证相关接口login / logout / captcha / refresh
if (authProperties.getAuthPaths() != null) { if (authProperties.getAuthPaths() != null) {
for (String pattern : authProperties.getAuthPaths()) { for (String pattern : authProperties.getAuthPaths()) {

View File

@@ -34,12 +34,12 @@ public class JwtTokenParser implements TokenParser {
} }
@Override @Override
public boolean validateToken(String token, String userId) { public Boolean validateToken(String token, String userId) {
return jwtTokenUtil.validateToken(token, userId); return jwtTokenUtil.validateToken(token, userId);
} }
@Override @Override
public boolean isTokenExpired(String token) { public Boolean isTokenExpired(String token) {
return jwtTokenUtil.isTokenExpired(token); return jwtTokenUtil.isTokenExpired(token);
} }
} }

View File

@@ -33,19 +33,19 @@ public interface TokenParser {
* @description 验证令牌 * @description 验证令牌
* @param token 令牌 * @param token 令牌
* @param userId 用户ID * @param userId 用户ID
* @return boolean 是否有效 * @return Boolean 是否有效
* @author yslg * @author yslg
* @since 2025-11-02 * @since 2025-11-02
*/ */
boolean validateToken(String token, String userId); Boolean validateToken(String token, String userId);
/** /**
* @description 检查令牌是否过期 * @description 检查令牌是否过期
* @param token 令牌 * @param token 令牌
* @return boolean 是否过期 * @return Boolean 是否过期
* @author yslg * @author yslg
* @since 2025-11-02 * @since 2025-11-02
*/ */
boolean isTokenExpired(String token); Boolean isTokenExpired(String token);
} }

View File

@@ -114,11 +114,11 @@ public class JwtTokenUtil {
* @description 验证令牌 * @description 验证令牌
* @param token JWT令牌 * @param token JWT令牌
* @param userId 用户ID * @param userId 用户ID
* @return boolean 是否有效 * @return Boolean 是否有效
* @author yslg * @author yslg
* @since 2025-11-07 * @since 2025-11-07
*/ */
public boolean validateToken(String token, String userId) { public Boolean validateToken(String token, String userId) {
try { try {
final String tokenUserId = getUserIdFromToken(token); final String tokenUserId = getUserIdFromToken(token);
return (userId.equals(tokenUserId) && !isTokenExpired(token)); return (userId.equals(tokenUserId) && !isTokenExpired(token));
@@ -130,11 +130,11 @@ public class JwtTokenUtil {
/** /**
* @description 检查令牌是否过期 * @description 检查令牌是否过期
* @param token JWT令牌 * @param token JWT令牌
* @return boolean 是否过期 * @return Boolean 是否过期
* @author yslg * @author yslg
* @since 2025-11-07 * @since 2025-11-07
*/ */
public boolean isTokenExpired(String token) { public Boolean isTokenExpired(String token) {
final Date expiration = getExpirationDateFromToken(token); final Date expiration = getExpirationDateFromToken(token);
return expiration.before(new Date()); return expiration.before(new Date());
} }

View File

@@ -20,7 +20,7 @@ public class ResultDomain<T> implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private Integer code; private Integer code;
private boolean success; private Boolean success;
private String message; private String message;
private T data; private T data;
private List<T> dataList; private List<T> dataList;

View File

@@ -36,8 +36,8 @@ public class TbSysAclPolicyDTO extends BaseDTO {
private Integer defaultPermission=0; private Integer defaultPermission=0;
@Schema(description = "默认是否允许", defaultValue = "true") @Schema(description = "默认是否允许", defaultValue = "true")
private boolean defaultAllow=true; private Boolean defaultAllow=true;
@Schema(description = "是否默认应用到子级", defaultValue = "true") @Schema(description = "是否默认应用到子级", defaultValue = "true")
private boolean applyToChildren=true; private Boolean applyToChildren=true;
} }

View File

@@ -33,5 +33,5 @@ public class TbSysRoleDTO extends BaseDTO {
private String ownerDeptId; private String ownerDeptId;
@Schema(description = "角色状态 true 有效, false 无效") @Schema(description = "角色状态 true 有效, false 无效")
private boolean status; private Boolean status;
} }

View File

@@ -79,11 +79,11 @@ public class RedisService {
/** /**
* @description 判断key是否存在 * @description 判断key是否存在
* @param key String 键 * @param key String 键
* @return boolean 是否存在 * @return Boolean 是否存在
* @author yslg * @author yslg
* @since 2025-11-02 * @since 2025-11-02
*/ */
public boolean hasKey(String key) { public Boolean hasKey(String key) {
return redisTemplate.hasKey(key); return redisTemplate.hasKey(key);
} }

View File

@@ -25,7 +25,7 @@ public class NonUtils {
* @param obj 待判断的对象 * @param obj 待判断的对象
* @return true-对象为nullfalse-对象不为null * @return true-对象为nullfalse-对象不为null
*/ */
public static boolean isNull(Object obj) { public static Boolean isNull(Object obj) {
return obj == null; return obj == null;
} }
@@ -35,7 +35,7 @@ public class NonUtils {
* @param obj 待判断的对象 * @param obj 待判断的对象
* @return true-对象不为nullfalse-对象为null * @return true-对象不为nullfalse-对象为null
*/ */
public static boolean isNotNull(Object obj) { public static Boolean isNotNull(Object obj) {
return obj != null; return obj != null;
} }
@@ -45,7 +45,7 @@ public class NonUtils {
* @param objects 待判断的对象数组 * @param objects 待判断的对象数组
* @return true-所有对象都为nullfalse-至少有一个对象不为null * @return true-所有对象都为nullfalse-至少有一个对象不为null
*/ */
public static boolean isAllNull(Object... objects) { public static Boolean isAllNull(Object... objects) {
if (objects == null || objects.length == 0) { if (objects == null || objects.length == 0) {
return true; return true;
} }
@@ -63,7 +63,7 @@ public class NonUtils {
* @param objects 待判断的对象数组 * @param objects 待判断的对象数组
* @return true-所有对象都不为nullfalse-至少有一个对象为null * @return true-所有对象都不为nullfalse-至少有一个对象为null
*/ */
public static boolean isAllNotNull(Object... objects) { public static Boolean isAllNotNull(Object... objects) {
if (objects == null || objects.length == 0) { if (objects == null || objects.length == 0) {
return false; return false;
} }
@@ -81,7 +81,7 @@ public class NonUtils {
* @param objects 待判断的对象数组 * @param objects 待判断的对象数组
* @return true-存在null对象false-不存在null对象 * @return true-存在null对象false-不存在null对象
*/ */
public static boolean hasNull(Object... objects) { public static Boolean hasNull(Object... objects) {
if (objects == null || objects.length == 0) { if (objects == null || objects.length == 0) {
return true; return true;
} }
@@ -106,7 +106,7 @@ public class NonUtils {
* @param obj 待判断的对象 * @param obj 待判断的对象
* @return true-对象为空false-对象不为空 * @return true-对象为空false-对象不为空
*/ */
public static boolean isEmpty(Object obj) { public static Boolean isEmpty(Object obj) {
if (isNull(obj)) { if (isNull(obj)) {
return true; return true;
} }
@@ -145,7 +145,7 @@ public class NonUtils {
* @param obj 待判断的对象 * @param obj 待判断的对象
* @return true-对象不为空false-对象为空 * @return true-对象不为空false-对象为空
*/ */
public static boolean isNotEmpty(Object obj) { public static Boolean isNotEmpty(Object obj) {
return !isEmpty(obj); return !isEmpty(obj);
} }
@@ -155,7 +155,7 @@ public class NonUtils {
* @param objects 待判断的对象数组 * @param objects 待判断的对象数组
* @return true-所有对象都为空false-至少有一个对象不为空 * @return true-所有对象都为空false-至少有一个对象不为空
*/ */
public static boolean isAllEmpty(Object... objects) { public static Boolean isAllEmpty(Object... objects) {
if (objects == null || objects.length == 0) { if (objects == null || objects.length == 0) {
return true; return true;
} }
@@ -173,7 +173,7 @@ public class NonUtils {
* @param objects 待判断的对象数组 * @param objects 待判断的对象数组
* @return true-所有对象都不为空false-至少有一个对象为空 * @return true-所有对象都不为空false-至少有一个对象为空
*/ */
public static boolean isAllNotEmpty(Object... objects) { public static Boolean isAllNotEmpty(Object... objects) {
if (objects == null || objects.length == 0) { if (objects == null || objects.length == 0) {
return false; return false;
} }
@@ -191,7 +191,7 @@ public class NonUtils {
* @param objects 待判断的对象数组 * @param objects 待判断的对象数组
* @return true-存在空值false-不存在空值 * @return true-存在空值false-不存在空值
*/ */
public static boolean hasEmpty(Object... objects) { public static Boolean hasEmpty(Object... objects) {
if (objects == null || objects.length == 0) { if (objects == null || objects.length == 0) {
return true; return true;
} }
@@ -212,7 +212,7 @@ public class NonUtils {
* @param obj 待判断的对象 * @param obj 待判断的对象
* @return true-对象为空包括递归检查false-对象不为空 * @return true-对象为空包括递归检查false-对象不为空
*/ */
public static boolean isDeepEmpty(Object obj) { public static Boolean isDeepEmpty(Object obj) {
return isDeepEmpty(obj, new HashSet<>()); return isDeepEmpty(obj, new HashSet<>());
} }
@@ -223,7 +223,7 @@ public class NonUtils {
* @param visited 已访问对象集合,用于防止循环引用 * @param visited 已访问对象集合,用于防止循环引用
* @return true-对象为空包括递归检查false-对象不为空 * @return true-对象为空包括递归检查false-对象不为空
*/ */
private static boolean isDeepEmpty(Object obj, Set<Object> visited) { private static Boolean isDeepEmpty(Object obj, Set<Object> visited) {
if (isEmpty(obj)) { if (isEmpty(obj)) {
return true; return true;
} }
@@ -288,7 +288,7 @@ public class NonUtils {
* @param obj 待判断的对象 * @param obj 待判断的对象
* @return true-对象不为空包括递归检查false-对象为空 * @return true-对象不为空包括递归检查false-对象为空
*/ */
public static boolean isDeepNotEmpty(Object obj) { public static Boolean isDeepNotEmpty(Object obj) {
return !isDeepEmpty(obj); return !isDeepEmpty(obj);
} }
@@ -300,7 +300,7 @@ public class NonUtils {
* @param collection 待判断的集合 * @param collection 待判断的集合
* @return true-集合为null或空false-集合不为空 * @return true-集合为null或空false-集合不为空
*/ */
public static boolean isEmptyCollection(Collection<?> collection) { public static Boolean isEmptyCollection(Collection<?> collection) {
return collection == null || collection.isEmpty(); return collection == null || collection.isEmpty();
} }
@@ -310,7 +310,7 @@ public class NonUtils {
* @param collection 待判断的集合 * @param collection 待判断的集合
* @return true-集合不为null且不为空false-集合为null或空 * @return true-集合不为null且不为空false-集合为null或空
*/ */
public static boolean isNotEmptyCollection(Collection<?> collection) { public static Boolean isNotEmptyCollection(Collection<?> collection) {
return collection != null && !collection.isEmpty(); return collection != null && !collection.isEmpty();
} }
@@ -320,7 +320,7 @@ public class NonUtils {
* @param collection 待判断的集合 * @param collection 待判断的集合
* @return true-集合包含有效元素false-集合为空或只包含null/空元素 * @return true-集合包含有效元素false-集合为空或只包含null/空元素
*/ */
public static boolean hasValidElements(Collection<?> collection) { public static Boolean hasValidElements(Collection<?> collection) {
if (isEmptyCollection(collection)) { if (isEmptyCollection(collection)) {
return false; return false;
} }
@@ -338,7 +338,7 @@ public class NonUtils {
* @param collection 待判断的集合 * @param collection 待判断的集合
* @return true-集合所有元素都有效false-集合为空或包含null/空元素 * @return true-集合所有元素都有效false-集合为空或包含null/空元素
*/ */
public static boolean allValidElements(Collection<?> collection) { public static Boolean allValidElements(Collection<?> collection) {
if (isEmptyCollection(collection)) { if (isEmptyCollection(collection)) {
return false; return false;
} }
@@ -378,7 +378,7 @@ public class NonUtils {
* @param map 待判断的Map * @param map 待判断的Map
* @return true-Map为null或空false-Map不为空 * @return true-Map为null或空false-Map不为空
*/ */
public static boolean isEmptyMap(Map<?, ?> map) { public static Boolean isEmptyMap(Map<?, ?> map) {
return map == null || map.isEmpty(); return map == null || map.isEmpty();
} }
@@ -388,7 +388,7 @@ public class NonUtils {
* @param map 待判断的Map * @param map 待判断的Map
* @return true-Map不为null且不为空false-Map为null或空 * @return true-Map不为null且不为空false-Map为null或空
*/ */
public static boolean isNotEmptyMap(Map<?, ?> map) { public static Boolean isNotEmptyMap(Map<?, ?> map) {
return map != null && !map.isEmpty(); return map != null && !map.isEmpty();
} }
@@ -400,7 +400,7 @@ public class NonUtils {
* @param array 待判断的数组 * @param array 待判断的数组
* @return true-数组为null或空false-数组不为空 * @return true-数组为null或空false-数组不为空
*/ */
public static boolean isEmptyArray(Object array) { public static Boolean isEmptyArray(Object array) {
return array == null || !array.getClass().isArray() || Array.getLength(array) == 0; return array == null || !array.getClass().isArray() || Array.getLength(array) == 0;
} }
@@ -410,7 +410,7 @@ public class NonUtils {
* @param array 待判断的数组 * @param array 待判断的数组
* @return true-数组不为null且不为空false-数组为null或空 * @return true-数组不为null且不为空false-数组为null或空
*/ */
public static boolean isNotEmptyArray(Object array) { public static Boolean isNotEmptyArray(Object array) {
return array != null && array.getClass().isArray() && Array.getLength(array) > 0; return array != null && array.getClass().isArray() && Array.getLength(array) > 0;
} }
@@ -422,7 +422,7 @@ public class NonUtils {
* @param str 待判断的字符串 * @param str 待判断的字符串
* @return true-字符串为null、空或只包含空白字符false-字符串有有效内容 * @return true-字符串为null、空或只包含空白字符false-字符串有有效内容
*/ */
public static boolean isEmptyString(String str) { public static Boolean isEmptyString(String str) {
return str == null || str.trim().isEmpty(); return str == null || str.trim().isEmpty();
} }
@@ -432,7 +432,7 @@ public class NonUtils {
* @param str 待判断的字符串 * @param str 待判断的字符串
* @return true-字符串不为null且有有效内容false-字符串为null、空或只包含空白字符 * @return true-字符串不为null且有有效内容false-字符串为null、空或只包含空白字符
*/ */
public static boolean isNotEmptyString(String str) { public static Boolean isNotEmptyString(String str) {
return str != null && !str.trim().isEmpty(); return str != null && !str.trim().isEmpty();
} }
@@ -581,7 +581,7 @@ public class NonUtils {
* @param <T> 目标类型 * @param <T> 目标类型
* @return true-对象不为null且为指定类型false-否则 * @return true-对象不为null且为指定类型false-否则
*/ */
public static <T> boolean isInstanceAndNotNull(Object obj, Class<T> clazz) { public static <T> Boolean isInstanceAndNotNull(Object obj, Class<T> clazz) {
return isNotNull(obj) && clazz.isInstance(obj); return isNotNull(obj) && clazz.isInstance(obj);
} }

View File

@@ -88,7 +88,7 @@ public class ServletUtils {
* @author yslg * @author yslg
* @since 2025-11-02 * @since 2025-11-02
*/ */
public static boolean isAjaxRequest(HttpServletRequest request) { public static Boolean isAjaxRequest(HttpServletRequest request) {
String header = request.getHeader("X-Requested-With"); String header = request.getHeader("X-Requested-With");
return "XMLHttpRequest".equalsIgnoreCase(header); return "XMLHttpRequest".equalsIgnoreCase(header);
} }
@@ -153,7 +153,7 @@ public class ServletUtils {
* @author yslg * @author yslg
* @since 2025-11-02 * @since 2025-11-02
*/ */
public static boolean isGet(HttpServletRequest request) { public static Boolean isGet(HttpServletRequest request) {
return "GET".equalsIgnoreCase(request.getMethod()); return "GET".equalsIgnoreCase(request.getMethod());
} }
@@ -164,7 +164,7 @@ public class ServletUtils {
* @author yslg * @author yslg
* @since 2025-11-02 * @since 2025-11-02
*/ */
public static boolean isPost(HttpServletRequest request) { public static Boolean isPost(HttpServletRequest request) {
return "POST".equalsIgnoreCase(request.getMethod()); return "POST".equalsIgnoreCase(request.getMethod());
} }
@@ -175,7 +175,7 @@ public class ServletUtils {
* @author yslg * @author yslg
* @since 2025-11-02 * @since 2025-11-02
*/ */
public static boolean isPut(HttpServletRequest request) { public static Boolean isPut(HttpServletRequest request) {
return "PUT".equalsIgnoreCase(request.getMethod()); return "PUT".equalsIgnoreCase(request.getMethod());
} }
@@ -186,7 +186,7 @@ public class ServletUtils {
* @author yslg * @author yslg
* @since 2025-11-02 * @since 2025-11-02
*/ */
public static boolean isDelete(HttpServletRequest request) { public static Boolean isDelete(HttpServletRequest request) {
return "DELETE".equalsIgnoreCase(request.getMethod()); return "DELETE".equalsIgnoreCase(request.getMethod());
} }
@@ -286,7 +286,7 @@ public class ServletUtils {
* @author yslg * @author yslg
* @since 2025-11-02 * @since 2025-11-02
*/ */
public static boolean isHttps(HttpServletRequest request) { public static Boolean isHttps(HttpServletRequest request) {
return "https".equals(request.getScheme()) || request.isSecure() || return "https".equals(request.getScheme()) || request.isSecure() ||
"443".equals(request.getHeader("X-Forwarded-Port")) || "443".equals(request.getHeader("X-Forwarded-Port")) ||
"https".equals(request.getHeader("X-Forwarded-Proto")); "https".equals(request.getHeader("X-Forwarded-Proto"));
@@ -311,7 +311,7 @@ public class ServletUtils {
* @author yslg * @author yslg
* @since 2025-11-02 * @since 2025-11-02
*/ */
public static boolean hasParameter(HttpServletRequest request, String paramName) { public static Boolean hasParameter(HttpServletRequest request, String paramName) {
return request.getParameter(paramName) != null; return request.getParameter(paramName) != null;
} }

View File

@@ -15,7 +15,7 @@ public class StringUtils {
* @author yslg * @author yslg
* @since 2025-11-02 * @since 2025-11-02
*/ */
public static boolean isEmpty(String str) { public static Boolean isEmpty(String str) {
return str == null || str.isEmpty(); return str == null || str.isEmpty();
} }
@@ -26,7 +26,7 @@ public class StringUtils {
* @author yslg * @author yslg
* @since 2025-11-02 * @since 2025-11-02
*/ */
public static boolean isNotEmpty(String str) { public static Boolean isNotEmpty(String str) {
return !isEmpty(str); return !isEmpty(str);
} }
@@ -54,7 +54,7 @@ public class StringUtils {
* @author yslg * @author yslg
* @since 2025-11-02 * @since 2025-11-02
*/ */
public static boolean equals(String a, String b) { public static Boolean equals(String a, String b) {
return a == null ? b == null : a.equals(b); return a == null ? b == null : a.equals(b);
} }
@@ -66,7 +66,7 @@ public class StringUtils {
* @author yslg * @author yslg
* @since 2025-11-02 * @since 2025-11-02
*/ */
public static boolean contains(String str, String sub) { public static Boolean contains(String str, String sub) {
return str != null && sub != null && str.contains(sub); return str != null && sub != null && str.contains(sub);
} }
@@ -116,7 +116,7 @@ public class StringUtils {
* @author yslg * @author yslg
* @since 2025-11-02 * @since 2025-11-02
*/ */
public static boolean startsWith(String str, String prefix) { public static Boolean startsWith(String str, String prefix) {
return str != null && prefix != null && str.startsWith(prefix); return str != null && prefix != null && str.startsWith(prefix);
} }
@@ -128,7 +128,7 @@ public class StringUtils {
* @author yslg * @author yslg
* @since 2025-11-02 * @since 2025-11-02
*/ */
public static boolean endsWith(String str, String suffix) { public static Boolean endsWith(String str, String suffix) {
return str != null && suffix != null && str.endsWith(suffix); return str != null && suffix != null && str.endsWith(suffix);
} }
@@ -203,7 +203,7 @@ public class StringUtils {
* @author yslg * @author yslg
* @since 2025-11-02 * @since 2025-11-02
*/ */
public static boolean isNumeric(String str) { public static Boolean isNumeric(String str) {
if (isEmpty(str)) return false; if (isEmpty(str)) return false;
for (int i = 0; i < str.length(); i++) { for (int i = 0; i < str.length(); i++) {
if (!Character.isDigit(str.charAt(i))) return false; if (!Character.isDigit(str.charAt(i))) return false;
@@ -218,7 +218,7 @@ public class StringUtils {
* @author yslg * @author yslg
* @since 2025-11-02 * @since 2025-11-02
*/ */
public static boolean isBlank(String str) { public static Boolean isBlank(String str) {
return str == null || str.isBlank(); return str == null || str.isBlank();
} }
/** /**
@@ -228,7 +228,7 @@ public class StringUtils {
* @author yslg * @author yslg
* @since 2025-11-02 * @since 2025-11-02
*/ */
public static boolean isNotBlank(String str) { public static Boolean isNotBlank(String str) {
return !isBlank(str); return !isBlank(str);
} }

View File

@@ -265,7 +265,7 @@ public class TimeUtils {
* @author yslg * @author yslg
* @since 2025-11-02 * @since 2025-11-02
*/ */
public static boolean isNowBetween(String startTime, String endTime) { public static Boolean isNowBetween(String startTime, String endTime) {
LocalTime now = LocalTime.now(); LocalTime now = LocalTime.now();
LocalTime start = LocalTime.parse(startTime, DateTimeFormatter.ofPattern("HH:mm:ss")); LocalTime start = LocalTime.parse(startTime, DateTimeFormatter.ofPattern("HH:mm:ss"));
LocalTime end = LocalTime.parse(endTime, DateTimeFormatter.ofPattern("HH:mm:ss")); LocalTime end = LocalTime.parse(endTime, DateTimeFormatter.ofPattern("HH:mm:ss"));

View File

@@ -37,7 +37,7 @@ public class ExcelColumnMapping {
/** /**
* @description 是否必填 * @description 是否必填
*/ */
private boolean required = false; private Boolean required = false;
/** /**
* @description 默认值 * @description 默认值
@@ -74,7 +74,7 @@ public class ExcelColumnMapping {
return fieldType; return fieldType;
} }
public boolean isRequired() { public Boolean isRequired() {
return required; return required;
} }
@@ -131,7 +131,7 @@ public class ExcelColumnMapping {
/** /**
* 设置是否必填 * 设置是否必填
*/ */
public Builder required(boolean required) { public Builder required(Boolean required) {
mapping.required = required; mapping.required = required;
return this; return this;
} }

View File

@@ -17,7 +17,7 @@ public class ExcelReadResult<T> {
/** /**
* @description 是否成功 * @description 是否成功
*/ */
private boolean success; private Boolean success;
/** /**
* @description 成功读取的数据列表 * @description 成功读取的数据列表
@@ -58,11 +58,11 @@ public class ExcelReadResult<T> {
this.errorRowsCount = 0; this.errorRowsCount = 0;
} }
public boolean isSuccess() { public Boolean isSuccess() {
return success; return success;
} }
public void setSuccess(boolean success) { public void setSuccess(Boolean success) {
this.success = success; this.success = success;
} }
@@ -124,7 +124,7 @@ public class ExcelReadResult<T> {
this.errorRowsCount++; this.errorRowsCount++;
} }
public boolean hasErrors() { public Boolean hasErrors() {
return this.errorRowsCount > 0; return this.errorRowsCount > 0;
} }

View File

@@ -97,9 +97,9 @@ public class ExcelReaderUtils {
int dataStartRowIndex = (int) options.getOrDefault("dataStartRowIndex", 1); int dataStartRowIndex = (int) options.getOrDefault("dataStartRowIndex", 1);
int sheetIndex = (int) options.getOrDefault("sheetIndex", 0); int sheetIndex = (int) options.getOrDefault("sheetIndex", 0);
String sheetName = (String) options.get("sheetName"); String sheetName = (String) options.get("sheetName");
boolean skipEmptyRow = (boolean) options.getOrDefault("skipEmptyRow", true); Boolean skipEmptyRow = (Boolean) options.getOrDefault("skipEmptyRow", true);
int maxRows = (int) options.getOrDefault("maxRows", 0); int maxRows = (int) options.getOrDefault("maxRows", 0);
boolean continueOnError = (boolean) options.getOrDefault("continueOnError", true); Boolean continueOnError = (Boolean) options.getOrDefault("continueOnError", true);
// 创建Workbook // 创建Workbook
Workbook workbook = createWorkbook(inputStream, fileName); Workbook workbook = createWorkbook(inputStream, fileName);
@@ -177,8 +177,8 @@ public class ExcelReaderUtils {
*/ */
private static void readData(Sheet sheet, Map<String, Integer> headerMap, private static void readData(Sheet sheet, Map<String, Integer> headerMap,
Class<?> targetClass, List<ExcelColumnMapping> columnMappings, Class<?> targetClass, List<ExcelColumnMapping> columnMappings,
int startRow, boolean skipEmptyRow, int maxRows, int startRow, Boolean skipEmptyRow, int maxRows,
boolean continueOnError, ExcelReadResult<Object> result) { Boolean continueOnError, ExcelReadResult<Object> result) {
int lastRowNum = sheet.getLastRowNum(); int lastRowNum = sheet.getLastRowNum();
int endRow = maxRows > 0 ? Math.min(startRow + maxRows, lastRowNum + 1) : lastRowNum + 1; int endRow = maxRows > 0 ? Math.min(startRow + maxRows, lastRowNum + 1) : lastRowNum + 1;
@@ -229,7 +229,7 @@ public class ExcelReaderUtils {
/** /**
* @description 判断是否为空行 * @description 判断是否为空行
*/ */
private static boolean isEmptyRow(Row row) { private static Boolean isEmptyRow(Row row) {
if (row == null) { if (row == null) {
return true; return true;
} }
@@ -369,7 +369,7 @@ public class ExcelReaderUtils {
return strValue; return strValue;
} }
// Integer类型 // INTEGER类型
if (targetType == Integer.class || targetType == int.class) { if (targetType == Integer.class || targetType == int.class) {
if (value instanceof Number) { if (value instanceof Number) {
return ((Number) value).intValue(); return ((Number) value).intValue();
@@ -401,7 +401,7 @@ public class ExcelReaderUtils {
return Float.parseFloat(strValue); return Float.parseFloat(strValue);
} }
// Boolean类型 // BOOLEAN类型
if (targetType == Boolean.class || targetType == boolean.class) { if (targetType == Boolean.class || targetType == boolean.class) {
if (value instanceof Boolean) { if (value instanceof Boolean) {
return value; return value;

View File

@@ -426,7 +426,7 @@ public ResultDomain<String> importUsers(@RequestParam("file") MultipartFile file
- **Long/long** - 长整数 - **Long/long** - 长整数
- **Double/double** - 双精度浮点数 - **Double/double** - 双精度浮点数
- **Float/float** - 单精度浮点数 - **Float/float** - 单精度浮点数
- **Boolean/boolean** - 布尔值支持true/false、1/0、是/否) - **Boolean/Boolean** - 布尔值支持true/false、1/0、是/否)
- **Date** - 日期需指定dateFormat - **Date** - 日期需指定dateFormat
## 配置选项 ## 配置选项
@@ -439,7 +439,7 @@ public ResultDomain<String> importUsers(@RequestParam("file") MultipartFile file
| columnIndex(int) | 设置Excel列索引从0开始优先级高于columnName | columnName和columnIndex至少一个 | | columnIndex(int) | 设置Excel列索引从0开始优先级高于columnName | columnName和columnIndex至少一个 |
| fieldName(String) | 设置对象字段名 | 是 | | fieldName(String) | 设置对象字段名 | 是 |
| fieldType(Class<?>) | 设置字段类型 | 否默认String.class | | fieldType(Class<?>) | 设置字段类型 | 否默认String.class |
| required() / required(boolean) | 设置是否必填 | 否默认false | | required() / required(Boolean) | 设置是否必填 | 否默认false |
| defaultValue(String) | 设置默认值 | 否 | | defaultValue(String) | 设置默认值 | 否 |
| dateFormat(String) | 设置日期格式 | 否(默认"yyyy-MM-dd" | | dateFormat(String) | 设置日期格式 | 否(默认"yyyy-MM-dd" |
| addValidation(ValidationParam) | 添加校验参数 | 否 | | addValidation(ValidationParam) | 添加校验参数 | 否 |
@@ -455,9 +455,9 @@ public ResultDomain<String> importUsers(@RequestParam("file") MultipartFile file
| dataStartRowIndex | int | 数据起始行从0开始 | 1 | | dataStartRowIndex | int | 数据起始行从0开始 | 1 |
| sheetIndex | int | Sheet索引从0开始 | 0 | | sheetIndex | int | Sheet索引从0开始 | 0 |
| sheetName | String | Sheet名称优先级高于sheetIndex | null | | sheetName | String | Sheet名称优先级高于sheetIndex | null |
| skipEmptyRow | boolean | 跳过空行 | true | | skipEmptyRow | Boolean | 跳过空行 | true |
| maxRows | int | 最大读取行数0=不限制) | 0 | | maxRows | int | 最大读取行数0=不限制) | 0 |
| continueOnError | boolean | 遇到错误继续读取 | true | | continueOnError | Boolean | 遇到错误继续读取 | true |
## 核心特性 ## 核心特性
@@ -480,7 +480,7 @@ public ResultDomain<String> importUsers(@RequestParam("file") MultipartFile file
5. **布尔类型**支持多种格式true/false、1/0、是/否) 5. **布尔类型**支持多种格式true/false、1/0、是/否)
6. **错误处理**根据continueOnError选项决定遇到错误时是否继续 6. **错误处理**根据continueOnError选项决定遇到错误时是否继续
7. **文件格式**:支持.xls和.xlsx两种格式 7. **文件格式**:支持.xls和.xlsx两种格式
8. **类型转换**自动转换支持String、Integer、Long、Double、Float、Boolean、Date 8. **类型转换**自动转换支持String、Interge、Long、Double、Float、Boolean、Date
## 依赖 ## 依赖

View File

@@ -342,7 +342,7 @@ public ResultDomain<User> register(@RequestBody Map<String, Object> params) {
public class CustomValidateMethod implements ValidateMethod { public class CustomValidateMethod implements ValidateMethod {
@Override @Override
public boolean validate(Object value) { public Boolean validate(Object value) {
// 实现校验逻辑 // 实现校验逻辑
return true; return true;
} }

View File

@@ -27,7 +27,7 @@ public class ValidationParam {
/** /**
* @description 是否必传 * @description 是否必传
*/ */
private boolean required; private Boolean required;
/** /**
* @description 字段类型 * @description 字段类型
@@ -77,7 +77,7 @@ public class ValidationParam {
/** /**
* @description 是否允许为空字符串(默认不允许) * @description 是否允许为空字符串(默认不允许)
*/ */
private boolean allowEmpty = false; private Boolean allowEmpty = false;
/** /**
* @description 校验方法(使用预定义的校验方法) * @description 校验方法(使用预定义的校验方法)
@@ -106,7 +106,7 @@ public class ValidationParam {
return fieldLabel; return fieldLabel;
} }
public boolean isRequired() { public Boolean isRequired() {
return required; return required;
} }
@@ -146,7 +146,7 @@ public class ValidationParam {
return customErrorMessage; return customErrorMessage;
} }
public boolean isAllowEmpty() { public Boolean isAllowEmpty() {
return allowEmpty; return allowEmpty;
} }
@@ -178,7 +178,7 @@ public class ValidationParam {
return this; return this;
} }
public Builder required(boolean required) { public Builder required(Boolean required) {
param.required = required; param.required = required;
return this; return this;
} }
@@ -233,7 +233,7 @@ public class ValidationParam {
return this; return this;
} }
public Builder allowEmpty(boolean allowEmpty) { public Builder allowEmpty(Boolean allowEmpty) {
param.allowEmpty = allowEmpty; param.allowEmpty = allowEmpty;
return this; return this;
} }

View File

@@ -15,7 +15,7 @@ public class ValidationResult {
/** /**
* @description 是否校验通过 * @description 是否校验通过
*/ */
private boolean valid; private Boolean valid;
/** /**
* @description 错误信息列表 * @description 错误信息列表
@@ -32,11 +32,11 @@ public class ValidationResult {
this.errors = new ArrayList<>(); this.errors = new ArrayList<>();
} }
public boolean isValid() { public Boolean isValid() {
return valid; return valid;
} }
public void setValid(boolean valid) { public void setValid(Boolean valid) {
this.valid = valid; this.valid = valid;
} }
@@ -70,9 +70,9 @@ public class ValidationResult {
/** /**
* @description 是否有错误 * @description 是否有错误
* @return boolean * @return Boolean
*/ */
public boolean hasErrors() { public Boolean hasErrors() {
return !valid; return !valid;
} }

View File

@@ -289,7 +289,7 @@ public class ValidationUtils {
* @param required 是否必填 * @param required 是否必填
* @return ValidationParam * @return ValidationParam
*/ */
public static ValidationParam email(String fieldName, String fieldLabel, boolean required) { public static ValidationParam email(String fieldName, String fieldLabel, Boolean required) {
return ValidationParam.builder() return ValidationParam.builder()
.fieldName(fieldName) .fieldName(fieldName)
.fieldLabel(fieldLabel) .fieldLabel(fieldLabel)
@@ -307,7 +307,7 @@ public class ValidationUtils {
* @param required 是否必填 * @param required 是否必填
* @return ValidationParam * @return ValidationParam
*/ */
public static ValidationParam phone(String fieldName, String fieldLabel, boolean required) { public static ValidationParam phone(String fieldName, String fieldLabel, Boolean required) {
return ValidationParam.builder() return ValidationParam.builder()
.fieldName(fieldName) .fieldName(fieldName)
.fieldLabel(fieldLabel) .fieldLabel(fieldLabel)

View File

@@ -10,7 +10,7 @@ package org.xyzh.common.utils.validation.method;
public class BankCardValidateMethod implements ValidateMethod { public class BankCardValidateMethod implements ValidateMethod {
@Override @Override
public boolean validate(Object value) { public Boolean validate(Object value) {
if (value == null || !(value instanceof String)) { if (value == null || !(value instanceof String)) {
return false; return false;
} }
@@ -34,11 +34,11 @@ public class BankCardValidateMethod implements ValidateMethod {
/** /**
* @description Luhn算法校验银行卡校验算法 * @description Luhn算法校验银行卡校验算法
* @param cardNumber 银行卡号 * @param cardNumber 银行卡号
* @return boolean 是否通过校验 * @return Boolean 是否通过校验
*/ */
private boolean luhnCheck(String cardNumber) { private Boolean luhnCheck(String cardNumber) {
int sum = 0; int sum = 0;
boolean alternate = false; Boolean alternate = false;
// 从右向左遍历 // 从右向左遍历
for (int i = cardNumber.length() - 1; i >= 0; i--) { for (int i = cardNumber.length() - 1; i >= 0; i--) {

View File

@@ -14,18 +14,18 @@ public class ChineseValidateMethod implements ValidateMethod {
// 中文字符正则(包括中文标点符号) // 中文字符正则(包括中文标点符号)
private static final Pattern CHINESE_PATTERN = Pattern.compile("^[\u4e00-\u9fa5]+$"); private static final Pattern CHINESE_PATTERN = Pattern.compile("^[\u4e00-\u9fa5]+$");
private final boolean allowPunctuation; // 是否允许中文标点符号 private final Boolean allowPunctuation; // 是否允许中文标点符号
public ChineseValidateMethod() { public ChineseValidateMethod() {
this.allowPunctuation = false; this.allowPunctuation = false;
} }
public ChineseValidateMethod(boolean allowPunctuation) { public ChineseValidateMethod(Boolean allowPunctuation) {
this.allowPunctuation = allowPunctuation; this.allowPunctuation = allowPunctuation;
} }
@Override @Override
public boolean validate(Object value) { public Boolean validate(Object value) {
if (value == null || !(value instanceof String)) { if (value == null || !(value instanceof String)) {
return false; return false;
} }

View File

@@ -34,7 +34,7 @@ public class EmailValidateMethod implements ValidateMethod {
} }
@Override @Override
public boolean validate(Object value) { public Boolean validate(Object value) {
if (value == null || !(value instanceof String)) { if (value == null || !(value instanceof String)) {
return false; return false;
} }
@@ -48,7 +48,7 @@ public class EmailValidateMethod implements ValidateMethod {
// 域名限制校验 // 域名限制校验
if (allowedDomains != null && allowedDomains.length > 0) { if (allowedDomains != null && allowedDomains.length > 0) {
boolean domainMatched = false; Boolean domainMatched = false;
for (String domain : allowedDomains) { for (String domain : allowedDomains) {
if (email.endsWith("@" + domain.toLowerCase())) { if (email.endsWith("@" + domain.toLowerCase())) {
domainMatched = true; domainMatched = true;

View File

@@ -60,7 +60,7 @@ public class IdCardValidateMethod implements ValidateMethod {
} }
@Override @Override
public boolean validate(Object value) { public Boolean validate(Object value) {
if (value == null || !(value instanceof String)) { if (value == null || !(value instanceof String)) {
return false; return false;
} }
@@ -83,7 +83,7 @@ public class IdCardValidateMethod implements ValidateMethod {
/** /**
* @description 校验15位身份证 * @description 校验15位身份证
*/ */
private boolean validate15IdCard(String idCard) { private Boolean validate15IdCard(String idCard) {
// 15位身份证格式省(2位)市(2位)县(2位)年(2位)月(2位)日(2位)顺序号(3位) // 15位身份证格式省(2位)市(2位)县(2位)年(2位)月(2位)日(2位)顺序号(3位)
if (!Pattern.matches("^\\d{15}$", idCard)) { if (!Pattern.matches("^\\d{15}$", idCard)) {
return false; return false;
@@ -106,7 +106,7 @@ public class IdCardValidateMethod implements ValidateMethod {
/** /**
* @description 校验18位身份证 * @description 校验18位身份证
*/ */
private boolean validate18IdCard(String idCard) { private Boolean validate18IdCard(String idCard) {
// 18位身份证格式省(2位)市(2位)县(2位)年(4位)月(2位)日(2位)顺序号(3位)校验码(1位) // 18位身份证格式省(2位)市(2位)县(2位)年(4位)月(2位)日(2位)顺序号(3位)校验码(1位)
if (!Pattern.matches("^\\d{17}[0-9Xx]$", idCard)) { if (!Pattern.matches("^\\d{17}[0-9Xx]$", idCard)) {
return false; return false;
@@ -134,7 +134,7 @@ public class IdCardValidateMethod implements ValidateMethod {
/** /**
* @description 校验日期是否合法 * @description 校验日期是否合法
*/ */
private boolean validateDate(String year, String month, String day) { private Boolean validateDate(String year, String month, String day) {
try { try {
int y = Integer.parseInt(year); int y = Integer.parseInt(year);
int m = Integer.parseInt(month); int m = Integer.parseInt(month);
@@ -168,14 +168,14 @@ public class IdCardValidateMethod implements ValidateMethod {
/** /**
* @description 判断是否为闰年 * @description 判断是否为闰年
*/ */
private boolean isLeapYear(int year) { private Boolean isLeapYear(int year) {
return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0); return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
} }
/** /**
* @description 校验18位身份证的校验码 * @description 校验18位身份证的校验码
*/ */
private boolean validateCheckCode(String idCard) { private Boolean validateCheckCode(String idCard) {
int sum = 0; int sum = 0;
for (int i = 0; i < 17; i++) { for (int i = 0; i < 17; i++) {
sum += (idCard.charAt(i) - '0') * WEIGHT[i]; sum += (idCard.charAt(i) - '0') * WEIGHT[i];

View File

@@ -13,10 +13,10 @@ public class PasswordValidateMethod implements ValidateMethod {
private final int minLength; private final int minLength;
private final int maxLength; private final int maxLength;
private final boolean requireUpperCase; private final Boolean requireUpperCase;
private final boolean requireLowerCase; private final Boolean requireLowerCase;
private final boolean requireDigit; private final Boolean requireDigit;
private final boolean requireSpecialChar; private final Boolean requireSpecialChar;
/** /**
* @description 默认密码规则6-20位必须包含字母和数字 * @description 默认密码规则6-20位必须包含字母和数字
@@ -35,8 +35,8 @@ public class PasswordValidateMethod implements ValidateMethod {
* @param requireSpecialChar 是否需要特殊字符 * @param requireSpecialChar 是否需要特殊字符
*/ */
public PasswordValidateMethod(int minLength, int maxLength, public PasswordValidateMethod(int minLength, int maxLength,
boolean requireUpperCase, boolean requireLowerCase, Boolean requireUpperCase, Boolean requireLowerCase,
boolean requireDigit, boolean requireSpecialChar) { Boolean requireDigit, Boolean requireSpecialChar) {
this.minLength = minLength; this.minLength = minLength;
this.maxLength = maxLength; this.maxLength = maxLength;
this.requireUpperCase = requireUpperCase; this.requireUpperCase = requireUpperCase;
@@ -46,7 +46,7 @@ public class PasswordValidateMethod implements ValidateMethod {
} }
@Override @Override
public boolean validate(Object value) { public Boolean validate(Object value) {
if (value == null || !(value instanceof String)) { if (value == null || !(value instanceof String)) {
return false; return false;
} }
@@ -88,7 +88,7 @@ public class PasswordValidateMethod implements ValidateMethod {
if (requireUpperCase || requireLowerCase || requireDigit || requireSpecialChar) { if (requireUpperCase || requireLowerCase || requireDigit || requireSpecialChar) {
msg.append(",且包含"); msg.append(",且包含");
boolean first = true; Boolean first = true;
if (requireUpperCase) { if (requireUpperCase) {
msg.append("大写字母"); msg.append("大写字母");

View File

@@ -20,18 +20,18 @@ public class PhoneValidateMethod implements ValidateMethod {
// 台湾手机号正则 // 台湾手机号正则
private static final Pattern TW_PHONE_PATTERN = Pattern.compile("^09\\d{8}$"); private static final Pattern TW_PHONE_PATTERN = Pattern.compile("^09\\d{8}$");
private final boolean strictMode; // 严格模式,只验证中国大陆手机号 private final Boolean strictMode; // 严格模式,只验证中国大陆手机号
public PhoneValidateMethod() { public PhoneValidateMethod() {
this.strictMode = true; this.strictMode = true;
} }
public PhoneValidateMethod(boolean strictMode) { public PhoneValidateMethod(Boolean strictMode) {
this.strictMode = strictMode; this.strictMode = strictMode;
} }
@Override @Override
public boolean validate(Object value) { public Boolean validate(Object value) {
if (value == null || !(value instanceof String)) { if (value == null || !(value instanceof String)) {
return false; return false;
} }

View File

@@ -16,18 +16,18 @@ public class UrlValidateMethod implements ValidateMethod {
"^(https?|ftp)://[a-zA-Z0-9+&@#/%?=~_|!:,.;-]*[a-zA-Z0-9+&@#/%=~_|-]$" "^(https?|ftp)://[a-zA-Z0-9+&@#/%?=~_|!:,.;-]*[a-zA-Z0-9+&@#/%=~_|-]$"
); );
private final boolean requireHttps; // 是否要求HTTPS private final Boolean requireHttps; // 是否要求HTTPS
public UrlValidateMethod() { public UrlValidateMethod() {
this.requireHttps = false; this.requireHttps = false;
} }
public UrlValidateMethod(boolean requireHttps) { public UrlValidateMethod(Boolean requireHttps) {
this.requireHttps = requireHttps; this.requireHttps = requireHttps;
} }
@Override @Override
public boolean validate(Object value) { public Boolean validate(Object value) {
if (value == null || !(value instanceof String)) { if (value == null || !(value instanceof String)) {
return false; return false;
} }

View File

@@ -12,9 +12,9 @@ public interface ValidateMethod {
/** /**
* @description 校验方法 * @description 校验方法
* @param value 待校验的值 * @param value 待校验的值
* @return boolean 是否校验通过 * @return Boolean 是否校验通过
*/ */
boolean validate(Object value); Boolean validate(Object value);
/** /**
* @description 获取校验失败的错误提示信息 * @description 获取校验失败的错误提示信息

View File

@@ -0,0 +1,13 @@
FROM maven:3.9-eclipse-temurin-21-alpine
WORKDIR /app
# 复制 pom.xml 和源代码
COPY pom.xml ./
COPY src ./src
# 暴露端口
EXPOSE 8080
# 使用 spring-boot-devtools 支持热重载
CMD ["mvn", "spring-boot:run", "-Dspring-boot.run.jvmArguments=-Dspring.devtools.restart.enabled=true"]

View File

@@ -0,0 +1,121 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.xyzh</groupId>
<artifactId>urban-lifeline</artifactId>
<version>1.0.0</version>
</parent>
<groupId>org.xyzh</groupId>
<artifactId>gateway</artifactId>
<version>${urban-lifeline.version}</version>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
</properties>
<dependencies>
<!-- Spring Cloud Gateway -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- Nacos 服务注册与发现 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- Nacos 配置中心 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!-- 负载均衡 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<!-- 认证模块JWT验证 -->
<dependency>
<groupId>org.xyzh.common</groupId>
<artifactId>common-auth</artifactId>
</dependency>
<!-- Redis用于Token验证、限流等 -->
<dependency>
<groupId>org.xyzh.common</groupId>
<artifactId>common-redis</artifactId>
</dependency>
<!-- 工具类 -->
<dependency>
<groupId>org.xyzh.common</groupId>
<artifactId>common-utils</artifactId>
</dependency>
<!-- 核心模块 -->
<dependency>
<groupId>org.xyzh.common</groupId>
<artifactId>common-core</artifactId>
</dependency>
<!-- Spring Boot Actuator健康检查 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- Log4j2 日志 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<!-- WebFluxGateway依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<!-- 配置处理 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!-- FastJson -->
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>org.xyzh.gateway.GatewayApplication</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,21 @@
package org.xyzh.gateway;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
* @description Gateway 网关启动类
* @author yslg
* @since 2025-12-02
*/
@SpringBootApplication
@EnableDiscoveryClient
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
System.out.println("========================================");
System.out.println(" Gateway 网关服务启动成功!");
System.out.println("========================================");
}
}

View File

@@ -0,0 +1,78 @@
package org.xyzh.gateway.config;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @description Gateway 动态路由配置Java 代码方式)
* @author yslg
* @since 2025-12-02
*
* 这是路由配置的第二种方式,与 application.yml 中的配置二选一或配合使用
* 优点:灵活性更高,可以动态添加/修改路由,支持更复杂的路由逻辑
*/
@Configuration
public class GatewayRouteConfig {
/**
* 通过代码方式配置路由(示例,可根据需要启用)
*/
// @Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
// 认证服务路由
.route("auth-service", r -> r
.path("/auth/**")
.filters(f -> f
.stripPrefix(1)
.addRequestHeader("X-Gateway", "gateway-service")
)
.uri("lb://auth-service")
)
// 系统服务路由
.route("system-service", r -> r
.path("/system/**")
.filters(f -> f
.stripPrefix(1)
.addRequestHeader("X-Gateway", "gateway-service")
)
.uri("lb://system-service")
)
// 日志服务路由
.route("log-service", r -> r
.path("/log/**")
.filters(f -> f
.stripPrefix(1)
.addRequestHeader("X-Gateway", "gateway-service")
)
.uri("lb://log-service")
)
// 文件服务路由
.route("file-service", r -> r
.path("/file/**")
.filters(f -> f
.stripPrefix(1)
.addRequestHeader("X-Gateway", "gateway-service")
)
.uri("lb://file-service")
)
// WebSocket 路由示例
.route("websocket-route", r -> r
.path("/ws/**")
.uri("lb:ws://system-service")
)
.build();
}
/**
* 动态路由刷新端点(如需要)
* 可以配合 Nacos 配置中心实现动态路由更新
*/
}

View File

@@ -0,0 +1,47 @@
package org.xyzh.gateway.config;
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import reactor.core.publisher.Mono;
/**
* @description 限流配置 - 基于 Redis 的令牌桶算法
* @author yslg
* @since 2025-12-02
*/
@Configuration
public class RateLimiterConfig {
/**
* 基于 IP 地址的限流
*/
@Bean
public KeyResolver ipKeyResolver() {
return exchange -> {
String ip = exchange.getRequest().getRemoteAddress() != null
? exchange.getRequest().getRemoteAddress().getAddress().getHostAddress()
: "unknown";
return Mono.just(ip);
};
}
/**
* 基于用户 ID 的限流(从请求头获取)
*/
// @Bean
public KeyResolver userKeyResolver() {
return exchange -> {
String userId = exchange.getRequest().getHeaders().getFirst("X-User-Id");
return Mono.just(userId != null ? userId : "anonymous");
};
}
/**
* 基于 API 路径的限流
*/
// @Bean
public KeyResolver apiKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getPath().value());
}
}

View File

@@ -0,0 +1,177 @@
package org.xyzh.gateway.filter;
import com.alibaba.fastjson2.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import org.xyzh.common.auth.config.AuthProperties;
import org.xyzh.common.auth.contants.AuthContants;
import org.xyzh.common.auth.token.TokenParser;
import org.xyzh.common.core.domain.ResultDomain;
import reactor.core.publisher.Mono;
import java.nio.charset.StandardCharsets;
import java.util.List;
/**
* @description Gateway 全局认证过滤器 - 用于验证 JWT Token
* @author yslg
* @since 2025-12-02
*/
@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {
private static final Logger log = LoggerFactory.getLogger(AuthGlobalFilter.class);
@Autowired
private TokenParser tokenParser;
@Autowired
private AuthProperties authProperties;
private final AntPathMatcher pathMatcher = new AntPathMatcher();
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String path = request.getURI().getPath();
log.debug("Gateway 请求路径: {}", path);
// 1. 检查认证功能是否启用
if (!authProperties.isEnabled()) {
log.debug("认证功能未启用,直接放行");
return chain.filter(exchange);
}
// 2. 检查是否在白名单中
if (isWhitelisted(path)) {
log.debug("路径在白名单中,跳过认证: {}", path);
return chain.filter(exchange);
}
// 3. 提取 Token
String token = extractToken(request);
if (!StringUtils.hasText(token)) {
log.warn("请求缺少 Token: {}", path);
return unauthorizedResponse(exchange, "未提供认证令牌,请先登录");
}
try {
// 4. 验证 Token
if (tokenParser.isTokenExpired(token)) {
log.warn("Token 已过期: {}", path);
return unauthorizedResponse(exchange, "认证令牌已过期,请重新登录");
}
// 5. 获取用户信息
String userId = tokenParser.getUserIdFromToken(token);
if (!StringUtils.hasText(userId)) {
log.warn("Token 中未找到用户ID: {}", path);
return unauthorizedResponse(exchange, "认证令牌无效");
}
// 6. 验证 Token 有效性
if (!tokenParser.validateToken(token, userId)) {
log.warn("Token 验证失败: userId={}, path={}", userId, path);
return unauthorizedResponse(exchange, "认证令牌验证失败");
}
// 7. 将用户信息添加到请求头中,传递给下游服务
ServerHttpRequest mutatedRequest = request.mutate()
.header(AuthContants.USER_ID_ATTRIBUTE, userId)
.header(AuthContants.TOKEN_ATTRIBUTE, token)
.build();
log.debug("Token 验证成功: userId={}, path={}", userId, path);
// 8. 继续执行过滤器链
return chain.filter(exchange.mutate().request(mutatedRequest).build());
} catch (Exception e) {
log.error("Token 解析或验证异常: path={}, error={}", path, e.getMessage(), e);
return unauthorizedResponse(exchange, "认证令牌解析失败: " + e.getMessage());
}
}
/**
* 检查路径是否在白名单中
*/
private boolean isWhitelisted(String path) {
// 1. 检查认证相关接口
if (authProperties.getAuthPaths() != null) {
for (String pattern : authProperties.getAuthPaths()) {
if (pattern != null && pathMatcher.match(pattern, path)) {
return true;
}
}
}
// 2. 检查通用白名单
if (authProperties.getWhitelist() != null) {
for (String pattern : authProperties.getWhitelist()) {
if (pattern != null && pathMatcher.match(pattern, path)) {
return true;
}
}
}
return false;
}
/**
* 从请求头中提取 Token
*/
private String extractToken(ServerHttpRequest request) {
List<String> headers = request.getHeaders().get(authProperties.getTokenHeader());
if (headers == null || headers.isEmpty()) {
return null;
}
String header = headers.get(0);
if (!StringUtils.hasText(header)) {
return null;
}
// 支持 Bearer 前缀
String prefix = authProperties.getTokenPrefix();
if (StringUtils.hasText(prefix) && header.startsWith(prefix)) {
return header.substring(prefix.length()).trim();
}
// 也支持直接传递 Token不带前缀
return header.trim();
}
/**
* 返回未授权响应
*/
private Mono<Void> unauthorizedResponse(ServerWebExchange exchange, String message) {
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.UNAUTHORIZED);
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
ResultDomain<Object> result = ResultDomain.failure(HttpStatus.UNAUTHORIZED.value(), message);
String json = JSON.toJSONString(result);
DataBuffer buffer = response.bufferFactory().wrap(json.getBytes(StandardCharsets.UTF_8));
return response.writeWith(Mono.just(buffer));
}
@Override
public int getOrder() {
// 优先级设置为最高,确保在其他过滤器之前执行
return Ordered.HIGHEST_PRECEDENCE;
}
}

View File

@@ -0,0 +1,54 @@
package org.xyzh.gateway.filter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
* @description Gateway 全局日志过滤器 - 记录请求信息
* @author yslg
* @since 2025-12-02
*/
@Component
public class LogGlobalFilter implements GlobalFilter, Ordered {
private static final Logger log = LoggerFactory.getLogger(LogGlobalFilter.class);
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
long startTime = System.currentTimeMillis();
String path = request.getURI().getPath();
String method = request.getMethod().name();
String remoteAddress = request.getRemoteAddress() != null
? request.getRemoteAddress().getAddress().getHostAddress()
: "unknown";
log.info("==> Gateway 请求: {} {} | 来源: {}", method, path, remoteAddress);
return chain.filter(exchange).then(
Mono.fromRunnable(() -> {
long endTime = System.currentTimeMillis();
long duration = endTime - startTime;
int statusCode = exchange.getResponse().getStatusCode() != null
? exchange.getResponse().getStatusCode().value()
: 0;
log.info("<== Gateway 响应: {} {} | 状态: {} | 耗时: {}ms",
method, path, statusCode, duration);
})
);
}
@Override
public int getOrder() {
// 优先级设置较低,在认证过滤器之后执行
return Ordered.LOWEST_PRECEDENCE;
}
}

View File

@@ -0,0 +1,70 @@
package org.xyzh.gateway.handler;
import com.alibaba.fastjson2.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
import org.springframework.cloud.gateway.support.NotFoundException;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.server.ServerWebExchange;
import org.xyzh.common.core.domain.ResultDomain;
import reactor.core.publisher.Mono;
import java.nio.charset.StandardCharsets;
/**
* @description Gateway 全局异常处理器
* @author yslg
* @since 2025-12-02
*/
@Component
@Order(-1) // 优先级设置为最高
public class GatewayExceptionHandler implements ErrorWebExceptionHandler {
private static final Logger log = LoggerFactory.getLogger(GatewayExceptionHandler.class);
@Override
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
ServerHttpResponse response = exchange.getResponse();
if (response.isCommitted()) {
return Mono.error(ex);
}
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
ResultDomain<Object> result;
HttpStatus httpStatus;
// 根据不同异常类型返回不同的错误信息
if (ex instanceof NotFoundException) {
log.error("服务未找到: {}", ex.getMessage());
httpStatus = HttpStatus.SERVICE_UNAVAILABLE;
result = ResultDomain.failure(httpStatus.value(), "服务暂时不可用: " + ex.getMessage());
} else if (ex instanceof ResponseStatusException rse) {
log.error("响应状态异常: {}", rse.getMessage());
httpStatus = (HttpStatus) rse.getStatusCode();
result = ResultDomain.failure(httpStatus.value(), rse.getReason());
} else if (ex instanceof IllegalStateException) {
log.error("非法状态异常: {}", ex.getMessage());
httpStatus = HttpStatus.BAD_REQUEST;
result = ResultDomain.failure(httpStatus.value(), "请求参数错误: " + ex.getMessage());
} else {
log.error("Gateway 内部错误", ex);
httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;
result = ResultDomain.failure(httpStatus.value(), "网关内部错误,请稍后重试");
}
response.setStatusCode(httpStatus);
String json = JSON.toJSONString(result);
DataBuffer buffer = response.bufferFactory().wrap(json.getBytes(StandardCharsets.UTF_8));
return response.writeWith(Mono.just(buffer));
}
}

View File

@@ -0,0 +1,19 @@
spring:
cloud:
gateway:
routes:
# 开发环境可以添加更详细的路由配置或测试路由
# Nacos 管理界面路由(开发专用)
- id: nacos-console
uri: http://${NACOS_SERVER_ADDR:localhost:8848}
predicates:
- Path=/nacos/**
# 开发环境日志
logging:
level:
org.springframework.cloud.gateway: DEBUG
org.springframework.web.reactive: DEBUG
reactor.netty: DEBUG
org.xyzh: DEBUG

View File

@@ -0,0 +1,133 @@
server:
port: 8080
spring:
application:
name: gateway-service
# 配置中心
cloud:
nacos:
discovery:
server-addr: ${NACOS_SERVER_ADDR:localhost:8848}
namespace: dev
group: DEFAULT_GROUP
config:
server-addr: ${NACOS_SERVER_ADDR:localhost:8848}
file-extension: yml
namespace: dev
group: DEFAULT_GROUP
# Gateway 路由配置
gateway:
# 服务发现路由(自动路由)
discovery:
locator:
enabled: false # 关闭自动路由,使用手动配置
# 手动配置路由
routes:
# ==================== 认证服务路由 ====================
- id: auth-service
uri: lb://auth-service
predicates:
- Path=/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=/system/**
filters:
- StripPrefix=1
# ==================== 日志服务路由 ====================
- id: log-service
uri: lb://log-service
predicates:
- Path=/log/**
filters:
- StripPrefix=1
# ==================== 文件服务路由 ====================
- id: file-service
uri: lb://file-service
predicates:
- Path=/file/**
filters:
- StripPrefix=1
# 全局跨域配置
globalcors:
cors-configurations:
'[/**]':
allowedOriginPatterns: "*"
allowedMethods:
- GET
- POST
- PUT
- DELETE
- OPTIONS
allowedHeaders: "*"
allowCredentials: true
maxAge: 3600
# Redis 配置(用于限流、缓存)
data:
redis:
host: ${REDIS_HOST:localhost}
port: ${REDIS_PORT:6379}
database: 0
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/logout/captcha/refresh
auth-paths:
- /auth/login
- /auth/logout
- /auth/captcha
- /auth/refresh
# 通用白名单Swagger、健康检查等
whitelist:
- /actuator/**
- /v3/api-docs/**
- /swagger-ui/**
- /swagger-resources/**
- /webjars/**
- /doc.html
- /favicon.ico
- /error
# Actuator 监控端点
management:
endpoints:
web:
exposure:
include: health,info,gateway
endpoint:
health:
show-details: always
# 日志配置
logging:
level:
org.springframework.cloud.gateway: DEBUG
org.xyzh.gateway: DEBUG
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n"

View File

@@ -10,6 +10,7 @@
<modules> <modules>
<module>common</module> <module>common</module>
<module>apis</module> <module>apis</module>
<module>gateway</module>
<module>log</module> <module>log</module>
<module>system</module> <module>system</module>
<module>auth</module> <module>auth</module>

View File

@@ -12,8 +12,8 @@
<result column="principal_id" property="principalId" jdbcType="VARCHAR"/> <result column="principal_id" property="principalId" jdbcType="VARCHAR"/>
<result column="principal_dept_id" property="principalDeptId" jdbcType="VARCHAR"/> <result column="principal_dept_id" property="principalDeptId" jdbcType="VARCHAR"/>
<result column="permission" property="permission" jdbcType="VARCHAR"/> <result column="permission" property="permission" jdbcType="VARCHAR"/>
<result column="allow" property="allow" jdbcType="BOOLEAN"/> <result column="allow" property="allow" jdbcType="Boolean"/>
<result column="include_descendants" property="includeDescendants" jdbcType="BOOLEAN"/> <result column="include_descendants" property="includeDescendants" jdbcType="Boolean"/>
<!-- 基础字段 --> <!-- 基础字段 -->
<result column="optsn" property="optsn" jdbcType="VARCHAR"/> <result column="optsn" property="optsn" jdbcType="VARCHAR"/>
<result column="creator" property="creator" jdbcType="VARCHAR"/> <result column="creator" property="creator" jdbcType="VARCHAR"/>
@@ -23,7 +23,7 @@
<result column="create_time" property="createTime" jdbcType="DATE"/> <result column="create_time" property="createTime" jdbcType="DATE"/>
<result column="update_time" property="updateTime" jdbcType="DATE"/> <result column="update_time" property="updateTime" jdbcType="DATE"/>
<result column="delete_time" property="deleteTime" jdbcType="DATE"/> <result column="delete_time" property="deleteTime" jdbcType="DATE"/>
<result column="deleted" property="deleted" jdbcType="BOOLEAN"/> <result column="deleted" property="deleted" jdbcType="Boolean"/>
</resultMap> </resultMap>
<!-- VO结果映射用于前端展示 --> <!-- VO结果映射用于前端展示 -->
@@ -35,9 +35,9 @@
<result column="principal_type" property="principalType" jdbcType="VARCHAR"/> <result column="principal_type" property="principalType" jdbcType="VARCHAR"/>
<result column="principal_id" property="principalId" jdbcType="VARCHAR"/> <result column="principal_id" property="principalId" jdbcType="VARCHAR"/>
<result column="principal_dept_id" property="principalDeptId" jdbcType="VARCHAR"/> <result column="principal_dept_id" property="principalDeptId" jdbcType="VARCHAR"/>
<result column="permission" property="permission" jdbcType="INTEGER"/> <result column="permission" property="permission" jdbcType="Integer"/>
<result column="allow" property="allow" jdbcType="BOOLEAN"/> <result column="allow" property="allow" jdbcType="Boolean"/>
<result column="include_descendants" property="includeDescendants" jdbcType="BOOLEAN"/> <result column="include_descendants" property="includeDescendants" jdbcType="Boolean"/>
<!-- BaseVO 基础字段 --> <!-- BaseVO 基础字段 -->
<result column="optsn" property="optsn" jdbcType="VARCHAR"/> <result column="optsn" property="optsn" jdbcType="VARCHAR"/>
<result column="creator" property="creator" jdbcType="VARCHAR"/> <result column="creator" property="creator" jdbcType="VARCHAR"/>
@@ -47,7 +47,7 @@
<result column="create_time" property="createTime" jdbcType="DATE"/> <result column="create_time" property="createTime" jdbcType="DATE"/>
<result column="update_time" property="updateTime" jdbcType="DATE"/> <result column="update_time" property="updateTime" jdbcType="DATE"/>
<result column="delete_time" property="deleteTime" jdbcType="DATE"/> <result column="delete_time" property="deleteTime" jdbcType="DATE"/>
<result column="deleted" property="deleted" jdbcType="BOOLEAN"/> <result column="deleted" property="deleted" jdbcType="Boolean"/>
</resultMap> </resultMap>
<!-- 基础列 --> <!-- 基础列 -->

View File

@@ -10,9 +10,9 @@
<result column="object_type" property="objectType" jdbcType="VARCHAR"/> <result column="object_type" property="objectType" jdbcType="VARCHAR"/>
<result column="edit_hierarchy_rule" property="editHierarchyRule" jdbcType="VARCHAR"/> <result column="edit_hierarchy_rule" property="editHierarchyRule" jdbcType="VARCHAR"/>
<result column="view_hierarchy_rule" property="viewHierarchyRule" jdbcType="VARCHAR"/> <result column="view_hierarchy_rule" property="viewHierarchyRule" jdbcType="VARCHAR"/>
<result column="default_permission" property="defaultPermission" jdbcType="INTEGER"/> <result column="default_permission" property="defaultPermission" jdbcType="Integer"/>
<result column="default_allow" property="defaultAllow" jdbcType="BOOLEAN"/> <result column="default_allow" property="defaultAllow" jdbcType="Boolean"/>
<result column="apply_to_children" property="applyToChildren" jdbcType="BOOLEAN"/> <result column="apply_to_children" property="applyToChildren" jdbcType="Boolean"/>
<!-- 基础字段 --> <!-- 基础字段 -->
<result column="optsn" property="optsn" jdbcType="VARCHAR"/> <result column="optsn" property="optsn" jdbcType="VARCHAR"/>
<result column="creator" property="creator" jdbcType="VARCHAR"/> <result column="creator" property="creator" jdbcType="VARCHAR"/>
@@ -22,7 +22,7 @@
<result column="create_time" property="createTime" jdbcType="DATE"/> <result column="create_time" property="createTime" jdbcType="DATE"/>
<result column="update_time" property="updateTime" jdbcType="DATE"/> <result column="update_time" property="updateTime" jdbcType="DATE"/>
<result column="delete_time" property="deleteTime" jdbcType="DATE"/> <result column="delete_time" property="deleteTime" jdbcType="DATE"/>
<result column="deleted" property="deleted" jdbcType="BOOLEAN"/> <result column="deleted" property="deleted" jdbcType="Boolean"/>
</resultMap> </resultMap>
<!-- VO结果映射用于前端展示 --> <!-- VO结果映射用于前端展示 -->
@@ -33,9 +33,9 @@
<result column="object_type" property="policyObjectType" jdbcType="VARCHAR"/> <result column="object_type" property="policyObjectType" jdbcType="VARCHAR"/>
<result column="edit_hierarchy_rule" property="editHierarchyRule" jdbcType="VARCHAR"/> <result column="edit_hierarchy_rule" property="editHierarchyRule" jdbcType="VARCHAR"/>
<result column="view_hierarchy_rule" property="viewHierarchyRule" jdbcType="VARCHAR"/> <result column="view_hierarchy_rule" property="viewHierarchyRule" jdbcType="VARCHAR"/>
<result column="default_permission" property="defaultPermission" jdbcType="INTEGER"/> <result column="default_permission" property="defaultPermission" jdbcType="Integer"/>
<result column="default_allow" property="defaultAllow" jdbcType="BOOLEAN"/> <result column="default_allow" property="defaultAllow" jdbcType="Boolean"/>
<result column="apply_to_children" property="applyToChildren" jdbcType="BOOLEAN"/> <result column="apply_to_children" property="applyToChildren" jdbcType="Boolean"/>
<!-- BaseVO 基础字段 --> <!-- BaseVO 基础字段 -->
<result column="optsn" property="optsn" jdbcType="VARCHAR"/> <result column="optsn" property="optsn" jdbcType="VARCHAR"/>
<result column="creator" property="creator" jdbcType="VARCHAR"/> <result column="creator" property="creator" jdbcType="VARCHAR"/>
@@ -45,7 +45,7 @@
<result column="create_time" property="createTime" jdbcType="DATE"/> <result column="create_time" property="createTime" jdbcType="DATE"/>
<result column="update_time" property="updateTime" jdbcType="DATE"/> <result column="update_time" property="updateTime" jdbcType="DATE"/>
<result column="delete_time" property="deleteTime" jdbcType="DATE"/> <result column="delete_time" property="deleteTime" jdbcType="DATE"/>
<result column="deleted" property="deleted" jdbcType="BOOLEAN"/> <result column="deleted" property="deleted" jdbcType="Boolean"/>
</resultMap> </resultMap>
<!-- 基础列 --> <!-- 基础列 -->

View File

@@ -16,8 +16,8 @@
<result column="options" property="options" jdbcType="OTHER"/> <result column="options" property="options" jdbcType="OTHER"/>
<result column="group" property="group" jdbcType="VARCHAR"/> <result column="group" property="group" jdbcType="VARCHAR"/>
<result column="module_id" property="moduleId" jdbcType="VARCHAR"/> <result column="module_id" property="moduleId" jdbcType="VARCHAR"/>
<result column="order_num" property="orderNum" jdbcType="INTEGER"/> <result column="order_num" property="orderNum" jdbcType="Integer"/>
<result column="status" property="status" jdbcType="INTEGER"/> <result column="status" property="status" jdbcType="Integer"/>
<!-- 基础字段 --> <!-- 基础字段 -->
<result column="optsn" property="optsn" jdbcType="VARCHAR"/> <result column="optsn" property="optsn" jdbcType="VARCHAR"/>
<result column="creator" property="creator" jdbcType="VARCHAR"/> <result column="creator" property="creator" jdbcType="VARCHAR"/>
@@ -27,7 +27,7 @@
<result column="create_time" property="createTime" jdbcType="DATE"/> <result column="create_time" property="createTime" jdbcType="DATE"/>
<result column="update_time" property="updateTime" jdbcType="DATE"/> <result column="update_time" property="updateTime" jdbcType="DATE"/>
<result column="delete_time" property="deleteTime" jdbcType="DATE"/> <result column="delete_time" property="deleteTime" jdbcType="DATE"/>
<result column="deleted" property="deleted" jdbcType="BOOLEAN"/> <result column="deleted" property="deleted" jdbcType="Boolean"/>
</resultMap> </resultMap>
<!-- VO结果映射用于前端展示 --> <!-- VO结果映射用于前端展示 -->
@@ -44,8 +44,8 @@
<result column="options" property="options" jdbcType="OTHER"/> <result column="options" property="options" jdbcType="OTHER"/>
<result column="group" property="group" jdbcType="VARCHAR"/> <result column="group" property="group" jdbcType="VARCHAR"/>
<result column="module_id" property="moduleId" jdbcType="VARCHAR"/> <result column="module_id" property="moduleId" jdbcType="VARCHAR"/>
<result column="order_num" property="orderNum" jdbcType="INTEGER"/> <result column="order_num" property="orderNum" jdbcType="Integer"/>
<result column="status" property="status" jdbcType="INTEGER"/> <result column="status" property="status" jdbcType="Integer"/>
<!-- 模块关联字段 --> <!-- 模块关联字段 -->
<result column="module_name" property="moduleName" jdbcType="VARCHAR"/> <result column="module_name" property="moduleName" jdbcType="VARCHAR"/>
<result column="module_description" property="moduleDescription" jdbcType="VARCHAR"/> <result column="module_description" property="moduleDescription" jdbcType="VARCHAR"/>
@@ -58,7 +58,7 @@
<result column="create_time" property="createTime" jdbcType="DATE"/> <result column="create_time" property="createTime" jdbcType="DATE"/>
<result column="update_time" property="updateTime" jdbcType="DATE"/> <result column="update_time" property="updateTime" jdbcType="DATE"/>
<result column="delete_time" property="deleteTime" jdbcType="DATE"/> <result column="delete_time" property="deleteTime" jdbcType="DATE"/>
<result column="deleted" property="deleted" jdbcType="BOOLEAN"/> <result column="deleted" property="deleted" jdbcType="Boolean"/>
</resultMap> </resultMap>
<!-- 基础列 --> <!-- 基础列 -->

View File

@@ -18,7 +18,7 @@
<result column="create_time" property="createTime" jdbcType="DATE"/> <result column="create_time" property="createTime" jdbcType="DATE"/>
<result column="update_time" property="updateTime" jdbcType="DATE"/> <result column="update_time" property="updateTime" jdbcType="DATE"/>
<result column="delete_time" property="deleteTime" jdbcType="DATE"/> <result column="delete_time" property="deleteTime" jdbcType="DATE"/>
<result column="deleted" property="deleted" jdbcType="BOOLEAN"/> <result column="deleted" property="deleted" jdbcType="Boolean"/>
</resultMap> </resultMap>
<!-- VO结果映射用于前端展示 --> <!-- VO结果映射用于前端展示 -->
@@ -37,7 +37,7 @@
<result column="create_time" property="createTime" jdbcType="DATE"/> <result column="create_time" property="createTime" jdbcType="DATE"/>
<result column="update_time" property="updateTime" jdbcType="DATE"/> <result column="update_time" property="updateTime" jdbcType="DATE"/>
<result column="delete_time" property="deleteTime" jdbcType="DATE"/> <result column="delete_time" property="deleteTime" jdbcType="DATE"/>
<result column="deleted" property="deleted" jdbcType="BOOLEAN"/> <result column="deleted" property="deleted" jdbcType="Boolean"/>
</resultMap> </resultMap>
<!-- 基础列 --> <!-- 基础列 -->

View File

@@ -16,7 +16,7 @@
<result column="create_time" property="createTime" jdbcType="DATE"/> <result column="create_time" property="createTime" jdbcType="DATE"/>
<result column="update_time" property="updateTime" jdbcType="DATE"/> <result column="update_time" property="updateTime" jdbcType="DATE"/>
<result column="delete_time" property="deleteTime" jdbcType="DATE"/> <result column="delete_time" property="deleteTime" jdbcType="DATE"/>
<result column="deleted" property="deleted" jdbcType="BOOLEAN"/> <result column="deleted" property="deleted" jdbcType="Boolean"/>
</resultMap> </resultMap>
<!-- VO结果映射用于前端展示 --> <!-- VO结果映射用于前端展示 -->
@@ -32,7 +32,7 @@
<result column="role_description" property="roleDescription" jdbcType="VARCHAR"/> <result column="role_description" property="roleDescription" jdbcType="VARCHAR"/>
<result column="scope" property="roleScope" jdbcType="VARCHAR"/> <result column="scope" property="roleScope" jdbcType="VARCHAR"/>
<result column="owner_dept_id" property="roleOwnerDeptId" jdbcType="VARCHAR"/> <result column="owner_dept_id" property="roleOwnerDeptId" jdbcType="VARCHAR"/>
<result column="role_status" property="roleStatus" jdbcType="BOOLEAN"/> <result column="role_status" property="roleStatus" jdbcType="Boolean"/>
<!-- BaseVO 基础字段 --> <!-- BaseVO 基础字段 -->
<result column="optsn" property="optsn" jdbcType="VARCHAR"/> <result column="optsn" property="optsn" jdbcType="VARCHAR"/>
<result column="creator" property="creator" jdbcType="VARCHAR"/> <result column="creator" property="creator" jdbcType="VARCHAR"/>
@@ -42,7 +42,7 @@
<result column="create_time" property="createTime" jdbcType="DATE"/> <result column="create_time" property="createTime" jdbcType="DATE"/>
<result column="update_time" property="updateTime" jdbcType="DATE"/> <result column="update_time" property="updateTime" jdbcType="DATE"/>
<result column="delete_time" property="deleteTime" jdbcType="DATE"/> <result column="delete_time" property="deleteTime" jdbcType="DATE"/>
<result column="deleted" property="deleted" jdbcType="BOOLEAN"/> <result column="deleted" property="deleted" jdbcType="Boolean"/>
</resultMap> </resultMap>
<!-- 基础列 --> <!-- 基础列 -->

View File

@@ -17,7 +17,7 @@
<result column="create_time" property="createTime" jdbcType="DATE"/> <result column="create_time" property="createTime" jdbcType="DATE"/>
<result column="update_time" property="updateTime" jdbcType="DATE"/> <result column="update_time" property="updateTime" jdbcType="DATE"/>
<result column="delete_time" property="deleteTime" jdbcType="DATE"/> <result column="delete_time" property="deleteTime" jdbcType="DATE"/>
<result column="deleted" property="deleted" jdbcType="BOOLEAN"/> <result column="deleted" property="deleted" jdbcType="Boolean"/>
</resultMap> </resultMap>
<!-- VO结果映射用于前端展示 --> <!-- VO结果映射用于前端展示 -->
@@ -35,7 +35,7 @@
<result column="create_time" property="createTime" jdbcType="DATE"/> <result column="create_time" property="createTime" jdbcType="DATE"/>
<result column="update_time" property="updateTime" jdbcType="DATE"/> <result column="update_time" property="updateTime" jdbcType="DATE"/>
<result column="delete_time" property="deleteTime" jdbcType="DATE"/> <result column="delete_time" property="deleteTime" jdbcType="DATE"/>
<result column="deleted" property="deleted" jdbcType="BOOLEAN"/> <result column="deleted" property="deleted" jdbcType="Boolean"/>
</resultMap> </resultMap>
<!-- 基础列 --> <!-- 基础列 -->

View File

@@ -20,7 +20,7 @@
<result column="create_time" property="createTime" jdbcType="DATE"/> <result column="create_time" property="createTime" jdbcType="DATE"/>
<result column="update_time" property="updateTime" jdbcType="DATE"/> <result column="update_time" property="updateTime" jdbcType="DATE"/>
<result column="delete_time" property="deleteTime" jdbcType="DATE"/> <result column="delete_time" property="deleteTime" jdbcType="DATE"/>
<result column="deleted" property="deleted" jdbcType="BOOLEAN"/> <result column="deleted" property="deleted" jdbcType="Boolean"/>
</resultMap> </resultMap>
<!-- VO结果映射用于前端展示 --> <!-- VO结果映射用于前端展示 -->
@@ -44,7 +44,7 @@
<result column="create_time" property="createTime" jdbcType="DATE"/> <result column="create_time" property="createTime" jdbcType="DATE"/>
<result column="update_time" property="updateTime" jdbcType="DATE"/> <result column="update_time" property="updateTime" jdbcType="DATE"/>
<result column="delete_time" property="deleteTime" jdbcType="DATE"/> <result column="delete_time" property="deleteTime" jdbcType="DATE"/>
<result column="deleted" property="deleted" jdbcType="BOOLEAN"/> <result column="deleted" property="deleted" jdbcType="Boolean"/>
</resultMap> </resultMap>
<!-- 基础列 --> <!-- 基础列 -->

View File

@@ -16,7 +16,7 @@
<result column="create_time" property="createTime" jdbcType="DATE"/> <result column="create_time" property="createTime" jdbcType="DATE"/>
<result column="update_time" property="updateTime" jdbcType="DATE"/> <result column="update_time" property="updateTime" jdbcType="DATE"/>
<result column="delete_time" property="deleteTime" jdbcType="DATE"/> <result column="delete_time" property="deleteTime" jdbcType="DATE"/>
<result column="deleted" property="deleted" jdbcType="BOOLEAN"/> <result column="deleted" property="deleted" jdbcType="Boolean"/>
</resultMap> </resultMap>
<!-- VO结果映射用于前端展示 --> <!-- VO结果映射用于前端展示 -->
@@ -28,9 +28,9 @@
<result column="view_url" property="viewUrl" jdbcType="VARCHAR"/> <result column="view_url" property="viewUrl" jdbcType="VARCHAR"/>
<result column="view_component" property="viewComponent" jdbcType="VARCHAR"/> <result column="view_component" property="viewComponent" jdbcType="VARCHAR"/>
<result column="view_icon" property="viewIcon" jdbcType="VARCHAR"/> <result column="view_icon" property="viewIcon" jdbcType="VARCHAR"/>
<result column="view_type" property="viewType" jdbcType="INTEGER"/> <result column="view_type" property="viewType" jdbcType="Integer"/>
<result column="view_layout" property="viewLayout" jdbcType="VARCHAR"/> <result column="view_layout" property="viewLayout" jdbcType="VARCHAR"/>
<result column="view_order_num" property="viewOrderNum" jdbcType="INTEGER"/> <result column="view_order_num" property="viewOrderNum" jdbcType="Integer"/>
<result column="view_description" property="viewDescription" jdbcType="VARCHAR"/> <result column="view_description" property="viewDescription" jdbcType="VARCHAR"/>
<!-- 权限字段 --> <!-- 权限字段 -->
<result column="permission_id" property="permissionId" jdbcType="VARCHAR"/> <result column="permission_id" property="permissionId" jdbcType="VARCHAR"/>
@@ -51,7 +51,7 @@
<result column="create_time" property="createTime" jdbcType="DATE"/> <result column="create_time" property="createTime" jdbcType="DATE"/>
<result column="update_time" property="updateTime" jdbcType="DATE"/> <result column="update_time" property="updateTime" jdbcType="DATE"/>
<result column="delete_time" property="deleteTime" jdbcType="DATE"/> <result column="delete_time" property="deleteTime" jdbcType="DATE"/>
<result column="deleted" property="deleted" jdbcType="BOOLEAN"/> <result column="deleted" property="deleted" jdbcType="Boolean"/>
</resultMap> </resultMap>
<!-- 基础列 --> <!-- 基础列 -->

View File

@@ -10,7 +10,7 @@
<result column="description" property="description" jdbcType="VARCHAR"/> <result column="description" property="description" jdbcType="VARCHAR"/>
<result column="scope" property="scope" jdbcType="VARCHAR"/> <result column="scope" property="scope" jdbcType="VARCHAR"/>
<result column="owner_dept_id" property="ownerDeptId" jdbcType="VARCHAR"/> <result column="owner_dept_id" property="ownerDeptId" jdbcType="VARCHAR"/>
<result column="status" property="status" jdbcType="BOOLEAN"/> <result column="status" property="status" jdbcType="Boolean"/>
<!-- 基础字段 --> <!-- 基础字段 -->
<result column="optsn" property="optsn" jdbcType="VARCHAR"/> <result column="optsn" property="optsn" jdbcType="VARCHAR"/>
<result column="creator" property="creator" jdbcType="VARCHAR"/> <result column="creator" property="creator" jdbcType="VARCHAR"/>
@@ -20,7 +20,7 @@
<result column="create_time" property="createTime" jdbcType="DATE"/> <result column="create_time" property="createTime" jdbcType="DATE"/>
<result column="update_time" property="updateTime" jdbcType="DATE"/> <result column="update_time" property="updateTime" jdbcType="DATE"/>
<result column="delete_time" property="deleteTime" jdbcType="DATE"/> <result column="delete_time" property="deleteTime" jdbcType="DATE"/>
<result column="deleted" property="deleted" jdbcType="BOOLEAN"/> <result column="deleted" property="deleted" jdbcType="Boolean"/>
</resultMap> </resultMap>
<!-- VO结果映射用于前端展示 --> <!-- VO结果映射用于前端展示 -->
@@ -31,7 +31,7 @@
<result column="description" property="roleDescription" jdbcType="VARCHAR"/> <result column="description" property="roleDescription" jdbcType="VARCHAR"/>
<result column="scope" property="roleScope" jdbcType="VARCHAR"/> <result column="scope" property="roleScope" jdbcType="VARCHAR"/>
<result column="owner_dept_id" property="roleOwnerDeptId" jdbcType="VARCHAR"/> <result column="owner_dept_id" property="roleOwnerDeptId" jdbcType="VARCHAR"/>
<result column="status" property="roleStatus" jdbcType="BOOLEAN"/> <result column="status" property="roleStatus" jdbcType="Boolean"/>
<!-- BaseVO 基础字段 --> <!-- BaseVO 基础字段 -->
<result column="optsn" property="optsn" jdbcType="VARCHAR"/> <result column="optsn" property="optsn" jdbcType="VARCHAR"/>
<result column="creator" property="creator" jdbcType="VARCHAR"/> <result column="creator" property="creator" jdbcType="VARCHAR"/>
@@ -41,7 +41,7 @@
<result column="create_time" property="createTime" jdbcType="DATE"/> <result column="create_time" property="createTime" jdbcType="DATE"/>
<result column="update_time" property="updateTime" jdbcType="DATE"/> <result column="update_time" property="updateTime" jdbcType="DATE"/>
<result column="delete_time" property="deleteTime" jdbcType="DATE"/> <result column="delete_time" property="deleteTime" jdbcType="DATE"/>
<result column="deleted" property="deleted" jdbcType="BOOLEAN"/> <result column="deleted" property="deleted" jdbcType="Boolean"/>
</resultMap> </resultMap>
<!-- 基础列 --> <!-- 基础列 -->

View File

@@ -16,7 +16,7 @@
<result column="create_time" property="createTime" jdbcType="DATE"/> <result column="create_time" property="createTime" jdbcType="DATE"/>
<result column="update_time" property="updateTime" jdbcType="DATE"/> <result column="update_time" property="updateTime" jdbcType="DATE"/>
<result column="delete_time" property="deleteTime" jdbcType="DATE"/> <result column="delete_time" property="deleteTime" jdbcType="DATE"/>
<result column="deleted" property="deleted" jdbcType="BOOLEAN"/> <result column="deleted" property="deleted" jdbcType="Boolean"/>
</resultMap> </resultMap>
<!-- VO结果映射用于前端展示 --> <!-- VO结果映射用于前端展示 -->
@@ -27,7 +27,7 @@
<result column="role_description" property="roleDescription" jdbcType="VARCHAR"/> <result column="role_description" property="roleDescription" jdbcType="VARCHAR"/>
<result column="role_scope" property="roleScope" jdbcType="VARCHAR"/> <result column="role_scope" property="roleScope" jdbcType="VARCHAR"/>
<result column="role_owner_dept_id" property="roleOwnerDeptId" jdbcType="VARCHAR"/> <result column="role_owner_dept_id" property="roleOwnerDeptId" jdbcType="VARCHAR"/>
<result column="role_status" property="roleStatus" jdbcType="BOOLEAN"/> <result column="role_status" property="roleStatus" jdbcType="Boolean"/>
<!-- 权限字段 --> <!-- 权限字段 -->
<result column="permission_id" property="permissionId" jdbcType="VARCHAR"/> <result column="permission_id" property="permissionId" jdbcType="VARCHAR"/>
<result column="permission_name" property="permissionName" jdbcType="VARCHAR"/> <result column="permission_name" property="permissionName" jdbcType="VARCHAR"/>
@@ -47,7 +47,7 @@
<result column="create_time" property="createTime" jdbcType="DATE"/> <result column="create_time" property="createTime" jdbcType="DATE"/>
<result column="update_time" property="updateTime" jdbcType="DATE"/> <result column="update_time" property="updateTime" jdbcType="DATE"/>
<result column="delete_time" property="deleteTime" jdbcType="DATE"/> <result column="delete_time" property="deleteTime" jdbcType="DATE"/>
<result column="deleted" property="deleted" jdbcType="BOOLEAN"/> <result column="deleted" property="deleted" jdbcType="Boolean"/>
</resultMap> </resultMap>
<!-- 基础列 --> <!-- 基础列 -->

View File

@@ -7,11 +7,11 @@
<!-- 用户信息字段 --> <!-- 用户信息字段 -->
<id column="user_id" property="userId" jdbcType="VARCHAR"/> <id column="user_id" property="userId" jdbcType="VARCHAR"/>
<result column="avatar" property="avatar" jdbcType="VARCHAR"/> <result column="avatar" property="avatar" jdbcType="VARCHAR"/>
<result column="gender" property="gender" jdbcType="INTEGER"/> <result column="gender" property="gender" jdbcType="Integer"/>
<result column="family_name" property="familyName" jdbcType="VARCHAR"/> <result column="family_name" property="familyName" jdbcType="VARCHAR"/>
<result column="given_name" property="givenName" jdbcType="VARCHAR"/> <result column="given_name" property="givenName" jdbcType="VARCHAR"/>
<result column="full_name" property="fullName" jdbcType="VARCHAR"/> <result column="full_name" property="fullName" jdbcType="VARCHAR"/>
<result column="level" property="level" jdbcType="INTEGER"/> <result column="level" property="level" jdbcType="Integer"/>
<result column="id_card" property="idCard" jdbcType="VARCHAR"/> <result column="id_card" property="idCard" jdbcType="VARCHAR"/>
<result column="address" property="address" jdbcType="VARCHAR"/> <result column="address" property="address" jdbcType="VARCHAR"/>
<!-- 基础字段 --> <!-- 基础字段 -->
@@ -23,7 +23,7 @@
<result column="create_time" property="createTime" jdbcType="DATE"/> <result column="create_time" property="createTime" jdbcType="DATE"/>
<result column="update_time" property="updateTime" jdbcType="DATE"/> <result column="update_time" property="updateTime" jdbcType="DATE"/>
<result column="delete_time" property="deleteTime" jdbcType="DATE"/> <result column="delete_time" property="deleteTime" jdbcType="DATE"/>
<result column="deleted" property="deleted" jdbcType="BOOLEAN"/> <result column="deleted" property="deleted" jdbcType="Boolean"/>
</resultMap> </resultMap>
<!-- 基础列 --> <!-- 基础列 -->

View File

@@ -10,14 +10,14 @@
<result column="email" property="email" jdbcType="VARCHAR"/> <result column="email" property="email" jdbcType="VARCHAR"/>
<result column="phone" property="phone" jdbcType="VARCHAR"/> <result column="phone" property="phone" jdbcType="VARCHAR"/>
<result column="wechat_id" property="wechatId" jdbcType="VARCHAR"/> <result column="wechat_id" property="wechatId" jdbcType="VARCHAR"/>
<result column="status" property="status" jdbcType="INTEGER"/> <result column="status" property="status" jdbcType="Integer"/>
<!-- BaseDTO 中在该表实际存在的字段 --> <!-- BaseDTO 中在该表实际存在的字段 -->
<result column="optsn" property="optsn" jdbcType="VARCHAR"/> <result column="optsn" property="optsn" jdbcType="VARCHAR"/>
<result column="create_time" property="createTime" jdbcType="DATE"/> <result column="create_time" property="createTime" jdbcType="DATE"/>
<result column="update_time" property="updateTime" jdbcType="DATE"/> <result column="update_time" property="updateTime" jdbcType="DATE"/>
<result column="delete_time" property="deleteTime" jdbcType="DATE"/> <result column="delete_time" property="deleteTime" jdbcType="DATE"/>
<result column="deleted" property="deleted" jdbcType="BOOLEAN"/> <result column="deleted" property="deleted" jdbcType="Boolean"/>
</resultMap> </resultMap>
<!-- VO结果映射用于前端展示 --> <!-- VO结果映射用于前端展示 -->
@@ -28,14 +28,14 @@
<result column="email" property="email" jdbcType="VARCHAR"/> <result column="email" property="email" jdbcType="VARCHAR"/>
<result column="phone" property="phone" jdbcType="VARCHAR"/> <result column="phone" property="phone" jdbcType="VARCHAR"/>
<result column="wechat_id" property="wechatId" jdbcType="VARCHAR"/> <result column="wechat_id" property="wechatId" jdbcType="VARCHAR"/>
<result column="status" property="status" jdbcType="INTEGER"/> <result column="status" property="status" jdbcType="Integer"/>
<!-- 用户信息关联字段 --> <!-- 用户信息关联字段 -->
<result column="avatar" property="avatar" jdbcType="VARCHAR"/> <result column="avatar" property="avatar" jdbcType="VARCHAR"/>
<result column="gender" property="gender" jdbcType="INTEGER"/> <result column="gender" property="gender" jdbcType="Integer"/>
<result column="family_name" property="familyName" jdbcType="VARCHAR"/> <result column="family_name" property="familyName" jdbcType="VARCHAR"/>
<result column="given_name" property="givenName" jdbcType="VARCHAR"/> <result column="given_name" property="givenName" jdbcType="VARCHAR"/>
<result column="full_name" property="fullName" jdbcType="VARCHAR"/> <result column="full_name" property="fullName" jdbcType="VARCHAR"/>
<result column="level" property="level" jdbcType="INTEGER"/> <result column="level" property="level" jdbcType="Integer"/>
<result column="id_card" property="idCard" jdbcType="VARCHAR"/> <result column="id_card" property="idCard" jdbcType="VARCHAR"/>
<result column="address" property="address" jdbcType="VARCHAR"/> <result column="address" property="address" jdbcType="VARCHAR"/>
<!-- BaseVO 中在该表实际存在的字段 --> <!-- BaseVO 中在该表实际存在的字段 -->
@@ -43,7 +43,7 @@
<result column="create_time" property="createTime" jdbcType="DATE"/> <result column="create_time" property="createTime" jdbcType="DATE"/>
<result column="update_time" property="updateTime" jdbcType="DATE"/> <result column="update_time" property="updateTime" jdbcType="DATE"/>
<result column="delete_time" property="deleteTime" jdbcType="DATE"/> <result column="delete_time" property="deleteTime" jdbcType="DATE"/>
<result column="deleted" property="deleted" jdbcType="BOOLEAN"/> <result column="deleted" property="deleted" jdbcType="Boolean"/>
</resultMap> </resultMap>
<!-- 基础列(完全按表结构) --> <!-- 基础列(完全按表结构) -->

View File

@@ -16,7 +16,7 @@
<result column="create_time" property="createTime" jdbcType="DATE"/> <result column="create_time" property="createTime" jdbcType="DATE"/>
<result column="update_time" property="updateTime" jdbcType="DATE"/> <result column="update_time" property="updateTime" jdbcType="DATE"/>
<result column="delete_time" property="deleteTime" jdbcType="DATE"/> <result column="delete_time" property="deleteTime" jdbcType="DATE"/>
<result column="deleted" property="deleted" jdbcType="BOOLEAN"/> <result column="deleted" property="deleted" jdbcType="Boolean"/>
</resultMap> </resultMap>
<!-- VO结果映射用于前端展示 --> <!-- VO结果映射用于前端展示 -->
@@ -32,11 +32,11 @@
<result column="user_type" property="userType" jdbcType="VARCHAR"/> <result column="user_type" property="userType" jdbcType="VARCHAR"/>
<!-- 用户信息字段 --> <!-- 用户信息字段 -->
<result column="avatar" property="avatar" jdbcType="VARCHAR"/> <result column="avatar" property="avatar" jdbcType="VARCHAR"/>
<result column="gender" property="gender" jdbcType="INTEGER"/> <result column="gender" property="gender" jdbcType="Integer"/>
<result column="family_name" property="familyName" jdbcType="VARCHAR"/> <result column="family_name" property="familyName" jdbcType="VARCHAR"/>
<result column="given_name" property="givenName" jdbcType="VARCHAR"/> <result column="given_name" property="givenName" jdbcType="VARCHAR"/>
<result column="full_name" property="fullName" jdbcType="VARCHAR"/> <result column="full_name" property="fullName" jdbcType="VARCHAR"/>
<result column="level" property="level" jdbcType="INTEGER"/> <result column="level" property="level" jdbcType="Integer"/>
<result column="id_card" property="idCard" jdbcType="VARCHAR"/> <result column="id_card" property="idCard" jdbcType="VARCHAR"/>
<result column="address" property="address" jdbcType="VARCHAR"/> <result column="address" property="address" jdbcType="VARCHAR"/>
<!-- 部门字段 --> <!-- 部门字段 -->
@@ -50,7 +50,7 @@
<result column="role_description" property="roleDescription" jdbcType="VARCHAR"/> <result column="role_description" property="roleDescription" jdbcType="VARCHAR"/>
<result column="scope" property="scope" jdbcType="VARCHAR"/> <result column="scope" property="scope" jdbcType="VARCHAR"/>
<result column="owner_dept_id" property="ownerDeptId" jdbcType="VARCHAR"/> <result column="owner_dept_id" property="ownerDeptId" jdbcType="VARCHAR"/>
<result column="role_status" property="roleStatus" jdbcType="BOOLEAN"/> <result column="role_status" property="roleStatus" jdbcType="Boolean"/>
<!-- BaseVO 基础字段 --> <!-- BaseVO 基础字段 -->
<result column="optsn" property="optsn" jdbcType="VARCHAR"/> <result column="optsn" property="optsn" jdbcType="VARCHAR"/>
<result column="creator" property="creator" jdbcType="VARCHAR"/> <result column="creator" property="creator" jdbcType="VARCHAR"/>
@@ -60,7 +60,7 @@
<result column="create_time" property="createTime" jdbcType="DATE"/> <result column="create_time" property="createTime" jdbcType="DATE"/>
<result column="update_time" property="updateTime" jdbcType="DATE"/> <result column="update_time" property="updateTime" jdbcType="DATE"/>
<result column="delete_time" property="deleteTime" jdbcType="DATE"/> <result column="delete_time" property="deleteTime" jdbcType="DATE"/>
<result column="deleted" property="deleted" jdbcType="BOOLEAN"/> <result column="deleted" property="deleted" jdbcType="Boolean"/>
</resultMap> </resultMap>
<!-- 基础列 --> <!-- 基础列 -->

View File

@@ -11,9 +11,9 @@
<result column="url" property="url" jdbcType="VARCHAR"/> <result column="url" property="url" jdbcType="VARCHAR"/>
<result column="component" property="component" jdbcType="VARCHAR"/> <result column="component" property="component" jdbcType="VARCHAR"/>
<result column="icon" property="icon" jdbcType="VARCHAR"/> <result column="icon" property="icon" jdbcType="VARCHAR"/>
<result column="type" property="type" jdbcType="INTEGER"/> <result column="type" property="type" jdbcType="Integer"/>
<result column="layout" property="layout" jdbcType="VARCHAR"/> <result column="layout" property="layout" jdbcType="VARCHAR"/>
<result column="order_num" property="orderNum" jdbcType="INTEGER"/> <result column="order_num" property="orderNum" jdbcType="Integer"/>
<result column="description" property="description" jdbcType="VARCHAR"/> <result column="description" property="description" jdbcType="VARCHAR"/>
<!-- 基础字段 --> <!-- 基础字段 -->
<result column="optsn" property="optsn" jdbcType="VARCHAR"/> <result column="optsn" property="optsn" jdbcType="VARCHAR"/>
@@ -24,7 +24,7 @@
<result column="create_time" property="createTime" jdbcType="DATE"/> <result column="create_time" property="createTime" jdbcType="DATE"/>
<result column="update_time" property="updateTime" jdbcType="DATE"/> <result column="update_time" property="updateTime" jdbcType="DATE"/>
<result column="delete_time" property="deleteTime" jdbcType="DATE"/> <result column="delete_time" property="deleteTime" jdbcType="DATE"/>
<result column="deleted" property="deleted" jdbcType="BOOLEAN"/> <result column="deleted" property="deleted" jdbcType="Boolean"/>
</resultMap> </resultMap>
<!-- 基础列 --> <!-- 基础列 -->

View File

@@ -0,0 +1,21 @@
FROM node:20-alpine
RUN npm install -g pnpm@latest
WORKDIR /app
# 复制依赖文件
COPY packages/portal/package.json ./
COPY pnpm-lock.yaml pnpm-workspace.yaml ./
# 安装依赖
RUN pnpm install --frozen-lockfile
# 复制源代码
COPY packages/portal/ ./
# 暴露端口
EXPOSE 3000
# 开发模式HMR
CMD ["pnpm", "dev", "--host", "0.0.0.0"]

View File

@@ -0,0 +1,32 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" href="/favicon.ico">
<title>泰豪电源 AI 数智化平台</title>
<!-- Import Maps 配置 - 只配置共享模块 -->
<script type="importmap">
{
"imports": {
"@shared/components": "/shared/components.js",
"@shared/utils": "/shared/utils.js",
"@shared/api": "/shared/api.js",
"@shared/composables": "/shared/composables.js",
"@shared/types": "/shared/types.js"
}
}
</script>
<!-- 预加载关键模块 -->
<link rel="modulepreload" href="/shared/components.js">
<link rel="modulepreload" href="/shared/utils.js">
</head>
<body>
<div id="app"></div>
<!-- ES Module 入口 -->
<script type="module" src="/src/main.ts"></script>
</body>
</html>

View File

@@ -0,0 +1,156 @@
<script setup lang="ts">
/**
* Import Maps 使用示例
*
* 通过 HTML 中的 <script type="importmap"> 配置,
* 可以直接从 HTTP URL 导入共享组件
*/
// ✅ 直接导入!浏览器会自动从 http://localhost/shared/components.js 加载
import { UlTable } from '@shared/components'
import { http } from '@shared/utils'
import { authApi } from '@shared/api'
import { useTable } from '@shared/composables'
// 类型定义
interface User {
id: string
username: string
email: string
createTime: string
}
// 使用共享的 useTable 组合式函数
const {
loading,
tableData,
pagination,
handlePageChange,
handleSizeChange,
refresh
} = useTable<User>({
fetchData: async (params) => {
// 使用共享的 API 函数
return await authApi.getUserList(params)
}
})
// 表格列配置
const columns = [
{ prop: 'username', label: '用户名', minWidth: 150 },
{ prop: 'email', label: '邮箱', minWidth: 200 },
{ prop: 'createTime', label: '创建时间', width: 180 }
]
// 测试 HTTP 请求
const testRequest = async () => {
try {
// 使用共享的 http 工具
const result = await http.get('/api/test')
console.log('请求结果:', result)
} catch (error) {
console.error('请求失败:', error)
}
}
</script>
<template>
<div class="import-maps-example">
<div class="header">
<h1>Import Maps 示例</h1>
<p class="description">
共享组件从 <code>http://localhost/shared/</code> 动态加载<br>
无需打包浏览器原生 ES Module 支持<br>
真正的运行时共享所有应用使用同一份代码
</p>
<el-button type="primary" @click="testRequest">
测试 HTTP 请求
</el-button>
</div>
<!-- 使用从 HTTP 加载的共享组件 -->
<UlTable
:data="tableData"
:columns="columns"
:loading="loading"
:pagination="pagination"
@page-change="handlePageChange"
@size-change="handleSizeChange"
/>
<div class="info">
<h3>📦 当前加载的模块</h3>
<ul>
<li><code>@shared/components</code> http://localhost/shared/components.js</li>
<li><code>@shared/utils</code> http://localhost/shared/utils.js</li>
<li><code>@shared/api</code> http://localhost/shared/api.js</li>
<li><code>@shared/composables</code> http://localhost/shared/composables.js</li>
</ul>
<h3>🔍 如何查看</h3>
<ol>
<li>打开浏览器开发者工具 (F12)</li>
<li>切换到 Network 标签页</li>
<li>筛选 JS 类型</li>
<li>刷新页面可以看到从 /shared/ 加载的模块</li>
</ol>
<h3> 优势</h3>
<ul>
<li> 真正的代码共享所有应用共用一份</li>
<li> 支持热更新修改共享组件所有应用自动更新</li>
<li> 减小构建体积共享代码不打包到业务应用</li>
<li> 浏览器缓存共享模块只下载一次</li>
</ul>
</div>
</div>
</template>
<style scoped>
.import-maps-example {
padding: 24px;
}
.header {
margin-bottom: 24px;
}
.description {
color: #666;
line-height: 1.8;
margin: 16px 0;
}
.description code {
background: #f0f0f0;
padding: 2px 6px;
border-radius: 3px;
font-family: 'Courier New', monospace;
}
.info {
margin-top: 32px;
padding: 20px;
background: #f5f7fa;
border-radius: 8px;
}
.info h3 {
margin-top: 20px;
margin-bottom: 12px;
color: #409eff;
}
.info ul, .info ol {
line-height: 2;
color: #606266;
}
.info code {
background: #fff;
padding: 2px 8px;
border-radius: 3px;
font-family: 'Courier New', monospace;
color: #e6a23c;
}
</style>

View File

@@ -0,0 +1,22 @@
FROM node:20-alpine
# 安装 pnpm
RUN npm install -g pnpm@latest
WORKDIR /app
# 复制 package.json
COPY packages/shared/package.json ./
COPY pnpm-lock.yaml pnpm-workspace.yaml ./
# 安装依赖
RUN pnpm install --frozen-lockfile
# 复制源代码
COPY packages/shared/ ./
# 暴露端口
EXPOSE 5000
# 开发模式启动(支持热更新)
CMD ["pnpm", "dev", "--host", "0.0.0.0"]

View File

@@ -0,0 +1,28 @@
{
"name": "@shared/all",
"version": "1.0.0",
"type": "module",
"private": true,
"scripts": {
"dev": "vite --port 5000 --host",
"build": "run-p build:*",
"build:esm": "vite build --mode esm",
"build:federation": "vite build --mode federation",
"preview": "vite preview --port 5000"
},
"dependencies": {
"vue": "^3.5.13",
"vue-router": "^4.5.0",
"pinia": "^2.2.8",
"element-plus": "^2.9.1",
"@vueuse/core": "^11.3.0"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.2.1",
"@vitejs/plugin-vue-jsx": "^4.1.1",
"typescript": "^5.7.2",
"vite": "^6.0.3",
"@originjs/vite-plugin-federation": "^1.3.6",
"npm-run-all": "^4.1.5"
}
}

View File

@@ -0,0 +1,93 @@
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
import { resolve } from 'path'
/**
* ES Module 构建配置
* 用于 Import Maps 方案
*
* 策略:将 Vue、Element Plus 等依赖打包进共享模块
* 业务应用只需引入 @shared/*,无需关心底层依赖
*/
export default defineConfig({
plugins: [vue(), vueJsx()],
build: {
lib: {
entry: {
components: resolve(__dirname, 'src/components/index.ts'),
utils: resolve(__dirname, 'src/utils/index.ts'),
api: resolve(__dirname, 'src/api/index.ts'),
composables: resolve(__dirname, 'src/composables/index.ts'),
types: resolve(__dirname, 'src/types/index.ts')
},
formats: ['es'], // 仅构建 ES Module
fileName: (format, entryName) => `${entryName}.js`
},
rollupOptions: {
// ⚠️ 不外部化依赖,将它们打包进共享模块
// 这样业务应用只需引入 @shared/* 即可
external: [],
output: {
// 保持 ES Module 格式
format: 'es',
// 导出命名导出
exports: 'named',
// 生成 sourcemap
sourcemap: true,
// 分块策略:将大的依赖分离出来
manualChunks(id) {
// Vue 核心
if (id.includes('node_modules/vue/') ||
id.includes('node_modules/@vue/')) {
return 'vue-core'
}
// Vue Router
if (id.includes('node_modules/vue-router/')) {
return 'vue-router'
}
// Pinia
if (id.includes('node_modules/pinia/')) {
return 'pinia'
}
// Element Plus
if (id.includes('node_modules/element-plus/')) {
return 'element-plus'
}
// VueUse
if (id.includes('node_modules/@vueuse/')) {
return 'vueuse'
}
}
}
},
// 输出目录
outDir: 'dist/esm',
emptyOutDir: true,
// 目标浏览器
target: 'esnext',
// 不压缩(开发环境)
minify: false,
// 启用代码分割
cssCodeSplit: true
},
// 开发服务器配置
server: {
port: 5000,
host: true,
cors: true,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, OPTIONS',
'Content-Type': 'application/javascript; charset=utf-8'
}
}
})