【JavaEE】留言板与图书管理系统

目录

        • 留言板
          • 1. 准备工作
          • 2. 约定前后端交互接口
          • lombok
          • 3. 服务器代码
          • 4. 调整前端页面代码
        • 图书管理系统
          • 1. 准备工作
          • 2. 约定前后端交互接口
          • 3. 服务器代码
          • 4. 调整前端页面代码


留言板

需求:

界⾯如下图所⽰

  1. 输⼊留⾔信息, 点击提交. 后端把数据存储起来.
  2. ⻚⾯展⽰输⼊的表⽩墙的信息
1. 准备工作

前端没有保存数据的功能,后端把数据保存下来(内存或者数据库中…,这里先存内存中)

把前端⻚⾯放在项⽬中

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>留言板</title>
    <style>
        .container {
            width: 350px;
            height: 300px;
            margin: 0 auto;
            /* border: 1px black solid; */
            text-align: center;
        }

        .grey {
            color: grey;
        }

        .container .row {
            width: 350px;
            height: 40px;

            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        .container .row input {
            width: 260px;
            height: 30px;
        }

        #submit {
            width: 350px;
            height: 40px;
            background-color: orange;
            color: white;
            border: none;
            margin: 10px;
            border-radius: 5px;
            font-size: 20px;
        }
    </style>
</head>

<body>
    <div class="container">
        <h1>留言板</h1>
        <p class="grey">输入后点击提交, 会将信息显示下方空白处</p>
        <div class="row">
            <span>谁:</span> <input type="text" name="" id="from">
        </div>
        <div class="row">
            <span>对谁:</span> <input type="text" name="" id="to">
        </div>
        <div class="row">
            <span>说什么:</span> <input type="text" name="" id="say">
        </div>
        <input type="button" value="提交" id="submit" onclick="submit()">
        <!-- <div>A 对 B 说: hello</div> -->
    </div>

    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
    <script>
 
        function submit(){
            //1. 获取留言的内容
            var from = $('#from').val();
            var to = $('#to').val();
            var say = $('#say').val();
            if (from== '' || to == '' || say == '') {
                return;
            }
            //2. 构造节点
            var divE = "<div>"+from +"对" + to + "说:" + say+"</div>";
            //3. 把节点添加到页面上    
            $(".container").append(divE);

            //4. 清空输入框的值
            $('#from').val("");
            $('#to').val("");
            $('#say').val("");
            
        }
        
    </script>
</body>

</html>
2. 约定前后端交互接口

需求分析

后端需要提供两个服务

  1. 提交留⾔: ⽤⼾输⼊留⾔信息之后, 后端需要把留⾔信息保存起来
  2. 展⽰留⾔: ⻚⾯展⽰时, 需要从后端获取到所有的留⾔信息

接⼝定义

  1. 获取全部留⾔

全部留⾔信息, 我们⽤List来表⽰, 可以⽤JSON来描述这个List数据.

请求:

参数:无

GET /message/getList

响应: JSON 格式

返回结果:List<MessageInfo>

[
	{
		"from": "⿊猫",
		"to": "⽩猫",
		"message": "喵"
	},{
		"from": "⿊狗",
		"to": "⽩狗",
		"message": "汪"
	},
	//...
]

浏览器给服务器发送⼀个 GET /message/getList这样的请求, 就能返回当前⼀共有哪些留⾔记录. 结果以 json 的格式返回过来

  1. 发表新留⾔

请求: body 也为 JSON 格式.

参数:MessageInfo(from,to,message)

POST /message/publish

{
	"from": "⿊猫",
	"to": "⽩猫",
	"message": "喵"
}

响应: JSON 格式.

返回结果:true/false

{
	ok: 1
}

我们期望浏览器给服务器发送⼀个 POST /message/publish 这样的请求, 就能把当前的留⾔提交给服务器.

lombok

在这个环节, 我们介绍⼀个新的⼯具包 lombok

Lombok是⼀个Java⼯具库,通过添加注解的⽅式,简化Java的开发.

简单来学习下它的使⽤

  1. 引⼊依赖
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.24</version>
    <scope>provided</scope>
</dependency>

引入后记得刷新

  1. 使用

lombok通过⼀些注解的⽅式, 可以帮助我们消除⼀些冗⻓代码, 使代码看起来简洁⼀些

⽐如之前的Person对象 就可以改为

@Data
public class Person {
	private int id;
	private String name;
	private String password;
}

@Data 注解会帮助我们⾃动⼀些⽅法, 包含getter/setter, equals, toString

  1. 原理解释

可以观察加了 @Data 注解之后, Idea反编译的class⽂件

这不是真正的字节码⽂件, ⽽是 IDEA 根据字节码进⾏反编译后的⽂件

反编译是将可执⾏的程序代码转换为某种形式的⾼级编程语⾔, 使其具有更易读的格式. 反编译是⼀种逆向⼯程,它的作⽤与编译器的作⽤相反

即.java文件就是用了注解的,.class文件是把加了这个注解的功能反编译了,变成你本应该自己写的代码

可以看出来, lombok是⼀款在编译期⽣成代码的⼯具包.

Java 程序的运⾏原理:

Lombok 的作⽤如下图所⽰:

  1. 更多使⽤

如果觉得@Data⽐较粗暴(⽣成⽅法太多), lombok也提供了⼀些更精细粒度的注解

注解作用
@Getter⾃动添加 getter ⽅法
@Setter⾃动添加 setter ⽅法
@ToString⾃动添加 toString ⽅法
@EqualsAndHashCode⾃动添加 equals 和 hashCode ⽅法
@NoArgsConstructor⾃动添加⽆参构造⽅法
@AllArgsConstructor⾃动添加全属性构造⽅法,顺序按照属性的定义顺序
@NonNull属性不能为 null
@RequiredArgsConstructor⾃动添加必需属性的构造⽅法,final + @NonNull 的属性为必需

@Data = @Getter + @Setter + @ToString + @EqualsAndHashCode + @RequiredArgsConstructor + @NoArgsConstructor

更快捷的引⼊依赖

上述引⼊lombok依赖, 需要去找lombok的坐标

接下来介绍更简单引⼊依赖的⽅式

  1. 安装插件EditStarter, 重启Idea

  1. 在pom.xml⽂件中, 单击右键, 选择Generate, 操作如下图所⽰

进⼊Edit Starters的编辑界⾯, 添加对应依赖即可.

注意:

不是所有依赖都可以在这⾥添加的, 这个界⾯和SpringBoot创建项⽬界⾯⼀样.

依赖不在这⾥的, 还需要去Maven仓库查找坐标, 添加依赖.

3. 服务器代码

定义留⾔对象 MessageInfo 类

@Data
public class MessageInfo {
    private String from;
    private String to;
    private String message;
}

创建 MessageController 类

使⽤ List<MessageInfo> 来存储留⾔板信息

@RestController
@RequestMapping("/message")
public class MessageController {
    private List<MessageInfo> messageInfos=new ArrayList<>();

    @RequestMapping("/publish")
    public Boolean publishMessage(MessageInfo messageInfo){
        //进行参数校验
        //三个任意一个为空
        if(!StringUtils.hasLength(messageInfo.getFrom())
                || !StringUtils.hasLength(messageInfo.getTo())
                || !StringUtils.hasLength(messageInfo.getMessage())){
            return false;
        }
        //添加留言
        messageInfos.add(messageInfo);
        return false;
    }

    @RequestMapping("/getMessageInfo")
    public List<MessageInfo> getMessage(){
        //直接返回留言
        return messageInfos;
    }
}

测试一下后端接口

为什么前端校验之后,后端还需要校验呢?

  1. 这是两个团队的事情
  2. 后端可能会收到攻击,收到非正常请求,比如什么爬虫之类的
4. 调整前端页面代码

修改 messagewall.html

vscode的代码格式化:alt+shift+F

  1. 添加函数, ⽤于在⻚⾯加载的时候获取数据
//页面加载时,请求后端,获取留言列表
        $.ajax({
            url: "/message/getMessageInfo",
            type: "get",
            success: function (messages) {
                for (var m of messages) {
                    //拼接显示的记录
                    //2. 拼接节点的html
                    var divE = "<div>" + m.from + "对" + m.to + "说:" + m.message + "</div>";
                    //3. 把节点添加到页面上    
                    $(".container").append(divE);
                }
            }
        });
  1. 修改原来的点击事件函数. 在点击按钮的时候给服务器发送添加留⾔请求
function submit() {
            //1. 获取留言的内容
            var from = $('#from').val();
            var to = $('#to').val();
            var say = $('#say').val();
            if (from == '' || to == '' || say == '') {
                return;
            }
            //提交留言
            $.ajax({
                url: "/message/publish",
                type: "post",
                data: {
                    "from": from,
                    "to": to,
                    "message": say
                },
                success: function (result) {
                    if (result) {
                        //添加成功
                        //2. 拼接节点的html
                        var divE = "<div>" + from + "对" + to + "说:" + say + "</div>";
                        //3. 把节点添加到页面上    
                        $(".container").append(divE);

                        //4. 清空输入框的值
                        $('#from').val("");
                        $('#to').val("");
                        $('#say').val("");
                    } else {
                        //添加失败
                        alert("留言发布失败");
                    }
                }
            });
        }

测试http://127.0.0.1:8080/messagewall.html输入信息后点击刷新然后fiddler抓包

此时我们每次提交的数据都会发送给服务器. 每次打开⻚⾯的时候⻚⾯都会从服务器加载数据. 因此及时关闭⻚⾯, 数据也不会丢失.

但是数据此时是存储在服务器的内存中 ( private List<Message> messages = new ArrayList<Message>(); )

⼀旦服务器重启, 数据仍然会丢失.

要想数据不丢失, 需要把数据存储在数据库中, 后⾯再讲

图书管理系统

需求:

  1. 登录: ⽤⼾输⼊账号,密码完成登录功能
  2. 列表展⽰: 展⽰图书
1. 准备工作

创建新项⽬, 引⼊对应依赖, 把前端⻚⾯放在项⽬中

系统后续其他功能也会完善, 此处建议创建新项⽬

在这里插入图片描述

2. 约定前后端交互接口

需求分析

图书管理系统是⼀个相对较⼤⼀点的案例, 咱们先实现其中的⼀部分功能.

1. ⽤⼾登录

2. 图书列表

根据需求可以得知, 后端需要提供两个接⼝

  1. 账号密码校验接⼝: 根据输⼊⽤⼾名和密码校验登录是否通过
  2. 图书列表: 提供图书列表信息

接⼝定义

  1. 登录接口
[URL]
POST /user/login

[请求参数]
userName=admin&password=admin

[响应]
true //账号密码验证成功
false//账号密码验证失败
  1. 图书列表展示
[URL]
POST /book/getBookList

[请求参数]
⽆

[响应]
返回图书列表
[
 	{
 		"id": 1,
 		"bookName": "活着",
 		"author": "余华",
 		"count": 270,
 		"price": 20,
 		"publish": "北京⽂艺出版社",
 		"status": 1,
 		"statusCN": "可借阅"
 	},
 	...
]

字段说明:

字段含义
id图书ID
bookName图书名称
author作者
count数量
price定价
publish图书出版社
status图书状态 1-可借阅, 其他-不可借阅
statusCN图书状态中⽂含义
3. 服务器代码

创建图书类 BookInfo

@Data
public class BookInfo {
    //图书ID
    private Integer id;
    //书名
    private String bookName;
    //作者
    private String author;
    //数量
    private Integer conut;
    //价格
    private BigDecimal price;
    //出版社
    private String publish;
    //状态
    private Integer status;//1-可借阅 2-不可借阅
    private String statusCN;
}

这里为什么priceBigDecimal可以去查一下

https://www.cnblogs.com/jayworld/p/10107335.html

还有什么这里状态statusInteger而不是用字符串表示?

是因为这里数据库只能存数字,如果后续改需求了,就需要大改,而我们存的是数字,可以通过数字来映射成文字

创建 UserController, 实现登录验证接⼝

@RestController
@RequestMapping("/user")
public class UserController {
    @RequestMapping("/login")
    public Boolean login(String userName, String password, HttpSession session) {
        //校验参数
        if (!StringUtils.hasLength(userName) || !StringUtils.hasLength(password)) {
            return false;
        }
        //验证账号密码是否正确
        //if(userName.equals("admin")这种写法,如果userName为null就会报空指针异常
        //这是开发习惯,要养成
        if("admin".equals(userName)&&"admin".equals(password)){
            //账号密码正确
            //存session
            session.setAttribute("userName",userName);
            return true;
        }
        return false;
    }
}

创建 BookController, 获取图书列表

@RestController
@RequestMapping("/book")
public class BookController {
    @RequestMapping("/getBookList")
    public List<BookInfo> getBookList(){
        //1.获取图书数据
        List<BookInfo> bookInfos=mockData();
        //2.对图书数据进行修改处理
        for(BookInfo bookInfo:bookInfos){
            if(bookInfo.getStatus()==1){
                bookInfo.setStatusCN("可借阅");
            }else{
                bookInfo.setStatusCN("不可借阅");
            }
        }
        //3.返回数据
        return bookInfos;
    }

    //mock - 虚拟数据,假数据
    private List<BookInfo> mockData() {
        //对于已知的数据量或者大概知道这个集合的数量时,创建List时建议指定初始化容量
        List<BookInfo> bookInfos=new ArrayList<>(15);
        for(int i=0;i<15;i++){
            BookInfo bookInfo=new BookInfo();
            bookInfo.setId(i);
            bookInfo.setBookName("图书"+i);
            bookInfo.setAuthor("作者"+i);
            bookInfo.setConut(new Random().nextInt(200));
            bookInfo.setPrice(new BigDecimal(new Random().nextInt(100)));
            bookInfo.setPublish("出版社"+i);
            bookInfo.setStatus(i%5==0?2:1);
            bookInfos.add(bookInfo);
        }
        return bookInfos;
    }
}

数据采⽤mock的⽅式, 实际数据应该从数据库中获取

mock:模拟的, 假的

在开发和测试过程中,由于环境不稳定或者协同开发的同事未完成等情况下, 有些数据不容易构造或者不容易获取,就创建⼀个虚拟的对象或者数据样本,⽤来辅助开发或者测试⼯作.

简单来说, 就是假数据。

上述经过Postman测试都没有问题

4. 调整前端页面代码

登录⻚⾯:

添加登录处理逻辑

function login() {
            $.ajax({
                url:"/user/login",
                type:"post",
                data:{
                    "userName":$("#userName").val(),
                    "password":$("#password").val()
                },
                success:function(result){
                    //console.log(result);
                    if(result){
                        location.href = "book_list.html";
                    }else{
                        alert("用户名或密码错误");
                    }
                }
            });
        } 

图书列表展⽰:

删除前端伪造的代码, 从后端获取数据并渲染到⻚⾯上.

  1. 删除 标签中的内容

  1. 完善获取图书⽅法
getBookList();
            function getBookList() {
                $.ajax({
                    type:"get",
                    url:"/book/getBookList",
                    success:function(books){
                        var finalHtml="";   
                        for(var book of books){
                            //根据每一条记录拼接html,也就是一个<tr>
                            finalHtml+='<tr>';
                            finalHtml+='<td><input type="checkbox" name="selectBook" value="'+book.id+'" id="selectBook" class="book-select"></td>';
                            finalHtml+='<td>'+book.id+'</td>';
                            finalHtml+='<td>'+book.bookName+'</td>';
                            finalHtml+='<td>'+book.author+'</td>';
                            finalHtml+='<td>'+book.count+'</td>';
                            finalHtml+='<td>'+book.price+'</td>';
                            finalHtml+='<td>'+book.publish+'</td>';
                            finalHtml+='<td>'+book.statusCN+'</td>';
                            finalHtml+='<td>';
                            finalHtml+='<div class="op">';
                            finalHtml+='<a href="book_update.html?bookId='+book.id+'">修改</a>';
                            finalHtml+='<a href="javascript:void(0)" οnclick="deleteBook('+book.id+')">删除</a>';
                            finalHtml+='</div>';
                            finalHtml+='</td>';
                            finalHtml+='</tr>';
                        }
                        console.log(finalHtml);
                        $("tBody").html(finalHtml);
                    }
                });
            }

运行测试:http://127.0.0.1:8080/login.html,输⼊账号密码: admin admin, 登录成功, 跳转到 图书列表⻚

界⾯展⽰:

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/686166.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Mysql使用中的性能优化——搭建Mysql的监测服务

大纲 环境安装配置Mysql安装设置root密码新增远程访问账户修改绑定地址重启 新增 MySQL Server Exporter 用户 安装启动mysqld_exporter安装启动新增配置启动 安装启动Prometheus创建用户下载并解压修改配置启动 安装启动grafana安装启动 测试参考资料 抛开场景和数据&#xff…

【YOLOv8改进[CONV]】SPDConv助力YOLOv8目标检测效果 + 含全部代码和详细修改方式 + 手撕结构图

本文将使用SPDConv助力YOLOv8目标检测效果的实践,文中含全部代码、详细修改方式以及手撕结构图。助您轻松理解改进的方法。 改进前和改进后的参数对比: 目录 一 SPDConv 二 SPDConv助力YOLOv8目标检测效果 1 整体修改 ① 添加SPDConv.py文件 ② 修改ultralytics/nn/tas…

笔记95:车辆横向动力学方程转化为误差形式 -- 详细推导过程

1. 非误差型车辆横向动力学方程 注&#xff1a;关于轮胎侧偏刚度的正负 深蓝课程推导得到的车辆横向动力学返程使用的轮胎侧偏刚度是默认为正数&#xff1b;老王课程推导得到的车辆横向动力学方程使用的轮胎侧偏刚度是默认为负数&#xff1b; 1.1 深蓝课程推导得到的方程&…

【Qt知识】部分QWidget属性表格

QWidget是Qt库中所有图形用户界面组件的基类&#xff0c;它提供了大量属性以供自定义和配置控件的行为和外观。下面列出了一些主要的QWidget属性及其作用。 属性 作用 accessibleName 控件的辅助技术名称&#xff0c;用于无障碍访问。 accessibleDescription 控件的辅助技…

18 - 各赛事的用户注册率(高频 SQL 50 题基础版)

18 - 各赛事的用户注册率 -- 注册率注册用户数/所有用户数 selectr.contest_id,round(100*count(*)/(select count(*) from Users),2) percentage from Register r group by r.contest_id order bypercentage desc,r.contest_id ASC;

网鼎杯 2020 玄武组 SSRFMe

复习一下常见的redis主从复制 主要是redis伪服务器的选择和一些小坑点 <?php function check_inner_ip($url) { $match_resultpreg_match(/^(http|https|gopher|dict)?:\/\/.*(\/)?.*$/,$url); if (!$match_result) { die(url fomat error); } try { …

3D Web轻量化平台HOOPS Web Platform助力Rapid DCS迅速推出成本与碳排放估算产品

英国公司Rapid DCS利用HOOPS Web平台在短短六个月内成功开发出创新的云解决方案Sterling&#xff0c;旨在帮助建筑行业客户高效地估算成本和计算碳排放。 Rapid DCS&#xff0c;一家总部位于英国的公司&#xff0c;专注于为建筑环境领域的客户提供全面的数字化解决方案和服务。…

NextJs 实现自定义点火操作

NextJs 实现自定义点火操作 前言实现自定义点火 前言 我希望在Nextjs 启动的时候&#xff0c;能够自定义实现一些项目的初始化逻辑&#xff0c;也可以说是一些点火操作&#xff0c;比如资源的加载&#xff0c;数据的初始化等操作。 实现自定义点火 我们可以在根目录下创建一…

22 - 游戏玩法分析 IV(高频 SQL 50 题基础版)

22 - 游戏玩法分析 IV 考点&#xff1a; 聚合函数 # 日期相加 date_add(min(event_date),INTERVAL 1 DAY) select round(count(distinct player_id)/(select count(distinct player_id) from Activity),2) fraction fromActivity where-- 如果日期加一天的数据能在表中…

界面组件DevExpress Reports v23.2增强用户体验 - 轻松导航Web设计器

DevExpress Reporting是.NET Framework下功能完善的报表平台&#xff0c;它附带了易于使用的Visual Studio报表设计器和丰富的报表控件集&#xff0c;包括数据透视表、图表&#xff0c;因此您可以构建无与伦比、信息清晰的报表。 DevExpress Reports v23.2(我们最近的主要更新…

C++ 11【右值引用】

&#x1f493;博主CSDN主页:麻辣韭菜&#x1f493;   ⏩专栏分类&#xff1a;C修炼之路⏪   &#x1f69a;代码仓库:C高阶&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学习更多C知识   &#x1f51d;&#x1f51d; 1.C 11 简介 目录 1.C 11 简介 2. 统一的列表…

小程序自定义marker弹出框教程

需求背景 微信小程序开发&#xff0c;需要使用腾讯地图显示自定义marker&#xff0c;并且点击marker后弹出自定义的customCallout&#xff0c;并且customCallout的内容为用户点击marker的时候再从后台接口获取数据。 百度了一圈后发现居然没有一篇文章可以一次性完成&#xf…

使用opencv在图像上画带刻度线的十字线,以图像中心点为0点

使用OpenCV在图像上绘制带刻度线的十字线&#xff0c;可以通过以下步骤实现。我们将首先找到图像的中心点&#xff0c;然后绘制水平和垂直线&#xff0c;并在这些线的适当位置绘制刻度线。以下是详细的C代码示例&#xff1a; #include<opencv2\opencv.hpp> //画十字标注…

LeetCode题练习与总结:杨辉三角Ⅱ--119

一、题目描述 给定一个非负索引 rowIndex&#xff0c;返回「杨辉三角」的第 rowIndex 行。 在「杨辉三角」中&#xff0c;每个数是它左上方和右上方的数的和。 示例 1: 输入: rowIndex 3 输出: [1,3,3,1]示例 2: 输入: rowIndex 0 输出: [1]示例 3: 输入: rowIndex 1 输…

Spring Cloud工程添加子模块打包后文件为war包而非jar包

Spring Cloud工程添加子模块打包后文件为war包而非jar包 Spring Cloud子模块打出的包通常是JAR包而非WAR包&#xff0c;这是因为Spring Cloud主要基于Spring Boot构建&#xff0c;而Spring Boot默认打包为可执行JAR包。然而&#xff0c;如果遇到了Spring Cloud子模块打成了WAR…

前端 JS 经典:图片裁剪上传原理

前言&#xff1a;图片裁剪一般都是用户选择头像时用到&#xff0c;现在很多插件都可以满足这个功能&#xff0c;但是我们不仅要会用插件&#xff0c;还要自己懂的裁剪原理。 1. 流程 流程分为&#xff1a;1. 预览本地图片 2. 选择裁剪区域 3. 上传裁剪图像 2. 如何预览图片 …

08-指针与数组的结合——数组指针与指针数组的区别

指针与数组的结合 示例 1:指针访问数组元素 通过指针访问数组元素的例子&#xff1a; #include <stdio.h>int main() {int arr[5] {1,2,3,4,5};//int *p1 &arr;int *p1 (int *)&arr; // 需要强制类型转换int *p2 arr;printf("*p1:%d\n", *(p1 …

如何备份和恢复华为手机?

智能手机已成为我们日常生活中不可或缺的一部分&#xff0c;它们存储着大量敏感数据。因此&#xff0c;确保数据安全&#xff0c;定期备份至关重要&#xff0c;以防手机意外丢失、损坏或被盗。 如果您拥有华为设备&#xff0c;并且正在寻找如何将华为手机备份到PC的方法&#…

uniapp在自定义tabbar上动态修改svg图标颜色和字体颜色

需求&#xff1a;在uniapp项目内&#xff0c;自定义tabbar&#xff0c;需要将图标更换成svg格式&#xff0c;可动态修改图标及字体颜色。 效果图如下&#xff1a; 我使用的是uniapp结合uview2的组件使用&#xff0c;代码如下&#xff1a; <u-tabbar :value"currentIn…

Python Excel 指定内容修改

需求描述 在处理Excel 自动化时,财务部门经常有一个繁琐的场景,需要读取分发的Excel文件内容复制到汇总Excel文件对应的单元格内,如下图所示: 这种需求可以延申为,财务同事制作一个模板,将模板发送给各员工,财务同事需收取邮件将员工填写的excel文件下载到本机,再类似…