...
 
Commits (2)
    https://gitcode.net/weixin_74916722/1024opensource/-/commit/a93075c03e28d4357ef267ed3b5b3987b7c0e24b first commit 2022-11-13T22:41:39+08:00 yourname 277199202@qq.com https://gitcode.net/weixin_74916722/1024opensource/-/commit/0acf4eb7bdaa97d70bffcdad34c4080e248510c9 first commit 2022-11-13T22:45:39+08:00 yourname 277199202@qq.com
/target/
!.mvn/wrapper/maven-wrapper.jar
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### jhint ###
.jshintrc
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### NetBeans ###
/nbproject/private/
/build/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
{
"java.configuration.updateBuildConfiguration": "interactive"
}
\ No newline at end of file
# 1024程序员开源挑战赛
## **任务快速入口 ↓ ↓ ↓**
**_- 任务一,赢取 一年10核10G云实验环境会员:[GitCode平台基础操作](https://gitcode.net/gitcode/1024opensource/-/blob/master/%E5%BC%80%E6%BA%90%E4%BB%BB%E5%8A%A11.md)_**
- 2022/11/11 [29人开发者任务完成名单](https://gitcode.net/gitcode/1024opensource/-/blob/master/%E4%BB%BB%E5%8A%A11%E4%B8%AD%E5%A5%96%E5%90%8D%E5%8D%95.md#1-20221111-%E4%BB%BB%E5%8A%A1%E5%AE%8C%E6%88%90%E5%90%8D%E5%8D%95%E5%85%AC%E5%B8%8329%E4%BA%BA)
- 2022/11/10 [47人开发者任务完成名单](https://gitcode.net/gitcode/1024opensource/-/blob/master/%E4%BB%BB%E5%8A%A11%E4%B8%AD%E5%A5%96%E5%90%8D%E5%8D%95.md#2-20221110-%E4%BB%BB%E5%8A%A1%E5%AE%8C%E6%88%90%E5%90%8D%E5%8D%95%E5%85%AC%E5%B8%8347%E4%BA%BA)
- 2022/11/09 [32人开发者任务完成名单](https://gitcode.net/gitcode/1024opensource/-/blob/master/%E4%BB%BB%E5%8A%A11%E4%B8%AD%E5%A5%96%E5%90%8D%E5%8D%95.md#3-20221109-%E4%BB%BB%E5%8A%A1%E5%AE%8C%E6%88%90%E5%90%8D%E5%8D%95%E5%85%AC%E5%B8%8332%E4%BA%BA)
- 2022/11/08 [43人开发者任务完成名单](https://gitcode.net/gitcode/1024opensource/-/blob/master/%E4%BB%BB%E5%8A%A11%E4%B8%AD%E5%A5%96%E5%90%8D%E5%8D%95.md#4-20221108-%E4%BB%BB%E5%8A%A1%E5%AE%8C%E6%88%90%E5%90%8D%E5%8D%95%E5%85%AC%E5%B8%8343%E4%B8%AA)
- 2022/11/07 [45人开发者任务完成名单](https://gitcode.net/gitcode/1024opensource/-/blob/master/%E4%BB%BB%E5%8A%A11%E4%B8%AD%E5%A5%96%E5%90%8D%E5%8D%95.md#5-20221107-%E4%BB%BB%E5%8A%A1%E5%AE%8C%E6%88%90%E5%90%8D%E5%8D%95%E5%85%AC%E5%B8%8345%E4%BA%BA)
- 2022/11/06 [32人开发者任务完成名单](https://gitcode.net/gitcode/1024opensource/-/blob/master/%E4%BB%BB%E5%8A%A11%E4%B8%AD%E5%A5%96%E5%90%8D%E5%8D%95.md#6-20221106-%E4%BB%BB%E5%8A%A1%E5%AE%8C%E6%88%90%E5%90%8D%E5%8D%95%E5%85%AC%E5%B8%8332%E4%BA%BA)
- 2022/11/05 [63人开发者任务完成名单](https://gitcode.net/gitcode/1024opensource/-/blob/master/%E4%BB%BB%E5%8A%A11%E4%B8%AD%E5%A5%96%E5%90%8D%E5%8D%95.md#7-20221105-%E4%BB%BB%E5%8A%A1%E5%AE%8C%E6%88%90%E5%90%8D%E5%8D%95%E5%85%AC%E5%B8%8363%E4%BA%BA)
- 2022/11/04 [161人开发者任务完成名单](https://gitcode.net/gitcode/1024opensource/-/blob/master/%E4%BB%BB%E5%8A%A11%E4%B8%AD%E5%A5%96%E5%90%8D%E5%8D%95.md#8-20221104-%E4%BB%BB%E5%8A%A1%E5%AE%8C%E6%88%90%E5%90%8D%E5%8D%95%E5%85%AC%E5%B8%83161%E4%BA%BA)
- 2022/11/03 [208人开发者任务完成名单](https://gitcode.net/gitcode/1024opensource/-/blob/master/%E4%BB%BB%E5%8A%A11%E4%B8%AD%E5%A5%96%E5%90%8D%E5%8D%95.md#9-20221103-%E4%BB%BB%E5%8A%A1%E5%AE%8C%E6%88%90%E5%90%8D%E5%8D%95%E5%85%AC%E5%B8%83208%E4%BA%BA)
- 2022/11/02 [162人开发者任务完成名单](https://gitcode.net/gitcode/1024opensource/-/blob/master/%E4%BB%BB%E5%8A%A11%E4%B8%AD%E5%A5%96%E5%90%8D%E5%8D%95.md#10-20221102-%E4%BB%BB%E5%8A%A1%E5%AE%8C%E6%88%90%E5%90%8D%E5%8D%95%E5%85%AC%E5%B8%83162%E4%BA%BA)
- 2022/11/01 [127人开发者任务完成名单](https://gitcode.net/gitcode/1024opensource/-/blob/master/%E4%BB%BB%E5%8A%A11%E4%B8%AD%E5%A5%96%E5%90%8D%E5%8D%95.md#11-20221101-%E4%BB%BB%E5%8A%A1%E5%AE%8C%E6%88%90%E5%90%8D%E5%8D%95%E5%85%AC%E5%B8%83127%E4%BA%BA)
- 2022/10/31 [103人开发者任务完成名单](https://gitcode.net/gitcode/1024opensource/-/blob/master/%E4%BB%BB%E5%8A%A11%E4%B8%AD%E5%A5%96%E5%90%8D%E5%8D%95.md#12-20221031-%E4%BB%BB%E5%8A%A1%E5%AE%8C%E6%88%90%E5%90%8D%E5%8D%95%E5%85%AC%E5%B8%83103%E4%BA%BA)
- 2022/10/30 [10人开发者任务完成名单](https://gitcode.net/gitcode/1024opensource/-/blob/master/%E4%BB%BB%E5%8A%A11%E4%B8%AD%E5%A5%96%E5%90%8D%E5%8D%95.md#13-20221030-%E4%BB%BB%E5%8A%A1%E5%AE%8C%E6%88%90%E5%90%8D%E5%8D%95%E5%85%AC%E5%B8%8310%E4%BA%BA)
- 2022/10/29 [11人开发者任务完成名单](https://gitcode.net/gitcode/1024opensource/-/blob/master/%E4%BB%BB%E5%8A%A11%E4%B8%AD%E5%A5%96%E5%90%8D%E5%8D%95.md#14-20221029-%E4%BB%BB%E5%8A%A1%E5%AE%8C%E6%88%90%E5%90%8D%E5%8D%95%E5%85%AC%E5%B8%8311%E4%BA%BA)
- 2022/10/28 [28人开发者任务完成名单](https://gitcode.net/gitcode/1024opensource/-/blob/master/%E4%BB%BB%E5%8A%A11%E4%B8%AD%E5%A5%96%E5%90%8D%E5%8D%95.md#15-20221028-%E4%BB%BB%E5%8A%A1%E5%AE%8C%E6%88%90%E5%90%8D%E5%8D%95%E5%85%AC%E5%B8%8328%E4%BA%BA)
- 2022/10/27 [31人开发者任务完成名单](https://gitcode.net/gitcode/1024opensource/-/blob/master/%E4%BB%BB%E5%8A%A11%E4%B8%AD%E5%A5%96%E5%90%8D%E5%8D%95.md#16-20221027-%E4%BB%BB%E5%8A%A1%E5%AE%8C%E6%88%90%E5%90%8D%E5%8D%95%E5%85%AC%E5%B8%8331%E4%BA%BA)
- 2022/10/26 [37人开发者任务完成名单](https://gitcode.net/gitcode/1024opensource/-/blob/master/%E4%BB%BB%E5%8A%A11%E4%B8%AD%E5%A5%96%E5%90%8D%E5%8D%95.md#17-20221026-%E4%BB%BB%E5%8A%A1%E5%AE%8C%E6%88%90%E5%90%8D%E5%8D%95%E5%85%AC%E5%B8%8337%E4%BA%BA)
- 2022/10/25 [48人开发者任务完成名单](https://gitcode.net/gitcode/1024opensource/-/blob/master/%E4%BB%BB%E5%8A%A11%E4%B8%AD%E5%A5%96%E5%90%8D%E5%8D%95.md#18-20221025-%E4%BB%BB%E5%8A%A1%E5%AE%8C%E6%88%90%E5%90%8D%E5%8D%95%E5%85%AC%E5%B8%8348%E4%BA%BA)
- 2022/10/24 [58人开发者任务完成名单](https://gitcode.net/gitcode/1024opensource/-/blob/master/%E4%BB%BB%E5%8A%A11%E4%B8%AD%E5%A5%96%E5%90%8D%E5%8D%95.md#19-20221024-%E4%BB%BB%E5%8A%A1%E5%AE%8C%E6%88%90%E5%90%8D%E5%8D%95%E5%85%AC%E5%B8%83%E5%85%B1%E8%AE%A158%E4%BA%BA)
- 2022/10/23 [22人开发者任务完成名单](https://gitcode.net/gitcode/1024opensource/-/blob/master/%E4%BB%BB%E5%8A%A11%E4%B8%AD%E5%A5%96%E5%90%8D%E5%8D%95.md#20-20221023-%E4%BB%BB%E5%8A%A1%E5%AE%8C%E6%88%90%E5%90%8D%E5%8D%95%E5%85%AC%E5%B8%83-%E5%85%B1%E8%AE%A122%E4%BA%BA)
**_- 任务二,赢取 CSDN定制书包:[开源项目开发与运行](https://gitcode.net/gitcode/1024opensource/-/blob/master/%E5%BC%80%E6%BA%90%E4%BB%BB%E5%8A%A12.md)_**
- 2022/10/31 至 2022/11/06 [85人开发者任务完成名单](https://gitcode.net/gitcode/1024opensource/-/blob/master/%E4%BB%BB%E5%8A%A12%E4%B8%AD%E5%A5%96%E5%90%8D%E5%8D%95.md#1-20221031-%E8%87%B3-20221106-%E4%BB%BB%E5%8A%A1%E5%AE%8C%E6%88%90%E5%90%8D%E5%8D%95%E5%85%AC%E5%B8%8385%E4%BA%BA)
- 2022/10/23 至 2022/10/30 [71人开发者任务完成名单](https://gitcode.net/gitcode/1024opensource/-/blob/master/%E4%BB%BB%E5%8A%A12%E4%B8%AD%E5%A5%96%E5%90%8D%E5%8D%95.md#2-20221023-%E8%87%B3-20221030-%E4%BB%BB%E5%8A%A1%E5%AE%8C%E6%88%90%E5%90%8D%E5%8D%95%E5%85%AC%E5%B8%8371%E4%BA%BA)
**_- 任务三,赢取 现金大礼:[1024云IDE应用挑战赛](https://gitcode.net/cloud-ide/1024)_**
上述每项任务无顺序依赖关系,三项任务都可参与或独立参与。
______________________________________________
## 一、活动介绍
**1024开源挑战赛** 是CSDN官方在1024程序员节重磅推出的开源活动,致力让开发者使用和参与开源项目。在“开源正在吞噬世界”的大背景下,94%的开发者正在使用开源项目与软件,此次活动希望广大开发者了解开源的同时获取社区奖励。
## 二、活动安排
| 序号 | 任务名称 | 用户群体 | 时间安排 |
| ---- | ---- | ---- | ---- |
| 1 | GitCode平台基础操作 | 开源使用者 | 2022.10.23 - 2022.11.14 |
| 2 | 开源项目开发与运行 | 开源使用者 | 2022.10.23 - 2022.11.14 |
| 3 | 1024云IDE应用挑战赛 | 开源贡献者 | 2022.10.23 - 2022.11.14 |
上述每项任务无顺序依赖关系,三项任务都可参与或独立参与。
## 三、奖品设置及发放规则
| 序号 | 任务名称 | 任务奖励 | 领取条件 | 发放规则 | 人数限制 |
| ---- | ---- | ---- | ---- | ---- | ---- |
| 1 | GitCode平台基础操作 | [一年10核10G云实验环境会员](https://mydev.csdn.net/product/pod/new)(非云服务器,是云容器实验环境,可免费用于实验环境和云IDE中) | 完成任务即可发放,用户可按照任务操作步骤自行检查是否完成,官方会每天运行程序,自动统计并发放奖励。每个用户仅可领取1次。| 次日12点前在任务页面公布中奖名单,24点前完成发放奖励,成功发放后会有短信通知,若未收到短信,可点击[此链接,](https://mydev.csdn.net/product/ide/dashboard )查看VIP是否到账(成功领取到的用户,可在页面看到VIP标识及到期时间等提示) |10,000 |
| 2 | 开源项目开发与运行 | [CSDN定制书包](https://img-home.csdnimg.cn/images/20221013053300.png) | 完成任务即可发放,用户可按照任务操作步骤自行检查是否完成,官方人员会每周统一通过检查后发放。每个用户仅可领取1次。 | 每周一12点前在任务页面公布中奖名单,工作人员会通过CSDN私信联系您,奖品在活动结束后30个工作日发放 | 2,000 |
| 3 | 1024云IDE应用挑战赛 | 最高5000元现金奖励 | 代码提交完成,评选获奖后发放 | 11月15日在任务页面公布评选结果,工作人员会通过CSDN私信联系您,奖品在活动结束后7个工作日发放 | 88 |
## 四、参与任务入口
| 序号 | 任务名称 | 任务入口 |
| ---- | ---- | ---- |
| 1 | GitCode平台基础操作| [https://gitcode.net/gitcode/1024opensource/-/blob/master/%E5%BC%80%E6%BA%90%E4%BB%BB%E5%8A%A11.md](https://gitcode.net/gitcode/1024opensource/-/blob/master/%E5%BC%80%E6%BA%90%E4%BB%BB%E5%8A%A11.md) |
| 2 | 开源项目开发与运行 | [https://gitcode.net/gitcode/1024opensource/-/blob/master/%E5%BC%80%E6%BA%90%E4%BB%BB%E5%8A%A12.md](https://gitcode.net/gitcode/1024opensource/-/blob/master/%E5%BC%80%E6%BA%90%E4%BB%BB%E5%8A%A12.md) |
| 3 | [1024云IDE应用挑战赛](https://gitcode.net/cloud-ide/1024) | [https://gitcode.net/cloud-ide/1024](https://gitcode.net/cloud-ide/1024) |
## 五、活动交流
开源活动官方交流群(仅参与者之间讨论和交流)。扫码进入选手沟通群。活动重要节点通知会在群内第一时间告知,请所有参与者尽量加群。
<img src="/uploads/45be870f33053a686cb60b7c8cca38c4/b435ccc7d422528ad8212ca53d687687.jpg" width="400px" style="border: 6px solid #fff;border-radius: 2px;">
## 六、活动点评
### 活动建议
如果大家对本次活动有建议,可以直接在本项目中提交issue,我们会认真对待每一个评论,非常感谢大家!!!
### 活动点赞👍
如果大家喜欢本次开源挑战赛活动,可以点击本项目的star,我们会根据大家的star数作为后期是否长期举办的重要依据,非常感谢大家!!!
## 七、活动声明
本活动最终解释权归CSDN所有,所有中奖名单会在活动结束后进行公示
<<<<<<< HEAD
# 任务一:GitCode平台基础操作
## 1. 2022/11/09 任务完成名单公布(32人)
=======
# 任务一:GitCode平台基础操作获奖名单
## 1. 2022/11/11 任务完成名单公布(29人)
......@@ -96,6 +101,7 @@
## 3. 2022/11/09 任务完成名单公布(32人)
>>>>>>> 0bad356fec25eef056d40d89d412b14e3782d911
| 序号 | 用户名 | 项目地址 |
|---- | ---- | ---- |
| 1 | pigpigeon | https://gitcode.net/pigpigeon/easyexcel |
......@@ -137,7 +143,11 @@
- 以上用户获得 [一年10核10G云容器实验环境会员](https://mydev.csdn.net/product/pod/new),11月10日当天会发放完成。
- 上述顺序不区分先后顺序!
<<<<<<< HEAD
## 2. 2022/11/08 任务完成名单公布(43个)
=======
## 4. 2022/11/08 任务完成名单公布(43个)
>>>>>>> 0bad356fec25eef056d40d89d412b14e3782d911
| 序号 | 用户名 | 项目地址 |
|---- | ---- | ---- |
| 1 | weixin_40331132 | https://gitcode.net/weixin_40331132/JavaBooks |
......@@ -188,7 +198,11 @@
- 以上用户获得 [一年10核10G云容器实验环境会员](https://mydev.csdn.net/product/pod/new),11月9日当天会发放完成。
- 上述顺序不区分先后顺序!
<<<<<<< HEAD
## 3. 2022/11/07 任务完成名单公布(45人)
=======
## 5. 2022/11/07 任务完成名单公布(45人)
>>>>>>> 0bad356fec25eef056d40d89d412b14e3782d911
| 序号 | 用户名 | 项目地址 |
|---- | ---- | ---- |
......@@ -244,7 +258,11 @@
<<<<<<< HEAD
## 4. 2022/11/06 任务完成名单公布(32人)
=======
## 6. 2022/11/06 任务完成名单公布(32人)
>>>>>>> 0bad356fec25eef056d40d89d412b14e3782d911
| 序号 | 用户名 | 项目地址 |
|---- | ---- | ---- |
......@@ -286,7 +304,11 @@
- 上述顺序不区分先后顺序!
<<<<<<< HEAD
## 5. 2022/11/05 任务完成名单公布(63人)
=======
## 7. 2022/11/05 任务完成名单公布(63人)
>>>>>>> 0bad356fec25eef056d40d89d412b14e3782d911
| 序号 | 用户名 | 项目地址 |
|---- | ---- | ---- |
......@@ -358,7 +380,11 @@
- 以上用户获得 [一年10核10G云容器实验环境会员](https://mydev.csdn.net/product/pod/new),11月6日当天会发放完成。
- 上述顺序不区分先后顺序!
<<<<<<< HEAD
## 6. 2022/11/04 任务完成名单公布(161人)
=======
## 8. 2022/11/04 任务完成名单公布(161人)
>>>>>>> 0bad356fec25eef056d40d89d412b14e3782d911
| 序号 | 用户名 | 项目地址 |
|---- | ---- | ---- |
......@@ -528,7 +554,11 @@
- 以上用户获得 [一年10核10G云容器实验环境会员](https://mydev.csdn.net/product/pod/new),11月5日当天会发放完成。
- 上述顺序不区分先后顺序!
<<<<<<< HEAD
## 7. 2022/11/03 任务完成名单公布(208人)
=======
## 9. 2022/11/03 任务完成名单公布(208人)
>>>>>>> 0bad356fec25eef056d40d89d412b14e3782d911
| 序号 | 用户名 | 项目地址 |
|---- | ---- | ---- |
......@@ -745,7 +775,11 @@
- 以上用户获得 [一年10核10G云容器实验环境会员](https://mydev.csdn.net/product/pod/new),11月4日当天会发放完成。
- 上述顺序不区分先后顺序!
<<<<<<< HEAD
## 8. 2022/11/02 任务完成名单公布(162人)
=======
## 10. 2022/11/02 任务完成名单公布(162人)
>>>>>>> 0bad356fec25eef056d40d89d412b14e3782d911
| 序号 | 用户名 | 项目地址 |
|---- | ---- | ---- |
......@@ -916,7 +950,11 @@
- 以上用户获得 [一年10核10G云容器实验环境会员](https://mydev.csdn.net/product/pod/new),11月3日当天会发放完成。
- 上述顺序不区分先后顺序!
<<<<<<< HEAD
## 9. 2022/11/01 任务完成名单公布(127人)
=======
## 11. 2022/11/01 任务完成名单公布(127人)
>>>>>>> 0bad356fec25eef056d40d89d412b14e3782d911
| 序号 | 用户名 | 项目地址 |
|---- | ---- | ---- |
| 1 | Aaron_King | https://gitcode.net/Aaron_King/JavaBooks |
......@@ -1052,7 +1090,11 @@
- 上述顺序不区分先后顺序!
<<<<<<< HEAD
## 10. 2022/10/31 任务完成名单公布(103人)
=======
## 12. 2022/10/31 任务完成名单公布(103人)
>>>>>>> 0bad356fec25eef056d40d89d412b14e3782d911
| 序号 | 用户名 | 项目地址 |
|---- | ---- | ---- |
......@@ -1164,7 +1206,11 @@
- 以上用户获得 [一年10核10G云容器实验环境会员](https://mydev.csdn.net/product/pod/new),11月1日当天会发放完成。
- 上述顺序不区分先后顺序!
<<<<<<< HEAD
## 11. 2022/10/30 任务完成名单公布(10人)
=======
## 13. 2022/10/30 任务完成名单公布(10人)
>>>>>>> 0bad356fec25eef056d40d89d412b14e3782d911
| 序号 | 用户名 | 项目地址 |
|---- | ---- | ---- |
......@@ -1183,7 +1229,11 @@
- 以上用户获得 [一年10核10G云容器实验环境会员](https://mydev.csdn.net/product/pod/new),10月31日当天会发放完成。
- 上述顺序不区分先后顺序!
<<<<<<< HEAD
## 12. 2022/10/29 任务完成名单公布(11人)
=======
## 14. 2022/10/29 任务完成名单公布(11人)
>>>>>>> 0bad356fec25eef056d40d89d412b14e3782d911
| 序号 | 用户名 | 项目地址 |
|---- | ---- | ---- |
......@@ -1203,7 +1253,11 @@
- 以上用户获得 [一年10核10G云容器实验环境会员](https://mydev.csdn.net/product/pod/new),10月30日当天会发放完成。
- 上述顺序不区分先后顺序!
<<<<<<< HEAD
## 13. 2022/10/28 任务完成名单公布(28人)
=======
## 15. 2022/10/28 任务完成名单公布(28人)
>>>>>>> 0bad356fec25eef056d40d89d412b14e3782d911
|序号 | 用户名 | 项目地址 |
|---- | ---- | ---- |
......@@ -1241,7 +1295,11 @@
- 上述顺序不区分先后顺序!
<<<<<<< HEAD
## 14. 2022/10/27 任务完成名单公布(31人)
=======
## 16. 2022/10/27 任务完成名单公布(31人)
>>>>>>> 0bad356fec25eef056d40d89d412b14e3782d911
|序号 | 用户名 | 项目地址 |
|---- | ---- | ---- |
......@@ -1281,7 +1339,11 @@
- 以上用户获得 [一年10核10G云容器实验环境会员](https://mydev.csdn.net/product/pod/new),10月28日当天会发放完成。
- 上述顺序不区分先后顺序!
<<<<<<< HEAD
## 15. 2022/10/26 任务完成名单公布(37人)
=======
## 17. 2022/10/26 任务完成名单公布(37人)
>>>>>>> 0bad356fec25eef056d40d89d412b14e3782d911
|序号 | 用户名 | 项目地址 |
|---- | ---- | ---- |
......@@ -1328,7 +1390,11 @@
- 上述顺序不区分先后顺序!
<<<<<<< HEAD
## 16. 2022/10/25 任务完成名单公布(48人)
=======
## 18. 2022/10/25 任务完成名单公布(48人)
>>>>>>> 0bad356fec25eef056d40d89d412b14e3782d911
|序号 | 用户名 | 项目地址 |
|---- | ---- | ---- |
......@@ -1385,7 +1451,11 @@
- 以上用户获得 [一年10核10G云容器实验环境会员](https://mydev.csdn.net/product/pod/new),10月26日当天会发放完成。
- 上述顺序不区分先后顺序!
<<<<<<< HEAD
## 17. 2022/10/24 任务完成名单公布(共计58人)
=======
## 19. 2022/10/24 任务完成名单公布(共计58人)
>>>>>>> 0bad356fec25eef056d40d89d412b14e3782d911
| 序号 | 用户名 | 项目地址 |
|---- | ---- | ---- |
......@@ -1453,7 +1523,11 @@
- 上述顺序不区分先后顺序!
<<<<<<< HEAD
## 18. 2022/10/23 任务完成名单公布 (共计22人)
=======
## 20. 2022/10/23 任务完成名单公布 (共计22人)
>>>>>>> 0bad356fec25eef056d40d89d412b14e3782d911
|序号 | 用户名 | 项目地址 |
......
<<<<<<< HEAD
# 任务二:开源项目开发与运行
=======
# 任务二:开源项目开发与运行 获奖名单
>>>>>>> 0bad356fec25eef056d40d89d412b14e3782d911
## 1. 2022/10/31 至 2022/11/06 任务完成名单公布(87人)
......
......@@ -3,8 +3,11 @@
## 一、任务介绍
1024开源挑战赛 是CSDN官方在1024程序员节重磅推出的开源活动,致力让开发者使用和参与开源项目。在“开源正在吞噬世界”的大背景下,94%的开发者正在使用开源项目与软件,此次活动希望广大开发者了解Gitcode平台的基础操作,并获取社区奖励。
<<<<<<< HEAD
=======
活动时间:2022.10.23 - 2022.11.14
>>>>>>> 0bad356fec25eef056d40d89d412b14e3782d911
## 二、任务步骤 (请按照步骤提示操作,并自行检查是否遗漏哦~)
| 步骤 | 步骤名称 | 步骤描述 | 示意图 |
| ---- | ---- | ---- | ---- |
......
......@@ -3,8 +3,11 @@
## 一、任务介绍
1024程序员节结合开源大趋势,开发者可根据自己情况选择业界知名开源项目或自研可开源项目结合GitCode和Cloud IDE进行开发与适配,需要在IDE中能够运行起来为标准获得CSDN定制书包奖励。
<<<<<<< HEAD
=======
活动时间:2022.10.23 - 2022.11.14
>>>>>>> 0bad356fec25eef056d40d89d412b14e3782d911
特别提醒:不可直接使用官方提供的demo项目参赛(官方demo: [前端项目2048](https://gitcode.net/cloud-ide/2048)[后端项目CnOCR](https://gitcode.net/cloud-ide/cnocr)
......
#!/bin/sh
# ----------------------------------------------------------------------------
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
# ----------------------------------------------------------------------------
# ----------------------------------------------------------------------------
# Maven2 Start Up Batch script
#
# Required ENV vars:
# ------------------
# JAVA_HOME - location of a JDK home dir
#
# Optional ENV vars
# -----------------
# M2_HOME - location of maven2's installed home dir
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
# e.g. to debug Maven itself, use
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
# ----------------------------------------------------------------------------
if [ -z "$MAVEN_SKIP_RC" ] ; then
if [ -f /etc/mavenrc ] ; then
. /etc/mavenrc
fi
if [ -f "$HOME/.mavenrc" ] ; then
. "$HOME/.mavenrc"
fi
fi
# OS specific support. $var _must_ be set to either true or false.
cygwin=false;
darwin=false;
mingw=false
case "`uname`" in
CYGWIN*) cygwin=true ;;
MINGW*) mingw=true;;
Darwin*) darwin=true
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html
if [ -z "$JAVA_HOME" ]; then
if [ -x "/usr/libexec/java_home" ]; then
export JAVA_HOME="`/usr/libexec/java_home`"
else
export JAVA_HOME="/Library/Java/Home"
fi
fi
;;
esac
if [ -z "$JAVA_HOME" ] ; then
if [ -r /etc/gentoo-release ] ; then
JAVA_HOME=`java-config --jre-home`
fi
fi
if [ -z "$M2_HOME" ] ; then
## resolve links - $0 may be a link to maven's home
PRG="$0"
# need this for relative symlinks
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG="`dirname "$PRG"`/$link"
fi
done
saveddir=`pwd`
M2_HOME=`dirname "$PRG"`/..
# make it fully qualified
M2_HOME=`cd "$M2_HOME" && pwd`
cd "$saveddir"
# echo Using m2 at $M2_HOME
fi
# For Cygwin, ensure paths are in UNIX format before anything is touched
if $cygwin ; then
[ -n "$M2_HOME" ] &&
M2_HOME=`cygpath --unix "$M2_HOME"`
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
[ -n "$CLASSPATH" ] &&
CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
fi
# For Mingw, ensure paths are in UNIX format before anything is touched
if $mingw ; then
[ -n "$M2_HOME" ] &&
M2_HOME="`(cd "$M2_HOME"; pwd)`"
[ -n "$JAVA_HOME" ] &&
JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
# TODO classpath?
fi
if [ -z "$JAVA_HOME" ]; then
javaExecutable="`which javac`"
if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
# readlink(1) is not available as standard on Solaris 10.
readLink=`which readlink`
if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
if $darwin ; then
javaHome="`dirname \"$javaExecutable\"`"
javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
else
javaExecutable="`readlink -f \"$javaExecutable\"`"
fi
javaHome="`dirname \"$javaExecutable\"`"
javaHome=`expr "$javaHome" : '\(.*\)/bin'`
JAVA_HOME="$javaHome"
export JAVA_HOME
fi
fi
fi
if [ -z "$JAVACMD" ] ; then
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
else
JAVACMD="`which java`"
fi
fi
if [ ! -x "$JAVACMD" ] ; then
echo "Error: JAVA_HOME is not defined correctly." >&2
echo " We cannot execute $JAVACMD" >&2
exit 1
fi
if [ -z "$JAVA_HOME" ] ; then
echo "Warning: JAVA_HOME environment variable is not set."
fi
CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
# traverses directory structure from process work directory to filesystem root
# first directory with .mvn subdirectory is considered project base directory
find_maven_basedir() {
if [ -z "$1" ]
then
echo "Path not specified to find_maven_basedir"
return 1
fi
basedir="$1"
wdir="$1"
while [ "$wdir" != '/' ] ; do
if [ -d "$wdir"/.mvn ] ; then
basedir=$wdir
break
fi
# workaround for JBEAP-8937 (on Solaris 10/Sparc)
if [ -d "${wdir}" ]; then
wdir=`cd "$wdir/.."; pwd`
fi
# end of workaround
done
echo "${basedir}"
}
# concatenates all lines of a file
concat_lines() {
if [ -f "$1" ]; then
echo "$(tr -s '\n' ' ' < "$1")"
fi
}
BASE_DIR=`find_maven_basedir "$(pwd)"`
if [ -z "$BASE_DIR" ]; then
exit 1;
fi
##########################################################################################
# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
# This allows using the maven wrapper in projects that prohibit checking in binary data.
##########################################################################################
if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found .mvn/wrapper/maven-wrapper.jar"
fi
else
if [ "$MVNW_VERBOSE" = true ]; then
echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
fi
jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar"
while IFS="=" read key value; do
case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
esac
done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
if [ "$MVNW_VERBOSE" = true ]; then
echo "Downloading from: $jarUrl"
fi
wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
if command -v wget > /dev/null; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found wget ... using wget"
fi
wget "$jarUrl" -O "$wrapperJarPath"
elif command -v curl > /dev/null; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found curl ... using curl"
fi
curl -o "$wrapperJarPath" "$jarUrl"
else
if [ "$MVNW_VERBOSE" = true ]; then
echo "Falling back to using Java to download"
fi
javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
if [ -e "$javaClass" ]; then
if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
if [ "$MVNW_VERBOSE" = true ]; then
echo " - Compiling MavenWrapperDownloader.java ..."
fi
# Compiling the Java class
("$JAVA_HOME/bin/javac" "$javaClass")
fi
if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
# Running the downloader
if [ "$MVNW_VERBOSE" = true ]; then
echo " - Running MavenWrapperDownloader.java ..."
fi
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
fi
fi
fi
fi
##########################################################################################
# End of extension
##########################################################################################
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
if [ "$MVNW_VERBOSE" = true ]; then
echo $MAVEN_PROJECTBASEDIR
fi
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
# For Cygwin, switch paths to Windows format before running java
if $cygwin; then
[ -n "$M2_HOME" ] &&
M2_HOME=`cygpath --path --windows "$M2_HOME"`
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
[ -n "$CLASSPATH" ] &&
CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
[ -n "$MAVEN_PROJECTBASEDIR" ] &&
MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
fi
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
exec "$JAVACMD" \
$MAVEN_OPTS \
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
"-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
@REM ----------------------------------------------------------------------------
@REM Licensed to the Apache Software Foundation (ASF) under one
@REM or more contributor license agreements. See the NOTICE file
@REM distributed with this work for additional information
@REM regarding copyright ownership. The ASF licenses this file
@REM to you under the Apache License, Version 2.0 (the
@REM "License"); you may not use this file except in compliance
@REM with the License. You may obtain a copy of the License at
@REM
@REM http://www.apache.org/licenses/LICENSE-2.0
@REM
@REM Unless required by applicable law or agreed to in writing,
@REM software distributed under the License is distributed on an
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@REM KIND, either express or implied. See the License for the
@REM specific language governing permissions and limitations
@REM under the License.
@REM ----------------------------------------------------------------------------
@REM ----------------------------------------------------------------------------
@REM Maven2 Start Up Batch script
@REM
@REM Required ENV vars:
@REM JAVA_HOME - location of a JDK home dir
@REM
@REM Optional ENV vars
@REM M2_HOME - location of maven2's installed home dir
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
@REM e.g. to debug Maven itself, use
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
@REM ----------------------------------------------------------------------------
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
@echo off
@REM set title of command window
title %0
@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on'
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
@REM set %HOME% to equivalent of $HOME
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
@REM Execute a user defined script before this one
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
:skipRcPre
@setlocal
set ERROR_CODE=0
@REM To isolate internal variables from possible post scripts, we use another setlocal
@setlocal
@REM ==== START VALIDATION ====
if not "%JAVA_HOME%" == "" goto OkJHome
echo.
echo Error: JAVA_HOME not found in your environment. >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
:OkJHome
if exist "%JAVA_HOME%\bin\java.exe" goto init
echo.
echo Error: JAVA_HOME is set to an invalid directory. >&2
echo JAVA_HOME = "%JAVA_HOME%" >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
@REM ==== END VALIDATION ====
:init
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
@REM Fallback to current working directory if not found.
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
set EXEC_DIR=%CD%
set WDIR=%EXEC_DIR%
:findBaseDir
IF EXIST "%WDIR%"\.mvn goto baseDirFound
cd ..
IF "%WDIR%"=="%CD%" goto baseDirNotFound
set WDIR=%CD%
goto findBaseDir
:baseDirFound
set MAVEN_PROJECTBASEDIR=%WDIR%
cd "%EXEC_DIR%"
goto endDetectBaseDir
:baseDirNotFound
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
cd "%EXEC_DIR%"
:endDetectBaseDir
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
@setlocal EnableExtensions EnableDelayedExpansion
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
:endReadAdditionalConfig
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar"
FOR /F "tokens=1,2 delims==" %%A IN (%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties) DO (
IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
)
@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
if exist %WRAPPER_JAR% (
echo Found %WRAPPER_JAR%
) else (
echo Couldn't find %WRAPPER_JAR%, downloading it ...
echo Downloading from: %DOWNLOAD_URL%
powershell -Command "(New-Object Net.WebClient).DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"
echo Finished downloading %WRAPPER_JAR%
)
@REM End of extension
%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
if ERRORLEVEL 1 goto error
goto end
:error
set ERROR_CODE=1
:end
@endlocal & set ERROR_CODE=%ERROR_CODE%
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
@REM check for post script, once with legacy .bat ending and once with .cmd ending
if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
:skipRcPost
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
if "%MAVEN_BATCH_PAUSE%" == "on" pause
if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
exit /B %ERROR_CODE%
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.kanlon.cfile</groupId>
<artifactId>cfile</artifactId>
<version>0.0.3-SNAPSHOT</version>
<packaging>jar</packaging>
<name>cfile</name>
<description>在线收集文件并自动统计名单的小项目</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.0.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- mybatis的单元测试 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter-test</artifactId>
<version>1.3.2</version>
</dependency>
<!-- lombok,简化getter和setter操作 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<!-- thymeleaf模板包 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- thymeleaf模板依赖的ognl包 -->
<dependency>
<groupId>ognl</groupId>
<artifactId>ognl</artifactId>
<version>3.0.8</version>
</dependency>
<!-- 邮件服务 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<!-- mybatis的配置 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<!-- 必须有,因为自己数据库版本太低,需要该版本数据库 jar才能链接,否则会报错-->
<version>8.0.28</version>
</dependency>
<!-- 开发时保证支持热启动 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<repositories>
<repository>
<id>alimaven</id>
<name>aliyun maven</name>
<url>https://maven.aliyun.com/nexus/content/repositories/central/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>alimaven</id>
<name>aliyun maven</name>
<url>https://maven.aliyun.com/nexus/content/repositories/central/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<!-- 开发时保证支持热启动 -->
<configuration>
<fork>true</fork>
</configuration>
</plugin>
</plugins>
</build>
</project>
# 自动运行脚本
# preview.yml
autoOpen: false # 打开工作空间时是否自动开启所有应用的预览
apps:
- port: 8082 # 应用的端口
run: apt-get purge openjdk-17-jdk:amd64 -y && apt-get purge openjdk-17-jdk-headless:amd64 -y && apt-get purge openjdk-17-jre:amd64 -y && apt-get purge openjdk-17-jre-headless:amd64 -y && echo "删除jdk17成功" && sudo apt install openjdk-8-jdk && echo "安装jdk1.8成功" && echo "export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64" >> /etc/profile && echo "export JRE_HOME=/usr/lib/jvm/java-8-openjdk-amd64/jre" >> /etc/profile && source /etc/profile && export PATH=$PATH:/usr/lib/jvm/java-8-openjdk-amd64/bin && echo "配置环境变量成功" && mvn install -Dmaven.test.skip=true && echo "maven 打包成功" && cd target && apt install unzip && unzip -o cfile-0.0.3-SNAPSHOT.jar && echo "解压启动包成功" && echo "启动mysql,这里mysql 为8.0" && service mysql start && echo "获取mysql 用户名和密码" && mysqlUser=`sudo cat /etc/mysql/debian.cnf | grep user | awk -F '=' '{print $2}' | sed -n 1p | sed -e 's/^[ ]*//g' | sed -e 's/[ ]*$//g'` && mysqlPassword=`sudo cat /etc/mysql/debian.cnf | grep password | awk -F '=' '{print $2}' | sed -n 1p | sed -e 's/^[ ]*//g' | sed -e 's/[ ]*$//g'` && echo "给mysql修改root 账号的密码" && mysql -h 127.0.0.1 -P 3306 -u${mysqlUser} -p${mysqlPassword} -e "update mysql.user set plugin='mysql_native_password' where user='root'; ALTER USER 'root'@'localhost' IDENTIFIED BY 'root';FLUSH PRIVILEGES;" && echo "给mysql 创建cfiel的用户" && mysql -h 127.0.0.1 -P 3306 -u${mysqlUser} -p${mysqlPassword} -e "use mysql;create user cfile_admin@'%' identified by 'cfile_admin_password';" ; mysql -h 127.0.0.1 -P 3306 -u${mysqlUser} -p${mysqlPassword} -e "use mysql;grant all privileges on cfile.* to cfile_admin@'%' ;FLUSH PRIVILEGES;alter user cfile_admin@'%' identified with mysql_native_password by 'cfile_admin_password';" && echo "初始化cfile的数据库" && mysql -h 127.0.0.1 -P 3306 -u${mysqlUser} -p${mysqlPassword} -e "source ../sql/建表语句备份.sql" && echo "数据库初始化完成" && echo "创建并授权目录" &&sudo mkdir -vp /opt/cfile && sudo chmod 777 /opt/cfile && echo "开始启动java" && java -Dsys-service=cfile org.springframework.boot.loader.JarLauncher && echo "启动完之后,返回项目根目录" && cd ../
command: # 使用此命令启动服务,且不执行run
root: ./ # 应用的启动目录
name: 在线收集文件项目 # 应用名称
description: 这个项目主要是帮助班级班委,老师等便捷收集班级同学的文件的小项目。
autoOpen: true # 打开工作空间时是否自动开启预览(优先级高于根级 autoOpen
### 支持MySQL 版本为5.6,5.7 其他还没测试过
-- 创建收集文件数据库
CREATE DATABASE /*!32312 IF NOT EXISTS*/ `cfile` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
USE cfile;
DROP TABLE IF EXISTS teacher ;
-- 创建教师用户表
DROP TABLE IF EXISTS teacher;
CREATE TABLE IF NOT EXISTS teacher(
uid INT(11) PRIMARY KEY AUTO_INCREMENT COMMENT '主键自增id',
username VARCHAR(20) NOT NULL COMMENT '用户名',
PASSWORD CHAR(32) NOT NULL COMMENT '密码',
salt CHAR(24) NOT NULL COMMENT '用于密码md5加密的盐',
nickname VARCHAR(20) COMMENT '昵称',
email VARCHAR(50) COMMENT '邮箱',
ctime TIMESTAMP NOT NULL DEFAULT NOW() COMMENT '创建时间',
mtime TIMESTAMP NOT NULL DEFAULT NOW() COMMENT '修改时间',
dr INT(1) NOT NULL DEFAULT 0 COMMENT '是否有效,标记删除'
) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='教师用户表';
-- 截断表
TRUNCATE TABLE teacher;
-- 插入一条默认用户数据
INSERT INTO teacher(username,PASSWORD,salt,nickname,email,ctime) VALUES('admin','ea48576f30be1669971699c09ad05c94','123456','默认用户','s19961234@126.com','2018-11-28 23:14:00');
-- 查询表
SELECT * FROM teacher;
USE cfile;
-- 创建任务表
DROP TABLE IF EXISTS `task`;
CREATE TABLE IF NOT EXISTS task(
tid INT(11) PRIMARY KEY AUTO_INCREMENT COMMENT '主键自增id',
uid INT(11) NOT NULL COMMENT '对应的用户id',
task_name VARCHAR(30) NOT NULL COMMENT '任务名',
dendline DATETIME DEFAULT '2099-01-01 00:00:00' NOT NULL COMMENT '提交截止时间',
file_type VARCHAR(10) DEFAULT 'all' NOT NULL COMMENT '限定提交的文件类型',
submit_num INT(11) DEFAULT '10000' NOT NULL COMMENT '限定提交人数',
submiting_num INT(11) DEFAULT '0' NOT NULL COMMENT '已经提交的人数',
remark VARCHAR(500) DEFAULT '' NOT NULL COMMENT '备注',
authentication TINYINT(1) DEFAULT '0' NOT NULL COMMENT '是否经过验证',
submiting_list text COMMENT '提交了名单',
ctime TIMESTAMP NOT NULL DEFAULT NOW() COMMENT '创建时间',
mtime TIMESTAMP NOT NULL DEFAULT NOW() COMMENT '修改时间',
dr INT(1) NOT NULL DEFAULT 0 COMMENT '是否有效,标记删除'
) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='任务表';
TRUNCATE TABLE task;
-- 插入一个测试任务
INSERT INTO task(uid,task_name,ctime) VALUES(1,'测试任务','2018-12-01 00:00:00');
USE cfile;
SELECT * FROM task;
SELECT * FROM teacher;
SELECT COUNT(*) num FROM task WHERE uid=1 AND task_name='测试任务';
-- 更新提交的人数
UPDATE task SET submiting_num=0 WHERE tid=1;
-- 更新是否通过验证
UPDATE task SET authentication=1 WHERE tid=1;
-- 还原所有任务数据
UPDATE task SET submiting_num=0;
package com.kanlon.cfile;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* 启动类
*
* @author zhangcanlong
* @date 2022/10/13
*/
@SpringBootApplication
@MapperScan("com.kanlon.cfile.dao.mapper")
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
package com.kanlon.cfile;
import com.kanlon.cfile.filter.AuthenFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* spring MVC的配置
*
* @author zhangcanlong
* @date 2022/10/13
*/
@SpringBootConfiguration
public class MySpringMvcConfig implements WebMvcConfigurer {
@Autowired
private AuthenFilter authenFilter;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authenFilter).addPathPatterns("/**");
}
}
package com.kanlon.cfile;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
/**
* servlet初始化
*
* @author zhangcanlong
* @date 2022/10/13
*/
public class ServletInitializer extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(DemoApplication.class);
}
}
package com.kanlon.cfile.configure;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* 项目自身的相关配置
*
* @author zhangcanlong
* @since 2022/10/13 12:24
**/
@Data
@Component
@ConfigurationProperties("project.config")
public class ProjectConfigProperty {
/**
* 默认文件邮件 接收者, 上传文件会发送到邮件
*/
private String defaultFileEmailRec = "s19961234@126.com";
/**
* 上传文件路径
*/
private String updateFileBasePath = "/opt/cfile/upload/student";
}
package com.kanlon.cfile.configure;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
/**
* 线程池配置类
*
* @author zhangcanlong
* @since 2019/3/31 15:43
**/
@Configuration
@EnableAsync
public class TaskAsyncConfigurer implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor threadPool = new ThreadPoolTaskExecutor();
//设置核心线程数
threadPool.setCorePoolSize(10);
//设置最大线程数
threadPool.setMaxPoolSize(100);
//线程池所使用的缓冲队列
threadPool.setQueueCapacity(10);
//等待任务在关机时完成--表明等待所有线程执行完
threadPool.setWaitForTasksToCompleteOnShutdown(true);
// 等待时间 (默认为0,此时立即停止),并没等待xx秒后强制停止
threadPool.setAwaitTerminationSeconds(60);
// 线程名称前缀
threadPool.setThreadNamePrefix("MyAsync-");
// 初始化线程
threadPool.initialize();
return threadPool;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return null;
}
}
package com.kanlon.cfile.controller;
import com.kanlon.cfile.dao.mapper.TeacherUserMapper;
import com.kanlon.cfile.domain.po.TeacherUserPO;
import com.kanlon.cfile.domain.vo.LoginInfoVO;
import com.kanlon.cfile.domain.vo.RegisterInfoVO;
import com.kanlon.cfile.utli.Constant;
import com.kanlon.cfile.utli.JsonResult;
import com.kanlon.cfile.utli.MD5Util;
import com.kanlon.cfile.utli.MailUtil;
import com.kanlon.cfile.utli.RandomUtil;
import com.kanlon.cfile.utli.TimeUtil;
import com.kanlon.cfile.utli.captcha.Captcha;
import com.kanlon.cfile.utli.captcha.CaptchaUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.thymeleaf.util.StringUtils;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.validation.constraints.NotNull;
import java.io.IOException;
import java.io.OutputStream;
/**
* 教师或班委登录的controller
*
* @author zhangcanlong
* @date 2018年11月28日
*/
@Slf4j
@RestController
@RequestMapping("/login")
public class LoginController {
@Resource
private TeacherUserMapper teacherUserMapper;
@Resource
private MailUtil mailUtil;
@Resource
private HttpSession session;
/**
* 老师或班委注册
*
* @param registerVO 注册信息
* @return 是否成功
*/
@PostMapping(value = "/register")
public JsonResult<String> teachRegister(@RequestBody RegisterInfoVO registerVO) {
JsonResult<String> result = new JsonResult<>();
if (StringUtils.isEmptyOrWhitespace(registerVO.getPassword())
|| StringUtils.isEmptyOrWhitespace(registerVO.getUsername())) {
result.setStateCode(Constant.REQUEST_ERROR, "用户名或密码不能为null或全是空白字符");
return result;
}
String sessionCode = (String) session.getAttribute(Constant.SESSION_REG_CAPTCHA);
if (!registerVO.getImgCaptcha().toLowerCase().equals(sessionCode)) {
result.setStateCode(Constant.REQUEST_ERROR, "验证码错误!");
session.removeAttribute(Constant.SESSION_REG_CAPTCHA);
return result;
}
session.removeAttribute(Constant.SESSION_REG_CAPTCHA);
// 检查用户名是否已经存在了
int userNum = teacherUserMapper.selectTeacherByUsername(registerVO.getUsername());
if (userNum >= 1) {
result.setStateCode(Constant.REQUEST_ERROR, "该用户名已存在");
return result;
}
// 如果邮箱不为null,则检查邮箱是否重复
if (!StringUtils.isEmptyOrWhitespace(registerVO.getEmail())) {
if (teacherUserMapper.selectTeacherByEmail(registerVO.getEmail()) >= 1) {
result.setStateCode(Constant.REQUEST_ERROR, "该邮箱已存在");
return result;
}
}
String salt = RandomUtil.createSalt();
String md5Password = MD5Util.encryptPwd(registerVO.getPassword(), salt);
// 将邮箱,用户名,密码,盐存入数据库
TeacherUserPO userPo = new TeacherUserPO();
userPo.setEmail(registerVO.getEmail());
userPo.setUsername(registerVO.getUsername());
userPo.setPassword(md5Password);
userPo.setSalt(salt);
teacherUserMapper.insertUserOne(userPo);
return result;
}
/**
* 获取注册验证码
*
* @param response 响应信息
*/
@GetMapping(value = "/register/captcha")
public void getRegisterCaptcha(HttpServletResponse response) {
try {
response.setContentType("image/png");
OutputStream out = response.getOutputStream();
Captcha captcha = CaptchaUtil.create();
String code = captcha.getCode().toLowerCase();
log.info(code);
// 将验证码放入session中
session.setAttribute(Constant.SESSION_REG_CAPTCHA, code);
captcha.createCaptchaImg(out);
} catch (IOException e) {
e.printStackTrace();
log.error("获取验证码错误!", e);
}
}
/**
* 老师或班委登陆的方法
*
* @param loginVO 注册时的信息
* @return 是否成功
*/
@PostMapping(value = "/teacher")
public JsonResult<String> teachLogin(@RequestBody LoginInfoVO loginVO) {
JsonResult<String> result = new JsonResult<>();
if (StringUtils.isEmptyOrWhitespace(loginVO.getPassword())
|| StringUtils.isEmptyOrWhitespace(loginVO.getUsername())) {
result.setStateCode(Constant.REQUEST_ERROR, "用户名或密码不能为null或全是空白字符");
return result;
}
String sessionCode = (String) session.getAttribute(Constant.SESSION_LOGIN_CAPTCHA);
if (!loginVO.getCaptcha().toLowerCase().equals(sessionCode)) {
result.setStateCode(Constant.REQUEST_ERROR, "验证码错误");
session.removeAttribute(Constant.SESSION_LOGIN_CAPTCHA);
return result;
}
session.removeAttribute(Constant.SESSION_LOGIN_CAPTCHA);
// 根据用户名获取用户信息
TeacherUserPO userPo = teacherUserMapper.getUserByUsernameOrEmail(loginVO.getUsername());
if (userPo == null) {
result.setStateCode(Constant.REQUEST_ERROR, "该用户不存在");
return result;
}
if (!MD5Util.encryptPwd(loginVO.getPassword(), userPo.getSalt()).equals(userPo.getPassword())) {
result.setStateCode(Constant.REQUEST_ERROR, "用户名或密码错误");
return result;
}
session.setAttribute(Constant.SESSION_USER, userPo);
return result;
}
/**
* 获取登陆验证码
*
* @param response 响应
*/
@GetMapping(value = "/login/captcha")
public void getLoginCaptcha(HttpServletResponse response) {
try {
response.setContentType("image/png");
OutputStream out = response.getOutputStream();
Captcha captcha = CaptchaUtil.create();
String code = captcha.getCode().toLowerCase();
log.info(code);
// 将验证码放入session中
session.setAttribute(Constant.SESSION_LOGIN_CAPTCHA, code);
captcha.createCaptchaImg(out);
} catch (IOException e) {
e.printStackTrace();
log.error("获取验证码错误!", e);
}
}
/**
* 获取忘记密码的邮箱验证码
*
* @param email 邮箱
*/
@GetMapping(value = "/forget/password/captcha")
public JsonResult<String> getForgetPasswordEmailCaptcha(String email) {
JsonResult<String> result = new JsonResult<>();
Captcha captcha = CaptchaUtil.create();
String code = captcha.getCode().toLowerCase();
log.info(code);
// 将验证码放入session中
// 十分钟有效,session中存放验证码和创建时间的毫秒值,code#createTime
session.setAttribute(Constant.SESSION_FORGET_PASSWORD_EMAIL_CAPTCHA, code + "#" + System.currentTimeMillis());
// 将用户id存入session中
TeacherUserPO userPo = teacherUserMapper.getUserByUsernameOrEmail(email);
session.setAttribute(Constant.SESSION_FORGET_PASSWORD_UID, userPo.getUid());
// 验证是否存在该邮箱
if (teacherUserMapper.selectTeacherByEmail(email) <= 0) {
result.setStateCode(Constant.REQUEST_ERROR, "该邮箱还没注册");
return result;
}
// 发送邮箱
mailUtil.sendHtmlMail(email, "忘记密码邮箱验证码", "你的忘记密码邮箱验证码为(十分钟内有效):<br/>" + code);
return result;
}
/**
* 忘记密码功能中,根据邮箱验证码修改密码
*
* @param newPassword 新的密码
* @param emailCaptcha 邮箱验证码
* @return 是否成功
*/
@PutMapping(value = "/find/password")
public JsonResult<String> modifyPassword(@NotNull String newPassword, @NotNull String emailCaptcha) {
JsonResult<String> result = new JsonResult<>();
String sessionCaptchaAndCreateTime = (String) session
.getAttribute(Constant.SESSION_FORGET_PASSWORD_EMAIL_CAPTCHA);
if (sessionCaptchaAndCreateTime == null) {
result.setStateCode(Constant.REQUEST_ERROR, "还没发送验证码");
return result;
}
String sessionCaptcha = sessionCaptchaAndCreateTime.split("#")[0];
long createTime = Long.parseLong(sessionCaptchaAndCreateTime.split("#")[1]);
emailCaptcha = emailCaptcha.toLowerCase();
if (!emailCaptcha.equals(sessionCaptcha)) {
result.setStateCode(Constant.REQUEST_ERROR, "邮箱验证码错误");
return result;
} else if (createTime + TimeUtil.TEN_MINUTE < System.currentTimeMillis()) {
result.setStateCode(Constant.REQUEST_ERROR, "邮箱验证码已过期");
return result;
}
int uid = (Integer) session.getAttribute(Constant.SESSION_FORGET_PASSWORD_UID);
TeacherUserPO userPo = teacherUserMapper.getOne(uid);
userPo.setPassword(MD5Util.encryptPwd(newPassword, userPo.getSalt()));
teacherUserMapper.updateUserOneByKey(userPo);
return result;
}
/**
* 登出
*/
@GetMapping(value = "/logout")
public JsonResult<String> logout() {
JsonResult<String> result = new JsonResult<>();
session.removeAttribute(Constant.SESSION_USER);
return result;
}
}
package com.kanlon.cfile.controller;
import com.kanlon.cfile.domain.vo.StudentSubmitFileVO;
import com.kanlon.cfile.domain.vo.StudentTaskInfoVO;
import com.kanlon.cfile.service.TeacherUserService;
import com.kanlon.cfile.utli.IpUtil;
import com.kanlon.cfile.utli.JsonResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.validation.constraints.NotNull;
import java.util.List;
/**
* 学生的提交的controller
*
* @author zhangcanlong
* @date 2018年11月28日
*/
@Slf4j
@RestController
@RequestMapping("/student")
public class StudentController {
@Resource
private TeacherUserService teacherUserService;
@Resource
private HttpServletRequest httpServletRequest;
/**
* 学生提交文件
*
* @param submitVO 提交的信息
* @param uid 用户id
* @param tid 任务id
* @return 上传文件的路径
*/
@RequestMapping(value = "/submit/{uid}/{tid}", method = {RequestMethod.POST})
public JsonResult<String> submitFile(@ModelAttribute StudentSubmitFileVO submitVO, @PathVariable(value = "uid") Integer uid, @PathVariable(value = "tid") Integer tid) {
return teacherUserService.submitFile(submitVO, uid, tid, IpUtil.getRealIP(httpServletRequest));
}
/**
* 得到任务信息
*
* @param tid 任务id
* @return 学生信息
*/
@GetMapping("/task/{tid}")
public JsonResult<StudentTaskInfoVO> getTaskInfo(@PathVariable(value = "tid") @NotNull Integer tid) {
return teacherUserService.getTaskInfo(tid);
}
/**
* 得到已经提交了人的学号
*
* @param uid 用户id
* @param tid 任务id
* @return 学号列表
*/
@GetMapping("/task/list/{uid}/{tid}")
public JsonResult<List<String>> getSubmittingList(@PathVariable @NotNull Integer uid, @PathVariable @NotNull Integer tid) {
return teacherUserService.getSubmittingList(uid, tid);
}
}
package com.kanlon.cfile.controller;
import com.kanlon.cfile.domain.vo.TaskInfoListsVO;
import com.kanlon.cfile.domain.vo.TaskVO;
import com.kanlon.cfile.domain.vo.TeacherCenterVO;
import com.kanlon.cfile.domain.vo.TeacherTaskInfo;
import com.kanlon.cfile.service.TeacherService;
import com.kanlon.cfile.utli.JsonResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotNull;
import javax.websocket.server.PathParam;
import java.util.List;
/**
* 老师或班委的控制类
*
* @author zhangcanlong
* @date 2018年11月30日
*/
@Slf4j
@RestController
@RequestMapping("/teacher")
public class TeacherController {
@Resource
private TeacherService teacherService;
/**
* 创建提交任务
*
* @param task 任务信息
* @return 是否成功
*/
@PostMapping(value = "/task")
public JsonResult<Void> createTask(@RequestBody TaskVO task) {
return teacherService.createTask(task);
}
/**
* 更新任务
*
* @param task 任务信息
* @return 任务id
*/
@PutMapping(value = "/task/{tid}")
public JsonResult<Integer> modifyTask(@RequestBody TaskVO task, @PathVariable(value = "tid") Integer tid) {
return teacherService.modifyTask(task, tid);
}
/**
* 得到个人信息
*
* @return 返回教师或班委个人信息类
*/
@GetMapping(value = "/center/info")
public JsonResult<TeacherCenterVO> getCenterInfo() {
return teacherService.getCenterInfo();
}
/**
* 修改个人中心信息(暂时只能修改昵称)
*
* @param newUserInfo 新的用户信息
* @return 是否成功
*/
@PutMapping(value = "/center/info")
public JsonResult<String> modifyCenterInfo(@RequestBody TeacherCenterVO newUserInfo) {
return teacherService.modifyCenterInfo(newUserInfo);
}
/**
* 获取所有发布的任务列表
*
* @param request 请求
* @return 任务列表
*/
@GetMapping(value = "/all/tasks")
public JsonResult<List<TaskInfoListsVO>> getAllTasks(HttpServletRequest request) {
return teacherService.getAllTasks(request);
}
/**
* 得到任务信息
*
* @param tid 任务id
* @return 任务信息
*/
@GetMapping("/task/{tid}")
public JsonResult<TeacherTaskInfo> getTaskInfo(@PathVariable(value = "tid") @NotNull Integer tid) {
return teacherService.getTaskInfo(tid);
}
/**
* 得到某个任务已经提交的人员的名单(即是文件名,在前端再取相应字段,文件名后端下载需要用到)
*
* @param tid 任务id
* @return 名单列表
*/
@GetMapping("/task/list/{tid}")
public JsonResult<List<String>> getSubmitList(@PathVariable(value = "tid") @NotNull Integer tid) {
return teacherService.getSubmitList(tid);
}
/**
* 根据 任务id获取所有提交的文件的压缩包
*
* @param response 响应
* @param tid 任务id
*/
@GetMapping("/files/{tid}")
public void getSubmitFileZip(HttpServletResponse response, @PathVariable Integer tid) {
teacherService.getSubmitFileZip(response, tid);
}
/**
* 根据 任务id和提交的学号姓名得到该文件
*
* @param response 响应
* @param tid 任务id
* @param filename 文件名 ,学号姓名
*/
@GetMapping("/file/{tid}")
public void getSubmitFile(HttpServletResponse response, @PathVariable(value = "tid") Integer tid,
@PathParam(value = "filename") String filename) {
teacherService.getSubmitFile(response, tid, filename);
}
/**
* 得到某个项目的链接
*
* @param tid 项目id
* @return 项目链接
*/
@GetMapping("/task/link/{tid}")
public JsonResult<String> getTaskLink(@PathVariable(value = "tid") Integer tid) {
return teacherService.getTaskLink(tid);
}
}
package com.kanlon.cfile.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 测试的第一个controller
*
* @author zhangcanlong
* @date 2018年11月27日
*/
@RestController
public class TestController {
/**
* 测试打印出hello world
*
* @return hello world
*/
@RequestMapping("/hello")
public String hello() {
return "Hello World";
}
}
package com.kanlon.cfile.controller;
import com.kanlon.cfile.dao.mapper.TeacherUserMapper;
import com.kanlon.cfile.domain.po.TeacherUserPO;
import com.kanlon.cfile.utli.Constant;
import com.kanlon.cfile.utli.JsonResult;
import com.kanlon.cfile.utli.MailUtil;
import com.kanlon.cfile.utli.TimeUtil;
import com.kanlon.cfile.utli.captcha.Captcha;
import com.kanlon.cfile.utli.captcha.CaptchaUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.annotation.Resource;
import javax.servlet.http.HttpSession;
/**
* 用户的安全控制类
*
* @author zhangcanlong
* @date 2018年12月27日
*/
@Slf4j
@Controller
@RequestMapping("/security")
public class UserSecurityController {
@Resource
private TeacherUserMapper userMapper;
@Resource
private HttpSession session;
@Resource
private MailUtil mailUtil;
/**
* 发送给新邮箱验证码
*
* @param newEmail 新邮箱
* @return 是否成功
*/
@GetMapping(value = "/new/email/captcha")
public JsonResult<String> sendNewEmailCaptcha(String newEmail) {
JsonResult<String> result = new JsonResult<>();
Captcha captcha = CaptchaUtil.create();
String code = captcha.getCode().toLowerCase();
log.info(code);
// 十分钟有效,存放,code#currenttime
session.setAttribute(Constant.SESSION_MODIFY_EMAIL_CAPTCHA, code + "#" + System.currentTimeMillis());
// 发送邮箱
try {
mailUtil.sendHtmlMail(newEmail, "修改邮箱验证码", "修改邮箱验证码为(十分钟内有效):<br/>" + code);
} catch (Exception e) {
log.error(e.getMessage());
result.setStateCode(Constant.RESPONSE_ERROR, "发送邮件时发生异常");
return result;
}
return result;
}
/**
* 修改邮箱
*
* @param email 邮箱地址
* @param captcha 验证码信息
* @return 是否成功
*/
@PutMapping(value = "/new/email")
public JsonResult<String> modifyEmail(String email, String captcha) {
JsonResult<String> result = new JsonResult<>();
try {
String codeAndTime = (String) session.getAttribute(Constant.SESSION_MODIFY_EMAIL_CAPTCHA);
if (codeAndTime == null) {
result.setStateCode(Constant.REQUEST_ERROR, "还没发送验证码");
return result;
}
String sessionCaptcha = codeAndTime.split("#")[0];
long createCaptchaTime = Long.parseLong(codeAndTime.split("#")[1]);
if (!sessionCaptcha.equals(captcha)) {
result.setStateCode(Constant.REQUEST_ERROR, "验证码错误");
return result;
} else if (createCaptchaTime + TimeUtil.TEN_MINUTE < System.currentTimeMillis()) {
result.setStateCode(Constant.REQUEST_ERROR, "验证码过期了");
return result;
}
TeacherUserPO oldUser = (TeacherUserPO) session.getAttribute(Constant.SESSION_USER);
TeacherUserPO emailModifyUser = new TeacherUserPO();
emailModifyUser.setEmail(email);
emailModifyUser.setUid(oldUser.getUid());
userMapper.updateUserOneByKey(emailModifyUser);
} catch (Exception e) {
log.error("修改邮箱异常", e);
result.setStateCode(Constant.RESPONSE_ERROR, "修改邮箱错误!" + e.getMessage());
}
return result;
}
/**
* 修改密码
*
* @param newPassword 新的密码
* @return 是否成功
*/
@PutMapping(value = "/new/password")
public JsonResult<String> modifyPassword(String newPassword) {
JsonResult<String> result = new JsonResult<>();
TeacherUserPO oldUser = (TeacherUserPO) session.getAttribute(Constant.SESSION_USER);
TeacherUserPO passwordModifyUser = new TeacherUserPO();
passwordModifyUser.setEmail(newPassword);
passwordModifyUser.setUid(oldUser.getUid());
userMapper.updateUserOneByKey(passwordModifyUser);
return result;
}
}
package com.kanlon.cfile.dao.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.InsertProvider;
import org.apache.ibatis.annotations.Options;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import org.apache.ibatis.annotations.UpdateProvider;
import org.apache.ibatis.jdbc.SQL;
import com.kanlon.cfile.domain.po.TaskPO;
/**
* 任务实体类的mapper(参考自:http://www.ityouknow.com/springboot/2016/11/06/spring-boo-mybatis.html)
*
* @author zhangcanlong
* @date 2018年11月28日
*/
public interface TaskMapper {
/**
* 获取所有发布的任务
* @param uid 用户id
* @return 任务信息列表集合
**/
@Select("SELECT * FROM task where uid=#{uid} order by mtime desc")
List<TaskPO> getAll(@Param(value = "uid") Integer uid);
/**
* 得到所有任务的数量
* @param uid 用户id
* @return 任务总数
**/
@Select("SELECT count(*) FROM task where uid=#{uid}")
Integer getAllNum(@Param(value = "uid") Integer uid);
/**
* 根据主键id查询出任务信息
*
* @param tid 用户id
* @return 任务信息类
*/
@Select("SELECT * FROM task WHERE tid = #{tid}")
TaskPO getOne(@Param(value = "tid") Integer tid);
/**
* 根据用户id和任务名查询出任务信息
*
* @param uid 用户id主键
* @param taskName 任务名
* @return
*/
@Select("SELECT COUNT(*) NUM FROM task WHERE UID=#{uid} AND TASK_NAME=#{taskName}")
Integer selectTaskNameByUid(@Param(value = "uid") Integer uid, @Param(value = "taskName") String taskName);
/**
* 将一个新的任务插入到数据库中去
* @param taskPO 任务信息
* @return 1
*/
@InsertProvider(type = TaskProvider.class, method = "insertTaskOneSql")
@Options(useGeneratedKeys = true, keyProperty = "tid")
Integer insertOne(TaskPO taskPO);
/**
* 根据一个新的任务信息中的id,更新该任务所有信息
* @param taskPO 新的任务信息
* @return 1
*/
@UpdateProvider(type = TaskProvider.class, method = "updateTaskOneByKeySql")
Integer updateByKey(TaskPO taskPO);
/**
* 根据 任务id在原来已经提交任务人数的基础上+1
*
* @param tid
* 任务id
* @return 1 表示1行
*/
@Update("UPDATE task SET SUBMITING_NUM=SUBMITING_NUM+1 WHERE TID=#{TID}")
Integer updateSubmitingNumByTid(Integer tid);
/**
* 根据任务id删除某条任务
*
* @param id 任务id主键
* @return 1
*/
@Delete("DELETE FROM task WHERE tid =#{tid}")
Integer deleteByKey(Integer id);
/**
* 任务的动态sql提供类
*
* @author zhangcanlong
* @date 2018年12月2日
*/
class TaskProvider {
/**
* 插入一个任务的动态sql
*
* @param task
* @return
*/
public String insertTaskOneSql(TaskPO task) {
return new SQL() {
{
INSERT_INTO("task");
VALUES("uid", "#{uid}");
if (task.getTaskName() != null) {
VALUES("task_name", "#{taskName}");
}
if (task.getDendline() != null) {
VALUES("dendline", "#{dendline}");
}
if (task.getFileType() != null) {
VALUES("file_type", "#{fileType}");
}
if (task.getSubmitNum() != null) {
VALUES("submit_num", "#{submitNum}");
}
if (task.getSubmitingNum() != null) {
VALUES("submiting_num", "#{submitingNum}");
}
if (task.getRemark() != null) {
VALUES("remark", "#{remark}");
}
if (task.getAuthentication() != null) {
VALUES("authentication", "#{authentication}");
}
if (task.getSubmitingList() != null) {
VALUES("submiting_list", "#{submitingList}");
}
if (task.getCtime() != null) {
VALUES("ctime", "#{ctime}");
}
if (task.getMtime() != null) {
VALUES("mtime", "#{mtime}");
}
if (task.getDr() != null) {
VALUES("dr", "#{dr}");
}
}
}.toString();
}
/**
*
* 根据主键tid更新任务方法的动态sql
*
* @param task
* @return
*/
public String updateTaskOneByKeySql(TaskPO task) {
return new SQL() {
{
UPDATE("task");
if (task.getTaskName() != null) {
SET("task_name=#{taskName}");
}
if (task.getDendline() != null) {
SET("dendline=#{dendline}");
}
if (task.getFileType() != null) {
SET("file_type=#{fileType}");
}
if (task.getSubmitNum() != null) {
SET("submit_num=#{submitNum}");
}
if (task.getSubmitingNum() != null) {
SET("submiting_num=#{submitingNum}");
}
if (task.getRemark() != null) {
SET("remark=#{remark}");
}
if (task.getAuthentication() != null) {
SET("authentication=#{authentication}");
}
if (task.getSubmitingList() != null) {
SET("submiting_list=#{submitingList}");
}
if (task.getCtime() != null) {
SET("ctime=#{ctime}");
}
if (task.getMtime() != null) {
SET("mtime=#{mtime}");
}
if (task.getDr() != null) {
SET("dr=#{dr}");
}
WHERE("tid = #{tid}");
}
}.toString();
}
}
}
package com.kanlon.cfile.dao.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.InsertProvider;
import org.apache.ibatis.annotations.Options;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.UpdateProvider;
import org.apache.ibatis.jdbc.SQL;
import com.kanlon.cfile.domain.po.TeacherUserPO;
/**
* 教师或班委实体类的mapper(参考自:http://www.ityouknow.com/springboot/2016/11/06/spring-boo-mybatis.html)
*
* @author zhangcanlong
* @date 2018年11月28日
*/
public interface TeacherUserMapper {
@Select("SELECT * FROM teacher")
@Results({ @Result(property = "username", column = "username") })
List<TeacherUserPO> getAll();
@Select("SELECT * FROM teacher WHERE uid = #{uid}")
@Results({ @Result(property = "username", column = "username") })
TeacherUserPO getOne(Integer id);
@InsertProvider(type = TeacherUserProvider.class, method = "insertUserOneSql")
// 将插入后的id传回到实体类中
@Options(useGeneratedKeys = true, keyProperty = "tid")
Integer insertUserOne(TeacherUserPO user);
@UpdateProvider(type = TeacherUserProvider.class, method = "updateUserOneByKeySql")
void updateUserOneByKey(TeacherUserPO user);
@Delete("DELETE FROM teacher WHERE uid =#{uid}")
void delete(Integer id);
/**
* 查询用户名的数量
*
* @param username
* @return
*/
@Select("select count(*) from teacher where username=#{username}")
Integer selectTeacherByUsername(String username);
/**
* 查询某邮箱地址的数量
*
* @param email
* @return
*/
@Select("select count(*) from teacher where email=#{email}")
Integer selectTeacherByEmail(String email);
/**
* 根据用户名或邮箱查询某用户
*
* @param usernameOrEmail
* 用户名或邮箱
* @return
*/
@Select("SELECT * FROM teacher WHERE username = #{usernameOrEmail} or email=#{usernameOrEmail}")
@Results
TeacherUserPO getUserByUsernameOrEmail(String usernameOrEmail);
/**
* 用户的动态sql提供类
*
* @author zhangcanlong
* @date 2018年12月2日
*/
class TeacherUserProvider {
/**
* 插入一个用户的动态sql
*
* @param user
* @return
*/
public String insertUserOneSql(TeacherUserPO user) {
return new SQL() {
{
INSERT_INTO("teacher");
if (user.getUsername() != null) {
VALUES("username", "#{username}");
}
if (user.getPassword() != null) {
VALUES("password", "#{password}");
}
if (user.getSalt() != null) {
VALUES("salt", "#{salt}");
}
if (user.getNickname() != null) {
VALUES("nickname", "#{nickname}");
}
if (user.getEmail() != null) {
VALUES("email", "#{email}");
}
if (user.getCtime() != null) {
VALUES("ctime", "#{ctime}");
}
if (user.getDr() != null) {
VALUES("dr", "#{dr}");
}
}
}.toString();
}
/**
*
* 根据主键tid更新用户方法的动态sql
*
* @param user
* @return
*/
public String updateUserOneByKeySql(TeacherUserPO user) {
return new SQL() {
{
UPDATE("teacher");
if (user.getUsername() != null) {
SET("username=#{username}");
}
if (user.getPassword() != null) {
SET("password=#{password}");
}
if (user.getSalt() != null) {
SET("salt=#{salt}");
}
if (user.getNickname() != null) {
SET("nickname=#{nickname}");
}
if (user.getEmail() != null) {
SET("email=#{email}");
}
if (user.getCtime() != null) {
SET("ctime=#{ctime}");
}
WHERE("uid = #{uid}");
}
}.toString();
}
}
}
package com.kanlon.cfile.domain.po;
import java.io.Serializable;
import java.util.Date;
import javax.validation.constraints.NotNull;
import lombok.Data;
/**
* 数据库中对应任务表的持久层任务的实体类
*
* @author zhangcanlong
* @date 2018年11月30日
*/
@Data
public class TaskPO implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
/**
* 任务id
*/
private Integer tid;
/**
* 该任务对应的用户id
*/
private Integer uid;
/**
* 任务名称
*/
@NotNull
private String taskName;
/**
* 限定提交截止时间
*/
private Date dendline;
/**
* 限定提交文件类型,对应FileTypeConstant类中的常量
*/
private String fileType;
/**
* 限定应该提交的人数(默认为10000人)
*/
private Integer submitNum;
/**
* 目前已经提交的人数(默认为0)
*/
private Integer submitingNum;
/**
* 本次任务的备注
*/
private String remark;
/**
* 是否经过验证
*/
private Integer authentication;
/**
* 已经提交的名单,默认以学号,逗号分隔,例如:151612220,151612221
*/
private String submitingList;
/**
* 创建的时间
*/
private Date ctime = new Date();
/**
* 修改时间
*/
private Date mtime = new Date();
/**
* 删除标志
*/
private Integer dr = 0;
}
package com.kanlon.cfile.domain.po;
import java.io.Serializable;
import java.util.Date;
import lombok.Data;
/**
* 教师或班委用户对应的数据库的持久层实体类
*
* @author zhangcanlong
* @date 2018年11月28日
*/
@Data
public class TeacherUserPO implements Serializable {
/**
*
*/
private static final long serialVersionUID = 2673741785965677663L;
/**
* 自增主键id
*/
private Integer uid;
/**
* 用户名
*/
private String username;
/**
* 密码
*/
private String password;
/**
* 用于加密的盐
*/
private String salt;
/**
* 昵称
*/
private String nickname;
/**
* 邮箱
*/
private String email;
/**
* 创建时间
*/
private Date ctime = new Date();
/**
* 修改时间
*/
private Date mtime;
/**
* 删除标志
*/
private Integer dr;
}
package com.kanlon.cfile.domain.vo;
/**
* 提交的文件的类型
*
* @author zhangcanlong
* @date 2018年11月30日
*/
public enum FileTypeEnum {
/**
* word文档,以doc或docx结尾
*/
WORD,
/**
* excel文件,xls或xlsx结尾
*/
EXCEL,
/**
* PowerPoint文件,PPT文件,以ppt,pptx,pptm结尾
*/
POWERPOINT,
/**
* image图片文件,以png,jpeg,jpg,tif,BMP,GIF
*/
IMAGE,
/**
* pdf文件
*/
PDF
}
package com.kanlon.cfile.domain.vo;
import java.io.Serializable;
import lombok.Getter;
import lombok.Setter;
/**
* 登陆时的信息
*
* @author zhangcanlong
* @date 2018年11月29日
*/
public class LoginInfoVO implements Serializable {
/**
*
*/
private static final long serialVersionUID = 8948348858012820487L;
/**
* 用户名或邮箱
*/
@Getter
@Setter
private String username;
/**
* 密码
*/
@Getter
@Setter
private String password;
/**
* 登陆验证码
*/
@Getter
@Setter
private String captcha;
}
package com.kanlon.cfile.domain.vo;
import java.io.Serializable;
/**
* 注册信息的展示层实体类
*
* @author zhangcanlong
* @date 2018年11月28日
*/
public class RegisterInfoVO implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1627675005037620143L;
/**
* 用户名
*/
private String username;
/**
* 密码
*/
private String password;
/**
* 图片验证码
*/
private String imgCaptcha = "";
/**
* 邮箱
*/
private String email;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getImgCaptcha() {
return imgCaptcha;
}
public void setImgCaptcha(String imgCaptcha) {
this.imgCaptcha = imgCaptcha;
}
}
package com.kanlon.cfile.domain.vo;
import java.io.Serializable;
import org.springframework.web.multipart.MultipartFile;
/**
* 学生提交文件的展示层vo对象
*
* @author zhangcanlong
* @date 2018年11月28日
*/
public class StudentSubmitFileVO implements Serializable {
/**
*
*/
private static final long serialVersionUID = 4964984763012815442L;
/**
* 学号
*/
private String studentId;
/**
* 姓名
*/
private String name;
/**
* 文件
*/
private MultipartFile file;
/**
* 备注
*/
private String remark;
public String getStudentId() {
return studentId;
}
public void setStudentId(String studentId) {
this.studentId = studentId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public MultipartFile getFile() {
return file;
}
public void setFile(MultipartFile file) {
this.file = file;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
}
package com.kanlon.cfile.domain.vo;
import java.io.Serializable;
import lombok.Data;
/**
* 学生可以看到的项目信息
*
* @author zhangcanlong
* @date 2018年11月30日
*/
@Data
public class StudentTaskInfoVO implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
/**
* 任务名
*/
private String taskName;
/**
* 任务截止时间字符串(2018-11-11 11:12:12)这种形式
*/
private String dendlineStr;
/**
* 提交的文件类型
*/
private String fileType;
/**
* 应该要提交的总人数
*/
private Integer submitNum;
/**
* 该项目备注
*/
private String remark;
/**
* 该项目发布者
*/
private String publisher;
/**
* 验证状态(0为未验证的,1为已验证,2是正在验证的)
*/
private Integer authentication;
}
package com.kanlon.cfile.domain.vo;
import java.io.Serializable;
import lombok.Data;
/**
* 教师或班委查看所有任务时的任务列表实体类
*
* @author zhangcanlong
* @date 2018年11月30日
*/
@Data
public class TaskInfoListsVO implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
/**
* 任务名称
*/
private String taskName;
/**
* 任务id
*/
private Integer tid;
/**
* 应该提交的人数
*/
private Integer submitNum;
/**
* 实际提交的人数
*/
private Integer submitingNum;
/**
* 认证状态(0为未认证,1为已认证,2为认证中)
*/
private Integer authencation;
/**
* 截止时间字符串(2018-11-11 11:12:12)这种形式
*/
private String dendlineStr;
/**
* 限定提交的文件类型
*/
private String fileType;
/**
* 备注信息
*/
private String remark;
}
package com.kanlon.cfile.domain.vo;
import java.io.Serializable;
import javax.validation.constraints.NotNull;
import lombok.Data;
/**
* 创建任务时,提交的信息的实体类展示类
*
* @author zhangcanlong
* @date 2018年11月30日
*/
@Data
public class TaskVO implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
/**
* 任务名称(非空)
*/
@NotNull
private String taskName;
/**
* 任务提交截止时间字符串(以2018-11-02 11:12:11)这种形式
*/
private String dendlineStr;
/**
* 任务提交的文件的类型
*/
private String fileType;
/**
* 预计提交的人数
*/
private Integer submitNum;
/**
* 备注
*/
private String remark;
}
package com.kanlon.cfile.domain.vo;
import java.io.Serializable;
import lombok.Data;
/**
* 教师或班委 个人中心的信息
*
* @author zhangcanlong
* @date 2018年12月27日
*/
@Data
public class TeacherCenterVO implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
/**
* 用户名
*/
private String username;
/**
* 昵称
*/
private String nickname;
/**
* 邮箱
*/
private String email;
/**
* 是否经过验证
*/
private Integer authentication;
}
package com.kanlon.cfile.domain.vo;
import java.io.Serializable;
import java.util.Date;
import javax.validation.constraints.NotNull;
import lombok.Data;
/**
* 教师或班委可以查看到的某个任务的信息
*
* @author zhangcanlong
* @date 2018年12月7日
*/
@Data
public class TeacherTaskInfo implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
/**
* 任务id
*/
private Integer tid;
/**
* 任务名称
*/
@NotNull
private String taskName;
/**
* 限定提交截止时间字符串(如:2018-11-11 10:22:12这种形式)
*/
private String dendlineStr;
/**
* 限定提交文件类型,对应FileTypeConstant类中的常量
*/
private String fileType;
/**
* 限定应该提交的人数(默认为10000人)
*/
private Integer submitNum;
/**
* 目前已经提交的人数(默认为0)
*/
private Integer submitingNum;
/**
* 本次任务的备注
*/
private String remark;
/**
* 是否经过验证
*/
private Integer authentication;
/**
* 已经提交的名单,默认以学号,逗号分隔,例如:151612220,151612221
*/
private String submitingList;
/**
* 创建的时间
*/
private Date ctime = new Date();
/**
* 修改时间
*/
private Date mtime = new Date();
}
package com.kanlon.cfile.exception;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import com.kanlon.cfile.utli.Constant;
import com.kanlon.cfile.utli.JsonResult;
/**
* 全局异常处理类
*
* @author zhangcanlong
* @date 2018年12月11日
*/
@ControllerAdvice
public class GlobalExceptionHandler {
private Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
@ExceptionHandler(value = Exception.class)
@ResponseBody
public JsonResult<String> defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {
JsonResult<String> result = new JsonResult<>();
result.setStateCode(Constant.RESPONSE_ERROR, e.getMessage());
logger.error("内部服务器错误!", e);
return result;
}
}
package com.kanlon.cfile.filter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
/**
* 权限认证的过滤器
*
* @author zhangcanlong
* @date 2018年11月30日
*/
@Component
public class AuthenFilter implements HandlerInterceptor {
private static Logger logger = LoggerFactory.getLogger(AuthenFilter.class);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
String url = request.getRequestURI();
logger.info(url);
// 获取到公开的地址
if (url.equalsIgnoreCase("/")) {
return true;
}
List<String> open_urls = new ArrayList<>();
open_urls.add("/index.html");
open_urls.add("/404.html");
open_urls.add("/login");
open_urls.add("/images");
open_urls.add("/component");
open_urls.add("/common");
open_urls.add("/javascripts");
open_urls.add("/js");
open_urls.add("/stylesheets");
open_urls.add("/css");
open_urls.add("/reg");
open_urls.add("/index");
open_urls.add("/hello");
open_urls.add("/student");
open_urls.add("/error");
// 如果是公开地址,则放行
for (String open_url : open_urls) {
if (url.startsWith(open_url)) {
return true;
}
}
// 判断用户是否已经登录
HttpSession session = request.getSession(false);
if (session == null) {
logger.info("session为空,用户没有登录,准备跳转");
if (isAjax(request)) {
printCNJSON("还没有登陆", response);
} else {
response.sendRedirect("/login.html");
}
return false;
} else {
if (session.getAttribute("user") != null) {
logger.info("用户已经登录");
return true;
}
}
// 执行到这里跳转到登录页面,用户进行身份认证
if (isAjax(request)) {
printCNJSON("还没有登陆", response);
} else {
response.sendRedirect("/login.html");
}
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
/**
* 判断请求是不是Ajax请求
*
* @param request
* @return
*/
private boolean isAjax(HttpServletRequest request) {
return request.getHeader("X-Requested-With") != null
&& "XMLHttpRequest".equals(request.getHeader("X-Requested-With").toString());
}
/**
* 向浏览器输出请求错误的JSON数据
*
* @param result
* @param response
*/
private void printCNJSON(String msg, HttpServletResponse response) {
String result = "{\"code\":1,\"msg\":\"" + msg + "\"}";
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json;charset=utf-8");
try {
PrintWriter writer = response.getWriter();
writer.write(result);
writer.flush();
writer.close();
} catch (IOException e) {
logger.error("发送ajax请求时错误!" + e);
e.printStackTrace();
}
}
}
package com.kanlon.cfile.service;
import com.kanlon.cfile.configure.ProjectConfigProperty;
import com.kanlon.cfile.utli.MailUtil;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.io.File;
/**
* 异步发送邮件的service
*
* @author zhangcanlong
* @since 2022/10/8 21:00
**/
@Service
public class AsyncMailService {
@Resource
private MailUtil mailUtil;
@Resource
private ProjectConfigProperty projectConfigProperty;
/**
* 异步发送邮件
*
* @param fileStorePath 文件存储的地址
* @param fileNewName 新的文件名
* @param clientIp 上传者的ip
**/
@Async
public void sentEmail(File fileStorePath, String fileNewName, String clientIp) {
// 发送邮件,进行备份
String logStr = "文件上传到" + fileStorePath + "/" + fileNewName + "了!" + "发送者IP地址为:" + clientIp;
mailUtil.sendAttachmentsMail(projectConfigProperty.getDefaultFileEmailRec(), "备份-" + fileNewName, logStr, fileStorePath + "/" + fileNewName);
}
}
package com.kanlon.cfile.service;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import java.io.File;
/**
* 异步删除文件
*
* @author zhangcanlong
* @since 2022/10/13 12:13
**/
@Slf4j
@Service
public class AsyncRemoveFileService {
/**
* 删除一个文件
*
* @param fileName 文件名称
*/
@Async
public void removeOneFile(String fileName) {
// 删除压缩成子文件的重复提交文件
File childFilesZip = new File(fileName);
if (childFilesZip.exists()) {
if (!childFilesZip.delete()) {
log.error("删除文件失败!要删除文件的路径为:{}", childFilesZip.getAbsoluteFile());
}
}
}
}
package com.kanlon.cfile.service;
import com.kanlon.cfile.configure.ProjectConfigProperty;
import com.kanlon.cfile.dao.mapper.TaskMapper;
import com.kanlon.cfile.dao.mapper.TeacherUserMapper;
import com.kanlon.cfile.domain.po.TaskPO;
import com.kanlon.cfile.domain.po.TeacherUserPO;
import com.kanlon.cfile.domain.vo.TaskInfoListsVO;
import com.kanlon.cfile.domain.vo.TaskVO;
import com.kanlon.cfile.domain.vo.TeacherCenterVO;
import com.kanlon.cfile.domain.vo.TeacherTaskInfo;
import com.kanlon.cfile.utli.Constant;
import com.kanlon.cfile.utli.JsonResult;
import com.kanlon.cfile.utli.TimeUtil;
import com.kanlon.cfile.utli.ZipFilesUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
/**
* 教师 的操作相关service
*
* @author zhangcanlong
* @since 2022/10/13 11:31
**/
@Slf4j
@Service
public class TeacherService {
@Resource
private TaskMapper taskMapper;
@Resource
private TeacherUserMapper userMapper;
@Resource
private HttpSession session;
@Resource
private AsyncRemoveFileService asyncRemoveFileService;
@Resource
private ProjectConfigProperty projectConfigProperty;
/**
* 创建提交任务
*
* @param task 任务信息
* @return 是否成功
*/
public JsonResult<Void> createTask(TaskVO task) {
JsonResult<Void> result = new JsonResult<>();
// 判断任务名是否为null
if (StringUtils.isEmpty(task.getTaskName())) {
result.setStateCode(Constant.REQUEST_ERROR, "任务名为null");
return result;
}
// 转化文件格式(如果传入的文件格式是null或"",则将其转化为null)
if (StringUtils.isEmpty(task.getFileType())) {
task.setFileType(null);
}
// 转化截止时间(如果传入的截止时间是null或"",则将其转化为null)
if (StringUtils.isEmpty(task.getDendlineStr())) {
task.setDendlineStr(null);
}
// 转化预提交数量(如果传入的截止时间是null或"",则将其转化为null)
if (StringUtils.isEmpty(task.getSubmitNum())) {
task.setSubmitNum(null);
}
TaskPO taskPo = new TaskPO();
TeacherUserPO userPo = (TeacherUserPO) session.getAttribute("user");
Integer uid = userPo.getUid();
// 判断是否有重复的任务名称
if (taskMapper.selectTaskNameByUid(uid, task.getTaskName()) >= 1) {
result.setStateCode(Constant.REQUEST_ERROR, "任务名重复");
return result;
}
taskPo.setUid(userPo.getUid());
taskPo.setTaskName(task.getTaskName());
taskPo.setCtime(new Date());
taskPo.setFileType(task.getFileType());
taskPo.setRemark(task.getRemark());
taskPo.setDendline(TimeUtil.getDateBySimpleDateStr(task.getDendlineStr()));
taskPo.setSubmitNum(task.getSubmitNum());
taskMapper.insertOne(taskPo);
return result;
}
/**
* 更新任务
*
* @param task 任务信息
* @return 更新的任务id
*/
public JsonResult<Integer> modifyTask(TaskVO task, Integer tid) {
JsonResult<Integer> result = new JsonResult<>();
// 判断任务名是否为null
if (StringUtils.isEmpty(task.getTaskName())) {
result.setStateCode(Constant.REQUEST_ERROR, "任务名为null");
return result;
}
// 转化文件格式(如果传入的文件格式是null或"",则将其转化为null)
if (StringUtils.isEmpty(task.getFileType())) {
task.setFileType(null);
}
// 转化截止时间(如果传入的截止时间是null或"",则将其转化为null)
if (StringUtils.isEmpty(task.getDendlineStr())) {
task.setDendlineStr(null);
}
// 转化预提交数量(如果传入的截止时间是null或"",则将其转化为null)
if (StringUtils.isEmpty(task.getSubmitNum())) {
task.setSubmitNum(null);
}
TaskPO taskPo = taskMapper.getOne(tid);
TeacherUserPO userPo = (TeacherUserPO) session.getAttribute("user");
Integer uid = userPo.getUid();
// 如果两个用户id不相等,则表示该用户没有权限修改该任务
if (!taskPo.getUid().equals(uid)) {
result.setStateCode(Constant.REQUEST_ERROR, "你没有权限修改该任务!");
return result;
}
// 判断是否有重复的任务名称()
if (taskMapper.selectTaskNameByUid(uid, task.getTaskName()) >= 1 && !task.getTaskName().equals(taskPo.getTaskName())) {
result.setStateCode(Constant.REQUEST_ERROR, "任务名重复");
return result;
}
taskPo.setTid(tid);
taskPo.setUid(userPo.getUid());
taskPo.setTaskName(task.getTaskName());
taskPo.setMtime(new Date());
taskPo.setFileType(task.getFileType());
taskPo.setRemark(task.getRemark());
taskPo.setDendline(TimeUtil.getDateBySimpleDateStr(task.getDendlineStr()));
taskPo.setSubmitNum(task.getSubmitNum());
taskMapper.updateByKey(taskPo);
result.setData(taskPo.getTid());
return result;
}
/**
* 得到个人信息
*
* @return 返回教师或班委个人信息类
*/
public JsonResult<TeacherCenterVO> getCenterInfo() {
JsonResult<TeacherCenterVO> result = new JsonResult<>();
TeacherUserPO user = (TeacherUserPO) session.getAttribute(Constant.SESSION_USER);
TeacherCenterVO userVO = new TeacherCenterVO();
userVO.setAuthentication(0);
userVO.setEmail(user.getEmail());
userVO.setNickname(user.getNickname());
userVO.setUsername(user.getUsername());
result.setData(userVO);
return result;
}
/**
* 修改个人中心信息(暂时只能修改昵称)
*
* @param newUserInfo 新的用户信息
* @return 是否成功
*/
public JsonResult<String> modifyCenterInfo(TeacherCenterVO newUserInfo) {
JsonResult<String> result = new JsonResult<>();
TeacherUserPO user = (TeacherUserPO) session.getAttribute(Constant.SESSION_USER);
int uid = user.getUid();
TeacherUserPO userPo = new TeacherUserPO();
userPo.setUid(uid);
userPo.setNickname(StringUtils.isEmpty(newUserInfo.getNickname()) ? null : newUserInfo.getNickname());
try {
userMapper.updateUserOneByKey(userPo);
// 修改完后,更新session里面的用户信息
user = userMapper.getOne(uid);
session.setAttribute(Constant.SESSION_USER, user);
} catch (Exception e) {
log.error("更新用户信息时错误!", e);
result.setStateCode(Constant.RESPONSE_ERROR, "修改失败!" + e.getMessage());
}
return result;
}
/**
* 获取所有发布的任务列表
*
* @param request 请求
* @return 任务列表信息
*/
public JsonResult<List<TaskInfoListsVO>> getAllTasks(HttpServletRequest request) {
JsonResult<List<TaskInfoListsVO>> result = new JsonResult<>();
List<TaskInfoListsVO> tasks = new ArrayList<>();
HttpSession session = request.getSession();
TeacherUserPO user = (TeacherUserPO) session.getAttribute("user");
List<TaskPO> taskPoList = taskMapper.getAll(user.getUid());
if (taskPoList == null || taskPoList.isEmpty()) {
return result;
}
for (TaskPO po : taskPoList) {
TaskInfoListsVO taskInfoList = new TaskInfoListsVO();
taskInfoList.setAuthencation(po.getAuthentication());
taskInfoList.setDendlineStr(TimeUtil.getSimpleDateTimeByDate(po.getDendline()));
taskInfoList.setSubmitingNum(po.getSubmitingNum());
taskInfoList.setSubmitNum(po.getSubmitNum());
taskInfoList.setTaskName(po.getTaskName());
taskInfoList.setTid(po.getTid());
taskInfoList.setFileType(po.getFileType());
taskInfoList.setRemark(po.getRemark());
tasks.add(taskInfoList);
}
result.setData(tasks);
return result;
}
/**
* 得到任务信息
*
* @param tid 任务id
* @return 单个任务信息
*/
public JsonResult<TeacherTaskInfo> getTaskInfo(Integer tid) {
JsonResult<TeacherTaskInfo> result = new JsonResult<>();
TaskPO task = taskMapper.getOne(tid);
if (task == null) {
result.setStateCode(Constant.REQUEST_ERROR, "所请求的任务不存在");
return result;
}
TeacherUserPO userPo = (TeacherUserPO) session.getAttribute("user");
Integer uid = userPo.getUid();
// 如果两个用户id不相等,则表示该用户没有权限修改该任务
if (!task.getUid().equals(uid)) {
result.setStateCode(Constant.REQUEST_ERROR, "你没有权限修改该任务!");
return result;
}
TeacherTaskInfo taskInfo = new TeacherTaskInfo();
taskInfo.setTid(tid);
taskInfo.setTaskName(task.getTaskName());
taskInfo.setDendlineStr(TimeUtil.getSimpleDateTimeByDate(task.getDendline()));
taskInfo.setFileType(task.getFileType());
taskInfo.setSubmitNum(task.getSubmitNum());
taskInfo.setSubmitingNum(task.getSubmitingNum());
taskInfo.setRemark(task.getRemark());
taskInfo.setAuthentication(task.getAuthentication());
taskInfo.setSubmitingList(task.getSubmitingList());
taskInfo.setCtime(task.getCtime());
taskInfo.setMtime(task.getMtime());
result.setData(taskInfo);
return result;
}
/**
* 得到某个任务已经提交的人员的名单(即是文件名,在前端再取相应字段,文件名后端下载需要用到)
*
* @param tid 任务id
* @return 人员名单列表
*/
public JsonResult<List<String>> getSubmitList(Integer tid) {
JsonResult<List<String>> result = new JsonResult<>();
List<String> submitList = new ArrayList<>();
TeacherUserPO user = (TeacherUserPO) session.getAttribute("user");
result.setData(submitList);
File submitFile = new File(projectConfigProperty.getUpdateFileBasePath() + "/" + user.getUid() + "/" + tid);
if (!submitFile.exists()) {
return result;
}
File[] submitFiles = Optional.ofNullable(submitFile.listFiles()).orElse(new File[0]);
// 遍历所有提交的文件,得到文件名,从而获取名单
for (File tempFile : submitFiles) {
if (tempFile.isFile()) {
String filename = tempFile.getName();
submitList.add(filename);
}
}
return result;
}
/**
* 根据 任务id获取所有提交的文件的压缩包
*
* @param response 响应
* @param tid 任务id
*/
public void getSubmitFileZip(HttpServletResponse response, Integer tid) {
FileInputStream fis = null;
TeacherUserPO userPo = (TeacherUserPO) session.getAttribute("user");
File files = new File(projectConfigProperty.getUpdateFileBasePath() + "/" + userPo.getUid() + "/" + tid);
try {
if (!files.exists()) {
response.setContentType("text/html");
response.sendRedirect("/404.html");
return;
}
response.setCharacterEncoding("UTF-8");
response.setContentType("application/octet-stream");
File file = ZipFilesUtil.compress(files, "");
if (file == null) {
throw new RuntimeException("压缩文件获取到的文件为null");
}
// 压缩之后可以立刻异步执行删除操作
// 如果不新建一条线程来删除文件,则一旦发生异常,不会继续继续执行删除操作(常见点击下载后,不下载文件)
String tempFileName = projectConfigProperty.getUpdateFileBasePath() + "/" + userPo.getUid() + "/" + tid + "/" + Constant.UPLOAD_FILE_STUDENT_REPEAT_FOLDER + ".zip";
asyncRemoveFileService.removeOneFile(tempFileName);
fis = new FileInputStream(file);
response.setHeader("Content-Disposition", "attachment; filename=" + file.getName());
IOUtils.copy(fis, response.getOutputStream());
response.flushBuffer();
} catch (IOException e) {
log.error("获取文件时发生异常!", e);
throw new RuntimeException("获取文件时发生异常!" + e.getMessage());
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
log.error("获取文件时发生异常!", e);
}
}
}
}
/**
* 根据 任务id和提交的学号姓名得到该文件
*
* @param response 响应
* @param tid 任务id
* @param filename 文件名 ,学号姓名
*/
public void getSubmitFile(HttpServletResponse response, Integer tid, String filename) {
TeacherUserPO userPo = (TeacherUserPO) session.getAttribute("user");
TaskPO taskPo = taskMapper.getOne(tid);
if (taskPo == null || !Objects.equals(taskPo.getUid(), userPo.getUid())) {
throw new RuntimeException("你没有该任务的下载权限!任务id为:" + tid);
}
// 存储对应任务的文件目录
File parentFileDir = new File(projectConfigProperty.getUpdateFileBasePath() + "/" + userPo.getUid() + "/" + tid);
if (!parentFileDir.exists()) {
throw new RuntimeException("对应任务没上传的文件!");
}
File[] theTaskAllUploadFiles = Optional.ofNullable(parentFileDir.listFiles()).orElse(new File[0]);
File file = null;
for (File tempFile : theTaskAllUploadFiles) {
if (Objects.equals(tempFile.getName(), filename)) {
file = tempFile;
}
}
if (file == null) {
throw new RuntimeException("要下载的文件不存在!");
}
try (FileInputStream fis = new FileInputStream(file)) {
if (!file.exists()) {
response.setContentType("text/html");
response.sendRedirect("/404.html");
return;
}
response.setCharacterEncoding("UTF-8");
response.setContentType("application/octet-stream");
// 解决显示不了下载文件时,中文文件名问题,大体的原因就是header中只支持ASCII,所以我们传输的文件名必须是ASCII
String fileName = new String(file.getName().getBytes(), StandardCharsets.ISO_8859_1);
response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
IOUtils.copy(fis, response.getOutputStream());
response.flushBuffer();
} catch (IOException e) {
log.error("获取文件时发生异常!", e);
throw new RuntimeException("获取文件时发生异常!" + e.getMessage());
}
}
/**
* 得到某个项目的链接
*
* @param tid 项目id
* @return 项目链接
*/
public JsonResult<String> getTaskLink(Integer tid) {
JsonResult<String> result = new JsonResult<>();
if (taskMapper.getOne(tid) == null) {
result.setStateCode(Constant.REQUEST_ERROR, "该项目不存在!");
return result;
}
TeacherUserPO userpo = (TeacherUserPO) session.getAttribute("user");
Integer uid = userpo.getUid();
// 构造链接
result.setData("/student.html?" + "uid=" + uid + "&tid=" + tid);
return result;
}
}
package com.kanlon.cfile.service;
import com.kanlon.cfile.configure.ProjectConfigProperty;
import com.kanlon.cfile.dao.mapper.TaskMapper;
import com.kanlon.cfile.dao.mapper.TeacherUserMapper;
import com.kanlon.cfile.domain.po.TaskPO;
import com.kanlon.cfile.domain.po.TeacherUserPO;
import com.kanlon.cfile.domain.vo.StudentSubmitFileVO;
import com.kanlon.cfile.domain.vo.StudentTaskInfoVO;
import com.kanlon.cfile.utli.Constant;
import com.kanlon.cfile.utli.FileUtil;
import com.kanlon.cfile.utli.JsonResult;
import com.kanlon.cfile.utli.TimeUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.FileCopyUtils;
import org.thymeleaf.util.StringUtils;
import javax.annotation.Resource;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Optional;
/**
* 学生的service
*
* @author zhangcanlong
* @since 2022/10/13 11:30
**/
@Slf4j
@Service
public class TeacherUserService {
@Resource
private TaskMapper taskMapper;
@Resource
private TeacherUserMapper userMapper;
@Resource
private AsyncMailService asyncMailService;
@Resource
private ProjectConfigProperty projectConfigProperty;
private static final String DOT = ".";
/**
* 学生提交文件
*
* @param submitVO 提交的信息
* @param uid 用户id
* @param tid 任务id
* @param clientIp 客户端ip
* @return 提交后的文件路径
*/
public JsonResult<String> submitFile(StudentSubmitFileVO submitVO, Integer uid, Integer tid, String clientIp) {
JsonResult<String> result = new JsonResult<>();
// 标志是否已经是提交了重复的文件
boolean flag = false;
if (submitVO.getFile() == null || StringUtils.isEmptyOrWhitespace(submitVO.getName()) || StringUtils.isEmptyOrWhitespace(submitVO.getStudentId())) {
result.setStateCode(Constant.REQUEST_ERROR, "学号或文件或姓名为null");
return result;
}
TaskPO task = taskMapper.getOne(tid);
if (task == null) {
result.setStateCode(Constant.REQUEST_ERROR, "所请求的任务不存在");
return result;
}
// 检查是否已经过了提交时间
if (new Date().after(task.getDendline())) {
result.setStateCode(Constant.REQUEST_ERROR, "已经过了截止时间,不能提交了");
return result;
}
String originalFilename = submitVO.getFile().getOriginalFilename();
if (StringUtils.isEmpty(originalFilename)) {
result.setStateCode(Constant.REQUEST_ERROR, "上传文件名为空,不能提交");
return result;
}
String suffix = ".txt";
if (originalFilename.lastIndexOf(DOT) != -1 && originalFilename.charAt(originalFilename.length() - 1) != DOT.charAt(0)) {
// 新的文件名,学号姓名.后缀名
suffix = submitVO.getFile().getOriginalFilename().substring(submitVO.getFile().getOriginalFilename().lastIndexOf(DOT));
}
String studentIdAndName = submitVO.getStudentId() + submitVO.getName();
String fileNewName = studentIdAndName + suffix;
// 保存上传的文件目录
Path fileStoreDirPath = Paths.get(projectConfigProperty.getUpdateFileBasePath(), String.valueOf(uid), String.valueOf(tid));
File fileStorePath = fileStoreDirPath.toFile();
if (!fileStorePath.exists()) {
if (!fileStorePath.mkdirs()) {
result.setStateCode(Constant.RESPONSE_ERROR, "创建文件路径!路径为:" + fileStorePath.getAbsolutePath());
return result;
}
}
// 临时的上传新的目录文件
Path tempUploadFileNewNamePath = Paths.get(fileStoreDirPath.toString(), fileNewName);
try {
// 校验上传的文件,是否在 指定的目录下,不是,则报错
if (!FileUtil.isSubFile(fileStoreDirPath.toString(), tempUploadFileNewNamePath.toString())) {
result.setStateCode(Constant.REQUEST_ERROR, "非法路径!路径为:" + tempUploadFileNewNamePath.toString());
return result;
}
// 遍历当前文件下所有文件,查看是否存在该学号和姓名,如果存在则将其移动到“重复提交的文件”文件夹并将在其文件名后加时间后,然后存储新的文件,如果不存在,直接存储新文件
File[] tempFiles = Optional.ofNullable(fileStorePath.listFiles()).orElse(new File[0]);
for (File tempFile : tempFiles) {
String tempFileName = tempFile.getName();
// 如果遍历到文件是文件夹或者是没有.号的文件,则直接跳过
if (tempFile.isDirectory() || !tempFileName.contains(DOT)) {
continue;
}
if (tempFileName.substring(0, tempFileName.indexOf(DOT)).equals(studentIdAndName)) {
String repeatFilePath = fileStorePath + "/" + Constant.UPLOAD_FILE_STUDENT_REPEAT_FOLDER;
// 如果重复提交的文件夹不存在,则创建
File repeatPath = new File(repeatFilePath);
if (!repeatPath.exists()) {
if (!repeatPath.mkdirs()) {
result.setStateCode(Constant.RESPONSE_ERROR, "创建文件路径!路径为:" + repeatPath);
return result;
}
}
String oldSuffix = tempFile.getName().substring(tempFile.getName().indexOf(DOT));
File repeatFile = new File(repeatFilePath + "/" + studentIdAndName + TimeUtil.getLocalDateTimeByDate(new Date()) + oldSuffix);
// 路径在指定上传目录下,才能创建
if (FileUtil.isSubFile(fileStorePath, repeatFile) && !repeatFile.createNewFile()) {
log.warn("创建文件失败!要创建的文件为:{}", repeatFile.getAbsoluteFile());
result.setStateCode(Constant.RESPONSE_ERROR, "创建文件失败!文件或路径为:" + repeatFile.getAbsolutePath());
return result;
}
// 这里复制文件名之后会热部署,把之前的request清空
FileCopyUtils.copy(tempFile, repeatFile);
if (!tempFile.delete()) {
result.setStateCode(Constant.RESPONSE_ERROR, "删除文件失败!文件为:" + tempFile.getAbsolutePath());
return result;
}
flag = true;
}
}
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(tempUploadFileNewNamePath.toFile()));
log.info("文件上传到" + tempUploadFileNewNamePath.toString() + "了!" + "发送者IP地址为:" + clientIp);
out.write(submitVO.getFile().getBytes());
out.flush();
out.close();
// 如果不是重复提交,则 增加提交人数
if (!flag) {
taskMapper.updateSubmitingNumByTid(tid);
}
// 异步发送邮件,进行备份
asyncMailService.sentEmail(fileStorePath, fileNewName, clientIp);
} catch (IOException e) {
result.setStateCode(Constant.RESPONSE_ERROR, "存储文件时发生错误!" + e.getMessage());
log.error("存储学生上传文件时发生错误!", e);
return result;
}
return result;
}
/**
* 得到任务信息
*
* @param tid 任务id
* @return 学生信息
*/
public JsonResult<StudentTaskInfoVO> getTaskInfo(Integer tid) {
JsonResult<StudentTaskInfoVO> result = new JsonResult<>();
TaskPO task = taskMapper.getOne(tid);
if (task == null) {
result.setStateCode(Constant.REQUEST_ERROR, "所请求的任务不存在");
return result;
}
StudentTaskInfoVO taskInfo = new StudentTaskInfoVO();
taskInfo.setAuthentication(task.getAuthentication());
taskInfo.setDendlineStr(TimeUtil.getSimpleDateTimeByDate(task.getDendline()));
taskInfo.setFileType(task.getFileType());
taskInfo.setRemark(task.getRemark());
taskInfo.setSubmitNum(task.getSubmitNum());
taskInfo.setTaskName(task.getTaskName());
// 获取昵称
TeacherUserPO userPo = userMapper.getOne(task.getUid());
taskInfo.setPublisher(StringUtils.isEmpty(userPo.getNickname()) ? "默认用户昵称" : userPo.getNickname());
result.setData(taskInfo);
return result;
}
/**
* 得到已经提交了人的学号
*
* @param uid 用户id
* @param tid 任务id
* @return 学号列表
*/
public JsonResult<List<String>> getSubmittingList(Integer uid, Integer tid) {
JsonResult<List<String>> result = new JsonResult<>();
List<String> submitList = new ArrayList<>();
result.setData(submitList);
File submitFile = new File(projectConfigProperty.getUpdateFileBasePath() + "/" + uid + "/" + tid);
if (!submitFile.exists()) {
return result;
}
File[] submitFiles = Optional.ofNullable(submitFile.listFiles()).orElse(new File[0]);
// 遍历所有提交的文件,得到文件名,从而获取名单
for (File tempFile : submitFiles) {
// 如果是文件
if (tempFile.isFile()) {
// 这里限定了前面学号是9位,只截止前面学号
// String filename = cutByRegex(tempFile.getName(),
// "(\\d+){0,9}\\D+.*");
String filename = tempFile.getName().substring(0, 9);
submitList.add(filename);
}
}
return result;
}
}
package com.kanlon.cfile.utli;
import org.springframework.util.ResourceUtils;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
/**
* 保存常用的常量,例如,响应码,项目路径等
*
* @author zhangcanlong
* @date 2018年11月28日
*/
public final class Constant {
// 获取根目录,解决异常
static {
try {
// 获取项目根目录并转码
temp_path = new File(URLDecoder.decode(ResourceUtils.getURL("classpath:").getPath(), "UTF-8")).getPath();
} catch (FileNotFoundException | UnsupportedEncodingException e) {
e.printStackTrace();
}
}
/**
* 临时存储根目录
*/
private static String temp_path;
/**
* 项目根目录
*/
public static final String WEB_ROOT = temp_path;
/**
* 学生提交重复文件的文件夹的文件名常量
*/
public static final String UPLOAD_FILE_STUDENT_REPEAT_FOLDER = "重复提交的文件";
/**
* 正常响应的状态码
*/
public static final int SUCCESS_CODE = 0;
/**
* 前端请求错误的状态码
*/
public static final int REQUEST_ERROR = 1;
/**
* 服务端错误的状态码
*/
public static final int RESPONSE_ERROR = 2;
/**
* 未认证
*/
public static final int UNAUTHENTICATION = 0;
/**
* 已认证
*/
public static final int AUTHENTICATION = 1;
/**
* 认证中
*/
public static final int AUTHENTICATING = 2;
/**
* session中的存放用户信息的常量
*/
public static final String SESSION_USER = "user";
/**
* session中存放忘记密码的的邮箱验证码的key
*/
public static final String SESSION_FORGET_PASSWORD_EMAIL_CAPTCHA = "ForgetPasswordEmailCaptcha";
/**
* 忘记密码时,发送邮箱验证码,在session存放该用户的uid
*/
public static final String SESSION_FORGET_PASSWORD_UID = "ForgetPasswordUid";
/**
* session中存放修改邮箱验证码的key
*/
public static final String SESSION_MODIFY_EMAIL_CAPTCHA = "ModifyEmailCaptcha";
/**
* session中存放注册验证码的key
*/
public static final String SESSION_REG_CAPTCHA = "regCaptcha";
/**
* session中存放登录验证的key
*/
public static final String SESSION_LOGIN_CAPTCHA = "loginCaptcha";
}
package com.kanlon.cfile.utli;
/**
* 提交的文件的类型
*
* @author zhangcanlong
* @date 2018年11月30日
*/
public class FileTypeConstant {
/**
* 所有类型
*/
public final static String ALL = "all";
/**
* word文档,以doc或docx结尾
*/
public final static String WORD = "word";
/**
* excel文件,xls或xlsx结尾
*/
public final static String EXCEL = "excel";
/**
* PowerPoint文件,PPT文件,以ppt,pptx,pptm结尾
*/
public final static String POWERPOINT = "powerpoint";
/**
* image图片文件,以png,jpeg,jpg,tif,BMP,GIF
*/
public final static String IMAGE = "image";
/**
* pdf文件
*/
public final static String PDF = "pdf";
/**
* 压缩包,以zip,rar,7z结尾
*/
public final static String ZIP = "zip";
}
package com.kanlon.cfile.utli;
import java.io.File;
import java.io.IOException;
/**
* 文件的工具类
*
* @author zhangcanlong
* @since 2022/10/13 23:08
**/
public class FileUtil {
private FileUtil() {}
/**
* 是否为父目录的下的子文件或目录
*
* @param parent 父目录文件路径
* @param child 子目录文件路径
* @return boolean
* @throws IOException ioException
*/
public static boolean isSubFile(String parent, String child) throws IOException {
return isSubFile(new File(parent), new File(child));
}
/**
* 是否为父目录的下的子文件或目录
*
* @param parent 父目录文件
* @param child 子目录文件
* @return boolean
* @throws IOException ioException
*/
public static boolean isSubFile(File parent, File child) throws IOException {
return child.getCanonicalPath().startsWith(parent.getCanonicalPath() + File.separator);
}
}
package com.kanlon.cfile.utli;
import javax.servlet.http.HttpServletRequest;
/**
* IP地址工具类 参考自:http://www.ibloger.net/article/144.html
*
* @author zhangcanlong
* @date 2018年11月30日
*/
public class IpUtil {
/**
* 私有化构造器
*/
private IpUtil() {
}
/**
* 获取真实IP地址
* <p>
* 使用getRealIP代替该方法
* </p>
*
* @param request
* req
* @return ip
*/
@Deprecated
public static String getClinetIpByReq(HttpServletRequest request) {
// 获取客户端ip地址
String clientIp = request.getHeader("x-forwarded-for");
if (clientIp == null || clientIp.length() == 0 || "unknown".equalsIgnoreCase(clientIp)) {
clientIp = request.getHeader("Proxy-Client-IP");
}
if (clientIp == null || clientIp.length() == 0 || "unknown".equalsIgnoreCase(clientIp)) {
clientIp = request.getHeader("WL-Proxy-Client-IP");
}
if (clientIp == null || clientIp.length() == 0 || "unknown".equalsIgnoreCase(clientIp)) {
clientIp = request.getRemoteAddr();
}
/*
* 对于获取到多ip的情况下,找到公网ip.
*/
String sIP = null;
if (clientIp != null && !clientIp.contains("unknown") && clientIp.indexOf(",") > 0) {
String[] ipsz = clientIp.split(",");
for (String anIpsz : ipsz) {
if (!isInnerIP(anIpsz.trim())) {
sIP = anIpsz.trim();
break;
}
}
/*
* 如果多ip都是内网ip,则取第一个ip.
*/
if (null == sIP) {
sIP = ipsz[0].trim();
}
clientIp = sIP;
}
if (clientIp != null && clientIp.contains("unknown")) {
clientIp = clientIp.replaceAll("unknown,", "");
clientIp = clientIp.trim();
}
if ("".equals(clientIp) || null == clientIp) {
clientIp = "127.0.0.1";
}
return clientIp;
}
/**
* 判断IP是否是内网地址
*
* @param ipAddress
* ip地址
* @return 是否是内网地址
*/
public static boolean isInnerIP(String ipAddress) {
boolean isInnerIp;
long ipNum = getIpNum(ipAddress);
/**
* 私有IP:A类 10.0.0.0-10.255.255.255 B类 172.16.0.0-172.31.255.255 C类
* 192.168.0.0-192.168.255.255 当然,还有127这个网段是环回地址
**/
long aBegin = getIpNum("10.0.0.0");
long aEnd = getIpNum("10.255.255.255");
long bBegin = getIpNum("172.16.0.0");
long bEnd = getIpNum("172.31.255.255");
long cBegin = getIpNum("192.168.0.0");
long cEnd = getIpNum("192.168.255.255");
isInnerIp = isInner(ipNum, aBegin, aEnd) || isInner(ipNum, bBegin, bEnd) || isInner(ipNum, cBegin, cEnd)
|| ipAddress.equals("127.0.0.1");
return isInnerIp;
}
private static long getIpNum(String ipAddress) {
String[] ip = ipAddress.split("\\.");
long a = Integer.parseInt(ip[0]);
long b = Integer.parseInt(ip[1]);
long c = Integer.parseInt(ip[2]);
long d = Integer.parseInt(ip[3]);
return a * 256 * 256 * 256 + b * 256 * 256 + c * 256 + d;
}
private static boolean isInner(long userIp, long begin, long end) {
return (userIp >= begin) && (userIp <= end);
}
/**
* 得到客户端真实的ip地址
*
* @param request
* @return
*/
public static String getRealIP(HttpServletRequest request) {
// 获取客户端ip地址
String clientIp = request.getHeader("x-forwarded-for");
if (clientIp == null || clientIp.length() == 0 || "unknown".equalsIgnoreCase(clientIp)) {
clientIp = request.getRemoteAddr();
}
String[] clientIps = clientIp.split(",");
if (clientIps.length <= 1) {
return clientIp.trim();
}
// 判断是否来自CDN
if (isComefromCDN(request)) {
if (clientIps.length >= 2) {
return clientIps[clientIps.length - 2].trim();
}
}
return clientIps[clientIps.length - 1].trim();
}
private static boolean isComefromCDN(HttpServletRequest request) {
String host = request.getHeader("host");
return host.contains("www.189.cn") || host.contains("shouji.189.cn")
|| host.contains("image2.chinatelecom-ec.com") || host.contains("image1.chinatelecom-ec.com");
}
}
\ No newline at end of file
package com.kanlon.cfile.utli;
import java.io.Serializable;
/**
* 用来产生返回数据的标准工具类
*
* @author zhangcanlong
* @date 2018年11月28日
*/
public class JsonResult<Result> implements Serializable {
private static final long serialVersionUID = 2773621628776387781L;
/**
* 响应状态(0表示正常,1表示前端请求错误,2表示后端发生错误)
*/
private int code;
/**
* 响应信息
*/
private String msg;
/**
* 响应数据
*/
private Result data;
/**
* 构造函数
*/
public JsonResult() {
this.code = 0;
this.msg = "";
this.data = null;
}
/**
* 设置返回数据的状态码和信息
*
* @param code
* 表示返回数据的状态码
* @param msg
* 描述返回数据的错误信息,正确时候,则为空
*/
public void setStateCode(int code, String msg) {
this.code = code;
this.msg = msg;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public Result getData() {
return data;
}
public void setData(Result data) {
this.data = data;
}
}
\ No newline at end of file
package com.kanlon.cfile.utli;
import java.security.MessageDigest;
/**
* MD5加密工具类
*
* @author zhangcanlong
* @date 2018年11月28日
*/
public class MD5Util {
/**
* 传入原始密码,返回加密之后的密码字符串
*
* @param password
* 原始密码
* @return String 加密后的字符串
*/
public static String md5(String password) {
try {
// 1)创建加密类对象
MessageDigest md = MessageDigest.getInstance("md5");
// 2)进行加密
byte[] byteArray = md.digest(password.getBytes());
StringBuilder sb = new StringBuilder();
for (byte b : byteArray) {
sb.append(numToHex(b));
}
return sb.toString();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
/**
* 利用盐按照一定规则加密密码
*
* @return 加密后的密码
*/
public static String encryptPwd(String password, String salt) {
return md5(password + salt);
}
/**
* 传入一个10进制的字节数值,返回2位的十六进制的字符串
*
* @param num
*
* @return String
*/
private static String numToHex(byte num) {
/*
* byte类型: 无符号位 : 取值范围: 0 ~ 255 有符号为: 取值范围: -128 ~ 127
*/
int targetNum = 0;
// 如果是负数,则先转为正数,再进行计算;否则,直接使用
if (num < 0) {
targetNum = 256 + num;
} else {
targetNum = num;
}
// 第一位字符的值
int first = targetNum / 16;
// 第二位字符的值
int sencond = targetNum % 16;
return strArray[first] + strArray[sencond];
}
private static String[] strArray = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e",
"f" };
}
\ No newline at end of file
package com.kanlon.cfile.utli;
import java.io.File;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.FileSystemResource;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Component;
/**
* 邮箱发送工具类
*
* @author zhangcanlong
* @date 2018年11月27日
*/
@Component
public class MailUtil {
private static final Logger logger = LoggerFactory.getLogger(MailUtil.class);
@Autowired
private JavaMailSender mailSender;
/**
* 发送者
*/
@Value("${mail.fromMail.addr}")
private String from;
/**
* 简单的邮件发送,发送纯文本
*
* @param to
* 接受者
* @param subject
* 主题
* @param content
* 内容
*/
public void sendSimpleMail(String to, String subject, String content) {
SimpleMailMessage message = new SimpleMailMessage();
message.setFrom(from);
message.setTo(to);
message.setSubject(subject);
message.setText(content);
try {
mailSender.send(message);
logger.info("简单邮件已经发送");
} catch (Exception e) {
logger.error("发送简单简单邮件时发生异常!", e);
}
}
/**
* 发送html邮件
*
* @param to
* 接受者
* @param subject
* 主题
* @param content
* 内容
*/
public void sendHtmlMail(String to, String subject, String content) {
MimeMessage message = mailSender.createMimeMessage();
try {
// true表示需要创建一个multipart message
MimeMessageHelper helper = new MimeMessageHelper(message, true);
helper.setFrom(from);
helper.setTo(to);
helper.setSubject(subject);
helper.setText(content, true);
mailSender.send(message);
logger.info("html邮件发送成功");
} catch (MessagingException e) {
logger.error("发送html邮件时发生异常!", e);
throw new RuntimeException("发送邮件时发送异常");
}
}
/**
* 发送带附件的邮件
*
* @param to
* 接受者
* @param subject
* 主题
* @param content
* 内容
* @param filePath
* 文件路径
*/
public void sendAttachmentsMail(String to, String subject, String content, String filePath) {
MimeMessage message = mailSender.createMimeMessage();
try {
MimeMessageHelper helper = new MimeMessageHelper(message, true);
helper.setFrom(from);
helper.setTo(to);
helper.setSubject(subject);
helper.setText(content);
FileSystemResource file = new FileSystemResource(new File(filePath));
String fileName = filePath.substring(filePath.lastIndexOf(File.separator));
helper.addAttachment(fileName, file);
mailSender.send(message);
logger.info("带附件的邮件: " + filePath + "已经发送");
} catch (MessagingException e) {
logger.error("发送带附件的邮件时发送异常!", e);
}
}
/**
* 发送静态资源的邮件
*
* @param to
* 接受者
* @param subject
* 主题
* @param content
* 内容
* @param rscPath
* 静态资源文件路劲
* @param rscId
* 静态资源id
*/
public void sendInlineResoureceMail(String to, String subject, String content, String rscPath, String rscId) {
MimeMessage message = mailSender.createMimeMessage();
try {
MimeMessageHelper helper = new MimeMessageHelper(message, true);
helper.setFrom(from);
helper.setTo(to);
helper.setSubject(subject);
helper.setText(content, true);
FileSystemResource res = new FileSystemResource(new File(rscPath));
helper.addInline(rscId, res);
mailSender.send(message);
logger.info("嵌入静态资源的邮件已经发送");
} catch (MessagingException e) {
logger.error("发送嵌入静态资源的邮件时发生异常!");
}
}
}
package com.kanlon.cfile.utli;
import java.security.SecureRandom;
import java.util.Base64;
import java.util.Random;
/**
* 随机数工具
*
* @author zhangcanlong
* @date 2018年11月28日
*/
public class RandomUtil {
private static final char[] base = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '1', '2', '3', '4', '5', '6',
'7', '8', '9', '0', '_' };
/**
* 生成盐
*
* @return 盐
*/
public static String createSalt() {
Random random = new SecureRandom();
byte[] salt = new byte[16];
random.nextBytes(salt);
return new String(Base64.getEncoder().encode(salt));
}
/**
* 生成随机文件名
*
* @return 随机文件名
*/
public static String createFileName(int fileNameLength) {
Random random = new Random();
StringBuilder sb = new StringBuilder();
sb.append(base[random.nextInt(26)]);
for (int i = 0; i < fileNameLength - 2; i++) {
int num = random.nextInt(base.length);
sb.append(base[num]);
}
// 追加最后一个数字
sb.append(base[random.nextInt(10) + 42]);
return sb.toString();
}
/**
* 生成随机文件名,长度为20位
*
* @return 随机文件名
*/
public static String createFileName() {
return createFileName(20);
}
}
package com.kanlon.cfile.utli;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 时间的工具类
*
* @author zhangcanlong
* @date 2018年11月12日
*/
public class TimeUtil {
private static final Logger logger = LoggerFactory.getLogger(TimeUtil.class);
/**
* 十分钟的毫秒值
*/
public static final long TEN_MINUTE = 10 * 60 * 1000;
/**
* 根据当前时间得到例如20181112190311的时间格式
*
* @param millis
* 时间的毫秒值
* @return 返回yyyyMMddHHmmss之类的时间字符串格式
*/
public static String getLocalDateTimeByDate(Date date) {
SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss");
String dateStr = format.format(date);
return dateStr;
}
/**
* 根据当前时间得到例如2018-11-12 19:03:11的时间格式
*
* @param millis
* 时间毫秒值
* @return 返回yyyy-MM-dd HH:mm:ss之类的时间格式字符串
*/
public static String getSimpleDateTimeByDate(Date date) {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateStr = format.format(date);
return dateStr;
}
/**
* 将本地时间(如:20181112190311)解析为时间类
*
* @param LocalDateStr
* @return
*/
public static Date getDateByLocalDateStr(String LocalDateStr) {
SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss");
Date date = null;
try {
date = format.parse(LocalDateStr);
} catch (ParseException e) {
logger.error("解析时间出现错误!", e);
e.printStackTrace();
}
return date;
}
public static Date getDateBySimpleDateStr(String simpleDateStr) {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = null;
try {
date = format.parse(simpleDateStr);
} catch (ParseException e) {
logger.error("解析时间出现错误!", e);
e.printStackTrace();
}
return date;
}
}
\ No newline at end of file
package com.kanlon.cfile.utli;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 将指定文件夹下的所有文件压缩 参考资料:https://blog.csdn.net/u010366748/article/details/78615004
*
* @author zhangcanlong
* @date 2018年11月30日
*/
public class ZipFilesUtil {
private static Logger logger = LoggerFactory.getLogger(ZipFilesUtil.class);
/**
* 压缩指定目录中所有文件
*
* @param sourceFilePath
* 要压缩的目录
* @param baseDir
* 在压缩目前之上新建一个文件夹,递归方法需要该函数,一般填写""就可以了
*/
public static File compress(File sourceFilePath, String baseDir) {
if (!sourceFilePath.exists()) {
logger.info("待压缩的文件目录或文件" + sourceFilePath.getName() + "不存在");
return null;
}
File[] fs = sourceFilePath.listFiles();
BufferedInputStream bis = null;
ZipOutputStream zos = null;
byte[] bufs = new byte[1024 * 10];
FileInputStream fis = null;
File zipedFile = new File(sourceFilePath.getAbsolutePath() + ".zip");
try {
zos = new ZipOutputStream(new FileOutputStream(zipedFile));
for (int i = 0; i < fs.length; i++) {
String fName = fs[i].getName();
logger.info("压缩:" + baseDir + fName);
if (fs[i].isFile()) {
ZipEntry zipEntry = new ZipEntry(baseDir + fName);
zos.putNextEntry(zipEntry);
// 读取待压缩的文件并写进压缩包里
fis = new FileInputStream(fs[i]);
bis = new BufferedInputStream(fis, 1024 * 10);
int read = 0;
while ((read = bis.read(bufs, 0, 1024 * 10)) != -1) {
zos.write(bufs, 0, read);
}
// 如果需要删除源文件,则需要执行下面2句
// fis.close();
// fs[i].delete();
} else if (fs[i].isDirectory()) {
// 如果需要对子文件夹进行压缩,再执行这条
compress(fs[i], baseDir + fName + "/");
}
}
} catch (IOException e) {
logger.error("压缩文件异常!", e);
throw new RuntimeException("压缩文件时发生异常" + e.getMessage());
} finally {
// 关闭流
try {
if (null != bis) {
bis.close();
}
if (null != zos) {
zos.close();
}
if (null != fis) {
fis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return zipedFile;
}
// public static void main(String[] args) throws ParseException {
// String sourceFilePath = "D:\\程序猿\\bootstrap-3.3.7\\less";
// compress(new File(sourceFilePath), "");
// }
}
package com.kanlon.cfile.utli.captcha;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Random;
import javax.imageio.ImageIO;
/**
* 验证码生成器
*
* @author zhangcanlong
* @date 2018年11月28日
*/
public class Captcha {
/**
* 图片的宽度
*/
private int width = 120;
/**
* 图片的高度
*/
private int height = 50;
/**
* 验证码字符个数
*/
private int codeCount = 5;
/**
* 验证码干扰线数
*/
private int lineCount = 3;
/**
* 验证码字符串
*/
private String code = null;
/**
* 验证码图片buffer
*/
private BufferedImage buffImg = null;
/**
* 随机数
*/
private Random random = new Random();
/**
* 字符队列
*/
private char[] codeSequence = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S',
'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '2', '3', '4', '5', '6', '7', '8', '9' };
public Captcha() {
this(120, 40, 5, 3);
}
/**
* @param width
* 图片宽
* @param height
* 图片高
* @param codeCount
* 字符个数
* @param lineCount
* 干扰线条数
*/
private Captcha(int width, int height, int codeCount, int lineCount) {
this.width = width;
this.height = height;
this.codeCount = codeCount;
this.lineCount = lineCount;
createRandomCaptcha();
}
/**
* 创建随机验证码
*/
private void createRandomCaptcha() {
// randomCode记录随机产生的验证码
StringBuilder randomCode = new StringBuilder();
for (int i = 0; i < codeCount; i++) {
String strRand = String.valueOf(codeSequence[random.nextInt(codeSequence.length)]);
randomCode.append(strRand);
}
// 生成随机字符
this.code = randomCode.toString();
}
/**
* 创建验证码颜色方案
*/
private void createImg() {
int x, fontHeight, codeY;
int red, green, blue;
x = width / (codeCount + 3);// 每个字符的宽度
fontHeight = height - 2;// 字体的高度
codeY = height - 4;
// 图像buffer
buffImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g = buffImg.createGraphics();
// 将图像填充为白色
g.setColor(Color.WHITE);
g.fillRect(0, 0, width, height);
// 创建字体
CaptchaImgFont captchaImgFont = new CaptchaImgFont();
Font font = captchaImgFont.getFont(fontHeight);
g.setFont(font);
// 随机产生codeCount个字符的验证码。
for (int i = 0; i < codeCount; i++) {
// 产生随机的颜色值,让输出的每个字符的颜色值都将不同。
red = random.nextInt(255);
green = random.nextInt(255);
blue = random.nextInt(255);
g.setColor(new Color(red, green, blue));
char[] str = code.toCharArray();
g.drawString(String.valueOf(str[i]), (i + 1) * x, codeY);
// 将产生的四个随机数组合在一起。
}
// 随机生成干扰线的颜色
for (int i = 0; i < lineCount; i++) {
int xs = random.nextInt(width);
int ys = random.nextInt(height);
int xe = xs + random.nextInt(width);
int ye = ys + random.nextInt(height);
red = random.nextInt(255);
green = random.nextInt(255);
blue = random.nextInt(255);
g.setColor(new Color(red, green, blue));
g.drawLine(xs, ys, xe, ye);
}
}
/**
* 创建验证码图片
*
* @param path
* 图片存放路径
* @throws IOException
* 输入输出异常
*/
public void createCaptchaImg(String path) throws IOException {
OutputStream sos = new FileOutputStream(path);
this.createCaptchaImg(sos);
}
/**
* 创建验证码图片
*
* @param sos
* 输出流
* @throws IOException
* 输入输出异常
*/
public void createCaptchaImg(OutputStream sos) throws IOException {
createImg();
ImageIO.write(buffImg, "png", sos);
}
/**
* 获得验证码字符串
*
* @return 验证码字符串
*/
public String getCode() {
if (code == null || "".equals(code)) {
createRandomCaptcha();
}
return code;
}
}
\ No newline at end of file
package com.kanlon.cfile.utli.captcha;
import java.awt.Font;
import java.io.ByteArrayInputStream;
/**
* 验证码图片字体
*/
class CaptchaImgFont {
/**
* 常量,由于太长,放到文件中,服务启动时加载到内存中
*/
private static String fontStr = null;
Font getFont(int fontHeight) {
try {
Font baseFont = Font.createFont(Font.TRUETYPE_FONT, new ByteArrayInputStream(hex2byte(fontStr)));
return baseFont.deriveFont(Font.PLAIN, fontHeight);
} catch (Exception e) {
return new Font("Arial", Font.PLAIN, fontHeight);
}
}
private byte[] hex2byte(String str) {
if (str == null) {
return null;
}
str = str.trim();
int len = str.length();
if (len == 0 || len % 2 == 1) {
return null;
}
byte[] b = new byte[len / 2];
try {
for (int i = 0; i < str.length(); i += 2) {
b[i / 2] = (byte) Integer.decode("0x" + str.substring(i, i + 2)).intValue();
}
return b;
} catch (Exception e) {
return null;
}
}
}
\ No newline at end of file
package com.kanlon.cfile.utli.captcha;
/**
* 验证码工具类
*
* @author zhangcanlong
* @date 2018年11月28日
*/
public class CaptchaUtil {
/**
* 创建新的验证码实例
*
* @return 验证码信息
*/
public static Captcha create() {
return new Captcha();
}
}
\ No newline at end of file
# 标志是正式环境(formal)还是开发环境(develop)
environment=formal
# 项目相关的其他配置
# 默认文件邮件 接收者, 上传文件会发送到邮件
project.config.defaultFileEmailRec=s19961234@126.com
# 上传文件路径
project.config.updateFileBasePath=/opt/cfile/upload/student
# 项目启动配置
# 项目contextPath,一般在正式发布版本中,我们不配置
# server.context-path=/cfile
# 错误页,指定发生错误时,跳转的URL。请查看BasicErrorController源码便知
server.error.path=/error
# 服务端口
server.port=8082
# session最大超时时间(分钟),默认为30
server.session-timeout=30
# tomcat最大线程数,默认为200
server.tomcat.max-threads=800
# tomcat的URI编码
server.tomcat.uri-encoding=UTF-8
#邮件配置
#邮箱服务器地址(smtp.126.com),ip地址(220.181.15.114),
spring.mail.host=smtp.126.com
#用户名
spring.mail.username=****
#密码
spring.mail.password=********
spring.mail.default-encoding=UTF-8
#登录服务器是否需要认证
spring.mail.properties.mail.smtp.auth=true
#SSL证书Socket工厂
spring.mail.properties.mail.smtp.socketFactory.class=javax.net.ssl.SSLSocketFactory
#使用SMTPS协议465端口(必须使用这个端口,25默认端口阿里云服务器封了)
spring.mail.properties.mail.smtp.socketFactory.port=465
#以谁来发送邮件
mail.fromMail.addr=****
# 日志记录
# 日志文件路径(当前为项目根目录)
# 平常测试时使用这个路径,项目根目录
#logging.file=./logs/mylog.log
# 正式部署时使用这个路径
logging.file=/opt/cfile/logs/mylog.log
# 日志级别logging.level:日志级别控制前缀,*为包名或Logger名 , LEVEL:选项TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF
logging.level.com.kanlon.cfile.dao.mapper=DEBUG
logging.level.com.kanlon.cfile.controller=DEBUG
# mybatis设置
mybatis.type-aliases-package=com.kanlon.domain.po
spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/cfile?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true
spring.datasource.username=cfile_admin
spring.datasource.password=cfile_admin_password
# 驼峰自动转下划线
mybatis.configuration.mapUnderscoreToCamelCase=true
#打印出日志
#logging.level.com.kanlon.cfile.dao.mapper=debug
# 设置上传文件的大小
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=10MB
<html lang="zh">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Page not found </title>
<link href="css/404.css" rel="stylesheet" type="text/css" media="screen">
<meta name="robots" content="noindex,follow">
<meta name="generator" content="WordPress 4.9.8">
</head>
<body data-rsssl="1">
<div id="scroll-block" class="">
<div class="remodal-bg">
<!-- START HEADER -->
<header>
<div class="container-fluid"></div>
</header>
<!-- END HEADER -->
<!-- Page Content -->
<section id="container-404">
<div class="container-fluid">
<div class="row row-404">
<div class="col" id="content-404">
<div class="fade-in fade-in-1 in-view fade-in-show"
id="main-404-heading">
<h1>404</h1>
</div>
<div class="fade-in fade-in-2 in-view fade-in-show">
<p>别找了,没有这个页面!</p>
</div>
<img class="astronaut floating" src="/images/404.png"
alt="Moon Man">
<div class="btn-container fade-in fade-in-3 fade-in-show in-view"
id="main-404-btn"></div>
</div>
</div>
</div>
</section>
</div>
</div>
</body>
</html>
\ No newline at end of file
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
/*
* Base structure
*/
/* Move down content because we have a fixed navbar that is 50px tall */
body {
padding-top: 50px;
}
/*
* Global add-ons
*/
.sub-header {
padding-bottom: 10px;
border-bottom: 1px solid #eee;
}
/*
* Top navigation
* Hide default border to remove 1px line.
*/
.navbar-fixed-top {
border: 0;
}
/*
* Sidebar
*/
/* Hide for mobile, show later */
.sidebar {
display: none;
}
@media (min-width: 768px) {
.sidebar {
position: fixed;
top: 51px;
bottom: 0;
left: 0;
z-index: 1000;
display: block;
padding: 20px;
overflow-x: hidden;
overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */
background-color: #f5f5f5;
border-right: 1px solid #eee;
}
}
/* Sidebar navigation */
.nav-sidebar {
margin-right: -21px; /* 20px padding + 1px border */
margin-bottom: 20px;
margin-left: -20px;
}
.nav-sidebar > li > a {
padding-right: 20px;
padding-left: 20px;
}
.nav-sidebar > .active > a,
.nav-sidebar > .active > a:hover,
.nav-sidebar > .active > a:focus {
color: #fff;
background-color: #428bca;
}
/*
* Main content
*/
.main {
padding: 20px;
}
@media (min-width: 768px) {
.main {
padding-right: 40px;
padding-left: 40px;
}
}
.main .page-header {
margin-top: 0;
}
/*
* Placeholder dashboard ideas
*/
.placeholders {
margin-bottom: 30px;
text-align: center;
}
.placeholders h4 {
margin-bottom: 0;
}
.placeholder {
margin-bottom: 20px;
}
.placeholder img {
display: inline-block;
border-radius: 50%;
}
此差异已折叠。
/*!
* IE10 viewport hack for Surface/desktop Windows 8 bug
* Copyright 2014-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
*/
/*
* See the Getting Started docs for more information:
* http://getbootstrap.com/getting-started/#support-ie10-width
*/
@-ms-viewport { width: device-width; }
@-o-viewport { width: device-width; }
@viewport { width: device-width; }
此差异已折叠。
此差异已折叠。
此差异已折叠。
/* 错误标签 */
label.error{
font-size:6px;
color: red;
float: left;
display:none;
}
.form-group{
text-align:left;
}
\ No newline at end of file
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
/*!
* IE10 viewport hack for Surface/desktop Windows 8 bug
* Copyright 2014-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
*/
// See the Getting Started docs for more information:
// http://getbootstrap.com/getting-started/#support-ie10-width
(function () {
'use strict';
if (navigator.userAgent.match(/IEMobile\/10\.0/)) {
var msViewportStyle = document.createElement('style')
msViewportStyle.appendChild(
document.createTextNode(
'@-ms-viewport{width:auto!important}'
)
)
document.querySelector('head').appendChild(msViewportStyle)
}
})();
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。