目录

  • 一、快速启动userop_srv
    • 1 - 快速生成项目
    • 2 - 表结构设计
    • 3 - nacos与yaml配置
    • 4 - proto接口定义
    • 5 - 启动userop_srv服务
  • 二、service的handle实现
    • 1 - handle_base
    • 2 - handle_address
    • 3 - handle_message
    • 4 - handle_userfav
    • 5 - proto注册修改
  • 三、handle接口测试
  • 四、快速启动userop_web
    • 1 - 快速生成项目
    • 2 - api修改
    • 3 - config修改
    • 4 - form表单修改
    • 5 - 从srv拷贝proto文件
    • 6 - global修改
    • 7 - router修改
    • 8 - 初始化连接修改
    • 9 - nacos配置与yaml修改
  • 五、YApi测试userop_web
    • 1 - 收藏YApi测试
    • 2 - 地址YApi测试
    • 3 - 留言YApi测试
  • 六、完整源码

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

一、快速启动userop_srv

1 - 快速生成项目

  • userop_srv:将inventory_srv下除了tmp以外的文件直接拷贝到userop_srv中;将inventory_srv在选中文件夹中替换成userop_srv

2 - 表结构设计

  • userop_srv/model/userop.go
package modelconst (LEAVING_MESSAGES = iota + 1COMPLAINTINQUIRYPOST_SALEWANT_TO_BUY
)type LeavingMessages struct {BaseModelUser        int32  `gorm:"type:int;index"`MessageType int32  `gorm:"type:int comment '留言类型: 1(留言),2(投诉),3(询问),4(售后),5(求购)'"`Subject     string `gorm:"type:varchar(100)"`Message stringFile    string `gorm:"type:varchar(200)"`
}func (LeavingMessages) TableName() string {return "leavingmessages"
}type Address struct {BaseModelUser         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 {BaseModelUser  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 mainimport ("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 writerlogger.Config{SlowThreshold: time.Second, // 慢 SQL 阈值LogLevel:      logger.Info, // Log levelColorful:      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 handlerimport ("gorm.io/gorm""nd/userop_srv/proto"
)type UserOpServer struct {proto.UnimplementedAddressServerproto.UnimplementedUserFavServerproto.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 = 100case pageSize <= 0:pageSize = 10}offset := (page - 1) * pageSizereturn db.Offset(offset).Limit(pageSize)}
}

2 - handle_address

  • userop_srv/handler/handle_address.go
package addressimport ("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.IdreMap["user_id"] = value.UserIdreMap["province"] = value.ProvincereMap["city"] = value.CityreMap["district"] = value.DistrictreMap["address"] = value.AddressreMap["signer_name"] = value.SignerNamereMap["signer_mobile"] = value.SignerMobileresult = append(result, reMap)}reMap["data"] = resultctx.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 handlerimport ("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.MessageListResponsevar messages []model.LeavingMessagesvar messageList []*proto.MessageResponseresult := 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 = messageListreturn &rsp, nil
}func (*UserOpServer) CreateMessage(ctx context.Context, req *proto.MessageRequest) (*proto.MessageResponse, error) {var message model.LeavingMessagesmessage.User = req.UserIdmessage.MessageType = req.MessageTypemessage.Subject = req.Subjectmessage.Message = req.Messagemessage.File = req.Fileglobal.DB.Save(&message)return &proto.MessageResponse{Id: message.ID}, nil
}

4 - handle_userfav

  • userop_srv/handler/handle_userfav.go
package handlerimport ("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.UserFavListResponsevar userFavs []model.UserFavvar 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 = userFavListreturn &rsp, nil
}func (*UserOpServer) AddUserFav(ctx context.Context, req *proto.UserFavRequest) (*emptypb.Empty, error) {var userFav model.UserFavuserFav.User = req.UserIduserFav.Goods = req.GoodsIdglobal.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.UserFavif 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 mainimport ("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.ClientConnfunc 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 errorconn, 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 addressimport ("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.IdreMap["user_id"] = value.UserIdreMap["province"] = value.ProvincereMap["city"] = value.CityreMap["district"] = value.DistrictreMap["address"] = value.AddressreMap["signer_name"] = value.SignerNamereMap["signer_mobile"] = value.SignerMobileresult = append(result, reMap)}reMap["data"] = resultctx.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 messageimport ("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.IdreMap["user_id"] = value.UserIdreMap["type"] = value.MessageTypereMap["subject"] = value.SubjectreMap["message"] = value.MessagereMap["file"] = value.Fileresult = append(result, reMap)}reMap["data"] = resultctx.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_favimport ("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.Namedata["shop_price"] = good.ShopPrice}}goodsList = append(goodsList, data)}reMap["data"] = goodsListctx.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 configtype 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 formstype 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 formstype 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 formstype UserFavForm struct {GoodsId    int32 `form:"goods" json:"goods" binding:"required"`
}

5 - 从srv拷贝proto文件

6 - global修改

  • userop_web/global/global.go
package globalimport (ut "github.com/go-playground/universal-translator""web_api/userop_web/config""web_api/userop_web/proto"
)var (Trans ut.TranslatorServerConfig *config.ServerConfig = &config.ServerConfig{}NacosConfig *config.NacosConfig = &config.NacosConfig{}GoodsSrvClient proto.GoodsClientMessageClient proto.MessageClientAddressClient proto.AddressClientUserFavClient proto.UserFavClient
)

7 - router修改

  • userop_web/router/router_address.go
package routerimport ("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 routerimport ("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 routerimport ("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 initializeimport ("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 initializeimport ("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.ConsulInfogoodsConn, 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的配置导入文件

27、用户操作srv、web服务实现相关推荐

  1. 一文讲透推荐系统提供web服务的2种方式

    作者丨gongyouliu 编辑丨zandy 来源 | 大数据与人工智能(ID: ai-big-data) 推荐系统是一种信息过滤技术,通过从用户行为中挖掘用户兴趣偏好,为用户提供个性化的信息,减少用 ...

  2. web service(web服务)总结

    由于开发需要用到接口, 想到了web service 以前了解过web service 但是没有实际写过, 今天就工作需要写一篇示例, 谨以为后学之人提供一点了解途径. 首先 说明的是我用的开发环境是 ...

  3. 基于 REST 的 Web 服务:基础

    from: https://www.ibm.com/developerworks/cn/webservices/ws-restful/ 基础 REST 定义了一组体系架构原则,您可以根据这些原则设计以 ...

  4. RESTful Web 服务 - 方法

    正如目前为止我们所讨论的,RESTful Web 服务大量使用 HTTP 动词确定要对指定资源进行的操作.下面的表格演示了常用 HTTP 动词的例子. 编号 HTTP 方法,URI 和操作 1 GET ...

  5. Linux实战教学笔记37:企业级Nginx Web服务优化实战(上)

    一,Nginx基本安全优化 1.1 调整参数隐藏Nginx软件版本号信息 一般来说,软件的漏洞都和版本有关,这个很像汽车的缺陷,同一批次的要有问题就都有问题,别的批次可能就都是好的.因此,我们应尽量隐 ...

  6. OGC 的 Web 服务规范

    一.关于 Web 服务 Web 服务是一种服务导向架构的技术,通过标准的 Web 协议提供服务,目的是实现跨平台. 根据 W3C 的定义,Web 服务应当是一个软件系统,用以支持网络间不同机器的互动操 ...

  7. Nginx web服务

    一.  Nginx网站服务软件 1. Nginx的优势:高性能web和反向代理服务器 1)作为web服务器:消耗内存小,实现高并发 2)作为负载均衡器:在内部可以直接支持PHP和Rails,也可支持作 ...

  8. 学习笔记---Web服务、Remoting、WCF (上) --- Web服务

    自从程序设计诞生起, 如何最大限度的重用代码, 减小编码的重复劳动就是程序员永恒不变的课题. 我们从学习面向对象开始, 先后引入了很多种共享代码的手段, 随着学习的不断深入就越发觉得程序设计的发展过程 ...

  9. 基于使用学习排序算法的Web服务学习的个性化的决策战略

    摘要----为了从类似的服务列表中进行功能上的选择,用户往往需要根据多个QoS准则做出他们的决定,它们需要对目标服务.在这个过程中,不同的用户可能遵循不同的决策策略,有些是补偿性的,在这个补偿中在所有 ...

最新文章

  1. 出任 Twitter 独立董事,AI 女神李飞飞的传奇人生
  2. idea log 不输出error_还在使用console.log()吗?Bunyan:一个简单易用的JS日志框架
  3. DeepWide推荐系统
  4. Gradle -- buildScript块与allprojects块及根级别的repositories区别
  5. python编辑器vim下载_vim做python编辑器
  6. 2.8 使用开源的实现方案-深度学习第四课《卷积神经网络》-Stanford吴恩达教授
  7. JS 变量的数据类型转换
  8. springboot 读取bootStrap.properties流程
  9. python微信机器人部署服务器_玩法收藏/云服务器/零基础微信机器人实践( Python )...
  10. BeJavaGod - 如何正确使用数据字典进行分类统一操作(一)
  11. android 电池续航时间,《Android优化专题》——优化电池续航能力
  12. java join字符串_Java字符串join()
  13. 《淘宝网开店 进货 运营 管理 客服 实战200招》——1.3 常见网上开店平台
  14. 李开复就中国移动开发者大会的演讲总结
  15. 跟着老万学linux运维-vi编辑器中的大小写转换技巧
  16. 使用fastdb的感受
  17. 自动化测试框架如果都总结成这样,人人都能学好
  18. c语言目标文件作用,关于编译:C语言中的目标文件是什么?
  19. 电脑PDF阅读+谷歌翻译
  20. [Python][自己写的杀病毒脚本]

热门文章

  1. 国防科技大学计算机专业课代码,国防科技大学各省院校代码与专业代码的说明...
  2. [APIO2020]交换城市(交互+kruskal重构树)
  3. AI+PS插画的运用与制作
  4. ISD1700系列语音芯片使用注意事项
  5. 架构师的软实力之估算
  6. 双色球笔记1--输出所有双色球号码
  7. 微信企业转账JAVA版(包括:1,转账个人零钱;2,转账个人银行卡;3,微信官网的SDK3.0.9存在的支付问题)
  8. 【智能制造】「人.机.料.法.环」最全面解析
  9. Android Studio 3.2新功能特性
  10. 网络通信——客户端服务器端交互