You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
355 lines
10 KiB
355 lines
10 KiB
package service |
|
|
|
import ( |
|
"database/sql" |
|
"fmt" |
|
|
|
"go-common/app/admin/main/appstatic/model" |
|
"go-common/library/log" |
|
|
|
"github.com/jinzhu/gorm" |
|
) |
|
|
|
const ( |
|
// file type |
|
_fullPackage = 0 |
|
_diffPackge = 1 |
|
// diff file name format |
|
_diffFormat = "Mod_%d/V_%d-V_%d.bspatch" |
|
// limit column |
|
deviceCol = "device" |
|
mobiAppCol = "mobi_app" |
|
platCol = "plat" |
|
buildCol = "build" |
|
sysverCol = "sysver" |
|
scaleCol = "scale" |
|
levelCol = "level" |
|
archCol = "arch" |
|
// condition column |
|
_bk = "bk" |
|
_wt = "wt" |
|
buildLtCdt = "lt" |
|
buildGtCdt = "gt" |
|
buildLeCdt = "le" |
|
buildGeCdt = "ge" |
|
_valid = 1 |
|
) |
|
|
|
// GenerateVer generates a new version ( resource ) and cover the diff logic |
|
func (s *Service) GenerateVer(resName string, limitData *model.Limit, fInfo *model.FileInfo, pool *model.ResourcePool, defPkg int) (resID int, version int, err error) { |
|
// create a new version |
|
var tx = s.DB.Begin() |
|
var resource = tx.Create(transResource(resName, pool.ID)) |
|
if err = resource.Error; err != nil { |
|
log.Error("GenerateVer DBCreate Resource Error(%v)", err) |
|
tx.Rollback() |
|
return |
|
} |
|
resID = int(resource.Value.(*model.Resource).ID) |
|
log.Info("Resource Generated: ID = %d", resID) |
|
// create the full package in File Table |
|
if err = tx.Create(transFile(fInfo, resID)).Error; err != nil { |
|
log.Error("GenerateVer DBCreate ResourceFile Error(%v)", err) |
|
tx.Rollback() |
|
return |
|
} |
|
// create the resource config |
|
var config = tx.Create(transConfig(int64(resID), limitData)) |
|
if err = config.Error; err != nil { |
|
log.Error("GenerateVer DBCreate ResoureConfig Error(%v)", err) |
|
tx.Rollback() |
|
return |
|
} |
|
configID := int64(config.Value.(*model.ResourceConfig).ID) |
|
log.Info("Resource Config Generated: ID = %d", configID) |
|
// create the resource limits |
|
limits := createLimit(configID, limitData) |
|
if len(limits) != 0 { |
|
for _, v := range limits { |
|
if err = tx.Create(v).Error; err != nil { |
|
log.Error("GenerateVer DCreate ResourceLimit (%v) Error(%v)", v, err) |
|
tx.Rollback() |
|
return |
|
} |
|
} |
|
} else { |
|
log.Error("[GenerateVer]-[createLimit]-No limit to create") |
|
} |
|
// commit the transaction |
|
tx.Commit() |
|
log.Info("Transaction Committed, ResID: %d", resID) |
|
// treat the default package setting |
|
if defPkg == 1 { |
|
if err = s.DefaultPkg(resID, pool.ID, configID); err != nil { |
|
log.Error("defaultPkg Error (%v)", err) |
|
return |
|
} |
|
} |
|
// defines the version of this resource |
|
if version, err = s.defineVer(resID, pool.ID); err != nil { |
|
log.Error("defineVer Error (%v)", err) |
|
return |
|
} |
|
// create diff records |
|
if err = s.createDiff(resID); err != nil { |
|
log.Error("[GenerateVer]-[createDiff]-Error(%v)", err) |
|
return |
|
} |
|
return |
|
} |
|
|
|
// DefaultPkg sets the resID's config as default package, and resets the other resources' config |
|
func (s *Service) DefaultPkg(resID int, poolID int64, confID int64) (err error) { |
|
var ( |
|
tx = s.DB.Begin() |
|
rows *sql.Rows |
|
rid int // resource id |
|
) |
|
// find out all the resources under the same pool, and put them as non-default pkg |
|
if rows, err = s.DB.Model(&model.Resource{}).Where("pool_id = ?", poolID).Select("id").Rows(); err != nil { |
|
return |
|
} |
|
for rows.Next() { |
|
if err = rows.Scan(&rid); err != nil { |
|
tx.Rollback() |
|
return |
|
} |
|
if err = tx.Model(&model.ResourceConfig{}).Where("resource_id = ?", rid).Update("default_package", 0).Error; err != nil { |
|
tx.Rollback() |
|
return |
|
} |
|
} |
|
// defines the new package as the default pkg |
|
if err = tx.Model(&model.ResourceConfig{}).Where("id = ?", confID).Update("default_package", 1).Error; err != nil { |
|
tx.Rollback() |
|
return |
|
} |
|
tx.Commit() |
|
return |
|
} |
|
|
|
// defines the version of the resource after the transaction commited |
|
func (s *Service) defineVer(resID int, poolID int64) (version int, err error) { |
|
var maxVer = model.Resource{} |
|
if err = s.DB.Where("id < ?", resID).Where("pool_id = ?", poolID).Order("version desc").First(&maxVer).Error; err == gorm.ErrRecordNotFound { |
|
err = nil |
|
} |
|
if err != nil { |
|
log.Error("GenerateVer DBFind ResourceVer (%d) Error(%v)", resID, err) |
|
return |
|
} |
|
version = int(maxVer.Version) + 1 |
|
if err = s.DB.Model(&model.Resource{}).Where("id = ?", resID).Update("version", version).Error; err != nil { |
|
log.Error("GenerateVer DBUpdate ResourceVer (%d)-(%d) Error(%v)", resID, maxVer.Version+1, err) |
|
return |
|
} |
|
return |
|
} |
|
|
|
// create diff packages for the latest version with the history versions |
|
func (s *Service) createDiff(resID int) (err error) { |
|
var ( |
|
prodVers, testVers []int64 |
|
currRes *model.Resource |
|
) |
|
// pick history versions to calculate diff |
|
if prodVers, testVers, currRes, err = s.pickDiff(resID); err != nil { |
|
return |
|
} |
|
// put diff packages in our DB |
|
if err = s.putDiff(resID, mergeSlice(prodVers, testVers), currRes); err != nil { |
|
return |
|
} |
|
return |
|
} |
|
|
|
// pick history versions to calculate diff |
|
func (s *Service) pickDiff(resID int) (prodVers []int64, testVers []int64, currRes *model.Resource, err error) { |
|
var ( |
|
VersProd = []*model.Resource{} // prod |
|
VersTest = []*model.Resource{} // test |
|
res = model.Resource{} |
|
) |
|
if err = s.DB.Where("id = ?", resID).First(&res).Error; err != nil { |
|
log.Error("[createDiff]-[FindCurrentRes]-Error(%v)", err) |
|
return |
|
} |
|
currRes = &res |
|
poolID := currRes.PoolID |
|
// calculate prod diffs |
|
if err = s.DB.Joins("LEFT JOIN resource_config ON resource.id = resource_config.resource_id"). |
|
Where("resource.pool_id = ?", poolID). |
|
Where("resource.id < ?", resID). |
|
Where("resource_config.valid = ?", _valid). |
|
Order("resource.version desc").Limit(s.c.Cfg.HistoryVer). |
|
Select("resource.*"). |
|
Find(&VersProd).Error; err != nil { |
|
log.Error("[createDiff]-[FindHistoryVers]-Error(%v)", err) |
|
return |
|
} |
|
log.Info("Get Prod History Versions: %d", len(VersProd)) |
|
// calculate test diffs |
|
if err = s.DB.Joins("LEFT JOIN resource_config ON resource.id = resource_config.resource_id"). |
|
Where("resource.pool_id = ?", poolID). |
|
Where("resource.id < ?", resID). |
|
Where("resource_config.valid != ?", _valid). |
|
Where("resource_config.valid_test = ?", _valid). |
|
Order("resource.version desc").Limit(s.c.Cfg.HistoryVer). |
|
Select("resource.*"). |
|
Find(&VersTest).Error; err != nil { |
|
log.Error("[createDiff]-[FindHistoryVers]-Error(%v)", err) |
|
return |
|
} |
|
log.Info("Get Test History Versions: %d", len(VersTest)) |
|
// merge slices |
|
prodVers = pickVersion(VersProd) |
|
testVers = pickVersion(VersTest) |
|
return |
|
} |
|
|
|
// put diff package in our DB |
|
func (s *Service) putDiff(resID int, historyVers []int64, currRes *model.Resource) (err error) { |
|
for _, v := range historyVers { |
|
var diffPkg = &model.ResourceFile{ |
|
Name: fmt.Sprintf(_diffFormat, currRes.PoolID, v, currRes.Version), |
|
FromVer: v, |
|
ResourceID: resID, |
|
FileType: _diffPackge, |
|
} |
|
if err = s.DB.Create(diffPkg).Error; err != nil { |
|
log.Error("[createDiff]-[createDiffPkg]-Error(%v)", err) |
|
return |
|
} |
|
} |
|
log.Info("[createDiff]-Create (%d) Diff Pkg for ResID:(%d)", len(historyVers), resID) |
|
return |
|
} |
|
|
|
// pick resource version |
|
func pickVersion(s1 []*model.Resource) (res []int64) { |
|
if len(s1) > 0 { |
|
for _, v := range s1 { |
|
res = append(res, v.Version) |
|
} |
|
} |
|
return |
|
} |
|
|
|
// merge int64 slices |
|
func mergeSlice(s1 []int64, s2 []int64) []int64 { |
|
slice := make([]int64, len(s1)+len(s2)) |
|
copy(slice, s1) |
|
copy(slice[len(s1):], s2) |
|
return slice |
|
} |
|
|
|
// transform to Resource struct |
|
func transResource(resMame string, id int64) *model.Resource { |
|
return &model.Resource{ |
|
Name: resMame, |
|
Version: 0, // will be updated after the transaction commits |
|
PoolID: id, |
|
} |
|
} |
|
|
|
// transform to File struct |
|
func transFile(fInfo *model.FileInfo, resID int) *model.ResourceFile { |
|
return &model.ResourceFile{ |
|
Name: fInfo.Name, |
|
Type: fInfo.Type, |
|
Md5: fInfo.Md5, |
|
Size: int(fInfo.Size), |
|
URL: fInfo.URL, |
|
ResourceID: resID, |
|
FileType: _fullPackage, |
|
} |
|
} |
|
|
|
// transform to Config struct |
|
func transConfig(resID int64, limitData *model.Limit) *model.ResourceConfig { |
|
var cfg = &model.ResourceConfig{ |
|
ResourceID: resID, |
|
Valid: 0, |
|
IsDeleted: 0, |
|
IsWifi: limitData.IsWifi, |
|
} |
|
if limitData.TimeRange != nil { |
|
cfg.Etime = limitData.TimeRange.Etime |
|
cfg.Stime = limitData.TimeRange.Stime |
|
} |
|
return cfg |
|
} |
|
|
|
// createLimit |
|
func createLimit(configID int64, limitData *model.Limit) (res []*model.ResourceLimit) { |
|
// create device limit |
|
if len(limitData.Device) != 0 { |
|
generateDevice(limitData.Device, &res, configID, deviceCol, _bk) |
|
} |
|
// create plat limit |
|
if len(limitData.Plat) != 0 { |
|
generateDevice(limitData.Plat, &res, configID, platCol, _wt) |
|
} |
|
// create mobi_app limit |
|
if len(limitData.MobiApp) != 0 { |
|
generateDevice(limitData.MobiApp, &res, configID, mobiAppCol, _wt) |
|
} |
|
// scale, level, arch |
|
if len(limitData.Level) != 0 { |
|
generateDevice(limitData.Level, &res, configID, levelCol, _wt) |
|
} |
|
if len(limitData.Scale) != 0 { |
|
generateDevice(limitData.Scale, &res, configID, scaleCol, _wt) |
|
} |
|
if len(limitData.Arch) != 0 { |
|
generateDevice(limitData.Arch, &res, configID, archCol, _wt) |
|
} |
|
// create build & sysver limit |
|
if build := limitData.Build; build != nil { |
|
generateBuild(build, configID, &res, buildCol) |
|
} |
|
if sysver := limitData.Sysver; sysver != nil { |
|
generateBuild(sysver, configID, &res, sysverCol) |
|
} |
|
log.Info("createLimit creates %d limits", len(res)) |
|
return |
|
} |
|
|
|
// generate build-like limits data (json, range), insert them into the slice 'res' |
|
func generateBuild(build *model.Build, configID int64, res *[]*model.ResourceLimit, column string) { |
|
if build.GT != 0 { |
|
*res = append(*res, transBuild(buildGtCdt, configID, build.GT, column)) |
|
} |
|
if build.LT != 0 { |
|
*res = append(*res, transBuild(buildLtCdt, configID, build.LT, column)) |
|
} |
|
if build.GE != 0 { |
|
*res = append(*res, transBuild(buildGeCdt, configID, build.GE, column)) |
|
} |
|
if build.LE != 0 { |
|
*res = append(*res, transBuild(buildLeCdt, configID, build.LE, column)) |
|
} |
|
} |
|
|
|
// generate device-like limits data([]string), insert them into the slice 'res' |
|
func generateDevice(device []string, res *[]*model.ResourceLimit, configID int64, col string, cdt string) { |
|
for _, v := range device { |
|
*res = append(*res, &model.ResourceLimit{ |
|
ConfigID: configID, |
|
Column: col, |
|
Condition: cdt, |
|
Value: v, |
|
IsDeleted: 0, |
|
}) |
|
} |
|
} |
|
|
|
func transBuild(condition string, configID int64, value int, column string) *model.ResourceLimit { |
|
return &model.ResourceLimit{ |
|
ConfigID: configID, |
|
Column: column, |
|
Condition: condition, |
|
Value: fmt.Sprintf("%d", value), |
|
IsDeleted: 0, |
|
} |
|
}
|
|
|