feat(contest): Implement contest scoring and real-time systems - #5
Merged
Conversation
Add columns.ts for the moderation queue view with: - FlagStatus type (PENDING, REVIEWED, RESOLVED, DISMISSED) - ModerationActions interface for callbacks - Terminal-style status and difficulty badges - Column definitions: select, problem, flag_status, flag_reason, flag_reported_by, flag_reported_at, actions
Add new i18n keys for the moderation queue redesign including: - columns.problem for table column header - quickResolve/quickDismiss for batch action buttons - unknownReporter for anonymous reporter display - drawerTitle/drawerDescription for detail drawer - problemDetails/flagInfo/moderationActions for sections - searchPlaceholder/allDifficulties for filters
Refactored ModerationQueueView to use DataTable layout for improved UX: - Replaced card-based layout with DataTable component - Added DataTableToolbar with search and filters (status, difficulty) - Integrated BaseDetailDrawer for viewing/editing individual flags - Added batch moderation dialog for bulk operations - Implemented terminal-style header with stats ticker - Added batch actions bar for selected rows - Used useDebounceFn for debounced search - Added watch for pagination changes - Improved state management with typed refs
Design for console frontend recommendation page featuring: - Independent /recommendations route - Left navigation + content area layout - 4 recommendation scenarios (daily, weak-points, challenge, similar) - Tag filtering functionality Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix API import path to use @/utils/request - Add complete type definitions section - Add error handling in store actions - Fix typo in RecommendationNav template - Add loading/empty states handling - Add component imports for Combobox - Clarify SimilarProblemSearch usage context - Add prerequisites checklist Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Detailed plan with 12 tasks covering: - Types and API layer - Store with unit tests - i18n translations - UI components (ProblemCard, Nav, Filter, Search) - Main view and routing - Sidebar integration - Integration testing Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix v-model on computed property in RecommendationsView - Fix search event handling in SimilarProblemSearch with debounce - Add ComboboxEmpty to TagFilter component - Remove unnecessary emit from RecommendationNav Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add Chinese and English translation files for the recommendation feature: - Sidebar navigation labels - Page title and filter labels - Card display labels - Empty state messages - Search placeholder and no results text
- Add recommendationSidebarData with four navigation items (daily, weak-points, challenge, similar) - Import Sparkles, Target, GitBranch icons for recommendation types - Add isRecommendationContext computed property in AppSidebar - Integrate recommendation sidebar data into context-aware navigation
- Simplify scoring system from Elo to point-based (LeetCode style) - Add support for 6 contest types: weekly, biweekly, monthly, themed, corporate, campus - Design realtime features: live ranking, first-solve announcements, code replay - Add admin features: contest management, scoring rules config, anti-cheat, analytics - Include comprehensive migration strategy with rollback scripts - Add feature flags for zero-downtime deployment - Preserve backward compatibility with existing data
- Chunk 1: Database migration with rollback scripts - Chunk 2: Backend scoring service with rules management - Chunk 3: Realtime WebSocket gateway and service - Chunk 4: Frontend pages (console) with Vue components - Chunk 5: Admin panel scoring rules management - Chunk 6: Anti-cheat similarity detection and analytics - Chunk 7: Integration tests and performance optimization Includes feature flags for gradual rollout and complete rollback strategy.
- Add missing foreign key constraint for problem_id in first_solve_records - Use IF EXISTS syntax for constraint drops in rollback script for better safety
DavidHLP
added a commit
that referenced
this pull request
Jun 14, 2026
ApplicationReadyEvent 监听,白名单非法组合反向断言(reviewer P1-1 指导): - F1 (hard fail): use-port=true + use-judge-outbox=false (port 无 producer → Pending 孤儿) - W1 (soft warn): use-port=true + envelope-version=1 (dispatcher hard-code v2, flag 是 dead config) - W2 (soft warn): use-port=true + cutover-at 过期 (配置陈旧) CI features-off profile 全 false 自然通过。envelope-version 当前无消费者 (JudgeOutboxDispatcher.toEnvelope hard-code v2),ADR-005 §2.4 实施 envelope-aware 写入时 W1 升级为 fail-fast。 Tests: FlagCombinationValidatorTest (6 case) + FeatureFlagsPropertiesBindingTest (3 case) = 9 PASS Refs: task #5 (P1-1) Co-Authored-By: Claude <noreply@anthropic.com>
DavidHLP
added a commit
that referenced
this pull request
Jun 14, 2026
SubmissionServiceImpl.submit 已有 @transactional (line 122), 但 outbox insert 原在 try-catch 中被吞掉, 导致 port 模式下 outbox 是唯一 producer 时 insert 失败会留 Pending 孤儿 (reviewer P1-1 concern). Fix (方案 A+): 去掉 outbox insert 的 try-catch, 让异常自然冒泡. @transactional 默认 RuntimeException 回滚, submission + outbox 同生共死 (ADR-003). 异常冒泡到 GlobalExceptionHandler 返回 5xx. portActive 分支保持 (skip RQueue, 由 outbox dispatcher 取 row enqueue). recordContestSubmissionIfNeeded try-catch 保留 (supplementary). Tests: 55 case 全 PASS (含 SubmissionServiceImplIT Testcontainers) Refs: task #5 (P1-1) Co-Authored-By: Claude <noreply@anthropic.com>
DavidHLP
added a commit
that referenced
this pull request
Jun 18, 2026
F-06 (SubmissionServiceImpl.recordContestSubmissionIfNeeded): timeFromStart was computed from contest.startTime (planned) — wrong for virtual sessions (their clock is participant.startedAt, not the real contest's start). Now picks the right clock per participant.isVirtual; for real contests also uses actualStartTime (fallback to startTime) so submissions before the scheduler triggers RUNNING don't compute negative offsets. F-01 audit (F-01-STATE_MACHINE_AUDIT.md): documents the status-machine audit, finding one violation (#5 finishVirtualContest direct write) and one pre-existing concern (B.1, addressed in R6.5). F-01 fix (ContestSchedulerServiceImpl.finishVirtualContest): routes through participantMapper.bulkFinishByIds(...) instead of updateById(participant) so the 'auto-finish central dispatch' invariant from the audit doc holds. F-07 (ContestServiceImpl.submitContestProblem): virtual participants get a hard deadline at started_at + duration_minutes; previously the auto-finish only kicked in on the next 10s tick, leaving a window where late submissions slipped through. 409 CONTEST_ENDED on miss.
DavidHLP
added a commit
that referenced
this pull request
Jun 18, 2026
新增 EXECUTION_PLAN_R10.md(9 项,3.5-4 人日): - R10.1 per-contest evict 真实现 - R10.2 i18n view 模板接线 - R10.3 i18n key 同步审计 - R10.4 旧 getGlobalRankingsPaginated 签名删除 - R10.5 M1 contestMapper.selectById 优化(独立 PR) - R10.6/10.7 F-01 状态机复核销项(doc-only) - R10.8 F-SEC-10 迁移期 checklist - R10.9 F-SEC-13 log retention 文档 F-01-STATE_MACHINE_AUDIT.md R10 销项: - §3.1 finishVirtualContest 复核通过(已走 bulkFinishByIds:251-255) - §6.4 F-06 timeFromStart 复核通过(三元分支:1387-1395,虚拟用 p.startedAt,真实用 contest.actualStartTime) - §5 结论表 #5 行从'需复核'→'已复核通过' - §7 行动项两行标 ✅ - 头部加 R10 销项声明 REVIEW_V3.md §12 同步: - 'F-01 状态机待复核'→'F-01 状态机(已 R10 销项)'+ 复核证据 - R10 deferred 项加 EXECUTION_PLAN_R10.md 链接 README.md 加 R10 计划到'实施计划 (中游)'表格。 完成模块 v4.3 收口入口;R10 实施 = v4.3 完结。
DavidHLP
added a commit
that referenced
this pull request
Jul 3, 2026
feat(contest): Implement contest scoring and real-time systems
DavidHLP
added a commit
that referenced
this pull request
Jul 3, 2026
管理后台鉴权初始化(CRITICAL #1): - management/src/main.ts: 新增完整 auth bootstrap 流程 - management/src/stores/auth.ts: 新增 setupSessionExpiredCallback + clearUser WebSocket Token 安全泄露(CRITICAL #2): - console/src/lib/socket.ts: 移除 URL ?token= 参数,token 改走 connectHeaders - console/src/composables/contest/useContestSocket.ts: 同上 OAuth 重定向 URL 统一(CRITICAL #4): - management/src/views/auth/components/OAuthButton.vue: 相对路径→绝对路径 Router Guard 错误处理(HIGH #5): - console/src/router/index.ts: catch 块增加 ApiError instanceof 判断 WebSocket Token 传递优化(HIGH #6): - useContestSocket.ts: 已正确从 socket.ts 导入 getTokenFromCookie verifyAuth 绕过 axios 拦截器(HIGH #7): - console/src/utils/auth.ts: fetch() → apiGet() 性能优化(HIGH #9): - console/src/contexts/AuthContext.ts: setInterval(1000) → watch(isAuthenticated) 代码质量提升(MEDIUM): - console/src/stores/auth.ts: 移除 LOGOUT_KEY localStorage 追踪 - 新增 hasAuthCookie() 提前检查,173 行净减少 - clearUser() 重置 status="idle" 允许重新初始化 - 移除所有调试 console.log Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
DavidHLP
added a commit
that referenced
this pull request
Jul 3, 2026
…_ADMIN + rankings 404 + is_deleted filter + Unicode title + @Audited + new ErrorCode + controller XSS guard + mutator tests) 修复 docs/contests-api-test-report.md 报告的 7 处缺陷 (1 HIGH + 6 MEDIUM/LOW): 缺陷 #1 (HIGH) — DELETE 软删除 is_deleted 字段未持久化 - 根因:Contest.isDeleted 字段 @TableLogic + mapper.updateById(entity) 自动忽略逻辑删除字段 - 修复:改用 mapper.update(null, LambdaUpdateWrapper.set(...)) 显式写入 缺陷 #2 — PATCH on FINISHED/RUNNING 比赛无状态守卫 - 修复:service 层加 status guard,仅允许 UPCOMING 修改 - 新增 ErrorCode CONTEST_ONLY_UPDATE_UPCOMING (70006) 区分注册与修改语义 缺陷 #3 — service 层 hasRole("ADMIN") 拒绝 SUPER_ADMIN,与控制器 @PreAuthorize 不一致 - 修复:SecurityUtil 新增 hasAnyRole(String...) 工具方法,ContestServiceImpl 7 处 mutator 替换 缺陷 #4 — CreateContestDTO/UpdateContestDTO title @pattern 拒绝 CJK 字符 - 修复:正则放宽为 [\p{L}\p{N}\s\p{P}]+ 接受 Unicode 字母 - AdminContestController 新增 rejectUnsafeTitleChars helper 显式排除 < 和 > 缺陷 #5 — GET /admin/contest/{id}/rankings 对不存在 contest 返 200+空 - 修复:getAdminContestRanking 入口加 contest 存在性 + is_deleted 校验 缺陷 #6 — addProblem/removeProblem/startContest/endContest 未校验 is_deleted - 修复:4 个 mutator 入口加 .filter(c -> !isDeleted).orElseThrow(COMPETITION_NOT_FOUND) 缺陷 #7 — @Audited 注解缺失导致 audit_logs 无 contest 记录 - 修复:7 个 mutator 方法加 @Audited + AuditContext.setOldValues/setNewValues/setUserId - 复用 AuditActionUtil.CREATE_CONTEST/UPDATE_CONTEST/DELETE_CONTEST 常量 新增测试 ContestServiceImplMutatorTest:5 case 覆盖缺陷 #2/#5/#6 的状态守卫 + 软删除过滤 + 404 路径。 缺陷 #1 的 LambdaUpdateWrapper.getSqlSet() 断言需 @SpringBootTest 上下文,保留为 follow-up, 运行时 curl 验证已确认三列同改 (docs/contests-api-test-report.md §3.5)。 验证: - mvnw compile -B:0 错误 - mvnw test -Dtest=ContestServiceImplMutatorTest:5/5 通过,2.0s - 运行时 curl 5/5 critical 缺陷修复确认 (PM2 重启 ulticode-9001) Artifacts: - Plan: .claude/PRPs/plans/completed/admin-contests-mutator-fixes.plan.md - Report: .claude/PRPs/reports/admin-contests-mutator-fixes-report.md - Review v1: .claude/reviews/admin-contests-mutator-review.md - Review v2: .claude/reviews/admin-contests-mutator-review-v2.md - Test report: docs/contests-api-test-report.md
DavidHLP
added a commit
that referenced
this pull request
Jul 3, 2026
Backend UserVO uses @JsonInclude(NON_NULL), so admin (no bio set)
returns without bio field, and /users/{id} public endpoint
strips email via toPublicVO(). Frontend type declared both as
required, causing type/runtime mismatch.
- console/src/api/user.ts: UserProfile.bio?: string, email?: string
UI components already guard with v-if='profile.bio', no template
changes needed.
Verified: pnpm type-check 0 errors, pnpm test 241/241 pass, pnpm build OK.
Refs: docs/api-test-report-user-userstats.md (Defect #5)
DavidHLP
added a commit
that referenced
this pull request
Jul 3, 2026
… handler, compute real progress Fixes 6 issues from docs/achievement-api-test-report-2026-06-11.md plus 2 newly-discovered typeHandler bugs (Bug #7, Bug #8) surfaced during validation. CRITICAL #1 — escape `key` column via @TableField(value = "`key`") - Achievement.java:19 — affects MyBatis-Plus auto-generated column lists in selectById / selectPage / selectBatchIds. - Without this, every SQL that lists the achievements columns fails with 'You have an error in your SQL syntax ... near key,...'. CRITICAL #2 — escape `key` in @select SQL - AchievementMapper.findByKey:24 — WHERE clause now backticks. - Fixes admin POST /achievements which called findByKey for dedup. HIGH #3 — T2b /achievements/{invalid} now returns 404 (was 500) - Follows from CRITICAL #1+#2: the BadSqlGrammarException no longer fires before the business-level ACHIEVEMENT_NOT_FOUND can be raised. MEDIUM #4 — BadSqlGrammarException → DATABASE_ERROR (50001) - GlobalExceptionHandler:227 — new handler preserves root cause in server log while returning generic 'Database error' to clients. - Pairs with existing MyBatisSystemException / BindingException / DataIntegrityViolationException handlers (no fallback to 50000 'Unknown error'). LOW #5 — getUserAchievements progress is no longer hard-coded to 0 - AchievementServiceImpl:280 — mirrors getUserProgress logic, reads criteria.type from JSON Map, dispatches to SubmissionMapper counter. LOW #6 — re-scoped Javadoc: getUserAchievements and getUserProgress are NOT duplicates (verified UserController /me/achievements/progress uses the latter). Both serve different endpoints with different DTO shapes. Bug #7 — findAllActive missing JacksonTypeHandler - AchievementMapper:46 — @select bypassed @TableField(typeHandler= JacksonTypeHandler.class), returning criteria=null. - Converted to default method via BaseMapper.selectList. Bug #8 — findByKey same typeHandler bypass - Discovered by AchievementMapperIT (L2) on first run; findByKey_criteriaIsDeserializedAsMap failed. - AchievementMapper:24 — also converted to default method. - Latent: only caller (create) never read criteria. Refactors - M1 (review): getUserAchievements extracted buildProgressDTO helper (52 → 16 lines). - L1 (review): findAllActive uses LambdaQueryWrapper<Achievement> method refs instead of string column names. - L3 (review): Javadoc clarified the two service methods. Tests + AchievementMapperSQLGuardTest: 3 reflection-guard tests (CRITICAL #1, CRITICAL #2, no @select on findAllActive/findByKey). + AchievementMapperIT: 5 Testcontainers MySQL 8.0 IT tests verifying JacksonTypeHandler runtime behavior + Bug #7/#8 regression guards. + AchievementServiceTest: 3 progress tests + 2 new mock fields (SubmissionMapper, ContestParticipantMapper). + GlobalExceptionHandlerTest: 2 BadSqlGrammarException tests. Validation - compile / package: BUILD SUCCESS - Unit tests: 36/36 pass - Testcontainers IT: 5/5 pass - 8 curl integration tests on worktree:9091: all match expected HTTP codes (200/400/404/409) and real progress values (admin 6 accepted problems → progress=6 for all 3 rows). Refs: docs/achievement-api-test-report-2026-06-11.md, .claude/PRPs/plans/completed/achievement-api-fixes.plan.md, .claude/PRPs/reports/achievement-api-fixes-report.md, .claude/reviews/achievement-api-fixes-review.md
DavidHLP
added a commit
that referenced
this pull request
Jul 3, 2026
…dation Merges the fix/achievement-api worktree changes: - CRITICAL #1, #2 (MySQL reserved word `key`) - HIGH #3 (T2b 404) - MEDIUM #4 (BadSqlGrammarException handler) - LOW #5 (real progress computation) - LOW #6 (Javadoc re-scoped) - Bug #7 (findAllActive typeHandler) - Bug #8 (findByKey typeHandler, IT-discovered) M1/L1/L2/L3/L4 from .claude/reviews/achievement-api-fixes-review.md all addressed. Test coverage: 36 unit + 5 Testcontainers IT.
DavidHLP
added a commit
that referenced
this pull request
Jul 3, 2026
…nt dispatch - SubmissionServiceImpl: 2 dispatch sites (updateSubmissionResult + triggerPostVerdictSideEffects) flag-gated to use SubmissionCompletedIntent. Legacy path is the ADR-004 §2.5 fallback; new path is the new dispatcher. - AchievementNotificationListener: flag-gated to dispatch AchievementEarnedIntent. The legacy path keeps the manual realtimeService push; the new path relies on WebSocketNotificationChannel to emit the BadgeEarnedPayload (no double-push). - FollowServiceImpl: flag-gated to dispatch FollowReceivedIntent. - ContestScheduler.sendContestReminder: flag-gated to dispatch ContestStartingIntent. The 24h and 1h reminders naturally get separate intent ids via ContestStartingIntent.intentId() derivation. - NotificationDispatchService + Impl: @deprecated. Javadoc points to the new dispatcher. Removal is M4d's job. Test updates (4 files): - AchievementNotificationListenerTest, FollowServiceImplTest, SubmissionServiceImplTest, SubmissionServiceImplIT: add @mock for the new NotificationDispatcher field, mock FeatureFlagsProperties with lenient().when(isUseNotificationIntent).thenReturn(false) so existing assertions on the legacy dispatch path keep working. Grep audit (ADR-004 §4 #5): business modules no longer import EmailService or RealtimeService. mvn compile + 4 unit test classes: green. Refs: docs/adr/ADR-004-notification-intents.md §2.4, §2.6, §4 #5.
DavidHLP
added a commit
that referenced
this pull request
Jul 3, 2026
- docs/adr/ADR-004-notification-intents.md: status Proposed → Accepted; §4 validation checkboxes all checked; commit list added in header. - docs/adr/README.md: ADR-004 row updated to 'Accepted (2026-06-13)' with the M4a-M4d commit hashes. M4 status: - M4a: e38e340 (ledger + intent records + dispatcher skeleton) - M4b: bf02f48 (3 channel impls + EmailTemplates) - M4c: 9ecf10e (migrate 4 callers to typed intent dispatch) - M4d: 62a4dca (dispatcher contract + idempotency + perf + per-channel tests) Grep audit (ADR-004 §4 #5): business modules no longer import EmailService or RealtimeService directly. Legacy NotificationDispatchService + Impl kept with @deprecated; removal is a follow-up chore once the useNotificationIntent flag has been on in production for ≥1 cycle.
DavidHLP
added a commit
that referenced
this pull request
Jul 3, 2026
…nedAt) 2 review findings about intentId() collisions on the ledger UNIQUE (intent_id, channel_id) constraint: - SubmissionCompletedIntent: factory of() now throws IllegalStateException when submission.getGeneration() is null, instead of silently falling back to 0L. Two distinct submissions (one real g=0, one with null generation) would have produced identical intentIds, and the second dispatch would have been silently dropped by tryClaim returning 0. The null-generation case indicates a hydration bug, not user data, so fail-fast is correct. Fixes finding #5. - AchievementEarnedIntent: added earnedAt field, intentId now includes the millisecond timestamp (:at<epochMs>). Without this, a re-issued event (tier-up promotion, system re-trigger) for the same achievement collapsed under the existing intentId and was silently dropped on every channel. The natural key for achievement notifications now includes the issue time, matching the AchievementEarnedEvent one-publish-per-earn invariant. Fixes finding #6. Test updates: 5 test files updated to pass the new earnedAt=Instant.now() arg. The sample intents in the channel contract test and the per-channel tests use the new field; the legacy follow-system test path is unchanged (FollowReceivedIntent has no earnedAt). mvn test on the notification/follow/submission/email slice: green. Refs: docs/adr/ADR-004-notification-intents.md (M4d-1 review findings #5, #6).
DavidHLP
added a commit
that referenced
this pull request
Jul 3, 2026
ADR-005 §4 #5: 之前 DB 迁移文件名完全靠人工/CLAUDE.md 文档约束, 零自动化. 本次新增 flyway-filename-lint job, 在 init-db/migrations 下扫所有 V*.sql 文件, 校验符合 V<14-digit-ts>__<desc>.sql 格式 (兼容 V20260604_110000 与 V20260604110000 两种时间戳写法). 正则在本地 dry-run: 现有 28 个迁移文件 0 fail. 故意改名会触发 ::error. Refs: docs/adr/ADR-005-rolling-deploy-playbook.md §4 Row #5
DavidHLP
added a commit
that referenced
this pull request
Jul 3, 2026
…rationProperties) Pre-P0-2: FeatureFlagsProperties 用 flat 字段 judgeQueueUsePort/judgeQueueEnvelopeVersion, 但 application.yml 用 nested key app.features.judge-queue.use-port。Spring Binder 的 kebab→camel 转换只作用于叶节点名,不作用于路径段,导致 YAML override 永远不生效, M3c cutover flag 静默卡在 false。 Fix: 改为 @NestedConfigurationProperty JudgeQueue inner class (usePort/envelopeVersion/cutoverAt), 与 app.features 整体 prefix 一致,方便后续 FlagCombinationValidator 单 bean 聚合校验。 cutover-at (F13 watermark) 之前被 Binder 静默忽略,本轮一并补字段。 调用方更新 (2 处): - JudgeWorkerProcessor.pollAndProcessFromPort - SubmissionServiceImpl.submit (portActive) Note: JudgeOutboxDispatcher 仍用 @value 直接读 YAML key (reviewer 补充 #1), task #7 统一切到 FeatureFlagsProperties,避免 YAML 双解析。 @ConditionalOnProperty 4 处 (RedissonStreamsJudgeQueueAdapter/QueueConfig/...) 不动 (reviewer 补充 #2 — 它们直接读 YAML key,P0-2 只修 API 暴露层)。 Tests: FeatureFlagsPropertiesBindingTest (3 case, 真实 Binder 端到端验证) + 回归 41 case 全 PASS (JudgeWorkerProcessorTest + P0-1 测试集)。 Refs: task #5 (P0-2) Co-Authored-By: Claude <noreply@anthropic.com>
DavidHLP
added a commit
that referenced
this pull request
Jul 3, 2026
ApplicationReadyEvent 监听,白名单非法组合反向断言(reviewer P1-1 指导): - F1 (hard fail): use-port=true + use-judge-outbox=false (port 无 producer → Pending 孤儿) - W1 (soft warn): use-port=true + envelope-version=1 (dispatcher hard-code v2, flag 是 dead config) - W2 (soft warn): use-port=true + cutover-at 过期 (配置陈旧) CI features-off profile 全 false 自然通过。envelope-version 当前无消费者 (JudgeOutboxDispatcher.toEnvelope hard-code v2),ADR-005 §2.4 实施 envelope-aware 写入时 W1 升级为 fail-fast。 Tests: FlagCombinationValidatorTest (6 case) + FeatureFlagsPropertiesBindingTest (3 case) = 9 PASS Refs: task #5 (P1-1) Co-Authored-By: Claude <noreply@anthropic.com>
DavidHLP
added a commit
that referenced
this pull request
Jul 3, 2026
SubmissionServiceImpl.submit 已有 @transactional (line 122), 但 outbox insert 原在 try-catch 中被吞掉, 导致 port 模式下 outbox 是唯一 producer 时 insert 失败会留 Pending 孤儿 (reviewer P1-1 concern). Fix (方案 A+): 去掉 outbox insert 的 try-catch, 让异常自然冒泡. @transactional 默认 RuntimeException 回滚, submission + outbox 同生共死 (ADR-003). 异常冒泡到 GlobalExceptionHandler 返回 5xx. portActive 分支保持 (skip RQueue, 由 outbox dispatcher 取 row enqueue). recordContestSubmissionIfNeeded try-catch 保留 (supplementary). Tests: 55 case 全 PASS (含 SubmissionServiceImplIT Testcontainers) Refs: task #5 (P1-1) Co-Authored-By: Claude <noreply@anthropic.com>
DavidHLP
added a commit
that referenced
this pull request
Jul 3, 2026
F-06 (SubmissionServiceImpl.recordContestSubmissionIfNeeded): timeFromStart was computed from contest.startTime (planned) — wrong for virtual sessions (their clock is participant.startedAt, not the real contest's start). Now picks the right clock per participant.isVirtual; for real contests also uses actualStartTime (fallback to startTime) so submissions before the scheduler triggers RUNNING don't compute negative offsets. F-01 audit (F-01-STATE_MACHINE_AUDIT.md): documents the status-machine audit, finding one violation (#5 finishVirtualContest direct write) and one pre-existing concern (B.1, addressed in R6.5). F-01 fix (ContestSchedulerServiceImpl.finishVirtualContest): routes through participantMapper.bulkFinishByIds(...) instead of updateById(participant) so the 'auto-finish central dispatch' invariant from the audit doc holds. F-07 (ContestServiceImpl.submitContestProblem): virtual participants get a hard deadline at started_at + duration_minutes; previously the auto-finish only kicked in on the next 10s tick, leaving a window where late submissions slipped through. 409 CONTEST_ENDED on miss.
DavidHLP
added a commit
that referenced
this pull request
Jul 3, 2026
新增 EXECUTION_PLAN_R10.md(9 项,3.5-4 人日): - R10.1 per-contest evict 真实现 - R10.2 i18n view 模板接线 - R10.3 i18n key 同步审计 - R10.4 旧 getGlobalRankingsPaginated 签名删除 - R10.5 M1 contestMapper.selectById 优化(独立 PR) - R10.6/10.7 F-01 状态机复核销项(doc-only) - R10.8 F-SEC-10 迁移期 checklist - R10.9 F-SEC-13 log retention 文档 F-01-STATE_MACHINE_AUDIT.md R10 销项: - §3.1 finishVirtualContest 复核通过(已走 bulkFinishByIds:251-255) - §6.4 F-06 timeFromStart 复核通过(三元分支:1387-1395,虚拟用 p.startedAt,真实用 contest.actualStartTime) - §5 结论表 #5 行从'需复核'→'已复核通过' - §7 行动项两行标 ✅ - 头部加 R10 销项声明 REVIEW_V3.md §12 同步: - 'F-01 状态机待复核'→'F-01 状态机(已 R10 销项)'+ 复核证据 - R10 deferred 项加 EXECUTION_PLAN_R10.md 链接 README.md 加 R10 计划到'实施计划 (中游)'表格。 完成模块 v4.3 收口入口;R10 实施 = v4.3 完结。
DavidHLP
added a commit
that referenced
this pull request
Jul 4, 2026
把 OAuth state 生命周期(安全不变量 #5:HttpOnly cookie 绑定 + Redis 原子消费)从 OAuthService 提取为独立 deep module OAuthStatePort + OAuthStateModule,与既有 AuthSessionPort 并列。 - 新增 OAuthStatePort(issueState/validateAndConsume) + OAuthStateModule(@component) - OAuthService 瘦身:移除 state 相关 6 个 helper + 3 个 dead dependency (CsrfService/StringRedisTemplate/JwtProperties 注入但未用) + dead private method getCookie + 4 个 public 方法的 dead param HttpServletRequest - AuthController github/google callback 同步移除 dead HttpServletRequest 形参 - 删除 OAuthServiceTest(state 契约迁移),新建 OAuthStateModuleTest(7 个契约测试) state Redis key/TTL/cookie Path/SameSite/Secure 耦合 + getAndDelete 原子消费行为与原代码逐字一致;HTTP 端点签名不变(仅 Java 形参清理)。
DavidHLP
added a commit
that referenced
this pull request
Jul 10, 2026
…rough port Architecture review candidate #5 (top recommendation): AdminProblemServiceImpl used to @Autowired ProblemService (problem module) and SubmissionMapper (submission module) directly, violating the port pattern established across ADR-0001/06/07/08 and 46 existing port/projection interfaces. Create AdminProblemPort interface (owned by admin) with 6 methods: - Read: toVO, findBySlug, findSubmissionsByProblemId - Write: publishProblem, unpublishProblem, deleteProblem Create AdminProblemAdapter (@component) that delegates to the existing ProblemService and SubmissionMapper — same runtime behavior, but admin no longer imports those types at compile time. AdminProblemServiceImpl now depends on AdminProblemPort only. Test surface shrinks: one port mock replaces two cross-module service mocks. Verified: mvn compile succeeds. Pre-existing test failures in AdminProblemListControllerTest (undefined DTO setters) are unrelated.
DavidHLP
added a commit
that referenced
this pull request
Jul 10, 2026
…imitAspect Architecture review candidate #5 — the @ratelimit annotation carried 3 fields (key/limit/period) but its enforcing aspect embedded: - the Redis Lua script (INCR + EXPIRE) - the 'rate-limit:' key prefix - direct StringRedisTemplate injection Callers (155 @ratelimit sites across 33 controllers) could not see what they were getting; the aspect could not be unit-tested without Redis. Deep-module extraction — splits the aspect's two responsibilities: 1. Request-context key generation (placeholder substitution, user/IP detection) STAYS in RateLimitAspect. This legitimately needs the ProceedingJoinPoint and HttpServletRequest — it cannot move behind the port. 2. Rate-check mechanism MOVES behind a new port: common/ratelimiter/RateLimiter (interface, 1 method) common/ratelimiter/AcquisitionVerdict (record: allowed + retryAfter) common/ratelimiter/RedisRateLimiter (@component, prod adapter) common/ratelimiter/InMemoryRateLimiter (NOT a bean; test adapter) Two adapters justify the seam (architecture-review glossary): - RedisRateLimiter: atomic INCR+EXPIRE Lua script (preserves exact pre-refactor behavior — the 'sliding-since-last-activity' window is documented in javadoc as out-of-scope for this extraction) - InMemoryRateLimiter: synchronized ConcurrentHashMap; constructed manually in unit tests, NOT auto-registered as a bean RateLimitAspect now injects RateLimiter and delegates the actual check. 155 @ratelimit sites untouched — annotation contract is unchanged. Verified: ./mvnw compile clean, AuditPolicyCoverageTest 2/2 pass. RateLimitIT (Testcontainers) requires Docker daemon to run, unchanged by this refactor — it tests behavior, which is preserved. Wins (glossary terms): locality — rate-limit policy in one module; leverage — aspect drops to a delegator; tests run without Redis; two adapters justify the seam.
DavidHLP
added a commit
that referenced
this pull request
Jul 10, 2026
#1 SecurityUtil → CurrentUserProvider port - New CurrentUserProvider interface in common/auth/ - SecurityCurrentUserProvider @component adapter (wraps SecurityContextHolder) - 37 files migrated from static SecurityUtil calls to injected interface - Eliminates duplicated userId==null guards across controller layer #2 DashboardMapper+Service → DashboardStatsProjection - New DashboardStatsProjection interface + DefaultDashboardStatsProjection - Absorbs 7 private sub-aggregators + 6 mapper default methods - DashboardService + DashboardServiceImpl deleted - DashboardController now injects the projection directly - Mirrors ADR-0011 *Projection pattern #3 AdminCommentService → CommentModerator polymorphic seam - New CommentModerator interface with ForumCommentModerator + SolutionCommentModerator - AdminCommentServiceImpl refactored to thin router - Eliminates 5x duplicated forum/solution type-string switch #5 SubmissionMapper.calculateStreak → SubmissionStreakCalculator - New SubmissionStreakCalculator interface + JdbcSubmissionStreakCalculator - Streak algorithm (recursive CTE) now behind a JVM-testable interface - 3 production callers redirected + 3 test classes updated #7 Frontend wrapper cleanup - 8 re-export wrapper files deleted (markdown, useTheme, useTypographyDensity, cn) - tsconfig path remaps @/lib/utils → @/shared/auth-core/src/utils - shared/sidebar-menu cn() consolidated to import from auth-core - LOCALE_HEADER_KEY duplicate declarations removed #8 OAuthService → OAuthClient port + adapters - New OAuthClient interface + OAuthTokenResponse/OAuthUserInfo DTOs - GithubOAuthClient + GoogleOAuthClient @component adapters - OAuthService refactored to thin coordinator (221→195 LoC) - 11 unit tests covering dispatch, callback, DB upsert Production code compiles (BUILD SUCCESS). 53 test compilation errors from constructor signature changes — mechanical @mock field additions. Candidates #4 (analytics projections) and #6 (contest.ts split) pending.
DavidHLP
added a commit
that referenced
this pull request
Jul 10, 2026
… casing leak Arch review #5. Production callers of @ulticode/domain-types import only PageResult; the Problem/Contest/Comment DTOs are declared but unused (a "fake canonical" superset). - Problem: drop `acceptance_rate` (snake) — backend serves camelCase JSON only (Spring Boot default Jackson, no SNAKE_CASE strategy; verified `acceptanceRate` across ProfileVO/UserStatsDTO/ProblemVO). Keep `acceptanceRate?` (camel). The snake field was transport-casing leakage, never returned by the API. - File doc: declare the contract proven-canonical for PageResult only; the other DTOs are the intended home but not yet consumed — migrate one-by-one as a caller proves the shared shape, and do not balloon into a superset. The parallel Problem definitions in console/types and management/api stay (they are the actual consumers today); this change fixes only the shared contract's casing correctness and narrows its stated scope. Verified: console + management vue-tsc type-check clean.
DavidHLP
added a commit
that referenced
this pull request
Jul 10, 2026
…nagement Arch review 2026-07-10 candidate #5: console/src/types/sandbox.ts and management/src/types/sandbox.ts were byte-identical pure re-exports of @ulticode/sandbox-types with zero callers (grep verified). Apps consume the shared module directly via @/shared/sandbox-types/src/index. The shim was a no-strategy adapter — it just renamed imports, which the path alias already does. Delete is the right call.
DavidHLP
added a commit
that referenced
this pull request
Jul 10, 2026
…view #5) Three auth services (AuthServiceImpl, OAuthService, PasswordResetService) used to build LambdaQueryWrapper against the User entity directly and read password-reset / OAuth columns inline. The same column knowledge and the same query construction appeared in three files; deletion of any one copy regenerated it in the other two. Move all user-table / reset-token / OAuth persistence behind a single AuthAccountPort interface. The default implementation lives in com.ulticode.modules.auth.account.DefaultAuthAccountAdapter and is the only file on the auth side that imports UserMapper. - Login / register / refresh: User lookups go through findByUsername / findByEmail / findById. Insertions and password updates go through create / updatePassword. - Password reset: token storage is hidden behind findPasswordReset / savePasswordReset / clearPasswordReset / findUsersWithActivePasswordReset — the auth service no longer knows the column names tokenHash / expiresAt. - OAuth: the email match is the only persistence step the auth service knows about; the adapter decides whether to look it up by email or by a future identity table. The auth side keeps its existing AuthSessionPort for the post-auth tail; the new AuthAccountPort is purely the pre-auth persistence seam.
DavidHLP
added a commit
that referenced
this pull request
Jul 10, 2026
…pace (arch-review #5) ForumPostDetailView and CommentDetailView each inlined the same detail lifecycle: isInitialLoad, the isLoaded staggered-reveal setTimeout, the onMounted fetch, a loadData() wrapper, and the action-refresh pattern (call loadData after a mutation). Detail correctness depended on each view remembering which reads to refresh. Concentrate that lifecycle in one deep module — useDetailWorkspace (isInitialLoad / isLoaded / refresh) — mirroring the useDataTable collection-workspace pattern. Each view becomes an adapter supplying its entityId + fetch (+ optional secondary refresh: Forum passes audit history). Domain tabs, actions, permissions and dialog state stay in the view. Also drops the redundant double audit-fetch the old unflagPost/handleFlagSuccess paths had. Behaviour preserved: 100ms animation, first-load skeleton gating, silent error handoff to the store, fire-and-forget secondary refresh. Validated: management vue-tsc 0 errors, eslint clean, forum/comments/ composables tests 22/22. Co-Authored-By: Claude <noreply@anthropic.com>
DavidHLP
added a commit
that referenced
this pull request
Jul 16, 2026
Implements candidates from /tmp/architecture-review-1783827842.html: #1 AdminBulkExecutor (admin/bulk): lifts the duplicated per-item try/catch loop + counting + optional existence-guard out of AdminForum/Comment/ Solution/Problem bulk actions into one consumer-owned executor with a canonical ItemOutcome/Run. batchRejudge withdrawn with evidence (rich RejudgeResult shape ≠ void-action aggregated contract). #2 ProblemExportService + ExportPayload: moves format validation, the 10k size cap, CSV header/escaping, and the LocalDate.now() time-leak out of AdminProblemController into a Clock-injected service; controller shrinks to response-writing. #3 LegacyRejudgeStrategy + RejudgePolicy.rejudge dispatcher: moves the 50-line inline legacy rejudge state machine out of AdminSubmissionServiceImpl behind the policy port; fenced vs legacy selection now lives in DefaultRejudgePolicy. AdminSubmissionServiceImpl.rejudge = 3-line dispatch. #4 createAuthStore factory (shared/auth-core): owns the duplicated login/logout/fetchUser/loadPermissions/initialize/clearUser/hasPermission chain + CSRF contract once; management store migrated. Console deferred (already composable-shaped with a richer status state machine). #6 AdminContestReadPort + adapter: ADR-0011 phase 2 (contest) — admin contest reads cross the seam instead of reaching into ContestProblemMapper. #5 (AdminCrudListView wrapper) withdrawn: scaffolded then deleted after code-review flagged it as dead code (0 views wired); the genuine shared seam already lives in DataTable + useDataTable + DataTableToolbar. Validation: mvn compile + test-compile green; 37 targeted admin/submission tests pass (incl. new AdminBulkExecutorTest); management eslint green on auth store. code-review skill run (Standards pass, Spec gaps documented in-code). Co-Authored-By: Claude <noreply@anthropic.com>
DavidHLP
added a commit
that referenced
this pull request
Jul 16, 2026
…fecycle Two architecture-review candidates sharing the ContestScheduler / ContestLifecycleServiceImpl seam. #1 Notification delivery collapse: delete NotificationDispatchService, the createNotification WS-push wrapper, and the useNotificationIntent flag. The four per-user producers (achievement, follow, submission-judged, contest reminder) now dispatch only a typed NotificationIntent; the dispatcher owns preference gating, channel fan-out, and ledger idempotency. Admin broadcast stays a batch exception. #5 Contest lifecycle deepening: ContestScheduler is a thin trigger adapter (285->67 lines); tick()/sendReminders() and the transition policy concentrate in ContestLifecycleServiceImpl behind a @lazy self proxy that preserves @transactional on batchStart/autoFinish (self-invocation would otherwise bypass the proxy). Co-Authored-By: Claude <noreply@anthropic.com>
DavidHLP
pushed a commit
that referenced
this pull request
Jul 16, 2026
Candidate 1 — Submission judging execution - New JudgingLanguageSupport port + DefaultJudgingLanguageSupport. - CodeExecutionHelper.SUPPORTED_LANGUAGES / DFORM_SUPPORTED_LANGUAGES stop leaking to problem/projection and submission/sandbox modules; DefaultProblemProjection, InMemorySandboxAdapter, CodeExecutionService cross the port. Candidate 2 — Problem List workflow - New useProblemListMutations shared composable concentrating the HTTP-call + toast + reload policy. - useProblemLists composable now delegates mutation boilerplate. (useSidebarLists / useProblemListOperations migration deferred — literal toast strings need a raw-mode flag on the helper.) Candidate 3 — Problem solving session (top recommendation) - New useProblemSession deep composable owning navigation, problem load, contest context, layout, panel state, panel component map, and the provide() setup order. - ProblemDetailView shrinks from 309 → 150 lines; zero inline connector components; setup order is internal to the session. Candidate 4 — Notification delivery - New AnnouncementBroadcaster port + DefaultAnnouncementBroadcaster. - AdminNotificationServiceImpl.createSystemNotification delegates the 79-line inline fan-out (target resolution, preference filter, batch row insert) to the broadcaster. ADR-004 §2.3 force-delivery policy preserved; admin broadcast remains the documented exception outside SystemAlertIntent. Candidate 5 — Audit capture - New AuditRecorder port + DefaultAuditRecorder mirroring the @Audited aspect's metadata-capture contract (performer / IP / user-agent) and forwarding to AuditSinkPort. - ForumFlagPolicyImpl, ForumPostFieldToggleImpl, AdminForumServiceImpl.deletePost, UserManagementServiceImpl.bulkDelete migrate off the deprecated AuditHelper. AdminContestMutationServiceImpl dead injection removed. Verification - ./mvnw test: 1610 / 1610 (4 pre-existing skips) - console pnpm test: 436 / 436 - console pnpm type-check: clean 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR implements the contest scoring system with real-time updates, anti-cheat detection, and analytics capabilities.
Backend Features
Scoring System
Real-time Updates
Anti-cheat System
Analytics Service
Frontend Features (Console)
Management Features
Test Plan
Database Migration Required
When database is available, run: