读源码先找入口:一次"发起工程"把工程管理模块走通

1 阅读7分钟

40 多个文件没必要从头扫。本文按一条真实调用链串起来看:发起列表 → 创建实例 → 项目处理器 → 任务中心,顺带把目录职责、状态枚举和四个常见二次开发落点讲清楚。

阅读顺序建议

先不要按目录把 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:

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.csPrjSta / TaskSta

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


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

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

  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, '项目处理');
  }

对应后端非常薄:

        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 递增那么直觉:

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

任务状态:

None     = 0;  // 未开始
Complete = 1;
Delay    = 2;
Pause    = 3;

人员状态在 GenerWorker.cs:

UnRead   = 0;
Complete = 1;
Read     = 2;
Refuse   = 3;

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

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

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


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

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

        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:

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页面背后依赖
0TaskGener.vuePrjWorkerLog 工时日志 + 附件
1TaskFlow.vue模板任务绑定的流程 RefTemplateNo / refFlowNo
2TaskBill.vueFool 表单,绑定单据

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

  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

把这条链走通,再去看文件工作区和甘特配置,不会迷路。本文只解决一件事:代码从哪进、改完往哪写。