feat: 移除 Docker 部署,添加 Gitea CI/CD workflows

- 删除 docker-compose.yml、Dockerfile、.dockerignore、docker/ 目录
- 更新 deploy.sh:移除 Docker Compose MySQL 启动,改为 systemd 检查
- 新增 .gitea/workflows/deploy.yml:手动触发、rsync + SSH + PM2 部署
- 新增 .gitea/workflows/ci.yml:PR lint + typecheck + test 自动检查
This commit is contained in:
2026-07-20 15:33:46 +08:00
parent ad62b8595c
commit a87f26a915
8 changed files with 139 additions and 692 deletions

View File

@@ -1,21 +0,0 @@
node_modules
npm-debug.log
Dockerfile
.dockerignore
.git
.gitignore
README.md
*.md
dist
build
.env
.env.local
.env.*.local
coverage
.nyc_output
.DS_Store
.vscode
.idea
*.swp
*.swo
*~

40
.gitea/workflows/ci.yml Normal file
View File

@@ -0,0 +1,40 @@
# PR 自动检查:代码风格 + 类型检查 + 测试
name: CI 检查
on:
pull_request:
branches: [main]
push:
branches: [main]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '22'
- run: npm ci
- run: npm run lint
typecheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '22'
- run: npm ci
- run: npm run typecheck
test:
runs-on: ubuntu-latest
needs: [lint, typecheck]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '22'
- run: npm ci
- run: npm run test

View File

@@ -0,0 +1,92 @@
# PM2 手动部署工作流
# 手动触发 → 构建前后端 → rsync 同步 → 服务器执行 PM2 重载
#
# 前置准备:
# 1. 服务器需安装 PM2: npm i -g pm2
# 2. 服务器需预先启动 MySQL
# 3. 在 Gitea 仓库 Settings → Secrets 中配置以下密钥:
# - SSH_PRIVATE_KEY : 部署用 SSH 私钥
# - SSH_HOST : 服务器地址
# - SSH_USER : SSH 用户名
# - SSH_PORT : SSH 端口(默认 22可选
# - REMOTE_DIR : 服务器上的项目目录,如 /opt/gongxue
name: PM2 部署
on:
workflow_dispatch:
inputs:
skip_build:
description: '跳过构建(仅同步 + PM2 重载)'
required: false
type: boolean
default: false
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: 检出代码
uses: actions/checkout@v4
- name: 安装 Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
- name: 安装依赖 & 构建
if: ${{ !inputs.skip_build }}
run: |
npm ci
npm run build -w @gongxue/server
npm run build -w @gongxue/admin
- name: 配置 SSH
run: |
mkdir -p ~/.ssh
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/deploy_key
chmod 600 ~/.ssh/deploy_key
cat >> ~/.ssh/config <<'EOF'
Host deploy-server
HostName ${{ secrets.SSH_HOST }}
User ${{ secrets.SSH_USER }}
Port ${{ secrets.SSH_PORT || '22' }}
IdentityFile ~/.ssh/deploy_key
StrictHostKeyChecking accept-new
EOF
- name: 同步代码到服务器
run: |
rsync -avz --delete \
--exclude='node_modules' \
--exclude='.git' \
--exclude='*.db' \
--exclude='.DS_Store' \
--exclude='logs/' \
--exclude='.turbo/' \
--exclude='.claude/' \
--exclude='.codegraph/' \
./ deploy-server:${{ secrets.REMOTE_DIR }}/
- name: 安装依赖 & 数据库迁移
run: |
ssh deploy-server "
cd ${{ secrets.REMOTE_DIR }}
mkdir -p logs
if [ ! -d node_modules ]; then
echo '首次部署,安装生产依赖...'
npm ci --omit=dev
fi
echo '执行数据库迁移...'
npm run migration:run -w @gongxue/server || echo '迁移跳过(无变更或已是最新)'
"
- name: PM2 重载
run: |
ssh deploy-server "
cd ${{ secrets.REMOTE_DIR }}
pm2 startOrReload ecosystem.config.cjs --update-env
pm2 save
echo '=== PM2 进程状态 ==='
pm2 status
"

View File

@@ -1,14 +0,0 @@
FROM node:22-alpine AS builder
WORKDIR /app
COPY package*.json ./
COPY apps/admin/package.json ./apps/admin/package.json
COPY packages/typescript-config/package.json ./packages/typescript-config/package.json
RUN npm ci
COPY . .
RUN npm run build -w @gongxue/admin
FROM nginx:alpine
COPY --from=builder /app/apps/admin/dist /usr/share/nginx/html
COPY apps/admin/nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

View File

@@ -1,16 +0,0 @@
FROM node:22-alpine AS builder
WORKDIR /app
COPY package*.json ./
COPY apps/server/package.json ./apps/server/package.json
COPY packages/typescript-config/package.json ./packages/typescript-config/package.json
RUN npm ci --no-optional
COPY . .
RUN npm run build -w @gongxue/server
FROM node:22-alpine
WORKDIR /app
COPY --from=builder /app/apps/server/dist ./dist
COPY --from=builder /app/apps/server/package.json ./
COPY --from=builder /app/node_modules ./node_modules
EXPOSE 3000
CMD ["node", "dist/main.js"]

View File

@@ -22,16 +22,16 @@ rsync -avz --delete \
--exclude='.turbo/' \
./ "${SSH_HOST}:${REMOTE_DIR}/"
echo "=== 4/5 启动 MySQL + 安装依赖 ==="
echo "=== 4/5 检查 MySQL + 安装依赖 ==="
ssh "${SSH_HOST}" "
cd ${REMOTE_DIR}
docker compose up -d mysql
mkdir -p logs
for i in \$(seq 1 30); do
docker compose exec -T mysql mysqladmin ping -h localhost --silent 2>/dev/null && break
echo '等待 MySQL...'
sleep 2
done
# 确保 MySQL 已启动systemd 或手动启动)
if systemctl is-active --quiet mysqld 2>/dev/null || systemctl is-active --quiet mysql 2>/dev/null; then
echo 'MySQL 已运行'
else
echo '⚠ MySQL 未运行,请确保 MySQL 已启动'
fi
if [ ! -d node_modules ]; then
echo '首次部署,安装依赖...'
npm ci --omit=dev

View File

@@ -1,32 +0,0 @@
services:
backend:
build:
context: .
dockerfile: apps/server/Dockerfile
container_name: gongxue_backend
restart: always
ports:
- "127.0.0.1:3000:3000"
environment:
DB_TYPE: mysql
DB_HOST: host.docker.internal
DB_PORT: 3306
DB_USERNAME: ${MYSQL_ROOT_USERNAME:-root}
DB_PASSWORD: ${MYSQL_ROOT_PASSWORD}
DB_DATABASE: ${DB_DATABASE:-dorm_billing_v2}
DB_SYNCHRONIZE: "false"
JWT_SECRET: ${JWT_SECRET:-gongxue-jwt-prod-2026-k3y}
JWT_EXPIRES_IN: 24h
PORT: 3000
frontend:
build:
context: .
dockerfile: apps/admin/Dockerfile
container_name: gongxue_frontend
restart: always
ports:
- "127.0.0.1:9527:80"
depends_on:
- backend

View File

@@ -1,602 +0,0 @@
-- 恭学教育 MySQL 初始化脚本
-- 用途:在 DB_SYNCHRONIZE=false 时初始化空 MySQL 8.x 数据库,使当前项目可直接运行。
-- 默认管理员admin / admin123首次登录后请立即修改密码
-- 如果用 docker-entrypoint-initdb.d 自动初始化,可直接挂载本文件。
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
CREATE DATABASE IF NOT EXISTS `dorm_billing_v2` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE `dorm_billing_v2`;
-- ---------- 重置旧表 ----------
DROP TABLE IF EXISTS `archive_attachments`;
DROP TABLE IF EXISTS `result_archives`;
DROP TABLE IF EXISTS `learning_records`;
DROP TABLE IF EXISTS `exam_scores`;
DROP TABLE IF EXISTS `student_enrollments`;
DROP TABLE IF EXISTS `student_profiles`;
DROP TABLE IF EXISTS `notifications`;
DROP TABLE IF EXISTS `ding_attendance_raw`;
DROP TABLE IF EXISTS `attendance_period_configs`;
DROP TABLE IF EXISTS `attendance_devices`;
DROP TABLE IF EXISTS `attendance_records`;
DROP TABLE IF EXISTS `attendance_sessions`;
DROP TABLE IF EXISTS `class_schedule`;
DROP TABLE IF EXISTS `class_teacher`;
DROP TABLE IF EXISTS `class_student`;
DROP TABLE IF EXISTS `classes`;
DROP TABLE IF EXISTS `classroom_rentals`;
DROP TABLE IF EXISTS `classrooms`;
DROP TABLE IF EXISTS `deposit_installments`;
DROP TABLE IF EXISTS `deposits`;
DROP TABLE IF EXISTS `financial_operations`;
DROP TABLE IF EXISTS `operation_logs`;
DROP TABLE IF EXISTS `student_wallets`;
DROP TABLE IF EXISTS `wallet_transactions`;
DROP TABLE IF EXISTS `student_ding_mapping`;
DROP TABLE IF EXISTS `students`;
DROP TABLE IF EXISTS `user_roles`;
DROP TABLE IF EXISTS `role_permissions`;
DROP TABLE IF EXISTS `users`;
DROP TABLE IF EXISTS `roles`;
DROP TABLE IF EXISTS `permissions`;
DROP TABLE IF EXISTS `bills`;
DROP TABLE IF EXISTS `bill_items`;
DROP TABLE IF EXISTS `personal_expenses`;
DROP TABLE IF EXISTS `room_expenses`;
DROP TABLE IF EXISTS `occupancies`;
DROP TABLE IF EXISTS `beds`;
DROP TABLE IF EXISTS `lockers`;
DROP TABLE IF EXISTS `rooms`;
DROP TABLE IF EXISTS `organizations`;
DROP TABLE IF EXISTS `sync_state`;
DROP TABLE IF EXISTS `sync_logs`;
DROP TABLE IF EXISTS `expense_types`;
DROP TABLE IF EXISTS `ai_config`;
DROP TABLE IF EXISTS `integration_config_detail`;
DROP TABLE IF EXISTS `integration_config`;
-- ---------- 表结构 ----------
CREATE TABLE IF NOT EXISTS `room_expenses` (`id` int NOT NULL AUTO_INCREMENT, `room_id` int NOT NULL, `expense_type` varchar(20) NOT NULL, `amount` decimal(10,2) NOT NULL, `period_start` date NOT NULL, `period_end` date NOT NULL, `description` text NULL, `recorded_by` int NULL, `status` varchar(20) NOT NULL DEFAULT 'active', `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), PRIMARY KEY (`id`)) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `rooms` (`id` int NOT NULL AUTO_INCREMENT, `room_number` varchar(20) NOT NULL, `building` varchar(50) NULL, `floor` int NULL, `capacity` int NOT NULL, `status` varchar(20) NOT NULL DEFAULT 'available', `room_type` varchar(20) NULL, `rental_category` varchar(10) NOT NULL DEFAULT 'short', `monthly_rate` decimal(10,2) NOT NULL DEFAULT 0.00, `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), UNIQUE INDEX `IDX_8f7c6fa4c469bab1a06fe3e49f` (`room_number`), PRIMARY KEY (`id`)) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `organizations` (`id` int NOT NULL AUTO_INCREMENT, `public_id` varchar(36) NOT NULL, `code` varchar(50) NOT NULL, `name` varchar(100) NOT NULL, `is_host` tinyint NOT NULL DEFAULT 0, `contact_name` varchar(50) NULL, `phone` varchar(30) NULL, `color` varchar(20) NULL, `notes` text NULL, `status` varchar(20) NOT NULL DEFAULT 'active', `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), UNIQUE INDEX `IDX_0db5eda192bf60a02bd41931f8` (`public_id`), UNIQUE INDEX `IDX_7e27c3b62c681fbe3e2322535f` (`code`), PRIMARY KEY (`id`)) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `beds` (`id` int NOT NULL AUTO_INCREMENT, `room_id` int NOT NULL, `bed_number` varchar(20) NOT NULL, `status` varchar(20) NOT NULL DEFAULT 'available', `notes` text NULL, `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), UNIQUE INDEX `IDX_95e9ba0a907346ef7b0d5ca488` (`room_id`, `bed_number`), PRIMARY KEY (`id`)) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `lockers` (`id` int NOT NULL AUTO_INCREMENT, `room_id` int NOT NULL, `locker_number` varchar(20) NOT NULL, `status` varchar(20) NOT NULL DEFAULT 'available', `notes` text NULL, `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), UNIQUE INDEX `IDX_8bc984d80d58c6909f738d8282` (`room_id`, `locker_number`), PRIMARY KEY (`id`)) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `occupancies` (`id` int NOT NULL AUTO_INCREMENT, `student_id` int NOT NULL, `room_id` int NOT NULL, `check_in_date` date NOT NULL, `check_out_date` date NULL, `billing_start_date` date NOT NULL, `billing_end_date` date NULL, `check_out_reason` varchar(100) NULL, `notes` text NULL, `status` varchar(20) NOT NULL DEFAULT 'active', `bed_id` int NULL, `locker_id` int NULL, `stay_type` varchar(10) NOT NULL DEFAULT 'short', `responsible_organization_id` int NULL, `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), PRIMARY KEY (`id`)) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `personal_expenses` (`id` int NOT NULL AUTO_INCREMENT, `student_id` int NOT NULL, `room_id` int NULL, `expense_type` varchar(20) NOT NULL, `amount` decimal(10,2) NOT NULL, `expense_date` date NOT NULL, `description` text NULL, `recorded_by` int NULL, `status` varchar(20) NOT NULL DEFAULT 'active', `bill_id` int NULL, `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), PRIMARY KEY (`id`)) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `bill_items` (`id` int NOT NULL AUTO_INCREMENT, `bill_id` int NOT NULL, `room_id` int NULL, `expense_type` varchar(20) NULL, `description` varchar(200) NULL, `days` int NULL, `total_room_days` int NULL, `room_total_amount` decimal(10,2) NULL, `student_amount` decimal(10,2) NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `bills` (`id` int NOT NULL AUTO_INCREMENT, `student_id` int NOT NULL, `period_start` date NOT NULL, `period_end` date NOT NULL, `shared_amount` decimal(10,2) NOT NULL DEFAULT 0.00, `personal_amount` decimal(10,2) NOT NULL DEFAULT 0.00, `total_amount` decimal(10,2) NOT NULL DEFAULT 0.00, `source` varchar(30) NOT NULL DEFAULT 'batch', `paid_amount` decimal(10,2) NOT NULL DEFAULT 0.00, `outstanding_amount` decimal(10,2) NOT NULL DEFAULT 0.00, `status` varchar(20) NOT NULL DEFAULT 'unpaid', `cancelled_at` datetime NULL, `cancel_reason` varchar(300) NULL, `generated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), PRIMARY KEY (`id`)) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `permissions` (`id` int NOT NULL AUTO_INCREMENT, `code` varchar(50) NOT NULL, `name` varchar(50) NOT NULL, `group` varchar(30) NOT NULL, `description` varchar(200) NULL, UNIQUE INDEX `IDX_8dad765629e83229da6feda1c1` (`code`), PRIMARY KEY (`id`)) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `roles` (`id` int NOT NULL AUTO_INCREMENT, `name` varchar(30) NOT NULL, `code` varchar(30) NULL, `description` varchar(200) NULL, `is_system` tinyint NOT NULL DEFAULT 0, `status` tinyint NOT NULL DEFAULT 1, `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), UNIQUE INDEX `IDX_648e3f5447f725579d7d4ffdfb` (`name`), UNIQUE INDEX `IDX_f6d54f95c31b73fb1bdd8e91d0` (`code`), PRIMARY KEY (`id`)) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `users` (`id` int NOT NULL AUTO_INCREMENT, `username` varchar(50) NOT NULL, `password_hash` varchar(255) NOT NULL, `name` varchar(50) NULL, `is_active` tinyint NOT NULL DEFAULT 1, `last_login_at` datetime NULL, `is_archived` tinyint NOT NULL DEFAULT 0, `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), `profile` text NULL, UNIQUE INDEX `IDX_fe0bb3f6520ee0469504521e71` (`username`), PRIMARY KEY (`id`)) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `students` (`id` int NOT NULL AUTO_INCREMENT, `name` varchar(50) NOT NULL, `student_no` varchar(30) NULL, `phone` varchar(20) NULL, `id_number` varchar(30) NULL, `gender` varchar(10) NULL, `ethnicity` varchar(20) NULL, `emergency_contact` varchar(50) NULL, `emergency_phone` varchar(20) NULL, `status` varchar(20) NOT NULL DEFAULT 'active', `supervisor` varchar(50) NULL, `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), `user_id` int NULL, `organization_id` int NULL, UNIQUE INDEX `IDX_fb3eff90b11bddf7285f9b4e28` (`user_id`), UNIQUE INDEX `REL_fb3eff90b11bddf7285f9b4e28` (`user_id`), PRIMARY KEY (`id`)) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `financial_operations` (`id` int NOT NULL AUTO_INCREMENT, `operation_id` varchar(64) NOT NULL, `type` varchar(64) NOT NULL, `status` varchar(20) NOT NULL DEFAULT 'running', `result_json` text NULL, `error_message` varchar(500) NULL, `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), UNIQUE INDEX `IDX_financial_operations_operation_id` (`operation_id`), PRIMARY KEY (`id`)) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `operation_logs` (`id` int NOT NULL AUTO_INCREMENT, `user_id` int NULL, `username` varchar(50) NULL, `module` varchar(50) NOT NULL, `action` varchar(50) NOT NULL, `target_id` int NULL, `target_type` varchar(50) NULL, `detail` text NULL, `ip_address` varchar(50) NULL, `user_agent` varchar(500) NULL, `status` varchar(20) NULL DEFAULT 'success', `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), PRIMARY KEY (`id`)) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `deposit_installments` (`id` int NOT NULL AUTO_INCREMENT, `deposit_id` int NOT NULL, `amount` decimal(10,2) NOT NULL, `due_date` date NOT NULL, `paid_date` date NULL, `status` varchar(20) NOT NULL DEFAULT 'pending', `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), PRIMARY KEY (`id`)) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `deposits` (`id` int NOT NULL AUTO_INCREMENT, `student_id` int NOT NULL, `amount` decimal(10,2) NOT NULL DEFAULT '500.00', `status` varchar(20) NOT NULL DEFAULT 'paid', `paid_date` date NOT NULL, `refund_date` date NULL, `refund_amount` decimal(10,2) NULL, `deduction_amount` decimal(10,2) NOT NULL DEFAULT 0.00, `deduction_reason` text NULL, `notes` text NULL, `recorded_by` int NULL, `refunded_by` int NULL, `refunded_at` datetime NULL, `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), PRIMARY KEY (`id`)) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `classrooms` (`id` int NOT NULL AUTO_INCREMENT, `name` varchar(50) NOT NULL, `building` varchar(50) NULL, `floor` int NULL, `capacity` int NOT NULL DEFAULT '30', `room_type` varchar(20) NOT NULL DEFAULT '', `status` varchar(20) NOT NULL DEFAULT 'available', `notes` text NULL, `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), PRIMARY KEY (`id`)) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `attendance_period_configs` (`id` int NOT NULL AUTO_INCREMENT, `period_key` varchar(40) NOT NULL, `label` varchar(40) NOT NULL, `start_time` varchar(5) NOT NULL, `end_time` varchar(5) NOT NULL, `sort_order` int NOT NULL DEFAULT 0, `enabled` tinyint NOT NULL DEFAULT 1, `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), UNIQUE INDEX `IDX_attendance_period_configs_period_key` (`period_key`), INDEX `IDX_attendance_period_configs_sort_order` (`sort_order`), PRIMARY KEY (`id`)) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `attendance_devices` (`id` int NOT NULL AUTO_INCREMENT, `device_sn` varchar(100) NOT NULL, `device_name` varchar(100) NOT NULL, `classroom_id` int NOT NULL, `status` varchar(20) NOT NULL DEFAULT 'active', `location` varchar(200) NULL, `notes` text NULL, `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), UNIQUE INDEX `IDX_attendance_devices_device_sn` (`device_sn`), INDEX `IDX_attendance_devices_classroom_id` (`classroom_id`), PRIMARY KEY (`id`)) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `classroom_rentals` (`id` int NOT NULL AUTO_INCREMENT, `classroom_id` int NOT NULL, `lessor_organization_id` int NULL, `lessee_organization_id` int NULL, `start_date` date NOT NULL, `end_date` date NOT NULL, `contract_path` varchar(255) NULL, `contract_original_name` varchar(255) NULL, `daily_rate` decimal(10,2) NULL, `total_amount` decimal(10,2) NULL, `status` varchar(20) NOT NULL DEFAULT 'active', `notes` text NULL, `created_by` int NULL, `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), INDEX `IDX_b748a951d00b3f0c2090d10397` (`classroom_id`, `start_date`, `end_date`), PRIMARY KEY (`id`)) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `classes` (`id` int NOT NULL AUTO_INCREMENT, `name` varchar(100) NOT NULL, `code` varchar(50) NOT NULL, `class_type` varchar(20) NOT NULL, `start_date` date NULL, `end_date` date NULL, `status` varchar(20) NOT NULL DEFAULT 'enrolling', `head_teacher_id` int NULL, `life_teacher_id` int NULL, `academic_teacher_id` int NULL, `max_students` int NOT NULL DEFAULT 0, `notes` text NULL, `is_archived` tinyint NOT NULL DEFAULT 0, `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), UNIQUE INDEX `IDX_cf7491878e0fca859943862998` (`code`), PRIMARY KEY (`id`)) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `class_student` (`id` int NOT NULL AUTO_INCREMENT, `class_id` int NOT NULL, `student_id` int NOT NULL, `join_date` date NULL, `leave_date` date NULL, `status` varchar(10) NOT NULL DEFAULT 'active', `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), UNIQUE INDEX `IDX_b0ed786e05e93dd9bf77189af1` (`class_id`, `student_id`), PRIMARY KEY (`id`)) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `class_teacher` (`id` int NOT NULL AUTO_INCREMENT, `class_id` int NOT NULL, `user_id` int NOT NULL, `role_type` varchar(30) NOT NULL, `subject` varchar(50) NULL, `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), UNIQUE INDEX `IDX_0117e9171e1533ff7b5c63f5d6` (`class_id`, `user_id`, `role_type`), PRIMARY KEY (`id`)) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `class_schedule` (`id` int NOT NULL AUTO_INCREMENT, `class_id` int NULL, `classroom_id` int NOT NULL, `week_day` int NOT NULL, `start_time` varchar(5) NOT NULL, `end_time` varchar(5) NOT NULL, `attendance_advance_minutes` int NOT NULL DEFAULT '30', `start_date` date NOT NULL, `end_date` date NOT NULL, `subject` varchar(50) NOT NULL, `teacher_id` int NULL, `schedule_type` varchar(20) NOT NULL DEFAULT 'INTERNAL', `rental_id` int NULL, `status` varchar(20) NOT NULL DEFAULT 'active', `notes` text NULL, `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), PRIMARY KEY (`id`)) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `attendance_sessions` (`id` int NOT NULL AUTO_INCREMENT, `schedule_id` int NOT NULL, `class_id` int NOT NULL, `lesson_date` date NOT NULL, `status` varchar(20) NOT NULL DEFAULT 'in_progress', `started_by` int NULL, `started_at` datetime NULL, `completed_by` int NULL, `completed_at` datetime NULL, `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), UNIQUE INDEX `IDX_d4288982a2baaa6085cf872990` (`schedule_id`, `lesson_date`), PRIMARY KEY (`id`)) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `attendance_records` (`id` int NOT NULL AUTO_INCREMENT, `student_id` int NOT NULL, `class_id` int NULL, `schedule_id` int NULL, `attendance_session_id` int NULL, `attendance_date` date NOT NULL, `session` varchar(20) NOT NULL, `status` varchar(20) NOT NULL, `remark` varchar(200) NULL, `source` varchar(20) NOT NULL DEFAULT 'manual', `punch_time` datetime NULL, `punch_source` varchar(40) NULL, `punch_device_name` varchar(100) NULL, `punch_device_id` varchar(100) NULL, `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), UNIQUE INDEX `IDX_43ac61c4ffa4117b738dd3a1e4` (`attendance_session_id`, `student_id`), INDEX `IDX_6b8f69a76bea22962e54bd21f5` (`student_id`, `attendance_date`), INDEX `IDX_a8eee7b8a0e0af27e79cb27ccc` (`class_id`, `attendance_date`), PRIMARY KEY (`id`)) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `ding_attendance_raw` (`id` int NOT NULL AUTO_INCREMENT, `ding_user_id` varchar(100) NOT NULL, `user_name` varchar(100) NOT NULL, `attendance_date` date NOT NULL, `ding_id` varchar(100) NOT NULL, `check_in_time` datetime NULL, `check_out_time` datetime NULL, `attendance_type` varchar(20) NOT NULL, `time_result` varchar(20) NOT NULL, `location_result` varchar(20) NULL, `punch_source` varchar(40) NULL, `punch_device_name` varchar(100) NULL, `punch_device_id` varchar(100) NULL, `match_status` varchar(20) NOT NULL DEFAULT 'unmatched', `matched_student_id` int NULL, `raw_data` text NULL, `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), INDEX `IDX_d6680d5150f0bfa47dbc1fe96f` (`match_status`), INDEX `IDX_088de070f537b15ab7da255e5b` (`attendance_date`), UNIQUE INDEX `IDX_997849bb04ff69149fcf00a8d3` (`ding_id`), PRIMARY KEY (`id`)) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `sync_logs` (`id` int NOT NULL AUTO_INCREMENT, `platform` varchar(20) NOT NULL, `sync_type` varchar(20) NOT NULL, `status` varchar(20) NOT NULL, `records_count` int NOT NULL DEFAULT 0, `error_message` text NULL, `started_at` datetime NULL, `finished_at` datetime NULL, `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), PRIMARY KEY (`id`)) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `sync_state` (`platform` varchar(20) NOT NULL, `last_sync_at` datetime NULL, PRIMARY KEY (`platform`)) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `expense_types` (`id` int NOT NULL AUTO_INCREMENT, `code` varchar(30) NOT NULL, `name` varchar(30) NOT NULL, `category` varchar(20) NOT NULL DEFAULT 'room', `sort_order` int NOT NULL DEFAULT 0, `enabled` tinyint NOT NULL DEFAULT 1, `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), UNIQUE INDEX `IDX_36eda3eb0f6740ecf2ba906012` (`code`), PRIMARY KEY (`id`)) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `notifications` (`id` int NOT NULL AUTO_INCREMENT, `recipient_id` int NOT NULL, `type` varchar(30) NOT NULL, `title` varchar(200) NOT NULL, `content` text NULL, `link` varchar(500) NULL, `is_read` tinyint NOT NULL DEFAULT 0, `read_at` datetime NULL, `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), PRIMARY KEY (`id`)) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `student_profiles` (`id` int NOT NULL AUTO_INCREMENT, `student_id` int NOT NULL, `target_college` varchar(100) NULL, `target_major` varchar(100) NULL, `subject_direction` varchar(50) NULL, `grade` varchar(20) NULL, `profile_date` date NULL, `notes` text NULL, `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), UNIQUE INDEX `IDX_4cedc08d3dc1f2c2da8a12f7a8` (`student_id`), PRIMARY KEY (`id`)) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `student_enrollments` (`id` int NOT NULL AUTO_INCREMENT, `student_id` int NOT NULL, `course_category` varchar(50) NULL, `class_type` varchar(50) NULL, `class_name` varchar(100) NULL, `head_teacher` varchar(50) NULL, `subject_teacher` varchar(50) NULL, `start_date` date NULL, `end_date` date NULL, `status` varchar(20) NOT NULL DEFAULT 'active', `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), PRIMARY KEY (`id`)) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `exam_scores` (`id` int NOT NULL AUTO_INCREMENT, `student_id` int NOT NULL, `enrollment_id` int NULL, `exam_type` varchar(50) NULL, `exam_name` varchar(100) NULL, `subject` varchar(50) NULL, `score` decimal(5,2) NULL, `class_avg` decimal(5,2) NULL, `rank` int NULL, `exam_date` date NULL, `status` varchar(20) NOT NULL DEFAULT 'active', `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), PRIMARY KEY (`id`)) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `learning_records` (`id` int NOT NULL AUTO_INCREMENT, `student_id` int NOT NULL, `record_date` date NULL, `record_type` varchar(50) NULL, `content` text NULL, `follow_up_method` varchar(50) NULL, `next_step` text NULL, `status` varchar(20) NOT NULL DEFAULT 'active', `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), PRIMARY KEY (`id`)) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `result_archives` (`id` int NOT NULL AUTO_INCREMENT, `student_id` int NOT NULL, `culture_final_score` decimal(5,2) NULL, `professional_final_score` decimal(5,2) NULL, `admission_status` varchar(50) NULL, `admitted_college` varchar(100) NULL, `admitted_major` varchar(100) NULL, `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), UNIQUE INDEX `IDX_377bba8eb6a027eecd9737d4ed` (`student_id`), PRIMARY KEY (`id`)) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `archive_attachments` (`id` int NOT NULL AUTO_INCREMENT, `student_id` int NOT NULL, `category` varchar(50) NULL, `file_name` varchar(255) NULL, `file_path` varchar(500) NULL, `file_size` int NULL, `mime_type` varchar(100) NULL, `status` varchar(20) NOT NULL DEFAULT 'active', `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), PRIMARY KEY (`id`)) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `student_ding_mapping` (`id` int NOT NULL AUTO_INCREMENT, `ding_user_id` varchar(100) NOT NULL, `student_id` int NOT NULL, `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), UNIQUE INDEX `IDX_0d1ec47e2f901d37e3b6e56331` (`ding_user_id`), UNIQUE INDEX `IDX_f9ba15ff04de8ffbd8679ae9db` (`student_id`), PRIMARY KEY (`id`)) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `ai_config` (`id` int NOT NULL AUTO_INCREMENT, `singleton_key` varchar(20) NOT NULL DEFAULT 'GLOBAL', `provider` varchar(50) NOT NULL DEFAULT 'OPENAI', `base_url` varchar(500) NULL, `encrypted_api_key` text NULL, `api_key_iv` varchar(50) NULL, `api_key_auth_tag` varchar(50) NULL, `key_last4` varchar(4) NULL, `default_model` varchar(100) NULL, `enabled` tinyint NOT NULL DEFAULT 0, `timeout_ms` int NOT NULL DEFAULT '30000', `verified` tinyint NOT NULL DEFAULT 0, `last_tested_at` datetime NULL, `last_test_latency_ms` int NULL, `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), UNIQUE INDEX `uq_ai_config_singleton` (`singleton_key`), PRIMARY KEY (`id`)) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `student_wallets` (`id` int NOT NULL AUTO_INCREMENT, `student_id` int NOT NULL, `balance` decimal(12,2) NOT NULL DEFAULT 0.00, `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), UNIQUE INDEX `IDX_07a434ad1a960d506386754d59` (`student_id`), UNIQUE INDEX `REL_07a434ad1a960d506386754d59` (`student_id`), PRIMARY KEY (`id`)) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `wallet_transactions` (`id` int NOT NULL AUTO_INCREMENT, `student_id` int NOT NULL, `bill_id` int NULL, `type` varchar(30) NOT NULL, `amount` decimal(12,2) NOT NULL, `balance_after` decimal(12,2) NOT NULL, `description` varchar(300) NULL, `recorded_by` int NULL, `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), INDEX `IDX_680bbd0275ac5e06c179f9b84c` (`student_id`, `created_at`), PRIMARY KEY (`id`)) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `integration_config` (`id` int NOT NULL AUTO_INCREMENT, `type` varchar(50) NOT NULL, `sync_resource` varchar(50) NULL, `is_sync` tinyint NOT NULL DEFAULT 0, `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), PRIMARY KEY (`id`)) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `integration_config_detail` (`id` int NOT NULL AUTO_INCREMENT, `config_id` int NOT NULL, `name` varchar(100) NULL, `type` varchar(50) NOT NULL, `content` text NULL, `enable` tinyint NOT NULL DEFAULT 0, `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), INDEX `IDX_a58106c4b876c86a5e085e4f42` (`config_id`), PRIMARY KEY (`id`)) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `role_permissions` (`role_id` int NOT NULL, `permission_id` int NOT NULL, INDEX `IDX_178199805b901ccd220ab7740e` (`role_id`), INDEX `IDX_17022daf3f885f7d35423e9971` (`permission_id`), PRIMARY KEY (`role_id`, `permission_id`)) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `user_roles` (`user_id` int NOT NULL, `role_id` int NOT NULL, INDEX `IDX_87b8888186ca9769c960e92687` (`user_id`), INDEX `IDX_b23c65e50a758245a33ee35fda` (`role_id`), PRIMARY KEY (`user_id`, `role_id`)) ENGINE=InnoDB;
-- ---------- 外键 ----------
SET @fk_exists := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'FK_178199805b901ccd220ab7740ec' AND CONSTRAINT_TYPE = 'FOREIGN KEY');
SET @sql := IF(@fk_exists = 0, 'ALTER TABLE `role_permissions` ADD CONSTRAINT `FK_178199805b901ccd220ab7740ec` FOREIGN KEY (`role_id`) REFERENCES `roles`(`id`) ON DELETE CASCADE ON UPDATE CASCADE', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @fk_exists := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'FK_17022daf3f885f7d35423e9971e' AND CONSTRAINT_TYPE = 'FOREIGN KEY');
SET @sql := IF(@fk_exists = 0, 'ALTER TABLE `role_permissions` ADD CONSTRAINT `FK_17022daf3f885f7d35423e9971e` FOREIGN KEY (`permission_id`) REFERENCES `permissions`(`id`) ON DELETE CASCADE ON UPDATE CASCADE', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @fk_exists := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'FK_87b8888186ca9769c960e926870' AND CONSTRAINT_TYPE = 'FOREIGN KEY');
SET @sql := IF(@fk_exists = 0, 'ALTER TABLE `user_roles` ADD CONSTRAINT `FK_87b8888186ca9769c960e926870` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE CASCADE ON UPDATE CASCADE', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @fk_exists := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'FK_b23c65e50a758245a33ee35fda1' AND CONSTRAINT_TYPE = 'FOREIGN KEY');
SET @sql := IF(@fk_exists = 0, 'ALTER TABLE `user_roles` ADD CONSTRAINT `FK_b23c65e50a758245a33ee35fda1` FOREIGN KEY (`role_id`) REFERENCES `roles`(`id`) ON DELETE NO ACTION ON UPDATE NO ACTION', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @fk_exists := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'FK_7bb1ca73f8161af27e538917a55' AND CONSTRAINT_TYPE = 'FOREIGN KEY');
SET @sql := IF(@fk_exists = 0, 'ALTER TABLE `room_expenses` ADD CONSTRAINT `FK_7bb1ca73f8161af27e538917a55` FOREIGN KEY (`room_id`) REFERENCES `rooms`(`id`) ON DELETE NO ACTION ON UPDATE NO ACTION', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @fk_exists := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'FK_fd7413faee42749f1b2e8270394' AND CONSTRAINT_TYPE = 'FOREIGN KEY');
SET @sql := IF(@fk_exists = 0, 'ALTER TABLE `beds` ADD CONSTRAINT `FK_fd7413faee42749f1b2e8270394` FOREIGN KEY (`room_id`) REFERENCES `rooms`(`id`) ON DELETE CASCADE ON UPDATE NO ACTION', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @fk_exists := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'FK_344ae29b9cacbee1e24da7c07ea' AND CONSTRAINT_TYPE = 'FOREIGN KEY');
SET @sql := IF(@fk_exists = 0, 'ALTER TABLE `lockers` ADD CONSTRAINT `FK_344ae29b9cacbee1e24da7c07ea` FOREIGN KEY (`room_id`) REFERENCES `rooms`(`id`) ON DELETE CASCADE ON UPDATE NO ACTION', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @fk_exists := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'FK_bed301f5a4e2135dd14b55fec0e' AND CONSTRAINT_TYPE = 'FOREIGN KEY');
SET @sql := IF(@fk_exists = 0, 'ALTER TABLE `occupancies` ADD CONSTRAINT `FK_bed301f5a4e2135dd14b55fec0e` FOREIGN KEY (`bed_id`) REFERENCES `beds`(`id`) ON DELETE NO ACTION ON UPDATE NO ACTION', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @fk_exists := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'FK_ea80244bd95c8c04334931e39a2' AND CONSTRAINT_TYPE = 'FOREIGN KEY');
SET @sql := IF(@fk_exists = 0, 'ALTER TABLE `occupancies` ADD CONSTRAINT `FK_ea80244bd95c8c04334931e39a2` FOREIGN KEY (`locker_id`) REFERENCES `lockers`(`id`) ON DELETE NO ACTION ON UPDATE NO ACTION', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @fk_exists := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'FK_3c80232a56303a75ffd544b982f' AND CONSTRAINT_TYPE = 'FOREIGN KEY');
SET @sql := IF(@fk_exists = 0, 'ALTER TABLE `occupancies` ADD CONSTRAINT `FK_3c80232a56303a75ffd544b982f` FOREIGN KEY (`responsible_organization_id`) REFERENCES `organizations`(`id`) ON DELETE NO ACTION ON UPDATE NO ACTION', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @fk_exists := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'FK_dc062720596049470b56ce06973' AND CONSTRAINT_TYPE = 'FOREIGN KEY');
SET @sql := IF(@fk_exists = 0, 'ALTER TABLE `occupancies` ADD CONSTRAINT `FK_dc062720596049470b56ce06973` FOREIGN KEY (`student_id`) REFERENCES `students`(`id`) ON DELETE NO ACTION ON UPDATE NO ACTION', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @fk_exists := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'FK_9f7acc7567e42dda281ead9a75a' AND CONSTRAINT_TYPE = 'FOREIGN KEY');
SET @sql := IF(@fk_exists = 0, 'ALTER TABLE `occupancies` ADD CONSTRAINT `FK_9f7acc7567e42dda281ead9a75a` FOREIGN KEY (`room_id`) REFERENCES `rooms`(`id`) ON DELETE NO ACTION ON UPDATE NO ACTION', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @fk_exists := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'FK_5e5bcfb70705c2c349c74b70912' AND CONSTRAINT_TYPE = 'FOREIGN KEY');
SET @sql := IF(@fk_exists = 0, 'ALTER TABLE `personal_expenses` ADD CONSTRAINT `FK_5e5bcfb70705c2c349c74b70912` FOREIGN KEY (`student_id`) REFERENCES `students`(`id`) ON DELETE NO ACTION ON UPDATE NO ACTION', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @fk_exists := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'FK_b424156152a3230b034bdb51db4' AND CONSTRAINT_TYPE = 'FOREIGN KEY');
SET @sql := IF(@fk_exists = 0, 'ALTER TABLE `bill_items` ADD CONSTRAINT `FK_b424156152a3230b034bdb51db4` FOREIGN KEY (`bill_id`) REFERENCES `bills`(`id`) ON DELETE NO ACTION ON UPDATE NO ACTION', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @fk_exists := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'FK_65a6cb602ebcbac1bf7a995e0c9' AND CONSTRAINT_TYPE = 'FOREIGN KEY');
SET @sql := IF(@fk_exists = 0, 'ALTER TABLE `bills` ADD CONSTRAINT `FK_65a6cb602ebcbac1bf7a995e0c9` FOREIGN KEY (`student_id`) REFERENCES `students`(`id`) ON DELETE NO ACTION ON UPDATE NO ACTION', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @fk_exists := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'FK_fb3eff90b11bddf7285f9b4e281' AND CONSTRAINT_TYPE = 'FOREIGN KEY');
SET @sql := IF(@fk_exists = 0, 'ALTER TABLE `students` ADD CONSTRAINT `FK_fb3eff90b11bddf7285f9b4e281` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE SET NULL ON UPDATE NO ACTION', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @fk_exists := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'FK_9571384818ecf499779d3a9d141' AND CONSTRAINT_TYPE = 'FOREIGN KEY');
SET @sql := IF(@fk_exists = 0, 'ALTER TABLE `students` ADD CONSTRAINT `FK_9571384818ecf499779d3a9d141` FOREIGN KEY (`organization_id`) REFERENCES `organizations`(`id`) ON DELETE NO ACTION ON UPDATE NO ACTION', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @fk_exists := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'FK_fba3c52b86d51fff34593877ff5' AND CONSTRAINT_TYPE = 'FOREIGN KEY');
SET @sql := IF(@fk_exists = 0, 'ALTER TABLE `deposit_installments` ADD CONSTRAINT `FK_fba3c52b86d51fff34593877ff5` FOREIGN KEY (`deposit_id`) REFERENCES `deposits`(`id`) ON DELETE CASCADE ON UPDATE NO ACTION', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @fk_exists := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'FK_ec1b340d2963ed907421696fc33' AND CONSTRAINT_TYPE = 'FOREIGN KEY');
SET @sql := IF(@fk_exists = 0, 'ALTER TABLE `deposits` ADD CONSTRAINT `FK_ec1b340d2963ed907421696fc33` FOREIGN KEY (`student_id`) REFERENCES `students`(`id`) ON DELETE NO ACTION ON UPDATE NO ACTION', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @fk_exists := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'FK_6a0af46fe476070c4c660307641' AND CONSTRAINT_TYPE = 'FOREIGN KEY');
SET @sql := IF(@fk_exists = 0, 'ALTER TABLE `classroom_rentals` ADD CONSTRAINT `FK_6a0af46fe476070c4c660307641` FOREIGN KEY (`classroom_id`) REFERENCES `classrooms`(`id`) ON DELETE NO ACTION ON UPDATE NO ACTION', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @fk_exists := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'FK_40a46e95470ff454ffdcbb1e902' AND CONSTRAINT_TYPE = 'FOREIGN KEY');
SET @sql := IF(@fk_exists = 0, 'ALTER TABLE `classroom_rentals` ADD CONSTRAINT `FK_40a46e95470ff454ffdcbb1e902` FOREIGN KEY (`lessor_organization_id`) REFERENCES `organizations`(`id`) ON DELETE NO ACTION ON UPDATE NO ACTION', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @fk_exists := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'FK_f60533a91f16a331c9388308418' AND CONSTRAINT_TYPE = 'FOREIGN KEY');
SET @sql := IF(@fk_exists = 0, 'ALTER TABLE `classroom_rentals` ADD CONSTRAINT `FK_f60533a91f16a331c9388308418` FOREIGN KEY (`lessee_organization_id`) REFERENCES `organizations`(`id`) ON DELETE NO ACTION ON UPDATE NO ACTION', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @fk_exists := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'FK_60674dc4f23b794de0a3560e200' AND CONSTRAINT_TYPE = 'FOREIGN KEY');
SET @sql := IF(@fk_exists = 0, 'ALTER TABLE `class_student` ADD CONSTRAINT `FK_60674dc4f23b794de0a3560e200` FOREIGN KEY (`class_id`) REFERENCES `classes`(`id`) ON DELETE CASCADE ON UPDATE NO ACTION', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @fk_exists := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'FK_451334295b9cf221d55aca7cac1' AND CONSTRAINT_TYPE = 'FOREIGN KEY');
SET @sql := IF(@fk_exists = 0, 'ALTER TABLE `class_student` ADD CONSTRAINT `FK_451334295b9cf221d55aca7cac1` FOREIGN KEY (`student_id`) REFERENCES `students`(`id`) ON DELETE NO ACTION ON UPDATE NO ACTION', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @fk_exists := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'FK_9b647c8d8894a661dc1dac40361' AND CONSTRAINT_TYPE = 'FOREIGN KEY');
SET @sql := IF(@fk_exists = 0, 'ALTER TABLE `class_teacher` ADD CONSTRAINT `FK_9b647c8d8894a661dc1dac40361` FOREIGN KEY (`class_id`) REFERENCES `classes`(`id`) ON DELETE CASCADE ON UPDATE NO ACTION', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @fk_exists := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'FK_a9f0e77d193b015b6d5ca6637f5' AND CONSTRAINT_TYPE = 'FOREIGN KEY');
SET @sql := IF(@fk_exists = 0, 'ALTER TABLE `class_teacher` ADD CONSTRAINT `FK_a9f0e77d193b015b6d5ca6637f5` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE CASCADE ON UPDATE NO ACTION', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @fk_exists := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'FK_attendance_devices_classroom' AND CONSTRAINT_TYPE = 'FOREIGN KEY');
SET @sql := IF(@fk_exists = 0, 'ALTER TABLE `attendance_devices` ADD CONSTRAINT `FK_attendance_devices_classroom` FOREIGN KEY (`classroom_id`) REFERENCES `classrooms`(`id`) ON DELETE CASCADE ON UPDATE NO ACTION', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @fk_exists := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'FK_0aa74ee427967d4c82f298511bc' AND CONSTRAINT_TYPE = 'FOREIGN KEY');
SET @sql := IF(@fk_exists = 0, 'ALTER TABLE `class_schedule` ADD CONSTRAINT `FK_0aa74ee427967d4c82f298511bc` FOREIGN KEY (`class_id`) REFERENCES `classes`(`id`) ON DELETE NO ACTION ON UPDATE NO ACTION', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @fk_exists := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'FK_c1d5d50c871fdf563be13d6e93a' AND CONSTRAINT_TYPE = 'FOREIGN KEY');
SET @sql := IF(@fk_exists = 0, 'ALTER TABLE `class_schedule` ADD CONSTRAINT `FK_c1d5d50c871fdf563be13d6e93a` FOREIGN KEY (`classroom_id`) REFERENCES `classrooms`(`id`) ON DELETE NO ACTION ON UPDATE NO ACTION', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @fk_exists := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'FK_f1e761cac59f2d0a3d7949468c5' AND CONSTRAINT_TYPE = 'FOREIGN KEY');
SET @sql := IF(@fk_exists = 0, 'ALTER TABLE `class_schedule` ADD CONSTRAINT `FK_f1e761cac59f2d0a3d7949468c5` FOREIGN KEY (`teacher_id`) REFERENCES `users`(`id`) ON DELETE NO ACTION ON UPDATE NO ACTION', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @fk_exists := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'FK_107206b64415a644d571d597ca3' AND CONSTRAINT_TYPE = 'FOREIGN KEY');
SET @sql := IF(@fk_exists = 0, 'ALTER TABLE `attendance_sessions` ADD CONSTRAINT `FK_107206b64415a644d571d597ca3` FOREIGN KEY (`schedule_id`) REFERENCES `class_schedule`(`id`) ON DELETE RESTRICT ON UPDATE NO ACTION', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @fk_exists := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'FK_6fc552e07b31ac92445cd3e21fa' AND CONSTRAINT_TYPE = 'FOREIGN KEY');
SET @sql := IF(@fk_exists = 0, 'ALTER TABLE `attendance_sessions` ADD CONSTRAINT `FK_6fc552e07b31ac92445cd3e21fa` FOREIGN KEY (`class_id`) REFERENCES `classes`(`id`) ON DELETE RESTRICT ON UPDATE NO ACTION', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @fk_exists := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'FK_dbace05c012526710663f8d8911' AND CONSTRAINT_TYPE = 'FOREIGN KEY');
SET @sql := IF(@fk_exists = 0, 'ALTER TABLE `attendance_records` ADD CONSTRAINT `FK_dbace05c012526710663f8d8911` FOREIGN KEY (`student_id`) REFERENCES `students`(`id`) ON DELETE CASCADE ON UPDATE NO ACTION', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @fk_exists := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'FK_cc48ccbd03396874857ab06ac3b' AND CONSTRAINT_TYPE = 'FOREIGN KEY');
SET @sql := IF(@fk_exists = 0, 'ALTER TABLE `attendance_records` ADD CONSTRAINT `FK_cc48ccbd03396874857ab06ac3b` FOREIGN KEY (`class_id`) REFERENCES `classes`(`id`) ON DELETE SET NULL ON UPDATE NO ACTION', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @fk_exists := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'FK_0dfe6d219c4a7a162ca0f84243c' AND CONSTRAINT_TYPE = 'FOREIGN KEY');
SET @sql := IF(@fk_exists = 0, 'ALTER TABLE `attendance_records` ADD CONSTRAINT `FK_0dfe6d219c4a7a162ca0f84243c` FOREIGN KEY (`schedule_id`) REFERENCES `class_schedule`(`id`) ON DELETE SET NULL ON UPDATE NO ACTION', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @fk_exists := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'FK_32a57ba853da6939ceaccf8cfc8' AND CONSTRAINT_TYPE = 'FOREIGN KEY');
SET @sql := IF(@fk_exists = 0, 'ALTER TABLE `attendance_records` ADD CONSTRAINT `FK_32a57ba853da6939ceaccf8cfc8` FOREIGN KEY (`attendance_session_id`) REFERENCES `attendance_sessions`(`id`) ON DELETE SET NULL ON UPDATE NO ACTION', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @fk_exists := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'FK_2b06491a70540db69e78e57ecf2' AND CONSTRAINT_TYPE = 'FOREIGN KEY');
SET @sql := IF(@fk_exists = 0, 'ALTER TABLE `ding_attendance_raw` ADD CONSTRAINT `FK_2b06491a70540db69e78e57ecf2` FOREIGN KEY (`matched_student_id`) REFERENCES `students`(`id`) ON DELETE SET NULL ON UPDATE NO ACTION', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @fk_exists := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'FK_5332a4daa46fd3f4e6625dd275d' AND CONSTRAINT_TYPE = 'FOREIGN KEY');
SET @sql := IF(@fk_exists = 0, 'ALTER TABLE `notifications` ADD CONSTRAINT `FK_5332a4daa46fd3f4e6625dd275d` FOREIGN KEY (`recipient_id`) REFERENCES `users`(`id`) ON DELETE CASCADE ON UPDATE NO ACTION', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @fk_exists := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'FK_4cedc08d3dc1f2c2da8a12f7a88' AND CONSTRAINT_TYPE = 'FOREIGN KEY');
SET @sql := IF(@fk_exists = 0, 'ALTER TABLE `student_profiles` ADD CONSTRAINT `FK_4cedc08d3dc1f2c2da8a12f7a88` FOREIGN KEY (`student_id`) REFERENCES `students`(`id`) ON DELETE NO ACTION ON UPDATE NO ACTION', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @fk_exists := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'FK_08caafd8a026a19ecf54db0e958' AND CONSTRAINT_TYPE = 'FOREIGN KEY');
SET @sql := IF(@fk_exists = 0, 'ALTER TABLE `student_enrollments` ADD CONSTRAINT `FK_08caafd8a026a19ecf54db0e958` FOREIGN KEY (`student_id`) REFERENCES `students`(`id`) ON DELETE NO ACTION ON UPDATE NO ACTION', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @fk_exists := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'FK_925e4f79518c947512cb600b452' AND CONSTRAINT_TYPE = 'FOREIGN KEY');
SET @sql := IF(@fk_exists = 0, 'ALTER TABLE `exam_scores` ADD CONSTRAINT `FK_925e4f79518c947512cb600b452` FOREIGN KEY (`student_id`) REFERENCES `students`(`id`) ON DELETE NO ACTION ON UPDATE NO ACTION', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @fk_exists := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'FK_b0d812b639f32939ea57b81a89f' AND CONSTRAINT_TYPE = 'FOREIGN KEY');
SET @sql := IF(@fk_exists = 0, 'ALTER TABLE `exam_scores` ADD CONSTRAINT `FK_b0d812b639f32939ea57b81a89f` FOREIGN KEY (`enrollment_id`) REFERENCES `student_enrollments`(`id`) ON DELETE NO ACTION ON UPDATE NO ACTION', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @fk_exists := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'FK_61f4664f02d86245e1231493951' AND CONSTRAINT_TYPE = 'FOREIGN KEY');
SET @sql := IF(@fk_exists = 0, 'ALTER TABLE `learning_records` ADD CONSTRAINT `FK_61f4664f02d86245e1231493951` FOREIGN KEY (`student_id`) REFERENCES `students`(`id`) ON DELETE NO ACTION ON UPDATE NO ACTION', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @fk_exists := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'FK_377bba8eb6a027eecd9737d4ed6' AND CONSTRAINT_TYPE = 'FOREIGN KEY');
SET @sql := IF(@fk_exists = 0, 'ALTER TABLE `result_archives` ADD CONSTRAINT `FK_377bba8eb6a027eecd9737d4ed6` FOREIGN KEY (`student_id`) REFERENCES `students`(`id`) ON DELETE NO ACTION ON UPDATE NO ACTION', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @fk_exists := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'FK_0bc7365c2629d0c6dae8052fc25' AND CONSTRAINT_TYPE = 'FOREIGN KEY');
SET @sql := IF(@fk_exists = 0, 'ALTER TABLE `archive_attachments` ADD CONSTRAINT `FK_0bc7365c2629d0c6dae8052fc25` FOREIGN KEY (`student_id`) REFERENCES `students`(`id`) ON DELETE NO ACTION ON UPDATE NO ACTION', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @fk_exists := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'FK_f9ba15ff04de8ffbd8679ae9dbb' AND CONSTRAINT_TYPE = 'FOREIGN KEY');
SET @sql := IF(@fk_exists = 0, 'ALTER TABLE `student_ding_mapping` ADD CONSTRAINT `FK_f9ba15ff04de8ffbd8679ae9dbb` FOREIGN KEY (`student_id`) REFERENCES `students`(`id`) ON DELETE CASCADE ON UPDATE NO ACTION', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @fk_exists := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'FK_07a434ad1a960d506386754d592' AND CONSTRAINT_TYPE = 'FOREIGN KEY');
SET @sql := IF(@fk_exists = 0, 'ALTER TABLE `student_wallets` ADD CONSTRAINT `FK_07a434ad1a960d506386754d592` FOREIGN KEY (`student_id`) REFERENCES `students`(`id`) ON DELETE CASCADE ON UPDATE NO ACTION', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @fk_exists := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'FK_a58106c4b876c86a5e085e4f42b' AND CONSTRAINT_TYPE = 'FOREIGN KEY');
SET @sql := IF(@fk_exists = 0, 'ALTER TABLE `integration_config_detail` ADD CONSTRAINT `FK_a58106c4b876c86a5e085e4f42b` FOREIGN KEY (`config_id`) REFERENCES `integration_config`(`id`) ON DELETE NO ACTION ON UPDATE NO ACTION', 'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET FOREIGN_KEY_CHECKS = 1;
-- ---------- 初始化数据 ----------
INSERT INTO `permissions` (`id`, `code`, `name`, `group`) VALUES
('1', 'dashboard:view', '查看数据面板', 'dashboard'),
('2', 'profile:view', '查看个人资料', 'profile'),
('3', 'notification:view', '查看通知', 'notification'),
('4', 'student:view', '查看学生管理', 'student'),
('5', 'student:basic-view', '查看学生基础信息', 'student-scope'),
('6', 'teacher-workspace:view', '查看教师工作台', 'teacher-workspace'),
('7', 'teacher:view', '查看教师', 'teacher'),
('8', 'teacher:edit', '编辑教师', 'teacher'),
('9', 'student:create', '新增学生', 'student'),
('10', 'student:edit', '编辑学生', 'student'),
('11', 'student:delete', '归档学生', 'student'),
('12', 'student:import', '导入学生', 'student'),
('13', 'student:export', '导出学生', 'student'),
('14', 'room:view', '查看宿舍', 'room'),
('15', 'room:create', '新增宿舍', 'room'),
('16', 'room:edit', '编辑宿舍', 'room'),
('17', 'room:delete', '归档宿舍', 'room'),
('18', 'occupancy:view', '查看入住', 'occupancy'),
('19', 'occupancy:checkin', '办理入住', 'occupancy'),
('20', 'occupancy:checkout', '办理退宿', 'occupancy'),
('21', 'occupancy:transfer', '调换宿舍', 'occupancy'),
('22', 'occupancy:delete', '归档入住记录', 'occupancy'),
('23', 'expense:view', '查看费用', 'expense'),
('24', 'expense:create', '录入费用', 'expense'),
('25', 'expense:edit', '编辑费用', 'expense'),
('26', 'expense:delete', '归档费用', 'expense'),
('27', 'bill:view', '查看账单', 'bill'),
('28', 'bill:generate', '生成账单', 'bill'),
('29', 'bill:confirm', '确认账单', 'bill'),
('30', 'bill:delete', '归档账单', 'bill'),
('31', 'bill:export-excel', '导出 Excel', 'bill'),
('32', 'bill:export-pdf', '导出 PDF', 'bill'),
('33', 'deposit:view', '查看押金', 'deposit'),
('34', 'deposit:create', '新增押金', 'deposit'),
('35', 'deposit:edit', '编辑押金', 'deposit'),
('36', 'deposit:delete', '归档押金', 'deposit'),
('37', 'deposit:refund', '直接退还押金', 'deposit'),
('38', 'wallet:view', '查看学生余额', 'wallet'),
('39', 'wallet:edit', '充值和调账', 'wallet'),
('40', 'classroom:view', '查看教室', 'classroom'),
('41', 'classroom:create', '新增教室', 'classroom'),
('42', 'classroom:edit', '编辑教室', 'classroom'),
('43', 'classroom:delete', '归档教室', 'classroom'),
('44', 'organization:view', '查看机构', 'organization'),
('45', 'organization:create', '新增机构', 'organization'),
('46', 'organization:edit', '编辑机构', 'organization'),
('47', 'organization:delete', '归档机构', 'organization'),
('48', 'rental:view', '查看租赁订单', 'rental'),
('49', 'rental:create', '新增租赁订单', 'rental'),
('50', 'rental:edit', '编辑租赁订单', 'rental'),
('51', 'rental:delete', '归档租赁订单', 'rental'),
('52', 'log:view', '查看操作日志', 'log'),
('53', 'log:create', '写入操作日志', 'log'),
('54', 'user:view', '查看用户', 'user'),
('55', 'user:create', '创建用户', 'user'),
('56', 'user:edit', '编辑用户', 'user'),
('57', 'user:delete', '归档用户', 'user'),
('58', 'user:reset-password', '重置密码', 'user'),
('59', 'role:view', '查看角色', 'role'),
('60', 'role:create', '创建角色', 'role'),
('61', 'role:edit', '编辑角色', 'role'),
('62', 'role:delete', '停用角色', 'role'),
('63', 'class:view', '查看班级', 'class'),
('64', 'class:create', '创建班级', 'class'),
('65', 'class:edit', '编辑班级', 'class'),
('66', 'class:delete', '归档班级', 'class'),
('67', 'schedule:view', '查看排课', 'schedule'),
('68', 'schedule:create', '创建排课', 'schedule'),
('69', 'schedule:edit', '编辑排课', 'schedule'),
('70', 'schedule:delete', '停用排课', 'schedule'),
('71', 'attendance:view', '查看考勤', 'attendance'),
('72', 'attendance:create', '新增考勤', 'attendance'),
('73', 'attendance:edit', '编辑全部考勤', 'attendance'),
('74', 'attendance:self-edit', '编辑任教班级考勤', 'attendance-scope'),
('75', 'attendance:export', '导出考勤', 'attendance'),
('76', 'attendance:generate', '按课表生成考勤', 'attendance'),
('77', 'learning:create', '创建学习任务', 'learning'),
('78', 'learning:edit', '编辑学习任务', 'learning'),
('79', 'learning:delete', '删除学习任务', 'learning'),
('80', 'exam:create', '创建考试', 'exam'),
('81', 'exam:edit', '编辑考试', 'exam'),
('82', 'exam:delete', '删除考试', 'exam'),
('83', 'sync:trigger', '触发数据同步', 'sync'),
('84', 'sync:read', '查看同步状态', 'sync'),
('85', 'integration:trigger', '触发集成', 'integration'),
('86', 'integration:read', '查看集成状态', 'integration'),
('87', 'department:view', '查看部门', 'department'),
('88', 'department:edit', '编辑部门', 'department'),
('89', 'department:delete', '删除部门', 'department'),
('90', 'ai:config:read', '查看 AI 配置', 'ai'),
('91', 'ai:config:write', '修改 AI 配置', 'ai'),
('92', 'ai:config:test', '测试 AI 连接', 'ai') AS new_values
ON DUPLICATE KEY UPDATE `name` = new_values.`name`, `group` = new_values.`group`;
INSERT INTO `roles` (`id`, `name`, `code`, `description`, `is_system`, `status`) VALUES
('1', '超级管理员', 'super_admin', '系统初始化、应急维护和全局权限处理', '1', '1'),
('2', '任课老师', 'teacher', '查看自己的排课、今日课程和任教班级考勤', '1', '1'),
('3', '教务管理员', 'academic', '管理学生、班级、教师、全局排课和历史考勤', '1', '1'),
('4', '住宿运营管理员', 'accommodation_operations', '管理宿舍、入住、住宿费用、账单、押金和退宿结算', '1', '1'),
('5', '教室运营管理员', 'classroom_operations', '管理教室、教室排期、外部机构和租赁订单', '1', '1'),
('6', '系统管理员', 'system_admin', '管理账号、角色、日志、同步和系统配置', '1', '1') AS new_values
ON DUPLICATE KEY UPDATE `name` = new_values.`name`, `description` = new_values.`description`, `is_system` = new_values.`is_system`, `status` = new_values.`status`;
INSERT IGNORE INTO `role_permissions` (`role_id`, `permission_id`) VALUES
('1', '1'),
('1', '2'),
('1', '3'),
('1', '4'),
('1', '5'),
('1', '6'),
('1', '7'),
('1', '8'),
('1', '9'),
('1', '10'),
('1', '11'),
('1', '12'),
('1', '13'),
('1', '14'),
('1', '15'),
('1', '16'),
('1', '17'),
('1', '18'),
('1', '19'),
('1', '20'),
('1', '21'),
('1', '22'),
('1', '23'),
('1', '24'),
('1', '25'),
('1', '26'),
('1', '27'),
('1', '28'),
('1', '29'),
('1', '30'),
('1', '31'),
('1', '32'),
('1', '33'),
('1', '34'),
('1', '35'),
('1', '36'),
('1', '37'),
('1', '38'),
('1', '39'),
('1', '40'),
('1', '41'),
('1', '42'),
('1', '43'),
('1', '44'),
('1', '45'),
('1', '46'),
('1', '47'),
('1', '48'),
('1', '49'),
('1', '50'),
('1', '51'),
('1', '52'),
('1', '53'),
('1', '54'),
('1', '55'),
('1', '56'),
('1', '57'),
('1', '58'),
('1', '59'),
('1', '60'),
('1', '61'),
('1', '62'),
('1', '63'),
('1', '64'),
('1', '65'),
('1', '66'),
('1', '67'),
('1', '68'),
('1', '69'),
('1', '70'),
('1', '71'),
('1', '72'),
('1', '73'),
('1', '74'),
('1', '75'),
('1', '76'),
('1', '77'),
('1', '78'),
('1', '79'),
('1', '80'),
('1', '81'),
('1', '82'),
('1', '83'),
('1', '84'),
('1', '85'),
('1', '86'),
('1', '87'),
('1', '88'),
('1', '89'),
('1', '90'),
('1', '91'),
('1', '92'),
('2', '6'),
('2', '67'),
('2', '71'),
('2', '72'),
('2', '74'),
('2', '2'),
('2', '3'),
('3', '6'),
('3', '7'),
('3', '8'),
('3', '84'),
('3', '83'),
('3', '1'),
('3', '2'),
('3', '3'),
('3', '4'),
('3', '9'),
('3', '10'),
('3', '11'),
('3', '12'),
('3', '13'),
('3', '40'),
('3', '41'),
('3', '42'),
('3', '43'),
('3', '63'),
('3', '64'),
('3', '65'),
('3', '66'),
('3', '67'),
('3', '68'),
('3', '69'),
('3', '70'),
('3', '71'),
('3', '72'),
('3', '73'),
('3', '75'),
('3', '76'),
('3', '77'),
('3', '78'),
('3', '79'),
('3', '80'),
('3', '81'),
('3', '82'),
('4', '5'),
('4', '1'),
('4', '2'),
('4', '3'),
('4', '14'),
('4', '15'),
('4', '16'),
('4', '17'),
('4', '18'),
('4', '19'),
('4', '20'),
('4', '21'),
('4', '22'),
('4', '23'),
('4', '24'),
('4', '25'),
('4', '26'),
('4', '27'),
('4', '28'),
('4', '29'),
('4', '30'),
('4', '31'),
('4', '32'),
('4', '33'),
('4', '34'),
('4', '35'),
('4', '36'),
('4', '37'),
('4', '38'),
('4', '39'),
('5', '2'),
('5', '3'),
('5', '40'),
('5', '41'),
('5', '42'),
('5', '43'),
('5', '44'),
('5', '45'),
('5', '46'),
('5', '47'),
('5', '48'),
('5', '49'),
('5', '50'),
('5', '51'),
('6', '2'),
('6', '3'),
('6', '52'),
('6', '53'),
('6', '54'),
('6', '55'),
('6', '56'),
('6', '57'),
('6', '58'),
('6', '59'),
('6', '60'),
('6', '61'),
('6', '62'),
('6', '83'),
('6', '84'),
('6', '85'),
('6', '86'),
('6', '87'),
('6', '88'),
('6', '89'),
('6', '90'),
('6', '91'),
('6', '92');
INSERT INTO `users` (`id`, `username`, `password_hash`, `name`, `is_active`, `is_archived`) VALUES
('1', 'admin', '$2b$10$vu8/qt4UCb6QnbmgQdbUtexPZWieuok030ZF0bI6xfpcf49MEML62', '管理员', '1', '0')
ON DUPLICATE KEY UPDATE `username` = `username`;
INSERT IGNORE INTO `user_roles` (`user_id`, `role_id`) VALUES (1, 1);
INSERT INTO `organizations` (`id`, `public_id`, `code`, `name`, `is_host`, `color`, `notes`, `status`) VALUES
('1', '01900000-0000-7000-8000-000000000001', 'HOST', '本机构', '1', '#1677ff', '系统默认运营主体', 'active') AS new_values
ON DUPLICATE KEY UPDATE `name` = new_values.`name`, `is_host` = new_values.`is_host`, `status` = new_values.`status`;
INSERT INTO `expense_types` (`code`, `name`, `category`, `sort_order`, `enabled`) VALUES
('water', '水费', 'room', '1', '1'),
('electricity', '电费', 'room', '2', '1'),
('cleaning', '保洁费', 'room', '3', '1'),
('damage', '损坏赔偿', 'both', '4', '1'),
('penalty', '罚款', 'personal', '5', '1'),
('key', '钥匙费', 'personal', '6', '1'),
('remote', '空调遥控器', 'personal', '7', '1'),
('deposit_deduction', '押金扣除', 'personal', '8', '1'),
('other', '其他', 'both', '99', '1') AS new_values
ON DUPLICATE KEY UPDATE `name` = new_values.`name`, `category` = new_values.`category`, `sort_order` = new_values.`sort_order`, `enabled` = new_values.`enabled`;
INSERT INTO `ai_config` (`singleton_key`, `provider`, `enabled`, `timeout_ms`, `verified`) VALUES
('GLOBAL', 'OPENAI', '0', '30000', '0')
ON DUPLICATE KEY UPDATE `singleton_key` = `singleton_key`;
INSERT INTO `integration_config` (`id`, `type`, `is_sync`) VALUES
('1', 'THIRD', '0') AS new_values
ON DUPLICATE KEY UPDATE `type` = new_values.`type`;
-- 让后续 AUTO_INCREMENT 从安全位置继续。
ALTER TABLE `permissions` AUTO_INCREMENT = 1000;
ALTER TABLE `roles` AUTO_INCREMENT = 100;
ALTER TABLE `users` AUTO_INCREMENT = 100;
ALTER TABLE `organizations` AUTO_INCREMENT = 100;
ALTER TABLE `attendance_devices` AUTO_INCREMENT = 100;