工单中心系统(Work Order Center)
当前版本:1.2.79
景区慢速四轮车现场运维工单的统一接入、分派、跟踪与考核中枢。方案文档见 工单中心系统设计方案.md。
版本记录
| 版本 | 内容 |
|---|---|
| 1.2.79 | .env 新增内置兜底解析:未安装 phpdotenv 时也能读取(系统环境变量优先);CLI 脚本(migrate/诊断)统一使用同一加载器 |
| 1.2.78 | 新增 .env 诊断工具与启动告警(缺少 phpdotenv/文件不可读时提示),README 补充 Linux 部署排查清单 |
| 1.2.77 | 后台退出登录增加二次确认(取消留在当前页,确认后退出) |
| 1.2.76 | 新建工单所属景点改为必填(前端校验 + 后端 422);建单成功后自动跳转该工单详情页 |
| 1.2.75 | 列表/详情输出所属景点(不再显示位置);待处理/已驳回工单可编辑(含扩展字段,记录编辑时间轴);新建/编辑成功右上角提示;派单按景点匹配 |
| 1.2.74 | 新建工单「所属景点」改为可搜索单选下拉(数据来自景点管理,输入关键字过滤,点击选择,单选替换) |
| 1.2.73 | 重新应用新建工单景点数据源改动(/api/stations 启用景点全量;修复 /admin/stations 无分页参数只返回 1 条) |
| 1.2.72 | 修复后台多页面无数据:恢复 v1.2.65 重写派单弹窗时误删的 19 个函数(人员/景点/扩展登录/系统配置/权限节点及分页组件) |
| 1.2.71 | (已回滚)新建工单景点数据源调整 |
| 1.2.70 | 详情页操作区显示当前处理人「已派给X ✓」灰色只读按钮;派单/改派成功提示带 ✓,详情自动刷新展示当前处理人 |
| 1.2.69 | 选择接单人弹窗比例调整:宽度 800px、列表最多显示约 3 行(内部滚动),整体更宽扁,接近参考稿比例 |
| 1.2.68 | 「前往派单/改派」选择接单人按设计稿重做:候选人卡片(头像/状态/景点/标签/推荐分)、可派人数与排序说明、底部「取消 / 派给X」;抢单池/转人工指派先输出待实现提示 |
| 1.2.67 | 测试工单种子支持 --append 追加批次;当前已追加一批(测试工单共 66 条) |
| 1.2.66 | 修复详情页「前往派单/重新派单/改派」无反应:恢复重做弹窗时误删的 assignModal 选人流程 |
| 1.2.65 | 派单/改派二次确认按设计稿重做:人员卡片(头像/状态/景点/推荐分)、技能匹配/在手单/上月 SLA 指标、工单摘要与剩余时限、推荐说明,底部「暂不派单/确认派单」 |
| 1.2.64 | 修复工作台待派工单列表少于统计数:改为服务端按 created+rejected 查询,不再受「最新 50 条」截断 |
| 1.2.63 | 工作台「待派工单」口径与侧栏列表计数一致:待受理 + 已驳回(dispatch_count) |
| 1.2.62 | 管理员工作台「待处理工单」改为「待派工单」,口径为待受理(尚未派单);列表只展示待派工单 |
| 1.2.61 | 管理员工作台「待处理工单」改为待受理+已派单数量(不再包含处理中/挂起),列表只展示待处理工单 |
| 1.2.60 | 工作台按角色区分:运维人员显示个人指标(待接单/进行中/今日完成/超时临期),隐藏「运维人员工单分布」,卡片标题改为个人视角;管理角色保持全局视图 |
| 1.2.59 | 首页「各类型分布」进度条按工单类型区分颜色(11 种类型各有独立色) |
| 1.2.58 | SLA 进度条前增加「响应 / 完成」名称标签,便于区分两条进度含义 |
| 1.2.57 | 工单列表 SLA 改为两条进度条(响应/完成),按已用时长占时限比例填充,超时红色、临期橙色,数值在悬停提示中 |
| 1.2.56 | 工单列表 SLA 列改为「响应 / 完成」:已接单显示响应耗时与已用时间,已完成/已关闭显示响应与完成耗时 |
| 1.2.55 | 侧栏「全部工单」计数改为仅统计待受理+已驳回;各类型入口增加同口径计数(新增 /api/work-orders/nav-counts) |
| 1.2.54 | 后台「退出」按钮增加退出图标 |
| 1.2.53 | 点击工单消息提示打开详情时,同时将该条消息标记为已读并更新红点/未读数(后台与 H5) |
| 1.2.52 | 工单类消息的实时提示(右上角 toast / H5 底部提示)支持点击直接打开工单详情 |
| 1.2.51 | 响应时限改为以「接单」计算:派单后仍显示等待,运维接单(开始处理)才记为已响应 |
| 1.2.50 | 工单详情 SLA 显示耗时:响应/完成行显示已用时长与时限,已完成显示总耗时,并展示计时起点 |
| 1.2.49 | 后台接单增加二次确认;新增各类型测试工单种子脚本(33 条,覆盖 8 种状态) |
| 1.2.48 | 已驳回工单仅管理员可标记完成/关闭(前端按钮与后端接口双重限制);修复 HttpOnly 会话下前端无法建立通知 WebSocket 的问题(改为服务端从握手 Cookie 鉴权) |
| 1.2.47 | 后台状态操作成功改为右上角弱提示(接单/挂起/恢复/完成/驳回/派单/改派/关闭/取消),不再弹窗打断 |
| 1.2.46 | 顶栏消息入口改为铃铛(红点/未读数在铃铛上,点击弹出消息列表);「刷新权限」从顶栏移至权限节点管理页 |
| 1.2.45 | 新增用户消息中心:新派单/SLA 临期通知,后台与 H5 未读红点与数量、消息列表、实时提示音(系统配置可定制,默认「叮」) |
| 1.2.44 | 新增 .env 配置:默认配置集中到项目根目录 .env(含 .env.example 模板),引入 phpdotenv 由 webman 启动时加载;景点导入地址改为 SCENIC_IMPORT_URL |
| 1.2.43 | 类型子入口的折叠箭头按状态显示:收起时向上、展开时向下 |
| 1.2.42 | 「全部工单」下的类型子入口最多显示 5 个,超出可滚动;新增「展开全部(N)/ 收起」按钮,展开状态记忆在本地 |
| 1.2.41 | 侧栏新增「集成」分组:飞书集成、扩展登录从「管理」移入,管理组保留人员/景点/工单类型/权限节点/系统配置 |
| 1.2.40 | 通知 WebSocket 地址改为配置:新增 /api/client-config 下发 notify_ws_url/notify_ws_port,前端不再写死 8789;内部 UDP 地址也可配置(WOC_NOTIFY_*) |
| 1.2.39 | 远程仓库迁移至 gitee.com/explorer-software/tickethub |
| 1.2.38 | 移除代码中写死的本机地址:新增 app.url 配置(APP_URL 环境变量,默认按端口推导);OAuth 回调地址与数据初始化改为读取配置,后台自定义值仍优先 |
| 1.2.37 | 调整「API 生成工单」入口位置:从工作台与侧栏移除,改为首页 / 入口卡片(/console/openapi 页面保留可直达);恢复侧栏「派单」组原状 |
| 1.2.36 | 新增「API 请求生成工单」页面(/console/openapi):工作台与侧栏入口,可调试开放接口(创建/查询/更新状态),Bearer Token、JSON 请求体、请求预览、响应回显与 cURL 复制 |
| 1.2.35 | 扩展登录(OAuth2)页面去掉表单区内部滚动条:移除 max-height/overflow,内容按原有长度完整展开 |
| 1.2.34 | 后台列表统一分页(每页 20):人员管理改为服务端分页;权限节点、工单类型、SLA 配置、事件映射接入分页条;超过 20 条才显示翻页 |
| 1.2.33 | 恢复姓名同步:本地姓名随认证中心 nickname 更新(与用户名/手机号/邮箱/角色一同比对,有变化才写库) |
| 1.2.32 | OAuth 资料同步不再用昵称(nickname)覆盖本地姓名:仅同步用户名/手机号/邮箱/角色,姓名以工单系统为准(新建账号时昵称仅作初始姓名) |
| 1.2.31 | OAuth 资料同步扩展到用户名/手机号/邮箱:登录与刷新权限时比对并更新(有变化才写库),新增 users.email;用户名冲突时保留本地值 |
| 1.2.30 | 权限节点页统计改为一行小字说明(无卡片边框),内容:节点数 · 来源 · 导入方式 |
| 1.2.29 | 权限节点页统计仅保留「本地节点数」,移除顶层/类型/导入方式与 OAuth 状态相关内容 |
| 1.2.28 | 移除对 OAuth 数据库的直连:删除权限同步服务/接口与人员 OAuth 绑定查询;权限节点改由导出 JSON 在 OAuth 后台导入;OAuth 登录按登录名自动关联本地账号 |
| 1.2.27 | OAuth 登录改为 upsert:本地不存在则新增,存在则按认证中心更新姓名/手机号/角色(不覆盖本地登录名、密码、景点) |
| 1.2.26 | 修复 OAuth 首次登录自动建档时 users.username 唯一约束冲突(冲突时自动生成唯一登录名) |
| 1.2.25 | 待派队列增加父级目录 dispatch(派单管理),dispatch:view 挂到其下;同步 OAuth(30 节点) |
| 1.2.24 | 权限节点支持导出 JSON(嵌套树,可直接导入 OAuth 后台权限导入);权限节点页新增「下载 JSON」 |
| 1.2.23 | 人员列表优化:飞书 open_id 改为「查看 / 复制」(不再整列输出),景点列加宽至可完整展示名称 |
| 1.2.22 | 后台响应式修复:折叠态下窄屏仍两列、顶栏元素溢出被裁;新增 1100/860/560 三档断点与表格横向滚动 |
| 1.2.21 | 修复景点下拉点击无响应:.ts 容器的 overflow 裁剪了绝对定位的下拉列表 |
| 1.2.20 | 景点多选增加下拉按钮(一键展开全部);新增/编辑人员保存后不再弹提示,直接刷新页面 |
| 1.2.19 | 负责景点改为「可搜索标签式多选」:搜索→选中→标签回显,可连续选择、点 × 移除;修复浏览器自动填充污染人员表单 |
| 1.2.18 | 人员管理支持多景点(景点下拉多选、联动景点管理)与登录方式提示修正;新增 user_stations 关联表,派单按多景点匹配 |
| 1.2.17 | 登录支持本地账号(OAuth 改为可选):登录页方式切换、人员管理设置登录名/本地密码、密码哈希隐藏;auto 模式先本地后 OAuth |
| 1.2.16 | 修复登录页 logo 残留「+」:login.html 引用的 console.css 版本号过期导致命中旧缓存 |
| 1.2.15 | 品牌标识更新:工单纸+勾选新 logo,品牌名「工单中心」,覆盖后台顶栏 / 登录页 / H5 登录页 |
| 1.2.14 | 左侧「工单」类型子菜单补充语义图标(维修=扳手、挪车=箭头、充电=闪电、遇困=警示、采购=购物车等 11 个) |
| 1.2.13 | 恢复误删的 User::workOrders 关联,修复「前往派单/改派」候选加载失败(withCount 依赖该关联) |
| 1.2.12 | 代码清理:删除未使用的 import、JS 函数与死常量(如 WorkOrder::STATUSES、User::workOrders 等),消除冗余 |
| 1.2.11 | 新增「未来功能计划」文档(操作审计日志、飞书推送日志与查漏补缺清单、阶段拆解),接入文档导航 |
| 1.2.10 | 首页视觉优化:标题与入口整体居中、入口图标放大(58px)、卡片留白/圆角/阴影更正式 |
| 1.2.9 | 首页仅保留四个入口(文档 / 后台 / 测试 / H5),移除快速建单演示、接口清单、快速上手等页内内容与相关脚本样式 |
| 1.2.8 | 首页改版:文档 / 后台 / 测试 / H5 四个入口卡片;H5 入口以手机外框 + iframe 模拟移动端,可直接操作 |
| 1.2.7 | 修复权限节点页「刷新」与工单类型页「查询/筛选」点击无反应(IIFE 内函数未挂到 window) |
| 1.2.6 | 权限节点管理页「类型」列改为中文标识(目录 / 菜单 / 按钮) |
| 1.2.5 | 权限节点层级修正:workorder:list 改为目录(dir),按类型的列表节点挂到其下(workorder:list:{type});权限节点页支持多级缩进 |
| 1.2.4 | 新增后台「权限节点管理」页:查看注册表节点与 OAuth 侧状态(一致/差异/缺失)、一键推送;新增权限码 admin:permissions |
| 1.2.3 | H5 动作后返回对应列表(完成→已完成;接单/挂起/恢复/驳回→进行中),不再停留在详情页 |
| 1.2.2 | H5 动作(接单/挂起/恢复/完成/驳回/取消接单)后立即刷新顶部 Tab 统计,无需手动刷新页面 |
| 1.2.1 | 卡片链接票据有效期改为可配置(飞书集成页新增「卡片链接有效期(小时)」1–168,默认 24) |
| 1.2.0 | H5 链接免登:飞书卡片带 24h 一次性票据直达工单详情;票据过期/已用/已转派回落登录并自动跳回;新增权限快照表(OAuth 登录时写入,免登时读取) |
| 1.1.9 | 工单详情操作按钮按「本人」收敛:接单仅对派单对象显示(后台),H5 底部操作栏仅本人的工单显示 |
| 1.1.8 | 新增 40 条随机演示工单(10 类型 / 6 状态 / 4 来源 / 扩展字段与时间轴完整);修复 SLA 截止时间格式化导致列表出现「NaN」的缺陷 |
| 1.1.7 | 修复详情页返回列表无响应;接单仅限派单对象;派单/改派二次确认与即时提示;飞书推送改异步(WebSocket 右上角结果通知) |
| 1.1.6 | 飞书工单卡片改版:按优先级配色、展开工单详情与问题描述、字段单行两列排布、分隔线与脚注、接单/查看按钮分组、SLA 文案紧凑化 |
| 1.1.5 | 人员管理列表新增手机号列(便于核对飞书手机号绑定) |
| 1.1.4 | 飞书集成:后台配置页(凭证/单聊/匹配字段/H5 地址/卡片/通知开关/事件回调)、真实消息发送(tenant_access_token 缓存 + 卡片)、账号按手机号批量绑定 open_id、卡片「接单」事件回调(幂等 + 操作人校验 + AES 解密) |
| 1.1.3 | H5 接单端新增「工作台」个人概览页(今日/进行中/待接单/超时临期 KPI、本月 SLA 达成率、近 7 日完单趋势、需关注超时任务、快捷入口) |
| 1.1.2 | 侧栏图标 + 工单列表条数气泡、状态/处理人下拉筛选、工作台运维工单分布、详情页整页滚动、时长格式 Xh Ym、导航分组空则隐藏、工单类型可配置(独立管理入口 + 按类型列表入口 + 权限节点同步 oauth)、列表类型路径化 /console/work-orders/{type}、类型级权限节点 workorder:list:{type}、OAuth 接口调试页、侧栏收起(仅留图标) |
| 1.1.1 | 已驳回工单可重新派单/标记完成/关闭、关闭二次确认 |
| 1.0.1 | H5 接单端、SLA 超时处理等基础能力 |
提交约定:每次改动完成后提交一个 git 版本;提交说明以当次需求原文为参考,整理为规范描述(一行摘要 + 要点列表,说明改了什么、影响范围),并同步更新上方「当前版本」与「版本记录」;版本号按补丁位递增(1.2.79 → 1.2.80 → …)。提交后推送到远端仓库 origin(git@gitee.com:explorer-software/tickethub.git,main 分支)。
首页入口
/ 为系统入口页,仅保留四个入口卡片(无其他内容):
| 入口 | 说明 |
|---|---|
| 文档 | /docs/plan(方案 / 状态与流程 / OAuth 对接 / 使用说明) |
| 后台 | /console(客服工作台、工单列表、派单、管理与配置,需登录) |
| 测试 | /console/oauth/debug(OAuth 接口调试;同页可继续进入飞书测试、权限节点等,需登录) |
| H5 接单端 | 打开手机样式模拟器:390×800 手机外框内嵌 /m,同源可直接登录与操作;支持刷新、关闭(Esc / 点遮罩) |
响应式
- 断点:
≤1100px品牌区自适应、搜索框收窄;≤860px单列布局(含折叠态)、隐藏侧栏/搜索框/次要按钮、页面留白收紧、宽表格横向滚动;≤560px隐藏品牌副标题与用户角色说明 - 后台表格在窄屏通过内部滚动容器横向滚动,页面本身不产生横向滚动条
- 首页:
≤760px入口两列;登录页卡片max-width:380px
技术栈
- PHP 8.1+ / webman 2.x(与公司 oauth 项目同栈)
- Eloquent ORM(illuminate/database 10)
- 数据库:默认 SQLite(零配置演示),生产 MySQL(环境变量切换)
快速启动
# 1. 安装依赖(需 PHP 8.1+)
composer install
# 2. 环境配置(首次)
cp .env.example .env
# 按实际环境修改 .env(端口 / 数据库 / OAuth / 通知 WebSocket 等)
# 3. 建表 + 默认数据(站点/示例用户/SLA配置/事件映射)
php tools/migrate.php
# 可选:生成各类型测试工单(11 种类型 × 3 条,覆盖待受理/待接单/处理中/挂起/已完成/已关闭/驳回/取消)
php tools/seed_test_orders.php
# 已有测试数据时再追加一批
php tools/seed_test_orders.php --append
# 4. 启动服务(默认 8787 端口)
php start.php start
配置集中在项目根目录 .env(webman 启动时自动加载;系统环境变量优先,.env 不会覆盖已存在的变量):
| 变量 | 默认值 | 说明 |
|---|---|---|
WOC_LISTEN_PORT |
8787 |
HTTP 监听端口 |
APP_URL |
http://127.0.0.1:8787 |
系统对外地址(OAuth 回调、对外链接) |
DB_DRIVER |
sqlite |
sqlite / mysql |
DB_HOST / DB_PORT / DB_DATABASE / DB_USERNAME / DB_PASSWORD |
— | MySQL 连接(DB_DRIVER=mysql 时生效) |
OAUTH_BASE_URL |
http://127.0.0.1:8788 |
认证中心地址 |
OAUTH_CLIENT_ID / OAUTH_CLIENT_SECRET |
woc / — |
OAuth 客户端凭据 |
WOC_NOTIFY_WS_URL |
空 | 前端通知 WS 地址(完整 ws:///wss:// 或路径,留空自动推导) |
WOC_NOTIFY_WS_PORT |
8789 |
通知 WS 端口 |
WOC_NOTIFY_UDP_HOST / WOC_NOTIFY_UDP_PORT |
127.0.0.1 / 8790 |
内部通知投递地址 |
SCENIC_IMPORT_URL |
内置地址 | 景点导入接口 |
SLA_WARN_MINUTES |
30 |
SLA 临期提醒阈值(分钟),剩余不足时通知处理人一次 |
.env 已被 .gitignore 忽略,不会提交;仓库内提供 .env.example 作为模板。
生产环境切 MySQL:编辑 .env 设置 DB_DRIVER=mysql 及连接信息后执行迁移与启动。
Linux 服务器 .env 不生效排查
webman 仅在 已安装 phpdotenv 且 .env 存在 时加载配置,否则静默使用默认值。按顺序检查:
php tools/check_env.php # 一键诊断:文件/权限/编码/依赖/解析结果
常见原因:
.env没上传:.env在.gitignore中,git pull不会包含,需单独scp或复制.env.example后修改。- 没执行
composer install:缺少vlucas/phpdotenv。现已内置兜底解析,.env仍会生效(建议仍执行composer install以获得完整语法支持)。 - 文件权限/所有者:运行 PHP 的用户(www-data/nginx 等)需可读
.env(建议chown后chmod 640)。 - 系统环境变量优先:webman 使用 immutable 模式,
.env不会覆盖已存在的系统环境变量(systemdEnvironment=、dockerenv_file、export)。排查:ps eww <worker-pid>或查看 systemd/docker 配置。 - 未重启:环境文件在进程启动时读取,修改后必须
php start.php restart -d。 - BOM / CRLF:Windows 编辑保存可能带 BOM 导致第一行失效,另存为 UTF-8(无 BOM)+ LF。
- 路径不对:
.env必须与start.php同目录(webmanrun_path())。
工单状态机
完整状态与流程说明见 docs/状态与流程.md。核心:
created → assigned → processing → pending → completed → closed
created → cancelled
assigned/processing/pending → rejected → assigned(重新派单)/ completed(标记完成)/ closed(关闭)
assigned/processing/pending → created(取消接单,退回待受理)
- 转派不改状态,仅更换处理人并留痕;改派仅限「完成时限已超时」的工单,改派后 SLA 重新计时;
- 关闭后复发:生成新工单并关联
parent_order_id; - 每次流转写入
work_order_status_log(含操作人、备注、时间)。
登录与权限(本地账号 + OAuth 可选)
登录支持两种方式,OAuth 为可选:
| 方式 | 说明 |
|---|---|
| 本地账号(默认) | 人员管理中创建的账号(登录名 + 本地密码,bcrypt 存储);权限来自权限快照,缺省按角色兜底(DefaultPermissions) |
| OAuth 统一认证 | 公司认证中心账号(默认 http://127.0.0.1:8788);登录时拉取 RBAC 权限码与角色 |
- 登录页提供「本地账号 / OAuth 统一登录」切换;未传
mode时按auto处理:本地命中且已设密码则本地登录,否则回落 OAuth - 接口:
POST /login参数username/password/mode(auto|local|oauth) - 本地账号在「人员管理」中维护:登录名与本地密码(留空不修改,至少 6 位);绑定 OAuth 时登录名自动与认证中心账号一致
- 密码哈希不会随接口返回(
User模型已隐藏password)
注意:本地密码错误时不会回落 OAuth(避免掩盖错误);本地账号未设密码时 mode=local 会明确提示,「auto」则回落 OAuth。
OAuth 登录细节
- 本地档案采用 upsert:不存在则新增(登录名冲突自动追加 oauth id),存在则用 OAuth 资料更新 姓名(取 nickname)/用户名/手机号/邮箱/角色;本地密码、景点归属不被覆盖
- 同步时机:每次 OAuth 登录 以及 每次「刷新权限」(
/api/refresh-permissions)都会比对认证中心资料,有变化才写库 - 用户名受本地唯一约束:若认证中心的用户名已被其他本地账号占用,则保留本地值并记日志,其余字段照常更新;空值不覆盖本地已有值
- 打开
/console未登录 → 302/login - OAuth 模式提交账号密码 → 后端调 oauth
passwordgrant 换令牌 → 取user-permissions(RBAC 权限码)→ 查本地users.role→ 写 Session - RBAC 权限码校验:登录时经
/user-permissions缓存权限码到 Session,AuthMiddleware按路由前缀/动作映射到权限码拦截(详见 oauth 后台 woc 应用的权限节点树与角色分配) - 令牌临近过期(剩 10 分钟)自动
refresh_token刷新,并重新拉取权限码(权限变更即时生效) - 演示账号:
demo/123456(客服 cs)、wocadmin/admin123(运营管理员 admin) - oauth client:
woc/ secret79220b54d91d7753af40f2de4f7cbf289ab2c49375083a3a(在认证中心后台注册,正式环境改环境变量OAUTH_BASE_URL/OAUTH_CLIENT_ID/OAUTH_CLIENT_SECRET)
权限码清单(woc client,oauth 后台可调整):
| 权限码 | 覆盖 |
|---|---|
dashboard |
工作台 |
workorder:list |
列表/详情/搜索 |
workorder:create |
新建工单 |
workorder:assign |
分派/转派/分派建议 |
workorder:process |
接单/挂起/恢复/完成/驳回/评论/附件 |
workorder:close |
关闭工单 |
workorder:cancel |
取消工单 |
dispatch:view |
待派单队列 |
admin:sla / admin:users / admin:stations / admin:events / admin:stats |
系统配置各模块 |
内置角色:admin(全部)、cs_leader(客服主管)、cs(客服)、ops(现场运维);在 oauth 后台为 woc 应用维护权限树/角色/用户分配。
后台控制台(需登录)
浏览器打开 http://<host>:8787/console(默认端口 8787,基于 TicketHub 原型设计系统改造)。
- 消息中心:顶部铃铛显示未读红点与数量;点击铃铛查看消息列表并标记已读。新派单、SLA 临期通过 WebSocket 实时推送并播放提示音
- 按视图区分路由(后续可逐路由挂 oauth 权限节点):
/console工作台/console/work-orders工单列表(表格/看板、类型 chips + 状态/处理人/优先级下拉 + 「仅看超时」+ 搜索、分页)/console/work-orders/{type}按类型列表(类型路径段,如/console/work-orders/repair)/console/work-orders/new新建工单/console/work-orders/{id}工单详情/console/dispatch待派单队列(类型筛选、双击弹详情)/console/scenics景点管理(增删改查 + 外部接口导入)/console/users人员管理(增删改查 + 本地登录名/密码 + 负责景点可搜索标签多选 + 搜索/角色筛选 + 分页(每页 20);OAuth 登录按登录名自动关联)/console/types工单类型管理(类型增删改查、启停、排序、扩展字段模板、分页)/console/feishu飞书集成(应用凭证、单聊通知、H5 深链、账号批量绑定、事件回调)/console/permissions权限节点管理(节点清单、层级展示、分页、导出 JSON)/console/oauth扩展登录(OAuth2 对接参数配置)/console/admin系统配置(SLA / 事件映射 / 报表)- 左侧「工单」组按类型列出入口:
全部工单+ 各启用类型(点击进入列表并按该类型筛选,URL 形如/console/work-orders/repair,与类型 chips 双向联动;兼容旧的?type=写法,未知类型自动退化为全部);类型由后台配置驱动,新增/停用后侧栏即时联动 - 类型列表采用路径段而非查询参数,便于后续按类型挂权限节点(
AuthMiddleware::ROUTE_PERMISSIONS按最长前缀匹配,可直接追加/console/work-orders/move => workorder:type:move之类映射);数字段仍为工单详情/console/work-orders/{id},非数字段为类型列表 - 分组显隐:任一分组内已无可见菜单项时,整组(含分组标题)自动隐藏
- 侧栏可收起:顶栏左侧按钮折叠侧栏(212px → 60px),收起后仅保留图标(文字、计数气泡、类型子入口隐藏,图标放大至 16px 并居中,悬停显示名称提示);状态存
localStorage刷新后保持;快捷键Ctrl/Cmd + \切换;主内容区自动占满剩余宽度;移动端断点下侧栏仍整体隐藏
景点管理(独立页)
- CRUD:
POST /admin/stations(新建/更新)、DELETE /admin/stations/{id}(删除)、GET /admin/stations(列表,支持keyword/status/page/page_size) - 删除保护:景点已被工单或人员引用时,删除自动降级为「停用」,避免破坏历史关联
- 外部导入:
POST /admin/stations/import(需admin:stations)拉取iot2.txjer.com/.../scenicsimple并 upsert(按external_code幂等),已导入 169 个景区 - 权限:
admin:stations(仅 admin 角色,侧栏对该角色可见) - 模态弹窗:全部交互(提示/确认/输入/多字段表单/候选选择)走自定义 modal 组件,无原生 alert/prompt/confirm
- 附件上传:工单详情页支持图片/文件/视频上传与删除
POST /api/work-orders/{id}/attachments(multipart:file + kind=image/file/video)DELETE /api/work-orders/attachments/{attId}- 存储于
public/uploads/{order_id}/,扩展名/大小白名单校验(image≤10MB、video≤200MB、file≤50MB)
扩展登录(OAuth2 对接配置,独立页)
「扩展登录」为独立菜单页(/console/oauth,需 admin),参考常见管理后台的扩展登录设计,全部参数后台可配置(存 oauth_settings 表,替代硬编码):
| 字段 | 说明 |
|---|---|
| 启用 OAuth2 登录 | 开关;关闭后登录接口返回 403 |
| Callback URL | 工单中心回调地址(授权码模式备用) |
| 入口文字提示 | 登录页副标题文案 |
| Client ID / Client Secret | 应用凭据(密钥展示脱敏,留空保存不修改) |
| 协议 / OAuth 服务器 | http/https + host:port |
| Authorize / AccessToken / Resource / Userinfo 路径 | 各端点路径(默认 /authorize、/token、/token、/userinfo) |
| 权限节点路径(RBAC) | 默认 /user-permissions,登录时据此拉取角色与权限节点 |
| 登出回调地址 / Scope | 登录后 scope 声明 |
- 接口:
GET /admin/oauth-setting(读取,密钥脱敏)、POST /admin/oauth-setting(保存)、POST /admin/oauth-setting/test(连通性测试,client_credentials 实调) - 权限:
admin:sla(归属系统配置) OAuthClient全部从该配置构建 URL,缺省回退config('app.oauth');权限节点验证链路不变(登录拉权限码 → Session 缓存 →AuthMiddleware按路由/动作校验)
移动接单端(H5)
现场运维使用,浏览器打开 http://<host>:8787/m(手机访问同一地址)。与后台同一套 OAuth2 登录与权限(ops 角色:dashboard / workorder:list / workorder:process)。
- 消息提醒:右上角消息入口显示未读红点与数量;收到新工单/SLA 临期时实时提示并播放提示音(与后台一致)
- 工作台(默认首页):登录后首屏是个人概览(参考原型「接单工作台」的信息密度 + H5 接单端形态),数据来自
GET /api/workbench/mine(全部以当前登录人为处理人口径) - 身份卡:姓名 · 角色 · 所属景点
- 四宫格 KPI:今日完成(附本周)、进行中(含挂起)、待接单、超时 / 临期(有超时即高亮警示)
- 快捷入口:待接单 / 进行中 / 已完成(带数量,点击切 Tab)
- 本月表现:本月完单量、SLA 达成率(本月已完成工单中未晚于完成时限的占比,未设完成时限不计入;对比目标 95% 显示达标/未达标)
- 近 7 日完单趋势(柱状,今日高亮)
- 需要关注:未闭环且已超时或剩余不足 2 小时的任务,按紧急度排序(超时优先),点击直接进入任务详情
- 工作台不显示搜索栏(切回任务 Tab 自动恢复),页签吸顶位置随之调整
- 搜索:顶部搜索框,按工单号 / 标题 / 描述 / 位置 / 车辆号 / 景点号检索(300ms 防抖,输入即搜);搜索跨状态展示全部命中(并显示「命中 N 条」),清空或切换 Tab 即退出搜索态
- 我的任务:三个 Tab(待接单 / 进行中 / 已完成),按当前登录人(
assignee_id)过滤 - 任务详情:标准信息 + 按类型的扩展字段(Schema 标签展示)+ 现场凭证附件 + 流转记录
- 底部固定操作栏(按状态变化):接单 / 驳回(assigned)、取消接单 / 挂起 / 完成(processing)、取消接单 / 恢复处理(pending)
- 取消接单:工单退回「待受理」并清空处理人,等待客服重新派单;已上传的完工凭证保留;操作记录写入时间轴(含取消原因与原因处理人)
超时工单改派(改派后重新计时)
- 改派支持状态:
assigned/processing/pending(挂起) —— 超时或挂起中的工单均可改派 - 改派后 SLA 重新计时:新增
work_orders.sla_started_at(SLA 计时起点) - 分派 / 改派时写入当前时间,响应与完成时限自此重新计算(超时工单改派后即脱离超时)
- 未设置时回退用
created_at(历史工单口径不变) - 列表 SLA 列、详情 SLA 条、工作台超时统计、
overdue=1筛选、SLA 超时提醒进程均以该起点计算 - 时间轴记录「转派: 原处理人 → 新处理人(SLA 重新计时)」
- 改派仅限「完成时限已超时」的工单(避免正常处理中被随意转派):
- 超时(
assigned/processing/pending):改派按钮可用 → 选人改派,SLA 重新计时 - 未超时:按钮置灰(
title提示「工单未超出完成时限,不能改派」),点击提示「工单未超出完成时限,无需改派。如需换人,请等待超时或由运维驳回后重新派单。」 - 后端强制校验(
WorkOrderService::reassign返回「工单未超出完成时限,不能改派」),防止绕过前端 - 派单 / 改派自动分流(后台):待受理工单走「分派」、已超时的已派单/处理中/挂起工单走「改派」,弹窗标题与提示按场景区分,改派同样触发 SLA 重新计时并通知飞书
- 列表「仅看超时」筛选:
GET /api/work-orders?overdue=1(未关闭且超出完成时限,按 SQLite/MySQL 分别计算),列表工具条勾选即筛出超时单 - 超时工单在列表/看板 SLA 列以红色「已超时」标注,便于定位并改派给其他运维
- 完单回填:按类型 Schema 渲染"接单人后补"字段(如充电结果、诊断结论、升级结果)
- 附件按阶段区分(
work_order_attachments.stage):提单凭证(report,报单时上传的问题照片,只读)与完工凭证(fix,接单后上传的修复结果) - 待接单:只显示提单凭证(不显示完工凭证区块)
- 进行中 / 已挂起:显示完工凭证,可上传
- 已完成 / 已关闭:显示完工凭证,只读
- 现场取证:接单后
+拍照/上传追加到完工凭证(图片/视频/文件,静默上传后即时刷新) - 登录接口与后台一致(
POST /login),未登录自动显示登录表单 - 登录页(供飞书内打开使用):
- 未登录自动展示;账号/密码输入(移动端大号输入框、自动聚焦)
- 记住账号(localStorage,只存用户名不存密码),下次自动填充并聚焦密码框
- 飞书环境识别:UA 含 Lark/Feishu 时显示「检测到飞书环境 · 请使用工单中心账号登录」提示
- 友好错误提示(账号密码错误 / 账号未开通 / 认证服务不可用 / 网络异常)
- 顶部用户名可点击退出登录(飞书内切换账号)
- 演示账号:
wocops / ops123456(现场运维 李四)、demo / 123456(客服)
- 流转时间轴记录操作人:每次状态流转写入
work_order_status_log(from/to、operator_type、operator_id、备注、时间),详情/弹窗/H5 时间轴显示操作人姓名(如「客服小李」「运维李四」),系统自动建单显示「系统」,外部系统回调显示「外部系统」 - 分派记录派给谁:
待受理 → 已派单 · 客服小李 · 客服分派给 运维张三 - 改派记录双方:
已派单 → 已派单 · 客服小李 · 转派:运维张三 → 运维李四(SLA 重新计时) - 取消接单记录原因:
处理中 → 待受理 · 运维李四 · 取消接单,退回待受理:不在现场
权限节点同步(每次登录 / 进入后台自动更新)
工单中心不缓存过期权限,每次登录都会重新拉取 oauth 权限节点,并在以下时机自动同步:
| 时机 | 行为 |
|---|---|
| 登录成功 | 调 /user-permissions 拉取角色 + 权限码 + 权限节点树,写入会话 |
每次进入后台页面(/console*)或前端初始化(/api/me) |
再次拉取,权限变更无需重新登录 |
| 令牌临期自动刷新 | 刷新令牌同时重拉权限 |
| 令牌已失效(被撤销/过期未续) | 拉取权限失败时自动用 refresh_token 续期并重试一次;refresh_token 也失效则登出回登录页 |
| 手动刷新 | POST /api/refresh-permissions(后台顶栏「刷新权限」按钮),无需重登 |
- 权限项数显示在顶栏(
角色 · 权限 N 项);/api/me返回permissions/permission_items(含中文标签)/permission_count/perm_synced_at - 导航菜单与操作按钮均按权限码显隐(不再依赖本地 role):
- 菜单:工作台
dashboard、工单列表workorder:list、新建工单workorder:create、待派队列dispatch:view、人员管理admin:users、景点管理admin:stations、工单类型admin:types、扩展登录与系统配置admin:sla;权限变更后刷新即联动 - 工单详情操作(状态 + 权限双重判断):前往派单/改派
workorder:assign、接单/挂起/完成/驳回/恢复workorder:process、关闭workorder:close、取消workorder:cancel、附件上传/删除workorder:create或workorder:process - 待派队列「派单给 XX」
workorder:assign;页面「新建工单」按钮workorder:create(HTML 用data-perm属性声明) - 「刷新权限」弹窗展示当前账号的权限节点明细(按「工单与派单 / 系统管理」分组,含中文名称),无权限时提示需在认证中心分配角色
- 拉取失败不阻断请求(沿用会话内已有权限),避免认证中心抖动影响使用
数据可见范围(现场运维隔离)
ops(现场运维)登录后台时仅可见指派给自己的工单:
- 工单列表:强制过滤
assignee_id = 本人(忽略传入的assignee_id参数,防越权查看他人) - 工单详情:非本人工单返回 403
- 工作台统计(
/api/dashboard):待处理数、超期数、状态/类型分布均限本人范围 - 其他角色(
cs/cs_leader/admin)不受此限制,可见全部工单
类型级可见范围(与角色无关,按权限码判定,见「工单列表的类型级权限节点」):无 workorder:list 时,列表、详情、工作台统计一律收敛到已授权的 workorder:list:{type} 类型并集;两者叠加时以更严格者为准(如现场运维 + 仅维修类型权限 → 只看自己的维修工单)。
首次登录自动建档(免手工绑定)
oauth 认证通过但本地 users 表无对应记录时,自动创建本地人员档案(无需管理员预绑定):
oauth_user_id/username/mobile从认证中心回传信息写入- 本地角色按 oauth 分配的角色自动映射(优先级
admin>cs_leader>cs>ops,无角色默认ops) - 姓名取 oauth 昵称(无则用户名);后续可在「人员管理」调整角色/景点/飞书 ID
- 幂等:同一账号再次登录不会重复建档;接口返回
is_new_user供前端提示 - 实际功能权限仍由 oauth 的 RBAC 权限节点决定(本地角色只影响菜单显隐与展示),无角色用户仅能进入工作台
飞书内打开的前置条件
- 可访问地址:飞书 WebView 需能访问该服务,
127.0.0.1仅本机可用;需以内网 IP 或域名暴露(如http://192.168.x.x:8787/m),或经网关/nginx 转发 - 链接形式:飞书群/机器人消息中发送
http(s)://<可访问地址>/m - 登录:飞书 WebView 无历史会话,首次打开即显示登录页,运维人员输入账号密码后进入任务列表
- 免登(可选,需飞书应用):若后续需要飞书免登,需在飞书开放平台创建自建应用 → 获取
app_id/app_secret→ H5 内走飞书 JSAPI 取code→ 后端换取open_id→ 与users.feishu_user_id匹配后签发会话(当前版本未实现,users.feishu_user_id字段已就绪)
工单类型管理(后台可配置)
类型不再硬编码,存于 work_order_types 表(后台「工单类型」页维护):
| 字段 | 说明 |
|---|---|
code |
类型编码,工单 type 存该值;创建后不可修改(新建时校验 ^[a-z][a-z0-9_]{1,49}$) |
name |
类型名称(侧栏入口 / 类型卡片 / 列表标签均取此值) |
sort |
排序(升序,决定侧栏与类型卡片顺序) |
enabled |
启用 / 停用(停用后不再出现在侧栏入口与新建表单;历史工单不受影响) |
fields |
扩展字段模板(JSON 数组,见下方 Schema 说明) |
- 接口:
GET /api/work-order-types(登录即可,前端渲染入口/下拉)、POST /admin/work-order-types(新建/更新,admin:types)、POST /admin/work-order-types/{id}/toggle、DELETE /admin/work-order-types/{id}(admin:types) - 删除保护:该类型下已有工单时拒绝删除,提示改用「停用」
- 配置驱动全链路:侧栏类型入口、列表类型 chips、新建页类型卡片、扩展字段表单、详情页字段标签、
typeName展示全部读同一份配置;自定义类型在新建页自动回退文案(无预置描述时用类型名称与「自定义类型」) - 多进程一致性:类型表体量极小,读取不做跨请求缓存(webman 各 worker 进程持有独立静态变量,缓存会导致「改了配置部分请求仍读旧值」)
- 前台改动类型配置后,前端会失效本地 schema 缓存,下次进入新建/详情页自动重取
- 权限节点:
admin:types(工单类型配置),侧栏「工单类型」与类型管理接口均按该权限码显隐
列表分页约定
后台所有列表统一每页 20 条:
- 后端分页接口:
GET ...?page=1&page_size=20,返回{items,total,page,page_size,pages}(如/admin/users、/admin/stations) - 前端分页条:
renderPagerBar(elId, meta, onPage);数据 ≤ 20 条时只显示「共 N 条」,超过才显示上一页/下一页 - 已接入:人员、工单类型、权限节点、景点、SLA 配置、事件映射、工单列表
权限节点管理(/console/permissions,权限 admin:permissions)
独立页面查看与维护权限节点:
- 页面顶部一行小字:
共 N 个权限节点 · 定义在本系统注册表 · 下载 JSON 后在 OAuth 后台导入 - 节点表:权限码(按父子缩进)、名称、类型(目录 / 菜单 / 按钮)、父节点、排序
- 本系统不直连认证中心数据库;节点维护流程为:本系统注册表 → 下载 JSON → OAuth 后台「权限导入」
- 下载 JSON:
GET /admin/permissions/export(嵌套树,保存在 OAuth 后台「权限导入」中导入) - 额外提示:认证中心存在但本系统未声明的节点(仅展示,不自动删除)
- 接口:
GET /admin/permissions/nodes、POST /admin/permissions/sync、GET /admin/permissions/export(嵌套树 JSON 下载)
oauth 角色层级(认证中心)
认证中心 oauth_roles.parent_id 构成角色树,本系统复用的 woc 接入方当前为:
| 角色 | code | 父角色 |
|---|---|---|
| 运营管理员 | admin |
— |
| 客服主管 | cs_leader |
— |
| 客服 | cs |
客服主管 |
| 现场运维 | ops |
运营管理员 |
- 认证中心按父角色向下继承权限:子角色自动获得父角色的权限码(例如运营管理员获得
admin:types后,现场运维也随之获得) - 若需「上级只给权限、但要控制子角色能否管理类型」,应在认证中心调整角色树或单独授权,而非在本系统内硬编码角色名
OAuth 接口调试(/console/oauth/debug,权限 admin:sla)
对接认证中心时的排查页(从「扩展登录」页顶部「接口调试」进入),回显原始 HTTP 状态 + 原始响应体 + 耗时(不经过客户端异常封装,失败时也能看到服务端返回内容):
- 取令牌方式:
账号密码(password grant)/直接填 access_token;可改目标路径(默认「权限节点」接口),可附加userinfo、单权限码校验(access_permissions) - 结果区:请求概览(状态码着色 / 耗时 / 请求体,密码与密钥脱敏)、结构解析、原始响应(可复制)
- 结构解析专为权限对接:列出顶层字段、角色的字段清单(并高亮是否含层级字段
parent_id等)、角色明细表、权限码标签、节点数 - 安全:仅允许以
/开头的相对路径(拒绝绝对 URL 与..穿越);密码/密钥/令牌回显均脱敏 - 实测返回(认证中心
woc接入方):user-permissions顶层字段为client_id, scope, token_type, expires_at, user, store, scenics, roles, permissions, nodes;roles[].parent_id已存在(如现场运维ops→parent_id: 4= 运营管理员),可直接用于角色树展开
工单类型字段模板(差异化录入)
不同工单类型录入格式不同,由后端 Schema 驱动(app/service/WorkOrderSchema.php):
GET /api/work-order-schema返回各类型字段定义(key / label / type / required / options / hint)- 新建工单:选类型后动态装载该类型字段(含必填标记
*)、右侧「派单预览」显示目标承接与 SLA 时限 - 提交校验:按类型校验必填项(如维修必填「故障现象/故障等级」、挪车必填「起始位置/目标位置/挪车方式」),通过后写入
work_order_ext.payload - 详情展示:扩展字段按 Schema 标签渲染(不再显示原始 key)
- 11 种类型字段模板:维修/挪车/充电/遇困/保养巡检/清洁/事故/基础设施/远程诊断/采购/售后
- 事件自动建单(
/internal/v1/events)与外部 API 建单(/open/v1)不受该必填约束影响
一键自动派单
POST /api/work-orders/auto-assign(需 workorder:assign):对全部(或指定 ids)待受理工单按「同景点 + 在单负荷」自动派给首位候选,返回成功/失败明细。
接口一览
客服工作台 /api
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /api/work-orders | 建单(type/priority/title 必填) |
| GET | /api/work-orders | 列表(status/type/priority/station_id/assignee_id 筛选,分页) |
| GET | /api/work-orders/{id} | 详情(含状态日志/评论/扩展字段/SLA) |
| POST | /api/work-orders/{id}/assign | 分派(assignee_id, cs_id) |
| POST | /api/work-orders/{id}/status | 运维端流转:start/pending/resume/complete/reject/reassign |
| POST | /api/work-orders/{id}/close | 客服关闭 |
| POST | /api/work-orders/{id}/cancel | 取消(创建人可取消;已分派需主管 is_supervisor=1) |
| POST | /api/work-orders/{id}/comment | 评论(飞书通知触达) |
| GET | /api/assign-suggestions | 分派建议(站点+负荷排序) |
车辆平台事件接入 /internal/v1(M2)
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /internal/v1/events | 事件推送自动建单;event_id 幂等;事件→工单映射见 event_configs |
外部系统开放 API /open/v1(M3)
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /open/v1/work-orders | 外部建单(type=procurement/after_sales;external_order_no+source_system 幂等) |
| GET | /open/v1/work-orders/{no} | 查询工单(Bearer token 鉴权,仅本系统工单) |
| PATCH | /open/v1/work-orders/{no}/status | 外部更新状态/进度 |
认证:Authorization: Bearer <token>,生产环境应复用公司 OAuth2 认证中心校验并映射 external_systems.code(当前为演示映射:factory_ 前缀 → vehicle_factory)。
管理后台 /admin
SLA 配置、人员(含 feishu_user_id)、站点、事件映射、基础报表、工单类型配置(/admin/work-order-types*)、权限节点导出(/admin/permissions/export)。
数据表
work_orders / work_order_status_log / work_order_ext(JSON) / work_order_attachments / work_order_comments / work_order_types / sla_config / event_configs / users / stations / external_systems
后台进程
webman:HTTP 服务(端口 8787)sla_checker:每分钟扫描 SLA 超时工单,飞书通知客服主管(可php tools/sla_check.php手动触发)monitor:文件变更热重载
飞书集成(单聊通知 + 卡片接单)
后台「飞书集成」页(/console/feishu,权限 admin:feishu)维护全部配置,保存即生效:
| 配置组 | 内容 |
|---|---|
| 凭证 | 启用开关、App ID、App Secret(密文存储、回显脱敏)、开放平台地址(默认 https://open.feishu.cn,可指向 mock 联调) |
| 接收 | 通知方式(单聊 open_id / 群 chat_id)、默认群 chat_id、账号匹配字段(手机号) |
| H5 | 对外地址(配置后卡片带「查看并处理」按钮,链接形如 {h5_base_url}?order={id};留空则卡片不带跳转) |
| 卡片 | 按钮文案;通知开关:新单分派 / 转派 / SLA 超时 / 评论 |
| 事件 | 接收卡片回调开关、Verification Token、Encrypt Key(配置后 AES 解密)、回调路径 |
发送链路
app/service/FeishuService.php:tenant_access_token获取(文件缓存,提前 5 分钟过期)→im/v1/messages(receive_id_type=open_id,msg_type=interactive)- 卡片内容:标题(如「【新工单】WO-…」)、类型 / 优先级 / 状态 / SLA 剩余、位置与车辆、可选备注;
assigned状态额外带「接单」按钮 - 未启用或凭证缺失时自动回退日志桩(
[Feishu-stub]),本地可验证触发链路,不影响工单流转 - 发送失败只记日志(
[Feishu] 发送失败 …),绝不阻断分派/流转 - 派单/改派不等待飞书:接口立即返回(实测 20ms 级),飞书推送在响应后异步执行;结果通过 WebSocket 提示在后台右上角
- 成功:「飞书已推送 · 工单已推送至 张三 · WO-…」
- 失败:「飞书推送失败 · 张三 未绑定飞书账号 · WO-…」(含明确原因)
用户消息中心
- 消息表
user_messages(user_id/type/title/body/order_id/is_read) - 触发时机:
- 新派单:通知接单人(类型
assign) - SLA 临期:完成时限剩余不足
SLA_WARN_MINUTES(默认 30 分钟)时通知处理人(类型sla_warn,每单每人只提醒一次) - 接口(登录后):
GET /api/messages(分页,含未读数)GET /api/messages/unread-countPOST /api/messages/{id}/readPOST /api/messages/read-all- 实时推送:写库后经
NotifyBus推送type=user_message,后台与 H5 同时更新红点并播放提示音 - 提示音:系统配置「消息通知」中可填写音频地址(http(s):// 或 / 开头的路径);留空使用内置「叮」提示音(WebAudio 双音)
后台实时通知(WebSocket)
- 独立进程
app/process/NotifyServer.php,默认监听 8789;前端建连后由服务端从握手 Cookie(PHPSID,含 HttpOnly)或?sid=识别用户,按用户精准下发 - HTTP 进程通过本机 UDP 8790(
NotifyBus)投递消息,WS 进程广播;未登录会话在握手时即被拒绝 - 前端连接地址不写死:读取
GET /api/client-config返回的notify_ws_url/notify_ws_port,按以下优先级生成连接地址
notify_ws_url:完整地址(ws:///wss://,可含路径)或路径(如/notify)→ 直接使用/同域拼接- HTTPS 页面:
wss://<当前域名>/notify - HTTP 页面:
ws://<当前主机>:<notify_ws_port>
- 可配置项:
WOC_NOTIFY_WS_URL:前端连接地址(生产建议配wss://例.com/notify,走网关)WOC_NOTIFY_WS_PORT:通知 WS 端口(默认 8789)WOC_NOTIFY_UDP_HOST/WOC_NOTIFY_UDP_PORT:内部 UDP 投递地址(默认127.0.0.1:8790)- 连接失败/端口不可达时前端静默重试(最长 15s 间隔),不影响任何业务操作
- 部署注意:HTTPS 页面必须走
wss(浏览器拦截混合内容)。前端在 https 下连接同源wss://<域名>/notify,需由网关反代:
location /notify {
proxy_pass http://127.0.0.1:8789;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}
H5 链接免登(飞书卡片直达,POST /api/link/login)
卡片上的「查看并处理」按钮不是裸链接,而是带一次性票据的深链:
{h5_base_url}?order=117&t=<ticket>
- 票据:有效期可配置(飞书集成 →「卡片链接有效期(小时)」,1–168,默认 24)+ 一次性,
base64url(payload).HMAC-SHA256,载荷含用户ID / 工单ID / 过期时间 / nonce - 签名密钥自动生成在
runtime/link_secret.key(不写入任何业务配置) - 校验顺序:签名 → 有效期 → 未被使用 → 工单仍归属该用户(已转派则失效)
- 消费记录在
h5_tickets表(防重放;后续刷新依赖 cookie 会话) - 打开后 URL 自动清理掉票据,只保留
?order= - 失效回落:票据过期/已用/已转派 → 显示登录页并提示原因,「登录后将继续打开该工单」;登录成功后自动跳回该工单详情
- 已登录用户打开旧卡片链接时,票据失败不影响使用(沿用现有 cookie 会话)
权限来源(免登没有 OAuth 令牌,无法实时拉权限):
- 权限快照(优先):OAuth 登录 / 手动刷新权限时写入
user_permission_cache(权限码 + 角色 + 同步时间) - 角色兜底:无快照时按本地角色给最小可用权限(
DefaultPermissions),并记录[LinkAuth] 无权限快照警告,提示尽快用浏览器登录一次刷新
安全:票据只对「本人被派单的工单」有效;不自动创建账号;未匹配到用户直接拒绝。
账号绑定
- 人员管理中的
feishu_user_id为接收方 open_id;后台「飞书集成 → 批量匹配 open_id」按手机号批量反查(contact/v3/users/batch_get_id)并回填 - 单条发送前若用户未绑定,会按配置字段尝试解析并回填(失败则跳过并记日志)
- 需应用具备
contact:user.id:readonly权限
事件回调(卡片内接单)
- 公开路由
POST /api/feishu/event(不走登录) url_verification:校验 Verification Token 后原样返回challengecard.action.trigger:读取按钮value({action:'accept', order_id})与操作人open_id→ 匹配本地用户 → 校验「工单属于本人 + 状态为已派单」→ 接单(置为处理中)- 幂等:非
assigned状态返回提示「无需重复接单」;未绑定/非本人分别给出明确 toast - 加密模式:配置 Encrypt Key 后按
AES-256-CBC(key=sha256(encrypt_key),iv 取密文前 16 字节)解密 - 飞书后台配置:开启事件订阅 → 请求地址填
https://<H5域名>/api/feishu/event(或当前回调路径)→ 订阅card.action.trigger→ 填 Verification Token / Encrypt Key
开放平台需要开通
| 项 | 说明 |
|---|---|
| 机器人能力 | 应用 → 添加应用能力 → 机器人 |
im:message:send_as_bot |
以机器人身份发消息 |
contact:user.id:readonly |
按手机号/邮箱反查 open_id(批量绑定) |
| 网页应用能力 + 安全域名 | H5 在飞书内打开(可先只打通消息推送,此步与免登一并配置) |
事件订阅 card.action.trigger |
卡片内接单/操作回调 |
| 应用发布 + 可用范围 | 未发布或成员不在范围时接口成功但收不到消息 |
自测
后台「发送测试消息」支持填 open_id / 手机号 / 本地用户 ID,结果(时间 / 成功失败 / 原因)显示在「当前状态」卡片;api_base 指向本地 mock 即可在无真实凭证时验证完整链路。
验证记录
完整冒烟测试已通过:建单→分派→接单→挂起→恢复→完成(回填扩展字段)→关闭;驳回→重新分派→转派;取消权限控制;事件自动建单+幂等;外部单创建/查询/状态回传+幂等+401;SLA 超时升级飞书通知。