stages.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594
  1. package api
  2. import (
  3. "box-cost/db/model"
  4. "box-cost/db/repo"
  5. "encoding/json"
  6. "errors"
  7. "fmt"
  8. "time"
  9. "github.com/gin-gonic/gin"
  10. "go.mongodb.org/mongo-driver/bson"
  11. "go.mongodb.org/mongo-driver/bson/primitive"
  12. "go.mongodb.org/mongo-driver/mongo"
  13. "go.mongodb.org/mongo-driver/mongo/options"
  14. )
  15. // StageRequest 用于添加和更新stage的请求结构
  16. type StageRequest struct {
  17. PlanId string `json:"planId"`
  18. // PackId string `json:"packId"`
  19. CompId string `json:"componentId"`
  20. StageId string `json:"stageId,omitempty"`
  21. Stages []*model.ComponentStage `json:"stages"`
  22. }
  23. // 更新计划和部件的价格
  24. func updatePrices(c *gin.Context, db *mongo.Collection, planId primitive.ObjectID, compId string) error {
  25. // 1. 获取计划详情
  26. var plan model.ProductPlan
  27. err := db.FindOne(c.Request.Context(), bson.M{"_id": planId}).Decode(&plan)
  28. if err != nil {
  29. return err
  30. }
  31. // 2. 遍历所有组件,找到目标组件并重新计算价格
  32. var totalPrice float64 = 0
  33. for _, comp := range plan.Pack.Components {
  34. compPrice := 0.0
  35. // 计算组件的所有stage总价
  36. for _, stage := range comp.Stages {
  37. stagePrice := stage.OrderPrice * float64(stage.OrderCount)
  38. compPrice += stagePrice
  39. }
  40. // 更新组件总价
  41. if comp.Id == compId {
  42. _, err = db.UpdateOne(c.Request.Context(),
  43. bson.M{"_id": planId, "pack.components.id": compId},
  44. bson.M{"$set": bson.M{"pack.components.$.totalPrice": compPrice}})
  45. if err != nil {
  46. return err
  47. }
  48. }
  49. totalPrice += compPrice
  50. }
  51. // 3. 更新计划总价
  52. _, err = db.UpdateOne(c.Request.Context(),
  53. bson.M{"_id": planId},
  54. bson.M{"$set": bson.M{"totalPrice": &totalPrice}})
  55. return err
  56. }
  57. // 增加stage
  58. // 根据planId packId compentId定位到到计划中的compent
  59. // 注意更新计划价格 组件价格
  60. func AddStage(c *gin.Context, apictx *ApiSession) (interface{}, error) {
  61. var req StageRequest
  62. if err := c.ShouldBindJSON(&req); err != nil {
  63. return nil, err
  64. }
  65. planId, err := primitive.ObjectIDFromHex(req.PlanId)
  66. if err != nil {
  67. return nil, err
  68. }
  69. // 更新数据库,添加多个stage
  70. update := bson.M{
  71. "$push": bson.M{
  72. "pack.components.$[components].stages": bson.M{
  73. "$each": req.Stages,
  74. },
  75. },
  76. "$set": bson.M{
  77. "updateTime": time.Now(),
  78. },
  79. }
  80. arrayFilters := []interface{}{
  81. bson.M{"components.id": req.CompId},
  82. }
  83. opts := options.Update().SetArrayFilters(options.ArrayFilters{
  84. Filters: arrayFilters,
  85. })
  86. db := apictx.Svc.Mongo.GetCollection(repo.CollectionProductPlan)
  87. result, err := db.UpdateOne(c.Request.Context(),
  88. bson.M{"_id": planId},
  89. update,
  90. opts,
  91. )
  92. if err != nil {
  93. return nil, err
  94. }
  95. // 更新价格
  96. err = updatePrices(c, db, planId, req.CompId)
  97. if err != nil {
  98. return nil, err
  99. }
  100. _, err = AddPlanHistory(c, apictx, req.PlanId)
  101. if err != nil {
  102. fmt.Println("记录历史失败:", err)
  103. }
  104. return result, nil
  105. }
  106. // 删除stage
  107. // 根据planId packId compentId stageId定位到到计划中的stage
  108. // 注意更新计划价格 组件价格
  109. func DeleteStage(c *gin.Context, apictx *ApiSession) (interface{}, error) {
  110. var req StageRequest
  111. if err := c.ShouldBindJSON(&req); err != nil {
  112. return nil, err
  113. }
  114. planId, err := primitive.ObjectIDFromHex(req.PlanId)
  115. if err != nil {
  116. return nil, err
  117. }
  118. if req.StageId == "" || req.CompId == "" {
  119. return nil, errors.New("stageId and compId are required")
  120. }
  121. // 更新数据库
  122. db := apictx.Svc.Mongo.GetCollection(repo.CollectionProductPlan)
  123. update := bson.M{
  124. "$pull": bson.M{
  125. "pack.components.$[components].stages": bson.M{
  126. "id": req.StageId,
  127. },
  128. },
  129. }
  130. arrayFilters := []interface{}{
  131. bson.M{"components.id": req.CompId},
  132. }
  133. opts := options.Update().SetArrayFilters(options.ArrayFilters{
  134. Filters: arrayFilters,
  135. })
  136. result, err := db.UpdateOne(c.Request.Context(),
  137. bson.M{"_id": planId},
  138. update,
  139. opts,
  140. )
  141. if err != nil {
  142. return nil, err
  143. }
  144. // 更新价格
  145. err = updatePrices(c, db, planId, req.CompId)
  146. if err != nil {
  147. return nil, err
  148. }
  149. _, err = AddPlanHistory(c, apictx, req.PlanId)
  150. if err != nil {
  151. fmt.Println("记录历史失败:", err)
  152. }
  153. return result, nil
  154. }
  155. // 根据planId packId compentId stageId定位到到计划中的stage
  156. // 注意更新计划价格 组件价格
  157. // 根据planId packId compentId stageId定位到到计划中的stage
  158. // 注意更新计划价格 组件价格
  159. func updateStage(c *gin.Context, db *mongo.Collection, req *StageRequest) (interface{}, error) {
  160. if len(req.PlanId) == 0 || len(req.CompId) == 0 || len(req.StageId) == 0 {
  161. return nil, fmt.Errorf("planId, componentId and stageId are required")
  162. }
  163. planId, err := primitive.ObjectIDFromHex(req.PlanId)
  164. if err != nil {
  165. return nil, fmt.Errorf("invalid planId: %v", err)
  166. }
  167. // 构建更新
  168. update := bson.M{
  169. "$set": bson.M{
  170. "pack.components.$[components].stages.$[stage]": req.Stages[0],
  171. "updateTime": time.Now(),
  172. },
  173. }
  174. // 设置数组过滤器
  175. arrayFilters := options.ArrayFilters{
  176. Filters: []interface{}{
  177. bson.M{"components.id": req.CompId},
  178. bson.M{"stage.id": req.StageId},
  179. },
  180. }
  181. // 执行更新
  182. opts := options.FindOneAndUpdate().
  183. SetArrayFilters(arrayFilters).
  184. SetReturnDocument(options.After)
  185. result := db.FindOneAndUpdate(
  186. c.Request.Context(),
  187. bson.M{"_id": planId},
  188. update,
  189. opts,
  190. )
  191. if result.Err() != nil {
  192. if result.Err() == mongo.ErrNoDocuments {
  193. return nil, fmt.Errorf("plan or stage not found")
  194. }
  195. return nil, fmt.Errorf("failed to update stage: %v", result.Err())
  196. }
  197. // 更新价格
  198. err = updatePrices(c, db, planId, req.CompId)
  199. if err != nil {
  200. return nil, fmt.Errorf("failed to update prices: %v", err)
  201. }
  202. return true, nil
  203. }
  204. // 更新stages
  205. // 根据planId packId compentId stageId定位到到计划中的stage
  206. // 注意更新计划价格 组件价格
  207. func UpdateStages(c *gin.Context, apictx *ApiSession) (interface{}, error) {
  208. var req StageRequest
  209. if err := c.ShouldBindJSON(&req); err != nil {
  210. return nil, err
  211. }
  212. // 参数检查
  213. if len(req.PlanId) == 0 || len(req.CompId) == 0 || len(req.Stages) == 0 {
  214. return nil, fmt.Errorf("invalid parameters: planId, componentId and stages are required")
  215. }
  216. planId, err := primitive.ObjectIDFromHex(req.PlanId)
  217. if err != nil {
  218. return nil, fmt.Errorf("invalid planId: %v", err)
  219. }
  220. // 获取更新前的数据用于验证
  221. db := apictx.Svc.Mongo.GetCollection(repo.CollectionProductPlan)
  222. ctx := c.Request.Context()
  223. var plan model.ProductPlan
  224. err = db.FindOne(ctx, bson.M{"_id": planId}).Decode(&plan)
  225. if err != nil {
  226. return nil, fmt.Errorf("failed to find plan: %v", err)
  227. }
  228. // 验证组件是否存在
  229. componentExists := false
  230. if plan.Pack != nil {
  231. for _, comp := range plan.Pack.Components {
  232. if comp.Id == req.CompId {
  233. componentExists = true
  234. break
  235. }
  236. }
  237. }
  238. if !componentExists {
  239. return nil, fmt.Errorf("component %s not found in plan", req.CompId)
  240. }
  241. // 准备批量更新操作
  242. var models []mongo.WriteModel
  243. now := time.Now()
  244. for _, stage := range req.Stages {
  245. if stage.Id == "" {
  246. continue // 跳过没有ID的stage
  247. }
  248. update := bson.M{
  249. "$set": bson.M{
  250. "pack.components.$[components].stages.$[stage]": stage,
  251. "updateTime": now,
  252. },
  253. }
  254. arrayFilters := options.ArrayFilters{
  255. Filters: []interface{}{
  256. bson.M{"components.id": req.CompId},
  257. bson.M{"stage.id": stage.Id},
  258. },
  259. }
  260. model := mongo.NewUpdateOneModel().
  261. SetFilter(bson.M{"_id": planId}).
  262. SetUpdate(update).
  263. SetArrayFilters(arrayFilters)
  264. models = append(models, model)
  265. }
  266. if len(models) == 0 {
  267. return nil, fmt.Errorf("no valid stages to update")
  268. }
  269. // 执行批量更新
  270. opts := options.BulkWrite().SetOrdered(false) // 允许并行执行
  271. result, err := db.BulkWrite(c.Request.Context(), models, opts)
  272. if err != nil {
  273. return nil, fmt.Errorf("failed to update stages: %v", err)
  274. }
  275. // 更新价格
  276. err = updatePrices(c, db, planId, req.CompId)
  277. if err != nil {
  278. return nil, fmt.Errorf("failed to update prices: %v", err)
  279. }
  280. // 记录历史
  281. _, err = AddPlanHistory(c, apictx, req.PlanId)
  282. if err != nil {
  283. fmt.Println("记录历史失败:", err)
  284. }
  285. return result, nil
  286. }
  287. type UpdatePlanFieldsReq struct {
  288. PlanId string `json:"planId"`
  289. Total int `json:"total,omitempty"`
  290. CreateUser string `json:"createUser,omitempty"`
  291. Name string `json:"name,omitempty"`
  292. }
  293. // 单独更新计划字段
  294. func UpdatePlanfileds(c *gin.Context, apictx *ApiSession) (interface{}, error) {
  295. var req UpdatePlanFieldsReq
  296. if err := c.ShouldBindJSON(&req); err != nil {
  297. return nil, err
  298. }
  299. planId, _ := primitive.ObjectIDFromHex(req.PlanId)
  300. if planId.IsZero() {
  301. return nil, fmt.Errorf("invalid planId")
  302. }
  303. result, err := repo.RepoUpdateSetDoc(apictx.CreateRepoCtx(), repo.CollectionProductPlan, req.PlanId, &model.ProductPlan{
  304. Total: req.Total,
  305. CreateUser: req.CreateUser,
  306. Name: req.Name,
  307. UpdateTime: time.Now(),
  308. })
  309. if err != nil {
  310. return nil, err
  311. }
  312. // 记录历史
  313. _, err = AddPlanHistory(c, apictx, req.PlanId)
  314. if err != nil {
  315. fmt.Println("记录历史失败:", err)
  316. }
  317. return result, nil
  318. }
  319. // 新增组件
  320. func AddCompnonet(c *gin.Context, apictx *ApiSession) (interface{}, error) {
  321. _planId := c.Param("id")
  322. planId, _ := primitive.ObjectIDFromHex(_planId)
  323. if planId.IsZero() {
  324. return nil, fmt.Errorf("planId is invalid")
  325. }
  326. var component model.PackComponent
  327. if err := c.ShouldBindJSON(&component); err != nil {
  328. return nil, err
  329. }
  330. // 计算新的总价
  331. newTotalPrice := component.TotalPrice
  332. oldPlan := &model.ProductPlan{}
  333. db := apictx.Svc.Mongo.GetCollection(repo.CollectionProductPlan)
  334. if err := db.FindOne(c.Request.Context(), bson.M{"_id": planId}).Decode(&oldPlan); err != nil {
  335. return nil, err
  336. } else if oldPlan.TotalPrice != nil {
  337. newTotalPrice += *oldPlan.TotalPrice
  338. }
  339. update := bson.M{
  340. "$push": bson.M{
  341. "pack.components": component,
  342. },
  343. "$set": bson.M{
  344. "updateTime": time.Now(),
  345. "totalPrice": newTotalPrice,
  346. },
  347. "$inc": bson.M{
  348. "pack.compCounts": 1,
  349. },
  350. }
  351. filter := bson.M{"_id": planId}
  352. result := db.FindOneAndUpdate(c.Request.Context(), filter, update)
  353. if result.Err() != nil {
  354. return nil, result.Err()
  355. }
  356. _, err := AddPlanHistory(c, apictx, _planId)
  357. if err != nil {
  358. fmt.Println("记录历史失败:", err)
  359. }
  360. return true, nil
  361. }
  362. func DeleteCompnonet(c *gin.Context, apictx *ApiSession) (interface{}, error) {
  363. _planId := c.Param("id")
  364. planId, _ := primitive.ObjectIDFromHex(_planId)
  365. if planId.IsZero() {
  366. return nil, fmt.Errorf("planId is invalid")
  367. }
  368. componentId := c.Param("componentId")
  369. if len(componentId) == 0 {
  370. return nil, fmt.Errorf("componentId is invalid")
  371. }
  372. oldPlan := &model.ProductPlan{}
  373. // Find the component to be deleted and its price
  374. db := apictx.Svc.Mongo.GetCollection(repo.CollectionProductPlan)
  375. var componentPrice float64
  376. if err := db.FindOne(c.Request.Context(), bson.M{"_id": planId}).Decode(&oldPlan); err != nil {
  377. return nil, err
  378. } else if oldPlan.Pack != nil {
  379. for _, comp := range oldPlan.Pack.Components {
  380. if comp.Id == componentId {
  381. componentPrice = comp.TotalPrice
  382. break
  383. }
  384. }
  385. }
  386. var newTotalPrice float64
  387. if oldPlan.TotalPrice != nil {
  388. newTotalPrice = *oldPlan.TotalPrice - componentPrice
  389. if newTotalPrice < 0 {
  390. newTotalPrice = 0
  391. }
  392. }
  393. update := bson.M{
  394. "$pull": bson.M{
  395. "pack.components": bson.M{"id": componentId},
  396. },
  397. "$set": bson.M{
  398. "updateTime": time.Now(),
  399. "totalPrice": newTotalPrice,
  400. },
  401. "$inc": bson.M{
  402. "pack.compCounts": -1,
  403. },
  404. }
  405. filter := bson.M{"_id": planId}
  406. result := db.FindOneAndUpdate(c.Request.Context(), filter, update)
  407. if result.Err() != nil {
  408. return nil, result.Err()
  409. }
  410. _, err := AddPlanHistory(c, apictx, _planId)
  411. if err != nil {
  412. fmt.Println("记录历史失败:", err)
  413. }
  414. return true, nil
  415. }
  416. type UpdateCompnonetReq struct {
  417. Name string `json:"name,omitempty"`
  418. Stages []*model.ComponentStage `json:"stages,omitempty"`
  419. }
  420. func UpdateCompnonet(c *gin.Context, apictx *ApiSession) (interface{}, error) {
  421. _planId := c.Param("id")
  422. planId, _ := primitive.ObjectIDFromHex(_planId)
  423. if planId.IsZero() {
  424. return nil, fmt.Errorf("planId is invalid")
  425. }
  426. componentId := c.Param("componentId")
  427. if len(componentId) == 0 {
  428. return nil, fmt.Errorf("componentId is invalid")
  429. }
  430. var nameUpdate UpdateCompnonetReq
  431. if err := c.ShouldBindJSON(&nameUpdate); err != nil {
  432. return nil, err
  433. }
  434. // 检查是否有需要更新的字段
  435. if len(nameUpdate.Name) == 0 && len(nameUpdate.Stages) == 0 {
  436. return nil, fmt.Errorf("no fields to update")
  437. }
  438. db := apictx.Svc.Mongo.GetCollection(repo.CollectionProductPlan)
  439. update := bson.M{
  440. "$set": bson.M{
  441. "updateTime": time.Now(),
  442. },
  443. }
  444. if len(nameUpdate.Name) > 0 {
  445. update["$set"].(bson.M)["pack.components.$.name"] = nameUpdate.Name
  446. }
  447. if len(nameUpdate.Stages) > 0 {
  448. update["$set"].(bson.M)["pack.components.$.stages"] = nameUpdate.Stages
  449. }
  450. filter := bson.M{
  451. "_id": planId,
  452. "pack.components.id": componentId,
  453. }
  454. result := db.FindOneAndUpdate(c.Request.Context(), filter, update)
  455. if result.Err() != nil {
  456. return nil, result.Err()
  457. }
  458. // 如果更新了stages,需要更新价格
  459. if len(nameUpdate.Stages) > 0 {
  460. err := updatePrices(c, db, planId, componentId)
  461. if err != nil {
  462. return nil, fmt.Errorf("failed to update prices: %v", err)
  463. }
  464. }
  465. // 记录历史
  466. _, err := AddPlanHistory(c, apictx, _planId)
  467. if err != nil {
  468. fmt.Println("记录历史失败:", err)
  469. }
  470. return true, nil
  471. }
  472. // 记录历史
  473. func AddPlanHistory(c *gin.Context, apictx *ApiSession, planId string, ht ...string) (interface{}, error) {
  474. htype := "update"
  475. if len(ht) > 0 {
  476. htype = ht[0]
  477. }
  478. // 获取更新后的数据并记录历史
  479. newPlan := &model.ProductPlan{}
  480. found, err := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
  481. CollectName: repo.CollectionProductPlan,
  482. Query: repo.Map{"_id": planId},
  483. }, newPlan)
  484. if !found {
  485. return false, errors.New("数据未找到")
  486. }
  487. if err != nil {
  488. return false, err
  489. }
  490. newPlanBytes, _ := json.Marshal(newPlan)
  491. userId, _ := primitive.ObjectIDFromHex(apictx.User.ID)
  492. userInfo, err := getUserById(apictx, userId)
  493. if err != nil {
  494. return false, err
  495. }
  496. history := &model.History{
  497. Userinfo: userInfo,
  498. TargetId: planId,
  499. Path: c.Request.URL.Path,
  500. Collection: repo.CollectionProductPlan,
  501. Type: htype,
  502. Content: string(newPlanBytes),
  503. CreateTime: time.Now(),
  504. }
  505. _, err = repo.RepoAddDoc(apictx.CreateRepoCtx(), repo.CollectionPlanHistory, history)
  506. if err != nil {
  507. return false, err
  508. }
  509. return true, nil
  510. }