- Drop SetAffiliateService setters and ProvideAuthService /
ProvidePaymentService / ProvideUserHandler wrappers in favor of direct
Wire constructor injection. AffiliateService has no back-edge to
Auth/Payment/User, so the indirection was never required.
- Change RegisterWithVerification's variadic affiliateCode to a fixed
parameter; adjust all call sites.
- Validate aff_code length and charset in BindInviterByCode before any
DB lookup, eliminating timing-side-channel and useless DB roundtrips
on malformed input.
- Make affiliate cache invalidation synchronous; surface Redis errors
via the project logger instead of swallowing them in a detached
goroutine.
- Add an integration test guarding cross-layer tx propagation in
AccrueQuota and a unit test pinning the aff_code format rules.
P0: fix wildcard matching test assertion (config order, not longest prefix)
P0: add TotalRecharged to auth cache snapshot (v5) for percentage threshold
P1: move pricing rules into per-platform sections in ChannelsView
P1: populate account name cache when editing existing channel rules
P1: sanitize email subject headers to prevent SMTP injection
P1: make Redis INCR+EXPIRE idempotent for rate limiting
P1: deep copy FeaturesConfig in Channel.Clone()
P2: clean up stale email="" placeholder comments
P2: replace log.Printf with slog in email_service.go
- Change balance_notify_extra_emails and account_quota_notify_emails
from []string to []NotifyEmailEntry{email, disabled, verified}
- Add per-email enable/disable toggle for both user and admin notifications
- Add PUT /user/notify-email/toggle API endpoint
- Fix critical bug: API key auth cache snapshot missing balance notify
fields (Email, Username, BalanceNotifyEnabled, etc.), causing
notifications to never fire on cached request paths
- Bump cache snapshot version 3→4 to invalidate stale entries
- Add SQL migration 104 to convert old format data
- Backward compatible: parseNotifyEmails auto-detects old/new format
- User balance notify: max 3 emails (primary + 2 extra)
- Admin quota notify: unlimited emails, each with toggle
Balance low notification only supports fixed USD amount threshold.
Percentage threshold is a quota concept, not applicable to balance.
Reverted threshold_type from admin settings, user profile, and all
backend/frontend layers. DB fields (balance_notify_threshold_type,
total_recharged) retained for potential future quota use.
- Add threshold_type field (fixed/percentage) to system and user settings
- Add total_recharged field to users table, auto-incremented on balance credit
- Percentage mode: effective threshold = total_recharged × percentage / 100
- User-level threshold_type inherits from system default when not set
- Update admin settings UI with radio selector (fixed amount / percentage)
- Migration: 102_add_balance_notify_threshold_type.sql
- Use per-recipient context timeout in sendEmails to prevent later
recipients from failing due to shared timeout exhaustion
- Return updated user object from RemoveNotifyEmail handler for
frontend state consistency (matching VerifyNotifyEmail pattern)
Migrate the hardcoded wechat field to the new extensible user
attributes system and improve the users management UI.
Migration:
- Add migration 019 to move wechat data to user_attribute_values
- Remove wechat field from User entity, DTOs, and API contracts
- Clean up wechat-related code from backend and frontend
UsersView enhancements:
- Add text labels to action buttons (Filter Settings, Column Settings,
Attributes Config) for better UX
- Change status column to show colored dot + Chinese text instead of
English text
- Add dynamic attribute columns support with batch loading
- Add column visibility settings with localStorage persistence
- Add filter settings modal for search and filter preferences
- Update i18n translations
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>