获取标签成员列表，注意权限问题.

@param int  $tagId        标签ID
@param bool $fetchDetail  是否获取成员详情

@link http://qydev.weixin.qq.com/wiki/index.php?title=%E7%AE%A1%E7%90%86%E6%A0%87%E7%AD%BE

@return array 获取到的数据 ['userlist', 'partylist']
182294
public function getTagMembers($tagId, $fetchDetail = false)
    {
        $apiUrl = 'https://qyapi.weixin.qq.com/cgi-bin/tag/get';

        $result = $this->_curl($apiUrl, [
            'access_token' => $this->getAccessToken(),
            'tagid' => $tagId,
        ]);

        if ($result and isset($result['userlist']) and isset($result['partylist'])) {
            if ($fetchDetail) {
                $result['userlist'] = array_map(function ($item) {
                    return $this->getUserInfo($item['userid']);
                }, $result['userlist']);
            }

            return ['userlist' => $result['userlist'], 'partylist' => $result['partylist']];
        }

        return null;
    }
658828
static public function fetchByParentID( $parentID, $offset = 0, $limit = null, $mainTranslation = false )
    {
        if ( $offset === 0 && $limit === null )
        {
            $limits = null;
        }
        else
        {
            $limits = array( 'offset' => $offset, 'limit' => $limit );
        }
        return self::fetchList(
            array( 'parent_id' => $parentID, 'main_tag_id' => 0 ),
            $limits,
            null,
            $mainTranslation
        );
    }
941090
public function getTableInfo($tableName = '', $fetch = '')
    {
        if (!$tableName) {
            $tableName = $this->getTable();
        }
        if (is_array($tableName)) {
            $tableName = key($tableName) ?: current($tableName);
        }

        if (strpos($tableName, ',')) {
            // 多表不获取字段信息
            return false;
        } else {
            $tableName = $this->parseSqlTable($tableName);
        }

        // 修正子查询作为表名的问题
        if (strpos($tableName, ')')) {
            return [];
        }

        list($guid) = explode(' ', $tableName);
        $db         = $this->getConfig('database');
        if (!isset(self::$info[$db . '.' . $guid])) {
            $info = $this->connection->getFields($guid);
            $fields = array_keys($info);
            $bind   = $type   = [];
            foreach ($info as $key => $val) {
                // 记录字段类型
                $type[$key] = $val['type'];
                $bind[$key] = $this->getFieldBindType($val['type']);
                if (!empty($val['primary'])) {
                    $pk[] = $key;
                }
            }
            if (isset($pk)) {
                // 设置主键
                $pk = count($pk) > 1 ? $pk : $pk[0];
            } else {
                $pk = null;
            }
            self::$info[$db . '.' . $guid] = ['fields' => $fields, 'type' => $type, 'bind' => $bind, 'pk' => $pk];
        }
        return $fetch ? self::$info[$db . '.' . $guid][$fetch] : self::$info[$db . '.' . $guid];
    }
847815
static function fetchListForUser( $userID, $offset = false, $limit = false )
    {
        $objectList = eZPersistentObject::fetchObjectList( eZContentBrowseBookmark::definition(),
                                                            null,
                                                            array( 'user_id' => $userID ),
                                                            array( 'id' => 'desc' ),
                                                            array( 'offset' => $offset, 'length' => $limit ),
                                                            true );
        return $objectList;
    }
1197361
public function addGroup(array $groupDetail)
    {
        $result = $this->wechat->httpRaw(self::WECHAT_GROUP_ADD_PREFIX, [
            'group_detail' => $groupDetail
        ], [
            'access_token' => $this->wechat->getAccessToken()
        ]);
        return isset($result['errmsg']) && $result['errmsg'] == 'success' ? $result['group_id'] : false;
    }
24116
static public function subTreeByTagID( $params = array(), $tagID = 0 )
    {
        if ( !is_numeric( $tagID ) || (int) $tagID < 0 )
            return false;

        $tag = self::fetch( (int) $tagID );
        if ( (int) $tagID > 0 && !$tag instanceof self )
            return false;

        if ( $tag instanceof self && $tag->attribute( 'main_tag_id' ) != 0 )
            return false;

        if ( !is_array( $params ) )
            $params = array();

        $offset          = ( isset( $params['Offset'] ) && (int) $params['Offset'] > 0 )   ? (int) $params['Offset']           : 0;
        $limit           = ( isset( $params['Limit'] ) && (int) $params['Limit'] > 0 )     ? (int) $params['Limit']            : 0;
        $sortBy          = ( isset( $params['SortBy'] ) && is_array( $params['SortBy'] ) ) ? $params['SortBy']                 : array();
        $depth           = ( isset( $params['Depth'] ) )                                   ? $params['Depth']                  : false;
        $depthOperator   = ( isset( $params['DepthOperator'] ) )                           ? $params['DepthOperator']          : false;
        $includeSynonyms = ( isset( $params['IncludeSynonyms'] ) )                         ? (bool) $params['IncludeSynonyms'] : false;

        $fetchParams = array();

        if ( (int) $tagID > 0 )
        {
            $fetchParams['path_string'] = array( 'like', '%/' . (string) ( (int) $tagID ) . '/%' );
            $fetchParams['id'] = array( '!=', (int) $tagID );
        }

        if ( !$includeSynonyms )
            $fetchParams['main_tag_id'] = 0;

        if ( $depth !== false && (int) $depth > 0 )
        {
            $tagDepth = 0;
            if ( $tag instanceof self )
                $tagDepth = (int) $tag->attribute( 'depth' );

            $depth = (int) $depth + $tagDepth;

            $sqlDepthOperator = '<=';
            if ( $depthOperator == 'lt' )
                $sqlDepthOperator = '<';
            else if ( $depthOperator == 'gt' )
                $sqlDepthOperator = '>';
            else if ( $depthOperator == 'le' )
                $sqlDepthOperator = '<=';
            else if ( $depthOperator == 'ge' )
                $sqlDepthOperator = '>=';
            else if ( $depthOperator == 'eq' )
                $sqlDepthOperator = '=';

            $fetchParams['depth'] = array( $sqlDepthOperator, $depth );
        }

        $limits = null;
        if ( $limit > 0 )
        {
            $limits = array(
                'offset' => $offset,
                'limit' => $limit,
            );
        }

        $sorts = array();
        if ( !empty( $sortBy ) )
        {
            $columnArray = array( 'id', 'parent_id', 'main_tag_id', 'keyword', 'depth', 'path_string', 'modified' );
            $orderArray = array( 'asc', 'desc' );

            if ( count( $sortBy ) == 2 && !is_array( $sortBy[0] ) && !is_array( $sortBy[1] ) )
            {
                $sortBy = array( $sortBy );
            }

            foreach ( $sortBy as $sortCond )
            {
                if ( is_array( $sortCond ) && count( $sortCond ) == 2 )
                {
                    if ( in_array( strtolower( trim( $sortCond[0] ) ), $columnArray ) )
                    {
                        $sortCond[0] = trim( strtolower( $sortCond[0] ) );

                        if( in_array( strtolower( trim( $sortCond[1] ) ), $orderArray ) )
                            $sortCond[1] = trim( strtolower( $sortCond[1] ) );
                        else
                            $sortCond[1] = 'asc';

                        if ( !array_key_exists( $sortCond[0], $sorts ) )
                            $sorts[$sortCond[0]] = $sortCond[1];
                    }
                }
            }
        }

        if ( empty( $sorts ) )
            $sorts = null;

        $fetchResults = self::fetchList( $fetchParams, $limits, $sorts );

        if ( is_array( $fetchResults ) && !empty( $fetchResults ) )
            return $fetchResults;

        return false;
    }
1354173
public function getUserListByTag($tagid, $next_openid = '')
    {
        $url = 'https://api.weixin.qq.com/cgi-bin/user/tag/get?access_token=ACCESS_TOKEN';
        $this->registerApi($url, __FUNCTION__, func_get_args());
        return $this->httpPostForJson($url, ['tagid' => $tagid, 'next_openid' => $next_openid]);
    }
1324021
public function updateTagsName($tagid, $name)
    {
        $queryStr = [
            'tag' => [
                'id'   => $tagid,
                'name' => $name,
            ],
        ];

        $res = $this->_post('update', $queryStr);

        return $res;
    }
337701
static function fetchUserList( $nodeIDList, $contentObject )
    {
        if ( count( $nodeIDList ) == 0 )
        {
            $retValue = array();
            return $retValue;
        }

        $db = eZDB::instance();
        $concatString = $db->concatString(  array( 'user_tree.path_string', "'%'" ) );

        // Select affected users
        $sqlINString = $db->generateSQLINStatement( $nodeIDList, 'subtree_rule.node_id', false, false, 'int' );
        $sql = "SELECT DISTINCT subtree_rule.user_id,
                                user_node.node_id
                FROM ezsubtree_notification_rule subtree_rule,
                     ezcontentobject_tree user_node,
                     ezuser_setting
                WHERE $sqlINString AND
                      user_node.contentobject_id = subtree_rule.user_id AND
                      ezuser_setting.user_id = subtree_rule.user_id AND
                      user_node.is_invisible = 0 AND
                      ezuser_setting.is_enabled = 1";
        $userPart = $db->arrayQuery( $sql );

        // Remove duplicates
        $userNodeIDList = array();
        foreach ( $userPart as $row )
            $userNodeIDList[] = $row['node_id'];
        $userNodeIDList = array_unique( $userNodeIDList );

        if ( count( $userNodeIDList ) == 0 )
        {
            $retValue = array();
            return $retValue;
        }

        // Select affected nodes
        $sqlINString = $db->generateSQLINStatement( $userNodeIDList, 'user_node.node_id', false, false, 'int' );
        $sql = "SELECT DISTINCT user_node.node_id,
                                user_node.path_string,
                                user_tree.contentobject_id
                FROM ezcontentobject_tree user_node,
                     ezcontentobject_tree user_tree
                WHERE $sqlINString AND
                      user_node.path_string LIKE $concatString";
        $nodePart = $db->arrayQuery( $sql );

        // Remove duplicates
        $objectIDList = array();
        foreach ( $nodePart as $row )
            if ( $row['contentobject_id'] != '0' )
                $objectIDList[] = $row['contentobject_id'];
        $objectIDList = array_unique( $objectIDList );

        if ( count( $objectIDList ) == 0 )
        {
            $retValue = array();
            return $retValue;
        }

        // Select affected roles and policies
        $sqlINString = $db->generateSQLINStatement( $objectIDList, 'user_role.contentobject_id', false, false, 'int' );
        $sql = "SELECT DISTINCT user_role.contentobject_id,
                                policy.id AS policy_id,
                                user_role.limit_identifier AS limitation,
                                user_role.limit_value AS value
                FROM ezuser_role user_role,
                     ezpolicy policy
                WHERE $sqlINString AND
                      ( user_role.role_id=policy.role_id AND
                        ( policy.module_name='*' OR
                          ( policy.module_name='content' AND
                            ( policy.function_name='*' OR
                              policy.function_name='read'
                            )
                          )
                        )
                      )";
        $rolePart = $db->arrayQuery( $sql );

        // Build resultArray. Make sure there are no duplicates.
        $resultArray = array();
        foreach ( $userPart as $up )
        {
            foreach ( $nodePart as $np )
            {
                if ( $up['node_id'] == $np['node_id'] )
                {
                    foreach ( $rolePart as $rp )
                    {
                        if ( $np['contentobject_id'] == $rp['contentobject_id'] )
                        {
                            $key = $rp['policy_id'] . $up['user_id'] . $rp['limitation'] . $rp['value'];
                            $resultArray[$key] = array( 'policy_id' => $rp['policy_id'],
                                                        'user_id' => $up['user_id'],
                                                        'limitation' => $rp['limitation'],
                                                        'value' => $rp['value'] );
                        }
                    }
                }
            }
        }

        $policyIDArray = array();
        $limitedPolicyIDArray = array();
        $userIDArray = array();
        foreach( $resultArray as $result )
        {
            $userIDArray[(string)$result['user_id']] = (int)$result['user_id'];
        }

        foreach( $resultArray as $result )
        {
            if ( $result['limitation'] == '' )
            {
                $policyIDArray[(string)$result['policy_id']][] =& $userIDArray[(string)$result['user_id']];
            }
            else
            {
                $limitedPolicyIDArray[] = array( 'user_id' => $userIDArray[(string)$result['user_id']],
                                                 'limitation' => $result['limitation'],
                                                 'value' => $result['value'],
                                                 'policyID' => $result['policy_id'] );
            }
        }

        $acceptedUserArray = array();
        foreach( array_keys( $policyIDArray ) as $policyID )
        {
            foreach( array_keys( $policyIDArray[$policyID] ) as $key )
            {
                if ( $policyIDArray[$policyID][$key] === false )
                {
                    unset( $policyIDArray[$policyID][$key] );
                }
            }

            if ( count( $policyIDArray[$policyID] ) == 0 )
            {
                continue;
            }

            $userArray = eZSubtreeNotificationRule::checkObjectAccess( $contentObject, $policyID, $policyIDArray[$policyID] );
            $acceptedUserArray = array_merge( $acceptedUserArray, $userArray );

            foreach ( $userArray as $userID )
            {
                $userIDArray[(string)$userID] = false;
            }
        }

        foreach( $limitedPolicyIDArray as $policyEntry )
        {
            if ( $policyEntry['user_id'] === false )
            {
                continue;
            }

            $userArray = eZSubtreeNotificationRule::checkObjectAccess( $contentObject,
                                                                       $policyEntry['policyID'],
                                                                       array( $policyEntry['user_id'] ),
                                                                       array( $policyEntry['limitation'] => $policyEntry['value'] ) );

            $acceptedUserArray = array_merge( $acceptedUserArray, $userArray );
            foreach ( $userArray as $userID )
            {
                $userIDArray[(string)$userID] = false;
            }
        }
        $acceptedUserArray = array_unique( $acceptedUserArray );

        foreach( array_keys( $acceptedUserArray ) as $key )
        {
            if ( !is_int( $acceptedUserArray[$key] ) or $acceptedUserArray[$key] == 0 )
            {
                unset( $acceptedUserArray[$key] );
            }
        }

        if ( count( $acceptedUserArray ) == 0 )
        {
            $retValue = array();
            return $retValue;
        }

        $nodeIDWhereString = $db->generateSQLINStatement( $nodeIDList, 'rule.node_id', false, false, 'int' );
        $userIDWhereString = $db->generateSQLINStatement( $acceptedUserArray, 'rule.user_id', false, false, 'int' );
        $rules = $db->arrayQuery( "SELECT rule.user_id, rule.use_digest, ezuser.email as address
                                      FROM ezsubtree_notification_rule rule, ezuser
                                      WHERE rule.user_id=ezuser.contentobject_id AND
                                            $nodeIDWhereString AND
                                            $userIDWhereString" );
        return $rules;
    }
926695
public function getBasicTagData($tagId)
    {
        $query = $this->handler->createSelectQuery();
        $query
            ->select('*')
            ->from($this->handler->quoteTable('eztags'))
            ->where(
                $query->expr->eq(
                    $this->handler->quoteColumn('id'),
                    $query->bindValue($tagId, null, PDO::PARAM_INT)
                )
            );

        $statement = $query->prepare();
        $statement->execute();

        if ($row = $statement->fetch(PDO::FETCH_ASSOC)) {
            return $row;
        }

        throw new NotFoundException('tag', $tagId);
    }
954808
public function setUsersTag($tagid, array $openid_list)
    {
        if (!is_array($openid_list)) {
            $this->setError('参数必须为一个数组');

            return false;
        }

        $queryStr = [
            'openid_list' => $openid_list,
            'tagid'       => $tagid,
        ];

        $res = $this->_post('members/batchtagging', $queryStr);

        return $res;
    }
381543
public function getTableInfo($tableName = '', $fetch = '')
    {
        if (!$tableName) {
            $tableName = $this->getTable();
        }
        if (is_array($tableName)) {
            $tableName = key($tableName) ?: current($tableName);
        }

        if (strpos($tableName, ',')) {
            // 多表不获取字段信息
            return false;
        } else {
            $tableName = $this->parseSqlTable($tableName);
        }

        $guid = md5($tableName);
        if (!isset(self::$info[$guid])) {
            $result = $this->table($tableName)->find();
            if (!$result) {
                $result = [];
            }
            $fields = array_keys($result);
            $type   = [];
            foreach ($result as $key => $val) {
                // 记录字段类型
                $type[$key] = getType($val);
                if ('_id' == $key) {
                    $pk = $key;
                }
            }
            if (!isset($pk)) {
                // 设置主键
                $pk = null;
            }
            $result            = ['fields' => $fields, 'type' => $type, 'pk' => $pk];
            self::$info[$guid] = $result;
        }
        return $fetch ? self::$info[$guid][$fetch] : self::$info[$guid];
    }
603944
public static function getList($excludeId = 0)
    {
        $list = [];
        $query = self::find()->orderBy('position DESC, title');

        if (!empty($excludeId)) {
            $query->where('`id` != :excludeId', ['excludeId' => $excludeId]);
        }

        $models = $query->all();

        foreach ($models as $model) {
            $list[$model->id] = $model->title;
        }

        return $list;
    }
97556
public function deleteTag($tagId)
    {
        $query = $this->handler->createSelectQuery();
        $query
            ->select('id')
            ->from($this->handler->quoteTable('eztags'))
            ->where(
                $query->expr->lOr(
                    $query->expr->like(
                        $this->handler->quoteColumn('path_string'),
                        $query->bindValue('%/' . (int) $tagId . '/%', null, PDO::PARAM_STR)
                    ),
                    $query->expr->eq(
                        $this->handler->quoteColumn('main_tag_id'),
                        $query->bindValue($tagId, null, PDO::PARAM_INT)
                    )
                )
            );

        $statement = $query->prepare();
        $statement->execute();

        $tagIds = [];
        while ($row = $statement->fetch(PDO::FETCH_ASSOC)) {
            $tagIds[] = (int) $row['id'];
        }

        if (empty($tagIds)) {
            return;
        }

        $query = $this->handler->createDeleteQuery();
        $query
            ->deleteFrom($this->handler->quoteTable('eztags_attribute_link'))
            ->where(
                $query->expr->in(
                    $this->handler->quoteColumn('keyword_id'),
                    $tagIds
                )
            );

        $query->prepare()->execute();

        $query = $this->handler->createDeleteQuery();
        $query
            ->deleteFrom($this->handler->quoteTable('eztags_keyword'))
            ->where(
                $query->expr->in(
                    $this->handler->quoteColumn('keyword_id'),
                    $tagIds
                )
            );

        $query->prepare()->execute();

        $query = $this->handler->createDeleteQuery();
        $query
            ->deleteFrom($this->handler->quoteTable('eztags'))
            ->where(
                $query->expr->in(
                    $this->handler->quoteColumn('id'),
                    $tagIds
                )
            );

        $query->prepare()->execute();
    }
729116
public GetUsersResponse getUsersByTagId(Integer tagId, String nextOpenid) {
        BeanUtil.requireNonNull(tagId, "tagId is null");
        GetUsersResponse response;
        LOG.debug("开始获取标签下粉丝.....");
        String url = BASE_API_URL + "cgi-bin/user/tag/get?access_token=#";
        Map<String, Object> param = new HashMap<String, Object>();
        param.put("tagid", tagId);
        param.put("next_openid", null == nextOpenid ? "" : nextOpenid);
        BaseResponse r = executePost(url, JSONUtil.toJson(param));
        String resultJson = isSuccess(r.getErrcode()) ? r.getErrmsg() : r.toJsonString();
        response = JSONUtil.toBean(resultJson, GetUsersResponse.class);
        return response;
    }
182890
public function getTagUserList($tagId)
    {
        $result = $this->httpGet(self::WECHAT_TAG_USER_LIST_GET_PREFIX, [
            'access_token' => $this->getAccessToken(),
            'tagid' => $tagId
        ]);
        return isset($result['errmsg']) && $result['errmsg'] == 'ok' ? $result : $this->dump($result);
    }
954413
static public function fetchByTagID( $tagID )
    {
        $objects = parent::fetchObjectList( self::definition(), null, array( 'keyword_id' => $tagID ) );

        if ( is_array( $objects ) )
            return $objects;

        return array();
    }