mirror of
https://github.com/kuaifan/dootask.git
synced 2025-12-11 18:42:54 +00:00
feat: 支持人脸打卡设备
This commit is contained in:
parent
a69b01ecf5
commit
5413457b6b
@ -393,6 +393,7 @@ class SystemController extends AbstractController
|
||||
'remindin',
|
||||
'remindexceed',
|
||||
'edit',
|
||||
'faceupload',
|
||||
'modes',
|
||||
'key',
|
||||
])) {
|
||||
@ -414,6 +415,7 @@ class SystemController extends AbstractController
|
||||
}
|
||||
//
|
||||
$setting['open'] = $setting['open'] ?: 'close';
|
||||
$setting['faceupload'] = $setting['faceupload'] ?: 'close';
|
||||
$setting['time'] = $setting['time'] ? Base::json2array($setting['time']) : ['09:00', '18:00'];
|
||||
$setting['advance'] = intval($setting['advance']) ?: 120;
|
||||
$setting['delay'] = intval($setting['delay']) ?: 120;
|
||||
|
||||
@ -20,6 +20,7 @@ use App\Models\UmengAlias;
|
||||
use App\Models\UserDelete;
|
||||
use App\Models\UserTransfer;
|
||||
use App\Models\AbstractModel;
|
||||
use App\Models\UserCheckinFace;
|
||||
use App\Models\UserCheckinMac;
|
||||
use App\Models\UserDepartment;
|
||||
use App\Models\WebSocketDialog;
|
||||
@ -775,6 +776,14 @@ class UsersController extends AbstractController
|
||||
}
|
||||
return $user;
|
||||
});
|
||||
// user_face
|
||||
$list->transform(function (User $user) use ($getCheckinMac) {
|
||||
if ($getCheckinMac) {
|
||||
$checkinFace = UserCheckinFace::query()->whereUserid($user->userid)->first();
|
||||
$user->checkin_face = $checkinFace ? Base::fillUrl($checkinFace->faceimg) : '';
|
||||
}
|
||||
return $user;
|
||||
});
|
||||
}
|
||||
//
|
||||
return Base::retSuccess('success', $list);
|
||||
@ -795,6 +804,7 @@ class UsersController extends AbstractController
|
||||
* - settemp 设为临时帐号
|
||||
* - cleartemp 取消临时身份(取消临时帐号)
|
||||
* - checkin_macs 修改自动签到mac地址(需要参数 checkin_macs)
|
||||
* - checkin_face 修改签到人脸图片(需要参数 checkin_face)
|
||||
* - department 修改部门(需要参数 department)
|
||||
* - setdisable 设为离职(需要参数 disable_time、transfer_userid)
|
||||
* - cleardisable 取消离职
|
||||
@ -805,6 +815,7 @@ class UsersController extends AbstractController
|
||||
* @apiParam {String} [nickname] 昵称
|
||||
* @apiParam {String} [profession] 职位
|
||||
* @apiParam {String} [checkin_macs] 自动签到mac地址
|
||||
* @apiParam {String} [checkin_face] 人脸图片地址
|
||||
* @apiParam {String} [department] 部门
|
||||
* @apiParam {String} [disable_time] 离职时间
|
||||
* @apiParam {String} [transfer_userid] 离职交接人
|
||||
@ -869,6 +880,11 @@ class UsersController extends AbstractController
|
||||
}
|
||||
return UserCheckinMac::saveMac($userInfo->userid, $array);
|
||||
|
||||
case 'checkin_face':
|
||||
$faceimg = $data['checkin_face'] ? $data['checkin_face'] : '';
|
||||
|
||||
return UserCheckinFace::saveFace($userInfo->userid, $userInfo->nickname, $faceimg, "管理员上传");
|
||||
|
||||
case 'department':
|
||||
if (!is_array($data['department'])) {
|
||||
$data['department'] = [];
|
||||
@ -1643,8 +1659,16 @@ class UsersController extends AbstractController
|
||||
$user = User::auth();
|
||||
//
|
||||
$list = UserCheckinMac::whereUserid($user->userid)->orderBy('id')->get();
|
||||
$userface = UserCheckinFace::whereUserid($user->userid)->first();
|
||||
|
||||
// 组装数据
|
||||
// TODO 如何获取http连接
|
||||
$data = [
|
||||
'list' => $list,
|
||||
'faceimg'=> $userface ? Base::fillUrl($userface->faceimg) : ''
|
||||
];
|
||||
//
|
||||
return Base::retSuccess('success', $list);
|
||||
return Base::retSuccess('success', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1674,6 +1698,7 @@ class UsersController extends AbstractController
|
||||
}
|
||||
//
|
||||
$list = Request::input('list');
|
||||
$faceimg = Request::input('faceimg');
|
||||
$array = [];
|
||||
if (empty($list) || !is_array($list)) {
|
||||
return Base::retError('参数错误');
|
||||
@ -1691,8 +1716,31 @@ class UsersController extends AbstractController
|
||||
if (count($array) > 3) {
|
||||
return Base::retError('最多只能添加3个MAC地址');
|
||||
}
|
||||
//
|
||||
return UserCheckinMac::saveMac($user->userid, $array);
|
||||
// TODO 后续考虑是否单独写一个接口
|
||||
if ($setting['faceupload'] !== 'open' && $faceimg != '') {
|
||||
return Base::retError('未开放修改权限,请联系管理员');
|
||||
}
|
||||
if ($setting['faceupload'] === 'open') {
|
||||
try{
|
||||
$saveFaceRes = UserCheckinFace::saveFace($user->userid, $user->nickname(), $faceimg, "用户上传");
|
||||
if ($saveFaceRes['ret'] == 0) {
|
||||
return $saveFaceRes;
|
||||
}
|
||||
} catch(\Throwable) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
$saveMacRes = UserCheckinMac::saveMac($user->userid, $array);
|
||||
|
||||
|
||||
$data = [
|
||||
'list' => $saveMacRes['data'],
|
||||
'faceimg' => $faceimg
|
||||
];
|
||||
$saveMacRes['data'] = $data;
|
||||
return $saveMacRes;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -223,7 +223,20 @@ class UserBot extends AbstractModel
|
||||
'remark' => '手动签到',
|
||||
];
|
||||
}
|
||||
}
|
||||
} elseif (Base::leftExists($mac, "checkin-", true)) {
|
||||
$mac = Base::leftDelete($mac, "checkin-", true);
|
||||
if ($UserInfo = User::whereUserid($mac)->whereBot(0)->first()) {
|
||||
$array = [
|
||||
'userid' => $UserInfo->userid,
|
||||
'mac' => '00:00:00:00:00:00',
|
||||
'date' => $nowDate,
|
||||
];
|
||||
$checkins[] = [
|
||||
'userid' => $UserInfo->userid,
|
||||
'remark' => '考勤机',
|
||||
];
|
||||
}
|
||||
}
|
||||
if ($array) {
|
||||
$record = UserCheckinRecord::where($array)->first();
|
||||
if (empty($record)) {
|
||||
|
||||
108
app/Models/UserCheckinFace.php
Normal file
108
app/Models/UserCheckinFace.php
Normal file
@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Exceptions\ApiException;
|
||||
use App\Module\Base;
|
||||
use App\Module\Ihttp;
|
||||
|
||||
/**
|
||||
* App\Models\UserCheckinFace
|
||||
*
|
||||
* @property int $id
|
||||
* @property int|null $userid 会员id
|
||||
* @property string|null $faceimg 人脸图片
|
||||
* @property int|null $status 状态
|
||||
* @property string|null $remark 备注
|
||||
* @property \Illuminate\Support\Carbon|null $created_at
|
||||
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|UserCheckinFace newModelQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|UserCheckinFace newQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|UserCheckinFace query()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|UserCheckinFace whereCreatedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|UserCheckinFace whereFaceimg($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|UserCheckinFace whereId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|UserCheckinFace whereRemark($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|UserCheckinFace whereStatus($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|UserCheckinFace whereUpdatedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|UserCheckinFace whereUserid($value)
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class UserCheckinFace extends AbstractModel
|
||||
{
|
||||
|
||||
public static function saveFace($userid, $nickname, $faceimg, $remark='')
|
||||
{
|
||||
// 取上传图片的URL
|
||||
$faceimg = Base::unFillUrl($faceimg);
|
||||
$record = "";
|
||||
if ($faceimg != '') {
|
||||
$faceFile = public_path($faceimg);
|
||||
$record = base64_encode(file_get_contents($faceFile));
|
||||
}
|
||||
|
||||
$url = 'http://' . env('APP_IPPR') . '.55' . ":7788/user";
|
||||
$data = [
|
||||
'name' => $nickname,
|
||||
'enrollid' => $userid,
|
||||
'admin' => 0,
|
||||
'backupnum' => 50,
|
||||
];
|
||||
if ($record != '') {
|
||||
$data['record'] = $record;
|
||||
}
|
||||
|
||||
$res = Ihttp::ihttp_post($url, json_encode($data));
|
||||
if($res['data'] && $data = json_decode($res['data'])){
|
||||
if($data->ret != 1 && $data->msg){
|
||||
return Base::retError($data->msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return AbstractModel::transaction(function() use ($userid, $faceimg, $remark) {
|
||||
// self::updateInsert([
|
||||
// 'userid' => $userid,
|
||||
// 'faceimg' => $faceimg,
|
||||
// 'status' => 1,
|
||||
// 'remark' => $remark
|
||||
// ]);
|
||||
$checkinFace = self::query()->whereUserid($userid)->first();
|
||||
if ($checkinFace) {
|
||||
self::updateData(['id' => $checkinFace->id], [
|
||||
'faceimg' => $faceimg,
|
||||
'status' => 1,
|
||||
'remark' => $remark
|
||||
]);
|
||||
} else {
|
||||
$checkinFace = new UserCheckinFace();
|
||||
$checkinFace->faceimg = $faceimg;
|
||||
$checkinFace->userid = $userid;
|
||||
$checkinFace->remark = $remark;
|
||||
$checkinFace->save();
|
||||
}
|
||||
if ($faceimg == '') {
|
||||
$res = UserCheckinFace::deleteDeviceUser($userid);
|
||||
if ($res) {
|
||||
return $res;
|
||||
}
|
||||
}
|
||||
return Base::retSuccess('上传成功');
|
||||
});
|
||||
}
|
||||
|
||||
public static function deleteDeviceUser($userid) {
|
||||
$url = 'http://' . env('APP_IPPR') . '.55' . ":7788/user/delete";
|
||||
$data = [
|
||||
'enrollid' => $userid,
|
||||
'backupnum' => 50,
|
||||
];
|
||||
|
||||
$res = Ihttp::ihttp_post($url, json_encode($data));
|
||||
if($res['data'] && $data = json_decode($res['data'])){
|
||||
if($data->ret != 1 && $data->msg){
|
||||
return Base::retError($data->msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateUserCheckinFacesTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
if (Schema::hasTable('user_checkin_faces'))
|
||||
return;
|
||||
|
||||
Schema::create('user_checkin_faces', function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->bigInteger('userid')->nullable()->default(0)->comment('会员id');
|
||||
$table->string('faceimg', 100)->nullable()->default('')->comment('人脸图片');
|
||||
$table->integer('status')->nullable()->default(0)->comment('状态');
|
||||
$table->string('remark',100)->nullable()->default('')->comment('备注');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('user_checkin_faces');
|
||||
}
|
||||
}
|
||||
@ -237,6 +237,34 @@
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
|
||||
<!--修改Face-->
|
||||
<Modal
|
||||
v-model="checkinFaceEditShow"
|
||||
:title="$L('修改签到人脸图片')">
|
||||
<Form :model="checkinMacEditData" label-width="auto" @submit.native.prevent>
|
||||
<Alert type="error" style="margin-bottom:18px">{{$L(`正在进行帐号【ID:${checkinFaceEditData.userid},${checkinFaceEditData.nickname}】人脸图片修改。`)}}</Alert>
|
||||
<Row class="team-department-checkin-item">
|
||||
<Col span="12">{{$L('人脸图片')}}</Col>
|
||||
<Col span="12"></Col>
|
||||
</Row>
|
||||
<Row class="team-department-checkin-item">
|
||||
<Col span="12">
|
||||
<ImgUpload v-model="checkinFaceEditData.faceimg" :num="1" :width="512" :height="512" :whcut="1"></ImgUpload>
|
||||
<span class="form-tip">{{$L('建议尺寸:200x200')}}</span>
|
||||
</Col>
|
||||
<Col span="12">
|
||||
<!-- <Input v-model="item.remark" :maxlength="100" :placeholder="$L('备注')"/> -->
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
</Form>
|
||||
<div slot="footer" class="adaption">
|
||||
<Button type="default" @click="checkinFaceEditShow=false">{{$L('取消')}}</Button>
|
||||
<Button type="primary" :loading="checkinFaceEditLoading > 0" @click="operationUser(checkinFaceEditData, true)">{{$L('确定修改')}}</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
<!--修改部门-->
|
||||
<Modal
|
||||
v-model="departmentEditShow"
|
||||
@ -300,10 +328,11 @@
|
||||
<script>
|
||||
import UserSelect from "../../../components/UserSelect.vue";
|
||||
import UserAvatarTip from "../../../components/UserAvatar/tip.vue";
|
||||
import ImgUpload from "../../../components/ImgUpload";
|
||||
|
||||
export default {
|
||||
name: "TeamManagement",
|
||||
components: {UserAvatarTip, UserSelect},
|
||||
components: {UserAvatarTip, UserSelect, ImgUpload},
|
||||
props: {
|
||||
checkinMac: {
|
||||
type: Boolean,
|
||||
@ -587,6 +616,12 @@ export default {
|
||||
command: 'checkin_mac',
|
||||
},
|
||||
}, [h('div', this.$L('修改MAC'))]))
|
||||
|
||||
dropdownItems.push(h('EDropdownItem', {
|
||||
props: {
|
||||
command: 'checkin_face',
|
||||
},
|
||||
}, [h('div', this.$L('修改人脸图片'))]))
|
||||
}
|
||||
|
||||
dropdownItems.push(h('EDropdownItem', {
|
||||
@ -667,6 +702,10 @@ export default {
|
||||
checkinMacEditLoading: 0,
|
||||
checkinMacEditData: {},
|
||||
|
||||
checkinFaceEditShow: false,
|
||||
checkinFaceEditLoading: 0,
|
||||
checkinFaceEditData: {},
|
||||
|
||||
departmentEditShow: false,
|
||||
departmentEditLoading: 0,
|
||||
departmentEditData: {},
|
||||
@ -951,6 +990,16 @@ export default {
|
||||
}
|
||||
this.checkinMacEditShow = true;
|
||||
break;
|
||||
case 'checkin_face':
|
||||
this.checkinFaceEditData = {
|
||||
type: 'checkin_face',
|
||||
userid: row.userid,
|
||||
nickname: row.nickname,
|
||||
faceimg: row.checkin_face
|
||||
};
|
||||
|
||||
this.checkinFaceEditShow = true;
|
||||
break;
|
||||
|
||||
case 'department':
|
||||
let departments = []
|
||||
@ -1022,6 +1071,14 @@ export default {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (data.type == 'checkin_macs') {
|
||||
this.checkinMacEditLoading++;
|
||||
} else if (data.type == 'checkin_face') {
|
||||
this.checkinFaceEditLoading++;
|
||||
data = {
|
||||
type: data.type,
|
||||
userid: data.userid,
|
||||
nickname: data.nickname,
|
||||
checkin_face: data.faceimg[0] ? data.faceimg[0].url : ''
|
||||
}
|
||||
} else if (data.type == 'department') {
|
||||
this.departmentEditLoading++;
|
||||
} else if (data.type == 'setdisable') {
|
||||
@ -1038,6 +1095,8 @@ export default {
|
||||
resolve()
|
||||
if (data.type == 'checkin_macs') {
|
||||
this.checkinMacEditShow = false;
|
||||
} else if (data.type == 'checkin_face') {
|
||||
this.checkinFaceEditShow = false;
|
||||
} else if (data.type == 'department') {
|
||||
this.departmentEditShow = false;
|
||||
} else if (data.type == 'setdisable') {
|
||||
@ -1052,6 +1111,8 @@ export default {
|
||||
}).finally(_ => {
|
||||
if (data.type == 'checkin_macs') {
|
||||
this.checkinMacEditLoading--;
|
||||
} else if (data.type == 'checkin_face') {
|
||||
this.checkinFaceEditLoading--;
|
||||
} else if (data.type == 'department') {
|
||||
this.departmentEditLoading--;
|
||||
} else if (data.type == 'setdisable') {
|
||||
|
||||
@ -41,6 +41,19 @@
|
||||
</Row>
|
||||
</div>
|
||||
<Button type="default" icon="md-add" @click="addDatum">{{$L('添加设备')}}</Button>
|
||||
<div class="setting-checkin-row">
|
||||
<Row class="setting-template">
|
||||
<Col span="12">{{$L('人脸图片')}}</Col>
|
||||
<Col span="12"></Col>
|
||||
</Row>
|
||||
|
||||
<Row class="setting-template">
|
||||
<Col span="12">
|
||||
<ImgUpload v-model="faceimgs" :num="1" :width="512" :height="512" :whcut="1"></ImgUpload>
|
||||
<span class="form-tip">{{$L('建议尺寸:200x200')}}</span>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
</Form>
|
||||
<div class="setting-footer">
|
||||
<Button :loading="loadIng > 0" type="primary" @click="submitForm">{{$L('提交')}}</Button>
|
||||
@ -59,9 +72,10 @@
|
||||
|
||||
<script>
|
||||
import CheckinCalendar from "../components/CheckinCalendar";
|
||||
import ImgUpload from "../../../components/ImgUpload";
|
||||
export default {
|
||||
name: "ManageCheckin",
|
||||
components: {CheckinCalendar},
|
||||
components: {CheckinCalendar, ImgUpload},
|
||||
|
||||
data() {
|
||||
return {
|
||||
@ -69,6 +83,8 @@ export default {
|
||||
|
||||
formData: [],
|
||||
|
||||
faceimgs: [],
|
||||
|
||||
nullDatum: {
|
||||
'mac': '',
|
||||
'remark': '',
|
||||
@ -104,7 +120,8 @@ export default {
|
||||
this.$store.dispatch("call", {
|
||||
url: 'users/checkin/get',
|
||||
}).then(({data}) => {
|
||||
this.formData = data.length > 0 ? data : [$A.cloneJSON(this.nullDatum)];
|
||||
this.formData = data.list.length > 0 ? data.list : [$A.cloneJSON(this.nullDatum)];
|
||||
this.faceimgs = data.faceimg
|
||||
this.formData_bak = $A.cloneJSON(this.formData);
|
||||
}).catch(({msg}) => {
|
||||
$A.modalError(msg);
|
||||
@ -124,14 +141,16 @@ export default {
|
||||
remark: item.remark.trim()
|
||||
}
|
||||
});
|
||||
const faceimg = this.faceimgs ? this.faceimgs[0].url : ''
|
||||
//
|
||||
this.loadIng++;
|
||||
this.$store.dispatch("call", {
|
||||
url: 'users/checkin/save',
|
||||
data: {list},
|
||||
data: {list, faceimg},
|
||||
method: 'post',
|
||||
}).then(({data}) => {
|
||||
this.formData = data;
|
||||
this.formData = data.list;
|
||||
this.faceimgs = data.faceimg
|
||||
this.formData_bak = $A.cloneJSON(this.formData);
|
||||
$A.messageSuccess('修改成功');
|
||||
}).catch(({msg}) => {
|
||||
|
||||
@ -56,6 +56,11 @@
|
||||
<Radio label="close">{{ $L('禁止') }}</Radio>
|
||||
</RadioGroup>
|
||||
<div class="form-tip">{{$L('允许成员自己修改MAC地址')}}</div>
|
||||
<RadioGroup v-model="formData.faceupload">
|
||||
<Radio label="open">{{ $L('允许') }}</Radio>
|
||||
<Radio label="close">{{ $L('禁止') }}</Radio>
|
||||
</RadioGroup>
|
||||
<div class="form-tip">{{$L('允许成员自己上传人脸图片')}}</div>
|
||||
</FormItem>
|
||||
<FormItem :label="$L('签到方式')" prop="modes">
|
||||
<CheckboxGroup v-model="formData.modes">
|
||||
@ -119,6 +124,7 @@ export default {
|
||||
|
||||
formData: {
|
||||
open: '',
|
||||
faceupload: '',
|
||||
edit: '',
|
||||
cmd: '',
|
||||
modes: [],
|
||||
|
||||
@ -1 +1 @@
|
||||
Subproject commit 06d8bf1f8d2e4932d98d33d13df9b2bf73ccb778
|
||||
Subproject commit c359b7d11daca3d6adba287089c4705f2ec5d652
|
||||
Loading…
x
Reference in New Issue
Block a user