Commit a5cd8229 by Archer Committed by GitHub

perf: wechat channel (#6800)

* perf: wechat channel

* fix: review
parent b2cfada9
---
title: 'V4.14.14'
description: 'FastGPT V4.14.14 更新说明'
---
## 升级指南
### 1. 更新镜像 tag
- 更新 fastgpt-app(fastgpt 主服务) 镜像 tag: v4.14.14
## 🐛 修复
## ⚙️ 优化
1. 个人微信发布渠道,优化轮询策略(拉取与回复解耦),避免数据量超大时出现阻塞。
2. 新增环境变量 `WECHAT_CHANNEL_CONCURRENCY`(默认 1000)用于控制微信渠道 poll worker 并发数,建议 ≥ online channel 峰值。
\ No newline at end of file
......@@ -117,6 +117,7 @@ description: FastGPT 文档目录
- [/docs/self-host/upgrading/4-14/41411](/docs/self-host/upgrading/4-14/41411)
- [/docs/self-host/upgrading/4-14/41412](/docs/self-host/upgrading/4-14/41412)
- [/docs/self-host/upgrading/4-14/41413](/docs/self-host/upgrading/4-14/41413)
- [/docs/self-host/upgrading/4-14/41414](/docs/self-host/upgrading/4-14/41414)
- [/docs/self-host/upgrading/4-14/4142](/docs/self-host/upgrading/4-14/4142)
- [/docs/self-host/upgrading/4-14/4143](/docs/self-host/upgrading/4-14/4143)
- [/docs/self-host/upgrading/4-14/4144](/docs/self-host/upgrading/4-14/4144)
......
......@@ -230,6 +230,7 @@
"document/content/docs/self-host/upgrading/4-14/41412.mdx": "2026-04-21T23:04:26+08:00",
"document/content/docs/self-host/upgrading/4-14/41413.en.mdx": "2026-04-21T23:04:26+08:00",
"document/content/docs/self-host/upgrading/4-14/41413.mdx": "2026-04-21T23:04:26+08:00",
"document/content/docs/self-host/upgrading/4-14/41414.mdx": "2026-04-22T16:09:29+08:00",
"document/content/docs/self-host/upgrading/4-14/4142.en.mdx": "2026-03-03T17:39:47+08:00",
"document/content/docs/self-host/upgrading/4-14/4142.mdx": "2026-03-03T17:39:47+08:00",
"document/content/docs/self-host/upgrading/4-14/4143.en.mdx": "2026-03-03T17:39:47+08:00",
......@@ -250,6 +251,7 @@
"document/content/docs/self-host/upgrading/4-14/41481.mdx": "2026-03-09T17:39:53+08:00",
"document/content/docs/self-host/upgrading/4-14/4149.en.mdx": "2026-03-23T12:17:04+08:00",
"document/content/docs/self-host/upgrading/4-14/4149.mdx": "2026-04-07T21:01:52+08:00",
"document/content/docs/self-host/upgrading/4-15/4150.mdx": "2026-04-22T14:36:14+08:00",
"document/content/docs/self-host/upgrading/outdated/40.en.mdx": "2026-03-03T17:39:47+08:00",
"document/content/docs/self-host/upgrading/outdated/40.mdx": "2026-03-03T17:39:47+08:00",
"document/content/docs/self-host/upgrading/outdated/41.en.mdx": "2026-03-03T17:39:47+08:00",
......@@ -391,7 +393,7 @@
"document/content/docs/self-host/upgrading/upgrade-intruction.en.mdx": "2026-03-03T17:39:47+08:00",
"document/content/docs/self-host/upgrading/upgrade-intruction.mdx": "2026-04-20T13:51:34+08:00",
"document/content/docs/toc.en.mdx": "2026-04-21T23:04:26+08:00",
"document/content/docs/toc.mdx": "2026-04-21T23:04:26+08:00",
"document/content/docs/toc.mdx": "2026-04-22T16:09:29+08:00",
"document/content/docs/use-cases/app-cases/dalle3.en.mdx": "2026-02-26T22:14:30+08:00",
"document/content/docs/use-cases/app-cases/dalle3.mdx": "2025-07-23T21:35:03+08:00",
"document/content/docs/use-cases/app-cases/english_essay_correction_bot.en.mdx": "2026-02-26T22:14:30+08:00",
......
......@@ -36,6 +36,7 @@ export enum QueueNames {
// Publish
wechatPoll = 'wechatPoll',
wechatReply = 'wechatReply',
/** @deprecated */
websiteSync = 'websiteSync'
......
......@@ -113,6 +113,12 @@ export const env = createEnv({
/** Redis 内存水位检测缓存时长(毫秒),避免每个流请求都调用 INFO MEMORY */
STREAM_RESUME_REDIS_MEMORY_CHECK_INTERVAL_MS: IntSchema.positive().default(5000),
// ===== Wechat outLink =====
/** 微信渠道 poll worker 并发数,需 ≥ online shareId 峰值,否则消息延迟会线性恶化 */
WECHAT_CHANNEL_CONCURRENCY: NumSchema.int().positive().default(1000).meta({
description: '微信渠道 poll worker 并发数'
}),
// Beta features
// Whether the Skill feature is enabled (frontend entries + backend runtime)
SHOW_SKILL: BoolSchema.default(false),
......
......@@ -8,7 +8,7 @@ export type ParsedMessageGroup = {
userId: string;
text: string;
contextToken: string;
msgIds: string[];
lastMsgId: string;
};
export function extractTextFromItem(item: NonNullable<WeixinMessage['item_list']>[number]): string {
......@@ -42,11 +42,11 @@ export function groupMessagesByUser(msgs: WeixinMessage[]): ParsedMessageGroup[]
if (!text) continue;
const userId = msg.from_user_id ?? 'unknown';
const existing = groups.get(userId);
if (existing) {
existing.text += '\n' + text;
existing.msgIds.push(msg.msgid);
existing.lastMsgId = msg.msgid;
if (msg.context_token) {
existing.contextToken = msg.context_token;
}
......@@ -55,7 +55,7 @@ export function groupMessagesByUser(msgs: WeixinMessage[]): ParsedMessageGroup[]
userId,
text,
contextToken: msg.context_token ?? '',
msgIds: [msg.msgid]
lastMsgId: msg.msgid
});
}
}
......
export type WechatPollJobData = {
shareId: string;
};
export type WechatReplyJobData = {
shareId: string;
userId: string;
text: string;
contextToken: string;
lastMsgId: string;
};
......@@ -160,6 +160,8 @@ CHECK_INTERNAL_IP=false
APP_FOLDER_MAX_AMOUNT=1000
# 数据集文件夹最大数量
DATASET_FOLDER_MAX_AMOUNT=1000
# 微信渠道 poll worker 并发数(默认 1000),需 ≥ online channel 数;channel 数超过该值时消息延迟会线性恶化
WECHAT_CHANNEL_CONCURRENCY=1000
# ==================== 上传与账号策略 ====================
# 最大上传文件大小(MB)
......
......@@ -10,10 +10,9 @@ import { useDebounceEffect, useMount } from 'ahooks';
import { v1Workflow2V2 } from '@/web/core/workflow/adapt';
import { defaultAppSelectFileConfig } from '@fastgpt/global/core/app/constants';
import { form2AppWorkflow, appWorkflow2Form } from './utils';
const Edit = dynamic(() => import('./Edit'));
const Logs = dynamic(() => import('../../Logs/index'));
const PublishChannel = dynamic(() => import('../../Publish'));
import PublishChannel from '../../Publish';
import Logs from '../../Logs';
import Edit from './Edit';
const SimpleEdit = () => {
const { t } = useTranslation();
......
......@@ -6,6 +6,7 @@ import { NextAPI } from '@/service/middleware/entry';
import { addAuditLog } from '@fastgpt/service/support/user/audit/util';
import { AuditEventEnum } from '@fastgpt/global/support/user/audit/constants';
import { getI18nAppType } from '@fastgpt/service/support/user/audit/util';
import { stopWechatPolling } from '@fastgpt/service/support/outLink/wechat/mq';
export type OutLinkDeleteQuery = {
id: string;
......@@ -25,7 +26,15 @@ async function handler(
per: OwnerPermissionVal
});
await MongoOutLink.findByIdAndDelete(id);
const outlink = await MongoOutLink.findById(id);
if (outlink && outlink.type === 'wechat') {
await stopWechatPolling(outlink.shareId).catch((error) => {
console.warn('Stop wechat polling failed', error);
});
}
await outlink?.deleteOne();
(async () => {
addAuditLog({
......
......@@ -61,7 +61,7 @@ describe('groupMessagesByUser', () => {
userId: 'u1',
text: 'default text',
contextToken: 'ctx1',
msgIds: ['m1']
lastMsgId: 'm1'
});
});
......@@ -84,7 +84,7 @@ describe('groupMessagesByUser', () => {
expect(result).toHaveLength(1);
expect(result[0].text).toBe('first\nsecond');
expect(result[0].msgIds).toEqual(['m1', 'm2']);
expect(result[0].lastMsgId).toBe('m2');
expect(result[0].contextToken).toBe('ctx2'); // 取最后一条的
});
......@@ -107,7 +107,7 @@ describe('groupMessagesByUser', () => {
const result = groupMessagesByUser(msgs);
expect(result).toHaveLength(1);
expect(result[0].msgIds).toEqual(['m2']);
expect(result[0].lastMsgId).toBe('m2');
});
it('should skip messages with no extractable text', () => {
......@@ -122,7 +122,7 @@ describe('groupMessagesByUser', () => {
const result = groupMessagesByUser(msgs);
expect(result).toHaveLength(1);
expect(result[0].msgIds).toEqual(['m2']);
expect(result[0].lastMsgId).toBe('m2');
});
it('should handle empty message list', () => {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment