当前位置: 首页 > news >正文

网站到期怎么续费苏州网站建设制度

网站到期怎么续费,苏州网站建设制度,网页与网站的区别是什么,网页编辑文字第一天#xff1a; 基本内容如下#xff1a; 从gitee拉取对应的基础代码。做好配置相关工作。测试页面是否可以正常打开。 无问题 需要学习的内容#xff1a;spring security 了解到这个框架的基础作用大概是#xff1a;管理请求路径#xff0c;管理用户权限#xf…第一天 基本内容如下 从gitee拉取对应的基础代码。做好配置相关工作。测试页面是否可以正常打开。 无问题 需要学习的内容spring security 了解到这个框架的基础作用大概是管理请求路径管理用户权限用于登陆安全的保护。 第二天 分配任务我需要实现的是预约功能相关的代码开发。需要分析表结构书写接口文档。然后开发对应的代码。 基本内容如下 1.理解五张表的关系 套餐表 套餐体验组关联表 体验组表 体验组体验项关联表  体验项目表 三张表两张中间表。互为多对多关系。 2.编写接口文档 问题请求路径等不明确首次写接口文档不熟练。返回内容请求内容等不明确。。。 解决方式学习看懂前端代码通过查看pages目录下的对应页面内容找到对应的请求路径设计了接口文档。 3.编写检查项表的crud分页查询。 基础crud问题不大。wu 小问题在于不熟悉代码结构。没有仔细查看返回值pageresult类。导致分页查询出现问题。 sql中我们直接对queryString做模糊查询 select idfindPage resultTypecom.itheima.pojo.vo.CheckItemVO select id,code,name,sex,age,remark from t_checkitemwhereif testqueryString!null and queryString!and (code like concat(%,#{queryString},%) or name like concat(%,#{queryString},%))/if/where /select 和之前手打的苍穹外卖代码对比查看了一下发现这样写其实挺妙的 在传智健康中我们定义了下面这样一个类用于辅助实现分页查询。 /*** 封装查询条件*/ public class QueryPageBean implements Serializable{private Integer currentPage;//页码private Integer pageSize;//每页记录数private String queryString;//查询条件对应的其实是苍穹外卖中各种分页查询的DTO package com.sky.dto;import lombok.Data;import java.io.Serializable;Data public class DishPageQueryDTO implements Serializable {private int page;private int pageSize;private String name;//分类idprivate Integer categoryId;//状态 0表示禁用 1表示启用private Integer status;}这里相当于可以少写很多DTO而将查询条件直接写在了SQL脚本当中。 if testqueryString!null and queryString!and (code like concat(%,#{queryString},%) or name like concat(%,#{queryString},%))/if 省略了很多代码。学到了。 需要学习前后端联调相关知识。 第三天 基本内容如下 1.学习前后端联调前后端联调关键步骤、必要性与有效实践-CSDN博客 总结查看数据能否正常返回统一请求方式请求路径。 2.完成套餐相关和检查组相关的基础代码。 注意点需要实现阿里云oss的图片上传功能。 基础配置 package com.itheima.config;import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component;Data Component ConfigurationProperties(prefix health.alioss) public class AliOssProperties {private String endpoint;private String accessKeyId;private String accessKeySecret;private String bucketName;} package com.itheima.config;import com.itheima.common.utils.AliOssUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;/*** 配置类用于创建AliOssUtil对象*/ Slf4j Configuration public class OssConfiguration {BeanConditionalOnMissingBeanpublic AliOssUtil aliOssUtil(AliOssProperties aliOssProperties){log.info(开始创建阿里云文件上传工具类对象{},aliOssProperties);return new AliOssUtil(aliOssProperties.getEndpoint(),aliOssProperties.getAccessKeyId(),aliOssProperties.getAccessKeySecret(),aliOssProperties.getBucketName());} } 以及你的pom依赖和yml文件当中的四项配置。 还有这个工具类 import com.aliyun.oss.ClientException; import com.aliyun.oss.OSS; import com.aliyun.oss.OSSClientBuilder; import com.aliyun.oss.OSSException; import lombok.AllArgsConstructor; import lombok.Data; import lombok.extern.slf4j.Slf4j;import java.io.ByteArrayInputStream;Data Slf4j AllArgsConstructor public class AliOssUtil {private String endpoint;private String accessKeyId;private String accessKeySecret;private String bucketName;/*** 文件上传** param bytes* param objectName* return*/public String upload(byte[] bytes, String objectName) {// 创建OSSClient实例。OSS ossClient new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);try {// 创建PutObject请求。ossClient.putObject(bucketName, objectName, new ByteArrayInputStream(bytes));} catch (OSSException oe) {log.error(Caught an OSSException, which means your request made it to OSS, but was rejected with an error response for some reason.);log.error(Error Message:{}, oe.getErrorMessage());log.error(Error Code:{},oe.getErrorCode());log.error(Request ID:{},oe.getRequestId());log.error(Host ID:{}, oe.getHostId());} catch (ClientException ce) {log.error(Caught an ClientException, which means the client encountered a serious internal problem while trying to communicate with OSS, such as not being able to access the network.);log.error(Error Message:{},ce.getMessage());} finally {if (ossClient ! null) {ossClient.shutdown();}}//文件访问路径规则 https://BucketName.Endpoint/ObjectNameStringBuilder stringBuilder new StringBuilder(https://);stringBuilder.append(bucketName).append(.).append(endpoint).append(/).append(objectName);log.info(文件上传到:{}, stringBuilder.toString());return stringBuilder.toString();}public void delete(String objectName) {OSS ossClient new OSSClientBuilder().build(endpoint, accessKeyId,accessKeySecret);try {// 删除文件或目录。如果要删除目录目录必须为空。ossClient.deleteObject(bucketName, objectName);} catch (OSSException oe) {System.out.println(Caught an OSSException, which means your request made it to OSS, but was rejected with an error response for some reason.);System.out.println(Error Message: oe.getErrorMessage());System.out.println(Error Code: oe.getErrorCode());System.out.println(Request ID: oe.getRequestId());System.out.println(Host ID: oe.getHostId());} catch (ClientException ce) {System.out.println(Caught an ClientException, which means the client encountered a serious internal problem while trying to communicate with OSS, such as not being able to access the network.);System.out.println(Error Message: ce.getMessage());} finally {if (ossClient ! null) {ossClient.shutdown();}}} }注意到里面相比苍穹外卖还运用到了文件删除的功能。 文件上传核心代码如下 /*** 上传文件* param imgFile* return*/PostMapping(/upload)public Result upload(MultipartFile imgFile){log.info(上传文件中...);//获的原始文件的名字String originalFilename imgFile.getOriginalFilename();//截取原始文件名-获的扩展名String exctension originalFilename.substring(originalFilename.lastIndexOf(.));//使用uuid构造新的文件名String newFileName UUID.randomUUID() exctension;//使用阿里云工具类上传文件try {String filePath aliOssUtil.upload(imgFile.getBytes(), newFileName);log.info(文件上传成功:{},filePath);//将图片缓存在Redis中redisTemplate.opsForSet().add(RedisConstant.SETMEAL_PIC_RESOURCES,filePath);return new Result(true,MessageConstant.PIC_UPLOAD_SUCCESS,filePath);} catch (IOException e) {log.error(文件上传失败{},e.getMessage());}return new Result(false,MessageConstant.PIC_UPLOAD_FAIL);} } 其余crud很基础不必细讲。 需要学习的内容redis Redis中文网 第四天 基本内容如下 1.与前端工程师完成代码的联调发现问题在上传图片的操作过程中点击图片上传的按钮时图片就已经上传成功回显了。但是此时如果取消编辑或者新增图片依然存在阿里云服务器当中。会出现垃圾图片的现象。 2.探讨解决方式 redis里面的set应该是能对比差集的文件上传的时候将地址保存到redis里面的一个set集合保存数据库成功的图片插入到redis里面的一个set集和里面。然后再用定时任务去触发对比对比这两个集合就能比较出差集了然后去阿里云OSS里面删除差集的内容就好了。 3.编码解决问题 参考文档 spring boot中使用redisTemplate如何在Spring Boot中使用RedisTemplate_springboot redistemplate 使用-CSDN博客 spring boot中使用Spring TaskSpringBoot使用Scheduled注解实现定时任务_springboot scheduled注解-CSDN博客 解决思路如下 1.在文件上传成功后将图片保存到了一个redis集合中 Overridepublic void add(ListInteger checkGroupIds, Setmeal setmeal) {//增加套餐SetmealMapper.add(setmeal);redisTemplate.opsForSet().add(RedisConstant.SETMEAL_PIC_DB_RESOURCES,setmeal.getImg());//添加套餐和检查组的关联关系if(checkGroupIds!nullcheckGroupIds.size()0){for (Integer checkGroupId : checkGroupIds) {SetmealMapper.addCheckGroupAndSetmeal(setmeal.getId(),checkGroupId);}}}/*** 编辑套餐* param checkGroupIds* param setmeal*/TransactionalOverridepublic void edit(ListInteger checkGroupIds, Setmeal setmeal) {//修改套餐SetmealMapper.update(setmeal);redisTemplate.opsForSet().add(RedisConstant.SETMEAL_PIC_DB_RESOURCES,setmeal.getImg());log.info(图片添加到redis缓存);//修改套餐和检查组的关联关系if(checkGroupIds!nullcheckGroupIds.size()0){//删除套餐和检查组的关联关系SetmealMapper.deleteCheckGroupAndSetmeal(setmeal.getId());//添加套餐和检查组的关联关系for (Integer checkGroupId : checkGroupIds) {SetmealMapper.addCheckGroupAndSetmeal(setmeal.getId(),checkGroupId);}}} 可以看到在添加和修改套餐时我们调用redisTemplate类将新添加的image图片添加到了redis当中的SETMEAL_PIC_DB_RESOURCES中。 2. 当套餐数据插入到数据库后我们又将图片名称保存到另一个redis集合中 /*** 上传文件* param imgFile* return*/PostMapping(/upload)public Result upload(MultipartFile imgFile){log.info(上传文件中...);//获的原始文件的名字String originalFilename imgFile.getOriginalFilename();//截取原始文件名-获的扩展名String exctension originalFilename.substring(originalFilename.lastIndexOf(.));//使用uuid构造新的文件名String newFileName UUID.randomUUID() exctension;//使用阿里云工具类上传文件try {String filePath aliOssUtil.upload(imgFile.getBytes(), newFileName);log.info(文件上传成功:{},filePath);//将图片缓存在Redis中redisTemplate.opsForSet().add(RedisConstant.SETMEAL_PIC_RESOURCES,filePath);return new Result(true,MessageConstant.PIC_UPLOAD_SUCCESS,filePath);} catch (IOException e) {log.error(文件上传失败{},e.getMessage());}return new Result(false,MessageConstant.PIC_UPLOAD_FAIL);} } 可以看到在上传成功到数据库后我们将图片缓存到了redis的SETMEAL_PIC_RESOURCES中 3. 通过计算这两个集合的差值就可以获得所有垃圾图片的名称。 这一步可以写在定时任务类当中 4.使用SpringTask定时任务定时计算redis两个集合的差值就是所有的垃圾图片 package com.itheima.common;import com.itheima.common.constant.RedisConstant; import com.itheima.common.utils.AliOssUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component;import java.util.Set;Slf4j Componentpublic class GarbageImageCleaner {Autowiredprivate RedisTemplateString,String redisTemplate;Autowiredprivate AliOssUtil aliOssUtil;Scheduled(cron 0/10 * * * * ? )public void cleanGarbageImages() {log.info(垃圾图片清理任务执行了);// 获取Redis中所有图片的完整路径SetString ossPaths redisTemplate.opsForSet().members(RedisConstant.SETMEAL_PIC_RESOURCES);// 获取数据库中使用的纯文件名集合SetString dbFileNames redisTemplate.opsForSet().members(RedisConstant.SETMEAL_PIC_DB_RESOURCES);if (ossPaths ! null !ossPaths.isEmpty()) {for (String ossPath : ossPaths) {// 从完整路径中提取文件名String fileName ossPath.substring(ossPath.lastIndexOf(/) 1);// 检查该文件名是否在数据库使用的集合中if (!dbFileNames.contains(fileName)) {// 文件名不在数据库中属于垃圾图片log.info(发现垃圾图片: {}, ossPath);try {String[] split ossPath.split(/);// 尝试删除OSS中的图片aliOssUtil.delete(split[3]);log.info(成功删除OSS图片: {}, ossPath);// 从Redis中移除该路径redisTemplate.opsForSet().remove(RedisConstant.SETMEAL_PIC_RESOURCES, ossPath);log.info(已从缓存中移除: {}, ossPath);} catch (Exception e) {log.error(删除OSS图片失败: {}, ossPath, e);}}}}}} 可以看到这里用两个String类型的set集合分别存储了两个不同常量当中所存储的img路径。分别是存入数据库的和触发过文件上传的。 然后遍历其中一个集合依次对集合中图片的完整路径做截取来和另一个集合比对。 如过出现了不一致的图片路径则判断为垃圾图片调用阿里云工具类当中的delete方法删除再调用remove方法移除缓存。 最后通过  Scheduled(cron 0/10 * *  * * ? )注解设置定时条件。我设置的是定时十秒钟执行一次。 这样就实现了垃圾图片删除的操作。 需要学习知识批量导入 第五天 基本内容如下 分析预约管理的功能功能很简单就三个一个模板下载、一个导入预约设置、还有一个设置的每天的预约人数。 excel导入在苍穹外卖中使用过是用阿帕奇下的POI来实现的。资料如下。 spring boot使用POI实现导入导出Apache POI Excel 导入、导出简单使用_apache poi导入excel-CSDN博客 所以这次想试试新玩意基于POI进一步实现的阿里云旗下的easyexcel EasyExcel读取Excel数据(含多种方式)_easyexcel读取excel内容-CSDN博客 上文资料中主要实现了三步操作 1.引入依赖 2.实现对excel的写入 3.实现对excel内容的读取。 在我分析的预约管理功能当中并不需要实现书写excel的过程导入现成的excel表格实现读取和修改数据库就可以了。 任务如下 1. 编写预约设置的模板。 基础代码已经有了放在resource文件当中。 2. 编写模板下载接口下载项目resource目录下的excel模板。 建议学习一下请求头请求头响应头content-type等相关属性的作用参考链接如下 MDNProperly Configuring Server MIME Types - 学习 Web 开发 | MDN 菜鸟教程HTTP content-type | 菜鸟教程 GetMapping(/download)public ResponseEntityResource downloadFile(String filename) {// 记录下载文件的日志信息log.info(下载模板文件:{},filename);// 通过服务方法获取文件资源Resource resource orderSettingService.downloadFile(filename);// 检查获取的文件资源是否为空if (resource ! null) {try {// 如果文件资源存在构建HTTP响应设置响应头、内容长度、内容类型并将文件作为响应体返回return ResponseEntity.ok().header(Content-Disposition, attachment;filename filename).contentLength(resource.contentLength()).contentType(MediaType.APPLICATION_OCTET_STREAM).body(resource);//以application开头的媒体格式类型////application/xhtmlxml XHTML格式//application/xml XML数据格式//application/atomxml Atom XML聚合格式//application/json JSON数据格式//application/pdfpdf格式//application/msword Word文档格式//application/octet-stream 二进制流数据如常见的文件下载} catch (IOException e) {// 捕获IO异常记录文件下载失败的错误信息log.error(文件下载失败{},e.getMessage());}}// 如果文件资源为空或下载失败返回404未找到的HTTP响应return ResponseEntity.notFound().build();} /*** 下载文件** param filename*/Overridepublic Resource downloadFile(String filename) {// 构建文件路径String filePath bxg-health-backend\\src\\main\\resources\\templates\\ filename;File file new File(filePath);// 检查文件是否存在if (!file.exists()) {log.error(文件不存在{}, filePath);}// 创建文件资源对象Resource resource new FileSystemResource(file);return resource;} 代码解析 我调用download方法将架构中template包下的文件名为filename的包返回给controller层 这里之所以用filename是发现前端代码中是提供了固定的文件名称的。我就把这个文件名称传下来用于文件获取。其实挺好的需要更改不同的文件只需要修改前端代码就好了。只要文件都在template包下。 然后对文件构建了http响应。来让用户可以从网页获取我服务端的指定文件。 3. 编写excel上传接口上传excel文件并将excel数据保存到MySQL /*** 上传文件设置预约人数* param excelFile* return*/PostMapping(/upload)public Result uploadFile(MultipartFile excelFile){log.info(上传文件中...);orderSettingService.uploadFileAndUpdate(excelFile);return new Result(true,MessageConstant.UPLOAD_SUCCESS);}/*** 上传文件并更新预约设置*/Overridepublic void uploadFileAndUpdate(MultipartFile excelFile) {try {// 使用EasyExcel读取文件EasyExcel.read(excelFile.getInputStream(), null,new OrderSettingExcelListener(this)).sheet().doRead();} catch (IOException e) {throw new RuntimeException(上传文件读取失败, e);}} package com.itheima.common;import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.event.AnalysisEventListener; import com.itheima.pojo.OrderSetting; import com.itheima.service.OrderSettingService; import org.slf4j.Logger; import org.slf4j.LoggerFactory;import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Map;/*** Excel读取监听器用于处理预约设置数据*/ public class OrderSettingExcelListener extends AnalysisEventListenerMapInteger, String {private static final Logger log LoggerFactory.getLogger(OrderSettingExcelListener.class);private static final int BATCH_SIZE 20; // 每批处理的数据量private final OrderSettingService orderSettingService;private ListOrderSetting orderSettings new ArrayList();private SimpleDateFormat dateFormat new SimpleDateFormat(yyyy-MM-dd);public OrderSettingExcelListener(OrderSettingService orderSettingService) {this.orderSettingService orderSettingService;}/*** 逐行读取Excel内容*/Overridepublic void invoke(MapInteger, String data, AnalysisContext context) {// 跳过表头行if (context.readRowHolder().getRowIndex() 0) {return;}try {// 解析Excel行数据String orderDateStr data.get(0);String numberStr data.get(1);if (orderDateStr null || numberStr null) {log.warn(无效的Excel行数据跳过: {}, data);return;}// 转换日期格式Date orderDate dateFormat.parse(orderDateStr);int number Integer.parseInt(numberStr);// 创建OrderSetting对象OrderSetting orderSetting new OrderSetting();orderSetting.setOrderDate(orderDate);orderSetting.setNumber(number);orderSettings.add(orderSetting);// 达到批处理数量时执行入库if (orderSettings.size() BATCH_SIZE) {saveData();}} catch (ParseException e) {log.error(日期解析错误: {}, data, e);} catch (NumberFormatException e) {log.error(数量格式错误: {}, data, e);} catch (Exception e) {log.error(处理Excel行时发生未知错误: {}, data, e);}}/*** 所有数据解析完成后调用*/Overridepublic void doAfterAllAnalysed(AnalysisContext context) {// 处理剩余数据saveData();log.info(Excel文件解析完成共处理 {} 条数据, orderSettings.size());}/*** 保存数据到数据库*/private void saveData() {if (!orderSettings.isEmpty()) {try {orderSettingService.addBatch(orderSettings);log.info(批量插入 {} 条预约设置数据, orderSettings.size());orderSettings.clear(); // 清空集合} catch (Exception e) {log.error(批量插入预约设置数据失败, e);// 可添加失败重试逻辑或事务回滚逻辑}}} } 代码逻辑 创建文件上传的接口在实现类中调用监听器来实现读取和保存。监听器中方法是仿照其他人书写的easyexcel监听器来写的。作用分别是逐行读取整理数据内容并保存数据。 /*** 批量添加预约设置*/Overridepublic void addBatch(ListOrderSetting orderSettings) {if (orderSettings null || orderSettings.isEmpty()) {return;}// 优化批量插入性能// 这里采用分批处理避免一次性处理过多数据导致内存问题// 先检查日期是否已存在MapString, Object paramMap new HashMap();ListDate dates new ArrayList();for (OrderSetting os : orderSettings) {dates.add(os.getOrderDate());}paramMap.put(dates, dates);// 查询已存在的日期ListDate existDates orderSettingMapper.findOrderDateByDates(paramMap);// 分离需要插入和更新的数据ListOrderSetting insertList new ArrayList();ListOrderSetting updateList new ArrayList();for (OrderSetting os : orderSettings) {if (existDates.contains(os.getOrderDate())) {updateList.add(os);} else {insertList.add(os);}}// 执行批量插入和更新if (!insertList.isEmpty()) {orderSettingMapper.addBatch(insertList);}if (!updateList.isEmpty()) {orderSettingMapper.updateBatch(updateList);}} 在添加数据的方法中我做了数据判断根据时间 将数据分为存在相同时间的数据和新数据。 存在相同时间的数据执行更新操作新数据执行插入操作。防止同一时间存在多个数据导致的bug。 !-- 批量添加预约设置 --insert idaddBatchINSERT INTO t_ordersetting (orderDate, number, reservations)VALUESforeach collectionlist itemitem separator,(#{item.orderDate}, #{item.number}, 0)/foreach/insert!-- 批量更新预约设置update idupdateBatchforeach collectionlist itemitem separator;UPDATE t_ordersettingSET number #{item.number}WHERE orderDate #{item.orderDate}/foreach/update--!--MyBatis 会自动为每个 item 执行一次 UPDATE虽然性能略差但兼容性和安全性更高。--!--update idupdateBatchforeach collectionlist itemitemUPDATE t_ordersettingSET number #{item.number}WHERE orderDate #{item.orderDate}/foreach/update--!--究极解决方法不太懂--update idupdateBatchUPDATE t_ordersettingtrim prefixSET number CASE orderDate suffixENDforeach collectionlist itemitemWHEN #{item.orderDate} THEN #{item.number}/foreach/trimWHERE orderDate INforeach collectionlist itemitem open( separator, close)#{item.orderDate}/foreach/update /mapper 由于我的操作我就需要自己写批量新增的sql自己没写明白ai教的。 4. 编写设置每天可预约人数功能接口 /**** 编辑预约设置* param orderSettingDTO* return*/PostMapping(/editNumberByOrderDate)public Result editNumberByOrderDate(RequestBody OrderSettingDTO orderSettingDTO) {log.info(编辑预约设置:{}, orderSettingDTO);try {orderSettingService.editNumberByOrderDate(orderSettingDTO);} catch (Exception e) {return new Result(false, MessageConstant.ORDERSETTING_FAIL);}return new Result(true, MessageConstant.ORDERSETTING_SUCCESS);}/*** 根据预约日期修改预约人数** param orderSettingDTO*/Overridepublic void editNumberByOrderDate(OrderSettingDTO orderSettingDTO) {OrderSetting orderSetting new OrderSetting();BeanUtils.copyProperties(orderSettingDTO, orderSetting);//把orderSetting中的日期格式化成yyyy-MM-ddSimpleDateFormat sdf new SimpleDateFormat(yyyy-MM-dd);String formattedDate sdf.format(orderSetting.getOrderDate());try {//sdf.parse(formattedDate)//把格式化后的日期字符串格式为yyyy-MM-dd转回java.util.Date对象。//new java.sql.Date(...)//借助时间戳创建java.sql.Date对象。这里使用的java.sql.Date是专门为 SQL 日期字段设计的它只包含年月日信息不包含时分秒。orderSetting.setOrderDate(new java.sql.Date(sdf.parse(formattedDate).getTime()));} catch (ParseException e) {log.info(日期格式化失败);}orderSettingMapper.editNumberByOrderDate(orderSetting);} 这段代码中修改时间的那段感觉有些多余不过留着也不出错。 因为接口文档中的时间就是yyyy-mm-dd的格式其次我在dto中也规定了时间格式 DateTimeFormat(pattern yyyy-MM-dd)private Date orderDate;//预约设置日期 5. 编写预约设置列表查询接口 /**** param month* return*/GetMapping(/getOrderSettingByMonth)public Result getOrderSettingByMonth(String month) {log.info(根据月份获取预约设置数据:{}, month);if (month.split(-).length 2 month.split(-)[1].length() 1) {month month.split(-)[0] -0 month.split(-)[1];}ListOrderSettingVO orderLists orderSettingService.getOrderSettingByMonth(month);return new Result(true, MessageConstant.GET_ORDERSETTING_SUCCESS, orderLists);}/*** 根据月份获取预约设置数据** param month* return*/Overridepublic ListOrderSettingVO getOrderSettingByMonth(String month) {log.info(根据月份获取预约设置数据:{}, month);ListOrderSettingVO orderLists orderSettingMapper.getOrderSettingByMonth(month);return orderLists;}/*** 根据月份查询预约设置* param month* return*/Select(select day(orderDate) as date,number,reservations from t_ordersetting where DATE_FORMAT(orderDate, %Y-%m) #{month})ListOrderSettingVO getOrderSettingByMonth(String month);/*** 根据月份查询预约设置* param month* return*/Select(select day(orderDate) as date,number,reservations from t_ordersetting where DATE_FORMAT(orderDate, %Y-%m) #{month})ListOrderSettingVO getOrderSettingByMonth(String month); 由于接口文档中接收到的数据是个月份就根据这个月份做了一系列的时间判断搞得人头大还好管用。 这个方法接收一个形如 yyyy-MM 的月份参数例如2023-6 或 2023-06然后 格式检查验证月份是否为 yyyy-M 格式即月份部分只有 1 位数字。格式转换将 yyyy-M 格式转换为 yyyy-MM 格式补零。业务处理调用 orderSettingService.getOrderSettingByMonth() 获取该月的预约设置数据。 分割字符串使用 - 分割月份参数例如 2023-6 → [2023, 6]2023-06 → [2023, 06] 条件判断 month.split(-).length 2确保参数是 yyyy-M 或 yyyy-MM 格式。month.split(-)[1].length() 1检查月份部分是否只有 1 位数字如 6。 格式转换 如果是 2023-6则转换为 2023-06。如果已经是 2023-06则保持不变。 最后的sql语法解析 字段选择 day(orderDate) as date提取日期中的 日 部分如 1-31并重命名为date。number可预约的最大数量。reservations已预约的数量。 表名t_ordersetting存储预约设置的表。条件过滤 DATE_FORMAT(orderDate, %Y-%m) #{month}将日期格式化为YYYY-MM格式后与传入的month参数进行比较。例如2023-06。 这样预约管理的代码就算搞定了。 需要学习的知识阿里云短信  第六天 基本内容如下 1. 编写套餐列表查询不需要分页。 【注意】页面原型有根据性别查询实战中不用。直接查询所有列表即可 2. 根据套餐id查询套餐详情接口。 【注意】在套餐详情页面需要展示当前套餐的信息包括图片、套餐名称、套餐介绍、适用性别、适用年龄、此套餐包含的检查组信息、检查组包含的检查项信息等。 难点多表联查 MyBatis学习之多表操作_mybatis多表-CSDN博客 代码实现如下 这一段就是移动端的代码开发了。 RestController RequestMapping(/setmeal) Slf4j public class SetmealController {Autowiredprivate SetmealService setmealService;/*** 查询所有套餐* return*/RequestMapping(/getSetmeal)public Result findAll(){log.info(查询所有套餐);ListSetmeal setmeals setmealService.findAll();return new Result(true, MessageConstant.QUERY_SETMEAL_SUCCESS,setmeals);}/*** 根据套餐id查询套餐详情* param id* return*/PostMapping(/findById)public Result findById(Integer id){log.info(查询套餐详情);Setmeal setmeal setmealService.findById(id);return new Result(true,MessageConstant.QUERY_SETMEAL_SUCCESS,setmeal);} }!-- 配置映射关系--resultMap idSetmealResultMap typecom.itheima.pojo.Setmealid propertyid columnsetmeal_id/result propertyname columnsetmeal_name/result propertycode columnsetmeal_code/result propertyhelpCode columnsetmeal_helpCode/result propertysex columnsetmeal_sex/result propertyage columnsetmeal_age/result propertyprice columnsetmeal_price/result propertyremark columnsetmeal_remark/result propertyattention columnsetmeal_attention/result propertyimg columnsetmeal_img/collection propertycheckGroups ofTypecom.itheima.pojo.CheckGroup resultMapCheckGroupResultMap/collection/resultMapresultMap idCheckGroupResultMap typecom.itheima.pojo.CheckGroupid propertyid columncheckgroup_id/result propertycode columncheckgroup_code/result propertyname columncheckgroup_name/result propertyhelpCode columncheckgroup_helpCode/result propertysex columncheckgroup_sex/result propertyremark columncheckgroup_remark/result propertyattention columncheckgroup_attention/collection propertycheckItems ofTypecom.itheima.pojo.CheckItem resultMapCheckItemResultMap/collection/resultMapresultMap idCheckItemResultMap typecom.itheima.pojo.CheckItemid propertyid columncheckitem_id/result propertycode columncheckitem_code/result propertyname columncheckitem_name/result propertysex columncheckitem_sex/result propertyage columncheckitem_age/result propertyprice columncheckitem_price/result propertytype columncheckitem_type/result propertyremark columncheckitem_remark/result propertyattention columncheckitem_attention//resultMapselect idfindById resultMapSetmealResultMapselect sm.id AS setmeal_id,sm.name AS setmeal_name,sm.code AS setmeal_code,sm.helpCode AS setmeal_helpCode,sm.sex AS setmeal_sex,sm.age AS setmeal_age,sm.price AS setmeal_price,sm.remark AS setmeal_remark,sm.attention AS setmeal_attention,sm.img AS setmeal_img,cg.id AS checkgroup_id,cg.code AS checkgroup_code,cg.name AS checkgroup_name,cg.helpCode AS checkgroup_helpCode,cg.sex AS checkgroup_sex,cg.remark AS checkgroup_remark,cg.attention AS checkgroup_attention,ci.id AS checkitem_id,ci.code AS checkitem_code,ci.name AS checkitem_name,ci.sex AS checkitem_sex,ci.age AS checkitem_age,ci.price AS checkitem_price,ci.type AS checkitem_type,ci.remark AS checkitem_remark,ci.attention AS checkitem_attentionFROM t_setmeal smLEFT JOINt_setmeal_checkgroup smcg ON sm.id smcg.setmeal_idLEFT JOINt_checkgroup cg ON smcg.checkgroup_id cg.idLEFT JOINt_checkgroup_checkitem cgci ON cg.id cgci.checkgroup_idLEFT JOINt_checkitem ci ON cgci.checkitem_id ci.idWHERE sm.id #{id};/select 这一段是给我写吐了大体代码思路是三张表分别指定查询字段和别名。然后用collection标签关联。套餐关联检查组检查组关联检查项。然后查询字段中se是套餐表别名cg是检查组表别名ci是检查项表别名。再用五个表三个表两个中间表左外连连连实现的多表联查。 除了这个查询恶心其他到没啥问题。 3.编写用户预约功能。 思路如下 1. 验证码保存到redis保存5分钟超时自动删除。 首先阿里云短信需要公司资质俺没这个条件。就随机生成随机数当验证码存储redis替代啦。 随机生成验证码如下 /*** 随机生成验证码工具类*/ public class ValidateCodeUtils {public static final Integer CODE_NUMBER 4;/*** 随机生成验证码* param length 长度为4位或者6位* return*/public static Integer generateValidateCode(int length){Integer code null;if(length 4){code new Random().nextInt(9999);//生成随机数最大为9999if(code 1000){code code 1000;//保证随机数为4位数字}}else if(length 6){code new Random().nextInt(999999);//生成随机数最大为999999if(code 100000){code code 100000;//保证随机数为6位数字}}else{throw new RuntimeException(只能生成4位或6位数字验证码);}return code;}/*** 随机生成指定长度字符串验证码* param length 长度* return*/public static String generateValidateCode4String(int length){Random rdm new Random();String hash1 Integer.toHexString(rdm.nextInt());String capstr hash1.substring(0, length);return capstr;} }实现类中将验证码存入redis中并且配置有效时间。 /*** 发送验证码* param telephone* return*/Overridepublic String getCode(String telephone) {//引用工具类中定义好的常量作为length来生成对应位数的验证码String code ValidateCodeUtils.generateValidateCode4String(ValidateCodeUtils.CODE_NUMBER);log.info({}的验证码为:{},telephone,code);//将验证码通过 redisTemplate.opsForValue().set() 存入 Redis并设置过期时间为 5 分钟。redisTemplate.opsForValue().set(telephone, code,5, TimeUnit.MINUTES);return code;} 2. 校验用户输入的验证码是否正确。 3. 预约日期的预约人数没有设置的话不能预约。 4. 预约日期是否已经约满如果已经约满则无法预约。 5. 不能重复预约同一个用户在同一天预约了同一个套餐。 6. 当前用户不是会员需要自动完成注册。  7. 更新已预约人数 上述功能判断就写在一起咯发现常量类中还有很多没用到但是可以用的常量所以这里干脆用上 /*** 提交预约* return*/RequestMapping(/submitOrder)public Result submitOrder(RequestBody OrderDTO orderDTO) {log.info(提交预约:{}, orderDTO);Integer submitInfoorderService.submitOrder(orderDTO);if (submitInfo -1) {return new Result(false, MessageConstant.SELECTED_DATE_CANNOT_ORDER);}else if (submitInfo -2){return new Result(false, MessageConstant.ORDER_FULL);}else if (submitInfo -3){return new Result(false, MessageConstant.NOT_MEMBER);}else if (submitInfo -4){return new Result(false, MessageConstant.HAS_ORDERED);}else if (submitInfo -5){return new Result(false, MessageConstant.VALIDATECODE_ERROR);}else if (submitInfo 0){return new Result(true, MessageConstant.ORDER_SUCCESS, submitInfo);}return new Result(false, MessageConstant.QUERY_ORDER_FAIL);} Autowiredprivate OrderSettingMapper orderSettingMapper;Autowiredprivate OrderMapper orderMapper;Autowiredprivate MemberMapper memberMapper;Overridepublic Integer submitOrder(OrderDTO orderDTO) {//拷贝dto属性到pojo类中Order order new Order();BeanUtils.copyProperties(orderDTO, order);Member member new Member();BeanUtils.copyProperties(orderDTO, member);//因为手机号码变量名不一样重新赋值member.setPhoneNumber(orderDTO.getTelephone());String redisValidateCode redisTemplate.opsForValue().get(orderDTO.getTelephone());if (redisValidateCode ! null Objects.equals(redisValidateCode,orderDTO.getValidateCode())) {//验证码正确//查询是否设置预约人数Integer number orderSettingMapper.getOrderSettingByOrderDate(order.getOrderDate());if (number null || number 0) {return -1;}//判断是否已经约满Integer reservationsByOrderDate orderSettingMapper.getReservationsByOrderDate(order.getOrderDate());if (number reservationsByOrderDate) {return -2;}//判断是否是会员Integer memberId memberMapper.getRemberIdByUserPhoneNumber(member.getPhoneNumber());if (memberId null) {return -3;}//不能重复预约同一个用户在同一天预约了同一个套餐Order orderInfo orderMapper.findByMemberIdAndOrderDateAndSetmealId(memberId, order.getOrderDate(), order.getSetmealId());//判断是否已经预约if (orderInfo ! null) {return -4;}//预约成功,添加预约信息order.setOrderStatus(Order.ORDERSTATUS_NO);order.setOrderType(Order.ORDERTYPE_WEIXIN);order.setMemberId(memberId);orderMapper.add(order);//设置预约人数reservationsByOrderDatereservationsByOrderDate1;orderSettingMapper.setReservationsByOrderDate(reservationsByOrderDate,order.getOrderDate());return order.getId();} else {//验证码错误return -5;}} 还有个根据id查询预约数据没什么好讲的就不打出来了。 //查询是否设置了预约人数Select(select number from t_ordersetting where DATE_FORMAT(orderDate, %Y-%m-%d) DATE_FORMAT(#{orderDate}, %Y-%m-%d))Integer getOrderSettingByOrderDate(Date orderDate);//查询预约人数Select(select reservations from t_ordersetting where DATE_FORMAT(orderDate, %Y-%m-%d) DATE_FORMAT(#{orderDate}, %Y-%m-%d))Integer getReservationsByOrderDate(Date orderDate);//预约人数1Update(update t_ordersetting set reservations #{reservationsByOrderDate} where DATE_FORMAT(orderDate, %Y-%m-%d) DATE_FORMAT(#{orderDate}, %Y-%m-%d))void setReservationsByOrderDate(Integer reservationsByOrderDate, Date orderDate);功能到此完成 需要学习知识强化springsecurity 第七天 基本内容如下 1.实现用户端的手机号验证码登录功能 代码实现如下 /*** 登录* param telephone* param validateCode* return*/PostMapping(/login)public Result login(String telephone, String validateCode) {log.info({}输入的验证码为:{},telephone,validateCode);String loginInfo memberService.login(telephone, validateCode);return new Result(true, loginInfo);} /*** 实现登录功能* param telephone* param validateCode*/public String login(String telephone, String validateCode) {//获取redis中的验证码String redisValidateCode redisTemplate.opsForValue().get(telephone);if (redisValidateCode ! null Objects.equals(redisValidateCode, validateCode)) {//验证码正确//判断是否是会员Integer memberId memberMapper.getRemberIdByUserPhoneNumber(telephone);if (memberId null) {//不是会员添加会员LocalDate registerTime LocalDate.now();memberMapper.add(registerTime, telephone);}} else {//验证码错误return MessageConstant.VALIDATECODE_ERROR;}return MessageConstant.LOGIN_SUCCESS;}} 用的还是自己瞎搞的随机生成验证码。让登录接口也调用那个方法做验证码判断。多加了一个点是判断是否是会员如过不是会员则自动添加会员。如果有这个点的话我昨日写的判断是否预约成功的代码就要改了。不需要判断是否是会员了。 2.完成管理端用户登陆和授权的功能。 Service public class SpringSecurityUserService implements UserDetailsService {//查找服务实现查询数据库Autowiredprivate UserService userService;//根据用户名查询数据库中用户信息Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {// 从数据库中查询用户User user userService.loadUserByUsername(username);if (user null) {throw new UsernameNotFoundException(用户不存在);}// 查询用户的角色ListRole roles userService.loadRolesByUserId(user.getId());// 把角色转换成数组//String[] roleCodes roles.stream().map(Role::getKeyword).collect(Collectors.toList()).toArray(new String[]{});ListString keywordList new ArrayList();for (Role role : roles) {keywordList.add(role.getKeyword());}String[] roleCodes keywordList.toArray(new String[0]);ListGrantedAuthority authorityList AuthorityUtils.createAuthorityList(roleCodes);// 设置用户的权限user.setAuthorities(authorityList);return new CustomUserDetails(user);} } /*** Security配置类*/Configuration public class SecurityConfiguration extends WebSecurityConfigurerAdapter {/*** http请求处理方法** param http* throws Exception*/Overrideprotected void configure(HttpSecurity http) throws Exception {http.formLogin()// 开启表单认证.loginPage(/backend/login.html)// 自定义登录页面.loginProcessingUrl(/login)// 登录处理Url.usernameParameter(username).passwordParameter(password)// 修改自定义表单name值..defaultSuccessUrl(/backend/pages/main.html)// 登录成功后跳转路径.and().authorizeRequests().antMatchers(/backend/login.html).permitAll().anyRequest().authenticated(); //所有请求都需要登录认证才能访问;// 关闭csrf防护http.csrf().disable();// 允许iframe加载页面http.headers().frameOptions().sameOrigin();}Overridepublic void configure(WebSecurity web) throws Exception {web.ignoring().antMatchers(/backend/css/**, /backend/img/**,/backend/js/**, /backend/plugins/**, /favicon.ico);}Autowiredprivate SpringSecurityUserService userService;Beanprotected PasswordEncoder myPasswordEncoder(){return new BCryptPasswordEncoder();}Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userService).passwordEncoder(myPasswordEncoder());} } 代码解析拆解如下 1. 创建SpringSecurityUserService类实现UserDetailsService Service public class SpringSecurityUserService implements UserDetailsService {//查找服务实现查询数据库Autowiredprivate UserService userService;2. 重写 public UserDetails loadUserByUsername(String username)方法 Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { 3. 调用用户服务获取用户信息 // 从数据库中查询用户User user userService.loadUserByUsername(username);if (user null) {throw new UsernameNotFoundException(用户不存在);} 4. 查询当前用户对应的角色和权限 // 查询用户的角色ListRole roles userService.loadRolesByUserId(user.getId());// 把角色转换成数组//String[] roleCodes roles.stream().map(Role::getKeyword).collect(Collectors.toList()).toArray(new String[]{});ListString keywordList new ArrayList();for (Role role : roles) {keywordList.add(role.getKeyword());}String[] roleCodes keywordList.toArray(new String[0]);ListGrantedAuthority authorityList AuthorityUtils.createAuthorityList(roleCodes);// 设置用户的权限user.setAuthorities(authorityList); 5. 封装当前用户的角色和权限 return new CustomUserDetails(user); 6. 返回带用户名密码权限列表的user对象User(username,user.getPassword(),list) 根据前端需求定义了这个类 public class CustomUserDetails implements UserDetails {private User user;public User getUser() {return user;}public CustomUserDetails(User user) {this.user user;}Overridepublic Collection? extends GrantedAuthority getAuthorities() {return user.getAuthorities();}Overridepublic String getPassword() {return user.getPassword();}Overridepublic String getUsername() {return user.getUsername();}Overridepublic boolean isAccountNonExpired() {return true;}Overridepublic boolean isAccountNonLocked() {return true;}Overridepublic boolean isCredentialsNonExpired() {return true;}Overridepublic boolean isEnabled() {return true;} } 代码开发完成 需要学习知识学习一下Echarts 第八天 基本内容如下  1.分析报表模块的相关功能并开始设计相关接口。 折线图 饼图 各种统计数据。。。。。。。晕 2. 编写会员数量折线图接口 1. 分析会员数量折线图原型图确定数据格式 /*** 会员数量统计* return*/GetMapping(/getMemberReport)public Result getMemberReport() {log.info(会员数量统计...);MemberReportVO memberReportVO reportService.getMemberReport();return new Result(true, MessageConstant.GET_MEMBER_NUMBER_REPORT_SUCCESS, memberReportVO);}/*** 会员数量统计* return*/public MemberReportVO getMemberReport() {ListLocalDate lastDays new ArrayList();// 获取当前日期LocalDate currentDate LocalDate.now();for (int i 0; i 12; i) {// 获取当前月份的最后一天LocalDate lastDayOfMonth currentDate.with(TemporalAdjusters.lastDayOfMonth());lastDays.add(lastDayOfMonth);// 往前推一个月currentDate currentDate.minusMonths(1);}//统计每月的会员数量ListInteger memberCounts new ArrayList();for (LocalDate lastDay : lastDays) {// 获取当前月份的最后一天LocalDate lastDayOfMonth lastDay.with(TemporalAdjusters.lastDayOfMonth());// 获取当前月份的第一天LocalDate firstDayOfMonth lastDay.with(TemporalAdjusters.firstDayOfMonth());// 获取当前月份的会员数量int memberCount 0;try {memberCount reportMapper.getMemberCountByDate(firstDayOfMonth, lastDayOfMonth);} catch (Exception e) {e.printStackTrace();}// 获取当前月份的会员数量memberCounts.add(memberCount);}//转行成数组String[] monthsArray lastDays.stream().map(LocalDate::toString).toArray(String[]::new);String[] memberCountsArray memberCounts.stream().map(String::valueOf).toArray(String[]::new);return MemberReportVO.builder().months(monthsArray).memberCounts(memberCountsArray).build();} 这里面的vo全是自己爬前端代码爬出来的。。自己设计要晕 3. 分析套餐预约占比饼形原型图确定数据格式 4. 编写套餐预约占比饼形图接口 /*** 套餐占比统计* return*/GetMapping(/getSetmealReport)public Result getSetmealReport(){log.info(套餐占比统计...);SetmealReportVO setmealReportVO reportService.getSetmealReport();return new Result(true, MessageConstant.GET_SETMEAL_COUNT_REPORT_SUCCESS, setmealReportVO);}/*** 套餐数量统计* return*/Overridepublic SetmealReportVO getSetmealReport() {//获取套餐namesListString setmealNames reportMapper.getSetmealNames();ListSetmealCount setmealCountsList new ArrayList();for (String setmealName : setmealNames) {//获取套餐的valueInteger setmealCount reportMapper.getSetmealCountByName(setmealName);//封装成SetmealCount对象SetmealCount setmealCounts SetmealCount.builder().name(setmealName).value(setmealCount).build();//添加到集合中setmealCountsList.add(setmealCounts);}//转换为数组String[] setmealNamesArray setmealNames.toArray(new String[0]);SetmealCount[] setmealCountsArray setmealCountsList.toArray(new SetmealCount[0]);return SetmealReportVO.builder().setmealNames(setmealNamesArray).setmealCounts(setmealCountsArray).build();} 2.运营数据统计接口和数据导出功能。 1. 分析运营数据统计原型图 2. 编写运营数据统计接口 /*** 获取运营数据* return*/GetMapping(/getBusinessReportData)public Result getBusinessReportData(){log.info(获取运营数据...);BusinessReportVO businessReportVO reportService.getBusinessReportData();return new Result(true, MessageConstant.GET_BUSINESS_REPORT_SUCCESS, businessReportVO);}/*** 运营数据统计* return*/Overridepublic BusinessReportVO getBusinessReportData() {//计算今日时间范围LocalDate currentDate LocalDate.now();LocalDateTime dayOfBeginTime LocalDateTime.of(currentDate, LocalTime.MIN);LocalDateTime dayOfEndTime LocalDateTime.of(currentDate, LocalTime.MAX);//计算本周时间范围currentDate currentDate.minusDays(currentDate.getDayOfWeek().getValue() - 1);LocalDate firstDayOfMonth currentDate.with(TemporalAdjusters.firstDayOfMonth());LocalDateTime firstMonthsOfBeginTime LocalDateTime.of(firstDayOfMonth, LocalTime.MIN);LocalDate lastDayOfMonth currentDate.with(TemporalAdjusters.lastDayOfMonth());LocalDateTime lastMonthsOfEndTime LocalDateTime.of(lastDayOfMonth, LocalTime.MAX);//计算本月时间范围LocalDate firstDayOfWeek currentDate.minusDays(currentDate.getDayOfWeek().getValue() - 1);LocalDateTime firstweeksOfBeginTime LocalDateTime.of(firstDayOfWeek, LocalTime.MIN);LocalDate lastDayOfWeek currentDate.plusDays(7 - currentDate.getDayOfWeek().getValue());LocalDateTime lastweeksOfEndTime LocalDateTime.of(lastDayOfWeek, LocalTime.MAX);// 查询今日访问量Integer todayVisitsNumber reportMapper.getVisitsNumberByDate(dayOfBeginTime, dayOfEndTime);// 查询今日新增会员数Integer todayNewMember reportMapper.getNewMemberCountByDate(dayOfBeginTime,dayOfEndTime);// 查询本周访问量Integer thisWeekVisitsNumber reportMapper.getVisitsNumberByDate(firstweeksOfBeginTime,lastweeksOfEndTime);// 查询本月新增会员数Integer thisMonthNewMember reportMapper.getNewMemberCountByDate(firstMonthsOfBeginTime, lastMonthsOfEndTime);// 查询本周新增会员数Integer thisWeekNewMember reportMapper.getNewMemberCountByDate(firstweeksOfBeginTime, lastweeksOfEndTime);// 查询总会员数Integer totalMember reportMapper.getTotalMemberCount();// 查询本月订单数Integer thisMonthOrderNumber reportMapper.getOrderNumberByDate(firstMonthsOfBeginTime, lastMonthsOfEndTime);// 查询本月访问量Integer thisMonthVisitsNumber reportMapper.getVisitsNumberByDate(firstMonthsOfBeginTime, lastMonthsOfEndTime);// 查询今日订单数Integer todayOrderNumber reportMapper.getOrderNumberByDate(dayOfBeginTime, dayOfEndTime);// 查询本周订单数Integer thisWeekOrderNumber reportMapper.getOrderNumberByDate(firstweeksOfBeginTime, lastweeksOfEndTime);// 查询热门套餐ListHotSetmeal hotSetmealList reportMapper.getHotSetmeal();// 获取所有的套餐总数Integer totalSetmealCount reportMapper.getTotalSetmealCount();//计算每一种套餐的占比并赋值给proportionfor (HotSetmeal hotSetmeal : hotSetmealList) {// 计算占比,保留四位小数double proportion (double) hotSetmeal.getSetmeal_count() / totalSetmealCount;// 格式化保留四位小数DecimalFormat df new DecimalFormat(#0.0000);hotSetmeal.setProportion(Double.parseDouble(df.format(proportion)));}HotSetmeal[] hotSetmealArray hotSetmealList.toArray(new HotSetmeal[0]);return BusinessReportVO.builder().todayVisitsNumber(todayVisitsNumber).reportDate(currentDate).todayNewMember(todayNewMember).thisWeekVisitsNumber(thisWeekVisitsNumber).hotSetmeal(hotSetmealArray).thisMonthNewMember(thisMonthNewMember).thisWeekNewMember(thisWeekNewMember).totalMember(totalMember).thisMonthOrderNumber(thisMonthOrderNumber).thisMonthVisitsNumber(thisMonthVisitsNumber).todayOrderNumber(todayOrderNumber).thisWeekOrderNumber(thisWeekOrderNumber).build();} 3. 跟据张楠提供的模板编写运营数据统计导出功能 /*** 导出PDF运营数据* return*///TODO PostMapping(/exportBusinessReport4PDF)PostMapping(/exportBusinessReport4PDF)public Result exportBusinessReport4PDF(){log.info(导出PDF运营数据...);reportService.exportBusinessReport4PDF();return new Result(true, MessageConstant.GET_BUSINESS_REPORT_SUCCESS);}Overridepublic void exportBusinessReport4PDF() {BusinessReportVO businessReportVo getBusinessReportData();ListMapString, Object dataList new ArrayList();dataList.add(new HashMapString, Object() {{put(reportDate, businessReportVo.getReportDate());put(todayNewMember, businessReportVo.getTodayNewMember());put(totalMember, businessReportVo.getTotalMember());put(thisWeekNewMember, businessReportVo.getThisWeekNewMember());put(thisMonthNewMember, businessReportVo.getThisMonthNewMember());put(todayVisitsNumber, businessReportVo.getTodayVisitsNumber());put(thisWeekVisitsNumber, businessReportVo.getThisWeekVisitsNumber());put(thisMonthVisitsNumber, businessReportVo.getThisMonthVisitsNumber());put(todayOrderNumber, businessReportVo.getTodayOrderNumber());put(thisWeekOrderNumber, businessReportVo.getThisWeekOrderNumber());put(thisMonthOrderNumber, businessReportVo.getThisMonthOrderNumber());put(hotSetmeal, businessReportVo.getHotSetmeal());}});// 临时Excel文件路径String excelFilePath E:\\temp_report.xlsx;// 最终PDF文件路径String pdfFilePath E:\\report_template.pdf;File templateFile new File(bxg-health-backend\\src\\main\\resources\\templates\\report_template.xlsx);if (!templateFile.exists()) {throw new IllegalArgumentException(模板文件不存在: templateFile);}try {// 1. 先生成Excel文件try (ExcelWriter excelWriter EasyExcel.write(excelFilePath).withTemplate(templateFile).build()) {WriteSheet writeSheet EasyExcel.writerSheet(0).build();excelWriter.fill(dataList, writeSheet);}// 2. 将Excel转换为PDFconvertExcelToPdf(excelFilePath, pdfFilePath);// 3. 删除临时Excel文件new File(excelFilePath).delete();} catch (Exception e) {log.error(导出PDF失败, e);throw new RuntimeException(导出PDF失败, e);}}/*** 将Excel文件转换为PDF文件*/private void convertExcelToPdf(String excelPath, String pdfPath) throws Exception {// 加载Excel文件XSSFWorkbook workbook new XSSFWorkbook(new FileInputStream(excelPath));// 创建PDF文档PdfDocument pdfDoc new PdfDocument(new PdfWriter(pdfPath));Document document new Document(pdfDoc);// 遍历Excel的每个sheetfor (int sheetIndex 0; sheetIndex workbook.getNumberOfSheets(); sheetIndex) {XSSFSheet sheet workbook.getSheetAt(sheetIndex);// 创建PDF表格int columnCount getColumnCount(sheet);Table table new Table(columnCount);// 遍历Excel的每一行for (int rowIndex 0; rowIndex sheet.getLastRowNum(); rowIndex) {XSSFRow row sheet.getRow(rowIndex);if (row null) continue;// 遍历Excel的每一列for (int colIndex 0; colIndex columnCount; colIndex) {XSSFCell cell row.getCell(colIndex);String cellValue cell ! null ? getCellValueAsString(cell) : ;// 添加单元格到PDF表格table.addCell(new Cell().add(new Paragraph(cellValue)));}}// 将表格添加到文档document.add(table);// 如果不是最后一个sheet添加分页符if (sheetIndex workbook.getNumberOfSheets() - 1) {document.add(new AreaBreak(AreaBreakType.NEXT_PAGE));}}// 关闭文档document.close();workbook.close();}/*** 获取Excel表格的列数*/private int getColumnCount(XSSFSheet sheet) {int maxColumnCount 0;for (int i 0; i sheet.getLastRowNum(); i) {XSSFRow row sheet.getRow(i);if (row ! null row.getLastCellNum() maxColumnCount) {maxColumnCount row.getLastCellNum();}}return maxColumnCount;}/*** 获取单元格的值并转换为字符串*/private String getCellValueAsString(XSSFCell cell) {switch (cell.getCellType()) {case STRING:return cell.getStringCellValue();case NUMERIC:if (DateUtil.isCellDateFormatted(cell)) {return cell.getDateCellValue().toString();} else {return String.valueOf(cell.getNumericCellValue());}case BOOLEAN:return String.valueOf(cell.getBooleanCellValue());case FORMULA:return cell.getCellFormula();default:return ;}} } 上图的导入pdf是我自己用itextpdf搞的原理就是先生成excel后转成pdf /*** 导出运营数据统计* return*/Overridepublic void exportBusinessReport() {BusinessReportVO businessReportVo getBusinessReportData();ListMapString, Object dataList new ArrayList();dataList.add(new HashMapString, Object() {{put(reportDate, businessReportVo.getReportDate());put(todayNewMember, businessReportVo.getTodayNewMember());put(totalMember, businessReportVo.getTotalMember());put(thisWeekNewMember, businessReportVo.getThisWeekNewMember());put(thisMonthNewMember, businessReportVo.getThisMonthNewMember());put(todayVisitsNumber, businessReportVo.getTodayVisitsNumber());put(thisWeekVisitsNumber, businessReportVo.getThisWeekVisitsNumber());put(thisMonthVisitsNumber, businessReportVo.getThisMonthVisitsNumber());put(todayOrderNumber, businessReportVo.getTodayOrderNumber());put(thisWeekOrderNumber, businessReportVo.getThisWeekOrderNumber());put(thisMonthOrderNumber, businessReportVo.getThisMonthOrderNumber());put(hotSetmeal, businessReportVo.getHotSetmeal());}});int sheetNo 0;File templateFile new File(bxg-health-backend\\src\\main\\resources\\templates\\report_template.xlsx);String outputFilePath E:\\report_template.xlsx;if (!templateFile.exists()) {throw new IllegalArgumentException(模板文件不存在: templateFile);}try (ExcelWriter excelWriter EasyExcel.write(outputFilePath).withTemplate(templateFile).build()) {WriteSheet writeSheet EasyExcel.writerSheet(sheetNo).build();excelWriter.fill(dataList, writeSheet);}} 代码开发完成。 需要学习知识 学习一下并思考如何提升页面的加载速度呢 参考地址 http://www.freemarker.net/ 参考地址 Freemarker学习指南-CSDN博客 第九天 基本内容如下 用FreeMarker生成套餐列表和套餐详情页 1.修改套餐列表页面为FreeMarker模板 2.修改套餐详情页面为FreeMarker模板 3. 编写添加套餐的时候使用模板生成页面的接口 1新增套餐同时关联检查组 2完成数据库操作后需要将图片名称保存到redis 3 新增套餐后需要重新生成静态页面 4新增套餐后需要重新生成静态页面 5生成套餐列表静态页面 6生成套餐详情静态页面多个 框架中提供好了两个模板参考文档准备好yml内的配置类然后代码如下补足 //新增套餐同时关联检查组public void add(Setmeal setmeal, Integer[] checkgroupIds) {//新增套餐插入t_setmeal表设置idSetmealMapper.add(setmeal);Integer setmealId setmeal.getId();this.setSetmealAndCheckGroup(setmealId,checkgroupIds);//完成数据库操作后需要将图片名称保存到redisString replaced setmeal.getImg().replace(https://sky-incast-jiaxingyu.oss-cn-beijing.aliyuncs.com/, );redisTemplate.opsForSet().add(RedisConstant.SETMEAL_PIC_DB_RESOURCES,replaced);//当添加套餐后需要重新生成静态页面套餐列表页面、套餐详情页面generateMobileStaticHtml();}//生成当前方法所需的静态页面public void generateMobileStaticHtml(){//在生成静态页面之前需要查询数据ListSetmeal list SetmealMapper.findAll();//需要生成套餐列表静态页面generateMobileSetmealListHtml(list);//需要生成套餐详情静态页面generateMobileSetmealDetailHtml(list);}//生成套餐列表静态页面public void generateMobileSetmealListHtml(ListSetmeal list){Map map new HashMap();//为模板提供数据用于生成静态页面map.put(setmealList,list);generteHtml(mobile_setmeal.ftl,m_setmeal.html,map);}//通用的方法用于生成静态页面public void generteHtml(String templateName,String htmlPageName,Map map){Configuration configuration freeMarkerConfigurer.getConfiguration();//获得配置对象Writer out null;try {Template template configuration.getTemplate(templateName);//构造输出流out new FileWriter(new File(outPutPath / htmlPageName));//输出文件template.process(map,out);out.close();} catch (Exception e) {e.printStackTrace();}}//生成套餐详情静态页面可能有多个public void generateMobileSetmealDetailHtml(ListSetmeal list) {for (Setmeal setmeal : list) {Map map new HashMap();map.put(setmeal, SetmealMapper.findById4Detail(setmeal.getId()));generteHtml(mobile_setmeal_detail.ftl, setmeal_detail_ setmeal.getId() .html, map);}}//设置套餐和检查组多对多关联关系public void setSetmealAndCheckGroup(Integer setmealId,Integer[] checkgroupIds){if(checkgroupIds ! null checkgroupIds.length 0){for (Integer checkgroupId : checkgroupIds) {MapString,Integer map new HashMap();map.put(setmealId,setmealId);map.put(checkgroupId,checkgroupId);SetmealMapper.setSetmealAndCheckGroup(map);}}}2.学习如何上线一个项目 部署常用的方式参考如下如何在服务端部署SpringBoot项目_服务器部署springboot项目-CSDN博客 需要学习的知识 如何写项目总结如何写Java开发项目总结_java基础项目总结从哪里写-CSDN博客 参考案例https://juejin.cn/post/6984755776543784968 第十天 基本内容如下: 编写项目总结 完结撒花
http://www.sczhlp.com/news/269574/

相关文章:

  • 热转印 东莞网站建设最容易被收录的网站
  • 做网站合同封面盘锦网站开发公司
  • 怎么做自己的微信网站网站建设管理制度实施方案
  • 网站文字排版用来做区位分析的地图网站
  • 沂源做网站企业网站开发哪个好薇
  • 手机可怎么样做网站文登南海建设局网站
  • 我的网站突然打不开了怎么回事啊广告设计培训班一般都要多少钱
  • 国内创意网站案例深圳设计网站排行
  • 游戏网站wordpress一台云服务器可以做多少个网站
  • 哪些网站可以做设计建造师个人业绩查询系统
  • 2025 年 11 月除锈剂厂家推荐排行榜,钢铁除锈剂,金属除锈剂,钢材除锈剂,不锈钢除锈剂,螺丝除锈剂,弹簧除锈剂,铝型材除锈剂公司推荐
  • Spring Cloud Alibaba + Sentinel
  • 2025年双组份喷涂泵定做厂家权威推荐榜单:双组份喷漆机专用喷枪/无气喷涂机/高压无气喷涂泵专用喷枪源头厂家精选
  • 小 E 的传奇一生
  • 杭州久邦电力建设有限公司网站房产app开发公司
  • 上海杨浦区建设网站WordPress使用微博外链
  • 潍坊专业建站哪些网站会盗取
  • 设计网站的公司名称怎样制作软件程序
  • 网站建设未完成WordPress有评论邮件通知
  • 云主机 网站指南惠山网页制作
  • 企业crm系统哪里搜索引擎优化好
  • 本地企业网站建设服务杭州百度seo
  • 一个网站的制作流程公民道德建设网站
  • 做外贸生意上哪个网站跟黄聪学WordPress主题开发
  • 给个网站手机能看的建站之星网站模板
  • 怎么提高网站排名wordpress 离线
  • 苏州网站建设的公司哪家好商贸有限公司名称大全
  • 佛山网站制作外包wordpress 上传图片重命名
  • 做分析图用的地图网站百度手机浏览器下载
  • 娱乐网站建设ppt模板不懂代码如何开始网站程序建设