电商项目需求分析
电商项目概述
电商业务离不开我们的生活。
设计一个最小化的电商系统,理清电商系统的架构,对电商系统的业务逻辑、系统架构、核心业务流程有一个基本的认知,这个直接用技术实现不就OK了?所以嘛,不仅要深耕上述的技术层面,还要学习和掌握什么是电商的业务需求和系统。(技术+业务)
电商类型
- B2C:面向消费者销售产品和服务商业零售模式 (小米、华为)
- B2B:企业与企业间通过互联网展开的交易活动商业模式 (1688)
- B2B2C:B2B、B2C模式结合体 (天猫、京东、唯品会、苏宁易购)
- C2C:个人与个人之间的电子商务 (淘宝网、咸鱼)
- O2O:线下商家与互联网称为线下交易平台 (美团、饿了吗)
- B2G:企业与政府机构的电子商务 (中国政府采购网)
运营方向
- 传统电商: 淘宝天猫、京东、唯品会、聚美优品、当当
- 社交电商: 拼多多、京东、国美、苏宁
- 网红电商: 抖音、快手
- 内容电商: 头条三农领域
电商术语
- PV: Page view,即网站被浏览的总次数
- UV: Unique Vister的缩写,独立访客
- CR: ConversionRate的缩写,是指访问某一网站访客中,转化的访客占全访客的比例(订单转化率=有效订单数/访客数)
- SPU: Standard Product Unit (标准化产品单元),SPU是商品信息聚合的最小单位,是一组可复用、易检索的标准化信息的集合,该集合描述了一个产品的特性。 例如:华为手机中的处理器、电池等等。
- SKU是物理上不可分割的最小存货单元SKU即库存进出计量的单位(买家购买、商家进货、供应商备货、工厂生产都是依据SKU进行的),在服装、鞋类商品中使用最多最普遍。
例如纺织品中一个SKU通常表示:规格、颜色、款式。SKU是物理上不可分割的最小存货单元
一个最小化的电商系统,也依然是非常复杂的,需求永远不明确,永远在变化,唯一不变的只有变化。应该适应变化,并且拥抱变化。在需求还不太明确的情况下,比较可行的方案:首先搭建不太会发生变化的核心系统,然后尽量简单地实现一个最小化的系统,后续再逐步迭代和完善
设计电商系统的核心流程
从需求阶段开始,需求分析的工作还不是由开发者完成的真的是,因为很多项目交付以后,仍需要不断地进行修改和变更,用户不满意,开发者也很痛苦,背后的原因是缺失了需求分析的步骤,所以应该掌握一些用于需求分析的方法。
先明确下面两个点(业务需求)
- 这个系统(或者功能)是给哪些人用的
- 首先是买东西的人,即“用户”;其次是卖东西的人,即“运营”;还有一个非常重要的角色就是出钱的人,即“管理者”(请记住,在设计任何一个系统的时候,出钱的人的意见都是重要的,甚至可以说是最重要的)
- 这些人使用这个系统是为了解决什么问题
- 用户为了买东西,运营为了卖东西,管理者需要通过系统了解自己所得的收益。
电商系统用例图如下:
做业务需求的主要目的是理清楚业务场景是怎样的。
电商系统的业务流程:从用户浏览商品 -> 将心仪的商品加入购物车 -> 提交订单 -> 确认订单(计算订单中商品总价格…)-> 用户支付 -> 支付成功后运营人员为支付状态为成功的订单发货,邮寄给用户 -> 用户收到货后并确认收货
- 用户为了买东西,运营为了卖东西,管理者需要通过系统了解自己所得的收益。
根据核心流程划分模块
对上述电商业务流程做细化成时序图(这里时序图并不是很详细哦,只是根据上述流程细化一点罢了)
- 用户浏览商品,这个步骤需要通过一个商品模块来展示商品详情页,用户可以从中获取所浏览商品的详细介绍和价格等信息
- 用户把选好的商品加入购物车,这个步骤需要使用一个购物车模块来维护用户购物车中的商品
- 用户下单,这个步骤需要基于一个订单模块来创建新订单。订单创建好了之后,系统需要把订单中的商品从购物车中删减掉
- 订单创建完成后,系统需要引导用户付款,即发起支付流程,可通过一个支付模块来实现支付功能,用户成功完成支付之后,系统需要把订单的状态变更为“已支付”
- 成功支付之后,运营人员就可以发货了,发货之后,系统需要扣减对应商品的库存数量,这个步骤需要基于一个库存模块来实现库存数量的变更,同时系统还需要把订单状态变更为“已发货”
- 用户收到商品,在系统中确认收货,系统需要把订单状态变更为“已收货”。
所以,电商项目的购物业务的核心功能模块是:商品、购物车、订单、支付和库存
管理员的查看报表,运营人员的进货出货这些没有覆盖到,这里后续我扩展下
- 商品:维护和展示商品的相关信息
- 订单:维护订单信息和订单状态,计算订单金额。
- 购物车:维护用户购物车中商品的信息。
- 支付:负责与系统内外部的支付渠道对接,实现支付功能。
- 库存:维护商品的库存信息。
- 促销:制定促销规则,计算促销优惠信息。
- 用户:维护系统的用户信息,注意,用户模块是一个业务模块,一般不负责用户的登录和认证,这是两个完全不同的功能。
- 账户:账户模块负责维护用户的账户信息,例如用户的积分、等级、余额。
- 搜索推荐:提供商品搜索功能,并负责各种商品列表页和促销页的组织和展示,搜索推荐决定用户优先看到哪些商品。
- 报表:实现数据统计和分析功能,生成报表,为管理者进行经营分析和决策提供数据信息
在实际的电商网站中,促销模块是电商系统中最复杂的一个模块。各种优惠券、满减、返现等促销规则,每一条都非常复杂,再加上这些规则往往还要叠加计算,有时甚至会复杂到连制定促销规则的人都算不清楚。在实际的运营中,所有电商公司几乎都发送过因为促销规则制定失误,导致商品实际售价远低于成本价,使公司受到一定程度的损失。尽管如此,五花八门的促销活动依然是提升销量最有效的手段,因此需要充分利用。
作为电商系统的设计者,需要把促销规则的变化和复杂性控制在促销模块内部,不能因为一个促销模块而导致整个电商系统都变得非常复杂,否则设计和实现将会很难。所以,可以把促销模块与其他模块的接口设计得相对简单和固定,这样系统的其他模块就不会因为新的促销规则改变而随之进行改变。销模块返回一个可以使用的促销列表,用户选择对应的促销和优惠,订单模块把商品、价格、促销优惠等信息,再次传给促销模块,促销模块再返回促销之后的价格。在最终生成的订单中,系统只需要记录订单使用了哪几种促销规则,以及最终的促销价格就可以了。这样,无论促销模块如何变化,订单和其他模块的业务逻辑都不需要随之改变。
总结电商系统设计核心要点
首先,电商系统面向的角色是:用户、运营人员和管理者。这三个角色对电商系统的需求是:用户通过系统来购物,运营人员负责商品的销售,管理者关注系统中的经营数据。电商系统最核心的流程是用户购物的流程,购物流程从用户浏览选购商品开始,加购、下单、支付、运营人员发货、用户确认收货,至此电商系统的购物流程结束。
细化这个流程之后,我们可以分析出支撑这个流程的核心功能模块:商品、订单、购物车、支付和库存。除此之外,一个完整的电商系统还包括促销、用户、账户、搜索推荐和报表这些必备的功能模块。
作为一名开发者,在做需求分析的时候,需要把握的一个要点是:不要一上来就设计功能,而是要先理清业务需求。
如果系统业务是复杂而多变的,那么请尽量识别出这部分复杂业务的边界,将复杂业务控制在一个模块内部,从而避免将这种复杂度扩散到整个系统中去。
项目架构图
项目环境
代码管理和项目发布
- GitLab
- Jenkins
- Nexus
中间件和基础设施
- MySQL
- Redis
- ELK
- RocketMQ
- MongoDB
- Canal
- Promethus
- Grafana
- SpingCloudAlibaba系列
项目运行所需最小环境
- MySQL: 8.0
- Nacos: 2.1.0
- RocketMQ: 4.9.4
- ElasticSearch: 7.6.1
- Redis集群: 5.0
项目中将学习的技术问题(重点技术问题)
- 分库分表、读写分离
- 分布式事务
- 全局唯一性ID
- 分布式Session
- 分布式链路跟踪
- 日志收集与展示
- 商品搜索
- 分布式锁
- 服务降级/限流/熔断/隔离
- 页面静态化
- 分布式任务调度
- 数据迁移方案
- 数据同步方案
- 多级缓存、缓存预热
- 高并发秒杀系统实现(秒杀系统的商详页静态化、秒杀系统的隔离、秒杀的削峰和限流等等)
项目学习必备知识
- 并发编程
- SpringBoot + Mybatis + MySQL 下的WEB应用开发
- 普通基础商城项目单体版
- 分布式基本概念
- 微服务的基本概念
- 事务一致性概念、多副本一致概念和CAP理论
- SpingCloudAlibaba系列组件(Nacos、Seata等等)的作用和基本使用
- Redis各种数据结构和操作、Redis主从和Redis Cluster基本概念
- Lua语言的基础语法
- MongoDB基本概念和操作、MongoDB集群基本概念
- 消息中间件的基本概念和RocketMQ的基本操作、RocketMQ集群基本概念
- ELK的基本概念和基本使用
- JavaScript基础语法
- Linux的基本概念和操作
- Nginx基本概念和操作
- Git的基本操作和Maven基本配置方法和常用命令
后台管理项目多数据源管理方案
整个电商管理后台本质上是一个访问频率比较低的CRUD管理系统,所以本身不需要考虑微服务拆分的事情。接入微服务体系也只是为了能够调用其他的微服务。为了简化业务流程,在做后端管理系统时,并没有严格按照微服务原则进行数据隔离,而是让管理系统直接访问所有的业务数据,那么需要让管理系统可以同时管理多个数据库的数据哦。
电商后台项目需要访问的数据源说明
整个电商项目的数据库设计情况:
这些不同库中的很多基础数据,除了购物车模块外,都需要由电商管理后台进行统一管理。所以,对于电商管理系统,会采用多数据源管理的方式,尽量快速的完成基础数据维护。其中,订单库由于进行了分库分表,管理比较复杂,所以电商管理后台不会直接访问订单相关的表,而是通过微服务的方式调用订单模块的相关功能来间接管理订单。
电商后台使用MyBatis-plus快速访问多个数据源
电商后台项目使用的MyBatis-plus框架访问数据库。这里设计的电商项目管理数据的方式是直接粗暴的,直接跨多个数据库管理的后台数据。
使用Spring提供的AbstractRoutingDataSource
这种方式的核心是使用Spring提供的AbstractRoutingDataSource抽象类,注入多个数据源。
AbstractRoutingDataSource 是 Spring 提供的一个抽象类,用于实现动态数据源路由的功能。它允许在运行时根据某些条件(比如当前线程的上下文)决定使用哪个具体的数据源。
以下组件的逻辑通常用于实现读写分离(一个写库和多个读库),以提升数据库性能。
@Component // 这个类标记为 Spring 的组件,Spring 容器会自动扫描并创建其 Bean 实例
@Primary // 将该Bean设置为主要注入Bean,当有多个 DataSource Bean 存在时,Spring 默认会注入被标记为 @Primary 的那个 Bean
public class DynamicDataSource extends AbstractRoutingDataSource {
// 用来保存当前线程使用的数据源标识
public static ThreadLocal<String> name=new ThreadLocal<>();
// 数据源注入,注入两个实际的数据源对象,分别代表写库(dataSource1)和读库(dataSource2)
// 写库
@Autowired
DataSource dataSource1;
// 读库
@Autowired
DataSource dataSource2;
// 返回当前数据源标识(返回“W”或“R”) (AbstractRoutingDataSource 的核心方法,用于决定当前线程应该使用哪个数据源)
@Override
protected Object determineCurrentLookupKey() {
return name.get();
}
/**
* 实现afterPropertiesSet()方法,返回当前应使用的数据源标识符
*/
@Override
public void afterPropertiesSet() {
// 为targetDataSources初始化所有数据源
// 创建一个 Map<Object, Object>,键是数据源标识(如 "W"),值是实际的 DataSource 对象
Map<Object, Object> targetDataSources=new HashMap<>();
// 将写库和读库放入这个 Map 中
targetDataSources.put("W",dataSource1);
targetDataSources.put("R",dataSource2);
// 调用 setTargetDataSources() 设置这些数据源
super.setTargetDataSources(targetDataSources);
// 为defaultTargetDataSource 设置默认的数据源
super.setDefaultTargetDataSource(dataSource1);
// 调用父类的 afterPropertiesSet() 完成初始化
super.afterPropertiesSet();
}
}
将自己实现的DynamicDataSource注册成为默认的DataSource实例后,只需要在每次使用DataSource时,提前改变一下其中的name标识,就可以快速切换数据源。(注解+AOP)
@Component
@Aspect
public class DynamicDataSourceAspect implements Ordered {
// 前置通知,在每个访问数据库的方法执行前执行。
@Before("within(com.yxx.dynamic.datasource.service.impl.*) && @annotation(wr)")
public void before(JoinPoint point, WR wr){
String name = wr.value();
DynamicDataSource.name.set(name);
System.out.println(name);
}
@Override
public int getOrder() {
return 0;
}
}
自定义两个数据源的配置
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
datasource1:
url: jdbc:mysql://127.0.0.1:3306/datasource1?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF8&useSSL=false
username: root
password: root
initial-size: 1
min-idle: 1
max-active