diff --git a/business/api/auth.go b/business/api/auth.go index b690469..0b6482d 100644 --- a/business/api/auth.go +++ b/business/api/auth.go @@ -12,6 +12,7 @@ import ( "pet-house.com/core/server/web" "pet-house.com/core/server/web/web_iris" "pet-house.com/core/server/zap_server" + "time" ) type LoginRequest struct { @@ -91,8 +92,9 @@ func (p DefParty) login() web_iris.Party { if coupons.SendStatus == 2 { //新注册用户下发优惠券 newUserCoupons := models.UserCoupons{ - Uid: userInfo.Id, - Cid: coupons.Id, + Uid: userInfo.Id, + Cid: coupons.Id, + LastExpireTime: time.Now().AddDate(0, 0, coupons.DrawDay), } database.Instance().Create(&newUserCoupons) } diff --git a/business/api/car.go b/business/api/car.go index e4c2a2b..f021f28 100644 --- a/business/api/car.go +++ b/business/api/car.go @@ -7,7 +7,6 @@ import ( "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" "time" @@ -126,18 +125,15 @@ func (p DefParty) carServiceProcess() web_iris.Party { var orderUserInfo *models.User database.Instance().Model(&models.User{}).Where("id = ?", mainOrder.Uid).Find(&orderUserInfo) var processPayAmount = carServiceProcessRequest.PayAmount - var originTotalAmount = float64(subOrderInfo.TotalAmount) + var originTotalAmount = float64(subOrderInfo.PayAmount) / 10.0 if processPayAmount > 0 && originTotalAmount != processPayAmount { //原价 originAmount := subOrderInfo.PayAmount userOriginAmount := orderUserInfo.Amount - p := int(utils.RoundToOneDecimalPlace(processPayAmount)) p1 := int(processPayAmount * 10) - mainOrder.TotalAmount = mainOrder.TotalAmount - subOrderInfo.TotalAmount + p if mainOrder.PayTotalAmount > 0 && originTotalAmount != processPayAmount { mainOrder.PayTotalAmount = mainOrder.PayTotalAmount - originAmount + p1 } - subOrderInfo.TotalAmount = p subOrderInfo.PayAmount = p1 if mainOrder.PayStatus == 1 { recordType := 2 @@ -187,7 +183,6 @@ func (p DefParty) carServiceProcess() web_iris.Party { } subOrderInfo.PayTime = time.Now() subOrderInfo.PayStatus = 1 - } if carServiceProcessRequest.Type == 1 { subOrderInfo.OrderStatus = 2 @@ -225,8 +220,9 @@ func (p DefParty) carServiceProcess() web_iris.Party { if coupons.SendStatus == 3 { //完成订单给邀请用户发送优惠券 newUserCoupons := models.UserCoupons{ - Uid: orderUserInfo.InviteUserId, - Cid: coupons.Id, + Uid: orderUserInfo.InviteUserId, + Cid: coupons.Id, + LastExpireTime: time.Now().AddDate(0, 0, coupons.DrawDay), } database.Instance().Create(&newUserCoupons) } diff --git a/business/api/common.go b/business/api/common.go index 5fbbad5..3d901f9 100644 --- a/business/api/common.go +++ b/business/api/common.go @@ -60,6 +60,9 @@ var ( OrderUpdateBalanceNotEnough = Error{Code: 217, Msg: "当前余额不足"} CouponsNotExistError = Error{Code: 218, Msg: "优惠券不存在"} UserCouponsNExistError = Error{Code: 219, Msg: "已领取同类型优惠券"} + UserCouponsNExistOrUseError = Error{Code: 220, Msg: "用户不存在优惠券或已使用"} + PayError = Error{Code: 221, Msg: "支付失败"} + OrderNotPayError = Error{Code: 222, Msg: "订单未支付定金"} ) func Success(ctx *context.Context, request any, data any) { @@ -152,7 +155,6 @@ func DataInit() { //--------------------------------------------------宠物商品关联数据--------------------------------------------------------- var petGoodsList []models.PetGoods database.Instance().Model(&models.PetGoods{}).Where("status = 1").Find(&petGoodsList) - zap_server.ZAPLOG.Info("111111", zap.Any("222", petGoodsList)) var PetGoodsMapC1 = make(map[string][]models.PetGoods) for _, value := range petGoodsList { key := strconv.Itoa(value.Assortment) + strconv.Itoa(value.PetType) + strconv.Itoa(value.Weight) + strconv.Itoa(value.Hair) diff --git a/business/api/control.go b/business/api/control.go index b1e66d3..46c4fc1 100644 --- a/business/api/control.go +++ b/business/api/control.go @@ -11,6 +11,7 @@ var CarBase = "/car" var Admin = "/admin" var System = "/system" var CouponsBase = "/coupons" +var PayBase = "/pay" func (p DefParty) RegisterList() []web_iris.Party { ps := []web_iris.Party{ @@ -51,6 +52,10 @@ func (p DefParty) RegisterList() []web_iris.Party { p.getCouponsList(), p.getUserCouponsList(), p.drawCoupons(), + //支付 + p.rechargeInfoList(), + p.toPay(), + p.payNotify(), //系统 p.systemConfigInfo(), p.systemBanners(), diff --git a/business/api/coupons.go b/business/api/coupons.go index 89f85f6..fd7b235 100644 --- a/business/api/coupons.go +++ b/business/api/coupons.go @@ -23,9 +23,21 @@ func (p DefParty) getCouponsList() web_iris.Party { UserNotExistError.Fail(ctx, nil) return } + + var userCoupons []*models.UserCoupons + database.Instance().Model(&models.UserCoupons{}).Where("uid = ? and coupons_status = 1", headerInfo.Uid).Find(&userCoupons) + var userCouponsMap = make(map[int]*models.UserCoupons) + for _, value := range userCoupons { + userCouponsMap[value.Cid] = value + } + var coupons []models.Coupons - database.Instance().Model(&models.Coupons{}).Where("status = 1 and expire_time > ?", time.Now().UnixNano()/int64(time.Millisecond)).Find(&coupons) + database.Instance().Model(&models.Coupons{}).Where("status = 1 and send_status = 1 and expire_time > ?", time.Now().UnixNano()/int64(time.Millisecond)).Find(&coupons) for index, coupon := range coupons { + m := userCouponsMap[coupon.Id] + if m != nil && m.Id > 0 { + coupons[index].ReceiverStatus = 1 + } coupons[index].PeriodInfo = strings.Split(coupon.Period, ",") } Success(ctx, headerInfo, coupons) @@ -55,10 +67,20 @@ func (p DefParty) getUserCouponsList() web_iris.Party { return } + var userCoupons []models.UserCoupons var coupons []models.Coupons - database.Instance().Model(&models.Coupons{}).Joins("JOIN user_coupons on coupons.id = user_coupons.cid and coupons.expire_time > ? and coupons.send_status = 1", time.Now().UnixNano()/int64(time.Millisecond)).Where(" user_coupons.coupons_status = ? and user_coupons.uid = ?", getUserCouponsListRequest.Status, headerInfo.Uid).Find(&coupons) - for index, coupon := range coupons { - coupons[index].PeriodInfo = strings.Split(coupon.Period, ",") + database.Instance().Model(&models.UserCoupons{}).Where("uid = ? and coupons_status = ?", headerInfo.Uid, getUserCouponsListRequest.Status).Find(&userCoupons) + for _, userCoupon := range userCoupons { + uc := CouponsMap[userCoupon.Cid] + if uc.Id == 0 { + var couponsData *models.Coupons + database.Instance().Model(&models.Coupons{}).Where("id = ?", userCoupon.Cid).Find(&couponsData) + uc = models.Coupons{Id: couponsData.Id, Name: couponsData.Name, Source: couponsData.Source, Discount: couponsData.Discount, GoodsSubType: couponsData.GoodsSubType} + } + uc.LastExpireTime = userCoupon.LastExpireTime.Format("2006-01-02") + uc.PeriodInfo = strings.Split(uc.Period, ",") + uc.Id = userCoupon.Id + coupons = append(coupons, uc) } Success(ctx, headerInfo, coupons) }) @@ -88,7 +110,7 @@ func (p DefParty) drawCoupons() web_iris.Party { } var coupons *models.Coupons - database.Instance().Model(&models.Coupons{}).Where("id = ?", drawCouponsRequest.Cid).Find(&coupons) + database.Instance().Model(&models.Coupons{}).Where("id = ? and status = 1", drawCouponsRequest.Cid).Find(&coupons) if coupons.Id == 0 { CouponsNotExistError.Fail(ctx, nil) return @@ -96,7 +118,7 @@ func (p DefParty) drawCoupons() web_iris.Party { lockMap := &utils.KeyedMutex{} lockMap.Lock(headerInfo.Token) var userCoupons *models.UserCoupons - database.Instance().Model(&models.UserCoupons{}).Where("uid = ? and cid = ?", headerInfo.Uid, drawCouponsRequest.Cid).Find(&userCoupons) + database.Instance().Model(&models.UserCoupons{}).Where("uid = ? and cid = ? and coupons_status = 1", headerInfo.Uid, drawCouponsRequest.Cid).Find(&userCoupons) if userCoupons.Id > 0 { UserCouponsNExistError.Fail(ctx, nil) lockMap.Unlock(headerInfo.Token) @@ -105,6 +127,8 @@ func (p DefParty) drawCoupons() web_iris.Party { newUserCoupons := models.UserCoupons{ Uid: headerInfo.Uid, Cid: drawCouponsRequest.Cid, + //领取后最后过期时间 + LastExpireTime: time.Now().AddDate(0, 0, coupons.DrawDay), } err := database.Instance().Create(&newUserCoupons).Error if err != nil { diff --git a/business/api/init.go b/business/api/init.go index e36f7f0..d17ddcc 100644 --- a/business/api/init.go +++ b/business/api/init.go @@ -47,6 +47,8 @@ func ModuleInit() { //&models.UserAmountRecord{}, //&models.Coupons{}, //&models.UserCoupons{}, + //&models.RechargeInfo{}, + //&models.PayOrder{}, ) zap_server.ZAPLOG.Info("data init ", zap.Any("err", _err)) DataInit() diff --git a/business/api/order.go b/business/api/order.go index aec3160..0930518 100644 --- a/business/api/order.go +++ b/business/api/order.go @@ -29,6 +29,7 @@ type OrderCreateRequest struct { PetGoodsInfos []PetGoodsInfo //宠物商品信息列表 ServiceTime string //预约时间 yyyy-MM-dd HH:mm ServiceAddrId int64 //预约服务地址 + PayOrderId string //支付单号 } type OrderCreateResponse struct { @@ -53,20 +54,35 @@ func (p DefParty) orderCreatePreCheck() web_iris.Party { } var userInfo *models.User database.Instance().Model(&models.User{}).Where("id = ?", headerBaseInfo.Uid).Find(&userInfo) - var orderTotalAmount = 0 + var goodsDiscountAmount = 0 + var goodsNotDiscountAmount = 0 for _, value := range orderCreateRequest.PetGoodsInfos { - for _, value := range value.GoodsIds { - goods := GoodsMap[value] - orderTotalAmount = orderTotalAmount + goods.Price + for _, gid := range value.GoodsIds { + goods := GoodsMap[gid] + if value.Cid > 0 { + var userCouponsInfo models.UserCoupons + database.Instance().Model(&models.UserCoupons{}).Where("id = ?", value.Cid).Order(clause.OrderByColumn{Column: clause.Column{Name: "update_time"}, Desc: true}).Find(&userCouponsInfo) + var coupons = CouponsMap[userCouponsInfo.Cid] + if coupons.Id == 0 { + var couponsData *models.Coupons + database.Instance().Model(&models.Coupons{}).Where("id = ?", coupons.Id).Find(&couponsData) + coupons = models.Coupons{Id: couponsData.Id, Discount: couponsData.Discount, GoodsSubType: couponsData.GoodsSubType} + } + if goods.GoodsSubType == coupons.GoodsSubType { + goodsDiscountAmount = goodsDiscountAmount + int(utils.RoundToOneDecimalPlace(float64(goods.Price)*(float64(coupons.Discount)/100.0))*10) + } else { + goodsNotDiscountAmount = goodsNotDiscountAmount + goods.Price + } + } } } orderCreatePreCheckResponse := OrderCreatePreCheckResponse{ HasAlert: false, } - var discountAmount = orderTotalAmount if userInfo.Discount > 0 { discount := float64(userInfo.Discount) - discountAmount = int(utils.RoundToOneDecimalPlace(float64(orderTotalAmount)*(discount/100.0)) * 10) + //此处计算享受会员折扣及优惠券折扣的价格 + var discountAmount = int(utils.RoundToOneDecimalPlace(float64(goodsNotDiscountAmount)*(discount/100.0))*10) + goodsDiscountAmount if userInfo.Amount < discountAmount { orderCreatePreCheckResponse.HasAlert = true orderCreatePreCheckResponse.AlertMsg = "您的会员余额不够,需要进行线下支付,当前订单将不享受会员折扣优惠价格" @@ -94,16 +110,18 @@ func (p DefParty) orderCreate() web_iris.Party { UserServiceAddrNotExistError.Fail(ctx, nil) return } + var userCouponsList []*models.UserCoupons var projectionServiceTime = 0 + var userCoupons *models.UserCoupons for _, value := range orderCreateRequest.PetGoodsInfos { - var userCoupons *models.UserCoupons - database.Instance().Model(&models.UserCoupons{}).Where("uid = ? and cid = ? and coupons_status = 1", headerBaseInfo.Uid, value.Cid).Find(&userCoupons) + database.Instance().Model(&models.UserCoupons{}).Where("id = ? and coupons_status = 1", value.Cid).Find(&userCoupons) + userCouponsList = append(userCouponsList, userCoupons) if value.Cid > 0 { if userCoupons.Id == 0 { CouponsNotExistError.Fail(ctx, nil) return } - coupons := CouponsMap[value.Cid] + coupons := CouponsMap[userCoupons.Cid] if coupons.Id == 0 { CouponsNotExistError.Fail(ctx, nil) return @@ -145,12 +163,16 @@ func (p DefParty) orderCreate() web_iris.Party { } } + var payOrder *models.PayOrder + database.Instance().Model(&models.PayOrder{}).Where("order_id = ? and order_status = 0", orderCreateRequest.PayOrderId).Find(&payOrder) + if payOrder.Id > 0 { + OrderNotPayError.DefFail(ctx, orderCreateRequest, nil) + return + } + //订单总金额 var orderTotalAmount = 0 - //可以参与会员折扣的金额 - var needDiscountAmount = 0 - //不可参与会员折扣的金额 - var notDiscountAmount = 0 + orderLock.Lock() orderId := NextId.Generate().String() orderMain := models.OrderMain{ @@ -167,8 +189,17 @@ func (p DefParty) orderCreate() web_iris.Party { var orderSubList []models.OrderSub var orderSubDetailList []models.OrderDetail var mainGoods = 0 + + var coupons models.Coupons for _, value := range orderCreateRequest.PetGoodsInfos { - coupons := CouponsMap[value.Cid] + //可以参与会员折扣的金额 + var needDiscountAmount = 0 + //不可参与会员折扣的金额 + var notDiscountAmount = 0 + + var userCouponsInfo models.UserCoupons + database.Instance().Model(&models.UserCoupons{}).Where("id = ?", value.Cid).Order(clause.OrderByColumn{Column: clause.Column{Name: "update_time"}, Desc: true}).Find(&userCouponsInfo) + coupons = CouponsMap[userCouponsInfo.Cid] subOrderId := NextId.Generate().String() var totalAmount = 0 var projectionServiceTime = 0 @@ -193,18 +224,28 @@ func (p DefParty) orderCreate() web_iris.Party { mainGoods++ } goodsName = goods.Name + "、" + goodsName - } - userPetInfo := GetUserPet(headerBaseInfo.Uid, value.PetId) - var petInfo = "宠物名称:" + userPetInfo.PetInfo.NickName + "
品种:" + userPetInfo.PetBaseInfo.Assortment + "
服务项目:" + goodsName - projectionServiceTimeAll += projectionServiceTime - - for _, orderDetail := range orderSubDetailList { - if orderDetail.Cid > 0 { + //符合折扣金额才计入折扣价 + if orderDetail.Cid > 0 && coupons.GoodsSubType == goods.GoodsSubType { notDiscountAmount = notDiscountAmount + orderDetail.DiscountAmount } else { needDiscountAmount = needDiscountAmount + orderDetail.Amount } } + userPetInfo := GetUserPet(headerBaseInfo.Uid, value.PetId) + var weightStr = "无" + if userPetInfo.PetBaseInfo.PetType == 1 { + weightStr = CatWeightMap[userPetInfo.PetInfo.Weight] + } else { + weightStr = DogWeightMap[userPetInfo.PetInfo.Weight] + } + var petInfo = "宠物名称:" + userPetInfo.PetInfo.NickName + "
品种:" + userPetInfo.PetBaseInfo.Assortment + "
体重:" + weightStr + "
服务项目:" + goodsName + + projectionServiceTimeAll += projectionServiceTime + + /*for _, orderDetail := range orderSubDetailList { + goods := GoodsMap[orderDetail.GoodsId] + + }*/ orderSub := models.OrderSub{ OrderId: subOrderId, @@ -214,7 +255,8 @@ func (p DefParty) orderCreate() web_iris.Party { PayType: 1, Discount: 100, TotalAmount: totalAmount, - PayAmount: totalAmount, + //折扣价格 默认*10 + PayAmount: notDiscountAmount + needDiscountAmount*10, //设置商品折扣金额用于后续打折计算 GoodsDiscountAmount: notDiscountAmount, //设置商品非折扣金额 @@ -223,7 +265,10 @@ func (p DefParty) orderCreate() web_iris.Party { PetInfo: petInfo, Status: 1, ProjectionServiceTime: projectionServiceTime, + Cid: coupons.Id, + Ucid: userCoupons.Id, } + //订单总金额 orderTotalAmount = orderTotalAmount + totalAmount orderSubList = append(orderSubList, orderSub) } @@ -253,7 +298,6 @@ func (p DefParty) orderCreate() web_iris.Party { } else { discountAmount = int(utils.RoundToOneDecimalPlace(float64(inDiscountAmount) * 10)) } - orderMain.TotalAmount = orderTotalAmount tx := database.Instance().Begin() var db4 *gorm.DB @@ -282,19 +326,26 @@ func (p DefParty) orderCreate() web_iris.Party { ProScene: "下单", Status: 1, } + for index_, orderSub := range orderSubList { + if userInfo.Discount > 0 { + discount := float64(userInfo.Discount) + discountAmount1 := int(utils.RoundToOneDecimalPlace(float64(orderSub.GoodsOriginAmount)*(discount/100.0)) * 10) + orderSubList[index_].PayAmount = discountAmount1 + orderSub.GoodsDiscountAmount + } + } tx.Model(&models.UserAmountRecord{}).Create(&userAmountRecord) - zap_server.ZAPLOG.Info("会员金额扣除", zap.Any("用户ID", userInfo.Id), zap.Any("订单号", orderMain.OrderId), zap.Any("当前余额", currAmount), zap.Any("扣除余额", discountAmount+orderGoodsDiscountAmount), zap.Any("剩余余额", userInfo.Amount), zap.Any("折扣", userInfo.Discount)) - for index, _ := range orderSubList { - orderSubList[index].Discount = userInfo.Discount + zap_server.ZAPLOG.Info("会员金额扣除", zap.Any("用户ID", userInfo.Id), zap.Any("订单号", orderMain.OrderId), zap.Any("当前余额", currAmount), zap.Any("扣除余额", discountAmount+orderGoodsDiscountAmount), zap.Any("剩余余额", userInfo.Amount), zap.Any("折扣", userInfo.Discount), zap.Any("优惠券折扣信息", coupons)) + for index_, _ := range orderSubList { + orderSubList[index_].Discount = userInfo.Discount //orderSubList[index].PayAmount = int32(math.Round(float64(orderSubList[index].TotalAmount) * (discount / 100.0))) - orderSubList[index].PayAmount = discountAmount + orderGoodsDiscountAmount + //orderSubList[index].PayAmount = discountAmount + orderGoodsDiscountAmount } } else { userAmountRecord := models.UserAmountRecord{ UserId: userInfo.Id, Type: 3, OriginAmount: userInfo.Amount, - ProAmount: discountAmount + orderGoodsDiscountAmount, + ProAmount: inDiscountAmount*10 + orderGoodsDiscountAmount, CurrAmount: userInfo.Amount, OrderId: orderId, ProScene: "下单", @@ -302,6 +353,7 @@ func (p DefParty) orderCreate() web_iris.Party { } tx.Model(&models.UserAmountRecord{}).Create(&userAmountRecord) } + orderMain.PayOrderId = orderCreateRequest.PayOrderId var findUserServiceAddr models.UserServiceAddr database.Instance().Model(&models.UserServiceAddr{}).Where("id = ? and uid = ?", orderCreateRequest.ServiceAddrId, headerBaseInfo.Uid).Find(&findUserServiceAddr) userInfo.Mobile = findUserServiceAddr.Mobile @@ -312,6 +364,13 @@ func (p DefParty) orderCreate() web_iris.Party { 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) + for _, userCoupons := range userCouponsList { + updateUserCoupons := map[string]interface{}{ + "CouponsStatus": 2, + } + //更新用户优惠券状态 + tx.Model(&userCoupons).Updates(&updateUserCoupons) + } if (db4 != nil && db4.Error != nil) || db.Error != nil || db1.Error != nil || db2.Error != nil { tx.Callback() orderLock.Unlock() @@ -361,10 +420,39 @@ func GetOrderDetail(orderId string) OrderDetail { var orderDetailList []models.OrderDetail database.Instance().Model(&models.OrderDetail{}).Where("sub_order_id = ? ", orderSub.OrderId).Find(&orderDetailList) var goods []models.Goods + var couponsGoods models.Goods + var coupons models.Coupons for _, orderDetail := range orderDetailList { good := GoodsMap[orderDetail.GoodsId] goods = append(goods, good) + coupons = CouponsMap[orderSub.Cid] + if coupons.Id == 0 { + var couponsS *models.Coupons + database.Instance().Model(&models.Coupons{}).Where("id = ?", orderSub.Cid).Find(&couponsS) + coupons = models.Coupons{ + Id: couponsS.Id, + Name: couponsS.Name, + GoodsSubType: couponsS.GoodsSubType, + Discount: couponsS.Discount, + Source: couponsS.Source, + PeriodType: couponsS.PeriodType, + Period: couponsS.Period, + PeriodInfo: couponsS.PeriodInfo, + ExpireTime: couponsS.ExpireTime, + CreateTime: couponsS.CreateTime, + UpdateTime: couponsS.UpdateTime, + Status: couponsS.Status, + SendStatus: couponsS.SendStatus, + ReceiverStatus: couponsS.ReceiverStatus, + } + } + if orderSub.Cid > 0 { + if coupons.GoodsSubType == good.GoodsSubType { + couponsGoods = good + } + } } + orderSubR1 := SubOrder{ OrderId: orderSub.OrderId, Status: orderSub.OrderStatus, @@ -373,20 +461,39 @@ func GetOrderDetail(orderId string) OrderDetail { PayAmount: strconv.Itoa(orderSub.PayAmount), Discount: float64(orderSub.Discount), Goods: goods, + Coupons: nil, + } + if coupons.Id > 0 { + orderSubR1.Coupons = &models.Coupons{ + Id: coupons.Id, + Name: coupons.Name, + GoodsSubType: coupons.GoodsSubType, + Discount: coupons.Discount, + Source: coupons.Source, + PeriodType: coupons.PeriodType, + Period: coupons.Period, + PeriodInfo: coupons.PeriodInfo, + SendStatus: coupons.SendStatus, + } + orderSubR1.CouponsAmount = strconv.FormatFloat(float64(couponsGoods.Price*10-orderSub.GoodsDiscountAmount)/10, 'f', 1, 64) + } else { + orderSubR1.Coupons = nil } discountTotalAmount = discountTotalAmount + orderSub.GoodsOriginAmount*10 + orderSub.GoodsDiscountAmount - if orderSub.PayType == 3 { + if orderSub.PayType == 3 || orderSubR1.Coupons != nil { x := float64(orderSub.PayAmount) / 10 orderSubR1.PayAmount = strconv.FormatFloat(x, 'f', 1, 64) } - if orderSubR1.Discount < 100 { - //orderSubR1.PayAmount = int32(math.Round(float64(orderSubR1.TotalAmount) * (orderSubR1.Discount / 100.0))) - //x := float64(orderSubR1.TotalAmount) * (orderSubR1.Discount / 100.0) / 10 + if orderSubR1.Discount < 100 || orderSubR1.Coupons != nil { orderSubR1.DiscountAmount = orderSubR1.PayAmount } else { - orderSubR1.DiscountAmount = strconv.Itoa(orderSubR1.TotalAmount) + if orderSub.PayAmount > 0 { + orderSubR1.DiscountAmount = strconv.FormatFloat(float64(orderSub.PayAmount)/10, 'f', 1, 64) + } else { + orderSubR1.DiscountAmount = strconv.Itoa(orderSubR1.TotalAmount) + } } - if orderSub.PayType == 1 { + if orderSub.PayType == 1 && orderSubR1.Coupons == nil { orderSubR1.PayAmount = strconv.Itoa(orderSubR1.TotalAmount) } subOrderList = append(subOrderList, orderSubR1) @@ -437,14 +544,16 @@ type OrderListRequest struct { } type SubOrder struct { - OrderId string `json:"orderId"` //子订单ID - Status int `json:"status"` //子订单状态 0待派单 1待服务 2服务中 3已完成 4已取消 - UserPetInfo UserPetInfo `json:"userPetInfo"` //用户宠物信息 - TotalAmount int `json:"totalAmount"` //总金额 - PayAmount string `json:"payAmount"` //实际支付金额 - DiscountAmount string `json:"discountAmount"` //折扣金额 - Discount float64 `json:"discount"` //用户折扣 - Goods []models.Goods `json:"goods"` //商品信息 + OrderId string `json:"orderId"` //子订单ID + Status int `json:"status"` //子订单状态 0待派单 1待服务 2服务中 3已完成 4已取消 + UserPetInfo UserPetInfo `json:"userPetInfo"` //用户宠物信息 + TotalAmount int `json:"totalAmount"` //总金额 + PayAmount string `json:"payAmount"` //实际支付金额 + DiscountAmount string `json:"discountAmount"` //折扣金额 + Discount float64 `json:"discount"` //用户折扣 + Goods []models.Goods `json:"goods"` //商品信息 + Coupons *models.Coupons `json:"coupons"` //优惠券信息 + CouponsAmount string `json:"couponsAmount"` //优惠券减免金额 } type OrderDetail struct { @@ -613,6 +722,16 @@ func (p DefParty) orderCancel() web_iris.Party { "OrderStatus": 4, } database.Instance().Model(&orderSub).Updates(&updateValuesSub) + //返还优惠券 + if orderSub.Cid > 0 { + var userCoupons []models.UserCoupons + database.Instance().Model(&models.UserCoupons{}).Where("uid = ? and cid = ? and coupons_status = 2", headerBaseInfo.Uid, orderSub.Cid).Order(clause.OrderByColumn{Column: clause.Column{Name: "update_time"}, Desc: true}).Find(&userCoupons) + coupons := userCoupons[0] + updateValuesCoupons := map[string]interface{}{ + "CouponsStatus": 1, + } + database.Instance().Model(&coupons).Updates(updateValuesCoupons) + } } Success(ctx, orderCancelRequest, OrderCancelResponse{GetOrderDetail(orderMain.OrderId)}) }) @@ -774,8 +893,23 @@ func (p DefParty) orderGoodsUpdate() web_iris.Party { database.Instance().Model(&models.OrderMain{}).Where("order_id = ? and status = 1", orderSub.MainOrderId).Find(&orderMain) var nowOrderTotalAmount = 0 var projectionServiceTime = 0 + + //商品优惠券价格 + var goodsDiscountAmount = 0 + //商品非优惠券价格 + var goodsNotDiscountAmount = 0 + var coupons models.Coupons for _, value := range orderGoodsUpdateRequest.GoodsIds { goods := GoodsMap[value] + if orderSub.Cid > 0 { + coupons = CouponsMap[orderSub.Cid] + } + if coupons.Id > 0 && coupons.GoodsSubType == goods.GoodsSubType { + goodsDiscountAmount = goodsDiscountAmount + int(utils.RoundToOneDecimalPlace(float64(goods.Price)*(float64(coupons.Discount)/100.0))*10) + } else { + goodsNotDiscountAmount = goodsNotDiscountAmount + goods.Price + } + nowOrderTotalAmount = nowOrderTotalAmount + goods.Price if goods.Time != "/" && len(goods.Time) > 0 { goodsTime, _ := strconv.Atoi(goods.Time) @@ -808,13 +942,15 @@ func (p DefParty) orderGoodsUpdate() web_iris.Party { if userInfo.Discount > 0 { discount := float64(userInfo.Discount) - subOrderAmount = int(utils.RoundToOneDecimalPlace(float64(subOrderAmount)*(discount/100.0)) * 10) + //计算非折扣商品 + subOrderAmount = int(utils.RoundToOneDecimalPlace(float64(goodsNotDiscountAmount)*(discount/100.0)) * 10) + } else { + subOrderAmount = goodsNotDiscountAmount * 10 } - orderMain.PayTotalAmount = orderMain.PayTotalAmount - originOrderPayAmount + subOrderAmount - orderMain.TotalAmount = orderMain.TotalAmount - originOrderTotalAmount + nowOrderTotalAmount + orderMain.PayTotalAmount = orderMain.PayTotalAmount - originOrderPayAmount + subOrderAmount + goodsDiscountAmount //余额不足 - if userInfo.Amount < subOrderAmount { + if userInfo.Amount < subOrderAmount+goodsDiscountAmount { OrderUpdateBalanceNotEnough.DefFail(ctx, orderGoodsUpdateRequest, "原订单金额:"+strconv.FormatFloat(float64(orderMain.PayTotalAmount)/10, 'f', 1, 64)+" 修改后订单金额:"+strconv.FormatFloat(float64(orderMain.PayTotalAmount)/10, 'f', 1, 64)+";修改服务项目后余额不足,请充值后再支付") return } @@ -822,13 +958,13 @@ func (p DefParty) orderGoodsUpdate() web_iris.Party { var amountType = 2 var proAmount = 0 //原始价格小于新价格 扣,原始价格大于新价格 加 - if originOrderPayAmount < subOrderAmount { + if originOrderPayAmount < subOrderAmount+goodsDiscountAmount { } else { amountType = 1 } var preAmount = orderMain.PayTotalAmount - userInfo.Amount = userInfo.Amount - subOrderAmount + userInfo.Amount = userInfo.Amount - (subOrderAmount + goodsDiscountAmount) updateValues := map[string]interface{}{ "Amount": userInfo.Amount, } @@ -854,6 +990,7 @@ func (p DefParty) orderGoodsUpdate() web_iris.Party { database.Instance().Model(&models.UserAmountRecord{}).Create(&userAmountRecord) zap_server.ZAPLOG.Info("会员金额扣除", zap.Any("用户ID", userInfo.Id), zap.Any("订单ID", orderMain.OrderId), zap.Any("用户修改之前余额", originAmount), zap.Any("当前余额", userInfo.Amount), zap.Any("商品修改之前金额", preAmount), zap.Any("商品修改之后金额", orderMain.PayTotalAmount), zap.Any("折扣", userInfo.Discount)) } + orderSub.PayAmount = subOrderAmount + goodsDiscountAmount } else { var x = 0 if originOrderTotalAmount > nowOrderTotalAmount { @@ -873,35 +1010,58 @@ func (p DefParty) orderGoodsUpdate() web_iris.Party { Status: 1, } database.Instance().Model(&models.UserAmountRecord{}).Create(&userAmountRecord) + orderSub.PayAmount = goodsNotDiscountAmount*10 + goodsDiscountAmount } database.Instance().Where("sub_order_id = ?", orderGoodsUpdateRequest.OrderId).Delete(&models.OrderDetail{}) + var goodsName = "" for _, value := range orderGoodsUpdateRequest.GoodsIds { goods := GoodsMap[value] orderDetail := models.OrderDetail{ SubOrderId: orderGoodsUpdateRequest.OrderId, GoodsId: goods.Id, Amount: goods.Price, + Cid: orderSub.Cid, } + goodsName = goods.Name + "、" + goodsName database.Instance().Model(&models.OrderDetail{}).Create(&orderDetail) } - + userPetInfo := GetUserPet(orderMain.Uid, orderSub.PetId) + var weightStr = "无" + if userPetInfo.PetBaseInfo.PetType == 1 { + weightStr = CatWeightMap[userPetInfo.PetInfo.Weight] + } else { + weightStr = DogWeightMap[userPetInfo.PetInfo.Weight] + } + var petInfo = "宠物名称:" + userPetInfo.PetInfo.NickName + "
品种:" + userPetInfo.PetBaseInfo.Assortment + "
体重:" + weightStr + "
服务项目:" + goodsName + orderSub.PetInfo = petInfo orderSub.TotalAmount = nowOrderTotalAmount - orderSub.PayAmount = subOrderAmount orderSub.ProjectionServiceTime = projectionServiceTime updateValues := map[string]interface{}{ "TotalAmount": orderSub.TotalAmount, "PayAmount": orderSub.PayAmount, "ProjectionServiceTime": orderSub.ProjectionServiceTime, + "GoodsDiscountAmount": goodsDiscountAmount, + "GoodsOriginAmount": goodsNotDiscountAmount, + "PetInfo": orderSub.PetInfo, } + database.Instance().Model(&orderSub).Updates(&updateValues) + //当前所有订单 + var orderSubListAll []models.OrderSub + database.Instance().Model(&models.OrderSub{}).Where("main_order_id = ?", orderMain.OrderId).Find(&orderSubListAll) + var totalAmount = 0 + for _, sub := range orderSubListAll { + totalAmount = totalAmount + sub.TotalAmount + } + orderMain.ProjectionServiceTime = allProjectionServiceTime + orderMain.TotalAmount = totalAmount updateValues1 := map[string]interface{}{ "PayTotalAmount": orderMain.PayTotalAmount, "TotalAmount": orderMain.TotalAmount, "ProjectionServiceTime": orderMain.ProjectionServiceTime, } database.Instance().Model(&orderMain).Updates(&updateValues1) - database.Instance().Model(&orderSub).Updates(&updateValues) Success(ctx, orderGoodsUpdateRequest, OrderDetailResponse{GetOrderDetail(orderSub.MainOrderId)}) }) }} diff --git a/business/api/pay.go b/business/api/pay.go new file mode 100644 index 0000000..f137c4a --- /dev/null +++ b/business/api/pay.go @@ -0,0 +1,110 @@ +package api + +import ( + "crypto/x509" + "encoding/json" + "github.com/kataras/iris/v12" + "github.com/kataras/iris/v12/context" + "github.com/wechatpay-apiv3/wechatpay-go/core" + "github.com/wechatpay-apiv3/wechatpay-go/core/auth/verifiers" + "github.com/wechatpay-apiv3/wechatpay-go/core/notify" + "github.com/wechatpay-apiv3/wechatpay-go/services/payments" + "github.com/wechatpay-apiv3/wechatpay-go/services/payments/jsapi" + utils2 "github.com/wechatpay-apiv3/wechatpay-go/utils" + "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" + "pet-house.com/core/server/web/web_iris" + "pet-house.com/core/server/zap_server" + "strconv" +) + +// 充值挡位列表 +func (p DefParty) rechargeInfoList() web_iris.Party { + return web_iris.Party{Prefix: p.Prefix, PartyFunc: func(index iris.Party) { + index.Post(PayBase+"/rechargeInfoList", func(ctx *context.Context) { + var rechargeInfoList []models.RechargeInfo + database.Instance().Model(&models.RechargeInfo{}).Where("type = 1 and status = 1").Find(&rechargeInfoList) + Success(ctx, nil, rechargeInfoList) + }) + }} +} + +type ToPayRequest struct { + PayId int //支付ID +} + +type ToPayResponse struct { + Content *jsapi.PrepayWithRequestPaymentResponse `json:"content"` + PayOrderId string +} + +// 充值 +func (p DefParty) toPay() web_iris.Party { + return web_iris.Party{Prefix: p.Prefix, PartyFunc: func(index iris.Party) { + index.Post(PayBase+"/toPay", func(ctx *context.Context) { + headerBaseInfo := GetHeaderBaseInfo(ctx) + body, _ := io.ReadAll(ctx.Request().Body) + var toPayRequest ToPayRequest + json.Unmarshal(body, &toPayRequest) + var userInfo *models.User + database.Instance().Model(&models.User{}).Where("id = ?", headerBaseInfo.Uid).Find(&userInfo) + var rechargeInfo *models.RechargeInfo + database.Instance().Model(&models.RechargeInfo{}).Where("id = ?", toPayRequest.PayId).Find(&rechargeInfo) + svc := jsapi.JsapiApiService{Client: utils.GetWxPayService()} + price, _ := strconv.ParseInt(rechargeInfo.Price, 10, 64) + orderId := NextId.Generate().String() + // 得到prepay_id,以及调起支付所需的参数和签名 + resp, _, err := svc.PrepayWithRequestPayment(ctx, + jsapi.PrepayRequest{ + Appid: core.String(utils.CONFIG.AppId), + Mchid: core.String(utils.CONFIG.MchId), + Description: core.String(rechargeInfo.Name), + OutTradeNo: core.String(orderId), + NotifyUrl: core.String(web.CONFIG.System.Domain + "/pet-house/pay/payNotify"), + Amount: &jsapi.Amount{ + Total: core.Int64(price * 100), + }, + Payer: &jsapi.Payer{ + Openid: core.String(userInfo.OpenId), + }, + }, + ) + if err != nil { + PayError.DefFail(ctx, toPayRequest, nil) + return + } + payOrder := models.PayOrder{ + OrderId: orderId, + OrderName: rechargeInfo.Name, + OrderPrice: rechargeInfo.Price, + PayId: "", + } + database.Instance().Model(&models.PayOrder{}).Create(&payOrder) + Success(ctx, toPayRequest, ToPayResponse{Content: resp, PayOrderId: orderId}) + }) + }} +} + +// 充值通知 +func (p DefParty) payNotify() web_iris.Party { + return web_iris.Party{Prefix: p.Prefix, PartyFunc: func(index iris.Party) { + index.Post(PayBase+"/payNotify", func(ctx *context.Context) { + wechatPayCert, _ := utils2.LoadCertificate(utils.CONFIG.WechatCert) + // 2. 使用本地管理的微信支付平台证书获取微信支付平台证书访问器 + certificateVisitor := core.NewCertificateMapWithList([]*x509.Certificate{wechatPayCert}) + // 3. 使用apiv3 key、证书访问器初始化 `notify.Handler` + handler := notify.NewNotifyHandler(utils.CONFIG.MchAPIv3Key, verifiers.NewSHA256WithRSAVerifier(certificateVisitor)) + transaction := new(payments.Transaction) + notifyReq, err := handler.ParseNotifyRequest(ctx, ctx.Request(), transaction) + zap_server.ZAPLOG.Info("payNotify", zap.Any("notifyReq", notifyReq), zap.Any("transaction", transaction), zap.Any("err", err)) + var payOrder *models.PayOrder + database.Instance().Model(&models.PayOrder{}).Where("order_id = ?", transaction.OutTradeNo).Find(&payOrder) + payOrder.Status = 1 + database.Instance().Model(&models.PayOrder{}).Updates(&payOrder) + }) + }} +} diff --git a/business/api/system.go b/business/api/system.go index 439ef96..c772b67 100644 --- a/business/api/system.go +++ b/business/api/system.go @@ -25,7 +25,9 @@ func (p DefParty) systemConfigInfo() web_iris.Party { } type SystemBannersResponse struct { - Banners []models.SystemConfig `json:"banners"` + Banners []models.SystemConfig `json:"banners"` + ShareText string `json:"shareText"` + ShareImg string `json:"shareImg"` } // 获取系统banner @@ -34,7 +36,9 @@ func (p DefParty) systemBanners() web_iris.Party { index.Post(System+"/systemBanners", func(ctx *context.Context) { var systemConfig []models.SystemConfig database.Instance().Model(&models.SystemConfig{}).Where("config_type = 0").Order(clause.OrderByColumn{Column: clause.Column{Name: "sort"}, Desc: true}).Find(&systemConfig) - Success(ctx, nil, SystemBannersResponse{systemConfig}) + var systemConfigShare models.SystemConfig + database.Instance().Model(&models.SystemConfig{}).Where("config_type = 3").Order(clause.OrderByColumn{Column: clause.Column{Name: "sort"}, Desc: true}).Limit(1).Find(&systemConfigShare) + Success(ctx, nil, SystemBannersResponse{systemConfig, systemConfigShare.Content, systemConfigShare.JumpUrl}) }) }} } diff --git a/business/config/apiclient_cert.pem b/business/config/apiclient_cert.pem new file mode 100644 index 0000000..11d57da --- /dev/null +++ b/business/config/apiclient_cert.pem @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIEKzCCAxOgAwIBAgIUfaIQgSDkxBhTe888BhqrEJzPiKkwDQYJKoZIhvcNAQEL +BQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsT +FFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3Qg +Q0EwHhcNMjQwOTEwMDc0NDUzWhcNMjkwOTA5MDc0NDUzWjCBhDETMBEGA1UEAwwK +MTY4MjYzNzYxMjEbMBkGA1UECgwS5b6u5L+h5ZWG5oi357O757ufMTAwLgYDVQQL +DCflroHms6LlrqDmiqTlrrblrqDnianmnI3liqHmnInpmZDlhazlj7gxCzAJBgNV +BAYTAkNOMREwDwYDVQQHDAhTaGVuWmhlbjCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBALoZ/ZK7BdW1DKCzsqQnS9ElkWhv8Df4JidAupVoaaGa0Iz8cukj +8JgqzzmOA0fSief3z0OvOvmXQujNLXka38Y+8KB/K/BnqRYVGSvLWKwfdalSS+ca +JZUNLVQVv1zDrPVPb3JmoJMADPIq2sOfLqjmWPIxRcpa7sPeall5fMx07aVrpi2O +v1ueRUphIYNT6aV6KnPrRf/lcRwZxE0+l30l45sDNrd7l5tUsnJq/1OLP+kgidkp +ic6W1f3jrHZTwYakgShEUgkRsHeUjIQ0ZRYSsD4Mxn/bqdAxDSDWB6aWGwMrY1ku +bsPkE/AcXTpgpaC+2kQVhaYrWuly0w0T1jMCAwEAAaOBuTCBtjAJBgNVHRMEAjAA +MAsGA1UdDwQEAwID+DCBmwYDVR0fBIGTMIGQMIGNoIGKoIGHhoGEaHR0cDovL2V2 +Y2EuaXRydXMuY29tLmNuL3B1YmxpYy9pdHJ1c2NybD9DQT0xQkQ0MjIwRTUwREJD +MDRCMDZBRDM5NzU0OTg0NkMwMUMzRThFQkQyJnNnPUhBQ0M0NzFCNjU0MjJFMTJC +MjdBOUQzM0E4N0FEMUNERjU5MjZFMTQwMzcxMA0GCSqGSIb3DQEBCwUAA4IBAQC7 +Zgux40pvNohZx721rygjo0yUbx54docNS79Eh5J9ukSKMf0cTsORlAnrsCbSYnIo +MiqdPqccRYJUefUUuhOrKtuzaka4T+VSiaDTnZ07kB3I8xlvZtW32jHOstVPrJsB +fEg53sl5sfWHY+OfuEhwOkoH3v/Fs4nH4hQxjO8RW9tNXqLvTrl1JeEwV4bMZxCA +wCvoMPqn19WBFsaFb0kXF46oGeH5kId6oon0RCi+vXb8nSZ4oPXIJwxi9u/uy+wD +rVlHohjCK+bRbxGgaIcX5uMPK/M73mzuuLFCbIWN4CLz9yyZGKxNJBq4YuGe+jWa +gh1GheVuvTdcJ4ZGDhQT +-----END CERTIFICATE----- diff --git a/business/config/apiclient_key.pem b/business/config/apiclient_key.pem new file mode 100644 index 0000000..cb6de82 --- /dev/null +++ b/business/config/apiclient_key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC6Gf2SuwXVtQyg +s7KkJ0vRJZFob/A3+CYnQLqVaGmhmtCM/HLpI/CYKs85jgNH0onn989Drzr5l0Lo +zS15Gt/GPvCgfyvwZ6kWFRkry1isH3WpUkvnGiWVDS1UFb9cw6z1T29yZqCTAAzy +KtrDny6o5ljyMUXKWu7D3mpZeXzMdO2la6Ytjr9bnkVKYSGDU+mleipz60X/5XEc +GcRNPpd9JeObAza3e5ebVLJyav9Tiz/pIInZKYnOltX946x2U8GGpIEoRFIJEbB3 +lIyENGUWErA+DMZ/26nQMQ0g1gemlhsDK2NZLm7D5BPwHF06YKWgvtpEFYWmK1rp +ctMNE9YzAgMBAAECggEBAKaAmHGOKLNCb9Db1amuitPNw+HnvMmmmCQococ4SsHO +rGZnezwB7sGGE69P0rmQsde7zFGnVqF2ZuO4psWieLF7IHjILvzBEfPy3nljTqt4 +3iGmwsg6iYNorbR8XKDXZUh5UKHPFj1PH0YPd3jsU6fDp7LQyLs6TkMqHIZ+/lxB +fsU2IDg9TjyeXDHrtjhAEhEkj9ehWYrgNfZtFTDSv6Bwu3fbSvUaMwJCJsxi11n3 +s15UK+TI7rOh65+cCFlAAF5Xa+z4KLkhMQuUSgwNnoe1kK3JA2ATzqdvCHU/WLu+ +o2ZJvGG7lmjWHFkGhafIRbf0zSPHg+j7qWPOcTCYHekCgYEA25qDi9z2LlLTRt+U +KfdsRZasHpncK16o15NBYrU9Va9BJlTaLWnOOqikYpqiM2OugUiUoy4mSSep4By4 +nr/mqmHfj0Q9qQ3u0gw6YVoEI8ULNM/abYyhn4ov3UhBe3pMsOdkhXqj9qzB7UL3 +j8M90WgRwBbdImAHHLf/M6dIjQcCgYEA2PIHmOEGa+x6FGYTudwLthfC2hm3/RWM +P8+EclnSAZ1XlBZK53ca/QH28dYcAT8fTp4RkKCpZNf6rWN/ulImncAEJnOCd8eE +dykFyMnreIKNYY93hYSMSbJ6pjhPVEU6YtgEGeZP/8U+GNTIfmnTD2NpoTkWqo0P +bbpiDczjDnUCgYBmc6SxaKnVnMCCzSYDmjICGTQ9poxoE2Z0BHg5w9JUmkmrx2Ru +UXFsPmdKtVe83+F24VSu+IXWFVIUg3HCZkcH8FdrMGwP5bcoeZn29xb9VR1QSzRd +bYGtu7tu4hnisID8+0cTWs8J8zkaJub2RCEgBAxb8I7ETcajqFfAUmfvmwKBgESt +oiAQpKLH0wHgKsB826xCq2m2GJvjFWoh/LDipGvkbpQv1nieKQoNdCqs+GKgo/2U +ZsUdR2LoSLBZPlcyqIzp+6ZcjOH0ZIgAkZc17PhShAVtkI3RH0Q5X0B9tQddfxVJ +g0rbsVmsDHN58Rqrz6ggdZEXbIiDbW30QhRSV1L9AoGBAKUuj3NUd2595U2eZQyP +IUmTSUrS4sK8zBdGGkSDfDZlyUHf2drz6h6650YVY78h0GHIivJj39PoSFGgD1Oa +ec0LF6hFiPDStNHtZHbShPxlIQSyOjm938EBmhAbSkJDzbDadVbBJlmVBY8a08ep +mvCcYE/i1K/t6tO/lZ6NJi82 +-----END PRIVATE KEY----- diff --git a/business/config/web.json b/business/config/web.json index 1935175..862f5c9 100644 --- a/business/config/web.json +++ b/business/config/web.json @@ -24,7 +24,7 @@ "session-timeout": 60, "system": { "addr": "127.0.0.1:18005", - "domain": "http://49.232.45.133:18005", + "domain": "https://test-pet-house.ifish7.com", "db-type": "mysql", "level": "debug", "time-format": "2006-01-02 15:04:05", diff --git a/business/config/wxconfig.json b/business/config/wxconfig.json index f0c82a5..23d65bd 100644 --- a/business/config/wxconfig.json +++ b/business/config/wxconfig.json @@ -3,5 +3,10 @@ "secret": "a29358be9ca3e34fa23d23994f5a386a", "token": "", "aesKey": "", - "msgDataFormat": "" + "msgDataFormat": "", + "mchId": "1682637612", + "mchCertificateSerialNumber": "7DA2108120E4C418537BCF3C061AAB109CCF88A9", + "mchAPIv3Key": "chonghujiapanlei1234567891234567", + "privateKeyPath": "F:\\work\\go\\pet-house\\business\\config\\apiclient_key.pem", + "wechatCert": "MIIEKzCCAxOgAwIBAgIUfaIQgSDkxBhTe888BhqrEJzPiKkwDQYJKoZIhvcNAQELBQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsTFFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3QgQ0EwHhcNMjQwOTEwMDc0NDUzWhcNMjkwOTA5MDc0NDUzWjCBhDETMBEGA1UEAwwKMTY4MjYzNzYxMjEbMBkGA1UECgwS5b6u5L+h5ZWG5oi357O757ufMTAwLgYDVQQLDCflroHms6LlrqDmiqTlrrblrqDnianmnI3liqHmnInpmZDlhazlj7gxCzAJBgNVBAYTAkNOMREwDwYDVQQHDAhTaGVuWmhlbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALoZ/ZK7BdW1DKCzsqQnS9ElkWhv8Df4JidAupVoaaGa0Iz8cukj8JgqzzmOA0fSief3z0OvOvmXQujNLXka38Y+8KB/K/BnqRYVGSvLWKwfdalSS+caJZUNLVQVv1zDrPVPb3JmoJMADPIq2sOfLqjmWPIxRcpa7sPeall5fMx07aVrpi2Ov1ueRUphIYNT6aV6KnPrRf/lcRwZxE0+l30l45sDNrd7l5tUsnJq/1OLP+kgidkpic6W1f3jrHZTwYakgShEUgkRsHeUjIQ0ZRYSsD4Mxn/bqdAxDSDWB6aWGwMrY1kubsPkE/AcXTpgpaC+2kQVhaYrWuly0w0T1jMCAwEAAaOBuTCBtjAJBgNVHRMEAjAAMAsGA1UdDwQEAwID+DCBmwYDVR0fBIGTMIGQMIGNoIGKoIGHhoGEaHR0cDovL2V2Y2EuaXRydXMuY29tLmNuL3B1YmxpYy9pdHJ1c2NybD9DQT0xQkQ0MjIwRTUwREJDMDRCMDZBRDM5NzU0OTg0NkMwMUMzRThFQkQyJnNnPUhBQ0M0NzFCNjU0MjJFMTJCMjdBOUQzM0E4N0FEMUNERjU5MjZFMTQwMzcxMA0GCSqGSIb3DQEBCwUAA4IBAQC7Zgux40pvNohZx721rygjo0yUbx54docNS79Eh5J9ukSKMf0cTsORlAnrsCbSYnIoMiqdPqccRYJUefUUuhOrKtuzaka4T+VSiaDTnZ07kB3I8xlvZtW32jHOstVPrJsBfEg53sl5sfWHY+OfuEhwOkoH3v/Fs4nH4hQxjO8RW9tNXqLvTrl1JeEwV4bMZxCAwCvoMPqn19WBFsaFb0kXF46oGeH5kId6oon0RCi+vXb8nSZ4oPXIJwxi9u/uy+wDrVlHohjCK+bRbxGgaIcX5uMPK/M73mzuuLFCbIWN4CLz9yyZGKxNJBq4YuGe+jWagh1GheVuvTdcJ4ZGDhQT" } \ No newline at end of file diff --git a/business/go.mod b/business/go.mod index 9e78790..47f0f98 100644 --- a/business/go.mod +++ b/business/go.mod @@ -82,6 +82,7 @@ require ( github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect + github.com/wechatpay-apiv3/wechatpay-go v0.2.20 // indirect github.com/yosssi/ace v0.0.5 // indirect github.com/ziutek/mymysql v1.5.4 // indirect go.uber.org/multierr v1.10.0 // indirect diff --git a/business/go.sum b/business/go.sum index f739da9..65aaf35 100644 --- a/business/go.sum +++ b/business/go.sum @@ -12,6 +12,7 @@ github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8L github.com/OwnLocal/goes v1.0.0/go.mod h1:8rIFjBGTue3lCU0wplczcUgt9Gxgrkkrw7etMIcn8TM= github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06 h1:KkH3I3sJuOLP3TjA/dfr4NAY8bghDwnXiU7cTKxQqo0= github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06/go.mod h1:7erjKLwalezA0k99cWs5L11HWOAPNjdUZ6RxH1BXbbM= +github.com/agiledragon/gomonkey v2.0.2+incompatible/go.mod h1:2NGfXu1a80LLr2cmWXGBDaHEjb1idR6+FVlX5T3D9hw= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= @@ -219,6 +220,7 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= @@ -237,6 +239,8 @@ github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IU github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= +github.com/wechatpay-apiv3/wechatpay-go v0.2.20 h1:gS8oFn1bHGnyapR2Zb4aqTV6l4kJWgbtqjCq6k1L9DQ= +github.com/wechatpay-apiv3/wechatpay-go v0.2.20/go.mod h1:A254AUBVB6R+EqQFo3yTgeh7HtyqRRtN2w9hQSOrd4Q= github.com/wendal/errors v0.0.0-20130201093226-f66c77a7882b/go.mod h1:Q12BUT7DqIlHRmgv3RskH+UCM/4eqVMgI0EMmlSpAXc= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= diff --git a/business/models/dataModel.go b/business/models/dataModel.go index 797b108..2b68978 100644 --- a/business/models/dataModel.go +++ b/business/models/dataModel.go @@ -117,7 +117,7 @@ type SystemConfig struct { Id int `gorm:"primaryKey;autoIncrement" json:"id"` //id Name string `json:"name"` //名称 Content string `json:"content"` //内容 - ConfigType string `json:"configType"` //类型 0banner 1关于我们 2客服电话 + ConfigType string `json:"configType"` //类型 0banner 1关于我们 2客服电话 3分享信息 ContentType int `json:"contentType"` //1文本 2链接 Sort int `json:"sort"` //排序位 JumpType int `gorm:"default:0" json:"JumpType"` //跳转类型 0无法跳转 1跳转外部 2跳转内部 @@ -143,6 +143,7 @@ type OrderMain struct { PayDiscount int `gorm:"default:0" json:"payDiscount"` //支付折扣 PayType int `json:"payType"` //支付方式 1线下 2线上 3会员余额 Status int `json:"status1"` //信息状态 + PayOrderId string `json:"payOrderId"` //支付订单号 } // OrderSub 子订单 @@ -166,6 +167,8 @@ type OrderSub struct { CreateTime time.Time `gorm:"type:timestamp;default:CURRENT_TIMESTAMP" json:"createTime"` //创建时间 UpdateTime time.Time `gorm:"type:timestamp;default:CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP" json:"-"` //更新时间 Status int `json:"status1"` + Cid int `gorm:"not null" json:"cid"` //优惠券ID + Ucid int `gorm:"not null" json:"ucid"` //优惠券ID - user } // OrderDetail 订单明细 @@ -273,27 +276,58 @@ type AddrServiceTime struct { // Coupons 优惠券 type Coupons struct { - Id int `gorm:"primaryKey;autoIncrement" json:"id"` //id - Name string `gorm:"not null" json:"name"` - GoodsSubType int `gorm:"not null" json:"goodsSubType"` //商品子类型 - Discount int `gorm:"not null" json:"discount"` //折扣 - Source string `gorm:"not null" json:"source"` //来源 - PeriodType int `gorm:"not null" json:"periodType"` //使用类型 0、无 1、星期 2、截至时间 3、指定日期可用 - Period string `json:"-"` //使用信息 具体时间 星期类型:1,2,3截止日期:2024-09-01 23:59:59指定日期:2024-08-10 - PeriodInfo []string `gorm:"-" json:"periodInfo"` - ExpireTime int64 `json:"expireTime"` //优惠券过期时间 - CreateTime time.Time `gorm:"type:timestamp;default:CURRENT_TIMESTAMP" json:"createTime"` //创建时间 - UpdateTime time.Time `gorm:"type:timestamp;default:CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP" json:"-"` //更新时间 - Status int `gorm:"default:1" json:"-"` //数据状态 - SendStatus int `gorm:"not null" json:"-"` //下发类型 1领取 2新用户注册赠送 3邀请新用户赠送 + Id int `gorm:"primaryKey;autoIncrement" json:"id"` //id + Name string `gorm:"not null" json:"name"` + GoodsSubType int `gorm:"not null" json:"goodsSubType"` //商品子类型 1:洗护 2:美容 3:SPA 4:驱虫 5:洁牙 + Discount int `gorm:"not null" json:"discount"` //折扣 + Source string `gorm:"not null" json:"source"` //来源 + PeriodType int `gorm:"not null" json:"periodType"` //使用类型 0、无 1、星期 2、截至时间 3、指定日期可用 + Period string `json:"-"` //使用信息 具体时间 星期类型:1,2,3截止日期:2024-09-01 23:59:59指定日期:2024-08-10 + PeriodInfo []string `gorm:"-" json:"periodInfo"` + ExpireTime int64 `json:"expireTime"` //优惠券过期时间 + CreateTime time.Time `gorm:"type:timestamp;default:CURRENT_TIMESTAMP" json:"createTime"` //创建时间 + UpdateTime time.Time `gorm:"type:timestamp;default:CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP" json:"-"` //更新时间 + Status int `gorm:"default:1" json:"-"` //数据状态 + SendStatus int `gorm:"not null" json:"-"` //下发类型 1领取 2新用户注册赠送 3邀请新用户赠送 + ReceiverStatus int `gorm:"-" json:"receiverStatus"` //领取状态 0未领取 1已领取 + DrawDay int `gorm:"not null" json:"drawDay"` //优惠券领取后过期天数 + LastExpireTime string `gorm:"-" json:"lastExpireTime"` //优惠券最后过期时间 } +// UserCoupons 用户优惠券 type UserCoupons struct { - Id int `gorm:"primaryKey;autoIncrement" json:"id"` //id - Uid int64 `gorm:"not null" json:"uid"` //用户id - Cid int `gorm:"not null" json:"cid"` //优惠券ID - CreateTime time.Time `gorm:"type:timestamp;default:CURRENT_TIMESTAMP" json:"createTime"` //创建时间 - UpdateTime time.Time `gorm:"type:timestamp;default:CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP" json:"-"` //更新时间 - Status int `gorm:"default:1" json:"-"` - CouponsStatus int `gorm:"default:1" json:"status"` //状态 1未使用 2已使用 3已过期 + Id int `gorm:"primaryKey;autoIncrement" json:"id"` //id + Uid int64 `gorm:"not null" json:"uid"` //用户id + Cid int `gorm:"not null" json:"cid"` //优惠券ID + CreateTime time.Time `gorm:"type:timestamp;default:CURRENT_TIMESTAMP" json:"createTime"` //创建时间 + UpdateTime time.Time `gorm:"type:timestamp;default:CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP" json:"-"` //更新时间 + Status int `gorm:"default:1" json:"-"` + CouponsStatus int `gorm:"default:1" json:"status"` //状态 1未使用 2已使用 3已过期 + LastExpireTime time.Time `gorm:"not null" json:"lastExpireTime"` //优惠券最后过期时间 +} + +// RechargeInfo 充值挡位信息 +type RechargeInfo struct { + Id int `gorm:"primaryKey;autoIncrement" json:"id"` //id + Name string `gorm:"not null" json:"name"` //挡位名称 + Desc string `json:"desc"` //挡位描述 + Price string `gorm:"not null" json:"price"` //价格 + Give string `gorm:"default:无" json:"give"` //赠送 + CreateTime time.Time `gorm:"type:timestamp;default:CURRENT_TIMESTAMP" json:"-"` //创建时间 + UpdateTime time.Time `gorm:"type:timestamp;default:CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP" json:"-"` //更新时间 + Status int `gorm:"default:1" json:"-" json:"-"` //状态 + Type int `gorm:"default:1" json:"type" json:"type"` //类型 1充值挡位 2押金 +} + +// PayOrder 支付订单 +type PayOrder struct { + Id int `gorm:"primaryKey;autoIncrement"` //id + OrderId string `gorm:"not null"` //订单ID + OrderName string `gorm:"not null"` //订单名称 + OrderPrice string `gorm:"not null"` //订单金额 + OrderStatus int `gorm:"not null;default:0"` //订单状态 0:未支付 1:已支付 2:已退款 + PayId string //三方交易号 + CreateTime time.Time `gorm:"type:timestamp;default:CURRENT_TIMESTAMP" json:"createTime"` //创建时间 + UpdateTime time.Time `gorm:"type:timestamp;default:CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP" json:"-"` //更新时间 + Status int `gorm:"default:1" json:"-"` //状态 } diff --git a/business/utils/wechat.go b/business/utils/wechat.go index 0f9808f..50c02ed 100644 --- a/business/utils/wechat.go +++ b/business/utils/wechat.go @@ -2,28 +2,48 @@ package utils import ( "fmt" + "github.com/bwmarrin/snowflake" "github.com/dgb8901/go-wechat-miniapp-sdk/config" "github.com/dgb8901/go-wechat-miniapp-sdk/models/response" "github.com/dgb8901/go-wechat-miniapp-sdk/service" "github.com/spf13/viper" + "github.com/wechatpay-apiv3/wechatpay-go/core" + "github.com/wechatpay-apiv3/wechatpay-go/core/option" + "github.com/wechatpay-apiv3/wechatpay-go/services/refunddomestic" + "github.com/wechatpay-apiv3/wechatpay-go/utils" + "go.uber.org/zap" + "golang.org/x/net/context" + "log" "pet-house.com/core/g" "pet-house.com/core/server/viper_server" + "pet-house.com/core/server/web" + "pet-house.com/core/server/zap_server" ) var CONFIG = WxConfig{ - AppId: "", - Secret: "", - Token: "", - AesKey: "", - MsgDataFormat: "", + AppId: "", + Secret: "", + Token: "", + AesKey: "", + MsgDataFormat: "", + MchId: "", + MchCertificateSerialNumber: "", + MchAPIv3Key: "", + PrivateKeyPath: "", + WechatCert: "", } type WxConfig struct { - AppId string `mapstructure:"appId" json:"appId" yaml:"appId"` - Secret string `mapstructure:"secret" json:"secret" yaml:"secret"` - Token string `mapstructure:"token" json:"token" yaml:"token"` - AesKey string `mapstructure:"aesKey" json:"aesKey" yaml:"aesKey"` - MsgDataFormat string `mapstructure:"msgDataFormat" json:"msgDataFormat" yaml:"msgDataFormat"` + AppId string `mapstructure:"appId" json:"appId" yaml:"appId"` + Secret string `mapstructure:"secret" json:"secret" yaml:"secret"` + Token string `mapstructure:"token" json:"token" yaml:"token"` + AesKey string `mapstructure:"aesKey" json:"aesKey" yaml:"aesKey"` + MsgDataFormat string `mapstructure:"msgDataFormat" json:"msgDataFormat" yaml:"msgDataFormat"` + MchId string `mapstructure:"mchId" json:"mchId" yaml:"mchId"` + MchCertificateSerialNumber string `mapstructure:"mchCertificateSerialNumber" json:"mchCertificateSerialNumber" yaml:"mchCertificateSerialNumber"` + MchAPIv3Key string `mapstructure:"mchAPIv3Key" json:"mchAPIv3Key" yaml:"mchAPIv3Key"` + PrivateKeyPath string `mapstructure:"privateKeyPath" json:"privateKeyPath" yaml:"privateKeyPath"` + WechatCert string `mapstructure:"wechatCert" json:"wechatCert" yaml:"wechatCert"` } // getViperConfig get viper config @@ -49,12 +69,18 @@ func getViperConfig() viper_server.ViperConfig { "token": "` + CONFIG.Token + `", "aesKey": "` + CONFIG.AesKey + `", "msgDataFormat": ` + CONFIG.MsgDataFormat + ` + "mchId": ` + CONFIG.MchId + ` + "mchCertificateSerialNumber": ` + CONFIG.MchCertificateSerialNumber + ` + "mchAPIv3Key": ` + CONFIG.MchAPIv3Key + ` + "privateKeyPath": ` + CONFIG.PrivateKeyPath + ` + "wechatCert": ` + CONFIG.WechatCert + ` }`), } } type WechatHelper struct { - wechatService *service.WxaService + wechatService *service.WxaService + wechatPayClient *core.Client } var wxHelper = &WechatHelper{} @@ -71,12 +97,36 @@ func WechatInit() { wxaConfig := config.NewInMemory(cfg) wxaService := service.NewService(wxaConfig) wxHelper.wechatService = wxaService + getPayApi() +} + +func getPayApi() { + // 使用 utils 提供的函数从本地文件中加载商户私钥,商户私钥会用来生成请求的签名 + mchPrivateKey, err := utils.LoadPrivateKeyWithPath(CONFIG.PrivateKeyPath) + if err != nil { + log.Fatal("load merchant private key error") + } + + ctx := context.Background() + // 使用商户私钥等初始化 client,并使它具有自动定时获取微信支付平台证书的能力 + opts := []core.ClientOption{ + option.WithWechatPayAutoAuthCipher(CONFIG.MchId, CONFIG.MchCertificateSerialNumber, mchPrivateKey, CONFIG.MchAPIv3Key), + } + client, err := core.NewClient(ctx, opts...) + if err != nil { + log.Fatalf("new wechat pay client err:%s", err) + } + wxHelper.wechatPayClient = client } func getWxaService() *service.WxaService { return wxHelper.wechatService } +func GetWxPayService() *core.Client { + return wxHelper.wechatPayClient +} + type WxUserInfo struct { OpenId string UnionId string @@ -89,3 +139,33 @@ func GetWxUserInfo(code string) (*response.JsCode2SessionResult, error) { } return info, nil } + +func WxPayRefund(payId string, payOrderId string, orderAmount int64, ctx context.Context) { + ras := refunddomestic.RefundsApiService{Client: wxHelper.wechatPayClient} + var refundNo = new(string) + reason := "取消订单" + currency := "CNY" + notifyUrl := web.CONFIG.System.Domain + "/pet-house/pay/payNotify" + + amount := refunddomestic.AmountReq{ + Refund: &orderAmount, + Total: &orderAmount, + Currency: ¤cy, + } + var NextId, _ = snowflake.NewNode(1) + *refundNo = NextId.Generate().String() + var req = refunddomestic.CreateRequest{ + TransactionId: &payId, + OutTradeNo: &payOrderId, + OutRefundNo: refundNo, + Reason: &reason, + NotifyUrl: ¬ifyUrl, + Amount: &amount, + } + createResult, _, err := ras.Create(ctx, req) + if err != nil { + return + } + zap_server.ZAPLOG.Info("WxPayRefund", zap.Any("request", req), zap.Any("response", createResult)) + +}