赞
踩
项目介绍
健康管理机构的业务系统
传统的互联网项目(后端系统,前端微信网页)
1.需求说明书PRD【含功能大纲,功能详情,流程图,性能需求】、产品原型图
2.UI:原型图并非最终效果图,最终要过要以UI为准。所以如果有了也需要拿到
3.与前端工作人员对接接口名等参数
需求分析:产品人员做,产出PRD(含功能大纲,功能详情,流程图,性能需求)、产品原型图
设计(概设和详设): 架构师做,产出概设和详设(状态机,类图,时序图)
编码 : 程序员,产出功能代码(源代码,功能的单元测试代码)
测试 :测试人员,产出测试报告(测试用例[使用中的各种情况],功能测试报告,性能测试报告,安全测试报告,集成测试报告)
运维 : 程序员,对功能进行升级维护
1.搞清楚Controller、RestController、ResponseBody的区别(已解决)
1.报错:找不到父工程中的版本1.0-SNAPSHOT
原因:由于相互依赖,打包的时候找不到上一个依赖包
解决:首次打包顺序:domain–> 父工程
2.报错:控制层成员变量正常注入时空指针
原因:开启zk的时候 出现了线程阻塞
解决:重启zk,注意不要让光标停止。右键取消
3.报错: 业务层成员变量注入为null
业务层的东西多半都是jar包 所以直接自动注入即可
4.报错:
Failed to write HTTP message: org.springframework.http.converter.HttpMessageNotWritableException: No converter found for return value of type: class com.wz.result.Result
原因:返回结果不能正常转换为json
解决: 需要json依赖包
业务层可以正常获得结果 在返回到控制层时报错 超时
原因:网络波动与自己电脑有关
解决: 在业务层和控制层都添加timeout的时长
动态sql语句报错:java.sql.SQLException: Parameter index out of range (2 > number of parameters, which is 1).
原因:1)在动态sql中注释语句
2)只能只用concat的方式进行拼接字符串
解决: where code = #{value} or name like CONCAT(CONCAT(’%’, #{value}), ‘%’)
<select id="findAllCheckItem" resultType="CheckItem" parameterType="string">
SELECT * FROM t_checkitem
//注意:此处的value 必须与string的getvalue方法名一致,如果要使用其他的名字 那么要使用@param指定变量名
<if test="value!=null and value.length>0">
where code = #{value} or name like CONCAT(CONCAT('%', #{value}), '%')
</if>
ORDER BY id DESC
</select>
7.sql错误,Duplicate entry ‘xxx’ for key ‘PRIMARY’
原因:在操作数据库的时候,同一数据的主键进行了增删操作 (同事务中)
解决:逻辑错误,检查逻辑或者sql语句是否与逻辑一致
8.前台状态码400 后台HTTP 转换失败
原因:前台向后台传输的参数必须与后台@RequestBody的一致,否则报错
解决: 传入后台所需的参数
9.前端415错误 ,不是application/json类型
原因:依赖包错误导致
解决:使用完整的json类型依赖包
10.在使用freemarker的hashmap进行存储的时候, 不能使用object存储实体类对象,会导致map.put不能封装实体类中的实体类
11.interval = window.setInterval(timer,100);方法时, 该调用器方法不可以为timer() 否则只执行一次!!
12.一切正常。设置过,也确认过权限被移除,但是还是可以正常操作被移除权限。
原因:使用hasAuthority而不是hasPermission。
解决:后台使用注解设置权限的时候 ,是使用hasAuthority而不是hasPermission。
@PreAuthorize("hasAuthority('CHECKITEM_EDIT')")
13.(接上一个bug)该用户没有该权限但是没有被拦截仍然可以使用的情况
原因:在使用spring security安全框架的时候 web.xml配置文件不可以同时扫描,会导致权限不生效。
解决:使用dispatcher扫描器直接扫描全部配置文件,且不可以在spring中使用import标签导入
1.父工程中指定了必定继承的依赖,那么子工程中不能再指定,否则报错。
2.web.xml中的标签是存在顺序的
3.dubbo注解扫包底层会自动扫描component及其子注解,故可以不使用spring扫包
【建议加上spring扫包,因为如果还需要使用到其他spring注解会报错】
4.设置web.xml访问路径为/ 必须配置静态资源过滤
5.业务层的dao对象只能用autowared的方式自动注入,不能远程注入,否则为null
6.疑问: 为什么使用了ssm result对象还要new不用自动注入?
并不是所有的类都要交给容器来管理,像这种频繁创建的类都交给容器 容器会裂开的。
为什么dao不分为三层架构?
答:重新分解一层占用网咯资源 没有必要
7.由于是使用的post方式,所以在方法的参数上 必须使用@RequestBody来自动注入
8.dao中的sql语句 $#的区别: #在编译的时候会加上‘ ’ 另一个不会,传什么值就会存入什么值 不加 ‘ ’ 【高频面试】
9.如果dao写入数据库时出现乱码,在jdbc的url末尾拼接‘ ?characterEncoding=utf8’
10.业务层中@Service和@Trasactional一起使用时,需要对@Service指定Interfaceclass或interfaceName 在zk中生成实体对象供访问
11.为什么在后端使用requestBody可以正常接续json数据? 因为在前端页面的contentType中指定的时是application/json 这一步注解底层帮我们指定了 所以可以
12.前端的绝大多数方法都是异步方法。
13.在使用JDBC配置文件时,必须在url项加上增强型后缀。
14.如果使用了父类框架进行远程调用,必须用该成员变量的set方法注入 否则不生效为null
15.在对mybatis进行查询时,查询的total为1 但是在mybatis中实际上查询的是0 只是这个0依然也是一条记录,所以total是1
16.校验规则三部曲
1.输入过程校验
2.提交表单校验
3.后台校验 后台校验如果需要用到网络远程传输 如dubbo 那么写在控制层。其余情况可以些在业务层,可以方便打包复用。
18.redis中的值如果有过期时间,当过期之后,redis的key仍然是存在。只是拿不到这个key的值了。redis底层有一个200ms的定时清理器进行清理这些已经过期的key
19.在mybatis的插入sql语句中 useGenerate=true keyproperty=“表字段名” 可以取代selectKey来获取主键
1.如果遇到jar包冲突,可以选择删除对应jar包的updated文件(建议删除本地仓库中全部带有updated的文件)
2.在dubbo中使用事务,要在业务层的service注解中使用属性interfaceClass=类名.class定义,如果不定义就会去zk的注册中心中去找到代理对象,只有定义过才会在注册中心中有这样一个包名相同的对象
3.VUE内置的输出弹框 this.$message.error/success/info(" ") ;
4.前端代码post请求参数超过一个,可以在方法后面添加?以get的形式传输,后端使用多个参数接收,?后的参数不加@RequestBody 的json来接收
5.在百度搜索maven项目依赖包 以Maven开头 依赖包在后可以快速找到
6.在vue中 ,可以对{}类型的对象 使用[‘key’]的方式进行赋值。作为key存在, 有覆盖,无创建 这是一种js语法
if(vali) { //校验规则
let temp = []; //存储临时变量
this.checkitemIds.forEach(item => { //变量被勾选的检查项
temp[temp.length] = {'id': item}; //起使索引是0 每添加一个元素索引长度就+1
});
this.formData['checkItems'] =temp; //完成赋值
// 只要是一个对象类型就可以使用[]进行赋值,有覆盖,无创建
} //完成赋值
// 只要是一个对象类型就可以使用[]进行赋值,有覆盖,无创建
7.使用freemarker
注意事项:1.需要的占位符每一个循环变量都不能为空
2.占位符符号不能写错
1.所有的异常都由控制层统一接收处理,最好是封装一个类来统一返回
2.如果有字符串的信息,尽量使用静态final类来定义调用,而不是直接硬编码
3.三部曲验证,最后一步都在控制层进行控制,避免dubbo远程在业务层验证影响效率
1.dubbo 扫包标签 在底层会对component注解进行加载,所以如果仅适用了component不需要使用spring扫包也是可以使用的 【component实际上是controller,service,repository的父类】
2.在父类中 想要对私有成员变量进行注入 AutoWare的支持在父类中进行注入
但是使用dubbo的远程注入 是不可以的,因为源码有限制,只能以set方法开头的方法进行远程注入
故: 定义成员变量,使用起set方法实现注入
3.每次在使用ZK的时候都会生成 dubbo的节点,如果不指定interfaceName名字(全路径名)/interfaceClass(类名.class) 在java底层会…?
4.mybatis底层有两种取参数名的方式
@Param 和 //参数名 如果不使用注解 JVM在底层会首先尝试使用这个名字的.class文件 如果找到就可以使用 如果没有找到就会以arg0 arg1 等名字进行解析 能否找到与电脑有关。
统一规范,在使用单个变量名的时候要使用注解的方式
5.PageHelper 分页插件的 start方法 底层是使用的threadLocal 共享内存区
所以在查找方法的时候 才会去共享区拿到看似毫不相关的 当前页和每页大小
然后会在mybatis中追加limit 关键字进行查询
数据库创建时必须指定字符集为utf8mb4 作用:使用手机端时 支持emoj 四个字符
使用PMD ,对每一个表的每一个字段的含义和使用操作做出说明备注(规范)
数据库表 varchar(32) 存储 32个字母 16个字符(2字节) 8个表情(4字节)
导入实体类
导入公共资源
1.返回前端的状态类型对象
2.返回前端的结果对象
3.分页对象 【总数,当前页结果】
4.按条件查询对象
5.基础静态资源
查看项目第一天PDF具体使用步骤
是一款ER实体关系图设计软件 简称PDM(数据库设计图表)
可以方便的对系统进行分析设计 ,从而制作数据流程图,物理数据模型(数据库表设计),概念数据模型,面向对象模型。
1.制表过程
创建表格要使用Mysql 5.0 否则 sql yog创建报错
code – 表示表名、字段名
p --主键
identity – 主键自增
2.导出sql表
设计时要指定好外键,但是在创数据库时 过滤掉
1.菜单栏中 database
2.create database
3.option --> 取消勾选 外键
3.逆向工程
将建表文件解析为表图结构 显示到软件上
file —reverse --database
4.生成数据库报表文件
生成一个网页版的整体表数据直观,可以在里面查看所有的表的结构或信息
report - report wizard
datagroup 新型软件 好用 需要破解
datafactory,压力测试软件。大量为数据库表生产数据
使用任何组件 都需要先创建对象
new Vue ({
el : “ ”
})
布局容器总体布局规律图
解析:
在el - main中,会自动填充当行的剩余页面空间
容器的属性 定义 每一个子标签都是一行
如果想 aside跟main 保持在一行 需要使用container 存储在一起
左右侧的 aside 以子标签的顺序作为左右的设置
<!--导入以前的vue组件 可以生效--> <link rel="stylesheet" href="element-ui/lib/theme-chalk/index.css"> <script src="element-ui/vue.js"></script> <!-- 引入ElementUI组件库 --> <script src="element-ui/lib/index.js"></script> <el-container> <el-header>Header</el-header> <el-container> <!--在同一个容器中就可以左置--> <el-aside width="200px">Aside</el-aside> <el-container> <el-main>Main</el-main> <el-footer>Footer</el-footer> </el-container> <!--在同一个容器中就可以右置--> <el-aside width="200px">Aside</el-aside> </el-container> </el-container>
目标: 学习组件 关注 属性和类型
size
trigger
子标签中的 divided 可以在每个子选项中加分割线
<el-container> <el-aside width="200px"> <el-menu> <el-submenu v-for="menu in menuList" :index="menu.path"> <template slot="title"> <i class="fa" :class="menu.icon"></i> {{menu.title}} </template> <template v-for="child in menu.children"> <el-menu-item :index="child.path"> <a :href="child.linkUrl" target="right">{{child.title}}</a> </el-menu-item> </template> </el-submenu> </el-menu> </el-aside> <el-container> <iframe name="right" class="el-main" src="ordersetting.html" width="100%" height="580px" frameborder="0"></iframe> </el-container> </el-container>
<script> new Vue({ el: '#app', data:{ menuList:[ //循环该菜单显示对应数据和跳转功能 { "path": "1", "title": "工作台", "icon":"fa-dashboard", "children": [] }, { "path": "2", "title": "会员管理", "icon":"fa-user-md", "children": [ { "path": "/2-1", "title": "会员档案", "linkUrl":"member.html", "children":[] }, { "path": "/2-2", "title": "体检上传", "children":[] }, { "path": "/2-3", "title": "会员统计", "linkUrl":"all-item-list.html", "children":[] }, ] }, { "path": "3", "title": "预约管理", "icon":"fa-tty", "children": [ { "path": "/3-1", "title": "预约列表", "linkUrl":"ordersettinglist.html", "children":[] }, { "path": "/3-2", "title": "预约设置", "linkUrl":"ordersetting.html", "children":[] }, { "path": "/3-3", "title": "套餐管理", "linkUrl":"setmeal.html", "children":[] }, { "path": "/3-4", "title": "检查组管理", "linkUrl":"checkgroup.html", "children":[] }, { "path": "/3-5", "title": "检查项管理", "linkUrl":"checkitem.html", "children":[] }, ] }, { "path": "4", "title": "健康评估", "icon":"fa-stethoscope", "children":[ { "path": "/4-1", "title": "中医体质辨识", "linkUrl":"all-medical-list.html", "children":[] }, ] }, { "path": "5", //菜单项所对应的路由路径 "title": "统计分析", //菜单项名称 "icon":"fa-heartbeat", "children":[//是否有子菜单,若没有,则为[] { "path": "/5-1", "title": "工作量统计", "linkUrl":"all-medical-list.html", "children":[] } ] } ] } }); $(function() { var wd = 200; $(".el-main").css('width', $('body').width() - wd + 'px'); }); </script>
在官网中查看
在官网中查看更多
内置对象 this.$message 使用事件success/erorr/info/warning进行激活
此处以回调函数为例
axios.post("/跳转",this.formData).then((resp)=>{ //判断回调结果 //如果结果为true if(resp.data.flag){ //给出提示,成功 this.$message.success(resp.data.message) ; //使用弹窗内置对象 //重新刷新页面 显示检查项全部数据 this.pagination.currentPage=1; this.findPage(); }else { //给出提示,错误 this.$message.error(resp.data.message) ; //重新刷新页面 显示检查项全部数据 this.pagination.currentPage=1; this.findPage(); } })
Table组件功能
template slot-scope=“‘scope’ 作用域插槽 在子标签template中定义
//1.设置行固定 //2.斑马纹 //3.dataList变量进行双向绑定 <el-table size="small" current-row-key="id" :data="dataList" stripe highlight-current-row> <el-table-column type="index" align="center" label="序号"></el-table-column> <el-table-column prop="code" label="项目编码" align="center"></el-table-column> <el-table-column prop="name" label="项目名称" align="center"></el-table-column> <el-table-column label="适用性别" align="center"> <template slot-scope="scope"> <span>{{ scope.row.sex == '0' ? '不限' : scope.row.sex == '1' ? '男' : '女'}}</span> </template> </el-table-column> <el-table-column prop="age" label="适用年龄" align="center"></el-table-column> <el-table-column prop="remark" label="项目说明" align="center"></el-table-column> <el-table-column label="操作" align="center"> <template slot-scope="scope"> <el-button type="primary" size="mini" @click="handleUpdate(scope.row)">编辑</el-button> <el-button size="mini" type="danger" @click="handleDelete(scope.row)">删除</el-button> </template> </el-table-column> </el-table>
奇偶行不同颜色(斑马纹)stripe
背景色的变更 :row-class-name=“tableRowClassName”
//提供方法变更颜色
tableRowClassName({row, rowIndex}) {
if (rowIndex === 1) {
return 'warning-row';
} else if (rowIndex === 3) {
return 'success-row';
}
return '';
}
}
表头固定,设置表格的高度即可height=“250”
列固定/冻结,在列上设置fixed,如果是右固定则是fixed=‘right’
表格生成序号
<el-table-column type="index" width="50"></el-table-column> // type="index"
表格列的单选 highlight-current-row
表格的多选
<el-table-column type="selection" width="55"> </el-table-column>// type="selection"
表格按照列头排序,增加sortable即可
在官网中查看
表单的布局
表单的校验
1.实时校验
2.提交校验
//在data大属性中定义校验规则 //在form表单中使规则生效 <!--设置引用名,双向绑定formdata表单内容,指定规则--> <el-form ref="dataEditForm" :model="formData" label-position="right" label-width="100px" :rules="rules"> rules: { name: [ { required: true, message: '请输入活动名称', trigger: 'blur' }, { min: 3, max: 5, message: '长度在 3 到 5 个字符', trigger: 'blur' } ], region: [ { required: true, message: '请选择活动区域', trigger: 'change' } ] } //进行输入校验的语法 this.$refs['form'].validate(function(valid){ }); //清除校验 this.$refs["dataAddForm"].resetFields(); this.$refs["dataAddForm"].clearValidate();
3.表单划分
在表单中 可以分为24份 分开定义长度
<el-form ref="dataEditForm" :model="formData" label-position="right" label-width="100px" :rules="rules"> //此行为一组, 使用列标签 span属性进行绑定 建议以24为整数进行划分 <el-row> <el-col :span="12"> <!--设置校验对象--> <el-form-item label="编码" prop="code" > <el-input v-model="formData.code"/> </el-form-item> </el-col> <el-col :span="12"> <!--设置校验对象--> <el-form-item label="名称" prop="name"> <el-input v-model="formData.name"/> </el-form-item> </el-col> </el-row> <el-row> <el-col :span="12"> <el-form-item label="适用性别"> <el-select v-model="formData.sex"> <el-option label="不限" value="0"></el-option> <el-option label="男" value="1"></el-option> <el-option label="女" value="2"></el-option> </el-select> </el-form-item> </el-col> <el-col :span="12"> <!--设置校验对象--> <el-form-item label="助记码" prop="helpCode"> <el-input v-model="formData.helpCode"/> </el-form-item> </el-col> </el-row> <el-row> <el-col :span="24"> <el-form-item label="说明"> <el-input v-model="formData.remark" type="textarea"></el-input> </el-form-item> </el-col> </el-row> <el-row> <el-col :span="24"> <el-form-item label="注意事项"> <el-input v-model="formData.attention" type="textarea"></el-input> </el-form-item> </el-col> </el-row> </el-form>
<div class="pagination-container"> <el-pagination class="pagiantion" //内置类样式 @current-change="handleCurrentChange" //点击分页时的激活方法 :current-page="pagination.currentPage" //当前页 :page-size="pagination.pageSize" //每页大小 layout="total, prev, pager, next, jumper"//可供点击的按钮 :total="pagination.total" //总页数 > </el-pagination> </div> //方法名字和内容自己定义 handleCurrentChange(currentPage) { this.pagination.currentPage=currentPage; //修改当前页 this.pagination.queryString=null;//重置查询框内容 this.findPage();//调用查询 },
作用:在DAO层进行代码生成,名字【mybatis-generator】(free mybatis插件已经集成有该图形化功能)
步骤:
1.在build/plugins中导入依赖
2.创建配置文件【在需要生成代码的项目中】
3.连接到数据库
图片服务器 有一个热点图片缓存 还可以再加一个CDN缓存 (很贵) 来形成一套图片响应体系架构
– 网页上传到自己服务器,服务器在转发到图片服务器,下次访问直接从图片服务器(七牛)获取
API使用:七牛云SDK帮助文档
使用详情:
1.在控制层配置文件上传对象和导入依赖
<!--文件上传组件-->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="104857600" />
<property name="maxInMemorySize" value="4096" />
<property name="defaultEncoding" value="UTF-8"/>
</bean>
<quartz.version>2.2.1</quartz.version> <commons-fileupload.version>1.3.1</commons-fileupload.version> <!-- 文件上传组件 --> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>${commons-fileupload.version}</version> </dependency> <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>${quartz.version}</version> </dependency> <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz-jobs</artifactId> <version>${quartz.version}</version> </dependency> <dependency> <groupId>com.sun.jersey</groupId> <artifactId>jersey-client</artifactId> <version>1.18.1</version> </dependency> <dependency> <groupId>com.qiniu</groupId> <artifactId>qiniu-java-sdk</artifactId> <version>7.2.0</version> </dependency>
2.在页面添加两个必要方法,上传前校验,上传完成后显示的方法
//文件上传成功后的钩子,response为服务端返回的值,file为当前上传的文件封装成的js对象 handleAvatarSuccess(response, file) { //此处的response是返回的result对象,直接可以.属性 this.imageUrl=response.data; this.$message({ type:response.flag?"success":"error", message:response.message, name:response.name }); if(response.flag){ //预览刚刚上传的文件 this.imageUrl = "http://puco9aur6.bkt.clouddn.com/"+response.data; //为url赋值,绑定显示 this.formData.img = response.data;//将文件名称赋值给模型数据,用于表单提交 } }, //上传图片之前执行 beforeAvatarUpload(file) { console.log(file); //打印file内容到控制台 const isJPG = file.type === 'image/jpeg'; //图片格式为jpeg const isLt2M = file.size / 1024 / 1024 < 2; //大小小于2M if (!isJPG) { this.$message.error('上传套餐图片只能是 JPG 格式!'); } if (!isLt2M) { this.$message.error('上传套餐图片大小不能超过 2MB!'); } return isJPG && isLt2M; },
3.上传组件各个变量的功能
action:每次只要有上传行为就会执行该后台操作
<el-upload class="avatar-uploader" <!--通过上传前回调后,符合约束的文件上传的地址--> action="/setmeal/upload.do" <!--是否自动上传--> :auto-upload="true" <!--后端 接收此文件的参数名称 必须一致--> name="imgFile" :show-file-list="false" <!--上传成功之后的回调函数--> :on-success="handleAvatarSuccess" <!--上传之前的回调--> :before-upload="beforeAvatarUpload"> <!--图片上传成功之后,图片的预览效果--> <img v-if="imageUrl" :src="imageUrl" class="avatar"> //如果上传图片成功,那么url有值 就会直接赋值 <i v-else class="el-icon-plus avatar-uploader-icon"></i>//否则不显示,用对用设置的图片代替 此处是“+” </el-upload>
4.后台文件上传控制层
//文件上传 @RequestMapping("/uploadFile") public Result updoadFile(@RequestParam("imgFile") MultipartFile imgFile){ String originalFilename = imgFile.getOriginalFilename();//原始文件名 int index = originalFilename.lastIndexOf("."); //获得.的索引 String suffix = originalFilename.substring(index); //按照索引截取 获得.jpg String fileName = UUID.randomUUID().toString() + suffix; //生成一个文件名拼接 .jpg //将图片保存到七牛云 try{ //调用工具类 QiniuUtils.upload2Qiniu(imgFile.getBytes(),fileName); //文件上传成功后,需要将文件名称保存到redis中 jedisPool.getResource().sadd(RedisConstant.SETMEAL_PIC_RESOURCES,fileName); return new Result(true, MessageConstant.PIC_UPLOAD_SUCCESS,fileName); }catch (Exception e){ e.printStackTrace(); return new Result(false,MessageConstant.PIC_UPLOAD_FAIL); } }
5.点击确认提交表单后的业务层
成功插入数据库后,再使用流的方式提交到redis中进行缓存保存,主要是为了保存名字
//创建结果对象 Result result = new Result(); //插入套餐 int meal =mealDao.insertGroup(setmeal); //获得新增对象id Integer id = setmeal.getId(); //判断是否插入成功 //=0 表示插入失败 if(meal==0){ result.setFlag(false); result.setMessage(MessageConstant.EDIT_CHECKGROUP_FAIL); return result; } //套餐插入成功则将对应的图片存入redis中,确认成功后才提交到redis中缓存保存 jedisPool.getResource().sadd(RedisConstant.SETMEAL_PIC_DB_RESOURCES,setmeal.getImg());//自动注入过redispool
方案一
nginx 反向代理 一台简单的高性能服务器 偏向于静态资源部署 tomcat偏向于动态资源部署
场景:企业网站自己需要使用的图片数据
方案二:
开源分布式存储系统,如 Fastdfs (阿里开源)、HDFS
场景:用户使用过程中上传的图片。头像,朋友圈图片等。
方案三:
云存储。 如 七牛云,百度云 。费用高昂
场景:大型项目,例京东淘宝。
使用步骤:
1)上传
1.获取本地服务器对象
2.创建七牛云对象
3.鉴权(仓库,仓库AKey、SKey)
4.获得权限认证
5.上传 (不指定key 默认以hash值作为文件名)
6.解析上传结果
2)删除
1.获取本地服务器对象
2.创建七牛云对象
3.鉴权(仓库,仓库AKey、SKey)
4.指定删除文件名
5.传输指令
6.解析结果
期间需要由Redis提供缓存服务支持
Quartz定义: 开源的分布式定时任务调度框架
创建自定义job定时器类,创建需要执行的逻辑方法
配置文件
1.定时器类加入spring容器
2.配置指定需要使用的方法(必须public,否则可能无法调用)
3.创建触发器
4.创建触发器调度工厂
使用
1.导包(依赖spring包)
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.2.1</version> </dependency> <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz-jobs</artifactId> <version>2.2.1</version> </dependency> </dependencies>
2.编写job的实现逻辑,打为war包使用
/** * 自定义任务,定时清理垃圾图片 */ public class ClearImgJob { @Autowired private JedisPool jedisPool; //清理图片 public void clearImg(){ System.out.println("定时清理垃圾图片"); Set<String> set = jedisPool.getResource().sdiff(RedisConstant.SETMEAL_PIC_RESOURCES, RedisConstant.SETMEAL_PIC_DB_RESOURCES); if(set != null){ for (String fileName : set) { System.out.println("定时清理垃圾图片: " + fileName); //根据图片名称从七牛云服务器删除文件 QiniuUtils.deleteFileFromQiniu(fileName); //从redis集合中删除图片名称 jedisPool.getResource().srem(RedisConstant.SETMEAL_PIC_RESOURCES,fileName); } } } }
3.配置
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 注册自定义Job --> <bean id="jobDemo" class="com.itheima.jobs.JobDemo"></bean> <!-- 注册JobDetail,作用是负责通过反射调用指定的Job --> <bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"> <!-- 注入目标对象 --> <property name="targetObject" ref="jobDemo"/> <!-- 注入目标方法 --> <property name="targetMethod" value="run"/> </bean> <!-- 注册一个触发器,指定任务触发的时间 --> <bean id="myTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean"> <!-- 注入JobDetail --> <property name="jobDetail" ref="jobDetail"/> <!-- 指定触发的时间,基于Cron表达式 --> <property name="cronExpression"> <value>0/10 * * * * ?</value> </property> </bean> <!-- 注册一个统一的调度工厂,通过这个调度工厂调度任务 --> <bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <!-- 注入多个触发器 --> <property name="triggers"> <list> <ref bean="myTrigger"/> </list> </property> </bean> </beans>
七个位置 最后一位可以没有 空格分割
例如: 每十秒执行一次 0/10 * * * * * ?
每天凌晨2点执行一次 0 0 2 * *?
每天早上3-5,每隔5分钟执行一次 0 0/5 3-5 * * ?
名字 | 是否必须 | 允许值 | 特殊字符 |
---|---|---|---|
秒 | 是 | 0-59 | , - * / |
分 | 是 | 0-59 | |
时 | 是 | 0-23 | |
日 | 是 | 1-31 | |
月 | 是 | 0-11 | |
周 | 是 | 1-7 或 SUN-SAT | |
年 | 否 | 空 或 1970-2099 |
可以操作所有微软办公自动软件,只是更多的是用来操作excel表格
作用:批量数据上传/导出
注意:1. 读取单元格时 如果类型不一致 报错
2.获取数据
获取行 (获取行数/单元格数时,为去除表头所以行索引 从1开始且普通for循环遍历时<=,单元格索引从0开始)
获取单元格 (注: 如果单元格赋值后删除那么也会被读取,值为“ ”)
3. flush() 将内存中的缓存区数据 强制刷新到磁盘中存储
4.Excel07版之后压缩时,是以xml文件存储 用SXSSworkbook(不占内存,不支持公式)
XSSFworkbook 把excel读入内存并组织结构树(占内存,但是支持excel的公式计算)
导包(此处包仅支持excel)
<!--poi-->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
</dependency>
//获得poi对象,将文件读入内存 XSSFWorkbook sheets = new XSSFWorkbook("D:\\poi.xlsx"); //获取sheet页 getSheeAt / getSheetName //获取第一张表格 XSSFSheet sheet = sheets.getSheetAt(0); //循环每一行 for (Row row : sheet) { //循环每一行的每一格 for (Cell cell : row) { //转换读取的数据类型为string cell.setCellType(Cell.CELL_TYPE_STRING); //获取该单元格信息 String value = cell.getStringCellValue(); //输出 System.out.print(value); } }
//创建poi对象 XSSFWorkbook workbook = new XSSFWorkbook(); //创建工作表 XSSFSheet sheet = workbook.createSheet("表"); //创建第一行 XSSFRow row = sheet.createRow(0); //创建对该行单元格 XSSFCell cell = row.createCell(0); //对该行赋值 cell.setCellValue("aa11"); //创建第一行 XSSFCell cell_1 = row.createCell(1); //对该行赋值 cell_1.setCellValue("bb11"); //创建输出流 FileOutputStream out = new FileOutputStream("D:\\testPOI.xlsx"); //将内存中的excel写出` workbook.write(out); //刷新内存 out.flush(); //关流 out.close(); //关闭excel workbook.close();
判断该单元格是什么类型的方法可用,其他慎用
import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.List; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.usermodel.*; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.springframework.web.multipart.MultipartFile; public class POIUtils { private final static String xls = "xls"; private final static String xlsx = "xlsx"; private final static String DATE_FORMAT = "yyyy/MM/dd"; /** * 读入excel文件,解析后返回 * @param file * @throws IOException */ //获取该excel文件的每一行数据 public static List<String[]> readExcel(MultipartFile file) throws IOException { //检查文件 checkFile(file); int a = 0;//去除重复线,忽略 //获得Workbook工作薄对象 Workbook workbook = getWorkBook(file); //创建返回对象,把每行中的值作为一个数组,所有行作为一个集合返回 List<String[]> list = new ArrayList<String[]>(); //getWorkBook方法可能返回null if(workbook != null){ //遍历有几张表(表索引从0开始) for(int sheetNum = 0;sheetNum < workbook.getNumberOfSheets();sheetNum++){ //依次获得每一张表 Sheet sheet = workbook.getSheetAt(sheetNum); //判断该表为null直接进入下一次循环 if(sheet == null){ continue; } int a1= 0;//去除重复线,忽略 //获得当前sheet的开始行 int firstRowNum = sheet.getFirstRowNum(); //获得当前sheet的结束行 int lastRowNum = sheet.getLastRowNum(); //循环除了第一行的所有行(因为有表头,且行索引从1开始) for(int rowNum = firstRowNum+1;rowNum <= lastRowNum;rowNum++){ //获得当前行 Row row = sheet.getRow(rowNum); //判断该行为null直接进入下一次循环 if(row == null){ continue; } //获得当前行的第一个单元格(规则按照getLastCellNum方法实现) int firstCellNum = row.getFirstCellNum(); //获得当前行的列数 //getLastCellNum:获得该行的最后列数(修改过,后来重置为null的单元格也算一列) //getPhysicalNumberOfCells : 获得该行的物理列数(目前有值的最后一列单元格) //short lastCellNum = row.getLastCellNum(); //获取当前行实际上有多少单元格 int lastCellNum = row.getPhysicalNumberOfCells(); int a3 = 0;//去除重复线,忽略 //将实际单元格个数存入 String[] cells = new String[row.getPhysicalNumberOfCells()]; //循环当前行 for(int cellNum = firstCellNum; cellNum < lastCellNum;cellNum++){ Cell cell = row.getCell(cellNum);//获得当前这个单元格的对象 cells[cellNum] = getCellValue(cell);//判断该对象是那种类型的值,转换后存入数组 } list.add(cells);//将数组结果加入集合 } } workbook.close(); } return list; } //校验文件是否合法 public static void checkFile(MultipartFile file) throws IOException{ //判断文件是否存在 if(null == file){ throw new FileNotFoundException("文件不存在!"); } //获得文件名 String fileName = file.getOriginalFilename(); //判断文件是否是excel文件 if(!fileName.endsWith(xls) && !fileName.endsWith(xlsx)){ throw new IOException(fileName + "不是excel文件"); } } //判断是以哪一种格式结尾的,返回对应的数据类型对象 public static Workbook getWorkBook(MultipartFile file) { //获得文件名 String fileName = file.getOriginalFilename(); //创建Workbook工作薄对象,表示整个excel Workbook workbook = null; try { //获取excel文件的io流 InputStream is = file.getInputStream(); //根据文件后缀名不同(xls和xlsx)获得不同的Workbook实现类对象 if(fileName.endsWith(xls)){ //2003 workbook = new HSSFWorkbook(is); }else if(fileName.endsWith(xlsx)){ //2007 workbook = new XSSFWorkbook(is); } } catch (IOException e) { e.printStackTrace(); } return workbook; } //判断该单元格是什么类型 可用,其他慎用 public static String getCellValue(Cell cell){ //判断是都为空 String cellValue = ""; if(cell == null){ return cellValue; } //如果当前单元格内容为日期类型,需要特殊处理 CellStyle cellStyle = cell.getCellStyle(); String dataFormatString = cell.getCellStyle().getDataFormatString(); if(dataFormatString.equals("m/d/yy")){ //月日年-->年月日 cellValue = new SimpleDateFormat(DATE_FORMAT).format(cell.getDateCellValue()); return cellValue; } //把数字当成String来读,避免出现1读成1.0的情况 if(cell.getCellType() == Cell.CELL_TYPE_NUMERIC){ cell.setCellType(Cell.CELL_TYPE_STRING); } //判断数据的类型 switch (cell.getCellType()){ case Cell.CELL_TYPE_NUMERIC: //数字 cellValue = String.valueOf(cell.getNumericCellValue()); break; case Cell.CELL_TYPE_STRING: //字符串 cellValue = String.valueOf(cell.getStringCellValue()); break; case Cell.CELL_TYPE_BOOLEAN: //Boolean cellValue = String.valueOf(cell.getBooleanCellValue()); break; case Cell.CELL_TYPE_FORMULA: //公式 cellValue = String.valueOf(cell.getCellFormula()); break; case Cell.CELL_TYPE_BLANK: //空值 cellValue = ""; break; case Cell.CELL_TYPE_ERROR: //故障 cellValue = "非法字符"; break; default: cellValue = "未知类型"; break; } return cellValue; } }
<!--文件上传到后端,直接进行逻辑判断保存操作-->
<!--上传前会执行beforeUpload方法,为true才会访问该后端-->
<el-upload action="/ordersetting/uploadFile" //调用后端control的方法
name="imgFile" //前端后端名字必须一致且用@RequestParam("imgFile")注解接收
:show-file-list="false"
:on-success="handleSuccess" //action后端方法执行成功后的回调函数
:before-upload="beforeUpload" //上传前的回调函数(用户选择图片后的逻辑校验回调函数)
>
<el-button type="primary">上传文件</el-button>
</el-upload>
@Override
//添加上传文件,批量操作
public Result add(List<OrderSetting> list) {
//先删除,后插入,如有异常直接返回批量失败
try {
orderSettingDao.deleteOrderSetting(list); //删除,如果遇到该list是没有的 不会出错,影响行数0
orderSettingDao.insertOrderSetting(list); //插入
return new Result(true,MessageConstant.IMPORT_ORDERSETTING_SUCCESS);
} catch (Exception e) {
e.printStackTrace();
return new Result(true,MessageConstant.IMPORT_ORDERSETTING_FAIL);
}
}
前端解析代码和数据库中的字段不一致的时候 可以用map进行封装返回 因为没有直接可用的pojo,偷懒。
//内置方法,转换日期格式
this.formatDate(day.getFullYear(),day.getMonth()+1,day.getDate())
当封装的POJO中,有自定义POJO类型级联查询或字段与pojp不一致,需要使用resultMap进行映射封装
mybatis官网查询帮助文档:https://mybatis.org/mybatis-3/zh/index.html
两种方式
1.纯代码解析查询sql resultType 循环查询多次 影响性能?
解析:每一次循环都要进行查询,此查询是简单sql连接语句
//根据套餐ID查询套餐详情(套餐基本信息、套餐对应的检查组信息、检查组对应的检查项信息)
public Setmeal findById2(int id) {
Setmeal setmeal = setmealDao.findById(id);
if(setmeal!=null) {
// select b.* from t_setmeal_checkgroup a LEFT JOIN t_checkgroup b on a.checkgroup_id=b.id where a.setmeal_id=#{id}
List<CheckGroup> checkGroups = checkGroupDao.findBySetmealId(id);
for(CheckGroup group : checkGroups){
// select b.* from t_checkgroup_checkitem a LEFT JOIN t_checkitem b on a.checkitem_id=b.id where a.checkgroup_id=#{id}
List<CheckItem> checkItems = checkItemDao.finByGroupId(group.getId());
group.setCheckItems(checkItems);
}
setmeal.setCheckGroups(checkGroups);
}
}
2.级联关联 resultMap
resultMap特性:
1) 对pojo进行一一映射 javatype和jdbctype的使用场景?
2)继承特性
注意:
1)在做前端页面的时候,不能使用table标签,因为页面刷新,会等到所有table数据读取完成才会统一显示,所以用div、li、等标签来替换。避免网络卡顿,数据过大时,打开网页时白一片
1.页面刷新开始异步调用访问数据
window.location.toString() 重点语句,获取当前网址的字符串信息
mounted(){ //获取到网址的字符串信息 // http://localhost:81/pages/setmeal_detail.html?id=5&name=wz var page = window.location.toString(); //按照?进行切分 var target = page.split("?"); //切分之后的[1]就是?之后的信息 // id=5&name=wz //获得[1],按照&进行切分,获得一个数组 var arr = target[1].split("&"); //遍历数组id=5 、name=wz for(let i = 0 ;i < arr.length;i++){ //每一个值都要按照=进行切分 获得数组 [0]id [1]5 let num = arr[i].split("="); //判断[0]是否是id 如果是那么返回[1]的结果 if(num[0]=id){ //只要进入页面就发送异步请求查询该套餐的所有信息显示到页面 axios.post("/setmeal/getAllInfoById?id=" + num[1]).then((response) => { if(response.data.flag){ this.setmeal = response.data.data[0]; this.imgUrl = 'http://qekirsm87.bkt.clouddn.com/' + this.setmeal.img; } }); } }
2.业务层的开始调用dao,返回一个套餐对象
//前端页面-查询所有检查组和检查项
@Override
public Result getCheckGroupsAndCheckItems(String id) {
//调用dao级联查询
Setmeal setmeal = mealDao.getCheckGroupsAndCheckItemsInMeal(id);
if(setmeals!=null){
return new Result(true,MessageConstant.GET_SETMEAL_LIST_SUCCESS,setmeals);
}
//返回结果
return new Result(false,MessageConstant.GET_SETMEAL_LIST_FAIL);
}
3.套餐的dao
解析:1.由getCheckGroupsAndCheckItemsInMeal方法进入,开始查询该套餐信息。查询结果封装进getMeal的resultMap
2.由于继承关系,所以可以完整的把所有字段封装进Setmeal对象
3.执行级联查询字段checkGroups
<mapper namespace="com.wz.dao.MealDao"> <!--套餐查询的结果集,检查组由继承完成--> <resultMap id="baseMealResultMap" type="Setmeal"> <id column="id" property="id"/> <result column="name" property="name"/> <result column="code" property="code"/> <result column="helpCode" property="helpCode"/> <result column="sex" property="sex"/> <result column="age" property="age"/> <result column="price" property="price"/> <result column="remark" property="remark"/> <result column="attention" property="attention"/> <result column="img" property="img"/> </resultMap> <!--该套餐查询结果的检查组级联查询--> <resultMap id="getMeal" type="SetMeal" extends="baseMealResultMap"> <!--由于是集合类型,使用collection--> <collection property="checkGroups" ofType="CheckGroup" column="id" select="com.wz.dao.CheckGroupDao.findCheckGroupAndItemByGroupId" /> </resultMap> <!--前端页面,查询该套餐的所有检查项和组--> <select id="getCheckGroupsAndCheckItemsInMeal" resultMap="getMeal"> select * from t_setmeal where id=#{id} </select> </mapper>
4.检查组的dao
解析:1.由上层调用,在findCheckGroupAndItemByGroupId方法进入,开始查询该检查组信息。
查询结果封装进getGroup的resultMap。
2.由于继承关系,所以可以完整的把所有字段封装进CheckGroup对象
3.执行级联查询字段checkItems,此处是一个list集合,但是底层会将每一个元素单独进行调用下一层sql语句
<mapper namespace="com.wz.dao.CheckGroupDao"> <!--检查组的映射配置--> <resultMap id="baseGroupResultMap" type="CheckGroup"> <id column="id" property="id"/> <result column="name" property="name"/> <result column="code" property="code"/> <result column="helpCode" property="helpCode"/> <result column="sex" property="sex"/> <result column="remark" property="remark"/> <result column="attention" property="attention"/> </resultMap> <!--该检查组级联查询检查项的设置,此处是存放的是一个集合id,会依次向下调用--> <!--所以会id=5封装一个CheckItem,id=6封装一个CheckItem--> <resultMap id="getGroup" type="CheckGroup" extends="baseGroupResultMap"> <collection property="checkItems" ofType="CheckItem" column="id" select="com.wz.dao.CheckItemDao.findItemByGroupId"/> </resultMap> <!--前端,级联查询检查组--> <select id="findCheckGroupAndItemByGroupId" resultMap="getGroup"> SELECT * FROM t_checkgroup WHERE id IN (SELECT checkgroup_id FROM t_setmeal_checkgroup WHERE setmeal_id =#{id}) </select> </mapper>
5.检查项的dao
解析:1.由上层调用,在findItemByGroupId方法进入,开始查询每一个检查项信息。
查询结果封装进自己的resultType的CheckItem。由于上层是list集合所以会变成一个个元素存取
2.至此查询完毕,实现页面模型的多json嵌套结构
setmeal:{
img:"",
name:"",
checkGroups:[
{
name:"",
code:"",
checkItems:[
{name:"",code:""}
]
}
]
}
<!--前端,级联查询检查项-->
<select id="findItemByGroupId" resultType="CheckItem">
SELECT * FROM t_checkitem WHERE id IN (SELECT checkitem_id FROM t_checkgroup_checkitem WHERE checkgroup_id=#{id})
</select>
阿里云短信服务
==观看代码实现==
官网:http://freemarker.foofun.cn/ 查询帮助文档
作用:使网页可以更快响应,在进行增删改操作后,生成一个静态页面以后续直接调用
定义:java语言开发的模板引擎
场景 : 页面静态化生成(将一个动态数据加载的页面生成为html的过程,称为静态化)
如:合同模板、邮件模板
组成: template 基本模板
model 模板需要使用的数据
1.导包
<!--FreeMarker-->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.30</version>
</dependencml
2.准备模板(该模板文件要以.ftl结尾)
<html>
<head>
<title>Welcome!</title>
</head>
<body>
<h1>Welcome ${user}!</h1>
<p>Our latest product:
<#-- 我是一个注解 -->
<h1>GOAWAY ${user}!</h1>
<a href="${latestProduct.url}">${latestProduct.name}</a>!
</body>
</html>
3.代码
此处放置在d盘,正常下是放在web的ftl文件下
// Configuration cfg = new Configuration(Configuration.VERSION_2_3_22); Configuration cfg = new Configuration(Configuration.getVersion()); //创建freemarker对象 cfg.setDirectoryForTemplateLoading(new File("D:\\te"));//指定模板位置 cfg.setDefaultEncoding("UTF-8"); //指定生成模板的编码 Template temp = cfg.getTemplate("ts.ftl"); //指定模板文件名 //模板只能以json格式接收 //创建map1 Map root = new HashMap(); root.put("user", "Blank"); //由于模板中存在对象数据,存入对象 //方式一: Map latest = new HashMap(); root.put("latestProduct", latest); latest.put("url", "https://www.baidu.com"); latest.put("name", "百度一下啊"); //方式二: root.put("latestProduct",new User("https://www.baidu.com","百度一下啊")); // 通过一个文件输出流,就可以写到相应的文件中,此处用的是绝对路径 FileWriter out = new FileWriter("D:\\te\\test.html"); temp.process(root, out); //生成test.html文件 out.close(); //关流,不然会引起内存泄漏
所有指令都是以#开始
<html> <head> <title>Welcome!</title> </head> <body> <h1>Welcome ${user}!</h1> <!-- 使用指令定义变量 --> <#assign shit="大牛"> <h1>GOAWAY ${shit}!</h1> <#assign info={"mobile":"13812345678",'address':'北京市昌平区'} > 电话:${info.mobile} 地址:${info.address} <p>Our latest prodct: <#-- 我是一个注解 --> <a href="${latestProduct.url}">${latestProduct.name}</a>! </body> </html>
<html>
<head>
<title>Welcome!</title>
</head>
<body>
<h1>Welcome ${user}!</h1>
<#assign shit="大牛">
<h1>GOAWAY ${shit}!</h1>
<!-- 在java代码中指定文件存放的是位置找到该文件。并载入其内容 -->
<#include "he.ftl">
</body>
</html>
判断条件,可以在Java中定义变量,也可以使用assign定义。
使用: 可以使用if else elseif /if 传入的判断值在java中的map的key来定义
<html> <head> <title>Welcome!</title> </head> <body> <!-- 此success在Map中定义,如果没有定义则报错 --> <!-- 同时存在,以assign为准 --> <#if success=1> 条件1成立.. <#elseif success=2> 条件2成立.. <#else> 条件不成立.. </#if> </body> </html>
HashMap<Object, Object> map = new HashMap<>(); //模板只能以json格式接收 ArrayList<Map> goods = new ArrayList<>(); Map root1 = new HashMap(); root1.put("name","sa"); root1.put("price","23"); Map root2 = new HashMap(); root2.put("name","sa"); root2.put("price","23"); Map root3 = new HashMap(); root3.put("name","sa"); root3.put("price","23"); //存入 goods.add(root1); goods.add(root2); goods.add(root3); map.put("goods",goods); // 通过一个文件输出流,就可以写到相应的文件中,此处用的是绝对路径 FileWriter out = new FileWriter("D:\\te\\test.html"); temp.process(map, out);
ftl文件list输出格式 以as作为遍历每一个
<#list goods as good>
商品名称: ${good.name} 价格:${good.price}<br>
</#list>
<html> <head> <title>Welcome!</title> </head> <body> date:仅日期部分,没有一天当中的时间部分。 time:仅一天当中的时间部分,没有日期部分。 datetime:日期和时间都在 ${openingTime?time}<br> ${openingTime?date}<br> ${openingTime?datetime}<br> //结果 22:17:37 2020-8-10 2020-8-10 22:17:37 </body> </html>
时机: 在任何本体或者子项发生改变的时候生成一次静态页面,静态页面是是使用代码后台直接生成的,不需要使用异步调用!
注意事项:
1.指定输出的模板存储位置
2.代码中自动注入对象为FreeMarkerConfigurer
@Autowired
private FreeMarkerConfigurer freeMarkerConfigurer;//注入freemarker映射工厂
3.模板中的访问地址要与代码中地址保持一致。且是指定磁盘绝对路径
4.生成的主页面信息文件都是在后端代码生成的,前端做逻辑处理。比如跳转页面等
5.模板要以.ftl结尾
6.各个参数的定义
//spring中定义,代码中注入FreeMarkerConfigurer
Configuration configuration = freeMarkerConfigurer.getConfiguration();
cfg.setDirectoryForTemplateLoading(new File("D:\\te"));//指定模板位置
cfg.setDefaultEncoding("UTF-8"); //spring中定义,转换的页面编码格式
Template temp = cfg.getTemplate("ts.ftl"); //java中定义指定模板位置中的哪个ftl模板文件
//java中定义,配合外部配置文件字符串拼接
FileWriter out = new FileWriter("输出路径+文件名字+.html");
temp.process(map数据, out); //java中定义,传入数据生成到指定位置
步骤:
<!--FreeMarker-->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.30</version>
</dependencml
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! --> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0,user-scalable=no,minimal-ui"> <meta name="description" content=""> <meta name="author" content=""> <link rel="icon" href="../img/asset-favico.ico"> <title>预约</title> <link rel="stylesheet" href="../css/page-health-order.css" /> </head> <body data-spy="scroll" data-target="#myNavbar" data-offset="150"> <div class="app" id="app"> <!-- 页面头部 --> <div class="top-header"> <span class="f-left"><i class="icon-back" onclick="history.go(-1)"></i></span> <span class="center">传智健康</span> <span class="f-right"><i class="icon-more"></i></span> </div> <!-- 页面内容 --> <div class="contentBox"> <div class="list-column1"> <ul class="list"> <li class="list-item"> <#--循环集合--> <#list setmealList as setmeal> <#--点击链接跳转到检查组详情页面--> <a class="link-page" href="/yemian/setmeal_detail_${setmeal.id}.html?id=${setmeal.id}"> <#--<a class="link-page" href="/yemian/setmeal_detail_${setmeal.id}.html">--> <img class="img-object f-left" src="http://qekirsm87.bkt.clouddn.com/${setmeal.img}" alt=""> <div class="item-body"> <h4 class="ellipsis item-title">${setmeal.name}</h4> <p class="ellipsis-more item-desc">${setmeal.remark}</p> <p class="item-keywords"> <span> <#if setmeal.sex == '0'> 性别不限 <#else> <#if setmeal.sex == '1'> 男 <#else> 女 </#if> </#if> </span> <span>${setmeal.age}</span> </p> </div> </a> </#list> </li> </ul> </div> </div> </div> <script> var vue = new Vue({ el:'#app', data:{ setmealList:[] } }); </body>
此处使用的fastjson依赖包。不推荐使用
修改为freemarker语法,vue自动双向绑定到页面中且内存占用小!!
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! --> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0,user-scalable=no,minimal-ui"> <meta name="description" content=""> <meta name="author" content=""> <link rel="icon" href="../img/asset-favico.ico"> <title>预约详情</title> <link rel="stylesheet" href="../css/page-health-orderDetail.css" /> <script src="../plugins/vue/vue.js"></script> <script src="../plugins/vue/axios-0.18.0.js"></script> <script src="../plugins/healthmobile.js"></script> <script> var id = getUrlParam("id"); </script> </head> <body data-spy="scroll" data-target="#myNavbar" data-offset="150"> <div id="app" class="app"> <!-- 页面头部 --> <div class="top-header"> <span class="f-left"><i class="icon-back" onclick="history.go(-1)"></i></span> <span class="center">传智健康</span> <span class="f-right"><i class="icon-more"></i></span> </div> <!-- 页面内容 --> <div class="contentBox"> <div class="card"> <div class="project-img"> <img :src="imgUrl" width="100%" height="100%" /> </div> <div class="project-text"> <h4 class="tit">{{setmeal.name}}</h4> <p class="subtit">{{setmeal.remark}}</p> <p class="keywords"> <span>{{setmeal.sex == '0' ? '性别不限' : setmeal.sex == '1' ? '男':'女'}}</span> <span>{{setmeal.age}}</span> </p> </div> <!--<div class="project-know"> <a href="orderNotice.html" class="link-page"> <i class="icon-ask-circle"><span class="path1"></span><span class="path2"></span></i> <span class="word">预约须知</span> <span class="arrow"><i class="icon-rit-arrow"></i></span> </a> </div>--> </div> <div class="table-listbox"> <div class="box-title"> <i class="icon-zhen"><span class="path1"></span><span class="path2"></span></i> <span>套餐详情</span> </div> <div class="box-table"> <div class="table-title"> <div class="tit-item flex2">项目名称</div> <div class="tit-item flex3">项目内容</div> <div class="tit-item flex3">项目解读</div> </div> <div class="table-content"> <ul class="table-list"> <li class="table-item" v-for="checkgroup in setmeal.checkGroups"> <div class="item flex2">{{checkgroup.name}}</div> <div class="item flex3"> <label v-for="checkitem in checkgroup.checkItems"> {{checkitem.name}} </label> </div> <div class="item flex3">{{checkgroup.remark}}</div> </li> </ul> </div> <div class="box-button"> <a @click="toOrderInfo()" class="order-btn">立即预约</a> </div> </div> </div> </div> </div> <script> var vue = new Vue({ el:'#app', data:{ imgUrl:null,//套餐对应的图片链接 //使用json格式回传的时候,只需要在此处修改为freemarker语法,vue自动双向绑定到页面中且内存占用小!! setmeal:${key} }, methods:{ toOrderInfo(){ window.location.href = "/pages/orderInfo.html?id=" + id; } }, }); </script> </body>
(占位符都使用freemarker语法)
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! --> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0,user-scalable=no,minimal-ui"> <meta name="description" content=""> <meta name="author" content=""> <link rel="icon" href="../img/asset-favico.ico"> <title>预约详情</title> <link rel="stylesheet" href="../css/page-health-orderDetail.css" /> <script src="../plugins/vue/vue.js"></script> <script src="../plugins/vue/axios-0.18.0.js"></script> <script src="../plugins/healthmobile.js"></script> <script> var id = getUrlParam("id"); </script> </head> <body data-spy="scroll" data-target="#myNavbar" data-offset="150"> <div id="app" class="app"> <!-- 页面头部 --> <div class="top-header"> <span class="f-left"><i class="icon-back" onclick="history.go(-1)"></i></span> <span class="center">传智健康</span> <span class="f-right"><i class="icon-more"></i></span> </div> <!-- 页面内容 --> <div class="contentBox"> <div class="card"> <div class="project-img"> <img :src="imgUrl" width="100%" height="100%" /> </div> <div class="project-text"> <h4 class="tit">${setmeal.name}</h4> <p class="subtit">${setmeal.remark}</p> <p class="keywords"> <span> <#if setmeal.sex == '0'> 性别不限 <#else> <#if setmeal.sex == '1'> 男 <#else> 女 </#if> </#if> </span> <span>${setmeal.age}</span> </p> </div> </div> <div class="table-listbox"> <div class="box-title"> <i class="icon-zhen"><span class="path1"></span><span class="path2"></span></i> <span>套餐详情</span> </div> <div class="box-table"> <div class="table-title"> <div class="tit-item flex2">项目名称</div> <div class="tit-item flex3">项目内容</div> <div class="tit-item flex3">项目解读</div> </div> <div class="table-content"> <ul class="table-list"> <#list setmeal.checkGroups as checkgroup> <li class="table-item"> <div class="item flex2">${checkgroup.name}</div> <div class="item flex3"> <#list checkgroup.checkItems as checkitem> <label> ${checkitem.name} </label> </#list> </div> <div class="item flex3">${checkgroup.remark}</div> </li> </#list> </ul> </div> <div class="box-button"> <a @click="toOrderInfo()" class="order-btn">立即预约</a> </div> </div> </div> </div> </div> <script> var vue = new Vue({ el:'#app', data:{ imgUrl:null,//套餐对应的图片链接 setmeal:{} }, methods:{ toOrderInfo(){ window.location.href = "/pages/orderInfo.html?id=" + id; } } }); </script> </body>
作用:在增删改操作完成之后直接执行方法,方法内部进行查询生成模板。前端自动访问模板。
//生成模板方法 public void makeTemplate(String templateFileName, String pageName, Map data) throws Exception { //通过工厂获得对象 Configuration configuration = freeMarkerConfigurer.getConfiguration(); //指定模板 Template template = configuration.getTemplate(templateFileName); //创建输出流 FileWriter fileWriter = new FileWriter(new File(outputpath+"/"+pageName)); //输出模板 template.process(data,fileWriter); //关流 fileWriter.close(); } -----------------------------详细页面--------------------------------------------------------------------- -----------------------------详细页面--------------------------------------------------------------------- -----------------------------详细页面--------------------------------------------------------------------- //生成详细页面方法 public void makeStaticDetailPage(List<Setmeal> list) throws Exception { //定义模板名字 // String templateName = "setmeal_detail.ftl"; String templateName = "setmeal_detailForSmall.ftl"; //使用map回传的模板 //循环集合 for(Setmeal setmeal : list){ //定义生成的文件名,文件末尾按照该套餐id结尾。方便前端使用循环时好查找 String detailPageName = "setmeal_detail_"+String.valueOf(setmeal.getId()+".html"); //级联查询所有子集合,获得结果 List<Setmeal> s = mealDao.getCheckGroupsAndCheckItemsInMeal(String.valueOf(setmeal.getId())); //获取查询出来的套餐对象 Setmeal smeal = s.get(0); //创建map HashMap<String, String> map = new HashMap<>(); String s1 = JSON.toJSONString(smeal); //存入集合 // map.put("setmeal",smeal); map.put("key",s1); //使用map的模板时。回传json格式 //调用基本模板方法生成静态页面 makeTemplate(templateName,detailPageName,map); } -----------------------------套餐页面--------------------------------------------------------------------- -----------------------------套餐页面--------------------------------------------------------------------- -----------------------------套餐页面--------------------------------------------------------------------- //生成套餐页面方法 //生成套餐页面,异常上抛,谁调用谁处理 public void makeStaticPage(List<Setmeal> list) throws Exception { //定义数据 HashMap<String, List<Setmeal>> map = new HashMap<>(); //存入map map.put("setmealList",list); //定义模板名字 String templateName = "m.ftl"; //定义生成的文件名 String mealPageName = "mealPage.html"; //调用基本模板方法,生成一个网页输出到指定位置 makeTemplate(templateName,mealPageName,map); }
1.在业务层中spring容器中配置freemarkerConfig
<!--配置freemarker模板对象-->
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<!--指定模板文件所在目录,外部不可访问-->
<property name="templateLoaderPath" value="/WEB-INF/ftl/" />
<!--指定字符集-->
<property name="defaultEncoding" value="UTF-8" />
</bean>
2.创建地址的外部文件
outputPath=F:/ideaProject/project/projectHEALTH/mobile/src/main/webapp/yemian //项目中webapp在磁盘中的绝对路径
在index中修改跳转首个跳转位为模板网页
datepicker 日期控件 安卓
身份证验证 后端是连接的公安局的服务器获取的身份证? 身份证是有一套验证规则的
此处的提交前校验为什么不能交给vue的校验 后端访问人数少
前端页面怎么在失去焦点的基础上再验证正则? async-validator
提供一个通用的查询条件,返回list集合,模板
<!--动态条件查询--> <select id="findByCondition" parameterType="com.itheima.pojo.Order" resultMap="baseResultMap"> select * from t_order <where> <if test="id != null"> and id = #{id} </if> <if test="memberId != null"> and member_id = #{memberId} </if> <if test="orderDate != null"> and orderDate = #{orderDate} </if> <if test="orderType != null"> and orderType = #{orderType} </if> <if test="orderStatus != null"> and orderStatus = #{orderStatus} </if> <if test="setmealId != null"> and setmeal_id = #{setmealId} </if> </where> </select>
cookie为什么要设置路径为/ 这样不是所有路径都会携带该cookie? 常规操作 所有的页面都需要验证
拦截规则/**拦截所有请求,以前使用的/ * 安全框架独有
使用注解,认证之后这个就可以访问其他方法,是使用value字符串内存中定义的吗
配置form和loginout标签,如果还有其他的自定义的页面也是要放行的,怎么弄?
<security:http security="none" pattern="/pages/a.html"></security:http>
如何从spring中跳转到service页面
<security:authentication-provider user-service-ref="logical">
spring security 框架支持功能:
登录:自动生成登录网址,供输入密码账户。
认证:框架底层通过暗号网址自动与指定方式的结果进行对接。
鉴权:按照给定的方式进行判断权限
退出:框架底层通过暗号网址自动注销所有权限功能
site: spring.io 关键字 按照网站的关键字查询
1.access和exception要一致
2.配置用户时,password 的{noop}表示是否配置明暗文
3.使用form标签,表单提交必须为post,否则404
4.如果遇到ifram嵌套网页 视为不安全 需要在http标签中配置security:header标签
工程定义:war包,非继承父工程
web 中
/ 不拦截 jsp
/ 均不拦截*
*.do 只拦截.do结尾的请求
1.导入依赖包
一般情况下都需要依赖spring,此处未给出
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
</dependency>
2.配置delegatingFilterproxy委派过滤器
delegatingFilterproxy 类 作用:整合三方框架 含shrio
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app> <display-name>Archetype Created Web Application</display-name> <filter> <!-- DelegatingFilterProxy用于整合第三方框架 整合Spring Security时过滤器的名称必须为springSecurityFilterChain, 否则会抛出NoSuchBeanDefinitionException异常 --> <filter-name>springSecurityFilterChain</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>springSecurityFilterChain</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- 指定加载的配置文件 ,通过参数contextConfigLocation加载 --> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> </web-app>
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" xmlns:context="http://www.springframework.org/schema/context" xmlns:security="http://www.springframework.org/schema/security" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd "> <!--扫包--> <context:component-scan base-package="com.wz"/> <!--放行静态页面--> <mvc:default-servlet-handler/> <!--扫描spring框架的注解--> <mvc:annotation-driven/> <!-- 配置第二层拦截器 --> <!--1.配置可以直接访问的地址--> <!--<security:http security="none" pattern="/pages/a.html"></security:http>--> <!--2.配置需要拦截的地址--> <!--由于有权限验证,所以如果没有就自动跳转到框架登录页面--> <security:http auto-config="true" use-expressions="true"> <!-- 访问下列网页时需要带有该角色 --> <security:intercept-url pattern="/user/**" access="hasRole('ROLE_ADMIN')" /> <security:intercept-url pattern="/TT/**" access="hasRole('ROLE_GOOD')" /> <security:intercept-url pattern="/pages/**" access="isAuthenticated()" /> <!--匿名访问--> <!--<security:intercept-url pattern="/pages/**" access="isAnonymous()" />--> <!--自定义登录页面--> <!--因为设置了自定义页面,所以要使用拦截调用器来授权--> <security:form-login login-page="/login.html" username-parameter="account" password-parameter="password" login-processing-url="/hhhh" default-target-url="/index.html" authentication-failure-url="/login.html"></security:form-login> <!--关闭自定义表单的过滤器,不关闭所有请求被拦截--> <security:csrf disabled="true"></security:csrf> <!--退出登录,注销所有权限信息--> <security:logout logout-url="/logout" logout-success-url="/login.html" invalidate-session="true" /> </security:http> <!--配置第三层拦截器,主要用于权限的逻辑控制和分配--> <security:authentication-manager> <!-- 定义拦截调用器 --> <!--<security:authentication-provider user-service-ref="logical">--> <security:authentication-provider > <!-- 配置一个具体的用户数据写死,只要满足name,pwd就给指定权限 --> <security:user-service> <!--{noop}表示使用明文--> <security:user name="admin1" password="{noop}123" authorities="ROLE_ADMIN"/> </security:user-service> <!--指定密码进行加密的对象,底层自行加密--> <!--在暗号地址中进行密码验证处理。密码正确则赋予权限--> <!--<security:password-encoder ref="passwordEncoder"></security:password-encoder>--> </security:authentication-provider> </security:authentication-manager> <!--开启注解方式权限控制--> <security:global-method-security pre-post-annotations="enabled" /> <!-- 定义bean,用于逻辑分配 --> <bean id="logical" class="com.wz.LogicalService"></bean> <!--配置密码加密对象--> <bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" /> <!--<dubbo:application name="dubbo_controller" />--> <!--<!–指定服务注册中心地址–>--> <!--<dubbo:registry address="zookeeper://localhost:2181"/>--> <!--<!–批量扫描–>--> <!--<dubbo:annotation package="com.wz" />--> </beans>
此处的注解只能使用一种权限,如果想要多种如何写? json不可以 使用 and 连接多个权限
1.联合查询所有权限来降低数据库的性能消耗,否则如果权限和角色越多,访问数据库的查询次数越多。如果联合查询则仅一次就可以查询所有
优化方案:
1.mian页面优化显示用户的名字
2.显示对应的权限操作按钮,有删除就显示删除
3.如果删除失败就给出提示,权限不足(2/3 选其一)
升级自学POI : easypoi工具包、 easy excel阿里对poi性能优化项目
每次使用 创建一个对象?
方式一:
直接导出一个模板
window.location.href = '/report/BusinessReport.xlsx'; //导出在服务器中某区域存储的模板文件
方式二:
此处统一都不使用ajax,因为使用的话需要将流封装为 sjon回传
访问后台,生成数据,再通过流的方式导出
package com.wz.analysis; import com.alibaba.dubbo.config.annotation.Reference; import com.interfaces.Analysis; import com.wz.result.MessageConstant; import com.wz.result.Result; import org.apache.poi.xssf.usermodel.*; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.awt.*; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.OutputStream; import java.util.HashMap; import java.util.List; import java.util.Map; @RestController @RequestMapping("/report") public class MemberAnalysis { @Reference private Analysis analysis; //调用方法获取过去一年每月新增人数 @RequestMapping("/showMemberReport") public Map showMemberReport(){ Map map = analysis.checkMemberCountByMonths(); return map; } //调用方法获取过去一年每月新增人数 @RequestMapping("/showMealReport") public Map showMealReport(){ //调用方法获取每个套餐的预约数量 Map map = analysis.checkMealReservation(); return map; } //调用方法获取报表数据 @RequestMapping("/showReport") public Result showReport(){ try{ Map<String,Object> data = analysis.checkReportInfo(); return new Result(true,MessageConstant.GET_BUSINESS_REPORT_SUCCESS,data); }catch (Exception e){ return new Result(false,MessageConstant.GET_BUSINESS_REPORT_FAIL); } } //导出报表 @RequestMapping("/exportBusinessReport") public Result exportBusinessReport(HttpServletRequest request, HttpServletResponse response) throws Exception { try { //读入模板文件 String filePath = request.getSession().getServletContext().getRealPath("template") + File.separator + "report_template.xlsx"; //获取文件工作簿对象 XSSFWorkbook excel = new XSSFWorkbook(new FileInputStream(new File(filePath))); // //获取数据 Map<String, Object> result = analysis.checkReportInfo(); //取出返回结果数据,准备将报表数据写入到Excel文件中 String reportDate = (String) result.get("reportDate"); Integer todayNewMember = (Integer) result.get("todayNewMember"); Integer totalMember = (Integer) result.get("totalMember"); Integer thisWeekNewMember = (Integer) result.get("thisWeekNewMember"); Integer thisMonthNewMember = (Integer) result.get("thisMonthNewMember"); Integer todayOrderNumber = (Integer) result.get("todayOrderNumber"); Integer thisWeekOrderNumber = (Integer) result.get("thisWeekOrderNumber"); Integer thisMonthOrderNumber = (Integer) result.get("thisMonthOrderNumber"); Integer todayVisitsNumber = (Integer) result.get("todayVisitsNumber"); Integer thisWeekVisitsNumber = (Integer) result.get("thisWeekVisitsNumber"); Integer thisMonthVisitsNumber = (Integer) result.get("thisMonthVisitsNumber"); List<Map> hotSetmeal = (List<Map>) result.get("hotSetmeal"); // XSSFSheet sheet = excel.getSheetAt(0); // 获取公有都是行和多少列 int rowNum = sheet.getLastRowNum(); //获取所有行 for (int i = 0; i <=rowNum ; i++) { XSSFRow row = sheet.getRow(i); //获取单行对象 if(row!=null){ int colNum = row.getLastCellNum(); //获取该行的最后一个单元格。含被使用过的单元格 for (int j = 0; j < colNum; j++) { XSSFCell cell = row.getCell(j); //获取每一个单元格对象 if(cell!=null){ XSSFCellStyle cellStyle = cell.getCellStyle(); //获得样式对象 // 红底是FFFF0000 // 绿底是FF00B050 if(cellStyle.getFillForegroundColorColor()!=null) { //判断是否有颜色 String color = cellStyle.getFillForegroundColorColor().getARGBHex(); //获取颜色号数 System.out.println(cell.getStringCellValue()+"\t:"+color+"\t"+result.get(cell.getStringCellValue())); if("FFFF0000".equals(color)||"FFFFFF00".equals(color)){ //判断是否为指定颜色,此处颜色为大红色,绿色 String [] name = cell.getStringCellValue().split(":"); //分割单元格,以此判断是否已经到达热门板块 cellStyle.setFillForegroundColor(new XSSFColor(Color.YELLOW)); //填充新样式 if(name.length==2) {//判断分割后的结果是否真的到达热门板块 Map map = (Map) result.get(name[0]); //key,此处的key为hotSetmeal cell.setCellValue("" + map.get(name[1])); //value }else{ //模板中每个需要填写的单元格的值都是result查询结果的key cell.setCellValue("" + result.get(cell.getStringCellValue())); } } } } } } } //使用输出流进行表格下载,基于浏览器作为客户端下载 OutputStream out = response.getOutputStream(); response.setContentType("application/vnd.ms-excel");//代表的是Excel文件类型 response.setHeader("content-Disposition", "attachment;filename=report.xlsx");//指定以附件形式进行下载 excel.write(out); out.flush(); out.close(); // excel.close(); return null; }catch (Exception e){ e.printStackTrace(); return new Result(false,MessageConstant.GET_SETMEAL_COUNT_REPORT_FAIL); } } }
注意:1.使用表单的时候 由于指定过数据源,所以可以显示数据
但是如果在程序中执行,那么是没有办法关闭的。会有问题,所以不用
2.在生成边框时,为了没有间隙。要对每个区域进行缩小至上一个区域的下标线
3.中文不能正常显示,要使用华文宋体且加入布丁文件与jrxml模板同级
以后都尽量不使用feilds功能制作报表?
输出流 ateBytes 。tostring
XX.class. -->
getResource(“ ”) 拿同包下文件
getResource(“/ ”)拿class文件下的文件
getClassLoader.getResource(“ ”)
getClassLoader.getResource(“ /”)
入门案例
@Test public void show() throws Exception { //定义模板位置 String jrxmlPath ="F:\\ideaProject\\project\\projectHEALTH\\controller\\src\\main\\resources\\demo.jrxml"; //定义虚拟输出位置和名字 String jasperPath ="F:\\ideaProject\\project\\projectHEALTH\\controller\\src\\main\\resources\\demo.jasper"; JasperCompileManager.compileReportToFile(jrxmlPath,jasperPath); //编译模板 //定义头数据 Map paramters = new HashMap(); paramters.put("reportDate","2019-10-10"); paramters.put("company","itcast"); //定义模板主数据 List<Map> list = new ArrayList(); HashMap<Object, Object> map1 = new HashMap<>(); map1.put("name","xiaoming"); map1.put("address","beijing"); map1.put("email","xiaoming@itcast.cn"); Map map2 = new HashMap(); map2.put("name","xiaoli"); map2.put("address","nanjing"); map2.put("email","xiaoli@itcast.cn"); list.add(map1); list.add(map2); //填充数据,使用javaBean类 JasperPrint jasperPrint =JasperFillManager.fillReport(jasperPath,paramters,new JRBeanCollectionDataSource(list)); //定义输出文件位置 String pdfPath = "D:\\test.pdf"; JasperExportManager.exportReportToPdfFile(jasperPrint,pdfPath); //导出 } }
Color.YELLOW)); //填充新样式
if(name.length==2) {//判断分割后的结果是否真的到达热门板块
Map map = (Map) result.get(name[0]); //key,此处的key为hotSetmeal
cell.setCellValue("" + map.get(name[1])); //value
}else{
//模板中每个需要填写的单元格的值都是result查询结果的key
cell.setCellValue("" + result.get(cell.getStringCellValue()));
}
}
}
}
}
}
}
//使用输出流进行表格下载,基于浏览器作为客户端下载
OutputStream out = response.getOutputStream();
response.setContentType("application/vnd.ms-excel");//代表的是Excel文件类型
response.setHeader("content-Disposition", "attachment;filename=report.xlsx");//指定以附件形式进行下载
excel.write(out);
out.flush();
out.close();
// excel.close();
return null;
}catch (Exception e){
e.printStackTrace();
return new Result(false,MessageConstant.GET_SETMEAL_COUNT_REPORT_FAIL);
}
}
}
## JasperReport模拟设计器 注意:1.使用表单的时候 由于指定过数据源,所以可以显示数据 但是如果在程序中执行,那么是没有办法关闭的。会有问题,所以不用 2.在生成边框时,为了没有间隙。要对每个区域进行缩小至上一个区域的下标线 3.中文不能正常显示,要使用华文宋体且加入布丁文件与jrxml模板同级 ==以后都尽量不使用feilds功能制作报表?== 输出流 ateBytes 。tostring XX.class. --> getResource(“ ”) 拿同包下文件 getResource(“/ ”)拿class文件下的文件 getClassLoader.getResource(“ ”) getClassLoader.getResource(“ /”) 入门案例 ```java @Test public void show() throws Exception { //定义模板位置 String jrxmlPath ="F:\\ideaProject\\project\\projectHEALTH\\controller\\src\\main\\resources\\demo.jrxml"; //定义虚拟输出位置和名字 String jasperPath ="F:\\ideaProject\\project\\projectHEALTH\\controller\\src\\main\\resources\\demo.jasper"; JasperCompileManager.compileReportToFile(jrxmlPath,jasperPath); //编译模板 //定义头数据 Map paramters = new HashMap(); paramters.put("reportDate","2019-10-10"); paramters.put("company","itcast"); //定义模板主数据 List<Map> list = new ArrayList(); HashMap<Object, Object> map1 = new HashMap<>(); map1.put("name","xiaoming"); map1.put("address","beijing"); map1.put("email","xiaoming@itcast.cn"); Map map2 = new HashMap(); map2.put("name","xiaoli"); map2.put("address","nanjing"); map2.put("email","xiaoli@itcast.cn"); list.add(map1); list.add(map2); //填充数据,使用javaBean类 JasperPrint jasperPrint =JasperFillManager.fillReport(jasperPath,paramters,new JRBeanCollectionDataSource(list)); //定义输出文件位置 String pdfPath = "D:\\test.pdf"; JasperExportManager.exportReportToPdfFile(jasperPrint,pdfPath); //导出 } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。