// 盘点任务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 }