feat: order rpc

This commit is contained in:
zzw
2026-09-14 10:56:34 +08:00
parent 5a25bf9263
commit d895c9456d
5 changed files with 105 additions and 97 deletions
+4 -3
View File
@@ -7,12 +7,13 @@
package order
import (
_ "google.golang.org/genproto/googleapis/api/annotations"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
_ "google.golang.org/genproto/googleapis/api/annotations"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
)
const (
+12 -32
View File
@@ -4,13 +4,11 @@ import (
"context"
"errors"
"lone-services/pkg/modelbase"
pkgpay "lone-services/pkg/payment"
"lone-services/pkg/utils"
payment "lone-services/rpc/payment/pb"
"lone-services/services/payment/internal/channel"
"lone-services/services/payment/internal/dao"
"lone-services/services/payment/internal/model"
"lone-services/services/payment/internal/svc"
"github.com/zeromicro/go-zero/core/logx"
@@ -51,7 +49,7 @@ func (l *NotifyLogic) Notify(in *payment.NotifyReq) (*payment.Response, error) {
return outResponse(utils.Fail, "支付未成功"), nil
}
if err := markOrderPaid(result); err != nil {
if err := l.markOrderPaid(result); err != nil {
l.Errorf("notify mark paid: %v", err)
return outResponse(utils.Fail, err.Error()), nil
}
@@ -59,38 +57,20 @@ func (l *NotifyLogic) Notify(in *payment.NotifyReq) (*payment.Response, error) {
return okResponse(result), nil
}
func markOrderPaid(result *pkgpay.NotifyResult) error {
func (l *NotifyLogic) markOrderPaid(result *pkgpay.NotifyResult) error {
if result.OutTradeNo == utils.StringEmpty {
return errors.New("out_trade_no empty")
}
var row dao.OrderPayRow
err := model.OrderModel{}.Init().GetOne(modelbase.Params{
Eq: map[string]string{"order_sn": result.OutTradeNo},
}, &row)
if err != nil {
return err
}
if row.Id < utils.NumberOne {
return errors.New("order not found")
}
if row.Status == dao.PayStatusOk {
return nil // idempotent
}
expected := yuanToFen(row.PayPrice)
if expected > 0 && result.AmountFen > 0 && expected != result.AmountFen {
return errors.New("amount mismatch")
}
updates := map[string]any{
"status": dao.PayStatusOk,
}
if result.OutTradeNo != utils.StringEmpty {
updates["out_trade_no"] = result.OutTradeNo
}
_, err = model.OrderModel{}.Init().Edit(modelbase.Params{
Eq: map[string]string{"order_sn": result.OutTradeNo},
}, updates)
// 回调无登录态:先按支付中状态回写;金额校验依赖 order 服务 UpdateOrderDPayStatus 内实现。
err := updateOrderPayStatus(
l.ctx,
l.svcCtx.OrderSvcName,
result.OutTradeNo,
dao.PayStatusOk,
float64(result.AmountFen)/100,
result.TransactionID,
0,
)
return err
}
@@ -0,0 +1,73 @@
package logic
import (
"context"
"fmt"
"lone-services/pkg/utils"
orderpb "lone-services/rpc/order/pb"
"lone-services/services/payment/internal/svc"
)
func getOrderInfo(ctx context.Context, orderSvcName string, userID int64, orderSn string, typ uint8) (*orderpb.OrderInfoResponse, error) {
if orderSvcName == utils.StringEmpty {
return nil, fmt.Errorf("order service not configured")
}
if userID < utils.NumberOne || orderSn == utils.StringEmpty || typ < utils.NumberOne {
return nil, fmt.Errorf("invalid get order params")
}
cli, err := svc.GetRpcClient(orderSvcName)
if err != nil {
return nil, err
}
client := orderpb.NewOrderClient(cli.Conn())
info, err := client.GetOrderInfo(ctx, &orderpb.GetOrderInfoRequest{
UserId: userID,
OrderSn: orderSn,
Type: int32(typ),
})
if err != nil {
return nil, err
}
if info == nil || info.Id < utils.NumberOne {
return nil, fmt.Errorf("order not found")
}
return info, nil
}
func updateOrderPayStatus(ctx context.Context, orderSvcName, orderSn string, status uint8, payPrice float64, remitNo string, typ uint8) error {
if orderSvcName == utils.StringEmpty {
return fmt.Errorf("order service not configured")
}
cli, err := svc.GetRpcClient(orderSvcName)
if err != nil {
return err
}
client := orderpb.NewOrderClient(cli.Conn())
_, err = client.UpdateOrderDPayStatus(ctx, &orderpb.UpdateOrderPayStatusRequest{
OrderSn: orderSn,
Status: int32(status),
PayPrice: payPrice,
RemitNo: remitNo,
Type: int32(typ),
})
return err
}
func orderPayStatus(info *orderpb.OrderInfoResponse) uint8 {
if info == nil {
return 0
}
return uint8(info.Status)
}
func orderOutTradeNo(info *orderpb.OrderInfoResponse) string {
if info == nil {
return utils.StringEmpty
}
if info.OutTradeNo != utils.StringEmpty {
return info.OutTradeNo
}
return info.OrderSn
}
+10 -48
View File
@@ -4,7 +4,6 @@ import (
"context"
"fmt"
"math"
"strconv"
"lone-services/pkg/modelbase"
pkgpay "lone-services/pkg/payment"
@@ -12,7 +11,6 @@ import (
payment "lone-services/rpc/payment/pb"
"lone-services/services/payment/internal/channel"
"lone-services/services/payment/internal/dao"
"lone-services/services/payment/internal/model"
"lone-services/services/payment/internal/svc"
"github.com/zeromicro/go-zero/core/logx"
@@ -45,22 +43,23 @@ func (l *PayLogic) Pay(in *payment.PayReq) (*payment.Response, error) {
client := resolveClient(in.Client, loginUser.ClientCode)
channelCode := resolveChannel(in.Channel)
order, err := loadPayableOrder(in.OrderSn, loginUser.ID)
orderInfo, err := getOrderInfo(l.ctx, l.svcCtx.OrderSvcName, loginUser.ID, in.OrderSn, loginUser.Type)
if err != nil {
l.Errorf("pay load order: %v", err)
return failResponse(utils.ErrorNotFund), nil
}
if order.Status == dao.PayStatusOk {
if orderPayStatus(orderInfo) == dao.PayStatusOk {
return &payment.Response{
Code: int32(utils.ErrorRepeatPayment.GetCode()),
Msg: utils.ErrorRepeatPayment.GetMsg(),
}, nil
}
if order.Status != dao.PayStatusWait && order.Status != dao.PayStatusIn && order.Status != dao.PayStatusFail {
status := orderPayStatus(orderInfo)
if status != dao.PayStatusWait && status != dao.PayStatusIn && status != dao.PayStatusFail {
return outResponse(utils.Fail, "订单状态不可支付"), nil
}
amountFen := yuanToFen(order.PayPrice)
amountFen := yuanToFen(orderInfo.PayPrice)
if amountFen <= 0 {
return outResponse(utils.Fail, "支付金额无效"), nil
}
@@ -97,39 +96,17 @@ func (l *PayLogic) Pay(in *payment.PayReq) (*payment.Response, error) {
return outResponse(utils.Fail, err.Error()), nil
}
_ = markOrderPaying(in.OrderSn, channelCode, in.OrderSn)
_ = updateOrderPayStatus(l.ctx, l.svcCtx.OrderSvcName, in.OrderSn, dao.PayStatusIn, 0, in.OrderSn, loginUser.Type)
return okResponse(result), nil
}
func loadPayableOrder(orderSn string, userID int64) (*dao.OrderPayRow, error) {
var row dao.OrderPayRow
err := model.OrderModel{}.Init().GetOne(modelbase.Params{
Eq: map[string]string{
"order_sn": orderSn,
},
}, &row)
if err != nil {
return nil, err
}
if row.Id < utils.NumberOne {
return nil, fmt.Errorf("order not found")
}
if row.UserId > 0 && row.UserId != userID {
return nil, fmt.Errorf("order user mismatch")
}
return &row, nil
}
func loadOpenID(userID int64) (string, error) {
var cred dao.UserCredential
err := model.UserCredentialModel{}.Init().GetOne(modelbase.Params{
Eq: map[string]string{
"user_id": strconv.FormatInt(userID, 10),
"credential_type": dao.CredentialTypeOpenid,
"status": strconv.Itoa(int(dao.CredentialEnabled)),
},
}, &cred)
err := modelbase.DB().Table(modelbase.Prefix()+"user_credential").
Where("user_id = ? AND credential_type = ? AND status = ?",
userID, dao.CredentialTypeOpenid, dao.CredentialEnabled).
Take(&cred).Error
if err != nil {
return "", err
}
@@ -139,21 +116,6 @@ func loadOpenID(userID int64) (string, error) {
return cred.Identifier, nil
}
func markOrderPaying(orderSn, channelCode, outTradeNo string) error {
payType := dao.ChannelToPayType(channelCode)
updates := map[string]any{
"status": dao.PayStatusIn,
"out_trade_no": outTradeNo,
}
if payType > 0 {
updates["pay_type"] = payType
}
_, err := model.OrderModel{}.Init().Edit(modelbase.Params{
Eq: map[string]string{"order_sn": orderSn},
}, updates)
return err
}
func yuanToFen(yuan float64) int64 {
return int64(math.Round(yuan * 100))
}
+6 -14
View File
@@ -6,13 +6,11 @@ import (
"strconv"
"time"
"lone-services/pkg/modelbase"
pkgpay "lone-services/pkg/payment"
"lone-services/pkg/utils"
payment "lone-services/rpc/payment/pb"
"lone-services/services/payment/internal/channel"
"lone-services/services/payment/internal/dao"
"lone-services/services/payment/internal/model"
"lone-services/services/payment/internal/svc"
"github.com/zeromicro/go-zero/core/logx"
@@ -45,14 +43,11 @@ func (l *RefundLogic) Refund(in *payment.RefundReq) (*payment.Response, error) {
channelCode := resolveChannel(in.Channel)
client := resolveClient(utils.StringEmpty, loginUser.ClientCode)
var row dao.OrderPayRow
err := model.OrderModel{}.Init().GetOne(modelbase.Params{
Eq: map[string]string{"order_sn": in.OrderSn},
}, &row)
if err != nil || row.Id < utils.NumberOne {
orderInfo, err := getOrderInfo(l.ctx, l.svcCtx.OrderSvcName, loginUser.ID, in.OrderSn, loginUser.Type)
if err != nil {
return failResponse(utils.ErrorNotFund), nil
}
if row.Status != dao.PayStatusOk {
if orderPayStatus(orderInfo) != dao.PayStatusOk {
return outResponse(utils.Fail, "订单未支付,无法退款"), nil
}
@@ -61,7 +56,7 @@ func (l *RefundLogic) Refund(in *payment.RefundReq) (*payment.Response, error) {
return failResponse(utils.ErrorParams), nil
}
refundFen := yuanToFen(refundYuan)
totalFen := yuanToFen(row.PayPrice)
totalFen := yuanToFen(orderInfo.PayPrice)
if refundFen > totalFen {
return outResponse(utils.Fail, "退款金额不能大于支付金额"), nil
}
@@ -71,11 +66,8 @@ func (l *RefundLogic) Refund(in *payment.RefundReq) (*payment.Response, error) {
return outResponse(utils.Fail, err.Error()), nil
}
outTradeNo := row.OutTradeNo
if outTradeNo == utils.StringEmpty {
outTradeNo = row.OrderSn
}
outRefundNo := fmt.Sprintf("RF%s%d", row.OrderSn, time.Now().UnixNano())
outTradeNo := orderOutTradeNo(orderInfo)
outRefundNo := fmt.Sprintf("RF%s%d", orderInfo.OrderSn, time.Now().UnixNano())
result, err := ch.Refund(l.ctx, &pkgpay.RefundRequest{
OutTradeNo: outTradeNo,