This commit is contained in:
@@ -192,7 +192,7 @@ func initServices(s *stores, deps *Dependencies) *services {
|
|||||||
shopCommission := shopCommissionSvc.New(s.Shop, s.Account, s.AgentWallet, s.CommissionWithdrawalRequest, s.CommissionWithdrawalSetting, s.CommissionRecord, s.AgentWalletTransaction, deps.DB, deps.Logger)
|
shopCommission := shopCommissionSvc.New(s.Shop, s.Account, s.AgentWallet, s.CommissionWithdrawalRequest, s.CommissionWithdrawalSetting, s.CommissionRecord, s.AgentWalletTransaction, deps.DB, deps.Logger)
|
||||||
packageService := packageSvc.New(s.Package, s.PackageSeries, s.ShopPackageAllocation, s.ShopSeriesAllocation)
|
packageService := packageSvc.New(s.Package, s.PackageSeries, s.ShopPackageAllocation, s.ShopSeriesAllocation)
|
||||||
orderService := orderSvc.New(deps.DB, deps.Redis, s.Order, s.OrderItem, s.AgentWallet, s.AssetWallet, s.Payment, purchaseValidation, s.ShopPackageAllocation, s.ShopSeriesAllocation, s.IotCard, s.Device, s.PackageSeries, s.PackageUsage, s.Package, wechatConfig, deps.WechatPayment, paymentLoader, deps.QueueClient, deps.Logger, s.AssetIdentifier, s.PersonalCustomer, s.PersonalCustomerPhone)
|
orderService := orderSvc.New(deps.DB, deps.Redis, s.Order, s.OrderItem, s.AgentWallet, s.AssetWallet, s.Payment, purchaseValidation, s.ShopPackageAllocation, s.ShopSeriesAllocation, s.IotCard, s.Device, s.PackageSeries, s.PackageUsage, s.Package, wechatConfig, deps.WechatPayment, paymentLoader, deps.QueueClient, deps.Logger, s.AssetIdentifier, s.PersonalCustomer, s.PersonalCustomerPhone)
|
||||||
assetService := assetSvc.New(deps.DB, s.Device, s.IotCard, s.PackageUsage, s.Package, s.PackageSeries, s.DeviceSimBinding, s.Shop, deps.Redis, iotCard, deps.GatewayClient, s.AssetIdentifier, s.Order, s.OrderItem, s.ExchangeOrder)
|
assetService := assetSvc.New(deps.DB, s.Device, s.IotCard, s.PackageUsage, s.Package, s.PackageSeries, s.DeviceSimBinding, s.Shop, deps.Redis, iotCard, deps.GatewayClient, s.AssetIdentifier, s.Order, s.OrderItem, s.ExchangeOrder, assetAudit)
|
||||||
agentOpenAPI := agentOpenAPISvc.New(assetService, packageService, orderService, shopCommission, stopResumeService, device, s.IotCard, s.PackageUsage, s.Package, s.PackageSeries, s.AgentWallet, s.DeviceSimBinding, s.Device)
|
agentOpenAPI := agentOpenAPISvc.New(assetService, packageService, orderService, shopCommission, stopResumeService, device, s.IotCard, s.PackageUsage, s.Package, s.PackageSeries, s.AgentWallet, s.DeviceSimBinding, s.Device)
|
||||||
|
|
||||||
return &services{
|
return &services{
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package admin
|
|||||||
import (
|
import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
|
|
||||||
@@ -162,6 +163,63 @@ func (h *AssetHandler) CurrentPackage(c *fiber.Ctx) error {
|
|||||||
return response.Success(c, result)
|
return response.Success(c, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdatePackageExpiresAt 修改资产套餐过期时间
|
||||||
|
// PATCH /api/admin/assets/:identifier/packages/:package_usage_id/expires-at
|
||||||
|
func (h *AssetHandler) UpdatePackageExpiresAt(c *fiber.Ctx) error {
|
||||||
|
if err := ensureAssetPackageAdjuster(c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
asset, packageUsageID, err := h.resolveAssetPackageUsagePath(c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var req dto.UpdateAssetPackageExpiresAtRequest
|
||||||
|
if err := c.BodyParser(&req); err != nil {
|
||||||
|
return errors.New(errors.CodeInvalidParam)
|
||||||
|
}
|
||||||
|
expiresAt, err := parseAssetPackageTime(req.ExpiresAt)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New(errors.CodeInvalidParam, "过期时间格式无效")
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := h.assetService.UpdatePackageExpiresAt(c.UserContext(), asset.AssetType, asset.AssetID, assetIdentifierForAudit(asset), packageUsageID, expiresAt)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.Success(c, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdatePackageUsage 修改资产套餐已用量
|
||||||
|
// PATCH /api/admin/assets/:identifier/packages/:package_usage_id/used-data
|
||||||
|
func (h *AssetHandler) UpdatePackageUsage(c *fiber.Ctx) error {
|
||||||
|
if err := ensureAssetPackageAdjuster(c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
asset, packageUsageID, err := h.resolveAssetPackageUsagePath(c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var req dto.UpdateAssetPackageUsageRequest
|
||||||
|
if err := c.BodyParser(&req); err != nil {
|
||||||
|
return errors.New(errors.CodeInvalidParam)
|
||||||
|
}
|
||||||
|
if req.DataUsageMB == nil || *req.DataUsageMB < 0 {
|
||||||
|
return errors.New(errors.CodeInvalidParam)
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := h.assetService.UpdatePackageUsage(c.UserContext(), asset.AssetType, asset.AssetID, assetIdentifierForAudit(asset), packageUsageID, *req.DataUsageMB)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.Success(c, result)
|
||||||
|
}
|
||||||
|
|
||||||
// Stop 停机(设备批量停机,单卡停机)
|
// Stop 停机(设备批量停机,单卡停机)
|
||||||
// POST /api/admin/assets/:identifier/stop
|
// POST /api/admin/assets/:identifier/stop
|
||||||
func (h *AssetHandler) Stop(c *fiber.Ctx) error {
|
func (h *AssetHandler) Stop(c *fiber.Ctx) error {
|
||||||
@@ -440,3 +498,57 @@ func resolveCallerAccountType(c *fiber.Ctx) string {
|
|||||||
}
|
}
|
||||||
return "agent"
|
return "agent"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *AssetHandler) resolveAssetPackageUsagePath(c *fiber.Ctx) (*dto.AssetResolveResponse, uint, error) {
|
||||||
|
asset, err := h.resolveByIdentifier(c)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
packageUsageID, parseErr := strconv.ParseUint(c.Params("package_usage_id"), 10, 64)
|
||||||
|
if parseErr != nil || packageUsageID == 0 {
|
||||||
|
return nil, 0, errors.New(errors.CodeInvalidParam)
|
||||||
|
}
|
||||||
|
|
||||||
|
return asset, uint(packageUsageID), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ensureAssetPackageAdjuster(c *fiber.Ctx) error {
|
||||||
|
if middleware.GetUserIDFromContext(c.UserContext()) != 2 {
|
||||||
|
return errors.New(errors.CodeForbidden, "无权限操作该资源或资源不存在")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseAssetPackageTime(value string) (time.Time, error) {
|
||||||
|
raw := strings.TrimSpace(value)
|
||||||
|
if raw == "" {
|
||||||
|
return time.Time{}, errors.New(errors.CodeInvalidParam)
|
||||||
|
}
|
||||||
|
|
||||||
|
layouts := []string{
|
||||||
|
time.RFC3339,
|
||||||
|
"2006-01-02 15:04:05",
|
||||||
|
"2006-01-02T15:04:05",
|
||||||
|
"2006-01-02",
|
||||||
|
}
|
||||||
|
for _, layout := range layouts {
|
||||||
|
if t, err := time.ParseInLocation(layout, raw, time.Local); err == nil {
|
||||||
|
return t, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return time.Time{}, errors.New(errors.CodeInvalidParam)
|
||||||
|
}
|
||||||
|
|
||||||
|
func assetIdentifierForAudit(asset *dto.AssetResolveResponse) string {
|
||||||
|
if asset == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if asset.AssetType == "card" && strings.TrimSpace(asset.ICCID) != "" {
|
||||||
|
return asset.ICCID
|
||||||
|
}
|
||||||
|
if strings.TrimSpace(asset.VirtualNo) != "" {
|
||||||
|
return asset.VirtualNo
|
||||||
|
}
|
||||||
|
return asset.Identifier
|
||||||
|
}
|
||||||
|
|||||||
@@ -57,14 +57,14 @@ type AssetResolveResponse struct {
|
|||||||
SwitchMode string `json:"switch_mode,omitempty" description:"切卡模式:0=自动,1=手动(设备类型时有效)"`
|
SwitchMode string `json:"switch_mode,omitempty" description:"切卡模式:0=自动,1=手动(设备类型时有效)"`
|
||||||
LastGatewaySyncAt *time.Time `json:"last_gateway_sync_at,omitempty" description:"最后 sync-info 同步时间(设备类型时有效)"`
|
LastGatewaySyncAt *time.Time `json:"last_gateway_sync_at,omitempty" description:"最后 sync-info 同步时间(设备类型时有效)"`
|
||||||
// 卡专属字段(device类型时为零值)
|
// 卡专属字段(device类型时为零值)
|
||||||
CarrierID uint `json:"carrier_id,omitempty" description:"运营商ID"`
|
CarrierID uint `json:"carrier_id,omitempty" description:"运营商ID"`
|
||||||
CarrierType string `json:"carrier_type,omitempty" description:"运营商类型"`
|
CarrierType string `json:"carrier_type,omitempty" description:"运营商类型"`
|
||||||
CarrierName string `json:"carrier_name,omitempty" description:"运营商名称"`
|
CarrierName string `json:"carrier_name,omitempty" description:"运营商名称"`
|
||||||
MSISDN string `json:"msisdn,omitempty" description:"手机号"`
|
MSISDN string `json:"msisdn,omitempty" description:"手机号"`
|
||||||
IMSI string `json:"imsi,omitempty" description:"IMSI"`
|
IMSI string `json:"imsi,omitempty" description:"IMSI"`
|
||||||
CardCategory string `json:"card_category,omitempty" description:"卡业务类型"`
|
CardCategory string `json:"card_category,omitempty" description:"卡业务类型"`
|
||||||
Supplier string `json:"supplier,omitempty" description:"供应商"`
|
Supplier string `json:"supplier,omitempty" description:"供应商"`
|
||||||
EnablePolling bool `json:"enable_polling" description:"是否参与轮询"`
|
EnablePolling bool `json:"enable_polling" description:"是否参与轮询"`
|
||||||
NetworkStatus int `json:"network_status" description:"网络状态:0停机 1开机(asset_type=card时有效)"`
|
NetworkStatus int `json:"network_status" description:"网络状态:0停机 1开机(asset_type=card时有效)"`
|
||||||
GatewayExtend string `json:"gateway_extend" description:"Gateway 卡状态扩展字段,原样返回上游 extend,用于展示运营商侧实际停机原因"`
|
GatewayExtend string `json:"gateway_extend" description:"Gateway 卡状态扩展字段,原样返回上游 extend,用于展示运营商侧实际停机原因"`
|
||||||
GatewayCardIMEI string `json:"gateway_card_imei" description:"插拔卡业务 IMEI(由 Gateway 卡状态接口同步,非设备自身 IMEI,无业务含义,仅供查看)"`
|
GatewayCardIMEI string `json:"gateway_card_imei" description:"插拔卡业务 IMEI(由 Gateway 卡状态接口同步,非设备自身 IMEI,无业务含义,仅供查看)"`
|
||||||
@@ -172,6 +172,20 @@ type AssetPackagesRequest struct {
|
|||||||
Status *int `query:"status" validate:"omitempty,min=0,max=4" minimum:"0" maximum:"4" description:"套餐状态 (0:待生效, 1:生效中, 2:已用完, 3:已过期, 4:已失效)"`
|
Status *int `query:"status" validate:"omitempty,min=0,max=4" minimum:"0" maximum:"4" description:"套餐状态 (0:待生效, 1:生效中, 2:已用完, 3:已过期, 4:已失效)"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateAssetPackageExpiresAtRequest 修改资产套餐过期时间请求
|
||||||
|
type UpdateAssetPackageExpiresAtRequest struct {
|
||||||
|
Identifier string `path:"identifier" description:"资产标识符(虚拟号/ICCID/IMEI/SN/MSISDN)" required:"true"`
|
||||||
|
PackageUsageID uint `path:"package_usage_id" description:"套餐使用记录ID" required:"true"`
|
||||||
|
ExpiresAt string `json:"expires_at" validate:"required" required:"true" description:"新的套餐过期时间,格式:YYYY-MM-DD HH:MM:SS 或 RFC3339"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateAssetPackageUsageRequest 修改资产套餐已用量请求
|
||||||
|
type UpdateAssetPackageUsageRequest struct {
|
||||||
|
Identifier string `path:"identifier" description:"资产标识符(虚拟号/ICCID/IMEI/SN/MSISDN)" required:"true"`
|
||||||
|
PackageUsageID uint `path:"package_usage_id" description:"套餐使用记录ID" required:"true"`
|
||||||
|
DataUsageMB *int64 `json:"data_usage_mb" validate:"required,min=0" required:"true" minimum:"0" description:"新的套餐真实已用量(MB)"`
|
||||||
|
}
|
||||||
|
|
||||||
// DeviceIDRequest 设备ID请求(路径参数)
|
// DeviceIDRequest 设备ID请求(路径参数)
|
||||||
type DeviceIDRequest struct {
|
type DeviceIDRequest struct {
|
||||||
DeviceID uint `path:"device_id" description:"设备ID" required:"true"`
|
DeviceID uint `path:"device_id" description:"设备ID" required:"true"`
|
||||||
|
|||||||
@@ -56,6 +56,24 @@ func registerAssetRoutes(router fiber.Router, handler *admin.AssetHandler, walle
|
|||||||
Auth: true,
|
Auth: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Register(assets, doc, groupPath, "PATCH", "/:identifier/packages/:package_usage_id/expires-at", handler.UpdatePackageExpiresAt, RouteSpec{
|
||||||
|
Summary: "修改资产套餐过期时间",
|
||||||
|
Description: "修改指定资产下某条套餐使用记录的过期时间。仅账号ID为2的账号可调用。",
|
||||||
|
Tags: []string{"资产管理"},
|
||||||
|
Input: new(dto.UpdateAssetPackageExpiresAtRequest),
|
||||||
|
Output: new(dto.AssetPackageResponse),
|
||||||
|
Auth: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
Register(assets, doc, groupPath, "PATCH", "/:identifier/packages/:package_usage_id/used-data", handler.UpdatePackageUsage, RouteSpec{
|
||||||
|
Summary: "修改资产套餐已用量",
|
||||||
|
Description: "修改指定资产下某条套餐使用记录的真实已用量(MB)。仅账号ID为2的账号可调用。",
|
||||||
|
Tags: []string{"资产管理"},
|
||||||
|
Input: new(dto.UpdateAssetPackageUsageRequest),
|
||||||
|
Output: new(dto.AssetPackageResponse),
|
||||||
|
Auth: true,
|
||||||
|
})
|
||||||
|
|
||||||
Register(assets, doc, groupPath, "POST", "/:identifier/stop", handler.Stop, RouteSpec{
|
Register(assets, doc, groupPath, "POST", "/:identifier/stop", handler.Stop, RouteSpec{
|
||||||
Summary: "停机",
|
Summary: "停机",
|
||||||
Description: "设备批量停机设备下所有已实名卡;单卡手动停机。",
|
Description: "设备批量停机设备下所有已实名卡;单卡手动停机。",
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import (
|
|||||||
"github.com/break/junhong_cmp_fiber/internal/gateway"
|
"github.com/break/junhong_cmp_fiber/internal/gateway"
|
||||||
"github.com/break/junhong_cmp_fiber/internal/model"
|
"github.com/break/junhong_cmp_fiber/internal/model"
|
||||||
"github.com/break/junhong_cmp_fiber/internal/model/dto"
|
"github.com/break/junhong_cmp_fiber/internal/model/dto"
|
||||||
|
assetAuditSvc "github.com/break/junhong_cmp_fiber/internal/service/asset_audit"
|
||||||
"github.com/break/junhong_cmp_fiber/internal/store/postgres"
|
"github.com/break/junhong_cmp_fiber/internal/store/postgres"
|
||||||
"github.com/break/junhong_cmp_fiber/pkg/constants"
|
"github.com/break/junhong_cmp_fiber/pkg/constants"
|
||||||
"github.com/break/junhong_cmp_fiber/pkg/errors"
|
"github.com/break/junhong_cmp_fiber/pkg/errors"
|
||||||
@@ -45,6 +46,7 @@ type Service struct {
|
|||||||
iotCardService IotCardRefresher
|
iotCardService IotCardRefresher
|
||||||
gatewayClient *gateway.Client
|
gatewayClient *gateway.Client
|
||||||
assetIdentifierStore *postgres.AssetIdentifierStore
|
assetIdentifierStore *postgres.AssetIdentifierStore
|
||||||
|
assetAuditService assetAuditSvc.OperationLogger
|
||||||
}
|
}
|
||||||
|
|
||||||
// New 创建资产服务实例
|
// New 创建资产服务实例
|
||||||
@@ -64,6 +66,7 @@ func New(
|
|||||||
orderStore *postgres.OrderStore,
|
orderStore *postgres.OrderStore,
|
||||||
orderItemStore *postgres.OrderItemStore,
|
orderItemStore *postgres.OrderItemStore,
|
||||||
exchangeOrderStore *postgres.ExchangeOrderStore,
|
exchangeOrderStore *postgres.ExchangeOrderStore,
|
||||||
|
assetAuditService assetAuditSvc.OperationLogger,
|
||||||
) *Service {
|
) *Service {
|
||||||
return &Service{
|
return &Service{
|
||||||
db: db,
|
db: db,
|
||||||
@@ -81,6 +84,7 @@ func New(
|
|||||||
iotCardService: iotCardService,
|
iotCardService: iotCardService,
|
||||||
gatewayClient: gatewayClient,
|
gatewayClient: gatewayClient,
|
||||||
assetIdentifierStore: assetIdentifierStore,
|
assetIdentifierStore: assetIdentifierStore,
|
||||||
|
assetAuditService: assetAuditService,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -834,49 +838,102 @@ func (s *Service) GetCurrentPackage(ctx context.Context, assetType string, id ui
|
|||||||
return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询当前生效套餐失败")
|
return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询当前生效套餐失败")
|
||||||
}
|
}
|
||||||
|
|
||||||
pkg, pkgErr := s.packageStore.GetByID(ctx, usage.PackageID)
|
return s.buildAssetPackageResponse(ctx, usage, callerAccountType), nil
|
||||||
metrics := usage.BuildTrafficMetrics()
|
}
|
||||||
pkgName := usage.PackageName
|
|
||||||
pkgType := ""
|
// UpdatePackageExpiresAt 修改资产套餐过期时间。
|
||||||
expiryBase := ""
|
func (s *Service) UpdatePackageExpiresAt(ctx context.Context, assetType string, assetID uint, assetIdentifier string, packageUsageID uint, expiresAt time.Time) (*dto.AssetPackageResponse, error) {
|
||||||
if pkgErr == nil && pkg != nil {
|
carrierType, err := normalizeAssetCarrierType(assetType)
|
||||||
if pkgName == "" {
|
if err != nil {
|
||||||
pkgName = pkg.PackageName
|
return nil, err
|
||||||
}
|
|
||||||
pkgType = pkg.PackageType
|
|
||||||
expiryBase = pkg.ExpiryBase
|
|
||||||
}
|
}
|
||||||
var paidAmount *int64
|
|
||||||
if callerAccountType == constants.OwnerTypePlatform {
|
before, err := s.packageUsageStore.GetByIDForCarrier(ctx, packageUsageID, carrierType, assetID)
|
||||||
paidAmount = usage.PaidAmount
|
if err != nil {
|
||||||
|
return nil, wrapPackageUsageNotFound(err)
|
||||||
}
|
}
|
||||||
return &dto.AssetPackageResponse{
|
|
||||||
PackageUsageID: usage.ID,
|
beforeData := packageUsageExpiresAtAuditData(before)
|
||||||
PackageID: usage.PackageID,
|
rows, err := s.packageUsageStore.UpdateExpiresAtForCarrier(ctx, packageUsageID, carrierType, assetID, expiresAt)
|
||||||
PackageName: pkgName,
|
if err != nil {
|
||||||
PackageType: pkgType,
|
appErr := errors.Wrap(errors.CodeDatabaseError, err, "修改套餐过期时间失败")
|
||||||
ExpiryBase: expiryBase,
|
s.logPackageAdjustmentAudit(ctx, constants.AssetAuditOpAssetPackageExpiresAt, "修改资产套餐过期时间失败", constants.AssetAuditResultFailed, assetType, assetID, assetIdentifier, beforeData, map[string]any{
|
||||||
OrderID: usage.OrderID,
|
"package_usage_id": packageUsageID,
|
||||||
OrderNo: usage.OrderNo,
|
"expires_at": expiresAt,
|
||||||
RefundID: usage.RefundID,
|
}, appErr)
|
||||||
RefundNo: usage.RefundNo,
|
return nil, appErr
|
||||||
UsageType: usage.UsageType,
|
}
|
||||||
Status: usage.Status,
|
if rows == 0 {
|
||||||
StatusName: packageStatusName(usage.Status),
|
appErr := errors.New(errors.CodeForbidden, "无权限操作该资源或资源不存在")
|
||||||
RealTotalMB: metrics.RealTotalMB,
|
s.logPackageAdjustmentAudit(ctx, constants.AssetAuditOpAssetPackageExpiresAt, "修改资产套餐过期时间失败", constants.AssetAuditResultFailed, assetType, assetID, assetIdentifier, beforeData, map[string]any{
|
||||||
RealUsedMB: metrics.RealUsedMB,
|
"package_usage_id": packageUsageID,
|
||||||
VirtualTotalMB: metrics.VirtualTotalMB,
|
"expires_at": expiresAt,
|
||||||
VirtualUsedMB: metrics.VirtualUsedMB,
|
}, appErr)
|
||||||
ReductionPct: metrics.ReductionPct,
|
return nil, appErr
|
||||||
EnableVirtualData: usage.EnableVirtualDataSnapshot,
|
}
|
||||||
ActivatedAt: usage.ActivatedAt,
|
|
||||||
ExpiresAt: usage.ExpiresAt,
|
updated, err := s.packageUsageStore.GetByIDForCarrier(ctx, packageUsageID, carrierType, assetID)
|
||||||
MasterUsageID: usage.MasterUsageID,
|
if err != nil {
|
||||||
Priority: usage.Priority,
|
appErr := errors.Wrap(errors.CodeDatabaseError, err, "查询套餐使用记录失败")
|
||||||
PaidAmount: paidAmount,
|
s.logPackageAdjustmentAudit(ctx, constants.AssetAuditOpAssetPackageExpiresAt, "修改资产套餐过期时间失败", constants.AssetAuditResultFailed, assetType, assetID, assetIdentifier, beforeData, map[string]any{
|
||||||
RetailAmount: usage.RetailAmount,
|
"package_usage_id": packageUsageID,
|
||||||
CreatedAt: usage.CreatedAt,
|
"expires_at": expiresAt,
|
||||||
}, nil
|
}, appErr)
|
||||||
|
return nil, appErr
|
||||||
|
}
|
||||||
|
|
||||||
|
s.logPackageAdjustmentAudit(ctx, constants.AssetAuditOpAssetPackageExpiresAt, "修改资产套餐过期时间", constants.AssetAuditResultSuccess, assetType, assetID, assetIdentifier, beforeData, packageUsageExpiresAtAuditData(updated), nil)
|
||||||
|
return s.buildAssetPackageResponse(ctx, updated, constants.OwnerTypePlatform), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdatePackageUsage 修改资产套餐已用量。
|
||||||
|
func (s *Service) UpdatePackageUsage(ctx context.Context, assetType string, assetID uint, assetIdentifier string, packageUsageID uint, dataUsageMB int64) (*dto.AssetPackageResponse, error) {
|
||||||
|
if dataUsageMB < 0 {
|
||||||
|
return nil, errors.New(errors.CodeInvalidParam)
|
||||||
|
}
|
||||||
|
|
||||||
|
carrierType, err := normalizeAssetCarrierType(assetType)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
before, err := s.packageUsageStore.GetByIDForCarrier(ctx, packageUsageID, carrierType, assetID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, wrapPackageUsageNotFound(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeData := packageUsageTrafficAuditData(before)
|
||||||
|
nextStatus := statusForManualDataUsage(before, dataUsageMB)
|
||||||
|
rows, err := s.packageUsageStore.UpdateDataUsageForCarrier(ctx, packageUsageID, carrierType, assetID, dataUsageMB, nextStatus)
|
||||||
|
if err != nil {
|
||||||
|
appErr := errors.Wrap(errors.CodeDatabaseError, err, "修改套餐已用量失败")
|
||||||
|
s.logPackageAdjustmentAudit(ctx, constants.AssetAuditOpAssetPackageUsage, "修改资产套餐已用量失败", constants.AssetAuditResultFailed, assetType, assetID, assetIdentifier, beforeData, map[string]any{
|
||||||
|
"package_usage_id": packageUsageID,
|
||||||
|
"data_usage_mb": dataUsageMB,
|
||||||
|
}, appErr)
|
||||||
|
return nil, appErr
|
||||||
|
}
|
||||||
|
if rows == 0 {
|
||||||
|
appErr := errors.New(errors.CodeForbidden, "无权限操作该资源或资源不存在")
|
||||||
|
s.logPackageAdjustmentAudit(ctx, constants.AssetAuditOpAssetPackageUsage, "修改资产套餐已用量失败", constants.AssetAuditResultFailed, assetType, assetID, assetIdentifier, beforeData, map[string]any{
|
||||||
|
"package_usage_id": packageUsageID,
|
||||||
|
"data_usage_mb": dataUsageMB,
|
||||||
|
}, appErr)
|
||||||
|
return nil, appErr
|
||||||
|
}
|
||||||
|
|
||||||
|
updated, err := s.packageUsageStore.GetByIDForCarrier(ctx, packageUsageID, carrierType, assetID)
|
||||||
|
if err != nil {
|
||||||
|
appErr := errors.Wrap(errors.CodeDatabaseError, err, "查询套餐使用记录失败")
|
||||||
|
s.logPackageAdjustmentAudit(ctx, constants.AssetAuditOpAssetPackageUsage, "修改资产套餐已用量失败", constants.AssetAuditResultFailed, assetType, assetID, assetIdentifier, beforeData, map[string]any{
|
||||||
|
"package_usage_id": packageUsageID,
|
||||||
|
"data_usage_mb": dataUsageMB,
|
||||||
|
}, appErr)
|
||||||
|
return nil, appErr
|
||||||
|
}
|
||||||
|
|
||||||
|
s.logPackageAdjustmentAudit(ctx, constants.AssetAuditOpAssetPackageUsage, "修改资产套餐已用量", constants.AssetAuditResultSuccess, assetType, assetID, assetIdentifier, beforeData, packageUsageTrafficAuditData(updated), nil)
|
||||||
|
return s.buildAssetPackageResponse(ctx, updated, constants.OwnerTypePlatform), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getDeviceProtectStatus 查询设备保护期状态
|
// getDeviceProtectStatus 查询设备保护期状态
|
||||||
@@ -973,6 +1030,153 @@ func (s *Service) resolveAssetGeneration(ctx context.Context, assetType string,
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Service) buildAssetPackageResponse(ctx context.Context, usage *model.PackageUsage, callerAccountType string) *dto.AssetPackageResponse {
|
||||||
|
if usage == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
pkg, pkgErr := s.packageStore.GetByID(ctx, usage.PackageID)
|
||||||
|
metrics := usage.BuildTrafficMetrics()
|
||||||
|
pkgName := usage.PackageName
|
||||||
|
pkgType := ""
|
||||||
|
expiryBase := ""
|
||||||
|
if pkgErr == nil && pkg != nil {
|
||||||
|
if pkgName == "" {
|
||||||
|
pkgName = pkg.PackageName
|
||||||
|
}
|
||||||
|
pkgType = pkg.PackageType
|
||||||
|
expiryBase = pkg.ExpiryBase
|
||||||
|
}
|
||||||
|
var paidAmount *int64
|
||||||
|
if callerAccountType == constants.OwnerTypePlatform {
|
||||||
|
paidAmount = usage.PaidAmount
|
||||||
|
}
|
||||||
|
|
||||||
|
return &dto.AssetPackageResponse{
|
||||||
|
PackageUsageID: usage.ID,
|
||||||
|
PackageID: usage.PackageID,
|
||||||
|
PackageName: pkgName,
|
||||||
|
PackageType: pkgType,
|
||||||
|
ExpiryBase: expiryBase,
|
||||||
|
OrderID: usage.OrderID,
|
||||||
|
OrderNo: usage.OrderNo,
|
||||||
|
RefundID: usage.RefundID,
|
||||||
|
RefundNo: usage.RefundNo,
|
||||||
|
UsageType: usage.UsageType,
|
||||||
|
Status: usage.Status,
|
||||||
|
StatusName: packageStatusName(usage.Status),
|
||||||
|
RealTotalMB: metrics.RealTotalMB,
|
||||||
|
RealUsedMB: metrics.RealUsedMB,
|
||||||
|
VirtualTotalMB: metrics.VirtualTotalMB,
|
||||||
|
VirtualUsedMB: metrics.VirtualUsedMB,
|
||||||
|
ReductionPct: metrics.ReductionPct,
|
||||||
|
EnableVirtualData: usage.EnableVirtualDataSnapshot,
|
||||||
|
ActivatedAt: usage.ActivatedAt,
|
||||||
|
ExpiresAt: usage.ExpiresAt,
|
||||||
|
MasterUsageID: usage.MasterUsageID,
|
||||||
|
Priority: usage.Priority,
|
||||||
|
PaidAmount: paidAmount,
|
||||||
|
RetailAmount: usage.RetailAmount,
|
||||||
|
CreatedAt: usage.CreatedAt,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalizeAssetCarrierType(assetType string) (string, error) {
|
||||||
|
switch assetType {
|
||||||
|
case "card", constants.AssetTypeIotCard:
|
||||||
|
return constants.AssetTypeIotCard, nil
|
||||||
|
case constants.AssetTypeDevice:
|
||||||
|
return constants.AssetTypeDevice, nil
|
||||||
|
default:
|
||||||
|
return "", errors.New(errors.CodeInvalidParam, "不支持的资产类型,仅支持 card 或 device")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func wrapPackageUsageNotFound(err error) error {
|
||||||
|
if stderrors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return errors.New(errors.CodeForbidden, "无权限操作该资源或资源不存在")
|
||||||
|
}
|
||||||
|
return errors.Wrap(errors.CodeDatabaseError, err, "查询套餐使用记录失败")
|
||||||
|
}
|
||||||
|
|
||||||
|
func statusForManualDataUsage(usage *model.PackageUsage, dataUsageMB int64) *int {
|
||||||
|
if usage == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
threshold := usage.EffectiveThresholdMB()
|
||||||
|
if threshold <= 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
switch usage.Status {
|
||||||
|
case constants.PackageUsageStatusActive:
|
||||||
|
if dataUsageMB >= threshold {
|
||||||
|
status := constants.PackageUsageStatusDepleted
|
||||||
|
return &status
|
||||||
|
}
|
||||||
|
case constants.PackageUsageStatusDepleted:
|
||||||
|
if dataUsageMB < threshold {
|
||||||
|
status := constants.PackageUsageStatusActive
|
||||||
|
return &status
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func packageUsageExpiresAtAuditData(usage *model.PackageUsage) map[string]any {
|
||||||
|
if usage == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return map[string]any{
|
||||||
|
"package_usage_id": usage.ID,
|
||||||
|
"expires_at": usage.ExpiresAt,
|
||||||
|
"status": usage.Status,
|
||||||
|
"status_name": packageStatusName(usage.Status),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func packageUsageTrafficAuditData(usage *model.PackageUsage) map[string]any {
|
||||||
|
if usage == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
metrics := usage.BuildTrafficMetrics()
|
||||||
|
return map[string]any{
|
||||||
|
"package_usage_id": usage.ID,
|
||||||
|
"data_usage_mb": usage.DataUsageMB,
|
||||||
|
"virtual_used_mb": metrics.VirtualUsedMB,
|
||||||
|
"real_total_mb": metrics.RealTotalMB,
|
||||||
|
"virtual_total_mb": metrics.VirtualTotalMB,
|
||||||
|
"status": usage.Status,
|
||||||
|
"status_name": packageStatusName(usage.Status),
|
||||||
|
"enable_virtual_data": usage.EnableVirtualDataSnapshot,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) logPackageAdjustmentAudit(ctx context.Context, operationType, operationDesc, resultStatus, assetType string, assetID uint, assetIdentifier string, beforeData, afterData map[string]any, err error) {
|
||||||
|
if s == nil || s.assetAuditService == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
errorCode, errorMsg := assetAuditSvc.BuildErrorInfo(err)
|
||||||
|
wrappedBefore, wrappedAfter := assetAuditSvc.WrapOperationContent(beforeData, afterData, map[string]any{
|
||||||
|
"asset_type": assetAuditSvc.NormalizeAssetType(assetType),
|
||||||
|
"asset_id": assetID,
|
||||||
|
"asset_identifier": assetIdentifier,
|
||||||
|
})
|
||||||
|
s.assetAuditService.LogOperation(ctx, assetAuditSvc.BuildLog(ctx, assetAuditSvc.BuildLogParams{
|
||||||
|
Operator: assetAuditSvc.OperatorFromContext(ctx),
|
||||||
|
AssetType: assetType,
|
||||||
|
AssetID: assetID,
|
||||||
|
AssetIdentifier: assetIdentifier,
|
||||||
|
OperationType: operationType,
|
||||||
|
OperationDesc: operationDesc,
|
||||||
|
BeforeData: wrappedBefore,
|
||||||
|
AfterData: wrappedAfter,
|
||||||
|
ResultStatus: resultStatus,
|
||||||
|
ErrorCode: errorCode,
|
||||||
|
ErrorMsg: errorMsg,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
// tracePreviousGenerations 通过换货链逆向追溯前代资产的订单,最多追溯10代
|
// tracePreviousGenerations 通过换货链逆向追溯前代资产的订单,最多追溯10代
|
||||||
func (s *Service) tracePreviousGenerations(ctx context.Context, assetType string, assetID uint) ([]*dto.PreviousGenerationOrders, bool) {
|
func (s *Service) tracePreviousGenerations(ctx context.Context, assetType string, assetID uint) ([]*dto.PreviousGenerationOrders, bool) {
|
||||||
const maxDepth = 10
|
const maxDepth = 10
|
||||||
|
|||||||
@@ -339,6 +339,24 @@ func getOperationFieldDesc(operationType string) map[string]string {
|
|||||||
return map[string]string{
|
return map[string]string{
|
||||||
"realname_policy": "实名策略(none/before_order/after_order)",
|
"realname_policy": "实名策略(none/before_order/after_order)",
|
||||||
}
|
}
|
||||||
|
case "asset_package_expires_at":
|
||||||
|
return map[string]string{
|
||||||
|
"package_usage_id": "套餐使用记录ID",
|
||||||
|
"expires_at": "套餐过期时间",
|
||||||
|
"status": "套餐状态",
|
||||||
|
"status_name": "套餐状态名称",
|
||||||
|
}
|
||||||
|
case "asset_package_usage":
|
||||||
|
return map[string]string{
|
||||||
|
"package_usage_id": "套餐使用记录ID",
|
||||||
|
"data_usage_mb": "套餐真实已用量(MB)",
|
||||||
|
"virtual_used_mb": "套餐展示已用量(MB)",
|
||||||
|
"real_total_mb": "套餐真实总量(MB)",
|
||||||
|
"virtual_total_mb": "套餐业务停机阈值(MB)",
|
||||||
|
"status": "套餐状态",
|
||||||
|
"status_name": "套餐状态名称",
|
||||||
|
"enable_virtual_data": "是否启用虚流量",
|
||||||
|
}
|
||||||
case "card_realname_status":
|
case "card_realname_status":
|
||||||
return map[string]string{
|
return map[string]string{
|
||||||
"real_name_status": "实名状态(0未实名/1已实名)",
|
"real_name_status": "实名状态(0未实名/1已实名)",
|
||||||
|
|||||||
@@ -42,6 +42,17 @@ func (s *PackageUsageStore) GetByID(ctx context.Context, id uint) (*model.Packag
|
|||||||
return &usage, nil
|
return &usage, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetByIDForCarrier 根据载体查询套餐使用记录。
|
||||||
|
func (s *PackageUsageStore) GetByIDForCarrier(ctx context.Context, id uint, carrierType string, carrierID uint) (*model.PackageUsage, error) {
|
||||||
|
var usage model.PackageUsage
|
||||||
|
query := s.db.WithContext(ctx).Where("id = ?", id)
|
||||||
|
query = applyPackageUsageCarrierFilter(query, carrierType, carrierID)
|
||||||
|
if err := query.First(&usage).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &usage, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Update 更新套餐使用记录
|
// Update 更新套餐使用记录
|
||||||
func (s *PackageUsageStore) Update(ctx context.Context, usage *model.PackageUsage) error {
|
func (s *PackageUsageStore) Update(ctx context.Context, usage *model.PackageUsage) error {
|
||||||
return s.db.WithContext(ctx).Save(usage).Error
|
return s.db.WithContext(ctx).Save(usage).Error
|
||||||
@@ -140,6 +151,37 @@ func (s *PackageUsageStore) UpdateDataUsage(ctx context.Context, id uint, increm
|
|||||||
Update("data_usage_mb", gorm.Expr("data_usage_mb + ?", incrementMB)).Error
|
Update("data_usage_mb", gorm.Expr("data_usage_mb + ?", incrementMB)).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateExpiresAtForCarrier 按载体条件更新套餐过期时间。
|
||||||
|
func (s *PackageUsageStore) UpdateExpiresAtForCarrier(ctx context.Context, id uint, carrierType string, carrierID uint, expiresAt time.Time) (int64, error) {
|
||||||
|
query := s.db.WithContext(ctx).
|
||||||
|
Model(&model.PackageUsage{}).
|
||||||
|
Where("id = ?", id)
|
||||||
|
query = applyPackageUsageCarrierFilter(query, carrierType, carrierID)
|
||||||
|
|
||||||
|
result := query.Updates(map[string]any{
|
||||||
|
"expires_at": expiresAt,
|
||||||
|
})
|
||||||
|
return result.RowsAffected, result.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateDataUsageForCarrier 按载体条件更新套餐已用量。
|
||||||
|
func (s *PackageUsageStore) UpdateDataUsageForCarrier(ctx context.Context, id uint, carrierType string, carrierID uint, dataUsageMB int64, status *int) (int64, error) {
|
||||||
|
query := s.db.WithContext(ctx).
|
||||||
|
Model(&model.PackageUsage{}).
|
||||||
|
Where("id = ?", id)
|
||||||
|
query = applyPackageUsageCarrierFilter(query, carrierType, carrierID)
|
||||||
|
|
||||||
|
updates := map[string]any{
|
||||||
|
"data_usage_mb": dataUsageMB,
|
||||||
|
}
|
||||||
|
if status != nil {
|
||||||
|
updates["status"] = *status
|
||||||
|
}
|
||||||
|
|
||||||
|
result := query.Updates(updates)
|
||||||
|
return result.RowsAffected, result.Error
|
||||||
|
}
|
||||||
|
|
||||||
// GetPackagesForReset 查询需要重置的套餐(WHERE next_reset_at <= NOW)
|
// GetPackagesForReset 查询需要重置的套餐(WHERE next_reset_at <= NOW)
|
||||||
func (s *PackageUsageStore) GetPackagesForReset(ctx context.Context, limit int) ([]*model.PackageUsage, error) {
|
func (s *PackageUsageStore) GetPackagesForReset(ctx context.Context, limit int) ([]*model.PackageUsage, error) {
|
||||||
var usages []*model.PackageUsage
|
var usages []*model.PackageUsage
|
||||||
@@ -239,11 +281,7 @@ func (s *PackageUsageStore) ListByCarrier(ctx context.Context, carrierType strin
|
|||||||
var usages []*model.PackageUsage
|
var usages []*model.PackageUsage
|
||||||
query := s.db.WithContext(ctx).Order("priority ASC, created_at DESC")
|
query := s.db.WithContext(ctx).Order("priority ASC, created_at DESC")
|
||||||
|
|
||||||
if carrierType == "iot_card" {
|
query = applyPackageUsageCarrierFilter(query, carrierType, carrierID)
|
||||||
query = query.Where("iot_card_id = ?", carrierID)
|
|
||||||
} else if carrierType == "device" {
|
|
||||||
query = query.Where("device_id = ?", carrierID)
|
|
||||||
}
|
|
||||||
if status != nil {
|
if status != nil {
|
||||||
query = query.Where("status = ?", *status)
|
query = query.Where("status = ?", *status)
|
||||||
}
|
}
|
||||||
@@ -253,3 +291,13 @@ func (s *PackageUsageStore) ListByCarrier(ctx context.Context, carrierType strin
|
|||||||
}
|
}
|
||||||
return usages, nil
|
return usages, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func applyPackageUsageCarrierFilter(query *gorm.DB, carrierType string, carrierID uint) *gorm.DB {
|
||||||
|
if carrierType == "iot_card" {
|
||||||
|
return query.Where("iot_card_id = ?", carrierID)
|
||||||
|
}
|
||||||
|
if carrierType == "device" {
|
||||||
|
return query.Where("device_id = ?", carrierID)
|
||||||
|
}
|
||||||
|
return query
|
||||||
|
}
|
||||||
|
|||||||
@@ -18,36 +18,38 @@ const (
|
|||||||
|
|
||||||
// 资产审计日志操作类型常量
|
// 资产审计日志操作类型常量
|
||||||
const (
|
const (
|
||||||
AssetAuditOpCardAllocate = "card_allocate" // 单卡分配
|
AssetAuditOpCardAllocate = "card_allocate" // 单卡分配
|
||||||
AssetAuditOpCardRecall = "card_recall" // 单卡回收
|
AssetAuditOpCardRecall = "card_recall" // 单卡回收
|
||||||
AssetAuditOpCardSeriesBinding = "card_series_binding" // 卡系列绑定
|
AssetAuditOpCardSeriesBinding = "card_series_binding" // 卡系列绑定
|
||||||
AssetAuditOpCardPollingStatus = "card_polling_status" // 卡轮询状态更新
|
AssetAuditOpCardPollingStatus = "card_polling_status" // 卡轮询状态更新
|
||||||
AssetAuditOpCardRealnamePolicy = "card_realname_policy" // 卡实名策略更新
|
AssetAuditOpCardRealnamePolicy = "card_realname_policy" // 卡实名策略更新
|
||||||
AssetAuditOpCardRealnameStatus = "card_realname_status" // 卡实名状态更新
|
AssetAuditOpCardRealnameStatus = "card_realname_status" // 卡实名状态更新
|
||||||
AssetAuditOpCardDelete = "card_delete" // 删除卡
|
AssetAuditOpCardDelete = "card_delete" // 删除卡
|
||||||
AssetAuditOpCardBatchDelete = "card_batch_delete" // 批量删除卡
|
AssetAuditOpCardBatchDelete = "card_batch_delete" // 批量删除卡
|
||||||
AssetAuditOpCardManualStop = "card_manual_stop" // 手动停卡
|
AssetAuditOpCardManualStop = "card_manual_stop" // 手动停卡
|
||||||
AssetAuditOpCardManualStart = "card_manual_start" // 手动复机
|
AssetAuditOpCardManualStart = "card_manual_start" // 手动复机
|
||||||
AssetAuditOpCardAutoStop = "card_auto_stop" // 自动停卡
|
AssetAuditOpCardAutoStop = "card_auto_stop" // 自动停卡
|
||||||
AssetAuditOpCardAutoStart = "card_auto_start" // 自动复机
|
AssetAuditOpCardAutoStart = "card_auto_start" // 自动复机
|
||||||
AssetAuditOpDeviceDelete = "device_delete" // 删除设备
|
AssetAuditOpDeviceDelete = "device_delete" // 删除设备
|
||||||
AssetAuditOpDeviceAllocate = "device_allocate" // 设备分配
|
AssetAuditOpDeviceAllocate = "device_allocate" // 设备分配
|
||||||
AssetAuditOpDeviceRecall = "device_recall" // 设备回收
|
AssetAuditOpDeviceRecall = "device_recall" // 设备回收
|
||||||
AssetAuditOpDeviceSeriesBinding = "device_series_binding" // 设备系列绑定
|
AssetAuditOpDeviceSeriesBinding = "device_series_binding" // 设备系列绑定
|
||||||
AssetAuditOpDeviceBindCard = "device_bind_card" // 设备绑卡
|
AssetAuditOpDeviceBindCard = "device_bind_card" // 设备绑卡
|
||||||
AssetAuditOpDeviceUnbindCard = "device_unbind_card" // 设备解绑卡
|
AssetAuditOpDeviceUnbindCard = "device_unbind_card" // 设备解绑卡
|
||||||
AssetAuditOpDeviceStop = "device_stop" // 设备停机
|
AssetAuditOpDeviceStop = "device_stop" // 设备停机
|
||||||
AssetAuditOpDeviceStart = "device_start" // 设备复机
|
AssetAuditOpDeviceStart = "device_start" // 设备复机
|
||||||
AssetAuditOpDeviceSpeedLimit = "device_speed_limit" // 设备限速
|
AssetAuditOpDeviceSpeedLimit = "device_speed_limit" // 设备限速
|
||||||
AssetAuditOpDeviceSetWiFi = "device_set_wifi" // 设备 WiFi 设置
|
AssetAuditOpDeviceSetWiFi = "device_set_wifi" // 设备 WiFi 设置
|
||||||
AssetAuditOpDeviceSwitchCard = "device_switch_card" // 设备切卡
|
AssetAuditOpDeviceSwitchCard = "device_switch_card" // 设备切卡
|
||||||
AssetAuditOpDeviceSwitchMode = "device_switch_mode" // 设备切卡模式
|
AssetAuditOpDeviceSwitchMode = "device_switch_mode" // 设备切卡模式
|
||||||
AssetAuditOpDeviceReboot = "device_reboot" // 设备重启
|
AssetAuditOpDeviceReboot = "device_reboot" // 设备重启
|
||||||
AssetAuditOpDeviceReset = "device_reset" // 设备恢复出厂
|
AssetAuditOpDeviceReset = "device_reset" // 设备恢复出厂
|
||||||
AssetAuditOpAssetDeactivate = "asset_deactivate" // 资产停用
|
AssetAuditOpAssetDeactivate = "asset_deactivate" // 资产停用
|
||||||
AssetAuditOpAssetPollingStatus = "asset_polling_status" // 统一入口轮询状态更新
|
AssetAuditOpAssetPollingStatus = "asset_polling_status" // 统一入口轮询状态更新
|
||||||
AssetAuditOpAssetRealnamePolicy = "asset_realname_policy" // 统一入口实名策略更新
|
AssetAuditOpAssetRealnamePolicy = "asset_realname_policy" // 统一入口实名策略更新
|
||||||
AssetAuditOpDeviceImportTaskCreate = "device_import_task_create" // 设备导入任务创建
|
AssetAuditOpAssetPackageExpiresAt = "asset_package_expires_at" // 资产套餐过期时间更新
|
||||||
|
AssetAuditOpAssetPackageUsage = "asset_package_usage" // 资产套餐已用量更新
|
||||||
|
AssetAuditOpDeviceImportTaskCreate = "device_import_task_create" // 设备导入任务创建
|
||||||
AssetAuditOpIotCardImportTaskCreate = "iot_card_import_task_create" // 卡导入任务创建
|
AssetAuditOpIotCardImportTaskCreate = "iot_card_import_task_create" // 卡导入任务创建
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user