八.订单微服务API(order-web)
1.项目初始化
1.1初始化目录

1.2添加yaml配置文件
order-web/order-web.yaml
host: '192.168.0.4'
port: 8848
namespace: 'ba14206d-403f-4377-b7e7-70a3e8fa2bbf'
user: 'nacos'
password: 'nacos'
dataid: 'order-web.json'
group: 'dev'
1.3建立全局变量
order-web/global/global.go
package global
import (
ut "github.com/go-playground/universal-translator"
goodsProto "mxshop_api/goods-web/proto"
"mxshop_api/order-web/config"
"mxshop_api/order-web/proto"
)
var (
Trans ut.Translator
ServerConfig *config.ServerConfig = &config.ServerConfig{}
NacosConfig *config.NacosConfig = &config.NacosConfig{}
GoodsSrvClient goodsProto.GoodsClient
OrderSrvClient proto.OrderClient
InventorySrvClient proto.InventoryClient
)
1.4在nacos建立配置文件

1.5建立配置文件的映射文件
order-web/config/config.go
package config
type GoodsSrvConfig struct {
Name string `mapstructure:"name" json:"name"`
}
type JWTConfig struct {
SigningKey string `mapstructure:"key" json:"key"`
}
type ConsulConfig struct {
Host string `mapstructure:"host" json:"host"`
Port int `mapstructure:"port" json:"port"`
}
type AlipayConfig struct {
AppID string `mapstructure:"app_id" json:"app_id"`
PrivateKey string `mapstructure:"private_key" json:"private_key"`
AliPublicKey string `mapstructure:"ali_public_key" json:"ali_public_key"`
NotifyURL string `mapstructure:"notify_url" json:"notify_url"`
ReturnURL string `mapstructure:"return_url" json:"return_url"`
}
type RedisConfig struct {
Host string `mapstructure:"host" json:"host"`
Port int `mapstructure:"port" json:"port"`
Expire int `mapstructure:"expire" json:"expire"`
}
type ServerConfig struct {
Name string `mapstructure:"name" json:"name"`
Host string `mapstructure:"host" json:"host"`
Tags []string `mapstructure:"tags" json:"tags"`
Port int `mapstructure:"port" json:"port"`
GoodsSrvInfo GoodsSrvConfig `mapstructure:"goods_srv" json:"goods_srv"`
OrderSrvInfo GoodsSrvConfig `mapstructure:"order_srv" json:"order_srv"`
InventorySrvInfo GoodsSrvConfig `mapstructure:"inventory_srv" json:"inventory_srv"`
JWTInfo JWTConfig `mapstructure:"jwt" json:"jwt"`
RedisInfo RedisConfig `mapstructure:"redis" json:"redis"`
ConsulInfo ConsulConfig `mapstructure:"consul" json:"consul"`
AliPayInfo AlipayConfig `mapstructure:"alipay" json:"alipay"`
}
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"`
}
1.6配 置文件初始化
order-web/initalize/config.go
package initalize
import (
"encoding/json"
"fmt"
"github.com/nacos-group/nacos-sdk-go/clients"
"github.com/nacos-group/nacos-sdk-go/common/constant"
"github.com/nacos-group/nacos-sdk-go/vo"
"github.com/spf13/viper"
"go.uber.org/zap"
"mxshop_api/order-web/global"
)
func GetEnvInfo(env string) bool {
viper.AutomaticEnv()
return viper.GetBool(env)
}
func InitConfig() {
debug := GetEnvInfo("MXSHOP_DEBUG")
configFilePrefix := "config"
configFileName := fmt.Sprintf("order-web/%s-pro.yaml", configFilePrefix)
if debug {
configFileName = fmt.Sprintf("order-web/%s-debug.yaml", configFilePrefix)
}
v := viper.New()
v.SetConfigFile(configFileName)
if err := v.ReadInConfig(); err != nil {
panic(err)
}
if err := v.Unmarshal(global.NacosConfig); err != nil {
panic(err)
}
zap.S().Infof("配置信息: &v", global.NacosConfig)
//viper功能,动态监控变化
//v.WatchConfig()
//v.OnConfigChange(func(e fsnotify.Event) {
// zap.S().Infof("配置信息产生变化:%v", e.Name)
// _ = v.ReadInConfig()
// _ = v.Unmarshal(global.ServerConfig)
// fmt.Println(global.ServerConfig)
//})
//从nacos读取信息
sc := []constant.ServerConfig{
{
IpAddr: global.NacosConfig.Host,
Port: global.NacosConfig.Port,
},
}
cc := constant.ClientConfig{
NamespaceId: global.NacosConfig.Namespace, // 如果需要支持多namespace,我们可以场景多个client,它们有不同的NamespaceId
TimeoutMs: 5000,
NotLoadCacheAtStart: true,
LogDir: "tmp/nacos/log",
CacheDir: "tmp/nacos/cache",
LogLevel: "debug",
}
configClient, err := clients.CreateConfigClient(map[string]interface{}{
"serverConfigs": sc,
"clientConfig": cc,
})
if err != nil {
panic(err)
}
content, err := configClient.GetConfig(vo.ConfigParam{
DataId: global.NacosConfig.DataId,
Group: global.NacosConfig.Group})
if err != nil {
panic(err)
}
fmt.Println(content) //字符串 - yaml
//想要将一个json字符串转换成struct,需要去设置这个struct的tag
err = json.Unmarshal([]byte(content), &global.ServerConfig)
if err != nil {
zap.S().Fatalf("读取nacos配置失败: %s", err.Error())
}
fmt.Println(&global.ServerConfig)
}
order-web/initalize/srv_conn.go
package initalize
import (
"fmt"
_ "github.com/mbobakov/grpc-consul-resolver"
"go.uber.org/zap"
"google.golang.org/grpc"
goodsProto "mxshop_api/goods-web/proto"
"mxshop_api/order-web/global"
"mxshop_api/order-web/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.WithInsecure(),
grpc.WithDefaultServiceConfig(`{"loadBalancingPolicy": "round_robin"}`),
)
if err != nil {
zap.S().Fatal("[InitSrvConn] 连接 【商品服务失败】")
}
global.GoodsSrvClient = goodsProto.NewGoodsClient(goodsConn)
orderConn, err := grpc.Dial(
fmt.Sprintf("consul://%s:%d/%s?wait=14s", consulInfo.Host, consulInfo.Port, global.ServerConfig.OrderSrvInfo.Name),
grpc.WithInsecure(),
grpc.WithDefaultServiceConfig(`{"loadBalancingPolicy": "round_robin"}`),
)
fmt.Println("err", err)
if err != nil {
zap.S().Fatal("[InitSrvConn] 连接 【订单服务失败】")
}
global.OrderSrvClient = proto.NewOrderClient(orderConn)
invConn, err := grpc.Dial(
fmt.Sprintf("consul://%s:%d/%s?wait=14s", consulInfo.Host, consulInfo.Port, global.ServerConfig.InventorySrvInfo.Name),
grpc.WithInsecure(),
grpc.WithDefaultServiceConfig(`{"loadBalancingPolicy": "round_robin"}`),
)
if err != nil {
zap.S().Fatal("[InitSrvConn] 连接 【库存服务失败】")
}
global.InventorySrvClient = proto.NewInventoryClient(invConn)
}
order-web/initalize/logger.go
package initalize
import "go.uber.org/zap"
func InitLogger() {
logger, _ := zap.NewDevelopment()
zap.ReplaceGlobals(logger)
}
order-web/initalize/router.go
package initalize
import (
"github.com/gin-gonic/gin"
"mxshop_api/order-web/middlewares"
"mxshop_api/order-web/router"
"net/http"
)
func Routers() *gin.Engine {
Router := gin.Default()
Router.GET("/health", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"code": http.StatusOK,
"success": true,
})
})
//配置跨域
Router.Use(middlewares.Cors())
ApiGroup := Router.Group("/o/v1")
router.InitOrderRouter(ApiGroup)
router.InitShopCartRouter(ApiGroup)
return Router
}
order-web/initalize/validator.go
package initalize
import (
"fmt"
"mxshop_api/order-web/global"
"reflect"
"strings"
"github.com/gin-gonic/gin/binding"
"github.com/go-playground/locales/en"
"github.com/go-playground/locales/zh"
ut "github.com/go-playground/universal-translator"
"github.com/go-playground/validator/v10"
en_translations "github.com/go-playground/validator/v10/translations/en"
zh_translations "github.com/go-playground/validator/v10/translations/zh"
)
func InitTrans(locale string) (err error) {
//修改gin框架中的validator引擎属性, 实现定制
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
//注册一个获取json的tag的自定义方法
v.RegisterTagNameFunc(func(fld reflect.StructField) string {
name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
if name == "-" {
return ""
}
return name
})
zhT := zh.New() //中文翻译器
enT := en.New() //英文翻译器
//第一个参数是备用的语言环境,后面的参数是应该支持的语言环境
uni := ut.New(enT, zhT, enT)
global.Trans, ok = uni.GetTranslator(locale)
if !ok {
return fmt.Errorf("uni.GetTranslator(%s)", locale)
}
switch locale {
case "en":
en_translations.RegisterDefaultTranslations(v, global.Trans)
case "zh":
zh_translations.RegisterDefaultTranslations(v, global.Trans)
default:
en_translations.RegisterDefaultTranslations(v, global.Trans)
}
return
}
return
}
1.7main.go文件
package main
import (
"fmt"
_ "github.com/gin-gonic/gin/binding"
_ "github.com/go-playground/universal-translator"
uuid "github.com/satori/go.uuid"
"github.com/spf13/viper"
"go.uber.org/zap"
"mxshop_api/order-web/global"
"mxshop_api/order-web/initalize"
"mxshop_api/order-web/utils"
"mxshop_api/order-web/utils/registry/consul"
_ "mxshop_api/order-web/validator"
"os"
"os/signal"
"syscall"
)
func main() {
//1.初始化logger
initalize.InitLogger()
//2. 初始化配置文件
initalize.InitConfig()
//3.初始化routers
Router := initalize.Routers()
zap.S().Debugf("启动服务器,端口:%d", global.ServerConfig.Port)
//4. 初始化翻译
if err := initalize.InitTrans("zh"); err != nil {
panic(err)
}
//5.初始化SRV的连接
initalize.InitSrvConn()
viper.AutomaticEnv()
debug := viper.GetBool("MXSHOP_DEBUG")
if !debug {
port, err := utils.GetFreePort()
if err == nil {
global.ServerConfig.Port = port
}
}
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, global.ServerConfig.Port, global.ServerConfig.Name, global.ServerConfig.Tags, serviceId)
if err != nil {
zap.S().Panic("服务注册失败:", err.Error())
}
zap.S().Debugf("启动服务器, 端口: %d", global.ServerConfig.Port)
go func() {
if err := Router.Run(fmt.Sprintf(":%d", global.ServerConfig.Port)); err != nil {
zap.S().Panic("启动失败:", 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("注销成功:")
}
}
2.API接口开发
2.1订单列表开发
func List(ctx *gin.Context) {
//订单的列表
userId, _ := ctx.Get("userId")
claims, _ := ctx.Get("claims")
request := proto.OrderFilterRequest{}
//如果是管理员用户则返回所有的订单
model := claims.(*models.CustomClaims)
if model.AuthorityId == 1 {
request.UserId = int32(userId.(uint))
}
pages := ctx.DefaultQuery("p", "0")
pagesInt, _ := strconv.Atoi(pages)
request.Pages = int32(pagesInt)
perNums := ctx.DefaultQuery("pnum", "0")
perNumsInt, _ := strconv.Atoi(perNums)
request.PagePerNums = int32(perNumsInt)
request.Pages = int32(pagesInt)
request.PagePerNums = int32(perNumsInt)
rsp, err := global.OrderSrvClient.OrderList(context.Background(), &request)
if err != nil {
zap.S().Errorw("获取订单列表失败")
api.HandleGrpcErrorToHttp(err, ctx)
return
}
reMap := gin.H{
"total": rsp.Total,
}
orderList := make([]interface{}, 0)
for _, item := range rsp.Data {
tmpMap := map[string]interface{}{}
tmpMap["id"] = item.Id
tmpMap["status"] = item.Status
tmpMap["pay_type"] = item.PayType
tmpMap["user"] = item.UserId
tmpMap["post"] = item.Post
tmpMap["total"] = item.Total
tmpMap["address"] = item.Address
tmpMap["name"] = item.Name
tmpMap["mobile"] = item.Mobile
tmpMap["order_sn"] = item.OrderSn
tmpMap["id"] = item.Id
tmpMap["add_time"] = item.AddTime
orderList = append(orderList, tmpMap)
}
reMap["data"] = orderList
ctx.JSON(http.StatusOK, reMap)
}
2.2创建订单
func New(ctx *gin.Context) {
orderForm := forms.CreateOrderForm{}
if err := ctx.ShouldBindJSON(&orderForm); err != nil {
api.HandleValidatorError(ctx, err)
}
userId, _ := ctx.Get("userId")
rsp, err := global.OrderSrvClient.CreateOrder(context.WithValue(context.Background(), "ginContext", ctx), &proto.OrderRequest{
UserId: int32(userId.(uint)),
Name: orderForm.Name,
Mobile: orderForm.Mobile,
Address: orderForm.Address,
Post: orderForm.Post,
})
if err != nil {
zap.S().Errorw("新建订单失败")
api.HandleGrpcErrorToHttp(err, ctx)
return
}
//生成支付宝的支付url
client, err := alipay.New(global.ServerConfig.AliPayInfo.AppID, global.ServerConfig.AliPayInfo.PrivateKey, false)
if err != nil {
zap.S().Errorw("实例化支付宝失败")
ctx.JSON(http.StatusInternalServerError, gin.H{
"msg": err.Error(),
})
return
}
err = client.LoadAliPayPublicKey((global.ServerConfig.AliPayInfo.AliPublicKey))
if err != nil {
zap.S().Errorw("加载支付宝的公钥失败")
ctx.JSON(http.StatusInternalServerError, gin.H{
"msg": err.Error(),
})
return
}
var p = alipay.TradePagePay{}
p.NotifyURL = global.ServerConfig.AliPayInfo.NotifyURL
p.ReturnURL = global.ServerConfig.AliPayInfo.ReturnURL
p.Subject = "慕学生鲜订单-" + rsp.OrderSn
p.OutTradeNo = rsp.OrderSn
p.TotalAmount = strconv.FormatFloat(float64(rsp.Total), 'f', 2, 64)
p.ProductCode = "FAST_INSTANT_TRADE_PAY"
url, err := client.TradePagePay(p)
if err != nil {
zap.S().Errorw("生成支付url失败")
ctx.JSON(http.StatusInternalServerError, gin.H{
"msg": err.Error(),
})
return
}
ctx.JSON(http.StatusOK, gin.H{
"id": rsp.Id,
"alipay_url": url.String(),
})
}
2.3订单详情
func Detail(ctx *gin.Context) {
id := ctx.Param("id")
userId, _ := ctx.Get("userId")
i, err := strconv.Atoi(id)
if err != nil {
ctx.JSON(http.StatusNotFound, gin.H{
"msg": "url格式出错",
})
return
}
//如果是管理员用户则返回所有的订单
request := proto.OrderRequest{
Id: int32(i),
}
claims, _ := ctx.Get("claims")
model := claims.(*models.CustomClaims)
if model.AuthorityId == 1 {
request.UserId = int32(userId.(uint))
}
rsp, err := global.OrderSrvClient.OrderDetail(context.Background(), &request)
if err != nil {
zap.S().Errorw("获取订单详情失败")
api.HandleGrpcErrorToHttp(err, ctx)
return
}
reMap := gin.H{}
reMap["id"] = rsp.OrderInfo.Id
reMap["status"] = rsp.OrderInfo.Status
reMap["user"] = rsp.OrderInfo.UserId
reMap["post"] = rsp.OrderInfo.Post
reMap["total"] = rsp.OrderInfo.Total
reMap["address"] = rsp.OrderInfo.Address
reMap["name"] = rsp.OrderInfo.Name
reMap["mobile"] = rsp.OrderInfo.Mobile
reMap["pay_type"] = rsp.OrderInfo.PayType
reMap["order_sn"] = rsp.OrderInfo.OrderSn
goodsList := make([]interface{}, 0)
for _, item := range rsp.Goods {
tmpMap := gin.H{
"id": item.GoodsId,
"name": item.GoodsName,
"image": item.GoodsImage,
"price": item.GoodsPrice,
"nums": item.Nums,
}
goodsList = append(goodsList, tmpMap)
}
reMap["goods"] = goodsList
//生成支付宝的支付url
client, err := alipay.New(global.ServerConfig.AliPayInfo.AppID, global.ServerConfig.AliPayInfo.PrivateKey, false)
if err != nil {
zap.S().Errorw("实例化支付宝失败")
ctx.JSON(http.StatusInternalServerError, gin.H{
"msg": err.Error(),
})
return
}
err = client.LoadAliPayPublicKey((global.ServerConfig.AliPayInfo.AliPublicKey))
if err != nil {
zap.S().Errorw("加载支付宝的公钥失败")
ctx.JSON(http.StatusInternalServerError, gin.H{
"msg": err.Error(),
})
return
}
var p = alipay.TradePagePay{}
p.NotifyURL = global.ServerConfig.AliPayInfo.NotifyURL
p.ReturnURL = global.ServerConfig.AliPayInfo.ReturnURL
p.Subject = "慕学生鲜订单-" + rsp.OrderInfo.OrderSn
p.OutTradeNo = rsp.OrderInfo.OrderSn
p.TotalAmount = strconv.FormatFloat(float64(rsp.OrderInfo.Total), 'f', 2, 64)
p.ProductCode = "FAST_INSTANT_TRADE_PAY"
url, err := client.TradePagePay(p)
if err != nil {
zap.S().Errorw("生成支付url失败")
ctx.JSON(http.StatusInternalServerError, gin.H{
"msg": err.Error(),
})
return
}
reMap["alipay_url"] = url.String()
ctx.JSON(http.StatusOK, reMap)
}
2.4购物车列表
func List(ctx *gin.Context) {
//获取购物车商品
userId, _ := ctx.Get("userId")
rsp, err := global.OrderSrvClient.CartItemList(context.Background(), &proto.UserInfo{
Id: int32(userId.(uint)),
})
if err != nil {
zap.S().Errorw("[List] 查询 【购物车列表】失败")
panic(err)
api.HandleGrpcErrorToHttp(err, ctx)
return
}
ids := make([]int32, 0)
for _, item := range rsp.Data {
ids = append(ids, item.GoodsId)
}
if len(ids) == 0 {
ctx.JSON(http.StatusOK, gin.H{
"total": 0,
})
return
}
//请求商品服务获取商品信息
goodsRsp, err := global.GoodsSrvClient.BatchGetGoods(context.Background(), &goodsProto.BatchGoodsIdInfo{
Id: ids,
})
if err != nil {
zap.S().Errorw("[List] 批量查询【商品列表】失败")
api.HandleGrpcErrorToHttp(err, ctx)
return
}
reMap := gin.H{
"total": rsp.Total,
}
goodsList := make([]interface{}, 0)
for _, item := range rsp.Data {
for _, good := range goodsRsp.Data {
if good.Id == item.GoodsId {
tmpMap := map[string]interface{}{}
tmpMap["id"] = item.Id
tmpMap["goods_id"] = item.GoodsId
tmpMap["good_name"] = good.Name
tmpMap["good_image"] = good.GoodsFrontImage
tmpMap["good_price"] = good.ShopPrice
tmpMap["nums"] = item.Nums
tmpMap["checked"] = item.Checked
goodsList = append(goodsList, tmpMap)
}
}
}
reMap["data"] = goodsList
ctx.JSON(http.StatusOK, reMap)
}
2.5创建购物车
func New(ctx *gin.Context) {
//添加商品到购物车
itemForm := forms.ShopCartItemForm{}
if err := ctx.ShouldBindJSON(&itemForm); err != nil {
api.HandleValidatorError(ctx, err)
return
}
//为了严谨性,添加商品到购物车之前,记得检查一下商品是否存在
_, err := global.GoodsSrvClient.GetGoodsDetail(context.Background(), &goodsProto.GoodInfoRequest{
Id: itemForm.GoodsId,
})
if err != nil {
zap.S().Errorw("[List] 查询【商品信息】失败")
api.HandleGrpcErrorToHttp(err, ctx)
return
}
//如果现在添加到购物车的数量和库存的数量不一致
invRsp, err := global.InventorySrvClient.InvDetail(context.Background(), &proto.GoodsInvInfo{
GoodsId: itemForm.GoodsId,
})
if err != nil {
zap.S().Errorw("[List] 查询【库存信息】失败")
api.HandleGrpcErrorToHttp(err, ctx)
return
}
if invRsp.Num < itemForm.Nums {
ctx.JSON(http.StatusBadRequest, gin.H{
"nums": "库存不足",
})
return
}
userId, _ := ctx.Get("userId")
rsp, err := global.OrderSrvClient.CreateCartItem(context.Background(), &proto.CartItemRequest{
GoodsId: itemForm.GoodsId,
UserId: int32(userId.(uint)),
Nums: itemForm.Nums,
})
if err != nil {
zap.S().Errorw("添加到购物车失败")
api.HandleGrpcErrorToHttp(err, ctx)
return
}
ctx.JSON(http.StatusOK, gin.H{
"id": rsp.Id,
})
}
2.6更新购物车
func Update(ctx *gin.Context) {
// o/v1/421
id := ctx.Param("id")
i, err := strconv.Atoi(id)
if err != nil {
ctx.JSON(http.StatusNotFound, gin.H{
"msg": "url格式出错",
})
return
}
itemForm := forms.ShopCartItemUpdateForm{}
if err := ctx.ShouldBindJSON(&itemForm); err != nil {
api.HandleValidatorError(ctx, err)
return
}
userId, _ := ctx.Get("userId")
request := proto.CartItemRequest{
UserId: int32(userId.(uint)),
GoodsId: int32(i),
Nums: itemForm.Nums,
Checked: false,
}
if itemForm.Checked != nil {
request.Checked = *itemForm.Checked
}
_, err = global.OrderSrvClient.UpdateCartItem(context.Background(), &request)
if err != nil {
zap.S().Errorw("更新购物车记录失败")
api.HandleGrpcErrorToHttp(err, ctx)
return
}
ctx.Status(http.StatusOK)
}
2.7删除购物车
func Delete(ctx *gin.Context) {
id := ctx.Param("id")
i, err := strconv.Atoi(id)
if err != nil {
ctx.JSON(http.StatusNotFound, gin.H{
"msg": "url格式出错",
})
return
}
userId, _ := ctx.Get("userId")
_, err = global.OrderSrvClient.DeleteCartItem(context.Background(), &proto.CartItemRequest{
UserId: int32(userId.(uint)),
GoodsId: int32(i),
})
if err != nil {
zap.S().Errorw("删除购物车记录失败")
api.HandleGrpcErrorToHttp(err, ctx)
return
}
ctx.Status(http.StatusOK)
}
lipay.TradePagePay{}
p.NotifyURL = global.ServerConfig.AliPayInfo.NotifyURL
p.ReturnURL = global.ServerConfig.AliPayInfo.ReturnURL
p.Subject = “慕学生鲜订单-” + rsp.OrderSn
p.OutTradeNo = rsp.OrderSn
p.TotalAmount = strconv.FormatFloat(float64(rsp.Total), ‘f’, 2, 64)
p.ProductCode = “FAST_INSTANT_TRADE_PAY”
url, err := client.TradePagePay§
if err != nil {
zap.S().Errorw(“生成支付url失败”)
ctx.JSON(http.StatusInternalServerError, gin.H{
“msg”: err.Error(),
})
return
}
ctx.JSON(http.StatusOK, gin.H{
“id”: rsp.Id,
“alipay_url”: url.String(),
})
}
##### 2.3订单详情
```go
func Detail(ctx *gin.Context) {
id := ctx.Param("id")
userId, _ := ctx.Get("userId")
i, err := strconv.Atoi(id)
if err != nil {
ctx.JSON(http.StatusNotFound, gin.H{
"msg": "url格式出错",
})
return
}
//如果是管理员用户则返回所有的订单
request := proto.OrderRequest{
Id: int32(i),
}
claims, _ := ctx.Get("claims")
model := claims.(*models.CustomClaims)
if model.AuthorityId == 1 {
request.UserId = int32(userId.(uint))
}
rsp, err := global.OrderSrvClient.OrderDetail(context.Background(), &request)
if err != nil {
zap.S().Errorw("获取订单详情失败")
api.HandleGrpcErrorToHttp(err, ctx)
return
}
reMap := gin.H{}
reMap["id"] = rsp.OrderInfo.Id
reMap["status"] = rsp.OrderInfo.Status
reMap["user"] = rsp.OrderInfo.UserId
reMap["post"] = rsp.OrderInfo.Post
reMap["total"] = rsp.OrderInfo.Total
reMap["address"] = rsp.OrderInfo.Address
reMap["name"] = rsp.OrderInfo.Name
reMap["mobile"] = rsp.OrderInfo.Mobile
reMap["pay_type"] = rsp.OrderInfo.PayType
reMap["order_sn"] = rsp.OrderInfo.OrderSn
goodsList := make([]interface{}, 0)
for _, item := range rsp.Goods {
tmpMap := gin.H{
"id": item.GoodsId,
"name": item.GoodsName,
"image": item.GoodsImage,
"price": item.GoodsPrice,
"nums": item.Nums,
}
goodsList = append(goodsList, tmpMap)
}
reMap["goods"] = goodsList
//生成支付宝的支付url
client, err := alipay.New(global.ServerConfig.AliPayInfo.AppID, global.ServerConfig.AliPayInfo.PrivateKey, false)
if err != nil {
zap.S().Errorw("实例化支付宝失败")
ctx.JSON(http.StatusInternalServerError, gin.H{
"msg": err.Error(),
})
return
}
err = client.LoadAliPayPublicKey((global.ServerConfig.AliPayInfo.AliPublicKey))
if err != nil {
zap.S().Errorw("加载支付宝的公钥失败")
ctx.JSON(http.StatusInternalServerError, gin.H{
"msg": err.Error(),
})
return
}
var p = alipay.TradePagePay{}
p.NotifyURL = global.ServerConfig.AliPayInfo.NotifyURL
p.ReturnURL = global.ServerConfig.AliPayInfo.ReturnURL
p.Subject = "慕学生鲜订单-" + rsp.OrderInfo.OrderSn
p.OutTradeNo = rsp.OrderInfo.OrderSn
p.TotalAmount = strconv.FormatFloat(float64(rsp.OrderInfo.Total), 'f', 2, 64)
p.ProductCode = "FAST_INSTANT_TRADE_PAY"
url, err := client.TradePagePay(p)
if err != nil {
zap.S().Errorw("生成支付url失败")
ctx.JSON(http.StatusInternalServerError, gin.H{
"msg": err.Error(),
})
return
}
reMap["alipay_url"] = url.String()
ctx.JSON(http.StatusOK, reMap)
}
2.4购物车列表
func List(ctx *gin.Context) {
//获取购物车商品
userId, _ := ctx.Get("userId")
rsp, err := global.OrderSrvClient.CartItemList(context.Background(), &proto.UserInfo{
Id: int32(userId.(uint)),
})
if err != nil {
zap.S().Errorw("[List] 查询 【购物车列表】失败")
panic(err)
api.HandleGrpcErrorToHttp(err, ctx)
return
}
ids := make([]int32, 0)
for _, item := range rsp.Data {
ids = append(ids, item.GoodsId)
}
if len(ids) == 0 {
ctx.JSON(http.StatusOK, gin.H{
"total": 0,
})
return
}
//请求商品服务获取商品信息
goodsRsp, err := global.GoodsSrvClient.BatchGetGoods(context.Background(), &goodsProto.BatchGoodsIdInfo{
Id: ids,
})
if err != nil {
zap.S().Errorw("[List] 批量查询【商品列表】失败")
api.HandleGrpcErrorToHttp(err, ctx)
return
}
reMap := gin.H{
"total": rsp.Total,
}
goodsList := make([]interface{}, 0)
for _, item := range rsp.Data {
for _, good := range goodsRsp.Data {
if good.Id == item.GoodsId {
tmpMap := map[string]interface{}{}
tmpMap["id"] = item.Id
tmpMap["goods_id"] = item.GoodsId
tmpMap["good_name"] = good.Name
tmpMap["good_image"] = good.GoodsFrontImage
tmpMap["good_price"] = good.ShopPrice
tmpMap["nums"] = item.Nums
tmpMap["checked"] = item.Checked
goodsList = append(goodsList, tmpMap)
}
}
}
reMap["data"] = goodsList
ctx.JSON(http.StatusOK, reMap)
}
2.5创建购物车
func New(ctx *gin.Context) {
//添加商品到购物车
itemForm := forms.ShopCartItemForm{}
if err := ctx.ShouldBindJSON(&itemForm); err != nil {
api.HandleValidatorError(ctx, err)
return
}
//为了严谨性,添加商品到购物车之前,记得检查一下商品是否存在
_, err := global.GoodsSrvClient.GetGoodsDetail(context.Background(), &goodsProto.GoodInfoRequest{
Id: itemForm.GoodsId,
})
if err != nil {
zap.S().Errorw("[List] 查询【商品信息】失败")
api.HandleGrpcErrorToHttp(err, ctx)
return
}
//如果现在添加到购物车的数量和库存的数量不一致
invRsp, err := global.InventorySrvClient.InvDetail(context.Background(), &proto.GoodsInvInfo{
GoodsId: itemForm.GoodsId,
})
if err != nil {
zap.S().Errorw("[List] 查询【库存信息】失败")
api.HandleGrpcErrorToHttp(err, ctx)
return
}
if invRsp.Num < itemForm.Nums {
ctx.JSON(http.StatusBadRequest, gin.H{
"nums": "库存不足",
})
return
}
userId, _ := ctx.Get("userId")
rsp, err := global.OrderSrvClient.CreateCartItem(context.Background(), &proto.CartItemRequest{
GoodsId: itemForm.GoodsId,
UserId: int32(userId.(uint)),
Nums: itemForm.Nums,
})
if err != nil {
zap.S().Errorw("添加到购物车失败")
api.HandleGrpcErrorToHttp(err, ctx)
return
}
ctx.JSON(http.StatusOK, gin.H{
"id": rsp.Id,
})
}
2.6更新购物车
func Update(ctx *gin.Context) {
// o/v1/421
id := ctx.Param("id")
i, err := strconv.Atoi(id)
if err != nil {
ctx.JSON(http.StatusNotFound, gin.H{
"msg": "url格式出错",
})
return
}
itemForm := forms.ShopCartItemUpdateForm{}
if err := ctx.ShouldBindJSON(&itemForm); err != nil {
api.HandleValidatorError(ctx, err)
return
}
userId, _ := ctx.Get("userId")
request := proto.CartItemRequest{
UserId: int32(userId.(uint)),
GoodsId: int32(i),
Nums: itemForm.Nums,
Checked: false,
}
if itemForm.Checked != nil {
request.Checked = *itemForm.Checked
}
_, err = global.OrderSrvClient.UpdateCartItem(context.Background(), &request)
if err != nil {
zap.S().Errorw("更新购物车记录失败")
api.HandleGrpcErrorToHttp(err, ctx)
return
}
ctx.Status(http.StatusOK)
}
2.7删除购物车
func Delete(ctx *gin.Context) {
id := ctx.Param("id")
i, err := strconv.Atoi(id)
if err != nil {
ctx.JSON(http.StatusNotFound, gin.H{
"msg": "url格式出错",
})
return
}
userId, _ := ctx.Get("userId")
_, err = global.OrderSrvClient.DeleteCartItem(context.Background(), &proto.CartItemRequest{
UserId: int32(userId.(uint)),
GoodsId: int32(i),
})
if err != nil {
zap.S().Errorw("删除购物车记录失败")
api.HandleGrpcErrorToHttp(err, ctx)
return
}
ctx.Status(http.StatusOK)
}
875

被折叠的 条评论
为什么被折叠?



