| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182 |
- package service
- import (
- "errors"
- "fmt"
- "time"
- "wails-app/internal/dao"
- "wails-app/internal/global"
- )
- var (
- // ErrFeeConfigNotFound 车辆类型未配置收费规则(不再隐式按 0.1 元/分钟兜底收费)。
- ErrFeeConfigNotFound = errors.New("车辆类型未配置收费规则")
- // ErrFeeConfigInvalid 收费配置数值非法。
- ErrFeeConfigInvalid = errors.New("收费配置无效")
- )
- const minutesPerDay = 1440
- // CalculateFee is the shared parking-fee calculation used by vehicle exit
- // flows and the digital-ticket live preview.
- //
- // 规则:
- // - 该车辆类型必须存在收费配置,否则返回错误(避免隐式兜底造成错误收费)。
- // - 免费时长(StartTime)内费用为 0;超出后按起步价 + 单位时长累加,不足一个单位按一个单位。
- // - 配置了日封顶(DailyMaxFee)时,费用不超过"跨天数 × 日封顶",跨天向上取整,
- // 修复多日停车只应用一次日封顶的问题。
- // - VIP 免费优先于折扣;折扣应用于封顶后的金额。
- func CalculateFee(vehicle *dao.Vehicle, stayTime int64) (float64, error) {
- if vehicle == nil || global.GVA_DB == nil {
- return 0, errors.New("计费上下文缺失")
- }
- if stayTime < 0 {
- stayTime = 0
- }
- var feeConfig dao.FeeConfig
- if err := global.GVA_DB.Where("vehicle_type_id = ?", vehicle.VehicleTypeID).First(&feeConfig).Error; err != nil {
- return 0, fmt.Errorf("%w: 车辆类型ID=%d", ErrFeeConfigNotFound, vehicle.VehicleTypeID)
- }
- if feeConfig.UnitTime <= 0 {
- return 0, fmt.Errorf("%w: 单位时间必须大于0", ErrFeeConfigInvalid)
- }
- isVIP := false
- if vehicle.OwnerId != nil {
- var owner dao.Owner
- if err := global.GVA_DB.First(&owner, *vehicle.OwnerId).Error; err == nil {
- now := time.Now()
- isVIP = owner.IsVip && !owner.VipExpireTime.IsZero() && owner.VipExpireTime.After(now)
- }
- }
- if isVIP && feeConfig.IsVIPFree {
- return 0, nil
- }
- if stayTime <= int64(feeConfig.StartTime) {
- return 0, nil
- }
- extraTime := stayTime - int64(feeConfig.StartTime)
- extraUnits := extraTime / int64(feeConfig.UnitTime)
- if extraTime%int64(feeConfig.UnitTime) > 0 {
- extraUnits++
- }
- fee := feeConfig.StartFee + float64(extraUnits)*feeConfig.UnitFee
- if feeConfig.DailyMaxFee > 0 {
- days := stayTime / minutesPerDay
- if stayTime%minutesPerDay > 0 {
- days++
- }
- capFee := float64(days) * feeConfig.DailyMaxFee
- if fee > capFee {
- fee = capFee
- }
- }
- if isVIP {
- fee *= feeConfig.VIPDiscount
- }
- return fee, nil
- }
|