| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188 |
- package service
- import (
- "errors"
- "fmt"
- "time"
- "wails-app/internal/dao"
- "wails-app/internal/global"
- "wails-app/internal/modules/monthly/repository"
- paymentSvc "wails-app/internal/modules/payment/service"
- "gorm.io/gorm"
- )
- type MonthlyCardService struct {
- repo *repository.MonthlyCardRepository
- }
- func NewMonthlyCardService() *MonthlyCardService {
- return &MonthlyCardService{repo: &repository.MonthlyCardRepository{}}
- }
- func cardDays(cardType string) int {
- switch cardType {
- case "month":
- return 30
- case "quarter":
- return 90
- case "year":
- return 365
- default:
- return 30
- }
- }
- // Create 办理月卡:月卡、白名单、支付流水在同一事务中,任一步失败整体回滚。
- // Create 办理月卡。effectiveDate 可选,未传入时兼容旧调用并使用当天。
- func (s *MonthlyCardService) Create(vehicleID uint, cardType string, fee float64, operatorID uint, remark string, effectiveDate ...time.Time) (*dao.MonthlyCard, error) {
- existing, err := s.repo.GetByVehicleID(vehicleID)
- if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
- return nil, fmt.Errorf("查询有效月卡失败: %w", err)
- }
- if existing != nil {
- return nil, errors.New("该车辆已有有效月卡,请先退卡或等过期后续费")
- }
- var vehicle dao.Vehicle
- if err := global.GVA_DB.First(&vehicle, vehicleID).Error; err != nil {
- return nil, errors.New("车辆不存在")
- }
- now := time.Now()
- startDate := normalizeDate(now)
- if len(effectiveDate) > 0 && !effectiveDate[0].IsZero() {
- startDate = normalizeDate(effectiveDate[0])
- }
- if startDate.Before(normalizeDate(now)) {
- return nil, errors.New("生效日期不能早于今天")
- }
- endDate := startDate.AddDate(0, 0, cardDays(cardType))
- card := &dao.MonthlyCard{
- VehicleID: vehicleID, CardType: cardType,
- StartDate: startDate, EndDate: endDate,
- Fee: fee, PaymentStatus: "paid", OperatorID: operatorID, Remark: remark,
- }
- err = global.GVA_DB.Transaction(func(tx *gorm.DB) error {
- if err := tx.Create(card).Error; err != nil {
- return fmt.Errorf("创建月卡失败: %w", err)
- }
- shortlist := dao.Shortlist{
- VehicleId: int(vehicleID),
- ListType: "白名单",
- ExpirationTime: &endDate,
- }
- if err := tx.Create(&shortlist).Error; err != nil {
- return fmt.Errorf("写入白名单失败: %w", err)
- }
- payRecord := dao.PaymentRecord{
- RecordID: 0, PaymentEntry: paymentSvc.EntryCounter, PaymentMethod: paymentSvc.PaymentCash,
- Amount: fee, PaidAmount: fee, ChangeAmount: 0,
- OperatorID: operatorID, PaidAt: now,
- Remark: fmt.Sprintf("包月卡办理(%s)", cardType),
- }
- if err := tx.Create(&payRecord).Error; err != nil {
- return fmt.Errorf("创建支付流水失败: %w", err)
- }
- return nil
- })
- if err != nil {
- return nil, err
- }
- return card, nil
- }
- // Renew 续费:未过期卡从原到期日顺延,已过期卡从今天起算;
- // 月卡、白名单、支付流水在同一事务中,任一步失败整体回滚。
- // Renew 续费。生效日期必须不早于今天,且有效月卡不能早于原到期日续费。
- func (s *MonthlyCardService) Renew(cardID uint, cardType string, fee float64, operatorID uint, effectiveDate ...time.Time) error {
- return global.GVA_DB.Transaction(func(tx *gorm.DB) error {
- var card dao.MonthlyCard
- if err := tx.First(&card, cardID).Error; err != nil {
- return errors.New("月卡记录不存在")
- }
- if card.PaymentStatus != "paid" {
- return errors.New("月卡已退卡,无法续费")
- }
- now := time.Now()
- startDate := normalizeDate(now)
- hasEffectiveDate := len(effectiveDate) > 0 && !effectiveDate[0].IsZero()
- if hasEffectiveDate {
- startDate = normalizeDate(effectiveDate[0])
- if startDate.Before(normalizeDate(now)) {
- return errors.New("生效日期不能早于今天")
- }
- if card.EndDate.After(startDate) && card.EndDate.After(now) {
- return errors.New("有效月卡的生效日期不能早于原到期日")
- }
- } else {
- // 兼容旧的内部调用:有效卡从原到期日顺延,过期卡从当前时间起算。
- startDate = card.EndDate
- if startDate.Before(now) {
- startDate = now
- }
- }
- newEnd := startDate.AddDate(0, 0, cardDays(cardType))
- card.EndDate = newEnd
- if hasEffectiveDate {
- card.StartDate = startDate
- }
- card.Fee += fee
- if err := tx.Save(&card).Error; err != nil {
- return fmt.Errorf("更新月卡失败: %w", err)
- }
- if err := tx.Model(&dao.Shortlist{}).
- Where("vehicle_id = ? AND list_type = ?", card.VehicleID, "白名单").
- Update("expiration_time", newEnd).Error; err != nil {
- return fmt.Errorf("更新白名单失败: %w", err)
- }
- payRecord := dao.PaymentRecord{
- RecordID: 0, PaymentEntry: paymentSvc.EntryCounter, PaymentMethod: paymentSvc.PaymentCash,
- Amount: fee, PaidAmount: fee, ChangeAmount: 0,
- OperatorID: operatorID, PaidAt: now,
- Remark: fmt.Sprintf("包月卡续费(%s)", cardType),
- }
- if err := tx.Create(&payRecord).Error; err != nil {
- return fmt.Errorf("创建支付流水失败: %w", err)
- }
- return nil
- })
- }
- // Refund 退卡:标记退款状态与移除白名单在同一事务中,任一步失败整体回滚。
- func normalizeDate(value time.Time) time.Time {
- local := value.In(time.Local)
- return time.Date(local.Year(), local.Month(), local.Day(), 0, 0, 0, 0, time.Local)
- }
- func (s *MonthlyCardService) Refund(cardID uint) error {
- return global.GVA_DB.Transaction(func(tx *gorm.DB) error {
- var card dao.MonthlyCard
- if err := tx.First(&card, cardID).Error; err != nil {
- return errors.New("月卡记录不存在")
- }
- card.PaymentStatus = "refunded"
- if err := tx.Save(&card).Error; err != nil {
- return fmt.Errorf("更新月卡状态失败: %w", err)
- }
- if err := tx.Where("vehicle_id = ? AND list_type = ?", card.VehicleID, "白名单").
- Unscoped().Delete(&dao.Shortlist{}).Error; err != nil {
- return fmt.Errorf("移除白名单失败: %w", err)
- }
- return nil
- })
- }
- func (s *MonthlyCardService) List(q repository.MonthlyCardQuery) ([]repository.MonthlyCardResult, int64, error) {
- return s.repo.List(q)
- }
|