前后端的代码都在GitHub上,https://github.com/xiguanlezz/E-Commerce
SpringBoot + Swagger接口文档 + tk-mybatis持久层框架 + FastDFS分布式文件系统 + Thymeleaf模板引擎 + 支付宝API
一、tk-mybatis
这个框架可以使用很多现成的API,简单的增删改查不需要再自己写SQL语句,和Mybatis-plus很像,安利一下,非常好使。
测试类:
@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两个字段即可。
算法的设计思想就是先将本节点加入集合,查找出下一层子节点的信息,再遍历子节点,每个子结点都调用一次这个方法,使用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
四、解决浮点数运算丢失精度的问题
测试问题浮点数运算存在的精度问题:
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();
}
}
使用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); //四舍五入并保留两位小数
}
}