Dockerfile

This commit is contained in:
2026-03-18 10:18:03 +08:00
parent 5c5dbc7420
commit b65f3439f3
189 changed files with 19027 additions and 0 deletions

View File

@@ -0,0 +1,73 @@
package dao
import (
"assets/consts/public"
dto "assets/model/dto/stock"
entity "assets/model/entity/stock"
"context"
"gitea.com/red-future/common/db/mongo"
"gitea.com/red-future/common/utils"
"github.com/gogf/gf/v2/frame/g"
"go.mongodb.org/mongo-driver/v2/bson"
)
var InventoryCountAdjustHistory = &inventoryCountAdjustHistory{}
type inventoryCountAdjustHistory struct{}
// Insert 插入盘点调整历史记录
func (d *inventoryCountAdjustHistory) Insert(ctx context.Context, req *dto.CreateInventoryCountAdjustHistoryReq) (ids []interface{}, err error) {
var result *entity.InventoryCountAdjustHistory
if err = utils.Struct(req, &result); err != nil {
return
}
ids, err = mongo.DB().Insert(ctx, []interface{}{&result}, public.InventoryCountAdjustHistoryCollection)
return
}
// GetOne 根据ID查询单条盘点调整历史记录
func (d *inventoryCountAdjustHistory) GetOne(ctx context.Context, req *dto.GetInventoryCountAdjustHistoryReq) (res *entity.InventoryCountAdjustHistory, err error) {
filter := bson.M{"_id": req.Id}
err = mongo.DB().FindOne(ctx, filter, &res, public.InventoryCountAdjustHistoryCollection)
return
}
// DeleteFake 软删除盘点调整历史记录
func (d *inventoryCountAdjustHistory) DeleteFake(ctx context.Context, req *dto.DeleteInventoryCountAdjustHistoryReq) (err error) {
filter := bson.M{"_id": req.Id}
_, err = mongo.DB().DeleteSoft(ctx, filter, public.InventoryCountAdjustHistoryCollection)
return
}
// List 分页查询盘点调整历史列表
func (d *inventoryCountAdjustHistory) List(ctx context.Context, req *dto.ListInventoryCountAdjustHistoryReq) (res []entity.InventoryCountAdjustHistory, total int64, err error) {
filter, err := d.buildListFilter(ctx, req)
if err != nil {
return
}
total, err = mongo.DB().Find(ctx, filter, &res, public.InventoryCountAdjustHistoryCollection, req.Page, req.OrderBy)
return
}
// buildListFilter 构建列表查询过滤条件
func (d *inventoryCountAdjustHistory) buildListFilter(ctx context.Context, req *dto.ListInventoryCountAdjustHistoryReq) (filter bson.M, err error) {
_ = ctx
filter = bson.M{}
if !g.IsEmpty(req.CountID) {
filter["countId"] = req.CountID
}
if !g.IsEmpty(req.DetailID) {
filter["detailId"] = req.DetailID
}
if !g.IsEmpty(req.AssetSkuID) {
filter["assetSkuId"] = req.AssetSkuID
}
if !g.IsEmpty(req.WarehouseID) {
filter["warehouseId"] = req.WarehouseID
}
if !g.IsEmpty(req.BatchNo) {
filter["batchNo"] = req.BatchNo
}
return
}

View File

@@ -0,0 +1,202 @@
// 盘点任务DAO层
// 职责盘点任务CRUD、检查未完成任务、更新状态/统计
// 紧密耦合service.InventoryCount、dao.InventoryCountDetail(级联删除)
// 注意HasUncompletedTask使用NoCache()跳过缓存,同一时间只允许一个盘点任务
package dao
import (
"assets/consts/public"
"assets/consts/stock"
dto "assets/model/dto/stock"
entity "assets/model/entity/stock"
"context"
"fmt"
"gitea.com/red-future/common/db/mongo"
"gitea.com/red-future/common/utils"
"github.com/gogf/gf/v2/frame/g"
"go.mongodb.org/mongo-driver/v2/bson"
)
var InventoryCount = new(inventoryCount)
type inventoryCount struct{}
// convertToObjectIDs 将字符串ID列表批量转换为ObjectID列表跳过空值
func convertToObjectIDs(hexIDs []string, label string) ([]*bson.ObjectID, error) {
if len(hexIDs) == 0 {
return nil, nil
}
result := make([]*bson.ObjectID, 0, len(hexIDs))
for _, hex := range hexIDs {
if g.IsEmpty(hex) {
continue
}
id, err := bson.ObjectIDFromHex(hex)
if err != nil {
return nil, fmt.Errorf("%sID格式错误(值:%s): %v", label, hex, err)
}
result = append(result, &id)
}
return result, nil
}
func (d *inventoryCount) Insert(ctx context.Context, req *dto.CreateInventoryCountReq, countNo string) (ids []interface{}, err error) {
result := &entity.InventoryCount{
CountNo: countNo,
Title: req.Title,
Description: req.Description,
CountType: req.CountType,
Scope: req.Scope,
AssigneeID: req.AssigneeID,
AssigneeName: req.AssigneeName,
Participants: req.Participants,
Remark: req.Remark,
Status: stock.InventoryCountStatusInProgress,
}
// 批量转换字符串ID为ObjectID
if result.WarehouseIDs, err = convertToObjectIDs(req.WarehouseIDs, "仓库"); err != nil {
return
}
if result.ZoneIDs, err = convertToObjectIDs(req.ZoneIDs, "库区"); err != nil {
return
}
if result.LocationIDs, err = convertToObjectIDs(req.LocationIDs, "库位"); err != nil {
return
}
if result.AssetSkuIDs, err = convertToObjectIDs(req.AssetSkuIDs, "资产SKU"); err != nil {
return
}
ids, err = mongo.DB().Insert(ctx, []interface{}{result}, public.InventoryCountCollection)
return
}
func (d *inventoryCount) GetOne(ctx context.Context, req *dto.GetInventoryCountReq) (res *entity.InventoryCount, err error) {
filter := bson.M{"_id": req.Id}
err = mongo.DB().FindOne(ctx, filter, &res, public.InventoryCountCollection)
return
}
func (d *inventoryCount) Update(ctx context.Context, req *dto.UpdateInventoryCountReq) (err error) {
var updateData entity.InventoryCount
if err = utils.Struct(req, &updateData); err != nil {
return
}
filter := bson.M{"_id": req.Id}
update := bson.M{"$set": updateData}
_, err = mongo.DB().Update(ctx, filter, update, public.InventoryCountCollection)
return
}
func (d *inventoryCount) Delete(ctx context.Context, req *dto.DeleteInventoryCountReq) (err error) {
filter := bson.M{"_id": req.Id}
_, err = mongo.DB().Delete(ctx, filter, public.InventoryCountCollection)
return
}
func (d *inventoryCount) List(ctx context.Context, req *dto.ListInventoryCountReq) (res []entity.InventoryCount, total int64, err error) {
filter, err := d.buildListFilter(ctx, req)
if err != nil {
return
}
total, err = mongo.DB().Find(ctx, filter, &res, public.InventoryCountCollection, req.Page, req.OrderBy)
return
}
func (d *inventoryCount) buildListFilter(ctx context.Context, req *dto.ListInventoryCountReq) (filter bson.M, err error) {
_ = ctx
filter = bson.M{}
// 兼容单值和数组参数(优先使用数组)
if len(req.WarehouseIDs) > 0 {
// 将字符串数组转为 ObjectID 数组
var oids []bson.ObjectID
for _, id := range req.WarehouseIDs {
if oid, e := bson.ObjectIDFromHex(id); e == nil {
oids = append(oids, oid)
}
}
if len(oids) > 0 {
filter["warehouseIds"] = bson.M{"$in": oids}
}
} else if !g.IsEmpty(req.WarehouseID) {
if wid, e := bson.ObjectIDFromHex(req.WarehouseID); e == nil {
filter["warehouseIds"] = wid
}
}
if len(req.ZoneIDs) > 0 {
// 将字符串数组转为 ObjectID 数组
var oids []bson.ObjectID
for _, id := range req.ZoneIDs {
if oid, e := bson.ObjectIDFromHex(id); e == nil {
oids = append(oids, oid)
}
}
if len(oids) > 0 {
filter["zoneIds"] = bson.M{"$in": oids}
}
} else if !g.IsEmpty(req.ZoneID) {
if zid, e := bson.ObjectIDFromHex(req.ZoneID); e == nil {
filter["zoneIds"] = zid
}
}
if !g.IsEmpty(req.CountType) {
filter["countType"] = req.CountType
}
if !g.IsEmpty(req.Status) {
filter["status"] = req.Status
}
if !g.IsEmpty(req.AssigneeID) {
filter["assigneeId"] = req.AssigneeID
}
if !g.IsEmpty(req.Keyword) {
filter["$or"] = bson.A{
bson.M{"countNo": bson.M{"$regex": req.Keyword, "$options": "i"}},
bson.M{"title": bson.M{"$regex": req.Keyword, "$options": "i"}},
}
}
return
}
// HasUncompletedTask 检查是否存在未完成的盘点任务
func (d *inventoryCount) HasUncompletedTask(ctx context.Context) (has bool, err error) {
filter := bson.M{
"status": stock.InventoryCountStatusInProgress,
}
var result entity.InventoryCount
err = mongo.DB().NoCache().FindOne(ctx, filter, &result, public.InventoryCountCollection)
if err != nil {
return false, err
}
return result.Id != nil, nil
}
// UpdateStatus 更新盘点状态
func (d *inventoryCount) UpdateStatus(ctx context.Context, id *bson.ObjectID, status stock.InventoryCountStatus) (err error) {
filter := bson.M{"_id": id}
update := bson.M{
"$set": bson.M{
"status": status,
},
}
_, err = mongo.DB().Update(ctx, filter, update, public.InventoryCountCollection)
return
}
// UpdateStats 更新盘点统计信息
func (d *inventoryCount) UpdateStats(ctx context.Context, id *bson.ObjectID, totalItems, completedItems, discrepancyItems int) (err error) {
filter := bson.M{"_id": id}
var progress float64
if totalItems > 0 {
progress = float64(completedItems) / float64(totalItems) * 100
}
update := bson.M{
"$set": bson.M{
"totalItems": totalItems,
"completedItems": completedItems,
"discrepancyItems": discrepancyItems,
"progress": progress,
},
}
_, err = mongo.DB().Update(ctx, filter, update, public.InventoryCountCollection)
return
}

View File

@@ -0,0 +1,185 @@
// 盘点明细DAO层
// 职责明细CRUD、批量插入(性能优化)、按盘点ID查询、更新调整状态
// 紧密耦合service.InventoryCountDetail、service.InventoryCount(统计更新)
// 注意InsertBatch批量插入减少网络往返ListByCountId使用mongo.DB(false)跳过缓存
package dao
import (
"assets/consts/public"
dto "assets/model/dto/stock"
entity "assets/model/entity/stock"
"context"
"fmt"
"gitea.com/red-future/common/db/mongo"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
"go.mongodb.org/mongo-driver/v2/bson"
)
var InventoryCountDetail = new(inventoryCountDetail)
type inventoryCountDetail struct{}
func (d *inventoryCountDetail) Insert(ctx context.Context, req *dto.CreateInventoryCountDetailReq) (ids []interface{}, err error) {
result := &entity.InventoryCountDetail{
BookQuantity: req.BookQuantity,
BookBatchInfo: req.BookBatchInfo,
ActualQuantity: req.ActualQuantity,
ActualBatchInfo: req.ActualBatchInfo,
Remark: req.Remark,
}
// required string → *bson.ObjectID
for _, item := range []struct {
val string
dest **bson.ObjectID
name string
}{
{req.CountID, &result.CountID, "盘点单ID"},
{req.AssetID, &result.AssetID, "资产ID"},
{req.AssetSkuID, &result.AssetSkuID, "资产SKU ID"},
{req.WarehouseID, &result.WarehouseID, "仓库ID"},
} {
id, e := bson.ObjectIDFromHex(item.val)
if e != nil {
return nil, fmt.Errorf("%s格式错误: %v", item.name, e)
}
*item.dest = &id
}
// optional string → *bson.ObjectID
if !g.IsEmpty(req.ZoneID) {
id, e := bson.ObjectIDFromHex(req.ZoneID)
if e != nil {
return nil, fmt.Errorf("库区ID格式错误: %v", e)
}
result.ZoneID = &id
}
if !g.IsEmpty(req.LocationID) {
id, e := bson.ObjectIDFromHex(req.LocationID)
if e != nil {
return nil, fmt.Errorf("库位ID格式错误: %v", e)
}
result.LocationID = &id
}
ids, err = mongo.DB().Insert(ctx, []interface{}{result}, public.InventoryCountDetailCollection)
return
}
// InsertBatch 批量插入盘点明细性能优化100条/批,减少网络往返)
func (d *inventoryCountDetail) InsertBatch(ctx context.Context, details []*entity.InventoryCountDetail) (ids []interface{}, err error) {
if len(details) == 0 {
return
}
docs := make([]interface{}, len(details))
for i, detail := range details {
docs[i] = detail
}
ids, err = mongo.DB().Insert(ctx, docs, public.InventoryCountDetailCollection)
return
}
func (d *inventoryCountDetail) GetOne(ctx context.Context, req *dto.GetInventoryCountDetailReq) (res *entity.InventoryCountDetail, err error) {
filter := bson.M{"_id": req.Id}
err = mongo.DB().FindOne(ctx, filter, &res, public.InventoryCountDetailCollection)
return
}
func (d *inventoryCountDetail) Update(ctx context.Context, req *dto.UpdateInventoryCountDetailReq) (err error) {
buildFilter, err := mongo.BuildUpdateData(ctx, req)
if err != nil {
return
}
filter := bson.M{"_id": req.Id}
update := bson.M{"$set": buildFilter}
_, err = mongo.DB().Update(ctx, filter, update, public.InventoryCountDetailCollection)
return
}
func (d *inventoryCountDetail) DeleteFake(ctx context.Context, req *dto.DeleteInventoryCountDetailReq) (err error) {
filter := bson.M{"_id": req.Id}
_, err = mongo.DB().DeleteSoft(ctx, filter, public.InventoryCountDetailCollection)
return
}
// DeleteByCountId 按盘点任务ID真删除所有明细
func (d *inventoryCountDetail) DeleteByCountId(ctx context.Context, countId *bson.ObjectID) (err error) {
filter := bson.M{"countId": countId}
_, err = mongo.DB().Delete(ctx, filter, public.InventoryCountDetailCollection)
return
}
func (d *inventoryCountDetail) List(ctx context.Context, req *dto.ListInventoryCountDetailReq) (res []entity.InventoryCountDetail, total int64, err error) {
filter, err := d.buildListFilter(ctx, req)
if err != nil {
return
}
total, err = mongo.DB().Find(ctx, filter, &res, public.InventoryCountDetailCollection, req.Page, req.OrderBy)
return
}
func (d *inventoryCountDetail) buildListFilter(ctx context.Context, req *dto.ListInventoryCountDetailReq) (filter bson.M, err error) {
_ = ctx
filter = bson.M{}
if !g.IsEmpty(req.CountID) {
filter["countId"] = req.CountID
}
if !g.IsEmpty(req.AssetID) {
filter["assetId"] = req.AssetID
}
if !g.IsEmpty(req.AssetSkuID) {
filter["assetSkuId"] = req.AssetSkuID
}
if !g.IsEmpty(req.WarehouseID) {
filter["warehouseId"] = req.WarehouseID
}
if !g.IsEmpty(req.ZoneID) {
filter["zoneId"] = req.ZoneID
}
if !g.IsEmpty(req.LocationID) {
filter["locationId"] = req.LocationID
}
if !g.IsEmpty(req.DiscrepancyType) {
filter["discrepancyType"] = req.DiscrepancyType
}
if !g.IsEmpty(req.Status) {
filter["status"] = req.Status
}
if !g.IsEmpty(req.IsAdjusted) {
filter["isAdjusted"] = req.IsAdjusted
}
return
}
// ListByCountId 按盘点ID查询所有明细
func (d *inventoryCountDetail) ListByCountId(ctx context.Context, countId *bson.ObjectID) (res []entity.InventoryCountDetail, err error) {
filter := bson.M{"countId": countId}
_, err = mongo.DB(false).Find(ctx, filter, &res, public.InventoryCountDetailCollection, nil, nil)
return
}
// UpdateAdjusted 更新调整状态
func (d *inventoryCountDetail) UpdateAdjusted(ctx context.Context, id *bson.ObjectID, adjustedBy string) (err error) {
filter := bson.M{"_id": id}
update := bson.M{
"$set": bson.M{
"isAdjusted": true,
"adjustedBy": adjustedBy,
"adjustedAt": gtime.Now(),
},
}
_, err = mongo.DB().Update(ctx, filter, update, public.InventoryCountDetailCollection)
return
}
// UpdateDiscrepancyReason 更新差异原因
func (d *inventoryCountDetail) UpdateDiscrepancyReason(ctx context.Context, id *bson.ObjectID, reason string) (err error) {
filter := bson.M{"_id": id}
update := bson.M{
"$set": bson.M{
"discrepancyReason": reason,
"updatedAt": gtime.Now(),
},
}
_, err = mongo.DB().Update(ctx, filter, update, public.InventoryCountDetailCollection)
return
}

View File

@@ -0,0 +1,71 @@
// 库存预警DAO层
// 职责:预警查询(无Insert/Update/Delete由系统自动生成)
// 紧密耦合service.InventoryWarning
// 注意:预警记录由定时任务或库存变动触发,非用户手动创建
package dao
import (
"assets/consts/public"
dto "assets/model/dto/stock"
entity "assets/model/entity/stock"
"context"
"gitea.com/red-future/common/db/mongo"
"github.com/gogf/gf/v2/frame/g"
"go.mongodb.org/mongo-driver/v2/bson"
)
var InventoryWarning = new(inventoryWarning)
type inventoryWarning struct{}
func (d *inventoryWarning) GetOne(ctx context.Context, req *dto.GetInventoryWarningReq) (res *entity.InventoryWarning, err error) {
filter := bson.M{"_id": req.Id}
err = mongo.DB().FindOne(ctx, filter, &res, public.InventoryWarningCollection)
return
}
func (d *inventoryWarning) List(ctx context.Context, req *dto.ListInventoryWarningReq) (res []entity.InventoryWarning, total int64, err error) {
filter, err := d.buildListFilter(ctx, req)
if err != nil {
return
}
total, err = mongo.DB().Find(ctx, filter, &res, public.InventoryWarningCollection, req.Page, req.OrderBy)
return
}
func (d *inventoryWarning) buildListFilter(ctx context.Context, req *dto.ListInventoryWarningReq) (filter bson.M, err error) {
_ = ctx
filter = bson.M{}
if !g.IsEmpty(req.WarningType) {
filter["warningType"] = req.WarningType
}
// 兼容单值和数组参数(优先使用数组)
if len(req.BatchIDs) > 0 {
filter["batchId"] = bson.M{"$in": req.BatchIDs}
} else if !g.IsEmpty(req.BatchID) {
filter["batchId"] = req.BatchID
}
if len(req.AssetIDs) > 0 {
filter["assetId"] = bson.M{"$in": req.AssetIDs}
} else if !g.IsEmpty(req.AssetID) {
filter["assetId"] = req.AssetID
}
if len(req.AssetSkuIDs) > 0 {
filter["assetSkuId"] = bson.M{"$in": req.AssetSkuIDs}
} else if !g.IsEmpty(req.AssetSkuID) {
filter["assetSkuId"] = req.AssetSkuID
}
if len(req.SupplierIDs) > 0 {
filter["supplierId"] = bson.M{"$in": req.SupplierIDs}
} else if !g.IsEmpty(req.SupplierID) {
filter["supplierId"] = req.SupplierID
}
if !g.IsEmpty(req.Status) {
filter["status"] = req.Status
}
if !g.IsEmpty(req.Keyword) {
filter["batchNo"] = bson.M{"$regex": req.Keyword, "$options": "i"}
}
return
}

View File

@@ -0,0 +1,80 @@
// 库存预警历史DAO层
// 职责:预警历史查询、删除(无Insert/Update由预警状态变更时自动归档)
// 紧密耦合service.InventoryWarningHistory
// 注意:历史记录由系统自动归档,非用户手动创建
package dao
import (
"assets/consts/public"
dto "assets/model/dto/stock"
entity "assets/model/entity/stock"
"context"
"gitea.com/red-future/common/db/mongo"
"github.com/gogf/gf/v2/frame/g"
"go.mongodb.org/mongo-driver/v2/bson"
)
var InventoryWarningHistory = new(inventoryWarningHistory)
type inventoryWarningHistory struct{}
func (d *inventoryWarningHistory) GetOne(ctx context.Context, req *dto.GetInventoryWarningHistoryReq) (res *entity.InventoryWarningHistory, err error) {
filter := bson.M{"_id": req.Id}
err = mongo.DB().FindOne(ctx, filter, &res, public.InventoryWarningHistoryCollection)
return
}
func (d *inventoryWarningHistory) List(ctx context.Context, req *dto.ListInventoryWarningHistoryReq) (res []entity.InventoryWarningHistory, total int64, err error) {
filter, err := d.buildListFilter(ctx, req)
if err != nil {
return
}
total, err = mongo.DB().Find(ctx, filter, &res, public.InventoryWarningHistoryCollection, req.Page, req.OrderBy)
return
}
func (d *inventoryWarningHistory) DeleteFake(ctx context.Context, req *dto.DeleteInventoryWarningHistoryReq) (err error) {
filter := bson.M{"_id": req.Id}
_, err = mongo.DB().DeleteSoft(ctx, filter, public.InventoryWarningHistoryCollection)
return
}
func (d *inventoryWarningHistory) buildListFilter(ctx context.Context, req *dto.ListInventoryWarningHistoryReq) (filter bson.M, err error) {
_ = ctx
filter = bson.M{}
if !g.IsEmpty(req.WarningType) {
filter["warningType"] = req.WarningType
}
// 兼容单值和数组参数(优先使用数组)
if len(req.BatchIDs) > 0 {
filter["batchId"] = bson.M{"$in": req.BatchIDs}
} else if !g.IsEmpty(req.BatchID) {
filter["batchId"] = req.BatchID
}
if len(req.AssetIDs) > 0 {
filter["assetId"] = bson.M{"$in": req.AssetIDs}
} else if !g.IsEmpty(req.AssetID) {
filter["assetId"] = req.AssetID
}
if len(req.AssetSkuIDs) > 0 {
filter["assetSkuId"] = bson.M{"$in": req.AssetSkuIDs}
} else if !g.IsEmpty(req.AssetSkuID) {
filter["assetSkuId"] = req.AssetSkuID
}
if len(req.SupplierIDs) > 0 {
filter["supplierId"] = bson.M{"$in": req.SupplierIDs}
} else if !g.IsEmpty(req.SupplierID) {
filter["supplierId"] = req.SupplierID
}
if !g.IsEmpty(req.Status) {
filter["status"] = req.Status
}
if !g.IsEmpty(req.ProcessMethod) {
filter["processMethod"] = req.ProcessMethod
}
if !g.IsEmpty(req.Keyword) {
filter["batchNo"] = bson.M{"$regex": req.Keyword, "$options": "i"}
}
return
}

191
dao/stock/location_dao.go Normal file
View File

@@ -0,0 +1,191 @@
// 库位DAO层
// 职责库位CRUD、状态更新、批量状态更新、删除前检查(3个库存集合)、更新容量
// 紧密耦合service.Location(删除检查)、service.Capacity(容量计算入口)
// 注意DeleteFake前需检查StockDetails/StockBatch/PrivateStock三个集合
package dao
import (
"assets/consts/public"
"assets/consts/stock"
dto "assets/model/dto/stock"
entity "assets/model/entity/stock"
"context"
"gitea.com/red-future/common/db/mongo"
"gitea.com/red-future/common/utils"
"github.com/gogf/gf/v2/frame/g"
"go.mongodb.org/mongo-driver/v2/bson"
)
var Location = new(location)
type location struct{}
func (d *location) Insert(ctx context.Context, req *dto.CreateLocationReq) (ids []interface{}, err error) {
var result *entity.Location
if err = utils.Struct(req, &result); err != nil {
return
}
// 如果未传入状态,设置默认值为空闲
if result.Status == "" {
result.Status = stock.LocationStatusIdle
}
// 初始容量默认为0
//if result.CurrentCapacity == 0 {
// result.CurrentCapacity = 0
//}
ids, err = mongo.DB().Insert(ctx, []interface{}{&result}, public.LocationCollection)
return
}
func (d *location) GetOne(ctx context.Context, req *dto.GetLocationReq) (res *entity.Location, err error) {
filter := bson.M{"_id": req.Id}
err = mongo.DB().FindOne(ctx, filter, &res, public.LocationCollection)
return
}
func (d *location) Update(ctx context.Context, req *dto.UpdateLocationReq) (err error) {
buildFilter, err := mongo.BuildUpdateData(ctx, req)
if err != nil {
return
}
filter := bson.M{"_id": req.Id}
update := bson.M{"$set": buildFilter}
_, err = mongo.DB().Update(ctx, filter, update, public.LocationCollection)
return
}
func (d *location) DeleteFake(ctx context.Context, req *dto.DeleteLocationReq) (err error) {
filter := bson.M{"_id": req.Id}
_, err = mongo.DB().DeleteSoft(ctx, filter, public.LocationCollection)
return
}
// UpdateStatus 更新库位状态
func (d *location) UpdateStatus(ctx context.Context, req *dto.UpdateLocationStatusReq) (err error) {
filter := bson.M{"_id": req.Id}
update := bson.M{"$set": bson.M{"status": req.Status}}
_, err = mongo.DB().Update(ctx, filter, update, public.LocationCollection)
return
}
func (d *location) List(ctx context.Context, req *dto.ListLocationReq) (res []entity.Location, total int64, err error) {
filter, err := d.buildListFilter(ctx, req)
if err != nil {
return
}
total, err = mongo.DB().Find(ctx, filter, &res, public.LocationCollection, req.Page, req.OrderBy)
return
}
func (d *location) buildListFilter(ctx context.Context, req *dto.ListLocationReq) (filter bson.M, err error) {
_ = ctx
filter = bson.M{}
// 兼容单值和数组参数(优先使用数组)
if len(req.WarehouseIds) > 0 {
filter["warehouseId"] = bson.M{"$in": req.WarehouseIds}
} else if !g.IsEmpty(req.WarehouseId) {
filter["warehouseId"] = req.WarehouseId
}
if len(req.ZoneIds) > 0 {
filter["zoneId"] = bson.M{"$in": req.ZoneIds}
} else if !g.IsEmpty(req.ZoneId) {
filter["zoneId"] = req.ZoneId
}
if !g.IsEmpty(req.LocationType) {
filter["locationType"] = req.LocationType
}
if !g.IsEmpty(req.Status) {
filter["status"] = req.Status
}
if !g.IsEmpty(req.Keyword) {
filter["$or"] = bson.A{
bson.M{"locationCode": bson.M{"$regex": req.Keyword, "$options": "i"}},
bson.M{"locationName": bson.M{"$regex": req.Keyword, "$options": "i"}},
}
}
return
}
// CountByZoneId 统计库区下的库位数量(用于删除前检查)
func (d *location) CountByZoneId(ctx context.Context, zoneId string) (count int64, err error) {
filter := bson.M{"zoneId": zoneId}
count, err = mongo.DB().Count(ctx, filter, public.LocationCollection)
return
}
// CountByWarehouseId 统计仓库下的库位数量(用于删除前检查)
func (d *location) CountByWarehouseId(ctx context.Context, warehouseId string) (count int64, err error) {
filter := bson.M{"warehouseId": warehouseId}
count, err = mongo.DB().Count(ctx, filter, public.LocationCollection)
return
}
// BatchUpdateStatusByZoneId 批量更新库区下所有库位状态(用于状态联动)
func (d *location) BatchUpdateStatusByZoneId(ctx context.Context, zoneId string, status stock.LocationStatus) (modifiedCount int64, err error) {
filter := bson.M{"zoneId": zoneId}
update := bson.M{"$set": bson.M{"status": status}}
modifiedCount, err = mongo.DB().Update(ctx, filter, update, public.LocationCollection)
return
}
// BatchUpdateStatusByWarehouseId 批量更新仓库下所有库位状态(用于状态联动)
func (d *location) BatchUpdateStatusByWarehouseId(ctx context.Context, warehouseId string, status stock.LocationStatus) (modifiedCount int64, err error) {
filter := bson.M{"warehouseId": warehouseId}
update := bson.M{"$set": bson.M{"status": status}}
modifiedCount, err = mongo.DB().Update(ctx, filter, update, public.LocationCollection)
return
}
// DeleteByZoneId 批量删除库区下所有库位(用于级联删除)
func (d *location) DeleteByZoneId(ctx context.Context, zoneId string) (modifiedCount int64, err error) {
filter := bson.M{"zoneId": zoneId}
modifiedCount, err = mongo.DB().DeleteSoft(ctx, filter, public.LocationCollection)
return
}
// DeleteByWarehouseId 批量删除仓库下所有库位(用于级联删除)
func (d *location) DeleteByWarehouseId(ctx context.Context, warehouseId string) (modifiedCount int64, err error) {
filter := bson.M{"warehouseId": warehouseId}
modifiedCount, err = mongo.DB().DeleteSoft(ctx, filter, public.LocationCollection)
return
}
// CountStockDetailsByLocationId 统计库位上的库存明细数量(用于删除前检查)
func (d *location) CountStockDetailsByLocationId(ctx context.Context, locationId string) (count int64, err error) {
filter := bson.M{"locationId": locationId}
count, err = mongo.DB().Count(ctx, filter, public.StockDetailsCollection)
return
}
// CountStockBatchByLocationId 统计库位上的批次库存数量(用于删除前检查)
func (d *location) CountStockBatchByLocationId(ctx context.Context, locationId string) (count int64, err error) {
filter := bson.M{"locationId": locationId}
count, err = mongo.DB().Count(ctx, filter, public.StockBatchCollection)
return
}
// CountPrivateStockByLocationId 统计库位上的私域库存数量(用于删除前检查)
func (d *location) CountPrivateStockByLocationId(ctx context.Context, locationId string) (count int64, err error) {
filter := bson.M{"locationId": locationId}
count, err = mongo.DB().Count(ctx, filter, public.PrivateStockCollection)
return
}
// UpdateCapacity 更新库位容量
func (d *location) UpdateCapacity(ctx context.Context, locationId *bson.ObjectID, currentCapacity int) (err error) {
filter := bson.M{"_id": locationId}
update := bson.M{"$set": bson.M{"capacity.currentCapacity": currentCapacity}}
_, err = mongo.DB().Update(ctx, filter, update, public.LocationCollection)
return
}
// ListByZoneAndUnitType 按库区和容量单位类型查询库位列表
func (d *location) ListByZoneAndUnitType(ctx context.Context, zoneId *bson.ObjectID, unitType stock.CapacityUnitType) (res []entity.Location, err error) {
filter := bson.M{
"zoneId": zoneId,
"capacityUnitType": unitType,
}
_, err = mongo.DB().Find(ctx, filter, &res, public.LocationCollection, nil, nil)
return
}

View File

@@ -0,0 +1,268 @@
// 实物库存批次DAO层
// 职责CRUD、IncrementAvailableQty原子操作(防并发超卖)、SumAvailableQtyByLocation聚合汇总
// 紧密耦合service.PrivateStock、service.Capacity(容量计算入口)
// 注意IncrementAvailableQty使用$inc+$gte条件防止并发导致库存变负
package dao
import (
"assets/consts/public"
"assets/consts/stock"
dto "assets/model/dto/stock"
entity "assets/model/entity/stock"
"context"
"gitea.com/red-future/common/beans"
"gitea.com/red-future/common/db/mongo"
"gitea.com/red-future/common/utils"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"go.mongodb.org/mongo-driver/v2/bson"
)
var PrivateStock = new(privateStock)
type privateStock struct {
}
// Insert 插入私域库存
func (d *privateStock) Insert(ctx context.Context, req *dto.CreatePrivateStockReq) (ids []interface{}, err error) {
var result *entity.PrivateStock
if err = utils.Struct(req, &result); err != nil {
return
}
// 设置StockType如果未指定默认为PrivateStock类型
if !g.IsEmpty(req.StockType) {
result.StockType = req.StockType
} else {
result.StockType = stock.StockLocationTypePrivateStock
}
// 获取仓库信息(非必填)
if req.WarehouseId != nil && !req.WarehouseId.IsZero() {
warehouse, err := Warehouse.GetOne(ctx, &dto.GetWarehouseReq{Id: req.WarehouseId})
if err != nil {
return nil, err
}
result.WarehouseID = req.WarehouseId
result.WarehouseCode = warehouse.WarehouseCode
result.WarehouseName = warehouse.WarehouseName
}
// 如果有库区信息
if req.ZoneId != nil && !req.ZoneId.IsZero() {
zone, err := Zone.GetOne(ctx, &dto.GetZoneReq{Id: req.ZoneId})
if err != nil {
return nil, err
}
result.ZoneID = req.ZoneId
result.ZoneCode = zone.ZoneCode
result.ZoneName = zone.ZoneName
result.ZoneType = zone.ZoneType
}
// 如果有库位信息
if req.LocationId != nil && !req.LocationId.IsZero() {
location, err := Location.GetOne(ctx, &dto.GetLocationReq{Id: req.LocationId})
if err != nil {
return nil, err
}
result.LocationID = req.LocationId
result.LocationCode = location.LocationCode
result.LocationName = location.LocationName
result.LocationType = location.LocationType
}
ids, err = mongo.DB().Insert(ctx, []interface{}{&result}, public.PrivateStockCollection)
return
}
// Update 更新私域库存
func (d *privateStock) Update(ctx context.Context, req *dto.UpdatePrivateStockReq) (err error) {
filter := bson.M{"_id": req.Id}
update := bson.M{"$set": bson.M{}}
if req.WarehouseId != nil && !req.WarehouseId.IsZero() {
warehouse, err := Warehouse.GetOne(ctx, &dto.GetWarehouseReq{Id: req.WarehouseId})
if err != nil {
return err
}
update["$set"].(bson.M)["warehouseId"] = req.WarehouseId
update["$set"].(bson.M)["warehouseCode"] = warehouse.WarehouseCode
update["$set"].(bson.M)["warehouseName"] = warehouse.WarehouseName
}
if req.ZoneId != nil && !req.ZoneId.IsZero() {
zone, err := Zone.GetOne(ctx, &dto.GetZoneReq{Id: req.ZoneId})
if err != nil {
return err
}
update["$set"].(bson.M)["zoneId"] = req.ZoneId
update["$set"].(bson.M)["zoneCode"] = zone.ZoneCode
update["$set"].(bson.M)["zoneName"] = zone.ZoneName
update["$set"].(bson.M)["zoneType"] = zone.ZoneType
}
if req.LocationId != nil && !req.LocationId.IsZero() {
location, err := Location.GetOne(ctx, &dto.GetLocationReq{Id: req.LocationId})
if err != nil {
return err
}
update["$set"].(bson.M)["locationId"] = req.LocationId
update["$set"].(bson.M)["locationCode"] = location.LocationCode
update["$set"].(bson.M)["locationName"] = location.LocationName
update["$set"].(bson.M)["locationType"] = location.LocationType
}
if req.PrivateSkuID != nil {
update["$set"].(bson.M)["privateSkuId"] = req.PrivateSkuID
}
if !g.IsEmpty(req.BatchNo) {
update["$set"].(bson.M)["batchNo"] = req.BatchNo
}
if req.BatchQty > 0 {
update["$set"].(bson.M)["batchQty"] = req.BatchQty
}
// AvailableQty已移除全量覆盖请使用IncrementAvailableQty方法进行增量更新
if req.BatchStatus != nil {
update["$set"].(bson.M)["batchStatus"] = req.BatchStatus
}
if req.StockStatus != nil {
update["$set"].(bson.M)["stockStatus"] = req.StockStatus
}
if req.SupplierID != nil {
update["$set"].(bson.M)["supplierId"] = req.SupplierID
}
if req.SupportsRecycle != nil {
update["$set"].(bson.M)["supportsRecycle"] = *req.SupportsRecycle
}
if req.ProductionDate != nil {
update["$set"].(bson.M)["productionDate"] = req.ProductionDate
}
if req.ExpiryDate != nil {
update["$set"].(bson.M)["expiryDate"] = req.ExpiryDate
}
if req.ExpiryWarningDate != nil {
update["$set"].(bson.M)["expiryWarningDate"] = req.ExpiryWarningDate
}
if !g.IsEmpty(req.PrivateCategoryPath) {
update["$set"].(bson.M)["privateCategoryPath"] = req.PrivateCategoryPath
}
if !g.IsEmpty(req.StockType) {
update["$set"].(bson.M)["stockType"] = req.StockType
}
_, err = mongo.DB().Update(ctx, filter, update, public.PrivateStockCollection)
return
}
// DeleteFake 软删除私域库存
func (d *privateStock) DeleteFake(ctx context.Context, req *dto.DeletePrivateStockReq) error {
filter := bson.M{"_id": req.Id}
_, err := mongo.DB().DeleteSoft(ctx, filter, public.PrivateStockCollection)
return err
}
// GetOne 根据ID查询私域库存
func (d *privateStock) GetOne(ctx context.Context, req *dto.GetPrivateStockReq) (res *entity.PrivateStock, err error) {
filter := bson.M{"_id": req.Id}
err = mongo.DB().FindOne(ctx, filter, &res, public.PrivateStockCollection)
return
}
// IncrementAvailableQty 增量更新可用数量(并发安全)
// deltaQty正数表示增加入库负数表示减少出库
// 减少时自动添加 availableQty >= |deltaQty| 条件,防止并发导致库存变负
func (d *privateStock) IncrementAvailableQty(ctx context.Context, id *bson.ObjectID, deltaQty int) (err error) {
filter := bson.M{"_id": id}
if deltaQty < 0 {
filter["availableQty"] = bson.M{"$gte": -deltaQty}
}
update := bson.M{
"$inc": bson.M{
"availableQty": deltaQty,
},
}
modifiedCount, err := mongo.DB().Update(ctx, filter, update, public.PrivateStockCollection)
if err != nil {
return
}
if deltaQty < 0 && modifiedCount == 0 {
err = gerror.Newf("库存不足,无法减少%d", -deltaQty)
}
return
}
// List 查询私域库存列表
func (d *privateStock) List(ctx context.Context, req *dto.ListPrivateStockReq) (res []entity.PrivateStock, total int64, err error) {
filter := bson.M{}
if req.WarehouseId != nil && !req.WarehouseId.IsZero() {
filter["warehouseId"] = req.WarehouseId
}
if req.ZoneId != nil && !req.ZoneId.IsZero() {
filter["zoneId"] = req.ZoneId
}
if req.LocationId != nil && !req.LocationId.IsZero() {
filter["locationId"] = req.LocationId
}
if req.PrivateSkuID != nil && !req.PrivateSkuID.IsZero() {
filter["privateSkuId"] = req.PrivateSkuID
}
if req.BatchStatus != nil {
filter["batchStatus"] = req.BatchStatus
}
if req.StockStatus != nil {
filter["stockStatus"] = req.StockStatus
}
if req.SupplierID != nil && !req.SupplierID.IsZero() {
filter["supplierId"] = req.SupplierID
}
if !g.IsEmpty(req.PrivateCategoryPath) {
filter["privateCategoryPath"] = req.PrivateCategoryPath
}
if !g.IsEmpty(req.StockType) {
filter["stockType"] = req.StockType
}
// 默认排序
if len(req.OrderBy) == 0 {
req.OrderBy = []beans.OrderBy{
{Field: "createdAt", Order: beans.Desc},
}
}
total, err = mongo.DB().Find(ctx, filter, &res, public.PrivateStockCollection, req.Page, req.OrderBy)
return
}
// SumAvailableQtyByLocation 按库位ID汇总所有库存的可用数量使用MongoDB聚合管道
func (d *privateStock) SumAvailableQtyByLocation(ctx context.Context, locationId *bson.ObjectID) (totalQty int, err error) {
pipeline := bson.A{
bson.M{"$match": bson.M{
"locationId": locationId,
"isDeleted": false,
}},
bson.M{"$group": bson.M{
"_id": nil,
"totalQty": bson.M{"$sum": "$availableQty"},
}},
}
coll := mongo.GetDB().Collection(public.PrivateStockCollection)
cursor, err := coll.Aggregate(ctx, pipeline)
if err != nil {
return
}
defer cursor.Close(ctx)
var results []struct {
TotalQty int `bson:"totalQty"`
}
if err = cursor.All(ctx, &results); err != nil {
return
}
if len(results) > 0 {
totalQty = results[0].TotalQty
}
return
}

View File

@@ -0,0 +1,78 @@
// 批次库存DAO层(逻辑库存)
// 职责批次CRUD、使用$inc原子操作更新数量
// 紧密耦合service.StockBatch、service.StockManage(入库出库)
// 注意Update使用$inc原子操作GetOne使用NoCache()跳过缓存
package dao
import (
"assets/consts/public"
dto "assets/model/dto/stock"
entity "assets/model/entity/stock"
"context"
"gitea.com/red-future/common/db/mongo"
"gitea.com/red-future/common/utils"
"go.mongodb.org/mongo-driver/v2/bson"
)
var StockBatch = new(stockBatch)
type stockBatch struct {
}
// Insert 插入
func (d *stockBatch) Insert(ctx context.Context, req *dto.CreateBatchReq) (ids []interface{}, err error) {
var result *entity.StockBatch
if err = utils.Struct(req, &result); err != nil {
return
}
ids, err = mongo.DB().Insert(ctx, []interface{}{&result}, public.StockBatchCollection)
return
}
// Update 更新批次数量(使用$inc原子操作并发安全
func (d *stockBatch) Update(ctx context.Context, req *dto.UpdateBatchReq) (err error) {
filter := bson.M{"_id": req.Id}
update := bson.M{
"$inc": bson.M{
"batchQty": req.BatchQty,
"availableQty": req.AvailableQty,
},
}
_, err = mongo.DB().Update(ctx, filter, update, public.StockBatchCollection)
return
}
// GetOne 根据批次号查询使用NoCache跳过缓存确保获取最新数据
func (d *stockBatch) GetOne(ctx context.Context, batchNo string) (res *entity.StockBatch, err error) {
filter := bson.M{"batchNo": batchNo}
err = mongo.DB().NoCache().FindOne(ctx, filter, &res, public.StockBatchCollection)
return
}
// GetOneById 根据ID查询批次
func (d *stockBatch) GetOneById(ctx context.Context, req *dto.GetBatchReq) (res *entity.StockBatch, err error) {
filter := bson.M{"_id": req.Id}
err = mongo.DB().FindOne(ctx, filter, &res, public.StockBatchCollection)
return
}
// DeleteFake 软删除批次
func (d *stockBatch) DeleteFake(ctx context.Context, req *dto.DeleteBatchReq) error {
filter := bson.M{"_id": req.Id}
_, err := mongo.DB().DeleteSoft(ctx, filter, public.StockBatchCollection)
return err
}
// List 查询批次列表
func (d *stockBatch) List(ctx context.Context, req *dto.ListBatchReq) (res []entity.StockBatch, total int64, err error) {
filter := bson.M{}
if req.AssetId != nil {
filter["assetId"] = req.AssetId
}
if req.AssetSkuId != nil {
filter["assetSkuId"] = req.AssetSkuId
}
total, err = mongo.DB().Find(ctx, filter, &res, public.StockBatchCollection, req.Page, req.OrderBy)
return
}

View File

@@ -0,0 +1,80 @@
// 库存明细DAO层(逻辑库存)
// 职责:批量插入/删除、按SKU统计数量、查询列表
// 紧密耦合service.StockDetails、service.StockManage(入库出库)
// 注意GetStockCountBySkuId使用NoCache()跳过缓存BatchInsert用于批量入库
package dao
import (
"assets/consts/public"
"assets/consts/stock"
dto "assets/model/dto/stock"
entity "assets/model/entity/stock"
"context"
"gitea.com/red-future/common/beans"
"gitea.com/red-future/common/db/mongo"
"github.com/gogf/gf/v2/frame/g"
"go.mongodb.org/mongo-driver/v2/bson"
)
var StockDetails = new(stockDetails)
type stockDetails struct {
}
// BatchInsert 批量插入库存
func (d *stockDetails) BatchInsert(ctx context.Context, stockInterfaces []interface{}) (ids []interface{}, err error) {
ids, err = mongo.DB().Insert(ctx, stockInterfaces, public.StockDetailsCollection)
return
}
// DeleteManyByIds 根据ID批量删除库存
func (d *stockDetails) DeleteManyByIds(ctx context.Context, allStockIds []*bson.ObjectID) (count int64, err error) {
if len(allStockIds) == 0 {
return 0, nil
}
filter := bson.M{"_id": bson.M{"$in": allStockIds}}
count, err = mongo.DB().Delete(ctx, filter, public.StockDetailsCollection)
return
}
// GetStockCountBySkuId 获取库存数根据SKU ID
func (d *stockDetails) GetStockCountBySkuId(ctx context.Context, assetSkuId *bson.ObjectID) (total int64, err error) {
// 构建查询过滤条件
filter := bson.M{}
filter["assetSkuId"] = assetSkuId
filter["status"] = stock.StockStatusAvailable
// 检查总数
total, err = mongo.DB().NoCache().Count(ctx, filter, public.StockDetailsCollection)
return total, err
}
// GetOneById 根据ID查询库存明细
func (d *stockDetails) GetOneById(ctx context.Context, req *dto.GetStockDetailsReq) (res *entity.StockDetails, err error) {
filter := bson.M{"_id": req.Id}
err = mongo.DB().FindOne(ctx, filter, &res, public.StockDetailsCollection)
return
}
// List 获取SKU列表
func (d *stockDetails) List(ctx context.Context, req *dto.ListStockDetailsReq) (res []entity.StockDetails, total int64, err error) {
// 构建查询过滤条件
filter := bson.M{}
if !g.IsEmpty(req.AssetId) {
filter["assetId"] = req.AssetId
}
if !g.IsEmpty(req.AssetSkuId) {
filter["assetSkuId"] = req.AssetSkuId
}
if !g.IsEmpty(req.Status) {
filter["status"] = req.Status
}
// 排序处理
req.OrderBy = []beans.OrderBy{
{Field: "sort", Order: beans.Asc},
{Field: "createdAt", Order: beans.Desc},
}
total, err = mongo.DB().Find(ctx, filter, &res, public.StockDetailsCollection, req.Page, req.OrderBy)
return
}

View File

@@ -0,0 +1,69 @@
// 单位换算DAO层
// 职责换算规则CRUD、按单位类型和源目标单位查询
// 紧密耦合service.UnitConversion、service.Capacity(容量计算换算)
// 注意GetByUnits用于容量计算时获取换算系数
package dao
import (
"assets/consts/public"
"assets/consts/stock"
entity "assets/model/entity/stock"
"context"
"gitea.com/red-future/common/db/mongo"
"go.mongodb.org/mongo-driver/v2/bson"
)
var UnitConversion = new(unitConversion)
type unitConversion struct{}
// GetByUnits 根据单位类型和源目标单位查询换算规则
func (d *unitConversion) GetByUnits(ctx context.Context, unitType stock.CapacityUnitType, fromUnit, toUnit string) (res *entity.UnitConversion, err error) {
filter := bson.M{
"unitType": unitType,
"fromUnit": fromUnit,
"toUnit": toUnit,
}
err = mongo.DB().FindOne(ctx, filter, &res, public.UnitConversionCollection)
return
}
// Insert 插入换算规则
func (d *unitConversion) Insert(ctx context.Context, conversion *entity.UnitConversion) (ids []interface{}, err error) {
ids, err = mongo.DB().Insert(ctx, []interface{}{conversion}, public.UnitConversionCollection)
return
}
// Update 更新换算规则
func (d *unitConversion) Update(ctx context.Context, id *bson.ObjectID, update bson.M) (err error) {
filter := bson.M{"_id": id}
updateDoc := bson.M{"$set": update}
_, err = mongo.DB().Update(ctx, filter, updateDoc, public.UnitConversionCollection)
return
}
// DeleteFake 软删除换算规则
func (d *unitConversion) DeleteFake(ctx context.Context, id *bson.ObjectID) (err error) {
filter := bson.M{"_id": id}
_, err = mongo.DB().DeleteSoft(ctx, filter, public.UnitConversionCollection)
return
}
// List 查询换算规则列表
func (d *unitConversion) List(ctx context.Context, unitType *stock.CapacityUnitType, fromUnit, toUnit string) (res []entity.UnitConversion, err error) {
filter := bson.M{}
if unitType != nil {
filter["unitType"] = *unitType
}
if fromUnit != "" {
filter["fromUnit"] = fromUnit
}
if toUnit != "" {
filter["toUnit"] = toUnit
}
_, err = mongo.DB().Find(ctx, filter, &res, public.UnitConversionCollection, nil, nil)
return
}

141
dao/stock/warehouse_dao.go Normal file
View File

@@ -0,0 +1,141 @@
// 仓库DAO层
// 职责仓库CRUD、状态更新、批量更新库区/库位状态(状态联动)、更新容量
// 紧密耦合service.Warehouse(状态联动)、service.Capacity(容量汇总)
// 注意UpdateCapacityByUnitType使用嵌套路径更新map字段
package dao
import (
"assets/consts/public"
"assets/consts/stock"
"assets/model/config"
dto "assets/model/dto/stock"
entity "assets/model/entity/stock"
"context"
"gitea.com/red-future/common/db/mongo"
"gitea.com/red-future/common/utils"
"github.com/gogf/gf/v2/frame/g"
"go.mongodb.org/mongo-driver/v2/bson"
)
var Warehouse = new(warehouse)
type warehouse struct{}
func (d *warehouse) Insert(ctx context.Context, req *dto.CreateWarehouseReq) (ids []interface{}, err error) {
var result *entity.Warehouse
if err = utils.Struct(req, &result); err != nil {
return
}
// 如果未传入状态,设置默认值为启用
if result.Status == "" {
result.Status = stock.WarehouseStatusEnabled
}
// 初始化Capacity为空map避免后续UpdateCapacityByUnitType panic
if result.Capacity == nil {
emptyMap := make(map[stock.CapacityUnitType]config.Capacity)
result.Capacity = &emptyMap
}
ids, err = mongo.DB().Insert(ctx, []interface{}{&result}, public.WarehouseCollection)
return
}
func (d *warehouse) GetOne(ctx context.Context, req *dto.GetWarehouseReq) (res *entity.Warehouse, err error) {
filter := bson.M{"_id": req.Id}
err = mongo.DB().FindOne(ctx, filter, &res, public.WarehouseCollection)
return
}
func (d *warehouse) Update(ctx context.Context, req *dto.UpdateWarehouseReq) (err error) {
buildFilter, err := mongo.BuildUpdateData(ctx, req)
if err != nil {
return
}
filter := bson.M{"_id": req.Id}
update := bson.M{"$set": buildFilter}
_, err = mongo.DB().Update(ctx, filter, update, public.WarehouseCollection)
return
}
func (d *warehouse) DeleteFake(ctx context.Context, req *dto.DeleteWarehouseReq) (err error) {
filter := bson.M{"_id": req.Id}
_, err = mongo.DB().DeleteSoft(ctx, filter, public.WarehouseCollection)
return
}
// UpdateStatus 更新仓库状态
func (d *warehouse) UpdateStatus(ctx context.Context, req *dto.UpdateWarehouseStatusReq) (err error) {
filter := bson.M{"_id": req.Id}
update := bson.M{"$set": bson.M{"status": req.Status}}
_, err = mongo.DB().Update(ctx, filter, update, public.WarehouseCollection)
return
}
func (d *warehouse) List(ctx context.Context, req *dto.ListWarehouseReq) (res []entity.Warehouse, total int64, err error) {
filter, err := d.buildListFilter(ctx, req)
if err != nil {
return
}
total, err = mongo.DB().Find(ctx, filter, &res, public.WarehouseCollection, req.Page, req.OrderBy)
return
}
func (d *warehouse) buildListFilter(ctx context.Context, req *dto.ListWarehouseReq) (filter bson.M, err error) {
_ = ctx
filter = bson.M{}
if !g.IsEmpty(req.Status) {
filter["status"] = req.Status
}
if !g.IsEmpty(req.Keyword) {
filter["$or"] = bson.A{
bson.M{"warehouseCode": bson.M{"$regex": req.Keyword, "$options": "i"}},
bson.M{"warehouseName": bson.M{"$regex": req.Keyword, "$options": "i"}},
}
}
return
}
// CountZonesByWarehouseId 统计仓库下的库区数量(用于删除前检查)
func (d *warehouse) CountZonesByWarehouseId(ctx context.Context, warehouseId string) (count int64, err error) {
filter := bson.M{"warehouseId": warehouseId}
count, err = mongo.DB().Count(ctx, filter, public.ZoneCollection)
return
}
// BatchUpdateZoneStatus 批量更新仓库下所有库区状态(用于状态联动)
// fromStatus 可选:指定时只更新当前状态为 fromStatus 的记录,避免覆盖其他状态
func (d *warehouse) BatchUpdateZoneStatus(ctx context.Context, warehouseId string, status stock.ZoneStatus, fromStatus ...stock.ZoneStatus) (modifiedCount int64, err error) {
filter := bson.M{"warehouseId": warehouseId}
if len(fromStatus) > 0 {
filter["status"] = fromStatus[0]
}
update := bson.M{"$set": bson.M{"status": status}}
modifiedCount, err = mongo.DB().Update(ctx, filter, update, public.ZoneCollection)
return
}
// UpdateCapacityByUnitType 更新仓库指定容量单位类型的容量
func (d *warehouse) UpdateCapacityByUnitType(ctx context.Context, warehouseId *bson.ObjectID, unitType stock.CapacityUnitType, currentCapacity int, maxCapacity int, capacityUnit string) (err error) {
filter := bson.M{"_id": warehouseId}
update := bson.M{
"$set": bson.M{
"capacity." + string(unitType) + ".currentCapacity": currentCapacity,
"capacity." + string(unitType) + ".maxCapacity": maxCapacity,
"capacity." + string(unitType) + ".capacityUnit": capacityUnit,
},
}
_, err = mongo.DB().Update(ctx, filter, update, public.WarehouseCollection)
return
}
// BatchUpdateLocationStatus 批量更新仓库下所有库位状态(用于状态联动)
// fromStatus 可选:指定时只更新当前状态为 fromStatus 的记录,避免覆盖其他状态
func (d *warehouse) BatchUpdateLocationStatus(ctx context.Context, warehouseId string, status stock.LocationStatus, fromStatus ...stock.LocationStatus) (modifiedCount int64, err error) {
filter := bson.M{"warehouseId": warehouseId}
if len(fromStatus) > 0 {
filter["status"] = fromStatus[0]
}
update := bson.M{"$set": bson.M{"status": status}}
modifiedCount, err = mongo.DB().Update(ctx, filter, update, public.LocationCollection)
return
}

162
dao/stock/zone_dao.go Normal file
View File

@@ -0,0 +1,162 @@
// 库区DAO层
// 职责库区CRUD、状态更新、批量更新库位状态(状态联动)、更新容量
// 紧密耦合service.Zone(状态联动)、service.Capacity(容量汇总)
// 注意UpdateCapacityByUnitType使用嵌套路径更新map字段
package dao
import (
"assets/consts/public"
"assets/consts/stock"
"assets/model/config"
dto "assets/model/dto/stock"
entity "assets/model/entity/stock"
"context"
"gitea.com/red-future/common/db/mongo"
"gitea.com/red-future/common/utils"
"github.com/gogf/gf/v2/frame/g"
"go.mongodb.org/mongo-driver/v2/bson"
)
var Zone = new(zone)
type zone struct{}
func (d *zone) Insert(ctx context.Context, req *dto.CreateZoneReq) (ids []interface{}, err error) {
var result *entity.Zone
if err = utils.Struct(req, &result); err != nil {
return
}
// 如果未传入状态,设置默认值为启用
if result.Status == "" {
result.Status = stock.ZoneStatusEnabled
}
// 初始化Capacity为空map避免后续UpdateCapacityByUnitType panic
if result.Capacity == nil {
emptyMap := make(map[stock.CapacityUnitType]config.Capacity)
result.Capacity = &emptyMap
}
ids, err = mongo.DB().Insert(ctx, []interface{}{&result}, public.ZoneCollection)
return
}
func (d *zone) GetOne(ctx context.Context, req *dto.GetZoneReq) (res *entity.Zone, err error) {
filter := bson.M{"_id": req.Id}
err = mongo.DB().FindOne(ctx, filter, &res, public.ZoneCollection)
return
}
func (d *zone) Update(ctx context.Context, req *dto.UpdateZoneReq) (err error) {
buildFilter, err := mongo.BuildUpdateData(ctx, req)
if err != nil {
return
}
filter := bson.M{"_id": req.Id}
update := bson.M{"$set": buildFilter}
_, err = mongo.DB().Update(ctx, filter, update, public.ZoneCollection)
return
}
func (d *zone) DeleteFake(ctx context.Context, req *dto.DeleteZoneReq) (err error) {
filter := bson.M{"_id": req.Id}
_, err = mongo.DB().DeleteSoft(ctx, filter, public.ZoneCollection)
return
}
// UpdateStatus 更新库区状态
func (d *zone) UpdateStatus(ctx context.Context, req *dto.UpdateZoneStatusReq) (err error) {
filter := bson.M{"_id": req.Id}
update := bson.M{"$set": bson.M{"status": req.Status}}
_, err = mongo.DB().Update(ctx, filter, update, public.ZoneCollection)
return
}
func (d *zone) List(ctx context.Context, req *dto.ListZoneReq) (res []entity.Zone, total int64, err error) {
filter, err := d.buildListFilter(ctx, req)
if err != nil {
return
}
total, err = mongo.DB().Find(ctx, filter, &res, public.ZoneCollection, req.Page, req.OrderBy)
return
}
func (d *zone) buildListFilter(ctx context.Context, req *dto.ListZoneReq) (filter bson.M, err error) {
_ = ctx
filter = bson.M{}
// 兼容单值和数组参数(优先使用数组)
if len(req.WarehouseIds) > 0 {
filter["warehouseId"] = bson.M{"$in": req.WarehouseIds}
} else if !g.IsEmpty(req.WarehouseId) {
filter["warehouseId"] = req.WarehouseId
}
if !g.IsEmpty(req.ZoneType) {
filter["zoneType"] = req.ZoneType
}
if !g.IsEmpty(req.Status) {
filter["status"] = req.Status
}
if !g.IsEmpty(req.Keyword) {
filter["$or"] = bson.A{
bson.M{"zoneCode": bson.M{"$regex": req.Keyword, "$options": "i"}},
bson.M{"zoneName": bson.M{"$regex": req.Keyword, "$options": "i"}},
}
}
return
}
// CountLocationsByZoneId 统计库区下的库位数量(用于删除前检查)
func (d *zone) CountLocationsByZoneId(ctx context.Context, zoneId string) (count int64, err error) {
filter := bson.M{"zoneId": zoneId}
count, err = mongo.DB().Count(ctx, filter, public.LocationCollection)
return
}
// CountByWarehouseId 统计仓库下的库区数量(用于删除前检查)
func (d *zone) CountByWarehouseId(ctx context.Context, warehouseId string) (count int64, err error) {
filter := bson.M{"warehouseId": warehouseId}
count, err = mongo.DB().Count(ctx, filter, public.ZoneCollection)
return
}
// BatchUpdateStatusByWarehouseId 批量更新仓库下所有库区状态(用于状态联动)
func (d *zone) BatchUpdateStatusByWarehouseId(ctx context.Context, warehouseId string, status stock.ZoneStatus) (modifiedCount int64, err error) {
filter := bson.M{"warehouseId": warehouseId}
update := bson.M{"$set": bson.M{"status": status}}
modifiedCount, err = mongo.DB().Update(ctx, filter, update, public.ZoneCollection)
return
}
// BatchUpdateLocationStatus 批量更新库区下所有库位状态(用于状态联动)
// fromStatus 可选:指定时只更新当前状态为 fromStatus 的记录,避免覆盖其他状态
func (d *zone) BatchUpdateLocationStatus(ctx context.Context, zoneId string, status stock.LocationStatus, fromStatus ...stock.LocationStatus) (modifiedCount int64, err error) {
filter := bson.M{"zoneId": zoneId}
if len(fromStatus) > 0 {
filter["status"] = fromStatus[0]
}
update := bson.M{"$set": bson.M{"status": status}}
modifiedCount, err = mongo.DB().Update(ctx, filter, update, public.LocationCollection)
return
}
// UpdateCapacityByUnitType 更新库区指定容量单位类型的容量
func (d *zone) UpdateCapacityByUnitType(ctx context.Context, zoneId *bson.ObjectID, unitType stock.CapacityUnitType, currentCapacity int, maxCapacity int, capacityUnit string) (err error) {
filter := bson.M{"_id": zoneId}
update := bson.M{
"$set": bson.M{
"capacity." + string(unitType) + ".currentCapacity": currentCapacity,
"capacity." + string(unitType) + ".maxCapacity": maxCapacity,
"capacity." + string(unitType) + ".capacityUnit": capacityUnit,
},
}
_, err = mongo.DB().Update(ctx, filter, update, public.ZoneCollection)
return
}
// ListByWarehouseAndUnitType 按仓库ID查询所有库区用于汇总仓库容量
func (d *zone) ListByWarehouseAndUnitType(ctx context.Context, warehouseId string) (res []entity.Zone, err error) {
filter := bson.M{
"warehouseId": warehouseId,
}
_, err = mongo.DB().Find(ctx, filter, &res, public.ZoneCollection, nil, nil)
return
}