From 73e41552e44b1bf59b01155ac2910919932cef2f Mon Sep 17 00:00:00 2001 From: wucongxing8150 <815046773@qq.com> Date: Mon, 29 Jul 2024 08:58:07 +0800 Subject: [PATCH] 2 --- api/controller/CallBack.go | 195 +++++++++++++++++++++++++++++++++- api/controller/Login.go | 79 +++++++++++++- api/controller/OrderMember.go | 65 ++++++++++++ api/controller/OrderSingle.go | 53 +++++++++ api/controller/Question.go | 2 +- api/dao/OrderMember.go | 4 +- api/dao/OrderSingle.go | 4 +- api/dto/Login.go | 38 ++++++- api/dto/OrderMember.go | 25 ++++- api/dto/OrderSingle.go | 23 ++++ api/requests/Login.go | 14 ++- api/router/router.go | 9 ++ api/service/OrderMember.go | 2 +- api/service/OrderSingle.go | 36 ++++++- api/service/User.go | 23 ++++ extend/weChat/base.go | 65 ++++++++++++ extend/weChat/parseNotify.go | 12 +-- go.mod | 1 + go.sum | 2 + utils/intToString.go | 14 +++ 20 files changed, 638 insertions(+), 28 deletions(-) diff --git a/api/controller/CallBack.go b/api/controller/CallBack.go index 380753d..b05af41 100644 --- a/api/controller/CallBack.go +++ b/api/controller/CallBack.go @@ -2,9 +2,14 @@ package controller import ( "github.com/gin-gonic/gin" + "hepa-calc-api/api/dao" "hepa-calc-api/api/responses" + "hepa-calc-api/api/service" "hepa-calc-api/extend/weChat" + "hepa-calc-api/global" "hepa-calc-api/utils" + "net/http" + "time" ) // CallBack 回调 @@ -12,7 +17,7 @@ type CallBack struct{} // WxPaySingle 微信支付回调-单项 func (r *CallBack) WxPaySingle(c *gin.Context) { - notifyReq, err := weChat.ParseNotify(c) + notifyReq, transaction, err := weChat.ParseNotify(c) if err != nil { responses.FailWithMessage(err.Error(), c) return @@ -20,7 +25,195 @@ func (r *CallBack) WxPaySingle(c *gin.Context) { // 记录日志 utils.LogJsonErr("微信支付回调-单项", notifyReq) + utils.LogJsonErr("微信支付回调-单项", transaction) + + if transaction == nil { + c.JSON(http.StatusBadRequest, gin.H{"code": "ERROR", "message": "缺少订单数据"}) + return + } + + if transaction.OutTradeNo == nil { + c.JSON(http.StatusBadRequest, gin.H{"code": "ERROR", "message": "缺少外部订单号"}) + return + } // 查询订单 + orderSingleDao := dao.OrderSingleDao{} + maps := make(map[string]interface{}) + maps["order_no"] = transaction.OutTradeNo + orderSingle, err := orderSingleDao.GetOrderSingle(maps) + if err != nil || orderSingle == nil { + c.JSON(http.StatusBadRequest, gin.H{"code": "ERROR", "message": "无订单数据"}) + return + } + // 验证订单状态-订单状态(1:待支付 2:已完成 3:已取消) + if orderSingle.OrderStatus != 1 { + message := "订单状态:" + utils.OrderSingleStatusToString(orderSingle.OrderStatus) + " 无需处理" + c.JSON(http.StatusOK, gin.H{"code": "SUCCESS", "message": message}) + return + } + + // 处理支付状态 + if transaction.TradeState == nil { + c.JSON(http.StatusBadRequest, gin.H{"code": "ERROR", "message": "缺少支付状态"}) + return + } + + // 处理支付状态 + wxPayResult, err := weChat.HandlePayStatus(transaction) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"code": "ERROR", "message": err}) + return + } + + // 开始事务 + tx := global.Db.Begin() + defer func() { + if r := recover(); r != nil { + tx.Rollback() + } + }() + + // 修改订单 + orderSingleData := make(map[string]interface{}) + if wxPayResult.OrderStatus != nil { + orderSingleData["order_status"] = &wxPayResult.OrderStatus + } + orderSingleData["pay_status"] = &wxPayResult.PayStatus + if wxPayResult.PayTime != nil { + orderSingleData["pay_time"] = &wxPayResult.PayTime + } + orderSingleData["escrow_trade_no"] = transaction.TransactionId + orderSingleData["updated_at"] = time.Now().Format("2006-01-02 15:04:05") + err = orderSingleDao.EditOrderSingleById(tx, orderSingle.OrderId, orderSingleData) + if err != nil { + tx.Rollback() + c.JSON(http.StatusBadRequest, gin.H{"code": "ERROR", "message": err}) + return + } + + tx.Commit() + c.JSON(http.StatusOK, gin.H{"code": "SUCCESS", "message": "OK"}) +} + +// WxPayMember 微信支付回调-会员 +func (r *CallBack) WxPayMember(c *gin.Context) { + notifyReq, transaction, err := weChat.ParseNotify(c) + if err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + // 记录日志 + utils.LogJsonErr("微信支付回调-会员", notifyReq) + utils.LogJsonErr("微信支付回调-会员", transaction) + + if transaction == nil { + c.JSON(http.StatusBadRequest, gin.H{"code": "ERROR", "message": "缺少订单数据"}) + return + } + + if transaction.OutTradeNo == nil { + c.JSON(http.StatusBadRequest, gin.H{"code": "ERROR", "message": "缺少外部订单号"}) + return + } + + // 查询订单 + orderMemberDao := dao.OrderMemberDao{} + maps := make(map[string]interface{}) + maps["order_no"] = transaction.OutTradeNo + orderMember, err := orderMemberDao.GetOrderMember(maps) + if err != nil || orderMember == nil { + c.JSON(http.StatusBadRequest, gin.H{"code": "ERROR", "message": "无订单数据"}) + return + } + + // 验证订单状态-订单状态(1:待支付 2:已完成 3:已取消) + if orderMember.OrderStatus != 1 { + message := "订单状态:" + utils.OrderSingleStatusToString(orderMember.OrderStatus) + " 无需处理" + c.JSON(http.StatusOK, gin.H{"code": "SUCCESS", "message": message}) + return + } + + // 处理支付状态 + if transaction.TradeState == nil { + c.JSON(http.StatusBadRequest, gin.H{"code": "ERROR", "message": "缺少支付状态"}) + return + } + + // 处理支付状态 + wxPayResult, err := weChat.HandlePayStatus(transaction) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"code": "ERROR", "message": err}) + return + } + + // 获取用户数据 + userDao := dao.UserDao{} + user, err := userDao.GetUserById(orderMember.UserId) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"code": "ERROR", "message": "用户数据错误"}) + return + } + + // 开始事务 + tx := global.Db.Begin() + defer func() { + if r := recover(); r != nil { + tx.Rollback() + } + }() + + // 修改订单 + orderSingleData := make(map[string]interface{}) + if wxPayResult.OrderStatus != nil { + orderSingleData["order_status"] = &wxPayResult.OrderStatus + } + orderSingleData["pay_status"] = &wxPayResult.PayStatus + if wxPayResult.PayTime != nil { + orderSingleData["pay_time"] = &wxPayResult.PayTime + } + orderSingleData["escrow_trade_no"] = transaction.TransactionId + orderSingleData["updated_at"] = time.Now().Format("2006-01-02 15:04:05") + err = orderMemberDao.EditOrderMemberById(tx, orderMember.OrderId, orderSingleData) + if err != nil { + tx.Rollback() + c.JSON(http.StatusInternalServerError, gin.H{"code": "ERROR", "message": err}) + return + } + + // 处理未支付单项订单 + if *wxPayResult.OrderStatus == 2 { + orderSingleService := service.OrderSingleService{} + res, err := orderSingleService.CompleteUnPayOrderSingle(tx, orderMember.UserId) + if err != nil || res == false { + tx.Rollback() + c.JSON(http.StatusInternalServerError, gin.H{"code": "ERROR", "message": err}) + return + } + } + + // 处理用户会员过期时间 + if *wxPayResult.OrderStatus == 2 { + // 获取订单配置 + systemMemberDao := dao.SystemMemberDao{} + systemMember, err := systemMemberDao.GetSystemMemberById(orderMember.SystemMemberId) + if err != nil { + tx.Rollback() + c.JSON(http.StatusInternalServerError, gin.H{"code": "ERROR", "message": "会员配置错误"}) + return + } + + userService := service.UserService{} + res := userService.AddUserMemberValidDate(tx, user, int(systemMember.MemberDays)) + if res == false { + tx.Rollback() + c.JSON(http.StatusInternalServerError, gin.H{"code": "ERROR", "message": "增加用户会员到期时间失败"}) + return + } + } + + tx.Commit() + c.JSON(http.StatusOK, gin.H{"code": "SUCCESS", "message": "OK"}) } diff --git a/api/controller/Login.go b/api/controller/Login.go index c9de67f..2f0ae59 100644 --- a/api/controller/Login.go +++ b/api/controller/Login.go @@ -12,7 +12,6 @@ import ( "hepa-calc-api/extend/weChat" "hepa-calc-api/global" "hepa-calc-api/utils" - "strconv" "time" ) @@ -20,18 +19,89 @@ type Login struct{} // LoginPhone 手机号登录 func (r *Login) LoginPhone(c *gin.Context) { + loginRequest := requests.LoginRequest{} + req := loginRequest.LoginPhone + if err := c.ShouldBind(&req); err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + // 参数验证 + if err := global.Validate.Struct(req); err != nil { + responses.FailWithMessage(utils.Translate(err), c) + return + } + + // 检测用户信息 + userDao := dao.UserDao{} + maps := make(map[string]interface{}) + maps["mobile"] = req.Mobile + user, _ := userDao.GetUser(maps) + // 新用户处理方式 + if user == nil { + // 开始事务 + tx := global.Db.Begin() + defer func() { + if r := recover(); r != nil { + fmt.Println(r) + tx.Rollback() + } + }() + + avatar := "www.baidu.com" + + // 新增用户 + user = &model.User{ + UserName: "", + Mobile: req.Mobile, + RegisterSource: req.Source, + OpenId: "", + UnionId: "", + Age: nil, + Sex: 0, + Avatar: avatar, + IsMember: 0, + MemberExpireDate: nil, + LoginAt: model.LocalTime(time.Now()), + LoginIp: req.LoginIp, + } + user, err := userDao.AddUser(tx, user) + if err != nil { + tx.Rollback() + responses.FailWithMessage(err.Error(), c) + return + } + + fmt.Println(user) + + // 获取app用户信息 + //var result *string + //if result == nil { + // // 新增app用户信息 + //} + + tx.Commit() + } + + // 下发token token := &utils.Token{ - UserId: strconv.FormatInt(1, 10), + UserId: fmt.Sprintf("%d", user.UserId), } // 生成jwt jwt, err := token.NewJWT() - if err != nil { + if err != nil || jwt == "" { responses.FailWithMessage("登陆失败", c) return } - responses.OkWithData(jwt, c) + // 处理返回值 + g := dto.LoginMobileDto(user) + + // 加载token + g.LoadToken(jwt) + + responses.OkWithData(g, c) } // LoginWx 微信授权登录 @@ -55,7 +125,6 @@ func (r *Login) LoginWx(c *gin.Context) { responses.FailWithMessage(err.Error(), c) return } - fmt.Println(webAccessToken) // 网页授权拉取用户信息 userInfo, err := weChat.GetUserInfo(webAccessToken.AccessToken, webAccessToken.OpenId) diff --git a/api/controller/OrderMember.go b/api/controller/OrderMember.go index d73c4bd..2a08407 100644 --- a/api/controller/OrderMember.go +++ b/api/controller/OrderMember.go @@ -214,3 +214,68 @@ func (b *OrderMember) GetOrderMemberPay(c *gin.Context) { responses.OkWithData(g, c) } + +// GetOrderMemberPayStatus 查询订单支付状态-会员 +func (b *OrderMember) GetOrderMemberPayStatus(c *gin.Context) { + userId := c.GetInt64("UserId") + + id := c.Param("order_id") + if id == "" { + responses.FailWithMessage("缺少参数", c) + return + } + + // 将 id 转换为 int64 类型 + orderId, err := strconv.ParseInt(id, 10, 64) + if err != nil { + responses.Fail(c) + return + } + + // 获取订单数据 + orderMemberDao := dao.OrderMemberDao{} + maps := make(map[string]interface{}) + maps["user_id"] = userId + maps["order_id"] = orderId + orderMember, err := orderMemberDao.GetOrderMember(maps) + if err != nil { + responses.FailWithMessage("订单异常", c) + return + } + + // 处理返回值 + g := dto.GetOrderMemberPayStatus(orderMember) + + // 处理有效期 + if g.PayStatus == 2 { + // 获取单项配置 + systemMemberDao := dao.SystemMemberDao{} + systemMember, err := systemMemberDao.GetSystemMemberById(orderMember.SystemMemberId) + if err != nil { + responses.FailWithMessage("内部错误", c) + return + } + + // 获取用户数据 + userDao := dao.UserDao{} + user, err := userDao.GetUserById(orderMember.UserId) + if err != nil { + responses.FailWithMessage("用户数据错误", c) + return + } + + // 获取有效期 + var validTime time.Time + if user.MemberExpireDate != nil { + validTime = user.MemberExpireDate.Add(time.Duration(systemMember.MemberDays) * 24 * time.Hour) + } else { + now := time.Now() + validTime = now.Add(time.Duration(systemMember.MemberDays) * 24 * time.Hour) + } + + // 加载到期时间 + g.LoadValidDate(&validTime) + } + + responses.OkWithData(g, c) +} diff --git a/api/controller/OrderSingle.go b/api/controller/OrderSingle.go index 9c1edfe..6aef00b 100644 --- a/api/controller/OrderSingle.go +++ b/api/controller/OrderSingle.go @@ -334,3 +334,56 @@ func (b *OrderSingle) DeleteOrderSingle(c *gin.Context) { tx.Commit() responses.Ok(c) } + +// GetOrderSinglePayStatus 查询订单支付状态-单项 +func (b *OrderSingle) GetOrderSinglePayStatus(c *gin.Context) { + userId := c.GetInt64("UserId") + + id := c.Param("order_id") + if id == "" { + responses.FailWithMessage("缺少参数", c) + return + } + + // 将 id 转换为 int64 类型 + orderId, err := strconv.ParseInt(id, 10, 64) + if err != nil { + responses.Fail(c) + return + } + + // 获取订单数据 + orderSingleDao := dao.OrderSingleDao{} + maps := make(map[string]interface{}) + maps["user_id"] = userId + maps["order_id"] = orderId + orderSingle, err := orderSingleDao.GetOrderSingle(maps) + if err != nil { + responses.FailWithMessage("订单异常", c) + return + } + + // 处理返回值 + g := dto.GetOrderSinglePayStatus(orderSingle) + + // 处理有效期 + if g.PayStatus == 2 { + // 获取单项配置 + systemSingleDao := dao.SystemSingleDao{} + + maps = make(map[string]interface{}) + systemSingle, err := systemSingleDao.GetSystemSingle(maps) + if err != nil { + responses.FailWithMessage("内部错误", c) + return + } + + // 获取有效期 + validTime := orderSingle.PayTime.Add(time.Duration(systemSingle.ValidDays) * 24 * time.Hour) + + // 加载到期时间 + g.LoadValidDate(&validTime) + } + + responses.OkWithData(g, c) +} diff --git a/api/controller/Question.go b/api/controller/Question.go index 065f1c1..7c51936 100644 --- a/api/controller/Question.go +++ b/api/controller/Question.go @@ -229,7 +229,7 @@ func (r *Question) GetQuestionBuyStatus(c *gin.Context) { // 判断是否还在有效期内 now := time.Now() - ValidTime := single.PayTime.Add(time.Duration(systemSingle.ValidDays)) + ValidTime := single.PayTime.Add(time.Duration(systemSingle.ValidDays) * 24 * time.Hour) if ValidTime.Before(now) { continue } diff --git a/api/dao/OrderMember.go b/api/dao/OrderMember.go index e31db41..dcab610 100644 --- a/api/dao/OrderMember.go +++ b/api/dao/OrderMember.go @@ -56,8 +56,8 @@ func (r *OrderMemberDao) EditOrderMember(tx *gorm.DB, maps interface{}, data int } // EditOrderMemberById 修改-id -func (r *OrderMemberDao) EditOrderMemberById(tx *gorm.DB, OrderMemberId int64, data interface{}) error { - err := tx.Model(&model.OrderMember{}).Where("order_id = ?", OrderMemberId).Updates(data).Error +func (r *OrderMemberDao) EditOrderMemberById(tx *gorm.DB, OrderId int64, data interface{}) error { + err := tx.Model(&model.OrderMember{}).Where("order_id = ?", OrderId).Updates(data).Error if err != nil { return err } diff --git a/api/dao/OrderSingle.go b/api/dao/OrderSingle.go index a3566b4..4f8c614 100644 --- a/api/dao/OrderSingle.go +++ b/api/dao/OrderSingle.go @@ -56,8 +56,8 @@ func (r *OrderSingleDao) EditOrderSingle(tx *gorm.DB, maps interface{}, data int } // EditOrderSingleById 修改-id -func (r *OrderSingleDao) EditOrderSingleById(tx *gorm.DB, OrderSingleId int64, data interface{}) error { - err := tx.Model(&model.OrderSingle{}).Where("order_id = ?", OrderSingleId).Updates(data).Error +func (r *OrderSingleDao) EditOrderSingleById(tx *gorm.DB, orderId int64, data interface{}) error { + err := tx.Model(&model.OrderSingle{}).Where("order_id = ?", orderId).Updates(data).Error if err != nil { return err } diff --git a/api/dto/Login.go b/api/dto/Login.go index 6796e08..448c718 100644 --- a/api/dto/Login.go +++ b/api/dto/Login.go @@ -7,7 +7,18 @@ import ( "time" ) -type LoginDto struct { +type WxDto struct { + UserId string `json:"user_id"` // 用户id + UserName string `json:"user_name"` // 用户名称 + Mobile string `json:"mobile"` // 手机号 + OpenId string `json:"open_id"` // 用户微信标识 + Avatar string `json:"avatar"` // 头像 + IsMember int `json:"is_member"` // 是否会员(0:否 1:是) + MemberExpireDate *time.Time `json:"member_expire_date"` // 会员到期时间(非会员时为null) + Token string `json:"token"` // token +} + +type MobileDto struct { UserId string `json:"user_id"` // 用户id UserName string `json:"user_name"` // 用户名称 Mobile string `json:"mobile"` // 手机号 @@ -19,8 +30,21 @@ type LoginDto struct { } // LoginWxDto 微信登陆 -func LoginWxDto(m *model.User) *LoginDto { - return &LoginDto{ +func LoginWxDto(m *model.User) *WxDto { + return &WxDto{ + UserId: fmt.Sprintf("%d", m.UserId), + UserName: m.UserName, + Mobile: m.Mobile, + OpenId: m.OpenId, + Avatar: utils.AddOssDomain(m.Avatar), + IsMember: m.IsMember, + MemberExpireDate: m.MemberExpireDate, + } +} + +// LoginMobileDto 手机号登陆 +func LoginMobileDto(m *model.User) *MobileDto { + return &MobileDto{ UserId: fmt.Sprintf("%d", m.UserId), UserName: m.UserName, Mobile: m.Mobile, @@ -32,7 +56,13 @@ func LoginWxDto(m *model.User) *LoginDto { } // LoadToken 加载token -func (r *LoginDto) LoadToken(token string) *LoginDto { +func (r *WxDto) LoadToken(token string) *WxDto { + r.Token = token + return r +} + +// LoadToken 加载token +func (r *MobileDto) LoadToken(token string) *MobileDto { r.Token = token return r } diff --git a/api/dto/OrderMember.go b/api/dto/OrderMember.go index d149bc2..dd7120a 100644 --- a/api/dto/OrderMember.go +++ b/api/dto/OrderMember.go @@ -46,6 +46,13 @@ type OrderMemberPayDto struct { PrepayApp *app.PrepayWithRequestPaymentResponse `json:"prepay_app"` // 预支付交易数据-app } +// OrderMemberPayStatusDto 会员订单支付状态 +type OrderMemberPayStatusDto struct { + PayStatus int `json:"pay_status"` // 支付状态(1:未支付 2:已支付 3:支付中 4:支付失败 5:支付超时 6:支付关闭 7:已撤销 8:转入退款) + PaymentAmountTotal float64 `json:"payment_amount_total"` // 实际付款金额 + ValidDate model.LocalTime `json:"valid_date"` // 到期时间 +} + // GetOrderMemberListDto 列表 func GetOrderMemberListDto(m []*model.OrderMember) []*OrderMemberDto { // 处理返回值 @@ -89,7 +96,7 @@ func GetOrderMemberListDto(m []*model.OrderMember) []*OrderMemberDto { return responses } -// GetOrderMemberPayDto 获取单项订单支付数据 +// GetOrderMemberPayDto 获取会员订单支付数据 func GetOrderMemberPayDto(m *model.OrderMember) *OrderMemberPayDto { return &OrderMemberPayDto{ OrderId: fmt.Sprintf("%d", m.OrderId), @@ -102,6 +109,14 @@ func GetOrderMemberPayDto(m *model.OrderMember) *OrderMemberPayDto { } } +// GetOrderMemberPayStatus 获取会员订单支付状态 +func GetOrderMemberPayStatus(m *model.OrderMember) *OrderMemberPayStatusDto { + return &OrderMemberPayStatusDto{ + PayStatus: m.PayStatus, + PaymentAmountTotal: m.PaymentAmountTotal, + } +} + // LoadSystemMember 加载会员数据 func (r *OrderMemberDto) LoadSystemMember(m *model.SystemMember) *OrderMemberDto { if m != nil { @@ -109,3 +124,11 @@ func (r *OrderMemberDto) LoadSystemMember(m *model.SystemMember) *OrderMemberDto } return r } + +// LoadValidDate 加载到期时间 +func (r *OrderMemberPayStatusDto) LoadValidDate(m *time.Time) *OrderMemberPayStatusDto { + if m != nil { + r.ValidDate = model.LocalTime(*m) + } + return r +} diff --git a/api/dto/OrderSingle.go b/api/dto/OrderSingle.go index 91b58b5..98b5629 100644 --- a/api/dto/OrderSingle.go +++ b/api/dto/OrderSingle.go @@ -46,6 +46,13 @@ type OrderSinglePayDto struct { PrepayApp *app.PrepayWithRequestPaymentResponse `json:"prepay_app"` // 预支付交易数据-app } +// OrderSinglePayStatusDto 单项订单支付状态 +type OrderSinglePayStatusDto struct { + PayStatus int `json:"pay_status"` // 支付状态(1:未支付 2:已支付 3:支付中 4:支付失败 5:支付超时 6:支付关闭 7:已撤销 8:转入退款) + PaymentAmountTotal float64 `json:"payment_amount_total"` // 实际付款金额 + ValidDate model.LocalTime `json:"valid_date"` // 到期时间 +} + // GetOrderSingleListDto 列表 func GetOrderSingleListDto(m []*model.OrderSingle) []*OrderSingleDto { // 处理返回值 @@ -102,6 +109,14 @@ func GetOrderSinglePayDto(m *model.OrderSingle) *OrderSinglePayDto { } } +// GetOrderSinglePayStatus 获取单项订单支付状态 +func GetOrderSinglePayStatus(m *model.OrderSingle) *OrderSinglePayStatusDto { + return &OrderSinglePayStatusDto{ + PayStatus: m.PayStatus, + PaymentAmountTotal: m.PaymentAmountTotal, + } +} + // LoadQuestion 加载题目数据 func (r *OrderSingleDto) LoadQuestion(m *model.Question) *OrderSingleDto { if m != nil { @@ -109,3 +124,11 @@ func (r *OrderSingleDto) LoadQuestion(m *model.Question) *OrderSingleDto { } return r } + +// LoadValidDate 加载到期时间 +func (r *OrderSinglePayStatusDto) LoadValidDate(m *time.Time) *OrderSinglePayStatusDto { + if m != nil { + r.ValidDate = model.LocalTime(*m) + } + return r +} diff --git a/api/requests/Login.go b/api/requests/Login.go index aaa51b1..60314ce 100644 --- a/api/requests/Login.go +++ b/api/requests/Login.go @@ -1,12 +1,20 @@ package requests type LoginRequest struct { - LoginWx // 微信授权登录 + LoginWx // 微信授权登录 + LoginPhone // 手机号登录 } // LoginWx 微信授权登录 type LoginWx struct { - Code string `json:"code" form:"code" label:"授权码"` - Source int `json:"source" form:"source" label:"来源(1:app 2:公众号)"` + Code string `json:"code" form:"code" label:"授权码" validate:"required"` + Source int `json:"source" form:"source" label:"来源" validate:"required,oneof=1 2"` // (1:app 2:公众号 + LoginIp string `json:"login_ip" form:"login_ip" label:"登录ip"` +} + +// LoginPhone 手机号登录 +type LoginPhone struct { + Mobile string `json:"mobile" form:"mobile" label:"手机号" validate:"required"` + Source int `json:"source" form:"source" label:"来源)" validate:"required"` // (1:app 2:公众号 LoginIp string `json:"login_ip" form:"login_ip" label:"登录ip"` } diff --git a/api/router/router.go b/api/router/router.go index 71bdab7..7b50c72 100644 --- a/api/router/router.go +++ b/api/router/router.go @@ -100,6 +100,9 @@ func publicRouter(r *gin.Engine, api controller.Api) { { // 单项 wxpayGroup.POST("/single", api.CallBack.WxPaySingle) + + // 会员 + wxpayGroup.POST("/member", api.CallBack.WxPayMember) } } } @@ -201,6 +204,9 @@ func privateRouter(r *gin.Engine, api controller.Api) { // 删除单项订单 singleGroup.DELETE("/:order_id", api.OrderSingle.DeleteOrderSingle) + + // 查询订单支付状态-单项 + singleGroup.GET("/pay/status/:order_id", api.OrderSingle.GetOrderSinglePayStatus) } // 会员订单 @@ -214,6 +220,9 @@ func privateRouter(r *gin.Engine, api controller.Api) { // 获取会员订单支付数据 memberGroup.GET("/pay/:order_id", api.OrderMember.GetOrderMemberPay) + + // 查询订单支付状态-会员 + memberGroup.GET("/pay/status/:order_id", api.OrderMember.GetOrderMemberPayStatus) } } diff --git a/api/service/OrderMember.go b/api/service/OrderMember.go index b46df16..4565109 100644 --- a/api/service/OrderMember.go +++ b/api/service/OrderMember.go @@ -21,7 +21,7 @@ type OrderMemberService struct { // AddOrderMember 创建会员订单 func (r *OrderMemberService) AddOrderMember(tx *gorm.DB, UserId, SystemMemberId int64, UserCouponId *int64, payChannel int, orderPrice float64) (orderMember *model.OrderMember, err error) { - // 检测多次请求 + // 检测并发请求 redisKey := "AddOrderMember" + fmt.Sprintf("%d", UserId) + fmt.Sprintf("%d", SystemMemberId) res, _ := global.Redis.Get(context.Background(), redisKey).Result() if res != "" { diff --git a/api/service/OrderSingle.go b/api/service/OrderSingle.go index bcbb595..ec0d9a2 100644 --- a/api/service/OrderSingle.go +++ b/api/service/OrderSingle.go @@ -20,8 +20,9 @@ type OrderSingleService struct { } // AddOrderSingle 创建单项订单 +// payChannel:支付渠道(1:h5支付 2:app支付 3:会员支付) func (r *OrderSingleService) AddOrderSingle(tx *gorm.DB, UserId, QuestionId int64, UserCouponId *int64, payChannel int, orderPrice float64) (orderSingle *model.OrderSingle, err error) { - // 检测多次请求 + // 检测并发请求 redisKey := "AddOrderSingle" + fmt.Sprintf("%d", UserId) + fmt.Sprintf("%d", QuestionId) res, _ := global.Redis.Get(context.Background(), redisKey).Result() if res != "" { @@ -343,3 +344,36 @@ func (r *OrderSingleService) GetAppPrepay(m *model.OrderSingle) (prepay *app.Pre return prepay, nil } + +// CompleteUnPayOrderSingle 完成未支付单项订单-开通会员成功时使用 +func (r *OrderSingleService) CompleteUnPayOrderSingle(tx *gorm.DB, userId int64) (bool, error) { + // 获取所有未支付单项订单 + orderSingleDao := dao.OrderSingleDao{} + maps := make(map[string]interface{}) + maps["user_id"] = userId + maps["order_status"] = 1 + maps["pay_status"] = 1 + maps["cancel_status"] = 0 + orderSingles, err := orderSingleDao.GetOrderSingleList(maps) + if err != nil { + return false, err + } + + for _, single := range orderSingles { + // 生成第三方支付流水号 + escrowTradeNo := "GD" + global.Snowflake.Generate().String() + + orderSingleData := make(map[string]interface{}) + orderSingleData["order_status"] = 2 + orderSingleData["pay_status"] = 2 + orderSingleData["pay_time"] = time.Now().Format("2006-01-02 15:04:05") + orderSingleData["escrow_trade_no"] = escrowTradeNo + orderSingleData["updated_at"] = time.Now().Format("2006-01-02 15:04:05") + err = orderSingleDao.EditOrderSingleById(tx, single.OrderId, orderSingleData) + if err != nil { + return false, err + } + } + + return true, nil +} diff --git a/api/service/User.go b/api/service/User.go index 3a80aa1..5fb5c6f 100644 --- a/api/service/User.go +++ b/api/service/User.go @@ -3,6 +3,7 @@ package service import ( "errors" "fmt" + "gorm.io/gorm" "hepa-calc-api/api/dao" "hepa-calc-api/api/model" "hepa-calc-api/extend/aliyun" @@ -82,3 +83,25 @@ func (r *UserService) CheckUserBuyOrderMember(userId int64) bool { return true } + +// AddUserMemberValidDate 增加用户会员过期时间 +func (r *UserService) AddUserMemberValidDate(tx *gorm.DB, user *model.User, d int) bool { + userData := make(map[string]interface{}) + if user.IsMember == 0 { + userData["is_member"] = 1 + } + + if user.MemberExpireDate == nil { + userData["is_member"] = time.Now().Format("2006-01-02 15:04:05") + } else { + userData["is_member"] = user.MemberExpireDate.Add(time.Duration(d) * 24 * time.Hour) + } + + userDao := dao.UserDao{} + err := userDao.EditUserById(tx, user.UserId, userData) + if err != nil { + return false + } + + return true +} diff --git a/extend/weChat/base.go b/extend/weChat/base.go index bceefdf..8cf036f 100644 --- a/extend/weChat/base.go +++ b/extend/weChat/base.go @@ -5,8 +5,10 @@ import ( "errors" "github.com/wechatpay-apiv3/wechatpay-go/core" "github.com/wechatpay-apiv3/wechatpay-go/core/option" + "github.com/wechatpay-apiv3/wechatpay-go/services/payments" "github.com/wechatpay-apiv3/wechatpay-go/utils" "hepa-calc-api/config" + "time" ) // 创建客户端 @@ -38,3 +40,66 @@ func createClient() (*core.Client, error) { return client, nil } + +type WxPayResult struct { + OrderStatus *int `json:"order_status"` // 订单状态(1:待支付 2:已完成 3:已取消) + PayStatus *int `json:"pay_status"` // 支付状态(1:未支付 2:已支付 3:支付中 4:支付失败 5:支付超时 6:支付关闭 7:已撤销 8:转入退款) + PayTime *time.Time `json:"pay_time"` // 支付时间 +} + +// HandlePayStatus 处理支付状态 +func HandlePayStatus(t *payments.Transaction) (w *WxPayResult, err error) { + // 支付成功 + if *t.TradeState == "SUCCESS" { + orderStatus := 2 + w.OrderStatus = &orderStatus + + payStatus := 2 + w.PayStatus = &payStatus + + parse, err := time.Parse("2006-01-02T15:04:05+07:00", *t.SuccessTime) + if err != nil { + return nil, errors.New("支付时间处理错误") + } + + w.PayTime = &parse + if err != nil { + return nil, errors.New("支付时间错误") + } + } + + switch *t.TradeState { + case "SUCCESS": // 支付成功 + orderStatus := 2 + w.OrderStatus = &orderStatus + + payStatus := 2 + w.PayStatus = &payStatus + + parse, err := time.Parse("2006-01-02T15:04:05+07:00", *t.SuccessTime) + if err != nil { + return nil, errors.New("支付时间处理错误") + } + + w.PayTime = &parse + if err != nil { + return nil, errors.New("支付时间错误") + } + case "CLOSED": // 已关闭 + payStatus := 6 + w.PayStatus = &payStatus + case "REVOKED": // 已撤销(付款码支付) + payStatus := 7 + w.PayStatus = &payStatus + case "USERPAYING": // 用户支付中(付款码支付) + payStatus := 3 + w.PayStatus = &payStatus + case "PAYERROR": // 支付失败(其他原因,如银行返回失败) + payStatus := 4 + w.PayStatus = &payStatus + default: + return nil, errors.New("未知支付状态") + } + + return w, nil +} diff --git a/extend/weChat/parseNotify.go b/extend/weChat/parseNotify.go index cebb91c..dc8b08b 100644 --- a/extend/weChat/parseNotify.go +++ b/extend/weChat/parseNotify.go @@ -3,7 +3,6 @@ package weChat import ( "context" "errors" - "fmt" "github.com/gin-gonic/gin" "github.com/wechatpay-apiv3/wechatpay-go/core/auth/verifiers" "github.com/wechatpay-apiv3/wechatpay-go/core/downloader" @@ -14,7 +13,7 @@ import ( ) // ParseNotify 回调通知的验签与解密 -func ParseNotify(c *gin.Context) (notifyReq *notify.Request, err error) { +func ParseNotify(c *gin.Context) (notifyReq *notify.Request, t *payments.Transaction, err error) { mchId := config.C.Wechat.Pay1659662936.MchId // 商户号 mchCertificateSerialNumber := config.C.Wechat.Pay1659662936.MchCertificateSerialNumber // 商户证书序列号 v3ApiSecret := config.C.Wechat.Pay1659662936.V3ApiSecret // 商户APIv3密钥 @@ -23,13 +22,13 @@ func ParseNotify(c *gin.Context) (notifyReq *notify.Request, err error) { // 使用 utils 提供的函数从本地文件中加载商户私钥,商户私钥会用来生成请求的签名 mchPrivateKey, err := utils.LoadPrivateKeyWithPath(privateKeyPath) if err != nil { - return nil, errors.New("微信支付生成失败") + return nil, nil, errors.New("微信支付生成失败") } // 1. 使用 `RegisterDownloaderWithPrivateKey` 注册下载器 err = downloader.MgrInstance().RegisterDownloaderWithPrivateKey(c, mchPrivateKey, mchCertificateSerialNumber, mchId, v3ApiSecret) if err != nil { - return nil, err + return nil, nil, err } // 2. 获取商户号对应的微信支付平台证书访问器 @@ -41,9 +40,8 @@ func ParseNotify(c *gin.Context) (notifyReq *notify.Request, err error) { notifyReq, err = handler.ParseNotifyRequest(context.Background(), c.Request, transaction) // 如果验签未通过,或者解密失败 if err != nil { - return nil, err + return nil, nil, err } - fmt.Println(transaction) - return notifyReq, nil + return notifyReq, transaction, nil } diff --git a/go.mod b/go.mod index bf0e907..333af8d 100644 --- a/go.mod +++ b/go.mod @@ -43,6 +43,7 @@ require ( github.com/cloudwego/base64x v0.1.4 // indirect github.com/cloudwego/iasm v0.2.0 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect + github.com/dustin/go-humanize v1.0.1 // indirect github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a // indirect github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c // indirect github.com/facebookgo/freeport v0.0.0-20150612182905-d4adf43b75b9 // indirect diff --git a/go.sum b/go.sum index 4703bd6..6edd94e 100644 --- a/go.sum +++ b/go.sum @@ -57,6 +57,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/facebookarchive/grace v0.0.0-20180706040059-75cf19382434 h1:AFIATPhFj7mrISc4z9zEpfm4a8UfwsCWzJ+Je5jA5Rs= github.com/facebookarchive/grace v0.0.0-20180706040059-75cf19382434/go.mod h1:PY9iiFMrFjTzegsqfKCcb+Ekk5/j0ch0sMdBwlT6atI= github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a h1:yDWHCSQ40h88yih2JAcL6Ls/kVkSE8GFACTGVnMPruw= diff --git a/utils/intToString.go b/utils/intToString.go index 11a3698..c760f47 100644 --- a/utils/intToString.go +++ b/utils/intToString.go @@ -13,3 +13,17 @@ func OrderCancelReasonToString(i int) string { return "取消" } } + +// OrderSingleStatusToString 订单状态(1:待支付 2:已完成 3:已取消) +func OrderSingleStatusToString(i int) string { + switch i { + case 1: + return "待支付" + case 2: + return "已完成" + case 3: + return "已取消" + default: + return "未知" + } +}