feat: 累积功能变更 — 聊天集成、租户管理、小程序更新、ETL 增强、迁移脚本

包含多个会话的累积代码变更:
- backend: AI 聊天服务、触发器调度、认证增强、WebSocket、调度器最小间隔
- admin-web: ETL 状态页、任务管理、调度配置、登录优化
- miniprogram: 看板页面、聊天集成、UI 组件、导航更新
- etl: DWS 新任务(finance_area_daily/board_cache)、连接器增强
- tenant-admin: 项目初始化
- db: 19 个迁移脚本(etl_feiqiu 11 + zqyy_app 8)
- packages/shared: 枚举和工具函数更新
- tools: 数据库工具、报表生成、健康检查
- docs: PRD/架构/部署/合约文档更新

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Neo
2026-04-06 00:03:48 +08:00
parent 70324d8542
commit 6f8f12314f
515 changed files with 76604 additions and 7456 deletions

View File

@@ -1,3 +1,9 @@
/* AI_CHANGELOG
| 日期 | Prompt | 变更 |
|------|--------|------|
| 2026-03-23 | 角色路由+页面权限守卫 | onShow 添加 checkPageAccess 权限守卫 |
*/
import { checkPageAccess } from '../../utils/auth-guard'
// pages/chat/chat.ts — AI 对话页
// CHANGE 2026-03-20 | RNS1.4 T8.1: 多入口参数路由,替换 mock 为真实 API
// CHANGE 2026-03-20 | RNS1.4 T8.2: 替换 simulateStreamOutput 为真实 SSE 连接
@@ -177,6 +183,10 @@ Page({
customerId: '',
/** 输入栏距底部距离(软键盘弹出时动态调整)*/
inputBarBottom: 0,
/** 来源页面标识(看板入口) */
sourcePage: '',
/** 看板筛选参数 */
pageFilters: {} as Record<string, string>,
},
/** 消息计数器,用于生成唯一 ID */
@@ -188,6 +198,11 @@ Page({
/** 最后一次发送的用户消息内容(用于重试) */
_lastUserContent: '',
onShow() {
// 权限守卫:检查登录状态、账号禁用、角色权限
checkPageAccess('pages/chat/chat')
},
// CHANGE 2026-03-20 | RNS1.4 T8.1: 多入口参数路由
// 优先级historyId → taskId → customerId → coachId → general
onLoad(options) {
@@ -210,6 +225,15 @@ Page({
} else if (options?.coachId) {
// 从 coach-detail 跳转:≤ 3 天复用、> 3 天新建
this.loadMessagesByContext('coach', options.coachId)
} else if (options?.sourcePage) {
// 看板类入口:保存来源页面和筛选参数
const filterKeys = ['timeDimension', 'areaFilter', 'dimension', 'typeFilter', 'projectFilter']
const pageFilters: Record<string, string> = {}
for (const key of filterKeys) {
if (options[key]) pageFilters[key] = options[key]
}
this.setData({ sourcePage: options.sourcePage, pageFilters })
this.loadMessagesByContext(options.sourcePage, '')
} else {
// 无参数入口:始终新建通用对话
this.loadMessagesByContext('general', '')
@@ -219,6 +243,7 @@ Page({
/** 通过 chatId 加载历史消息historyId 入口) */
async loadMessages(chatId: string) {
this.setData({ pageState: 'loading' })
wx.showLoading({ title: '加载中...', mask: true })
try {
const res = await fetchChatMessages(chatId)
this.setData({ chatId: String(res.chatId) })
@@ -231,12 +256,15 @@ Page({
this.scrollToBottom()
} catch {
this.setData({ pageState: 'error' })
} finally {
wx.hideLoading()
}
},
/** 通过上下文类型加载消息task/customer/coach/general 入口) */
async loadMessagesByContext(contextType: string, contextId: string) {
this.setData({ pageState: 'loading' })
wx.showLoading({ title: '加载中...', mask: true })
try {
const res = await fetchChatMessagesByContext(contextType, contextId)
// 缓存后端返回的 chatId后续发送消息使用
@@ -258,6 +286,8 @@ Page({
this.scrollToBottom()
} catch {
this.setData({ pageState: 'error' })
} finally {
wx.hideLoading()
}
},
@@ -427,7 +457,14 @@ Page({
const requestTask = wx.request({
url: `${API_BASE}/api/xcx/chat/stream`,
method: 'POST',
data: { chatId: Number(chatId), content },
data: {
chatId: Number(chatId),
content,
...(this.data.sourcePage ? {
sourcePage: this.data.sourcePage,
pageContext: this.data.pageFilters,
} : {}),
},
header: headers,
enableChunked: true,
responseType: 'arraybuffer',