From 71288e85afbfc7ac12d5060feb1d29aa139513d6 Mon Sep 17 00:00:00 2001 From: zhangqian <zhangqian@123.com> Date: 星期二, 16 四月 2024 20:47:10 +0800 Subject: [PATCH] 增加月度统计库存定时任务,统计期初数量,出入库数量,期末数量 --- go.mod | 4 models/location_product_amount.go | 22 + models/material.go | 18 + models/operation.go | 34 ++ models/common.go | 13 task/tasklist.go | 17 + task/month_stats.go | 189 +++++++++++++ models/month_stats.go | 348 ++++++++++++++++++++++++ go.sum | 16 + models/db.go | 2 service/month_stats.go | 5 main.go | 3 models/lock.go | 115 ++++++++ models/operation_details.go | 47 ++ 14 files changed, 824 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 596b730..3d1b8cc 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/gin-gonic/gin v1.9.0 github.com/golang-jwt/jwt/v4 v4.5.0 - github.com/google/uuid v1.3.1 + github.com/google/uuid v1.4.0 github.com/huichen/sego v0.0.0-20210824061530-c87651ea5c76 github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 github.com/nsqio/go-nsq v1.1.0 @@ -62,6 +62,7 @@ github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/gabriel-vasile/mimetype v1.4.2 // indirect github.com/gin-contrib/sse v0.1.0 // indirect + github.com/go-co-op/gocron v1.37.0 // indirect github.com/go-ini/ini v1.67.0 // indirect github.com/go-logr/logr v1.2.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect @@ -104,6 +105,7 @@ github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 // indirect github.com/richardlehane/mscfb v1.0.4 // indirect github.com/richardlehane/msoleps v1.0.3 // indirect + github.com/robfig/cron/v3 v3.0.1 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/spf13/afero v1.9.3 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect diff --git a/go.sum b/go.sum index d7740e2..4b0ea23 100644 --- a/go.sum +++ b/go.sum @@ -46,6 +46,8 @@ github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8= github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/RoaringBitmap/roaring v1.2.3 h1:yqreLINqIrX22ErkKI0vY47/ivtJr6n+kMhVOVmhWBY= github.com/RoaringBitmap/roaring v1.2.3/go.mod h1:plvDsJQpxOC5bw8LRteu/MLWHsHez/3y6cubLI4/1yE= github.com/adamzy/cedar-go v0.0.0-20170805034717-80a9c64b256d h1:ir/IFJU5xbja5UaBEQLjcvn7aAU01nqU/NUyOBEU+ew= @@ -142,11 +144,14 @@ github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.9.0 h1:OjyFBKICoexlu99ctXNR2gg+c5pKrKMuyjgARg9qeY8= github.com/gin-gonic/gin v1.9.0/go.mod h1:W1Me9+hsUSyj3CePGrd1/QrKJMSJ1Tu/0hFEH89961k= +github.com/go-co-op/gocron v1.37.0 h1:ZYDJGtQ4OMhTLKOKMIch+/CY70Brbb1dGdooLEhh7b0= +github.com/go-co-op/gocron v1.37.0/go.mod h1:3L/n6BkO7ABj+TrfSVXLRzsP26zmikL4ISkLQ0O8iNY= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= +github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= @@ -250,6 +255,8 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= +github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= @@ -273,10 +280,12 @@ github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kirinlabs/HttpRequest v1.1.1 h1:eBbFzpRd/Y7vQhRY30frHK3yAJiT1wDlB31Ryzyklc0= github.com/kirinlabs/HttpRequest v1.1.1/go.mod h1:XV38fA4rXZox83tlEV9KIQ7Cdsut319x6NGzVLuRlB8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= @@ -287,6 +296,7 @@ github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= @@ -317,6 +327,7 @@ github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/mschoch/smat v0.2.0 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM= github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= @@ -326,6 +337,7 @@ github.com/open-policy-agent/opa v0.57.1/go.mod h1:YYcVsWcdOW47owR0zElx8HPYZK60vL0MOPsEmh13us4= github.com/pelletier/go-toml/v2 v2.0.9 h1:uH2qQXheeefCCkuBBSLi7jCiSmj3VRh2+Goq2N7Xxu0= github.com/pelletier/go-toml/v2 v2.0.9/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -348,7 +360,11 @@ github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg= github.com/richardlehane/msoleps v1.0.3 h1:aznSZzrwYRl3rLKRT3gUk9am7T/mLNSnJINvN0AQoVM= github.com/richardlehane/msoleps v1.0.3/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg= +github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= +github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= diff --git a/main.go b/main.go index 2a44bc8..cbc62e9 100644 --- a/main.go +++ b/main.go @@ -19,6 +19,7 @@ "wms/proto/purchase_wms" "wms/router" "wms/service" + "wms/task" ) func main() { @@ -75,6 +76,8 @@ go service.InitLocationReportData() go service.InitHistoryReportData() + task.Init() + logx.Error(server.ListenAndServe().Error()) } diff --git a/models/common.go b/models/common.go new file mode 100644 index 0000000..7b5bc67 --- /dev/null +++ b/models/common.go @@ -0,0 +1,13 @@ +package models + +import "github.com/shopspring/decimal" + +type GroupCount struct { + Class string + Total int64 +} + +type GroupSum struct { + Class string + Sum decimal.Decimal +} diff --git a/models/db.go b/models/db.go index 2458d84..8a77447 100644 --- a/models/db.go +++ b/models/db.go @@ -118,6 +118,8 @@ LogisticCompany{}, FileTemplateAttachment{}, MoveHistory{}, + Lock{}, + MonthStats{}, ) return err } diff --git a/models/location_product_amount.go b/models/location_product_amount.go index c102c5b..447b469 100644 --- a/models/location_product_amount.go +++ b/models/location_product_amount.go @@ -289,3 +289,25 @@ return nil } + +func (slf *LocationProductAmountSearch) GroupCount(field string) ([]*GroupCount, error) { + var ( + db = slf.build() + result = make([]*GroupCount, 0) + ) + if err := db.Select("count(*) as total, " + field + " as class").Group(field).Scan(&result).Error; err != nil { + return nil, fmt.Errorf("select group err: %v", err) + } + return result, nil +} + +func (slf *LocationProductAmountSearch) GroupSum(groupField string, sumField string) ([]*GroupSum, error) { + var ( + db = slf.build() + result = make([]*GroupSum, 0) + ) + if err := db.Select("sum(" + sumField + ") as sum, " + groupField + " as class").Group(groupField).Scan(&result).Error; err != nil { + return nil, fmt.Errorf("select group err: %v", err) + } + return result, nil +} diff --git a/models/lock.go b/models/lock.go new file mode 100644 index 0000000..c23d8b0 --- /dev/null +++ b/models/lock.go @@ -0,0 +1,115 @@ +package models + +import ( + "fmt" + "gorm.io/gorm" + "time" + "wms/pkg/mysqlx" +) + +type ( + // Lock 鍒嗗竷寮忛攣 + Lock struct { + LockName string `gorm:"primaryKey"` + LockedBy string + LockedAt time.Time + } + + LockSearch struct { + Lock + Orm *gorm.DB + } +) + +func (slf *Lock) TableName() string { + return "distributed_lock" +} + +func NewLockSearch() *LockSearch { + return &LockSearch{Orm: mysqlx.GetDB()} +} + +func (slf *LockSearch) SetOrm(tx *gorm.DB) *LockSearch { + slf.Orm = tx + + return slf +} + +func (slf *LockSearch) SetLockName(lockName string) *LockSearch { + slf.LockName = lockName + return slf +} + +func (slf *LockSearch) SetLockedBy(lockedBy string) *LockSearch { + slf.LockedBy = lockedBy + return slf +} + +func (slf *LockSearch) build() *gorm.DB { + var db = slf.Orm.Model(&Lock{}) + + if slf.LockName != "" { + db = db.Where("lock_name = ?", slf.LockName) + } + + if slf.LockedBy != "" { + db = db.Where("locked_by = ?", slf.LockedBy) + } + + return db +} + +// Create 鍗曟潯鎻掑叆 +func (slf *LockSearch) Create(record *Lock) error { + var db = slf.build() + + if err := db.Create(record).Error; err != nil { + return err + } + + return nil +} + +func (slf *LockSearch) Delete() error { + var db = slf.build() + return db.Delete(&Lock{}).Error +} + +func (slf *LockSearch) First() (*Lock, error) { + var ( + record = new(Lock) + db = slf.build() + ) + + if err := db.First(record).Error; err != nil { + return record, err + } + + return record, nil +} + +func (slf *LockSearch) AcquireLock(lockName, serviceID string) error { + err := WithTransaction(func(db *gorm.DB) error { + lock, err := slf.SetLockName(lockName).SetLockedBy(serviceID).First() + if err != nil && err != gorm.ErrRecordNotFound { + return err + } + if lock.LockedBy != "" { + return fmt.Errorf("AcquireLock failed, lockName: %s, serviceID: %+v", lockName, serviceID) + } + return slf.Create(&Lock{ + LockName: lockName, + LockedBy: serviceID, + LockedAt: time.Now(), + }) + }) + return err +} + +func (slf *LockSearch) ReleaseLock(lockName, serviceID string) error { + err := slf.SetLockName(lockName).SetLockedBy(serviceID).Delete() + if err != nil { + return fmt.Errorf("AcquireLock err: %v, lockName: %s, serviceID: %+v", err, lockName, serviceID) + } + return nil +} diff --git a/models/material.go b/models/material.go index 5c03da9..47c9e36 100644 --- a/models/material.go +++ b/models/material.go @@ -109,6 +109,7 @@ Orm *gorm.DB CategoryIds []int Preload bool + Fields string } IdAndName struct { @@ -258,6 +259,11 @@ return slf } +func (slf *MaterialSearch) SetFields(fields string) *MaterialSearch { + slf.Fields = fields + return slf +} + func (slf *MaterialSearch) build() *gorm.DB { var db = slf.Orm.Table(slf.TableName()) @@ -322,6 +328,10 @@ } if len(slf.CategoryIds) > 0 { db = db.Where("category_id in ?", slf.CategoryIds) + } + + if slf.Fields != "" { + db = db.Select(slf.Fields) } if slf.Preload { @@ -611,3 +621,11 @@ return nil } + +func MaterialMap(records []*Material) (m map[string]*Material) { + m = make(map[string]*Material, len(records)) + for _, record := range records { + m[record.ID] = record + } + return m +} diff --git a/models/month_stats.go b/models/month_stats.go new file mode 100644 index 0000000..7d73f36 --- /dev/null +++ b/models/month_stats.go @@ -0,0 +1,348 @@ +package models + +import ( + "encoding/json" + "fmt" + "github.com/shopspring/decimal" + "gorm.io/gorm" + "wms/pkg/mysqlx" +) + +type ( + // MonthStats 绉诲姩鍘嗗彶 + MonthStats struct { + WmsModel + Id int `json:"id" gorm:"column:id;primary_key;AUTO_INCREMENT"` + ProductId string `json:"productId" gorm:"type:varchar(255);not null;comment:浜у搧id"` //浜у搧id + ProductName string `json:"productName" gorm:"type:varchar(255);not null;comment:浜у搧鍚嶇О"` //浜у搧鍚嶇О + Unit string `json:"unit" gorm:"type:char(10);not null;comment:鍗曚綅"` //鍗曚綅 + + BeginAmount decimal.Decimal `json:"beginAmount" gorm:"type:decimal(30,10);not null;comment:鏁伴噺"` //鏈熷垵鏁伴噺 + BeginMoreUnitsArr []UnitItems `json:"beginMoreUnitsArr" gorm:"-"` //鏈熷垵鍏朵粬鍗曚綅鏁扮粍 + BeginMoreUnits string `json:"-" gorm:"type:varchar(1000);not null;default:''"` //鏈熷垵鍏朵粬鍗曚綅json瀛楃涓� + + InputAmount decimal.Decimal `json:"inputAmount" gorm:"type:decimal(30,10);not null;comment:鏁伴噺"` //鍏ュ簱鏁伴噺 + InputMoreUnitsArr []UnitItems `json:"inputMoreUnitsArr" gorm:"-"` //鍏ュ簱鍏朵粬鍗曚綅鏁扮粍 + InputMoreUnits string `json:"-" gorm:"type:varchar(1000);not null;default:''"` //鍏ュ簱鍏朵粬鍗曚綅json瀛楃涓� + + OutputAmount decimal.Decimal `json:"outputAmount" gorm:"type:decimal(30,10);not null;comment:鏁伴噺"` //鍑哄簱鏁伴噺 + OutputMoreUnitsArr []UnitItems `json:"outputMoreUnitsArr" gorm:"-"` //鍑哄簱鍏朵粬鍗曚綅鏁扮粍 + OutputMoreUnits string `json:"-" gorm:"type:varchar(1000);not null;default:''"` //鍑哄簱鍏朵粬鍗曚綅json瀛楃涓� + + EndAmount decimal.Decimal `json:"amount" gorm:"type:decimal(30,10);not null;comment:鏁伴噺"` //鏈熸湯缁撲綑鏁伴噺 + EndMoreUnitsArr []UnitItems `json:"MoreUnitsArr" gorm:"-"` //鏈熸湯鍏朵粬鍗曚綅鏁扮粍 + EndMoreUnits string `json:"-" gorm:"type:varchar(1000);not null;default:''"` //鏈熸湯鍏朵粬鍗曚綅json瀛楃涓� + + Weight decimal.Decimal `json:"weight" gorm:"type:decimal(20,5);not null;comment:閲嶉噺"` //閲嶉噺 + Date string `json:"date" gorm:"index;type:varchar(255); not null;default ''"` //鏃ユ湡 2024-04 + } + + MonthStatsSearch struct { + MonthStats + Order string + PageNum int + PageSize int + Keyword string + Orm *gorm.DB + Preload bool + Fields string + } +) + +func (slf *MonthStats) TableName() string { + return "wms_month_stats" +} + +func (slf *MonthStats) BeforeCreate(tx *gorm.DB) error { + if len(slf.BeginMoreUnitsArr) != 0 { + str, err := json.Marshal(slf.BeginMoreUnitsArr) + if err != nil { + return err + } + slf.BeginMoreUnits = string(str) + } + if len(slf.EndMoreUnitsArr) != 0 { + str, err := json.Marshal(slf.EndMoreUnitsArr) + if err != nil { + return err + } + slf.EndMoreUnits = string(str) + } + if len(slf.InputMoreUnitsArr) != 0 { + str, err := json.Marshal(slf.InputMoreUnitsArr) + if err != nil { + return err + } + slf.InputMoreUnits = string(str) + } + if len(slf.OutputMoreUnitsArr) != 0 { + str, err := json.Marshal(slf.OutputMoreUnitsArr) + if err != nil { + return err + } + slf.OutputMoreUnits = string(str) + } + return nil +} + +func (slf *MonthStats) AfterFind(tx *gorm.DB) error { + if slf.BeginMoreUnits != "" { + var arr []UnitItems + err := json.Unmarshal([]byte(slf.BeginMoreUnits), &arr) + if err != nil { + return err + } + slf.BeginMoreUnitsArr = arr + } + if slf.EndMoreUnits != "" { + var arr []UnitItems + err := json.Unmarshal([]byte(slf.EndMoreUnits), &arr) + if err != nil { + return err + } + slf.EndMoreUnitsArr = arr + } + if slf.InputMoreUnits != "" { + var arr []UnitItems + err := json.Unmarshal([]byte(slf.InputMoreUnits), &arr) + if err != nil { + return err + } + slf.InputMoreUnitsArr = arr + } + if slf.OutputMoreUnits != "" { + var arr []UnitItems + err := json.Unmarshal([]byte(slf.OutputMoreUnits), &arr) + if err != nil { + return err + } + slf.OutputMoreUnitsArr = arr + } + return nil +} + +func NewMonthStatsSearch() *MonthStatsSearch { + return &MonthStatsSearch{Orm: mysqlx.GetDB()} +} + +func (slf *MonthStatsSearch) SetOrm(tx *gorm.DB) *MonthStatsSearch { + slf.Orm = tx + return slf +} + +func (slf *MonthStatsSearch) SetPage(page, size int) *MonthStatsSearch { + slf.PageNum, slf.PageSize = page, size + return slf +} + +func (slf *MonthStatsSearch) SetOrder(order string) *MonthStatsSearch { + slf.Order = order + return slf +} + +func (slf *MonthStatsSearch) SetID(id int) *MonthStatsSearch { + slf.Id = id + return slf +} + +func (slf *MonthStatsSearch) SetKeyword(keyword string) *MonthStatsSearch { + slf.Keyword = keyword + return slf +} + +func (slf *MonthStatsSearch) SetPreload(preload bool) *MonthStatsSearch { + slf.Preload = preload + return slf +} + +func (slf *MonthStatsSearch) SetDate(date string) *MonthStatsSearch { + slf.Date = date + return slf +} + +func (slf *MonthStatsSearch) SetFields(fields string) *MonthStatsSearch { + slf.Fields = fields + return slf +} + +func (slf *MonthStatsSearch) build() *gorm.DB { + var db = slf.Orm.Model(&MonthStats{}) + + if slf.Id != 0 { + db = db.Where("id = ?", slf.Id) + } + + if slf.Order != "" { + db = db.Order(slf.Order) + } + + if slf.Keyword != "" { + db = db.Where("product_name like ?", fmt.Sprintf("%%%v%%", slf.Keyword)) + } + + if slf.Date != "" { + db = db.Where("date = ?", slf.Date) + } + + if slf.Fields != "" { + db = db.Select(slf.Fields) + } + + return db +} + +// Create 鍗曟潯鎻掑叆 +func (slf *MonthStatsSearch) Create(record *MonthStats) error { + var db = slf.build() + + if err := db.Create(record).Error; err != nil { + return err + } + + return nil +} + +// CreateBatch 鎵归噺鎻掑叆 +func (slf *MonthStatsSearch) CreateBatch(records []*MonthStats) error { + var db = slf.build() + + if err := db.Create(&records).Error; err != nil { + return fmt.Errorf("create batch err: %v, records: %+v", err, records) + } + + return nil +} + +func (slf *MonthStatsSearch) Update(record *MonthStats) error { + var db = slf.build() + + if err := db.Omit("CreatedAt").Updates(record).Error; err != nil { + return fmt.Errorf("save err: %v, record: %+v", err, record) + } + + return nil +} + +func (slf *MonthStatsSearch) UpdateByMap(upMap map[string]interface{}) error { + var ( + db = slf.build() + ) + + if err := db.Updates(upMap).Error; err != nil { + return fmt.Errorf("update by map err: %v, upMap: %+v", err, upMap) + } + + return nil +} + +func (slf *MonthStatsSearch) UpdateByQuery(query string, args []interface{}, upMap map[string]interface{}) error { + var ( + db = slf.Orm.Table(slf.TableName()).Where(query, args...) + ) + + if err := db.Updates(upMap).Error; err != nil { + return fmt.Errorf("update by query err: %v, query: %s, args: %+v, upMap: %+v", err, query, args, upMap) + } + + return nil +} + +func (slf *MonthStatsSearch) Delete() error { + var db = slf.build() + return db.Delete(&MonthStats{}).Error +} + +func (slf *MonthStatsSearch) First() (*MonthStats, error) { + var ( + record = new(MonthStats) + db = slf.build() + ) + + if err := db.First(record).Error; err != nil { + return record, err + } + + return record, nil +} + +func (slf *MonthStatsSearch) Find() ([]*MonthStats, int64, error) { + var ( + records = make([]*MonthStats, 0) + total int64 + db = slf.build() + ) + + if err := db.Count(&total).Error; err != nil { + return records, total, fmt.Errorf("find count err: %v", err) + } + if slf.PageNum*slf.PageSize > 0 { + db = db.Offset((slf.PageNum - 1) * slf.PageSize).Limit(slf.PageSize) + } + if err := db.Find(&records).Error; err != nil { + return records, total, fmt.Errorf("find records err: %v", err) + } + + return records, total, nil +} + +func (slf *MonthStatsSearch) FindNotTotal() ([]*MonthStats, error) { + var ( + records = make([]*MonthStats, 0) + db = slf.build() + ) + + if slf.PageNum*slf.PageSize > 0 { + db = db.Offset((slf.PageNum - 1) * slf.PageSize).Limit(slf.PageSize) + } + if err := db.Find(&records).Error; err != nil { + return records, fmt.Errorf("find records err: %v", err) + } + + return records, nil +} + +// FindByQuery 鎸囧畾鏉′欢鏌ヨ. +func (slf *MonthStatsSearch) FindByQuery(query string, args []interface{}) ([]*MonthStats, int64, error) { + var ( + records = make([]*MonthStats, 0) + total int64 + db = slf.Orm.Table(slf.TableName()).Where(query, args...) + ) + + if err := db.Count(&total).Error; err != nil { + return records, total, fmt.Errorf("find by query count err: %v", err) + } + if slf.PageNum*slf.PageSize > 0 { + db = db.Offset((slf.PageNum - 1) * slf.PageSize).Limit(slf.PageSize) + } + if err := db.Find(&records).Error; err != nil { + return records, total, fmt.Errorf("find by query records err: %v, query: %s, args: %+v", err, query, args) + } + + return records, total, nil +} + +// FindByQueryNotTotal 鎸囧畾鏉′欢鏌ヨ&涓嶆煡璇㈡�绘潯鏁�. +func (slf *MonthStatsSearch) FindByQueryNotTotal(query string, args []interface{}) ([]*MonthStats, error) { + var ( + records = make([]*MonthStats, 0) + db = slf.Orm.Table(slf.TableName()).Where(query, args...) + ) + + if slf.PageNum*slf.PageSize > 0 { + db = db.Offset((slf.PageNum - 1) * slf.PageSize).Limit(slf.PageSize) + } + if err := db.Find(&records).Error; err != nil { + return records, fmt.Errorf("find by query records err: %v, query: %s, args: %+v", err, query, args) + } + + return records, nil +} + +func MonthStatsMap(records []*MonthStats) (m map[string]*MonthStats) { + m = make(map[string]*MonthStats, len(records)) + for _, record := range records { + m[record.ProductId] = record + } + return m +} diff --git a/models/operation.go b/models/operation.go index ccf54fd..b8aab80 100644 --- a/models/operation.go +++ b/models/operation.go @@ -4,6 +4,7 @@ "fmt" "github.com/shopspring/decimal" "gorm.io/gorm" + "time" "wms/constvar" "wms/pkg/mysqlx" ) @@ -70,6 +71,9 @@ Ids []int SourceNumbers []string SalesDetailsNumbers []string + Fields string + BeginTime time.Time + EndTime time.Time } ) @@ -103,6 +107,11 @@ func (slf *OperationSearch) SetKeyword(keyword string) *OperationSearch { slf.Keyword = keyword + return slf +} + +func (slf *OperationSearch) SetFields(fields string) *OperationSearch { + slf.Fields = fields return slf } @@ -163,6 +172,12 @@ func (slf *OperationSearch) SetOperationSource(operationSource constvar.OperationSource) *OperationSearch { slf.OperationSource = operationSource + return slf +} + +func (slf *OperationSearch) SetTimeBetween(beginTime, endTime time.Time) *OperationSearch { + slf.BeginTime = beginTime + slf.EndTime = endTime return slf } @@ -228,6 +243,13 @@ db = db.Where("operation_source = ?", slf.OperationSource) } + if slf.Fields != "" { + db = db.Select(slf.Fields) + } + + if !slf.BeginTime.IsZero() && !slf.EndTime.IsZero() { + db = db.Where("created_at between ? and ?", slf.BeginTime, slf.EndTime) + } return db } @@ -351,6 +373,18 @@ return records, nil } +func (slf *OperationSearch) FindIds() ([]int, error) { + var ( + records = make([]int, 0) + db = slf.build() + ) + if err := db.Find(&records).Error; err != nil { + return records, fmt.Errorf("find records err: %v", err) + } + + return records, nil +} + // FindByQuery 鎸囧畾鏉′欢鏌ヨ. func (slf *OperationSearch) FindByQuery(query string, args []interface{}) ([]*Operation, int64, error) { var ( diff --git a/models/operation_details.go b/models/operation_details.go index 13b8463..6c53666 100644 --- a/models/operation_details.go +++ b/models/operation_details.go @@ -12,8 +12,8 @@ OperationDetails struct { WmsModel Id int `json:"id" gorm:"column:id;primary_key;AUTO_INCREMENT"` - OperationID int `json:"operationId" gorm:"type:int;not null;comment:鎿嶄綔璁板綍id"` //鎿嶄綔id - ProductId string `json:"productId" gorm:"type:varchar(191);not null;comment:浜у搧id"` //浜у搧id + OperationID int `json:"operationId" gorm:"index;type:int;not null;comment:鎿嶄綔璁板綍id"` //鎿嶄綔id + ProductId string `json:"productId" gorm:"type:varchar(191);not null;comment:浜у搧id"` //浜у搧id //ProductName string `json:"productName" gorm:"type:varchar(255);not null;comment:浜у搧鍚嶇О"` //浜у搧鍚嶇О Amount decimal.Decimal `json:"amount" gorm:"type:decimal(20,2);not null;comment:鏁伴噺"` //鏁伴噺 //Unit string `json:"unit" gorm:"type:varchar(31);comment:鍗曚綅"` //鍗曚綅 @@ -29,12 +29,14 @@ OperationDetailsSearch struct { OperationDetails - Order string - PageNum int - PageSize int - Keyword string - Orm *gorm.DB - Preload bool + Order string + PageNum int + PageSize int + Keyword string + Orm *gorm.DB + Preload bool + OperationIDs []int + Fields string } ) @@ -81,6 +83,16 @@ return slf } +func (slf *OperationDetailsSearch) SetOperationIds(operationIds []int) *OperationDetailsSearch { + slf.OperationIDs = operationIds + return slf +} + +func (slf *OperationDetailsSearch) SetFields(fields string) *OperationDetailsSearch { + slf.Fields = fields + return slf +} + func (slf *OperationDetailsSearch) SetProductId(productId string) *OperationDetailsSearch { slf.ProductId = productId return slf @@ -109,6 +121,14 @@ } if slf.Preload { db = db.Preload("Product") + } + + if len(slf.OperationIDs) > 0 { + db = db.Where("operation_id in ?", slf.OperationIDs) + } + + if slf.Fields != "" { + db = db.Select(slf.Fields) } return db @@ -274,3 +294,14 @@ return records, nil } + +func (slf *OperationDetailsSearch) GroupSum(groupField string, sumField string) ([]*GroupSum, error) { + var ( + db = slf.build() + result = make([]*GroupSum, 0) + ) + if err := db.Select("sum(" + sumField + ") as sum, " + groupField + " as class").Group(groupField).Scan(&result).Error; err != nil { + return nil, fmt.Errorf("select group err: %v", err) + } + return result, nil +} diff --git a/service/month_stats.go b/service/month_stats.go new file mode 100644 index 0000000..a9a9c6c --- /dev/null +++ b/service/month_stats.go @@ -0,0 +1,5 @@ +package service + +func GetStats() { + +} diff --git a/task/month_stats.go b/task/month_stats.go new file mode 100644 index 0000000..2b33ef6 --- /dev/null +++ b/task/month_stats.go @@ -0,0 +1,189 @@ +package task + +import ( + "encoding/json" + "github.com/shopspring/decimal" + "time" + "wms/constvar" + "wms/models" + "wms/pkg/logx" +) + +func MonthStats() { + //鍔犻攣锛屽彧闇�瑕佷竴涓繘绋嬭繍琛屾浠诲姟 + var ( + lockName = "monthStats" + serviceID = "wms" + ) + err := models.NewLockSearch().AcquireLock(lockName, serviceID) + if err != nil { + logx.Errorf("MonthStats AcquireLock err:%v", err) + return + } + defer func() { + err := models.NewLockSearch().ReleaseLock(lockName, serviceID) + if err != nil { + logx.Errorf("MonthStats ReleaseLock err:%v", err) + } + }() + + date := time.Now().Format("2006-01") + lastDate := time.Now().AddDate(0, -1, 0).Format("2006-01") + + oldRecords, err := models.NewMonthStatsSearch().SetDate(lastDate).SetFields("id, product_id").FindNotTotal() + if err != nil { + logx.Errorf("MonthStats get last date record err:%v", err) + return + } + oldRecordsMap := models.MonthStatsMap(oldRecords) + + //鏈湀鏈熷垵鏁伴噺/涓婃湀缁撲綑鏁伴噺 + groupSumList, err := models.NewLocationProductAmountSearch().GroupSum("product_id", "amount") + + productIds := make([]string, 0, len(groupSumList)) + + for _, groupSum := range groupSumList { + productIds = append(productIds, groupSum.Class) + } + products, err := models.NewMaterialSearch().SetFields("id, name, unit, weight, more_unit, more_unit_value").SetIDs(productIds).FindNotTotal() + if err != nil { + logx.Errorf("MonthStats get products err:%v", err) + return + } + productMap := models.MaterialMap(products) + + beginTime, endTime := GetLastMonthPeriod() + inputMap, err := GetStatsByOperationType(beginTime, endTime, constvar.BaseOperationTypeIncoming) + if err != nil { + logx.Errorf("MonthStats GetStatsByOperationType input err:%v", err) + return + } + + outputMap, err := GetStatsByOperationType(beginTime, endTime, constvar.BaseOperationTypeOutgoing) + if err != nil { + logx.Errorf("MonthStats GetStatsByOperationType output err:%v", err) + return + } + var record models.MonthStats + for _, groupSum := range groupSumList { + productId := groupSum.Class + if productMap[productId] == nil { + continue + } + product := productMap[productId] + amount := groupSum.Sum + record = models.MonthStats{ + ProductId: productId, + ProductName: product.Name, + BeginAmount: amount, + Unit: product.Unit, + Weight: product.Weight.Mul(amount), + Date: date, + } + + var ( + moreUnits string + inputMoreUnits string + outputMoreUnits string + ) + if product.MoreUnit { + moreValueArr := make([]models.UnitItems, 0, len(product.MoreUnitList)) + inputMoreValueArr := make([]models.UnitItems, 0, len(product.MoreUnitList)) + outputMoreValueArr := make([]models.UnitItems, 0, len(product.MoreUnitList)) + for _, unitItem := range product.MoreUnitList { + moreValueArr = append(moreValueArr, models.UnitItems{ + Amount: amount.Mul(unitItem.Amount), + Unit: unitItem.Unit, + Floating: unitItem.Floating, + }) + bys, _ := json.Marshal(moreValueArr) + moreUnits = string(bys) + + if !inputMap[productId].IsZero() { + inputMoreValueArr = append(inputMoreValueArr, models.UnitItems{ + Amount: inputMap[productId].Mul(unitItem.Amount), + Unit: unitItem.Unit, + Floating: unitItem.Floating, + }) + bys, _ = json.Marshal(inputMoreValueArr) + inputMoreUnits = string(bys) + + } + + if !outputMap[productId].IsZero() { + outputMoreValueArr = append(outputMoreValueArr, models.UnitItems{ + Amount: outputMap[productId].Mul(unitItem.Amount), + Unit: unitItem.Unit, + Floating: unitItem.Floating, + }) + bys, _ = json.Marshal(outputMoreValueArr) + outputMoreUnits = string(bys) + + } + } + } + + record.BeginMoreUnits = moreUnits + err = models.NewMonthStatsSearch().Create(&record) + if err != nil { + logx.Errorf("NewMonthStatsSearch Create err:%v, record: %+v", err, record) + } + + if oldRecordsMap[productId] != nil && (!inputMap[productId].IsZero() || !outputMap[productId].IsZero()) { + record.InputAmount = inputMap[productId] + record.InputMoreUnits = inputMoreUnits + record.OutputAmount = outputMap[productId] + record.OutputMoreUnits = outputMoreUnits + m := map[string]interface{}{ + "input_amount": inputMap[productId], + "input_more_units": inputMoreUnits, + "output_amount": outputMap[productId], + "output_more_units": outputMoreUnits, + "end_more_units": moreUnits, + "end_amount": amount, + } + err = models.NewMonthStatsSearch().SetID(oldRecordsMap[productId].Id).UpdateByMap(m) + if err != nil { + logx.Errorf("NewMonthStatsSearch UpdateByMap err:%v, id:%v, m:%+v", err, oldRecordsMap[productId].ID, m) + } + } + } + return +} + +func GetStatsByOperationType(beginTime, endTime time.Time, operationType constvar.BaseOperationType) (m map[string]decimal.Decimal, err error) { + operationIds, err := models.NewOperationSearch().SetBaseOperationType(operationType).SetFields("id").SetTimeBetween(beginTime, endTime).FindIds() + if err != nil { + return + } + groupSumList, err := models.NewOperationDetailsSearch().SetOperationIds(operationIds).SetFields("product_id, amount").GroupSum("product_id", "amount") + if err != nil { + return + } + m = make(map[string]decimal.Decimal, len(groupSumList)) + for _, v := range groupSumList { + m[v.Class] = v.Sum + } + return +} + +// GetLastMonthPeriod 杩斿洖涓婁釜鏈堢殑鏈堝垵鏃堕棿鍜屾湀鏈椂闂� +func GetLastMonthPeriod() (time.Time, time.Time) { + // 鑾峰彇褰撳墠鏃堕棿 + now := time.Now() + + // 璁$畻涓婁釜鏈堢殑骞翠唤鍜屾湀浠� + lastMonth := now.AddDate(0, -1, 0) + lastYear, lastMonthNum, _ := lastMonth.Date() + + // 鑾峰彇涓婁釜鏈堢殑绗竴澶╃殑鏃ユ湡锛堝嵆涓婁釜鏈堟湀鍒濓級 + firstDayOfLastMonth := time.Date(lastYear, lastMonthNum, 1, 0, 0, 0, 0, now.Location()) + + // 鑾峰彇鏈湀绗竴澶╃殑鏃ユ湡锛堝嵆鏈湀鏈堝垵锛� + firstDayOfThisMonth := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, now.Location()) + + // 涓婁釜鏈堟湀鏈椂闂村嵆涓烘湰鏈堟湀鍒濆噺鍘讳竴绉� + lastDayOfLastMonth := firstDayOfThisMonth.Add(-time.Second) + + return firstDayOfLastMonth, lastDayOfLastMonth +} diff --git a/task/tasklist.go b/task/tasklist.go new file mode 100644 index 0000000..d2063b0 --- /dev/null +++ b/task/tasklist.go @@ -0,0 +1,17 @@ +package task + +import ( + "github.com/go-co-op/gocron" + "time" +) + +var s *gocron.Scheduler + +func init() { + s = gocron.NewScheduler(time.UTC) +} +func Init() { + s.Every(1).Month().Do(MonthStats) //姣忔湀鍒濇墽琛屼竴娆� + + s.StartAsync() +} -- Gitblit v1.8.0