当前位置:   article > 正文

健康体检中心_$(function () { var wd = 200; $(".el-main").css('w

$(function () { var wd = 200; $(".el-main").css('width', $('body').width() -

传智健康

项目介绍

​ 健康管理机构的业务系统

​ 传统的互联网项目(后端系统,前端微信网页)

开发人员应该需要的资料

​ 1.需求说明书PRD【含功能大纲,功能详情,流程图,性能需求】、产品原型图

​ 2.UI:原型图并非最终效果图,最终要过要以UI为准。所以如果有了也需要拿到

​ 3.与前端工作人员对接接口名等参数

开发过程

  • 需求分析:产品人员做,产出PRD(含功能大纲,功能详情,流程图,性能需求)、产品原型图

  • 设计(概设和详设): 架构师做,产出概设和详设(状态机,类图,时序图)

  • 编码 : 程序员,产出功能代码(源代码,功能的单元测试代码)

  • 测试 :测试人员,产出测试报告(测试用例[使用中的各种情况],功能测试报告,性能测试报告,安全测试报告,集成测试报告)

  • 运维 : 程序员,对功能进行升级维护

在这里插入图片描述

项目结构图

  • 工程目录结构

    在这里插入图片描述

    在这里插入图片描述

  • 项目技术支持

    在这里插入图片描述

待解决问题

1.搞清楚Controller、RestController、ResponseBody的区别(已解决)

项目BUG汇总

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
  • 1

​ 原因:返回结果不能正常转换为json

​ 解决: 需要json依赖包

  1. 业务层可以正常获得结果 在返回到控制层时报错 超时

    原因:网络波动与自己电脑有关

    解决: 在业务层和控制层都添加timeout的时长

  2. 动态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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

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')")
  • 1

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 那么写在控制层。其余情况可以些在业务层,可以方便打包复用。

  1. 每一张表都必须要有明确的字段含义!

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; //完成赋值
        // 只要是一个对象类型就可以使用[]进行赋值,有覆盖,无创建
        } //完成赋值 
// 只要是一个对象类型就可以使用[]进行赋值,有覆盖,无创建
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

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 关键字进行查询

第一阶段–后台开发

开发模块功能

1) 检查项/组管理模块

导入数据库和表单
		数据库创建时必须指定字符集为utf8mb4  作用:使用手机端时 支持emoj 四个字符
  • 1

​ 使用PMD ,对每一个表的每一个字段的含义和使用操作做出说明备注(规范)

数据库表 varchar(32) 存储 32个字母 16个字符(2字节) 8个表情(4字节)

​ 导入实体类

​ 导入公共资源

​ 1.返回前端的状态类型对象

​ 2.返回前端的结果对象

​ 3.分页对象 【总数,当前页结果】

​ 4.按条件查询对象

​ 5.基础静态资源

PowerDesigner 制表设计

查看项目第一天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,压力测试软件。大量为数据库表生产数据

Element UI 前端设计

使用任何组件 都需要先创建对象

​ 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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
下拉菜单

目标: 学习组件 关注 属性和类型

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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
<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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
导航菜单

在官网中查看

message消息提示

在官网中查看更多

内置对象 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();
}
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
表格

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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 奇偶行不同颜色(斑马纹)stripe

  • 背景色的变更 :row-class-name=“tableRowClassName”

    //提供方法变更颜色
    tableRowClassName({row, rowIndex}) {
            if (rowIndex === 1) {
              return 'warning-row';
            } else if (rowIndex === 3) {
              return 'success-row';
            }
            return '';
          }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
  • 表头固定,设置表格的高度即可height=“250”

  • 列固定/冻结,在列上设置fixed,如果是右固定则是fixed=‘right’

  • 表格生成序号

    <el-table-column type="index" width="50"></el-table-column>  // type="index"
    
    • 1
  • 表格列的单选 highlight-current-row

  • 表格的多选

    <el-table-column type="selection" width="55"> </el-table-column>// type="selection"
    
    • 1
  • 表格按照列头排序,增加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();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

​ 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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
分页组件
  • sizes,每页多少条
  • total
  • prev
  • pager,快速点击的页码
  • next
  • jumper,跳转页
<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();//调用查询
 },
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

代码生成工具

作用:在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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  <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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

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;    
           },
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

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);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

存储方案

方案一

	nginx  反向代理   一台简单的高性能服务器 偏向于静态资源部署 tomcat偏向于动态资源部署
  • 1

​ 场景:企业网站自己需要使用的图片数据

方案二:

​ 开源分布式存储系统,如 Fastdfs (阿里开源)、HDFS

​ 场景:用户使用过程中上传的图片。头像,朋友圈图片等。

方案三:

​ 云存储。 如 七牛云,百度云 。费用高昂

​ 场景:大型项目,例京东淘宝。

鉴权

使用步骤:

1)上传

​ 1.获取本地服务器对象

​ 2.创建七牛云对象

​ 3.鉴权(仓库,仓库AKey、SKey)

​ 4.获得权限认证

​ 5.上传 (不指定key 默认以hash值作为文件名)

​ 6.解析上传结果

2)删除

​ 1.获取本地服务器对象

​ 2.创建七牛云对象

​ 3.鉴权(仓库,仓库AKey、SKey)

​ 4.指定删除文件名

​ 5.传输指令

​ 6.解析结果

定时器Quartz

期间需要由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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

​ 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);
            }
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

​ 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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

Cron表达式

七个位置 最后一位可以没有 空格分割

例如: 每十秒执行一次 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

Apache POI上传文件

​ 可以操作所有微软办公自动软件,只是更多的是用来操作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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

读取数据

//获得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);
      }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

写出数据

//创建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();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

工具类

判断该单元格是什么类型的方法可用,其他慎用

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;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180

文件上传vue

<!--文件上传到后端,直接进行逻辑判断保存操作-->
<!--上传前会执行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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

业务层插入操作

@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);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

日历

前端解析代码和数据库中的字段不一致的时候 可以用map进行封装返回 因为没有直接可用的pojo,偷懒。

批量添加数据

预约设置

//内置方法,转换日期格式
this.formatDate(day.getFullYear(),day.getMonth()+1,day.getDate())
  • 1
  • 2

第二阶段–移动端开发

套餐级联查询

ResultMap多级联查询

当封装的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);
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

​ 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;
                        }
                    });
                }
            }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

5.检查项的dao

​ 解析:1.由上层调用,在findItemByGroupId方法进入,开始查询每一个检查项信息。

​ 查询结果封装进自己的resultType的CheckItem。由于上层是list集合所以会变成一个个元素存取

​ 2.至此查询完毕,实现页面模型的多json嵌套结构

	setmeal:{
                img:"",
                name:"",
                checkGroups:[
                    {
                        name:"",
                        code:"",
                        checkItems:[
                            {name:"",code:""}
                        ]
                    }
                ]
            }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
<!--前端,级联查询检查项-->
<select id="findItemByGroupId" resultType="CheckItem">
  SELECT * FROM t_checkitem WHERE id IN (SELECT checkitem_id FROM t_checkgroup_checkitem WHERE checkgroup_id=#{id})
</select>
  • 1
  • 2
  • 3
  • 4

短信发送

	阿里云短信服务

	==观看代码实现==
  • 1
  • 2
  • 3

FreeMarker静态页面

官网:http://freemarker.foofun.cn/ 查询帮助文档

​ 作用:使网页可以更快响应,在进行增删改操作后,生成一个静态页面以后续直接调用

​ 定义:java语言开发的模板引擎

​ 场景 : 页面静态化生成(将一个动态数据加载的页面生成为html的过程,称为静态化)

​ 如:合同模板、邮件模板

​ 组成: template 基本模板

​ model 模板需要使用的数据

入门案例

1.导包

<!--FreeMarker-->
<dependency>
    <groupId>org.freemarker</groupId>
    <artifactId>freemarker</artifactId>
    <version>2.3.30</version>
</dependencml
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

​ 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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

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();  //关流,不然会引起内存泄漏
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

FreeMarker指令

所有指令都是以#开始

  • assign
    • 在模板页面中定义一个变量
    • 使用:在调试模板时使用,可以定义对象或者字符串,优先级高于后端传入的变量数据
<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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • include
    • 包含其他的模板文件进入主模板文件
    • 使用:在java中的设置模板所在路径开始找其他路径的模板文件,包含进主模板中
<html>
<head>
  <title>Welcome!</title>
</head>
<body>
  <h1>Welcome ${user}!</h1>
    <#assign shit="大牛">
   <h1>GOAWAY ${shit}!</h1>
   
   <!-- 在java代码中指定文件存放的是位置找到该文件。并载入其内容 -->
	<#include "he.ftl">  
</body>
</html>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • if
    • 判断条件,可以在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>
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
  • list
    • 遍历集合,java需要使用list集合来存储map
    • list else 如果list为空执行 else
    • list … as …
   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);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

ftl文件list输出格式 以as作为遍历每一个

<#list goods as good>
商品名称: ${good.name} 价格:${good.price}<br>
</#list>
  • 1
  • 2
  • 3
  • 内置函数
    • 此处只对日期做出示例 官方文档中查询
    • 使用时 要在变量后使用 ?函数名
<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
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

生成静态页面

时机: 在任何本体或者子项发生改变的时候生成一次静态页面,静态页面是是使用代码后台直接生成的,不需要使用异步调用!

注意事项:

​ 1.指定输出的模板存储位置

​ 2.代码中自动注入对象为FreeMarkerConfigurer

@Autowired
private FreeMarkerConfigurer freeMarkerConfigurer;//注入freemarker映射工厂
  • 1
  • 2

​ 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中定义,传入数据生成到指定位置

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

步骤:

1.导包(此处最新版)
<!--FreeMarker-->
<dependency>
    <groupId>org.freemarker</groupId>
    <artifactId>freemarker</artifactId>
    <version>2.3.30</version>
</dependencml
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
2.生成套餐模板
<!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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
3.生成检查组模板
1.json格式回传(推荐)

​ 此处使用的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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
2.常规回传

(占位符都使用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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
4.工具类

作用:在增删改操作完成之后直接执行方法,方法内部进行查询生成模板。前端自动访问模板。

 //生成模板方法
    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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
5.配置文件

​ 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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

​ 2.创建地址的外部文件

outputPath=F:/ideaProject/project/projectHEALTH/mobile/src/main/webapp/yemian //项目中webapp在磁盘中的绝对路径
  • 1
6.修改初始访问接口

在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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

权限控制(RBAC)

cookie为什么要设置路径为/ 这样不是所有路径都会携带该cookie? 常规操作 所有的页面都需要验证

拦截规则/**拦截所有请求,以前使用的/ * 安全框架独有

使用注解,认证之后这个就可以访问其他方法,是使用value字符串内存中定义的吗

配置form和loginout标签,如果还有其他的自定义的页面也是要放行的,怎么弄?

<security:http security="none" pattern="/pages/a.html"></security:http>
  • 1

如何从spring中跳转到service页面

<security:authentication-provider  user-service-ref="logical">
  • 1

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结尾的请求

步骤

pom.xml

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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
web.xml

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>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
spring.xml
<?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" />-->
    <!--&lt;!&ndash;指定服务注册中心地址&ndash;&gt;-->
    <!--<dubbo:registry address="zookeeper://localhost:2181"/>-->
    <!--&lt;!&ndash;批量扫描&ndash;&gt;-->
    <!--<dubbo:annotation package="com.wz" />-->
</beans>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103

项目权限整改

此处的注解只能使用一种权限,如果想要多种如何写? json不可以 使用 and 连接多个权限

1.联合查询所有权限来降低数据库的性能消耗,否则如果权限和角色越多,访问数据库的查询次数越多。如果联合查询则仅一次就可以查询所有

优化方案:

​ 1.mian页面优化显示用户的名字

​ 2.显示对应的权限操作按钮,有删除就显示删除

​ 3.如果删除失败就给出提示,权限不足(2/3 选其一)

文件导出

升级自学POI : easypoi工具包、 easy excel阿里对poi性能优化项目

每次使用 创建一个对象?

方式一:

直接导出一个模板

window.location.href = '/report/BusinessReport.xlsx';  //导出在服务器中某区域存储的模板文件
  • 1

方式二:

此处统一都不使用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
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140

JasperReport模拟设计器

​ 注意: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); //导出

  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

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();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

// 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); //导出

  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/319358
推荐阅读
相关标签
  

闽ICP备14008679号