sun-pc 2 months ago
parent
commit
52040d5fda
5 changed files with 330 additions and 37 deletions
  1. 9 0
      boxcost/api/router.go
  2. 309 36
      boxcost/api/stages.go
  3. 1 1
      boxcost/db/db.go
  4. 4 0
      boxcost/go.mod
  5. 7 0
      boxcost/go.sum

+ 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/componentName/:id/:componentId", UpdateCompnonetName)
+	boxcost.POSTJWT("/plan/add/stages", AddStage)
+	boxcost.POSTJWT("/plan/update/stages", UpdateStage)
+	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",

+ 309 - 36
boxcost/api/stages.go

@@ -4,6 +4,10 @@ import (
 	"box-cost/db/model"
 	"box-cost/db/repo"
 	"context"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"time"
 
 	"github.com/gin-gonic/gin"
 	"go.mongodb.org/mongo-driver/bson"
@@ -14,11 +18,11 @@ 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"`
 }
 
 // 更新计划和部件的价格
@@ -72,15 +76,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,6 +94,7 @@ 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,
 		bson.M{"_id": planId},
@@ -107,6 +112,10 @@ func AddStage(c *gin.Context, apictx *ApiSession) (interface{}, error) {
 		return nil, err
 	}
 
+	_, err = AddPlanHistory(c, apictx, req.PlanId)
+	if err != nil {
+		fmt.Println("记录历史失败:", err)
+	}
 	return result, nil
 }
 
@@ -123,6 +132,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)
@@ -157,10 +169,15 @@ func DeleteStage(c *gin.Context, apictx *ApiSession) (interface{}, error) {
 		return nil, err
 	}
 
+	_, err = AddPlanHistory(c, apictx, req.PlanId)
+	if err != nil {
+		fmt.Println("记录历史失败:", err)
+	}
+
 	return result, nil
 }
 
-// 更新stage
+// 更新stages
 // 根据planId packId compentId stageId定位到到计划中的stage
 // 注意更新计划价格 组件价格
 func UpdateStage(c *gin.Context, apictx *ApiSession) (interface{}, error) {
@@ -169,65 +186,321 @@ func UpdateStage(c *gin.Context, apictx *ApiSession) (interface{}, error) {
 		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, 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,
-		},
+	var plan model.ProductPlan
+	err = db.FindOne(apictx.CreateRepoCtx().Ctx, bson.M{"_id": planId}).Decode(&plan)
+	if err != nil {
+		return nil, fmt.Errorf("failed to find plan: %v", err)
 	}
 
-	arrayFilters := []interface{}{
-		bson.M{"components.id": req.CompId},
-		bson.M{"stage.id": req.StageId},
+	// 验证组件是否存在
+	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)
 	}
 
-	opts := options.Update().SetArrayFilters(options.ArrayFilters{
-		Filters: arrayFilters,
-	})
+	// 准备批量更新操作
+	var models []mongo.WriteModel
+	now := time.Now()
 
-	result, err := db.UpdateOne(apictx.CreateRepoCtx().Ctx,
-		bson.M{"_id": planId},
-		update,
-		opts,
-	)
+	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(apictx.CreateRepoCtx().Ctx, models, opts)
 	if err != nil {
-		return nil, err
+		return nil, fmt.Errorf("failed to update stages: %v", err)
 	}
 
 	// 更新价格
 	err = updatePrices(apictx.CreateRepoCtx().Ctx, db, planId, req.CompId)
 	if err != nil {
-		return nil, err
+		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,
+	})
+	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, bson.M{"_id": planId}).Decode(&oldPlan); err != nil {
+		return nil, err
+	} else if oldPlan.TotalPrice != nil {
+		newTotalPrice += *oldPlan.TotalPrice
+	}
+	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, 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, 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
+		}
+	}
+
+	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, 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 UpdateCompnonetName(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")
+	}
+
+	var nameUpdate struct {
+		Name string `json:"name"`
+	}
+	if err := c.ShouldBindJSON(&nameUpdate); err != nil {
+		return nil, err
+	}
+
+	update := bson.M{
+		"$set": bson.M{
+			"pack.components.$.name": nameUpdate.Name,
+			"updateTime":             time.Now(),
+		},
+	}
+
+	filter := bson.M{
+		"_id":                planId,
+		"pack.components.id": componentId,
+	}
+	db := apictx.Svc.Mongo.GetCollection(repo.CollectionProductPlan)
+	result := db.FindOneAndUpdate(c, 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 AddPlanHistory(c *gin.Context, apictx *ApiSession, planId string) (interface{}, error) {
+	// 获取更新后的数据并记录历史
+	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:       "update",
+		Content:    string(newPlanBytes),
+		CreateTime: time.Now(),
+	}
+
+	_, err = repo.RepoAddDoc(apictx.CreateRepoCtx(), repo.CollectionPlanHistory, history)
+	if err != nil {
+		return false, err
+	}
+	return true, nil
 }

+ 1 - 1
boxcost/db/db.go

@@ -35,7 +35,7 @@ func (db *MongoDB) GetOrCreateDatabase(name string) *mongo.Database {
 }
 
 func NewMongoDB(bus *comm.NatsBus) *MongoDB {
-	inst, err := bus.NewMongoDBFromConfig("box-mongo")
+	inst, err := bus.NewMongoDBFromConfigDev("box-mongo")
 	if err != nil {
 		panic(err)
 	}

+ 4 - 0
boxcost/go.mod

@@ -21,6 +21,7 @@ require (
 )
 
 require (
+	github.com/StackExchange/wmi v1.2.1 // indirect
 	github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4 // indirect
 	github.com/alibabacloud-go/debug v0.0.0-20190504072949-9472017b5c68 // indirect
 	github.com/alibabacloud-go/endpoint-util v1.1.0 // indirect
@@ -29,6 +30,9 @@ require (
 	github.com/alibabacloud-go/tea-xml v1.1.2 // indirect
 	github.com/aliyun/credentials-go v1.1.2 // indirect
 	github.com/clbanning/mxj/v2 v2.5.6 // indirect
+	github.com/go-ole/go-ole v1.2.5 // indirect
+	github.com/jessevdk/go-flags v1.5.0 // indirect
+	github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect
 	github.com/tjfoc/gmsm v1.3.2 // indirect
 	golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect
 	google.golang.org/appengine v1.6.7 // indirect

+ 7 - 0
boxcost/go.sum

@@ -109,6 +109,8 @@ github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbt
 github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
 github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
 github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
+github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA=
+github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8=
 github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
 github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
 github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
@@ -491,6 +493,8 @@ github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbV
 github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
 github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI=
 github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
+github.com/go-ole/go-ole v1.2.5 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY=
+github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
 github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
 github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
 github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
@@ -786,6 +790,8 @@ github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0f
 github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
 github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
 github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
+github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LFvc=
+github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
 github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
 github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
 github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
@@ -817,6 +823,7 @@ github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+
 github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
 github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k=
 github.com/k0kubun/pp v2.3.0+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg=
+github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA=
 github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
 github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4=
 github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=