package model
import "time"
type ShoppingCart struct { // 购物车
BaseModel
User int32 `gorm:"type:int;index"` // 在购物车列表中我们需要查询当前用户的购物车记录
Goods int32 `gorm:"type:int;index"` // 加索引:我们需要查询时候, 1. 会影响插入性能 2. 会占用磁盘
Nums int32 `gorm:"type:int"` // 商品的数量
Checked bool // 是否选中
}
func (ShoppingCart) TableName() string {
return "shoppingcart"
}
type OrderInfo struct {
BaseModel
User int32 `gorm:"type:int;index"`
OrderSn string `gorm:"type:varchar(30);index"` // 订单号,我们平台自己生成的订单号
PayType string `gorm:"type:varchar(20) comment 'alipay(支付宝), wechat(微信)'"`
// status大家可以考虑使用iota来做
Status string `gorm:"type:varchar(20) comment 'PAYING(待支付), TRADE_SUCCESS(成功), TRADE_CLOSED(超时关闭), WAIT_BUYER_PAY(交易创建), TRADE_FINISHED(交易结束)'"`
TradeNo string `gorm:"type:varchar(100) comment '交易号'"` // 交易号就是支付宝的订单号,查账
OrderMount float32
PayTime *time.Time `gorm:"type:datetime"`
Address string `gorm:"type:varchar(100)"`
SignerName string `gorm:"type:varchar(20)"`
SingerMobile string `gorm:"type:varchar(11)"`
Post string `gorm:"type:varchar(20)"`
}
func (OrderInfo) TableName() string {
return "orderinfo"
}
type OrderGoods struct {
BaseModel
Order int32 `gorm:"type:int;index"`
Goods int32 `gorm:"type:int;index"`
//把商品的信息保存下来了 , 字段冗余, 高并发系统中我们一般都不会遵循三范式, 做镜像记录
GoodsName string `gorm:"type:varchar(100);index"`
GoodsImage string `gorm:"type:varchar(200)"`
GoodsPrice float32
Nums int32 `gorm:"type:int"`
}
func (OrderGoods) TableName() string {
return "ordergoods"
}
package main
import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"gorm.io/gorm/schema"
"log"
"nd/order_srv/global"
"nd/order_srv/initialize"
"nd/order_srv/model"
"os"
"time"
)
func main() {
initialize.InitConfig()
dsn := fmt.Sprintf("root:jiushi@tcp(%s:3306)/mxshop_order_srv?charset=utf8mb4&parseTime=True&loc=Local", global.ServerConfig.MysqlInfo.Host)
newLogger := logger.New(
log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer
logger.Config{
SlowThreshold: time.Second, // 慢 SQL 阈值
LogLevel: logger.Info, // Log level
Colorful: true, // 禁用彩色打印
},
)
// 全局模式
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
NamingStrategy: schema.NamingStrategy{
SingularTable: true,
},
Logger: newLogger,
})
if err != nil {
panic(err)
}
_ = db.AutoMigrate(&model.ShoppingCart{}, &model.OrderInfo{}, &model.OrderGoods{})
}
protoc --go_out=. --go_opt=paths=import --go-grpc_out=. --go-grpc_opt=paths=import *.proto
syntax = "proto3";
import "google/protobuf/empty.proto";
option go_package = ".;proto";
service Order {
//购物车
rpc CartItemList(UserInfo) returns(CartItemListResponse); //获取用户的购物车信息
rpc CreateCartItem(CartItemRequest) returns(ShopCartInfoResponse); //添加商品到购物车
rpc UpdateCartItem(CartItemRequest) returns(google.protobuf.Empty); //修改购物车信息
rpc DeleteCartItem(CartItemRequest) returns(google.protobuf.Empty); //删除购物车条目
//订单
rpc CreateOrder(OrderRequest) returns (OrderInfoResponse); //创建订单
rpc OrderList(OrderFilterRequest) returns (OrderListResponse); // 订单列表
rpc OrderDetail(OrderRequest) returns (OrderInfoDetailResponse); // 订单详情
rpc UpdateOrderStatus(OrderStatus) returns (google.protobuf.Empty); // 修改订单状态
}
message UserInfo {
int32 id = 1;
}
message OrderStatus {
int32 id = 1;
string orderSn = 2;
string status = 3;
}
message CartItemRequest {
int32 id = 1;
int32 userId = 2;
int32 goodsId = 3;
string goodsName = 4;
string goodsImage = 5;
float goodsPrice = 6;
int32 nums = 7;
bool checked = 8;
}
message OrderRequest {
int32 id = 1;
int32 userId = 2;
string address = 3;
string name = 4;
string mobile = 5;
string post = 6;
}
message OrderInfoResponse {
int32 id = 1;
int32 userId = 2;
string orderSn = 3;
string payType = 4;
string status = 5;
string post = 6;
float total = 7;
string address = 8;
string name = 9;
string mobile = 10;
string addTime = 11;
}
message ShopCartInfoResponse {
int32 id = 1;
int32 userId = 2;
int32 goodsId = 3;
int32 nums = 4;
bool checked = 5;
}
message OrderItemResponse {
int32 id = 1;
int32 orderId = 2;
int32 goodsId = 3;
string goodsName = 4;
string goodsImage = 5;
float goodsPrice = 6;
int32 nums = 7;
}
message OrderInfoDetailResponse {
OrderInfoResponse orderInfo = 1;
repeated OrderItemResponse goods = 2;
}
message OrderFilterRequest {
int32 userId = 1;
int32 pages = 2;
int32 pagePerNums = 3;
}
message OrderListResponse {
int32 total = 1;
repeated OrderInfoResponse data = 2;
}
message CartItemListResponse {
int32 total = 1;
repeated ShopCartInfoResponse data = 2;
}
{
"name": "order_srv",
"host": "192.168.124.9",
"tags": ["imooc", "bobby", "order", "srv"],
"mysql": {
"host": "192.168.124.51",
"port": 3306,
"user": "root",
"password": "jiushi",
"db": "mxshop_order_srv"
},
"consul": {
"host": "192.168.124.51",
"port": 8500
}
}
host: '192.168.78.131'
port: 8848
namespace: '4fd0ed12-957c-490e-a116-99672c761433'
user: 'nacos'
password: 'nacos'
dataid: 'order_srv.json'
group: 'comp'
proto.RegisterOrderServer(server, &proto.UnimplementedOrderServer{})
package main
import (
"flag"
"fmt"
"net"
"os"
"os/signal"
"syscall"
"github.com/satori/go.uuid"
"go.uber.org/zap"
"google.golang.org/grpc"
"google.golang.org/grpc/health"
"google.golang.org/grpc/health/grpc_health_v1"
"nd/order_srv/global"
"nd/order_srv/initialize"
"nd/order_srv/proto"
"nd/order_srv/utils"
"nd/order_srv/utils/register/consul"
)
func main() {
IP := flag.String("ip", "0.0.0.0", "ip地址")
Port := flag.Int("port", 50060, "端口号") // 这个修改为0,如果我们从命令行带参数启动的话就不会为0
//初始化
initialize.InitLogger()
initialize.InitConfig()
initialize.InitDB()
zap.S().Info(global.ServerConfig)
flag.Parse()
zap.S().Info("ip: ", *IP)
if *Port == 0 {
*Port, _ = utils.GetFreePort()
}
zap.S().Info("port: ", *Port)
server := grpc.NewServer()
proto.RegisterOrderServer(server, &proto.UnimplementedOrderServer{})
lis, err := net.Listen("tcp", fmt.Sprintf("%s:%d", *IP, *Port))
if err != nil {
panic("failed to listen:" + err.Error())
}
//注册服务健康检查
grpc_health_v1.RegisterHealthServer(server, health.NewServer())
//服务注册
register_client := consul.NewRegistryClient(global.ServerConfig.ConsulInfo.Host, global.ServerConfig.ConsulInfo.Port)
serviceId := fmt.Sprintf("%s", uuid.NewV4())
err = register_client.Register(global.ServerConfig.Host, *Port, global.ServerConfig.Name, global.ServerConfig.Tags, serviceId)
if err != nil {
zap.S().Panic("服务注册失败:", err.Error())
}
zap.S().Debugf("启动服务器, 端口: %d", *Port)
go func() {
err = server.Serve(lis)
if err != nil {
panic("failed to start grpc:" + err.Error())
}
}()
//接收终止信号
quit := make(chan os.Signal)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
if err = register_client.DeRegister(serviceId); err != nil {
zap.S().Info("注销失败:", err.Error())
} else {
zap.S().Info("注销成功:")
}
}
func (*OrderServer) CartItemList(ctx context.Context, req *proto.UserInfo) (*proto.CartItemListResponse, error) {
//获取用户的购物车列表
var shopCarts []model.ShoppingCart
var rsp proto.CartItemListResponse
if result := global.DB.Where(&model.ShoppingCart{User: req.Id}).Find(&shopCarts); result.Error != nil {
return nil, result.Error
} else {
rsp.Total = int32(result.RowsAffected)
}
for _, shopCart := range shopCarts {
rsp.Data = append(rsp.Data, &proto.ShopCartInfoResponse{
Id: shopCart.ID,
UserId: shopCart.User,
GoodsId: shopCart.Goods,
Nums: shopCart.Nums,
Checked: shopCart.Checked,
})
}
return &rsp, nil
}
func (*OrderServer) CreateCartItem(ctx context.Context, req *proto.CartItemRequest) (*proto.ShopCartInfoResponse, error) {
//将商品添加到购物车 1. 购物车中原本没有这件商品 - 新建一个记录 2. 这个商品之前添加到了购物车- 合并
var shopCart model.ShoppingCart
if result := global.DB.Where(&model.ShoppingCart{Goods: req.GoodsId, User: req.UserId}).First(&shopCart); result.RowsAffected == 1 {
//如果记录已经存在,则合并购物车记录, 更新操作
shopCart.Nums += req.Nums
} else {
//插入操作
shopCart.User = req.UserId
shopCart.Goods = req.GoodsId
shopCart.Nums = req.Nums
shopCart.Checked = false
}
global.DB.Save(&shopCart)
return &proto.ShopCartInfoResponse{Id: shopCart.ID}, nil
}
func (*OrderServer) UpdateCartItem(ctx context.Context, req *proto.CartItemRequest) (*emptypb.Empty, error) {
//更新购物车记录,更新数量和选中状态
var shopCart model.ShoppingCart
if result := global.DB.First(&shopCart, req.Id); result.RowsAffected == 0 {
return nil, status.Errorf(codes.NotFound, "购物车记录不存在")
}
shopCart.Checked = req.Checked
if req.Nums > 0 {
shopCart.Nums = req.Nums
}
global.DB.Save(&shopCart)
return &emptypb.Empty{}, nil
}
func (*OrderServer) DeleteCartItem(ctx context.Context, req *proto.CartItemRequest) (*emptypb.Empty, error) {
if result := global.DB.Where("goods=? and user=?", req.GoodsId, req.UserId).Delete(&model.ShoppingCart{}); result.RowsAffected == 0 {
return nil, status.Errorf(codes.NotFound, "购物车记录不存在")
}
return &emptypb.Empty{}, nil
}
func (*OrderServer) OrderList(ctx context.Context, req *proto.OrderFilterRequest) (*proto.OrderListResponse, error) {
var orders []model.OrderInfo
var rsp proto.OrderListResponse
var total int64
global.DB.Where(&model.OrderInfo{User: req.UserId}).Count(&total)
rsp.Total = int32(total)
//分页
global.DB.Scopes(Paginate(int(req.Pages), int(req.PagePerNums))).Where(&model.OrderInfo{User: req.UserId}).Find(&orders)
for _, order := range orders {
rsp.Data = append(rsp.Data, &proto.OrderInfoResponse{
Id: order.ID,
UserId: order.User,
OrderSn: order.OrderSn,
PayType: order.PayType,
Status: order.Status,
Post: order.Post,
Total: order.OrderMount,
Address: order.Address,
Name: order.SignerName,
Mobile: order.SingerMobile,
AddTime: order.CreatedAt.Format("2006-01-02 15:04:05"),
})
}
return &rsp, nil
}
func (*OrderServer) OrderDetail(ctx context.Context, req *proto.OrderRequest) (*proto.OrderInfoDetailResponse, error) {
var order model.OrderInfo
var rsp proto.OrderInfoDetailResponse
//这个订单的id是否是当前用户的订单, 如果在web层用户传递过来一个id的订单, web层应该先查询一下订单id是否是当前用户的
//在个人中心可以这样做,但是如果是后台管理系统,web层如果是后台管理系统 那么只传递order的id,如果是电商系统还需要一个用户的id
if result := global.DB.Where(&model.OrderInfo{BaseModel: model.BaseModel{ID: req.Id}, User: req.UserId}).First(&order); result.RowsAffected == 0 {
return nil, status.Errorf(codes.NotFound, "订单不存在")
}
orderInfo := proto.OrderInfoResponse{}
orderInfo.Id = order.ID
orderInfo.UserId = order.User
orderInfo.OrderSn = order.OrderSn
orderInfo.PayType = order.PayType
orderInfo.Status = order.Status
orderInfo.Post = order.Post
orderInfo.Total = order.OrderMount
orderInfo.Address = order.Address
orderInfo.Name = order.SignerName
orderInfo.Mobile = order.SingerMobile
rsp.OrderInfo = &orderInfo
var orderGoods []model.OrderGoods
if result := global.DB.Where(&model.OrderGoods{Order: order.ID}).Find(&orderGoods); result.Error != nil {
return nil, result.Error
}
for _, orderGood := range orderGoods {
rsp.Goods = append(rsp.Goods, &proto.OrderItemResponse{
GoodsId: orderGood.Goods,
GoodsName: orderGood.GoodsName,
GoodsPrice: orderGood.GoodsPrice,
GoodsImage: orderGood.GoodsImage,
Nums: orderGood.Nums,
})
}
return &rsp, nil
}
package global
import (
"gorm.io/gorm"
"nd/order_srv/config"
"nd/order_srv/proto"
)
var (
DB *gorm.DB
ServerConfig config.ServerConfig
NacosConfig config.NacosConfig
GoodsSrvClient proto.GoodsClient
InventorySrvClient proto.InventoryClient
)
package config
type MysqlConfig struct {
Host string `mapstructure:"host" json:"host"`
Port int `mapstructure:"port" json:"port"`
Name string `mapstructure:"db" json:"db"`
User string `mapstructure:"user" json:"user"`
Password string `mapstructure:"password" json:"password"`
}
type ConsulConfig struct {
Host string `mapstructure:"host" json:"host"`
Port int `mapstructure:"port" json:"port"`
}
type SrvConfig struct {
Name string `mapstructure:"name" json:"name"`
}
type ServerConfig struct {
Name string `mapstructure:"name" json:"name"`
Host string `mapstructure:"host" json:"host"`
Tags []string `mapstructure:"tags" json:"tags"`
MysqlInfo MysqlConfig `mapstructure:"mysql" json:"mysql"`
ConsulInfo ConsulConfig `mapstructure:"consul" json:"consul"`
//商品微服务的配置
GoodsSrvInfo SrvConfig `mapstructure:"goods_srv" json:"goods_srv"`
//库存微服务的配置
InventorySrvInfo SrvConfig `mapstructure:"inventory_srv" json:"inventory_srv"`
}
type NacosConfig struct {
Host string `mapstructure:"host"`
Port uint64 `mapstructure:"port"`
Namespace string `mapstructure:"namespace"`
User string `mapstructure:"user"`
Password string `mapstructure:"password"`
DataId string `mapstructure:"dataid"`
Group string `mapstructure:"group"`
}
{
"name": "order_srv",
"host": "192.168.124.9",
"tags": ["imooc", "bobby", "order", "srv"],
"mysql": {
"host": "192.168.124.51",
"port": 3306,
"user": "root",
"password": "jiushi",
"db": "mxshop_order_srv"
},
"goods_srv":{
"name":"goods_srv"
},
"inventory_srv":{
"name":"inventory_srv"
},
"consul": {
"host": "192.168.124.51",
"port": 8500
}
}
package initialize
import (
"fmt"
"google.golang.org/grpc/credentials/insecure"
_ "github.com/mbobakov/grpc-consul-resolver" // It's important
"go.uber.org/zap"
"google.golang.org/grpc"
"nd/order_srv/global"
"nd/order_srv/proto"
)
func InitSrvConn() {
consulInfo := global.ServerConfig.ConsulInfo
goodsConn, err := grpc.Dial(
fmt.Sprintf("consul://%s:%d/%s?wait=14s", consulInfo.Host, consulInfo.Port, global.ServerConfig.GoodsSrvInfo.Name),
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithDefaultServiceConfig(`{"loadBalancingPolicy": "round_robin"}`),
//grpc.WithUnaryInterceptor(otgrpc.OpenTracingClientInterceptor(opentracing.GlobalTracer())),
)
if err != nil {
zap.S().Fatal("[InitSrvConn] 连接 【商品服务失败】")
}
global.GoodsSrvClient = proto.NewGoodsClient(goodsConn)
//初始化库存服务连接
invConn, err := grpc.Dial(
fmt.Sprintf("consul://%s:%d/%s?wait=14s", consulInfo.Host, consulInfo.Port, global.ServerConfig.InventorySrvInfo.Name),
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithDefaultServiceConfig(`{"loadBalancingPolicy": "round_robin"}`),
//grpc.WithUnaryInterceptor(otgrpc.OpenTracingClientInterceptor(opentracing.GlobalTracer())),
)
if err != nil {
zap.S().Fatal("[InitSrvConn] 连接 【库存服务失败】")
}
global.InventorySrvClient = proto.NewInventoryClient(invConn)
}
func main() {
IP := flag.String("ip", "0.0.0.0", "ip地址")
Port := flag.Int("port", 50060, "端口号") // 这个修改为0,如果我们从命令行带参数启动的话就不会为0
//初始化
initialize.InitLogger()
initialize.InitConfig()
initialize.InitDB()
initialize.InitSrvConn()
zap.S().Info(global.ServerConfig)
//省略。。。
因为涉及到了库存的扣减,需要使用分布式的事务;
我们这里先实现本地mysql事务,后续再优化成分布式的事务
func (*OrderServer) CreateOrder(ctx context.Context, req *proto.OrderRequest) (*proto.OrderInfoResponse, error) {
/*
新建订单
1. 从购物车中获取到选中的商品
2. 商品的价格自己查询 - 访问商品服务 (跨微服务)
3. 库存的扣减 - 访问库存服务 (跨微服务)
4. 订单的基本信息表 - 订单的商品信息表
5. 从购物车中删除已购买的记录
*/
var goodsIds []int32
var shopCarts []model.ShoppingCart
goodsNumsMap := make(map[int32]int32)
if result := global.DB.Where(&model.ShoppingCart{User: req.UserId, Checked: true}).Find(&shopCarts); result.RowsAffected == 0 {
return nil, status.Errorf(codes.InvalidArgument, "没有选中结算的商品")
}
for _, shopCarts := range shopCarts {
goodsIds = append(goodsIds, shopCarts.Goods)
goodsNumsMap[shopCarts.Goods] = shopCarts.Nums
}
//跨服务调用 - 商品微服务 —— 批量查询商品与价格
goods, err := global.GoodsSrvClient.BatchGetGoods(context.Background(), &proto.BatchGoodsIdInfo{Id: goodsIds})
if err != nil {
return nil, status.Errorf(codes.Internal, "批量查询商品信息失败")
}
var orderAmount float32
var orderGoods []*model.OrderGoods
var goodsInvInfo []*proto.GoodsInvInfo
for _, good := range goods.Data {
orderAmount += good.ShopPrice * float32(goodsNumsMap[good.Id])
orderGoods = append(orderGoods, &model.OrderGoods{
Goods: good.Id,
GoodsName: good.Name,
GoodsImage: good.GoodsFrontImage,
GoodsPrice: good.ShopPrice,
Nums: goodsNumsMap[good.Id],
})
goodsInvInfo = append(goodsInvInfo, &proto.GoodsInvInfo{
GoodsId: good.Id,
Num: goodsNumsMap[good.Id],
})
}
//跨服务调用 - 库存微服务 —— 扣减库存
if _, err = global.InventorySrvClient.Sell(context.Background(), &proto.SellInfo{GoodsInfo: goodsInvInfo}); err != nil {
return nil, status.Errorf(codes.ResourceExhausted, "扣减库存失败")
}
tx := global.DB.Begin()
//生成订单表
order := model.OrderInfo{
OrderSn: GenerateOrderSn(req.UserId),
OrderMount: orderAmount,
Address: req.Address,
SignerName: req.Name,
SingerMobile: req.Mobile,
Post: req.Post,
User: req.UserId,
}
if result := tx.Save(&order); result.RowsAffected == 0 {
tx.Rollback()
return nil, status.Errorf(codes.Internal, "创建订单失败")
}
for _, orderGood := range orderGoods {
orderGood.Order = order.ID
}
// 批量插入orderGoods
if result := tx.CreateInBatches(orderGoods, 100); result.RowsAffected == 0 {
tx.Rollback()
return nil, status.Errorf(codes.Internal, "创建订单失败")
}
// 删除购物车的记录
if result := tx.Where(&model.ShoppingCart{User: req.UserId, Checked: true}).Delete(&model.ShoppingCart{}); result.RowsAffected == 0 {
tx.Rollback()
return nil, status.Errorf(codes.Internal, "创建订单失败")
}
//提交事务
tx.Commit()
return &proto.OrderInfoResponse{Id: order.ID, OrderSn: order.OrderSn, Total: order.OrderMount}, nil
}
func (*OrderServer) UpdateOrderStatus(ctx context.Context, req *proto.OrderStatus) (*emptypb.Empty, error) {
//先查询,再更新 实际上有两条sql执行, select 和 update语句
if result := global.DB.Model(&model.OrderInfo{}).Where("order_sn = ?", req.OrderSn).Update("status", req.Status); result.RowsAffected == 0 {
return nil, status.Errorf(codes.NotFound, "订单不存在")
}
return &emptypb.Empty{}, nil
}
测试接口前,要注意 order_srv/main.go 中
proto.RegisterOrderServer(server, &handler.OrderServer{})
,这个注册的实现
否则会一直报空实现错误
package tests
import (
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"nd/order_srv/proto"
)
var (
TargetAddr = "127.0.0.1:50060"
OrderClient proto.OrderClient
Conn *grpc.ClientConn
)
func Init() {
var err error
Conn, err = grpc.Dial(TargetAddr, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
panic(err)
}
OrderClient = proto.NewOrderClient(Conn)
}
package main
import (
"context"
"fmt"
"nd/order_srv/proto"
"nd/order_srv/tests"
)
func TestCartItemList(userId int32) {
rsp, err := tests.OrderClient.CartItemList(context.Background(), &proto.UserInfo{
Id: userId,
})
if err != nil {
panic(err)
}
for _, item := range rsp.Data {
fmt.Println(item.Id, item.GoodsId, item.Nums)
}
}
func TestCreateCartItem(userId, nums, goodsId int32) {
rsp, err := tests.OrderClient.CreateCartItem(context.Background(), &proto.CartItemRequest{
UserId: userId,
Nums: nums,
GoodsId: goodsId,
})
if err != nil {
panic(err)
}
fmt.Println(rsp.Id)
}
func TestUpdateCartItem(id int32) {
_, err := tests.OrderClient.UpdateCartItem(context.Background(), &proto.CartItemRequest{
Id: id,
Checked: true,
})
if err != nil {
panic(err)
}
}
func main() {
tests.Init()
TestCreateCartItem(1, 1, 26)
TestCreateCartItem(1, 1, 27)
TestCartItemList(1)
TestUpdateCartItem(1)
tests.Conn.Close()
}
package main
import (
"context"
"nd/order_srv/proto"
"nd/order_srv/tests"
)
func TestCreateOrder() {
_, err := tests.OrderClient.CreateOrder(context.Background(), &proto.OrderRequest{
UserId: 1,
Address: "北京市",
Name: "bobby",
Mobile: "18787878787",
Post: "请尽快发货",
})
if err != nil {
panic(err)
}
}
func main() {
tests.Init()
TestCreateOrder()
tests.Conn.Close()
}
func TestGetOrderDetail(orderId int32) {
rsp, err := tests.OrderClient.OrderDetail(context.Background(), &proto.OrderRequest{
Id: orderId,
})
if err != nil {
panic(err)
}
fmt.Println(rsp.OrderInfo.OrderSn)
for _, good := range rsp.Goods {
fmt.Println(good.GoodsName)
}
}
func TestOrderList() {
rsp, err := tests.OrderClient.OrderList(context.Background(), &proto.OrderFilterRequest{})
//rsp, err := tests.OrderClient.OrderList(context.Background(), &proto.OrderFilterRequest{
// UserId: 1,
//})
if err != nil {
panic(err)
}
for _, order := range rsp.Data {
fmt.Println(order.OrderSn)
}
}