0%

cc-develop-note-ii

image-20201112194147771

==管理员批量导入新生名单==

==整理会议论文列表==

不用添加老师;导入毕业生名单是另外的功能,只需要学号,把对应学号的学生的毕业状态置位即可

新账户密码:NKcc+身份证后六位,没有身份证的话,用NKcc+学号。后台处理一下 或者 设置密码一列从excel里读取,让使用者自己录入密码。

Excel需要的列:用户名、年级、层次(本、学硕、专硕、博)、身份证号、密码

会议论文列表

https://m.ais.cn/goodMeet

电子信息,计算机科学类,ei检索。偏系统,软件的。找下会议论文列表。有的是会议网站就有,没有的看看谷歌学术或者百度学术有没有搜索指定会议的。

搜索论文:https://www.engineeringvillage.com/search/quick.url

工程索引(The Engineering Index,简称EI)

git储藏

git stash 将当前未提交到本地(和服务器)的代码推入到Git的栈中,操作后的工作区间和上一次提交的内容是完全一样的。

git checkout -b feat-import创建分支并进行切换。git新功能用的前缀是 feat

git stash pop 恢复之前缓存的工作目录。将缓存堆栈中的第一个stash删除,并将对应修改应用到当前的工作目录下。

git stash apply将以前的工作应用回来,但并不删除stash拷贝。

Windows徽标键+ V,打开剪贴板历史记录面板

批量导入毕业生

下载Excel空表

使用 a 标签的 download 属性

1
<a href="src/assets/graduate.xlsx">下载毕业生信息excel表格</a>

image-20201114093539717

路径中 @ 和 . 的区别

import store from ‘. /vuex/store’是相对路径,代表当前路径同级下vuex下的store
import store from ‘ @/vuex/store’也是相对路径,和上面意思差不多,但是具体代表什么路径,要看webpack里面对于@是如何配置的,比如:

1
2
3
4
5
alias: {
'vue$': 'vue/dist/vue. esm.js',
'@': resolve('src'),
Axios':' axios
}

上传Excel

el-upload组件

action 必选参数,上传的地址

手动提交:

1
this.$refs.upload.submit();

显示跨域问题

1
Access to XMLHttpRequest at 'http://localhost:8081/cc-computer-homepage/backend/web/index.php/api/v1/students/batch' from origin 'http://localhost:8084' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
  • 后端没有提供API

backend/config/main.php中配置方法路径和方法名。controller中补充 verbs。在controller中编写方法。

控制器 ID user 以复数形式出现在 users 末端。要获取请求参数,调用 request 组件的 get()-detail) 方法和 post()-detail) 方法。 他们分别返回 $_GET$_POST 的值。先发一个 OPTION 请求,成功之后再发 POST 请求。

1
2
// 设置上传的请求头部
headers: { Authorization: "Bearer " + getCookie("access-token")},
  • 路由管理,数据库中没有该权限

PHP调试

1
return ["success"=> false, "data"=>$UploadTime];

返回 json 格式的数据到输出流,在 Network –> Response 中可以查看

文件保存

调用 common/components/fileUpload.php 的 uploadFile() 函数,将文件保存到设置的路径。拿到该路径存入日志。

获取当前用户 ID

localStorage 和 sessionStorage 属性允许在浏览器中存储 key/value 对的数据。localStorage 用于长久保存整个网站的数据,保存的数据没有过期时间,直到手动去删除。

UserID: getCookie(“realid”)

从前端获取是不安全的,直接从后端获取 UserID :

1
$user_id = \Yii::$app->user->identity->getId();

Yii::$app->user->identity 返回当前登录用户或当前用户未通过身份验证(表示访客)的身份类的实例。

PHPExcel

composer 换源
1
composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/
使用 phpExcel

没找到PHPExcel的命名空间,暂时使用了简单粗暴的方式来引入第三方类库:

在web目录中index.php中:

1
require(__DIR__ . '/../../vendor/phpoffice/phpexcel/Classes/PHPExcel/IOFactory.php');

在 controller 中使用的时候 new \IOFactory 需要加一个反斜杠:

1
$inputFileType = \PHPExcel_IOFactory::identify($path);//自动识别Excel版本
读取指定列的信息

backend/modules/api/v1/models/student/StudentModel.php :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//解析Excel表格,返回毕业生学号数组
private static function readGraduateFromExcel($path)
{
//自动识别Excel版本
$inputFileType = \PHPExcel_IOFactory::identify($path);
$objReader = \PHPExcel_IOFactory::createReader($inputFileType);
$objPHPExcel = $objReader->load($path);
$sheet = $objPHPExcel->getSheet(0);//读取第一个sheet

$highestRow = $sheet->getHighestRow();//取得总行数
$result = [];
//过滤第一行表头,内容从Excel的第二行开始读
for ($i = 2; $i <= $highestRow; $i++) {
//列的索引是从0开始计数
$result[] = $sheet->getCellByColumnAndRow(0, $i)->getValue();
}
return $result;
}
没有考虑学号为空的情况,即:必须填写要添加的毕业生的学号
创建自己的packagist 安装包
  1. 创建GitHub项目并clone到本地

  2. 在项目下打开cmd,输入命令composer init用来创建composer.json,接下来需要在控制台中设置项目名称、描述、作者等信息,方括号里面是默认的信息。其中,资源包的最低稳定版本,默认为 stable。 可选 -dev、-patch、-alpha、-beta 或 -RC;包类型默认选择 library ,License []: mit

  3. 在composer文件中添加以下代码,使安装自动加载

    1
    2
    3
    4
    5
    "autoload": {
    "psr-4": {
    "joyce\\myExcel\\": ""
    }
    },

    就可以使用

    1
    use joyce\myExcel xxx
  4. 进入 packagist 官网(https://packagist.org)注册账户并在设置里面关联自己的GitHub账户,使用GitHub账户登录,点击submit 提交GitHub项目地址,点击check。

  5. 部署自动更新( packgist 官网有教程)

  6. 发布版本 (改动之后需要发布版本 packagist 里面才会更新)

    1
    2
    3
    git log --oneline
    git tag -a '1.0.1' xxxxxx
    git push --tags
  7. 遇到的问题:

    1
    2
    [UnexpectedValueException]
    Error while installing pcy/my-php-excel-packgist, composer-plugin packages should have a class defined in their extra key to be usable.

    是因为包类型选择了 composer-plugin

添加批量导入操作的日志

对于毕业生,excel 表格中只需要有学号信息。在数据库表 cc_student 中根据学号 SID 修改记录,将 IsGraduated 和 StudyStatus 两个字段对应改为 1 和 已毕业。

数据库表 cc_log_batch

用于记录在批量导入过程中,

参考批量创建活动 backend/modules/api/v1/models/activity/ActivityParticipationModel.php

1
$transaction=Yii::$app->db->beginTransaction('SERIALIZABLE');

MySQL数据库提供的四种隔离级别:

① Serializable (串行化):可避免脏读、不可重复读、幻读的发生。

② Repeatable read (可重复读):可避免脏读、不可重复读的发生。

③ Read committed (读已提交):可避免脏读的发生。

④ Read uncommitted (读未提交):最低级别,任何情况都无法保证。

LogBatchModel.php

参照backend/modules/api/v1/models/log/LogApiModel.php编写 LogBatchModel .

核心验证器 safe:该验证器并不进行数据验证。

  • “message”:”Setting unknown property: backend\modules\api\v1\models\log\LogBatchModel::ResStatus”

原因:afterBatch() 函数中 copy-paste 错误。

image-20201127215353617

backend/modules/api/v1/models/student/StudentModel.php:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
//批量导入毕业生,记录日志,成功时返回毕业生SID集合
public static function batchGraduateStudentUpload($data){
date_default_timezone_set('PRC');
$log = LogBatchModel::beforeBatch($data);
$res['success'] = false;
if($log['success']===false)
{
//创建批量导入的日志时出现错误
$res['logBatch']=$log;
return $res;
}
$LBID=$log['data']['LBID'];
$filePath=$data['FilePath'];
try{
//获得毕业生的SID集合
$SIDArray = self::readGraduateFromExcel($filePath);
}catch (Exception $e)
{
//解析 Excel 出现错误
$res['filePath']=$filePath;
return $res;
}

$transaction=Yii::$app->db->beginTransaction('SERIALIZABLE');
try {
for ($i = 0; $i < count($SIDArray); $i++)
{
$SID = $SIDArray[$i];
$newStatus['IsGraduated']=1;
$newStatus['StudyStatus']='已毕业';
//将 IsGraduated 和 StudyStatus 两个字段对应改为 1 和 已毕业
self::updateStudent($SID,$newStatus);
}
$transaction->commit();
$res['data'] = $SIDArray;
} catch (\Exception $exception) {
$transaction->rollBack();
LogBatchModel::afterBatch(false,$LBID);
$res['data'] = $exception->getMessage();
return $res;
}
$res['success'] = true;
LogBatchModel::afterBatch(true,$LBID);
return $res;
}

测试 batchGraduateStudentUpload() ,batch-graduate 接口 Network -> Response:

1
{"success":true,"data":[1713650,1713429,1713032]}

==UpdateStudent() 接口或许有点问题==,在batchGraduateStudentUpload() 中重写了修改学生毕业状态的语句。

在前端给出提示,显示已修改为毕业生的学号或给出错误提示:

image-20201129162233663

src\views\student\StudentInfoManage.vue:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 文件上传成功处理
handleFileSuccess(response, file, fileList) {
this.upload.open = false;
this.upload.isUploading = false;
this.$refs.upload.clearFiles();
if(response.success===true){
this.$alert("以下学号的学生已修改为毕业生:\n"+response.data, "导入结果", { dangerouslyUseHTMLString: true });
}
else{
this.$alert("导入失败,请检查Excel数据格式", "导入结果", { dangerouslyUseHTMLString: true });
}
// 重新获取列表
this.searchStudentInfoManage();
},

批量导入新生

在批量导入毕业生的基础上,按照相同逻辑实现导入新生的功能。尽量整合两个模块。

1
2
3
4
5
6
7
8
9
// 导入新生
handleImportFreshman() {
this.upload.filePath="src/assets/freshman.xlsx";
this.upload.title = "导入新生";
this.upload.url=this.DownBaseUrL + "/students/batch-freshman";
this.attachmentInfo.ModuleName="batchFreshmanStudentUpload";
// console.log("clicked 导入新生 button");
this.upload.open = true;
},

设计新生excel

学号、姓名、年级、层次、专业、身份证号、密码、导师。

层次和专业设置下拉菜单。将模板 freshman.xlsx 放入 cc-frontend\src\assets 文件夹下。

  • 修改Excel表格,修改可选项、必填项,增加导师列 (数据 –> 数据验证)

数据库表

需要进行插入的表:cc_major_enrollyear、cc_student、cc_user、cc_auth_assignment(如果是导入学生的话需要插两条:学生、游客)

关于“层次”属性需要查表 cc_education_level ,考虑到条目不多,可以直接在后端做映射而不去数据库查表。为了避免数据库有变更,还是查表吧。

添加 user、student 参照 backend/modules/api/v1/controllers/UserController.php 的 actionSignup()。cc_student 的 EnrollDate 设置为上半年/下半年

添加 assignment 参照 vendor/clement/yii-rest-rbac2.0/controllers/AssignmentController.php 的 actionAssign($id)

backend/config/main.php:"class" => "clement\\rest\Module",

可能会存储失败的情况:学号为空、找不到导师、找不到 MajorName & EducationLevelID 对应的 MID(cc_major表)、找不到 年级 & MajorID 对应的 MEYID(cc_major_enrollyear表)

密码明文默认111111

password加密传输

常用的签名算法:MD5 和 HMAC-SHA256

  • 验证 yii2 和 vue 对密码的 hash 算法结果是否一致,用户登录或新建单个用户时输入密码后由前端进行 HMAC-SHA256 加密存入数据库(src\components\admin\UserInfo.vue),但批量导入数据库时是由后端来加密密码存入数据库,需要保持加密结果一致。
1
2
import {sha256} from 'js-sha256';
console.log(sha256.hmac('111111', 'cc-frontend-vue'));

image-20201130221713885

1
2
(property) Hash.hmac: Hmac
(secretKey: string, message: Message) => string

注意与 PHP 参数的顺序不同:

1
function hash_hmac ($algo, $data, $key, $raw_output = false) {}
1
2
3
4
<?php
$hash = hash_hmac('SHA256', 'cc-frontend-vue','111111');
echo "result = " . $hash;
?>

xampp/htdocs/dashboard/phpinfo.php,访问http://localhost:8081/dashboard/phpinfo.php

后端 batch-freshman 接口

backend/config/main.php: (‘controller’ => ‘api/v1/student’)

1
'POST,OPTIONS batch-freshman' => 'batch-freshman-student-upload',

backend/modules/api/v1/controllers/StudentController.php:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//批量导入新生
public function actionBatchFreshmanStudentUpload()
{
$request = \Yii::$app->request;
if ($request->getIsOptions()) {
return $this->ResponseOptions($this->verbs()['batch-freshman-student-upload']);
}
$upload = new fileUpload(false, 2);
$res = $upload->uploadFile();
if ($res->{'success'} == false) {
return ['success' => false, 'data' => $res->{'error'}];
}
$temp['FilePath']=$res->{'data'}['Destination'];
$temp['UploadTime']=$request->post('UploadTime');
$temp['UserID'] = \Yii::$app->user->identity->getId();
$temp['ModuleName'] = $request->post('ModuleName');
$temp['IsImported']=0;
return StudentModel::batchFreshmanStudentUpload($temp);
}

解析新生Excel表格

backend/modules/api/v1/models/student/StudentModel.php :

  • 如果某一格为空?
1
public function rangeToArray($pRange = 'A1', $nullValue = null, $calculateFormulas = true, $formatData = true, $returnCellRef = false)
  • 由于在后面的列中编写了下拉列表,因此不能用函数来获取总行数总列数?
1
2
$highestRow = $sheet->getHighestRow();//取得总行数
$highestCol = $sheet->getHighestColumn();//取得总行数

解决:将下拉列表的选项放在新的 sheet 中

{“success”:true,

“data”:[[[2011430,”张一”,2020,”本科”,null,”123654166623564612”,111111,”温延龙”]],[[2011431,”张二”,2017,”博士”,”信息安全”,”369852232365894626”,111111,”宋春瑶”]],[[2011432,”张三”,2018,”硕士”,”软件工程”,null,111111,”王刚强”]],[[null,”张四”,2019,”专业硕士”,”运筹学与控制论”,null,null,”温延龙”]],[[2011434,”张五”,2020,”本科”,”计算机科学与技术”,null,111111,”袁晓洁”]]],

“logBatch”:{“success”:true,”data”:{“FilePath”:”/storager/2020_year/12_month/02_day/1c7b9bac371e8038e26b3081fee650e3/1c7b9bac371e8038e26b3081fee650e3.xlsx”,”UploadTime”:”2020-12-02 10:56:00”,”UserID”:64041,”ModuleName”:”batchFreshmanStudentUpload”,”IsImported”:0,”LBID”:18}}}

batchFreshmanStudentUpload($data) 得到 excel 解析内容后,先对异常数据进行预处理。

新生数组数据处理

哪些不能为空?MajorEnrollYearID –> 专业、年级、层次都不能为空;StudentName;SID。

创建 user 时仿照 backend/modules/api/v1/controllers/UserController.php 的 actionSignup()

测试 batchFreshmanStudentUpload($data) 报错:

1
{"success":false,"data":"Undefined offset: 1"}

image-20201203120812297

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$debugOffset=true;
if($debugOffset===true)
{
for ($i = 0; $i < count($StudentArray); $i++)
{
$row = $StudentArray[$i][0];
$student=[];
for($j=0;$j<count($key);$j++)
{
$student[$key[$j]]=$row[$j];
}
$res['data'][]=$student;
}
for ($i = 0; $i < count($StudentArray); $i++)
$res['SID'][] = $StudentArray[$i][0][0];
return $res;
}

测试:

1
2
3
$res['StudentArray']=$StudentArray;
$res['Student']=$StudentArray[1];
$res['SID']=$StudentArray[1][0][0];

结果:

1
2
3
4
5
6
7
{
"success":false,
"StudentArray":
[[[2011430,"张一",2017,"本科","自动化","123654166623564612",111111,null]],[[2011431,"张二",2017,"本科","信息安全","369852232365894626",111111,null]],[[2011432,"张三",2018,"博士","软件工程",null,111111,"王刚(计)"]],[[2011431,"张四",2019,"硕士","计算机应用技术",null,123456,"温延龙"]],[[2011434,"张五",2017,"本科","计算机科学与技术",null,111111,null]]],
"Student":[[2011431,"张二",2017,"本科","信息安全","369852232365894626",111111,null]],
"SID":2011431
}

是因为每行多了一层数组。

提交

前端不需要提交 package-lock.json、dev.env.js、http.js、util.js

后端:

1
2
3
git add .
git commit -m "feat: 批量导入毕业生名单;批量导入新生名单"
git push origin feat-import:feat-import