feat: 新增统一身份认证用户数据定时同步脚本

将校园系统部门、教职工、学生数据同步到认证中间库6张标准表,支持 MySQL 本地测试与 Oracle 生产目标库配置。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
“wuzhen”andCursor committed 2026-08-24 10:01:16 +08:00
1 parent 672f45befd
commit 334bd8742e
12 files changed
+1392

No files matched your search

@@ -0,0 +1,313 @@
-- Create table
create table GXXX_YXSDWJBSJZL
(
dwh VARCHAR2(10) not null,
dwmc VARCHAR2(60),
dwywmc VARCHAR2(180),
dwjc VARCHAR2(12),
dwywjc VARCHAR2(8),
dwjp VARCHAR2(6),
dwdz VARCHAR2(180),
lsdwh VARCHAR2(10),
dwlbm VARCHAR2(1),
dwbbm VARCHAR2(1),
dwyxbs VARCHAR2(1),
sxrq VARCHAR2(8),
sfst VARCHAR2(1),
jlny VARCHAR2(6),
dwfzrh VARCHAR2(20),
zhgxsj VARCHAR2(20) default to_char(sysdate,'yyyymmddhh24miss'),
scbz VARCHAR2(1),
dwjb VARCHAR2(8)
);
-- Add comments to the table
comment on table GXXX_YXSDWJBSJZL is 'GXXX0301院系所单位基本数据子类';
-- Add comments to the columns
comment on column GXXX_YXSDWJBSJZL.dwh is '单位号';
comment on column GXXX_YXSDWJBSJZL.dwmc is '单位名称';
comment on column GXXX_YXSDWJBSJZL.dwywmc is '单位英文名称';
comment on column GXXX_YXSDWJBSJZL.dwjc is '单位简称';
comment on column GXXX_YXSDWJBSJZL.dwywjc is '单位英文简称';
comment on column GXXX_YXSDWJBSJZL.dwjp is '单位简拼';
comment on column GXXX_YXSDWJBSJZL.dwdz is '单位地址';
comment on column GXXX_YXSDWJBSJZL.lsdwh is '隶属单位号';
comment on column GXXX_YXSDWJBSJZL.dwlbm is '单位类别码';
comment on column GXXX_YXSDWJBSJZL.dwbbm is '单位办别码';
comment on column GXXX_YXSDWJBSJZL.dwyxbs is '单位有效标识';
comment on column GXXX_YXSDWJBSJZL.sxrq is '失效日期';
comment on column GXXX_YXSDWJBSJZL.sfst is '是否实体';
comment on column GXXX_YXSDWJBSJZL.jlny is '建立年月';
comment on column GXXX_YXSDWJBSJZL.dwfzrh is '单位负责人号';
comment on column GXXX_YXSDWJBSJZL.zhgxsj is '最后更新时间';
comment on column GXXX_YXSDWJBSJZL.scbz is '删除标志:y/n';
comment on column GXXX_YXSDWJBSJZL.dwjb is '单位级别';
-- Create/Recreate primary, unique and foreign key constraints
alter table GXXX_YXSDWJBSJZL add primary key (DWH);
/
-- Create table
create table GXJG_JZGJCSJZLB
(
gh VARCHAR2(20) not null,
dwh VARCHAR2(10) not null,
xm VARCHAR2(36),
ywxm VARCHAR2(60),
xmpy VARCHAR2(60),
cym VARCHAR2(36),
xbm VARCHAR2(1),
csrq VARCHAR2(8),
csdm VARCHAR2(6),
jg VARCHAR2(20),
mzm VARCHAR2(2),
gjdqm VARCHAR2(3),
sfzjlxm VARCHAR2(1),
sfzjh VARCHAR2(20),
hyztm VARCHAR2(2),
gatqwm VARCHAR2(2),
zzmmm VARCHAR2(2),
jkzkm VARCHAR2(2),
xyzjm VARCHAR2(2),
xxm VARCHAR2(1),
zp BLOB,
sfzjyxq VARCHAR2(17),
xqh VARCHAR2(2),
zgxlm VARCHAR2(3),
whcdm VARCHAR2(3),
cjgzny VARCHAR2(6),
lxrq VARCHAR2(8),
qxrq VARCHAR2(8),
cjny VARCHAR2(6),
bzlbm VARCHAR2(2),
jzglbm VARCHAR2(3),
rkzkm VARCHAR2(2),
dabh VARCHAR2(20),
dawb CLOB,
dqztm VARCHAR2(2),
tc VARCHAR2(100),
xklbm VARCHAR2(6),
yjxkm VARCHAR2(4),
ejxkm VARCHAR2(6),
yjfx VARCHAR2(100),
zhgxsj VARCHAR2(20) default to_char(sysdate,'yyyymmddhh24miss'),
scbz VARCHAR2(1),
zhxw VARCHAR2(32),
zc VARCHAR2(32),
ygxs VARCHAR2(2)
);
-- Add comments to the table
comment on table GXJG_JZGJCSJZLB is 'GXJG0101教职工基础数据子类表';
-- Add comments to the columns
comment on column GXJG_JZGJCSJZLB.gh is '工号';
comment on column GXJG_JZGJCSJZLB.dwh is '单位号';
comment on column GXJG_JZGJCSJZLB.xm is '姓名';
comment on column GXJG_JZGJCSJZLB.ywxm is '英文姓名';
comment on column GXJG_JZGJCSJZLB.xmpy is '姓名拼音';
comment on column GXJG_JZGJCSJZLB.cym is '曾用名';
comment on column GXJG_JZGJCSJZLB.xbm is '性别码';
comment on column GXJG_JZGJCSJZLB.csrq is '出生日期';
comment on column GXJG_JZGJCSJZLB.csdm is '出生地码';
comment on column GXJG_JZGJCSJZLB.jg is '籍贯';
comment on column GXJG_JZGJCSJZLB.mzm is '民族码';
comment on column GXJG_JZGJCSJZLB.gjdqm is '国籍/地区码';
comment on column GXJG_JZGJCSJZLB.sfzjlxm is '身份证件类型码';
comment on column GXJG_JZGJCSJZLB.sfzjh is '身份证件号';
comment on column GXJG_JZGJCSJZLB.hyztm is '婚姻状况码';
comment on column GXJG_JZGJCSJZLB.gatqwm is '港澳台侨外码';
comment on column GXJG_JZGJCSJZLB.zzmmm is '政治面貌码';
comment on column GXJG_JZGJCSJZLB.jkzkm is '健康状况码[DM_GB_B_JKZKEWSZDM]';
comment on column GXJG_JZGJCSJZLB.xyzjm is '信仰宗教码';
comment on column GXJG_JZGJCSJZLB.xxm is '血型码';
comment on column GXJG_JZGJCSJZLB.zp is '照片';
comment on column GXJG_JZGJCSJZLB.sfzjyxq is '身份证件有效期';
comment on column GXJG_JZGJCSJZLB.xqh is '校区号';
comment on column GXJG_JZGJCSJZLB.zgxlm is '最高学历码';
comment on column GXJG_JZGJCSJZLB.whcdm is '文化程度码';
comment on column GXJG_JZGJCSJZLB.cjgzny is '参加工作年月';
comment on column GXJG_JZGJCSJZLB.lxrq is '来校日期';
comment on column GXJG_JZGJCSJZLB.qxrq is '起薪日期';
comment on column GXJG_JZGJCSJZLB.cjny is '从教年月';
comment on column GXJG_JZGJCSJZLB.bzlbm is '编制类别码';
comment on column GXJG_JZGJCSJZLB.jzglbm is '教职工类别码';
comment on column GXJG_JZGJCSJZLB.rkzkm is '任课状况码';
comment on column GXJG_JZGJCSJZLB.dabh is '档案编号';
comment on column GXJG_JZGJCSJZLB.dawb is '档案文本';
comment on column GXJG_JZGJCSJZLB.dqztm is '当前状态码';
comment on column GXJG_JZGJCSJZLB.tc is '特长';
comment on column GXJG_JZGJCSJZLB.xklbm is '学科类别码';
comment on column GXJG_JZGJCSJZLB.yjxkm is '一级学科码';
comment on column GXJG_JZGJCSJZLB.ejxkm is '二级学科码';
comment on column GXJG_JZGJCSJZLB.yjfx is '研究方向';
comment on column GXJG_JZGJCSJZLB.zhgxsj is '最后更新时间';
comment on column GXJG_JZGJCSJZLB.scbz is '删除标志:y/n';
comment on column GXJG_JZGJCSJZLB.zhxw is '最后学位';
comment on column GXJG_JZGJCSJZLB.zc is '职称';
comment on column GXJG_JZGJCSJZLB.ygxs is '用工形式';
-- Create/Recreate primary, unique and foreign key constraints
alter table GXJG_JZGJCSJZLB add primary key (GH);
/
-- Create table
create table GXXS_XSJBSJZL
(
xh VARCHAR2(20) not null,
xm VARCHAR2(60),
ywxm VARCHAR2(60),
xmpy VARCHAR2(60),
cym VARCHAR2(60),
xbm VARCHAR2(1),
csrq VARCHAR2(10),
csdm VARCHAR2(200),
jg VARCHAR2(60),
mzm VARCHAR2(2),
gjdqm VARCHAR2(50),
sfzjlxm VARCHAR2(2),
sfzjh VARCHAR2(20),
hyzkm VARCHAR2(10),
gatqwm VARCHAR2(2),
zzmmm VARCHAR2(2),
jkzkm VARCHAR2(20),
xyzjm VARCHAR2(50),
xxm VARCHAR2(30),
sfzjyxq VARCHAR2(17),
sfdszn VARCHAR2(1),
zp BLOB,
zhgxsj VARCHAR2(20) default to_char(sysdate,'yyyymmddhh24miss'),
scbz VARCHAR2(1),
ksh VARCHAR2(20),
dwh VARCHAR2(20) not null,
szbh VARCHAR2(20),
sfzx VARCHAR2(10),
sfyby VARCHAR2(6)
);
-- Add comments to the table
comment on table GXXS_XSJBSJZL is 'GXXS0101学生基本数据子类';
-- Add comments to the columns
comment on column GXXS_XSJBSJZL.xh is '学号';
comment on column GXXS_XSJBSJZL.xm is '姓名';
comment on column GXXS_XSJBSJZL.ywxm is '英文姓名';
comment on column GXXS_XSJBSJZL.xmpy is '姓名拼音';
comment on column GXXS_XSJBSJZL.cym is '曾用名';
comment on column GXXS_XSJBSJZL.xbm is '性别码';
comment on column GXXS_XSJBSJZL.csrq is '出生日期';
comment on column GXXS_XSJBSJZL.csdm is '出生地码';
comment on column GXXS_XSJBSJZL.jg is '籍贯';
comment on column GXXS_XSJBSJZL.mzm is '民族码';
comment on column GXXS_XSJBSJZL.gjdqm is '国籍/地区码';
comment on column GXXS_XSJBSJZL.sfzjlxm is '身份证件类型码';
comment on column GXXS_XSJBSJZL.sfzjh is '身份证件号';
comment on column GXXS_XSJBSJZL.hyzkm is '婚姻状况码';
comment on column GXXS_XSJBSJZL.gatqwm is '港澳台侨外码';
comment on column GXXS_XSJBSJZL.zzmmm is '政治面貌码';
comment on column GXXS_XSJBSJZL.jkzkm is '健康状况码';
comment on column GXXS_XSJBSJZL.xyzjm is '信仰宗教码';
comment on column GXXS_XSJBSJZL.xxm is '血型码';
comment on column GXXS_XSJBSJZL.sfzjyxq is '身份证件有效期';
comment on column GXXS_XSJBSJZL.sfdszn is '是否独生子女';
comment on column GXXS_XSJBSJZL.zp is '照片';
comment on column GXXS_XSJBSJZL.zhgxsj is '最后更新时间';
comment on column GXXS_XSJBSJZL.scbz is '删除标志:y/n';
comment on column GXXS_XSJBSJZL.ksh is '考生号';
comment on column GXXS_XSJBSJZL.dwh is 'null';
comment on column GXXS_XSJBSJZL.szbh is '所在班号';
comment on column GXXS_XSJBSJZL.sfzx is '是否在校';
comment on column GXXS_XSJBSJZL.sfyby is '是否已毕业';
-- Create/Recreate primary, unique and foreign key constraints
alter table GXXS_XSJBSJZL add primary key (XH);
/
-- Create table
create table GXXS_GRTXZL
(
yzbm VARCHAR2(6),
txdz VARCHAR2(180),
dh VARCHAR2(50),
yddh VARCHAR2(30),
czdh VARCHAR2(30),
dzxx VARCHAR2(40),
wldz VARCHAR2(60),
jstxh VARCHAR2(40),
xh VARCHAR2(20) not null,
zhgxsj VARCHAR2(20) default to_char(sysdate,'yyyymmddhh24miss'),
scbz VARCHAR2(1)
);
-- Add comments to the table
comment on table GXXS_GRTXZL is 'GXXS0109学生个人通讯子类';
-- Add comments to the columns
comment on column GXXS_GRTXZL.yzbm is '邮政编码';
comment on column GXXS_GRTXZL.txdz is '通信地址';
comment on column GXXS_GRTXZL.dh is '电话';
comment on column GXXS_GRTXZL.yddh is '移动电话';
comment on column GXXS_GRTXZL.czdh is '传真电话';
comment on column GXXS_GRTXZL.dzxx is '电子信箱';
comment on column GXXS_GRTXZL.wldz is '网络地址';
comment on column GXXS_GRTXZL.jstxh is '即时通讯号';
comment on column GXXS_GRTXZL.xh is '学号';
comment on column GXXS_GRTXZL.zhgxsj is '最后更新时间';
comment on column GXXS_GRTXZL.scbz is '删除标志:y/n';
-- Create/Recreate primary, unique and foreign key constraints
alter table GXXS_GRTXZL add primary key (XH);
/
-- Create table
create table GXJG_GRTXFSZL
(
yzbm VARCHAR2(6),
txdz VARCHAR2(180),
dh VARCHAR2(30),
yddh VARCHAR2(30),
czdh VARCHAR2(30),
wldz VARCHAR2(60),
jstxh VARCHAR2(40),
gh VARCHAR2(20) not null,
dzxx VARCHAR2(40),
zhgxsj VARCHAR2(20) default to_char(sysdate,'yyyymmddhh24miss'),
scbz VARCHAR2(1),
id VARCHAR2(48) not null
);
-- Add comments to the table
comment on table GXJG_GRTXFSZL is 'GXJG0102教职工个人通讯方式子类';
-- Add comments to the columns
comment on column GXJG_GRTXFSZL.yzbm is '邮政编码';
comment on column GXJG_GRTXFSZL.txdz is '通信地址';
comment on column GXJG_GRTXFSZL.dh is '电话';
comment on column GXJG_GRTXFSZL.yddh is '移动电话';
comment on column GXJG_GRTXFSZL.czdh is '传真电话';
comment on column GXJG_GRTXFSZL.wldz is '网络地址';
comment on column GXJG_GRTXFSZL.jstxh is '即时通讯号';
comment on column GXJG_GRTXFSZL.gh is '工号';
comment on column GXJG_GRTXFSZL.dzxx is '电子信箱';
comment on column GXJG_GRTXFSZL.zhgxsj is '最后更新时间';
comment on column GXJG_GRTXFSZL.scbz is '删除标志:y/n';
-- Create/Recreate primary, unique and foreign key constraints
alter table GXJG_GRTXFSZL add primary key (ID);
/
-- Create table
create table ZFTAL_XTGL_ERRLOG
(
YWXTMC VARCHAR2(100),
YWXTBM VARCHAR2(100),
YWXTCZ VARCHAR2(200),
SJCFQ VARCHAR2(100),
SJCCGC VARCHAR2(100),
CWZDXX VARCHAR2(1000),
CWSJ VARCHAR2(20) default to_char(sysdate,'yyyy-mm-dd hh24:mi:ss')
);
-- Add comments to the table
comment on table ZFTAL_XTGL_ERRLOG is '系统错误日志表';
-- Add comments to the columns
comment on column ZFTAL_XTGL_ERRLOG.YWXTMC is '业务系统名称(例:认证系统)';
comment on column ZFTAL_XTGL_ERRLOG.YWXTBM is '业务系统表名(例:ZFTAL_XTGL_YHB)';
comment on column ZFTAL_XTGL_ERRLOG.YWXTCZ is '业务系统操作(例:修改学生用户联系电话,地址邮箱)';
comment on column ZFTAL_XTGL_ERRLOG.SJCFQ is '操作涉及触发器(例:pro_gxxs_grtxzl)';
comment on column ZFTAL_XTGL_ERRLOG.SJCCGC is '操作涉及存储过程(例:pro_gxxs_grtxzl)';
comment on column ZFTAL_XTGL_ERRLOG.CWZDXX is '显示会导致报错的字段(例如: ZGH:200010)';
comment on column ZFTAL_XTGL_ERRLOG.CWSJ is '错误时间';
/
@@ -0,0 +1,68 @@
# 统一身份认证用户数据同步
将校园管理系统用户/组织数据定时同步到认证中间库,供第三方统一身份认证平台拉取。
## 目录结构
```
用户信息同步统一身份认证/
├── 3.在认证中间库建立表.sql # 第三方原始 Oracle 建表脚本(勿修改)
├── 建表脚本_mysql.sql # MySQL 适配建表脚本(本地测试用)
├── data.conf # 研发环境配置
├── data.conf.prod # 生产环境配置(Oracle 目标库)
├── create_tables.sh # 创建中间表
├── sync_user_to_sso.sh # 主同步脚本
├── verify_sync.sh # 同步结果校验
├── sync_sql/sync_all_mysql.sql # MySQL 同步 SQL
├── sync_oracle/merge_all.sql # Oracle MERGE 模板(生产)
├── 字段映射说明.md
└── logs/ # 运行日志(自动创建)
```
## 快速开始(研发环境)
```bash
cd 用户信息同步统一身份认证
# 1. 创建中间表(仅新增6张表,不修改已有表)
chmod +x *.sh
./create_tables.sh
# 2. 执行一次全量同步
./sync_user_to_sso.sh --full --once
# 3. 校验同步结果
./verify_sync.sh
```
## 定时运行
```bash
# 守护进程模式,默认每 3600 秒(1小时)同步一次
./sync_user_to_sso.sh
# 修改同步频率:编辑 data.conf 中 CYCLE_INTERVAL(秒)
```
## 生产部署
1. 复制 `data.conf.prod` 为 `data.conf` 或指定 `--config=data.conf.prod`
2. 填写源库(校园系统 MySQL)和目标库(认证中间库 Oracle)连接信息
3. 目标库使用第三方提供的 `3.在认证中间库建立表.sql` 在 Oracle 中建表
4. 确保服务器已安装 `sqlplus`(Oracle 客户端)
5. 使用 crontab 或 systemd 定时执行:
```bash
# crontab 示例:每小时执行一次
0 * * * * /path/to/用户信息同步统一身份认证/sync_user_to_sso.sh --once >> /path/to/logs/cron.log 2>&1
```
## 配置说明
| 配置项 | 说明 |
|--------|------|
| SOURCE_DB_* | 源库(校园管理系统,只读) |
| TARGET_DB_* | 目标库(认证中间库,读写) |
| TARGET_DB_TYPE | `mysql`(测试)或 `oracle`(生产) |
| SYNC_MODE | `full` 全量 / `incremental` 增量 |
| CYCLE_INTERVAL | 循环间隔秒数,默认 3600 |
+31
View File
@@ -0,0 +1,31 @@
#!/bin/bash
# ===========================================
# 创建统一身份认证中间库表(MySQL 测试环境)
# ===========================================
set -euo pipefail
export LANG=C
export LC_ALL=C
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CONFIG_FILE="${SCRIPT_DIR}/data.conf"
if [[ ! -f "$CONFIG_FILE" ]]; then
echo "错误:配置文件 $CONFIG_FILE 未找到"
exit 1
fi
source "$CONFIG_FILE"
SQL_FILE="${SCRIPT_DIR}/建表脚本_mysql.sql"
if [[ ! -f "$SQL_FILE" ]]; then
echo "错误:建表脚本 $SQL_FILE 未找到"
exit 1
fi
echo "正在目标库创建中间表: ${TARGET_DB_USER}@${TARGET_DB_HOST}:${TARGET_DB_PORT}/${TARGET_DB_NAME}"
python3 "${SCRIPT_DIR}/db_helper.py" \
--host "$TARGET_DB_HOST" --port "$TARGET_DB_PORT" \
--user "$TARGET_DB_USER" --password "$TARGET_DB_PASS" --database "$TARGET_DB_NAME" \
file "$SQL_FILE"
echo "建表完成"
@@ -0,0 +1,41 @@
# ===========================================
# 统一身份认证用户数据同步 - 研发环境配置
# ===========================================
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# ---------- 源库(校园管理系统,只读) ----------
SOURCE_DB_TYPE="mysql"
SOURCE_DB_HOST="47.99.164.66"
SOURCE_DB_PORT="3306"
SOURCE_DB_NAME="hzdyjs_xg"
SOURCE_DB_USER="root"
SOURCE_DB_PASS="hn02le.34lkdLKD"
# ---------- 目标库(认证中间库,读写) ----------
# 研发环境:与源库同库,仅新增中间表
TARGET_DB_TYPE="mysql"
TARGET_DB_HOST="47.99.164.66"
TARGET_DB_PORT="3306"
TARGET_DB_NAME="hzdyjs_xg"
TARGET_DB_USER="root"
TARGET_DB_PASS="hn02le.34lkdLKD"
# ---------- 同步策略 ----------
# 同步模式:full=全量,incremental=增量(按 update_time)
SYNC_MODE="full"
# 循环间隔(秒),默认 3600 = 1 小时
CYCLE_INTERVAL=3600
# 单次批量大小
BATCH_SIZE=500
# 失败重试
MAX_RETRY=3
RETRY_DELAY=5
# ---------- 日志与锁 ----------
LOG_DIR="${SCRIPT_DIR}/logs"
LOCK_FILE="${SCRIPT_DIR}/sync_user_to_sso.lock"
STATE_FILE="${SCRIPT_DIR}/.sync_state"
# ---------- 业务系统标识(写入错误日志) ----------
YWXTMC="校园管理系统"
@@ -0,0 +1,38 @@
# ===========================================
# 统一身份认证用户数据同步 - 生产环境配置
# ===========================================
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# ---------- 源库(校园管理系统,只读) ----------
SOURCE_DB_TYPE="mysql"
SOURCE_DB_HOST="127.0.0.1"
SOURCE_DB_PORT="3306"
SOURCE_DB_NAME="hzdyjs_xg"
SOURCE_DB_USER="root"
SOURCE_DB_PASS="请填写生产源库密码"
# ---------- 目标库(认证中间库 Oracle,读写) ----------
TARGET_DB_TYPE="oracle"
TARGET_DB_HOST="请填写Oracle主机"
TARGET_DB_PORT="1521"
TARGET_DB_NAME="请填写Oracle服务名或SID"
TARGET_DB_USER="请填写Oracle用户名"
TARGET_DB_PASS="请填写Oracle密码"
# Oracle 连接串示例://host:1521/service_name
TARGET_ORACLE_CONNECT="${TARGET_DB_HOST}:${TARGET_DB_PORT}/${TARGET_DB_NAME}"
# ---------- 同步策略 ----------
SYNC_MODE="incremental"
CYCLE_INTERVAL=3600
BATCH_SIZE=500
MAX_RETRY=3
RETRY_DELAY=5
# ---------- 日志与锁 ----------
LOG_DIR="${SCRIPT_DIR}/logs"
LOCK_FILE="${SCRIPT_DIR}/sync_user_to_sso.lock"
STATE_FILE="${SCRIPT_DIR}/.sync_state"
# ---------- 业务系统标识 ----------
YWXTMC="校园管理系统"
+85
View File
@@ -0,0 +1,85 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""数据库操作辅助脚本,供 shell 同步脚本调用。"""
import argparse
import sys
from pathlib import Path
try:
import pymysql
except ImportError:
print("ERROR: 需要安装 pymysql: pip install pymysql", file=sys.stderr)
sys.exit(1)
def connect_mysql(host, port, user, password, database):
return pymysql.connect(
host=host,
port=int(port),
user=user,
password=password,
database=database,
charset="utf8mb4",
autocommit=True,
cursorclass=pymysql.cursors.Cursor,
)
def exec_sql_file(conn, sql_file):
sql = Path(sql_file).read_text(encoding="utf-8")
statements = [s.strip() for s in sql.split(";") if s.strip()]
with conn.cursor() as cur:
for stmt in statements:
if stmt.upper().startswith("SET "):
cur.execute(stmt)
continue
cur.execute(stmt)
def exec_sql(conn, sql):
with conn.cursor() as cur:
cur.execute(sql)
if cur.description:
return cur.fetchall()
return None
def main():
parser = argparse.ArgumentParser(description="MySQL 数据库辅助工具")
parser.add_argument("--host", required=True)
parser.add_argument("--port", default="3306")
parser.add_argument("--user", required=True)
parser.add_argument("--password", required=True)
parser.add_argument("--database", required=True)
sub = parser.add_subparsers(dest="action", required=True)
p_file = sub.add_parser("file", help="执行 SQL 文件")
p_file.add_argument("sql_file")
p_query = sub.add_parser("query", help="执行单条 SQL 并输出结果")
p_query.add_argument("sql")
p_scalar = sub.add_parser("scalar", help="执行 SQL 并输出单个值")
p_scalar.add_argument("sql")
args = parser.parse_args()
conn = connect_mysql(args.host, args.port, args.user, args.password, args.database)
try:
if args.action == "file":
exec_sql_file(conn, args.sql_file)
print("OK")
elif args.action == "query":
rows = exec_sql(conn, args.sql)
for row in rows or []:
print("\t".join("" if v is None else str(v) for v in row))
elif args.action == "scalar":
rows = exec_sql(conn, args.sql)
if rows:
print(rows[0][0])
finally:
conn.close()
if __name__ == "__main__":
main()
@@ -0,0 +1,17 @@
-- ==========================================
-- Oracle 目标库 MERGE 同步脚本(生产环境)
-- 由 sync_user_to_sso.sh 通过 sqlplus 调用
-- 注意:需根据实际导出文件格式调整
-- ==========================================
-- 示例:部门 MERGE(生产部署时需配合 sqlldr 或外部表导入)
-- 当前为占位模板,待 Oracle 环境连通后完善
WHENEVER SQLERROR EXIT SQL.SQLCODE
SET ECHO OFF
SET FEEDBACK ON
SET VERIFY OFF
PROMPT Oracle MERGE sync placeholder - 请根据生产环境完善
EXIT;
@@ -0,0 +1,146 @@
-- ==========================================
-- 统一身份认证中间库 - MySQL 目标库同步 SQL
-- 适用:SOURCE 与 TARGET 均为 MySQL(研发同库测试)
-- 使用方式:由 sync_user_to_sso.sh 调用,可传入增量时间条件
-- ==========================================
SET NAMES utf8mb4;
-- 1. 院系所单位 GXXX_YXSDWJBSJZL <- sys_dept
INSERT INTO GXXX_YXSDWJBSJZL (
dwh, dwmc, lsdwh, dwyxbs, zhgxsj, scbz
)
SELECT
RIGHT(CAST(d.dept_id AS CHAR), 10) AS dwh,
LEFT(IFNULL(d.dept_name, ''), 60) AS dwmc,
CASE
WHEN d.parent_id IS NULL OR d.parent_id = 0 THEN NULL
ELSE RIGHT(CAST(d.parent_id AS CHAR), 10)
END AS lsdwh,
CASE WHEN d.status = '0' AND d.del_flag = '0' THEN '1' ELSE '0' END AS dwyxbs,
DATE_FORMAT(IFNULL(d.update_time, d.create_time), '%Y%m%d%H%i%s') AS zhgxsj,
CASE WHEN d.del_flag = '1' THEN 'y' ELSE 'n' END AS scbz
FROM sys_dept d
WHERE 1=1
/*INCREMENTAL_FILTER_DEPT*/
ON DUPLICATE KEY UPDATE
dwmc = VALUES(dwmc),
lsdwh = VALUES(lsdwh),
dwyxbs = VALUES(dwyxbs),
zhgxsj = VALUES(zhgxsj),
scbz = VALUES(scbz);
-- 2. 教职工基础 GXJG_JZGJCSJZLB <- sys_user (account_type=1)
INSERT INTO GXJG_JZGJCSJZLB (
gh, dwh, xm, xbm, dqztm, zhgxsj, scbz
)
SELECT
LEFT(u.user_name, 20) AS gh,
RIGHT(CAST(IFNULL(u.dept_id, 0) AS CHAR), 10) AS dwh,
LEFT(IFNULL(u.nick_name, u.user_name), 36) AS xm,
CASE u.sex WHEN '0' THEN '1' WHEN '1' THEN '2' ELSE '0' END AS xbm,
CASE WHEN u.status = '0' THEN '11' ELSE '99' END AS dqztm,
DATE_FORMAT(IFNULL(u.update_time, u.create_time), '%Y%m%d%H%i%s') AS zhgxsj,
CASE WHEN u.del_flag = '1' THEN 'y' ELSE 'n' END AS scbz
FROM sys_user u
WHERE u.account_type = '1'
/*INCREMENTAL_FILTER_TEACHER*/
ON DUPLICATE KEY UPDATE
dwh = VALUES(dwh),
xm = VALUES(xm),
xbm = VALUES(xbm),
dqztm = VALUES(dqztm),
zhgxsj = VALUES(zhgxsj),
scbz = VALUES(scbz);
-- 3. 教职工通讯 GXJG_GRTXFSZL <- sys_user (account_type=1)
INSERT INTO GXJG_GRTXFSZL (
id, gh, yddh, dzxx, zhgxsj, scbz
)
SELECT
LEFT(u.user_name, 48) AS id,
LEFT(u.user_name, 20) AS gh,
LEFT(NULLIF(u.phonenumber, ''), 30) AS yddh,
LEFT(NULLIF(u.email, ''), 40) AS dzxx,
DATE_FORMAT(IFNULL(u.update_time, u.create_time), '%Y%m%d%H%i%s') AS zhgxsj,
CASE WHEN u.del_flag = '1' THEN 'y' ELSE 'n' END AS scbz
FROM sys_user u
WHERE u.account_type = '1'
/*INCREMENTAL_FILTER_TEACHER*/
ON DUPLICATE KEY UPDATE
gh = VALUES(gh),
yddh = VALUES(yddh),
dzxx = VALUES(dzxx),
zhgxsj = VALUES(zhgxsj),
scbz = VALUES(scbz);
-- 4. 学生基础 GXXS_XSJBSJZL <- xg_student_msg
INSERT INTO GXXS_XSJBSJZL (
xh, xm, ywxm, cym, xbm, csrq, jg, sfzjlxm, sfzjh, zzmmm, xxm,
sfdszn, zhgxsj, scbz, dwh, szbh, sfzx, sfyby
)
SELECT
LEFT(s.student_no, 20) AS xh,
LEFT(IFNULL(s.name, ''), 60) AS xm,
LEFT(IFNULL(s.name_english, ''), 60) AS ywxm,
LEFT(IFNULL(s.former_name, ''), 60) AS cym,
CASE s.gender WHEN '0' THEN '1' WHEN '1' THEN '2' ELSE '0' END AS xbm,
DATE_FORMAT(s.birth_date, '%Y%m%d') AS csrq,
LEFT(IFNULL(s.native_place, ''), 60) AS jg,
'1' AS sfzjlxm,
LEFT(IFNULL(s.id_card, ''), 20) AS sfzjh,
LEFT(IFNULL(s.political_status, ''), 2) AS zzmmm,
LEFT(IFNULL(s.blood_type, ''), 30) AS xxm,
CASE WHEN s.is_single_child = 1 THEN '1' ELSE '0' END AS sfdszn,
DATE_FORMAT(IFNULL(s.update_time, s.create_time), '%Y%m%d%H%i%s') AS zhgxsj,
CASE WHEN s.deleted = 1 THEN 'y' ELSE 'n' END AS scbz,
LEFT(IFNULL(s.college_id, '0'), 20) AS dwh,
LEFT(IFNULL(c.class_code, IFNULL(s.class_id, '')), 20) AS szbh,
CASE WHEN s.student_status IN ('1', '2', '3') THEN '1' ELSE '0' END AS sfzx,
CASE WHEN s.student_status = '5' THEN '1' ELSE '0' END AS sfyby
FROM xg_student_msg s
LEFT JOIN xg_class_msg c ON c.id = s.class_id AND c.deleted = 0
WHERE 1=1
/*INCREMENTAL_FILTER_STUDENT*/
ON DUPLICATE KEY UPDATE
xm = VALUES(xm),
ywxm = VALUES(ywxm),
cym = VALUES(cym),
xbm = VALUES(xbm),
csrq = VALUES(csrq),
jg = VALUES(jg),
sfzjh = VALUES(sfzjh),
zzmmm = VALUES(zzmmm),
xxm = VALUES(xxm),
sfdszn = VALUES(sfdszn),
zhgxsj = VALUES(zhgxsj),
scbz = VALUES(scbz),
dwh = VALUES(dwh),
szbh = VALUES(szbh),
sfzx = VALUES(sfzx),
sfyby = VALUES(sfyby);
-- 5. 学生通讯 GXXS_GRTXZL <- xg_student_msg
INSERT INTO GXXS_GRTXZL (
xh, yzbm, txdz, yddh, dzxx, jstxh, zhgxsj, scbz
)
SELECT
LEFT(s.student_no, 20) AS xh,
LEFT(IFNULL(s.postal_code, ''), 6) AS yzbm,
LEFT(IFNULL(NULLIF(s.current_address, ''), s.home_address), 180) AS txdz,
LEFT(IFNULL(s.phone, ''), 30) AS yddh,
LEFT(IFNULL(s.email, ''), 40) AS dzxx,
LEFT(IFNULL(NULLIF(s.qq, ''), s.wechat), 40) AS jstxh,
DATE_FORMAT(IFNULL(s.update_time, s.create_time), '%Y%m%d%H%i%s') AS zhgxsj,
CASE WHEN s.deleted = 1 THEN 'y' ELSE 'n' END AS scbz
FROM xg_student_msg s
WHERE 1=1
/*INCREMENTAL_FILTER_STUDENT*/
ON DUPLICATE KEY UPDATE
yzbm = VALUES(yzbm),
txdz = VALUES(txdz),
yddh = VALUES(yddh),
dzxx = VALUES(dzxx),
jstxh = VALUES(jstxh),
zhgxsj = VALUES(zhgxsj),
scbz = VALUES(scbz);
+283
View File
@@ -0,0 +1,283 @@
#!/bin/bash
# ===========================================
# 统一身份认证用户数据同步脚本
# 参考:学生换班级 process_student_class_change_fixed.sh
# 功能:定时将校园系统用户/组织数据同步到认证中间库
# 用法:
# ./sync_user_to_sso.sh # 守护进程,按 CYCLE_INTERVAL 循环
# ./sync_user_to_sso.sh --once # 仅执行一次同步
# ./sync_user_to_sso.sh --full # 强制全量同步(一次)
# ===========================================
set -euo pipefail
export LANG=C
export LC_ALL=C
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CONFIG_FILE="${SCRIPT_DIR}/data.conf"
RUN_ONCE=false
FORCE_FULL=false
for arg in "$@"; do
case "$arg" in
--once) RUN_ONCE=true ;;
--full) FORCE_FULL=true ;;
--config=*) CONFIG_FILE="${arg#*=}" ;;
esac
done
if [[ ! -f "$CONFIG_FILE" ]]; then
echo "错误:配置文件 $CONFIG_FILE 未找到"
exit 1
fi
source "$CONFIG_FILE"
mkdir -p "$LOG_DIR"
LOG_FILE="${LOG_DIR}/sync_user_to_sso_$(date +%Y%m%d_%H%M%S).log"
log() {
local level="$1"
local message="$2"
local timestamp
timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo "[$timestamp] [$level] $message" | tee -a "$LOG_FILE"
}
error_exit() {
log "ERROR" "$1"
[[ -f "$LOCK_FILE" ]] && rm -f "$LOCK_FILE"
exit 1
}
acquire_lock() {
if [[ -f "$LOCK_FILE" ]]; then
local pid
pid=$(cat "$LOCK_FILE" 2>/dev/null || echo "")
if [[ -n "$pid" ]] && kill -0 "$pid" 2>/dev/null; then
error_exit "同步进程已在运行 (PID: $pid)"
fi
rm -f "$LOCK_FILE"
fi
echo $$ > "$LOCK_FILE"
}
release_lock() {
[[ -f "$LOCK_FILE" ]] && rm -f "$LOCK_FILE"
}
trap release_lock EXIT
_db_helper() {
local host="$1" port="$2" user="$3" pass="$4" db="$5" action="$6"
shift 6
python3 "${SCRIPT_DIR}/db_helper.py" \
--host "$host" --port "$port" \
--user "$user" --password "$pass" --database "$db" \
"$action" "$@"
}
source_mysql() {
local sql="$1"
_db_helper "$SOURCE_DB_HOST" "$SOURCE_DB_PORT" "$SOURCE_DB_USER" "$SOURCE_DB_PASS" "$SOURCE_DB_NAME" scalar "$sql" 2>&1
}
target_mysql() {
local sql="$1"
_db_helper "$TARGET_DB_HOST" "$TARGET_DB_PORT" "$TARGET_DB_USER" "$TARGET_DB_PASS" "$TARGET_DB_NAME" scalar "$sql" 2>&1
}
target_mysql_file() {
local file="$1"
_db_helper "$TARGET_DB_HOST" "$TARGET_DB_PORT" "$TARGET_DB_USER" "$TARGET_DB_PASS" "$TARGET_DB_NAME" file "$file" 2>&1
}
write_errlog() {
local table_name="$1"
local action="$2"
local err_msg="$3"
local escaped_msg
escaped_msg=$(echo "$err_msg" | sed "s/'/''/g" | head -c 900)
local sql="INSERT INTO ZFTAL_XTGL_ERRLOG (YWXTMC, YWXTBM, YWXTCZ, SJCCGC, CWZDXX, CWSJ)
VALUES ('${YWXTMC}', '${table_name}', '${action}', 'sync_user_to_sso.sh', '${escaped_msg}', DATE_FORMAT(NOW(), '%Y-%m-%d %H:%i:%s'));"
if [[ "$TARGET_DB_TYPE" == "mysql" ]]; then
target_mysql "$sql" >/dev/null 2>&1 || true
fi
}
get_last_sync_time() {
if [[ -f "$STATE_FILE" ]]; then
cat "$STATE_FILE"
else
echo "1970-01-01 00:00:00"
fi
}
save_sync_time() {
date '+%Y-%m-%d %H:%M:%S' > "$STATE_FILE"
}
build_incremental_filter() {
local alias="$1"
local last_sync="$2"
if [[ "$FORCE_FULL" == true ]] || [[ "$SYNC_MODE" == "full" ]]; then
echo ""
else
echo "AND (${alias}.update_time > '${last_sync}' OR ${alias}.create_time > '${last_sync}')"
fi
}
prepare_mysql_sync_sql() {
local last_sync="$1"
local template="${SCRIPT_DIR}/sync_sql/sync_all_mysql.sql"
local output="/tmp/sso_sync_mysql_$$.sql"
if [[ ! -f "$template" ]]; then
error_exit "同步 SQL 模板未找到: $template"
fi
local dept_filter teacher_filter student_filter
dept_filter=$(build_incremental_filter "d" "$last_sync")
teacher_filter=$(build_incremental_filter "u" "$last_sync")
student_filter=$(build_incremental_filter "s" "$last_sync")
sed \
-e "s|/\*INCREMENTAL_FILTER_DEPT\*/|${dept_filter}|g" \
-e "s|/\*INCREMENTAL_FILTER_TEACHER\*/|${teacher_filter}|g" \
-e "s|/\*INCREMENTAL_FILTER_STUDENT\*/|${student_filter}|g" \
"$template" > "$output"
echo "$output"
}
sync_mysql_target() {
local last_sync
last_sync=$(get_last_sync_time)
log "INFO" "开始 MySQL 目标库同步,模式: $([[ "$FORCE_FULL" == true || "$SYNC_MODE" == "full" ]] && echo 全量 || echo 增量),上次同步: $last_sync"
local sql_file
sql_file=$(prepare_mysql_sync_sql "$last_sync")
local result
if result=$(target_mysql_file "$sql_file" 2>&1); then
log "INFO" "MySQL 同步 SQL 执行成功"
rm -f "$sql_file"
save_sync_time
return 0
else
log "ERROR" "MySQL 同步失败: $result"
write_errlog "ALL" "数据同步" "$result"
rm -f "$sql_file"
return 1
fi
}
sync_oracle_target() {
local last_sync
last_sync=$(get_last_sync_time)
log "INFO" "开始 Oracle 目标库同步,上次同步: $last_sync"
if ! command -v sqlplus >/dev/null 2>&1; then
error_exit "未找到 sqlplus 命令,无法连接 Oracle 目标库"
fi
local export_dir="/tmp/sso_sync_export_$$"
mkdir -p "$export_dir"
# 导出部门
source_mysql "
SELECT
RIGHT(CAST(d.dept_id AS CHAR), 10),
REPLACE(IFNULL(d.dept_name,''),'\t',' '),
CASE WHEN d.parent_id IS NULL OR d.parent_id=0 THEN '' ELSE RIGHT(CAST(d.parent_id AS CHAR),10) END,
CASE WHEN d.status='0' AND d.del_flag='0' THEN '1' ELSE '0' END,
DATE_FORMAT(IFNULL(d.update_time,d.create_time),'%Y%m%d%H%i%s'),
CASE WHEN d.del_flag='1' THEN 'y' ELSE 'n' END
FROM sys_dept d
" > "${export_dir}/dept.tsv" || true
local oracle_script="${SCRIPT_DIR}/sync_oracle/merge_all.sql"
if [[ ! -f "$oracle_script" ]]; then
error_exit "Oracle MERGE 脚本未找到: $oracle_script"
fi
local conn="${TARGET_DB_USER}/${TARGET_DB_PASS}@${TARGET_ORACLE_CONNECT:-${TARGET_DB_HOST}:${TARGET_DB_PORT}/${TARGET_DB_NAME}}"
if sqlplus -S "$conn" @"$oracle_script" "$export_dir" >> "$LOG_FILE" 2>&1; then
log "INFO" "Oracle 同步完成"
save_sync_time
rm -rf "$export_dir"
return 0
else
log "ERROR" "Oracle 同步失败,详见日志"
write_errlog "ALL" "Oracle数据同步" "sqlplus执行失败"
rm -rf "$export_dir"
return 1
fi
}
run_sync_cycle() {
log "INFO" "========== 开始同步周期 =========="
# 测试源库连接
if ! source_mysql "SELECT 1;" >/dev/null 2>&1; then
log "ERROR" "源库连接失败: ${SOURCE_DB_USER}@${SOURCE_DB_HOST}:${SOURCE_DB_PORT}/${SOURCE_DB_NAME}"
return 1
fi
if [[ "$TARGET_DB_TYPE" == "mysql" ]]; then
if ! target_mysql "SELECT 1;" >/dev/null 2>&1; then
log "ERROR" "目标库连接失败: ${TARGET_DB_USER}@${TARGET_DB_HOST}:${TARGET_DB_PORT}/${TARGET_DB_NAME}"
return 1
fi
sync_mysql_target
elif [[ "$TARGET_DB_TYPE" == "oracle" ]]; then
sync_oracle_target
else
log "ERROR" "不支持的目标库类型: $TARGET_DB_TYPE"
return 1
fi
log "INFO" "========== 同步周期结束 =========="
}
verify_counts() {
log "INFO" "---------- 数据校验 ----------"
if [[ "$TARGET_DB_TYPE" != "mysql" ]]; then
log "INFO" "Oracle 目标库校验请使用 verify_sync.sh"
return 0
fi
local tables=("GXXX_YXSDWJBSJZL" "GXJG_JZGJCSJZLB" "GXJG_GRTXFSZL" "GXXS_XSJBSJZL" "GXXS_GRTXZL")
for t in "${tables[@]}"; do
local cnt
cnt=$(target_mysql "SELECT COUNT(*) FROM ${t};" 2>/dev/null | tail -1)
log "INFO" "目标表 ${t}: ${cnt} 条"
done
local src_dept src_teacher src_student
src_dept=$(source_mysql "SELECT COUNT(*) FROM sys_dept;" 2>/dev/null | tail -1)
src_teacher=$(source_mysql "SELECT COUNT(*) FROM sys_user WHERE account_type='1';" 2>/dev/null | tail -1)
src_student=$(source_mysql "SELECT COUNT(*) FROM xg_student_msg WHERE deleted=0;" 2>/dev/null | tail -1)
log "INFO" "源表 sys_dept: ${src_dept} 条"
log "INFO" "源表 sys_user(教职工): ${src_teacher} 条"
log "INFO" "源表 xg_student_msg(未删除): ${src_student} 条"
}
main() {
acquire_lock
log "INFO" "统一身份认证用户数据同步启动"
log "INFO" "源库: ${SOURCE_DB_TYPE} ${SOURCE_DB_USER}@${SOURCE_DB_HOST}:${SOURCE_DB_PORT}/${SOURCE_DB_NAME}"
log "INFO" "目标库: ${TARGET_DB_TYPE} ${TARGET_DB_USER}@${TARGET_DB_HOST}:${TARGET_DB_PORT}/${TARGET_DB_NAME}"
log "INFO" "同步模式: ${SYNC_MODE},循环间隔: ${CYCLE_INTERVAL}秒"
if [[ "$RUN_ONCE" == true ]]; then
run_sync_cycle && verify_counts
exit $?
fi
while true; do
run_sync_cycle && verify_counts || log "WARN" "本轮同步失败,将在 ${CYCLE_INTERVAL} 秒后重试"
log "INFO" "等待 ${CYCLE_INTERVAL} 秒后开始下一轮..."
sleep "$CYCLE_INTERVAL"
done
}
main "$@"
+81
View File
@@ -0,0 +1,81 @@
#!/bin/bash
# ===========================================
# 统一身份认证同步结果校验脚本
# ===========================================
set -euo pipefail
export LANG=C
export LC_ALL=C
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CONFIG_FILE="${SCRIPT_DIR}/data.conf"
[[ -f "$CONFIG_FILE" ]] || { echo "配置文件未找到"; exit 1; }
source "$CONFIG_FILE"
mysql_q() {
python3 "${SCRIPT_DIR}/db_helper.py" \
--host "$1" --port "$2" --user "$3" --password "$4" --database "$5" \
scalar "$6" 2>/dev/null
}
echo "========== 统一身份认证同步校验 =========="
echo "时间: $(date '+%Y-%m-%d %H:%M:%S')"
echo ""
# 源库统计
SRC_DEPT=$(mysql_q "$SOURCE_DB_HOST" "$SOURCE_DB_PORT" "$SOURCE_DB_USER" "$SOURCE_DB_PASS" "$SOURCE_DB_NAME" "SELECT COUNT(*) FROM sys_dept;")
SRC_TEACHER=$(mysql_q "$SOURCE_DB_HOST" "$SOURCE_DB_PORT" "$SOURCE_DB_USER" "$SOURCE_DB_PASS" "$SOURCE_DB_NAME" "SELECT COUNT(*) FROM sys_user WHERE account_type='1';")
SRC_STUDENT=$(mysql_q "$SOURCE_DB_HOST" "$SOURCE_DB_PORT" "$SOURCE_DB_USER" "$SOURCE_DB_PASS" "$SOURCE_DB_NAME" "SELECT COUNT(*) FROM xg_student_msg WHERE deleted=0;")
echo "[源库] sys_dept: ${SRC_DEPT}"
echo "[源库] sys_user(教职工): ${SRC_TEACHER}"
echo "[源库] xg_student_msg(有效): ${SRC_STUDENT}"
echo ""
if [[ "$TARGET_DB_TYPE" == "mysql" ]]; then
TGT_DEPT=$(mysql_q "$TARGET_DB_HOST" "$TARGET_DB_PORT" "$TARGET_DB_USER" "$TARGET_DB_PASS" "$TARGET_DB_NAME" "SELECT COUNT(*) FROM GXXX_YXSDWJBSJZL;")
TGT_TEACHER=$(mysql_q "$TARGET_DB_HOST" "$TARGET_DB_PORT" "$TARGET_DB_USER" "$TARGET_DB_PASS" "$TARGET_DB_NAME" "SELECT COUNT(*) FROM GXJG_JZGJCSJZLB;")
TGT_TEACHER_TX=$(mysql_q "$TARGET_DB_HOST" "$TARGET_DB_PORT" "$TARGET_DB_USER" "$TARGET_DB_PASS" "$TARGET_DB_NAME" "SELECT COUNT(*) FROM GXJG_GRTXFSZL;")
TGT_STUDENT=$(mysql_q "$TARGET_DB_HOST" "$TARGET_DB_PORT" "$TARGET_DB_USER" "$TARGET_DB_PASS" "$TARGET_DB_NAME" "SELECT COUNT(*) FROM GXXS_XSJBSJZL;")
TGT_STUDENT_TX=$(mysql_q "$TARGET_DB_HOST" "$TARGET_DB_PORT" "$TARGET_DB_USER" "$TARGET_DB_PASS" "$TARGET_DB_NAME" "SELECT COUNT(*) FROM GXXS_GRTXZL;")
TGT_ERR=$(mysql_q "$TARGET_DB_HOST" "$TARGET_DB_PORT" "$TARGET_DB_USER" "$TARGET_DB_PASS" "$TARGET_DB_NAME" "SELECT COUNT(*) FROM ZFTAL_XTGL_ERRLOG;")
echo "[目标库] GXXX_YXSDWJBSJZL: ${TGT_DEPT}"
echo "[目标库] GXJG_JZGJCSJZLB: ${TGT_TEACHER}"
echo "[目标库] GXJG_GRTXFSZL: ${TGT_TEACHER_TX}"
echo "[目标库] GXXS_XSJBSJZL: ${TGT_STUDENT}"
echo "[目标库] GXXS_GRTXZL: ${TGT_STUDENT_TX}"
echo "[目标库] ZFTAL_XTGL_ERRLOG: ${TGT_ERR}"
echo ""
# 抽样对比
echo "---------- 抽样数据对比 ----------"
python3 "${SCRIPT_DIR}/db_helper.py" \
--host "$TARGET_DB_HOST" --port "$TARGET_DB_PORT" \
--user "$TARGET_DB_USER" --password "$TARGET_DB_PASS" --database "$TARGET_DB_NAME" \
query "SELECT gh, xm, dwh, scbz FROM GXJG_JZGJCSJZLB LIMIT 3" 2>/dev/null
python3 "${SCRIPT_DIR}/db_helper.py" \
--host "$TARGET_DB_HOST" --port "$TARGET_DB_PORT" \
--user "$TARGET_DB_USER" --password "$TARGET_DB_PASS" --database "$TARGET_DB_NAME" \
query "SELECT xh, xm, dwh, szbh, scbz FROM GXXS_XSJBSJZL LIMIT 3" 2>/dev/null
echo ""
# 简单一致性检查
PASS=true
[[ "$TGT_DEPT" -ge 1 ]] || { echo "WARN: 部门同步数量为0"; PASS=false; }
[[ "$TGT_TEACHER" -ge 1 ]] || { echo "WARN: 教职工同步数量为0"; PASS=false; }
[[ "$TGT_STUDENT" -ge 1 ]] || { echo "WARN: 学生同步数量为0"; PASS=false; }
[[ "$TGT_TEACHER" == "$TGT_TEACHER_TX" ]] || echo "WARN: 教职工基础表与通讯表数量不一致"
[[ "$TGT_STUDENT" == "$TGT_STUDENT_TX" ]] || echo "WARN: 学生基础表与通讯表数量不一致"
if [[ "$PASS" == true ]]; then
echo "校验结果: 通过(基础数据已写入)"
exit 0
else
echo "校验结果: 未通过"
exit 1
fi
else
echo "目标库为 Oracle,请使用 sqlplus 手动校验目标表记录数"
exit 0
fi
@@ -0,0 +1,110 @@
# 统一身份认证用户数据同步 - 字段映射说明
## 1. 概述
将校园管理系统(`hzdyjs_xg`)的组织与用户数据,定时同步到统一身份认证中间库 6 张标准表,供第三方平台拉取。
| 目标表 | 说明 | 数据源 |
|--------|------|--------|
| `GXXX_YXSDWJBSJZL` | 院系所单位 | `sys_dept` |
| `GXJG_JZGJCSJZLB` | 教职工基础 | `sys_user`(`account_type='1'`) |
| `GXJG_GRTXFSZL` | 教职工通讯 | `sys_user`(`account_type='1'`) |
| `GXXS_XSJBSJZL` | 学生基础 | `xg_student_msg` |
| `GXXS_GRTXZL` | 学生通讯 | `xg_student_msg` |
| `ZFTAL_XTGL_ERRLOG` | 同步错误日志 | 同步脚本写入 |
**不同步字段**:`zp`(照片 BLOB)按约定不处理。
---
## 2. 单位表 GXXX_YXSDWJBSJZL
| 目标字段 | 源字段 | 转换规则 |
|----------|--------|----------|
| dwh | sys_dept.dept_id | `RIGHT(CAST(dept_id AS CHAR), 10)`(雪花ID超长,取后10位) |
| dwmc | sys_dept.dept_name | 截断至60字符 |
| lsdwh | sys_dept.parent_id | parent_id=0 时 NULL,否则取后10位 |
| dwyxbs | status + del_flag | 正常且未删除='1',否则='0' |
| zhgxsj | update_time / create_time | `YYYYMMDDhhmmss` |
| scbz | del_flag | '0'→'n','1'→'y' |
---
## 3. 教职工表 GXJG_JZGJCSJZLB
| 目标字段 | 源字段 | 转换规则 |
|----------|--------|----------|
| gh | sys_user.user_name | 工号,截断至20字符 |
| dwh | sys_user.dept_id | 取后10位 |
| xm | sys_user.nick_name | 姓名 |
| xbm | sys_user.sex | 0男→'1',1女→'2',其他→'0' |
| dqztm | sys_user.status | 0正常→'11',停用→'99' |
| zhgxsj | update_time / create_time | `YYYYMMDDhhmmss` |
| scbz | del_flag | '0'→'n','1'→'y' |
---
## 4. 教职工通讯表 GXJG_GRTXFSZL
| 目标字段 | 源字段 | 转换规则 |
|----------|--------|----------|
| id | sys_user.user_name | 主键,用工号 |
| gh | sys_user.user_name | 工号 |
| yddh | sys_user.phonenumber | 手机号 |
| dzxx | sys_user.email | 邮箱 |
| zhgxsj | update_time / create_time | `YYYYMMDDhhmmss` |
| scbz | del_flag | '0'→'n','1'→'y' |
---
## 5. 学生表 GXXS_XSJBSJZL
| 目标字段 | 源字段 | 转换规则 |
|----------|--------|----------|
| xh | xg_student_msg.student_no | 学号 |
| xm | name | 姓名 |
| ywxm | name_english | 英文名 |
| cym | former_name | 曾用名 |
| xbm | gender | 0男→'1',1女→'2' |
| csrq | birth_date | `YYYYMMDD` |
| jg | native_place | 籍贯 |
| sfzjlxm | - | 固定 '1'(身份证) |
| sfzjh | id_card | 身份证号 |
| zzmmm | political_status | 政治面貌 |
| xxm | blood_type | 血型 |
| sfdszn | is_single_child | 1→'1',0→'0' |
| dwh | college_id | 学院ID |
| szbh | xg_class_msg.class_code | 关联班级表取班级代码 |
| sfzx | student_status | 1/2/3→'1'在校,其他→'0' |
| sfyby | student_status | 5毕业→'1',其他→'0' |
| zhgxsj | update_time / create_time | `YYYYMMDDhhmmss` |
| scbz | deleted | 0→'n',1→'y' |
---
## 6. 学生通讯表 GXXS_GRTXZL
| 目标字段 | 源字段 | 转换规则 |
|----------|--------|----------|
| xh | student_no | 学号 |
| yzbm | postal_code | 邮编 |
| txdz | current_address / home_address | 优先现住址 |
| yddh | phone | 手机 |
| dzxx | email | 邮箱 |
| jstxh | qq / wechat | 优先QQ |
| zhgxsj | update_time / create_time | `YYYYMMDDhhmmss` |
| scbz | deleted | 0→'n',1→'y' |
---
## 7. 注意事项
1. **单位号 dwh 长度限制**:第三方表 `dwh` 仅 10 位,而 `sys_dept.dept_id` 为雪花ID(19位),当前取后10位;若对方有编码规范需另行映射。
2. **增量同步**:基于源表 `update_time` / `create_time`,状态记录在 `.sync_state`。
3. **全量同步**:设置 `SYNC_MODE=full` 或执行 `./sync_user_to_sso.sh --full --once`。
4. **生产 Oracle**:修改 `data.conf.prod` 中 `TARGET_DB_TYPE=oracle` 及连接信息,使用 `sqlplus` 执行 MERGE(模板见 `sync_oracle/`)。
---
**版本**: v1.0.0
**更新时间**: 2026-08-24
@@ -0,0 +1,179 @@
-- ==========================================
-- 统一身份认证中间库建表脚本(MySQL 适配版)
-- 说明:由第三方 Oracle 建表语句转换,仅供本地 MySQL 测试使用
-- 原始 Oracle 脚本:3.在认证中间库建立表.sql(请勿修改原文件)
-- ==========================================
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- GXXX_YXSDWJBSJZL 院系所单位基本数据子类
-- ----------------------------
DROP TABLE IF EXISTS `GXXX_YXSDWJBSJZL`;
CREATE TABLE `GXXX_YXSDWJBSJZL` (
`dwh` VARCHAR(10) NOT NULL COMMENT '单位号',
`dwmc` VARCHAR(60) DEFAULT NULL COMMENT '单位名称',
`dwywmc` VARCHAR(180) DEFAULT NULL COMMENT '单位英文名称',
`dwjc` VARCHAR(12) DEFAULT NULL COMMENT '单位简称',
`dwywjc` VARCHAR(8) DEFAULT NULL COMMENT '单位英文简称',
`dwjp` VARCHAR(6) DEFAULT NULL COMMENT '单位简拼',
`dwdz` VARCHAR(180) DEFAULT NULL COMMENT '单位地址',
`lsdwh` VARCHAR(10) DEFAULT NULL COMMENT '隶属单位号',
`dwlbm` VARCHAR(1) DEFAULT NULL COMMENT '单位类别码',
`dwbbm` VARCHAR(1) DEFAULT NULL COMMENT '单位办别码',
`dwyxbs` VARCHAR(1) DEFAULT NULL COMMENT '单位有效标识',
`sxrq` VARCHAR(8) DEFAULT NULL COMMENT '失效日期',
`sfst` VARCHAR(1) DEFAULT NULL COMMENT '是否实体',
`jlny` VARCHAR(6) DEFAULT NULL COMMENT '建立年月',
`dwfzrh` VARCHAR(20) DEFAULT NULL COMMENT '单位负责人号',
`zhgxsj` VARCHAR(20) DEFAULT NULL COMMENT '最后更新时间',
`scbz` VARCHAR(1) DEFAULT NULL COMMENT '删除标志:y/n',
`dwjb` VARCHAR(8) DEFAULT NULL COMMENT '单位级别',
PRIMARY KEY (`dwh`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='GXXX0301院系所单位基本数据子类';
-- ----------------------------
-- GXJG_JZGJCSJZLB 教职工基础数据子类表
-- ----------------------------
DROP TABLE IF EXISTS `GXJG_JZGJCSJZLB`;
CREATE TABLE `GXJG_JZGJCSJZLB` (
`gh` VARCHAR(20) NOT NULL COMMENT '工号',
`dwh` VARCHAR(10) NOT NULL COMMENT '单位号',
`xm` VARCHAR(36) DEFAULT NULL COMMENT '姓名',
`ywxm` VARCHAR(60) DEFAULT NULL COMMENT '英文姓名',
`xmpy` VARCHAR(60) DEFAULT NULL COMMENT '姓名拼音',
`cym` VARCHAR(36) DEFAULT NULL COMMENT '曾用名',
`xbm` VARCHAR(1) DEFAULT NULL COMMENT '性别码',
`csrq` VARCHAR(8) DEFAULT NULL COMMENT '出生日期',
`csdm` VARCHAR(6) DEFAULT NULL COMMENT '出生地码',
`jg` VARCHAR(20) DEFAULT NULL COMMENT '籍贯',
`mzm` VARCHAR(2) DEFAULT NULL COMMENT '民族码',
`gjdqm` VARCHAR(3) DEFAULT NULL COMMENT '国籍/地区码',
`sfzjlxm` VARCHAR(1) DEFAULT NULL COMMENT '身份证件类型码',
`sfzjh` VARCHAR(20) DEFAULT NULL COMMENT '身份证件号',
`hyztm` VARCHAR(2) DEFAULT NULL COMMENT '婚姻状况码',
`gatqwm` VARCHAR(2) DEFAULT NULL COMMENT '港澳台侨外码',
`zzmmm` VARCHAR(2) DEFAULT NULL COMMENT '政治面貌码',
`jkzkm` VARCHAR(2) DEFAULT NULL COMMENT '健康状况码',
`xyzjm` VARCHAR(2) DEFAULT NULL COMMENT '信仰宗教码',
`xxm` VARCHAR(1) DEFAULT NULL COMMENT '血型码',
`zp` LONGBLOB DEFAULT NULL COMMENT '照片(不同步)',
`sfzjyxq` VARCHAR(17) DEFAULT NULL COMMENT '身份证件有效期',
`xqh` VARCHAR(2) DEFAULT NULL COMMENT '校区号',
`zgxlm` VARCHAR(3) DEFAULT NULL COMMENT '最高学历码',
`whcdm` VARCHAR(3) DEFAULT NULL COMMENT '文化程度码',
`cjgzny` VARCHAR(6) DEFAULT NULL COMMENT '参加工作年月',
`lxrq` VARCHAR(8) DEFAULT NULL COMMENT '来校日期',
`qxrq` VARCHAR(8) DEFAULT NULL COMMENT '起薪日期',
`cjny` VARCHAR(6) DEFAULT NULL COMMENT '从教年月',
`bzlbm` VARCHAR(2) DEFAULT NULL COMMENT '编制类别码',
`jzglbm` VARCHAR(3) DEFAULT NULL COMMENT '教职工类别码',
`rkzkm` VARCHAR(2) DEFAULT NULL COMMENT '任课状况码',
`dabh` VARCHAR(20) DEFAULT NULL COMMENT '档案编号',
`dawb` LONGTEXT DEFAULT NULL COMMENT '档案文本',
`dqztm` VARCHAR(2) DEFAULT NULL COMMENT '当前状态码',
`tc` VARCHAR(100) DEFAULT NULL COMMENT '特长',
`xklbm` VARCHAR(6) DEFAULT NULL COMMENT '学科类别码',
`yjxkm` VARCHAR(4) DEFAULT NULL COMMENT '一级学科码',
`ejxkm` VARCHAR(6) DEFAULT NULL COMMENT '二级学科码',
`yjfx` VARCHAR(100) DEFAULT NULL COMMENT '研究方向',
`zhgxsj` VARCHAR(20) DEFAULT NULL COMMENT '最后更新时间',
`scbz` VARCHAR(1) DEFAULT NULL COMMENT '删除标志:y/n',
`zhxw` VARCHAR(32) DEFAULT NULL COMMENT '最后学位',
`zc` VARCHAR(32) DEFAULT NULL COMMENT '职称',
`ygxs` VARCHAR(2) DEFAULT NULL COMMENT '用工形式',
PRIMARY KEY (`gh`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='GXJG0101教职工基础数据子类表';
-- ----------------------------
-- GXXS_XSJBSJZL 学生基本数据子类
-- ----------------------------
DROP TABLE IF EXISTS `GXXS_XSJBSJZL`;
CREATE TABLE `GXXS_XSJBSJZL` (
`xh` VARCHAR(20) NOT NULL COMMENT '学号',
`xm` VARCHAR(60) DEFAULT NULL COMMENT '姓名',
`ywxm` VARCHAR(60) DEFAULT NULL COMMENT '英文姓名',
`xmpy` VARCHAR(60) DEFAULT NULL COMMENT '姓名拼音',
`cym` VARCHAR(60) DEFAULT NULL COMMENT '曾用名',
`xbm` VARCHAR(1) DEFAULT NULL COMMENT '性别码',
`csrq` VARCHAR(10) DEFAULT NULL COMMENT '出生日期',
`csdm` VARCHAR(200) DEFAULT NULL COMMENT '出生地码',
`jg` VARCHAR(60) DEFAULT NULL COMMENT '籍贯',
`mzm` VARCHAR(2) DEFAULT NULL COMMENT '民族码',
`gjdqm` VARCHAR(50) DEFAULT NULL COMMENT '国籍/地区码',
`sfzjlxm` VARCHAR(2) DEFAULT NULL COMMENT '身份证件类型码',
`sfzjh` VARCHAR(20) DEFAULT NULL COMMENT '身份证件号',
`hyzkm` VARCHAR(10) DEFAULT NULL COMMENT '婚姻状况码',
`gatqwm` VARCHAR(2) DEFAULT NULL COMMENT '港澳台侨外码',
`zzmmm` VARCHAR(2) DEFAULT NULL COMMENT '政治面貌码',
`jkzkm` VARCHAR(20) DEFAULT NULL COMMENT '健康状况码',
`xyzjm` VARCHAR(50) DEFAULT NULL COMMENT '信仰宗教码',
`xxm` VARCHAR(30) DEFAULT NULL COMMENT '血型码',
`sfzjyxq` VARCHAR(17) DEFAULT NULL COMMENT '身份证件有效期',
`sfdszn` VARCHAR(1) DEFAULT NULL COMMENT '是否独生子女',
`zp` LONGBLOB DEFAULT NULL COMMENT '照片(不同步)',
`zhgxsj` VARCHAR(20) DEFAULT NULL COMMENT '最后更新时间',
`scbz` VARCHAR(1) DEFAULT NULL COMMENT '删除标志:y/n',
`ksh` VARCHAR(20) DEFAULT NULL COMMENT '考生号',
`dwh` VARCHAR(20) NOT NULL COMMENT '单位号(学院)',
`szbh` VARCHAR(20) DEFAULT NULL COMMENT '所在班号',
`sfzx` VARCHAR(10) DEFAULT NULL COMMENT '是否在校',
`sfyby` VARCHAR(6) DEFAULT NULL COMMENT '是否已毕业',
PRIMARY KEY (`xh`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='GXXS0101学生基本数据子类';
-- ----------------------------
-- GXXS_GRTXZL 学生个人通讯子类
-- ----------------------------
DROP TABLE IF EXISTS `GXXS_GRTXZL`;
CREATE TABLE `GXXS_GRTXZL` (
`yzbm` VARCHAR(6) DEFAULT NULL COMMENT '邮政编码',
`txdz` VARCHAR(180) DEFAULT NULL COMMENT '通信地址',
`dh` VARCHAR(50) DEFAULT NULL COMMENT '电话',
`yddh` VARCHAR(30) DEFAULT NULL COMMENT '移动电话',
`czdh` VARCHAR(30) DEFAULT NULL COMMENT '传真电话',
`dzxx` VARCHAR(40) DEFAULT NULL COMMENT '电子信箱',
`wldz` VARCHAR(60) DEFAULT NULL COMMENT '网络地址',
`jstxh` VARCHAR(40) DEFAULT NULL COMMENT '即时通讯号',
`xh` VARCHAR(20) NOT NULL COMMENT '学号',
`zhgxsj` VARCHAR(20) DEFAULT NULL COMMENT '最后更新时间',
`scbz` VARCHAR(1) DEFAULT NULL COMMENT '删除标志:y/n',
PRIMARY KEY (`xh`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='GXXS0109学生个人通讯子类';
-- ----------------------------
-- GXJG_GRTXFSZL 教职工个人通讯方式子类
-- ----------------------------
DROP TABLE IF EXISTS `GXJG_GRTXFSZL`;
CREATE TABLE `GXJG_GRTXFSZL` (
`yzbm` VARCHAR(6) DEFAULT NULL COMMENT '邮政编码',
`txdz` VARCHAR(180) DEFAULT NULL COMMENT '通信地址',
`dh` VARCHAR(30) DEFAULT NULL COMMENT '电话',
`yddh` VARCHAR(30) DEFAULT NULL COMMENT '移动电话',
`czdh` VARCHAR(30) DEFAULT NULL COMMENT '传真电话',
`wldz` VARCHAR(60) DEFAULT NULL COMMENT '网络地址',
`jstxh` VARCHAR(40) DEFAULT NULL COMMENT '即时通讯号',
`gh` VARCHAR(20) NOT NULL COMMENT '工号',
`dzxx` VARCHAR(40) DEFAULT NULL COMMENT '电子信箱',
`zhgxsj` VARCHAR(20) DEFAULT NULL COMMENT '最后更新时间',
`scbz` VARCHAR(1) DEFAULT NULL COMMENT '删除标志:y/n',
`id` VARCHAR(48) NOT NULL COMMENT '主键ID',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='GXJG0102教职工个人通讯方式子类';
-- ----------------------------
-- ZFTAL_XTGL_ERRLOG 系统错误日志表
-- ----------------------------
DROP TABLE IF EXISTS `ZFTAL_XTGL_ERRLOG`;
CREATE TABLE `ZFTAL_XTGL_ERRLOG` (
`YWXTMC` VARCHAR(100) DEFAULT NULL COMMENT '业务系统名称',
`YWXTBM` VARCHAR(100) DEFAULT NULL COMMENT '业务系统表名',
`YWXTCZ` VARCHAR(200) DEFAULT NULL COMMENT '业务系统操作',
`SJCFQ` VARCHAR(100) DEFAULT NULL COMMENT '操作涉及触发器',
`SJCCGC` VARCHAR(100) DEFAULT NULL COMMENT '操作涉及存储过程',
`CWZDXX` VARCHAR(1000) DEFAULT NULL COMMENT '报错字段信息',
`CWSJ` VARCHAR(20) DEFAULT NULL COMMENT '错误时间'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统错误日志表';
SET FOREIGN_KEY_CHECKS = 1;