diff --git a/business/api/common.go b/business/api/common.go index 4ece83b..15c6f78 100644 --- a/business/api/common.go +++ b/business/api/common.go @@ -47,7 +47,7 @@ var ( PetNotExistError = Error{Code: 206, Msg: "用户宠物不存在"} PetBaseNotExistError = Error{Code: 207, Msg: "宠物基础信息不存在"} UserServiceAddrNotExistError = Error{Code: 207, Msg: "用户服务地址不存在"} - ServiceAddrNotExistError = Error{Code: 208, Msg: "服务地址区域,请重新配置地址"} + ServiceAddrNotExistError = Error{Code: 208, Msg: "不在服务地址区域,请重新配置地址"} NotInServiceExistError = Error{Code: 209, Msg: "不在服务范围"} OrderCreateError = Error{Code: 210, Msg: "订单创建失败"} LoginError = Error{Code: 211, Msg: "授权登录失败"} @@ -121,6 +121,7 @@ var PetGoodsMap map[string][]models.PetGoods var GoodsMap map[int64]models.Goods var CarMap map[int]models.ServiceCar var ReserveMap map[string]models.ReserveTimeFilter +var AddrServiceMap map[int64]models.AddrServiceTime func DataInit() { //--------------------------------------------------宠物基础信息数据--------------------------------------------------------- @@ -173,6 +174,13 @@ func DataInit() { ReserveMap[value.Content] = value } zap_server.ZAPLOG.Info("dataInit ReserveMap : ", zap.Any("ReserveMap", CarMap)) + var serviceTimesList []models.AddrServiceTime + database.Instance().Model(&models.AddrServiceTime{}).Find(&serviceTimesList) + AddrServiceMap = make(map[int64]models.AddrServiceTime) + for _, value := range serviceTimesList { + AddrServiceMap[value.ServiceAddrId] = value + } + zap_server.ZAPLOG.Info("dataInit AddrServiceMap : ", zap.Any("AddrServiceMap", CarMap)) } func DataCacheJob() { diff --git a/business/api/init.go b/business/api/init.go index 9b2f125..e3209ba 100644 --- a/business/api/init.go +++ b/business/api/init.go @@ -44,6 +44,7 @@ func ModuleInit() { &models.ServiceUserMarkRecord{}, &models.ReserveTimeFilter{}, &models.OrderServiceRecord{}, + &models.AddrServiceTime{}, ) DataInit() DataCacheJob() diff --git a/business/api/order.go b/business/api/order.go index 6c85b28..ddd2c7b 100644 --- a/business/api/order.go +++ b/business/api/order.go @@ -13,7 +13,9 @@ import ( "pet-house.com/core/server/web/web_iris" "pet-house.com/core/server/zap_server" "strconv" + "strings" "sync" + "time" ) type PetGoodsInfo struct { @@ -25,6 +27,7 @@ type OrderCreateRequest struct { PetGoodsInfos []PetGoodsInfo //宠物商品信息列表 ServiceTime string //预约时间 yyyy-MM-dd HH:mm ServiceAddrId int64 //预约服务地址 + ServiceAreaId int64 //服务地址ID } type OrderCreateResponse struct { @@ -47,12 +50,26 @@ func (p DefParty) orderCreate() web_iris.Party { UserServiceAddrNotExistError.Fail(ctx, nil) return } - serviceAddr := ServiceAddrMap[userServiceAddr.AreaId] + serviceAddr := ServiceAddrMap[orderCreateRequest.ServiceAreaId] if serviceAddr.Id == 0 { - ServiceAddrNotExistError.Fail(ctx, nil) + NotInServiceExistError.Fail(ctx, nil) return } - haveReserve := checkOrderServiceTime(orderCreateRequest.ServiceTime) + /*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 @@ -78,6 +95,7 @@ func (p DefParty) orderCreate() web_iris.Party { 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 @@ -90,6 +108,9 @@ func (p DefParty) orderCreate() web_iris.Party { goodsTime, _ := strconv.Atoi(goods.Time) projectionServiceTime += goodsTime } + if goods.GoodsType == 1 || goods.GoodsType == 2 { + mainGoods++ + } } projectionServiceTimeAll += projectionServiceTime orderSub := models.OrderSub{ @@ -106,6 +127,11 @@ func (p DefParty) orderCreate() web_iris.Party { 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() @@ -181,12 +207,15 @@ func GetOrderDetail(orderId string) OrderDetail { return orderDetail } -func checkOrderServiceTime(serviceTime string) bool { +func checkOrderServiceTime(serviceTime string, projectionServiceTime int) bool { type orderMainTmp struct { - ServiceTime string + ServiceTime string + ProjectionServiceTime int } + var orderMainTmpList []orderMainTmp - database.Instance().Model(&models.OrderMain{}).Where("service_time = ?", serviceTime).Find(&orderMainTmpList) + //服务时间>=预约时间 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 @@ -296,6 +325,10 @@ type TimeObject struct { Y bool `json:"y"` } +type OrderServiceTimeRequest struct { + ServiceAddrId int64 +} + type OrderServiceTimeResponse struct { Times map[string][]TimeObject `json:"times"` } @@ -305,8 +338,16 @@ 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 + 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) @@ -315,9 +356,21 @@ func (p DefParty) orderServiceTime() web_iris.Party { 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) + daysMap := utils.GetStrDays(7, 60, ReserveMap, orderTimeMap, carNum, orderTimeNum, CarServiceNum, times) var dayHoursMap = make(map[string][]TimeObject) for day, values := range daysMap { key := day @@ -334,7 +387,6 @@ func (p DefParty) orderServiceTime() web_iris.Party { }} } } - } Success(ctx, headerBaseInfo, OrderServiceTimeResponse{dayHoursMap}) }) diff --git a/business/api/service.go b/business/api/service.go index b2d6378..6ae5d13 100644 --- a/business/api/service.go +++ b/business/api/service.go @@ -4,15 +4,11 @@ import ( "encoding/json" "github.com/kataras/iris/v12" "github.com/kataras/iris/v12/context" - geo "github.com/kellydunn/golang-geo" - "go.uber.org/zap" "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 "strconv" ) type ServiceAddrListResponse struct { @@ -56,18 +52,18 @@ func (p DefParty) serviceAddOrEdit() web_iris.Party { body, _ := io.ReadAll(ctx.Request().Body) var serviceAddOrEditRequest ServiceAddOrEditRequest json.Unmarshal(body, &serviceAddOrEditRequest) - var serviceAddr models.ServiceAddr + /*var serviceAddr models.ServiceAddr serviceAddr = ServiceAddrMap[serviceAddOrEditRequest.AreaId] if serviceAddr.Id == 0 { ServiceAddrNotExistError.Fail(ctx, serviceAddOrEditRequest) return - } - database.Instance().Model(&models.ServiceAddr{}).Where("id = ?", serviceAddOrEditRequest.AreaId).Find(&serviceAddr) + }*/ + //database.Instance().Model(&models.ServiceAddr{}).Where("id = ?", serviceAddOrEditRequest.AreaId).Find(&serviceAddr) userServiceAddr := models.UserServiceAddr{ - Uid: headerBaseInfo.Uid, - Name: serviceAddOrEditRequest.Name, - Mobile: serviceAddOrEditRequest.Mobile, - AreaId: serviceAddr.Id, + Uid: headerBaseInfo.Uid, + Name: serviceAddOrEditRequest.Name, + Mobile: serviceAddOrEditRequest.Mobile, + //AreaId: serviceAddr.Id, Addr: serviceAddOrEditRequest.Addr, Longitude: serviceAddOrEditRequest.Longitude, Latitude: serviceAddOrEditRequest.Latitude, @@ -77,21 +73,33 @@ func (p DefParty) serviceAddOrEdit() web_iris.Party { userServiceAddr.Longitude = addrGeocoding.Longitude userServiceAddr.Latitude = addrGeocoding.Latitude } - serviceAddrLat, _ := strconv.ParseFloat(serviceAddr.Latitude, 64) - serviceAddrLng, _ := strconv.ParseFloat(serviceAddr.Longitude, 64) - servicePoint := geo.NewPoint(serviceAddrLat, serviceAddrLng) + //最远服务距离>0 才计算 + /*if serviceAddr.DistantGapMeters > 0 { + serviceAddrLat, _ := strconv.ParseFloat(serviceAddr.Latitude, 64) + serviceAddrLng, _ := strconv.ParseFloat(serviceAddr.Longitude, 64) + servicePoint := geo.NewPoint(serviceAddrLat, serviceAddrLng) - userAddrLat, _ := strconv.ParseFloat(userServiceAddr.Latitude, 64) - userAddrLng, _ := strconv.ParseFloat(userServiceAddr.Longitude, 64) - userPoint := geo.NewPoint(userAddrLat, userAddrLng) - // 计算两个点之间的距离(单位:米) - distance := servicePoint.GreatCircleDistance(userPoint) * 1000 - zap_server.ZAPLOG.Info("服务距离", zap.Any("distance", distance), zap.Any("服务经纬度", servicePoint), zap.Any("用户经纬度", userPoint)) - //超出服务范围 - if distance >= serviceAddr.DistantGapMeters { - NotInServiceExistError.Fail(ctx, serviceAddOrEditRequest) - return - } + userAddrLat, _ := strconv.ParseFloat(userServiceAddr.Latitude, 64) + userAddrLng, _ := strconv.ParseFloat(userServiceAddr.Longitude, 64) + userPoint := geo.NewPoint(userAddrLat, userAddrLng) + // 计算两个点之间的距离(单位:米) + distance := servicePoint.GreatCircleDistance(userPoint) * 1000 + zap_server.ZAPLOG.Info("服务距离", zap.Any("distance", distance), zap.Any("服务经纬度", servicePoint), zap.Any("用户经纬度", userPoint)) + //超出服务范围 + if distance >= serviceAddr.DistantGapMeters { + NotInServiceExistError.Fail(ctx, serviceAddOrEditRequest) + return + } + }*/ + /*recogResponse := utils.GetAddrRecog(utils.BmapRecogRequest{Address: userServiceAddr.Addr}) + if len(serviceAddr.ServiceAreaId) > 0 { + zap_server.ZAPLOG.Info("serviceArea", zap.Any("response", recogResponse), zap.Any("serviceAddr", serviceAddr.ServiceAreaId)) + if recogResponse.County != serviceAddr.ServiceAreaId { + NotInServiceExistError.Fail(ctx, serviceAddOrEditRequest) + return + } + }*/ + //userServiceAddr.AddrArea = recogResponse.County if serviceAddOrEditRequest.Id == 0 { database.Instance().Model(&models.UserServiceAddr{}).Create(&userServiceAddr) } else { @@ -103,10 +111,10 @@ func (p DefParty) serviceAddOrEdit() web_iris.Party { return } updateValues := map[string]interface{}{ - "Uid": headerBaseInfo.Uid, - "Name": serviceAddOrEditRequest.Name, - "Mobile": serviceAddOrEditRequest.Mobile, - "AreaId": serviceAddr.Id, + "Uid": headerBaseInfo.Uid, + "Name": serviceAddOrEditRequest.Name, + "Mobile": serviceAddOrEditRequest.Mobile, + //"AreaId": serviceAddr.Id, "Addr": serviceAddOrEditRequest.Addr, "Longitude": serviceAddOrEditRequest.Longitude, "Latitude": serviceAddOrEditRequest.Latitude, diff --git a/business/models/dataModel.go b/business/models/dataModel.go index 81ba9fa..61520ef 100644 --- a/business/models/dataModel.go +++ b/business/models/dataModel.go @@ -58,6 +58,7 @@ type ServiceAddr struct { Latitude string `json:"latitude"` //纬度 Addr string `gorm:"not null" json:"addr"` //详细地址 DistantGapMeters float64 `gorm:"not null" json:"distantGapMeters"` //服务最远距离 单位/米 + ServiceArea string `json:"serviceArea"` //服务区域 } // UserServiceAddr 用户服务地址 @@ -66,11 +67,12 @@ type UserServiceAddr struct { Uid int64 `gorm:"index;not null" json:"uid"` //用户ID Name string `gorm:"not null" json:"name"` //名称 Mobile string `gorm:"not null" json:"mobile"` //手机号 - AreaId int64 `gorm:"not null" json:"areaId"` //服务区域ID + AreaId int64 `json:"areaId"` //服务区域ID Addr string `gorm:"not null" json:"addr"` //详细地址 Longitude string `json:"longitude"` //经度 Latitude string `json:"latitude"` //纬度 Area string `gorm:"-" json:"area"` //服务区域 + AddrArea string `json:"addrArea"` //地址区域 CreateTime time.Time `gorm:"type:timestamp;default:CURRENT_TIMESTAMP;not null" json:"-"` //创建时间 } @@ -222,3 +224,10 @@ type ReserveTimeFilter struct { Type int `gorm:"not null" json:"type"` //类型 1日期 2小时 Content string `gorm:"not null" json:"content"` //类型 具体内容 比如:2024-04-08 12 } + +// AddrServiceTime 服务地址时间排期 +type AddrServiceTime struct { + Id int `gorm:"primaryKey;autoIncrement" json:"id"` //id + ServiceAddrId int64 `gorm:"index;not null" json:"ServiceAddrId"` //服务地址ID + Times string `gorm:"index;not null" json:"times"` //时间列表 +} diff --git a/business/utils/bmap.go b/business/utils/bmap.go index 3309d49..6ebf295 100644 --- a/business/utils/bmap.go +++ b/business/utils/bmap.go @@ -10,7 +10,8 @@ import ( var ak = "ji0v0JfOZDlXHmICe93NFMEN67AxMSxN" var host = "https://api.map.baidu.com" -var uri = "/geocoding/v3" +var geoUrl = "/geocoding/v3" +var recogUrl = "/api_recog_address/v1/recog" type Location struct { Lng float64 @@ -36,6 +37,27 @@ type BmapGeoCodingResponse struct { Latitude string } +type BmapRecogRequest struct { + Address string +} + +type BmapRecogResponse struct { + Province string + City string + County string + Town string +} + +type RecogResponse struct { + Status int + Admin_info struct { + Province string + City string + County string + Town string + } +} + func GetAddrGeocoding(request BmapGeoCodingRequest) *BmapGeoCodingResponse { // 设置请求参数 params := url.Values{ @@ -45,7 +67,7 @@ func GetAddrGeocoding(request BmapGeoCodingRequest) *BmapGeoCodingResponse { } // 发起请求 - requestStr, _ := url.Parse(host + uri + "?" + params.Encode()) + requestStr, _ := url.Parse(host + geoUrl + "?" + params.Encode()) resp, err := http.Get(requestStr.String()) if err != nil || resp.StatusCode != 200 { return nil @@ -56,3 +78,27 @@ func GetAddrGeocoding(request BmapGeoCodingRequest) *BmapGeoCodingResponse { bmapGeoCodingResponse := BmapGeoCodingResponse{strconv.FormatFloat(response.Result.Location.Lng, 'f', -1, 64), strconv.FormatFloat(response.Result.Location.Lat, 'f', -1, 64)} return &bmapGeoCodingResponse } + +func GetAddrRecog(request BmapRecogRequest) *BmapRecogResponse { + // 设置请求参数 + params := url.Values{ + "address": []string{request.Address}, + "ak": []string{ak}, + } + // 发起请求 + requestStr, _ := url.Parse(host + recogUrl + "?" + params.Encode()) + resp, err := http.Get(requestStr.String()) + if err != nil || resp.StatusCode != 200 { + return nil + } + r, _ := io.ReadAll(resp.Body) + var response RecogResponse + json.Unmarshal(r, &response) + bmapRecogResponse := BmapRecogResponse{ + Province: response.Admin_info.Province, + City: response.Admin_info.City, + County: response.Admin_info.County, + Town: response.Admin_info.Town, + } + return &bmapRecogResponse +} diff --git a/business/utils/commonUtil.go b/business/utils/commonUtil.go index 2181eb6..1314b3a 100644 --- a/business/utils/commonUtil.go +++ b/business/utils/commonUtil.go @@ -20,9 +20,9 @@ type TimeObject struct { Y bool `json:"y"` } -func GetStrDays(day int, minute int, reserveMap map[string]models.ReserveTimeFilter, orderTimeMap map[string]string, carNum int, orderNumMap map[string]int, carServiceNum int) map[string][]TimeObject { +func GetStrDays(day int, minute int, reserveMap map[string]models.ReserveTimeFilter, orderTimeMap map[string]string, carNum int, orderNumMap map[string]int, carServiceNum int, times []string) map[string][]TimeObject { // 获取当前时间 - currentTime := time.Now() + currentTime := time.Now().AddDate(0, 0, 1) // 计算半小时后的时间 halfHourLater := currentTime.Add(time.Duration(minute) * time.Minute) @@ -42,8 +42,12 @@ func GetStrDays(day int, minute int, reserveMap map[string]models.ReserveTimeFil workStart := 9 workEnd := 19 var dayHoursMap = make(map[string][]TimeObject) + var serviceDaysMap = make(map[string]string) // 循环生成每半个小时的整点时间列表,直到一周后 oneWeekLater := currentTime.Add(time.Duration(day) * 24 * time.Hour) + for _, dayTime := range times { + serviceDaysMap[dayTime] = dayTime + } for nextHour.Before(oneWeekLater) { //且不存在过滤时间中 _, existsDay := reserveMap[nextHour.Format("2006-01-02")] @@ -52,6 +56,14 @@ func GetStrDays(day int, minute int, reserveMap map[string]models.ReserveTimeFil orderNum, existOrderNum := orderNumMap[nextHour.Format("2006-01-02 15:04")] if (nextHour.Hour() >= workStart && nextHour.Hour() < workEnd) && (!existsDay && !existsHour) && !existOrderTime { key := nextHour.Format("2006-01-02") + if _, ok := serviceDaysMap[key]; !ok { + //不存在服务配置中返回false + dayHoursMap[key] = append(dayHoursMap[key], TimeObject{ + Time: nextHour.Format("15:04"), + Y: false, + }) + continue + } if _, ok := dayHoursMap[key]; ok { dayHoursMap[key] = append(dayHoursMap[key], TimeObject{ Time: nextHour.Format("15:04"),