package api import ( "encoding/json" "github.com/kataras/iris/v12" "github.com/kataras/iris/v12/context" "go.uber.org/zap" "gorm.io/gorm/clause" "io" "pet-house.com/business/models" "pet-house.com/business/utils" "pet-house.com/core/server/database" "pet-house.com/core/server/web/web_iris" "pet-house.com/core/server/zap_server" "strconv" "strings" "sync" "time" ) type PetGoodsInfo struct { PetId int64 GoodsIds []int64 } type OrderCreateRequest struct { PetGoodsInfos []PetGoodsInfo //宠物商品信息列表 ServiceTime string //预约时间 yyyy-MM-dd HH:mm ServiceAddrId int64 //预约服务地址 ServiceAreaId int64 //服务地址ID } type OrderCreateResponse struct { OrderDetail OrderDetail `json:"orderDetail"` } var orderLock sync.Mutex // 创建 func (p DefParty) orderCreate() web_iris.Party { return web_iris.Party{Prefix: p.Prefix, PartyFunc: func(index iris.Party) { index.Post(OrderBase+"/orderCreate", func(ctx *context.Context) { headerBaseInfo := GetHeaderBaseInfo(ctx) body, _ := io.ReadAll(ctx.Request().Body) var orderCreateRequest OrderCreateRequest json.Unmarshal(body, &orderCreateRequest) var userServiceAddr models.UserServiceAddr database.Instance().Model(&models.UserServiceAddr{}).Where("uid = ? and id = ?", headerBaseInfo.Uid, orderCreateRequest.ServiceAddrId).Find(&userServiceAddr) if userServiceAddr.Id == 0 { UserServiceAddrNotExistError.Fail(ctx, nil) return } serviceAddr := ServiceAddrMap[orderCreateRequest.ServiceAreaId] if serviceAddr.Id == 0 { NotInServiceExistError.Fail(ctx, nil) return } /*if len(serviceAddr.ServiceArea) > 0 && serviceAddr.ServiceArea != userServiceAddr.AddrArea { ServiceAddrNotExistError.Fail(ctx, nil) return }*/ var projectionServiceTime = 0 for _, value := range orderCreateRequest.PetGoodsInfos { for _, value := range value.GoodsIds { goods := GoodsMap[value] if goods.Time != "/" && len(goods.Time) > 0 { goodsTime, _ := strconv.Atoi(goods.Time) projectionServiceTime += goodsTime } } } haveReserve := checkOrderServiceTime(orderCreateRequest.ServiceTime, projectionServiceTime) if haveReserve { OrderCreateError.DefFail(ctx, orderCreateRequest, "当前时间点不可预约") return } for _, value := range orderCreateRequest.PetGoodsInfos { userPet := GetUserPet(headerBaseInfo.Uid, value.PetId) if userPet.PetInfo.Vaccine == 0 { OrderCreateError.DefFail(ctx, orderCreateRequest, "当前订单存在未接种疫苗的宠物,无法下单") return } } var orderTotalAmount = 0 orderLock.Lock() orderId := NextId.Generate().String() orderMain := models.OrderMain{ OrderId: orderId, Uid: headerBaseInfo.Uid, OrderStatus: 1, ServiceTime: orderCreateRequest.ServiceTime, ServiceAddrId: orderCreateRequest.ServiceAddrId, ServiceRemark: "", } var projectionServiceTimeAll = 0 var orderSubList []models.OrderSub var orderSubDetailList []models.OrderDetail var mainGoods = 0 for _, value := range orderCreateRequest.PetGoodsInfos { subOrderId := NextId.Generate().String() var totalAmount int32 = 0 var projectionServiceTime = 0 for _, value := range value.GoodsIds { goods := GoodsMap[value] totalAmount = totalAmount + goods.Price orderSubDetailList = append(orderSubDetailList, models.OrderDetail{SubOrderId: subOrderId, GoodsId: value, Amount: goods.Price}) if goods.Time != "/" && len(goods.Time) > 0 { goodsTime, _ := strconv.Atoi(goods.Time) projectionServiceTime += goodsTime } if goods.GoodsType == 1 || goods.GoodsType == 2 { mainGoods++ } } projectionServiceTimeAll += projectionServiceTime orderSub := models.OrderSub{ OrderId: subOrderId, MainOrderId: orderMain.OrderId, OrderStatus: 1, PetId: value.PetId, PayType: 1, Discount: 100, TotalAmount: totalAmount, PayAmount: 0, ProjectionServiceTime: projectionServiceTime, } orderTotalAmount = orderTotalAmount + int(totalAmount) orderSubList = append(orderSubList, orderSub) } if mainGoods == 0 { OrderCreateError.DefFail(ctx, orderCreateRequest, "订单未选择主商品,创建失败") return } orderMain.ProjectionServiceTime = projectionServiceTimeAll var userInfo *models.User database.Instance().Model(&models.User{}).Where("id = ?", headerBaseInfo.Uid).Find(&userInfo) tx := database.Instance().Begin() if userInfo.Amount >= orderTotalAmount { orderMain.PayStatus = 1 userInfo.Amount = userInfo.Amount - orderTotalAmount updateValues := map[string]interface{}{ "Amount": userInfo.Amount, } tx.Model(&userInfo).Updates(&updateValues) } db := tx.Model(&models.OrderDetail{}).CreateInBatches(&orderSubDetailList, len(orderSubDetailList)) db1 := tx.Model(&models.OrderSub{}).CreateInBatches(&orderSubList, len(orderSubList)) db2 := tx.Model(&models.OrderMain{}).Create(&orderMain) if db.Error != nil || db1.Error != nil || db2.Error != nil { tx.Callback() orderLock.Unlock() zap_server.ZAPLOG.Error("订单插入失败", zap.Any("db", db.Error), zap.Any("db1", db1.Error), zap.Any("db2", db2.Error)) OrderCreateError.Fail(ctx, orderCreateRequest) return } tx.Commit() orderLock.Unlock() Success(ctx, orderCreateRequest, OrderCreateResponse{GetOrderDetail(orderMain.OrderId)}) }) }} } func GetOrderDetail(orderId string) OrderDetail { var orderMain models.OrderMain database.Instance().Model(&models.OrderMain{}).Where("order_id = ?", orderId).Find(&orderMain) var findUserServiceAddr models.UserServiceAddr database.Instance().Model(&models.UserServiceAddr{}).Where("id = ?", orderMain.ServiceAddrId).Find(&findUserServiceAddr) orderDetail := OrderDetail{ OrderId: orderMain.OrderId, Status: orderMain.OrderStatus, ServiceTime: orderMain.ServiceTime, ServiceAddr: findUserServiceAddr, ServiceRemark: orderMain.ServiceRemark, CreateTime: orderMain.CreateTime.Format("2006-01-02 15:04:05"), PayStatus: orderMain.PayStatus, } var subOrderList []SubOrder var orderSubList []models.OrderSub database.Instance().Model(&models.OrderSub{}).Where("main_order_id = ? ", orderMain.OrderId).Find(&orderSubList) for _, orderSub := range orderSubList { var orderDetailList []models.OrderDetail database.Instance().Model(&models.OrderDetail{}).Where("sub_order_id = ? ", orderSub.OrderId).Find(&orderDetailList) var goods []models.Goods for _, orderDetail := range orderDetailList { good := GoodsMap[orderDetail.GoodsId] goods = append(goods, good) } orderSub := SubOrder{ OrderId: orderSub.OrderId, Status: orderSub.OrderStatus, UserPetInfo: GetUserPet(orderMain.Uid, orderSub.PetId), TotalAmount: orderSub.TotalAmount, PayAmount: orderSub.PayAmount, Goods: goods, } subOrderList = append(subOrderList, orderSub) } var carOrder models.CarOrder database.Instance().Model(&models.CarOrder{}).Where("order_id = ?", orderMain.OrderId).Find(&carOrder) if carOrder.Id > 0 { car := CarMap[carOrder.CarId] orderDetail.ServiceCar = &car } else { orderDetail.ServiceCar = &models.ServiceCar{} } orderDetail.SubOrderList = subOrderList return orderDetail } func checkOrderServiceTime(serviceTime string, projectionServiceTime int) bool { type orderMainTmp struct { ServiceTime string ProjectionServiceTime int } var orderMainTmpList []orderMainTmp //服务时间>=预约时间 and 服务时间<=预计结束时间 时间范围内无订单才可预约 database.Instance().Model(&models.OrderMain{}).Where("service_time >= ? and service_time <= DATE_FORMAT(DATE_ADD('"+serviceTime+"', INTERVAL "+strconv.Itoa(projectionServiceTime)+" MINUTE), '%Y-%m-%d %H:%i')", serviceTime).Find(&orderMainTmpList) //时间点订单数量<服务车辆*车辆单次服务数量 才可接受预约 if len(orderMainTmpList) > 0 { return len(orderMainTmpList) >= len(CarMap)*CarServiceNum } return false } type OrderListRequest struct { Status int //订单状态 0所有 1待服务 2服务中 3已完成 4已取消 PageNo int //页码 PageSize int //数据数量 } type SubOrder struct { OrderId string `json:"orderId"` //子订单ID Status int `json:"status"` //子订单状态 0待派单 1待服务 2服务中 3已完成 4已取消 UserPetInfo UserPetInfo `json:"userPetInfo"` //用户宠物信息 TotalAmount int32 `json:"totalAmount"` //总金额 单位:分 PayAmount int32 `json:"payAmount"` //实际支付金额 单位:分 Goods []models.Goods `json:"goods"` //商品信息 } type OrderDetail struct { OrderId string `json:"orderId"` //主订单号 Status int `json:"status"` //主订单状态 0待派单 1待服务 2服务中 3已完成 4已取消 ServiceTime string `json:"serviceTime"` //服务时间 ServiceAddr models.UserServiceAddr `json:"serviceAddr"` //服务地址信息 ServiceRemark string `json:"serviceRemark"` //服务备注 CreateTime string `json:"createTime"` //创建时间 SubOrderList []SubOrder `json:"subOrderList"` //子订单列表 ServiceCar *models.ServiceCar `json:"serviceCar"` //服务车辆 只有主订单状态为 1 2 3的时候才会存在 PayStatus int `json:"payStatus"` //支付状态 0未支付 1已支付 } type OrderListResponse struct { OrderDetails []OrderDetail `json:"orderDetails"` //订单列表 PageNo int `json:"pageNo"` //页码 PageSize int `json:"pageSize"` //数据数量 } // 订单列表 func (p DefParty) orderList() web_iris.Party { return web_iris.Party{Prefix: p.Prefix, PartyFunc: func(index iris.Party) { index.Post(OrderBase+"/orderList", func(ctx *context.Context) { headerBaseInfo := GetHeaderBaseInfo(ctx) body, _ := io.ReadAll(ctx.Request().Body) var orderListRequest OrderListRequest json.Unmarshal(body, &orderListRequest) if orderListRequest.PageNo == 0 { ParamError.DefFail(ctx, orderListRequest, "页码必须>0") return } var orderList []models.OrderMain if orderListRequest.Status == 0 { database.Instance().Model(&models.OrderMain{}).Where("uid = ?", headerBaseInfo.Uid).Offset((orderListRequest.PageNo - 1) * orderListRequest.PageSize).Limit(orderListRequest.PageSize).Order(clause.OrderByColumn{Column: clause.Column{Name: "create_time"}, Desc: true}).Find(&orderList) } else { database.Instance().Model(&models.OrderMain{}).Where("uid = ? and status = ?", headerBaseInfo.Uid, orderListRequest.Status).Offset((orderListRequest.PageNo - 1) * orderListRequest.PageSize).Limit(orderListRequest.PageSize).Order(clause.OrderByColumn{Column: clause.Column{Name: "create_time"}, Desc: true}).Find(&orderList) } var orderDetails []OrderDetail for _, value := range orderList { var findUserServiceAddr models.UserServiceAddr database.Instance().Model(&models.UserServiceAddr{}).Where("id = ? and uid = ?", value.ServiceAddrId, headerBaseInfo.Uid).Find(&findUserServiceAddr) orderListResponse := OrderDetail{ OrderId: value.OrderId, Status: value.OrderStatus, ServiceTime: value.ServiceTime, ServiceAddr: findUserServiceAddr, ServiceRemark: value.ServiceRemark, CreateTime: value.CreateTime.Format("2006-01-02 15:04:05"), } var subOrderList []SubOrder var orderSubList []models.OrderSub database.Instance().Model(&models.OrderSub{}).Where("main_order_id = ? ", value.OrderId).Find(&orderSubList) for _, orderSub := range orderSubList { var orderDetailList []models.OrderDetail database.Instance().Model(&models.OrderDetail{}).Where("sub_order_id = ? ", orderSub.OrderId).Find(&orderDetailList) var goods []models.Goods for _, orderDetail := range orderDetailList { good := GoodsMap[orderDetail.GoodsId] goods = append(goods, good) } orderSub := SubOrder{ OrderId: orderSub.OrderId, Status: orderSub.OrderStatus, UserPetInfo: GetUserPet(headerBaseInfo.Uid, orderSub.PetId), TotalAmount: orderSub.TotalAmount, PayAmount: orderSub.PayAmount, Goods: goods, } subOrderList = append(subOrderList, orderSub) } orderListResponse.SubOrderList = subOrderList orderDetails = append(orderDetails, orderListResponse) } var orderListResponse = OrderListResponse{ OrderDetails: orderDetails, PageNo: orderListRequest.PageNo + 1, PageSize: orderListRequest.PageSize, } Success(ctx, orderListRequest, orderListResponse) }) }} } type TimeObject struct { Time string `json:"time"` Y bool `json:"y"` } type OrderServiceTimeRequest struct { ServiceAddrId int64 } type OrderServiceTimeResponse struct { Times map[string][]TimeObject `json:"times"` } // 获取订单可预约时间 func (p DefParty) orderServiceTime() web_iris.Party { return web_iris.Party{Prefix: p.Prefix, PartyFunc: func(index iris.Party) { index.Post(OrderBase+"/orderServiceTime", func(ctx *context.Context) { headerBaseInfo := GetHeaderBaseInfo(ctx) body, _ := io.ReadAll(ctx.Request().Body) var orderServiceTimeRequest OrderServiceTimeRequest json.Unmarshal(body, &orderServiceTimeRequest) if orderServiceTimeRequest.ServiceAddrId == 0 { ParamError.Fail(ctx, orderServiceTimeRequest) return } type orderMainTmp struct { ServiceTime string ProjectionServiceTime int } var orderMainTmpList []orderMainTmp database.Instance().Model(&models.OrderMain{}).Where("service_time >= DATE_SUB(NOW(), INTERVAL 7 DAY) and status != 3 and status != 5").Find(&orderMainTmpList) var orderTimeMap = make(map[string]string) var orderTimeNum = make(map[string]int) for _, value := range orderMainTmpList { orderTimeMap[value.ServiceTime] = value.ServiceTime orderTimeNum[value.ServiceTime] = orderTimeNum[value.ServiceTime] + 1 if value.ProjectionServiceTime > 0 { //计算出服务时间点之后累计延长时间 date, _ := time.Parse("2006-01-02 15:04", value.ServiceTime) lastDate := date.Add(time.Minute * time.Duration(value.ProjectionServiceTime)) for t := date; t.Before(lastDate); t = t.Add(time.Hour) { timeStr := t.Format("2006-01-02 15:04") orderTimeMap[timeStr] = timeStr orderTimeNum[timeStr] = orderTimeNum[timeStr] + 1 } } } serviceTime := AddrServiceMap[orderServiceTimeRequest.ServiceAddrId] times := strings.Split(serviceTime.Times, ",") carNum := len(CarMap) daysMap := utils.GetStrDays(7, 60, ReserveMap, orderTimeMap, carNum, orderTimeNum, CarServiceNum, times) var dayHoursMap = make(map[string][]TimeObject) for day, values := range daysMap { key := day for _, value := range values { if _, ok := dayHoursMap[key]; ok { dayHoursMap[key] = append(dayHoursMap[key], TimeObject{ Time: value.Time, Y: value.Y, }) } else { dayHoursMap[key] = []TimeObject{{ Time: value.Time, Y: value.Y, }} } } } Success(ctx, headerBaseInfo, OrderServiceTimeResponse{dayHoursMap}) }) }} } type OrderEditRequest struct { OrderId string //订单ID Status int //订单状态 4取消 } // 修改订单 func (p DefParty) orderEdit() web_iris.Party { return web_iris.Party{Prefix: p.Prefix, PartyFunc: func(index iris.Party) { index.Post(OrderBase+"/orderEdit", func(ctx *context.Context) { headerBaseInfo := GetHeaderBaseInfo(ctx) body, _ := io.ReadAll(ctx.Request().Body) var orderEditRequest OrderEditRequest json.Unmarshal(body, &orderEditRequest) var orderMain models.OrderMain var orderSub models.OrderSub database.Instance().Model(&models.OrderSub{}).Where("order_id = ?", orderEditRequest.OrderId).Find(&orderSub) database.Instance().Model(&models.OrderMain{}).Where("order_id = ?", orderSub.MainOrderId).Find(&orderMain) if orderMain.Uid != headerBaseInfo.Uid { OrderError.Fail(ctx, orderEditRequest) return } if orderSub.OrderStatus > 0 || orderEditRequest.Status == 0 { OrderError.Fail(ctx, orderEditRequest) return } orderSub.OrderStatus = orderEditRequest.Status updateValues := map[string]interface{}{ "OrderStatus": orderSub.OrderStatus, } database.Instance().Model(&orderSub).Updates(&updateValues) Success(ctx, orderEditRequest, GetOrderDetail(orderMain.OrderId)) }) }} } type OrderDetailRequest struct { OrderId string } type OrderDetailResponse struct { OrderDetail OrderDetail `json:"orderDetail"` } // 订单详情 func (p DefParty) orderDetail() web_iris.Party { return web_iris.Party{Prefix: p.Prefix, PartyFunc: func(index iris.Party) { index.Post(OrderBase+"/orderDetail", func(ctx *context.Context) { body, _ := io.ReadAll(ctx.Request().Body) var orderDetailRequest OrderDetailRequest json.Unmarshal(body, &orderDetailRequest) orderDetail := GetOrderDetail(orderDetailRequest.OrderId) if len(orderDetail.OrderId) == 0 { OrderExistError.Fail(ctx, orderDetailRequest) return } Success(ctx, orderDetailRequest, OrderDetailResponse{GetOrderDetail(orderDetailRequest.OrderId)}) }) }} } type OrderGoodsUpdateRequest struct { OrderId string GoodsIds []int64 } // 修改订单商品 func (p DefParty) orderGoodsUpdate() web_iris.Party { return web_iris.Party{Prefix: p.Prefix, PartyFunc: func(index iris.Party) { index.Post(OrderBase+"/orderGoodsUpdate", func(ctx *context.Context) { body, _ := io.ReadAll(ctx.Request().Body) var orderGoodsUpdateRequest OrderGoodsUpdateRequest json.Unmarshal(body, &orderGoodsUpdateRequest) var orderSub models.OrderSub database.Instance().Model(&models.OrderSub{}).Where("order_id = ?", orderGoodsUpdateRequest.OrderId).Find(&orderSub) if len(orderSub.OrderId) == 0 { OrderExistError.Fail(ctx, orderGoodsUpdateRequest) return } database.Instance().Where("sub_order_id = ?", orderGoodsUpdateRequest.OrderId).Delete(&models.OrderDetail{}) var totalAmount int32 = 0 var projectionServiceTime = 0 for _, value := range orderGoodsUpdateRequest.GoodsIds { goods := GoodsMap[value] totalAmount = totalAmount + goods.Price if goods.Time != "/" && len(goods.Time) > 0 { goodsTime, _ := strconv.Atoi(goods.Time) projectionServiceTime += goodsTime } orderDetail := models.OrderDetail{ SubOrderId: orderGoodsUpdateRequest.OrderId, GoodsId: goods.Id, Amount: goods.Price, } database.Instance().Model(&models.OrderDetail{}).Create(&orderDetail) } orderSub.TotalAmount = totalAmount orderSub.ProjectionServiceTime = projectionServiceTime updateValues := map[string]interface{}{ "TotalAmount": orderSub.TotalAmount, "ProjectionServiceTime": orderSub.ProjectionServiceTime, } database.Instance().Model(&orderSub).Updates(&updateValues) Success(ctx, orderGoodsUpdateRequest, OrderDetailResponse{GetOrderDetail(orderSub.MainOrderId)}) }) }} } // 支付 func (p DefParty) orderPay() web_iris.Party { return web_iris.Party{Prefix: p.Prefix, PartyFunc: func(index iris.Party) { index.Post(OrderBase+"/orderPay", func(ctx *context.Context) { }) }} }