service.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. package service
  2. import (
  3. "errors"
  4. "fmt"
  5. "time"
  6. "wails-app/internal/dao"
  7. "wails-app/internal/global"
  8. "wails-app/internal/modules/monthly/repository"
  9. paymentSvc "wails-app/internal/modules/payment/service"
  10. "gorm.io/gorm"
  11. )
  12. type MonthlyCardService struct {
  13. repo *repository.MonthlyCardRepository
  14. }
  15. func NewMonthlyCardService() *MonthlyCardService {
  16. return &MonthlyCardService{repo: &repository.MonthlyCardRepository{}}
  17. }
  18. func cardDays(cardType string) int {
  19. switch cardType {
  20. case "month":
  21. return 30
  22. case "quarter":
  23. return 90
  24. case "year":
  25. return 365
  26. default:
  27. return 30
  28. }
  29. }
  30. // Create 办理月卡:月卡、白名单、支付流水在同一事务中,任一步失败整体回滚。
  31. func (s *MonthlyCardService) Create(vehicleID uint, cardType string, fee float64, operatorID uint, remark string) (*dao.MonthlyCard, error) {
  32. existing, err := s.repo.GetByVehicleID(vehicleID)
  33. if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
  34. return nil, fmt.Errorf("查询有效月卡失败: %w", err)
  35. }
  36. if existing != nil {
  37. return nil, errors.New("该车辆已有有效月卡,请先退卡或等过期后续费")
  38. }
  39. var vehicle dao.Vehicle
  40. if err := global.GVA_DB.First(&vehicle, vehicleID).Error; err != nil {
  41. return nil, errors.New("车辆不存在")
  42. }
  43. now := time.Now()
  44. endDate := now.AddDate(0, 0, cardDays(cardType))
  45. card := &dao.MonthlyCard{
  46. VehicleID: vehicleID, CardType: cardType,
  47. StartDate: now, EndDate: endDate,
  48. Fee: fee, PaymentStatus: "paid", OperatorID: operatorID, Remark: remark,
  49. }
  50. err = global.GVA_DB.Transaction(func(tx *gorm.DB) error {
  51. if err := tx.Create(card).Error; err != nil {
  52. return fmt.Errorf("创建月卡失败: %w", err)
  53. }
  54. shortlist := dao.Shortlist{
  55. VehicleId: int(vehicleID),
  56. ListType: "白名单",
  57. ExpirationTime: &endDate,
  58. }
  59. if err := tx.Create(&shortlist).Error; err != nil {
  60. return fmt.Errorf("写入白名单失败: %w", err)
  61. }
  62. payRecord := dao.PaymentRecord{
  63. RecordID: 0, PaymentEntry: paymentSvc.EntryCounter, PaymentMethod: paymentSvc.PaymentCash,
  64. Amount: fee, PaidAmount: fee, ChangeAmount: 0,
  65. OperatorID: operatorID, PaidAt: now,
  66. Remark: fmt.Sprintf("包月卡办理(%s)", cardType),
  67. }
  68. if err := tx.Create(&payRecord).Error; err != nil {
  69. return fmt.Errorf("创建支付流水失败: %w", err)
  70. }
  71. return nil
  72. })
  73. if err != nil {
  74. return nil, err
  75. }
  76. return card, nil
  77. }
  78. // Renew 续费:未过期卡从原到期日顺延,已过期卡从今天起算;
  79. // 月卡、白名单、支付流水在同一事务中,任一步失败整体回滚。
  80. func (s *MonthlyCardService) Renew(cardID uint, cardType string, fee float64, operatorID uint) error {
  81. return global.GVA_DB.Transaction(func(tx *gorm.DB) error {
  82. var card dao.MonthlyCard
  83. if err := tx.First(&card, cardID).Error; err != nil {
  84. return errors.New("月卡记录不存在")
  85. }
  86. if card.PaymentStatus != "paid" {
  87. return errors.New("月卡已退卡,无法续费")
  88. }
  89. now := time.Now()
  90. // 过期卡从今天起算,避免"续费后仍处于过期状态"
  91. base := card.EndDate
  92. if base.Before(now) {
  93. base = now
  94. }
  95. newEnd := base.AddDate(0, 0, cardDays(cardType))
  96. card.EndDate = newEnd
  97. card.Fee += fee
  98. if err := tx.Save(&card).Error; err != nil {
  99. return fmt.Errorf("更新月卡失败: %w", err)
  100. }
  101. if err := tx.Model(&dao.Shortlist{}).
  102. Where("vehicle_id = ? AND list_type = ?", card.VehicleID, "白名单").
  103. Update("expiration_time", newEnd).Error; err != nil {
  104. return fmt.Errorf("更新白名单失败: %w", err)
  105. }
  106. payRecord := dao.PaymentRecord{
  107. RecordID: 0, PaymentEntry: paymentSvc.EntryCounter, PaymentMethod: paymentSvc.PaymentCash,
  108. Amount: fee, PaidAmount: fee, ChangeAmount: 0,
  109. OperatorID: operatorID, PaidAt: now,
  110. Remark: fmt.Sprintf("包月卡续费(%s)", cardType),
  111. }
  112. if err := tx.Create(&payRecord).Error; err != nil {
  113. return fmt.Errorf("创建支付流水失败: %w", err)
  114. }
  115. return nil
  116. })
  117. }
  118. // Refund 退卡:标记退款状态与移除白名单在同一事务中,任一步失败整体回滚。
  119. func (s *MonthlyCardService) Refund(cardID uint) error {
  120. return global.GVA_DB.Transaction(func(tx *gorm.DB) error {
  121. var card dao.MonthlyCard
  122. if err := tx.First(&card, cardID).Error; err != nil {
  123. return errors.New("月卡记录不存在")
  124. }
  125. card.PaymentStatus = "refunded"
  126. if err := tx.Save(&card).Error; err != nil {
  127. return fmt.Errorf("更新月卡状态失败: %w", err)
  128. }
  129. if err := tx.Where("vehicle_id = ? AND list_type = ?", card.VehicleID, "白名单").
  130. Unscoped().Delete(&dao.Shortlist{}).Error; err != nil {
  131. return fmt.Errorf("移除白名单失败: %w", err)
  132. }
  133. return nil
  134. })
  135. }
  136. func (s *MonthlyCardService) List(q repository.MonthlyCardQuery) ([]repository.MonthlyCardResult, int64, error) {
  137. return s.repo.List(q)
  138. }