增加了微信支付

This commit is contained in:
wucongxing8150 2024-07-25 14:39:57 +08:00
parent 8c84ed3656
commit a525a67a01
29 changed files with 1173 additions and 149 deletions

View File

@ -12,4 +12,5 @@ type Api struct {
OrderSingle // 订单-单项
SystemMember // 会员配置
OrderMember // 订单-会员
CallBack // 回调
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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

View File

@ -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 {

View File

@ -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,

View File

@ -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,
}
}

View File

@ -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"`
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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 获取单项订单支付数据

View File

@ -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"`
}

View File

@ -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)
}
}

350
api/service/OrderMember.go Normal file
View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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

View File

@ -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
}

View File

@ -45,7 +45,7 @@ dysms:
dysms-access-key: LTAI4GGygjsKhyBwvvC3CghV
dysms-access-secret: rcx7lO9kQxG10m8NqNPEfEtT9IS8EI
# [微信]v
# [微信]
wechat:
app-id: wxc8ac5051745bc795
app-secret: 678b63a8a7541e528abc3040c3cea809

View File

@ -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()

View File

@ -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
}