Some checks failed
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Has been cancelled
- 新增六对成对迁移 000232–000237:H5 弹窗类型、退款结算标识与申请人备注、优先轮询事实字段与两个新终态、通道阈值命中留痕、手机号最近解绑人、提现资格校验留痕 - 退款:原因必填与申请人备注、来源支付与渠道流水冻结、线下处理流水号补录审计、按订单查询可选退款方式、企微审批材料补齐且新增字段缺失映射即明确失败 - 优先轮询:人工关闭、有效期到期独立周期任务、失败与过期人工重触发、事实字段与异常重试查询、资产解析端点只读投影 - 通道阈值:命中事实同事务留痕与命中记录查询;员工账单:列表筛选与详情投影;商户池:列表投影与统计周期语义;H5:弹窗类型与类别排序 - 手机号:有效关联数量与最近解绑人、短信验证码失败次数限制;导出:佣金明细十五列与报表序号列 - 时间筛选:三处新增筛选纳入统一严格解析契约,员工账单产生时间参数改名 - 同步 12 份主 Spec 需求、两端点与异步任务证据链,门禁 context-health 与 OpenSpec 校验通过
448 lines
18 KiB
Go
448 lines
18 KiB
Go
package carrier
|
||
|
||
import (
|
||
"context"
|
||
"strconv"
|
||
"time"
|
||
|
||
"gorm.io/gorm"
|
||
|
||
systemconfigapp "github.com/break/junhong_cmp_fiber/internal/application/systemconfig"
|
||
carrierthreshold "github.com/break/junhong_cmp_fiber/internal/domain/carrierthreshold"
|
||
"github.com/break/junhong_cmp_fiber/internal/model"
|
||
"github.com/break/junhong_cmp_fiber/internal/model/dto"
|
||
carrierthresholdquery "github.com/break/junhong_cmp_fiber/internal/query/carrierthreshold"
|
||
"github.com/break/junhong_cmp_fiber/internal/store"
|
||
"github.com/break/junhong_cmp_fiber/internal/store/postgres"
|
||
"github.com/break/junhong_cmp_fiber/pkg/auditfailure"
|
||
"github.com/break/junhong_cmp_fiber/pkg/constants"
|
||
"github.com/break/junhong_cmp_fiber/pkg/errors"
|
||
"github.com/break/junhong_cmp_fiber/pkg/middleware"
|
||
)
|
||
|
||
type Service struct {
|
||
carrierStore *postgres.CarrierStore
|
||
audit systemconfigapp.AuditWriter
|
||
// thresholdHits 是通道阈值命中记录只读查询;与运营商配置共用同一数据库连接。
|
||
thresholdHits *carrierthresholdquery.Query
|
||
}
|
||
|
||
func New(carrierStore *postgres.CarrierStore, audit systemconfigapp.AuditWriter) *Service {
|
||
service := &Service{carrierStore: carrierStore, audit: audit}
|
||
if carrierStore != nil {
|
||
service.thresholdHits = carrierthresholdquery.NewQuery(carrierStore.DB())
|
||
}
|
||
return service
|
||
}
|
||
|
||
// ListThresholdHits 分页查询通道阈值命中记录。
|
||
//
|
||
// 账号门禁与卡数据范围在查询侧判定(ENG-AUTHZ-001);本方法只做装配,
|
||
// 权限失败与资源不可见都按查询侧的统一拒绝返回。
|
||
func (s *Service) ListThresholdHits(ctx context.Context, req *dto.CarrierThresholdHitListRequest) (*dto.CarrierThresholdHitPageResult, error) {
|
||
if s == nil || s.thresholdHits == nil {
|
||
return nil, errors.New(errors.CodeInternalError, "通道阈值命中记录查询未配置")
|
||
}
|
||
return s.thresholdHits.List(ctx, *req)
|
||
}
|
||
|
||
func (s *Service) Create(ctx context.Context, req *dto.CreateCarrierRequest) (*dto.CarrierResponse, error) {
|
||
currentUserID := middleware.GetUserIDFromContext(ctx)
|
||
if currentUserID == 0 {
|
||
return nil, errors.New(errors.CodeUnauthorized, "未授权访问")
|
||
}
|
||
|
||
// 阈值字段只由超级管理员与平台账号配置:非平台账号提交即整体拒绝,不落任何配置。
|
||
if req.HasTrafficThresholdFields() && !canManageThreshold(ctx) {
|
||
s.recordDenied(ctx, constants.AuditOperationCarrierCreate, "拒绝非平台账号配置运营商通道流量阈值",
|
||
thresholdCreateProbe(req), errors.CodeForbidden)
|
||
return nil, errors.New(errors.CodeForbidden, constants.PlatformManagementForbiddenMessage)
|
||
}
|
||
thresholdEnabled, thresholdValue, thresholdUnit := 0, req.TrafficThresholdValue, ""
|
||
if req.TrafficThresholdEnabled != nil {
|
||
thresholdEnabled = *req.TrafficThresholdEnabled
|
||
}
|
||
if req.TrafficThresholdUnit != nil {
|
||
thresholdUnit = *req.TrafficThresholdUnit
|
||
}
|
||
if err := validateThresholdConfig(thresholdEnabled, thresholdValue, thresholdUnit); err != nil {
|
||
s.recordDenied(ctx, constants.AuditOperationCarrierCreate, "拒绝保存非法运营商通道流量阈值配置",
|
||
thresholdCreateProbe(req), errors.CodeInvalidParam)
|
||
return nil, err
|
||
}
|
||
|
||
existing, _ := s.carrierStore.GetByCode(ctx, req.CarrierCode)
|
||
if existing != nil {
|
||
s.recordDenied(ctx, constants.AuditOperationCarrierCreate, "拒绝创建重复运营商配置", existing, errors.CodeCarrierCodeExists)
|
||
return nil, errors.New(errors.CodeCarrierCodeExists, "运营商编码已存在")
|
||
}
|
||
if s.audit == nil {
|
||
return nil, errors.New(errors.CodeInvalidStatus, "运营商配置审计接缝未配置")
|
||
}
|
||
|
||
carrier := &model.Carrier{
|
||
CarrierCode: req.CarrierCode,
|
||
CarrierName: req.CarrierName,
|
||
CarrierType: req.CarrierType,
|
||
Description: req.Description,
|
||
Status: constants.StatusEnabled,
|
||
DataResetDay: 1,
|
||
|
||
TrafficThresholdEnabled: thresholdEnabled,
|
||
TrafficThresholdValue: thresholdValue,
|
||
TrafficThresholdUnit: thresholdUnit,
|
||
}
|
||
if req.DataResetDay != nil {
|
||
carrier.DataResetDay = *req.DataResetDay
|
||
}
|
||
if req.RealnameLinkType != nil {
|
||
carrier.RealnameLinkType = *req.RealnameLinkType
|
||
}
|
||
if req.RealnameLinkTemplate != nil {
|
||
carrier.RealnameLinkTemplate = *req.RealnameLinkTemplate
|
||
}
|
||
carrier.Creator = currentUserID
|
||
|
||
err := s.carrierStore.DB().WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||
if err := s.carrierStore.WithTx(tx).Create(ctx, carrier); err != nil {
|
||
return err
|
||
}
|
||
return s.writeAudit(ctx, tx, constants.AuditOperationCarrierCreate, "创建运营商配置", nil, carrier)
|
||
})
|
||
if err != nil {
|
||
s.recordFailure(ctx, constants.AuditOperationCarrierCreate, "创建运营商配置失败", carrier)
|
||
return nil, errors.Wrap(errors.CodeInternalError, err, "创建运营商失败")
|
||
}
|
||
|
||
return s.toResponse(ctx, carrier), nil
|
||
}
|
||
|
||
func (s *Service) Get(ctx context.Context, id uint) (*dto.CarrierResponse, error) {
|
||
carrier, err := s.carrierStore.GetByID(ctx, id)
|
||
if err != nil {
|
||
if err == gorm.ErrRecordNotFound {
|
||
return nil, errors.New(errors.CodeCarrierNotFound, "运营商不存在")
|
||
}
|
||
return nil, errors.Wrap(errors.CodeInternalError, err, "获取运营商失败")
|
||
}
|
||
return s.toResponse(ctx, carrier), nil
|
||
}
|
||
|
||
func (s *Service) Update(ctx context.Context, id uint, req *dto.UpdateCarrierRequest) (*dto.CarrierResponse, error) {
|
||
currentUserID := middleware.GetUserIDFromContext(ctx)
|
||
if currentUserID == 0 {
|
||
return nil, errors.New(errors.CodeUnauthorized, "未授权访问")
|
||
}
|
||
|
||
carrier, err := s.carrierStore.GetByID(ctx, id)
|
||
if err != nil {
|
||
if err == gorm.ErrRecordNotFound {
|
||
return nil, errors.New(errors.CodeCarrierNotFound, "运营商不存在")
|
||
}
|
||
return nil, errors.Wrap(errors.CodeInternalError, err, "获取运营商失败")
|
||
}
|
||
before := *carrier
|
||
|
||
// 阈值字段只由超级管理员与平台账号修改:非平台账号提交即整体拒绝。
|
||
if req.HasTrafficThresholdFields() && !canManageThreshold(ctx) {
|
||
s.recordDenied(ctx, constants.AuditOperationCarrierUpdate, "拒绝非平台账号配置运营商通道流量阈值",
|
||
&before, errors.CodeForbidden)
|
||
return nil, errors.New(errors.CodeForbidden, constants.PlatformManagementForbiddenMessage)
|
||
}
|
||
// 阈值按「请求覆盖既有配置」后的结果校验,避免保存出启用但缺数值/单位的半配置。
|
||
thresholdEnabled, thresholdValue, thresholdUnit := carrier.TrafficThresholdEnabled, carrier.TrafficThresholdValue, carrier.TrafficThresholdUnit
|
||
if req.TrafficThresholdEnabled != nil {
|
||
thresholdEnabled = *req.TrafficThresholdEnabled
|
||
}
|
||
if req.TrafficThresholdValue != nil {
|
||
thresholdValue = req.TrafficThresholdValue
|
||
}
|
||
if req.TrafficThresholdUnit != nil {
|
||
thresholdUnit = *req.TrafficThresholdUnit
|
||
}
|
||
if err := validateThresholdConfig(thresholdEnabled, thresholdValue, thresholdUnit); err != nil {
|
||
s.recordDenied(ctx, constants.AuditOperationCarrierUpdate, "拒绝保存非法运营商通道流量阈值配置",
|
||
&before, errors.CodeInvalidParam)
|
||
return nil, err
|
||
}
|
||
carrier.TrafficThresholdEnabled = thresholdEnabled
|
||
carrier.TrafficThresholdValue = thresholdValue
|
||
carrier.TrafficThresholdUnit = thresholdUnit
|
||
|
||
if req.CarrierName != nil {
|
||
carrier.CarrierName = *req.CarrierName
|
||
}
|
||
if req.Description != nil {
|
||
carrier.Description = *req.Description
|
||
}
|
||
if req.DataResetDay != nil {
|
||
carrier.DataResetDay = *req.DataResetDay
|
||
}
|
||
if req.RealnameLinkType != nil {
|
||
carrier.RealnameLinkType = *req.RealnameLinkType
|
||
}
|
||
if req.RealnameLinkTemplate != nil {
|
||
carrier.RealnameLinkTemplate = *req.RealnameLinkTemplate
|
||
}
|
||
if carrier.RealnameLinkType == "template" && carrier.RealnameLinkTemplate == "" {
|
||
s.recordDenied(ctx, constants.AuditOperationCarrierUpdate, "拒绝保存非法运营商实名链接配置", &before, errors.CodeInvalidParam)
|
||
return nil, errors.New(errors.CodeInvalidParam, "模板URL类型必须提供实名链接模板")
|
||
}
|
||
carrier.Updater = currentUserID
|
||
|
||
err = s.carrierStore.DB().WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||
if err := s.carrierStore.WithTx(tx).Update(ctx, carrier); err != nil {
|
||
return err
|
||
}
|
||
return s.writeAudit(ctx, tx, constants.AuditOperationCarrierUpdate, "更新运营商配置", &before, carrier)
|
||
})
|
||
if err != nil {
|
||
s.recordFailure(ctx, constants.AuditOperationCarrierUpdate, "更新运营商配置失败", carrier)
|
||
return nil, errors.Wrap(errors.CodeInternalError, err, "更新运营商失败")
|
||
}
|
||
|
||
return s.toResponse(ctx, carrier), nil
|
||
}
|
||
|
||
func (s *Service) Delete(ctx context.Context, id uint) error {
|
||
carrier, err := s.carrierStore.GetByID(ctx, id)
|
||
if err != nil {
|
||
if err == gorm.ErrRecordNotFound {
|
||
return errors.New(errors.CodeCarrierNotFound, "运营商不存在")
|
||
}
|
||
return errors.Wrap(errors.CodeInternalError, err, "获取运营商失败")
|
||
}
|
||
if s.audit == nil {
|
||
return errors.New(errors.CodeInvalidStatus, "运营商配置审计接缝未配置")
|
||
}
|
||
|
||
err = s.carrierStore.DB().WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||
if err := s.carrierStore.WithTx(tx).Delete(ctx, id); err != nil {
|
||
return err
|
||
}
|
||
return s.writeAudit(ctx, tx, constants.AuditOperationCarrierDelete, "删除运营商配置", carrier, nil)
|
||
})
|
||
if err != nil {
|
||
s.recordFailure(ctx, constants.AuditOperationCarrierDelete, "删除运营商配置失败", carrier)
|
||
return errors.Wrap(errors.CodeInternalError, err, "删除运营商失败")
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
func (s *Service) List(ctx context.Context, req *dto.CarrierListRequest) ([]*dto.CarrierResponse, int64, error) {
|
||
opts := &store.QueryOptions{
|
||
Page: req.Page,
|
||
PageSize: req.PageSize,
|
||
OrderBy: "id DESC",
|
||
}
|
||
if opts.Page == 0 {
|
||
opts.Page = 1
|
||
}
|
||
if opts.PageSize == 0 {
|
||
opts.PageSize = constants.DefaultPageSize
|
||
}
|
||
|
||
filters := make(map[string]interface{})
|
||
if req.CarrierType != nil {
|
||
filters["carrier_type"] = *req.CarrierType
|
||
}
|
||
if req.CarrierName != nil {
|
||
filters["carrier_name"] = *req.CarrierName
|
||
}
|
||
if req.Status != nil {
|
||
filters["status"] = *req.Status
|
||
}
|
||
|
||
carriers, total, err := s.carrierStore.List(ctx, opts, filters)
|
||
if err != nil {
|
||
return nil, 0, errors.Wrap(errors.CodeInternalError, err, "查询运营商列表失败")
|
||
}
|
||
|
||
responses := make([]*dto.CarrierResponse, len(carriers))
|
||
for i, c := range carriers {
|
||
responses[i] = s.toResponse(ctx, c)
|
||
}
|
||
|
||
return responses, total, nil
|
||
}
|
||
|
||
func (s *Service) UpdateStatus(ctx context.Context, id uint, status int) error {
|
||
currentUserID := middleware.GetUserIDFromContext(ctx)
|
||
if currentUserID == 0 {
|
||
return errors.New(errors.CodeUnauthorized, "未授权访问")
|
||
}
|
||
|
||
carrier, err := s.carrierStore.GetByID(ctx, id)
|
||
if err != nil {
|
||
if err == gorm.ErrRecordNotFound {
|
||
return errors.New(errors.CodeCarrierNotFound, "运营商不存在")
|
||
}
|
||
return errors.Wrap(errors.CodeInternalError, err, "获取运营商失败")
|
||
}
|
||
if s.audit == nil {
|
||
return errors.New(errors.CodeInvalidStatus, "运营商配置审计接缝未配置")
|
||
}
|
||
before := *carrier
|
||
|
||
carrier.Status = status
|
||
carrier.Updater = currentUserID
|
||
|
||
err = s.carrierStore.DB().WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||
if err := s.carrierStore.WithTx(tx).Update(ctx, carrier); err != nil {
|
||
return err
|
||
}
|
||
return s.writeAudit(ctx, tx, constants.AuditOperationCarrierStatusUpdate, "更新运营商配置状态", &before, carrier)
|
||
})
|
||
if err != nil {
|
||
s.recordFailure(ctx, constants.AuditOperationCarrierStatusUpdate, "更新运营商配置状态失败", carrier)
|
||
return errors.Wrap(errors.CodeInternalError, err, "更新运营商状态失败")
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
func (s *Service) writeAudit(ctx context.Context, tx *gorm.DB, operation, description string, before, after *model.Carrier) error {
|
||
carrier := after
|
||
if carrier == nil {
|
||
carrier = before
|
||
}
|
||
resourceID := strconv.FormatUint(uint64(carrier.ID), 10)
|
||
requestID := ""
|
||
if value := middleware.GetRequestIDFromContext(ctx); value != nil {
|
||
requestID = *value
|
||
}
|
||
return s.audit.WriteConfigChange(ctx, tx, systemconfigapp.ChangeAudit{
|
||
OperatorID: middleware.GetUserIDFromContext(ctx), OperationType: operation, Description: description,
|
||
ConfigKey: "carrier." + carrier.CarrierCode, Module: "carrier", ResourceID: &resourceID,
|
||
DisplayName: carrier.CarrierName, Identity: carrierIdentity(carrier),
|
||
BeforeData: carrierAuditSnapshot(before), AfterData: carrierAuditSnapshot(after),
|
||
RequestID: requestID, CorrelationID: requestID,
|
||
})
|
||
}
|
||
|
||
func (s *Service) recordDenied(ctx context.Context, operation, description string, carrier *model.Carrier, code int) {
|
||
s.recordAuditResult(ctx, operation, description, carrier, constants.AuditResultDenied, code)
|
||
}
|
||
|
||
func (s *Service) recordFailure(ctx context.Context, operation, description string, carrier *model.Carrier) {
|
||
s.recordAuditResult(ctx, operation, description, carrier, constants.AuditResultFailed, errors.CodeDatabaseError)
|
||
}
|
||
|
||
func (s *Service) recordAuditResult(ctx context.Context, operation, description string, carrier *model.Carrier, result string, code int) {
|
||
if s.audit == nil || carrier == nil || s.carrierStore == nil || s.carrierStore.DB() == nil {
|
||
return
|
||
}
|
||
resourceID := strconv.FormatUint(uint64(carrier.ID), 10)
|
||
requestID := ""
|
||
if value := middleware.GetRequestIDFromContext(ctx); value != nil {
|
||
requestID = *value
|
||
}
|
||
audit := systemconfigapp.ChangeAudit{
|
||
OperatorID: middleware.GetUserIDFromContext(ctx), OperationType: operation, Description: description,
|
||
ConfigKey: "carrier." + carrier.CarrierCode, Module: "carrier", ResourceID: &resourceID,
|
||
DisplayName: carrier.CarrierName, Identity: carrierIdentity(carrier), BeforeData: carrierAuditSnapshot(carrier),
|
||
Result: result, ErrorCode: strconv.Itoa(code), ErrorSummary: description,
|
||
RequestID: requestID, CorrelationID: requestID,
|
||
}
|
||
if err := s.carrierStore.DB().WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||
return s.audit.WriteConfigChange(ctx, tx, audit)
|
||
}); err != nil {
|
||
auditfailure.RecordSecondaryWriteFailure(operation, audit.ConfigKey, requestID, requestID, audit.ErrorCode, err)
|
||
}
|
||
}
|
||
|
||
func carrierIdentity(carrier *model.Carrier) map[string]any {
|
||
if carrier == nil {
|
||
return nil
|
||
}
|
||
return map[string]any{
|
||
"id": carrier.ID, "carrier_code": carrier.CarrierCode, "carrier_name": carrier.CarrierName,
|
||
"carrier_type": carrier.CarrierType, "status": carrier.Status,
|
||
}
|
||
}
|
||
|
||
func carrierAuditSnapshot(carrier *model.Carrier) map[string]any {
|
||
if carrier == nil {
|
||
return nil
|
||
}
|
||
return map[string]any{
|
||
"id": carrier.ID, "carrier_code": carrier.CarrierCode, "carrier_name": carrier.CarrierName,
|
||
"carrier_type": carrier.CarrierType, "description": carrier.Description, "status": carrier.Status,
|
||
"realname_link_type": carrier.RealnameLinkType, "realname_link_template": carrier.RealnameLinkTemplate,
|
||
"data_reset_day": carrier.DataResetDay,
|
||
// 通道流量阈值的新增、修改、启用与停用都并入既有运营商更新审计的前后值快照,不另建审计动作。
|
||
"traffic_threshold_enabled": carrier.TrafficThresholdEnabled,
|
||
"traffic_threshold_value": carrier.TrafficThresholdValue,
|
||
"traffic_threshold_unit": carrier.TrafficThresholdUnit,
|
||
}
|
||
}
|
||
|
||
// canManageThreshold 判断当前账号是否具备通道流量阈值字段的读写权限。
|
||
// 只有超级管理员与平台账号可见可写,与 requirePlatformManagement 的判定一致(引用同一用户类型枚举)。
|
||
func canManageThreshold(ctx context.Context) bool {
|
||
userType := middleware.GetUserTypeFromContext(ctx)
|
||
return userType == constants.UserTypeSuperAdmin || userType == constants.UserTypePlatform
|
||
}
|
||
|
||
// validateThresholdConfig 校验通道流量阈值配置:启停仅 0/1、数值必须为正、单位仅 MB/GB、配置齐备。
|
||
// 「启用但无数值/单位」与「只给数值不给单位」都会让达量判定失去唯一口径;
|
||
// 非 0/1 的启停取值会被判定端按「未启用」静默处理,因此必须显式拒绝。
|
||
func validateThresholdConfig(enabled int, value *float64, unit string) error {
|
||
if enabled != constants.StatusDisabled && enabled != constants.StatusEnabled {
|
||
return errors.New(errors.CodeInvalidParam, "通道流量阈值是否启用仅支持 0 或 1")
|
||
}
|
||
if value != nil && *value <= 0 {
|
||
return errors.New(errors.CodeInvalidParam, "通道流量阈值数值必须为正数")
|
||
}
|
||
if unit != "" && unit != carrierthreshold.UnitMB && unit != carrierthreshold.UnitGB {
|
||
return errors.New(errors.CodeInvalidParam, "通道流量阈值单位仅支持 MB 或 GB")
|
||
}
|
||
configured := value != nil || unit != ""
|
||
if configured && (value == nil || unit == "") {
|
||
return errors.New(errors.CodeInvalidParam, "通道流量阈值数值与流量单位必须同时提供")
|
||
}
|
||
if enabled == constants.StatusEnabled && !configured {
|
||
return errors.New(errors.CodeInvalidParam, "启用通道流量阈值必须同时提供正的阈值数值与流量单位")
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// thresholdCreateProbe 构造仅供拒绝审计使用的运营商身份,不写库。
|
||
func thresholdCreateProbe(req *dto.CreateCarrierRequest) *model.Carrier {
|
||
if req == nil {
|
||
return nil
|
||
}
|
||
return &model.Carrier{
|
||
CarrierCode: req.CarrierCode, CarrierName: req.CarrierName, CarrierType: req.CarrierType,
|
||
}
|
||
}
|
||
|
||
// toResponse 把运营商事实投影为响应;阈值字段只对超级管理员与平台账号返回。
|
||
func (s *Service) toResponse(ctx context.Context, c *model.Carrier) *dto.CarrierResponse {
|
||
result := &dto.CarrierResponse{
|
||
ID: c.ID,
|
||
CarrierCode: c.CarrierCode,
|
||
CarrierName: c.CarrierName,
|
||
CarrierType: c.CarrierType,
|
||
Description: c.Description,
|
||
DataResetDay: c.DataResetDay,
|
||
RealnameLinkType: c.RealnameLinkType,
|
||
RealnameLinkTemplate: c.RealnameLinkTemplate,
|
||
Status: c.Status,
|
||
CreatedAt: c.CreatedAt.Format(time.RFC3339),
|
||
UpdatedAt: c.UpdatedAt.Format(time.RFC3339),
|
||
}
|
||
if !canManageThreshold(ctx) {
|
||
return result
|
||
}
|
||
enabled := c.TrafficThresholdEnabled
|
||
result.TrafficThresholdEnabled = &enabled
|
||
result.TrafficThresholdValue = c.TrafficThresholdValue
|
||
if c.TrafficThresholdUnit != "" {
|
||
unit := c.TrafficThresholdUnit
|
||
result.TrafficThresholdUnit = &unit
|
||
}
|
||
return result
|
||
}
|