Files
junhong_cmp_fiber/internal/service/package/utils.go

157 lines
5.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package packagepkg
import (
"time"
"github.com/break/junhong_cmp_fiber/pkg/constants"
)
// CalculateExpiryTime 计算套餐过期时间
// calendarType: 套餐周期类型natural_month=自然月by_day=按天)
// activatedAt: 激活时间
// durationMonths: 套餐时长月数calendar_type=natural_month 时使用)
// durationDays: 套餐天数calendar_type=by_day 时使用,兼容每月折算天数)
// 返回:过期时间(当天 23:59:59
func CalculateExpiryTime(calendarType string, activatedAt time.Time, durationMonths, durationDays int) time.Time {
var expiryDate time.Time
if calendarType == constants.PackageCalendarTypeNaturalMonth {
// 自然月套餐activated_at 月份 + N 个月,月末 23:59:59
// 计算目标年月
targetYear := activatedAt.Year()
targetMonth := activatedAt.Month() + time.Month(durationMonths)
// 处理月份溢出
for targetMonth > 12 {
targetMonth -= 12
targetYear++
}
// 获取目标月份的最后一天下个月的第0天就是本月最后一天
expiryDate = time.Date(targetYear, targetMonth+1, 0, 23, 59, 59, 0, activatedAt.Location())
} else {
// 按天套餐activated_at + N 天23:59:59
effectiveDays := calculateByDayValidityDays(durationMonths, durationDays)
expiryDate = activatedAt.AddDate(0, 0, effectiveDays)
expiryDate = time.Date(expiryDate.Year(), expiryDate.Month(), expiryDate.Day(), 23, 59, 59, 0, expiryDate.Location())
}
return expiryDate
}
// CalculateValidityDays 计算套餐展示有效天数。
// 按天套餐优先返回折算后的总天数;自然月套餐按 baseTime 推算到目标月月末。
func CalculateValidityDays(calendarType string, baseTime time.Time, durationMonths, durationDays int) int {
if calendarType == constants.PackageCalendarTypeNaturalMonth {
return calculateNaturalMonthValidityDays(baseTime, durationMonths)
}
return calculateByDayValidityDays(durationMonths, durationDays)
}
func calculateByDayValidityDays(durationMonths, durationDays int) int {
if durationDays <= 0 {
if durationMonths <= 0 {
return 0
}
return durationMonths * 30
}
if durationMonths > 1 && durationDays <= 31 {
return durationMonths * durationDays
}
return durationDays
}
func calculateNaturalMonthValidityDays(baseTime time.Time, durationMonths int) int {
if baseTime.IsZero() || durationMonths <= 0 {
return 0
}
expiresAt := CalculateExpiryTime(constants.PackageCalendarTypeNaturalMonth, baseTime, durationMonths, 0)
startDate := time.Date(baseTime.Year(), baseTime.Month(), baseTime.Day(), 0, 0, 0, 0, baseTime.Location())
endDate := time.Date(expiresAt.Year(), expiresAt.Month(), expiresAt.Day(), 0, 0, 0, 0, expiresAt.Location())
days := int(endDate.Sub(startDate).Hours()/24) + 1
if days < 0 {
return 0
}
return days
}
// CalculateNextResetTime 计算下次流量重置时间
// dataResetCycle: 流量重置周期daily/monthly/yearly/none
// calendarType: 套餐周期类型natural_month/by_day影响月重置逻辑
// currentTime: 当前时间
// activatedAt: 套餐激活时间(按天套餐月重置时使用)
// 返回下次重置时间00:00:00
func CalculateNextResetTime(dataResetCycle, calendarType string, currentTime, activatedAt time.Time) *time.Time {
if dataResetCycle == constants.PackageDataResetNone {
// 不重置
return nil
}
var nextResetTime time.Time
switch dataResetCycle {
case constants.PackageDataResetDaily:
// 日重置:明天 00:00:00
nextResetTime = time.Date(
currentTime.Year(),
currentTime.Month(),
currentTime.Day()+1,
0, 0, 0, 0,
currentTime.Location(),
)
case constants.PackageDataResetMonthly:
if calendarType == constants.PackageCalendarTypeNaturalMonth {
// 自然月套餐每月1号 00:00:00 重置
year := currentTime.Year()
month := currentTime.Month()
// 如果当前日期 >= 1号即已过重置点则下次重置为下个月1号
if currentTime.Day() >= 1 {
month++
if month > 12 {
month = 1
year++
}
}
nextResetTime = time.Date(year, month, 1, 0, 0, 0, 0, currentTime.Location())
} else {
// 按天套餐从激活日期开始每30天重置一次
// 计算从激活到现在经过了多少个30天周期
daysSinceActivation := int(currentTime.Sub(activatedAt).Hours() / 24)
cyclesPassed := daysSinceActivation / 30
// 下次重置时间 = 激活时间 + (已过周期数+1) * 30天
nextResetTime = activatedAt.AddDate(0, 0, (cyclesPassed+1)*30)
nextResetTime = time.Date(
nextResetTime.Year(),
nextResetTime.Month(),
nextResetTime.Day(),
0, 0, 0, 0,
nextResetTime.Location(),
)
}
case constants.PackageDataResetYearly:
// 年重置每年1月1日 00:00:00
year := currentTime.Year()
// 如果当前日期已经过了1月1日则使用明年
jan1ThisYear := time.Date(year, 1, 1, 0, 0, 0, 0, currentTime.Location())
if currentTime.After(jan1ThisYear) || currentTime.Equal(jan1ThisYear) {
year++
}
nextResetTime = time.Date(year, 1, 1, 0, 0, 0, 0, currentTime.Location())
default:
return nil
}
return &nextResetTime
}