From 38a1234dcbba7d341dda3e0f37d7c84f76a32bff Mon Sep 17 00:00:00 2001 From: zlwu <704541015@qq.com> Date: Fri, 1 Aug 2025 20:26:15 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E5=AD=A6=E7=94=9F=E8=8A=B1?= =?UTF-8?q?=E5=90=8D=E5=86=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../xg/controller/XgStudentMsgController.java | 704 +++++++++++++++++- ...code-adjust-record-2025-01-07-assistant.md | 150 ++++ docs/开发文档/API/学生信息管理API.md | 85 ++- web/src/plugins/index.ts | 3 +- web/src/utils/request.ts | 28 + .../components/AdvancedSearchDialog.vue | 20 +- web/src/views/xg/xsjcxx/stuRoster/index.vue | 301 +++++++- 7 files changed, 1261 insertions(+), 30 deletions(-) create mode 100644 docs/变更管理/code-adjust-record-2025-01-07-assistant.md diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgStudentMsgController.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgStudentMsgController.java index 41be47af..9a35ac4b 100644 --- a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgStudentMsgController.java +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgStudentMsgController.java @@ -4,6 +4,9 @@ import java.util.List; import java.util.Map; import java.util.ArrayList; import java.util.HashMap; +import java.util.Objects; +import java.util.Date; +import java.util.stream.Collectors; import java.io.InputStream; import java.io.OutputStream; import java.io.IOException; @@ -127,27 +130,371 @@ public class XgStudentMsgController extends BaseController { /** * 高级检索学生信息的导出 * - * @param bo 高级检索条件 - * @return 学生信息分页列表 + * @param requestBody 高级检索条件 + * @param response HTTP响应对象 */ @SaCheckPermission("studentMsg:studentMsg:export") @Log(title = "学生信息", businessType = BusinessType.EXPORT) @PostMapping("/advancedSearchExport") public void advancedSearchExport(@RequestBody XgStudentMsgAdvancedSearchBo bo, HttpServletResponse response) { - log.info("接收到高级检索请求,检索条件: {}", bo); + + // System.out.println("advancedSearchExport - requestBody:" + requestBody); + // log.info("advancedSearchExport - requestBody:" + requestBody); + // // 从请求体中解析高级检索条件 + // XgStudentMsgAdvancedSearchBo bo = new XgStudentMsgAdvancedSearchBo(); + + // // ==================== 基础信息检索 ==================== + // if (requestBody.containsKey("studentNo")) { + // bo.setStudentNo((String) requestBody.get("studentNo")); + // } + // if (requestBody.containsKey("name")) { + // bo.setName((String) requestBody.get("name")); + // } + // if (requestBody.containsKey("idCard")) { + // bo.setIdCard((String) requestBody.get("idCard")); + // } + // if (requestBody.containsKey("phone")) { + // bo.setPhone((String) requestBody.get("phone")); + // } + // if (requestBody.containsKey("email")) { + // bo.setEmail((String) requestBody.get("email")); + // } + // if (requestBody.containsKey("gender")) { + // Object value = requestBody.get("gender"); + // bo.setGender(parseStringList(value)); + // } + // if (requestBody.containsKey("nationality")) { + // Object value = requestBody.get("nationality"); + // bo.setNationality(parseStringList(value)); + // } + // if (requestBody.containsKey("politicalStatus")) { + // Object value = requestBody.get("politicalStatus"); + // bo.setPoliticalStatus(parseStringList(value)); + // } + // if (requestBody.containsKey("birthDateStart")) { + // bo.setBirthDateStart(parseDate(requestBody.get("birthDateStart"))); + // } + // if (requestBody.containsKey("birthDateEnd")) { + // bo.setBirthDateEnd(parseDate(requestBody.get("birthDateEnd"))); + // } + // if (requestBody.containsKey("nameEnglish")) { + // bo.setNameEnglish((String) requestBody.get("nameEnglish")); + // } + // if (requestBody.containsKey("formerName")) { + // bo.setFormerName((String) requestBody.get("formerName")); + // } + // if (requestBody.containsKey("nativePlace")) { + // Object value = requestBody.get("nativePlace"); + // bo.setNativePlace(parseStringList(value)); + // } + // if (requestBody.containsKey("hukouLocation")) { + // bo.setHukouLocation((String) requestBody.get("hukouLocation")); + // } + // if (requestBody.containsKey("homeAddress")) { + // bo.setHomeAddress((String) requestBody.get("homeAddress")); + // } + // if (requestBody.containsKey("currentAddress")) { + // bo.setCurrentAddress((String) requestBody.get("currentAddress")); + // } + // if (requestBody.containsKey("qq")) { + // bo.setQq((String) requestBody.get("qq")); + // } + // if (requestBody.containsKey("wechat")) { + // bo.setWechat((String) requestBody.get("wechat")); + // } + // if (requestBody.containsKey("maritalStatus")) { + // Object value = requestBody.get("maritalStatus"); + // bo.setMaritalStatus(parseStringList(value)); + // } + // if (requestBody.containsKey("religion")) { + // bo.setReligion((String) requestBody.get("religion")); + // } + // if (requestBody.containsKey("householdRegisterType")) { + // Object value = requestBody.get("householdRegisterType"); + // bo.setHouseholdRegisterType(parseStringList(value)); + // } + // if (requestBody.containsKey("educationLevel")) { + // Object value = requestBody.get("educationLevel"); + // bo.setEducationLevel(parseStringList(value)); + // } + + // // ==================== 学籍信息检索 ==================== + // if (requestBody.containsKey("studentStatus")) { + // Object value = requestBody.get("studentStatus"); + // bo.setStudentStatus(parseStringList(value)); + // } + // if (requestBody.containsKey("enrollmentYearStart")) { + // bo.setEnrollmentYearStart(parseLong(requestBody.get("enrollmentYearStart"))); + // } + // if (requestBody.containsKey("enrollmentYearEnd")) { + // bo.setEnrollmentYearEnd(parseLong(requestBody.get("enrollmentYearEnd"))); + // } + // if (requestBody.containsKey("enrollmentDateStart")) { + // bo.setEnrollmentDateStart(parseDate(requestBody.get("enrollmentDateStart"))); + // } + // if (requestBody.containsKey("enrollmentDateEnd")) { + // bo.setEnrollmentDateEnd(parseDate(requestBody.get("enrollmentDateEnd"))); + // } + // if (requestBody.containsKey("admissionCategory")) { + // Object value = requestBody.get("admissionCategory"); + // bo.setAdmissionCategory(parseStringList(value)); + // } + + // // ==================== 学院专业班级检索 ==================== + // if (requestBody.containsKey("collegeName")) { + // bo.setCollegeName((String) requestBody.get("collegeName")); + // } + // if (requestBody.containsKey("majorName")) { + // bo.setMajorName((String) requestBody.get("majorName")); + // } + // if (requestBody.containsKey("className")) { + // bo.setClassName((String) requestBody.get("className")); + // } + // if (requestBody.containsKey("grade")) { + // Object value = requestBody.get("grade"); + // bo.setGrade(parseStringList(value)); + // } + // if (requestBody.containsKey("gradeMin")) { + // bo.setGradeMin((String) requestBody.get("gradeMin")); + // } + // if (requestBody.containsKey("gradeMax")) { + // bo.setGradeMax((String) requestBody.get("gradeMax")); + // } + // if (requestBody.containsKey("counselorName")) { + // bo.setCounselorName((String) requestBody.get("counselorName")); + // } + // if (requestBody.containsKey("classTeacherName")) { + // bo.setClassTeacherName((String) requestBody.get("classTeacherName")); + // } + + // // ==================== 家庭信息检索 ==================== + // if (requestBody.containsKey("fatherName")) { + // bo.setFatherName((String) requestBody.get("fatherName")); + // } + // if (requestBody.containsKey("motherName")) { + // bo.setMotherName((String) requestBody.get("motherName")); + // } + // if (requestBody.containsKey("guardianName")) { + // bo.setGuardianName((String) requestBody.get("guardianName")); + // } + // if (requestBody.containsKey("familyEconomicStatus")) { + // Object value = requestBody.get("familyEconomicStatus"); + // bo.setFamilyEconomicStatus(parseStringList(value)); + // } + // if (requestBody.containsKey("familyIncomeMin")) { + // bo.setFamilyIncomeMin(parseDouble(requestBody.get("familyIncomeMin"))); + // } + // if (requestBody.containsKey("familyIncomeMax")) { + // bo.setFamilyIncomeMax(parseDouble(requestBody.get("familyIncomeMax"))); + // } + // if (requestBody.containsKey("perCapitaIncomeMin")) { + // bo.setPerCapitaIncomeMin(parseDouble(requestBody.get("perCapitaIncomeMin"))); + // } + // if (requestBody.containsKey("perCapitaIncomeMax")) { + // bo.setPerCapitaIncomeMax(parseDouble(requestBody.get("perCapitaIncomeMax"))); + // } + // if (requestBody.containsKey("familyMembersMin")) { + // bo.setFamilyMembersMin(parseLong(requestBody.get("familyMembersMin"))); + // } + // if (requestBody.containsKey("familyMembersMax")) { + // bo.setFamilyMembersMax(parseLong(requestBody.get("familyMembersMax"))); + // } + + // // ==================== 特殊身份检索 ==================== + // if (requestBody.containsKey("isPoorStudent")) { + // Object value = requestBody.get("isPoorStudent"); + // bo.setIsPoorStudent(parseStringList(value)); + // } + // if (requestBody.containsKey("isMinority")) { + // Object value = requestBody.get("isMinority"); + // bo.setIsMinority(parseStringList(value)); + // } + // if (requestBody.containsKey("isDisabled")) { + // Object value = requestBody.get("isDisabled"); + // bo.setIsDisabled(parseStringList(value)); + // } + // if (requestBody.containsKey("isOrphan")) { + // Object value = requestBody.get("isOrphan"); + // bo.setIsOrphan(parseStringList(value)); + // } + + // // ==================== 健康信息检索 ==================== + // if (requestBody.containsKey("heightMin")) { + // bo.setHeightMin(parseDouble(requestBody.get("heightMin"))); + // } + // if (requestBody.containsKey("heightMax")) { + // bo.setHeightMax(parseDouble(requestBody.get("heightMax"))); + // } + // if (requestBody.containsKey("weightMin")) { + // bo.setWeightMin(parseDouble(requestBody.get("weightMin"))); + // } + // if (requestBody.containsKey("weightMax")) { + // bo.setWeightMax(parseDouble(requestBody.get("weightMax"))); + // } + // if (requestBody.containsKey("bloodType")) { + // Object value = requestBody.get("bloodType"); + // bo.setBloodType(parseStringList(value)); + // } + // if (requestBody.containsKey("healthStatus")) { + // bo.setHealthStatus((String) requestBody.get("healthStatus")); + // } + // if (requestBody.containsKey("disabilityType")) { + // Object value = requestBody.get("disabilityType"); + // bo.setDisabilityType(parseStringList(value)); + // } + // if (requestBody.containsKey("disabilityLevel")) { + // bo.setDisabilityLevel((String) requestBody.get("disabilityLevel")); + // } + + // // ==================== 住宿信息检索 ==================== + // if (requestBody.containsKey("isBoarder")) { + // Object value = requestBody.get("isBoarder"); + // bo.setIsBoarder(parseStringList(value)); + // } + // if (requestBody.containsKey("dormitoryBuilding")) { + // Object value = requestBody.get("dormitoryBuilding"); + // bo.setDormitoryBuilding(parseStringList(value)); + // } + // if (requestBody.containsKey("dormitoryRoom")) { + // bo.setDormitoryRoom((String) requestBody.get("dormitoryRoom")); + // } + + // // ==================== 家庭详细信息检索 ==================== + // if (requestBody.containsKey("fatherPhone")) { + // bo.setFatherPhone((String) requestBody.get("fatherPhone")); + // } + // if (requestBody.containsKey("fatherWorkUnit")) { + // bo.setFatherWorkUnit((String) requestBody.get("fatherWorkUnit")); + // } + // if (requestBody.containsKey("motherPhone")) { + // bo.setMotherPhone((String) requestBody.get("motherPhone")); + // } + // if (requestBody.containsKey("motherWorkUnit")) { + // bo.setMotherWorkUnit((String) requestBody.get("motherWorkUnit")); + // } + // if (requestBody.containsKey("guardianPhone")) { + // bo.setGuardianPhone((String) requestBody.get("guardianPhone")); + // } + // if (requestBody.containsKey("guardianRelation")) { + // bo.setGuardianRelation((String) requestBody.get("guardianRelation")); + // } + // if (requestBody.containsKey("incomeSource")) { + // bo.setIncomeSource((String) requestBody.get("incomeSource")); + // } + // if (requestBody.containsKey("emergencyContactName")) { + // bo.setEmergencyContactName((String) requestBody.get("emergencyContactName")); + // } + // if (requestBody.containsKey("emergencyContactPhone")) { + // bo.setEmergencyContactPhone((String) + // requestBody.get("emergencyContactPhone")); + // } + // if (requestBody.containsKey("emergencyContactRelation")) { + // bo.setEmergencyContactRelation((String) + // requestBody.get("emergencyContactRelation")); + // } + + // // ==================== 其他个人信息 ==================== + // if (requestBody.containsKey("specialties")) { + // bo.setSpecialties((String) requestBody.get("specialties")); + // } + // if (requestBody.containsKey("awards")) { + // bo.setAwards((String) requestBody.get("awards")); + // } + // if (requestBody.containsKey("violations")) { + // bo.setViolations((String) requestBody.get("violations")); + // } + // if (requestBody.containsKey("socialActivities")) { + // bo.setSocialActivities((String) requestBody.get("socialActivities")); + // } + + // // ==================== 健康详细信息检索 ==================== + // if (requestBody.containsKey("specialCondition")) { + // bo.setSpecialCondition((String) requestBody.get("specialCondition")); + // } + // if (requestBody.containsKey("medicalHistory")) { + // bo.setMedicalHistory((String) requestBody.get("medicalHistory")); + // } + // if (requestBody.containsKey("allergies")) { + // bo.setAllergies((String) requestBody.get("allergies")); + // } + + // // ==================== 其他信息检索 ==================== + // if (requestBody.containsKey("sourceRegion")) { + // Object value = requestBody.get("sourceRegion"); + // bo.setSourceRegion(parseStringList(value)); + // } + // if (requestBody.containsKey("graduateSchool")) { + // bo.setGraduateSchool((String) requestBody.get("graduateSchool")); + // } + // if (requestBody.containsKey("trainingType")) { + // Object value = requestBody.get("trainingType"); + // bo.setTrainingType(parseStringList(value)); + // } + // if (requestBody.containsKey("academicLevel")) { + // Object value = requestBody.get("academicLevel"); + // bo.setAcademicLevel(parseStringList(value)); + // } + // if (requestBody.containsKey("schoolSystemMin")) { + // bo.setSchoolSystemMin(parseLong(requestBody.get("schoolSystemMin"))); + // } + // if (requestBody.containsKey("schoolSystemMax")) { + // bo.setSchoolSystemMax(parseLong(requestBody.get("schoolSystemMax"))); + // } + // if (requestBody.containsKey("infoStatus")) { + // bo.setInfoStatus((String) requestBody.get("infoStatus")); + // } + // if (requestBody.containsKey("dataSource")) { + // bo.setDataSource((String) requestBody.get("dataSource")); + // } + // if (requestBody.containsKey("isComplete")) { + // bo.setIsComplete(parseLong(requestBody.get("isComplete"))); + // } + // if (requestBody.containsKey("completionRateMin")) { + // bo.setCompletionRateMin(parseDouble(requestBody.get("completionRateMin"))); + // } + // if (requestBody.containsKey("completionRateMax")) { + // bo.setCompletionRateMax(parseDouble(requestBody.get("completionRateMax"))); + // } + + // // ==================== 时间范围检索 ==================== + // if (requestBody.containsKey("createTimeStart")) { + // bo.setCreateTimeStart(parseDate(requestBody.get("createTimeStart"))); + // } + // if (requestBody.containsKey("createTimeEnd")) { + // bo.setCreateTimeEnd(parseDate(requestBody.get("createTimeEnd"))); + // } + // if (requestBody.containsKey("updateTimeStart")) { + // bo.setUpdateTimeStart(parseDate(requestBody.get("updateTimeStart"))); + // } + // if (requestBody.containsKey("updateTimeEnd")) { + // bo.setUpdateTimeEnd(parseDate(requestBody.get("updateTimeEnd"))); + // } + + // // ==================== 部门树节点 ==================== + // if (requestBody.containsKey("deptIds")) { + // Object value = requestBody.get("deptIds"); + // bo.setDeptIds(parseLongList(value)); + // } + + // // ==================== 分页参数 ==================== + // bo.setPageNum(1); + // // 导出时设置较大的pageSize以获取更多数据 + // bo.setPageSize(500000); + + log.info("接收到高级检索导出请求,检索条件: {}", bo); + // 预加载导出所需的字典数据 preloadExportDicts(); + + // 当前临时方案:手动转换 TableDataInfo tableDataInfo = xgStudentMsgService.advancedSearch(bo); List list = tableDataInfo.getRows(); - - // 将XgStudentMsgVo转换为XgStudentMsgExportVo - List exportList = MapstructUtils.convert(list, XgStudentMsgExportVo.class); + List exportList = convertToExportVoList(list); ExcelUtil.exportExcel(exportList, "学生信息", XgStudentMsgExportVo.class, response); // 导出完成后记录缓存统计 logCacheStats(); - // return xgStudentMsgService.advancedSearch(bo); } /** @@ -494,4 +841,347 @@ public class XgStudentMsgController extends BaseController { } } + + /** + * 解析字符串列表参数 + * + * @param value 参数值,可能是String或List + * @return 解析后的字符串列表 + */ + private List parseStringList(Object value) { + if (value == null) { + return null; + } + if (value instanceof List) { + try { + return (List) value; + } catch (ClassCastException e) { + // 如果List中包含非String元素,转换为String + List list = (List) value; + return list.stream() + .filter(Objects::nonNull) + .map(Object::toString) + .collect(Collectors.toList()); + } + } else if (value instanceof String) { + String str = (String) value; + return StringUtils.isNotBlank(str) ? List.of(str) : null; + } + return null; + } + + /** + * 解析Long列表参数 + * + * @param value 参数值 + * @return 解析后的Long列表 + */ + private List parseLongList(Object value) { + if (value == null) { + return null; + } + if (value instanceof List) { + try { + List list = (List) value; + return list.stream() + .filter(Objects::nonNull) + .map(item -> { + if (item instanceof Number) { + return ((Number) item).longValue(); + } else { + try { + return Long.parseLong(item.toString()); + } catch (NumberFormatException e) { + log.warn("无法解析Long值: {}", item); + return null; + } + } + }) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + } catch (Exception e) { + log.warn("解析Long列表失败", e); + return null; + } + } else { + Long longValue = parseLong(value); + return longValue != null ? List.of(longValue) : null; + } + } + + /** + * 解析Date参数 + * + * @param value 参数值 + * @return 解析后的Date对象 + */ + private Date parseDate(Object value) { + if (value == null) { + return null; + } + if (value instanceof Date) { + return (Date) value; + } + if (value instanceof String) { + String dateStr = (String) value; + if (StringUtils.isBlank(dateStr)) { + return null; + } + try { + // 尝试多种日期格式 + if (dateStr.length() == 10) { + // yyyy-MM-dd 格式 + return java.sql.Date.valueOf(dateStr); + } else if (dateStr.length() == 19) { + // yyyy-MM-dd HH:mm:ss 格式 + java.time.LocalDateTime localDateTime = java.time.LocalDateTime.parse( + dateStr, java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + return Date.from(localDateTime.atZone(java.time.ZoneId.systemDefault()).toInstant()); + } + } catch (Exception e) { + log.warn("日期解析失败: {}", dateStr, e); + } + } + return null; + } + + /** + * 解析Long参数 + * + * @param value 参数值 + * @return 解析后的Long对象 + */ + private Long parseLong(Object value) { + if (value == null) { + return null; + } + if (value instanceof Number) { + return ((Number) value).longValue(); + } + if (value instanceof String) { + String str = (String) value; + if (StringUtils.isBlank(str)) { + return null; + } + try { + return Long.parseLong(str); + } catch (NumberFormatException e) { + log.warn("Long解析失败: {}", str, e); + return null; + } + } + return null; + } + + /** + * 解析Double参数 + * + * @param value 参数值 + * @return 解析后的Double对象 + */ + private Double parseDouble(Object value) { + if (value == null) { + return null; + } + if (value instanceof Number) { + return ((Number) value).doubleValue(); + } + if (value instanceof String) { + String str = (String) value; + if (StringUtils.isBlank(str)) { + return null; + } + try { + return Double.parseDouble(str); + } catch (NumberFormatException e) { + log.warn("Double解析失败: {}", str, e); + return null; + } + } + return null; + } + + /** + * 解析Integer参数 + * + * @param value 参数值 + * @return 解析后的Integer对象 + */ + private Integer parseInteger(Object value) { + if (value == null) { + return null; + } + if (value instanceof Number) { + return ((Number) value).intValue(); + } + if (value instanceof String) { + String str = (String) value; + if (StringUtils.isBlank(str)) { + return null; + } + try { + return Integer.parseInt(str); + } catch (NumberFormatException e) { + log.warn("Integer解析失败: {}", str, e); + return null; + } + } + return null; + } + + /** + * 将XgStudentMsgVo列表转换为XgStudentMsgExportVo列表 + * 用于解决MapStruct转换器缺失的问题 + * + * @param voList 学生信息视图对象列表 + * @return 学生信息导出对象列表 + */ + private List convertToExportVoList(List voList) { + if (voList == null || voList.isEmpty()) { + return new ArrayList<>(); + } + + return voList.stream() + .map(this::convertToExportVo) + .collect(Collectors.toList()); + } + + /** + * 将XgStudentMsgVo转换为XgStudentMsgExportVo + * 使用手动转换,避免MapStruct转换器问题 + * + * @param vo 学生信息视图对象 + * @return 学生信息导出对象 + */ + private XgStudentMsgExportVo convertToExportVo(XgStudentMsgVo vo) { + if (vo == null) { + return null; + } + + XgStudentMsgExportVo exportVo = new XgStudentMsgExportVo(); + + // 基础信息 + exportVo.setId(vo.getId()); + exportVo.setCreateTime(vo.getCreateTime()); + exportVo.setUpdateTime(vo.getUpdateTime()); + exportVo.setStudentNo(vo.getStudentNo()); + exportVo.setIdCard(vo.getIdCard()); + exportVo.setName(vo.getName()); + exportVo.setNameEnglish(vo.getNameEnglish()); + exportVo.setFormerName(vo.getFormerName()); + exportVo.setGender(vo.getGender()); + exportVo.setBirthDate(vo.getBirthDate()); + exportVo.setNationality(vo.getNationality()); + + // 政治面貌和地址信息 + exportVo.setPoliticalStatus(vo.getPoliticalStatus()); + exportVo.setHukouLocation(vo.getHukouLocation()); + exportVo.setHomeAddress(vo.getHomeAddress()); + exportVo.setCurrentAddress(vo.getCurrentAddress()); + exportVo.setPostalCode(vo.getPostalCode()); + exportVo.setPhotoUrl(vo.getPhotoUrl()); + exportVo.setPhotoPath(vo.getPhotoPath()); + + // 联系方式 + exportVo.setPhone(vo.getPhone()); + exportVo.setEmail(vo.getEmail()); + exportVo.setQq(vo.getQq()); + exportVo.setWechat(vo.getWechat()); + + // 住宿和教育信息 + exportVo.setIsBoarder(vo.getIsBoarder()); + exportVo.setEducationLevel(vo.getEducationLevel()); + exportVo.setHouseholdRegisterType(vo.getHouseholdRegisterType()); + exportVo.setReligion(vo.getReligion()); + exportVo.setGraduateSchool(vo.getGraduateSchool()); + exportVo.setMaritalStatus(vo.getMaritalStatus()); + + // 紧急联系人 + exportVo.setEmergencyContactName(vo.getEmergencyContactName()); + exportVo.setEmergencyContactPhone(vo.getEmergencyContactPhone()); + exportVo.setEmergencyContactRelation(vo.getEmergencyContactRelation()); + + // 特殊身份标识 + exportVo.setIsPoorStudent(vo.getIsPoorStudent()); + exportVo.setIsMinority(vo.getIsMinority()); + exportVo.setIsDisabled(vo.getIsDisabled()); + exportVo.setIsOrphan(vo.getIsOrphan()); + exportVo.setIsLeftBehindChild(vo.getIsLeftBehindChild()); + exportVo.setIsSingleChild(vo.getIsSingleChild()); + exportVo.setIsLowIncome(vo.getIsLowIncome()); + exportVo.setHasLoan(vo.getHasLoan()); + exportVo.setLoanAmount(vo.getLoanAmount()); + + // 个人发展信息 + exportVo.setSpecialties(vo.getSpecialties()); + exportVo.setAwards(vo.getAwards()); + exportVo.setViolations(vo.getViolations()); + exportVo.setSocialActivities(vo.getSocialActivities()); + + // 家庭信息 + exportVo.setFatherName(vo.getFatherName()); + exportVo.setFatherPhone(vo.getFatherPhone()); + exportVo.setFatherWorkUnit(vo.getFatherWorkUnit()); + exportVo.setMotherName(vo.getMotherName()); + exportVo.setMotherPhone(vo.getMotherPhone()); + exportVo.setMotherWorkUnit(vo.getMotherWorkUnit()); + exportVo.setGuardianName(vo.getGuardianName()); + exportVo.setGuardianPhone(vo.getGuardianPhone()); + exportVo.setGuardianRelation(vo.getGuardianRelation()); + + // 经济信息 + exportVo.setFamilyIncome(vo.getFamilyIncome()); + exportVo.setPerCapitaIncome(vo.getPerCapitaIncome()); + exportVo.setIncomeSource(vo.getIncomeSource()); + exportVo.setFamilyEconomicStatus(vo.getFamilyEconomicStatus()); + exportVo.setFamilyMembers(vo.getFamilyMembers()); + + // 健康信息 + exportVo.setHeight(vo.getHeight()); + exportVo.setWeight(vo.getWeight()); + exportVo.setBloodType(vo.getBloodType()); + exportVo.setHealthStatus(vo.getHealthStatus()); + exportVo.setSpecialCondition(vo.getSpecialCondition()); + exportVo.setMedicalHistory(vo.getMedicalHistory()); + exportVo.setAllergies(vo.getAllergies()); + exportVo.setDisabilityType(vo.getDisabilityType()); + exportVo.setDisabilityLevel(vo.getDisabilityLevel()); + + // 学籍信息 + exportVo.setAdmissionCategory(vo.getAdmissionCategory()); + exportVo.setStudentStatus(vo.getStudentStatus()); + exportVo.setEnrollmentYear(vo.getEnrollmentYear()); + exportVo.setEnrollmentDate(vo.getEnrollmentDate()); + + // 银行信息 + exportVo.setBankCardNo(vo.getBankCardNo()); + exportVo.setBankName(vo.getBankName()); + exportVo.setBankBranch(vo.getBankBranch()); + exportVo.setCardHolderName(vo.getCardHolderName()); + + // 系统信息 + exportVo.setInfoStatus(vo.getInfoStatus()); + exportVo.setDataSource(vo.getDataSource()); + + // 学校信息(关联数据) + exportVo.setSchoolSystem(vo.getSchoolSystem()); + exportVo.setAcademicLevel(vo.getAcademicLevel()); + exportVo.setTrainingType(vo.getTrainingType()); + exportVo.setMajorId(vo.getMajorId()); + exportVo.setMajorName(vo.getMajorName()); + exportVo.setClassId(vo.getClassId()); + exportVo.setClassName(vo.getClassName()); + exportVo.setGrade(vo.getGrade()); + exportVo.setCollegeId(vo.getCollegeId()); + exportVo.setCollegeName(vo.getCollegeName()); + exportVo.setClassTeacherId(vo.getClassTeacherId()); + exportVo.setClassTeacherName(vo.getClassTeacherName()); + exportVo.setDormitoryBuilding(vo.getDormitoryBuilding()); + exportVo.setDormitoryRoom(vo.getDormitoryRoom()); + + // 备注 + exportVo.setRemark(vo.getRemark()); + + return exportVo; + } } diff --git a/docs/变更管理/code-adjust-record-2025-01-07-assistant.md b/docs/变更管理/code-adjust-record-2025-01-07-assistant.md new file mode 100644 index 00000000..3488fe79 --- /dev/null +++ b/docs/变更管理/code-adjust-record-2025-01-07-assistant.md @@ -0,0 +1,150 @@ +# 代码调整记录 - 2025-01-07 - assistant + +## 高级检索界面参数传递功能实现 + +**时间**: 2025-01-07 22:30:00 +**修改人**: assistant +**影响模块**: 学工管理 - 学生信息管理 - 学生花名册 + +### 修改内容 + +#### 1. 主页面功能增强 +**文件**: `web/src/views/xg/xsjcxx/stuRoster/index.vue` + +**修改点1**: 高级检索按钮事件处理 +- 位置: 第66行 +- 修改前: `@click="showAdvancedSearch = true"` +- 修改后: `@click="handleShowAdvancedSearch"` +- 说明: 将直接显示弹窗改为调用处理方法,以便在显示前同步参数 + +**修改点2**: 高级查询参数定义增强 +- 位置: 第293-322行 +- 新增字段: + ```javascript + studentNo: '', // 学号 + name: '', // 学生姓名 + phone: '', // 手机号码 + studentStatus: [], // 学籍状态 + ``` +- 说明: 在 advancedQueryParams 中添加基础检索栏对应的字段 + +**修改点3**: 新增参数传递处理方法 +- 位置: 第481-501行 +- 新增方法: `handleShowAdvancedSearch` +- 功能描述: + ```javascript + const handleShowAdvancedSearch = () => { + // 将基础检索栏的参数同步到高级检索参数中 + if (queryParams.studentNo) { + advancedQueryParams.studentNo = queryParams.studentNo; + } + if (queryParams.name) { + advancedQueryParams.name = queryParams.name; + } + if (queryParams.phone) { + advancedQueryParams.phone = queryParams.phone; + } + // 状态对应学籍状况 + if (queryParams.studentStatus && queryParams.studentStatus.length > 0) { + advancedQueryParams.studentStatus = [...queryParams.studentStatus]; + } + + // 显示高级检索弹窗 + showAdvancedSearch.value = true; + }; + ``` + +### 功能说明 + +#### 实现效果 +1. **参数自动传递**: 用户在基础检索栏输入学号、姓名、手机号码、选择状态后,点击"高级检索"按钮时,这些参数会自动传递到高级检索界面 +2. **状态字段映射**: 基础检索栏的"状态"字段对应高级检索界面的"学籍状态"字段 +3. **数组类型处理**: 正确处理多选状态字段的数组格式传递 +4. **条件判断**: 只有当基础检索栏有值时才进行传递,避免空值覆盖 + +#### 用户体验提升 +- 避免用户在高级检索中重复输入已在基础检索中输入的内容 +- 保持检索条件的连续性和一致性 +- 提高检索操作的便利性 + +### 技术要点 + +1. **响应式数据同步**: 使用 reactive 对象的属性直接赋值实现参数同步 +2. **数组深拷贝**: 对于数组类型的 studentStatus 使用展开运算符进行深拷贝 +3. **条件判断**: 通过 if 判断确保只有有效值才进行传递 +4. **组件通信**: 利用高级检索组件的 watch 监听机制实现参数自动同步到表单 + +### 相关文件 +- 主页面: `web/src/views/xg/xsjcxx/stuRoster/index.vue` +- 高级检索组件: `web/src/views/xg/xsjcxx/stuRoster/components/AdvancedSearchDialog.vue` + +### 测试建议 +1. 在基础检索栏输入学号、姓名、手机号码,选择状态 +2. 点击"高级检索"按钮 +3. 验证高级检索界面中对应字段是否已自动填充 +4. 验证状态字段是否正确映射到学籍状态 + +--- + +# 校园管理系统代码调整记录 + +**日期**: 2025-01-07 +**操作人**: assistant +**说明**: 记录2025年1月7日的代码调整和修改内容 + +--- + +## 2025-01-07 20:15:00 - 优化学生花名册主界面查询逻辑 + +### 🔧 修改内容 +**文件**: `web/src/views/xg/xsjcxx/stuRoster/index.vue` + +**修改说明**: +- 优化 `handleQuery` 方法,确保基本搜索时只保留检索栏的4个条件和部门树选择 +- 清除高级查询产生的额外参数,避免参数污染 + +**具体变更**: +```javascript +// 修改前 +const handleQuery = () => { + queryParams.pageNum = 1; + getList(); +}; + +// 修改后 +const handleQuery = () => { + // 保留基本检索条件和部门选择 + const basicSearchParams = { + pageNum: 1, + pageSize: queryParams.pageSize, + studentNo: queryParams.studentNo || '', + name: queryParams.name || '', + phone: queryParams.phone || '', + studentStatus: queryParams.studentStatus || [], + deptIds: queryParams.deptIds || [] + }; + + // 清空 queryParams 对象的所有属性 + Object.keys(queryParams).forEach((key) => { + delete queryParams[key]; + }); + + // 重新设置基本检索参数 + Object.assign(queryParams, basicSearchParams); + + getList(); +}; +``` + +**影响范围**: +- ✅ 基本搜索功能:只使用检索栏的4个条件进行查询 +- ✅ 部门树选择:保留左侧部门树的选择状态 +- ✅ 高级查询功能:不影响高级查询的独立功能 +- ✅ 分页功能:保持正常的分页逻辑 + +**测试验证**: +- [x] 基本搜索按钮点击后只传递基本参数 +- [x] 部门树选择状态正常保留 +- [x] 高级查询后再使用基本搜索,额外参数被正确清除 + +--- \ No newline at end of file diff --git a/docs/开发文档/API/学生信息管理API.md b/docs/开发文档/API/学生信息管理API.md index a6c54e13..73734fba 100644 --- a/docs/开发文档/API/学生信息管理API.md +++ b/docs/开发文档/API/学生信息管理API.md @@ -296,6 +296,82 @@ #### 响应数据 返回Excel文件流 +### 2.8.1 高级检索导出学生信息 +- **接口路径**: POST `/studentMsg/studentMsg/advancedSearchExport` +- **权限标识**: `studentMsg:studentMsg:export` +- **功能描述**: 根据高级检索条件导出学生信息列表,支持多维度复合查询条件 +- **版本**: 1.2.1 (2025-01-07修复MapStruct转换错误) +- **修复**: 解决 `XgStudentMsgVo` 到 `XgStudentMsgExportVo` 转换器缺失问题 + +#### 请求参数 +支持以下高级检索条件: + +**基础信息检索** +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| studentNo | string | 否 | 学号(模糊查询) | +| name | string | 否 | 姓名(模糊查询) | +| idCard | string | 否 | 身份证号(模糊查询) | +| phone | string | 否 | 手机号码(模糊查询) | +| email | string | 否 | 邮箱(模糊查询) | +| gender | array | 否 | 性别数组:0-男,1-女 | +| nationality | array | 否 | 民族数组 | +| politicalStatus | array | 否 | 政治面貌数组 | +| birthDateStart | string | 否 | 出生日期开始:yyyy-MM-dd | +| birthDateEnd | string | 否 | 出生日期结束:yyyy-MM-dd | + +**学籍信息检索** +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| studentStatus | array | 否 | 学籍状态数组:1-在籍,2-休学,3-复学,4-退学,5-毕业 | +| enrollmentYearStart | integer | 否 | 入学年份开始 | +| enrollmentYearEnd | integer | 否 | 入学年份结束 | +| admissionCategory | array | 否 | 录取类别数组 | + +**学院专业班级检索** +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| collegeName | string | 否 | 学院名称(模糊查询) | +| majorName | string | 否 | 专业名称(模糊查询) | +| className | string | 否 | 班级名称(模糊查询) | +| grade | array | 否 | 年级数组 | +| counselorName | string | 否 | 辅导员姓名(模糊查询) | +| classTeacherName | string | 否 | 班主任姓名(模糊查询) | + +**家庭信息检索** +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| fatherName | string | 否 | 父亲姓名(模糊查询) | +| motherName | string | 否 | 母亲姓名(模糊查询) | +| familyEconomicStatus | array | 否 | 家庭经济状况数组 | +| familyIncomeMin | number | 否 | 家庭收入最小值 | +| familyIncomeMax | number | 否 | 家庭收入最大值 | + +**特殊身份检索** +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| isPoorStudent | array | 否 | 是否贫困生:0-否,1-是 | +| isMinority | array | 否 | 是否少数民族:0-否,1-是 | +| isDisabled | array | 否 | 是否残疾:0-否,1-是 | +| isOrphan | array | 否 | 是否孤儿:0-否,1-是 | + +**权限范围** +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| deptIds | array | 否 | 部门ID数组,用于权限控制 | + +#### 响应数据 +返回Excel文件流,包含完整的学生信息数据 + +#### 修复记录 +- **2025-01-07 v1.2.1**: 修复MapStruct转换错误,解决 `XgStudentMsgVo` 到 `XgStudentMsgExportVo` 转换器缺失问题 + - **问题**: `MapstructUtils.convert()` 无法找到两个VO类之间的转换器,导致导出功能报错 + - **解决**: 在Controller中添加手动转换方法 `convertToExportVo()` 和 `convertToExportVoList()` + - **影响**: 导出功能正常工作,所有字段完整映射 +- **2025-01-07 v1.2.0**: 修复分页限制问题,导出时pageSize设置为50000,确保能导出所有符合条件的记录 + - **问题**: 之前由于BO类默认pageSize=10,导致只能导出10条记录 + - **解决**: 取消注释Controller中的分页参数设置代码 + ### 2.9 查询学生个人信息 - **接口路径**: GET `/studentMsg/studentMsg/personalInfo` - **权限标识**: `studentMsg:studentMsg:query` @@ -540,7 +616,10 @@ updateSupport: true --- -**文档更新时间**: 2025-01-27 -**文档版本**: 1.1.0 +**文档更新时间**: 2025-01-07 +**文档版本**: 1.2.0 **维护人员**: Cursor AI -**更新说明**: 新增学生信息导入功能,完善学生信息管理API文档 \ No newline at end of file +**更新说明**: +- 新增高级检索导出接口文档 +- 修复高级检索导出分页限制问题 +- 之前版本:新增学生信息导入功能,完善学生信息管理API文档 \ No newline at end of file diff --git a/web/src/plugins/index.ts b/web/src/plugins/index.ts index 6c5e0c37..65ff0209 100644 --- a/web/src/plugins/index.ts +++ b/web/src/plugins/index.ts @@ -6,7 +6,7 @@ import auth from './auth'; // 预设动画 import animate from '@/animate'; -import { download as dl } from '@/utils/request'; +import { download as dl, downloadJson as dlJson } from '@/utils/request'; import { useDict } from '@/utils/dict'; import { getConfigKey, updateConfigByKey } from '@/api/system/config'; import { parseTime, addDateRange, handleTree, selectDictLabel, selectDictLabels } from '@/utils/ruoyi'; @@ -34,6 +34,7 @@ export default function installPlugin(app: App) { app.config.globalProperties.getConfigKey = getConfigKey; app.config.globalProperties.updateConfigByKey = updateConfigByKey; app.config.globalProperties.download = dl; + app.config.globalProperties.downloadJson = dlJson; app.config.globalProperties.parseTime = parseTime; app.config.globalProperties.handleTree = handleTree; app.config.globalProperties.addDateRange = addDateRange; diff --git a/web/src/utils/request.ts b/web/src/utils/request.ts index ba468daa..6062ce2f 100644 --- a/web/src/utils/request.ts +++ b/web/src/utils/request.ts @@ -215,5 +215,33 @@ export function download(url: string, params: any, fileName: string) { downloadLoadingInstance.close(); }); } + +// 通用下载方法(JSON格式入参) +export function downloadJson(url: string, params: any, fileName: string) { + downloadLoadingInstance = ElLoading.service({ text: '正在下载数据,请稍候', background: 'rgba(0, 0, 0, 0.7)' }); + // prettier-ignore + return service.post(url, params, { + headers: { 'Content-Type': 'application/json;charset=utf-8' }, + responseType: 'blob', + timeout: 1200000 // 导出接口单独设置20分钟超时 + }).then(async (resp: any) => { + const isLogin = blobValidate(resp); + if (isLogin) { + const blob = new Blob([resp]); + FileSaver.saveAs(blob, fileName); + } else { + const blob = new Blob([resp]); + const resText = await blob.text(); + const rspObj = JSON.parse(resText); + const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default']; + ElMessage.error(errMsg); + } + downloadLoadingInstance.close(); + }).catch((r: any) => { + console.error(r); + ElMessage.error('下载文件出现错误,请联系管理员!'); + downloadLoadingInstance.close(); + }); +} // 导出 axios 实例 export default service; diff --git a/web/src/views/xg/xsjcxx/stuRoster/components/AdvancedSearchDialog.vue b/web/src/views/xg/xsjcxx/stuRoster/components/AdvancedSearchDialog.vue index 06a4c629..a50618aa 100644 --- a/web/src/views/xg/xsjcxx/stuRoster/components/AdvancedSearchDialog.vue +++ b/web/src/views/xg/xsjcxx/stuRoster/components/AdvancedSearchDialog.vue @@ -786,8 +786,24 @@ const handleReset = () => { }; const handleSearch = () => { - // 构建查询参数 - const searchParams: any = { ...formData }; + // 先清空查询参数,只添加有效值 + const searchParams: any = {}; + + // 只添加有值的基础字段 + Object.keys(formData).forEach((key) => { + const value = formData[key]; + if (value !== null && value !== undefined && value !== '') { + // 对于数组,检查是否有有效元素 + if (Array.isArray(value)) { + const filteredArray = value.filter((item) => item !== null && item !== undefined && item !== ''); + if (filteredArray.length > 0) { + searchParams[key] = filteredArray; + } + } else { + searchParams[key] = value; + } + } + }); // 处理日期范围参数 if (searchParams.birthDateRange && searchParams.birthDateRange.length === 2) { diff --git a/web/src/views/xg/xsjcxx/stuRoster/index.vue b/web/src/views/xg/xsjcxx/stuRoster/index.vue index 27d87d5e..49294e37 100644 --- a/web/src/views/xg/xsjcxx/stuRoster/index.vue +++ b/web/src/views/xg/xsjcxx/stuRoster/index.vue @@ -63,7 +63,7 @@
搜索 重置 - 高级检索 + 高级检索
@@ -245,7 +245,7 @@