Клиент на базе мини-программы WeChat
Мини-программа WeChat предоставляет мобильную точку входа для вопросов по базе знаний и импорта веб-страниц, подключаясь к уже работающему сервису aihelpme.ru. Исходный код находится в miniprogram/. При использовании сначала настройте адрес API и API Key, затем выберите базу знаний:
- Импорт URL веб-страницы в выбранную базу знаний.
- Задавайте вопросы по выбранной базе знаний.
Адрес бэкенда и настройка аутентификации
Мини-программа не хардкодит адрес бэкенда в коде: все сведения о подключении пользователь вводит на странице «Настройки», они сохраняются в локальном хранилище по ключу weknora_settings через wx.setStorageSync и состоят из четырёх полей:
{
baseUrl: "http://localhost:8080", // значение по умолчанию, записывается app.js в onLaunch
apiKey: "",
selectedKnowledgeBaseId: "",
locale: "zh" // язык интерфейса: zh | en
}- Значения по умолчанию: если в
onLaunchвminiprogram/app.jsобнаруживается отсутствие локальных настроек, записываются значения по умолчаниюbaseUrl: "http://localhost:8080", пустойapiKeyиlocale: "zh". Значения по умолчанию лишь упрощают локальную разработку; при реальном использовании нужно заменить их на настоящий адрес на странице настроек. - Чтение/запись и нормализация:
miniprogram/utils/config.jsпредоставляетgetSettings()/saveSettings(); функцияnormalizeBaseUrl()убирает пробелы по краям и завершающий/, аnormalizeLocale()относит любое значение, отличное отen, кzh. - Способ аутентификации — API Key: все запросы в
miniprogram/utils/request.jsединообразно несут заголовки: X-API-Key: <введённый пользователем API Key>(получается в aihelpme.ru в разделе «Настройки → Интеграции публикации → Интеграция API», имеет видsk-...);X-Request-ID: mp-<метка времени>-<случайная строка>(для трассировки на сервере);Content-Type: application/json.- Предварительная проверка: если отсутствует
baseUrlилиapiKey, запрос сразу отклоняется ошибочным Promise, а подсказка отображается на текущем языке (например, «Сначала настройте адрес API aihelpme.ru.» / «Сначала настройте API Key aihelpme.ru.»);onShowвpages/index/index.jsтакже показывает подсказку, ведущую пользователя на страницу настроек. - Настройка AppID: скопируйте
miniprogram/project.private.config.json.exampleвproject.private.config.jsonи укажите собственный AppID (содержимое файла-примера —{"appid": "your-wechat-mini-program-appid"}). Внимание: текущий общийproject.config.jsonсодержит полеappid; при использовании собственной мини-программы перекройте его приватной конфигурацией.
Используемые интерфейсы бэкенда (все определены в miniprogram/utils/request.js):
| Функция | Метод и путь |
|---|---|
listKnowledgeBases() | GET /api/v1/knowledge-bases |
createKnowledgeFromURL(kbId, url, enableMultimodel) | POST /api/v1/knowledge-bases/{kbId}/knowledge/url |
createSession(kbId) | POST /api/v1/sessions |
knowledgeChat(sessionId, query, kbId) | POST /api/v1/knowledge-chat/{sessionId} |
Китайский/английский и локальные настройки
Начиная с v0.8.2, на странице настроек можно выбрать «Китайский» или «English», по умолчанию — китайский; выбор сохраняется в локальных настройках как locale (zh / en) и переживает перезапуск. После переключения немедленно обновляются тексты страниц, заголовок панели навигации (wx.setNavigationBarTitle) и тексты нижних tab (wx.setTabBarItem); нижние tab также имеют иконки (assets/tab/*.png). Тексты собраны в utils/i18n.js; новые страницы должны переиспользовать этот модуль.
Сейчас страница чата после завершения запроса разбирает весь текст SSE и накапливает фрагменты answer, без пофрагментного рендеринга в реальном времени. Для официального выпуска нужно добавить домен API в список разрешённых доменов для request.
Процесс сборки и публикации
Мини-программа не требует шага компиляции (нативная разработка, без цепочки инструментов сборки) — её достаточно открыть напрямую в инструментах разработчика WeChat (WeChat DevTools):
- Импорт проекта: в инструментах разработчика WeChat выберите «Импорт проекта», указав каталог
miniprogram/репозитория. Инструмент прочитаетproject.config.json(имя проекта "aihelpme.ru Mini Program"). - Настройка AppID: скопируйте
miniprogram/project.private.config.json.exampleвproject.private.config.jsonи заменитеappidна фактический AppID мини-программы.project.private.config.jsonявляется личной приватной конфигурацией, игнорируется.gitignoreи не должен коммититься. - Настройка подключения к бэкенду: после запуска перейдите на tab «Настройки», укажите API Base URL (например,
https://weknora.example.com) и API Key aihelpme.ru (получается в «Настройки → Интеграции публикации → Интеграция API»), при необходимости переключите язык интерфейса и сохраните. - Замечания по локальной отладке: в
project.config.jsonвключёнurlCheck: true, поэтому инструменты разработчика по умолчанию блокируют запросы к неразрешённым доменам, таким какlocalhost. Для локального теста можно поставить галочку «Не проверять разрешённые домены» в DevTools или открыть сервис aihelpme.ru через HTTPS-домен разработки. - Публикация: перед официальным выпуском нужно в административной панели мини-программы на платформе WeChat добавить домен API aihelpme.ru (обязательно HTTPS) в список разрешённых доменов для request (белый список доменов request); затем в инструментах разработчика нажать «Загрузить», чтобы отправить код, а в административной панели отправить на проверку и опубликовать.
Тестирование
miniprogram/package.json определяет единственный скрипт:
cd miniprogram
npm test # фактически выполняет node --test ../tests/miniprogram/*.test.jsТо есть используются встроенный test runner Node.js и модульные тесты из tests/miniprogram/miniprogram.test.js репозитория (охватывают разбор SSE, нормализацию адреса, переключение китайский/английский, заголовки и тело запроса, логику загрузки страницы баз знаний); устанавливать зависимости не требуется.
Совместимость с базовой библиотекой
Страницы и код в utils/ не используют опциональную цепочку ?. и объединение с null ??: часть версий базовой библиотеки не поддерживает этот синтаксис, что приводит к белому экрану (в v0.8.2 исправлена пустая страница базы знаний из-за этой проблемы). Привязки текста в WXML также остаются плоскими полями, чтобы корректно рендериться в разных версиях базовой библиотеки. При изменении кода сохраняйте это ограничение.
Справочная реализация
Технологический стек
Этот клиент — нативная мини-программа WeChat (native Mini Program), без кросс-платформенных фреймворков вроде Taro / uni-app / mpvue и без каких-либо npm-зависимостей времени выполнения:
miniprogram/app.js— стандартная точка входаApp({...}), приonLaunchзаписывает в локальное хранилище настройки по умолчанию;miniprogram/app.json— стандартная глобальная конфигурация мини-программы (pages,window,tabBar);miniprogram/app.wxss— глобальные стили; каждая страница представляет собой комплект из четырёх файловjs / wxml / wxss / json;miniprogram/package.json— имя пакетаweknora-miniprogram(version0.1.0),description— "WeChat Mini Program plugin for aihelpme.ru", нетdependencies, только один тестовый скрипт (см. «Тестирование» ниже);miniprogram/project.config.json—compileType: "miniprogram",libVersion: "latest"(базовая библиотека последней версии), в опциях компиляции включеныes6,enhance,postcss,minified,minifyWXSS,minifyWXML, а такжеurlCheck: true(проверка разрешённых доменов). Сейчас этот файл содержит полеappid; собственный AppID задаётся через приватный файл конфигурации (см. «Процесс сборки и публикации»).
Глобальный стиль окна: заголовок панели навигации WeKnora, цвет фона #0d3b2a (тёмно-зелёный), текст белый.
Список страниц
В miniprogram/app.json зарегистрированы 3 страницы, и все три одновременно образуют нижний tabBar (цвет выбора #07c05f, у каждого пункта два набора иконок — обычная / выбранная). Тексты tab по умолчанию китайские и переключаются по языку во время работы:
| Путь страницы | Текст tabBar (Китайский / English) | Назначение |
|---|---|---|
pages/index/index | База знаний / Knowledge | Главная страница. Проверяет, настроены ли baseUrl / API Key; если нет — показывает подсказку и позволяет одним действием перейти на страницу настроек; вызывает GET /api/v1/knowledge-bases для загрузки списка баз знаний, выбирает базу через picker или по списку (результат выбора сохраняется в локальное хранилище); после ввода URL веб-страницы вызывает POST /api/v1/knowledge-bases/{id}/knowledge/url, импортируя этот URL в выбранную базу знаний (enable_multimodel жёстко равно false) |
pages/chat/chat | Вопросы / Chat | Страница вопросов по базе знаний. При первом вопросе лениво создаёт сессию через POST /api/v1/sessions (с выбранным knowledge_base_id), затем задаёт вопрос через POST /api/v1/knowledge-chat/{sessionId}; тело ответа — текст SSE, клиент разбирает его через utils/sse.js, склеивает фрагменты с response_type === "answer" и показывает целиком (при сбое разбора отображает исходный ответ) |
pages/settings/settings | Настройки / Settings | Страница настройки подключения. Ввод API Base URL и API Key (поле ввода пароля), выбор языка интерфейса, сохранение в локальное хранилище weknora_settings |
Служебные модули utils/
| Файл | Назначение |
|---|---|
miniprogram/utils/config.js | Слой хранения настроек: определяет ключ STORAGE_KEY = "weknora_settings", предоставляет getSettings(), saveSettings() (объединяющее обновление), normalizeBaseUrl() (trim и удаление завершающей косой черты) и normalizeLocale() |
miniprogram/utils/i18n.js | Таблица текстов китайский/английский и t(), getLocale() / setLocale(), applyTabBar() (обновляет тексты и иконки нижних tab), applyNavTitle() (обновляет заголовок панели навигации) |
miniprogram/utils/request.js | HTTP-обёртка на промисах поверх wx.request: склейка baseUrl + path, добавление заголовков X-API-Key / X-Request-ID, единая проверка 2xx и извлечение сообщения об ошибке (сначала error.message, затем message, в крайнем случае HTTP <status>); также экспортирует 4 бизнес-функции API из таблицы выше |
miniprogram/utils/sse.js | Разбор текста Server-Sent Events: parseSSE(raw) разбивает на блоки событий по пустым строкам, разбирает строки event: / data:; collectAnswerFromSSE(raw) разбирает data каждого события как JSON и накапливает content с response_type === "answer", получая итоговый текст ответа. Обратите внимание: на стороне мини-программы нет потокового рендеринга — после получения полного текста SSE от wx.request он разбирается и отображается один раз |
