-
Five mermaid diagrams across the en wiki, plus fix one stale arrow in the existing concepts/index diagram. New: - reference/maintainer/deployment.md — full topology at a glance: 6 deployable Spring Boot apps with their default-profile ports + context paths + Boot main classes, the 8 library modules and what they're consumed by, the operator-facing SPAs (BACK :8597 / FROUNT :8598) traced through nginx into xlyEntry/xlyApi, and the shared infra cluster (MySQL / Redis :16379 / ActiveMQ :61616 / MongoDB wired-but-unused). Surfaces the "xlyFlow + xlyPlc share /xlyEntry context-path" subtlety and the "xlyErpJmsConsumer has no port, inherits" oddity in one picture. - reference/maintainer/cache-invalidation.md — dual-path diagram showing the synchronous @CacheEvict path (green) next to the JMS PRO_ERPMERGEBASEGDSMODULE base-data merge (red, labelled "NOT cache"). Anchors the page's main correction visually so a reader can't confuse the two systems again. - concepts/request-lifecycle.md — sequence diagram covering the full /getModelBysId + /getBusinessDataByFormcustomId round-trips, with the AuthorizationInterceptor + RequestAddParamUtil preamble, the ADMIN-skips-jurisdiction branch, and which service makes which DB call. Supplements (doesn't replace) the ASCII layout diagram above it. - concepts/customization-layers.md — colour-coded vertical stack (system → tenant → user) showing the five overlay tables (gdsconfigformmaster / personalize / slave / customslave / userslave) flowing into the merged form delivered to the SPA. - slices/01-hello-world.md — full save-flow sequence: addSysLocking optimistic-lock → addUpdateDelBusinessData → BusinessBaseServiceImpl per-row dispatch → DB write (with sTableNameList tenant-bypass callout) → BusinessCleanRedisData synchronous @CacheEvict → grid re-fetch. Built from the live capture in commit 054efbef. Fixed: - concepts/index.md — the existing "metadata change → AMQ → xlyErpJmsConsumer → REDIS" arrow chain implied JMS busts the cache, which the cache-invalidation pass already corrected in prose. Diagram updated to show the two distinct paths: synchronous @CacheEvict directly to Redis from xlyEntry, plus the separate domain-events JMS path that runs PRO_ERPMERGEBASE* against MySQL (NOT cache invalidation). Caption added pointing readers at cache-invalidation.md. All 6 diagrams render as <pre class="mermaid"> via Material for MkDocs's mermaid integration; mkdocs --strict green.
-
Drove the BACK SPA's universal-CRUD save end-to-end via real keystrokes + clicks. Captured: - Network: POST /xlyEntry/business/addUpdateDelBusinessData ?sModelsId=13 → 200 OK (sModelsId only as query, no path variable — matches the SPA bundle's saveData call site: business/addUpdateDelBusinessData?sModelsId=" + sModelsId). - DB side-effect: directly after save, gdsformconst.sChinese for sId=20260123… returned the modified value with the typed audit- tag suffix (since reverted). Empirical proof that the body's updateData[].column.sChinese landed in the named column on the named sTable — the universal-CRUD path verified end-to-end. - Body shape: still rests on Javadoc + the SPA bundle's value parameter. The exact byte sequence the SPA serializes wasn't captured because the SPA's whatwg-fetch polyfill caches the XMLHttpRequest constructor at startup and bypasses any post- startup XHR monkey-patch (constructor wrapping, prototype override, fetch hook all silently bypassed). Documented this technical reason inline. - Pre-save addSysLocking POST also confirmed in the same network capture (optimistic-lock flow already documented in internal.md). Item 3 (SQL-layer capture from syslog4j / MyBatis debug log) demoted to "Future-work backlog" — the dev DB's logging level doesn't include MyBatis statements; closing it requires a deployment with MyBatis debug logging enabled.
-
Cross-verified every load-bearing claim against current src/db. Two real defects found and fixed; everything else holds. Fixes: - tech-stack.md HikariCP row: claim was "8 files reference com.zaxxer.hikari (xlyApi=6, xlyInterface=2)". Empirical recount on the cleanup branch: only 3 files import HikariDataSource (xlyApi=2: MasterDataSourceConfig + SlaveDataSourceConfig; xlyInterface=1: MasterDataSourceConfig). Updated the cell to match. - slices/01-hello-world.md "Open verification items" had two items numbered 3 (a copy/paste artefact from the Pass-E live-trace edit). Renumbered the second to 4, fixed the cross-ref to point at the actual section anchor (#4-user-edits-a-row-clicks-save), and rewrote the closing one-liner so it names the items that are still open (2 + 3) rather than the original v1 framing. Re-verified (no change needed): - All cited line numbers in BusinessBaseController.java (63, 65, 105, 107, 165, 167) and BusinessBaseServiceImpl.java (162, 165, 181, 196, 246, 325-327, 1014, 1078, 1250, 1738, 1824) still accurate. - BusinessGdsconfigformsServiceImpl.java:209 @Cacheable still reads the documented value/key pair. - RequestAddParamUtil line counts: 56 (xlyPersist), 57 (xlyApi). - DB count claims: 1358 gdsmodule, 124,524 kpimaster, 2986 gdsformconst, 0 gdsconfigformcustomslave, 311 views, 901 tables, 1687 procs, 177 functions, 18 customer override dirs, 305 viw_* views, 19 leaky. - BI/management line counts: CharServiceImpl 2219, KpiServiceImpl 833, BusinessModelKpiServiceImpl 901, all 10 Gds*ServiceImpl line counts. - Cache regions: 17 in CleanRedisServiceImpl matches the wiki. - xlyPlc: 5 java files + 9 yml profiles. xlyEntity: 22 @Document. - EntryApplicationBoot.java:22 @EnableCaching, :23-24 SecurityAutoConfig exclude — both still where the wiki says. - act_*/biz_flow/biz_todo_item/gdsmoduleflow all still 0 rows (Activiti is wired-but-idle as the activiti.md page says). - gdsconfigtbmaster drift: 11 of 307 sTbName values unresolved (3.6 %). - All 6 BACK framework-builder admin pages (系统模块配置, 数据表内容 配置, 界面显示内容配置, 接口自定义配置, 系统常量配置, 系统权限 配置, etc.) referenced consistently across wiki. - Cross-page consistency on all key facts: gdsjurisdiction-as-key vs sysjurisdiction-as-table; sTableNameList 4-table tenant-bypass exception; Spring RedisCacheManager (NOT JMS) is the cross-node cache mechanism; xlyPlc as plugin; xlyFace in-build-out-of-doc- scope; the 5-key composite (formData, gdsformconst, gdsjurisdiction, billnosetting, report). - mkdocs --strict green. - nav matches filesystem (no orphans).
-
Editorial pass per user direction: stop justifying the architecture. For every "why this design works" passage, name the costs the design imposes — not as a parenthetical aside but as substantive critical analysis. Each major architectural-claim page now carries an explicit drawbacks/costs section. Pages revised: concepts/thesis.md - "The reward" → "What the design enables (and what each enabler still costs)": for each promised benefit (single codebase, PMs evolve without engineering, customisations layered cleanly), name the limit. Added closing observation that data-driven design redistributes complexity to people and tools the framework can't compile-check. - "When it breaks down": rewrote to call out that "bypassing the framework" via 18 customer dirs makes the data-driven thesis partial, not complete. concepts/semantic-fk.md - "Why xly disabled FKs": added critical analysis. Both reasons could be addressed surgically; the chosen "no FKs anywhere" is the trade for DB-enforced integrity, paid every day the system runs. concepts/master-slave.md - "Slave naming caveat": stop framing retention as wise pragmatism. The naming was a poor choice; preservation has a real ongoing cost. concepts/modules-forms-vtables.md - "Three nouns, one engine": the universal dispatch path concentrates 3,500+ lines + edge cases + special-case hardcodes in one class. Naming the trade. concepts/multi-tenancy.md - "How the design scales" → "How the design scales — and where it doesn't": shared schema = shared contention; tenant-filter index discipline; no physical hard-delete; rigid (sBrandsId, sSubsidiaryId) tenancy unit. concepts/customization-channels.md - Soften "90%+ should live here" claim — that's an aspirational target, not a measured fact. The 18 customer override directories are evidence the channel-2 demand is non-trivial. concepts/api-surface.md - "Why three tiers, not one" → "Why three tiers (and what splitting them costs)": three WARs to deploy, duplicate code, no shared session, three reverse-proxy entries. Note the alternative (single-WAR with package boundaries) and what that would cost vs gain. reference/maintainer/proc-dispatch.md - "Why dynamic proc dispatch matters": added five concrete costs (no compile-time check, no type safety, no call-site discoverability, no static analysis, broken stack traces). Reframed: dynamic dispatch made it cheap to keep adding procs, which made the pile grow, which made the pile harder to audit. reference/maintainer/cache-invalidation.md - New "Drawbacks of this design" section: confusing co-named systems, eviction in same transaction as write (silent corruption on Redis outage), allEntries=true blunt eviction, no batching, direct DB writes bypass everything. Also fixed the "if cache is local" hedge in section 3 (we've now empirically confirmed Redis- backed, so cache is shared). reference/maintainer/bi-engine.md - New "Drawbacks of the homebrewed approach" section: every chart needs a SQL author, charts run heavy SQL on OLTP DB, no semantic consistency between charts, no drill-down, customer-divergent KPI logic. Also dedup'd the duplicated "What this is not" section. reference/maintainer/sql-templates.md - "Why this is a 'template' library and not a code generator" → added costs: no enforcement, no regeneration, no template-origin tracking, customer overrides drift from scaffold. The 1,687 procs the schema carries are the evidence that "discipline rather than enforcement" doesn't fully hold. reference/maintainer/activiti.md - "Why this design works for xly's audience" → "Why xly avoided Activiti — and what that costs": scattered workflow logic, no central audit trail, no parallel-branch/reassignment, invisible flow-graph evolution, idle Activiti engine paying boot cost anyway. - "Why xly bothered with Activiti at all" → "Why xly bothered with Activiti — and whether it was worth it": named the costs (second engine, second schema, second auth surface, modeler UI to learn) and the damning fact that on this dev DB the engine is idle. A future cleanup could plausibly remove Activiti entirely. reference/maintainer/runtime.md - New "What 'universal CRUD' means in practice" section: 3,500-line single-point-of-failure class, no type system on Map<String,Object>, poor discoverability ("what endpoints write to table X" is unanswerable). The trade: adding a module is essentially free, touching the runtime essentially never is. - Updated cache-invalidation cross-link to drop the "open question" hedge (now empirically resolved). slices/04-custom-field.md - "Why it works without code changes" → "Why it works without code changes — and what that costs": merge runs on every request, three near-empty tables on every schema, display-only extension (real persisted fields still need ALTER TABLE), debuggability requires diffing 3 overlay tables. slices/05-customer-sql-override.md - Added drawbacks: no version control on the deployed body, no type-safety bridge, compounds the BI problem. Reframed the "right rule of thumb": 18 customer override directories suggest the channel-2 demand is structural, not exceptional — that's evidence the metadata model isn't expressive enough, not a celebration of the escape hatch. slices/06-hardware.md - "The cleanest story xly tells about an awkward problem" → removed the "cleanest" framing. Added costs of "DB as the only contract": no backpressure, no request/response, bridge-side state invisible to the framework, three layers of polling multiply latency, hardest code (byte protocols) gets least CI. A real-time-aware architecture would use streaming end-to-end; xly's choice trades latency, observability, flow control for operational simplicity. Liveable for press tempo, not for faster shop-floor signals. -
The FROUNT home card "KPI监控" is open-task aggregation (BusinessModelCenterController), not analytics — already covered in runtime.md from the parallel session. Added a callout at the top of the KPI subsystem section so readers don't conflate the two.
-
User asked "Is there a BI engine?" Investigation found a substantial metadata-driven BI layer that the wiki had no dedicated coverage for. New page reference/maintainer/bi-engine.md: - Charts: gdsconfigcharmaster (3,006 rows) + slave (1,951) + CharServiceImpl (2,219 lines, one of the heaviest in xlyBusinessService). 11 chart types catalogued by sCharType distribution: Div(1558)/sLabel(1143)/Progress(137)/sPie(52)/ commonList(45)/sColumnarGroup(30)/sColumnar(28)/sBrokenLine(5)/ sBar(3)/ColorBlock(3)/sGauge(2). - 20 Sp_chart_* aggregation procs catalogued by domain (homepage cards, today/month sales, financial, equipment/shop-floor). - 6 /indexPage/commonChar dashboard modules listed by sId+name. - KPI subsystem: 6 kpi* tables (kpimaster=124,524 rows live), KpiServiceImpl (833 lines), BusinessModelKpiServiceImpl (901), FlushModleKpiThread, 2 Sp_KPI_* procs + spKPImodule. - Render flow end-to-end (SPA → getModelBysId → CharServiceImpl → generic proc dispatch → ECharts). - Customer-override KPI examples under script/客户/ noted. - Explicit "what this is not": not self-service BI, not real-time analytics, not OLAP-cube-backed. - Added to mkdocs nav under Reference (Maintainer). Tech-stack.md correction: - The OLAP4J row previously claimed "1 file in xlyPersist imports org.olap4j.*". Empirical re-grep returns ZERO Java imports of org.olap4j anywhere in source. The jars are classpath dead weight (the xmlaserver line is even commented out in build.gradle). Updated row to reflect the actual state and link to bi-engine.md.
-
Plan: /Users/reporkey/.claude/plans/noble-tumbling-sparkle.md Inventory: 33 hedge entries / open verification items across 12 hand- written pages, clustered into 5 groups by why-they're-not-verified. Cluster A — closed inline by reading source / running DB queries: - slices/02-multi-tenancy item 1: edition gating is **not** sVersionFlowId → sisversionflow. Verified: `grep sVersionFlowId xly-src --include='*.java' --include='*.xml'` returns ZERO mapper hits. The actual filter is `MenuChildServiceImpl.getBuMenuSql` line 64: `AND m.sId in (#{sVersionFlowId... wait sVerifyLicense})`. sVerifyLicense is sourced from the TrueLicense-bound `VerifyLicense.getModelAllList()` and injected via xlyApi RequestAddParamUtil:50 or controller-level param assembly. sVersionFlowId/Code are catalogue tags, NOT runtime gates. Wiki body of "How modules are filtered per edition" rewritten. - slices/02 item 2: closed; cross-link to the activiti.md rewrite that documents Activiti is wired-but-idle. - slices/02 item 3: session→tenant chain mapped: AuthorizationInterceptor.preHandle → RedisTokenManager.getToken (AES-decrypts bearer, checkToken validates Redis at <sLoginType><userId>) → @CurrentUser via CurrentUserMethodArgumentResolver → RequestAddParamUtil.addParams injects 16 keys. - slices/04 item 3: bVisible semantics closed. BusinessGdsconfigformsServiceImpl.java:413-433 — customslave row matches base by sControlName/sName, REPLACES base. Lines 446-468 — user-overlay then explicitly sets bVisible=false at line 464 when user-row hides the field. Hides at either layer; scope differs (per-tenant vs per-user). - slices/05 item 1: DbToDbServiceImpl is inter-DB sync (getData/ getDataDetail/etc. over Druid+JDBC), NOT a script-applier. `grep "script/客户" xly-src --include='*.java'` returns zero. Manual application via mysql CLI confirmed. - slices/06 item 2: PlcScheduledTasks ships two @Scheduled cron methods (`0/30 * * * * ?` line 74, `0/1 * * * * ?` line 105). Per-profile tuning is parameter-side, not cron-side. - slices/06 item 3: xlyRxtx git history — added in commit daf581311 ("1、添加串口功能"), commented out in cleanup branch for builds where serial isn't needed. xlyPlc runs without RXTX on TCP/Ethernet press models; serial-only models would re-enable. - builder/define-vtable item 1: 11 of 307 sTbName values (3.6 %) don't resolve to a base table. Breakdown: 4 point at views (viw_*), 3 at procs (Sp_*), 4 at case-drift / dropped tables. Audit query embedded. - builder/define-vtable hedge: real worked example added — `包装方式 / SisPacking` with 10 slave columns mapped to physical columns. Cluster B — closed via live BACK browser session: - slices/04 item 1: 界面显示内容配置 (gdsmodule.sId=11, /jmnrpz) renders three form-master panels. Third panel (sId=19211681019715596285250620, sTbName=gdsconfigformcustomslave) is the customslave editor. Verified live via clicking the menu and inspecting the GET /business/getBusinessDataByFormcustomId call. Cluster D — left in place with "Deferred (needs populated DB)" admonition: - slices/03 item 1 (view-with-print-template): no view-backed forms with a sysreport row — DB query returns 0. - slices/04 item 4 (real customslave example): COUNT(*) = 0. - slices/07 stub (active workflow): act_re_procdef = 0; bCheckflowCheck hard-disabled regardless. - builder/attach-workflow stub: same — recipe is code-derived hypothesis, not live-verified. Cluster E — left in place with "Deferred (outside repository)" admonition: - slices/06 item 1 (wire protocols): vendor docs, not source. - deployment.md "Open: production URL routing": nginx config in deployment ops, not the codebase. Cluster F — converted to "Future-work backlog" callouts: - slices/03 hedge "future revision should pick a module with print template": demoted to a future-work callout adjacent to the open item. - slices/05 items 2 & 3: re-cast as "Future-work backlog item — not a verification claim" with the workable command/mitigation noted. - concepts/customization-channels line 66: rephrased — the choice is about *runtime divergence visibility in source control*, not maintainer opinion. Bonus closure: - slices/03 item 3 (tenant-leaky views): DB audit returns 19 of 305 viw_* lacking sBrandsId (~6.2 %). Embedded the SQL + the count. - slices/04 line-41 hedge ("needs verification by clicking through BACK"): replaced with the verified mapping from item 1. After-state: of the original 33 entries: - 19 newly closed with evidence, - 4 left as deferred-because-DB-state with concrete blocker query, - 2 left as deferred-because-outside-repo with concrete blocker note, - 4 demoted to "Future-work backlog" callouts, - 4 already closed in earlier passes. Build verified `mkdocs --strict` green. -
User asked: are there examples of customising workflow other than the standard hardcoded one? Investigated and found a substantial customer example. Findings: - script/客户/万昌/计件工资/日报审核/领班驳回.sql is a 185-line customer-side workflow customisation defining Sp_mftproductionreportmaster_check1_0 ("Foreman Rejection", state 1 → state 0 transition). - Naming convention: Sp_<table>_check<currentState>_<nextState>. No procs in standard DB use this pattern; it's 万昌's own convention. - The proc resets SIX approval flags simultaneously (bManager, bIPQC, bDeputy, bSubmit, bWorkshopManager, bCheck) — 万昌 has ALTER TABLEd mftproductionreportmaster to add 5 columns that don't exist in the standard schema. - Adds sRejectMemo rejection-reason history (also custom column), appends each rejection's reason + timestamp + foreman name. - Calls sp_add_flow_log — a custom audit-log proc that doesn't exist in the standard DB. - Includes customer-specific business rules (e.g., block rejection if any slave row has bSAPCheck=1 — SAP-sync guard). So the answer to "any examples of customising workflow": - Yes — 万昌 has built their OWN multi-level approval workflow on top of xly's button-dispatch primitive: schema extension + custom transition procs + custom audit log. The framework provides only the button-press dispatch (via /business/ genericProcedureCall* or sButtonParam); everything else is customer-defined. - This is fundamentally different from Activiti — no BPMN, no FSM library, no engine. Just SQL + ALTER TABLE. - Other customer dirs (千彩, 重庆展印, 朝阳, etc.) mostly customise *calculations and reports*, not workflow. 万昌's pattern is rare but real. Slice 5 gets a "Worked-example 2" section walking through 领班驳回.sql in detail, plus a customisation-patterns-at-a-glance breakdown of what each of the 18 customer dirs actually contains. Activiti.md gets a back-reference: "A real Path-1 customisation example" pointing at the new slice 5 worked example, with the empirical observation that Activiti deployment is NOT seen in any script/客户/ directory. -
User asked: if Activiti isn't used, what implements workflow? Investigated and replaced the original two-path table with a proper three-path explainer. Findings: - Path 1 (single-step approval via stored proc + bCheck flag) is the dominant pattern. Each business table carries bCheck (426 tables), tCheckDate (400), sCheckPerson (398) — virtually universal. The 审核 button POSTs to /business/doExamine which calls the proc named in gdsmodule.sProcName; the proc owns the business validation and flips bCheck=1 directly. No engine, no state machine, no queue. - Path 2 (document chaining) is how multi-document business processes work: separate gdsmodule entries arranged as parent + ordered children, each with its own form and approval button. A "convert-to-next-doc" proc on form A creates the document for form B. The "01/04, 02/04…" KPI Work Center step numbering is just iOrder of children under a parent gdsmodule. No state machine; the workflow is the chain of procs wired by buttons. - Path 3 (Activiti BPMN) is GATED by ConstantUtils.bCheckflowCheck which is hard-coded false. So even if a tenant configured gdsmoduleflow rows and deployed BPMN, the gate in ExamineServiceImpl.doExcuExamine would short-circuit. To activate Path 3 a maintainer would have to recompile xly with the constant flipped, OR runtime-patch it. The page now leads with all three paths and a comparison table covering state storage, step transitions, reassignment/delegation, parallel branches, current activation status, and tooling. Concluding section explains why this design fits xly's audience (printing-industry ERP, where each business step is naturally its own document) and the trade-off (workflow logic scattered across stored procs rather than visualised in BPMN).
-
Critical correction in response to "no BPMN deployed means not used?": yes, in this dev DB, Activiti's runtime is never invoked. Findings: - xly has TWO parallel approval mechanisms, only one of which uses Activiti. - Path 1 (simple toggle): the "审核" button in the SPA POSTs to /business/doExamine → BusinessBaseController.java:384-391 → BusinessBaseServiceImpl.doExamine() → ExamineServiceImpl. Verified (grep -E 'runtimeService|taskService|processService|complete' against ExamineServiceImpl.java returns 0 hits) — this service flips bCheck=1 via direct SQL, never touching Activiti. This is the path actually used today. - Path 2 (BPMN workflow): would only trigger when a gdsmodule has bCheck=1 AND a populated gdsmoduleflow row pointing at a deployed procdef. None of these exist in the dev DB. - The bCheck flag is therefore xly's own approval boolean, owned by BusinessBaseServiceImpl, used widely as a list filter (SalesOrderServiceImpl etc. do WHERE bCheck=1 on every "show approved-only" query). - /business/doExamine + /business/getProData added to internal.md endpoint table. - activiti.md gets a "Two approval paths" section as the lead so a reader doesn't conflate the simple bCheck toggle with the workflow engine. - The TL;DR is rewritten to make the "engine wired but not invoked" story explicit.
-
Re-investigated Activiti's actual integration after the user asked how it's used. Substantive findings: State on this dev DB: engine wired, never used. - The Activiti 6.0 engine bootstraps with xlyEntry. activiti-spring- boot-starter-rest-api:6.0.0 declared in xlyFlow/build.gradle pulls ProcessEngineAutoConfiguration. xlyEntry's EntryApplicationBoot only excludes Activiti's REST-security autoconfig, not the engine. xlyFlow is consumed as api project(':xlyFlow') in xlyEntry, so all xlyFlow controllers serve at xlyEntry's context-path. - ActivitiConfig.java is a real @Configuration that wires Chinese fonts and a custom ICustomProcessDiagramGenerator into the engine. - Real call sites confirmed: * runtimeService.startProcessInstanceByKey(...) at ProcessServiceImpl.java:107 * taskService / repositoryService used across CurrencyFlowController, ModelerController, ProcessDefinitionController, ProcessActController, BizTodoItemServiceImpl, etc. * Modeler endpoints: /modeler/model/{id}/save, /modeler/editor/ stencilset, /modeler/create, /modeler/deploy/{id}. - Empirical state: every workflow table is 0 rows in this dev DB — act_re_model, act_re_procdef, act_ru_task, act_hi_procinst, gdsmoduleflow, biz_flow, biz_todo_item, plus 0 gdsmodule rows with bCheck=1. Engine running, plumbing hot, no traffic. CheckFlowController correction: - The class file is 22 lines with ZERO handler methods. Just @RestController @RequestMapping("/checkflow") shell. The wiki previously described it as "Activiti workflow surface (approve/ reject/view)" — this was wrong. /checkflow/* returns 404. - Real workflow URLs live in xlyFlow's CurrencyFlowController: /complete/{taskId}/{sBrandsId}/{sSubsidiaryId}/{sUserId}, /completeerp/{...}, plus /modeler/* for BPMN authoring. - Fixed in runtime.md (load-bearing-controllers row), internal.md (specialised-runtime row), with cross-link to activiti.md. activiti.md rewritten: - Reframed from "deferred for full coverage" to "engine wired, never used in this dev DB" — the engine state is concrete and documentable; only the flow content is empty. - Added "Activiti is wired — engine ON" section with the bootstrap chain. - Added a concrete table of Activiti API call sites (file:line + which Activiti service is invoked). - Added URLs-the-modeler-exposes section listing the real workflow URLs served via xlyEntry. - Added the act_* schema state table with the live 0-row counts. - Added "What would make it move" — the 6-step path from drawing a BPMN to seeing a flowing approval. - Added "Why xly bothered with Activiti at all" — the architectural rationale. - Documented xlyApi/xlyPersist 5.17 declarations as vestigial (only IdGen.java's User import; type signature works under 6.0 too — safe to remove). -
Investigated the FROUNT home-page card titled `KPI监控` to determine whether it's Activiti-driven (it isn't). Findings: - Endpoint: POST /xlyEntry/modelCenter/getModelCenter → BusinessModelCenterController.getModelCenter (xlyEntry, web/businessweb/) → BusinessModelCenterServiceImpl.getKPIModelList (thin wrapper) → BusinessModelKpiServiceImpl.getKPIModelList (xlyBusinessService). - Internal name: "KPI Work Center" (Javadoc 初次获取KPI工作中心). - Data source: gdsmodule rows where bUnTask=1, partitioned by sUnType ∈ {Pending, PendingCheck, MyWarning}. No KPI math; just open-task aggregation. - Categorisation in UI: by role (sftlogininfojurisdictiongroup ⋈ sisjurisdictionclassify) and by business flow (gdsmodule parent- child tree; "01/04, 02/04…" is the iOrder within a flow parent). - Cache: @Cacheable(value="getKpiModelByUser") evicted by getModelCenterCalculation and gdsmodule-changing paths. - Empirical proof of independence from Activiti: in this dev DB, biz_todo_item and biz_flow are both 0 rows, yet the KPI Work Center still shows non-zero counts. Activiti's task surface flows through CheckFlowController instead. Updates: - runtime.md: add BusinessModelCenterController row to the load- bearing-controllers table; new "The KPI Work Center" subsection with full architecture, data source, cache behaviour, and the why-not-Activiti contrast. - internal.md: add /modelCenter/* row to the runtime endpoints table with cross-link. -
Live findings from BACK + FROUNT browser session: - /business/addSysLocking: undocumented optimistic-lock endpoint (POST, called when entering edit mode; handler at BusinessBaseController.java:400-407). Added to internal.md universal-CRUD table. - BACK admin sidebar fully enumerated (10 modules) and mapped to backing form-master sTbName + owning service. 8/10 are framework primitives covered elsewhere; 常用操作配置 has no gdsconfigformmaster row in this dev DB (SPA-side admin special-case); 图表配置 is gdsconfigcharmaster/slave (added to the gds prefix entry in modules-forms-vtables.md). - FROUNT login confirmed; this dev DB exposes only 4 sidebar modules (2 are AI/LLM out-of-scope). Production tenants would carry the full business-module catalog. - Slice 1 save endpoint live-corroborated: clicking 新增 → 保存 fired POST /xlyEntry/business/addUpdateDelBusinessData?sModelsId=13 → 200 OK. (Audit-tag mutation didn't actually land because the Vue model didn't pick up our scripted input change — endpoint flowed but with original row state.) Updated slice-01 open- verification item with the partial verification. Dead-source flag (sql-templates.md): - Two `FileSqlUtil` classes coexist. xlyFlow's loader points at the 8 templates that exist on disk. xlyApi's loader names 7 templates (sInSqlStrTemple, sOutSqlStrTemple, sDataSqlTmp[Def], sJsonSqlTmp[Out], sDbPro) — NONE of them exist as files. Documented as broken-by-default; either restore templates or remove the loader. Auto-catalog regen verified accurate (gdsmodule.md columns match live DB).
-
Third commit closing high-value gaps the user flagged in the verification plan. Cross-node cache coherence — LOCKED EMPIRICALLY: - Connected to live Redis at 118.178.19.35:16379 db=0. - 233 of 267 keys use Spring's `<cacheName>::<key>` separator. - Confirmed key shapes match @Cacheable SpEL specs: businessGdsconfigformsServiceGetFormconstData::{...} (37 entries) gdsmoduleById::gdsmoduleById_<sBrandsId>_<sSubsidiaryId>_<sLanguage> (2 entries) - Conclusion: Spring's RedisCacheManager IS the active CacheManager. @CacheEvict on any node clears the shared Redis store; cross-node coherence works without any JMS involvement. Removed the "open question" hedge in cache-invalidation.md. New page — Metadata-management services (xlyManage): - Closes the biggest documentation gap surfaced by Pass C2. - Catalogs the 8 large Gds*ServiceImpl classes (878+729+555+489+ 362+319+243+221 = ~3,800 lines of metadata-CRUD logic) plus CommonServiceImpl (56) and SysbrandsServiceImpl (125). - Documents the universal five-method shape every Gds*Service follows (get/getBysId/add/update/delete) and how it pairs with the corresponding Gds*Controller in xlyEntry/.../systemweb/. - Maps each service to its BACK admin screen. - Notes the cache-invalidation hookpoint (synchronous BusinessCleanRedisData.delCleanRedisData* on commit). - Added to mkdocs nav under Reference (Maintainer). Worked examples: - Slice 04: gdsconfigformcustomslave is empty in the dev DB (0 rows). Updated the open verification item to confirm this rather than leave the framing "depends on the deployment". - Slice 05: side-by-side diff of 重庆展印's Sp_SalSalesCheck vs the standard. Quantified differences (1714 vs 723 lines, same 14-param signature, override adds CbxSrcNoCheck branch and strips temp-table aggregation, 12 sibling procs use the same CbxSrcNoCheck pattern). Added a copy-pasteable diff command. Honest scope acknowledgement: - api-reference/internal.md: explicit "what this catalog includes vs treats as illustrative" paragraph. ~19 framework- primitive controllers documented; ~52 business-domain controllers (workorder/salesorder/productionPlan/etc.) treated as illustrations of the framework at work, with grep guidance for maintainers who need to find them. Auto-catalog regenerated: - Ran scripts/gen_catalog.py against live DB. No changes (catalog was already current); 3081 generated pages. Pass E save trace: - Tried multiple angles (UI flow, hand-crafted POST with token in Authorization header, fetch interception). Edit-mode UI didn't yield a save fire under our setup (Ant Design grid + Vue SPA edit-mode peculiarity). Read trace remains fully verified end-to-end. Save body shape is documented in the Javadoc on BusinessBaseController.java:161-163 and reflected in the wiki; live save corroboration deferred again. -
Second commit completing the verification plan after the first commit covered Pass A + most of Pass B P0/P1 + Pass C1/C2. P1 gaps: - slices/02-multi-tenancy.md: add the four-table sTableNameList exception (gdsformconst, gdsmodule, gdsconfigformmaster, gdsconfigformslave strip tenant cols at write); rewrite query-shape section to distinguish global framework metadata from tenant-scoped overlays + business state; flag dev DB has only 8S_001 in sisversionflow despite gdsmodule.sVersionFlowCode referencing EBC-SD/EBC-RD/EBC-MDM/etc.; add live row counts (1002 untagged, 322 8S_001, 15 EBC-SD-002, …). - api-reference/internal.md, messaging.md, notifications.md verified as-is (Pass A surface fixes were sufficient, all file paths exist). P2: - slices/04-custom-field.md: close out the merge-code open item — merge is Java at BusinessBaseServiceImpl.java:246-248 (getFormSlaveData + getFormCustomSlaveData); the two views supply the master⋈slave shape, the merge itself is in Java. - slices/06-hardware.md: PlcScheduledTasks uses Spring @Scheduled cron (every 30s + every 1s), not Quartz directly. - concepts/thesis.md: fix the "four-table read" framing — actual reads are from gdsconfigformmaster (with overlays), gdsformconst, sysjurisdiction, sysbillnosettings, sysreport. Soften slice-1 reference to remove the "four-table" misframing. - slices/03-report.md, concepts/api-surface.md, customization-channels, customization-layers, semantic-fk verified clean (script/客户/ customer dirs = 18, 0 FK / 0 triggers on xly tables, both overlay views exist). P3: - reference/builder/define-form.md: rewrite cache-invalidation section to match the corrected architecture (synchronous @CacheEvict, not the JMS path). - builder/define-vtable, builder/permissions, builder/index, builder/attach-workflow, slices/07-workflow, glossary/index, reference/maintainer/index, slices/index, contributing/index verified appropriately marked deferred / consistent. Pass D consistency sweep: - Ports (8080/8090/8000/8091/8597/8598) consistent across pages. - Schema names consistently use xlyweberp_saas_ai; running-locally.md correctly documents the xlyweberp_saas vs _ai divergence. - Counts (1358 gdsmodule rows, 311 views, 1687 procs, 177 functions) consistent. - Module/Modle/Models codebase quirk consistently surfaced. - xlyPlc plugin framing consistent across index.md, tech-stack.md, slice 06. - vtable / virtual table — single canonical form ("virtual table") used in narrative; URL-style filename `define-vtable` preserved. - proc / procedure / stored procedure usage clean (long form for emphasis, short for lists). Pass E live traces (partial): - Slice 1 read flow CORROBORATED end-to-end at BACK admin/123 edition 基础版/8s. Both documented URLs captured exactly: GET /xlyEntry/business/getModelBysId/13?sModelsId=13 → 200 POST /xlyEntry/business/getBusinessDataByFormcustomId/19211681019715574676360040?sModelsId=13 → 200 including the redundant ?sModelsId=13 alongside the path variable. Updated slice-01 open verification items to mark the read item closed. - Slice 1 save flow + slices 2-6 traces deferred (would mutate shared dev DB; FROUNT enumeration declined; monthly usage limit reached). Pass C3 frontend inventory (partial): - BACK admin sidebar = 10 framework-builder modules: 系统模块配置, 数据表内容配置, 界面显示内容配置, 接口自定义配置, 系统常量配置, 系统权限配置, 常用操作配置, 用户信息配置, Mysql脚本配置, 图表配置. - 8/10 covered by existing wiki pages; 2 silent (常用操作配置, 图表配置 — admin BACK utilities, not framework runtime). - FROUNT enumeration declined by user. -
Audit every concrete claim in the 41 hand-written en pages against the three primary sources (DB, source on cleanup branch, source-tree inventory). Fix divergent claims in place; preserve framing where verified. Substantive corrections: - request-lifecycle / runtime / slice 01: the metadata read sources from five tables/families (gdsconfigformmaster + overlays, gdsformconst, sysjurisdiction, sysbillnosettings, sysreport), not four. The map key `gdsjurisdiction` is misleading — the per-user grant read queries `sysjurisdiction`; `gdsjurisdiction` is the builder-side action catalogue. `gdsformconst`, `gdsconfigformmaster`, `gdsconfigformslave` are NOT tenant-scoped; they filter by form-id only. - multi-tenancy: four metadata tables (gdsformconst, gdsmodule, gdsconfigformmaster, gdsconfigformslave) are an explicit exception to the "every table tenant-scoped" promise — `sTableNameList` strips sBrandsId/sSubsidiaryId from writes against them. - sSaveProName / sSaveProNameBefore are pre/post-save HOOKS on top of the always-running base path (BusinessBaseServiceImpl.add/update), not either/or branches. Default add/update path is in BusinessBaseServiceImpl, not AddDelUpdCommonServiceImpl. - cache-invalidation: redis cache is cleared synchronously in BACK via @CacheEvict on CleanRedisServiceImpl during save. The JMS CHANGE_GDS_MODULE queue triggers PRO_ERPMERGEBASEGDSMODULE (base-data merge), NOT cache invalidation despite the name. Cross-node coherence open question (no custom CacheManager bean configured). - messaging: enumerate all 24 P2pQueue destinations grouped by intent; fix CHANGE_GDS_MODULE description; clarify single Consumer.java with 24 @JmsListener methods (not 24 listener classes). - API paths: /checkflow lowercase (mapping value, not class name); /procedureCall/doGenericProcedureCall (not /business/genericProcedureCall*). - tech-stack: Druid 6 java imports + 16 yml mentions (was 25 conflated); fastjson per-module xlyInterface 9 (was 10); commons-lang3 39 (was 41); @Document classes 20 PLAT_* + 2 DIKE_TEST* (was "all PLAT_*"); xlyPersist activiti hit is IdGen.java (was BaseDao.java); add Springfox to declared-but-no-imports table; reconcile module list to 11 framework core + xlyPlc plugin + xlyPlatConstant utility. - index.md: clarify xlyFace as "in build, not documented"; add xlyErpTask / xlyPlatTask scheduler bullet; correct MongoDB framing (caller is in xlyPersist with no consumers, not xlyPlat*); add xlyPlc note; extend backup-table OOS to cover *_copy1 / *_history / *YYYYMMDD[HHMMSS]. - deployment.md: split deployable Boot apps from library modules; enumerate 12 commented-out includes (was 3); remove xlyPlatConstant from out-of-scope Plat* list; split profile permutations by service. - activiti.md: add xlyApi to 5.17 dependency list; replace speculative BPMN path hint with verified state; name actual ActivitiConfig.java; note act_id_* are views projecting xly users into Activiti shapes. - api-reference/external.md: fix bearer-token validation flow (sysapibrand via AES-decrypted corpid, not sysapithirdtoken); /online/* are page renders not API execution; /pro/* mostly returns Thymeleaf views; mark sysapidbtodb as xlyFlow-owned; /token/getToken accepts GET and POST. - api-reference/webhooks.md: add Swagger Docket caveat (UI shell ships but no Docket bean → /v2/api-docs effectively empty); flag /send/sendQw as stub (returns "ok"). - slices/03-report.md: fix dir path xlyEntry/com/xly/report/ → xlyEntry/com/xly/web/report/; reframe PrintReportControllerOld as dead source (file body fully commented out). - concepts/modules-forms-vtables.md: add 22-prefix glossary table (gds/sys/sis/sft/ele/mft/sal/quo/acc/pur/ops/cah/sgd/ept/mit/pit/qly/ kpi/udf/viw_/plat_/ai_/act_/qrtz_) so a maintainer can enumerate business-data domains at a glance. - concepts/master-slave.md: disambiguate document-row pattern from DataSource master/slave (different concept, name overlap). - proc-dispatch.md: add proc-name molds (Sp_*_BeforeSave/AfterSave/ SaveReturn, sp_btn_*, PRO_ERPMERGE*) + function-layer paragraph (Fun_*/Fn_*/get_*; SQL-called, not Java-dispatched). - concepts/index.md: schema label MySQL\nxlyweberp → xlyweberp_*. Pass E (live behavioural traces) deferred — source/DB-side audit was thorough; live traces best done as a follow-up sweep against a running instance.
-
Reflect xly-src cleanup branch: - xlyPlat* modules dropped (except xlyPlatConstant, kept as shared utility) - xlyTestService, xlyTestController dropped - xlyFile, xlyRxtx already excluded; now explicitly listed - Note xlyPlatConstant as misnamed shared-utility (xlyPersist imports MultiThreadServer + TimeContant)
-
en: - new: api-reference/notifications.md, reference/maintainer/running-locally.md, reference/maintainer/tech-stack.md - concepts/index.md: add Mermaid architecture diagram - index.md: scope-note MongoDB store as plat-tier - concepts/api-surface.md: point "the four-table read" link at the reference/maintainer/runtime.md anchor that actually contains the section - maintainer/index.md, api-reference/index.md, mkdocs.yml: nav + cross-links zh: - mirror translations of the new en pages, plus zh api-reference/* section
-
Verified every factual claim in the hand-written prose against the codebase under ../xly/ and the live xlyweberp_saas_ai schema, and fixed the drift: - semantic-fk: clarified — zero FKs on xly tables; the ones that exist are all on bundled Activiti / Quartz schemas, which the runtime doesn't join through. - multi-tenancy / runtime / slice 2: corrected RequestAddParamUtil shape (16 keys, 56 lines), noted the parallel xlyApi copy. - slice 1: corrected line ranges for addUpdateDelBusinessData; clarified that sTableNameList is a cache-invalidation gate, not an authorisation gate; corrected the gdsroute claim — unregistered paths are NOT 404'd server-side, the SPA uses gdsroute as a client-side whitelist (web-verified). - runtime: fixed controller package paths (Gdsmodule/Gdsconfigform/Gdsconfigtb live in systemweb/, not businessweb/); added CheckFlowController. - sql-templates / proc-dispatch: 8 scaffolds, not 7 (sSqlStr.sql). - master-slave: removed table names that don't exist (accOrderCostAnalysisMaster, quoQuotationCalc, mftWorkOrderCalc, mftWorkOrderSlaveMoney); added the *_tmp family that does. - permissions: full rewrite. The original page described gdsjurisdiction as a per-(module, role, button) rule table backed by empty plat_base_authority_* lookups; reality is gdsjurisdiction is a per-module catalog of buttons/actions, with sysjurisdiction carrying the actual role/user grants. - Removed environment-specific facts (row counts, sizes, timestamps, per-DB enumerations) so the wiki documents the framework, not one particular DB snapshot. Auto-catalog generator (en/scripts/gen_catalog.py): - Rewritten to query MySQL directly via ~/.my.cnf (replaces the recon/*.tsv pipeline). - Stripped ephemeral fields from output (Rows, Data size, Created, Updated on tables; Created, Last altered on routines; Definer on views). - Strips the schema-name prefix from VIEW_DEFINITION so view bodies are portable across deployments. - Wipes and rewrites docs/auto-catalog/{tables,views,procedures,functions}/ on each run. - Auto-catalog regenerated. New API Reference chapter (5): - concepts/api-surface.md introduces the three-tier design (xlyEntry internal, xlyApi external, xlyInterface webhooks). - api-reference/{index,internal,external,webhooks,messaging}.md cover each surface, with the data-driven /api/invoke{sApiCode} pattern, sysapi schema, token flow, and the SpringFox Swagger UI location on xlyInterface. - mkdocs nav: chapters renumbered (Auto-Catalog → 6, Glossary → 7, Contributing → 8); glossary/contributing wrapped as section indexes to render correctly under navigation.sections. Other: - Bilingual top-level README (Chinese + English). - requirements.txt: pymysql added for the new generator. -
Documents the xly (小羚羊) printing-industry ERP framework. Built with MkDocs Material; CJK search via jieba; 3,076 auto-generated catalog pages from recon/*.tsv plus hand-written prose for the framework's core mental model and end-to-end vertical slices. Phase 0 recon: stack, schema shape, framework metadata layer, scope. Phase 1 wiki: scaffold + auto-catalog + Slices 1-6 (Slice 7 deferred). Slice coverage: 1. CRUD module (Hello World) — observed network + cited source 2. Multi-tenancy & product editions — sBrandsId/sSubsidiaryId/sVersionFlowId 3. View-backed module (read-only report) 4. Custom field overlay (gdsconfigformcustomslave) 5. Per-customer SQL override (script/客户/<customer>/) 6. Hardware integration (xlyPlc, optional) 7. Workflow (deferred — Activiti tables empty in dev DB) Concepts: thesis, modules-forms-vtables, master/slave, semantic-FK, customization channels & layers, multi-tenancy, request lifecycle. Reference (Builder): define-form, define-vtable, permissions, attach-workflow (deferred). Reference (Maintainer): runtime, proc-dispatch, cache-invalidation, sql-templates, deployment, activiti.