<?php /** * Created by PhpStorm. * User: liyifei2012it * Date: 16/9/18 * Time: 14:38 */ namespace Common\Service; use Com\PythonExcel; use Common\Common\Cache; use Common\Common\Constant; use Common\Model\UserModel; use VcySDK\Service; use VcySDK\Member; use Common\Common\User; use Common\Common\Department; use Common\Model\AttrModel; class UserService extends AbstractService { /** * 查询人员状态:全部 */ const USER_STATUS_ALL = 0; /** * 查询人员状态:已关注 */ const USER_STATUS_FOLLOW = 1; /** * 查询人员状态:已禁用 */ const USER_STATUS_DISABLE = 2; /** * 查询人员状态:未关注 */ const USER_STATUS_UNFOLLOW = 4; /** * 操作人员:启用 */ const ENABLE_USER = 1; /** * 操作人员:禁用 */ const DISABLE_USER = 2; /** * 操作人员:复职 */ const REHAB_USER = 3; /** * 操作人员:离职 */ const QUIT_USER = 4; /** * (架构)人员状态:启用 */ const STATUS_ENABLE = 1; /** * (架构)人员状态:禁用 */ const STATUS_DISABLE = 0; /** * 是否递归查询部门下所有子部门人员信息:是 */ const DEPT_CHILDREN_FLAG = 1; /** * 一年的天数 */ const YEAR_OF_DAY = 365; /** * 一天的毫秒数 */ const DAY_OF_MILLISECOND = 86400000; /** * 用户加入方式: 管理员添加 */ const ADMIN_ADD_JOIN = 1; /** * 用户加入方式: 邀请加入 */ const USER_INVITE_JOIN = 2; /** * 邀请加入:不是通知所有人未加入的人员 */ const NOTICE_NOT_TO_ALL_UNFOLLOW = 1; /** * 邀请加入:通知所有人未加入的人员 */ const NOTICE_TO_ALL_UNFOLLOW = 2; // 构造方法 public function __construct() { parent::__construct(); $this->_attr = new AttrModel(); } /** * 保存人员(新增、修改人员信息) * @author liyifei * @param string $uid 人员UID * @param array $data 校验过的表单数据 * @return bool|mixed */ public function saveUser($uid, $data) { // 调用架构接口,创建、修改用户信息 $memServ = new Member(Service::instance()); if ($uid) { $data['memUid'] = $uid; unset($data['memJoinType']); unset($data['memJoinInviter']); $result = $memServ->update($data); } else { $result = $memServ->add($data); } // 清理缓存 clear_sys_cache([ 'Common.Department', 'Common.Job', 'Common.Role', 'Common.User' ]); return $result; } /** * 单个或批量删除用户 * @author liyifei * @param array $uids 人员uid * @return array|bool */ public function delete($uids) { $userServ = new User(); if (count($uids) > 1) { $userServ->batDelete($uids); } else { $userServ->delete($uids[0]); } // 清空人员缓存 $userServ->clearUserCache($uids); // 清空部门缓存(增加和删除人员,涉及到部门成员数量) $deptCom = new Department(); $deptCom->clearDepCache(); } /** * 用户详情(手机端展示信息) * @author liyifei * @param string $uid 人员ID * @return mixed */ public function getUserInfoByUid($uid, $attrs) { // 用户信息(从缓存架构用户信息表中的数据获取用户信息,参数设为true越过缓存) $commUser = new User(); $userInfo = $commUser->getByUid($uid); // 匹配人员属性和人员详情数据 $list = $this->getAttrListWithUserInfo($attrs, $userInfo, ['memFace',]); // 邀请人名称为空时, 说明是历史数据 if (empty($userInfo['memJoinInviter'])) { $joinDesc = "管理员添加"; } else { $joinDesc = $userInfo['memJoinType'] == self::ADMIN_ADD_JOIN ? "管理员({$userInfo['memJoinInviter']})添加" : "{$userInfo['memJoinInviter']}邀请加入"; } // add by zhuxun37, 加入方式特殊处理 $list[] = array( 'field_name' => 'memJoin', 'attr_name' => '加入方式', 'attr_value' => $joinDesc, 'option' => '', 'type' => AttrModel::AREA_CONTACT, 'order' => 0, 'is_allow_user_modify' => AttrModel::ATTR_NOT_ALLOWED_USER_MODIFY ); return $list; } /** * 用户详情(管理后台) * @author liyifei * @param string $uid 人员ID * @param array $userInfo 人员详情 * @return mixed */ public function userInfo($uid, $userInfo = []) { // 用户信息 if (empty($userInfo)) { $newUser = new User(); $userInfo = $newUser->getByUid($uid, true); } // Edit by liyifei at 2016-11-10 18:41:36 in V1.1.0 展示所有属性,包括空值的属性 $attrs = $this->getAttrList(true, array(), true); $list = []; foreach ($attrs as $attr) { // 以下属性,不在list中出现 if (in_array($attr['field_name'], ['memUsername', 'dpName'])) { continue; } // 根据属性类型不同,将属性值转为与前端约定好的格式 $attrValue = $this->formatValueByType($attr['type'], $userInfo[$attr['field_name']]); $list[] = [ 'field_name' => $attr['field_name'], 'attr_name' => $attr['attr_name'], 'attr_value' => $attrValue, 'option' => $attr['option'], 'type' => $attr['type'], 'order' => $attr['order'], ]; } // 邀请人名称为空时, 说明是历史数据 if (empty($userInfo['memJoinInviter'])) { $joinDesc = "管理员添加"; } else { $joinDesc = $userInfo['memJoinType'] == self::ADMIN_ADD_JOIN ? "管理员({$userInfo['memJoinInviter']})添加" : "{$userInfo['memJoinInviter']}邀请加入"; } // add by zhuxun37, 加入方式特殊处理 $list[] = array( 'field_name' => 'memJoin', 'attr_name' => '加入方式', 'attr_value' => $joinDesc, 'option' => '', 'type' => AttrModel::AREA_CONTACT, 'order' => 0 ); // 关注时间 if ($userInfo['memSubscribeTime']) { $list[] = array( 'field_name' => 'memSubscribeTime', 'attr_name' => '关注时间', 'attr_value' => $userInfo['memSubscribeTime'], 'option' => '', 'type' => AttrModel::AREA_PERSONAL, 'order' => 0 ); } // 被删除时间 if ($userInfo['memDeleted']) { $list[] = array( 'field_name' => 'memDeleted', 'attr_name' => '删除时间', 'attr_value' => $userInfo['memDeleted'], 'option' => '', 'type' => AttrModel::AREA_PERSONAL, 'order' => 0 ); } // Edit by liyifei in V1.3.0 人员详情展示"司龄"属性(当前日期-入职日期) /**$age = [ 'attr_name' => '司龄', 'attr_value' => '', ]; $endTime = $userInfo['leaveDate'] ? $userInfo['leaveDate'] : MILLI_TIME; if ($userInfo['memJoinTime'] !== null) { $myMilliTime = $endTime - $userInfo['memJoinTime']; if ($myMilliTime < self::YEAR_OF_DAY * self::DAY_OF_MILLISECOND) { $myDay = ceil($myMilliTime / (self::DAY_OF_MILLISECOND)) > 0 ? ceil($myMilliTime / (self::DAY_OF_MILLISECOND)) : 0; if ($myDay < 365) { // 司龄小于365天按天显示 $age['attr_value'] = $myDay . '天'; } else { // 司龄等于365天时,显示为年 $age['attr_value'] = "1年"; } } else { // 司龄大于等于365天按年显示,小数点后保留一位,四舍五入 $year = floor($myMilliTime / (self::YEAR_OF_DAY * self::DAY_OF_MILLISECOND)); $age['attr_value'] = $year + round(($myMilliTime - $year * (self::YEAR_OF_DAY * self::DAY_OF_MILLISECOND)) / (self::YEAR_OF_DAY * self::DAY_OF_MILLISECOND), 1) . '年'; } } array_push($list, $age);*/ return $list; } /** * 根据条件查询读取人员列表(可查看通讯录范围内的人员列表) * @author liyifei * @param array $conds 搜索条件 * + string $uid 人员UID * + string $dpId 部门ID * + string $keyword 关键字 * + string $index 姓名首字母 * @param int $page 当前页码 * @param int $limit 每页数据总数 * @return mixed */ public function getListByConds($conds = [], $page = 1, $limit = 30) { // 验证参数 if (empty($conds['uid'])) { E('_ERR_PARAMS_IS_NULL'); } // 实例化 $newUser = new User(); $rightServ = new DeptRightService(); // 读取登录人员所在部门的通讯录可见范围中部门列表 $range = $rightServ->getRangByUid($conds['uid']); $dpList = array_column($range, 'dpId'); // 初始化搜索条件 $condition = []; // 根据部门ID搜索 if (!empty($conds['dpId'])) { // 无权查看,且自己所在的部门ID集合 $notDpIds = []; $myDpIds = $newUser->listDepartment(['memUid' => $conds['uid']]); foreach ($myDpIds as $myDpId) { if (!in_array($myDpId, $dpList)) { $notDpIds[] = $myDpId; } } // 查看自己所在的部门,且无权查看本部门通讯录时,仅返回该用户信息 if (in_array($conds['dpId'], $notDpIds)) { // 返回本人信息详情 $userInfo = $newUser->getByUid($conds['uid']); return $userInfo; } // 搜索的部门,是否有权限查看 if (!in_array($conds['dpId'], $dpList)) { E('_ERR_CANOT_FIND_USER'); } // 搜索条件 $condition['dpId'] = $conds['dpId']; $condition['departmentChildrenFlag'] = !isset($conds['departmentChildrenFlag']) ? self::DEPT_CHILDREN_FLAG : (int)$conds['departmentChildrenFlag']; } else { // 当可查看的部门被删除时,仅可查看本人信息 if (empty($dpList)) { // 返回本人信息详情 $userInfo = $newUser->getByUid($conds['uid']); return $userInfo; } else { $condition['dpIdList'] = $dpList; } } // 搜索关键字 if (!empty($conds['keyword'])) { $condition['memUsername'] = $conds['keyword']; } // 搜索首字母 if (!empty($conds['index'])) { $condition['index'] = $conds['index']; } // 排序规则 $orderList = [ 'memIndex' => 'ASC', ]; // 获取缓存用户列表信息 return $newUser->listByConds($condition, $page, $limit, $orderList); } /** * 读取 Excel 文件, 为导入做准备 * @param $result * @param $request * @param $user * @return bool */ public function readExcel(&$result, $request, $user) { //导入默认起始行数 $default_line = Constant::STAFF_IMPORT_START; // 分页和每页数 $page = (int)$request['page']; $limit = (int)$request['limit']; $start = $default_line + $limit * ($page - 1); $result = array( 'rowsReaded' => 0, 'page' => $page, 'limit' => $limit ); // 附件Url $attachmentUrl = $request['attachmentUrl']; if (empty($attachmentUrl)) { E('1002:Excel文件地址为空'); return false; } // 导入唯一标识 $importFlag = $request['importFlag']; if (empty($importFlag)) { $importFlag = NOW_TIME . random(8); } $result['importFlag'] = $importFlag; // 获取附件 $localFile = get_sitedir() . $importFlag . '.xls'; if (!file_exists($localFile)) { file_put_contents($localFile, file_get_contents($attachmentUrl)); } // 如果文件大小为 0 if (0 >= filesize($localFile)) { E('1004:Excel文件错误或文件内容为空'); return false; } $importDataService = new ImportDataService(); // 读取指定行 $data = PythonExcel::instance()->read($localFile, $start, $start + $limit); if (empty($data)) { return true; } $result['rowsReaded'] = count($data); // 默认表头没有失败原因一列 $existMessage = false; // 最后一列索引 $i = 0; // 如果是第一页, 则需要额外保存表头 if (1 == $page) { // 校验表头是否有多出的'失败原因'一列,有的话需要过滤掉 $exceHead = $data[0]; $i = count($exceHead) - 1; if ($exceHead[$i] == '失败原因') { $existMessage = true; array_splice($exceHead, $i, 1); } $importDataService->insert(array( 'ea_id' => $user['eaId'], 'import_flag' => $importFlag, 'data_type' => 'title', 'data' => rjson_encode($exceHead) )); unset($data[0]); unset($data[1]); } // 其他数据入库 $insertData = array(); foreach ($data as $_data) { if ($existMessage) { array_splice($_data, $i, 1); } $insertData[] = array( 'ea_id' => $user['eaId'], 'import_flag' => $importFlag, 'data_type' => 'user', 'data' => rjson_encode($_data) ); } $importDataService->insert_all($insertData); return true; } // 导出模板 public function exportTpl($attr) { $data = []; $titles = $this->formatExportTitle($attr); // 初始化标题简介数据 $merge_titles = Constant::STAFF_LIST_TITLES_DESC; $merge_titles[] = array_values($titles); // 初始化默认值数据 $firstData = Constant::STAFF_LIST_DATA; //删除导出特定状态 unset($firstData[count($firstData)-1]); unset($firstData[count($firstData)-1]); $merge_titles[] = $firstData; // 合并标题到数据中 $data = array_merge($merge_titles, $data); $title_xls[] = '填写说明'; $filename = NOW_TIME . random(8) . '.xls'; PythonExcel::instance()->write(get_sitedir() . $filename, $title_xls, $data); header("Pragma: public"); header("Expires: 0"); header("Cache-Control:must-revalidate, post-check=0, pre-check=0"); header("Content-Type:application/force-download"); header("Content-Type:application/vnd.ms-execl"); header("Content-Type:application/octet-stream"); header("Content-Type:application/download"); header('Content-Disposition:attachment;filename=userTpl.xls'); header("Content-Transfer-Encoding:binary"); echo file_get_contents(get_sitedir() . $filename); exit; } /** * 获取用户所在部门列表 * @param $user * @return array */ public function listUserDpIds($user) { $dpIds = array(); foreach ($user['dpName'] as $_user) { $dpIds[] = $_user['dpId']; } return $dpIds; } /** * 获取用户的顶级部门 * @param $user * @param $currentDpId * @return bool|mixed */ public function getUserTopDpId($user, $currentDpId = null) { $myDpIds = $this->listUserDpIds($user); if (1 == $myDpIds) { return $myDpIds[0]; } // 获取所有上级部门 $parentDpIds = array(); foreach ($myDpIds as $_dpId) { Department::instance()->list_parent_cdids($_dpId, $parentDpIds); } // 获取子部门 $childIds = Department::instance()->list_childrens_by_cdid($myDpIds, true); $ids = array_merge($parentDpIds, $childIds); $departments = Department::instance()->listAll(); // 整理上/下级关系 $p2c = array(); foreach ($ids as $_id) { $current = $departments[$_id]; $parentId = empty($current['dpParentid']) ? 0 : $current['dpParentid']; if (empty($p2c[$parentId])) { $p2c[$parentId] = array(); } $p2c[$parentId][] = $_id; } if (null == $currentDpId || !in_array($currentDpId, $childIds)) { $currentDpId = $this->getTopDpId($p2c, $myDpIds, 0); } return array($currentDpId, $p2c, $childIds, $ids); } /** * 获取用户顶级部门ID * @param $p2c * @param $myDpIds * @param $currentIds * @return mixed */ public function getTopDpId($p2c, $myDpIds, $currentIds) { $currentIds = (array)$currentIds; $results = array(); foreach ($currentIds as $_id) { if (empty($p2c[$_id])) { continue; } foreach ($p2c[$_id] as $_cid) { if (in_array($_cid, $myDpIds)) { return $_cid; } $results[] = $_cid; } } if (empty($results)) { return 0; } return $this->getTopDpId($p2c, $myDpIds, $results); } /** * 重新整理标题 * @param $attr * @return mixed */ protected function formatExportTitle($attr) { $title = []; foreach ($attr as $item) { // 没有开启该字段 if ($item['is_open_cp'] != AttrModel::ATTR_IS_OPEN_TRUE) { continue; } // 是否必填 if ($item['is_required_cp'] == AttrModel::ATTR_IS_REQUIRED_TRUE) { $title[] = [ 'value' => $item['attr_name'], 'pattern' => [ 'pattern' => 'solid', 'fore_colour' => 'red' ] ]; } else { $title[] = $item['attr_name']; } } return $title; } /** * 匹配人员属性和人员详情数据 * @param $attrs * @param $userInfo * @param $skipField * @return array */ private function getAttrListWithUserInfo($attrs, $userInfo, $skipField) { $list = []; foreach ($attrs as $attr) { // 跳过不显示的字段 if (in_array($attr['field_name'], $skipField)) { continue; } // 根据属性类型不同,将属性值转为与前端约定好的格式 $attrValue = $this->formatValueByType($attr['type'], $userInfo[$attr['field_name']]); // 属性类型为单选、下拉框单选时,将属性值由单选value转为单选name显示 if (in_array($attr['type'], [AttrModel::ATTR_TYPE_RADIO, AttrModel::ATTR_TYPE_DROPBOX])) { foreach ($attr['option'] as &$item) { if ($item['value'] == $attrValue) { // 前端需要选中标志 $item['checked'] = true; $attrValue = $item['name']; } else { $item['checked'] = false; } } } // 保留有用的字段 $temp = array_intersect_key_reserved($attr, [ 'field_name', 'attr_name', 'option', 'postion', 'type', 'order', 'is_allow_user_modify', 'is_required_cp', 'is_show'], true); $temp['is_require'] = $temp['is_required_cp']; unset($temp['is_required_cp']); $list[] = array_merge($temp, ['attr_value' => $attrValue]); } return $list; } }