This commit is contained in:
98
internal/exporter/device_scene.go
Normal file
98
internal/exporter/device_scene.go
Normal file
@@ -0,0 +1,98 @@
|
||||
package exporter
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/break/junhong_cmp_fiber/internal/model"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/constants"
|
||||
)
|
||||
|
||||
// DeviceSceneStrategy 设备导出场景策略。
|
||||
type DeviceSceneStrategy struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewDeviceSceneStrategy 创建设备场景策略。
|
||||
func NewDeviceSceneStrategy(db *gorm.DB) *DeviceSceneStrategy {
|
||||
return &DeviceSceneStrategy{db: db}
|
||||
}
|
||||
|
||||
// Scene 场景编码。
|
||||
func (s *DeviceSceneStrategy) Scene() string {
|
||||
return constants.ExportTaskSceneDevice
|
||||
}
|
||||
|
||||
// Headers 导出表头。
|
||||
func (s *DeviceSceneStrategy) Headers() []string {
|
||||
return []string{"ID", "设备虚拟号", "设备名称", "设备型号", "设备类型", "状态", "店铺ID", "创建时间"}
|
||||
}
|
||||
|
||||
// NextShardBoundary 获取下一段分片边界。
|
||||
func (s *DeviceSceneStrategy) NextShardBoundary(ctx context.Context, task *model.ExportTask, afterID uint64, limit int) (*ShardBoundary, error) {
|
||||
if limit <= 0 {
|
||||
limit = constants.ExportDefaultShardSize
|
||||
}
|
||||
|
||||
var ids []uint64
|
||||
query := s.db.WithContext(ctx).
|
||||
Model(&model.Device{}).
|
||||
Where("id > ?", afterID).
|
||||
Order("id ASC").
|
||||
Limit(limit)
|
||||
query = applyShopScopeForTask(query, task)
|
||||
|
||||
if err := query.Pluck("id", &ids).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(ids) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return &ShardBoundary{
|
||||
StartID: afterID,
|
||||
EndID: ids[len(ids)-1],
|
||||
RowCount: len(ids),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// QueryRows 查询分片内数据行。
|
||||
func (s *DeviceSceneStrategy) QueryRows(ctx context.Context, task *model.ExportTask, startID, endID uint64) ([][]string, error) {
|
||||
if endID == 0 {
|
||||
return [][]string{}, nil
|
||||
}
|
||||
|
||||
var devices []model.Device
|
||||
query := s.db.WithContext(ctx).
|
||||
Model(&model.Device{}).
|
||||
Where("id > ? AND id <= ?", startID, endID).
|
||||
Order("id ASC")
|
||||
query = applyShopScopeForTask(query, task)
|
||||
|
||||
if err := query.Find(&devices).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rows := make([][]string, 0, len(devices))
|
||||
for _, item := range devices {
|
||||
shopID := ""
|
||||
if item.ShopID != nil {
|
||||
shopID = strconv.FormatUint(uint64(*item.ShopID), 10)
|
||||
}
|
||||
|
||||
rows = append(rows, []string{
|
||||
strconv.FormatUint(uint64(item.ID), 10),
|
||||
item.VirtualNo,
|
||||
item.DeviceName,
|
||||
item.DeviceModel,
|
||||
item.DeviceType,
|
||||
strconv.Itoa(item.Status),
|
||||
shopID,
|
||||
item.CreatedAt.Format("2006-01-02 15:04:05"),
|
||||
})
|
||||
}
|
||||
|
||||
return rows, nil
|
||||
}
|
||||
98
internal/exporter/iot_card_scene.go
Normal file
98
internal/exporter/iot_card_scene.go
Normal file
@@ -0,0 +1,98 @@
|
||||
package exporter
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/break/junhong_cmp_fiber/internal/model"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/constants"
|
||||
)
|
||||
|
||||
// IotCardSceneStrategy IoT 卡导出场景策略。
|
||||
type IotCardSceneStrategy struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewIotCardSceneStrategy 创建 IoT 卡场景策略。
|
||||
func NewIotCardSceneStrategy(db *gorm.DB) *IotCardSceneStrategy {
|
||||
return &IotCardSceneStrategy{db: db}
|
||||
}
|
||||
|
||||
// Scene 场景编码。
|
||||
func (s *IotCardSceneStrategy) Scene() string {
|
||||
return constants.ExportTaskSceneIotCard
|
||||
}
|
||||
|
||||
// Headers 导出表头。
|
||||
func (s *IotCardSceneStrategy) Headers() []string {
|
||||
return []string{"ID", "ICCID", "MSISDN", "虚拟号", "运营商名称", "状态", "店铺ID", "创建时间"}
|
||||
}
|
||||
|
||||
// NextShardBoundary 获取下一段分片边界。
|
||||
func (s *IotCardSceneStrategy) NextShardBoundary(ctx context.Context, task *model.ExportTask, afterID uint64, limit int) (*ShardBoundary, error) {
|
||||
if limit <= 0 {
|
||||
limit = constants.ExportDefaultShardSize
|
||||
}
|
||||
|
||||
var ids []uint64
|
||||
query := s.db.WithContext(ctx).
|
||||
Model(&model.IotCard{}).
|
||||
Where("id > ?", afterID).
|
||||
Order("id ASC").
|
||||
Limit(limit)
|
||||
query = applyShopScopeForTask(query, task)
|
||||
|
||||
if err := query.Pluck("id", &ids).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(ids) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return &ShardBoundary{
|
||||
StartID: afterID,
|
||||
EndID: ids[len(ids)-1],
|
||||
RowCount: len(ids),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// QueryRows 查询分片内数据行。
|
||||
func (s *IotCardSceneStrategy) QueryRows(ctx context.Context, task *model.ExportTask, startID, endID uint64) ([][]string, error) {
|
||||
if endID == 0 {
|
||||
return [][]string{}, nil
|
||||
}
|
||||
|
||||
var cards []model.IotCard
|
||||
query := s.db.WithContext(ctx).
|
||||
Model(&model.IotCard{}).
|
||||
Where("id > ? AND id <= ?", startID, endID).
|
||||
Order("id ASC")
|
||||
query = applyShopScopeForTask(query, task)
|
||||
|
||||
if err := query.Find(&cards).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rows := make([][]string, 0, len(cards))
|
||||
for _, item := range cards {
|
||||
shopID := ""
|
||||
if item.ShopID != nil {
|
||||
shopID = strconv.FormatUint(uint64(*item.ShopID), 10)
|
||||
}
|
||||
|
||||
rows = append(rows, []string{
|
||||
strconv.FormatUint(uint64(item.ID), 10),
|
||||
item.ICCID,
|
||||
item.MSISDN,
|
||||
item.VirtualNo,
|
||||
item.CarrierName,
|
||||
strconv.Itoa(item.Status),
|
||||
shopID,
|
||||
item.CreatedAt.Format("2006-01-02 15:04:05"),
|
||||
})
|
||||
}
|
||||
|
||||
return rows, nil
|
||||
}
|
||||
83
internal/exporter/registry.go
Normal file
83
internal/exporter/registry.go
Normal file
@@ -0,0 +1,83 @@
|
||||
package exporter
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sort"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/break/junhong_cmp_fiber/internal/model"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/constants"
|
||||
)
|
||||
|
||||
// ShardBoundary 分片边界信息。
|
||||
type ShardBoundary struct {
|
||||
StartID uint64
|
||||
EndID uint64
|
||||
RowCount int
|
||||
}
|
||||
|
||||
// SceneStrategy 导出场景策略接口。
|
||||
type SceneStrategy interface {
|
||||
Scene() string
|
||||
Headers() []string
|
||||
NextShardBoundary(ctx context.Context, task *model.ExportTask, afterID uint64, limit int) (*ShardBoundary, error)
|
||||
QueryRows(ctx context.Context, task *model.ExportTask, startID, endID uint64) ([][]string, error)
|
||||
}
|
||||
|
||||
// Registry 场景策略注册中心。
|
||||
type Registry struct {
|
||||
strategies map[string]SceneStrategy
|
||||
}
|
||||
|
||||
// NewRegistry 创建场景策略注册中心。
|
||||
func NewRegistry(strategies ...SceneStrategy) *Registry {
|
||||
m := make(map[string]SceneStrategy, len(strategies))
|
||||
for _, strategy := range strategies {
|
||||
if strategy == nil {
|
||||
continue
|
||||
}
|
||||
m[strategy.Scene()] = strategy
|
||||
}
|
||||
return &Registry{strategies: m}
|
||||
}
|
||||
|
||||
// NewDefaultRegistry 创建默认场景策略注册中心。
|
||||
func NewDefaultRegistry(db *gorm.DB) *Registry {
|
||||
return NewRegistry(
|
||||
NewDeviceSceneStrategy(db),
|
||||
NewIotCardSceneStrategy(db),
|
||||
)
|
||||
}
|
||||
|
||||
// Get 获取指定场景策略。
|
||||
func (r *Registry) Get(scene string) (SceneStrategy, bool) {
|
||||
strategy, ok := r.strategies[scene]
|
||||
return strategy, ok
|
||||
}
|
||||
|
||||
// IsSupported 判断场景是否支持。
|
||||
func (r *Registry) IsSupported(scene string) bool {
|
||||
_, ok := r.strategies[scene]
|
||||
return ok
|
||||
}
|
||||
|
||||
// Scenes 返回当前支持的场景列表。
|
||||
func (r *Registry) Scenes() []string {
|
||||
result := make([]string, 0, len(r.strategies))
|
||||
for scene := range r.strategies {
|
||||
result = append(result, scene)
|
||||
}
|
||||
sort.Strings(result)
|
||||
return result
|
||||
}
|
||||
|
||||
// IsSupportedScene 判断是否为受支持的场景。
|
||||
func IsSupportedScene(scene string) bool {
|
||||
switch scene {
|
||||
case constants.ExportTaskSceneDevice, constants.ExportTaskSceneIotCard:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
26
internal/exporter/scope.go
Normal file
26
internal/exporter/scope.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package exporter
|
||||
|
||||
import (
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/break/junhong_cmp_fiber/internal/model"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/constants"
|
||||
)
|
||||
|
||||
func applyShopScopeForTask(query *gorm.DB, task *model.ExportTask) *gorm.DB {
|
||||
if task == nil {
|
||||
return query.Where("1 = 0")
|
||||
}
|
||||
|
||||
switch task.CreatorUserType {
|
||||
case constants.UserTypeSuperAdmin, constants.UserTypePlatform:
|
||||
return query
|
||||
case constants.UserTypeAgent:
|
||||
if len(task.ScopeShopIDs) == 0 {
|
||||
return query.Where("1 = 0")
|
||||
}
|
||||
return query.Where("shop_id IN ?", []uint(task.ScopeShopIDs))
|
||||
default:
|
||||
return query.Where("1 = 0")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user