1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/ntbl-ntbl-handle

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
В этом репозитории не указан файл с открытой лицензией (LICENSE). При использовании обратитесь к конкретному описанию проекта и его зависимостям в коде.
Клонировать/Скачать
README.md 25 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
gitlife-traslator Отправлено 27.11.2024 22:39 0fdddc6

Handle.js

Handle — это промежуточный слой, основанный на koa и sequelize, который позволяет вам сосредоточиться только на логике интерфейса.

API Documentation:

  • Installation;
  • Usage;
  • Загрузчик;
  • Методы экземпляра;
  • Изменение метода по умолчанию;
  • Инструменты;
  • Параметры интерфейса;
  • Нечёткие запросы;
  • Разбиение на страницы;
  • Сортировка;
  • Связывание;
  • Обработка данных;
  • Условные переходы;
  • Scope;
  • Настраиваемые параметры;
  • Process;
  • Транзакции;
  • Перехватчики;
  • Исходные данные;
  • В одном предложении.

Установка:

npm i @ntbl/handle --save

Использование:

import Handle from '@ntbl/handle'
// Импорт модели sequelize
import { Article } from '../models/db'

// Передача статьи в Handle и создание экземпляра
const article = new Handle(Article)

// Создание промежуточного программного обеспечения для запроса всех данных текущей модели
const find = article.findAll()

// Привязка к маршруту
router.get('/article/find', find)

Загрузчик:

Загрузчик объединяет импорт моделей sequelize и создание экземпляров Handle.

// Предыдущий способ написания

const Article sequelize.import(__dirname + './article')
const article = new Handle(Article)

// Использование загрузчика после
// Обратите внимание, что вам всё ещё нужно передать sequelize
// Внутренний метод использует sequelize.import() для загрузки файла модели
const article = Handle.load(sequelize, __dirname + './article')

Кроме того, поддерживается пакетная загрузка, что делает процесс более эффективным.

// Прохождение через все файлы .js в указанном каталоге (по умолчанию игнорируются index.js и файлы, начинающиеся с _) и их загрузка
// Возвращает объект, где ключ — это имя файла, а значение — экземпляр Handle
// Также возвращает объект с именем _models, содержащий экземпляры моделей sequelize
const db = Handle.loadAll(sequelize, __dirname, {
    // Помимо объекта параметров конструктора Handle,
    // также поддерживает правила сопоставления (поддерживает запись glob)
    rule: '/**/!(index|_)*.js',  // По умолчанию
})

Методы экземпляра:

Handle имеет большинство методов моделей sequelize, которые можно разделить на две категории.

Первая категория называется «быстрыми методами». После вызова они сразу же генерируют асинхронную функцию (интерфейсную функцию), которую можно напрямую привязать к маршруту, без необходимости писать строку кода.

  • GET: findOne, findAll, findById, findOrCreate, findAndCountAll, findCreateFind, count, max, min, sum;
  • POST: create, bulkCreate, update, destroy, increment, decrement.
router.get('/article/find', article.findAll())

Вторая категория называется «методы процесса», после вызова они возвращают только данные, которые необходимо обработать с помощью метода process экземпляра.

rawFindOne, rawFindAll, rawFindById, rawFindOrCreate, rawFindAndCountAll, rawFindCreateFind, rawCount, rawMax, rawMin, rawSum, rawCreate, rawBulkCreate, rawUpdate, rawDestroy, rawIncrement, rawDecrement.

const find = artcile.process(async function (d) {
    // Проверка данных
    // Опущено
    
    // Запрос пользователя
    const userData = await user
        .where('username', 'password')
        .rawFindOne()
    // Запрос статей текущего пользователя
    const result = await this
        .where(['id', userData.id])
        .rawFindAll()
    // Возврат рекомендованных статей
    return result.filter(e => e.type === 'recommend')
})

// Наконец, привязка к маршруту
router.get('/article/find', find)

Изменение метода по умолчанию:

Вы можете заметить, что быстрые методы имеют фиксированный метод запроса, мы можем изменить его следующим образом.

// Это повлияет на всё приложение
// Не передавайте прокси-серверу объект, он перезапишет значения по умолчанию
Handle.defaults.proxy.findAll.method = 'post'

// Пусть весь экземпляр действует
// Это нормально, потому что это пустой объект
article.options.proxy = {
  findAll: {
    method: 'post'  
  }
}

// Сделать текущий вызов эффективным
article
  .method('post')
  .findAll()

Обратите внимание, что порядок приоритета: метод > экземпляр > всё приложение, первый будет перекрывать второй.

Инструменты:

handle.js включает набор инструментов, который инкапсулирует некоторые распространённые логики интерфейса, помогая вам быстро создавать сложные интерфейсы и в полной мере использовать преимущества инкапсуляции.

Параметры интерфейса:

Метод where инструмента помогает вам более гибко обрабатывать логику параметров интерфейса и предоставляет шесть удобных способов записи предложений where.

  1. То же имя:
article
    // uid ☞ идентификатор пользователя
    // Запросить все данные статьи указанного пользователя
    .where('uid')
    .findAll()
  1. Множественное условие с тем же именем:
article
    // Запросите данные указанной статьи пользователя, удовлетворяющие двум условиям одновременно
    .where('uid', 'id')
    .findAll()
  1. Предоставление значений по умолчанию:
article
    // Запросить данные статьи с id = 1
    // Обратите внимание, что необходимо передать массив
    .where(['id', 1])
    .findAll()
  1. Псевдоним:
article
    // Используйте псевдоним aid для запроса данных статьи
    // Внешнее использование псевдонима aid, внутреннее использование id
    .where(['id', '@aid'])
    .findAll()
  1. Необязательные значения:
article
    // Запросить данные статьи, используя id или uid
    // Если ни id, ни uid не указаны, это означает отсутствие каких-либо ограничений условий, и это вернёт все данные
    // Вы должны быть осторожны!
    .where(['!id', '!uid'])
    .findAll()
  1. Op:
article
    // Запросить данные статьи больше указанного id
    .where('id >')
    .findAll()

Некоторые синтаксисы Op требуют специальных динамических значений, поэтому Handle добавляет поддержку передачи массива в качестве второго элемента функции. findAll()

where 支持的所有 Op 便捷写法。
let opTag = {
    '>': 'gt',
    '>=': 'gte',
    '<': 'lt',
    '<=': 'lte',
    '!=': 'ne',
    '=': 'and',
    '#and': 'and',
    '#or': 'or',
    '#gt': 'gt',
    '#gte': 'gte',
    '#lt': 'lt',
    '#lte': 'lte',
    '#ne': 'ne',
    '#eq': 'eq',
    '#not': 'not',
    '#between': 'between',
    '#notBetween': 'notBetween',
    '#in': 'in',
    '#notIn': 'notIn',
    '#like': 'like',
    '#notLike': 'notLike',
    '#iLike': 'iLike',
    '#regexp': 'regexp',
    '#iRegexp': 'iRegexp',
    '#notIRegexp': 'notIRegexp',
    '#overlap': 'overlap',
    '#contains': 'contains',
    '#contained': 'contained',
    '#any': 'any',
    '#col': 'col',
};

Кроме того, вышеуказанные шесть видов удобных сокращений можно комбинировать, но необходимо учитывать некоторые ограничения:

  • псевдонимный синтаксис (@) может использоваться только для второго элемента массива;
  • синтаксис необязательного значения (!) не может использоваться для второго элемента массива;
  • за исключением значения по умолчанию, все имена должны быть допустимыми идентификаторами;
  • если в одной позиции указано несколько обозначений операций (Op), будет применяться только первое, а одинаковые обозначения операций в разных позициях будут перекрывать предыдущие.

Мы можем создавать более мощные параметры интерфейса.

/*

以下接口参数逻辑, может помочь нам выполнить ☞

1. Запрос данных всех статей указанного пользователя.
2. Запрос данных статей указанного пользователя и указанных данных статьи.
3. Запрос данных статей указанного пользователя с приблизительным соответствием указанному заголовку статьи.

Обратите внимание, что id и title являются необязательными,
а uid должен быть указан.
*/
full: db.article
    .where('uid')
    .where('!id')
    .where(['!title #like', d => `%${d.title}%`])
    .findAll();

Нечёткое соответствие

fuzzyQuery, fuzzyQueryLeft и fuzzyQueryRight могут помочь вам быстро создать запрос с нечётким соответствием.

article
    // принимает один параметр запроса
    // по умолчанию это name
    .fuzzyQuery('title')
    .findAll()
article
    // необязательно
    .fuzzyQuery('!title')
    .findAll()

Разбиение на страницы

pagination помогает быстро создать логику разбиения на страницы.


article
    // на каждой странице 10 записей данных
    // count — количество записей на странице, по умолчанию 15
    // page — номер страницы, начиная с которой нужно начать, по умолчанию 0
    .pagination(10)
    .findAll()

Сортировка

order помогает легко создать сортировку.


article
    // сортировка по дате создания в порядке убывания
    .order(['createdAt', 'DESC'])
    .findAll()

Связывание

include помогает добавить связь (дополнительные сведения о связывании см. в официальной документации sequelize)).

article
    // запросить данные статьи, одновременно
    // запрашивая данные пользователя и комментарии каждой статьи
    .include(User, Comment)
    .findAll()

Обработка данных

remove помогает удалить указанное поле из данных запроса. set позволяет изменить его.

article
    // удалить поле status
    // запретить пользователям обновлять его
    .remove('status')
    .update()
article
    // изменить значение поля status на fall
    set('status', 'fall')
    .update()

Условные ветви

it похож на оператор if, он может позволить логике вашего интерфейса иметь ветви, что очень важно для некоторых интерфейсов с небольшими различиями, вы можете объединить их в один интерфейс с помощью it. Кроме того, он также может выполнять роль переключателя для определённых запросов.

it(condition, f1, [f2])

  • string/function condition: используется для условия данных запроса;
  • array/function f1: выполняется при успешном тестировании;
  • array/function f2: выполняется при неудачном тестировании.

Его синтаксис:

it(условие, условие выполнено, условие не выполнено)
// поле
// когда comment == ture, выполнить f1, иначе f2
// обратите внимание, что внутреннее сравнение использует равенство
it('comment', f1, f2)

// функция
// когда count больше 2, выполнить f1, иначе f2
it(d => d.count > 2, f1, f2)

// другое, f1, f2. Может быть функцией или массивом функций
it('comment', f1, [f1, f2, f3])

// условие невыполнения может быть опущено
it('comment', [f1, f2, f3])
article
    // когда комментарий,
    // одновременно запрашивать данные комментариев каждой статьи
    .it('comment', include(Comment))

not является обратным вариантом it.

not(условие, не выполнено, выполнено)

more похож на switch, но может разветвляться на несколько условий.

article
    itField('sort', {
      // когда sort = 'name', выполнить
      'name': f1,    
      // когда sort = 'age', выполнить
      'age': [f2, f3],   
      // когда sort = 'height', выполнить
      'height': f4          
    })
    .findAll()

Scope

Цепочка вызовов выглядит лаконично и элегантно, но ей не хватает хорошей возможности повторного использования. Когда у вас есть группа одинаковых или похожих логических интерфейсов, вы можете использовать независимые версии функций для повторного инкапсуляции, а затем вызвать метод scope для добавления.

// импортировать независимый объект функций Scopes
const Scopes = Handle.Scopes
const {where, pagination, fuzzyQuery, include, order, it, merge} = Scopes

function nb () {
  // использовать функцию слияния для объединения нескольких функций инструментов
  return merge(
    where('uid'),
    where('!id'),
    fuzzyQuery('!title'),
    order(['createdAt', 'DESC']),
    pagination(10),
  )
}


article
    .scope(nb)
    .findAll()

Параметры объекта, объединённые методом scope, действительны только для первого используемого метода. Если вы хотите, чтобы все текущие экземпляры модели использовали некоторые методы инструментов, вы можете добавить их через defaultScope к экземпляру.

Пользовательский

Вы также можете расширить пользовательские методы инструментов. Вам нужно добавить свои пользовательские функции инструментов в Handle.Scope перед инициализацией.

Handle.Scope.myUtil = function (d) {
   // вернуть полный объект параметров
    return {
        where: {
            uid: d.uid
        }
        // другие параметры
    }
}

Предоставляя функцию по умолчанию для указания параметров, вы можете глобально использовать пользовательскую функцию инструмента myUtil.

article
    .myUtil()
    .findAll() **Является этапом для случаев, когда необходимо выполнить многотабличные операции над интерфейсом и дополнительно обработать возвращаемые данные.** Также это один из шагов при реализации более сложных интерфейсов.

```js
const find = artcile.process(async function (d) {
    // 数据校验
    // Опущено
    
    // Запрос пользователя
    const userData = await user
        .where('username', 'password')
        .rawFindOne()
    // Запрос текущих статей пользователя
    const result = await this
        .where(['id', userData.id])
        .rawFindAll()
    // Возврат только рекомендованных статей
    return result.filter(e => e.type === 'recommend')
})

По умолчанию метод process используется для запросов get, но Handle поддерживает 6 стандартных методов HTTP-запросов (get/head/put/delete/post/options).

articleStar.process('post', async function (d) {})

Транзакции

transaction — это простая оболочка для транзакций sequelize, встроенная в process. В использовании они полностью идентичны.

articleStar.transaction(async function (d) {
  /** Обработка, связанная с транзакциями */
  return /** Обработанные данные */
}),

Перехватчики

Handle предоставляет три глобальных перехватчика before, after и data в объекте параметров. Каждый быстрый метод вызывает эти перехватчики, а метод процесса игнорирует их. Процесс вызывает before перед вызовом обратного вызова и after и data после него.

new Handle(model, {
    // before-перехватчик выполняется перед операцией с базой данных
    before (data, ctx, next) {
    
    }
    // after-перехватчик выполняется после операции с базой данных
    after (result, ctx, next) {
        
    }
    // data-перехватчик может выполнять некоторую обработку до возврата данных на фронт или после перехвата исключений
    data (err, result, ctx, next) {
        
    }
})

Кроме того, каждый экземпляр метода имеет функции before и after, которые позволяют регистрировать перехватчики только для экземпляра. Это помогает нам выполнять некоторые полезные задачи.

Мы можем использовать before-перехватчик для проверки данных, отправленных фронтом.

article
    .before(function (data) {
      const {title} = data
      if (!title) {
        // Выбрасывается исключение
        // Оно будет перехвачено глобальным data-перехватчиком
        throw new Error('Заголовок статьи не может быть пустым')
      }
    
      if (title.length < 1 || title.code.length > 25) {
        throw new Error('Длина заголовка статьи должна быть от 2 до 25 символов')
      }
      return data
    })
    .create()

Также можно использовать after-перехватчик для фильтрации данных.

article
    .after(function (data) {
      // Возвращается только количество статей
      return data.length
    })
    .where('uid')
    .findAll()

Обратите внимание, что before-перехватчик экземпляра выполняется раньше глобального before-перехватчика, а after-перехватчик экземпляра — позже глобального after-перехватчика.

Исходные данные

Handle разумно генерирует параметры методов sequelize. Обычно нам не нужно об этом беспокоиться. Однако для increment, decrement или некоторых особых случаев вы можете использовать указанные данные вместо данных запроса, обработанных Handle. Для этого используйте метод raw.

// Увеличение поля hot
article
  .raw('hot')
  .increment('id')

Однако имейте в виду, что данные запроса всё равно будут использоваться в различных сценариях, таких как инструменты анализа области видимости и where. Просто данные запроса заменяются исходными данными при объединении в параметры метода sequelize. Поэтому изменение данных запроса в перехватчиках или других местах не влияет на доступ к базе данных. Благодаря этому вы можете использовать библиотеки, подобные mock, для пакетного добавления данных в базу данных. (Возможно, в будущем будет поддерживаться имитация данных с помощью mock.)

Итог

Если вы не можете найти функцию, которая решает вашу проблему в наборе инструментов, я настоятельно рекомендую вам обернуть соответствующий код в пользовательскую область видимости и использовать её. Во-первых, у вас будет элегантная структура кода и читаемые имена. Во-вторых, вам не придётся заново писать код при повторном использовании. Если ваша область видимости достаточно универсальна, вы можете отправить её в handle.js, чтобы помочь другим пользователям.

Если вы не используете pull requests или Issues, вы также можете связаться со мной следующим образом:

Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/ntbl-ntbl-handle.git
git@api.gitlife.ru:oschina-mirror/ntbl-ntbl-handle.git
oschina-mirror
ntbl-ntbl-handle
ntbl-ntbl-handle
dev