Files
junhong_cmp_fiber/internal/model/device.go
huang ec86dbf463 feat: 客户端接口数据模型基础准备
- 新增资产状态、订单来源、操作人类型、实名链接类型常量
- 8个模型新增字段(asset_status/generation/source/retail_price等)
- 数据库迁移000082:7张表15+字段,含存量retail_price回填
- BUG-1修复:代理零售价渠道隔离,cost_price分配锁定
- BUG-2修复:一次性佣金仅客户端订单触发
- BUG-4修复:充值回调Store操作纳入事务
- 新增资产手动停用接口(PATCH /iot-cards/:id/deactivate、/devices/:id/deactivate)
- Carrier管理新增实名链接配置
- 后台订单generation写时快照
- BatchUpdatePricing支持retail_price调价目标
- 清理全部H5旧接口和个人客户旧登录方法
2026-03-19 10:56:50 +08:00

144 lines
6.4 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 model
import (
"encoding/json"
"strconv"
"time"
"gorm.io/gorm"
)
// Device 设备模型
// 物联网设备(如 GPS 追踪器、智能传感器)
// 通过 shop_id 区分所有权NULL=平台库存,有值=店铺所有
// 标识符说明virtual_no 为虚拟号/别名imei/sn 为设备真实标识
type Device struct {
gorm.Model
BaseModel `gorm:"embedded"`
VirtualNo string `gorm:"column:virtual_no;type:varchar(100);uniqueIndex:idx_device_virtual_no,where:deleted_at IS NULL;not null;comment:设备虚拟号/别名(用户友好的短标识)" json:"virtual_no"`
IMEI string `gorm:"column:imei;type:varchar(20);comment:设备IMEI(有蜂窝网络的设备标识,用于Gateway API调用)" json:"imei"`
SN string `gorm:"column:sn;type:varchar(100);comment:设备序列号(厂商唯一标识,预留字段)" json:"sn"`
DeviceName string `gorm:"column:device_name;type:varchar(255);comment:设备名称" json:"device_name"`
DeviceModel string `gorm:"column:device_model;type:varchar(100);comment:设备型号" json:"device_model"`
DeviceType string `gorm:"column:device_type;type:varchar(50);comment:设备类型" json:"device_type"`
MaxSimSlots int `gorm:"column:max_sim_slots;type:int;default:4;comment:最大插槽数量(默认4)" json:"max_sim_slots"`
Manufacturer string `gorm:"column:manufacturer;type:varchar(255);comment:制造商" json:"manufacturer"`
BatchNo string `gorm:"column:batch_no;type:varchar(100);comment:批次号" json:"batch_no"`
ShopID *uint `gorm:"column:shop_id;index;comment:店铺ID(NULL=平台库存,有值=店铺所有)" json:"shop_id,omitempty"`
Status int `gorm:"column:status;type:int;default:1;not null;comment:状态 1-在库 2-已分销 3-已激活 4-已停用" json:"status"`
ActivatedAt *time.Time `gorm:"column:activated_at;comment:激活时间" json:"activated_at"`
DeviceUsername string `gorm:"column:device_username;type:varchar(100);comment:设备登录用户名" json:"device_username"`
DevicePasswordEncrypted string `gorm:"column:device_password_encrypted;type:varchar(255);comment:设备登录密码(加密)" json:"device_password_encrypted"`
DeviceAPIEndpoint string `gorm:"column:device_api_endpoint;type:varchar(500);comment:设备API端点" json:"device_api_endpoint"`
SeriesID *uint `gorm:"column:series_id;index;comment:套餐系列ID(关联PackageSeries)" json:"series_id,omitempty"`
FirstCommissionPaid bool `gorm:"column:first_commission_paid;type:boolean;default:false;comment:一次性佣金是否已发放(废弃,使用按系列追踪)" json:"first_commission_paid"`
AccumulatedRecharge int64 `gorm:"column:accumulated_recharge;type:bigint;default:0;comment:累计充值金额(分,废弃,使用按系列追踪)" json:"accumulated_recharge"`
AccumulatedRechargeBySeriesJSON string `gorm:"column:accumulated_recharge_by_series;type:jsonb;default:'{}';comment:按套餐系列追踪的累计充值金额" json:"-"`
FirstRechargeTriggeredBySeriesJSON string `gorm:"column:first_recharge_triggered_by_series;type:jsonb;default:'{}';comment:按套餐系列追踪的首充触发状态" json:"-"`
AssetStatus int `gorm:"column:asset_status;type:int;not null;default:1;comment:业务状态 1-在库 2-已销售 3-已换货 4-已停用" json:"asset_status"`
Generation int `gorm:"column:generation;type:int;not null;default:1;comment:资产世代编号" json:"generation"`
}
// TableName 指定表名
func (Device) TableName() string {
return "tb_device"
}
func (d *Device) GetAccumulatedRechargeBySeriesMap() (map[uint]int64, error) {
result := make(map[uint]int64)
if d.AccumulatedRechargeBySeriesJSON == "" || d.AccumulatedRechargeBySeriesJSON == "{}" {
return result, nil
}
var raw map[string]int64
if err := json.Unmarshal([]byte(d.AccumulatedRechargeBySeriesJSON), &raw); err != nil {
return nil, err
}
for k, v := range raw {
id, err := strconv.ParseUint(k, 10, 64)
if err != nil {
continue
}
result[uint(id)] = v
}
return result, nil
}
func (d *Device) SetAccumulatedRechargeBySeriesMap(m map[uint]int64) error {
raw := make(map[string]int64)
for k, v := range m {
raw[strconv.FormatUint(uint64(k), 10)] = v
}
data, err := json.Marshal(raw)
if err != nil {
return err
}
d.AccumulatedRechargeBySeriesJSON = string(data)
return nil
}
func (d *Device) GetAccumulatedRechargeBySeries(seriesID uint) int64 {
m, err := d.GetAccumulatedRechargeBySeriesMap()
if err != nil {
return 0
}
return m[seriesID]
}
func (d *Device) AddAccumulatedRechargeBySeries(seriesID uint, amount int64) error {
m, err := d.GetAccumulatedRechargeBySeriesMap()
if err != nil {
m = make(map[uint]int64)
}
m[seriesID] += amount
return d.SetAccumulatedRechargeBySeriesMap(m)
}
func (d *Device) GetFirstRechargeTriggeredBySeriesMap() (map[uint]bool, error) {
result := make(map[uint]bool)
if d.FirstRechargeTriggeredBySeriesJSON == "" || d.FirstRechargeTriggeredBySeriesJSON == "{}" {
return result, nil
}
var raw map[string]bool
if err := json.Unmarshal([]byte(d.FirstRechargeTriggeredBySeriesJSON), &raw); err != nil {
return nil, err
}
for k, v := range raw {
id, err := strconv.ParseUint(k, 10, 64)
if err != nil {
continue
}
result[uint(id)] = v
}
return result, nil
}
func (d *Device) SetFirstRechargeTriggeredBySeriesMap(m map[uint]bool) error {
raw := make(map[string]bool)
for k, v := range m {
raw[strconv.FormatUint(uint64(k), 10)] = v
}
data, err := json.Marshal(raw)
if err != nil {
return err
}
d.FirstRechargeTriggeredBySeriesJSON = string(data)
return nil
}
func (d *Device) IsFirstRechargeTriggeredBySeries(seriesID uint) bool {
m, err := d.GetFirstRechargeTriggeredBySeriesMap()
if err != nil {
return false
}
return m[seriesID]
}
func (d *Device) SetFirstRechargeTriggeredBySeries(seriesID uint, triggered bool) error {
m, err := d.GetFirstRechargeTriggeredBySeriesMap()
if err != nil {
m = make(map[uint]bool)
}
m[seriesID] = triggered
return d.SetFirstRechargeTriggeredBySeriesMap(m)
}