资产详情新增两个字段
Some checks failed
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Has been cancelled

This commit is contained in:
2026-07-02 16:49:50 +09:00
parent 44fb21eb6a
commit b3fb8c7a82
13 changed files with 179 additions and 17 deletions

View File

@@ -91,7 +91,8 @@ func New(
// Resolve 通过任意标识符解析资产
// 主路径:查注册表(精确匹配 ICCID 或 VirtualNo
// Fallback原有跨表 OR 查询(处理 IMEI/SN/MSISDN 等非注册标识符)
func (s *Service) Resolve(ctx context.Context, identifier string) (*dto.AssetResolveResponse, error) {
// includeUsageSummary 为 true 时额外填充当前世代流量汇总字段
func (s *Service) Resolve(ctx context.Context, identifier string, includeUsageSummary bool) (*dto.AssetResolveResponse, error) {
if s.assetIdentifierStore != nil {
regRecord, regErr := s.assetIdentifierStore.FindByIdentifier(ctx, identifier)
if regErr == nil && regRecord != nil {
@@ -102,6 +103,9 @@ func (s *Service) Resolve(ctx context.Context, identifier string) (*dto.AssetRes
resp, buildErr := s.buildDeviceResolveResponse(ctx, device)
if buildErr == nil {
resp.Identifier = identifier
if includeUsageSummary {
s.fillUsageSummary(ctx, resp, "device", device.ID)
}
}
return resp, buildErr
}
@@ -111,6 +115,9 @@ func (s *Service) Resolve(ctx context.Context, identifier string) (*dto.AssetRes
resp, buildErr := s.buildCardResolveResponse(ctx, card)
if buildErr == nil {
resp.Identifier = identifier
if includeUsageSummary {
s.fillUsageSummary(ctx, resp, "iot_card", card.ID)
}
}
return resp, buildErr
}
@@ -123,6 +130,9 @@ func (s *Service) Resolve(ctx context.Context, identifier string) (*dto.AssetRes
resp, buildErr := s.buildDeviceResolveResponse(ctx, device)
if buildErr == nil {
resp.Identifier = identifier
if includeUsageSummary {
s.fillUsageSummary(ctx, resp, "device", device.ID)
}
}
return resp, buildErr
}
@@ -135,6 +145,9 @@ func (s *Service) Resolve(ctx context.Context, identifier string) (*dto.AssetRes
resp, buildErr := s.buildCardResolveResponse(ctx, &card)
if buildErr == nil {
resp.Identifier = identifier
if includeUsageSummary {
s.fillUsageSummary(ctx, resp, "iot_card", card.ID)
}
}
return resp, buildErr
}
@@ -336,6 +349,19 @@ func (s *Service) fillPackageInfo(ctx context.Context, resp *dto.AssetResolveRes
resp.EnableVirtualData = usage.EnableVirtualDataSnapshot
}
// fillUsageSummary 填充当前世代流量汇总字段。
// 仅在 includeUsageSummary=true 时被调用,无套餐时返回 0.0(非 null
func (s *Service) fillUsageSummary(ctx context.Context, resp *dto.AssetResolveResponse, carrierType string, carrierID uint) {
usages, err := s.packageUsageStore.GetCurrentGenerationUsagesForSummary(ctx, carrierType, carrierID)
if err != nil {
logger.GetAppLogger().Error("查询流量汇总失败", zap.String("carrierType", carrierType), zap.Uint("carrierID", carrierID), zap.Error(err))
return
}
totalUsed, totalRemaining := computeUsageSummary(usages)
resp.TotalVirtualUsedMB = &totalUsed
resp.TotalVirtualRemainingMB = &totalRemaining
}
// fillShopName 填充店铺名称
func (s *Service) fillShopName(ctx context.Context, resp *dto.AssetResolveResponse) {
if resp.ShopID == nil || *resp.ShopID == 0 {
@@ -971,7 +997,7 @@ func (s *Service) GetOrders(ctx context.Context, identifier string, page, pageSi
pageSize = 100
}
asset, err := s.Resolve(ctx, identifier)
asset, err := s.Resolve(ctx, identifier, false)
if err != nil {
return nil, err
}

View File

@@ -0,0 +1,16 @@
package asset
import "github.com/break/junhong_cmp_fiber/internal/model"
// computeUsageSummary 汇总当前世代套餐的虚已用量和虚剩余量。
// 对每条记录调用 BuildTrafficMetrics() 获取虚流量字段,累加后计算剩余。
func computeUsageSummary(usages []*model.PackageUsage) (totalUsedMB float64, totalRemainingMB float64) {
var totalVirtualTotal float64
for _, u := range usages {
m := u.BuildTrafficMetrics()
totalUsedMB += m.VirtualUsedMB
totalVirtualTotal += float64(m.VirtualTotalMB)
}
totalRemainingMB = totalVirtualTotal - totalUsedMB
return
}

View File

@@ -0,0 +1,81 @@
package asset
import (
"testing"
"github.com/break/junhong_cmp_fiber/internal/model"
)
func TestComputeUsageSummary_EmptyList(t *testing.T) {
totalUsed, totalRemaining := computeUsageSummary(nil)
if totalUsed != 0.0 {
t.Errorf("期望 totalUsed=0.0,实际=%v", totalUsed)
}
if totalRemaining != 0.0 {
t.Errorf("期望 totalRemaining=0.0,实际=%v", totalRemaining)
}
}
func TestComputeUsageSummary_SingleVirtualPackage(t *testing.T) {
// 启用虚流量:真实总量=100MB虚拟总量=200MB倍率=0.5
// 真实已用=40MB → 虚拟已用=min(40*0.5, 100)=20MB
// 虚拟剩余=200-20=180MB
usage := &model.PackageUsage{
DataLimitMB: 100,
DataUsageMB: 40,
VirtualTotalMBSnapshot: 200,
DisplayGainRatioSnapshot: 0.5,
EnableVirtualDataSnapshot: true,
}
totalUsed, totalRemaining := computeUsageSummary([]*model.PackageUsage{usage})
if totalUsed != 20.0 {
t.Errorf("期望 totalUsed=20.0,实际=%v", totalUsed)
}
if totalRemaining != 180.0 {
t.Errorf("期望 totalRemaining=180.0,实际=%v", totalRemaining)
}
}
func TestComputeUsageSummary_SingleNonVirtualPackage(t *testing.T) {
// 未启用虚流量:退化为真实值
// 真实总量=100MB真实已用=40MB → 虚拟已用=40MB虚拟剩余=60MB
usage := &model.PackageUsage{
DataLimitMB: 100,
DataUsageMB: 40,
EnableVirtualDataSnapshot: false,
}
totalUsed, totalRemaining := computeUsageSummary([]*model.PackageUsage{usage})
if totalUsed != 40.0 {
t.Errorf("期望 totalUsed=40.0,实际=%v", totalUsed)
}
if totalRemaining != 60.0 {
t.Errorf("期望 totalRemaining=60.0,实际=%v", totalRemaining)
}
}
func TestComputeUsageSummary_MultiplePackages(t *testing.T) {
// 套餐1启用虚流量VirtualUsed=20, VirtualTotal=200
// 套餐2未启用虚流量VirtualUsed=10, VirtualTotal=50
// 汇总totalUsed=30, totalRemaining=(200+50)-30=220
usages := []*model.PackageUsage{
{
DataLimitMB: 100,
DataUsageMB: 40,
VirtualTotalMBSnapshot: 200,
DisplayGainRatioSnapshot: 0.5,
EnableVirtualDataSnapshot: true,
},
{
DataLimitMB: 50,
DataUsageMB: 10,
EnableVirtualDataSnapshot: false,
},
}
totalUsed, totalRemaining := computeUsageSummary(usages)
if totalUsed != 30.0 {
t.Errorf("期望 totalUsed=30.0,实际=%v", totalUsed)
}
if totalRemaining != 220.0 {
t.Errorf("期望 totalRemaining=220.0,实际=%v", totalRemaining)
}
}