11 Commits

Author SHA1 Message Date
18f74101f9 fix(attachments): ticket_attachment.dspy的safe_name只在download分支定义,inline打开路径500——提到分支外统一计算 2026-09-11 12:41:07 +08:00
4aa01d4d79 feat(attachments): 工单全链路附件——客户建单/追问、运维回复/内部备注支持多附件上传(multipart→FileStorage),详情弹窗(页面+待办共用)消息流内联附件行+全部附件汇总区,上传方彩色徽标区分(客户/助手/客服),打开/下载走ticket_attachment.dspy(I5服务端可见性校验+防路径穿越),服务端normalize_attachments规整+file_useful持久化豁免,错误码TK_E014/E015 2026-09-11 12:36:10 +08:00
e613bc860f fix: _post_js拼接dict致TypeError500——所有带按钮弹窗路径全炸(staff_replied态无按钮分支掩盖了此bug),dict改json.dumps序列化 2026-09-10 17:42:02 +08:00
9c36ab38d2 fix: 工单管理查不到+待办清不掉 四个叠加缺陷根治(2026-09-10用户报障)
报障现象: owner端工单管理查不到任何工单, 但有个工单待办; 待办弹窗无处理按钮致待办数不变。

根因链(全部有DB/代码证据):
1. 弹窗缺客户动作按钮: 双身份用户(admin既是TK0004客户又是staff), staff视角在
   staff_replied态一个按钮都没有, 而此刻待办要求的动作是客户确认——待办永远清不掉。
   修: staff视角下当前用户=工单客户且状态在已回复态时追加客户动作按钮。
2. 管理页默认队列=待认领, 当时无待认领单+前端首次查询点击渲染时序中间态→看似查不到。
   修: 默认队列改全部工单。
3. T12转派校验不全: 只查目标用户属于0机构不查owner角色, 测试时把TK0003转给无角色的hyz1
   产生孤儿工单(角色池无人可认领+hyz1打开管理页被权限分支挡)。
   修: 目标用户必须持owner角色否则TK_E004拒绝; 候选端点只列持owner角色的用户;
   孤儿工单TK0003数据修复退回角色池。
4. staff_tickets的all分支权限过窄: 无owner角色的账号直接返回空, 受理人看不到自己手上的工单。
   修: 无owner角色账号至少能看到受理人是自己的工单。
2026-09-10 17:38:31 +08:00
86adf1aa8c fix: sqlor占位符外层引号致双重引号1064——去掉ph占位符的单引号包裹
上一提交修占位符时引入的新错: 给 dollar-brace 占位符加了外层单引号,
渲染成 IN ('占位符') → sqlor参数化时aiomysql再自动加引号 → 双重引号
IN (''owner.maintainer'') → MySQL把空串后 owner.maintainer 当裸标识符 → 1064。

证据: 同文件其余占位符(user/ticket等)均无外层引号且全部成功,
仅动态IN子句两处(todos角色池+core待认领队列)加了引号即报错。

修法: 去掉外层单引号, 占位符直接拼入IN子句, 与同文件其余用法一致。
渲染SQL现为 assignee_role IN (占位符) 正确绑定参数。

教训: sqlor占位符(dollar-brace-key-brace-dollar)由驱动参数化并自动加引号,
手工再包单引号=双重引号必炸; 拼接动态IN子句时占位符裸写不加引号。
2026-09-10 17:09:53 +08:00
8e0bbdbbd1 fix: sqlor占位符语法错——角色IN子句用字面量非参数占位致永远匹配不到角色
部署实测抓到: 工单TK0002状态human_pending且assignee_role=owner.maintainer,
但provider用owner.maintainer角色查待办返回0条。

根因: 动态IN子句拼接时 ph 生成的是字面量 'r0' 而非 sqlor 的参数占位格式,
渲染出的SQL是 assignee_role IN ('r0') 恒不匹配任何真实角色值,
params里设置的 r0=owner.maintainer 从未被SQL引用。

两处同款错:
- todos.py 角色池待办条件(human_pending且assignee_role在我的角色)
- core.py list_staff_tickets 的 pending 队列条件

修法: 占位符改为 sqlor 的标准参数格式(dollar-brace-key-brace-dollar)。
渲染SQL现为 assignee_role IN (参数占位) 正确绑定。

教训: sqlor 动态 IN 子句拼接, 占位符必须用标准参数格式,
裸引号包键名会变成SQL字面量常量, 静默匹配0行不报错——极难排查。
2026-09-10 17:07:17 +08:00
d151ea76d5 feat(agent): LLM调用org_id可配(params ticket_agent_org_id)——'0'机构无模型网关策略致__LEGACY__降级转人工的实测修复 2026-09-10 16:50:54 +08:00
ce685bc684 fix: appcodes_kv.id列宽32,组合键tk_transfer_action_escalate_to_human(36)超长1406致种子整段回滚——parentid缩短tk_action(最长27)+models codes cond同步
部署实测抓到第二个根因: import_init用id=parentid_k组合键, appcodes_kv.id是varchar(32),
tk_transfer_action前缀过长→1406 DataError→ticket整段种子中断(owner.maintainer角色也没导)。
改parentid为tk_action,全码表组合id最长27,0超长。
2026-09-10 16:36:08 +08:00
fa415aa379 fix: current_role是MariaDB保留字致CREATE INDEX/WHERE全1064——改名assignee_role/assignee_id 2026-09-10 16:24:39 +08:00
54f2f2d597 feat: 工单管理模块初版——客户提问→agent预处理→人工运维→转派,接入平台待办
- 3表: tk_tickets(状态机+受理角色/人+agent锁)/tk_messages(客户可见/内部备注)/tk_transfers(append-only流水)
- core.py: 8态13迁移状态机全乐观锁; 转派候选动态查owner角色+'0'机构用户(不硬编码);
  默认受理角色params可配(ticket_default_role=owner.maintainer)
- agent.py: 异步poller(bid_flow范式), RAG检索'0'机构KB+历史工单, llm_call(org_id=0)
  LLM裁决能否回答(严格JSON), 硬门禁reply<20字符转人工, 故障≠答不了(fail_rounds>=3转人工),
  stale回收10min; 追问回流(agent_processing+owner空)立即拾取不等stale
- todos.py: 平台待办provider, 状态派生(客户待确认/角色池待认领/受理人待处理)
- init.py: load_ticket注册env+provider+poller(PIPELINE_MODE=web不启动)
- wwwroot: 客户我的工单页+建单表单, 运维工单管理页(队列切换), 详情弹窗(正文与操作同屏,按视角出按钮)
- load_path.py: logined全部API(业务权限服务端按current_role动态校验)+管理页壳owner三角色
- init/data.json: 7组码表+owner.maintainer角色种子(app_audit先例)
2026-09-10 16:17:58 +08:00
f1aec291e9 Initial commit 2026-09-10 16:10:34 +08:00