27、用户操作srv、web服务实现

需求分析:收藏、收货地址管理、留言,我们可以统一成用户的操作,所以我们新建一个服务userop_srv来进行实现

一、快速启动userop_srv

1 - 快速生成项目

  • userop_srv:将inventory_srv下除了tmp以外的文件直接拷贝到userop_srv中;将inventory_srv在选中文件夹中替换成userop_srv
    在这里插入图片描述

2 - 表结构设计

  • userop_srv/model/userop.go
package model

const (
	LEAVING_MESSAGES = iota + 1
	COMPLAINT
	INQUIRY
	POST_SALE
	WANT_TO_BUY
)

type LeavingMessages struct {
	BaseModel

	User        int32  `gorm:"type:int;index"`
	MessageType int32  `gorm:"type:int comment '留言类型: 1(留言),2(投诉),3(询问),4(售后),5(求购)'"`
	Subject     string `gorm:"type:varchar(100)"`

	Message string
	File    string `gorm:"type:varchar(200)"`
}

func (LeavingMessages) TableName() string {
	return "leavingmessages"
}

type Address struct {
	BaseModel

	User         int32  `gorm:"type:int;index"`
	Province     string `gorm:"type:varchar(10)"`
	City         string `gorm:"type:varchar(10)"`
	District     string `gorm:"type:varchar(20)"`
	Address      string `gorm:"type:varchar(100)"`
	SignerName   string `gorm:"type:varchar(20)"`
	SignerMobile string `gorm:"type:varchar(11)"`
}

type UserFav struct {
	BaseModel

	User  int32 `gorm:"type:int;index:idx_user_goods,unique"`
	Goods int32 `gorm:"type:int;index:idx_user_goods,unique"`
}

func (UserFav) TableName() string {
	return "userfav"
}

  • 数据库新建:mxshop_userop_srv
    在这里插入图片描述

  • userop_srv/model/main/main.go:创建表结构

package main

import (
	"fmt"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
	"gorm.io/gorm/schema"
	"log"
	"nd/userop_srv/global"
	"nd/userop_srv/initialize"
	"nd/userop_srv/model"
	"os"
	"time"
)

func main() {
	initialize.InitConfig()
	dsn := fmt.Sprintf("root:jiushi@tcp(%s:3306)/mxshop_userop_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.LeavingMessages{}, &model.UserFav{}, model.Address{})
}

在这里插入图片描述

3 - nacos与yaml配置

  • 添加nacos配置
    在这里插入图片描述
  • 克隆inventory的配置:对应修改为userop即可
{
  "name": "userop_srv",
  "host": "192.168.78.1",
  "tags": ["imooc", "bobby", "userop", "srv"],
  "mysql": {
    "host": "192.168.78.131",
    "port": 3306,
    "user": "root",
    "password": "jiushi",
    "db": "mxshop_userop_srv"
  },
  "consul": {
    "host": "192.168.78.131",
    "port": 8500
  }
}
  • yaml修改命名空间id
host: '192.168.78.131'
port: 8848
namespace: '167f44f6-8d47-4b13-a01a-e2a570dabaab'
user: 'nacos'
password: 'nacos'
dataid: 'userop_srv.json'
group: 'comp'

4 - proto接口定义

  • userop_srv/proto/address.proto
syntax = "proto3";
import "google/protobuf/empty.proto";
option go_package = ".;proto";

service Address{
  rpc GetAddressList(AddressRequest) returns(AddressListResponse); //查看地址
  rpc CreateAddress(AddressRequest) returns(AddressResponse); //新增地址
  rpc DeleteAddress(AddressRequest) returns(google.protobuf.Empty); //删除地址
  rpc UpdateAddress(AddressRequest) returns(google.protobuf.Empty); //修改地址
}

message AddressRequest{
  int32 id = 1;
  int32 userId = 2;
  string province = 3;
  string city = 4;
  string district = 5;
  string address = 6;
  string signerName = 7;
  string signerMobile = 8;
}

message  AddressResponse{
  int32 id = 1;
  int32 userId = 2;
  string province = 3;
  string city = 4;
  string district = 5;
  string address = 6;
  string signerName = 7;
  string signerMobile = 8;
}


message AddressListResponse {
  int32 total = 1;
  repeated AddressResponse data = 2;
}

  • userop_srv/proto/message.proto
syntax = "proto3";
option go_package = ".;proto";

service Message{
rpc MessageList(MessageRequest) returns(MessageListResponse); //批量获取留言信息
rpc CreateMessage(MessageRequest) returns(MessageResponse); //添加留言
}

message MessageRequest{
int32 id = 1;
int32 userId = 2;
int32 messageType = 3;
string subject = 4;
string message = 5;
string file = 6;
}

message  MessageResponse{
int32 id = 1;
int32 userId = 2;
int32 messageType = 3;
string subject = 4;
string message = 5;
string file = 6;
}

message MessageListResponse {
int32 total = 1;
repeated MessageResponse data = 2;
}

  • userop_srv/proto/userfav.proto
syntax = "proto3";
import "google/protobuf/empty.proto";
option go_package = ".;proto";

service UserFav{
  rpc GetFavList(UserFavRequest) returns(UserFavListResponse); //过滤收藏信息
  rpc AddUserFav(UserFavRequest) returns(google.protobuf.Empty); //添加收藏
  rpc DeleteUserFav(UserFavRequest) returns(google.protobuf.Empty); //删除收藏
  rpc GetUserFavDetail(UserFavRequest) returns(google.protobuf.Empty); //查看用户是否已经收藏某件商品
}

message UserFavRequest{
  int32 userId = 1;
  int32 goodsId = 2;
}
message UserFavResponse{
  int32 userId = 1;
  int32 goodsId = 2;
}

message UserFavListResponse {
  int32 total = 1;
  repeated UserFavResponse data = 2;
}
  • userop_srv/proto/gen_proto.bat:使用bat生成proto
protoc --go_out=. --go_opt=paths=import --go-grpc_out=. --go-grpc_opt=paths=import *.proto

在这里插入图片描述

  • userop_srv/main.go:修改对应的proto注册及默认启动端口
func main() {
	IP := flag.String("ip", "0.0.0.0", "ip地址")
	Port := flag.Int("port", 50061, "端口号") // 这个修改为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.RegisterAddressServer(server, &proto.UnimplementedAddressServer{})
	proto.RegisterMessageServer(server, &proto.UnimplementedMessageServer{})
	proto.RegisterUserFavServer(server, &proto.UnimplementedUserFavServer{})
	//省略。。。

5 - 启动userop_srv服务

在这里插入图片描述


二、service的handle实现

1 - handle_base

  • userop_srv/handler/handle_base.go:因为我们拆分了3个handle,所以将公共部分UserOpServer结构和分页逻辑提取出来
package handler

import (
	"gorm.io/gorm"
	"nd/userop_srv/proto"
)

type UserOpServer struct {
	proto.UnimplementedAddressServer
	proto.UnimplementedUserFavServer
	proto.UnimplementedMessageServer
}

func Paginate(page, pageSize int) func(db *gorm.DB) *gorm.DB {
	return func(db *gorm.DB) *gorm.DB {
		if page == 0 {
			page = 1
		}

		switch {
		case pageSize > 100:
			pageSize = 100
		case pageSize <= 0:
			pageSize = 10
		}

		offset := (page - 1) * pageSize
		return db.Offset(offset).Limit(pageSize)
	}
}

2 - handle_address

  • userop_srv/handler/handle_address.go
package address

import (
	"context"
	"github.com/gin-gonic/gin"
	"go.uber.org/zap"
	"net/http"
	"strconv"
	"web_api/userop_web/api"
	"web_api/userop_web/forms"
	"web_api/userop_web/global"
	"web_api/userop_web/models"
	"web_api/userop_web/proto"
)

func List(ctx *gin.Context) {
	request := &proto.AddressRequest{}

	claims, _ := ctx.Get("claims")
	currentUser := claims.(*models.CustomClaims)

	if currentUser.AuthorityId != 2 {
		userId, _ := ctx.Get("userId")
		request.UserId = int32(userId.(uint))
	}

	rsp, err := global.AddressClient.GetAddressList(context.Background(), request)
	if err != nil {
		zap.S().Errorw("获取地址列表失败")
		api.HandleGrpcErrorToHttp(err, ctx)
		return
	}

	reMap := gin.H{
		"total": rsp.Total,
	}

	result := make([]interface{}, 0)
	for _, value := range rsp.Data {
		reMap := make(map[string]interface{})
		reMap["id"] = value.Id
		reMap["user_id"] = value.UserId
		reMap["province"] = value.Province
		reMap["city"] = value.City
		reMap["district"] = value.District
		reMap["address"] = value.Address
		reMap["signer_name"] = value.SignerName
		reMap["signer_mobile"] = value.SignerMobile

		result = append(result, reMap)
	}

	reMap["data"] = result

	ctx.JSON(http.StatusOK, reMap)
}

func New(ctx *gin.Context) {
	addressForm := forms.AddressForm{}
	if err := ctx.ShouldBindJSON(&addressForm); err != nil {
		api.HandleValidatorError(ctx, err)
		return
	}

	userId, _ := ctx.Get("userId")
	rsp, err := global.AddressClient.CreateAddress(context.Background(), &proto.AddressRequest{
		UserId:       int32(userId.(uint)),
		Province:     addressForm.Province,
		City:         addressForm.City,
		District:     addressForm.District,
		Address:      addressForm.Address,
		SignerName:   addressForm.SignerName,
		SignerMobile: addressForm.SignerMobile,
	})

	if err != nil {
		zap.S().Errorw("新建地址失败")
		api.HandleGrpcErrorToHttp(err, ctx)
		return
	}

	ctx.JSON(http.StatusOK, gin.H{
		"id": rsp.Id,
	})
}

func Delete(ctx *gin.Context) {
	id := ctx.Param("id")
	i, err := strconv.ParseInt(id, 10, 32)
	if err != nil {
		ctx.Status(http.StatusNotFound)
		return
	}
	userId, _ := ctx.Get("userId")
	_, err = global.AddressClient.DeleteAddress(context.Background(), &proto.AddressRequest{
		Id:     int32(i),
		UserId: int32(userId.(uint)),
	})
	if err != nil {
		zap.S().Errorw("删除地址失败")
		api.HandleGrpcErrorToHttp(err, ctx)
		return
	}

	ctx.JSON(http.StatusOK, gin.H{
		"msg": "删除成功",
	})
}

func Update(ctx *gin.Context) {
	addressForm := forms.AddressForm{}
	if err := ctx.ShouldBindJSON(&addressForm); err != nil {
		api.HandleValidatorError(ctx, err)
		return
	}

	id := ctx.Param("id")
	i, err := strconv.ParseInt(id, 10, 32)
	if err != nil {
		ctx.Status(http.StatusNotFound)
		return
	}

	userId, _ := ctx.Get("userId")

	_, err = global.AddressClient.UpdateAddress(context.Background(), &proto.AddressRequest{
		Id:           int32(i),
		UserId:       int32(userId.(uint)),
		Province:     addressForm.Province,
		City:         addressForm.City,
		District:     addressForm.District,
		Address:      addressForm.Address,
		SignerName:   addressForm.SignerName,
		SignerMobile: addressForm.SignerMobile,
	})
	if err != nil {
		zap.S().Errorw("更新地址失败")
		api.HandleGrpcErrorToHttp(err, ctx)
		return
	}
	ctx.JSON(http.StatusOK, gin.H{})
}

3 - handle_message

  • userop_srv/handler/handle_message.go
package handler

import (
	"context"

	"nd/userop_srv/global"
	"nd/userop_srv/model"
	"nd/userop_srv/proto"
)

func (*UserOpServer) MessageList(ctx context.Context, req *proto.MessageRequest) (*proto.MessageListResponse, error) {
	var rsp proto.MessageListResponse
	var messages []model.LeavingMessages
	var messageList []*proto.MessageResponse

	result := global.DB.Where(&model.LeavingMessages{User: req.UserId}).Find(&messages)
	rsp.Total = int32(result.RowsAffected)

	for _, message := range messages {
		messageList = append(messageList, &proto.MessageResponse{
			Id:          message.ID,
			UserId:      message.User,
			MessageType: message.MessageType,
			Subject:     message.Subject,
			Message:     message.Message,
			File:        message.File,
		})
	}

	rsp.Data = messageList
	return &rsp, nil
}

func (*UserOpServer) CreateMessage(ctx context.Context, req *proto.MessageRequest) (*proto.MessageResponse, error) {
	var message model.LeavingMessages

	message.User = req.UserId
	message.MessageType = req.MessageType
	message.Subject = req.Subject
	message.Message = req.Message
	message.File = req.File

	global.DB.Save(&message)

	return &proto.MessageResponse{Id: message.ID}, nil
}

4 - handle_userfav

  • userop_srv/handler/handle_userfav.go
package handler

import (
	"context"
	"google.golang.org/protobuf/types/known/emptypb"

	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"nd/userop_srv/global"
	"nd/userop_srv/model"
	"nd/userop_srv/proto"
)

func (*UserOpServer) GetFavList(ctx context.Context, req *proto.UserFavRequest) (*proto.UserFavListResponse, error) {
	var rsp proto.UserFavListResponse
	var userFavs []model.UserFav
	var userFavList []*proto.UserFavResponse
	//查询用户的收藏记录
	//查询某件商品被哪些用户收藏了
	result := global.DB.Where(&model.UserFav{User: req.UserId, Goods: req.GoodsId}).Find(&userFavs)
	rsp.Total = int32(result.RowsAffected)

	for _, userFav := range userFavs {
		userFavList = append(userFavList, &proto.UserFavResponse{
			UserId:  userFav.User,
			GoodsId: userFav.Goods,
		})
	}

	rsp.Data = userFavList

	return &rsp, nil
}

func (*UserOpServer) AddUserFav(ctx context.Context, req *proto.UserFavRequest) (*emptypb.Empty, error) {
	var userFav model.UserFav

	userFav.User = req.UserId
	userFav.Goods = req.GoodsId

	global.DB.Save(&userFav)

	return &emptypb.Empty{}, nil
}

func (*UserOpServer) DeleteUserFav(ctx context.Context, req *proto.UserFavRequest) (*emptypb.Empty, error) {
	if result := global.DB.Unscoped().Where("goods=? and user=?", req.GoodsId, req.UserId).Delete(&model.UserFav{}); result.RowsAffected == 0 {
		return nil, status.Errorf(codes.NotFound, "收藏记录不存在")
	}
	return &emptypb.Empty{}, nil
}

func (*UserOpServer) GetUserFavDetail(ctx context.Context, req *proto.UserFavRequest) (*emptypb.Empty, error) {
	var userfav model.UserFav
	if result := global.DB.Where("goods=? and user=?", req.GoodsId, req.UserId).Find(&userfav); result.RowsAffected == 0 {
		return nil, status.Errorf(codes.NotFound, "收藏记录不存在")
	}
	return &emptypb.Empty{}, nil
}

5 - proto注册修改

  • userop_srv/main.go:因为之前我们使用userop_srv/handler/handle_base.go统一定义了UserOpServer结构,所以我们在注册的时候可以将3个都注册成&handler.UserOpServer{}
func main() {
	IP := flag.String("ip", "0.0.0.0", "ip地址")
	Port := flag.Int("port", 50061, "端口号") // 这个修改为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.RegisterAddressServer(server, &handler.UserOpServer{})
	proto.RegisterMessageServer(server, &handler.UserOpServer{})
	proto.RegisterUserFavServer(server, &handler.UserOpServer{})
	//省略。。。

三、handle接口测试

  • userop_srv/tests/userop/main.go:这里简单调用下3个查询接口,没有报错即可,其他的等我们在web服务中调用检查
package main

import (
	"context"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
	"nd/userop_srv/proto"
	"nd/userop_srv/tests"
)

var userFavClient proto.UserFavClient
var messageClient proto.MessageClient
var addressClient proto.AddressClient
var conn *grpc.ClientConn

func TestAddressList() {
	_, err := addressClient.GetAddressList(context.Background(), &proto.AddressRequest{
		UserId: 1,
	})
	if err != nil {
		panic(err)
	}
}

func TestMessageList() {
	_, err := messageClient.MessageList(context.Background(), &proto.MessageRequest{
		UserId: 1,
	})
	if err != nil {
		panic(err)
	}
}

func TestUserFav() {
	_, err := userFavClient.GetFavList(context.Background(), &proto.UserFavRequest{
		UserId: 1,
	})
	if err != nil {
		panic(err)
	}
}
func Init() {
	var err error
	conn, err = grpc.Dial(tests.TargetAddr, grpc.WithTransportCredentials(insecure.NewCredentials()))
	if err != nil {
		panic(err)
	}
	userFavClient = proto.NewUserFavClient(conn)
	messageClient = proto.NewMessageClient(conn)
	addressClient = proto.NewAddressClient(conn)
}

func main() {
	Init()

	TestAddressList()
	TestMessageList()
	TestUserFav()
	conn.Close()
}

在这里插入图片描述


四、快速启动userop_web

1 - 快速生成项目

  • userop_web:将order_web下除了tmp以外的文件直接拷贝到userop_web中;将order_web在选中文件夹中替换成userop_web
    在这里插入图片描述

2 - api修改

  • userop_web/api/address/api_address.go
package address

import (
	"context"
	"github.com/gin-gonic/gin"
	"go.uber.org/zap"
	"net/http"
	"strconv"
	"web_api/userop_web/api"
	"web_api/userop_web/forms"
	"web_api/userop_web/global"
	"web_api/userop_web/models"
	"web_api/userop_web/proto"
)

func List(ctx *gin.Context) {
	request := &proto.AddressRequest{}

	claims, _ := ctx.Get("claims")
	currentUser := claims.(*models.CustomClaims)

	if currentUser.AuthorityId != 2 {
		userId, _ := ctx.Get("userId")
		request.UserId = int32(userId.(uint))
	}

	rsp, err := global.AddressClient.GetAddressList(context.Background(), request)
	if err != nil {
		zap.S().Errorw("获取地址列表失败")
		api.HandleGrpcErrorToHttp(err, ctx)
		return
	}

	reMap := gin.H{
		"total": rsp.Total,
	}

	result := make([]interface{}, 0)
	for _, value := range rsp.Data {
		reMap := make(map[string]interface{})
		reMap["id"] = value.Id
		reMap["user_id"] = value.UserId
		reMap["province"] = value.Province
		reMap["city"] = value.City
		reMap["district"] = value.District
		reMap["address"] = value.Address
		reMap["signer_name"] = value.SignerName
		reMap["signer_mobile"] = value.SignerMobile

		result = append(result, reMap)
	}

	reMap["data"] = result

	ctx.JSON(http.StatusOK, reMap)
}

func New(ctx *gin.Context) {
	addressForm := forms.AddressForm{}
	if err := ctx.ShouldBindJSON(&addressForm); err != nil {
		api.HandleValidatorError(ctx, err)
		return
	}

	userId, _ := ctx.Get("userId")
	rsp, err := global.AddressClient.CreateAddress(context.Background(), &proto.AddressRequest{
		UserId:       int32(userId.(uint)),
		Province:     addressForm.Province,
		City:         addressForm.City,
		District:     addressForm.District,
		Address:      addressForm.Address,
		SignerName:   addressForm.SignerName,
		SignerMobile: addressForm.SignerMobile,
	})

	if err != nil {
		zap.S().Errorw("新建地址失败")
		api.HandleGrpcErrorToHttp(err, ctx)
		return
	}

	ctx.JSON(http.StatusOK, gin.H{
		"id": rsp.Id,
	})
}

func Delete(ctx *gin.Context) {
	id := ctx.Param("id")
	i, err := strconv.ParseInt(id, 10, 32)
	if err != nil {
		ctx.Status(http.StatusNotFound)
		return
	}
	_, err = global.AddressClient.DeleteAddress(context.Background(), &proto.AddressRequest{Id: int32(i)})
	if err != nil {
		zap.S().Errorw("删除地址失败")
		api.HandleGrpcErrorToHttp(err, ctx)
		return
	}

	ctx.JSON(http.StatusOK, gin.H{
		"msg": "删除成功",
	})
}

func Update(ctx *gin.Context) {
	addressForm := forms.AddressForm{}
	if err := ctx.ShouldBindJSON(&addressForm); err != nil {
		api.HandleValidatorError(ctx, err)
		return
	}

	id := ctx.Param("id")
	i, err := strconv.ParseInt(id, 10, 32)
	if err != nil {
		ctx.Status(http.StatusNotFound)
		return
	}

	_, err = global.AddressClient.UpdateAddress(context.Background(), &proto.AddressRequest{
		Id:           int32(i),
		Province:     addressForm.Province,
		City:         addressForm.City,
		District:     addressForm.District,
		Address:      addressForm.Address,
		SignerName:   addressForm.SignerName,
		SignerMobile: addressForm.SignerMobile,
	})
	if err != nil {
		zap.S().Errorw("更新地址失败")
		api.HandleGrpcErrorToHttp(err, ctx)
		return
	}
	ctx.JSON(http.StatusOK, gin.H{})
}

  • userop_web/api/message/api_message.go
package message

import (
	"context"
	"github.com/gin-gonic/gin"
	"go.uber.org/zap"
	"net/http"
	"web_api/userop_web/api"
	"web_api/userop_web/forms"
	"web_api/userop_web/global"
	"web_api/userop_web/models"
	"web_api/userop_web/proto"
)

func List(ctx *gin.Context) {
	request := &proto.MessageRequest{}

	userId, _ := ctx.Get("userId")
	claims, _ := ctx.Get("claims")
	model := claims.(*models.CustomClaims)
	if model.AuthorityId == 1 {
		request.UserId = int32(userId.(uint))
	}

	rsp, err := global.MessageClient.MessageList(context.Background(), request)
	if err != nil {
		zap.S().Errorw("获取留言失败")
		api.HandleGrpcErrorToHttp(err, ctx)
		return
	}

	reMap := map[string]interface{}{
		"total": rsp.Total,
	}
	result := make([]interface{}, 0)
	for _, value := range rsp.Data {
		reMap := make(map[string]interface{})
		reMap["id"] = value.Id
		reMap["user_id"] = value.UserId
		reMap["type"] = value.MessageType
		reMap["subject"] = value.Subject
		reMap["message"] = value.Message
		reMap["file"] = value.File

		result = append(result, reMap)
	}
	reMap["data"] = result

	ctx.JSON(http.StatusOK, reMap)
}

func New(ctx *gin.Context) {
	userId, _ := ctx.Get("userId")

	messageForm := forms.MessageForm{}
	if err := ctx.ShouldBindJSON(&messageForm); err != nil {
		api.HandleValidatorError(ctx, err)
		return
	}

	rsp, err := global.MessageClient.CreateMessage(context.Background(), &proto.MessageRequest{
		UserId:      int32(userId.(uint)),
		MessageType: messageForm.MessageType,
		Subject:     messageForm.Subject,
		Message:     messageForm.Message,
		File:        messageForm.File,
	})

	if err != nil {
		zap.S().Errorw("添加留言失败")
		api.HandleGrpcErrorToHttp(err, ctx)
		return
	}
	ctx.JSON(http.StatusOK, gin.H{
		"id": rsp.Id,
	})
}

  • userop_web/api/user_fav/api_user_fav.go
package user_fav

import (
	"context"
	"github.com/gin-gonic/gin"
	"go.uber.org/zap"
	"net/http"
	"strconv"
	"web_api/userop_web/api"
	"web_api/userop_web/forms"
	"web_api/userop_web/global"
	"web_api/userop_web/proto"
)

func List(ctx *gin.Context) {
	userId, _ := ctx.Get("userId")
	userFavRsp, err := global.UserFavClient.GetFavList(context.Background(), &proto.UserFavRequest{
		UserId: int32(userId.(uint)),
	})
	if err != nil {
		zap.S().Errorw("获取收藏列表失败")
		api.HandleGrpcErrorToHttp(err, ctx)
		return
	}

	ids := make([]int32, 0)
	for _, item := range userFavRsp.Data {
		ids = append(ids, item.GoodsId)
	}

	if len(ids) == 0 {
		ctx.JSON(http.StatusOK, gin.H{
			"total": 0,
		})
		return
	}

	//请求商品服务
	goods, err := global.GoodsSrvClient.BatchGetGoods(context.Background(), &proto.BatchGoodsIdInfo{
		Id: ids,
	})
	if err != nil {
		zap.S().Errorw("[List] 批量查询【商品列表】失败")
		api.HandleGrpcErrorToHttp(err, ctx)
		return
	}

	reMap := map[string]interface{}{
		"total": userFavRsp.Total,
	}

	goodsList := make([]interface{}, 0)
	for _, item := range userFavRsp.Data {
		data := gin.H{
			"id": item.GoodsId,
		}

		for _, good := range goods.Data {
			if item.GoodsId == good.Id {
				data["name"] = good.Name
				data["shop_price"] = good.ShopPrice
			}
		}

		goodsList = append(goodsList, data)
	}
	reMap["data"] = goodsList
	ctx.JSON(http.StatusOK, reMap)
}

func New(ctx *gin.Context) {
	userFavForm := forms.UserFavForm{}
	if err := ctx.ShouldBindJSON(&userFavForm); err != nil {
		api.HandleValidatorError(ctx, err)
		return
	}

	//缺少一步, 这个时候应该去商品服务查询一下这个是否存在
	userId, _ := ctx.Get("userId")
	_, err := global.UserFavClient.AddUserFav(context.Background(), &proto.UserFavRequest{
		UserId:  int32(userId.(uint)),
		GoodsId: userFavForm.GoodsId,
	})

	if err != nil {
		zap.S().Errorw("添加收藏记录失败")
		api.HandleGrpcErrorToHttp(err, ctx)
		return
	}

	ctx.JSON(http.StatusOK, gin.H{})
}

func Delete(ctx *gin.Context) {
	id := ctx.Param("id")
	i, err := strconv.ParseInt(id, 10, 32)
	if err != nil {
		ctx.Status(http.StatusNotFound)
		return
	}

	userId, _ := ctx.Get("userId")
	_, err = global.UserFavClient.DeleteUserFav(context.Background(), &proto.UserFavRequest{
		UserId:  int32(userId.(uint)),
		GoodsId: int32(i),
	})
	if err != nil {
		zap.S().Errorw("删除收藏记录失败")
		api.HandleGrpcErrorToHttp(err, ctx)
		return
	}

	ctx.JSON(http.StatusOK, gin.H{
		"msg": "删除成功",
	})
}

func Detail(ctx *gin.Context) {
	goodsId := ctx.Param("id")
	goodsIdInt, err := strconv.ParseInt(goodsId, 10, 32)
	if err != nil {
		ctx.Status(http.StatusNotFound)
		return
	}
	userId, _ := ctx.Get("userId")
	_, err = global.UserFavClient.GetUserFavDetail(context.Background(), &proto.UserFavRequest{
		UserId:  int32(userId.(uint)),
		GoodsId: int32(goodsIdInt),
	})
	if err != nil {
		zap.S().Errorw("查询收藏状态失败")
		api.HandleGrpcErrorToHttp(err, ctx)
		return
	}

	ctx.Status(http.StatusOK)
}

3 - config修改

  • userop_web/config/config.go
package config

type SrvConfig 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 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  SrvConfig    `mapstructure:"goods_srv" json:"goods_srv"`
	UserOpSrvInfo SrvConfig    `mapstructure:"userop_srv" json:"userop_srv"`
	JWTInfo       JWTConfig    `mapstructure:"jwt" json:"jwt"`
	ConsulInfo    ConsulConfig `mapstructure:"consul" json:"consul"`
}

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

4 - form表单修改

  • userop_web/forms/form_address.go
package forms

type AddressForm struct {
	Province    string `form:"province" json:"province" binding:"required"`
	City string `form:"city" json:"city" binding:"required"`
	District string `form:"district" json:"district" binding:"required"`
	Address string `form:"address" json:"address" binding:"required"`
	SignerName string `form:"signer_name" json:"signer_name" binding:"required"`
	SignerMobile string `form:"signer_mobile" json:"signer_mobile" binding:"required"`
}
  • userop_web/forms/form_message.go
package forms

type MessageForm struct {
	MessageType    int32 `form:"type" json:"type" binding:"required,oneof=1 2 3 4 5"`
	Subject string `form:"subject" json:"subject" binding:"required"`
	Message string `form:"message" json:"message" binding:"required"`
	File string `form:"file" json:"file" binding:"required"`
}
  • userop_web/forms/form_user_fav.go
package forms

type UserFavForm struct {
	GoodsId    int32 `form:"goods" json:"goods" binding:"required"`
}

5 - 从srv拷贝proto文件

在这里插入图片描述

6 - global修改

  • userop_web/global/global.go
package global

import (
	ut "github.com/go-playground/universal-translator"

	"web_api/userop_web/config"
	"web_api/userop_web/proto"
)

var (
	Trans ut.Translator

	ServerConfig *config.ServerConfig = &config.ServerConfig{}

	NacosConfig *config.NacosConfig = &config.NacosConfig{}

	GoodsSrvClient proto.GoodsClient

	MessageClient proto.MessageClient
	AddressClient proto.AddressClient
	UserFavClient proto.UserFavClient
)

7 - router修改

  • userop_web/router/router_address.go
package router

import (
	"github.com/gin-gonic/gin"
	"web_api/userop_web/api/address"
	"web_api/userop_web/middlewares"
)

func InitAddressRouter(Router *gin.RouterGroup) {
	//AddressRouter := Router.Group("address").Use(middlewares.JWTAuth())
	AddressRouter := Router.Group("address").Use(middlewares.SetUserId())
	{
		AddressRouter.GET("", address.List)
		AddressRouter.DELETE("/:id", address.Delete)
		AddressRouter.POST("", address.New)
		AddressRouter.PUT("/:id", address.Update)
	}
}

  • userop_web/router/router_message.go
package router

import (
	"github.com/gin-gonic/gin"
	"web_api/userop_web/api/message"
	"web_api/userop_web/middlewares"
)

func InitMessageRouter(Router *gin.RouterGroup) {
	//MessageRouter := Router.Group("message").Use(middlewares.JWTAuth())
	MessageRouter := Router.Group("message").Use(middlewares.SetUserId())
	{
		MessageRouter.GET("", message.List) // 轮播图列表页
		MessageRouter.POST("", message.New) //新建轮播图
	}
}

  • userop_web/router/router_user_fav.go
package router

import (
	"github.com/gin-gonic/gin"
	"web_api/userop_web/api/user_fav"
	"web_api/userop_web/middlewares"
)

func InitUserFavRouter(Router *gin.RouterGroup) {
	//UserFavRouter := Router.Group("userfavs").Use(middlewares.JWTAuth())
	UserFavRouter := Router.Group("userfavs").Use(middlewares.SetUserId())
	{
		UserFavRouter.DELETE("/:id", user_fav.Delete) // 删除收藏记录
		UserFavRouter.GET("/:id", user_fav.Detail)    // 获取收藏记录
		UserFavRouter.POST("", user_fav.New)          //新建收藏记录
		UserFavRouter.GET("", user_fav.List)          //获取当前用户的收藏
	}
}

  • userop_web/initialize/init_router.go:初始化router修改
package initialize

import (
	"github.com/gin-gonic/gin"

	"web_api/userop_web/middlewares"
	"web_api/userop_web/router"
)

func Routers() *gin.Engine {
	Router := gin.Default()

	//配置跨域
	Router.Use(middlewares.Cors())
	ApiGroup := Router.Group("/up/v1")

	router.InitAddressRouter(ApiGroup)
	router.InitMessageRouter(ApiGroup)
	router.InitUserFavRouter(ApiGroup)

	return Router
}

8 - 初始化连接修改

  • userop_web/initialize/init_srv_conn.go
package initialize

import (
	"fmt"
	_ "github.com/mbobakov/grpc-consul-resolver" // It's important
	"go.uber.org/zap"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
	"web_api/userop_web/global"
	"web_api/userop_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.WithTransportCredentials(insecure.NewCredentials()),
		grpc.WithDefaultServiceConfig(`{"loadBalancingPolicy": "round_robin"}`),
	)
	if err != nil {
		zap.S().Fatal("[InitSrvConn] 连接 【商品服务失败】")
	}

	global.GoodsSrvClient = proto.NewGoodsClient(goodsConn)

	userOpConn, err := grpc.Dial(
		fmt.Sprintf("consul://%s:%d/%s?wait=14s", consulInfo.Host, consulInfo.Port, global.ServerConfig.UserOpSrvInfo.Name),
		grpc.WithTransportCredentials(insecure.NewCredentials()),
		grpc.WithDefaultServiceConfig(`{"loadBalancingPolicy": "round_robin"}`),
	)
	if err != nil {
		zap.S().Fatal("[InitSrvConn] 连接 【用户操作服务失败】")
	}

	global.UserFavClient = proto.NewUserFavClient(userOpConn)
	global.MessageClient = proto.NewMessageClient(userOpConn)
	global.AddressClient = proto.NewAddressClient(userOpConn)
}

9 - nacos配置与yaml修改

  • 克隆配置:注意需要添加userop_srv服务;端口修改为8085
    在这里插入图片描述
{
  "host": "192.168.78.1",
  "name": "userop_web",
  "port": 8085,
  "tags": ["mxshop","imooc","bobby","userop","web"],
  "goods_srv": {
    "name": "goods_srv"
  },
  "userop_srv": {
    "name": "userop_srv"
  },
  "jwt": {
    "key": "VYLDYq3&hGWjWqF$K1ih"
  },
  "consul": {
    "host": "192.168.78.131",
    "port": 8500
  }
}
  • yaml修改空间id
host: '192.168.124.51'
port: 8848
namespace: '167f44f6-8d47-4b13-a01a-e2a570dabaab'
user: 'nacos'
password: 'nacos'
dataid: 'userop_web.json'
group: 'zsz'

在这里插入图片描述


五、YApi测试userop_web

测试需要启动goods_srv服务、userop_srv服务、userop_web服务

1 - 收藏YApi测试

在这里插入图片描述
在这里插入图片描述

  • 查询用户收藏列表
    在这里插入图片描述
  • 查询用户是否收藏某商品
    在这里插入图片描述
  • 删除某件商品的收藏
    在这里插入图片描述

2 - 地址YApi测试

  • 添加地址
    在这里插入图片描述
  • 地址列表

在这里插入图片描述

  • 更新地址
    在这里插入图片描述
  • 删除地址
    在这里插入图片描述

3 - 留言YApi测试

  • 添加留言
    在这里插入图片描述
  • 留言列表
    在这里插入图片描述

六、完整源码

  • 完整源码下载mxshop_srvsV10.0rar
  • 源码说明:(nacos的ip配置自行修改,全局变量DEV_CONFIG设置:1=zsz,2=comp,3=home)
    • goods_srv/model/sql/mxshop_goods.sql:包含了建表语句
    • other_import/api.json:YApi的导入文件
    • other_import/nacos_config_export_user.zip:nacos的user配置集导入文件
    • other_import/nacos_config_export_goods.zip:nacos的goods配置集导入文件
    • other_import/nacos_config_export_inventory.zip:nacos的inventory的配置导入文件
    • other_import/nacos_config_export_orders.zip:nacos的orders的配置导入文件
    • other_import/nacos_config_export_userop.zip:nacos的userop的配置导入文件
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

无休止符

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值