fix: 飞书群绑定租户完善

- tenants.js 去掉已删除的 appid/appsecret 元素引用
- showEditTenantModal 改为从 API 加载完整租户数据(不再传参拼接)
- saveTenant 保留已有的非 feishu 配置,只更新 chat_groups
- 租户列表显示绑定群数量或'未绑定飞书群'
- 飞书 bot/longconn 复用已有会话时同步更新 tenant_id(群重新绑定后立即生效)
- 删除租户后同步刷新租户选择器
This commit is contained in:
2026-04-02 15:25:50 +08:00
parent 683b64ed62
commit 28e90d2182
4 changed files with 42 additions and 35 deletions

Binary file not shown.

View File

@@ -150,6 +150,9 @@ class FeishuLongConnService:
for session in active_sessions:
if session.get('user_id') == session_user_id:
session_id = session.get('session_id')
# 更新会话的 tenant_id群可能重新绑定了租户
if session_id in chat_manager.active_sessions:
chat_manager.active_sessions[session_id]['tenant_id'] = tenant_id
logger.info(f"✅ 找到已有会话: {session_id}")
break

View File

@@ -117,6 +117,9 @@ def _process_message_in_background(app, event_data: dict):
for session in active_sessions:
if session.get('user_id') == user_id:
session_id = session.get('session_id')
# 更新会话的 tenant_id群可能重新绑定了租户
if session_id in chat_manager.active_sessions:
chat_manager.active_sessions[session_id]['tenant_id'] = tenant_id
logger.info(f"[Feishu Bot] 找到已有会话: {session_id}")
break

View File

@@ -15,9 +15,7 @@ Object.assign(TSPDashboard.prototype, {
}
container.innerHTML = tenants.map(t => {
const feishuCfg = t.config?.feishu || {};
const groupCount = (feishuCfg.chat_groups || []).length;
const hasFeishu = feishuCfg.app_id || groupCount > 0;
const chatGroups = t.config?.feishu?.chat_groups || [];
return `
<div class="card mb-2">
<div class="card-body d-flex justify-content-between align-items-center py-2">
@@ -26,10 +24,10 @@ Object.assign(TSPDashboard.prototype, {
<span class="text-muted ms-2">(${t.tenant_id})</span>
${t.description ? `<br><small class="text-muted">${t.description}</small>` : ''}
${!t.is_active ? '<span class="badge bg-secondary ms-2">已禁用</span>' : ''}
${hasFeishu ? `<span class="badge bg-info ms-2"><i class="fas fa-robot me-1"></i>飞书${groupCount > 0 ? ` (${groupCount}群)` : ''}</span>` : ''}
${chatGroups.length > 0 ? `<span class="badge bg-info ms-2"><i class="fas fa-comments me-1"></i>${chatGroups.length} 个飞书群</span>` : '<span class="badge bg-light text-muted ms-2">未绑定飞书群</span>'}
</div>
<div class="btn-group btn-group-sm">
<button class="btn btn-outline-primary" onclick="dashboard.showEditTenantModal('${t.tenant_id}', '${(t.name || '').replace(/'/g, "\\'")}', '${(t.description || '').replace(/'/g, "\\'")}')">
<button class="btn btn-outline-primary" onclick="dashboard.showEditTenantModal('${t.tenant_id}')">
<i class="fas fa-edit"></i>
</button>
${t.tenant_id !== 'default' ? `
@@ -54,33 +52,30 @@ Object.assign(TSPDashboard.prototype, {
document.getElementById('tenant-id-group').style.display = '';
document.getElementById('tenant-name-input').value = '';
document.getElementById('tenant-desc-input').value = '';
document.getElementById('tenant-feishu-appid').value = '';
document.getElementById('tenant-feishu-appsecret').value = '';
document.getElementById('tenant-feishu-chatgroups').value = '';
new bootstrap.Modal(document.getElementById('tenantModal')).show();
},
async showEditTenantModal(tenantId, name, description) {
async showEditTenantModal(tenantId) {
document.getElementById('tenantModalTitle').textContent = '编辑租户';
document.getElementById('tenant-edit-id').value = tenantId;
document.getElementById('tenant-id-input').value = tenantId;
document.getElementById('tenant-id-input').disabled = true;
document.getElementById('tenant-name-input').value = name;
document.getElementById('tenant-desc-input').value = description;
document.getElementById('tenant-id-group').style.display = 'none';
// 加载租户的飞书配置
// 加载租户完整数据
try {
const resp = await fetch('/api/tenants');
const tenants = await resp.json();
const tenant = tenants.find(t => t.tenant_id === tenantId);
const feishuCfg = tenant?.config?.feishu || {};
document.getElementById('tenant-feishu-appid').value = feishuCfg.app_id || '';
document.getElementById('tenant-feishu-appsecret').value = feishuCfg.app_secret || '';
document.getElementById('tenant-feishu-chatgroups').value = (feishuCfg.chat_groups || []).join('\n');
if (tenant) {
document.getElementById('tenant-name-input').value = tenant.name || '';
document.getElementById('tenant-desc-input').value = tenant.description || '';
const chatGroups = tenant.config?.feishu?.chat_groups || [];
document.getElementById('tenant-feishu-chatgroups').value = chatGroups.join('\n');
}
} catch (e) {
document.getElementById('tenant-feishu-appid').value = '';
document.getElementById('tenant-feishu-appsecret').value = '';
document.getElementById('tenant-feishu-chatgroups').value = '';
console.warn('加载租户数据失败:', e);
}
new bootstrap.Modal(document.getElementById('tenantModal')).show();
@@ -92,24 +87,32 @@ Object.assign(TSPDashboard.prototype, {
const name = document.getElementById('tenant-name-input').value.trim();
const description = document.getElementById('tenant-desc-input').value.trim();
// 飞书配置
const feishuAppId = document.getElementById('tenant-feishu-appid').value.trim();
const feishuAppSecret = document.getElementById('tenant-feishu-appsecret').value.trim();
// 飞书群绑定
const chatGroupsText = document.getElementById('tenant-feishu-chatgroups').value.trim();
const chatGroups = chatGroupsText ? chatGroupsText.split('\n').map(s => s.trim()).filter(Boolean) : [];
const config = {};
if (feishuAppId || feishuAppSecret || chatGroups.length > 0) {
config.feishu = {};
if (feishuAppId) config.feishu.app_id = feishuAppId;
if (feishuAppSecret) config.feishu.app_secret = feishuAppSecret;
if (chatGroups.length > 0) config.feishu.chat_groups = chatGroups;
// 构建 config保留已有的非 feishu 配置
let existingConfig = {};
if (editId) {
try {
const resp = await fetch('/api/tenants');
const tenants = await resp.json();
const tenant = tenants.find(t => t.tenant_id === editId);
existingConfig = tenant?.config || {};
} catch (e) {}
}
const config = { ...existingConfig };
if (chatGroups.length > 0) {
config.feishu = { ...(config.feishu || {}), chat_groups: chatGroups };
} else {
// 清空飞书群绑定
if (config.feishu) {
delete config.feishu.chat_groups;
if (Object.keys(config.feishu).length === 0) delete config.feishu;
}
}
if (!name) {
this.showNotification('租户名称不能为空', 'error');
return;
}
if (!name) { this.showNotification('租户名称不能为空', 'error'); return; }
try {
let response;
@@ -120,10 +123,7 @@ Object.assign(TSPDashboard.prototype, {
body: JSON.stringify({ name, description, config })
});
} else {
if (!tenantId) {
this.showNotification('租户标识不能为空', 'error');
return;
}
if (!tenantId) { this.showNotification('租户标识不能为空', 'error'); return; }
response = await fetch('/api/tenants', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
@@ -154,6 +154,7 @@ Object.assign(TSPDashboard.prototype, {
if (data.success) {
this.showNotification('租户已删除', 'success');
this.loadTenantList();
this.populateTenantSelectors();
} else {
this.showNotification(data.error || '删除失败', 'error');
}