feat(integrations,classisland,server,web,desktop): ClassIsland 联动插件——上报课表与上课状态、下发提醒并回执、教室端推送通道
教室机器上的 ClassIsland 现在可以直接和班级小助手互通:插件走 /api/integrations/classisland/*,用设备令牌(X-ClassIsland-Token,库里只存 sha256)独立鉴权,不占用 JWT 通道。 - 服务端:新增 integrations 模块(上报 / 待弹出 / 回执 / 课表镜像 / 回写),两个迁移(设备令牌表、Class.notificationChannel);通知、叫人、联动页下发统一复用 pushToClassIsland,避免"某个页面发的通知不联动" - 插件:packages/classisland-plugin(.NET 8,独立于 pnpm workspace)——BridgeService 上报与提醒轮询、ScheduleMapper/ClassPlanWriter 课表镜像、Avalonia 设置页、ClassHelperNotificationProvider 在 ClassIsland 上弹出提醒;本地/内网地址强制直连,躲开系统代理返回的假 404 - 客户端:设置页可选提醒通道(both / client / classisland),并订阅相应实时事件 - Web 端:新增「ClassIsland 联动」页(设备管理与提醒下发) - 校验与打包:verify:classisland(需后端)、verify:classisland-plugin(静态契约 54 项)、build/dist:classisland-plugin 产出 .cipx - 文档:README 与 AGENTS.md 同步新增联动说明,docs/screenshots/classisland 留三张真机截图 门禁:pnpm lint 0 error、pnpm typecheck 四包通过、verify:classisland-plugin 54/54 通过。运行期仍需在装了 ClassIsland 的机器上实测(步骤见 AGENTS.md §8)。
This commit is contained in:
1 parent
fe41ebb7d6
commit
d0830f6830
61 files changed
+7026
-93
No files matched your search
+58
@@ -0,0 +1,58 @@
|
||||
-- ClassIsland 联动:设备接入 + 提醒下发
|
||||
--
|
||||
-- 说明:
|
||||
-- 1) IntegrationDevice 保存「班级 ↔ 一台装有联动插件的机器」的绑定关系,
|
||||
-- 令牌只存 sha256 哈希,明文仅在创建/重置接口返回一次。
|
||||
-- 2) 同一班级内 deviceKey 唯一:插件重装后带同一机器码会重绑原记录,避免设备列表堆重复项。
|
||||
-- 3) ClassIslandPush 落库是为了「设备离线时通知不丢」:插件重连后拉取未确认的一条立即弹出。
|
||||
-- 4) 本文件重复执行是安全的(逐条执行、忽略"已存在"错误)。
|
||||
|
||||
CREATE TABLE "IntegrationDevice" (
|
||||
"id" TEXT NOT NULL PRIMARY KEY,
|
||||
"classId" TEXT NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"deviceKey" TEXT NOT NULL,
|
||||
"mode" TEXT NOT NULL DEFAULT 'plugin',
|
||||
"enabled" BOOLEAN NOT NULL DEFAULT true,
|
||||
"tokenHash" TEXT NOT NULL,
|
||||
"tokenHint" TEXT NOT NULL,
|
||||
"syncScheduleToServer" BOOLEAN NOT NULL DEFAULT true,
|
||||
"mirrorScheduleToClassIsland" BOOLEAN NOT NULL DEFAULT false,
|
||||
"classIslandVersion" TEXT,
|
||||
"pluginVersion" TEXT,
|
||||
"lastSeenAt" DATETIME,
|
||||
"classPlanLoaded" BOOLEAN NOT NULL DEFAULT false,
|
||||
"currentSubject" TEXT,
|
||||
"currentTimeState" TEXT,
|
||||
"currentPeriodStart" TEXT,
|
||||
"currentPeriodEnd" TEXT,
|
||||
"nextSubject" TEXT,
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" DATETIME NOT NULL,
|
||||
CONSTRAINT "IntegrationDevice_classId_fkey" FOREIGN KEY ("classId") REFERENCES "Class" ("id") ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX "IntegrationDevice_classId_deviceKey_key" ON "IntegrationDevice"("classId", "deviceKey");
|
||||
CREATE INDEX "IntegrationDevice_classId_idx" ON "IntegrationDevice"("classId");
|
||||
CREATE INDEX "IntegrationDevice_tokenHash_idx" ON "IntegrationDevice"("tokenHash");
|
||||
|
||||
CREATE TABLE "ClassIslandPush" (
|
||||
"id" TEXT NOT NULL PRIMARY KEY,
|
||||
"classId" TEXT NOT NULL,
|
||||
"deviceId" TEXT,
|
||||
"title" TEXT NOT NULL,
|
||||
"content" TEXT NOT NULL,
|
||||
"durationSeconds" INTEGER NOT NULL DEFAULT 8,
|
||||
"speechContent" TEXT,
|
||||
"urgent" BOOLEAN NOT NULL DEFAULT false,
|
||||
"createdBy" TEXT,
|
||||
"notificationId" TEXT,
|
||||
"ackedAt" DATETIME,
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"expiresAt" DATETIME NOT NULL,
|
||||
CONSTRAINT "ClassIslandPush_classId_fkey" FOREIGN KEY ("classId") REFERENCES "Class" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
CONSTRAINT "ClassIslandPush_deviceId_fkey" FOREIGN KEY ("deviceId") REFERENCES "IntegrationDevice" ("id") ON DELETE SET NULL ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
CREATE INDEX "ClassIslandPush_classId_createdAt_idx" ON "ClassIslandPush"("classId", "createdAt");
|
||||
CREATE INDEX "ClassIslandPush_deviceId_ackedAt_idx" ON "ClassIslandPush"("deviceId", "ackedAt");
|
||||
Reference in new issue
Block a user