From a525a67a01ea5be7726ab7dfa265459a432a5ed6 Mon Sep 17 00:00:00 2001 From: wucongxing8150 <815046773@qq.com> Date: Thu, 25 Jul 2024 14:39:57 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BA=86=E5=BE=AE=E4=BF=A1?= =?UTF-8?q?=E6=94=AF=E4=BB=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/controller/Base.go | 1 + api/controller/CallBack.go | 23 +++ api/controller/OrderMember.go | 158 +++++++++++++++ api/controller/OrderSingle.go | 21 +- api/controller/Question.go | 127 +++++++++++- api/controller/SystemMember.go | 17 +- api/controller/UserCoupon.go | 65 +++--- api/dao/OrderMember.go | 12 ++ api/dao/UserCoupon.go | 9 + api/dto/OrderMember.go | 35 +++- api/dto/Question.go | 4 + api/dto/SystemMember.go | 43 ++-- api/model/OrderMember.go | 36 ++-- api/model/Question.go | 29 +-- api/model/SystemMember.go | 10 +- api/requests/OrderMember.go | 15 ++ api/requests/OrderSingle.go | 7 +- api/requests/UserCoupon.go | 18 +- api/router/router.go | 29 ++- api/service/OrderMember.go | 350 +++++++++++++++++++++++++++++++++ api/service/OrderSingle.go | 54 +++-- api/service/Question.go | 47 +++-- api/service/SystemMember.go | 17 ++ api/service/User.go | 27 +++ api/service/UserCollection.go | 12 +- api/service/UserCoupon.go | 92 +++++++++ config.yaml | 2 +- extend/weChat/base.go | 19 +- extend/weChat/parseNotify.go | 43 ++++ 29 files changed, 1173 insertions(+), 149 deletions(-) create mode 100644 api/controller/CallBack.go create mode 100644 api/service/OrderMember.go create mode 100644 api/service/SystemMember.go create mode 100644 extend/weChat/parseNotify.go diff --git a/api/controller/Base.go b/api/controller/Base.go index f534626..0315cf3 100644 --- a/api/controller/Base.go +++ b/api/controller/Base.go @@ -12,4 +12,5 @@ type Api struct { OrderSingle // 订单-单项 SystemMember // 会员配置 OrderMember // 订单-会员 + CallBack // 回调 } diff --git a/api/controller/CallBack.go b/api/controller/CallBack.go new file mode 100644 index 0000000..641e43b --- /dev/null +++ b/api/controller/CallBack.go @@ -0,0 +1,23 @@ +package controller + +import ( + "github.com/gin-gonic/gin" + "hepa-calc-api/api/responses" + "hepa-calc-api/extend/weChat" + "hepa-calc-api/utils" +) + +// CallBack 回调 +type CallBack struct{} + +// WxJsapiPay 微信支付回调-jsapi +func (r *CallBack) WxJsapiPay(c *gin.Context) { + notifyReq, err := weChat.ParseNotify(c) + if err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + // 记录日志 + utils.LogJsonErr("微信支付回调-jsapi", notifyReq) +} diff --git a/api/controller/OrderMember.go b/api/controller/OrderMember.go index 593e7ca..d73c4bd 100644 --- a/api/controller/OrderMember.go +++ b/api/controller/OrderMember.go @@ -1,13 +1,17 @@ package controller import ( + "fmt" "github.com/gin-gonic/gin" "hepa-calc-api/api/dao" "hepa-calc-api/api/dto" "hepa-calc-api/api/requests" "hepa-calc-api/api/responses" + "hepa-calc-api/api/service" "hepa-calc-api/global" "hepa-calc-api/utils" + "strconv" + "time" ) type OrderMember struct{} @@ -56,3 +60,157 @@ func (b *OrderMember) GetOrderMemberPage(c *gin.Context) { result["data"] = g responses.OkWithData(result, c) } + +// AddOrderMember 创建会员订单 +func (b *OrderMember) AddOrderMember(c *gin.Context) { + orderMemberRequest := requests.OrderMemberRequest{} + req := orderMemberRequest.AddOrderMember + 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 + } + + userId := c.GetInt64("UserId") + + // 将 id 转换为 int64 类型 + systemMemberId, err := strconv.ParseInt(req.SystemMemberId, 10, 64) + if err != nil { + responses.Fail(c) + return + } + + // 用户优惠卷id + var userCouponId *int64 + if req.UserCouponId != "" { + id, err := strconv.ParseInt(req.UserCouponId, 10, 64) + if err != nil { + responses.Fail(c) + return + } + + userCouponId = &id + } + + if req.OrderPrice < 0 { + responses.FailWithMessage("价格错误", c) + return + } + + // 开始事务 + tx := global.Db.Begin() + defer func() { + if r := recover(); r != nil { + tx.Rollback() + } + }() + + // 创建会员订单 + orderMemberService := service.OrderMemberService{} + orderMember, err := orderMemberService.AddOrderMember(tx, userId, systemMemberId, userCouponId, req.PayChannel, req.OrderPrice) + if err != nil { + tx.Rollback() + responses.FailWithMessage(err.Error(), c) + return + } + + tx.Commit() + + // 处理返回值 + orderId := fmt.Sprintf("%d", orderMember.OrderId) + + responses.OkWithData(orderId, c) +} + +// GetOrderMemberPay 获取会员订单支付数据 +func (b *OrderMember) GetOrderMemberPay(c *gin.Context) { + orderMemberRequest := requests.OrderMemberRequest{} + req := orderMemberRequest.GetOrderMemberPay + 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 + } + + 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 + } + + // 检测订单状态(1:待支付 2:已完成 3:已取消) + if orderMember.OrderStatus != 1 { + responses.FailWithMessage("订单状态异常", c) + return + } + + // 支付状态(1:未支付 2:已支付 3:支付中 4:支付失败 5:支付超时 6:支付关闭 7:已撤销 8:转入退款) + if orderMember.PayStatus != 1 { + responses.FailWithMessage("订单支付状态异常", c) + return + } + + // 验证订单过期支付时间 + now := time.Now() + validTime := time.Time(orderMember.CreatedAt).Add(30 * time.Minute) + if validTime.Before(now) { + responses.FailWithMessage("订单已过期", c) + return + } + + // 处理返回值 + g := dto.GetOrderMemberPayDto(orderMember) + + // 获取预支付交易会话标识 + if req.ClientType == 1 { + orderMemberService := service.OrderMemberService{} + prepay, err := orderMemberService.GetJsapiPrepay(orderMember) + if err != nil { + responses.FailWithMessage("发起支付失败", c) + return + } + + g.PrepayJsapi = prepay + } else { + orderMemberService := service.OrderMemberService{} + prepay, err := orderMemberService.GetAppPrepay(orderMember) + if err != nil { + responses.FailWithMessage("发起支付失败", c) + return + } + + g.PrepayApp = prepay + } + + responses.OkWithData(g, c) +} diff --git a/api/controller/OrderSingle.go b/api/controller/OrderSingle.go index e713827..9c1edfe 100644 --- a/api/controller/OrderSingle.go +++ b/api/controller/OrderSingle.go @@ -97,6 +97,25 @@ func (b *OrderSingle) AddOrderSingle(c *gin.Context) { userCouponId = &id } + // 获取用户数据 + userDao := dao.UserDao{} + user, err := userDao.GetUserById(userId) + if err != nil || user == nil { + responses.FailWithMessage("用户错误", c) + return + } + + // 检测是否会员 + if req.PayChannel == 3 && user.IsMember == 0 { + responses.FailWithMessage("非法操作", c) + return + } + + if req.OrderPrice < 0 { + responses.FailWithMessage("价格错误", c) + return + } + // 开始事务 tx := global.Db.Begin() defer func() { @@ -107,7 +126,7 @@ func (b *OrderSingle) AddOrderSingle(c *gin.Context) { // 创建单项订单 orderSingleService := service.OrderSingleService{} - orderSingle, err := orderSingleService.AddOrderSingle(tx, userId, questionId, userCouponId, req.PayChannel) + orderSingle, err := orderSingleService.AddOrderSingle(tx, userId, questionId, userCouponId, req.PayChannel, req.OrderPrice) if err != nil { tx.Rollback() responses.FailWithMessage(err.Error(), c) diff --git a/api/controller/Question.go b/api/controller/Question.go index 426925f..065f1c1 100644 --- a/api/controller/Question.go +++ b/api/controller/Question.go @@ -10,6 +10,7 @@ import ( "hepa-calc-api/global" "hepa-calc-api/utils" "strconv" + "time" ) type Question struct{} @@ -99,12 +100,16 @@ func (r *Question) GetQuestion(c *gin.Context) { return } - // 检测问题是否被用户收藏 + questionService := service.QuestionService{} + + // 处理问题优惠价格 + question.DiscountPrice = questionService.HandleQuestionDiscountPrice(question.DiscountPrice, question.DiscountEndTime) + + // 检测用户收藏状态 userCollectionService := service.UserCollectionService{} - IsCollection := userCollectionService.CheckUserCollectionQuestion(userId, questionId) + IsCollection := userCollectionService.GetUserCollectionQuestionStatus(userId, questionId) // 获取用户首次购买价格 - questionService := service.QuestionService{} firstTimePrice, err := questionService.GetUserFirstTimeBuyPrice(userId, questionId) if err != nil { responses.FailWithMessage("题目错误", c) @@ -112,7 +117,7 @@ func (r *Question) GetQuestion(c *gin.Context) { } // 获取问题被购买数量 - buyCount, err := questionService.GetQuestionBuyCount(userId, questionId) + buyCount, _ := questionService.GetQuestionBuyCount(userId, questionId) g := dto.GetQuestionDto(question) @@ -127,3 +132,117 @@ func (r *Question) GetQuestion(c *gin.Context) { responses.OkWithData(g, c) } + +// GetQuestionBuyStatus 获取问题购买状态 +func (r *Question) GetQuestionBuyStatus(c *gin.Context) { + // 购买状态(0:否 1:是) + var status int + userId := c.GetInt64("UserId") + + id := c.Param("question_id") + if id == "" { + responses.FailWithMessage("缺少参数", c) + return + } + + // 将 id 转换为 int64 类型 + questionId, err := strconv.ParseInt(id, 10, 64) + if err != nil { + responses.Fail(c) + return + } + + // 获取题目数据 + questionDao := dao.QuestionDao{} + question, err := questionDao.GetQuestionById(questionId) + if err != nil { + responses.FailWithMessage("题目错误", c) + return + } + + if question.QuestionStatus != 1 { + responses.FailWithMessage("题目错误", c) + return + } + + // 获取用户数据 + userDao := dao.UserDao{} + user, err := userDao.GetUserById(userId) + if err != nil || user == nil { + responses.FailWithMessage("用户错误", c) + return + } + + // 检测用户会员 + userService := service.UserService{} + isMember := userService.CheckUserMember(user) + if isMember == true { + // 用户为会员,可直接查看 + status = 1 + responses.OkWithData(status, c) + return + } + + // 获取单项配置 + systemSingleDao := dao.SystemSingleDao{} + + maps := make(map[string]interface{}) + systemSingle, err := systemSingleDao.GetSystemSingle(maps) + if err != nil { + responses.FailWithMessage("内部错误", c) + return + } + + // 获取用户订单 + orderSingleDao := dao.OrderSingleDao{} + maps = make(map[string]interface{}) + maps["user_id"] = userId + maps["question_id"] = questionId + orderSingles, err := orderSingleDao.GetOrderSingleList(maps) + if err != nil { + responses.FailWithMessage("用户错误", c) + return + } + + // 是否购买过(0:否 1:是) + var isBuy int + for _, single := range orderSingles { + // 订单状态(1:待支付 2:已完成 3:已取消) + if single.OrderStatus != 2 { + continue + } + + // 支付状态(1:未支付 2:已支付 3:支付中 4:支付失败 5:支付超时 6:支付关闭 7:已撤销 8:转入退款) + if single.PayStatus != 2 { + continue + } + + // 订单退款状态(0:无退款 1:申请退款 2:退款中 3:退款成功 4:拒绝退款 5:退款关闭 6:退款异常 7:部分退款) + if single.RefundStatus != 0 { + continue + } + + // 取消状态(0:否 1:是) + if single.CancelStatus == 1 { + continue + } + + // 判断是否还在有效期内 + now := time.Now() + ValidTime := single.PayTime.Add(time.Duration(systemSingle.ValidDays)) + if ValidTime.Before(now) { + continue + } + + isBuy = 1 + break + } + + if isBuy == 1 { + status = 1 + responses.OkWithData(status, c) + return + } + + responses.OkWithData(status, c) +} diff --git a/api/controller/SystemMember.go b/api/controller/SystemMember.go index 5fce2d3..2b61cf1 100644 --- a/api/controller/SystemMember.go +++ b/api/controller/SystemMember.go @@ -1,27 +1,40 @@ package controller import ( + "fmt" "github.com/gin-gonic/gin" "hepa-calc-api/api/dao" "hepa-calc-api/api/dto" "hepa-calc-api/api/responses" + "hepa-calc-api/api/service" ) type SystemMember struct{} // GetSystemMember 获取会员配置数据 func (b *SystemMember) GetSystemMember(c *gin.Context) { + userId := c.GetInt64("UserId") + systemMemberDao := dao.SystemMemberDao{} maps := make(map[string]interface{}) - systemMember, err := systemMemberDao.GetSystemMemberList(maps) + systemMembers, err := systemMemberDao.GetSystemMemberList(maps) if err != nil { responses.FailWithMessage(err.Error(), c) return } + // 检测用户是否购买过会员 + userService := service.UserService{} + isBuy := userService.CheckUserBuyOrderMember(userId) + if isBuy == true { + for _, member := range systemMembers { + fmt.Println(member) + } + } + // 处理返回值 - g := dto.GetSystemMemberListDto(systemMember) + g := dto.GetSystemMemberListDto(systemMembers) responses.OkWithData(g, c) } diff --git a/api/controller/UserCoupon.go b/api/controller/UserCoupon.go index 356f7e9..e021d92 100644 --- a/api/controller/UserCoupon.go +++ b/api/controller/UserCoupon.go @@ -6,8 +6,10 @@ import ( "hepa-calc-api/api/dto" "hepa-calc-api/api/requests" "hepa-calc-api/api/responses" + "hepa-calc-api/api/service" "hepa-calc-api/global" "hepa-calc-api/utils" + "strconv" ) type UserCoupon struct{} @@ -89,10 +91,10 @@ func (b *UserCoupon) GetUserCouponUnnotified(c *gin.Context) { responses.OkWithData(g, c) } -// GetUserUsableCoupon 获取用户当前可用优惠卷 -func (b *UserCoupon) GetUserUsableCoupon(c *gin.Context) { +// GetUserUsableQuestionCoupon 获取患者可使用优惠卷-单项 +func (b *UserCoupon) GetUserUsableQuestionCoupon(c *gin.Context) { userCouponRequest := requests.UserCouponRequest{} - req := userCouponRequest.GetUserCouponPage + req := userCouponRequest.GetUserUsableQuestionCoupon if err := c.ShouldBind(&req); err != nil { responses.FailWithMessage(err.Error(), c) return @@ -104,32 +106,49 @@ func (b *UserCoupon) GetUserUsableCoupon(c *gin.Context) { return } - if req.Page == 0 { - req.Page = 1 - } - - if req.PageSize == 0 { - req.PageSize = 20 - } - userId := c.GetInt64("UserId") - req.UserId = userId - // 获取数据 - userCouponDao := dao.UserCouponDao{} - userCoupon, total, err := userCouponDao.GetUserCouponPageSearch(req, req.Page, req.PageSize) + // 将 id 转换为 int64 类型 + questionId, err := strconv.ParseInt(req.QuestionId, 10, 64) if err != nil { + responses.Fail(c) + return + } + + // 获取患者可使用优惠卷-单项 + userCouponService := service.UserCouponService{} + userCoupon, _ := userCouponService.GetUserUsableQuestionCoupon(userId, questionId, req.AmountTotal) + + responses.OkWithData(userCoupon, c) +} + +// GetUserUsableMemberCoupon 获取患者可使用优惠卷-会员 +func (b *UserCoupon) GetUserUsableMemberCoupon(c *gin.Context) { + userCouponRequest := requests.UserCouponRequest{} + req := userCouponRequest.GetUserUsableMemberCoupon + if err := c.ShouldBind(&req); err != nil { responses.FailWithMessage(err.Error(), c) return } - // 处理返回值 - g := dto.GetUserCouponListDto(userCoupon) + // 参数验证 + if err := global.Validate.Struct(req); err != nil { + responses.FailWithMessage(utils.Translate(err), c) + return + } - result := make(map[string]interface{}) - result["page"] = req.Page - result["page_size"] = req.PageSize - result["total"] = total - result["data"] = g - responses.OkWithData(result, c) + userId := c.GetInt64("UserId") + + // 将 id 转换为 int64 类型 + systemMemberId, err := strconv.ParseInt(req.SystemMemberId, 10, 64) + if err != nil { + responses.Fail(c) + return + } + + // 获取患者可使用优惠卷-会员 + userCouponService := service.UserCouponService{} + userCoupon, _ := userCouponService.GetUserUsableMemberCoupon(userId, systemMemberId, req.AmountTotal) + + responses.OkWithData(userCoupon, c) } diff --git a/api/dao/OrderMember.go b/api/dao/OrderMember.go index b19fda3..e31db41 100644 --- a/api/dao/OrderMember.go +++ b/api/dao/OrderMember.go @@ -173,3 +173,15 @@ func (r *OrderMemberDao) GetOrderMemberPageSearch(req requests.GetOrderMemberPag } return m, totalRecords, nil } + +// GetUserFirstTimeBuyOrderMember 获取用户首次购买的订单 +func (r *OrderMemberDao) GetUserFirstTimeBuyOrderMember(userId int64) (m *model.OrderMember, err error) { + err = global.Db. + Where("user_id = ?", userId). + Where("order_status != ?", 2). + First(&m).Error + if err != nil { + return nil, err + } + return m, nil +} diff --git a/api/dao/UserCoupon.go b/api/dao/UserCoupon.go index 8425ea5..b7b7dba 100644 --- a/api/dao/UserCoupon.go +++ b/api/dao/UserCoupon.go @@ -75,6 +75,15 @@ func (r *UserCouponDao) GetUserCouponList(maps interface{}) (m []*model.UserCoup return m, nil } +// GetUserCouponPreloadList 获取列表-加载全部关联 +func (r *UserCouponDao) GetUserCouponPreloadList(maps interface{}) (m []*model.UserCoupon, err error) { + err = global.Db.Preload(clause.Associations).Where(maps).Find(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + // GetUserCouponCount 获取数量 func (r *UserCouponDao) GetUserCouponCount(maps interface{}) (total int64, err error) { err = global.Db.Model(&model.UserCoupon{}).Where(maps).Count(&total).Error diff --git a/api/dto/OrderMember.go b/api/dto/OrderMember.go index 0b40a4e..d149bc2 100644 --- a/api/dto/OrderMember.go +++ b/api/dto/OrderMember.go @@ -2,10 +2,13 @@ package dto import ( "fmt" + "github.com/wechatpay-apiv3/wechatpay-go/services/payments/app" + "github.com/wechatpay-apiv3/wechatpay-go/services/payments/jsapi" "hepa-calc-api/api/model" + "time" ) -// OrderMemberDto 订单-单项 +// OrderMemberDto 订单-会员 type OrderMemberDto struct { OrderId string `json:"order_id"` // 主键id UserId string `json:"user_id"` // 用户id @@ -14,7 +17,7 @@ type OrderMemberDto struct { IsDelete int `json:"is_delete"` // 用户删除状态(0:否 1:是) PayChannel int `json:"pay_channel"` // 支付渠道(1:h5支付 2:app支付 3:会员支付) PayStatus int `json:"pay_status"` // 支付状态(1:未支付 2:已支付 3:支付中 4:支付失败 5:支付超时 6:支付关闭 7:已撤销 8:转入退款) - PayTime model.LocalTime `json:"pay_time"` // 支付时间 + PayTime *time.Time `json:"pay_time"` // 支付时间 RefundStatus int `json:"refund_status"` // 订单退款状态(0:无退款 1:申请退款 2:退款中 3:退款成功 4:拒绝退款 5:退款关闭 6:退款异常 7:部分退款) OrderNo string `json:"order_no"` // 系统订单编号 EscrowTradeNo string `json:"escrow_trade_no"` // 第三方支付流水号 @@ -22,7 +25,7 @@ type OrderMemberDto struct { CouponAmountTotal float64 `json:"coupon_amount_total"` // 优惠卷总金额 PaymentAmountTotal float64 `json:"payment_amount_total"` // 实际付款金额 CancelStatus int `json:"cancel_status"` // 取消状态(0:否 1:是) - CancelTime model.LocalTime `json:"cancel_time"` // 订单取消时间 + CancelTime *time.Time `json:"cancel_time"` // 订单取消时间 CancelRemarks string `json:"cancel_remarks"` // 取消订单备注 OrderRemarks string `json:"order_remarks"` // 订单备注 CreatedAt model.LocalTime `json:"created_at"` // 创建时间 @@ -30,6 +33,19 @@ type OrderMemberDto struct { SystemMember *SystemMemberDto `json:"system_member"` // 会员 } +// OrderMemberPayDto 会员订单支付数据 +type OrderMemberPayDto struct { + OrderId string `json:"order_id"` // 主键id + SystemMemberId string `json:"system_member_id"` // 会员id + OrderNo string `json:"order_no"` // 系统订单编号 + AmountTotal float64 `json:"amount_total"` // 订单金额 + CouponAmountTotal float64 `json:"coupon_amount_total"` // 优惠卷总金额 + PaymentAmountTotal float64 `json:"payment_amount_total"` // 实际付款金额 + CreatedAt model.LocalTime `json:"created_at"` // 创建时间 + PrepayJsapi *jsapi.PrepayWithRequestPaymentResponse `json:"prepay_jsapi"` // 预支付交易数据-jsapi + PrepayApp *app.PrepayWithRequestPaymentResponse `json:"prepay_app"` // 预支付交易数据-app +} + // GetOrderMemberListDto 列表 func GetOrderMemberListDto(m []*model.OrderMember) []*OrderMemberDto { // 处理返回值 @@ -73,6 +89,19 @@ func GetOrderMemberListDto(m []*model.OrderMember) []*OrderMemberDto { return responses } +// GetOrderMemberPayDto 获取单项订单支付数据 +func GetOrderMemberPayDto(m *model.OrderMember) *OrderMemberPayDto { + return &OrderMemberPayDto{ + OrderId: fmt.Sprintf("%d", m.OrderId), + SystemMemberId: fmt.Sprintf("%d", m.SystemMemberId), + OrderNo: m.OrderNo, + AmountTotal: m.AmountTotal, + CouponAmountTotal: m.CouponAmountTotal, + PaymentAmountTotal: m.PaymentAmountTotal, + CreatedAt: m.CreatedAt, + } +} + // LoadSystemMember 加载会员数据 func (r *OrderMemberDto) LoadSystemMember(m *model.SystemMember) *OrderMemberDto { if m != nil { diff --git a/api/dto/Question.go b/api/dto/Question.go index f0fa55f..b8055f8 100644 --- a/api/dto/Question.go +++ b/api/dto/Question.go @@ -3,6 +3,7 @@ package dto import ( "fmt" "hepa-calc-api/api/model" + "time" ) // QuestionDto 问题表 @@ -19,6 +20,7 @@ type QuestionDto struct { PayCount int `json:"pay_count"` // 支付次数(查看报告的人次) Price float64 `json:"price"` // 价格(原价) DiscountPrice *float64 `json:"discount_price"` // 优惠价格 + DiscountEndTime *time.Time `json:"discount_end_time"` // 优惠截止时间 QuestionBrief string `json:"question_brief"` // 问题介绍 QuestionExplain string `json:"question_explain"` // 问题解释/科普 CreatedAt model.LocalTime `json:"created_at"` // 创建时间 @@ -43,6 +45,7 @@ func GetQuestionDto(m *model.Question) *QuestionDto { PayCount: m.PayCount, Price: m.Price, DiscountPrice: m.DiscountPrice, + DiscountEndTime: m.DiscountEndTime, QuestionBrief: m.QuestionBrief, QuestionExplain: m.QuestionExplain, CreatedAt: m.CreatedAt, @@ -70,6 +73,7 @@ func GetQuestionListDto(m []*model.Question) []*QuestionDto { PayCount: v.PayCount, Price: v.Price, DiscountPrice: v.DiscountPrice, + DiscountEndTime: v.DiscountEndTime, QuestionBrief: v.QuestionBrief, QuestionExplain: v.QuestionExplain, CreatedAt: v.CreatedAt, diff --git a/api/dto/SystemMember.go b/api/dto/SystemMember.go index e9c70ec..8831f3e 100644 --- a/api/dto/SystemMember.go +++ b/api/dto/SystemMember.go @@ -3,17 +3,18 @@ package dto import ( "fmt" "hepa-calc-api/api/model" + "time" ) // SystemMemberDto 配置-会员配置 type SystemMemberDto struct { - SystemMemberId string `json:"system_member_id"` // 主键id - MemberDays uint `json:"member_days"` // 会员天数 - Price float64 `json:"price"` // 价格(原价) - DiscountPrice float64 `json:"discount_price"` // 优惠价格 - FirstTimePrice float64 `json:"first_time_price"` // 首次购买价格 - CreatedAt model.LocalTime `json:"created_at"` // 创建时间 - UpdatedAt model.LocalTime `json:"updated_at"` // 更新时间 + SystemMemberId string `json:"system_member_id"` // 主键id + MemberDays uint `json:"member_days"` // 会员天数 + Price float64 `json:"price"` // 价格(原价) + DiscountPrice *float64 `json:"discount_price"` // 优惠价格 + DiscountEndTime *time.Time `json:"discount_end_time"` // 优惠截止时间 + CreatedAt model.LocalTime `json:"created_at"` // 创建时间 + UpdatedAt model.LocalTime `json:"updated_at"` // 更新时间 } // GetSystemMemberListDto 列表 @@ -24,13 +25,13 @@ func GetSystemMemberListDto(m []*model.SystemMember) []*SystemMemberDto { if len(m) > 0 { for i, v := range m { response := &SystemMemberDto{ - SystemMemberId: fmt.Sprintf("%d", v.SystemMemberId), - MemberDays: v.MemberDays, - Price: v.Price, - DiscountPrice: v.DiscountPrice, - FirstTimePrice: v.FirstTimePrice, - CreatedAt: v.CreatedAt, - UpdatedAt: v.UpdatedAt, + SystemMemberId: fmt.Sprintf("%d", v.SystemMemberId), + MemberDays: v.MemberDays, + Price: v.Price, + DiscountPrice: v.DiscountPrice, + DiscountEndTime: v.DiscountEndTime, + CreatedAt: v.CreatedAt, + UpdatedAt: v.UpdatedAt, } // 将转换后的结构体添加到新切片中 @@ -44,12 +45,12 @@ func GetSystemMemberListDto(m []*model.SystemMember) []*SystemMemberDto { // GetSystemMemberDto 详情 func GetSystemMemberDto(m *model.SystemMember) *SystemMemberDto { return &SystemMemberDto{ - SystemMemberId: fmt.Sprintf("%d", m.SystemMemberId), - MemberDays: m.MemberDays, - Price: m.Price, - DiscountPrice: m.DiscountPrice, - FirstTimePrice: m.FirstTimePrice, - CreatedAt: m.CreatedAt, - UpdatedAt: m.UpdatedAt, + SystemMemberId: fmt.Sprintf("%d", m.SystemMemberId), + MemberDays: m.MemberDays, + Price: m.Price, + DiscountPrice: m.DiscountPrice, + DiscountEndTime: m.DiscountEndTime, + CreatedAt: m.CreatedAt, + UpdatedAt: m.UpdatedAt, } } diff --git a/api/model/OrderMember.go b/api/model/OrderMember.go index 6bbbbf4..8424c8a 100644 --- a/api/model/OrderMember.go +++ b/api/model/OrderMember.go @@ -8,24 +8,24 @@ import ( // OrderMember 订单-单项 type OrderMember struct { - OrderId int64 `gorm:"column:order_id;type:bigint(19);primary_key;comment:主键id" json:"order_id"` - UserId int64 `gorm:"column:user_id;type:bigint(19);comment:用户id;NOT NULL" json:"user_id"` - SystemMemberId int64 `gorm:"column:system_member_id;type:bigint(19);comment:会员id;NOT NULL" json:"system_member_id"` - OrderStatus int `gorm:"column:order_status;type:tinyint(1);default:1;comment:订单状态(1:待支付 2:已完成 3:已取消)" json:"order_status"` - IsDelete int `gorm:"column:is_delete;type:tinyint(1);default:0;comment:用户删除状态(0:否 1:是)" json:"is_delete"` - PayChannel int `gorm:"column:pay_channel;type:tinyint(1);comment:支付渠道(1:h5支付 2:app支付 3:会员支付);NOT NULL" json:"pay_channel"` - PayStatus int `gorm:"column:pay_status;type:tinyint(1);default:1;comment:支付状态(1:未支付 2:已支付 3:支付中 4:支付失败 5:支付超时 6:支付关闭 7:已撤销 8:转入退款);NOT NULL" json:"pay_status"` - PayTime LocalTime `gorm:"column:pay_time;type:datetime;comment:支付时间" json:"pay_time"` - RefundStatus int `gorm:"column:refund_status;type:tinyint(1);default:0;comment:订单退款状态(0:无退款 1:申请退款 2:退款中 3:退款成功 4:拒绝退款 5:退款关闭 6:退款异常 7:部分退款);NOT NULL" json:"refund_status"` - OrderNo string `gorm:"column:order_no;type:varchar(30);comment:系统订单编号;NOT NULL" json:"order_no"` - EscrowTradeNo string `gorm:"column:escrow_trade_no;type:varchar(100);comment:第三方支付流水号;NOT NULL" json:"escrow_trade_no"` - AmountTotal float64 `gorm:"column:amount_total;type:decimal(10,2) unsigned;default:0.00;comment:订单金额" json:"amount_total"` - CouponAmountTotal float64 `gorm:"column:coupon_amount_total;type:decimal(10,2);default:0.00;comment:优惠卷总金额" json:"coupon_amount_total"` - PaymentAmountTotal float64 `gorm:"column:payment_amount_total;type:decimal(10,2);default:0.00;comment:实际付款金额" json:"payment_amount_total"` - CancelStatus int `gorm:"column:cancel_status;type:tinyint(1);default:0;comment:取消状态(0:否 1:是)" json:"cancel_status"` - CancelTime LocalTime `gorm:"column:cancel_time;type:datetime;comment:订单取消时间" json:"cancel_time"` - CancelRemarks string `gorm:"column:cancel_remarks;type:varchar(255);comment:取消订单备注" json:"cancel_remarks"` - OrderRemarks string `gorm:"column:order_remarks;type:varchar(255);comment:订单备注" json:"order_remarks"` + OrderId int64 `gorm:"column:order_id;type:bigint(19);primary_key;comment:主键id" json:"order_id"` + UserId int64 `gorm:"column:user_id;type:bigint(19);comment:用户id;NOT NULL" json:"user_id"` + SystemMemberId int64 `gorm:"column:system_member_id;type:bigint(19);comment:会员id;NOT NULL" json:"system_member_id"` + OrderStatus int `gorm:"column:order_status;type:tinyint(1);default:1;comment:订单状态(1:待支付 2:已完成 3:已取消)" json:"order_status"` + IsDelete int `gorm:"column:is_delete;type:tinyint(1);default:0;comment:用户删除状态(0:否 1:是)" json:"is_delete"` + PayChannel int `gorm:"column:pay_channel;type:tinyint(1);comment:支付渠道(1:h5支付 2:app支付 3:会员支付);NOT NULL" json:"pay_channel"` + PayStatus int `gorm:"column:pay_status;type:tinyint(1);default:1;comment:支付状态(1:未支付 2:已支付 3:支付中 4:支付失败 5:支付超时 6:支付关闭 7:已撤销 8:转入退款);NOT NULL" json:"pay_status"` + PayTime *time.Time `gorm:"column:pay_time;type:datetime;comment:支付时间" json:"pay_time"` + RefundStatus int `gorm:"column:refund_status;type:tinyint(1);default:0;comment:订单退款状态(0:无退款 1:申请退款 2:退款中 3:退款成功 4:拒绝退款 5:退款关闭 6:退款异常 7:部分退款);NOT NULL" json:"refund_status"` + OrderNo string `gorm:"column:order_no;type:varchar(30);comment:系统订单编号;NOT NULL" json:"order_no"` + EscrowTradeNo string `gorm:"column:escrow_trade_no;type:varchar(100);comment:第三方支付流水号;NOT NULL" json:"escrow_trade_no"` + AmountTotal float64 `gorm:"column:amount_total;type:decimal(10,2) unsigned;default:0.00;comment:订单金额" json:"amount_total"` + CouponAmountTotal float64 `gorm:"column:coupon_amount_total;type:decimal(10,2);default:0.00;comment:优惠卷总金额" json:"coupon_amount_total"` + PaymentAmountTotal float64 `gorm:"column:payment_amount_total;type:decimal(10,2);default:0.00;comment:实际付款金额" json:"payment_amount_total"` + CancelStatus int `gorm:"column:cancel_status;type:tinyint(1);default:0;comment:取消状态(0:否 1:是)" json:"cancel_status"` + CancelTime *time.Time `gorm:"column:cancel_time;type:datetime;comment:订单取消时间" json:"cancel_time"` + CancelRemarks string `gorm:"column:cancel_remarks;type:varchar(255);comment:取消订单备注" json:"cancel_remarks"` + OrderRemarks string `gorm:"column:order_remarks;type:varchar(255);comment:订单备注" json:"order_remarks"` Model SystemMember *SystemMember `gorm:"foreignKey:SystemMemberId;references:system_member_id" json:"system_member"` } diff --git a/api/model/Question.go b/api/model/Question.go index 46b0eb5..1d5e322 100644 --- a/api/model/Question.go +++ b/api/model/Question.go @@ -8,20 +8,21 @@ import ( // Question 问题表 type Question struct { - QuestionId int64 `gorm:"column:question_id;type:bigint(19);primary_key;comment:主键id" json:"question_id"` - QuestionTitle string `gorm:"column:question_title;type:varchar(200);comment:标题" json:"question_title"` - QuestionSubtitle string `gorm:"column:question_subtitle;type:varchar(200);comment:副标题" json:"question_subtitle"` - QuestionIden string `gorm:"column:question_iden;type:varchar(255);comment:唯一标识(用于和前端对应)" json:"question_iden"` - QuestionStatus int `gorm:"column:question_status;type:tinyint(1);default:2;comment:问题状态(1:正常 2:待发布)" json:"question_status"` - IsHide int `gorm:"column:is_hide;type:tinyint(1);default:0;comment:是否隐藏(0:否 1:是)" json:"is_hide"` - IsRecommend int `gorm:"column:is_recommend;type:tinyint(1);default:0;comment:是否推荐(0:否 1:是)" json:"is_recommend"` - ClickCount int `gorm:"column:click_count;type:int(5);default:0;comment:点击次数(点击进入详情页的人次)" json:"click_count"` - SubmitCount int `gorm:"column:submit_count;type:int(5);default:0;comment:提交次数(提交个人信息进行了算算的人次)" json:"submit_count"` - PayCount int `gorm:"column:pay_count;type:int(5);default:0;comment:支付次数(查看报告的人次)" json:"pay_count"` - Price float64 `gorm:"column:price;type:decimal(10,2) unsigned;default:0.00;comment:价格(原价)" json:"price"` - DiscountPrice *float64 `gorm:"column:discount_price;type:decimal(10,2);comment:优惠价格" json:"discount_price"` - QuestionBrief string `gorm:"column:question_brief;type:text;comment:问题介绍" json:"question_brief"` - QuestionExplain string `gorm:"column:question_explain;type:text;comment:问题解释/科普" json:"question_explain"` + QuestionId int64 `gorm:"column:question_id;type:bigint(19);primary_key;comment:主键id" json:"question_id"` + QuestionTitle string `gorm:"column:question_title;type:varchar(200);comment:标题" json:"question_title"` + QuestionSubtitle string `gorm:"column:question_subtitle;type:varchar(200);comment:副标题" json:"question_subtitle"` + QuestionIden string `gorm:"column:question_iden;type:varchar(255);comment:唯一标识(用于和前端对应)" json:"question_iden"` + QuestionStatus int `gorm:"column:question_status;type:tinyint(1);default:2;comment:问题状态(1:正常 2:待发布)" json:"question_status"` + IsHide int `gorm:"column:is_hide;type:tinyint(1);default:0;comment:是否隐藏(0:否 1:是)" json:"is_hide"` + IsRecommend int `gorm:"column:is_recommend;type:tinyint(1);default:0;comment:是否推荐(0:否 1:是)" json:"is_recommend"` + ClickCount int `gorm:"column:click_count;type:int(5);default:0;comment:点击次数(点击进入详情页的人次)" json:"click_count"` + SubmitCount int `gorm:"column:submit_count;type:int(5);default:0;comment:提交次数(提交个人信息进行了算算的人次)" json:"submit_count"` + PayCount int `gorm:"column:pay_count;type:int(5);default:0;comment:支付次数(查看报告的人次)" json:"pay_count"` + Price float64 `gorm:"column:price;type:decimal(10,2) unsigned;default:0.00;comment:价格(原价)" json:"price"` + DiscountPrice *float64 `gorm:"column:discount_price;type:decimal(10,2);comment:优惠价格" json:"discount_price"` + DiscountEndTime *time.Time `gorm:"column:discount_end_time;type:datetime;comment:优惠截止时间" json:"discount_end_time"` + QuestionBrief string `gorm:"column:question_brief;type:text;comment:问题介绍" json:"question_brief"` + QuestionExplain string `gorm:"column:question_explain;type:text;comment:问题解释/科普" json:"question_explain"` Model } diff --git a/api/model/SystemMember.go b/api/model/SystemMember.go index 22e5d46..3f97129 100644 --- a/api/model/SystemMember.go +++ b/api/model/SystemMember.go @@ -8,11 +8,11 @@ import ( // SystemMember 配置-会员配置 type SystemMember struct { - SystemMemberId int64 `gorm:"column:system_member_id;type:bigint(19);primary_key;comment:主键id" json:"system_member_id"` - MemberDays uint `gorm:"column:member_days;type:int(10) unsigned;default:0;comment:会员天数" json:"member_days"` - Price float64 `gorm:"column:price;type:decimal(10,2);default:0.00;comment:价格(原价)" json:"price"` - DiscountPrice float64 `gorm:"column:discount_price;type:decimal(10,2);comment:优惠价格" json:"discount_price"` - FirstTimePrice float64 `gorm:"column:first_time_price;type:decimal(10,2) unsigned;default:0.00;comment:首次购买价格" json:"first_time_price"` + SystemMemberId int64 `gorm:"column:system_member_id;type:bigint(19);primary_key;comment:主键id" json:"system_member_id"` + MemberDays uint `gorm:"column:member_days;type:int(10) unsigned;default:0;comment:会员天数" json:"member_days"` + Price float64 `gorm:"column:price;type:decimal(10,2);default:0.00;comment:价格(原价)" json:"price"` + DiscountPrice *float64 `gorm:"column:discount_price;type:decimal(10,2);comment:优惠价格" json:"discount_price"` + DiscountEndTime *time.Time `gorm:"column:discount_end_time;type:datetime;comment:优惠截止时间" json:"discount_end_time"` Model } diff --git a/api/requests/OrderMember.go b/api/requests/OrderMember.go index de227e0..9e9235f 100644 --- a/api/requests/OrderMember.go +++ b/api/requests/OrderMember.go @@ -2,6 +2,8 @@ package requests type OrderMemberRequest struct { GetOrderMemberPage // 获取会员订单列表-分页 + AddOrderMember // 创建会员订单 + GetOrderMemberPay // 获取会员订单支付数据 } // GetOrderMemberPage 获取会员订单列表-分页 @@ -18,3 +20,16 @@ type GetOrderMemberPage struct { EscrowTradeNo string `json:"escrow_trade_no" form:"escrow_trade_no" label:"第三方支付流水号"` CancelStatus *int `json:"cancel_status" form:"cancel_status" label:"取消状态"` } + +// AddOrderMember 创建会员订单 +type AddOrderMember struct { + SystemMemberId string `json:"system_member_id" form:"system_member_id" label:"会员" validate:"required"` + UserCouponId string `json:"user_coupon_id" form:"user_coupon_id" label:"优惠卷"` + PayChannel int `json:"pay_channel" form:"pay_channel" label:"支付渠道" validate:"required,oneof=1 2"` // 支付渠道(1:h5支付 2:app支付) + OrderPrice float64 `json:"order_price" form:"order_price" label:"订单金额" validate:"required"` // 订单金额 +} + +// GetOrderMemberPay 获取会员订单支付数据 +type GetOrderMemberPay struct { + ClientType int `json:"client_type" form:"client_type" label:"客户端类型" validate:"required,oneof=1 2"` // 客户端类型(1:h5 2:app) +} diff --git a/api/requests/OrderSingle.go b/api/requests/OrderSingle.go index 91745f8..aa62828 100644 --- a/api/requests/OrderSingle.go +++ b/api/requests/OrderSingle.go @@ -23,9 +23,10 @@ type GetOrderSinglePage struct { // AddOrderSingle 创建单项订单 type AddOrderSingle struct { - QuestionId string `json:"question_id" form:"question_id" label:"问题" validate:"required"` - UserCouponId string `json:"user_coupon_id" form:"user_coupon_id" label:"优惠卷"` - PayChannel int `json:"pay_channel" form:"pay_channel" label:"支付渠道" validate:"required,oneof=1 2 3"` // 支付渠道(1:h5支付 2:app支付 3:会员支付) + QuestionId string `json:"question_id" form:"question_id" label:"问题" validate:"required"` + UserCouponId string `json:"user_coupon_id" form:"user_coupon_id" label:"优惠卷"` + PayChannel int `json:"pay_channel" form:"pay_channel" label:"支付渠道" validate:"required,oneof=1 2 3"` // 支付渠道(1:h5支付 2:app支付 3:会员支付) + OrderPrice float64 `json:"order_price" form:"order_price" label:"订单金额" validate:"required"` // 订单金额 } // GetOrderSinglePay 获取单项订单支付数据 diff --git a/api/requests/UserCoupon.go b/api/requests/UserCoupon.go index b76306a..3d00170 100644 --- a/api/requests/UserCoupon.go +++ b/api/requests/UserCoupon.go @@ -1,7 +1,9 @@ package requests type UserCouponRequest struct { - GetUserCouponPage // 获取优惠卷列表-分页 + GetUserCouponPage // 获取优惠卷列表-分页 + GetUserUsableQuestionCoupon // 获取患者可使用优惠卷-单项 + GetUserUsableMemberCoupon // 获取患者可使用优惠卷-会员 } // GetUserCouponPage 获取优惠卷列表-分页 @@ -17,8 +19,14 @@ type GetUserCouponPage struct { ValidEndTime string `json:"valid_end_time" form:"valid_end_time" label:"有效结束时间"` // 同上 } -// GetUserUsableCoupon 获取用户当前可用优惠卷 -type GetUserUsableCoupon struct { - UserId int64 `json:"user_id" form:"user_id" label:"用户id"` - UserCouponStatus *int `json:"user_coupon_status" form:"user_coupon_status" label:"状态"` // 状态(0:未使用 1:已使用 3:已过期) +// GetUserUsableQuestionCoupon 获取患者可使用优惠卷-单项 +type GetUserUsableQuestionCoupon struct { + QuestionId string `json:"question_id" form:"question_id" label:"问题" validate:"required"` + AmountTotal float64 `json:"amount_total" form:"amount_total" label:"价格1" validate:"required"` +} + +// GetUserUsableMemberCoupon 获取患者可使用优惠卷-会员 +type GetUserUsableMemberCoupon struct { + SystemMemberId string `json:"system_member_id" form:"system_member_id" label:"会员" validate:"required"` + AmountTotal float64 `json:"amount_total" form:"amount_total" label:"价格1" validate:"required"` } diff --git a/api/router/router.go b/api/router/router.go index 143f961..8140100 100644 --- a/api/router/router.go +++ b/api/router/router.go @@ -91,6 +91,16 @@ func publicRouter(r *gin.Engine, api controller.Api) { // 首页 r.GET("/index", api.Public.GetIndex) + + // 回调 + callbackGroup := r.Group("/callback") + { + // 微信支付回调 + wxpayGroup := callbackGroup.Group("/wxpay") + { + wxpayGroup.GET("/jsapi", api.CallBack.WxJsapiPay) + } + } } // adminRouter 公共路由-验证权限 @@ -126,6 +136,9 @@ func privateRouter(r *gin.Engine, api controller.Api) { // 获取问题详情 questionGroup.GET("/:question_id", api.Question.GetQuestion) + + // 获取问题购买状态 + questionGroup.GET("/buy/status/:question_id", api.Question.GetQuestionBuyStatus) } // 用户 @@ -144,8 +157,11 @@ func privateRouter(r *gin.Engine, api controller.Api) { // 获取还未弹窗的优惠卷 couponGroup.GET("/unnotified", api.UserCoupon.GetUserCouponUnnotified) - // 获取用户当前可用优惠卷 - couponGroup.GET("/usable", api.UserCoupon.GetUserUsableCoupon) + // 获取患者可使用优惠卷-单项 + couponGroup.GET("/question", api.UserCoupon.GetUserUsableQuestionCoupon) + + // 获取患者可使用优惠卷-会员 + couponGroup.GET("/member", api.UserCoupon.GetUserUsableMemberCoupon) } // 收藏 @@ -182,7 +198,7 @@ func privateRouter(r *gin.Engine, api controller.Api) { // 获取单项订单支付数据 singleGroup.GET("/pay/:order_id", api.OrderSingle.GetOrderSinglePay) - // 删除单项订单支付数据 + // 删除单项订单 singleGroup.DELETE("/:order_id", api.OrderSingle.DeleteOrderSingle) } @@ -191,6 +207,12 @@ func privateRouter(r *gin.Engine, api controller.Api) { { // 获取会员订单列表-分页 memberGroup.GET("/page", api.OrderMember.GetOrderMemberPage) + + // 创建会员订单 + memberGroup.POST("", api.OrderMember.AddOrderMember) + + // 获取会员订单支付数据 + memberGroup.GET("/pay/:order_id", api.OrderMember.GetOrderMemberPay) } } @@ -200,4 +222,5 @@ func privateRouter(r *gin.Engine, api controller.Api) { // 获取会员配置数据 memberGroup.GET("", api.SystemMember.GetSystemMember) } + } diff --git a/api/service/OrderMember.go b/api/service/OrderMember.go new file mode 100644 index 0000000..b46df16 --- /dev/null +++ b/api/service/OrderMember.go @@ -0,0 +1,350 @@ +package service + +import ( + "context" + "errors" + "fmt" + "github.com/wechatpay-apiv3/wechatpay-go/services/payments/app" + "github.com/wechatpay-apiv3/wechatpay-go/services/payments/jsapi" + "gorm.io/gorm" + "hepa-calc-api/api/dao" + "hepa-calc-api/api/model" + "hepa-calc-api/config" + "hepa-calc-api/extend/weChat" + "hepa-calc-api/global" + "hepa-calc-api/utils" + "time" +) + +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 != "" { + return nil, errors.New("请勿重复操作") + } + + defer func(redisKey string) { + global.Redis.Del(context.Background(), redisKey) + }(redisKey) + + // 添加缓存 + _, err = global.Redis.Set(context.Background(), redisKey, "1", (10)*time.Second).Result() + if err != nil { + return nil, errors.New("生成订单失败") + } + + // 获取会员配置数据 + systemMemberDao := dao.SystemMemberDao{} + systemMember, err := systemMemberDao.GetSystemMemberById(SystemMemberId) + if err != nil { + return nil, errors.New("会员配置异常") + } + + // 获取用户数据 + userDao := dao.UserDao{} + user, err := userDao.GetUserById(UserId) + if err != nil || user == nil { + return nil, errors.New("用户错误") + } + + // 检测会员有效期 + if user.IsMember == 1 { + now := time.Now() + + diff := user.MemberExpireDate.Sub(now) + // 将差值转换为天数 + diffDays := int(diff.Hours() / 24) + totalDays := diffDays + int(systemMember.MemberDays) + if totalDays > 180 { + return nil, errors.New("超出会员有效期") + } + } + + var amountTotal float64 // 总金额 + var couponAmountTotal float64 // 优惠卷总金额 + var paymentAmountTotal float64 // 实际付款金额 + var orderStatus int // 订单状态(1:待支付 2:已完成 3:已取消) + var payStatus int // 支付状态(1:未支付 2:已支付 3:支付中 4:支付失败 5:支付超时 6:支付关闭 7:已撤销 8:转入退款) + var payTime *time.Time // 支付时间 + var escrowTradeNo string // 第三方支付流水号 + + // 获取会员购买价格 + systemMemberService := &SystemMemberService{} + amountTotal = systemMemberService.GetSystemMemberBuyPrice(systemMember) + if err != nil { + return nil, err + } + + // 检测用户优惠卷 + var userCoupon *model.UserCoupon + if UserCouponId != nil { + // 检测用户是否购买过会员 + userService := &UserService{} + isBuy := userService.CheckUserBuyOrderMember(UserId) + if isBuy == true { + // 已购买过会员,无法使用优惠卷 + return nil, errors.New("优惠券异常") + } + + // 获取优惠卷数据 + UserCouponDao := dao.UserCouponDao{} + userCoupon, err = UserCouponDao.GetUserCouponPreloadById(*UserCouponId) + if err != nil { + return nil, errors.New("优惠券异常") + } + + userCouponService := &UserCouponService{} + isCanUse, err := userCouponService.CheckUserCoupon(userCoupon, SystemMemberId, 2, amountTotal) + if err != nil || isCanUse == false { + return nil, errors.New("价格异常") + } + + // 优惠卷总金额 + couponAmountTotal = userCoupon.Coupon.CouponPrice + } + + // 实际付款金额 + paymentAmountTotal = amountTotal - couponAmountTotal // 实际付款金额 + if paymentAmountTotal < 0 { + return nil, errors.New("价格异常") + } + + if orderPrice != paymentAmountTotal { + return nil, errors.New("价格异常") + } + + orderStatus = 1 // 订单状态(1:待支付 2:已完成 3:已取消) + payStatus = 1 // 支付状态(1:未支付 2:已支付 3:支付中 4:支付失败 5:支付超时 6:支付关闭 7:已撤销 8:转入退款) + payTime = nil // 支付时间 + + // 生成订单号 + orderNo := global.Snowflake.Generate().String() + + // 创建订单 + orderMember = &model.OrderMember{ + UserId: UserId, + SystemMemberId: SystemMemberId, + OrderStatus: orderStatus, + IsDelete: 0, + PayChannel: payChannel, + PayStatus: payStatus, + PayTime: payTime, + RefundStatus: 0, + OrderNo: orderNo, + EscrowTradeNo: escrowTradeNo, + AmountTotal: amountTotal, + CouponAmountTotal: couponAmountTotal, + PaymentAmountTotal: paymentAmountTotal, + CancelStatus: 0, + CancelTime: nil, + CancelRemarks: "", + OrderRemarks: "", + } + + orderMemberDao := dao.OrderMemberDao{} + orderMember, err = orderMemberDao.AddOrderMember(tx, orderMember) + if err != nil { + return nil, errors.New("订单创建失败") + } + + // 创建优惠卷表 + if userCoupon != nil { + orderMemberCoupon := &model.OrderMemberCoupon{ + OrderId: orderMember.OrderId, + UserCouponId: *UserCouponId, + CouponName: userCoupon.Coupon.CouponName, + CouponUsePrice: userCoupon.Coupon.CouponPrice, + } + + orderMemberCouponDao := dao.OrderMemberCouponDao{} + orderMemberCoupon, err = orderMemberCouponDao.AddOrderMemberCoupon(tx, orderMemberCoupon) + if err != nil { + tx.Rollback() + return nil, errors.New("订单创建失败") + } + + // 修改优惠卷使用状态 + userCouponDao := dao.UserCouponDao{} + + userCouponData := make(map[string]interface{}) + userCouponData["user_coupon_status"] = 1 + userCouponData["coupon_use_date"] = time.Now().Format("2006-01-02 15:04:05") + err := userCouponDao.EditUserCouponById(tx, userCoupon.UserCouponId, userCouponData) + if err != nil { + return nil, errors.New("订单创建失败") + } + } + + return orderMember, nil +} + +// PutCancelOrderMember 取消会员订单 +// cancelReason:订单取消原因(1:主动取消 2:后台取消 3:支付超时取消) +func (r *OrderMemberService) PutCancelOrderMember(tx *gorm.DB, userId, orderId int64, cancelReason int) (bool, error) { + // 检测多次请求 + redisKey := "PutCancelOrderSingle" + fmt.Sprintf("%d", userId) + fmt.Sprintf("%d", orderId) + res, _ := global.Redis.Get(context.Background(), redisKey).Result() + if res != "" { + return false, errors.New("请勿重复操作") + } + + defer func(redisKey string) { + global.Redis.Del(context.Background(), redisKey) + }(redisKey) + + // 添加缓存 + _, err := global.Redis.Set(context.Background(), redisKey, "1", (10)*time.Second).Result() + if err != nil { + return false, errors.New("取消订单失败") + } + + // 获取订单数据 + orderSingleDao := dao.OrderSingleDao{} + maps := make(map[string]interface{}) + maps["user_id"] = userId + maps["order_id"] = orderId + orderSingle, err := orderSingleDao.GetOrderSingle(maps) + if err != nil { + return false, errors.New("订单异常") + } + + // 订单状态(1:待支付 2:已完成 3:已取消) + if orderSingle.OrderStatus == 2 { + return false, errors.New("订单已完成,无法取消") + } + + if orderSingle.OrderStatus == 3 { + return false, errors.New("订单已取消,请勿重复操作") + } + + // 取消状态(0:否 1:是) + if orderSingle.CancelStatus == 1 { + return false, errors.New("订单已取消") + } + + // 支付状态(1:未支付 2:已支付 3:支付中 4:支付失败 5:支付超时 6:支付关闭 7:已撤销 8:转入退款) + if orderSingle.PayStatus == 2 { + return false, errors.New("订单已支付,无法取消") + } + + // 订单退款状态(0:无退款 1:申请退款 2:退款中 3:退款成功 4:拒绝退款 5:退款关闭 6:退款异常 7:部分退款) + if orderSingle.RefundStatus == 1 { + return false, errors.New("订单已申请退款") + } + + if orderSingle.RefundStatus == 2 { + return false, errors.New("订单退款中") + } + + if orderSingle.RefundStatus == 3 { + return false, errors.New("订单已退款成功") + } + + if orderSingle.RefundStatus == 6 { + return false, errors.New("订单退款异常") + } + + // 修改订单为取消 + orderSingleData := make(map[string]interface{}) + orderSingleData["order_status"] = 3 + if cancelReason == 3 { + // 支付超时取消 + orderSingleData["pay_status"] = 5 + } + orderSingleData["cancel_status"] = 1 + orderSingleData["cancel_time"] = time.Now().Format("2006-01-02 15:04:05") + orderSingleData["cancel_remarks"] = utils.OrderCancelReasonToString(cancelReason) + orderSingleData["updated_at"] = time.Now().Format("2006-01-02 15:04:05") + err = orderSingleDao.EditOrderSingleById(tx, orderId, orderSingleData) + if err != nil { + return false, errors.New("订单取消失败") + } + + // 退还订单优惠卷 + if orderSingle.CouponAmountTotal != 0 { + // 获取订单优惠卷数据 + orderSingleCouponDao := dao.OrderSingleCouponDao{} + orderSingleCoupon, err := orderSingleCouponDao.GetOrderSingleCouponByOrderId(orderId) + if err != nil { + tx.Rollback() + return false, errors.New("订单取消失败") + } + + userCouponService := &UserCouponService{} + userCouponService.ReturnUserCoupon(tx, orderSingleCoupon.UserCouponId) + } + + return true, nil +} + +// GetJsapiPrepay 获取jsapi预支付交易会话标识 +func (r *OrderMemberService) GetJsapiPrepay(m *model.OrderMember) (prepay *jsapi.PrepayWithRequestPaymentResponse, err error) { + // 获取用户数据 + userDao := dao.UserDao{} + user, err := userDao.GetUserById(m.UserId) + if err != nil || user == nil { + return nil, errors.New("用户错误") + } + + if user.OpenId != "" { + return nil, errors.New("发起支付失败") + } + + jsapiRequest := weChat.JsapiRequest{ + AppId: config.C.Wechat.AppId, + MchId: config.C.Wechat.Pay1659662936.MchId, + Description: "肝病算一算", + OutTradeNo: m.OrderNo, + NotifyUrl: config.C.Wechat.RefundNotifyDomain + config.C.Wechat.RefundNotifyUrl, + Amount: weChat.JsapiRequestAmountRequest{ + Total: int64(m.PaymentAmountTotal * 100), + Currency: "CNY", + }, + Payer: weChat.JsapiRequestPayerRequest{OpenId: user.OpenId}, + } + + prepay, err = jsapiRequest.GetJsapiPrepay() + if err != nil { + return nil, err + } + + return prepay, nil +} + +// GetAppPrepay 获取app预支付交易会话标识 +func (r *OrderMemberService) GetAppPrepay(m *model.OrderMember) (prepay *app.PrepayWithRequestPaymentResponse, err error) { + // 获取用户数据 + userDao := dao.UserDao{} + user, err := userDao.GetUserById(m.UserId) + if err != nil || user == nil { + return nil, errors.New("用户错误") + } + + if user.OpenId != "" { + return nil, errors.New("发起支付失败") + } + + appRequest := weChat.AppRequest{ + AppId: config.C.Wechat.AppId, + MchId: config.C.Wechat.Pay1659662936.MchId, + Description: "肝病算一算", + OutTradeNo: m.OrderNo, + NotifyUrl: config.C.Wechat.RefundNotifyDomain + config.C.Wechat.RefundNotifyUrl, + Amount: weChat.AppRequestAmountRequest{ + Total: int64(m.PaymentAmountTotal * 100), + Currency: "CNY", + }, + } + + prepay, err = appRequest.GetAppPrepay() + if err != nil { + return nil, err + } + + return prepay, nil +} diff --git a/api/service/OrderSingle.go b/api/service/OrderSingle.go index fd92ba5..bcbb595 100644 --- a/api/service/OrderSingle.go +++ b/api/service/OrderSingle.go @@ -20,7 +20,7 @@ type OrderSingleService struct { } // AddOrderSingle 创建单项订单 -func (r *OrderSingleService) AddOrderSingle(tx *gorm.DB, UserId, QuestionId int64, UserCouponId *int64, payChannel int) (orderSingle *model.OrderSingle, err error) { +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() @@ -52,16 +52,24 @@ func (r *OrderSingleService) AddOrderSingle(tx *gorm.DB, UserId, QuestionId int6 return nil, err } - var amountTotal float64 // 总金额 + var amountTotal *float64 // 总金额 var couponAmountTotal float64 // 优惠卷总金额 var paymentAmountTotal float64 // 实际付款金额 + var orderStatus int // 订单状态(1:待支付 2:已完成 3:已取消) + var payStatus int // 支付状态(1:未支付 2:已支付 3:支付中 4:支付失败 5:支付超时 6:支付关闭 7:已撤销 8:转入退款) + var payTime *time.Time // 支付时间 + var escrowTradeNo string // 第三方支付流水号 - // 获取问题价格 + // 获取问题最终价格 amountTotal, err = questionService.GetUserBuyPrice(UserId, QuestionId) if err != nil { return nil, err } + if amountTotal == nil { + return nil, errors.New("价格错误") + } + // 检测用户优惠卷 var userCoupon *model.UserCoupon if UserCouponId != nil { @@ -73,7 +81,7 @@ func (r *OrderSingleService) AddOrderSingle(tx *gorm.DB, UserId, QuestionId int6 } userCouponService := &UserCouponService{} - isCanUse, err := userCouponService.CheckUserCoupon(userCoupon, QuestionId, 1, amountTotal) + isCanUse, err := userCouponService.CheckUserCoupon(userCoupon, QuestionId, 1, *amountTotal) if err != nil || isCanUse == false { return nil, errors.New("价格异常") } @@ -82,15 +90,26 @@ func (r *OrderSingleService) AddOrderSingle(tx *gorm.DB, UserId, QuestionId int6 couponAmountTotal = userCoupon.Coupon.CouponPrice } - // 实际付款金额 - paymentAmountTotal = amountTotal - couponAmountTotal - if paymentAmountTotal < 0 { - return nil, errors.New("价格异常") - } - // 会员支付 if payChannel == 3 { - paymentAmountTotal = 0 + paymentAmountTotal = 0 // 实际付款金额 + orderStatus = 2 // 订单状态(1:待支付 2:已完成 3:已取消) + payStatus = 2 // 支付状态(1:未支付 2:已支付 3:支付中 4:支付失败 5:支付超时 6:支付关闭 7:已撤销 8:转入退款) + + now := time.Now() + payTime = &now // 支付时间 + + escrowTradeNo = "GD" + global.Snowflake.Generate().String() // 第三方支付流水号 + } else { + // 实际付款金额 + paymentAmountTotal = *amountTotal - couponAmountTotal + if orderPrice != paymentAmountTotal { + return nil, errors.New("价格异常") + } + + orderStatus = 1 // 订单状态(1:待支付 2:已完成 3:已取消) + payStatus = 1 // 支付状态(1:未支付 2:已支付 3:支付中 4:支付失败 5:支付超时 6:支付关闭 7:已撤销 8:转入退款) + payTime = nil // 支付时间 } // 生成订单号 @@ -100,15 +119,15 @@ func (r *OrderSingleService) AddOrderSingle(tx *gorm.DB, UserId, QuestionId int6 orderSingle = &model.OrderSingle{ UserId: UserId, QuestionId: QuestionId, - OrderStatus: 1, + OrderStatus: orderStatus, IsDelete: 0, PayChannel: payChannel, - PayStatus: 1, - PayTime: nil, + PayStatus: payStatus, + PayTime: payTime, RefundStatus: 0, OrderNo: orderNo, - EscrowTradeNo: "", - AmountTotal: amountTotal, + EscrowTradeNo: escrowTradeNo, + AmountTotal: *amountTotal, CouponAmountTotal: couponAmountTotal, PaymentAmountTotal: paymentAmountTotal, CancelStatus: 0, @@ -152,6 +171,9 @@ func (r *OrderSingleService) AddOrderSingle(tx *gorm.DB, UserId, QuestionId int6 } // 增加至未支付取消订单延迟队列 + if payChannel == 1 || payChannel == 2 { + + } return orderSingle, nil } diff --git a/api/service/Question.go b/api/service/Question.go index 0866b77..397856c 100644 --- a/api/service/Question.go +++ b/api/service/Question.go @@ -4,6 +4,7 @@ import ( "errors" "hepa-calc-api/api/dao" "hepa-calc-api/api/model" + "time" ) type QuestionService struct { @@ -126,14 +127,14 @@ func (r *QuestionService) GetUserFirstTimeBuyPrice(userId, questionId int64) (f // GetQuestionBuyCount 获取问题被购买数量 func (r *QuestionService) GetQuestionBuyCount(userId, questionId int64) (c int, err error) { // 未购买过 - systemSingleDao := dao.SystemSingleDao{} + orderSingleDao := dao.OrderSingleDao{} maps := make(map[string]interface{}) maps["user_id"] = userId maps["question_id"] = questionId maps["order_status"] = 2 maps["refund_status"] = 0 - buyCount, err := systemSingleDao.GetSystemSingleCount(maps) + buyCount, err := orderSingleDao.GetOrderSingleCount(maps) if err != nil { return 0, err } @@ -141,20 +142,13 @@ func (r *QuestionService) GetQuestionBuyCount(userId, questionId int64) (c int, return int(buyCount), nil } -// GetUserBuyPrice 获取问题价格 -func (r *QuestionService) GetUserBuyPrice(userId, questionId int64) (p float64, err error) { +// GetUserBuyPrice 获取问题最终价格 +func (r *QuestionService) GetUserBuyPrice(userId, questionId int64) (p *float64, err error) { // 获取问题详情 questionDao := dao.QuestionDao{} question, err := questionDao.GetQuestionById(questionId) if err != nil { - return 0, errors.New("题目异常") - } - - // 问题价格 - p = question.Price - - if question.DiscountPrice != nil { - p = *question.DiscountPrice + return nil, errors.New("题目异常") } // 检测用户是否购买过该问题 @@ -166,10 +160,19 @@ func (r *QuestionService) GetUserBuyPrice(userId, questionId int64) (p float64, maps := make(map[string]interface{}) systemSingle, err := systemSingleDao.GetSystemSingle(maps) if err != nil { - return 0, err + return nil, err } - p = systemSingle.FirstTimePrice + p = &systemSingle.FirstTimePrice + } + + // 处理问题优惠价格 + if p == nil { + p = r.HandleQuestionDiscountPrice(question.DiscountPrice, question.DiscountEndTime) + } + + if p == nil { + p = &question.Price } return p, nil @@ -187,3 +190,19 @@ func (r *QuestionService) CheckQuestion(m *model.Question) (bool, error) { return true, nil } + +// HandleQuestionDiscountPrice 处理问题优惠价格 +func (r *QuestionService) HandleQuestionDiscountPrice(discountPrice *float64, discountEndTime *time.Time) (p *float64) { + // 优惠价格 + if discountPrice != nil { + // 检测是否超出优惠时间 + now := time.Now() + if discountEndTime.Before(now) { + p = nil + } else { + p = discountPrice + } + } + + return p +} diff --git a/api/service/SystemMember.go b/api/service/SystemMember.go new file mode 100644 index 0000000..3d9008a --- /dev/null +++ b/api/service/SystemMember.go @@ -0,0 +1,17 @@ +package service + +import "hepa-calc-api/api/model" + +type SystemMemberService struct { +} + +// GetSystemMemberBuyPrice 获取会员购买价格 +func (r *SystemMemberService) GetSystemMemberBuyPrice(m *model.SystemMember) (p float64) { + p = m.Price + + if m.DiscountPrice != nil { + p = *m.DiscountPrice + } + + return p +} diff --git a/api/service/User.go b/api/service/User.go index c6734c2..3a80aa1 100644 --- a/api/service/User.go +++ b/api/service/User.go @@ -3,6 +3,8 @@ package service import ( "errors" "fmt" + "hepa-calc-api/api/dao" + "hepa-calc-api/api/model" "hepa-calc-api/extend/aliyun" "io" "math/rand" @@ -55,3 +57,28 @@ func (r *UserService) HandleUserAvatar(wxAvatar string) (avatar string, err erro return ossPath, nil } + +// CheckUserMember 检测用户会员 +func (r *UserService) CheckUserMember(user *model.User) bool { + if user.IsMember == 0 { + return false + } + + now := time.Now() + if user.MemberExpireDate.Before(now) { + return false + } + + return true +} + +// CheckUserBuyOrderMember 检测用户是否购买过会员 +func (r *UserService) CheckUserBuyOrderMember(userId int64) bool { + orderMemberDao := dao.OrderMemberDao{} + orderMember, _ := orderMemberDao.GetUserFirstTimeBuyOrderMember(userId) + if orderMember == nil { + return false + } + + return true +} diff --git a/api/service/UserCollection.go b/api/service/UserCollection.go index 0dc4cf6..bf3092f 100644 --- a/api/service/UserCollection.go +++ b/api/service/UserCollection.go @@ -10,8 +10,8 @@ import ( type UserCollectionService struct { } -// CheckUserCollectionQuestion 检测问题是否被用户收藏 -func (r *UserCollectionService) CheckUserCollectionQuestion(userId, questionId int64) bool { +// GetUserCollectionQuestionStatus 检测用户收藏状态 +func (r *UserCollectionService) GetUserCollectionQuestionStatus(userId, questionId int64) bool { userCollectionDao := dao.UserCollectionDao{} maps := make(map[string]interface{}) @@ -27,8 +27,8 @@ func (r *UserCollectionService) CheckUserCollectionQuestion(userId, questionId i // PutUserCollection 收藏题目 func (r *UserCollectionService) PutUserCollection(userId, questionId int64) (bool, error) { - // 检测问题是否被用户收藏 - IsCollection := r.CheckUserCollectionQuestion(userId, questionId) + // 检测用户收藏状态 + IsCollection := r.GetUserCollectionQuestionStatus(userId, questionId) if IsCollection == true { // 已收藏 return true, nil @@ -60,8 +60,8 @@ func (r *UserCollectionService) PutUserCollection(userId, questionId int64) (boo // PutUserCollectionCancel 取消收藏题目 func (r *UserCollectionService) PutUserCollectionCancel(userId, questionId int64) (bool, error) { - // 检测问题是否被用户收藏 - IsCollection := r.CheckUserCollectionQuestion(userId, questionId) + // 检测用户收藏状态 + IsCollection := r.GetUserCollectionQuestionStatus(userId, questionId) if IsCollection == false { // 已收藏 return true, nil diff --git a/api/service/UserCoupon.go b/api/service/UserCoupon.go index b4c3e53..4baee2a 100644 --- a/api/service/UserCoupon.go +++ b/api/service/UserCoupon.go @@ -4,6 +4,7 @@ import ( "errors" "gorm.io/gorm" "hepa-calc-api/api/dao" + "hepa-calc-api/api/dto" "hepa-calc-api/api/model" "time" ) @@ -12,6 +13,7 @@ type UserCouponService struct { } // CheckUserCoupon 检测用户优惠卷 +// orderType:类型(1:单项 2:会员) func (r *UserCouponService) CheckUserCoupon(m *model.UserCoupon, id int64, orderType int, amountTotal float64) (bool, error) { if m.UserCouponStatus == 1 { return false, errors.New("优惠卷异常") @@ -114,3 +116,93 @@ func (r *UserCouponService) ReturnUserCoupon(tx *gorm.DB, userCouponId int64) bo return true } + +// GetUserUsableQuestionCoupon 获取患者可使用优惠卷-单项 +func (r *UserCouponService) GetUserUsableQuestionCoupon(userId, questionId int64, amountTotal float64) (g []*dto.UserCouponDto, err error) { + // 获取用户数据 + userDao := dao.UserDao{} + user, err := userDao.GetUserById(userId) + if err != nil || user == nil { + return nil, errors.New("用户错误") + } + + // 检测用户会员 + userService := &UserService{} + isMember := userService.CheckUserMember(user) + if isMember == true { + // 会员无需使用优惠卷 + return nil, nil + } + + // 获取用户优惠卷 + UserCouponDao := dao.UserCouponDao{} + + maps := make(map[string]interface{}) + maps["user_id"] = userId + maps["user_coupon_status"] = 0 + userCoupons, err := UserCouponDao.GetUserCouponPreloadList(maps) + if err != nil { + return nil, errors.New("优惠券异常") + } + + //定义返回数据 + var responses []*model.UserCoupon + + for _, userCoupon := range userCoupons { + isCanUse, err := r.CheckUserCoupon(userCoupon, questionId, 1, amountTotal) + if err != nil || isCanUse == false { + continue + } + + responses = append(responses, userCoupon) + } + + g = dto.GetUserCouponListDto(responses) + + return g, nil +} + +// GetUserUsableMemberCoupon 获取患者可使用优惠卷-会员 +func (r *UserCouponService) GetUserUsableMemberCoupon(userId, systemMemberId int64, amountTotal float64) (g []*dto.UserCouponDto, err error) { + // 获取用户数据 + userDao := dao.UserDao{} + user, err := userDao.GetUserById(userId) + if err != nil || user == nil { + return nil, errors.New("用户错误") + } + + // 检测用户会员 + userService := &UserService{} + isMember := userService.CheckUserMember(user) + if isMember == true { + // 会员无需使用优惠卷 + return nil, nil + } + + // 获取用户优惠卷 + UserCouponDao := dao.UserCouponDao{} + + maps := make(map[string]interface{}) + maps["user_id"] = userId + maps["user_coupon_status"] = 0 + userCoupons, err := UserCouponDao.GetUserCouponPreloadList(maps) + if err != nil { + return nil, errors.New("优惠券异常") + } + + //定义返回数据 + var responses []*model.UserCoupon + + for _, userCoupon := range userCoupons { + isCanUse, err := r.CheckUserCoupon(userCoupon, systemMemberId, 2, amountTotal) + if err != nil || isCanUse == false { + continue + } + + responses = append(responses, userCoupon) + } + + g = dto.GetUserCouponListDto(responses) + + return g, nil +} diff --git a/config.yaml b/config.yaml index be92a9b..569d5ff 100644 --- a/config.yaml +++ b/config.yaml @@ -45,7 +45,7 @@ dysms: dysms-access-key: LTAI4GGygjsKhyBwvvC3CghV dysms-access-secret: rcx7lO9kQxG10m8NqNPEfEtT9IS8EI -# [微信]v +# [微信] wechat: app-id: wxc8ac5051745bc795 app-secret: 678b63a8a7541e528abc3040c3cea809 diff --git a/extend/weChat/base.go b/extend/weChat/base.go index 42daa77..59a4dfc 100644 --- a/extend/weChat/base.go +++ b/extend/weChat/base.go @@ -7,25 +7,24 @@ import ( "github.com/wechatpay-apiv3/wechatpay-go/core/option" "github.com/wechatpay-apiv3/wechatpay-go/utils" "hepa-calc-api/config" - "path/filepath" +) + +var ( + mchId = config.C.Wechat.Pay1659662936.MchId // 商户号 + mchCertificateSerialNumber = config.C.Wechat.Pay1659662936.MchCertificateSerialNumber // 商户证书序列号 + v3ApiSecret = config.C.Wechat.Pay1659662936.V3ApiSecret // 商户APIv3密钥 + privateKeyPath = "extend/weChat/certs/" + mchId + "/apiclient_key.pem" // 商户私钥文件地址 ) // 创建客户端 func createClient() (*core.Client, error) { - mchId := config.C.Wechat.Pay1659662936.MchId // 商户号 - mchCertificateSerialNumber := config.C.Wechat.Pay1659662936.MchCertificateSerialNumber // 商户证书序列号 - v3ApiSecret := config.C.Wechat.Pay1659662936.V3ApiSecret // 商户APIv3密钥 - if mchId == "" { return nil, errors.New("商户号错误") } - // 使用 utils 提供的函数从本地文件中加载商户私钥,商户私钥会用来生成请求的签名 - certsDir := filepath.Join("extend/weChat/certs", mchId, "/apiclient_key.pem") - // certsDir := filepath.Join("extend/weChat/certs", "/1636644248/apiclient_key.pem") - mchPrivateKey, err := utils.LoadPrivateKeyWithPath(certsDir) + mchPrivateKey, err := utils.LoadPrivateKeyWithPath(privateKeyPath) if err != nil { - return nil, errors.New("微信签名生成失败") + return nil, errors.New("微信支付生成失败") } ctx := context.Background() diff --git a/extend/weChat/parseNotify.go b/extend/weChat/parseNotify.go new file mode 100644 index 0000000..9e46d5b --- /dev/null +++ b/extend/weChat/parseNotify.go @@ -0,0 +1,43 @@ +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" + "github.com/wechatpay-apiv3/wechatpay-go/core/notify" + "github.com/wechatpay-apiv3/wechatpay-go/services/payments" + "github.com/wechatpay-apiv3/wechatpay-go/utils" +) + +// ParseNotify 回调通知的验签与解密 +func ParseNotify(c *gin.Context) (notifyReq *notify.Request, err error) { + // 使用 utils 提供的函数从本地文件中加载商户私钥,商户私钥会用来生成请求的签名 + mchPrivateKey, err := utils.LoadPrivateKeyWithPath(privateKeyPath) + if err != nil { + return nil, errors.New("微信支付生成失败") + } + + // 1. 使用 `RegisterDownloaderWithPrivateKey` 注册下载器 + err = downloader.MgrInstance().RegisterDownloaderWithPrivateKey(c, mchPrivateKey, mchCertificateSerialNumber, mchId, v3ApiSecret) + if err != nil { + return nil, err + } + + // 2. 获取商户号对应的微信支付平台证书访问器 + certificateVisitor := downloader.MgrInstance().GetCertificateVisitor(mchId) + // 3. 使用证书访问器初始化 `notify.Handler` + handler, err := notify.NewRSANotifyHandler(v3ApiSecret, verifiers.NewSHA256WithRSAVerifier(certificateVisitor)) + + transaction := new(payments.Transaction) + notifyReq, err = handler.ParseNotifyRequest(context.Background(), c.Request, transaction) + // 如果验签未通过,或者解密失败 + if err != nil { + return nil, err + } + + fmt.Println(transaction) + return notifyReq, nil +}