Skip to content

Latest commit

 

History

History
452 lines (321 loc) · 17.6 KB

2.md

File metadata and controls

452 lines (321 loc) · 17.6 KB

2 litemall基础系统

目前litemall基础系统主要由litemall数据库、litemall-db模块和litemall-os-api模块组成。

目前存在的问题:

  • 严重数据库采用git,每次跟新都是5MB数据,影响项目下载速度
  • 缺失litemall-os-api返回的图片不能在浏览器直接显示
  • 改善 litemall-db的一些CRUD操作可以基于开源库重构
  • 功能可以参考一些云存储服务的API加强一些功能

2.1 litemall.sql

litemall.sql数据库基于nideshop中的nideshop.sql数据库,然后在实际开发过程中进行了调整和修改:

  • 删除了一些目前不必要的表;
  • 删除了表中一些目前不必要的字段;
  • 行政区域数据litemall_region没有采用原nideshop中的数据,而是采用了Administrative-divisions-of-China
  • 表中的某些字段采用JSON;
  • 表中的日期或时间字段采用DATE、DATETIME;
  • 字段的数据类型粗粒度化,例如避免MEDIUMINT,而是INT;
  • 表的数据做了清理、调整和补充(假数据)。

具体不同可以比较litemall-db模块下sql文件夹中nideshop.sql和litemall.sql。

以下讨论一些关键性设计

注意:

以下设计基于个人理解,很可能存在不合理或者与实际系统不符合的地方。

2.1.1 商品和货品设计

这里商品存在商品,商品属性,商品规格,货品四种表

商品表是一种商品的基本信息,主要包括商品介绍,商品图片,商品所属类目,商品品牌商等;

商品参数表其实也是商品的基本信息,但是由于是一对多关系,因此不能直接保存在商品表中(虽然采用JSON也可以但是不合理), 因此采用独立的商品参数表,通常是商品的一些公共基本商品参数;

商品规格表是商品进一步区分货品的标识,例如同样一款衣服,基本信息一致,基本属性一致,但是在尺寸这个属性上可以 把衣服区分成多个货品,而且造成对应的数量和价格不一致。商品规格可以看着是商品属性,但具有特殊特征。

商品规格和规格值存在以下几种关系:

  • 单一规格和单一规格值,最常见的,即当前商品存在一种货品;
  • 单一规格和多个规格值,较常见,即当前商品基于某个规格存在多种货品,通常价格都是相同的,当然也可能不相同;
  • 多个规格和单一规格值,可以简化成第一种情况,或者采用第四种情况,通常实际情况下不常见;
  • 多个规格和多个规格值,通常是两种规格或者三种规格较为常见,而且对应的价格不完全相同。

货品则是最终面向开发者购买的商品标识,存在多个规格值、数量和价格。

因此这里一个商品表项,存在(至少0个)多个商品属性表项目,存在(至少一个)多个商品规格表项, 存在(至少一个)多个货品表项。

举例如下:

  • 一个商品“2018春季衣服商品编号1111111”,
  • 存在两个商品参数,
    • 属性名称“面向人群”,属性值“男士”
    • 属性名称“面料”,属性值“100%棉”
  • 存在两种规格共八个商品规格项,
    • 规格名称“尺寸”,规则值“S”
    • 规格名称“尺寸”,规则值“M”
    • 规格名称“尺寸”,规则值“L”
    • 规格名称“尺寸”,规则值“XL”
    • 规格名称“尺寸”,规则值“XXL”
    • 规格名称“颜色”,规格值“蓝色”
    • 规格名称“颜色”,规格值“灰色”
    • 规格名称“颜色”,规格值“黑色”
  • 存在15个货品(尺寸*颜色=15个货品)
    • 货品“S蓝”,数量 100, 价格 100
    • 货品“M蓝”,数量 100, 价格 100
    • 货品“L蓝”,数量 100, 价格 100
    • 货品“XL蓝”,数量 100, 价格 100
    • 货品“XXL蓝”,数量 100, 价格 100
    • 货品“S灰”,数量 100, 价格 100
    • 货品“M灰”,数量 100, 价格 100
    • 货品“L灰”,数量 100, 价格 100
    • 货品“XL灰”,数量 100, 价格 100
    • 货品“XXL灰”,数量 100, 价格 100
    • 货品“S黑”,数量 100, 价格 100
    • 货品“M黑”,数量 100, 价格 100
    • 货品“L黑”,数量 100, 价格 100
    • 货品“XL黑”,数量 0, 价格 100
    • 货品“XXL黑”,数量 0, 价格 100

以下是一些细节的讨论:

  • 商品表中可能存在数量和价格属性,而货品中也存在数量和价格属性,目前设计这样:
    • 商品表的价格应该和某个货品的价格一样,通常应该是所有货品价格的最小值,或者基本款式的价格;
    • 商品表中的数量和价格应该仅用于展示,而不能用于最终的订单价格计算;
    • 商品表的数量应该设置成所有货品数量的总和;
    • 在管理后台添加商品时,如果管理员不填写商品表的数量和价格属性,则自动填写合适的值;如果填写,则使用显示。
    • 当小商城中,用户查看商品详情时,初始显示商品表的价格,而如果用户选择具体规格后,则商品 详情里面的价格需要自动切换到该规格的价格。
  • 商品规格可以存在规格图片,效果是规格名称前放置规格图片
  • 货品也可以存在货品图片,效果是所有规格选定以后对应的货品有货,则在货品价格前放置货品图片
  • 如果商品是两种规格,分别是M个和N个规格值,那么通常应该是M*N个货品,但是有些货品可能天然不存在。 那么,此时数据库如何来设计,是允许少于M*N个项,还是必须等于M*N个,而不存在货品的数量设置为0? *

注意:

这里的设计可能与实际项目设计不一致,但是目前是可行的。 商品的中文用语“商品”和英语用语“goods”,货品的中文用语“货品”和英语用语“product”可能是不正确的。

2.1.2 用户和微信用户设计

目前准备支持用户普通账号登录和微信登录两种方式,两种登录方式仅仅采用一个litemall-user表可能不是很合适。此外,如果进一步支持其他多种第三方登录,那么这里需要重新设计。

2.1.3 行政区域设计

原nideship.sql中存在region数据,但是litemall.sql的region数据则来自 Administrative-divisions-of-China项目。从该项目中导入数据到litemall.sql的litemall-province、litemall-city、litemall-area和litemall-street四个表,然后重新生成一个新的litemall-region表。

2.1.4 订单设计

订单信息主要由基本信息、商品信息、地址信息、费用信息、快递信息、支付信息和其他信息组成。

  • 基本信息 订单创建时的一些基本信息。

  • 商品信息 由于订单可以存在多个商品,因此订单的商品信息是由独立的订单商品表记录(可能更应该称为货品)。

  • 费用信息

  • 快递信息 目前快递信息仅仅记录快递公司、快递单号、快递发出时间、快递接收时间。而如果快递过程中如果存在一些异常,例如物品丢失,则目前系统难以处理。关于快递费的计算,目前采取简单方式,即满88元则免费,否则10元。

  • 支付信息

  • 其他信息

2.1.4.1 订单状态

订单分成几种基本的状态:

  • 下单 状态码101,此时订单生成,记录订单编号、收货地址信息、订单商品信息和订单相关费用信息;
  • 付款 状态码201,此时用户微信支付付款,系统记录微信支付订单号、支付时间、支付状态;
  • 发货 状态码301,此时商场已经发货,系统记录快递公司、快递单号、快递发送时间。 当快递公司反馈用户签收后,系统记录快递到达时间。
  • 收货 状态码401,当用户收到货以后点击确认收货,系统记录确认时间。 此时,用户可以评价订单商品。

除了这几种正常状态以外,还存在一些非普通的状态:

  • 订单取消 状态码102,用户在生产订单以后未付款之前,点击取消按钮,系统记录结束时间
  • 订单取消并退款 状态码202,用户付款以后未发货前,点击取消按钮,系统记录结束时间和退款信息
  • 系统自动确认收货 状态码402,快递反馈商场用户已签收,但是用户却不点击确认收货按钮, 此时系统在快递到达时间的一段时间后,自动确认收货。 用户不能再点击确认收货按钮,但是可以评价订单商品

当然,以上的基本状态和非普通状态,和实际项目相比仍然相对简单。

此外,当订单状态码是102、202、401、402时,订单可以设置删除状态,此时 用户查看自己订单信息时将看不到这些“已删除”的订单。

2.1.4.2 状态码所支持的操作

不同的状态码下面,用户能够进行的操作是:

  • 101 此时,用户可以“订单支付”、“订单取消”
  • 102 此时,用户可以“订单删除”
  • 201 此时,用户可以“订单取消”(并退款)
  • 202 此时,用户可以“订单删除”
  • 301 此时,用户可以“确认收货”
  • 401 此时,用户可以“订单删除”、“评价”、“再次购买”
  • 402 此时,用户可以“订单删除”、“评价”、“再次购买”

2.1.4.3 售后处理

目前不支持售后或退货相关业务。

2.1.4.4 黑名单

从一些资料看,如果用户订单多次取消,应该加入黑名单。 目前不支持。

2.1.5 数据删除

所有表的数据在代码层面都不支持删除,除非数据库管理员连接到数据库删除数据。

在访问层,数据库的删除操作

2.2 litemall-db

技术:

  • Spring Boot 1.5.10
  • MySQL
  • Druid
  • Mybatis
  • PageHelper
  • Mybatis Generator
  • Mybatis Generator非官方插件mybatis-generator-plugin

因为litemall-db是一个业务模块,并不对外直接服务,因此无需使用Spring MVC。

这里litemall-db模块可以分成以下几种代码:

  • mybatis generator自动化代码
  • 业务代码
  • 安全代码
  • JSON支持代码
  • 配置代码

2.2.1 自动化代码

如上图所示,双击mybatis-generator:generate,则mybatis generator插件会:

  1. 读取mybatis-generator文件夹下的generatorConfig.xml文件
  2. 根据jdbcConnection访问数据库
  3. 根据table, 自动生成三种代码:
    • src文件夹org.linlinjava.litemall.db.domain 包内的Java代码
    • src文件夹org.linlinjava.litemall.db.domain 包内的Java代码
    • resources文件夹org.linlinjava.litemall.db.domain.dao 内的XML文件

以上三种代码即可封装对数据库的操作,开发者无需直接操作sql代码, 而是直接操作Java代码来完成对数据库的访问处理。

关于如何基于mybatis的Example代码来访问数据库,请查阅相关资料, 或者参考本模块org.linlinjava.litemall.db.dservice 包内的Java代码。

当然,为了达到数据库访问效率,开发者也可以手动自定义mapper文件和对应的Java代码,但目前这里不采用或者不建议采用。 例如,当需要访问两个表的数据时,这里是在业务层通过Java代码遍历的形式来访问两个表。

这里,以litemall_brand表举例说明:

  1. mybatis generator插件会根据数据库table标签

    <generatorConfiguration>
         <table tableName="litemall_brand">
             <generatedKey column="id" sqlStatement="MySql" identity="true" />
             <columnOverride javaType="java.time.LocalDateTime" column="add_time"/>
         </table>
    </generatorConfiguration>
    
  2. 自动生产src文件夹下domain包内的LitemallBrand.java类、LitemallBrandExample.java类、 dao包内的LitemallBrandMapper.java接口和resources文件夹下dao包内的LitemallBrandMapper.xml文件。

  3. 手动在service包内创建LitemallBrandService.java来对外提供具体的服务。 例如,为了得到Brand列表,那么创建list方法,基于前面创建的三个Java来来实现。

     @Service
     public class LitemallBrandService {
        @Resource
        private LitemallBrandMapper brandMapper;
    
         public List<LitemallBrand> query(int offset, int limit) {
            LitemallBrandExample example = new LitemallBrandExample();
            example.or().andDeletedEqualTo(false);
            PageHelper.startPage(offset, limit);
            return brandMapper.selectByExample(example);    
         }
     }

如果基于一个新表创建新访问组件,请阅读下面章节2.2.6

2.2.2 业务代码

基于2.2.1的代码,业务代码处理一些具体业务相关的操作,对其他模块提供具体的服务。

2.2.3 安全代码

2.2.4 JSON支持代码

2.2.5 配置代码

采用Java注解的方式来完成一些特定的配置操作。

2.2.6 新服务组件

本节介绍如果基于一个表创建新的服务组件。

  1. 在数据库里面创建一个表,例如litemall_demo:

    CREATE TABLE `litemall`.`litemall_demo` (
      `id` INT NOT NULL AUTO_INCREMENT,
      `name` VARCHAR(45) NULL,
      `address` VARCHAR(45) NULL,
      PRIMARY KEY (`id`));
      
    INSERT INTO `litemall`.`litemall_demo` (`id`, `name`, `address`) 
    VALUES ('1', 'hello', 'world');
  2. 在generatorConfig.xml中增加一个新的table标签

    <generatorConfiguration>
         <table tableName="litemall_demo">
             <generatedKey column="id" sqlStatement="MySql" identity="true" />
         </table>
    </generatorConfiguration>
    
  3. 双击mybatis generator插件,检查LitemallDemo.java类、LitemallDemoExample.java类、 LitemallDemoMapper.java接口和LitemallDemoMapper.xml是否生产。

  4. 在service里面新建LitemallDemoService.java类,

     @Service
     public class LitemallDemoService {
        @Resource
        private LitemallDemoMapper demoMapper;
    
         public List<LitemallDemo> list() {
            LitemallDemoExample example = new LitemallDemoExample();
            return demoMapper.selectByExample(example);    
         }
     }
  5. 可以在src/test/java/org.linlinjava.litemall.db包里面创建LitemallDemoTest.java类, 使用Junit进行测试。

    @WebAppConfiguration
    @RunWith(SpringJUnit4ClassRunner.class)
    @SpringBootTest
    public class LitemallDemoTest {    
       @Autowired
       private LitemallDemoService demoService;
    
       @Test
       public void test() {    
        List<LitemallDemo> litemallDemoList = demoService.list();
        Assert.assertTrue(litemallDemoList.size() != 0);
       }
    
    }
  6. 同样地,可以在Controller中使用LitemallDemoService来对外提供服务。

    @RestController
    @RequestMapping("/demo")
    public class DemoController {
       @Autowired
       private LitemallDemoService demoService;
    
       @RequestMapping("/list")
       public Object list(){    
           List<LitemallDemo> demoList = demoService.list();   
           return demoList;
       }
    }

2.3 litemall-core

2.4 litemall-os-api

对象存储服务目前的目标是支持图片的上传下载。

作为后台模块之一,litemall-os-api并没有对应的前端模块,而只是在litemall-admin模块 的对象存储页面中允许管理员修改。

注意:

这个模块是可选的,或者说不建议最终部署时所使用。 最终部署时建议采用第三方云存储方案。

2.4.1 业务

支持服务:

  • 列表
  • 创建
  • 修改
  • 读取
  • 删除
  • 下载,即下载对象数据文件
  • 访问,即直接访问对象数据

2.4.2 安全

警告

目前这里没有任何安全机制,这意味着任何人如果知道对象存储服务的地址,都可以直接存储访问对象数据。

这样简化的目的是对象存储服务建议最终采用云服务,因此这里仅仅实现一个简单的服务面向测试开发。

如果开发者需要局域网部署,那么这里需要加入一定的安全机制。

2.4.3 文件Key

每一个上传的文件都会采用一个随机值key,作为当前文件的网络访问链接的一部分。

以后可能需要进一步支持自定义Key,例如采用原文件名字作为key。

2.5 litemall-all

在章节1.5中讨论的部署方案中设计了一种单主机单服务方案, 也就是说三个后台服务和静态文件都部署在一个Spring Boot应用中。

注意:

这个模块也是可选的,或者说不是非常建议的,应该仅用在主机内存资源紧张的情况下。 最终部署,仍然建议部署多个服务更为安全和稳定。

查看litemall-all模块,代码仅仅只有一个Application类。

实际的原理是litemall-all模块内的pom.xml文件:

  1. 声明打包方式是war,因此最后会打包war格式

  2. 设置spring-boot-starter-tomcat包是provided,因此最终不会打包

  3. 申明依赖litemall-os-apilitemall-wx-apilitemall-admin-api, 因此最终会打包;

    在Application类里面通过scanBasePackages即可把三个后台服务模块 的服务启动。此外在tomcat中启动,需要采用继承SpringBootServletInitializer类 的Application。

  4. 利用maven-resources-plugin插件,将litemall-admin模块下编译得到dist文件下的 静态文件打包到static目录中。

    注意,这里只是简单的复制。因此开发者需要保证litemall-all打包前,litemall-admin 模块内dist目录下静态文件已经生成。

最终打包以后则是一个war格式的项目包,包含了三个后台服务和静态文件。