Go项目(商品微服务-1)

news2024/11/26 2:28:53

文章目录

  • 简介
  • 建表
  • proto
  • handler
    • 商品
  • 小结

简介

  • 商品微服务主要在于表的设计,建哪些表?表之间的关系是怎样的?
    • 主要代码就是 CURD
    • 表和字段的设计是一个比较有挑战性的工作,比较难说清楚,也需要经验的积累,这里关注点在微服务,暂时先 “借鉴” 别人的设计
    • 一般需要多次迭代才能设计出比较合理的表结构
  • 我们将商品相关的服务都放在这部分,方便建表和管理;微服务并不是越多越好,分成太多服务可能会让系统太复杂难以管理
    • 如何划分微服务需要积累经验,也有针对性的方法可以套用,需要单独学习

建表

  • 新建商品微服务 Service 层,直接把 user_srv 的目录拷过来,整体替换相关路径
    1
  • 表设计及相关操作,主要有四张表
    2
  • 放个商品表的结构
    // 商品表
    // 某一个具体的商品
    type Goods struct {
    	BaseModel
    
    	CategoryID int32    `gorm:"type:int;not null"`
    	Category   Category // 一对多/一对一
    	BrandsID   int32    `gorm:"type:int;not null"`
    	Brands     Brands   // 一对一
    
    	OnSale   bool `gorm:"default:false;not null"`
    	ShipFree bool `gorm:"default:false;not null"`
    	IsNew    bool `gorm:"default:false;not null"`
    	IsHot    bool `gorm:"default:false;not null"`
    
    	Name            string   `gorm:"type:varchar(50);not null"`
    	GoodsSn         string   `gorm:"type:varchar(50);not null"`
    	ClickNum        int32    `gorm:"type:int;default:0;not null"`
    	SoldNum         int32    `gorm:"type:int;default:0;not null"`
    	FavNum          int32    `gorm:"type:int;default:0;not null"`
    	MarketPrice     float32  `gorm:"not null"`
    	ShopPrice       float32  `gorm:"not null"`
    	GoodsBrief      string   `gorm:"type:varchar(100);not null"`
    	Images          GormList `gorm:"type:varchar(1000);not null"`
    	DescImages      GormList `gorm:"type:varchar(1000);not null"`
    	GoodsFrontImage string   `gorm:"type:varchar(200);not null"`
    }
    
  • 对字段的限制一般都要求 not null,也就是必须有值,然后限制用户必须输入或者我们设置 default
    • 设置为 null 带来的问题
  • 定义 model
    • 注意表之间的关联关系,查看 gorm 文档了解使用方式
  • 生成表结构
    • 新建数据库,直接在(109.128)机器上操作,方便启动项目
    • CREATE DATABASE IF NOT EXISTS shop_goods_srv DEFAULT CHARSET utf8 COLLATE utf8_general_ci;
    • 一个微服务对应一个数据库,要和其他微服务隔离开
    • 还是在 model/main 下面运行一次 main 方法即可
      4
    • 运行 sql 文件,导入数据;将上面创建的表结构全部删掉(不删也行),因为 SQL 文件里包含了建表语句
      3

proto

  • proto 文件的设计又是一个重点,首先要根据页面进行需求分析(前端页面和后台管理页面),需要哪些接口?传递哪些参数?会有什么响应?
  • 接口定义也不是一蹴而就的,后面可能会修改,不必在一开始追求完美
  • protoc -I . goods.proto --go_out=plugins=grpc:. 生成 stub 文件
    • 遇到点小问题,empty.proto 找不到,拷过来放到 proto/google/protobuf 下面试试
  • 定义 Service 层(Server端)接口(handler),直接搜 GoodsServer,将接口定义拷过来
    • 这里接口太多,我们分在不同 handler 文件实现,不着急实现,先把接口定义拷过去修改一下引用,因为没有实现的话项目无法启动,
    • 再修改 main.go 和 nacos 配置文件(本地/中心配置),启动项目看能否注册成功
      1

handler

  • 具体实现各接口,先从品牌和轮播图开始,这里放个获取品牌列表的例子
    // 品牌
    func (s *GoodsServer) BrandList(ctx context.Context, req *proto.BrandFilterRequest) (*proto.BrandListResponse, error) {
    	brandListResponse := proto.BrandListResponse{}
    
    	var brands []model.Brands
    	// 分页:第几页,每页数量
    	// Scopes 需要传一个匿名函数进去,在里面做一些判断并查询数据
    	result := global.DB.Scopes(Paginate(int(req.Pages), int(req.PagePerNums))).Find(&brands)
    	if result.Error != nil {
    		return nil, result.Error
    	}
    
    	var total int64
    	global.DB.Model(&model.Brands{}).Count(&total) // 单独再获取一次品牌总数
    	brandListResponse.Total = int32(total)
    
    	// 准备响应数据,单个品牌的BrandInfoResponse 和品牌列表的 brandListResponse,要根据 proto 定义的 message 安排好
    	var brandResponses []*proto.BrandInfoResponse
    	for _, brand := range brands {
    		brandResponses = append(brandResponses, &proto.BrandInfoResponse{
    			Id:   brand.ID,
    			Name: brand.Name,
    			Logo: brand.Logo,
    		})
    	}
    	brandListResponse.Data = brandResponses
    	return &brandListResponse, nil
    }
    
  • 回答一个上一篇的问题:查询数据时不传表名,传入 model 定义的 struct 就可以吗?
    • 是的,比如 DB.First(&model.Brands{}),因为 struct 名就是表名,改表名也是通过绑在 struct 上的函数进行的
  • 商品分类
    • 先看 GetAllCategorysList,这里比较 tricky,因为有多级分类,需要同时查询两级 subCategory
    • 我们希望得到的数据格式
      [
      	{
      		"id":xxx,
      		"name":"",
      		"level":1,
      		"is_tab":false,
      		"parent":13xxx,
      		"sub_category":[
      			"id":xxx,
      			"name":"",
      			"level":1,
      			"is_tab":false,
      			"sub_category":[
      				"id":xxx,
      				"name":"",
      			]
      		]
      	}
      ]
      
    • 通过 gorm 提供的预加载(反向查询)实现
    • 条件查询一级目录:global.DB.Where(&model.Category{Level: 1})
    • 反向查询两级子类目:Preload("SubCategory.SubCategory")
    • 再贴一个获取子分类的函数,grpc 的接口实现都是三步走
      // 获取子分类,传入一级或二级类别ID
      func (s *GoodsServer) GetSubCategory(ctx context.Context, req *proto.CategoryListRequest) (*proto.SubCategoryListResponse, error) {
      	// 1.准备返回值
      	categoryListResponse := proto.SubCategoryListResponse{} // total, info(parent), sub
      	// 2.根据请求参数查询数据+判空
      	var category model.Category
      	if result := global.DB.First(&category, req.Id); result.RowsAffected == 0 {
      		return nil, status.Errorf(codes.NotFound, "商品分类不存在")
      	}
      	// 3.填充返回值各字段-Info
      	categoryListResponse.Info = &proto.CategoryInfoResponse{
      		Id:             category.ID,
      		Name:           category.Name,
      		Level:          category.Level,
      		IsTab:          category.IsTab,
      		ParentCategory: category.ParentCategoryID,
      	}
      
      	var subCategorys []model.Category
      	var subCategoryResponse []*proto.CategoryInfoResponse
      	// 如果要获取三级子分类,默认获取下面一级
      	//preloads := "SubCategory"
      	//if category.Level == 1 {
      	//	preloads = "SubCategory.SubCategory"
      	//}
      	// 只有在一级时才有获取三级的需求,但是这里没必要,这里把这个接口做的小一点,限制在只获取下一级,需要三级的话再在二级一一请求
      	global.DB.Where(&model.Category{ParentCategoryID: req.Id}).Find(&subCategorys)
      	for _, subCategory := range subCategorys {
      		subCategoryResponse = append(subCategoryResponse, &proto.CategoryInfoResponse{
      			Id:             subCategory.ID,
      			Name:           subCategory.Name,
      			Level:          subCategory.Level,
      			IsTab:          subCategory.IsTab,
      			ParentCategory: subCategory.ParentCategoryID,
      		})
      	}
      	// 3.填充返回值各字段-SubCategorys
      	categoryListResponse.SubCategorys = subCategoryResponse
      	return &categoryListResponse, nil
      }
      
    • 这里需要写接口测试,可以在 test 目录写 UT 或者每个 handler 新建目录在 main 函数运行看效果(一个目录下只能有一个main)
  • 品牌分类
    • 查询品牌和分类数据表,这是个中间表,内容较多,相关操作也较为复杂一点
      5
    • model 定义如下
      type GoodsCategoryBrand struct {
      	BaseModel
      	CategoryID int32    `gorm:"type:int;index:idx_category_brand,unique"` // 外键
      	Category   Category // 外键约束,约束谁就指向谁
      
      	BrandsID int32 `gorm:"type:int;index:idx_category_brand,unique"` // 外键
      	Brands   Brands
      }
      
  • 这些接口都是给 web 层调用,但有的是给用户准备数据,有的是给后台管理员准备

商品

  • 商品接口,关系到最终展示给用户的数据,最重要
  • 注:这部分有很多入口:搜索、新品、热门、价格区间、商品分类,都需要调用接口获取对应商品,这些都可以看做是查询条件
  • 注意下面几点
    • 条件是可以叠加的,所有用 localDB 接收前面过滤出的数据体
    • 分类条件要考虑层级
  • 后续会集成 elastic 用于搜索

小结

  • 快速开发了商品微服务的 Service 层,服务配置项还是和 user_srv 相同,包括 nacos,mysql,consul,serverConfig,不需要更改定义
  • 接下来是商品微服务 API 层的快速开发

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/373408.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Linux】工具(2)——vim

本期博客我们进入到Linux环境下vim工具的学习:一、vim是什么📌Vim是一个超级超级强大的文本编辑器。Vim及前身VI,历史悠久(可能比多数读者的年龄更大),经历了几十年的考验和发展。Vim全称叫Vi IMproved. 而…

Linux安装云原生网关Kong/KongA

目录1 概述2 创建服务器3 安装postgres4 安装kong5 安装node6 安装KONGA1 概述 Kong Kong是一款基于OpenResty(NginxLua模块)编写的高可用、易扩展的开源API网关,专为云原生和云混合架构而建,并针对微服务和分布式架构进行了特别…

蓝桥杯算法模板

模拟散列表拉链法import java.io.*; import java.util.*; public class a1 {static int n;static int N100003;static int[] hnew int[N];static int[] enew int[N];static int[] nenew int[N]; static int idx; static void insert(int x){int k(x%NN)%N;e[idx]x;ne[idx]h[k];…

终端软件架构说

目录 零:前言 一,基于服务的架构 二,基于多进程多线程的架构 三,以数据为中心的架构 四,类Android的分层架构设计 五,总结 零:前言 谈到架构,可能大家的第一感觉是信息系统的…

2023年三月份图形化三级打卡试题

活动时间 从2023年3月1日至3月21日,每天一道编程题。 本次打卡的规则如下: 小朋友每天利用10~15分钟做一道编程题,遇到问题就来群内讨论,我来给大家答疑。 小朋友做完题目后,截图到朋友圈打卡并把打卡的截图发到活动群…

Vue 3第三章:模板语法及指令介绍

文章目录1. 插值表达式1.1. 声明变量可直接在模板中使用,采用{{变量名称}}的方式1.2. 模板语法支持三元表达式1.3. 模板语法支持运算1.4. 模板语法支持方法调用2. 指令2.1. v-bind:用于绑定属性或动态绑定对象的值到元素上。2.2. v-if、v-else-if、v-els…

C#学习记录——接口的实现

一小部分知识精英依旧直面核心困难,努力地进行深度钻研,生产内容;而大多数信息受众始终在享受轻度学习,消费内容。如果我们真的希望在时代潮流中占据一席之地,那就应该尽早抛弃轻松学习的幻想,锤炼深度学习…

Burp Suite 常用模块简介

Burp Suite 常用模块分为 目标站点(target)模块 代理(proxy)模块 攻击(Intruder)模块 重放(Repeater) 模块 Target模块是对站点资源的收集,与站点各资源包发出和相应包的记录 Proxy模块是核心模块,可以拦截数据包发送往浏览器,进行修改后再…

网络协议分析(2)判断两个ip数据包是不是同一个数据包分片

一个节点收到两个IP包的首部如下:(1)45 00 05 dc 18 56 20 00 40 01 bb 12 c0 a8 00 01 c0 a8 00 67(2)45 00 00 15 18 56 00 b9 49 01 e0 20 c0 a8 00 01 c0 a8 00 67分析并判断这两个IP包是不是同一个数据报的分片&a…

Android JetPack之启动优化StartUp初始化组件的详解和使用

一、背景 先看一下Android系统架构图 在Android设备中,设备先通电(PowerManager),然后加载内核层,内核走完,开始检查硬件,以及为硬件提供的公开接口,然后进入到库的加载。库挂载后开…

Winform控件开发(16)——Timer(史上最全)

前言: Timer控件的作用是按用户定义的时间间隔引发事件的计时器,说的直白点就是,他就像一个定时炸弹一样到了一定时间就爆炸一次,区别在于定时炸弹炸完了就不会再次爆炸了,但是Timer这个计时器到了下一个固定时间还会触发一次,上面那张图片就是一个典型的计时器,该定时器…

【Java】Spring Boot 配置文件

文章目录SpringBoot 配置文件1. 配置文件的作用2. 配置文件的格式3. properties配置文件说明3.1 properties基本语法3.2 读取配置文件3.3 properties缺点分析4. yml配置文件说明4.1 yml基本语法4.2 yml使用进阶4.2.1 yml配置不同的数据类型及null4.2.1 yml配置的读取4.2.2 配置…

Python蓝桥杯训练:基本数据结构 [哈希表]

Python蓝桥杯训练:基本数据结构 [哈希表] 文章目录Python蓝桥杯训练:基本数据结构 [哈希表]一、哈希表理论基础知识1、开放寻址法2、链式法二、有关哈希表的一些常见操作三、力扣上面一些有关哈希表的题目练习1、[有效的字母异位词](https://leetcode.cn…

0101基础概念-图-数据结构和算法(Java)

文章目录1 图1.1 定义1.2 4种图模型2 无向图2.1 定义2.2 术语后记1 图 1.1 定义 图是一种非线性的数据结构,表示多对多的关系。 图(Graph)是由顶点的有穷非空集合和顶点之间边的集合组成,通常表示为:G(V, E)&#xf…

ecology9-谷歌浏览器下-pdf.js在渲染时部分发票丢失文字 问题定位及解决

问题 问题描述 : 在谷歌浏览器下,pdf.js在渲染时部分发票丢失文字;360浏览器兼容模式不存在此问题 排查思路:1、对比谷歌浏览器的css样式和360浏览器兼容模式下的样式,没有发现关键差别 2、✔使用Fiddler修改网页js D…

什么是线程死锁?如何解决死锁问题

死锁,一组互相竞争的资源的线程之间相互等待,导致永久阻塞的现象。 如下图所示: 与死锁对应的,还有活锁,是指线程没有出现阻塞,但是无限循环。 有一个经典的银行转账例子如下: 我们有个账户类…

操作系统权限提升(十四)之绕过UAC提权-基于白名单AutoElevate绕过UAC提权

系列文章 操作系统权限提升(十二)之绕过UAC提权-Windows UAC概述 操作系统权限提升(十三)之绕过UAC提权-MSF和CS绕过UAC提权 注:阅读本编文章前,请先阅读系列文章,以免造成看不懂的情况!! 基于白名单AutoElevate绕过…

2-MATLAB APP Design-下拉菜单栏的使用

一、APP 界面设计展示 1.新建一个空白的APP,在此次的学习中,我们会用到编辑字段(文本框)、下拉菜单栏、坐标区,首先在界面中拖入一个编辑字段(文本框),在文本框中输入内容:下拉菜单栏的使用,调整背景颜色,字体的颜色为黑色,字体的大小调为26. 2.在左侧组件库常用栏…

Qt音视频开发17-vlc内核回调拿图片进行绘制

一、前言 在众多播放器中,支持的种类格式众多,并支持DVD影音光盘,VCD影音光盘及各类流式协议,提供了sdk进行开发,这点是至关重要的,尽管很多优秀的播放器很牛逼,由于没有提供sdk第三方开发&…

【网络编程套接字(一)】

网络编程套接字(一)理解源IP地址和目的IP地址理解源MAC地址和目的MAC地址理解源端口号和目的端口号PORT VS PID认识TCP协议和UDP协议网络字节序socket编程接口socket常见APIsockaddr结构简单的UDP网络程序服务端创建套接字服务端绑定字符串IP VS 整数IP客…