Files
junhong_cmp_fiber/internal/service/package/reset_service.go
huang 353621d923
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 6m33s
移除所有测试代码和测试要求
**变更说明**:
- 删除所有 *_test.go 文件(单元测试、集成测试、验收测试、流程测试)
- 删除整个 tests/ 目录
- 更新 CLAUDE.md:用"测试禁令"章节替换所有测试要求
- 删除测试生成 Skill (openspec-generate-acceptance-tests)
- 删除测试生成命令 (opsx:gen-tests)
- 更新 tasks.md:删除所有测试相关任务

**新规范**:
-  禁止编写任何形式的自动化测试
-  禁止创建 *_test.go 文件
-  禁止在任务中包含测试相关工作
-  仅当用户明确要求时才编写测试

**原因**:
业务系统的正确性通过人工验证和生产环境监控保证,测试代码维护成本高于价值。

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-11 17:13:42 +08:00

243 lines
7.1 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 (
"context"
"time"
"github.com/break/junhong_cmp_fiber/internal/model"
"github.com/break/junhong_cmp_fiber/internal/store/postgres"
"github.com/break/junhong_cmp_fiber/pkg/constants"
"github.com/break/junhong_cmp_fiber/pkg/errors"
"github.com/redis/go-redis/v9"
"go.uber.org/zap"
"gorm.io/gorm"
)
type ResetService struct {
db *gorm.DB
redis *redis.Client
packageUsageStore *postgres.PackageUsageStore
logger *zap.Logger
}
func NewResetService(
db *gorm.DB,
redis *redis.Client,
packageUsageStore *postgres.PackageUsageStore,
logger *zap.Logger,
) *ResetService {
return &ResetService{
db: db,
redis: redis,
packageUsageStore: packageUsageStore,
logger: logger,
}
}
// ResetDailyUsage 任务 11.2-11.3: 重置日流量
func (s *ResetService) ResetDailyUsage(ctx context.Context) error {
return s.resetDailyUsageWithDB(ctx, s.db)
}
// resetDailyUsageWithDB 内部方法,支持传入 DB/TX
func (s *ResetService) resetDailyUsageWithDB(ctx context.Context, db *gorm.DB) error {
now := time.Now()
return db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
// 查询需要重置的套餐
var packages []*model.PackageUsage
err := tx.Where("data_reset_cycle = ?", constants.PackageDataResetDaily).
Where("next_reset_at <= ?", now).
Where("status IN ?", []int{constants.PackageUsageStatusActive, constants.PackageUsageStatusDepleted}).
Find(&packages).Error
if err != nil {
return errors.Wrap(errors.CodeDatabaseError, err, "查询待重置套餐失败")
}
if len(packages) == 0 {
s.logger.Info("没有需要重置的日流量套餐")
return nil
}
// 批量重置
packageIDs := make([]uint, len(packages))
for i, pkg := range packages {
packageIDs[i] = pkg.ID
}
// 计算下次重置时间(明天 00:00:00
nextReset := time.Date(now.Year(), now.Month(), now.Day()+1, 0, 0, 0, 0, now.Location())
// 批量更新
updates := map[string]interface{}{
"data_usage_mb": 0,
"last_reset_at": now,
"next_reset_at": nextReset,
"status": constants.PackageUsageStatusActive, // 重置后恢复为生效中
}
if err := tx.Model(&model.PackageUsage{}).
Where("id IN ?", packageIDs).
Updates(updates).Error; err != nil {
return errors.Wrap(errors.CodeDatabaseError, err, "批量重置日流量失败")
}
s.logger.Info("日流量重置完成",
zap.Int("count", len(packages)),
zap.Time("next_reset_at", nextReset))
return nil
})
}
// ResetMonthlyUsage 任务 11.4-11.5: 重置月流量
func (s *ResetService) ResetMonthlyUsage(ctx context.Context) error {
return s.resetMonthlyUsageWithDB(ctx, s.db)
}
// resetMonthlyUsageWithDB 内部方法,支持传入 DB/TX
func (s *ResetService) resetMonthlyUsageWithDB(ctx context.Context, db *gorm.DB) error {
now := time.Now()
return db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
// 查询需要重置的套餐
var packages []*model.PackageUsage
err := tx.Where("data_reset_cycle = ?", constants.PackageDataResetMonthly).
Where("next_reset_at <= ?", now).
Where("status IN ?", []int{constants.PackageUsageStatusActive, constants.PackageUsageStatusDepleted}).
Find(&packages).Error
if err != nil {
return errors.Wrap(errors.CodeDatabaseError, err, "查询待重置套餐失败")
}
if len(packages) == 0 {
s.logger.Info("没有需要重置的月流量套餐")
return nil
}
// 按套餐分组处理因为需要区分联通27号 vs 其他1号
for _, pkg := range packages {
// 查询运营商信息以确定计费日
// 只有单卡套餐才根据运营商判断设备级套餐统一使用1号计费
billingDay := 1
if pkg.IotCardID != 0 {
var card model.IotCard
if err := tx.First(&card, pkg.IotCardID).Error; err == nil {
var carrier model.Carrier
if err := tx.First(&carrier, card.CarrierID).Error; err == nil {
if carrier.CarrierType == "CUCC" {
billingDay = 27
}
}
}
}
// 设备级套餐默认使用1号计费已在 billingDay := 1 初始化)
// 计算下次重置时间
nextReset := calculateNextMonthlyResetTime(now, billingDay)
// 更新套餐
updates := map[string]interface{}{
"data_usage_mb": 0,
"last_reset_at": now,
"next_reset_at": nextReset,
"status": constants.PackageUsageStatusActive, // 重置后恢复为生效中
}
if err := tx.Model(pkg).Updates(updates).Error; err != nil {
return errors.Wrap(errors.CodeDatabaseError, err, "重置月流量失败")
}
s.logger.Info("月流量已重置",
zap.Uint("usage_id", pkg.ID),
zap.Int("billing_day", billingDay),
zap.Time("next_reset_at", nextReset))
}
return nil
})
}
// ResetYearlyUsage 任务 11.6-11.7: 重置年流量
func (s *ResetService) ResetYearlyUsage(ctx context.Context) error {
return s.resetYearlyUsageWithDB(ctx, s.db)
}
// resetYearlyUsageWithDB 内部方法,支持传入 DB/TX
func (s *ResetService) resetYearlyUsageWithDB(ctx context.Context, db *gorm.DB) error {
now := time.Now()
return db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
// 查询需要重置的套餐
var packages []*model.PackageUsage
err := tx.Where("data_reset_cycle = ?", constants.PackageDataResetYearly).
Where("next_reset_at <= ?", now).
Where("status IN ?", []int{constants.PackageUsageStatusActive, constants.PackageUsageStatusDepleted}).
Find(&packages).Error
if err != nil {
return errors.Wrap(errors.CodeDatabaseError, err, "查询待重置套餐失败")
}
if len(packages) == 0 {
s.logger.Info("没有需要重置的年流量套餐")
return nil
}
// 批量重置
packageIDs := make([]uint, len(packages))
for i, pkg := range packages {
packageIDs[i] = pkg.ID
}
// 计算下次重置时间(明年 1月1日 00:00:00
nextReset := time.Date(now.Year()+1, 1, 1, 0, 0, 0, 0, now.Location())
// 批量更新
updates := map[string]interface{}{
"data_usage_mb": 0,
"last_reset_at": now,
"next_reset_at": nextReset,
"status": constants.PackageUsageStatusActive, // 重置后恢复为生效中
}
if err := tx.Model(&model.PackageUsage{}).
Where("id IN ?", packageIDs).
Updates(updates).Error; err != nil {
return errors.Wrap(errors.CodeDatabaseError, err, "批量重置年流量失败")
}
s.logger.Info("年流量重置完成",
zap.Int("count", len(packages)),
zap.Time("next_reset_at", nextReset))
return nil
})
}
// calculateNextMonthlyResetTime 计算下次月重置时间
func calculateNextMonthlyResetTime(now time.Time, billingDay int) time.Time {
currentDay := now.Day()
targetMonth := now.Month()
targetYear := now.Year()
// 如果当前日期 >= 计费日,下次重置是下月计费日
if currentDay >= billingDay {
targetMonth++
if targetMonth > 12 {
targetMonth = 1
targetYear++
}
}
// 处理月末天数不足的情况例如2月没有27日
maxDay := time.Date(targetYear, targetMonth+1, 0, 0, 0, 0, 0, now.Location()).Day()
if billingDay > maxDay {
billingDay = maxDay
}
return time.Date(targetYear, targetMonth, billingDay, 0, 0, 0, 0, now.Location())
}