采用的是renren-fast的后台脚手架框架,是人人开源社区采用Spring、MyBatis、Shiro编写的;
搭建完成后,先需要建立好数据库的所有数据表,以便代码生成器生成
随后利用代码生成器生成相应实体的controller,service,dao,entity实体以及mapper中的xml映射文件。
基本的增删改查具有了,接下来就根据实际的业务需求修改代码(主要针对业务逻辑层)
表内容组成:
产品配方主表 P:(物料suppliesCode,原膜memType,产品名称productName,id)
字表 S:配方子表(颜色color,对应区域fileUrl,header_id)
字表 S2:配色明细(厂家inkFactor,油墨inkName,型号model,配比propertion,header_id)
建立好主表和相应字表间的关系,字表作为成员变量存放在主表中
点击进入详情,传入主表id,找到对应的S表,再利用S表id找到S2表,逐层封装,最后返回给前端
条件搜索,能够确定唯一的产品
因为最后的配方列表需要将s2表的内容组装起来,所以先将产品名称-p表、颜色-s表、对应区域-s表进行表连接,然后再将s2表中的数据拼接为配方需要的字符串信息,再将两张表拼接起来。
-- 先尝试将s2的信息拼接成功,成一张新表
SELECT
header_id,
GROUP_CONCAT( model, '(', ink_name, '):', proportion, '%' ) AS info
FROM
bo_packprint_recp_s2
GROUP BY
header_id
-- 再将新表和产品、颜色、对应区域部分进行拼接,即可达到上图的检索效果
SELECT
p.product_name,
s.color,
s.file_url,
pf.info
FROM
bo_packprint_recp_p AS p
LEFT JOIN bo_packprint_recp_s AS s ON p.id = s.header_id
LEFT JOIN bo_packprint_recp_s2 AS s2 ON s.id = s2.header_id
LEFT JOIN (
SELECT
header_id,
GROUP_CONCAT( model, '(', ink_name, '):', proportion, '%' ) AS info
FROM
bo_packprint_recp_s2
GROUP BY
header_id) AS pf ON s.id = pf.header_id
WHERE
s.color LIKE '%蓝%' AND
s2.ink_name LIKE '%原蓝%' AND
p.product_name LIKE '%苏打水%' AND
p.mem_type LIKE '%透明膜%'
效果图中涉及到数据的替换,动态插入数据和图片,根据要求合并单元格。
首先,进行模板的绘制,将固定替换的位置标注出来。
将表格的内容进行遍历,如果表格单元格中的内容包含${},则执行替换的操作
根据实际数据个数,增加模板行数。
插入数据,图片转换为字节输入流保存,先插入数据,再进行判断合并。
动态添加数据和图片,并进行合并单元格操作的代码部分:
模板:
private static void insertTable(XWPFTable table, List<String[]> tableList, Integer matrListSize) {
//创建行,根据需要插入的数据添加新行,不处理表头
for (int i = 0; i < tableList.size(); i++) {
setTableStyle(table);
XWPFTableRow row = table.createRow();
for (int k = 1; k < tableList.get(0).length; k++) {
row.createCell();//根据String数组第一条数据的长度动态创建列
}
}
//遍历表格插入数据
List<XWPFTableRow> rows = table.getRows();
int length = table.getRows().size();
for (int i = 3; i < length; i++) {
XWPFTableRow newRow = table.getRow(i);
List<XWPFTableCell> cells = newRow.getTableCells();
for (int j = 0; j < cells.size(); j++) {
XWPFTableCell cell = cells.get(j);
if (j == 2) {
List<XWPFParagraph> paragraphs = cell.getParagraphs();
XWPFParagraph newPara = paragraphs.get(0);
XWPFRun imageCellRunn = newPara.createRun();
try {
String fileName = tableList.get(i - 3)[j];
File file = new File(fileName);
FileInputStream is = new FileInputStream(file);
imageCellRunn.addPicture(is, getPictureType(fileName), file.getName(), Units.toEMU(70), Units.toEMU(30));
} catch (Exception e) {
e.printStackTrace();
}
} else {
cell.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER);
CTTc cttc = cell.getCTTc();
CTP ctp = cttc.getPList().get(0);
CTPPr ctpPr = ctp.getPPr();
if (ctpPr == null) {
ctpPr = ctp.addNewPPr();
}
CTJc ctJc = ctpPr.getJc();
if (ctJc == null) {
ctJc = ctpPr.addNewJc();
}
ctJc.setVal(STJc.CENTER);
String s = tableList.get(i - 3)[j];
cell.setText(s);
}
}
}
//合并单元格(对每一行的进行遍历,根据相同的条件进行合并)
for (int n = matrListSize; n < tableList.size(); ) {
//获取到tableList里面的序号,如果序号相同就可以进行合并
String[] strings = tableList.get(n);
String index = strings[0];
// 定义可以合并的单元行数
int mergeNumber = 0;
for (String[] strings1 : tableList) {
String s = strings1[0];
if (index.equals(s)) {
mergeNumber += 1;
}
}
// col:指定列 fromRow:从第几行开始 toRow:从第几行结束
mergeCellsVertically(table, 0, 3 + n, 3 + n + mergeNumber - 1);
mergeCellsVertically(table, 1, 3 + n, 3 + n + mergeNumber - 1);
mergeCellsVertically(table, 2, 3 + n, 3 + n + mergeNumber - 1);
n = n + mergeNumber;
}
}
效果图:
NI~1\AppData\Local\Temp\1591692057388.png)]
for: 最原始的循环方式,基于索引来实现;
它的内容垃圾比foreach少,在遍历的过程中能对元素本身进行增删改的操作。
foreach:实现便捷,基于迭代器实现(实现Iterator集合类 ),能快速循环取出元素;
由于底层实现原理导致不能在遍历的过程中操作元素(增删改),否则会抛出ConcurrentModificationException的异常,但可以对元素的已有属性进行更改;
其中有两种情况可以在foreach中实现部分修改(自己操作过),在try…catch的情况下可以进行增删的操作,有结果但也报错;除此之外,没有catch的情况下删除需要保证元素只有两个,否则报错(没意义…哈哈)
@ExplicitConstraint注解
这各注解是一种数组方式的下拉框约束,(source={“aa”,“bb”})指定该注解修饰的字段在导出excel后能进行下拉框内容的选择。
@Api开头的注解作用
通常是用做接口文档Swagger的生成,在控制层,方法,实体类上添加相应注解完成接口文档的各个部分:
@Api(tags = "产品配方主表") // 作用在类上,说明具体针对哪个实体进行操作
@ApiOperation("查询/新增/删除产品配方主表") // 作用在方法上,具体说明方法的作用
@ApiImplicitParams({ // 执行方法时需要的多个参数说明(名称,含义,参数作用,数据类型)
@ApiImplicitParam(name = "memType", value = "原膜类型", paramType = "query", dataType = "String"),
@ApiImplicitParam(name = "suppliesCode", value = "物料代码", paramType = "query", dataType = "String")
})
@ApiModel(value="产品配方主表") // 说明实体类的含义,作用在具体的实体上面
@ApiModelProperty(value = "原膜类型") // 作用在实体类的属性上,说明实体的属性代表含义
// 其他常用注释
@Excel // 报表技术常用的一种注释,用于导出表格(创建人,id,更新时间和更新人的id不要加注释!!!)
@ExplicitConstraint(source = {"PVC","PET透明膜","PET白膜","BOPP","珠光膜","PE"})
// 在导出excel表格后,要求能在该字段下进行多选操作,则需要该注释,source里放入多选项。
@TableId // 修饰主键id
@TableName("bo_packprint_recp_p") // 对应的数据库中的表
@TableField("order_id") // 表中的字段和实际使用的字段(orderId)是不一致的
1、BigDecimal数据类型的特性?
是用于高精度计算的数据类型,有两类:BigInteger(针对大整数的处理类)和BigDecimal(针对大小数的处理类),他们创建的是对象,不能用普通的运算符计算,需调用对应的方法,方法的参数也必须一致。
加:add 减:subtract 乘:multiply 除:divide
推荐初始化时参数string,保证精度。BigDecimal decimal = new BigDecimal(“100”)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LisGxpyS-1591749647656)(C:\Users\ADMINI~1\AppData\Local\Temp\1591020963111.png)]
2、==和equals, compareTo的使用?
== 比较的是值是否相等,对于基本类型数据的变量,则直接比较值是否相等;对引用类型的变量则比较指向对象的地址。
equals 方法是不能作用于基本数据类型的变量的,equals方法继承自objec类,比较的是否为统一对象,没有重写的情况下比较的是引用类型的变量所指向的地址;但是String、Date类对equals方法重写,所以比较的是内容。
compareTo:用于对象的比较和字符串的比较,大于比较对象则返回正数,小于返回负数,等于返回0;
获得文件路径:this.getClass().getClassLoader().getResource("")
this.getClass().getClassLoader().getResource("").getPath()
// 获取src资源文件编译后的路径(即classes路径)
getClass():获得当前对象所属的Class对象
getClassLoader():获得Class对象的类加载器
// 输出: /D:/06-hs/packprint/target/classes/
lambda表达式不能访问非final修饰的局部变量,因为实例变量存在堆中,而局部变量存放在栈上,lambda表达是在另外的线程上进行执行的,所以当该线程访问局部变量的时候,该局部变量可能已经销毁了.
final类型的局部变量则是作为原变量的拷贝副本来使用。
jdk8中不需要显示的声明,在表达式(匿名类)中访问了就会强制加上final属性,后面就无法进行赋值等修改操作了;其中实例和静态变量是不受限制的。
pull:将服务器上的代码拉取到本地仓库,每次推送代码前拉取一次代码,保证代码是最新的。
commit:把修改好的代码提交到本地仓库,在此之前还有个add添加操作也就是添加到暂存区。
push:将本地仓库的代码接着推送到服务器上面,也就是主(master)从分支上面。
pull:将服务器上的代码拉取到本地仓库,每次推送代码前拉取一次代码,保证代码是最新的。
commit:把修改好的代码提交到本地仓库,在此之前还有个add添加操作也就是添加到暂存区。
push:将本地仓库的代码接着推送到服务器上面,也就是主(master)从分支上面。