Conversation
chore: 删除 api.md 文档,更新 .gitignore feat(request): 添加 silent 静默模式、URL 校验、循环引用保护 refactor(request): token 读取改用 authStore,缓存清理计时器封装为函数 fix(request): GET 空参数处理,响应非对象类型兼容 refactor(utils): 移除未使用的工具函数(formatPhone、debounce、throttle 等) fix(utils/time): formatDateTime 增加无效日期防护,substr 替换为 substring fix(utils/index): formatDate 增加无效日期校验和未来日期处理 feat(constants): 添加 OPERATOR 运营者角色定义 fix(courseTableCache): classId 始终写入缓存 refactor(image): 移除未使用的 generateLocalFileName 函数 feat(semesterCache): 添加 lastKnownCurrent 方法用于学期变更检测 fix(auth store): expireToken 增加重复调用防护和异常捕获 refactor(countdown store): 改用 countdownAPI 模块,修复排序副作用 fix(notifications store): 修复统计数据响应层级取值错误 refactor(schedule store): getter 改用普通函数支持 this 调用,提取颜色分配逻辑 feat(schedule store): 添加 acknowledgeCurrentSemester,优化学期变更检测 fix(schedule store): updateClass 课表刷新失败不阻断绑定流程 refactor(study-tasks store): 改用 studyTaskAPI 模块,studyTasks 重命名为 pendingTasks fix(study-tasks store): 新建/恢复任务插入到列表头部 refactor(app): pinia 实例提前创建,移除冗余参数 fix(admin): refreshData 添加异常捕获,配置值复制兼容对象类型 fix(admin/rbac): 角色数据解析增加容错处理 refactor(admin/reviews): 移除不准确的状态计数,增加日期校验 fix(gpa-calculator): 百分制转五分制改为线性换算公式 feat(TodayCourse): 同时段多门课程折叠展示,添加绑定班级入口 refactor(TodayCourse): 支持 debugDate 属性,定时器改用 onBeforeUnmount 清理 feat(StudyTaskCard): 首次加载并行请求待完成和已完成任务 feat(materials): 添加热度/时间排序切换,API 调用统一添加异常捕获 fix(materials/detail): 详情加载添加 try-catch,分享标题增加兜底值 refactor(schedule page): 功能说明弹窗改为网格布局,补充学期切换等说明 fix(schedule): 修复 courseData 非空判断,eventCenter 事件正确解绑 fix(schedule-bind): 绑定成功后仅更新本地用户信息,添加定时器清理 fix(ScheduleTable): 日期显示隐藏年份,触摸事件增加空值防护,使用 maxWeeks fix(notifications): 投稿入口角色判断修正,内容上限提升至 10000 字 fix(notifications/detail): 分享标题增加可选链兜底 fix(map): 异步操作增加组件卸载检查 fix(discover): 仅登录状态下启动在线人数轮询 feat(final-review): 子题全部作答后自动提交父题,项目数据校验 fix(teacher-reviews): 新增校区必填校验,移除无用定时器 fix(webview): decodeURIComponent 增加异常捕获 fix(profile): 积分显示添加加载和失败状态,改用 useDidShow 刷新 fix(contributions/detail): 移除未实现的删除投稿函数 fix(hero): 移除冗余注释 fix(login): 修复空白字符格式
|
/gemini review |
There was a problem hiding this comment.
Pull request overview
This PR performs a broad cleanup and reliability pass across the client: it refactors API access into dedicated modules, enhances the request utility (silent mode, validation, idempotency/cache robustness), and fixes/optimizes multiple pages and stores (schedule, study-tasks, notifications, materials, etc.).
Changes:
- Refactors the monolithic
src/api/index.jsinto per-domain API modules and updates stores/pages to consume them. - Enhances
src/utils/request.js(silent mode, URL validation, circular-reference-safe request identifier generation, cache cleanup encapsulation, token sourcing viaauthStore). - Improves schedule/semester handling and multiple UI behaviors (event unbinding, timer cleanup, better error handling, and UX tweaks across pages).
Reviewed changes
Copilot reviewed 57 out of 59 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/utils/time.js | Adds invalid-date guarding and safer string handling in date formatting (but see review comment). |
| src/utils/semesterCache.js | Adds last-known-current semester persistence for semester-change detection. |
| src/utils/request.js | Adds silent mode, URL validation, circular reference protection, and cache cleanup encapsulation; token now comes from authStore. |
| src/utils/index.js | Adds invalid date and future-date handling in formatDate; removes unused utilities. |
| src/utils/image.js | Removes unused local filename generator; adjusts cache-cleaning return semantics documentation. |
| src/utils/courseTableCache.js | Always persists classId alongside cached course table entries. |
| src/utils/constants.js | Adds OPERATOR role constant and formatting cleanup. |
| src/stores/study-tasks.js | Switches to studyTaskAPI, renames state fields, adjusts insertion order and list transitions. |
| src/stores/schedule.js | Improves semester-change detection, reduces side effects, adds rollback on failed semester switch, and refactors course color assignment. |
| src/stores/notifications.js | Fixes stats response assignment behavior to match actual return shape. |
| src/stores/countdown.js | Switches to countdownAPI, avoids getter sort side effects, and reuses day-calculation logic. |
| src/stores/auth.js | Adds idempotent/guarded expireToken with try/catch protection. |
| src/pages/webview/index.vue | Adds decodeURIComponent error handling for URL/title params. |
| src/pages/teacher-reviews/index.vue | Removes unused timer cleanup; adds campus required validation and safer date formatting. |
| src/pages/schedule/schedule-bind/index.vue | Clears debounce timer on unmount; updates binding flow to only update local user info. |
| src/pages/schedule/index.vue | Refines help modal UI, fixes courseData emptiness check, adds safer submit guard, and corrects eventCenter unbinding. |
| src/pages/schedule/components/ScheduleTable.vue | Improves date display, touch null-guards, and uses maxWeeks instead of magic number. |
| src/pages/profile/index.vue | Adds loading/failed states for points display and refreshes points on useDidShow. |
| src/pages/notifications/index.vue | Adds unmount cleanup for search timer; adjusts contribution entry gating (but see review comment). |
| src/pages/notifications/detail/index.vue | Adds share title fallbacks via optional chaining. |
| src/pages/notifications/create/index.vue | Raises content length limit to 10000 chars with updated validation message. |
| src/pages/notifications/categories/index.vue | Fixes empty-list handling when computing next sort value. |
| src/pages/materials/index.vue | Adds sorting toggle, and wraps API calls with try/catch/finally for more robust loading state handling. |
| src/pages/materials/detail/index.vue | Adds try/catch with toast on load failure; share title fallbacks. |
| src/pages/map/index.vue | Prevents state updates after unmount for async loading; normalizes image handler signatures. |
| src/pages/login/index.vue | Minor whitespace cleanup. |
| src/pages/home/components/TodayCourse.vue | Adds grouped/expandable rendering for same-slot courses, bind-class shortcut, debug date support, and proper interval cleanup. |
| src/pages/home/components/StudyTaskCard.vue | Parallelizes initial loads (pending/completed/stats), updates to new store getters/fields, and improves pagination state handling. |
| src/pages/hero/index.vue | Removes misleading comments and simplifies response-shape handling. |
| src/pages/gpa-calculator/index.vue | Updates 100→5 GPA conversion to a linear formula with clamping. |
| src/pages/final-review/index.vue | Adds project id validation before navigation. |
| src/pages/final-review/detail/index.vue | Adds sub-question completion auto-submit and optional scroll skipping. |
| src/pages/discover/index.vue | Starts online-count polling only when logged in. |
| src/pages/contributions/detail/index.vue | Removes unimplemented delete contribution function. |
| src/pages/admin/teacher-reviews/index.vue | Removes inaccurate status counts and adds safer date parsing. |
| src/pages/admin/rbac/index.vue | Adds tolerant parsing for roles response format and filters invalid entries. |
| src/pages/admin/heroes/index.vue | Adds error isolation around refresh toast behavior. |
| src/pages/admin/config/index.vue | Adds error isolation around refresh toast behavior; improves clipboard copy for object values. |
| src/app.js | Creates Pinia earlier and simplifies lifecycle hook signatures. |
| src/api/system.js | Adds system/stat API module (health check + online stats). |
| src/api/studyTask.js | Adds study task API module. |
| src/api/review.js | Adds review API module. |
| src/api/rbac.js | Adds RBAC API module. |
| src/api/questions.js | Adds questions API module. |
| src/api/points.js | Adds points API module. |
| src/api/oss.js | Adds OSS API module. |
| src/api/notification.js | Adds notification API module. |
| src/api/material.js | Adds material API module. |
| src/api/index.js | Converts to re-export hub for per-domain API modules. |
| src/api/hero.js | Adds hero API module. |
| src/api/failRate.js | Adds fail-rate API module. |
| src/api/dictionary.js | Adds dictionary API module. |
| src/api/courseTable.js | Adds course-table API module with basic input validation. |
| src/api/countdown.js | Adds countdown API module. |
| src/api/contribution.js | Adds contribution API module. |
| src/api/config.js | Adds config API module with basic input validation. |
| src/api/auth.js | Adds auth + user profile API module. |
| api.md | Removes the API markdown document from the repo. |
| .gitignore | Adds temp/ and CLAUDE.md to ignored paths. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! 本次提交是对项目进行的一次全面的代码审查和修复,旨在提升代码质量、增强应用功能和提高系统稳定性。通过代码重构和优化,提高了代码的可读性和可维护性。同时,对API接口进行了调整,统一添加了异常捕获,增强了系统的健壮性。 Highlights
Changelog
Activity
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request primarily focuses on refactoring API calls into dedicated modules, enhancing state management, and improving UI/UX across various pages, while also addressing several critical issues identified in the review. Key changes include restructuring API calls into separate files for better modularity, updating Pinia stores for countdowns and study tasks to use the new API structure and improve data handling (e.g., fixing side-effects in getters and implementing state rollback on API failures), and refining course schedule display with grouped courses and better semester change detection. Critical fixes address potential security vulnerabilities related to path traversal in API URL construction and open redirects in the webview component, by recommending strict input validation or encoding. Other significant improvements include proper cleanup of event listeners to prevent memory leaks, robust handling of Math.max with empty arrays, and adding error handling and loading states to various data fetching operations across admin and user-facing pages.
| const sortObjectKeys = (obj, seen = new WeakSet()) => { | ||
| if (obj === null || typeof obj !== 'object') { | ||
| return obj | ||
| } | ||
|
|
||
| if (seen.has(obj)) { | ||
| return '[Circular]' | ||
| } | ||
| seen.add(obj) | ||
|
|
||
| if (Array.isArray(obj)) { | ||
| return obj.map(item => sortObjectKeys(item)) | ||
| return obj.map(item => sortObjectKeys(item, seen)) | ||
| } | ||
|
|
||
| return Object.keys(obj) | ||
| .sort() | ||
| .reduce((sorted, key) => { | ||
| sorted[key] = sortObjectKeys(obj[key]) | ||
| sorted[key] = sortObjectKeys(obj[key], seen) | ||
| return sorted | ||
| }, {}) | ||
| } |
| getters: { | ||
| // 按目标日期排序的倒数日列表 | ||
| sortedCountdowns: (state) => { | ||
| return state.countdowns.sort((a, b) => { |
| const reloadScheduleHandler = () => { | ||
| loadCourseData() | ||
| } | ||
|
|
||
| // 页面加载时初始化 | ||
| onMounted(async () => { | ||
| // 监听 reloadSchedule 事件,收到后刷新课表 | ||
| Taro.eventCenter.on('reloadSchedule', () => { | ||
| loadCourseData() | ||
| }) | ||
| Taro.eventCenter.on('reloadSchedule', reloadScheduleHandler) | ||
| }) | ||
|
|
||
| Taro.useUnload(() => { | ||
| Taro.eventCenter.off('reloadSchedule', reloadScheduleHandler) | ||
| }) |
| // 保存旧状态,用于失败回滚 | ||
| const prevSemester = this.semester | ||
| const prevSemesterInfo = this.currentSemesterInfo | ||
| const prevMaxWeeks = this.maxWeeks | ||
| const prevStartDate = this.semesterStartDate | ||
| const prevCurrentWeek = this.currentWeek | ||
|
|
||
| // 更新学期信息并计算当前周数 | ||
| this.updateSemesterInfo(semesterId, true) | ||
|
|
||
| // 保存用户手动选择的学期到独立存储 | ||
| semesterCache.saveUserSelectedSemester(semesterId) | ||
| try { | ||
| courseTableCache.clearCache(semesterId) | ||
| await this.fetchCourseTable(semesterId, true) | ||
| // 成功后才持久化用户选择,避免失败时 storage 残留错误学期 | ||
| semesterCache.saveUserSelectedSemester(semesterId) | ||
| } catch (error) { | ||
| // 请求失败,回滚 Pinia 状态 | ||
| this.semester = prevSemester | ||
| this.currentSemesterInfo = prevSemesterInfo | ||
| this.maxWeeks = prevMaxWeeks | ||
| this.semesterStartDate = prevStartDate | ||
| this.currentWeek = prevCurrentWeek | ||
| throw error | ||
| } |
| getMaterialDetail(md5) { | ||
| return get(`/api/v0/materials/${md5}`) | ||
| }, |
There was a problem hiding this comment.
The application constructs API request URLs by directly concatenating user-supplied identifiers into the URL path. In this file, the getMaterialDetail function concatenates the md5 parameter directly into the URL: return get(`/api/v0/materials/${md5}`). If an attacker provides a malicious string containing path traversal sequences (e.g., ../admin/stats), they can manipulate the request to hit unintended API endpoints. Since these requests are automatically authenticated with the user's token in the request utility, this could lead to unauthorized access to sensitive data or administrative functions if the backend does not strictly validate the path. This pattern is also observed in other API modules such as src/api/config.js, src/api/courseTable.js, and src/api/rbac.js.
Remediation: Implement strict validation for all identifiers used in URL paths. Ensure they match the expected format (e.g., alphanumeric, specific length) and do not contain characters like / or ... Alternatively, use encodeURIComponent for each path component.
| if (params.url) { | ||
| webviewUrl.value = decodeURIComponent(params.url) | ||
| try { | ||
| webviewUrl.value = decodeURIComponent(params.url) | ||
| } catch (e) { | ||
| webviewUrl.value = params.url | ||
| } |
There was a problem hiding this comment.
The webview page takes a url parameter from the router and loads it into a <web-view> component. The implementation includes a fallback where if decodeURIComponent fails, the raw params.url is used. This allows an attacker to craft a link that redirects users to arbitrary external websites. While WeChat mini-programs have a domain allow-list, this can still be exploited for open redirects if any allowed domain has its own redirect vulnerability, or if the allow-list is too broad.
Remediation: Implement a strict allow-list of permitted domains or URLs. Before setting webviewUrl.value, verify that the provided URL belongs to an authorized domain.
| categoryForm.value = { | ||
| name: '', | ||
| sort: Math.max(...categories.value.map(c => c.sort), 0) + 1, | ||
| sort: (categories.value.length > 0 ? Math.max(...categories.value.map(c => c.sort), 0) : 0) + 1, |
There was a problem hiding this comment.
Code Review
这是一个非常出色的 Pull Request,包含了大量的重构、修复和功能增强工作,显著提升了代码库的质量、可维护性和用户体验。
主要亮点包括:
- API 层重构:将庞大的
api/index.js拆分为多个模块化的文件,极大地提高了代码的组织性和可读性。 - 健壮性提升:在多个组件和工具函数中增加了错误处理(
try...catch)、空值校验和边界条件检查,例如在map页面中处理组件卸载后的异步操作,以及在request工具中处理循环引用和非标准响应,有效避免了潜在的运行时错误。 - 性能优化:在
StudyTaskCard中使用Promise.all并行加载数据,提升了首页加载速度。 - 用户体验改善:
TodayCourse组件支持了同一时间段多门课程的折叠展示,非常实用。- 在多个页面(如
materials)的分享功能中增加了标题兜底值。 - 在
profile页面为积分显示增加了加载和失败状态。
- 代码质量:
- 清理了大量未使用的工具函数,减少了代码冗余。
- 在
countdownstore 中,通过创建数组副本修复了sort方法直接修改 state 的问题,遵循了状态管理的最佳实践。 - 在多个组件中,正确地在组件卸载时清理定时器,防止了内存泄漏。
整体而言,这次提交的工作量巨大且质量很高。我只发现了一个小问题,具体见代码评论。合并后,应用的稳定性和可维护性将得到巨大提升。做得非常好!
Note: Security Review did not run due to the size of the PR.
| const showAddModal = ref(false); | ||
| const submitting = ref(false); | ||
| const campusIndex = ref(0); | ||
| const searchTimer = ref(null); |
There was a problem hiding this comment.
你好,这里移除了 searchTimer 的 ref 定义,但在 onSearchInput 函数中仍然使用了 searchTimer.value,这会导致运行时错误。同时,相关的 onUnmounted 钩子也被移除了(L619-623),这可能会导致内存泄漏。
建议恢复定时器相关的逻辑,但可以进行优化。可以将 searchTimer 定义为普通的 let 变量,并在 onUnmounted 中进行清理。
例如:
let searchTimer = null;
const onSearchInput = (e) => {
searchKeyword.value = e.detail.value;
if (searchTimer) {
clearTimeout(searchTimer);
}
searchTimer = setTimeout(async () => {
// ... 搜索逻辑
}, 300);
};
onUnmounted(() => {
if (searchTimer) {
clearTimeout(searchTimer);
}
});这样既能保证功能正常,又能在组件卸载时正确清理资源。
chore: 删除 api.md 文档,更新 .gitignore
feat(request): 添加 silent 静默模式、URL 校验、循环引用保护
refactor(request): token 读取改用 authStore,缓存清理计时器封装为函数
fix(request): GET 空参数处理,响应非对象类型兼容
refactor(utils): 移除未使用的工具函数(formatPhone、debounce、throttle 等)
fix(utils/time): formatDateTime 增加无效日期防护,substr 替换为 substring
fix(utils/index): formatDate 增加无效日期校验和未来日期处理
feat(constants): 添加 OPERATOR 运营者角色定义
fix(courseTableCache): classId 始终写入缓存
refactor(image): 移除未使用的 generateLocalFileName 函数
feat(semesterCache): 添加 lastKnownCurrent 方法用于学期变更检测
fix(auth store): expireToken 增加重复调用防护和异常捕获
refactor(countdown store): 改用 countdownAPI 模块,修复排序副作用
fix(notifications store): 修复统计数据响应层级取值错误
refactor(schedule store): getter 改用普通函数支持 this 调用,提取颜色分配逻辑
feat(schedule store): 添加 acknowledgeCurrentSemester,优化学期变更检测
fix(schedule store): updateClass 课表刷新失败不阻断绑定流程
refactor(study-tasks store): 改用 studyTaskAPI 模块,studyTasks 重命名为 pendingTasks
fix(study-tasks store): 新建/恢复任务插入到列表头部
refactor(app): pinia 实例提前创建,移除冗余参数
fix(admin): refreshData 添加异常捕获,配置值复制兼容对象类型
fix(admin/rbac): 角色数据解析增加容错处理
refactor(admin/reviews): 移除不准确的状态计数,增加日期校验
fix(gpa-calculator): 百分制转五分制改为线性换算公式
feat(TodayCourse): 同时段多门课程折叠展示,添加绑定班级入口
refactor(TodayCourse): 支持 debugDate 属性,定时器改用 onBeforeUnmount 清理
feat(StudyTaskCard): 首次加载并行请求待完成和已完成任务
feat(materials): 添加热度/时间排序切换,API 调用统一添加异常捕获
fix(materials/detail): 详情加载添加 try-catch,分享标题增加兜底值
refactor(schedule page): 功能说明弹窗改为网格布局,补充学期切换等说明
fix(schedule): 修复 courseData 非空判断,eventCenter 事件正确解绑
fix(schedule-bind): 绑定成功后仅更新本地用户信息,添加定时器清理
fix(ScheduleTable): 日期显示隐藏年份,触摸事件增加空值防护,使用 maxWeeks
fix(notifications): 投稿入口角色判断修正,内容上限提升至 10000 字
fix(notifications/detail): 分享标题增加可选链兜底
fix(map): 异步操作增加组件卸载检查
fix(discover): 仅登录状态下启动在线人数轮询
feat(final-review): 子题全部作答后自动提交父题,项目数据校验
fix(teacher-reviews): 新增校区必填校验,移除无用定时器
fix(webview): decodeURIComponent 增加异常捕获
fix(profile): 积分显示添加加载和失败状态,改用 useDidShow 刷新
fix(contributions/detail): 移除未实现的删除投稿函数
fix(hero): 移除冗余注释
fix(login): 修复空白字符格式