需求分析
在一个电商系统中,如果用户下单之后,就需要去对当前订单去申请一个发票,然后发票里面就会包含当前的商品信息等,最后再由商家开发票以线上或线下的形式发送到用户。
由商家可以由用户的开发票的需求以及用户的一些信息对这些内容进行开票处理
然后其他就是一些常规的CRUD的操作,对抬头进行用户的管理,这里抬头与发票的关系是1对n
发票和商品是1对n的关系,因此有了如下的库表实现
技术要求
传统的技术栈
- SpringBoot
- Mybatis-plus
- MySQL
库表涉及
当前项目先进行单表操作,根据各自的id进行关联
发票表
CREATE TABLE `invoce` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`invoice_apply_number` varchar(31) DEFAULT NULL COMMENT '发票申请号',
`order_id` int(11) DEFAULT NULL COMMENT '订单id',
`seller_id` int(11) DEFAULT NULL COMMENT '商户id',
`invoice_id` int(11) DEFAULT NULL COMMENT '抬头id',
`invoice_status` tinyint(2) NOT NULL DEFAULT '0' COMMENT '发票状态(待开,开票中,开票完成)',
`seller_name` varchar(255) DEFAULT NULL COMMENT '商户名称',
`apply_time` timestamp NULL DEFAULT NULL COMMENT '申请日期',
`over_time` timestamp NULL DEFAULT NULL COMMENT '开具日期',
`invoice_amount` decimal(8,2) DEFAULT NULL COMMENT '金额总额',
`invoice_tax` decimal(8,2) DEFAULT NULL COMMENT '税额总额',
`complained` tinyint(2) NOT NULL DEFAULT '1' COMMENT '客诉标识',
`invoice_channel` tinyint(2) NOT NULL COMMENT '开票渠道',
`create_time` timestamp NULL DEFAULT NULL COMMENT '创建时间',
`update_time` timestamp NULL DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8;
抬头表
CREATE TABLE `invoce` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`invoice_apply_number` varchar(31) DEFAULT NULL COMMENT '发票申请号',
`order_id` int(11) DEFAULT NULL COMMENT '订单id',
`seller_id` int(11) DEFAULT NULL COMMENT '商户id',
`invoice_id` int(11) DEFAULT NULL COMMENT '抬头id',
`invoice_status` tinyint(2) NOT NULL DEFAULT '0' COMMENT '发票状态(待开,开票中,开票完成)',
`seller_name` varchar(255) DEFAULT NULL COMMENT '商户名称',
`apply_time` timestamp NULL DEFAULT NULL COMMENT '申请日期',
`over_time` timestamp NULL DEFAULT NULL COMMENT '开具日期',
`invoice_amount` decimal(8,2) DEFAULT NULL COMMENT '金额总额',
`invoice_tax` decimal(8,2) DEFAULT NULL COMMENT '税额总额',
`complained` tinyint(2) NOT NULL DEFAULT '1' COMMENT '客诉标识',
`invoice_channel` tinyint(2) NOT NULL COMMENT '开票渠道',
`create_time` timestamp NULL DEFAULT NULL COMMENT '创建时间',
`update_time` timestamp NULL DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8;
商品信息表
CREATE TABLE `merchandise` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`invoce_id` int(11) DEFAULT NULL COMMENT '关联发票id',
`merchandise_tax` decimal(8,2) DEFAULT NULL COMMENT '商品税率',
`merchandise_amount` decimal(12,6) DEFAULT NULL COMMENT '商品单价',
`merchandise_tax_number` decimal(8,2) NOT NULL COMMENT '税额',
`merchandise_money` decimal(8,2) DEFAULT NULL COMMENT '商品金额',
`merchandise_num` int(11) DEFAULT NULL COMMENT '数量',
`merchandise_unit` varchar(255) DEFAULT NULL COMMENT '单位',
`shop_name` varchar(255) DEFAULT NULL COMMENT '商品名',
`merchandise_model` varchar(255) DEFAULT NULL COMMENT '商品型号',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=524328962 DEFAULT CHARSET=utf8;
SET FOREIGN_KEY_CHECKS = 1;
后台实现
在编写项目中还有一些细节问题
不重复的申请号
添加数据的时候,这个发票的申请号是由我们自己生成的并且填充到数据库当中,所以我们使用一个工具类直接调用工具类set到sql中即可。
如下是工具类:
/**
* 根据长度生成随机数字
*
* @param start 起始数字
* @param end 结束数字
* @return 生成的随机码
*/
public static Integer randomCount(Integer start, Integer end) {
return (int) (Math.random() * (end - start + 1) + start);
}
/**
* 申请号生成
*
* @param payType String 支付类型
* @return 生成的随机码
*/
public static String getOrderNo(String payType) {
return payType + randomCount(111, 999) + System.currentTimeMillis() + randomCount(11111, 99999);
}
调用方法:
invoce.getInvoce().setInvoiceApplyNumber(RandomUtils.getOrderNo("F"));
在前面添加前缀,然后就可以顺利的将这些内容添加到数据库中
金额计算
因为我们对一些金额在数据库中存储的是decimal所以在对java计算中也需要使用相关的工具对其进行加减乘除四则运算。
比如前台只会给我们传递一些基本的货物信息,那么就需要由我们将这些内容进行计算存储到数据库表中,然后将来对管理端进行展示所需的一些信息。
BigDecimal invoiceTaxSum = BigDecimal.ZERO;
BigDecimal invoiceAmountSum = BigDecimal.ZERO;
for (Merchandise merchandise : invoce.getMerchandiseList()) {
merchandise.setInvoceId(invoce.getInvoce().getId());
merchandise.setMerchandiseTaxNumber(
merchandise.getMerchandiseTax().divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP)
.multiply(merchandise.getMerchandiseMoney())
);
invoiceTaxSum = invoiceTaxSum.add(merchandise.getMerchandiseTaxNumber());
invoiceAmountSum = invoiceAmountSum.add(merchandise.getMerchandiseMoney());
}
merchandiseService.saveBatch(invoce.getMerchandiseList());
q1. 如上就是我们对数据进行统计,这里之前我使用的是stream流因为在操作的时候使用BigDecimal进行计算的时候就会出现编译错误,所以后来使用的for循环来遍历
q2. 如果税额为小数当除数除不尽的时候,就会出现异常,所以我们这里使用BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP的方法对其进行四舍五入的操作
最后将这些数据存储到数据库中完成操作