This commit is contained in:
zlwu committed 2025-12-25 22:20:22 +08:00
1 parent ff1228873c
commit 34a843e790
23 files changed
+1540 -717

No files matched your search

@@ -66,7 +66,7 @@ public interface XgStudentMsgMapper extends BaseMapperPlus<XgStudentMsg, XgStude
* @param studentId 学生ID
* @return 学院ID
*/
Long selectCollegeIdByStudentId(@Param("studentId") Long studentId);
String selectCollegeIdByStudentId(@Param("studentId") Long studentId);
/**
* 根据学院ID和角色Key查询具备该角色的用户列表
@@ -171,7 +171,7 @@ public class XgClassTeacherCounselorRelServiceImpl implements IXgClassTeacherCou
// 3. 更新学生表xg_student_msg表中的班主任信息
LambdaUpdateWrapper<XgStudentMsg> studentUpdateWrapper = Wrappers.lambdaUpdate(XgStudentMsg.class)
.eq(XgStudentMsg::getClassId, bo.getClassId())
.eq(XgStudentMsg::getClassId, String.valueOf(bo.getClassId()))
.set(XgStudentMsg::getClassTeacherId, null)
.set(XgStudentMsg::getClassTeacherName, null);
int updateResult11 = xgStudentMsgMapper.update(null, studentUpdateWrapper);
@@ -195,7 +195,7 @@ public class XgClassTeacherCounselorRelServiceImpl implements IXgClassTeacherCou
// 3. 更新学生表xg_student_msg表中的班主任信息
LambdaUpdateWrapper<XgStudentMsg> studentUpdateWrapper = Wrappers.lambdaUpdate(XgStudentMsg.class)
.eq(XgStudentMsg::getClassId, bo.getClassId())
.eq(XgStudentMsg::getClassId, String.valueOf(bo.getClassId()))
.set(XgStudentMsg::getClassTeacherId, bo.getTeacherId())
.set(XgStudentMsg::getClassTeacherName, bo.getTeacherName());
int updateResult11 = xgStudentMsgMapper.update(null, studentUpdateWrapper);
@@ -273,7 +273,7 @@ public class XgClassTeacherCounselorRelServiceImpl implements IXgClassTeacherCou
// 3. 更新学生表xg_student_msg表中的辅导员信息
LambdaUpdateWrapper<XgStudentMsg> studentUpdateWrapper = Wrappers.lambdaUpdate(XgStudentMsg.class)
.eq(XgStudentMsg::getClassId, bo.getClassId())
.eq(XgStudentMsg::getClassId, String.valueOf(bo.getClassId()))
.set(XgStudentMsg::getCounselorId, null)
.set(XgStudentMsg::getCounselorName, null);
int updateResult11 = xgStudentMsgMapper.update(null, studentUpdateWrapper);
@@ -295,7 +295,7 @@ public class XgClassTeacherCounselorRelServiceImpl implements IXgClassTeacherCou
bo.getClassId(), bo.getTeacherId(), bo.getTeacherName());
// 3. 更新学生表xg_student_msg表中的辅导员信息
LambdaUpdateWrapper<XgStudentMsg> studentUpdateWrapper = Wrappers.lambdaUpdate(XgStudentMsg.class)
.eq(XgStudentMsg::getClassId, bo.getClassId())
.eq(XgStudentMsg::getClassId, String.valueOf(bo.getClassId()))
.set(XgStudentMsg::getCounselorId, bo.getTeacherId())
.set(XgStudentMsg::getCounselorName, bo.getTeacherName());
int updateResult11 = xgStudentMsgMapper.update(null, studentUpdateWrapper);
@@ -1472,7 +1472,7 @@ public class XgDormBedStudentServiceImpl implements IXgDormBedStudentService {
studentQuery.eq(XgStudentMsg::getStudentStatus, "1") // 学生状态为正常
.eq(XgStudentMsg::getDeleted, 0L) // 未删除
.eq(XgStudentMsg::getIsBoarder, "1") // 需要住校
.eq(XgStudentMsg::getClassId, targetClassId) // 指定班级
.eq(XgStudentMsg::getClassId, String.valueOf(targetClassId)) // 指定班级(转换为String类型)
.orderByDesc(XgStudentMsg::getStudentNo); // 按学号倒序排序
List<XgStudentMsg> boardingStudents = xgStudentMsgMapper.selectList(studentQuery);
@@ -82,7 +82,8 @@ public class XgDormRoomServiceImpl implements IXgDormRoomService {
@Override
public TableDataInfo<XgDormRoomVo> queryPageList(XgDormRoomBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<XgDormRoom> lqw = buildQueryWrapper(bo);
Page<XgDormRoomVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
// 由于buildQueryWrapper中已经设置了业务排序,禁用PageQuery的默认排序(创建时间倒序)
Page<XgDormRoomVo> result = baseMapper.selectVoPage(pageQuery.disableDefaultOrder().build(), lqw);
return TableDataInfo.build(result);
}
@@ -163,6 +164,7 @@ public class XgDormRoomServiceImpl implements IXgDormRoomService {
private LambdaQueryWrapper<XgDormRoom> buildQueryWrapper(XgDormRoomBo bo) {
Map<String, Object> params = bo.getParams();
LambdaQueryWrapper<XgDormRoom> lqw = Wrappers.lambdaQuery();
// 设置业务排序规则
lqw.orderByAsc(XgDormRoom::getFloorNum); // 第一优先级:按楼层升序
lqw.orderByAsc(XgDormRoom::getSort); // 第二优先级:按排序号升序
lqw.orderByAsc(XgDormRoom::getRoomName); // 第三优先级:按房间名称升序
@@ -1098,7 +1098,16 @@ public class XgStudentMsgServiceImpl implements IXgStudentMsgService {
log.info("根据学生ID获取学院请假审批人列表 - 学生ID: {}", studentId);
// 1. 根据学生ID查询学院ID
Long collegeId = baseMapper.selectCollegeIdByStudentId(studentId);
String collegeIdStr = baseMapper.selectCollegeIdByStudentId(studentId);
Long collegeId = null;
if (StringUtils.isNotBlank(collegeIdStr)) {
try {
collegeId = Long.valueOf(collegeIdStr);
} catch (NumberFormatException e) {
log.warn("学院ID格式错误,无法转换为Long类型 - 学生ID: {}, collegeIdStr: {}", studentId, collegeIdStr);
return new ArrayList<>();
}
}
if (collegeId == null) {
log.warn("未找到学生对应的学院信息 - 学生ID: {}", studentId);
return new ArrayList<>();
@@ -1124,7 +1133,16 @@ public class XgStudentMsgServiceImpl implements IXgStudentMsgService {
log.info("根据学生ID获取二级学院分管领导ID列表 - 学生ID: {}", studentId);
// 1. 根据学生ID查询学院ID
Long collegeId = baseMapper.selectCollegeIdByStudentId(studentId);
String collegeIdStr = baseMapper.selectCollegeIdByStudentId(studentId);
Long collegeId = null;
if (StringUtils.isNotBlank(collegeIdStr)) {
try {
collegeId = Long.valueOf(collegeIdStr);
} catch (NumberFormatException e) {
log.warn("学院ID格式错误,无法转换为Long类型 - 学生ID: {}, collegeIdStr: {}", studentId, collegeIdStr);
return new ArrayList<>();
}
}
if (collegeId == null) {
log.warn("未找到学生对应的学院信息 - 学生ID: {}", studentId);
return new ArrayList<>();
@@ -148,7 +148,7 @@ public class XgStudentServiceImpl implements IXgStudentService {
// "#result == null || #result.isEmpty()")
public List<XgStudentVo> getStudentsByClassId(Long classId) {
LambdaQueryWrapper<XgStudentMsg> lqw = Wrappers.lambdaQuery();
lqw.eq(XgStudentMsg::getClassId, classId);
lqw.eq(XgStudentMsg::getClassId, String.valueOf(classId));
lqw.eq(XgStudentMsg::getStudentStatus, "1"); // 只查询在籍学生
lqw.orderByDesc(XgStudentMsg::getStudentNo); // 按学号倒序排序
return baseMapper.selectVoList(lqw);
@@ -148,7 +148,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</select>
<!-- 根据学生ID查询学院ID -->
<select id="selectCollegeIdByStudentId" resultType="java.lang.Long">
<select id="selectCollegeIdByStudentId" resultType="java.lang.String">
SELECT college_id
FROM xg_student_msg
WHERE id = #{studentId}
+7
View File
@@ -10,6 +10,13 @@
- **[学生换班级记录表.sql](学生换班级记录表.sql)** - 学生换班级记录表的完整建表SQL脚本
- **[学生换班级记录表设计说明.md](学生换班级记录表设计说明.md)** - 详细的表结构设计说明文档
### 🔧 处理脚本(新增)
- **[process_student_class_change.sh](process_student_class_change.sh)** - 准实时处理换班级工单的主脚本
- **[control_scripts.sh](control_scripts.sh)** - 脚本控制程序,支持启动、停止、重启等操作
- **[health_check.sh](health_check.sh)** - 服务健康检查和监控脚本
- **[data.conf](data.conf)** - 数据库连接和脚本运行配置
- **[README_换班级脚本.md](README_换班级脚本.md)** - 换班级处理脚本的详细技术文档
## 🎯 业务概述
学生换班级记录表用于记录学校所有学生换班级的操作历史,包括:
+248
View File
@@ -0,0 +1,248 @@
# 学生换班级处理脚本
## 📁 概述
本脚本用于准实时处理学生换班级工单,实现学生信息的自动更新和干部信息的终止处理。参考退学、休学、复学、毕业脚本的架构设计,提供完整的换班级业务处理能力。
## 🎯 业务需求
### 1. 干部信息终止
- 参考退学等脚本思路,终止学生的干部职务
- 设置干部状态为"换班级终止"(cadre_status = '5')
- 记录终止原因和时间
### 2. 学生信息更新
- 更新学生基本班级信息(学院、专业、班级、年级等)
- 更新预留字段,从新班级表获取完整信息
- 特别处理 `academic_level` 字段,对应 `xg_class_msg.class_type`
## 📋 文件清单
### 🔧 核心脚本
- **[process_student_class_change.sh](process_student_class_change.sh)** - 主处理脚本,实现换班级业务逻辑
- **[control_scripts.sh](control_scripts.sh)** - 脚本控制程序,支持启动、停止、重启等操作
- **[health_check.sh](health_check.sh)** - 服务健康检查脚本
### ⚙️ 配置文件
- **[data.conf](data.conf)** - 数据库连接配置
### 📊 数据库设计
- **[学生换班级记录表.sql](学生换班级记录表.sql)** - 换班级记录表结构
- **[学生换班级记录表设计说明.md](学生换班级记录表设计说明.md)** - 详细设计文档
## 🔄 处理流程
```
换班级工单提交 → 脚本检测 → 获取新班级信息 → 终止干部职务 → 更新学生信息 → 标记处理完成
```
### 详细步骤
1. **工单检测**: 扫描 `status = '0'` 的换班级记录
2. **班级信息获取**: 从 `xg_class_msg` 表获取新班级完整信息
3. **干部信息终止**:
- 设置 `is_current = 0`
- 设置 `cadre_status = '5'`(换班级终止)
- 记录终止日期和原因
4. **学生信息更新**:
- 更新基础班级信息(college_id, major_id, class_id等)
- 更新预留字段(school_system, academic_level, training_type等)
- academic_level 对应 class_type
5. **工单状态更新**: 设置 `status = '1'` 标记处理完成
## 🗄️ 数据映射
### 新班级信息获取
```sql
SELECT
college_id, college_name, major_id, major_name,
class_code, class_name, class_type, grade,
training_type, counselor_id, counselor_name,
class_teacher_id, class_teacher_name, school_system
FROM xg_class_msg
WHERE id = {new_class_id}
```
### 学生信息更新字段
```sql
UPDATE xg_student_msg SET
-- 基础班级信息
college_id = '{college_id}',
college_name = '{college_name}',
major_id = '{major_id}',
major_name = '{major_name}',
class_id = '{new_class_id}',
class_name = '{class_name}',
grade = '{grade}',
-- 预留字段 (7182-7195行)
school_system = '{school_system}',
academic_level = '{class_type}', -- 关键映射
training_type = '{training_type}',
counselor_id = {counselor_id},
counselor_name = '{counselor_name}',
class_teacher_id = {class_teacher_id},
class_teacher_name = '{class_teacher_name}',
-- 更新信息
update_by = {operator_id},
update_time = NOW()
WHERE id = {student_id}
```
## 🚀 部署使用
### 1. 配置数据库连接
```bash
vim data.conf
# 修改数据库连接信息
```
### 2. 启动服务
```bash
# 启动换班级处理服务
./control_scripts.sh start class_change
# 或启动所有服务
./control_scripts.sh startall
```
### 3. 查看服务状态
```bash
# 查看所有服务状态
./control_scripts.sh statusall
# 查看特定服务
./control_scripts.sh status class_change
```
### 4. 健康检查
```bash
# 执行健康检查
./health_check.sh
# 生成详细报告
./health_check.sh --report
```
### 5. 停止服务
```bash
# 停止换班级处理服务
./control_scripts.sh stop class_change
# 或停止所有服务
./control_scripts.sh stopall
```
## 📊 监控日志
### 日志位置
- **主日志**: `./student_class_change/process_student_class_change_*.log`
- **包含信息**: 处理详情、错误信息、性能统计
### 日志内容示例
```
[2025-01-25 10:00:00] [INFO] Starting student class change processing daemon
[2025-01-25 10:00:00] [INFO] Processing student change - ID: 123, Student: 20240001 (456), New Class ID: 789
[2025-01-25 10:00:00] [INFO] Successfully processed student class change - ID: 123, Student: 20240001
```
## ⚙️ 配置参数
### data.conf 配置项
```bash
# 数据库配置
DB_HOST="localhost"
DB_PORT="3306"
DB_NAME="hzdyjs_xg"
DB_USER="root"
DB_PASS="password"
# 脚本配置
LOG_DIR="./student_class_change"
BATCH_SIZE=100 # 每批处理数量
MAX_RETRY=3 # 最大重试次数
RETRY_DELAY=5 # 重试间隔(秒)
CYCLE_INTERVAL=600 # 检查周期(秒)
```
## 🔧 故障排除
### 常见问题
1. **数据库连接失败**
```bash
# 检查配置
cat data.conf
# 测试连接
mysql -h$DB_HOST -P$DB_PORT -u$DB_USER -p$DB_PASS $DB_NAME -e "SELECT 1;"
```
2. **服务无法启动**
```bash
# 检查脚本权限
ls -la *.sh
# 设置执行权限
chmod +x *.sh
```
3. **处理失败**
```bash
# 查看日志
tail -f ./student_class_change/*.log
# 检查数据
mysql -e "SELECT * FROM xg_student_class_change WHERE status = '0';"
```
### 性能优化
1. **索引检查**: 确保相关表的索引完整
2. **批量处理**: 每次处理适当数量的记录
3. **日志轮转**: 定期清理旧日志文件
4. **监控资源**: 监控CPU、内存、磁盘使用情况
## 📈 统计监控
### 处理统计
```sql
-- 查看处理统计
SELECT
COUNT(*) as total_records,
SUM(CASE WHEN status = '0' THEN 1 ELSE 0 END) as pending,
SUM(CASE WHEN status = '1' THEN 1 ELSE 0 END) as processed
FROM xg_student_class_change
WHERE deleted = 0;
-- 查看最近处理情况
SELECT
DATE(update_time) as process_date,
COUNT(*) as processed_count
FROM xg_student_class_change
WHERE status = '1'
AND update_time >= DATE_SUB(NOW(), INTERVAL 7 DAY)
GROUP BY DATE(update_time)
ORDER BY process_date DESC;
```
## 🔄 扩展开发
### 添加新的业务逻辑
1. 在 `process_student_class_change()` 函数中添加处理步骤
2. 更新事务SQL,确保数据一致性
3. 添加相应的日志记录
4. 更新健康检查逻辑
### 自定义配置
1. 在 `data.conf` 中添加新配置项
2. 在脚本中读取和使用配置
3. 更新文档说明
## 📞 技术支持
如有疑问或需要技术支持,请联系项目技术团队。
---
**创建时间**: 2025-01-25
**维护人员**: AI Assistant
**版本**: v1.0.0
+332
View File
@@ -0,0 +1,332 @@
#!/bin/bash
# ===========================================
# 学生换班级处理脚本控制程序
# 用于启动、停止、重启处理脚本
# ===========================================
set -euo pipefail
# Load configuration
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"
# Script definitions
declare -A SCRIPTS=(
["class_change"]="process_student_class_change.sh"
)
# PID files
PID_DIR="${SCRIPT_DIR}/pids"
mkdir -p "$PID_DIR"
# Functions
log() {
local level="$1"
local message="$2"
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo "[$timestamp] [$level] $message"
}
get_pid_file() {
local script_name="$1"
echo "${PID_DIR}/${script_name}.pid"
}
get_lock_file() {
local script_name="$1"
echo "${SCRIPT_DIR}/${script_name}.lock"
}
is_running() {
local script_name="$1"
local pid_file=$(get_pid_file "$script_name")
local lock_file=$(get_lock_file "$script_name")
# Check PID file
if [[ -f "$pid_file" ]]; then
local pid=$(cat "$pid_file")
if kill -0 "$pid" 2>/dev/null; then
return 0 # Running
else
log "WARN" "删除脚本 $script_name 的过期PID文件"
rm -f "$pid_file"
fi
fi
# Check lock file
if [[ -f "$lock_file" ]]; then
local lock_pid=$(cat "$lock_file")
if kill -0 "$lock_pid" 2>/dev/null; then
return 0 # Running
else
log "WARN" "删除脚本 $script_name 的过期锁定文件"
rm -f "$lock_file"
fi
fi
return 1 # Not running
}
start_script() {
local script_name="$1"
local script_file="${SCRIPT_DIR}/${SCRIPTS[$script_name]}"
local pid_file=$(get_pid_file "$script_name")
if [[ ! -f "$script_file" ]]; then
log "ERROR" "脚本文件未找到: $script_file"
return 1
fi
if is_running "$script_name"; then
log "INFO" "$script_name 已在运行"
return 0
fi
log "INFO" "正在启动 $script_name..."
chmod +x "$script_file"
# Start script in background
nohup "$script_file" > /dev/null 2>&1 &
local pid=$!
# Save PID
echo $pid > "$pid_file"
# Wait a moment and check if it's still running
sleep 2
if kill -0 "$pid" 2>/dev/null; then
log "INFO" "$script_name 启动成功 (PID: $pid)"
return 0
else
log "ERROR" "$script_name 启动失败"
rm -f "$pid_file"
return 1
fi
}
stop_script() {
local script_name="$1"
local pid_file=$(get_pid_file "$script_name")
local lock_file=$(get_lock_file "$script_name")
if ! is_running "$script_name"; then
log "INFO" "$script_name 未运行"
return 0
fi
log "INFO" "正在停止 $script_name..."
# Try graceful shutdown first
local pids=()
[[ -f "$pid_file" ]] && pids+=($(cat "$pid_file"))
[[ -f "$lock_file" ]] && pids+=($(cat "$lock_file"))
for pid in "${pids[@]}"; do
if kill -0 "$pid" 2>/dev/null; then
kill "$pid" 2>/dev/null || true
fi
done
# Wait for graceful shutdown
local count=0
while [[ $count -lt 10 ]] && is_running "$script_name"; do
sleep 1
((count++))
done
# Force kill if still running
if is_running "$script_name"; then
log "WARN" "Graceful shutdown failed, force killing $script_name..."
for pid in "${pids[@]}"; do
if kill -0 "$pid" 2>/dev/null; then
kill -9 "$pid" 2>/dev/null || true
fi
done
sleep 1
fi
# Clean up files
rm -f "$pid_file" "$lock_file"
if is_running "$script_name"; then
log "ERROR" "Failed to stop $script_name"
return 1
else
log "INFO" "$script_name stopped successfully"
return 0
fi
}
restart_script() {
local script_name="$1"
log "INFO" "正在重启 $script_name..."
if stop_script "$script_name" && start_script "$script_name"; then
log "INFO" "$script_name 重启成功"
return 0
else
log "ERROR" "$script_name 重启失败"
return 1
fi
}
status_script() {
local script_name="$1"
if is_running "$script_name"; then
local pid_file=$(get_pid_file "$script_name")
local lock_file=$(get_lock_file "$script_name")
local pid=""
if [[ -f "$pid_file" ]]; then
pid=$(cat "$pid_file")
elif [[ -f "$lock_file" ]]; then
pid=$(cat "$lock_file")
fi
echo "$script_name 正在运行 (PID: $pid)"
return 0
else
echo "$script_name 未运行"
return 1
fi
}
usage() {
echo "用法: $0 {start|stop|restart|status} {script_name}"
echo " $0 {startall|stopall|restartall|statusall}"
echo ""
echo "可用脚本:"
for script in "${!SCRIPTS[@]}"; do
echo " $script - ${SCRIPTS[$script]}"
done
echo ""
echo "示例:"
echo " $0 start class_change # 启动换班级处理"
echo " $0 stop class_change # 停止换班级处理"
echo " $0 restart class_change # 重启换班级处理"
echo " $0 status class_change # 检查换班级处理状态"
echo " $0 startall # 启动所有脚本"
echo " $0 stopall # 停止所有脚本"
echo " $0 statusall # 检查所有脚本状态"
}
# Main logic
case "${1:-}" in
start)
if [[ -z "${2:-}" ]]; then
usage
exit 1
fi
if [[ -n "${SCRIPTS[$2]:-}" ]]; then
start_script "$2"
else
echo "未知脚本: $2"
usage
exit 1
fi
;;
stop)
if [[ -z "${2:-}" ]]; then
usage
exit 1
fi
if [[ -n "${SCRIPTS[$2]:-}" ]]; then
stop_script "$2"
else
echo "未知脚本: $2"
usage
exit 1
fi
;;
restart)
if [[ -z "${2:-}" ]]; then
usage
exit 1
fi
if [[ -n "${SCRIPTS[$2]:-}" ]]; then
restart_script "$2"
else
echo "未知脚本: $2"
usage
exit 1
fi
;;
status)
if [[ -z "${2:-}" ]]; then
usage
exit 1
fi
if [[ -n "${SCRIPTS[$2]:-}" ]]; then
status_script "$2"
else
echo "未知脚本: $2"
usage
exit 1
fi
;;
startall)
log "INFO" "正在启动所有脚本..."
local failed=()
for script in "${!SCRIPTS[@]}"; do
if ! start_script "$script"; then
failed+=("$script")
fi
done
if [[ ${#failed[@]} -eq 0 ]]; then
log "INFO" "所有脚本启动成功"
else
log "ERROR" "启动脚本失败: ${failed[*]}"
exit 1
fi
;;
stopall)
log "INFO" "正在停止所有脚本..."
local failed=()
for script in "${!SCRIPTS[@]}"; do
if ! stop_script "$script"; then
failed+=("$script")
fi
done
if [[ ${#failed[@]} -eq 0 ]]; then
log "INFO" "所有脚本停止成功"
else
log "ERROR" "停止脚本失败: ${failed[*]}"
exit 1
fi
;;
restartall)
log "INFO" "正在重启所有脚本..."
local failed=()
for script in "${!SCRIPTS[@]}"; do
if ! restart_script "$script"; then
failed+=("$script")
fi
done
if [[ ${#failed[@]} -eq 0 ]]; then
log "INFO" "所有脚本重启成功"
else
log "ERROR" "重启脚本失败: ${failed[*]}"
exit 1
fi
;;
statusall)
log "INFO" "正在检查所有脚本的状态..."
for script in "${!SCRIPTS[@]}"; do
echo -n "$script: "
status_script "$script"
done
;;
*)
usage
exit 1
;;
esac
+17
View File
@@ -0,0 +1,17 @@
# Database configuration
DB_HOST="47.99.164.66"
DB_PORT="3306"
DB_NAME="hzdyjs_xg"
DB_USER="root"
DB_PASS="hn02le.34lkdLKD"
# Script configuration
LOG_DIR="${SCRIPT_DIR}/student_class_change"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
LOCK_FILE="${SCRIPT_DIR}/script.lock"
# Processing configuration
BATCH_SIZE=100
MAX_RETRY=3
RETRY_DELAY=5
CYCLE_INTERVAL=600 # 循环间隔时间(秒),默认10分钟
+348
View File
@@ -0,0 +1,348 @@
#!/bin/bash
# ===========================================
# 学生换班级处理服务健康检查脚本
# 用于监控服务运行状态和数据库连接
# ===========================================
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CONFIG_FILE="${SCRIPT_DIR}/data.conf"
CONTROL_SCRIPT="${SCRIPT_DIR}/control_scripts.sh"
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Load configuration
if [[ ! -f "$CONFIG_FILE" ]]; then
echo -e "${RED}错误:配置文件 $CONFIG_FILE 未找到!${NC}"
exit 1
fi
source "$CONFIG_FILE"
# Functions
log() {
local level="$1"
local message="$2"
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo -e "${timestamp} [$level] $message"
}
success() {
echo -e "${GREEN}✓ $1${NC}"
}
error() {
echo -e "${RED}✗ $1${NC}"
}
warning() {
echo -e "${YELLOW}⚠ $1${NC}"
}
info() {
echo -e "${BLUE}ℹ $1${NC}"
}
# 检查数据库连接
check_database() {
info "正在检查数据库连接..."
local db_test
if db_test=$(mysql -h"$DB_HOST" -P"$DB_PORT" -u"$DB_USER" -p"$DB_PASS" "$DB_NAME" -e "SELECT 1 as test;" 2>/dev/null); then
if echo "$db_test" | grep -q "test"; then
success "数据库连接成功"
return 0
else
error "数据库查询失败"
return 1
fi
else
error "数据库连接失败"
return 1
fi
}
# 检查服务状态
check_service() {
local service_name="$1"
local service_desc="$2"
info "正在检查 $service_desc..."
if [[ ! -x "$CONTROL_SCRIPT" ]]; then
error "控制脚本未找到或不可执行: $CONTROL_SCRIPT"
return 1
fi
local status_output
status_output=$("$CONTROL_SCRIPT" status "$service_name" 2>/dev/null)
if echo "$status_output" | grep -q "is running"; then
success "$service_desc 正在运行"
# 提取PID(如果可用)
local pid
pid=$(echo "$status_output" | grep -o "PID: [0-9]*" | cut -d' ' -f2)
if [[ -n "$pid" ]]; then
info " 进程ID: $pid"
fi
return 0
else
error "$service_desc 未运行"
return 1
fi
}
# 检查待处理记录
check_pending_records() {
info "正在检查待处理的换班级记录..."
local pending_count
if pending_count=$(mysql -h"$DB_HOST" -P"$DB_PORT" -u"$DB_USER" -p"$DB_PASS" "$DB_NAME" -e "
SELECT COUNT(*) as pending
FROM xg_student_class_change
WHERE status = '0' AND deleted = 0;
" 2>/dev/null | tail -n1); then
if [[ "$pending_count" -eq 0 ]]; then
success "无待处理的换班级记录"
elif [[ "$pending_count" -lt 10 ]]; then
warning "$pending_count 条待处理的换班级记录"
else
error "$pending_count 条待处理的换班级记录 (积压严重)"
fi
return 0
else
error "检查待处理记录失败"
return 1
fi
}
# 检查最近的处理活动
check_recent_processing() {
info "正在检查最近的处理活动..."
local recent_count
if recent_count=$(mysql -h"$DB_HOST" -P"$DB_PORT" -u"$DB_USER" -p"$DB_PASS" "$DB_NAME" -e "
SELECT COUNT(*) as recent
FROM xg_student_class_change
WHERE status = '1'
AND update_time >= DATE_SUB(NOW(), INTERVAL 1 HOUR)
AND deleted = 0;
" 2>/dev/null | tail -n1); then
if [[ "$recent_count" -gt 0 ]]; then
success "最近一小时处理了 $recent_count 条记录"
else
warning "最近一小时未处理任何记录"
fi
return 0
else
error "检查最近处理活动失败"
return 1
fi
}
# 检查日志文件
check_logs() {
info "正在检查日志文件..."
if [[ -d "$LOG_DIR" ]]; then
local log_count
log_count=$(find "$LOG_DIR" -name "*.log" -type f -mtime -1 2>/dev/null | wc -l)
if [[ "$log_count" -gt 0 ]]; then
success "发现最近的日志文件 ($log_count 个文件来自最近24小时)"
else
warning "未发现最近的日志文件"
fi
# 检查错误日志
local error_count
error_count=$(find "$LOG_DIR" -name "*.log" -type f -exec grep -l "ERROR" {} \; 2>/dev/null | wc -l)
if [[ "$error_count" -gt 0 ]]; then
warning "$error_count 个日志文件包含错误"
else
success "最近日志中未发现错误消息"
fi
else
error "日志目录未找到: $LOG_DIR"
return 1
fi
}
# 检查磁盘空间
check_disk_space() {
info "正在检查磁盘空间..."
local disk_usage
disk_usage=$(df "$SCRIPT_DIR" | tail -1 | awk '{print $5}' | sed 's/%//')
if [[ "$disk_usage" -lt 80 ]]; then
success "磁盘使用率: ${disk_usage}%"
elif [[ "$disk_usage" -lt 90 ]]; then
warning "磁盘使用率: ${disk_usage}% (接近限制)"
else
error "磁盘使用率: ${disk_usage}% (严重)"
return 1
fi
}
# 检查内存使用情况
check_memory() {
info "正在检查内存使用情况..."
local mem_usage
mem_usage=$(free | grep Mem | awk '{printf "%.0f", $3/$2 * 100.0}')
if [[ "$mem_usage" -lt 80 ]]; then
success "内存使用率: ${mem_usage}%"
elif [[ "$mem_usage" -lt 90 ]]; then
warning "内存使用率: ${mem_usage}% (接近限制)"
else
error "内存使用率: ${mem_usage}% (严重)"
return 1
fi
}
# Generate health report
generate_report() {
local report_file="${SCRIPT_DIR}/health_report_$(date +%Y%m%d_%H%M%S).txt"
{
echo "=== 学生换班级处理服务健康检查报告 ==="
echo "检查时间: $(date)"
echo "服务器信息: $(hostname)"
echo ""
echo "=== 数据库连接 ==="
if check_database; then
echo "✓ 数据库连接正常"
else
echo "✗ 数据库连接异常"
fi
echo ""
echo "=== 服务状态 ==="
if check_service "class_change" "学生换班级处理服务"; then
echo "✓ 学生换班级处理服务运行正常"
else
echo "✗ 学生换班级处理服务未运行"
fi
echo ""
echo "=== 业务数据检查 ==="
check_pending_records
echo ""
check_recent_processing
echo ""
echo "=== 系统资源 ==="
check_disk_space
echo ""
check_memory
echo ""
echo "=== 日志检查 ==="
check_logs
echo ""
echo "报告生成完成"
} > "$report_file" 2>/dev/null
success "健康检查报告已生成: $report_file"
}
# Main function
main() {
echo ""
echo "=== 学生换班级处理服务健康检查 ==="
echo "检查时间: $(date)"
echo "服务器信息: $(hostname)"
echo ""
local checks_passed=0
local total_checks=0
# Database connection
echo "=== 数据库连接 ==="
((total_checks++))
check_database && ((checks_passed++))
echo ""
# Service status
echo "=== 服务状态 ==="
((total_checks++))
check_service "class_change" "学生换班级处理服务" && ((checks_passed++))
echo ""
# Business data
echo "=== 业务数据检查 ==="
((total_checks++))
check_pending_records && ((checks_passed++))
echo ""
((total_checks++))
check_recent_processing && ((checks_passed++))
echo ""
# System resources
echo "=== 系统资源 ==="
((total_checks++))
check_disk_space && ((checks_passed++))
echo ""
((total_checks++))
check_memory && ((checks_passed++))
echo ""
# Logs
echo "=== 日志检查 ==="
((total_checks++))
check_logs && ((checks_passed++))
echo ""
# 总结
echo "=== 检查总结 ==="
local success_rate=$((checks_passed * 100 / total_checks))
echo "通过检查: $checks_passed/$total_checks ($success_rate%)"
if [[ $success_rate -eq 100 ]]; then
success "所有检查通过,服务运行正常"
exit 0
elif [[ $success_rate -ge 80 ]]; then
warning "大部分检查通过,服务运行基本正常"
exit 0
else
error "多项检查失败,建议立即检查服务状态"
exit 1
fi
}
# 命令行选项
case "${1:-}" in
--report)
generate_report
;;
--help|-h)
echo "用法: $0 [选项]"
echo ""
echo "选项:"
echo " --report 生成详细的健康检查报告"
echo " --help 显示此帮助信息"
echo ""
echo "无选项时,执行标准健康检查。"
;;
*)
main "$@"
;;
esac
+301
View File
@@ -0,0 +1,301 @@
#!/bin/bash
# ===========================================
# 学生换班级处理脚本
# 处理类型: CLASS_CHANGE(换班级)
# ===========================================
set -euo pipefail
# Load configuration
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"
# Create log directory
mkdir -p "$LOG_DIR"
# Log file
LOG_FILE="${LOG_DIR}/process_student_class_change_$(date +%Y%m%d_%H%M%S).log"
# Lock file to prevent multiple instances
LOCK_FILE="${SCRIPT_DIR}/process_student_class_change.lock"
# Functions
log() {
local level="$1"
local message="$2"
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo "[$timestamp] [$level] $message" | tee -a "$LOG_FILE"
}
error_exit() {
local message="$1"
log "ERROR" "$message"
# Remove lock file
[[ -f "$LOCK_FILE" ]] && rm -f "$LOCK_FILE"
exit 1
}
# 检查脚本是否已在运行
if [[ -f "$LOCK_FILE" ]]; then
if kill -0 $(cat "$LOCK_FILE") 2>/dev/null; then
log "INFO" "脚本已在运行 (PID: $(cat "$LOCK_FILE"))"
exit 1
else
log "WARN" "删除过期的锁定文件"
rm -f "$LOCK_FILE"
fi
fi
# Create lock file
echo $$ > "$LOCK_FILE"
# 清理函数
cleanup() {
[[ -f "$LOCK_FILE" ]] && rm -f "$LOCK_FILE"
log "INFO" "脚本执行完成"
}
trap cleanup EXIT
# Database functions
execute_sql() {
local sql="$1"
local result
result=$(mysql -h"$DB_HOST" -P"$DB_PORT" -u"$DB_USER" -p"$DB_PASS" "$DB_NAME" -e "$sql" 2>&1)
local exit_code=$?
if [[ $exit_code -ne 0 ]]; then
log "ERROR" "SQL执行失败: $sql"
log "ERROR" "MySQL错误: $result"
return 1
fi
echo "$result"
}
# Process student class change
process_student_class_change() {
local record_id="$1"
local student_id="$2"
local student_no="$3"
local new_class_id="$4"
local operator_id="${5:-1}"
log "INFO" "Processing student class change - ID: $record_id, Student: $student_no ($student_id), New Class ID: $new_class_id"
# Get new class information
local class_info_sql="
SELECT
c.college_id, c.college_name, c.major_id, c.major_name,
c.class_code, c.class_name, c.class_type, c.grade,
c.training_type, c.counselor_id, c.counselor_name,
c.class_teacher_id, c.class_teacher_name, c.school_system
FROM xg_class_msg c
WHERE c.id = $new_class_id AND c.deleted = 0
"
local class_info
class_info=$(execute_sql "$class_info_sql")
if [[ -z "$class_info" || "$class_info" == *"Empty set"* ]]; then
log "ERROR" "获取班级ID为 $new_class_id 的班级信息失败"
return 1
fi
# Parse class information (skip header and separator lines, get data row)
local class_data
class_data=$(echo "$class_info" | grep -E '^[0-9]' | head -1)
if [[ -z "$class_data" ]]; then
log "ERROR" "解析班级信息失败,无法获取数据行"
return 1
fi
# Use cut to extract specific fields reliably
local college_id=$(echo "$class_data" | cut -f1)
local college_name=$(echo "$class_data" | cut -f2)
local major_id=$(echo "$class_data" | cut -f3)
local major_name=$(echo "$class_data" | cut -f4)
local class_code=$(echo "$class_data" | cut -f5)
local class_name=$(echo "$class_data" | cut -f6)
local class_type=$(echo "$class_data" | cut -f7)
local grade=$(echo "$class_data" | cut -f8)
local training_type=$(echo "$class_data" | cut -f9)
local counselor_id=$(echo "$class_data" | cut -f10)
local counselor_name=$(echo "$class_data" | cut -f11)
local class_teacher_id=$(echo "$class_data" | cut -f12)
local class_teacher_name=$(echo "$class_data" | cut -f13)
local school_system=$(echo "$class_data" | cut -f14)
# Handle NULL values for IDs
local counselor_id_value class_teacher_id_value
if [[ "$counselor_id" == "NULL" || -z "$counselor_id" ]]; then
counselor_id_value="NULL"
else
counselor_id_value="'$counselor_id'"
fi
if [[ "$class_teacher_id" == "NULL" || -z "$class_teacher_id" ]]; then
class_teacher_id_value="NULL"
else
class_teacher_id_value="'$class_teacher_id'"
fi
# Start transaction
local transaction_sql="
START TRANSACTION;
-- 1. Backup original data to his tables (ignore if already exists)
INSERT IGNORE INTO xg_student_msg_his SELECT * FROM xg_student_msg WHERE id = $student_id;
INSERT IGNORE INTO xg_class_pos_msg_his SELECT * FROM xg_class_pos_msg WHERE student_id = $student_id;
-- 2. Terminate cadre positions (干部信息终止)
UPDATE xg_class_pos_msg SET
is_current = 0,
cadre_status = '4', -- 免职
dismissal_date = CURDATE(),
dismissal_reason = '学生换班级',
term_end_date = CURDATE(),
term_duration = TIMESTAMPDIFF(MONTH, term_start_date, CURDATE()),
update_by = $operator_id,
update_time = NOW()
WHERE student_id = $student_id AND is_current = 1;
-- 3. Update student information with new class data
UPDATE xg_student_msg SET
-- 基础班级信息
college_id = '$college_id',
college_name = '$college_name',
major_id = '$major_id',
major_name = '$major_name',
class_id = '$new_class_id',
class_name = '$class_name',
grade = '$grade',
-- 预留字段从班级表关联获取的数据 (7182-7195行)
school_system = '$school_system',
academic_level = '$class_type', -- class_type 对应 academic_level
training_type = '$training_type',
counselor_id = $counselor_id_value,
counselor_name = '$counselor_name',
class_teacher_id = $class_teacher_id_value,
class_teacher_name = '$class_teacher_name',
-- 更新信息
update_by = $operator_id,
update_time = NOW()
WHERE id = $student_id;
-- 4. Update class change record status
UPDATE xg_student_class_change SET
status = '1', -- 已处理
update_by = $operator_id,
update_time = NOW()
WHERE id = $record_id;
COMMIT;
"
# 执行事务
if execute_sql "$transaction_sql"; then
log "INFO" "成功处理学生换班级 - ID: $record_id, 学生: $student_no"
return 0
else
log "ERROR" "处理学生换班级失败 - ID: $record_id, 学生: $student_no"
return 1
fi
}
# Main function
main() {
log "INFO" "启动学生换班级处理守护进程"
log "INFO" "处理类型: CLASS_CHANGE(换班级)"
log "INFO" "以连续模式运行,间隔10分钟"
# 测试数据库连接
log "INFO" "正在测试数据库连接..."
if ! execute_sql "SELECT 1;" >/dev/null 2>&1; then
error_exit "数据库连接失败"
fi
log "INFO" "数据库连接成功"
# Main processing loop
while true; do
log "INFO" "开始新的处理周期"
# 获取未处理的记录
local query_sql="
SELECT id, student_id, student_no, new_class_id, update_by
FROM xg_student_class_change
WHERE status = '0'
AND deleted = 0
AND effective_time <= NOW()
ORDER BY create_time ASC
LIMIT $BATCH_SIZE;
"
local records
records=$(execute_sql "$query_sql")
# 检查是否有记录需要处理
# 过滤掉MySQL的边框和表头,只保留数据行
local data_records
data_records=$(echo "$records" | grep -E '^[0-9]' || true)
if [[ -z "$data_records" ]]; then
log "INFO" "未找到未处理的记录,正在等待..."
else
log "INFO" "找到待处理记录,开始处理..."
records="$data_records"
local processed_count=0
local failed_count=0
# Process each record
while IFS=$'\t' read -r record_id student_id student_no new_class_id operator_id; do
# Skip empty lines
[[ -z "$record_id" ]] && continue
local retry_count=0
local success=false
while [[ $retry_count -lt $MAX_RETRY && $success == false ]]; do
if process_student_class_change "$record_id" "$student_id" "$student_no" "$new_class_id" "$operator_id"; then
success=true
((processed_count++))
else
((retry_count++))
if [[ $retry_count -lt $MAX_RETRY ]]; then
log "WARN" "处理失败,$RETRY_DELAY 秒后重试... (尝试 $retry_count/$MAX_RETRY)"
sleep $RETRY_DELAY
fi
fi
done
if [[ $success == false ]]; then
((failed_count++))
log "ERROR" "$MAX_RETRY 次尝试后仍处理失败 - ID: $record_id"
fi
done <<< "$records"
log "INFO" "处理周期完成 - 已处理: $processed_count, 失败: $failed_count"
fi
# 等待配置的间隔时间后开始下一个周期
log "INFO" "等待 $CYCLE_INTERVAL 秒后开始下一个处理周期..."
sleep $CYCLE_INTERVAL
done
}
# Run main function
main "$@"
+19 -23
View File
@@ -9,13 +9,10 @@
1. **`process_student_changes.sh`** - 处理毕业、退学、休学的状态变更
2. **`process_student_resume.sh`** - 处理复学的状态变更
3. **`control_scripts.sh`** - 脚本控制程序(启动、停止、重启)
4. **`start_services.sh`** - 一键启动所有服务
5. **`stop_services.sh`** - 一键停止所有服务
6. **`health_check.sh`** - 服务健康检查脚本
7. **`data.conf`** - 数据库配置文件
8. **`test_data.sql`** - 测试数据脚本
9. **`optional_crontab_config.txt`** - 可选的定时任务配置(备用)
10. **`demo.sh`** - 脚本使用演示
4. **`health_check.sh`** - 服务健康检查脚本
5. **`data.conf`** - 数据库配置文件
6. **`test_data.sql`** - 测试数据脚本
7. **`optional_crontab_config.txt`** - 可选的定时任务配置(备用)
### 处理逻辑
@@ -53,7 +50,7 @@ DB_USER="root" # 数据库用户名
DB_PASS="password" # 数据库密码
# Script configuration
LOG_DIR="/var/log/student_status_change" # 日志目录
LOG_DIR="./student_status_change" # 日志目录
SCRIPT_DIR="/path/to/scripts" # 脚本目录
LOCK_FILE="${SCRIPT_DIR}/script.lock" # 锁文件
@@ -84,7 +81,7 @@ DB_PASS="your_password"
```bash
# 启动所有服务(持续运行模式)
./start_services.sh
./control_scripts.sh startall
# 查看服务状态
./control_scripts.sh statusall
@@ -92,7 +89,7 @@ DB_PASS="your_password"
#### 持续运行模式说明
- **启动方式**: `./start_services.sh` 一次性启动所有服务
- **启动方式**: `./control_scripts.sh startall` 一次性启动所有服务
- **运行逻辑**: 每个脚本启动后进入while循环,持续监控和处理数据
- **处理周期**: 检查数据库 → 处理未处理记录 → 等待10分钟 → 重复循环
- **并发控制**: 通过锁文件确保同一脚本只运行一个实例
@@ -114,10 +111,10 @@ DB_PASS="your_password"
```bash
# 正常停止所有服务
./stop_services.sh
./control_scripts.sh stopall
# 强制停止所有服务(如果正常停止失败)
./stop_services.sh force
# 手动kill进程或使用系统命令
```
### 5. 健康检查
@@ -187,7 +184,7 @@ DB_PASS="your_password"
## 📝 日志说明
### 日志文件位置
- 日志目录:`/var/log/student_status_change/`
- 日志目录:`./student_status_change/`
- 日志格式:`process_student_changes_YYYYMMDD_HHMMSS.log`
### 日志内容
@@ -268,9 +265,9 @@ rm -f pids/*.pid
脚本采用持续运行模式,启动后自动循环执行:
1. **启动脚本**: `./start_services.sh`
1. **启动脚本**: `./control_scripts.sh startall`
2. **处理逻辑**: 检查未处理的记录 → 处理业务 → 等待10分钟 → 重复
3. **停止脚本**: `./stop_services.sh`
3. **停止脚本**: `./control_scripts.sh stopall`
### 可选的定时任务模式
@@ -286,7 +283,7 @@ crontab -e
| 特性 | 持续运行模式 | Crontab模式 |
|------|-------------|-------------|
| 启动方式 | `./start_services.sh` | crontab配置 |
| 启动方式 | `./control_scripts.sh startall` | crontab配置 |
| 执行频率 | 每10分钟自动 | 可自定义时间 |
| 资源占用 | 常驻进程 | 按需启动 |
| 监控便利性 | 更容易监控 | 需要额外配置 |
@@ -297,16 +294,15 @@ crontab -e
## 🚀 快速开始
### 💡 使用指南
1. **查看演示**: 运行 `./demo.sh` 查看脚本使用说明
2. **配置数据库**: 编辑 `data.conf` 配置数据库连接信息
3. **启动服务**: 使用 `./start_services.sh` 启动持续运行的服务
4. **监控运行**: 使用 `./health_check.sh` 检查服务状态
5. **停止服务**: 需要时使用 `./stop_services.sh` 停止服务
1. **配置数据库**: 编辑 `data.conf` 配置数据库连接信息
2. **启动服务**: 使用 `./control_scripts.sh startall` 启动持续运行的服务
3. **监控运行**: 使用 `./health_check.sh` 检查服务状态
4. **停止服务**: 需要时使用 `./control_scripts.sh stopall` 停止服务
### 🧪 测试步骤
1. **插入测试数据**: 执行 `test_data.sql` 插入测试记录
2. **启动服务**: `./start_services.sh`
3. **观察日志**: 查看 `/var/log/student_status_change/` 目录的日志
2. **启动服务**: `./control_scripts.sh startall`
3. **观察日志**: 查看 `./student_status_change/` 目录的日志
4. **检查结果**: 确认 `xg_student_status_change.process_status` 已更新为1
---
@@ -12,7 +12,7 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CONFIG_FILE="${SCRIPT_DIR}/data.conf"
if [[ ! -f "$CONFIG_FILE" ]]; then
echo "Error: Configuration file $CONFIG_FILE not found!"
echo "错误:配置文件 $CONFIG_FILE 未找到!"
exit 1
fi
@@ -57,7 +57,7 @@ is_running() {
if kill -0 "$pid" 2>/dev/null; then
return 0 # Running
else
log "WARN" "Removing stale PID file for $script_name"
log "WARN" "删除脚本 $script_name 的过期PID文件"
rm -f "$pid_file"
fi
fi
@@ -68,7 +68,7 @@ is_running() {
if kill -0 "$lock_pid" 2>/dev/null; then
return 0 # Running
else
log "WARN" "Removing stale lock file for $script_name"
log "WARN" "删除脚本 $script_name 的过期锁定文件"
rm -f "$lock_file"
fi
fi
@@ -82,16 +82,16 @@ start_script() {
local pid_file=$(get_pid_file "$script_name")
if [[ ! -f "$script_file" ]]; then
log "ERROR" "Script file not found: $script_file"
log "ERROR" "脚本文件未找到: $script_file"
return 1
fi
if is_running "$script_name"; then
log "INFO" "$script_name is already running"
log "INFO" "$script_name 已在运行"
return 0
fi
log "INFO" "Starting $script_name..."
log "INFO" "正在启动 $script_name..."
chmod +x "$script_file"
# Start script in background
@@ -103,10 +103,10 @@ start_script() {
if kill -0 "$pid" 2>/dev/null; then
echo $pid > "$pid_file"
log "INFO" "$script_name started successfully (PID: $pid)"
log "INFO" "$script_name 启动成功 (PID: $pid)"
return 0
else
log "ERROR" "Failed to start $script_name"
log "ERROR" "$script_name 启动失败"
return 1
fi
}
@@ -117,30 +117,30 @@ stop_script() {
local lock_file=$(get_lock_file "$script_name")
local stopped=false
log "INFO" "Stopping $script_name..."
log "INFO" "正在停止 $script_name..."
# Try to stop by PID file
# 尝试通过PID文件停止
if [[ -f "$pid_file" ]]; then
local pid=$(cat "$pid_file")
if kill -0 "$pid" 2>/dev/null; then
kill "$pid"
sleep 2
if ! kill -0 "$pid" 2>/dev/null; then
log "INFO" "$script_name stopped successfully (PID: $pid)"
log "INFO" "$script_name 停止成功 (PID: $pid)"
stopped=true
fi
fi
rm -f "$pid_file"
fi
# Try to stop by lock file
# 尝试通过锁定文件停止
if [[ -f "$lock_file" ]]; then
local lock_pid=$(cat "$lock_file")
if kill -0 "$lock_pid" 2>/dev/null; then
kill "$lock_pid"
sleep 2
if ! kill -0 "$lock_pid" 2>/dev/null; then
log "INFO" "$script_name stopped successfully (PID: $lock_pid)"
log "INFO" "$script_name 停止成功 (PID: $lock_pid)"
stopped=true
fi
fi
@@ -148,13 +148,13 @@ stop_script() {
fi
if [[ "$stopped" == false ]]; then
log "WARN" "$script_name may not have been running or failed to stop gracefully"
log "WARN" "$script_name 可能未运行或优雅停止失败"
fi
}
restart_script() {
local script_name="$1"
log "INFO" "Restarting $script_name..."
log "INFO" "正在重启 $script_name..."
stop_script "$script_name"
sleep 3
start_script "$script_name"
@@ -168,43 +168,43 @@ status_script() {
if [[ -f "$pid_file" ]]; then
local pid=$(cat "$pid_file")
log "INFO" "$script_name is running (PID: $pid)"
log "INFO" "$script_name 正在运行 (PID: $pid)"
elif [[ -f "$lock_file" ]]; then
local lock_pid=$(cat "$lock_file")
log "INFO" "$script_name is running (PID: $lock_pid)"
log "INFO" "$script_name 正在运行 (PID: $lock_pid)"
else
log "INFO" "$script_name status unknown"
log "INFO" "$script_name 状态未知"
fi
else
log "INFO" "$script_name is not running"
log "INFO" "$script_name 未运行"
fi
}
show_usage() {
cat << EOF
Usage: $0 <command> <script_name>
用法: $0 <命令> <脚本名称>
Commands:
start Start the specified script
stop Stop the specified script
restart Restart the specified script
status Show status of the specified script
startall Start all scripts
stopall Stop all scripts
statusall Show status of all scripts
命令:
start 启动指定的脚本
stop 停止指定的脚本
restart 重启指定的脚本
status 显示指定脚本的状态
startall 启动所有脚本
stopall 停止所有脚本
statusall 显示所有脚本的状态
Script names:
changes Student status changes script (休学/退学/毕业)
resume Student resume script (复学)
all All scripts
脚本名称:
changes 学生状态变更脚本 (休学/退学/毕业)
resume 学生复学脚本 (复学)
all 所有脚本
Examples:
$0 start changes # Start changes processing script
$0 stop resume # Stop resume processing script
$0 restart changes # Restart changes processing script
$0 status all # Show status of all scripts
$0 startall # Start all scripts
$0 stopall # Stop all scripts
示例:
$0 start changes # 启动状态变更处理脚本
$0 stop resume # 停止复学处理脚本
$0 restart changes # 重启状态变更处理脚本
$0 status all # 显示所有脚本的状态
$0 startall # 启动所有脚本
$0 stopall # 停止所有脚本
EOF
}
@@ -223,7 +223,7 @@ main() {
elif [[ -n "${SCRIPTS[$script_name]}" ]]; then
start_script "$script_name"
else
log "ERROR" "Unknown script name: $script_name"
log "ERROR" "未知的脚本名称: $script_name"
show_usage
exit 1
fi
@@ -237,7 +237,7 @@ main() {
elif [[ -n "${SCRIPTS[$script_name]}" ]]; then
stop_script "$script_name"
else
log "ERROR" "Unknown script name: $script_name"
log "ERROR" "未知的脚本名称: $script_name"
show_usage
exit 1
fi
@@ -251,7 +251,7 @@ main() {
elif [[ -n "${SCRIPTS[$script_name]}" ]]; then
restart_script "$script_name"
else
log "ERROR" "Unknown script name: $script_name"
log "ERROR" "未知的脚本名称: $script_name"
show_usage
exit 1
fi
@@ -265,35 +265,35 @@ main() {
elif [[ -n "${SCRIPTS[$script_name]}" ]]; then
status_script "$script_name"
else
log "ERROR" "Unknown script name: $script_name"
log "ERROR" "未知的脚本名称: $script_name"
show_usage
exit 1
fi
;;
startall)
log "INFO" "Starting all scripts..."
log "INFO" "正在启动所有脚本..."
for script in "${!SCRIPTS[@]}"; do
start_script "$script"
done
;;
stopall)
log "INFO" "Stopping all scripts..."
log "INFO" "正在停止所有脚本..."
for script in "${!SCRIPTS[@]}"; do
stop_script "$script"
done
;;
statusall)
log "INFO" "Checking status of all scripts..."
log "INFO" "正在检查所有脚本的状态..."
for script in "${!SCRIPTS[@]}"; do
status_script "$script"
done
;;
*)
log "ERROR" "Unknown command: $command"
log "ERROR" "未知命令: $command"
show_usage
exit 1
;;
+3 -3
View File
@@ -1,12 +1,12 @@
# Database configuration
DB_HOST="localhost"
DB_HOST="47.99.164.66"
DB_PORT="3306"
DB_NAME="hzdyjs_xg"
DB_USER="root"
DB_PASS="password"
DB_PASS="hn02le.34lkdLKD"
# Script configuration
LOG_DIR="/var/log/student_status_change"
LOG_DIR="${SCRIPT_DIR}/student_status_change"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
LOCK_FILE="${SCRIPT_DIR}/script.lock"
-84
View File
@@ -1,84 +0,0 @@
#!/bin/bash
# ===========================================
# 学生状态变更处理脚本演示
# 展示如何使用这些脚本
# ===========================================
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
echo "========================================"
echo " 学生状态变更处理脚本演示"
echo "========================================"
echo ""
echo "脚本位置: $SCRIPT_DIR"
echo ""
echo "📋 脚本文件说明:"
echo " process_student_changes.sh - 处理毕业/退学/休学的状态变更"
echo " process_student_resume.sh - 处理复学的状态变更"
echo " control_scripts.sh - 脚本控制程序"
echo " start_services.sh - 一键启动所有服务"
echo " stop_services.sh - 一键停止所有服务"
echo " health_check.sh - 服务健康检查"
echo ""
echo "🔧 配置文件:"
echo " data.conf - 数据库配置"
echo " test_data.sql - 测试数据"
echo ""
echo "📖 使用步骤:"
echo ""
echo "1️⃣ 配置数据库连接:"
echo " vim data.conf"
echo " # 修改数据库连接信息"
echo ""
echo "2️⃣ 启动服务(持续运行模式):"
echo " ./start_services.sh"
echo ""
echo "3️⃣ 查看服务状态:"
echo " ./control_scripts.sh statusall"
echo ""
echo "4️⃣ 执行健康检查:"
echo " ./health_check.sh"
echo ""
echo "5️⃣ 停止服务:"
echo " ./stop_services.sh"
echo ""
echo "📊 运行模式说明:"
echo "• 脚本启动后进入持续运行模式"
echo "• 每10分钟自动检查和处理一次数据"
echo "• 支持并发控制,避免重复运行"
echo "• 完整的日志记录和错误处理"
echo ""
echo "🧪 测试数据:"
echo "• 运行 test_data.sql 插入测试数据"
echo "• 脚本会自动处理 process_status = 0 的记录"
echo ""
echo "📝 日志位置:"
echo "• /var/log/student_status_change/"
echo "• 包含详细的处理日志和错误信息"
echo ""
echo "⚠️ 注意事项:"
echo "• 确保数据库连接正确"
echo "• 确保用户有足够的数据库权限"
echo "• 定期检查日志文件"
echo "• 监控磁盘空间使用情况"
echo ""
echo "========================================"
echo " 演示完成"
echo "========================================"
@@ -20,7 +20,7 @@ NC='\033[0m' # No Color
# Load configuration
if [[ ! -f "$CONFIG_FILE" ]]; then
echo -e "${RED}Error: Configuration file $CONFIG_FILE not found!${NC}"
echo -e "${RED}错误:配置文件 $CONFIG_FILE 未找到!${NC}"
exit 1
fi
@@ -50,34 +50,34 @@ info() {
echo -e "${BLUE}ℹ $1${NC}"
}
# Check database connection
# 检查数据库连接
check_database() {
info "Checking database connection..."
info "正在检查数据库连接..."
local db_test
if db_test=$(mysql -h"$DB_HOST" -P"$DB_PORT" -u"$DB_USER" -p"$DB_PASS" "$DB_NAME" -e "SELECT 1 as test;" 2>/dev/null); then
if echo "$db_test" | grep -q "test"; then
success "Database connection successful"
success "数据库连接成功"
return 0
else
error "Database query failed"
error "数据库查询失败"
return 1
fi
else
error "Database connection failed"
error "数据库连接失败"
return 1
fi
}
# Check service status
# 检查服务状态
check_service() {
local service_name="$1"
local service_desc="$2"
info "Checking $service_desc..."
info "正在检查 $service_desc..."
if [[ ! -x "$CONTROL_SCRIPT" ]]; then
error "Control script not found or not executable: $CONTROL_SCRIPT"
error "控制脚本未找到或不可执行: $CONTROL_SCRIPT"
return 1
fi
@@ -85,58 +85,58 @@ check_service() {
status_output=$("$CONTROL_SCRIPT" status "$service_name" 2>/dev/null)
if echo "$status_output" | grep -q "is running"; then
success "$service_desc is running"
success "$service_desc 正在运行"
# Extract PID if available
# 提取PID(如果可用)
local pid
pid=$(echo "$status_output" | grep -o "PID: [0-9]*" | cut -d' ' -f2)
if [[ -n "$pid" ]]; then
info " Process ID: $pid"
info " 进程ID: $pid"
fi
return 0
else
error "$service_desc is not running"
error "$service_desc 未运行"
return 1
fi
}
# Check log files
# 检查日志文件
check_logs() {
info "Checking log files..."
info "正在检查日志文件..."
if [[ ! -d "$LOG_DIR" ]]; then
warning "Log directory does not exist: $LOG_DIR"
warning "日志目录不存在: $LOG_DIR"
return 1
fi
# Count log files
# 统计日志文件数量
local log_count
log_count=$(find "$LOG_DIR" -name "*.log" -type f | wc -l)
if [[ $log_count -gt 0 ]]; then
success "Found $log_count log files"
success "发现 $log_count 个日志文件"
# Check for recent errors
# 检查最近的错误
local recent_errors
recent_errors=$(find "$LOG_DIR" -name "*.log" -type f -mmin -60 -exec grep -l "ERROR" {} \; | wc -l)
if [[ $recent_errors -gt 0 ]]; then
warning "Found $recent_errors log files with recent errors (last 60 minutes)"
warning "发现 $recent_errors 个日志文件包含最近的错误 (最近60分钟)"
else
info "No recent errors found in log files"
info "日志文件中未发现最近的错误"
fi
return 0
else
warning "No log files found"
warning "未发现日志文件"
return 1
fi
}
# Check pending records
# 检查待处理记录
check_pending_records() {
info "Checking pending records in database..."
info "正在检查数据库中的待处理记录..."
local pending_changes pending_resume
@@ -171,39 +171,39 @@ check_pending_records() {
local total_pending=$((pending_changes + pending_resume))
if [[ $total_pending -gt 0 ]]; then
info "Found $total_pending pending records ($pending_changes changes, $pending_resume resume)"
info "发现 $total_pending 条待处理记录 ($pending_changes 条变更, $pending_resume 条复学)"
if [[ $pending_changes -gt 0 ]]; then
warning " $pending_changes records waiting for changes processing"
warning " $pending_changes 条记录等待变更处理"
fi
if [[ $pending_resume -gt 0 ]]; then
warning " $pending_resume records waiting for resume processing"
warning " $pending_resume 条记录等待复学处理"
fi
else
success "No pending records found"
success "未发现待处理记录"
fi
return 0
}
# Check disk space
# 检查磁盘空间
check_disk_space() {
info "Checking disk space..."
info "正在检查磁盘空间..."
local log_disk_usage
log_disk_usage=$(df "$LOG_DIR" 2>/dev/null | tail -n1 | awk '{print $5}' | sed 's/%//')
if [[ -z "$log_disk_usage" ]]; then
warning "Cannot determine log directory disk usage"
warning "无法确定日志目录的磁盘使用情况"
return 1
fi
if [[ $log_disk_usage -gt 90 ]]; then
error "Log directory disk usage is ${log_disk_usage}% - consider cleaning up old logs"
error "日志目录磁盘使用率达到 ${log_disk_usage}% - 建议清理旧日志"
return 1
elif [[ $log_disk_usage -gt 75 ]]; then
warning "Log directory disk usage is ${log_disk_usage}%"
warning "日志目录磁盘使用率达到 ${log_disk_usage}%"
else
success "Log directory disk usage is ${log_disk_usage}%"
success "日志目录磁盘使用率 ${log_disk_usage}%"
fi
return 0
@@ -267,9 +267,9 @@ generate_report() {
} > "$report_file" 2>/dev/null
if [[ -f "$report_file" ]]; then
success "Health check report generated: $report_file"
success "健康检查报告已生成: $report_file"
else
error "Failed to generate health check report"
error "生成健康检查报告失败"
fi
}
@@ -320,14 +320,14 @@ main() {
echo ""
}
# Show usage
# 显示使用方法
show_usage() {
cat << EOF
Usage: $0 [report]
用法: $0 [report]
执行学生状态变更处理服务的健康检查。
Options:
选项:
report 生成详细的健康检查报告
检查内容:
@@ -352,7 +352,7 @@ case "${1:-}" in
main
;;
*)
error "Unknown option: $1"
error "未知选项: $1"
show_usage
exit 1
;;
@@ -12,7 +12,7 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CONFIG_FILE="${SCRIPT_DIR}/data.conf"
if [[ ! -f "$CONFIG_FILE" ]]; then
echo "Error: Configuration file $CONFIG_FILE not found!"
echo "错误:配置文件 $CONFIG_FILE 未找到!"
exit 1
fi
@@ -43,13 +43,13 @@ error_exit() {
exit 1
}
# Check if script is already running
# 检查脚本是否已在运行
if [[ -f "$LOCK_FILE" ]]; then
if kill -0 $(cat "$LOCK_FILE") 2>/dev/null; then
log "INFO" "Script is already running (PID: $(cat "$LOCK_FILE"))"
log "INFO" "脚本已在运行 (PID: $(cat "$LOCK_FILE"))"
exit 1
else
log "WARN" "Removing stale lock file"
log "WARN" "删除过期的锁定文件"
rm -f "$LOCK_FILE"
fi
fi
@@ -57,10 +57,10 @@ fi
# Create lock file
echo $$ > "$LOCK_FILE"
# Cleanup function
# 清理函数
cleanup() {
[[ -f "$LOCK_FILE" ]] && rm -f "$LOCK_FILE"
log "INFO" "Script execution completed"
log "INFO" "脚本执行完成"
}
trap cleanup EXIT
@@ -74,8 +74,8 @@ execute_sql() {
local exit_code=$?
if [[ $exit_code -ne 0 ]]; then
log "ERROR" "SQL execution failed: $sql"
log "ERROR" "MySQL error: $result"
log "ERROR" "SQL执行失败: $sql"
log "ERROR" "MySQL错误: $result"
return 1
fi
@@ -90,19 +90,54 @@ process_student_change() {
local change_type="$4"
local operator_id="${5:-1}"
log "INFO" "Processing student change - ID: $record_id, Student: $student_no ($student_id), Type: $change_type"
log "INFO" "正在处理学生状态变更 - ID: $record_id, 学生: $student_no ($student_id), 类型: $change_type"
# Get dormitory assignment info before transaction
local dorm_query="
SELECT GROUP_CONCAT(id), GROUP_CONCAT(bed_id)
FROM xg_dorm_bed_student
WHERE student_id = $student_id AND status = '0' AND deleted = 0;
"
local dorm_info=""
local dorm_ids=""
local bed_ids=""
# Execute dormitory query with error handling
if dorm_info=$(execute_sql "$dorm_query" 2>/dev/null); then
# Parse the result (skip header if present)
if [[ -n "$dorm_info" ]]; then
# Remove warnings, keep only data rows
dorm_info=$(echo "$dorm_info" | grep -v "mysql:" | grep -E "^[0-9]+\t" | head -n 1)
if [[ -n "$dorm_info" && "$dorm_info" != "NULL" && "$dorm_info" != "NULL\tNULL" ]]; then
# Split by tab and handle NULL values
IFS=$'\t' read -r dorm_ids_raw bed_ids_raw <<< "$dorm_info"
if [[ "$dorm_ids_raw" != "NULL" && -n "$dorm_ids_raw" ]]; then
dorm_ids="$dorm_ids_raw"
fi
if [[ "$bed_ids_raw" != "NULL" && -n "$bed_ids_raw" ]]; then
bed_ids="$bed_ids_raw"
fi
fi
fi
else
log "WARN" "查询宿舍分配信息失败,将跳过宿舍处理 - 学生ID: $student_id"
fi
log "INFO" "找到宿舍分配 - ID: ${dorm_ids:-无}, 床位: ${bed_ids:-无}"
# Start transaction
local transaction_sql="
START TRANSACTION;
-- 1. Backup original data to his tables
INSERT INTO xg_student_msg_his SELECT * FROM xg_student_msg WHERE id = $student_id;
INSERT INTO sys_user_his SELECT * FROM sys_user WHERE user_id = $student_id;
INSERT INTO xg_class_pos_msg_his SELECT * FROM xg_class_pos_msg WHERE student_id = $student_id;
INSERT INTO club_member_his SELECT * FROM club_member WHERE user_id = $student_id;
INSERT INTO stgl_member_his SELECT * FROM stgl_member WHERE user_id = $student_id;
INSERT INTO xg_dorm_bed_student_his SELECT * FROM xg_dorm_bed_student WHERE student_id = $student_id;
-- 1. Backup original data to his tables (ignore if already exists)
INSERT IGNORE INTO xg_student_msg_his SELECT * FROM xg_student_msg WHERE id = '$student_id';
INSERT IGNORE INTO sys_user_his SELECT * FROM sys_user WHERE user_id = '$student_id';
INSERT IGNORE INTO xg_class_pos_msg_his SELECT * FROM xg_class_pos_msg WHERE student_id = '$student_id';
INSERT IGNORE INTO club_member_his SELECT * FROM club_member WHERE user_id = '$student_id';
INSERT IGNORE INTO stgl_member_his SELECT * FROM stgl_member WHERE user_id = '$student_id';
INSERT IGNORE INTO xg_dorm_bed_student_his SELECT * FROM xg_dorm_bed_student WHERE student_id = '$student_id';
-- 2. Update student status
UPDATE xg_student_msg SET
@@ -112,16 +147,16 @@ process_student_change() {
WHEN '$change_type' = 'BI_YE' THEN '5'
ELSE student_status
END,
update_by = $operator_id,
update_by = '$operator_id',
update_time = NOW()
WHERE id = $student_id;
WHERE id = '$student_id';
-- 3. Disable user account
UPDATE sys_user SET
status = '1',
update_by = $operator_id,
update_by = '$operator_id',
update_time = NOW()
WHERE user_id = $student_id AND account_type = '2';
WHERE user_id = '$student_id' AND account_type = '2';
-- 4. Process class position (cadre) - only for XIU_XUE, TUI_XUE, BI_YE
UPDATE xg_class_pos_msg SET
@@ -140,98 +175,101 @@ process_student_change() {
END,
term_end_date = CURDATE(),
term_duration = TIMESTAMPDIFF(MONTH, term_start_date, CURDATE()),
update_by = $operator_id,
update_by = '$operator_id',
update_time = NOW()
WHERE student_id = $student_id AND is_current = 1;
WHERE student_id = '$student_id' AND is_current = 1;
-- 5. Process club membership - only for XIU_XUE, TUI_XUE, BI_YE
UPDATE club_member SET
member_status = '4',
update_by = $operator_id,
update_time = NOW(),
remark = CONCAT(COALESCE(remark, ''), ' | 退出原因:',
CASE
WHEN '$change_type' = 'XIU_XUE' THEN '学生休学'
WHEN '$change_type' = 'TUI_XUE' THEN '学生退学'
WHEN '$change_type' = 'BI_YE' THEN '学生毕业'
ELSE '学生状态变更'
END, ' - ', NOW())
WHERE user_id = $student_id AND member_status IN ('0', '1', '2');
update_by = '$operator_id',
update_time = NOW()
WHERE user_id = '$student_id' AND member_status IN ('0', '1', '2');
-- 6. Process volunteer membership - only for XIU_XUE, TUI_XUE, BI_YE
UPDATE stgl_member SET
status = 4,
update_by = $operator_id,
update_by = '$operator_id',
update_time = NOW()
WHERE user_id = $student_id AND status IN (0, 1, 2);
WHERE user_id = '$student_id' AND status IN (0, 1, 2);
-- 7. Process dormitory - only for TUI_XUE, BI_YE
-- First, clear dormitory info from student record
UPDATE xg_student_msg SET
dormitory_building = NULL,
dormitory_room = NULL,
ext_field1 = NULL,
update_time = NOW(),
update_by = $operator_id
WHERE id = $student_id;
update_by = '$operator_id'
WHERE id = '$student_id';"
# Add dormitory processing only if assignments exist and are valid
if [[ -n "$dorm_ids" && "$dorm_ids" != "NULL" && -n "$bed_ids" && "$bed_ids" != "NULL" ]]; then
transaction_sql+="
-- Process dormitory checkout for specific assignments
UPDATE xg_dorm_bed_student SET
status = '1',
checkout_time = NOW(),
update_time = NOW(),
update_by = $operator_id
WHERE student_id = $student_id AND status = '0';
update_by = '$operator_id'
WHERE FIND_IN_SET(id, '$dorm_ids') AND deleted = 0;
-- Update bed status for specific beds
UPDATE xg_dorm_bed SET
bed_status = '0'
WHERE id IN (
SELECT bed_id FROM xg_dorm_bed_student
WHERE student_id = $student_id AND status = '1'
);
WHERE FIND_IN_SET(id, '$bed_ids') AND deleted = 0;"
log "INFO" "将处理宿舍退宿 - 分配ID: $dorm_ids, 床位ID: $bed_ids"
else
log "INFO" "无需处理宿舍退宿(无有效宿舍分配)"
fi
transaction_sql+="
-- 8. Update change record status
UPDATE xg_student_status_change SET
process_status = 1,
update_by = $operator_id,
update_by = '$operator_id',
update_time = NOW()
WHERE id = $record_id;
WHERE id = '$record_id';
COMMIT;
"
# Execute transaction
# 执行事务
if execute_sql "$transaction_sql"; then
log "INFO" "Successfully processed student change - ID: $record_id, Student: $student_no"
log "INFO" "成功处理学生状态变更 - ID: $record_id, 学生: $student_no"
return 0
else
log "ERROR" "Failed to process student change - ID: $record_id, Student: $student_no"
log "ERROR" "处理学生状态变更失败 - ID: $record_id, 学生: $student_no"
return 1
fi
}
# Main function
main() {
log "INFO" "Starting student status change processing daemon"
log "INFO" "Processing types: XIU_XUE(休学), TUI_XUE(退学), BI_YE(毕业)"
log "INFO" "Running in continuous mode with 10-minute intervals"
log "INFO" "启动学生状态变更处理守护进程"
log "INFO" "处理类型: XIU_XUE(休学), TUI_XUE(退学), BI_YE(毕业)"
log "INFO" "以连续模式运行,间隔10分钟"
# Test database connection
log "INFO" "Testing database connection..."
# 测试数据库连接
log "INFO" "正在测试数据库连接..."
if ! execute_sql "SELECT 1;" >/dev/null 2>&1; then
error_exit "Database connection failed"
error_exit "数据库连接失败"
fi
log "INFO" "Database connection successful"
log "INFO" "数据库连接成功"
# Main processing loop
while true; do
log "INFO" "Starting new processing cycle"
log "INFO" "开始新的处理周期"
# Get unprocessed records
# 获取未处理的记录
local query_sql="
SELECT id, student_id, student_no, change_type, operator_id
FROM xg_student_status_change
WHERE process_status = 0
AND change_type IN ('XIU_XUE', 'TUI_XUE', 'BI_YE')
AND deleted = 0
AND effective_date <= NOW()
ORDER BY create_time ASC
LIMIT $BATCH_SIZE;
"
@@ -239,15 +277,17 @@ main() {
local records
records=$(execute_sql "$query_sql")
# Check if there are records to process
# 检查是否有记录需要处理
local record_count
record_count=$(echo "$records" | wc -l)
if [[ $record_count -le 1 ]]; then
log "INFO" "No unprocessed records found, waiting..."
log "INFO" "未找到未处理的记录,正在等待..."
else
# Remove header line
records=$(echo "$records" | tail -n +2)
# 移除标题行和可能的警告信息
records=$(echo "$records" | grep -v "mysql:" | grep -v "id\t" | grep -v "^$")
local processed_count=0
local failed_count=0
@@ -257,6 +297,7 @@ main() {
# Skip empty lines
[[ -z "$record_id" ]] && continue
local retry_count=0
local success=false
@@ -267,7 +308,7 @@ main() {
else
((retry_count++))
if [[ $retry_count -lt $MAX_RETRY ]]; then
log "WARN" "Processing failed, retrying in $RETRY_DELAY seconds... (attempt $retry_count/$MAX_RETRY)"
log "WARN" "处理失败,$RETRY_DELAY 秒后重试... (尝试 $retry_count/$MAX_RETRY)"
sleep $RETRY_DELAY
fi
fi
@@ -275,16 +316,16 @@ main() {
if [[ $success == false ]]; then
((failed_count++))
log "ERROR" "Failed to process record after $MAX_RETRY attempts - ID: $record_id"
log "ERROR" "$MAX_RETRY 次尝试后仍处理失败 - ID: $record_id"
fi
done <<< "$records"
log "INFO" "Processing cycle completed - Processed: $processed_count, Failed: $failed_count"
log "INFO" "处理周期完成 - 已处理: $processed_count, 失败: $failed_count"
fi
# Wait for configured interval before next cycle
log "INFO" "Waiting $CYCLE_INTERVAL seconds before next processing cycle..."
# 等待配置的间隔时间后开始下一个周期
log "INFO" "等待 $CYCLE_INTERVAL 秒后开始下一个处理周期..."
sleep $CYCLE_INTERVAL
done
}
@@ -12,7 +12,7 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CONFIG_FILE="${SCRIPT_DIR}/data.conf"
if [[ ! -f "$CONFIG_FILE" ]]; then
echo "Error: Configuration file $CONFIG_FILE not found!"
echo "错误:配置文件 $CONFIG_FILE 未找到!"
exit 1
fi
@@ -43,13 +43,13 @@ error_exit() {
exit 1
}
# Check if script is already running
# 检查脚本是否已在运行
if [[ -f "$LOCK_FILE" ]]; then
if kill -0 $(cat "$LOCK_FILE") 2>/dev/null; then
log "INFO" "Script is already running (PID: $(cat "$LOCK_FILE"))"
log "INFO" "脚本已在运行 (PID: $(cat "$LOCK_FILE"))"
exit 1
else
log "WARN" "Removing stale lock file"
log "WARN" "删除过期的锁定文件"
rm -f "$LOCK_FILE"
fi
fi
@@ -57,10 +57,10 @@ fi
# Create lock file
echo $$ > "$LOCK_FILE"
# Cleanup function
# 清理函数
cleanup() {
[[ -f "$LOCK_FILE" ]] && rm -f "$LOCK_FILE"
log "INFO" "Script execution completed"
log "INFO" "脚本执行完成"
}
trap cleanup EXIT
@@ -74,8 +74,8 @@ execute_sql() {
local exit_code=$?
if [[ $exit_code -ne 0 ]]; then
log "ERROR" "SQL execution failed: $sql"
log "ERROR" "MySQL error: $result"
log "ERROR" "SQL执行失败: $sql"
log "ERROR" "MySQL错误: $result"
return 1
fi
@@ -90,74 +90,75 @@ process_student_resume() {
local change_type="$4"
local operator_id="${5:-1}"
log "INFO" "Processing student resume - ID: $record_id, Student: $student_no ($student_id), Type: $change_type"
log "INFO" "正在处理学生复学 - ID: $record_id, 学生: $student_no ($student_id), 类型: $change_type"
# Start transaction
local transaction_sql="
START TRANSACTION;
-- 1. Backup original data to his tables
INSERT INTO xg_student_msg_his SELECT * FROM xg_student_msg WHERE id = $student_id;
INSERT INTO sys_user_his SELECT * FROM sys_user WHERE user_id = $student_id;
-- 1. Backup original data to his tables (ignore if already exists)
INSERT IGNORE INTO xg_student_msg_his SELECT * FROM xg_student_msg WHERE id = '$student_id';
INSERT IGNORE INTO sys_user_his SELECT * FROM sys_user WHERE user_id = '$student_id';
-- 2. Update student status to resume (复学)
UPDATE xg_student_msg SET
student_status = '3',
update_by = $operator_id,
student_status = '1',
update_by = '$operator_id',
update_time = NOW()
WHERE id = $student_id;
WHERE id = '$student_id';
-- 3. Enable user account
UPDATE sys_user SET
status = '0',
update_by = $operator_id,
update_by = '$operator_id',
update_time = NOW()
WHERE user_id = $student_id AND account_type = '2';
WHERE user_id = '$student_id' AND account_type = '2';
-- 4. Update change record status
UPDATE xg_student_status_change SET
process_status = 1,
update_by = $operator_id,
update_by = '$operator_id',
update_time = NOW()
WHERE id = $record_id;
WHERE id = '$record_id';
COMMIT;
"
# Execute transaction
# 执行事务
if execute_sql "$transaction_sql"; then
log "INFO" "Successfully processed student resume - ID: $record_id, Student: $student_no"
log "INFO" "成功处理学生复学 - ID: $record_id, 学生: $student_no"
return 0
else
log "ERROR" "Failed to process student resume - ID: $record_id, Student: $student_no"
log "ERROR" "处理学生复学失败 - ID: $record_id, 学生: $student_no"
return 1
fi
}
# Main function
main() {
log "INFO" "Starting student resume processing daemon"
log "INFO" "Processing type: FU_XUE(复学)"
log "INFO" "Running in continuous mode with 10-minute intervals"
log "INFO" "启动学生复学处理守护进程"
log "INFO" "处理类型: FU_XUE(复学)"
log "INFO" "以连续模式运行,间隔10分钟"
# Test database connection
log "INFO" "Testing database connection..."
# 测试数据库连接
log "INFO" "正在测试数据库连接..."
if ! execute_sql "SELECT 1;" >/dev/null 2>&1; then
error_exit "Database connection failed"
error_exit "数据库连接失败"
fi
log "INFO" "Database connection successful"
log "INFO" "数据库连接成功"
# Main processing loop
while true; do
log "INFO" "Starting new resume processing cycle"
log "INFO" "开始新的复学处理周期"
# Get unprocessed records
# 获取未处理的记录
local query_sql="
SELECT id, student_id, student_no, change_type, operator_id
FROM xg_student_status_change
WHERE process_status = 0
AND change_type = 'FU_XUE'
AND deleted = 0
AND effective_date <= NOW()
ORDER BY create_time ASC
LIMIT $BATCH_SIZE;
"
@@ -165,15 +166,15 @@ main() {
local records
records=$(execute_sql "$query_sql")
# Check if there are records to process
# 检查是否有记录需要处理
local record_count
record_count=$(echo "$records" | wc -l)
if [[ $record_count -le 1 ]]; then
log "INFO" "No unprocessed resume records found, waiting..."
log "INFO" "未找到未处理的复学记录,正在等待..."
else
# Remove header line
records=$(echo "$records" | tail -n +2)
# 移除标题行和可能的警告信息
records=$(echo "$records" | grep -v "mysql:" | grep -v "id\t" | grep -v "^$")
local processed_count=0
local failed_count=0
@@ -193,7 +194,7 @@ main() {
else
((retry_count++))
if [[ $retry_count -lt $MAX_RETRY ]]; then
log "WARN" "Processing failed, retrying in $RETRY_DELAY seconds... (attempt $retry_count/$MAX_RETRY)"
log "WARN" "处理失败,$RETRY_DELAY 秒后重试... (尝试 $retry_count/$MAX_RETRY)"
sleep $RETRY_DELAY
fi
fi
@@ -201,16 +202,16 @@ main() {
if [[ $success == false ]]; then
((failed_count++))
log "ERROR" "Failed to process record after $MAX_RETRY attempts - ID: $record_id"
log "ERROR" "$MAX_RETRY 次尝试后仍处理失败 - ID: $record_id"
fi
done <<< "$records"
log "INFO" "Resume processing cycle completed - Processed: $processed_count, Failed: $failed_count"
log "INFO" "复学处理周期完成 - 已处理: $processed_count, 失败: $failed_count"
fi
# Wait for configured interval before next cycle
log "INFO" "Waiting $CYCLE_INTERVAL seconds before next resume processing cycle..."
# 等待配置的间隔时间后开始下一个周期
log "INFO" "等待 $CYCLE_INTERVAL 秒后开始下一个复学处理周期..."
sleep $CYCLE_INTERVAL
done
}
@@ -1,123 +0,0 @@
#!/bin/bash
# ===========================================
# 学生状态变更处理服务启动脚本
# 用于启动所有相关的处理服务
# ===========================================
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CONTROL_SCRIPT="${SCRIPT_DIR}/control_scripts.sh"
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
log() {
local level="$1"
local message="$2"
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo -e "${timestamp} [$level] $message"
}
success() {
log "${GREEN}SUCCESS${NC}" "$1"
}
error() {
log "${RED}ERROR${NC}" "$1"
}
warning() {
log "${YELLOW}WARNING${NC}" "$1"
}
# Check if control script exists and is executable
if [[ ! -x "$CONTROL_SCRIPT" ]]; then
error "Control script not found or not executable: $CONTROL_SCRIPT"
exit 1
fi
# Function to start a service
start_service() {
local service_name="$1"
local service_desc="$2"
log "INFO" "Starting $service_desc..."
if "$CONTROL_SCRIPT" start "$service_name"; then
success "$service_desc started successfully"
return 0
else
error "Failed to start $service_desc"
return 1
fi
}
# Function to check service status
check_service() {
local service_name="$1"
local service_desc="$2"
log "INFO" "Checking $service_desc status..."
if "$CONTROL_SCRIPT" status "$service_name" | grep -q "is running"; then
success "$service_desc is running"
return 0
else
warning "$service_desc is not running"
return 1
fi
}
# Main function
main() {
log "INFO" "=== 学生状态变更处理服务启动脚本 ==="
log "INFO" "Starting all student status change processing services..."
local failed_services=()
# Start changes processing service
if ! start_service "changes" "学生状态变更处理服务 (休学/退学/毕业)"; then
failed_services+=("changes")
fi
# Start resume processing service
if ! start_service "resume" "学生复学处理服务"; then
failed_services+=("resume")
fi
# Wait a moment for services to fully start
sleep 3
log "INFO" "=== 服务状态检查 ==="
# Check all services status
check_service "changes" "学生状态变更处理服务 (休学/退学/毕业)"
check_service "resume" "学生复学处理服务"
# Summary
log "INFO" "=== 启动完成 ==="
if [[ ${#failed_services[@]} -eq 0 ]]; then
success "所有服务启动成功"
echo ""
echo "服务控制命令:"
echo " 查看状态: $CONTROL_SCRIPT statusall"
echo " 停止服务: $CONTROL_SCRIPT stopall"
echo " 重启服务: $CONTROL_SCRIPT restart changes|resume"
echo ""
echo "日志文件位置: /var/log/student_status_change/"
else
error "以下服务启动失败: ${failed_services[*]}"
echo ""
echo "故障排除:"
echo "1. 检查数据库配置: cat data.conf"
echo "2. 查看日志文件: tail -f /var/log/student_status_change/*.log"
echo "3. 检查数据库连接: mysql -h\$DB_HOST -P\$DB_PORT -u\$DB_USER -p\$DB_PASS \$DB_NAME -e 'SELECT 1;'"
exit 1
fi
}
# Run main function
main "$@"
@@ -1,177 +0,0 @@
#!/bin/bash
# ===========================================
# 学生状态变更处理服务停止脚本
# 用于停止所有相关的处理服务
# ===========================================
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CONTROL_SCRIPT="${SCRIPT_DIR}/control_scripts.sh"
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
log() {
local level="$1"
local message="$2"
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo -e "${timestamp} [$level] $message"
}
success() {
log "${GREEN}SUCCESS${NC}" "$1"
}
error() {
log "${RED}ERROR${NC}" "$1"
}
warning() {
log "${YELLOW}WARNING${NC}" "$1"
}
# Check if control script exists and is executable
if [[ ! -x "$CONTROL_SCRIPT" ]]; then
error "Control script not found or not executable: $CONTROL_SCRIPT"
exit 1
fi
# Function to stop a service
stop_service() {
local service_name="$1"
local service_desc="$2"
log "INFO" "Stopping $service_desc..."
if "$CONTROL_SCRIPT" stop "$service_name"; then
success "$service_desc stopped successfully"
return 0
else
warning "$service_desc may not have been running"
return 0 # Not treating this as a hard error
fi
}
# Function to check service status after stop
check_service_stopped() {
local service_name="$1"
local service_desc="$2"
log "INFO" "Verifying $service_desc has stopped..."
if "$CONTROL_SCRIPT" status "$service_name" | grep -q "is not running"; then
success "$service_desc confirmed stopped"
return 0
else
warning "$service_desc may still be running"
return 1
fi
}
# Function to force kill if needed
force_kill_service() {
local service_name="$1"
local script_pattern="$2"
local service_desc="$3"
log "INFO" "Force killing $service_desc..."
if pkill -f "$script_pattern"; then
sleep 2
success "$service_desc force killed"
return 0
else
warning "No processes found for $service_desc"
return 0
fi
}
# Main function
main() {
local force_kill="${1:-false}"
log "INFO" "=== 学生状态变更处理服务停止脚本 ==="
if [[ "$force_kill" == "force" ]]; then
log "INFO" "Performing force stop of all services..."
# Force kill all related processes
force_kill_service "changes" "process_student_changes.sh" "学生状态变更处理服务"
force_kill_service "resume" "process_student_resume.sh" "学生复学处理服务"
# Clean up lock and PID files
log "INFO" "Cleaning up lock and PID files..."
rm -f "${SCRIPT_DIR}"/*.lock
rm -f "${SCRIPT_DIR}/pids"/*.pid
success "Force stop completed"
exit 0
fi
log "INFO" "Stopping all student status change processing services..."
# Stop services gracefully
stop_service "changes" "学生状态变更处理服务 (休学/退学/毕业)"
stop_service "resume" "学生复学处理服务"
# Wait a moment for services to fully stop
sleep 3
log "INFO" "=== 服务状态验证 ==="
# Check all services are stopped
check_service_stopped "changes" "学生状态变更处理服务 (休学/退学/毕业)"
check_service_stopped "resume" "学生复学处理服务"
# Final cleanup
log "INFO" "Performing final cleanup..."
rm -f "${SCRIPT_DIR}"/*.lock 2>/dev/null || true
rm -f "${SCRIPT_DIR}/pids"/*.pid 2>/dev/null || true
log "INFO" "=== 停止完成 ==="
success "所有服务已停止"
echo ""
echo "如果服务未能正常停止,请使用强制停止:"
echo " $0 force"
echo ""
echo "重新启动服务:"
echo " ./start_services.sh"
}
# Show usage
show_usage() {
cat << EOF
Usage: $0 [force]
停止所有学生状态变更处理服务。
Options:
force 强制杀死所有相关进程并清理锁文件
Examples:
$0 # 正常停止所有服务
$0 force # 强制停止所有服务
EOF
}
# Parse arguments
case "${1:-}" in
force)
main "force"
;;
-h|--help)
show_usage
;;
"")
main
;;
*)
error "Unknown option: $1"
show_usage
exit 1
;;
esac
@@ -1,104 +0,0 @@
-- ===========================================
-- 学生状态变更处理测试数据
-- 用于测试自动化处理脚本
-- ===========================================
-- 注意:请根据实际情况修改学生ID和学号
-- 插入测试数据到 xg_student_status_change 表
-- 状态:0=未处理,1=已处理
-- 测试休学处理
INSERT INTO xg_student_status_change (
id, student_id, student_no, change_type, apply_date,
effective_date, reason, operator_id, source, process_status
) VALUES (
10001, 10001, '20240001', 'XIU_XUE', NOW(),
CURDATE(), '学生申请休学', 1, 'manual', 0
);
-- 测试退学处理
INSERT INTO xg_student_status_change (
id, student_id, student_no, change_type, apply_date,
effective_date, reason, operator_id, source, process_status
) VALUES (
10002, 10002, '20240002', 'TUI_XUE', NOW(),
CURDATE(), '学生申请退学', 1, 'manual', 0
);
-- 测试毕业处理
INSERT INTO xg_student_status_change (
id, student_id, student_no, change_type, apply_date,
effective_date, reason, operator_id, source, process_status
) VALUES (
10003, 10003, '20240003', 'BI_YE', NOW(),
CURDATE(), '学生毕业处理', 1, 'manual', 0
);
-- 测试复学处理
INSERT INTO xg_student_status_change (
id, student_id, student_no, change_type, apply_date,
effective_date, reason, operator_id, source, process_status
) VALUES (
10004, 10004, '20240004', 'FU_XUE', NOW(),
CURDATE(), '学生申请复学', 1, 'manual', 0
);
-- 批量测试数据(休学)
INSERT INTO xg_student_status_change (
id, student_id, student_no, change_type, apply_date,
effective_date, reason, operator_id, source, process_status
) SELECT
20000 + (@row_number := @row_number + 1),
20000 + @row_number,
CONCAT('2024', LPAD(@row_number, 4, '0')),
'XIU_XUE',
NOW(),
CURDATE(),
CONCAT('批量测试休学-', @row_number),
1,
'manual',
0
FROM (SELECT @row_number := 0) r
CROSS JOIN (SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5) nums;
-- 批量测试数据(退学)
INSERT INTO xg_student_status_change (
id, student_id, student_no, change_type, apply_date,
effective_date, reason, operator_id, source, process_status
) SELECT
30000 + (@row_number2 := @row_number2 + 1),
30000 + @row_number2,
CONCAT('2024', LPAD(@row_number2 + 10, 4, '0')),
'TUI_XUE',
NOW(),
CURDATE(),
CONCAT('批量测试退学-', @row_number2),
1,
'manual',
0
FROM (SELECT @row_number2 := 0) r2
CROSS JOIN (SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5) nums2;
-- 查询测试数据
SELECT
'测试数据插入完成' as status,
COUNT(*) as total_test_records
FROM xg_student_status_change
WHERE id >= 10001;
-- 按类型统计
SELECT
change_type,
COUNT(*) as count
FROM xg_student_status_change
WHERE id >= 10001
GROUP BY change_type
ORDER BY change_type;
-- ===========================================
-- 清理测试数据的SQL(执行前请确认)
-- ===========================================
-- DELETE FROM xg_student_status_change WHERE id >= 10001;
-- COMMIT;