Browse Source

Merge branch 'dev' of http://124.70.149.18:10880/sunsheng/box-cost into dev

sun-pc-linux 2 months ago
parent
commit
bda71aace9
8 changed files with 470 additions and 61 deletions
  1. 2 0
      boxcost/api/bill-produce.go
  2. 1 0
      boxcost/api/bill-product.go
  3. 1 0
      boxcost/api/bill.go
  4. 35 10
      boxcost/api/plan.go
  5. 9 0
      boxcost/api/router.go
  6. 419 48
      boxcost/api/stages.go
  7. 2 2
      boxcost/build.sh
  8. 1 1
      comm

+ 2 - 0
boxcost/api/bill-produce.go

@@ -288,8 +288,10 @@ func UpdateProduceBill(c *gin.Context, apictx *ApiSession) (interface{}, error)
 				DeliveryTime:   produce.DeliveryTime,
 			}
 		}
+
 		_, err := updateBilltoStage(c, bill.PlanId, idStatges, apictx)
 		if err != nil {
+			fmt.Println(err)
 			return nil, errors.New("该单据改动同步到产品失败")
 		}
 		fmt.Println("单据同步到产品,planId:", bill.PlanId)

+ 1 - 0
boxcost/api/bill-product.go

@@ -280,6 +280,7 @@ func UpdateProductBill(c *gin.Context, apictx *ApiSession) (interface{}, error)
 		}
 		_, err := updateBilltoStage(c, bill.PlanId, idStatges, apictx)
 		if err != nil {
+			fmt.Println(err)
 			return nil, errors.New("该单据改动同步到产品失败")
 		}
 		fmt.Println("单据同步到产品,planId:", bill.PlanId)

+ 1 - 0
boxcost/api/bill.go

@@ -421,6 +421,7 @@ func UpdateBill(c *gin.Context, apictx *ApiSession) (interface{}, error) {
 		}
 		_, err := updateBilltoStage(c, bill.PlanId, idStatges, apictx)
 		if err != nil {
+			fmt.Println(err)
 			return nil, errors.New("该单据改动同步到产品失败")
 		}
 		fmt.Println("单据同步到产品,planId:", bill.PlanId)

+ 35 - 10
boxcost/api/plan.go

@@ -221,19 +221,44 @@ func updateBilltoStage(c *gin.Context, planId primitive.ObjectID, idStatges map[
 				stage.Size = idStatge.Size
 				stage.UpdateTime = time.Now()
 				fmt.Printf("stage信息:%#v\n", stage)
+
+				// 更新stage
+				req := StageRequest{
+					PlanId:  planId.Hex(),
+					CompId:  comp.Id,
+					StageId: stage.Id,
+					Stages:  []*model.ComponentStage{stage},
+				}
+				db := apictx.Svc.Mongo.GetCollection(repo.CollectionProductPlan)
+				_, err = updateStage(c, db, &req)
+				if err != nil {
+					return nil, err
+				}
+				// 更新计划价格
+				err := updatePrices(c, db, planId, comp.Id)
+				if err != nil {
+					return nil, err
+				}
+				// 记录历史
+				_, err = AddPlanHistory(c, apictx, req.PlanId, "sync")
+				if err != nil {
+					fmt.Println("记录历史失败:", err)
+				}
+				return true, nil
 			}
 		}
 	}
-	plan.UpdateTime = time.Now()
-	userId, _ := primitive.ObjectIDFromHex(apictx.User.ID)
-	user, _ := getUserById(apictx, userId)
-	// return repo.RepoUpdateSetDoc(apictx.CreateRepoCtx(), repo.CollectionProductPlan, planId.Hex(), &plan)
-	return repo.RepoUpdateSetDoc1(apictx.CreateRepoCtx(), repo.CollectionProductPlan, planId.Hex(), &plan, &repo.RecordLogReq{
-		Path:     c.Request.URL.Path,
-		UserInfo: user,
-		TargetId: planId.Hex(),
-		Type:     "sync",
-	})
+	return true, nil
+	// plan.UpdateTime = time.Now()
+	// userId, _ := primitive.ObjectIDFromHex(apictx.User.ID)
+	// user, _ := getUserById(apictx, userId)
+	// // return repo.RepoUpdateSetDoc(apictx.CreateRepoCtx(), repo.CollectionProductPlan, planId.Hex(), &plan)
+	// return repo.RepoUpdateSetDoc1(apictx.CreateRepoCtx(), repo.CollectionProductPlan, planId.Hex(), &plan, &repo.RecordLogReq{
+	// 	Path:     c.Request.URL.Path,
+	// 	UserInfo: user,
+	// 	TargetId: planId.Hex(),
+	// 	Type:     "sync",
+	// })
 }
 
 // 更新供应商确定数量与plan中stage项的同步

+ 9 - 0
boxcost/api/router.go

@@ -71,6 +71,15 @@ func RegRouters(svc *Service) {
 	// 计划汇总
 	Summary(boxcost)
 
+	// plan相关操作
+	boxcost.POSTJWT("/plan/add/component/:id", AddCompnonet)
+	boxcost.POSTJWT("/plan/delete/component/:id/:componentId", DeleteCompnonet)
+	boxcost.POSTJWT("/plan/update/component/:id/:componentId", UpdateCompnonet)
+	boxcost.POSTJWT("/plan/add/stages", AddStage)
+	boxcost.POSTJWT("/plan/update/stages", UpdateStages)
+	boxcost.POSTJWT("/plan/delete/stage", DeleteStage)
+	boxcost.POSTJWT("/plan/update/fileds", UpdatePlanfileds)
+
 	boxcost.GET("/apk/version", func(c *gin.Context, apictx *ApiSession) (interface{}, error) {
 		out, err := repo.RepoPageSearch(apictx.CreateRepoCtx(), &repo.PageSearchOptions{
 			CollectName: "versions",

+ 419 - 48
boxcost/api/stages.go

@@ -3,7 +3,11 @@ package api
 import (
 	"box-cost/db/model"
 	"box-cost/db/repo"
-	"context"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"math"
+	"time"
 
 	"github.com/gin-gonic/gin"
 	"go.mongodb.org/mongo-driver/bson"
@@ -14,18 +18,18 @@ import (
 
 // StageRequest 用于添加和更新stage的请求结构
 type StageRequest struct {
-	PlanId  string                `json:"planId"`
-	PackId  string                `json:"packId"`
-	CompId  string                `json:"compId"`
-	StageId string                `json:"stageId,omitempty"`
-	Stage   *model.ComponentStage `json:"stage"`
+	PlanId string `json:"planId"`
+	// PackId  string                  `json:"packId"`
+	CompId  string                  `json:"componentId"`
+	StageId string                  `json:"stageId,omitempty"`
+	Stages  []*model.ComponentStage `json:"stages"`
 }
 
 // 更新计划和部件的价格
-func updatePrices(ctx context.Context, db *mongo.Collection, planId primitive.ObjectID, compId string) error {
+func updatePrices(c *gin.Context, db *mongo.Collection, planId primitive.ObjectID, compId string) error {
 	// 1. 获取计划详情
 	var plan model.ProductPlan
-	err := db.FindOne(ctx, bson.M{"_id": planId}).Decode(&plan)
+	err := db.FindOne(c.Request.Context(), bson.M{"_id": planId}).Decode(&plan)
 	if err != nil {
 		return err
 	}
@@ -39,11 +43,13 @@ func updatePrices(ctx context.Context, db *mongo.Collection, planId primitive.Ob
 			stagePrice := stage.OrderPrice * float64(stage.OrderCount)
 			compPrice += stagePrice
 		}
+		// 截断组件总价到两位小数
+		_compPrice := math.Trunc(compPrice*1000) / 1000
 		// 更新组件总价
 		if comp.Id == compId {
-			_, err = db.UpdateOne(ctx,
+			_, err = db.UpdateOne(c.Request.Context(),
 				bson.M{"_id": planId, "pack.components.id": compId},
-				bson.M{"$set": bson.M{"pack.components.$.totalPrice": compPrice}})
+				bson.M{"$set": bson.M{"pack.components.$.totalPrice": _compPrice}})
 			if err != nil {
 				return err
 			}
@@ -51,8 +57,9 @@ func updatePrices(ctx context.Context, db *mongo.Collection, planId primitive.Ob
 		totalPrice += compPrice
 	}
 
-	// 3. 更新计划总价
-	_, err = db.UpdateOne(ctx,
+	// 3. 更新计划总价(截断到两位小数)
+	totalPrice = math.Trunc(totalPrice*1000) / 1000
+	_, err = db.UpdateOne(c.Request.Context(),
 		bson.M{"_id": planId},
 		bson.M{"$set": bson.M{"totalPrice": &totalPrice}})
 	return err
@@ -72,15 +79,15 @@ func AddStage(c *gin.Context, apictx *ApiSession) (interface{}, error) {
 		return nil, err
 	}
 
-	// 创建新的stage,使用请求中的stage数据
-	newStage := req.Stage
-	newStage.Id = primitive.NewObjectID().Hex() // 确保生成新的ID
-
-	// 更新数据库
-	db := apictx.Svc.Mongo.GetCollection(repo.CollectionProductPlan)
+	// 更新数据库,添加多个stage
 	update := bson.M{
 		"$push": bson.M{
-			"pack.components.$[components].stages": newStage,
+			"pack.components.$[components].stages": bson.M{
+				"$each": req.Stages,
+			},
+		},
+		"$set": bson.M{
+			"updateTime": time.Now(),
 		},
 	}
 	arrayFilters := []interface{}{
@@ -90,8 +97,9 @@ func AddStage(c *gin.Context, apictx *ApiSession) (interface{}, error) {
 	opts := options.Update().SetArrayFilters(options.ArrayFilters{
 		Filters: arrayFilters,
 	})
+	db := apictx.Svc.Mongo.GetCollection(repo.CollectionProductPlan)
 
-	result, err := db.UpdateOne(apictx.CreateRepoCtx().Ctx,
+	result, err := db.UpdateOne(c.Request.Context(),
 		bson.M{"_id": planId},
 		update,
 		opts,
@@ -102,11 +110,15 @@ func AddStage(c *gin.Context, apictx *ApiSession) (interface{}, error) {
 	}
 
 	// 更新价格
-	err = updatePrices(apictx.CreateRepoCtx().Ctx, db, planId, req.CompId)
+	err = updatePrices(c, db, planId, req.CompId)
 	if err != nil {
 		return nil, err
 	}
 
+	_, err = AddPlanHistory(c, apictx, req.PlanId)
+	if err != nil {
+		fmt.Println("记录历史失败:", err)
+	}
 	return result, nil
 }
 
@@ -123,6 +135,9 @@ func DeleteStage(c *gin.Context, apictx *ApiSession) (interface{}, error) {
 	if err != nil {
 		return nil, err
 	}
+	if req.StageId == "" || req.CompId == "" {
+		return nil, errors.New("stageId and compId are required")
+	}
 
 	// 更新数据库
 	db := apictx.Svc.Mongo.GetCollection(repo.CollectionProductPlan)
@@ -141,7 +156,7 @@ func DeleteStage(c *gin.Context, apictx *ApiSession) (interface{}, error) {
 		Filters: arrayFilters,
 	})
 
-	result, err := db.UpdateOne(apictx.CreateRepoCtx().Ctx,
+	result, err := db.UpdateOne(c.Request.Context(),
 		bson.M{"_id": planId},
 		update,
 		opts,
@@ -152,82 +167,438 @@ func DeleteStage(c *gin.Context, apictx *ApiSession) (interface{}, error) {
 	}
 
 	// 更新价格
-	err = updatePrices(apictx.CreateRepoCtx().Ctx, db, planId, req.CompId)
+	err = updatePrices(c, db, planId, req.CompId)
 	if err != nil {
 		return nil, err
 	}
 
+	_, err = AddPlanHistory(c, apictx, req.PlanId)
+	if err != nil {
+		fmt.Println("记录历史失败:", err)
+	}
+
 	return result, nil
 }
 
-// 更新stage
-// 根据planId packId compentId stageId定位到到计划中的stage
+// 根据planId compentId stageId定位到到计划中的stage
 // 注意更新计划价格 组件价格
-func UpdateStage(c *gin.Context, apictx *ApiSession) (interface{}, error) {
-	var req StageRequest
-	if err := c.ShouldBindJSON(&req); err != nil {
-		return nil, err
+// 根据planId compentId stageId定位到到计划中的stage
+// 注意更新计划价格 组件价格
+func updateStage(c *gin.Context, db *mongo.Collection, req *StageRequest) (interface{}, error) {
+	if len(req.PlanId) == 0 || len(req.CompId) == 0 || len(req.StageId) == 0 {
+		return nil, fmt.Errorf("planId, componentId and stageId are required")
 	}
 
 	planId, err := primitive.ObjectIDFromHex(req.PlanId)
 	if err != nil {
-		return nil, err
+		return nil, fmt.Errorf("invalid planId: %v", err)
 	}
 
-	// 更新数据库
-	db := apictx.Svc.Mongo.GetCollection(repo.CollectionProductPlan)
+	// 构建更新操作
 	update := bson.M{
 		"$set": bson.M{
-			"pack.components.$[components].stages.$[stage]": req.Stage,
+			"pack.components.$[comp].stages.$[stg]": req.Stages[0],
+			"updateTime":                            time.Now(),
 		},
 	}
 
-	arrayFilters := []interface{}{
-		bson.M{"components.id": req.CompId},
-		bson.M{"stage.id": req.StageId},
+	// 使用正确的数组过滤器
+	arrayFilters := options.ArrayFilters{
+		Filters: []interface{}{
+			bson.M{"comp.id": req.CompId},
+			bson.M{"stg.id": req.StageId},
+		},
 	}
 
-	opts := options.Update().SetArrayFilters(options.ArrayFilters{
-		Filters: arrayFilters,
-	})
+	// 执行更新
+	opts := options.FindOneAndUpdate().
+		SetArrayFilters(arrayFilters).
+		SetReturnDocument(options.After)
 
-	result, err := db.UpdateOne(apictx.CreateRepoCtx().Ctx,
+	var updatedDoc bson.M
+	err = db.FindOneAndUpdate(
+		c.Request.Context(),
 		bson.M{"_id": planId},
 		update,
 		opts,
-	)
+	).Decode(&updatedDoc)
 
 	if err != nil {
-		return nil, err
+		if err == mongo.ErrNoDocuments {
+			// 打印更详细的错误信息
+			fmt.Printf("Debug - Failed to update. PlanId: %s, CompId: %s, StageId: %s\n", planId.Hex(), req.CompId, req.StageId)
+			return nil, fmt.Errorf("plan or stage not found, planId: %s, compId: %s, stageId: %s", req.PlanId, req.CompId, req.StageId)
+		}
+		return nil, fmt.Errorf("failed to update stage: %v", err)
 	}
 
+	fmt.Printf("Debug - Update successful\n")
+
 	// 更新价格
-	err = updatePrices(apictx.CreateRepoCtx().Ctx, db, planId, req.CompId)
+	err = updatePrices(c, db, planId, req.CompId)
 	if err != nil {
+		return nil, fmt.Errorf("failed to update prices: %v", err)
+	}
+
+	return true, nil
+}
+
+// 更新stages
+// 根据planId packId compentId stageId定位到到计划中的stage
+// 注意更新计划价格 组件价格
+func UpdateStages(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	var req StageRequest
+	if err := c.ShouldBindJSON(&req); err != nil {
 		return nil, err
 	}
 
+	// 参数检查
+	if len(req.PlanId) == 0 || len(req.CompId) == 0 || len(req.Stages) == 0 {
+		return nil, fmt.Errorf("invalid parameters: planId, componentId and stages are required")
+	}
+
+	planId, err := primitive.ObjectIDFromHex(req.PlanId)
+	if err != nil {
+		return nil, fmt.Errorf("invalid planId: %v", err)
+	}
+
+	// 获取更新前的数据用于验证
+	db := apictx.Svc.Mongo.GetCollection(repo.CollectionProductPlan)
+	ctx := c.Request.Context()
+
+	var plan model.ProductPlan
+	err = db.FindOne(ctx, bson.M{"_id": planId}).Decode(&plan)
+	if err != nil {
+		return nil, fmt.Errorf("failed to find plan: %v", err)
+	}
+
+	// 验证组件是否存在
+	componentExists := false
+	if plan.Pack != nil {
+		for _, comp := range plan.Pack.Components {
+			if comp.Id == req.CompId {
+				componentExists = true
+				break
+			}
+		}
+	}
+	if !componentExists {
+		return nil, fmt.Errorf("component %s not found in plan", req.CompId)
+	}
+
+	// 准备批量更新操作
+	var models []mongo.WriteModel
+	now := time.Now()
+
+	for _, stage := range req.Stages {
+		if stage.Id == "" {
+			continue // 跳过没有ID的stage
+		}
+
+		update := bson.M{
+			"$set": bson.M{
+				"pack.components.$[components].stages.$[stage]": stage,
+				"updateTime": now,
+			},
+		}
+
+		arrayFilters := options.ArrayFilters{
+			Filters: []interface{}{
+				bson.M{"components.id": req.CompId},
+				bson.M{"stage.id": stage.Id},
+			},
+		}
+
+		model := mongo.NewUpdateOneModel().
+			SetFilter(bson.M{"_id": planId}).
+			SetUpdate(update).
+			SetArrayFilters(arrayFilters)
+
+		models = append(models, model)
+	}
+
+	if len(models) == 0 {
+		return nil, fmt.Errorf("no valid stages to update")
+	}
+
+	// 执行批量更新
+	opts := options.BulkWrite().SetOrdered(false) // 允许并行执行
+	result, err := db.BulkWrite(c.Request.Context(), models, opts)
+	if err != nil {
+		return nil, fmt.Errorf("failed to update stages: %v", err)
+	}
+
+	// 更新价格
+	err = updatePrices(c, db, planId, req.CompId)
+	if err != nil {
+		return nil, fmt.Errorf("failed to update prices: %v", err)
+	}
+
+	// 记录历史
+	_, err = AddPlanHistory(c, apictx, req.PlanId)
+	if err != nil {
+		fmt.Println("记录历史失败:", err)
+	}
+
 	return result, nil
 }
 
+type UpdatePlanFieldsReq struct {
+	PlanId     string `json:"planId"`
+	Total      int    `json:"total,omitempty"`
+	CreateUser string `json:"createUser,omitempty"`
+	Name       string `json:"name,omitempty"`
+}
+
 // 单独更新计划字段
 func UpdatePlanfileds(c *gin.Context, apictx *ApiSession) (interface{}, error) {
-	return nil, nil
+	var req UpdatePlanFieldsReq
+	if err := c.ShouldBindJSON(&req); err != nil {
+		return nil, err
+	}
+
+	planId, _ := primitive.ObjectIDFromHex(req.PlanId)
+	if planId.IsZero() {
+		return nil, fmt.Errorf("invalid planId")
+	}
+	result, err := repo.RepoUpdateSetDoc(apictx.CreateRepoCtx(), repo.CollectionProductPlan, req.PlanId, &model.ProductPlan{
+		Total:      req.Total,
+		CreateUser: req.CreateUser,
+		Name:       req.Name,
+		UpdateTime: time.Now(),
+	})
+	if err != nil {
+		return nil, err
+	}
 
+	// 记录历史
+	_, err = AddPlanHistory(c, apictx, req.PlanId)
+	if err != nil {
+		fmt.Println("记录历史失败:", err)
+	}
+
+	return result, nil
 }
 
 // 新增组件
 func AddCompnonet(c *gin.Context, apictx *ApiSession) (interface{}, error) {
-	return nil, nil
+	_planId := c.Param("id")
+	planId, _ := primitive.ObjectIDFromHex(_planId)
+	if planId.IsZero() {
+		return nil, fmt.Errorf("planId is invalid")
+	}
+
+	var component model.PackComponent
+	if err := c.ShouldBindJSON(&component); err != nil {
+		return nil, err
+	}
+
+	// 计算新的总价
+	newTotalPrice := component.TotalPrice
+	oldPlan := &model.ProductPlan{}
+	db := apictx.Svc.Mongo.GetCollection(repo.CollectionProductPlan)
+	if err := db.FindOne(c.Request.Context(), bson.M{"_id": planId}).Decode(&oldPlan); err != nil {
+		return nil, err
+	} else if oldPlan.TotalPrice != nil {
+		newTotalPrice += *oldPlan.TotalPrice
+	}
+	_newTotalPrice := math.Trunc(newTotalPrice*1000) / 1000
+	update := bson.M{
+		"$push": bson.M{
+			"pack.components": component,
+		},
+		"$set": bson.M{
+			"updateTime": time.Now(),
+			"totalPrice": _newTotalPrice,
+		},
+		"$inc": bson.M{
+			"pack.compCounts": 1,
+		},
+	}
+
+	filter := bson.M{"_id": planId}
+	result := db.FindOneAndUpdate(c.Request.Context(), filter, update)
+	if result.Err() != nil {
+		return nil, result.Err()
+	}
+
+	_, err := AddPlanHistory(c, apictx, _planId)
+	if err != nil {
+		fmt.Println("记录历史失败:", err)
+	}
 
+	return true, nil
 }
 
 func DeleteCompnonet(c *gin.Context, apictx *ApiSession) (interface{}, error) {
-	return nil, nil
+	_planId := c.Param("id")
+	planId, _ := primitive.ObjectIDFromHex(_planId)
+	if planId.IsZero() {
+		return nil, fmt.Errorf("planId is invalid")
+	}
+
+	componentId := c.Param("componentId")
+	if len(componentId) == 0 {
+		return nil, fmt.Errorf("componentId is invalid")
+	}
+
+	oldPlan := &model.ProductPlan{}
+	// Find the component to be deleted and its price
+	db := apictx.Svc.Mongo.GetCollection(repo.CollectionProductPlan)
+	var componentPrice float64
+	if err := db.FindOne(c.Request.Context(), bson.M{"_id": planId}).Decode(&oldPlan); err != nil {
+		return nil, err
+	} else if oldPlan.Pack != nil {
+		for _, comp := range oldPlan.Pack.Components {
+			if comp.Id == componentId {
+				componentPrice = comp.TotalPrice
+				break
+			}
+		}
+	}
 
+	var newTotalPrice float64
+	if oldPlan.TotalPrice != nil {
+		newTotalPrice = *oldPlan.TotalPrice - componentPrice
+		if newTotalPrice < 0 {
+			newTotalPrice = 0
+		}
+	}
+	_newTotalPrice := math.Trunc(newTotalPrice*1000) / 1000
+	update := bson.M{
+		"$pull": bson.M{
+			"pack.components": bson.M{"id": componentId},
+		},
+		"$set": bson.M{
+			"updateTime": time.Now(),
+			"totalPrice": _newTotalPrice,
+		},
+		"$inc": bson.M{
+			"pack.compCounts": -1,
+		},
+	}
+
+	filter := bson.M{"_id": planId}
+	result := db.FindOneAndUpdate(c.Request.Context(), filter, update)
+	if result.Err() != nil {
+		return nil, result.Err()
+	}
+
+	_, err := AddPlanHistory(c, apictx, _planId)
+	if err != nil {
+		fmt.Println("记录历史失败:", err)
+	}
+
+	return true, nil
+}
+
+type UpdateCompnonetReq struct {
+	Name   string                  `json:"name,omitempty"`
+	Stages []*model.ComponentStage `json:"stages,omitempty"`
 }
 
-func UpdateCompnonetName(c *gin.Context, apictx *ApiSession) (interface{}, error) {
-	return nil, nil
+func UpdateCompnonet(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	_planId := c.Param("id")
+	planId, _ := primitive.ObjectIDFromHex(_planId)
+	if planId.IsZero() {
+		return nil, fmt.Errorf("planId is invalid")
+	}
+	componentId := c.Param("componentId")
+	if len(componentId) == 0 {
+		return nil, fmt.Errorf("componentId is invalid")
+	}
+
+	var nameUpdate UpdateCompnonetReq
+	if err := c.ShouldBindJSON(&nameUpdate); err != nil {
+		return nil, err
+	}
+
+	// 检查是否有需要更新的字段
+	if len(nameUpdate.Name) == 0 && len(nameUpdate.Stages) == 0 {
+		return nil, fmt.Errorf("no fields to update")
+	}
+
+	db := apictx.Svc.Mongo.GetCollection(repo.CollectionProductPlan)
+	update := bson.M{
+		"$set": bson.M{
+			"updateTime": time.Now(),
+		},
+	}
+
+	if len(nameUpdate.Name) > 0 {
+		update["$set"].(bson.M)["pack.components.$.name"] = nameUpdate.Name
+	}
+
+	if len(nameUpdate.Stages) > 0 {
+		update["$set"].(bson.M)["pack.components.$.stages"] = nameUpdate.Stages
+	}
+
+	filter := bson.M{
+		"_id":                planId,
+		"pack.components.id": componentId,
+	}
 
+	result := db.FindOneAndUpdate(c.Request.Context(), filter, update)
+	if result.Err() != nil {
+		return nil, result.Err()
+	}
+
+	// 如果更新了stages,需要更新价格
+	if len(nameUpdate.Stages) > 0 {
+		err := updatePrices(c, db, planId, componentId)
+		if err != nil {
+			return nil, fmt.Errorf("failed to update prices: %v", err)
+		}
+	}
+
+	// 记录历史
+	_, err := AddPlanHistory(c, apictx, _planId)
+	if err != nil {
+		fmt.Println("记录历史失败:", err)
+	}
+
+	return true, nil
+}
+
+// 记录历史
+func AddPlanHistory(c *gin.Context, apictx *ApiSession, planId string, ht ...string) (interface{}, error) {
+	htype := "update"
+	if len(ht) > 0 {
+		htype = ht[0]
+	}
+	// 获取更新后的数据并记录历史
+	newPlan := &model.ProductPlan{}
+	found, err := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
+		CollectName: repo.CollectionProductPlan,
+		Query:       repo.Map{"_id": planId},
+	}, newPlan)
+	if !found {
+		return false, errors.New("数据未找到")
+	}
+	if err != nil {
+		return false, err
+	}
+
+	newPlanBytes, _ := json.Marshal(newPlan)
+	userId, _ := primitive.ObjectIDFromHex(apictx.User.ID)
+	userInfo, err := getUserById(apictx, userId)
+	if err != nil {
+		return false, err
+	}
+
+	history := &model.History{
+		Userinfo:   userInfo,
+		TargetId:   planId,
+		Path:       c.Request.URL.Path,
+		Collection: repo.CollectionProductPlan,
+		Type:       htype,
+		Content:    string(newPlanBytes),
+		CreateTime: time.Now(),
+	}
+
+	_, err = repo.RepoAddDoc(apictx.CreateRepoCtx(), repo.CollectionPlanHistory, history)
+	if err != nil {
+		return false, err
+	}
+	return true, nil
 }

+ 2 - 2
boxcost/build.sh

@@ -5,8 +5,8 @@ echo "building..."
 go build -o box-cost-service
 
 # 命名镜像
-local_imge="pack-box-cost:v1.0.5"
-repository_image="registry.cn-chengdu.aliyuncs.com/infish/pack-box-cost:v1.0.5"
+local_imge="pack-box-cost:v1.0.6"
+repository_image="registry.cn-chengdu.aliyuncs.com/infish/pack-box-cost:v1.0.6"
 
 # 删除本地已存在的镜像
 docker rmi $repository_image

+ 1 - 1
comm

@@ -1 +1 @@
-Subproject commit 813fd6263a72bf65a1cfa28866307963fb9c845a
+Subproject commit f4266e04a734dc123c65dd48e8fc30a51a92b9e0