本节大纲如下:
画板
从用例图中我们能看出来商品模块主要由两大类功能用例组成:
商品分类的相关功能
商品信息的相关功能
图片
图片
本节我们先来实现商品分类在商城应用中的常用功能。
商品分类作为商品的主要导购信息,在手机应用和PC网页应用上的页面优先级并不一样。手机因为可显示内容比PC少,所以首页上显示的内容大部分都是购买率高的推荐分类、爆款单品、以及各种在做的促销活动。
所以商品分类在App的首页上并不会全部展示给用户,而是像下面这样,在产品中留一个导航入口,进入二级页面在进行所有商品分类的展示。
图片
而在PC网页上因为页面显示区域的变大,一般在首页上就有一个商品分类导航,通过多级联动来展示所有的商品分类。
图片
所以服务端的API返回的分类数据的结构,要能适应商品分类在手机App和PC网页上的展示,一般商品分类都是采用三级分类的结构进行组织和维护(层级如果再多维护分类的打工人也头疼),以分类树的形式返回给客户端。
商品分类在C端接口中不涉及添加删除和更新的操作,这些工作统一放在管理后台由公司的工作人员来维护。一般管理后台的用户跟C端的用户是隔离开的,登录也不是用我们之前做的用户登录和Token认证,更多的是单独维护一套管理员用户。
现在人员规模多点的中型公司,基本上管理后台都是跟自己的员工的统一账号打通,使用LDAP登录认证,这样员工离职了工号一禁用,所有的后台就都不能登录了,现在使用钉钉和企业微信的公司都能基于它们自己搭建LDAP服务。
我们这里就不再费力去开发管理后台的接口和相关认证了,关于商品分类数据的创建,我们通过一个测试接口把商品分类数据初始化好,这里我为大家准备好了一份商品分类数据的JSON文件。
一共有97个分类,基本上电商App上常见的分类都有涵盖,文件我放在了项目的资源目录resources中,名称为category_init_data.json
图片
resources 这个目录我们在搭框架的时候说过是准备放静态资源文件的,之前创建了一直没用过,今天正好把它用起来。
不过大家别忘了,Go 程序打包的时候只会打.go文件,这种静态资源文件需要我们使用 go embed 功能才能打包到二进制文件里去,才能保证把Go项目的运行文件放在哪它都能读取到资源文件。
所以针对resources目录我们在其中创建一个loader.go
//go:embed * var f embed.FS func LoadResourceFile(filePath string) (io.Reader, error) { _bytes, err := f.ReadFile(filePath) if err != nil { return nil, err } return bytes.NewReader(_bytes), nil }
在loader.go中我们通过embed *载入当前目录下的所有文件。并提供一个LoadResourceFile方法,让项目的程序能读取指定的资源文件
针对我们刚才说的商品分类初始数据JSON文件,我们在项目中通过
resources.LoadResourceFile("category_init_data.json")
就能把JSON数据加载到内存中去。稍后我们会再写个初始化程序就能把这些数据初始化到MySQL的表里。
商品分类信息有了,接下来就该考虑怎么把他们查出来了。PC 网页版的购物网站首页都有一个按层级划分的分类导航,这个功能需要我们把商品的分类信息按层级组织好后返回给客户端,接下来我们就来开发这个功能。
按层级划分的分类信息该怎么实现呢?最简单的实现方式就是先把所有一级分类查出来,遍历它们然后再把各自下属的二级分类查出来,同时二级分类也是这个逻辑:遍历-每次查下属的三级分类。
这么做当然可以,就是有点费数据库,相当于一个获取所有商品分类的接口有可能要查几十次数据库才能组织好数据。当然还有另外一种方法:先把分类数据查出来,在程序内存中靠排序、临时变量等方式加工好要返回的数据。
那这里我们就采用第二种方法来实现啦,无论用哪种实现方式其实严格意义上来说都没问题,因为像层级分类这种变更不太频繁但是结构复杂的数据一般我们会在缓存中缓存一份数据,避免每次都让程序去查数据库。
开始写逻辑前,我们先在 api/reply/commodity.go 中定义好响应的结构
type HierarchicCommodityCategory struct { ID int64 `json:"id"` Level int `json:"level"` ParentId int64 `json:"parent_id"` Name string `json:"name"` IconImg string `json:"icon_img"` Rank int `json:"rank"` SubCategories []*HierarchicCommodityCategory `json:"sub_categories"` // 分类的子分类 }
我们的接口会返回[]*HierarchicCommodityCategory类型的列表,即以一级分类为元素的列表,每个分类的SubCategories中会包含子分类数据,这个字段的类型也是[]*HierarchicCommodityCategory,这样就能满足我们返回层级分类的要求啦,即使是有四、五级分类用这个结构也没问题。
一般商品的分类最多也就三级,再多就有点反人类了,维护起来也不好维护。
这里说一下命名的问题:Hierarchic 是形容词意思是按层级划分的,Hierarchy 是名词意思是等级、层级,所以在分类层级相关的技术文档里这两个单词的出现频率比较高。
用这个响应结构最终我们会构造出下面这样的按层划分好的分类数据,前端拿到这样数据后直接使用即可,不需要做更多逻辑处理。
图片
好了接下来我们来看生成商品分类层级信息的在领域服务中的主要逻辑:就能看到按层级划分好的所有商品分类信息。