diff --git a/app/controller/api/v1/User.php b/app/controller/api/v1/User.php index 7f4b261..e1599f1 100644 --- a/app/controller/api/v1/User.php +++ b/app/controller/api/v1/User.php @@ -3,9 +3,13 @@ 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\ClockLog; +use app\model\PayLog; +use app\model\Worksite; use app\repository\AccountRepository; use app\service\File; use app\service\Jwt; @@ -17,6 +21,7 @@ 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; @@ -333,12 +338,12 @@ class User extends Base { $list = []; - $week = ['日', '一', '二', '三', '四', '五', '六']; + $week = ['日', '一', '二', '三', '四', '五', '六']; - $info = [ - 'today' => date('Y年m月d日'), - 'week' => '星期'.$week[date('w')], - 'now' => date('H:i:s'), + $info = [ + 'today' => date('Y年m月d日'), + 'week' => '星期'.$week[date('w')], + 'now' => date('H:i:s'), 'is_sign' => (int) !empty($list), ]; @@ -390,10 +395,10 @@ class User extends Base try { $openid = input('openid/s'); if (empty($openid)) { - return $this->json(0,'success', ['status' => 0]); + return $this->json(0, 'success', ['status' => 0]); } - $isActive = (int)Account::where('openid', $openid)->value('is_active'); + $isActive = (int) Account::where('openid', $openid)->value('is_active'); return $this->json(0, 'success', ['status' => $isActive]); } catch (Exception $e) { @@ -405,13 +410,13 @@ class User extends Base public function monthSignLog(): Json { $accountId = $this->request->user['user_id'] ?? 0; - $date = input('date/d', date('Y-m')); - $ym = str_replace('-', '', $date); + $date = input('date/d', date('Y-m')); + $ym = str_replace('-', '', $date); $list = []; if ($accountId > 0) { - $where = []; + $where = []; $where[] = ['cl.day', 'like', $ym.'%']; $where[] = ['cl.account_id', '=', $accountId]; @@ -442,4 +447,119 @@ class User extends Base return $this->json(0, 'success', ['list' => $list]); } + + /** + * 打卡 + * 普通用户打卡不需要任何参数 + * 员工和负责人打卡 参数相同 + */ + public function sign(): Json + { + try { + $accountId = $this->request->user['user_id'] ?? 0; + + if (!$customer = Account::findById($accountId)) { + return $this->json(6001, '请先登录'); + } + + // 工人打卡 + if ($customer['role'] == Account::ROLE_NORMAL && $this->normalSign($accountId)) { + return $this->json(); + } + + $input = input('post.'); + + $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, '今日已打过此卡'); + } + + $data = [ + 'account_id' => $accountId, + 'type' => $input['type'], + 'worksite_id' => $input['worksite_id'], + 'created_at' => $now, + 'create_time' => $time, + 'day' => $day, + '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(); + } + + /** + * 普通用户打卡 + * + * @param int $accountId + * @return bool + * @throws \app\exception\ApiException + * @throws \think\db\exception\DbException + */ + private function normalSign(int $accountId): bool + { + $time = time(); + // $time = $time - 86401 * 3; + $now = date('Y-m-d H:i:s', $time); + $day = date('Ymd', $time); + + if (ClockLog::checkRate($accountId)) { + throw new ApiException('打卡频率过快!'); + } + + ClockLog::create([ + 'account_id' => $accountId, + 'type' => ClockLog::TYPE_NORMAL, + 'created_at' => $now, + 'create_time' => $time, + 'day' => $day, + ]); + + return true; + } } diff --git a/app/controller/api/v1/Worker.php b/app/controller/api/v1/Worker.php index ad575f0..34cc94d 100644 --- a/app/controller/api/v1/Worker.php +++ b/app/controller/api/v1/Worker.php @@ -188,10 +188,20 @@ class Worker extends Base public function sign(): Json { try { + $accountId = $this->request->user['user_id'] ?? 0; + + if (!$customer = Account::findById($accountId)) { + return $this->json(6001, '请先登录'); + } + + if ($customer['role'] == Account::ROLE_NORMAL) { + return $this->normalSign(); + } + $input = input('post.'); $rules = [ - 'type|打卡类型' => 'require|in:in,out', + 'type|打卡类型' => 'require|in:morning_on,morning_off,afternoon_on,afternoon_off', 'lat|维度' => 'require', 'lng|经度' => 'require', 'worksite_id|工地' => 'require|number', @@ -203,12 +213,6 @@ class Worker extends Base return $validate; } - $accountId = $this->request->user['user_id'] ?? 0; - - if (!$customer = Account::findById($accountId)) { - return $this->json(6001, '请先登录'); - } - if ($customer['role'] != Account::ROLE_WORKER) { return $this->json(4003, '完成审核后方可打卡'); } @@ -250,6 +254,47 @@ class Worker extends Base return $this->json(); } + private function normalSign($customer) + { + try { + + 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::where('account_id', $accountId)->where('type', $input['type'])->where('create_time', '>', time() - 60)->count()) { + return $this->json(4001, '打卡频率过快!'); + } + + ClockLog::create([ + 'account_id' => $accountId, + 'type' => $input['type'], + 'worksite_id' => $input['worksite_id'], + 'created_at' => $now, + 'create_time' => $time, + 'day' => $day, + 'indexs' => $accountId.'-'.$input['worksite_id'].'-'.$day, + ]); + + // 创建当日工资初始记录 + PayLog::createWhenNotExists($accountId, $input['worksite_id'], $day); + } catch (Exception $e) { + Log::error('工人打卡失败'.$e->getMessage()); + return $this->json(5000, '打卡失败!'); + } + + return $this->json(); + } + // 我的打卡 public function clockList(): Json { diff --git a/app/exception/ApiException.php b/app/exception/ApiException.php new file mode 100644 index 0000000..70323e5 --- /dev/null +++ b/app/exception/ApiException.php @@ -0,0 +1,10 @@ + '上班', - self::TYPE_OUT => '下班', + self::TYPE_NORMAL => '普通打卡', + self::TYPE_MORNING_ON => '上午上班', + self::TYPE_MORNING_OFF => '上午下班', + self::TYPE_AFTERNOON_ON => '下午上班', + self::TYPE_AFTERNOON_OFF => '下午下班', ]; } @@ -38,4 +46,44 @@ class ClockLog extends Base self::STATUS_NO => '审核拒绝', ]; } + + /** + * 检查打卡频率 + * 目前针对普通用户 + * + * @param int $accountId 用户ID + * @param string $type 打卡类型 + * @param int $worksiteId 工地ID 默认0 + * @param int $seconds 时间限制,秒 即多少秒内只能打一次 默认60 + * @return bool true=单位时间有打卡 false=单位时间未打卡 + * @throws \think\db\exception\DbException + */ + public static function checkRate(int $accountId, string $type = self::TYPE_NORMAL, int $worksiteId = 0, int $seconds = 60): bool + { + return self::where('account_id', $accountId) + ->where('type', $type) + ->where('worksite_id', $worksiteId) + ->where('create_time', '>', time() - $seconds) + ->count() > 0; + } + + /** + * 检查当天是否已打卡 + * 目前针对工人和负责人 + * + * @param int $accountId 用户ID + * @param string $type 打卡类型 + * @param int $worksiteId 工地ID 默认0 + * @return bool + * @throws \think\db\exception\DbException + */ + public static function hasSign(int $accountId, string $type, int $worksiteId): bool + { + return self::where('account_id', $accountId) + ->where('type', $type) + ->where('worksite_id', $worksiteId) + ->where('create_time', '>=', strtotime(date('Y-m-d'))) + ->where('create_time', '<=', strtotime(date('Y-m-d 23:59:59'))) + ->count() > 0; + } } diff --git a/app/model/Worksite.php b/app/model/Worksite.php index 9ec2447..800405c 100644 --- a/app/model/Worksite.php +++ b/app/model/Worksite.php @@ -18,8 +18,8 @@ class Worksite extends Base public static function statusText(): array { return [ - self::STATUS_NO => '禁用', - self::STATUS_YES => '正常', + self::STATUS_NO => '禁用', + self::STATUS_YES => '正常', ]; } // 获取指定距离内由近及远的N条记录 @@ -86,4 +86,67 @@ class Worksite extends Base { return self::order('sort', 'desc')->order('id', 'desc')->column('name', 'id'); } + + // 验证是否在打卡时间 + public static function checkSignTime(int $worksiteId, string $type): bool + { + if (!$item = self::find($worksiteId)) { + return false; + } + $morningBegin = $item['old_morning_begin']; + $morningEnd = $item['old_morning_end']; + $afternoonBegin = $item['old_afternoon_begin']; + $afternoonEnd = $item['old_afternoon_end']; + + $now = time(); + + if ($now > $item['start_at']) { + $morningBegin = $item['morning_begin']; + $morningEnd = $item['morning_end']; + $afternoonBegin = $item['afternoon_begin']; + $afternoonEnd = $item['afternoon_end']; + } + + // 时间戳 + $morningBeginTs = strtotime($morningBegin); + $morningEndTs = strtotime($morningEnd); + $afternoonBeginTs = strtotime($afternoonBegin); + $afternoonEndTs = strtotime($afternoonEnd); + + $result = false; + switch ($type) { + // 普通用户打卡 不限制时间 + case ClockLog::TYPE_NORMAL: + $result = true; + break; + // 上午上班时间 打卡时间在上班前的10分钟 + case ClockLog::TYPE_MORNING_ON: + if (($morningBeginTs - 10 * 60) <= $now && $now <= $morningBeginTs) { + $result = true; + } + break; + // 上午下班时间 打卡时间在下班时间后 下次上班时间以前 + case ClockLog::TYPE_MORNING_OFF: + if (($morningEndTs) <= $now && $now < $afternoonBeginTs) { + $result = true; + } + break; + + // 下午上班时间 打卡时间在上班前的10分钟 + case ClockLog::TYPE_AFTERNOON_ON: + if (($afternoonBeginTs - 10 * 60) <= $now && $now <= $afternoonBeginTs) { + $result = true; + } + break; + + // 下午下班时间 打卡时间在下班时间后 当天23:59:59以前 + case ClockLog::TYPE_AFTERNOON_OFF: + if (($afternoonEndTs) <= $now && $now < strtotime('23:59:59')) { + $result = true; + } + break; + } + + return $result; + } }