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>
148 lines
4.1 KiB
Go
148 lines
4.1 KiB
Go
package packagepkg
|
||
|
||
import (
|
||
"context"
|
||
|
||
"github.com/break/junhong_cmp_fiber/internal/model"
|
||
"github.com/break/junhong_cmp_fiber/internal/model/dto"
|
||
"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 CustomerViewService struct {
|
||
db *gorm.DB
|
||
redis *redis.Client
|
||
packageUsageStore *postgres.PackageUsageStore
|
||
logger *zap.Logger
|
||
}
|
||
|
||
func NewCustomerViewService(
|
||
db *gorm.DB,
|
||
redis *redis.Client,
|
||
packageUsageStore *postgres.PackageUsageStore,
|
||
logger *zap.Logger,
|
||
) *CustomerViewService {
|
||
return &CustomerViewService{
|
||
db: db,
|
||
redis: redis,
|
||
packageUsageStore: packageUsageStore,
|
||
logger: logger,
|
||
}
|
||
}
|
||
|
||
// GetMyUsage 任务 12.2-12.5: 获取客户套餐使用情况
|
||
// 根据载体ID和类型查询生效中的套餐,计算总流量使用情况
|
||
func (s *CustomerViewService) GetMyUsage(ctx context.Context, carrierType string, carrierID uint) (*dto.PackageUsageCustomerViewResponse, error) {
|
||
// 任务 12.3: 查询生效套餐(status IN (1,2))
|
||
var packages []*model.PackageUsage
|
||
query := s.db.WithContext(ctx).
|
||
Where("status IN ?", []int{constants.PackageUsageStatusActive, constants.PackageUsageStatusDepleted})
|
||
|
||
if carrierType == "iot_card" {
|
||
query = query.Where("iot_card_id = ?", carrierID)
|
||
} else if carrierType == "device" {
|
||
query = query.Where("device_id = ?", carrierID)
|
||
} else {
|
||
return nil, errors.New(errors.CodeInvalidParam, "无效的载体类型")
|
||
}
|
||
|
||
// 按优先级排序:主套餐在前,加油包在后
|
||
if err := query.Order("CASE WHEN master_usage_id IS NULL THEN 0 ELSE 1 END, priority ASC").
|
||
Find(&packages).Error; err != nil {
|
||
return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询套餐使用记录失败")
|
||
}
|
||
|
||
if len(packages) == 0 {
|
||
return nil, errors.New(errors.CodeNotFound, "未找到套餐使用记录")
|
||
}
|
||
|
||
// 任务 12.4: 区分主套餐和加油包,计算总流量
|
||
var mainPackage *dto.PackageUsageItemResponse
|
||
var addonPackages []dto.PackageUsageItemResponse
|
||
var totalUsedMB int64
|
||
var totalLimitMB int64
|
||
|
||
for _, pkg := range packages {
|
||
// 查询套餐信息
|
||
var packageInfo model.Package
|
||
if err := s.db.First(&packageInfo, pkg.PackageID).Error; err != nil {
|
||
s.logger.Warn("查询套餐信息失败",
|
||
zap.Uint("package_id", pkg.PackageID),
|
||
zap.Error(err))
|
||
continue
|
||
}
|
||
|
||
// 格式化状态文本
|
||
statusText := getStatusText(pkg.Status)
|
||
|
||
// 格式化时间
|
||
activatedAtStr := ""
|
||
if pkg.ActivatedAt.Year() > 1 {
|
||
activatedAtStr = pkg.ActivatedAt.Format("2006-01-02 15:04:05")
|
||
}
|
||
|
||
expiresAtStr := ""
|
||
if pkg.ExpiresAt.Year() > 1 {
|
||
expiresAtStr = pkg.ExpiresAt.Format("2006-01-02 15:04:05")
|
||
}
|
||
|
||
item := dto.PackageUsageItemResponse{
|
||
PackageUsageID: pkg.ID,
|
||
PackageID: pkg.PackageID,
|
||
PackageName: packageInfo.PackageName,
|
||
UsedMB: pkg.DataUsageMB,
|
||
TotalMB: pkg.DataLimitMB,
|
||
Status: pkg.Status,
|
||
StatusText: statusText,
|
||
ActivatedAt: activatedAtStr,
|
||
ExpiresAt: expiresAtStr,
|
||
Priority: pkg.Priority,
|
||
}
|
||
|
||
// 累计总流量
|
||
totalUsedMB += pkg.DataUsageMB
|
||
totalLimitMB += pkg.DataLimitMB
|
||
|
||
// 区分主套餐和加油包
|
||
if pkg.MasterUsageID == nil {
|
||
mainPackage = &item
|
||
} else {
|
||
addonPackages = append(addonPackages, item)
|
||
}
|
||
}
|
||
|
||
// 任务 12.5: 组装响应 DTO
|
||
response := &dto.PackageUsageCustomerViewResponse{
|
||
MainPackage: mainPackage,
|
||
AddonPackages: addonPackages,
|
||
Total: dto.PackageUsageTotalInfo{
|
||
UsedMB: totalUsedMB,
|
||
TotalMB: totalLimitMB,
|
||
},
|
||
}
|
||
|
||
return response, nil
|
||
}
|
||
|
||
// getStatusText 获取状态文本
|
||
func getStatusText(status int) string {
|
||
switch status {
|
||
case constants.PackageUsageStatusPending:
|
||
return "待生效"
|
||
case constants.PackageUsageStatusActive:
|
||
return "生效中"
|
||
case constants.PackageUsageStatusDepleted:
|
||
return "已用完"
|
||
case constants.PackageUsageStatusExpired:
|
||
return "已过期"
|
||
case constants.PackageUsageStatusInvalidated:
|
||
return "已失效"
|
||
default:
|
||
return "未知"
|
||
}
|
||
}
|