单体电商项目(单体架构->分布式架构)

前后端的代码都在GitHub上,https://github.com/xiguanlezz/E-Commerce


SpringBoot + Swagger接口文档 + tk-mybatis持久层框架 + FastDFS分布式文件系统 + Thymeleaf模板引擎 + 支付宝API


FastDFS的安装即与Spring整合的demo可以参见:https://blog.csdn.net/cj1561435010/article/details/105434066



一、tk-mybatis

       这个框架可以使用很多现成的API,简单的增删改查不需要再自己写SQL语句,和Mybatis-plus很像,安利一下,非常好使。
Alt


测试类:

	@SpringBootTest(classes = StartApplication.class)
	@RunWith(SpringRunner.class)
	public class TKMybatisApplicationTests {
	
	    @Autowired
	    private UserMapper userMapper;
	
	
	    public TKMybatisApplicationTests() {
	    }
	
	    /**
	     * 根据对象属性查询
	     */
	    @Test
	    public void testSelectOne() {
	        User user = new User();
	        user.setPassword("123456");
	        User u = userMapper.selectOne(user);
	        System.out.println(u);
	    }
	
	    /**
	     * 根据主键查询
	     */
	    @Test
	    public void testSelectByPrimaryKey() {
	        Integer employeeId = 1;
	        User u = userMapper.selectByPrimaryKey(employeeId);
	        System.out.println(u);
	    }
	
	    /**
	     * 根据主键判断数据记录是否存在
	     */
	    @Test
	    public void testExistsWithPrimaryKey() {
	        Integer userId = 13;
	        boolean b = userMapper.existsWithPrimaryKey(userId);
	        System.out.println(b);
	    }
	
	    /**
	     * 全插入
	     */
	    @Test
	    public void testInsert() {
	        User user = new User();
	        user.setUsername("xxyyx222333").setPassword("111").setRole(0).setCreateTime(LocalDateTime.now()).setUpdateTime(LocalDateTime.now());
	        userMapper.insert(user);
	        System.out.println(user.getId());
	    }
	
	    /**
	     * 部分插入
	     */
	    @Test
	    public void testInsertSelective() {
	        User user = new User();
	        user.setUsername("xxyyzz").setPassword("111").setRole(0).setCreateTime(LocalDateTime.now()).setUpdateTime(LocalDateTime.now());
	        userMapper.insertSelective(user);       //为null的属性不加入sql语句
	        System.out.println(user.getId());
	    }
	
	    /**
	     * 根据主键部分更新
	     */
	    @Test
	    public void testUpdateByPrimaryKeySelective() {
	        User user = new User();
	        user.setUsername("xxx").setRole(0).setPassword("012").setId(22);
	        userMapper.updateByPrimaryKeySelective(user);       //为null的属性不执行更新操作
	    }
	
	    /**
	     * 根据主键删除
	     */
	    @Test
	    public void testDeleteByPrimaryKey() {
	        Integer userId = 25;
	        userMapper.deleteByPrimaryKey(userId);
	    }
	
	    @Test
	    public void testSelectByExample() {
	        Example example = new Example(User.class);
	        Example.Criteria criteria01 = example.createCriteria();
	        Example.Criteria criteria02 = example.createCriteria();
	
	        example.orderBy("password").desc().orderBy("id").asc();     //设置排序规则
	        example.selectProperties("username", "password", "role");    //设置select的字段, 如果不设置默认就是SELECT *
	        example.setDistinct(true);      //设置去重
	
	        criteria01.andBetween("role", 0, 1).andEqualTo("username", "admin");    //第一个参数是实体类的属性名
	        criteria02.andLessThan("id", 10);
	        example.or(criteria02);     //拼接条件
	        List<User> users = userMapper.selectByExample(example);
	        for (User user : users) {
	            System.out.println(user);
	        }
	    }
	}



二、多层级路由的递归算法

       表的设计包含id和父id两个字段即可无限层级的嵌套。
Alt

       算法的设计思想就是先将本节点加入集合,查找出下一层子节点的信息,再遍历子节点,每个子结点都调用一次这个方法,使用Set集合进行去重,java还需要重写equals和hashCode方法,实现如何去重。

	@Override
    public ResultResponse getParallelChildrenCategory(Integer categoryId) {
        List<Category> categoryList = categoryMapper.selectCategoryChildrenByParentId(categoryId);
        if (CollectionUtils.isEmpty(categoryList)) {
            logger.info("未找到当前分类的子分类");
        }
        return ResultResponse.ok(categoryList);
    }

    @Override
    public ResultResponse getDeepChildrenCategory(Integer categoryId) {
        Set<Category> categorySet = new HashSet<>();
        this.findAllChildCategory(categorySet, categoryId);

        List<Integer> categoryIdList = new ArrayList<>();
        if (categoryId != null) {
            for (Category category : categorySet) {
                if (category != null) {
                    categoryIdList.add(category.getId());
                }
            }
        }
        return ResultResponse.ok(categoryIdList);
    }

    /**
     * 递归算法, 查询出本节点信息和所有子节点的信息
     * 方法的设计思想: 先将本节点加入集合, 在查找子节点依次调用本方法
     *
     */
    private Set<Category> findAllChildCategory(Set<Category> categorySet, Integer categoryId) {
        Category category = categoryMapper.selectByPrimaryKey(categoryId);
        if (categoryId != null) {
            categorySet.add(category);
        }
        List<Category> categoryList = (List<Category>) this.getParallelChildrenCategory(categoryId).getData();
        for (Category c : categoryList) {
            if (c != null) {
                findAllChildCategory(categorySet, c.getId());   //递归查找子节点
            }
        }
        return categorySet;
    }

       实体类如下,即对象的id不同就代表两个对象不同:

	@ApiModel("品类实体类")
	@Getter
	@Setter
	@ToString
	@Accessors(chain = true)
	@Table(name = "mmall_category")
	public class Category {
	    @ApiModelProperty(value = "类别Id", example = "100020")
	    @Id
	    @GeneratedValue(strategy = GenerationType.IDENTITY)     //返回自增的主键
	    private Integer id;
	
	    @ApiModelProperty(value = "父类别id当id=0时说明是根节点, 一级类别", example = "0")
	    @Column(name = "parent_id")
	    private Integer parentId;
	
	    @ApiModelProperty("类别名称")
	    private String name;
	
	    @ApiModelProperty("类别状态: 1-正常, 2-已废弃")
	    private Boolean status;
	
	    @ApiModelProperty(value = "排序编号, 同类展示顺序, 数值相等则自然排序", example = "1")
	    @Column(name = "sort_order")
	    private Integer sortOrder;
	
	    @ApiModelProperty("品类创建时间")
	    @Column(name = "create_time")
	    private LocalDateTime createTime;
	
	    @ApiModelProperty("品类最后更新时间")
	    @Column(name = "update_time")
	    private LocalDateTime updateTime;
	
	    @Override
	    public boolean equals(Object o) {
	        if (this == o) return true;
	        if (o == null || getClass() != o.getClass()) return false;
	        Category category = (Category) o;
	        return Objects.equals(id, category.id);
	    }
	
	    @Override
	    public int hashCode() {
	        return Objects.hash(id);
	    }
	}



三、对接支付宝沙箱

       SpringBoot中引入第三方jar包,需要进行相关配置,并且还要配置一下maven方可将第三方的jar包打包起来。

       沙箱相关的说明文档:https://open.alipay.com/platform/appDaily.htm?tab=info

       密钥生成工具的说明文档:https://opendocs.alipay.com/open/291/105971


       配置文件详解:

Alt


       调用支付宝API的流程图:

Alt



四、解决浮点数运算丢失精度的问题

       测试问题浮点数运算存在的精度问题:

	public class BigDecimalTest {
	    @Test
	    public void test01() {
	        System.out.println("-----------test01-----------");
	        System.out.println(0.05 + 0.01);
	        System.out.println(1.0 - 0.42);
	        System.out.println(4.015 * 100);
	        System.out.println(123.3 / 100);
	    }
	
	    @Test
	    public void test02() {
	        System.out.println("-----------test02-----------");
	        BigDecimal b1 = new BigDecimal(0.05);
	        BigDecimal b2 = new BigDecimal(0.01);
	        System.out.println(b1.add(b2));
	    }
	
	    @Test
	    public void test03() {
	        System.out.println("-----------test03-----------");
	        BigDecimal b1 = new BigDecimal("0.05");
	        BigDecimal b2 = new BigDecimal("0.01");
	        System.out.println(b1.add(b2));
	    }
	
	    @Test
	    public void test() {
	        test01();
	        test02();
	        test03();
	    }
	}

Alt


       使用BigDecimal传入String的构造器可以解决浮点数丢失精度的问题。下面封装了一个工具类:

	public class BigDecimalUtil {
	    private BigDecimalUtil() {
	    }
	
	    public static BigDecimal add(double v1, double v2) {
	        BigDecimal b1 = new BigDecimal(Double.toString(v1));
	        BigDecimal b2 = new BigDecimal(Double.toString(v2));
	        return b1.add(b2);
	    }
	
	    public static BigDecimal sub(double v1, double v2) {
	        BigDecimal b1 = new BigDecimal(Double.toString(v1));
	        BigDecimal b2 = new BigDecimal(Double.toString(v2));
	        return b1.subtract(b2);
	    }
	
	    public static BigDecimal mul(double v1, double v2) {
	        BigDecimal b1 = new BigDecimal(Double.toString(v1));
	        BigDecimal b2 = new BigDecimal(Double.toString(v2));
	        return b1.multiply(b2);
	    }
	
	    public static BigDecimal div(double v1, double v2) {
	        BigDecimal b1 = new BigDecimal(Double.toString(v1));
	        BigDecimal b2 = new BigDecimal(Double.toString(v2));
	        return b1.divide(b2, 2, BigDecimal.ROUND_HALF_UP);  //四舍五入并保留两位小数
	    }
	}



五、表设计

Alt

  • 10
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值