Sfoglia il codice sorgente

add: 山东华宇工学院教务适配 (#237)

* 添加华宇工学院

* 添加山东华宇工索引

* 修改华宇工周数识别逻辑,添加开学日期的输入

* 修改为api获取方式,学期已自动计算,无需手填

* 修改了学校id,使其更规范
Noxiouskoala1 2 settimane fa
parent
commit
ebb3c58da6
3 ha cambiato i file con 434 aggiunte e 0 eliminazioni
  1. 6 0
      index/root_index.yaml
  2. 8 0
      resources/HUAYU/adapters.yaml
  3. 420 0
      resources/HUAYU/huayu.js

+ 6 - 0
index/root_index.yaml

@@ -391,3 +391,9 @@ schools:
     name: "中国石油大学(北京)克拉玛依校区"
     initial: "Z"
     resource_folder: "CUPK"
+  
+  - id: "HUAYU"
+    name: "山东华宇工学院"
+    initial: "S"
+    resource_folder: "HUAYU"
+

+ 8 - 0
resources/HUAYU/adapters.yaml

@@ -0,0 +1,8 @@
+adapters:
+  - adapter_id: "HUAYU_01"
+    adapter_name: "山东华宇工学院正方教务"
+    category: "BACHELOR_AND_ASSOCIATE"
+    asset_js_path: "huayu.js"
+    import_url: "http://jwgl.huayu.edu.cn/jwglxt/xtgl/login_slogin.html"
+    maintainer: "Noxiouskoala1"
+    description: "适配山东华宇工学院正方教务系统"

+ 420 - 0
resources/HUAYU/huayu.js

@@ -0,0 +1,420 @@
+// 山东华宇工学院 拾光课程表适配脚本
+// 基于正方教务系统API适配
+
+/**
+ * 解析周次字符串
+ */
+function parseWeeks(weekStr) {
+	var weeks = [];
+	if (!weekStr) return weeks;
+	
+	var weekSets = weekStr.split(',');
+	for (var i = 0; i < weekSets.length; i++) {
+		var set = weekSets[i].trim();
+		var isSingle = set.indexOf('(单)') !== -1;
+		var isDouble = set.indexOf('(双)') !== -1;
+		
+		set = set.split('(单)').join('');
+		set = set.split('(双)').join('');
+		set = set.trim();
+		
+		var dashIdx = set.indexOf('-');
+		var start = 0;
+		var end = 0;
+		var processed = false;
+		
+		if (dashIdx !== -1 && set.indexOf('周') !== -1) {
+			start = parseInt(set.substring(0, dashIdx));
+			var endPart = set.substring(dashIdx + 1);
+			endPart = endPart.split('周').join('');
+			end = parseInt(endPart);
+			processed = true;
+		} else {
+			var weekNum = parseInt(set);
+			if (!isNaN(weekNum)) {
+				start = end = weekNum;
+				processed = true;
+			}
+		}
+		
+		if (processed) {
+			for (var w = start; w <= end; w++) {
+				if (isSingle && w % 2 === 0) continue;
+				if (isDouble && w % 2 !== 0) continue;
+				weeks.push(w);
+			}
+		}
+	}
+	
+	var uniqueWeeks = [];
+	for (var j = 0; j < weeks.length; j++) {
+		if (uniqueWeeks.indexOf(weeks[j]) === -1) {
+			uniqueWeeks.push(weeks[j]);
+		}
+	}
+	uniqueWeeks.sort(function(a, b) { return a - b; });
+	return uniqueWeeks;
+}
+
+/**
+ * 解析API返回的JSON数据
+ */
+function parseJsonData(jsonData) {
+	console.log('JS: parseJsonData 正在解析JSON数据...');
+	
+	if (!jsonData || !Array.isArray(jsonData.kbList)) {
+		console.warn('JS: JSON数据结构错误或缺少kbList字段');
+		return [];
+	}
+	
+	var rawCourseList = jsonData.kbList;
+	var finalCourseList = [];
+	
+	for (var i = 0; i < rawCourseList.length; i++) {
+		var rawCourse = rawCourseList[i];
+		
+		if (!rawCourse.kcmc || !rawCourse.xm || !rawCourse.cdmc || 
+			!rawCourse.xqj || !rawCourse.jcs || !rawCourse.zcd) {
+			continue;
+		}
+		
+		var weeksArray = parseWeeks(rawCourse.zcd);
+		if (weeksArray.length === 0) {
+			continue;
+		}
+		
+		var sectionParts = rawCourse.jcs.split('-');
+		var startSection = parseInt(sectionParts[0]);
+		var endSection = parseInt(sectionParts[sectionParts.length - 1]);
+		var day = parseInt(rawCourse.xqj);
+		
+		if (isNaN(day) || isNaN(startSection) || isNaN(endSection) || 
+			day < 1 || day > 7 || startSection > endSection) {
+			continue;
+		}
+		
+		var courseName = rawCourse.kcmc.trim();
+		courseName = courseName.split('★').join('');
+		courseName = courseName.split('●').join('');
+		courseName = courseName.split('◆').join('');
+		courseName = courseName.split('◇').join('');
+		courseName = courseName.split('○').join('');
+		
+		finalCourseList.push({
+			name: courseName,
+			teacher: rawCourse.xm.trim(),
+			position: rawCourse.cdmc.trim(),
+			day: day,
+			startSection: startSection,
+			endSection: endSection,
+			weeks: weeksArray
+		});
+	}
+	
+	finalCourseList.sort(function(a, b) {
+		if (a.day !== b.day) return a.day - b.day;
+		if (a.startSection !== b.startSection) return a.startSection - b.startSection;
+		return a.name.localeCompare(b.name);
+	});
+	
+	console.log('JS: JSON数据解析完成,共找到 ' + finalCourseList.length + ' 门课程');
+	return finalCourseList;
+}
+
+/**
+ * 检查是否在登录页面
+ */
+function isLoginPage() {
+	var url = window.location.href;
+	var loginUrls = [
+		'login_slogin.html',
+		'login_login.html'
+	];
+	for (var i = 0; i < loginUrls.length; i++) {
+		if (url.indexOf(loginUrls[i]) !== -1) return true;
+	}
+	return false;
+}
+
+/**
+ * 自动获取当前学年
+ * 优先从页面下拉框获取,失败则根据日期计算
+ */
+function getCurrentAcademicYear() {
+	// 尝试从页面获取
+	var xnmSelect = document.getElementById('xnm');
+	if (xnmSelect && xnmSelect.value) {
+		var year = xnmSelect.value;
+		console.log('JS: 从页面获取到学年: ' + year);
+		return year;
+	}
+	
+	// 根据当前日期计算学年
+	var now = new Date();
+	var year = now.getFullYear();
+	var month = now.getMonth() + 1;
+	
+	// 9月及以后属于新学年
+	if (month >= 9) {
+		console.log('JS: 根据日期计算学年: ' + year);
+		return year.toString();
+	} else {
+		console.log('JS: 根据日期计算学年: ' + (year - 1));
+		return (year - 1).toString();
+	}
+}
+
+/**
+ * 自动获取当前学期
+ * 优先从页面下拉框获取,失败则根据日期计算
+ */
+function getCurrentSemesterIndex() {
+	// 尝试从页面获取
+	var xqmSelect = document.getElementById('xqm');
+	if (xqmSelect && xqmSelect.value) {
+		var xqm = xqmSelect.value;
+		// xqm: 3=第一学期, 12=第二学期
+		if (xqm === '3') {
+			console.log('JS: 从页面获取到学期: 第一学期');
+			return 0;
+		} else if (xqm === '12') {
+			console.log('JS: 从页面获取到学期: 第二学期');
+			return 1;
+		}
+	}
+	
+	// 根据日期计算:9月-次年2月为第一学期,3月-7月为第二学期
+	var month = new Date().getMonth() + 1;
+	if (month >= 3 && month <= 7) {
+		console.log('JS: 根据日期计算学期: 第二学期');
+		return 1;
+	} else {
+		console.log('JS: 根据日期计算学期: 第一学期');
+		return 0;
+	}
+}
+
+function getSemesterCode(semesterIndex) {
+	return semesterIndex === 0 ? '3' : '12';
+}
+
+function getSemesterName(semesterIndex) {
+	return semesterIndex === 0 ? '第一学期' : '第二学期';
+}
+
+function validateDateInput(input) {
+	console.log('JS: validateDateInput 被调用,输入: ' + input);
+	if (/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/.test(input)) {
+		console.log('JS: validateDateInput 验证通过');
+		return false;
+	} else {
+		console.log('JS: validateDateInput 验证失败');
+		return '请输入正确格式的开学日期(如2025-09-01)!';
+	}
+}
+
+async function promptUserToStart() {
+	console.log('JS: 流程开始:显示公告');
+	return await window.AndroidBridgePromise.showAlert(
+		'教务系统课表导入',
+		'导入前请确保您已在浏览器中成功登录教务系统。',
+		'好的,开始导入'
+	);
+}
+
+async function confirmAcademicYear(year, semesterName) {
+	console.log('JS: 显示学年学期确认弹窗');
+	return await window.AndroidBridgePromise.showAlert(
+		'确认学年学期',
+		'检测到当前为 ' + year + '-' + (parseInt(year) + 1) + ' 学年' + semesterName + ',确认导入该学期课程吗?',
+		'确认导入'
+	);
+}
+
+async function getSemesterStartDate() {
+	console.log('JS: 提示用户输入开学日期');
+	return await window.AndroidBridgePromise.showPrompt(
+		'选择开学日期',
+		'请输入本学期开学日期(格式:YYYY-MM-DD,如2025-09-01):',
+		'2025-09-01',
+		'validateDateInput'
+	);
+}
+
+/**
+ * 通过API请求和解析课程数据
+ */
+async function fetchAndParseCourses(academicYear, semesterIndex) {
+	AndroidBridge.showToast('正在请求课表数据...');
+	
+	var semesterCode = getSemesterCode(semesterIndex);
+	var baseUrl = window.location.origin;
+	var url = baseUrl + '/jwglxt/kbcx/xskbcx_cxXsgrkb.html?gnmkdm=N2151';
+	var body = 'xnm=' + academicYear + '&xqm=' + semesterCode + '&kzlx=ck';
+	
+	console.log('JS: 发送请求到 ' + url + ', body: ' + body);
+	
+	try {
+		var response = await fetch(url, {
+			headers: {
+				'content-type': 'application/x-www-form-urlencoded;charset=UTF-8'
+			},
+			referer: baseUrl + '/jwglxt/kbcx/xskbcx_cxXskbcxIndex.html?gnmkdm=N2151',
+			body: body,
+			method: 'POST',
+			credentials: 'include'
+		});
+		
+		if (!response.ok) {
+			throw new Error('网络请求失败。状态码: ' + response.status);
+		}
+		
+		var jsonText = await response.text();
+		var jsonData;
+		try {
+			jsonData = JSON.parse(jsonText);
+		} catch (e) {
+			console.error('JS: JSON解析失败,可能是会话过期:', e);
+			AndroidBridge.showToast('数据返回格式错误,可能是您未成功登录或会话已过期。');
+			return null;
+		}
+		
+		var courses = parseJsonData(jsonData);
+		if (courses.length === 0) {
+			AndroidBridge.showToast('未找到任何课程数据,请检查所选学年学期是否正确。');
+			return null;
+		}
+		
+		console.log('JS: 课程数据解析成功,共找到 ' + courses.length + ' 门课程');
+		return courses;
+	} catch (error) {
+		AndroidBridge.showToast('请求或解析失败: ' + error.message);
+		console.error('JS: Fetch/Parse Error:', error);
+		return null;
+	}
+}
+
+async function saveCourses(parsedCourses) {
+	AndroidBridge.showToast('正在保存 ' + parsedCourses.length + ' 门课程...');
+	console.log('JS: 尝试保存 ' + parsedCourses.length + ' 门课程');
+	try {
+		await window.AndroidBridgePromise.saveImportedCourses(JSON.stringify(parsedCourses));
+		console.log('JS: 课程保存成功');
+		return true;
+	} catch (error) {
+		AndroidBridge.showToast('课程保存失败: ' + error.message);
+		console.error('JS: Save Courses Error:', error);
+		return false;
+	}
+}
+
+async function saveConfig(startDate) {
+	console.log('JS: 尝试保存课表配置,开学日期: ' + startDate);
+	var config = {
+		semesterStartDate: startDate,
+		semesterTotalWeeks: 20,
+		defaultClassDuration: 45,
+		defaultBreakDuration: 10,
+		firstDayOfWeek: 1
+	};
+	try {
+		await window.AndroidBridgePromise.saveCourseConfig(JSON.stringify(config));
+		AndroidBridge.showToast('课表配置更新成功!开学日期:' + startDate);
+		console.log('JS: 配置保存成功');
+		return true;
+	} catch (error) {
+		AndroidBridge.showToast('课表配置保存失败: ' + error.message);
+		console.error('JS: Save Config Error:', error);
+		return false;
+	}
+}
+
+// 山东华宇工学院西区冬季作息时间
+var TimeSlots = [
+	{ number: 1, startTime: '08:10', endTime: '08:55' },
+	{ number: 2, startTime: '09:05', endTime: '09:50' },
+	{ number: 3, startTime: '10:15', endTime: '11:00' },
+	{ number: 4, startTime: '11:15', endTime: '12:00' },
+	{ number: 5, startTime: '14:40', endTime: '15:25' },
+	{ number: 6, startTime: '15:35', endTime: '16:20' },
+	{ number: 7, startTime: '16:30', endTime: '17:15' },
+	{ number: 8, startTime: '17:25', endTime: '18:10' },
+	{ number: 9, startTime: '19:10', endTime: '19:55' },
+	{ number: 10, startTime: '20:05', endTime: '20:50' }
+];
+
+async function importPresetTimeSlots(timeSlots) {
+	console.log('JS: 准备导入 ' + timeSlots.length + ' 个预设时间段');
+	try {
+		await window.AndroidBridgePromise.savePresetTimeSlots(JSON.stringify(timeSlots));
+		AndroidBridge.showToast('预设时间段导入成功!');
+		console.log('JS: 预设时间段导入成功');
+	} catch (error) {
+		AndroidBridge.showToast('导入时间段失败: ' + error.message);
+		console.error('JS: Save Time Slots Error:', error);
+	}
+}
+
+async function runImportFlow() {
+	if (isLoginPage()) {
+		AndroidBridge.showToast('导入失败:请先登录教务系统!');
+		console.log('JS: 检测到当前在登录页面,终止导入');
+		return;
+	}
+	
+	var alertConfirmed = await promptUserToStart();
+	if (!alertConfirmed) {
+		AndroidBridge.showToast('用户取消了导入');
+		console.log('JS: 用户取消了导入流程');
+		return;
+	}
+	
+	// 自动获取学年和学期
+	var academicYear = getCurrentAcademicYear();
+	var semesterIndex = getCurrentSemesterIndex();
+	var semesterName = getSemesterName(semesterIndex);
+	
+	console.log('JS: 自动获取到学年: ' + academicYear + ', 学期: ' + semesterName);
+	
+	// 让用户确认
+	var confirmResult = await confirmAcademicYear(academicYear, semesterName);
+	if (!confirmResult) {
+		AndroidBridge.showToast('导入已取消');
+		console.log('JS: 用户取消确认学年学期');
+		return;
+	}
+	
+	var startDate = await getSemesterStartDate();
+	if (startDate === null) {
+		AndroidBridge.showToast('导入已取消');
+		console.log('JS: 获取开学日期失败/取消,流程终止');
+		return;
+	}
+	console.log('JS: 已选择开学日期: ' + startDate);
+	
+	var courses = await fetchAndParseCourses(academicYear, semesterIndex);
+	if (courses === null) {
+		console.log('JS: 课程获取或解析失败,流程终止');
+		return;
+	}
+	
+	var saveResult = await saveCourses(courses);
+	if (!saveResult) {
+		console.log('JS: 课程保存失败,流程终止');
+		return;
+	}
+	
+	var configResult = await saveConfig(startDate);
+	if (!configResult) {
+		console.log('JS: 配置保存失败,流程终止');
+		return;
+	}
+	
+	await importPresetTimeSlots(TimeSlots);
+	
+	AndroidBridge.showToast('课程导入成功,共导入 ' + courses.length + ' 门课程!');
+	console.log('JS: 整个导入流程执行完毕并成功');
+	AndroidBridge.notifyTaskCompletion();
+}
+
+runImportFlow();