从一次“发起工程“走完调用链:CCFlow CCPrj 工程管理源码导读驰骋BPM驰骋低代码低代码工作流引擎

跟着一次"发起工程"把 CCPrj 源码走完

本系列共 3 篇: ① 设计与实现 ② 产品介绍 ③ 源码导读(本文)

面向二次开发。不从概念出发,而从一次真实点击出发:用户在发起列表点了一个模板,系统如何创建实例、打开工作台、再按任务类型分发到不同页面。


阅读顺序建议

先不要按目录把 40 多个文件从头扫到尾。按这条调用链看,会快很多:

复制代码
GL_PrjStart.ts
  → WF_CCPrj.CreatePrj()
    → PrjDev2Interface.Prj_CreateBlankPrjID()
      → MyPrj.vue
        → TaskCenter / TaskGener / TaskFlow / TaskBill

记住两个 Handler:

场景 类名
运行时 BP.CCPrj.HttpHandler.WF_CCPrj
管理端 BP.CCPrj.HttpHandler.WF_Admin_CCPrj

前端封装在 Comm/PrjComm.ts:

ts 复制代码
PrjComm.GetHandler()      // WF_CCPrj
PrjComm.GetTaskHanlder()  // WF_Admin_CCPrj

业务不要写在 Vue 里拼 SQL,也不要写在 Handler 里堆逻辑。Handler 只接参数、转 JSON;真正干活的是 PrjDev2Interface。


一、目录地图

前端 Vue3/src/CCFast/CCPrj

文件 先看什么
Port.vue 模块入口,按 PageName 分发
GenerList/GL_PrjStart.ts 点模板 → 创建实例
GenerList/GL_PrjTodolist.ts 等 待办/在途/已完成,复用 GenerList.vue
TaskCenter.vue 个人任务工作台
MyPrj.vue 项目处理器,工程模块的 MyFlow
PrjToolBar.vue 按状态和角色出按钮
TaskGener.vue / TaskFlow.vue / TaskBill.vue 三类任务页
Comm/taskHandler.ts 任务类型、待办加载、打开哪个组件
SearchPrj.vue 按模板查实例
Admin/TemplateList.vue 模板库
Admin/Template/TemplatePrj.ts 模板实体 + 设计表单/文件工作区
Admin/Template/PG_TemplateTaskGroup2Task.ts 步骤设计(左组右任务)
FileWorkspace.vue 文件工作区

后端 CCFlow/Components/BP.WF/CCPrj

文件 先看什么
PrjDev2Interface.cs 全部业务接口
WF_CCPrj.cs 运行时 HTTP 入口
WF_Admin_CCPrj.cs 模板库、步骤、文件工作区配置
Template/*.cs 设计态实体
GenerPrj.cs / GenerTask.cs / GenerTaskGroup.cs 运行态实体
GenerWorker.cs 待办人员
PrjTrack.cs / PrjWorkerLog.cs 轨迹 / 工时日志
Template/PrjEnum.cs PrjSta / TaskSta

实体写法与流程模块相同:XxxAttr + Entity + Entities + EnMap。改字段先改 EnMap,再改接口,不要只改前端。


二、入口:点一个模板,发生了什么

GL_PrjStart 继承 PageBaseGenerList,页面本身复用流程的 GenerList.vue。它只做三件事:列怎么显示、数据从哪来、点行去哪。

11:40:Vue3/src/CCFast/CCPrj/GenerList/GL_PrjStart.ts 复制代码
  async Init() {
    this.PageSize = 0;
    this.LinkField = 'Name';
    this.GroupFields = 'SortName';
    this.HisGLShowModel = GenerListPageShowModel.Windows;
    // ...
    const handler = new HttpHandler('BP.CCPrj.HttpHandler.WF_CCPrj');
    const data = await handler.DoMethodReturnJson('TaskStart_Init');
    this.Data = data;
  }

  async LinkFieldClick(object: Record<string, any>) {
    const handler = new HttpHandler('BP.CCPrj.HttpHandler.WF_CCPrj');
    handler.AddPara('TemplateNo', object.No);
    const PrjID = await handler.DoMethodReturnString('CreatePrj');
    const url = `/src/CCFast/CCPrj/MyPrj.vue?PrjNo=${PrjID}`;
    return new GPNReturnObj(GPNReturnType.OpenUrlByDrawer90, url, '项目处理');
  }

对应后端非常薄:

1813:1820:CCFlow/Components/BP.WF/CCPrj/WF_CCPrj.cs 复制代码
        public string CreatePrj()
        {
            string templateNo = this.GetRequestVal("TemplateNo");
            Int64 oid = PrjDev2Interface.Prj_CreateBlankPrjID(templateNo);
            return oid.ToString();
        }

待办列表是同一模式。GL_PrjTodolist 调 PrjTodolist_Init,点行同样打开 MyPrj.vue?PrjNo=...。二次开发如果要加"我延期的工程",优先抄这两个 GL 文件,不要新写一张列表页。


三、创建实例:模板复制到运行时

核心在 Prj_CreateBlankPrjID。建议按注释里的步骤打断点:

  1. 读 TemplatePrj
  2. OID = DBAccess.GenerOID("PrjID"),不要用数据库自增
  3. 状态先写 PrjSta.Draft
  4. 用模板 BillNoFormat 调流程现成的 WorkFlowBuessRole.GenerBillNo
  5. 复制 TemplateTaskGroup → GenerTaskGroup
  6. 复制 TemplateTask → GenerTask
  7. SyncTaskWorkers 写待办人
  8. EnsurePrjForm 保证项目表单和物理表存在
  9. PrjTrack.WriteLog 记一条"创建工程"

任务主键约定必须守住:

复制代码
任务组 No = 工程OID + "_" + 模板任务组No
任务   No = 工程OID + "_" + 模板任务No

前端从实例号反推模板任务号时,用的就是这个规则,见 taskHandler.deriveTemplateTaskNo()。乱改编号,流程任务/单据任务会找不到绑定关系。

复制任务时会带上 TaskType 和 RefTemplateNo。通用任务、流程任务、单据任务在实例里只是不同类型的 GenerTask,不是三张表。

草稿规则和流程对齐:

  • Draft = 1(保存到待办):给发起人在 Idx 最小的任务上插一条 GenerWorker
  • Draft = 2(保存到草稿箱):只建实例,不进待办
  • Draft = 0:不走草稿策略

所以"为什么创建后有的人立刻看到待办,有的没有",先查模板的 Draft 字段。


四、状态值不要靠猜

Template/PrjEnum.cs 里工程状态不是 0/1/2 递增那么直觉:

csharp 复制代码
Draft    = 0;  // 草稿
Complete = 1;  // 已完成
Runing   = 2;  // 进行中
Pause    = 3;
Invalid  = 4;

任务状态:

csharp 复制代码
None     = 0;  // 未开始
Complete = 1;
Delay    = 2;
Pause    = 3;

人员状态在 GenerWorker.cs:

csharp 复制代码
UnRead   = 0;
Complete = 1;
Read     = 2;
Refuse   = 3;

前端 PrjComm.PrjStaText() / NodeStaText() / WorkerStaText() 必须和这里一致。工具栏判断也是按这些数字,见 PrjToolBar.vue。

发起校验在 CheckPrjStartData:没任务、没名称、没负责人、日期范围缺日期,都会返回 err@...。前端只要原样提示,不要自己再写一套校验。

完成任务走 Node_SetOver:任务置完成 → 相关 Worker 置完成 → 若全部任务完成则 Prj_SetOver。自动完成工程的开关就在这里,改产品规则优先改这一段。


五、项目处理器:MyPrj 怎么把数据摊开

MyPrj_Init 直接把甘特数据集吐给前端:

1473:1477:CCFlow/Components/BP.WF/CCPrj/WF_CCPrj.cs 复制代码
        public string MyPrj_Init()
        {
            DataSet ds = PrjDev2Interface.Prj_GetGanttData(this.PrjID);
            return BP.Tools.Json.ToJson(ds);
        }

MyPrj.vue 自己拼任务组和任务的树,三个 Tab:

  • gantt:节点进度
  • form:项目表单(Fool 表单,FrmID 默认 Prj + 模板编号)
  • log:轨迹

按钮不写死在页面里,而由 PrjToolBar.vue 按 PrjSta + 角色计算。草稿出发起/删除,进行中出暂停/完成/移交/作废,已完成出回滚。表单 Tab 只出保存和打印。

权限字段在 GenerPrj 上:

  • BtnSaveRole
  • BtnCompleteRole
  • BtnTransferRole
  • BtnPrintRole

二次开发加按钮时:

  1. PrjToolBar 加一个 action
  2. MyPrj.vue 的 handleToolbarAction 接住
  3. WF_CCPrj 加一个薄方法
  4. PrjDev2Interface 写业务并打 Track

不要只改 Vue。

打开具体任务前会走 Node_CheckAuth / MyPrjFrm_CheckAuth。无权限就停,不要假定点得开就能改。


六、任务中心:按类型打开不同组件

TaskCenter.vue 三个 scope:todo / running / complete,数据来自:

  • PrjTaskTodolist_Init
  • PrjTaskParticipatingList_Init
  • PrjTaskDoneList_Init

真正决定打开哪一页的是 Comm/taskHandler.ts:

52:67:Vue3/src/CCFast/CCPrj/Comm/taskHandler.ts 复制代码
export function getTaskTypeLabel(taskType: number) {
  switch (taskType) {
    case 1:
      return '流程任务';
    case 2:
      return '单据任务';
    default:
      return '通用任务';
  }
}

export function getTaskHandlerCompUrl(taskType: number) {
  if (taskType === 1) return '/@/CCFast/CCPrj/TaskFlow.vue';
  if (taskType === 2) return '/@/CCFast/CCPrj/TaskBill.vue';
  return '/@/CCFast/CCPrj/TaskGener.vue';
}
TaskType 页面 背后依赖
0 TaskGener.vue PrjWorkerLog 工时日志 + 附件
1 TaskFlow.vue 模板任务绑定的流程 RefTemplateNo / refFlowNo
2 TaskBill.vue Fool 表单,绑定单据

如果要加第四种任务(例如"绑定报表"):

  1. TemplateTask.TaskType 枚举加值
  2. 模板设计里能选
  3. 创建实例时把类型复制到 GenerTask
  4. getTaskHandlerCompUrl 增加映射
  5. 新建一个 TaskXxx.vue,继续用 loadTaskRuntimeData 做鉴权和基本信息

不要为新类型新建一套待办表。待办始终是 Prj_GenerWorker。


七、管理端:模板库和步骤设计

运行时看 WF_CCPrj,设计态看 WF_Admin_CCPrj:

方法 用途
Prj_CreateTemplate 新建模板
TemplateList_Init 模板库列表和统计
TemplateList_Instances 某模板下的实例
TemplateList_DeleteTemplate 删模板
Task_CreateTaskFlow / Task_CreateTaskBill 给任务绑定流程或单据
FileWorkspace_Init / Save 文件工作区配置

前端模板实体 TemplatePrj.ts 上两个入口最常用:

  • fStep():打开表单设计器,FrmID = Prj + 模板编号
  • DoFileWorkspaceConfig():没有配置就先 Save 一份,再打开配置实体

步骤设计不是流程图,是 PG_TemplateTaskGroup2Task:左边任务组,右边任务,支持组内排序、拖动。RefKey = GroupNo。新增任务时会带上当前 TemplateNo。

模板主表关键字段:

  • PTable / 表单编号
  • BillNoFormat
  • TitleRole
  • Draft
  • IsCanEditTemplate

改"发起后能不能改任务结构",看的是 IsCanEditTemplate,不是前端按钮显隐。


八、文件工作区从哪接进去

配置实体:Admin/Template/TemplateFileWorkspaceConfig.ts

表:Prj_FileWorkspaceConfig

字段要点:

  • ReviewFlowNo:文件送审流程
  • BucketName / ObjectPrefix:对象存储
  • ParticipantsNo:工作区参与人

运行时接口在 WF_CCPrj 后半段:Workspace_Init、File_StartReview、File_BindReviewWork。MyPrj 工具栏的「文件工作区」和任务页里的 TaskFilePanel 都走这套,不是另一套附件表。

改送审规则时,先看文件是如何绑定 WorkID 的,再改流程,不要在 Vue 里写死流程编号。


九、二次开发最常见的四个落点

1. 加一张用户列表

抄 GL_PrjTodolist.ts + WF_CCPrj 一个 xxx_Init + PrjDev2Interface.DB_xxx。列表 UI 继续用 GenerList.vue。

2. 加一个工程按钮

PrjToolBar.vue → MyPrj.vue → WF_CCPrj.MyPrj_Xxx → PrjDev2Interface.Prj_Xxx,最后 PrjTrack.WriteLog。

3. 加业务字段

不要改 GenerPrj 内核。去模板的表单设计器加字段,数据落在 PTable。EnsurePrjForm 已经会创建默认字段和物理表。

4. 改待办口径

待办不是按工程算,是按 Prj_GenerWorker + GenerTask 算。改"谁能看见这条待办",改 DB_TaskList,不要改 MyPrj.vue 的过滤。


十、调试时一眼能定位的问题

现象 先看
发起列表是空的 DB_Starter 的模板表/类别表是否与当前实体表名一致
点模板报错 CreatePrj 是否收到 TemplateNo;模板是否存在
创建成功但没有待办 模板 Draft 是否为 1;GenerWorker 有没有行
打不开任务 MyPrjFrm_CheckAuth / Node_CheckAuth
流程任务是空白 模板任务 TaskType=1 且绑定了流程号
单据任务提示未就绪 是否走过 Task_CreateTaskBill,FrmID 是否存在
状态显示错乱 是不是把已完成 1 当成了进行中
工具栏没有完成按钮 BtnCompleteRole + 是不是负责人/发起人
项目表单是空的 EnsurePrjForm 有没有跑,FrmID 是否 Prj + 模板编号

Handler 约定沿用 CCFlow:成功返回数据或纯文本,失败返回 err@原因。前端看到 err@ 就提示,不要当 JSON 解析。


十一、一条最小阅读清单

如果只有半天,按这个顺序打开文件:

  1. GenerList/GL_PrjStart.ts
  2. WF_CCPrj.CreatePrj / MyPrj_Init / MyPrj_Start
  3. PrjDev2Interface.Prj_CreateBlankPrjID
  4. PrjDev2Interface.Prj_Start + CheckPrjStartData
  5. GenerPrj.cs、GenerTask.cs、GenerWorker.cs
  6. MyPrj.vue + PrjToolBar.vue
  7. Comm/taskHandler.ts
  8. TaskGener.vue(通用任务是默认路径)
  9. Admin/Template/PG_TemplateTaskGroup2Task.ts

把这条链走通,再去看文件工作区和甘特配置,不会迷路。系列第 1 篇讲为什么这样分层,第 2 篇讲业务怎么用;本篇只解决一件事:代码从哪进、改完往哪写。

相关推荐
搭贝2 天前
上厕所的时间搭好一套系统:AI自动生成实测
开发语言·人工智能·低代码·ai·php·搭贝
逆天的蝈蝈2 天前
数据安全、信创适配与系统对接
低代码·软件开发
驰骋工作流2 天前
驰骋BPM工作流引擎的护城河:七项能力的工程解析驰骋BPM驰骋低代码低代码工作流引擎
低代码
搭贝4 天前
还在等需求评审?AI已经把系统交货了
低代码
资深技术分享员5 天前
Geejing WebBuilder 数据库连接配置与在线 SQL 工具,运维的左右手
运维·数据库·sql·低代码
低代码布道师6 天前
外包数字化平台 第05篇|修复部门详情显示问题
低代码
半摆烂日常7 天前
低代码平台API对接实践:接口鉴权与数据同步的完整实现
android·低代码·rxjava
液态不合群7 天前
AI低代码选型终局:SaaS轻量化vs私有化可控性深度博弈
人工智能·低代码·数字化·ai低代码
百数平台7 天前
百数 MCP 开发实战:私有 Python 工具编写、API Key 鉴权、Streamable-HTTP 接入与智能体挂载
人工智能·低代码