package service import ( "fmt" "testing" "time" "github.com/glebarez/sqlite" "github.com/stretchr/testify/require" "gorm.io/gorm" "wails-app/internal/dao" "wails-app/internal/global" ) func setupPaymentServiceTest(t *testing.T) *PaymentService { dsn := fmt.Sprintf("file:payment-service-%d?mode=memory&cache=shared", time.Now().UnixNano()) db, err := gorm.Open(sqlite.Open(dsn), &gorm.Config{}) require.NoError(t, err) sqlDB, err := db.DB() require.NoError(t, err) sqlDB.SetMaxOpenConns(16) t.Cleanup(func() { _ = sqlDB.Close() if global.GVA_DB == db { global.GVA_DB = nil } }) global.GVA_DB = db require.NoError(t, db.AutoMigrate(&dao.PaymentMethodConfig{}, &dao.PaymentEntryConfig{}, &dao.PaymentEntryMethod{}, &dao.VehicleRecord{}, &dao.PaymentRecord{})) require.NoError(t, db.Create(&[]dao.PaymentMethodConfig{ {Code: PaymentCash, Name: "人工支付", InputMode: InputModeManualAmount, Scenes: []string{SceneCounter}, Enabled: true, Sort: 10, IsSystem: true}, {Code: PaymentPOS, Name: "POS机支付", InputMode: InputModeConfirm, Scenes: []string{SceneCounter}, Enabled: true, Sort: 20, IsSystem: true}, {Code: PaymentTicketQR, Name: "小票扫码支付", InputMode: InputModeExternal, Scenes: []string{SceneTicket}, Enabled: true, Sort: 30, IsSystem: true}, }).Error) require.NoError(t, db.Create(&[]dao.PaymentEntryConfig{ {Code: EntryCounter, Name: "收费岗亭", Enabled: true, IsSystem: true}, {Code: EntryTicket, Name: "停车小票", Enabled: true, IsSystem: true}, }).Error) require.NoError(t, db.Create(&[]dao.PaymentEntryMethod{ {EntryCode: EntryCounter, MethodCode: PaymentCash, Sort: 10}, {EntryCode: EntryCounter, MethodCode: PaymentPOS, Sort: 20}, {EntryCode: EntryTicket, MethodCode: PaymentCash, Sort: 10}, {EntryCode: EntryTicket, MethodCode: PaymentPOS, Sort: 20}, {EntryCode: EntryTicket, MethodCode: PaymentTicketQR, Sort: 30}, }).Error) return NewPaymentService() } func TestCashCannotBeDisabledOrDeleted(t *testing.T) { svc := setupPaymentServiceTest(t) method, err := svc.repo.GetMethodByCode(nil, PaymentCash) require.NoError(t, err) require.EqualError(t, svc.SetMethodEnabled(method.ID, false), "人工支付是基础功能,不能停用") require.EqualError(t, svc.DeleteMethod(method.ID), "系统内置支付方式不能删除") } func TestPaymentMethodSceneAndAmountValidation(t *testing.T) { svc := setupPaymentServiceTest(t) _, _, err := svc.ValidatePayment(nil, PaymentTicketQR, SceneCounter, 10, 10) require.EqualError(t, err, "支付方式“小票扫码支付”未配置到当前支付入口") _, _, err = svc.ValidatePayment(nil, PaymentCash, SceneCounter, 10, 9.9) require.EqualError(t, err, "实收金额不足") _, paidAmount, err := svc.ValidatePayment(nil, PaymentPOS, SceneCounter, 10, 999) require.NoError(t, err) require.Equal(t, 10.0, paidAmount) _, paidAmount, err = svc.ValidatePayment(nil, PaymentFree, SceneCounter, 10, 0) require.NoError(t, err) require.Zero(t, paidAmount) _, _, err = svc.ValidatePayment(nil, PaymentFree, SceneTicket, 10, 0) require.EqualError(t, err, "免密放行不能用于当前支付场景") } func TestDisabledAndInvalidMethodsAreRejected(t *testing.T) { svc := setupPaymentServiceTest(t) method, err := svc.repo.GetMethodByCode(nil, PaymentPOS) require.NoError(t, err) require.NoError(t, svc.SetMethodEnabled(method.ID, false)) _, _, err = svc.ValidatePayment(nil, PaymentPOS, SceneCounter, 10, 10) require.EqualError(t, err, "支付方式“POS机支付”已停用") err = svc.CreateMethod(&dao.PaymentMethodConfig{Code: "Bad Code", Name: "测试", InputMode: InputModeConfirm, Scenes: []string{SceneCounter}}) require.ErrorContains(t, err, "支付编码必须") } func TestCustomMethodsAreOrderedAndDuplicateCodesRejected(t *testing.T) { svc := setupPaymentServiceTest(t) custom := &dao.PaymentMethodConfig{ Code: "bank_card", Name: "银行卡", InputMode: InputModeConfirm, EntryCodes: []string{EntryCounter}, Enabled: true, Sort: 5, } require.NoError(t, svc.CreateMethod(custom)) methods, err := svc.ListEnabledMethods(SceneCounter) require.NoError(t, err) require.Equal(t, "bank_card", methods[0].Code) duplicate := &dao.PaymentMethodConfig{ Code: "bank_card", Name: "重复编码", InputMode: InputModeConfirm, EntryCodes: []string{EntryCounter}, Enabled: true, } require.EqualError(t, svc.CreateMethod(duplicate), "支付编码已存在") }