Files
F9Web/AI开发日志/2026-08-15.md
T
fanhongcai 1bec470647 feat: 新增IM即时通讯(浮窗)、工作流、打印模块及工作台增强
- IM: 新增浮窗聊天(ImFloatWindow)、管理页(monitor/config/service/message)、SSE推送
- 工作流: 新增待办/我的流程页面及后端服务
- 打印: 新增打印模板、出库单打印(PrintPage)、模板种子脚本
- 工作台: 增强快捷入口与工作台数据
- 修复: TagsView页签关闭、CrudPage通用表格增强
- 移除导航菜单中的即时通讯入口,改为右下角浮窗
2026-08-16 00:19:24 +08:00

20 KiB
Raw Blame History

F9 智慧缫丝系统 —— CodeBuddy 持续工作存档

本文件为 CodeBuddy 开发日志归档(每日一份),归档日期:2026-08-15。 新对话开始后:先读本文件 + 功能清单 + 项目说明,按「任务状态」继续推进,无需重新调研。

最后更新:2026-08-15


一、任务状态总览(按用户约定的优先级 1→2→3)

序号 任务 状态 说明
1 通用打印接入主前端 v1 完成(三等分原料出库单可打印) 后端取数 + openprint /print 外部页 + web 打印按钮 + 三等分模板入库;联调收尾:已造测试数据(庄口 id=3 / 出库单 id=2),打印数据端点返回完整
2 简易工作流(采购/领料审批闭环) v1 完成(原料出库审批闭环) WorkflowService + API7 端点)+ 待办中心/我发起的页面 + 原料出库单「审批」按钮;流程定义/菜单已种子入库
3 专业业务逻辑(自动定级/能耗分摊/成本盈亏/断料预警) 待做 依赖任务 1/2 部分能力
4 轻量 IM(内部互发 + 系统通知) v1 完成 ImService + API6 端点)+ IM 聊天页 + 顶栏消息铃铛 + 演示通知种子

已完成的里程碑:

  • 动态 CRUD 体系(后端 EntityCatalog + CrudService + DataController,前端 CrudPage + table-map 配置驱动)
  • openprint 打印设计器全链路(模板设计→数据源内省→预览/打印,推 PDF 到本地客户端)
  • 工作台接真实数据(WorkBenchController + workbench/index.vue 重写)

二、项目架构速查

2.1 三端结构

目录 技术栈 端口 说明
PC 前端 web/ Vue3 + Element Plus + ECharts + Pinia vite dev 5173 代理 /api → localhost:5136
后端 server/ .NET 10 + FreeSql (v3.5.311) 5136 MySQL 云库 f9web116.198.221.105
打印设计器 openprint/ Vue3 + Naive UI + Vite 5227 对接 F9 的 PrintApiController,推 PDF(base64) 到本地客户端 127.0.0.1:18888
电视大屏 / 小程序 未开始(按项目说明先完成 PC 端)

2.2 后端关键约定

  • 表命名:模块前缀 + 下划线,如 CRM_CustomerRawMaterial_InstockCommon_WorkflowRepCenter_ReportDesign;实体类同前缀,如 RawMaterial_Instock
  • 默认字段idflag0=删除,>0 有效,<0 无效,-1=作废)、addtimeadderupdatetimeupdatermemo
  • 序列化Program.cs 全局 CamelCase + 忽略 null(即 C# 属性 OrderNo → JSON orderNo)。
  • 响应信封:普通 API 用 ApiResultPrintApiControllerapi/print)返回裸 JSON {items,total}openprint 的 http-client 已按此解析,主前端 axios 也可透传)。
  • 动态查询DataControllerEntityCatalog(表名→实体 Type 反射映射)+ CrudService<T> 泛型反射调用,GetList/GetById/Add/Update/Delete 等通用接口。
  • 工作流表(已有,未做服务):Common_Workflow(流程定义)/ Common_WorkflowNode(节点)/ Common_WorkflowInstance(实例)/ Common_WorkflowTask(任务,Status==0 待办、UserId 当前处理人)。

2.3 web 前端关键约定

  • Token 存储localStorage key = f9mes_tokenweb/src/stores/user.jsTOKEN_KEY)。
  • API 封装web/src/api/request.jsaxios 拦截器注入 Bearer);web/src/api/index.js 集中导出(含已新增的 getWorkBenchSummary)。
  • 配置驱动 CRUDweb/src/config/table-map.js 定义每张表的列/表单配置,web/src/components/CrudPage.vue 是通用页面组件,菜单由 InitDataService 生成。

2.4 openprint 关键约定

  • 对接开关openprint/src/main.ts 仅当配置 VITE_OPENPRINT_API_BASE 时才切换云端仓库(HttpRepository/HttpDataSource);.env 中配 VITE_OPENPRINT_API_BASE + VITE_OPENPRINT_API_TOKEN
  • HttpClient token:构造时静态传入(http-client.tsHttpOptions.token),构造后不可变;若改运行时 token 需改构造时机或加 setter。
  • 绑定 Path 约定:后端 PrintService.BuildFields 生成 Path = "{TableId}.{PropName}"(如 RawMaterial_Instock.OrderNo);openprint 的 expression.ts/data-binder.ts 按路径取值。注意:序列化后 JSON 为 camelCase,而 Path 是 PascalCase,取值器需兼容(大小写不敏感取 key 或按 camelCase 注入)。
  • 打印出口PrintDialog.vuedoPrint()store.buildTemplate() + buildPreviewData(dsStore.activeFields)buildPrintPayload({template, data}) → 推本地客户端(POST /print127.0.0.1:18888)。当前用假数据,是接入真实数据的关键改造点。
  • 数据源内省PrintApiControllerdata-sources/fieldsPrintService 按实体类前缀分组(ModuleNames 字典),字段缓存 TTL 10 分钟。

三、任务 1:通用打印接入主前端(进行中)

3.1 目标

表单通用打印:主前端任意 CRUD 列表 → 选中一行 → 选打印模板 → 打开 openprint 用该行真实数据打印(针式/A4 均支持,设计器已具备)。

3.2 已实施的三步方案(2026-08-15 落地 v1 三等分原料出库单)

Step A — 后端:取行数据接口(已完成)

  • 位置:PrintApiController.cs,路由 GET api/print/data/{table}/{id}
  • 实现:EntityCatalog 动态取实体 Type → 反射 CrudService<T>.GetByIdAsync(复用 DataController 模式,ActivatorUtilities.CreateInstance)→ 输出 { "<TableId>": { camelCase 字段 } } 裸 JSON(信封键=表名,与 BuildFields 的 Path 前缀一致)。
  • 增强:① 关联字段补全 RefMap*Id{Xxx}Code/{Xxx}Name,如 ZhuangkouCode、OutOrgName);② 枚举文本注入 EnumMaps(如 OutType → OutTypeText 领料/退货/报损)。
  • 注意:openprint 侧会为行对象自动补 PascalCase 别名(withPascalAliases),因此模板绑定 RawMaterial_OutStock.BillNobillNo 均可取到值。

Step B — openprint:外部打印模式(已完成)

  • 新增 openprint/src/print/PrintPage.vue(独立轻量打印页,不进入设计器),main.ts 检测 ?print=1 时改挂载该页。
  • URL 参数:?print=1&template=<模板id>&table=<表名>&row=<行id>&token=<JWT>;可选 apiAPI 根地址,缺省 VITE_OPENPRINT_API_BASE);可选 data(base64url 直传数据,绕过数据请求)。
  • 流程:解析参数 → createHttpRepository({baseUrl, token}) → 拉数据(fetch /api/print/data/{table}/{row} 带 Bearer,或解码 data)→ withPascalAliases 归一化 → createHeadless({repository}).buildRequest(tplId, data)headless.print()(浏览器打印对话框,可用三等分纸型)。
  • 页面状态机:加载中/打印中/完成/失败 +「重新打印」「关闭」按钮;缺参/HTTP 错误均给出中文提示。

Step C — webCrudPage 打印按钮(已完成)

  • 新增 web/src/config/print.jsOPENPRINT_URLenv VITE_OPENPRINT_URL,默认 http://localhost:5227)、PRINT_API_BASE(默认 http://localhost:5136)、buildPrintUrl()
  • web/src/config/table-map.jsPRINT_MAPcomponent → {templateName, title}+ resolvePrint();当前配置 RawMaterial/outstock → 模板名「三等分原料出库单」。
  • web/src/router/index.js:路由 meta 注入 printTemplate: resolvePrint(component)
  • CrudPage.vue:操作列按 printTemplate 条件渲染「打印」按钮 → GET /print/templates 按模板名找到 id → window.open(buildPrintUrl({template, table, row, token}))。操作列宽度 opWidth 随按钮动态计算。
  • 模板匹配用名称而非硬编码 id(数据库重建也不失效);多模板扩展时改 PRINT_MAP 即可。

三等分模板(已完成)

  • server/scripts/print-template-outstock.json:210×99mmA4 三等分)竖版 portrait、边距 8mm;标题/单号/庄口/领料车间/出库重量(kg)/出库日期/出库类型/领用人+仓管员签章区。
  • server/scripts/seed-print-template.mjs:登录→查重→POST /api/print/templates 创建(node 执行,避免 PowerShell 中文编码坑)。
  • 已入库:模板 id=2(Name=三等分原料出库单,PaperSize=三等分)。表 RawMaterial_OutStock 当前无数据,录入后可验证完整打印。

3.3 关键文件清单

  • 后端:server/src/F9MES.Api/Controllers/PrintApiController.csserver/src/F9MES.Application/Print/PrintService.csserver/src/F9MES.Api/Common/EntityCatalog.csserver/src/F9MES.Api/Controllers/DataController.cs
  • openprintsrc/main.tssrc/App.vuesrc/config/backend.tssrc/repository/http-client.tstoken 构造点)、src/design/stores/designer.tsloadTemplate)、src/design/stores/dataSource.tssrc/design/modals/PrintDialog.vuedoPrint)、src/design/toolbar/TopToolbar.vueshowPrint 挂载)、src/design/preview/preview-data.tsbuildPreviewData)、src/core/layout-engine/expression.ts + data-binder.ts(取值)
  • websrc/components/CrudPage.vuesrc/api/index.jssrc/config/table-map.jssrc/stores/user.jstoken

四、任务 2:简易工作流(v1 完成 ,原料出库审批闭环)

4.1 已实施(2026-08-15 落地)

  • 后端
    • Common_WorkflowInstance 实体新增 BizId(关联业务记录 ID,跳转业务详情用)。
    • 新增 server/src/F9MES.Application/Workflow/WorkflowService.cs + WorkflowDtos.csserver/src/F9MES.Api/Controllers/WorkflowController.csDI 注册于 Program.cs)。
    • APIapi/workflow/*,均需 JWT):
      • GET definitions?bizType= 已发布流程定义(含节点)
      • POST start 发起流程(创建实例 + 首个审批任务,审批人取节点 ApproverJson 数组首个用户,缺省当前操作人)
      • GET todos?page=&size= 我的待办
      • POST approve 同意(推进下一审批节点,无则实例通过 Status=1)
      • POST reject 驳回(实例终止 Status=2
      • GET instances?page=&size=&status= 我发起的实例
      • GET instance/{id} 实例详情(含任务轨迹)
  • 前端
    • web/src/api/workflow.js7 个方法封装)。
    • web/src/views/workflow/todo.vue 待办中心(同意/驳回带意见、详情抽屉轨迹时间线);my.vue 我发起的(状态筛选、详情轨迹)。
    • table-map.jsSPECIAL_PAGESWorkFlow/todoWorkFlow/my;新增 WORKFLOW_MAPcomponent → bizType,当前 RawMaterial/outstockRawMaterial_OutStock+ resolveWorkflow()
    • router/index.js:静态路由 /workflow/todo/workflow/my;动态路由 meta 注入 workflow: resolveWorkflow(component)
    • CrudPage.vue:操作列按 workflowCfg 条件渲染「审批」按钮 → 弹窗选流程定义 → 发起(opWidth 动态加宽)。
  • 种子server/scripts/seed-workflow.mjs 创建「原料出库审批」流程(Code=RM_OUT_APPROVE,节点:发起→车间主管审批[ApproverJson=["1"]]→结束)+ 侧边栏菜单(工作流目录→待办中心/我发起的)。
  • 验证:编译通过(0 错误);端到端 API 闭环通过——发起(instanceId 正常回填)→ 待办(任务归属当前用户)→ 同意(实例 Status=1)→ 驳回(Status=2);菜单树已含待办中心/我发起的;web 全部模块编译 200。

4.2 关键坑

  • FreeSql 自增回填Insert(entity).ExecuteAffrowsAsync() 不回填主键(返回 0),必须用 ExecuteIdentityAsync() 并手动赋值(CrudService.AddAsync 已是此写法)。

4.3 扩展方式

  • 新表单接审批:WORKFLOW_MAP 加一行 + 后端种子加流程定义(复用 seed-workflow.mjs 模式);审批人可在节点 ApproverJson 配置用户ID数组。
  • v1 简化:单审批人串行流转;同一节点多审批人(会签/或签)、条件分支、撤回、流程配置 UI 留待后续。

五、任务 3:专业业务逻辑(待做)

按优先级从高到低建议:

  1. 断料预警(原料库存超期/不足,工作台预警列表已预留位置)
  2. 自动定级(Lims 检验数据 → 调用定级标准自动判定生丝等级,标准表需先建/已有则复用)
  3. 能耗分摊(Enms 抄表 → 按投料量/产量/机台时长分摊到庄口 → 推成本)
  4. 成本盈亏(BizBoss 生产成本分摊 + 分庄口盈亏统计)

六、工程实践与坑位(必读)

  1. 编译锁定:后端进程运行时 DLL 被锁,dotnet build 会报 MSB3027/MSB3021 → 用 dotnet build -o build_tmp 验证编译,完成后删临时目录;改动后端必须重启服务才生效
  2. FreeSql 陷阱:非空 DateTime 没有 .HasValue;空集合时 .Where(x => ids.Contains(...)) 会生成非法 SQL,需先 if (list.Count > 0) 守卫。
  3. JSON 大小写:后端全局 camelCaseopenprint 绑定 Path 是 PascalCase,取值需兼容。
  4. openprint 是独立应用:改动它用 npm run dev(端口 5227)独立调试;主前端 web 是 vite dev5173)。
  5. 数据库连接(项目说明):MySQL 116.198.221.105f9web 用户 f9web;阿里云 OSSbbit-f9-web);天气用 Open-Meteo。
  6. 项目要求:类/文件命名带模块前缀、各模块类放各自文件夹、公共类放 Common;界面 MES 风格、高可配置、表单必须支持打印、表单带简易工作流。
  7. PowerShell 中文编码坑PS 5.1 按 GBK 读 UTF-8 无 BOM 文件(脚本/文件名乱码)、Invoke-RestMethod 发中文 body 易 400GBK 发送 vs UTF-8 解析)。对策:① 涉及中文的脚本用 node(.mjs) 写(原生 UTF-8);② 文件/命令行尽量用 ASCII 名。
  8. 启动服务:后端中文路径下 Start-Process dotnet 传中文路径会损坏 → 用 cmd /c "cd /d <路径> && dotnet run ..." 或先取 8.3 短路径;API 冷启动要等 SyncStructure 全表同步(114 张远程表,约 1-2 分钟)后才监听端口。

七、本轮对话已完成的工作(2026-08 中旬)

7.1 工作台真实数据(已完成

  • 新增 server/src/F9MES.Api/Controllers/WorkBenchController.csGET api/workbench/summary,聚合统计卡(今日产丝=ProHou_Daily.OutputWeight 合计、在线机组=ProQian_Machine 启用且运行、待办=工单+工作流待办、预警数)+ 近 30 天趋势 + 庄口占比 + 待办列表(工单/审批)+ 预警列表。
  • web/src/api/index.js:新增 getWorkBenchSummary
  • web/src/views/workbench/index.vue:重写,4 统计卡/趋势折线/庄口占比饼图/待办与预警列表全部接真实数据,无数据时占位,保留天气+快捷入口。
  • 已用 -o build_tmp 验证编译通过;需重启后端生效

7.2 openprint 全链路调研(已完成,结论见第三节)

已通读:PrintApiController / PrintService / EntityCatalog / DataController / http-repo / http-datasource / http-client / backend.ts / main.ts / App.vue / TopToolbar / PrintDialog / designer store / dataSource store / preview-data / expression / data-binder / CrudPage / table-map / request.js / user store / 两端 vite 配置。结论已沉淀到第三节。

7.3 通用打印接入 v1(已完成 ,三等分原料出库单)

  • 后端:GET api/print/data/{table}/{id}(关联名称 + 枚举文本补全,裸 JSON 信封),CORS 增加 localhost:5227
  • openprint/print 外部打印页(PrintPage.vue + main.ts 挂载逻辑),headless 渲染 + 浏览器打印。
  • web:打印按钮(CrudPage + PRINT_MAP + router meta + print.js 配置)。
  • 模板:210×99mm 三等分原料出库单已入库(id=2);种子脚本 server/scripts/seed-print-template.mjsnode)。
  • 验证:API/模板/数据端点 404 行为/两端模块编译均通过。
  • 联调收尾:已造测试数据——庄口 id=3YLZ20260815-001 四川凉山-2026春茧)、出库单 id=2CK-20260815-001125.5kg 领料/已出库);GET api/print/data/RawMaterial_OutStock/2 返回完整(含 ZhuangkouName、OutTypeText=领料);打印页 URL 已验证(选三等分纸型即可打印)。脚本 server/scripts/seed-test-outstock.mjs

7.4 简易工作流 v1(已完成 ,原料出库审批闭环)

  • 后端:WorkflowService + WorkflowController7 端点,api/workflow/*),实体加 BizIdDI 注册。
  • 前端:workflow.js / todo.vue / my.vue / WORKFLOW_MAP / 静态路由 / CrudPage「审批」按钮。
  • 种子:server/scripts/seed-workflow.mjs(原料出库审批流程 + 工作流菜单)。
  • 验证:编译 0 错误;API 闭环(发起→待办→同意 Status=1;驳回 Status=2);菜单树含待办中心/我发起的;web 模块编译 200。
  • 坑:FreeSql ExecuteAffrowsAsync 不回填自增主键,须用 ExecuteIdentityAsync

7.5 轻量 IM v1(已完成 ,内部互发 + 系统通知)

  • 数据:复用 Common_Message 表,扩展 SenderId/SenderName(发送人,0=系统),MsgType 增加 4=单聊消息(原 0=系统 1=业务 2=审批 3=预警)。
  • 后端server/src/F9MES.Application/Im/ImService.cs + ImDtos.csserver/src/F9MES.Api/Controllers/ImController.csDI 注册于 Program.cs)。
    • APIapi/im/*,均需 JWT):GET sessions 会话列表(按对端聚合最后消息+未读数)、GET messages?peerId=&page=&size= 聊天记录(双方)、POST send 发送单聊({peerId, content})、POST read 标记已读({peerId})、GET unread-count 未读总数(顶栏角标)、POST notify 系统/业务通知群发({userIds?, msgType, title, content},空 userIds=全部启用用户)。
  • 前端
    • web/src/api/im.js6 方法)。
    • web/src/views/im/index.vue 聊天中心(左侧会话列表:头像/最后消息/时间/未读角标,右上 + 发起新会话选用户;右侧消息气泡+Enter 发送;5s 轮询)。
    • layout/index.vue 顶栏新增消息铃铛(未读角标,30s 轮询,点击跳 /im)。
    • table-map.jsSPECIAL_PAGESIm/indexrouter/index.js 加静态路由 /im
  • 种子server/scripts/seed-im.mjs 创建「协作」目录 →「即时通讯」菜单 + 3 条演示通知(工单变更/工艺单变更/审批通知)。
  • 验证:编译 0 错误;API 闭环通过——管理员↔车间主管李工(测试用户 id=2)互发、未读计数、已读清零、会话聚合、聊天记录含 senderName;菜单树含「即时通讯/待办中心/我发起的」;web 模块编译 200;前端 lint 0 错误。
  • 坑(重要)InitDataService.InitMenusAsync 有开发期修复——只要存在 MenuType==1 && ParentId==0(顶层页面节点)就物理删除全部菜单重建。种子脚本新增菜单**必须用目录节点(MenuType=0, ParentId=0+ 页面子节点(MenuType=1, ParentId=目录id)**结构;否则会连带删掉工作流等全部自定义菜单(本次已踩坑并修复)。

八、下一步行动(新对话从这里开始)

  1. 先读本文件 + 功能清单 + 项目说明(若工作区未加载)。
  2. 人工验证
    • IM:登录 web → 顶栏消息铃铛(未读角标)→ 侧边栏「协作 → 即时通讯」聊天(管理员 ↔ 车间主管李工 13900000001/123456);演示通知已发到管理员账号。
    • 工作流:待办中心/我发起的列表 + 原料出库单「审批」按钮(测试数据出库单 id=2,流程 RM_OUT_APPROVE)。
  3. 多表单扩展a) 打印:PRINT_MAP 加 component → {templateName} + 设计模板;b) 审批:WORKFLOW_MAP 加 component → bizType + 种子脚本加流程定义;c) 通知:业务代码调 POST api/im/notify(工单/工艺单变更处已留接口)。
  4. 后续推进任务 3(专业业务逻辑:断料预警 → 自动定级 → 能耗分摊 → 成本盈亏)。
  5. 任何大改动前更新本文件「最后更新」日期与任务状态。
  6. 新增菜单铁律:种子脚本建菜单必须「目录节点(MenuType=0+ 页面子节点(MenuType=1)」结构,禁止顶层页面节点(MenuType=1, ParentId=0),否则 InitDataService 孤儿修复会物理删除全部菜单重建。