process_student_class_change.sh 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. #!/bin/bash
  2. # ===========================================
  3. # 学生换班级处理脚本
  4. # 处理类型: CLASS_CHANGE(换班级)
  5. # ===========================================
  6. set -euo pipefail
  7. # Load configuration
  8. SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
  9. CONFIG_FILE="${SCRIPT_DIR}/data.conf"
  10. if [[ ! -f "$CONFIG_FILE" ]]; then
  11. echo "错误:配置文件 $CONFIG_FILE 未找到!"
  12. exit 1
  13. fi
  14. source "$CONFIG_FILE"
  15. # Create log directory
  16. mkdir -p "$LOG_DIR"
  17. # Log file
  18. LOG_FILE="${LOG_DIR}/process_student_class_change_$(date +%Y%m%d_%H%M%S).log"
  19. # Lock file to prevent multiple instances
  20. LOCK_FILE="${SCRIPT_DIR}/process_student_class_change.lock"
  21. # Functions
  22. log() {
  23. local level="$1"
  24. local message="$2"
  25. local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
  26. echo "[$timestamp] [$level] $message" | tee -a "$LOG_FILE"
  27. }
  28. error_exit() {
  29. local message="$1"
  30. log "ERROR" "$message"
  31. # Remove lock file
  32. [[ -f "$LOCK_FILE" ]] && rm -f "$LOCK_FILE"
  33. exit 1
  34. }
  35. # 检查脚本是否已在运行
  36. if [[ -f "$LOCK_FILE" ]]; then
  37. if kill -0 $(cat "$LOCK_FILE") 2>/dev/null; then
  38. log "INFO" "脚本已在运行 (PID: $(cat "$LOCK_FILE"))"
  39. exit 1
  40. else
  41. log "WARN" "删除过期的锁定文件"
  42. rm -f "$LOCK_FILE"
  43. fi
  44. fi
  45. # Create lock file
  46. echo $$ > "$LOCK_FILE"
  47. # 清理函数
  48. cleanup() {
  49. [[ -f "$LOCK_FILE" ]] && rm -f "$LOCK_FILE"
  50. log "INFO" "脚本执行完成"
  51. }
  52. trap cleanup EXIT
  53. # Database functions
  54. execute_sql() {
  55. local sql="$1"
  56. local result
  57. result=$(mysql -h"$DB_HOST" -P"$DB_PORT" -u"$DB_USER" -p"$DB_PASS" "$DB_NAME" -e "$sql" 2>&1)
  58. local exit_code=$?
  59. if [[ $exit_code -ne 0 ]]; then
  60. log "ERROR" "SQL执行失败: $sql"
  61. log "ERROR" "MySQL错误: $result"
  62. return 1
  63. fi
  64. echo "$result"
  65. }
  66. # Process student class change
  67. process_student_class_change() {
  68. local record_id="$1"
  69. local student_id="$2"
  70. local student_no="$3"
  71. local new_class_id="$4"
  72. local operator_id="${5:-1}"
  73. log "INFO" "Processing student class change - ID: $record_id, Student: $student_no ($student_id), New Class ID: $new_class_id"
  74. # Get new class information
  75. local class_info_sql="
  76. SELECT
  77. c.college_id, c.college_name, c.major_id, c.major_name,
  78. c.class_code, c.class_name, c.class_type, c.grade,
  79. c.training_type, c.counselor_id, c.counselor_name,
  80. c.class_teacher_id, c.class_teacher_name, c.school_system
  81. FROM xg_class_msg c
  82. WHERE c.id = $new_class_id AND c.deleted = 0
  83. "
  84. local class_info
  85. class_info=$(execute_sql "$class_info_sql")
  86. if [[ -z "$class_info" || "$class_info" == *"Empty set"* ]]; then
  87. log "ERROR" "获取班级ID为 $new_class_id 的班级信息失败"
  88. return 1
  89. fi
  90. # Parse class information (skip header and separator lines, get data row)
  91. local class_data
  92. class_data=$(echo "$class_info" | grep -E '^[0-9]' | head -1)
  93. if [[ -z "$class_data" ]]; then
  94. log "ERROR" "解析班级信息失败,无法获取数据行"
  95. return 1
  96. fi
  97. # Use cut to extract specific fields reliably
  98. local college_id=$(echo "$class_data" | cut -f1)
  99. local college_name=$(echo "$class_data" | cut -f2)
  100. local major_id=$(echo "$class_data" | cut -f3)
  101. local major_name=$(echo "$class_data" | cut -f4)
  102. local class_code=$(echo "$class_data" | cut -f5)
  103. local class_name=$(echo "$class_data" | cut -f6)
  104. local class_type=$(echo "$class_data" | cut -f7)
  105. local grade=$(echo "$class_data" | cut -f8)
  106. local training_type=$(echo "$class_data" | cut -f9)
  107. local counselor_id=$(echo "$class_data" | cut -f10)
  108. local counselor_name=$(echo "$class_data" | cut -f11)
  109. local class_teacher_id=$(echo "$class_data" | cut -f12)
  110. local class_teacher_name=$(echo "$class_data" | cut -f13)
  111. local school_system=$(echo "$class_data" | cut -f14)
  112. # Handle NULL values for IDs
  113. local counselor_id_value class_teacher_id_value
  114. if [[ "$counselor_id" == "NULL" || -z "$counselor_id" ]]; then
  115. counselor_id_value="NULL"
  116. else
  117. counselor_id_value="'$counselor_id'"
  118. fi
  119. if [[ "$class_teacher_id" == "NULL" || -z "$class_teacher_id" ]]; then
  120. class_teacher_id_value="NULL"
  121. else
  122. class_teacher_id_value="'$class_teacher_id'"
  123. fi
  124. # Start transaction
  125. local transaction_sql="
  126. START TRANSACTION;
  127. -- 1. Backup original data to his tables (ignore if already exists)
  128. INSERT IGNORE INTO xg_student_msg_his SELECT * FROM xg_student_msg WHERE id = $student_id;
  129. INSERT IGNORE INTO xg_class_pos_msg_his SELECT * FROM xg_class_pos_msg WHERE student_id = $student_id;
  130. -- 2. Terminate cadre positions (干部信息终止)
  131. UPDATE xg_class_pos_msg SET
  132. is_current = 0,
  133. cadre_status = '4', -- 免职
  134. dismissal_date = CURDATE(),
  135. dismissal_reason = '学生换班级',
  136. term_end_date = CURDATE(),
  137. term_duration = TIMESTAMPDIFF(MONTH, term_start_date, CURDATE()),
  138. update_by = $operator_id,
  139. update_time = NOW()
  140. WHERE student_id = $student_id AND is_current = 1;
  141. -- 3. Update student information with new class data
  142. UPDATE xg_student_msg SET
  143. -- 基础班级信息
  144. college_id = '$college_id',
  145. college_name = '$college_name',
  146. major_id = '$major_id',
  147. major_name = '$major_name',
  148. class_id = '$new_class_id',
  149. class_name = '$class_name',
  150. grade = '$grade',
  151. -- 预留字段从班级表关联获取的数据 (7182-7195行)
  152. school_system = '$school_system',
  153. academic_level = '$class_type', -- class_type 对应 academic_level
  154. training_type = '$training_type',
  155. counselor_id = $counselor_id_value,
  156. counselor_name = '$counselor_name',
  157. class_teacher_id = $class_teacher_id_value,
  158. class_teacher_name = '$class_teacher_name',
  159. -- 更新信息
  160. update_by = $operator_id,
  161. update_time = NOW()
  162. WHERE id = $student_id;
  163. -- 4. Update class change record status
  164. UPDATE xg_student_class_change SET
  165. status = '1', -- 已处理
  166. update_by = $operator_id,
  167. update_time = NOW()
  168. WHERE id = $record_id;
  169. COMMIT;
  170. "
  171. # 执行事务
  172. if execute_sql "$transaction_sql"; then
  173. log "INFO" "成功处理学生换班级 - ID: $record_id, 学生: $student_no"
  174. return 0
  175. else
  176. log "ERROR" "处理学生换班级失败 - ID: $record_id, 学生: $student_no"
  177. return 1
  178. fi
  179. }
  180. # Main function
  181. main() {
  182. log "INFO" "启动学生换班级处理守护进程"
  183. log "INFO" "处理类型: CLASS_CHANGE(换班级)"
  184. log "INFO" "以连续模式运行,间隔10分钟"
  185. # 测试数据库连接
  186. log "INFO" "正在测试数据库连接..."
  187. if ! execute_sql "SELECT 1;" >/dev/null 2>&1; then
  188. error_exit "数据库连接失败"
  189. fi
  190. log "INFO" "数据库连接成功"
  191. # Main processing loop
  192. while true; do
  193. log "INFO" "开始新的处理周期"
  194. # 获取未处理的记录
  195. local query_sql="
  196. SELECT id, student_id, student_no, new_class_id, update_by
  197. FROM xg_student_class_change
  198. WHERE status = '0'
  199. AND deleted = 0
  200. AND effective_time <= NOW()
  201. ORDER BY create_time ASC
  202. LIMIT $BATCH_SIZE;
  203. "
  204. local records
  205. records=$(execute_sql "$query_sql")
  206. # 检查是否有记录需要处理
  207. # 过滤掉MySQL的边框和表头,只保留数据行
  208. local data_records
  209. data_records=$(echo "$records" | grep -E '^[0-9]' || true)
  210. if [[ -z "$data_records" ]]; then
  211. log "INFO" "未找到未处理的记录,正在等待..."
  212. else
  213. log "INFO" "找到待处理记录,开始处理..."
  214. records="$data_records"
  215. local processed_count=0
  216. local failed_count=0
  217. # Process each record
  218. while IFS=$'\t' read -r record_id student_id student_no new_class_id operator_id; do
  219. # Skip empty lines
  220. [[ -z "$record_id" ]] && continue
  221. local retry_count=0
  222. local success=false
  223. while [[ $retry_count -lt $MAX_RETRY && $success == false ]]; do
  224. if process_student_class_change "$record_id" "$student_id" "$student_no" "$new_class_id" "$operator_id"; then
  225. success=true
  226. ((processed_count++))
  227. else
  228. ((retry_count++))
  229. if [[ $retry_count -lt $MAX_RETRY ]]; then
  230. log "WARN" "处理失败,$RETRY_DELAY 秒后重试... (尝试 $retry_count/$MAX_RETRY)"
  231. sleep $RETRY_DELAY
  232. fi
  233. fi
  234. done
  235. if [[ $success == false ]]; then
  236. ((failed_count++))
  237. log "ERROR" "$MAX_RETRY 次尝试后仍处理失败 - ID: $record_id"
  238. fi
  239. done <<< "$records"
  240. log "INFO" "处理周期完成 - 已处理: $processed_count, 失败: $failed_count"
  241. fi
  242. # 等待配置的间隔时间后开始下一个周期
  243. log "INFO" "等待 $CYCLE_INTERVAL 秒后开始下一个处理周期..."
  244. sleep $CYCLE_INTERVAL
  245. done
  246. }
  247. # Run main function
  248. main "$@"