苍穹外卖

Entity:实体,通常与数据库表对应
DTO:数据传输对象,前端后端传输数据,封装成一个DTO,通常用于程序各层之间传输数据
VO:视图对象,为前端展示数据提供对象
POJO:衍生出他们三个

问题:前端请求路径和后端所需不一致
    nginx反向代理:将前端发送的动态请求由nginx转发到后端服务器
    反向代理好处:
        提高访问速度:nginx提供缓存,如果请求同一个地址,就在nginx处返回
        进行负载均衡:将大量的请求按照指定方式,均衡的分配给集群中的服务器
        保证后端服务安全:前端只能通过nginx反向代理请求后端服务
    nginx反向代理配置方式:nginx.conf文件
        eg:监听80端口,匹配/api/ ,拼接url
        

 
nginx负载均衡的配置:

Swagger
     Knife4j是Java MVC框架集成Swagger生成Api文档的增强解决方案
        引入依赖:knife4j-spring-boot-starter
            <dependency>
                <groupId>com.github.xiaoymin</groupId>
                <artifactId>knife4j-spring-boot-starter</artifactId>
                <version>${knife4j}</version>
            </dependency>
        配置类中配置信息
    /**
     * 通过knife4j生成接口文档
     * @return
     */
    @Bean
    public Docket docket() {
        ApiInfo apiInfo = new ApiInfoBuilder()
                .title("苍穹外卖项目接口文档")
                .version("2.0")
                .description("苍穹外卖项目接口文档")
                .build();
        Docket docket = new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo)
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.sky.controller"))
                .paths(PathSelectors.any())
                .build();
        return docket;
    }
    /**
     * 设置静态资源映射,主要是为了访问doc.html
     * @param registry
     */
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/doc.html").addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
    }
    常用注解:
        @Api 类,controller,表示对类的说明 @Api(tags="员工相关接口")
        @ApiModel 类,pojo
        @ApiModelProperty 属性,描述属性信息
        @ApiOperation 方法,说明方法用途 @Api(value="员工登录")

员工管理、分类管理:
    当前端提交的数据和实体类中对应的是属性差别比较大时,建议使用DTO来封装数据
    属性复制赋值:
     BeanUtils.copyProperties(employeeDTO,employee);
    ThreadLocal并不是一个Thread,而是Thread的局部变量
        ThreadLocal为每个线程提供单独一份存储空间,具有线程隔离的效果,只有在线程内才能获取到对应的值
        每次http请求都是一次线程
        常用方法:
            set(T value) 在当前线程存值
            T get() 取值
            remove() 移除
    日期格式化:
        方式一:在pojo属性加入注解@JsonFormat(pattern="yyy-MM-dd HH:mm:ss)
        方式二:在webMVCConfiguration中扩展Spring MVC的消息转换器,统一对日期类型进行格式化处理
    /**
     * 扩展SpringMVC MVC消息展示器
     * 作用:后端转给前端的数据进行统一的处理,比如日期格式化
     *  项目启动的时候自动加载
     * @param converters
     */
    @Override
    protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        log.info("扩展消息展示器");
        //创建消息转换器对象
        final MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        //需要为消息转换器设置一个转换器对象,对象转换器可以将java对象序列化为json数据
        converter.setObjectMapper(new JacksonObjectMapper());
        //将自己的消息转换器加入到集合中
        converters.add(0, converter);
    }
公共字段自动填充
    比如create_time create_user update_time update_user
    1.自定义注解,用于标识
    2.Aop

/**
 * 自定义切面,实现公共字段自动填充
 */
@Slf4j
public class AutoFillAspect {
    //切入点
    @Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")
    public void autoFillPointCut() {
    }

    //前置通知,在通知中为公共字段自动填充
    @Before("autoFillPointCut()")
    public void autoFill(JoinPoint joinPoint) {
        log.info("为公共字段自动填充");
        //获取到当前被拦截的方法上的数据库操作类型,也就是update/insert
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();//获取方法签名对象
        final AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);//获取方法上的注解对象
        final OperationType operationType = autoFill.value();//获取数据库操作类型
        //获取当前被拦截的方法的参数--实体对象
        final Object[] args = joinPoint.getArgs();
        if (args == null || args.length == 0) {
            return;
        }
        final Object entity = args[0];
        //准备填充
        final LocalDateTime now = LocalDateTime.now();
        final Long currentId = BaseContext.getCurrentId();
        //根据当前不同的操作类型,为当前对应的属性通过反射赋值
        if (Objects.equals(operationType, OperationType.INSERT)) {
            try {
                final Method setCreateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);
                final Method setCreateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, Long.class);
                final Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
                final Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);

                setCreateTime.invoke(entity, now);
                setCreateUser.invoke(entity, currentId);
                setUpdateTime.invoke(entity, now);
                setUpdateUser.invoke(entity, currentId);
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else if (Objects.equals(operationType, OperationType.UPDATE)) {
            try {
                final Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
                final Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
                setUpdateTime.invoke(entity, now);
                setUpdateUser.invoke(entity, currentId);
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    }
}
Redis
    Redis入门
        简介:
            基于 内存 的key-value结构数据库
                基于内存存储,读写性能高
                适合存储热点数据(热点商品、咨询等,特定时间点有大量用户访问)
            启动:redis-server redis.windows.conf
            客户端连接:(-h host -p port 可省略,没有用户的概念)redis-cli.exe -h localhost -p 6379 -a password
            密码配置:requirepass 123456
    Redis数据类型
        Redis存储的是key-value结构的数据,其中key是字符串类型,value有5种常见数据类型:
            字符串:string
            哈希:hash (field1 value1),也叫散列,类似于Java中HashMap
            列表:list 类似LinkedList 按照插入排序排序,可以有重复元素
            集合:set 类似HashSet,无序集合,没有重复元素
            有序集合:sorted set / zset 集合中每个元素关联一个分数score,根据分数升序排序,没有重复元素

    常用命令(不区分大小写)
        字符串操作命令:
            set key value 设置指定key的值,相当于插入
            get key 查询
            setex key seconds value 设置指定key的值,并将key的过期时间设置为seconds秒
            setnx key value 只有在key不存在时设置key的值

        哈希:redis hash是一个string类型的field和value的映射 表 ,hash特别适合存储对象,常用命令:
            hset key field value 将哈希表key中的字段field的值设为value
            hget key field 获取存储在哈希表中指定字段的值
            hdel key field 删除存储在哈希表中指定字段的值
            hkeys key 获取哈希表中所有字段
            hvals key 获取哈希表中所有值

        列表:redis列表是简单的字符串列表,按照插入顺序排序
            (l代表的是left,而不是list)
            lpush key value1[value2] 将一个或多个值插入到列表(队列)头部
            lrange key start stop 获取指定范围内的元素
                lrange key 0 -1 获取所有元素
            rpop key 移除并获取列表最后一个元素(最右侧,最先插入的)
            llen key 获取列表长度

        集合:string类型无序集合,集合成员唯一
            sadd key member1[member2] 向集合添加一个或多个成员
            smembers key 返回集合中所有成员
            scard key 返回集合的成员数量
            sinter key1[key2] 返回给定所有集合的交集
            sunion key1[key2] 返回所有给定集合的并集
            srem key member1[member2] 删除集合中的成员

        有序集合:string类型元素集合,不允许重复。每个元素会关联一个double类型的分数
            zadd key score1 member1[score2 member2] 添加
            zrange key start stop [withscores]  通过索引区间返回,加上withscores会返回分数
            zincrby key increment member 有序集合中对指定成员分数加上增量increment
            zrem key member [member1...] 移除

        通用命令:
            keys pattern 查找给定模式(pattern)的key 比如: keys * 返回所有key
            exists key 检查key是否存在
            type key 返回key所储存的值的类型
            del key 删除
Java中操作Redis
    Redis的Java客户端:
        Jedis
        Lettuce
        Spring Data Redis:是Spring的一部分,对Redis进行封装
    操作步骤:
        1.导入spring-boot-starter-data-redis
        2.配置Redis数据源 port host password [database]
        3.编写配置类,创建RedisTemplate对象
        4.通过RedisTemplate对象操作Redis
@Configuration
@Slf4j
public class RedisConfiguration {
    @Bean
    public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        log.info("开始创建redis模板对象");
        final RedisTemplate redisTemplate = new RedisTemplate();
        //设置redis的连接工厂对象
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        //设置redis key的序列化器
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        return redisTemplate;
    }
}
    @Autowired
    private RedisTemplate redisTemplate;

    @Test
    public void testRedisTemplate() {
        System.out.println(redisTemplate);
        ValueOperations valueOperations = redisTemplate.opsForValue();
        HashOperations hashOperations = redisTemplate.opsForHash();
        ListOperations listOperations = redisTemplate.opsForList();
        SetOperations setOperations = redisTemplate.opsForSet();
        ZSetOperations zSetOperations = redisTemplate.opsForZSet();

    }

    @Test
    public void testString() {
        final ValueOperations valueOperations = redisTemplate.opsForValue();
        //set get setex setnx
        valueOperations.set("name", "jack");
        final String name = (String) valueOperations.get("name");
        //指定时间销毁
        valueOperations.set("code", "1234", 30L, TimeUnit.SECONDS);
        valueOperations.setIfAbsent("lock", "1");

    }

    @Test
    public void testHash() {
        final HashOperations hashOperations = redisTemplate.opsForHash();
        //hset hget hdel hkeys hvals
        hashOperations.put("100", "name", "tom");
        hashOperations.put("100", "age", "20");
        hashOperations.put("100", "gender", "1");
        hashOperations.put("100", "address", "北京");

        System.out.println((String) hashOperations.get("100", "name"));
        //删除key对应的value
        hashOperations.delete("100", "gender", "address");
        //获取所有key
        System.out.println(hashOperations.keys("100"));
        //获取key对应所有value
        System.out.println(hashOperations.values("100"));

    }

    @Test
    public void testList() {
        final ListOperations listOperations = redisTemplate.opsForList();
        //lpust lrange rpop llen
        listOperations.leftPushAll("mylist","a","b","c");
        listOperations.leftPush("mylist","d");

        System.out.println(listOperations.range("mylist", 0, -1));

        listOperations.rightPop("mylist");

        System.out.println(listOperations.size("mylist"));

    }

    @Test
    public void testSet() {
        final SetOperations setOperations = redisTemplate.opsForSet();
        //sadd smenbers scard sinter sunion srem
        setOperations.add("set1","a","b","c","d");
        setOperations.add("set2","a","b","x","y");

        System.out.println(setOperations.members("set1"));
        //scard
        System.out.println(setOperations.size("set1"));

        //sinter交集 sunion并集
        System.out.println(setOperations.intersect("set1", "set2"));
        System.out.println(setOperations.union("set1", "set2"));

        setOperations.remove("set1","a","b");
    }

    @Test
    public void testZSet() {
        final ZSetOperations zSetOperations = redisTemplate.opsForZSet();
        //zadd zrange zincrby zrem
        zSetOperations.add("zset1","a",10);
        zSetOperations.add("zset1","b",12);
        zSetOperations.add("zset1","c",9);

        System.out.println(zSetOperations.range("zset1", 0, -1));

        zSetOperations.incrementScore("zset1","a",2);

        zSetOperations.remove("zset1","c");

    }
    @Test
    public void testCommon(){
        //keys exists type del
        Set keys=redisTemplate.keys("*");

        System.out.println(redisTemplate.hasKey("name"));

        for(Object key:keys){
            final DataType type = redisTemplate.type(key);
            System.out.println(type.name());
        }

        redisTemplate.delete("mylist");
    }
HttpClient
    介绍:
        提供http协议的客户端编程工具包,构造http请求
    依赖:httpclient
    核心API:
        HttpClient 发送http请求
        HttpClients  获取http对象
        CloseableHttpClient 实现httpClient
        HttpGet get请求
        HttpPost
    发送请求的方式:
        创建HttpClient对象
        创建Http请求对象(HttpGet/Post)
        调用HttpClient的execute方法发送请求
    aliyun-sdk-oss底层导入了httpclient依赖
    //通过httpClient发送get请求
    @Test
    public void testGet() throws IOException {
        //创建httpClient对象
        CloseableHttpClient httpClient = HttpClients.createDefault();
        //创建请求对象
        HttpGet httpGet = new HttpGet("http://localhost:8080/user/shop/status");
        //发送请求,接收响应结果
        CloseableHttpResponse response = httpClient.execute(httpGet);

        int statusCode = response.getStatusLine().getStatusCode();
        System.out.println("服务端返回的状态码为:" + statusCode);

        //服务端返回的数据
        HttpEntity entity = response.getEntity();
        String body = EntityUtils.toString(entity);
        System.out.println("服务端返回的数据为:" + body);

        //关闭资源
        response.close();
        httpClient.close();

    }

    @Test
    public void testPost() throws IOException {
        CloseableHttpClient httpClient = HttpClients.createDefault();
        HttpPost httpPost = new HttpPost("http://localhost:8080/admin/employee/login");

        JSONObject jsonObject = new JSONObject();
        jsonObject.put("username", "admin");
        jsonObject.put("password", "123456");

        StringEntity entity = new StringEntity(jsonObject.toString());
        //指定请求编码方式
        entity.setContentEncoding("utf-8");
        //数据格式
        entity.setContentType("application/json");
        httpPost.setEntity(entity);

        CloseableHttpResponse response = httpClient.execute(httpPost);
        int code = response.getStatusLine().getStatusCode();
        HttpEntity entityResponse = response.getEntity();
        String body = EntityUtils.toString(entityResponse);
        System.out.println("响应码:"+code+"\n"+"响应数据:"+body);
    }
缓存商品、购物车
    缓存菜品:
       http-后端服务-读取缓存-查询数据库-载入缓存
    缓存分析:
        每个分类下的菜品保存一份缓存数据
        数据库中菜品数据有变更时及时清理缓存数据
    Spring Cache
        是一个框架,实现了基于注解的缓存功能。
        Spring Cache提供了一层抽象,底层可以切换不同的缓存实现,例如:
            EHCache
            Caffeine
            Redis
        导入依赖:spring boot starter cache
        常用注解:
            @EnableCaching 启动类 开启缓存注解功能
            @Cacheable 方法 在方法执行前先查询缓存中是否有数据,如果有数据,则直接返回缓存数据;如果没有缓存数据,调用方法并将方法的返回值放到缓存中
            @CachePut 方法 将方法的返回值放到缓存中
            @CacheEvict 将一条或多条数据从缓存删除
        insertController:
            spEL
            @CachePut(cacheNames="userCache",key="#形参名.主键属性")
                或者:key="#result.id" result表示函数返回值
                或者:key="#a0.id" a0或p0表示形参第一个参数
            如果使用Spring Cache缓存数据,key的生成:userCache::key树形结构
            前提:主键返回
        selectController:
            @Cacheable(cacheName="userCache",key="#id")
        deleteController:
            @CacheEvict(cacheNames="userCache",key="#id")
            清理所有:@CacheEvict(cacheNames="userCache",allEntries="true")

Spring Task与WebSocket
    Spring Task是Spring家族提供的任务调度工具,可以按照约定时间自动执行某个代码逻辑。
        作用:定时任务框架
        应用场景:超时订单的处理 派送中订单处理
        cron表达式:
            cron表达式是一个字符串,通过cron表达式可以定义任务触发的时间
            构成规则:分为6或7个域,每个域代表一个含义
            每个域的含义:秒、分钟、小时、日、月,周(星期)、年(可选)
            https://cron.qqe2.com/
            步骤:
                导入spring-context
                在启动类上添加@EnableScheduling
                自定义定时任务类
                    该类需要交给Spring容器
                    方法注解@Scheduled(cron="")
    WebSocket
        webSocket是基于TCP协议的一种新的 网络协议。实现了浏览器与服务器全双工通信-浏览器与服务器只需要完成一次握手,两者之间就能创建 持久性 连接,并进行双向数据传输。
        Http:请求响应模式 客户端请求服务器,服务端响应客户端。 短连接,一次请求一次连接
        相同点:都是基于TCP连接
        应用场景:视频弹幕 网页聊天 实况更新
        步骤:
            导入依赖spring-boot-starter-websocket
            导入WebSocket服务端组件webSocketServer,用于和客户端通信
package com.sky.webSocket;

import org.springframework.stereotype.Component;

import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

/**
 * WebSocket服务
 */
@Component
@ServerEndpoint("/ws/{sid}")
public class WebSocketServer {

    //存放会话对象
    private static Map<String, Session> sessionMap = new HashMap();

    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("sid") String sid) {
        System.out.println("客户端:" + sid + "建立连接");
        sessionMap.put(sid, session);
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(String message, @PathParam("sid") String sid) {
        System.out.println("收到来自客户端:" + sid + "的信息:" + message);
    }

    /**
     * 连接关闭调用的方法
     *
     * @param sid
     */
    @OnClose
    public void onClose(@PathParam("sid") String sid) {
        System.out.println("连接断开:" + sid);
        sessionMap.remove(sid);
    }

    /**
     * 群发
     *
     * @param message
     */
    public void sendToAllClient(String message) {
        Collection<Session> sessions = sessionMap.values();
        for (Session session : sessions) {
            try {
                //服务器向客户端发送消息
                session.getBasicRemote().sendText(message);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

}
            导入配置类WebSocketConfiguration,注册WebSocket服务端组件
package com.sky.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

/**
 * WebSocket配置类,用于注册WebSocket的Bean
 */
@Configuration
public class WebSocketConfiguration {

    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }

}
Apache POI
    在Java中操作Office各种文件的开源项目
    应用场景:
        银行交易明细
        导出Excel报表
    使用:
        依赖:poi 和 poi-ooxml
package com.sky.test;

import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.junit.jupiter.api.Test;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class POITest {
    @Test//创建Excel文件
    public void testExcelWrite() throws IOException {
        //创建一个excel文件(在内存中)
        XSSFWorkbook excel = new XSSFWorkbook();
        //在excel文件中创建一个sheet页
        XSSFSheet sheet = excel.createSheet();
        //在sheet页中创建行,0开始
        XSSFRow rowFirst = sheet.createRow(0);
        //在行上面创建单元格
        XSSFCell cell00 = rowFirst.createCell(0);
        XSSFCell cell01 = rowFirst.createCell(1);
        //向单元格写入内容
        cell00.setCellValue("姓名");
        cell01.setCellValue("城市");

        XSSFRow rowSecond = sheet.createRow(1);
        XSSFCell cell10 = rowSecond.createCell(0);
        XSSFCell cell11 = rowSecond.createCell(1);
        cell10.setCellValue("张三");
        cell11.setCellValue("北京");

        //将内存中excel文件写入磁盘,没有会创建
        FileOutputStream fileOutputStream = new FileOutputStream("D:\\Users\\JavawebCode\\web四期\\lib\\1、黑马程序员Java项目《苍穹外卖》企业级开发实战\\资料\\day12\\Excel\\info.xlsx");
        excel.write(fileOutputStream);
        excel.close();
        fileOutputStream.close();
    }

    @Test
    public void testReadExcel() throws IOException {
        //读取磁盘上的excel文件
        XSSFWorkbook excel = new XSSFWorkbook(new FileInputStream(new File("D:\\Users\\JavawebCode\\web四期\\lib\\1、黑马程序员Java项目《苍穹外卖》企业级开发实战\\资料\\day12\\Excel\\info.xlsx")));
        //读取sheet页
        XSSFSheet sheetOne = excel.getSheetAt(0);
        //读取sheet最后有文字的一行的行号
        int lastRowNum = sheetOne.getLastRowNum();
        for (int i = 0; i <= lastRowNum; i++) {
            //获取每一行对象
            XSSFRow row = sheetOne.getRow(i);
            //获取单元格对象
            short lastCellNum = row.getLastCellNum();
            for (int j = 0; j < lastCellNum; j++) {
                XSSFCell cell = row.getCell(j);
                String value = cell.getStringCellValue();
                System.out.print(value + "\t");
            }
            System.out.println();
        }
        excel.close();

    }
}
实际实现步骤:
    1.设计Excel模板文件
    2.查询数据库
    3.将查询到的数据写入模板文件
    4.通过输出流将Excel文件下载到客户端浏览器
    @Override
    public void export(HttpServletResponse response) {
        //1.查数据库获得数据
        LocalDate dateBegin = LocalDate.now().minusDays(30);
        LocalDate dateEnd = LocalDate.now().minusDays(1);

        BusinessDataVO businessDataVO = workspaceService.getBusinessData(
                LocalDateTime.of(dateBegin, LocalTime.MIN),
                LocalDateTime.of(dateEnd, LocalTime.MAX)
        );
        //2.将数据写入
        InputStream in = this.getClass().getClassLoader().getResourceAsStream("template/运营数据报表模板.xlsx");
        try {
            //基于模板文件创建新的excel文件
            XSSFWorkbook excel = new XSSFWorkbook(in);
            //获取表格文件的sheet页
            XSSFSheet sheet = excel.getSheet("Sheet1");
            //填充数据-时间 第二行
            sheet.getRow(1).getCell(1).setCellValue("时间:"+dateBegin+"至"+dateEnd);
            //营业额 第4行 第3格

            //获得第4行
            XSSFRow row = sheet.getRow(3);
            row.getCell(2).setCellValue(businessDataVO.getTurnover());
            row.getCell(4).setCellValue(businessDataVO.getOrderCompletionRate());
            row.getCell(6).setCellValue(businessDataVO.getNewUsers());

            //获得第5行
            row = sheet.getRow(4);
            row.getCell(2).setCellValue(businessDataVO.getValidOrderCount());
            row.getCell(4).setCellValue(businessDataVO.getUnitPrice());


            //明细数据
            for (int i = 0; i < 30; i++) {
                LocalDate date = dateBegin.plusDays(i);
                //查询某一天的营业数据
                BusinessDataVO businessData = workspaceService.getBusinessData(LocalDateTime.of(date, LocalTime.MIN), LocalDateTime.of(date, LocalTime.MAX));

                //获得某一行
                row = sheet.getRow(7 + i);
                row.getCell(1).setCellValue(date.toString());
                row.getCell(2).setCellValue(businessData.getTurnover());
                row.getCell(3).setCellValue(businessData.getValidOrderCount());
                row.getCell(4).setCellValue(businessData.getOrderCompletionRate());
                row.getCell(5).setCellValue(businessData.getUnitPrice());
                row.getCell(6).setCellValue(businessData.getNewUsers());

            }

            //3.通过输出流下载到客户端浏览器
            ServletOutputStream outputStream = response.getOutputStream();
            excel.write(outputStream);
            excel.close();
            outputStream.close();
            in.close();
        } catch (IOException e) {
            e.printStackTrace();
        }


    }
操作:
    Debug:查看选中代码结果
        箭头:上 调上次命令
        右键->Evaluate Expression

             

需要开启的:
    @EnableScheduling Spring Task
    @EnableCaching Spring Cache
    @EnableTransactionManagement //开启注解方式的事务管理

   

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值