# Activiti integration > **TL;DR — Activiti is wired, the engine runs, no traffic flows > through it.** Engine bootstrapped, `act_*` schema provisioned at > 6.0.0.4, BPMN modeler reachable. But every workflow table is 0 > rows: no BPMN, no procdefs, no instances, no tasks. No > `gdsmodule.bCheck=1`. No `gdsmoduleflow` link. **The approval > button users see (`/business/doExamine`) bypasses Activiti > entirely** and just UPDATEs `bCheck=1` via SQL — see > [Two approval paths](#two-approval-paths) below. This page documents what's actually wired (concrete classes, URLs, engine state) and what would have to be true for it to do anything. ## Two approval paths xly ships two parallel approval mechanisms. They look similar from the UI but go through completely different code: | Path | Triggered by | Backend | Activiti involvement | |---|---|---|---| | **Simple "approve" toggle** | The "审核" button on a single-row form | `POST /business/doExamine` → `BusinessBaseController.java:384-391` → `BusinessBaseServiceImpl.doExamine()` → `ExamineServiceImpl` (xlyBusinessService). The service runs an UPDATE that flips `bCheck = 1` (and writes audit fields). | **None.** `grep` confirms `ExamineServiceImpl` calls neither `runtimeService`, `taskService`, `processService`, nor any other Activiti API. The `bCheck` flag is xly's own boolean, owned by `BusinessBaseServiceImpl`. Used widely as a list filter (`SalesOrderServiceImpl` and friends do `WHERE bCheck = 1` on every "show approved-only" query). | | **Multi-step BPMN workflow** | A user submits a row whose module has `bCheck = 1` AND a populated `gdsmoduleflow` row pointing at a deployed `act_re_procdef` | `ProcessServiceImpl.submitApply()` → `runtimeService.startProcessInstanceByKey(...)`. Subsequent approvers act through `CurrencyFlowController.complete(...)` → `taskService.complete()`. xly mirrors state in `biz_flow` + `biz_todo_item`. | **Full Activiti.** Requires a deployed BPMN (in `act_re_procdef`), an active process instance (in `act_ru_*`), and an assigned task (`act_ru_task`). | In this dev DB, **path 1 is the only path that runs**. Path 2 has zero configuration anywhere — no BPMN deployed, no `gdsmoduleflow` row, no `gdsmodule.bCheck=1` modules. So when a user clicks 审核 today, the record gets approved via the simple-toggle path and Activiti is not involved. Why the second path even exists: a customer that *does* need a real multi-step approval (warehouse → finance → GM, with reassignment and delegation) gets it by deploying a BPMN through the modeler and linking modules via `gdsmoduleflow`. That activates path 2 for those modules; path 1 still handles everything else. ## Activiti is wired — engine ON Despite the dev DB being idle, the engine boots with `xlyEntry`: - `xlyFlow/build.gradle:15` pulls `org.activiti:activiti-spring-boot-starter-rest-api:6.0.0`. That starter transitively pulls `activiti-spring-boot-starter`, which triggers Spring Boot's `ProcessEngineAutoConfiguration` to create a `SpringProcessEngineConfiguration` bean. - `xlyEntry/build.gradle` includes `xlyFlow` as `api project(':xlyFlow')`, so the starter is on the runtime classpath of the `xlyEntry` WAR. - `xlyEntry/.../EntryApplicationBoot.java:23-24` excludes only `org.activiti.spring.boot.SecurityAutoConfiguration` (the REST-endpoint security adapter) and Spring's own `SecurityAutoConfiguration`. **Activiti's main engine auto-config is NOT excluded** → the engine starts. - `xlyFlow/.../activiti/config/ActivitiConfig.java` is a `@Configuration implements ProcessEngineConfigurationConfigurer` whose only job is to set Chinese-friendly fonts on the diagram generator (`宋体` for activity / annotation / label fonts) and install a custom `ICustomProcessDiagramGenerator`. - `xlyApi`'s `ApiApplicationBoot` does NOT exclude Activiti either, but xlyApi doesn't include xlyFlow as a dep, so xlyApi has the engine's `org.activiti.engine.identity.User` class on classpath (used only by `IdGen.java` for crypto utilities) but no Activiti auto-config kicks in there. The 24 base `act_*` tables you see in the live schema are created by the engine's auto-DDL on first boot. ## Two Activiti versions on the classpath | Module | Version | Notes | |---|---|---| | `xlyPersist`, `xlyApi` | `org.activiti:activiti-engine:5.17.0` | Older 5.x line — declared in both modules. **Vestigial** — the runtime engine in `xlyEntry`'s WAR is the 6.0 one pulled by xlyFlow's starter. The 5.17 declarations are dead weight on the classpath. | | `xlyFlow` | `org.activiti:activiti-spring-boot-starter-rest-api:6.0.0`, `activiti-json-converter:6.0.0` | Newer 6.0 line — this is what runs. | A maintainer cleaning up should drop `5.17.0` from `xlyPersist` and `xlyApi`'s `build.gradle`. Verified: only `IdGen.java` in each of those modules touches `org.activiti.engine.identity.User`, and the type signature is satisfied by the 6.0 engine too — removal is safe. ## What's actually invoked from code The 154 Java files under `xlyFlow/src/main/java/com/xly/activiti/` plus the modeler subpackage are real call sites. Selected anchors: | Activity | Class : line | Activiti API used | |---|---|---| | Start a process instance | `ProcessServiceImpl.submitApply()` :107 | `runtimeService.startProcessInstanceByKey(module, businessKey, variables)` | | Complete a task | `CurrencyFlowController.complete(...)` :167 / `:200`; `WechatFlowPostThread` :132 | `processService.complete(taskId, ...)` → `taskService.complete()` | | Query active tasks | `CurrencyFlowController` :409, :480 | `taskService.createTaskQuery().active().list()` | | Query running instances | `CurrencyFlowController` :485, :659 | `runtimeService.createProcessInstanceQuery()` | | Save a model in the modeler | `ModelerController.create()` :122 | `repositoryService.saveModel()` + `addModelEditorSource()` | | Deploy a BPMN at runtime | `ModelerController.deploy()` :147 | `repositoryService.createDeployment().addString(name, bpmnXml).deploy()` | | List process definitions | `ProcessDefinitionController` :135 | `repositoryService.createProcessDefinitionQuery()` | | Read engine config | `ProcessActController` :281 | `ProcessEngines.getDefaultProcessEngine()` | | Bridge xly users into Activiti identity | `act_id_user` / `act_id_group` / `act_id_membership` are **views** projecting xly's `sftlogininfo*` schema | xly does not write to Activiti's identity tables; the views fake them | ## URLs the modeler exposes (xlyFlow controllers, on xlyEntry's port) Because xlyFlow is consumed as a library by xlyEntry (`api project(':xlyFlow')`), all xlyFlow controllers compile into the xlyEntry WAR and serve at xlyEntry's context-path (`/xlyEntry`). Notable URLs: - `POST /xlyEntry/modeler/model/{modelId}/save` — save BPMN-modeler XML - `GET /xlyEntry/modeler/model/{modelId}/json` — load model for editor - `GET /xlyEntry/modeler/editor/stencilset` — modeler stencil definitions - `GET /xlyEntry/modeler/create` / `/modeler/deploy/{modelId}` — create + deploy - `POST /xlyEntry/complete/{taskId}/{sBrandsId}/{sSubsidiaryId}/{sUserId}` — complete a task (CurrencyFlowController) - `POST /xlyEntry/completeerp/{sBrandsId}/{sSubsidiaryId}/{sUserName}` — ERP-side completion variant These are not catalogued on the [Internal API page](../../api-reference/internal.md) because they're rarely-touched workflow surface; treat the source as authoritative. ## What `CheckFlowController.java` actually contains This is a wiki-internal correction worth flagging: the class file exists at `xlyEntry/src/main/java/com/xly/web/businessweb/CheckFlowController.java` but its body is **22 lines, zero handler methods** — just a `@RestController @RequestMapping(value="/checkflow")` shell with no content. Earlier versions of this wiki described `/checkflow/*` as "Activiti workflow surface (approve / reject / view)"; that is not what the file currently contains. **`/checkflow/*` returns 404 for any sub-path on the live system.** The actual approve/reject/view URLs come from `CurrencyFlowController` and friends listed above. ## The `act_*` schema state (this dev DB) | Table | Rows | Meaning when populated | |---|---:|---| | `act_re_model` | 0 | BPMN models saved in the modeler | | `act_re_procdef` | 0 | Deployed process definitions | | `act_ru_task` | 0 | Active (waiting) tasks | | `act_hi_procinst` | 0 | Historical process instances | | `act_id_user` / `act_id_group` / `act_id_membership` | (views) | Project xly's `sftlogininfo*` users into Activiti identity shape | | `gdsmoduleflow` | 0 | xly's link from `gdsmodule` to a process definition | | `biz_flow` | 0 | xly's per-document flow state | | `biz_todo_item` | 0 | Pending approver tasks (xly wrapper, not Activiti's `act_ru_task`) | | `biz_todo_copyto` | 0 | CC'd parties on a flow | So Activiti is **stone-cold idle**. Engine running, schemas ready, no traffic. ## What would make it move For a flow to actually run, in roughly this order: 1. An engineer or PM opens the **modeler UI** (modeler static assets at `xlyFlow/src/main/resources/static/modeler/`, served via the `/modeler/*` endpoints). They draw a BPMN, save it (`act_re_model` populated). 2. They click *Deploy* in the modeler → `ModelerController.deploy()` calls `repositoryService.createDeployment().addString(name, bpmnXml).deploy()` → `act_re_procdef` populated. 3. A `gdsmodule` row is tagged `bCheck = 1` and a row in `gdsmoduleflow` links the module to the deployed `act_re_procdef.KEY_`. 4. When a user saves a row on that module, the save service detects `bCheck = 1` and calls `ProcessServiceImpl.submitApply(applyUserId, businessKey, itemName, itemContent, module, variables)`. That fires `runtimeService.startProcessInstanceByKey(module, businessKey, variables)` → `act_ru_*` tables populate, `biz_flow` + `biz_todo_item` get xly-side rows. 5. Approvers see pending tasks in their FROUNT inbox (probably the "审批" tab, separate from KPI Work Center). They click 通过/驳回 → `CurrencyFlowController.complete()` → `taskService.complete()`. 6. When the proc instance reaches `endEvent`, the row's `bCheck` transitions; downstream queries that filter on `bCheck = 1` start seeing it. ## Why xly bothered with Activiti at all The codebase has its own `biz_flow` / `biz_todo_item` tables that *could* implement a hand-rolled approval system. The decision to put Activiti behind them buys: - Standard BPMN modeling (the JS modeler pulls the same stencilset as Activiti Explorer). - Free state-machine semantics — the engine handles "task A done → task B available" without xly maintaining the FSM in SQL. - Diagram rendering (the page-as-PNG in `ProcessActController`). The cost: a second engine running in the JVM, a second DB schema with its own DDL drift, a second authentication surface (which xly papers over via the `act_id_*` views). ## What this page is *not* - A Slice 7 substitute. Slice 7 (deferred) would document an end-to-end traced flow against a deployment that actually runs one. - A modeler tutorial. The modeler comes from the Activiti project; xly embeds it as static assets without modification. - A migration plan from Activiti to anything else. That would be a larger architectural decision, not a wiki finding.