Go项目(商品微服务-1)
文章目录
- 简介
- 建表
- proto
- handler
- 商品
- 小结
简介
- 商品微服务主要在于表的设计,建哪些表?表之间的关系是怎样的?
- 主要代码就是 CURD
- 表和字段的设计是一个比较有挑战性的工作,比较难说清楚,也需要经验的积累,这里关注点在微服务,暂时先 “借鉴” 别人的设计
- 一般需要多次迭代才能设计出比较合理的表结构
- 我们将商品相关的服务都放在这部分,方便建表和管理;微服务并不是越多越好,分成太多服务可能会让系统太复杂难以管理
- 如何划分微服务需要积累经验,也有针对性的方法可以套用,需要单独学习
建表
- 新建商品微服务 Service 层,直接把 user_srv 的目录拷过来,整体替换相关路径

- 表设计及相关操作,主要有四张表

- 放个商品表的结构
// 商品表 // 某一个具体的商品 type Goods struct {BaseModelCategoryID 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 方法即可

- 运行 sql 文件,导入数据;将上面创建的表结构全部删掉(不删也行),因为 SQL 文件里包含了建表语句

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

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 int64global.DB.Model(&model.Brands{}).Count(&total) // 单独再获取一次品牌总数brandListResponse.Total = int32(total)// 准备响应数据,单个品牌的BrandInfoResponse 和品牌列表的 brandListResponse,要根据 proto 定义的 message 安排好var brandResponses []*proto.BrandInfoResponsefor _, brand := range brands {brandResponses = append(brandResponses, &proto.BrandInfoResponse{Id: brand.ID,Name: brand.Name,Logo: brand.Logo,})}brandListResponse.Data = brandResponsesreturn &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.Categoryif result := global.DB.First(&category, req.Id); result.RowsAffected == 0 {return nil, status.Errorf(codes.NotFound, "商品分类不存在")}// 3.填充返回值各字段-InfocategoryListResponse.Info = &proto.CategoryInfoResponse{Id: category.ID,Name: category.Name,Level: category.Level,IsTab: category.IsTab,ParentCategory: category.ParentCategoryID,}var subCategorys []model.Categoryvar 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.填充返回值各字段-SubCategoryscategoryListResponse.SubCategorys = subCategoryResponsereturn &categoryListResponse, nil } - 这里需要写接口测试,可以在 test 目录写 UT 或者每个 handler 新建目录在 main 函数运行看效果(一个目录下只能有一个main)
- 品牌分类
- 查询品牌和分类数据表,这是个中间表,内容较多,相关操作也较为复杂一点

- model 定义如下
type GoodsCategoryBrand struct {BaseModelCategoryID 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 层的快速开发
相关文章:
Go项目(商品微服务-1)
文章目录简介建表protohandler商品小结简介 商品微服务主要在于表的设计,建哪些表?表之间的关系是怎样的? 主要代码就是 CURD表和字段的设计是一个比较有挑战性的工作,比较难说清楚,也需要经验的积累,这里…...
机器学习——集成学习
引言 集成学习:让机器学习效果更好,单个不行,群殴走起。 分类 1. Bagging:训练多个分类器取平均(m代表树的个数)。 2.Boosting(提升算法):从弱学习器开始加,通过加权来进行训练。…...
VS编译系统 实用调试技巧
目录什么是bug?调试是什么?有多重要?debug和release的介绍windows环境调试介绍、一些调试实例如何写出(易于调试)的代码编程常见的错误什么是bug?其实bug在英文翻译中有表示臭虫的含义,因为第一次被发现的导致计算机…...
【华为OD机试模拟题】用 C++ 实现 - GPU 调度(2023.Q1)
最近更新的博客 【华为OD机试模拟题】用 C++ 实现 - 去重求和(2023.Q1) 文章目录 最近更新的博客使用说明GPU 调度题目输入输出示例一输入输出说明示例二输入输出说明Code使用说明 参加华为od机试,一定要注意不要完全背诵代码,需要理解之后模仿写出,通过率才会高。...
腾讯前端必会react面试题合集
React-Router的路由有几种模式? React-Router 支持使用 hash(对应 HashRouter)和 browser(对应 BrowserRouter) 两种路由规则, react-router-dom 提供了 BrowserRouter 和 HashRouter 两个组件来实现应用的…...
Linux搭建SVN服务器,并内网穿透实现公网远程访问
文章目录1. Ubuntu安装SVN服务2. 修改配置文件2.1 修改svnserve.conf文件2.2 修改passwd文件2.3 修改authz文件3. 启动svn服务4. 内网穿透4.1 安装cpolar内网穿透4.2 创建隧道映射本地端口5. 测试公网访问6. 配置固定公网TCP端口地址6.1 保留一个固定的公网TCP端口地址6.2 配置…...
C++STL之list的模拟实现
目录 一.list准备 二. iterator迭代器 1._list_iterator 2.begin()、end() 3.const_begin()、const_end() 4.!&& 5. && -- 6.operator* 7.operator-> 三.Modify(修改) 1.insert() 2.erase() 3.push_back() && push_front() 4.pop_bac…...
为什么硬件性能监控很重要
当今的混合网络环境平衡了分布式网络和现代技术的实施。但它们并不缺少一个核心组件:服务器。保持网络正常运行时间归结为监控和管理导致网络停机的因素。极有可能导致性能异常的此类因素之一是硬件。使用硬件监控器监控网络硬件已成为一项关键需求。 硬件监视器是…...
HTTP缓存
HTTP缓存HTTP缓存引发的一个问题HTTP缓存的作用HTTP缓存的分类强制缓存协商缓存(解决强缓存下资源不更新问题)缓存策略HTTP缓存引发的一个问题 有一次在开发移动端H5项目,UI提了几个UI问题,经过样式调试,android上没有…...
SPI设备树处理过程
SPI设备树处理过程 文章目录SPI设备树处理过程参考资料:一、 spi_device结构体二、 SPI设备树格式2.1 SPI Master2.2 SPI Device2.3 设备树示例三、设备树实例3.1 使用GPIO模拟的SPI控制器3.2 IMX6ULL SPI控制器四、 设备树处理过程致谢参考资料: 内核头…...
数据有哪些重要的作用?
我们正处在科技高速发展的时代,如今互联网已经与我们的生活息息相关,我们每天在互联网产生大量的数据,这些数据散落在网络中看似没有怎么作用,但是这些数据经过系统的处理整合起来确实非常有价值的。 一、 发展大数据技术可以提高…...
spring面试题总结
1、spring是什么? spring是一个轻量级IOC和AOP容器框架,是为Java应用程序提供基础性服务的一套框架,目的是用于简化企业应用的开发,开发者只需要关注业务需求即可: core container 容器组件 spring context,…...
使用MUI与H5+构建移动端app
前言 通过mui构建APP 效果图: <!DOCTYPE html> <html> <head><meta charset...
第17篇:Java变量总结
目录 1.变量的概念 1.1 变量来源 1.2 计算机中的变量 1.3 变量如何在内存中存储 2.Java变量...
使用51单片机的GPIO输出占空比可调节的PWM波
一、前言 在一些单片机或微控制器中,通用GPIO可以被配置为产生PWM信号。PWM即脉冲宽度调制,是一种用于模拟输出的技术。它可以通过改变输出信号的脉冲宽度来控制电路中的电平,从而实现对电路的控制。 二、什么是PWM波? PWM波&a…...
从产品经理的角度如何提升项目的交付质量?
提高交付质量 ,对于每个IT公司都是永恒的话题。 交付质量其实包含2重意义, 一是交付的高质量(客户角度),即客户的满意度;二是高质量的交付(交付团队的角度),这里是指如何…...
JavaScript BOM【快速掌握知识点】
目录 Window对象的常用属性 语法: Window对象的常用方法 语法: open()和close()方法 History对象 常用属性和方法 示例 Location对象 常用属性 常用方法 Document对象的常用方法 定时函数 超时调用:setTimeout() 间歇调用&…...
【算法】哈希表
作者:指针不指南吗 专栏:算法篇 🐾或许会很慢,但是不可以停下来🐾 文章目录1.定义2.优点3.数字哈希3.1拉链法3.2开放寻址法3.3 例题4.字符串哈希1.定义 哈希表(Hash table),是根据键…...
彻底搞懂React-hook链表构建原理
写在前面的小结 每一个 hook 函数都有对应的 hook 对象保存状态信息useContext是唯一一个不需要添加到 hook 链表的 hook 函数只有 useEffect、useLayoutEffect 以及 useImperativeHandle 这三个 hook 具有副作用,在 render 阶段需要给函数组件 fiber 添加对应的副…...
【数据挖掘实战】——应用系统负载分析与容量预测(ARIMA模型)
项目地址:Datamining_project: 数据挖掘实战项目代码 目录 一、背景和挖掘目标 1、问题背景 2、传统方法的不足 2、原始数据 3、挖掘目标 二、分析方法与过程 1、初步分析 2、总体流程 第一步:数据抽取 第二步:探索分析 第三步&a…...
从LeetCode到ACM:迷宫最短路径的C++ BFS模板,这么写就对了
从LeetCode到ACM:迷宫最短路径的C BFS模板实战精解 在算法竞赛和面试刷题中,迷宫类问题是最经典的场景之一。无论是LeetCode上的简单矩阵遍历,还是ACM竞赛中复杂的路径搜索,广度优先搜索(BFS)都是解决这类问…...
从PTA天梯赛L1真题看起:新手如何用C++快速搞定编程竞赛里的“送分题”?
从PTA天梯赛L1真题看起:新手如何用C快速搞定编程竞赛里的“送分题”? 第一次参加编程竞赛的新手,面对屏幕上密密麻麻的题目,往往会感到无从下手。但仔细观察历届PTA天梯赛L1级别的题目,你会发现一个有趣的现象——总有…...
8086汇编实战:用ZF、PF、SF标志位调试你的第一个程序(附调试截图)
8086汇编实战:用ZF、PF、SF标志位调试你的第一个程序(附调试截图) 刚接触汇编语言时,很多人会被那些神秘的标志位搞得一头雾水。记得我第一次在调试器里看到ZF、PF、SF这些缩写时,完全不明白它们有什么用——直到我在实…...
C语言文件操作实战:用fread和fwrite处理二进制数据的5个常见场景
C语言文件操作实战:用fread和fwrite处理二进制数据的5个常见场景 在嵌入式系统开发、游戏编程和工业控制等领域,二进制文件操作往往是数据持久化的核心手段。与文本文件相比,二进制格式能更精确地保存内存数据布局,避免字符编码转…...
ADRV9009+ZCU102实战:从HDL工程构建到no-OS移植的5个关键步骤
ADRV9009ZCU102全流程开发指南:从HDL工程构建到no-OS移植的深度实践 在射频系统开发领域,ADRV9009作为一款高性能射频收发器,与Xilinx ZCU102开发板的组合已成为许多硬件工程师的首选方案。本文将深入剖析五个关键环节的技术细节,…...
LrcHelper:网易云音乐双语歌词下载与设备适配完整指南
LrcHelper:网易云音乐双语歌词下载与设备适配完整指南 【免费下载链接】LrcHelper 从网易云音乐下载带翻译的歌词 Walkman 适配 项目地址: https://gitcode.com/gh_mirrors/lr/LrcHelper 你是否经常遇到喜欢的歌曲没有歌词,或者歌词与音乐不同步的…...
CAN总线大数据传输的解决方案
CAN总线通讯最多传输8个字节,如果需要传输大量数据该怎么办呢?这个问题工业界有很多成熟的解决方案,我现在就来详细为你介绍各种处理方法。 一、CAN协议的限制原因 CAN帧的数据场限制为8字节,主要是为了保证: • 实时性…...
FUTURE POLICE新手入门:无需代码基础,快速实现语音转字幕精准对齐
FUTURE POLICE新手入门:无需代码基础,快速实现语音转字幕精准对齐 你是不是也遇到过这样的烦恼?辛辛苦苦给视频配好了字幕,结果播放时总是对不上口型,要么字幕快了,要么慢了,来回调整时间轴&am…...
本地部署Qwen3大模型+OpenClaw接入实战教程:从零实现私有化AI助手
> **标签**: AI开发,大模型,Ollama,OpenClaw,Python,本地部署 > **阅读时间**: 约15分钟 > **难度**: 中级## 一、引言本地部署大模型可确保**数据不出境、不上云**,满足金融、医疗等行业的合规要求;同时长期使用成本更低,适合高频…...
RVC模型计算机组成原理视角:理解AI推理的硬件底层
RVC模型计算机组成原理视角:理解AI推理的硬件底层 你是不是觉得AI模型推理就像一个黑盒子?输入一段音频,点一下按钮,等一会儿,就得到了变声后的结果。整个过程看似简单,但背后却是一场在GPU硬件上精密上演…...
