building-sign/app/controller/api/v1/User.php

820 lines
27 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<?php
namespace app\controller\api\v1;
use app\controller\api\Base;
use app\exception\ApiException;
use app\exception\RepositoryException;
use app\model\Account;
use app\model\AccountRecord;
use app\model\AccountStar;
use app\model\ClockLog;
use app\model\PayLog;
use app\model\Worksite;
use app\repository\AccountRepository;
use app\service\File;
use app\service\Jwt;
use app\service\wx\WechatApplets;
use app\validate\User as UserValidate;
use EasyWeChat\Kernel\Exceptions\InvalidConfigException;
use Exception;
use think\Collection;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\facade\Config;
use think\facade\Log;
use think\response\Json;
class User extends Base
{
protected $noNeedLogin = [
'login',
'tempLogin',
'signInfo',
'signToday',
'checkActive'
];
/**
* 登录 成功返回token及用户信息
*
* @return Json
* @throws InvalidConfigException
*/
public function login(): Json
{
$params = input();
Log::error('进入参数');
Log::error($params);
Log::error('参数结束');
$validate = new UserValidate();
if (!$validate->scene('wx_applets')->check($params)) {
return $this->json(4000, $validate->getError());
}
$minApp = WechatApplets::getInstance();
$jsCode = $params['code'];
$wxUser = $minApp->auth->session($jsCode);
if (isset($wxUser['errcode']) && $wxUser['errcode'] != 0) {
return $this->json(4001, $wxUser['errcode'].';'.$wxUser['errmsg'] ?? '登录失败');
}
// $wxUser success has [session_key, openid, unionid]
// 有效期2小时
$wxUser['expire_time'] = time() + 7200;
$wxUser['session_key'] = $wxUser['session_key'] ?? '';
$openID = $wxUser['openid'];
$unionid = $wxUser['unionid'] ?? '';
if (empty($openID) && empty($unionid)) {
return $this->json(4002, '登录失败');
}
$isActive = $params['is_active'] ?? 0;
$isActive = (is_numeric($isActive) && $isActive > 0) ? AccountRepository::BOOL_TRUE : AccountRepository::BOOL_FALSE;
$phoneActive = $params['phone_active'] ?? 0;
$phoneActive = (is_numeric($phoneActive) && $phoneActive > 0) ? AccountRepository::BOOL_TRUE : AccountRepository::BOOL_FALSE;
try {
$repo = AccountRepository::getInstance();
$account = $repo->findByOpenID($openID);
if (!$account && !empty($unionid)) {
$account = $repo->findByUnionId($unionid);
}
$now = date('Y-m-d H:i:s');
if (!$account) {
// 自动注册
$account = $repo->create([
'unionid' => $unionid ?? '',
'openid' => $openID,
'last_login' => $now,
'login_ip' => $this->request->ip(),
'created_at' => $now,
'nickname' => $params['nickname'] ?? '',
'headimgurl' => $params['headimgurl'] ?? '',
'mobile' => $params['mobile'] ?? '',
'status' => AccountRepository::STATUS_NORMAL,
'is_active' => $isActive,
'phone_active' => $phoneActive,
'session_key' => $wxUser['session_key'] ?? '',
]);
} else {
$updateData = [
'last_login' => date('Y-m-d H:i:s'),
'login_ip' => $this->request->ip(),
'session_key' => $wxUser['session_key'] ?? '',
];
// 更新资料
$modifyStringList = ['headimgurl', 'nickname', 'mobile'];
foreach ($modifyStringList as $modifyKey) {
if (isset($account[$modifyKey]) && empty($account[$modifyKey])) {
$updateData[$modifyKey] = $params[$modifyKey] ?? '';
}
}
if (empty($account['gender'])) {
$updateData['gender'] = $params['gender'] ?? 0;
}
if (isset($account['is_active']) && $account['is_active'] == AccountRepository::BOOL_FALSE) {
$updateData['is_active'] = $isActive;
}
if (isset($account['phone_active']) && $account['phone_active'] == AccountRepository::BOOL_FALSE) {
$updateData['phone_active'] = $phoneActive;
}
$repo->update($updateData, ['id' => $account['id']]);
$account = $repo->findById($account['id']);
}
} catch (RepositoryException | Exception $e) {
return $this->json(4003, '登录失败!'.$e->getMessage());
}
$account = $account->toArray();
$jwtData = [
'user_id' => $account['id'],
'open_id' => $openID,
'session_key' => $wxUser['session_key'],
'expire_time' => $wxUser['expire_time'],
];
$account['headimgurl'] = File::convertCompleteFileUrl($account['headimgurl']);
$fields = [
'coding', 'real_name', 'nickname', 'headimgurl', 'gender', 'mobile',
'status', 'is_active', 'phone_active'
];
$accountData = arrayKeysFilter($account, $fields);
$data = [
'account_id' => $account['id'],
'token' => Jwt::generate($jwtData),
'expire' => $wxUser['expire_time'],
'openid' => $openID,
];
$data = array_merge($data, $accountData);
return $this->json(0, 'success', $data);
}
/**
* 获取用户信息
*
* @return Json
*/
public function info(): Json
{
try {
$accountId = $this->request->user['user_id'] ?? 0;
$user = Account::getUser($accountId);
return $this->json(0, 'success', $user);
} catch (Exception $e) {
return $this->json(4000, '没有相关的用户记录'.$e->getMessage());
}
}
/**
* 修改用户信息
*/
public function updateInfo(): Json
{
try {
$params = input('post.');
$rules = [
'field|修改项' => 'require|in:nickname',
'value|修改内容' => 'require',
];
$accountId = $this->request->user['user_id'] ?? 0;
$validate = $this->validateByApi($params, $rules, ['field.in' => '参数错误']);
if ($validate !== true) {
return $validate;
}
if (!$customer = Account::findById($accountId)) {
return $this->json(4004, '用户不存在');
}
$customer->save([
$params['field'] => $params['value']
]);
} catch (Exception $e) {
Log::error('修改用户信息失败'.$e->getMessage());
return $this->json(5000, '修改失败!'.$e->getMessage());
}
return $this->json();
}
/**
* 临时登录 通过openid登录 仅用于接口测试阶段
*
* @return Json
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
public function tempLogin(): Json
{
$params = $this->request->param();
if (!isset($params['openid'])) {
return $this->json(4001, '参数错误');
}
if (!$user = AccountRepository::getInstance()->findByOpenID($params['openid'])) {
return $this->json(4004, '账号不存在');
}
$data = [
'token' => Jwt::generate(['user_id' => $user['id'], 'nickname' => $user['nickname']]),
'expire' => Jwt::expire()
];
return $this->json(0, 'success', $data);
}
/**
* 用户操作记录 分享、咨询
*
* @return Json
* @throws Exception
*/
public function record(): Json
{
if (!$this->request->isPost()) {
return $this->json(4000, '无效请求');
}
$accountId = $this->request->user['user_id'] ?? 0;
$params = input('post.');
$rules = [
'type|操作类型' => 'require|in:content,other,spu,activity',
'action|操作' => 'require',
];
if (isset($params['type']) && $params['type'] == AccountRecord::TYPE_CONTENT) {
$rules['id|ID'] = 'require';
}
$validate = $this->validateByApi($params, $rules, ['type.in' => '类型错误', 'id.require' => '此类型 ID必传']);
if ($validate !== true) {
return $validate;
}
try {
$relationId = $params['id'] ?? 0;
$relationId = is_numeric($relationId) ? $relationId : 0;
AccountRecord::record($accountId, $params['type'], $params['action'], $relationId);
} catch (Exception $e) {
AccountRepository::log('记录用户操作失败', $e);
return $this->json(5001, '操作失败');
}
return $this->json();
}
/**
* 绑定手机
*
* @return bool|Json
* @throws Exception
*/
public function bindPhone()
{
if (!$this->request->isPost()) {
return $this->json(4000, '无效请求');
}
$accountId = $this->request->user['user_id'] ?? 0;
$params = input('post.');
$rules = [
'encryptedData|加密数据' => 'require',
'iv|IV' => 'require',
];
$validate = $this->validateByApi($params, $rules);
if ($validate !== true) {
return $validate;
}
try {
if (!$account = Account::findById($accountId)) {
return $this->json(4000, '用户不存在');
}
// 解密手机相关数据 若存在手机则覆盖
$minApp = WechatApplets::getInstance();
$sessionKey = $this->request->user['session_key'] ?? '';
$decryptData = $minApp->encryptor->decryptData($sessionKey, $params['iv'], $params['encryptedData']);
$phone = $decryptData['phoneNumber'] ?? ''; // 通过iv和加密数据 解密出手机号
if (Account::where('id', '<>', $accountId)->where('mobile', $phone)->count() > 0) {
return $this->json(4000, '该手机已被绑定,若有绑定错误,请联系客服');
}
if ($phone) {
$account->save(['mobile' => $phone, 'phone_active' => Account::COMMON_ON]);
}
return $this->json(0, 'success', ['phone' => $phone]);
} catch (Exception $e) {
AccountRepository::log('手机绑定失败', $e);
return $this->json(5001, '手机绑定失败');
}
}
// 打卡页面信息
public function signInfo(): Json
{
$list = [];
$week = ['日', '一', '二', '三', '四', '五', '六'];
$info = [
'today' => date('Y年m月d日'),
'week' => '星期'.$week[date('w')],
'now' => date('H:i:s'),
'is_sign' => (int) !empty($list),
];
return $this->json(0, 'success', ['info' => $info]);
}
/**
* 今日打卡记录
*
* @return \think\response\Json
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function signToday(): Json
{
$accountId = $this->request->user['user_id'] ?? 0;
$day = input('day/s', '');
$day = $day ?: date('Y-m-d');
$worksiteId = input('worksite_id/d', 0);
$worksiteId = $worksiteId ?: 0;
$list = [];
$buttonColor = 'gray';
if ($accountId > 0) {
if (!$account = Account::findById($accountId)) {
return $this->json(6001, '请先登录');
}
$where = [];
// $where[] = ['cl.created_at', '>', date($day.' 00:00:00')];
// $where[] = ['cl.created_at', '<', date($day.' 23:59:59')];
$dayInt = str_replace('-', '', $day);
$where[] = ['cl.day', '=', $dayInt];
$where[] = ['cl.account_id', '=', $accountId];
if ($worksiteId) {
$where[] = ['cl.worksite_id', '=', $worksiteId];
}
$where[] = ['cl.role', '=', $account['role']];
$limit = $account['role'] == Account::ROLE_NORMAL ? 4 : 0;//普通用户只展示最新4条 其他不限制
$list = \app\model\ClockLog::alias('cl')
->leftJoin('worksite w', 'w.id = cl.worksite_id')
->field('cl.*,w.name as worksite_name')
->where($where)
->limit($limit)
->order('cl.id', 'desc')
->select();
/**
* 打卡按钮颜色 gray=灰色 orange=橙色 绿色=green
* 普通人 只有灰色 和 绿色 未打卡灰色 打卡>0为绿色
* 工人和负责人
* 灰色: 未打卡、(中途休息期间如12~14 未判断此条件)
* 橙色:上班卡数量>下班卡数量且>0
* 绿色1. 下班卡数量>= 上班卡数量 且下班卡数量>=1 或 2.下午下班卡打了后
*/
if ($account['role'] == Account::ROLE_NORMAL) {
$buttonColor = $list->count() > 0 ? 'green' : 'gray';
} else {
$array = $list->whereIn('status', [0, 1])->column('type');
$onCount = 0;//上班卡数量
$offCount = 0;//下班卡数量
foreach ($array as $sign) {
if (strpos($sign, '_on')) {
$onCount++;
}
if (strpos($sign, '_off')) {
$offCount++;
}
}
if (($offCount >= $onCount && $offCount >= 1) || $onCount > 0) {
$buttonColor = 'orange';
}
if (in_array('afternoon_off', $array) || ($offCount >= $onCount && $offCount >= 1)) {
$buttonColor = 'green';
}
}
$list->each(function ($item) {
$item->type_text = ClockLog::typeText()[$item->type];
switch ($item->status) {
case 0:
$item->status_text = '待确认';
break;
case 1:
$item->status_text = '已确认';
break;
case -1:
$item->status_text = '不通过';
break;
}
$item->time = date('H:i', $item->create_time);
});
$list = $list->toArray();
}
return $this->json(0, 'success', ['buttonColor' => $buttonColor, 'list' => $list]);
}
public function checkActive(): Json
{
try {
$openid = input('openid/s');
if (empty($openid)) {
return $this->json(0, 'success', ['status' => 0]);
}
$isActive = (int) Account::where('openid', $openid)->value('is_active');
return $this->json(0, 'success', ['status' => $isActive]);
} catch (Exception $e) {
return $this->json(4000, '检查账号是否微信授权是不'.$e->getMessage());
}
}
/**
* 月度打卡记录
* {
* 'ok':[2,5,6,8,12],
* 'add':[4,7,11,22],
* 'no':[19,28]
* }
*
* @return \think\response\Json
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function monthSignLog(): Json
{
$accountId = $this->request->user['user_id'] ?? 0;
$date = input('date/s', '');
$worksiteId = input('worksite_id/d', 0);
$worksiteId = $worksiteId ?: 0;
$date = $date ?: date('Y-m');
$ym = str_replace('-', '', $date);
$res = [
'ok' => [], //正常打卡:普通用户当天有打卡=正常 工人和负责人打满4次=正常
'add' => [],// 补打卡:仅限工人和负责人 当天存在补卡
'no' => [],// 非正常打卡:仅限工人和负责人 4>打卡次数>0
];
if ($accountId > 0) {
if (!$account = Account::findById($accountId)) {
return $this->json(6001, '请先登录');
}
$where = [];
$where[] = ['cl.day', 'like', $ym.'%'];
$where[] = ['cl.account_id', '=', $accountId];
$where[] = ['cl.role', '=', $account['role']];
$where[] = ['cl.worksite_id', '=', $worksiteId];
$list = \app\model\ClockLog::alias('cl')
->where($where)
->select();
$signNum = [];//每日打卡次数
foreach ($list as $item) {
$day = (int) substr($item['day'], -2);
if (!isset($signNum[$day])) {
$signNum[$day] = 0;
}
$signNum[$day]++;
if ($item['is_replenish'] == ClockLog::COMMON_ON) {
$res['add'][] = $day;
}
}
foreach ($signNum as $day => $count) {
if ($account['role'] == Account::ROLE_NORMAL) {
if ($count >= 1) {
$res['ok'][] = $day;
}
} else {
if ($count == 4) {
$res['ok'][] = $day;
} else {
$res['no'][] = $day;
}
}
}
$res['ok'] = array_unique($res['ok']);
$res['add'] = array_unique($res['add']);
$res['no'] = array_unique($res['no']);
}
return $this->json(0, 'success', $res);
}
/**
* 打卡
* 普通用户打卡不需要任何参数
* 员工和负责人打卡 参数相同
*/
public function sign(): Json
{
try {
$accountId = $this->request->user['user_id'] ?? 0;
if (!$customer = Account::findById($accountId)) {
return $this->json(6001, '请先登录');
}
$input = input('post.');
// 普通人打卡
if ($customer['role'] == Account::ROLE_NORMAL && $this->normalSign($accountId, $input['type'])) {
return $this->json();
}
$rules = [
'type|打卡类型' => 'require|in:morning_on,morning_off,afternoon_on,afternoon_off',
'lat|维度' => 'require',
'lng|经度' => 'require',
'worksite_id|工地' => 'require|number',
];
$validate = $this->validateByApi($input, $rules, ['type.in' => '打卡类型错误']);
if ($validate !== true) {
return $validate;
}
Config::load('extra/base', 'base');
$baseConfig = config('base');
$signArea = $baseConfig['sign_area'] ?? 200;
$worksite = Worksite::getNearest($input['lng'], $input['lat'], $signArea);
if (empty($worksite) || $worksite['id'] != $input['worksite_id']) {
return $this->json(4004, '不在打卡范围!');
}
$time = time();
// $time = $time - 86401 * 3;
$now = date('Y-m-d H:i:s', $time);
$day = date('Ymd', $time);
if (ClockLog::hasSign($accountId, $input['type'], $input['worksite_id'])) {
return $this->json(4001, '今日已打过此卡');
}
// 是否在打卡时间
if (!Worksite::checkSignTime($input['worksite_id'], $input['type'])) {
// return $this->json(4002, '不在打卡时间段!');
}
$data = [
'account_id' => $accountId,
'type' => $input['type'],
'worksite_id' => $input['worksite_id'],
'created_at' => $now,
'create_time' => $time,
'day' => $day,
'role' => $customer['role'],
'indexs' => $accountId.'-'.$input['worksite_id'].'-'.$day,
];
// 工人
if ($customer['role'] == Account::ROLE_WORKER) {
$data['need_statistic'] = Account::COMMON_ON;
}
// 负责人
if ($customer['role'] == Account::ROLE_MANAGER) {
$data['status'] = Account::COMMON_ON;
$data['need_statistic'] = Account::COMMON_OFF;
}
ClockLog::create($data);
// 创建当日工资初始记录
PayLog::createWhenNotExists($accountId, $input['worksite_id'], $day);
} catch (ApiException $e) {
return $this->json(4000, $e->getMessage());
} catch (Exception $e) {
Log::error('打卡失败'.$e->getMessage());
return $this->json(5000, '打卡失败!');
}
return $this->json();
}
/**
* 提交补卡
*/
public function replenish(): Json
{
try {
$input = input('post.');
$rules = [
'day|补卡日期' => 'require|date',
'type|补卡类型' => 'require|in:morning_on,morning_off,afternoon_on,afternoon_off',
'worksite_id|工地' => 'require|number',
];
$validate = $this->validateByApi($input, $rules, ['worksite_id.number' => '工地必传', 'type.in' => '补卡类型错误']);
if ($validate !== true) {
return $validate;
}
$accountId = $this->request->user['user_id'] ?? 0;
if (!$customer = Account::findById($accountId)) {
return $this->json(6001, '请先登录');
}
if (!in_array($customer['role'], [1, 2])) {
return $this->json(4003, '当前身份不能补卡');
}
if (strtotime($input['day']) < (time() - 86400 * 3) || strtotime($input['day']) > time()) {
return $this->json(4003, '只能补三天以内');
}
$time = time();
$now = date('Y-m-d H:i:s', $time);
$day = date('Ymd', strtotime($input['day']));
$where = [
'account_id' => $accountId,
'type' => $input['type'],
'worksite_id' => $input['worksite_id'],
'day' => $day,
'role' => $customer['role'],
'indexs' => $accountId.'-'.$input['worksite_id'].'-'.$day,
];
if (ClockLog::where($where)->whereIn('status', [0, 1])->count() > 0) {
return $this->json(4001, '记录存在,无需补卡!');
}
$data = [
'account_id' => $accountId,
'type' => $input['type'],
'worksite_id' => $input['worksite_id'],
'created_at' => $now,
'create_time' => $time,
'day' => $day,
'role' => $customer['role'],
'is_replenish' => ClockLog::COMMON_ON,
'need_statistic' => $customer['role'] == 1 ? ClockLog::COMMON_ON : ClockLog::COMMON_OFF,
'indexs' => $accountId.'-'.$input['worksite_id'].'-'.$day,
];
ClockLog::create($data);
// 创建当日工资初始记录
PayLog::createWhenNotExists($accountId, $input['worksite_id'], $day);
} catch (Exception $e) {
Log::error('补卡提交失败'.$e->getMessage());
return $this->json(5000, '补卡申请失败!');
}
return $this->json();
}
/**
* 评级列表
*
* @return \think\response\Json
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function starList(): Json
{
$accountId = input('account_id/d', 0);
$accountId = $accountId ?: 0;
$worksiteId = input('worksite_id/d', 0);
$worksiteId = $worksiteId ?: 0;
if (!$account = Account::findById($accountId)) {
return $this->json(4004, '用户不存在');
}
$where = [];
$where[] = ['account_id', '=', $accountId];
$worksiteId = $worksiteId ?: $account['worksite_id'];
$where[] = ['worksite_id', '=', $worksiteId];
$where[] = ['type', '=', AccountStar::TYPE_WORKER];
$list = \app\model\AccountStar::where($where)
->order('ym', 'desc')
->select();
$yearArr = $list->column('year');
$yearArr = array_unique($yearArr);
$starList = [];
foreach ($yearArr as $year) {
$starList[$year.'年'] = [];
}
foreach ($list as $val) {
$starList[$val['year'].'年'][] = [
'year' => $val['year'],
'month' => $val['month'],
'star' => $val['star'],
];
}
$firstStar = $list->first()['star'] ?? 0;
$info = [
'real_name' => $account['real_name'] ?? '',
'worksite_name' => Worksite::where('id', $account['worksite_id'])->value('name'),
'star' => $firstStar,
];
return $this->json(0, 'success', ['info' => $info, 'list' => $starList]);
}
/**
* 用户详情
*
* @return Json
*/
public function detail(): Json
{
try {
$accountId = input('id/d');
$user = Account::getUser($accountId);
return $this->json(0, 'success', $user);
} catch (Exception $e) {
return $this->json(4000, '获取用户详情失败'.$e->getMessage());
}
}
/**
* 普通用户打卡
*
* @param int $accountId
* @param string $type
* @return bool
* @throws \app\exception\ApiException
* @throws \think\db\exception\DbException
*/
private function normalSign(int $accountId, string $type): bool
{
$time = time();
// $time = $time - 86401 * 3;
$now = date('Y-m-d H:i:s', $time);
$day = date('Ymd', $time);
if (ClockLog::checkRate($accountId, $type)) {
throw new ApiException('打卡频率过快!');
}
ClockLog::create([
'account_id' => $accountId,
'type' => $type,
'created_at' => $now,
'create_time' => $time,
'day' => $day,
'status' => ClockLog::COMMON_ON,
]);
return true;
}
}