企业授权增强与资产列表扩展
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 8m9s

- 企业卡授权唯一约束:新增 DB 迁移(000154),卡级部分唯一索引防止同一张卡被多个企业同时持有,Service 层新增跨企业冲突检测
- 单卡列表新增 network_status 过滤参数
- 单卡/设备列表新增 asset_status、asset_status_name、generation 响应字段
- 单卡/设备列表新增企业维度过滤(authorized_enterprise_id、is_authorized_to_enterprise)及响应中企业授权信息(批量加载,无 N+1)
- 主钱包流水/退款列表新增 asset_identifier 精确过滤参数
- 企业卡授权/收回接口升级为三模式(list/range/filter),企业设备授权/收回升级为双模式(list/filter)
- 升级 sonic v1.14.2 → v1.15.2 以兼容 Go 1.26

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-17 16:23:03 +09:00
parent cf36f1447f
commit 3b7b856e48
37 changed files with 1260 additions and 84 deletions

View File

@@ -18,8 +18,10 @@ type ListDeviceRequest struct {
Manufacturer string `json:"manufacturer" query:"manufacturer" validate:"omitempty,max=255" maxLength:"255" description:"制造商(模糊查询)"`
CreatedAtStart *time.Time `json:"created_at_start" query:"created_at_start" description:"创建时间起始"`
CreatedAtEnd *time.Time `json:"created_at_end" query:"created_at_end" description:"创建时间结束"`
HasActivePackage *bool `json:"has_active_package" query:"has_active_package" description:"是否有生效中的套餐true:有生效中主套餐, false:无生效中主套餐)"`
Keyword string `json:"keyword" query:"keyword" validate:"omitempty,max=100" maxLength:"100" description:"关键字搜索匹配虚拟号或IMEI(模糊查询与virtual_no/imei独立)"`
HasActivePackage *bool `json:"has_active_package" query:"has_active_package" description:"是否有生效中的套餐true:有生效中主套餐, false:无生效中主套餐)"`
Keyword string `json:"keyword" query:"keyword" validate:"omitempty,max=100" maxLength:"100" description:"关键字搜索匹配虚拟号或IMEI(模糊查询与virtual_no/imei独立)"`
AuthorizedEnterpriseID *uint `json:"authorized_enterprise_id" query:"authorized_enterprise_id" description:"按有效授权企业ID过滤只返回当前授权给该企业的设备"`
IsAuthorizedToEnterprise *bool `json:"is_authorized_to_enterprise" query:"is_authorized_to_enterprise" description:"企业授权状态过滤 (true:已授权给某企业, false:未授权任何企业)"`
}
type DeviceResponse struct {
@@ -52,6 +54,13 @@ type DeviceResponse struct {
SoftwareVersion string `json:"software_version" description:"固件版本号"`
SwitchMode string `json:"switch_mode" description:"切卡模式0=自动1=手动"`
LastGatewaySyncAt *time.Time `json:"last_gateway_sync_at" description:"最后 sync-info 同步时间"`
// asset_status=3 表示该资产已换货且尚未执行"旧资产转新",不再流通
// generation>1 表示该资产历史上曾执行过换货后转新,目前仍在使用
AssetStatus int `json:"asset_status" description:"业务状态 (1:在库, 2:已销售, 3:已换货, 4:已停用)"`
AssetStatusName string `json:"asset_status_name" description:"业务状态名称(中文)"`
Generation int `json:"generation" description:"资产世代编号初始值1每次换货转新后+1"`
AuthorizedEnterpriseID *uint `json:"authorized_enterprise_id" description:"当前有效授权的企业ID未授权时为 null"`
AuthorizedEnterpriseName string `json:"authorized_enterprise_name" description:"当前有效授权的企业名称,未授权时为空字符串"`
}
type ListDeviceResponse struct {

View File

@@ -33,9 +33,17 @@ type AllocateCardsPreviewResp struct {
}
type AllocateCardsReq struct {
ID uint `json:"-" params:"id" path:"id" validate:"required" required:"true" description:"企业ID"`
ICCIDs []string `json:"iccids" validate:"required,min=1,max=1000,dive,required" required:"true" description:"需要授权的 ICCID 列表"`
Remark string `json:"remark" validate:"max=500" description:"授权备注"`
ID uint `json:"-" params:"id" path:"id" validate:"required" required:"true" description:"企业ID"`
SelectionType string `json:"selection_type" validate:"required,oneof=list range filter" required:"true" description:"选取模式 (list:指定ICCID列表, range:号段范围, filter:按条件过滤)"`
ICCIDs []string `json:"iccids" validate:"omitempty,max=1000,dive,required" description:"[list模式] ICCID列表最多1000个"`
ICCIDStart string `json:"iccid_start" validate:"omitempty,max=20" maxLength:"20" description:"[range模式] 号段起始ICCID"`
ICCIDEnd string `json:"iccid_end" validate:"omitempty,max=20" maxLength:"20" description:"[range模式] 号段结束ICCID"`
ICCID string `json:"iccid" validate:"omitempty,max=20" maxLength:"20" description:"[filter模式] ICCID模糊匹配"`
BatchNo string `json:"batch_no" validate:"omitempty,max=100" maxLength:"100" description:"[filter模式] 批次号"`
CarrierID *uint `json:"carrier_id" description:"[filter模式] 运营商ID"`
ShopID *uint `json:"shop_id" description:"[filter模式] 店铺ID"`
ShopIDs []uint `json:"shop_ids" description:"[filter模式] 店铺ID列表"`
Remark string `json:"remark" validate:"omitempty,max=500" maxLength:"500" description:"授权备注(所有模式均可选)"`
}
type AllocateCardsResp struct {
@@ -45,8 +53,14 @@ type AllocateCardsResp struct {
}
type RecallCardsReq struct {
ID uint `json:"-" params:"id" path:"id" validate:"required" required:"true" description:"企业ID"`
ICCIDs []string `json:"iccids" validate:"required,min=1,max=1000,dive,required" required:"true" description:"需要回收授权的 ICCID 列表"`
ID uint `json:"-" params:"id" path:"id" validate:"required" required:"true" description:"企业ID"`
SelectionType string `json:"selection_type" validate:"required,oneof=list range filter" required:"true" description:"选取模式 (list:指定ICCID列表, range:号段范围, filter:按条件过滤)"`
ICCIDs []string `json:"iccids" validate:"omitempty,max=1000,dive,required" description:"[list模式] 需要回收授权的ICCID列表"`
ICCIDStart string `json:"iccid_start" validate:"omitempty,max=20" maxLength:"20" description:"[range模式] 号段起始ICCID"`
ICCIDEnd string `json:"iccid_end" validate:"omitempty,max=20" maxLength:"20" description:"[range模式] 号段结束ICCID"`
ICCID string `json:"iccid" validate:"omitempty,max=20" maxLength:"20" description:"[filter模式] ICCID模糊匹配"`
BatchNo string `json:"batch_no" validate:"omitempty,max=100" maxLength:"100" description:"[filter模式] 批次号"`
CarrierID *uint `json:"carrier_id" description:"[filter模式] 运营商ID"`
}
type RecalledDevice struct {

View File

@@ -2,10 +2,15 @@ package dto
import "time"
// AllocateDevicesReq 企业授权设备请求,支持 list/filter 两种模式
type AllocateDevicesReq struct {
ID uint `json:"-" params:"id" path:"id" validate:"required" required:"true" description:"企业ID"`
DeviceNos []string `json:"device_nos" validate:"required,min=1,max=100" description:"设备号列表最多100个"`
Remark string `json:"remark" validate:"max=500" description:"授权备注"`
ID uint `json:"-" params:"id" path:"id" validate:"required" required:"true" description:"企业ID"`
SelectionType string `json:"selection_type" validate:"required,oneof=list filter" required:"true" description:"选取模式 (list:指定设备号列表, filter:按条件过滤)"`
DeviceNos []string `json:"device_nos" validate:"omitempty,max=100" description:"[list模式] 设备号列表最多100个"`
VirtualNo string `json:"virtual_no" validate:"omitempty,max=100" maxLength:"100" description:"[filter模式] 设备虚拟号(模糊匹配)"`
BatchNo string `json:"batch_no" validate:"omitempty,max=100" maxLength:"100" description:"[filter模式] 批次号"`
ShopID *uint `json:"shop_id" description:"[filter模式] 店铺ID"`
Remark string `json:"remark" validate:"omitempty,max=500" maxLength:"500" description:"授权备注(所有模式均可选)"`
}
type AllocateDevicesResp struct {
@@ -26,9 +31,13 @@ type AuthorizedDeviceItem struct {
CardCount int `json:"card_count" description:"绑定卡数量"`
}
// RecallDevicesReq 企业收回设备授权请求,支持 list/filter 两种模式
type RecallDevicesReq struct {
ID uint `json:"-" params:"id" path:"id" validate:"required" required:"true" description:"企业ID"`
DeviceNos []string `json:"device_nos" validate:"required,min=1,max=100" description:"设备号列表最多100个"`
ID uint `json:"-" params:"id" path:"id" validate:"required" required:"true" description:"企业ID"`
SelectionType string `json:"selection_type" validate:"required,oneof=list filter" required:"true" description:"选取模式 (list:指定设备号列表, filter:按条件过滤)"`
DeviceNos []string `json:"device_nos" validate:"omitempty,max=100" description:"[list模式] 设备号列表最多100个"`
VirtualNo string `json:"virtual_no" validate:"omitempty,max=100" maxLength:"100" description:"[filter模式] 设备虚拟号(模糊匹配)"`
BatchNo string `json:"batch_no" validate:"omitempty,max=100" maxLength:"100" description:"[filter模式] 批次号"`
}
type RecallDevicesResp struct {

View File

@@ -23,6 +23,9 @@ type ListStandaloneIotCardRequest struct {
ICCIDEnd string `json:"iccid_end" query:"iccid_end" validate:"omitempty,max=20" maxLength:"20" description:"ICCID结束号"`
CarrierName string `json:"carrier_name" query:"carrier_name" validate:"omitempty,max=100" maxLength:"100" description:"运营商名称(模糊查询)"`
Keyword string `json:"keyword" query:"keyword" validate:"omitempty,max=100" maxLength:"100" description:"关键字搜索匹配ICCID或卡虚拟号(模糊查询与iccid/virtual_no独立)"`
NetworkStatus *int `json:"network_status" query:"network_status" validate:"omitempty,min=0,max=1" minimum:"0" maximum:"1" description:"网络状态 (0:停机, 1:开机)"`
AuthorizedEnterpriseID *uint `json:"authorized_enterprise_id" query:"authorized_enterprise_id" description:"按有效授权企业ID过滤只返回当前授权给该企业的卡"`
IsAuthorizedToEnterprise *bool `json:"is_authorized_to_enterprise" query:"is_authorized_to_enterprise" description:"企业授权状态过滤 (true:已授权给某企业, false:未授权任何企业)"`
}
type StandaloneIotCardResponse struct {
@@ -62,8 +65,15 @@ type StandaloneIotCardResponse struct {
SeriesName string `json:"series_name" description:"套餐系列名称"`
DeviceVirtualNo string `json:"device_virtual_no" description:"绑定设备的虚拟号(未绑定时为空字符串)"`
RealnamePolicy string `json:"realname_policy" description:"实名认证策略 (none:无需实名, before_order:先实名后充值/购买, after_order:先充值/购买后实名)"`
CreatedAt time.Time `json:"created_at" description:"创建时间"`
UpdatedAt time.Time `json:"updated_at" description:"更新时间"`
// asset_status=3 表示该资产已换货且尚未执行"旧资产转新",不再流通
// generation>1 表示该资产历史上曾执行过换货后转新,目前仍在使用
AssetStatus int `json:"asset_status" description:"业务状态 (1:在库, 2:已销售, 3:已换货, 4:已停用)"`
AssetStatusName string `json:"asset_status_name" description:"业务状态名称(中文)"`
Generation int `json:"generation" description:"资产世代编号初始值1每次换货转新后+1"`
AuthorizedEnterpriseID *uint `json:"authorized_enterprise_id" description:"当前有效授权的企业ID未授权时为 null"`
AuthorizedEnterpriseName string `json:"authorized_enterprise_name" description:"当前有效授权的企业名称,未授权时为空字符串"`
CreatedAt time.Time `json:"created_at" description:"创建时间"`
UpdatedAt time.Time `json:"updated_at" description:"更新时间"`
}
type ListStandaloneIotCardResponse struct {

View File

@@ -46,11 +46,12 @@ type ReturnRefundRequest struct {
// RefundListRequest 退款申请列表查询请求
type RefundListRequest struct {
Page int `json:"page" query:"page" validate:"omitempty,min=1" minimum:"1" description:"页码默认1"`
PageSize int `json:"page_size" query:"page_size" validate:"omitempty,min=1,max=100" minimum:"1" maximum:"100" description:"每页数量默认20最大100"`
Status *int `json:"status" query:"status" validate:"omitempty,min=1,max=4" minimum:"1" maximum:"4" description:"状态 (1:待审批, 2:已通过, 3:已拒绝, 4:已退回)"`
OrderID *uint `json:"order_id" query:"order_id" validate:"omitempty" description:"关联订单ID"`
ShopID *uint `json:"shop_id" query:"shop_id" validate:"omitempty" description:"店铺ID"`
Page int `json:"page" query:"page" validate:"omitempty,min=1" minimum:"1" description:"页码默认1"`
PageSize int `json:"page_size" query:"page_size" validate:"omitempty,min=1,max=100" minimum:"1" maximum:"100" description:"每页数量默认20最大100"`
Status *int `json:"status" query:"status" validate:"omitempty,min=1,max=4" minimum:"1" maximum:"4" description:"状态 (1:待审批, 2:已通过, 3:已拒绝, 4:已退回)"`
OrderID *uint `json:"order_id" query:"order_id" validate:"omitempty" description:"关联订单ID"`
ShopID *uint `json:"shop_id" query:"shop_id" validate:"omitempty" description:"店铺ID"`
AssetIdentifier string `json:"asset_identifier" query:"asset_identifier" validate:"omitempty,max=100" maxLength:"100" description:"资产标识精确检索ICCID 或 设备虚拟号,非空时精确匹配)"`
}
// RefundResponse 退款申请详情响应

View File

@@ -50,6 +50,7 @@ type MainWalletTransactionListRequest struct {
TransactionType string `json:"transaction_type" query:"transaction_type" validate:"omitempty,max=50" maxLength:"50" description:"交易类型过滤recharge:充值入账, deduct:套餐扣款, refund:退款)"`
StartDate string `json:"start_date" query:"start_date" validate:"omitempty" description:"开始日期YYYY-MM-DD"`
EndDate string `json:"end_date" query:"end_date" validate:"omitempty" description:"结束日期YYYY-MM-DD"`
AssetIdentifier string `json:"asset_identifier" query:"asset_identifier" validate:"omitempty,max=100" maxLength:"100" description:"资产标识精确检索ICCID 或 设备虚拟号,非空时精确匹配)"`
}
// MainWalletTransactionItem 主钱包流水记录项