diff --git a/internal/store/tasks.go b/internal/store/tasks.go index 0b1ca0e..7260272 100644 --- a/internal/store/tasks.go +++ b/internal/store/tasks.go @@ -205,7 +205,7 @@ func (s *Store) ClaimTask(ctx context.Context, input ClaimTaskInput) (*TaskOrder if wallet.Status != 1 { return nil, ErrAccountRestricted } - if member.AutoDispatch == 1 && wallet.AvailableAmount < input.MinimumAmount { + if member.AutoDispatch == 1 && (wallet.AvailableAmount <= 0 || wallet.AvailableAmount < input.MinimumAmount) { return nil, ErrInsufficientBalance } var pending int64 diff --git a/internal/tasks/service.go b/internal/tasks/service.go index 5060fcb..db00067 100644 --- a/internal/tasks/service.go +++ b/internal/tasks/service.go @@ -206,7 +206,7 @@ func (s *Service) Eligibility(ctx context.Context, tenantID, memberID, hallID in result.reject("LEVEL_NOT_ELIGIBLE", "当前等级不能进入该任务大厅") case row.WalletStatus != 1: result.reject("ACCOUNT_RESTRICTED", "账户状态受限") - case row.AutoDispatch == 1 && row.AvailableAmount < policy.MinimumAmount: + case row.AutoDispatch == 1 && (row.AvailableAmount <= 0 || row.AvailableAmount < policy.MinimumAmount): result.reject("INSUFFICIENT_BALANCE", "可用余额不足") case row.DailyLimit > 0 && row.TodayCount >= row.DailyLimit: result.reject("DAILY_LIMIT_REACHED", "已达当日领取上限") diff --git a/internal/tasks/service_test.go b/internal/tasks/service_test.go index 260e366..26ecf49 100644 --- a/internal/tasks/service_test.go +++ b/internal/tasks/service_test.go @@ -92,6 +92,21 @@ func TestEligibilityAllowsManualDispatchBelowMinimumBalance(t *testing.T) { } } +func TestEligibilityRejectsAutoDispatchWithZeroBalanceWhenMinimumIsZero(t *testing.T) { + repository := &fakeRepository{config: json.RawMessage(`{"features":{"task_enabled":true},"task_minimum_amount":0,"task_business_start_at":"09:00","task_business_end_at":"22:00"}`), + eligibility: &store.TaskEligibility{MemberStatus: 1, AutoDispatch: 1, HallEligible: true, AvailableAmount: 0, WalletStatus: 1, DailyLimit: 5}} + service := New(repository) + service.now = func() time.Time { return time.Date(2026, 10, 9, 10, 0, 0, 0, shanghai) } + + result, err := service.Eligibility(context.Background(), 0, 10, 1) + if err != nil { + t.Fatal(err) + } + if result.Eligible || result.Reason != "INSUFFICIENT_BALANCE" { + t.Fatalf("zero balance must not allow automatic claiming: %#v", result) + } +} + func TestClaimFailsClosedWithoutTaskSwitch(t *testing.T) { repository := &fakeRepository{config: json.RawMessage(`{}`)} service := New(repository)